diff --git a/core/modules/views/src/Form/ViewsExposedForm.php b/core/modules/views/src/Form/ViewsExposedForm.php index 72f754be2bfbb6cfc3a80b7156f0b49e83f03833..5e2eee9ae8362b70a7a9c2c82c0ab9d1b763928e 100644 --- a/core/modules/views/src/Form/ViewsExposedForm.php +++ b/core/modules/views/src/Form/ViewsExposedForm.php @@ -11,6 +11,7 @@ use Drupal\Component\Utility\String; use Drupal\Core\Form\FormBase; use Drupal\Core\Form\FormStateInterface; +use Drupal\Core\Url; use Drupal\views\ExposedFormCache; use Symfony\Component\DependencyInjection\ContainerInterface; @@ -115,7 +116,7 @@ public function buildForm(array $form, FormStateInterface $form_state) { '#id' => drupal_html_id('edit-submit-' . $view->storage->id()), ); - $form['#action'] = _url($view->display_handler->getUrl()); + $form['#action'] = $view->hasUrl() ? $view->getUrl()->toString() : Url::fromRoute('')->toString(); $form['#theme'] = $view->buildThemeFunctions('views_exposed_form'); $form['#id'] = Html::cleanCssIdentifier('views_exposed_form-' . String::checkPlain($view->storage->id()) . '-' . String::checkPlain($display['id'])); diff --git a/core/modules/views/src/Form/ViewsForm.php b/core/modules/views/src/Form/ViewsForm.php index c1296b20fed6af8716e783f0ed6dfd44e37dfb8a..cdf39eb19ee099a85724eb79d457d1841b941c5c 100644 --- a/core/modules/views/src/Form/ViewsForm.php +++ b/core/modules/views/src/Form/ViewsForm.php @@ -133,7 +133,8 @@ public function buildForm(array $form, FormStateInterface $form_state, ViewExecu $query = $this->requestStack->getCurrentRequest()->query->all(); $query = UrlHelper::filterQueryParameters($query, array(), ''); - $form['#action'] = $this->urlGenerator->generateFromPath($view->getUrl(), array('query' => $query)); + $options = array('query' => $query); + $form['#action'] = $view->hasUrl() ? $view->getUrl()->setOptions($options)->toString() : Url::fromRoute('')->setOptions($options)->toString(); // Tell the preprocessor whether it should hide the header, footer, pager... $form['show_view_elements'] = array( '#type' => 'value', diff --git a/core/modules/views/src/Plugin/views/display/DisplayPluginBase.php b/core/modules/views/src/Plugin/views/display/DisplayPluginBase.php index 7595977162338d0022640fa69abc15fe84ed7df9..93bd846eb0c58d715add889351b8252dfeb441fc 100644 --- a/core/modules/views/src/Plugin/views/display/DisplayPluginBase.php +++ b/core/modules/views/src/Plugin/views/display/DisplayPluginBase.php @@ -723,11 +723,31 @@ public function getPath() { } } + /** + * {@inheritdoc} + */ + public function getRoutedDisplay() { + // If this display has a route, return this display. + if ($this instanceof DisplayRouterInterface) { + return $this; + } + + // If the display does not have a route (e.g. a block display), get the + // route for the linked display. + $display_id = $this->getLinkDisplay(); + if ($display_id && $this->view->displayHandlers->has($display_id) && is_object($this->view->displayHandlers->get($display_id))) { + return $this->view->displayHandlers->get($display_id)->getRoutedDisplay(); + } + + // No routed display exists, so return NULL + return NULL; + } + /** * {@inheritdoc} */ public function getUrl() { - return $this->view->getUrl(); + return $this->view->getUrl(NULL, $this->display['id']); } /** @@ -1973,27 +1993,31 @@ public function renderPager() { */ public function renderMoreLink() { if ($this->isMoreEnabled() && ($this->useMoreAlways() || (!empty($this->view->pager) && $this->view->pager->hasMoreRecords()))) { - $path = $this->getPath(); - + // If the user has supplied a custom "More" link path, replace any + // argument tokens and use that for the URL. if ($this->getOption('link_display') == 'custom_url' && $override_path = $this->getOption('link_url')) { $tokens = $this->getArgumentsTokens(); $path = $this->viewsTokenReplace($override_path, $tokens); + $url = Url::fromUri('user-path:/' . $path); + } + // Otherwise, use the URL for the display. + else { + $url = $this->view->getUrl(NULL, $this->display['id']); } - if ($path) { - if (empty($override_path)) { - $path = $this->view->getUrl(NULL, $path); - } + // If a URL is available (either from the display or a custom path), + // render the "More" link. + if ($url) { $url_options = array(); if (!empty($this->view->exposed_raw_input)) { $url_options['query'] = $this->view->exposed_raw_input; } + $url->setOptions($url_options); $theme = $this->view->buildThemeFunctions('views_more'); - $path = check_url(_url($path, $url_options)); return array( '#theme' => $theme, - '#more_url' => $path, + '#more_url' => $url->toString(), '#link_text' => String::checkPlain($this->useMoreText()), '#view' => $this->view, ); diff --git a/core/modules/views/src/Plugin/views/display/DisplayPluginInterface.php b/core/modules/views/src/Plugin/views/display/DisplayPluginInterface.php index e84348cd757219f2fc4929abbe0b2f0790831af1..761fcd66f0c81d0ebe58dee4fe341c2cf00c192d 100644 --- a/core/modules/views/src/Plugin/views/display/DisplayPluginInterface.php +++ b/core/modules/views/src/Plugin/views/display/DisplayPluginInterface.php @@ -213,6 +213,23 @@ public function getLinkDisplay(); */ public function getPath(); + /** + * Points to the display which can be linked by this display. + * + * If the display has route information, the display itself is returned. + * Otherwise, the configured linked display is returned. For example, if a + * block display links to a page display, the page display will be returned + * in both cases. + * + * @return \Drupal\views\Plugin\views\display\DisplayRouterInterface|NULL + */ + public function getRoutedDisplay(); + + /** + * Returns a URL to $this display or its configured linked display. + * + * @return \Drupal\Core\Url|null + */ public function getUrl(); /** diff --git a/core/modules/views/src/Plugin/views/display/DisplayRouterInterface.php b/core/modules/views/src/Plugin/views/display/DisplayRouterInterface.php index 6223c522df26c1ed193abdad6a410bdca2572882..65328edcfcd519a9cac4b7945f1e9f485d024919 100644 --- a/core/modules/views/src/Plugin/views/display/DisplayRouterInterface.php +++ b/core/modules/views/src/Plugin/views/display/DisplayRouterInterface.php @@ -46,4 +46,29 @@ public function alterRoutes(RouteCollection $collection); */ public function getUrlInfo(); + /** + * Returns the route name for the display. + * + * The default route name for a display is views.$view_id.$display_id. Some + * displays may override existing routes; in these cases, the route that is + * overridden is returned instead. + * + * @return string + * The name of the route + * + * @see \Drupal\views\Plugin\views\display\DisplayRouterInterface::alterRoutes() + * @see \Drupal\views\Plugin\views\display\DisplayRouterInterface::getAlteredRouteNames() + */ + public function getRouteName(); + + /** + * Returns the list of routes overridden by Views. + * + * @return string[] + * An array of overridden route names. The keys are in the form + * view_id.display_id and the values are the route names. + * + * @see \Drupal\views\Plugin\views\display\DisplayRouterInterface::alterRoutes() + */ + public function getAlteredRouteNames(); } diff --git a/core/modules/views/src/Plugin/views/display/Feed.php b/core/modules/views/src/Plugin/views/display/Feed.php index 67171ab1c4650b7d03d2e8a81b8776749f1e3375..24e76ba64483acef3da81e6342131f8a3adc62b5 100644 --- a/core/modules/views/src/Plugin/views/display/Feed.php +++ b/core/modules/views/src/Plugin/views/display/Feed.php @@ -268,7 +268,7 @@ public function attachTo(ViewExecutable $clone, $display_id, array &$build) { $clone->setDisplay($this->display['id']); $clone->buildTitle(); if ($plugin = $clone->display_handler->getPlugin('style')) { - $plugin->attachTo($build, $display_id, $this->getPath(), $clone->getTitle()); + $plugin->attachTo($build, $display_id, $clone->getUrl(), $clone->getTitle()); foreach ($clone->feedIcons as $feed_icon) { $this->view->feedIcons[] = $feed_icon; } diff --git a/core/modules/views/src/Plugin/views/display/PathPluginBase.php b/core/modules/views/src/Plugin/views/display/PathPluginBase.php index 7738933607106d09d42ca91bf3a94e08a4a2da96..3a8473e84c7c0ec89777e9f221387f3757e9b852 100644 --- a/core/modules/views/src/Plugin/views/display/PathPluginBase.php +++ b/core/modules/views/src/Plugin/views/display/PathPluginBase.php @@ -294,8 +294,6 @@ public function getMenuLinks() { } } - $view_route_names = $this->state->get('views.view_route_names') ?: array(); - $path = implode('/', $bits); $view_id = $this->view->storage->id(); $display_id = $this->display['id']; @@ -309,7 +307,7 @@ public function getMenuLinks() { // Some views might override existing paths, so we have to set the route // name based upon the altering. $links[$menu_link_id] = array( - 'route_name' => isset($view_route_names[$view_id_display]) ? $view_route_names[$view_id_display] : "view.$view_id_display", + 'route_name' => $this->getRouteName(), // Identify URL embedded arguments and correlate them to a handler. 'load arguments' => array($this->view->storage->id(), $this->display['id'], '%index'), 'id' => $menu_link_id, @@ -490,11 +488,28 @@ public function validate() { * {@inheritdoc} */ public function getUrlInfo() { - if (strpos($this->getOption('path'), '%') !== FALSE) { - throw new \InvalidArgumentException('No placeholders supported yet.'); - } + return Url::fromRoute($this->getRouteName()); + } - return Url::fromRoute($this->getRoute($this->view->storage->id(), $this->display['id'])); + /** + * {@inheritdoc} + */ + public function getRouteName() { + $view_id = $this->view->storage->id(); + $display_id = $this->display['id']; + $view_route_key = "$view_id.$display_id"; + + // Check for overridden route names. + $view_route_names = $this->getAlteredRouteNames(); + + return (isset($view_route_names[$view_route_key]) ? $view_route_names[$view_route_key] : "views.$view_route_key"); + } + + /** + * {@inheritdoc} + */ + public function getAlteredRouteNames() { + return $this->state->get('views.view_route_names') ?: array(); } } diff --git a/core/modules/views/src/Plugin/views/row/RssFields.php b/core/modules/views/src/Plugin/views/row/RssFields.php index 6d0ae5b9c1149bd0ab81c4f72d062553ab5854ad..82b121278e6af593fc0e367172f61d2af3a342bd 100644 --- a/core/modules/views/src/Plugin/views/row/RssFields.php +++ b/core/modules/views/src/Plugin/views/row/RssFields.php @@ -8,6 +8,7 @@ namespace Drupal\views\Plugin\views\row; use Drupal\Core\Form\FormStateInterface; +use Drupal\Core\Url; /** * Renders an RSS item based on fields. @@ -143,7 +144,7 @@ public function render($row) { // Create the RSS item object. $item = new \stdClass(); $item->title = $this->getField($row_index, $this->options['title_field']); - $item->link = _url($this->getField($row_index, $this->options['link_field']), array('absolute' => TRUE)); + $item->link = Url::fromUri('user-path:/' . $this->getField($row_index, $this->options['link_field']))->setAbsolute()->toString(); $item->description = $this->getField($row_index, $this->options['description_field']); $item->elements = array( array('key' => 'pubDate', 'value' => $this->getField($row_index, $this->options['date_field'])), @@ -157,7 +158,7 @@ public function render($row) { $item_guid = $this->getField($row_index, $this->options['guid_field_options']['guid_field']); if ($this->options['guid_field_options']['guid_field_is_permalink']) { $guid_is_permalink_string = 'true'; - $item_guid = _url($item_guid, array('absolute' => TRUE)); + $item_guid = Url::fromUri('user-path:/' . $item_guid)->setAbsolute()->toString(); } $item->elements[] = array( 'key' => 'guid', diff --git a/core/modules/views/src/Plugin/views/style/Opml.php b/core/modules/views/src/Plugin/views/style/Opml.php index f41378f4111d6d10bf0e042898657f929288eec8..e0b9fb57c146e4a807171a94eeba0cdef7131a34 100644 --- a/core/modules/views/src/Plugin/views/style/Opml.php +++ b/core/modules/views/src/Plugin/views/style/Opml.php @@ -7,6 +7,8 @@ namespace Drupal\views\Plugin\views\style; +use Drupal\Core\Url; + /** * Default style plugin to render an OPML feed. * @@ -32,7 +34,7 @@ class Opml extends StylePluginBase { /** * {@inheritdoc} */ - public function attachTo(array &$build, $display_id, $path, $title) { + public function attachTo(array &$build, $display_id, Url $feed_url, $title) { $display = $this->view->displayHandlers->get($display_id); $url_options = array(); $input = $this->view->getExposedInput(); @@ -41,7 +43,7 @@ public function attachTo(array &$build, $display_id, $path, $title) { } $url_options['absolute'] = TRUE; - $url = _url($this->view->getUrl(NULL, $path), $url_options); + $url = $feed_url->setOptions($url_options)->toString(); if ($display->hasPath()) { if (empty($this->preview)) { $build['#attached']['feed'][] = array($url, $title); diff --git a/core/modules/views/src/Plugin/views/style/Rss.php b/core/modules/views/src/Plugin/views/style/Rss.php index f59711683d590f4c5e853fe49be41685e2aaf454..fbbf4dd59b6b5fed641e00f765a36e9ca9f9d488 100644 --- a/core/modules/views/src/Plugin/views/style/Rss.php +++ b/core/modules/views/src/Plugin/views/style/Rss.php @@ -9,6 +9,7 @@ use Drupal\Component\Utility\SafeMarkup; use Drupal\Core\Form\FormStateInterface; +use Drupal\Core\Url; /** * Default style plugin to render an RSS feed. @@ -32,7 +33,7 @@ class Rss extends StylePluginBase { */ protected $usesRowPlugin = TRUE; - public function attachTo(array &$build, $display_id, $path, $title) { + public function attachTo(array &$build, $display_id, Url $feed_url, $title) { $url_options = array(); $input = $this->view->getExposedInput(); if ($input) { @@ -40,7 +41,7 @@ public function attachTo(array &$build, $display_id, $path, $title) { } $url_options['absolute'] = TRUE; - $url = _url($this->view->getUrl(NULL, $path), $url_options); + $url = $feed_url->setOptions($url_options)->toString(); // Add the RSS icon to the view. $this->view->feedIcons[] = [ diff --git a/core/modules/views/src/Tests/Handler/FilterEqualityTest.php b/core/modules/views/src/Tests/Handler/FilterEqualityTest.php index 2a59e9f22bb02c1c1830508ecfee15d3ef2b56e8..bc0eb0de4585b804259c4598246f7ec538e8bdb2 100644 --- a/core/modules/views/src/Tests/Handler/FilterEqualityTest.php +++ b/core/modules/views/src/Tests/Handler/FilterEqualityTest.php @@ -76,6 +76,8 @@ public function testEqualGroupedExposed() { $filters['name']['group_info']['default_group'] = 1; $view->setDisplay('page_1'); $view->displayHandlers->get('page_1')->overrideOption('filters', $filters); + $view->save(); + $this->container->get('router.builder')->rebuild(); $this->executeView($view); $resultset = array( @@ -129,6 +131,8 @@ public function testEqualGroupedNotExposed() { $filters['name']['group_info']['default_group'] = 2; $view->setDisplay('page_1'); $view->displayHandlers->get('page_1')->overrideOption('filters', $filters); + $view->save(); + $this->container->get('router.builder')->rebuild(); $this->executeView($view); $resultset = array( @@ -153,6 +157,7 @@ protected function getGroupedExposedFilters() { $filters = array( 'name' => array( 'id' => 'name', + 'plugin_id' => 'equality', 'table' => 'views_test_data', 'field' => 'name', 'relationship' => 'none', diff --git a/core/modules/views/src/Tests/Handler/FilterNumericTest.php b/core/modules/views/src/Tests/Handler/FilterNumericTest.php index 9277da50b908b3072aef08766fa78beaa06923c8..e4914e6cc2a3ebdbaf5c857079d7437b49464dce 100644 --- a/core/modules/views/src/Tests/Handler/FilterNumericTest.php +++ b/core/modules/views/src/Tests/Handler/FilterNumericTest.php @@ -80,6 +80,8 @@ public function testFilterNumericExposedGroupedSimple() { $filters['age']['group_info']['default_group'] = 1; $view->setDisplay('page_1'); $view->displayHandlers->get('page_1')->overrideOption('filters', $filters); + $view->save(); + $this->container->get('router.builder')->rebuild(); $this->executeView($view); $resultset = array( @@ -173,6 +175,8 @@ public function testFilterNumericExposedGroupedBetween() { $filters['age']['group_info']['default_group'] = 2; $view->setDisplay('page_1'); $view->displayHandlers->get('page_1')->overrideOption('filters', $filters); + $view->save(); + $this->container->get('router.builder')->rebuild(); $this->executeView($view); $resultset = array( @@ -201,6 +205,8 @@ public function testFilterNumericExposedGroupedNotBetween() { $filters['age']['group_info']['default_group'] = 3; $view->setDisplay('page_1'); $view->displayHandlers->get('page_1')->overrideOption('filters', $filters); + $view->save(); + $this->container->get('router.builder')->rebuild(); $this->executeView($view); $resultset = array( @@ -289,6 +295,8 @@ public function testFilterNumericExposedGroupedEmpty() { $filters['age']['group_info']['default_group'] = 4; $view->setDisplay('page_1'); $view->displayHandlers->get('page_1')->overrideOption('filters', $filters); + $view->save(); + $this->container->get('router.builder')->rebuild(); $this->executeView($view); $resultset = array( @@ -305,6 +313,8 @@ public function testFilterNumericExposedGroupedNotEmpty() { $filters['age']['group_info']['default_group'] = 5; $view->setDisplay('page_1'); $view->displayHandlers->get('page_1')->overrideOption('filters', $filters); + $view->save(); + $this->container->get('router.builder')->rebuild(); $this->executeView($view); $resultset = array( @@ -366,6 +376,7 @@ protected function getGroupedExposedFilters() { $filters = array( 'age' => array( 'id' => 'age', + 'plugin_id' => 'numeric', 'table' => 'views_test_data', 'field' => 'age', 'relationship' => 'none', diff --git a/core/modules/views/src/Tests/Handler/FilterStringTest.php b/core/modules/views/src/Tests/Handler/FilterStringTest.php index baa34893b1e88201591534c88922ccba2ed8c058..f67eb227e35ed1828bab7e70fce8776ef84cd555 100644 --- a/core/modules/views/src/Tests/Handler/FilterStringTest.php +++ b/core/modules/views/src/Tests/Handler/FilterStringTest.php @@ -120,6 +120,8 @@ function testFilterStringGroupedExposedEqual() { $filters['name']['group_info']['default_group'] = 1; $view->setDisplay('page_1'); $view->displayHandlers->get('page_1')->overrideOption('filters', $filters); + $view->save(); + $this->container->get('router.builder')->rebuild(); $this->executeView($view); @@ -175,6 +177,8 @@ function testFilterStringGroupedExposedNotEqual() { $view->setDisplay('page_1'); $view->displayHandlers->get('page_1')->overrideOption('filters', $filters); + $view->save(); + $this->container->get('router.builder')->rebuild(); $this->executeView($view); @@ -230,6 +234,8 @@ function testFilterStringGroupedExposedContains() { $filters['name']['group_info']['default_group'] = '3'; $view->setDisplay('page_1'); $view->displayHandlers->get('page_1')->overrideOption('filters', $filters); + $view->save(); + $this->container->get('router.builder')->rebuild(); $this->executeView($view); @@ -304,6 +310,8 @@ function testFilterStringGroupedExposedWord() { $filters['name']['group_info']['default_group'] = '3'; $view->setDisplay('page_1'); $view->displayHandlers->get('page_1')->overrideOption('filters', $filters); + $view->save(); + $this->container->get('router.builder')->rebuild(); $this->executeView($view); @@ -369,6 +377,8 @@ function testFilterStringGroupedExposedStarts() { $filters['description']['group_info']['default_group'] = 2; $view->setDisplay('page_1'); $view->displayHandlers->get('page_1')->overrideOption('filters', $filters); + $view->save(); + $this->container->get('router.builder')->rebuild(); $this->executeView($view); @@ -420,6 +430,8 @@ function testFilterStringGroupedExposedNotStarts() { $filters['description']['group_info']['default_group'] = 3; $view->setDisplay('page_1'); $view->displayHandlers->get('page_1')->overrideOption('filters', $filters); + $view->save(); + $this->container->get('router.builder')->rebuild(); $this->executeView($view); @@ -474,6 +486,8 @@ function testFilterStringGroupedExposedEnds() { $filters['description']['group_info']['default_group'] = 4; $view->setDisplay('page_1'); $view->displayHandlers->get('page_1')->overrideOption('filters', $filters); + $view->save(); + $this->container->get('router.builder')->rebuild(); $this->executeView($view); @@ -525,6 +539,8 @@ function testFilterStringGroupedExposedNotEnds() { $filters['description']['group_info']['default_group'] = 5; $view->setDisplay('page_1'); $view->displayHandlers->get('page_1')->overrideOption('filters', $filters); + $view->save(); + $this->container->get('router.builder')->rebuild(); $this->executeView($view); @@ -578,6 +594,8 @@ function testFilterStringGroupedExposedNot() { $filters['description']['group_info']['default_group'] = 6; $view->setDisplay('page_1'); $view->displayHandlers->get('page_1')->overrideOption('filters', $filters); + $view->save(); + $this->container->get('router.builder')->rebuild(); $this->executeView($view); @@ -630,6 +648,8 @@ function testFilterStringGroupedExposedShorter() { $filters['name']['group_info']['default_group'] = 4; $view->setDisplay('page_1'); $view->displayHandlers->get('page_1')->overrideOption('filters', $filters); + $view->save(); + $this->container->get('router.builder')->rebuild(); $this->executeView($view); $resultset = array( @@ -676,6 +696,8 @@ function testFilterStringGroupedExposedLonger() { $filters['name']['group_info']['default_group'] = 5; $view->setDisplay('page_1'); $view->displayHandlers->get('page_1')->overrideOption('filters', $filters); + $view->save(); + $this->container->get('router.builder')->rebuild(); $this->executeView($view); $resultset = array( @@ -719,6 +741,8 @@ function testFilterStringGroupedExposedEmpty() { $filters['description']['group_info']['default_group'] = 7; $view->setDisplay('page_1'); $view->displayHandlers->get('page_1')->overrideOption('filters', $filters); + $view->save(); + $this->container->get('router.builder')->rebuild(); $this->executeView($view); $resultset = array( @@ -733,6 +757,7 @@ protected function getGroupedExposedFilters() { $filters = array( 'name' => array( 'id' => 'name', + 'plugin_id' => 'string', 'table' => 'views_test_data', 'field' => 'name', 'relationship' => 'none', @@ -778,6 +803,7 @@ protected function getGroupedExposedFilters() { ), 'description' => array( 'id' => 'description', + 'plugin_id' => 'string', 'table' => 'views_test_data', 'field' => 'description', 'relationship' => 'none', diff --git a/core/modules/views/src/Tests/TokenReplaceTest.php b/core/modules/views/src/Tests/TokenReplaceTest.php index f3f0028074b3859819845339e5db95edd3a261c3..d4bcc67bd4f103a3c09677aef1a3d6c567ec75b4 100644 --- a/core/modules/views/src/Tests/TokenReplaceTest.php +++ b/core/modules/views/src/Tests/TokenReplaceTest.php @@ -28,6 +28,7 @@ class TokenReplaceTest extends ViewUnitTestBase { protected function setUp() { parent::setUp(); $this->installSchema('system', 'url_alias'); + $this->container->get('router.builder')->rebuild(); } /** @@ -44,7 +45,7 @@ function testTokenReplacement() { '[view:description]' => 'Test view to token replacement tests.', '[view:id]' => 'test_tokens', '[view:title]' => 'Test token page', - '[view:url]' => $view->getUrlInfo('page_1')->setAbsolute(TRUE)->toString(), + '[view:url]' => $view->getUrl(NULL, 'page_1')->setAbsolute(TRUE)->toString(), '[view:total-rows]' => (string) $view->total_rows, '[view:base-table]' => 'views_test_data', '[view:base-field]' => 'id', diff --git a/core/modules/views/src/Tests/ViewExecutableTest.php b/core/modules/views/src/Tests/ViewExecutableTest.php index e99ca1de8756f625bf6de78cd5432ff40bd9f988..27c4e559cd5e365b97ac4e7e9e63cc8feb5996e2 100644 --- a/core/modules/views/src/Tests/ViewExecutableTest.php +++ b/core/modules/views/src/Tests/ViewExecutableTest.php @@ -322,18 +322,6 @@ public function testPropertyMethods() { $view->override_path = $override_path; $this->assertEqual($view->getPath(), $override_path); - // Test the getUrl method(). - $url = 'foo'; - $this->assertEqual($view->getUrl(NULL, $url), $url); - // Test with arguments. - $arg1 = 'bar'; - $arg2 = 12345; - $this->assertEqual($view->getUrl(array($arg1, $arg2), $url), "$url/$arg1/$arg2"); - // Test the override_url property override. - $override_url = 'baz'; - $view->override_url = $override_url; - $this->assertEqual($view->getUrl(NULL, $url), $override_url); - // Test the title methods. $title = $this->randomString(); $view->setTitle($title); diff --git a/core/modules/views/src/ViewExecutable.php b/core/modules/views/src/ViewExecutable.php index 9791a887cd6072882430ae00dd1b6e72cb3ee8a7..db01ad888a54215601377c8a254cd42eb8e3dd2c 100644 --- a/core/modules/views/src/ViewExecutable.php +++ b/core/modules/views/src/ViewExecutable.php @@ -10,7 +10,9 @@ use Drupal\Component\Utility\String; use Drupal\Core\DependencyInjection\DependencySerializationTrait; use Drupal\Core\Form\FormState; +use Drupal\Core\Routing\RouteProviderInterface; use Drupal\Core\Session\AccountInterface; +use Drupal\Core\Url; use Drupal\views\Plugin\views\display\DisplayRouterInterface; use Drupal\views\Plugin\views\query\QueryPluginBase; use Drupal\views\ViewEntityInterface; @@ -239,9 +241,9 @@ class ViewExecutable { /** * Allow to override the url of the current view. * - * @var string + * @var \Drupal\Core\Url */ - public $override_url = NULL; + public $override_url; /** * Allow to override the path used for generated urls. @@ -425,6 +427,13 @@ class ViewExecutable { */ protected $viewsData; + /** + * The route provider. + * + * @var \Drupal\Core\Routing\RouteProviderInterface + */ + protected $routeProvider; + /** * Constructs a new ViewExecutable object. * @@ -434,13 +443,16 @@ class ViewExecutable { * The current user. * @param \Drupal\views\ViewsData $views_data * The views data. + * @param \Drupal\Core\Routing\RouteProviderInterface $route_provider + * The route provider. */ - public function __construct(ViewEntityInterface $storage, AccountInterface $user, ViewsData $views_data) { + public function __construct(ViewEntityInterface $storage, AccountInterface $user, ViewsData $views_data, RouteProviderInterface $route_provider) { // Reference the storage and the executable to each other. $this->storage = $storage; $this->storage->set('executable', $this); $this->user = $user; $this->viewsData = $views_data; + $this->routeProvider = $route_provider; // Add the default css for a view. $this->element['#attached']['library'][] = 'views/views.module'; @@ -1692,12 +1704,47 @@ public function buildTitle() { $this->_buildArguments(); } + /** + * Determines whether you can link to the view or a particular display. + * + * Some displays (e.g. block displays) do not have their own route, but may + * optionally provide a link to another display that does have a route. + * + * @param array $args + * (optional) The arguments. + * @param string $display_id + * (optional) The display ID. The current display will be used by default. + * + * @return bool + */ + public function hasUrl($args = NULL, $display_id = NULL) { + if (!empty($this->override_url)) { + return TRUE; + } + + // If the display has a valid route available (either its own or for a + // linked display), then we can provide a URL for it. + $display_handler = $this->displayHandlers->get($display_id ?: $this->current_display)->getRoutedDisplay(); + if (!$display_handler instanceof DisplayRouterInterface) { + return FALSE; + } + + return TRUE; + } + /** * Get the URL for the current view. * * This URL will be adjusted for arguments. + * + * @param array $args + * (optional) Passed in arguments. + * @param string $display_id + * (optional) Specify the display ID to link to, fallback to the current ID. + * + * @return \Drupal\Core\Url */ - public function getUrl($args = NULL, $path = NULL) { + public function getUrl($args = NULL, $display_id = NULL) { if (!empty($this->override_url)) { return $this->override_url; } @@ -1705,6 +1752,12 @@ public function getUrl($args = NULL, $path = NULL) { if (!isset($path)) { $path = $this->getPath(); } + + $display_handler = $this->displayHandlers->get($display_id ?: $this->current_display)->getRoutedDisplay(); + if (!$display_handler instanceof DisplayRouterInterface) { + throw new \InvalidArgumentException('You cannot create a URL to a display without routes.'); + } + if (!isset($args)) { $args = $this->args; @@ -1721,41 +1774,41 @@ public function getUrl($args = NULL, $path = NULL) { } // Don't bother working if there's nothing to do: if (empty($path) || (empty($args) && strpos($path, '%') === FALSE)) { - return $path; + return $display_handler->getUrlInfo(); } - $pieces = array(); $argument_keys = isset($this->argument) ? array_keys($this->argument) : array(); $id = current($argument_keys); - foreach (explode('/', $path) as $piece) { - if ($piece != '%') { - $pieces[] = $piece; - } - else { - if (empty($args)) { - // Try to never put % in a url; use the wildcard instead. - if ($id && !empty($this->argument[$id]->options['exception']['value'])) { - $pieces[] = $this->argument[$id]->options['exception']['value']; - } - else { - $pieces[] = '*'; // gotta put something if there just isn't one. - } + /** @var \Drupal\Core\Url $url */ + $url = $display_handler->getUrlInfo(); + $route = $this->routeProvider->getRouteByName($url->getRouteName()); + + $variables = $route->compile()->getVariables(); + $parameters = $url->getRouteParameters(); + + foreach ($variables as $variable_name) { + if (empty($args)) { + // Try to never put % in a URL; use the wildcard instead. + if ($id && !empty($this->argument[$id]->options['exception']['value'])) { + $parameters[$variable_name] = $this->argument[$id]->options['exception']['value']; } else { - $pieces[] = array_shift($args); + // Provide some fallback in case no exception value could be found. + $parameters[$variable_name] = '*'; } + } + else { + $parameters[$variable_name] = array_shift($args); + } - if ($id) { - $id = next($argument_keys); - } + if ($id) { + $id = next($argument_keys); } } - if (!empty($args)) { - $pieces = array_merge($pieces, $args); - } - return implode('/', $pieces); + $url->setRouteParameters($parameters); + return $url; } /** diff --git a/core/modules/views/src/ViewExecutableFactory.php b/core/modules/views/src/ViewExecutableFactory.php index 04f61960144c8662c346b87e7caa82b614408a71..0975a48d2939e62bac97c003b7279d975c48ac6e 100644 --- a/core/modules/views/src/ViewExecutableFactory.php +++ b/core/modules/views/src/ViewExecutableFactory.php @@ -7,6 +7,7 @@ namespace Drupal\views; +use Drupal\Core\Routing\RouteProviderInterface; use Drupal\Core\Session\AccountInterface; use Drupal\views\ViewEntityInterface; use Symfony\Component\HttpFoundation\RequestStack; @@ -37,6 +38,13 @@ class ViewExecutableFactory { */ protected $viewsData; + /** + * The route provider. + * + * @var \Drupal\Core\Routing\RouteProviderInterface + */ + protected $routeProvider; + /** * Constructs a new ViewExecutableFactory * @@ -46,11 +54,14 @@ class ViewExecutableFactory { * The request stack. * @param \Drupal\views\ViewsData $views_data * The views data. + * @param \Drupal\Core\Routing\RouteProviderInterface $route_provider + * The route provider. */ - public function __construct(AccountInterface $user, RequestStack $request_stack, ViewsData $views_data) { + public function __construct(AccountInterface $user, RequestStack $request_stack, ViewsData $views_data, RouteProviderInterface $route_provider) { $this->user = $user; $this->requestStack = $request_stack; $this->viewsData = $views_data; + $this->routeProvider = $route_provider; } /** @@ -63,7 +74,7 @@ public function __construct(AccountInterface $user, RequestStack $request_stack, * A ViewExecutable instance. */ public function get(ViewEntityInterface $view) { - $view = new ViewExecutable($view, $this->user, $this->viewsData); + $view = new ViewExecutable($view, $this->user, $this->viewsData, $this->routeProvider); $view->setRequest($this->requestStack->getCurrentRequest()); return $view; } diff --git a/core/modules/views/tests/src/Unit/Plugin/area/ResultTest.php b/core/modules/views/tests/src/Unit/Plugin/area/ResultTest.php index a289d8e2991a925e94d301676b3aa0f63fc0823d..87c79a3d9c6009baadb68041c789003d52f82f3d 100644 --- a/core/modules/views/tests/src/Unit/Plugin/area/ResultTest.php +++ b/core/modules/views/tests/src/Unit/Plugin/area/ResultTest.php @@ -46,7 +46,8 @@ protected function setUp() { $views_data = $this->getMockBuilder('Drupal\views\ViewsData') ->disableOriginalConstructor() ->getMock(); - $this->view = new ViewExecutable($storage, $user, $views_data); + $route_provider = $this->getMock('Drupal\Core\Routing\RouteProviderInterface'); + $this->view = new ViewExecutable($storage, $user, $views_data, $route_provider); $this->resultHandler = new Result(array(), 'result', array()); $this->resultHandler->view = $this->view; diff --git a/core/modules/views/tests/src/Unit/Plugin/field/CounterTest.php b/core/modules/views/tests/src/Unit/Plugin/field/CounterTest.php index 959ce3d7e8d492e605c6e12f31c6a7dfbd36b685..7b0c14d8809a0f4546e816eedae5e05d2e532fe4 100644 --- a/core/modules/views/tests/src/Unit/Plugin/field/CounterTest.php +++ b/core/modules/views/tests/src/Unit/Plugin/field/CounterTest.php @@ -75,7 +75,8 @@ protected function setUp() { $views_data = $this->getMockBuilder('Drupal\views\ViewsData') ->disableOriginalConstructor() ->getMock(); - $this->view = $this->getMock('Drupal\views\ViewExecutable', NULL, array($storage, $user, $views_data)); + $route_provider = $this->getMock('Drupal\Core\Routing\RouteProviderInterface'); + $this->view = $this->getMock('Drupal\views\ViewExecutable', NULL, array($storage, $user, $views_data, $route_provider)); $this->display = $this->getMockBuilder('Drupal\views\Plugin\views\display\DisplayPluginBase') ->disableOriginalConstructor() diff --git a/core/modules/views/tests/src/Unit/ViewExecutableFactoryTest.php b/core/modules/views/tests/src/Unit/ViewExecutableFactoryTest.php index e82468e54f0c4ad36c6dd18b44b8aa5a2340b4d1..17b61c8d091fad77465bdb42e301625156d5d58c 100644 --- a/core/modules/views/tests/src/Unit/ViewExecutableFactoryTest.php +++ b/core/modules/views/tests/src/Unit/ViewExecutableFactoryTest.php @@ -53,6 +53,13 @@ class ViewExecutableFactoryTest extends UnitTestCase { */ protected $viewsData; + /** + * The mocked route provider. + * + * @var \Drupal\Core\Routing\RouteProviderInterface|\PHPUnit_Framework_MockObject_MockObject + */ + protected $routeProvider; + /** * {@inheritdoc} */ @@ -65,7 +72,8 @@ protected function setUp() { $this->viewsData = $this->getMockBuilder('Drupal\views\ViewsData') ->disableOriginalConstructor() ->getMock(); - $this->viewExecutableFactory = new ViewExecutableFactory($this->user, $this->requestStack, $this->viewsData); + $this->routeProvider = $this->getMock('Drupal\Core\Routing\RouteProviderInterface'); + $this->viewExecutableFactory = new ViewExecutableFactory($this->user, $this->requestStack, $this->viewsData, $this->routeProvider); } /** diff --git a/core/modules/views/tests/src/Unit/ViewExecutableUnitTest.php b/core/modules/views/tests/src/Unit/ViewExecutableTest.php similarity index 58% rename from core/modules/views/tests/src/Unit/ViewExecutableUnitTest.php rename to core/modules/views/tests/src/Unit/ViewExecutableTest.php index ecabe3ffa9a8f9c318d4643f4bacdb2f566788c5..d980f8237361e2a0ddebef926a8655a642e40269 100644 --- a/core/modules/views/tests/src/Unit/ViewExecutableUnitTest.php +++ b/core/modules/views/tests/src/Unit/ViewExecutableTest.php @@ -2,28 +2,51 @@ /** * @file - * Contains \Drupal\Tests\views\Unit\ViewExecutableUnitTest. + * Contains Drupal\Tests\views\Unit\ViewExecutableTest. */ namespace Drupal\Tests\views\Unit; +use Drupal\Core\DependencyInjection\ContainerBuilder; +use Drupal\Core\Url; use Drupal\Tests\UnitTestCase; use Drupal\views\Entity\View; use Drupal\views\ViewExecutable; -use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\Routing\Route; /** * @coversDefaultClass \Drupal\views\ViewExecutable * @group views */ -class ViewExecutableUnitTest extends UnitTestCase { +class ViewExecutableTest extends UnitTestCase { /** - * The mocked views data. + * A mocked display collection. * - * @var \Drupal\views\ViewsData|\PHPUnit_Framework_MockObject_MockObject + * @var \Drupal\views\DisplayPluginCollection|\PHPUnit_Framework_MockObject_MockObject */ - protected $viewsData; + protected $displayHandlers; + + /** + * The mocked view executable. + * + * @var \Drupal\views\ViewExecutableFactory|\PHPUnit_Framework_MockObject_MockObject + */ + protected $viewExecutableFactory; + + /** + * The tested view executable. + * + * @var \Drupal\views\ViewExecutable + */ + protected $executable; + + /** + * The mocked view entity. + * + * @var \Drupal\views\ViewEntityInterface|\PHPUnit_Framework_MockObject_MockObject + */ + protected $view; /** * The mocked user. @@ -33,18 +56,25 @@ class ViewExecutableUnitTest extends UnitTestCase { protected $user; /** - * A mocked display collection. + * The mocked views data. * - * @var \Drupal\views\DisplayPluginCollection|\PHPUnit_Framework_MockObject_MockObject + * @var \Drupal\views\ViewsData|\PHPUnit_Framework_MockObject_MockObject */ - protected $displayCollection; + protected $viewsData; /** - * The mocked view executable. + * The mocked display handler. * - * @var \Drupal\views\ViewExecutableFactory|\PHPUnit_Framework_MockObject_MockObject + * @var \Drupal\views\Plugin\views\display\DisplayPluginInterface|\PHPUnit_Framework_MockObject_MockObject */ - protected $viewExecutableFactory; + protected $displayHandler; + + /** + * The mocked route provider. + * + * @var \Drupal\Core\Routing\RouteProviderInterface|\PHPUnit_Framework_MockObject_MockObject + */ + protected $routeProvider; /** * {@inheritdoc} @@ -52,15 +82,23 @@ class ViewExecutableUnitTest extends UnitTestCase { protected function setUp() { parent::setUp(); + $this->view = $this->getMock('Drupal\views\ViewEntityInterface'); + $this->user = $this->getMock('Drupal\Core\Session\AccountInterface'); $this->viewsData = $this->getMockBuilder('Drupal\views\ViewsData') ->disableOriginalConstructor() ->getMock(); - $this->user = $this->getMock('Drupal\Core\Session\AccountInterface'); - - $this->displayCollection = $this->getMockBuilder('Drupal\views\DisplayPluginCollection') + $this->displayHandler = $this->getMockBuilder('Drupal\views\Plugin\views\display\DisplayRouterInterface') + ->disableOriginalConstructor() + ->getMock(); + $this->routeProvider = $this->getMock('Drupal\Core\Routing\RouteProviderInterface'); + $this->displayHandlers = $this->getMockBuilder('Drupal\views\DisplayPluginCollection') ->disableOriginalConstructor() ->getMock(); + $this->executable = new ViewExecutable($this->view, $this->user, $this->viewsData, $this->routeProvider); + $this->executable->display_handler = $this->displayHandler; + $this->executable->displayHandlers = $this->displayHandlers; + $this->viewExecutableFactory = $this->getMockBuilder('Drupal\views\ViewExecutableFactory') ->disableOriginalConstructor() ->getMock(); @@ -72,6 +110,140 @@ protected function setUp() { \Drupal::setContainer($container); } + /** + * @covers ::getUrl + */ + public function testGetUrlWithOverriddenUrl() { + $url = Url::fromRoute('example'); + $this->executable->override_url = $url; + + $this->assertSame($url, $this->executable->getUrl()); + } + + /** + * @covers ::getUrl + */ + public function testGetUrlWithPathNoPlaceholders() { + $this->displayHandler->expects($this->any()) + ->method('getRoutedDisplay') + ->willReturn($this->displayHandler); + $this->displayHandlers->expects($this->any()) + ->method('get') + ->willReturn($this->displayHandler); + $this->displayHandler->expects($this->any()) + ->method('getUrlInfo') + ->willReturn(Url::fromRoute('views.test.page_1')); + $this->displayHandler->expects($this->any()) + ->method('getPath') + ->willReturn('test-path'); + + $this->assertEquals(Url::fromRoute('views.test.page_1'), $this->executable->getUrl()); + } + + /** + * @expectedException \InvalidArgumentException + * + * @covers ::getUrl + */ + public function testGetUrlWithoutRouterDisplay() { + $this->displayHandler = $this->getMock('Drupal\views\Plugin\views\display\DisplayPluginInterface'); + $this->displayHandlers->expects($this->any()) + ->method('get') + ->willReturn($this->displayHandler); + $this->executable->display_handler = $this->displayHandler; + + $this->executable->getUrl(); + } + + /** + * @covers ::getUrl + */ + public function testGetUrlWithPlaceholdersAndArgs() { + $this->displayHandler->expects($this->any()) + ->method('getRoutedDisplay') + ->willReturn($this->displayHandler); + $this->displayHandlers->expects($this->any()) + ->method('get') + ->willReturn($this->displayHandler); + $this->displayHandler->expects($this->any()) + ->method('getUrlInfo') + ->willReturn(Url::fromRoute('views.test.page_1')); + $this->displayHandler->expects($this->any()) + ->method('getPath') + ->willReturn('test-path/%'); + + $route = new Route('/test-path/{arg_0}'); + $this->routeProvider->expects($this->any()) + ->method('getRouteByName') + ->with('views.test.page_1') + ->willReturn($route); + + $this->assertEquals(Url::fromRoute('views.test.page_1', ['arg_0' => 'test']), $this->executable->getUrl(['test'])); + } + + /** + * @covers ::getUrl + */ + public function testGetUrlWithPlaceholdersAndWithoutArgs() { + $this->displayHandler->expects($this->any()) + ->method('getRoutedDisplay') + ->willReturn($this->displayHandler); + $this->displayHandlers->expects($this->any()) + ->method('get') + ->willReturn($this->displayHandler); + $this->displayHandler->expects($this->any()) + ->method('getUrlInfo') + ->willReturn(Url::fromRoute('views.test.page_1')); + $this->displayHandler->expects($this->any()) + ->method('getPath') + ->willReturn('test-path/%/%'); + + $route = new Route('/test-path/{arg_0}/{arg_1}'); + $this->routeProvider->expects($this->any()) + ->method('getRouteByName') + ->with('views.test.page_1') + ->willReturn($route); + + $this->assertEquals(Url::fromRoute('views.test.page_1', ['arg_0' => '*', 'arg_1' => '*']), $this->executable->getUrl()); + } + + /** + * @covers ::getUrl + */ + public function testGetUrlWithPlaceholdersAndWithoutArgsAndExceptionValue() { + $this->displayHandler->expects($this->any()) + ->method('getRoutedDisplay') + ->willReturn($this->displayHandler); + $this->displayHandlers->expects($this->any()) + ->method('get') + ->willReturn($this->displayHandler); + $this->displayHandler->expects($this->any()) + ->method('getUrlInfo') + ->willReturn(Url::fromRoute('views.test.page_1')); + $this->displayHandler->expects($this->any()) + ->method('getPath') + ->willReturn('test-path/%/%'); + + $route = new Route('/test-path/{arg_0}/{arg_1}'); + $this->routeProvider->expects($this->any()) + ->method('getRouteByName') + ->with('views.test.page_1') + ->willReturn($route); + + $argument_handler = $this->getMockBuilder('Drupal\views\Plugin\views\argument\ArgumentPluginBase') + ->disableOriginalConstructor() + ->getMock(); + $argument_handler->options['exception']['value'] = 'exception_0'; + $this->executable->argument['key_1'] = $argument_handler; + $argument_handler = $this->getMockBuilder('Drupal\views\Plugin\views\argument\ArgumentPluginBase') + ->disableOriginalConstructor() + ->getMock(); + $argument_handler->options['exception']['value'] = 'exception_1'; + $this->executable->argument['key_2'] = $argument_handler; + + $this->assertEquals(Url::fromRoute('views.test.page_1', ['arg_0' => 'exception_0', 'arg_1' => 'exception_1']), $this->executable->getUrl()); + } + /** * Tests the buildThemeFunctions() method. */ @@ -81,14 +253,14 @@ public function testBuildThemeFunctions() { list($view, $display) = $this->setupBaseViewAndDisplay(); unset($view->display_handler); - $expected = array( + $expected = [ 'test_hook__test_view', 'test_hook' - ); + ]; $this->assertEquals($expected, $view->buildThemeFunctions('test_hook')); $view->display_handler = $display; - $expected = array( + $expected = [ 'test_hook__test_view__default', 'test_hook__default', 'test_hook__one', @@ -96,13 +268,13 @@ public function testBuildThemeFunctions() { 'test_hook__and_three', 'test_hook__test_view', 'test_hook' - ); + ]; $this->assertEquals($expected, $view->buildThemeFunctions('test_hook')); //Change the name of the display plugin and make sure that is in the array. $view->display_handler->display['display_plugin'] = 'default2'; - $expected = array( + $expected = [ 'test_hook__test_view__default', 'test_hook__default', 'test_hook__one', @@ -112,7 +284,7 @@ public function testBuildThemeFunctions() { 'test_hook__default2', 'test_hook__test_view', 'test_hook' - ); + ]; $this->assertEquals($expected, $view->buildThemeFunctions('test_hook')); } @@ -280,7 +452,7 @@ protected function setupBaseViewAndDisplay() { ); $storage = new View($config, 'view'); - $view = new ViewExecutable($storage, $this->user, $this->viewsData); + $view = new ViewExecutable($storage, $this->user, $this->viewsData, $this->routeProvider); $display = $this->getMockBuilder('Drupal\views\Plugin\views\display\DisplayPluginBase') ->disableOriginalConstructor() ->getMock(); @@ -288,7 +460,7 @@ protected function setupBaseViewAndDisplay() { $view->current_display = 'default'; $view->display_handler = $display; - $view->displayHandlers = $this->displayCollection; + $view->displayHandlers = $this->displayHandlers; $view->displayHandlers->expects($this->any()) ->method('get') ->with('default') diff --git a/core/modules/views/tests/src/Unit/ViewsTest.php b/core/modules/views/tests/src/Unit/ViewsTest.php index ab9bdf8c9ae422484bbb14d2eb294545596c3bed..aa7a8c9c8242a11e1b76bc18124f4e92bd47a3dc 100644 --- a/core/modules/views/tests/src/Unit/ViewsTest.php +++ b/core/modules/views/tests/src/Unit/ViewsTest.php @@ -34,7 +34,8 @@ protected function setUp() { $views_data = $this->getMockBuilder('Drupal\views\ViewsData') ->disableOriginalConstructor() ->getMock(); - $container->set('views.executable', new ViewExecutableFactory($user, $request_stack, $views_data)); + $route_provider = $this->getMock('Drupal\Core\Routing\RouteProviderInterface'); + $container->set('views.executable', new ViewExecutableFactory($user, $request_stack, $views_data, $route_provider)); $this->view = new View(array('id' => 'test_view'), 'view'); diff --git a/core/modules/views/views.services.yml b/core/modules/views/views.services.yml index f98ad1c6e3ea2ec2c8486b1bb067e294bc1c9f6f..1a01543c6747b98b20896390900f37ecec65b351 100644 --- a/core/modules/views/views.services.yml +++ b/core/modules/views/views.services.yml @@ -64,7 +64,7 @@ services: arguments: ['@views.views_data'] views.executable: class: Drupal\views\ViewExecutableFactory - arguments: ['@current_user', '@request_stack', '@views.views_data'] + arguments: ['@current_user', '@request_stack', '@views.views_data', '@router.route_provider'] views.analyzer: class: Drupal\views\Analyzer arguments: ['@module_handler'] diff --git a/core/modules/views/views.theme.inc b/core/modules/views/views.theme.inc index 0e70dd47d33a5b0ad6e2ede9baa9a1bdadedbfeb..dca6e3c3cfde55b5e9c194a755c91d0e626d8770 100644 --- a/core/modules/views/views.theme.inc +++ b/core/modules/views/views.theme.inc @@ -294,6 +294,7 @@ function template_preprocess_views_view_field(&$variables) { * - rows: The raw row data. */ function template_preprocess_views_view_summary(&$variables) { + /** @var \Drupal\views\ViewExecutable $view */ $view = $variables['view']; $argument = $view->argument[$view->build_info['summary_level']]; @@ -331,11 +332,16 @@ function template_preprocess_views_view_summary(&$variables) { $args = $view->args; $args[$argument->position] = $row_args[$id]; - $base_path = NULL; if (!empty($argument->options['summary_options']['base_path'])) { $base_path = $argument->options['summary_options']['base_path']; + $tokens = $this->getArgumentsTokens(); + $base_path = $this->viewsTokenReplace($base_path, $tokens); + $url = Url::fromUri('user-path:/' . $base_path); } - $variables['rows'][$id]->url = _url($view->getUrl($args, $base_path), $url_options); + else { + $url = $view->getUrl($args)->setOptions($url_options); + } + $variables['rows'][$id]->url = $url->toString(); $variables['rows'][$id]->count = intval($row->{$argument->count_alias}); if (isset($active_urls[$variables['rows'][$id]->url])) { $variables['rows'][$id]->attributes['class'][] = 'active'; @@ -395,11 +401,16 @@ function template_preprocess_views_view_summary_unformatted(&$variables) { $args = $view->args; $args[$argument->position] = $row_args[$id]; - $base_path = NULL; if (!empty($argument->options['summary_options']['base_path'])) { $base_path = $argument->options['summary_options']['base_path']; + $tokens = $this->getArgumentsTokens(); + $base_path = $this->viewsTokenReplace($base_path, $tokens); + $url = Url::fromUri('user-path:/' . $base_path); + } + else { + $url = $view->getUrl($args)->setOptions($url_options); } - $variables['rows'][$id]->url = _url($view->getUrl($args, $base_path), $url_options); + $variables['rows'][$id]->url = $url->toString(); $variables['rows'][$id]->count = intval($row->{$argument->count_alias}); if (isset($active_urls[$variables['rows'][$id]->url])) { $variables['rows'][$id]->attributes['class'][] = 'active'; @@ -891,11 +902,11 @@ function template_preprocess_views_view_rss(&$variables) { // there isn't one, use the global $base_url $link_display_id = $view->display_handler->getLinkDisplay(); if ($link_display_id && $display = $view->displayHandlers->get($link_display_id)) { - $path = $view->displayHandlers->get($link_display_id)->getPath(); + $url = $view->getUrl(NULL, $link_display_id); } - if ($path) { - $path = $view->getUrl(NULL, $path); + /** @var \Drupal\Core\Url $url */ + if ($url) { $url_options = array('absolute' => TRUE); if (!empty($view->exposed_raw_input)) { $url_options['query'] = $view->exposed_raw_input; @@ -903,11 +914,12 @@ function template_preprocess_views_view_rss(&$variables) { // Compare the link to the default home page; if it's the default home page, // just use $base_url. - if ($path == $config->get('page.front')) { - $path = ''; + $url_string = $url->setOptions($url_options)->toString(); + if ($url_string === Url::fromUri('user-path:/' . $config->get('page.front'))->toString()) { + $url_string = Url::fromRoute('')->setAbsolute()->toString(); } - $variables['link'] = check_url(_url($path, $url_options)); + $variables['link'] = $url_string; } $variables['langcode'] = String::checkPlain(\Drupal::languageManager()->getCurrentLanguage()->getId()); @@ -935,7 +947,7 @@ function template_preprocess_views_view_row_rss(&$variables) { $item = $variables['row']; $variables['title'] = String::checkPlain($item->title); - $variables['link'] = check_url($item->link); + $variables['link'] = $item->link; $variables['description'] = String::checkPlain($item->description); $variables['item_elements'] = empty($item->elements) ? '' : format_xml_elements($item->elements); } diff --git a/core/modules/views/views.tokens.inc b/core/modules/views/views.tokens.inc index 4160d857217ae4f5246849a93dc2188b72d9ac1f..85f6ec07194475e9b322cea06cfbc3d0ee2948d6 100644 --- a/core/modules/views/views.tokens.inc +++ b/core/modules/views/views.tokens.inc @@ -80,6 +80,7 @@ function views_tokens($type, $tokens, array $data = array(), array $options = ar $replacements = array(); if ($type == 'view' && !empty($data['view'])) { + /** @var \Drupal\views\ViewExecutable $view */ $view = $data['view']; foreach ($tokens as $name => $original) { @@ -102,8 +103,8 @@ function views_tokens($type, $tokens, array $data = array(), array $options = ar break; case 'url': - if ($path = $view->getUrl()) { - $replacements[$original] = _url($path, $url_options); + if ($url = $view->getUrl()) { + $replacements[$original] = $url->setOptions($url_options)->toString(); } break; case 'base-table': @@ -132,7 +133,7 @@ function views_tokens($type, $tokens, array $data = array(), array $options = ar // [view:url:*] nested tokens. This only works if Token module is installed. if ($url_tokens = $token_service->findWithPrefix($tokens, 'url')) { if ($path = $view->getUrl()) { - $replacements += $token_service->generate('url', $url_tokens, array('path' => $path), $options); + $replacements += $token_service->generate('url', $url_tokens, array('path' => $url->getInternalPath()), $options); } } } diff --git a/core/modules/views_ui/tests/src/Unit/ViewListBuilderTest.php b/core/modules/views_ui/tests/src/Unit/ViewListBuilderTest.php index b77f5959f1b2b403b28b9f51a32465d6fb67a448..a42a879e22ce0c390023f87a7c4deb1e6e4eccc9 100644 --- a/core/modules/views_ui/tests/src/Unit/ViewListBuilderTest.php +++ b/core/modules/views_ui/tests/src/Unit/ViewListBuilderTest.php @@ -125,7 +125,8 @@ public function testBuildRowEntityList() { $views_data = $this->getMockBuilder('Drupal\views\ViewsData') ->disableOriginalConstructor() ->getMock(); - $executable_factory = new ViewExecutableFactory($user, $request_stack, $views_data); + $route_provider = $this->getMock('Drupal\Core\Routing\RouteProviderInterface'); + $executable_factory = new ViewExecutableFactory($user, $request_stack, $views_data, $route_provider); $container->set('views.executable', $executable_factory); $container->set('plugin.manager.views.display', $display_manager); \Drupal::setContainer($container);