Project

General

Profile

Paste
Download (109 KB) Statistics
| Branch: | Revision:

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

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('@handler' => $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
   * Does the display have custom link text?
370
   */
371
  function use_more_text() {
372
    if (!empty($this->definition['use more'])) {
373
      return $this->get_option('use_more_text');
374
    }
375
    return FALSE;
376
  }
377

    
378
  /**
379
   * Can this display accept attachments?
380
   */
381
  function accept_attachments() {
382
    if (empty($this->definition['accept attachments'])) {
383
      return FALSE;
384
    }
385
    if (!empty($this->view->argument) && $this->get_option('hide_attachment_summary')) {
386
      foreach ($this->view->argument as $argument_id => $argument) {
387
        if ($argument->needs_style_plugin() && empty($argument->argument_validated)) {
388
          return FALSE;
389
        }
390
      }
391
    }
392
    return TRUE;
393
  }
394

    
395
  /**
396
   * Allow displays to attach to other views.
397
   */
398
  function attach_to($display_id) { }
399

    
400
  /**
401
   * Static member function to list which sections are defaultable
402
   * and what items each section contains.
403
   */
404
  function defaultable_sections($section = NULL) {
405
    $sections = array(
406
      'access' => array('access', 'access_options'),
407
      'access_options' => array('access', 'access_options'),
408
      'cache' => array('cache', 'cache_options'),
409
      'cache_options' => array('cache', 'cache_options'),
410
      'title' => array('title'),
411
      'css_class' => array('css_class'),
412
      'use_ajax' => array('use_ajax'),
413
      'hide_attachment_summary' => array('hide_attachment_summary'),
414
      'hide_admin_links' => array('hide_admin_links'),
415
      'group_by' => array('group_by'),
416
      'query' => array('query'),
417
      'use_more' => array('use_more', 'use_more_always', 'use_more_text'),
418
      'use_more_always' => array('use_more', 'use_more_always', 'use_more_text'),
419
      'use_more_text' => array('use_more', 'use_more_always', 'use_more_text'),
420
      'link_display' => array('link_display', 'link_url'),
421

    
422
      // Force these to cascade properly.
423
      'style_plugin' => array('style_plugin', 'style_options', 'row_plugin', 'row_options'),
424
      'style_options' => array('style_plugin', 'style_options', 'row_plugin', 'row_options'),
425
      'row_plugin' => array('style_plugin', 'style_options', 'row_plugin', 'row_options'),
426
      'row_options' => array('style_plugin', 'style_options', 'row_plugin', 'row_options'),
427

    
428
      'pager' => array('pager', 'pager_options'),
429
      'pager_options' => array('pager', 'pager_options'),
430

    
431
      'exposed_form' => array('exposed_form', 'exposed_form_options'),
432
      'exposed_form_options' => array('exposed_form', 'exposed_form_options'),
433

    
434
      // These guys are special
435
      'header' => array('header'),
436
      'footer' => array('footer'),
437
      'empty' => array('empty'),
438
      'relationships' => array('relationships'),
439
      'fields' => array('fields'),
440
      'sorts' => array('sorts'),
441
      'arguments' => array('arguments'),
442
      'filters' => array('filters', 'filter_groups'),
443
      'filter_groups' => array('filters', 'filter_groups'),
444
    );
445

    
446
    // If the display cannot use a pager, then we cannot default it.
447
    if (empty($this->definition['use pager'])) {
448
      unset($sections['pager']);
449
      unset($sections['items_per_page']);
450
    }
451

    
452
    foreach ($this->extender as $extender) {
453
      $extender->defaultable_sections($sections, $section);
454
    }
455

    
456
    if ($section) {
457
      if (!empty($sections[$section])) {
458
        return $sections[$section];
459
      }
460
    }
461
    else {
462
      return $sections;
463
    }
464
  }
465

    
466
  function option_definition() {
467
    $options = array(
468
      'defaults' => array(
469
        'default' => array(
470
          'access' => TRUE,
471
          'cache' => TRUE,
472
          'query' => TRUE,
473
          'title' => TRUE,
474
          'css_class' => TRUE,
475

    
476
          'display_description' => FALSE,
477
          'use_ajax' => TRUE,
478
          'hide_attachment_summary' => TRUE,
479
          'hide_admin_links' => TRUE,
480
          'pager' => TRUE,
481
          'pager_options' => TRUE,
482
          'use_more' => TRUE,
483
          'use_more_always' => TRUE,
484
          'use_more_text' => TRUE,
485
          'exposed_form' => TRUE,
486
          'exposed_form_options' => TRUE,
487

    
488
          'link_display' => TRUE,
489
          'link_url' => '',
490
          'group_by' => TRUE,
491

    
492
          'style_plugin' => TRUE,
493
          'style_options' => TRUE,
494
          'row_plugin' => TRUE,
495
          'row_options' => TRUE,
496

    
497
          'header' => TRUE,
498
          'footer' => TRUE,
499
          'empty' => TRUE,
500

    
501
          'relationships' => TRUE,
502
          'fields' => TRUE,
503
          'sorts' => TRUE,
504
          'arguments' => TRUE,
505
          'filters' => TRUE,
506
          'filter_groups' => TRUE,
507
        ),
508
        'export' => FALSE,
509
      ),
510

    
511
      'title' => array(
512
        'default' => '',
513
        'translatable' => TRUE,
514
      ),
515
      'enabled' => array(
516
        'default' => TRUE,
517
        'translatable' => FALSE,
518
        'bool' => TRUE,
519
      ),
520
      'display_comment' => array(
521
        'default' => '',
522
      ),
523
      'css_class' => array(
524
        'default' => '',
525
        'translatable' => FALSE,
526
      ),
527
      'display_description' => array(
528
        'default' => '',
529
        'translatable' => TRUE,
530
      ),
531
      'use_ajax' => array(
532
        'default' => FALSE,
533
        'bool' => TRUE,
534
      ),
535
      'hide_attachment_summary' => array(
536
        'default' => FALSE,
537
        'bool' => TRUE,
538
      ),
539
      'hide_admin_links' => array(
540
        'default' => FALSE,
541
        'bool' => TRUE,
542
      ),
543
      // This is legacy code:
544
      // Items_per/offset/use_pager is moved to the pager plugin
545
      // but the automatic update path needs this items defined, so don't remove it.
546
      // @see views_plugin_display::init()
547
      'items_per_page' => array(
548
        'default' => 10,
549
      ),
550
      'offset' => array(
551
        'default' => 0,
552
      ),
553
      'use_pager' => array(
554
        'default' => FALSE,
555
        'bool' => TRUE,
556
      ),
557
      'use_more' => array(
558
        'default' => FALSE,
559
        'bool' => TRUE,
560
      ),
561
      'use_more_always' => array(
562
        'default' => FALSE,
563
        'bool' => TRUE,
564
        'export' => 'export_option_always',
565
      ),
566
      'use_more_text' => array(
567
        'default' => 'more',
568
        'translatable' => TRUE,
569
      ),
570
      'link_display' => array(
571
        'default' => '',
572
      ),
573
      'link_url' => array(
574
        'default' => '',
575
      ),
576
      'group_by' => array(
577
        'default' => FALSE,
578
        'bool' => TRUE,
579
      ),
580
      'field_language' => array(
581
        'default' => '***CURRENT_LANGUAGE***',
582
      ),
583
      'field_language_add_to_query' => array(
584
        'default' => 1,
585
      ),
586

    
587
      // These types are all plugins that can have individual settings
588
      // and therefore need special handling.
589
      'access' => array(
590
        'contains' => array(
591
          'type' => array('default' => 'none', 'export' => 'export_plugin', 'unpack_translatable' => 'unpack_plugin'),
592
         ),
593
      ),
594
      'cache' => array(
595
        'contains' => array(
596
          'type' => array('default' => 'none', 'export' => 'export_plugin', 'unpack_translatable' => 'unpack_plugin'),
597
         ),
598
      ),
599
      'query' => array(
600
        'contains' => array(
601
          'type' => array('default' => 'views_query', 'export' => 'export_plugin'),
602
          'options' => array('default' => array(), 'export' => FALSE),
603
         ),
604
      ),
605
      // Note that exposed_form plugin has options in a separate array,
606
      // while access and cache do not. access and cache are legacy and
607
      // that pattern should not be repeated, but it is left as is to
608
      // reduce the need to modify older views. Let's consider the
609
      // pattern used here to be the template from which future plugins
610
      // should be copied.
611
      'exposed_form' => array(
612
        'contains' => array(
613
          'type' => array('default' => 'basic', 'export' => 'export_plugin', 'unpack_translatable' => 'unpack_plugin'),
614
          'options' => array('default' => array(), 'export' => FALSE),
615
         ),
616
      ),
617
      'pager' => array(
618
        'contains' => array(
619
          'type' => array('default' => 'full', 'export' => 'export_plugin', 'unpack_translatable' => 'unpack_plugin'),
620
          'options' => array('default' => array(), 'export' => FALSE),
621
         ),
622
      ),
623

    
624
      // Note that the styles have their options completely independent.
625
      // Like access and cache above, this is a legacy pattern and
626
      // should not be repeated.
627
      'style_plugin' => array(
628
        'default' => 'default',
629
        'export' => 'export_style',
630
        'unpack_translatable' => 'unpack_style',
631
      ),
632
      'style_options' => array(
633
        'default' => array(),
634
        'export' => FALSE,
635
      ),
636
      'row_plugin' => array(
637
        'default' => 'fields',
638
        'export' => 'export_style',
639
        'unpack_translatable' => 'unpack_style',
640
      ),
641
      'row_options' => array(
642
        'default' => array(),
643
        'export' => FALSE,
644
      ),
645

    
646
      'exposed_block' => array(
647
        'default' => FALSE,
648
      ),
649

    
650
      'header' => array(
651
        'default' => array(),
652
        'export' => 'export_handler',
653
        'unpack_translatable' => 'unpack_handler',
654
      ),
655
      'footer' => array(
656
        'default' => array(),
657
        'export' => 'export_handler',
658
        'unpack_translatable' => 'unpack_handler',
659
      ),
660
      'empty' => array(
661
        'default' => array(),
662
        'export' => 'export_handler',
663
        'unpack_translatable' => 'unpack_handler',
664
      ),
665

    
666
      // We want these to export last.
667
      // These are the 5 handler types.
668
      'relationships' => array(
669
        'default' => array(),
670
        'export' => 'export_handler',
671
        'unpack_translatable' => 'unpack_handler',
672

    
673
      ),
674
      'fields' => array(
675
        'default' => array(),
676
        'export' => 'export_handler',
677
        'unpack_translatable' => 'unpack_handler',
678
      ),
679
      'sorts' => array(
680
        'default' => array(),
681
        'export' => 'export_handler',
682
        'unpack_translatable' => 'unpack_handler',
683
      ),
684
      'arguments' => array(
685
        'default' => array(),
686
        'export' => 'export_handler',
687
        'unpack_translatable' => 'unpack_handler',
688
      ),
689
      'filter_groups' => array(
690
        'contains' => array(
691
          'operator' => array('default' => 'AND'),
692
          'groups' => array('default' => array(1 => 'AND')),
693
        ),
694
      ),
695
      'filters' => array(
696
        'default' => array(),
697
        'export' => 'export_handler',
698
        'unpack_translatable' => 'unpack_handler',
699
      ),
700
    );
701

    
702
    if (empty($this->definition['use pager'])) {
703
      $options['defaults']['default']['use_pager'] = FALSE;
704
      $options['defaults']['default']['items_per_page'] = FALSE;
705
      $options['defaults']['default']['offset'] = FALSE;
706
      $options['defaults']['default']['pager'] = FALSE;
707
      $options['pager']['contains']['type']['default'] = 'some';
708
    }
709

    
710
    if ($this->is_default_display()) {
711
      unset($options['defaults']);
712
    }
713

    
714
    foreach ($this->extender as $extender) {
715
      $extender->options_definition_alter($options);
716
    }
717

    
718
    return $options;
719
  }
720

    
721
  /**
722
   * Check to see if the display has a 'path' field.
723
   *
724
   * This is a pure function and not just a setting on the definition
725
   * because some displays (such as a panel pane) may have a path based
726
   * upon configuration.
727
   *
728
   * By default, displays do not have a path.
729
   */
730
  function has_path() { return FALSE; }
731

    
732
  /**
733
   * Check to see if the display has some need to link to another display.
734
   *
735
   * For the most part, displays without a path will use a link display. However,
736
   * sometimes displays that have a path might also need to link to another display.
737
   * This is true for feeds.
738
   */
739
  function uses_link_display() { return !$this->has_path(); }
740

    
741
  /**
742
   * Check to see if the display can put the exposed form in a block.
743
   *
744
   * By default, displays that do not have a path cannot disconnect
745
   * the exposed form and put it in a block, because the form has no
746
   * place to go and Views really wants the forms to go to a specific
747
   * page.
748
   */
749
  function uses_exposed_form_in_block() { return $this->has_path(); }
750

    
751
  /**
752
   * Check to see which display to use when creating links within
753
   * a view using this display.
754
   */
755
  function get_link_display() {
756
    $display_id = $this->get_option('link_display');
757
    // If unknown, pick the first one.
758
    if (empty($display_id) || empty($this->view->display[$display_id])) {
759
      foreach ($this->view->display as $display_id => $display) {
760
        if (!empty($display->handler) && $display->handler->has_path()) {
761
          return $display_id;
762
        }
763
      }
764
    }
765
    else {
766
      return $display_id;
767
    }
768
    // fall-through returns NULL
769
  }
770

    
771
  /**
772
   * Return the base path to use for this display.
773
   *
774
   * This can be overridden for displays that do strange things
775
   * with the path.
776
   */
777
  function get_path() {
778
    if ($this->has_path()) {
779
      return $this->get_option('path');
780
    }
781

    
782
    $display_id = $this->get_link_display();
783
    if ($display_id && !empty($this->view->display[$display_id]) && is_object($this->view->display[$display_id]->handler)) {
784
      return $this->view->display[$display_id]->handler->get_path();
785
    }
786

    
787
    if ($this->get_option('link_display') == 'custom_url' && $link_url = $this->get_option('link_url')) {
788
      return $link_url;
789
    }
790
  }
791

    
792
  function get_url() {
793
    return $this->view->get_url();
794
  }
795

    
796
  /**
797
   * Check to see if the display needs a breadcrumb
798
   *
799
   * By default, displays do not need breadcrumbs
800
   */
801
  function uses_breadcrumb() { return FALSE; }
802

    
803
  /**
804
   * Determine if a given option is set to use the default display or the
805
   * current display
806
   *
807
   * @return
808
   *   TRUE for the default display
809
   */
810
  function is_defaulted($option) {
811
    return !$this->is_default_display() && !empty($this->default_display) && !empty($this->options['defaults'][$option]);
812
  }
813

    
814
  /**
815
   * Intelligently get an option either from this display or from the
816
   * default display, if directed to do so.
817
   */
818
  function get_option($option) {
819
    if ($this->is_defaulted($option)) {
820
      return $this->default_display->get_option($option);
821
    }
822

    
823
    if (array_key_exists($option, $this->options)) {
824
      return $this->options[$option];
825
    }
826
  }
827

    
828
  /**
829
   * Determine if the display's style uses fields.
830
   */
831
  function uses_fields() {
832
    $plugin = $this->get_plugin();
833
    if ($plugin) {
834
      return $plugin->uses_fields();
835
    }
836
  }
837

    
838
  /**
839
   * Get the instance of a plugin, for example style or row.
840
   *
841
   * @param string $type
842
   *   The type of the plugin.
843
   * @param string $name
844
   *   The name of the plugin defined in hook_views_plugins.
845
   *
846
   * @return views_plugin|FALSE
847
   */
848
  function get_plugin($type = 'style', $name = NULL) {
849
    static $cache = array();
850
    if (!isset($cache[$type][$name])) {
851
      switch ($type) {
852
        case 'style':
853
        case 'row':
854
          $option_name = $type . '_plugin';
855
          $options = $this->get_option($type . '_options');
856
          if (!$name) {
857
            $name = $this->get_option($option_name);
858
          }
859

    
860
          break;
861
        case 'query':
862
          $views_data = views_fetch_data($this->view->base_table);
863
          $name = !empty($views_data['table']['base']['query class']) ? $views_data['table']['base']['query class'] : 'views_query';
864
        default:
865
          $option_name = $type;
866
          $options = $this->get_option($type);
867
          if (!$name) {
868
            $name = $options['type'];
869
          }
870

    
871
          // access & cache store their options as siblings with the
872
          // type; all others use an 'options' array.
873
          if ($type != 'access' && $type != 'cache') {
874
            $options = $options['options'];
875
          }
876
      }
877
      $plugin = views_get_plugin($type, $name);
878

    
879
      if (!$plugin) {
880
        return;
881
      }
882
      if ($type != 'query') {
883
        $plugin->init($this->view, $this->display, $options);
884
      }
885
      else {
886
        $display_id = $this->is_defaulted($option_name) ? $this->display->id : 'default';
887
        $plugin->localization_keys = array($display_id, $type);
888

    
889
        if (!isset($this->base_field)) {
890
          $views_data = views_fetch_data($this->view->base_table);
891
          $this->view->base_field = !empty($views_data['table']['base']['field']) ? $views_data['table']['base']['field'] : '';
892
        }
893
        $plugin->init($this->view->base_table, $this->view->base_field, $options);
894
      }
895
      $cache[$type][$name] = $plugin;
896
    }
897

    
898
    return $cache[$type][$name];
899
  }
900

    
901
  /**
902
   * Get the handler object for a single handler.
903
   */
904
  function &get_handler($type, $id) {
905
    if (!isset($this->handlers[$type])) {
906
      $this->get_handlers($type);
907
    }
908

    
909
    if (isset($this->handlers[$type][$id])) {
910
      return $this->handlers[$type][$id];
911
    }
912

    
913
    // So we can return a reference.
914
    $null = NULL;
915
    return $null;
916
  }
917

    
918
  /**
919
   * Get a full array of handlers for $type. This caches them.
920
   */
921
  function &get_handlers($type) {
922
    if (!isset($this->handlers[$type])) {
923
      $this->handlers[$type] = array();
924
      $types = views_object_types();
925
      $plural = $types[$type]['plural'];
926

    
927
      foreach ($this->get_option($plural) as $id => $info) {
928
        // If this is during form submission and there are temporary options
929
        // which can only appear if the view is in the edit cache, use those
930
        // options instead. This is used for AJAX multi-step stuff.
931
        if (isset($_POST['form_id']) && isset($this->view->temporary_options[$type][$id])) {
932
          $info = $this->view->temporary_options[$type][$id];
933
        }
934

    
935
        if ($info['id'] != $id) {
936
          $info['id'] = $id;
937
        }
938

    
939
        // If aggregation is on, the group type might override the actual
940
        // handler that is in use. This piece of code checks that and,
941
        // if necessary, sets the override handler.
942
        $override = NULL;
943
        if ($this->use_group_by() && !empty($info['group_type'])) {
944
          if (empty($this->view->query)) {
945
            $this->view->init_query();
946
          }
947
          $aggregate = $this->view->query->get_aggregation_info();
948
          if (!empty($aggregate[$info['group_type']]['handler'][$type])) {
949
            $override = $aggregate[$info['group_type']]['handler'][$type];
950
          }
951
        }
952

    
953
        if (!empty($types[$type]['type'])) {
954
          $handler_type = $types[$type]['type'];
955
        }
956
        else {
957
          $handler_type = $type;
958
        }
959

    
960
        $handler = views_get_handler($info['table'], $info['field'], $handler_type, $override);
961
        if ($handler) {
962
          // Special override for area types so they know where they come from.
963
          if ($handler_type == 'area') {
964
            $handler->handler_type = $type;
965
          }
966

    
967
          $handler->init($this->view, $info);
968
          $this->handlers[$type][$id] = &$handler;
969
        }
970

    
971
        // Prevent reference problems.
972
        unset($handler);
973
      }
974
    }
975

    
976
    return $this->handlers[$type];
977
  }
978

    
979
  /**
980
   * Retrieve a list of fields for the current display with the
981
   * relationship associated if it exists.
982
   *
983
   * @param $groupable_only
984
   *  Return only an array of field labels from handler that return TRUE
985
   *  from use_string_group_by method.
986
   */
987
  function get_field_labels() {
988
    // Use func_get_arg so the function signature isn't amended
989
    // but we can still pass TRUE into the function to filter
990
    // by groupable handlers.
991
    $args = func_get_args();
992
    $groupable_only = isset($args[0]) ? $args[0] : FALSE;
993

    
994
    $options = array();
995
    foreach ($this->get_handlers('relationship') as $relationship => $handler) {
996
      if ($label = $handler->label()) {
997
        $relationships[$relationship] = $label;
998
      }
999
      else {
1000
        $relationships[$relationship] = $handler->ui_name();
1001
      }
1002
    }
1003

    
1004
    foreach ($this->get_handlers('field') as $id => $handler) {
1005
      if ($groupable_only && !$handler->use_string_group_by()) {
1006
        // Continue to next handler if it's not groupable.
1007
        continue;
1008
      }
1009
      if ($label = $handler->label()) {
1010
        $options[$id] = $label;
1011
      }
1012
      else {
1013
        $options[$id] = $handler->ui_name();
1014
      }
1015
      if (!empty($handler->options['relationship']) && !empty($relationships[$handler->options['relationship']])) {
1016
        $options[$id] = '(' . $relationships[$handler->options['relationship']] . ') ' . $options[$id];
1017
      }
1018
    }
1019
    return $options;
1020
  }
1021

    
1022
  /**
1023
   * Intelligently set an option either from this display or from the
1024
   * default display, if directed to do so.
1025
   */
1026
  function set_option($option, $value) {
1027
    if ($this->is_defaulted($option)) {
1028
      return $this->default_display->set_option($option, $value);
1029
    }
1030

    
1031
    // Set this in two places: On the handler where we'll notice it
1032
    // but also on the display object so it gets saved. This should
1033
    // only be a temporary fix.
1034
    $this->display->display_options[$option] = $value;
1035
    return $this->options[$option] = $value;
1036
  }
1037

    
1038
  /**
1039
   * Set an option and force it to be an override.
1040
   */
1041
  function override_option($option, $value) {
1042
    $this->set_override($option, FALSE);
1043
    $this->set_option($option, $value);
1044
  }
1045

    
1046
  /**
1047
   * Because forms may be split up into sections, this provides
1048
   * an easy URL to exactly the right section. Don't override this.
1049
   */
1050
  function option_link($text, $section, $class = '', $title = '') {
1051
    views_add_js('ajax');
1052
    if (!empty($class)) {
1053
      $text = '<span>' . $text . '</span>';
1054
    }
1055

    
1056
    if (!trim($text)) {
1057
      $text = t('Broken field');
1058
    }
1059

    
1060
    if (empty($title)) {
1061
      $title = $text;
1062
    }
1063

    
1064
    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));
