Upgraded drupal core with security updates
[yaffs-website] / web / core / modules / views / src / Controller / ViewAjaxController.php
1 <?php
2
3 namespace Drupal\views\Controller;
4
5 use Drupal\Component\Utility\UrlHelper;
6 use Drupal\Core\Ajax\ReplaceCommand;
7 use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
8 use Drupal\Core\Entity\EntityStorageInterface;
9 use Drupal\Core\EventSubscriber\AjaxResponseSubscriber;
10 use Drupal\Core\EventSubscriber\MainContentViewSubscriber;
11 use Drupal\Core\Form\FormBuilderInterface;
12 use Drupal\Core\Path\CurrentPathStack;
13 use Drupal\Core\Render\BubbleableMetadata;
14 use Drupal\Core\Render\RenderContext;
15 use Drupal\Core\Render\RendererInterface;
16 use Drupal\Core\Routing\RedirectDestinationInterface;
17 use Drupal\views\Ajax\ScrollTopCommand;
18 use Drupal\views\Ajax\ViewAjaxResponse;
19 use Drupal\views\ViewExecutableFactory;
20 use Symfony\Component\DependencyInjection\ContainerInterface;
21 use Symfony\Component\HttpFoundation\Request;
22 use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
23 use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
24
25 /**
26  * Defines a controller to load a view via AJAX.
27  */
28 class ViewAjaxController implements ContainerInjectionInterface {
29
30   /**
31    * The entity storage for views.
32    *
33    * @var \Drupal\Core\Entity\EntityStorageInterface
34    */
35   protected $storage;
36
37   /**
38    * The factory to load a view executable with.
39    *
40    * @var \Drupal\views\ViewExecutableFactory
41    */
42   protected $executableFactory;
43
44   /**
45    * The renderer.
46    *
47    * @var \Drupal\Core\Render\RendererInterface
48    */
49   protected $renderer;
50
51   /**
52    * The current path.
53    *
54    * @var \Drupal\Core\Path\CurrentPathStack
55    */
56   protected $currentPath;
57
58   /**
59    * The redirect destination.
60    *
61    * @var \Drupal\Core\Routing\RedirectDestinationInterface
62    */
63   protected $redirectDestination;
64
65   /**
66    * Constructs a ViewAjaxController object.
67    *
68    * @param \Drupal\Core\Entity\EntityStorageInterface $storage
69    *   The entity storage for views.
70    * @param \Drupal\views\ViewExecutableFactory $executable_factory
71    *   The factory to load a view executable with.
72    * @param \Drupal\Core\Render\RendererInterface $renderer
73    *   The renderer.
74    * @param \Drupal\Core\Path\CurrentPathStack $current_path
75    *   The current path.
76    * @param \Drupal\Core\Routing\RedirectDestinationInterface $redirect_destination
77    *   The redirect destination.
78    */
79   public function __construct(EntityStorageInterface $storage, ViewExecutableFactory $executable_factory, RendererInterface $renderer, CurrentPathStack $current_path, RedirectDestinationInterface $redirect_destination) {
80     $this->storage = $storage;
81     $this->executableFactory = $executable_factory;
82     $this->renderer = $renderer;
83     $this->currentPath = $current_path;
84     $this->redirectDestination = $redirect_destination;
85   }
86
87   /**
88    * {@inheritdoc}
89    */
90   public static function create(ContainerInterface $container) {
91     return new static(
92       $container->get('entity.manager')->getStorage('view'),
93       $container->get('views.executable'),
94       $container->get('renderer'),
95       $container->get('path.current'),
96       $container->get('redirect.destination')
97     );
98   }
99
100   /**
101    * Loads and renders a view via AJAX.
102    *
103    * @param \Symfony\Component\HttpFoundation\Request $request
104    *   The current request object.
105    *
106    * @return \Drupal\views\Ajax\ViewAjaxResponse
107    *   The view response as ajax response.
108    *
109    * @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException
110    *   Thrown when the view was not found.
111    */
112   public function ajaxView(Request $request) {
113     $name = $request->request->get('view_name');
114     $display_id = $request->request->get('view_display_id');
115     if (isset($name) && isset($display_id)) {
116       $args = $request->request->get('view_args');
117       $args = isset($args) && $args !== '' ? explode('/', $args) : [];
118
119       // Arguments can be empty, make sure they are passed on as NULL so that
120       // argument validation is not triggered.
121       $args = array_map(function ($arg) {
122         return ($arg == '' ? NULL : $arg);
123       }, $args);
124
125       $path = $request->request->get('view_path');
126       $dom_id = $request->request->get('view_dom_id');
127       $dom_id = isset($dom_id) ? preg_replace('/[^a-zA-Z0-9_-]+/', '-', $dom_id) : NULL;
128       $pager_element = $request->request->get('pager_element');
129       $pager_element = isset($pager_element) ? intval($pager_element) : NULL;
130
131       $response = new ViewAjaxResponse();
132
133       // Remove all of this stuff from the query of the request so it doesn't
134       // end up in pagers and tablesort URLs.
135       foreach (['view_name', 'view_display_id', 'view_args', 'view_path', 'view_dom_id', 'pager_element', 'view_base_path', AjaxResponseSubscriber::AJAX_REQUEST_PARAMETER] as $key) {
136         $request->query->remove($key);
137         $request->request->remove($key);
138       }
139
140       // Load the view.
141       if (!$entity = $this->storage->load($name)) {
142         throw new NotFoundHttpException();
143       }
144       $view = $this->executableFactory->get($entity);
145       if ($view && $view->access($display_id) && $view->setDisplay($display_id) && $view->display_handler->getOption('use_ajax')) {
146         $response->setView($view);
147         // Fix the current path for paging.
148         if (!empty($path)) {
149           $this->currentPath->setPath('/' . $path, $request);
150         }
151
152         // Add all POST data, because AJAX is always a post and many things,
153         // such as tablesorts, exposed filters and paging assume GET.
154         $request_all = $request->request->all();
155         $query_all = $request->query->all();
156         $request->query->replace($request_all + $query_all);
157
158         // Overwrite the destination.
159         // @see the redirect.destination service.
160         $origin_destination = $path;
161
162         // Remove some special parameters you never want to have part of the
163         // destination query.
164         $used_query_parameters = $request->query->all();
165         // @todo Remove this parsing once these are removed from the request in
166         //   https://www.drupal.org/node/2504709.
167         unset($used_query_parameters[FormBuilderInterface::AJAX_FORM_REQUEST], $used_query_parameters[MainContentViewSubscriber::WRAPPER_FORMAT], $used_query_parameters['ajax_page_state']);
168
169         $query = UrlHelper::buildQuery($used_query_parameters);
170         if ($query != '') {
171           $origin_destination .= '?' . $query;
172         }
173         $this->redirectDestination->set($origin_destination);
174
175         // Override the display's pager_element with the one actually used.
176         if (isset($pager_element)) {
177           $response->addCommand(new ScrollTopCommand(".js-view-dom-id-$dom_id"));
178           $view->displayHandlers->get($display_id)->setOption('pager_element', $pager_element);
179         }
180         // Reuse the same DOM id so it matches that in drupalSettings.
181         $view->dom_id = $dom_id;
182
183         $context = new RenderContext();
184         $preview = $this->renderer->executeInRenderContext($context, function() use ($view, $display_id, $args) {
185           return $view->preview($display_id, $args);
186         });
187         if (!$context->isEmpty()) {
188           $bubbleable_metadata = $context->pop();
189           BubbleableMetadata::createFromRenderArray($preview)
190             ->merge($bubbleable_metadata)
191             ->applyTo($preview);
192         }
193         $response->addCommand(new ReplaceCommand(".js-view-dom-id-$dom_id", $preview));
194
195         return $response;
196       }
197       else {
198         throw new AccessDeniedHttpException();
199       }
200     }
201     else {
202       throw new NotFoundHttpException();
203     }
204   }
205
206 }