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