Projet

Général

Profil

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

root / drupal7 / sites / all / modules / views / plugins / views_plugin_display.inc @ 5d12d676

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
   *
36
   */
37
  public $handlers = array();
38

    
39
  /**
40
   * Stores all available display extenders.
41
   */
42
  public $extender = array();
43

    
44
  /**
45
   * {@inheritdoc}
46
   */
47
  public function init(&$view, &$display, $options = NULL) {
48
    $this->view = &$view;
49
    $this->display = &$display;
50

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

    
68
    // Track changes that the user should know about.
69
    $changed = FALSE;
70

    
71
    // Make some modifications.
72
    if (!isset($options) && isset($display->display_options)) {
73
      $options = $display->display_options;
74
    }
75

    
76
    if ($this->is_default_display() && isset($options['defaults'])) {
77
      unset($options['defaults']);
78
    }
79

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

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

    
130
      // Setup the pager options.
131
      $pager = array(
132
        'type' => $type,
133
        'options' => array(
134
          'offset' => intval($offset)
135
        ),
136
      );
137

    
138
      if ($items_per_page) {
139
        $pager['options']['items_per_page'] = $items_per_page;
140
      }
141
      // Setup the pager element.
142
      if ($id = $this->get_option('pager_element')) {
143
        $pager['options']['id'] = $id;
144
      }
145

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

    
155

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

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

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

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

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

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

    
255
      $changed = TRUE;
256
    }
257

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

    
264
  /**
265
   * {@inheritdoc}
266
   */
267
  public function destroy() {
268
    parent::destroy();
269

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

    
278
    if (isset($this->default_display)) {
279
      unset($this->default_display);
280
    }
281

    
282
    foreach ($this->extender as $extender) {
283
      $extender->destroy();
284
    }
285
  }
286

    
287
  /**
288
   * Determine if this display is the 'default' display which contains
289
   * fallback settings
290
   */
291
  public function is_default_display() {
292
    return FALSE;
293
  }
294

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

    
318
    return $this->has_exposed;
319
  }
320

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

    
334
  /**
335
   * Does the display use AJAX?
336
   */
337
  public function use_ajax() {
338
    if (!empty($this->definition['use ajax'])) {
339
      return $this->get_option('use_ajax');
340
    }
341
    return FALSE;
342
  }
343

    
344
  /**
345
   * Does the display have a pager enabled?
346
   */
347
  public function use_pager() {
348
    $pager = $this->get_plugin('pager');
349
    if ($pager) {
350
      return $pager->use_pager();
351
    }
352
  }
353

    
354
  /**
355
   * Does the display have a more link enabled?
356
   */
357
  public function use_more() {
358
    if (!empty($this->definition['use more'])) {
359
      return $this->get_option('use_more');
360
    }
361
    return FALSE;
362
  }
363

    
364
  /**
365
   * Does the display have groupby enabled?
366
   */
367
  public function use_group_by() {
368
    return $this->get_option('group_by');
369
  }
370

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

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

    
391
  /**
392
   * Does the display have custom link text?
393
   */
394
  public function use_more_text() {
395
    if (!empty($this->definition['use more'])) {
396
      return $this->get_option('use_more_text');
397
    }
398
    return FALSE;
399
  }
400

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

    
418
  /**
419
   * Allow displays to attach to other views.
420
   */
421
  public function attach_to($display_id) {
422
  }
423

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

    
466
      // Force these to cascade properly.
467
      'style_plugin' => array('style_plugin', 'style_options', 'row_plugin', 'row_options'),
468
      'style_options' => array('style_plugin', 'style_options', 'row_plugin', 'row_options'),
469
      'row_plugin' => array('style_plugin', 'style_options', 'row_plugin', 'row_options'),
470
      'row_options' => array('style_plugin', 'style_options', 'row_plugin', 'row_options'),
471

    
472
      'pager' => array('pager', 'pager_options'),
473
      'pager_options' => array('pager', 'pager_options'),
474

    
475
      'exposed_form' => array('exposed_form', 'exposed_form_options'),
476
      'exposed_form_options' => array('exposed_form', 'exposed_form_options'),
477

    
478
      // These guys are special
479
      'header' => array('header'),
480
      'footer' => array('footer'),
481
      'empty' => array('empty'),
482
      'relationships' => array('relationships'),
483
      'fields' => array('fields'),
484
      'sorts' => array('sorts'),
485
      'arguments' => array('arguments'),
486
      'filters' => array('filters', 'filter_groups'),
487
      'filter_groups' => array('filters', 'filter_groups'),
488
    );
489

    
490
    // If the display cannot use a pager, then we cannot default it.
491
    if (empty($this->definition['use pager'])) {
492
      unset($sections['pager']);
493
      unset($sections['items_per_page']);
494
    }
495

    
496
    foreach ($this->extender as $extender) {
497
      $extender->defaultable_sections($sections, $section);
498
    }
499

    
500
    if ($section) {
501
      if (!empty($sections[$section])) {
502
        return $sections[$section];
503
      }
504
    }
505
    else {
506
      return $sections;
507
    }
508
  }
509

    
510
  /**
511
   * {@inheritdoc}
512
   */
513
  public function option_definition() {
514
    $options = array(
515
      'defaults' => array(
516
        'default' => array(
517
          'access' => TRUE,
518
          'cache' => TRUE,
519
          'query' => TRUE,
520
          'title' => TRUE,
521
          'css_class' => TRUE,
522

    
523
          'display_description' => FALSE,
524
          'use_ajax' => TRUE,
525
          'hide_attachment_summary' => TRUE,
526
          'hide_admin_links' => TRUE,
527
          'pager' => TRUE,
528
          'pager_options' => TRUE,
529
          'use_more' => TRUE,
530
          'use_more_always' => TRUE,
531
          'open_new_window' => FALSE,
532
          'use_more_text' => TRUE,
533
          'exposed_form' => TRUE,
534
          'exposed_form_options' => TRUE,
535

    
536
          'link_display' => TRUE,
537
          'link_url' => '',
538
          'group_by' => TRUE,
539

    
540
          'style_plugin' => TRUE,
541
          'style_options' => TRUE,
542
          'row_plugin' => TRUE,
543
          'row_options' => TRUE,
544

    
545
          'header' => TRUE,
546
          'footer' => TRUE,
547
          'empty' => TRUE,
548

    
549
          'relationships' => TRUE,
550
          'fields' => TRUE,
551
          'sorts' => TRUE,
552
          'arguments' => TRUE,
553
          'filters' => TRUE,
554
          'filter_groups' => TRUE,
555
        ),
556
        'export' => FALSE,
557
      ),
558

    
559
      'title' => array(
560
        'default' => '',
561
        'translatable' => TRUE,
562
      ),
563
      'enabled' => array(
564
        'default' => TRUE,
565
        'translatable' => FALSE,
566
        'bool' => TRUE,
567
      ),
568
      'display_comment' => array(
569
        'default' => '',
570
      ),
571
      'css_class' => array(
572
        'default' => '',
573
        'translatable' => FALSE,
574
      ),
575
      'display_description' => array(
576
        'default' => '',
577
        'translatable' => TRUE,
578
      ),
579
      'use_ajax' => array(
580
        'default' => FALSE,
581
        'bool' => TRUE,
582
      ),
583
      'hide_attachment_summary' => array(
584
        'default' => FALSE,
585
        'bool' => TRUE,
586
      ),
587
      'hide_admin_links' => array(
588
        'default' => FALSE,
589
        'bool' => TRUE,
590
      ),
591
      // This is legacy code.
592
      // Items_per/offset/use_pager is moved to the pager plugin but the
593
      // automatic update path needs this items defined, so don't remove it.
594
      // @see views_plugin_display::init()
595
      'items_per_page' => array(
596
        'default' => 10,
597
      ),
598
      'offset' => array(
599
        'default' => 0,
600
      ),
601
      'use_pager' => array(
602
        'default' => FALSE,
603
        'bool' => TRUE,
604
      ),
605
      'use_more' => array(
606
        'default' => FALSE,
607
        'bool' => TRUE,
608
      ),
609
      'use_more_always' => array(
610
        'default' => FALSE,
611
        'bool' => TRUE,
612
        'export' => 'export_option_always',
613
      ),
614
      'use_more_text' => array(
615
        'default' => 'more',
616
        'translatable' => TRUE,
617
      ),
618
      'link_display' => array(
619
        'default' => '',
620
      ),
621
      'link_url' => array(
622
        'default' => '',
623
      ),
624
      'group_by' => array(
625
        'default' => FALSE,
626
        'bool' => TRUE,
627
      ),
628
      'field_language' => array(
629
        'default' => '***CURRENT_LANGUAGE***',
630
      ),
631
      'field_language_add_to_query' => array(
632
        'default' => 1,
633
      ),
634

    
635
      // These types are all plugins that can have individual settings and
636
      // therefore need special handling.
637
      'access' => array(
638
        'contains' => array(
639
          'type' => array('default' => 'none', 'export' => 'export_plugin', 'unpack_translatable' => 'unpack_plugin'),
640
         ),
641
      ),
642
      'cache' => array(
643
        'contains' => array(
644
          'type' => array('default' => 'none', 'export' => 'export_plugin', 'unpack_translatable' => 'unpack_plugin'),
645
         ),
646
      ),
647
      'query' => array(
648
        'contains' => array(
649
          'type' => array('default' => 'views_query', 'export' => 'export_plugin'),
650
          'options' => array('default' => array(), 'export' => FALSE),
651
         ),
652
      ),
653
      // Note that exposed_form plugin has options in a separate array,
654
      // while access and cache do not. access and cache are legacy and
655
      // that pattern should not be repeated, but it is left as is to
656
      // reduce the need to modify older views. Let's consider the
657
      // pattern used here to be the template from which future plugins
658
      // should be copied.
659
      'exposed_form' => array(
660
        'contains' => array(
661
          'type' => array('default' => 'basic', 'export' => 'export_plugin', 'unpack_translatable' => 'unpack_plugin'),
662
          'options' => array('default' => array(), 'export' => FALSE),
663
         ),
664
      ),
665
      'pager' => array(
666
        'contains' => array(
667
          'type' => array('default' => 'full', 'export' => 'export_plugin', 'unpack_translatable' => 'unpack_plugin'),
668
          'options' => array('default' => array(), 'export' => FALSE),
669
         ),
670
      ),
671

    
672
      // Note that the styles have their options completely independent.
673
      // Like access and cache above, this is a legacy pattern and
674
      // should not be repeated.
675
      'style_plugin' => array(
676
        'default' => 'default',
677
        'export' => 'export_style',
678
        'unpack_translatable' => 'unpack_style',
679
      ),
680
      'style_options' => array(
681
        'default' => array(),
682
        'export' => FALSE,
683
      ),
684
      'row_plugin' => array(
685
        'default' => 'fields',
686
        'export' => 'export_style',
687
        'unpack_translatable' => 'unpack_style',
688
      ),
689
      'row_options' => array(
690
        'default' => array(),
691
        'export' => FALSE,
692
      ),
693

    
694
      'exposed_block' => array(
695
        'default' => FALSE,
696
      ),
697

    
698
      'header' => array(
699
        'default' => array(),
700
        'export' => 'export_handler',
701
        'unpack_translatable' => 'unpack_handler',
702
      ),
703
      'footer' => array(
704
        'default' => array(),
705
        'export' => 'export_handler',
706
        'unpack_translatable' => 'unpack_handler',
707
      ),
708
      'empty' => array(
709
        'default' => array(),
710
        'export' => 'export_handler',
711
        'unpack_translatable' => 'unpack_handler',
712
      ),
713

    
714
      // We want these to export last.
715
      // These are the 5 handler types.
716
      'relationships' => array(
717
        'default' => array(),
718
        'export' => 'export_handler',
719
        'unpack_translatable' => 'unpack_handler',
720

    
721
      ),
722
      'fields' => array(
723
        'default' => array(),
724
        'export' => 'export_handler',
725
        'unpack_translatable' => 'unpack_handler',
726
      ),
727
      'sorts' => array(
728
        'default' => array(),
729
        'export' => 'export_handler',
730
        'unpack_translatable' => 'unpack_handler',
731
      ),
732
      'arguments' => array(
733
        'default' => array(),
734
        'export' => 'export_handler',
735
        'unpack_translatable' => 'unpack_handler',
736
      ),
737
      'filter_groups' => array(
738
        'contains' => array(
739
          'operator' => array('default' => 'AND'),
740
          'groups' => array('default' => array(1 => 'AND')),
741
        ),
742
      ),
743
      'filters' => array(
744
        'default' => array(),
745
        'export' => 'export_handler',
746
        'unpack_translatable' => 'unpack_handler',
747
      ),
748
    );
749

    
750
    if (empty($this->definition['use pager'])) {
751
      $options['defaults']['default']['use_pager'] = FALSE;
752
      $options['defaults']['default']['items_per_page'] = FALSE;
753
      $options['defaults']['default']['offset'] = FALSE;
754
      $options['defaults']['default']['pager'] = FALSE;
755
      $options['pager']['contains']['type']['default'] = 'some';
756
    }
757

    
758
    if ($this->is_default_display()) {
759
      unset($options['defaults']);
760
    }
761

    
762
    foreach ($this->extender as $extender) {
763
      $extender->options_definition_alter($options);
764
    }
765

    
766
    return $options;
767
  }
