--- nodereference.module.orig 2006-10-31 03:02:41.565355728 -0500 +++ nodereference.module 2006-10-31 03:28:13.078530336 -0500 @@ -23,8 +23,13 @@ $items = array(); if ($may_cache) { - $items[] = array('path' => 'nodereference/autocomplete', 'title' => t('node reference autocomplete'), - 'callback' => 'nodereference_autocomplete', 'access' => user_access('access content'), 'type' => MENU_CALLBACK); + $items[] = array( + 'path' => 'nodereference/autocomplete', + 'title' => t('node reference autocomplete'), + 'callback' => 'nodereference_autocomplete', + 'access' => user_access('access content'), + 'type' => MENU_CALLBACK, + ); } return $items; @@ -35,7 +40,8 @@ */ function nodereference_field_info() { return array( - 'nodereference' => array('label' => 'Node Reference'), + 'nodereference' => array('label' => t('Node Reference')), + 'nodereferrers' => array('label' => t('Node Referrers')), ); } @@ -43,8 +49,8 @@ * Implementation of hook_field_settings(). */ function nodereference_field_settings($op, $field) { - switch ($op) { - case 'form': + switch ($field['type'] .'/'. $op) { + case 'nodereference/form': $form = array(); $form['referenceable_types'] = array( '#type' => 'checkboxes', @@ -55,38 +61,98 @@ ); return $form; - case 'save': - return array('referenceable_types'); + case 'nodereference/save': + return array('referenceable_types'); + + case 'nodereference/filters': + return array( + 'default' => array( + 'list' => '_nodereference_filter_handler', + 'list-type' => 'list', + 'operator' => 'views_handler_operator_or', + 'value-type' => 'array', + 'extra' => array('field' => $field), + ), + ); + + case 'nodereferrers/form': + $form = array(); + $form['referrer_types'] = array( + '#type' => 'checkboxes', + '#title' => t('Referrer types to be displayed in list.'), + '#multiple' => TRUE, + '#default_value' => isset($field['referrer_types']) ? $field['referrer_types'] : array(), + '#options' => node_get_types(), + ); + return $form; + + case 'nodereferrers/save': + return array('referrer_types'); - case 'database columns': + case 'nodereference/database columns': + case 'nodereferrers/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(). + * + * @todo Remove not needed case nodereferrers/insert. + * @todo Search & destroy any backlinks in new $op nodereference/delete. */ -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); +function nodereference_field($op, &$node, $field, &$node_field, $teaser, $page) { + switch ($field['type'] .'/'. $op) { + case 'nodereference/view': + foreach ($node_field as $delta => $item) { + $node_field[$delta]['view'] = content_format($field, $item, $field['reference_output'], $node); + } + return theme('field', $node, $field, $node_field, $teaser, $page); + + case 'nodereference/load': + foreach ($node_field as $delta => $item) { + $node_field[$delta]['node'] = node_load($item['nid']); + } + return array($field['field_name'] => $node_field); + + case 'nodereferrers/view': + // Pass referring node objects to nodereference theming and + // pass the *array* of themed views to content theme_field(). 24/08/2006 sun + $output = array(); + foreach ($node_field as $delta => $item) { + $output[$delta]['view'] = theme('nodereference', $field, $item); + } + return theme('field', $node, $field, $output, $teaser, $page); + + case 'nodereferrers/insert': + // Assign the node's nid to the referrers data. This helps retrieving the node's referrers. + $node_field[0]['nid'] = $node->nid; + return; + + case 'nodereferrers/load': + $types = array_values($field['referrer_types']); + $values = nodereference_get_referrers($node->nid, $types); + + // Clear $node_field containing the displayed node id currently. + $node_field = array(); + + // Pass referring node objects into CCK content_load() cache. 24/08/2006 sun + foreach ($values as $nid => $rnode) { + $node_field[] = $rnode; } - return theme('field', $node, $field, $items, $teaser, $page); + return array($field['field_name'] => $node_field); + + case 'nodereference/update': + case 'nodereferrers/update': + // clear cache for referring nodes to update their views + foreach (nodereference_get_referrers($node->nid) as $delta => $item) { + cache_clear_all('content:'. $item->nid .':'. $item->vid); + } + return; } } @@ -96,11 +162,11 @@ function nodereference_field_formatter_info() { return array( 'default' => array( - 'label' => 'Default', + 'label' => 'Default (Link)', 'field types' => array('nodereference'), ), 'plain' => array( - 'label' => 'Plain text', + 'label' => 'Plain Text', 'field types' => array('nodereference'), ), ); @@ -111,11 +177,8 @@ */ function nodereference_field_formatter($field, $item, $formatter, $node) { $text = ''; - if (isset($item['nid'])) { - $referenced_node = node_load($item['nid']); - if ($referenced_node) { - $text = l($referenced_node->title, 'node/'. $referenced_node->nid); - } + if (is_object($item['node'])) { + $text = l($item['node']->title, 'node/'. $item['node']->nid); } switch ($formatter) { @@ -128,18 +191,50 @@ } /** + * Implementation of hook_field_view_item(). + * + * @todo Has to be moved to hook_field_formatter() 24/08/2006 sun + */ +function nodereference_field_view_item($field, $item) { + if (!isset($item['nid'])) { + return ''; + } + + switch($field['type']) { + case 'nodereference': + $referenced_node = node_load($item['nid']); + if ($referenced_node) { + return theme('nodereference', $field, $referenced_node); + } + break; + + case 'nodereferrers': + // Should/could these be retrieved on hook_field($op='load')? + $referrers = nodereference_get_referrers($item['nid']); + foreach($referrers as $referrer) { + $output .= theme('nodereference', $field, $referrer); + } + return $output; + } +} + +/** * Implementation of hook_widget_info(). */ function nodereference_widget_info() { return array( 'nodereference_select' => array( - 'label' => 'Select List', + 'label' => t('Select List'), 'field types' => array('nodereference'), ), 'nodereference_autocomplete' => array( - 'label' => 'Autocomplete Text Field', + 'label' => t('Autocomplete Text Field'), 'field types' => array('nodereference'), ), + 'nodereferrers_list' => array( + 'label' => t('Read-Only List'), + 'field types' => array('nodereferrers'), + ), ); } @@ -147,119 +242,116 @@ * 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); - if (!$field['required']) $options = array('' => '') + $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'], - ); + switch($field['widget']['type'] .'/'. $op) { + case 'nodereference_select/prepare form values': + $node_field_transposed = content_transpose_array_rows_cols($node_field); + $node_field['default nids'] = $node_field_transposed['nid']; + break; - return $form; - - case 'process form values': - if ($field['multiple']) { - $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) { - $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'])); - } - break; + case 'nodereference_select/form': + $form = array(); + $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' => _nodereference_potential_references($field), + '#required' => $field['required'], + '#description' => $field['widget']['description'], + ); + return $form; - case 'form': - $form = array(); - $form[$field['field_name']] = array('#tree' => TRUE); - - if ($field['multiple']) { - $form[$field['field_name']]['#type'] = 'fieldset'; - $form[$field['field_name']]['#title'] = t($field['widget']['label']); - $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' => '', - '#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) { + case 'nodereference_select/process form values': + if ($field['multiple']) { + $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']); + break; + + case 'nodereference_autocomplete/prepare form values': + foreach ($node_field as $delta => $item) { + $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'])); + } + break; + + case 'nodereference_autocomplete/form': + $form = array(); + $form[$field['field_name']] = array('#tree' => TRUE); + if ($field['multiple']) { + $form[$field['field_name']]['#type'] = 'fieldset'; + $form[$field['field_name']]['#title'] = t($field['widget']['label']); + $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' => '', '#autocomplete_path' => 'nodereference/autocomplete/'. $field['field_name'], - '#default_value' => '', + '#default_value' => $item['default node_name'], '#required' => ($delta == 0) ? $field['required'] : FALSE, ); + $delta++; } } - else { - $form[$field['field_name']][0]['node_name'] = array( + + foreach (range($delta, $delta + 2) as $delta) { + $form[$field['field_name']][$delta]['node_name'] = array( '#type' => 'textfield', - '#title' => t($field['widget']['label']), + '#title' => '', '#autocomplete_path' => 'nodereference/autocomplete/'. $field['field_name'], - '#default_value' => $node_field[0]['default node_name'], - '#required' => $field['required'], - '#description' => $field['widget']['description'], + '#default_value' => '', + '#required' => ($delta == 0) ? $field['required'] : FALSE, ); } - return $form; + } + 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) { - if ($item['node_name'] && !in_array($item['node_name'], _nodereference_potential_references($field))) { - form_set_error($field['field_name'], t('No post with that title exists.')); - } + case 'nodereference_autocomplete/validate': + foreach ($node_field as $delta => $item) { + if ($item['node_name'] && !in_array($item['node_name'], _nodereference_potential_references($field))) { + form_set_error($field['field_name'], t('No post with that title exists.')); } - return; + } + return; - case 'process form values': - foreach ($node_field as $delta => $item) { - $nid = 0; - if ($node_field[$delta]['node_name']) { - $nid = db_result(db_query(db_rewrite_sql("SELECT n.nid FROM {node} n WHERE n.title = '%s'"), $node_field[$delta]['node_name'])); - } - // Remove the widget's data representation so it isn't saved. - unset($node_field[$delta]['node_name']); - if ($nid) { - $node_field[$delta]['nid'] = $nid; - } - else { - unset($node_field[$delta]); - } + case 'nodereference_autocomplete/process form values': + foreach ($node_field as $delta => $item) { + $nid = db_result(db_query(db_rewrite_sql("SELECT n.nid FROM {node} n WHERE n.title = '%s'"), $node_field[$delta]['node_name'])); + + // Remove the widget's data representation so it isn't saved. + unset($node_field[$delta]['node_name']); + if ($nid) { + $node_field[$delta]['nid'] = $nid; } - break; - } + else { + unset($node_field[$delta]); + } + } + break; + case 'nodereferrers_list/form': + $form = array(); + $form[$field['field_name']] = array('#tree' => TRUE); + $form[$field['field_name']][0]['nid'] = array( + '#type' => 'hidden', + '#value' => $node->nid, + ); + return $form; } } @@ -273,17 +365,15 @@ if (isset($field['referenceable_types'])) { foreach ($field['referenceable_types'] as $related_type) { if ($related_type) { - $related_types[] = " type = '". $related_type ."'"; + $related_types[] = " n.type = '". $related_type ."'"; } } } - - $related_clause = implode(' OR ', $related_types); - if (!count($related_types)) { return array(); } + $related_clause = implode(' OR ', $related_types); $result = db_query(db_rewrite_sql("SELECT n.nid, n.title, n.type FROM {node} n WHERE ". $related_clause ." ORDER BY n.title, n.type")); if (db_num_rows($result) == 0) { @@ -291,7 +381,6 @@ } $rows = array(); - while ($node = db_fetch_object($result)) { if ($return_full_nodes) { $rows[$node->nid] = $node; @@ -300,7 +389,6 @@ $rows[$node->nid] = $node->title; } } - return $rows; } @@ -330,3 +418,46 @@ $options = $options + _nodereference_potential_references($filterinfo['extra']['field']); return $options; } + +/** + * Get an array of referrers by nid and content type. + * @param nid + * referred to nodes nid + * @param content_types + * array of content types to be queried for. empty array == all content types + * @return array + */ +function nodereference_get_referrers($nid, $content_types = array()) { + // Filter all content fields to find names of all fields of type nodereference + $fields = content_fields(); + $values = array(); + foreach($fields as $field) { + if ($field['type'] == 'nodereference') { + $db_info = content_database_info($field); + + // Gather nodereferences that refer to $nid + // Fetch only nid and title for output and vid for caching 24/08/2006 sun + $filter_content_types = ''; + if ($content_types) { + $filter_content_types = "AND n.type IN ('". implode("', '", $content_types) ."') "; + } + $result = db_query( + "SELECT n.nid, n.vid, n.title + FROM {" . $db_info['table'] . "} nr + INNER JOIN {node} n ON n.vid = nr.vid AND n.status = 1 ". $filter_content_types ." + WHERE nr." . $db_info['columns']['nid']['column'] . " = %d + ORDER BY n.created DESC", $nid); + + while ($value = db_fetch_object($result)) { + // avoid duplicate referrers by using nid as key + $values[$value->nid] = $value; + } + } + } + return $values; +} + +function theme_nodereference($field, $node) { + return '