. * * 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; }