768

    
769
  /**
770
   * Check to see if the display has a 'path' field.
771
   *
772
   * This is a pure function and not just a setting on the definition
773
   * because some displays (such as a panel pane) may have a path based
774
   * upon configuration.
775
   *
776
   * By default, displays do not have a path.
777
   */
778
  public function has_path() {
779
    return FALSE;
780
  }
781

    
782
  /**
783
   * Check to see if the display has some need to link to another display.
784
   *
785
   * For the most part, displays without a path will use a link display.
786
   * However, sometimes displays that have a path might also need to link to
787
   * another display. This is true for feeds.
788
   */
789
  public function uses_link_display() {
790
    return !$this->has_path();
791
  }
792

    
793
  /**
794
   * Check to see if the display can put the exposed form in a block.
795
   *
796
   * By default, displays that do not have a path cannot disconnect the exposed
797
   * form and put it in a block, because the form has no place to go and Views
798
   * really wants the forms to go to a specific page.
799
   */
800
  public function uses_exposed_form_in_block() {
801
    return $this->has_path();
802
  }
803

    
804
  /**
805
   * Check to see which display to use when creating links.
806
   */
807
  public function get_link_display() {
808
    $display_id = $this->get_option('link_display');
809
    // If unknown, pick the first one.
810
    if (empty($display_id) || empty($this->view->display[$display_id])) {
811
      foreach ($this->view->display as $display_id => $display) {
812
        if (!empty($display->handler) && $display->handler->has_path()) {
813
          return $display_id;
814
        }
815
      }
816
    }
817
    else {
818
      return $display_id;
819
    }
820
    // fall-through returns NULL
821
  }
822

    
823
  /**
824
   * Return the base path to use for this display.
825
   *
826
   * This can be overridden for displays that do strange things with the path.
827
   */
828
  public function get_path() {
829
    if ($this->has_path()) {
830
      return $this->get_option('path');
831
    }
832

    
833
    $display_id = $this->get_link_display();
834
    if ($display_id && !empty($this->view->display[$display_id]) && is_object($this->view->display[$display_id]->handler)) {
835
      return $this->view->display[$display_id]->handler->get_path();
836
    }
837

    
838
    if ($this->get_option('link_display') == 'custom_url' && $link_url = $this->get_option('link_url')) {
839
      return $link_url;
840
    }
841
  }
842

    
843
  /**
844
   *
845
   */
846
  public function get_url() {
847
    return $this->view->get_url();
848
  }
849

    
850
  /**
851
   * Check to see if the display needs a breadcrumb.
852
   *
853
   * By default, displays do not need breadcrumbs.
854
   */
855
  public function uses_breadcrumb() {
856
    return FALSE;
857
  }
858

    
859
  /**
860
   * Determine if a given option is set to use the default or current display.
861
   *
862
   * @return
863
   *   TRUE for the default display.
864
   */
865
  public function is_defaulted($option) {
866
    return !$this->is_default_display() && !empty($this->default_display) && !empty($this->options['defaults'][$option]);
867
  }
868

    
869
  /**
870
   * Intelligently get an option either from this or default display.
871
   */
872
  public function get_option($option) {
873
    if ($this->is_defaulted($option)) {
874
      return $this->default_display->get_option($option);
875
    }
876

    
877
    if (isset($this->options[$option]) || array_key_exists($option, $this->options)) {
878
      return $this->options[$option];
879
    }
880
  }
881

    
882
  /**
883
   * Determine if the display's style uses fields.
884
   */
885
  public function uses_fields() {
886
    $plugin = $this->get_plugin();
887
    if ($plugin) {
888
      return $plugin->uses_fields();
889
    }
890
  }
891

    
892
  /**
893
   * Get the instance of a plugin, for example style or row.
894
   *
895
   * @param string $type
896
   *   The type of the plugin.
897
   * @param string $name
898
   *   The name of the plugin defined in hook_views_plugins.
899
   *
900
   * @return views_plugin|FALSE
901
   */
902
  public function get_plugin($type = 'style', $name = NULL) {
903
    static $cache = array();
904
    if (!isset($cache[$type][$name])) {
905
      switch ($type) {
906
        case 'style':
907
        case 'row':
908
          $option_name = $type . '_plugin';
909
          $options = $this->get_option($type . '_options');
910
          if (!$name) {
911
            $name = $this->get_option($option_name);
912
          }
913
          break;
914

    
915
        case 'query':
916
          $views_data = views_fetch_data($this->view->base_table);
917
          $name = !empty($views_data['table']['base']['query class']) ? $views_data['table']['base']['query class'] : 'views_query';
918
        default:
919
          $option_name = $type;
920
          $options = $this->get_option($type);
921
          if (!$name) {
922
            $name = $options['type'];
923
          }
924

    
925
          // Access & cache store their options as siblings with the type; all
926
          // others use an 'options' array.
927
          if ($type != 'access' && $type != 'cache') {
928
            $options = $options['options'];
929
          }
930
      }
931
      $plugin = views_get_plugin($type, $name);
932

    
933
      if (!$plugin) {
934
        return;
935
      }
936
      if ($type != 'query') {
937
        $plugin->init($this->view, $this->display, $options);
938
      }
939
      else {
940
        $display_id = $this->is_defaulted($option_name) ? $this->display->id : 'default';
941
        $plugin->localization_keys = array($display_id, $type);
942

    
943
        if (!isset($this->base_field)) {
944
          $views_data = views_fetch_data($this->view->base_table);
945
          $this->view->base_field = !empty($views_data['table']['base']['field']) ? $views_data['table']['base']['field'] : '';
946
        }
947
        $plugin->init($this->view->base_table, $this->view->base_field, $options);
948
      }
949
      $cache[$type][$name] = $plugin;
950
    }
951

    
952
    return $cache[$type][$name];
953
  }
954

    
955
  /**
956
   * Get the handler object for a single handler.
957
   */
958
  public function &get_handler($type, $id) {
959
    if (!isset($this->handlers[$type])) {
960
      $this->get_handlers($type);
961
    }
962

    
963
    if (isset($this->handlers[$type][$id])) {
964
      return $this->handlers[$type][$id];
965
    }
966

    
967
    // So we can return a reference.
968
    $null = NULL;
969
    return $null;
970
  }
971

    
972
  /**
973
   * Get a full array of handlers for $type. This caches them.
974
   */
975
  public function &get_handlers($type) {
976
    if (!isset($this->handlers[$type])) {
977
      $this->handlers[$type] = array();
978
      $types = views_object_types();
979
      $plural = $types[$type]['plural'];
980

    
981
      foreach ($this->get_option($plural) as $id => $info) {
982
        // If this is during form submission and there are temporary options
983
        // which can only appear if the view is in the edit cache, use those
984
        // options instead. This is used for AJAX multi-step stuff.
985
        if (isset($_POST['form_id']) && isset($this->view->temporary_options[$type][$id])) {
986
          $info = $this->view->temporary_options[$type][$id];
987
        }
988

    
989
        if ($info['id'] != $id) {
990
          $info['id'] = $id;
991
        }
992

    
993
        // If aggregation is on, the group type might override the actual
994
        // handler that is in use. This piece of code checks that and,
995
        // if necessary, sets the override handler.
996
        $override = NULL;
997
        if ($this->use_group_by() && !empty($info['group_type'])) {
998
          if (empty($this->view->query)) {
999
            $this->view->init_query();
1000
          }
1001
          $aggregate = $this->view->query->get_aggregation_info();
1002
          if (!empty($aggregate[$info['group_type']]['handler'][$type])) {
1003
            $override = $aggregate[$info['group_type']]['handler'][$type];
1004
          }
1005
        }
1006

    
1007
        if (!empty($types[$type]['type'])) {
1008
          $handler_type = $types[$type]['type'];
1009
        }
1010
        else {
1011
          $handler_type = $type;
1012
        }
1013

    
1014
        $handler = views_get_handler($info['table'], $info['field'], $handler_type, $override);
1015
        if ($handler) {
1016
          // Special override for area types so they know where they come from.
1017
          if ($handler_type == 'area') {
1018
            $handler->handler_type = $type;
1019
          }
1020

    
1021
          $handler->init($this->view, $info);
1022
          $this->handlers[$type][$id] = &$handler;
1023
        }
1024

    
1025
        // Prevent reference problems.
1026
        unset($handler);
1027
      }
1028
    }
1029

    
1030
    return $this->handlers[$type];
1031
  }
1032

    
1033
  /**
1034
   * List of fields for the current display with the associated relationship.
1035
   *
1036
   * @param  $groupable_only
1037
   *  Return only an array of field labels from handler that return TRUE
1038
   *  from use_string_group_by method.
1039
   */
1040
  public function get_field_labels() {
1041
    // Use func_get_arg so the function signature isn't amended but we can
1042
    // still pass TRUE into the function to filter by groupable handlers.
1043
    $args = func_get_args();
1044
    $groupable_only = isset($args[0]) ? $args[0] : FALSE;
1045

    
1046
    $options = array();
1047
    foreach ($this->get_handlers('relationship') as $relationship => $handler) {
1048
      if ($label = $handler->label()) {
1049
        $relationships[$relationship] = $label;
1050
      }
1051
      else {
1052
        $relationships[$relationship] = $handler->ui_name();
1053
      }
1054
    }
1055

    
1056
    foreach ($this->get_handlers('field') as $id => $handler) {
1057
      if ($groupable_only && !$handler->use_string_group_by()) {
1058
        // Continue to next handler if it's not groupable.
1059
        continue;
1060
      }
1061
      if ($label = $handler->label()) {
1062
        $options[$id] = $label;
1063
      }
1064
      else {
1065
        $options[$id] = $handler->ui_name();
1066
      }
1067
      if (!empty($handler->options['relationship']) && !empty($relationships[$handler->options['relationship']])) {
1068
        $options[$id] = '(' . $relationships[$handler->options['relationship']] . ') ' . $options[$id];
1069
      }
1070
    }
1071
    return $options;
1072
  }
1073

    
1074
  /**
1075
   * Intelligently set an option either from this display or from the
1076
   * default display, if directed to do so.
1077
   */
1078
  public function set_option($option, $value) {
1079
    if ($this->is_defaulted($option)) {
1080
      return $this->default_display->set_option($option, $value);
1081
    }
1082

    
1083
    // Set this in two places: On the handler where we'll notice it
1084
    // but also on the display object so it gets saved. This should
1085
    // only be a temporary fix.
1086
    $this->display->display_options[$option] = $value;
1087
    return $this->options[$option] = $value;
1088
  }
1089

    
1090
  /**
1091
   * Set an option and force it to be an override.
1092
   */
1093
  public function override_option($option, $value) {
1094
    $this->set_override($option, FALSE);
1095
    $this->set_option($option, $value);
1096
  }
1097

    
1098
  /**
1099
   * Because forms may be split up into sections, this provides
1100
   * an easy URL to exactly the right section. Don't override this.
1101
   */
1102
  public function option_link($text, $section, $class = '', $title = '') {
1103
    views_add_js('ajax');
1104
    if (!empty($class)) {
1105
      $text = '<span>' . $text . '</span>';
1106
    }
1107

    
1108
    if (!trim($text)) {
1109
      $text = t('Broken field');
1110
    }
1111

    
1112
    if (empty($title)) {
1113
      $title = $text;
1114
    }
1115

    
1116
    // Truncate the path as it is displayed as a link.
1117
    if ($section == 'path') {
1118
      $text = views_ui_truncate($text, 24);
1119
    }
1120

    
1121
    return l($text, 'admin/structure/views/nojs/display/' . $this->view->name . '/' . $this->display->id . '/' . $section, array('attributes' => array('class' => 'views-ajax-link ' . $class, 'title' => $title, 'id' => drupal_html_id('views-' . $this->display->id . '-' . $section)), 'html' => TRUE));
1122
  }
1123

    
1124
  /**
1125
   * Returns to tokens for arguments.
1126
   *
1127
   * This function is similar to views_handler_field::get_render_tokens()
1128
   * but without fields tokens.
1129
   */
