Projet

Général

Profil

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

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

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 formin 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(drupal_substr($this->get_option('display_comment'), 0, 10));
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 hilite 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
          $form['#help_topic'] = $plugin->definition['help topic'];
1577
          $form['#help_module'] = $plugin->definition['module'];
1578

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

    
1597
        $cache = $this->get_option('cache');
1598
        $form['cache']['type'] =  array(
1599
          '#type' => 'radios',
1600
          '#options' => views_fetch_plugin_names('cache', NULL, array($this->view->base_table)),
1601
          '#default_value' => $cache['type'],
1602
        );
1603

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

    
1621
          $form['cache_options'] = array(
1622
            '#tree' => TRUE,
1623
          );
1624
          $form['cache_options']['type'] = array(
1625
            '#type' => 'value',
1626
            '#value' => $cache['type'],
1627
          );
1628
          $plugin->options_form($form['cache_options'], $form_state);
1629
        }
1630
        break;
1631
      case 'query':
1632
        $query_options = $this->get_option('query');
1633
        $plugin_name = $query_options['type'];
1634

    
1635
        $form['#title'] .= t('Query options');
1636
        $this->view->init_query();
1637
        if ($this->view->query) {
1638
          if (isset($this->view->query->definition['help topic'])) {
1639
            $form['#help_topic'] = $this->view->query->definition['help topic'];
1640
          }
1641

    
1642
          if (isset($this->view->query->definition['module'])) {
1643
            $form['#help_module'] = $this->view->query->definition['module'];
1644
          }
1645

    
1646
          $form['query'] = array(
1647
            '#tree' => TRUE,
1648
            'type' => array(
1649
              '#type' => 'value',
1650
              '#value' => $plugin_name,
1651
            ),
1652
            'options' => array(
1653
              '#tree' => TRUE,
1654
            ),
1655
          );
1656

    
1657
          $this->view->query->options_form($form['query']['options'], $form_state);
1658
        }
1659
        break;
1660
      case 'field_language':
1661
        $form['#title'] .= t('Field Language');
1662

    
1663
        $entities = entity_get_info();
1664
        $entity_tables = array();
1665
        $has_translation_handlers = FALSE;
1666
        foreach ($entities as $type => $entity_info) {
1667
          $entity_tables[] = $entity_info['base table'];
1668

    
1669
          if (!empty($entity_info['translation'])) {
1670
            $has_translation_handlers = TRUE;
1671
          }
1672
        }
1673

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

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

    
1712
        $style_plugin = views_fetch_plugin_data('style', $this->get_option('style_plugin'));
1713
        if (!empty($style_plugin['uses options'])) {
1714
          $form['markup'] = array(
1715
            '#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>',
1716
          );
1717
        }
1718

    
1719
        break;
1720
      case 'style_options':
1721
        $form['#title'] .= t('Style options');
1722
        $style = TRUE;
1723
        $type = 'style_plugin';
1724
        $name = $this->get_option('style_plugin');
1725

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

    
1756
        $row_plugin = views_fetch_plugin_data('row', $this->get_option('row_plugin'));
1757
        if (!empty($row_plugin['uses options'])) {
1758
          $form['markup'] = array(
1759
            '#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>',
1760
          );
1761
        }
1762

    
1763
        break;
1764
      case 'link_display':
1765
        $form['#title'] .= t('Which display to use for path');
1766
        foreach ($this->view->display as $display_id => $display) {
1767
          if ($display->handler->has_path()) {
1768
            $options[$display_id] = $display->display_title;
1769
          }
1770
        }
1771
        $options['custom_url'] = t('Custom URL');
1772
        if (count($options)) {
1773
          $form['link_display'] = array(
1774
            '#type' => 'radios',
1775
            '#options' => $options,
1776
            '#description' => t("Which display to use to get this display's path for things like summary links, rss feed links, more links, etc."),
1777
            '#default_value' => $this->get_option('link_display'),
1778
          );
1779
        }
1780

    
1781
        $options = array();
1782
        $count = 0; // This lets us prepare the key as we want it printed.
1783
        foreach ($this->view->display_handler->get_handlers('argument') as $arg => $handler) {
1784
          $options[t('Arguments')]['%' . ++$count] = t('@argument title', array('@argument' => $handler->ui_name()));
1785
          $options[t('Arguments')]['!' . $count] = t('@argument input', array('@argument' => $handler->ui_name()));
1786
        }
1787

    
1788
        // Default text.
1789
        // We have some options, so make a list.
1790
        $output = '';
1791
        if (!empty($options)) {
1792
          $output = t('<p>The following tokens are available for this link.</p>');
1793
          foreach (array_keys($options) as $type) {
1794
            if (!empty($options[$type])) {
1795
              $items = array();
1796
              foreach ($options[$type] as $key => $value) {
1797
                $items[] = $key . ' == ' . $value;
1798
              }
1799
              $output .= theme('item_list',
1800
                array(
1801
                  'items' => $items,
1802
                  'type' => $type
1803
                ));
1804
            }
1805
          }
1806
        }
1807

    
1808
        $form['link_url'] = array(
1809
          '#type' => 'textfield',
1810
          '#title' => t('Custom URL'),
1811
          '#default_value' => $this->get_option('link_url'),
1812
          '#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,
1813
          '#dependency' => array('radio:link_display' => array('custom_url')),
1814
        );
1815
        break;
1816
      case 'analyze-theme':
1817
        $form['#title'] .= t('Theming information');
1818
        $form['#help_topic'] = 'analyze-theme';
1819

    
1820
        if (isset($_POST['theme'])) {
1821
          $this->theme = $_POST['theme'];
1822
        }
1823
        elseif (empty($this->theme)) {
1824
          $this->theme = variable_get('theme_default', 'bartik');
1825
        }
1826

    
1827
        if (isset($GLOBALS['theme']) && $GLOBALS['theme'] == $this->theme) {
1828
          $this->theme_registry = theme_get_registry();
1829
          $theme_engine = $GLOBALS['theme_engine'];
1830
        }
