security upadtes

This commit is contained in:
Bachir Soussi Chiadmi
2017-09-25 15:16:35 +02:00
parent 650c6448e4
commit 8d8a60b615
240 changed files with 3022 additions and 1300 deletions

View File

@@ -7,9 +7,9 @@ dependencies[] = references
dependencies[] = options
files[] = node_reference.test
; Information added by drupal.org packaging script on 2011-12-22
version = "7.x-2.0"
; Information added by Drupal.org packaging script on 2017-04-18
version = "7.x-2.2"
core = "7.x"
project = "references"
datestamp = "1324596643"
datestamp = "1492534745"

View File

@@ -0,0 +1,38 @@
<?php
/**
* @file
* Install, update and uninstall functions for the node_reference module.
*/
/**
* Implements hook_field_schema().
*/
function node_reference_field_schema($field) {
$columns = array(
'nid' => array(
'type' => 'int',
'unsigned' => TRUE,
'not null' => FALSE,
),
);
return array(
'columns' => $columns,
'indexes' => array('nid' => array('nid')),
'foreign keys' => array(
'nid' => array(
'table' => 'node',
'columns' => array('nid' => 'nid'),
),
),
);
}
/**
* Rebuild views data cache (a callabck was renamed).
*/
function node_reference_update_7000() {
if (function_exists('views_invalidate_cache')) {
views_invalidate_cache();
}
}

View File