1130
  public function get_arguments_tokens() {
1131
    $tokens = array();
1132
    if (!empty($this->view->build_info['substitutions'])) {
1133
      $tokens = $this->view->build_info['substitutions'];
1134
    }
1135
    $count = 0;
1136
    foreach ($this->view->display_handler->get_handlers('argument') as $arg => $handler) {
1137
      $token = '%' . ++$count;
1138
      if (!isset($tokens[$token])) {
1139
        $tokens[$token] = '';
1140
      }
1141

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

    
1148
    return $tokens;
1149
  }
1150

    
1151
  /**
1152
   * Provide the default summary for options in the views UI.
1153
   *
1154
   * This output is returned as an array.
1155
   */
1156
  public function options_summary(&$categories, &$options) {
1157
    $categories = array(
1158
      'title' => array(
1159
        'title' => t('Title'),
1160
        'column' => 'first',
1161
      ),
1162
      'format' => array(
1163
        'title' => t('Format'),
1164
        'column' => 'first',
1165
      ),
1166
      'filters' => array(
1167
        'title' => t('Filters'),
1168
        'column' => 'first',
1169
      ),
1170
      'fields' => array(
1171
        'title' => t('Fields'),
1172
        'column' => 'first',
1173
      ),
1174
      'pager' => array(
1175
        'title' => t('Pager'),
1176
        'column' => 'second',
1177
      ),
1178
      'exposed' => array(
1179
        'title' => t('Exposed form'),
1180
        'column' => 'third',
1181
        'build' => array(
1182
          '#weight' => 1,
1183
        ),
1184
      ),
1185
      'access' => array(
1186
        'title' => '',
1187
        'column' => 'second',
1188
        'build' => array(
1189
          '#weight' => -5,
1190
        ),
1191
      ),
1192
      'other' => array(
1193
        'title' => t('Other'),
1194
        'column' => 'third',
1195
        'build' => array(
1196
          '#weight' => 2,
1197
        ),
1198
      ),
1199
    );
1200

    
1201
    if ($this->display->id != 'default') {
1202
      $options['display_id'] = array(
1203
        'category' => 'other',
1204
        'title' => t('Machine Name'),
1205
        'value' => !empty($this->display->new_id) ? check_plain($this->display->new_id) : check_plain($this->display->id),
1206
        'desc' => t('Change the machine name of this display.'),
1207
      );
1208
    }
1209

    
1210
    $display_comment = check_plain(views_ui_truncate($this->get_option('display_comment'), 80));
1211
    $options['display_comment'] = array(
1212
      'category' => 'other',
1213
      'title' => t('Comment'),
1214
      'value' => !empty($display_comment) ? $display_comment : t('No comment'),
1215
      'desc' => t('Comment or document this display.'),
1216
    );
1217

    
1218
    $title = strip_tags($this->get_option('title'));
1219
    if (!$title) {
1220
      $title = t('None');
1221
    }
1222

    
1223
    $options['title'] = array(
1224
      'category' => 'title',
1225
      'title' => t('Title'),
1226
      'value' => $title,
1227
      'desc' => t('Change the title that this display will use.'),
1228
    );
1229

    
1230
    $style_plugin = views_fetch_plugin_data('style', $this->get_option('style_plugin'));
1231
    $style_plugin_instance = $this->get_plugin('style');
1232
    $style_summary = empty($style_plugin['title']) ? t('Missing style plugin') : $style_plugin_instance->summary_title();
1233
    $style_title = empty($style_plugin['title']) ? t('Missing style plugin') : $style_plugin_instance->plugin_title();
1234

    
1235
    $style = '';
1236

    
1237
    $options['style_plugin'] = array(
1238
      'category' => 'format',
1239
      'title' => t('Format'),
1240
      'value' => $style_title,
1241
      'setting' => $style_summary,
1242
      'desc' => t('Change the way content is formatted.'),
1243
    );
1244

    
1245
    // This adds a 'Settings' link to the style_options setting if the style
1246
    // has options.
1247
    if (!empty($style_plugin['uses options'])) {
1248
      $options['style_plugin']['links']['style_options'] = t('Change settings for this format');
1249
    }
1250

    
1251
    if (!empty($style_plugin['uses row plugin'])) {
1252
      $row_plugin = views_fetch_plugin_data('row', $this->get_option('row_plugin'));
1253
      $row_plugin_instance = $this->get_plugin('row');
1254
      $row_summary = empty($row_plugin['title']) ? t('Missing style plugin') : $row_plugin_instance->summary_title();
1255
      $row_title = empty($row_plugin['title']) ? t('Missing style plugin') : $row_plugin_instance->plugin_title();
1256

    
1257
      $options['row_plugin'] = array(
1258
        'category' => 'format',
1259
        'title' => t('Show'),
1260
        'value' => $row_title,
1261
        'setting' => $row_summary,
1262
        'desc' => t('Change the way each row in the view is styled.'),
1263
      );
1264
      // This adds a 'Settings' link to the row_options setting if the row
1265
      // style has options.
1266
      if (!empty($row_plugin['uses options'])) {
1267
        $options['row_plugin']['links']['row_options'] = t('Change settings for this style');
1268
      }
1269
    }
1270
    if (!empty($this->definition['use ajax'])) {
1271
      $options['use_ajax'] = array(
1272
        'category' => 'other',
1273
        'title' => t('Use AJAX'),
1274
        'value' => $this->get_option('use_ajax') ? t('Yes') : t('No'),
1275
        'desc' => t('Change whether or not this display will use AJAX.'),
1276
      );
1277
    }
1278
    if (!empty($this->definition['accept attachments'])) {
1279
      $options['hide_attachment_summary'] = array(
1280
        'category' => 'other',
1281
        'title' => t('Hide attachments in summary'),
1282
        'value' => $this->get_option('hide_attachment_summary') ? t('Yes') : t('No'),
1283
        'desc' => t('Change whether or not to display attachments when displaying a contextual filter summary.'),
1284
      );
1285
    }
1286
    if (!isset($this->definition['contextual links locations']) || !empty($this->definition['contextual links locations'])) {
1287
      $options['hide_admin_links'] = array(
1288
        'category' => 'other',
1289
        'title' => t('Hide contextual links'),
1290
        'value' => $this->get_option('hide_admin_links') ? t('Yes') : t('No'),
1291
        'desc' => t('Change whether or not to display contextual links for this view.'),
1292
      );
1293
    }
1294

    
1295
    $pager_plugin = $this->get_plugin('pager');
1296
    if (!$pager_plugin) {
1297
      // Default to the no pager plugin.
1298
      $pager_plugin = views_get_plugin('pager', 'none');
1299
    }
1300

    
1301
    $pager_str = $pager_plugin->summary_title();
1302

    
1303
    $options['pager'] = array(
1304
      'category' => 'pager',
1305
      'title' => t('Use pager'),
1306
      'value' => $pager_plugin->plugin_title(),
1307
      'setting' => $pager_str,
1308
      'desc' => t("Change this display's pager setting."),
1309
    );
1310

    
1311
    // If pagers aren't allowed, change the text of the item.
1312
    if (empty($this->definition['use pager'])) {
1313
      $options['pager']['title'] = t('Items to display');
1314
    }
1315

    
1316
    if (!empty($pager_plugin->definition['uses options'])) {
1317
      $options['pager']['links']['pager_options'] = t('Change settings for this pager type.');
1318
    }
1319

    
1320
    if (!empty($this->definition['use more'])) {
1321
      $options['use_more'] = array(
1322
        'category' => 'pager',
1323
        'title' => t('More link'),
1324
        'value' => $this->get_option('use_more') ? t('Yes') : t('No'),
1325
        'desc' => t('Specify whether this display will provide a "more" link.'),
1326
      );
1327
    }
1328

    
1329
    $this->view->init_query();
1330
    if ($this->view->query->get_aggregation_info()) {
1331
      $options['group_by'] = array(
1332
        'category' => 'other',
1333
        'title' => t('Use aggregation'),
1334
        'value' => $this->get_option('group_by') ? t('Yes') : t('No'),
1335
        'desc' => t('Allow grouping and aggregation (calculation) of fields.'),
1336
      );
1337
    }
1338

    
1339
    $options['query'] = array(
1340
      'category' => 'other',
1341
      'title' => t('Query settings'),
1342
      'value' => t('Settings'),
1343
      'desc' => t('Allow to set some advanced settings for the query plugin'),
1344
    );
1345

    
1346
    $languages = array(
1347
        '***CURRENT_LANGUAGE***' => t("Current user's language"),
1348
        '***DEFAULT_LANGUAGE***' => t("Default site language"),
1349
        LANGUAGE_NONE => t('Language neutral'),
1350
    );
1351
    if (module_exists('locale')) {
1352
      $languages = array_merge($languages, locale_language_list());
1353
    }
1354
    $field_language = array();
1355
    $options['field_language'] = array(
1356
      'category' => 'other',
1357
      'title' => t('Field Language'),
1358
      'value' => $languages[$this->get_option('field_language')],
1359
      'desc' => t('All fields which support translations will be displayed in the selected language.'),
1360
    );
1361

    
1362
    $access_plugin = $this->get_plugin('access');
1363
    if (!$access_plugin) {
1364
      // default to the no access control plugin.
1365
      $access_plugin = views_get_plugin('access', 'none');
1366
    }
1367

    
1368
    $access_str = $access_plugin->summary_title();
1369

    
1370
    $options['access'] = array(
1371
      'category' => 'access',
1372
      'title' => t('Access'),
1373
      'value' => $access_plugin->plugin_title(),
1374
      'setting' => $access_str,
1375
      'desc' => t('Specify access control type for this display.'),
1376
    );
1377

    
1378
    if (!empty($access_plugin->definition['uses options'])) {
1379
      $options['access']['links']['access_options'] = t('Change settings for this access type.');
1380
    }
1381

    
1382
    $cache_plugin = $this->get_plugin('cache');
1383
    if (!$cache_plugin) {
1384
      // default to the no cache control plugin.
1385
      $cache_plugin = views_get_plugin('cache', 'none');
1386
    }
1387

    
1388
    $cache_str = $cache_plugin->summary_title();
1389

    
1390
    $options['cache'] = array(
1391
      'category' => 'other',
1392
      'title' => t('Caching'),
1393
      'value' => $cache_plugin->plugin_title(),
1394
      'setting' => $cache_str,
1395
      'desc' => t('Specify caching type for this display.'),
1396
    );
1397

    
1398
    if (!empty($cache_plugin->definition['uses options'])) {
1399
      $options['cache']['links']['cache_options'] = t('Change settings for this caching type.');
1400
    }
1401

    
1402
    if (!empty($access_plugin->definition['uses options'])) {
1403
      $options['access']['links']['access_options'] = t('Change settings for this access type.');
1404
    }
1405

    
1406
    if ($this->uses_link_display()) {
1407
      $display_id = $this->get_link_display();
1408
      $link_display = empty($this->view->display[$display_id]) ? t('None') : check_plain($this->view->display[$display_id]->display_title);
1409
      $link_display =  $this->get_option('link_display') == 'custom_url' ? t('Custom URL') : $link_display;
1410
      $options['link_display'] = array(
1411
        'category' => 'other',
1412
        'title' => t('Link display'),
1413
        'value' => $link_display,
1414
        'desc' => t('Specify which display or custom url this display will link to.'),
1415
      );
1416
    }
1417

    
1418
    if ($this->uses_exposed_form_in_block()) {
1419
      $options['exposed_block'] = array(
1420
        'category' => 'exposed',
1421
        'title' => t('Exposed form in block'),
1422
        'value' => $this->get_option('exposed_block') ? t('Yes') : t('No'),
1423
        'desc' => t('Allow the exposed form to appear in a block instead of the view.'),
1424
      );
1425
    }
1426

    
1427
    $exposed_form_plugin = $this->get_plugin('exposed_form');
1428
    if (!$exposed_form_plugin) {
1429
      // Default to the no cache control plugin.
1430
      $exposed_form_plugin = views_get_plugin('exposed_form', 'basic');
1431
    }
1432

    
1433
    $exposed_form_str = $exposed_form_plugin->summary_title();
1434

    
1435
    $options['exposed_form'] = array(
1436
      'category' => 'exposed',
1437
      'title' => t('Exposed form style'),
1438
      'value' => $exposed_form_plugin->plugin_title(),
1439
      'setting' => $exposed_form_str,
1440
      'desc' => t('Select the kind of exposed filter to use.'),
1441
    );
1442

    
1443
    if (!empty($exposed_form_plugin->definition['uses options'])) {
1444
      $options['exposed_form']['links']['exposed_form_options'] = t('Exposed form settings for this exposed form style.');
1445
    }
1446

    
1447
    $css_class = check_plain(trim($this->get_option('css_class')));
1448
    if (!$css_class) {
1449
      $css_class = t('None');
1450
    }
1451

    
1452
    $options['css_class'] = array(
1453
      'category' => 'other',
1454
      'title' => t('CSS class'),
1455
      'value' => $css_class,
1456
      'desc' => t('Change the CSS class name(s) that will be added to this display.'),
1457
    );
1458

    
1459
    $options['analyze-theme'] = array(
1460
      'category' => 'other',
1461
      'title' => t('Theme'),
1462
      'value' => t('Information'),
1463
      'desc' => t('Get information on how to theme this display'),
1464
    );
1465

    
1466
    foreach ($this->extender as $extender) {
1467
      $extender->options_summary($categories, $options);
1468
    }
1469
  }
1470

    
1471
  /**
1472
   * Provide the default form for setting options.
1473
   */
1474
  public function options_form(&$form, &$form_state) {
1475
    parent::options_form($form, $form_state);
1476
    if ($this->defaultable_sections($form_state['section'])) {
1477
      views_ui_standard_display_dropdown($form, $form_state, $form_state['section']);
1478
    }
1479
    $form['#title'] = check_plain($this->display->display_title) . ': ';
1480

    
1481
    // Set the 'section' to highlight on the form.
1482
    // If it's the item we're looking at is pulling from the default display,
1483
    // reflect that. Don't use is_defaulted since we want it to show up even
1484
    // on the default display.
1485
    if (!empty($this->options['defaults'][$form_state['section']])) {
1486
      $form['#section'] = 'default-' . $form_state['section'];
1487
    }
1488
    else {
1489
      $form['#section'] = $this->display->id . '-' . $form_state['section'];
1490
    }
1491

    
1492
    switch ($form_state['section']) {
1493
      case 'display_id':
1494
        $form['#title'] .= t('The machine name of this display');
1495
        $form['display_id'] = array(
1496
          '#type' => 'textfield',
1497
          '#description' => t('This is machine name of the display.'),
1498
          '#default_value' => !empty($this->display->new_id) ? $this->display->new_id : $this->display->id,
1499
          '#required' => TRUE,
1500
          '#size' => 64,
1501
        );
1502
        break;
1503

    
1504
      case 'display_title':
1505
        $form['#title'] .= t('The name and the description of this display');
1506
        $form['display_title'] = array(
1507
          '#title' => t('Name'),
1508
          '#type' => 'textfield',
1509
          '#description' => t('This name will appear only in the administrative interface for the View.'),
1510
          '#default_value' => $this->display->display_title,
1511
        );
1512
        $form['display_description'] = array(
1513
          '#title' => t('Description'),
1514
          '#type' => 'textfield',
1515
          '#description' => t('This description will appear only in the administrative interface for the View.'),
1516
          '#default_value' => $this->get_option('display_description'),
1517
        );
1518
        break;
1519

    
1520
      case 'display_comment':
1521
        $form['#title'] .= t("This display's comments");
1522
        $form['display_comment'] = array(
1523
          '#type' => 'textarea',
1524
          '#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.'),
1525
          '#default_value' => $this->get_option('display_comment'),
1526
        );
1527
        break;
1528

    
1529
      case 'title':
1530
        $form['#title'] .= t('The title of this view');
1531
        $form['title'] = array(
1532
          '#type' => 'textfield',
1533
          '#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.'),
1534
          '#default_value' => $this->get_option('title'),
1535
        );
1536
        break;
1537

    
1538
      case 'css_class':
1539
        $form['#title'] .= t('CSS class');
1540
        $form['css_class'] = array(
1541
          '#type' => 'textfield',
1542
          '#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.'),
1543
          '#default_value' => $this->get_option('css_class'),
1544
        );