1831
        else {
1832
          $themes = list_themes();
1833
          $theme = $themes[$this->theme];
1834

    
1835
          // Find all our ancestor themes and put them in an array.
1836
          $base_theme = array();
1837
          $ancestor = $this->theme;
1838
          while ($ancestor && isset($themes[$ancestor]->base_theme)) {
1839
            $ancestor = $themes[$ancestor]->base_theme;
1840
            $base_theme[] = $themes[$ancestor];
1841
          }
1842

    
1843
          // The base themes should be initialized in the right order.
1844
          $base_theme = array_reverse($base_theme);
1845

    
1846
          // This code is copied directly from _drupal_theme_initialize()
1847
          $theme_engine = NULL;
1848

    
1849
          // Initialize the theme.
1850
          if (isset($theme->engine)) {
1851
            // Include the engine.
1852
            include_once DRUPAL_ROOT . '/' . $theme->owner;
1853

    
1854
            $theme_engine = $theme->engine;
1855
            if (function_exists($theme_engine . '_init')) {
1856
              foreach ($base_theme as $base) {
1857
                call_user_func($theme_engine . '_init', $base);
1858
              }
1859
              call_user_func($theme_engine . '_init', $theme);
1860
            }
1861
          }
1862
          else {
1863
            // include non-engine theme files
1864
            foreach ($base_theme as $base) {
1865
              // Include the theme file or the engine.
1866
              if (!empty($base->owner)) {
1867
                include_once DRUPAL_ROOT . '/' . $base->owner;
1868
              }
1869
            }
1870
            // and our theme gets one too.
1871
            if (!empty($theme->owner)) {
1872
              include_once DRUPAL_ROOT . '/' . $theme->owner;
1873
            }
1874
          }
1875
          $this->theme_registry = _theme_load_registry($theme, $base_theme, $theme_engine);
1876
        }
1877

    
1878
        // If there's a theme engine involved, we also need to know its extension
1879
        // so we can give the proper filename.
1880
        $this->theme_extension = '.tpl.php';
1881
        if (isset($theme_engine)) {
1882
          $extension_function = $theme_engine . '_extension';
1883
          if (function_exists($extension_function)) {
1884
            $this->theme_extension = $extension_function();
1885
          }
1886
        }
1887

    
1888
        $funcs = array();
1889
        // Get theme functions for the display. Note that some displays may
1890
        // not have themes. The 'feed' display, for example, completely
1891
        // delegates to the style.
1892
        if (!empty($this->definition['theme'])) {
1893
          $funcs[] = $this->option_link(t('Display output'), 'analyze-theme-display') . ': '  . $this->format_themes($this->theme_functions());
1894
          $themes = $this->additional_theme_functions();
1895
          if ($themes) {
1896
            foreach ($themes as $theme) {
1897
              $funcs[] = $this->option_link(t('Alternative display output'), 'analyze-theme-display') . ': '  . $this->format_themes($theme);
1898
            }
1899
          }
1900
        }
1901

    
1902
        $plugin = $this->get_plugin();
1903
        if ($plugin) {
1904
          $funcs[] = $this->option_link(t('Style output'), 'analyze-theme-style') . ': ' . $this->format_themes($plugin->theme_functions(), $plugin->additional_theme_functions());
1905
          $themes = $plugin->additional_theme_functions();
1906
          if ($themes) {
1907
            foreach ($themes as $theme) {
1908
              $funcs[] = $this->option_link(t('Alternative style'), 'analyze-theme-style') . ': '  . $this->format_themes($theme);
1909
            }
1910
          }
1911

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

    
1925
          if ($plugin->uses_fields()) {
1926
            foreach ($this->get_handlers('field') as $id => $handler) {
1927
              $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());
1928
            }
1929
          }
1930
        }
1931

    
1932
        $form['important'] = array(
1933
          '#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>',
1934
        );
1935

    
1936
        if (isset($this->view->display[$this->view->current_display]->new_id)) {
1937
          $form['important']['new_id'] = array(
1938
            '#prefix' => '<div class="description">',
1939
            '#suffix' => '</div>',
1940
            '#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."),
1941
          );
1942
        }
1943

    
1944
        foreach (list_themes() as $key => $theme) {
1945
          if (!empty($theme->info['hidden'])) {
1946
            continue;
1947
          }
1948
          $options[$key] = $theme->info['name'];
1949
        }
1950

    
1951
        $form['box'] = array(
1952
          '#prefix' => '<div class="container-inline">',
1953
          '#suffix' => '</div>',
1954
        );
1955
        $form['box']['theme'] = array(
1956
          '#type' => 'select',
1957
          '#options' => $options,
1958
          '#default_value' => $this->theme,
1959
        );
1960

    
1961
        $form['box']['change'] = array(
1962
          '#type' => 'submit',
1963
          '#value' => t('Change theme'),
1964
          '#submit' => array('views_ui_edit_display_form_change_theme'),
1965
        );
1966

    
1967
        $form['analysis'] = array(
1968
          '#markup' => '<div class="form-item">' . theme('item_list', array('items' => $funcs)) . '</div>',
1969
        );
1970

    
1971
        $form['rescan_button'] = array(
1972
          '#prefix' => '<div class="form-item">',
1973
          '#suffix' => '</div>',
1974
        );
1975
        $form['rescan_button']['button'] = array(
1976
          '#type' => 'submit',
1977
          '#value' => t('Rescan template files'),
1978
          '#submit' => array('views_ui_config_item_form_rescan'),
1979
        );
1980
        $form['rescan_button']['markup'] = array(
1981
          '#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>',
1982
        );
1983

    
1984
        $form_state['ok_button'] = TRUE;
1985
        break;
1986
      case 'analyze-theme-display':
1987
        $form['#title'] .= t('Theming information (display)');
1988
        $output = '<p>' . t('Back to !info.', array('!info' => $this->option_link(t('theming information'), 'analyze-theme'))) . '</p>';
1989

    
1990
        if (empty($this->definition['theme'])) {
1991
          $output .= t('This display has no theming information');
1992
        }
1993
        else {
1994
          $output .= '<p>' . t('This is the default theme template used for this display.') . '</p>';
1995
          $output .= '<pre>' . check_plain(file_get_contents('./' . $this->definition['theme path'] . '/' . strtr($this->definition['theme'], '_', '-') . '.tpl.php')) . '</pre>';
1996
        }
1997

    
1998
        if (!empty($this->definition['additional themes'])) {
1999
          foreach ($this->definition['additional themes'] as $theme => $type) {
2000
            $output .= '<p>' . t('This is an alternative template for this display.') . '</p>';
2001
            $output .= '<pre>' . check_plain(file_get_contents('./' . $this->definition['theme path'] . '/' . strtr($theme, '_', '-') . '.tpl.php')) . '</pre>';
2002
          }
2003
        }
2004

    
2005
        $form['analysis'] = array(
2006
          '#markup' => '<div class="form-item">' . $output . '</div>',
2007
        );
2008

    
2009
        $form_state['ok_button'] = TRUE;
2010
        break;
2011
      case 'analyze-theme-style':
2012
        $form['#title'] .= t('Theming information (style)');
2013
        $output = '<p>' . t('Back to !info.', array('!info' => $this->option_link(t('theming information'), 'analyze-theme'))) . '</p>';
2014

    
2015
        $plugin = $this->get_plugin();
2016

    
2017
        if (empty($plugin->definition['theme'])) {
2018
          $output .= t('This display has no style theming information');
2019
        }
