| [ Index ] |
PHP Cross Reference of Drupal 6 (gatewave) |
[Summary view] [Print] [Text view]
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 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
| Generated: Thu Mar 24 11:18:33 2011 | Cross-referenced by PHPXref 0.7 |