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 @ ef1afbb9

1
<?php
2

    
3
/**
4
 * @file
5
 * Contains the base display plugin.
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
   * The top object of a view.
28
   *
29
   * @var view
30
   */
31
  var $view = NULL;
32

    
33
  var $handlers = array();
34

    
35
  /**
36
   * Stores all available display extenders.
37
   */
38
  var $extender = array();
39

    
40
  function init(&$view, &$display, $options = NULL) {
41
    $this->view = &$view;
42
    $this->display = &$display;
43

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

    
61
    // Track changes that the user should know about.
62
    $changed = FALSE;
63

    
64
    // Make some modifications:
65
    if (!isset($options) && isset($display->display_options)) {
66
      $options = $display->display_options;
67
    }
68

    
69
    if ($this->is_default_display() && isset($options['defaults'])) {
70
      unset($options['defaults']);
71
    }
72

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

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

    
123
      // Setup the pager options.
124
      $pager = array(
125
        'type' => $type,
126
        'options' => array(
127
          'offset' => intval($offset)
128
        ),
129
      );
130

    
131
      if ($items_per_page) {
132
        $pager['options']['items_per_page'] = $items_per_page;
133
      }
134
      // Setup the pager element.
135
      if ($id = $this->get_option('pager_element')) {
136
        $pager['options']['id'] = $id;
137
      }
138

    
139
      // Unset the previous options
140
      // After edit and save the view they will be erased
141
      $this->set_option('items_per_page', NULL);
142
      $this->set_option('offset', NULL);
143
      $this->set_option('use_pager', NULL);
144
      $this->set_option('pager', $pager);
145
      $changed = TRUE;
146
    }
147

    
148

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

    
170
            if ($area != 'empty' && $empty = $this->get_option($area . '_empty')) {
171
              $options['empty'] = $empty;
172
            }
173
            $this->set_option($area, array('text' => $options));
174
            $converted = TRUE;
175
            $changed = TRUE;
176
          }
177
        }
178
        // Ensure that options are at least an empty array
179
        if (!$converted) {
180
          $this->set_option($area, array());
181
        }
182
      }
183
    }
184

    
185
    // Convert distinct setting from display to query settings.
186
    $distinct = $this->get_option('distinct');
187
    if (!empty($distinct)) {
188
      $query_settings = $this->get_option('query');
189
      $query_settings['options']['distinct'] = $distinct;
190
      $this->set_option('query', $query_settings);
191
      // Clear the values
192
      $this->set_option('distinct', NULL);
193
      $changed = TRUE;
194
    }
195

    
196
    // Convert field language settings.
197
    $query_options = $this->get_option('query');
198
    if (isset($query_options['options']['field_language'])) {
199
      $this->set_option('field_language', $query_options['options']['field_language']);
200
      unset($query_options['options']['field_language']);
201
      $changed = TRUE;
202
    }
203
    if (isset($query_options['options']['field_language_add_to_query'])) {
204
      $this->set_option('field_language_add_to_query', $query_options['options']['field_language_add_to_query']);
205
      unset($query_options['options']['field_language_add_to_query']);
206
      $changed = TRUE;
207
    }
208
    $this->set_option('query', $query_options);
209

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

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

    
247
      $changed = TRUE;
248
    }
249

    
250
    // Mark the view as changed so the user has a chance to save it.
251
    if ($changed) {
252
      $this->view->changed = TRUE;
253
    }
254
  }
255

    
256
  function destroy() {
257
    parent::destroy();
258

    
259
    foreach ($this->handlers as $type => $handlers) {
260
      foreach ($handlers as $id => $handler) {
261
        if (is_object($handler)) {
262
          $this->handlers[$type][$id]->destroy();
263
        }
264
      }
265
    }
266

    
267
    if (isset($this->default_display)) {
268
      unset($this->default_display);
269
    }
270

    
271
    foreach ($this->extender as $extender) {
272
      $extender->destroy();
273
    }
274
  }
275

    
276
  /**
277
   * Determine if this display is the 'default' display which contains
278
   * fallback settings
279
   */
280
  function is_default_display() { return FALSE; }
281

    
282
  /**
283
   * Determine if this display uses exposed filters, so the view
284
   * will know whether or not to build them.
285
   */
286
  function uses_exposed() {
287
    if (!isset($this->has_exposed)) {
288
      foreach ($this->handlers as $type => $value) {
289
        foreach ($this->view->$type as $id => $handler) {
290
          if ($handler->can_expose() && $handler->is_exposed()) {
291
            // one is all we need; if we find it, return true.
292
            $this->has_exposed = TRUE;
293
            return TRUE;
294
          }
295
        }
296
      }
297
      $pager = $this->get_plugin('pager');
298
      if (isset($pager) && $pager->uses_exposed()) {
299
        $this->has_exposed = TRUE;
300
        return TRUE;
301
      }
302
      $this->has_exposed = FALSE;
303
    }
304

    
305
    return $this->has_exposed;
306
  }
307

    
308
  /**
309
   * Determine if this display should display the exposed
310
   * filters widgets, so the view will know whether or not
311
   * to render them.
312
   *
313
   * Regardless of what this function
314
   * returns, exposed filters will not be used nor
315
   * displayed unless uses_exposed() returns TRUE.
316
   */
317
  function displays_exposed() {
318
    return TRUE;
319
  }
320

    
321
  /**
322
   * Does the display use AJAX?
323
   */
324
  function use_ajax() {
325
    if (!empty($this->definition['use ajax'])) {
326
      return $this->get_option('use_ajax');
327
    }
328
    return FALSE;
329
  }
330

    
331
  /**
332
   * Does the display have a pager enabled?
333
   */
334
  function use_pager() {
335
    $pager = $this->get_plugin('pager');
336
    if ($pager) {
337
      return $pager->use_pager();
338
    }
339
  }
340

    
341
  /**
342
   * Does the display have a more link enabled?
343
   */
344
  function use_more() {
345
    if (!empty($this->definition['use more'])) {
346
      return $this->get_option('use_more');
347
    }
348
    return FALSE;
349
  }
350

    
351
  /**
352
   * Does the display have groupby enabled?
353
   */
354
  function use_group_by() {
355
    return $this->get_option('group_by');
356
  }
357

    
358
  /**
359
   * Should the enabled display more link be shown when no more items?
360
   */
361
  function use_more_always() {
362
    if (!empty($this->definition['use more'])) {
363
      return $this->get_option('use_more_always');
364
    }
365
    return FALSE;
366
  }
367

    
368
  /**
369
   * Should the enabled display more link being opened in an new window?
370
   *
371
   * @codingStandardsIgnoreStart
372
   */
373
  function use_more_open_new_window() {
374
    // @codingStandardsIgnoreEnd
375
    if (!empty($this->definition['use more'])) {
376
      return $this->get_option('open_new_window');
377
    }
378
    return FALSE;
379
  }
380

    
381
  /**
382
   * Does the display have custom link text?
383
   */
384
  function use_more_text() {
385
    if (!empty($this->definition['use more'])) {
386
      return $this->get_option('use_more_text');
387
    }
388
    return FALSE;
389
  }
390

    
391
  /**
392
   * Can this display accept attachments?
393
   */
394
  function accept_attachments() {
395
    if (empty($this->definition['accept attachments'])) {
396
      return FALSE;
397
    }
398
    if (!empty($this->view->argument) && $this->get_option('hide_attachment_summary')) {
399
      foreach ($this->view->argument as $argument_id => $argument) {
400
        if ($argument->needs_style_plugin() && empty($argument->argument_validated)) {
401
          return FALSE;
402
        }
403
      }
404
    }
405
    return TRUE;
406
  }
407

    
408
  /**
409
   * Allow displays to attach to other views.
410
   */
411
  function attach_to($display_id) { }
412

    
413
  /**
414
   * Static member function to list which sections are defaultable
415
   * and what items each section contains.
416
   */
417
  function defaultable_sections($section = NULL) {
418
    $sections = array(
419
      'access' => array('access', 'access_options'),
420
      'access_options' => array('access', 'access_options'),
421
      'cache' => array('cache', 'cache_options'),
422
      'cache_options' => array('cache', 'cache_options'),
423
      'title' => array('title'),
424
      'css_class' => array('css_class'),
425
      'use_ajax' => array('use_ajax'),
426
      'hide_attachment_summary' => array('hide_attachment_summary'),
427
      'hide_admin_links' => array('hide_admin_links'),
428
      'group_by' => array('group_by'),
429
      'query' => array('query'),
430
      'use_more' => array('use_more',
431
        'use_more_always',
432
        'open_new_window',
433
        'use_more_text',
434
      ),
435
      'use_more_always' => array(
436
        'use_more',
437
        'use_more_always',
438
        'open_new_window',
439
        'use_more_text',
440
      ),
441
      'use_more_text' => array(
442
        'use_more',
443
        'use_more_always',
444
        'open_new_window',
445
        'use_more_text',
446
      ),
447
      'open_new_window' => array(
448
        'use_more',
449
        'use_more_always',
450
        'open_new_window',
451
        'use_more_text',
452
      ),
453
      'link_display' => array('link_display', 'link_url'),
454

    
455
      // Force these to cascade properly.
456
      'style_plugin' => array('style_plugin', 'style_options', 'row_plugin', 'row_options'),
457
      'style_options' => array('style_plugin', 'style_options', 'row_plugin', 'row_options'),
458
      'row_plugin' => array('style_plugin', 'style_options', 'row_plugin', 'row_options'),
459
      'row_options' => array('style_plugin', 'style_options', 'row_plugin', 'row_options'),
460

    
461
      'pager' => array('pager', 'pager_options'),
462
      'pager_options' => array('pager', 'pager_options'),
463

    
464
      'exposed_form' => array('exposed_form', 'exposed_form_options'),
465
      'exposed_form_options' => array('exposed_form', 'exposed_form_options'),
466

    
467
      // These guys are special
468
      'header' => array('header'),
469
      'footer' => array('footer'),
470
      'empty' => array('empty'),
471
      'relationships' => array('relationships'),
472
      'fields' => array('fields'),
473
      'sorts' => array('sorts'),
474
      'arguments' => array('arguments'),
475
      'filters' => array('filters', 'filter_groups'),
476
      'filter_groups' => array('filters', 'filter_groups'),
477
    );
478

    
479
    // If the display cannot use a pager, then we cannot default it.
480
    if (empty($this->definition['use pager'])) {
481
      unset($sections['pager']);
482
      unset($sections['items_per_page']);
483
    }
484

    
485
    foreach ($this->extender as $extender) {
486
      $extender->defaultable_sections($sections, $section);
487
    }
488

    
489
    if ($section) {
490
      if (!empty($sections[$section])) {
491
        return $sections[$section];
492
      }
493
    }
494
    else {
495
      return $sections;
496
    }
497
  }