@@ -47,29 +47,6 @@ function node_reference_field_info() {
);
}
/**
* Implements hook_field_schema().
*/
function node_reference_field_schema($field) {
$columns = array(
'nid' => array(
'type' => 'int',
'unsigned' => TRUE,
'not null' => FALSE,
),
);
return array(
'columns' => $columns,
'indexes' => array('nid' => array('nid')),
'foreign keys' => array(
'nid' => array(
'table' => 'node',
'columns' => array('nid' => 'nid'),
),
),
);
}
/**
* Implements hook_field_settings_form().
*/
@@ -220,8 +197,8 @@ function node_reference_field_validate($entity_type, $entity, $field, $instance,
function node_reference_field_prepare_view($entity_type, $entities, $field, $instances, $langcode, &$items) {
$checked_ids = &drupal_static(__FUNCTION__, array());
// Set an 'access' property on each item (TRUE if the node exists and is
// accessible by the current user.
// Set an 'access' property on each item (TRUE if the node exists and is
// accessible by the current user).
// Extract ids to check.
$ids = array();
@@ -243,22 +220,35 @@ function node_reference_field_prepare_view($entity_type, $entities, $field, $ins
$ids_to_check = array_diff($ids, array_keys($checked_ids));
if (!empty($ids_to_check)) {
$query = db_select('node', 'n')
->addTag('node_access')
->addMetaData('id', 'node_reference_field_prepare_view')
->addMetaData('field', $field)
->fields('n', array('nid'))
->addTag('node_access');
$condition = db_and()
->condition('n.nid', $ids_to_check, 'IN')
->condition('n.status', NODE_PUBLISHED);
// Take the 'view own unpublished content' permission into account to
// decide whether unpublished nodes should be hidden.
if (!user_access('administer nodes') && !user_access('bypass node access')) {
if (user_access('view own unpublished content') && $own_unpublished = db_query('SELECT nid FROM {node} WHERE uid = :uid AND status = :status', array(':uid' => $GLOBALS['user']->uid, ':status' => NODE_NOT_PUBLISHED))->fetchCol()) {
// (n.nid IN (nodes) AND n.status = 1) OR n.nid = (unpublished)
$condition = db_or()
->condition($condition)
// WHERE n.nid IN (nids to check) AND ...
->condition('n.nid', $ids_to_check, 'IN');
// Unless the user has the right permissions, restrict on the node status.
// (note: the 'view any unpublished content' permission is provided by the
// 'view_unpublished' contrib module.)
if (!user_access('bypass node access') && !user_access('view any unpublished content')) {
// ... AND n.status = 1
$status_condition = db_or()
->condition('n.status', NODE_PUBLISHED);
// Take the 'view own unpublished content' permission into account to
// decide whether some unpublished nodes should still be visible. We
// only need the items in $ids_to_check because those are the only
// entries that we are interested in. Any other nodes created by the
// user are simply ignored so lets only retrieve that subset.
if (user_access('view own unpublished content') && ($own_unpublished = db_query('SELECT nid FROM {node} WHERE uid = :uid AND status = :status AND nid IN (:nodes)', array(':uid' => $GLOBALS['user']->uid, ':status' => NODE_NOT_PUBLISHED, ':nodes' => $ids_to_check))->fetchCol())) {
// ... AND (n.status = 1 OR n.nid IN (own unpublished))
$status_condition
->condition('n.nid', $own_unpublished, 'IN');
}
$query->condition($status_condition);
}
$query->condition($condition);
$accessible_ids = $query->execute()->fetchAllAssoc('nid');
// Populate our static list so that we do not query on those ids again.
@@ -599,6 +589,7 @@ function node_reference_field_widget_settings_form($field, $instance) {
'#options' => array(
'starts_with' => t('Starts with'),
'contains' => t('Contains'),
'fuzzy' => t('Fuzzy search'),
),
'#description' => t('Select the method used to collect autocomplete suggestions. Note that <em>Contains</em> can cause performance issues on sites with thousands of nodes.'),
);
@@ -624,6 +615,7 @@ function node_reference_field_widget_form(&$form, &$form_state, $field, $instanc
'#default_value' => isset($items[$delta]['nid']) ? $items[$delta]['nid'] : NULL,
'#autocomplete_path' => $instance['widget']['settings']['autocomplete_path'] . '/' . $instance['entity_type'] . '/' . $instance['bundle'] . '/' . $field['field_name'],
'#size' => $instance['widget']['settings']['size'],
'#maxlength' => NULL,
'#element_validate' => array('node_reference_autocomplete_validate'),
'#value_callback' => 'node_reference_autocomplete_value',
);
@@ -675,14 +667,14 @@ function node_reference_autocomplete_validate($element, &$form_state, $form) {
// Explicit nid. Check that the 'title' part matches the actual title for
// the nid.
list(, $title, $nid) = $matches;
if (!empty($title)) {
if (!empty($nid)) {
$real_title = db_select('node', 'n')
->fields('n', array('title'))
->condition('n.nid', $nid)
->execute()
->fetchField();
if (trim($title) != trim($real_title)) {
form_error($element, t('%name: title mismatch. Please check your selection.', array('%name' => $instance['label'])));
if (empty($real_title)) {
form_error($element, t('%name: No node found. Please check your selection.', array('%name' => $instance['label'])));
}
}
}
@@ -741,14 +733,18 @@ function _node_reference_options($field, $flat = TRUE) {
$options = array();
foreach ($references as $key => $value) {
// The label, displayed in selects and checkboxes/radios, should have HTML
// entities unencoded. The widgets (core's options.module) take care of
// applying the relevant filters (strip_tags() or filter_xss()).
$label = html_entity_decode($value['rendered'], ENT_QUOTES);
if (empty($value['group']) || $flat) {
$options[$key] = $value['rendered'];
$options[$key] = $label;
}
else {
// The group name, displayed in selects, cannot contain tags, and should
// have HTML entities unencoded.
$group = html_entity_decode(strip_tags($value['group']), ENT_QUOTES);
$options[$group][$key] = $value['rendered'];
$options[$group][$key] = $label;
}
}
@@ -842,11 +838,29 @@ function _node_reference_potential_references_standard($field, $options) {
}
$query = db_select('node', 'n');
if (!user_access('bypass node access')) {
// If the user is able to view their own unpublished nodes, allow them to
// see these in addition to published nodes. Check that they actually have
// some unpublished nodes to view before adding the condition.
if (user_access('view own unpublished content') && $own_unpublished = db_query('SELECT nid FROM {node} WHERE uid = :uid AND status = :status', array(':uid' => $GLOBALS['user']->uid, ':status' => NODE_NOT_PUBLISHED))->fetchCol()) {
$query->condition(db_or()
->condition('n.status', NODE_PUBLISHED)
->condition('n.nid', $own_unpublished, 'IN')
);
}
else {
// If not, restrict the query to published nodes.
$query->condition('n.status', NODE_PUBLISHED);
}
$query->addTag('node_access');
}
$node_nid_alias = $query->addField('n', 'nid');
$node_title_alias = $query->addField('n', 'title', 'node_title');
$node_type_alias = $query->addField('n', 'type', 'node_type');
$query->addTag('node_access')
->addMetaData('id', ' _node_reference_potential_references_standard')
$query->addMetaData('id', ' _node_reference_potential_references_standard')
->addMetaData('field', $field)
->addMetaData('options', $options);
@@ -864,6 +878,13 @@ function _node_reference_potential_references_standard($field, $options) {
$query->condition('n.title', $options['string'] . '%', 'LIKE');
break;
case 'fuzzy':
$words = explode(' ', $options['string']);
foreach ($words as $word) {
$query->condition('n.title', '%' . $word . '%', 'LIKE');
}
break;
case 'equals':
default: // no match type or incorrect match type: use "="
$query->condition('n.title', $options['string']);
@@ -898,8 +919,8 @@ function _node_reference_potential_references_standard($field, $options) {
* Menu callback for the autocomplete results.
*/
function node_reference_autocomplete($entity_type, $bundle, $field_name, $string = '') {
$field = field_info_field($field_name);
$instance = field_info_instance($entity_type, $field_name, $bundle);
$field = field_info_field($field_name);
$options = array(
'string' => $string,
@@ -953,11 +974,11 @@ function node_reference_preprocess_node(&$vars) {
// in a specific view mode).
if (!empty($vars['node']->referencing_field)) {
$node = $vars['node'];
$field = $node->referencing_field;
$vars['theme_hook_suggestions'][] = 'node_reference';
$vars['theme_hook_suggestions'][] = 'node_reference__' . $field['field_name'];
$vars['theme_hook_suggestions'][] = 'node_reference__' . $node->type;
$vars['theme_hook_suggestions'][] = 'node_reference__' . $field['field_name'] . '__' . $node->type;
$field_name = $node->referencing_field;
$vars['theme_hook_suggestions'][] = 'node__node_reference';
$vars['theme_hook_suggestions'][] = 'node__node_reference__' . $field_name;
$vars['theme_hook_suggestions'][] = 'node__node_reference__' . $node->type;
$vars['theme_hook_suggestions'][] = 'node__node_reference__' . $field_name . '__' . $node->type;
}
}
@@ -966,33 +987,42 @@ function node_reference_preprocess_node(&$vars) {
*
* When preparing a translation, load any translations of existing
* references.
* @todo Correctly implement after http://drupal.org/node/362021 is fixed.
*/
function node_reference_field_prepare_translation($entity_type, $entity, $field, $instance, $langcode, &$items) {
$addition = array();
$addition[$field['field_name']] = array();
if (isset($entity->translation_source->$field['field_name'])
&& is_array($entity->translation_source->$field['field_name'])) {
foreach ($entity->translation_source->$field['field_name'] as $key => $reference) {
$reference_node = node_load($reference['nid']);
// Test if the referenced node type is translatable and, if so,
// load translations if the reference is not for the current language.
// We can assume the translation module is present because it invokes 'prepare translation'.
if (translation_supported_type($reference_node->type)
&& !empty($reference_node->language)
&& $reference_node->language != $node->language
&& $translations = translation_node_get_translations($reference_node->tnid)) {
// If there is a translation for the current language, use it.
$addition[$field['field_name']][] = array(
'nid' => isset($translations[$node->language])
? $translations[$node->language]->nid
: $reference['nid'],
);
function node_reference_field_prepare_translation($entity_type, $entity, $field, $instance, $langcode, &$items, $source_entity, $source_langcode) {
if (isset($items) && is_array($items)) {
// Match each reference with its matching translation, if it exists.
foreach ($items as $key => $item) {
$reference_node = node_load($item['nid']);
$items[$key]['nid'] = node_reference_find_translation($reference_node, $entity->language);
}
}
}
/**
* Find a translation for a specific node reference, if it exists.
*
* @param $reference_node
* The untranslated node reference.
* @param $langcode
*
* @return
* A nid for the translation of the node reference,
* otherwise the original untranslated nid if no translation exists.
*/
function node_reference_find_translation($reference_node, $langcode) {
// Check if the source node translation is set and if translations are supported.
if (isset($reference_node->tnid) && translation_supported_type($reference_node->type)) {
// Determine whether an alternative language is being used.
if (!empty($reference_node->language) && $reference_node->language != $langcode) {
// Return a corresponding translation nid for the reference (if it exists).
$translations = translation_node_get_translations($reference_node->tnid);
if (isset($translations[$langcode])) {
return $translations[$langcode]->nid;
}
}
}
return $addition;
// Return the untranslated reference nid, no matching translations found.
return $reference_node->nid;
}
/**
@@ -1028,7 +1058,7 @@ function node_reference_content_migrate_field_alter(&$field_value, $instance_val
'args' => $view_args,
);
if ($view_name) {
$field_value['messages'][] = t("The field uses the view @view_name to determine referenceable nodes. You will need to manually edit the view and add a display of type 'References'.");
$field_value['messages'][] = t("The field uses the view @view_name to determine referenceable nodes. You will need to manually edit the view and add a display of type 'References'.", array('@view_name' => $view_name));
}
unset($field_value['settings']['advanced_view']);
unset($field_value['settings']['advanced_view_args']);
@@ -1105,7 +1135,7 @@ function node_reference_field_views_data($field) {
// the field name instead of the whole $field structure to keep views
// data to a reasonable size.
$data[$table][$id_column]['filter']['handler'] = 'views_handler_filter_in_operator';
$data[$table][$id_column]['filter']['options callback'] = 'node_reference_views_options';
$data[$table][$id_column]['filter']['options callback'] = 'node_reference_views_filter_options';
$data[$table][$id_column]['filter']['options arguments'] = array($field['field_name']);
// Argument: display node.title in argument titles (handled in our custom
@@ -1174,20 +1204,24 @@ function node_reference_field_views_data_views_data_alter(&$data, $field) {
}
/**
* Helper callback for the views_handler_filter_in_operator filter.
* 'options callback' for the views_handler_filter_in_operator filter.
*
* @param $field_name
* The field name.
*/
function node_reference_views_options($field_name) {
function node_reference_views_filter_options($field_name) {
$options = array();
if ($field = field_info_field($field_name)) {
$options = _node_reference_options($field, TRUE);
// The options will be used as is in checkboxes, and thus need to be
// sanitized first.
// The options are displayed in checkboxes within the filter admin form, and
// in a select within an exposed filter. Checkboxes accept HTML, other
// entities should be encoded; selects require the exact opposite: no HTML,
// no encoding. We go for a middle ground: strip tags, leave entities
// unencoded.
foreach ($options as $key => $value) {
$options[$key] = field_filter_xss($value);
$options[$key] = strip_tags($value);
}
}

View File

@@ -97,4 +97,59 @@ class NodeReferenceFormTest extends FieldTestCase {
$allowed = array_slice(node_type_get_names(), 0, 1, TRUE);
$this->runReferenceableNodeTest($allowed, t('Limited referencing'));
}
/**
* Test autocomplete widget.
*/
function testLongNodeReferenceWidget() {
// Create regular test user.
$web_user = $this->drupalCreateUser(array('create article content', 'access content'));
$this->drupalLogin($web_user);
// Create test field instance on article node type.
$instance = array(
'field_name' => $this->field_name,
'entity_type' => 'node',
'bundle' => 'article',
'widget' => array(
'type' => 'node_reference_autocomplete',
),
);
$instance = field_create_instance($instance);
// Create a node with a short title and a node with a title longer than
// 128 characters.
$node_short_title = $this->drupalCreateNode(array(
'type' => 'page',
'title' => $this->randomName(8),
));
$node_long_title = $this->drupalCreateNode(array(
'type' => 'page',
'title' => $this->randomName(200),
));
// Display node creation form.
$langcode = LANGUAGE_NONE;
$this->drupalGet('node/add/article');
$this->assertFieldByName("{$this->field_name}[$langcode][0][nid]", '', t('Widget is displayed'));
// Submit node form with autocomplete value for short title.
$edit = array(
'title' => $this->randomName(8),
"{$this->field_name}[$langcode][0][nid]" => $node_short_title->title . ' [nid:' . $node_short_title->nid . ']',
);
$this->drupalPost('node/add/article', $edit, t('Save'));
$this->assertRaw(t('!post %title has been created.', array('!post' => 'Article', '%title' => $edit["title"])), t('Article created.'));
$this->assertText($node_short_title->title, t('Referenced node title is displayed.'));
// Submit node form with autocomplete value for long title.
$edit = array(
'title' => $this->randomName(8),
"{$this->field_name}[$langcode][0][nid]" => $node_long_title->title . ' [nid:' . $node_long_title->nid . ']',
);
$this->drupalPost('node/add/article', $edit, t('Save'));
$this->assertRaw(t('!post %title has been created.', array('!post' => 'Article', '%title' => $edit["title"])), t('Article created.'));
$this->assertText($node_long_title->title, t('Referenced node title is displayed.'));
}
}