diff --git a/core/modules/views/config/schema/views.data_types.schema.yml b/core/modules/views/config/schema/views.data_types.schema.yml index a3d1b34..ab96c74 100644 --- a/core/modules/views/config/schema/views.data_types.schema.yml +++ b/core/modules/views/config/schema/views.data_types.schema.yml @@ -599,6 +599,9 @@ views_pager_sql: id: type: integer label: 'Pager ID' + query_type: + type: string + label: 'Query type' tags: type: mapping label: 'Pager link labels' diff --git a/core/modules/views/js/ajax_view.js b/core/modules/views/js/ajax_view.js index 85975b5..8ceb6d5 100644 --- a/core/modules/views/js/ajax_view.js +++ b/core/modules/views/js/ajax_view.js @@ -155,6 +155,7 @@ var $link = $(link); var viewData = {}; var href = $link.attr('href'); + var type = typeof this.settings.pager_query_method !== 'undefined' ? this.settings.pager_query_method : 'POST'; // Construct an object using the settings defaults and then overriding // with data specific to the link. $.extend( @@ -168,7 +169,8 @@ var self_settings = $.extend({}, this.element_settings, { submit: viewData, base: false, - element: link + element: link, + type: type }); this.pagerAjax = Drupal.ajax(self_settings); }; diff --git a/core/modules/views/src/Ajax/ViewAjaxResponse.php b/core/modules/views/src/Ajax/ViewAjaxResponse.php index d191800..01ce19b 100644 --- a/core/modules/views/src/Ajax/ViewAjaxResponse.php +++ b/core/modules/views/src/Ajax/ViewAjaxResponse.php @@ -4,6 +4,7 @@ use Drupal\Core\Ajax\AjaxResponse; use Drupal\views\ViewExecutable; +use Drupal\Core\Cache\CacheableResponseTrait; /** * Custom JSON response object for an ajax view response. @@ -12,6 +13,8 @@ */ class ViewAjaxResponse extends AjaxResponse { + use CacheableResponseTrait; + /** * The view executed on this ajax request. * diff --git a/core/modules/views/src/Controller/ViewAjaxController.php b/core/modules/views/src/Controller/ViewAjaxController.php index c40cf85..e91ea16 100644 --- a/core/modules/views/src/Controller/ViewAjaxController.php +++ b/core/modules/views/src/Controller/ViewAjaxController.php @@ -110,10 +110,10 @@ public static function create(ContainerInterface $container) { * Thrown when the view was not found. */ public function ajaxView(Request $request) { - $name = $request->request->get('view_name'); - $display_id = $request->request->get('view_display_id'); + $name = $this->getParam($request, 'view_name'); + $display_id = $this->getParam($request, 'view_display_id'); if (isset($name) && isset($display_id)) { - $args = $request->request->get('view_args'); + $args = $this->getParam($request, 'view_args'); $args = isset($args) && $args !== '' ? explode('/', $args) : []; // Arguments can be empty, make sure they are passed on as NULL so that @@ -122,10 +122,10 @@ public function ajaxView(Request $request) { return ($arg == '' ? NULL : $arg); }, $args); - $path = $request->request->get('view_path'); - $dom_id = $request->request->get('view_dom_id'); + $path = $this->getParam($request, 'view_path'); + $dom_id = $this->getParam($request, 'view_dom_id'); $dom_id = isset($dom_id) ? preg_replace('/[^a-zA-Z0-9_-]+/', '-', $dom_id) : NULL; - $pager_element = $request->request->get('pager_element'); + $pager_element = $this->getParam($request, 'pager_element'); $pager_element = isset($pager_element) ? intval($pager_element) : NULL; $response = new ViewAjaxResponse(); @@ -149,8 +149,8 @@ public function ajaxView(Request $request) { $this->currentPath->setPath('/' . $path, $request); } - // Add all POST data, because AJAX is always a post and many things, - // such as tablesorts, exposed filters and paging assume GET. + // Add all POST data, because many things, such as tablesorts, exposed + // filters and paging assume GET. $request_all = $request->request->all(); $query_all = $request->query->all(); $request->query->replace($request_all + $query_all); @@ -180,16 +180,10 @@ public function ajaxView(Request $request) { // Reuse the same DOM id so it matches that in drupalSettings. $view->dom_id = $dom_id; - $context = new RenderContext(); - $preview = $this->renderer->executeInRenderContext($context, function() use ($view, $display_id, $args) { - return $view->preview($display_id, $args); - }); - if (!$context->isEmpty()) { - $bubbleable_metadata = $context->pop(); - BubbleableMetadata::createFromRenderArray($preview) - ->merge($bubbleable_metadata) - ->applyTo($preview); - } + // Build the view and add metadata to the response. + $preview = $view->buildRenderable($display_id, $args); + $cache_metadata = BubbleableMetadata::createFromRenderArray($preview); + $response->addCacheableDependency($cache_metadata); $response->addCommand(new ReplaceCommand(".js-view-dom-id-$dom_id", $preview)); return $response; @@ -203,4 +197,27 @@ public function ajaxView(Request $request) { } } + /** + * Get a request parameter. + * + * Determines whether request is GET or POST. + * + * @param \Symfony\Component\HttpFoundation\Request $request + * An HTTP request. + * @param string $param + * The name of a parameter. + * + * @return mixed + * The parameter value. + */ + public function getParam(Request $request, $param) { + $type = $request->getMethod(); + + if ($type == 'GET') { + return $request->query->get($param); + } + + return $request->request->get($param); + } + } diff --git a/core/modules/views/src/Plugin/views/pager/SqlBase.php b/core/modules/views/src/Plugin/views/pager/SqlBase.php index 46daeb7..73f25f3 100644 --- a/core/modules/views/src/Plugin/views/pager/SqlBase.php +++ b/core/modules/views/src/Plugin/views/pager/SqlBase.php @@ -16,6 +16,7 @@ protected function defineOptions() { $options['items_per_page'] = ['default' => 10]; $options['offset'] = ['default' => 0]; $options['id'] = ['default' => 0]; + $options['query_type'] = array('default' => 'POST'); $options['total_pages'] = ['default' => '']; $options['expose'] = [ 'contains' => [ @@ -72,6 +73,18 @@ public function buildOptionsForm(&$form, FormStateInterface $form_state) { '#default_value' => $this->options['total_pages'], ]; + if ($this->displayHandler->getOption('use_ajax')) { + $form['query_type'] = array( + '#type' => 'select', + '#title' => $this->t('Pager query type'), + '#options' => array( + 'GET' => $this->t('GET'), + 'POST' => $this->t('POST'), + ), + '#default_value' => $this->options['query_type'], + ); + } + $form['tags'] = [ '#type' => 'details', '#open' => TRUE, diff --git a/core/modules/views/tests/src/Unit/Controller/ViewAjaxControllerTest.php b/core/modules/views/tests/src/Unit/Controller/ViewAjaxControllerTest.php index f766975..d9811dc 100644 --- a/core/modules/views/tests/src/Unit/Controller/ViewAjaxControllerTest.php +++ b/core/modules/views/tests/src/Unit/Controller/ViewAjaxControllerTest.php @@ -127,8 +127,8 @@ public function testMissingViewName() { */ public function testMissingView() { $request = new Request(); - $request->request->set('view_name', 'test_view'); - $request->request->set('view_display_id', 'page_1'); + $request->query->set('view_name', 'test_view'); + $request->query->set('view_display_id', 'page_1'); $this->viewStorage->expects($this->once()) ->method('load') @@ -144,8 +144,8 @@ public function testMissingView() { */ public function testAccessDeniedView() { $request = new Request(); - $request->request->set('view_name', 'test_view'); - $request->request->set('view_display_id', 'page_1'); + $request->query->set('view_name', 'test_view'); + $request->query->set('view_display_id', 'page_1'); $view = $this->getMockBuilder('Drupal\views\Entity\View') ->disableOriginalConstructor() @@ -177,12 +177,13 @@ public function testAccessDeniedView() { */ public function testAjaxView() { $request = new Request(); - $request->request->set('view_name', 'test_view'); - $request->request->set('view_display_id', 'page_1'); - $request->request->set('view_path', '/test-page'); - $request->request->set('_wrapper_format', 'ajax'); - $request->request->set('ajax_page_state', 'drupal.settings[]'); - $request->request->set('type', 'article'); + + $request->query->set('view_name', 'test_view'); + $request->query->set('view_display_id', 'page_1'); + $request->query->set('view_path', '/test-page'); + $request->query->set('_wrapper_format', 'ajax'); + $request->query->set('ajax_page_state', 'drupal.settings[]'); + $request->query->set('type', 'article'); list($view, $executable) = $this->setupValidMocks(); @@ -220,13 +221,13 @@ public function testAjaxView() { */ public function testAjaxViewWithArguments() { $request = new Request(); - $request->request->set('view_name', 'test_view'); - $request->request->set('view_display_id', 'page_1'); - $request->request->set('view_args', 'arg1/arg2'); + $request->query->set('view_name', 'test_view'); + $request->query->set('view_display_id', 'page_1'); + $request->query->set('view_args', 'arg1/arg2'); list($view, $executable) = $this->setupValidMocks(); $executable->expects($this->once()) - ->method('preview') + ->method('buildRenderable') ->with('page_1', ['arg1', 'arg2']); $response = $this->viewAjaxController->ajaxView($request); @@ -240,14 +241,14 @@ public function testAjaxViewWithArguments() { */ public function testAjaxViewWithEmptyArguments() { $request = new Request(); - $request->request->set('view_name', 'test_view'); - $request->request->set('view_display_id', 'page_1'); + $request->query->set('view_name', 'test_view'); + $request->query->set('view_display_id', 'page_1'); // Simulate a request that has a second, empty argument. - $request->request->set('view_args', 'arg1/'); + $request->query->set('view_args', 'arg1/'); list($view, $executable) = $this->setupValidMocks(); $executable->expects($this->once()) - ->method('preview') + ->method('buildRenderable') ->with('page_1', $this->identicalTo(['arg1', NULL])); $response = $this->viewAjaxController->ajaxView($request); @@ -260,12 +261,36 @@ public function testAjaxViewWithEmptyArguments() { * Tests a valid view with a pager. */ public function testAjaxViewWithPager() { + $this->ajaxViewWithPagerTest(); + } + + /** + * Test a valid view with a pager using POST. + */ + public function testAjaxViewWithPostPager() { + $this->ajaxViewWithPagerTest(TRUE); + } + + /** + * Generate a request for a paged view. + * + * @param boolean $post + * Whether to structure the request as a POST. + */ + protected function ajaxViewWithPagerTest($post = FALSE) { $request = new Request(); - $request->request->set('view_name', 'test_view'); - $request->request->set('view_display_id', 'page_1'); + $param_property = 'query'; + + if ($post) { + $request->setMethod('POST'); + $param_property = 'request'; + } + + $request->{$param_property}->set('view_name', 'test_view'); + $request->{$param_property}->set('view_display_id', 'page_1'); $dom_id = $this->randomMachineName(20); - $request->request->set('view_dom_id', $dom_id); - $request->request->set('pager_element', '0'); + $request->{$param_property}->set('view_dom_id', $dom_id); + $request->{$param_property}->set('pager_element', '0'); list($view, $executable) = $this->setupValidMocks(); @@ -315,7 +340,7 @@ protected function setupValidMocks() { ->method('access') ->will($this->returnValue(TRUE)); $executable->expects($this->once()) - ->method('preview') + ->method('buildRenderable') ->will($this->returnValue(['#markup' => 'View result'])); $this->executableFactory->expects($this->once()) diff --git a/core/modules/views/views.module b/core/modules/views/views.module index f6b4f79..2a73768 100644 --- a/core/modules/views/views.module +++ b/core/modules/views/views.module @@ -66,6 +66,7 @@ function views_views_pre_render($view) { // To fit multiple views on a page, the programmer may have // overridden the display's pager_element. 'pager_element' => isset($view->pager) ? $view->pager->getPagerId() : 0, + 'pager_query_method' => !empty($view->pager->options['query_type']) ? $view->pager->options['query_type'] : 'POST' ], ], ];