498

    
499
  function option_definition() {
500
    $options = array(
501
      'defaults' => array(
502
        'default' => array(
503
          'access' => TRUE,
504
          'cache' => TRUE,
505
          'query' => TRUE,
506
          'title' => TRUE,
507
          'css_class' => TRUE,
508

    
509
          'display_description' => FALSE,
510
          'use_ajax' => TRUE,
511
          'hide_attachment_summary' => TRUE,
512
          'hide_admin_links' => TRUE,
513
          'pager' => TRUE,
514
          'pager_options' => TRUE,
515
          'use_more' => TRUE,
516
          'use_more_always' => TRUE,
517
          'open_new_window' => FALSE,
518
          'use_more_text' => TRUE,
519
          'exposed_form' => TRUE,
520
          'exposed_form_options' => TRUE,
521

    
522
          'link_display' => TRUE,
523
          'link_url' => '',
524
          'group_by' => TRUE,
525

    
526
          'style_plugin' => TRUE,
527
          'style_options' => TRUE,
528
          'row_plugin' => TRUE,
529
          'row_options' => TRUE,
530

    
531
          'header' => TRUE,
532
          'footer' => TRUE,
533
          'empty' => TRUE,
534

    
535
          'relationships' => TRUE,
536
          'fields' => TRUE,
537
          'sorts' => TRUE,
538
          'arguments' => TRUE,
539
          'filters' => TRUE,
540
          'filter_groups' => TRUE,
541
        ),
542
        'export' => FALSE,
543
      ),
544

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

    
621
      // These types are all plugins that can have individual settings
622
      // and therefore need special handling.
623
      'access' => array(
624
        'contains' => array(
625
          'type' => array('default' => 'none', 'export' => 'export_plugin', 'unpack_translatable' => 'unpack_plugin'),
626
         ),
627
      ),
628
      'cache' => array(
629
        'contains' => array(
630
          'type' => array('default' => 'none', 'export' => 'export_plugin', 'unpack_translatable' => 'unpack_plugin'),
631
         ),
632
      ),
633
      'query' => array(
634
        'contains' => array(
635
          'type' => array('default' => 'views_query', 'export' => 'export_plugin'),
636
          'options' => array('default' => array(), 'export' => FALSE),
637
         ),
638
      ),
639
      // Note that exposed_form plugin has options in a separate array,
640
      // while access and cache do not. access and cache are legacy and
641
      // that pattern should not be repeated, but it is left as is to
642
      // reduce the need to modify older views. Let's consider the
643
      // pattern used here to be the template from which future plugins
644
      // should be copied.
645
      'exposed_form' => array(
646
        'contains' => array(
647
          'type' => array('default' => 'basic', 'export' => 'export_plugin', 'unpack_translatable' => 'unpack_plugin'),
648
          'options' => array('default' => array(), 'export' => FALSE),
649
         ),
650
      ),
651
      'pager' => array(
652
        'contains' => array(
653
          'type' => array('default' => 'full', 'export' => 'export_plugin', 'unpack_translatable' => 'unpack_plugin'),
654
          'options' => array('default' => array(), 'export' => FALSE),
655
         ),
656
      ),
657

    
658
      // Note that the styles have their options completely independent.
659
      // Like access and cache above, this is a legacy pattern and
660
      // should not be repeated.
661
      'style_plugin' => array(
662
        'default' => 'default',
663
        'export' => 'export_style',
664
        'unpack_translatable' => 'unpack_style',
665
      ),
666
      'style_options' => array(
667
        'default' => array(),
668
        'export' => FALSE,
669
      ),
670
      'row_plugin' => array(
671
        'default' => 'fields',
672
        'export' => 'export_style',
673
        'unpack_translatable' => 'unpack_style',
674
      ),
675
      'row_options' => array(
676
        'default' => array(),
677
        'export' => FALSE,
678
      ),
679

    
680
      'exposed_block' => array(
681
        'default' => FALSE,
682
      ),
683

    
684
      'header' => array(
685
        'default' => array(),
686
        'export' => 'export_handler',
687
        'unpack_translatable' => 'unpack_handler',
688
      ),
689
      'footer' => array(
690
        'default' => array(),
691
        'export' => 'export_handler',
692
        'unpack_translatable' => 'unpack_handler',
693
      ),
694
      'empty' => array(
695
        'default' => array(),
696
        'export' => 'export_handler',
697
        'unpack_translatable' => 'unpack_handler',
698
      ),
699

    
700
      // We want these to export last.
701
      // These are the 5 handler types.
702
      'relationships' => array(
703
        'default' => array(),
704
        'export' => 'export_handler',
705
        'unpack_translatable' => 'unpack_handler',
706

    
707
      ),
708
      'fields' => array(
709
        'default' => array(),
710
        'export' => 'export_handler',
711
        'unpack_translatable' => 'unpack_handler',
712
      ),
713
      'sorts' => array(
714
        'default' => array(),
715
        'export' => 'export_handler',
716
        'unpack_translatable' => 'unpack_handler',
717
      ),
718
      'arguments' => array(
719
        'default' => array(),
720
        'export' => 'export_handler',
721
        'unpack_translatable' => 'unpack_handler',
722
      ),
723
      'filter_groups' => array(
724
        'contains' => array(
725
          'operator' => array('default' => 'AND'),
726
          'groups' => array('default' => array(1 => 'AND')),
727
        ),
728
      ),
729
      'filters' => array(
730
        'default' => array(),
731
        'export' => 'export_handler',
732
        'unpack_translatable' => 'unpack_handler',
733
      ),
734
    );
735

    
736
    if (empty($this->definition['use pager'])) {
737
      $options['defaults']['default']['use_pager'] = FALSE;
738
      $options['defaults']['default']['items_per_page'] = FALSE;
739
      $options['defaults']['default']['offset'] = FALSE;
740
      $options['defaults']['default']['pager'] = FALSE;
741
      $options['pager']['contains']['type']['default'] = 'some';
742
    }
743

    
744
    if ($this->is_default_display()) {
745
      unset($options['defaults']);
746
    }
747

    
748
    foreach ($this->extender as $extender) {
749
      $extender->options_definition_alter($options);
750
    }
751

    
752
    return $options;
753
  }
754

    
755
  /**
756
   * Check to see if the display has a 'path' field.
757
   *
758
   * This is a pure function and not just a setting on the definition
759
   * because some displays (such as a panel pane) may have a path based
760
   * upon configuration.
761
   *
762
   * By default, displays do not have a path.
763
   */
764
  function has_path() { return FALSE; }
765

    
766
  /**
767
   * Check to see if the display has some need to link to another display.
768
   *
769
   * For the most part, displays without a path will use a link display. However,
770
   * sometimes displays that have a path might also need to link to another display.
771
   * This is true for feeds.
772
   */
773
  function uses_link_display() { return !$this->has_path(); }
774

    
775
  /**
776
   * Check to see if the display can put the exposed form in a block.
777
   *
778
   * By default, displays that do not have a path cannot disconnect
779
   * the exposed form and put it in a block, because the form has no
780
   * place to go and Views really wants the forms to go to a specific
781
   * page.
782
   */
783
  function uses_exposed_form_in_block() { return $this->has_path(); }
784

    
785
  /**
786
   * Check to see which display to use when creating links within
787
   * a view using this display.
788
   */
789
  function get_link_display() {
790
    $display_id = $this->get_option('link_display');
791
    // If unknown, pick the first one.
792
    if (empty($display_id) || empty($this->view->display[$display_id])) {
793
      foreach ($this->view->display as $display_id => $display) {
794
        if (!empty($display->handler) && $display->handler->has_path()) {
795
          return $display_id;
796
        }
797
      }
798
    }
799
    else {
800
      return $display_id;
801
    }
802
    // fall-through returns NULL
803
  }
804

    
805
  /**
806
   * Return the base path to use for this display.
807
   *
808
   * This can be overridden for displays that do strange things
809
   * with the path.
810
   */
811
  function get_path() {
812
    if ($this->has_path()) {
813
      return $this->get_option('path');
814
    }
815

    
816
    $display_id = $this->get_link_display();
817
    if ($display_id && !empty($this->view->display[$display_id]) && is_object($this->view->display[$display_id]->handler)) {
818
      return $this->view->display[$display_id]->handler->get_path();
819
    }
820

    
821
    if ($this->get_option('link_display') == 'custom_url' && $link_url = $this->get_option('link_url')) {
822
      return $link_url;
823
    }
824
  }
825

    
826
  function get_url() {
827
    return $this->view->get_url();
828
  }
829

    
830
  /**
831
   * Check to see if the display needs a breadcrumb
832
   *
833
   * By default, displays do not need breadcrumbs
834
   */
835
  function uses_breadcrumb() { return FALSE; }
836

    
837
  /**
838
   * Determine if a given option is set to use the default display or the
839
   * current display
840
   *
841
   * @return
842
   *   TRUE for the default display
843
   */
844
  function is_defaulted($option) {
845
    return !$this->is_default_display() && !empty($this->default_display) && !empty($this->options['defaults'][$option]);
846
  }
847

    
848
  /**
849
   * Intelligently get an option either from this display or from the
850
   * default display, if directed to do so.
851
   */
852
  function get_option($option) {
853
    if ($this->is_defaulted($option)) {
854
      return $this->default_display->get_option($option);
855
    }
856

    
857
    if (array_key_exists($option, $this->options)) {
858
      return $this->options[$option];
859
    }
860
  }
861

    
862
  /**
863
   * Determine if the display's style uses fields.
864
   */
865
  function uses_fields() {
866
    $plugin = $this->get_plugin();
867
    if ($plugin) {
868
      return $plugin->uses_fields();
869
    }
870
  }
871

    
872
  /**
873
   * Get the instance of a plugin, for example style or row.
874
   *
875
   * @param string $type
876
   *   The type of the plugin.
877
   * @param string $name
878
   *   The name of the plugin defined in hook_views_plugins.
879
   *
880
   * @return views_plugin|FALSE
881
   */
882
  function get_plugin($type = 'style', $name = NULL) {
883
    static $cache = array();
884
    if (!isset($cache[$type][$name])) {
885
      switch ($type) {
886
        case 'style':
887
        case 'row':
888
          $option_name = $type . '_plugin';
889
          $options = $this->get_option($type . '_options');
890
          if (!$name) {
891
            $name = $this->get_option($option_name);
892
          }
893

    
894
          break;
895
        case 'query':
896
          $views_data = views_fetch_data($this->view->base_table);
897
          $name = !empty($views_data['table']['base']['query class']) ? $views_data['table']['base']['query class'] : 'views_query';
898
        default:
899
          $option_name = $type;
900
          $options = $this->get_option($type);
901
          if (!$name) {
902
            $name = $options['type'];
903
          }
904

    
905
          // access & cache store their options as siblings with the
906
          // type; all others use an 'options' array.
907
          if ($type != 'access' && $type != 'cache') {
908
            $options = $options['options'];
909
          }
910
      }
911
      $plugin = views_get_plugin($type, $name);
912

    
913
      if (!$plugin) {
914
        return;
915
      }
916
      if ($type != 'query') {
917
        $plugin->init($this->view, $this->display, $options);
918
      }
919
      else {
920
        $display_id = $this->is_defaulted($option_name) ? $this->display->id : 'default';
921
        $plugin->localization_keys = array($display_id, $type);
922

    
923
        if (!isset($this->base_field)) {
924
          $views_data = views_fetch_data($this->view->base_table);
925
          $this->view->base_field = !empty($views_data['table']['base']['field']) ? $views_data['table']['base']['field'] : '';
926
        }
927
        $plugin->init($this->view->base_table, $this->view->base_field, $options);
928
      }
929
      $cache[$type][$name] = $plugin;
930
    }
931

    
932
    return $cache[$type][$name];
933
  }
934

    
935
  /**
936
   * Get the handler object for a single handler.
937
   */
938
  function &get_handler($type, $id) {
939
    if (!isset($this->handlers[$type])) {
940
      $this->get_handlers($type);
941
    }
942

    
943
    if (isset($this->handlers[$type][$id])) {
944
      return $this->handlers[$type][$id];
945
    }
946

    
947
    // So we can return a reference.
948
    $null = NULL;
949
    return $null;
950
  }
951

    
952
  /**
953
   * Get a full array of handlers for $type. This caches them.
954
   */
955
  function &get_handlers($type) {
956
    if (!isset($this->handlers[$type])) {
957
      $this->handlers[$type] = array();
958
      $types = views_object_types();
959
      $plural = $types[$type]['plural'];
960

    
961
      foreach ($this->get_option($plural) as $id => $info) {
962
        // If this is during form submission and there are temporary options
963
        // which can only appear if the view is in the edit cache, use those
964
        // options instead. This is used for AJAX multi-step stuff.
965
        if (isset($_POST['form_id']) && isset($this->view->temporary_options[$type][$id])) {
966
          $info = $this->view->temporary_options[$type][$id];
967
        }
968

    
969
        if ($info['id'] != $id) {
970
          $info['id'] = $id;
971
        }
972

    
973
        // If aggregation is on, the group type might override the actual
974
        // handler that is in use. This piece of code checks that and,
975
        // if necessary, sets the override handler.
976
        $override = NULL;
977
        if ($this->use_group_by() && !empty($info['group_type'])) {
978
          if (empty($this->view->query)) {
979
            $this->view->init_query();
980
          }
981
          $aggregate = $this->view->query->get_aggregation_info();
982
          if (!empty($aggregate[$info['group_type']]['handler'][$type])) {
983
            $override = $aggregate[$info['group_type']]['handler'][$type];
984
          }
985
        }
986

    
987
        if (!empty($types[$type]['type'])) {
988
          $handler_type = $types[$type]['type'];
989
        }
990
        else {
991
          $handler_type = $type;
992
        }
993

    
994
        $handler = views_get_handler($info['table'], $info['field'], $handler_type, $override);
995
        if ($handler) {
996
          // Special override for area types so they know where they come from.
997
          if ($handler_type == 'area') {
998
            $handler->handler_type = $type;
999
          }
1000

    
1001
          $handler->init($this->view, $info);
1002
          $this->handlers[$type][$id] = &$handler;
1003
        }
1004

    
1005
        // Prevent reference problems.
1006
        unset($handler);
1007
      }
1008
    }
1009

    
1010
    return $this->handlers[$type];
1011
  }
1012

    
1013
  /**
1014
   * Retrieve a list of fields for the current display with the
1015
   * relationship associated if it exists.
1016
   *
1017
   * @param $groupable_only
1018
   *  Return only an array of field labels from handler that return TRUE
1019
   *  from use_string_group_by method.
1020
   */
1021
  function get_field_labels() {
1022
    // Use func_get_arg so the function signature isn't amended
1023
    // but we can still pass TRUE into the function to filter
1024
    // by groupable handlers.
1025
    $args = func_get_args();
1026
    $groupable_only = isset($args[0]) ? $args[0] : FALSE;
1027

    
1028
    $options = array();
1029
    foreach ($this->get_handlers('relationship') as $relationship => $handler) {
1030
      if ($label = $handler->label()) {
1031
        $relationships[$relationship] = $label;
1032
      }
1033
      else {
1034
        $relationships[$relationship] = $handler->ui_name();
1035
      }
1036
    }
1037

    
1038
    foreach ($this->get_handlers('field') as $id => $handler) {
1039
      if ($groupable_only && !$handler->use_string_group_by()) {
1040
        // Continue to next handler if it's not groupable.
1041
        continue;
1042
      }
1043
      if ($label = $handler->label()) {
1044
        $options[$id] = $label;
1045
      }
1046
      else {
1047
        $options[$id] = $handler->ui_name();
1048
      }
1049
      if (!empty($handler->options['relationship']) && !empty($relationships[$handler->options['relationship']])) {
1050
        $options[$id] = '(' . $relationships[$handler->options['relationship']] . ') ' . $options[$id];
1051
      }
1052
    }
1053
    return $options;
1054
  }