1545
        break;
1546

    
1547
      case 'use_ajax':
1548
        $form['#title'] .= t('Use AJAX when available to load this view');
1549
        $form['description'] = array(
1550
          '#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>',
1551
        );
1552
        $form['use_ajax'] = array(
1553
          '#type' => 'radios',
1554
          '#options' => array(1 => t('Yes'), 0 => t('No')),
1555
          '#default_value' => $this->get_option('use_ajax') ? 1 : 0,
1556
        );
1557
        break;
1558

    
1559
      case 'hide_attachment_summary':
1560
        $form['#title'] .= t('Hide attachments when displaying a contextual filter summary');
1561
        $form['hide_attachment_summary'] = array(
1562
          '#type' => 'radios',
1563
          '#options' => array(1 => t('Yes'), 0 => t('No')),
1564
          '#default_value' => $this->get_option('hide_attachment_summary') ? 1 : 0,
1565
        );
1566
        break;
1567

    
1568
      case 'hide_admin_links':
1569
        $form['#title'] .= t('Hide contextual links on this view.');
1570
        $form['hide_admin_links'] = array(
1571
          '#type' => 'radios',
1572
          '#options' => array(1 => t('Yes'), 0 => t('No')),
1573
          '#default_value' => $this->get_option('hide_admin_links') ? 1 : 0,
1574
        );
1575
        break;
1576

    
1577
      case 'use_more':
1578
        $form['#title'] .= t('Add a more link to the bottom of the display.');
1579
        $form['use_more'] = array(
1580
          '#type' => 'checkbox',
1581
          '#title' => t('Create more link'),
1582
          '#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."),
1583
          '#default_value' => $this->get_option('use_more'),
1584
        );
1585
        $form['use_more_always'] = array(
1586
          '#type' => 'checkbox',
1587
          '#title' => t("Display 'more' link only if there is more content"),
1588
          '#description' => t("Leave this unchecked to display the 'more' link even if there are no more items to display."),
1589
          '#default_value' => !$this->get_option('use_more_always'),
1590
            '#dependency' => array(
1591
              'edit-use-more' => array(TRUE),
1592
            ),
1593
        );
1594
        $form['open_new_window'] = array(
1595
          '#type' => 'checkbox',
1596
          '#title' => t("Open 'more' link in new window (target='blank')"),
1597
          '#description' => t("Leave this unchecked to open the more link in the same window."),
1598
          '#default_value' => $this->get_option('open_new_window'),
1599
          '#dependency' => array(
1600
            'edit-use-more' => array(TRUE),
1601
          ),
1602
        );
1603
        $form['use_more_text'] = array(
1604
          '#type' => 'textfield',
1605
          '#title' => t('More link text'),
1606
          '#description' => t("The text to display for the more link."),
1607
          '#default_value' => $this->get_option('use_more_text'),
1608
          '#dependency' => array(
1609
            'edit-use-more' => array(TRUE),
1610
          ),
1611
        );
1612
        break;
1613

    
1614
      case 'group_by':
1615
        $form['#title'] .= t('Allow grouping and aggregation (calculation) of fields.');
1616
        $form['group_by'] = array(
1617
          '#type' => 'checkbox',
1618
          '#title' => t('Aggregate'),
1619
          '#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.'),
1620
          '#default_value' => $this->get_option('group_by'),
1621
        );
1622
        break;
1623

    
1624
      case 'access':
1625
        $form['#title'] .= t('Access restrictions');
1626
        $form['access'] = array(
1627
          '#prefix' => '<div class="clearfix">',
1628
          '#suffix' => '</div>',
1629
          '#tree' => TRUE,
1630
        );
1631
        $access = $this->get_option('access');
1632
        $form['access']['type'] =  array(
1633
          '#type' => 'radios',
1634
          '#options' => views_fetch_plugin_names('access', NULL, array($this->view->base_table)),
1635
          '#default_value' => $access['type'],
1636
        );
1637
        $access_plugin = views_fetch_plugin_data('access', $access['type']);
1638
        if (!empty($access_plugin['uses options'])) {
1639
          $form['markup'] = array(
1640
            '#prefix' => '<div class="form-item description">',
1641
            '#markup' => t('You may also adjust the !settings for the currently selected access restriction.', array('!settings' => $this->option_link(t('settings'), 'access_options'))),
1642
            '#suffix' => '</div>',
1643
          );
1644
        }
1645
        break;
1646

    
1647
      case 'access_options':
1648
        $access = $this->get_option('access');
1649
        $plugin = $this->get_plugin('access');
1650
        $form['#title'] .= t('Access options');
1651
        if ($plugin) {
1652
          if (!empty($plugin->definition['help topic'])) {
1653
            $form['#help_topic'] = $plugin->definition['help topic'];
1654
          }
1655
          if (!empty($plugin->definition['module'])) {
1656
            $form['#help_module'] = $plugin->definition['module'];
1657
          }
1658
          $form['access_options'] = array(
1659
            '#tree' => TRUE,
1660
          );
1661
          $form['access_options']['type'] = array(
1662
            '#type' => 'value',
1663
            '#value' => $access['type'],
1664
          );
1665
          $plugin->options_form($form['access_options'], $form_state);
1666
        }
1667
        break;
1668

    
1669
      case 'cache':
1670
        $form['#title'] .= t('Caching');
1671
        $form['cache'] = array(
1672
          '#prefix' => '<div class="clearfix">',
1673
          '#suffix' => '</div>',
1674
          '#tree' => TRUE,
1675
        );
1676
        $cache = $this->get_option('cache');
1677
        $form['cache']['type'] =  array(
1678
          '#type' => 'radios',
1679
          '#options' => views_fetch_plugin_names('cache', NULL, array($this->view->base_table)),
1680
          '#default_value' => $cache['type'],
1681
        );
1682
        $cache_plugin = views_fetch_plugin_data('cache', $cache['type']);
1683
        if (!empty($cache_plugin['uses options'])) {
1684
          $form['markup'] = array(
1685
            '#prefix' => '<div class="form-item description">',
1686
            '#suffix' => '</div>',
1687
            '#markup' => t('You may also adjust the !settings for the currently selected cache mechanism.', array('!settings' => $this->option_link(t('settings'), 'cache_options'))),
1688
          );
1689
        }
1690
        break;
1691

    
1692
      case 'cache_options':
1693
        $cache = $this->get_option('cache');
1694
        $plugin = $this->get_plugin('cache');
1695
        $form['#title'] .= t('Caching options');
1696
        if ($plugin) {
1697
          if (!empty($plugin->definition['help topic'])) {
1698
            $form['#help_topic'] = $plugin->definition['help topic'];
1699
          }
1700
          if (!empty($plugin->definition['module'])) {
1701
            $form['#help_module'] = $plugin->definition['module'];
1702
          }
1703
          $form['cache_options'] = array(
1704
            '#tree' => TRUE,
1705
          );
1706
          $form['cache_options']['type'] = array(
1707
            '#type' => 'value',
1708
            '#value' => $cache['type'],
1709
          );
1710
          $plugin->options_form($form['cache_options'], $form_state);
1711
        }
1712
        break;
1713

    
1714
      case 'query':
1715
        $query_options = $this->get_option('query');
1716
        $plugin_name = $query_options['type'];
1717

    
1718
        $form['#title'] .= t('Query options');
1719
        $this->view->init_query();
1720
        if ($this->view->query) {
1721
          if (!empty($this->view->query->definition['help topic'])) {
1722
            $form['#help_topic'] = $this->view->query->definition['help topic'];
1723
          }
1724
          if (!empty($this->view->query->definition['module'])) {
1725
            $form['#help_module'] = $this->view->query->definition['module'];
1726
          }
1727
          $form['query'] = array(
1728
            '#tree' => TRUE,
1729
            'type' => array(
1730
              '#type' => 'value',
1731
              '#value' => $plugin_name,
1732
            ),
1733
            'options' => array(
1734
              '#tree' => TRUE,
1735
            ),
1736
          );
1737
          $this->view->query->options_form($form['query']['options'], $form_state);
1738
        }
1739
        break;
1740

    
1741
      case 'field_language':
1742
        $form['#title'] .= t('Field Language');
1743

    
1744
        $entities = entity_get_info();
1745
        $entity_tables = array();
1746
        $has_translation_handlers = FALSE;
1747
        foreach ($entities as $type => $entity_info) {
1748
          $entity_tables[] = $entity_info['base table'];
1749

    
1750
          if (!empty($entity_info['translation'])) {
1751
            $has_translation_handlers = TRUE;
1752
          }
1753
        }
1754

    
1755
        // Doesn't make sense to show a field setting here if we aren't querying
1756
        // an entity base table. Also, we make sure that there's at least one
1757
        // entity type with a translation handler attached.
1758
        if (in_array($this->view->base_table, $entity_tables) && $has_translation_handlers) {
1759
          $languages = array(
1760
            '***CURRENT_LANGUAGE***' => t("Current user's language"),
1761
            '***DEFAULT_LANGUAGE***' => t("Default site language"),
1762
            LANGUAGE_NONE => t('Language neutral'),
1763
          );
1764
          $languages = array_merge($languages, views_language_list());
1765

    
1766
          $form['field_language'] = array(
1767
            '#type' => 'select',
1768
            '#title' => t('Field Language'),
1769
            '#description' => t('All fields which support translations will be displayed in the selected language.'),
1770
            '#options' => $languages,
1771
            '#default_value' => $this->get_option('field_language'),
1772
          );
1773
          $form['field_language_add_to_query'] = array(
1774
            '#type' => 'checkbox',
1775
            '#title' => t('When needed, add the field language condition to the query'),
1776
            '#default_value' => $this->get_option('field_language_add_to_query'),
1777
          );
1778
        }
1779
        else {
1780
          $form['field_language']['#markup'] = t("You don't have translatable entity types.");
1781
        }
1782
        break;
1783

    
1784
      case 'style_plugin':
1785
        $form['#title'] .= t('How should this view be styled');
1786
        $form['#help_topic'] = 'style';
1787
        $form['style_plugin'] =  array(
1788
          '#type' => 'radios',
1789
          '#options' => views_fetch_plugin_names('style', $this->get_style_type(), array($this->view->base_table)),
1790
          '#default_value' => $this->get_option('style_plugin'),
1791
          '#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.'),
1792
        );
1793

    
1794
        $style_plugin = views_fetch_plugin_data('style', $this->get_option('style_plugin'));
1795
        if (!empty($style_plugin['uses options'])) {
1796
          $form['markup'] = array(
1797
            '#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>',
1798
          );
1799
        }
1800
        break;
1801

    
1802
      case 'style_options':
1803
        $form['#title'] .= t('Style options');
1804
        $style = TRUE;
1805
        $type = 'style_plugin';
1806
        $name = $this->get_option('style_plugin');
1807

    
1808
      case 'row_options':
1809
        if (!isset($name)) {
1810
          $name = $this->get_option('row_plugin');
1811
        }
1812
        // If row, $style will be empty.
1813
        if (empty($style)) {
1814
          $form['#title'] .= t('Row style options');
1815
          $type = 'row_plugin';
1816
        }
1817
        $plugin = $this->get_plugin(empty($style) ? 'row' : 'style');
1818
        if ($plugin) {
1819
          if (!empty($plugin->definition['help topic'])) {
1820
            $form['#help_topic'] = $plugin->definition['help topic'];
1821
          }
1822
          if (!empty($plugin->definition['module'])) {
1823
            $form['#help_module'] = $plugin->definition['module'];
1824
          }
1825
          $form[$form_state['section']] = array(
1826
            '#tree' => TRUE,
1827
          );
1828
          $plugin->options_form($form[$form_state['section']], $form_state);
1829
        }
