Projet

Général

Profil

Paste
Télécharger (112 ko) Statistiques
| Branche: | Révision:

root / drupal7 / sites / all / modules / views / plugins / views_plugin_display.inc @ 4003efde

1
<?php
2

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

    
8
/**
9
 * @defgroup views_display_plugins Views display plugins
10
 * @{
11
 * Display plugins control how Views interact with the rest of Drupal.
12
 *
13
 * They can handle creating Views from a Drupal page hook; they can
14
 * handle creating Views from a Drupal block hook. They can also
15
 * handle creating Views from an external module source, such as
16
 * a Panels pane, or an insert view, or a CCK field type.
17
 *
18
 * @see hook_views_plugins()
19
 */
20

    
21
/**
22
 * The default display plugin handler. Display plugins handle options and
23
 * basic mechanisms for different output methods.
24
 */
25
class views_plugin_display extends views_plugin {
26

    
27
  /**
28
   * The top object of a view.
29
   *
30
   * @var view
31
   */
32
  public $view = NULL;
33

    
34
  /**
35
   * List of handlers for this display.
36
   *
37
   * @var array
38
   */
39
  public $handlers = array();
40

    
41
  /**
42
   * Stores all available display extenders.
43
   *
44
   * @var array
45
   */
46
  public $extender = array();
47

    
48
  /**
49
   * {@inheritdoc}
50
   */
51
  public function init(&$view, &$display, $options = NULL) {
52
    $this->view = &$view;
53
    $this->display = &$display;
54

    
55
    // Load extenders as soon as possible.
56
    $this->extender = array();
57
    $extenders = views_get_enabled_display_extenders();
58
    // If you update to the dev version the registry might not be loaded yet.
59
    if (!empty($extenders) && class_exists('views_plugin_display_extender')) {
60
      foreach ($extenders as $extender) {
61
        $plugin = views_get_plugin('display_extender', $extender);
62
        if ($plugin) {
63
          $plugin->init($this->view, $this);
64
          $this->extender[$extender] = $plugin;
65
        }
66
        else {
67
          vpr('Invalid display extender @extender', array('@extender' => $extender));
68
        }
69
      }
70
    }
71

    
72
    // Track changes that the user should know about.
73
    $changed = FALSE;
74

    
75
    // Make some modifications.
76
    if (!isset($options) && isset($display->display_options)) {
77
      $options = $display->display_options;
78
    }
79

    
80
    if ($this->is_default_display() && isset($options['defaults'])) {
81
      unset($options['defaults']);
82
    }
83

    
84
    // Cache for unpack_options, but not if we are in the ui.
85
    static $unpack_options = array();
86
    if (empty($view->editing)) {
87
      $cid = 'unpack_options:' . md5(serialize(array($this->options, $options)));
88
      if (empty($unpack_options[$cid])) {
89
        $cache = views_cache_get($cid, TRUE);
90
        if (!empty($cache->data)) {
91
          $this->options = $cache->data;
92
        }
93
        else {
94
          $this->unpack_options($this->options, $options);
95
          views_cache_set($cid, $this->options, TRUE);
96
        }
97
        $unpack_options[$cid] = $this->options;
98
      }
99
      else {
100
        $this->options = $unpack_options[$cid];
101
      }
102
    }
103
    else {
104
      $this->unpack_options($this->options, $options);
105
    }
106

    
107
    // Translate changed settings.
108
    $items_per_page = $this->get_option('items_per_page');
109
    $offset = $this->get_option('offset');
110
    $use_pager = $this->get_option('use_pager');
111
    $pager = $this->get_option('pager');
112
    // Check if the pager options were already converted.
113
    // The pager settings of a Views 2.x view specifying 10 items with an
114
    // offset of 0 and no pager is the same as of a Views 3.x view with
115
    // default settings. In this case, the only way to determine which case we
116
    // are dealing with is checking the API version but that's only available
117
    // for exported Views as it's not stored in the database.
118
    // If you would like to change this code, really take care that you thought
119
    // of every possibility.
120
    // @todo Provide a way to convert the database views as well.
121
    if (((!empty($items_per_page) && $items_per_page != 10) || !empty($offset) || !empty($use_pager))
122
      || (!empty($view->api_version) && $view->api_version == 2)) {
123
      // Find out the right pager type.
124
      // If the view "use pager" it's a normal/full pager.
125
      if ($use_pager) {
126
        $type = 'full';
127
      }
128
      // If it does not use pager, but 0 items per page it should not page
129
      // else it should display just a certain amount of items.
130
      else {
131
        $type = $items_per_page ? 'some' : 'none';
132
      }
133

    
134
      // Setup the pager options.
135
      $pager = array(
136
        'type' => $type,
137
        'options' => array(
138
          'offset' => intval($offset),
139
        ),
140
      );
141

    
142
      if ($items_per_page) {
143
        $pager['options']['items_per_page'] = $items_per_page;
144
      }
145
      // Setup the pager element.
146
      if ($id = $this->get_option('pager_element')) {
147
        $pager['options']['id'] = $id;
148
      }
149

    
150
      // Unset the previous options. After edit and save the view they will be
151
      // erased.
152
      $this->set_option('items_per_page', NULL);
153
      $this->set_option('offset', NULL);
154
      $this->set_option('use_pager', NULL);
155
      $this->set_option('pager', $pager);
156
      $changed = TRUE;
157
    }
158

    
159
    // Plugable headers, footer and empty texts are not compatible with
160
    // previous version of views. This code converts old values into a
161
    // configured handler for each area.
162
    foreach (array('header', 'footer', 'empty') as $area) {
163
      $converted = FALSE;
164
      if (isset($this->options[$area]) && !is_array($this->options[$area])) {
165
        if (!empty($this->options[$area])) {
166
          $content = $this->get_option($area);
167
          if (!empty($content) && !is_array($content)) {
168
            $format = $this->get_option($area . '_format');
169
            $options = array(
170
              'id' => 'area',
171
              'table' => 'views',
172
              'field' => 'area',
173
              'label' => '',
174
              'relationship' => 'none',
175
              'group_type' => 'group',
176
              'content' => $content,
177
              'format' => !empty($format) ? $format : filter_default_format(),
178
            );
179

    
180
            if ($area != 'empty' && $empty = $this->get_option($area . '_empty')) {
181
              $options['empty'] = $empty;
182
            }
183
            $this->set_option($area, array('text' => $options));
184
            $converted = TRUE;
185
            $changed = TRUE;
186
          }
187
        }
188
        // Ensure that options are at least an empty array.
189
        if (!$converted) {
190
          $this->set_option($area, array());
191
        }
192
      }
193
    }
194

    
195
    // Convert distinct setting from display to query settings.
196
    $distinct = $this->get_option('distinct');
197
    if (!empty($distinct)) {
198
      $query_settings = $this->get_option('query');
199
      $query_settings['options']['distinct'] = $distinct;
200
      $this->set_option('query', $query_settings);
201
      // Clear the values.
202
      $this->set_option('distinct', NULL);
203
      $changed = TRUE;
204
    }
205

    
206
    // Convert field language settings.
207
    $query_options = $this->get_option('query');
208
    if (isset($query_options['options']['field_language'])) {
209
      $this->set_option('field_language', $query_options['options']['field_language']);
210
      unset($query_options['options']['field_language']);
211
      $changed = TRUE;
212
    }
213
    if (isset($query_options['options']['field_language_add_to_query'])) {
214
      $this->set_option('field_language_add_to_query', $query_options['options']['field_language_add_to_query']);
215
      unset($query_options['options']['field_language_add_to_query']);
216
      $changed = TRUE;
217
    }
218
    $this->set_option('query', $query_options);
219

    
220
    // Convert filter groups.
221
    $filter_groups = $this->get_option('filter_groups');
222
    // Only convert if it wasn't converted yet, which is the case if there is a
223
    // '0' group.
224
    if (isset($filter_groups['groups'][0])) {
225
      // Update filter groups.
226
      $filter_groups['groups'] = views_array_key_plus($filter_groups['groups']);
227
      $this->set_option('filter_groups', $filter_groups);
228
      // Update the filter group on each filter.
229
      $filters = $this->get_option('filters');
230
      foreach ($filters as &$filter) {
231
        if (isset($filter['group'])) {
232
          $filter['group']++;
233
        }
234
        else {
235
          $filter['group'] = 1;
236
        }
237
      }
238
      $this->set_option('filters', $filters);
239
      $changed = TRUE;
240
    }
241

    
242
    // Filter groups were allowed to be rewritten without its filters, so
243
    // before this update the view was using the default values. To be sure that
244
    // the existing view isn't broken, don't use this overridden values but copy
245
    // them from the default display. Only do this if the filters are overridden
246
    // but the filter_groups are not marked as so.
247
    if (!$this->is_default_display() && !$this->options['defaults']['filters'] && $this->options['defaults']['filter_groups']) {
248
      // Set filter_groups to be overridden and save the value in the
249
      // display_options as well.
250
      $this->options['defaults']['filter_groups'] = FALSE;
251
      $this->display->display_options['defaults']['filter_groups'] = $this->options['defaults']['filter_groups'];
252
      // Copy the filter_groups from the default, and add them to the
253
      // display_options as well. $this->default_display is not initialized at
254
      // this point.
255
      $this->options['filter_groups'] = $this->view->display['default']->handler->options['filter_groups'];
256
      $this->display->display_options['filter_groups'] = $this->options['filter_groups'];
257

    
258
      $changed = TRUE;
259
    }
260

    
261
    // Mark the view as changed so the user has a chance to save it.
262
    if ($changed) {
263
      $this->view->changed = TRUE;
264
    }
265
  }
266

    
267
  /**
268
   * {@inheritdoc}
269
   */
270
  public function destroy() {
271
    parent::destroy();
272

    
273
    foreach ($this->handlers as $type => $handlers) {
274
      foreach ($handlers as $id => $handler) {
275
        if (is_object($handler)) {
276
          $this->handlers[$type][$id]->destroy();
277
        }
278
      }
279
    }
280

    
281
    if (isset($this->default_display)) {
282
      unset($this->default_display);
283
    }
284

    
285
    foreach ($this->extender as $extender) {
286
      $extender->destroy();
287
    }
288
  }
289

    
290
  /**
291
   * If this display is the 'default' display which contains fallback settings.
292
   *
293
   * @return bool
294
   *   This is the default display and contains fallback settings.
295
   */
296
  public function is_default_display() {
297
    return FALSE;
298
  }
299

    
300
  /**
301
   * Does this display uses exposed filters?
302
   *
303
   * So the view will know whether or not to build them.
304
   */
305
  public function uses_exposed() {
306
    if (!isset($this->has_exposed)) {
307
      foreach ($this->handlers as $type => $value) {
308
        foreach ($this->view->$type as $id => $handler) {
309
          if ($handler->can_expose() && $handler->is_exposed()) {
310
            // one is all we need; if we find it, return true.
311
            $this->has_exposed = TRUE;
312
            return TRUE;
313
          }
314
        }
315
      }
316
      $pager = $this->get_plugin('pager');
317
      if ($pager && $pager->uses_exposed()) {
318
        $this->has_exposed = TRUE;
319
        return TRUE;
320
      }
321
      $this->has_exposed = FALSE;
322
    }
323

    
324
    return $this->has_exposed;
325
  }
326

    
327
  /**
328
   * Determine if this display should display the exposed filters widgets.
329
   *
330
   * If so, the view will know whether or not to render them.
331
   *
332
   * Regardless of what this function returns, exposed filters will not be used
333
   * nor displayed unless uses_exposed() returns TRUE.
334
   */
335
  public function displays_exposed() {
336
    return TRUE;
337
  }
338

    
339
  /**
340
   * Does the display use AJAX?
341
   */
342
  public function use_ajax() {
343
    if (!empty($this->definition['use ajax'])) {
344
      return $this->get_option('use_ajax');
345
    }
346
    return FALSE;
347
  }
348

    
349
  /**
350
   * Does the display have a pager enabled?
351
   */
352
  public function use_pager() {
353
    $pager = $this->get_plugin('pager');
354
    if ($pager) {
355
      return $pager->use_pager();
356
    }
357
  }
358

    
359
  /**
360
   * Does the display have a more link enabled?
361
   */
362
  public function use_more() {
363
    if (!empty($this->definition['use more'])) {
364
      return $this->get_option('use_more');
365
    }
366
    return FALSE;
367
  }
368

    
369
  /**
370
   * Does the display have groupby enabled?
371
   */
372
  public function use_group_by() {
373
    return $this->get_option('group_by');
374
  }
375

    
376
  /**
377
   * Should the enabled display more link be shown when no more items?
378
   */
379
  public function use_more_always() {
380
    if (!empty($this->definition['use more'])) {
381
      return $this->get_option('use_more_always');
382
    }
383
    return FALSE;
384
  }
385

    
386
  /**
387
   * Should the enabled display more link being opened in an new window?
388
   */
389
  public function use_more_open_new_window() {
390
    if (!empty($this->definition['use more'])) {
391
      return $this->get_option('open_new_window');
392
    }
393
    return FALSE;
394
  }
395

    
396
  /**
397
   * Does the display have custom link text?
398
   */
399
  public function use_more_text() {
400
    if (!empty($this->definition['use more'])) {
401
      return $this->get_option('use_more_text');
402
    }
403
    return FALSE;
404
  }
405

    
406
  /**
407
   * Can this display accept attachments?
408
   */
409
  public function accept_attachments() {
410
    if (empty($this->definition['accept attachments'])) {
411
      return FALSE;
412
    }
413
    if (!empty($this->view->argument) && $this->get_option('hide_attachment_summary')) {
414
      foreach ($this->view->argument as $argument_id => $argument) {
415
        if ($argument->needs_style_plugin() && empty($argument->argument_validated)) {
416
          return FALSE;
417
        }
418
      }
419
    }
420
    return TRUE;
421
  }
422

    
423
  /**
424
   * Allow displays to attach to other views.
425
   */
426
  public function attach_to($display_id) {
427
  }
428

    
429
  /**
430
   * List which sections are defaultable and what items each section contains.
431
   */
432
  public function defaultable_sections($section = NULL) {
433
    $sections = array(
434
      'access' => array('access', 'access_options'),
435
      'access_options' => array('access', 'access_options'),
436
      'cache' => array('cache', 'cache_options'),
437
      'cache_options' => array('cache', 'cache_options'),
438
      'title' => array('title'),
439
      'css_class' => array('css_class'),
440
      'use_ajax' => array('use_ajax'),
441
      'hide_attachment_summary' => array('hide_attachment_summary'),
442
      'hide_admin_links' => array('hide_admin_links'),
443
      'group_by' => array('group_by'),
444
      'query' => array('query'),
445
      'use_more' => array('use_more',
446
        'use_more_always',
447
        'open_new_window',
448
        'use_more_text',
449
      ),
450
      'use_more_always' => array(
451
        'use_more',
452
        'use_more_always',
453
        'open_new_window',
454
        'use_more_text',
455
      ),
456
      'use_more_text' => array(
457
        'use_more',
458
        'use_more_always',
459
        'open_new_window',
460
        'use_more_text',
461
      ),
462
      'open_new_window' => array(
463
        'use_more',
464
        'use_more_always',
465
        'open_new_window',
466
        'use_more_text',
467
      ),
468
      'link_display' => array('link_display', 'link_url'),
469

    
470
      // Force these to cascade properly.
471
      'style_plugin' => array(
472
        'style_plugin',
473
        'style_options',
474
        'row_plugin',
475
        'row_options',
476
      ),
477
      'style_options' => array(
478
        'style_plugin',
479
        'style_options',
480
        'row_plugin',
481
        'row_options',
482
      ),
483
      'row_plugin' => array(
484
        'style_plugin',
485
        'style_options',
486
        'row_plugin',
487
        'row_options',
488
      ),
489
      'row_options' => array(
490
        'style_plugin',
491
        'style_options',
492
        'row_plugin',
493
        'row_options',
494
      ),
495

    
496
      'pager' => array('pager', 'pager_options'),
497
      'pager_options' => array('pager', 'pager_options'),
498

    
499
      'exposed_form' => array('exposed_form', 'exposed_form_options'),
500
      'exposed_form_options' => array('exposed_form', 'exposed_form_options'),
501

    
502
      // These guys are special.
503
      'header' => array('header'),
504
      'footer' => array('footer'),
505
      'empty' => array('empty'),
506
      'relationships' => array('relationships'),
507
      'fields' => array('fields'),
508
      'sorts' => array('sorts'),
509
      'arguments' => array('arguments'),
510
      'filters' => array('filters', 'filter_groups'),
511
      'filter_groups' => array('filters', 'filter_groups'),
512
    );
513

    
514
    // If the display cannot use a pager, then we cannot default it.
515
    if (empty($this->definition['use pager'])) {
516
      unset($sections['pager']);
517
      unset($sections['items_per_page']);
518
    }
519

    
520
    foreach ($this->extender as $extender) {
521
      $extender->defaultable_sections($sections, $section);
522
    }
523

    
524
    if ($section) {
525
      if (!empty($sections[$section])) {
526
        return $sections[$section];
527
      }
528
    }
529
    else {
530
      return $sections;
531
    }
532
  }
533

    
534
  /**
535
   * {@inheritdoc}
536
   */
537
  public function option_definition() {
538
    $options = array(
539
      'defaults' => array(
540
        'default' => array(
541
          'access' => TRUE,
542
          'cache' => TRUE,
543
          'query' => TRUE,
544
          'title' => TRUE,
545
          'css_class' => TRUE,
546

    
547
          'display_description' => FALSE,
548
          'use_ajax' => TRUE,
549
          'hide_attachment_summary' => TRUE,
550
          'hide_admin_links' => TRUE,
551
          'pager' => TRUE,
552
          'pager_options' => TRUE,
553
          'use_more' => TRUE,
554
          'use_more_always' => TRUE,
555
          'open_new_window' => FALSE,
556
          'use_more_text' => TRUE,
557
          'exposed_form' => TRUE,
558
          'exposed_form_options' => TRUE,
559

    
560
          'link_display' => TRUE,
561
          'link_url' => '',
562
          'group_by' => TRUE,
563

    
564
          'style_plugin' => TRUE,
565
          'style_options' => TRUE,
566
          'row_plugin' => TRUE,
567
          'row_options' => TRUE,
568

    
569
          'header' => TRUE,
570
          'footer' => TRUE,
571
          'empty' => TRUE,
572

    
573
          'relationships' => TRUE,
574
          'fields' => TRUE,
575
          'sorts' => TRUE,
576
          'arguments' => TRUE,
577
          'filters' => TRUE,
578
          'filter_groups' => TRUE,
579
        ),
580
        'export' => FALSE,
581
      ),
582

    
583
      'title' => array(
584
        'default' => '',
585
        'translatable' => TRUE,
586
      ),
587
      'enabled' => array(
588
        'default' => TRUE,
589
        'translatable' => FALSE,
590
        'bool' => TRUE,
591
      ),
592
      'display_comment' => array(
593
        'default' => '',
594
      ),
595
      'css_class' => array(
596
        'default' => '',
597
        'translatable' => FALSE,
598
      ),
599
      'display_description' => array(
600
        'default' => '',
601
        'translatable' => TRUE,
602
      ),
603
      'use_ajax' => array(
604
        'default' => FALSE,
605
        'bool' => TRUE,
606
      ),
607
      'hide_attachment_summary' => array(
608
        'default' => FALSE,
609
        'bool' => TRUE,
610
      ),
611
      'hide_admin_links' => array(
612
        'default' => FALSE,
613
        'bool' => TRUE,
614
      ),
615
      // This is legacy code.
616
      // Items_per/offset/use_pager is moved to the pager plugin but the
617
      // automatic update path needs this items defined, so don't remove it.
618
      // @see views_plugin_display::init()
619
      'items_per_page' => array(
620
        'default' => 10,
621
      ),
622
      'offset' => array(
623
        'default' => 0,
624
      ),
625
      'use_pager' => array(
626
        'default' => FALSE,
627
        'bool' => TRUE,
628
      ),
629
      'use_more' => array(
630
        'default' => FALSE,
631
        'bool' => TRUE,
632
      ),
633
      'use_more_always' => array(
634
        'default' => FALSE,
635
        'bool' => TRUE,
636
        'export' => 'export_option_always',
637
      ),
638
      'use_more_text' => array(
639
        'default' => 'more',
640
        'translatable' => TRUE,
641
      ),
642
      'link_display' => array(
643
        'default' => '',
644
      ),
645
      'link_url' => array(
646
        'default' => '',
647
      ),
648
      'group_by' => array(
649
        'default' => FALSE,
650
        'bool' => TRUE,
651
      ),
652
      'field_language' => array(
653
        'default' => '***CURRENT_LANGUAGE***',
654
      ),
655
      'field_language_add_to_query' => array(
656
        'default' => 1,
657
      ),
658

    
659
      // These types are all plugins that can have individual settings and
660
      // therefore need special handling.
661
      'access' => array(
662
        'contains' => array(
663
          'type' => array(
664
            'default' => 'none',
665
            'export' => 'export_plugin',
666
            'unpack_translatable' => 'unpack_plugin',
667
          ),
668
        ),
669
      ),
670
      'cache' => array(
671
        'contains' => array(
672
          'type' => array(
673
            'default' => 'none',
674
            'export' => 'export_plugin',
675
            'unpack_translatable' => 'unpack_plugin',
676
          ),
677
        ),
678
      ),
679
      'query' => array(
680
        'contains' => array(
681
          'type' => array(
682
            'default' => 'views_query',
683
            'export' => 'export_plugin',
684
          ),
685
          'options' => array('default' => array(), 'export' => FALSE),
686
        ),
687
      ),
688
      // Note that exposed_form plugin has options in a separate array, while
689
      // access and cache do not. access and cache are legacy and that pattern
690
      // should not be repeated, but it is left as is to reduce the need to
691
      // modify older views. Let's consider the pattern used here to be the
692
      // template from which future plugins should be copied.
693
      'exposed_form' => array(
694
        'contains' => array(
695
          'type' => array(
696
            'default' => 'basic',
697
            'export' => 'export_plugin',
698
            'unpack_translatable' => 'unpack_plugin',
699
          ),
700
          'options' => array('default' => array(), 'export' => FALSE),
701
        ),
702
      ),
703
      'pager' => array(
704
        'contains' => array(
705
          'type' => array(
706
            'default' => 'full',
707
            'export' => 'export_plugin',
708
            'unpack_translatable' => 'unpack_plugin',
709
          ),
710
          'options' => array('default' => array(), 'export' => FALSE),
711
        ),
712
      ),
713

    
714
      // Note that the styles have their options completely independent. Like
715
      // access and cache above, this is a legacy pattern and should not be
716
      // repeated.
717
      'style_plugin' => array(
718
        'default' => 'default',
719
        'export' => 'export_style',
720
        'unpack_translatable' => 'unpack_style',
721
      ),
722
      'style_options' => array(
723
        'default' => array(),
724
        'export' => FALSE,
725
      ),
726
      'row_plugin' => array(
727
        'default' => 'fields',
728
        'export' => 'export_style',
729
        'unpack_translatable' => 'unpack_style',
730
      ),
731
      'row_options' => array(
732
        'default' => array(),
733
        'export' => FALSE,
734
      ),
735

    
736
      'exposed_block' => array(
737
        'default' => FALSE,
738
      ),
739

    
740
      'header' => array(
741
        'default' => array(),
742
        'export' => 'export_handler',
743
        'unpack_translatable' => 'unpack_handler',
744
      ),
745
      'footer' => array(
746
        'default' => array(),
747
        'export' => 'export_handler',
748
        'unpack_translatable' => 'unpack_handler',
749
      ),
750
      'empty' => array(
751
        'default' => array(),
752
        'export' => 'export_handler',
753
        'unpack_translatable' => 'unpack_handler',
754
      ),
755

    
756
      // We want these to export last.
757
      // These are the 5 handler types.
758
      'relationships' => array(
759
        'default' => array(),
760
        'export' => 'export_handler',
761
        'unpack_translatable' => 'unpack_handler',
762

    
763
      ),
764
      'fields' => array(
765
        'default' => array(),
766
        'export' => 'export_handler',
767
        'unpack_translatable' => 'unpack_handler',
768
      ),
769
      'sorts' => array(
770
        'default' => array(),
771
        'export' => 'export_handler',
772
        'unpack_translatable' => 'unpack_handler',
773
      ),
774
      'arguments' => array(
775
        'default' => array(),
776
        'export' => 'export_handler',
777
        'unpack_translatable' => 'unpack_handler',
778
      ),
779
      'filter_groups' => array(
780
        'contains' => array(
781
          'operator' => array('default' => 'AND'),
782
          'groups' => array('default' => array(1 => 'AND')),
783
        ),
784
      ),
785
      'filters' => array(
786
        'default' => array(),
787
        'export' => 'export_handler',
788
        'unpack_translatable' => 'unpack_handler',
789
      ),
790
    );
791

    
792
    if (empty($this->definition['use pager'])) {
793
      $options['defaults']['default']['use_pager'] = FALSE;
794
      $options['defaults']['default']['items_per_page'] = FALSE;
795
      $options['defaults']['default']['offset'] = FALSE;
796
      $options['defaults']['default']['pager'] = FALSE;
797
      $options['pager']['contains']['type']['default'] = 'some';
798
    }
799

    
800
    if ($this->is_default_display()) {
801
      unset($options['defaults']);
802
    }
803

    
804
    foreach ($this->extender as $extender) {
805
      $extender->options_definition_alter($options);
806
    }
807

    
808
    return $options;
809
  }
810

    
811
  /**
812
   * Check to see if the display has a 'path' field.
813
   *
814
   * This is a pure function and not just a setting on the definition
815
   * because some displays (such as a panel pane) may have a path based
816
   * upon configuration.
817
   *
818
   * By default, displays do not have a path.
819
   */
820
  public function has_path() {
821
    return FALSE;
822
  }
823

    
824
  /**
825
   * Check to see if the display has some need to link to another display.
826
   *
827
   * For the most part, displays without a path will use a link display.
828
   * However, sometimes displays that have a path might also need to link to
829
   * another display. This is true for feeds.
830
   */
831
  public function uses_link_display() {
832
    return !$this->has_path();
833
  }
834

    
835
  /**
836
   * Check to see if the display can put the exposed form in a block.
837
   *
838
   * By default, displays that do not have a path cannot disconnect the exposed
839
   * form and put it in a block, because the form has no place to go and Views
840
   * really wants the forms to go to a specific page.
841
   */
842
  public function uses_exposed_form_in_block() {
843
    return $this->has_path();
844
  }
845

    
846
  /**
847
   * Check to see which display to use when creating links.
848
   */
849
  public function get_link_display() {
850
    $display_id = $this->get_option('link_display');
851
    // If unknown, pick the first one.
852
    if (empty($display_id) || empty($this->view->display[$display_id])) {
853
      foreach ($this->view->display as $display_id => $display) {
854
        if (!empty($display->handler) && $display->handler->has_path()) {
855
          return $display_id;
856
        }
857
      }
858
    }
859
    else {
860
      return $display_id;
861
    }
862
    // Fall-through returns NULL.
863
  }
864

    
865
  /**
866
   * Return the base path to use for this display.
867
   *
868
   * This can be overridden for displays that do strange things with the path.
869
   */
870
  public function get_path() {
871
    if ($this->has_path()) {
872
      return $this->get_option('path');
873
    }
874

    
875
    $display_id = $this->get_link_display();
876
    if ($display_id && !empty($this->view->display[$display_id]) && is_object($this->view->display[$display_id]->handler)) {
877
      return $this->view->display[$display_id]->handler->get_path();
878
    }
879

    
880
    if ($this->get_option('link_display') == 'custom_url' && $link_url = $this->get_option('link_url')) {
881
      return $link_url;
882
    }
883
  }
884

    
885
  /**
886
   *
887
   */
888
  public function get_url() {
889
    return $this->view->get_url();
890
  }
891

    
892
  /**
893
   * Check to see if the display needs a breadcrumb.
894
   *
895
   * By default, displays do not need breadcrumbs.
896
   */
897
  public function uses_breadcrumb() {
898
    return FALSE;
899
  }
900

    
901
  /**
902
   * Determine if a given option is set to use the default or current display.
903
   *
904
   * @return
905
   *   TRUE for the default display.
906
   */
907
  public function is_defaulted($option) {
908
    return !$this->is_default_display() && !empty($this->default_display) && !empty($this->options['defaults'][$option]);
909
  }
910

    
911
  /**
912
   * Intelligently get an option either from this or default display.
913
   */
914
  public function get_option($option) {
915
    if ($this->is_defaulted($option)) {
916
      return $this->default_display->get_option($option);
917
    }
918

    
919
    if (isset($this->options[$option]) || array_key_exists($option, $this->options)) {
920
      return $this->options[$option];
921
    }
922
  }
923

    
924
  /**
925
   * Determine if the display's style uses fields.
926
   */
927
  public function uses_fields() {
928
    $plugin = $this->get_plugin();
929
    if ($plugin) {
930
      return $plugin->uses_fields();
931
    }
932
  }
933

    
934
  /**
935
   * Get the instance of a plugin, for example style or row.
936
   *
937
   * @param string $type
938
   *   The type of the plugin.
939
   * @param string $name
940
   *   The name of the plugin defined in hook_views_plugins.
941
   *
942
   * @return views_plugin|FALSE
943
   *   FALSE if no plugin, otherwise the requested instance of a plugin.
944
   */
945
  public function get_plugin($type = 'style', $name = NULL) {
946
    static $cache = array();
947
    if (!isset($cache[$type][$name])) {
948
      switch ($type) {
949
        case 'style':
950
        case 'row':
951
          $option_name = $type . '_plugin';
952
          $options = $this->get_option($type . '_options');
953
          if (!$name) {
954
            $name = $this->get_option($option_name);
955
          }
956
          break;
957

    
958
        case 'query':
959
          $views_data = views_fetch_data($this->view->base_table);
960
          $name = !empty($views_data['table']['base']['query class']) ? $views_data['table']['base']['query class'] : 'views_query';
961
        default:
962
          $option_name = $type;
963
          $options = $this->get_option($type);
964
          if (!$name) {
965
            $name = $options['type'];
966
          }
967

    
968
          // Access & cache store their options as siblings with the type; all
969
          // others use an 'options' array.
970
          if ($type != 'access' && $type != 'cache') {
971
            $options = $options['options'];
972
          }
973
      }
974
      $plugin = views_get_plugin($type, $name);
975

    
976
      if (!$plugin) {
977
        return FALSE;
978
      }
979
      if ($type != 'query') {
980
        $plugin->init($this->view, $this->display, $options);
981
      }
982
      else {
983
        $display_id = $this->is_defaulted($option_name) ? $this->display->id : 'default';
984
        $plugin->localization_keys = array($display_id, $type);
985

    
986
        if (!isset($this->base_field)) {
987
          $views_data = views_fetch_data($this->view->base_table);
988
          $this->view->base_field = !empty($views_data['table']['base']['field']) ? $views_data['table']['base']['field'] : '';
989
        }
990
        $plugin->init($this->view->base_table, $this->view->base_field, $options);
991
      }
992
      $cache[$type][$name] = $plugin;
993
    }
994

    
995
    return $cache[$type][$name];
996
  }
997

    
998
  /**
999
   * Get the handler object for a single handler.
1000
   */
1001
  public function &get_handler($type, $id) {
1002
    if (!isset($this->handlers[$type])) {
1003
      $this->get_handlers($type);
1004
    }
1005

    
1006
    if (isset($this->handlers[$type][$id])) {
1007
      return $this->handlers[$type][$id];
1008
    }
1009

    
1010
    // So we can return a reference.
1011
    $null = NULL;
1012
    return $null;
1013
  }
1014

    
1015
  /**
1016
   * Get a full array of handlers for $type. This caches them.
1017
   */
1018
  public function &get_handlers($type) {
1019
    if (!isset($this->handlers[$type])) {
1020
      $this->handlers[$type] = array();
1021
      $types = views_object_types();
1022
      $plural = $types[$type]['plural'];
1023

    
1024
      foreach ($this->get_option($plural) as $id => $info) {
1025
        // If this is during form submission and there are temporary options
1026
        // which can only appear if the view is in the edit cache, use those
1027
        // options instead. This is used for AJAX multi-step stuff.
1028
        if (isset($_POST['form_id']) && isset($this->view->temporary_options[$type][$id])) {
1029
          $info = $this->view->temporary_options[$type][$id];
1030
        }
1031

    
1032
        if ($info['id'] != $id) {
1033
          $info['id'] = $id;
1034
        }
1035

    
1036
        // If aggregation is on, the group type might override the actual
1037
        // handler that is in use. This piece of code checks that and,
1038
        // if necessary, sets the override handler.
1039
        $override = NULL;
1040
        if ($this->use_group_by() && !empty($info['group_type'])) {
1041
          if (empty($this->view->query)) {
1042
            $this->view->init_query();
1043
          }
1044
          $aggregate = $this->view->query->get_aggregation_info();
1045
          if (!empty($aggregate[$info['group_type']]['handler'][$type])) {
1046
            $override = $aggregate[$info['group_type']]['handler'][$type];
1047
          }
1048
        }
1049

    
1050
        if (!empty($types[$type]['type'])) {
1051
          $handler_type = $types[$type]['type'];
1052
        }
1053
        else {
1054
          $handler_type = $type;
1055
        }
1056

    
1057
        $handler = views_get_handler($info['table'], $info['field'], $handler_type, $override);
1058
        if ($handler) {
1059
          // Special override for area types so they know where they come from.
1060
          if ($handler_type == 'area') {
1061
            $handler->handler_type = $type;
1062
          }
1063

    
1064
          $handler->init($this->view, $info);
1065
          $this->handlers[$type][$id] = &$handler;
1066
        }
1067

    
1068
        // Prevent reference problems.
1069
        unset($handler);
1070
      }
1071
    }
1072

    
1073
    return $this->handlers[$type];
1074
  }
1075

    
1076
  /**
1077
   * List of fields for the current display with the associated relationship.
1078
   *
1079
   * @param bool $groupable_only
1080
   *   Return only an array of field labels from handler that return TRUE from
1081
   *   use_string_group_by method.
1082
   */
1083
  public function get_field_labels() {
1084
    // Use func_get_arg so the function signature isn't amended but we can still
1085
    // pass TRUE into the function to filter by groupable handlers.
1086
    $args = func_get_args();
1087
    $groupable_only = isset($args[0]) ? $args[0] : FALSE;
1088

    
1089
    $options = array();
1090
    foreach ($this->get_handlers('relationship') as $relationship => $handler) {
1091
      if ($label = $handler->label()) {
1092
        $relationships[$relationship] = $label;
1093
      }
1094
      else {
1095
        $relationships[$relationship] = $handler->ui_name();
1096
      }
1097
    }
1098

    
1099
    foreach ($this->get_handlers('field') as $id => $handler) {
1100
      if ($groupable_only && !$handler->use_string_group_by()) {
1101
        // Continue to next handler if it's not groupable.
1102
        continue;
1103
      }
1104
      if ($label = $handler->label()) {
1105
        $options[$id] = $label;
1106
      }
1107
      else {
1108
        $options[$id] = $handler->ui_name();
1109
      }
1110
      if (!empty($handler->options['relationship']) && !empty($relationships[$handler->options['relationship']])) {
1111
        $options[$id] = '(' . $relationships[$handler->options['relationship']] . ') ' . $options[$id];
1112
      }
1113
    }
1114
    return $options;
1115
  }
1116

    
1117
  /**
1118
   * Intelligently set an option either from this display or from the
1119
   * default display, if directed to do so.
1120
   */
1121
  public function set_option($option, $value) {
1122
    if ($this->is_defaulted($option)) {
1123
      return $this->default_display->set_option($option, $value);
1124
    }
1125

    
1126
    // Set this in two places: On the handler where we'll notice it but also on
1127
    // the display object so it gets saved. This should only be a temporary fix.
1128
    $this->display->display_options[$option] = $value;
1129
    return $this->options[$option] = $value;
1130
  }
1131

    
1132
  /**
1133
   * Set an option and force it to be an override.
1134
   */
1135
  public function override_option($option, $value) {
1136
    $this->set_override($option, FALSE);
1137
    $this->set_option($option, $value);
1138
  }
1139

    
1140
  /**
1141
   * Because forms may be split up into sections, this provides
1142
   * an easy URL to exactly the right section. Don't override this.
1143
   */
1144
  public function option_link($text, $section, $class = '', $title = '') {
1145
    views_add_js('ajax');
1146
    if (!empty($class)) {
1147
      $text = '<span>' . $text . '</span>';
1148
    }
1149

    
1150
    if (!trim($text)) {
1151
      $text = t('Broken field');
1152
    }
1153

    
1154
    if (empty($title)) {
1155
      $title = $text;
1156
    }
1157

    
1158
    // Truncate the path as it is displayed as a link.
1159
    if ($section == 'path') {
1160
      $text = views_ui_truncate($text, 24);
1161
    }
1162

    
1163
    return l($text,
1164
        'admin/structure/views/nojs/display/' . $this->view->name . '/' . $this->display->id . '/' . $section,
1165
        array(
1166
          'attributes' => array(
1167
            'class' => 'views-ajax-link ' . $class,
1168
            'title' => $title,
1169
            'id' => drupal_html_id('views-' . $this->display->id . '-' . $section),
1170
          ),
1171
          'html' => TRUE,
1172
        )
1173
      );
1174
  }
1175

    
1176
  /**
1177
   * Returns to tokens for arguments.
1178
   *
1179
   * This function is similar to views_handler_field::get_render_tokens() but
1180
   * without fields tokens.
1181
   */
1182
  public function get_arguments_tokens() {
1183
    $tokens = array();
1184
    if (!empty($this->view->build_info['substitutions'])) {
1185
      $tokens = $this->view->build_info['substitutions'];
1186
    }
1187
    $count = 0;
1188
    foreach ($this->view->display_handler->get_handlers('argument') as $arg => $handler) {
1189
      $token = '%' . ++$count;
1190
      if (!isset($tokens[$token])) {
1191
        $tokens[$token] = '';
1192
      }
1193

    
1194
      // Use strip tags as there should never be HTML in the path.
1195
      // However, we need to preserve special characters like " that
1196
      // were removed by check_plain().
1197
      $tokens['!' . $count] = isset($this->view->args[$count - 1]) ? strip_tags(decode_entities($this->view->args[$count - 1])) : '';
1198
    }
1199

    
1200
    return $tokens;
1201
  }
1202

    
1203
  /**
1204
   * Provide the default summary for options in the views UI.
1205
   *
1206
   * This output is returned as an array.
1207
   */
1208
  public function options_summary(&$categories, &$options) {
1209
    $categories = array(
1210
      'title' => array(
1211
        'title' => t('Title'),
1212
        'column' => 'first',
1213
      ),
1214
      'format' => array(
1215
        'title' => t('Format'),
1216
        'column' => 'first',
1217
      ),
1218
      'filters' => array(
1219
        'title' => t('Filters'),
1220
        'column' => 'first',
1221
      ),
1222
      'fields' => array(
1223
        'title' => t('Fields'),
1224
        'column' => 'first',
1225
      ),
1226
      'pager' => array(
1227
        'title' => t('Pager'),
1228
        'column' => 'second',
1229
      ),
1230
      'exposed' => array(
1231
        'title' => t('Exposed form'),
1232
        'column' => 'third',
1233
        'build' => array(
1234
          '#weight' => 1,
1235
        ),
1236
      ),
1237
      'access' => array(
1238
        'title' => '',
1239
        'column' => 'second',
1240
        'build' => array(
1241
          '#weight' => -5,
1242
        ),
1243
      ),
1244
      'other' => array(
1245
        'title' => t('Other'),
1246
        'column' => 'third',
1247
        'build' => array(
1248
          '#weight' => 2,
1249
        ),
1250
      ),
1251
    );
1252

    
1253
    if ($this->display->id != 'default') {
1254
      $options['display_id'] = array(
1255
        'category' => 'other',
1256
        'title' => t('Machine Name'),
1257
        'value' => !empty($this->display->new_id) ? check_plain($this->display->new_id) : check_plain($this->display->id),
1258
        'desc' => t('Change the machine name of this display.'),
1259
      );
1260
    }
1261

    
1262
    $display_comment = check_plain(views_ui_truncate($this->get_option('display_comment'), 80));
1263
    $options['display_comment'] = array(
1264
      'category' => 'other',
1265
      'title' => t('Comment'),
1266
      'value' => !empty($display_comment) ? $display_comment : t('No comment'),
1267
      'desc' => t('Comment or document this display.'),
1268
    );
1269

    
1270
    $title = strip_tags($this->get_option('title'));
1271
    if (!$title) {
1272
      $title = t('None');
1273
    }
1274

    
1275
    $options['title'] = array(
1276
      'category' => 'title',
1277
      'title' => t('Title'),
1278
      'value' => $title,
1279
      'desc' => t('Change the title that this display will use.'),
1280
    );
1281

    
1282
    $style_plugin = views_fetch_plugin_data('style', $this->get_option('style_plugin'));
1283
    $style_plugin_instance = $this->get_plugin('style');
1284
    $style_summary = empty($style_plugin['title']) ? t('Missing style plugin') : $style_plugin_instance->summary_title();
1285
    $style_title = empty($style_plugin['title']) ? t('Missing style plugin') : $style_plugin_instance->plugin_title();
1286

    
1287
    $style = '';
1288

    
1289
    $options['style_plugin'] = array(
1290
      'category' => 'format',
1291
      'title' => t('Format'),
1292
      'value' => $style_title,
1293
      'setting' => $style_summary,
1294
      'desc' => t('Change the way content is formatted.'),
1295
    );
1296

    
1297
    // This adds a 'Settings' link to the style_options setting if the style
1298
    // has options.
1299
    if (!empty($style_plugin['uses options'])) {
1300
      $options['style_plugin']['links']['style_options'] = t('Change settings for this format');
1301
    }
1302

    
1303
    if (!empty($style_plugin['uses row plugin'])) {
1304
      $row_plugin = views_fetch_plugin_data('row', $this->get_option('row_plugin'));
1305
      $row_plugin_instance = $this->get_plugin('row');
1306
      $row_summary = empty($row_plugin['title']) ? t('Missing style plugin') : $row_plugin_instance->summary_title();
1307
      $row_title = empty($row_plugin['title']) ? t('Missing style plugin') : $row_plugin_instance->plugin_title();
1308

    
1309
      $options['row_plugin'] = array(
1310
        'category' => 'format',
1311
        'title' => t('Show'),
1312
        'value' => $row_title,
1313
        'setting' => $row_summary,
1314
        'desc' => t('Change the way each row in the view is styled.'),
1315
      );
1316
      // This adds a 'Settings' link to the row_options setting if the row
1317
      // style has options.
1318
      if (!empty($row_plugin['uses options'])) {
1319
        $options['row_plugin']['links']['row_options'] = t('Change settings for this style');
1320
      }
1321
    }
1322
    if (!empty($this->definition['use ajax'])) {
1323
      $options['use_ajax'] = array(
1324
        'category' => 'other',
1325
        'title' => t('Use AJAX'),
1326
        'value' => $this->get_option('use_ajax') ? t('Yes') : t('No'),
1327
        'desc' => t('Change whether or not this display will use AJAX.'),
1328
      );
1329
    }
1330
    if (!empty($this->definition['accept attachments'])) {
1331
      $options['hide_attachment_summary'] = array(
1332
        'category' => 'other',
1333
        'title' => t('Hide attachments in summary'),
1334
        'value' => $this->get_option('hide_attachment_summary') ? t('Yes') : t('No'),
1335
        'desc' => t('Change whether or not to display attachments when displaying a contextual filter summary.'),
1336
      );
1337
    }
1338
    if (!isset($this->definition['contextual links locations']) || !empty($this->definition['contextual links locations'])) {
1339
      $options['hide_admin_links'] = array(
1340
        'category' => 'other',
1341
        'title' => t('Hide contextual links'),
1342
        'value' => $this->get_option('hide_admin_links') ? t('Yes') : t('No'),
1343
        'desc' => t('Change whether or not to display contextual links for this view.'),
1344
      );
1345
    }
1346

    
1347
    $pager_plugin = $this->get_plugin('pager');
1348
    if (!$pager_plugin) {
1349
      // Default to the no pager plugin.
1350
      $pager_plugin = views_get_plugin('pager', 'none');
1351
    }
1352

    
1353
    $pager_str = $pager_plugin->summary_title();
1354

    
1355
    $options['pager'] = array(
1356
      'category' => 'pager',
1357
      'title' => t('Use pager'),
1358
      'value' => $pager_plugin->plugin_title(),
1359
      'setting' => $pager_str,
1360
      'desc' => t("Change this display's pager setting."),
1361
    );
1362

    
1363
    // If pagers aren't allowed, change the text of the item.
1364
    if (empty($this->definition['use pager'])) {
1365
      $options['pager']['title'] = t('Items to display');
1366
    }
1367

    
1368
    if (!empty($pager_plugin->definition['uses options'])) {
1369
      $options['pager']['links']['pager_options'] = t('Change settings for this pager type.');
1370
    }
1371

    
1372
    if (!empty($this->definition['use more'])) {
1373
      $options['use_more'] = array(
1374
        'category' => 'pager',
1375
        'title' => t('More link'),
1376
        'value' => $this->get_option('use_more') ? t('Yes') : t('No'),
1377
        'desc' => t('Specify whether this display will provide a "more" link.'),
1378
      );
1379
    }
1380

    
1381
    $this->view->init_query();
1382
    if ($this->view->query->get_aggregation_info()) {
1383
      $options['group_by'] = array(
1384
        'category' => 'other',
1385
        'title' => t('Use aggregation'),
1386
        'value' => $this->get_option('group_by') ? t('Yes') : t('No'),
1387
        'desc' => t('Allow grouping and aggregation (calculation) of fields.'),
1388
      );
1389
    }
1390

    
1391
    $options['query'] = array(
1392
      'category' => 'other',
1393
      'title' => t('Query settings'),
1394
      'value' => t('Settings'),
1395
      'desc' => t('Allow to set some advanced settings for the query plugin'),
1396
    );
1397

    
1398
    $languages = array(
1399
      '***CURRENT_LANGUAGE***' => t("Current user's language"),
1400
      '***DEFAULT_LANGUAGE***' => t("Default site language"),
1401
      LANGUAGE_NONE => t('Language neutral'),
1402
    );
1403
    $languages = array_merge($languages, views_language_list());
1404

    
1405
    $field_language = array();
1406
    $options['field_language'] = array(
1407
      'category' => 'other',
1408
      'title' => t('Field Language'),
1409
      'value' => $languages[$this->get_option('field_language')],
1410
      'desc' => t('All fields which support translations will be displayed in the selected language.'),
1411
    );
1412

    
1413
    $access_plugin = $this->get_plugin('access');
1414
    if (!$access_plugin) {
1415
      // Default to the no access control plugin.
1416
      $access_plugin = views_get_plugin('access', 'none');
1417
    }
1418

    
1419
    $access_str = $access_plugin->summary_title();
1420

    
1421
    $options['access'] = array(
1422
      'category' => 'access',
1423
      'title' => t('Access'),
1424
      'value' => $access_plugin->plugin_title(),
1425
      'setting' => $access_str,
1426
      'desc' => t('Specify access control type for this display.'),
1427
    );
1428

    
1429
    if (!empty($access_plugin->definition['uses options'])) {
1430
      $options['access']['links']['access_options'] = t('Change settings for this access type.');
1431
    }
1432

    
1433
    $cache_plugin = $this->get_plugin('cache');
1434
    if (!$cache_plugin) {
1435
      // Default to the no cache control plugin.
1436
      $cache_plugin = views_get_plugin('cache', 'none');
1437
    }
1438

    
1439
    $cache_str = $cache_plugin->summary_title();
1440

    
1441
    $options['cache'] = array(
1442
      'category' => 'other',
1443
      'title' => t('Caching'),
1444
      'value' => $cache_plugin->plugin_title(),
1445
      'setting' => $cache_str,
1446
      'desc' => t('Specify caching type for this display.'),
1447
    );
1448

    
1449
    if (!empty($cache_plugin->definition['uses options'])) {
1450
      $options['cache']['links']['cache_options'] = t('Change settings for this caching type.');
1451
    }
1452

    
1453
    if (!empty($access_plugin->definition['uses options'])) {
1454
      $options['access']['links']['access_options'] = t('Change settings for this access type.');
1455
    }
1456

    
1457
    if ($this->uses_link_display()) {
1458
      $display_id = $this->get_link_display();
1459
      $link_display = empty($this->view->display[$display_id]) ? t('None') : check_plain($this->view->display[$display_id]->display_title);
1460
      $link_display = $this->get_option('link_display') == 'custom_url' ? t('Custom URL') : $link_display;
1461
      $options['link_display'] = array(
1462
        'category' => 'other',
1463
        'title' => t('Link display'),
1464
        'value' => $link_display,
1465
        'desc' => t('Specify which display or custom url this display will link to.'),
1466
      );
1467
    }
1468

    
1469
    if ($this->uses_exposed_form_in_block()) {
1470
      $options['exposed_block'] = array(
1471
        'category' => 'exposed',
1472
        'title' => t('Exposed form in block'),
1473
        'value' => $this->get_option('exposed_block') ? t('Yes') : t('No'),
1474
        'desc' => t('Allow the exposed form to appear in a block instead of the view.'),
1475
      );
1476
    }
1477

    
1478
    $exposed_form_plugin = $this->get_plugin('exposed_form');
1479
    if (!$exposed_form_plugin) {
1480
      // Default to the no cache control plugin.
1481
      $exposed_form_plugin = views_get_plugin('exposed_form', 'basic');
1482
    }
1483

    
1484
    $exposed_form_str = $exposed_form_plugin->summary_title();
1485

    
1486
    $options['exposed_form'] = array(
1487
      'category' => 'exposed',
1488
      'title' => t('Exposed form style'),
1489
      'value' => $exposed_form_plugin->plugin_title(),
1490
      'setting' => $exposed_form_str,
1491
      'desc' => t('Select the kind of exposed filter to use.'),
1492
    );
1493

    
1494
    if (!empty($exposed_form_plugin->definition['uses options'])) {
1495
      $options['exposed_form']['links']['exposed_form_options'] = t('Exposed form settings for this exposed form style.');
1496
    }
1497

    
1498
    $css_class = check_plain(trim($this->get_option('css_class')));
1499
    if (!$css_class) {
1500
      $css_class = t('None');
1501
    }
1502

    
1503
    $options['css_class'] = array(
1504
      'category' => 'other',
1505
      'title' => t('CSS class'),
1506
      'value' => $css_class,
1507
      'desc' => t('Change the CSS class name(s) that will be added to this display.'),
1508
    );
1509

    
1510
    $options['analyze-theme'] = array(
1511
      'category' => 'other',
1512
      'title' => t('Theme'),
1513
      'value' => t('Information'),
1514
      'desc' => t('Get information on how to theme this display'),
1515
    );
1516

    
1517
    foreach ($this->extender as $extender) {
1518
      $extender->options_summary($categories, $options);
1519
    }
1520
  }
1521

    
1522
  /**
1523
   * Provide the default form for setting options.
1524
   */
1525
  public function options_form(&$form, &$form_state) {
1526
    parent::options_form($form, $form_state);
1527
    if ($this->defaultable_sections($form_state['section'])) {
1528
      views_ui_standard_display_dropdown($form, $form_state, $form_state['section']);
1529
    }
1530
    $form['#title'] = check_plain($this->display->display_title) . ': ';
1531

    
1532
    // Set the 'section' to highlight on the form. If it's the item we're
1533
    // looking at is pulling from the default display, reflect that. Don't use
1534
    // is_defaulted since we want it to show up even on the default display.
1535
    if (!empty($this->options['defaults'][$form_state['section']])) {
1536
      $form['#section'] = 'default-' . $form_state['section'];
1537
    }
1538
    else {
1539
      $form['#section'] = $this->display->id . '-' . $form_state['section'];
1540
    }
1541

    
1542
    switch ($form_state['section']) {
1543
      case 'display_id':
1544
        $form['#title'] .= t('The machine name of this display');
1545
        $form['display_id'] = array(
1546
          '#type' => 'textfield',
1547
          '#description' => t('This is machine name of the display.'),
1548
          '#default_value' => !empty($this->display->new_id) ? $this->display->new_id : $this->display->id,
1549
          '#required' => TRUE,
1550
          '#size' => 64,
1551
        );
1552
        break;
1553

    
1554
      case 'display_title':
1555
        $form['#title'] .= t('The name and the description of this display');
1556
        $form['display_title'] = array(
1557
          '#title' => t('Name'),
1558
          '#type' => 'textfield',
1559
          '#description' => t('This name will appear only in the administrative interface for the View.'),
1560
          '#default_value' => $this->display->display_title,
1561
        );
1562
        $form['display_description'] = array(
1563
          '#title' => t('Description'),
1564
          '#type' => 'textfield',
1565
          '#description' => t('This description will appear only in the administrative interface for the View.'),
1566
          '#default_value' => $this->get_option('display_description'),
1567
        );
1568
        break;
1569

    
1570
      case 'display_comment':
1571
        $form['#title'] .= t("This display's comments");
1572
        $form['display_comment'] = array(
1573
          '#type' => 'textarea',
1574
          '#description' => t('This value will be seen and used only within the Views UI and can be used to document this display. You can use this to provide notes for other or future maintainers of your site about how or why this display is configured.'),
1575
          '#default_value' => $this->get_option('display_comment'),
1576
        );
1577
        break;
1578

    
1579
      case 'title':
1580
        $form['#title'] .= t('The title of this view');
1581
        $form['title'] = array(
1582
          '#type' => 'textfield',
1583
          '#description' => t('This title will be displayed with the view, wherever titles are normally displayed; i.e, as the page title, block title, etc. Use &lt;none&gt; to not assign a title; this can allow other modules to control the page title.'),
1584
          '#default_value' => $this->get_option('title'),
1585
        );
1586
        break;
1587

    
1588
      case 'css_class':
1589
        $form['#title'] .= t('CSS class');
1590
        $form['css_class'] = array(
1591
          '#type' => 'textfield',
1592
          '#description' => t('The CSS class names will be added to the view. This enables you to use specific CSS code for each view. You may define multiples classes separated by spaces.'),
1593
          '#default_value' => $this->get_option('css_class'),
1594
        );
1595
        break;
1596

    
1597
      case 'use_ajax':
1598
        $form['#title'] .= t('Use AJAX when available to load this view');
1599
        $form['description'] = array(
1600
          '#markup' => '<div class="description form-item">' . t('If set, this view will use an AJAX mechanism for paging, table sorting and exposed filters. This means the entire page will not refresh. It is not recommended that you use this if this view is the main content of the page as it will prevent deep linking to specific pages, but it is very useful for side content.') . '</div>',
1601
        );
1602
        $form['use_ajax'] = array(
1603
          '#type' => 'radios',
1604
          '#options' => array(1 => t('Yes'), 0 => t('No')),
1605
          '#default_value' => $this->get_option('use_ajax') ? 1 : 0,
1606
        );
1607
        break;
1608

    
1609
      case 'hide_attachment_summary':
1610
        $form['#title'] .= t('Hide attachments when displaying a contextual filter summary');
1611
        $form['hide_attachment_summary'] = array(
1612
          '#type' => 'radios',
1613
          '#options' => array(1 => t('Yes'), 0 => t('No')),
1614
          '#default_value' => $this->get_option('hide_attachment_summary') ? 1 : 0,
1615
        );
1616
        break;
1617

    
1618
      case 'hide_admin_links':
1619
        $form['#title'] .= t('Hide contextual links on this view.');
1620
        $form['hide_admin_links'] = array(
1621
          '#type' => 'radios',
1622
          '#options' => array(1 => t('Yes'), 0 => t('No')),
1623
          '#default_value' => $this->get_option('hide_admin_links') ? 1 : 0,
1624
        );
1625
        break;
1626

    
1627
      case 'use_more':
1628
        $form['#title'] .= t('Add a more link to the bottom of the display.');
1629
        $form['use_more'] = array(
1630
          '#type' => 'checkbox',
1631
          '#title' => t('Create more link'),
1632
          '#description' => t("This will add a more link to the bottom of this view, which will link to the page view. If you have more than one page view, the link will point to the display specified in 'Link display' section under advanced. You can override the url at the link display setting."),
1633
          '#default_value' => $this->get_option('use_more'),
1634
        );
1635
        $form['use_more_always'] = array(
1636
          '#type' => 'checkbox',
1637
          '#title' => t("Display 'more' link only if there is more content"),
1638
          '#description' => t("Leave this unchecked to display the 'more' link even if there are no more items to display."),
1639
          '#default_value' => !$this->get_option('use_more_always'),
1640
          '#dependency' => array(
1641
            'edit-use-more' => array(TRUE),
1642
          ),
1643
        );
1644
        $form['open_new_window'] = array(
1645
          '#type' => 'checkbox',
1646
          '#title' => t("Open 'more' link in new window (target='blank')"),
1647
          '#description' => t("Leave this unchecked to open the more link in the same window."),
1648
          '#default_value' => $this->get_option('open_new_window'),
1649
          '#dependency' => array(
1650
            'edit-use-more' => array(TRUE),
1651
          ),
1652
        );
1653
        $form['use_more_text'] = array(
1654
          '#type' => 'textfield',
1655
          '#title' => t('More link text'),
1656
          '#description' => t("The text to display for the more link."),
1657
          '#default_value' => $this->get_option('use_more_text'),
1658
          '#dependency' => array(
1659
            'edit-use-more' => array(TRUE),
1660
          ),
1661
        );
1662
        break;
1663

    
1664
      case 'group_by':
1665
        $form['#title'] .= t('Allow grouping and aggregation (calculation) of fields.');
1666
        $form['group_by'] = array(
1667
          '#type' => 'checkbox',
1668
          '#title' => t('Aggregate'),
1669
          '#description' => t('If enabled, some fields may become unavailable. All fields that are selected for grouping will be collapsed to one record per distinct value. Other fields which are selected for aggregation will have the function run on them. For example, you can group nodes on title and count the number of nids in order to get a list of duplicate titles.'),
1670
          '#default_value' => $this->get_option('group_by'),
1671
        );
1672
        break;
1673

    
1674
      case 'access':
1675
        $form['#title'] .= t('Access restrictions');
1676
        $form['access'] = array(
1677
          '#prefix' => '<div class="clearfix">',
1678
          '#suffix' => '</div>',
1679
          '#tree' => TRUE,
1680
        );
1681
        $access = $this->get_option('access');
1682
        $form['access']['type'] = array(
1683
          '#type' => 'radios',
1684
          '#options' => views_fetch_plugin_names('access', NULL, array($this->view->base_table)),
1685
          '#default_value' => $access['type'],
1686
        );
1687
        $access_plugin = views_fetch_plugin_data('access', $access['type']);
1688
        if (!empty($access_plugin['uses options'])) {
1689
          $form['markup'] = array(
1690
            '#prefix' => '<div class="form-item description">',
1691
            '#markup' => t('You may also adjust the !settings for the currently selected access restriction.', array('!settings' => $this->option_link(t('settings'), 'access_options'))),
1692
            '#suffix' => '</div>',
1693
          );
1694
        }
1695
        break;
1696

    
1697
      case 'access_options':
1698
        $access = $this->get_option('access');
1699
        $plugin = $this->get_plugin('access');
1700
        $form['#title'] .= t('Access options');
1701
        if ($plugin) {
1702
          if (!empty($plugin->definition['help topic'])) {
1703
            $form['#help_topic'] = $plugin->definition['help topic'];
1704
          }
1705
          if (!empty($plugin->definition['module'])) {
1706
            $form['#help_module'] = $plugin->definition['module'];
1707
          }
1708
          $form['access_options'] = array(
1709
            '#tree' => TRUE,
1710
          );
1711
          $form['access_options']['type'] = array(
1712
            '#type' => 'value',
1713
            '#value' => $access['type'],
1714
          );
1715
          $plugin->options_form($form['access_options'], $form_state);
1716
        }
1717
        break;
1718

    
1719
      case 'cache':
1720
        $form['#title'] .= t('Caching');
1721
        $form['cache'] = array(
1722
          '#prefix' => '<div class="clearfix">',
1723
          '#suffix' => '</div>',
1724
          '#tree' => TRUE,
1725
        );
1726
        $cache = $this->get_option('cache');
1727
        $form['cache']['type'] = array(
1728
          '#type' => 'radios',
1729
          '#options' => views_fetch_plugin_names('cache', NULL, array($this->view->base_table)),
1730
          '#default_value' => $cache['type'],
1731
        );
1732
        $cache_plugin = views_fetch_plugin_data('cache', $cache['type']);
1733
        if (!empty($cache_plugin['uses options'])) {
1734
          $form['markup'] = array(
1735
            '#prefix' => '<div class="form-item description">',
1736
            '#suffix' => '</div>',
1737
            '#markup' => t('You may also adjust the !settings for the currently selected cache mechanism.', array('!settings' => $this->option_link(t('settings'), 'cache_options'))),
1738
          );
1739
        }
1740
        break;
1741

    
1742
      case 'cache_options':
1743
        $cache = $this->get_option('cache');
1744
        $plugin = $this->get_plugin('cache');
1745
        $form['#title'] .= t('Caching options');
1746
        if ($plugin) {
1747
          if (!empty($plugin->definition['help topic'])) {
1748
            $form['#help_topic'] = $plugin->definition['help topic'];
1749
          }
1750
          if (!empty($plugin->definition['module'])) {
1751
            $form['#help_module'] = $plugin->definition['module'];
1752
          }
1753
          $form['cache_options'] = array(
1754
            '#tree' => TRUE,
1755
          );
1756
          $form['cache_options']['type'] = array(
1757
            '#type' => 'value',
1758
            '#value' => $cache['type'],
1759
          );
1760
          $plugin->options_form($form['cache_options'], $form_state);
1761
        }
1762
        break;
1763

    
1764
      case 'query':
1765
        $query_options = $this->get_option('query');
1766
        $plugin_name = $query_options['type'];
1767

    
1768
        $form['#title'] .= t('Query options');
1769
        $this->view->init_query();
1770
        if ($this->view->query) {
1771
          if (!empty($this->view->query->definition['help topic'])) {
1772
            $form['#help_topic'] = $this->view->query->definition['help topic'];
1773
          }
1774
          if (!empty($this->view->query->definition['module'])) {
1775
            $form['#help_module'] = $this->view->query->definition['module'];
1776
          }
1777
          $form['query'] = array(
1778
            '#tree' => TRUE,
1779
            'type' => array(
1780
              '#type' => 'value',
1781
              '#value' => $plugin_name,
1782
            ),
1783
            'options' => array(
1784
              '#tree' => TRUE,
1785
            ),
1786
          );
1787
          $this->view->query->options_form($form['query']['options'], $form_state);
1788
        }
1789
        break;
1790

    
1791
      case 'field_language':
1792
        $form['#title'] .= t('Field Language');
1793

    
1794
        $entities = entity_get_info();
1795
        $entity_tables = array();
1796
        $has_translation_handlers = FALSE;
1797
        foreach ($entities as $type => $entity_info) {
1798
          $entity_tables[] = $entity_info['base table'];
1799

    
1800
          if (!empty($entity_info['translation'])) {
1801
            $has_translation_handlers = TRUE;
1802
          }
1803
        }
1804

    
1805
        // Doesn't make sense to show a field setting here if we aren't querying
1806
        // an entity base table. Also, we make sure that there's at least one
1807
        // entity type with a translation handler attached.
1808
        if (in_array($this->view->base_table, $entity_tables) && $has_translation_handlers) {
1809
          $languages = array(
1810
            '***CURRENT_LANGUAGE***' => t("Current user's language"),
1811
            '***DEFAULT_LANGUAGE***' => t("Default site language"),
1812
            LANGUAGE_NONE => t('Language neutral'),
1813
          );
1814
          $languages = array_merge($languages, views_language_list());
1815

    
1816
          $form['field_language'] = array(
1817
            '#type' => 'select',
1818
            '#title' => t('Field Language'),
1819
            '#description' => t('All fields which support translations will be displayed in the selected language.'),
1820
            '#options' => $languages,
1821
            '#default_value' => $this->get_option('field_language'),
1822
          );
1823
          $form['field_language_add_to_query'] = array(
1824
            '#type' => 'checkbox',
1825
            '#title' => t('When needed, add the field language condition to the query'),
1826
            '#default_value' => $this->get_option('field_language_add_to_query'),
1827
          );
1828
        }
1829
        else {
1830
          $form['field_language']['#markup'] = t("You don't have translatable entity types.");
1831
        }
1832
        break;
1833

    
1834
      case 'style_plugin':
1835
        $form['#title'] .= t('How should this view be styled');
1836
        $form['#help_topic'] = 'style';
1837
        $form['style_plugin'] = array(
1838
          '#type' => 'radios',
1839
          '#options' => views_fetch_plugin_names('style', $this->get_style_type(), array($this->view->base_table)),
1840
          '#default_value' => $this->get_option('style_plugin'),
1841
          '#description' => t('If the style you choose has settings, be sure to click the settings button that will appear next to it in the View summary.'),
1842
        );
1843

    
1844
        $style_plugin = views_fetch_plugin_data('style', $this->get_option('style_plugin'));
1845
        if (!empty($style_plugin['uses options'])) {
1846
          $form['markup'] = array(
1847
            '#markup' => '<div class="form-item description">' . t('You may also adjust the !settings for the currently selected style.', array('!settings' => $this->option_link(t('settings'), 'style_options'))) . '</div>',
1848
          );
1849
        }
1850
        break;
1851

    
1852
      case 'style_options':
1853
        $form['#title'] .= t('Style options');
1854
        $style = TRUE;
1855
        $type = 'style_plugin';
1856
        $name = $this->get_option('style_plugin');
1857

    
1858
      case 'row_options':
1859
        if (!isset($name)) {
1860
          $name = $this->get_option('row_plugin');
1861
        }
1862
        // If row, $style will be empty.
1863
        if (empty($style)) {
1864
          $form['#title'] .= t('Row style options');
1865
          $type = 'row_plugin';
1866
        }
1867
        $plugin = $this->get_plugin(empty($style) ? 'row' : 'style');
1868
        if ($plugin) {
1869
          if (!empty($plugin->definition['help topic'])) {
1870
            $form['#help_topic'] = $plugin->definition['help topic'];
1871
          }
1872
          if (!empty($plugin->definition['module'])) {
1873
            $form['#help_module'] = $plugin->definition['module'];
1874
          }
1875
          $form[$form_state['section']] = array(
1876
            '#tree' => TRUE,
1877
          );
1878
          $plugin->options_form($form[$form_state['section']], $form_state);
1879
        }
1880
        break;
1881

    
1882
      case 'row_plugin':
1883
        $form['#title'] .= t('How should each row in this view be styled');
1884
        $form['#help_topic'] = 'style-row';
1885
        $form['row_plugin'] = array(
1886
          '#type' => 'radios',
1887
          '#options' => views_fetch_plugin_names('row', $this->get_style_type(), array($this->view->base_table)),
1888
          '#default_value' => $this->get_option('row_plugin'),
1889
        );
1890

    
1891
        $row_plugin = views_fetch_plugin_data('row', $this->get_option('row_plugin'));
1892
        if (!empty($row_plugin['uses options'])) {
1893
          $form['markup'] = array(
1894
            '#markup' => '<div class="form-item description">' . t('You may also adjust the !settings for the currently selected row style.', array('!settings' => $this->option_link(t('settings'), 'row_options'))) . '</div>',
1895
          );
1896
        }
1897
        break;
1898

    
1899
      case 'link_display':
1900
        $form['#title'] .= t('Which display to use for path');
1901
        foreach ($this->view->display as $display_id => $display) {
1902
          if ($display->handler->has_path()) {
1903
            $options[$display_id] = $display->display_title;
1904
          }
1905
        }
1906
        $options['custom_url'] = t('Custom URL');
1907
        if (count($options)) {
1908
          $form['link_display'] = array(
1909
            '#type' => 'radios',
1910
            '#options' => $options,
1911
            '#description' => t("Which display to use to get this display's path for things like summary links, rss feed links, more links, etc."),
1912
            '#default_value' => $this->get_option('link_display'),
1913
          );
1914
        }
1915

    
1916
        $options = array();
1917
        // This lets us prepare the key as we want it printed.
1918
        $count = 0;
1919
        foreach ($this->view->display_handler->get_handlers('argument') as $arg => $handler) {
1920
          $options[t('Arguments')]['%' . ++$count] = t('@argument title', array('@argument' => $handler->ui_name()));
1921
          $options[t('Arguments')]['!' . $count] = t('@argument input', array('@argument' => $handler->ui_name()));
1922
        }
1923

    
1924
        // Default text. We have some options, so make a list.
1925
        $output = '';
1926
        if (!empty($options)) {
1927
          $output = t('<p>The following tokens are available for this link.</p>');
1928
          foreach (array_keys($options) as $type) {
1929
            if (!empty($options[$type])) {
1930
              $items = array();
1931
              foreach ($options[$type] as $key => $value) {
1932
                $items[] = $key . ' == ' . $value;
1933
              }
1934
              $output .= theme('item_list',
1935
                array(
1936
                  'items' => $items,
1937
                  'type' => $type,
1938
                ));
1939
            }
1940
          }
1941
        }
1942

    
1943
        $form['link_url'] = array(
1944
          '#type' => 'textfield',
1945
          '#title' => t('Custom URL'),
1946
          '#default_value' => $this->get_option('link_url'),
1947
          '#description' => t('A Drupal path or external URL the more link will point to. Note that this will override the link display setting above.') . $output,
1948
          '#dependency' => array('radio:link_display' => array('custom_url')),
1949
        );
1950
        break;
1951

    
1952
      case 'analyze-theme':
1953
        $form['#title'] .= t('Theming information');
1954
        $form['#help_topic'] = 'analyze-theme';
1955

    
1956
        if (isset($_POST['theme'])) {
1957
          $this->theme = $_POST['theme'];
1958
        }
1959
        elseif (empty($this->theme)) {
1960
          $this->theme = variable_get('theme_default', 'bartik');
1961
        }
1962

    
1963
        if (isset($GLOBALS['theme']) && $GLOBALS['theme'] == $this->theme) {
1964
          $this->theme_registry = theme_get_registry();
1965
          $theme_engine = $GLOBALS['theme_engine'];
1966
        }
1967
        else {
1968
          $themes = list_themes();
1969
          $theme = $themes[$this->theme];
1970

    
1971
          // Find all our ancestor themes and put them in an array.
1972
          $base_theme = array();
1973
          $ancestor = $this->theme;
1974
          while ($ancestor && isset($themes[$ancestor]->base_theme)) {
1975
            $ancestor = $themes[$ancestor]->base_theme;
1976
            $base_theme[] = $themes[$ancestor];
1977
          }
1978

    
1979
          // The base themes should be initialized in the right order.
1980
          $base_theme = array_reverse($base_theme);
1981

    
1982
          // This code is copied directly from _drupal_theme_initialize().
1983
          $theme_engine = NULL;
1984

    
1985
          // Initialize the theme.
1986
          if (isset($theme->engine)) {
1987
            // Include the engine.
1988
            include_once DRUPAL_ROOT . '/' . $theme->owner;
1989

    
1990
            $theme_engine = $theme->engine;
1991
            if (function_exists($theme_engine . '_init')) {
1992
              foreach ($base_theme as $base) {
1993
                call_user_func($theme_engine . '_init', $base);
1994
              }
1995
              call_user_func($theme_engine . '_init', $theme);
1996
            }
1997
          }
1998
          else {
1999
            // Include non-engine theme files.
2000
            foreach ($base_theme as $base) {
2001
              // Include the theme file or the engine.
2002
              if (!empty($base->owner)) {
2003
                include_once DRUPAL_ROOT . '/' . $base->owner;
2004
              }
2005
            }
2006
            // And our theme gets one too.
2007
            if (!empty($theme->owner)) {
2008
              include_once DRUPAL_ROOT . '/' . $theme->owner;
2009
            }
2010
          }
2011
          $this->theme_registry = _theme_load_registry($theme, $base_theme, $theme_engine);
2012
        }
2013

    
2014
        // If there's a theme engine involved, we also need to know its
2015
        // extension so we can give the proper filename.
2016
        $this->theme_extension = '.tpl.php';
2017
        if (isset($theme_engine)) {
2018
          $extension_function = $theme_engine . '_extension';
2019
          if (function_exists($extension_function)) {
2020
            $this->theme_extension = $extension_function();
2021
          }
2022
        }
2023

    
2024
        $funcs = array();
2025
        // Get theme functions for the display. Note that some displays may not
2026
        // have themes. The 'feed' display, for example, completely delegates to
2027
        // the style.
2028
        if (!empty($this->definition['theme'])) {
2029
          $funcs[] = $this->option_link(t('Display output'), 'analyze-theme-display') . ': ' . $this->format_themes($this->theme_functions());
2030
          $themes = $this->additional_theme_functions();
2031
          if ($themes) {
2032
            foreach ($themes as $theme) {
2033
              $funcs[] = $this->option_link(t('Alternative display output'), 'analyze-theme-display') . ': ' . $this->format_themes($theme);
2034
            }
2035
          }
2036
        }
2037

    
2038
        $plugin = $this->get_plugin();
2039
        if ($plugin) {
2040
          $funcs[] = $this->option_link(t('Style output'), 'analyze-theme-style') . ': ' . $this->format_themes($plugin->theme_functions(), $plugin->additional_theme_functions());
2041
          $themes = $plugin->additional_theme_functions();
2042
          if ($themes) {
2043
            foreach ($themes as $theme) {
2044
              $funcs[] = $this->option_link(t('Alternative style'), 'analyze-theme-style') . ': ' . $this->format_themes($theme);
2045
            }
2046
          }
2047

    
2048
          if ($plugin->uses_row_plugin()) {
2049
            $row_plugin = $this->get_plugin('row');
2050
            if ($row_plugin) {
2051
              $funcs[] = $this->option_link(t('Row style output'), 'analyze-theme-row') . ': ' . $this->format_themes($row_plugin->theme_functions());
2052
              $themes = $row_plugin->additional_theme_functions();
2053
              if ($themes) {
2054
                foreach ($themes as $theme) {
2055
                  $funcs[] = $this->option_link(t('Alternative row style'), 'analyze-theme-row') . ': ' . $this->format_themes($theme);
2056
                }
2057
              }
2058
            }
2059
          }
2060

    
2061
          if ($plugin->uses_fields()) {
2062
            foreach ($this->get_handlers('field') as $id => $handler) {
2063
              $funcs[] = $this->option_link(t('Field @field (ID: @id)', array('@field' => $handler->ui_name(), '@id' => $id)), 'analyze-theme-field') . ': ' . $this->format_themes($handler->theme_functions());
2064
            }
2065
          }
2066
        }
2067

    
2068
        $form['important'] = array(
2069
          '#markup' => '<div class="form-item description"><p>' . t('This section lists all possible templates for the display plugin and for the style plugins, ordered roughly from the least specific to the most specific. The active template for each plugin -- which is the most specific template found on the system -- is highlighted in bold.') . '</p></div>',
2070
        );
2071

    
2072
        if (isset($this->view->display[$this->view->current_display]->new_id)) {
2073
          $form['important']['new_id'] = array(
2074
            '#prefix' => '<div class="description">',
2075
            '#suffix' => '</div>',
2076
            '#value' => t("<strong>Important!</strong> You have changed the display's machine name. Anything that attached to this display specifically, such as theming, may stop working until it is updated. To see theme suggestions for it, you need to save the view."),
2077
          );
2078
        }
2079

    
2080
        foreach (list_themes() as $key => $theme) {
2081
          if (!empty($theme->info['hidden'])) {
2082
            continue;
2083
          }
2084
          $options[$key] = $theme->info['name'];
2085
        }
2086

    
2087
        $form['box'] = array(
2088
          '#prefix' => '<div class="container-inline">',
2089
          '#suffix' => '</div>',
2090
        );
2091
        $form['box']['theme'] = array(
2092
          '#type' => 'select',
2093
          '#options' => $options,
2094
          '#default_value' => $this->theme,
2095
        );
2096

    
2097
        $form['box']['change'] = array(
2098
          '#type' => 'submit',
2099
          '#value' => t('Change theme'),
2100
          '#submit' => array('views_ui_edit_display_form_change_theme'),
2101
        );
2102

    
2103
        $form['analysis'] = array(
2104
          '#markup' => '<div class="form-item">' . theme('item_list', array('items' => $funcs)) . '</div>',
2105
        );
2106

    
2107
        $form['rescan_button'] = array(
2108
          '#prefix' => '<div class="form-item">',
2109
          '#suffix' => '</div>',
2110
        );
2111
        $form['rescan_button']['button'] = array(
2112
          '#type' => 'submit',
2113
          '#value' => t('Rescan template files'),
2114
          '#submit' => array('views_ui_config_item_form_rescan'),
2115
        );
2116
        $form['rescan_button']['markup'] = array(
2117
          '#markup' => '<div class="description">' . t("<strong>Important!</strong> When adding, removing, or renaming template files, it is necessary to make Drupal aware of the changes by making it rescan the files on your system. By clicking this button you clear Drupal's theme registry and thereby trigger this rescanning process. The highlighted templates above will then reflect the new state of your system.") . '</div>',
2118
        );
2119

    
2120
        $form_state['ok_button'] = TRUE;
2121
        break;
2122

    
2123
      case 'analyze-theme-display':
2124
        $form['#title'] .= t('Theming information (display)');
2125
        $output = '<p>' . t('Back to !info.', array('!info' => $this->option_link(t('theming information'), 'analyze-theme'))) . '</p>';
2126

    
2127
        if (empty($this->definition['theme'])) {
2128
          $output .= t('This display has no theming information');
2129
        }
2130
        else {
2131
          $output .= '<p>' . t('This is the default theme template used for this display.') . '</p>';
2132
          $output .= '<pre>' . check_plain(file_get_contents('./' . $this->definition['theme path'] . '/' . strtr($this->definition['theme'], '_', '-') . '.tpl.php')) . '</pre>';
2133
        }
2134

    
2135
        if (!empty($this->definition['additional themes'])) {
2136
          foreach ($this->definition['additional themes'] as $theme => $type) {
2137
            $output .= '<p>' . t('This is an alternative template for this display.') . '</p>';
2138
            $output .= '<pre>' . check_plain(file_get_contents('./' . $this->definition['theme path'] . '/' . strtr($theme, '_', '-') . '.tpl.php')) . '</pre>';
2139
          }
2140
        }
2141

    
2142
        $form['analysis'] = array(
2143
          '#markup' => '<div class="form-item">' . $output . '</div>',
2144
        );
2145

    
2146
        $form_state['ok_button'] = TRUE;
2147
        break;
2148

    
2149
      case 'analyze-theme-style':
2150
        $form['#title'] .= t('Theming information (style)');
2151
        $output = '<p>' . t('Back to !info.', array('!info' => $this->option_link(t('theming information'), 'analyze-theme'))) . '</p>';
2152

    
2153
        $plugin = $this->get_plugin();
2154

    
2155
        if (empty($plugin->definition['theme'])) {
2156
          $output .= t('This display has no style theming information');
2157
        }
2158
        else {
2159
          $output .= '<p>' . t('This is the default theme template used for this style.') . '</p>';
2160
          $output .= '<pre>' . check_plain(file_get_contents('./' . $plugin->definition['theme path'] . '/' . strtr($plugin->definition['theme'], '_', '-') . '.tpl.php')) . '</pre>';
2161
        }
2162

    
2163
        if (!empty($plugin->definition['additional themes'])) {
2164
          foreach ($plugin->definition['additional themes'] as $theme => $type) {
2165
            $output .= '<p>' . t('This is an alternative template for this style.') . '</p>';
2166
            $output .= '<pre>' . check_plain(file_get_contents('./' . $plugin->definition['theme path'] . '/' . strtr($theme, '_', '-') . '.tpl.php')) . '</pre>';
2167
          }
2168
        }
2169

    
2170
        $form['analysis'] = array(
2171
          '#markup' => '<div class="form-item">' . $output . '</div>',
2172
        );
2173

    
2174
        $form_state['ok_button'] = TRUE;
2175
        break;
2176

    
2177
      case 'analyze-theme-row':
2178
        $form['#title'] .= t('Theming information (row style)');
2179
        $output = '<p>' . t('Back to !info.', array('!info' => $this->option_link(t('theming information'), 'analyze-theme'))) . '</p>';
2180

    
2181
        $plugin = $this->get_plugin('row');
2182

    
2183
        if (empty($plugin->definition['theme'])) {
2184
          $output .= t('This display has no row style theming information');
2185
        }
2186
        else {
2187
          $output .= '<p>' . t('This is the default theme template used for this row style.') . '</p>';
2188
          $output .= '<pre>' . check_plain(file_get_contents('./' . $plugin->definition['theme path'] . '/' . strtr($plugin->definition['theme'], '_', '-') . '.tpl.php')) . '</pre>';
2189
        }
2190

    
2191
        if (!empty($plugin->definition['additional themes'])) {
2192
          foreach ($plugin->definition['additional themes'] as $theme => $type) {
2193
            $output .= '<p>' . t('This is an alternative template for this row style.') . '</p>';
2194
            $output .= '<pre>' . check_plain(file_get_contents('./' . $plugin->definition['theme path'] . '/' . strtr($theme, '_', '-') . '.tpl.php')) . '</pre>';
2195
          }
2196
        }
2197

    
2198
        $form['analysis'] = array(
2199
          '#markup' => '<div class="form-item">' . $output . '</div>',
2200
        );
2201

    
2202
        $form_state['ok_button'] = TRUE;
2203
        break;
2204

    
2205
      case 'analyze-theme-field':
2206
        $form['#title'] .= t('Theming information (row style)');
2207
        $output = '<p>' . t('Back to !info.', array('!info' => $this->option_link(t('theming information'), 'analyze-theme'))) . '</p>';
2208

    
2209
        $output .= '<p>' . t('This is the default theme template used for this row style.') . '</p>';
2210

    
2211
        // Field templates aren't registered the normal way... and they're
2212
        // always this one, anyhow.
2213
        $output .= '<pre>' . check_plain(file_get_contents(drupal_get_path('module', 'views') . '/theme/views-view-field.tpl.php')) . '</pre>';
2214

    
2215
        $form['analysis'] = array(
2216
          '#markup' => '<div class="form-item">' . $output . '</div>',
2217
        );
2218
        $form_state['ok_button'] = TRUE;
2219
        break;
2220

    
2221
      case 'exposed_block':
2222
        $form['#title'] .= t('Put the exposed form in a block');
2223
        $form['description'] = array(
2224
          '#markup' => '<div class="description form-item">' . t('If set, any exposed widgets will not appear with this view. Instead, a block will be made available to the Drupal block administration system, and the exposed form will appear there. Note that this block must be enabled manually, Views will not enable it for you.') . '</div>',
2225
        );
2226
        $form['exposed_block'] = array(
2227
          '#type' => 'radios',
2228
          '#options' => array(1 => t('Yes'), 0 => t('No')),
2229
          '#default_value' => $this->get_option('exposed_block') ? 1 : 0,
2230
        );
2231
        break;
2232

    
2233
      case 'exposed_form':
2234
        $form['#title'] .= t('Exposed Form');
2235
        $form['exposed_form'] = array(
2236
          '#prefix' => '<div class="clearfix">',
2237
          '#suffix' => '</div>',
2238
          '#tree' => TRUE,
2239
        );
2240

    
2241
        $exposed_form = $this->get_option('exposed_form');
2242
        $form['exposed_form']['type'] = array(
2243
          '#type' => 'radios',
2244
          '#options' => views_fetch_plugin_names('exposed_form', NULL, array($this->view->base_table)),
2245
          '#default_value' => $exposed_form['type'],
2246
        );
2247

    
2248
        $exposed_form_plugin = views_fetch_plugin_data('exposed_form', $exposed_form['type']);
2249
        if (!empty($exposed_form_plugin['uses options'])) {
2250
          $form['markup'] = array(
2251
            '#prefix' => '<div class="form-item description">',
2252
            '#suffix' => '</div>',
2253
            '#markup' => t('You may also adjust the !settings for the currently selected style.', array('!settings' => $this->option_link(t('settings'), 'exposed_form_options'))),
2254
          );
2255
        }
2256
        break;
2257

    
2258
      case 'exposed_form_options':
2259
        $plugin = $this->get_plugin('exposed_form');
2260
        $form['#title'] .= t('Exposed form options');
2261
        if ($plugin) {
2262
          if (!empty($plugin->definition['help topic'])) {
2263
            $form['#help_topic'] = $plugin->definition['help topic'];
2264
          }
2265
          if (!empty($plugin->definition['module'])) {
2266
            $form['#help_module'] = $plugin->definition['module'];
2267
          }
2268

    
2269
          $form['exposed_form_options'] = array(
2270
            '#tree' => TRUE,
2271
          );
2272
          $plugin->options_form($form['exposed_form_options'], $form_state);
2273
        }
2274
        break;
2275

    
2276
      case 'pager':
2277
        $form['#title'] .= t('Select which pager, if any, to use for this view');
2278
        $form['pager'] = array(
2279
          '#prefix' => '<div class="clearfix">',
2280
          '#suffix' => '</div>',
2281
          '#tree' => TRUE,
2282
        );
2283

    
2284
        $pager = $this->get_option('pager');
2285
        $form['pager']['type'] = array(
2286
          '#type' => 'radios',
2287
          '#options' => views_fetch_plugin_names('pager', empty($this->definition['use pager']) ? 'basic' : NULL, array($this->view->base_table)),
2288
          '#default_value' => $pager['type'],
2289
        );
2290

    
2291
        $pager_plugin = views_fetch_plugin_data('pager', $pager['type']);
2292
        if (!empty($pager_plugin['uses options'])) {
2293
          $form['markup'] = array(
2294
            '#prefix' => '<div class="form-item description">',
2295
            '#suffix' => '</div>',
2296
            '#markup' => t('You may also adjust the !settings for the currently selected pager.', array('!settings' => $this->option_link(t('settings'), 'pager_options'))),
2297
          );
2298
        }
2299
        break;
2300

    
2301
      case 'pager_options':
2302
        $plugin = $this->get_plugin('pager');
2303
        $form['#title'] .= t('Pager options');
2304
        if ($plugin) {
2305
          if (!empty($plugin->definition['help topic'])) {
2306
            $form['#help_topic'] = $plugin->definition['help topic'];
2307
          }
2308
          if (!empty($plugin->definition['module'])) {
2309
            $form['#help_module'] = $plugin->definition['module'];
2310
          }
2311

    
2312
          $form['pager_options'] = array(
2313
            '#tree' => TRUE,
2314
          );
2315
          $plugin->options_form($form['pager_options'], $form_state);
2316
        }
2317
        break;
2318
    }
2319

    
2320
    foreach ($this->extender as $extender) {
2321
      $extender->options_form($form, $form_state);
2322
    }
2323
  }
2324

    
2325
  /**
2326
   * Format a list of theme templates for output by the theme info helper.
2327
   */
2328
  public function format_themes($themes) {
2329
    $registry = $this->theme_registry;
2330
    $extension = $this->theme_extension;
2331

    
2332
    $output = '';
2333
    $picked = FALSE;
2334
    foreach ($themes as $theme) {
2335
      $template = strtr($theme, '_', '-') . $extension;
2336
      if (!$picked && !empty($registry[$theme])) {
2337
        $template_path = isset($registry[$theme]['path']) ? $registry[$theme]['path'] . '/' : './';
2338
        if (file_exists($template_path . $template)) {
2339
          $hint = t('File found in folder @template-path', array('@template-path' => $template_path));
2340
          $template = '<strong title="' . $hint . '">' . $template . '</strong>';
2341
        }
2342
        else {
2343
          $template = '<strong class="error">' . $template . ' ' . t('(File not found, in folder @template-path)', array('@template-path' => $template_path)) . '</strong>';
2344
        }
2345
        $picked = TRUE;
2346
      }
2347
      $fixed[] = $template;
2348
    }
2349

    
2350
    return theme('item_list', array('items' => array_reverse($fixed)));
2351
  }
2352

    
2353
  /**
2354
   * Validate the options form.
2355
   */
2356
  public function options_validate(&$form, &$form_state) {
2357
    switch ($form_state['section']) {
2358
      case 'display_title':
2359
        if (empty($form_state['values']['display_title'])) {
2360
          form_error($form['display_title'], t('Display title may not be empty.'));
2361
        }
2362
        break;
2363

    
2364
      case 'css_class':
2365
        $css_class = $form_state['values']['css_class'];
2366
        if (preg_match('/[^a-zA-Z0-9-_ ]/', $css_class)) {
2367
          form_error($form['css_class'], t('CSS classes must be alphanumeric or dashes only.'));
2368
        }
2369
        break;
2370

    
2371
      case 'display_id':
2372
        if ($form_state['values']['display_id']) {
2373
          if (preg_match('/[^a-z0-9_]/', $form_state['values']['display_id'])) {
2374
            form_error($form['display_id'], t('Display name must be letters, numbers, or underscores only.'));
2375
          }
2376

    
2377
          foreach ($this->view->display as $id => $display) {
2378
            if ($id != $this->view->current_display && ($form_state['values']['display_id'] == $id || (isset($display->new_id) && $form_state['values']['display_id'] == $display->new_id))) {
2379
              form_error($form['display_id'], t('Display id should be unique.'));
2380
            }
2381
          }
2382
        }
2383
        break;
2384

    
2385
      case 'style_options':
2386
        $style = TRUE;
2387
      case 'row_options':
2388
        // If row, $style will be empty.
2389
        $plugin = $this->get_plugin(empty($style) ? 'row' : 'style');
2390
        if ($plugin) {
2391
          $plugin->options_validate($form[$form_state['section']], $form_state);
2392
        }
2393
        break;
2394

    
2395
      case 'access_options':
2396
        $plugin = $this->get_plugin('access');
2397
        if ($plugin) {
2398
          $plugin->options_validate($form['access_options'], $form_state);
2399
        }
2400
        break;
2401

    
2402
      case 'query':
2403
        if ($this->view->query) {
2404
          $this->view->query->options_validate($form['query'], $form_state);
2405
        }
2406
        break;
2407

    
2408
      case 'cache_options':
2409
        $plugin = $this->get_plugin('cache');
2410
        if ($plugin) {
2411
          $plugin->options_validate($form['cache_options'], $form_state);
2412
        }
2413
        break;
2414

    
2415
      case 'exposed_form_options':
2416
        $plugin = $this->get_plugin('exposed_form');
2417
        if ($plugin) {
2418
          $plugin->options_validate($form['exposed_form_options'], $form_state);
2419
        }
2420
        break;
2421

    
2422
      case 'pager_options':
2423
        $plugin = $this->get_plugin('pager');
2424
        if ($plugin) {
2425
          $plugin->options_validate($form['pager_options'], $form_state);
2426
        }
2427
        break;
2428
    }
2429

    
2430
    foreach ($this->extender as $extender) {
2431
      $extender->options_validate($form, $form_state);
2432
    }
2433
  }
2434

    
2435
  /**
2436
   * Perform any necessary changes to the form values prior to storage.
2437
   *
2438
   * There is no need for this function to actually store the data.
2439
   */
2440
  public function options_submit(&$form, &$form_state) {
2441
    // Not sure I like this being here, but it seems (?) like a logical place.
2442
    $cache_plugin = $this->get_plugin('cache');
2443
    if ($cache_plugin) {
2444
      $cache_plugin->cache_flush();
2445
    }
2446

    
2447
    $section = $form_state['section'];
2448
    switch ($section) {
2449
      case 'display_id':
2450
        if (isset($form_state['values']['display_id'])) {
2451
          $this->display->new_id = $form_state['values']['display_id'];
2452
        }
2453
        break;
2454

    
2455
      case 'display_title':
2456
        $this->display->display_title = $form_state['values']['display_title'];
2457
        $this->set_option('display_description', $form_state['values']['display_description']);
2458
        break;
2459

    
2460
      case 'access':
2461
        $access = $this->get_option('access');
2462
        if ($access['type'] != $form_state['values']['access']['type']) {
2463
          $plugin = views_get_plugin('access', $form_state['values']['access']['type']);
2464
          if ($plugin) {
2465
            $access = array('type' => $form_state['values']['access']['type']);
2466
            $this->set_option('access', $access);
2467
            if (!empty($plugin->definition['uses options'])) {
2468
              views_ui_add_form_to_stack('display', $this->view, $this->display->id, array('access_options'));
2469
            }
2470
          }
2471
        }
2472
        break;
2473

    
2474
      case 'access_options':
2475
        $plugin = views_get_plugin('access', $form_state['values'][$section]['type']);
2476
        if ($plugin) {
2477
          $plugin->options_submit($form['access_options'], $form_state);
2478
          $this->set_option('access', $form_state['values'][$section]);
2479
        }
2480
        break;
2481

    
2482
      case 'cache':
2483
        $cache = $this->get_option('cache');
2484
        if ($cache['type'] != $form_state['values']['cache']['type']) {
2485
          $plugin = views_get_plugin('cache', $form_state['values']['cache']['type']);
2486
          if ($plugin) {
2487
            $cache = array('type' => $form_state['values']['cache']['type']);
2488
            $this->set_option('cache', $cache);
2489
            if (!empty($plugin->definition['uses options'])) {
2490
              views_ui_add_form_to_stack('display', $this->view, $this->display->id, array('cache_options'));
2491
            }
2492
          }
2493
        }
2494
        break;
2495

    
2496
      case 'cache_options':
2497
        $plugin = views_get_plugin('cache', $form_state['values'][$section]['type']);
2498
        if ($plugin) {
2499
          $plugin->options_submit($form['cache_options'], $form_state);
2500
          $this->set_option('cache', $form_state['values'][$section]);
2501
        }
2502
        break;
2503

    
2504
      case 'query':
2505
        $plugin = $this->get_plugin('query');
2506
        if ($plugin) {
2507
          $plugin->options_submit($form['query']['options'], $form_state);
2508
          $this->set_option('query', $form_state['values'][$section]);
2509
        }
2510
        break;
2511

    
2512
      case 'link_display':
2513
        $this->set_option('link_url', $form_state['values']['link_url']);
2514
      case 'title':
2515
      case 'css_class':
2516
      case 'display_comment':
2517
        $this->set_option($section, $form_state['values'][$section]);
2518
        break;
2519

    
2520
      case 'field_language':
2521
        $this->set_option('field_language', $form_state['values']['field_language']);
2522
        $this->set_option('field_language_add_to_query', $form_state['values']['field_language_add_to_query']);
2523
        break;
2524

    
2525
      case 'use_ajax':
2526
      case 'hide_attachment_summary':
2527
      case 'hide_admin_links':
2528
        $this->set_option($section, (bool) $form_state['values'][$section]);
2529
        break;
2530

    
2531
      case 'use_more':
2532
        $this->set_option($section, intval($form_state['values'][$section]));
2533
        $this->set_option('use_more_always', !intval($form_state['values']['use_more_always']));
2534
        $this->set_option('open_new_window', $form_state['values']['open_new_window']) == '1';
2535
        $this->set_option('use_more_text', $form_state['values']['use_more_text']);
2536
      case 'distinct':
2537
        $this->set_option($section, $form_state['values'][$section]);
2538
        break;
2539

    
2540
      case 'group_by':
2541
        $this->set_option($section, $form_state['values'][$section]);
2542
        break;
2543

    
2544
      case 'row_plugin':
2545
        // This if prevents resetting options to default if they don't change
2546
        // the plugin.
2547
        if ($this->get_option($section) != $form_state['values'][$section]) {
2548
          $plugin = views_get_plugin('row', $form_state['values'][$section]);
2549
          if ($plugin) {
2550
            $this->set_option($section, $form_state['values'][$section]);
2551
            $this->set_option('row_options', array());
2552

    
2553
            // Send ajax form to options page if we use it.
2554
            if (!empty($plugin->definition['uses options'])) {
2555
              views_ui_add_form_to_stack('display', $this->view, $this->display->id, array('row_options'));
2556
            }
2557
          }
2558
        }
2559
        break;
2560

    
2561
      case 'style_plugin':
2562
        // This if prevents resetting options to default if they don't change
2563
        // the plugin.
2564
        if ($this->get_option($section) != $form_state['values'][$section]) {
2565
          $plugin = views_get_plugin('style', $form_state['values'][$section]);
2566
          if ($plugin) {
2567
            $this->set_option($section, $form_state['values'][$section]);
2568
            $this->set_option('style_options', array());
2569
            // Send ajax form to options page if we use it.
2570
            if (!empty($plugin->definition['uses options'])) {
2571
              views_ui_add_form_to_stack('display', $this->view, $this->display->id, array('style_options'));
2572
            }
2573
          }
2574
        }
2575
        break;
2576

    
2577
      case 'style_options':
2578
        $style = TRUE;
2579
      case 'row_options':
2580
        // If row, $style will be empty.
2581
        $plugin = $this->get_plugin(empty($style) ? 'row' : 'style');
2582
        if ($plugin) {
2583
          $plugin->options_submit($form['options'][$section], $form_state);
2584
        }
2585
        $this->set_option($section, $form_state['values'][$section]);
2586
        break;
2587

    
2588
      case 'exposed_block':
2589
        $this->set_option($section, (bool) $form_state['values'][$section]);
2590
        break;
2591

    
2592
      case 'exposed_form':
2593
        $exposed_form = $this->get_option('exposed_form');
2594
        if ($exposed_form['type'] != $form_state['values']['exposed_form']['type']) {
2595
          $plugin = views_get_plugin('exposed_form', $form_state['values']['exposed_form']['type']);
2596
          if ($plugin) {
2597
            $exposed_form = array('type' => $form_state['values']['exposed_form']['type'], 'options' => array());
2598
            $this->set_option('exposed_form', $exposed_form);
2599
            if (!empty($plugin->definition['uses options'])) {
2600
              views_ui_add_form_to_stack('display', $this->view, $this->display->id, array('exposed_form_options'));
2601
            }
2602
          }
2603
        }
2604
        break;
2605

    
2606
      case 'exposed_form_options':
2607
        $plugin = $this->get_plugin('exposed_form');
2608
        if ($plugin) {
2609
          $exposed_form = $this->get_option('exposed_form');
2610
          $plugin->options_submit($form['exposed_form_options'], $form_state);
2611
          $exposed_form['options'] = $form_state['values'][$section];
2612
          $this->set_option('exposed_form', $exposed_form);
2613
        }
2614
        break;
2615

    
2616
      case 'pager':
2617
        $pager = $this->get_option('pager');
2618
        if ($pager['type'] != $form_state['values']['pager']['type']) {
2619
          $plugin = views_get_plugin('pager', $form_state['values']['pager']['type']);
2620
          if ($plugin) {
2621
            // Because pagers have very similar options, let's allow pagers to
2622
            // try to carry the options over.
2623
            $plugin->init($this->view, $this->display, $pager['options']);
2624

    
2625
            $pager = array('type' => $form_state['values']['pager']['type'], 'options' => $plugin->options);
2626
            $this->set_option('pager', $pager);
2627
            if (!empty($plugin->definition['uses options'])) {
2628
              views_ui_add_form_to_stack('display', $this->view, $this->display->id, array('pager_options'));
2629
            }
2630
          }
2631
        }
2632
        break;
2633

    
2634
      case 'pager_options':
2635
        $plugin = $this->get_plugin('pager');
2636
        if ($plugin) {
2637
          $pager = $this->get_option('pager');
2638
          $plugin->options_submit($form['pager_options'], $form_state);
2639
          $pager['options'] = $form_state['values'][$section];
2640
          $this->set_option('pager', $pager);
2641
        }
2642
        break;
2643
    }
2644

    
2645
    foreach ($this->extender as $extender) {
2646
      $extender->options_submit($form, $form_state);
2647
    }
2648
  }
2649

    
2650
  /**
2651
   * If override/revert was clicked, perform the proper toggle.
2652
   */
2653
  public function options_override($form, &$form_state) {
2654
    $this->set_override($form_state['section']);
2655
  }
2656

    
2657
  /**
2658
   * Flip the override setting for the given section.
2659
   *
2660
   * @param string $section
2661
   *   Which option should be marked as overridden, for example "filters".
2662
   * @param bool $new_state
2663
   *   Select the new state of the option.
2664
   *   - TRUE: Revert to default.
2665
   *   - FALSE: Mark it as overridden.
2666
   */
2667
  public function set_override($section, $new_state = NULL) {
2668
    $options = $this->defaultable_sections($section);
2669
    if (!$options) {
2670
      return;
2671
    }
2672

    
2673
    if (!isset($new_state)) {
2674
      $new_state = empty($this->options['defaults'][$section]);
2675
    }
2676

    
2677
    // For each option that is part of this group, fix our settings.
2678
    foreach ($options as $option) {
2679
      if ($new_state) {
2680
        // Revert to defaults.
2681
        unset($this->options[$option]);
2682
        unset($this->display->display_options[$option]);
2683
      }
2684
      else {
2685
        // Copy existing values into our display.
2686
        $this->options[$option] = $this->get_option($option);
2687
        $this->display->display_options[$option] = $this->options[$option];
2688
      }
2689
      $this->options['defaults'][$option] = $new_state;
2690
      $this->display->display_options['defaults'][$option] = $new_state;
2691
    }
2692
  }
2693

    
2694
  /**
2695
   * Inject anything into the query that the display handler needs.
2696
   */
2697
  public function query() {
2698
    foreach ($this->extender as $extender) {
2699
      $extender->query();
2700
    }
2701
  }
2702

    
2703
  /**
2704
   * Not all display plugins will support filtering.
2705
   */
2706
  public function render_filters() {
2707
  }
2708

    
2709
  /**
2710
   * Not all display plugins will suppert pager rendering.
2711
   */
2712
  public function render_pager() {
2713
    return TRUE;
2714
  }
2715

    
2716
  /**
2717
   * Render the 'more' link.
2718
   */
2719
  public function render_more_link() {
2720
    if ($this->use_more() && ($this->use_more_always() || (!empty($this->view->query->pager) && $this->view->query->pager->has_more_records()))) {
2721
      $path = $this->get_path();
2722

    
2723
      if ($this->get_option('link_display') == 'custom_url' && $override_path = $this->get_option('link_url')) {
2724
        $tokens = $this->get_arguments_tokens();
2725
        $path = strtr($override_path, $tokens);
2726
      }
2727

    
2728
      if ($path) {
2729
        if (empty($override_path)) {
2730
          $path = $this->view->get_url(NULL, $path);
2731
        }
2732
        $url_options = array();
2733
        if (!empty($this->view->exposed_raw_input)) {
2734
          $url_options['query'] = $this->view->exposed_raw_input;
2735
        }
2736
        $theme = views_theme_functions('views_more', $this->view, $this->display);
2737

    
2738
        $parsed_url = drupal_parse_url($path);
2739
        // Preserve the query string from url.
2740
        if (!empty($parsed_url['query'])) {
2741
          if (!empty($url_options['query'])) {
2742
            $url_options['query'] = array_merge($parsed_url['query'], $url_options['query']);
2743
          }
2744
          else {
2745
            $url_options['query'] = $parsed_url['query'];
2746
          }
2747
          $path = $parsed_url['path'];
2748
        }
2749
        // Add fragment if applicable.
2750
        if (!empty($parsed_url['fragment'])) {
2751
          $url_options['fragment'] = $parsed_url['fragment'];
2752
        }
2753

    
2754
        $path = check_url(url($path, $url_options));
2755

    
2756
        return theme($theme, array(
2757
          'more_url' => $path,
2758
          'new_window' => $this->use_more_open_new_window(),
2759
          'link_text' => check_plain($this->use_more_text()),
2760
          'view' => $this->view,
2761
        ));
2762
      }
2763
    }
2764
  }
2765

    
2766
  /**
2767
   * Legacy functions.
2768
   */
2769

    
2770
  /**
2771
   * Render the header of the view.
2772
   */
2773
  public function render_header() {
2774
    $empty = !empty($this->view->result);
2775
    return $this->render_area('header', $empty);
2776
  }
2777

    
2778
  /**
2779
   * Render the footer of the view.
2780
   */
2781
  public function render_footer() {
2782
    $empty = !empty($this->view->result);
2783
    return $this->render_area('footer', $empty);
2784
  }
2785

    
2786
  /**
2787
   *
2788
   */
2789
  public function render_empty() {
2790
    return $this->render_area('empty');
2791
  }
2792

    
2793
  /**
2794
   * If this display creates a block, implement one of these.
2795
   */
2796
  public function hook_block_list($delta = 0, $edit = array()) {
2797
    return array();
2798
  }
2799

    
2800
  /**
2801
   * If this display creates a page with a menu item, implement it here.
2802
   */
2803
  public function hook_menu() {
2804
    return array();
2805
  }
2806

    
2807
  /**
2808
   * Render this display.
2809
   */
2810
  public function render() {
2811
    return theme($this->theme_functions(), array('view' => $this->view));
2812
  }
2813

    
2814
  /**
2815
   *
2816
   */
2817
  public function render_area($area, $empty = FALSE) {
2818
    $return = '';
2819
    foreach ($this->get_handlers($area) as $area) {
2820
      $return .= $area->render($empty);
2821
    }
2822
    return $return;
2823
  }
2824

    
2825
  /**
2826
   * Determine if the user has access to this display of the view.
2827
   */
2828
  public function access($account = NULL) {
2829
    if (!isset($account)) {
2830
      global $user;
2831
      $account = $user;
2832
    }
2833

    
2834
    // Full override.
2835
    if (user_access('access all views', $account)) {
2836
      return TRUE;
2837
    }
2838

    
2839
    $plugin = $this->get_plugin('access');
2840
    if ($plugin) {
2841
      return $plugin->access($account);
2842
    }
2843

    
2844
    // Fallback to all access if no plugin.
2845
    return TRUE;
2846
  }
2847

    
2848
  /**
2849
   * Set up any variables on the view prior to execution.
2850
   *
2851
   * These are separated from execute because they are extremely common and
2852
   * unlikely to be overridden on an individual display.
2853
   */
2854
  public function pre_execute() {
2855
    $this->view->set_use_ajax($this->use_ajax());
2856
    if ($this->use_more() && !$this->use_more_always()) {
2857
      $this->view->get_total_rows = TRUE;
2858
    }
2859
    $this->view->init_handlers();
2860
    if ($this->uses_exposed()) {
2861
      $exposed_form = $this->get_plugin('exposed_form');
2862
      $exposed_form->pre_execute();
2863
    }
2864

    
2865
    foreach ($this->extender as $extender) {
2866
      $extender->pre_execute();
2867
    }
2868

    
2869
    if ($this->get_option('hide_admin_links')) {
2870
      $this->view->hide_admin_links = TRUE;
2871
    }
2872
  }
2873

    
2874
  /**
2875
   * When used externally, this is how a view gets run and returns
2876
   * data in the format required.
2877
   *
2878
   * The base class cannot be executed.
2879
   */
2880
  public function execute() {
2881
  }
2882

    
2883
  /**
2884
   * Fully render the display.
2885
   *
2886
   * Used for the purposes of a live preview or some other AJAXy reason.
2887
   */
2888
  public function preview() {
2889
    return $this->view->render();
2890
  }
2891

    
2892
  /**
2893
   * Displays can require a certain type of style plugin.
2894
   *
2895
   * By default, they will be 'normal'.
2896
   */
2897
  public function get_style_type() {
2898
    return 'normal';
2899
  }
2900

    
2901
  /**
2902
   * Make sure the display and all associated handlers are valid.
2903
   *
2904
   * @return
2905
   *   Empty array if the display is valid; an array of error strings if it is
2906
   *   not.
2907
   */
2908
  public function validate() {
2909
    $errors = array();
2910
    // Make sure displays that use fields HAVE fields.
2911
    if ($this->uses_fields()) {
2912
      $fields = FALSE;
2913
      foreach ($this->get_handlers('field') as $field) {
2914
        if (empty($field->options['exclude'])) {
2915
          $fields = TRUE;
2916
        }
2917
      }
2918

    
2919
      if (!$fields) {
2920
        $errors[] = t('Display "@display" uses fields but there are none defined for it or all are excluded.', array('@display' => $this->display->display_title));
2921
      }
2922
    }
2923

    
2924
    if ($this->has_path() && !$this->get_option('path')) {
2925
      $errors[] = t('Display "@display" uses a path but the path is undefined.', array('@display' => $this->display->display_title));
2926
    }
2927

    
2928
    // Validate style plugin.
2929
    $style = $this->get_plugin();
2930
    if (empty($style)) {
2931
      $errors[] = t('Display "@display" has an invalid style plugin.', array('@display' => $this->display->display_title));
2932
    }
2933
    else {
2934
      $result = $style->validate();
2935
      if (!empty($result) && is_array($result)) {
2936
        $errors = array_merge($errors, $result);
2937
      }
2938
    }
2939

    
2940
    // Validate query plugin.
2941
    $query = $this->get_plugin('query');
2942
    $result = $query->validate();
2943
    if (!empty($result) && is_array($result)) {
2944
      $errors = array_merge($errors, $result);
2945
    }
2946

    
2947
    // Validate handlers.
2948
    foreach (views_object_types() as $type => $info) {
2949
      foreach ($this->get_handlers($type) as $handler) {
2950
        $result = $handler->validate();
2951
        if (!empty($result) && is_array($result)) {
2952
          $errors = array_merge($errors, $result);
2953
        }
2954
      }
2955
    }
2956

    
2957
    return $errors;
2958
  }
2959

    
2960
  /**
2961
   * Check if the provided identifier is unique.
2962
   *
2963
   * @param string $id
2964
   *   The id of the handler which is checked.
2965
   * @param string $identifier
2966
   *   The actual get identifier configured in the exposed settings.
2967
   *
2968
   * @return bool
2969
   *   Returns whether the identifier is unique on all handlers.
2970
   */
2971
  public function is_identifier_unique($id, $identifier) {
2972
    foreach (views_object_types() as $type => $info) {
2973
      foreach ($this->get_handlers($type) as $key => $handler) {
2974
        if ($handler->can_expose() && $handler->is_exposed()) {
2975
          if ($handler->is_a_group()) {
2976
            if ($id != $key && $identifier == $handler->options['group_info']['identifier']) {
2977
              return FALSE;
2978
            }
2979
          }
2980
          else {
2981
            if ($id != $key && isset($handler->options['expose']['identifier']) && $identifier == $handler->options['expose']['identifier']) {
2982
              return FALSE;
2983
            }
2984
          }
2985
        }
2986
      }
2987
    }
2988
    return TRUE;
2989
  }
2990

    
2991
  /**
2992
   * Provide the block system with any exposed widget blocks for this display.
2993
   */
2994
  public function get_special_blocks() {
2995
    $blocks = array();
2996

    
2997
    if ($this->uses_exposed_form_in_block()) {
2998
      $delta = '-exp-' . $this->view->name . '-' . $this->display->id;
2999
      $desc = t('Exposed form: @view-@display_id', array('@view' => $this->view->name, '@display_id' => $this->display->id));
3000

    
3001
      $blocks[$delta] = array(
3002
        'info' => $desc,
3003
        'cache' => DRUPAL_NO_CACHE,
3004
      );
3005
    }
3006

    
3007
    return $blocks;
3008
  }
3009

    
3010
  /**
3011
   * Render any special blocks provided for this display.
3012
   */
3013
  public function view_special_blocks($type) {
3014
    if ($type == '-exp') {
3015
      // Avoid interfering with the admin forms.
3016
      if (arg(0) == 'admin' && arg(1) == 'structure' && arg(2) == 'views') {
3017
        return;
3018
      }
3019
      $this->view->init_handlers();
3020

    
3021
      if ($this->uses_exposed() && $this->get_option('exposed_block')) {
3022
        $exposed_form = $this->get_plugin('exposed_form');
3023
        return array(
3024
          'content' => $exposed_form->render_exposed_form(TRUE),
3025
        );
3026
      }
3027
    }
3028
  }
3029

    
3030
  /**
3031
   * Override of export_option()
3032
   *
3033
   * Because displays do not want to export options that are NOT overridden from
3034
   * the default display, we need some special handling during the export
3035
   * process.
3036
   */
3037
  public function export_option($indent, $prefix, $storage, $option, $definition, $parents) {
3038
    // The $prefix is wrong because we store our actual options a little
3039
    // differently.
3040
    $prefix = '$handler->display->display_options';
3041
    $output = '';
3042
    if (!$parents && !$this->is_default_display()) {
3043
      // Do not export items that are not overridden.
3044
      if ($this->is_defaulted($option)) {
3045
        return;
3046
      }
3047

    
3048
      // If this is not defaulted and is overrideable, flip the switch to say
3049
      // this is overridden.
3050
      if ($this->defaultable_sections($option)) {
3051
        $output .= $indent . $prefix . "['defaults']['$option'] = FALSE;\n";
3052
      }
3053
    }
3054

    
3055
    $output .= parent::export_option($indent, $prefix, $storage, $option, $definition, $parents);
3056
    return $output;
3057
  }
3058

    
3059
  /**
3060
   * Special method to export items that have handlers.
3061
   *
3062
   * This method was specified in the option_definition() as the method to
3063
   * utilize to export fields, filters, sort criteria, relationships and
3064
   * arguments. This passes the export off to the individual handlers so that
3065
   * they can export themselves properly.
3066
   */
3067
  public function export_handler($indent, $prefix, $storage, $option, $definition, $parents) {
3068
    $output = '';
3069

    
3070
    // Cut the 's' off because the data is stored as the plural form but we need
3071
    // the singular form.
3072
    if ($option != 'header' && $option != 'footer' && $option != 'empty') {
3073
      $type = substr($option, 0, -1);
3074
    }
3075
    else {
3076
      $type = $option;
3077
    }
3078
    $types = views_object_types();
3079
    foreach ($storage[$option] as $id => $info) {
3080
      if (!empty($types[$type]['type'])) {
3081
        $handler_type = $types[$type]['type'];
3082
      }
3083
      else {
3084
        $handler_type = $type;
3085
      }
3086
      // If aggregation is on, the group type might override the actual handler
3087
      // that is in use. This piece of code checks that and, if necessary, sets
3088
      // the override handler.
3089
      $override = NULL;
3090
      if ($this->use_group_by() && !empty($info['group_type'])) {
3091
        if (empty($this->view->query)) {
3092
          $this->view->init_query();
3093
        }
3094
        $aggregate = $this->view->query->get_aggregation_info();
3095
        if (!empty($aggregate[$info['group_type']]['handler'][$type])) {
3096
          $override = $aggregate[$info['group_type']]['handler'][$type];
3097
        }
3098
      }
3099
      $handler = views_get_handler($info['table'], $info['field'], $handler_type, $override);
3100
      if ($handler) {
3101
        $handler->init($this->view, $info);
3102
        $output .= $indent . '/* ' . $types[$type]['stitle'] . ': ' . $handler->ui_name() . " */\n";
3103
        $output .= $handler->export_options($indent, $prefix . "['$option']['$id']");
3104
      }
3105

    
3106
      // Prevent reference problems.
3107
      unset($handler);
3108
    }
3109

    
3110
    return $output;
3111
  }
3112

    
3113
  /**
3114
   * Special handling for the style export.
3115
   *
3116
   * Styles are stored as style_plugin and style_options or row_plugin and
3117
   * row_options accordingly. The options are told not to export, and the export
3118
   * for the plugin should export both.
3119
   */
3120
  public function export_style($indent, $prefix, $storage, $option, $definition, $parents) {
3121
    $output = '';
3122
    $style_plugin = $this->get_plugin();
3123
    if ($option == 'style_plugin') {
3124
      $type = 'style';
3125
      $options_field = 'style_options';
3126
      $plugin = $style_plugin;
3127
    }
3128
    else {
3129
      if (!$style_plugin || !$style_plugin->uses_row_plugin()) {
3130
        return;
3131
      }
3132

    
3133
      $type = 'row';
3134
      $options_field = 'row_options';
3135
      $plugin = $this->get_plugin('row');
3136
      // If the style plugin doesn't use row plugins, don't even bother.
3137
    }
3138

    
3139
    if ($plugin) {
3140
      // Write which plugin to use.
3141
      $value = $this->get_option($option);
3142
      $output .= $indent . $prefix . "['$option'] = '$value';\n";
3143

    
3144
      // Pass off to the plugin to export itself.
3145
      $output .= $plugin->export_options($indent, $prefix . "['$options_field']");
3146
    }
3147

    
3148
    return $output;
3149
  }
3150

    
3151
  /**
3152
   * Special handling for plugin export.
3153
   *
3154
   * Plugins other than styles are stored in array with 'type' being the key
3155
   * to the plugin. For modern plugins, the options are stored in the 'options'
3156
   * array, but for legacy plugins (access and cache) options are stored as
3157
   * siblings to the type.
3158
   */
3159
  public function export_plugin($indent, $prefix, $storage, $option, $definition, $parents) {
3160
    $output = '';
3161
    $plugin_type = end($parents);
3162
    $plugin = $this->get_plugin($plugin_type);
3163
    if ($plugin) {
3164
      // Write which plugin to use.
3165
      $value = $storage[$option];
3166
      $new_prefix = $prefix . "['$plugin_type']";
3167

    
3168
      $output .= $indent . $new_prefix . "['$option'] = '$value';\n";
3169

    
3170
      if ($plugin_type != 'access' && $plugin_type != 'cache') {
3171
        $new_prefix .= "['options']";
3172
      }
3173

    
3174
      // Pass off to the plugin to export itself.
3175
      $output .= $plugin->export_options($indent, $new_prefix);
3176
    }
3177

    
3178
    return $output;
3179
  }
3180

    
3181
  /**
3182
   *
3183
   */
3184
  public function unpack_style($indent, $prefix, $storage, $option, $definition, $parents) {
3185
    $output = '';
3186
    $style_plugin = $this->get_plugin();
3187
    if ($option == 'style_plugin') {
3188
      $type = 'style';
3189
      $options_field = 'style_options';
3190
      $plugin = $style_plugin;
3191
    }
3192
    else {
3193
      if (!$style_plugin || !$style_plugin->uses_row_plugin()) {
3194
        return;
3195
      }
3196

    
3197
      $type = 'row';
3198
      $options_field = 'row_options';
3199
      $plugin = $this->get_plugin('row');
3200
      // If the style plugin doesn't use row plugins, don't even bother.
3201
    }
3202

    
3203
    if ($plugin) {
3204
      return $plugin->unpack_translatables($translatable, $parents);
3205
    }
3206
  }
3207

    
3208
  /**
3209
   * Special handling for plugin unpacking.
3210
   */
3211
  public function unpack_plugin(&$translatable, $storage, $option, $definition, $parents) {
3212
    $plugin_type = end($parents);
3213
    $plugin = $this->get_plugin($plugin_type);
3214
    if ($plugin) {
3215
      // Write which plugin to use.
3216
      return $plugin->unpack_translatables($translatable, $parents);
3217
    }
3218
  }
3219

    
3220
  /**
3221
   * Special method to unpack items that have handlers.
3222
   *
3223
   * This method was specified in the option_definition() as the method to
3224
   * utilize to export fields, filters, sort criteria, relationships and
3225
   * arguments. This passes the export off to the individual handlers so that
3226
   * they can export themselves properly.
3227
   */
3228
  public function unpack_handler(&$translatable, $storage, $option, $definition, $parents) {
3229
    $output = '';
3230

    
3231
    // Cut the 's' off because the data is stored as the plural form but we need
3232
    // the singular form.
3233
    if ($option != 'header' && $option != 'footer' && $option != 'empty') {
3234
      $type = substr($option, 0, -1);
3235
    }
3236
    else {
3237
      $type = $option;
3238
    }
3239
    $types = views_object_types();
3240
    foreach ($storage[$option] as $id => $info) {
3241
      if (!empty($types[$type]['type'])) {
3242
        $handler_type = $types[$type]['type'];
3243
      }
3244
      else {
3245
        $handler_type = $type;
3246
      }
3247
      $handler = views_get_handler($info['table'], $info['field'], $handler_type);
3248
      if ($handler) {
3249
        $handler->init($this->view, $info);
3250
        $items = array_merge($parents, array($type, $info['table'], $info['id']));
3251
        $handler->unpack_translatables($translatable, $items);
3252
      }
3253

    
3254
      // Prevent reference problems.
3255
      unset($handler);
3256
    }
3257

    
3258
    return $output;
3259
  }
3260

    
3261
  /**
3262
   * Provide some helpful text for the arguments.
3263
   *
3264
   * The result should contain of an array with:
3265
   * - filter value present: The title of the fieldset in the argument where
3266
   *   you can configure what should be done with a given argument.
3267
   * - filter value not present: The tiel of the fieldset in the argument where
3268
   *   you can configure what should be done if the argument does not exist.
3269
   * - description: A description about how arguments comes to the display. For
3270
   *   example blocks don't get it from url.
3271
   */
3272
  public function get_argument_text() {
3273
    return array(
3274
      'filter value not present' => t('When the filter value is <em>NOT</em> available'),
3275
      'filter value present' => t('When the filter value <em>IS</em> available or a default is provided'),
3276
      'description' => t("This display does not have a source for contextual filters, so no contextual filter value will be available unless you select 'Provide default'."),
3277
    );
3278
  }
3279

    
3280
  /**
3281
   * Provide some helpful text for pagers.
3282
   *
3283
   * The result should contain of an array with:
3284
   * - items per page title.
3285
   * - items per page description.
3286
   */
3287
  public function get_pager_text() {
3288
    return array(
3289
      'items per page title' => t('Items to display'),
3290
      'items per page description' => t('The number of items to display. Enter 0 for no limit.'),
3291
    );
3292
  }
3293

    
3294
}
3295

    
3296
/**
3297
 * @}
3298
 */