/[drupal]/contributions/modules/views/handlers/views_handler_argument.inc
ViewVC logotype

Contents of /contributions/modules/views/handlers/views_handler_argument.inc

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


Revision 1.9 - (show annotations) (download) (as text)
Fri Jun 26 00:23:42 2009 UTC (5 months ago) by merlinofchaos
Branch: MAIN
CVS Tags: DRUPAL-6--2-7, HEAD
Branch point for: DRUPAL-6--2, DRUPAL-7--3
Changes since 1.8: +2 -2 lines
File MIME type: text/x-php
#466454 by neclimdul: PHP strict fixes.
1 <?php
2 // $Id: views_handler_argument.inc,v 1.8 2009/06/02 19:38:33 merlinofchaos Exp $
3
4 /**
5 * @defgroup views_argument_handlers Handlers for arguments
6 * @{
7 */
8
9 /**
10 * Base class for arguments.
11 *
12 * The basic argument works for very simple arguments such as nid and uid
13 *
14 * Definition terms for this handler:
15 * - name field: The field to use for the name to use in the summary, which is
16 * the displayed output. For example, for the node: nid argument,
17 * the argument itself is the nid, but node.title is displayed.
18 * - name table: The table to use for the name, should it not be in the same
19 * table as the argument.
20 * - empty field name: For arguments that can have no value, such as taxonomy
21 * which can have "no term", this is the string which
22 * will be displayed for this lack of value. Be sure to use
23 * t().
24 * - validate type: A little used string to allow an argument to restrict
25 * which validator is available to just one. Use the
26 * validator ID. This probably should not be used at all,
27 * and may disappear or change.
28 * - numeric: If set to TRUE this field is numeric and will use %d instead of
29 * %s in queries.
30 *
31 * @ingroup views_argument_handlers
32 */
33 class views_handler_argument extends views_handler {
34 var $name_field = NULL;
35 /**
36 * Constructor
37 */
38 function construct() {
39 parent::construct();
40
41 if (!empty($this->definition['name field'])) {
42 $this->name_field = $this->definition['name field'];
43 }
44 if (!empty($this->definition['name table'])) {
45 $this->name_table = $this->definition['name table'];
46 }
47 }
48
49 function init(&$view, &$options) {
50 parent::init($view, $options);
51 }
52
53 /**
54 * Give an argument the opportunity to modify the breadcrumb, if it wants.
55 * This only gets called on displays where a breadcrumb is actually used.
56 *
57 * The breadcrumb will be in the form of an array, with the keys being
58 * the path and the value being the already sanitized title of the path.
59 */
60 function set_breadcrumb(&$breadcrumb) { }
61
62 /**
63 * Determine if the argument can generate a breadcrumb
64 *
65 * @return TRUE/FALSE
66 */
67 function uses_breadcrumb() {
68 $info = $this->default_actions($this->options['default_action']);
69 return !empty($info['breadcrumb']);
70 }
71
72 function is_wildcard($arg = NULL) {
73 if (!isset($arg)) {
74 $arg = $this->argument;
75 }
76
77 return !empty($this->options['wildcard']) && $this->options['wildcard'] === $arg;
78 }
79
80 function wildcard_title() {
81 return $this->options['wildcard_substitution'];
82 }
83
84 /**
85 * Determine if the argument needs a style plugin.
86 *
87 * @return TRUE/FALSE
88 */
89 function needs_style_plugin() {
90 $info = $this->default_actions($this->options['default_action']);
91 $validate_info = $this->default_actions($this->options['validate_fail']);
92 return !empty($info['style plugin']) || !empty($validate_info['style plugin']);
93 }
94
95 function option_definition() {
96 $options = parent::option_definition();
97
98 $options['default_action'] = array('default' => 'ignore');
99 $options['style_plugin'] = array('default' => 'default_summary');
100 $options['style_options'] = array('default' => array());
101 $options['wildcard'] = array('default' => 'all');
102 $options['wildcard_substitution'] = array('default' => t('All'), 'translatable' => TRUE);
103 $options['title'] = array('default' => '', 'translatable' => TRUE);
104 $options['breadcrumb'] = array('default' => '', 'translatable' => TRUE);
105 $options['default_argument_type'] = array('default' => 'fixed');
106 $options['default_argument'] = array('default' => '');
107 $options['validate_type'] = array('default' => 'none');
108 $options['validate_fail'] = array('default' => 'not found');
109
110 return $options;
111 }
112
113 function options_form(&$form, &$form_state) {
114 $defaults = $this->default_actions();
115
116 $form['title'] = array(
117 '#prefix' => '<div class="clear-block">',
118 '#suffix' => '</div>',
119 '#type' => 'textfield',
120 '#title' => t('Title'),
121 '#default_value' => $this->options['title'],
122 '#description' => t('The title to use when this argument is present. It will override the title of the view and titles from previous arguments. You can use percent substitution here to replace with argument titles. Use "%1" for the first argument, "%2" for the second, etc.'),
123 );
124
125 $form['breadcrumb'] = array(
126 '#prefix' => '<div class="clear-block">',
127 '#suffix' => '</div>',
128 '#type' => 'textfield',
129 '#title' => t('Breadcrumb'),
130 '#default_value' => $this->options['breadcrumb'],
131 '#description' => t('The Breadcrumb title to use when this argument is present. If no breadcrumb is setted here, default Title values will be used, see "Title" for percent substitutions.'),
132 );
133
134 $form['clear_start'] = array(
135 '#value' => '<div class="clear-block">',
136 );
137
138 $form['defaults_start'] = array(
139 '#value' => '<div class="views-left-50">',
140 );
141
142 $form['default_action'] = array(
143 '#type' => 'radios',
144 '#title' => t('Action to take if argument is not present'),
145 '#default_value' => $this->options['default_action'],
146 );
147
148 $form['defaults_stop'] = array(
149 '#value' => '</div>',
150 );
151
152 $form['wildcard'] = array(
153 '#prefix' => '<div class="views-right-50">',
154 // prefix and no suffix means these two items will be grouped together.
155 '#type' => 'textfield',
156 '#title' => t('Wildcard'),
157 '#size' => 20,
158 '#default_value' => $this->options['wildcard'],
159 '#description' => t('If this value is received as an argument, the argument will be ignored; i.e, "all values"'),
160 );
161
162 $form['wildcard_substitution'] = array(
163 '#suffix' => '</div>',
164 '#type' => 'textfield',
165 '#title' => t('Wildcard title'),
166 '#size' => 20,
167 '#default_value' => $this->options['wildcard_substitution'],
168 '#description' => t('The title to use for the wildcard in substitutions elsewhere.'),
169 );
170
171 $form['clear_stop'] = array(
172 '#value' => '</div>',
173 );
174
175 $options = array();
176 $validate_options = array();
177 foreach ($defaults as $id => $info) {
178 $options[$id] = $info['title'];
179 if (empty($info['default only'])) {
180 $validate_options[$id] = $info['title'];
181 }
182 if (!empty($info['form method'])) {
183 $this->{$info['form method']}($form, $form_state);
184 }
185 }
186
187 $form['default_action']['#options'] = $options;
188
189 $form['validate_options_div_prefix'] = array(
190 '#id' => 'views-validator-options',
191 '#value' => '<fieldset id="views-validator-options"><legend>' . t('Validator options') . '</legend>',
192 );
193
194 $form['validate_type'] = array(
195 '#type' => 'select',
196 '#title' => t('Validator'),
197 '#default_value' => $this->options['validate_type'],
198 );
199
200 $validate_types = array('none' => t('<Basic validation>'));
201 $plugins = views_fetch_plugin_data('argument validator');
202 foreach ($plugins as $id => $info) {
203 if (!empty($info['no ui'])) {
204 continue;
205 }
206
207 $valid = TRUE;
208 if (!empty($info['type'])) {
209 $valid = FALSE;
210 if (empty($this->definition['validate type'])) {
211 continue;
212 }
213 foreach ((array) $info['type'] as $type) {
214 if ($type == $this->definition['validate type']) {
215 $valid = TRUE;
216 break;
217 }
218 }
219 }
220
221 // If we decide this validator is ok, add it to the list.
222 if ($valid) {
223 $plugin = views_get_plugin('argument validator', $id);
224 if ($plugin) {
225 $plugin->init($this->view, $this, $id);
226 if ($plugin->access() || $this->options['validate_type'] == $id) {
227 $plugin->validate_form($form, $form_state, $id);
228 $validate_types[$id] = $info['title'];
229 }
230 }
231 }
232 }
233
234 asort($validate_types);
235 $form['validate_type']['#options'] = $validate_types;
236
237 $form['validate_fail'] = array(
238 '#type' => 'select',
239 '#title' => t('Action to take if argument does not validate'),
240 '#default_value' => $this->options['validate_fail'],
241 '#options' => $validate_options,
242 );
243
244 $form['validate_options_div_suffix'] = array(
245 '#value' => '</fieldset>',
246 );
247 }
248
249 /**
250 * Provide a list of default behaviors for this argument if the argument
251 * is not present.
252 *
253 * Override this method to provide additional (or fewer) default behaviors.
254 */
255 function default_actions($which = NULL) {
256 $defaults = array(
257 'ignore' => array(
258 'title' => t('Display all values'),
259 'method' => 'default_ignore',
260 'breadcrumb' => TRUE, // generate a breadcrumb to here
261 ),
262 'not found' => array(
263 'title' => t('Hide view / Page not found (404)'),
264 'method' => 'default_not_found',
265 'hard fail' => TRUE, // This is a hard fail condition
266 ),
267 'empty' => array(
268 'title' => t('Display empty text'),
269 'method' => 'default_empty',
270 'breadcrumb' => TRUE, // generate a breadcrumb to here
271 ),
272 'summary asc' => array(
273 'title' => t('Summary, sorted ascending'),
274 'method' => 'default_summary',
275 'method args' => array('asc'),
276 'style plugin' => TRUE,
277 'breadcrumb' => TRUE, // generate a breadcrumb to here
278 ),
279 'summary desc' => array(
280 'title' => t('Summary, sorted descending'),
281 'method' => 'default_summary',
282 'method args' => array('desc'),
283 'style plugin' => TRUE,
284 'breadcrumb' => TRUE, // generate a breadcrumb to here
285 ),
286 'default' => array(
287 'title' => t('Provide default argument'),
288 'method' => 'default_default',
289 'form method' => 'default_argument_form',
290 'has default argument' => TRUE,
291 'default only' => TRUE, // this can only be used for missing argument, not validation failure
292 ),
293 );
294
295 if ($which) {
296 if (!empty($defaults[$which])) {
297 return $defaults[$which];
298 }
299 }
300 else {
301 return $defaults;
302 }
303 }
304
305 /**
306 * Provide a form for selecting the default argument when the
307 * default action is set to provide default argument.
308 */
309 function default_argument_form(&$form, &$form_state) {
310 $plugins = views_fetch_plugin_data('argument default');
311 $options = array();
312
313 // This construct uses 'hidden' and not markup because process doesn't
314 // run. It also has an extra div because the dependency wants to hide
315 // the parent in situations like this, so we need a second div to
316 // make this work.
317 $form['default_options_div_prefix'] = array(
318 '#type' => 'hidden',
319 '#id' => 'views-default-options',
320 '#prefix' => '<div><fieldset id="views-default-options"><legend>' . t('Provide default argument options') . '</legend>',
321 '#process' => array('views_process_dependency'),
322 '#dependency' => array('radio:options[default_action]' => array('default')),
323 );
324
325 $form['default_argument_type'] = array(
326 '#prefix' => '<div id="edit-options-default-argument-type-wrapper">',
327 '#suffix' => '</div>',
328 '#type' => 'radios',
329 '#id' => 'edit-options-default-argument-type',
330 '#title' => t('Default argument type'),
331 '#default_value' => $this->options['default_argument_type'],
332 '#process' => array('expand_radios', 'views_process_dependency'),
333 '#dependency' => array('radio:options[default_action]' => array('default')),
334 );
335
336 foreach ($plugins as $id => $info) {
337 $plugin = views_get_plugin('argument default', $id);
338 if ($plugin) {
339 $plugin->init($this->view, $this, $id);
340
341 if ($plugin->access() || $this->options['default_argument_type'] == $id) {
342 $options[$id] = $info['title'];
343 $plugin->argument_form($form, $form_state);
344 }
345 }
346 }
347
348 $form['default_options_div_suffix'] = array(
349 '#value' => '</fieldset></div>',
350 );
351
352 asort($options);
353 $form['default_argument_type']['#options'] = $options;
354 }
355
356 /**
357 * Handle the default action, which means our argument wasn't present.
358 *
359 * Override this method only with extreme care.
360 *
361 * @return
362 * A boolean value; if TRUE, continue building this view. If FALSE,
363 * building the view will be aborted here.
364 */
365 function default_action($info = NULL) {
366 if (!isset($info)) {
367 $info = $this->default_actions($this->options['default_action']);
368 }
369
370 if (!$info) {
371 return FALSE;
372 }
373
374 if (!empty($info['method args'])) {
375 return call_user_func_array(array(&$this, $info['method']), $info['method args']);
376 }
377 else {
378 return $this->{$info['method']}();
379 }
380 }
381
382 /**
383 * How to act if validation failes
384 */
385 function validate_fail() {
386 $info = $this->default_actions($this->options['validate_fail']);
387 return $this->default_action($info);
388 }
389 /**
390 * Default action: ignore.
391 *
392 * If an argument was expected and was not given, in this case, simply
393 * ignore the argument entirely.
394 */
395 function default_ignore() {
396 return TRUE;
397 }
398
399 /**
400 * Default action: not found.
401 *
402 * If an argument was expected and was not given, in this case, report
403 * the view as 'not found' or hide it.
404 */
405 function default_not_found() {
406 // Set a failure condition and let the display manager handle it.
407 $this->view->build_info['fail'] = TRUE;
408 return FALSE;
409 }
410
411 /**
412 * Default action: empty
413 *
414 * If an argument was expected and was not given, in this case, display
415 * the view's empty text
416 */
417 function default_empty() {
418 // We return with no query; this will force the empty text.
419 $this->view->built = TRUE;
420 $this->view->executed = TRUE;
421 $this->view->result = array();
422 return FALSE;
423 }
424
425 /**
426 * This just returns true. The view argument builder will know where
427 * to find the argument from.
428 */
429 function default_default() {
430 return TRUE;
431 }
432
433 /**
434 * Determine if the argument is set to provide a default argument.
435 */
436 function has_default_argument() {
437 $info = $this->default_actions($this->options['default_action']);
438 return !empty($info['has default argument']);
439 }
440
441 /**
442 * Get a default argument, if available.
443 */
444 function get_default_argument() {
445 $plugin = views_get_plugin('argument default', $this->options['default_argument_type']);
446 if ($plugin) {
447 $plugin->init($this->view, $this);
448 return $plugin->get_argument();
449 }
450 }
451
452 /**
453 * Default action: summary.
454 *
455 * If an argument was expected and was not given, in this case, display
456 * a summary query.
457 */
458 function default_summary($order) {
459 $this->view->build_info['summary'] = TRUE;
460 $this->view->build_info['summary_level'] = $this->options['id'];
461
462 // Change the display style to the summary style for this
463 // argument.
464 $this->view->plugin_name = $this->options['style_plugin'];
465 $this->view->style_options = $this->options['style_options'];
466
467 // Clear out the normal primary field and whatever else may have
468 // been added and let the summary do the work.
469 $this->query->clear_fields();
470 $this->summary_query();
471
472 $this->summary_sort($order);
473
474 // Summaries have their own sorting and fields, so tell the View not
475 // to build these.
476 $this->view->build_sort = $this->view->build_fields = FALSE;
477 return TRUE;
478 }
479
480 /**
481 * Build the info for the summary query.
482 *
483 * This must:
484 * - add_groupby: group on this field in order to create summaries.
485 * - add_field: add a 'num_nodes' field for the count. Usually it will
486 * be a count on $view->base_field
487 * - set_count_field: Reset the count field so we get the right paging.
488 *
489 * @return
490 * The alias used to get the number of records (count) for this entry.
491 */
492 function summary_query() {
493 $this->ensure_my_table();
494 // Add the field.
495 $this->base_alias = $this->query->add_field($this->table_alias, $this->real_field);
496
497 $this->summary_name_field();
498 return $this->summary_basics();
499 }
500
501 /**
502 * Add the name field, which is the field displayed in summary queries.
503 * This is often used when the argument is numeric.
504 */
505 function summary_name_field() {
506 // Add the 'name' field. For example, if this is a uid argument, the
507 // name field would be 'name' (i.e, the username).
508
509 if (isset($this->name_table)) {
510 // if the alias is different then we're probably added, not ensured,
511 // so look up the join and add it instead.
512 if ($this->table_alias != $this->table) {
513 $j = views_get_table_join($this->name_table, $this->table);
514 if ($j) {
515 $join = drupal_clone($j);
516 $join->left_table = $this->table_alias;
517 $this->name_table_alias = $this->query->add_table($this->name_table, $this->relationship, $join);
518 }
519 }
520 else {
521 $this->name_table_alias = $this->query->ensure_table($this->name_table, $this->relationship);
522 }
523 }
524 else {
525 $this->name_table_alias = $this->table_alias;
526 }
527
528 if (isset($this->name_field)) {
529 $this->name_alias = $this->query->add_field($this->name_table_alias, $this->name_field);
530 }
531 else {
532 $this->name_alias = $this->base_alias;
533 }
534 }
535
536 /**
537 * Some basic summary behavior that doesn't need to be repeated as much as
538 * code that goes into summary_query()
539 */
540 function summary_basics($count_field = TRUE) {
541 // Add the number of nodes counter
542 $field = $this->query->base_table . '.' . $this->query->base_field;
543 $distinct = ($this->view->display_handler->get_option('distinct') && empty($this->query->no_distinct));
544
545 $count_alias = $this->query->add_field(NULL, $field, 'num_records',
546 array('count' => TRUE, 'distinct' => $distinct));
547 $this->query->add_groupby($this->name_alias);
548
549 if ($count_field) {
550 $this->query->set_count_field($this->table_alias, $this->real_field);
551 }
552
553 $this->count_alias = $count_alias;
554 }
555
556 /**
557 * Sorts the summary based upon the user's selection. The base variant of
558 * this is usually adequte.
559 *
560 * @param $order
561 * The order selected in the UI.
562 */
563 function summary_sort($order) {
564 $this->query->add_orderby(NULL, NULL, $order, $this->name_alias);
565 }
566
567 /**
568 * Provide the argument to use to link from the summary to the next level;
569 * this will be called once per row of a summary, and used as part of
570 * $view->get_url().
571 *
572 * @param $data
573 * The query results for the row.
574 */
575 function summary_argument($data) {
576 return $data->{$this->base_alias};
577 }
578
579 /**
580 * Provides the name to use for the summary. By default this is just
581 * the name field.
582 *
583 * @param $data
584 * The query results for the row.
585 */
586 function summary_name($data) {
587 $value = $data->{$this->name_alias};
588 if (empty($value) && !empty($this->definition['empty field name'])) {
589 $value = $this->definition['empty field name'];
590 }
591 return check_plain($value);
592 }
593
594 /**
595 * Set up the query for this argument.
596 *
597 * The argument sent may be found at $this->argument.
598 */
599 function query() {
600 $this->ensure_my_table();
601 $placeholder = empty($this->definition['numeric']) ? "'%s'" : '%d';
602 $this->query->add_where(0, "$this->table_alias.$this->real_field = $placeholder", $this->argument);
603 }
604
605 /**
606 * Get the title this argument will assign the view, given the argument.
607 *
608 * This usually needs to be overridden to provide a proper title.
609 */
610 function title() {
611 return check_plain($this->argument);
612 }
613
614 /**
615 * Called by the view object to get the title. This may be set by a
616 * validator so we don't necessarily call through to title().
617 */
618 function get_title() {
619 if (isset($this->validated_title)) {
620 return $this->validated_title;
621 }
622 else {
623 return $this->title();
624 }
625 }
626
627 /**
628 * Validate that this argument works. By default, all arguments are valid.
629 */
630 function validate_arg($arg) {
631 // By using % in URLs, arguments could be validated twice; this eases
632 // that pain.
633 if (isset($this->argument_validated)) {
634 return $this->argument_validated;
635 }
636
637 if ($this->is_wildcard($arg)) {
638 return $this->argument_validated = TRUE;
639 }
640
641 if ($this->options['validate_type'] == 'none') {
642 return $this->argument_validated = $this->validate_argument_basic($arg);
643 }
644
645 $plugin = views_get_plugin('argument validator', $this->options['validate_type']);
646 if ($plugin) {
647 $plugin->init($this->view, $this, $this->options['validate_type']);
648 return $this->argument_validated = $plugin->validate_argument($arg);
649 }
650
651 // If the plugin isn't found, fall back to the basic validation path:
652 return $this->argument_validated = $this->validate_argument_basic($arg);
653 }
654
655 /**
656 * Called by the menu system to validate an argument.
657 *
658 * This checks to see if this is a 'soft fail', which means that if the
659 * argument fails to validate, but there is an action to take anyway,
660 * then validation cannot actually fail.
661 */
662 function validate_argument($arg) {
663 $validate_info = $this->default_actions($this->options['validate_fail']);
664 if (empty($validate_info['hard fail'])) {
665 return TRUE;
666 }
667
668 $rc = $this->validate_arg($arg);
669
670 // If the validator has changed the validate fail condition to a
671 // soft fail, deal with that:
672 $validate_info = $this->default_actions($this->options['validate_fail']);
673 if (empty($validate_info['hard fail'])) {
674 return TRUE;
675 }
676
677 return $rc;
678 }
679
680 /**
681 * Provide a basic argument validation.
682 *
683 * This can be overridden for more complex types; the basic
684 * validator only checks to see if the argument is not NULL
685 * or is numeric if the definition says it's numeric.
686 */
687 function validate_argument_basic($arg) {
688 if (!isset($arg) || $arg === '') {
689 return FALSE;
690 }
691
692 if (isset($this->definition['numeric']) && !isset($this->options['break_phrase']) && !is_numeric($arg)) {
693 return FALSE;
694 }
695
696 return TRUE;
697 }
698
699 /**
700 * Set the input for this argument
701 *
702 * @return TRUE if it successfully validates; FALSE if it does not.
703 */
704 function set_argument($arg) {
705 $this->argument = $arg;
706 return $this->validate_arg($arg);
707 }
708
709 /**
710 * Get the value of this argument.
711 */
712 function get_value() {
713 // If we already processed this argument, we're done.
714 if (isset($this->argument)) {
715 return $this->argument;
716 }
717
718 // Otherwise, we have to pretend to process ourself to find the value.
719 $value = NULL;
720 // Find the position of this argument within the view.
721 $position = 0;
722 foreach ($this->view->argument as $id => $argument) {
723 if ($id == $this->options['id']) {
724 break;
725 }
726 $position++;
727 }
728
729 $arg = isset($this->view->args[$position]) ? $this->view->args[$position] : NULL;
730 $this->position = $position;
731
732 // Clone ourselves so that we don't break things when we're really
733 // processing the arguments.
734 $argument = drupal_clone($this);
735 if (!isset($arg) && $argument->has_default_argument()) {
736 $arg = $argument->get_default_argument();
737 }
738 // Set the argument, which will also validate that the argument can be set.
739 if ($argument->set_argument($arg)) {
740 $value = $argument->argument;
741 }
742 unset($argument);
743 return $value;
744 }
745 }
746
747 /**
748 * A special handler to take the place of missing or broken handlers.
749 *
750 * @ingroup views_argument_handlers
751 */
752 class views_handler_argument_broken extends views_handler_argument {
753 function ui_name($short = FALSE) {
754 return t('Broken/missing handler');
755 }
756
757 function ensure_my_table() { /* No table to ensure! */ }
758 function query() { /* No query to run */ }
759 function options_form(&$form, &$form_state) {
760 $form['markup'] = array(
761 '#prefix' => '<div class="form-item description">',
762 '#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.'),
763 );
764 }
765
766 /**
767 * Determine if the handler is considered 'broken'
768 */
769 function broken() { return TRUE; }
770 }
771
772 /**
773 * @}
774 */

  ViewVC Help
Powered by ViewVC 1.1.2