1830
        break;
1831

    
1832
      case 'row_plugin':
1833
        $form['#title'] .= t('How should each row in this view be styled');
1834
        $form['#help_topic'] = 'style-row';
1835
        $form['row_plugin'] =  array(
1836
          '#type' => 'radios',
1837
          '#options' => views_fetch_plugin_names('row', $this->get_style_type(), array($this->view->base_table)),
1838
          '#default_value' => $this->get_option('row_plugin'),
1839
        );
1840

    
1841
        $row_plugin = views_fetch_plugin_data('row', $this->get_option('row_plugin'));
1842
        if (!empty($row_plugin['uses options'])) {
1843
          $form['markup'] = array(
1844
            '#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>',
1845
          );
1846
        }
1847
        break;
1848

    
1849
      case 'link_display':
1850
        $form['#title'] .= t('Which display to use for path');
1851
        foreach ($this->view->display as $display_id => $display) {
1852
          if ($display->handler->has_path()) {
1853
            $options[$display_id] = $display->display_title;
1854
          }
1855
        }
1856
        $options['custom_url'] = t('Custom URL');
1857
        if (count($options)) {
1858
          $form['link_display'] = array(
1859
            '#type' => 'radios',
1860
            '#options' => $options,
1861
            '#description' => t("Which display to use to get this display's path for things like summary links, rss feed links, more links, etc."),
1862
            '#default_value' => $this->get_option('link_display'),
1863
          );
1864
        }
1865

    
1866
        $options = array();
1867
        // This lets us prepare the key as we want it printed.
1868
        $count = 0;
1869
        foreach ($this->view->display_handler->get_handlers('argument') as $arg => $handler) {
1870
          $options[t('Arguments')]['%' . ++$count] = t('@argument title', array('@argument' => $handler->ui_name()));
1871
          $options[t('Arguments')]['!' . $count] = t('@argument input', array('@argument' => $handler->ui_name()));
1872
        }
1873

    
1874
        // Default text. We have some options, so make a list.
1875
        $output = '';
1876
        if (!empty($options)) {
1877
          $output = t('<p>The following tokens are available for this link.</p>');
1878
          foreach (array_keys($options) as $type) {
1879
            if (!empty($options[$type])) {
1880
              $items = array();
1881
              foreach ($options[$type] as $key => $value) {
1882
                $items[] = $key . ' == ' . $value;
1883
              }
1884
              $output .= theme('item_list',
1885
                array(
1886
                  'items' => $items,
1887
                  'type' => $type
1888
                ));
1889
            }
1890
          }
1891
        }
1892

    
1893
        $form['link_url'] = array(
1894
          '#type' => 'textfield',
1895
          '#title' => t('Custom URL'),
1896
          '#default_value' => $this->get_option('link_url'),
1897
          '#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,
1898
          '#dependency' => array('radio:link_display' => array('custom_url')),
1899
        );
1900
        break;
1901

    
1902
      case 'analyze-theme':
1903
        $form['#title'] .= t('Theming information');
1904
        $form['#help_topic'] = 'analyze-theme';
1905

    
1906
        if (isset($_POST['theme'])) {
1907
          $this->theme = $_POST['theme'];
1908
        }
1909
        elseif (empty($this->theme)) {
1910
          $this->theme = variable_get('theme_default', 'bartik');
1911
        }
1912

    
1913
        if (isset($GLOBALS['theme']) && $GLOBALS['theme'] == $this->theme) {
1914
          $this->theme_registry = theme_get_registry();
1915
          $theme_engine = $GLOBALS['theme_engine'];
1916
        }
1917
        else {
1918
          $themes = list_themes();
1919
          $theme = $themes[$this->theme];
1920

    
1921
          // Find all our ancestor themes and put them in an array.
1922
          $base_theme = array();
1923
          $ancestor = $this->theme;
1924
          while ($ancestor && isset($themes[$ancestor]->base_theme)) {
1925
            $ancestor = $themes[$ancestor]->base_theme;
1926
            $base_theme[] = $themes[$ancestor];
1927
          }
1928

    
1929
          // The base themes should be initialized in the right order.
1930
          $base_theme = array_reverse($base_theme);
1931

    
1932
          // This code is copied directly from _drupal_theme_initialize().
1933
          $theme_engine = NULL;
1934

    
1935
          // Initialize the theme.
1936
          if (isset($theme->engine)) {
1937
            // Include the engine.
1938
            include_once DRUPAL_ROOT . '/' . $theme->owner;
1939

    
1940
            $theme_engine = $theme->engine;
1941
            if (function_exists($theme_engine . '_init')) {
1942
              foreach ($base_theme as $base) {
1943
                call_user_func($theme_engine . '_init', $base);
1944
              }
1945
              call_user_func($theme_engine . '_init', $theme);
1946
            }
1947
          }
1948
          else {
1949
            // include non-engine theme files
1950
            foreach ($base_theme as $base) {
1951
              // Include the theme file or the engine.
1952
              if (!empty($base->owner)) {
1953
                include_once DRUPAL_ROOT . '/' . $base->owner;
1954
              }
1955
            }
1956
            // and our theme gets one too.
1957
            if (!empty($theme->owner)) {
1958
              include_once DRUPAL_ROOT . '/' . $theme->owner;
1959
            }
1960
          }
1961
          $this->theme_registry = _theme_load_registry($theme, $base_theme, $theme_engine);
1962
        }
1963

    
1964
        // If there's a theme engine involved, we also need to know its
1965
        // extension so we can give the proper filename.
1966
        $this->theme_extension = '.tpl.php';
1967
        if (isset($theme_engine)) {
1968
          $extension_function = $theme_engine . '_extension';
1969
          if (function_exists($extension_function)) {
1970
            $this->theme_extension = $extension_function();
1971
          }
1972
        }
1973

    
1974
        $funcs = array();
1975
        // Get theme functions for the display. Note that some displays may
1976
        // not have themes. The 'feed' display, for example, completely
1977
        // delegates to the style.
1978
        if (!empty($this->definition['theme'])) {
1979
          $funcs[] = $this->option_link(t('Display output'), 'analyze-theme-display') . ': '  . $this->format_themes($this->theme_functions());
1980
          $themes = $this->additional_theme_functions();
1981
          if ($themes) {
1982
            foreach ($themes as $theme) {
1983
              $funcs[] = $this->option_link(t('Alternative display output'), 'analyze-theme-display') . ': '  . $this->format_themes($theme);
1984
            }
1985
          }
1986
        }
1987

    
1988
        $plugin = $this->get_plugin();
1989
        if ($plugin) {
1990
          $funcs[] = $this->option_link(t('Style output'), 'analyze-theme-style') . ': ' . $this->format_themes($plugin->theme_functions(), $plugin->additional_theme_functions());
1991
          $themes = $plugin->additional_theme_functions();
1992
          if ($themes) {
1993
            foreach ($themes as $theme) {
1994
              $funcs[] = $this->option_link(t('Alternative style'), 'analyze-theme-style') . ': '  . $this->format_themes($theme);
1995
            }
1996
          }
1997

    
1998
          if ($plugin->uses_row_plugin()) {
1999
            $row_plugin = $this->get_plugin('row');
2000
            if ($row_plugin) {
2001
              $funcs[] = $this->option_link(t('Row style output'), 'analyze-theme-row') . ': ' . $this->format_themes($row_plugin->theme_functions());
2002
              $themes = $row_plugin->additional_theme_functions();
2003
              if ($themes) {
2004
                foreach ($themes as $theme) {
2005
                  $funcs[] = $this->option_link(t('Alternative row style'), 'analyze-theme-row') . ': '  . $this->format_themes($theme);
2006
                }
2007
              }
2008
            }
2009
          }
2010

    
2011
          if ($plugin->uses_fields()) {
2012
            foreach ($this->get_handlers('field') as $id => $handler) {
2013
              $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());
2014
            }
2015
          }
2016
        }
2017

    
2018
        $form['important'] = array(
2019
          '#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>',
2020
        );
2021

    
2022
        if (isset($this->view->display[$this->view->current_display]->new_id)) {
2023
          $form['important']['new_id'] = array(
2024
            '#prefix' => '<div class="description">',
2025
            '#suffix' => '</div>',
2026
            '#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."),
2027
          );
2028
        }
2029

    
2030
        foreach (list_themes() as $key => $theme) {
2031
          if (!empty($theme->info['hidden'])) {
2032
            continue;
2033
          }
2034
          $options[$key] = $theme->info['name'];
2035
        }
2036

    
2037
        $form['box'] = array(
2038
          '#prefix' => '<div class="container-inline">',
2039
          '#suffix' => '</div>',
2040
        );
2041
        $form['box']['theme'] = array(
2042
          '#type' => 'select',
2043
          '#options' => $options,
2044
          '#default_value' => $this->theme,
2045
        );
2046

    
2047
        $form['box']['change'] = array(
2048
          '#type' => 'submit',
2049
          '#value' => t('Change theme'),
2050
          '#submit' => array('views_ui_edit_display_form_change_theme'),
2051
        );
2052

    
2053
        $form['analysis'] = array(
2054
          '#markup' => '<div class="form-item">' . theme('item_list', array('items' => $funcs)) . '</div>',
2055
        );
2056

    
2057
        $form['rescan_button'] = array(
2058
          '#prefix' => '<div class="form-item">',
2059
          '#suffix' => '</div>',
2060
        );
2061
        $form['rescan_button']['button'] = array(
2062
          '#type' => 'submit',
2063
          '#value' => t('Rescan template files'),
2064
          '#submit' => array('views_ui_config_item_form_rescan'),
2065
        );
2066
        $form['rescan_button']['markup'] = array(
2067
          '#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>',
2068
        );
2069

    
2070
        $form_state['ok_button'] = TRUE;
2071
        break;
2072

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

    
2077
        if (empty($this->definition['theme'])) {
2078
          $output .= t('This display has no theming information');
2079
        }
2080
        else {
2081
          $output .= '<p>' . t('This is the default theme template used for this display.') . '</p>';
2082
          $output .= '<pre>' . check_plain(file_get_contents('./' . $this->definition['theme path'] . '/' . strtr($this->definition['theme'], '_', '-') . '.tpl.php')) . '</pre>';
2083
        }
2084

    
2085
        if (!empty($this->definition['additional themes'])) {
2086
          foreach ($this->definition['additional themes'] as $theme => $type) {
2087
            $output .= '<p>' . t('This is an alternative template for this display.') . '</p>';
2088
            $output .= '<pre>' . check_plain(file_get_contents('./' . $this->definition['theme path'] . '/' . strtr($theme, '_', '-') . '.tpl.php')) . '</pre>';
2089
          }
2090
        }
2091

    
2092
        $form['analysis'] = array(
2093
          '#markup' => '<div class="form-item">' . $output . '</div>',
2094
        );
2095

    
2096
        $form_state['ok_button'] = TRUE;
2097
        break;
2098

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

    
2103
        $plugin = $this->get_plugin();
2104

    
2105
        if (empty($plugin->definition['theme'])) {
2106
          $output .= t('This display has no style theming information');
2107
        }
2108
        else {
2109
          $output .= '<p>' . t('This is the default theme template used for this style.') . '</p>';
2110
          $output .= '<pre>' . check_plain(file_get_contents('./' . $plugin->definition['theme path'] . '/' . strtr($plugin->definition['theme'], '_', '-') . '.tpl.php')) . '</pre>';
2111
        }
2112

    
2113
        if (!empty($plugin->definition['additional themes'])) {
2114
          foreach ($plugin->definition['additional themes'] as $theme => $type) {
2115
            $output .= '<p>' . t('This is an alternative template for this style.') . '</p>';
2116
            $output .= '<pre>' . check_plain(file_get_contents('./' . $plugin->definition['theme path'] . '/' . strtr($theme, '_', '-') . '.tpl.php')) . '</pre>';
2117
          }
2118
        }
2119

    
2120
        $form['analysis'] = array(
2121
          '#markup' => '<div class="form-item">' . $output . '</div>',
2122
        );
2123

    
2124
        $form_state['ok_button'] = TRUE;
2125
        break;
2126

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

    
2131
        $plugin = $this->get_plugin('row');
2132

    
2133
        if (empty($plugin->definition['theme'])) {
2134
          $output .= t('This display has no row style theming information');
2135
        }
2136
        else {
2137
          $output .= '<p>' . t('This is the default theme template used for this row style.') . '</p>';
2138
          $output .= '<pre>' . check_plain(file_get_contents('./' . $plugin->definition['theme path'] . '/' . strtr($plugin->definition['theme'], '_', '-') . '.tpl.php')) . '</pre>';
2139
        }
2140

    
2141
        if (!empty($plugin->definition['additional themes'])) {
2142
          foreach ($plugin->definition['additional themes'] as $theme => $type) {
2143
            $output .= '<p>' . t('This is an alternative template for this row style.') . '</p>';
2144
            $output .= '<pre>' . check_plain(file_get_contents('./' . $plugin->definition['theme path'] . '/' . strtr($theme, '_', '-') . '.tpl.php')) . '</pre>';
2145
          }
2146
        }
