|
|
|
@@ -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);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|