Projet

Général

Profil

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

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

1
<?php
2

    
3
/**
4
 * @file
5
 * Contains the base display plugin.
6
 */
7

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

    
21
/**
22
 * The default display plugin handler. Display plugins handle options and
23
 * basic mechanisms for different output methods.
24
 */
25
class views_plugin_display extends views_plugin {
26
  /**
27
   * The top object of a view.
28
   *
29
   * @var view
30
   */
31
  var $view = NULL;
32

    
33
  var $handlers = array();
34

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

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

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

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

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

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

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

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

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

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

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

    
148

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

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

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

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

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

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

    
247
      $changed = TRUE;
248
    }
249

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

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

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

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

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

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

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

    
305
    return $this->has_exposed;
306
  }
307

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

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

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

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

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

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

    
368
  /**
369
   * Does the display have custom link text?
370
   */
371
  function use_more_text() {
372
    if (!empty($this->definition['use more'])) {
373
      return $this->get_option('use_more_text');
374
    }
375
    return FALSE;
376
  }
377

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
718
    return $options;
719
  }
720

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
1064
    // Truncate the path as it is displayed as a link.
1065
    if ($section == 'path') {
1066
      $text = views_ui_truncate($text, 24);
1067
    }
1068

    
1069
    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));
1070
  }
1071

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

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

    
1096
    return $tokens;
1097
  }
1098

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

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

    
1158
    $display_comment = check_plain(views_ui_truncate($this->get_option('display_comment'), 80));
1159
    $options['display_comment'] = array(
1160
      'category' => 'other',
1161
      'title' => t('Comment'),
1162
      'value' => !empty($display_comment) ? $display_comment : t('No comment'),
1163
      'desc' => t('Comment or document this display.'),
1164
    );
1165

    
1166
    $title = strip_tags($this->get_option('title'));
1167
    if (!$title) {
1168
      $title = t('None');
1169
    }
1170

    
1171
    $options['title'] = array(
1172
      'category' => 'title',
1173
      'title' => t('Title'),
1174
      'value' => $title,
1175
      'desc' => t('Change the title that this display will use.'),
1176
    );
1177

    
1178
    $style_plugin = views_fetch_plugin_data('style', $this->get_option('style_plugin'));
1179
    $style_plugin_instance = $this->get_plugin('style');
1180
    $style_summary = empty($style_plugin['title']) ? t('Missing style plugin') : $style_plugin_instance->summary_title();
1181
    $style_title = empty($style_plugin['title']) ? t('Missing style plugin') : $style_plugin_instance->plugin_title();
1182

    
1183
    $style = '';
1184

    
1185
    $options['style_plugin'] = array(
1186
      'category' => 'format',
1187
      'title' => t('Format'),
1188
      'value' => $style_title,
1189
      'setting' => $style_summary,
1190
      'desc' => t('Change the way content is formatted.'),
1191
    );
1192

    
1193
    // This adds a 'Settings' link to the style_options setting if the style has options.
1194
    if (!empty($style_plugin['uses options'])) {
1195
      $options['style_plugin']['links']['style_options'] = t('Change settings for this format');
1196
    }