1055

    
1056
  /**
1057
   * Intelligently set an option either from this display or from the
1058
   * default display, if directed to do so.
1059
   */
1060
  function set_option($option, $value) {
1061
    if ($this->is_defaulted($option)) {
1062
      return $this->default_display->set_option($option, $value);
1063
    }
1064

    
1065
    // Set this in two places: On the handler where we'll notice it
1066
    // but also on the display object so it gets saved. This should
1067
    // only be a temporary fix.
1068
    $this->display->display_options[$option] = $value;
1069
    return $this->options[$option] = $value;
1070
  }
1071

    
1072
  /**
1073
   * Set an option and force it to be an override.
1074
   */
1075
  function override_option($option, $value) {
1076
    $this->set_override($option, FALSE);
1077
    $this->set_option($option, $value);
1078
  }
1079

    
1080
  /**
1081
   * Because forms may be split up into sections, this provides
1082
   * an easy URL to exactly the right section. Don't override this.
1083
   */
1084
  function option_link($text, $section, $class = '', $title = '') {
1085
    views_add_js('ajax');
1086
    if (!empty($class)) {
1087
      $text = '<span>' . $text . '</span>';
1088
    }
1089

    
1090
    if (!trim($text)) {
1091
      $text = t('Broken field');
1092
    }
1093

    
1094
    if (empty($title)) {
1095
      $title = $text;
1096
    }
1097

    
1098
    // Truncate the path as it is displayed as a link.
1099
    if ($section == 'path') {
1100
      $text = views_ui_truncate($text, 24);
1101
    }
1102

    
1103
    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));
1104
  }
1105

    
1106
  /**
1107
   * Returns to tokens for arguments.
1108
   *
1109
   * This function is similar to views_handler_field::get_render_tokens()
1110
   * but without fields tokens.
1111
   */
1112
  function get_arguments_tokens() {
1113
    $tokens = array();
1114
    if (!empty($this->view->build_info['substitutions'])) {
1115
      $tokens = $this->view->build_info['substitutions'];
1116
    }
1117
    $count = 0;
1118
    foreach ($this->view->display_handler->get_handlers('argument') as $arg => $handler) {
1119
      $token = '%' . ++$count;
1120
      if (!isset($tokens[$token])) {
1121
        $tokens[$token] = '';
1122
      }
1123

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

    
1130
    return $tokens;
1131
  }
1132

    
1133
  /**
1134
   * Provide the default summary for options in the views UI.
1135
   *
1136
   * This output is returned as an array.
1137
   */
1138
  function options_summary(&$categories, &$options) {
1139
    $categories = array(
1140
      'title' => array(
1141
        'title' => t('Title'),
1142
        'column' => 'first',
1143
      ),
1144
      'format' => array(
1145
        'title' => t('Format'),
1146
        'column' => 'first',
1147
      ),
1148
      'filters' => array(
1149
        'title' => t('Filters'),
1150
        'column' => 'first',
1151
      ),
1152
      'fields' => array(
1153
        'title' => t('Fields'),
1154
        'column' => 'first',
1155
      ),
1156
      'pager' => array(
1157
        'title' => t('Pager'),
1158
        'column' => 'second',
1159
      ),
1160
      'exposed' => array(
1161
        'title' => t('Exposed form'),
1162
        'column' => 'third',
1163
        'build' => array(
1164
          '#weight' => 1,
1165
        ),
1166
      ),
1167
      'access' => array(
1168
        'title' => '',
1169
        'column' => 'second',
1170
        'build' => array(
1171
          '#weight' => -5,
1172
        ),
1173
      ),
1174
      'other' => array(
1175
        'title' => t('Other'),
1176
        'column' => 'third',
1177
        'build' => array(
1178
          '#weight' => 2,
1179
        ),
1180
      ),
1181
    );
1182

    
1183
    if ($this->display->id != 'default') {
1184
      $options['display_id'] = array(
1185
        'category' => 'other',
1186
        'title' => t('Machine Name'),
1187
        'value' => !empty($this->display->new_id) ? check_plain($this->display->new_id) : check_plain($this->display->id),
1188
        'desc' => t('Change the machine name of this display.'),
1189
      );
1190
    }
1191

    
1192
    $display_comment = check_plain(views_ui_truncate($this->get_option('display_comment'), 80));
1193
    $options['display_comment'] = array(
1194
      'category' => 'other',
1195
      'title' => t('Comment'),
1196
      'value' => !empty($display_comment) ? $display_comment : t('No comment'),
1197
      'desc' => t('Comment or document this display.'),
1198
    );
1199

    
1200
    $title = strip_tags($this->get_option('title'));
1201
    if (!$title) {
1202
      $title = t('None');
1203
    }
1204

    
1205
    $options['title'] = array(
1206
      'category' => 'title',
1207
      'title' => t('Title'),
1208
      'value' => $title,
1209
      'desc' => t('Change the title that this display will use.'),
1210
    );
1211

    
1212
    $style_plugin = views_fetch_plugin_data('style', $this->get_option('style_plugin'));
1213
    $style_plugin_instance = $this->get_plugin('style');
1214
    $style_summary = empty($style_plugin['title']) ? t('Missing style plugin') : $style_plugin_instance->summary_title();
1215
    $style_title = empty($style_plugin['title']) ? t('Missing style plugin') : $style_plugin_instance->plugin_title();
1216

    
1217
    $style = '';
1218

    
1219
    $options['style_plugin'] = array(
1220
      'category' => 'format',
1221
      'title' => t('Format'),
1222
      'value' => $style_title,
1223
      'setting' => $style_summary,
1224
      'desc' => t('Change the way content is formatted.'),
1225
    );
1226

    
1227
    // This adds a 'Settings' link to the style_options setting if the style has options.
1228
    if (!empty($style_plugin['uses options'])) {
1229
      $options['style_plugin']['links']['style_options'] = t('Change settings for this format');
1230
    }
1231

    
1232
    if (!empty($style_plugin['uses row plugin'])) {
1233
      $row_plugin = views_fetch_plugin_data('row', $this->get_option('row_plugin'));
1234
      $row_plugin_instance = $this->get_plugin('row');
1235
      $row_summary = empty($row_plugin['title']) ? t('Missing style plugin') : $row_plugin_instance->summary_title();
1236
      $row_title = empty($row_plugin['title']) ? t('Missing style plugin') : $row_plugin_instance->plugin_title();
1237

    
1238
      $options['row_plugin'] = array(
1239
        'category' => 'format',
1240
        'title' => t('Show'),
1241
        'value' => $row_title,
1242
        'setting' => $row_summary,
1243
        'desc' => t('Change the way each row in the view is styled.'),
1244
      );
1245
      // This adds a 'Settings' link to the row_options setting if the row style has options.
1246
      if (!empty($row_plugin['uses options'])) {
1247
        $options['row_plugin']['links']['row_options'] = t('Change settings for this style');
1248
      }
1249
    }
1250
    if (!empty($this->definition['use ajax'])) {
1251
      $options['use_ajax'] = array(
1252
        'category' => 'other',
1253
        'title' => t('Use AJAX'),
1254
        'value' => $this->get_option('use_ajax') ? t('Yes') : t('No'),
1255
        'desc' => t('Change whether or not this display will use AJAX.'),
1256
      );
1257
    }
1258
    if (!empty($this->definition['accept attachments'])) {
1259
      $options['hide_attachment_summary'] = array(
1260
        'category' => 'other',
1261
        'title' => t('Hide attachments in summary'),
1262
        'value' => $this->get_option('hide_attachment_summary') ? t('Yes') : t('No'),
1263
        'desc' => t('Change whether or not to display attachments when displaying a contextual filter summary.'),
1264
      );
1265
    }
1266
    if (!isset($this->definition['contextual links locations']) || !empty($this->definition['contextual links locations'])) {
1267
      $options['hide_admin_links'] = array(
1268
        'category' => 'other',
1269
        'title' => t('Hide contextual links'),
1270
        'value' => $this->get_option('hide_admin_links') ? t('Yes') : t('No'),
1271
        'desc' => t('Change whether or not to display contextual links for this view.'),
1272
      );
1273
    }
1274

    
1275
    $pager_plugin = $this->get_plugin('pager');
1276
    if (!$pager_plugin) {
1277
      // default to the no pager plugin.
1278
      $pager_plugin = views_get_plugin('pager', 'none');
1279
    }
1280

    
1281
    $pager_str = $pager_plugin->summary_title();
1282

    
1283
    $options['pager'] = array(
1284
      'category' => 'pager',
1285
      'title' => t('Use pager'),
1286
      'value' => $pager_plugin->plugin_title(),
1287
      'setting' => $pager_str,
1288
      'desc' => t("Change this display's pager setting."),
1289
    );
1290

    
1291
    // If pagers aren't allowed, change the text of the item:
1292
    if (empty($this->definition['use pager'])) {
1293
      $options['pager']['title'] = t('Items to display');
1294
    }
1295

    
1296
    if (!empty($pager_plugin->definition['uses options'])) {
1297
      $options['pager']['links']['pager_options'] = t('Change settings for this pager type.');
1298
    }
1299

    
1300
    if (!empty($this->definition['use more'])) {
1301
      $options['use_more'] = array(
1302
        'category' => 'pager',
1303
        'title' => t('More link'),
1304
        'value' => $this->get_option('use_more') ? t('Yes') : t('No'),
1305
        'desc' => t('Specify whether this display will provide a "more" link.'),
1306
      );
1307
    }
1308

    
1309
    $this->view->init_query();
1310
    if ($this->view->query->get_aggregation_info()) {
1311
      $options['group_by'] = array(
1312
        'category' => 'other',
1313
        'title' => t('Use aggregation'),
1314
        'value' => $this->get_option('group_by') ? t('Yes') : t('No'),
1315
        'desc' => t('Allow grouping and aggregation (calculation) of fields.'),
1316
      );
1317
    }
1318

    
1319
    $options['query'] = array(
1320
      'category' => 'other',
1321
      'title' => t('Query settings'),
1322
      'value' => t('Settings'),
1323
      'desc' => t('Allow to set some advanced settings for the query plugin'),
1324
    );
1325

    
1326
    $languages = array(
1327
        '***CURRENT_LANGUAGE***' => t("Current user's language"),
1328
        '***DEFAULT_LANGUAGE***' => t("Default site language"),
1329
        LANGUAGE_NONE => t('Language neutral'),
1330
    );
1331
    if (module_exists('locale')) {
1332
      $languages = array_merge($languages, locale_language_list());
1333
    }
1334
    $field_language = array();
1335
    $options['field_language'] = array(
1336
      'category' => 'other',
1337
      'title' => t('Field Language'),
1338
      'value' => $languages[$this->get_option('field_language')],
1339
      'desc' => t('All fields which support translations will be displayed in the selected language.'),
1340
    );
1341

    
1342
    $access_plugin = $this->get_plugin('access');
1343
    if (!$access_plugin) {
1344
      // default to the no access control plugin.
1345
      $access_plugin = views_get_plugin('access', 'none');
1346
    }
1347

    
1348
    $access_str = $access_plugin->summary_title();
1349

    
1350
    $options['access'] = array(
1351
      'category' => 'access',
1352
      'title' => t('Access'),
1353
      'value' => $access_plugin->plugin_title(),
1354
      'setting' => $access_str,
1355
      'desc' => t('Specify access control type for this display.'),
1356
    );
1357

    
1358
    if (!empty($access_plugin->definition['uses options'])) {
1359
      $options['access']['links']['access_options'] = t('Change settings for this access type.');
1360
    }
1361

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

    
1368
    $cache_str = $cache_plugin->summary_title();
1369

    
1370
    $options['cache'] = array(
1371
      'category' => 'other',
1372
      'title' => t('Caching'),
1373
      'value' => $cache_plugin->plugin_title(),
1374
      'setting' => $cache_str,
1375
      'desc' => t('Specify caching type for this display.'),
1376
    );
1377

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

    
1382
    if (!empty($access_plugin->definition['uses options'])) {
1383
      $options['access']['links']['access_options'] = t('Change settings for this access type.');
1384
    }
1385

    
1386
    if ($this->uses_link_display()) {
1387
      $display_id = $this->get_link_display();
1388
      $link_display = empty($this->view->display[$display_id]) ? t('None') : check_plain($this->view->display[$display_id]->display_title);
1389
      $link_display =  $this->get_option('link_display') == 'custom_url' ? t('Custom URL') : $link_display;
1390
      $options['link_display'] = array(
1391
        'category' => 'other',
1392
        'title' => t('Link display'),
1393
        'value' => $link_display,
1394
        'desc' => t('Specify which display or custom url this display will link to.'),
1395
      );
1396
    }
1397

    
1398
    if ($this->uses_exposed_form_in_block()) {
1399
      $options['exposed_block'] = array(
1400
        'category' => 'exposed',
1401
        'title' => t('Exposed form in block'),
1402
        'value' => $this->get_option('exposed_block') ? t('Yes') : t('No'),
1403
        'desc' => t('Allow the exposed form to appear in a block instead of the view.'),
1404
      );
1405
    }
1406

    
1407
    $exposed_form_plugin = $this->get_plugin('exposed_form');
1408
    if (!$exposed_form_plugin) {
1409
      // default to the no cache control plugin.
1410
      $exposed_form_plugin = views_get_plugin('exposed_form', 'basic');
1411
    }
1412

    
1413
    $exposed_form_str = $exposed_form_plugin->summary_title();
1414

    
1415
    $options['exposed_form'] = array(
1416
      'category' => 'exposed',
1417
      'title' => t('Exposed form style'),
1418
      'value' => $exposed_form_plugin->plugin_title(),
1419
      'setting' => $exposed_form_str,
1420
      'desc' => t('Select the kind of exposed filter to use.'),
1421
    );
1422

    
1423
    if (!empty($exposed_form_plugin->definition['uses options'])) {
1424
      $options['exposed_form']['links']['exposed_form_options'] = t('Exposed form settings for this exposed form style.');
1425
    }
1426

    
1427
    $css_class = check_plain(trim($this->get_option('css_class')));
1428
    if (!$css_class) {
1429
      $css_class = t('None');
1430
    }
1431

    
1432
    $options['css_class'] = array(
1433
      'category' => 'other',
1434
      'title' => t('CSS class'),
1435
      'value' => $css_class,
1436
      'desc' => t('Change the CSS class name(s) that will be added to this display.'),
1437
    );
1438

    
1439
    $options['analyze-theme'] = array(
1440
      'category' => 'other',
1441
      'title' => t('Theme'),
1442
      'value' => t('Information'),
1443
      'desc' => t('Get information on how to theme this display'),
1444
    );
1445

    
1446
    foreach ($this->extender as $extender) {
1447
      $extender->options_summary($categories, $options);
1448
    }
1449
  }
1450

    
1451
  /**
1452
   * Provide the default form for setting options.
1453
   */
1454
  function options_form(&$form, &$form_state) {
1455
    parent::options_form($form, $form_state);
1456
    if ($this->defaultable_sections($form_state['section'])) {
1457
      views_ui_standard_display_dropdown($form, $form_state, $form_state['section']);
1458
    }
1459
    $form['#title'] = check_plain($this->display->display_title) . ': ';
1460

    
1461
    // Set the 'section' to highlight on the form.
1462
    // If it's the item we're looking at is pulling from the default display,
1463
    // reflect that. Don't use is_defaulted since we want it to show up even
1464
    // on the default display.
1465
    if (!empty($this->options['defaults'][$form_state['section']])) {
1466
      $form['#section'] = 'default-' . $form_state['section'];
1467
    }
1468
    else {
1469
      $form['#section'] = $this->display->id . '-' . $form_state['section'];
1470
    }
1471

    
1472
    switch ($form_state['section']) {
1473
      case 'display_id':
1474
        $form['#title'] .= t('The machine name of this display');
1475
        $form['display_id'] = array(
1476
          '#type' => 'textfield',
1477
          '#description' => t('This is machine name of the display.'),
1478
          '#default_value' => !empty($this->display->new_id) ? $this->display->new_id : $this->display->id,
1479
          '#required' => TRUE,
1480
          '#size' => 64,
1481
        );
1482
        break;
1483
      case 'display_title':
1484
        $form['#title'] .= t('The name and the description of this display');
1485
        $form['display_title'] = array(
1486
          '#title' => t('Name'),
1487
          '#type' => 'textfield',
1488
          '#description' => t('This name will appear only in the administrative interface for the View.'),
1489
          '#default_value' => $this->display->display_title,
1490
        );
1491
        $form['display_description'] = array(
1492
          '#title' => t('Description'),
1493
          '#type' => 'textfield',
1494
          '#description' => t('This description will appear only in the administrative interface for the View.'),
1495
          '#default_value' => $this->get_option('display_description'),
1496
        );
1497
        break;
1498
      case 'display_comment':
1499
        $form['#title'] .= t("This display's comments");
1500
        $form['display_comment'] = array(
1501
          '#type' => 'textarea',
1502
          '#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.'),
1503
          '#default_value' => $this->get_option('display_comment'),
1504
        );
1505
        break;
1506
      case 'title':
1507
        $form['#title'] .= t('The title of this view');
1508
        $form['title'] = array(
1509
          '#type' => 'textfield',
1510
          '#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.'),
1511
          '#default_value' => $this->get_option('title'),
1512
        );
1513
        break;
1514
      case 'css_class':
1515
        $form['#title'] .= t('CSS class');
1516
        $form['css_class'] = array(
1517
          '#type' => 'textfield',
1518
          '#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.'),
1519
          '#default_value' => $this->get_option('css_class'),
1520
        );
