annotate core/modules/views/src/Controller/ViewAjaxController.php @ 19:fa3358dc1485 tip

Add ndrum files
author Chris Cannam
date Wed, 28 Aug 2019 13:14:47 +0100
parents c2387f117808
children
rev   line source
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 }