1197

    
1198
    if (!empty($style_plugin['uses row plugin'])) {
1199
      $row_plugin = views_fetch_plugin_data('row', $this->get_option('row_plugin'));
1200
      $row_plugin_instance = $this->get_plugin('row');
1201
      $row_summary = empty($row_plugin['title']) ? t('Missing style plugin') : $row_plugin_instance->summary_title();
1202
      $row_title = empty($row_plugin['title']) ? t('Missing style plugin') : $row_plugin_instance->plugin_title();
1203

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

    
1241
    $pager_plugin = $this->get_plugin('pager');
1242
    if (!$pager_plugin) {
1243
      // default to the no pager plugin.
1244
      $pager_plugin = views_get_plugin('pager', 'none');
1245
    }
1246

    
1247
    $pager_str = $pager_plugin->summary_title();
1248

    
1249
    $options['pager'] = array(
1250
      'category' => 'pager',
1251
      'title' => t('Use pager'),
1252
      'value' => $pager_plugin->plugin_title(),
1253
      'setting' => $pager_str,
1254
      'desc' => t("Change this display's pager setting."),
1255
    );
1256

    
1257
    // If pagers aren't allowed, change the text of the item:
1258
    if (empty($this->definition['use pager'])) {
1259
      $options['pager']['title'] = t('Items to display');
1260
    }
1261

    
1262
    if (!empty($pager_plugin->definition['uses options'])) {
1263
      $options['pager']['links']['pager_options'] = t('Change settings for this pager type.');
1264
    }
1265

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

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

    
1285
    $options['query'] = array(
1286
      'category' => 'other',
1287
      'title' => t('Query settings'),
1288
      'value' => t('Settings'),
1289
      'desc' => t('Allow to set some advanced settings for the query plugin'),
1290
    );
1291

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

    
1308
    $access_plugin = $this->get_plugin('access');
1309
    if (!$access_plugin) {
1310
      // default to the no access control plugin.
1311
      $access_plugin = views_get_plugin('access', 'none');
1312
    }
1313

    
1314
    $access_str = $access_plugin->summary_title();
1315

    
1316
    $options['access'] = array(
1317
      'category' => 'access',
1318
      'title' => t('Access'),
1319
      'value' => $access_plugin->plugin_title(),
1320
      'setting' => $access_str,
1321
      'desc' => t('Specify access control type for this display.'),
1322
    );
1323

    
1324
    if (!empty($access_plugin->definition['uses options'])) {
1325
      $options['access']['links']['access_options'] = t('Change settings for this access type.');
1326
    }
1327

    
1328
    $cache_plugin = $this->get_plugin('cache');
1329
    if (!$cache_plugin) {
1330
      // default to the no cache control plugin.
1331
      $cache_plugin = views_get_plugin('cache', 'none');
1332
    }
1333

    
1334
    $cache_str = $cache_plugin->summary_title();
1335

    
1336
    $options['cache'] = array(
1337
      'category' => 'other',
1338
      'title' => t('Caching'),
1339
      'value' => $cache_plugin->plugin_title(),
1340
      'setting' => $cache_str,
1341
      'desc' => t('Specify caching type for this display.'),
1342
    );
1343

    
1344
    if (!empty($cache_plugin->definition['uses options'])) {
1345
      $options['cache']['links']['cache_options'] = t('Change settings for this caching type.');
1346
    }
1347

    
1348
    if (!empty($access_plugin->definition['uses options'])) {
1349
      $options['access']['links']['access_options'] = t('Change settings for this access type.');
1350
    }
1351

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

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

    
1373
    $exposed_form_plugin = $this->get_plugin('exposed_form');
1374
    if (!$exposed_form_plugin) {
1375
      // default to the no cache control plugin.
1376
      $exposed_form_plugin = views_get_plugin('exposed_form', 'basic');
1377
    }
1378

    
1379
    $exposed_form_str = $exposed_form_plugin->summary_title();
1380

    
1381
    $options['exposed_form'] = array(
1382
      'category' => 'exposed',
1383
      'title' => t('Exposed form style'),
1384
      'value' => $exposed_form_plugin->plugin_title(),
1385
      'setting' => $exposed_form_str,
1386
      'desc' => t('Select the kind of exposed filter to use.'),
1387
    );
1388

    
1389
    if (!empty($exposed_form_plugin->definition['uses options'])) {
1390
      $options['exposed_form']['links']['exposed_form_options'] = t('Exposed form settings for this exposed form style.');
1391
    }
1392

    
1393
    $css_class = check_plain(trim($this->get_option('css_class')));
1394
    if (!$css_class) {
1395
      $css_class = t('None');
1396
    }
1397

    
1398
    $options['css_class'] = array(
1399
      'category' => 'other',
1400
      'title' => t('CSS class'),
1401
      'value' => $css_class,
1402
      'desc' => t('Change the CSS class name(s) that will be added to this display.'),
1403
    );
1404

    
1405
    $options['analyze-theme'] = array(
1406
      'category' => 'other',
1407
      'title' => t('Theme'),
1408
      'value' => t('Information'),
1409
      'desc' => t('Get information on how to theme this display'),
1410
    );
1411

    
1412
    foreach ($this->extender as $extender) {
1413
      $extender->options_summary($categories, $options);
1414
    }
1415
  }
1416

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

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

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

    
1559
        $access = $this->get_option('access');
1560
        $form['access']['type'] =  array(
1561
          '#type' => 'radios',
1562
          '#options' => views_fetch_plugin_names('access', NULL, array($this->view->base_table)),
1563
          '#default_value' => $access['type'],
1564
        );
1565

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

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

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

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

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

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

    
1648
        $form['#title'] .= t('Query options');
1649
        $this->view->init_query();
1650
        if ($this->view->query) {
1651
          if (!empty($this->view->query->definition['help topic'])) {
1652
            $form['#help_topic'] = $this->view->query->definition['help topic'];
1653
          }
1654
          if (!empty($this->view->query->definition['module'])) {
1655
            $form['#help_module'] = $this->view->query->definition['module'];
1656
          }
1657

    
1658
          $form['query'] = array(
1659
            '#tree' => TRUE,
1660
            'type' => array(
1661
              '#type' => 'value',
1662
              '#value' => $plugin_name,
1663
            ),
1664
            'options' => array(
1665
              '#tree' => TRUE,
1666
            ),
1667
          );
1668

    
1669
          $this->view->query->options_form($form['query']['options'], $form_state);
1670
        }