1521
        break;
1522
      case 'use_ajax':
1523
        $form['#title'] .= t('Use AJAX when available to load this view');
1524
        $form['description'] = array(
1525
          '#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>',
1526
        );
1527
        $form['use_ajax'] = array(
1528
          '#type' => 'radios',
1529
          '#options' => array(1 => t('Yes'), 0 => t('No')),
1530
          '#default_value' => $this->get_option('use_ajax') ? 1 : 0,
1531
        );
1532
        break;
1533
      case 'hide_attachment_summary':
1534
        $form['#title'] .= t('Hide attachments when displaying a contextual filter summary');
1535
        $form['hide_attachment_summary'] = array(
1536
          '#type' => 'radios',
1537
          '#options' => array(1 => t('Yes'), 0 => t('No')),
1538
          '#default_value' => $this->get_option('hide_attachment_summary') ? 1 : 0,
1539
        );
1540
        break;
1541
      case 'hide_admin_links':
1542
        $form['#title'] .= t('Hide contextual links on this view.');
1543
        $form['hide_admin_links'] = array(
1544
          '#type' => 'radios',
1545
          '#options' => array(1 => t('Yes'), 0 => t('No')),
1546
          '#default_value' => $this->get_option('hide_admin_links') ? 1 : 0,
1547
        );
1548
      break;
1549
      case 'use_more':
1550
        $form['#title'] .= t('Add a more link to the bottom of the display.');
1551
        $form['use_more'] = array(
1552
          '#type' => 'checkbox',
1553
          '#title' => t('Create more link'),
1554
          '#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."),
1555
          '#default_value' => $this->get_option('use_more'),
1556
        );
1557
        $form['use_more_always'] = array(
1558
          '#type' => 'checkbox',
1559
          '#title' => t("Display 'more' link only if there is more content"),
1560
          '#description' => t("Leave this unchecked to display the 'more' link even if there are no more items to display."),
1561
          '#default_value' => !$this->get_option('use_more_always'),
1562
            '#dependency' => array(
1563
              'edit-use-more' => array(TRUE),
1564
            ),
1565
        );
1566

    
1567
        $form['open_new_window'] = array(
1568
          '#type' => 'checkbox',
1569
          '#title' => t("Open 'more' link in new window (target='blank')"),
1570
          '#description' => t("Leave this unchecked to open the more link in the same window."),
1571
          '#default_value' => $this->get_option('open_new_window'),
1572
          '#dependency' => array(
1573
            'edit-use-more' => array(TRUE),
1574
          ),
1575
        );
1576

    
1577
        $form['use_more_text'] = array(
1578
          '#type' => 'textfield',
1579
          '#title' => t('More link text'),
1580
          '#description' => t("The text to display for the more link."),
1581
          '#default_value' => $this->get_option('use_more_text'),
1582
          '#dependency' => array(
1583
            'edit-use-more' => array(TRUE),
1584
          ),
1585
        );
1586
        break;
1587
      case 'group_by':
1588
        $form['#title'] .= t('Allow grouping and aggregation (calculation) of fields.');
1589
        $form['group_by'] = array(
1590
          '#type' => 'checkbox',
1591
          '#title' => t('Aggregate'),
1592
          '#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.'),
1593
          '#default_value' => $this->get_option('group_by'),
1594
        );
1595
        break;
1596
      case 'access':
1597
        $form['#title'] .= t('Access restrictions');
1598
        $form['access'] = array(
1599
          '#prefix' => '<div class="clearfix">',
1600
          '#suffix' => '</div>',
1601
          '#tree' => TRUE,
1602
        );
1603

    
1604
        $access = $this->get_option('access');
1605
        $form['access']['type'] =  array(
1606
          '#type' => 'radios',
1607
          '#options' => views_fetch_plugin_names('access', NULL, array($this->view->base_table)),
1608
          '#default_value' => $access['type'],
1609
        );
1610

    
1611
        $access_plugin = views_fetch_plugin_data('access', $access['type']);
1612
        if (!empty($access_plugin['uses options'])) {
1613
          $form['markup'] = array(
1614
            '#prefix' => '<div class="form-item description">',
1615
            '#markup' => t('You may also adjust the !settings for the currently selected access restriction.', array('!settings' => $this->option_link(t('settings'), 'access_options'))),
1616
            '#suffix' => '</div>',
1617
          );
1618
        }
1619

    
1620
        break;
1621
      case 'access_options':
1622
        $access = $this->get_option('access');
1623
        $plugin = $this->get_plugin('access');
1624
        $form['#title'] .= t('Access options');
1625
        if ($plugin) {
1626
          if (!empty($plugin->definition['help topic'])) {
1627
            $form['#help_topic'] = $plugin->definition['help topic'];
1628
          }
1629
          if (!empty($plugin->definition['module'])) {
1630
            $form['#help_module'] = $plugin->definition['module'];
1631
          }
1632

    
1633
          $form['access_options'] = array(
1634
            '#tree' => TRUE,
1635
          );
1636
          $form['access_options']['type'] = array(
1637
            '#type' => 'value',
1638
            '#value' => $access['type'],
1639
          );
1640
          $plugin->options_form($form['access_options'], $form_state);
1641
        }
1642
        break;
1643
      case 'cache':
1644
        $form['#title'] .= t('Caching');
1645
        $form['cache'] = array(
1646
          '#prefix' => '<div class="clearfix">',
1647
          '#suffix' => '</div>',
1648
          '#tree' => TRUE,
1649
        );
1650

    
1651
        $cache = $this->get_option('cache');
1652
        $form['cache']['type'] =  array(
1653
          '#type' => 'radios',
1654
          '#options' => views_fetch_plugin_names('cache', NULL, array($this->view->base_table)),
1655
          '#default_value' => $cache['type'],
1656
        );
1657

    
1658
        $cache_plugin = views_fetch_plugin_data('cache', $cache['type']);
1659
        if (!empty($cache_plugin['uses options'])) {
1660
          $form['markup'] = array(
1661
            '#prefix' => '<div class="form-item description">',
1662
            '#suffix' => '</div>',
1663
            '#markup' => t('You may also adjust the !settings for the currently selected cache mechanism.', array('!settings' => $this->option_link(t('settings'), 'cache_options'))),
1664
          );
1665
        }
1666
        break;
1667
      case 'cache_options':
1668
        $cache = $this->get_option('cache');
1669
        $plugin = $this->get_plugin('cache');
1670
        $form['#title'] .= t('Caching options');
1671
        if ($plugin) {
1672
          if (!empty($plugin->definition['help topic'])) {
1673
            $form['#help_topic'] = $plugin->definition['help topic'];
1674
          }
1675
          if (!empty($plugin->definition['module'])) {
1676
            $form['#help_module'] = $plugin->definition['module'];
1677
          }
1678

    
1679
          $form['cache_options'] = array(
1680
            '#tree' => TRUE,
1681
          );
1682
          $form['cache_options']['type'] = array(
1683
            '#type' => 'value',
1684
            '#value' => $cache['type'],
1685
          );
1686
          $plugin->options_form($form['cache_options'], $form_state);
1687
        }
1688
        break;
1689
      case 'query':
1690
        $query_options = $this->get_option('query');
1691
        $plugin_name = $query_options['type'];
1692

    
1693
        $form['#title'] .= t('Query options');
1694
        $this->view->init_query();
1695
        if ($this->view->query) {
1696
          if (!empty($this->view->query->definition['help topic'])) {
1697
            $form['#help_topic'] = $this->view->query->definition['help topic'];
1698
          }
1699
          if (!empty($this->view->query->definition['module'])) {
1700
            $form['#help_module'] = $this->view->query->definition['module'];
1701
          }
1702

    
1703
          $form['query'] = array(
1704
            '#tree' => TRUE,
1705
            'type' => array(
1706
              '#type' => 'value',
1707
              '#value' => $plugin_name,
1708
            ),
1709
            'options' => array(
1710
              '#tree' => TRUE,
1711
            ),
1712
          );
1713

    
1714
          $this->view->query->options_form($form['query']['options'], $form_state);
1715
        }
1716
        break;
1717
      case 'field_language':
1718
        $form['#title'] .= t('Field Language');
1719

    
1720
        $entities = entity_get_info();
1721
        $entity_tables = array();
1722
        $has_translation_handlers = FALSE;
1723
        foreach ($entities as $type => $entity_info) {
1724
          $entity_tables[] = $entity_info['base table'];
1725

    
1726
          if (!empty($entity_info['translation'])) {
1727
            $has_translation_handlers = TRUE;
1728
          }
1729
        }
1730

    
1731
        // Doesn't make sense to show a field setting here if we aren't querying
1732
        // an entity base table. Also, we make sure that there's at least one
1733
        // entity type with a translation handler attached.
