Issue #2208015 by tim.plunkett: Remove "convert to trait" comments about EntityWithPl...
[project/drupal.git] / core / modules / views / lib / Drupal / views / ViewExecutable.php
1 <?php
2
3 /**
4 * @file
5 * Definition of Drupal\views\ViewExecutable.
6 */
7
8 namespace Drupal\views;
9
10 use Drupal\Core\DependencyInjection\DependencySerialization;
11 use Drupal\Core\Session\AccountInterface;
12 use Drupal\views\Plugin\views\query\QueryPluginBase;
13 use Drupal\views\ViewStorageInterface;
14 use Drupal\Component\Utility\Tags;
15 use Symfony\Component\HttpFoundation\Request;
16 use Symfony\Component\HttpFoundation\Response;
17
18 /**
19 * @defgroup views_objects Objects that represent a View or part of a view
20 * @{
21 * These objects are the core of Views do the bulk of the direction and
22 * storing of data. All database activity is in these objects.
23 */
24
25 /**
26 * An object to contain all of the data to generate a view, plus the member
27 * functions to build the view query, execute the query and render the output.
28 */
29 class ViewExecutable extends DependencySerialization {
30
31 /**
32 * The config entity in which the view is stored.
33 *
34 * @var \Drupal\views\Entity\View
35 */
36 public $storage;
37
38 /**
39 * Whether or not the view has been built.
40 *
41 * @todo Group with other static properties.
42 *
43 * @var bool
44 */
45 public $built = FALSE;
46
47 /**
48 * Whether the view has been executed/query has been run.
49 *
50 * @todo Group with other static properties.
51 *
52 * @var bool
53 */
54 public $executed = FALSE;
55
56 /**
57 * Any arguments that have been passed into the view.
58 *
59 * @var array
60 */
61 public $args = array();
62
63 /**
64 * An array of build info.
65 *
66 * @var array
67 */
68 public $build_info = array();
69
70 /**
71 * Whether this view uses AJAX.
72 *
73 * @var bool
74 */
75 protected $ajaxEnabled = FALSE;
76
77 /**
78 * Where the results of a query will go.
79 *
80 * The array must use a numeric index starting at 0.
81 *
82 * @var \Drupal\views\ResultRow[]
83 */
84 public $result = array();
85
86 // May be used to override the current pager info.
87
88 /**
89 * The current page. If the view uses pagination.
90 *
91 * @var int
92 */
93 public $current_page = NULL;
94
95 /**
96 * The number of items per page.
97 *
98 * @var int
99 */
100 public $items_per_page = NULL;
101
102 /**
103 * The pager offset.
104 *
105 * @var int
106 */
107 public $offset = NULL;
108
109 /**
110 * The total number of rows returned from the query.
111 *
112 * @var int
113 */
114 public $total_rows = NULL;
115
116 /**
117 * Attachments to place before the view.
118 *
119 * @var array()
120 */
121 public $attachment_before = array();
122
123 /**
124 * Attachments to place after the view.
125 *
126 * @var array
127 */
128 public $attachment_after = array();
129
130 // Exposed widget input
131
132 /**
133 * All the form data from $form_state['values'].
134 *
135 * @var array
136 */
137 public $exposed_data = array();
138
139 /**
140 * An array of input values from exposed forms.
141 *
142 * @var array
143 */
144 public $exposed_input = array();
145
146 /**
147 * Exposed widget input directly from the $form_state['values'].
148 *
149 * @var array
150 */
151 public $exposed_raw_input = array();
152
153 /**
154 * Used to store views that were previously running if we recurse.
155 *
156 * @var array
157 */
158 public $old_view = array();
159
160 /**
161 * To avoid recursion in views embedded into areas.
162 *
163 * @var array
164 */
165 public $parent_views = array();
166
167 /**
168 * Whether this view is an attachment to another view.
169 *
170 * @var bool
171 */
172 public $is_attachment = NULL;
173
174 /**
175 * Identifier of the current display.
176 *
177 * @var string
178 */
179 public $current_display;
180
181 /**
182 * Where the $query object will reside.
183 *
184 * @var \Drupal\views\Plugin\views\query\QueryPluginBase
185 */
186 public $query = NULL;
187
188 /**
189 * The used pager plugin used by the current executed view.
190 *
191 * @var \Drupal\views\Plugin\views\pager\PagerPluginBase
192 */
193 public $pager = NULL;
194
195 /**
196 * The current used display plugin.
197 *
198 * @var \Drupal\views\Plugin\views\display\DisplayPluginBase
199 */
200 public $display_handler;
201
202 /**
203 * The list of used displays of the view.
204 *
205 * An array containing Drupal\views\Plugin\views\display\DisplayPluginBase
206 * objects.
207 *
208 * @var \Drupal\views\DisplayBag
209 */
210 public $displayHandlers;
211
212 /**
213 * The current used style plugin.
214 *
215 * @var \Drupal\views\Plugin\views\style\StylePluginBase
216 */
217 public $style_plugin;
218
219 /**
220 * The current used row plugin, if the style plugin supports row plugins.
221 *
222 * @var \Drupal\views\Plugin\views\row\RowPluginBase
223 */
224 public $rowPlugin;
225
226 /**
227 * Stores the current active row while rendering.
228 *
229 * @var int
230 */
231 public $row_index;
232
233 /**
234 * Allow to override the url of the current view.
235 *
236 * @var string
237 */
238 public $override_url = NULL;
239
240 /**
241 * Allow to override the path used for generated urls.
242 *
243 * @var string
244 */
245 public $override_path = NULL;
246
247 /**
248 * Allow to override the used database which is used for this query.
249 *
250 * @var bool
251 */
252 public $base_database = NULL;
253
254 // Handlers which are active on this view.
255
256 /**
257 * Stores the field handlers which are initialized on this view.
258 *
259 * An array containing Drupal\views\Plugin\views\field\FieldPluginBase
260 * objects.
261 *
262 * @var array
263 */
264 public $field;
265
266 /**
267 * Stores the argument handlers which are initialized on this view.
268 *
269 * An array containing Drupal\views\Plugin\views\argument\ArgumentPluginBase
270 * objects.
271 *
272 * @var array
273 */
274 public $argument;
275
276 /**
277 * Stores the sort handlers which are initialized on this view.
278 *
279 * An array containing Drupal\views\Plugin\views\sort\SortPluginBase objects.
280 *
281 * @var array
282 */
283 public $sort;
284
285 /**
286 * Stores the filter handlers which are initialized on this view.
287 *
288 * An array containing Drupal\views\Plugin\views\filter\FilterPluginBase
289 * objects.
290 *
291 * @var array
292 */
293 public $filter;
294
295 /**
296 * Stores the relationship handlers which are initialized on this view.
297 *
298 * An array containing Drupal\views\Plugin\views\relationship\RelationshipPluginBase
299 * objects.
300 *
301 * @var array
302 */
303 public $relationship;
304
305 /**
306 * Stores the area handlers for the header which are initialized on this view.
307 *
308 * An array containing Drupal\views\Plugin\views\area\AreaPluginBase objects.
309 *
310 * @var array
311 */
312 public $header;
313
314 /**
315 * Stores the area handlers for the footer which are initialized on this view.
316 *
317 * An array containing Drupal\views\Plugin\views\area\AreaPluginBase objects.
318 *
319 * @var array
320 */
321 public $footer;
322
323 /**
324 * Stores the area handlers for the empty text which are initialized on this view.
325 *
326 * An array containing Drupal\views\Plugin\views\area\AreaPluginBase objects.
327 *
328 * @var array
329 */
330 public $empty;
331
332 /**
333 * Stores the current response object.
334 *
335 * @var \Symfony\Component\HttpFoundation\Response
336 */
337 protected $response = NULL;
338
339 /**
340 * Stores the current request object.
341 *
342 * @var \Symfony\Component\HttpFoundation\Request
343 */
344 protected $request;
345
346 /**
347 * Does this view already have loaded it's handlers.
348 *
349 * @todo Group with other static properties.
350 *
351 * @var bool
352 */
353 public $inited;
354
355 /**
356 * The rendered output of the exposed form.
357 *
358 * @var string
359 */
360 public $exposed_widgets;
361
362 /**
363 * If this view has been previewed.
364 *
365 * @var bool
366 */
367 public $preview;
368
369 /**
370 * Force the query to calculate the total number of results.
371 *
372 * @todo Move to the query.
373 *
374 * @var bool
375 */
376 public $get_total_rows;
377
378 /**
379 * Indicates if the sorts have been built.
380 *
381 * @todo Group with other static properties.
382 *
383 * @var bool
384 */
385 public $build_sort;
386
387 /**
388 * Stores the many-to-one tables for performance.
389 *
390 * @var array
391 */
392 public $many_to_one_tables;
393
394 /**
395 * A unique identifier which allows to update multiple views output via js.
396 *
397 * @var string
398 */
399 public $dom_id;
400
401 /**
402 * A render array container to store render related information.
403 *
404 * For example you can alter the array and attach some css/js via the
405 * #attached key. This is the required way to add custom css/js.
406 *
407 * @var array
408 *
409 * @see drupal_process_attached
410 */
411 public $element = array(
412 '#attached' => array(
413 'css' => array(),
414 'js' => array(),
415 'library' => array(),
416 ),
417 );
418
419 /**
420 * The current user.
421 *
422 * @var \Drupal\Core\Session\AccountInterface
423 */
424 protected $user;
425
426 /**
427 * Should the admin links be shown on the rendered view.
428 *
429 * @var bool
430 */
431 protected $showAdminLinks;
432
433 /**
434 * Constructs a new ViewExecutable object.
435 *
436 * @param \Drupal\views\ViewStorageInterface $storage
437 * The view config entity the actual information is stored on.
438 * @param \Drupal\Core\Session\AccountInterface $user
439 * The current user.
440 */
441 public function __construct(ViewStorageInterface $storage, AccountInterface $user) {
442 // Reference the storage and the executable to each other.
443 $this->storage = $storage;
444 $this->storage->set('executable', $this);
445 $this->user = $user;
446
447 // Add the default css for a view.
448 $this->element['#attached']['library'][] = 'views/views.module';
449 }
450
451 /**
452 * @todo.
453 */
454 public function save() {
455 $this->storage->save();
456 }
457
458 /**
459 * Set the arguments that come to this view. Usually from the URL
460 * but possibly from elsewhere.
461 */
462 public function setArguments($args) {
463 $this->args = $args;
464 }
465
466 /**
467 * Change/Set the current page for the pager.
468 */
469 public function setCurrentPage($page) {
470 $this->current_page = $page;
471
472 // If the pager is already initialized, pass it through to the pager.
473 if (!empty($this->pager)) {
474 return $this->pager->setCurrentPage($page);
475 }
476 }
477
478 /**
479 * Get the current page from the pager.
480 */
481 public function getCurrentPage() {
482 // If the pager is already initialized, pass it through to the pager.
483 if (!empty($this->pager)) {
484 return $this->pager->getCurrentPage();
485 }
486
487 if (isset($this->current_page)) {
488 return $this->current_page;
489 }
490 }
491
492 /**
493 * Get the items per page from the pager.
494 */
495 public function getItemsPerPage() {
496 // If the pager is already initialized, pass it through to the pager.
497 if (!empty($this->pager)) {
498 return $this->pager->getItemsPerPage();
499 }
500
501 if (isset($this->items_per_page)) {
502 return $this->items_per_page;
503 }
504 }
505
506 /**
507 * Set the items per page on the pager.
508 */
509 public function setItemsPerPage($items_per_page) {
510 $this->items_per_page = $items_per_page;
511
512 // If the pager is already initialized, pass it through to the pager.
513 if (!empty($this->pager)) {
514 $this->pager->setItemsPerPage($items_per_page);
515 }
516 }
517
518 /**
519 * Get the pager offset from the pager.
520 */
521 public function getOffset() {
522 // If the pager is already initialized, pass it through to the pager.
523 if (!empty($this->pager)) {
524 return $this->pager->getOffset();
525 }
526
527 if (isset($this->offset)) {
528 return $this->offset;
529 }
530 }
531
532 /**
533 * Set the offset on the pager.
534 */
535 public function setOffset($offset) {
536 $this->offset = $offset;
537
538 // If the pager is already initialized, pass it through to the pager.
539 if (!empty($this->pager)) {
540 $this->pager->setOffset($offset);
541 }
542 }
543
544 /**
545 * Determine if the pager actually uses a pager.
546 */
547 public function usePager() {
548 if (!empty($this->pager)) {
549 return $this->pager->usePager();
550 }
551 }
552
553 /**
554 * Sets whether or not AJAX should be used.
555 *
556 * If AJAX is used, paging, tablesorting and exposed filters will be fetched
557 * via an AJAX call rather than a page refresh.
558 *
559 * @param bool $use_ajax
560 * TRUE if AJAX should be used, FALSE otherwise.
561 */
562 public function setAjaxEnabled($ajax_enabled) {
563 $this->ajaxEnabled = (bool) $ajax_enabled;
564 }
565
566 /**
567 * Whether or not AJAX should be used.
568 *
569 * @see \Drupal\views\ViewExecutable::setAjaxEnabled().
570 *
571 * @return bool
572 */
573 public function ajaxEnabled() {
574 return $this->ajaxEnabled;
575 }
576
577 /**
578 * Set the exposed filters input to an array. If unset they will be taken
579 * from \Drupal::request()->query when the time comes.
580 */
581 public function setExposedInput($filters) {
582 $this->exposed_input = $filters;
583 }
584
585 /**
586 * Figure out what the exposed input for this view is.
587 */
588 public function getExposedInput() {
589 // Fill our input either from \Drupal::request()->query or from something
590 // previously set on the view.
591 if (empty($this->exposed_input)) {
592 $this->exposed_input = \Drupal::request()->query->all();
593 // unset items that are definitely not our input:
594 foreach (array('page', 'q') as $key) {
595 if (isset($this->exposed_input[$key])) {
596 unset($this->exposed_input[$key]);
597 }
598 }
599
600 // If we have no input at all, check for remembered input via session.
601
602 // If filters are not overridden, store the 'remember' settings on the
603 // default display. If they are, store them on this display. This way,
604 // multiple displays in the same view can share the same filters and
605 // remember settings.
606 $display_id = ($this->display_handler->isDefaulted('filters')) ? 'default' : $this->current_display;
607
608 if (empty($this->exposed_input) && !empty($_SESSION['views'][$this->storage->id()][$display_id])) {
609 $this->exposed_input = $_SESSION['views'][$this->storage->id()][$display_id];
610 }
611 }
612
613 return $this->exposed_input;
614 }
615
616 /**
617 * Set the display for this view and initialize the display handler.
618 */
619 public function initDisplay() {
620 if (isset($this->current_display)) {
621 return TRUE;
622 }
623
624 // Initialize the display cache array.
625 $this->displayHandlers = new DisplayBag($this, Views::pluginManager('display'));
626
627 $this->current_display = 'default';
628 $this->display_handler = $this->displayHandlers->get('default');
629
630 return TRUE;
631 }
632
633 /**
634 * Get the first display that is accessible to the user.
635 *
636 * @param array|string $displays
637 * Either a single display id or an array of display ids.
638 *
639 * @return string
640 * The first accessible display id, at least default.
641 */
642 public function chooseDisplay($displays) {
643 if (!is_array($displays)) {
644 return $displays;
645 }
646
647 $this->initDisplay();
648
649 foreach ($displays as $display_id) {
650 if ($this->displayHandlers->get($display_id)->access($this->user)) {
651 return $display_id;
652 }
653 }
654
655 return 'default';
656 }
657
658 /**
659 * Gets the current display plugin.
660 *
661 * @return \Drupal\views\Plugin\views\display\DisplayPluginBase
662 */
663 public function getDisplay() {
664 if (!isset($this->display_handler)) {
665 $this->initDisplay();
666 }
667
668 return $this->display_handler;
669 }
670
671 /**
672 * Sets the current display.
673 *
674 * @param string $display_id
675 * The ID of the display to mark as current.
676 *
677 * @return bool
678 * TRUE if the display was correctly set, FALSE otherwise.
679 */
680 public function setDisplay($display_id = NULL) {
681 // If we have not already initialized the display, do so.
682 if (!isset($this->current_display)) {
683 // This will set the default display and instantiate the default display
684 // plugin.
685 $this->initDisplay();
686 }
687
688 // If no display ID is passed, we either have initialized the default or
689 // already have a display set.
690 if (!isset($display_id)) {
691 return TRUE;
692 }
693
694 $display_id = $this->chooseDisplay($display_id);
695
696 // Ensure the requested display exists.
697 if (!$this->displayHandlers->has($display_id)) {
698 debug(format_string('setDisplay() called with invalid display ID "@display".', array('@display' => $display_id)));
699 return FALSE;
700 }
701
702 // Reset if the display has changed. It could be called multiple times for
703 // the same display, especially in the UI.
704 if ($this->current_display != $display_id) {
705 // Set the current display.
706 $this->current_display = $display_id;
707
708 // Reset the style and row plugins.
709 $this->style_plugin = NULL;
710 $this->plugin_name = NULL;
711 $this->rowPlugin = NULL;
712 }
713
714 if ($display = $this->displayHandlers->get($display_id)) {
715 // Set a shortcut.
716 $this->display_handler = $display;
717 return TRUE;
718 }
719
720 return FALSE;
721 }
722
723 /**
724 * Creates a new display and a display handler instance for it.
725 *
726 * @param string $plugin_id
727 * (optional) The plugin type from the Views plugin annotation. Defaults to
728 * 'page'.
729 * @param string $title
730 * (optional) The title of the display. Defaults to NULL.
731 * @param string $id
732 * (optional) The ID to use, e.g., 'default', 'page_1', 'block_2'. Defaults
733 * to NULL.
734 *
735 * @return \Drupal\views\Plugin\views\display\DisplayPluginBase
736 * A new display plugin instance if executable is set, the new display ID
737 * otherwise.
738 */
739 public function newDisplay($plugin_id = 'page', $title = NULL, $id = NULL) {
740 $this->initDisplay();
741
742 $id = $this->storage->addDisplay($plugin_id, $title, $id);
743 $this->displayHandlers->addInstanceId($id);
744
745 $display = $this->displayHandlers->get($id);
746 $display->newDisplay();
747 return $display;
748 }
749
750 /**
751 * Gets the current style plugin.
752 *
753 * @return \Drupal\views\Plugin\views\style\StylePluginBase
754 */
755 public function getStyle() {
756 if (!isset($this->style_plugin)) {
757 $this->initStyle();
758 }
759
760 return $this->style_plugin;
761 }
762
763 /**
764 * Find and initialize the style plugin.
765 *
766 * Note that arguments may have changed which style plugin we use, so
767 * check the view object first, then ask the display handler.
768 */
769 public function initStyle() {
770 if (isset($this->style_plugin)) {
771 return TRUE;
772 }
773
774 $this->style_plugin = $this->display_handler->getPlugin('style');
775
776 if (empty($this->style_plugin)) {
777 return FALSE;
778 }
779
780 return TRUE;
781 }
782
783 /**
784 * Acquire and attach all of the handlers.
785 */
786 public function initHandlers() {
787 $this->initDisplay();
788 if (empty($this->inited)) {
789 foreach ($this::getHandlerTypes() as $key => $info) {
790 $this->_initHandler($key, $info);
791 }
792 $this->inited = TRUE;
793 }
794 }
795
796 /**
797 * Get the current pager plugin.
798 *
799 * @return \Drupal\views\Plugin\views\pager\PagerPluginBase
800 */
801 public function getPager() {
802 if (!isset($this->pager)) {
803 $this->initPager();
804 }
805
806 return $this->pager;
807 }
808
809 /**
810 * Initialize the pager
811 *
812 * Like style initialization, pager initialization is held until late
813 * to allow for overrides.
814 */
815 public function initPager() {
816 if (!isset($this->pager)) {
817 $this->pager = $this->display_handler->getPlugin('pager');
818
819 if ($this->pager->usePager()) {
820 $this->pager->setCurrentPage($this->current_page);
821 }
822
823 // These overrides may have been set earlier via $view->set_*
824 // functions.
825 if (isset($this->items_per_page)) {
826 $this->pager->setItemsPerPage($this->items_per_page);
827 }
828
829 if (isset($this->offset)) {
830 $this->pager->setOffset($this->offset);
831 }
832 }
833 }
834
835 /**
836 * Render the pager, if necessary.
837 */
838 public function renderPager($exposed_input) {
839 if (!empty($this->pager) && $this->pager->usePager()) {
840 return $this->pager->render($exposed_input);
841 }
842
843 return '';
844 }
845
846 /**
847 * Create a list of base tables eligible for this view. Used primarily
848 * for the UI. Display must be already initialized.
849 */
850 public function getBaseTables() {
851 $base_tables = array(
852 $this->storage->get('base_table') => TRUE,
853 '#global' => TRUE,
854 );
855
856 foreach ($this->display_handler->getHandlers('relationship') as $handler) {
857 $base_tables[$handler->definition['base']] = TRUE;
858 }
859 return $base_tables;
860 }
861
862 /**
863 * Run the preQuery() on all active handlers.
864 */
865 protected function _preQuery() {
866 foreach ($this::getHandlerTypes() as $key => $info) {
867 $handlers = &$this->$key;
868 $position = 0;
869 foreach ($handlers as $id => $handler) {
870 $handlers[$id]->position = $position;
871 $handlers[$id]->preQuery();
872 $position++;
873 }
874 }
875 }
876
877 /**
878 * Run the postExecute() on all active handlers.
879 */
880 protected function _postExecute() {
881 foreach ($this::getHandlerTypes() as $key => $info) {
882 $handlers = &$this->$key;
883 foreach ($handlers as $id => $handler) {
884 $handlers[$id]->postExecute($this->result);
885 }
886 }
887 }
888
889 /**
890 * Attach all of the handlers for each type.
891 *
892 * @param $key
893 * One of 'argument', 'field', 'sort', 'filter', 'relationship'
894 * @param $info
895 * The $info from getHandlerTypes for this object.
896 */
897 protected function _initHandler($key, $info) {
898 // Load the requested items from the display onto the object.
899 $this->$key = $this->display_handler->getHandlers($key);
900
901 // This reference deals with difficult PHP indirection.
902 $handlers = &$this->$key;
903
904 // Run through and test for accessibility.
905 foreach ($handlers as $id => $handler) {
906 if (!$handler->access($this->user)) {
907 unset($handlers[$id]);
908 }
909 }
910 }
911
912 /**
913 * Build all the arguments.
914 */
915 protected function _buildArguments() {
916 // Initially, we want to build sorts and fields. This can change, though,
917 // if we get a summary view.
918 if (empty($this->argument)) {
919 return TRUE;
920 }
921
922 // build arguments.
923 $position = -1;
924 $substitutions = array();
925 $status = TRUE;
926
927 // Get the title.
928 $title = $this->display_handler->getOption('title');
929
930 // Iterate through each argument and process.
931 foreach ($this->argument as $id => $arg) {
932 $position++;
933 $argument = $this->argument[$id];
934
935 if ($argument->broken()) {
936 continue;
937 }
938
939 $argument->setRelationship();
940
941 $arg = isset($this->args[$position]) ? $this->args[$position] : NULL;
942 $argument->position = $position;
943
944 if (isset($arg) || $argument->hasDefaultArgument()) {
945 if (!isset($arg)) {
946 $arg = $argument->getDefaultArgument();
947 // make sure default args get put back.
948 if (isset($arg)) {
949 $this->args[$position] = $arg;
950 }
951 // remember that this argument was computed, not passed on the URL.
952 $argument->is_default = TRUE;
953 }
954
955 // Set the argument, which will also validate that the argument can be set.
956 if (!$argument->setArgument($arg)) {
957 $status = $argument->validateFail($arg);
958 break;
959 }
960
961 if ($argument->isException()) {
962 $arg_title = $argument->exceptionTitle();
963 }
964 else {
965 $arg_title = $argument->getTitle();
966 $argument->query($this->display_handler->useGroupBy());
967 }
968
969 // Add this argument's substitution
970 $substitutions['%' . ($position + 1)] = $arg_title;
971 $substitutions['!' . ($position + 1)] = strip_tags(decode_entities($arg));
972
973 // Test to see if we should use this argument's title
974 if (!empty($argument->options['title_enable']) && !empty($argument->options['title'])) {
975 $title = $argument->options['title'];
976 }
977 }
978 else {
979 // determine default condition and handle.
980 $status = $argument->defaultAction();
981 break;
982 }
983
984 // Be safe with references and loops:
985 unset($argument);
986 }
987
988 // set the title in the build info.
989 if (!empty($title)) {
990 $this->build_info['title'] = $title;
991 }
992
993 // Store the arguments for later use.
994 $this->build_info['substitutions'] = $substitutions;
995
996 return $status;
997 }
998
999 /**
1000 * Gets the current query plugin.
1001 *
1002 * @return \Drupal\views\Plugin\views\query\QueryPluginBase
1003 */
1004 public function getQuery() {
1005 if (!isset($this->query)) {
1006 $this->initQuery();
1007 }
1008
1009 return $this->query;
1010 }
1011
1012 /**
1013 * Do some common building initialization.
1014 */
1015 public function initQuery() {
1016 if (!empty($this->query)) {
1017 $class = get_class($this->query);
1018 if ($class && $class != 'stdClass') {
1019 // return if query is already initialized.
1020 return TRUE;
1021 }
1022 }
1023
1024 // Create and initialize the query object.
1025 $views_data = Views::viewsData()->get($this->storage->get('base_table'));
1026 $this->storage->set('base_field', !empty($views_data['table']['base']['field']) ? $views_data['table']['base']['field'] : '');
1027 if (!empty($views_data['table']['base']['database'])) {
1028 $this->base_database = $views_data['table']['base']['database'];
1029 }
1030
1031 $this->query = $this->display_handler->getPlugin('query');
1032 return TRUE;
1033 }
1034
1035 /**
1036 * Build the query for the view.
1037 */
1038 public function build($display_id = NULL) {
1039 if (!empty($this->built)) {
1040 return;
1041 }
1042
1043 if (empty($this->current_display) || $display_id) {
1044 if (!$this->setDisplay($display_id)) {
1045 return FALSE;
1046 }
1047 }
1048
1049 // Let modules modify the view just prior to building it.
1050 $module_handler = \Drupal::moduleHandler();
1051 $module_handler->invokeAll('views_pre_build', array($this));
1052
1053 // Attempt to load from cache.
1054 // @todo Load a build_info from cache.
1055
1056 $start = microtime(TRUE);
1057 // If that fails, let's build!
1058 $this->build_info = array(
1059 'query' => '',
1060 'count_query' => '',
1061 'query_args' => array(),
1062 );
1063
1064 $this->initQuery();
1065
1066 // Call a module hook and see if it wants to present us with a
1067 // pre-built query or instruct us not to build the query for
1068 // some reason.
1069 // @todo: Implement this. Use the same mechanism Panels uses.
1070
1071 // Run through our handlers and ensure they have necessary information.
1072 $this->initHandlers();
1073
1074 // Let the handlers interact with each other if they really want.
1075 $this->_preQuery();
1076
1077 if ($this->display_handler->usesExposed()) {
1078 $exposed_form = $this->display_handler->getPlugin('exposed_form');
1079 $this->exposed_widgets = $exposed_form->renderExposedForm();
1080 if (\Drupal::formBuilder()->getAnyErrors() || !empty($this->build_info['abort'])) {
1081 $this->built = TRUE;
1082 // Don't execute the query, $form_state, but rendering will still be executed to display the empty text.
1083 $this->executed = TRUE;
1084 return empty($this->build_info['fail']);
1085 }
1086 }
1087
1088 // Build all the relationships first thing.
1089 $this->_build('relationship');
1090
1091 // Set the filtering groups.
1092 if (!empty($this->filter)) {
1093 $filter_groups = $this->display_handler->getOption('filter_groups');
1094 if ($filter_groups) {
1095 $this->query->setGroupOperator($filter_groups['operator']);
1096 foreach ($filter_groups['groups'] as $id => $operator) {
1097 $this->query->setWhereGroup($operator, $id);
1098 }
1099 }
1100 }
1101
1102 // Build all the filters.
1103 $this->_build('filter');
1104
1105 $this->build_sort = TRUE;
1106
1107 // Arguments can, in fact, cause this whole thing to abort.
1108 if (!$this->_buildArguments()) {
1109 $this->build_time = microtime(TRUE) - $start;
1110 $this->attachDisplays();
1111 return $this->built;
1112 }
1113
1114 // Initialize the style; arguments may have changed which style we use,
1115 // so waiting as long as possible is important. But we need to know
1116 // about the style when we go to build fields.
1117 if (!$this->initStyle()) {
1118 $this->build_info['fail'] = TRUE;
1119 return FALSE;
1120 }
1121
1122 if ($this->style_plugin->usesFields()) {
1123 $this->_build('field');
1124 }
1125
1126 // Build our sort criteria if we were instructed to do so.
1127 if (!empty($this->build_sort)) {
1128 // Allow the style handler to deal with sorting.
1129 if ($this->style_plugin->buildSort()) {
1130 $this->_build('sort');
1131 }
1132 // allow the plugin to build second sorts as well.
1133 $this->style_plugin->buildSortPost();
1134 }
1135
1136 // Allow area handlers to affect the query.
1137 $this->_build('header');
1138 $this->_build('footer');
1139 $this->_build('empty');
1140
1141 // Allow display handler to affect the query:
1142 $this->display_handler->query($this->display_handler->useGroupBy());
1143
1144 // Allow style handler to affect the query:
1145 $this->style_plugin->query($this->display_handler->useGroupBy());
1146
1147 // Allow exposed form to affect the query:
1148 if (isset($exposed_form)) {
1149 $exposed_form->query();
1150 }
1151
1152 if (\Drupal::config('views.settings')->get('sql_signature')) {
1153 $this->query->addSignature($this);
1154 }
1155
1156 // Let modules modify the query just prior to finalizing it.
1157 $this->query->alter($this);
1158
1159 // Only build the query if we weren't interrupted.
1160 if (empty($this->built)) {
1161 // Build the necessary info to execute the query.
1162 $this->query->build($this);
1163 }
1164
1165 $this->built = TRUE;
1166 $this->build_time = microtime(TRUE) - $start;
1167
1168 // Attach displays
1169 $this->attachDisplays();
1170
1171 // Let modules modify the view just after building it.
1172 $module_handler->invokeAll('views_post_build', array($this));
1173
1174 return TRUE;
1175 }
1176
1177 /**
1178 * Internal method to build an individual set of handlers.
1179 *
1180 * @todo Some filter needs this function, even it is internal.
1181 *
1182 * @param string $key
1183 * The type of handlers (filter etc.) which should be iterated over to
1184 * build the relationship and query information.
1185 */
1186 public function _build($key) {
1187 $handlers = &$this->$key;
1188 foreach ($handlers as $id => $data) {
1189
1190 if (!empty($handlers[$id]) && is_object($handlers[$id])) {
1191 $multiple_exposed_input = array(0 => NULL);
1192 if ($handlers[$id]->multipleExposedInput()) {
1193 $multiple_exposed_input = $handlers[$id]->groupMultipleExposedInput($this->exposed_data);
1194 }
1195 foreach ($multiple_exposed_input as $group_id) {
1196 // Give this handler access to the exposed filter input.
1197 if (!empty($this->exposed_data)) {
1198 if ($handlers[$id]->isAGroup()) {
1199 $converted = $handlers[$id]->convertExposedInput($this->exposed_data, $group_id);
1200 $handlers[$id]->storeGroupInput($this->exposed_data, $converted);
1201 if (!$converted) {
1202 continue;
1203 }
1204 }
1205 $rc = $handlers[$id]->acceptExposedInput($this->exposed_data);
1206 $handlers[$id]->storeExposedInput($this->exposed_data, $rc);
1207 if (!$rc) {
1208 continue;
1209 }
1210 }
1211 $handlers[$id]->setRelationship();
1212 $handlers[$id]->query($this->display_handler->useGroupBy());
1213 }
1214 }
1215 }
1216 }
1217
1218 /**
1219 * Execute the view's query.
1220 *
1221 * @param string $display_id
1222 * The machine name of the display, which should be executed.
1223 *
1224 * @return bool
1225 * Return whether the executing was successful, for example an argument
1226 * could stop the process.
1227 */
1228 public function execute($display_id = NULL) {
1229 if (empty($this->built)) {
1230 if (!$this->build($display_id)) {
1231 return FALSE;
1232 }
1233 }
1234
1235 if (!empty($this->executed)) {
1236 return TRUE;
1237 }
1238
1239 // Don't allow to use deactivated displays, but display them on the live preview.
1240 if (!$this->display_handler->isEnabled() && empty($this->live_preview)) {
1241 $this->build_info['fail'] = TRUE;
1242 return FALSE;
1243 }
1244
1245 // Let modules modify the view just prior to executing it.
1246 $module_handler = \Drupal::moduleHandler();
1247 $module_handler->invokeAll('views_pre_execute', array($this));
1248
1249 // Check for already-cached results.
1250 if (!empty($this->live_preview)) {
1251 $cache = $this->display_handler->getPlugin('cache', 'none');
1252 }
1253 else {
1254 $cache = $this->display_handler->getPlugin('cache');
1255 }
1256
1257 if ($cache->cacheGet('results')) {
1258 if ($this->pager->usePager()) {
1259 $this->pager->total_items = $this->total_rows;
1260 $this->pager->updatePageInfo();
1261 }
1262 }
1263 else {
1264 $this->query->execute($this);
1265 // Enforce the array key rule as documented in
1266 // views_plugin_query::execute().
1267 $this->result = array_values($this->result);
1268 $this->_postExecute();
1269 $cache->cacheSet('results');
1270 }
1271
1272 // Let modules modify the view just after executing it.
1273 $module_handler->invokeAll('views_post_execute', array($this));
1274
1275 $this->executed = TRUE;
1276 }
1277
1278 /**
1279 * Render this view for a certain display.
1280 *
1281 * Note: You should better use just the preview function if you want to
1282 * render a view.
1283 *
1284 * @param string $display_id
1285 * The machine name of the display, which should be rendered.
1286 *
1287 * @return string|null
1288 * Return the output of the rendered view or NULL if something failed in the process.
1289 */
1290 public function render($display_id = NULL) {
1291 $this->execute($display_id);
1292
1293 // Check to see if the build failed.
1294 if (!empty($this->build_info['fail'])) {
1295 return;
1296 }
1297 if (!empty($this->build_info['denied'])) {
1298 return;
1299 }
1300
1301 $exposed_form = $this->display_handler->getPlugin('exposed_form');
1302 $exposed_form->preRender($this->result);
1303
1304 $module_handler = \Drupal::moduleHandler();
1305
1306 // Check for already-cached output.
1307 if (!empty($this->live_preview)) {
1308 $cache = FALSE;
1309 }
1310 else {
1311 $cache = $this->display_handler->getPlugin('cache');
1312 }
1313
1314 if ($cache && $cache->cacheGet('output')) {
1315 }
1316 else {
1317 if ($cache) {
1318 $cache->cacheStart();
1319 }
1320
1321 // Run preRender for the pager as it might change the result.
1322 if (!empty($this->pager)) {
1323 $this->pager->preRender($this->result);
1324 }
1325
1326 // Initialize the style plugin.
1327 $this->initStyle();
1328
1329 if (!isset($this->response)) {
1330 // Set the response so other parts can alter it.
1331 $this->response = new Response('', 200);
1332 }
1333
1334 // Give field handlers the opportunity to perform additional queries
1335 // using the entire resultset prior to rendering.
1336 if ($this->style_plugin->usesFields()) {
1337 foreach ($this->field as $id => $handler) {
1338 if (!empty($this->field[$id])) {
1339 $this->field[$id]->preRender($this->result);
1340 }
1341 }
1342 }
1343
1344 $this->style_plugin->preRender($this->result);
1345
1346 // Let each area handler have access to the result set.
1347 $areas = array('header', 'footer');
1348 // Only call preRender() on the empty handlers if the result is empty.
1349 if (empty($this->result)) {
1350 $areas[] = 'empty';
1351 }
1352 foreach ($areas as $area) {
1353 foreach ($this->{$area} as $handler) {
1354 $handler->preRender($this->result);
1355 }
1356 }
1357
1358 // Let modules modify the view just prior to rendering it.
1359 $module_handler->invokeAll('views_pre_render', array($this));
1360
1361 // Let the themes play too, because pre render is a very themey thing.
1362 if (isset($GLOBALS['base_theme_info']) && isset($GLOBALS['theme'])) {
1363 foreach ($GLOBALS['base_theme_info'] as $base) {
1364 $module_handler->invoke($base->getName(), 'views_pre_render', array($this));
1365 }
1366
1367 $module_handler->invoke($GLOBALS['theme'], 'views_pre_render', array($this));
1368 }
1369
1370 $this->display_handler->output = $this->display_handler->render();
1371 if ($cache) {
1372 $cache->cacheSet('output');
1373 }
1374 }
1375
1376 $exposed_form->postRender($this->display_handler->output);
1377
1378 if ($cache) {
1379 $cache->postRender($this->display_handler->output);
1380 }
1381
1382 // Let modules modify the view output after it is rendered.
1383 $module_handler->invokeAll('views_post_render', array($this, &$this->display_handler->output, $cache));
1384
1385 // Let the themes play too, because post render is a very themey thing.
1386 if (isset($GLOBALS['base_theme_info']) && isset($GLOBALS['theme'])) {
1387 foreach ($GLOBALS['base_theme_info'] as $base) {
1388 $module_handler->invoke($base->getName(), 'views_post_render', array($this));
1389 }
1390
1391 $module_handler->invoke($GLOBALS['theme'], 'views_post_render', array($this));
1392 }
1393
1394 return $this->display_handler->output;
1395 }
1396
1397 /**
1398 * Execute the given display, with the given arguments.
1399 * To be called externally by whatever mechanism invokes the view,
1400 * such as a page callback, hook_block, etc.
1401 *
1402 * This function should NOT be used by anything external as this
1403 * returns data in the format specified by the display. It can also
1404 * have other side effects that are only intended for the 'proper'
1405 * use of the display, such as setting page titles.
1406 *
1407 * If you simply want to view the display, use View::preview() instead.
1408 */
1409 public function executeDisplay($display_id = NULL, $args = array()) {
1410 if (empty($this->current_display) || $this->current_display != $this->chooseDisplay($display_id)) {
1411 if (!$this->setDisplay($display_id)) {
1412 return NULL;
1413 }
1414 }
1415
1416 $this->preExecute($args);
1417
1418 // Execute the view
1419 $output = $this->display_handler->execute();
1420
1421 $this->postExecute();
1422 return $output;
1423 }
1424
1425 /**
1426 * Preview the given display, with the given arguments.
1427 *
1428 * To be called externally, probably by an AJAX handler of some flavor.
1429 * Can also be called when views are embedded, as this guarantees
1430 * normalized output.
1431 *
1432 * This function does not do any access checks on the view. It is the
1433 * responsibility of the caller to check $view->access() or implement other
1434 * access logic. To render the view normally with access checks, use
1435 * views_embed_view() instead.
1436 */
1437 public function preview($display_id = NULL, $args = array()) {
1438 if (empty($this->current_display) || ((!empty($display_id)) && $this->current_display != $display_id)) {
1439 if (!$this->setDisplay($display_id)) {
1440 return FALSE;
1441 }
1442 }
1443
1444 $this->preview = TRUE;
1445 $this->preExecute($args);
1446 // Preview the view.
1447 $output = $this->display_handler->preview();
1448
1449 $this->postExecute();
1450 return $output;
1451 }
1452
1453 /**
1454 * Run attachments and let the display do what it needs to do prior
1455 * to running.
1456 */
1457 public function preExecute($args = array()) {
1458 $this->old_view[] = views_get_current_view();
1459 views_set_current_view($this);
1460 $display_id = $this->current_display;
1461
1462 // Prepare the view with the information we have, but only if we were
1463 // passed arguments, as they may have been set previously.
1464 if ($args) {
1465 $this->setArguments($args);
1466 }
1467
1468 // Let modules modify the view just prior to executing it.
1469 \Drupal::moduleHandler()->invokeAll('views_pre_view', array($this, $display_id, &$this->args));
1470
1471 // Allow hook_views_pre_view() to set the dom_id, then ensure it is set.
1472 $this->dom_id = !empty($this->dom_id) ? $this->dom_id : hash('sha256', $this->storage->id() . REQUEST_TIME . mt_rand());
1473
1474 // Allow the display handler to set up for execution
1475 $this->display_handler->preExecute();
1476 }
1477
1478 /**
1479 * Unset the current view, mostly.
1480 */
1481 public function postExecute() {
1482 // unset current view so we can be properly destructed later on.
1483 // Return the previous value in case we're an attachment.
1484
1485 if ($this->old_view) {
1486 $old_view = array_pop($this->old_view);
1487 }
1488
1489 views_set_current_view(isset($old_view) ? $old_view : FALSE);
1490 }
1491
1492 /**
1493 * Run attachment displays for the view.
1494 */
1495 public function attachDisplays() {
1496 if (!empty($this->is_attachment)) {
1497 return;
1498 }
1499
1500 if (!$this->display_handler->acceptAttachments()) {
1501 return;
1502 }
1503
1504 $this->is_attachment = TRUE;
1505 // Find out which other displays attach to the current one.
1506 foreach ($this->display_handler->getAttachedDisplays() as $id) {
1507 // Create a clone for the attachments to manipulate. 'static' refers to the current class name.
1508 $cloned_view = new static($this->storage, $this->user);
1509 $this->displayHandlers->get($id)->attachTo($cloned_view, $this->current_display);
1510 }
1511 $this->is_attachment = FALSE;
1512 }
1513
1514 /**
1515 * Returns default menu links from the view and the named display handler.
1516 *
1517 * @param string $display_id
1518 * A display ID.
1519 * @param array $links
1520 * An array of default menu link items passed from
1521 * views_menu_link_defaults_alter().
1522 *
1523 * @return array|bool
1524 */
1525 public function executeHookMenuLinkDefaults($display_id = NULL, &$links = array()) {
1526 // Prepare the view with the information we have. This was probably already
1527 // called, but it's good to be safe.
1528 if (!$this->setDisplay($display_id)) {
1529 return FALSE;
1530 }
1531
1532 // Execute the hook.
1533 if (isset($this->display_handler)) {
1534 return $this->display_handler->executeHookMenuLinkDefaults($links);
1535 }
1536 }
1537
1538 /**
1539 * Determine if the given user has access to the view. Note that
1540 * this sets the display handler if it hasn't been.
1541 */
1542 public function access($displays = NULL, $account = NULL) {
1543 // No one should have access to disabled views.
1544 if (!$this->storage->status()) {
1545 return FALSE;
1546 }
1547
1548 if (!isset($this->current_display)) {
1549 $this->initDisplay();
1550 }
1551
1552 if (!$account) {
1553 $account = $this->user;
1554 }
1555
1556 // We can't use choose_display() here because that function
1557 // calls this one.
1558 $displays = (array)$displays;
1559 foreach ($displays as $display_id) {
1560 if ($this->displayHandlers->has($display_id)) {
1561 if (($display = $this->displayHandlers->get($display_id)) && $display->access($account)) {
1562 return TRUE;
1563 }
1564 }
1565 }
1566
1567 return FALSE;
1568 }
1569
1570 /**
1571 * Sets the used response object of the view.
1572 *
1573 * @param Symfony\Component\HttpFoundation\Response $response
1574 * The response object which should be set.
1575 */
1576 public function setResponse(Response $response) {
1577 $this->response = $response;
1578 }
1579
1580 /**
1581 * Gets the response object used by the view.
1582 *
1583 * @return \Symfony\Component\HttpFoundation\Response
1584 * The response object of the view.
1585 */
1586 public function getResponse() {
1587 if (!isset($this->response)) {
1588 $this->response = new Response();
1589 }
1590 return $this->response;
1591 }
1592
1593 /**
1594 * Sets the request object.
1595 *
1596 * @param \Symfony\Component\HttpFoundation\Request $request
1597 * The request object.
1598 */
1599 public function setRequest(Request $request) {
1600 $this->request = $request;
1601 }
1602
1603 /**
1604 * Gets the request object.
1605 *
1606 * @return \Symfony\Component\HttpFoundation\Request $request
1607 * Returns the request object.
1608 */
1609 public function getRequest() {
1610 return $this->request;
1611 }
1612
1613 /**
1614 * Get the view's current title. This can change depending upon how it
1615 * was built.
1616 */
1617 public function getTitle() {
1618 if (empty($this->display_handler)) {
1619 if (!$this->setDisplay('default')) {
1620 return FALSE;
1621 }
1622 }
1623
1624 // During building, we might find a title override. If so, use it.
1625 if (!empty($this->build_info['title'])) {
1626 $title = $this->build_info['title'];
1627 }
1628 else {
1629 $title = $this->display_handler->getOption('title');
1630 }
1631
1632 // Allow substitutions from the first row.
1633 if ($this->initStyle()) {
1634 $title = $this->style_plugin->tokenizeValue($title, 0);
1635 }
1636 return $title;
1637 }
1638
1639 /**
1640 * Override the view's current title.
1641 *
1642 * The tokens in the title get's replaced before rendering.
1643 */
1644 public function setTitle($title) {
1645 $this->build_info['title'] = $title;
1646 return TRUE;
1647 }
1648
1649 /**
1650 * Force the view to build a title.
1651 */
1652 public function buildTitle() {
1653 $this->initDisplay();
1654
1655 if (empty($this->built)) {
1656 $this->initQuery();
1657 }
1658
1659 $this->initHandlers();
1660
1661 $this->_buildArguments();
1662 }
1663
1664 /**
1665 * Get the URL for the current view.
1666 *
1667 * This URL will be adjusted for arguments.
1668 */
1669 public function getUrl($args = NULL, $path = NULL) {
1670 if (!empty($this->override_url)) {
1671 return $this->override_url;
1672 }
1673
1674 if (!isset($path)) {
1675 $path = $this->getPath();
1676 }
1677 if (!isset($args)) {
1678 $args = $this->args;
1679
1680 // Exclude arguments that were computed, not passed on the URL.
1681 $position = 0;
1682 if (!empty($this->argument)) {
1683 foreach ($this->argument as $argument) {
1684 if (!empty($argument->is_default) && !empty($argument->options['default_argument_skip_url'])) {
1685 unset($args[$position]);
1686 }
1687 $position++;
1688 }
1689 }
1690 }
1691 // Don't bother working if there's nothing to do:
1692 if (empty($path) || (empty($args) && strpos($path, '%') === FALSE)) {
1693 return $path;
1694 }
1695
1696 $pieces = array();
1697 $argument_keys = isset($this->argument) ? array_keys($this->argument) : array();
1698 $id = current($argument_keys);
1699 foreach (explode('/', $path) as $piece) {
1700 if ($piece != '%') {
1701 $pieces[] = $piece;
1702 }
1703 else {
1704 if (empty($args)) {
1705 // Try to never put % in a url; use the wildcard instead.
1706 if ($id && !empty($this->argument[$id]->options['exception']['value'])) {
1707 $pieces[] = $this->argument[$id]->options['exception']['value'];
1708 }
1709 else {
1710 $pieces[] = '*'; // gotta put something if there just isn't one.
1711 }
1712
1713 }
1714 else {
1715 $pieces[] = array_shift($args);
1716 }
1717
1718 if ($id) {
1719 $id = next($argument_keys);
1720 }
1721 }
1722 }
1723
1724 if (!empty($args)) {
1725 $pieces = array_merge($pieces, $args);
1726 }
1727 return implode('/', $pieces);
1728 }
1729
1730 /**
1731 * Get the base path used for this view.
1732 */
1733 public function getPath() {
1734 if (!empty($this->override_path)) {
1735 return $this->override_path;
1736 }
1737
1738 if (empty($this->display_handler)) {
1739 if (!$this->setDisplay('default')) {
1740 return FALSE;
1741 }
1742 }
1743 return $this->display_handler->getPath();
1744 }
1745
1746 /**
1747 * Gets the current user.
1748 *
1749 * Views plugins can recieve the current user in order to not need dependency
1750 * injection.
1751 *
1752 * @return \Drupal\Core\Session\AccountInterface
1753 */
1754 public function getUser() {
1755 return $this->user;
1756 }
1757
1758 /**
1759 * Creates a duplicate ViewExecutable object.
1760 *
1761 * Makes a copy of this view that has been sanitized of handlers, any runtime
1762 * data, ID, and UUID.
1763 */
1764 public function createDuplicate() {
1765 return $this->storage->createDuplicate()->getExecutable();
1766 }
1767
1768 /**
1769 * Unset references so that a $view object may be properly garbage
1770 * collected.
1771 */
1772 public function destroy() {
1773 foreach ($this::getHandlerTypes() as $type => $info) {
1774 if (isset($this->$type)) {
1775 foreach ($this->{$type} as $handler) {
1776 $handler->destroy();
1777 }
1778 }
1779 }
1780
1781 if (isset($this->style_plugin)) {
1782 $this->style_plugin->destroy();
1783 }
1784
1785 $reflection = new \ReflectionClass($this);
1786 $defaults = $reflection->getDefaultProperties();
1787 // The external dependencies should not be reset. This is not generated by
1788 // the execution of a view.
1789 unset($defaults['storage']);
1790 unset($defaults['user']);
1791 foreach ($defaults as $property => $default) {
1792 $this->{$property} = $default;
1793 }
1794 }
1795
1796 /**
1797 * Makes sure the view is completely valid.
1798 *
1799 * @return array
1800 * An array of error strings. This will be empty if there are no validation
1801 * errors.
1802 */
1803 public function validate() {
1804 $errors = array();
1805
1806 $this->initDisplay();
1807 $current_display = $this->current_display;
1808
1809 foreach ($this->displayHandlers as $id => $display) {
1810 if (!empty($display)) {
1811 if (!empty($display->display['deleted'])) {
1812 continue;
1813 }
1814
1815 $result = $this->displayHandlers->get($id)->validate();
1816 if (!empty($result) && is_array($result)) {
1817 $errors[$id] = $result;
1818 }
1819 }
1820 }
1821
1822 $this->setDisplay($current_display);
1823
1824 return $errors;
1825 }
1826
1827 /**
1828 * Provide a list of views handler types used in a view, with some information
1829 * about them.
1830 *
1831 * @return array
1832 * An array of associative arrays containing:
1833 * - title: The title of the handler type.
1834 * - ltitle: The lowercase title of the handler type.
1835 * - stitle: A singular title of the handler type.
1836 * - lstitle: A singular lowercase title of the handler type.
1837 * - plural: Plural version of the handler type.
1838 * - (optional) type: The actual internal used handler type. This key is
1839 * just used for header,footer,empty to link to the internal type: area.
1840 */
1841 public static function getHandlerTypes() {
1842 return Views::getHandlerTypes();
1843 }
1844
1845 /**
1846 * Returns the valid types of plugins that can be used.
1847 *
1848 * @return array
1849 * An array of plugin type strings.
1850 */
1851 public static function getPluginTypes($type = NULL) {
1852 return Views::getPluginTypes($type);
1853 }
1854
1855 /**
1856 * Adds an instance of a handler to the view.
1857 *
1858 * Items may be fields, filters, sort criteria, or arguments.
1859 *
1860 * @param string $display_id
1861 * The machine name of the display.
1862 * @param string $type
1863 * The type of handler being added.
1864 * @param string $table
1865 * The name of the table this handler is from.
1866 * @param string $field
1867 * The name of the field this handler is from.
1868 * @param array $options
1869 * (optional) Extra options for this instance. Defaults to an empty array.
1870 * @param string $id
1871 * (optional) A unique ID for this handler instance. Defaults to NULL, in
1872 * which case one will be generated.
1873 *
1874 * @return string
1875 * The unique ID for this handler instance.
1876 */
1877 public function addHandler($display_id, $type, $table, $field, $options = array(), $id = NULL) {
1878 $types = $this::getHandlerTypes();
1879 $this->setDisplay($display_id);
1880
1881 $fields = $this->displayHandlers->get($display_id)->getOption($types[$type]['plural']);
1882
1883 if (empty($id)) {
1884 $id = $this->generateHandlerId($field, $fields);
1885 }
1886
1887 // If the desired type is not found, use the original value directly.
1888 $handler_type = !empty($types[$type]['type']) ? $types[$type]['type'] : $type;
1889
1890 $fields[$id] = array(
1891 'id' => $id,
1892 'table' => $table,
1893 'field' => $field,
1894 ) + $options;
1895
1896 // Load the plugin ID if available.
1897 $data = Views::viewsData()->get($table);
1898 if (isset($data[$field][$handler_type]['id'])) {
1899 $fields[$id]['plugin_id'] = $data[$field][$handler_type]['id'];
1900 if ($definition = Views::pluginManager($handler_type)->getDefinition($fields[$id]['plugin_id'])) {
1901 $fields[$id]['provider'] = isset($definition['provider']) ? $definition['provider'] : 'views';
1902 }
1903 }
1904
1905 $this->displayHandlers->get($display_id)->setOption($types[$type]['plural'], $fields);
1906
1907 return $id;
1908 }
1909
1910 /**
1911 * Generates a unique ID for an handler instance.
1912 *
1913 * These handler instances are typically fields, filters, sort criteria, or
1914 * arguments.
1915 *
1916 * @param string $requested_id
1917 * The requested ID for the handler instance.
1918 * @param array $existing_items
1919 * An array of existing handler instancess, keyed by their IDs.
1920 *
1921 * @return string
1922 * A unique ID. This will be equal to $requested_id if no handler instance
1923 * with that ID already exists. Otherwise, it will be appended with an
1924 * integer to make it unique, e.g., "{$requested_id}_1",
1925 * "{$requested_id}_2", etc.
1926 */
1927 public static function generateHandlerId($requested_id, $existing_items) {
1928 $count = 0;
1929 $id = $requested_id;
1930 while (!empty($existing_items[$id])) {
1931 $id = $requested_id . '_' . ++$count;
1932 }
1933 return $id;
1934 }
1935
1936 /**
1937 * Gets an array of handler instances for the current display.
1938 *
1939 * @param string $type
1940 * The type of handlers to retrieve.
1941 * @param string $display_id
1942 * (optional) A specific display machine name to use. If NULL, the current
1943 * display will be used.
1944 *
1945 * @return array
1946 * An array of handler instances of a given type for this display.
1947 */
1948 public function getHandlers($type, $display_id = NULL) {
1949 $this->setDisplay($display_id);
1950
1951 if (!isset($display_id)) {
1952 $display_id = $this->current_display;
1953 }
1954
1955 // Get info about the types so we can get the right data.
1956 $types = static::getHandlerTypes();
1957 return $this->displayHandlers->get($display_id)->getOption($types[$type]['plural']);
1958 }
1959
1960 /**
1961 * Gets the configuration of a handler instance on a given display.
1962 *
1963 * @param string $display_id
1964 * The machine name of the display.
1965 * @param string $type
1966 * The type of handler to retrieve.
1967 * @param string $id
1968 * The ID of the handler to retrieve.
1969 *
1970 * @return array|null
1971 * Either the handler instance's configuration, or NULL if the handler is
1972 * not used on the display.
1973 */
1974 public function getHandler($display_id, $type, $id) {
1975 // Get info about the types so we can get the right data.
1976 $types = static::getHandlerTypes();
1977 // Initialize the display
1978 $this->setDisplay($display_id);
1979
1980 // Get the existing configuration
1981 $fields = $this->displayHandlers->get($display_id)->getOption($types[$type]['plural']);
1982
1983 return isset($fields[$id]) ? $fields[$id] : NULL;
1984 }
1985
1986 /**
1987 * Sets the configuration of a handler instance on a given display.
1988 *
1989 * @param string $display_id
1990 * The machine name of the display.
1991 * @param string $type
1992 * The type of handler being set.
1993 * @param string $id
1994 * The ID of the handler being set.
1995 * @param array|null $item
1996 * An array of configuration for a handler, or NULL to remove this instance.
1997 *
1998 * @see set_item_option()
1999 */
2000 public function setHandler($display_id, $type, $id, $item) {
2001 // Get info about the types so we can get the right data.
2002 $types = static::getHandlerTypes();
2003 // Initialize the display.
2004 $this->setDisplay($display_id);
2005
2006 // Get the existing configuration.
2007 $fields = $this->displayHandlers->get($display_id)->getOption($types[$type]['plural']);
2008 if (isset($item)) {
2009 $fields[$id] = $item;
2010 }
2011
2012 // Store.
2013 $this->displayHandlers->get($display_id)->setOption($types[$type]['plural'], $fields);
2014 }
2015
2016 /**
2017 * Removes configuration for a handler instance on a given display.
2018 *
2019 * @param string $display_id
2020 * The machine name of the display.
2021 * @param string $type
2022 * The type of handler being removed.
2023 * @param string $id
2024 * The ID of the handler being removed.
2025 */
2026 public function removeHandler($display_id, $type, $id) {
2027 // Get info about the types so we can get the right data.
2028 $types = static::getHandlerTypes();
2029 // Initialize the display.
2030 $this->setDisplay($display_id);
2031
2032 // Get the existing configuration.
2033 $fields = $this->displayHandlers->get($display_id)->getOption($types[$type]['plural']);
2034 // Unset the item.
2035 unset($fields[$id]);
2036
2037 // Store.
2038 $this->displayHandlers->get($display_id)->setOption($types[$type]['plural'], $fields);
2039 }
2040
2041 /**
2042 * Sets an option on a handler instance.
2043 *
2044 * Use this only if you have just 1 or 2 options to set; if you have many,
2045 * consider getting the handler instance, adding the options and using
2046 * set_item() directly.
2047 *
2048 * @param string $display_id
2049 * The machine name of the display.
2050 * @param string $type
2051 * The type of handler being set.
2052 * @param string $id
2053 * The ID of the handler being set.
2054 * @param string $option
2055 * The configuration key for the value being set.
2056 * @param mixed $value
2057 * The value being set.
2058 *
2059 * @see set_item()
2060 */
2061 public function setHandlerOption($display_id, $type, $id, $option, $value) {
2062 $item = $this->getHandler($display_id, $type, $id);
2063 $item[$option] = $value;
2064 $this->setHandler($display_id, $type, $id, $item);
2065 }
2066
2067 /**
2068 * Enables admin links on the rendered view.
2069 *
2070 * @param bool $show_admin_links
2071 * TRUE if the admin links should be shown.
2072 */
2073 public function setShowAdminLinks($show_admin_links) {
2074 $this->showAdminLinks = (bool) $show_admin_links;
2075 }
2076
2077 /**
2078 * Returns whether admin links should be rendered on the view.
2079 *
2080 * @return bool
2081 * Returns TRUE if admin links should be rendered, else FALSE.
2082 */
2083 public function getShowAdminLinks() {
2084 return $this->showAdminLinks;
2085 }
2086
2087 /**
2088 * Merges all plugin default values for each display.
2089 */
2090 public function mergeDefaults() {
2091 $this->initDisplay();
2092 // Initialize displays and merge all plugin defaults.
2093 foreach ($this->displayHandlers as $display) {
2094 $display->mergeDefaults();
2095 }
2096 }
2097
2098 /**
2099 * Provide a full array of possible theme functions to try for a given hook.
2100 *
2101 * @param string $hook
2102 * The hook to use. This is the base theme/template name.
2103 *
2104 * @return array
2105 * An array of theme hook suggestions.
2106 */
2107 public function buildThemeFunctions($hook) {
2108 $themes = array();
2109 $display = isset($this->display_handler) ? $this->display_handler->display : NULL;
2110 $id = $this->storage->id();
2111
2112 if ($display) {
2113 $themes[] = $hook . '__' . $id . '__' . $display['id'];
2114 $themes[] = $hook . '__' . $display['id'];
2115 // Add theme suggestions for each single tag.
2116 foreach (Tags::explode($this->storage->get('tag')) as $tag) {
2117 $themes[] = $hook . '__' . preg_replace('/[^a-z0-9]/', '_', strtolower($tag));
2118 }
2119
2120 if ($display['id'] != $display['display_plugin']) {
2121 $themes[] = $hook . '__' . $id . '__' . $display['display_plugin'];
2122 $themes[] = $hook . '__' . $display['display_plugin'];
2123 }
2124 }
2125 $themes[] = $hook . '__' . $id;
2126 $themes[] = $hook;
2127
2128 return $themes;
2129 }
2130
2131 /**
2132 * Determines if this view has form elements.
2133 *
2134 * @return bool
2135 * Returns TRUE if this view contains handlers with views form
2136 * implementations, FALSE otherwise.
2137 */
2138 public function hasFormElements() {
2139 foreach ($this->field as $field) {
2140 if (property_exists($field, 'views_form_callback') || method_exists($field, 'viewsForm')) {
2141 return TRUE;
2142 }
2143 }
2144 $area_handlers = array_merge(array_values($this->header), array_values($this->footer));
2145 $empty = empty($this->result);
2146 foreach ($area_handlers as $area) {
2147 if (method_exists($area, 'viewsForm') && !$area->viewsFormEmpty($empty)) {
2148 return TRUE;
2149 }
2150 }
2151
2152 return FALSE;
2153 }
2154
2155 /**
2156 * Calculates dependencies for the view.
2157 *
2158 * @see \Drupal\views\Entity\View::calculateDependencies()
2159 *
2160 * @return array
2161 * An array of dependencies grouped by type (module, theme, entity).
2162 */
2163 public function calculateDependencies() {
2164 return $this->storage->calculateDependencies();
2165 }
2166
2167 }