1671
        break;
1672
      case 'field_language':
1673
        $form['#title'] .= t('Field Language');
1674

    
1675
        $entities = entity_get_info();
1676
        $entity_tables = array();
1677
        $has_translation_handlers = FALSE;
1678
        foreach ($entities as $type => $entity_info) {
1679
          $entity_tables[] = $entity_info['base table'];
1680

    
1681
          if (!empty($entity_info['translation'])) {
1682
            $has_translation_handlers = TRUE;
1683
          }
1684
        }
1685

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

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

    
1724
        $style_plugin = views_fetch_plugin_data('style', $this->get_option('style_plugin'));
1725
        if (!empty($style_plugin['uses options'])) {
1726
          $form['markup'] = array(
1727
            '#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>',
1728
          );
1729
        }
1730

    
1731
        break;
1732
      case 'style_options':
1733
        $form['#title'] .= t('Style options');
1734
        $style = TRUE;
1735
        $type = 'style_plugin';
1736
        $name = $this->get_option('style_plugin');
1737

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

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

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

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

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

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

    
1834
        if (isset($_POST['theme'])) {
1835
          $this->theme = $_POST['theme'];
1836
        }
1837
        elseif (empty($this->theme)) {
1838
          $this->theme = variable_get('theme_default', 'bartik');
1839
        }
1840

    
1841
        if (isset($GLOBALS['theme']) && $GLOBALS['theme'] == $this->theme) {
1842
          $this->theme_registry = theme_get_registry();
1843
          $theme_engine = $GLOBALS['theme_engine'];
1844
        }
1845
        else {
1846
          $themes = list_themes();
1847
          $theme = $themes[$this->theme];
1848

    
1849
          // Find all our ancestor themes and put them in an array.
1850
          $base_theme = array();
1851
          $ancestor = $this->theme;
1852
          while ($ancestor && isset($themes[$ancestor]->base_theme)) {
1853
            $ancestor = $themes[$ancestor]->base_theme;
1854
            $base_theme[] = $themes[$ancestor];
1855
          }
1856

    
1857
          // The base themes should be initialized in the right order.
1858
          $base_theme = array_reverse($base_theme);
1859

    
1860
          // This code is copied directly from _drupal_theme_initialize()
1861
          $theme_engine = NULL;
1862

    
1863
          // Initialize the theme.
1864
          if (isset($theme->engine)) {
1865
            // Include the engine.
1866
            include_once DRUPAL_ROOT . '/' . $theme->owner;
1867

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

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

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

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

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

    
1939
          if ($plugin->uses_fields()) {
1940
            foreach ($this->get_handlers('field') as $id => $handler) {
1941
              $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());
1942
            }
1943
          }
1944
        }
1945

    
1946
        $form['important'] = array(
1947
          '#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>',
1948
        );
1949

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

    
1958
        foreach (list_themes() as $key => $theme) {
1959
          if (!empty($theme->info['hidden'])) {
1960
            continue;
1961
          }
1962
          $options[$key] = $theme->info['name'];
1963
        }
1964

    
1965
        $form['box'] = array(
1966
          '#prefix' => '<div class="container-inline">',
1967
          '#suffix' => '</div>',
1968
        );
1969
        $form['box']['theme'] = array(
1970
          '#type' => 'select',
1971
          '#options' => $options,
1972
          '#default_value' => $this->theme,
1973
        );
1974

    
1975
        $form['box']['change'] = array(
1976
          '#type' => 'submit',
1977
          '#value' => t('Change theme'),
1978
          '#submit' => array('views_ui_edit_display_form_change_theme'),
1979
        );
1980

    
1981
        $form['analysis'] = array(
1982
          '#markup' => '<div class="form-item">' . theme('item_list', array('items' => $funcs)) . '</div>',
1983
        );
1984

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

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

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

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

    
2019
        $form['analysis'] = array(
2020
          '#markup' => '<div class="form-item">' . $output . '</div>',
2021
        );
2022

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

    
2029
        $plugin = $this->get_plugin();
2030

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

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

    
2046
        $form['analysis'] = array(
2047
          '#markup' => '<div class="form-item">' . $output . '</div>',
2048
        );
2049

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

    
2056
        $plugin = $this->get_plugin('row');
2057

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

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

    
2073
        $form['analysis'] = array(
2074
          '#markup' => '<div class="form-item">' . $output . '</div>',
2075
        );
