From 4ef59776b016e0afb25d6c9c3c02165d9976ce9f Mon Sep 17 00:00:00 2001 From: bachy Date: Sat, 8 Dec 2012 11:59:44 +0100 Subject: [PATCH] update 7.1 Signed-off-by: bachy --- entityreference.diff.inc | 90 ++++ entityreference.info | 7 +- entityreference.migrate.inc | 5 +- entityreference.module | 122 ++++- .../entityreference_behavior_example.info | 6 +- ...yReferenceBehavior_TaxonomyIndex.class.php | 189 +++++++ plugins/behavior/abstract.inc | 27 + plugins/behavior/taxonomy-index.inc | 16 + ...ference_SelectionHandler_Generic.class.php | 138 ++--- ...ce_SelectionHandler_Generic.class.php.orig | 489 ------------------ ...Reference_SelectionHandler_Views.class.php | 12 +- tests/entityreference.handlers.test | 201 ++++++- tests/entityreference.taxonomy.test | 115 ++++ views/entityreference_plugin_display.inc | 7 +- 14 files changed, 823 insertions(+), 601 deletions(-) create mode 100644 entityreference.diff.inc create mode 100644 plugins/behavior/EntityReferenceBehavior_TaxonomyIndex.class.php create mode 100644 plugins/behavior/taxonomy-index.inc delete mode 100644 plugins/selection/EntityReference_SelectionHandler_Generic.class.php.orig create mode 100644 tests/entityreference.taxonomy.test diff --git a/entityreference.diff.inc b/entityreference.diff.inc new file mode 100644 index 00000000..5005670d --- /dev/null +++ b/entityreference.diff.inc @@ -0,0 +1,90 @@ + $info) { + $old_items[$delta]['entity'] = isset($entities[$info['target_id']]) ? $entities[$info['target_id']] : NULL; + } + foreach ($new_items as $delta => $info) { + $new_items[$delta]['entity'] = isset($entities[$info['target_id']]) ? $entities[$info['target_id']] : NULL; + } +} + +/** + * Diff field callback for parsing entity field comparative values. + */ +function entityreference_field_diff_view($items, $context) { + $field = $context['field']; + $instance = $context['instance']; + $settings = $context['settings']; + $entity_type = $field['settings']['target_type']; + + $diff_items = array(); + + // We populate as much as possible to allow the best flexability in any + // string overrides. + $t_args = array(); + $t_args['!entity_type'] = $entity_type; + + $entity_info = entity_get_info($entity_type); + $t_args['!entity_type_label'] = $entity_info['label']; + + foreach ($items as $delta => $item) { + if (isset($item['entity'])) { + $output = array(); + + list($id,, $bundle) = entity_extract_ids($entity_type, $item['entity']); + $t_args['!id'] = $id; + $t_args['!bundle'] = $bundle; + $t_args['!diff_entity_label'] = entity_label($entity_type, $item['entity']); + + $output['entity'] = t('!diff_entity_label', $t_args); + if ($settings['show_id']) { + $output['id'] = t('ID: !id', $t_args); + } + $diff_items[$delta] = implode('; ', $output); + } + } + + return $diff_items; +} + +/** + * Provide default field comparison options. + */ +function entityreference_field_diff_default_options($field_type) { + return array( + 'show_id' => 0, + ); +} + +/** + * Provide a form for setting the field comparison options. + */ +function entityreference_field_diff_options_form($field_type, $settings) { + $options_form = array(); + $options_form['show_id'] = array( + '#type' => 'checkbox', + '#title' => t('Show ID'), + '#default_value' => $settings['show_id'], + ); + return $options_form; +} diff --git a/entityreference.info b/entityreference.info index ea0f6640..3aa51aba 100644 --- a/entityreference.info +++ b/entityreference.info @@ -19,11 +19,12 @@ files[] = views/entityreference_plugin_row_fields.inc ; Tests. files[] = tests/entityreference.handlers.test +files[] = tests/entityreference.taxonomy.test files[] = tests/entityreference.admin.test -; Information added by drupal.org packaging script on 2012-09-25 -version = "7.x-1.0-rc5" +; Information added by drupal.org packaging script on 2012-11-18 +version = "7.x-1.0" core = "7.x" project = "entityreference" -datestamp = "1348565045" +datestamp = "1353230808" diff --git a/entityreference.migrate.inc b/entityreference.migrate.inc index 280160f7..79c8f791 100644 --- a/entityreference.migrate.inc +++ b/entityreference.migrate.inc @@ -9,7 +9,10 @@ * Implement hook_migrate_api(). */ function entityreference_migrate_api() { - return array('api' => 2); + return array( + 'api' => 2, + 'field_handlers' => array('MigrateEntityReferenceFieldHandler'), + ); } class MigrateEntityReferenceFieldHandler extends MigrateSimpleFieldHandler { diff --git a/entityreference.module b/entityreference.module index 14e45a42..5f03c5b1 100644 --- a/entityreference.module +++ b/entityreference.module @@ -330,6 +330,49 @@ function entityreference_field_attach_delete($entity_type, $entity) { } } +/** + * Implements hook_entity_insert(). + */ +function entityreference_entity_insert($entity, $entity_type) { + entityreference_entity_crud($entity, $entity_type, 'entityPostInsert'); +} + +/** + * Implements hook_entity_update(). + */ +function entityreference_entity_update($entity, $entity_type) { + entityreference_entity_crud($entity, $entity_type, 'entityPostUpdate'); +} + +/** + * Implements hook_entity_delete(). + */ +function entityreference_entity_delete($entity, $entity_type) { + entityreference_entity_crud($entity, $entity_type, 'entityPostDelete'); +} + +/** + * Invoke a behavior based on entity CRUD. + * + * @param $entity + * The entity object. + * @param $entity_type + * The entity type. + * @param $method_name + * The method to invoke. + */ +function entityreference_entity_crud($entity, $entity_type, $method_name) { + list(, , $bundle) = entity_extract_ids($entity_type, $entity); + foreach (field_info_instances($entity_type, $bundle) as $field_name => $instance) { + $field = field_info_field($field_name); + if ($field['type'] == 'entityreference') { + foreach (entityreference_get_behavior_handlers($field, $instance) as $handler) { + $handler->{$method_name}($entity_type, $entity, $field, $instance); + } + } + } +} + /** * Implements hook_field_settings_form(). */ @@ -719,7 +762,21 @@ function entityreference_field_widget_settings_form($field, $instance) { * Implements hook_options_list(). */ function entityreference_options_list($field, $instance = NULL, $entity_type = NULL, $entity = NULL) { - return entityreference_get_selection_handler($field, $instance, $entity_type, $entity)->getReferencableEntities(); + if (!$options = entityreference_get_selection_handler($field, $instance, $entity_type, $entity)->getReferencableEntities()) { + return array(); + } + + // Rebuild the array, by changing the bundle key into the bundle label. + $target_type = $field['settings']['target_type']; + $entity_info = entity_get_info($target_type); + + $return = array(); + foreach ($options as $bundle => $entity_ids) { + $bundle_label = check_plain($entity_info['bundles'][$bundle]['label']); + $return[$bundle_label] = $entity_ids; + } + + return count($return) == 1 ? reset($return) : $return; } /** @@ -830,6 +887,10 @@ function _entityreference_autocomplete_validate($element, &$form_state, $form) { // autocomplete but filled in a value manually. $field = field_info_field($element['#field_name']); $handler = entityreference_get_selection_handler($field); + $field_name = $element['#field_name']; + $field = field_info_field($field_name); + $instance = field_info_instance($element['#entity_type'], $field_name, $element['#bundle']); + $handler = entityreference_get_selection_handler($field, $instance); $value = $handler->validateAutocompleteInput($element['#value'], $element, $form_state, $form); } } @@ -916,6 +977,31 @@ function entityreference_autocomplete_access_callback($type, $field_name, $entit function entityreference_autocomplete_callback($type, $field_name, $entity_type, $bundle_name, $entity_id = '', $string = '') { $field = field_info_field($field_name); $instance = field_info_instance($entity_type, $field_name, $bundle_name); + + return entityreference_autocomplete_callback_get_matches($type, $field, $instance, $entity_type, $entity_id, $string); +} + +/** + * Return JSON based on given field, instance and string. + * + * This function can be used by other modules that wish to pass a mocked + * definition of the field on instance. + * + * @param $type + * The widget type (i.e. 'single' or 'tags'). + * @param $field + * The field array defintion. + * @param $instance + * The instance array defintion. + * @param $entity_type + * The entity type. + * @param $entity_id + * Optional; The entity ID the entity-reference field is attached to. + * Defaults to ''. + * @param $string + * The label of the entity to query by. + */ +function entityreference_autocomplete_callback_get_matches($type, $field, $instance, $entity_type, $entity_id = '', $string = '') { $matches = array(); $entity = NULL; @@ -925,6 +1011,7 @@ function entityreference_autocomplete_callback($type, $field_name, $entity_type, return MENU_ACCESS_DENIED; } } + $handler = entityreference_get_selection_handler($field, $instance, $entity_type, $entity); if ($type == 'tags') { @@ -946,15 +1033,17 @@ function entityreference_autocomplete_callback($type, $field_name, $entity_type, $entity_labels = $handler->getReferencableEntities($tag_last, $instance['widget']['settings']['match_operator'], 10); // Loop through the products and convert them into autocomplete output. - foreach ($entity_labels as $entity_id => $label) { - $key = "$label ($entity_id)"; - // Strip things like starting/trailing white spaces, line breaks and tags. - $key = preg_replace('/\s\s+/', ' ', str_replace("\n", '', trim(decode_entities(strip_tags($key))))); - // Names containing commas or quotes must be wrapped in quotes. - if (strpos($key, ',') !== FALSE || strpos($key, '"') !== FALSE) { - $key = '"' . str_replace('"', '""', $key) . '"'; + foreach ($entity_labels as $values) { + foreach ($values as $entity_id => $label) { + $key = "$label ($entity_id)"; + // Strip things like starting/trailing white spaces, line breaks and tags. + $key = preg_replace('/\s\s+/', ' ', str_replace("\n", '', trim(decode_entities(strip_tags($key))))); + // Names containing commas or quotes must be wrapped in quotes. + if (strpos($key, ',') !== FALSE || strpos($key, '"') !== FALSE) { + $key = '"' . str_replace('"', '""', $key) . '"'; + } + $matches[$prefix . $key] = '
' . $label . '
'; } - $matches[$prefix . $key] = '
' . $label . '
'; } } @@ -1083,12 +1172,14 @@ function entityreference_field_formatter_prepare_view($entity_type, $entities, $ $rekey = FALSE; foreach ($items[$id] as $delta => $item) { - // Check whether the referenced entity could be loaded and that the user has access to it. - if (isset($target_entities[$item['target_id']]) && entity_access('view', $field['settings']['target_type'], $target_entities[$item['target_id']])) { + // Check whether the referenced entity could be loaded. + if (isset($target_entities[$item['target_id']])) { // Replace the instance value with the term data. $items[$id][$delta]['entity'] = $target_entities[$item['target_id']]; + // Check whether the user has access to the referenced entity. + $items[$id][$delta]['access'] = entity_access('view', $field['settings']['target_type'], $target_entities[$item['target_id']]); } - // Otherwise, unset the instance value, since the entity does not exists or should not be accessible. + // Otherwise, unset the instance value, since the entity does not exist. else { unset($items[$id][$delta]); $rekey = TRUE; @@ -1109,6 +1200,13 @@ function entityreference_field_formatter_view($entity_type, $entity, $field, $in $result = array(); $settings = $display['settings']; + // Rebuild the items list to contain only those with access. + foreach ($items as $key => $item) { + if (empty($item['access'])) { + unset($items[$key]); + } + } + switch ($display['type']) { case 'entityreference_label': $handler = entityreference_get_selection_handler($field, $instance, $entity_type, $entity); diff --git a/examples/entityreference_behavior_example/entityreference_behavior_example.info b/examples/entityreference_behavior_example/entityreference_behavior_example.info index 78ff3d95..813eab36 100644 --- a/examples/entityreference_behavior_example/entityreference_behavior_example.info +++ b/examples/entityreference_behavior_example/entityreference_behavior_example.info @@ -4,9 +4,9 @@ core = 7.x package = Fields dependencies[] = entityreference -; Information added by drupal.org packaging script on 2012-09-25 -version = "7.x-1.0-rc5" +; Information added by drupal.org packaging script on 2012-11-18 +version = "7.x-1.0" core = "7.x" project = "entityreference" -datestamp = "1348565045" +datestamp = "1353230808" diff --git a/plugins/behavior/EntityReferenceBehavior_TaxonomyIndex.class.php b/plugins/behavior/EntityReferenceBehavior_TaxonomyIndex.class.php new file mode 100644 index 00000000..43ac693f --- /dev/null +++ b/plugins/behavior/EntityReferenceBehavior_TaxonomyIndex.class.php @@ -0,0 +1,189 @@ +buildNodeIndex($entity); + } + + /** + * Overrides EntityReference_BehaviorHandler_Abstract::entityPostUpdate(). + * + * Runs after hook_node_update() used by taxonomy module. + */ + public function entityPostUpdate($entity_type, $entity, $field, $instance) { + if ($entity_type != 'node') { + return; + } + + $this->buildNodeIndex($entity); + } + + /** + * Builds and inserts taxonomy index entries for a given node. + * + * The index lists all terms that are related to a given node entity, and is + * therefore maintained at the entity level. + * + * @param $node + * The node object. + * + * @see taxonomy_build_node_index() + */ + protected function buildNodeIndex($node) { + // We maintain a denormalized table of term/node relationships, containing + // only data for current, published nodes. + $status = NULL; + if (variable_get('taxonomy_maintain_index_table', TRUE)) { + // If a node property is not set in the node object when node_save() is + // called, the old value from $node->original is used. + if (!empty($node->original)) { + $status = (int)(!empty($node->status) || (!isset($node->status) && !empty($node->original->status))); + $sticky = (int)(!empty($node->sticky) || (!isset($node->sticky) && !empty($node->original->sticky))); + } + else { + $status = (int)(!empty($node->status)); + $sticky = (int)(!empty($node->sticky)); + } + } + // We only maintain the taxonomy index for published nodes. + if ($status) { + // Collect a unique list of all the term IDs from all node fields. + $tid_all = array(); + foreach (field_info_instances('node', $node->type) as $instance) { + $field_name = $instance['field_name']; + $field = field_info_field($field_name); + if (!empty($field['settings']['target_type']) && $field['settings']['target_type'] == 'taxonomy_term' && $field['storage']['type'] == 'field_sql_storage') { + // If a field value is not set in the node object when node_save() is + // called, the old value from $node->original is used. + if (isset($node->{$field_name})) { + $items = $node->{$field_name}; + } + elseif (isset($node->original->{$field_name})) { + $items = $node->original->{$field_name}; + } + else { + continue; + } + foreach (field_available_languages('node', $field) as $langcode) { + if (!empty($items[$langcode])) { + foreach ($items[$langcode] as $item) { + $tid_all[$item['target_id']] = $item['target_id']; + } + } + } + } + + // Re-calculate the terms added in taxonomy_build_node_index() so + // we can optimize database queries. + $original_tid_all = array(); + if ($field['module'] == 'taxonomy' && $field['storage']['type'] == 'field_sql_storage') { + // If a field value is not set in the node object when node_save() is + // called, the old value from $node->original is used. + if (isset($node->{$field_name})) { + $items = $node->{$field_name}; + } + elseif (isset($node->original->{$field_name})) { + $items = $node->original->{$field_name}; + } + else { + continue; + } + foreach (field_available_languages('node', $field) as $langcode) { + if (!empty($items[$langcode])) { + foreach ($items[$langcode] as $item) { + $original_tid_all[$item['tid']] = $item['tid']; + } + } + } + } + } + + // Insert index entries for all the node's terms, that were not + // already inserted in taxonomy_build_node_index(). + $tid_all = array_diff($tid_all, $original_tid_all); + + // Insert index entries for all the node's terms. + if (!empty($tid_all)) { + $query = db_insert('taxonomy_index')->fields(array('nid', 'tid', 'sticky', 'created')); + foreach ($tid_all as $tid) { + $query->values(array( + 'nid' => $node->nid, + 'tid' => $tid, + 'sticky' => $sticky, + 'created' => $node->created, + )); + } + $query->execute(); + } + } + } + + /** + * Overrides EntityReference_BehaviorHandler_Abstract::settingsForm(). + */ + public function settingsForm($field, $instance) { + $form = array(); + $target = $field['settings']['target_type']; + if ($target != 'taxonomy_term') { + $form['ti-on-terms'] = array( + '#markup' => t('This behavior can only be set when the target type is taxonomy_term, but the target of this field is %target.', array('%target' => $target)), + ); + } + + $entity_type = $instance['entity_type']; + if ($entity_type != 'node') { + $form['ti-on-nodes'] = array( + '#markup' => t('This behavior can only be set when the entity type is node, but the entity type of this instance is %type.', array('%type' => $entity_type)), + ); + } + + if (!variable_get('taxonomy_maintain_index_table', TRUE)) { + $form['ti-disabled'] = array( + '#markup' => t('This core variable "taxonomy_maintain_index_table" is disabled.'), + ); + } + return $form; + } +} diff --git a/plugins/behavior/abstract.inc b/plugins/behavior/abstract.inc index 43b2b362..de827bc0 100644 --- a/plugins/behavior/abstract.inc +++ b/plugins/behavior/abstract.inc @@ -107,6 +107,27 @@ interface EntityReference_BehaviorHandler { */ public function postDelete($entity_type, $entity, $field, $instance); + /** + * Act after inserting an entity. + * + * @see hook_entity_insert() + */ + public function entityPostInsert($entity_type, $entity, $field, $instance); + + /** + * Act after updating an entity. + * + * @see hook_entity_update() + */ + public function entityPostUpdate($entity_type, $entity, $field, $instance); + + /** + * Act after deleting an entity. + * + * @see hook_entity_delete() + */ + public function entityPostDelete($entity_type, $entity, $field, $instance); + /** * Generate a settings form for this handler. */ @@ -167,6 +188,12 @@ abstract class EntityReference_BehaviorHandler_Abstract implements EntityReferen public function postDelete($entity_type, $entity, $field, $instance) {} + public function entityPostInsert($entity_type, $entity, $field, $instance) {} + + public function entityPostUpdate($entity_type, $entity, $field, $instance) {} + + public function entityPostDelete($entity_type, $entity, $field, $instance) {} + public function settingsForm($field, $instance) {} public function access($field, $instance) { diff --git a/plugins/behavior/taxonomy-index.inc b/plugins/behavior/taxonomy-index.inc new file mode 100644 index 00000000..1a29d6e5 --- /dev/null +++ b/plugins/behavior/taxonomy-index.inc @@ -0,0 +1,16 @@ + t('Taxonomy index'), + 'description' => t('Include the term references created by instances of this field carried by node entities in the core {taxonomy_index} table. This will allow various modules to handle them like core term_reference fields.'), + 'class' => 'EntityReferenceBehavior_TaxonomyIndex', + 'behavior type' => 'instance', + 'force enabled' => TRUE, + ); +} diff --git a/plugins/selection/EntityReference_SelectionHandler_Generic.class.php b/plugins/selection/EntityReference_SelectionHandler_Generic.class.php index 96996b62..2d1c5d72 100644 --- a/plugins/selection/EntityReference_SelectionHandler_Generic.class.php +++ b/plugins/selection/EntityReference_SelectionHandler_Generic.class.php @@ -169,7 +169,8 @@ class EntityReference_SelectionHandler_Generic implements EntityReference_Select if (!empty($results[$entity_type])) { $entities = entity_load($entity_type, array_keys($results[$entity_type])); foreach ($entities as $entity_id => $entity) { - $options[$entity_id] = check_plain($this->getLabel($entity)); + list(,, $bundle) = entity_extract_ids($entity_type, $entity); + $options[$bundle][$entity_id] = check_plain($this->getLabel($entity)); } } @@ -305,6 +306,42 @@ class EntityReference_SelectionHandler_Generic implements EntityReference_Select public function getLabel($entity) { return entity_label($this->field['settings']['target_type'], $entity); } + + /** + * Ensure a base table exists for the query. + * + * If we have a field-only query, we want to assure we have a base-table + * so we can later alter the query in entityFieldQueryAlter(). + * + * @param $query + * The Select query. + * + * @return + * The alias of the base-table. + */ + public function ensureBaseTable(SelectQueryInterface $query) { + $tables = $query->getTables(); + + // Check the current base table. + foreach ($tables as $table) { + if (empty($table['join'])) { + $alias = $table['alias']; + break; + } + } + + if (strpos($alias, 'field_data_') !== 0) { + // The existing base-table is the correct one. + return $alias; + } + + // Join the known base-table. + $target_type = $this->field['settings']['target_type']; + $entity_info = entity_get_info($target_type); + $id = $entity_info['entity keys']['id']; + // Return the alias of the table. + return $query->innerJoin($target_type, NULL, "$target_type.$id = $alias.entity_id"); + } } /** @@ -320,8 +357,8 @@ class EntityReference_SelectionHandler_Generic_node extends EntityReference_Sele // modules in use on the site. As long as one access control module is there, // it is supposed to handle this check. if (!user_access('bypass node access') && !count(module_implements('node_grants'))) { - $tables = $query->getTables(); - $query->condition(key($tables) . '.status', NODE_PUBLISHED); + $base_table = $this->ensureBaseTable($query); + $query->condition("$base_table.status", NODE_PUBLISHED); } } } @@ -332,27 +369,6 @@ class EntityReference_SelectionHandler_Generic_node extends EntityReference_Sele * This only exists to workaround core bugs. */ class EntityReference_SelectionHandler_Generic_user extends EntityReference_SelectionHandler_Generic { - /** - * Implements EntityReferenceHandler::settingsForm(). - */ - public static function settingsForm($field, $instance) { - $settings = $field['settings']['handler_settings']; - $form = parent::settingsForm($field, $instance); - $form['referenceable_roles'] = array( - '#type' => 'checkboxes', - '#title' => t('User roles that can be referenced'), - '#default_value' => isset($settings['referenceable_roles']) ? array_filter($settings['referenceable_roles']) : array(), - '#options' => user_roles(TRUE), - ); - $form['referenceable_status'] = array( - '#type' => 'checkboxes', - '#title' => t('User status that can be referenced'), - '#default_value' => isset($settings['referenceable_status']) ? array_filter($settings['referenceable_status']) : array('active' => 'active'), - '#options' => array('active' => t('Active'), 'blocked' => t('Blocked')), - ); - return $form; - } - public function buildEntityFieldQuery($match = NULL, $match_operator = 'CONTAINS') { $query = parent::buildEntityFieldQuery($match, $match_operator); @@ -361,33 +377,21 @@ class EntityReference_SelectionHandler_Generic_user extends EntityReference_Sele $query->propertyCondition('name', $match, $match_operator); } - $field = $this->field; - $settings = $field['settings']['handler_settings']; - $referenceable_roles = isset($settings['referenceable_roles']) ? array_filter($settings['referenceable_roles']) : array(); - $referenceable_status = isset($settings['referenceable_status']) ? array_filter($settings['referenceable_status']) : array('active' => 'active'); - - // If this filter is not filled, use the users access permissions. - if (empty($referenceable_status)) { - // Adding the 'user_access' tag is sadly insufficient for users: core - // requires us to also know about the concept of 'blocked' and 'active'. - if (!user_access('administer users')) { - $query->propertyCondition('status', 1); - } + // Adding the 'user_access' tag is sadly insufficient for users: core + // requires us to also know about the concept of 'blocked' and + // 'active'. + if (!user_access('administer users')) { + $query->propertyCondition('status', 1); } - elseif (count($referenceable_status) == 1) { - $values = array('active' => 1, 'blocked' => 0); - $query->propertyCondition('status', $values[key($referenceable_status)]); - } - return $query; } public function entityFieldQueryAlter(SelectQueryInterface $query) { - $conditions = &$query->conditions(); if (user_access('administer users')) { - // If the user is administrator, we need to make sure to + // In addition, if the user is administrator, we need to make sure to // match the anonymous user, that doesn't actually have a name in the // database. + $conditions = &$query->conditions(); foreach ($conditions as $key => $condition) { if ($key !== '#conjunction' && is_string($condition['field']) && $condition['field'] === 'users.name') { // Remove the condition. @@ -414,19 +418,6 @@ class EntityReference_SelectionHandler_Generic_user extends EntityReference_Sele } } } - - $field = $this->field; - $settings = $field['settings']['handler_settings']; - $referenceable_roles = isset($settings['referenceable_roles']) ? array_filter($settings['referenceable_roles']) : array(); - if (!$referenceable_roles || !empty($referenceable_roles[DRUPAL_AUTHENTICATED_RID])) { - // Return early if "authenticated user" choosen. - return; - } - - if (!isset($referenceable_roles[DRUPAL_AUTHENTICATED_RID])) { - $query->join('users_roles', 'users_roles', 'users.uid = users_roles.uid'); - $query->condition('users_roles.rid', array_keys($referenceable_roles), 'IN'); - } } } @@ -441,8 +432,8 @@ class EntityReference_SelectionHandler_Generic_comment extends EntityReference_S // requires us to also know about the concept of 'published' and // 'unpublished'. if (!user_access('administer comments')) { - $tables = $query->getTables(); - $query->condition(key($tables) . '.status', COMMENT_PUBLISHED); + $base_table = $this->ensureBaseTable($query); + $query->condition("$base_table.status", COMMENT_PUBLISHED); } // The Comment module doesn't implement any proper comment access, @@ -514,8 +505,7 @@ class EntityReference_SelectionHandler_Generic_taxonomy_term extends EntityRefer // The Taxonomy module doesn't implement any proper taxonomy term access, // and as a consequence doesn't make sure that taxonomy terms cannot be viewed // when the user doesn't have access to the vocabulary. - $tables = $query->getTables(); - $base_table = key($tables); + $base_table = $this->ensureBaseTable($query); $vocabulary_alias = $query->innerJoin('taxonomy_vocabulary', 'n', '%alias.vid = ' . $base_table . '.vid'); $query->addMetadata('base_table', $vocabulary_alias); // Pass the query to the taxonomy access control. @@ -532,4 +522,32 @@ class EntityReference_SelectionHandler_Generic_taxonomy_term extends EntityRefer } } } + + /** + * Implements EntityReferenceHandler::getReferencableEntities(). + */ + public function getReferencableEntities($match = NULL, $match_operator = 'CONTAINS', $limit = 0) { + if ($match || $limit) { + return parent::getReferencableEntities($match , $match_operator, $limit); + } + + $options = array(); + $entity_type = $this->field['settings']['target_type']; + + // We imitate core by calling taxonomy_get_tree(). + $entity_info = entity_get_info('taxonomy_term'); + $bundles = !empty($this->field['settings']['handler_settings']['target_bundles']) ? $this->field['settings']['handler_settings']['target_bundles'] : array_keys($entity_info['bundles']); + + foreach ($bundles as $bundle) { + if ($vocabulary = taxonomy_vocabulary_machine_name_load($bundle)) { + if ($terms = taxonomy_get_tree($vocabulary->vid, 0)) { + foreach ($terms as $term) { + $options[$vocabulary->machine_name][$term->tid] = str_repeat('-', $term->depth) . check_plain($term->name); + } + } + } + } + + return $options; + } } diff --git a/plugins/selection/EntityReference_SelectionHandler_Generic.class.php.orig b/plugins/selection/EntityReference_SelectionHandler_Generic.class.php.orig deleted file mode 100644 index 9c342580..00000000 --- a/plugins/selection/EntityReference_SelectionHandler_Generic.class.php.orig +++ /dev/null @@ -1,489 +0,0 @@ -field = $field; - $this->instance = $instance; - $this->entity_type = $entity_type; - $this->entity = $entity; - } - - /** - * Implements EntityReferenceHandler::settingsForm(). - */ - public static function settingsForm($field, $instance) { - $entity_info = entity_get_info($field['settings']['target_type']); - - // Merge-in default values. - $field['settings']['handler_settings'] += array( - 'target_bundles' => array(), - 'sort' => array( - 'type' => 'none', - ) - ); - - if (!empty($entity_info['entity keys']['bundle'])) { - $bundles = array(); - foreach ($entity_info['bundles'] as $bundle_name => $bundle_info) { - $bundles[$bundle_name] = $bundle_info['label']; - } - - $form['target_bundles'] = array( - '#type' => 'checkboxes', - '#title' => t('Target bundles'), - '#options' => $bundles, - '#default_value' => $field['settings']['handler_settings']['target_bundles'], - '#size' => 6, - '#multiple' => TRUE, - '#description' => t('The bundles of the entity type that can be referenced. Optional, leave empty for all bundles.'), - '#element_validate' => array('_entityreference_element_validate_filter'), - ); - } - else { - $form['target_bundles'] = array( - '#type' => 'value', - '#value' => array(), - ); - } - - $form['sort']['type'] = array( - '#type' => 'select', - '#title' => t('Sort by'), - '#options' => array( - 'none' => t("Don't sort"), - 'property' => t('A property of the base table of the entity'), - 'field' => t('A field attached to this entity'), - ), - '#ajax' => TRUE, - '#limit_validation_errors' => array(), - '#default_value' => $field['settings']['handler_settings']['sort']['type'], - ); - - $form['sort']['settings'] = array( - '#type' => 'container', - '#attributes' => array('class' => array('entityreference-settings')), - '#process' => array('_entityreference_form_process_merge_parent'), - ); - - if ($field['settings']['handler_settings']['sort']['type'] == 'property') { - // Merge-in default values. - $field['settings']['handler_settings']['sort'] += array( - 'property' => NULL, - ); - - $form['sort']['settings']['property'] = array( - '#type' => 'select', - '#title' => t('Sort property'), - '#required' => TRUE, - '#options' => drupal_map_assoc($entity_info['schema_fields_sql']['base table']), - '#default_value' => $field['settings']['handler_settings']['sort']['property'], - ); - } - elseif ($field['settings']['handler_settings']['sort']['type'] == 'field') { - // Merge-in default values. - $field['settings']['handler_settings']['sort'] += array( - 'field' => NULL, - ); - - $fields = array(); - foreach (field_info_instances($field['settings']['target_type']) as $bundle_name => $bundle_instances) { - foreach ($bundle_instances as $instance_name => $instance_info) { - $field_info = field_info_field($instance_name); - foreach ($field_info['columns'] as $column_name => $column_info) { - $fields[$instance_name . ':' . $column_name] = t('@label (column @column)', array('@label' => $instance_info['label'], '@column' => $column_name)); - } - } - } - - $form['sort']['settings']['field'] = array( - '#type' => 'select', - '#title' => t('Sort field'), - '#required' => TRUE, - '#options' => $fields, - '#default_value' => $field['settings']['handler_settings']['sort']['field'], - ); - } - - if ($field['settings']['handler_settings']['sort']['type'] != 'none') { - // Merge-in default values. - $field['settings']['handler_settings']['sort'] += array( - 'direction' => 'ASC', - ); - - $form['sort']['settings']['direction'] = array( - '#type' => 'select', - '#title' => t('Sort direction'), - '#required' => TRUE, - '#options' => array( - 'ASC' => t('Ascending'), - 'DESC' => t('Descending'), - ), - '#default_value' => $field['settings']['handler_settings']['sort']['direction'], - ); - } - - return $form; - } - - /** - * Implements EntityReferenceHandler::getReferencableEntities(). - */ - public function getReferencableEntities($match = NULL, $match_operator = 'CONTAINS', $limit = 0) { - $options = array(); - $entity_type = $this->field['settings']['target_type']; - - $query = $this->buildEntityFieldQuery($match, $match_operator); - if ($limit > 0) { - $query->range(0, $limit); - } - - $results = $query->execute(); - - if (!empty($results[$entity_type])) { - $entities = entity_load($entity_type, array_keys($results[$entity_type])); - foreach ($entities as $entity_id => $entity) { - $options[$entity_id] = check_plain($this->getLabel($entity)); - } - } - - return $options; - } - - /** - * Implements EntityReferenceHandler::countReferencableEntities(). - */ - public function countReferencableEntities($match = NULL, $match_operator = 'CONTAINS') { - $query = $this->buildEntityFieldQuery($match, $match_operator); - return $query - ->count() - ->execute(); - } - - /** - * Implements EntityReferenceHandler::validateReferencableEntities(). - */ - public function validateReferencableEntities(array $ids) { - if ($ids) { - $entity_type = $this->field['settings']['target_type']; - $query = $this->buildEntityFieldQuery(); - $query->entityCondition('entity_id', $ids, 'IN'); - $result = $query->execute(); - if (!empty($result[$entity_type])) { - return array_keys($result[$entity_type]); - } - } - - return array(); - } - - /** - * Implements EntityReferenceHandler::validateAutocompleteInput(). - */ - public function validateAutocompleteInput($input, &$element, &$form_state, $form) { - $entities = $this->getReferencableEntities($input, '=', 6); - if (empty($entities)) { - // Error if there are no entities available for a required field. - form_error($element, t('There are no entities matching "%value"', array('%value' => $input))); - } - elseif (count($entities) > 5) { - // Error if there are more than 5 matching entities. - form_error($element, t('Many entities are called %value. Specify the one you want by appending the id in parentheses, like "@value (@id)"', array( - '%value' => $input, - '@value' => $input, - '@id' => key($entities), - ))); - } - elseif (count($entities) > 1) { - // More helpful error if there are only a few matching entities. - $multiples = array(); - foreach ($entities as $id => $name) { - $multiples[] = $name . ' (' . $id . ')'; - } - form_error($element, t('Multiple entities match this reference; "%multiple"', array('%multiple' => implode('", "', $multiples)))); - } - else { - // Take the one and only matching entity. - return key($entities); - } - } - - /** - * Build an EntityFieldQuery to get referencable entities. - */ - protected function buildEntityFieldQuery($match = NULL, $match_operator = 'CONTAINS') { - $query = new EntityFieldQuery(); - $query->entityCondition('entity_type', $this->field['settings']['target_type']); - if (!empty($this->field['settings']['handler_settings']['target_bundles'])) { - $query->entityCondition('bundle', $this->field['settings']['handler_settings']['target_bundles'], 'IN'); - } - if (isset($match)) { - $entity_info = entity_get_info($this->field['settings']['target_type']); - if (isset($entity_info['entity keys']['label'])) { - $query->propertyCondition($entity_info['entity keys']['label'], $match, $match_operator); - } - } - - // Add a generic entity access tag to the query. - $query->addTag($this->field['settings']['target_type'] . '_access'); - $query->addTag('entityreference'); - $query->addMetaData('field', $this->field); - $query->addMetaData('entityreference_selection_handler', $this); - - // Add the sort option. - if (!empty($this->field['settings']['handler_settings']['sort'])) { - $sort_settings = $this->field['settings']['handler_settings']['sort']; - if ($sort_settings['type'] == 'property') { - $query->propertyOrderBy($sort_settings['property'], $sort_settings['direction']); - } - elseif ($sort_settings['type'] == 'field') { - list($field, $column) = explode(':', $sort_settings['field'], 2); - $query->fieldOrderBy($field, $column, $sort_settings['direction']); - } - } - - return $query; - } - - /** - * Implements EntityReferenceHandler::entityFieldQueryAlter(). - */ - public function entityFieldQueryAlter(SelectQueryInterface $query) { - - } - - /** - * Helper method: pass a query to the alteration system again. - * - * This allow Entity Reference to add a tag to an existing query, to ask - * access control mechanisms to alter it again. - */ - protected function reAlterQuery(SelectQueryInterface $query, $tag, $base_table) { - // Save the old tags and metadata. - // For some reason, those are public. - $old_tags = $query->alterTags; - $old_metadata = $query->alterMetaData; - - $query->alterTags = array($tag => TRUE); - $query->alterMetaData['base_table'] = $base_table; - drupal_alter(array('query', 'query_' . $tag), $query); - - // Restore the tags and metadata. - $query->alterTags = $old_tags; - $query->alterMetaData = $old_metadata; - } - - /** - * Implements EntityReferenceHandler::getLabel(). - */ - public function getLabel($entity) { - return entity_label($this->field['settings']['target_type'], $entity); - } -} - -/** - * Override for the Node type. - * - * This only exists to workaround core bugs. - */ -class EntityReference_SelectionHandler_Generic_node extends EntityReference_SelectionHandler_Generic { - public function entityFieldQueryAlter(SelectQueryInterface $query) { - // Adding the 'node_access' tag is sadly insufficient for nodes: core - // requires us to also know about the concept of 'published' and - // 'unpublished'. We need to do that as long as there are no access control - // modules in use on the site. As long as one access control module is there, - // it is supposed to handle this check. - if (!user_access('bypass node access') && !count(module_implements('node_grants'))) { - $tables = $query->getTables(); - $query->condition(key($tables) . '.status', NODE_PUBLISHED); - } - } -} - -/** - * Override for the User type. - * - * This only exists to workaround core bugs. - */ -class EntityReference_SelectionHandler_Generic_user extends EntityReference_SelectionHandler_Generic { - public function buildEntityFieldQuery($match = NULL, $match_operator = 'CONTAINS') { - $query = parent::buildEntityFieldQuery($match, $match_operator); - - // The user entity doesn't have a label column. - if (isset($match)) { - $query->propertyCondition('name', $match, $match_operator); - } - - // Adding the 'user_access' tag is sadly insufficient for users: core - // requires us to also know about the concept of 'blocked' and - // 'active'. - if (!user_access('administer users')) { - $query->propertyCondition('status', 1); - } - return $query; - } - - public function entityFieldQueryAlter(SelectQueryInterface $query) { - if (user_access('administer users')) { - // In addition, if the user is administrator, we need to make sure to - // match the anonymous user, that doesn't actually have a name in the - // database. - $conditions = &$query->conditions(); - foreach ($conditions as $key => $condition) { - if ($key !== '#conjunction' && is_string($condition['field']) && $condition['field'] === 'users.name') { - // Remove the condition. - unset($conditions[$key]); - - // Re-add the condition and a condition on uid = 0 so that we end up - // with a query in the form: - // WHERE (name LIKE :name) OR (:anonymous_name LIKE :name AND uid = 0) - $or = db_or(); - $or->condition($condition['field'], $condition['value'], $condition['operator']); - // Sadly, the Database layer doesn't allow us to build a condition - // in the form ':placeholder = :placeholder2', because the 'field' - // part of a condition is always escaped. - // As a (cheap) workaround, we separately build a condition with no - // field, and concatenate the field and the condition separately. - $value_part = db_and(); - $value_part->condition('anonymous_name', $condition['value'], $condition['operator']); - $value_part->compile(Database::getConnection(), $query); - $or->condition(db_and() - ->where(str_replace('anonymous_name', ':anonymous_name', (string) $value_part), $value_part->arguments() + array(':anonymous_name' => format_username(user_load(0)))) - ->condition('users.uid', 0) - ); - $query->condition($or); - } - } - } - } -} - -/** - * Override for the Comment type. - * - * This only exists to workaround core bugs. - */ -class EntityReference_SelectionHandler_Generic_comment extends EntityReference_SelectionHandler_Generic { - public function entityFieldQueryAlter(SelectQueryInterface $query) { - // Adding the 'comment_access' tag is sadly insufficient for comments: core - // requires us to also know about the concept of 'published' and - // 'unpublished'. - if (!user_access('administer comments')) { - $tables = $query->getTables(); - $query->condition(key($tables) . '.status', COMMENT_PUBLISHED); - } - - // The Comment module doesn't implement any proper comment access, - // and as a consequence doesn't make sure that comments cannot be viewed - // when the user doesn't have access to the node. - $tables = $query->getTables(); - $base_table = key($tables); - $node_alias = $query->innerJoin('node', 'n', '%alias.nid = ' . $base_table . '.nid'); - // Pass the query to the node access control. - $this->reAlterQuery($query, 'node_access', $node_alias); - - // Alas, the comment entity exposes a bundle, but doesn't have a bundle column - // in the database. We have to alter the query ourself to go fetch the - // bundle. - $conditions = &$query->conditions(); - foreach ($conditions as $key => &$condition) { - if ($key !== '#conjunction' && is_string($condition['field']) && $condition['field'] === 'node_type') { - $condition['field'] = $node_alias . '.type'; - foreach ($condition['value'] as &$value) { - if (substr($value, 0, 13) == 'comment_node_') { - $value = substr($value, 13); - } - } - break; - } - } - - // Passing the query to node_query_node_access_alter() is sadly - // insufficient for nodes. - // @see EntityReferenceHandler_node::entityFieldQueryAlter() - if (!user_access('bypass node access') && !count(module_implements('node_grants'))) { - $query->condition($node_alias . '.status', 1); - } - } -} - -/** - * Override for the File type. - * - * This only exists to workaround core bugs. - */ -class EntityReference_SelectionHandler_Generic_file extends EntityReference_SelectionHandler_Generic { - public function entityFieldQueryAlter(SelectQueryInterface $query) { - // Core forces us to know about 'permanent' vs. 'temporary' files. - $tables = $query->getTables(); - $base_table = key($tables); - $query->condition('status', FILE_STATUS_PERMANENT); - - // Access control to files is a very difficult business. For now, we are not - // going to give it a shot. - // @todo: fix this when core access control is less insane. - return $query; - } - - public function getLabel($entity) { - // The file entity doesn't have a label. More over, the filename is - // sometimes empty, so use the basename in that case. - return $entity->filename !== '' ? $entity->filename : basename($entity->uri); - } -} - -/** - * Override for the Taxonomy term type. - * - * This only exists to workaround core bugs. - */ -class EntityReference_SelectionHandler_Generic_taxonomy_term extends EntityReference_SelectionHandler_Generic { - public function entityFieldQueryAlter(SelectQueryInterface $query) { - // The Taxonomy module doesn't implement any proper taxonomy term access, - // and as a consequence doesn't make sure that taxonomy terms cannot be viewed - // when the user doesn't have access to the vocabulary. - $tables = $query->getTables(); - $base_table = key($tables); - $vocabulary_alias = $query->innerJoin('taxonomy_vocabulary', 'n', '%alias.vid = ' . $base_table . '.vid'); - $query->addMetadata('base_table', $vocabulary_alias); - // Pass the query to the taxonomy access control. - $this->reAlterQuery($query, 'taxonomy_vocabulary_access', $vocabulary_alias); - - // Also, the taxonomy term entity exposes a bundle, but doesn't have a bundle - // column in the database. We have to alter the query ourself to go fetch - // the bundle. - $conditions = &$query->conditions(); - foreach ($conditions as $key => &$condition) { - if ($key !== '#conjunction' && is_string($condition['field']) && $condition['field'] === 'vocabulary_machine_name') { - $condition['field'] = $vocabulary_alias . '.machine_name'; - break; - } - } - } -} diff --git a/plugins/selection/EntityReference_SelectionHandler_Views.class.php b/plugins/selection/EntityReference_SelectionHandler_Views.class.php index 0cdc67ed..1b036a7d 100644 --- a/plugins/selection/EntityReference_SelectionHandler_Views.class.php +++ b/plugins/selection/EntityReference_SelectionHandler_Views.class.php @@ -110,7 +110,17 @@ class EntityReference_SelectionHandler_Views implements EntityReference_Selectio // Get the results. $result = $this->view->execute_display($display_name, $args); } - return $result; + + $return = array(); + if ($result) { + $target_type = $this->field['settings']['target_type']; + $entities = entity_load($target_type, array_keys($result)); + foreach($entities as $entity) { + list($id,, $bundle) = entity_extract_ids($target_type, $entity); + $return[$bundle][$id] = $result[$id]; + } + } + return $return; } /** diff --git a/tests/entityreference.handlers.test b/tests/entityreference.handlers.test index 6c2ceca0..03bc54f4 100644 --- a/tests/entityreference.handlers.test +++ b/tests/entityreference.handlers.test @@ -27,10 +27,18 @@ class EntityReferenceHandlersTestCase extends DrupalWebTestCase { foreach ($tests as $test) { foreach ($test['arguments'] as $arguments) { $result = call_user_func_array(array($handler, 'getReferencableEntities'), $arguments); - $this->assertEqual($result, $test['result'], t('Valid result set returned by @handler.', array('@handler' => $handler_name))); + $this->assertEqual($result, $test['result'], format_string('Valid result set returned by @handler.', array('@handler' => $handler_name))); $result = call_user_func_array(array($handler, 'countReferencableEntities'), $arguments); - $this->assertEqual($result, count($test['result']), t('Valid count returned by @handler.', array('@handler' => $handler_name))); + if (!empty($test['result'])) { + $bundle = key($test['result']); + $count = count($test['result'][$bundle]); + } + else { + $count = 0; + } + + $this->assertEqual($result, $count, format_string('Valid count returned by @handler.', array('@handler' => $handler_name))); } } } @@ -93,8 +101,10 @@ class EntityReferenceHandlersTestCase extends DrupalWebTestCase { array(NULL, 'CONTAINS'), ), 'result' => array( - $nodes['published1']->nid => $node_labels['published1'], - $nodes['published2']->nid => $node_labels['published2'], + 'article' => array( + $nodes['published1']->nid => $node_labels['published1'], + $nodes['published2']->nid => $node_labels['published2'], + ), ), ), array( @@ -103,7 +113,9 @@ class EntityReferenceHandlersTestCase extends DrupalWebTestCase { array('Published1', 'CONTAINS'), ), 'result' => array( - $nodes['published1']->nid => $node_labels['published1'], + 'article' => array( + $nodes['published1']->nid => $node_labels['published1'], + ), ), ), array( @@ -112,7 +124,9 @@ class EntityReferenceHandlersTestCase extends DrupalWebTestCase { array('Published2', 'CONTAINS'), ), 'result' => array( - $nodes['published2']->nid => $node_labels['published2'], + 'article' => array( + $nodes['published2']->nid => $node_labels['published2'], + ), ), ), array( @@ -139,9 +153,11 @@ class EntityReferenceHandlersTestCase extends DrupalWebTestCase { array(NULL, 'CONTAINS'), ), 'result' => array( - $nodes['published1']->nid => $node_labels['published1'], - $nodes['published2']->nid => $node_labels['published2'], - $nodes['unpublished']->nid => $node_labels['unpublished'], + 'article' => array( + $nodes['published1']->nid => $node_labels['published1'], + $nodes['published2']->nid => $node_labels['published2'], + $nodes['unpublished']->nid => $node_labels['unpublished'], + ), ), ), array( @@ -149,7 +165,9 @@ class EntityReferenceHandlersTestCase extends DrupalWebTestCase { array('Node unpublished', 'CONTAINS'), ), 'result' => array( - $nodes['unpublished']->nid => $node_labels['unpublished'], + 'article' => array( + $nodes['unpublished']->nid => $node_labels['unpublished'], + ), ), ), ); @@ -215,8 +233,10 @@ class EntityReferenceHandlersTestCase extends DrupalWebTestCase { array(NULL, 'CONTAINS'), ), 'result' => array( - $users['admin']->uid => $user_labels['admin'], - $users['non_admin']->uid => $user_labels['non_admin'], + 'user' => array( + $users['admin']->uid => $user_labels['admin'], + $users['non_admin']->uid => $user_labels['non_admin'], + ), ), ), array( @@ -225,7 +245,9 @@ class EntityReferenceHandlersTestCase extends DrupalWebTestCase { array('NON_ADMIN', 'CONTAINS'), ), 'result' => array( - $users['non_admin']->uid => $user_labels['non_admin'], + 'user' => array( + $users['non_admin']->uid => $user_labels['non_admin'], + ), ), ), array( @@ -250,10 +272,12 @@ class EntityReferenceHandlersTestCase extends DrupalWebTestCase { array(NULL, 'CONTAINS'), ), 'result' => array( - $users['anonymous']->uid => $user_labels['anonymous'], - $users['admin']->uid => $user_labels['admin'], - $users['non_admin']->uid => $user_labels['non_admin'], - $users['blocked']->uid => $user_labels['blocked'], + 'user' => array( + $users['anonymous']->uid => $user_labels['anonymous'], + $users['admin']->uid => $user_labels['admin'], + $users['non_admin']->uid => $user_labels['non_admin'], + $users['blocked']->uid => $user_labels['blocked'], + ), ), ), array( @@ -261,7 +285,9 @@ class EntityReferenceHandlersTestCase extends DrupalWebTestCase { array('blocked', 'CONTAINS'), ), 'result' => array( - $users['blocked']->uid => $user_labels['blocked'], + 'user' => array( + $users['blocked']->uid => $user_labels['blocked'], + ), ), ), array( @@ -270,7 +296,9 @@ class EntityReferenceHandlersTestCase extends DrupalWebTestCase { array('anonymous', 'CONTAINS'), ), 'result' => array( - $users['anonymous']->uid => $user_labels['anonymous'], + 'user' => array( + $users['anonymous']->uid => $user_labels['anonymous'], + ), ), ), ); @@ -364,7 +392,9 @@ class EntityReferenceHandlersTestCase extends DrupalWebTestCase { array(NULL, 'CONTAINS'), ), 'result' => array( - $comments['published_published']->cid => $comment_labels['published_published'], + 'comment_node_article' => array( + $comments['published_published']->cid => $comment_labels['published_published'], + ), ), ), array( @@ -372,7 +402,9 @@ class EntityReferenceHandlersTestCase extends DrupalWebTestCase { array('Published', 'CONTAINS'), ), 'result' => array( - $comments['published_published']->cid => $comment_labels['published_published'], + 'comment_node_article' => array( + $comments['published_published']->cid => $comment_labels['published_published'], + ), ), ), array( @@ -399,8 +431,10 @@ class EntityReferenceHandlersTestCase extends DrupalWebTestCase { array(NULL, 'CONTAINS'), ), 'result' => array( - $comments['published_published']->cid => $comment_labels['published_published'], - $comments['published_unpublished']->cid => $comment_labels['published_unpublished'], + 'comment_node_article' => array( + $comments['published_published']->cid => $comment_labels['published_published'], + $comments['published_unpublished']->cid => $comment_labels['published_unpublished'], + ), ), ), ); @@ -415,12 +449,127 @@ class EntityReferenceHandlersTestCase extends DrupalWebTestCase { array(NULL, 'CONTAINS'), ), 'result' => array( - $comments['published_published']->cid => $comment_labels['published_published'], - $comments['published_unpublished']->cid => $comment_labels['published_unpublished'], - $comments['unpublished_published']->cid => $comment_labels['unpublished_published'], + 'comment_node_article' => array( + $comments['published_published']->cid => $comment_labels['published_published'], + $comments['published_unpublished']->cid => $comment_labels['published_unpublished'], + $comments['unpublished_published']->cid => $comment_labels['unpublished_published'], + ), ), ), ); $this->assertReferencable($field, $referencable_tests, 'Comment handler (comment + node admin)'); } + + /** + * Assert sorting by field works for non-admins. + * + * Since we are sorting on a field, we need to make sure the base-table + * is added, and access-control is behaving as expected. + */ + public function testSortByField() { + // Add text field to entity, to sort by. + $field_info = array( + 'field_name' => 'field_text', + 'type' => 'text', + 'entity_types' => array('node'), + ); + field_create_field($field_info); + + $instance = array( + 'label' => 'Text Field', + 'field_name' => 'field_text', + 'entity_type' => 'node', + 'bundle' => 'article', + 'settings' => array(), + 'required' => FALSE, + ); + field_create_instance($instance); + + + // Build a fake field instance. + $field = array( + 'translatable' => FALSE, + 'entity_types' => array(), + 'settings' => array( + 'handler' => 'base', + 'target_type' => 'node', + 'handler_settings' => array( + 'target_bundles' => array(), + // Add sorting. + 'sort' => array( + 'type' => 'field', + 'field' => 'field_text:value', + 'direction' => 'DESC', + ), + ), + ), + 'field_name' => 'test_field', + 'type' => 'entityreference', + 'cardinality' => '1', + ); + + // Build a set of test data. + $nodes = array( + 'published1' => (object) array( + 'type' => 'article', + 'status' => 1, + 'title' => 'Node published1 (<&>)', + 'uid' => 1, + 'field_text' => array( + LANGUAGE_NONE => array( + array( + 'value' => 1, + ), + ), + ), + ), + 'published2' => (object) array( + 'type' => 'article', + 'status' => 1, + 'title' => 'Node published2 (<&>)', + 'uid' => 1, + 'field_text' => array( + LANGUAGE_NONE => array( + array( + 'value' => 2, + ), + ), + ), + ), + 'unpublished' => (object) array( + 'type' => 'article', + 'status' => 0, + 'title' => 'Node unpublished (<&>)', + 'uid' => 1, + 'field_text' => array( + LANGUAGE_NONE => array( + array( + 'value' => 3, + ), + ), + ), + ), + ); + + $node_labels = array(); + foreach ($nodes as $key => $node) { + node_save($node); + $node_labels[$key] = check_plain($node->title); + } + + // Test as a non-admin. + $normal_user = $this->drupalCreateUser(array('access content')); + $GLOBALS['user'] = $normal_user; + + $handler = entityreference_get_selection_handler($field); + + // Not only assert the result, but make sure the keys are sorted as + // expected. + $result = $handler->getReferencableEntities(); + $expected_result = array( + $nodes['published2']->nid => $node_labels['published2'], + $nodes['published1']->nid => $node_labels['published1'], + ); + $this->assertIdentical($result['article'], $expected_result, 'Query sorted by field returned expected values for non-admin.'); + } } diff --git a/tests/entityreference.taxonomy.test b/tests/entityreference.taxonomy.test new file mode 100644 index 00000000..6e4afb78 --- /dev/null +++ b/tests/entityreference.taxonomy.test @@ -0,0 +1,115 @@ + 'Entity Reference Taxonomy', + 'description' => 'Tests nodes with reference to terms as indexed.', + 'group' => 'Entity Reference', + ); + } + + public function setUp() { + parent::setUp('entityreference', 'taxonomy'); + + // Create an entity reference field. + $field = array( + 'entity_types' => array('node'), + 'settings' => array( + 'handler' => 'base', + 'target_type' => 'taxonomy_term', + 'handler_settings' => array( + 'target_bundles' => array(), + ), + ), + 'field_name' => 'field_entityreference_term', + 'type' => 'entityreference', + ); + $field = field_create_field($field); + $instance = array( + 'field_name' => 'field_entityreference_term', + 'bundle' => 'article', + 'entity_type' => 'node', + ); + + // Enable the taxonomy-index behavior. + $instance['settings']['behaviors']['taxonomy-index']['status'] = TRUE; + field_create_instance($instance); + + // Create a term reference field. + $field = array( + 'translatable' => FALSE, + 'entity_types' => array('node'), + 'settings' => array( + 'allowed_values' => array( + array( + 'vocabulary' => 'terms', + 'parent' => 0, + ), + ), + ), + 'field_name' => 'field_taxonomy_term', + 'type' => 'taxonomy_term_reference', + ); + $field = field_create_field($field); + $instance = array( + 'field_name' => 'field_taxonomy_term', + 'bundle' => 'article', + 'entity_type' => 'node', + ); + field_create_instance($instance); + + // Create a terms vocobulary. + $vocabulary = new stdClass(); + $vocabulary->name = 'Terms'; + $vocabulary->machine_name = 'terms'; + taxonomy_vocabulary_save($vocabulary); + + // Create term. + for ($i = 1; $i <= 2; $i++) { + $term = new stdClass(); + $term->name = "term $i"; + $term->vid = 1; + taxonomy_term_save($term); + } + } + + /** + * Test referencing a term using entity reference field. + */ + public function testNodeIndex() { + // Asert node insert with reference to term. + $settings = array(); + $settings['type'] = 'article'; + $settings['field_entityreference_term'][LANGUAGE_NONE][0]['target_id'] = 1; + $node = $this->drupalCreateNode($settings); + + $this->assertEqual(taxonomy_select_nodes(1), array($node->nid)); + + // Asert node update with reference to term. + node_save($node); + $this->assertEqual(taxonomy_select_nodes(1), array($node->nid)); + + // Assert node update with reference to term and taxonomy reference to + // another term. + $wrapper = entity_metadata_wrapper('node', $node); + $wrapper->field_taxonomy_term->set(2); + $wrapper->save(); + + $this->assertEqual(taxonomy_select_nodes(1), array($node->nid)); + $this->assertEqual(taxonomy_select_nodes(2), array($node->nid)); + + // Assert node update with reference to term and taxonomy reference to + // same term. + $wrapper->field_taxonomy_term->set(1); + $wrapper->save(); + $this->assertEqual(taxonomy_select_nodes(1), array($node->nid)); + + $wrapper->delete(); + $this->assertFalse(taxonomy_select_nodes(1)); + } + +} diff --git a/views/entityreference_plugin_display.inc b/views/entityreference_plugin_display.inc index 366eb7a5..7a1b768a 100644 --- a/views/entityreference_plugin_display.inc +++ b/views/entityreference_plugin_display.inc @@ -76,12 +76,7 @@ class entityreference_plugin_display extends views_plugin_display { foreach ($style_options['search_fields'] as $field_alias) { if (!empty($field_alias)) { // Get the table and field names for the checked field - if (empty($this->view->field[$field_alias]->field_info)) - $field = $this->view->query->fields[$this->view->field[$field_alias]->field_alias]; - else { - $this->view->query->add_field($this->view->field[$field_alias]->options['table'], $this->view->field[$field_alias]->real_field, $this->view->field[$field_alias]->options['field'], array()); - $field = $this->view->query->fields[$this->view->field[$field_alias]->options['field']]; - } + $field = $this->view->query->fields[$this->view->field[$field_alias]->field_alias]; // Add an OR condition for the field $conditions->condition($field['table'] . '.' . $field['field'], $value, 'LIKE'); }