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