Projet

Général

Profil

Paste
Télécharger (43,3 ko) Statistiques
| Branche: | Révision:

root / drupal7 / sites / all / modules / views / handlers / views_handler_argument.inc @ 5d12d676

1
<?php
2

    
3
/**
4
 * @file
5
 * Definition of views_handler_argument.
6
 */
7

    
8
/**
9
 * @defgroup views_argument_handlers Views argument handlers
10
 * Handlers to tell Views how to contextually filter queries.
11
 * @{
12
 */
13

    
14
/**
15
 * Base class for arguments.
16
 *
17
 * The basic argument works for very simple arguments such as nid and uid
18
 *
19
 * Definition terms for this handler:
20
 * - name field: The field to use for the name to use in the summary, which is
21
 *   the displayed output. For example, for the node: nid argument, the argument
22
 *   itself is the nid, but node.title is displayed.
23
 * - name table: The table to use for the name, should it not be in the same
24
 *   table as the argument.
25
 * - empty field name: For arguments that can have no value, such as taxonomy
26
 *   which can have "no term", this is the string which will be displayed for
27
 *   this lack of value. Be sure to use t().
28
 * - validate type: A little used string to allow an argument to restrict
29
 *   which validator is available to just one. Use the validator ID. This
30
 *   probably should not be used at all, and may disappear or change.
31
 * - numeric: If set to TRUE this field is numeric and will use %d instead of
32
 *   %s in queries.
33
 *
34
 * @ingroup views_argument_handlers
35
 */