2147

    
2148
        $form['analysis'] = array(
2149
          '#markup' => '<div class="form-item">' . $output . '</div>',
2150
        );
2151

    
2152
        $form_state['ok_button'] = TRUE;
2153
        break;
2154

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

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

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

    
2165
        $form['analysis'] = array(
2166
          '#markup' => '<div class="form-item">' . $output . '</div>',
2167
        );
2168
        $form_state['ok_button'] = TRUE;
2169
        break;
2170

    
2171
      case 'exposed_block':
2172
        $form['#title'] .= t('Put the exposed form in a block');
2173
        $form['description'] = array(
2174
          '#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>',
2175
        );
2176
        $form['exposed_block'] = array(
2177
          '#type' => 'radios',
2178
          '#options' => array(1 => t('Yes'), 0 => t('No')),
2179
          '#default_value' => $this->get_option('exposed_block') ? 1 : 0,
2180
        );
2181
        break;
2182

    
2183
      case 'exposed_form':
2184
        $form['#title'] .= t('Exposed Form');
2185
        $form['exposed_form'] = array(
2186
          '#prefix' => '<div class="clearfix">',
2187
          '#suffix' => '</div>',
2188
          '#tree' => TRUE,
2189
        );
2190

    
2191
        $exposed_form = $this->get_option('exposed_form');
2192
        $form['exposed_form']['type'] =  array(
2193
          '#type' => 'radios',
2194
          '#options' => views_fetch_plugin_names('exposed_form', NULL, array($this->view->base_table)),
2195
          '#default_value' => $exposed_form['type'],
2196
        );
2197

    
2198
        $exposed_form_plugin = views_fetch_plugin_data('exposed_form', $exposed_form['type']);
2199
        if (!empty($exposed_form_plugin['uses options'])) {
2200
          $form['markup'] = array(
2201
            '#prefix' => '<div class="form-item description">',
2202
            '#suffix' => '</div>',
2203
            '#markup' => t('You may also adjust the !settings for the currently selected style.', array('!settings' => $this->option_link(t('settings'), 'exposed_form_options'))),
2204
          );
2205
        }
2206
        break;
2207

    
2208
      case 'exposed_form_options':
2209
        $plugin = $this->get_plugin('exposed_form');
2210
        $form['#title'] .= t('Exposed form options');
2211
        if ($plugin) {
2212
          if (!empty($plugin->definition['help topic'])) {
2213
            $form['#help_topic'] = $plugin->definition['help topic'];
2214
          }
2215
          if (!empty($plugin->definition['module'])) {
2216
            $form['#help_module'] = $plugin->definition['module'];
2217
          }
2218

    
2219
          $form['exposed_form_options'] = array(
2220
            '#tree' => TRUE,
2221
          );
2222
          $plugin->options_form($form['exposed_form_options'], $form_state);
2223
        }
2224
        break;
2225

    
2226
      case 'pager':
2227
        $form['#title'] .= t('Select which pager, if any, to use for this view');
2228
        $form['pager'] = array(
2229
          '#prefix' => '<div class="clearfix">',
2230
          '#suffix' => '</div>',
2231
          '#tree' => TRUE,
2232
        );
2233

    
2234
        $pager = $this->get_option('pager');
2235
        $form['pager']['type'] =  array(
2236
          '#type' => 'radios',
2237
          '#options' => views_fetch_plugin_names('pager', empty($this->definition['use pager']) ? 'basic' : NULL, array($this->view->base_table)),
2238
          '#default_value' => $pager['type'],
2239
        );
2240

    
2241
        $pager_plugin = views_fetch_plugin_data('pager', $pager['type']);
2242
        if (!empty($pager_plugin['uses options'])) {
2243
          $form['markup'] = array(
2244
            '#prefix' => '<div class="form-item description">',
2245
            '#suffix' => '</div>',
2246
            '#markup' => t('You may also adjust the !settings for the currently selected pager.', array('!settings' => $this->option_link(t('settings'), 'pager_options'))),
2247
          );
2248
        }
2249
        break;
2250

    
2251
      case 'pager_options':
2252
        $plugin = $this->get_plugin('pager');
2253
        $form['#title'] .= t('Pager options');
2254
        if ($plugin) {
2255
          if (!empty($plugin->definition['help topic'])) {
2256
            $form['#help_topic'] = $plugin->definition['help topic'];
2257
          }
2258
          if (!empty($plugin->definition['module'])) {
2259
            $form['#help_module'] = $plugin->definition['module'];
2260
          }
2261

    
2262
          $form['pager_options'] = array(
2263
            '#tree' => TRUE,
2264
          );
2265
          $plugin->options_form($form['pager_options'], $form_state);
2266
        }
2267
        break;
2268
    }
2269

    
2270
    foreach ($this->extender as $extender) {
2271
      $extender->options_form($form, $form_state);
2272
    }
2273
  }
2274

    
2275
  /**
2276
   * Format a list of theme templates for output by the theme info helper.
2277
   */
2278
  public function format_themes($themes) {
2279
    $registry = $this->theme_registry;
2280
    $extension = $this->theme_extension;
2281

    
2282
    $output = '';
2283
    $picked = FALSE;
2284
    foreach ($themes as $theme) {
2285
      $template = strtr($theme, '_', '-') . $extension;
2286
      if (!$picked && !empty($registry[$theme])) {
2287
        $template_path = isset($registry[$theme]['path']) ? $registry[$theme]['path'] . '/' : './';
2288
        if (file_exists($template_path . $template)) {
2289
          $hint = t('File found in folder @template-path', array('@template-path' => $template_path));
2290
          $template = '<strong title="'. $hint .'">' . $template . '</strong>';
2291
        }
2292
        else {
2293
          $template = '<strong class="error">' . $template . ' ' . t('(File not found, in folder @template-path)', array('@template-path' => $template_path)) . '</strong>';
2294
        }
2295
        $picked = TRUE;
2296
      }
2297
      $fixed[] = $template;
2298
    }
2299

    
2300
    return implode(', ', array_reverse($fixed));
2301
  }
2302

    
2303
  /**
2304
   * Validate the options form.
2305
   */
2306
  public function options_validate(&$form, &$form_state) {
2307
    switch ($form_state['section']) {
2308
      case 'display_title':
2309
        if (empty($form_state['values']['display_title'])) {
2310
          form_error($form['display_title'], t('Display title may not be empty.'));
2311
        }
2312
        break;
2313

    
2314
      case 'css_class':
2315
        $css_class = $form_state['values']['css_class'];
2316
        if (preg_match('/[^a-zA-Z0-9-_ ]/', $css_class)) {
2317
          form_error($form['css_class'], t('CSS classes must be alphanumeric or dashes only.'));
2318
        }
2319
        break;
2320

    
2321
      case 'display_id':
2322
        if ($form_state['values']['display_id']) {
2323
          if (preg_match('/[^a-z0-9_]/', $form_state['values']['display_id'])) {
2324
            form_error($form['display_id'], t('Display name must be letters, numbers, or underscores only.'));
2325
          }
2326

    
2327
          foreach ($this->view->display as $id => $display) {
2328
            if ($id != $this->view->current_display && ($form_state['values']['display_id'] == $id || (isset($display->new_id) && $form_state['values']['display_id'] == $display->new_id))) {
2329
              form_error($form['display_id'], t('Display id should be unique.'));
2330
            }
2331
          }
2332
        }
2333
        break;
2334

    
2335
      case 'style_options':
2336
        $style = TRUE;
2337
      case 'row_options':
2338
        // if row, $style will be empty.
2339
        $plugin = $this->get_plugin(empty($style) ? 'row' : 'style');
2340
        if ($plugin) {
2341
          $plugin->options_validate($form[$form_state['section']], $form_state);
2342
        }
2343
        break;
2344

    
2345
      case 'access_options':
2346
        $plugin = $this->get_plugin('access');
2347
        if ($plugin) {
2348
          $plugin->options_validate($form['access_options'], $form_state);
2349
        }
2350
        break;
2351

    
2352
      case 'query':
2353
        if ($this->view->query) {
2354
          $this->view->query->options_validate($form['query'], $form_state);
2355
        }
2356
        break;
2357

    
2358
      case 'cache_options':
2359
        $plugin = $this->get_plugin('cache');
2360
        if ($plugin) {
2361
          $plugin->options_validate($form['cache_options'], $form_state);
2362
        }
2363
        break;
2364

    
2365
      case 'exposed_form_options':
2366
        $plugin = $this->get_plugin('exposed_form');
2367
        if ($plugin) {
2368
          $plugin->options_validate($form['exposed_form_options'], $form_state);
2369
        }
2370
        break;
2371

    
2372
      case 'pager_options':
2373
        $plugin = $this->get_plugin('pager');
2374
        if ($plugin) {
2375
          $plugin->options_validate($form['pager_options'], $form_state);
2376
        }
2377
        break;
2378
    }
2379

    
2380
    foreach ($this->extender as $extender) {
2381
      $extender->options_validate($form, $form_state);
2382
    }
2383
  }
2384

    
2385
  /**
2386
   * Perform any necessary changes to the form values prior to storage.
2387
   * There is no need for this function to actually store the data.
2388
   */
