1<?php 2/** 3 * @defgroup views_field_handlers Views' field handlers 4 * @{ 5 * Handlers to tell Views how to build and display fields. 6 * 7 */ 8 9/** 10 * Base field handler that has no options and renders an unformatted field. 11 * 12 * Definition terms: 13 * - additional fields: An array of fields that should be added to the query 14 * for some purpose. The array is in the form of: 15 * array('identifier' => array('table' => tablename, 16 * 'field' => fieldname); as many fields as are necessary 17 * may be in this array. 18 * - click sortable: If TRUE, this field may be click sorted. 19 */ 20class views_handler_field extends views_handler { 21 var $field_alias = 'unknown'; 22 var $aliases = array(); 23 24 /** 25 * Construct a new field handler. 26 */ 27 function construct() { 28 parent::construct(); 29 30 $this->additional_fields = array(); 31 if (!empty($this->definition['additional fields'])) { 32 $this->additional_fields = $this->definition['additional fields']; 33 } 34 35 if (!isset($this->options['exclude'])) { 36 $this->options['exclude'] = ''; 37 } 38 } 39 40 /** 41 * Determine if this field can allow advanced rendering. 42 * 43 * Fields can set this to FALSE if they do not wish to allow 44 * token based rewriting or link-making. 45 */ 46 function allow_advanced_render() { 47 return TRUE; 48 } 49 50 function init(&$view, $options) { 51 parent::init($view, $options); 52 53 $this->options += array( 54 'exclude' => FALSE, 55 ); 56 } 57 58 /** 59 * Called to add the field to a query. 60 */ 61 function query() { 62 $this->ensure_my_table(); 63 // Add the field. 64 $this->field_alias = $this->query->add_field($this->table_alias, $this->real_field); 65 66 $this->add_additional_fields(); 67 } 68 69 /** 70 * Add 'additional' fields to the query. 71 * 72 * @param $fields 73 * An array of fields. The key is an identifier used to later find the 74 * field alias used. The value is either a string in which case it's 75 * assumed to be a field on this handler's table; or it's an array in the 76 * form of 77 * @code array('table' => $tablename, 'field' => $fieldname) @endcode 78 */ 79 function add_additional_fields($fields = NULL) { 80 if (!isset($fields)) { 81 // notice check 82 if (empty($this->additional_fields)) { 83 return; 84 } 85 $fields = $this->additional_fields; 86 } 87 if (!empty($fields) && is_array($fields)) { 88 foreach ($fields as $identifier => $info) { 89 if (is_array($info)) { 90 if (isset($info['table'])) { 91 $table_alias = $this->query->ensure_table($info['table'], $this->relationship); 92 } 93 else { 94 $table_alias = $this->table_alias; 95 } 96 if (empty($table_alias)) { 97 vpr(t('Handler @handler tried to add additional_field @identifier but @table could not be added!', array('@handler' => $this->definition['handler'], '@identifier' => $identifier, '@table' => $info['table']))); 98 $this->aliases[$identifier] = 'broken'; 99 continue; 100 } 101 $this->aliases[$identifier] = $this->query->add_field($table_alias, $info['field']); 102 } 103 else { 104 $this->aliases[$info] = $this->query->add_field($this->table_alias, $info); 105 } 106 } 107 } 108 } 109 110 /** 111 * Called to determine what to tell the clicksorter. 112 */ 113 function click_sort($order) { 114 $this->query->add_orderby($this->table_alias, $this->real_field, $order, $this->field_alias); 115 } 116 117 /** 118 * Determine if this field is click sortable. 119 */ 120 function click_sortable() { 121 return !empty($this->definition['click sortable']); 122 } 123 124 /** 125 * Get this field's label. 126 */ 127 function label() { 128 if (!isset($this->options['label'])) { 129 return ''; 130 } 131 return $this->options['label']; 132 } 133 134 /** 135 * Return DIV or SPAN based upon the field's element type. 136 */ 137 function element_type() { 138 if (isset($this->definition['element type'])) { 139 return $this->definition['element type']; 140 } 141 142 return 'span'; 143 } 144 145 function option_definition() { 146 $options = parent::option_definition(); 147 148 $options['label'] = array('default' => $this->definition['title'], 'translatable' => TRUE); 149 $options['alter'] = array( 150 'contains' => array( 151 'alter_text' => array('default' => FALSE), 152 'text' => array('default' => '', 'translatable' => TRUE), 153 'make_link' => array('default' => FALSE), 154 'path' => array('default' => '', 'translatable' => TRUE), 155 'absolute' => array('default' => '', 'translatable' => FALSE), 156 'alt' => array('default' => '', 'translatable' => TRUE), 157 'rel' => array('default' => ''), 158 'link_class' => array('default' => ''), 159 'prefix' => array('default' => '', 'translatable' => TRUE), 160 'suffix' => array('default' => '', 'translatable' => TRUE), 161 'target' => array('default' => '', 'translatable' => TRUE), 162 'trim' => array('default' => FALSE), 163 'max_length' => array('default' => ''), 164 'word_boundary' => array('default' => TRUE), 165 'ellipsis' => array('default' => TRUE), 166 'strip_tags' => array('default' => FALSE), 167 'html' => array('default' => FALSE), 168 ), 169 ); 170 $options['empty'] = array('default' => '', 'translatable' => TRUE); 171 $options['hide_empty'] = array('default' => FALSE); 172 $options['empty_zero'] = array('default' => FALSE); 173 $options['hide_alter_empty'] = array('default' => TRUE); 174 175 return $options; 176 } 177 178 /** 179 * Default options form that provides the label widget that all fields 180 * should have. 181 */ 182 function options_form(&$form, &$form_state) { 183 $form['label'] = array( 184 '#type' => 'textfield', 185 '#title' => t('Label'), 186 '#default_value' => isset($this->options['label']) ? $this->options['label'] : '', 187 '#description' => t('The label for this field that will be displayed to end users if the style requires it.'), 188 ); 189 $form['exclude'] = array( 190 '#type' => 'checkbox', 191 '#title' => t('Exclude from display'), 192 '#default_value' => $this->options['exclude'], 193 '#description' => t('Check this box to not display this field, but still load it in the view. Use this option to not show a grouping field in each record, or when doing advanced theming.'), 194 ); 195 196 if ($this->allow_advanced_render()) { 197 $form['alter']['#tree'] = TRUE; 198 $form['alter']['alter_text'] = array( 199 '#type' => 'checkbox', 200 '#title' => t('Rewrite the output of this field'), 201 '#description' => t('If checked, you can alter the output of this field by specifying a string of text with replacement tokens that can use any existing field output.'), 202 '#default_value' => $this->options['alter']['alter_text'], 203 ); 204 205 $form['alter']['text'] = array( 206 '#title' => t('Text'), 207 '#type' => 'textarea', 208 '#default_value' => $this->options['alter']['text'], 209 '#description' => t('The text to display for this field. You may include HTML. You may enter data from this view as per the "Replacement patterns" below.'), 210 '#process' => array('views_process_dependency'), 211 '#dependency' => array( 212 'edit-options-alter-alter-text' => array(1) 213 ), 214 ); 215 216 $form['alter']['make_link'] = array( 217 '#type' => 'checkbox', 218 '#title' => t('Output this field as a link'), 219 '#description' => t('If checked, this field will be made into a link. The destination must be given below.'), 220 '#default_value' => $this->options['alter']['make_link'], 221 ); 222 $form['alter']['path'] = array( 223 '#title' => t('Link path'), 224 '#type' => 'textfield', 225 '#default_value' => $this->options['alter']['path'], 226 '#description' => t('The Drupal path or absolute URL for this link. You may enter data from this view as per the "Replacement patterns" below.'), 227 '#process' => array('views_process_dependency'), 228 '#dependency' => array( 229 'edit-options-alter-make-link' => array(1) 230 ), 231 '#maxlength' => 255, 232 ); 233 $form['alter']['absolute'] = array( 234 '#type' => 'checkbox', 235 '#title' => t('Use absolute path'), 236 '#default_value' => $this->options['alter']['absolute'], 237 '#process' => array('views_process_dependency'), 238 '#dependency' => array( 239 'edit-options-alter-make-link' => array(1) 240 ), 241 ); 242 243 $form['alter']['link_class'] = array( 244 '#title' => t('Link class'), 245 '#type' => 'textfield', 246 '#default_value' => $this->options['alter']['link_class'], 247 '#description' => t('The CSS class to apply to the link.'), 248 '#process' => array('views_process_dependency'), 249 '#dependency' => array( 250 'edit-options-alter-make-link' => array(1) 251 ), 252 ); 253 $form['alter']['alt'] = array( 254 '#title' => t('Alt text'), 255 '#type' => 'textfield', 256 '#default_value' => $this->options['alter']['alt'], 257 '#description' => t('Text to place as "alt" text which most browsers display as a tooltip when hovering over the link.'), 258 '#process' => array('views_process_dependency'), 259 '#dependency' => array( 260 'edit-options-alter-make-link' => array(1) 261 ), 262 ); 263 $form['alter']['rel'] = array( 264 '#title' => t('Rel Text'), 265 '#type' => 'textfield', 266 '#default_value' => $this->options['alter']['rel'], 267 '#description' => t('Include Rel attribute for use in lightbox2 or other javascript utility.'), 268 '#process' => array('views_process_dependency'), 269 '#dependency' => array( 270 'edit-options-alter-make-link' => array(1) 271 ), 272 ); 273 $form['alter']['prefix'] = array( 274 '#title' => t('Prefix text'), 275 '#type' => 'textfield', 276 '#default_value' => $this->options['alter']['prefix'], 277 '#description' => t('Any text to display before this link. You may include HTML.'), 278 '#process' => array('views_process_dependency'), 279 '#dependency' => array( 280 'edit-options-alter-make-link' => array(1) 281 ), 282 ); 283 $form['alter']['suffix'] = array( 284 '#title' => t('Suffix text'), 285 '#type' => 'textfield', 286 '#default_value' => $this->options['alter']['suffix'], 287 '#description' => t('Any text to display after this link. You may include HTML.'), 288 '#process' => array('views_process_dependency'), 289 '#dependency' => array( 290 'edit-options-alter-make-link' => array(1) 291 ), 292 ); 293 $form['alter']['target'] = array( 294 '#title' => t('Target'), 295 '#type' => 'textfield', 296 '#default_value' => $this->options['alter']['target'], 297 '#description' => t("Target of the link, such as _blank, _parent or an iframe's name. This field is rarely used."), 298 '#process' => array('views_process_dependency'), 299 '#dependency' => array( 300 'edit-options-alter-make-link' => array(1) 301 ), 302 ); 303 304 305 // Get a list of the available fields and arguments for token replacement. 306 $options = array(); 307 foreach ($this->view->display_handler->get_handlers('field') as $field => $handler) { 308 $options[t('Fields')]["[$field]"] = $handler->ui_name(); 309 // We only use fields up to (and including) this one. 310 if ($field == $this->options['id']) { 311 break; 312 } 313 } 314 $count = 0; // This lets us prepare the key as we want it printed. 315 foreach ($this->view->display_handler->get_handlers('argument') as $arg => $handler) { 316 $options[t('Arguments')]['%' . ++$count] = t('@argument title', array('@argument' => $handler->ui_name())); 317 $options[t('Arguments')]['!' . $count] = t('@argument input', array('@argument' => $handler->ui_name())); 318 } 319 320 $this->document_self_tokens($options[t('Fields')]); 321 322 // Default text. 323 $output = t('<p>You must add some additional fields to this display before using this field. These fields may be marked as <em>Exclude from display</em> if you prefer. Note that due to rendering order, you cannot use fields that come after this field; if you need a field not listed here, rearrange your fields.</p>'); 324 // We have some options, so make a list. 325 if (!empty($options)) { 326 $output = t('<p>The following tokens are available for this field. Note that due to rendering order, you cannot use fields that come after this field; if you need a field not listed here, rearrange your fields. 327If you would like to have the characters %5B and %5D please use the html entity codes \'%5B\' or \'%5D\' or they will get replaced with empty space.</p>'); 328 foreach (array_keys($options) as $type) { 329 if (!empty($options[$type])) { 330 $items = array(); 331 foreach ($options[$type] as $key => $value) { 332 $items[] = $key . ' == ' . $value; 333 } 334 $output .= theme('item_list', $items, $type); 335 } 336 } 337 } 338 // This construct uses 'hidden' and not markup because process doesn't 339 // run. It also has an extra div because the dependency wants to hide 340 // the parent in situations like this, so we need a second div to 341 // make this work. 342 $form['alter']['help'] = array( 343 '#type' => 'hidden', 344 '#id' => 'views-tokens-help', 345 '#prefix' => '<div><fieldset id="views-tokens-help"><legend>' . t('Replacement patterns') . '</legend>' . $output . '</fieldset></div>', 346 '#process' => array('views_process_dependency'), 347 '#dependency' => array( 348 'edit-options-alter-make-link' => array(1), 349 'edit-options-alter-alter-text' => array(1), 350 ), 351 ); 352 353 $form['alter']['trim'] = array( 354 '#type' => 'checkbox', 355 '#title' => t('Trim this field to a maximum length'), 356 '#description' => t('If checked, this field be trimmed to a maximum length in characters.'), 357 '#default_value' => $this->options['alter']['trim'], 358 ); 359 360 $form['alter']['max_length'] = array( 361 '#title' => t('Maximum length'), 362 '#type' => 'textfield', 363 '#default_value' => $this->options['alter']['max_length'], 364 '#description' => t('The maximum number of characters this field can be.'), 365 '#process' => array('views_process_dependency'), 366 '#dependency' => array( 367 'edit-options-alter-trim' => array(1) 368 ), 369 ); 370 371 $form['alter']['word_boundary'] = array( 372 '#type' => 'checkbox', 373 '#title' => t('Trim only on a word boundary'), 374 '#description' => t('If checked, this field be trimmed only on a word boundary. This is guaranteed to be the maximum characters stated or less. If there are no word boundaries this could trim a field to nothing.'), 375 '#default_value' => $this->options['alter']['word_boundary'], 376 '#process' => array('views_process_dependency'), 377 '#dependency' => array( 378 'edit-options-alter-trim' => array(1) 379 ), 380 ); 381 382 $form['alter']['ellipsis'] = array( 383 '#type' => 'checkbox', 384 '#title' => t('Add an ellipsis'), 385 '#description' => t('If checked, a "..." will be added if a field was trimmed.'), 386 '#default_value' => $this->options['alter']['ellipsis'], 387 '#process' => array('views_process_dependency'), 388 '#dependency' => array( 389 'edit-options-alter-trim' => array(1) 390 ), 391 ); 392 393 $form['alter']['html'] = array( 394 '#type' => 'checkbox', 395 '#title' => t('Field can contain HTML'), 396 '#description' => t('If checked, HTML corrector will be run to ensure tags are properly closed after trimming.'), 397 '#default_value' => $this->options['alter']['html'], 398 '#process' => array('views_process_dependency'), 399 '#dependency' => array( 400 'edit-options-alter-trim' => array(1) 401 ), 402 ); 403 404 $form['alter']['strip_tags'] = array( 405 '#type' => 'checkbox', 406 '#title' => t('Strip HTML tags'), 407 '#description' => t('If checked, all HTML tags will be stripped.'), 408 '#default_value' => $this->options['alter']['strip_tags'], 409 '#process' => array('views_process_dependency'), 410 ); 411 } 412 413 $form['empty'] = array( 414 '#type' => 'textfield', 415 '#title' => t('Empty text'), 416 '#default_value' => $this->options['empty'], 417 '#description' => t('If the field is empty, display this text instead.'), 418 ); 419 420 $form['empty_zero'] = array( 421 '#type' => 'checkbox', 422 '#title' => t('Count the number 0 as empty'), 423 '#default_value' => $this->options['empty_zero'], 424 '#description' => t('If the field contains the number zero, display the empty text instead'), 425 ); 426 427 $form['hide_empty'] = array( 428 '#type' => 'checkbox', 429 '#title' => t('Hide if empty'), 430 '#default_value' => $this->options['hide_empty'], 431 '#description' => t('Enable to hide this field if it is empty. Note that the field label or rewritten output may still be displayed. To hide labels, check the style or row style settings for empty fields. To hide rewritten content, check the Hide rewriting if empty checkbox.'), 432 ); 433 434 $form['hide_alter_empty'] = array( 435 '#type' => 'checkbox', 436 '#title' => t('Do not rewrite if empty'), 437 '#default_value' => $this->options['hide_alter_empty'], 438 ); 439 } 440 441 /** 442 * Provide extra data to the administration form 443 */ 444 function admin_summary() { 445 return $this->label(); 446 } 447 448 /** 449 * Run before any fields are rendered. 450 * 451 * This gives the handlers some time to set up before any handler has 452 * been rendered. 453 * 454 * @param $values 455 * An array of all objects returned from the query. 456 */ 457 function pre_render(&$values) { } 458 459 /** 460 * Render the field. 461 * 462 * @param $values 463 * The values retrieved from the database. 464 */ 465 function render($values) { 466 $value = $values->{$this->field_alias}; 467 return check_plain($value); 468 } 469 470 /** 471 * Render a field using advanced settings. 472 * 473 * This renders a field normally, then decides if render-as-link and 474 * text-replacement rendering is necessary. 475 */ 476 function advanced_render($values) { 477 if ($this->allow_advanced_render() && method_exists($this, 'render_item')) { 478 $raw_items = $this->get_items($values); 479 } 480 else { 481 $this->last_render = $value = $this->render($values); 482 $this->original_value = $value; 483 } 484 485 if ($this->allow_advanced_render()) { 486 $tokens = NULL; 487 if (method_exists($this, 'render_item')) { 488 $items = array(); 489 foreach ($raw_items as $count => $item) { 490 $this->last_render = $this->render_item($count, $item); 491 $this->original_value = $this->last_render; 492 493 $alter = $item + $this->options['alter']; 494 $items[] = $this->render_text($alter); 495 } 496 497 $value = $this->render_items($items); 498 } 499 else { 500 $value = $this->render_text($this->options['alter']); 501 } 502 503 // This happens here so that render_as_link can get the unaltered value of 504 // this field as a token rather than the altered value. 505 $this->last_render = $value; 506 } 507 508 if (empty($this->last_render)) { 509 if (($this->last_render !== 0 && $this->last_render !== '0') || !empty($this->options['empty_zero'])) { 510 $alter = $this->options['alter']; 511 $alter['alter_text'] = 1; 512 $alter['text'] = $this->options['empty']; 513 $this->last_render = $this->render_text($alter); 514 } 515 } 516 517 return $this->last_render; 518 } 519 520 /** 521 * Perform an advanced text render for the item. 522 * 523 * This is separated out as some fields may render lists, and this allows 524 * each item to be handled individually. 525 */ 526 function render_text($alter) { 527 $value = trim($this->last_render); 528 529 if (!empty($alter['alter_text']) && $alter['text'] !== '') { 530 $tokens = $this->get_render_tokens($alter); 531 $value = $this->render_altered($alter, $tokens); 532 } 533 534 if ((($this->options['hide_empty'] && empty($value)) || ($this->options['hide_alter_empty'] && empty($this->original_value))) && ($value !== 0 || $this->options['empty_zero'])) { 535 return ''; 536 } 537 538 if (!empty($alter['strip_tags'])) { 539 $value = strip_tags($value); 540 } 541 542 if (!empty($alter['trim']) && !empty($alter['max_length'])) { 543 $value = $this->render_trim_text($alter, $value); 544 } 545 546 if (!empty($alter['make_link']) && !empty($alter['path'])) { 547 if (!isset($tokens)) { 548 $tokens = $this->get_render_tokens($alter); 549 } 550 $value = $this->render_as_link($alter, $value, $tokens); 551 } 552 553 return $value; 554 } 555 556 /** 557 * Render this field as altered text, from a fieldset set by the user. 558 */ 559 function render_altered($alter, $tokens) { 560 // Filter this right away as our substitutions are already sanitized. 561 $value = filter_xss_admin($alter['text']); 562 $value = strtr($value, $tokens); 563 564 return $value; 565 } 566 567 /** 568 * Trim the field down to the specified length. 569 */ 570 function render_trim_text($alter, $value) { 571 if (!empty($alter['strip_tags'])) { 572 // NOTE: It's possible that some external fields might override the 573 // element type so if someone from, say, CCK runs into a bug here, 574 // this may be why =) 575 $this->definition['element type'] = 'span'; 576 } 577 return views_trim_text($alter, $value); 578 } 579 580 /** 581 * Render this field as a link, with the info from a fieldset set by 582 * the user. 583 */ 584 function render_as_link($alter, $text, $tokens) { 585 $value = ''; 586 587 if (!empty($alter['prefix'])) { 588 $value .= filter_xss_admin(strtr($alter['prefix'], $tokens)); 589 } 590 591 $options = array( 592 'html' => TRUE, 593 'absolute' => !empty($alter['absolute']) ? TRUE : FALSE, 594 ); 595 596 // $path will be run through check_url() by l() so we do not need to 597 // sanitize it ourselves. 598 $path = $alter['path']; 599 600 // html_entity_decode removes <front>, so check whether its different to front. 601 if ($path != '<front>') { 602 // Use strip tags as there should never be HTML in the path. 603 // However, we need to preserve special characters like " that 604 // were removed by check_plain(). 605 $path = strip_tags(html_entity_decode(strtr($path, $tokens))); 606 } 607 608 // If the path is empty do not build a link around the given text and return 609 // it as is. 610 if (empty($path)) { 611 return $text; 612 } 613 614 // Parse the URL and move any query and fragment parameters out of the path. 615 $url = parse_url($path); 616 if (isset($url['query'])) { 617 $path = strtr($path, array('?' . $url['query'] => '')); 618 $options['query'] = $url['query']; 619 } 620 if (isset($url['fragment'])) { 621 $path = strtr($path, array('#' . $url['fragment'] => '')); 622 // If the path is empty we want to have a fragment for the current site. 623 if ($path == '') { 624 $options['external'] = TRUE; 625 } 626 $options['fragment'] = $url['fragment']; 627 } 628 629 $alt = strtr($alter['alt'], $tokens); 630 // Set the title attribute of the link only if it improves accessibility 631 if ($alt && $alt != $text) { 632 $options['attributes']['title'] = html_entity_decode($alt, ENT_QUOTES); 633 } 634 635 $class = strtr($alter['link_class'], $tokens); 636 if ($class) { 637 $options['attributes']['class'] = $class; 638 } 639 640 if (!empty($alter['rel']) && $rel = strtr($alter['rel'], $tokens)) { 641 $options['attributes']['rel'] = $rel; 642 } 643 644 $target = check_plain(trim(strtr($alter['target'],$tokens))); 645 if (!empty($target)) { 646 $options['attributes']['target'] = $target; 647 } 648 649 // Allow the addition of arbitrary attributes to links. Additional attributes 650 // currently can only be altered in preprocessors and not within the UI. 651 if (isset($alter['link_attributes']) && is_array($alter['link_attributes'])) { 652 foreach ($alter['link_attributes'] as $key => $attribute) { 653 if (!isset($options['attributes'][$key])) { 654 $options['attributes'][$key] = strtr($attribute, $tokens); 655 } 656 } 657 } 658 659 // If the query and fragment were programatically assigned overwrite any 660 // parsed values. 661 if (isset($alter['query'])) { 662 $options['query'] = strtr($alter['query'], $tokens); 663 } 664 if (isset($alter['alias'])) { 665 // Alias is a boolean field, so no token. 666 $options['alias'] = $alter['alias']; 667 } 668 if (isset($alter['fragment'])) { 669 $options['fragment'] = strtr($alter['fragment'], $tokens); 670 } 671 if (isset($this->options['alter']['language'])) { 672 $options['language'] = $this->options['alter']['language']; 673 } 674 675 $value .= l($text, $path, $options); 676 677 if (!empty($alter['suffix'])) { 678 $value .= filter_xss_admin(strtr($alter['suffix'], $tokens)); 679 } 680 681 return $value; 682 } 683 684 /** 685 * Get the 'render' tokens to use for advanced rendering. 686 * 687 * This runs through all of the fields and arguments that 688 * are available and gets their values. This will then be 689 * used in one giant str_replace(). 690 */ 691 function get_render_tokens($item) { 692 $tokens = array(); 693 if (!empty($this->view->build_info['substitutions'])) { 694 $tokens = $this->view->build_info['substitutions']; 695 } 696 $count = 0; 697 foreach ($this->view->display_handler->get_handlers('argument') as $arg => $handler) { 698 $token = '%' . ++$count; 699 if (!isset($tokens[$token])) { 700 $tokens[$token] = ''; 701 } 702 703 // Use strip tags as there should never be HTML in the path. 704 // However, we need to preserve special characters like " that 705 // were removed by check_plain(). 706 $tokens['!' . $count] = isset($this->view->args[$count - 1]) ? strip_tags(html_entity_decode($this->view->args[$count - 1])) : ''; 707 } 708 709 // Now add replacements for our fields. 710 foreach ($this->view->display_handler->get_handlers('field') as $field => $handler) { 711 if (isset($handler->last_render)) { 712 $tokens["[$field]"] = $handler->last_render; 713 } 714 else { 715 $tokens["[$field]"] = ''; 716 } 717 $this->add_self_tokens($tokens, $item); 718 719 // We only use fields up to (and including) this one. 720 if ($field == $this->options['id']) { 721 break; 722 } 723 } 724 725 return $tokens; 726 } 727 728 /** 729 * Add any special tokens this field might use for itself. 730 * 731 * This method is intended to be overridden by items that generate 732 * fields as a list. For example, the field that displays all terms 733 * on a node might have tokens for the tid and the term. 734 * 735 * By convention, tokens should follow the format of [token-subtoken] 736 * where token is the field ID and subtoken is the field. If the 737 * field ID is terms, then the tokens might be [terms-tid] and [terms-name]. 738 */ 739 function add_self_tokens(&$tokens, $item) { } 740 741 /** 742 * Document any special tokens this field might use for itself. 743 * 744 * @see add_self_tokens() for details. 745 */ 746 function document_self_tokens(&$tokens) { } 747 748 /** 749 * Call out to the theme() function, which probably just calls render() but 750 * allows sites to override output fairly easily. 751 */ 752 function theme($values) { 753 return theme($this->theme_functions(), $this->view, $this, $values); 754 } 755 756 function theme_functions() { 757 $themes = array(); 758 $hook = 'views_view_field'; 759 760 $display = $this->view->display[$this->view->current_display]; 761 762 if (!empty($display)) { 763 $themes[] = $hook . '__' . $this->view->name . '__' . $display->id . '__' . $this->options['id']; 764 $themes[] = $hook . '__' . $this->view->name . '__' . $display->id; 765 $themes[] = $hook . '__' . $display->id . '__' . $this->options['id']; 766 $themes[] = $hook . '__' . $display->id; 767 if ($display->id != $display->display_plugin) { 768 $themes[] = $hook . '__' . $this->view->name . '__' . $display->display_plugin . '__' . $this->options['id']; 769 $themes[] = $hook . '__' . $this->view->name . '__' . $display->display_plugin; 770 $themes[] = $hook . '__' . $display->display_plugin . '__' . $this->options['id']; 771 $themes[] = $hook . '__' . $display->display_plugin; 772 } 773 } 774 $themes[] = $hook . '__' . $this->view->name . '__' . $this->options['id']; 775 $themes[] = $hook . '__' . $this->view->name; 776 $themes[] = $hook . '__' . $this->options['id']; 777 $themes[] = $hook; 778 779 return $themes; 780 } 781} 782 783/** 784 * A special handler to take the place of missing or broken handlers. 785 */ 786class views_handler_field_broken extends views_handler_field { 787 function ui_name($short = FALSE) { 788 return t('Broken/missing handler'); 789 } 790 791 function ensure_my_table() { /* No table to ensure! */ } 792 function query() { /* No query to run */ } 793 function options_form(&$form, &$form_state) { 794 $form['markup'] = array( 795 '#prefix' => '<div class="form-item description">', 796 '#value' => t('The handler for this item is broken or missing and cannot be used. If a module provided the handler and was disabled, re-enabling the module may restore it. Otherwise, you should probably delete this item.'), 797 ); 798 } 799 800 /** 801 * Determine if the handler is considered 'broken' 802 */ 803 function broken() { return TRUE; } 804} 805 806/** 807 * Render a numeric value as a size. 808 */ 809class views_handler_field_file_size extends views_handler_field { 810 function option_definition() { 811 $options = parent::option_definition(); 812 813 $options['file_size_display'] = array('default' => 'formatted'); 814 815 return $options; 816 } 817 818 function options_form(&$form, &$form_state) { 819 parent::options_form($form, $form_state); 820 $form['file_size_display'] = array( 821 '#title' => t('File size display'), 822 '#type' => 'select', 823 '#options' => array( 824 'formatted' => t('Formatted (in KB or MB)'), 825 'bytes' => t('Raw bytes'), 826 ), 827 ); 828 } 829 830 function render($values) { 831 if ($values->{$this->field_alias}) { 832 switch ($this->options['file_size_display']) { 833 case 'bytes': 834 return $values->{$this->field_alias}; 835 case 'formatted': 836 default: 837 return format_size($values->{$this->field_alias}); 838 } 839 } 840 else { 841 return ''; 842 } 843 } 844} 845 846/** 847 * A handler to run a field through simple XSS filtering 848 */ 849class views_handler_field_xss extends views_handler_field { 850 function render($values) { 851 $value = $values->{$this->field_alias}; 852 return filter_xss($value); 853 } 854} 855 856/** 857 * @} 858 */ 859 860