1734
        if (in_array($this->view->base_table, $entity_tables) && $has_translation_handlers) {
1735
          $languages = array(
1736
            '***CURRENT_LANGUAGE***' => t("Current user's language"),
1737
            '***DEFAULT_LANGUAGE***' => t("Default site language"),
1738
            LANGUAGE_NONE => t('Language neutral'),
1739
          );
1740
          $languages = array_merge($languages, views_language_list());
1741

    
1742
          $form['field_language'] = array(
1743
            '#type' => 'select',
1744
            '#title' => t('Field Language'),
1745
            '#description' => t('All fields which support translations will be displayed in the selected language.'),
1746
            '#options' => $languages,
1747
            '#default_value' => $this->get_option('field_language'),
1748
          );
1749
          $form['field_language_add_to_query'] = array(
1750
            '#type' => 'checkbox',
1751
            '#title' => t('When needed, add the field language condition to the query'),
1752
            '#default_value' => $this->get_option('field_language_add_to_query'),
1753
          );
1754
        }
1755
        else {
1756
          $form['field_language']['#markup'] = t("You don't have translatable entity types.");
1757
        }
1758
        break;
1759
      case 'style_plugin':
1760
        $form['#title'] .= t('How should this view be styled');
1761
        $form['#help_topic'] = 'style';
1762
        $form['style_plugin'] =  array(
1763
          '#type' => 'radios',
1764
          '#options' => views_fetch_plugin_names('style', $this->get_style_type(), array($this->view->base_table)),
1765
          '#default_value' => $this->get_option('style_plugin'),
1766
          '#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.'),
1767
        );
1768

    
1769
        $style_plugin = views_fetch_plugin_data('style', $this->get_option('style_plugin'));
1770
        if (!empty($style_plugin['uses options'])) {
1771
          $form['markup'] = array(
1772
            '#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>',
1773
          );
1774
        }
1775

    
1776
        break;
1777
      case 'style_options':
1778
        $form['#title'] .= t('Style options');
1779
        $style = TRUE;
1780
        $type = 'style_plugin';
1781
        $name = $this->get_option('style_plugin');
1782

    
1783
      case 'row_options':
1784
        if (!isset($name)) {
1785
          $name = $this->get_option('row_plugin');
1786
        }
1787
        // if row, $style will be empty.
1788
        if (empty($style)) {
1789
          $form['#title'] .= t('Row style options');
1790
          $type = 'row_plugin';
1791
        }
1792
        $plugin = $this->get_plugin(empty($style) ? 'row' : 'style');
1793
        if ($plugin) {
1794
          if (!empty($plugin->definition['help topic'])) {
1795
            $form['#help_topic'] = $plugin->definition['help topic'];
1796
          }
1797
          if (!empty($plugin->definition['module'])) {
1798
            $form['#help_module'] = $plugin->definition['module'];
1799
          }
1800
          $form[$form_state['section']] = array(
1801
            '#tree' => TRUE,
1802
          );
1803
          $plugin->options_form($form[$form_state['section']], $form_state);
1804
        }
1805
        break;
1806
      case 'row_plugin':
1807
        $form['#title'] .= t('How should each row in this view be styled');
1808
        $form['#help_topic'] = 'style-row';
1809
        $form['row_plugin'] =  array(
1810
          '#type' => 'radios',
1811
          '#options' => views_fetch_plugin_names('row', $this->get_style_type(), array($this->view->base_table)),
1812
          '#default_value' => $this->get_option('row_plugin'),
1813
        );
1814

    
1815
        $row_plugin = views_fetch_plugin_data('row', $this->get_option('row_plugin'));
1816
        if (!empty($row_plugin['uses options'])) {
1817
          $form['markup'] = array(
1818
            '#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>',
1819
          );
1820
        }
1821

    
1822
        break;
1823
      case 'link_display':
1824
        $form['#title'] .= t('Which display to use for path');
1825
        foreach ($this->view->display as $display_id => $display) {
1826
          if ($display->handler->has_path()) {
1827
            $options[$display_id] = $display->display_title;
1828
          }
1829
        }
1830
        $options['custom_url'] = t('Custom URL');
1831
        if (count($options)) {
1832
          $form['link_display'] = array(
1833
            '#type' => 'radios',
1834
            '#options' => $options,
1835
            '#description' => t("Which display to use to get this display's path for things like summary links, rss feed links, more links, etc."),
1836
            '#default_value' => $this->get_option('link_display'),
1837
          );
1838
        }
1839

    
1840
        $options = array();
1841
        $count = 0; // This lets us prepare the key as we want it printed.
1842
        foreach ($this->view->display_handler->get_handlers('argument') as $arg => $handler) {
1843
          $options[t('Arguments')]['%' . ++$count] = t('@argument title', array('@argument' => $handler->ui_name()));
1844
          $options[t('Arguments')]['!' . $count] = t('@argument input', array('@argument' => $handler->ui_name()));
1845
        }
1846

    
1847
        // Default text.
1848
        // We have some options, so make a list.
1849
        $output = '';
1850
        if (!empty($options)) {
1851
          $output = t('<p>The following tokens are available for this link.</p>');
1852
          foreach (array_keys($options) as $type) {
1853
            if (!empty($options[$type])) {
1854
              $items = array();
1855
              foreach ($options[$type] as $key => $value) {
1856
                $items[] = $key . ' == ' . $value;
1857
              }
1858
              $output .= theme('item_list',
1859
                array(
1860
                  'items' => $items,
1861
                  'type' => $type
1862
                ));
1863
            }
1864
          }
1865
        }
1866

    
1867
        $form['link_url'] = array(
1868
          '#type' => 'textfield',
1869
          '#title' => t('Custom URL'),
1870
          '#default_value' => $this->get_option('link_url'),
1871
          '#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,
1872
          '#dependency' => array('radio:link_display' => array('custom_url')),
1873
        );
1874
        break;
1875
      case 'analyze-theme':
1876
        $form['#title'] .= t('Theming information');
1877
        $form['#help_topic'] = 'analyze-theme';
1878

    
1879
        if (isset($_POST['theme'])) {
1880
          $this->theme = $_POST['theme'];
1881
        }
1882
        elseif (empty($this->theme)) {
1883
          $this->theme = variable_get('theme_default', 'bartik');
1884
        }
1885

    
1886
        if (isset($GLOBALS['theme']) && $GLOBALS['theme'] == $this->theme) {
1887
          $this->theme_registry = theme_get_registry();
1888
          $theme_engine = $GLOBALS['theme_engine'];
1889
        }
1890
        else {
1891
          $themes = list_themes();
1892
          $theme = $themes[$this->theme];
1893

    
1894
          // Find all our ancestor themes and put them in an array.
1895
          $base_theme = array();
1896
          $ancestor = $this->theme;
1897
          while ($ancestor && isset($themes[$ancestor]->base_theme)) {
1898
            $ancestor = $themes[$ancestor]->base_theme;
1899
            $base_theme[] = $themes[$ancestor];
1900
          }
1901

    
1902
          // The base themes should be initialized in the right order.
1903
          $base_theme = array_reverse($base_theme);
1904

    
1905
          // This code is copied directly from _drupal_theme_initialize()
1906
          $theme_engine = NULL;
1907

    
1908
          // Initialize the theme.
1909
          if (isset($theme->engine)) {
1910
            // Include the engine.
1911
            include_once DRUPAL_ROOT . '/' . $theme->owner;
1912

    
1913
            $theme_engine = $theme->engine;
1914
            if (function_exists($theme_engine . '_init')) {
1915
              foreach ($base_theme as $base) {
1916
                call_user_func($theme_engine . '_init', $base);
1917
              }
1918
              call_user_func($theme_engine . '_init', $theme);
1919
            }
1920
          }
1921
          else {
1922
            // include non-engine theme files
1923
            foreach ($base_theme as $base) {
1924
              // Include the theme file or the engine.
1925
              if (!empty($base->owner)) {
1926
                include_once DRUPAL_ROOT . '/' . $base->owner;
1927
              }
1928
            }
1929
            // and our theme gets one too.
1930
            if (!empty($theme->owner)) {
1931
              include_once DRUPAL_ROOT . '/' . $theme->owner;
1932
            }
1933
          }
1934
          $this->theme_registry = _theme_load_registry($theme, $base_theme, $theme_engine);
1935
        }
1936

    
1937
        // If there's a theme engine involved, we also need to know its extension
1938
        // so we can give the proper filename.
1939
        $this->theme_extension = '.tpl.php';
1940
        if (isset($theme_engine)) {
1941
          $extension_function = $theme_engine . '_extension';
1942
          if (function_exists($extension_function)) {
1943
            $this->theme_extension = $extension_function();
1944
          }
1945
        }
1946

    
1947
        $funcs = array();
1948
        // Get theme functions for the display. Note that some displays may
1949
        // not have themes. The 'feed' display, for example, completely
1950
        // delegates to the style.
1951
        if (!empty($this->definition['theme'])) {
1952
          $funcs[] = $this->option_link(t('Display output'), 'analyze-theme-display') . ': '  . $this->format_themes($this->theme_functions());
1953
          $themes = $this->additional_theme_functions();
1954
          if ($themes) {
1955
            foreach ($themes as $theme) {
1956
              $funcs[] = $this->option_link(t('Alternative display output'), 'analyze-theme-display') . ': '  . $this->format_themes($theme);
1957
            }
1958
          }
1959
        }
1960

    
1961
        $plugin = $this->get_plugin();
1962
        if ($plugin) {
1963
          $funcs[] = $this->option_link(t('Style output'), 'analyze-theme-style') . ': ' . $this->format_themes($plugin->theme_functions(), $plugin->additional_theme_functions());
1964
          $themes = $plugin->additional_theme_functions();
1965
          if ($themes) {
1966
            foreach ($themes as $theme) {
1967
              $funcs[] = $this->option_link(t('Alternative style'), 'analyze-theme-style') . ': '  . $this->format_themes($theme);
1968
            }
1969
          }
1970

    
1971
          if ($plugin->uses_row_plugin()) {
1972
            $row_plugin = $this->get_plugin('row');
1973
            if ($row_plugin) {
1974
              $funcs[] = $this->option_link(t('Row style output'), 'analyze-theme-row') . ': ' . $this->format_themes($row_plugin->theme_functions());
1975
              $themes = $row_plugin->additional_theme_functions();
1976
              if ($themes) {
1977
                foreach ($themes as $theme) {
1978
                  $funcs[] = $this->option_link(t('Alternative row style'), 'analyze-theme-row') . ': '  . $this->format_themes($theme);
1979
                }
1980
              }
1981
            }
1982
          }
1983

    
1984
          if ($plugin->uses_fields()) {
1985
            foreach ($this->get_handlers('field') as $id => $handler) {
1986
              $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());
1987
            }
1988
          }
1989
        }
1990

    
1991
        $form['important'] = array(
1992
          '#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>',
1993
        );
1994

    
1995
        if (isset($this->view->display[$this->view->current_display]->new_id)) {
1996
          $form['important']['new_id'] = array(
1997
            '#prefix' => '<div class="description">',
1998
            '#suffix' => '</div>',
1999
            '#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."),
2000
          );
2001
        }
2002

    
2003
        foreach (list_themes() as $key => $theme) {
2004
          if (!empty($theme->info['hidden'])) {
2005
            continue;
2006
          }
2007
          $options[$key] = $theme->info['name'];
2008
        }
2009

    
2010
        $form['box'] = array(
2011
          '#prefix' => '<div class="container-inline">',
2012
          '#suffix' => '</div>',
2013
        );
2014
        $form['box']['theme'] = array(
2015
          '#type' => 'select',
2016
          '#options' => $options,
2017
          '#default_value' => $this->theme,
2018
        );
2019

    
2020
        $form['box']['change'] = array(
2021
          '#type' => 'submit',
2022
          '#value' => t('Change theme'),
2023
          '#submit' => array('views_ui_edit_display_form_change_theme'),
2024
        );
2025

    
2026
        $form['analysis'] = array(
2027
          '#markup' => '<div class="form-item">' . theme('item_list', array('items' => $funcs)) . '</div>',
2028
        );
2029

    
2030
        $form['rescan_button'] = array(
2031
          '#prefix' => '<div class="form-item">',
2032
          '#suffix' => '</div>',
2033
        );
2034
        $form['rescan_button']['button'] = array(
2035
          '#type' => 'submit',
2036
          '#value' => t('Rescan template files'),
2037
          '#submit' => array('views_ui_config_item_form_rescan'),
2038
        );
2039
        $form['rescan_button']['markup'] = array(
2040
          '#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>',
2041
        );
2042

    
2043
        $form_state['ok_button'] = TRUE;
2044
        break;
2045
      case 'analyze-theme-display':
2046
        $form['#title'] .= t('Theming information (display)');
2047
        $output = '<p>' . t('Back to !info.', array('!info' => $this->option_link(t('theming information'), 'analyze-theme'))) . '</p>';
2048

    
2049
        if (empty($this->definition['theme'])) {
2050
          $output .= t('This display has no theming information');
2051
        }
2052
        else {
2053
          $output .= '<p>' . t('This is the default theme template used for this display.') . '</p>';
2054
          $output .= '<pre>' . check_plain(file_get_contents('./' . $this->definition['theme path'] . '/' . strtr($this->definition['theme'], '_', '-') . '.tpl.php')) . '</pre>';
2055
        }
2056

    
2057
        if (!empty($this->definition['additional themes'])) {
2058
          foreach ($this->definition['additional themes'] as $theme => $type) {
2059
            $output .= '<p>' . t('This is an alternative template for this display.') . '</p>';
2060
            $output .= '<pre>' . check_plain(file_get_contents('./' . $this->definition['theme path'] . '/' . strtr($theme, '_', '-') . '.tpl.php')) . '</pre>';
2061
          }
2062
        }