1065
  }
1066

    
1067
  /**
1068
   * Returns to tokens for arguments.
1069
   *
1070
   * This function is similar to views_handler_field::get_render_tokens()
1071
   * but without fields tokens.
1072
   */
1073
  function get_arguments_tokens() {
1074
    $tokens = array();
1075
    if (!empty($this->view->build_info['substitutions'])) {
1076
      $tokens = $this->view->build_info['substitutions'];
1077
    }
1078
    $count = 0;
1079
    foreach ($this->view->display_handler->get_handlers('argument') as $arg => $handler) {
1080
      $token = '%' . ++$count;
1081
      if (!isset($tokens[$token])) {
1082
        $tokens[$token] = '';
1083
      }
1084

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

    
1091
    return $tokens;
1092
  }
1093

    
1094
  /**
1095
   * Provide the default summary for options in the views UI.
1096
   *
1097
   * This output is returned as an array.
1098
   */
1099
  function options_summary(&$categories, &$options) {
1100
    $categories = array(
1101
      'title' => array(
1102
        'title' => t('Title'),
1103
        'column' => 'first',
1104
      ),
1105
      'format' => array(
1106
        'title' => t('Format'),
1107
        'column' => 'first',
1108
      ),
1109
      'filters' => array(
1110
        'title' => t('Filters'),
1111
        'column' => 'first',
1112
      ),
1113
      'fields' => array(
1114
        'title' => t('Fields'),
1115
        'column' => 'first',
1116
      ),
1117
      'pager' => array(
1118
        'title' => t('Pager'),
1119
        'column' => 'second',
1120
      ),
1121
      'exposed' => array(
1122
        'title' => t('Exposed form'),
1123
        'column' => 'third',
1124
        'build' => array(
1125
          '#weight' => 1,
1126
        ),
1127
      ),
1128
      'access' => array(
1129
        'title' => '',
1130
        'column' => 'second',
1131
        'build' => array(
1132
          '#weight' => -5,
1133
        ),
1134
      ),
1135
      'other' => array(
1136
        'title' => t('Other'),
1137
        'column' => 'third',
1138
        'build' => array(
1139
          '#weight' => 2,
1140
        ),
1141
      ),
1142
    );
1143

    
1144
    if ($this->display->id != 'default') {
1145
      $options['display_id'] = array(
1146
        'category' => 'other',
1147
        'title' => t('Machine Name'),
1148
        'value' => !empty($this->display->new_id) ? check_plain($this->display->new_id) : check_plain($this->display->id),
1149
        'desc' => t('Change the machine name of this display.'),
1150
      );
1151
    }
1152

    
1153
    $display_comment = check_plain(views_ui_truncate($this->get_option('display_comment'), 80));
1154
    $options['display_comment'] = array(
1155
      'category' => 'other',
1156
      'title' => t('Comment'),
1157
      'value' => !empty($display_comment) ? $display_comment : t('No comment'),
1158
      'desc' => t('Comment or document this display.'),
1159
    );
1160

    
1161
    $title = strip_tags($this->get_option('title'));
1162
    if (!$title) {
1163
      $title = t('None');
1164
    }
1165

    
1166
    $options['title'] = array(
1167
      'category' => 'title',
1168
      'title' => t('Title'),
1169
      'value' => $title,
1170
      'desc' => t('Change the title that this display will use.'),
1171
    );
1172

    
1173
    $style_plugin = views_fetch_plugin_data('style', $this->get_option('style_plugin'));
1174
    $style_plugin_instance = $this->get_plugin('style');
1175
    $style_summary = empty($style_plugin['title']) ? t('Missing style plugin') : $style_plugin_instance->summary_title();
1176
    $style_title = empty($style_plugin['title']) ? t('Missing style plugin') : $style_plugin_instance->plugin_title();
1177

    
1178
    $style = '';
1179

    
1180
    $options['style_plugin'] = array(
1181
      'category' => 'format',
1182
      'title' => t('Format'),
1183
      'value' => $style_title,
1184
      'setting' => $style_summary,
1185
      'desc' => t('Change the way content is formatted.'),
1186
    );
1187

    
1188
    // This adds a 'Settings' link to the style_options setting if the style has options.
1189
    if (!empty($style_plugin['uses options'])) {
1190
      $options['style_plugin']['links']['style_options'] = t('Change settings for this format');
1191
    }
1192

    
1193
    if (!empty($style_plugin['uses row plugin'])) {
1194
      $row_plugin = views_fetch_plugin_data('row', $this->get_option('row_plugin'));
1195
      $row_plugin_instance = $this->get_plugin('row');
1196
      $row_summary = empty($row_plugin['title']) ? t('Missing style plugin') : $row_plugin_instance->summary_title();
1197
      $row_title = empty($row_plugin['title']) ? t('Missing style plugin') : $row_plugin_instance->plugin_title();
1198

    
1199
      $options['row_plugin'] = array(
1200
        'category' => 'format',
1201
        'title' => t('Show'),
1202
        'value' => $row_title,
1203
        'setting' => $row_summary,
1204
        'desc' => t('Change the way each row in the view is styled.'),
1205
      );
1206
      // This adds a 'Settings' link to the row_options setting if the row style has options.
1207
      if (!empty($row_plugin['uses options'])) {
1208
        $options['row_plugin']['links']['row_options'] = t('Change settings for this style');
1209
      }
1210
    }
1211
    if (!empty($this->definition['use ajax'])) {
1212
      $options['use_ajax'] = array(
1213
        'category' => 'other',
1214
        'title' => t('Use AJAX'),
1215
        'value' => $this->get_option('use_ajax') ? t('Yes') : t('No'),
1216
        'desc' => t('Change whether or not this display will use AJAX.'),
1217
      );
1218
    }
1219
    if (!empty($this->definition['accept attachments'])) {
1220
      $options['hide_attachment_summary'] = array(
1221
        'category' => 'other',
1222
        'title' => t('Hide attachments in summary'),
1223
        'value' => $this->get_option('hide_attachment_summary') ? t('Yes') : t('No'),
1224
        'desc' => t('Change whether or not to display attachments when displaying a contextual filter summary.'),
1225
      );
1226
    }
1227
    if (!isset($this->definition['contextual links locations']) || !empty($this->definition['contextual links locations'])) {
1228
      $options['hide_admin_links'] = array(
1229
        'category' => 'other',
1230
        'title' => t('Hide contextual links'),
1231
        'value' => $this->get_option('hide_admin_links') ? t('Yes') : t('No'),
1232
        'desc' => t('Change whether or not to display contextual links for this view.'),
1233
      );
1234
    }
1235

    
1236
    $pager_plugin = $this->get_plugin('pager');
1237
    if (!$pager_plugin) {
1238
      // default to the no pager plugin.
1239
      $pager_plugin = views_get_plugin('pager', 'none');
1240
    }
1241

    
1242
    $pager_str = $pager_plugin->summary_title();
1243

    
1244
    $options['pager'] = array(
1245
      'category' => 'pager',
1246
      'title' => t('Use pager'),
1247
      'value' => $pager_plugin->plugin_title(),
1248
      'setting' => $pager_str,
1249
      'desc' => t("Change this display's pager setting."),
1250
    );
1251

    
1252
    // If pagers aren't allowed, change the text of the item:
1253
    if (empty($this->definition['use pager'])) {
1254
      $options['pager']['title'] = t('Items to display');
1255
    }
1256

    
1257
    if (!empty($pager_plugin->definition['uses options'])) {
1258
      $options['pager']['links']['pager_options'] = t('Change settings for this pager type.');
1259
    }
1260

    
1261
    if (!empty($this->definition['use more'])) {
1262
      $options['use_more'] = array(
1263
        'category' => 'pager',
1264
        'title' => t('More link'),
1265
        'value' => $this->get_option('use_more') ? t('Yes') : t('No'),
1266
        'desc' => t('Specify whether this display will provide a "more" link.'),
1267
      );
1268
    }
1269

    
1270
    $this->view->init_query();
1271
    if ($this->view->query->get_aggregation_info()) {
1272
      $options['group_by'] = array(
1273
        'category' => 'other',
1274
        'title' => t('Use aggregation'),
1275
        'value' => $this->get_option('group_by') ? t('Yes') : t('No'),
1276
        'desc' => t('Allow grouping and aggregation (calculation) of fields.'),
1277
      );
1278
    }
1279

    
1280
    $options['query'] = array(
1281
      'category' => 'other',
1282
      'title' => t('Query settings'),
1283
      'value' => t('Settings'),
1284
      'desc' => t('Allow to set some advanced settings for the query plugin'),
1285
    );
1286

    
1287
    $languages = array(
1288
        '***CURRENT_LANGUAGE***' => t("Current user's language"),
1289
        '***DEFAULT_LANGUAGE***' => t("Default site language"),
1290
        LANGUAGE_NONE => t('Language neutral'),
1291
    );
1292
    if (module_exists('locale')) {
1293
      $languages = array_merge($languages, locale_language_list());
1294
    }
1295
    $field_language = array();
1296
    $options['field_language'] = array(
1297
      'category' => 'other',
1298
      'title' => t('Field Language'),
1299
      'value' => $languages[$this->get_option('field_language')],
1300
      'desc' => t('All fields which support translations will be displayed in the selected language.'),
1301
    );
1302

    
1303
    $access_plugin = $this->get_plugin('access');
1304
    if (!$access_plugin) {
1305
      // default to the no access control plugin.
1306
      $access_plugin = views_get_plugin('access', 'none');
1307
    }
1308

    
1309
    $access_str = $access_plugin->summary_title();
1310

    
1311
    $options['access'] = array(
1312
      'category' => 'access',
1313
      'title' => t('Access'),
1314
      'value' => $access_plugin->plugin_title(),
1315
      'setting' => $access_str,
1316
      'desc' => t('Specify access control type for this display.'),
1317
    );
1318

    
1319
    if (!empty($access_plugin->definition['uses options'])) {
1320
      $options['access']['links']['access_options'] = t('Change settings for this access type.');
1321
    }
1322

    
1323
    $cache_plugin = $this->get_plugin('cache');
1324
    if (!$cache_plugin) {
1325
      // default to the no cache control plugin.
1326
      $cache_plugin = views_get_plugin('cache', 'none');
1327
    }
1328

    
1329
    $cache_str = $cache_plugin->summary_title();
1330

    
1331
    $options['cache'] = array(
1332
      'category' => 'other',
1333
      'title' => t('Caching'),
1334
      'value' => $cache_plugin->plugin_title(),
1335
      'setting' => $cache_str,
1336
      'desc' => t('Specify caching type for this display.'),
1337
    );
1338

    
1339
    if (!empty($cache_plugin->definition['uses options'])) {
1340
      $options['cache']['links']['cache_options'] = t('Change settings for this caching type.');
1341
    }
1342

    
1343
    if (!empty($access_plugin->definition['uses options'])) {
1344
      $options['access']['links']['access_options'] = t('Change settings for this access type.');
1345
    }
1346

    
1347
    if ($this->uses_link_display()) {
1348
      $display_id = $this->get_link_display();
1349
      $link_display = empty($this->view->display[$display_id]) ? t('None') : check_plain($this->view->display[$display_id]->display_title);
1350
      $link_display =  $this->get_option('link_display') == 'custom_url' ? t('Custom URL') : $link_display;
1351
      $options['link_display'] = array(
1352
        'category' => 'other',
1353
        'title' => t('Link display'),
1354
        'value' => $link_display,
1355
        'desc' => t('Specify which display or custom url this display will link to.'),
1356
      );
1357
    }
1358

    
1359
    if ($this->uses_exposed_form_in_block()) {
1360
      $options['exposed_block'] = array(
1361
        'category' => 'exposed',
1362
        'title' => t('Exposed form in block'),
1363
        'value' => $this->get_option('exposed_block') ? t('Yes') : t('No'),
1364
        'desc' => t('Allow the exposed form to appear in a block instead of the view.'),
1365
      );
1366
    }
1367

    
1368
    $exposed_form_plugin = $this->get_plugin('exposed_form');
1369
    if (!$exposed_form_plugin) {
1370
      // default to the no cache control plugin.
1371
      $exposed_form_plugin = views_get_plugin('exposed_form', 'basic');
1372
    }
1373

    
1374
    $exposed_form_str = $exposed_form_plugin->summary_title();
1375

    
1376
    $options['exposed_form'] = array(
1377
      'category' => 'exposed',
1378
      'title' => t('Exposed form style'),
1379
      'value' => $exposed_form_plugin->plugin_title(),
1380
      'setting' => $exposed_form_str,
1381
      'desc' => t('Select the kind of exposed filter to use.'),
1382
    );
1383

    
1384
    if (!empty($exposed_form_plugin->definition['uses options'])) {
1385
      $options['exposed_form']['links']['exposed_form_options'] = t('Exposed form settings for this exposed form style.');
1386
    }
1387

    
1388
    $css_class = check_plain(trim($this->get_option('css_class')));
1389
    if (!$css_class) {
1390
      $css_class = t('None');
1391
    }
1392

    
1393
    $options['css_class'] = array(
1394
      'category' => 'other',
1395
      'title' => t('CSS class'),
1396
      'value' => $css_class,
1397
      'desc' => t('Change the CSS class name(s) that will be added to this display.'),
1398
    );
1399

    
1400
    $options['analyze-theme'] = array(
1401
      'category' => 'other',
1402
      'title' => t('Theme'),
1403
      'value' => t('Information'),
1404
      'desc' => t('Get information on how to theme this display'),
1405
    );
1406

    
1407
    foreach ($this->extender as $extender) {
1408
      $extender->options_summary($categories, $options);
1409
    }
1410
  }
1411

    
1412
  /**
1413
   * Provide the default form for setting options.
1414
   */
1415
  function options_form(&$form, &$form_state) {
1416
    parent::options_form($form, $form_state);
1417
    if ($this->defaultable_sections($form_state['section'])) {
1418
      views_ui_standard_display_dropdown($form, $form_state, $form_state['section']);
1419
    }
1420
    $form['#title'] = check_plain($this->display->display_title) . ': ';
1421

    
1422
    // Set the 'section' to highlight on the form.
1423
    // If it's the item we're looking at is pulling from the default display,
1424
    // reflect that. Don't use is_defaulted since we want it to show up even
1425
    // on the default display.
1426
    if (!empty($this->options['defaults'][$form_state['section']])) {
1427
      $form['#section'] = 'default-' . $form_state['section'];
1428
    }
1429
    else {
1430
      $form['#section'] = $this->display->id . '-' . $form_state['section'];
1431
    }
1432

    
1433
    switch ($form_state['section']) {
1434
      case 'display_id':
1435
        $form['#title'] .= t('The machine name of this display');
1436
        $form['display_id'] = array(
1437
          '#type' => 'textfield',
1438
          '#description' => t('This is machine name of the display.'),
1439
          '#default_value' => !empty($this->display->new_id) ? $this->display->new_id : $this->display->id,
1440
          '#required' => TRUE,
1441
          '#size' => 64,
1442
        );
1443
        break;
1444
      case 'display_title':
1445
        $form['#title'] .= t('The name and the description of this display');
1446
        $form['display_title'] = array(
1447
          '#title' => t('Name'),
1448
          '#type' => 'textfield',
1449
          '#description' => t('This name will appear only in the administrative interface for the View.'),
1450
          '#default_value' => $this->display->display_title,
1451
        );
1452
        $form['display_description'] = array(
1453
          '#title' => t('Description'),
1454
          '#type' => 'textfield',
1455
          '#description' => t('This description will appear only in the administrative interface for the View.'),
1456
          '#default_value' => $this->get_option('display_description'),
1457
        );
1458
        break;
1459
      case 'display_comment':
1460
        $form['#title'] .= t("This display's comments");
1461
        $form['display_comment'] = array(
1462
          '#type' => 'textarea',
1463
          '#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.'),
1464
          '#default_value' => $this->get_option('display_comment'),
1465
        );
1466
        break;
1467
      case 'title':
1468
        $form['#title'] .= t('The title of this view');
1469
        $form['title'] = array(
1470
          '#type' => 'textfield',
1471
          '#description' => t('This title will be displayed with the view, wherever titles are normally displayed; i.e, as the page title, block title, etc.'),
1472
          '#default_value' => $this->get_option('title'),
1473
        );
1474
        break;
1475
      case 'css_class':
1476
        $form['#title'] .= t('CSS class');
1477
        $form['css_class'] = array(
1478
          '#type' => 'textfield',
1479
          '#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.'),
1480
          '#default_value' => $this->get_option('css_class'),
1481
        );
1482
        break;
1483
      case 'use_ajax':
1484
        $form['#title'] .= t('Use AJAX when available to load this view');
1485
        $form['description'] = array(
1486
          '#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>',
1487
        );
1488
        $form['use_ajax'] = array(
1489
          '#type' => 'radios',
1490
          '#options' => array(1 => t('Yes'), 0 => t('No')),
1491
          '#default_value' => $this->get_option('use_ajax') ? 1 : 0,
1492
        );
1493
        break;
1494
      case 'hide_attachment_summary':
1495
        $form['#title'] .= t('Hide attachments when displaying a contextual filter summary');
1496
        $form['hide_attachment_summary'] = array(
1497
          '#type' => 'radios',
1498
          '#options' => array(1 => t('Yes'), 0 => t('No')),
1499
          '#default_value' => $this->get_option('hide_attachment_summary') ? 1 : 0,
1500
        );
1501
        break;
1502
      case 'hide_admin_links':
1503
        $form['#title'] .= t('Hide contextual links on this view.');
1504
        $form['hide_admin_links'] = array(
1505
          '#type' => 'radios',
1506
          '#options' => array(1 => t('Yes'), 0 => t('No')),
1507
          '#default_value' => $this->get_option('hide_admin_links') ? 1 : 0,
1508
        );
1509
      break;
1510
      case 'use_more':
1511
        $form['#title'] .= t('Add a more link to the bottom of the display.');
1512
        $form['use_more'] = array(
1513
          '#type' => 'checkbox',
1514
          '#title' => t('Create more link'),
1515
          '#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."),
1516
          '#default_value' => $this->get_option('use_more'),
1517
        );
1518
        $form['use_more_always'] = array(
1519
          '#type' => 'checkbox',
1520
          '#title' => t("Display 'more' link only if there is more content"),
1521
          '#description' => t("Leave this unchecked to display the 'more' link even if there are no more items to display."),
1522
          '#default_value' => !$this->get_option('use_more_always'),
1523
            '#dependency' => array(
1524
              'edit-use-more' => array(TRUE),
1525
            ),
1526
        );
1527
        $form['use_more_text'] = array(
1528
          '#type' => 'textfield',
1529
          '#title' => t('More link text'),
1530
          '#description' => t("The text to display for the more link."),
1531
          '#default_value' => $this->get_option('use_more_text'),
1532
          '#dependency' => array(
1533
            'edit-use-more' => array(TRUE),
1534
          ),
1535
        );
1536
        break;
1537
      case 'group_by':
1538
        $form['#title'] .= t('Allow grouping and aggregation (calculation) of fields.');
1539
        $form['group_by'] = array(
1540
          '#type' => 'checkbox',
1541
          '#title' => t('Aggregate'),
1542
          '#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.'),
1543
          '#default_value' => $this->get_option('group_by'),
1544
        );
