source: sipes/modules_contrib/views/plugins/views_plugin_display.inc @ a8b1f3f

stableversion-3.0
Last change on this file since a8b1f3f was 59029b2, checked in by José Gregorio Puentes <jpuentes@…>, 8 años ago

se actualizo la version del modulo views

  • Propiedad mode establecida a 100644
File size: 96.8 KB
Línea 
1<?php
2/**
3 * @file
4 * Contains the base display plugin.
5 */
6
7/**
8 * @defgroup views_display_plugins Views' display plugins
9 * @{
10 * Display plugins control how Views interact with the rest of Drupal.
11 *
12 * They can handle creating Views from a Drupal page hook; they can
13 * handle creating Views from a Drupal block hook. They can also
14 * handle creating Views from an external module source, such as
15 * a Panels pane, or an insert view, or a CCK field type.
16 *
17 * @see hook_views_plugins
18 */
19
20/**
21 * The default display plugin handler. Display plugins handle options and
22 * basic mechanisms for different output methods.
23 *
24 * @ingroup views_display_plugins
25 */
26class views_plugin_display extends views_plugin {
27  /**
28   * The top object of a view.
29   *
30   * @var view
31   */
32  var $view = NULL;
33
34  var $handlers = array();
35
36  /**
37   * Stores all availible display extenders.
38   */
39  var $extender = array();
40
41  function init(&$view, &$display, $options = NULL) {
42    $this->view = &$view;
43    $this->display = &$display;
44
45    // Make some modifications:
46    if (!isset($options)) {
47      $options = $display->display_options;
48    }
49
50    if ($this->is_default_display() && isset($options['defaults'])) {
51      unset($options['defaults']);
52    }
53
54    // Cache for unpack_options, but not if we are in the ui.
55    static $unpack_options = array();
56    if (empty($view->editing)) {
57      $cid = 'unpack_options:' . md5(serialize(array($this->options, $options)));
58      if (empty($unpack_options[$cid])) {
59        $cache = views_cache_get($cid, TRUE);
60        if (!empty($cache->data)) {
61          $this->options = $cache->data;
62        }
63        else {
64          $this->unpack_options($this->options, $options);
65          views_cache_set($cid, $this->options, TRUE);
66        }
67        $unpack_options[$cid] = $this->options;
68      }
69      else {
70        $this->options = $unpack_options[$cid];
71      }
72    }
73    else {
74      $this->unpack_options($this->options, $options);
75    }
76
77    // Translate changed settings:
78    $items_per_page = $this->get_option('items_per_page');
79    $offset = $this->get_option('offset');
80    $use_pager = $this->get_option('use_pager');
81    $pager = $this->get_option('pager');
82    // Check if the pager options were already converted.
83    // The pager settings of a Views 2.x view specifying 10 items with an
84    // offset of 0 and no pager is the same as of a Views 3.x view with
85    // default settings. In this case, the only way to determine which case we
86    // are dealing with is checking the API version but that's only available
87    // for exported Views as it's not stored in the database.
88    // If you would like to change this code, really take care that you thought
89    // of every possibility.
90    // @TODO: Provide a way to convert the database views as well.
91    if (((!empty($items_per_page) && $items_per_page != 10) || !empty($offset) || !empty($use_pager))
92      || (!empty($view->api_version) && $view->api_version == 2)) {
93      // Find out the right pager type.
94      // If the view "use pager" it's a normal/full pager.
95      if ($use_pager) {
96        $type = 'full';
97      }
98      // If it does not use pager, but 0 items per page it should not page
99      // else it should display just a certain amount of items.
100      else {
101        $type = $items_per_page ? 'some' : 'none';
102      }
103
104      // Setup the pager options.
105      $pager = array(
106        'type' => $type,
107        'options' => array(
108          'offset' => intval($offset)
109        ),
110      );
111
112      if ($items_per_page) {
113        $pager['options']['items_per_page'] = $items_per_page;
114      }
115      // Setup the pager element.
116      if ($id = $this->get_option('pager_element')) {
117        $pager['options']['id'] = $id;
118      }
119
120      // Unset the previous options
121      // After edit and save the view they will be erased
122      $this->set_option('items_per_page', NULL);
123      $this->set_option('offset', NULL);
124      $this->set_option('use_pager', NULL);
125      $this->set_option('pager', $pager);
126    }
127
128    // Plugable headers, footer and empty texts are
129    // not compatible with previous version of views
130    // This code converts old values into a configured handler for each area
131    foreach (array('header', 'footer', 'empty') as $area) {
132      $converted = FALSE;
133      if (isset($this->options[$area]) && !is_array($this->options[$area])) {
134        if (!empty($this->options[$area])) {
135          $content = $this->get_option($area);
136          if (!empty($content) && !is_array($content)) {
137            $format = $this->get_option($area . '_format');
138            $options = array(
139              'id' => 'area',
140              'table' => 'views',
141              'field' => 'area',
142              'label' => '',
143              'relationship' => 'none',
144              'group_type' => 'group',
145              'content' => $content,
146              'format' => !empty($format) ? $format : variable_get('filter_default_format', 1),
147            );
148
149            if ($area != 'empty' && $empty = $this->get_option($area . '_empty')) {
150              $options['empty'] = $empty;
151            }
152            $this->set_option($area, array('text' => $options));
153            $converted = TRUE;
154          }
155        }
156        // Ensure that options are at least an empty array
157        if (!$converted) {
158          $this->set_option($area, array());
159        }
160      }
161    }
162
163    // Convert distinct setting from display to query settings.
164    $distinct = $this->get_option('distinct');
165    if (!empty($distinct)) {
166      $query_settings = $this->get_option('query');
167      $query_settings['options']['distinct'] = $distinct;
168      $this->set_option('query', $query_settings);
169      // Clear the values
170      $this->set_option('distinct', NULL);
171    }
172
173    // Convert filter groups.
174    $filter_groups = $this->get_option('filter_groups');
175    // Only convert if it wasn't converted yet, which is the case if there is a 0 group.
176    if (isset($filter_groups['groups'][0])) {
177      // Update filter groups.
178      $filter_groups ['groups'] = views_array_key_plus($filter_groups['groups']);
179      $this->set_option('filter_groups', $filter_groups);
180      // Update the filter group on each filter.
181      $filters = $this->get_option('filters');
182      foreach ($filters as &$filter) {
183        $filter['group']++;
184      }
185      $this->set_option('filters', $filters);
186    }
187  }
188
189  function construct() {
190    // Load extenders as soon as possible
191    $this->extender = array();
192    foreach (array_filter(variable_get('views_display_extenders', array())) as $extender) {
193      $data = views_fetch_plugin_data('display_extender', $extender);
194      views_include_handler($data['handler'], $data, 'display_extender');
195      $this->extender[$extender] = new $data['handler']($this->view, $this);
196    }
197    parent::construct();
198  }
199
200  function destroy() {
201    parent::destroy();
202
203    foreach ($this->handlers as $type => $handlers) {
204      foreach ($handlers as $id => $handler) {
205        if (is_object($handler)) {
206          $this->handlers[$type][$id]->destroy();
207        }
208      }
209    }
210
211    if (isset($this->default_display)) {
212      unset($this->default_display);
213    }
214
215    foreach ($this->extender as $extender) {
216      $extender->destroy();
217    }
218  }
219
220  /**
221   * Determine if this display is the 'default' display which contains
222   * fallback settings
223   */
224  function is_default_display() { return FALSE; }
225
226  /**
227   * Determine if this display uses exposed filters, so the view
228   * will know whether or not to build them.
229   */
230  function uses_exposed() {
231    if (!isset($this->has_exposed)) {
232      foreach ($this->handlers as $type => $value) {
233        foreach ($this->view->$type as $id => $handler) {
234          if ($handler->can_expose() && $handler->is_exposed()) {
235            // one is all we need; if we find it, return true.
236            $this->has_exposed = TRUE;
237            return TRUE;
238          }
239        }
240      }
241      $pager = $this->get_plugin('pager');
242      if (isset($pager) && $pager->uses_exposed()) {
243        $this->has_exposed = TRUE;
244        return TRUE;
245      }
246      $this->has_exposed = FALSE;
247    }
248
249    return $this->has_exposed;
250  }
251
252  /**
253   * Determine if this display should display the exposed
254   * filters widgets, so the view will know whether or not
255   * to render them.
256   *
257   * Regardless of what this function
258   * returns, exposed filters will not be used nor
259   * displayed unless uses_exposed() returns TRUE.
260   */
261  function displays_exposed() {
262    return TRUE;
263  }
264
265  /**
266   * Does the display use AJAX?
267   */
268  function use_ajax() {
269    if (!empty($this->definition['use ajax'])) {
270      return $this->get_option('use_ajax');
271    }
272    return FALSE;
273  }
274
275  /**
276   * Does the display have a pager enabled?
277   */
278  function use_pager() {
279    $pager = $this->get_plugin('pager');
280    if ($pager) {
281      return $pager->use_pager();
282    }
283  }
284
285  /**
286   * Does the display have a more link enabled?
287   */
288  function use_more() {
289    if (!empty($this->definition['use more'])) {
290      return $this->get_option('use_more');
291    }
292    return FALSE;
293  }
294
295  /**
296   * Does the display have groupby enabled?
297   */
298  function use_group_by() {
299    return $this->get_option('group_by');
300  }
301
302  /**
303   * Should the enabled display more link be shown when no more items?
304   */
305  function use_more_always() {
306    if (!empty($this->definition['use more'])) {
307      return $this->get_option('use_more_always');
308    }
309    return FALSE;
310  }
311
312  /**
313   * Does the display have custom link text?
314   */
315  function use_more_text() {
316    if (!empty($this->definition['use more'])) {
317      return $this->get_option('use_more_text');
318    }
319    return FALSE;
320  }
321
322  /**
323   * Can this display accept attachments?
324   */
325  function accept_attachments() {
326    if (empty($this->definition['accept attachments'])) {
327      return FALSE;
328    }
329    if (!empty($this->view->argument) && $this->get_option('hide_attachment_summary')) {
330      foreach ($this->view->argument as $argument_id => $argument) {
331        if ($argument->needs_style_plugin() && empty($argument->argument_validated)) {
332          return FALSE;
333        }
334      }
335    }
336    return TRUE;
337  }
338
339  /**
340   * Allow displays to attach to other views.
341   */
342  function attach_to($display_id) { }
343
344  /**
345   * Static member function to list which sections are defaultable
346   * and what items each section contains.
347   */
348  function defaultable_sections($section = NULL) {
349    $sections = array(
350      'access' => array('access', 'access_options'),
351      'access_options' => array('access', 'access_options'),
352      'cache' => array('cache', 'cache_options'),
353      'cache_options' => array('cache', 'cache_options'),
354      'title' => array('title'),
355      'css_class' => array('css_class'),
356      'use_ajax' => array('use_ajax'),
357      'hide_attachment_summary' => array('hide_attachment_summary'),
358      'group_by' => array('group_by'),
359      'query' => array('query'),
360      'use_more' => array('use_more', 'use_more_always', 'use_more_text'),
361      'link_display' => array('link_display', 'link_url'),
362
363      // Force these to cascade properly.
364      'style_plugin' => array('style_plugin', 'style_options', 'row_plugin', 'row_options'),
365      'style_options' => array('style_plugin', 'style_options', 'row_plugin', 'row_options'),
366      'row_plugin' => array('style_plugin', 'style_options', 'row_plugin', 'row_options'),
367      'row_options' => array('style_plugin', 'style_options', 'row_plugin', 'row_options'),
368
369      'pager' => array('pager', 'pager_options'),
370      'pager_options' => array('pager', 'pager_options'),
371
372      'exposed_form' => array('exposed_form', 'exposed_form_options'),
373      'exposed_form_options' => array('exposed_form', 'exposed_form_options'),
374
375      // These guys are special
376      'header' => array('header'),
377      'footer' => array('footer'),
378      'empty' => array('empty'),
379      'relationships' => array('relationships'),
380      'fields' => array('fields'),
381      'sorts' => array('sorts'),
382      'arguments' => array('arguments'),
383      'filters' => array('filters', 'filter_groups'),
384    );
385
386    // If the display cannot use a pager, then we cannot default it.
387    if (empty($this->definition['use pager'])) {
388      unset($sections['pager']);
389      unset($sections['items_per_page']);
390    }
391
392    foreach ($this->extender as $extender) {
393      $extender->defaultable_sections($sections, $section);
394    }
395
396    if ($section) {
397      if (!empty($sections[$section])) {
398        return $sections[$section];
399      }
400    }
401    else {
402      return $sections;
403    }
404  }
405
406  function option_definition() {
407    $options = array(
408      'defaults' => array(
409        'default' => array(
410          'access' => TRUE,
411          'cache' => TRUE,
412          'query' => TRUE,
413          'title' => TRUE,
414          'css_class' => TRUE,
415
416          'display_description' => FALSE,
417          'use_ajax' => TRUE,
418          'hide_attachment_summary' => TRUE,
419          'pager' => TRUE,
420          'pager_options' => TRUE,
421          'use_more' => TRUE,
422          'use_more_always' => TRUE,
423          'use_more_text' => TRUE,
424          'exposed_form' => TRUE,
425          'exposed_form_options' => TRUE,
426
427          'link_display' => TRUE,
428          'link_url' => '',
429          'group_by' => TRUE,
430
431          'style_plugin' => TRUE,
432          'style_options' => TRUE,
433          'row_plugin' => TRUE,
434          'row_options' => TRUE,
435
436          'header' => TRUE,
437          'footer' => TRUE,
438          'empty' => TRUE,
439
440          'relationships' => TRUE,
441          'fields' => TRUE,
442          'sorts' => TRUE,
443          'arguments' => TRUE,
444          'filters' => TRUE,
445          'filter_groups' => TRUE,
446        ),
447        'export' => FALSE,
448      ),
449
450      'title' => array(
451        'default' => '',
452        'translatable' => TRUE,
453      ),
454      'enabled' => array(
455        'default' => TRUE,
456        'translatable' => FALSE,
457        'bool' => TRUE,
458      ),
459      'display_comment' => array(
460        'default' => '',
461      ),
462      'css_class' => array(
463        'default' => '',
464        'translatable' => FALSE,
465      ),
466      'display_description' => array(
467        'default' => '',
468        'translatable' => TRUE,
469      ),
470      'use_ajax' => array(
471        'default' => FALSE,
472        'bool' => TRUE,
473      ),
474      'hide_attachment_summary' => array(
475        'default' => FALSE,
476        'bool' => TRUE,
477      ),
478      // This is legacy code:
479      // Items_per/offset/use_pager is moved to the pager plugin
480      // but the automatic update path needs this items defined, so don't remove it.
481      // @see views_plugin_display::init()
482      'items_per_page' => array(
483        'default' => 10,
484      ),
485      'offset' => array(
486        'default' => 0,
487      ),
488      'use_pager' => array(
489        'default' => FALSE,
490        'bool' => TRUE,
491      ),
492      'use_more' => array(
493        'default' => FALSE,
494        'bool' => TRUE,
495      ),
496      'use_more_always' => array(
497        'default' => FALSE,
498        'bool' => FALSE,
499      ),
500      'use_more_text' => array(
501        'default' => 'more',
502        'translatable' => TRUE,
503      ),
504      'link_display' => array(
505        'default' => '',
506      ),
507      'link_url' => array(
508        'default' => '',
509      ),
510      'group_by' => array(
511        'default' => FALSE,
512        'bool' => TRUE,
513      ),
514
515      // These types are all plugins that can have individual settings
516      // and therefore need special handling.
517      'access' => array(
518        'contains' => array(
519          'type' => array('default' => 'none', 'export' => 'export_plugin', 'unpack_translatable' => 'unpack_plugin'),
520         ),
521      ),
522      'cache' => array(
523        'contains' => array(
524          'type' => array('default' => 'none', 'export' => 'export_plugin', 'unpack_translatable' => 'unpack_plugin'),
525         ),
526      ),
527      'query' => array(
528        'contains' => array(
529          'type' => array('default' => 'views_query', 'export' => 'export_plugin'),
530          'options' => array('default' => array(), 'export' => FALSE),
531         ),
532      ),
533      // Note that exposed_form plugin has options in a separate array,
534      // while access and cache do not. access and cache are legacy and
535      // that pattern should not be repeated, but it is left as is to
536      // reduce the need to modify older views. Let's consider the
537      // pattern used here to be the template from which future plugins
538      // should be copied.
539      'exposed_form' => array(
540        'contains' => array(
541          'type' => array('default' => 'basic', 'export' => 'export_plugin', 'unpack_translatable' => 'unpack_plugin'),
542          'options' => array('default' => array(), 'export' => FALSE),
543         ),
544      ),
545      'pager' => array(
546        'contains' => array(
547          'type' => array('default' => 'full', 'export' => 'export_plugin', 'unpack_translatable' => 'unpack_plugin'),
548          'options' => array('default' => array(), 'export' => FALSE),
549         ),
550      ),
551
552      // Note that the styles have their options completely independent.
553      // Like access and cache above, this is a legacy pattern and
554      // should not be repeated.
555      'style_plugin' => array(
556        'default' => 'default',
557        'export' => 'export_style',
558        'unpack_translatable' => 'unpack_style',
559      ),
560      'style_options' => array(
561        'default' => array(),
562        'export' => FALSE,
563      ),
564      'row_plugin' => array(
565        'default' => 'fields',
566        'export' => 'export_style',
567        'unpack_translatable' => 'unpack_style',
568      ),
569      'row_options' => array(
570        'default' => array(),
571        'export' => FALSE,
572      ),
573
574      'exposed_block' => array(
575        'default' => FALSE,
576      ),
577
578      'header' => array(
579        'default' => array(),
580        'export' => 'export_handler',
581        'unpack_translatable' => 'unpack_handler',
582      ),
583      'footer' => array(
584        'default' => array(),
585        'export' => 'export_handler',
586        'unpack_translatable' => 'unpack_handler',
587      ),
588      'empty' => array(
589        'default' => array(),
590        'export' => 'export_handler',
591        'unpack_translatable' => 'unpack_handler',
592      ),
593
594      // We want these to export last.
595      // These are the 5 handler types.
596      'relationships' => array(
597        'default' => array(),
598        'export' => 'export_handler',
599        'unpack_translatable' => 'unpack_handler',
600
601      ),
602      'fields' => array(
603        'default' => array(),
604        'export' => 'export_handler',
605        'unpack_translatable' => 'unpack_handler',
606      ),
607      'sorts' => array(
608        'default' => array(),
609        'export' => 'export_handler',
610        'unpack_translatable' => 'unpack_handler',
611      ),
612      'arguments' => array(
613        'default' => array(),
614        'export' => 'export_handler',
615        'unpack_translatable' => 'unpack_handler',
616      ),
617      'filter_groups' => array(
618        'contains' => array(
619          'operator' => array('default' => 'AND'),
620          'groups' => array('default' => array(1 => 'AND')),
621        ),
622      ),
623      'filters' => array(
624        'default' => array(),
625        'export' => 'export_handler',
626        'unpack_translatable' => 'unpack_handler',
627      ),
628    );
629
630    if (empty($this->definition['use pager'])) {
631      $options['defaults']['default']['use_pager'] = FALSE;
632      $options['defaults']['default']['items_per_page'] = FALSE;
633      $options['defaults']['default']['offset'] = FALSE;
634      $options['defaults']['default']['pager'] = FALSE;
635      $options['pager']['contains']['type']['default'] = 'some';
636    }
637
638    if ($this->is_default_display()) {
639      unset($options['defaults']);
640    }
641
642    foreach ($this->extender as $extender) {
643      $extender->option_definition($options);
644    }
645
646    return $options;
647  }
648
649  /**
650   * Check to see if the display has a 'path' field.
651   *
652   * This is a pure function and not just a setting on the definition
653   * because some displays (such as a panel pane) may have a path based
654   * upon configuration.
655   *
656   * By default, displays do not have a path.
657   */
658  function has_path() { return FALSE; }
659
660  /**
661   * Check to see if the display has some need to link to another display.
662   *
663   * For the most part, displays without a path will use a link display. However,
664   * sometimes displays that have a path might also need to link to another display.
665   * This is true for feeds.
666   */
667  function uses_link_display() { return !$this->has_path(); }
668
669  /**
670   * Check to see if the display can put the exposed formin a block.
671   *
672   * By default, displays that do not have a path cannot disconnect
673   * the exposed form and put it in a block, because the form has no
674   * place to go and Views really wants the forms to go to a specific
675   * page.
676   */
677  function uses_exposed_form_in_block() { return $this->has_path(); }
678
679  /**
680   * Check to see which display to use when creating links within
681   * a view using this display.
682   */
683  function get_link_display() {
684    $display_id = $this->get_option('link_display');
685    // If unknown, pick the first one.
686    if (empty($display_id) || empty($this->view->display[$display_id])) {
687      foreach ($this->view->display as $display_id => $display) {
688        if (!empty($display->handler) && $display->handler->has_path()) {
689          return $display_id;
690        }
691      }
692    }
693    else {
694      return $display_id;
695    }
696    // fall-through returns NULL
697  }
698
699  /**
700   * Return the base path to use for this display.
701   *
702   * This can be overridden for displays that do strange things
703   * with the path.
704   */
705  function get_path() {
706    if ($this->has_path()) {
707      return $this->get_option('path');
708    }
709
710    $display_id = $this->get_link_display();
711    if ($display_id && !empty($this->view->display[$display_id]) && is_object($this->view->display[$display_id]->handler)) {
712      return $this->view->display[$display_id]->handler->get_path();
713    }
714  }
715
716  /**
717   * Check to see if the display needs a breadcrumb
718   *
719   * By default, displays do not need breadcrumbs
720   */
721  function uses_breadcrumb() { return FALSE; }
722
723  /**
724   * Determine if a given option is set to use the default display or the
725   * current display
726   *
727   * @return
728   *   TRUE for the default display
729   */
730  function is_defaulted($option) {
731    return !$this->is_default_display() && !empty($this->default_display) && !empty($this->options['defaults'][$option]);
732  }
733
734  /**
735   * Intelligently get an option either from this display or from the
736   * default display, if directed to do so.
737   */
738  function get_option($option) {
739    if ($this->is_defaulted($option)) {
740      return $this->default_display->get_option($option);
741    }
742
743    if (array_key_exists($option, $this->options)) {
744      return $this->options[$option];
745    }
746  }
747
748  /**
749   * Determine if the display's style uses fields.
750   */
751  function uses_fields() {
752    $plugin = $this->get_plugin();
753    if ($plugin) {
754      return $plugin->uses_fields();
755    }
756  }
757
758  /**
759   * Get the instance of a plugin, for example style or row.
760   *
761   * @param string $type
762   *   The type of the plugin.
763   * @param string $name
764   *   The name of the plugin defined in hook_views_plugins.
765   *
766   * @return views_plugin|FALSE
767   */
768  function get_plugin($type = 'style', $name = NULL) {
769    static $cache = array();
770    if (!isset($cache[$type][$name])) {
771      switch ($type) {
772        case 'style':
773        case 'row':
774          $option_name = $type . '_plugin';
775          $options = $this->get_option($type . '_options');
776          if (!$name) {
777            $name = $this->get_option($option_name);
778          }
779
780          break;
781        case 'query':
782          $views_data = views_fetch_data($this->view->base_table);
783          $name = !empty($views_data['table']['base']['query class']) ? $views_data['table']['base']['query class'] : 'views_query';
784        default:
785          $option_name = $type;
786          $options = $this->get_option($type);
787          if (!$name) {
788            $name = $options['type'];
789          }
790
791          // access & cache store their options as siblings with the
792          // type; all others use an 'options' array.
793          if ($type != 'access' && $type != 'cache') {
794            $options = $options['options'];
795          }
796      }
797      $plugin = views_get_plugin($type, $name);
798
799      if (!$plugin) {
800        return;
801      }
802      if ($type != 'query') {
803        $plugin->init($this->view, $this->display, $options);
804      }
805      else {
806        $display_id = $this->is_defaulted($option_name) ? $this->display->id : 'default';
807        $plugin->localization_keys = array($display_id, $type);
808
809        if (!isset($this->base_field)) {
810          $views_data = views_fetch_data($this->view->base_table);
811          $this->view->base_field = $views_data['table']['base']['field'];
812        }
813        $plugin->init($this->view->base_table, $this->view->base_field, $options);
814      }
815      $cache[$type][$name] = $plugin;
816    }
817
818    return $cache[$type][$name];
819  }
820
821  /**
822   * Get the handler object for a single handler.
823   */
824  function &get_handler($type, $id) {
825    if (!isset($this->handlers[$type])) {
826      $this->get_handlers($type);
827    }
828
829    if (isset($this->handlers[$type][$id])) {
830      return $this->handlers[$type][$id];
831    }
832
833    // So we can return a reference.
834    $null = NULL;
835    return $null;
836  }
837
838  /**
839   * Get a full array of handlers for $type. This caches them.
840   */
841  function get_handlers($type) {
842    if (!isset($this->handlers[$type])) {
843      $this->handlers[$type] = array();
844      $types = views_object_types();
845      $plural = $types[$type]['plural'];
846      foreach ($this->get_option($plural) as $id => $info) {
847        if ($info['id'] != $id) {
848          $info['id'] = $id;
849        }
850
851        // If aggregation is on, the group type might override the actual
852        // handler that is in use. This piece of code checks that and,
853        // if necessary, sets the override handler.
854        $override = NULL;
855        if ($this->use_group_by() && !empty($info['group_type'])) {
856          if (empty($this->view->query)) {
857            $this->view->init_query();
858          }
859          $aggregate = $this->view->query->get_aggregation_info();
860          if (!empty($aggregate[$info['group_type']]['handler'][$type])) {
861            $override = $aggregate[$info['group_type']]['handler'][$type];
862          }
863        }
864
865        if (!empty($types[$type]['type'])) {
866          $handler_type = $types[$type]['type'];
867        }
868        else {
869          $handler_type = $type;
870        }
871
872        $handler = views_get_handler($info['table'], $info['field'], $handler_type, $override);
873        if ($handler) {
874          // Special override for area types so they know where they come from.
875          if ($handler_type == 'area') {
876            $handler->handler_type = $type;
877          }
878
879          $handler->init($this->view, $info);
880          $this->handlers[$type][$id] = &$handler;
881        }
882
883        // Prevent reference problems.
884        unset($handler);
885      }
886    }
887
888    return $this->handlers[$type];
889  }
890
891  /**
892   * Retrieve a list of fields for the current display with the
893   *  relationship associated if it exists.
894   */
895  function get_field_labels() {
896    $options = array();
897    foreach ($this->get_handlers('relationship') as $relationship => $handler) {
898      if ($label = $handler->label()) {
899        $relationships[$relationship] = $label;
900      }
901      else {
902        $relationships[$relationship] = $handler->ui_name();
903      }
904    }
905
906    foreach ($this->get_handlers('field') as $id => $handler) {
907      if ($label = $handler->label()) {
908        $options[$id] = $label;
909      }
910      else {
911        $options[$id] = $handler->ui_name();
912      }
913      if (!empty($handler->options['relationship']) && !empty($relationships[$handler->options['relationship']])) {
914        $options[$id] = '(' . $relationships[$handler->options['relationship']] . ') ' . $options[$id];
915      }
916    }
917    return $options;
918  }
919
920  /**
921   * Returns to tokens for arguments.
922   *
923   * This function is similar to views_handler_field::get_render_tokens()
924   * but without fields tokens.
925   */
926  function get_arguments_tokens() {
927    $tokens = array();
928    if (!empty($this->view->build_info['substitutions'])) {
929      $tokens = $this->view->build_info['substitutions'];
930    }
931    $count = 0;
932    foreach ($this->view->display_handler->get_handlers('argument') as $arg => $handler) {
933      $token = '%' . ++$count;
934      if (!isset($tokens[$token])) {
935        $tokens[$token] = '';
936      }
937
938      // Use strip tags as there should never be HTML in the path.
939      // However, we need to preserve special characters like " that
940      // were removed by check_plain().
941      $tokens['!' . $count] = isset($this->view->args[$count - 1]) ? strip_tags(html_entity_decode($this->view->args[$count - 1])) : '';
942    }
943
944    return $tokens;
945  }
946
947  /**
948   * Intelligently set an option either from this display or from the
949   * default display, if directed to do so.
950   */
951  function set_option($option, $value) {
952    if ($this->is_defaulted($option)) {
953      return $this->default_display->set_option($option, $value);
954    }
955
956    // Set this in two places: On the handler where we'll notice it
957    // but also on the display object so it gets saved. This should
958    // only be a temporary fix.
959    $this->display->display_options[$option] = $value;
960    return $this->options[$option] = $value;
961  }
962
963  /**
964   * Set an option and force it to be an override.
965   */
966  function override_option($option, $value) {
967    $this->set_override($option, FALSE);
968    $this->set_option($option, $value);
969  }
970
971  /**
972   * Because forms may be split up into sections, this provides
973   * an easy URL to exactly the right section. Don't override this.
974   */
975  function option_link($text, $section, $class = '', $title = '') {
976    if (!empty($class)) {
977      $text = '<span>' . $text . '</span>';
978    }
979
980    if (!trim($text)) {
981      $text = t('Broken field');
982    }
983
984    if (empty($title)) {
985      $title = $text;
986    }
987
988    return l($text, 'admin/build/views/nojs/display/' . $this->view->name . '/' . $this->display->id . '/' . $section, array('attributes' => array('class' => 'views-ajax-link ' . $class, 'title' => $title), 'html' => TRUE));
989  }
990
991  /**
992   * Provide the default summary for options in the views UI.
993   *
994   * This output is returned as an array.
995   */
996  function options_summary(&$categories, &$options) {
997    $categories = array(
998      'basic' => array(
999        'title' => t('Basic settings'),
1000      ),
1001      'advanced' => array(
1002        'title' => t('Advanced settings'),
1003      ),
1004      'style' => array(
1005        'title' => t('Style settings'),
1006      ),
1007      'exposed' => array(
1008        'title' => t('Exposed form'),
1009      ),
1010    );
1011
1012    if ($this->display->id != 'default') {
1013      $options['display_id'] = array(
1014        'category' => 'basic',
1015        'title' => t('Machine Name'),
1016        'value' => !empty($this->display->new_id) ? check_plain($this->display->new_id) : check_plain($this->display->id),
1017        'desc' => t('Change the machine name of this display.'),
1018      );
1019    }
1020
1021    $options['display_title'] = array(
1022      'category' => 'basic',
1023      'title' => t('Name'),
1024      'value' => check_plain($this->display->display_title),
1025      'desc' => t('Change the name of this display.'),
1026    );
1027
1028    $display_comment = check_plain(drupal_substr($this->get_option('display_comment'), 0, 10));
1029    $options['display_comment'] = array(
1030      'category' => 'basic',
1031      'title' => t('Comment'),
1032      'value' => !empty($display_comment) ? $display_comment : t('No comment'),
1033      'desc' => t('Comment or document this display.'),
1034    );
1035
1036    $title = strip_tags($this->get_option('title'));
1037    if (!$title) {
1038      $title = t('None');
1039    }
1040
1041    $options['title'] = array(
1042      'category' => 'basic',
1043      'title' => t('Title'),
1044      'value' => $title,
1045      'desc' => t('Change the title that this display will use.'),
1046    );
1047
1048    $options['enabled'] = array(
1049      'category' => 'basic',
1050      'title' => t('Display status'),
1051      'value' => $this->get_option('enabled') ? t('Enabled') : t('Disabled'),
1052      'desc' => t('Define if this display is or is not enabled.'),
1053    );
1054
1055    $style_plugin = views_fetch_plugin_data('style', $this->get_option('style_plugin'));
1056    $style_title = empty($style_plugin['title']) ? t('Missing style plugin') : $style_plugin['title'];
1057
1058    $style = '';
1059
1060    $options['style_plugin'] = array(
1061      'category' => 'style',
1062      'title' => t('Style'),
1063      'value' => $style_title,
1064      'desc' => t('Change the style plugin.'),
1065    );
1066
1067    // This adds a 'Settings' link to the style_options setting if the style has options.
1068    if (!empty($style_plugin['uses options'])) {
1069      $options['style_plugin']['links']['style_options'] = t('Change settings for this style');
1070    }
1071
1072    if (!empty($style_plugin['uses row plugin'])) {
1073      $row_plugin = views_fetch_plugin_data('row', $this->get_option('row_plugin'));
1074      $row_title = empty($row_plugin['title']) ? t('Missing style plugin') : $row_plugin['title'];
1075
1076      $options['row_plugin'] = array(
1077        'category' => 'style',
1078        'title' => t('Row style'),
1079        'value' => $row_title,
1080        'desc' => t('Change the row plugin.'),
1081      );
1082      // This adds a 'Settings' link to the row_options setting if the row style has options.
1083      if (!empty($row_plugin['uses options'])) {
1084        $options['row_plugin']['links']['row_options'] = t('Change settings for this style');
1085      }
1086    }
1087    if (!empty($this->definition['use ajax'])) {
1088      $options['use_ajax'] = array(
1089        'category' => 'advanced',
1090        'title' => t('Use AJAX'),
1091        'value' => $this->get_option('use_ajax') ? t('Yes') : t('No'),
1092        'desc' => t('Change whether or not this display will use AJAX.'),
1093      );
1094    }
1095    if (!empty($this->definition['accept attachments'])) {
1096      $options['hide_attachment_summary'] = array(
1097        'category' => 'advanced',
1098        'title' => t('Hide attachments in summary'),
1099        'value' => $this->get_option('hide_attachment_summary') ? t('Yes') : t('No'),
1100        'desc' => t('Change whether or not to display attachments when displaying an argument summary.'),
1101      );
1102    }
1103
1104    $pager_plugin = $this->get_plugin('pager');
1105    if (!$pager_plugin) {
1106      // default to the no pager plugin.
1107      $pager_plugin = views_get_plugin('pager', 'none');
1108    }
1109
1110    $pager_str = $pager_plugin->summary_title();
1111
1112    $options['pager'] = array(
1113      'category' => 'basic',
1114      'title' => t('Use pager'),
1115      'value' => $pager_str,
1116      'desc' => t("Change this display's pager setting."),
1117    );
1118
1119    // If pagers aren't allowed, change the text of the item:
1120    if (empty($this->definition['use pager'])) {
1121      $options['pager']['title'] = t('Items to display');
1122    }
1123
1124    if (!empty($pager_plugin->definition['uses options'])) {
1125      $options['pager']['links']['pager_options'] = t('Change settings for this pager type.');
1126    }
1127
1128    if (!empty($this->definition['use more'])) {
1129      $options['use_more'] = array(
1130        'category' => 'basic',
1131        'title' => t('More link'),
1132        'value' => $this->get_option('use_more') ? t('Yes') : t('No'),
1133        'desc' => t('Specify whether this display will provide a "more" link.'),
1134      );
1135    }
1136
1137    $this->view->init_query();
1138    if ($this->view->query->get_aggregation_info()) {
1139      $options['group_by'] = array(
1140        'category' => 'advanced',
1141        'title' => t('Use grouping'),
1142        'value' => $this->get_option('group_by') ? t('Yes') : t('No'),
1143        'desc' => t('Allow grouping and aggregation (calculation) of fields.'),
1144      );
1145    }
1146
1147    $options['query'] = array(
1148      'category' => 'advanced',
1149      'title' => t('Query settings'),
1150      'value' => t('Settings'),
1151      'desc' => t('Allow to set some advanced settings for the query plugin'),
1152    );
1153
1154    $access_plugin = $this->get_plugin('access');
1155    if (!$access_plugin) {
1156      // default to the no access control plugin.
1157      $access_plugin = views_get_plugin('access', 'none');
1158    }
1159
1160    $access_str = $access_plugin->summary_title();
1161
1162    $options['access'] = array(
1163      'category' => 'basic',
1164      'title' => t('Access'),
1165      'value' => $access_str,
1166      'desc' => t('Specify access control type for this display.'),
1167    );
1168
1169    if (!empty($access_plugin->definition['uses options'])) {
1170      $options['access']['links']['access_options'] = t('Change settings for this access type.');
1171    }
1172
1173    $cache_plugin = $this->get_plugin('cache');
1174    if (!$cache_plugin) {
1175      // default to the no cache control plugin.
1176      $cache_plugin = views_get_plugin('cache', 'none');
1177    }
1178
1179    $cache_str = $cache_plugin->summary_title();
1180
1181    $options['cache'] = array(
1182      'category' => 'advanced',
1183      'title' => t('Caching'),
1184      'value' => $cache_str,
1185      'desc' => t('Specify caching type for this display.'),
1186    );
1187
1188    if (!empty($cache_plugin->definition['uses options'])) {
1189      $options['cache']['links']['cache_options'] = t('Change settings for this caching type.');
1190    }
1191
1192    if (!empty($access_plugin->definition['uses options'])) {
1193      $options['access']['links']['access_options'] = t('Change settings for this access type.');
1194    }
1195
1196    if ($this->uses_link_display()) {
1197      $display_id = $this->get_link_display();
1198      $link_display = empty($this->view->display[$display_id]) ? t('None') : check_plain($this->view->display[$display_id]->display_title);
1199      $link_display =  $this->get_option('link_display') == 'custom_url' ? t('Custom URL') : $link_display;
1200      $options['link_display'] = array(
1201        'category' => 'basic',
1202        'title' => t('Link display'),
1203        'value' => $link_display,
1204        'desc' => t('Specify which display or custom url this display will link to.'),
1205      );
1206    }
1207
1208    if ($this->uses_exposed_form_in_block()) {
1209      $options['exposed_block'] = array(
1210        'category' => 'exposed',
1211        'title' => t('Exposed form in block'),
1212        'value' => $this->get_option('exposed_block') ? t('Yes') : t('No'),
1213        'desc' => t('Allow the exposed form to appear in a block instead of the view.'),
1214      );
1215    }
1216
1217    $exposed_form_plugin = $this->get_plugin('exposed_form');
1218    if (!$exposed_form_plugin) {
1219      // default to the no cache control plugin.
1220      $exposed_form_plugin = views_get_plugin('exposed_form', 'basic');
1221    }
1222
1223    $exposed_form_str = $exposed_form_plugin->summary_title();
1224
1225    $options['exposed_form'] = array(
1226      'category' => 'exposed',
1227      'title' => t('Exposed form style'),
1228      'value' => $exposed_form_str,
1229      'desc' => t('Select the kind of exposed filter to use.'),
1230    );
1231
1232    if (!empty($exposed_form_plugin->definition['uses options'])) {
1233      $options['exposed_form']['links']['exposed_form_options'] = t('Exposed form settings for this exposed form style.');
1234    }
1235
1236    $css_class = check_plain(trim($this->get_option('css_class')));
1237    if (!$css_class) {
1238      $css_class = t('None');
1239    }
1240
1241    $options['css_class'] = array(
1242      'category' => 'style',
1243      'title' => t('CSS class'),
1244      'value' => $css_class,
1245      'desc' => t('Change the CSS class name(s) that will be added to this display.'),
1246    );
1247
1248    $options['analyze-theme'] = array(
1249      'category' => 'style',
1250      'title' => t('Theme'),
1251      'value' => t('Information'),
1252      'desc' => t('Get information on how to theme this display'),
1253    );
1254
1255    foreach ($this->extender as $extender) {
1256      $extender->options_summary($categories, $options);
1257    }
1258  }
1259
1260  /**
1261   * Provide the default form for setting options.
1262   */
1263  function options_form(&$form, &$form_state) {
1264    if ($this->defaultable_sections($form_state['section'])) {
1265      $this->add_override_button($form, $form_state, $form_state['section']);
1266    }
1267    $form['#title'] = check_plain($this->display->display_title) . ': ';
1268
1269    // Set the 'section' to hilite on the form.
1270    // If it's the item we're looking at is pulling from the default display,
1271    // reflect that. Don't use is_defaulted since we want it to show up even
1272    // on the default display.
1273    if (!empty($this->options['defaults'][$form_state['section']])) {
1274      $form['#section'] = 'default-' . $form_state['section'];
1275    }
1276    else {
1277      $form['#section'] = $this->display->id . '-' . $form_state['section'];
1278    }
1279
1280    switch ($form_state['section']) {
1281      case 'display_id':
1282        $form['#title'] .= t('The machine name of this display');
1283        $form['display_id'] = array(
1284          '#type' => 'textfield',
1285          '#description' => t('This is machine name of the display. It should contain only letters, numbers, and underscores. This name must be unique.'),
1286          '#default_value' => !empty($this->display->new_id) ? $this->display->new_id : $this->display->id,
1287          '#required' => TRUE,
1288          '#size' => 64,
1289        );
1290        break;
1291      case 'display_title':
1292        $form['#title'] .= t('The name and the description of this display');
1293        $form['display_title'] = array(
1294          '#title' => t('Name'),
1295          '#type' => 'textfield',
1296          '#description' => t('This name will appear only in the administrative interface for the View.'),
1297          '#default_value' => $this->display->display_title,
1298        );
1299        $form['display_description'] = array(
1300          '#title' => t('Description'),
1301          '#type' => 'textfield',
1302          '#description' => t('This description will appear only in the administrative interface for the View.'),
1303          '#default_value' => $this->get_option('display_description'),
1304        );
1305        break;
1306      case 'display_comment':
1307        $form['#title'] .= t("This display's comments");
1308        $form['display_comment'] = array(
1309          '#type' => 'textarea',
1310          '#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.'),
1311          '#default_value' => $this->get_option('display_comment'),
1312        );
1313        break;
1314      case 'title':
1315        $form['#title'] .= t('The title of this view');
1316        $form['title'] = array(
1317          '#type' => 'textfield',
1318          '#description' => t('This title will be displayed with the view, wherever titles are normally displayed; i.e, as the page title, block title, etc.'),
1319          '#default_value' => $this->get_option('title'),
1320        );
1321        break;
1322      case 'enabled':
1323        $form['#title'] .= t('Status of this display');
1324        $form['enabled'] = array(
1325          '#type' => 'checkbox',
1326          '#title' => t('Enable display'),
1327          '#description' => t('If unchecked, this display will not be available in the site.'),
1328          '#default_value' => $this->get_option('enabled'),
1329        );
1330        break;
1331      case 'css_class':
1332        $form['#title'] .= t('CSS class');
1333        $form['css_class'] = array(
1334          '#type' => 'textfield',
1335          '#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.'),
1336          '#default_value' => $this->get_option('css_class'),
1337        );
1338        break;
1339      case 'use_ajax':
1340        $form['#title'] .= t('Use AJAX when available to load this view');
1341        $form['description'] = array(
1342          '#prefix' => '<div class="description form-item">',
1343          '#suffix' => '</div>',
1344          '#value' => 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.'),
1345        );
1346        $form['use_ajax'] = array(
1347          '#type' => 'radios',
1348          '#options' => array(1 => t('Yes'), 0 => t('No')),
1349          '#default_value' => $this->get_option('use_ajax') ? 1 : 0,
1350        );
1351        break;
1352      case 'hide_attachment_summary':
1353        $form['#title'] .= t('Hide attachments when displaying argument summary');
1354        $form['hide_attachment_summary'] = array(
1355          '#type' => 'radios',
1356          '#options' => array(1 => t('Yes'), 0 => t('No')),
1357          '#default_value' => $this->get_option('hide_attachment_summary') ? 1 : 0,
1358        );
1359        break;
1360      case 'use_more':
1361        $form['#title'] .= t('Add a more link to the bottom of the display.');
1362        $form['use_more'] = array(
1363          '#type' => 'checkbox',
1364          '#title' => t('Create more link'),
1365          '#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' above. You can override the url at the link display setting."),
1366          '#default_value' => $this->get_option('use_more'),
1367        );
1368        $form['use_more_always'] = array(
1369          '#type' => 'checkbox',
1370          '#title' => t("Display 'more' link only if there is more content"),
1371          '#description' => t("Leave this unchecked to display the 'more' link even if there are no more items to display."),
1372          '#default_value' => !$this->get_option('use_more_always'),
1373            '#dependency' => array(
1374              'edit-use-more' => array(TRUE),
1375            ),
1376          '#process' => array('views_process_dependency'),
1377        );
1378        $form['use_more_text'] = array(
1379          '#type' => 'textfield',
1380          '#title' => t('More link text'),
1381          '#description' => t("The text to display for the more link."),
1382          '#default_value' => $this->get_option('use_more_text'),
1383          '#dependency' => array(
1384            'edit-use-more' => array(TRUE),
1385          ),
1386          '#process' => array('views_process_dependency'),
1387        );
1388        break;
1389      case 'group_by':
1390        $form['#title'] .= t('Allow grouping and aggregation (calculation) of fields.');
1391        $form['group_by'] = array(
1392          '#type' => 'checkbox',
1393          '#title' => t('Group by'),
1394          '#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.'),
1395          '#default_value' => $this->get_option('group_by'),
1396        );
1397        break;
1398      case 'access':
1399        $form['#title'] .= t('Access restrictions');
1400        $form['access'] = array(
1401          '#prefix' => '<div class="clear-block">',
1402          '#suffix' => '</div>',
1403          '#tree' => TRUE,
1404        );
1405
1406        $access = $this->get_option('access');
1407        $form['access']['type'] =  array(
1408          '#type' => 'radios',
1409          '#options' => views_fetch_plugin_names('access'),
1410          '#default_value' => $access['type'],
1411        );
1412
1413        $access_plugin = views_fetch_plugin_data('access', $access['type']);
1414        if (!empty($access_plugin['uses options'])) {
1415          $form['markup'] = array(
1416            '#prefix' => '<div class="form-item description">',
1417            '#suffix' => '</div>',
1418            '#value' => t('You may also adjust the !settings for the currently selected access restriction by clicking on the icon.', array('!settings' => $this->option_link(t('settings'), 'access_options'))),
1419          );
1420        }
1421
1422        break;
1423      case 'access_options':
1424        $access = $this->get_option('access');
1425        $plugin = $this->get_plugin('access');
1426        $form['#title'] .= t('Access options');
1427        if ($plugin) {
1428          $form['#help_topic'] = $plugin->definition['help topic'];
1429          $form['#help_module'] = $plugin->definition['module'];
1430
1431          $form['access_options'] = array(
1432            '#tree' => TRUE,
1433          );
1434          $form['access_options']['type'] = array(
1435            '#type' => 'value',
1436            '#value' => $access['type'],
1437          );
1438          $plugin->options_form($form['access_options'], $form_state);
1439        }
1440        break;
1441      case 'cache':
1442        $form['#title'] .= t('Caching');
1443        $form['cache'] = array(
1444          '#prefix' => '<div class="clear-block">',
1445          '#suffix' => '</div>',
1446          '#tree' => TRUE,
1447        );
1448
1449        $cache = $this->get_option('cache');
1450        $form['cache']['type'] =  array(
1451          '#type' => 'radios',
1452          '#options' => views_fetch_plugin_names('cache'),
1453          '#default_value' => $cache['type'],
1454        );
1455
1456        $cache_plugin = views_fetch_plugin_data('cache', $cache['type']);
1457        if (!empty($cache_plugin['uses options'])) {
1458          $form['markup'] = array(
1459            '#prefix' => '<div class="form-item description">',
1460            '#suffix' => '</div>',
1461            '#value' => t('You may also adjust the !settings for the currently selected cache mechanism by clicking on the icon.', array('!settings' => $this->option_link(t('settings'), 'cache_options'))),
1462          );
1463        }
1464        break;
1465      case 'cache_options':
1466        $cache = $this->get_option('cache');
1467        $plugin = $this->get_plugin('cache');
1468        $form['#title'] .= t('Caching options');
1469        if ($plugin) {
1470          $form['#help_topic'] = $plugin->definition['help topic'];
1471          $form['#help_module'] = $plugin->definition['module'];
1472
1473          $form['cache_options'] = array(
1474            '#tree' => TRUE,
1475          );
1476          $form['cache_options']['type'] = array(
1477            '#type' => 'value',
1478            '#value' => $cache['type'],
1479          );
1480          $plugin->options_form($form['cache_options'], $form_state);
1481        }
1482        break;
1483      case 'query':
1484        $query_options = $this->get_option('query');
1485        $plugin_name = $query_options['type'];
1486
1487        $form['#title'] .= t('Query options');
1488        $this->view->init_query();
1489        if ($this->view->query) {
1490          if (isset($this->view->query->definition['help topic'])) {
1491            $form['#help_topic'] = $this->view->query->definition['help topic'];
1492          }
1493
1494          if (isset($this->view->query->definition['module'])) {
1495            $form['#help_module'] = $this->view->query->definition['module'];
1496          }
1497
1498          $form['query'] = array(
1499            '#tree' => TRUE,
1500            'type' => array(
1501              '#type' => 'value',
1502              '#value' => $plugin_name,
1503            ),
1504            'options' => array(
1505              '#tree' => TRUE,
1506            ),
1507          );
1508
1509          $this->view->query->options_form($form['query']['options'], $form_state);
1510        }
1511        break;
1512      case 'style_plugin':
1513        $form['#title'] .= t('How should this view be styled');
1514        $form['#help_topic'] = 'style';
1515        $form['style_plugin'] =  array(
1516          '#type' => 'radios',
1517          '#options' => views_fetch_plugin_names('style', $this->get_style_type(), array($this->view->base_table)),
1518          '#default_value' => $this->get_option('style_plugin'),
1519          '#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.'),
1520        );
1521
1522        $style_plugin = views_fetch_plugin_data('style', $this->get_option('style_plugin'));
1523        if (!empty($style_plugin['uses options'])) {
1524          $form['markup'] = array(
1525            '#prefix' => '<div class="form-item description">',
1526            '#suffix' => '</div>',
1527            '#value' => t('You may also adjust the !settings for the currently selected style by clicking on the icon.', array('!settings' => $this->option_link(t('settings'), 'style_options'))),
1528          );
1529        }
1530
1531        break;
1532      case 'style_options':
1533        $form['#title'] .= t('Style options');
1534        $style = TRUE;
1535        $type = 'style_plugin';
1536        $name = $this->get_option('style_plugin');
1537
1538      case 'row_options':
1539        if (!isset($name)) {
1540          $name = $this->get_option('row_plugin');
1541        }
1542        // if row, $style will be empty.
1543        if (empty($style)) {
1544          $form['#title'] .= t('Row style options');
1545          $type = 'row_plugin';
1546        }
1547        $plugin = $this->get_plugin(empty($style) ? 'row' : 'style');
1548        if ($plugin) {
1549          if (isset($plugin->definition['help topic'])) {
1550            $form['#help_topic'] = $plugin->definition['help topic'];
1551            $form['#help_module'] = $plugin->definition['module'];
1552          }
1553          $form[$form_state['section']] = array(
1554            '#tree' => TRUE,
1555          );
1556          $plugin->options_form($form[$form_state['section']], $form_state);
1557        }
1558        break;
1559      case 'row_plugin':
1560        $form['#title'] .= t('How should each row in this view be styled');
1561        $form['#help_topic'] = 'style-row';
1562        $form['row_plugin'] =  array(
1563          '#type' => 'radios',
1564          '#options' => views_fetch_plugin_names('row', $this->get_style_type(), array($this->view->base_table)),
1565          '#default_value' => $this->get_option('row_plugin'),
1566        );
1567
1568        $row_plugin = views_fetch_plugin_data('row', $this->get_option('row_plugin'));
1569        if (!empty($row_plugin['uses options'])) {
1570          $form['markup'] = array(
1571            '#prefix' => '<div class="form-item description">',
1572            '#suffix' => '</div>',
1573            '#value' => t('You may also adjust the !settings for the currently selected row style by clicking on the icon.', array('!settings' => $this->option_link(t('settings'), 'row_options'))),
1574          );
1575        }
1576
1577        break;
1578      case 'link_display':
1579        $form['#title'] .= t('Which display to use for path');
1580        foreach ($this->view->display as $display_id => $display) {
1581          if ($display->handler->has_path()) {
1582            $options[$display_id] = $display->display_title;
1583          }
1584        }
1585        $options['custom_url'] = t('Custom URL');
1586        if (count($options)) {
1587          $form['link_display'] = array(
1588            '#type' => 'radios',
1589            '#options' => $options,
1590            '#description' => t("Which display to use to get this display's path for things like summary links, rss feed links, more links, etc."),
1591            '#default_value' => $this->get_option('link_display'),
1592          );
1593        }
1594        $options = array();
1595        $count = 0; // This lets us prepare the key as we want it printed.
1596        foreach ($this->view->display_handler->get_handlers('argument') as $arg => $handler) {
1597          $options[t('Arguments')]['%' . ++$count] = t('@argument title', array('@argument' => $handler->ui_name()));
1598          $options[t('Arguments')]['!' . $count] = t('@argument input', array('@argument' => $handler->ui_name()));
1599        }
1600
1601        // Default text.
1602        // We have some options, so make a list.
1603        $output = '';
1604        if (!empty($options)) {
1605          $output = t('<p>The following tokens are available for this link.</p>');
1606          foreach (array_keys($options) as $type) {
1607            if (!empty($options[$type])) {
1608              $items = array();
1609              foreach ($options[$type] as $key => $value) {
1610                $items[] = $key . ' == ' . $value;
1611              }
1612              $output .= theme('item_list', $items, $type);
1613            }
1614          }
1615        }
1616
1617        $form['link_url'] = array(
1618          '#type' => 'textfield',
1619          '#title' => t('Custom URL'),
1620          '#default_value' => $this->get_option('link_url'),
1621          '#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,
1622          '#process' => array('views_process_dependency'),
1623          '#dependency' => array('radio:link_display' => array('custom_url')),
1624        );
1625        break;
1626      case 'analyze-theme':
1627        $form['#title'] .= t('Theming information');
1628        $form['#help_topic'] = 'analyze-theme';
1629
1630        if (isset($_POST['theme'])) {
1631          $this->view->theme = $_POST['theme'];
1632        }
1633        else if (empty($this->view->theme)) {
1634          $this->view->theme = variable_get('theme_default', 'garland');
1635        }
1636
1637        global $custom_theme;
1638        $custom_theme = $this->view->theme;
1639        init_theme();
1640
1641        $funcs = array();
1642        // Get theme functions for the display. Note that some displays may
1643        // not have themes. The 'feed' display, for example, completely
1644        // delegates to the style.
1645        if (!empty($this->definition['theme'])) {
1646          $funcs[] = $this->option_link(t('Display output'), 'analyze-theme-display') . ': '  . $this->format_themes($this->theme_functions());
1647          $themes = $this->additional_theme_functions();
1648          if ($themes) {
1649            foreach ($themes as $theme) {
1650              $funcs[] = $this->option_link(t('Alternative display output'), 'analyze-theme-display') . ': '  . $this->format_themes($theme);
1651            }
1652          }
1653        }
1654
1655        $plugin = $this->get_plugin();
1656        if ($plugin) {
1657          $funcs[] = $this->option_link(t('Style output'), 'analyze-theme-style') . ': ' . $this->format_themes($plugin->theme_functions(), $plugin->additional_theme_functions());
1658          $themes = $plugin->additional_theme_functions();
1659          if ($themes) {
1660            foreach ($themes as $theme) {
1661              $funcs[] = $this->option_link(t('Alternative style'), 'analyze-theme-style') . ': '  . $this->format_themes($theme);
1662            }
1663          }
1664
1665          if ($plugin->uses_row_plugin()) {
1666            $row_plugin = $this->get_plugin('row');
1667            if ($row_plugin) {
1668              $funcs[] = $this->option_link(t('Row style output'), 'analyze-theme-row') . ': ' . $this->format_themes($row_plugin->theme_functions());
1669              $themes = $row_plugin->additional_theme_functions();
1670              if ($themes) {
1671                foreach ($themes as $theme) {
1672                  $funcs[] = $this->option_link(t('Alternative row style'), 'analyze-theme-row') . ': '  . $this->format_themes($theme);
1673                }
1674              }
1675            }
1676          }
1677
1678          if ($plugin->uses_fields()) {
1679            foreach ($this->get_handlers('field') as $id => $handler) {
1680              $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());
1681            }
1682          }
1683        }
1684
1685        $form['important'] = array(
1686          '#prefix' => '<div class="form-item description">',
1687          '#suffix' => '</div>',
1688          '#value' => '<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>',
1689        );
1690
1691        if (isset($this->view->display[$this->view->current_display]->new_id)) {
1692          $form['important']['new_id'] = array(
1693            '#prefix' => '<div class="description">',
1694            '#suffix' => '</div>',
1695            '#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."),
1696          );
1697        }
1698
1699        foreach (list_themes() as $key => $theme) {
1700          $options[$key] = $theme->info['name'];
1701        }
1702
1703        $form['box'] = array(
1704          '#prefix' => '<div class="container-inline">',
1705          '#suffix' => '</div>',
1706        );
1707        $form['box']['theme'] = array(
1708          '#type' => 'select',
1709          '#options' => $options,
1710          '#default_value' => $this->view->theme,
1711        );
1712
1713        $form['box']['change'] = array(
1714          '#type' => 'submit',
1715          '#value' => t('Change theme'),
1716          '#submit' => array('views_ui_edit_display_form_change_theme'),
1717        );
1718
1719        $form['analysis'] = array(
1720          '#prefix' => '<div class="form-item">',
1721          '#suffix' => '</div>',
1722          '#value' => theme('item_list', $funcs),
1723        );
1724
1725        $form['rescan_button'] = array(
1726          '#prefix' => '<div class="form-item">',
1727          '#suffix' => '</div>',
1728        );
1729        $form['rescan_button']['button'] = array(
1730          '#type' => 'submit',
1731          '#value' => t('Rescan template files'),
1732          '#submit' => array('views_ui_config_item_form_rescan'),
1733        );
1734        $form['rescan_button']['markup'] = array(
1735          '#prefix' => '<div class="description">',
1736          '#suffix' => '</div>',
1737          '#value' => 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."),
1738        );
1739
1740        $form_state['ok_button'] = TRUE;
1741        break;
1742      case 'analyze-theme-display':
1743        $form['#title'] .= t('Theming information (display)');
1744        $output = '<p>' . t('Back to !info.', array('!info' => $this->option_link(t('theming information'), 'analyze-theme'))) . '</p>';
1745
1746        if (empty($this->definition['theme'])) {
1747          $output .= t('This display has no theming information');
1748        }
1749        else {
1750          $output .= '<p>' . t('This is the default theme template used for this display.') . '</p>';
1751          $output .= '<pre>' . check_plain(file_get_contents('./' . $this->definition['theme path'] . '/' . strtr($this->definition['theme'], '_', '-') . '.tpl.php')) . '</pre>';
1752        }
1753
1754        if (!empty($this->definition['additional themes'])) {
1755          foreach ($this->definition['additional themes'] as $theme => $type) {
1756            $output .= '<p>' . t('This is an alternative template for this display.') . '</p>';
1757            $output .= '<pre>' . check_plain(file_get_contents('./' . $this->definition['theme path'] . '/' . strtr($theme, '_', '-') . '.tpl.php')) . '</pre>';
1758          }
1759        }
1760
1761        $form['analysis'] = array(
1762          '#prefix' => '<div class="form-item">',
1763          '#suffix' => '</div>',
1764          '#value' => $output,
1765        );
1766
1767        $form_state['ok_button'] = TRUE;
1768        break;
1769      case 'analyze-theme-style':
1770        $form['#title'] .= t('Theming information (style)');
1771        $output = '<p>' . t('Back to !info.', array('!info' => $this->option_link(t('theming information'), 'analyze-theme'))) . '</p>';
1772
1773        $plugin = $this->get_plugin();
1774
1775        if (empty($plugin->definition['theme'])) {
1776          $output .= t('This display has no style theming information');
1777        }
1778        else {
1779          $output .= '<p>' . t('This is the default theme template used for this style.') . '</p>';
1780          $output .= '<pre>' . check_plain(file_get_contents('./' . $plugin->definition['theme path'] . '/' . strtr($plugin->definition['theme'], '_', '-') . '.tpl.php')) . '</pre>';
1781        }
1782
1783        if (!empty($plugin->definition['additional themes'])) {
1784          foreach ($plugin->definition['additional themes'] as $theme => $type) {
1785            $output .= '<p>' . t('This is an alternative template for this style.') . '</p>';
1786            $output .= '<pre>' . check_plain(file_get_contents('./' . $plugin->definition['theme path'] . '/' . strtr($theme, '_', '-') . '.tpl.php')) . '</pre>';
1787          }
1788        }
1789
1790        $form['analysis'] = array(
1791          '#prefix' => '<div class="form-item">',
1792          '#suffix' => '</div>',
1793          '#value' => $output,
1794        );
1795
1796        $form_state['ok_button'] = TRUE;
1797        break;
1798      case 'analyze-theme-row':
1799        $form['#title'] .= t('Theming information (row style)');
1800        $output = '<p>' . t('Back to !info.', array('!info' => $this->option_link(t('theming information'), 'analyze-theme'))) . '</p>';
1801
1802        $plugin = $this->get_plugin('row');
1803
1804        if (empty($plugin->definition['theme'])) {
1805          $output .= t('This display has no row style theming information');
1806        }
1807        else {
1808          $output .= '<p>' . t('This is the default theme template used for this row style.') . '</p>';
1809          $output .= '<pre>' . check_plain(file_get_contents('./' . $plugin->definition['theme path'] . '/' . strtr($plugin->definition['theme'], '_', '-') . '.tpl.php')) . '</pre>';
1810        }
1811
1812        if (!empty($plugin->definition['additional themes'])) {
1813          foreach ($plugin->definition['additional themes'] as $theme => $type) {
1814            $output .= '<p>' . t('This is an alternative template for this row style.') . '</p>';
1815            $output .= '<pre>' . check_plain(file_get_contents('./' . $plugin->definition['theme path'] . '/' . strtr($theme, '_', '-') . '.tpl.php')) . '</pre>';
1816          }
1817        }
1818
1819        $form['analysis'] = array(
1820          '#prefix' => '<div class="form-item">',
1821          '#suffix' => '</div>',
1822          '#value' => $output,
1823        );
1824
1825        $form_state['ok_button'] = TRUE;
1826        break;
1827      case 'analyze-theme-field':
1828        $form['#title'] .= t('Theming information (row style)');
1829        $output = '<p>' . t('Back to !info.', array('!info' => $this->option_link(t('theming information'), 'analyze-theme'))) . '</p>';
1830
1831        $output .= '<p>' . t('This is the default theme template used for this row style.') . '</p>';
1832
1833        // Field templates aren't registered the normal way...and they're always
1834        // this one, anyhow.
1835        $output .= '<pre>' . check_plain(file_get_contents(drupal_get_path('module', 'views') . '/theme/views-view-field.tpl.php')) . '</pre>';
1836
1837        $form['analysis'] = array(
1838          '#prefix' => '<div class="form-item">',
1839          '#suffix' => '</div>',
1840          '#value' => $output,
1841        );
1842        $form_state['ok_button'] = TRUE;
1843        break;
1844
1845      case 'exposed_block':
1846        $form['#title'] .= t('Put the exposed form in a block');
1847        $form['description'] = array(
1848          '#prefix' => '<div class="description form-item">',
1849          '#suffix' => '</div>',
1850          '#value' => 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.'),
1851        );
1852        $form['exposed_block'] = array(
1853          '#type' => 'radios',
1854          '#options' => array(1 => t('Yes'), 0 => t('No')),
1855          '#default_value' => $this->get_option('exposed_block') ? 1 : 0,
1856        );
1857        break;
1858      case 'exposed_form':
1859        $form['#title'] .= t('Exposed Form');
1860        $form['exposed_form'] = array(
1861          '#prefix' => '<div class="clear-block">',
1862          '#suffix' => '</div>',
1863          '#tree' => TRUE,
1864        );
1865
1866        $exposed_form = $this->get_option('exposed_form');
1867        $form['exposed_form']['type'] =  array(
1868          '#type' => 'radios',
1869          '#options' => views_fetch_plugin_names('exposed_form'),
1870          '#default_value' => $exposed_form['type'],
1871        );
1872
1873        $exposed_form_plugin = views_fetch_plugin_data('exposed_form', $exposed_form['type']);
1874        if (!empty($exposed_form_plugin['uses options'])) {
1875          $form['markup'] = array(
1876            '#prefix' => '<div class="form-item description">',
1877            '#suffix' => '</div>',
1878            '#value' => t('You may also adjust the !settings for the currently selected style by clicking on the icon.', array('!settings' => $this->option_link(t('settings'), 'exposed_form_options'))),
1879          );
1880        }
1881        break;
1882      case 'exposed_form_options':
1883        $plugin = $this->get_plugin('exposed_form');
1884        $form['#title'] .= t('Exposed form options');
1885        if ($plugin) {
1886          $form['#help_topic'] = $plugin->definition['help topic'];
1887
1888          $form['exposed_form_options'] = array(
1889            '#tree' => TRUE,
1890          );
1891          $plugin->options_form($form['exposed_form_options'], $form_state);
1892        }
1893        break;
1894      case 'pager':
1895        $form['#title'] .= t('Select which pager, if any, to use for this view');
1896        $form['pager'] = array(
1897          '#prefix' => '<div class="clear-block">',
1898          '#suffix' => '</div>',
1899          '#tree' => TRUE,
1900        );
1901
1902        $pager = $this->get_option('pager');
1903        $form['pager']['type'] =  array(
1904          '#type' => 'radios',
1905          '#options' => views_fetch_plugin_names('pager', empty($this->definition['use pager']) ? 'basic' : NULL),
1906          '#default_value' => $pager['type'],
1907        );
1908
1909        $pager_plugin = views_fetch_plugin_data('pager', $pager['type']);
1910        if (!empty($pager_plugin['uses options'])) {
1911          $form['markup'] = array(
1912            '#prefix' => '<div class="form-item description">',
1913            '#suffix' => '</div>',
1914            '#value' => t('You may also adjust the !settings for the currently selected pager by clicking on the icon.', array('!settings' => $this->option_link(t('settings'), 'pager_options'))),
1915          );
1916        }
1917
1918        break;
1919      case 'pager_options':
1920        $plugin = $this->get_plugin('pager');
1921        $form['#title'] .= t('Pager options');
1922        if ($plugin) {
1923          $form['#help_topic'] = $plugin->definition['help topic'];
1924
1925          $form['pager_options'] = array(
1926            '#tree' => TRUE,
1927          );
1928          $plugin->options_form($form['pager_options'], $form_state);
1929        }
1930        break;
1931    }
1932
1933    foreach ($this->extender as $extender) {
1934      $extender->options_form($form, $form_state);
1935    }
1936  }
1937
1938  /**
1939   * Format a list of theme templates for output by the theme info helper.
1940   */
1941  function format_themes($themes) {
1942    $registry = theme_get_registry();
1943
1944    // Run through the theme engine variables, if necessary
1945    global $theme_engine;
1946    $extension = '.tpl.php';
1947    if (isset($theme_engine)) {
1948      $extension_function = $theme_engine . '_extension';
1949      if (function_exists($extension_function)) {
1950        $extension = $extension_function();
1951      }
1952    }
1953
1954    $output = '';
1955    $picked = FALSE;
1956    foreach ($themes as $theme) {
1957      $template = strtr($theme, '_', '-') . $extension;
1958      if (!$picked && !empty($registry[$theme])) {
1959        $template_path = isset($registry[$theme]['path']) ? $registry[$theme]['path'] . '/' : './';
1960        if (file_exists($template_path . $template)) {
1961          $hint = t('File found in folder @template-path', array('@template-path' => $template_path));
1962          $template = '<strong title="'. $hint .'">' . $template . '</strong>';
1963        }
1964        else {
1965          $template = '<strong class="error">' . $template . ' ' . t('(File not found, in folder @template-path)', array('@template-path' => $template_path)) . '</strong>';
1966        }
1967        $picked = TRUE;
1968      }
1969      $fixed[] = $template;
1970    }
1971
1972    return implode(', ', array_reverse($fixed));
1973  }
1974
1975  /**
1976   * Validate the options form.
1977   */
1978  function options_validate(&$form, &$form_state) {
1979    switch ($form_state['section']) {
1980      case 'display_title':
1981        if (empty($form_state['values']['display_title'])) {
1982          form_error($form['display_title'], t('Display title may not be empty.'));
1983        }
1984        break;
1985      case 'css_class':
1986        $css_class = $form_state['values']['css_class'];
1987        if (preg_match('/[^a-zA-Z0-9- _]/', $css_class)) {
1988          form_error($form['css_class'], t('CSS classes must be alphanumeric or dashes only.'));
1989        }
1990      break;
1991      case 'display_id':
1992        if ($form_state['values']['display_id']) {
1993          if (preg_match('/[^a-z0-9_]/', $form_state['values']['display_id'])) {
1994            form_error($form['display_id'], t('Display name must be letters, numbers, or underscores only.'));
1995          }
1996
1997          foreach ($this->view->display as $id => $display) {
1998            if ($id != $this->view->current_display && ($form_state['values']['display_id'] == $id || $form_state['values']['display_id'] == $display->new_id)) {
1999              form_error($form['display_id'], t('Display id should be unique.'));
2000            }
2001          }
2002        }
2003        break;
2004      case 'style_options':
2005        $style = TRUE;
2006      case 'row_options':
2007        // if row, $style will be empty.
2008        $plugin = $this->get_plugin(empty($style) ? 'row' : 'style');
2009        if ($plugin) {
2010          $plugin->options_validate($form[$form_state['section']], $form_state);
2011        }
2012        break;
2013      case 'access_options':
2014        $plugin = $this->get_plugin('access');
2015        if ($plugin) {
2016          $plugin->options_validate($form['access_options'], $form_state);
2017        }
2018        break;
2019      case 'query':
2020        if ($this->view->query) {
2021          $this->view->query->options_validate($form['query'], $form_state);
2022        }
2023        break;
2024      case 'cache_options':
2025        $plugin = $this->get_plugin('cache');
2026        if ($plugin) {
2027          $plugin->options_validate($form['cache_options'], $form_state);
2028        }
2029        break;
2030      case 'exposed_form_options':
2031        $plugin = $this->get_plugin('exposed_form');
2032        if ($plugin) {
2033          $plugin->options_validate($form['exposed_form_options'], $form_state);
2034        }
2035        break;
2036      case 'pager_options':
2037        $plugin = $this->get_plugin('pager');
2038        if ($plugin) {
2039          $plugin->options_validate($form['pager_options'], $form_state);
2040        }
2041        break;
2042    }
2043
2044    foreach ($this->extender as $extender) {
2045      $extender->options_validate($form, $form_state);
2046    }
2047  }
2048
2049  /**
2050   * Perform any necessary changes to the form values prior to storage.
2051   * There is no need for this function to actually store the data.
2052   */
2053  function options_submit($form, &$form_state) {
2054    // Not sure I like this being here, but it seems (?) like a logical place.
2055    $cache_plugin = $this->get_plugin('cache');
2056    if ($cache_plugin) {
2057      $cache_plugin->cache_flush();
2058    }
2059
2060    $section = $form_state['section'];
2061    switch ($section) {
2062      case 'display_id':
2063        if (isset($form_state['values']['display_id'])) {
2064          $this->display->new_id = $form_state['values']['display_id'];
2065        }
2066        break;
2067      case 'display_title':
2068        $this->display->display_title = $form_state['values']['display_title'];
2069        $this->set_option('display_description', $form_state['values']['display_description']);
2070        break;
2071      case 'access':
2072        $access = $this->get_option('access');
2073        if ($access['type'] != $form_state['values']['access']['type']) {
2074          $plugin = views_get_plugin('access', $form_state['values']['access']['type']);
2075          if ($plugin) {
2076            $access = array('type' => $form_state['values']['access']['type']);
2077            $this->set_option('access', $access);
2078            if (!empty($plugin->definition['uses options'])) {
2079              views_ui_add_form_to_stack('display', $this->view, $this->display->id, array('access_options'));
2080            }
2081          }
2082        }
2083
2084        break;
2085      case 'access_options':
2086        $plugin = views_get_plugin('access', $form_state['values'][$section]['type']);
2087        if ($plugin) {
2088          $plugin->options_submit($form['access_options'], $form_state);
2089          $this->set_option('access', $form_state['values'][$section]);
2090        }
2091        break;
2092      case 'cache':
2093        $cache = $this->get_option('cache');
2094        if ($cache['type'] != $form_state['values']['cache']['type']) {
2095          $plugin = views_get_plugin('cache', $form_state['values']['cache']['type']);
2096          if ($plugin) {
2097            $cache = array('type' => $form_state['values']['cache']['type']);
2098            $this->set_option('cache', $cache);
2099            if (!empty($plugin->definition['uses options'])) {
2100              views_ui_add_form_to_stack('display', $this->view, $this->display->id, array('cache_options'));
2101            }
2102          }
2103        }
2104
2105        break;
2106      case 'cache_options':
2107        $plugin = views_get_plugin('cache', $form_state['values'][$section]['type']);
2108        if ($plugin) {
2109          $plugin->options_submit($form['cache_options'], $form_state);
2110          $this->set_option('cache', $form_state['values'][$section]);
2111        }
2112        break;
2113      case 'query':
2114        $plugin = $this->get_plugin('query');
2115        if ($plugin) {
2116          $plugin->options_submit($form['query']['options'], $form_state);
2117          $this->set_option('query', $form_state['values'][$section]);
2118        }
2119        break;
2120
2121      case 'link_display':
2122        $this->set_option('link_url', $form_state['values']['link_url']);
2123      case 'title':
2124      case 'css_class':
2125      case 'display_comment':
2126        $this->set_option($section, $form_state['values'][$section]);
2127        break;
2128      case 'enabled':
2129      case 'use_ajax':
2130      case 'hide_attachment_summary':
2131        $this->set_option($section, (bool)$form_state['values'][$section]);
2132        break;
2133      case 'use_more':
2134        $this->set_option($section, intval($form_state['values'][$section]));
2135        $this->set_option('use_more_always', !intval($form_state['values']['use_more_always']));
2136        $this->set_option('use_more_text', $form_state['values']['use_more_text']);
2137        break;
2138      case 'group_by':
2139        $this->set_option($section, $form_state['values'][$section]);
2140        break;
2141      case 'row_plugin':
2142        // This if prevents resetting options to default if they don't change
2143        // the plugin.
2144        if ($this->get_option($section) != $form_state['values'][$section]) {
2145          $plugin = views_get_plugin('row', $form_state['values'][$section]);
2146          if ($plugin) {
2147            $this->set_option($section, $form_state['values'][$section]);
2148            $this->set_option('row_options', array());
2149
2150            // send ajax form to options page if we use it.
2151            if (!empty($plugin->definition['uses options'])) {
2152              views_ui_add_form_to_stack('display', $this->view, $this->display->id, array('row_options'));
2153            }
2154          }
2155        }
2156        break;
2157      case 'style_plugin':
2158        // This if prevents resetting options to default if they don't change
2159        // the plugin.
2160        if ($this->get_option($section) != $form_state['values'][$section]) {
2161          $plugin = views_get_plugin('style', $form_state['values'][$section]);
2162          if ($plugin) {
2163            $this->set_option($section, $form_state['values'][$section]);
2164            $this->set_option('style_options', array());
2165            // send ajax form to options page if we use it.
2166            if (!empty($plugin->definition['uses options'])) {
2167              views_ui_add_form_to_stack('display', $this->view, $this->display->id, array('style_options'));
2168            }
2169          }
2170        }
2171        break;
2172      case 'style_options':
2173        $style = TRUE;
2174      case 'row_options':
2175        // if row, $style will be empty.
2176        $plugin = $this->get_plugin(empty($style) ? 'row' : 'style');
2177        if ($plugin) {
2178          $plugin->options_submit($form[$section], $form_state);
2179        }
2180        $this->set_option($section, $form_state['values'][$section]);
2181        break;
2182      case 'exposed_block':
2183        $this->set_option($section, (bool) $form_state['values'][$section]);
2184        break;
2185      case 'exposed_form':
2186        $exposed_form = $this->get_option('exposed_form');
2187        if ($exposed_form['type'] != $form_state['values']['exposed_form']['type']) {
2188          $plugin = views_get_plugin('exposed_form', $form_state['values']['exposed_form']['type']);
2189          if ($plugin) {
2190            $exposed_form = array('type' => $form_state['values']['exposed_form']['type'], 'options' => array());
2191            $this->set_option('exposed_form', $exposed_form);
2192            if (!empty($plugin->definition['uses options'])) {
2193              views_ui_add_form_to_stack('display', $this->view, $this->display->id, array('exposed_form_options'));
2194            }
2195          }
2196        }
2197
2198        break;
2199      case 'exposed_form_options':
2200        $plugin = $this->get_plugin('exposed_form');
2201        if ($plugin) {
2202          $exposed_form = $this->get_option('exposed_form');
2203          $plugin->options_submit($form['exposed_form_options'], $form_state);
2204          $exposed_form['options'] = $form_state['values'][$section];
2205          $this->set_option('exposed_form', $exposed_form);
2206        }
2207        break;
2208      case 'pager':
2209        $pager = $this->get_option('pager');
2210        if ($pager['type'] != $form_state['values']['pager']['type']) {
2211          $plugin = views_get_plugin('pager', $form_state['values']['pager']['type']);
2212          if ($plugin) {
2213            // Because pagers have very similar options, let's allow pagers to
2214            // try to carry the options over.
2215            $plugin->init($this->view, $this->display, $pager['options']);
2216
2217            $pager = array('type' => $form_state['values']['pager']['type'], 'options' => $plugin->options);
2218            $this->set_option('pager', $pager);
2219            if (!empty($plugin->definition['uses options'])) {
2220              views_ui_add_form_to_stack('display', $this->view, $this->display->id, array('pager_options'));
2221            }
2222          }
2223        }
2224
2225        break;
2226      case 'pager_options':
2227        $plugin = $this->get_plugin('pager');
2228        if ($plugin) {
2229          $pager = $this->get_option('pager');
2230          $plugin->options_submit($form['pager_options'], $form_state);
2231          $pager['options'] = $form_state['values'][$section];
2232          $this->set_option('pager', $pager);
2233        }
2234        break;
2235    }
2236
2237    foreach ($this->extender as $extender) {
2238      $extender->options_submit($form, $form_state);
2239    }
2240  }
2241
2242  /**
2243   * Add an override button for a given section, allowing the user to
2244   * change whether this info is stored on the default display or on
2245   * the current display.
2246   */
2247  function add_override_button(&$form, &$form_state, $section) {
2248    if ($this->is_default_display()) {
2249      return;
2250    }
2251
2252    $form['override'] = array(
2253      '#prefix' => '<div class="views-override clear-block">',
2254      '#suffix' => '</div>',
2255    );
2256    if ($this->is_defaulted($section)) {
2257      $form['override']['button'] = array(
2258        '#type' => 'submit',
2259        '#value' => t('Override'),
2260        '#submit' => array('views_ui_edit_display_form_override'),
2261      );
2262      $form['override']['markup'] = array(
2263        '#prefix' => '<div class="description">',
2264        '#value' => theme('advanced_help_topic', 'views', 'overrides') . t('Status: using default values.'),
2265        '#suffix' => '</div>',
2266      );
2267
2268      $form_state['update_name'] = t('Update default display');
2269    }
2270    else {
2271      $form['override']['button'] = array(
2272        '#type' => 'submit',
2273        '#value' => t('Use default'),
2274        '#submit' => array('views_ui_edit_display_form_override'),
2275      );
2276      $form['override']['markup'] = array(
2277        '#prefix' => '<div class="description">',
2278        '#value' => theme('advanced_help_topic', 'views', 'overrides') . t('Status: using overridden values.'),
2279        '#suffix' => '</div>',
2280      );
2281
2282      $form_state['update_name'] = NULL;
2283    }
2284  }
2285
2286  /**
2287   * If override/revert was clicked, perform the proper toggle.
2288   */
2289  function options_override($form, &$form_state) {
2290    $this->set_override($form_state['section']);
2291  }
2292
2293  /**
2294   * Flip the override setting for the given section.
2295   */
2296  function set_override($section, $new_state = NULL) {
2297    $options = $this->defaultable_sections($section);
2298    if (!$options) {
2299      return;
2300    }
2301
2302    if (!isset($new_state)) {
2303      $new_state = empty($this->options['defaults'][$section]);
2304    }
2305
2306    // For each option that is part of this group, fix our settings.
2307    foreach ($options as $option) {
2308      if ($new_state) {
2309        // Revert to defaults.
2310        unset($this->options[$option]);
2311        unset($this->display->display_options[$option]);
2312      }
2313      else {
2314        // copy existing values into our display.
2315        $this->options[$option] = $this->get_option($option);
2316        $this->display->display_options[$option] = $this->options[$option];
2317      }
2318      $this->options['defaults'][$option] = $new_state;
2319      $this->display->display_options['defaults'][$option] = $new_state;
2320    }
2321  }
2322
2323  /**
2324   * Inject anything into the query that the display handler needs.
2325   */
2326  function query() {
2327    foreach ($this->extender as $extender) {
2328      $extender->query();
2329    }
2330  }
2331
2332  /**
2333   * Not all display plugins will support filtering
2334   */
2335  function render_filters() { }
2336
2337  /**
2338   * Not all display plugins will suppert pager rendering.
2339   */
2340  function render_pager() {
2341    return TRUE;
2342  }
2343
2344  /**
2345   * Render the 'more' link
2346   */
2347  function render_more_link() {
2348    if ($this->use_more() && ($this->use_more_always() || (!empty($this->view->query->pager) && $this->view->query->pager->has_more_records()))) {
2349      $path = $this->get_path();
2350
2351      if ($this->get_option('link_display') == 'custom_url' && $override_path = $this->get_option('link_url')) {
2352        $tokens = $this->get_arguments_tokens();
2353        $path = strtr($override_path, $tokens);
2354      }
2355
2356      if ($path) {
2357        if (empty($override_path) || (strpos($override_path, '!') === FALSE && strpos($override_path, '%') === FALSE)) {
2358          $path = $this->view->get_url(NULL, $path);
2359        }
2360        $url_options = array();
2361        if (!empty($this->view->exposed_raw_input)) {
2362          $url_options['query'] = $this->view->exposed_raw_input;
2363        }
2364        $theme = views_theme_functions('views_more', $this->view, $this->display);
2365        $path = check_url(url($path, $url_options));
2366        return theme($theme, $path, check_plain($this->use_more_text()));
2367      }
2368    }
2369  }
2370
2371  /**
2372   * If this display creates a block, implement one of these.
2373   */
2374  function hook_block($op = 'list', $delta = 0, $edit = array()) { return array(); }
2375
2376  /**
2377   * If this display creates a page with a menu item, implement it here.
2378   */
2379  function hook_menu() { return array(); }
2380
2381  /**
2382   * Render this display.
2383   */
2384  function render() {
2385    return theme($this->theme_functions(), $this->view);
2386  }
2387
2388  function render_area($area, $empty = FALSE) {
2389    $return = '';
2390    foreach ($this->get_handlers($area) as $area) {
2391      $return .= $area->render($empty);
2392    }
2393    return $return;
2394  }
2395
2396  /**
2397   * Legacy functions.
2398   */
2399  function render_header() {
2400    $empty = !empty($this->view->result);
2401    return $this->render_area('header', $empty);
2402  }
2403
2404  function render_footer() {
2405    $empty = !empty($this->view->result);
2406    return $this->render_area('footer', $empty);
2407  }
2408
2409  function render_empty() {
2410    return $this->render_area('empty');
2411  }
2412
2413  /**
2414   * Determine if the user has access to this display of the view.
2415   */
2416  function access($account = NULL) {
2417    if (!isset($account)) {
2418      global $user;
2419      $account = $user;
2420    }
2421
2422    // Full override.
2423    if (user_access('access all views', $account)) {
2424      return TRUE;
2425    }
2426
2427    $plugin = $this->get_plugin('access');
2428    if ($plugin) {
2429      return $plugin->access($account);
2430    }
2431
2432    // fallback to all access if no plugin.
2433    return TRUE;
2434  }
2435
2436  /**
2437   * Set up any variables on the view prior to execution. These are separated
2438   * from execute because they are extremely common and unlikely to be
2439   * overridden on an individual display.
2440   */
2441  function pre_execute() {
2442    $this->view->set_use_ajax($this->use_ajax());
2443    if ($this->use_more() && !$this->use_more_always()) {
2444      $this->view->get_total_rows = TRUE;
2445    }
2446    $this->view->init_handlers();
2447    if ($this->uses_exposed()) {
2448      $exposed_form = $this->get_plugin('exposed_form');
2449      $exposed_form->pre_execute();
2450    }
2451
2452    foreach ($this->extender as $extender) {
2453      $extender->pre_execute();
2454    }
2455  }
2456
2457  /**
2458   * When used externally, this is how a view gets run and returns
2459   * data in the format required.
2460   *
2461   * The base class cannot be executed.
2462   */
2463  function execute() { }
2464
2465  /**
2466   * Fully render the display for the purposes of a live preview or
2467   * some other AJAXy reason.
2468   */
2469  function preview() { return $this->view->render(); }
2470
2471  /**
2472   * Displays can require a certain type of style plugin. By default, they will
2473   * be 'normal'.
2474   */
2475  function get_style_type() { return 'normal'; }
2476
2477  /**
2478   * Make sure the display and all associated handlers are valid.
2479   *
2480   * @return
2481   *   Empty array if the display is valid; an array of error strings if it is not.
2482   */
2483  function validate() {
2484    $errors = array();
2485    // Make sure displays that use fields HAVE fields.
2486    if ($this->uses_fields()) {
2487      $fields = FALSE;
2488      foreach ($this->get_handlers('field') as $field) {
2489        if (empty($field->options['exclude'])) {
2490          $fields = TRUE;
2491        }
2492      }
2493
2494      if (!$fields) {
2495        $errors[] = t('Display "@display" uses fields but there are none defined for it or all are excluded.', array('@display' => $this->display->display_title));
2496      }
2497    }
2498
2499    if ($this->has_path() && !$this->get_option('path')) {
2500      $errors[] = t('Display "@display" uses a path but the path is undefined.', array('@display' => $this->display->display_title));
2501    }
2502
2503    // Validate style plugin
2504    $style = $this->get_plugin();
2505    if (empty($style)) {
2506      $errors[] = t('Display "@display" has an invalid style plugin.', array('@display' => $this->display->display_title));
2507    }
2508    else {
2509      $result = $style->validate();
2510      if (!empty($result) && is_array($result)) {
2511        $errors = array_merge($errors, $result);
2512      }
2513    }
2514
2515    // Validate query plugin.
2516    $query = $this->get_plugin('query');
2517    $result = $query->validate();
2518    if (!empty($result) && is_array($result)) {
2519      $errors = array_merge($errors, $result);
2520    }
2521
2522    // Validate handlers
2523    foreach (views_object_types() as $type => $info) {
2524      foreach ($this->get_handlers($type) as $handler) {
2525        $result = $handler->validate();
2526        if (!empty($result) && is_array($result)) {
2527          $errors = array_merge($errors, $result);
2528        }
2529      }
2530    }
2531
2532    return $errors;
2533  }
2534
2535  /**
2536   * Check if the provided identifier is unique.
2537   */
2538  function is_identifier_unique($id, $identifier) {
2539    foreach (views_object_types() as $type => $info) {
2540      foreach ($this->get_handlers($type) as $key => $handler) {
2541        if ($handler->can_expose() && $handler->is_exposed()) {
2542          if ($id != $key && $identifier == $handler->options['expose']['identifier']) {
2543            return FALSE;
2544          }
2545        }
2546      }
2547    }
2548    return TRUE;
2549  }
2550
2551  /**
2552   * Provide the block system with any exposed widget blocks for this display.
2553   */
2554  function get_special_blocks() {
2555    $blocks = array();
2556
2557    if ($this->uses_exposed_form_in_block()) {
2558      $delta = '-exp-' . $this->view->name . '-' . $this->display->id;
2559      $desc = t('Exposed form: @view-@display_id', array('@view' => $this->view->name, '@display_id' => $this->display->id));
2560
2561      $blocks[$delta] = array(
2562        'info' => $desc,
2563        'cache' => BLOCK_NO_CACHE,
2564      );
2565    }
2566
2567    return $blocks;
2568  }
2569
2570  /**
2571   * Render any special blocks provided for this display.
2572   */
2573  function view_special_blocks($type) {
2574    if ($type == '-exp') {
2575      // avoid interfering with the admin forms.
2576      if (arg(0) == 'admin' && arg(1) == 'build' && arg(2) == 'views') {
2577        return;
2578      }
2579      $this->view->init_handlers();
2580
2581      if ($this->uses_exposed() && $this->get_option('exposed_block')) {
2582        $exposed_form = $this->get_plugin('exposed_form');
2583        return array(
2584          'content' => $exposed_form->render_exposed_form(TRUE),
2585        );
2586      }
2587    }
2588  }
2589
2590  /**
2591   * Override of export_option()
2592   *
2593   * Because displays do not want to export options that are NOT overridden from the
2594   * default display, we need some special handling during the export process.
2595   */
2596  function export_option($indent, $prefix, $storage, $option, $definition, $parents) {
2597    // The $prefix is wrong because we store our actual options a little differently:
2598    $prefix = '$handler->display->display_options';
2599    $output = '';
2600    if (!$parents && !$this->is_default_display()) {
2601      // Do not export items that are not overridden.
2602      if ($this->is_defaulted($option)) {
2603        return;
2604      }
2605
2606      // If this is not defaulted and is overrideable, flip the switch to say this
2607      // is overridden.
2608      if ($this->defaultable_sections($option)) {
2609        $output .= $indent . $prefix . "['defaults']['$option'] = FALSE;\n";
2610      }
2611    }
2612
2613    $output .= parent::export_option($indent, $prefix, $storage, $option, $definition, $parents);
2614    return $output;
2615  }
2616
2617  /**
2618   * Special method to export items that have handlers.
2619   *
2620   * This method was specified in the option_definition() as the method to utilize to
2621   * export fields, filters, sort criteria, relationships and arguments. This passes
2622   * the export off to the individual handlers so that they can export themselves
2623   * properly.
2624   */
2625  function export_handler($indent, $prefix, $storage, $option, $definition, $parents) {
2626    $output = '';
2627
2628    // cut the 's' off because the data is stored as the plural form but we need
2629    // the singular form. Who designed that anyway? Oh yeah, I did. :(
2630    if ($option != 'header' && $option != 'footer' && $option != 'empty') {
2631      $type = substr($option, 0, -1);
2632    }
2633    else {
2634      $type = $option;
2635    }
2636    $types = views_object_types();
2637    foreach ($storage[$option] as $id => $info) {
2638      if (!empty($types[$type]['type'])) {
2639        $handler_type = $types[$type]['type'];
2640      }
2641      else {
2642        $handler_type = $type;
2643      }
2644      $handler = views_get_handler($info['table'], $info['field'], $handler_type);
2645      if ($handler) {
2646        $handler->init($this->view, $info);
2647        $output .= $indent . '/* ' . $types[$type]['stitle'] . ': ' . $handler->ui_name() . " */\n";
2648        $output .= $handler->export_options($indent, $prefix . "['$option']['$id']");
2649      }
2650
2651      // Prevent reference problems.
2652      unset($handler);
2653    }
2654
2655    return $output;
2656  }
2657
2658  /**
2659   * Special handling for the style export.
2660   *
2661   * Styles are stored as style_plugin and style_options or row_plugin and
2662   * row_options accordingly. The options are told not to export, and the
2663   * export for the plugin should export both.
2664   */
2665  function export_style($indent, $prefix, $storage, $option, $definition, $parents) {
2666    $output = '';
2667    $style_plugin = $this->get_plugin();
2668    if ($option == 'style_plugin') {
2669      $type = 'style';
2670      $options_field = 'style_options';
2671      $plugin = $style_plugin;
2672    }
2673    else {
2674      if (!$style_plugin || !$style_plugin->uses_row_plugin()) {
2675        return;
2676      }
2677
2678      $type = 'row';
2679      $options_field = 'row_options';
2680      $plugin = $this->get_plugin('row');
2681      // If the style plugin doesn't use row plugins, don't even bother.
2682    }
2683
2684    if ($plugin) {
2685      // Write which plugin to use.
2686      $value = $this->get_option($option);
2687      $output .= $indent . $prefix . "['$option'] = '$value';\n";
2688
2689      // Pass off to the plugin to export itself.
2690      $output .= $plugin->export_options($indent, $prefix . "['$options_field']");
2691    }
2692
2693    return $output;
2694  }
2695
2696  /**
2697   * Special handling for plugin export
2698   *
2699   * Plugins other than styles are stored in array with 'type' being the key
2700   * to the plugin. For modern plugins, the options are stored in the 'options'
2701   * array, but for legacy plugins (access and cache) options are stored as
2702   * siblings to the type.
2703   */
2704  function export_plugin($indent, $prefix, $storage, $option, $definition, $parents) {
2705    $output = '';
2706    $plugin_type = end($parents);
2707    $plugin = $this->get_plugin($plugin_type);
2708    if ($plugin) {
2709      // Write which plugin to use.
2710      $value = $storage[$option];
2711      $new_prefix = $prefix . "['$plugin_type']";
2712
2713      $output .= $indent . $new_prefix . "['$option'] = '$value';\n";
2714
2715      if ($plugin_type != 'access' && $plugin_type!= 'cache') {
2716        $new_prefix .= "['options']";
2717      }
2718
2719      // Pass off to the plugin to export itself.
2720      $output .= $plugin->export_options($indent, $new_prefix);
2721    }
2722
2723    return $output;
2724  }
2725
2726  function unpack_style($indent, $prefix, $storage, $option, $definition, $parents) {
2727    $output = '';
2728    $style_plugin = $this->get_plugin();
2729    if ($option == 'style_plugin') {
2730      $type = 'style';
2731      $options_field = 'style_options';
2732      $plugin = $style_plugin;
2733    }
2734    else {
2735      if (!$style_plugin || !$style_plugin->uses_row_plugin()) {
2736        return;
2737      }
2738
2739      $type = 'row';
2740      $options_field = 'row_options';
2741      $plugin = $this->get_plugin('row');
2742      // If the style plugin doesn't use row plugins, don't even bother.
2743    }
2744
2745    if ($plugin) {
2746      return $plugin->unpack_translatables($translatable, $parents);
2747    }
2748  }
2749
2750  /**
2751   * Special handling for plugin unpacking.
2752   */
2753  function unpack_plugin(&$translatable, $storage, $option, $definition, $parents) {
2754    $plugin_type = end($parents);
2755    $plugin = $this->get_plugin($plugin_type);
2756    if ($plugin) {
2757      // Write which plugin to use.
2758      return $plugin->unpack_translatables($translatable, $parents);
2759    }
2760  }
2761
2762    /**
2763   * Special method to unpack items that have handlers.
2764   *
2765   * This method was specified in the option_definition() as the method to utilize to
2766   * export fields, filters, sort criteria, relationships and arguments. This passes
2767   * the export off to the individual handlers so that they can export themselves
2768   * properly.
2769   */
2770  function unpack_handler(&$translatable, $storage, $option, $definition, $parents) {
2771    $output = '';
2772
2773    // cut the 's' off because the data is stored as the plural form but we need
2774    // the singular form. Who designed that anyway? Oh yeah, I did. :(
2775    if ($option != 'header' && $option != 'footer' && $option != 'empty') {
2776      $type = substr($option, 0, -1);
2777    }
2778    else {
2779      $type = $option;
2780    }
2781    $types = views_object_types();
2782    foreach ($storage[$option] as $id => $info) {
2783      if (!empty($types[$type]['type'])) {
2784        $handler_type = $types[$type]['type'];
2785      }
2786      else {
2787        $handler_type = $type;
2788      }
2789      $handler = views_get_handler($info['table'], $info['field'], $handler_type);
2790      if ($handler) {
2791        $handler->init($this->view, $info);
2792        $handler->unpack_translatables($translatable, array_merge($parents, array($type, $info['table'], $info['id'])));
2793      }
2794
2795      // Prevent reference problems.
2796      unset($handler);
2797    }
2798
2799    return $output;
2800  }
2801}
2802
2803
2804/**
2805 * @}
2806 */
Nota: Vea TracBrowser para ayuda de uso del navegador del repositorio.