2063

    
2064
        $form['analysis'] = array(
2065
          '#markup' => '<div class="form-item">' . $output . '</div>',
2066
        );
2067

    
2068
        $form_state['ok_button'] = TRUE;
2069
        break;
2070
      case 'analyze-theme-style':
2071
        $form['#title'] .= t('Theming information (style)');
2072
        $output = '<p>' . t('Back to !info.', array('!info' => $this->option_link(t('theming information'), 'analyze-theme'))) . '</p>';
2073

    
2074
        $plugin = $this->get_plugin();
2075

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

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

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

    
2095
        $form_state['ok_button'] = TRUE;
2096
        break;
2097
      case 'analyze-theme-row':
2098
        $form['#title'] .= t('Theming information (row style)');
2099
        $output = '<p>' . t('Back to !info.', array('!info' => $this->option_link(t('theming information'), 'analyze-theme'))) . '</p>';
2100

    
2101
        $plugin = $this->get_plugin('row');
2102

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

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

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

    
2122
        $form_state['ok_button'] = TRUE;
2123
        break;
2124
      case 'analyze-theme-field':
2125
        $form['#title'] .= t('Theming information (row style)');
2126
        $output = '<p>' . t('Back to !info.', array('!info' => $this->option_link(t('theming information'), 'analyze-theme'))) . '</p>';
2127

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

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

    
2134
        $form['analysis'] = array(
2135
          '#markup' => '<div class="form-item">' . $output . '</div>',
2136
        );
2137
        $form_state['ok_button'] = TRUE;
2138
        break;
2139

    
2140
      case 'exposed_block':
2141
        $form['#title'] .= t('Put the exposed form in a block');
2142
        $form['description'] = array(
2143
          '#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>',
2144
        );
2145
        $form['exposed_block'] = array(
2146
          '#type' => 'radios',
2147
          '#options' => array(1 => t('Yes'), 0 => t('No')),
2148
          '#default_value' => $this->get_option('exposed_block') ? 1 : 0,
2149
        );
2150
        break;
2151
      case 'exposed_form':
2152
        $form['#title'] .= t('Exposed Form');
2153
        $form['exposed_form'] = array(
2154
          '#prefix' => '<div class="clearfix">',
2155
          '#suffix' => '</div>',
2156
          '#tree' => TRUE,
2157
        );
2158

    
2159
        $exposed_form = $this->get_option('exposed_form');
2160
        $form['exposed_form']['type'] =  array(
2161
          '#type' => 'radios',
2162
          '#options' => views_fetch_plugin_names('exposed_form', NULL, array($this->view->base_table)),
2163
          '#default_value' => $exposed_form['type'],
2164
        );
2165

    
2166
        $exposed_form_plugin = views_fetch_plugin_data('exposed_form', $exposed_form['type']);
2167
        if (!empty($exposed_form_plugin['uses options'])) {
2168
          $form['markup'] = array(
2169
            '#prefix' => '<div class="form-item description">',
2170
            '#suffix' => '</div>',
2171
            '#markup' => t('You may also adjust the !settings for the currently selected style.', array('!settings' => $this->option_link(t('settings'), 'exposed_form_options'))),
2172
          );
2173
        }
2174
        break;
2175
      case 'exposed_form_options':
2176
        $plugin = $this->get_plugin('exposed_form');
2177
        $form['#title'] .= t('Exposed form options');
2178
        if ($plugin) {
2179
          if (!empty($plugin->definition['help topic'])) {
2180
            $form['#help_topic'] = $plugin->definition['help topic'];
2181
          }
2182
          if (!empty($plugin->definition['module'])) {
2183
            $form['#help_module'] = $plugin->definition['module'];
2184
          }
2185

    
2186
          $form['exposed_form_options'] = array(
2187
            '#tree' => TRUE,
2188
          );
2189
          $plugin->options_form($form['exposed_form_options'], $form_state);
2190
        }
2191
        break;
2192
      case 'pager':
2193
        $form['#title'] .= t('Select which pager, if any, to use for this view');
2194
        $form['pager'] = array(
2195
          '#prefix' => '<div class="clearfix">',
2196
          '#suffix' => '</div>',
2197
          '#tree' => TRUE,
2198
        );
2199

    
2200
        $pager = $this->get_option('pager');
2201
        $form['pager']['type'] =  array(
2202
          '#type' => 'radios',
2203
          '#options' => views_fetch_plugin_names('pager', empty($this->definition['use pager']) ? 'basic' : NULL, array($this->view->base_table)),
2204
          '#default_value' => $pager['type'],
2205
        );
2206

    
2207
        $pager_plugin = views_fetch_plugin_data('pager', $pager['type']);
2208
        if (!empty($pager_plugin['uses options'])) {
2209
          $form['markup'] = array(
2210
            '#prefix' => '<div class="form-item description">',
2211
            '#suffix' => '</div>',
2212
            '#markup' => t('You may also adjust the !settings for the currently selected pager.', array('!settings' => $this->option_link(t('settings'), 'pager_options'))),
2213
          );
2214
        }
2215

    
2216
        break;
2217
      case 'pager_options':
2218
        $plugin = $this->get_plugin('pager');
2219
        $form['#title'] .= t('Pager options');
2220
        if ($plugin) {
2221
          if (!empty($plugin->definition['help topic'])) {
2222
            $form['#help_topic'] = $plugin->definition['help topic'];
2223
          }
2224
          if (!empty($plugin->definition['module'])) {
2225
            $form['#help_module'] = $plugin->definition['module'];
2226
          }
2227

    
2228
          $form['pager_options'] = array(
2229
            '#tree' => TRUE,
2230
          );
2231
          $plugin->options_form($form['pager_options'], $form_state);
2232
        }
2233
        break;
2234
    }
2235

    
2236
    foreach ($this->extender as $extender) {
2237
      $extender->options_form($form, $form_state);
2238
    }
2239
  }
2240

    
2241
  /**
2242
   * Format a list of theme templates for output by the theme info helper.
2243
   */
2244
  function format_themes($themes) {
2245
    $registry = $this->theme_registry;
2246
    $extension = $this->theme_extension;
2247

    
2248
    $output = '';
2249
    $picked = FALSE;
2250
    foreach ($themes as $theme) {
2251
      $template = strtr($theme, '_', '-') . $extension;
2252
      if (!$picked && !empty($registry[$theme])) {
2253
        $template_path = isset($registry[$theme]['path']) ? $registry[$theme]['path'] . '/' : './';
2254
        if (file_exists($template_path . $template)) {
2255
          $hint = t('File found in folder @template-path', array('@template-path' => $template_path));
2256
          $template = '<strong title="'. $hint .'">' . $template . '</strong>';
2257
        }
2258
        else {
2259
          $template = '<strong class="error">' . $template . ' ' . t('(File not found, in folder @template-path)', array('@template-path' => $template_path)) . '</strong>';
2260
        }
2261
        $picked = TRUE;
2262
      }
2263
      $fixed[] = $template;
2264
    }
2265

    
2266
    return implode(', ', array_reverse($fixed));
2267
  }
2268

    
2269
  /**
2270
   * Validate the options form.
2271
   */
2272
  function options_validate(&$form, &$form_state) {
2273
    switch ($form_state['section']) {
2274
      case 'display_title':
2275
        if (empty($form_state['values']['display_title'])) {
2276
          form_error($form['display_title'], t('Display title may not be empty.'));
2277
        }
2278
        break;
2279
      case 'css_class':
2280
        $css_class = $form_state['values']['css_class'];
2281
        if (preg_match('/[^a-zA-Z0-9-_ ]/', $css_class)) {
2282
          form_error($form['css_class'], t('CSS classes must be alphanumeric or dashes only.'));
2283
        }
2284
      break;
2285
      case 'display_id':
2286
        if ($form_state['values']['display_id']) {
2287
          if (preg_match('/[^a-z0-9_]/', $form_state['values']['display_id'])) {
2288
            form_error($form['display_id'], t('Display name must be letters, numbers, or underscores only.'));
2289
          }
2290

    
2291
          foreach ($this->view->display as $id => $display) {
2292
            if ($id != $this->view->current_display && ($form_state['values']['display_id'] == $id || (isset($display->new_id) && $form_state['values']['display_id'] == $display->new_id))) {
2293
              form_error($form['display_id'], t('Display id should be unique.'));
2294
            }
2295
          }
2296
        }
2297
        break;
2298
      case 'style_options':
2299
        $style = TRUE;
2300
      case 'row_options':
2301
        // if row, $style will be empty.
2302
        $plugin = $this->get_plugin(empty($style) ? 'row' : 'style');
2303
        if ($plugin) {
2304
          $plugin->options_validate($form[$form_state['section']], $form_state);
2305
        }
2306
        break;
2307
      case 'access_options':
2308
        $plugin = $this->get_plugin('access');
2309
        if ($plugin) {
2310
          $plugin->options_validate($form['access_options'], $form_state);
2311
        }
2312
        break;
2313
      case 'query':
2314
        if ($this->view->query) {
2315
          $this->view->query->options_validate($form['query'], $form_state);
2316
        }
2317
        break;
2318
      case 'cache_options':
2319
        $plugin = $this->get_plugin('cache');
2320
        if ($plugin) {
2321
          $plugin->options_validate($form['cache_options'], $form_state);
2322
        }
2323
        break;
2324
      case 'exposed_form_options':
2325
        $plugin = $this->get_plugin('exposed_form');
2326
        if ($plugin) {
2327
          $plugin->options_validate($form['exposed_form_options'], $form_state);
2328
        }
2329
        break;
2330
      case 'pager_options':
2331
        $plugin = $this->get_plugin('pager');
2332
        if ($plugin) {
2333
          $plugin->options_validate($form['pager_options'], $form_state);
2334
        }
2335
        break;
2336
    }
2337

    
2338
    foreach ($this->extender as $extender) {
2339
      $extender->options_validate($form, $form_state);
2340
    }
2341
  }
2342

    
2343
  /**
2344
   * Perform any necessary changes to the form values prior to storage.
2345
   * There is no need for this function to actually store the data.
2346
   */
