security update for context link entity_reference

This commit is contained in:
2019-04-23 15:57:22 +02:00
parent 16996e1775
commit 835220fa8b
41 changed files with 1829 additions and 657 deletions
@@ -29,9 +29,9 @@ files[] = tests/entityreference.admin.test
files[] = tests/entityreference.feeds.test
files[] = tests/entityreference.entity_translation.test
; Information added by Drupal.org packaging script on 2016-09-19
version = "7.x-1.2"
; Information added by Drupal.org packaging script on 2017-08-16
version = "7.x-1.5"
core = "7.x"
project = "entityreference"
datestamp = "1474306740"
datestamp = "1502895850"
@@ -163,3 +163,28 @@ function entityreference_update_7002() {
));
}
}
/**
* Implements hook_update_N().
*
* Remove duplicate rows in the taxonomy_index table.
*/
function entityreference_update_7100() {
if (db_table_exists('taxonomy_index')) {
if (db_table_exists('taxonomy_index_tmp')) {
db_drop_table('taxonomy_index_tmp');
}
$tx_schema = drupal_get_schema('taxonomy_index');
db_create_table('taxonomy_index_tmp', $tx_schema);
$select = db_select('taxonomy_index', 'tx');
$select->fields('tx', array('nid', 'tid'));
$select->groupBy('tx.nid');
$select->groupBy('tx.tid');
$select->addExpression('MAX(sticky)', 'sticky');
$select->addExpression('MAX(created)', 'created');
db_insert('taxonomy_index_tmp')->from($select)->execute();
db_drop_table('taxonomy_index');
db_rename_table('taxonomy_index_tmp', 'taxonomy_index');
}
}
@@ -1,5 +1,12 @@
<?php
define('ENTITYREFERENCE_DENIED', '- Restricted access -');
/**
* @file
* Entityreference primary module file.
*/
/**
* Implements hook_ctools_plugin_directory().
*/
@@ -87,6 +94,20 @@ function entityreference_flush_caches() {
variable_set('entityreference:base-tables', $base_tables);
}
/**
* Implements hook_theme().
*/
function entityreference_theme($existing, $type, $theme, $path) {
return array(
'entityreference_label' => array(
'variables' => array('label' => NULL, 'item' => NULL, 'settings' => NULL, 'uri' => NULL),
),
'entityreference_entity_id' => array(
'variables' => array('item' => NULL, 'settings' => NULL),
),
);
}
/**
* Implements hook_menu().
*/
@@ -163,7 +184,7 @@ function entityreference_get_behavior_handlers($field, $instance = NULL) {
/**
* Get the behavior handler for a given entityreference field and instance.
*
* @param $handler
* @param $behavior
* The behavior handler name.
*/
function _entityreference_get_behavior_handler($behavior) {
@@ -400,6 +421,9 @@ function entityreference_field_settings_form($field, $instance, $has_data) {
return $form;
}
/**
* Callback for custom element processing.
*/
function _entityreference_field_settings_process($form, $form_state) {
$field = isset($form_state['entityreference']['field']) ? $form_state['entityreference']['field'] : $form['#field'];
$instance = isset($form_state['entityreference']['instance']) ? $form_state['entityreference']['instance'] : $form['#instance'];
@@ -481,11 +505,17 @@ function _entityreference_field_settings_process($form, $form_state) {
return $form;
}
/**
* Custom callback for ajax processing.
*/
function _entityreference_field_settings_ajax_process($form, $form_state) {
_entityreference_field_settings_ajax_process_element($form, $form);
return $form;
}
/**
* Helper function for custom ajax processing.
*/
function _entityreference_field_settings_ajax_process_element(&$element, $main_form) {
if (isset($element['#ajax']) && $element['#ajax'] === TRUE) {
$element['#ajax'] = array(
@@ -500,6 +530,9 @@ function _entityreference_field_settings_ajax_process_element(&$element, $main_f
}
}
/**
* Custom callback for element processing.
*/
function _entityreference_form_process_merge_parent($element) {
$parents = $element['#parents'];
array_pop($parents);
@@ -507,11 +540,17 @@ function _entityreference_form_process_merge_parent($element) {
return $element;
}
/**
* Helper function to remove blank elements.
*/
function _entityreference_element_validate_filter(&$element, &$form_state) {
$element['#value'] = array_filter($element['#value']);
form_set_value($element, $element['#value'], $form_state);
}
/**
* Implements hook_validate().
*/
function _entityreference_field_settings_validate($form, &$form_state) {
// Store the new values in the form state.
$field = $form['#field'];
@@ -547,6 +586,9 @@ function entityreference_field_instance_settings_form($field, $instance) {
return $form;
}
/**
* Implements hook_field_settings_form().
*/
function _entityreference_field_instance_settings_form($form, $form_state) {
$field = isset($form_state['entityreference']['field']) ? $form_state['entityreference']['field'] : $form['#field'];
$instance = isset($form_state['entityreference']['instance']) ? $form_state['entityreference']['instance'] : $form['#instance'];
@@ -564,6 +606,9 @@ function _entityreference_field_instance_settings_form($form, $form_state) {
return $form;
}
/**
* Implements hook_validate().
*/
function _entityreference_field_instance_settings_validate($form, &$form_state) {
// Store the new values in the form state.
$instance = $form['#instance'];
@@ -795,7 +840,7 @@ function entityreference_query_entityreference_alter(QueryAlterableInterface $qu
function entityreference_field_widget_form(&$form, &$form_state, $field, $instance, $langcode, $items, $delta, $element) {
// Ensure that the entity target type exists before displaying the widget.
$entity_info = entity_get_info($field['settings']['target_type']);
if (empty($entity_info)){
if (empty($entity_info)) {
return;
}
$entity_type = $instance['entity_type'];
@@ -820,7 +865,9 @@ function entityreference_field_widget_form(&$form, &$form_state, $field, $instan
// Build an array of entities ID.
foreach ($items as $item) {
$entity_ids[] = $item['target_id'];
if (isset($item['target_id'])) {
$entity_ids[] = $item['target_id'];
}
}
// Load those entities and loop through them to extract their labels.
@@ -881,6 +928,9 @@ function entityreference_field_widget_form(&$form, &$form_state, $field, $instan
}
}
/**
* Implements hook_validate().
*/
function _entityreference_autocomplete_validate($element, &$form_state, $form) {
// If a value was entered into the autocomplete...
$value = '';
@@ -905,6 +955,9 @@ function _entityreference_autocomplete_validate($element, &$form_state, $form) {
form_set_value($element, $value, $form_state);
}
/**
* Implements hook_validate().
*/
function _entityreference_autocomplete_tags_validate($element, &$form_state, $form) {
$value = array();
// If a value was entered into the autocomplete...
@@ -951,7 +1004,8 @@ function entityreference_field_widget_error($element, $error) {
* The entity type.
* @param $bundle_name
* The bundle name.
* @return
*
* @return bool
* True if user can access this menu item.
*/
function entityreference_autocomplete_access_callback($type, $field_name, $entity_type, $bundle_name) {
@@ -983,10 +1037,11 @@ function entityreference_autocomplete_access_callback($type, $field_name, $entit
*/
function entityreference_autocomplete_callback($type, $field_name, $entity_type, $bundle_name, $entity_id = '', $string = '') {
// If the request has a '/' in the search text, then the menu system will have
// split it into multiple arguments and $string will only be a partial. We want
// to make sure we recover the intended $string.
// split it into multiple arguments and $string will only be a partial.
// We want to make sure we recover the intended $string.
$args = func_get_args();
// Shift off the $type, $field_name, $entity_type, $bundle_name, and $entity_id args.
// Shift off the $type, $field_name, $entity_type,
// $bundle_name, and $entity_id args.
array_shift($args);
array_shift($args);
array_shift($args);
@@ -1022,6 +1077,7 @@ function entityreference_autocomplete_callback($type, $field_name, $entity_type,
*/
function entityreference_autocomplete_callback_get_matches($type, $field, $instance, $entity_type, $entity_id = '', $string = '') {
$matches = array();
$prefix = '';
$entity = NULL;
if ($entity_id !== 'NULL') {
@@ -1036,7 +1092,8 @@ function entityreference_autocomplete_callback_get_matches($type, $field, $insta
$handler = entityreference_get_selection_handler($field, $instance, $entity_type, $entity);
if ($type == 'tags') {
// The user enters a comma-separated list of tags. We only autocomplete the last tag.
// The user enters a comma-separated list of tags.
// We only autocomplete the last tag.
$tags_typed = drupal_explode_tags($string);
$tag_last = drupal_strtolower(array_pop($tags_typed));
if (!empty($tag_last)) {
@@ -1045,19 +1102,22 @@ function entityreference_autocomplete_callback_get_matches($type, $field, $insta
}
else {
// The user enters a single tag.
$prefix = '';
$tag_last = $string;
}
if (isset($tag_last)) {
// Get an array of matching entities.
$entity_labels = $handler->getReferencableEntities($tag_last, $instance['widget']['settings']['match_operator'], 10);
$denied_label = t(ENTITYREFERENCE_DENIED);
// Loop through the products and convert them into autocomplete output.
foreach ($entity_labels as $values) {
foreach ($values as $entity_id => $label) {
// Never autocomplete entities that aren't accessible.
if ($label == $denied_label) {
continue;
}
$key = "$label ($entity_id)";
// Strip things like starting/trailing white spaces, line breaks and tags.
// Strip 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) {
@@ -1071,6 +1131,32 @@ function entityreference_autocomplete_callback_get_matches($type, $field, $insta
drupal_json_output($matches);
}
/**
* Introspects field and instance settings, and determines the correct settings
* for the functioning of the formatter.
*
* Settings:
* - entity_type - The entity_type being loaded.
* - column - The name of the ref. field column that stores the entity id.
*/
function entityreference_field_type_settings($field) {
$settings = array(
'entity_type' => NULL,
'column' => NULL,
);
if ($field['type'] == 'entityreference') {
$settings['entity_type'] = $field['settings']['target_type'];
$settings['column'] = 'target_id';
}
elseif ($field['type'] == 'taxonomy_term_reference') {
$settings['entity_type'] = 'taxonomy_term';
$settings['column'] = 'tid';
}
return $settings;
}
/**
* Implements hook_field_formatter_info().
*/
@@ -1082,6 +1168,7 @@ function entityreference_field_formatter_info() {
'field types' => array('entityreference'),
'settings' => array(
'link' => FALSE,
'bypass_access' => FALSE,
),
),
'entityreference_entity_id' => array(
@@ -1092,7 +1179,7 @@ function entityreference_field_formatter_info() {
'entityreference_entity_view' => array(
'label' => t('Rendered entity'),
'description' => t('Display the referenced entities rendered by entity_view().'),
'field types' => array('entityreference'),
'field types' => array('entityreference', 'taxonomy_term_reference'),
'settings' => array(
'view_mode' => 'default',
'links' => TRUE,
@@ -1108,9 +1195,17 @@ function entityreference_field_formatter_info() {
function entityreference_field_formatter_settings_form($field, $instance, $view_mode, $form, &$form_state) {
$display = $instance['display'][$view_mode];
$settings = $display['settings'];
$field_type_settings = entityreference_field_type_settings($field);
$element = array();
if ($display['type'] == 'entityreference_label') {
$element['bypass_access'] = array(
'#title' => t('Show entity labels regardless of user access'),
'#description' => t("All entities in the field will be shown, without checking them for access. If the 'Link' setting is also enabled, an entity which the user does not have access to view will show without a link."),
'#type' => 'checkbox',
'#default_value' => $settings['bypass_access'],
);
$element['link'] = array(
'#title' => t('Link label to the referenced entity'),
'#type' => 'checkbox',
@@ -1119,7 +1214,7 @@ function entityreference_field_formatter_settings_form($field, $instance, $view_
}
if ($display['type'] == 'entityreference_entity_view') {
$entity_info = entity_get_info($field['settings']['target_type']);
$entity_info = entity_get_info($field_type_settings['entity_type']);
$options = array('default' => t('Default'));
if (!empty($entity_info['view modes'])) {
foreach ($entity_info['view modes'] as $view_mode => $view_mode_settings) {
@@ -1157,15 +1252,17 @@ function entityreference_field_formatter_settings_form($field, $instance, $view_
function entityreference_field_formatter_settings_summary($field, $instance, $view_mode) {
$display = $instance['display'][$view_mode];
$settings = $display['settings'];
$field_type_settings = entityreference_field_type_settings($field);
$summary = array();
if ($display['type'] == 'entityreference_label') {
$summary[] = $settings['link'] ? t('Link to the referenced entity') : t('No link');
$summary[] = $settings['bypass_access'] ? t('Show labels regardless of access') : t('Respect entity access for label visibility');
}
if ($display['type'] == 'entityreference_entity_view') {
$entity_info = entity_get_info($field['settings']['target_type']);
$entity_info = entity_get_info($field_type_settings['entity_type']);
$view_mode_label = $settings['view_mode'] == 'default' ? t('Default') : $settings['view_mode'];
if (isset($entity_info['view modes'][$settings['view_mode']]['label'])) {
$view_mode_label = $entity_info['view modes'][$settings['view_mode']]['label'];
@@ -1182,19 +1279,22 @@ function entityreference_field_formatter_settings_summary($field, $instance, $vi
* Implements hook_field_formatter_prepare_view().
*/
function entityreference_field_formatter_prepare_view($entity_type, $entities, $field, $instances, $langcode, &$items, $displays) {
$field_type_settings = entityreference_field_type_settings($field);
$target_type = $field_type_settings['entity_type'];
$column = $field_type_settings['column'];
$target_ids = array();
// Collect every possible entity attached to any of the entities.
foreach ($entities as $id => $entity) {
foreach ($items[$id] as $delta => $item) {
if (isset($item['target_id'])) {
$target_ids[] = $item['target_id'];
if (isset($item[$column])) {
$target_ids[] = $item[$column];
}
}
}
if ($target_ids) {
$target_entities = entity_load($field['settings']['target_type'], $target_ids);
$target_entities = entity_load($target_type, $target_ids);
}
else {
$target_entities = array();
@@ -1206,12 +1306,12 @@ function entityreference_field_formatter_prepare_view($entity_type, $entities, $
foreach ($items[$id] as $delta => $item) {
// Check whether the referenced entity could be loaded.
if (isset($item['target_id']) && isset($target_entities[$item['target_id']])) {
if (isset($target_entities[$item[$column]]) && isset($target_entities[$item[$column]])) {
// Replace the instance value with the term data.
$items[$id][$delta]['entity'] = $target_entities[$item['target_id']];
$items[$id][$delta]['entity'] = $target_entities[$item[$column]];
// Check whether the user has access to the referenced entity.
$has_view_access = (entity_access('view', $field['settings']['target_type'], $target_entities[$item['target_id']]) !== FALSE);
$has_update_access = (entity_access('update', $field['settings']['target_type'], $target_entities[$item['target_id']]) !== FALSE);
$has_view_access = (entity_access('view', $target_type, $target_entities[$item[$column]]) !== FALSE);
$has_update_access = (entity_access('update', $target_type, $target_entities[$item[$column]]) !== FALSE);
$items[$id][$delta]['access'] = ($has_view_access || $has_update_access);
}
// Otherwise, unset the instance value, since the entity does not exist.
@@ -1234,34 +1334,63 @@ function entityreference_field_formatter_prepare_view($entity_type, $entities, $
function entityreference_field_formatter_view($entity_type, $entity, $field, $instance, $langcode, $items, $display) {
$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]);
}
}
$field_type_settings = entityreference_field_type_settings($field);
$target_type = $field_type_settings['entity_type'];
$column = $field_type_settings['column'];
switch ($display['type']) {
case 'entityreference_label':
$handler = entityreference_get_selection_handler($field, $instance, $entity_type, $entity);
foreach ($items as $delta => $item) {
$label = $handler->getLabel($item['entity']);
// If the link is to be displayed and the entity has a uri, display a link.
// Note the assignment ($url = ) here is intended to be an assignment.
if ($display['settings']['link'] && ($uri = entity_uri($field['settings']['target_type'], $item['entity']))) {
$result[$delta] = array('#markup' => l($label, $uri['path'], $uri['options']));
// Skip an item that is not accessible, unless we're allowing output of
// entity labels without considering access.
if (empty($item['access']) && !$display['settings']['bypass_access']) {
continue;
}
else {
$result[$delta] = array('#markup' => check_plain($label));
// Calling EntityReferenceHandler::getLabel() would make a repeated,
// wasteful call to entity_access().
$label = entity_label($field['settings']['target_type'], $item['entity']);
// Check if the settings and access allow a link to be displayed.
$display_link = $display['settings']['link'] && $item['access'];
$uri = NULL;
// If the link is allowed and the entity has a uri, display a link.
if ($display_link) {
$uri = entity_uri($target_type, $item['entity']);
}
$result[$delta] = array(
'#theme' => 'entityreference_label',
'#label' => $label,
'#item' => $item,
'#uri' => $uri,
'#settings' => array(
'display' => $display['settings'],
'field' => $field['settings'],
),
);
}
break;
case 'entityreference_entity_id':
foreach ($items as $delta => $item) {
$result[$delta] = array('#markup' => check_plain($item['target_id']));
// Skip an item that is not accessible.
if (empty($item['access'])) {
continue;
}
$result[$delta] = array(
'#theme' => 'entityreference_entity_id',
'#item' => $item,
'#settings' => array(
'display' => $display['settings'],
'field' => $field['settings'],
),
);
}
break;
@@ -1272,19 +1401,24 @@ function entityreference_field_formatter_view($entity_type, $entity, $field, $in
}
foreach ($items as $delta => $item) {
// Skip an item that is not accessible.
if (empty($item['access'])) {
continue;
}
// Protect ourselves from recursive rendering.
static $depth = 0;
$depth++;
if ($depth > 20) {
throw new EntityReferenceRecursiveRenderingException(t('Recursive rendering detected when rendering entity @entity_type(@entity_id). Aborting rendering.', array('@entity_type' => $entity_type, '@entity_id' => $item['target_id'])));
throw new EntityReferenceRecursiveRenderingException(t('Recursive rendering detected when rendering entity @entity_type(@entity_id). Aborting rendering.', array('@entity_type' => $target_type, '@entity_id' => $item[$column])));
}
$target_entity = clone $item['entity'];
unset($target_entity->content);
$result[$delta] = entity_view($field['settings']['target_type'], array($item['target_id'] => $target_entity), $settings['view_mode'], $target_langcode, FALSE);
$result[$delta] = entity_view($target_type, array($item[$column] => $target_entity), $settings['view_mode'], $target_langcode, FALSE);
if (empty($settings['links']) && isset($result[$delta][$field['settings']['target_type']][$item['target_id']]['links'])) {
$result[$delta][$field['settings']['target_type']][$item['target_id']]['links']['#access'] = FALSE;
if (empty($settings['links']) && isset($result[$delta][$target_type][$column]['links'])) {
$result[$delta][$target_type][$item[$column]]['links']['#access'] = FALSE;
}
$depth = 0;
}
@@ -1308,3 +1442,44 @@ function entityreference_views_api() {
'path' => drupal_get_path('module', 'entityreference') . '/views',
);
}
/**
* Theme label.
*
* @ingroup themeable.
*/
function theme_entityreference_label($vars) {
$label = $vars['label'];
$settings = $vars['settings'];
$item = $vars['item'];
$uri = $vars['uri'];
$output = '';
// If the link is to be displayed and the entity has a uri, display a link.
// Note the assignment ($url = ) here is intended to be an assignment.
if ($settings['display']['link'] && isset($uri['path'])) {
$output .= l($label, $uri['path'], $uri['options']);
}
else {
$output .= check_plain($label);
}
return $output;
}
/**
* Theme entity_id
*
* @ingroup themeable.
*/
function theme_entityreference_entity_id($vars) {
$settings = $vars['settings'];
$item = $vars['item'];
$output = '';
$output = check_plain($item['target_id']);
return $output;
}
@@ -4,9 +4,9 @@ core = 7.x
package = Fields
dependencies[] = entityreference
; Information added by Drupal.org packaging script on 2016-09-19
version = "7.x-1.2"
; Information added by Drupal.org packaging script on 2017-08-16
version = "7.x-1.5"
core = "7.x"
project = "entityreference"
datestamp = "1474306740"
datestamp = "1502895850"
@@ -144,18 +144,20 @@ class EntityReferenceBehavior_TaxonomyIndex extends EntityReference_BehaviorHand
// 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.
// Insert index entries for all the node's terms, preventing duplicates.
if (!empty($tid_all)) {
$query = db_insert('taxonomy_index')->fields(array('nid', 'tid', 'sticky', 'created'));
foreach ($tid_all as $tid) {
$query->values(array(
$row = array(
'nid' => $node->nid,
'tid' => $tid,
'sticky' => $sticky,
'created' => $node->created,
));
);
$query = db_merge('taxonomy_index')
->key($row)
->fields($row);
$query->execute();
}
$query->execute();
}
}
}
@@ -208,7 +208,11 @@ class EntityReference_SelectionHandler_Generic implements EntityReference_Select
* Implements EntityReferenceHandler::validateAutocompleteInput().
*/
public function validateAutocompleteInput($input, &$element, &$form_state, $form) {
$entities = $this->getReferencableEntities($input, '=', 6);
$bundled_entities = $this->getReferencableEntities($input, '=', 6);
$entities = array();
foreach($bundled_entities as $entities_list) {
$entities += $entities_list;
}
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)));
@@ -305,7 +309,7 @@ class EntityReference_SelectionHandler_Generic implements EntityReference_Select
*/
public function getLabel($entity) {
$target_type = $this->field['settings']['target_type'];
return entity_access('view', $target_type, $entity) ? entity_label($target_type, $entity) : t('- Restricted access -');
return entity_access('view', $target_type, $entity) ? entity_label($target_type, $entity) : t(ENTITYREFERENCE_DENIED);
}
/**
@@ -9,12 +9,13 @@ class EntityReference_SelectionHandler_Views implements EntityReference_Selectio
* Implements EntityReferenceHandler::getInstance().
*/
public static function getInstance($field, $instance = NULL, $entity_type = NULL, $entity = NULL) {
return new EntityReference_SelectionHandler_Views($field, $instance);
return new EntityReference_SelectionHandler_Views($field, $instance, $entity);
}
protected function __construct($field, $instance) {
protected function __construct($field, $instance, $entity) {
$this->field = $field;
$this->instance = $instance;
$this->entity = $entity;
}
/**
@@ -52,13 +53,32 @@ class EntityReference_SelectionHandler_Views implements EntityReference_Selectio
);
$default = !empty($view_settings['args']) ? implode(', ', $view_settings['args']) : '';
$description = t('Provide a comma separated list of arguments to pass to the view.') . '<br />' . t('This field supports tokens.');
if (!module_exists('token')) {
$description .= '<br>' . t('Install the <a href="@url">token module</a> to get more tokens and display available once.', array('@url' => 'http://drupal.org/project/token'));
}
$form['view']['args'] = array(
'#type' => 'textfield',
'#title' => t('View arguments'),
'#default_value' => $default,
'#required' => FALSE,
'#description' => t('Provide a comma separated list of arguments to pass to the view.'),
'#description' => $description,
'#maxlength' => '512',
);
if (module_exists('token')) {
// Get the token type for the entity type our field is in (a type 'taxonomy_term' has a 'term' type token).
$info = entity_get_info($instance['entity_type']);
$form['view']['tokens'] = array(
'#theme' => 'token_tree',
'#token_types' => array($info['token type']),
'#global_types' => TRUE,
'#click_insert' => TRUE,
'#dialog' => TRUE,
);
}
}
else {
$form['view']['no_view_help'] = array(
@@ -84,6 +104,7 @@ class EntityReference_SelectionHandler_Views implements EntityReference_Selectio
return FALSE;
}
$this->view->set_display($display_name);
$this->view->pre_execute();
// Make sure the query is not cached.
$this->view->is_cacheable = FALSE;
@@ -104,7 +125,7 @@ class EntityReference_SelectionHandler_Views implements EntityReference_Selectio
*/
public function getReferencableEntities($match = NULL, $match_operator = 'CONTAINS', $limit = 0) {
$display_name = $this->field['settings']['handler_settings']['view']['display_name'];
$args = $this->field['settings']['handler_settings']['view']['args'];
$args = $this->handleArgs($this->field['settings']['handler_settings']['view']['args']);
$result = array();
if ($this->initializeView($match, $match_operator, $limit)) {
// Get the results.
@@ -133,7 +154,7 @@ class EntityReference_SelectionHandler_Views implements EntityReference_Selectio
function validateReferencableEntities(array $ids) {
$display_name = $this->field['settings']['handler_settings']['view']['display_name'];
$args = $this->field['settings']['handler_settings']['view']['args'];
$args = $this->handleArgs($this->field['settings']['handler_settings']['view']['args']);
$result = array();
if ($this->initializeView(NULL, 'CONTAINS', 0, $ids)) {
// Get the results.
@@ -166,6 +187,49 @@ class EntityReference_SelectionHandler_Views implements EntityReference_Selectio
}
/**
* Handles arguments for views.
*
* Replaces tokens using token_replace().
*
* @param array $args
* Usually $this->field['settings']['handler_settings']['view']['args'].
*
* @return array
* The arguments to be send to the View.
*/
protected function handleArgs($args) {
if (!module_exists('token')) {
return $args;
}
// Parameters for token_replace().
$data = array();
$options = array('clear' => TRUE);
if ($entity = $this->entity) {
// D7 HACK: For new entities, entity and revision id are not set. This leads to
// * token replacement emitting PHP warnings
// * views choking on empty arguments
// We workaround this by filling in '0' for these IDs
// and use a clone to leave no traces of our unholy doings.
$info = entity_get_info($this->instance['entity_type']);
if (!isset($entity->{$info['entity keys']['id']})) {
$entity = clone $entity;
$entity->{$info['entity keys']['id']} = '0';
if (!empty($info['entity keys']['revision'])) {
$entity->{$info['entity keys']['revision']} = '0';
}
}
$data[$info['token type']] = $entity;
}
// Replace tokens for each argument.
foreach ($args as $key => $arg) {
$args[$key] = token_replace($arg, $data, $options);
}
return $args;
}
}
function entityreference_view_settings_validate($element, &$form_state, $form) {
@@ -21,8 +21,9 @@ interface EntityReference_SelectionHandler {
* Return a list of referencable entities.
*
* @return
* An array of referencable entities, which keys are entity ids and
* values (safe HTML) labels to be displayed to the user.
* A nested array of entities, the first level is keyed by the
* entity bundle, which contains an array of entity labels (safe HTML),
* keyed by the entity ID.
*/
public function getReferencableEntities($match = NULL, $match_operator = 'CONTAINS', $limit = 0);
@@ -68,7 +68,7 @@ class EntityReferenceAdminTestCase extends DrupalWebTestCase {
'fields[_add_new_field][type]' => 'entityreference',
'fields[_add_new_field][widget_type]' => 'entityreference_autocomplete',
), t('Save'));
return;
// Node should be selected by default.
$this->assertFieldByName('field[settings][target_type]', 'node');
// The base handler should be selected by default.
@@ -194,6 +194,21 @@ class EntityReferenceHandlersTestCase extends DrupalWebTestCase {
),
);
$this->assertReferencable($field, $referencable_tests, 'Node handler (admin)');
// Verify autocomplete input validation.
$handler = entityreference_get_selection_handler($field);
$element = array(
'#parents' => array('element_name'),
);
$form_state = array();
$form = array();
$value = $handler->validateAutocompleteInput($nodes['published1']->title, $element, $form_state, $form);
$this->assertEqual($value, $nodes['published1']->nid);
$invalid_input = $this->randomName();
$value = $handler->validateAutocompleteInput($invalid_input, $element, $form_state, $form);
$this->assertNull($value);
$this->assertEqual(form_get_error($element), t('There are no entities matching "%value"', array('%value' => $invalid_input)));
}
/**
@@ -256,7 +271,7 @@ class EntityReferenceHandlersTestCase extends DrupalWebTestCase {
),
'result' => array(
'user' => array(
$users['admin']->uid => '- Restricted access -',
$users['admin']->uid => ENTITYREFERENCE_DENIED,
$users['non_admin']->uid => $user_labels['non_admin'],
),
),
@@ -112,4 +112,52 @@ class EntityReferenceTaxonomyTestCase extends DrupalWebTestCase {
$this->assertFalse(taxonomy_select_nodes(1));
}
/**
* Add a second ER field from node/article to taxonomy.
*
* This should not cause {taxonomy_index} to receive duplicate entries.
*/
protected function setupForIndexDuplicates() {
// 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_term2',
'type' => 'entityreference',
);
$field = field_create_field($field);
$instance = array(
'field_name' => 'field_entityreference_term2',
'bundle' => 'article',
'entity_type' => 'node',
);
// Enable the taxonomy-index behavior.
$instance['settings']['behaviors']['taxonomy-index']['status'] = TRUE;
field_create_instance($instance);
}
/**
* Make sure the index only contains one entry for a given node->term
* reference, even when multiple ER fields link from the node bundle to terms.
*/
public function testIndexDuplicates() {
// Extra setup for this test: add another ER field on this content type.
$this->setupForIndexDuplicates();
// Assert node insert with reference to term in first field.
$tid = 1;
$settings = array();
$settings['type'] = 'article';
$settings['field_entityreference_term'][LANGUAGE_NONE][0]['target_id'] = $tid;
$node = $this->drupalCreateNode($settings);
$this->assertEqual(taxonomy_select_nodes($tid), array($node->nid));
}
}
@@ -8,9 +8,9 @@ dependencies[] = feeds
dependencies[] = feeds_ui
dependencies[] = entityreference
; Information added by Drupal.org packaging script on 2016-09-19
version = "7.x-1.2"
; Information added by Drupal.org packaging script on 2017-08-16
version = "7.x-1.5"
core = "7.x"
project = "entityreference"
datestamp = "1474306740"
datestamp = "1502895850"
@@ -81,8 +81,9 @@ class entityreference_plugin_display extends views_plugin_display {
$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_table = $this->view->query->ensure_table($this->view->field[$field_alias]->table, $this->view->field[$field_alias]->relationship);
$this->view->query->add_field($field_table, $this->view->field[$field_alias]->real_field, $this->view->field[$field_alias]->field, array());
$field = $this->view->query->fields[$this->view->field[$field_alias]->field];
}
// Add an OR condition for the field
$conditions->condition($field['table'] . '.' . $field['field'], $value, 'LIKE');
@@ -26,7 +26,7 @@ class entityreference_plugin_style extends views_plugin_style {
'#title' => t('Search fields'),
'#options' => $options,
'#required' => TRUE,
'#default_value' => $this->options['search_fields'],
'#default_value' => isset($this->options['search_fields']) ? $this->options['search_fields'] : array(),
'#description' => t('Select the field(s) that will be searched when using the autocomplete widget.'),
'#weight' => -3,
);