1545
        break;
1546
      case 'access':
1547
        $form['#title'] .= t('Access restrictions');
1548
        $form['access'] = array(
1549
          '#prefix' => '<div class="clearfix">',
1550
          '#suffix' => '</div>',
1551
          '#tree' => TRUE,
1552
        );
1553

    
1554
        $access = $this->get_option('access');
1555
        $form['access']['type'] =  array(
1556
          '#type' => 'radios',
1557
          '#options' => views_fetch_plugin_names('access', NULL, array($this->view->base_table)),
1558
          '#default_value' => $access['type'],
1559
        );
1560

    
1561
        $access_plugin = views_fetch_plugin_data('access', $access['type']);
1562
        if (!empty($access_plugin['uses options'])) {
1563
          $form['markup'] = array(
1564
            '#prefix' => '<div class="form-item description">',
1565
            '#markup' => t('You may also adjust the !settings for the currently selected access restriction.', array('!settings' => $this->option_link(t('settings'), 'access_options'))),
1566
            '#suffix' => '</div>',
1567
          );
1568
        }
1569

    
1570
        break;
1571
      case 'access_options':
1572
        $access = $this->get_option('access');
1573
        $plugin = $this->get_plugin('access');
1574
        $form['#title'] .= t('Access options');
1575
        if ($plugin) {
1576
          if (!empty($plugin->definition['help topic'])) {
1577
            $form['#help_topic'] = $plugin->definition['help topic'];
1578
          }
1579
          if (!empty($plugin->definition['module'])) {
1580
            $form['#help_module'] = $plugin->definition['module'];
1581
          }
1582

    
1583
          $form['access_options'] = array(
1584
            '#tree' => TRUE,
1585
          );
1586
          $form['access_options']['type'] = array(
1587
            '#type' => 'value',
1588
            '#value' => $access['type'],
1589
          );
1590
          $plugin->options_form($form['access_options'], $form_state);
1591
        }
