Stripping CVS keywords
[project/views.git] / plugins / views_plugin_display.inc
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 */
26 class views_plugin_display extends views_plugin {
27 var $handlers = array();
28
29 function init(&$view, &$display, $options = NULL) {
30 $this->view = &$view;
31 $this->display = &$display;
32
33 // Make some modifications:
34 if (!isset($options)) {
35 $options = $display->display_options;
36 }
37
38 if ($this->is_default_display() && isset($options['defaults'])) {
39 unset($options['defaults']);
40 }
41
42 $this->unpack_options($this->options, $options);
43 }
44
45 function destroy() {
46 parent::destroy();
47
48 foreach ($this->handlers as $type => $handlers) {
49 foreach ($handlers as $id => $handler) {
50 if (is_object($handler)) {
51 $this->handlers[$type][$id]->destroy();
52 }
53 }
54 }
55
56 if (isset($this->default_display)) {
57 unset($this->default_display);
58 }
59 }
60
61 /**
62 * Determine if this display is the 'default' display which contains
63 * fallback settings
64 */
65 function is_default_display() { return FALSE; }
66
67 /**
68 * Determine if this display uses exposed filters, so the view
69 * will know whether or not to build them.
70 */
71 function uses_exposed() {
72 if (!isset($this->has_exposed)) {
73 foreach (array('field', 'filter') as $type) {
74 foreach ($this->view->$type as $key => $handler) {
75 if ($handler->is_exposed()) {
76 // one is all we need; if we find it, return true.
77 $this->has_exposed = TRUE;
78 return TRUE;
79 }
80 }
81 }
82 $this->has_exposed = FALSE;
83 }
84
85 return $this->has_exposed;
86 }
87
88 /**
89 * Determine if this display should display the exposed
90 * filters widgets, so the view will know whether or not
91 * to render them.
92 *
93 * Regardless of what this function
94 * returns, exposed filters will not be used nor
95 * displayed unless uses_exposed() returns TRUE.
96 */
97 function displays_exposed() {
98 return TRUE;
99 }
100
101 /**
102 * Does the display use AJAX?
103 */
104 function use_ajax() {
105 if (!empty($this->definition['use ajax'])) {
106 return $this->get_option('use_ajax');
107 }
108 return FALSE;
109 }
110
111 /**
112 * Does the display have a pager enabled?
113 */
114 function use_pager() {
115 if (!empty($this->definition['use pager'])) {
116 return $this->get_option('use_pager');
117 }
118 return FALSE;
119 }
120
121 /**
122 * Does the display have a more link enabled?
123 */
124 function use_more() {
125 if (!empty($this->definition['use more'])) {
126 return $this->get_option('use_more');
127 }
128 return FALSE;
129 }
130
131 /**
132 * Does the display have custom link text?
133 */
134 function use_more_text() {
135 if (!empty($this->definition['use more'])) {
136 return $this->get_option('use_more_text');
137 }
138 return FALSE;
139 }
140
141 /**
142 * Can this display accept attachments?
143 */
144 function accept_attachments() {
145 return !empty($this->definition['accept attachments']);
146 }
147
148 /**
149 * Allow displays to attach to other views.
150 */
151 function attach_to($display_id) { }
152
153 /**
154 * Static member function to list which sections are defaultable
155 * and what items each section contains.
156 */
157 function defaultable_sections($section = NULL) {
158 $sections = array(
159 'access' => array('access'),
160 'cache' => array('cache'),
161 'title' => array('title'),
162 'header' => array('header', 'header_format', 'header_empty'),
163 'footer' => array('footer', 'footer_format', 'footer_empty'),
164 'empty' => array('empty', 'empty_format'),
165 'use_ajax' => array('use_ajax'),
166 'items_per_page' => array('items_per_page', 'offset', 'use_pager', 'pager_element'),
167 'use_pager' => array('items_per_page', 'offset', 'use_pager', 'pager_element'),
168 'use_more' => array('use_more', 'use_more_text'),
169 'link_display' => array('link_display'),
170 'distinct' => array('distinct'),
171 'exposed_block' => array('exposed_block'),
172
173 // Force these to cascade properly.
174 'style_plugin' => array('style_plugin', 'style_options', 'row_plugin', 'row_options'),
175 'style_options' => array('style_plugin', 'style_options', 'row_plugin', 'row_options'),
176 'row_plugin' => array('style_plugin', 'style_options', 'row_plugin', 'row_options'),
177 'row_options' => array('style_plugin', 'style_options', 'row_plugin', 'row_options'),
178
179 // These guys are special
180 'relationships' => array('relationships'),
181 'fields' => array('fields'),
182 'sorts' => array('sorts'),
183 'arguments' => array('arguments'),
184 'filters' => array('filters'),
185 );
186 if ($section) {
187 if (!empty($sections[$section])) {
188 return $sections[$section];
189 }
190 }
191 else {
192 return $sections;
193 }
194 }
195
196 /**
197 * Set default options.
198 *
199 * Displays put their options in a different place than everything else; also
200 * displays spread their options out. We don't want to set defaults for
201 * items that are normally defaulted elsewhere.
202 */
203 function _set_option_defaults(&$storage, $options, $level = 0) {
204 foreach ($options as $option => $definition) {
205 // If defaulted to elsewhere and we're not the default display, skip.
206 if ($level == 0 && !$this->is_default_display() && !empty($options['defaults']['default'][$option])) {
207 continue;
208 }
209
210 if (isset($definition['contains']) && is_array($definition['contains'])) {
211 $storage[$option] = array();
212 $this->_set_option_defaults($storage[$option], $definition['contains'], $level++);
213 }
214 else {
215 $storage[$option] = isset($definition['default']) ? $definition['default'] : NULL;
216 }
217 }
218 }
219
220 function option_definition() {
221 $options = array(
222 'defaults' => array(
223 'default' => array(
224 'access' => TRUE,
225 'cache' => TRUE,
226 'title' => TRUE,
227 'header' => TRUE,
228 'header_format' => TRUE,
229 'header_empty' => TRUE,
230 'footer' => TRUE,
231 'footer_format' => TRUE,
232 'footer_empty' => TRUE,
233 'empty' => TRUE,
234 'empty_format' => TRUE,
235
236 'use_ajax' => TRUE,
237 'items_per_page' => TRUE,
238 'offset' => TRUE,
239 'use_pager' => TRUE,
240 'pager_element' => TRUE,
241 'use_more' => TRUE,
242 'use_more_text' => TRUE,
243 'distinct' => TRUE,
244 'exposed_block' => TRUE,
245
246 'link_display' => TRUE,
247
248 'style_plugin' => TRUE,
249 'style_options' => TRUE,
250 'row_plugin' => TRUE,
251 'row_options' => TRUE,
252
253 'relationships' => TRUE,
254 'fields' => TRUE,
255 'sorts' => TRUE,
256 'arguments' => TRUE,
257 'filters' => TRUE,
258 ),
259 ),
260 'relationships' => array(
261 'default' => array(),
262 'export' => 'export_item',
263 ),
264 'fields' => array(
265 'default' => array(),
266 'export' => 'export_item',
267 ),
268 'sorts' => array(
269 'default' => array(),
270 'export' => 'export_item',
271 ),
272 'arguments' => array(
273 'default' => array(),
274 'export' => 'export_item',
275 ),
276 'filters' => array(
277 'default' => array(),
278 'export' => 'export_item',
279 ),
280 'access' => array(
281 'contains' => array(
282 'type' => array('default' => 'none'),
283 ),
284 ),
285 'cache' => array(
286 'contains' => array(
287 'type' => array('default' => 'none'),
288 ),
289 ),
290 'title' => array(
291 'default' => '',
292 'translatable' => TRUE,
293 ),
294 'header' => array(
295 'default' => '',
296 'translatable' => TRUE,
297 ),
298 'header_format' => array(
299 'default' => FILTER_FORMAT_DEFAULT,
300 ),
301 'header_empty' => array(
302 'default' => FALSE,
303 ),
304 'footer' => array(
305 'default' => '',
306 'translatable' => TRUE,
307 ),
308 'footer_format' => array(
309 'default' => FILTER_FORMAT_DEFAULT,
310 ),
311 'footer_empty' => array(
312 'default' => FALSE,
313 ),
314 'empty' => array(
315 'default' => '',
316 'translatable' => TRUE,
317 ),
318 'empty_format' => array(
319 'default' => FILTER_FORMAT_DEFAULT,
320 ),
321 'use_ajax' => array(
322 'default' => FALSE,
323 ),
324 'items_per_page' => array(
325 'default' => 10,
326 ),
327 'offset' => array(
328 'default' => 0,
329 ),
330 'use_pager' => array(
331 'default' => FALSE,
332 ),
333 'pager_element' => array(
334 'default' => 0,
335 ),
336 'use_more' => array(
337 'default' => FALSE,
338 ),
339 'use_more_text' => array(
340 'default' => 'more',
341 'translatable' => TRUE,
342 ),
343 'link_display' => array(
344 'default' => '',
345 ),
346 'distinct' => array(
347 'default' => FALSE,
348 ),
349
350 'style_plugin' => array(
351 'default' => 'default',
352 ),
353 'style_options' => array(
354 'default' => array(),
355 ),
356 'row_plugin' => array(
357 'default' => 'fields',
358 ),
359 'row_options' => array(
360 'default' => array(),
361 ),
362
363 'exposed_block' => array(
364 'default' => FALSE,
365 ),
366 );
367
368 if ($this->is_default_display()) {
369 unset($options['defaults']);
370 }
371 return $options;
372 }
373
374 /**
375 * Check to see if the display has a 'path' field.
376 *
377 * This is a pure function and not just a setting on the definition
378 * because some displays (such as a panel pane) may have a path based
379 * upon configuration.
380 *
381 * By default, displays do not have a path.
382 */
383 function has_path() { return FALSE; }
384
385 /**
386 * Check to see if the display has some need to link to another display.
387 *
388 * For the most part, displays without a path will use a link display. However,
389 * sometimes displays that have a path might also need to link to another display.
390 * This is true for feeds.
391 */
392 function uses_link_display() { return !$this->has_path(); }
393
394 /**
395 * Check to see which display to use when creating links within
396 * a view using this display.
397 */
398 function get_link_display() {
399 $display_id = $this->get_option('link_display');
400 // If unknown, pick the first one.
401 if (empty($display_id) || empty($this->view->display[$display_id])) {
402 foreach ($this->view->display as $display_id => $display) {
403 if (!empty($display->handler) && $display->handler->has_path()) {
404 return $display_id;
405 }
406 }
407 }
408 else {
409 return $display_id;
410 }
411 // fall-through returns NULL
412 }
413
414 /**
415 * Return the base path to use for this display.
416 *
417 * This can be overridden for displays that do strange things
418 * with the path.
419 */
420 function get_path() {
421 if ($this->has_path()) {
422 return $this->get_option('path');
423 }
424
425 $display_id = $this->get_link_display();
426 if ($display_id && !empty($this->view->display[$display_id]) && is_object($this->view->display[$display_id]->handler)) {
427 return $this->view->display[$display_id]->handler->get_path();
428 }
429 }
430
431 /**
432 * Check to see if the display needs a breadcrumb
433 *
434 * By default, displays do not need breadcrumbs
435 */
436 function uses_breadcrumb() { return FALSE; }
437
438 /**
439 * Determine if a given option is set to use the default display or the
440 * current display
441 *
442 * @return
443 * TRUE for the default display
444 */
445 function is_defaulted($option) {
446 return !$this->is_default_display() && !empty($this->default_display) && !empty($this->options['defaults'][$option]);
447 }
448
449 /**
450 * Intelligently get an option either from this display or from the
451 * default display, if directed to do so.
452 */
453 function get_option($option) {
454 if ($this->is_defaulted($option)) {
455 return $this->default_display->get_option($option);
456 }
457
458 if (array_key_exists($option, $this->options)) {
459 return $this->options[$option];
460 }
461 }
462
463 /**
464 * Determine if the display's style uses fields.
465 */
466 function uses_fields() {
467 $plugin = $this->get_plugin();
468 if ($plugin) {
469 return $plugin->uses_fields();
470 }
471 }
472
473 /**
474 * Get the display or row plugin, if it exists.
475 */
476 function get_plugin($type = 'style', $name = NULL) {
477 if (!$name) {
478 $name = $this->get_option($type . '_plugin');
479 }
480
481 $plugin = views_get_plugin($type, $name);
482 if ($plugin) {
483 $options = $this->get_option($type . '_options');
484 $plugin->init($this->view, $this->display, $options);
485 return $plugin;
486 }
487 }
488
489 /**
490 * Get the access plugin
491 */
492 function get_access_plugin($name = NULL) {
493 if (!$name) {
494 $access = $this->get_option('access');
495 $name = $access['type'];
496 }
497
498 $plugin = views_get_plugin('access', $name);
499 if ($plugin) {
500 $plugin->init($this->view, $this->display);
501 return $plugin;
502 }
503 }
504
505 /**
506 * Get the cache plugin
507 */
508 function get_cache_plugin($name = NULL) {
509 if (!$name) {
510 $cache = $this->get_option('cache');
511 $name = $cache['type'];
512 }
513
514 $plugin = views_get_plugin('cache', $name);
515 if ($plugin) {
516 $plugin->init($this->view, $this->display);
517 return $plugin;
518 }
519 }
520
521 /**
522 * Get the handler object for a single handler.
523 */
524 function &get_handler($type, $id) {
525 if (!isset($this->handlers[$type])) {
526 $this->get_handlers($type);
527 }
528
529 if (isset($this->handlers[$type][$id])) {
530 return $this->handlers[$type][$id];
531 }
532
533 // So we can return a reference.
534 $null = NULL;
535 return $null;
536 }
537
538 /**
539 * Get a full array of handlers for $type. This caches them.
540 */
541 function get_handlers($type) {
542 if (!isset($this->handlers[$type])) {
543 $this->handlers[$type] = array();
544 $types = views_object_types();
545 $plural = $types[$type]['plural'];
546 foreach ($this->get_option($plural) as $id => $info) {
547 $handler = views_get_handler($info['table'], $info['field'], $type);
548 if ($handler) {
549 $handler->init($this->view, $info);
550 $this->handlers[$type][$id] = &$handler;
551 }
552
553 // Prevent reference problems.
554 unset($handler);
555 }
556 }
557
558 return $this->handlers[$type];
559 }
560
561 /**
562 * Intelligently set an option either from this display or from the
563 * default display, if directed to do so.
564 */
565 function set_option($option, $value) {
566 if ($this->is_defaulted($option)) {
567 return $this->default_display->set_option($option, $value);
568 }
569
570 // Set this in two places: On the handler where we'll notice it
571 // but also on the display object so it gets saved. This should
572 // only be a temporary fix.
573 $this->display->display_options[$option] = $value;
574 return $this->options[$option] = $value;
575 }
576
577 /**
578 * Set an option and force it to be an override.
579 */
580 function override_option($option, $value) {
581 $this->set_override($option, FALSE);
582 $this->set_option($option, $value);
583 }
584
585 /**
586 * Because forms may be split up into sections, this provides
587 * an easy URL to exactly the right section. Don't override this.
588 */
589 function option_link($text, $section, $class = '', $title = '') {
590 if (!empty($class)) {
591 $text = '<span>' . $text . '</span>';
592 }
593
594 if (!trim($text)) {
595 $text = t('Broken field');
596 }
597
598 if (empty($title)) {
599 $title = $text;
600 }
601
602 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));
603 }
604
605 /**
606 * Provide the default summary for options in the views UI.
607 *
608 * This output is returned as an array.
609 */
610 function options_summary(&$categories, &$options) {
611 $categories['basic'] = array(
612 'title' => t('Basic settings'),
613 );
614
615 $options['display_title'] = array(
616 'category' => 'basic',
617 'title' => t('Name'),
618 'value' => check_plain($this->display->display_title),
619 'desc' => t('Change the name of this display.'),
620 );
621
622 $title = strip_tags($this->get_option('title'));
623 if (!$title) {
624 $title = t('None');
625 }
626
627 $options['title'] = array(
628 'category' => 'basic',
629 'title' => t('Title'),
630 'value' => $title,
631 'desc' => t('Change the title that this display will use.'),
632 );
633
634 $style_plugin = views_fetch_plugin_data('style', $this->get_option('style_plugin'));
635 $style_title = empty($style_plugin['title']) ? t('Missing style plugin') : $style_plugin['title'];
636
637 $style = '';
638
639 $options['style_plugin'] = array(
640 'category' => 'basic',
641 'title' => t('Style'),
642 'value' => $style_title,
643 'desc' => t('Change the style plugin.'),
644 );
645
646 // This adds a 'Settings' link to the style_options setting if the style has options.
647 if (!empty($style_plugin['uses options'])) {
648 $options['style_plugin']['links']['style_options'] = t('Change settings for this style');
649 }
650
651 if (!empty($style_plugin['uses row plugin'])) {
652 $row_plugin = views_fetch_plugin_data('row', $this->get_option('row_plugin'));
653 $row_title = empty($row_plugin['title']) ? t('Missing style plugin') : $row_plugin['title'];
654
655 $options['row_plugin'] = array(
656 'category' => 'basic',
657 'title' => t('Row style'),
658 'value' => $row_title,
659 'desc' => t('Change the row plugin.'),
660 );
661 // This adds a 'Settings' link to the row_options setting if the row style has options.
662 if (!empty($row_plugin['uses options'])) {
663 $options['row_plugin']['links']['row_options'] = t('Change settings for this style');
664 }
665 }
666 if (!empty($this->definition['use ajax'])) {
667 $options['use_ajax'] = array(
668 'category' => 'basic',
669 'title' => t('Use AJAX'),
670 'value' => $this->get_option('use_ajax') ? t('Yes') : t('No'),
671 'desc' => t('Change whether or not this display will use AJAX.'),
672 );
673 }
674
675 if (!empty($this->definition['use pager'])) {
676 $options['use_pager'] = array(
677 'category' => 'basic',
678 'title' => t('Use pager'),
679 'value' => $this->get_option('use_pager') ? ($this->get_option('use_pager') === 'mini' ? t('Mini') : t('Yes')) : t('No'),
680 'desc' => t("Change this display's pager setting."),
681 );
682 }
683
684 $items = intval($this->get_option('items_per_page'));
685 $options['items_per_page'] = array(
686 'category' => 'basic',
687 'title' => $this->use_pager() ? t('Items per page') : t('Items to display'),
688 'value' => $items ? $items : t('Unlimited'),
689 'desc' => t('Change how many items to display.'),
690 );
691
692 if (!empty($this->definition['use more'])) {
693 $options['use_more'] = array(
694 'category' => 'basic',
695 'title' => t('More link'),
696 'value' => $this->get_option('use_more') ? t('Yes') : t('No'),
697 'desc' => t('Specify whether this display will provide a "more" link.'),
698 );
699 }
700
701 $options['distinct'] = array(
702 'category' => 'basic',
703 'title' => t('Distinct'),
704 'value' => $this->get_option('distinct') ? t('Yes') : t('No'),
705 'desc' => t('Display only distinct items, without duplicates.'),
706 );
707
708 $access_plugin = $this->get_access_plugin();
709 if (!$access_plugin) {
710 // default to the no access control plugin.
711 $access_plugin = views_get_plugin('access', 'none');
712 }
713
714 $access_str = $access_plugin->summary_title();
715
716 $options['access'] = array(
717 'category' => 'basic',
718 'title' => t('Access'),
719 'value' => $access_str,
720 'desc' => t('Specify access control type for this display.'),
721 );
722
723 if (!empty($access_plugin->definition['uses options'])) {
724 $options['access']['links']['access_options'] = t('Change settings for this access type.');
725 }
726
727 $cache_plugin = $this->get_cache_plugin();
728 if (!$cache_plugin) {
729 // default to the no cache control plugin.
730 $cache_plugin = views_get_plugin('cache', 'none');
731 }
732
733 $cache_str = $cache_plugin->summary_title();
734
735 $options['cache'] = array(
736 'category' => 'basic',
737 'title' => t('Caching'),
738 'value' => $cache_str,
739 'desc' => t('Specify caching type for this display.'),
740 );
741
742 if (!empty($cache_plugin->definition['uses options'])) {
743 $options['cache']['links']['cache_options'] = t('Change settings for this caching type.');
744 }
745
746 if ($this->uses_link_display()) {
747 // Only show the 'link display' if there is more than one option.
748 $count = 0;
749 foreach ($this->view->display as $display_id => $display) {
750 if (is_object($display->handler) && $display->handler->has_path()) {
751 $count++;
752 }
753 if ($count > 1) {
754 break;
755 }
756 }
757
758 if ($count > 1) {
759 $display_id = $this->get_link_display();
760 $link_display = empty($this->view->display[$display_id]) ? t('None') : check_plain($this->view->display[$display_id]->display_title);
761 $options['link_display'] = array(
762 'category' => 'basic',
763 'title' => t('Link display'),
764 'value' => $link_display,
765 'desc' => t('Specify which display this display will link to.'),
766 );
767 }
768 }
769
770 $options['exposed_block'] = array(
771 'category' => 'basic',
772 'title' => t('Exposed form in block'),
773 'value' => $this->get_option('exposed_block') ? t('Yes') : t('No'),
774 'desc' => t('Allow the exposed form to appear in a block instead of the view.'),
775 );
776
777 foreach (array('header' => t('Header'), 'footer' => t('Footer'), 'empty' => t('Empty text')) as $type => $name) {
778 if (!$this->get_option($type)) {
779 $field = t('None');
780 }
781 else {
782 // A lot of code to get the name of the filter format.
783 $fmt_string = $this->get_option($type . '_format');
784 if (empty($fmt_string)) {
785 $fmt_string = FILTER_FORMAT_DEFAULT;
786 }
787 $format_val = filter_resolve_format($fmt_string);
788 $format = filter_formats($format_val);
789 if ($format) {
790 $field = check_plain($format->name);
791 }
792 else {
793 $field = t('Unknown/missing format');
794 }
795 }
796
797 $options[$type] = array(
798 'category' => 'basic',
799 'title' => $name,
800 'value' => $field,
801 'desc' => t("Change this display's !name.", array('!name' => strtolower($name))),
802 );
803 }
804
805 $options['analyze-theme'] = array(
806 'category' => 'basic',
807 'title' => t('Theme'),
808 'value' => t('Information'),
809 'desc' => t('Get information on how to theme this display'),
810 );
811 }
812
813 /**
814 * Provide the default form for setting options.
815 */
816 function options_form(&$form, &$form_state) {
817 if ($this->defaultable_sections($form_state['section'])) {
818 $this->add_override_button($form, $form_state, $form_state['section']);
819 }
820 $form['#title'] = check_plain($this->display->display_title) . ': ';
821
822 // Set the 'section' to hilite on the form.
823 // If it's the item we're looking at is pulling from the default display,
824 // reflect that. Don't use is_defaulted since we want it to show up even
825 // on the default display.
826 if (!empty($this->options['defaults'][$form_state['section']])) {
827 $form['#section'] = 'default-' . $form_state['section'];
828 }
829 else {
830 $form['#section'] = $this->display->id . '-' . $form_state['section'];
831 }
832
833 switch ($form_state['section']) {
834 case 'display_title':
835 $form['#title'] .= t('The name of this display');
836 $form['display_title'] = array(
837 '#type' => 'textfield',
838 '#description' => t('This title will appear only in the administrative interface for the View.'),
839 '#default_value' => $this->display->display_title,
840 );
841 break;
842 case 'title':
843 $form['#title'] .= t('The title of this view');
844 $form['title'] = array(
845 '#type' => 'textfield',
846 '#description' => t('This title will be displayed with the view, wherever titles are normally displayed; i.e, as the page title, block title, etc.'),
847 '#default_value' => $this->get_option('title'),
848 );
849 break;
850 case 'use_ajax':
851 $form['#title'] .= t('Use AJAX when available to load this view');
852 $form['description'] = array(
853 '#prefix' => '<div class="description form-item">',
854 '#suffix' => '</div>',
855 '#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.'),
856 );
857 $form['use_ajax'] = array(
858 '#type' => 'radios',
859 '#options' => array(1 => t('Yes'), 0 => t('No')),
860 '#default_value' => $this->get_option('use_ajax') ? 1 : 0,
861 );
862 break;
863 case 'use_pager':
864 $form['#title'] .= t('Use a pager for this view');
865 $form['use_pager'] = array(
866 '#type' => 'radios',
867 '#options' => array(TRUE => t('Full pager'), 'mini' => t('Mini pager'), 0 => t('No')),
868 '#default_value' => $this->get_option('use_pager'),
869 );
870 $form['pager_element'] = array(
871 '#type' => 'textfield',
872 '#title' => t('Pager element'),
873 '#description' => t("Unless you're experiencing problems with pagers related to this view, you should leave this at 0. If using multiple pagers on one page you may need to set this number to a higher value so as not to conflict within the ?page= array. Large values will add a lot of commas to your URLs, so avoid if possible."),
874 '#default_value' => intval($this->get_option('pager_element')),
875 );
876 break;
877 case 'items_per_page':
878 $form['#title'] .= $this->use_pager() ? t('Items per page') : t('Items to display');
879
880 $form['items_per_page'] = array(
881 '#type' => 'textfield',
882 '#description' => t('The number of items to display per page. Enter 0 for no limit.'),
883 '#default_value' => intval($this->get_option('items_per_page')),
884 );
885 $form['offset'] = array(
886 '#type' => 'textfield',
887 '#title' => t('Offset'),
888 '#description' => t('The number of items to skip. For example, if this field is 3, the first 3 items will be skipped and not displayed. Offset can not be used if items to display is 0; instead use a very large number there.'),
889 '#default_value' => intval($this->get_option('offset')),
890 );
891 break;
892 case 'use_more':
893 $form['#title'] .= t('Add a more link to the bottom of the display.');
894 $form['use_more'] = array(
895 '#type' => 'checkbox',
896 '#title' => t('Create more link'),
897 '#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."),
898 '#default_value' => $this->get_option('use_more'),
899 );
900 $form['#title'] .= t('Text to use for the more link.');
901 $form['use_more_text'] = array(
902 '#type' => 'textfield',
903 '#title' => t('More link text'),
904 '#description' => t("The text to display for the more link."),
905 '#default_value' => $this->get_option('use_more_text'),
906 );
907 break;
908 case 'distinct':
909 $form['#title'] .= t('Display only distinct items, without duplicates.');
910 $form['distinct'] = array(
911 '#type' => 'checkbox',
912 '#title' => t('Distinct'),
913 '#description' => t('This will make the view display only distinct items. If there are multiple identical items, each will be displayed only once. You can use this to try and remove duplicates from a view, though it does not always work. Note that this can slow queries down, so use it with caution.'),
914 '#default_value' => $this->get_option('distinct'),
915 );
916 break;
917 case 'access':
918 $form['#title'] .= t('Access restrictions');
919 $form['access'] = array(
920 '#prefix' => '<div class="clear-block">',
921 '#suffix' => '</div>',
922 '#tree' => TRUE,
923 );
924
925 $access = $this->get_option('access');
926 $form['access']['type'] = array(
927 '#type' => 'radios',
928 '#options' => views_fetch_plugin_names('access'),
929 '#default_value' => $access['type'],
930 );
931
932 $access_plugin = views_fetch_plugin_data('access', $access['type']);
933 if (!empty($access_plugin['uses options'])) {
934 $form['markup'] = array(
935 '#prefix' => '<div class="form-item description">',
936 '#suffix' => '</div>',
937 '#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'))),
938 );
939 }
940
941 break;
942 case 'access_options':
943 $access = $this->get_option('access');
944 $plugin = $this->get_access_plugin();
945 $form['#title'] .= t('Access options');
946 if ($plugin) {
947 $form['#help_topic'] = $plugin->definition['help topic'];
948
949 $form['access_options'] = array(
950 '#tree' => TRUE,
951 );
952 $form['access_options']['type'] = array(
953 '#type' => 'value',
954 '#value' => $access['type'],
955 );
956 $plugin->options_form($form['access_options'], $form_state);
957 }
958 break;
959 case 'cache':
960 $form['#title'] .= t('Caching');
961 $form['cache'] = array(
962 '#prefix' => '<div class="clear-block">',
963 '#suffix' => '</div>',
964 '#tree' => TRUE,
965 );
966
967 $cache = $this->get_option('cache');
968 $form['cache']['type'] = array(
969 '#type' => 'radios',
970 '#options' => views_fetch_plugin_names('cache'),
971 '#default_value' => $cache['type'],
972 );
973
974 $cache_plugin = views_fetch_plugin_data('cache', $cache['type']);
975 if (!empty($cache_plugin['uses options'])) {
976 $form['markup'] = array(
977 '#prefix' => '<div class="form-item description">',
978 '#suffix' => '</div>',
979 '#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'))),
980 );
981 }
982 break;
983 case 'cache_options':
984 $cache = $this->get_option('cache');
985 $plugin = $this->get_cache_plugin();
986 $form['#title'] .= t('Caching options');
987 if ($plugin) {
988 $form['#help_topic'] = $plugin->definition['help topic'];
989
990 $form['cache_options'] = array(
991 '#tree' => TRUE,
992 );
993 $form['cache_options']['type'] = array(
994 '#type' => 'value',
995 '#value' => $cache['type'],
996 );
997 $plugin->options_form($form['cache_options'], $form_state);
998 }
999 break;
1000 case 'header':
1001 $form['#title'] .= t('Header');
1002 $form['header_empty'] = array(
1003 '#type' => 'checkbox',
1004 '#title' => t('Display even if view has no result'),
1005 '#default_value' => $this->get_option('header_empty'),
1006 );
1007 $form['header'] = array(
1008 '#type' => 'textarea',
1009 '#default_value' => $this->get_option('header'),
1010 '#rows' => 6,
1011 '#description' => t('Text to display at the top of the view. May contain an explanation or links or whatever you like. Optional.'),
1012 );
1013
1014 $form['header_format'] = filter_form($this->get_option('header_format'), NULL, array('header_format'));
1015 break;
1016 case 'footer':
1017 $form['#title'] .= t('Footer');
1018 $form['footer_empty'] = array(
1019 '#type' => 'checkbox',
1020 '#title' => t('Display even if view has no result'),
1021 '#default_value' => $this->get_option('footer_empty'),
1022 );
1023 $form['footer'] = array(
1024 '#type' => 'textarea',
1025 '#default_value' => $this->get_option('footer'),
1026 '#rows' => 6,
1027 '#description' => t('Text to display beneath the view. May contain an explanation or links or whatever you like. Optional.'),
1028 );
1029
1030 $form['footer_format'] = filter_form($this->get_option('footer_format'), NULL, array('footer_format'));
1031 break;
1032 case 'empty':
1033 $form['#title'] .= t('Empty text');
1034 $form['empty'] = array(
1035 '#type' => 'textarea',
1036 '#default_value' => $this->get_option('empty'),
1037 '#rows' => 6,
1038 '#description' => t('Text to display if the view has no results. Optional.'),
1039 );
1040
1041 $form['empty_format'] = filter_form($this->get_option('empty_format'), NULL, array('empty_format'));
1042 break;
1043 case 'style_plugin':
1044 $form['#title'] .= t('How should this view be styled');
1045 $form['#help_topic'] = 'style';
1046 $form['style_plugin'] = array(
1047 '#type' => 'radios',
1048 '#options' => views_fetch_plugin_names('style', $this->get_style_type(), array($this->view->base_table)),
1049 '#default_value' => $this->get_option('style_plugin'),
1050 '#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.'),
1051 );
1052
1053 $style_plugin = views_fetch_plugin_data('style', $this->get_option('style_plugin'));
1054 if (!empty($style_plugin['uses options'])) {
1055 $form['markup'] = array(
1056 '#prefix' => '<div class="form-item description">',
1057 '#suffix' => '</div>',
1058 '#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'))),
1059 );
1060 }
1061
1062 break;
1063 case 'style_options':
1064 $form['#title'] .= t('Style options');
1065 $style = TRUE;
1066 $type = 'style_plugin';
1067 $name = $this->get_option('style_plugin');
1068
1069 case 'row_options':
1070 if (!isset($name)) {
1071 $name = $this->get_option('row_plugin');
1072 }
1073 // if row, $style will be empty.
1074 if (empty($style)) {
1075 $form['#title'] .= t('Row style options');
1076 $type = 'row_plugin';
1077 }
1078 $plugin = $this->get_plugin(empty($style) ? 'row' : 'style');
1079 if ($plugin) {
1080 if (isset($plugin->definition['help topic'])) {
1081 $form['#help_topic'] = $plugin->definition['help topic'];
1082 }
1083 $form[$form_state['section']] = array(
1084 '#tree' => TRUE,
1085 );
1086 $plugin->options_form($form[$form_state['section']], $form_state);
1087 }
1088 break;
1089 case 'row_plugin':
1090 $form['#title'] .= t('How should each row in this view be styled');
1091 $form['#help_topic'] = 'style-row';
1092 $form['row_plugin'] = array(
1093 '#type' => 'radios',
1094 '#options' => views_fetch_plugin_names('row', $this->get_style_type(), array($this->view->base_table)),
1095 '#default_value' => $this->get_option('row_plugin'),
1096 );
1097
1098 $row_plugin = views_fetch_plugin_data('row', $this->get_option('row_plugin'));
1099 if (!empty($row_plugin['uses options'])) {
1100 $form['markup'] = array(
1101 '#prefix' => '<div class="form-item description">',
1102 '#suffix' => '</div>',
1103 '#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'))),
1104 );
1105 }
1106
1107 break;
1108 case 'link_display':
1109 $form['#title'] .= t('Which display to use for path');
1110 foreach ($this->view->display as $display_id => $display) {
1111 if ($display->handler->has_path()) {
1112 $options[$display_id] = $display->display_title;
1113 }
1114 }
1115 $form['link_display'] = array(
1116 '#type' => 'radios',
1117 '#options' => $options,
1118 '#description' => t("Which display to use to get this display's path for things like summary links, rss feed links, more links, etc."),
1119 '#default_value' => $this->get_link_display(),
1120 );
1121 break;
1122 case 'analyze-theme':
1123 $form['#title'] .= t('Theming information');
1124 $form['#help_topic'] = 'analyze-theme';
1125
1126 if (isset($_POST['theme'])) {
1127 $this->view->theme = $_POST['theme'];
1128 }
1129 else if (empty($this->view->theme)) {
1130 $this->view->theme = variable_get('theme_default', 'garland');
1131 }
1132
1133 global $custom_theme;
1134 $custom_theme = $this->view->theme;
1135 init_theme();
1136
1137 $funcs = array();
1138 // Get theme functions for the display. Note that some displays may
1139 // not have themes. The 'feed' display, for example, completely
1140 // delegates to the style.
1141 if (!empty($this->definition['theme'])) {
1142 $funcs[] = $this->option_link(t('Display output'), 'analyze-theme-display') . ': ' . $this->format_themes($this->theme_functions());
1143 $themes = $this->additional_theme_functions();
1144 if ($themes) {
1145 foreach ($themes as $theme) {
1146 $funcs[] = $this->option_link(t('Alternative display output'), 'analyze-theme-display') . ': ' . $this->format_themes($theme);
1147 }
1148 }
1149 }
1150
1151 $plugin = $this->get_plugin();
1152 if ($plugin) {
1153 $funcs[] = $this->option_link(t('Style output'), 'analyze-theme-style') . ': ' . $this->format_themes($plugin->theme_functions(), $plugin->additional_theme_functions());
1154 $themes = $plugin->additional_theme_functions();
1155 if ($themes) {
1156 foreach ($themes as $theme) {
1157 $funcs[] = $this->option_link(t('Alternative style'), 'analyze-theme-style') . ': ' . $this->format_themes($theme);
1158 }
1159 }
1160
1161 if ($plugin->uses_row_plugin()) {
1162 $row_plugin = $this->get_plugin('row');
1163 if ($row_plugin) {
1164 $funcs[] = $this->option_link(t('Row style output'), 'analyze-theme-row') . ': ' . $this->format_themes($row_plugin->theme_functions());
1165 $themes = $row_plugin->additional_theme_functions();
1166 if ($themes) {
1167 foreach ($themes as $theme) {
1168 $funcs[] = $this->option_link(t('Alternative row style'), 'analyze-theme-row') . ': ' . $this->format_themes($theme);
1169 }
1170 }
1171 }
1172 }
1173
1174 if ($plugin->uses_fields()) {
1175 foreach ($this->get_handlers('field') as $id => $handler) {
1176 $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());
1177 }
1178 }
1179 }
1180
1181 $form['important'] = array(
1182 '#prefix' => '<div class="form-item description">',
1183 '#suffix' => '</div>',
1184 '#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>',
1185 );
1186
1187 foreach (list_themes() as $key => $theme) {
1188 $options[$key] = $theme->info['name'];
1189 }
1190
1191 $form['box'] = array(
1192 '#prefix' => '<div class="container-inline">',
1193 '#suffix' => '</div>',
1194 );
1195 $form['box']['theme'] = array(
1196 '#type' => 'select',
1197 '#options' => $options,
1198 '#default_value' => $this->view->theme,
1199 );
1200
1201 $form['box']['change'] = array(
1202 '#type' => 'submit',
1203 '#value' => t('Change theme'),
1204 '#submit' => array('views_ui_edit_display_form_change_theme'),
1205 );
1206
1207 $form['analysis'] = array(
1208 '#prefix' => '<div class="form-item">',
1209 '#suffix' => '</div>',
1210 '#value' => theme('item_list', $funcs),
1211 );
1212
1213 $form['rescan_button'] = array(
1214 '#prefix' => '<div class="form-item">',
1215 '#suffix' => '</div>',
1216 );
1217 $form['rescan_button']['button'] = array(
1218 '#type' => 'submit',
1219 '#value' => t('Rescan template files'),
1220 '#submit' => array('views_ui_config_item_form_rescan'),
1221 );
1222 $form['rescan_button']['markup'] = array(
1223 '#prefix' => '<div class="description">',
1224 '#suffix' => '</div>',
1225 '#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."),
1226 );
1227
1228 $form_state['ok_button'] = TRUE;
1229 break;
1230 case 'analyze-theme-display':
1231 $form['#title'] .= t('Theming information (display)');
1232 $output = '<p>' . t('Back to !info.', array('!info' => $this->option_link(t('theming information'), 'analyze-theme'))) . '</p>';
1233
1234 if (empty($this->definition['theme'])) {
1235 $output .= t('This display has no theming information');
1236 }
1237 else {
1238 $output .= '<p>' . t('This is the default theme template used for this display.') . '</p>';
1239 $output .= '<pre>' . check_plain(file_get_contents('./' . $this->definition['theme path'] . '/' . strtr($this->definition['theme'], '_', '-') . '.tpl.php')) . '</pre>';
1240 }
1241
1242 if (!empty($this->definition['additional themes'])) {
1243 foreach ($this->definition['additional themes'] as $theme => $type) {
1244 $output .= '<p>' . t('This is an alternative template for this display.') . '</p>';
1245 $output .= '<pre>' . check_plain(file_get_contents('./' . $this->definition['theme path'] . '/' . strtr($theme, '_', '-') . '.tpl.php')) . '</pre>';
1246 }
1247 }
1248
1249 $form['analysis'] = array(
1250 '#prefix' => '<div class="form-item">',
1251 '#suffix' => '</div>',
1252 '#value' => $output,
1253 );
1254
1255 $form_state['ok_button'] = TRUE;
1256 break;
1257 case 'analyze-theme-style':
1258 $form['#title'] .= t('Theming information (style)');
1259 $output = '<p>' . t('Back to !info.', array('!info' => $this->option_link(t('theming information'), 'analyze-theme'))) . '</p>';
1260
1261 $plugin = $this->get_plugin();
1262
1263 if (empty($plugin->definition['theme'])) {
1264 $output .= t('This display has no style theming information');
1265 }
1266 else {
1267 $output .= '<p>' . t('This is the default theme template used for this style.') . '</p>';
1268 $output .= '<pre>' . check_plain(file_get_contents('./' . $plugin->definition['theme path'] . '/' . strtr($plugin->definition['theme'], '_', '-') . '.tpl.php')) . '</pre>';
1269 }
1270
1271 if (!empty($plugin->definition['additional themes'])) {
1272 foreach ($plugin->definition['additional themes'] as $theme => $type) {
1273 $output .= '<p>' . t('This is an alternative template for this style.') . '</p>';
1274 $output .= '<pre>' . check_plain(file_get_contents('./' . $plugin->definition['theme path'] . '/' . strtr($theme, '_', '-') . '.tpl.php')) . '</pre>';
1275 }
1276 }
1277
1278 $form['analysis'] = array(
1279 '#prefix' => '<div class="form-item">',
1280 '#suffix' => '</div>',
1281 '#value' => $output,
1282 );
1283
1284 $form_state['ok_button'] = TRUE;
1285 break;
1286 case 'analyze-theme-row':
1287 $form['#title'] .= t('Theming information (row style)');
1288 $output = '<p>' . t('Back to !info.', array('!info' => $this->option_link(t('theming information'), 'analyze-theme'))) . '</p>';
1289
1290 $plugin = $this->get_plugin('row');
1291
1292 if (empty($plugin->definition['theme'])) {
1293 $output .= t('This display has no row style theming information');
1294 }
1295 else {
1296 $output .= '<p>' . t('This is the default theme template used for this row style.') . '</p>';
1297 $output .= '<pre>' . check_plain(file_get_contents('./' . $plugin->definition['theme path'] . '/' . strtr($plugin->definition['theme'], '_', '-') . '.tpl.php')) . '</pre>';
1298 }
1299
1300 if (!empty($plugin->definition['additional themes'])) {
1301 foreach ($plugin->definition['additional themes'] as $theme => $type) {
1302 $output .= '<p>' . t('This is an alternative template for this row style.') . '</p>';
1303 $output .= '<pre>' . check_plain(file_get_contents('./' . $plugin->definition['theme path'] . '/' . strtr($theme, '_', '-') . '.tpl.php')) . '</pre>';
1304 }
1305 }
1306
1307 $form['analysis'] = array(
1308 '#prefix' => '<div class="form-item">',
1309 '#suffix' => '</div>',
1310 '#value' => $output,
1311 );
1312
1313 $form_state['ok_button'] = TRUE;
1314 break;
1315 case 'analyze-theme-field':
1316 $form['#title'] .= t('Theming information (row style)');
1317 $output = '<p>' . t('Back to !info.', array('!info' => $this->option_link(t('theming information'), 'analyze-theme'))) . '</p>';
1318
1319 $output .= '<p>' . t('This is the default theme template used for this row style.') . '</p>';
1320
1321 // Field templates aren't registered the normal way...and they're always
1322 // this one, anyhow.
1323 $output .= '<pre>' . check_plain(file_get_contents(drupal_get_path('module', 'views') . '/theme/views-view-field.tpl.php')) . '</pre>';
1324
1325 $form['analysis'] = array(
1326 '#prefix' => '<div class="form-item">',
1327 '#suffix' => '</div>',
1328 '#value' => $output,
1329 );
1330 $form_state['ok_button'] = TRUE;
1331 break;
1332
1333 case 'exposed_block':
1334 $form['#title'] .= t('Put the exposed form in a block');
1335 $form['description'] = array(
1336 '#prefix' => '<div class="description form-item">',
1337 '#suffix' => '</div>',
1338 '#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.'),
1339 );
1340 $form['exposed_block'] = array(
1341 '#type' => 'radios',
1342 '#options' => array(1 => t('Yes'), 0 => t('No')),
1343 '#default_value' => $this->get_option('exposed_block') ? 1 : 0,
1344 );
1345 break;
1346 }
1347 }
1348
1349 /**
1350 * Format a list of theme templates for output by the theme info helper.
1351 */
1352 function format_themes($themes) {
1353 $registry = theme_get_registry();
1354
1355 // Run through the theme engine variables, if necessary
1356 global $theme_engine;
1357 $extension = '.tpl.php';
1358 if (isset($theme_engine)) {
1359 $extension_function = $theme_engine . '_extension';
1360 if (function_exists($extension_function)) {
1361 $extension = $extension_function();
1362 }
1363 }
1364
1365 $output = '';
1366 $picked = FALSE;
1367 foreach ($themes as $theme) {
1368 $template = strtr($theme, '_', '-') . $extension;
1369 if (!$picked && !empty($registry[$theme])) {
1370 $template_path = isset($registry[$theme]['path']) ? $registry[$theme]['path'] . '/' : './';
1371 if (file_exists($template_path . $template)) {
1372 $hint = t('File found in folder @template-path', array('@template-path' => $template_path));
1373 $template = '<strong title="'. $hint .'">' . $template . '</strong>';
1374 }
1375 else {
1376 $template = '<strong class="error">' . $template . ' ' . t('(File not found, in folder @template-path)', array('@template-path' => $template_path)) . '</strong>';
1377 }
1378 $picked = TRUE;
1379 }
1380 $fixed[] = $template;
1381 }
1382
1383 return implode(', ', array_reverse($fixed));
1384 }
1385
1386 /**
1387 * Validate the options form.
1388 */
1389 function options_validate(&$form, &$form_state) {
1390 switch ($form_state['section']) {
1391 case 'style_options':
1392 $style = TRUE;
1393 case 'row_options':
1394 // if row, $style will be empty.
1395 $plugin = $this->get_plugin(empty($style) ? 'row' : 'style');
1396 if ($plugin) {
1397 $plugin->options_validate($form[$form_state['section']], $form_state);
1398 }
1399 break;
1400 case 'access_options':
1401 $plugin = $this->get_access_plugin();
1402 if ($plugin) {
1403 $plugin->options_validate($form['access_options'], $form_state);
1404 }
1405 break;
1406 case 'cache_options':
1407 $plugin = $this->get_cache_plugin();
1408 if ($plugin) {
1409 $plugin->options_validate($form['cache_options'], $form_state);
1410 }
1411 break;
1412 }
1413 }
1414
1415 /**
1416 * Perform any necessary changes to the form values prior to storage.
1417 * There is no need for this function to actually store the data.
1418 */
1419 function options_submit(&$form, &$form_state) {
1420 // Not sure I like this being here, but it seems (?) like a logical place.
1421 $cache_plugin = $this->get_cache_plugin();
1422 if ($cache_plugin) {
1423 $cache_plugin->cache_flush();
1424 }
1425
1426 $section = $form_state['section'];
1427 switch ($section) {
1428 case 'display_title':
1429 $this->display->display_title = $form_state['values']['display_title'];
1430 break;
1431 case 'access':
1432 $access = $this->get_option('access');
1433 if ($access['type'] != $form_state['values']['access']['type']) {
1434 $plugin = views_get_plugin('access', $form_state['values']['access']['type']);
1435 if ($plugin) {
1436 $access = array('type' => $form_state['values']['access']['type']);
1437 $plugin->option_defaults($access);
1438 $this->set_option('access', $access);
1439 if (!empty($plugin->definition['uses options'])) {
1440 views_ui_add_form_to_stack('display', $this->view, $this->display->id, array('access_options'));
1441 }
1442 }
1443 }
1444
1445 break;
1446 case 'access_options':
1447 $plugin = views_get_plugin('access', $form_state['values'][$section]['type']);
1448 if ($plugin) {
1449 $plugin->options_submit($form['access_options'], $form_state);
1450 $this->set_option('access', $form_state['values'][$section]);
1451 }
1452 break;
1453 case 'cache':
1454 $cache = $this->get_option('cache');
1455 if ($cache['type'] != $form_state['values']['cache']['type']) {
1456 $plugin = views_get_plugin('cache', $form_state['values']['cache']['type']);
1457 if ($plugin) {
1458 $cache = array('type' => $form_state['values']['cache']['type']);
1459 $plugin->option_defaults($cache);
1460 $this->set_option('cache', $cache);
1461 if (!empty($plugin->definition['uses options'])) {
1462 views_ui_add_form_to_stack('display', $this->view, $this->display->id, array('cache_options'));
1463 }
1464 }
1465 }
1466
1467 break;
1468 case 'cache_options':
1469 $plugin = views_get_plugin('cache', $form_state['values'][$section]['type']);
1470 if ($plugin) {
1471 $plugin->options_submit($form['cache_options'], $form_state);
1472 $this->set_option('cache', $form_state['values'][$section]);
1473 }
1474 break;
1475 case 'title':
1476 case 'link_display':
1477 $this->set_option($section, $form_state['values'][$section]);
1478 break;
1479 case 'use_ajax':
1480 $this->set_option($section, (bool)$form_state['values'][$section]);
1481 break;
1482 case 'use_pager':
1483 $this->set_option($section, $form_state['values'][$section]);
1484 $this->set_option('pager_element', intval($form_state['values']['pager_element']));
1485 break;
1486 case 'items_per_page':
1487 $this->set_option($section, intval($form_state['values'][$section]));
1488 $this->set_option('offset', intval($form_state['values']['offset']));
1489 break;
1490 case 'use_more':
1491 $this->set_option($section, intval($form_state['values'][$section]));
1492 $this->set_option('use_more_text', $form_state['values']['use_more_text']);
1493 case 'distinct':
1494 $this->set_option($section, $form_state['values'][$section]);
1495 break;
1496 case 'row_plugin':
1497 // This if prevents resetting options to default if they don't change
1498 // the plugin.
1499 if ($this->get_option($section) != $form_state['values'][$section]) {
1500 $plugin = views_get_plugin('row', $form_state['values'][$section]);
1501 if ($plugin) {
1502 $this->set_option($section, $form_state['values'][$section]);
1503 $this->set_option('row_options', array());
1504
1505 // send ajax form to options page if we use it.
1506 if (!empty($plugin->definition['uses options'])) {
1507 views_ui_add_form_to_stack('display', $this->view, $this->display->id, array('row_options'));
1508 }
1509 }
1510 }
1511 break;
1512 case 'style_plugin':
1513 // This if prevents resetting options to default if they don't change
1514 // the plugin.
1515 if ($this->get_option($section) != $form_state['values'][$section]) {
1516 $plugin = views_get_plugin('style', $form_state['values'][$section]);
1517 if ($plugin) {
1518 $this->set_option($section, $form_state['values'][$section]);
1519 $this->set_option('style_options', array());
1520 // send ajax form to options page if we use it.
1521 if (!empty($plugin->definition['uses options'])) {
1522 views_ui_add_form_to_stack('display', $this->view, $this->display->id, array('style_options'));
1523 }
1524 }
1525 }
1526 break;
1527 case 'style_options':
1528 $style = TRUE;
1529 case 'row_options':
1530 // if row, $style will be empty.
1531 $plugin = $this->get_plugin(empty($style) ? 'row' : 'style');
1532 if ($plugin) {
1533 $plugin->options_submit($form[$section], $form_state);
1534 }
1535 $this->set_option($section, $form_state['values'][$section]);
1536 break;
1537 case 'header':
1538 case 'footer':
1539 case 'empty':
1540 $this->set_option($section, $form_state['values'][$section]);
1541 $this->set_option($section . '_format', $form_state['values'][$section . '_format']);
1542 if ($section != 'empty') {
1543 $this->set_option($section . '_empty', $form_state['values'][$section . '_empty']);
1544 }
1545 break;
1546 case 'exposed_block':
1547 $this->set_option($section, (bool) $form_state['values'][$section]);
1548 break;
1549 }
1550 }
1551
1552 /**
1553 * Add an override button for a given section, allowing the user to
1554 * change whether this info is stored on the default display or on
1555 * the current display.
1556 */
1557 function add_override_button(&$form, &$form_state, $section) {
1558 if ($this->is_default_display()) {
1559 return;
1560 }
1561
1562 $form['override'] = array(
1563 '#prefix' => '<div class="views-override clear-block">',
1564 '#suffix' => '</div>',
1565 );
1566 if ($this->is_defaulted($section)) {
1567 $form['override']['button'] = array(
1568 '#type' => 'submit',
1569 '#value' => t('Override'),
1570 '#submit' => array('views_ui_edit_display_form_override'),
1571 );
1572 $form['override']['markup'] = array(
1573 '#prefix' => '<div class="description">',
1574 '#value' => theme('advanced_help_topic', 'views', 'overrides') . t('Status: using default values.'),
1575 '#suffix' => '</div>',
1576 );
1577
1578 $form_state['update_name'] = t('Update default display');
1579 }
1580 else {
1581 $form['override']['button'] = array(
1582 '#type' => 'submit',
1583 '#value' => t('Use default'),
1584 '#submit' => array('views_ui_edit_display_form_override'),
1585 );
1586 $form['override']['markup'] = array(
1587 '#prefix' => '<div class="description">',
1588 '#value' => theme('advanced_help_topic', 'views', 'overrides') . t('Status: using overridden values.'),
1589 '#suffix' => '</div>',
1590 );
1591
1592 $form_state['update_name'] = NULL;
1593 }
1594 }
1595
1596 /**
1597 * If override/revert was clicked, perform the proper toggle.
1598 */
1599 function options_override($form, &$form_state) {
1600 $this->set_override($form_state['section']);
1601 }
1602
1603 /**
1604 * Flip the override setting for the given section.
1605 */
1606 function set_override($section, $new_state = NULL) {
1607 $options = $this->defaultable_sections($section);
1608 if (!$options) {
1609 return;
1610 }
1611
1612 if (!isset($new_state)) {
1613 $new_state = empty($this->options['defaults'][$section]);
1614 }
1615
1616 // For each option that is part of this group, fix our settings.
1617 foreach ($options as $option) {
1618 if ($new_state) {
1619 // Revert to defaults.
1620 unset($this->options[$option]);
1621 unset($this->display->display_options[$option]);
1622 }
1623 else {
1624 // copy existing values into our display.
1625 $this->options[$option] = $this->get_option($option);
1626 $this->display->display_options[$option] = $this->options[$option];
1627 }
1628 $this->options['defaults'][$option] = $new_state;
1629 $this->display->display_options['defaults'][$option] = $new_state;
1630 }
1631 }
1632
1633 /**
1634 * Inject anything into the query that the display handler needs.
1635 */
1636 function query() {
1637 // Make the query distinct if the option was set.
1638 if ($this->get_option('distinct')) {
1639 $this->view->query->set_distinct();
1640 }
1641 }
1642
1643 /**
1644 * Not all display plugins will support filtering
1645 */
1646 function render_filters() { }
1647
1648 /**
1649 * Render the 'more' link
1650 */
1651 function render_more_link() {
1652 if ($this->use_more() && $this->view->total_rows > $this->view->pager['items_per_page']) {
1653 $path = $this->get_path();
1654 if ($path) {
1655 $path = $this->view->get_url(NULL, $path);
1656 $url_options = array();
1657 if (!empty($this->view->exposed_raw_input)) {
1658 $url_options['query'] = $this->view->exposed_raw_input;
1659 }
1660 $theme = views_theme_functions('views_more', $this->view, $this->display);
1661 $path = check_url(url($path, $url_options));
1662 return theme($theme, $path, $this->use_more_text());
1663 }
1664 }
1665 }
1666
1667 /**
1668 * Render a text area, using the proper format.
1669 */
1670 function render_textarea($area) {
1671 static $formats = array();
1672
1673 $value = $this->get_option($area);
1674 // Check to make sure the filter format exists; if not, we don't
1675 // display anything.
1676 $format = filter_resolve_format($this->get_option($area . '_format'));
1677
1678 if (!array_key_exists($format, $formats)) {
1679 $formats[$format] = db_result(db_query("SELECT name FROM {filter_formats} WHERE format = %d", $format));
1680 }
1681
1682 if (!$formats[$format]) {
1683 return;
1684 }
1685
1686 if ($value) {
1687 return check_markup($value, $format, FALSE);
1688 }
1689 }
1690
1691 /**
1692 * Render the header of the view.
1693 */
1694 function render_header() {
1695 if (!empty($this->view->result) || $this->get_option('header_empty')) {
1696 return $this->render_textarea('header');
1697 }
1698 }
1699
1700 /**
1701 * Render the footer of the view.
1702 */
1703 function render_footer() {
1704 if (!empty($this->view->result) || $this->get_option('footer_empty')) {
1705 return $this->render_textarea('footer');
1706 }
1707 }
1708
1709 /**
1710 * Render the empty text of the view.
1711 */
1712 function render_empty() { return $this->render_textarea('empty'); }
1713 /**
1714 * If this display creates a block, implement one of these.
1715 */
1716 function hook_block($op = 'list', $delta = 0, $edit = array()) { return array(); }
1717
1718 /**
1719 * If this display creates a page with a menu item, implement it here.
1720 */
1721 function hook_menu() { return array(); }
1722
1723 /**
1724 * Render this display.
1725 */
1726 function render() {
1727 return theme($this->theme_functions(), $this->view);
1728 }
1729
1730 /**
1731 * Determine if the user has access to this display of the view.
1732 */
1733 function access($account = NULL) {
1734 if (!isset($account)) {
1735 global $user;
1736 $account = $user;
1737 }
1738
1739 // Full override.
1740 if (user_access('access all views', $account)) {
1741 return TRUE;
1742 }
1743
1744 $plugin = $this->get_access_plugin();
1745 if ($plugin) {
1746 return $plugin->access($account);
1747 }
1748
1749 // fallback to all access if no plugin.
1750 return TRUE;
1751 }
1752
1753 /**
1754 * Set up any variables on the view prior to execution. These are separated
1755 * from execute because they are extremely common and unlikely to be
1756 * overridden on an individual display.
1757 */
1758 function pre_execute() {
1759 $this->view->set_use_ajax($this->use_ajax());
1760 // Copy pager information from the display.
1761 $this->view->set_use_pager($this->use_pager());
1762 $this->view->set_pager_element($this->get_option('pager_element'));
1763 $this->view->set_items_per_page($this->get_option('items_per_page'));
1764 $this->view->set_offset($this->get_option('offset'));
1765 if ($this->use_more()) {
1766 $this->view->get_total_rows = TRUE;
1767 }
1768 }
1769
1770 /**
1771 * When used externally, this is how a view gets run and returns
1772 * data in the format required.
1773 *
1774 * The base class cannot be executed.
1775 */
1776 function execute() { }
1777
1778 /**
1779 * Fully render the display for the purposes of a live preview or
1780 * some other AJAXy reason.
1781 */
1782 function preview() { return $this->view->render(); }
1783
1784 /**
1785 * Displays can require a certain type of style plugin. By default, they will
1786 * be 'normal'.
1787 */
1788 function get_style_type() { return 'normal'; }
1789
1790 /**
1791 * Make sure the display and all associated handlers are valid.
1792 *
1793 * @return
1794 * Empty array if the display is valid; an array of error strings if it is not.
1795 */
1796 function validate() {
1797 $errors = array();
1798 // Make sure displays that use fields HAVE fields.
1799 if ($this->uses_fields()) {
1800 $fields = FALSE;
1801 foreach ($this->get_handlers('field') as $field) {
1802 if (empty($field->options['exclude'])) {
1803 $fields = TRUE;
1804 }
1805 }
1806
1807 if (!$fields) {
1808 $errors[] = t('Display "@display" uses fields but there are none defined for it or all are excluded.', array('@display' => $this->display->display_title));
1809 }
1810 }
1811
1812 if ($this->has_path() && !$this->get_option('path')) {
1813 $errors[] = t('Display "@display" uses a path but the path is undefined.', array('@display' => $this->display->display_title));
1814 }
1815
1816 // Validate style plugin
1817 $style = $this->get_plugin();
1818 if (empty($style)) {
1819 $errors[] = t('Display "@display" has an invalid style plugin.', array('@display' => $this->display->display_title));
1820 }
1821 else {
1822 $result = $style->validate();
1823 if (!empty($result) && is_array($result)) {
1824 $errors = array_merge($errors, $result);
1825 }
1826 }
1827
1828 // Validate handlers
1829 foreach (views_object_types() as $type => $info) {
1830 foreach ($this->get_handlers($type) as $handler) {
1831 $result = $handler->validate();
1832 if (!empty($result) && is_array($result)) {
1833 $errors = array_merge($errors, $result);
1834 }
1835 }
1836 }
1837
1838 return $errors;
1839 }
1840
1841 /**
1842 * Provide the block system with any exposed widget blocks for this display.
1843 */
1844 function get_special_blocks() {
1845 $delta = '-exp-' . $this->view->name . '-' . $this->display->id;
1846 $desc = t('Exposed form: @view-@display_id', array('@view' => $this->view->name, '@display_id' => $this->display->id));
1847
1848 return array(
1849 $delta => array(
1850 'info' => $desc,
1851 )
1852 );
1853 }
1854
1855 /**
1856 * Render any special blocks provided for this display.
1857 */
1858 function view_special_blocks($type) {
1859 if ($type == '-exp') {
1860 // avoid interfering with the admin forms.
1861 if (arg(0) == 'admin' && arg(1) == 'build' && arg(2) == 'views') {
1862 return;
1863 }
1864 $this->view->init_handlers();
1865 return array(
1866 'content' => $this->view->render_exposed_form(TRUE),
1867 );
1868 }
1869 }
1870
1871 }
1872
1873
1874 /**
1875 * @}
1876 */
1877