2076

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

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

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

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

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

    
2114
        $exposed_form = $this->get_option('exposed_form');
2115
        $form['exposed_form']['type'] =  array(
2116
          '#type' => 'radios',
2117
          '#options' => views_fetch_plugin_names('exposed_form', NULL, array($this->view->base_table)),
2118
          '#default_value' => $exposed_form['type'],
2119
        );
2120

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

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

    
2155
        $pager = $this->get_option('pager');
2156
        $form['pager']['type'] =  array(
2157
          '#type' => 'radios',
2158
          '#options' => views_fetch_plugin_names('pager', empty($this->definition['use pager']) ? 'basic' : NULL, array($this->view->base_table)),
2159
          '#default_value' => $pager['type'],
2160
        );
2161

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

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

    
2183
          $form['pager_options'] = array(
2184
            '#tree' => TRUE,
2185
          );
2186
          $plugin->options_form($form['pager_options'], $form_state);
2187
        }
2188
        break;
2189
    }
2190

    
2191
    foreach ($this->extender as $extender) {
2192
      $extender->options_form($form, $form_state);
2193
    }
2194
  }
2195

    
2196
  /**
2197
   * Format a list of theme templates for output by the theme info helper.
2198
   */
2199
  function format_themes($themes) {
2200
    $registry = $this->theme_registry;
2201
    $extension = $this->theme_extension;
2202

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

    
2221
    return implode(', ', array_reverse($fixed));
2222
  }
2223

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

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

    
2293
    foreach ($this->extender as $extender) {
2294
      $extender->options_validate($form, $form_state);
2295
    }
2296
  }
2297

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

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

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

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

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

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

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

    
2472
            $pager = array('type' => $form_state['values']['pager']['type'], 'options' => $plugin->options);
2473
            $this->set_option('pager', $pager);
2474
            if (!empty($plugin->definition['uses options'])) {
2475
              views_ui_add_form_to_stack('display', $this->view, $this->display->id, array('pager_options'));
2476
            }
2477
          }
2478
        }
2479

    
2480
        break;
2481
      case 'pager_options':
2482
        $plugin = $this->get_plugin('pager');
2483
        if ($plugin) {
2484
          $pager = $this->get_option('pager');
2485
          $plugin->options_submit($form['pager_options'], $form_state);
2486
          $pager['options'] = $form_state['values'][$section];
2487
          $this->set_option('pager', $pager);
2488
        }
2489
        break;
2490
    }
2491

    
2492
    foreach ($this->extender as $extender) {
2493
      $extender->options_submit($form, $form_state);
2494
    }
2495
  }
2496

    
2497
  /**
2498
   * If override/revert was clicked, perform the proper toggle.
2499
   */
2500
  function options_override($form, &$form_state) {
2501
    $this->set_override($form_state['section']);
2502
  }
2503

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

    
2520
    if (!isset($new_state)) {
2521
      $new_state = empty($this->options['defaults'][$section]);
2522
    }
2523

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

    
2541
  /**
2542
   * Inject anything into the query that the display handler needs.
2543
   */
2544
  function query() {
2545
    foreach ($this->extender as $extender) {
2546
      $extender->query();
2547
    }
2548
  }
2549

    
2550
  /**
2551
   * Not all display plugins will support filtering
2552
   */
2553
  function render_filters() { }
2554

    
2555
  /**
2556
   * Not all display plugins will suppert pager rendering.
2557
   */
2558
  function render_pager() {
2559
    return TRUE;
2560
  }
2561

    
2562
  /**
2563
   * Render the 'more' link
2564
   */
2565
  function render_more_link() {
2566
    if ($this->use_more() && ($this->use_more_always() || (!empty($this->view->query->pager) && $this->view->query->pager->has_more_records()))) {
2567
      $path = $this->get_path();
2568

    
2569
      if ($this->get_option('link_display') == 'custom_url' && $override_path = $this->get_option('link_url')) {
2570
        $tokens = $this->get_arguments_tokens();
2571
        $path = strtr($override_path, $tokens);
2572
      }
2573

    
2574
      if ($path) {
2575
        if (empty($override_path)) {
2576
          $path = $this->view->get_url(NULL, $path);
2577
        }
2578
        $url_options = array();
2579
        if (!empty($this->view->exposed_raw_input)) {
2580
          $url_options['query'] = $this->view->exposed_raw_input;
2581
        }
2582
        $theme = views_theme_functions('views_more', $this->view, $this->display);
2583

    
2584
        $parsed_url = drupal_parse_url($path);
2585
        // Preserve the query string from url.
2586
        if (!empty($parsed_url['query'])) {
2587
          if (!empty($url_options['query'])) {
2588
            $url_options['query'] = array_merge($parsed_url['query'], $url_options['query']);
2589
          }
2590
          else {
2591
            $url_options['query'] = $parsed_url['query'];
2592
          }
2593
          $path = $parsed_url['path'];
2594
        }
2595
        // Add fragment if applicable.
2596
        if (!empty($parsed_url['fragment'])) {
2597
          $url_options['fragment'] = $parsed_url['fragment'];
2598
        }
2599

    
2600
        $path = check_url(url($path, $url_options));
2601

    
2602
        return theme($theme, array('more_url' => $path, 'link_text' => check_plain($this->use_more_text()), 'view' => $this->view));
2603
      }
2604
    }
2605
  }