1592
        break;
1593
      case 'cache':
1594
        $form['#title'] .= t('Caching');
1595
        $form['cache'] = array(
1596
          '#prefix' => '<div class="clearfix">',
1597
          '#suffix' => '</div>',
1598
          '#tree' => TRUE,
1599
        );
1600

    
1601
        $cache = $this->get_option('cache');
1602
        $form['cache']['type'] =  array(
1603
          '#type' => 'radios',
1604
          '#options' => views_fetch_plugin_names('cache', NULL, array($this->view->base_table)),
1605
          '#default_value' => $cache['type'],
1606
        );
1607

    
1608
        $cache_plugin = views_fetch_plugin_data('cache', $cache['type']);
1609
        if (!empty($cache_plugin['uses options'])) {
1610
          $form['markup'] = array(
1611
            '#prefix' => '<div class="form-item description">',
1612
            '#suffix' => '</div>',
1613
            '#markup' => t('You may also adjust the !settings for the currently selected cache mechanism.', array('!settings' => $this->option_link(t('settings'), 'cache_options'))),
1614
          );
1615
        }
1616
        break;
1617
      case 'cache_options':
1618
        $cache = $this->get_option('cache');
1619
        $plugin = $this->get_plugin('cache');
1620
        $form['#title'] .= t('Caching options');
1621
        if ($plugin) {
1622
          if (!empty($plugin->definition['help topic'])) {
1623
            $form['#help_topic'] = $plugin->definition['help topic'];
1624
          }
1625
          if (!empty($plugin->definition['module'])) {
1626
            $form['#help_module'] = $plugin->definition['module'];
1627
          }
1628

    
1629
          $form['cache_options'] = array(
1630
            '#tree' => TRUE,
1631
          );
1632
          $form['cache_options']['type'] = array(
1633
            '#type' => 'value',
1634
            '#value' => $cache['type'],
1635
          );
1636
          $plugin->options_form($form['cache_options'], $form_state);
1637
        }
1638
        break;
1639
      case 'query':
1640
        $query_options = $this->get_option('query');
1641
        $plugin_name = $query_options['type'];
1642

    
1643
        $form['#title'] .= t('Query options');
1644
        $this->view->init_query();
1645
        if ($this->view->query) {
1646
          if (!empty($this->view->query->definition['help topic'])) {
1647
            $form['#help_topic'] = $this->view->query->definition['help topic'];
1648
          }
1649
          if (!empty($this->view->query->definition['module'])) {
1650
            $form['#help_module'] = $this->view->query->definition['module'];
1651
          }
1652

    
1653
          $form['query'] = array(
1654
            '#tree' => TRUE,
1655
            'type' => array(
1656
              '#type' => 'value',
1657
              '#value' => $plugin_name,
1658
            ),
1659
            'options' => array(
1660
              '#tree' => TRUE,
1661
            ),
1662
          );
1663

    
1664
          $this->view->query->options_form($form['query']['options'], $form_state);
1665
        }
1666
        break;
1667
      case 'field_language':
1668
        $form['#title'] .= t('Field Language');
1669

    
1670
        $entities = entity_get_info();
1671
        $entity_tables = array();
1672
        $has_translation_handlers = FALSE;
1673
        foreach ($entities as $type => $entity_info) {
1674
          $entity_tables[] = $entity_info['base table'];
1675

    
1676
          if (!empty($entity_info['translation'])) {
1677
            $has_translation_handlers = TRUE;
1678
          }
1679
        }
1680

    
1681
        // Doesn't make sense to show a field setting here if we aren't querying
1682
        // an entity base table. Also, we make sure that there's at least one
1683
        // entity type with a translation handler attached.
1684
        if (in_array($this->view->base_table, $entity_tables) && $has_translation_handlers) {
1685
          $languages = array(
1686
            '***CURRENT_LANGUAGE***' => t("Current user's language"),
1687
            '***DEFAULT_LANGUAGE***' => t("Default site language"),
1688
            LANGUAGE_NONE => t('Language neutral'),
1689
          );
1690
          $languages = array_merge($languages, views_language_list());
1691

    
1692
          $form['field_language'] = array(
1693
            '#type' => 'select',
1694
            '#title' => t('Field Language'),
1695
            '#description' => t('All fields which support translations will be displayed in the selected language.'),
1696
            '#options' => $languages,
1697
            '#default_value' => $this->get_option('field_language'),
1698
          );
1699
          $form['field_language_add_to_query'] = array(
1700
            '#type' => 'checkbox',
1701
            '#title' => t('When needed, add the field language condition to the query'),
1702
            '#default_value' => $this->get_option('field_language_add_to_query'),
1703
          );
1704
        }
1705
        else {
1706
          $form['field_language']['#markup'] = t("You don't have translatable entity types.");
1707
        }
1708
        break;
1709
      case 'style_plugin':
1710
        $form['#title'] .= t('How should this view be styled');
1711
        $form['#help_topic'] = 'style';
1712
        $form['style_plugin'] =  array(
1713
          '#type' => 'radios',
1714
          '#options' => views_fetch_plugin_names('style', $this->get_style_type(), array($this->view->base_table)),
1715
          '#default_value' => $this->get_option('style_plugin'),
1716
          '#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.'),
1717
        );
1718

    
1719
        $style_plugin = views_fetch_plugin_data('style', $this->get_option('style_plugin'));
1720
        if (!empty($style_plugin['uses options'])) {
1721
          $form['markup'] = array(
1722
            '#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>',
1723
          );
1724
        }
1725

    
1726
        break;
1727
      case 'style_options':
1728
        $form['#title'] .= t('Style options');
1729
        $style = TRUE;
1730
        $type = 'style_plugin';
1731
        $name = $this->get_option('style_plugin');
1732

    
1733
      case 'row_options':
1734
        if (!isset($name)) {
1735
          $name = $this->get_option('row_plugin');
1736
        }
1737
        // if row, $style will be empty.
1738
        if (empty($style)) {
1739
          $form['#title'] .= t('Row style options');
1740
          $type = 'row_plugin';
1741
        }
1742
        $plugin = $this->get_plugin(empty($style) ? 'row' : 'style');
1743
        if ($plugin) {
1744
          if (!empty($plugin->definition['help topic'])) {
1745
            $form['#help_topic'] = $plugin->definition['help topic'];
1746
          }
1747
          if (!empty($plugin->definition['module'])) {
1748
            $form['#help_module'] = $plugin->definition['module'];
1749
          }
1750
          $form[$form_state['section']] = array(
1751
            '#tree' => TRUE,
1752
          );
1753
          $plugin->options_form($form[$form_state['section']], $form_state);
1754
        }
1755
        break;
1756
      case 'row_plugin':
1757
        $form['#title'] .= t('How should each row in this view be styled');
