Updated Drupal to 8.6. This goes with the following updates because it's possible...
[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       // @todo Remove this parsing once these are removed from the request in
136       //   https://www.drupal.org/node/2504709.
137       foreach ([
138           'view_name',
139           'view_display_id',
140           'view_args',
141           'view_path',
142           'view_dom_id',
143           'pager_element',
144           'view_base_path',
145           AjaxResponseSubscriber::AJAX_REQUEST_PARAMETER,
146           FormBuilderInterface::AJAX_FORM_REQUEST,
147           MainContentViewSubscriber::WRAPPER_FORMAT,
148         ] as $key) {
149         $request->query->remove($key);
150         $request->request->remove($key);
151       }
152
153       // Load the view.
154       if (!$entity = $this->storage->load($name)) {
155         throw new NotFoundHttpException();
156       }
157       $view = $this->executableFactory->get($entity);
158       if ($view && $view->access($display_id) && $view->setDisplay($display_id) && $view->display_handler->ajaxEnabled()) {
159         $response->setView($view);
160         // Fix the current path for paging.
161         if (!empty($path)) {
162           $this->currentPath->setPath('/' . $path, $request);
163         }
164
165         // Add all POST data, because AJAX is always a post and many things,
166         // such as tablesorts, exposed filters and paging assume GET.
167         $request_all = $request->request->all();
168         unset($request_all['ajax_page_state']);
169         $query_all = $request->query->all();
170         $request->query->replace($request_all + $query_all);
171
172         // Overwrite the destination.
173         // @see the redirect.destination service.
174         $origin_destination = $path;
175
176         $used_query_parameters = $request->query->all();
177         $query = UrlHelper::buildQuery($used_query_parameters);
178         if ($query != '') {
179           $origin_destination .= '?' . $query;
180         }
181         $this->redirectDestination->set($origin_destination);
182
183         // Override the display's pager_element with the one actually used.
184         if (isset($pager_element)) {
185           $response->addCommand(new ScrollTopCommand(".js-view-dom-id-$dom_id"));
186           $view->displayHandlers->get($display_id)->setOption('pager_element', $pager_element);
187         }
188         // Reuse the same DOM id so it matches that in drupalSettings.
189         $view->dom_id = $dom_id;
190
191         $context = new RenderContext();
192         $preview = $this->renderer->executeInRenderContext($context, function () use ($view, $display_id, $args) {
193           return $view->preview($display_id, $args);
194         });
195         if (!$context->isEmpty()) {
196           $bubbleable_metadata = $context->pop();
197           BubbleableMetadata::createFromRenderArray($preview)
198             ->merge($bubbleable_metadata)
199             ->applyTo($preview);
200         }
201         $response->addCommand(new ReplaceCommand(".js-view-dom-id-$dom_id", $preview));
202
203         return $response;
204       }
205       else {
206         throw new AccessDeniedHttpException();
207       }
208     }
209     else {
210       throw new NotFoundHttpException();
211     }
212   }
213
214 }