2606

    
2607

    
2608
  /**
2609
   * Legacy functions.
2610
   */
2611

    
2612
  /**
2613
   * Render the header of the view.
2614
   */
2615
  function render_header() {
2616
    $empty = !empty($this->view->result);
2617
    return $this->render_area('header', $empty);
2618
  }
2619

    
2620
  /**
2621
   * Render the footer of the view.
2622
   */
2623
  function render_footer() {
2624
    $empty = !empty($this->view->result);
2625
    return $this->render_area('footer', $empty);
2626
  }
2627

    
2628
  function render_empty() {
2629
    return $this->render_area('empty');
2630
  }
2631

    
2632
  /**
2633
   * If this display creates a block, implement one of these.
2634
   */
2635
  function hook_block_list($delta = 0, $edit = array()) { return array(); }
2636

    
2637
  /**
2638
   * If this display creates a page with a menu item, implement it here.
2639
   */
2640
  function hook_menu() { return array(); }
2641

    
2642
  /**
2643
   * Render this display.
2644
   */
2645
  function render() {
2646
    return theme($this->theme_functions(), array('view' => $this->view));
2647
  }
2648

    
2649
  function render_area($area, $empty = FALSE) {
2650
    $return = '';
2651
    foreach ($this->get_handlers($area) as $area) {
2652
      $return .= $area->render($empty);
2653
    }
2654
    return $return;
2655
  }
2656

    
2657

    
2658
  /**
2659
   * Determine if the user has access to this display of the view.
2660
   */
2661
  function access($account = NULL) {
2662
    if (!isset($account)) {
2663
      global $user;
2664
      $account = $user;
2665
    }
2666

    
2667
    // Full override.
2668
    if (user_access('access all views', $account)) {
2669
      return TRUE;
2670
    }
2671

    
2672
    $plugin = $this->get_plugin('access');
2673
    if ($plugin) {
2674
      return $plugin->access($account);
2675
    }
2676

    
2677
    // fallback to all access if no plugin.
2678
    return TRUE;
2679
  }
2680

    
2681
  /**
2682
   * Set up any variables on the view prior to execution. These are separated
2683
   * from execute because they are extremely common and unlikely to be
2684
   * overridden on an individual display.
2685
   */
2686
  function pre_execute() {
2687
    $this->view->set_use_ajax($this->use_ajax());
2688
    if ($this->use_more() && !$this->use_more_always()) {
2689
      $this->view->get_total_rows = TRUE;
2690
    }
2691
    $this->view->init_handlers();
2692
    if ($this->uses_exposed()) {
2693
      $exposed_form = $this->get_plugin('exposed_form');
2694
      $exposed_form->pre_execute();
2695
    }
2696

    
2697
    foreach ($this->extender as $extender) {
2698
      $extender->pre_execute();
2699
    }
2700

    
2701
    if ($this->get_option('hide_admin_links')) {
2702
      $this->view->hide_admin_links = TRUE;
2703
    }
2704
  }
2705

    
2706
  /**
2707
   * When used externally, this is how a view gets run and returns
2708
   * data in the format required.
2709
   *
2710
   * The base class cannot be executed.
2711
   */
2712
  function execute() { }
2713

    
2714
  /**
2715
   * Fully render the display for the purposes of a live preview or
2716
   * some other AJAXy reason.
2717
   */
2718
  function preview() { return $this->view->render(); }
2719

    
2720
  /**
2721
   * Displays can require a certain type of style plugin. By default, they will
2722
   * be 'normal'.
2723
   */
2724
  function get_style_type() { return 'normal'; }
2725

    
2726
  /**
2727
   * Make sure the display and all associated handlers are valid.
2728
   *
2729
   * @return
2730
   *   Empty array if the display is valid; an array of error strings if it is not.
2731
   */