1758
        $form['#help_topic'] = 'style-row';
1759
        $form['row_plugin'] =  array(
1760
          '#type' => 'radios',
1761
          '#options' => views_fetch_plugin_names('row', $this->get_style_type(), array($this->view->base_table)),
1762
          '#default_value' => $this->get_option('row_plugin'),
1763
        );
1764

    
1765
        $row_plugin = views_fetch_plugin_data('row', $this->get_option('row_plugin'));
1766
        if (!empty($row_plugin['uses options'])) {
1767
          $form['markup'] = array(
1768
            '#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>',
1769
          );
1770
        }
1771

    
1772
        break;
1773
      case 'link_display':
1774
        $form['#title'] .= t('Which display to use for path');
1775
        foreach ($this->view->display as $display_id => $display) {
1776
          if ($display->handler->has_path()) {
1777
            $options[$display_id] = $display->display_title;
1778
          }
1779
        }
1780
        $options['custom_url'] = t('Custom URL');
1781
        if (count($options)) {
1782
          $form['link_display'] = array(
1783
            '#type' => 'radios',
1784
            '#options' => $options,
1785
            '#description' => t("Which display to use to get this display's path for things like summary links, rss feed links, more links, etc."),
1786
            '#default_value' => $this->get_option('link_display'),
1787
          );
1788
        }
1789

    
1790
        $options = array();
1791
        $count = 0; // This lets us prepare the key as we want it printed.
1792
        foreach ($this->view->display_handler->get_handlers('argument') as $arg => $handler) {
1793
          $options[t('Arguments')]['%' . ++$count] = t('@argument title', array('@argument' => $handler->ui_name()));
1794
          $options[t('Arguments')]['!' . $count] = t('@argument input', array('@argument' => $handler->ui_name()));
1795
        }
1796

    
1797
        // Default text.
1798
        // We have some options, so make a list.
1799
        $output = '';
1800
        if (!empty($options)) {
1801
          $output = t('<p>The following tokens are available for this link.</p>');
1802
          foreach (array_keys($options) as $type) {
1803
            if (!empty($options[$type])) {
1804
              $items = array();
1805
              foreach ($options[$type] as $key => $value) {
1806
                $items[] = $key . ' == ' . $value;
1807
              }
1808
              $output .= theme('item_list',
1809
                array(
1810
                  'items' => $items,
1811
                  'type' => $type
1812
                ));
1813
            }
1814
          }
1815
        }
1816

    
1817
        $form['link_url'] = array(
1818
          '#type' => 'textfield',
1819
          '#title' => t('Custom URL'),
1820
          '#default_value' => $this->get_option('link_url'),
1821
          '#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,
1822
          '#dependency' => array('radio:link_display' => array('custom_url')),
1823
        );
1824
        break;
1825
      case 'analyze-theme':
1826
        $form['#title'] .= t('Theming information');
1827
        $form['#help_topic'] = 'analyze-theme';
1828

    
1829
        if (isset($_POST['theme'])) {
1830
          $this->theme = $_POST['theme'];
1831
        }
1832
        elseif (empty($this->theme)) {
1833
          $this->theme = variable_get('theme_default', 'bartik');
1834
        }
1835

    
1836
        if (isset($GLOBALS['theme']) && $GLOBALS['theme'] == $this->theme) {
1837
          $this->theme_registry = theme_get_registry();
1838
          $theme_engine = $GLOBALS['theme_engine'];
1839
        }
1840
        else {
1841
          $themes = list_themes();
1842
          $theme = $themes[$this->theme];
1843

    
1844
          // Find all our ancestor themes and put them in an array.
1845
          $base_theme = array();
1846
          $ancestor = $this->theme;
1847
          while ($ancestor && isset($themes[$ancestor]->base_theme)) {
1848
            $ancestor = $themes[$ancestor]->base_theme;
1849
            $base_theme[] = $themes[$ancestor];
1850
          }
1851

    
1852
          // The base themes should be initialized in the right order.
1853
          $base_theme = array_reverse($base_theme);
1854

    
1855
          // This code is copied directly from _drupal_theme_initialize()
1856
          $theme_engine = NULL;
1857

    
1858
          // Initialize the theme.
1859
          if (isset($theme->engine)) {
1860
            // Include the engine.
1861
            include_once DRUPAL_ROOT . '/' . $theme->owner;
1862

    
1863
            $theme_engine = $theme->engine;
1864
            if (function_exists($theme_engine . '_init')) {
1865
              foreach ($base_theme as $base) {
1866
                call_user_func($theme_engine . '_init', $base);
1867
              }
1868
              call_user_func($theme_engine . '_init', $theme);
1869
            }
1870
          }
1871
          else {
1872
            // include non-engine theme files
1873
            foreach ($base_theme as $base) {
1874
              // Include the theme file or the engine.
1875
              if (!empty($base->owner)) {
1876
                include_once DRUPAL_ROOT . '/' . $base->owner;
1877
              }
1878
            }
1879
            // and our theme gets one too.
1880
            if (!empty($theme->owner)) {
1881
              include_once DRUPAL_ROOT . '/' . $theme->owner;
1882
            }
1883
          }
1884
          $this->theme_registry = _theme_load_registry($theme, $base_theme, $theme_engine);
1885
        }
1886

    
1887
        // If there's a theme engine involved, we also need to know its extension
1888
        // so we can give the proper filename.
1889
        $this->theme_extension = '.tpl.php';
1890
        if (isset($theme_engine)) {
1891
          $extension_function = $theme_engine . '_extension';
1892
          if (function_exists($extension_function)) {
1893
            $this->theme_extension = $extension_function();
1894
          }
1895
        }
1896

    
1897
        $funcs = array();
1898
        // Get theme functions for the display. Note that some displays may
1899
        // not have themes. The 'feed' display, for example, completely
1900
        // delegates to the style.
1901
        if (!empty($this->definition['theme'])) {
1902
          $funcs[] = $this->option_link(t('Display output'), 'analyze-theme-display') . ': '  . $this->format_themes($this->theme_functions());
1903
          $themes = $this->additional_theme_functions();
1904
          if ($themes) {
1905
            foreach ($themes as $theme) {
1906
              $funcs[] = $this->option_link(t('Alternative display output'), 'analyze-theme-display') . ': '  . $this->format_themes($theme);
1907
            }
1908
          }
1909
        }
1910

    
1911
        $plugin = $this->get_plugin();
1912
        if ($plugin) {
1913
          $funcs[] = $this->option_link(t('Style output'), 'analyze-theme-style') . ': ' . $this->format_themes($plugin->theme_functions(), $plugin->additional_theme_functions());
1914
          $themes = $plugin->additional_theme_functions();
1915
          if ($themes) {
1916
            foreach ($themes as $theme) {
1917
              $funcs[] = $this->option_link(t('Alternative style'), 'analyze-theme-style') . ': '  . $this->format_themes($theme);
1918
            }
1919
          }
1920

    
1921
          if ($plugin->uses_row_plugin()) {
1922
            $row_plugin = $this->get_plugin('row');
1923
            if ($row_plugin) {
1924
              $funcs[] = $this->option_link(t('Row style output'), 'analyze-theme-row') . ': ' . $this->format_themes($row_plugin->theme_functions());
1925
              $themes = $row_plugin->additional_theme_functions();
1926
              if ($themes) {
1927
                foreach ($themes as $theme) {
1928
                  $funcs[] = $this->option_link(t('Alternative row style'), 'analyze-theme-row') . ': '  . $this->format_themes($theme);
1929
                }
1930
              }
1931
            }
1932
          }
1933

    
1934
          if ($plugin->uses_fields()) {
1935
            foreach ($this->get_handlers('field') as $id => $handler) {
1936
              $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());
1937
            }
1938
          }
1939
        }
1940

    
1941
        $form['important'] = array(
1942
          '#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>',
1943
        );
1944

    
1945
        if (isset($this->view->display[$this->view->current_display]->new_id)) {
1946
          $form['important']['new_id'] = array(
1947
            '#prefix' => '<div class="description">',
1948
            '#suffix' => '</div>',
1949
            '#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."),
1950
          );
1951
        }
1952

    
1953
        foreach (list_themes() as $key => $theme) {
1954
          if (!empty($theme->info['hidden'])) {
1955
            continue;
1956
          }
1957
          $options[$key] = $theme->info['name'];
1958
        }
1959

    
1960
        $form['box'] = array(
1961
          '#prefix' => '<div class="container-inline">',
1962
          '#suffix' => '</div>',
1963
        );
1964
        $form['box']['theme'] = array(
1965
          '#type' => 'select',
1966
          '#options' => $options,
1967
          '#default_value' => $this->theme,
1968
        );
1969

    
1970
        $form['box']['change'] = array(
1971
          '#type' => 'submit',
1972
          '#value' => t('Change theme'),
1973
          '#submit' => array('views_ui_edit_display_form_change_theme'),
1974
        );
1975

    
1976
        $form['analysis'] = array(
1977
          '#markup' => '<div class="form-item">' . theme('item_list', array('items' => $funcs)) . '</div>',
1978
        );
1979

    
1980
        $form['rescan_button'] = array(
1981
          '#prefix' => '<div class="form-item">',
1982
          '#suffix' => '</div>',
1983
        );
1984
        $form['rescan_button']['button'] = array(
1985
          '#type' => 'submit',
1986
          '#value' => t('Rescan template files'),
1987
          '#submit' => array('views_ui_config_item_form_rescan'),
1988
        );
1989
        $form['rescan_button']['markup'] = array(
1990
          '#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>',
1991
        );
1992

    
1993
        $form_state['ok_button'] = TRUE;
1994
        break;
1995
      case 'analyze-theme-display':
1996
        $form['#title'] .= t('Theming information (display)');
1997
        $output = '<p>' . t('Back to !info.', array('!info' => $this->option_link(t('theming information'), 'analyze-theme'))) . '</p>';
1998

    
1999
        if (empty($this->definition['theme'])) {
2000
          $output .= t('This display has no theming information');
2001
        }
2002
        else {
2003
          $output .= '<p>' . t('This is the default theme template used for this display.') . '</p>';
2004
          $output .= '<pre>' . check_plain(file_get_contents('./' . $this->definition['theme path'] . '/' . strtr($this->definition['theme'], '_', '-') . '.tpl.php')) . '</pre>';
2005
        }
2006

    
2007
        if (!empty($this->definition['additional themes'])) {
2008
          foreach ($this->definition['additional themes'] as $theme => $type) {
2009
            $output .= '<p>' . t('This is an alternative template for this display.') . '</p>';
2010
            $output .= '<pre>' . check_plain(file_get_contents('./' . $this->definition['theme path'] . '/' . strtr($theme, '_', '-') . '.tpl.php')) . '</pre>';
2011
          }
2012
        }
2013

    
2014
        $form['analysis'] = array(
2015
          '#markup' => '<div class="form-item">' . $output . '</div>',
2016
        );
2017

    
2018
        $form_state['ok_button'] = TRUE;
2019
        break;
2020
      case 'analyze-theme-style':
2021
        $form['#title'] .= t('Theming information (style)');
2022
        $output = '<p>' . t('Back to !info.', array('!info' => $this->option_link(t('theming information'), 'analyze-theme'))) . '</p>';
2023

    
2024
        $plugin = $this->get_plugin();
2025

    
2026
        if (empty($plugin->definition['theme'])) {
2027
          $output .= t('This display has no style theming information');
2028
        }
2029
        else {
2030
          $output .= '<p>' . t('This is the default theme template used for this style.') . '</p>';
2031
          $output .= '<pre>' . check_plain(file_get_contents('./' . $plugin->definition['theme path'] . '/' . strtr($plugin->definition['theme'], '_', '-') . '.tpl.php')) . '</pre>';
2032
        }
2033

    
2034
        if (!empty($plugin->definition['additional themes'])) {
2035
          foreach ($plugin->definition['additional themes'] as $theme => $type) {
2036
            $output .= '<p>' . t('This is an alternative template for this style.') . '</p>';
2037
            $output .= '<pre>' . check_plain(file_get_contents('./' . $plugin->definition['theme path'] . '/' . strtr($theme, '_', '-') . '.tpl.php')) . '</pre>';
2038
          }
2039
        }
2040

    
2041
        $form['analysis'] = array(
2042
          '#markup' => '<div class="form-item">' . $output . '</div>',
2043
        );
2044

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

    
2051
        $plugin = $this->get_plugin('row');
2052

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

    
2061
        if (!empty($plugin->definition['additional themes'])) {
2062
          foreach ($plugin->definition['additional themes'] as $theme => $type) {
2063
            $output .= '<p>' . t('This is an alternative template for this row style.') . '</p>';
2064
            $output .= '<pre>' . check_plain(file_get_contents('./' . $plugin->definition['theme path'] . '/' . strtr($theme, '_', '-') . '.tpl.php')) . '</pre>';
2065
          }
2066
        }