2347
  function options_submit(&$form, &$form_state) {
2348
    // Not sure I like this being here, but it seems (?) like a logical place.
2349
    $cache_plugin = $this->get_plugin('cache');
2350
    if ($cache_plugin) {
2351
      $cache_plugin->cache_flush();
2352
    }
2353

    
2354
    $section = $form_state['section'];
2355
    switch ($section) {
2356
      case 'display_id':
2357
        if (isset($form_state['values']['display_id'])) {
2358
          $this->display->new_id = $form_state['values']['display_id'];
2359
        }
2360
        break;
2361
      case 'display_title':
2362
        $this->display->display_title = $form_state['values']['display_title'];
2363
        $this->set_option('display_description', $form_state['values']['display_description']);
2364
        break;
2365
      case 'access':
2366
        $access = $this->get_option('access');
2367
        if ($access['type'] != $form_state['values']['access']['type']) {
2368
          $plugin = views_get_plugin('access', $form_state['values']['access']['type']);
2369
          if ($plugin) {
2370
            $access = array('type' => $form_state['values']['access']['type']);
2371
            $this->set_option('access', $access);
2372
            if (!empty($plugin->definition['uses options'])) {
2373
              views_ui_add_form_to_stack('display', $this->view, $this->display->id, array('access_options'));
2374
            }
2375
          }
2376
        }
2377

    
2378
        break;
2379
      case 'access_options':
2380
        $plugin = views_get_plugin('access', $form_state['values'][$section]['type']);
2381
        if ($plugin) {
2382
          $plugin->options_submit($form['access_options'], $form_state);
2383
          $this->set_option('access', $form_state['values'][$section]);
2384
        }
2385
        break;
2386
      case 'cache':
2387
        $cache = $this->get_option('cache');
2388
        if ($cache['type'] != $form_state['values']['cache']['type']) {
2389
          $plugin = views_get_plugin('cache', $form_state['values']['cache']['type']);
2390
          if ($plugin) {
2391
            $cache = array('type' => $form_state['values']['cache']['type']);
2392
            $this->set_option('cache', $cache);
2393
            if (!empty($plugin->definition['uses options'])) {
2394
              views_ui_add_form_to_stack('display', $this->view, $this->display->id, array('cache_options'));
2395
            }
2396
          }
2397
        }
2398

    
2399
        break;
2400
      case 'cache_options':
2401
        $plugin = views_get_plugin('cache', $form_state['values'][$section]['type']);
2402
        if ($plugin) {
2403
          $plugin->options_submit($form['cache_options'], $form_state);
2404
          $this->set_option('cache', $form_state['values'][$section]);
2405
        }
2406
        break;
2407
      case 'query':
2408
        $plugin = $this->get_plugin('query');
2409
        if ($plugin) {
2410
          $plugin->options_submit($form['query']['options'], $form_state);
2411
          $this->set_option('query', $form_state['values'][$section]);
2412
        }
2413
        break;
2414

    
2415
      case 'link_display':
2416
        $this->set_option('link_url', $form_state['values']['link_url']);
2417
      case 'title':
2418
      case 'css_class':
2419
      case 'display_comment':
2420
        $this->set_option($section, $form_state['values'][$section]);
2421
        break;
2422
      case 'field_language':
2423
        $this->set_option('field_language', $form_state['values']['field_language']);
2424
        $this->set_option('field_language_add_to_query', $form_state['values']['field_language_add_to_query']);
2425
        break;
2426
      case 'use_ajax':
2427
      case 'hide_attachment_summary':
2428
      case 'hide_admin_links':
2429
        $this->set_option($section, (bool)$form_state['values'][$section]);
2430
        break;
2431
      case 'use_more':
2432
        $this->set_option($section, intval($form_state['values'][$section]));
2433
        $this->set_option('use_more_always', !intval($form_state['values']['use_more_always']));
2434
        $this->set_option('open_new_window', $form_state['values']['open_new_window']) == '1';
2435
        $this->set_option('use_more_text', $form_state['values']['use_more_text']);
2436
      case 'distinct':
2437
        $this->set_option($section, $form_state['values'][$section]);
2438
        break;
2439
      case 'group_by':
2440
        $this->set_option($section, $form_state['values'][$section]);
2441
        break;
2442
      case 'row_plugin':
2443
        // This if prevents resetting options to default if they don't change
2444
        // the plugin.
2445
        if ($this->get_option($section) != $form_state['values'][$section]) {
2446
          $plugin = views_get_plugin('row', $form_state['values'][$section]);
2447
          if ($plugin) {
2448
            $this->set_option($section, $form_state['values'][$section]);
2449
            $this->set_option('row_options', array());
2450

    
2451
            // send ajax form to options page if we use it.
2452
            if (!empty($plugin->definition['uses options'])) {
2453
              views_ui_add_form_to_stack('display', $this->view, $this->display->id, array('row_options'));
2454
            }
2455
          }
2456
        }
2457
        break;
2458
      case 'style_plugin':
2459
        // This if prevents resetting options to default if they don't change
2460
        // the plugin.
2461
        if ($this->get_option($section) != $form_state['values'][$section]) {
2462
          $plugin = views_get_plugin('style', $form_state['values'][$section]);
2463
          if ($plugin) {
2464
            $this->set_option($section, $form_state['values'][$section]);
2465
            $this->set_option('style_options', array());
2466
            // send ajax form to options page if we use it.
2467
            if (!empty($plugin->definition['uses options'])) {
2468
              views_ui_add_form_to_stack('display', $this->view, $this->display->id, array('style_options'));
2469
            }
2470
          }
2471
        }
2472
        break;
2473
      case 'style_options':
2474
        $style = TRUE;
2475
      case 'row_options':
2476
        // if row, $style will be empty.
2477
        $plugin = $this->get_plugin(empty($style) ? 'row' : 'style');
2478
        if ($plugin) {
2479
          $plugin->options_submit($form['options'][$section], $form_state);
2480
        }
2481
        $this->set_option($section, $form_state['values'][$section]);
2482
        break;
2483
      case 'exposed_block':
2484
        $this->set_option($section, (bool) $form_state['values'][$section]);
2485
        break;
2486
      case 'exposed_form':
2487
        $exposed_form = $this->get_option('exposed_form');
2488
        if ($exposed_form['type'] != $form_state['values']['exposed_form']['type']) {
2489
          $plugin = views_get_plugin('exposed_form', $form_state['values']['exposed_form']['type']);
2490
          if ($plugin) {
2491
            $exposed_form = array('type' => $form_state['values']['exposed_form']['type'], 'options' => array());
2492
            $this->set_option('exposed_form', $exposed_form);
2493
            if (!empty($plugin->definition['uses options'])) {
2494
              views_ui_add_form_to_stack('display', $this->view, $this->display->id, array('exposed_form_options'));
2495
            }
2496
          }
2497
        }
2498

    
2499
        break;
2500
      case 'exposed_form_options':
2501
        $plugin = $this->get_plugin('exposed_form');
2502
        if ($plugin) {
2503
          $exposed_form = $this->get_option('exposed_form');
2504
          $plugin->options_submit($form['exposed_form_options'], $form_state);
2505
          $exposed_form['options'] = $form_state['values'][$section];
2506
          $this->set_option('exposed_form', $exposed_form);
2507
        }
2508
        break;
2509
      case 'pager':
2510
        $pager = $this->get_option('pager');
2511
        if ($pager['type'] != $form_state['values']['pager']['type']) {
2512
          $plugin = views_get_plugin('pager', $form_state['values']['pager']['type']);
2513
          if ($plugin) {
2514
            // Because pagers have very similar options, let's allow pagers to
2515
            // try to carry the options over.
2516
            $plugin->init($this->view, $this->display, $pager['options']);
2517

    
2518
            $pager = array('type' => $form_state['values']['pager']['type'], 'options' => $plugin->options);
2519
            $this->set_option('pager', $pager);
2520
            if (!empty($plugin->definition['uses options'])) {
2521
              views_ui_add_form_to_stack('display', $this->view, $this->display->id, array('pager_options'));
2522
            }
2523
          }
2524
        }
2525

    
2526
        break;
2527
      case 'pager_options':
2528
        $plugin = $this->get_plugin('pager');
2529
        if ($plugin) {
2530
          $pager = $this->get_option('pager');
2531
          $plugin->options_submit($form['pager_options'], $form_state);
2532
          $pager['options'] = $form_state['values'][$section];
2533
          $this->set_option('pager', $pager);
2534
        }
2535
        break;
2536
    }
2537

    
2538
    foreach ($this->extender as $extender) {
2539
      $extender->options_submit($form, $form_state);
2540
    }
2541
  }
2542

    
2543
  /**
2544
   * If override/revert was clicked, perform the proper toggle.
2545
   */
2546
  function options_override($form, &$form_state) {
2547
    $this->set_override($form_state['section']);
2548
  }
2549

    
2550
  /**
2551
   * Flip the override setting for the given section.
2552
   *
2553
   * @param string $section
2554
   *   Which option should be marked as overridden, for example "filters".
2555
   * @param bool $new_state
2556
   *   Select the new state of the option.
2557
   *     - TRUE: Revert to default.
2558
   *     - FALSE: Mark it as overridden.
2559
   */
2560
  function set_override($section, $new_state = NULL) {
2561
    $options = $this->defaultable_sections($section);
2562
    if (!$options) {
2563
      return;
2564
    }
2565

    
2566
    if (!isset($new_state)) {
2567
      $new_state = empty($this->options['defaults'][$section]);
2568
    }
2569

    
2570
    // For each option that is part of this group, fix our settings.
2571
    foreach ($options as $option) {
2572
      if ($new_state) {
2573
        // Revert to defaults.
2574
        unset($this->options[$option]);
2575
        unset($this->display->display_options[$option]);
2576
      }
2577
      else {
2578
        // copy existing values into our display.
2579
        $this->options[$option] = $this->get_option($option);
2580
        $this->display->display_options[$option] = $this->options[$option];
2581
      }
2582
      $this->options['defaults'][$option] = $new_state;
2583
      $this->display->display_options['defaults'][$option] = $new_state;
2584
    }
2585
  }
2586

    
2587
  /**
2588
   * Inject anything into the query that the display handler needs.
2589
   */
2590
  function query() {
2591
    foreach ($this->extender as $extender) {
2592
      $extender->query();
2593
    }
2594
  }
2595

    
2596
  /**
2597
   * Not all display plugins will support filtering
2598
   */
2599
  function render_filters() { }
2600

    
2601
  /**
2602
   * Not all display plugins will suppert pager rendering.
2603
   */
2604
  function render_pager() {
2605
    return TRUE;
2606
  }
2607

    
2608
  /**
2609
   * Render the 'more' link
2610
   */
2611
  function render_more_link() {
2612
    if ($this->use_more() && ($this->use_more_always() || (!empty($this->view->query->pager) && $this->view->query->pager->has_more_records()))) {
2613
      $path = $this->get_path();
2614

    
2615
      if ($this->get_option('link_display') == 'custom_url' && $override_path = $this->get_option('link_url')) {
2616
        $tokens = $this->get_arguments_tokens();
2617
        $path = strtr($override_path, $tokens);
2618
      }
2619

    
2620
      if ($path) {
2621
        if (empty($override_path)) {
2622
          $path = $this->view->get_url(NULL, $path);
2623
        }
2624
        $url_options = array();
2625
        if (!empty($this->view->exposed_raw_input)) {
2626
          $url_options['query'] = $this->view->exposed_raw_input;
2627
        }
2628
        $theme = views_theme_functions('views_more', $this->view, $this->display);
2629

    
2630
        $parsed_url = drupal_parse_url($path);
2631
        // Preserve the query string from url.
2632
        if (!empty($parsed_url['query'])) {
2633
          if (!empty($url_options['query'])) {
2634
            $url_options['query'] = array_merge($parsed_url['query'], $url_options['query']);
2635
          }
2636
          else {
2637
            $url_options['query'] = $parsed_url['query'];
2638
          }
2639
          $path = $parsed_url['path'];
2640
        }
2641
        // Add fragment if applicable.
2642
        if (!empty($parsed_url['fragment'])) {
2643
          $url_options['fragment'] = $parsed_url['fragment'];
2644
        }
2645

    
2646
        $path = check_url(url($path, $url_options));
2647

    
2648
        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));
2649
      }
2650
    }
2651
  }
2652

    
2653

    
2654
  /**
2655
   * Legacy functions.
2656
   */
2657

    
2658
  /**
2659
   * Render the header of the view.
2660
   */
2661
  function render_header() {
2662
    $empty = !empty($this->view->result);
2663
    return $this->render_area('header', $empty);
2664
  }
2665

    
2666
  /**
2667
   * Render the footer of the view.
2668
   */
2669
  function render_footer() {
2670
    $empty = !empty($this->view->result);
2671
    return $this->render_area('footer', $empty);
2672
  }
2673

    
2674
  function render_empty() {
2675
    return $this->render_area('empty');
2676
  }
2677

    
2678
  /**
2679
   * If this display creates a block, implement one of these.
2680
   */
2681
  function hook_block_list($delta = 0, $edit = array()) { return array(); }
2682

    
2683
  /**
2684
   * If this display creates a page with a menu item, implement it here.
2685
   */
2686
  function hook_menu() { return array(); }
2687

    
2688
  /**
2689
   * Render this display.
2690
   */
2691
  function render() {
2692
    return theme($this->theme_functions(), array('view' => $this->view));
2693
  }
2694

    
2695
  function render_area($area, $empty = FALSE) {
2696
    $return = '';
2697
    foreach ($this->get_handlers($area) as $area) {
2698
      $return .= $area->render($empty);
2699
    }
2700
    return $return;
2701
  }
2702

    
2703

    
2704
  /**
2705
   * Determine if the user has access to this display of the view.
2706
   */
2707
  function access($account = NULL) {
2708
    if (!isset($account)) {
2709
      global $user;
2710
      $account = $user;
2711
    }
2712

    
2713
    // Full override.
2714
    if (user_access('access all views', $account)) {
2715
      return TRUE;
2716
    }
2717

    
2718
    $plugin = $this->get_plugin('access');
2719
    if ($plugin) {
2720
      return $plugin->access($account);
2721
    }
2722

    
2723
    // fallback to all access if no plugin.
2724
    return TRUE;
2725
  }
2726

    
2727
  /**
2728
   * Set up any variables on the view prior to execution. These are separated
2729
   * from execute because they are extremely common and unlikely to be
2730
   * overridden on an individual display.
2731
   */
2732
  function pre_execute() {
2733
    $this->view->set_use_ajax($this->use_ajax());
2734
    if ($this->use_more() && !$this->use_more_always()) {
2735
      $this->view->get_total_rows = TRUE;
2736
    }
2737
    $this->view->init_handlers();
2738
    if ($this->uses_exposed()) {
2739
      $exposed_form = $this->get_plugin('exposed_form');
2740
      $exposed_form->pre_execute();
2741
    }
2742

    
2743
    foreach ($this->extender as $extender) {
2744
      $extender->pre_execute();
2745
    }
2746

    
2747
    if ($this->get_option('hide_admin_links')) {
2748
      $this->view->hide_admin_links = TRUE;
2749
    }
2750
  }
2751

    
2752
  /**
2753
   * When used externally, this is how a view gets run and returns
2754
   * data in the format required.
2755
   *
2756
   * The base class cannot be executed.
2757
   */
2758
  function execute() { }
2759

    
2760
  /**
2761
   * Fully render the display for the purposes of a live preview or
2762
   * some other AJAXy reason.
2763
   */
2764
  function preview() { return $this->view->render(); }
2765

    
2766
  /**
2767
   * Displays can require a certain type of style plugin. By default, they will
2768
   * be 'normal'.
2769
   */
2770
  function get_style_type() { return 'normal'; }
2771

    
2772
  /**
2773
   * Make sure the display and all associated handlers are valid.
2774
   *
2775
   * @return
2776
   *   Empty array if the display is valid; an array of error strings if it is not.
2777
   */