2020
        else {
2021
          $output .= '<p>' . t('This is the default theme template used for this style.') . '</p>';
2022
          $output .= '<pre>' . check_plain(file_get_contents('./' . $plugin->definition['theme path'] . '/' . strtr($plugin->definition['theme'], '_', '-') . '.tpl.php')) . '</pre>';
2023
        }
2024

    
2025
        if (!empty($plugin->definition['additional themes'])) {
2026
          foreach ($plugin->definition['additional themes'] as $theme => $type) {
2027
            $output .= '<p>' . t('This is an alternative template for this style.') . '</p>';
2028
            $output .= '<pre>' . check_plain(file_get_contents('./' . $plugin->definition['theme path'] . '/' . strtr($theme, '_', '-') . '.tpl.php')) . '</pre>';
2029
          }
2030
        }
2031

    
2032
        $form['analysis'] = array(
2033
          '#markup' => '<div class="form-item">' . $output . '</div>',
2034
        );
2035

    
2036
        $form_state['ok_button'] = TRUE;
2037
        break;
2038
      case 'analyze-theme-row':
2039
        $form['#title'] .= t('Theming information (row style)');
2040
        $output = '<p>' . t('Back to !info.', array('!info' => $this->option_link(t('theming information'), 'analyze-theme'))) . '</p>';
2041

    
2042
        $plugin = $this->get_plugin('row');
2043

    
2044
        if (empty($plugin->definition['theme'])) {
2045
          $output .= t('This display has no row style theming information');
2046
        }
2047
        else {
2048
          $output .= '<p>' . t('This is the default theme template used for this row style.') . '</p>';
2049
          $output .= '<pre>' . check_plain(file_get_contents('./' . $plugin->definition['theme path'] . '/' . strtr($plugin->definition['theme'], '_', '-') . '.tpl.php')) . '</pre>';
2050
        }
2051

    
2052
        if (!empty($plugin->definition['additional themes'])) {
2053
          foreach ($plugin->definition['additional themes'] as $theme => $type) {
2054
            $output .= '<p>' . t('This is an alternative template for this row style.') . '</p>';
2055
            $output .= '<pre>' . check_plain(file_get_contents('./' . $plugin->definition['theme path'] . '/' . strtr($theme, '_', '-') . '.tpl.php')) . '</pre>';
2056
          }
2057
        }
2058

    
2059
        $form['analysis'] = array(
2060
          '#markup' => '<div class="form-item">' . $output . '</div>',
2061
        );
2062

    
2063
        $form_state['ok_button'] = TRUE;
2064
        break;
2065
      case 'analyze-theme-field':
2066
        $form['#title'] .= t('Theming information (row style)');
2067
        $output = '<p>' . t('Back to !info.', array('!info' => $this->option_link(t('theming information'), 'analyze-theme'))) . '</p>';
2068

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

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

    
2075
        $form['analysis'] = array(
2076
          '#markup' => '<div class="form-item">' . $output . '</div>',
2077
        );
2078
        $form_state['ok_button'] = TRUE;
2079
        break;
2080

    
2081
      case 'exposed_block':
2082
        $form['#title'] .= t('Put the exposed form in a block');
2083
        $form['description'] = array(
2084
          '#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>',
2085
        );
2086
        $form['exposed_block'] = array(
2087
          '#type' => 'radios',
2088
          '#options' => array(1 => t('Yes'), 0 => t('No')),
2089
          '#default_value' => $this->get_option('exposed_block') ? 1 : 0,
2090
        );
2091
        break;
2092
      case 'exposed_form':
2093
        $form['#title'] .= t('Exposed Form');
2094
        $form['exposed_form'] = array(
2095
          '#prefix' => '<div class="clearfix">',
2096
          '#suffix' => '</div>',
2097
          '#tree' => TRUE,
2098
        );
2099

    
2100
        $exposed_form = $this->get_option('exposed_form');
2101
        $form['exposed_form']['type'] =  array(
2102
          '#type' => 'radios',
2103
          '#options' => views_fetch_plugin_names('exposed_form', NULL, array($this->view->base_table)),
2104
          '#default_value' => $exposed_form['type'],
2105
        );
2106

    
2107
        $exposed_form_plugin = views_fetch_plugin_data('exposed_form', $exposed_form['type']);
2108
        if (!empty($exposed_form_plugin['uses options'])) {
2109
          $form['markup'] = array(
2110
            '#prefix' => '<div class="form-item description">',
2111
            '#suffix' => '</div>',
2112
            '#markup' => t('You may also adjust the !settings for the currently selected style.', array('!settings' => $this->option_link(t('settings'), 'exposed_form_options'))),
2113
          );
2114
        }
2115
        break;
2116
      case 'exposed_form_options':
2117
        $plugin = $this->get_plugin('exposed_form');
2118
        $form['#title'] .= t('Exposed form options');
2119
        if ($plugin) {
2120
          $form['#help_topic'] = $plugin->definition['help topic'];
2121

    
2122
          $form['exposed_form_options'] = array(
2123
            '#tree' => TRUE,
2124
          );
2125
          $plugin->options_form($form['exposed_form_options'], $form_state);
2126
        }
2127
        break;
2128
      case 'pager':
2129
        $form['#title'] .= t('Select which pager, if any, to use for this view');
2130
        $form['pager'] = array(
2131
          '#prefix' => '<div class="clearfix">',
2132
          '#suffix' => '</div>',
2133
          '#tree' => TRUE,
2134
        );
2135

    
2136
        $pager = $this->get_option('pager');
2137
        $form['pager']['type'] =  array(
2138
          '#type' => 'radios',
2139
          '#options' => views_fetch_plugin_names('pager', empty($this->definition['use pager']) ? 'basic' : NULL, array($this->view->base_table)),
2140
          '#default_value' => $pager['type'],
2141
        );
2142

    
2143
        $pager_plugin = views_fetch_plugin_data('pager', $pager['type'], array($this->view->base_table));
2144
        if (!empty($pager_plugin['uses options'])) {
2145
          $form['markup'] = array(
2146
            '#prefix' => '<div class="form-item description">',
2147
            '#suffix' => '</div>',
2148
            '#markup' => t('You may also adjust the !settings for the currently selected pager.', array('!settings' => $this->option_link(t('settings'), 'pager_options'))),
2149
          );
2150
        }
2151

    
2152
        break;
2153
      case 'pager_options':
2154
        $plugin = $this->get_plugin('pager');
2155
        $form['#title'] .= t('Pager options');
2156
        if ($plugin) {
2157
          $form['#help_topic'] = $plugin->definition['help topic'];
2158

    
2159
          $form['pager_options'] = array(
2160
            '#tree' => TRUE,
2161
          );
2162
          $plugin->options_form($form['pager_options'], $form_state);
2163
        }