2067

    
2068
        $form['analysis'] = array(
2069
          '#markup' => '<div class="form-item">' . $output . '</div>',
2070
        );
2071

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

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

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

    
2084
        $form['analysis'] = array(
2085
          '#markup' => '<div class="form-item">' . $output . '</div>',
2086
        );
2087
        $form_state['ok_button'] = TRUE;
2088
        break;
2089

    
2090
      case 'exposed_block':
2091
        $form['#title'] .= t('Put the exposed form in a block');
2092
        $form['description'] = array(
2093
          '#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>',
2094
        );
2095
        $form['exposed_block'] = array(
2096
          '#type' => 'radios',
2097
          '#options' => array(1 => t('Yes'), 0 => t('No')),
2098
          '#default_value' => $this->get_option('exposed_block') ? 1 : 0,
2099
        );
2100
        break;
2101
      case 'exposed_form':
2102
        $form['#title'] .= t('Exposed Form');
2103
        $form['exposed_form'] = array(
2104
          '#prefix' => '<div class="clearfix">',
2105
          '#suffix' => '</div>',
2106
          '#tree' => TRUE,
2107
        );
2108

    
2109
        $exposed_form = $this->get_option('exposed_form');
2110
        $form['exposed_form']['type'] =  array(
2111
          '#type' => 'radios',
2112
          '#options' => views_fetch_plugin_names('exposed_form', NULL, array($this->view->base_table)),
2113
          '#default_value' => $exposed_form['type'],
2114
        );
2115

    
2116
        $exposed_form_plugin = views_fetch_plugin_data('exposed_form', $exposed_form['type']);
2117
        if (!empty($exposed_form_plugin['uses options'])) {
2118
          $form['markup'] = array(
2119
            '#prefix' => '<div class="form-item description">',
2120
            '#suffix' => '</div>',
2121
            '#markup' => t('You may also adjust the !settings for the currently selected style.', array('!settings' => $this->option_link(t('settings'), 'exposed_form_options'))),
2122
          );
2123
        }
2124
        break;
2125
      case 'exposed_form_options':
2126
        $plugin = $this->get_plugin('exposed_form');
2127
        $form['#title'] .= t('Exposed form options');
2128
        if ($plugin) {
2129
          if (!empty($plugin->definition['help topic'])) {
2130
            $form['#help_topic'] = $plugin->definition['help topic'];
2131
          }
2132
          if (!empty($plugin->definition['module'])) {
2133
            $form['#help_module'] = $plugin->definition['module'];
2134
          }
2135

    
2136
          $form['exposed_form_options'] = array(
2137
            '#tree' => TRUE,
2138
          );
2139
          $plugin->options_form($form['exposed_form_options'], $form_state);
2140
        }
2141
        break;
2142
      case 'pager':
2143
        $form['#title'] .= t('Select which pager, if any, to use for this view');
2144
        $form['pager'] = array(
2145
          '#prefix' => '<div class="clearfix">',
2146
          '#suffix' => '</div>',
2147
          '#tree' => TRUE,
2148
        );
2149

    
2150
        $pager = $this->get_option('pager');
2151
        $form['pager']['type'] =  array(
2152
          '#type' => 'radios',
2153
          '#options' => views_fetch_plugin_names('pager', empty($this->definition['use pager']) ? 'basic' : NULL, array($this->view->base_table)),
2154
          '#default_value' => $pager['type'],
2155
        );
2156

    
2157
        $pager_plugin = views_fetch_plugin_data('pager', $pager['type']);
2158
        if (!empty($pager_plugin['uses options'])) {
2159
          $form['markup'] = array(
2160
            '#prefix' => '<div class="form-item description">',
2161
            '#suffix' => '</div>',
2162
            '#markup' => t('You may also adjust the !settings for the currently selected pager.', array('!settings' => $this->option_link(t('settings'), 'pager_options'))),
2163
          );
2164
        }
2165

    
2166
        break;
2167
      case 'pager_options':
2168
        $plugin = $this->get_plugin('pager');
2169
        $form['#title'] .= t('Pager options');
2170
        if ($plugin) {
2171
          if (!empty($plugin->definition['help topic'])) {
2172
            $form['#help_topic'] = $plugin->definition['help topic'];
2173
          }
2174
          if (!empty($plugin->definition['module'])) {
2175
            $form['#help_module'] = $plugin->definition['module'];
2176
          }
2177

    
2178
          $form['pager_options'] = array(
2179
            '#tree' => TRUE,
2180
          );
2181
          $plugin->options_form($form['pager_options'], $form_state);
2182
        }
2183
        break;
2184
    }
2185

    
2186
    foreach ($this->extender as $extender) {
2187
      $extender->options_form($form, $form_state);
2188
    }
2189
  }
2190

    
2191
  /**
2192
   * Format a list of theme templates for output by the theme info helper.
2193
   */
2194
  function format_themes($themes) {
2195
    $registry = $this->theme_registry;
2196
    $extension = $this->theme_extension;
2197

    
2198
    $output = '';
2199
    $picked = FALSE;
2200
    foreach ($themes as $theme) {
2201
      $template = strtr($theme, '_', '-') . $extension;
2202
      if (!$picked && !empty($registry[$theme])) {
2203
        $template_path = isset($registry[$theme]['path']) ? $registry[$theme]['path'] . '/' : './';
2204
        if (file_exists($template_path . $template)) {
2205
          $hint = t('File found in folder @template-path', array('@template-path' => $template_path));
2206
          $template = '<strong title="'. $hint .'">' . $template . '</strong>';
2207
        }
2208
        else {
2209
          $template = '<strong class="error">' . $template . ' ' . t('(File not found, in folder @template-path)', array('@template-path' => $template_path)) . '</strong>';
2210
        }
2211
        $picked = TRUE;
2212
      }
2213
      $fixed[] = $template;
2214
    }
2215

    
2216
    return implode(', ', array_reverse($fixed));
2217
  }
2218

    
2219
  /**
2220
   * Validate the options form.
2221
   */
2222
  function options_validate(&$form, &$form_state) {
2223
    switch ($form_state['section']) {
2224
      case 'display_title':
2225
        if (empty($form_state['values']['display_title'])) {
2226
          form_error($form['display_title'], t('Display title may not be empty.'));
2227
        }
2228
        break;
2229
      case 'css_class':
2230
        $css_class = $form_state['values']['css_class'];
2231
        if (preg_match('/[^a-zA-Z0-9-_ ]/', $css_class)) {
2232
          form_error($form['css_class'], t('CSS classes must be alphanumeric or dashes only.'));
2233
        }
2234
      break;
2235
      case 'display_id':
2236
        if ($form_state['values']['display_id']) {
2237
          if (preg_match('/[^a-z0-9_]/', $form_state['values']['display_id'])) {
2238
            form_error($form['display_id'], t('Display name must be letters, numbers, or underscores only.'));
2239
          }
2240

    
2241
          foreach ($this->view->display as $id => $display) {
2242
            if ($id != $this->view->current_display && ($form_state['values']['display_id'] == $id || (isset($display->new_id) && $form_state['values']['display_id'] == $display->new_id))) {
2243
              form_error($form['display_id'], t('Display id should be unique.'));
2244
            }
2245
          }
2246
        }
2247
        break;
2248
      case 'style_options':
2249
        $style = TRUE;
2250
      case 'row_options':
2251
        // if row, $style will be empty.
2252
        $plugin = $this->get_plugin(empty($style) ? 'row' : 'style');
2253
        if ($plugin) {
2254
          $plugin->options_validate($form[$form_state['section']], $form_state);
2255
        }
2256
        break;
2257
      case 'access_options':
2258
        $plugin = $this->get_plugin('access');
2259
        if ($plugin) {
2260
          $plugin->options_validate($form['access_options'], $form_state);
2261
        }
2262
        break;
2263
      case 'query':
2264
        if ($this->view->query) {
2265
          $this->view->query->options_validate($form['query'], $form_state);
2266
        }
2267
        break;
2268
      case 'cache_options':
2269
        $plugin = $this->get_plugin('cache');
2270
        if ($plugin) {
2271
          $plugin->options_validate($form['cache_options'], $form_state);
2272
        }
2273
        break;
2274
      case 'exposed_form_options':
2275
        $plugin = $this->get_plugin('exposed_form');
2276
        if ($plugin) {
2277
          $plugin->options_validate($form['exposed_form_options'], $form_state);
2278
        }
2279
        break;
2280
      case 'pager_options':
2281
        $plugin = $this->get_plugin('pager');
2282
        if ($plugin) {
2283
          $plugin->options_validate($form['pager_options'], $form_state);
2284
        }
2285
        break;
2286
    }
2287

    
2288
    foreach ($this->extender as $extender) {
2289
      $extender->options_validate($form, $form_state);
2290
    }
2291
  }
2292

    
2293
  /**
2294
   * Perform any necessary changes to the form values prior to storage.
2295
   * There is no need for this function to actually store the data.
2296
   */