2732
  function validate() {
2733
    $errors = array();
2734
    // Make sure displays that use fields HAVE fields.
2735
    if ($this->uses_fields()) {
2736
      $fields = FALSE;
2737
      foreach ($this->get_handlers('field') as $field) {
2738
        if (empty($field->options['exclude'])) {
2739
          $fields = TRUE;
2740
        }
2741
      }
2742

    
2743
      if (!$fields) {
2744
        $errors[] = t('Display "@display" uses fields but there are none defined for it or all are excluded.', array('@display' => $this->display->display_title));
2745
      }
2746
    }
2747

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

    
2752
    // Validate style plugin
2753
    $style = $this->get_plugin();
2754
    if (empty($style)) {
2755
      $errors[] = t('Display "@display" has an invalid style plugin.', array('@display' => $this->display->display_title));
2756
    }
2757
    else {
2758
      $result = $style->validate();
2759
      if (!empty($result) && is_array($result)) {
2760
        $errors = array_merge($errors, $result);
2761
      }
2762
    }
2763

    
2764
    // Validate query plugin.
2765
    $query = $this->get_plugin('query');
2766
    $result = $query->validate();
2767
    if (!empty($result) && is_array($result)) {
2768
      $errors = array_merge($errors, $result);
2769
    }
2770

    
2771
    // Validate handlers
2772
    foreach (views_object_types() as $type => $info) {
2773
      foreach ($this->get_handlers($type) as $handler) {
2774
        $result = $handler->validate();
2775
        if (!empty($result) && is_array($result)) {
2776
          $errors = array_merge($errors, $result);
2777
        }
2778
      }
2779
    }
2780

    
2781
    return $errors;
2782
  }
2783

    
2784
  /**
2785
   * Check if the provided identifier is unique.
2786
   *
2787
   * @param string $id
2788
   *   The id of the handler which is checked.
2789
   * @param string $identifier
2790
   *   The actual get identifier configured in the exposed settings.
2791
   *
2792
   * @return bool
2793
   *   Returns whether the identifier is unique on all handlers.
2794
   *
2795
   */
2796
  function is_identifier_unique($id, $identifier) {
2797
    foreach (views_object_types() as $type => $info) {
2798
      foreach ($this->get_handlers($type) as $key => $handler) {
2799
        if ($handler->can_expose() && $handler->is_exposed()) {
2800
          if ($handler->is_a_group()) {
2801
            if ($id != $key && $identifier == $handler->options['group_info']['identifier']) {
2802
              return FALSE;
2803
            }
2804
          }
2805
          else {
2806
            if ($id != $key && isset($handler->options['expose']['identifier']) && $identifier == $handler->options['expose']['identifier']) {
2807
              return FALSE;
2808
            }
2809
          }
2810
        }
2811
      }
2812
    }
2813
    return TRUE;
2814
  }
2815

    
2816
  /**
2817
   * Provide the block system with any exposed widget blocks for this display.
2818
   */
2819
  function get_special_blocks() {
2820
    $blocks = array();
2821

    
2822
    if ($this->uses_exposed_form_in_block()) {
2823
      $delta = '-exp-' . $this->view->name . '-' . $this->display->id;
2824
      $desc = t('Exposed form: @view-@display_id', array('@view' => $this->view->name, '@display_id' => $this->display->id));
2825

    
2826
      $blocks[$delta] = array(
2827
        'info' => $desc,
2828
        'cache' => DRUPAL_NO_CACHE,
2829
      );
2830
    }
2831

    
2832
    return $blocks;
2833
  }
2834

    
2835
  /**
2836
   * Render any special blocks provided for this display.
2837
   */
2838
  function view_special_blocks($type) {
2839
    if ($type == '-exp') {
2840
      // avoid interfering with the admin forms.
2841
      if (arg(0) == 'admin' && arg(1) == 'structure' && arg(2) == 'views') {
2842
        return;
2843
      }
2844
      $this->view->init_handlers();
2845

    
2846
      if ($this->uses_exposed() && $this->get_option('exposed_block')) {
2847
        $exposed_form = $this->get_plugin('exposed_form');
2848
        return array(
2849
          'content' => $exposed_form->render_exposed_form(TRUE),
2850
        );
2851
      }
2852
    }
2853
  }
2854

    
2855
  /**
2856
   * Override of export_option()
2857
   *
2858
   * Because displays do not want to export options that are NOT overridden from the
2859
   * default display, we need some special handling during the export process.
2860
   */
2861
  function export_option($indent, $prefix, $storage, $option, $definition, $parents) {
2862
    // The $prefix is wrong because we store our actual options a little differently:
2863
    $prefix = '$handler->display->display_options';
2864
    $output = '';
2865
    if (!$parents && !$this->is_default_display()) {
2866
      // Do not export items that are not overridden.
2867
      if ($this->is_defaulted($option)) {
2868
        return;
2869
      }
2870

    
2871
      // If this is not defaulted and is overrideable, flip the switch to say this
2872
      // is overridden.
2873
      if ($this->defaultable_sections($option)) {
2874
        $output .= $indent . $prefix . "['defaults']['$option'] = FALSE;\n";
2875
      }
2876
    }
2877

    
2878
    $output .= parent::export_option($indent, $prefix, $storage, $option, $definition, $parents);
2879
    return $output;
2880
  }
