[ Index ]

PHP Cross Reference of Drupal 6 (gatewave)

title

Body

[close]

/sites/all/modules/cck/modules/userreference/ -> userreference.module (source)

   1  <?php
   2  // $Id: userreference.module,v 1.106.2.51 2010/06/10 16:17:06 markuspetrux Exp $
   3  
   4  /**
   5   * @file
   6   * Defines a field type for referencing a user from a node.
   7   */
   8  
   9  /**
  10   * Implementation of hook_menu().
  11   */
  12  function userreference_menu() {
  13    $items = array();
  14    $items['userreference/autocomplete'] = array(
  15      'title' => 'Userreference autocomplete',
  16      'page callback' => 'userreference_autocomplete',
  17      'access arguments' => array('access content'),
  18      'type' => MENU_CALLBACK
  19    );
  20    return $items;
  21  }
  22  
  23  /**
  24   * Implementation of hook_theme().
  25   */
  26  function userreference_theme() {
  27    return array(
  28      'userreference_select' => array(
  29        'arguments' => array('element' => NULL),
  30      ),
  31      'userreference_buttons' => array(
  32        'arguments' => array('element' => NULL),
  33      ),
  34      'userreference_autocomplete' => array(
  35        'arguments' => array('element' => NULL),
  36      ),
  37      'userreference_formatter_default' => array(
  38        'arguments' => array('element'),
  39      ),
  40      'userreference_formatter_plain' => array(
  41        'arguments' => array('element'),
  42      ),
  43    );
  44  }
  45  
  46  /**
  47   * Implementaion of hook_ctools_plugin_directory().
  48   */
  49  function userreference_ctools_plugin_directory($module, $plugin) {
  50    if ($module == 'ctools' && $plugin == 'relationships') {
  51      return 'panels/' . $plugin;
  52    }
  53  }
  54  
  55  /**
  56   * Implementation of hook_field_info().
  57   */
  58  function userreference_field_info() {
  59    return array(
  60      'userreference' => array(
  61        'label' => t('User reference'),
  62        'description' => t('Store the ID of a related user as an integer value.'),
  63  //      'content_icon' => 'icon_content_noderef.png',
  64      ),
  65    );
  66  }
  67  
  68  /**
  69   * Implementation of hook_field_settings().
  70   */
  71  function userreference_field_settings($op, $field) {
  72    switch ($op) {
  73      case 'form':
  74        $form = array();
  75        $form['referenceable_roles'] = array(
  76          '#type' => 'checkboxes',
  77          '#title' => t('User roles that can be referenced'),
  78          '#default_value' => isset($field['referenceable_roles']) && is_array($field['referenceable_roles']) ? array_filter($field['referenceable_roles']) : array(),
  79          '#options' => user_roles(1),
  80        );
  81        $form['referenceable_status'] = array(
  82          '#type' => 'radios',
  83          '#title' => t('User status that can be referenced'),
  84          '#default_value' => isset($field['referenceable_status']) ? $field['referenceable_status'] : '',
  85          '#options' => array('' => t('All users'), 1 => t('Active users'), 0 => t('Blocked users')),
  86        );
  87        if (module_exists('views')) {
  88          $views = array('--' => '--');
  89          $all_views = views_get_all_views();
  90          foreach ($all_views as $view) {
  91            // Only 'users' views that have fields will work for our purpose.
  92            if ($view->base_table == 'users' && !empty($view->display['default']->display_options['fields'])) {
  93              if ($view->type == 'Default') {
  94                $views[t('Default Views')][$view->name] = $view->name;
  95              }
  96              else {
  97                $views[t('Existing Views')][$view->name] = $view->name;
  98              }
  99            }
 100          }
 101  
 102          $form['advanced'] = array(
 103             '#type' => 'fieldset',
 104             '#title' => t('Advanced - Users that can be referenced (View)'),
 105             '#collapsible' => TRUE,
 106             '#collapsed' => !isset($field['advanced_view']) || $field['advanced_view'] == '--',
 107           );
 108          if (count($views) > 1) {
 109            $form['advanced']['advanced_view'] = array(
 110              '#type' => 'select',
 111              '#title' => t('View used to select the users'),
 112              '#options' => $views,
 113              '#default_value' => isset($field['advanced_view']) ? $field['advanced_view'] : '--',
 114              '#description' =>  t('<p>Choose the "Views module" view that selects the users that can be referenced.<br />Note:</p>') .
 115                t('<ul><li>Only views that have fields will work for this purpose.</li><li>This will discard the "Referenceable Roles" and "Referenceable Status" settings above. Use the view\'s "filters" section instead.</li><li>Use the view\'s "fields" section to display additional informations about candidate users on user creation/edition form.</li><li>Use the view\'s "sort criteria" section to determine the order in which candidate users will be displayed.</li></ul>'),
 116            );
 117            $form['advanced']['advanced_view_args'] = array(
 118              '#type' => 'textfield',
 119              '#title' => t('View arguments'),
 120              '#default_value' => isset($field['advanced_view_args']) ? $field['advanced_view_args'] : '',
 121              '#required' => FALSE,
 122              '#description' => t('Provide a comma separated list of arguments to pass to the view.'),
 123            );
 124          }
 125          else {
 126            $form['advanced']['no_view_help'] = array(
 127              '#value' => t('<p>The list of user that can be referenced can be based on a "Views module" view but no appropriate views were found. <br />Note:</p>') .
 128                t('<ul><li>Only views that have fields will work for this purpose.</li><li>This will discard the "Referenceable Roles" and "Referenceable Status" settings above. Use the view\'s "filters" section instead.</li><li>Use the view\'s "fields" section to display additional informations about candidate users on user creation/edition form.</li><li>Use the view\'s "sort criteria" section to determine the order in which candidate users will be displayed.</li></ul>'),
 129            );
 130          }
 131        }
 132        return $form;
 133  
 134      case 'save':
 135        $settings = array('referenceable_roles', 'referenceable_status');
 136        if (module_exists('views')) {
 137          $settings[] = 'advanced_view';
 138          $settings[] = 'advanced_view_args';
 139        }
 140        return $settings;
 141  
 142      case 'database columns':
 143        $columns = array(
 144          'uid' => array('type' => 'int', 'unsigned' => TRUE, 'not null' => FALSE, 'index' => TRUE),
 145        );
 146        return $columns;
 147  
 148      case 'views data':
 149        $data = content_views_field_views_data($field);
 150        $db_info = content_database_info($field);
 151        $table_alias = content_views_tablename($field);
 152  
 153        // Filter : swap the handler to the 'in' operator.
 154        $data[$table_alias][$field['field_name'] .'_uid']['filter']['handler'] = 'content_handler_filter_many_to_one';
 155        // Argument: get the user name for summaries.
 156        // We need to join a new instance of the users table.
 157        $data["users_$table_alias"]['table']['join']['node'] = array(
 158          'table' => 'users',
 159          'field' => 'uid',
 160          'left_table' => $table_alias,
 161          'left_field' => $field['field_name'] .'_uid',
 162        );
 163        $data[$table_alias][$field['field_name'] .'_uid']['argument']['handler'] = 'content_handler_argument_reference';
 164        $data[$table_alias][$field['field_name'] .'_uid']['argument']['name table'] = "users_$table_alias";
 165        $data[$table_alias][$field['field_name'] .'_uid']['argument']['name field'] = 'name';
 166        // Relationship: Add a relationship for related user.
 167        $data[$table_alias][$field['field_name'] .'_uid']['relationship'] = array(
 168          'base' => 'users',
 169          'field' => $db_info['columns']['uid']['column'],
 170          'handler' => 'content_handler_relationship',
 171          'label' => t($field['widget']['label']),
 172          'content_field_name' => $field['field_name'],
 173        );
 174        return $data;
 175  
 176    }
 177  }
 178  
 179  /**
 180   * Implementation of hook_field().
 181   */
 182  function userreference_field($op, &$node, $field, &$items, $teaser, $page) {
 183    switch ($op) {
 184      case 'validate':
 185        // Extract uids to check.
 186        $ids = array();
 187        foreach ($items as $delta => $item) {
 188          if (is_array($item) && !empty($item['uid'])) {
 189            if (is_numeric($item['uid'])) {
 190              $ids[] = $item['uid'];
 191            }
 192            else {
 193              $error_element = isset($item['_error_element']) ? $item['_error_element'] : '';
 194              if (is_array($item) && isset($item['_error_element'])) unset($item['_error_element']);
 195              form_set_error($error_element, t('%name: invalid input.', array('%name' => t($field['widget']['label']))));
 196            }
 197          }
 198        }
 199        // Prevent performance hog if there are no ids to check.
 200        if ($ids) {
 201          $refs = _userreference_potential_references($field, '', NULL, $ids);
 202          foreach ($items as $delta => $item) {
 203            if (is_array($item)) {
 204              $error_element = isset($item['_error_element']) ? $item['_error_element'] : '';
 205              if (is_array($item) && isset($item['_error_element'])) unset($item['_error_element']);
 206              if (!empty($item['uid']) && !isset($refs[$item['uid']])) {
 207                form_set_error($error_element, t('%name: invalid user.', array('%name' => t($field['widget']['label']))));
 208              }
 209            }
 210          }
 211        }
 212        return $items;
 213    }
 214  }
 215  
 216  /**
 217   * Implementation of hook_content_is_empty().
 218   */
 219  function userreference_content_is_empty($item, $field) {
 220    if (empty($item['uid'])) {
 221      return TRUE;
 222    }
 223    return FALSE;
 224  }
 225  
 226  /**
 227   * Implementation of hook_field_formatter_info().
 228   */
 229  function userreference_field_formatter_info() {
 230    return array(
 231      'default' => array(
 232        'label' => t('Default'),
 233        'field types' => array('userreference'),
 234        'multiple values' => CONTENT_HANDLE_CORE,
 235      ),
 236      'plain' => array(
 237        'label' => t('Plain text'),
 238        'field types' => array('userreference'),
 239        'multiple values' => CONTENT_HANDLE_CORE,
 240      ),
 241    );
 242  }
 243  
 244  /**
 245   * Theme function for 'default' userreference field formatter.
 246   */
 247  function theme_userreference_formatter_default($element) {
 248    $output = '';
 249  
 250    if (isset($element['#item']['uid']) && $account = user_load(array('uid' => $element['#item']['uid']))) {
 251      $output = theme('username', $account);
 252    }
 253    return $output;
 254  }
 255  
 256  /**
 257   * Theme function for 'plain' userreference field formatter.
 258   */
 259  function theme_userreference_formatter_plain($element) {
 260    $output = '';
 261    if (isset($element['#item']['uid']) && $account = user_load(array('uid' => $element['#item']['uid']))) {
 262      $output = $account->name;
 263    }
 264    return $output;
 265  }
 266  
 267  /**
 268   * Implementation of hook_widget_info().
 269   *
 270   * We need custom handling of multiple values for the userreference_select
 271   * widget because we need to combine them into a options list rather
 272   * than display multiple elements.
 273   *
 274   * We will use the content module's default handling for default value.
 275   *
 276   * Callbacks can be omitted if default handing is used.
 277   * They're included here just so this module can be used
 278   * as an example for custom modules that might do things
 279   * differently.
 280   */
 281  function userreference_widget_info() {
 282    return array(
 283      'userreference_select' => array(
 284        'label' => t('Select list'),
 285        'field types' => array('userreference'),
 286        'multiple values' => CONTENT_HANDLE_MODULE,
 287        'callbacks' => array(
 288          'default value' => CONTENT_CALLBACK_DEFAULT,
 289        ),
 290      ),
 291      'userreference_buttons' => array(
 292        'label' => t('Check boxes/radio buttons'),
 293        'field types' => array('userreference'),
 294        'multiple values' => CONTENT_HANDLE_MODULE,
 295        'callbacks' => array(
 296          'default value' => CONTENT_CALLBACK_DEFAULT,
 297        ),
 298      ),
 299      'userreference_autocomplete' => array(
 300        'label' => t('Autocomplete text field'),
 301        'field types' => array('userreference'),
 302        'multiple values' => CONTENT_HANDLE_CORE,
 303        'callbacks' => array(
 304          'default value' => CONTENT_CALLBACK_DEFAULT,
 305        ),
 306      ),
 307    );
 308  }
 309  
 310  /**
 311   * Implementation of FAPI hook_elements().
 312   *
 313   * Any FAPI callbacks needed for individual widgets can be declared here,
 314   * and the element will be passed to those callbacks for processing.
 315   *
 316   * Drupal will automatically theme the element using a theme with
 317   * the same name as the hook_elements key.
 318   *
 319   * Autocomplete_path is not used by text_widget but other widgets can use it
 320   * (see nodereference and userreference).
 321   */
 322  function userreference_elements() {
 323    return array(
 324      'userreference_select' => array(
 325        '#input' => TRUE,
 326        '#columns' => array('uid'), '#delta' => 0,
 327        '#process' => array('userreference_select_process'),
 328      ),
 329      'userreference_buttons' => array(
 330        '#input' => TRUE,
 331        '#columns' => array('uid'), '#delta' => 0,
 332        '#process' => array('userreference_buttons_process'),
 333      ),
 334      'userreference_autocomplete' => array(
 335        '#input' => TRUE,
 336        '#columns' => array('name'), '#delta' => 0,
 337        '#process' => array('userreference_autocomplete_process'),
 338        '#autocomplete_path' => FALSE,
 339        ),
 340      );
 341  }
 342  
 343  /**
 344   * Implementation of hook_widget_settings().
 345   */
 346  function userreference_widget_settings($op, $widget) {
 347    switch ($op) {
 348      case 'form':
 349        $form = array();
 350        $match = isset($widget['autocomplete_match']) ? $widget['autocomplete_match'] : 'contains';
 351        $size = (isset($widget['size']) && is_numeric($widget['size'])) ? $widget['size'] : 60;
 352        if ($widget['type'] == 'userreference_autocomplete') {
 353          $form['autocomplete_match'] = array(
 354            '#type' => 'select',
 355            '#title' => t('Autocomplete matching'),
 356            '#default_value' => $match,
 357            '#options' => array(
 358              'starts_with' => t('Starts with'),
 359              'contains' => t('Contains'),
 360            ),
 361            '#description' => t('Select the method used to collect autocomplete suggestions. Note that <em>Contains</em> can cause performance issues on sites with thousands of users.'),
 362          );
 363          $form['size'] = array(
 364            '#type' => 'textfield',
 365            '#title' => t('Size of textfield'),
 366            '#default_value' => $size,
 367            '#element_validate' => array('_element_validate_integer_positive'),
 368            '#required' => TRUE,
 369          );
 370        }
 371        else {
 372          $form['autocomplete_match'] = array('#type' => 'hidden', '#value' => $match);
 373          $form['size'] = array('#type' => 'hidden', '#value' => $size);
 374        }
 375        $form['reverse_link'] = array(
 376          '#type' => 'checkbox',
 377          '#title' => t('Reverse link'),
 378          '#default_value' => isset($widget['reverse_link']) ? $widget['reverse_link'] : 0,
 379          '#description' => t('If selected, a reverse link back to the referencing node will displayed on the referenced user record.'),
 380        );
 381        return $form;
 382  
 383      case 'save':
 384        return array('autocomplete_match', 'size', 'reverse_link');
 385    }
 386  }
 387  
 388  /**
 389   * Implementation of hook_widget().
 390   *
 391   * Attach a single form element to the form. It will be built out and
 392   * validated in the callback(s) listed in hook_elements. We build it
 393   * out in the callbacks rather than here in hook_widget so it can be
 394   * plugged into any module that can provide it with valid
 395   * $field information.
 396   *
 397   * Content module will set the weight, field name and delta values
 398   * for each form element. This is a change from earlier CCK versions
 399   * where the widget managed its own multiple values.
 400   *
 401   * If there are multiple values for this field, the content module will
 402   * call this function as many times as needed.
 403   *
 404   * @param $form
 405   *   the entire form array, $form['#node'] holds node information
 406   * @param $form_state
 407   *   the form_state, $form_state['values'][$field['field_name']]
 408   *   holds the field's form values.
 409   * @param $field
 410   *   the field array
 411   * @param $items
 412   *   array of default values for this field
 413   * @param $delta
 414   *   the order of this item in the array of subelements (0, 1, 2, etc)
 415   *
 416   * @return
 417   *   the form item for a single element for this field
 418   */
 419  function userreference_widget(&$form, &$form_state, $field, $items, $delta = 0) {
 420    switch ($field['widget']['type']) {
 421      case 'userreference_select':
 422        $element = array(
 423          '#type' => 'userreference_select',
 424          '#default_value' => $items,
 425        );
 426        break;
 427  
 428      case 'userreference_buttons':
 429        $element = array(
 430          '#type' => 'userreference_buttons',
 431          '#default_value' => $items,
 432        );
 433        break;
 434  
 435      case 'userreference_autocomplete':
 436        $element = array(
 437          '#type' => 'userreference_autocomplete',
 438          '#default_value' => isset($items[$delta]) ? $items[$delta] : NULL,
 439          '#value_callback' => 'userreference_autocomplete_value',
 440        );
 441        break;
 442    }
 443    return $element;
 444  }
 445  
 446  /**
 447   * Value for a userreference autocomplete element.
 448   *
 449   * Substitute in the user name for the uid.
 450   */
 451  function userreference_autocomplete_value($element, $edit = FALSE) {
 452    $field_key  = $element['#columns'][0];
 453    if (!empty($element['#default_value'][$field_key])) {
 454      $value = db_result(db_query("SELECT name FROM {users} WHERE uid = '%d'", $element['#default_value'][$field_key]));
 455      return array($field_key => $value);
 456    }
 457    return array($field_key => NULL);
 458  }
 459  
 460  /**
 461   * Process an individual element.
 462   *
 463   * Build the form element. When creating a form using FAPI #process,
 464   * note that $element['#value'] is already set.
 465   *
 466   * The $fields array is in $form['#field_info'][$element['#field_name']].
 467   */
 468  function userreference_select_process($element, $edit, $form_state, $form) {
 469    // The userreference_select widget doesn't need to create its own
 470    // element, it can wrap around the optionwidgets_select element.
 471    // Add a validation step where the value can be unwrapped.
 472    $field_key  = $element['#columns'][0];
 473    $element[$field_key] = array(
 474      '#type' => 'optionwidgets_select',
 475      '#default_value' => isset($element['#value']) ? $element['#value'] : '',
 476      // The following values were set by the content module and need
 477      // to be passed down to the nested element.
 478      '#title' => $element['#title'],
 479      '#required' => $element['#required'],
 480      '#description' => $element['#description'],
 481      '#field_name' => $element['#field_name'],
 482      '#type_name' => $element['#type_name'],
 483      '#delta' => $element['#delta'],
 484      '#columns' => $element['#columns'],
 485    );
 486    if (empty($element[$field_key]['#element_validate'])) {
 487      $element[$field_key]['#element_validate'] = array();
 488    }
 489    array_unshift($element[$field_key]['#element_validate'], 'userreference_optionwidgets_validate');
 490    return $element;
 491  }
 492  
 493  /**
 494   * Process an individual element.
 495   *
 496   * Build the form element. When creating a form using FAPI #process,
 497   * note that $element['#value'] is already set.
 498   *
 499   * The $fields array is in $form['#field_info'][$element['#field_name']].
 500   */
 501  function userreference_buttons_process($element, $edit, $form_state, $form) {
 502    // The userreference_select widget doesn't need to create its own
 503    // element, it can wrap around the optionwidgets_select element.
 504    // Add a validation step where the value can be unwrapped.
 505    $field_key  = $element['#columns'][0];
 506    $element[$field_key] = array(
 507      '#type' => 'optionwidgets_buttons',
 508      '#default_value' => isset($element['#value']) ? $element['#value'] : '',
 509      // The following values were set by the content module and need
 510      // to be passed down to the nested element.
 511      '#title' => $element['#title'],
 512      '#required' => $element['#required'],
 513      '#description' => $element['#description'],
 514      '#field_name' => $element['#field_name'],
 515      '#type_name' => $element['#type_name'],
 516      '#delta' => $element['#delta'],
 517      '#columns' => $element['#columns'],
 518    );
 519    if (empty($element[$field_key]['#element_validate'])) {
 520      $element[$field_key]['#element_validate'] = array();
 521    }
 522    array_unshift($element[$field_key]['#element_validate'], 'userreference_optionwidgets_validate');
 523    return $element;
 524  }
 525  
 526  /**
 527   * Process an individual element.
 528   *
 529   * Build the form element. When creating a form using FAPI #process,
 530   * note that $element['#value'] is already set.
 531   *
 532   */
 533  function userreference_autocomplete_process($element, $edit, $form_state, $form) {
 534    // The userreference autocomplete widget doesn't need to create its own
 535    // element, it can wrap around the text_textfield element and add an autocomplete
 536    // path and some extra processing to it.
 537    // Add a validation step where the value can be unwrapped.
 538    $field_key  = $element['#columns'][0];
 539  
 540    $element[$field_key] = array(
 541      '#type' => 'text_textfield',
 542      '#default_value' => isset($element['#value']) ? $element['#value'] : '',
 543      '#autocomplete_path' => 'userreference/autocomplete/'. $element['#field_name'],
 544      // The following values were set by the content module and need
 545      // to be passed down to the nested element.
 546      '#title' => $element['#title'],
 547      '#required' => $element['#required'],
 548      '#description' => $element['#description'],
 549      '#field_name' => $element['#field_name'],
 550      '#type_name' => $element['#type_name'],
 551      '#delta' => $element['#delta'],
 552      '#columns' => $element['#columns'],
 553    );
 554    if (empty($element[$field_key]['#element_validate'])) {
 555      $element[$field_key]['#element_validate'] = array();
 556    }
 557    array_unshift($element[$field_key]['#element_validate'], 'userreference_autocomplete_validate');
 558  
 559    // Used so that hook_field('validate') knows where to flag an error.
 560    $element['_error_element'] = array(
 561      '#type' => 'value',
 562      // Wrapping the element around a text_textfield element creates a
 563      // nested element, so the final id will look like 'field-name-0-uid-uid'.
 564      '#value' => implode('][', array_merge($element['#parents'], array($field_key, $field_key))),
 565    );
 566    return $element;
 567  }
 568  
 569  /**
 570   * Validate a select/buttons element.
 571   *
 572   * Remove the wrapper layer and set the right element's value.
 573   * We don't know exactly where this element is, so we drill down
 574   * through the element until we get to our key.
 575   *
 576   * We use $form_state['values'] instead of $element['#value']
 577   * to be sure we have the most accurate value when other modules
 578   * like optionwidgets are using #element_validate to alter the value.
 579   */
 580  function userreference_optionwidgets_validate($element, &$form_state) {
 581    $field_key  = $element['#columns'][0];
 582  
 583    $value = $form_state['values'];
 584    $new_parents = array();
 585    foreach ($element['#parents'] as $parent) {
 586      $value = $value[$parent];
 587      // Use === to be sure we get right results if parent is a zero (delta) value.
 588      if ($parent === $field_key) {
 589        $element['#parents'] = $new_parents;
 590        form_set_value($element, $value, $form_state);
 591        break;
 592      }
 593      $new_parents[] = $parent;
 594    }
 595  }
 596  
 597  /**
 598   * Validate an autocomplete element.
 599   *
 600   * Remove the wrapper layer and set the right element's value.
 601   * This will move the nested value at 'field-name-0-uid-uid'
 602   * back to its original location, 'field-name-0-uid'.
 603   */
 604  function userreference_autocomplete_validate($element, &$form_state) {
 605    $field_name = $element['#field_name'];
 606    $type_name = $element['#type_name'];
 607    $field = content_fields($field_name, $type_name);
 608    $field_key = $element['#columns'][0];
 609    $value = $element['#value'][$field_key];
 610    $uid = NULL;
 611    if (!empty($value)) {
 612      $reference = _userreference_potential_references($field, $value, 'equals', NULL, 1);
 613      if (empty($reference)) {
 614        form_error($element[$field_key], t('%name: found no valid user with that name.', array('%name' => t($field['widget']['label']))));
 615      }
 616      else {
 617        $uid = key($reference);
 618      }
 619    }
 620    form_set_value($element, $uid, $form_state);
 621  }
 622  
 623  /**
 624   * Implementation of hook_allowed_values().
 625   */
 626  function userreference_allowed_values($field) {
 627    $references = _userreference_potential_references($field);
 628  
 629    $options = array();
 630    foreach ($references as $key => $value) {
 631      $options[$key] = $value['rendered'];
 632    }
 633  
 634    return $options;
 635  }
 636  
 637  /**
 638   * Fetch an array of all candidate referenced users.
 639   *
 640   * This info is used in various places (aloowed values, autocomplete results,
 641   * input validation...). Some of them only need the uids, others nid + names,
 642   * others yet uid + names + rendered row (for display in widgets).
 643   * The array we return contains all the potentially needed information, and lets
 644   * consumers use the parts they actually need.
 645   *
 646   * @param $field
 647   *   The field description.
 648   * @param $string
 649   *   Optional string to filter usernames on (used by autocomplete)
 650   * @param $match
 651   *   Operator to match filtered name against, can be any of:
 652   *   'contains', 'equals', 'starts_with'
 653   * @param $ids
 654   *   Optional user ids to lookup (the $string and $match arguments will be
 655   *   ignored).
 656   * @param $limit
 657   *   If non-zero, limit the size of the result set.
 658   *
 659   * @return
 660   *   An array of valid users in the form:
 661   *   array(
 662   *     uid => array(
 663   *       'title' => The user name,
 664   *       'rendered' => The text to display in widgets (can be HTML)
 665   *     ),
 666   *     ...
 667   *   )
 668   */
 669  function _userreference_potential_references($field, $string = '', $match = 'contains', $ids = array(), $limit = NULL) {
 670    static $results = array();
 671  
 672    // Create unique id for static cache.
 673    $cid = $field['field_name'] .':'. $match .':'. ($string !== '' ? $string : implode('-', $ids)) .':'. $limit;
 674    if (!isset($results[$cid])) {
 675      $references = FALSE;
 676      if (module_exists('views') && !empty($field['advanced_view']) && $field['advanced_view'] != '--') {
 677        $references = _userreference_potential_references_views($field, $string, $match, $ids, $limit);
 678      }
 679      // If the view doesn't exist, we got FALSE, and fallback to the regular 'standard mode'.
 680  
 681      if ($references === FALSE) {
 682        $references = _userreference_potential_references_standard($field, $string, $match, $ids, $limit);
 683      }
 684  
 685      // Store the results.
 686      $results[$cid] = !empty($references) ? $references : array();
 687    }
 688  
 689    return $results[$cid];
 690  }
 691  
 692  /**
 693   * Helper function for _userreference_potential_references():
 694   * case of Views-defined referenceable users.
 695   */
 696  function _userreference_potential_references_views($field, $string = '', $match = 'contains', $ids = array(), $limit = NULL) {
 697    $view_name = $field['advanced_view'];
 698  
 699    if ($view = views_get_view($view_name)) {
 700      // We add a display, and let it derive from the 'default' display.
 701      // TODO: We should let the user pick a display in the fields settings - sort of requires AHAH...
 702      $display = $view->add_display('content_references');
 703      $view->set_display($display);
 704  
 705      // TODO from merlinofchaos on IRC : arguments using summary view can defeat the style setting.
 706      // We might also need to check if there's an argument, and set *its* style_plugin as well.
 707      $view->display_handler->set_option('style_plugin', 'content_php_array_autocomplete');
 708      $view->display_handler->set_option('row_plugin', 'fields');
 709      // Used in content_plugin_style_php_array::render(), to get
 710      // the 'field' to be used as title.
 711      $view->display_handler->set_option('content_title_field', 'name');
 712  
 713      // Additional options to let content_plugin_display_references::query()
 714      // narrow the results.
 715      $options = array(
 716        'table' => 'users',
 717        'field_string' => 'name',
 718        'string' => $string,
 719        'match' => $match,
 720        'field_id' => 'uid',
 721        'ids' => $ids,
 722      );
 723      $view->display_handler->set_option('content_options', $options);
 724  
 725      // TODO : for consistency, a fair amount of what's below
 726      // should be moved to content_plugin_display_references
 727  
 728      // Limit result set size.
 729      $limit = isset($limit) ? $limit : 0;
 730      $view->display_handler->set_option('items_per_page', $limit);
 731  
 732      // Get arguments for the view.
 733      if (!empty($field['advanced_view_args'])) {
 734        // TODO: Support Tokens using token.module ?
 735        $view_args = array_map('trim', explode(',', $field['advanced_view_args']));
 736      }
 737      else {
 738        $view_args = array();
 739      }
 740  
 741      // We do need name field, so add it if not present (unlikely, but...)
 742      $fields = $view->get_items('field', $display);
 743      if (!isset($fields['name'])) {
 744        $view->add_item($display, 'field', 'users', 'name');
 745      }
 746  
 747      // If not set, make all fields inline and define a separator.
 748      $options = $view->display_handler->get_option('row_options');
 749      if (empty($options['inline'])) {
 750        $options['inline'] = drupal_map_assoc(array_keys($view->get_items('field', $display)));
 751      }
 752      if (empty($options['separator'])) {
 753        $options['separator'] = '-';
 754      }
 755      $view->display_handler->set_option('row_options', $options);
 756  
 757      // Make sure the query is not cached
 758      $view->is_cacheable = FALSE;
 759  
 760      // Get the results.
 761      $result = $view->execute_display($display, $view_args);
 762    }
 763    else {
 764      $result = FALSE;
 765    }
 766  
 767    return $result;
 768  }
 769  
 770  /**
 771   * Helper function for _userreference_potential_references():
 772   * referenceable users defined by user role and status
 773   */
 774  function _userreference_potential_references_standard($field, $string = '', $match = 'contains', $ids = array(), $limit = NULL) {
 775    $where = array();
 776    $args = array();
 777    $join = array();
 778  
 779    if ($string !== '') {
 780      $like = $GLOBALS["db_type"] == 'pgsql' ? "ILIKE" : "LIKE";
 781      $match_clauses = array(
 782        'contains' => "$like '%%%s%%'",
 783        'equals' => "= '%s'",
 784        'starts_with' => "$like '%s%%'",
 785      );
 786      $where[] = 'u.name '. (isset($match_clauses[$match]) ? $match_clauses[$match] : $match_clauses['contains']);
 787      $args[] = $string;
 788    }
 789    elseif ($ids) {
 790      $where[] = 'u.uid IN (' . db_placeholders($ids) . ')';
 791      $args = array_merge($args, $ids);
 792    }
 793    else {
 794      $where[] = "u.uid > 0";
 795    }
 796  
 797    $roles = array();
 798    if (isset($field['referenceable_roles']) && is_array($field['referenceable_roles'])) {
 799      // keep only selected checkboxes
 800      $roles = array_filter($field['referenceable_roles']);
 801      // filter invalid values that seems to get through sometimes ??
 802      $roles = array_intersect(array_keys(user_roles(1)), $roles);
 803    }
 804    if (!empty($roles) && !in_array(DRUPAL_AUTHENTICATED_RID, $roles)) {
 805      $where[] = "r.rid IN (". implode($roles, ',') .")";
 806      $join[] = 'LEFT JOIN {users_roles} r ON u.uid = r.uid';
 807    }
 808  
 809    if (isset($field['referenceable_status']) && is_numeric($field['referenceable_status'])) {
 810      $where[] = 'u.status = %d';
 811      $args[] = $field['referenceable_status'];
 812    }
 813  
 814    $users = array();
 815    $where_clause = $where ? 'WHERE ('. implode(') AND (', $where) .')' : '';
 816    $result = db_query('SELECT u.name, u.uid FROM {users} u '. implode(' ', $join) ." $where_clause ORDER BY u.name ASC", $args);
 817    while ($user = db_fetch_object($result)) {
 818      $users[$user->uid] = array(
 819        'title' => $user->name,
 820        'rendered' => check_plain($user->name),
 821      );
 822    }
 823    return $users;
 824  }
 825  
 826  /**
 827   * Menu callback; Retrieve a pipe delimited string of autocomplete suggestions for existing users
 828   */
 829  function userreference_autocomplete($field_name, $string = '') {
 830    $fields = content_fields();
 831    $field = $fields[$field_name];
 832    $match = isset($field['widget']['autocomplete_match']) ? $field['widget']['autocomplete_match'] : 'contains';
 833    $matches = array();
 834  
 835    $references = _userreference_potential_references($field, $string, $match, array(), 10);
 836    foreach ($references as $id => $row) {
 837      // Add a class wrapper for a few required CSS overrides.
 838      $matches[$row['title']] = '<div class="reference-autocomplete">'. $row['rendered'] . '</div>';
 839    }
 840    drupal_json($matches);
 841  }
 842  
 843  /**
 844   * Implementation of hook_user().
 845   */
 846  function userreference_user($type, &$edit, &$account) {
 847    switch ($type) {
 848      case 'load':
 849        // Only add links if we are on the user 'view' page.
 850        if (arg(0) != 'user' || arg(2)) {
 851          return;
 852        }
 853        // find CCK userreference field tables
 854        // search through them for matching user ids and load those nodes
 855        $additions = array();
 856        $types = content_types();
 857  
 858        // Find the table and columns to search through, if the same
 859        // table comes up in more than one content type, we only need
 860        // to search it once.
 861        $search_tables = array();
 862        foreach ($types as $type_name => $type) {
 863          foreach ($type['fields'] as $field) {
 864            // Only add tables when reverse link has been selected.
 865            if ($field['type'] == 'userreference' && !empty($field['widget']['reverse_link'])) {
 866              $db_info = content_database_info($field);
 867              $search_tables[$db_info['table']][] = $db_info['columns']['uid']['column'];
 868            }
 869          }
 870        }
 871        foreach ($search_tables as $table => $columns) {
 872          foreach ($columns as $column) {
 873            $ids = db_query(db_rewrite_sql("SELECT DISTINCT(n.nid), n.title, n.type FROM {node} n LEFT JOIN {". $table ."} f ON n.vid = f.vid WHERE f.". $column ."=". $account->uid. " AND n.status = 1"));
 874            while ($data = db_fetch_object($ids)) {
 875              $additions[$data->type][$data->nid] = $data->title;
 876            }
 877          }
 878        }
 879        $account->userreference = $additions;
 880        break;
 881  
 882      case 'view':
 883        if (!empty($account->userreference)) {
 884          $node_types = content_types();
 885          $additions = array();
 886          $values = array();
 887          foreach ($account->userreference as $node_type => $nodes) {
 888            foreach ($nodes as $nid => $title) {
 889              $values[$node_type][] = l($title, 'node/'. $nid);
 890            }
 891            if (isset($values[$node_type])) {
 892              $additions[] = array(
 893                '#type' => 'user_profile_item',
 894                '#title' => check_plain($node_types[$node_type]['name']),
 895                '#value' => theme('item_list', $values[$node_type]),
 896              );
 897            }
 898          }
 899          if ($additions) {
 900            $account->content['userreference'] = $additions + array(
 901              '#type' => 'user_profile_category',
 902              '#attributes' => array('class' => 'user-member'),
 903              '#title' => t('Related content'),
 904              '#weight' => 10,
 905            );
 906          }
 907        }
 908        break;
 909    }
 910  }
 911  
 912  /**
 913   * FAPI theme for an individual elements.
 914   *
 915   * The textfield or select is already rendered by the
 916   * textfield or select themes and the html output
 917   * lives in $element['#children']. Override this theme to
 918   * make custom changes to the output.
 919   *
 920   * $element['#field_name'] contains the field name
 921   * $element['#delta]  is the position of this element in the group
 922   */
 923  function theme_userreference_select($element) {
 924    return $element['#children'];
 925  }
 926  
 927  function theme_userreference_buttons($element) {
 928    return $element['#children'];
 929  }
 930  
 931  function theme_userreference_autocomplete($element) {
 932    return $element['#children'];
 933  }


Generated: Thu Mar 24 11:18:33 2011 Cross-referenced by PHPXref 0.7