2297
  function options_submit(&$form, &$form_state) {
2298
    // Not sure I like this being here, but it seems (?) like a logical place.
2299
    $cache_plugin = $this->get_plugin('cache');
2300
    if ($cache_plugin) {
2301
      $cache_plugin->cache_flush();
2302
    }
2303

    
2304
    $section = $form_state['section'];
2305
    switch ($section) {
2306
      case 'display_id':
2307
        if (isset($form_state['values']['display_id'])) {
2308
          $this->display->new_id = $form_state['values']['display_id'];
2309
        }
2310
        break;
2311
      case 'display_title':
2312
        $this->display->display_title = $form_state['values']['display_title'];
2313
        $this->set_option('display_description', $form_state['values']['display_description']);
2314
        break;
2315
      case 'access':
2316
        $access = $this->get_option('access');
2317
        if ($access['type'] != $form_state['values']['access']['type']) {
2318
          $plugin = views_get_plugin('access', $form_state['values']['access']['type']);
2319
          if ($plugin) {
2320
            $access = array('type' => $form_state['values']['access']['type']);
2321
            $this->set_option('access', $access);
2322
            if (!empty($plugin->definition['uses options'])) {
2323
              views_ui_add_form_to_stack('display', $this->view, $this->display->id, array('access_options'));
2324
            }
2325
          }
2326
        }
2327

    
2328
        break;
2329
      case 'access_options':
2330
        $plugin = views_get_plugin('access', $form_state['values'][$section]['type']);
2331
        if ($plugin) {
2332
          $plugin->options_submit($form['access_options'], $form_state);
2333
          $this->set_option('access', $form_state['values'][$section]);
2334
        }
2335
        break;
2336
      case 'cache':
2337
        $cache = $this->get_option('cache');
2338
        if ($cache['type'] != $form_state['values']['cache']['type']) {
2339
          $plugin = views_get_plugin('cache', $form_state['values']['cache']['type']);
2340
          if ($plugin) {
2341
            $cache = array('type' => $form_state['values']['cache']['type']);
2342
            $this->set_option('cache', $cache);
2343
            if (!empty($plugin->definition['uses options'])) {
2344
              views_ui_add_form_to_stack('display', $this->view, $this->display->id, array('cache_options'));
2345
            }
2346
          }
2347
        }
2348

    
2349
        break;
2350
      case 'cache_options':
2351
        $plugin = views_get_plugin('cache', $form_state['values'][$section]['type']);
2352
        if ($plugin) {
2353
          $plugin->options_submit($form['cache_options'], $form_state);
2354
          $this->set_option('cache', $form_state['values'][$section]);
2355
        }
2356
        break;
2357
      case 'query':
2358
        $plugin = $this->get_plugin('query');
2359
        if ($plugin) {
2360
          $plugin->options_submit($form['query']['options'], $form_state);
2361
          $this->set_option('query', $form_state['values'][$section]);
2362
        }
2363
        break;
2364

    
2365
      case 'link_display':
2366
        $this->set_option('link_url', $form_state['values']['link_url']);
2367
      case 'title':
2368
      case 'css_class':
2369
      case 'display_comment':
2370
        $this->set_option($section, $form_state['values'][$section]);
2371
        break;
2372
      case 'field_language':
2373
        $this->set_option('field_language', $form_state['values']['field_language']);
2374
        $this->set_option('field_language_add_to_query', $form_state['values']['field_language_add_to_query']);
2375
        break;
2376
      case 'use_ajax':
2377
      case 'hide_attachment_summary':
2378
      case 'hide_admin_links':
2379
        $this->set_option($section, (bool)$form_state['values'][$section]);
2380
        break;
2381
      case 'use_more':
2382
        $this->set_option($section, intval($form_state['values'][$section]));
2383
        $this->set_option('use_more_always', !intval($form_state['values']['use_more_always']));
2384
        $this->set_option('use_more_text', $form_state['values']['use_more_text']);
2385
      case 'distinct':
2386
        $this->set_option($section, $form_state['values'][$section]);
2387
        break;
2388
      case 'group_by':
2389
        $this->set_option($section, $form_state['values'][$section]);
2390
        break;
2391
      case 'row_plugin':
2392
        // This if prevents resetting options to default if they don't change
2393
        // the plugin.
2394
        if ($this->get_option($section) != $form_state['values'][$section]) {
2395
          $plugin = views_get_plugin('row', $form_state['values'][$section]);
2396
          if ($plugin) {
2397
            $this->set_option($section, $form_state['values'][$section]);
2398
            $this->set_option('row_options', array());
2399

    
2400
            // send ajax form to options page if we use it.
2401
            if (!empty($plugin->definition['uses options'])) {
2402
              views_ui_add_form_to_stack('display', $this->view, $this->display->id, array('row_options'));
2403
            }
2404
          }
2405
        }
2406
        break;
2407
      case 'style_plugin':
2408
        // This if prevents resetting options to default if they don't change
2409
        // the plugin.
2410
        if ($this->get_option($section) != $form_state['values'][$section]) {
2411
          $plugin = views_get_plugin('style', $form_state['values'][$section]);
2412
          if ($plugin) {
2413
            $this->set_option($section, $form_state['values'][$section]);
2414
            $this->set_option('style_options', array());
2415
            // send ajax form to options page if we use it.
2416
            if (!empty($plugin->definition['uses options'])) {
2417
              views_ui_add_form_to_stack('display', $this->view, $this->display->id, array('style_options'));
2418
            }
2419
          }
2420
        }
2421
        break;
2422
      case 'style_options':
2423
        $style = TRUE;
2424
      case 'row_options':
2425
        // if row, $style will be empty.
2426
        $plugin = $this->get_plugin(empty($style) ? 'row' : 'style');
2427
        if ($plugin) {
2428
          $plugin->options_submit($form['options'][$section], $form_state);
2429
        }
2430
        $this->set_option($section, $form_state['values'][$section]);
2431
        break;
2432
      case 'exposed_block':
2433
        $this->set_option($section, (bool) $form_state['values'][$section]);
2434
        break;
2435
      case 'exposed_form':
2436
        $exposed_form = $this->get_option('exposed_form');
2437
        if ($exposed_form['type'] != $form_state['values']['exposed_form']['type']) {
2438
          $plugin = views_get_plugin('exposed_form', $form_state['values']['exposed_form']['type']);
2439
          if ($plugin) {
2440
            $exposed_form = array('type' => $form_state['values']['exposed_form']['type'], 'options' => array());
2441
            $this->set_option('exposed_form', $exposed_form);
2442
            if (!empty($plugin->definition['uses options'])) {
2443
              views_ui_add_form_to_stack('display', $this->view, $this->display->id, array('exposed_form_options'));
2444
            }
2445
          }
2446
        }
2447

    
2448
        break;
2449
      case 'exposed_form_options':
2450
        $plugin = $this->get_plugin('exposed_form');
2451
        if ($plugin) {
2452
          $exposed_form = $this->get_option('exposed_form');
2453
          $plugin->options_submit($form['exposed_form_options'], $form_state);
2454
          $exposed_form['options'] = $form_state['values'][$section];
2455
          $this->set_option('exposed_form', $exposed_form);
2456
        }
2457
        break;
2458
      case 'pager':
2459
        $pager = $this->get_option('pager');
2460
        if ($pager['type'] != $form_state['values']['pager']['type']) {
2461
          $plugin = views_get_plugin('pager', $form_state['values']['pager']['type']);
2462
          if ($plugin) {
2463
            // Because pagers have very similar options, let's allow pagers to
2464
            // try to carry the options over.
2465
            $plugin->init($this->view, $this->display, $pager['options']);
2466

    
2467
            $pager = array('type' => $form_state['values']['pager']['type'], 'options' => $plugin->options);
2468
            $this->set_option('pager', $pager);
2469
            if (!empty($plugin->definition['uses options'])) {
2470
              views_ui_add_form_to_stack('display', $this->view, $this->display->id, array('pager_options'));
2471
            }
2472
          }
2473
        }
2474

    
2475
        break;
2476
      case 'pager_options':
2477
        $plugin = $this->get_plugin('pager');
2478
        if ($plugin) {
2479
          $pager = $this->get_option('pager');
2480
          $plugin->options_submit($form['pager_options'], $form_state);
2481
          $pager['options'] = $form_state['values'][$section];
2482
          $this->set_option('pager', $pager);
2483
        }
2484
        break;
2485
    }
2486

    
2487
    foreach ($this->extender as $extender) {
2488
      $extender->options_submit($form, $form_state);
2489
    }
2490
  }
2491

    
2492
  /**
2493
   * If override/revert was clicked, perform the proper toggle.
2494
   */
2495
  function options_override($form, &$form_state) {
2496
    $this->set_override($form_state['section']);
2497
  }
2498

    
2499
  /**
2500
   * Flip the override setting for the given section.
2501
   *
2502
   * @param string $section
2503
   *   Which option should be marked as overridden, for example "filters".
2504
   * @param bool $new_state
2505
   *   Select the new state of the option.
2506
   *     - TRUE: Revert to default.
2507
   *     - FALSE: Mark it as overridden.
2508
   */
2509
  function set_override($section, $new_state = NULL) {
2510
    $options = $this->defaultable_sections($section);
2511
    if (!$options) {
2512
      return;
2513
    }
2514

    
2515
    if (!isset($new_state)) {
2516
      $new_state = empty($this->options['defaults'][$section]);
2517
    }
2518

    
2519
    // For each option that is part of this group, fix our settings.
2520
    foreach ($options as $option) {
2521
      if ($new_state) {
2522
        // Revert to defaults.
2523
        unset($this->options[$option]);
2524
        unset($this->display->display_options[$option]);
2525
      }
2526
      else {
2527
        // copy existing values into our display.
2528
        $this->options[$option] = $this->get_option($option);
2529
        $this->display->display_options[$option] = $this->options[$option];
2530
      }
2531
      $this->options['defaults'][$option] = $new_state;
2532
      $this->display->display_options['defaults'][$option] = $new_state;
2533
    }
2534
  }
2535

    
2536
  /**
2537
   * Inject anything into the query that the display handler needs.
2538
   */
2539
  function query() {
2540
    foreach ($this->extender as $extender) {
2541
      $extender->query();
2542
    }
2543
  }
2544

    
2545
  /**
2546
   * Not all display plugins will support filtering
2547
   */
2548
  function render_filters() { }
2549

    
2550
  /**
2551
   * Not all display plugins will suppert pager rendering.
2552
   */
2553
  function render_pager() {
2554
    return TRUE;
2555
  }
2556

    
2557
  /**
2558
   * Render the 'more' link
2559
   */
2560
  function render_more_link() {
2561
    if ($this->use_more() && ($this->use_more_always() || (!empty($this->view->query->pager) && $this->view->query->pager->has_more_records()))) {
2562
      $path = $this->get_path();
2563

    
2564
      if ($this->get_option('link_display') == 'custom_url' && $override_path = $this->get_option('link_url')) {
2565
        $tokens = $this->get_arguments_tokens();
2566
        $path = strtr($override_path, $tokens);
2567
      }
2568

    
2569
      if ($path) {
2570
        if (empty($override_path)) {
2571
          $path = $this->view->get_url(NULL, $path);
2572
        }
2573
        $url_options = array();
2574
        if (!empty($this->view->exposed_raw_input)) {
2575
          $url_options['query'] = $this->view->exposed_raw_input;
2576
        }
2577
        $theme = views_theme_functions('views_more', $this->view, $this->display);
2578
        $path = check_url(url($path, $url_options));
2579

    
2580
        return theme($theme, array('more_url' => $path, 'link_text' => check_plain($this->use_more_text()), 'view' => $this->view));
2581
      }
2582
    }
2583
  }
2584

    
2585

    
2586
  /**
2587
   * Legacy functions.
2588
   */
2589

    
2590
  /**
2591
   * Render the header of the view.
2592
   */
2593
  function render_header() {
2594
    $empty = !empty($this->view->result);
2595
    return $this->render_area('header', $empty);
2596
  }
2597

    
2598
  /**
2599
   * Render the footer of the view.
2600
   */
2601
  function render_footer() {
2602
    $empty = !empty($this->view->result);
2603
    return $this->render_area('footer', $empty);
2604
  }
2605

    
2606
  function render_empty() {
2607
    return $this->render_area('empty');
2608
  }
2609

    
2610
  /**
2611
   * If this display creates a block, implement one of these.
2612
   */
2613
  function hook_block_list($delta = 0, $edit = array()) { return array(); }
2614

    
2615
  /**
2616
   * If this display creates a page with a menu item, implement it here.
2617
   */
2618
  function hook_menu() { return array(); }
2619

    
2620
  /**
2621
   * Render this display.
2622
   */
2623
  function render() {
2624
    return theme($this->theme_functions(), array('view' => $this->view));
2625
  }
2626

    
2627
  function render_area($area, $empty = FALSE) {
2628
    $return = '';
2629
    foreach ($this->get_handlers($area) as $area) {
2630
      $return .= $area->render($empty);
2631
    }
2632
    return $return;
2633
  }
2634

    
2635

    
2636
  /**
2637
   * Determine if the user has access to this display of the view.
2638
   */
2639
  function access($account = NULL) {
2640
    if (!isset($account)) {
2641
      global $user;
2642
      $account = $user;
2643
    }
2644

    
2645
    // Full override.
2646
    if (user_access('access all views', $account)) {
2647
      return TRUE;
2648
    }
2649

    
2650
    $plugin = $this->get_plugin('access');
2651
    if ($plugin) {
2652
      return $plugin->access($account);
2653
    }
2654

    
2655
    // fallback to all access if no plugin.
2656
    return TRUE;
2657
  }
2658

    
2659
  /**
2660
   * Set up any variables on the view prior to execution. These are separated
2661
   * from execute because they are extremely common and unlikely to be
2662
   * overridden on an individual display.
2663
   */
2664
  function pre_execute() {
2665
    $this->view->set_use_ajax($this->use_ajax());
2666
    if ($this->use_more() && !$this->use_more_always()) {
2667
      $this->view->get_total_rows = TRUE;
2668
    }
2669
    $this->view->init_handlers();
2670
    if ($this->uses_exposed()) {
2671
      $exposed_form = $this->get_plugin('exposed_form');
2672
      $exposed_form->pre_execute();
2673
    }
2674

    
2675
    foreach ($this->extender as $extender) {
2676
      $extender->pre_execute();
2677
    }
2678

    
2679
    if ($this->get_option('hide_admin_links')) {
2680
      $this->view->hide_admin_links = TRUE;
2681
    }
2682
  }
2683

    
2684
  /**
2685
   * When used externally, this is how a view gets run and returns
2686
   * data in the format required.
2687
   *
2688
   * The base class cannot be executed.
2689
   */
2690
  function execute() { }
2691

    
2692
  /**
2693
   * Fully render the display for the purposes of a live preview or
2694
   * some other AJAXy reason.
2695
   */
2696
  function preview() { return $this->view->render(); }
2697

    
2698
  /**
2699
   * Displays can require a certain type of style plugin. By default, they will
2700
   * be 'normal'.
2701
   */
2702
  function get_style_type() { return 'normal'; }
2703

    
2704
  /**
2705
   * Make sure the display and all associated handlers are valid.
2706
   *
2707
   * @return
2708
   *   Empty array if the display is valid; an array of error strings if it is not.
2709
   */
2710
  function validate() {
2711
    $errors = array();
2712
    // Make sure displays that use fields HAVE fields.
2713
    if ($this->uses_fields()) {
2714
      $fields = FALSE;
2715
      foreach ($this->get_handlers('field') as $field) {
2716
        if (empty($field->options['exclude'])) {
2717
          $fields = TRUE;
2718
        }
2719
      }
2720

    
2721
      if (!$fields) {
2722
        $errors[] = t('Display "@display" uses fields but there are none defined for it or all are excluded.', array('@display' => $this->display->display_title));
2723
      }
2724
    }
2725

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

    
2730
    // Validate style plugin
2731
    $style = $this->get_plugin();
2732
    if (empty($style)) {
2733
      $errors[] = t('Display "@display" has an invalid style plugin.', array('@display' => $this->display->display_title));
2734
    }
2735
    else {
2736
      $result = $style->validate();
2737
      if (!empty($result) && is_array($result)) {
2738
        $errors = array_merge($errors, $result);
2739
      }
2740
    }
2741

    
2742
    // Validate query plugin.
2743
    $query = $this->get_plugin('query');
2744
    $result = $query->validate();
2745
    if (!empty($result) && is_array($result)) {
2746
      $errors = array_merge($errors, $result);
2747
    }
2748

    
2749
    // Validate handlers
2750
    foreach (views_object_types() as $type => $info) {
2751
      foreach ($this->get_handlers($type) as $handler) {
2752
        $result = $handler->validate();
2753
        if (!empty($result) && is_array($result)) {
2754
          $errors = array_merge($errors, $result);
2755
        }
2756
      }
2757
    }
2758

    
2759
    return $errors;
2760
  }