36
class views_handler_argument extends views_handler {
37

    
38
  /**
39
   * @var object
40
   */
41
  public $validator = NULL;
42

    
43
  /**
44
   * @var mixed
45
   */
46
  public $argument = NULL;
47

    
48
  /**
49
   * @var mixed
50
   */
51
  public $value = NULL;
52

    
53
  /**
54
   * The table to use for the name, if not the same table as the argument.
55
   *
56
   * @var string
57
   */
58
  public $name_table;
59

    
60
  /**
61
   * The field to use for the name to use in the summary.
62
   *
63
   * Used as the displayed output. For example, for the node: nid argument, the
64
   * argument itself is the nid, but node.title is displayed.
65
   *
66
   * @var string
67
   */
68
  public $name_field;
69

    
70
  /**
71
   * {@inheritdoc}
72
   */
73
  public function construct() {
74
    parent::construct();
75

    
76
    if (!empty($this->definition['name field'])) {
77
      $this->name_field = $this->definition['name field'];
78
    }
79
    if (!empty($this->definition['name table'])) {
80
      $this->name_table = $this->definition['name table'];
81
    }
82
  }
83

    
84
  /**
85
   * {@inheritdoc}
86
   */
87
  public function init(&$view, &$options) {
88
    parent::init($view, $options);
89

    
90
    // Compatibility: The new UI changed several settings.
91
    if (!empty($options['wildcard']) && !isset($options['exception']['value'])) {
92
      $this->options['exception']['value'] = $options['wildcard'];
93
    }
94
    if (!empty($options['wildcard_substitution']) && !isset($options['exception']['title'])) {
95
      // Enable the checkbox if the title is filled in.
96
      $this->options['exception']['title_enable'] = 1;
97
      $this->options['exception']['title'] = $options['wildcard_substitution'];
98
    }
99

    
100
    if (!isset($options['summary']['format']) && !empty($options['style_plugin'])) {
101
      $this->options['summary']['format'] = $options['style_plugin'];
102
    }
103

    
104
    // Setup default value.
105
    $options['style_options'] = isset($options['style_options']) ? $options['style_options'] : array();
106

    
107
    if (!isset($options['summary']['sort_order']) && !empty($options['default_action']) && $options['default_action'] == 'summary asc') {
108
      $this->options['default_action'] = 'summary';
109
      $this->options['summary']['sort_order'] = 'asc';
110
      $this->options['summary']['number_of_records'] = 0;
111
      $this->options['summary_options'] = $options['style_options'];
112
    }
113
    elseif (!isset($options['summary']['sort_order']) && !empty($options['default_action']) && $options['default_action'] == 'summary desc') {
114
      $this->options['default_action'] = 'summary';
115
      $this->options['summary']['sort_order'] = 'desc';
116
      $this->options['summary']['number_of_records'] = 0;
117
      $this->options['summary_options'] = $options['style_options'];
118
    }
119
    elseif (!isset($options['summary']['sort_order']) && !empty($options['default_action']) && $options['default_action'] == 'summary asc by count') {
120
      $this->options['default_action'] = 'summary';
121
      $this->options['summary']['sort_order'] = 'asc';
122
      $this->options['summary']['number_of_records'] = 1;
123
      $this->options['summary_options'] = $options['style_options'];
124
    }
125
    elseif (!isset($options['summary']['sort_order']) && !empty($options['default_action']) && $options['default_action'] == 'summary desc by count') {
126
      $this->options['default_action'] = 'summary';
127
      $this->options['summary']['sort_order'] = 'desc';
128
      $this->options['summary']['number_of_records'] = 1;
129
      $this->options['summary_options'] = $options['style_options'];
130
    }
131

    
132
    if (!empty($options['title']) && !isset($options['title_enable'])) {
133
      $this->options['title_enable'] = 1;
134
    }
135
    if (!empty($options['breadcrumb']) && !isset($options['breadcrumb_enable'])) {
136
      $this->options['breadcrumb_enable'] = 1;
137
    }
138

    
139
    if (!empty($options['validate_type']) && !isset($options['validate']['type'])) {
140
      $this->options['validate']['type'] = $options['validate_type'];
141
      $this->options['specify_validation'] = 1;
142
    }
143
    if (!empty($options['validate_fail']) && !isset($options['validate']['fail'])) {
144
      $this->options['validate']['fail'] = $options['validate_fail'];
145
      $this->options['specify_validation'] = 1;
146
    }
147
  }
148

    
149
  /**
150
   * Give an argument the opportunity to modify the breadcrumb, if it wants.
151
   *
152
   * Only gets called on displays where a breadcrumb is actually used.
153
   *
154
   * The breadcrumb will be in the form of an array, with the keys being
155
   * the path and the value being the already sanitized title of the path.
156
   */
157
  public function set_breadcrumb(&$breadcrumb) {
158
  }
159

    
160
  /**
161
   * Determine if the argument can generate a breadcrumb
162
   *
163
   * @return bool
164
   */
165
  public function uses_breadcrumb() {
166
    $info = $this->default_actions($this->options['default_action']);
167
    return !empty($info['breadcrumb']);
168
  }
169

    
170
  /**
171
   * {@inheritdoc}
172
   */
173
  public function is_exception($arg = NULL) {
174
    if (!isset($arg)) {
175
      $arg = isset($this->argument) ? $this->argument : NULL;
176
    }
177
    return !empty($this->options['exception']['value']) && ($this->options['exception']['value'] === $arg);
178
  }
179

    
180
  /**
181
   * Work out which title to use.
182
   *
183
   * @return string
184
   *   The title string to use.
185
   */
186
  public function exception_title() {
187
    // If title overriding is off for the exception, return the normal title.
188
    if (empty($this->options['exception']['title_enable'])) {
189
      return $this->get_title();
190
    }
191
    return $this->options['exception']['title'];
192
  }
193

    
194
  /**
195
   * Determine if the argument needs a style plugin.
196
   *
197
   * @return bool
198
   */
199
  public function needs_style_plugin() {
200
    $info = $this->default_actions($this->options['default_action']);
201
    $validate_info = $this->default_actions($this->options['validate']['fail']);
202
    return !empty($info['style plugin']) || !empty($validate_info['style plugin']);
203
  }
204

    
205
  /**
206
   * {@inheritdoc}
207
   */
208
  public function option_definition() {
209
    $options = parent::option_definition();
210

    
211
    $options['default_action'] = array('default' => 'ignore');
212
    $options['exception'] = array(
213
      'contains' => array(
214
        'value' => array('default' => 'all'),
215
        'title_enable' => array('default' => FALSE, 'bool' => TRUE),
216
        'title' => array('default' => 'All', 'translatable' => TRUE),
217
      ),
218
    );
219
    $options['title_enable'] = array('default' => FALSE, 'bool' => TRUE);
220
    $options['title'] = array('default' => '', 'translatable' => TRUE);
221
    $options['breadcrumb_enable'] = array('default' => FALSE, 'bool' => TRUE);
222
    $options['breadcrumb'] = array('default' => '', 'translatable' => TRUE);
223
    $options['default_argument_type'] = array('default' => 'fixed', 'export' => 'export_plugin');
224
    $options['default_argument_options'] = array('default' => array(), 'export' => FALSE);
225
    $options['default_argument_skip_url'] = array('default' => FALSE, 'bool' => TRUE);
226
    $options['summary_options'] = array('default' => array(), 'export' => FALSE);
227
    $options['summary'] = array(
228
      'contains' => array(
229
        'sort_order' => array('default' => 'asc'),
230
        'number_of_records' => array('default' => 0),
231
        'format' => array('default' => 'default_summary', 'export' => 'export_summary'),
232
      ),
233
    );
234
    $options['specify_validation'] = array('default' => FALSE, 'bool' => TRUE);
235
    $options['validate'] = array(
236
      'contains' => array(
237
        'type' => array('default' => 'none', 'export' => 'export_validation'),
238
        'fail' => array('default' => 'not found'),
239
      ),
240
    );
241
    $options['validate_options'] = array('default' => array(), 'export' => FALSE);
242

    
243
    return $options;
244
  }
245

    
246
  /**
247
   * {@inheritdoc}
248
   */
249
  public function options_form(&$form, &$form_state) {
250
    parent::options_form($form, $form_state);
251

    
252
    $argument_text = $this->view->display_handler->get_argument_text();
253

    
254
    $form['#pre_render'][] = 'views_ui_pre_render_move_argument_options';
255

    
256
    $form['description'] = array(
257
      '#markup' => $argument_text['description'],
258
      '#theme_wrappers' => array('container'),
259
      '#attributes' => array('class' => array('description')),
260
    );
261

    
262
    $form['no_argument'] = array(
263
      '#type' => 'fieldset',
264
      '#title' => $argument_text['filter value not present'],
265
    );
266
    // Everything in the fieldset is floated, so the last element needs to
267
    // clear those floats.
268
    $form['no_argument']['clearfix'] = array(
269
      '#weight' => 1000,
270
      '#markup' => '<div class="clearfix"></div>',
271
    );
272
    $form['default_action'] = array(
273
      '#type' => 'radios',
274
      '#process' => array('views_ui_process_container_radios'),
275
      '#default_value' => $this->options['default_action'],
276
      '#fieldset' => 'no_argument',
277
    );
278

    
279
    $form['exception'] = array(
280
      '#type' => 'fieldset',
281
      '#title' => t('Exceptions'),
282
      '#collapsible' => TRUE,
283
      '#collapsed' => TRUE,
284
      '#fieldset' => 'no_argument',
285
    );
286
    $form['exception']['value'] = array(
287
      '#type' => 'textfield',
288
      '#title' => t('Exception value'),
289
      '#size' => 20,
290
      '#default_value' => $this->options['exception']['value'],
291
      '#description' => t('If this value is received, the filter will be ignored; i.e, "all values"'),
292
    );
293
    $form['exception']['title_enable'] = array(
294
      '#type' => 'checkbox',
295
      '#title' => t('Override title'),
296
      '#default_value' => $this->options['exception']['title_enable'],
297
    );
298
    $form['exception']['title'] = array(
299
      '#type' => 'textfield',
300
      '#title' => t('Override title'),
301
      '#title_display' => 'invisible',
302
      '#size' => 20,
303
      '#default_value' => $this->options['exception']['title'],
304
      '#description' => t('Override the view and other argument titles. Use "%1" for the first argument, "%2" for the second, etc.'),
305
      '#dependency' => array(
306
        'edit-options-exception-title-enable' => array('1'),
307
      ),
308
    );
309

    
310
    $options = array();
311
    $defaults = $this->default_actions();
312
    $validate_options = array();
313
    foreach ($defaults as $id => $info) {
314
      $options[$id] = $info['title'];
315
      if (empty($info['default only'])) {
316
        $validate_options[$id] = $info['title'];
317
      }
318
      if (!empty($info['form method'])) {
319
        $this->{$info['form method']}($form, $form_state);
320
      }
321
    }
322
    $form['default_action']['#options'] = $options;
323

    
324
    $form['argument_present'] = array(
325
      '#type' => 'fieldset',
326
      '#title' => $argument_text['filter value present'],
327
    );
328
    $form['title_enable'] = array(
329
      '#type' => 'checkbox',
330
      '#title' => t('Override title'),
331
      '#default_value' => $this->options['title_enable'],
332
      '#fieldset' => 'argument_present',
333
    );
334
    $form['title'] = array(
335
      '#type' => 'textfield',
336
      '#title' => t('Provide title'),
337
      '#title_display' => 'invisible',
338
      '#default_value' => $this->options['title'],
339
      '#description' => t('Override the view and other argument titles. Use "%1" for the first argument, "%2" for the second, etc.'),
340
      '#dependency' => array(
341
        'edit-options-title-enable' => array('1'),
342
      ),
343
      '#fieldset' => 'argument_present',
344
    );
345

    
346
    $form['breadcrumb_enable'] = array(
347
      '#type' => 'checkbox',
348
      '#title' => t('Override breadcrumb'),
349
      '#default_value' => $this->options['breadcrumb_enable'],
350
      '#fieldset' => 'argument_present',
351
    );
352
    $form['breadcrumb'] = array(
353
      '#type' => 'textfield',
354
      '#title' => t('Provide breadcrumb'),
355
      '#title_display' => 'invisible',
356
      '#default_value' => $this->options['breadcrumb'],
357
      '#description' => t('Enter a breadcrumb name you would like to use. See "Title" for percent substitutions.'),
358
      '#dependency' => array(
359
        'edit-options-breadcrumb-enable' => array('1'),
360
      ),
361
      '#fieldset' => 'argument_present',
362
    );
363

    
364
    $form['specify_validation'] = array(
365
      '#type' => 'checkbox',
366
      '#title' => t('Specify validation criteria'),
367
      '#default_value' => $this->options['specify_validation'],
368
      '#fieldset' => 'argument_present',
369
    );
370

    
371
    $form['validate'] = array(
372
      '#type' => 'container',
373
      '#fieldset' => 'argument_present',
374
    );
375
    // @todo The mockup wanted to use "Validate using" here, but it doesn't
376
    // work well with many options (they'd need to be changed as well)
377
    $form['validate']['type'] = array(
378
      '#type' => 'select',
379
      '#title' => t('Validator'),
380
      '#default_value' => $this->options['validate']['type'],
381
      '#dependency' => array(
382
        'edit-options-specify-validation' => array('1'),
383
      ),
384
    );
385

    
386
    $validate_types = array('none' => t('- Basic validation -'));
387
    $plugins = views_fetch_plugin_data('argument validator');
388
    foreach ($plugins as $id => $info) {
389
      if (!empty($info['no ui'])) {
390
        continue;
391
      }
392

    
393
      $valid = TRUE;
394
      if (!empty($info['type'])) {
395
        $valid = FALSE;
396
        if (empty($this->definition['validate type'])) {
397
          continue;
398
        }
399
        foreach ((array) $info['type'] as $type) {
400
          if ($type == $this->definition['validate type']) {
401
            $valid = TRUE;
402
            break;
403
          }
404
        }
405
      }
406

    
407
      // If we decide this validator is ok, add it to the list.
408
      if ($valid) {
409
        $plugin = $this->get_plugin('argument validator', $id);
410
        if ($plugin) {
411
          if ($plugin->access() || $this->options['validate']['type'] == $id) {
412
            $form['validate']['options'][$id] = array(
413
              '#prefix' => '<div id="edit-options-validate-options-' . $id . '-wrapper">',
414
              '#suffix' => '</div>',
415
              '#type' => 'item',
416
              // Even if the plugin has no options, add the key to the
417
              // form_state. Trick it into checking input to make #process run.
418
              '#input' => TRUE,
419
              '#dependency' => array(
420
                'edit-options-specify-validation' => array('1'),
421
                'edit-options-validate-type' => array($id),
422
              ),
423
              '#dependency_count' => 2,
424
              '#id' => 'edit-options-validate-options-' . $id,
425
            );
426
            $plugin->options_form($form['validate']['options'][$id], $form_state);
427
            $validate_types[$id] = $info['title'];
428
          }
429
        }
430
      }
431
    }
432

    
433
    asort($validate_types);
434
    $form['validate']['type']['#options'] = $validate_types;
435

    
436
    $form['validate']['fail'] = array(
437
      '#type' => 'select',
438
      '#title' => t('Action to take if filter value does not validate'),
439
      '#default_value' => $this->options['validate']['fail'],
440
      '#options' => $validate_options,
441
      '#dependency' => array(
442
        'edit-options-specify-validation' => array('1'),
443
      ),
444
      '#fieldset' => 'argument_present',
445
    );
446
  }
447

    
448
  /**
449
   * {@inheritdoc}
450
   */
451
  public function options_validate(&$form, &$form_state) {
452
    if (empty($form_state['values']['options'])) {
453
      return;
454
    }
455

    
456
    // Let the plugins do validation.
457
    $default_id = $form_state['values']['options']['default_argument_type'];
458
    $plugin = $this->get_plugin('argument default', $default_id);
459
    if ($plugin && isset($form['argument_default'][$default_id]) && isset($form_state['values']['options']['argument_default'][$default_id])) {
460
      $plugin->options_validate($form['argument_default'][$default_id], $form_state, $form_state['values']['options']['argument_default'][$default_id]);
461
    }
462

    
463
    // Validate summary plugin options if one is present.
464
    if (isset($form_state['values']['options']['summary']['format'])) {
465
      $summary_id = $form_state['values']['options']['summary']['format'];
466
      $plugin = $this->get_plugin('style', $summary_id);
467
      if ($plugin) {
468
        $plugin->options_validate($form['summary']['options'][$summary_id], $form_state, $form_state['values']['options']['summary']['options'][$summary_id]);
469
      }
470
    }
471

    
472
    $validate_id = $form_state['values']['options']['validate']['type'];
473
    $plugin = $this->get_plugin('argument validator', $validate_id);
474
    if ($plugin) {
475
      $plugin->options_validate($form['validate']['options'][$default_id], $form_state, $form_state['values']['options']['validate']['options'][$validate_id]);
476
    }
477

    
478
  }
479

    
480
  /**
481
   * {@inheritdoc}
482
   */
483
  public function options_submit(&$form, &$form_state) {
484
    if (empty($form_state['values']['options'])) {
485
      return;
486
    }
487

    
488
    // Let the plugins make submit modifications if necessary.
489
    $default_id = $form_state['values']['options']['default_argument_type'];
490
    $plugin = $this->get_plugin('argument default', $default_id);
491
    if ($plugin) {
492
      $options = &$form_state['values']['options']['argument_default'][$default_id];
493
      $plugin->options_submit($form['argument_default'][$default_id], $form_state, $options);
494
      // Copy the now submitted options to their final resting place so they
495
      // get saved.
496
      $form_state['values']['options']['default_argument_options'] = $options;
497
    }
498

    
499
    // Handle summary plugin options if one is present.
500
    if (isset($form_state['values']['options']['summary']['format'])) {
501
      $summary_id = $form_state['values']['options']['summary']['format'];
502
      $plugin = $this->get_plugin('style', $summary_id);
503
      if ($plugin) {
504
        $options = &$form_state['values']['options']['summary']['options'][$summary_id];
505
        $plugin->options_submit($form['summary']['options'][$summary_id], $form_state, $options);
506
        // Copy the now submitted options to their final resting place so they
507
        // get saved.
508
        $form_state['values']['options']['summary_options'] = $options;
509
      }
510
    }
511

    
512
    $validate_id = $form_state['values']['options']['validate']['type'];
513
    $plugin = $this->get_plugin('argument validator', $validate_id);
514
    if ($plugin) {
515
      $options = &$form_state['values']['options']['validate']['options'][$validate_id];
516
      $plugin->options_submit($form['validate']['options'][$validate_id], $form_state, $options);
517
      // Copy the now submitted options to their final resting place so they
518
      // get saved.
519
      $form_state['values']['options']['validate_options'] = $options;
520
    }
521

    
522
    // Clear out the content of title if it's not enabled.
523
    $options =& $form_state['values']['options'];
524
    if (empty($options['title_enable'])) {
525
      $options['title'] = '';
526
    }
527
  }
528

    
529
  /**
530
   * Provide a list of default behaviors for this argument if the argument
531
   * is not present.
532
   *
533
   * Override this method to provide additional (or fewer) default behaviors.
534
   */
535
  public function default_actions($which = NULL) {
536
    $defaults = array(
537
      'ignore' => array(
538
        'title' => t('Display all results for the specified field'),
539
        'method' => 'default_ignore',
540
        // Generate a breadcrumb to here.
541
        'breadcrumb' => TRUE,
542
      ),
543
      'default' => array(
544
        'title' => t('Provide default value'),
545
        'method' => 'default_default',
546
        'form method' => 'default_argument_form',
547
        'has default argument' => TRUE,
548
        // This can only be used for missing argument, not validation failure.
549
        'default only' => TRUE,
550
        // Generate a breadcrumb to here.
551
        'breadcrumb' => TRUE,
552
      ),
553
      'not found' => array(
554
        'title' => t('Hide view'),
555
        'method' => 'default_not_found',
556
        // This is a hard fail condition.
557
        'hard fail' => TRUE,
558
      ),
559
      'summary' => array(
560
        'title' => t('Display a summary'),
561
        'method' => 'default_summary',
562
        'form method' => 'default_summary_form',
563
        'style plugin' => TRUE,
564
        // Generate a breadcrumb to here.
565
        'breadcrumb' => TRUE,
566
      ),
567
      'empty' => array(
568
        'title' => t('Display contents of "No results found"'),
569
        'method' => 'default_empty',
570
        // Generate a breadcrumb to here.
571
        'breadcrumb' => TRUE,
572
      ),
573
      'access denied' => array(
574
        'title' => t('Display "Access Denied"'),
575
        'method' => 'default_access_denied',
576
        // Generate a breadcrumb to here.
577
        'breadcrumb' => FALSE,
578
      ),
579
    );
580

    
581
    if ($this->view->display_handler->has_path()) {
582
      $defaults['not found']['title'] = t('Show "Page not found"');
583
    }
584

    
585
    if ($which) {
586
      if (!empty($defaults[$which])) {
587
        return $defaults[$which];
588
      }
589
    }
590
    else {
591
      return $defaults;
592
    }
593
  }
594

    
595
  /**
596
   * Provide a form for selecting the default argument.
597
   *
598
   * Used when the default action is set to provide default argument.
599
   */
600
  public function default_argument_form(&$form, &$form_state) {
601
    $plugins = views_fetch_plugin_data('argument default');
602
    $options = array();
603

    
604
    $form['default_argument_skip_url'] = array(
605
      '#type' => 'checkbox',
606
      '#title' => t('Skip default argument for view URL'),
607
      '#default_value' => $this->options['default_argument_skip_url'],
608
      '#description' => t('Select whether to include this default argument when constructing the URL for this view. Skipping default arguments is useful e.g. in the case of feeds.'),
609
    );
610

    
611
    $form['default_argument_type'] = array(
612
      '#prefix' => '<div id="edit-options-default-argument-type-wrapper">',
613
      '#suffix' => '</div>',
614
      '#type' => 'select',
615
      '#id' => 'edit-options-default-argument-type',
616
      '#title' => t('Type'),
617
      '#default_value' => $this->options['default_argument_type'],
618
      '#dependency' => array(
619
        'radio:options[default_action]' => array(
620
          'default',
621
        ),
622
      ),
623
      // Views custom key, moves this element to the appropriate container
624
      // under the radio button.
625
      '#argument_option' => 'default',
626
    );
627

    
628
    foreach ($plugins as $id => $info) {
629
      if (!empty($info['no ui'])) {
630
        continue;
631
      }
632
      $plugin = $this->get_plugin('argument default', $id);
633
      if ($plugin) {
634
        if ($plugin->access() || $this->options['default_argument_type'] == $id) {
635
          $form['argument_default']['#argument_option'] = 'default';
636
          $form['argument_default'][$id] = array(
637
            '#prefix' => '<div id="edit-options-argument-default-options-' . $id . '-wrapper">',
638
            '#suffix' => '</div>',
639
            '#id' => 'edit-options-argument-default-options-' . $id,
640
            '#type' => 'item',
641
            // Even if the plugin has no options add the key to the form_state.
642
            '#input' => TRUE,
643
            '#dependency' => array(
644
              'radio:options[default_action]' => array('default'),
645
              'edit-options-default-argument-type' => array($id),
646
            ),
647
            '#dependency_count' => 2,
648
          );
649
          $options[$id] = $info['title'];
650
          $plugin->options_form($form['argument_default'][$id], $form_state);
651
        }
652
      }
653
    }
654

    
655
    asort($options);
656
    $form['default_argument_type']['#options'] = $options;
657
  }
658

    
659
  /**
660
   * Provide a form for selecting further summary options when the default
661
   * action is set to display one.
662
   */
663
  public function default_summary_form(&$form, &$form_state) {
664
    $style_plugins = views_fetch_plugin_data('style');
665
    $summary_plugins = array();
666
    $format_options = array();
667
    foreach ($style_plugins as $key => $plugin) {
668
      if (isset($plugin['type']) && $plugin['type'] == 'summary') {
669
        $summary_plugins[$key] = $plugin;
670
        $format_options[$key] = $plugin['title'];
671
      }
672
    }
673

    
674
    $form['summary'] = array(
675
      // Views custom key, moves this element to the appropriate container
676
      // under the radio button.
677
      '#argument_option' => 'summary',
678
    );
679
    $form['summary']['sort_order'] = array(
680
      '#type' => 'radios',
681
      '#title' => t('Sort order'),
682
      '#options' => array('asc' => t('Ascending'), 'desc' => t('Descending')),
683
      '#default_value' => $this->options['summary']['sort_order'],
684
      '#dependency' => array('radio:options[default_action]' => array('summary')),
685
    );
686
    $form['summary']['number_of_records'] = array(
687
      '#type' => 'radios',
688
      '#title' => t('Sort by'),
689
      '#default_value' => $this->options['summary']['number_of_records'],
690
      '#options' => array(
691
        0 => $this->get_sort_name(),
692
        1 => t('Number of records'),
693
      ),
694
      '#dependency' => array('radio:options[default_action]' => array('summary')),
695
    );
696

    
697
    $form['summary']['format'] = array(
698
      '#type' => 'radios',
699
      '#title' => t('Format'),
700
      '#options' => $format_options,
701
      '#default_value' => $this->options['summary']['format'],
702
      '#dependency' => array('radio:options[default_action]' => array('summary')),
703
    );
704

    
705
    foreach ($summary_plugins as $id => $info) {
706
      if (empty($info['uses options'])) {
707
        continue;
708
      }
709
      $plugin = $this->get_plugin('style', $id);
710
      if ($plugin) {
711
        $form['summary']['options'][$id] = array(
712
          '#prefix' => '<div id="edit-options-summary-options-' . $id . '-wrapper">',
713
          '#suffix' => '</div>',
714
          '#id' => 'edit-options-summary-options-' . $id,
715
          '#type' => 'item',
716
          // Trick it into checking input to make #process run.
717
          '#input' => TRUE,
718
          '#dependency' => array(
719
            'radio:options[default_action]' => array('summary'),
720
            'radio:options[summary][format]' => array($id),
721
          ),
722
          '#dependency_count' => 2,
723
        );
724
        $options[$id] = $info['title'];
725
        $plugin->options_form($form['summary']['options'][$id], $form_state);
726
      }
727
    }
728
  }
729

    
730
  /**
731
   * Handle the default action, which means our argument wasn't present.
732
   *
733
   * Override this method only with extreme care.
734
   *
735
   * @return bool
736
   *   A boolean value; if TRUE, continue building this view. If FALSE,
737
   *   building the view will be aborted here.
738
   */
739
  public function default_action($info = NULL) {
740
    if (!isset($info)) {
741
      $info = $this->default_actions($this->options['default_action']);
742
    }
743

    
744
    if (!$info) {
745
      return FALSE;
746
    }
747

    
748
    if (!empty($info['method args'])) {
749
      return call_user_func_array(array(&$this, $info['method']), $info['method args']);
750
    }
751
    else {
752
      return $this->{$info['method']}();
753
    }
754
  }
755

    
756
  /**
757
   * How to act if validation fails.
758
   */
759
  public function validate_fail() {
760
    $info = $this->default_actions($this->options['validate']['fail']);
761
    return $this->default_action($info);
762
  }
763

    
764
  /**
765
   * Default action: ignore.
766
   *
767
   * If an argument was expected and was not given, in this case, simply ignore
768
   * the argument entirely.
769
   */
770
  public function default_ignore() {
771
    return TRUE;
772
  }
773

    
774
  /**
775
   * Default action: not found.
776
   *
777
   * If an argument was expected and was not given, in this case, report the
778
   * view as 'not found' or hide it.
779
   */
780
  public function default_not_found() {
781
    // Set a failure condition and let the display manager handle it.
782
    $this->view->build_info['fail'] = TRUE;
783
    return FALSE;
784
  }
785

    
786
  /**
787
   * Default action: access denied.
788
   *
789
   * If an argument was expected and was not given, in this case, report the
790
   * view as 'access denied'.
791
   */
792
  public function default_access_denied() {
793
    $this->view->build_info['denied'] = TRUE;
794
    return FALSE;
795
  }
796

    
797
  /**
798
   * Default action: empty
799
   *
800
   * If an argument was expected and was not given, in this case, display the
801
   * view's empty text
802
   */
803
  public function default_empty() {
804
    // We return with no query; this will force the empty text.
805
    $this->view->built = TRUE;
806
    $this->view->executed = TRUE;
807
    $this->view->result = array();
808
    return FALSE;
809
  }
810

    
811
  /**
812
   * This just returns true.
813
   *
814
   * The view argument builder will know where to find the argument from.
815
   *
816
   * @todo Why is this needed?
817
   */
818
  public function default_default() {
819
    return TRUE;
820
  }
821

    
822
  /**
823
   * Determine if the argument is set to provide a default argument.
824
   */
825
  public function has_default_argument() {
826
    $info = $this->default_actions($this->options['default_action']);
827
    return !empty($info['has default argument']);
828
  }
829

    
830
  /**
831
   * Get a default argument, if available.
832
   */
833
  public function get_default_argument() {
834
    $plugin = $this->get_plugin('argument default');
835
    if ($plugin) {
836
      return $plugin->get_argument();
837
    }
838
  }
839

    
840
  /**
841
   * Process the summary arguments for display.
842
   *
843
   * For example, the validation plugin may want to alter an argument for use in
844
   * the URL.
845
   */
846
  public function process_summary_arguments(&$args) {
847
    if ($this->options['validate']['type'] != 'none') {
848
      if (isset($this->validator) || $this->validator = $this->get_plugin('argument validator')) {
849
        $this->validator->process_summary_arguments($args);
850
      }
851
    }
852
  }
853

    
854
  /**
855
   * Default action: summary.
856
   *
857
   * If an argument was expected and was not given, in this case, display a
858
   * summary query.
859
   */
860
  public function default_summary() {
861
    $this->view->build_info['summary'] = TRUE;
862
    $this->view->build_info['summary_level'] = $this->options['id'];
863

    
864
    // Change the display style to the summary style for this argument.
865
    $this->view->plugin_name = $this->options['summary']['format'];
866
    $this->view->style_options = $this->options['summary_options'];
867

    
868
    // Clear out the normal primary field and whatever else may have been added
869
    // and let the summary do the work.
870
    $this->query->clear_fields();
871
    $this->summary_query();
872

    
873
    $by = $this->options['summary']['number_of_records'] ? 'num_records' : NULL;
874
    $this->summary_sort($this->options['summary']['sort_order'], $by);
875

    
876
    // Summaries have their own sorting and fields, so tell the View not
877
    // to build these.
878
    $this->view->build_sort = $this->view->build_fields = FALSE;
879
    return TRUE;
880
  }
881

    
882
  /**
883
   * Build the info for the summary query.
884
   *
885
   * This must:
886
   * - add_groupby: group on this field in order to create summaries.
887
   * - add_field: add a 'num_nodes' field for the count. Usually it will be a
888
   *   count on $view->base_field
889
   * - set_count_field: Reset the count field so we get the right paging.
890
   *
891
   * @return string
892
   *   The alias used to get the number of records (count) for this entry.
893
   */
894
  public function summary_query() {
895
    $this->ensure_my_table();
896
    // Add the field.
897
    $this->base_alias = $this->query->add_field($this->table_alias, $this->real_field);
898

    
899
    $this->summary_name_field();
900
    return $this->summary_basics();
901
  }
902

    
903
  /**
904
   * Add the name field, which is the field displayed in summary queries.
905
   *
906
   * This is often used when the argument is numeric.
907
   */
908
  public function summary_name_field() {
909
    // Add the 'name' field. For example, if this is a uid argument, the name
910
    // field would be 'name' (i.e, the username).
911
    if (isset($this->name_table)) {
912
      // If the alias is different then we're probably added, not ensured, so
913
      // look up the join and add it instead.
914
      if ($this->table_alias != $this->name_table) {
915
        $j = views_get_table_join($this->name_table, $this->table);
916
        if ($j) {
917
          $join = clone $j;
918
          $join->left_table = $this->table_alias;
919
          $this->name_table_alias = $this->query->add_table($this->name_table, $this->relationship, $join);
920
        }
921
      }
922
      else {
923
        $this->name_table_alias = $this->query->ensure_table($this->name_table, $this->relationship);
924
      }
925
    }
926
    else {
927
      $this->name_table_alias = $this->table_alias;
928
    }
929

    
930
    if (isset($this->name_field)) {
931
      $this->name_alias = $this->query->add_field($this->name_table_alias, $this->name_field);
932
    }
933
    else {
934
      $this->name_alias = $this->base_alias;
935
    }
936
  }
937

    
938
  /**
939
   * Some basic summary behavior.
940
   *
941
   * This doesn't need to be repeated as much as code that goes into
942
   * summary_query().
943
   */
944
  public function summary_basics($count_field = TRUE) {
945
    // Add the number of nodes counter.
946
    $distinct = ($this->view->display_handler->get_option('distinct') && empty($this->query->no_distinct));
947

    
948
    $count_alias = $this->query->add_field($this->query->base_table,
949
      $this->query->base_field, 'num_records',
950
      array(
951
        'count' => TRUE,
952
        'distinct' => $distinct,
953
      ));
954
    $this->query->add_groupby($this->name_alias);
955

    
956
    if ($count_field) {
957
      $this->query->set_count_field($this->table_alias, $this->real_field);
958
    }
959

    
960
    $this->count_alias = $count_alias;
961
  }
962

    
963
  /**
964
   * Sorts the summary based upon the user's selection.
965
   *
966
   * The base variant of this is usually adequte.
967
   *
968
   * @param string $order
969
   *   The order selected in the UI.
970
   */
971
  public function summary_sort($order, $by = NULL) {
972
    $this->query->add_orderby(NULL, NULL, $order, (!empty($by) ? $by : $this->name_alias));
973
  }
974

    
975
  /**
976
   * Provide the argument to use to link from the summary to the next level.
977
   *
978
   * This will be called once per row of a summary, and used as part of
979
   * $view->get_url().
980
   *
981
   * @param object $data
982
   *   The query results for the row.
983
   */
984
  public function summary_argument($data) {
985
    return $data->{$this->base_alias};
986
  }
987

    
988
  /**
989
   * Provides the name to use for the summary.
990
   *
991
   * By default this is just the name field.
992
   *
993
   * @param object $data
994
   *   The query results for the row.
995
   *
996
   * @return string
997
   *   The summary.
998
   */
999
  public function summary_name($data) {
1000
    $value = $data->{$this->name_alias};
1001
    if (empty($value) && !empty($this->definition['empty field name'])) {
1002
      $value = $this->definition['empty field name'];
1003
    }
1004
    return check_plain($value);
1005
  }
1006

    
1007
  /**
1008
   * Set up the query for this argument.
1009
   *
1010
   * The argument sent may be found at $this->argument.
1011
   *
1012
   * @param bool $group_by
1013
   *   Whether the query uses a group-by.
1014
   */
1015
  public function query($group_by = FALSE) {
1016
    $this->ensure_my_table();
1017
    $this->query->add_where(0, "$this->table_alias.$this->real_field", $this->argument);
1018
  }
1019

    
1020
  /**
1021
   * Get the title this argument will assign the view, given the argument.
1022
   *
1023
   * This usually needs to be overridden to provide a proper title.
1024
   */
1025
  public function title() {
1026
    return check_plain($this->argument);
1027
  }
1028

    
1029
  /**
1030
   * Called by the view object to get the title.
1031
   *
1032
   * This may be set by a validator so we don't necessarily call through to
1033
   * title().
1034
   */
1035
  public function get_title() {
1036
    if (isset($this->validated_title)) {
1037
      return $this->validated_title;
1038
    }
1039
    else {
1040
      return $this->title();
1041
    }
1042
  }
1043

    
1044
  /**
1045
   * Validate that this argument works. By default, all arguments are valid.
1046
   */
1047
  public function validate_arg($arg) {
1048
    // By using % in URLs, arguments could be validated twice; this eases
1049
    // that pain.
1050
    if (isset($this->argument_validated)) {
1051
      return $this->argument_validated;
1052
    }
1053

    
1054
    if ($this->is_exception($arg)) {
1055
      return $this->argument_validated = TRUE;
1056
    }
1057

    
1058
    if ($this->options['validate']['type'] == 'none') {
1059
      return $this->argument_validated = $this->validate_argument_basic($arg);
1060
    }
1061

    
1062
    $plugin = $this->get_plugin('argument validator');
1063
    if ($plugin) {
1064
      return $this->argument_validated = $plugin->validate_argument($arg);
1065
    }
1066

    
1067
    // If the plugin isn't found, fall back to the basic validation path.
1068
    return $this->argument_validated = $this->validate_argument_basic($arg);
1069
  }
1070

    
1071
  /**
1072
   * Called by the menu system to validate an argument.
1073
   *
1074
   * This checks to see if this is a 'soft fail', which means that if the
1075
   * argument fails to validate, but there is an action to take anyway, then
1076
   * validation cannot actually fail.
1077
   */
1078
  public function validate_argument($arg) {
1079
    $validate_info = $this->default_actions($this->options['validate']['fail']);
1080
    if (empty($validate_info['hard fail'])) {
1081
      return TRUE;
1082
    }
1083

    
1084
    $rc = $this->validate_arg($arg);
1085

    
1086
    // If the validator has changed the validate fail condition to a soft fail,
1087
    // deal with that.
1088
    $validate_info = $this->default_actions($this->options['validate']['fail']);
1089
    if (empty($validate_info['hard fail'])) {
1090
      return TRUE;
1091
    }
1092

    
1093
    return $rc;
1094
  }
1095

    
1096
  /**
1097
   * Provide a basic argument validation.
1098
   *
1099
   * This can be overridden for more complex types; the basic validator only
1100
   * checks to see if the argument is not NULL or is numeric if the definition
1101
   * says it's numeric.
1102
   *
1103
   * @return bool
1104
   *   Whether or not the argument validates.
1105
   */
1106
  public function validate_argument_basic($arg) {
1107
    if (!isset($arg) || $arg === '') {
1108
      return FALSE;
1109
    }
1110

    
1111
    if (!empty($this->definition['numeric']) && !isset($this->options['break_phrase']) && !is_numeric($arg)) {
1112
      return FALSE;
1113
    }
1114

    
1115
    return TRUE;
1116
  }
1117

    
1118
  /**
1119
   * Set the input for this argument
1120
   *
1121
   * @return bool
1122
   *   TRUE if it successfully validates; FALSE if it does not.
1123
   */
1124
  public function set_argument($arg) {
1125
    $this->argument = $arg;
1126
    return $this->validate_arg($arg);
1127
  }
1128

    
1129
  /**
1130
   * Get the value of this argument.
1131
   *
1132
   * @return string
1133
   *   The value.
1134
   */
1135
  public function get_value() {
1136
    // If we already processed this argument, we're done.
1137
    if (isset($this->argument)) {
1138
      return $this->argument;
1139
    }
1140

    
1141
    // Otherwise, we have to pretend to process ourself to find the value.
1142
    $value = NULL;
1143
    // Find the position of this argument within the view.
1144
    $position = 0;
1145
    foreach ($this->view->argument as $id => $argument) {
1146
      if ($id == $this->options['id']) {
1147
        break;
1148
      }
1149
      $position++;
1150
    }
1151

    
1152
    $arg = isset($this->view->args[$position]) ? $this->view->args[$position] : NULL;
1153
    $this->position = $position;
1154

    
1155
    // Clone ourselves so that we don't break things when we're really
1156
    // processing the arguments.
1157
    $argument = clone $this;
1158
    if (!isset($arg) && $argument->has_default_argument()) {
1159
      $arg = $argument->get_default_argument();
1160
    }
1161
    // Set the argument, which will also validate that the argument can be set.
1162
    if ($argument->set_argument($arg)) {
1163
      $value = $argument->argument;
1164
    }
1165
    unset($argument);
1166
    return $value;
1167
  }
1168

    
1169
  /**
1170
   * Export handler for summary export.
1171
   *
1172
   * Arguments can have styles for the summary view. This special export
1173
   * handler makes sure this works properly.
1174
   *
1175
   * @return string
1176
   *   The export summary.
1177
   */
1178
  public function export_summary($indent, $prefix, $storage, $option, $definition, $parents) {
1179
    $output = '';
1180
    $name = $this->options['summary'][$option];
1181
    $options = $this->options['summary_options'];
1182

    
1183
    $plugin = views_get_plugin('style', $name);
1184
    if ($plugin) {
1185
      $plugin->init($this->view, $this->view->display_handler->display, $options);
1186
      // Write which plugin to use.
1187
      $output .= $indent . $prefix . "['summary']['$option'] = '$name';\n";
1188

    
1189
      // Pass off to the plugin to export itself.
1190
      $output .= $plugin->export_options($indent, $prefix . "['summary_options']");
1191
    }
1192

    
1193
    return $output;
1194
  }
1195

    
1196
  /**
1197
   * Export handler for validation export.
1198
   *
1199
   * Arguments use validation plugins. This special export handler makes sure
1200
   * this works properly.
1201
   *
1202
   * @return string
1203
   *   The validation response.
1204
   */
1205
  public function export_validation($indent, $prefix, $storage, $option, $definition, $parents) {
1206
    $output = '';
1207
    $name = $this->options['validate'][$option];
1208
    $options = $this->options['validate_options'];
1209

    
1210
    $plugin = views_get_plugin('argument validator', $name);
1211
    if ($plugin) {
1212
      $plugin->init($this->view, $this->display, $options);
1213
      // Write which plugin to use.
1214
      $output .= $indent . $prefix . "['validate']['$option'] = '$name';\n";
1215

    
1216
      // Pass off to the plugin to export itself.
1217
      $output .= $plugin->export_options($indent, $prefix . "['validate_options']");
1218
    }
1219

    
1220
    return $output;
1221
  }
1222

    
1223
  /**
1224
   * Generic plugin export handler.
1225
   *
1226
   * Since style and validation plugins have their own export handlers, this
1227
   * one is currently only used for default argument plugins.
1228
   *
1229
   * @return string
1230
   *   Export string.
1231
   */
1232
  public function export_plugin($indent, $prefix, $storage, $option, $definition, $parents) {
1233
    $output = '';
1234
    if ($option == 'default_argument_type') {
1235
      $type = 'argument default';
1236
      $option_name = 'default_argument_options';
1237
    }
1238

    
1239
    $plugin = $this->get_plugin($type);
1240
    $name = $this->options[$option];
1241

    
1242
    if ($plugin) {
1243
      // Write which plugin to use.
1244
      $output .= $indent . $prefix . "['$option'] = '$name';\n";
1245

    
1246
      // Pass off to the plugin to export itself.
1247
      $output .= $plugin->export_options($indent, $prefix . "['$option_name']");
1248
    }
1249

    
1250
    return $output;
1251
  }
1252

    
1253
  /**
1254
   * Get the display or row plugin, if it exists.
1255
   */
1256
  public function get_plugin($type = 'argument default', $name = NULL) {
1257
    $options = array();
1258
    switch ($type) {
1259
      case 'argument default':
1260
        $plugin_name = $this->options['default_argument_type'];
1261
        $options_name = 'default_argument_options';
1262
        break;
1263

    
1264
      case 'argument validator':
1265
        $plugin_name = $this->options['validate']['type'];
1266
        $options_name = 'validate_options';
1267
        break;
1268

    
1269
      case 'style':
1270
        $plugin_name = $this->options['summary']['format'];
1271
        $options_name = 'summary_options';
1272
        break;
1273
    }
1274

    
1275
    if (!$name) {
1276
      $name = $plugin_name;
1277
    }
1278

    
1279
    // We only fetch the options if we're fetching the plugin actually in use.
1280
    if ($name == $plugin_name) {
1281
      $options = $this->options[$options_name];
1282
    }
1283

    
1284
    $plugin = views_get_plugin($type, $name);
1285
    if ($plugin) {
1286
      // Style plugins expects different parameters as argument related plugins.
1287
      if ($type == 'style') {
1288
        $plugin->init($this->view, $this->view->display_handler->display, $options);
1289
      }
1290
      else {
1291
        $plugin->init($this->view, $this, $options);
1292
      }
1293
      return $plugin;
1294
    }
1295
  }
1296

    
1297
  /**
1298
   * Return a description of how the argument would normally be sorted.
1299
   *
1300
   * Subclasses should override this to specify what the default sort order of
1301
   * their argument is (e.g. alphabetical, numeric, date).
1302
   *
1303
   * @return string
1304
   *   The label for the sorter.
1305
   */
1306
  public function get_sort_name() {
1307
    return t('Default sort', array(), array('context' => 'Sort order'));
1308
  }
1309

    
1310
}
1311

    
1312
/**
1313
 * A special handler to take the place of missing or broken handlers.
1314
 *
1315
 * @ingroup views_argument_handlers
1316
 */
1317
class views_handler_argument_broken extends views_handler_argument {
1318

    
1319
  /**
1320
   * {@inheritdoc}
1321
   */
1322
  public function ui_name($short = FALSE) {
1323
    return t('Broken/missing handler');
1324
  }
1325

    
1326
  /**
1327
   * {@inheritdoc}
1328
   */
1329
  public function ensure_my_table() {
1330
    // No table to ensure!
1331
  }
1332

    
1333
  /**
1334
   * {@inheritdoc}
1335
   */
1336
  public function query($group_by = FALSE) {
1337
    // No query to run.
1338
  }
1339

    
1340
  /**
1341
   * {@inheritdoc}
1342
   */
1343
  public function options_form(&$form, &$form_state) {
1344
    $form['markup'] = array(
1345
      '#markup' => '<div class="form-item description">' . 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.') . '</div>',
1346
    );
1347
  }
1348

    
1349
  /**
1350
   * {@inheritdoc}
1351
   */
1352
  public function broken() {
1353
    return TRUE;
1354
  }
1355

    
1356
}
1357

    
1358
/**
1359
 * @}
1360
 */