2389
  public function options_submit(&$form, &$form_state) {
2390
    // Not sure I like this being here, but it seems (?) like a logical place.
2391
    $cache_plugin = $this->get_plugin('cache');
2392
    if ($cache_plugin) {
2393
      $cache_plugin->cache_flush();
2394
    }
2395

    
2396
    $section = $form_state['section'];
2397
    switch ($section) {
2398
      case 'display_id':
2399
        if (isset($form_state['values']['display_id'])) {
2400
          $this->display->new_id = $form_state['values']['display_id'];
2401
        }
2402
        break;
2403

    
2404
      case 'display_title':
2405
        $this->display->display_title = $form_state['values']['display_title'];
2406
        $this->set_option('display_description', $form_state['values']['display_description']);
2407
        break;
2408

    
2409
      case 'access':
2410
        $access = $this->get_option('access');
2411
        if ($access['type'] != $form_state['values']['access']['type']) {
2412
          $plugin = views_get_plugin('access', $form_state['values']['access']['type']);
2413
          if ($plugin) {
2414
            $access = array('type' => $form_state['values']['access']['type']);
2415
            $this->set_option('access', $access);
2416
            if (!empty($plugin->definition['uses options'])) {
2417
              views_ui_add_form_to_stack('display', $this->view, $this->display->id, array('access_options'));
2418
            }
2419
          }
2420
        }
2421
        break;
2422

    
2423
      case 'access_options':
2424
        $plugin = views_get_plugin('access', $form_state['values'][$section]['type']);
2425
        if ($plugin) {
2426
          $plugin->options_submit($form['access_options'], $form_state);
2427
          $this->set_option('access', $form_state['values'][$section]);
2428
        }
2429
        break;
2430

    
2431
      case 'cache':
2432
        $cache = $this->get_option('cache');
2433
        if ($cache['type'] != $form_state['values']['cache']['type']) {
2434
          $plugin = views_get_plugin('cache', $form_state['values']['cache']['type']);
2435
          if ($plugin) {
2436
            $cache = array('type' => $form_state['values']['cache']['type']);
2437
            $this->set_option('cache', $cache);
2438
            if (!empty($plugin->definition['uses options'])) {
2439
              views_ui_add_form_to_stack('display', $this->view, $this->display->id, array('cache_options'));
2440
            }
2441
          }
2442
        }
2443
        break;
2444

    
2445
      case 'cache_options':
2446
        $plugin = views_get_plugin('cache', $form_state['values'][$section]['type']);
2447
        if ($plugin) {
2448
          $plugin->options_submit($form['cache_options'], $form_state);
2449
          $this->set_option('cache', $form_state['values'][$section]);
2450
        }
2451
        break;
2452

    
2453
      case 'query':
2454
        $plugin = $this->get_plugin('query');
2455
        if ($plugin) {
2456
          $plugin->options_submit($form['query']['options'], $form_state);
2457
          $this->set_option('query', $form_state['values'][$section]);
2458
        }
2459
        break;
2460

    
2461
      case 'link_display':
2462
        $this->set_option('link_url', $form_state['values']['link_url']);
2463
      case 'title':
2464
      case 'css_class':
2465
      case 'display_comment':
2466
        $this->set_option($section, $form_state['values'][$section]);
2467
        break;
2468

    
2469
      case 'field_language':
2470
        $this->set_option('field_language', $form_state['values']['field_language']);
2471
        $this->set_option('field_language_add_to_query', $form_state['values']['field_language_add_to_query']);
2472
        break;
2473

    
2474
      case 'use_ajax':
2475
      case 'hide_attachment_summary':
2476
      case 'hide_admin_links':
2477
        $this->set_option($section, (bool)$form_state['values'][$section]);
2478
        break;
2479

    
2480
      case 'use_more':
2481
        $this->set_option($section, intval($form_state['values'][$section]));
2482
        $this->set_option('use_more_always', !intval($form_state['values']['use_more_always']));
2483
        $this->set_option('open_new_window', $form_state['values']['open_new_window']) == '1';
2484
        $this->set_option('use_more_text', $form_state['values']['use_more_text']);
2485
      case 'distinct':
2486
        $this->set_option($section, $form_state['values'][$section]);
2487
        break;
2488

    
2489
      case 'group_by':
2490
        $this->set_option($section, $form_state['values'][$section]);
2491
        break;
2492

    
2493
      case 'row_plugin':
2494
        // This if prevents resetting options to default if they don't change
2495
        // the plugin.
2496
        if ($this->get_option($section) != $form_state['values'][$section]) {
2497
          $plugin = views_get_plugin('row', $form_state['values'][$section]);
2498
          if ($plugin) {
2499
            $this->set_option($section, $form_state['values'][$section]);
2500
            $this->set_option('row_options', array());
2501

    
2502
            // send ajax form to options page if we use it.
2503
            if (!empty($plugin->definition['uses options'])) {
2504
              views_ui_add_form_to_stack('display', $this->view, $this->display->id, array('row_options'));
2505
            }
2506
          }
2507
        }
2508
        break;
2509

    
2510
      case 'style_plugin':
2511
        // This if prevents resetting options to default if they don't change
2512
        // the plugin.
2513
        if ($this->get_option($section) != $form_state['values'][$section]) {
2514
          $plugin = views_get_plugin('style', $form_state['values'][$section]);
2515
          if ($plugin) {
2516
            $this->set_option($section, $form_state['values'][$section]);
2517
            $this->set_option('style_options', array());
2518
            // send ajax form to options page if we use it.
2519
            if (!empty($plugin->definition['uses options'])) {
2520
              views_ui_add_form_to_stack('display', $this->view, $this->display->id, array('style_options'));
2521
            }
2522
          }
2523
        }
2524
        break;
2525

    
2526
      case 'style_options':
2527
        $style = TRUE;
2528
      case 'row_options':
2529
        // if row, $style will be empty.
2530
        $plugin = $this->get_plugin(empty($style) ? 'row' : 'style');
2531
        if ($plugin) {
2532
          $plugin->options_submit($form['options'][$section], $form_state);
2533
        }
2534
        $this->set_option($section, $form_state['values'][$section]);
2535
        break;
2536

    
2537
      case 'exposed_block':
2538
        $this->set_option($section, (bool) $form_state['values'][$section]);
2539
        break;
2540

    
2541
      case 'exposed_form':
2542
        $exposed_form = $this->get_option('exposed_form');
2543
        if ($exposed_form['type'] != $form_state['values']['exposed_form']['type']) {
2544
          $plugin = views_get_plugin('exposed_form', $form_state['values']['exposed_form']['type']);
2545
          if ($plugin) {
2546
            $exposed_form = array('type' => $form_state['values']['exposed_form']['type'], 'options' => array());
2547
            $this->set_option('exposed_form', $exposed_form);
2548
            if (!empty($plugin->definition['uses options'])) {
2549
              views_ui_add_form_to_stack('display', $this->view, $this->display->id, array('exposed_form_options'));
2550
            }
2551
          }
2552
        }
2553
        break;
2554

    
2555
      case 'exposed_form_options':
2556
        $plugin = $this->get_plugin('exposed_form');
2557
        if ($plugin) {
2558
          $exposed_form = $this->get_option('exposed_form');
2559
          $plugin->options_submit($form['exposed_form_options'], $form_state);
2560
          $exposed_form['options'] = $form_state['values'][$section];
2561
          $this->set_option('exposed_form', $exposed_form);
2562
        }
2563
        break;
2564

    
2565
      case 'pager':
2566
        $pager = $this->get_option('pager');
2567
        if ($pager['type'] != $form_state['values']['pager']['type']) {
2568
          $plugin = views_get_plugin('pager', $form_state['values']['pager']['type']);
2569
          if ($plugin) {
2570
            // Because pagers have very similar options, let's allow pagers to
2571
            // try to carry the options over.
2572
            $plugin->init($this->view, $this->display, $pager['options']);
2573

    
2574
            $pager = array('type' => $form_state['values']['pager']['type'], 'options' => $plugin->options);
2575
            $this->set_option('pager', $pager);
2576
            if (!empty($plugin->definition['uses options'])) {
2577
              views_ui_add_form_to_stack('display', $this->view, $this->display->id, array('pager_options'));
2578
            }
2579
          }
2580
        }
2581
        break;
2582

    
2583
      case 'pager_options':
2584
        $plugin = $this->get_plugin('pager');
2585
        if ($plugin) {
2586
          $pager = $this->get_option('pager');
2587
          $plugin->options_submit($form['pager_options'], $form_state);
2588
          $pager['options'] = $form_state['values'][$section];
2589
          $this->set_option('pager', $pager);
2590
        }
2591
        break;
2592
    }
2593

    
2594
    foreach ($this->extender as $extender) {
2595
      $extender->options_submit($form, $form_state);
2596
    }
2597
  }
2598

    
2599
  /**
2600
   * If override/revert was clicked, perform the proper toggle.
2601
   */
2602
  public function options_override($form, &$form_state) {
2603
    $this->set_override($form_state['section']);
2604
  }
2605

    
2606
  /**
2607
   * Flip the override setting for the given section.
2608
   *
2609
   * @param string $section
2610
   *   Which option should be marked as overridden, for example "filters".
2611
   * @param bool $new_state
2612
   *   Select the new state of the option.
2613
   *     - TRUE: Revert to default.
2614
   *     - FALSE: Mark it as overridden.
2615
   */
2616
  public function set_override($section, $new_state = NULL) {
2617
    $options = $this->defaultable_sections($section);
2618
    if (!$options) {
2619
      return;
2620
    }
2621

    
2622
    if (!isset($new_state)) {
2623
      $new_state = empty($this->options['defaults'][$section]);
2624
    }
2625

    
2626
    // For each option that is part of this group, fix our settings.
2627
    foreach ($options as $option) {
2628
      if ($new_state) {
2629
        // Revert to defaults.
2630
        unset($this->options[$option]);
2631
        unset($this->display->display_options[$option]);
2632
      }
2633
      else {
2634
        // copy existing values into our display.
2635
        $this->options[$option] = $this->get_option($option);
2636
        $this->display->display_options[$option] = $this->options[$option];
2637
      }
2638
      $this->options['defaults'][$option] = $new_state;
2639
      $this->display->display_options['defaults'][$option] = $new_state;
2640
    }
2641
  }
2642

    
2643
  /**
2644
   * Inject anything into the query that the display handler needs.
2645
   */
2646
  public function query() {
2647
    foreach ($this->extender as $extender) {
2648
      $extender->query();
2649
    }
2650
  }
2651

    
2652
  /**
2653
   * Not all display plugins will support filtering.
2654
   */
2655
  public function render_filters() {
2656
  }
2657

    
2658
  /**
2659
   * Not all display plugins will suppert pager rendering.
2660
   */
2661
  public function render_pager() {
2662
    return TRUE;
2663
  }
2664

    
2665
  /**
2666
   * Render the 'more' link.
2667
   */
2668
  public function render_more_link() {
2669
    if ($this->use_more() && ($this->use_more_always() || (!empty($this->view->query->pager) && $this->view->query->pager->has_more_records()))) {
2670
      $path = $this->get_path();
2671

    
2672
      if ($this->get_option('link_display') == 'custom_url' && $override_path = $this->get_option('link_url')) {
2673
        $tokens = $this->get_arguments_tokens();
2674
        $path = strtr($override_path, $tokens);
2675
      }
2676

    
2677
      if ($path) {
2678
        if (empty($override_path)) {
2679
          $path = $this->view->get_url(NULL, $path);
2680
        }
2681
        $url_options = array();
2682
        if (!empty($this->view->exposed_raw_input)) {
2683
          $url_options['query'] = $this->view->exposed_raw_input;
2684
        }
2685
        $theme = views_theme_functions('views_more', $this->view, $this->display);
2686

    
2687
        $parsed_url = drupal_parse_url($path);
2688
        // Preserve the query string from url.
2689
        if (!empty($parsed_url['query'])) {
2690
          if (!empty($url_options['query'])) {
2691
            $url_options['query'] = array_merge($parsed_url['query'], $url_options['query']);
2692
          }
2693
          else {
2694
            $url_options['query'] = $parsed_url['query'];
2695
          }
2696
          $path = $parsed_url['path'];
2697
        }
2698
        // Add fragment if applicable.
2699
        if (!empty($parsed_url['fragment'])) {
2700
          $url_options['fragment'] = $parsed_url['fragment'];
2701
        }
2702

    
2703
        $path = check_url(url($path, $url_options));
2704

    
2705
        return theme($theme, array('more_url' => $path, 'new_window' => $this->use_more_open_new_window(), 'link_text' => check_plain($this->use_more_text()), 'view' => $this->view));
2706
      }
2707
    }
2708
  }
2709

    
2710
  /**
2711
   * Legacy functions.
2712
   */
2713

    
2714
  /**
2715
   * Render the header of the view.
2716
   */
2717
  public function render_header() {
2718
    $empty = !empty($this->view->result);
2719
    return $this->render_area('header', $empty);
2720
  }
2721

    
2722
  /**
2723
   * Render the footer of the view.
2724
   */
2725
  public function render_footer() {
2726
    $empty = !empty($this->view->result);
2727
    return $this->render_area('footer', $empty);
2728
  }
2729

    
2730
  /**
2731
   *
2732
   */
2733
  public function render_empty() {
2734
    return $this->render_area('empty');
2735
  }
2736

    
2737
  /**
2738
   * If this display creates a block, implement one of these.
2739
   */
2740
  public function hook_block_list($delta = 0, $edit = array()) {
2741
    return array();
2742
  }
2743

    
2744
  /**
2745
   * If this display creates a page with a menu item, implement it here.
2746
   */
2747
  public function hook_menu() {
2748
    return array();
2749
  }
2750

    
2751
  /**
2752
   * Render this display.
2753
   */
2754
  public function render() {
2755
    return theme($this->theme_functions(), array('view' => $this->view));
2756
  }
2757

    
2758
  /**
2759
   *
2760
   */
2761
  public function render_area($area, $empty = FALSE) {
2762
    $return = '';
2763
    foreach ($this->get_handlers($area) as $area) {
2764
      $return .= $area->render($empty);
2765
    }
2766
    return $return;
2767
  }
2768

    
2769
  /**
2770
   * Determine if the user has access to this display of the view.
2771
   */
2772
  public function access($account = NULL) {
2773
    if (!isset($account)) {
2774
      global $user;
2775
      $account = $user;
2776
    }
2777

    
2778
    // Full override.
2779
    if (user_access('access all views', $account)) {
2780
      return TRUE;
2781
    }
2782

    
2783
    $plugin = $this->get_plugin('access');
2784
    if ($plugin) {
2785
      return $plugin->access($account);
2786
    }
2787

    
2788
    // Fallback to all access if no plugin.
2789
    return TRUE;
2790
  }
2791

    
2792
  /**
2793
   * Set up any variables on the view prior to execution. These are separated
2794
   * from execute because they are extremely common and unlikely to be
2795
   * overridden on an individual display.
2796
   */
2797
  public function pre_execute() {
2798
    $this->view->set_use_ajax($this->use_ajax());
2799
    if ($this->use_more() && !$this->use_more_always()) {
2800
      $this->view->get_total_rows = TRUE;
2801
    }
2802
    $this->view->init_handlers();
2803
    if ($this->uses_exposed()) {
2804
      $exposed_form = $this->get_plugin('exposed_form');
2805
      $exposed_form->pre_execute();
2806
    }
2807

    
2808
    foreach ($this->extender as $extender) {
2809
      $extender->pre_execute();
2810
    }
2811

    
2812
    if ($this->get_option('hide_admin_links')) {
2813
      $this->view->hide_admin_links = TRUE;
2814
    }
2815
  }
2816

    
2817
  /**
2818
   * When used externally, this is how a view gets run and returns
2819
   * data in the format required.
2820
   *
2821
   * The base class cannot be executed.
2822
   */
2823
  public function execute() {
2824
  }
2825

    
2826
  /**
2827
   * Fully render the display.
2828
   *
2829
   * Used for the purposes of a live preview or some other AJAXy reason.
2830
   */
2831
  public function preview() {
2832
    return $this->view->render();
2833
  }
2834

    
2835
  /**
2836
   * Displays can require a certain type of style plugin.
2837
   *
2838
   * By default, they will be 'normal'.
2839
   */
2840
  public function get_style_type() {
2841
    return 'normal';
2842
  }
2843

    
2844
  /**
2845
   * Make sure the display and all associated handlers are valid.
2846
   *
2847
   * @return
2848
   *   Empty array if the display is valid; an array of error strings if it is
2849
   *   not.
2850
   */
