/[drupal]/contributions/modules/cck/nodereference.module
ViewVC logotype

Diff of /contributions/modules/cck/nodereference.module

Parent Directory Parent Directory | Revision Log Revision Log | View Revision Graph Revision Graph | View Patch Patch

revision 1.39.2.43, Wed Nov 5 19:38:03 2008 UTC revision 1.127, Wed Apr 23 18:02:14 2008 UTC
# Line 1  Line 1 
1  <?php  <?php
2  // $Id: nodereference.module,v 1.39.2.42 2008/09/17 18:02:28 yched Exp $  // $Id: nodereference.module,v 1.126 2008/04/23 08:24:06 karens Exp $
3    
4  /**  /**
5   * @file   * @file
6   * Defines a field type for referencing one node from another.   * Defines a field type for referencing one node from another.
7   */   */
8    
   
9  /**  /**
10   * Implementation of hook_menu().   * Implementation of hook_menu().
11   */   */
12  function nodereference_menu($may_cache) {  function nodereference_menu() {
13    $items = array();    $items = array();
14      $items['nodereference/autocomplete'] = array(
15    if ($may_cache) {      'title' => t('Nodereference autocomplete'),
16      $items[] = array('path' => 'nodereference/autocomplete', 'title' => t('node reference autocomplete'),      'page callback' => 'nodereference_autocomplete',
17        'callback' => 'nodereference_autocomplete', 'access' => user_access('access content'), 'type' => MENU_CALLBACK);      'access arguments' => array('access content'),
18    }      'type' => MENU_CALLBACK
19      );
20    return $items;    return $items;
21  }  }
22    
23  /**  /**
24     * Implementation of hook_theme().
25     */
26    function nodereference_theme() {
27      return array(
28        'nodereference_item_simple' => array(
29          'arguments' => array('item' => NULL),
30        ),
31        'nodereference_item_advanced' => array(
32          'arguments' => array('item' => NULL, 'view' => NULL),
33        ),
34        'nodereference_select' => array(
35          'arguments' => array('element' => NULL),
36        ),
37        'nodereference_autocomplete' => array(
38          'arguments' => array('element' => NULL),
39        ),
40        'nodereference_formatter_default' => array(
41          'arguments' => array('element'),
42        ),
43        'nodereference_formatter_full' => array(
44          'arguments' => array('element'),
45          'function' => 'theme_nodereference_formatter_full_teaser',
46        ),
47        'nodereference_formatter_teaser' => array(
48          'arguments' => array('element'),
49          'function' => 'theme_nodereference_formatter_full_teaser',
50        ),
51      );
52    }
53    
54    /**
55   * Implementation of hook_field_info().   * Implementation of hook_field_info().
56     *
57     * Here we indicate that the content module will use its default
58     * handling for the view of this field.
59     *
60     * Callbacks can be omitted if default handing is used.
61     * They're included here just so this module can be used
62     * as an example for custom modules that might do things
63     * differently.
64   */   */
65  function nodereference_field_info() {  function nodereference_field_info() {
66    return array(    return array(
67      'nodereference' => array('label' => t('Node Reference')),      'nodereference' => array(
68          'label' => t('Node reference'),
69          'description' => t('Store the ID of a related node as an integer value.'),
70          'callbacks' => array(
71            'tables' => CONTENT_CALLBACK_DEFAULT,
72            'arguments' => CONTENT_CALLBACK_DEFAULT,
73            ),
74          ),
75    );    );
76  }  }
77    
# Line 41  function nodereference_field_settings($o Line 86  function nodereference_field_settings($o
86          '#type' => 'checkboxes',          '#type' => 'checkboxes',
87          '#title' => t('Content types that can be referenced'),          '#title' => t('Content types that can be referenced'),
88          '#multiple' => TRUE,          '#multiple' => TRUE,
89          '#default_value' => isset($field['referenceable_types']) ? $field['referenceable_types'] : array(),          '#default_value' => is_array($field['referenceable_types']) ? $field['referenceable_types'] : array(),
90          '#options' => array_map('check_plain', node_get_types('names')),          '#options' => node_get_types('names'),
91        );        );
92        if (module_exists('views')) {        if (module_exists('views')) {
93          $views = array('--' => '--');          $views = array('--' => '--');
94          $result = db_query("SELECT name FROM {view_view} ORDER BY name");          $all_views = views_get_all_views();
95          while ($view = db_fetch_array($result)) {          foreach ($all_views as $view) {
96            $views[t('Existing Views')][$view['name']] = $view['name'];            $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;  
97          }          }
98    
99          if (count($views) > 1) {          if (count($views) > 1) {
100            $form['advanced'] = array(            $form['advanced'] = array(
101               '#type' => 'fieldset',               '#type' => 'fieldset',
# Line 90  function nodereference_field_settings($o Line 131  function nodereference_field_settings($o
131    
132      case 'database columns':      case 'database columns':
133        $columns = array(        $columns = array(
134          'nid' => array('type' => 'int', 'not null' => TRUE, 'default' => '0'),          'nid' => array('type' => 'int', 'unsigned' => TRUE, 'not null' => FALSE),
135        );        );
136        return $columns;        return $columns;
137    
138      case 'filters':      case 'views data':
139        return array(        $data = content_views_field_views_data($field);
140          'default' => array(        $db_info = content_database_info($field);
141            'list' => '_nodereference_filter_handler',        $table_alias = content_views_tablename($field);
142            'list-type' => 'list',  
143            'operator' => 'views_handler_operator_or',        // Swap the filter handler to the 'in' operator.
144            'value-type' => 'array',        $data[$table_alias][$field['field_name'] .'_nid']['filter']['handler'] = 'views_handler_filter_many_to_one_content';
145            'extra' => array('field' => $field),  
146          ),        // Add a relationship for related node.
147          $data[$table_alias][$field['field_name'] .'_nid']['relationship'] = array(
148            'base' => 'node',
149            'field' => $db_info['columns']['nid']['column'],
150            'handler' => 'views_handler_relationship',
151        );        );
152          return $data;
153    }    }
154  }  }
155    
# Line 115  function nodereference_field($op, &$node Line 161  function nodereference_field($op, &$node
161      case 'validate':      case 'validate':
162        $refs = _nodereference_potential_references($field, TRUE);        $refs = _nodereference_potential_references($field, TRUE);
163        foreach ($items as $delta => $item) {        foreach ($items as $delta => $item) {
164          $error_field = isset($item['error_field']) ? $item['error_field'] : '';          if (is_array($item) && !empty($item['error_field'])) {
165          unset($item['error_field']);            $error_field = $item['error_field'];
166          if (!empty($item['nid'])) {            unset($item['error_field']);
167            if (!in_array($item['nid'], array_keys($refs))) {            if (!empty($item['nid'])) {
168              form_set_error($error_field, t('%name : This post can\'t be referenced.', array('%name' => t($field['widget']['label']))));              if (!in_array($item['nid'], array_keys($refs))) {
169                  form_set_error($error_field, t('%name : This post can\'t be referenced.', array('%name' => t($field['widget']['label']))));
170                }
171            }            }
172          }          }
173        }        }
174        return;        return $items;
175    }    }
176  }  }
177    
178  /**  /**
179     * Implementation of hook_content_is_empty().
180     */
181    function nodereference_content_is_empty($item, $field) {
182      if (empty($item['nid'])) {
183        return TRUE;
184      }
185      return FALSE;
186    }
187    
188    /**
189   * Implementation of hook_field_formatter_info().   * Implementation of hook_field_formatter_info().
190   */   */
191  function nodereference_field_formatter_info() {  function nodereference_field_formatter_info() {
# Line 135  function nodereference_field_formatter_i Line 193  function nodereference_field_formatter_i
193      'default' => array(      'default' => array(
194        'label' => t('Title (link)'),        'label' => t('Title (link)'),
195        'field types' => array('nodereference'),        'field types' => array('nodereference'),
196          'multiple values' => CONTENT_HANDLE_CORE,
197      ),      ),
198      'plain' => array(      'plain' => array(
199        'label' => t('Title (no link)'),        'label' => t('Title (no link)'),
200        'field types' => array('nodereference'),        'field types' => array('nodereference'),
201          'multiple values' => CONTENT_HANDLE_CORE,
202      ),      ),
203      'full' => array(      'full' => array(
204        'label' => t('Full node'),        'label' => t('Full node'),
205        'field types' => array('nodereference'),        'field types' => array('nodereference'),
206          'multiple values' => CONTENT_HANDLE_CORE,
207      ),      ),
208      'teaser' => array(      'teaser' => array(
209        'label' => t('Teaser'),        'label' => t('Teaser'),
210        'field types' => array('nodereference'),        'field types' => array('nodereference'),
211          'multiple values' => CONTENT_HANDLE_CORE,
212      ),      ),
213    );    );
214  }  }
215    
216  /**  /**
217   * Implementation of hook_field_formatter().   * Theme function for 'default' nodereference field formatter.
218   */   */
219  function nodereference_field_formatter($field, $item, $formatter, $node) {  function theme_nodereference_formatter_default($element) {
220    static $titles = array();    $output = '';
221      if (!empty($element['#item']['nid']) && is_numeric($element['#item']['nid']) && ($title = _nodereference_titles($element['#item']['nid']))) {
222    // We store the rendered nids in order to prevent infinite recursion      $output = l($title, 'node/'. $element['#item']['nid']);
223    // when using the 'full node' / 'teaser' formatters.    }
224    static $recursion_queue = array();    return $output;
225    }
226    
227    if (empty($item['nid']) || !is_numeric($item['nid'])) {  /**
228      return '';   * Theme function for 'plain' nodereference field formatter.
229     */
230    function theme_nodereference_formatter_plain($element) {
231      $output = '';
232      if (!empty($element['#item']['nid']) && is_numeric($element['#item']['nid']) && ($title = _nodereference_titles($element['#item']['nid']))) {
233        $output = check_plain($title);
234    }    }
235      return $output;
236    }
237    
238    if ($formatter == 'full' || $formatter == 'teaser') {  /**
239     * Proxy theme function for 'full' and 'teaser' nodereference field formatters.
240     */
241    function theme_nodereference_formatter_full_teaser($element) {
242      static $recursion_queue = array();
243      $output = '';
244      if (!empty($element['#item']['nid']) && is_numeric($element['#item']['nid'])) {
245      // If no 'referencing node' is set, we are starting a new 'reference thread'      // If no 'referencing node' is set, we are starting a new 'reference thread'
246      if (!isset($node->referencing_node)) {      if (!isset($node->referencing_node)) {
247        $recursion_queue = array();        $recursion_queue = array();
248      }      }
249      $recursion_queue[] = $node->nid;      $recursion_queue[] = $node->nid;
250      if (in_array($item['nid'], $recursion_queue)) {      if (in_array($element['#item']['nid'], $recursion_queue)) {
251        // Prevent infinite recursion caused by reference cycles :        // Prevent infinite recursion caused by reference cycles :
252        // if the node has already been rendered earlier in this 'thread',        // if the node has already been rendered earlier in this 'thread',
253        // we fall back to 'default' (node title) formatter.        // we fall back to 'default' (node title) formatter.
254        $formatter = 'default';        return theme('nodereference_formatter_default', $element);
255      }      }
256      elseif ($referenced_node = node_load($item['nid'])) {      if ($referenced_node = node_load($element['#item']['nid'])) {
257        $referenced_node->referencing_node = $node;        $referenced_node->referencing_node = $node;
258        $referenced_node->referencing_field = $field;        $referenced_node->referencing_field = $field;
259        $titles[$item['nid']] = $referenced_node->title;        _nodereference_titles($element['#item']['nid'], $referenced_node->title);
260          $output = node_view($referenced_node, $element['#formatter'] == 'teaser');
261      }      }
262    }    }
263      return $output;
264    }
265    
266    if (!isset($titles[$item['nid']])) {  /**
267      $title = db_result(db_query("SELECT title FROM {node} WHERE nid=%d", $item['nid']));   * Helper function for formatters.
268      $titles[$item['nid']] = $title ? $title : '';   *
269    }   * Store node titles collected in the curent request.
270     */
271    switch ($formatter) {  function _nodereference_titles($nid, $known_title = NULL) {
272      case 'full':    static $titles = array();
273        return $referenced_node ? node_view($referenced_node, FALSE) : '';    if (!isset($titles[$nid])) {
274        $title = $known_title ? $known_title : db_result(db_query("SELECT title FROM {node} WHERE nid=%d", $nid));
275      case 'teaser':      $titles[$nid] = $title ? $title : '';
       return $referenced_node ? node_view($referenced_node, TRUE) : '';  
   
     case 'plain':  
       return check_plain($titles[$item['nid']]);  
   
     default:  
       return $titles[$item['nid']] ? l($titles[$item['nid']], 'node/'. $item['nid']) : '';  
276    }    }
277      return $titles[$nid];
278  }  }
279    
280  /**  /**
281   * Implementation of hook_widget_info().   * Implementation of hook_widget_info().
282     *
283     * We need custom handling of multiple values for the nodereference_select
284     * widget because we need to combine them into a options list rather
285     * than display multiple elements.
286     *
287     * We will use the content module's default handling for default value.
288     *
289     * Callbacks can be omitted if default handing is used.
290     * They're included here just so this module can be used
291     * as an example for custom modules that might do things
292     * differently.
293   */   */
294  function nodereference_widget_info() {  function nodereference_widget_info() {
295    return array(    return array(
296      'nodereference_select' => array(      'nodereference_select' => array(
297        'label' => t('Select List'),        'label' => t('Select list'),
298        'field types' => array('nodereference'),        'field types' => array('nodereference'),
299          'multiple values' => CONTENT_HANDLE_MODULE,
300          'callbacks' => array(
301            'default value' => CONTENT_CALLBACK_DEFAULT,
302          ),
303      ),      ),
304      'nodereference_autocomplete' => array(      'nodereference_autocomplete' => array(
305        'label' => t('Autocomplete Text Field'),        'label' => t('Autocomplete text field'),
306        'field types' => array('nodereference'),        'field types' => array('nodereference'),
307          'multiple values' => CONTENT_HANDLE_CORE,
308          'callbacks' => array(
309            'default value' => CONTENT_CALLBACK_DEFAULT,
310          ),
311      ),      ),
312    );    );
313  }  }
314    
315  /**  /**
316   * Implementation of hook_widget().   * Implementation of FAPI hook_elements().
317     *
318     * Any FAPI callbacks needed for individual widgets can be declared here,
319     * and the element will be passed to those callbacks for processing.
320     *
321     * Drupal will automatically theme the element using a theme with
322     * the same name as the hook_elements key.
323     *
324     * Autocomplete_path is not used by text_widget but other widgets can use it
325     * (see nodereference and userreference).
326   */   */
327  function nodereference_widget($op, &$node, $field, &$items) {  function nodereference_elements() {
328    if ($field['widget']['type'] == 'nodereference_select') {    return array(
329      switch ($op) {      'nodereference_select' => array(
330        case 'prepare form values':        '#input' => TRUE,
331          $items_transposed = content_transpose_array_rows_cols($items);        '#columns' => array('uid'), '#delta' => 0,
332          $items['default nids'] = $items_transposed['nid'];        '#process' => array('nodereference_select_process'),
333          break;        ),
334        'nodereference_autocomplete' => array(
335        case 'form':        '#input' => TRUE,
336          $form = array();        '#columns' => array('name'), '#delta' => 0,
337          '#process' => array('nodereference_autocomplete_process'),
338          $options = _nodereference_potential_references($field, TRUE);        '#autocomplete_path' => FALSE,
339          foreach ($options as $key => $value) {        ),
340            $options[$key] = _nodereference_item($field, $value, FALSE);      );
341          }  }
         if (!$field['required']) {  
           $options = array(0 => t('<none>')) + $options;  
         }  
342    
343          $form[$field['field_name']] = array('#tree' => TRUE);  /**
344          $form[$field['field_name']]['nids'] = array(   * Implementation of hook_widget().
345            '#type' => 'select',   *
346            '#title' => t($field['widget']['label']),   * Attach a single form element to the form. It will be built out and
347            '#default_value' => $items['default nids'],   * validated in the callback(s) listed in hook_elements. We build it
348            '#multiple' => $field['multiple'],   * out in the callbacks rather than here in hook_widget so it can be
349            '#size' =>  $field['multiple'] ? min(count($options), 6) : 0,   * plugged into any module that can provide it with valid
350            '#options' => $options,   * $field information.
351            '#required' => $field['required'],   *
352            '#description' => content_filter_xss(t($field['widget']['description'])),   * Content module will set the weight, field name and delta values
353          );   * for each form element. This is a change from earlier CCK versions
354     * where the widget managed its own multiple values.
355          return $form;   *
356     * If there are multiple values for this field, the content module will
357        case 'process form values':   * call this function as many times as needed.
358          if ($field['multiple']) {   *
359            // if nothing selected, make it 'none'   * @param $form
360            if (empty($items['nids'])) {   *   the entire form array, $form['#node'] holds node information
361              $items['nids'] = array(0 => '0');   * @param $form_state
362            }   *   the form_state, $form_state['values'][$field['field_name']]
363            // drop the 'none' options if other items were also selected   *   holds the field's form values.
364            elseif (count($items['nids']) > 1) {   * @param $field
365              unset($items['nids'][0]);   *   the field array
366            }   * @param $items
367     *   array of default values for this field
368     * @param $delta
369     *   the order of this item in the array of subelements (0, 1, 2, etc)
370     *
371     * @return
372     *   the form item for a single element for this field
373     */
374    function nodereference_widget(&$form, &$form_state, $field, $items, $delta = 0) {
375      switch ($field['widget']['type']) {
376        case 'nodereference_select':
377          $element = array(
378            '#type' => 'nodereference_select',
379            '#default_value' => $items,
380          );
381          break;
382    
383            $items = array_values(content_transpose_array_rows_cols(array('nid' => $items['nids'])));      case 'nodereference_autocomplete':
384          }        $element = array(
385          else {          '#type' => 'nodereference_autocomplete',
386            $items[0]['nid'] = $items['nids'];          '#default_value' => isset($items[$delta]) ? $items[$delta] : NULL,
387          }          '#value_callback' => 'nodereference_autocomplete_value',
388          // Remove the widget's data representation so it isn't saved.        );
389          unset($items['nids']);        break;
         foreach ($items as $delta => $item) {  
           $items[$delta]['error_field'] =  $field['field_name'] .'][nids';  
         }  
     }  
390    }    }
391    else {    return $element;
392      switch ($op) {  }
       case 'prepare form values':  
         foreach ($items as $delta => $item) {  
           if (!empty($items[$delta]['nid'])) {  
             $items[$delta]['default node_name'] = db_result(db_query(db_rewrite_sql('SELECT n.title FROM {node} n WHERE n.nid = %d'), $items[$delta]['nid']));  
             $items[$delta]['default node_name'] .= ' [nid:'. $items[$delta]['nid'] .']';  
           }  
         }  
         break;  
393    
394        case 'form':  /**
395          $form = array();   * Value for a nodereference autocomplete element.
396          $form[$field['field_name']] = array('#tree' => TRUE);   *
397     * Substitute in the node title for the node nid.
398          if ($field['multiple']) {   */
399            $form[$field['field_name']]['#type'] = 'fieldset';  function nodereference_autocomplete_value($element, $edit = FALSE) {
400            $form[$field['field_name']]['#description'] = content_filter_xss(t($field['widget']['description']));    $field_key  = $element['#columns'][0];
401            $delta = 0;    if (!empty($element['#default_value'][$field_key])) {
402            foreach ($items as $item) {      $nid = $element['#default_value'][$field_key];
403              if ($item['nid']) {      $value = db_result(db_query(db_rewrite_sql('SELECT n.title FROM {node} n WHERE n.nid = %d'), $nid));
404                $form[$field['field_name']][$delta]['node_name'] = array(      $value .= ' [nid:'. $nid .']';
405                  '#type' => 'textfield',      return array($field_key => $value);
406                  '#title' => ($delta == 0) ? t($field['widget']['label']) : '',    }
407                  '#autocomplete_path' => 'nodereference/autocomplete/'. $field['field_name'],    return array($field_key => NULL);
408                  '#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' => $items[0]['default node_name'],  
             '#required' => $field['required'],  
             '#description' => content_filter_xss( t( $field['widget']['description'] )),  
           );  
         }  
         return $form;  
409    
410        case 'validate':  /**
411          foreach ($items as $delta => $item) {   * Process an individual element.
412            $error_field = $field['field_name'] .']['. $delta .'][node_name';   *
413            if (!empty($item['node_name'])) {   * Build the form element. When creating a form using FAPI #process,
414              preg_match('/^(?:\s*|(.*) )?\[\s*nid\s*:\s*(\d+)\s*\]$/', $item['node_name'], $matches);   * note that $element['#value'] is already set.
415              if (!empty($matches)) {   *
416                // explicit nid   * The $fields array is in $form['#field_info'][$element['#field_name']].
417                list(, $title, $nid) = $matches;   */
418                if (!empty($title) && ($n = node_load($nid)) && $title != $n->title) {  function nodereference_select_process($element, $edit, $form_state, $form) {
419                  form_set_error($error_field, t('%name : Title mismatch. Please check your selection.', array('%name' => t($field['widget']['label']))));    // The nodereference_select widget doesn't need to create its own
420                }    // element, it can wrap around the optionwidgets_select element.
421              }    // Add a validation step where the value can be unwrapped.
422              else {    $field_key  = $element['#columns'][0];
423                // no explicit nid    $element[$field_key] = array(
424                $nids = _nodereference_potential_references($field, FALSE, $item['node_name'], TRUE);      '#type' => 'optionwidgets_select',
425                if (empty($nids)) {      '#default_value' => isset($element['#value']) ? $element['#value'] : '',
426                  form_set_error($error_field, t('%name: Found no valid post with that title.', array('%name' => t($field['widget']['label']))));      '#element_validate' => array('optionwidgets_validate', 'nodereference_select_validate'),
427                }  
428                else {      // The following values were set by the content module and need
429                  // TODO:      // to be passed down to the nested element.
430                  // the best thing would be to present the user with an additional form,      '#field_name' => $element['#field_name'],
431                  // allowing the user to choose between valid candidates with the same title      '#delta' => $element['#delta'],
432                  // ATM, we pick the first matching candidate...      '#columns' => $element['#columns'],
433                  $nid = array_shift(array_keys($nids));      '#title' => $element['#title'],
434                }      '#required' => $element['#required'],
435              }      '#description' => $element['#description'],
436            }    );
437          }    return $element;
438          return;  }
439    
440        case 'process form values':  /**
441          foreach ($items as $delta => $item) {   * Process an individual element.
442            $nid = 0;   *
443            if (!empty($item['node_name'])) {   * Build the form element. When creating a form using FAPI #process,
444              preg_match('/^(?:\s*|(.*) )?\[\s*nid\s*:\s*(\d+)\s*\]$/', $item['node_name'], $matches);   * note that $element['#value'] is already set.
445              if (!empty($matches)) {   *
446                // explicit nid   */
447                $nid = $matches[2];  function nodereference_autocomplete_process($element, $edit, $form_state, $form) {
448              }    // The nodereference autocomplete widget doesn't need to create its own
449              else {    // element, it can wrap around the text_textfield element and add an autocomplete
450                // no explicit nid    // path and some extra processing to it.
451                // TODO :    // Add a validation step where the value can be unwrapped.
452                // the best thing would be to present the user with an additional form,    $field_key  = $element['#columns'][0];
453                // allowing the user to choose between valid candidates with the same title  
454                // ATM, we pick the first matching candidate...    $element[$field_key] = array(
455                $nids = _nodereference_potential_references($field, FALSE, $item['node_name'], TRUE);      '#type' => 'text_textfield',
456                $nid = (!empty($nids)) ? array_shift(array_keys($nids)) : 0;      '#default_value' => isset($element['#value']) ? $element['#value'] : '',
457              }      '#autocomplete_path' => 'nodereference/autocomplete/'. $element['#field_name'],
458            }      '#element_validate' => array('nodereference_autocomplete_validate'),
459            // Remove the widget's data representation so it isn't saved.  
460            unset($items[$delta]['node_name']);      // The following values were set by the content module and need
461            if (!empty($nid)) {      // to be passed down to the nested element.
462              $items[$delta]['nid'] = $nid;      '#field_name' => $element['#field_name'],
463              $items[$delta]['error_field'] = $field['field_name'] .']['. $delta .'][node_name';      '#delta' => $element['#delta'],
464            }      '#columns' => $element['#columns'],
465            elseif ($delta > 0) {      '#title' => $element['#title'],
466              // Don't save empty fields when they're not the first value (keep '0' otherwise)      '#required' => $element['#required'],
467              unset($items[$delta]);      '#description' => $element['#description'],
468            }    );
469          }    return $element;
470          break;  }
471    
472    /**
473     * Validate an select element.
474     *
475     * Remove the wrapper layer and set the right element's value.
476     */
477    function nodereference_select_validate($element, &$form_state) {
478      $field_key  = $element['#columns'][0];
479      array_pop($element['#parents']);
480      form_set_value($element, $form_state['values'][$element['#field_name']][$field_key], $form_state);
481    }
482    
483    /**
484     * Validate an autocomplete element.
485     *
486     * Remove the wrapper layer and set the right element's value.
487     */
488    function nodereference_autocomplete_validate($element, &$form_state) {
489      $field_name = $element['#field_name'];
490      $field = content_fields($field_name);
491      $field_key  = $element['#columns'][0];
492      $delta = $element['#delta'];
493      $value = $element['#value'][$field_key];
494      $nid = NULL;
495      if (!empty($value)) {
496        preg_match('/^(?:\s*|(.*) )?\[\s*nid\s*:\s*(\d+)\s*\]$/', $value, $matches);
497        if (!empty($matches)) {
498          // explicit nid
499          list(, $title, $nid) = $matches;
500          if (!empty($title) && ($n = node_load($nid)) && $title != $n->title) {
501            form_set_error($element[$field_key], t('%name: Title mismatch. Please check your selection.'), array('%name' => t($element[$field_key]['#title'])));
502          }
503        }
504        else {
505          // no explicit nid
506          // TODO :
507          // the best thing would be to present the user with an additional form,
508          // allowing the user to choose between valid candidates with the same title
509          // ATM, we pick the first matching candidate...
510          $nids = _nodereference_potential_references($field, FALSE, $value, TRUE);
511          $nid = (!empty($nids)) ? array_shift(array_keys($nids)) : 0;
512      }      }
513    }    }
514      form_set_value($element, $nid, $form_state);
515      return $element;
516    }
517    
518    /**
519     * Implementation of hook_allowed_values().
520     */
521    function nodereference_allowed_values($field) {
522      $options = _nodereference_potential_references($field, TRUE);
523      foreach ($options as $key => $value) {
524        $options[$key] = _nodereference_item($field, $value);
525      }
526      if (!$field['required']) {
527        $options = array(0 => '<'. t('none') .'>') + $options;
528      }
529      return $options;
530  }  }
531    
532  /**  /**
533   * Fetch an array of all candidate referenced nodes, for use in presenting the selection form to the user.   * Fetch an array of all candidate referenced nodes,
534     * for use in presenting the selection form to the user.
535   */   */
536  function _nodereference_potential_references($field, $return_full_nodes = FALSE, $string = '', $exact_string = false) {  function _nodereference_potential_references($field, $return_full_nodes = FALSE, $string = '', $exact_string = false) {
537    if (module_exists('views') && isset($field['advanced_view']) && $field['advanced_view'] != '--' && ($view = views_get_view($field['advanced_view']))) {    if (module_exists('views')
538      && isset($field['advanced_view'])
539      && $field['advanced_view'] != '--'
540      && ($view = views_get_view($field['advanced_view']))) {
541      // advanced field : referenceable nodes defined by a view      // advanced field : referenceable nodes defined by a view
542      // let views.module build the query      // let views.module build the query
543    
544      // arguments for the view      $view->init();
545      $view_args = array();  
546        // TODO is this the right way to do this?
547        // make sure the fields get included in the query.
548    
549        // TODO Which display should we use here?
550        // In our Views 1 to Views 2 updates we are
551        // putting fields in the 'default' display.
552        $display = 'default';
553        $view->set_display($display);
554        $view->display[$display]->style_plugin = 'list';
555    
556        // Add arguments for the view.
557      if (isset($field['advanced_view_args'])) {      if (isset($field['advanced_view_args'])) {
558        // TODO: Support Tokens using token.module ?        // TODO: Support Tokens using token.module ?
559        $view_args = array_map(trim, explode(',', $field['advanced_view_args']));        $view_args = array();
560          $view_args = array_map('trim', explode(',', $field['advanced_view_args']));
561          $view->set_arguments($view_args);
562      }      }
563    
564      if (isset($string)) {      if (isset($string)) {
565        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...)
566          if (!array_key_exists('title', $view->display[$display]->display_options['fields'])) {
567            $view->add_item($display, 'field', 'node', 'title');
568          }
569          // We use the title filter in the view, so make sure it's there.
570          if (!array_key_exists('title', $view->display[$display]->display_options['filters'])) {
571            $view->add_item($display, 'filter', 'node', 'title');
572          }
573          // TODO Is this the best way to set values?
574          $view->display[$display]->display_options['filters']['title']['value'] = $string;
575          $view->display[$display]->display_options['filters']['title']['operator'] = $exact_string ? '=' : 'contains';
576      }      }
577    
578      // we do need title field, so add it if not present (unlikely, but...)      // Make sure the query is not cached
579      $has_title = array_reduce($view->field, create_function('$a, $b', 'return ($b["field"] == "title") || $a;'), false);      $view->is_cacheable = FALSE;
580      if (!$has_title) {      $view->render();
581        views_view_add_field($view, 'node', 'title', '');      $options = array();
582        foreach ($view->result as $row) {
583          foreach ($view->field as $field) {
584            if (!empty($field['handler']) && is_object($field['handler'])) {
585              $options[$row->nid][] = theme('views_view_field', $view, $field, $row);
586            }
587          }
588      }      }
589      views_load_cache();      return $options;
     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); // Views 1.5-  
     $view->is_cacheable = FALSE; // Views 1.6+  
   
     $view_result = views_build_view('result', $view, $view_args);  
     $result = $view_result['result'];  
590    }    }
591    else {    else {
592      // standard field : referenceable nodes defined by content types      // standard field : referenceable nodes defined by content types
# Line 441  function _nodereference_potential_refere Line 594  function _nodereference_potential_refere
594      $related_types = array();      $related_types = array();
595      $args = array();      $args = array();
596    
597      if (isset($field['referenceable_types'])) {      if (is_array($field['referenceable_types'])) {
598        foreach ($field['referenceable_types'] as $related_type) {        foreach ($field['referenceable_types'] as $related_type) {
599          if ($related_type) {          if ($related_type) {
600            $related_types[] = " n.type = '%s'";            $related_types[] = " n.type = '%s'";
# Line 456  function _nodereference_potential_refere Line 609  function _nodereference_potential_refere
609        return array();        return array();
610      }      }
611    
612      if ($string !== '') {      if (isset($string)) {
613        $string_clause = $exact_string ? " AND n.title = '%s'" : " AND n.title LIKE '%%%s%'";        $string_clause = $exact_string ? " AND n.title = '%s'" : " AND n.title LIKE '%%%s%'";
614        $related_clause = "(". $related_clause .")". $string_clause;        $related_clause = "(". $related_clause .")". $string_clause;
615        $args[] = $string;        $args[] = $string;
# Line 465  function _nodereference_potential_refere Line 618  function _nodereference_potential_refere
618      $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);      $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);
619    }    }
620    
   if (db_num_rows($result) == 0) {  
     return array();  
   }  
   
621    $rows = array();    $rows = array();
622    
623    while ($node = db_fetch_object($result)) {    while ($node = db_fetch_object($result)) {
# Line 492  function nodereference_autocomplete($fie Line 641  function nodereference_autocomplete($fie
641    $matches = array();    $matches = array();
642    
643    foreach (_nodereference_potential_references($field, TRUE, $string) as $row) {    foreach (_nodereference_potential_references($field, TRUE, $string) as $row) {
644      $matches[$row->node_title .' [nid:'. $row->nid .']'] = _nodereference_item($field, $row);      $matches[$row->node_title .' [nid:'. $row->nid .']'] = _nodereference_item($field, $row, TRUE);
645    }    }
646    print drupal_to_js($matches);    drupal_json($matches);
   exit();  
647  }  }
648    
649  function _nodereference_item($field, $item, $html = TRUE) {  function _nodereference_item($field, $item, $html = FALSE) {
650    if (module_exists('views') && isset($field['advanced_view']) && $field['advanced_view'] != '--' && ($view = views_get_view($field['advanced_view']))) {    if (module_exists('views') && isset($field['advanced_view']) && $field['advanced_view'] != '--' && ($view = views_get_view($field['advanced_view']))) {
651      $output = theme('nodereference_item_advanced', $item, $view);      $field_names = array();
652        $view->render();
653        foreach ($view->field as $name => $viewfield) {
654          $field_names[] = isset($viewfield->definition['content_field_name']) ? $viewfield->definition['content_field_name'] : $name;
655        }
656        $output = theme('nodereference_item_advanced', $item, $field_names, $view);
657      if (!$html) {      if (!$html) {
658        // Views theming runs check_plain (htmlentities) on the values.        // Views theming runs check_plain (htmlentities) on the values.
659        // We reverse that with html_entity_decode.        // We reverse that with html_entity_decode.
# Line 514  function _nodereference_item($field, $it Line 667  function _nodereference_item($field, $it
667    return $output;    return $output;
668  }  }
669    
670  function theme_nodereference_item_advanced($item, $view) {  function theme_nodereference_item_advanced($item, $field_names, $view) {
   $fields = _views_get_fields();  
671    $item_fields = array();    $item_fields = array();
672    foreach ($view->field as $field) {    foreach ($item as $delta => $value) {
     $value = views_theme_field('views_handle_field', $field['queryname'], $fields, $field, $item, $view);  
673      // remove link tags (ex : for node titles)      // remove link tags (ex : for node titles)
674      $value = preg_replace('/<a[^>]*>(.*)<\/a>/iU', '$1', $value);      $value = preg_replace('/<a[^>]*>(.*)<\/a>/iU', '$1', $value);
675      if (!empty($value)) {      if (!empty($value)) {
676        $item_fields[] = "<span class='view-field view-data-$field[queryname]'>$value</span>";;        $item_fields[] = "<span class='view-field view-data-$field_names[$delta]'>$value</span>";;
677      }      }
678    }    }
679    $output = implode(' - ', $item_fields);    $output = implode(' - ', $item_fields);
# Line 535  function theme_nodereference_item_simple Line 686  function theme_nodereference_item_simple
686  }  }
687    
688  /**  /**
689   * Provide a list of nodes to filter on.   * FAPI theme for an individual elements.
690     *
691     * The textfield or select is already rendered by the
692     * textfield or select themes and the html output
693     * lives in $element['#children']. Override this theme to
694     * make custom changes to the output.
695     *
696     * $element['#field_name'] contains the field name
697     * $element['#delta]  is the position of this element in the group
698   */   */
699  function _nodereference_filter_handler($op, $filterinfo) {  function theme_nodereference_select($element) {
700    $options = array(0 => t('<empty>'));    return $element['#children'];
   $options = $options + _nodereference_potential_references($filterinfo['extra']['field']);  
   return $options;  
701  }  }
702    
703  /**  function theme_nodereference_autocomplete($element) {
704   * Implementation of hook_panels_relationships().    return $element['#children'];
  */  
 function nodereference_panels_relationships() {  
   $args = array();  
   $args['node_from_noderef'] = array(  
     'title' => t('Node from reference'),  
     'keyword' => 'nodereference',  
     'description' => t('Adds a node from a node reference in a node context; if multiple nodes are referenced, this will get the first referenced node only.'),  
     'required context' => new panels_required_context(t('Node'), 'node'),  
     'context' => 'nodereference_node_from_noderef_context',  
     'settings form' => 'nodereference_node_from_noderef_settings_form',  
     'settings form validate' => 'nodereference_node_from_noderef_settings_form_validate',  
     );  
   return $args;  
 }  
   
 /**  
  * Return a new panels context based on an existing context  
  */  
 function nodereference_node_from_noderef_context($context = NULL, $conf) {  
   // If unset it wants a generic, unfilled context, which is just NULL  
   if (empty($context->data)) {  
     return panels_context_create_empty('node', NULL);  
   }  
   if (isset($context->data->{$conf['field_name']}[0]['nid']) && ($nid = $context->data->{$conf['field_name']}[0]['nid'])) {  
     if ($node = node_load($nid)) {  
       return panels_context_create('node', $node);  
     }  
   }  
 }  
   
 /**  
  * Settings form for the panels relationship.  
  */  
 function nodereference_node_from_noderef_settings_form($conf) {  
   $options = array();  
   foreach (content_fields() as $field) {  
     if ($field['type'] == 'nodereference') {  
       $options[$field['field_name']] = t($field['widget']['label']);  
     }  
   }  
   $form['field_name'] = array(  
     '#title' => t('Node reference field'),  
     '#type' => 'select',  
     '#options' => $options,  
     '#default_value' => $conf['field_name'],  
     '#prefix' => '<div class="clear-block">',  
     '#suffix' => '</div>',  
   );  
   
   return $form;  
705  }  }

Legend:
Removed from v.1.39.2.43  
changed lines
  Added in v.1.127

  ViewVC Help
Powered by ViewVC 1.1.2