Newer
Older
<?php
// $Id$
/**
* @file
* Defines a field type for referencing one node from another.
*/
/**
* Implementation of hook_help().
*/
function nodereference_help($section) {
switch ($section) {
case 'admin/modules#description':
Jonathan Chaffer
committed
return t('<strong>CCK:</strong> Defines a field type for referencing one node from another. <em>Note: Requires content.module.</em>');
/**
* Implementation of hook_menu().
*/
function nodereference_menu($may_cache) {
$items = array();
$items[] = array('path' => 'nodereference/autocomplete', 'title' => t('node reference autocomplete'),
'callback' => 'nodereference_autocomplete', 'access' => user_access('access content'), 'type' => MENU_CALLBACK);
}
return $items;
}
/**
* Implementation of hook_field_info().
*/
function nodereference_field_info() {
return array(
'nodereference' => array('label' => 'Node Reference'),
);
}
/**
* Implementation of hook_field_settings().
*/
function nodereference_field_settings($op, $field) {
switch ($op) {
case 'form':
$form = array();
$form['referenceable_types'] = array(
'#type' => 'checkboxes',
'#title' => t('Content types that can be referenced'),
'#multiple' => TRUE,
'#default_value' => isset($field['referenceable_types']) ? $field['referenceable_types'] : array(),
'#options' => node_get_types(),
);
if (module_exist('views')) {
$views = array('--' => '--');
$result = db_query("SELECT name FROM {view_view} ORDER BY name");
while ($view = db_fetch_array($result)) {
$views[t('Existing Views')][$view['name']] = $view['name'];
}
$default_views = _views_get_default_views();
foreach ($default_views as $view) {
$views[t('Default Views')][$view->name] = $view->name;
}
if (count($views) > 1) {
$form['advanced_view'] = array(
'#type' => 'select',
'#title' => t('Advanced - Nodes that can be referenced'),
'#options' => $views,
'#default_value' => isset($field['advanced_view']) ? $field['advanced_view'] : '--',
'#description' => t('Choose the "Views module" view that selects the nodes that can be referenced.<br>Note :<ul><li>This will discard the "Content types" settings above. Use the view\'s "filters" section instead.</li><li>Use the view\'s "fields" section to display additional informations about candidate nodes on node creation/edition form.</li><li>Use the view\'s "sort criteria" section to determine the order in which candidate nodes will be displayed.</li></ul>'),
);
}
}
return $form;
case 'save':
$settings = array('referenceable_types');
if (module_exist('views')) {
$settings[] = 'advanced_view';
}
return $settings;
case 'database columns':
$columns = array(
'nid' => array('type' => 'int', 'not null' => TRUE, 'default' => '0'),
);
return $columns;
Karen Stevenson
committed
case 'filters':
return array(
'default' => array(
'list' => '_nodereference_filter_handler',
'list-type' => 'list',
'operator' => 'views_handler_operator_or',
'value-type' => 'array',
'extra' => array('field' => $field),
),
);
}
}
/**
* Implementation of hook_field().
*/
Yves Chedemois
committed
//function nodereference_field($op, &$node, $field, &$items, $teaser, $page) {
// switch ($op) {
// }
//}
/**
* Implementation of hook_field_formatter_info().
*/
Jonathan Chaffer
committed
function nodereference_field_formatter_info() {
return array(
'default' => array(
'label' => 'Default',
'field types' => array('nodereference'),
),
'plain' => array(
'label' => 'Plain text',
'field types' => array('nodereference'),
),
);
}
/**
* Implementation of hook_field_formatter().
*/
function nodereference_field_formatter($field, $item, $formatter, $node) {
$text = '';
if (!empty($item['nid'])) {
Jonathan Chaffer
committed
$referenced_node = node_load($item['nid']);
if ($referenced_node) {
$text = l($referenced_node->title, 'node/'. $referenced_node->nid);
}
Jonathan Chaffer
committed
switch ($formatter) {
case 'plain':
return strip_tags($text);
Jonathan Chaffer
committed
default:
return $text;
}
}
/**
* Implementation of hook_widget_info().
*/
function nodereference_widget_info() {
return array(
'nodereference_select' => array(
'label' => 'Select List',
'field types' => array('nodereference'),
),
'nodereference_autocomplete' => array(
'label' => 'Autocomplete Text Field',
'field types' => array('nodereference'),
),
);
}
/**
* Implementation of hook_widget().
*/
function nodereference_widget($op, &$node, $field, &$node_field) {
if ($field['widget']['type'] == 'nodereference_select') {
case 'prepare form values':
Jonathan Chaffer
committed
$node_field_transposed = content_transpose_array_rows_cols($node_field);
$node_field['default nids'] = $node_field_transposed['nid'];
break;
case 'form':
$form = array();
$options = _nodereference_potential_references($field, true);
foreach ($options as $key => $value) {
$options[$key] = _nodereference_item($field, $value);
}
if (!$field['required']) {
$options = array(0 => t('<none>')) + $options;
}
Karen Stevenson
committed
$form[$field['field_name']] = array('#tree' => TRUE);
$form[$field['field_name']]['nids'] = array(
'#type' => 'select',
'#title' => t($field['widget']['label']),
'#default_value' => $node_field['default nids'],
'#multiple' => $field['multiple'],
'#size' => min(count($options), 6),
Karen Stevenson
committed
'#options' => $options,
'#required' => $field['required'],
'#description' => $field['widget']['description'],
);
return $form;
case 'process form values':
Jonathan Chaffer
committed
if ($field['multiple']) {
Yves Chedemois
committed
// if nothing selected, make it 'none'
if (empty($node_field['nids'])) {
Yves Chedemois
committed
$node_field['nids'] = array(0 => '0');
Yves Chedemois
committed
// drop the 'none' options if other items were also selected
elseif (count($node_field['nids']) > 1) {
unset($node_field['nids'][0]);
}
Yves Chedemois
committed
$node_field = content_transpose_array_rows_cols(array('nid' => $node_field['nids']));
Jonathan Chaffer
committed
$node_field[0]['nid'] = $node_field['nids'];
Jonathan Chaffer
committed
}
Jonathan Chaffer
committed
// Remove the widget's data representation so it isn't saved.
unset($node_field['nids']);
}
}
else {
switch ($op) {
case 'prepare form values':
foreach ($node_field as $delta => $item) {
Karen Stevenson
committed
if (!empty($node_field[$delta]['nid'])) {
$node_field[$delta]['default node_name'] = db_result(db_query(db_rewrite_sql('SELECT n.title FROM {node} n WHERE n.nid = %d'), $node_field[$delta]['nid']));
$node_field[$delta]['default node_name'] .= ' [nid:'. $node_field[$delta]['nid'] .']';
}
break;
case 'form':
$form = array();
$form[$field['field_name']] = array('#tree' => TRUE);
if ($field['multiple']) {
$form[$field['field_name']]['#type'] = 'fieldset';
$form[$field['field_name']]['#description'] = $field['widget']['description'];
$delta = 0;
foreach ($node_field as $item) {
if ($item['nid']) {
$form[$field['field_name']][$delta]['node_name'] = array(
'#type' => 'textfield',
'#title' => ($delta == 0) ? t($field['widget']['label']) : '',
'#autocomplete_path' => 'nodereference/autocomplete/'. $field['field_name'],
'#default_value' => $item['default node_name'],
'#required' => ($delta == 0) ? $field['required'] : FALSE,
);
$delta++;
}
}
foreach (range($delta, $delta + 2) as $delta) {
$form[$field['field_name']][$delta]['node_name'] = array(
'#type' => 'textfield',
'#title' => ($delta == 0) ? t($field['widget']['label']) : '',
'#autocomplete_path' => 'nodereference/autocomplete/'. $field['field_name'],
'#default_value' => '',
'#required' => ($delta == 0) ? $field['required'] : FALSE,
);
}
}
else {
$form[$field['field_name']][0]['node_name'] = array(
'#type' => 'textfield',
'#title' => t($field['widget']['label']),
'#autocomplete_path' => 'nodereference/autocomplete/'. $field['field_name'],
'#default_value' => $node_field[0]['default node_name'],
'#required' => $field['required'],
'#description' => $field['widget']['description'],
);
}
return $form;
case 'validate':
foreach ($node_field as $delta => $item) {
Karen Stevenson
committed
$error_field = $field['field_name'].']['.$delta.'][node_name';
if (!empty($item['node_name'])) {
Karen Stevenson
committed
preg_match('/^(?:\s*|(.*) )?\[\s*nid\s*:\s*(\d+)\s*\]$/', $item['node_name'], $matches);
if (!empty($matches)) {
// explicit nid
list(, $title, $nid) = $matches;
$refs = _nodereference_potential_references($field, TRUE);
if (!in_array($nid, array_keys($refs))) {
form_set_error($error_field, t('This post can\'t be referenced.'));
}
elseif (!empty($title) && ($n = $refs[$nid]) && $title != $n->node_title) {
form_set_error($error_field, t('Title mismatch. Please reiterate your selection.'));
}
}
else {
// no explicit nid
$refs = _nodereference_potential_references($field, FALSE, $item['node_name'], TRUE);
if (empty($refs)) {
form_set_error($error_field, t('No post with that title can be referenced.'));
}
case 'process form values':
foreach ($node_field as $delta => $item) {
Karen Stevenson
committed
if (!empty($item['node_name'])) {
preg_match('/^(?:\s*|(.*) )?\[\s*nid\s*:\s*(\d+)\s*\]$/', $item['node_name'], $matches);
if (!empty($matches)) {
// explicit nid
Karen Stevenson
committed
$nid = $matches[2];
}
else {
// no explicit nid
Karen Stevenson
committed
// TODO :
// the best thing would be to present the user with an additional form,
// allowing the user to choose between valid candidates with the same title
// ATM, we pick the first matching candidate...
$nids = _nodereference_potential_references($field, false, $item['node_name'], true);
$nid = array_shift(array_keys($nids));
}
Jonathan Chaffer
committed
}
if (!empty($nid)) {
Yves Chedemois
committed
$node_field[$delta]['nid'] = $nid;
// Remove the widget's data representation so it isn't saved.
unset($node_field[$delta]['node_name']);
}
else {
// Don't save empty fields when they're not the first value (keep '0' otherwise)
if ($delta > 0) {unset($node_field[$delta]);}
Yves Chedemois
committed
}
break;
Jonathan Chaffer
committed
}
}
}
/**
* Fetch an array of all candidate referenced nodes, for use in presenting the selection form to the user.
*/
function _nodereference_potential_references($field, $return_full_nodes = FALSE, $string = '', $exact_string = false) {
if (module_exist('views') && ($view = views_get_view($field['advanced_view']))) {
// advanced field : referenceable nodes defined by a view
// let views.module build the query
if (isset($string)) {
views_view_add_filter($view, 'node', 'title', $exact_string ? '=' : 'contains', $string, null);
// we do need title field, so add it if not present (unlikely, but...)
$has_title = array_reduce($view->field, create_function('$a, $b', 'return ($b["field"] == "title") || $a;'), false);
if (!$has_title) {
views_view_add_field($view, 'node', 'title', '');
}
views_sanitize_view($view);
// make sure the fields get included in the query
$view->page = true;
$view->page_type = 'list';
// make sure the query is not cached
unset($view->query);
$view_result = views_build_view('result', $view);
$result = $view_result['result'];
else {
// standard field : referenceable nodes defined by content types
// build the appropriate query
$related_types = array();
$args = array();
if (isset($field['referenceable_types'])) {
foreach ($field['referenceable_types'] as $related_type) {
if ($related_type) {
$related_types[] = " type = '%s'";
$args[] = $related_type;
}
}
}
$related_clause = implode(' OR ', $related_types);
if (!count($related_types)) {
return array();
}
if (isset($string)) {
$string_clause = $exact_string ? " AND title = '%s'" : " AND title LIKE '%%%s%'";
$related_clause = "(". $related_clause .")". $string_clause;
$args[] = $string;
}
$result = db_query(db_rewrite_sql("SELECT n.nid, n.title AS node_title, n.type AS node_type FROM {node} n WHERE ". $related_clause ." ORDER BY n.title, n.type"), $args);
}
if (db_num_rows($result) == 0) {
return array();
}
$rows = array();
while ($node = db_fetch_object($result)) {
Jonathan Chaffer
committed
$rows[$node->nid] = $node;
$rows[$node->nid] = $node->node_title;
Jonathan Chaffer
committed
}
}
return $rows;
}
/**
* Retrieve a pipe delimited string of autocomplete suggestions
*/
function nodereference_autocomplete($field_name, $string = '') {
$field = $fields[$field_name];
foreach (_nodereference_potential_references($field, TRUE, $string) as $row) {
Karen Stevenson
committed
$matches[$row->node_title .' [nid:'. $row->nid .']'] = _nodereference_item($field, $row, TRUE);
print drupal_to_js($matches);
Karen Stevenson
committed
}
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
function _nodereference_item($field, $item, $html = false) {
if (module_exist('views') && ($view = views_get_view($field['advanced_view']))) {
$output = theme('nodereference_item_advanced', $item, $view);
if (!$html) {
$output = strip_tags($output);
}
}
else {
$output = theme('nodereference_item_simple', $item);
}
return $output;
}
function theme_nodereference_item_advanced($item, $view) {
$fields = _views_get_fields();
$item_fields = array();
foreach ($view->field as $field) {
$value = views_theme_field('views_handle_field', $field['queryname'], $fields, $field, $item, $view);
// remove link tags (ex : for node titles)
$value = preg_replace('/<a[^>]*>(.*)<\/a>/iU', '$1', $value);
if (!empty($value)) {
$item_fields[] = "<span class='view-field view-data-$field[queryname]'>$value</span>";;
}
}
$output = implode(' - ', $item_fields);
$output = "<span class='view-item view-item-$view->name'>$output</span>";
return $output;
}
function theme_nodereference_item_simple($item) {
return $item->node_title;
}
Karen Stevenson
committed
/**
* Provide a list of users to filter on.
*/
function _nodereference_filter_handler($op, $filterinfo) {
Yves Chedemois
committed
$options = array(0 => t('<empty>'));
Karen Stevenson
committed
$options = $options + _nodereference_potential_references($filterinfo['extra']['field']);
return $options;