2851
  public function validate() {
2852
    $errors = array();
2853
    // Make sure displays that use fields HAVE fields.
2854
    if ($this->uses_fields()) {
2855
      $fields = FALSE;
2856
      foreach ($this->get_handlers('field') as $field) {
2857
        if (empty($field->options['exclude'])) {
2858
          $fields = TRUE;
2859
        }
2860
      }
2861

    
2862
      if (!$fields) {
2863
        $errors[] = t('Display "@display" uses fields but there are none defined for it or all are excluded.', array('@display' => $this->display->display_title));
2864
      }
2865
    }
2866

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

    
2871
    // Validate style plugin.
2872
    $style = $this->get_plugin();
2873
    if (empty($style)) {
2874
      $errors[] = t('Display "@display" has an invalid style plugin.', array('@display' => $this->display->display_title));
2875
    }
2876
    else {
2877
      $result = $style->validate();
2878
      if (!empty($result) && is_array($result)) {
2879
        $errors = array_merge($errors, $result);
2880
      }
2881
    }
2882

    
2883
    // Validate query plugin.
2884
    $query = $this->get_plugin('query');
2885
    $result = $query->validate();
2886
    if (!empty($result) && is_array($result)) {
2887
      $errors = array_merge($errors, $result);
2888
    }
2889

    
2890
    // Validate handlers.
2891
    foreach (views_object_types() as $type => $info) {
2892
      foreach ($this->get_handlers($type) as $handler) {
2893
        $result = $handler->validate();
2894
        if (!empty($result) && is_array($result)) {
2895
          $errors = array_merge($errors, $result);
2896
        }
2897
      }
2898
    }
2899

    
2900
    return $errors;
2901
  }
2902

    
2903
  /**
2904
   * Check if the provided identifier is unique.
2905
   *
2906
   * @param string $id
2907
   *   The id of the handler which is checked.
2908
   * @param string $identifier
2909
   *   The actual get identifier configured in the exposed settings.
2910
   *
2911
   * @return bool
2912
   *   Returns whether the identifier is unique on all handlers.
2913
   */
2914
  public function is_identifier_unique($id, $identifier) {
2915
    foreach (views_object_types() as $type => $info) {
2916
      foreach ($this->get_handlers($type) as $key => $handler) {
2917
        if ($handler->can_expose() && $handler->is_exposed()) {
2918
          if ($handler->is_a_group()) {
2919
            if ($id != $key && $identifier == $handler->options['group_info']['identifier']) {
2920
              return FALSE;
2921
            }
2922
          }
2923
          else {
2924
            if ($id != $key && isset($handler->options['expose']['identifier']) && $identifier == $handler->options['expose']['identifier']) {
2925
              return FALSE;
2926
            }
2927
          }
2928
        }
2929
      }
2930
    }
2931
    return TRUE;
2932
  }
2933

    
2934
  /**
2935
   * Provide the block system with any exposed widget blocks for this display.
2936
   */
2937
  public function get_special_blocks() {
2938
    $blocks = array();
2939

    
2940
    if ($this->uses_exposed_form_in_block()) {
2941
      $delta = '-exp-' . $this->view->name . '-' . $this->display->id;
2942
      $desc = t('Exposed form: @view-@display_id', array('@view' => $this->view->name, '@display_id' => $this->display->id));
2943

    
2944
      $blocks[$delta] = array(
2945
        'info' => $desc,
2946
        'cache' => DRUPAL_NO_CACHE,
2947
      );
2948
    }
2949

    
2950
    return $blocks;
2951
  }
2952

    
2953
  /**
2954
   * Render any special blocks provided for this display.
2955
   */
2956
  public function view_special_blocks($type) {
2957
    if ($type == '-exp') {
2958
      // avoid interfering with the admin forms.
2959
      if (arg(0) == 'admin' && arg(1) == 'structure' && arg(2) == 'views') {
2960
        return;
2961
      }
2962
      $this->view->init_handlers();
2963

    
2964
      if ($this->uses_exposed() && $this->get_option('exposed_block')) {
2965
        $exposed_form = $this->get_plugin('exposed_form');
2966
        return array(
2967
          'content' => $exposed_form->render_exposed_form(TRUE),
2968
        );
2969
      }
2970
    }
2971
  }
2972

    
2973
  /**
2974
   * Override of export_option()
2975
   *
2976
   * Because displays do not want to export options that are NOT overridden from
2977
   * the default display, we need some special handling during the export
2978
   * process.
2979
   */
2980
  public function export_option($indent, $prefix, $storage, $option, $definition, $parents) {
2981
    // The $prefix is wrong because we store our actual options a little
2982
    // differently.
2983
    $prefix = '$handler->display->display_options';
2984
    $output = '';
2985
    if (!$parents && !$this->is_default_display()) {
2986
      // Do not export items that are not overridden.
2987
      if ($this->is_defaulted($option)) {
2988
        return;
2989
      }
2990

    
2991
      // If this is not defaulted and is overrideable, flip the switch to say
2992
      // this is overridden.
2993
      if ($this->defaultable_sections($option)) {
2994
        $output .= $indent . $prefix . "['defaults']['$option'] = FALSE;\n";
2995
      }
2996
    }
2997

    
2998
    $output .= parent::export_option($indent, $prefix, $storage, $option, $definition, $parents);
2999
    return $output;
3000
  }
3001

    
3002
  /**
3003
   * Special method to export items that have handlers.
3004
   *
3005
   * This method was specified in the option_definition() as the method to
3006
   * utilize to export fields, filters, sort criteria, relationships and
3007
   * arguments. This passes the export off to the individual handlers so that
3008
   * they can export themselves properly.
3009
   */
3010
  public function export_handler($indent, $prefix, $storage, $option, $definition, $parents) {
3011
    $output = '';
3012

    
3013
    // Cut the 's' off because the data is stored as the plural form but we need
3014
    // the singular form. Who designed that anyway? Oh yeah, I did. :(
3015
    if ($option != 'header' && $option != 'footer' && $option != 'empty') {
3016
      $type = substr($option, 0, -1);
3017
    }
3018
    else {
3019
      $type = $option;
3020
    }
3021
    $types = views_object_types();
3022
    foreach ($storage[$option] as $id => $info) {
3023
      if (!empty($types[$type]['type'])) {
3024
        $handler_type = $types[$type]['type'];
3025
      }
3026
      else {
3027
        $handler_type = $type;
3028
      }
3029
      // If aggregation is on, the group type might override the actual
3030
      // handler that is in use. This piece of code checks that and,
3031
      // if necessary, sets the override handler.
3032
      $override = NULL;
3033
      if ($this->use_group_by() && !empty($info['group_type'])) {
3034
        if (empty($this->view->query)) {
3035
          $this->view->init_query();
3036
        }
3037
        $aggregate = $this->view->query->get_aggregation_info();
3038
        if (!empty($aggregate[$info['group_type']]['handler'][$type])) {
3039
          $override = $aggregate[$info['group_type']]['handler'][$type];
3040
        }
3041
      }
3042
      $handler = views_get_handler($info['table'], $info['field'], $handler_type, $override);
3043
      if ($handler) {
3044
        $handler->init($this->view, $info);
3045
        $output .= $indent . '/* ' . $types[$type]['stitle'] . ': ' . $handler->ui_name() . " */\n";
3046
        $output .= $handler->export_options($indent, $prefix . "['$option']['$id']");
3047
      }
3048

    
3049
      // Prevent reference problems.
3050
      unset($handler);
3051
    }
3052

    
3053
    return $output;
3054
  }
3055

    
3056
  /**
3057
   * Special handling for the style export.
3058
   *
3059
   * Styles are stored as style_plugin and style_options or row_plugin and
3060
   * row_options accordingly. The options are told not to export, and the
3061
   * export for the plugin should export both.
3062
   */
3063
  public function export_style($indent, $prefix, $storage, $option, $definition, $parents) {
3064
    $output = '';
3065
    $style_plugin = $this->get_plugin();
3066
    if ($option == 'style_plugin') {
3067
      $type = 'style';
3068
      $options_field = 'style_options';
3069
      $plugin = $style_plugin;
3070
    }
3071
    else {
3072
      if (!$style_plugin || !$style_plugin->uses_row_plugin()) {
3073
        return;
3074
      }
3075

    
3076
      $type = 'row';
3077
      $options_field = 'row_options';
3078
      $plugin = $this->get_plugin('row');
3079
      // If the style plugin doesn't use row plugins, don't even bother.
3080
    }
3081

    
3082
    if ($plugin) {
3083
      // Write which plugin to use.
3084
      $value = $this->get_option($option);
3085
      $output .= $indent . $prefix . "['$option'] = '$value';\n";
3086

    
3087
      // Pass off to the plugin to export itself.
3088
      $output .= $plugin->export_options($indent, $prefix . "['$options_field']");
3089
    }
3090

    
3091
    return $output;
3092
  }
3093

    
3094
  /**
3095
   * Special handling for plugin export
3096
   *
3097
   * Plugins other than styles are stored in array with 'type' being the key
3098
   * to the plugin. For modern plugins, the options are stored in the 'options'
3099
   * array, but for legacy plugins (access and cache) options are stored as
3100
   * siblings to the type.
3101
   */
3102
  public function export_plugin($indent, $prefix, $storage, $option, $definition, $parents) {
3103
    $output = '';
3104
    $plugin_type = end($parents);
3105
    $plugin = $this->get_plugin($plugin_type);
3106
    if ($plugin) {
3107
      // Write which plugin to use.
3108
      $value = $storage[$option];
3109
      $new_prefix = $prefix . "['$plugin_type']";
3110

    
3111
      $output .= $indent . $new_prefix . "['$option'] = '$value';\n";
3112

    
3113
      if ($plugin_type != 'access' && $plugin_type!= 'cache') {
3114
        $new_prefix .= "['options']";
3115
      }
3116

    
3117
      // Pass off to the plugin to export itself.
3118
      $output .= $plugin->export_options($indent, $new_prefix);
3119
    }
3120

    
3121
    return $output;
3122
  }
3123

    
3124
  /**
3125
   *
3126
   */
3127
  public function unpack_style($indent, $prefix, $storage, $option, $definition, $parents) {
3128
    $output = '';
3129
    $style_plugin = $this->get_plugin();
3130
    if ($option == 'style_plugin') {
3131
      $type = 'style';
3132
      $options_field = 'style_options';
3133
      $plugin = $style_plugin;
3134
    }
3135
    else {
3136
      if (!$style_plugin || !$style_plugin->uses_row_plugin()) {
3137
        return;
3138
      }
3139

    
3140
      $type = 'row';
3141
      $options_field = 'row_options';
3142
      $plugin = $this->get_plugin('row');
3143
      // If the style plugin doesn't use row plugins, don't even bother.
3144
    }
3145

    
3146
    if ($plugin) {
3147
      return $plugin->unpack_translatables($translatable, $parents);
3148
    }
3149
  }
3150

    
3151
  /**
3152
   * Special handling for plugin unpacking.
3153
   */
3154
  public function unpack_plugin(&$translatable, $storage, $option, $definition, $parents) {
3155
    $plugin_type = end($parents);
3156
    $plugin = $this->get_plugin($plugin_type);
3157
    if ($plugin) {
3158
      // Write which plugin to use.
3159
      return $plugin->unpack_translatables($translatable, $parents);
3160
    }
3161
  }
3162

    
3163
  /**
3164
   * Special method to unpack items that have handlers.
3165
   *
3166
   * This method was specified in the option_definition() as the method to
3167
   * utilize to export fields, filters, sort criteria, relationships and
3168
   * arguments. This passes the export off to the individual handlers so that
3169
   * they can export themselves properly.
3170
   */
3171
  public function unpack_handler(&$translatable, $storage, $option, $definition, $parents) {
3172
    $output = '';
3173

    
3174
    // Cut the 's' off because the data is stored as the plural form but we need
3175
    // the singular form. Who designed that anyway? Oh yeah, I did. :(
3176
    if ($option != 'header' && $option != 'footer' && $option != 'empty') {
3177
      $type = substr($option, 0, -1);
3178
    }
3179
    else {
3180
      $type = $option;
3181
    }
3182
    $types = views_object_types();
3183
    foreach ($storage[$option] as $id => $info) {
3184
      if (!empty($types[$type]['type'])) {
3185
        $handler_type = $types[$type]['type'];
3186
      }
3187
      else {
3188
        $handler_type = $type;
3189
      }
3190
      $handler = views_get_handler($info['table'], $info['field'], $handler_type);
3191
      if ($handler) {
3192
        $handler->init($this->view, $info);
3193
        $handler->unpack_translatables($translatable, array_merge($parents, array($type, $info['table'], $info['id'])));
3194
      }
3195

    
3196
      // Prevent reference problems.
3197
      unset($handler);
3198
    }
3199

    
3200
    return $output;
3201
  }
3202

    
3203
  /**
3204
   * Provide some helpful text for the arguments.
3205
   *
3206
   * The result should contain of an array with
3207
   *   - filter value present: The title of the fieldset in the argument
3208
   *     where you can configure what should be done with a given argument.
3209
   *   - filter value not present: The tiel of the fieldset in the argument
3210
   *     where you can configure what should be done if the argument does not
3211
   *     exist.
3212
   *   - description: A description about how arguments comes to the display.
3213
   *     For example blocks don't get it from url.
3214
   */
3215
  public function get_argument_text() {
3216
    return array(
3217
      'filter value not present' => t('When the filter value is <em>NOT</em> available'),
3218
      'filter value present' => t('When the filter value <em>IS</em> available or a default is provided'),
3219
      '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'."),
3220
    );
3221
  }
3222

    
3223
  /**
3224
   * Provide some helpful text for pagers.
3225
   *
3226
   * The result should contain of an array within
3227
   *   - items per page title
3228
   */
3229
  public function get_pager_text() {
3230
    return array(
3231
      'items per page title' => t('Items to display'),
3232
      'items per page description' => t('The number of items to display. Enter 0 for no limit.')
3233
    );
3234
  }
3235

    
3236
}
3237

    
3238
/**
3239
 * @}
3240
 */