'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('names'),
);
if (module_exists('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'];
}
views_load_cache();
$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.
Note :
- This will discard the "Content types" settings above. Use the view\'s "filters" section instead.
- Use the view\'s "fields" section to display additional informations about candidate nodes on node creation/edition form.
- Use the view\'s "sort criteria" section to determine the order in which candidate nodes will be displayed.
'),
);
}
}
return $form;
case 'save':
$settings = array('referenceable_types');
if (module_exists('views')) {
$settings[] = 'advanced_view';
}
return $settings;
case 'database columns':
$columns = array(
'nid' => array('type' => 'int', 'not null' => TRUE, 'default' => '0'),
);
return $columns;
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().
*/
function nodereference_field($op, &$node, $field, &$items, $teaser, $page) {
switch ($op) {
case 'view':
foreach ($items as $delta => $item) {
$items[$delta]['view'] = content_format($field, $item, 'default', $node);
}
return theme('field', $node, $field, $items, $teaser, $page);
}
}
/**
* Implementation of hook_field_formatter_info().
*/
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'])) {
$referenced_node = node_load($item['nid']);
if ($referenced_node) {
$text = l($referenced_node->title, 'node/'. $referenced_node->nid);
}
}
switch ($formatter) {
case 'plain':
return strip_tags($text);
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') {
switch ($op) {
case 'prepare form values':
$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('')) + $options;
}
$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'],
'#options' => $options,
'#required' => $field['required'],
'#description' => $field['widget']['description'],
);
return $form;
case 'process form values':
if ($field['multiple']) {
// if nothing selected, make it 'none'
if (empty($node_field['nids'])) {
$node_field['nids'] = array(0 => '0');
}
// drop the 'none' options if other items were also selected
elseif (count($node_field['nids']) > 1) {
unset($node_field['nids'][0]);
}
$node_field = content_transpose_array_rows_cols(array('nid' => $node_field['nids']));
}
else {
$node_field[0]['nid'] = $node_field['nids'];
}
// 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) {
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) {
$error_field = $field['field_name'].']['.$delta.'][node_name';
if (!empty($item['node_name'])) {
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.'));
}
}
}
}
return;
case 'process form values':
foreach ($node_field as $delta => $item) {
$nid = 0;
if (!empty($item['node_name'])) {
preg_match('/^(?:\s*|(.*) )?\[\s*nid\s*:\s*(\d+)\s*\]$/', $item['node_name'], $matches);
if (!empty($matches)) {
// explicit nid
$nid = $matches[2];
}
else {
// no explicit nid
// 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));
}
}
if (!empty($nid)) {
$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]);}
}
}
break;
}
}
}
/**
* 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_exists('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_load_cache();
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)) {
if ($return_full_nodes) {
$rows[$node->nid] = $node;
}
else {
$rows[$node->nid] = $node->node_title;
}
}
return $rows;
}
/**
* Retrieve a pipe delimited string of autocomplete suggestions
*/
function nodereference_autocomplete($field_name, $string = '') {
$fields = content_fields();
$field = $fields[$field_name];
$matches = array();
foreach (_nodereference_potential_references($field, TRUE, $string) as $row) {
$matches[$row->node_title .' [nid:'. $row->nid .']'] = _nodereference_item($field, $row, TRUE);
}
print drupal_to_js($matches);
exit();
}
function _nodereference_item($field, $item, $html = false) {
if (module_exists('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>/iU', '$1', $value);
if (!empty($value)) {
$item_fields[] = "$value";;
}
}
$output = implode(' - ', $item_fields);
$output = "$output";
return $output;
}
function theme_nodereference_item_simple($item) {
return $item->node_title;
}
/**
* Provide a list of users to filter on.
*/
function _nodereference_filter_handler($op, $filterinfo) {
$options = array(0 => t(''));
$options = $options + _nodereference_potential_references($filterinfo['extra']['field']);
return $options;
}