2881

    
2882
  /**
2883
   * Special method to export items that have handlers.
2884
   *
2885
   * This method was specified in the option_definition() as the method to utilize to
2886
   * export fields, filters, sort criteria, relationships and arguments. This passes
2887
   * the export off to the individual handlers so that they can export themselves
2888
   * properly.
2889
   */
2890
  function export_handler($indent, $prefix, $storage, $option, $definition, $parents) {
2891
    $output = '';
2892

    
2893
    // cut the 's' off because the data is stored as the plural form but we need
2894
    // the singular form. Who designed that anyway? Oh yeah, I did. :(
2895
    if ($option != 'header' && $option != 'footer' && $option != 'empty') {
2896
      $type = substr($option, 0, -1);
2897
    }
2898
    else {
2899
      $type = $option;
2900
    }
2901
    $types = views_object_types();
2902
    foreach ($storage[$option] as $id => $info) {
2903
      if (!empty($types[$type]['type'])) {
2904
        $handler_type = $types[$type]['type'];
2905
      }
2906
      else {
2907
        $handler_type = $type;
2908
      }
2909
      // If aggregation is on, the group type might override the actual
2910
      // handler that is in use. This piece of code checks that and,
2911
      // if necessary, sets the override handler.
2912
      $override = NULL;
2913
      if ($this->use_group_by() && !empty($info['group_type'])) {
2914
        if (empty($this->view->query)) {
2915
          $this->view->init_query();
2916
        }
2917
        $aggregate = $this->view->query->get_aggregation_info();
2918
        if (!empty($aggregate[$info['group_type']]['handler'][$type])) {
2919
          $override = $aggregate[$info['group_type']]['handler'][$type];
2920
        }
2921
      }
2922
      $handler = views_get_handler($info['table'], $info['field'], $handler_type, $override);
2923
      if ($handler) {
2924
        $handler->init($this->view, $info);
2925
        $output .= $indent . '/* ' . $types[$type]['stitle'] . ': ' . $handler->ui_name() . " */\n";
2926
        $output .= $handler->export_options($indent, $prefix . "['$option']['$id']");
2927
      }
2928

    
2929
      // Prevent reference problems.
2930
      unset($handler);
2931
    }
2932

    
2933
    return $output;
2934
  }
2935

    
2936
  /**
2937
   * Special handling for the style export.
2938
   *
2939
   * Styles are stored as style_plugin and style_options or row_plugin and
2940
   * row_options accordingly. The options are told not to export, and the
2941
   * export for the plugin should export both.
2942
   */
2943
  function export_style($indent, $prefix, $storage, $option, $definition, $parents) {
2944
    $output = '';
2945
    $style_plugin = $this->get_plugin();
2946
    if ($option == 'style_plugin') {
2947
      $type = 'style';
2948
      $options_field = 'style_options';
2949
      $plugin = $style_plugin;
2950
    }
2951
    else {
2952
      if (!$style_plugin || !$style_plugin->uses_row_plugin()) {
2953
        return;
2954
      }
2955

    
2956
      $type = 'row';
2957
      $options_field = 'row_options';
2958
      $plugin = $this->get_plugin('row');
2959
      // If the style plugin doesn't use row plugins, don't even bother.
2960
    }
2961

    
2962
    if ($plugin) {
2963
      // Write which plugin to use.
2964
      $value = $this->get_option($option);
2965
      $output .= $indent . $prefix . "['$option'] = '$value';\n";
2966

    
2967
      // Pass off to the plugin to export itself.
2968
      $output .= $plugin->export_options($indent, $prefix . "['$options_field']");
2969
    }
2970

    
2971
    return $output;
2972
  }
2973

    
2974
  /**
2975
   * Special handling for plugin export
2976
   *
2977
   * Plugins other than styles are stored in array with 'type' being the key
2978
   * to the plugin. For modern plugins, the options are stored in the 'options'
2979
   * array, but for legacy plugins (access and cache) options are stored as
2980
   * siblings to the type.
2981
   */