2778
  function validate() {
2779
    $errors = array();
2780
    // Make sure displays that use fields HAVE fields.
2781
    if ($this->uses_fields()) {
2782
      $fields = FALSE;
2783
      foreach ($this->get_handlers('field') as $field) {
2784
        if (empty($field->options['exclude'])) {
2785
          $fields = TRUE;
2786
        }
2787
      }
2788

    
2789
      if (!$fields) {
2790
        $errors[] = t('Display "@display" uses fields but there are none defined for it or all are excluded.', array('@display' => $this->display->display_title));
2791
      }
2792
    }
2793

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

    
2798
    // Validate style plugin
2799
    $style = $this->get_plugin();
2800
    if (empty($style)) {
2801
      $errors[] = t('Display "@display" has an invalid style plugin.', array('@display' => $this->display->display_title));
2802
    }
2803
    else {
2804
      $result = $style->validate();
2805
      if (!empty($result) && is_array($result)) {
2806
        $errors = array_merge($errors, $result);
2807
      }
2808
    }
2809

    
2810
    // Validate query plugin.
2811
    $query = $this->get_plugin('query');
2812
    $result = $query->validate();
2813
    if (!empty($result) && is_array($result)) {
2814
      $errors = array_merge($errors, $result);
2815
    }
2816

    
2817
    // Validate handlers
2818
    foreach (views_object_types() as $type => $info) {
2819
      foreach ($this->get_handlers($type) as $handler) {
2820
        $result = $handler->validate();
2821
        if (!empty($result) && is_array($result)) {
2822
          $errors = array_merge($errors, $result);
2823
        }
2824
      }
2825
    }
2826

    
2827
    return $errors;
2828
  }
2829

    
2830
  /**
2831
   * Check if the provided identifier is unique.
2832
   *
2833
   * @param string $id
2834
   *   The id of the handler which is checked.
2835
   * @param string $identifier
2836
   *   The actual get identifier configured in the exposed settings.
2837
   *
2838
   * @return bool
2839
   *   Returns whether the identifier is unique on all handlers.
2840
   *
2841
   */
2842
  function is_identifier_unique($id, $identifier) {
2843
    foreach (views_object_types() as $type => $info) {
2844
      foreach ($this->get_handlers($type) as $key => $handler) {
2845
        if ($handler->can_expose() && $handler->is_exposed()) {
2846
          if ($handler->is_a_group()) {
2847
            if ($id != $key && $identifier == $handler->options['group_info']['identifier']) {
2848
              return FALSE;
2849
            }
2850
          }
2851
          else {
2852
            if ($id != $key && isset($handler->options['expose']['identifier']) && $identifier == $handler->options['expose']['identifier']) {
2853
              return FALSE;
2854
            }
2855
          }
2856
        }
2857
      }
2858
    }
2859
    return TRUE;
2860
  }
2861

    
2862
  /**
2863
   * Provide the block system with any exposed widget blocks for this display.
2864
   */
2865
  function get_special_blocks() {
2866
    $blocks = array();
2867

    
2868
    if ($this->uses_exposed_form_in_block()) {
2869
      $delta = '-exp-' . $this->view->name . '-' . $this->display->id;
2870
      $desc = t('Exposed form: @view-@display_id', array('@view' => $this->view->name, '@display_id' => $this->display->id));
2871

    
2872
      $blocks[$delta] = array(
2873
        'info' => $desc,
2874
        'cache' => DRUPAL_NO_CACHE,
2875
      );
2876
    }
2877

    
2878
    return $blocks;
2879
  }
2880

    
2881
  /**
2882
   * Render any special blocks provided for this display.
2883
   */
2884
  function view_special_blocks($type) {
2885
    if ($type == '-exp') {
2886
      // avoid interfering with the admin forms.
2887
      if (arg(0) == 'admin' && arg(1) == 'structure' && arg(2) == 'views') {
2888
        return;
2889
      }
2890
      $this->view->init_handlers();
2891

    
2892
      if ($this->uses_exposed() && $this->get_option('exposed_block')) {
2893
        $exposed_form = $this->get_plugin('exposed_form');
2894
        return array(
2895
          'content' => $exposed_form->render_exposed_form(TRUE),
2896
        );
2897
      }
2898
    }
2899
  }
2900

    
2901
  /**
2902
   * Override of export_option()
2903
   *
2904
   * Because displays do not want to export options that are NOT overridden from the
2905
   * default display, we need some special handling during the export process.
2906
   */
2907
  function export_option($indent, $prefix, $storage, $option, $definition, $parents) {
2908
    // The $prefix is wrong because we store our actual options a little differently:
2909
    $prefix = '$handler->display->display_options';
2910
    $output = '';
2911
    if (!$parents && !$this->is_default_display()) {
2912
      // Do not export items that are not overridden.
2913
      if ($this->is_defaulted($option)) {
2914
        return;
2915
      }
2916

    
2917
      // If this is not defaulted and is overrideable, flip the switch to say this
2918
      // is overridden.
2919
      if ($this->defaultable_sections($option)) {
2920
        $output .= $indent . $prefix . "['defaults']['$option'] = FALSE;\n";
2921
      }
2922
    }
2923

    
2924
    $output .= parent::export_option($indent, $prefix, $storage, $option, $definition, $parents);
2925
    return $output;
2926
  }
2927

    
2928
  /**
2929
   * Special method to export items that have handlers.
2930
   *
2931
   * This method was specified in the option_definition() as the method to utilize to
2932
   * export fields, filters, sort criteria, relationships and arguments. This passes
2933
   * the export off to the individual handlers so that they can export themselves
2934
   * properly.
2935
   */
2936
  function export_handler($indent, $prefix, $storage, $option, $definition, $parents) {
2937
    $output = '';
2938

    
2939
    // cut the 's' off because the data is stored as the plural form but we need
2940
    // the singular form. Who designed that anyway? Oh yeah, I did. :(
2941
    if ($option != 'header' && $option != 'footer' && $option != 'empty') {
2942
      $type = substr($option, 0, -1);
2943
    }
2944
    else {
2945
      $type = $option;
2946
    }
2947
    $types = views_object_types();
2948
    foreach ($storage[$option] as $id => $info) {
2949
      if (!empty($types[$type]['type'])) {
2950
        $handler_type = $types[$type]['type'];
2951
      }
2952
      else {
2953
        $handler_type = $type;
2954
      }
2955
      // If aggregation is on, the group type might override the actual
2956
      // handler that is in use. This piece of code checks that and,
2957
      // if necessary, sets the override handler.
2958
      $override = NULL;
2959
      if ($this->use_group_by() && !empty($info['group_type'])) {
2960
        if (empty($this->view->query)) {
2961
          $this->view->init_query();
2962
        }
2963
        $aggregate = $this->view->query->get_aggregation_info();
2964
        if (!empty($aggregate[$info['group_type']]['handler'][$type])) {
2965
          $override = $aggregate[$info['group_type']]['handler'][$type];
2966
        }
2967
      }
2968
      $handler = views_get_handler($info['table'], $info['field'], $handler_type, $override);
2969
      if ($handler) {
2970
        $handler->init($this->view, $info);
2971
        $output .= $indent . '/* ' . $types[$type]['stitle'] . ': ' . $handler->ui_name() . " */\n";
2972
        $output .= $handler->export_options($indent, $prefix . "['$option']['$id']");
2973
      }
2974

    
2975
      // Prevent reference problems.
2976
      unset($handler);
2977
    }
2978

    
2979
    return $output;
2980
  }
2981

    
2982
  /**
2983
   * Special handling for the style export.
2984
   *
2985
   * Styles are stored as style_plugin and style_options or row_plugin and
2986
   * row_options accordingly. The options are told not to export, and the
2987
   * export for the plugin should export both.
2988
   */
2989
  function export_style($indent, $prefix, $storage, $option, $definition, $parents) {
2990
    $output = '';
2991
    $style_plugin = $this->get_plugin();
2992
    if ($option == 'style_plugin') {
2993
      $type = 'style';
2994
      $options_field = 'style_options';
2995
      $plugin = $style_plugin;
2996
    }
2997
    else {
2998
      if (!$style_plugin || !$style_plugin->uses_row_plugin()) {
2999
        return;
3000
      }
3001

    
3002
      $type = 'row';
3003
      $options_field = 'row_options';
3004
      $plugin = $this->get_plugin('row');
3005
      // If the style plugin doesn't use row plugins, don't even bother.
3006
    }
3007

    
3008
    if ($plugin) {
3009
      // Write which plugin to use.
3010
      $value = $this->get_option($option);
3011
      $output .= $indent . $prefix . "['$option'] = '$value';\n";
3012

    
3013
      // Pass off to the plugin to export itself.
3014
      $output .= $plugin->export_options($indent, $prefix . "['$options_field']");
3015
    }
3016

    
3017
    return $output;
3018
  }
3019

    
3020
  /**
3021
   * Special handling for plugin export
3022
   *
3023
   * Plugins other than styles are stored in array with 'type' being the key
3024
   * to the plugin. For modern plugins, the options are stored in the 'options'
3025
   * array, but for legacy plugins (access and cache) options are stored as
3026
   * siblings to the type.
3027
   */
3028
  function export_plugin($indent, $prefix, $storage, $option, $definition, $parents) {
3029
    $output = '';
3030
    $plugin_type = end($parents);
3031
    $plugin = $this->get_plugin($plugin_type);
3032
    if ($plugin) {
3033
      // Write which plugin to use.
3034
      $value = $storage[$option];
3035
      $new_prefix = $prefix . "['$plugin_type']";
3036

    
3037
      $output .= $indent . $new_prefix . "['$option'] = '$value';\n";
3038

    
3039
      if ($plugin_type != 'access' && $plugin_type!= 'cache') {
3040
        $new_prefix .= "['options']";
3041
      }
3042

    
3043
      // Pass off to the plugin to export itself.
3044
      $output .= $plugin->export_options($indent, $new_prefix);
3045
    }
3046

    
3047
    return $output;
3048
  }
3049

    
3050
  function unpack_style($indent, $prefix, $storage, $option, $definition, $parents) {
3051
    $output = '';
3052
    $style_plugin = $this->get_plugin();
3053
    if ($option == 'style_plugin') {
3054
      $type = 'style';
3055
      $options_field = 'style_options';
3056
      $plugin = $style_plugin;
3057
    }
3058
    else {
3059
      if (!$style_plugin || !$style_plugin->uses_row_plugin()) {
3060
        return;
3061
      }
3062

    
3063
      $type = 'row';
3064
      $options_field = 'row_options';
3065
      $plugin = $this->get_plugin('row');
3066
      // If the style plugin doesn't use row plugins, don't even bother.
3067
    }
3068

    
3069
    if ($plugin) {
3070
      return $plugin->unpack_translatables($translatable, $parents);
3071
    }
3072
  }
3073

    
3074
  /**
3075
   * Special handling for plugin unpacking.
3076
   */
3077
  function unpack_plugin(&$translatable, $storage, $option, $definition, $parents) {
3078
    $plugin_type = end($parents);
3079
    $plugin = $this->get_plugin($plugin_type);
3080
    if ($plugin) {
3081
      // Write which plugin to use.
3082
      return $plugin->unpack_translatables($translatable, $parents);
3083
    }
3084
  }
3085

    
3086
    /**
3087
   * Special method to unpack items that have handlers.
3088
   *
3089
   * This method was specified in the option_definition() as the method to utilize to
3090
   * export fields, filters, sort criteria, relationships and arguments. This passes
3091
   * the export off to the individual handlers so that they can export themselves
3092
   * properly.
3093
   */
3094
  function unpack_handler(&$translatable, $storage, $option, $definition, $parents) {
3095
    $output = '';
3096

    
3097
    // cut the 's' off because the data is stored as the plural form but we need
3098
    // the singular form. Who designed that anyway? Oh yeah, I did. :(
3099
    if ($option != 'header' && $option != 'footer' && $option != 'empty') {
3100
      $type = substr($option, 0, -1);
3101
    }
3102
    else {
3103
      $type = $option;
3104
    }
3105
    $types = views_object_types();
3106
    foreach ($storage[$option] as $id => $info) {
3107
      if (!empty($types[$type]['type'])) {
3108
        $handler_type = $types[$type]['type'];
3109
      }
3110
      else {
3111
        $handler_type = $type;
3112
      }
3113
      $handler = views_get_handler($info['table'], $info['field'], $handler_type);
3114
      if ($handler) {
3115
        $handler->init($this->view, $info);
3116
        $handler->unpack_translatables($translatable, array_merge($parents, array($type, $info['table'], $info['id'])));
3117
      }
3118

    
3119
      // Prevent reference problems.
3120
      unset($handler);
3121
    }
3122

    
3123
    return $output;
3124
  }
3125

    
3126
  /**
3127
   * Provide some helpful text for the arguments.
3128
   * The result should contain of an array with
3129
   *   - filter value present: The title of the fieldset in the argument
3130
   *     where you can configure what should be done with a given argument.
3131
   *   - filter value not present: The tiel of the fieldset in the argument
3132
   *     where you can configure what should be done if the argument does not
3133
   *     exist.
3134
   *   - description: A description about how arguments comes to the display.
3135
   *     For example blocks don't get it from url.
3136
   */
3137
  function get_argument_text() {
3138
    return array(
3139
      'filter value not present' => t('When the filter value is <em>NOT</em> available'),
3140
      'filter value present' => t('When the filter value <em>IS</em> available or a default is provided'),
3141
      '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'."),
3142
    );
3143
  }
3144

    
3145
  /**
3146
   * Provide some helpful text for pagers.
3147
   *
3148
   * The result should contain of an array within
3149
   *   - items per page title
3150
   */
3151
  function get_pager_text() {
3152
    return array(
3153
      'items per page title' => t('Items to display'),
3154
      'items per page description' => t('The number of items to display. Enter 0 for no limit.')
3155
    );
3156
  }
3157
}
3158

    
3159

    
3160
/**
3161
 * @}
3162
 */