2761

    
2762
  /**
2763
   * Check if the provided identifier is unique.
2764
   *
2765
   * @param string $id
2766
   *   The id of the handler which is checked.
2767
   * @param string $identifier
2768
   *   The actual get identifier configured in the exposed settings.
2769
   *
2770
   * @return bool
2771
   *   Returns whether the identifier is unique on all handlers.
2772
   *
2773
   */
2774
  function is_identifier_unique($id, $identifier) {
2775
    foreach (views_object_types() as $type => $info) {
2776
      foreach ($this->get_handlers($type) as $key => $handler) {
2777
        if ($handler->can_expose() && $handler->is_exposed()) {
2778
          if ($handler->is_a_group()) {
2779
            if ($id != $key && $identifier == $handler->options['group_info']['identifier']) {
2780
              return FALSE;
2781
            }
2782
          }
2783
          else {
2784
            if ($id != $key && $identifier == $handler->options['expose']['identifier']) {
2785
              return FALSE;
2786
            }
2787
          }
2788
        }
2789
      }
2790
    }
2791
    return TRUE;
2792
  }
2793

    
2794
  /**
2795
   * Provide the block system with any exposed widget blocks for this display.
2796
   */
2797
  function get_special_blocks() {
2798
    $blocks = array();
2799

    
2800
    if ($this->uses_exposed_form_in_block()) {
2801
      $delta = '-exp-' . $this->view->name . '-' . $this->display->id;
2802
      $desc = t('Exposed form: @view-@display_id', array('@view' => $this->view->name, '@display_id' => $this->display->id));
2803

    
2804
      $blocks[$delta] = array(
2805
        'info' => $desc,
2806
        'cache' => DRUPAL_NO_CACHE,
2807
      );
2808
    }
2809

    
2810
    return $blocks;
2811
  }
2812

    
2813
  /**
2814
   * Render any special blocks provided for this display.
2815
   */
2816
  function view_special_blocks($type) {
2817
    if ($type == '-exp') {
2818
      // avoid interfering with the admin forms.
2819
      if (arg(0) == 'admin' && arg(1) == 'structure' && arg(2) == 'views') {
2820
        return;
2821
      }
2822
      $this->view->init_handlers();
2823

    
2824
      if ($this->uses_exposed() && $this->get_option('exposed_block')) {
2825
        $exposed_form = $this->get_plugin('exposed_form');
2826
        return array(
2827
          'content' => $exposed_form->render_exposed_form(TRUE),
2828
        );
2829
      }
2830
    }
2831
  }
2832

    
2833
  /**
2834
   * Override of export_option()
2835
   *
2836
   * Because displays do not want to export options that are NOT overridden from the
2837
   * default display, we need some special handling during the export process.
2838
   */
2839
  function export_option($indent, $prefix, $storage, $option, $definition, $parents) {
2840
    // The $prefix is wrong because we store our actual options a little differently:
2841
    $prefix = '$handler->display->display_options';
2842
    $output = '';
2843
    if (!$parents && !$this->is_default_display()) {
2844
      // Do not export items that are not overridden.
2845
      if ($this->is_defaulted($option)) {
2846
        return;
2847
      }
2848

    
2849
      // If this is not defaulted and is overrideable, flip the switch to say this
2850
      // is overridden.
2851
      if ($this->defaultable_sections($option)) {
2852
        $output .= $indent . $prefix . "['defaults']['$option'] = FALSE;\n";
2853
      }
2854
    }
2855

    
2856
    $output .= parent::export_option($indent, $prefix, $storage, $option, $definition, $parents);
2857
    return $output;
2858
  }
2859

    
2860
  /**
2861
   * Special method to export items that have handlers.
2862
   *
2863
   * This method was specified in the option_definition() as the method to utilize to
2864
   * export fields, filters, sort criteria, relationships and arguments. This passes
2865
   * the export off to the individual handlers so that they can export themselves
2866
   * properly.
2867
   */
2868
  function export_handler($indent, $prefix, $storage, $option, $definition, $parents) {
2869
    $output = '';
2870

    
2871
    // cut the 's' off because the data is stored as the plural form but we need
2872
    // the singular form. Who designed that anyway? Oh yeah, I did. :(
2873
    if ($option != 'header' && $option != 'footer' && $option != 'empty') {
2874
      $type = substr($option, 0, -1);
2875
    }
2876
    else {
2877
      $type = $option;
2878
    }
2879
    $types = views_object_types();
2880
    foreach ($storage[$option] as $id => $info) {
2881
      if (!empty($types[$type]['type'])) {
2882
        $handler_type = $types[$type]['type'];
2883
      }
2884
      else {
2885
        $handler_type = $type;
2886
      }
2887
      // If aggregation is on, the group type might override the actual
2888
      // handler that is in use. This piece of code checks that and,
2889
      // if necessary, sets the override handler.
2890
      $override = NULL;
2891
      if ($this->use_group_by() && !empty($info['group_type'])) {
2892
        if (empty($this->view->query)) {
2893
          $this->view->init_query();
2894
        }
2895
        $aggregate = $this->view->query->get_aggregation_info();
2896
        if (!empty($aggregate[$info['group_type']]['handler'][$type])) {
2897
          $override = $aggregate[$info['group_type']]['handler'][$type];
2898
        }
2899
      }
2900
      $handler = views_get_handler($info['table'], $info['field'], $handler_type, $override);
2901
      if ($handler) {
2902
        $handler->init($this->view, $info);
2903
        $output .= $indent . '/* ' . $types[$type]['stitle'] . ': ' . $handler->ui_name() . " */\n";
2904
        $output .= $handler->export_options($indent, $prefix . "['$option']['$id']");
2905
      }
2906

    
2907
      // Prevent reference problems.
2908
      unset($handler);
2909
    }
2910

    
2911
    return $output;
2912
  }
2913

    
2914
  /**
2915
   * Special handling for the style export.
2916
   *
2917
   * Styles are stored as style_plugin and style_options or row_plugin and
2918
   * row_options accordingly. The options are told not to export, and the
2919
   * export for the plugin should export both.
2920
   */
2921
  function export_style($indent, $prefix, $storage, $option, $definition, $parents) {
2922
    $output = '';
2923
    $style_plugin = $this->get_plugin();
2924
    if ($option == 'style_plugin') {
2925
      $type = 'style';
2926
      $options_field = 'style_options';
2927
      $plugin = $style_plugin;
2928
    }
2929
    else {
2930
      if (!$style_plugin || !$style_plugin->uses_row_plugin()) {
2931
        return;
2932
      }
2933

    
2934
      $type = 'row';
2935
      $options_field = 'row_options';
2936
      $plugin = $this->get_plugin('row');
2937
      // If the style plugin doesn't use row plugins, don't even bother.
2938
    }
2939

    
2940
    if ($plugin) {
2941
      // Write which plugin to use.
2942
      $value = $this->get_option($option);
2943
      $output .= $indent . $prefix . "['$option'] = '$value';\n";
2944

    
2945
      // Pass off to the plugin to export itself.
2946
      $output .= $plugin->export_options($indent, $prefix . "['$options_field']");
2947
    }
2948

    
2949
    return $output;
2950
  }
2951

    
2952
  /**
2953
   * Special handling for plugin export
2954
   *
2955
   * Plugins other than styles are stored in array with 'type' being the key
2956
   * to the plugin. For modern plugins, the options are stored in the 'options'
2957
   * array, but for legacy plugins (access and cache) options are stored as
2958
   * siblings to the type.
2959
   */
2960
  function export_plugin($indent, $prefix, $storage, $option, $definition, $parents) {
2961
    $output = '';
2962
    $plugin_type = end($parents);
2963
    $plugin = $this->get_plugin($plugin_type);
2964
    if ($plugin) {
2965
      // Write which plugin to use.
2966
      $value = $storage[$option];
2967
      $new_prefix = $prefix . "['$plugin_type']";
2968

    
2969
      $output .= $indent . $new_prefix . "['$option'] = '$value';\n";
2970

    
2971
      if ($plugin_type != 'access' && $plugin_type!= 'cache') {
2972
        $new_prefix .= "['options']";
2973
      }
2974

    
2975
      // Pass off to the plugin to export itself.
2976
      $output .= $plugin->export_options($indent, $new_prefix);
2977
    }
2978

    
2979
    return $output;
2980
  }
2981

    
2982
  function unpack_style($indent, $prefix, $storage, $option, $definition, $parents) {
2983
    $output = '';
2984
    $style_plugin = $this->get_plugin();
2985
    if ($option == 'style_plugin') {
2986
      $type = 'style';
2987
      $options_field = 'style_options';
2988
      $plugin = $style_plugin;
2989
    }
2990
    else {
2991
      if (!$style_plugin || !$style_plugin->uses_row_plugin()) {
2992
        return;
2993
      }
2994

    
2995
      $type = 'row';
2996
      $options_field = 'row_options';
2997
      $plugin = $this->get_plugin('row');
2998
      // If the style plugin doesn't use row plugins, don't even bother.
2999
    }
3000

    
3001
    if ($plugin) {
3002
      return $plugin->unpack_translatables($translatable, $parents);
3003
    }
3004
  }
3005

    
3006
  /**
3007
   * Special handling for plugin unpacking.
3008
   */
3009
  function unpack_plugin(&$translatable, $storage, $option, $definition, $parents) {
3010
    $plugin_type = end($parents);
3011
    $plugin = $this->get_plugin($plugin_type);
3012
    if ($plugin) {
3013
      // Write which plugin to use.
3014
      return $plugin->unpack_translatables($translatable, $parents);
3015
    }
3016
  }
3017

    
3018
    /**
3019
   * Special method to unpack items that have handlers.
3020
   *
3021
   * This method was specified in the option_definition() as the method to utilize to
3022
   * export fields, filters, sort criteria, relationships and arguments. This passes
3023
   * the export off to the individual handlers so that they can export themselves
3024
   * properly.
3025
   */
3026
  function unpack_handler(&$translatable, $storage, $option, $definition, $parents) {
3027
    $output = '';
3028

    
3029
    // cut the 's' off because the data is stored as the plural form but we need
3030
    // the singular form. Who designed that anyway? Oh yeah, I did. :(
3031
    if ($option != 'header' && $option != 'footer' && $option != 'empty') {
3032
      $type = substr($option, 0, -1);
3033
    }
3034
    else {
3035
      $type = $option;
3036
    }
3037
    $types = views_object_types();
3038
    foreach ($storage[$option] as $id => $info) {
3039
      if (!empty($types[$type]['type'])) {
3040
        $handler_type = $types[$type]['type'];
3041
      }
3042
      else {
3043
        $handler_type = $type;
3044
      }
3045
      $handler = views_get_handler($info['table'], $info['field'], $handler_type);
3046
      if ($handler) {
3047
        $handler->init($this->view, $info);
3048
        $handler->unpack_translatables($translatable, array_merge($parents, array($type, $info['table'], $info['id'])));
3049
      }
3050

    
3051
      // Prevent reference problems.
3052
      unset($handler);
3053
    }
3054

    
3055
    return $output;
3056
  }
3057

    
3058
  /**
3059
   * Provide some helpful text for the arguments.
3060
   * The result should contain of an array with
3061
   *   - filter value present: The title of the fieldset in the argument
3062
   *     where you can configure what should be done with a given argument.
3063
   *   - filter value not present: The tiel of the fieldset in the argument
3064
   *     where you can configure what should be done if the argument does not
3065
   *     exist.
3066
   *   - description: A description about how arguments comes to the display.
3067
   *     For example blocks don't get it from url.
3068
   */
3069
  function get_argument_text() {
3070
    return array(
3071
      'filter value not present' => t('When the filter value is <em>NOT</em> available'),
3072
      'filter value present' => t('When the filter value <em>IS</em> available or a default is provided'),
3073
      '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'."),
3074
    );
3075
  }
3076

    
3077
  /**
3078
   * Provide some helpful text for pagers.
3079
   *
3080
   * The result should contain of an array within
3081
   *   - items per page title
3082
   */
3083
  function get_pager_text() {
3084
    return array(
3085
      'items per page title' => t('Items to display'),
3086
      'items per page description' => t('The number of items to display. Enter 0 for no limit.')
3087
    );
3088
  }
3089
}
3090

    
3091

    
3092
/**
3093
 * @}
3094
 */