2982
  function export_plugin($indent, $prefix, $storage, $option, $definition, $parents) {
2983
    $output = '';
2984
    $plugin_type = end($parents);
2985
    $plugin = $this->get_plugin($plugin_type);
2986
    if ($plugin) {
2987
      // Write which plugin to use.
2988
      $value = $storage[$option];
2989
      $new_prefix = $prefix . "['$plugin_type']";
2990

    
2991
      $output .= $indent . $new_prefix . "['$option'] = '$value';\n";
2992

    
2993
      if ($plugin_type != 'access' && $plugin_type!= 'cache') {
2994
        $new_prefix .= "['options']";
2995
      }
2996

    
2997
      // Pass off to the plugin to export itself.
2998
      $output .= $plugin->export_options($indent, $new_prefix);
2999
    }
3000

    
3001
    return $output;
3002
  }
3003

    
3004
  function unpack_style($indent, $prefix, $storage, $option, $definition, $parents) {
3005
    $output = '';
3006
    $style_plugin = $this->get_plugin();
3007
    if ($option == 'style_plugin') {
3008
      $type = 'style';
3009
      $options_field = 'style_options';
3010
      $plugin = $style_plugin;
3011
    }
3012
    else {
3013
      if (!$style_plugin || !$style_plugin->uses_row_plugin()) {
3014
        return;
3015
      }
3016

    
3017
      $type = 'row';
3018
      $options_field = 'row_options';
3019
      $plugin = $this->get_plugin('row');
3020
      // If the style plugin doesn't use row plugins, don't even bother.
3021
    }
3022

    
3023
    if ($plugin) {
3024
      return $plugin->unpack_translatables($translatable, $parents);
3025
    }
3026
  }
3027

    
3028
  /**
3029
   * Special handling for plugin unpacking.
3030
   */
3031
  function unpack_plugin(&$translatable, $storage, $option, $definition, $parents) {
3032
    $plugin_type = end($parents);
3033
    $plugin = $this->get_plugin($plugin_type);
3034
    if ($plugin) {
3035
      // Write which plugin to use.
3036
      return $plugin->unpack_translatables($translatable, $parents);
3037
    }
3038
  }
3039

    
3040
    /**
3041
   * Special method to unpack items that have handlers.
3042
   *
3043
   * This method was specified in the option_definition() as the method to utilize to
3044
   * export fields, filters, sort criteria, relationships and arguments. This passes
3045
   * the export off to the individual handlers so that they can export themselves
3046
   * properly.
3047
   */
3048
  function unpack_handler(&$translatable, $storage, $option, $definition, $parents) {
3049
    $output = '';
3050

    
3051
    // cut the 's' off because the data is stored as the plural form but we need
3052
    // the singular form. Who designed that anyway? Oh yeah, I did. :(
3053
    if ($option != 'header' && $option != 'footer' && $option != 'empty') {
3054
      $type = substr($option, 0, -1);
3055
    }
3056
    else {
3057
      $type = $option;
3058
    }
3059
    $types = views_object_types();
3060
    foreach ($storage[$option] as $id => $info) {
3061
      if (!empty($types[$type]['type'])) {
3062
        $handler_type = $types[$type]['type'];
3063
      }
3064
      else {
3065
        $handler_type = $type;
3066
      }
3067
      $handler = views_get_handler($info['table'], $info['field'], $handler_type);
3068
      if ($handler) {
3069
        $handler->init($this->view, $info);
3070
        $handler->unpack_translatables($translatable, array_merge($parents, array($type, $info['table'], $info['id'])));
3071
      }
3072

    
3073
      // Prevent reference problems.
3074
      unset($handler);
3075
    }
3076

    
3077
    return $output;
3078
  }
3079

    
3080
  /**
3081
   * Provide some helpful text for the arguments.
3082
   * The result should contain of an array with
3083
   *   - filter value present: The title of the fieldset in the argument
3084
   *     where you can configure what should be done with a given argument.
3085
   *   - filter value not present: The tiel of the fieldset in the argument
3086
   *     where you can configure what should be done if the argument does not
3087
   *     exist.
3088
   *   - description: A description about how arguments comes to the display.
3089
   *     For example blocks don't get it from url.
3090
   */
3091
  function get_argument_text() {
3092
    return array(
3093
      'filter value not present' => t('When the filter value is <em>NOT</em> available'),
3094
      'filter value present' => t('When the filter value <em>IS</em> available or a default is provided'),
3095
      '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'."),
3096
    );
3097
  }
3098

    
3099
  /**
3100
   * Provide some helpful text for pagers.
3101
   *
3102
   * The result should contain of an array within
3103
   *   - items per page title
3104
   */
3105
  function get_pager_text() {
3106
    return array(
3107
      'items per page title' => t('Items to display'),
3108
      'items per page description' => t('The number of items to display. Enter 0 for no limit.')
3109
    );
3110
  }
3111
}
3112

    
3113

    
3114
/**
3115
 * @}
3116
 */