2164
        break;
2165
    }
2166

    
2167
    foreach ($this->extender as $extender) {
2168
      $extender->options_form($form, $form_state);
2169
    }
2170
  }
2171

    
2172
  /**
2173
   * Format a list of theme templates for output by the theme info helper.
2174
   */
2175
  function format_themes($themes) {
2176
    $registry = $this->theme_registry;
2177
    $extension = $this->theme_extension;
2178

    
2179
    $output = '';
2180
    $picked = FALSE;
2181
    foreach ($themes as $theme) {
2182
      $template = strtr($theme, '_', '-') . $extension;
2183
      if (!$picked && !empty($registry[$theme])) {
2184
        $template_path = isset($registry[$theme]['path']) ? $registry[$theme]['path'] . '/' : './';
2185
        if (file_exists($template_path . $template)) {
2186
          $hint = t('File found in folder @template-path', array('@template-path' => $template_path));
2187
          $template = '<strong title="'. $hint .'">' . $template . '</strong>';
2188
        }
2189
        else {
2190
          $template = '<strong class="error">' . $template . ' ' . t('(File not found, in folder @template-path)', array('@template-path' => $template_path)) . '</strong>';
2191
        }
2192
        $picked = TRUE;
2193
      }
2194
      $fixed[] = $template;
2195
    }
2196

    
2197
    return implode(', ', array_reverse($fixed));
2198
  }
2199

    
2200
  /**
2201
   * Validate the options form.
2202
   */
