.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
* SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
include_once(drupal_get_path('module', 'mica_core') . '/mica_core.search.utils.inc');
/**
* Implements hook_entity_info().
*/
function mica_relation_entity_info() {
$info['mica_relation'] = array(
'label' => t('Mica Relation'),
'controller class' => 'EntityAPIController',
'entity class' => 'MicaRelation',
'base table' => 'mica_relation',
'uri callback' => 'mica_relation_url',
'module' => 'mica_relation',
'exportable' => TRUE,
'entity keys' => array(
'id' => 'id',
'label' => 'label',
),
);
return $info;
}
/**
* Implements hook_node_type_delete()
*/
function mica_relation_node_type_delete($info) {
$relations = mica_relation_find_relation_by_parent_or_child($info->type);
if (!empty($relations)) {
foreach ($relations as $relation) {
field_delete_field($relation->options['node_reference']);
watchdog('mica', 'Delete relation between parent %parent and child %child',
array(
'%parent' => $relation->parent_bundle,
'%child' => $relation->child_bundle,
), WATCHDOG_INFO);
}
db_delete('mica_relation')->condition('id', array_keys($relations), 'IN')->execute();
}
}
/**
* Hide relation node reference fields.
* Implements hook_field_attach_form()
*/
function mica_relation_field_attach_form($entity_type, $entity, &$form, &$form_state, $langcode) {
if ($entity_type === 'node') {
$relations = mica_relation_find_relations_by_parent($entity->type);
if (!empty($relations)) {
foreach ($relations as $relation) {
$form[$relation->options['node_reference']]['#attributes']['style'] = array('display:none;');
}
}
}
}
/**
* Implements hook_node_presave()
*/
function mica_relation_node_presave($node) {
if (empty($node->nid)) {
$relations = mica_relation_find_relations_by_parent($node->type);
if (!empty($relations)) {
$menu = mica_core_create_node_default_menu($node, FALSE);
foreach ($relations as $relation) {
$relation->create_child_node($node, $menu);
}
}
}
else {
$relations = mica_relation_find_relations_by_parent($node->type);
if (!empty($relations)) {
foreach ($relations as $relation) {
$relation->update_child_node($node);
}
}
}
}
/**
* Implements of hook_TYPE_load().
*
* Load search index infos from Search API
*/
function mica_relation_mica_relation_load($entities) {
if (empty($entities)) return;
$indexes = mica_core_find_indexes_by_bundle();
if (!empty($indexes)) {
foreach ($entities as $entity) {
if (!empty($indexes[$entity->parent_bundle])) {
$entity->options['parent_indexes'] = array_keys($indexes[$entity->parent_bundle]);
}
if (!empty($indexes[$entity->child_bundle])) {
$entity->options['child_indexes'] = array_keys($indexes[$entity->child_bundle]);
}
}
}
}
/**
* Implements hook_node_delete()
*/
function mica_relation_node_delete($node) {
$relations = mica_relation_find_relations_by_parent($node->type);
if (!empty($relations)) {
foreach ($relations as $relation) {
$relation->delete_child_node($node);
}
}
}
/**
* Do not allow users to delete a node if the node type is part of a relation as a child.
*/
function mica_relation_node_access($node, $op, $account) {
if ($op === 'delete') {
$type = is_string($node) ? $node : $node->type;
$relation = mica_relation_find_relation_by_child($type);
if (!is_null($relation)) return NODE_ACCESS_DENY;
}
else {
return NODE_ACCESS_IGNORE;
}
}
function mica_relation_form_alter(&$form, $form_state, $form_id) {
$node_type = NULL;
if (array_key_exists('#node', $form) && isset($form['type']['#value'])) {
$node_type = $form['type']['#value'];
}
elseif ($form_id === 'feeds_delete_tab_form') { // Disable for feeds also
$importer = feeds_importer($form['#importer_id']);
$node_type = $importer->processor->config['content_type'];
}
if (!is_null($node_type)) {
$relation = mica_relation_find_relation_by_child($node_type);
if (!is_null($relation) && array_key_exists('actions', $form)) {
$form['actions']['info'] = array(
'#markup' => ' This node should not be deleted because it is used by the relation "' .
$relation->label . '". Only parents of relations can be deleted.',
'#weight' => '50',
);
}
}
}
/**
* Enhance the content type form with mica_relation extensions.
*
* Implements hook_form_FORM_ID_alter()
*/
function mica_relation_form_node_type_form_alter(&$form, $form_state) {
if (isset($form['type'])) {
$current_type = $form['#node_type']->type;
$relation = mica_relation_find_relation_by_child($current_type);
$existing_types = array();
foreach (node_type_get_types() as $type) {
if ($type->type !== 'mica_relation' && $type->type !== $current_type) {
$existing_types[$type->type] = $type->name;
}
}
$selected_parent_bundle = isset($form_state['values']['parent_bundle'])
? $form_state['values']['parent_bundle']
: (empty($relation) ? '' : $relation->parent_bundle);
$indexes = mica_core_find_indexes_by_bundle();
$form['mica_relation'] = array(
'#type' => 'fieldset',
'#title' => t('Relations'),
'#collapsible' => TRUE,
'#collapsed' => TRUE,
'#group' => 'additional_settings',
'#attributes' => array(
'class' => array('relation-node-type-settings-form'),
),
'#attached' => array(
'js' => array(drupal_get_path('module', 'mica_relation') . '/relations-node-form.js'),
),
);
$form['mica_relation']['parent_bundle'] = array(
'#type' => 'select',
'#title' => t('Parent'),
'#description' => t('Select parent content type'),
'#default_value' => $selected_parent_bundle,
'#options' => array_merge(array('' => t('None')), $existing_types),
);
$form['mica_relation']['cascaded'] = array(
'#type' => 'checkbox',
'#title' => t('Cascade events'),
'#description' => t('Cascade parent\'s events: create, update, publish, unpublish.'),
'#default_value' => empty($relation) ? TRUE : $relation->options['cascaded'],
);
$form['mica_relation']['indexed'] = array(
'#type' => 'checkbox',
'#title' => t('Index content'),
'#description' => t('Index as part of its parent'),
'#default_value' => empty($relation) ? TRUE : $relation->options['indexed'],
'#attributes' => array('class' => array('enabled-for-ajax')),
'#ajax' => array(
'callback' => '_mica_relation_ajax_populate_indexes_callback',
'wrapper' => 'child-indexes-container',
),
);
$parent_indexes = empty($selected_parent_bundle) || !array_key_exists($selected_parent_bundle, $indexes)
? array()
: $indexes[$selected_parent_bundle];
if (empty($relation)) {
$child_indexes_default_value = empty($parent_indexes) ? array() : array_keys($parent_indexes);
}
else {
$child_indexes_default_value = empty($relation->options['child_indexes'])
? array()
: $relation->options['child_indexes'];
}
$form['mica_relation']['child_indexes'] = array(
'#type' => 'checkboxes',
'#prefix' => '
',
'#suffix' => '
',
'#title' => t('Search indexes'),
'#description' => t('Parent\'s search API indexes'),
'#default_value' => $child_indexes_default_value,
'#options' => $parent_indexes,
'#attributes' => array('class' => array('enabled-for-ajax')),
);
$form['#validate'][] = '_mica_relation_validate_node_type_relation';
$form['#submit'][] = '_mica_relation_edit_node_type_relation';
}
}
/**
* Returns just the list of search index checkboxes for re-rendering
*/
function _mica_relation_ajax_populate_indexes_callback($form, $form_state) {
return $form['mica_relation']['child_indexes'];
}
function _mica_relation_validate_node_type_relation($form, &$form_state) {
$parent_bundle = trim($form_state['values']['parent_bundle']);
if (!empty($parent_bundle) && $form_state['values']['indexed'] == 1) {
$valid = FALSE;
foreach ($form_state['values']['child_indexes'] as $child_indexes) {
if ($child_indexes) {
$valid = TRUE;
break;
}
}
if (!$valid) {
form_set_error('child_indexes', t('You must select a Search Index if you choose to index content.'));
}
}
}
/**
* Save relation info on content type edition
*/
function _mica_relation_edit_node_type_relation($form, &$form_state) {
$previous_type = $form['#node_type']->type;
$new_type = trim($form_state['values']['type']);
$name = trim($form_state['values']['name']);
$parent_bundle = trim($form_state['values']['parent_bundle']);
$relation = mica_relation_find_relation_by_child($previous_type);
if (empty($parent_bundle)) {
if (!empty($relation)) {
$relation->delete();
watchdog('mica', 'Delete relation between parent %parent and child %child',
array(
'%parent' => $relation->parent_bundle,
'%child' => $relation->child_bundle,
), WATCHDOG_INFO);
}
}
else {
if (empty($relation)) {
$relation = new MicaRelation();
$relation->parent_bundle = $parent_bundle;
$relation->child_bundle = $new_type;
$parent_bundle_infos = node_type_load($parent_bundle);
$child_bundle_infos = node_type_load($new_type);
$relation->label = $parent_bundle_infos->name . ' » ' . $child_bundle_infos->name;
}
$relation->options['cascaded'] = $form_state['values']['cascaded'];
$relation->options['indexed'] = $form_state['values']['indexed'];
if ($relation->options['indexed']) {
$relation->options['child_indexes'] = $form_state['values']['child_indexes'];
}
else {
$relation->options['child_indexes'] = array();
}
$previous_relation = mica_relation_find_relation_by_child($previous_type);
if (empty($previous_relation)) {
// create new relation
watchdog('mica', 'Create relation between parent %parent and child %child',
array(
'%parent' => $relation->parent_bundle,
'%child' => $relation->child_bundle,
), WATCHDOG_INFO);
$relation = _mica_relation_configure($relation, $new_type, $name);
}
else {
// check if parent type has content
$query = new EntityFieldQuery;
$result = $query->entityCondition('entity_type', 'node')
->entityCondition('bundle', $previous_relation->parent_bundle)
->execute();
if (empty($result)) {
// no content for parent type, we can replace node reference field and relation
field_delete_field($previous_relation->options['node_reference']);
// Override previous_relation parameters with current ones
$relation->parent_bundle = $parent_bundle;
$parent_bundle_infos = node_type_load($parent_bundle);
$child_bundle_infos = node_type_load($new_type);
$relation->label = $parent_bundle_infos->name . ' » ' . $child_bundle_infos->name;
$relation = _mica_relation_configure($relation, $new_type, $name);
}
else {
// if parent_bundle has not changed
if ($previous_relation->parent_bundle == $parent_bundle) {
// update node reference field label
$field_instance = field_info_instance('node', $previous_relation->options['node_reference'], $previous_relation->parent_bundle);
$field_instance['label'] = $name;
field_update_instance($field_instance);
}
else {
// Cannot create another field node_reference with the same name...
drupal_get_messages('status');
form_set_error('parent_bundle', t('Failed to update parent: @parent_bundle because a mica_relation already exists for the current content type and content exists for that relation.', array('@parent_bundle' => $parent_bundle)));
return;
}
}
}
$relation->save();
}
}
function _mica_relation_configure($relation, $new_type, $name) {
$indexes = mica_core_find_indexes_by_bundle();
$relation->options['parent_indexes'] = empty($indexes[$relation->parent_bundle])
? array()
: array_keys($indexes[$relation->parent_bundle]);
// set reference node and relation only at creation
$relation->options['node_reference'] = 'mica_' . $new_type;
// create node reference field
$relation->add_node_reference_to_parent($name);
// check if parent type already has content
$query = new EntityFieldQuery;
$result = $query->entityCondition('entity_type', 'node')
->entityCondition('bundle', $relation->parent_bundle)
->execute();
if (!empty($result['node']) && $relation->options['cascaded']) {
$entities = entity_load('node', array_keys($result['node']));
foreach ($entities as $entity) {
// create missing child nodes
if ($relation->create_child_node($entity, isset($entity->menu) ? $entity->menu : array())) {
node_save($entity);
}
}
}
return $relation;
}
/**
* Returns an array of MicaRelations with relation ID as key
*/
function mica_relation_find_relation_by_parent_or_child($parent_or_child_bundle) {
$query = new EntityFieldQuery;
$parent_result = $query->entityCondition('entity_type', 'mica_relation')
->propertyCondition('parent_bundle', $parent_or_child_bundle)
->execute();
$child_result = $query->entityCondition('entity_type', 'mica_relation')
->propertyCondition('child_bundle', $parent_or_child_bundle)
->execute();
$result = array();
if (isset($parent_result['mica_relation']) && isset($child_result['mica_relation'])) {
$result = array_merge($parent_result['mica_relation'], $child_result['mica_relation']);
}
if (!empty($result)) {
return entity_load('mica_relation', array_keys($result));
}
return array();
}
/**
* Returns an array of MicaRelations for this parent type with relation ID as key
*/
function mica_relation_find_relations_by_parent($parent_bundle) {
$query = new EntityFieldQuery;
$result = $query->entityCondition('entity_type', 'mica_relation')
->propertyCondition('parent_bundle', $parent_bundle)
->execute();
if (!empty($result['mica_relation'])) {
return entity_load('mica_relation', array_keys($result['mica_relation']));
}
return array();
}
/**
* Returns a unique MicaRelation for this parent and child type with relation ID as key
*/
function mica_relation_find_relations_by_parent_and_child($parent_bundle, $child_bundle) {
$query = new EntityFieldQuery;
$result = $query->entityCondition('entity_type', 'mica_relation')
->propertyCondition('parent_bundle', $parent_bundle)
->propertyCondition('child_bundle', $child_bundle)
->execute();
if (!empty($result['mica_relation'])) {
$keys = array_keys($result['mica_relation']);
$relations = entity_load('mica_relation', $keys);
return $relations[$keys[0]];
}
return NULL;
}
/**
* Returns a unique MicaRelation for this child type
*/
function mica_relation_find_relation_by_child($child_bundle) {
$query = new EntityFieldQuery;
$result = $query->entityCondition('entity_type', 'mica_relation')
->propertyCondition('child_bundle', $child_bundle)
->execute();
if (!empty($result['mica_relation'])) {
$keys = array_keys($result['mica_relation']);
$relations = entity_load('mica_relation', $keys);
return $relations[$keys[0]];
}
return NULL;
}
/**
* Implements hook_feeds_plugins().
*/
function mica_relation_feeds_plugins() {
$info = array();
$info['FeedsMicaRelationProcessor'] = array(
'name' => 'Mica Relation Node processor',
'description' => 'Update nodes that are linked to a Parent through a mica_relation.',
'help' => 'Update nodes from parsed content.',
'handler' => array(
'parent' => 'FeedsNodeProcessor',
'class' => 'FeedsMicaRelationProcessor',
'file' => 'includes/FeedsMicaRelationProcessor.inc',
'path' => drupal_get_path('module', 'mica_relation'),
),
);
return $info;
}