2203
  function options_validate(&$form, &$form_state) {
2204
    switch ($form_state['section']) {
2205
      case 'display_title':
2206
        if (empty($form_state['values']['display_title'])) {
2207
          form_error($form['display_title'], t('Display title may not be empty.'));
2208
        }
2209
        break;
2210
      case 'css_class':
2211
        $css_class = $form_state['values']['css_class'];
2212
        if (preg_match('/[^a-zA-Z0-9-_ ]/', $css_class)) {
2213
          form_error($form['css_class'], t('CSS classes must be alphanumeric or dashes only.'));
2214
        }
2215
      break;
2216
      case 'display_id':
2217
        if ($form_state['values']['display_id']) {
2218
          if (preg_match('/[^a-z0-9_]/', $form_state['values']['display_id'])) {
2219
            form_error($form['display_id'], t('Display name must be letters, numbers, or underscores only.'));
2220
          }
2221

    
2222
          foreach ($this->view->display as $id => $display) {
2223
            if ($id != $this->view->current_display && ($form_state['values']['display_id'] == $id || (isset($display->new_id) && $form_state['values']['display_id'] == $display->new_id))) {
2224
              form_error($form['display_id'], t('Display id should be unique.'));
2225
            }
2226
          }
2227
        }
2228
        break;
2229
      case 'style_options':
2230
        $style = TRUE;
2231
      case 'row_options':
2232
        // if row, $style will be empty.
2233
        $plugin = $this->get_plugin(empty($style) ? 'row' : 'style');
2234
        if ($plugin) {
2235
          $plugin->options_validate($form[$form_state['section']], $form_state);
2236
        }
2237
        break;
2238
      case 'access_options':
2239
        $plugin = $this->get_plugin('access');
2240
        if ($plugin) {
2241
          $plugin->options_validate($form['access_options'], $form_state);
2242
        }
2243
        break;
2244
      case 'query':
2245
        if ($this->view->query) {
2246
          $this->view->query->options_validate($form['query'], $form_state);
2247
        }
2248
        break;
2249
      case 'cache_options':
2250
        $plugin = $this->get_plugin('cache');
2251
        if ($plugin) {
2252
          $plugin->options_validate($form['cache_options'], $form_state);
2253
        }
2254
        break;
2255
      case 'exposed_form_options':
2256
        $plugin = $this->get_plugin('exposed_form');
2257
        if ($plugin) {
2258
          $plugin->options_validate($form['exposed_form_options'], $form_state);
2259
        }
2260
        break;
2261
      case 'pager_options':
2262
        $plugin = $this->get_plugin('pager');
2263
        if ($plugin) {
2264
          $plugin->options_validate($form['pager_options'], $form_state);
2265
        }
2266
        break;
2267
    }
2268

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

    
2274
  /**
2275
   * Perform any necessary changes to the form values prior to storage.
2276
   * There is no need for this function to actually store the data.
2277
   */
2278
  function options_submit(&$form, &$form_state) {
2279
    // Not sure I like this being here, but it seems (?) like a logical place.
2280
    $cache_plugin = $this->get_plugin('cache');
2281
    if ($cache_plugin) {
2282
      $cache_plugin->cache_flush();
2283
    }
2284

    
2285
    $section = $form_state['section'];
2286
    switch ($section) {
2287
      case 'display_id':
2288
        if (isset($form_state['values']['display_id'])) {
2289
          $this->display->new_id = $form_state['values']['display_id'];
2290
        }
2291
        break;
2292
      case 'display_title':
2293
        $this->display->display_title = $form_state['values']['display_title'];
2294
        $this->set_option('display_description', $form_state['values']['display_description']);
2295
        break;
2296
      case 'access':
2297
        $access = $this->get_option('access');
2298
        if ($access['type'] != $form_state['values']['access']['type']) {
2299
          $plugin = views_get_plugin('access', $form_state['values']['access']['type']);
2300
          if ($plugin) {
2301
            $access = array('type' => $form_state['values']['access']['type']);
2302
            $this->set_option('access', $access);
2303
            if (!empty($plugin->definition['uses options'])) {
2304
              views_ui_add_form_to_stack('display', $this->view, $this->display->id, array('access_options'));
2305
            }
2306
          }
2307
        }
2308

    
2309
        break;
2310
      case 'access_options':
2311
        $plugin = views_get_plugin('access', $form_state['values'][$section]['type']);
2312
        if ($plugin) {
2313
          $plugin->options_submit($form['access_options'], $form_state);
2314
          $this->set_option('access', $form_state['values'][$section]);
2315
        }
2316
        break;
2317
      case 'cache':
2318
        $cache = $this->get_option('cache');
2319
        if ($cache['type'] != $form_state['values']['cache']['type']) {
2320
          $plugin = views_get_plugin('cache', $form_state['values']['cache']['type']);
2321
          if ($plugin) {
2322
            $cache = array('type' => $form_state['values']['cache']['type']);
2323
            $this->set_option('cache', $cache);
2324
            if (!empty($plugin->definition['uses options'])) {
2325
              views_ui_add_form_to_stack('display', $this->view, $this->display->id, array('cache_options'));
2326
            }
2327
          }
2328
        }
2329

    
2330
        break;
2331
      case 'cache_options':
2332
        $plugin = views_get_plugin('cache', $form_state['values'][$section]['type']);
2333
        if ($plugin) {
2334
          $plugin->options_submit($form['cache_options'], $form_state);
2335
          $this->set_option('cache', $form_state['values'][$section]);
2336
        }
2337
        break;
2338
      case 'query':
2339
        $plugin = $this->get_plugin('query');
2340
        if ($plugin) {
2341
          $plugin->options_submit($form['query']['options'], $form_state);
2342
          $this->set_option('query', $form_state['values'][$section]);
2343
        }
2344
        break;
2345

    
2346
      case 'link_display':
2347
        $this->set_option('link_url', $form_state['values']['link_url']);
2348
      case 'title':
2349
      case 'css_class':
2350
      case 'display_comment':
2351
        $this->set_option($section, $form_state['values'][$section]);
2352
        break;
2353
      case 'field_language':
2354
        $this->set_option('field_language', $form_state['values']['field_language']);
2355
        $this->set_option('field_language_add_to_query', $form_state['values']['field_language_add_to_query']);
2356
        break;
2357
      case 'use_ajax':
2358
      case 'hide_attachment_summary':
2359
      case 'hide_admin_links':
2360
        $this->set_option($section, (bool)$form_state['values'][$section]);
2361
        break;
2362
      case 'use_more':
2363
        $this->set_option($section, intval($form_state['values'][$section]));
2364
        $this->set_option('use_more_always', !intval($form_state['values']['use_more_always']));
2365
        $this->set_option('use_more_text', $form_state['values']['use_more_text']);
2366
      case 'distinct':
2367
        $this->set_option($section, $form_state['values'][$section]);
2368
        break;
2369
      case 'group_by':
2370
        $this->set_option($section, $form_state['values'][$section]);
2371
        break;
2372
      case 'row_plugin':
2373
        // This if prevents resetting options to default if they don't change
2374
        // the plugin.
2375
        if ($this->get_option($section) != $form_state['values'][$section]) {
2376
          $plugin = views_get_plugin('row', $form_state['values'][$section]);
2377
          if ($plugin) {
2378
            $this->set_option($section, $form_state['values'][$section]);
2379
            $this->set_option('row_options', array());
2380

    
2381
            // send ajax form to options page if we use it.
2382
            if (!empty($plugin->definition['uses options'])) {
2383
              views_ui_add_form_to_stack('display', $this->view, $this->display->id, array('row_options'));
2384
            }
2385
          }
2386
        }
2387
        break;
2388
      case 'style_plugin':
2389
        // This if prevents resetting options to default if they don't change
2390
        // the plugin.
2391
        if ($this->get_option($section) != $form_state['values'][$section]) {
2392
          $plugin = views_get_plugin('style', $form_state['values'][$section]);
2393
          if ($plugin) {
2394
            $this->set_option($section, $form_state['values'][$section]);
2395
            $this->set_option('style_options', array());
2396
            // send ajax form to options page if we use it.
2397
            if (!empty($plugin->definition['uses options'])) {
2398
              views_ui_add_form_to_stack('display', $this->view, $this->display->id, array('style_options'));
2399
            }
2400
          }
2401
        }
2402
        break;
2403
      case 'style_options':
2404
        $style = TRUE;
2405
      case 'row_options':
2406
        // if row, $style will be empty.
2407
        $plugin = $this->get_plugin(empty($style) ? 'row' : 'style');
2408
        if ($plugin) {
2409
          $plugin->options_submit($form['options'][$section], $form_state);
2410
        }
2411
        $this->set_option($section, $form_state['values'][$section]);
2412
        break;
2413
      case 'exposed_block':
2414
        $this->set_option($section, (bool) $form_state['values'][$section]);
2415
        break;
2416
      case 'exposed_form':
2417
        $exposed_form = $this->get_option('exposed_form');
2418
        if ($exposed_form['type'] != $form_state['values']['exposed_form']['type']) {
2419
          $plugin = views_get_plugin('exposed_form', $form_state['values']['exposed_form']['type']);
2420
          if ($plugin) {
2421
            $exposed_form = array('type' => $form_state['values']['exposed_form']['type'], 'options' => array());
2422
            $this->set_option('exposed_form', $exposed_form);
2423
            if (!empty($plugin->definition['uses options'])) {
2424
              views_ui_add_form_to_stack('display', $this->view, $this->display->id, array('exposed_form_options'));
2425
            }
2426
          }
2427
        }
2428

    
2429
        break;
2430
      case 'exposed_form_options':
2431
        $plugin = $this->get_plugin('exposed_form');
2432
        if ($plugin) {
2433
          $exposed_form = $this->get_option('exposed_form');
2434
          $plugin->options_submit($form['exposed_form_options'], $form_state);
2435
          $exposed_form['options'] = $form_state['values'][$section];
2436
          $this->set_option('exposed_form', $exposed_form);
2437
        }
2438
        break;
2439
      case 'pager':
2440
        $pager = $this->get_option('pager');
2441
        if ($pager['type'] != $form_state['values']['pager']['type']) {
2442
          $plugin = views_get_plugin('pager', $form_state['values']['pager']['type']);
2443
          if ($plugin) {
2444
            // Because pagers have very similar options, let's allow pagers to
2445
            // try to carry the options over.
2446
            $plugin->init($this->view, $this->display, $pager['options']);
2447

    
2448
            $pager = array('type' => $form_state['values']['pager']['type'], 'options' => $plugin->options);
2449
            $this->set_option('pager', $pager);
2450
            if (!empty($plugin->definition['uses options'])) {
2451
              views_ui_add_form_to_stack('display', $this->view, $this->display->id, array('pager_options'));
2452
            }
2453
          }
2454
        }
2455

    
2456
        break;
2457
      case 'pager_options':
2458
        $plugin = $this->get_plugin('pager');
2459
        if ($plugin) {
2460
          $pager = $this->get_option('pager');
2461
          $plugin->options_submit($form['pager_options'], $form_state);
2462
          $pager['options'] = $form_state['values'][$section];
2463
          $this->set_option('pager', $pager);
2464
        }
2465
        break;
2466
    }
2467

    
2468
    foreach ($this->extender as $extender) {
2469
      $extender->options_submit($form, $form_state);
2470
    }
2471
  }
2472

    
2473
  /**
2474
   * If override/revert was clicked, perform the proper toggle.
2475
   */
2476
  function options_override($form, &$form_state) {
2477
    $this->set_override($form_state['section']);
2478
  }
2479

    
2480
  /**
2481
   * Flip the override setting for the given section.
2482
   *
2483
   * @param string $section
2484
   *   Which option should be marked as overridden, for example "filters".
2485
   * @param bool $new_state
2486
   *   Select the new state of the option.
2487
   *     - TRUE: Revert to default.
2488
   *     - FALSE: Mark it as overridden.
2489
   */
2490
  function set_override($section, $new_state = NULL) {
2491
    $options = $this->defaultable_sections($section);
2492
    if (!$options) {
2493
      return;
2494
    }
2495

    
2496
    if (!isset($new_state)) {
2497
      $new_state = empty($this->options['defaults'][$section]);
2498
    }
2499

    
2500
    // For each option that is part of this group, fix our settings.
2501
    foreach ($options as $option) {
2502
      if ($new_state) {
2503
        // Revert to defaults.
2504
        unset($this->options[$option]);
2505
        unset($this->display->display_options[$option]);
2506
      }
2507
      else {
2508
        // copy existing values into our display.
2509
        $this->options[$option] = $this->get_option($option);
2510
        $this->display->display_options[$option] = $this->options[$option];
2511
      }
2512
      $this->options['defaults'][$option] = $new_state;
2513
      $this->display->display_options['defaults'][$option] = $new_state;
2514
    }
2515
  }
2516

    
2517
  /**
2518
   * Inject anything into the query that the display handler needs.
2519
   */
2520
  function query() {
2521
    foreach ($this->extender as $extender) {
2522
      $extender->query();
2523
    }
2524
  }
2525

    
2526
  /**
2527
   * Not all display plugins will support filtering
2528
   */
2529
  function render_filters() { }
2530

    
2531
  /**
2532
   * Not all display plugins will suppert pager rendering.
2533
   */
2534
  function render_pager() {
2535
    return TRUE;
2536
  }
2537

    
2538
  /**
2539
   * Render the 'more' link
2540
   */
2541
  function render_more_link() {
2542
    if ($this->use_more() && ($this->use_more_always() || (!empty($this->view->query->pager) && $this->view->query->pager->has_more_records()))) {
2543
      $path = $this->get_path();
2544

    
2545
      if ($this->get_option('link_display') == 'custom_url' && $override_path = $this->get_option('link_url')) {
2546
        $tokens = $this->get_arguments_tokens();
2547
        $path = strtr($override_path, $tokens);
2548
      }
2549

    
2550
      if ($path) {
2551
        if (empty($override_path)) {
2552
          $path = $this->view->get_url(NULL, $path);
2553
        }
2554
        $url_options = array();
2555
        if (!empty($this->view->exposed_raw_input)) {
2556
          $url_options['query'] = $this->view->exposed_raw_input;
2557
        }
2558
        $theme = views_theme_functions('views_more', $this->view, $this->display);
2559
        $path = check_url(url($path, $url_options));
2560

    
2561
        return theme($theme, array('more_url' => $path, 'link_text' => check_plain($this->use_more_text()), 'view' => $this->view));
2562
      }
2563
    }
2564
  }
2565

    
2566

    
2567
  /**
2568
   * Legacy functions.
2569
   */
2570

    
2571
  /**
2572
   * Render the header of the view.
2573
   */
2574
  function render_header() {
2575
    $empty = !empty($this->view->result);
2576
    return $this->render_area('header', $empty);
2577
  }
2578

    
2579
  /**
2580
   * Render the footer of the view.
2581
   */
2582
  function render_footer() {
2583
    $empty = !empty($this->view->result);
2584
    return $this->render_area('footer', $empty);
2585
  }
2586

    
2587
  function render_empty() {
2588
    return $this->render_area('empty');
2589
  }
2590

    
2591
  /**
2592
   * If this display creates a block, implement one of these.
2593
   */
2594
  function hook_block_list($delta = 0, $edit = array()) { return array(); }
2595

    
2596
  /**
2597
   * If this display creates a page with a menu item, implement it here.
2598
   */
2599
  function hook_menu() { return array(); }
2600

    
2601
  /**
2602
   * Render this display.
2603
   */
2604
  function render() {
2605
    return theme($this->theme_functions(), array('view' => $this->view));
2606
  }
2607

    
2608
  function render_area($area, $empty = FALSE) {
2609
    $return = '';
2610
    foreach ($this->get_handlers($area) as $area) {
2611
      $return .= $area->render($empty);
2612
    }
2613
    return $return;
2614
  }
2615

    
2616

    
2617
  /**
2618
   * Determine if the user has access to this display of the view.
2619
   */
2620
  function access($account = NULL) {
2621
    if (!isset($account)) {
2622
      global $user;
2623
      $account = $user;
2624
    }
2625

    
2626
    // Full override.
2627
    if (user_access('access all views', $account)) {
2628
      return TRUE;
2629
    }
2630

    
2631
    $plugin = $this->get_plugin('access');
2632
    if ($plugin) {
2633
      return $plugin->access($account);
2634
    }
2635

    
2636
    // fallback to all access if no plugin.
2637
    return TRUE;
2638
  }
2639

    
2640
  /**
2641
   * Set up any variables on the view prior to execution. These are separated
2642
   * from execute because they are extremely common and unlikely to be
2643
   * overridden on an individual display.
2644
   */
2645
  function pre_execute() {
2646
    $this->view->set_use_ajax($this->use_ajax());
2647
    if ($this->use_more() && !$this->use_more_always()) {
2648
      $this->view->get_total_rows = TRUE;
2649
    }
2650
    $this->view->init_handlers();
2651
    if ($this->uses_exposed()) {
2652
      $exposed_form = $this->get_plugin('exposed_form');
2653
      $exposed_form->pre_execute();
2654
    }
2655

    
2656
    foreach ($this->extender as $extender) {
2657
      $extender->pre_execute();
2658
    }
2659

    
2660
    if ($this->get_option('hide_admin_links')) {
2661
      $this->view->hide_admin_links = TRUE;
2662
    }
2663
  }
2664

    
2665
  /**
2666
   * When used externally, this is how a view gets run and returns
2667
   * data in the format required.
2668
   *
2669
   * The base class cannot be executed.
2670
   */
2671
  function execute() { }
2672

    
2673
  /**
2674
   * Fully render the display for the purposes of a live preview or
2675
   * some other AJAXy reason.
2676
   */
2677
  function preview() { return $this->view->render(); }
2678

    
2679
  /**
2680
   * Displays can require a certain type of style plugin. By default, they will
2681
   * be 'normal'.
2682
   */
2683
  function get_style_type() { return 'normal'; }
2684

    
2685
  /**
2686
   * Make sure the display and all associated handlers are valid.
2687
   *
2688
   * @return
2689
   *   Empty array if the display is valid; an array of error strings if it is not.
2690
   */
2691
  function validate() {
2692
    $errors = array();
2693
    // Make sure displays that use fields HAVE fields.
2694
    if ($this->uses_fields()) {
2695
      $fields = FALSE;
2696
      foreach ($this->get_handlers('field') as $field) {
2697
        if (empty($field->options['exclude'])) {
2698
          $fields = TRUE;
2699
        }
2700
      }
2701

    
2702
      if (!$fields) {
2703
        $errors[] = t('Display "@display" uses fields but there are none defined for it or all are excluded.', array('@display' => $this->display->display_title));
2704
      }
2705
    }
2706

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

    
2711
    // Validate style plugin
2712
    $style = $this->get_plugin();
2713
    if (empty($style)) {
2714
      $errors[] = t('Display "@display" has an invalid style plugin.', array('@display' => $this->display->display_title));
2715
    }
2716
    else {
2717
      $result = $style->validate();
2718
      if (!empty($result) && is_array($result)) {
2719
        $errors = array_merge($errors, $result);
2720
      }
2721
    }
2722

    
2723
    // Validate query plugin.
2724
    $query = $this->get_plugin('query');
2725
    $result = $query->validate();
2726
    if (!empty($result) && is_array($result)) {
2727
      $errors = array_merge($errors, $result);
2728
    }
2729

    
2730
    // Validate handlers
2731
    foreach (views_object_types() as $type => $info) {
2732
      foreach ($this->get_handlers($type) as $handler) {
2733
        $result = $handler->validate();
2734
        if (!empty($result) && is_array($result)) {
2735
          $errors = array_merge($errors, $result);
2736
        }
2737
      }
2738
    }
2739

    
2740
    return $errors;
2741
  }
2742

    
2743
  /**
2744
   * Check if the provided identifier is unique.
2745
   *
2746
   * @param string $id
2747
   *   The id of the handler which is checked.
2748
   * @param string $identifier
2749
   *   The actual get identifier configured in the exposed settings.
2750
   *
2751
   * @return bool
2752
   *   Returns whether the identifier is unique on all handlers.
2753
   *
2754
   */
2755
  function is_identifier_unique($id, $identifier) {
2756
    foreach (views_object_types() as $type => $info) {
2757
      foreach ($this->get_handlers($type) as $key => $handler) {
2758
        if ($handler->can_expose() && $handler->is_exposed()) {
2759
          if ($handler->is_a_group()) {
2760
            if ($id != $key && $identifier == $handler->options['group_info']['identifier']) {
2761
              return FALSE;
2762
            }
2763
          }
2764
          else {
2765
            if ($id != $key && $identifier == $handler->options['expose']['identifier']) {
2766
              return FALSE;
2767
            }
2768
          }
2769
        }
2770
      }
2771
    }
2772
    return TRUE;
2773
  }
2774

    
2775
  /**
2776
   * Provide the block system with any exposed widget blocks for this display.
2777
   */
2778
  function get_special_blocks() {
2779
    $blocks = array();
2780

    
2781
    if ($this->uses_exposed_form_in_block()) {
2782
      $delta = '-exp-' . $this->view->name . '-' . $this->display->id;
2783
      $desc = t('Exposed form: @view-@display_id', array('@view' => $this->view->name, '@display_id' => $this->display->id));
2784

    
2785
      $blocks[$delta] = array(
2786
        'info' => $desc,
2787
        'cache' => DRUPAL_NO_CACHE,
2788
      );
2789
    }
2790

    
2791
    return $blocks;
2792
  }
2793

    
2794
  /**
2795
   * Render any special blocks provided for this display.
2796
   */
2797
  function view_special_blocks($type) {
2798
    if ($type == '-exp') {
2799
      // avoid interfering with the admin forms.
2800
      if (arg(0) == 'admin' && arg(1) == 'structure' && arg(2) == 'views') {
2801
        return;
2802
      }
2803
      $this->view->init_handlers();
2804

    
2805
      if ($this->uses_exposed() && $this->get_option('exposed_block')) {
2806
        $exposed_form = $this->get_plugin('exposed_form');
2807
        return array(
2808
          'content' => $exposed_form->render_exposed_form(TRUE),
2809
        );
2810
      }
2811
    }
2812
  }
2813

    
2814
  /**
2815
   * Override of export_option()
2816
   *
2817
   * Because displays do not want to export options that are NOT overridden from the
2818
   * default display, we need some special handling during the export process.
2819
   */
2820
  function export_option($indent, $prefix, $storage, $option, $definition, $parents) {
2821
    // The $prefix is wrong because we store our actual options a little differently:
2822
    $prefix = '$handler->display->display_options';
2823
    $output = '';
2824
    if (!$parents && !$this->is_default_display()) {
2825
      // Do not export items that are not overridden.
2826
      if ($this->is_defaulted($option)) {
2827
        return;
2828
      }
2829

    
2830
      // If this is not defaulted and is overrideable, flip the switch to say this
2831
      // is overridden.
2832
      if ($this->defaultable_sections($option)) {
2833
        $output .= $indent . $prefix . "['defaults']['$option'] = FALSE;\n";
2834
      }
2835
    }
2836

    
2837
    $output .= parent::export_option($indent, $prefix, $storage, $option, $definition, $parents);
2838
    return $output;
2839
  }
2840

    
2841
  /**
2842
   * Special method to export items that have handlers.
2843
   *
2844
   * This method was specified in the option_definition() as the method to utilize to
2845
   * export fields, filters, sort criteria, relationships and arguments. This passes
2846
   * the export off to the individual handlers so that they can export themselves
2847
   * properly.
2848
   */
2849
  function export_handler($indent, $prefix, $storage, $option, $definition, $parents) {
2850
    $output = '';
2851

    
2852
    // cut the 's' off because the data is stored as the plural form but we need
2853
    // the singular form. Who designed that anyway? Oh yeah, I did. :(
2854
    if ($option != 'header' && $option != 'footer' && $option != 'empty') {
2855
      $type = substr($option, 0, -1);
2856
    }
2857
    else {
2858
      $type = $option;
2859
    }
2860
    $types = views_object_types();
2861
    foreach ($storage[$option] as $id => $info) {
2862
      if (!empty($types[$type]['type'])) {
2863
        $handler_type = $types[$type]['type'];
2864
      }
2865
      else {
2866
        $handler_type = $type;
2867
      }
2868
      // If aggregation is on, the group type might override the actual
2869
      // handler that is in use. This piece of code checks that and,
2870
      // if necessary, sets the override handler.
2871
      $override = NULL;
2872
      if ($this->use_group_by() && !empty($info['group_type'])) {
2873
        if (empty($this->view->query)) {
2874
          $this->view->init_query();
2875
        }
2876
        $aggregate = $this->view->query->get_aggregation_info();
2877
        if (!empty($aggregate[$info['group_type']]['handler'][$type])) {
2878
          $override = $aggregate[$info['group_type']]['handler'][$type];
2879
        }
2880
      }
2881
      $handler = views_get_handler($info['table'], $info['field'], $handler_type, $override);
2882
      if ($handler) {
2883
        $handler->init($this->view, $info);
2884
        $output .= $indent . '/* ' . $types[$type]['stitle'] . ': ' . $handler->ui_name() . " */\n";
2885
        $output .= $handler->export_options($indent, $prefix . "['$option']['$id']");
2886
      }
2887

    
2888
      // Prevent reference problems.
2889
      unset($handler);
2890
    }
2891

    
2892
    return $output;
2893
  }
2894

    
2895
  /**
2896
   * Special handling for the style export.
2897
   *
2898
   * Styles are stored as style_plugin and style_options or row_plugin and
2899
   * row_options accordingly. The options are told not to export, and the
2900
   * export for the plugin should export both.
2901
   */
2902
  function export_style($indent, $prefix, $storage, $option, $definition, $parents) {
2903
    $output = '';
2904
    $style_plugin = $this->get_plugin();
2905
    if ($option == 'style_plugin') {
2906
      $type = 'style';
2907
      $options_field = 'style_options';
2908
      $plugin = $style_plugin;
2909
    }
2910
    else {
2911
      if (!$style_plugin || !$style_plugin->uses_row_plugin()) {
2912
        return;
2913
      }
2914

    
2915
      $type = 'row';
2916
      $options_field = 'row_options';
2917
      $plugin = $this->get_plugin('row');
2918
      // If the style plugin doesn't use row plugins, don't even bother.
2919
    }
2920

    
2921
    if ($plugin) {
2922
      // Write which plugin to use.
2923
      $value = $this->get_option($option);
2924
      $output .= $indent . $prefix . "['$option'] = '$value';\n";
2925

    
2926
      // Pass off to the plugin to export itself.
2927
      $output .= $plugin->export_options($indent, $prefix . "['$options_field']");
2928
    }
2929

    
2930
    return $output;
2931
  }
2932

    
2933
  /**
2934
   * Special handling for plugin export
2935
   *
2936
   * Plugins other than styles are stored in array with 'type' being the key
2937
   * to the plugin. For modern plugins, the options are stored in the 'options'
2938
   * array, but for legacy plugins (access and cache) options are stored as
2939
   * siblings to the type.
2940
   */
2941
  function export_plugin($indent, $prefix, $storage, $option, $definition, $parents) {
2942
    $output = '';
2943
    $plugin_type = end($parents);
2944
    $plugin = $this->get_plugin($plugin_type);
2945
    if ($plugin) {
2946
      // Write which plugin to use.
2947
      $value = $storage[$option];
2948
      $new_prefix = $prefix . "['$plugin_type']";
2949

    
2950
      $output .= $indent . $new_prefix . "['$option'] = '$value';\n";
2951

    
2952
      if ($plugin_type != 'access' && $plugin_type!= 'cache') {
2953
        $new_prefix .= "['options']";
2954
      }
2955

    
2956
      // Pass off to the plugin to export itself.
2957
      $output .= $plugin->export_options($indent, $new_prefix);
2958
    }
2959

    
2960
    return $output;
2961
  }
2962

    
2963
  function unpack_style($indent, $prefix, $storage, $option, $definition, $parents) {
2964
    $output = '';
2965
    $style_plugin = $this->get_plugin();
2966
    if ($option == 'style_plugin') {
2967
      $type = 'style';
2968
      $options_field = 'style_options';
2969
      $plugin = $style_plugin;
2970
    }
2971
    else {
2972
      if (!$style_plugin || !$style_plugin->uses_row_plugin()) {
2973
        return;
2974
      }
2975

    
2976
      $type = 'row';
2977
      $options_field = 'row_options';
2978
      $plugin = $this->get_plugin('row');
2979
      // If the style plugin doesn't use row plugins, don't even bother.
2980
    }
2981

    
2982
    if ($plugin) {
2983
      return $plugin->unpack_translatables($translatable, $parents);
2984
    }
2985
  }
2986

    
2987
  /**
2988
   * Special handling for plugin unpacking.
2989
   */
2990
  function unpack_plugin(&$translatable, $storage, $option, $definition, $parents) {
2991
    $plugin_type = end($parents);
2992
    $plugin = $this->get_plugin($plugin_type);
2993
    if ($plugin) {
2994
      // Write which plugin to use.
2995
      return $plugin->unpack_translatables($translatable, $parents);
2996
    }
2997
  }
2998

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

    
3010
    // cut the 's' off because the data is stored as the plural form but we need
3011
    // the singular form. Who designed that anyway? Oh yeah, I did. :(
3012
    if ($option != 'header' && $option != 'footer' && $option != 'empty') {
3013
      $type = substr($option, 0, -1);
3014
    }
3015
    else {
3016
      $type = $option;
3017
    }
3018
    $types = views_object_types();
3019
    foreach ($storage[$option] as $id => $info) {
3020
      if (!empty($types[$type]['type'])) {
3021
        $handler_type = $types[$type]['type'];
3022
      }
3023
      else {
3024
        $handler_type = $type;
3025
      }
3026
      $handler = views_get_handler($info['table'], $info['field'], $handler_type);
3027
      if ($handler) {
3028
        $handler->init($this->view, $info);
3029
        $handler->unpack_translatables($translatable, array_merge($parents, array($type, $info['table'], $info['id'])));
3030
      }
3031

    
3032
      // Prevent reference problems.
3033
      unset($handler);
3034
    }
3035

    
3036
    return $output;
3037
  }
3038

    
3039
  /**
3040
   * Provide some helpful text for the arguments.
3041
   * The result should contain of an array with
3042
   *   - filter value present: The title of the fieldset in the argument
3043
   *     where you can configure what should be done with a given argument.
3044
   *   - filter value not present: The tiel of the fieldset in the argument
3045
   *     where you can configure what should be done if the argument does not
3046
   *     exist.
3047
   *   - description: A description about how arguments comes to the display.
3048
   *     For example blocks don't get it from url.
3049
   */
3050
  function get_argument_text() {
3051
    return array(
3052
      'filter value not present' => t('When the filter value is <em>NOT</em> available'),
3053
      'filter value present' => t('When the filter value <em>IS</em> available or a default is provided'),
3054
      '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'."),
3055
    );
3056
  }
3057

    
3058
  /**
3059
   * Provide some helpful text for pagers.
3060
   *
3061
   * The result should contain of an array within
3062
   *   - items per page title
3063
   */
3064
  function get_pager_text() {
3065
    return array(
3066
      'items per page title' => t('Items to display'),
3067
      'items per page description' => t('The number of items to display. Enter 0 for no limit.')
3068
    );
3069
  }
3070
}
3071

    
3072

    
3073
/**
3074
 * @}
3075
 */