diff --git a/core/modules/views/src/Form/ViewsExposedForm.php b/core/modules/views/src/Form/ViewsExposedForm.php index 72f754be2bfb..5e2eee9ae836 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\Html; 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 @@ class ViewsExposedForm extends FormBase { '#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 c1296b20fed6..cdf39eb19ee0 100644 --- a/core/modules/views/src/Form/ViewsForm.php +++ b/core/modules/views/src/Form/ViewsForm.php @@ -133,7 +133,8 @@ class ViewsForm implements FormInterface, ContainerInjectionInterface { $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 759597716233..93bd846eb0c5 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 @@ abstract class DisplayPluginBase extends PluginBase implements DisplayPluginInte } } + /** + * {@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 @@ abstract class DisplayPluginBase extends PluginBase implements DisplayPluginInte */ 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 e84348cd7572..761fcd66f0c8 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 @@ interface DisplayPluginInterface { */ 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 6223c522df26..65328edcfcd5 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 @@ interface DisplayRouterInterface extends DisplayPluginInterface { */ 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 67171ab1c465..24e76ba64483 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 @@ class Feed extends PathPluginBase { $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 773893360710..3a8473e84c7c 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 @@ abstract class PathPluginBase extends DisplayPluginBase implements DisplayRouter } } - $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 @@ abstract class PathPluginBase extends DisplayPluginBase implements DisplayRouter // 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 @@ abstract class PathPluginBase extends DisplayPluginBase implements DisplayRouter * {@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 6d0ae5b9c114..82b121278e6a 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 @@ class RssFields extends RowPluginBase { // 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 @@ class RssFields extends RowPluginBase { $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 f41378f4111d..e0b9fb57c146 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 @@ class Opml extends StylePluginBase { } $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 f59711683d59..fbbf4dd59b6b 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 @@ namespace Drupal\views\Plugin\views\style; 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 @@ class Rss extends StylePluginBase { } $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 2a59e9f22bb0..bc0eb0de4585 100644 --- a/core/modules/views/src/Tests/Handler/FilterEqualityTest.php +++ b/core/modules/views/src/Tests/Handler/FilterEqualityTest.php @@ -76,6 +76,8 @@ class FilterEqualityTest extends ViewUnitTestBase { $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 @@ class FilterEqualityTest extends ViewUnitTestBase { $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 @@ class FilterEqualityTest extends ViewUnitTestBase { $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 9277da50b908..e4914e6cc2a3 100644 --- a/core/modules/views/src/Tests/Handler/FilterNumericTest.php +++ b/core/modules/views/src/Tests/Handler/FilterNumericTest.php @@ -80,6 +80,8 @@ class FilterNumericTest extends ViewUnitTestBase { $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 @@ class FilterNumericTest extends ViewUnitTestBase { $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 @@ class FilterNumericTest extends ViewUnitTestBase { $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 @@ class FilterNumericTest extends ViewUnitTestBase { $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 @@ class FilterNumericTest extends ViewUnitTestBase { $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 @@ class FilterNumericTest extends ViewUnitTestBase { $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 baa34893b1e8..f67eb227e35e 100644 --- a/core/modules/views/src/Tests/Handler/FilterStringTest.php +++ b/core/modules/views/src/Tests/Handler/FilterStringTest.php @@ -120,6 +120,8 @@ class FilterStringTest extends ViewUnitTestBase { $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 @@ class FilterStringTest extends ViewUnitTestBase { $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 @@ class FilterStringTest extends ViewUnitTestBase { $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 @@ class FilterStringTest extends ViewUnitTestBase { $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 @@ class FilterStringTest extends ViewUnitTestBase { $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 @@ class FilterStringTest extends ViewUnitTestBase { $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 @@ class FilterStringTest extends ViewUnitTestBase { $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 @@ class FilterStringTest extends ViewUnitTestBase { $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 @@ class FilterStringTest extends ViewUnitTestBase { $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 @@ class FilterStringTest extends ViewUnitTestBase { $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 @@ class FilterStringTest extends ViewUnitTestBase { $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 @@ class FilterStringTest extends ViewUnitTestBase { $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 @@ class FilterStringTest extends ViewUnitTestBase { $filters = array( 'name' => array( 'id' => 'name', + 'plugin_id' => 'string', 'table' => 'views_test_data', 'field' => 'name', 'relationship' => 'none', @@ -778,6 +803,7 @@ class FilterStringTest extends ViewUnitTestBase { ), '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 f3f0028074b3..d4bcc67bd4f1 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 @@ class TokenReplaceTest extends ViewUnitTestBase { '[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 e99ca1de8756..27c4e559cd5e 100644 --- a/core/modules/views/src/Tests/ViewExecutableTest.php +++ b/core/modules/views/src/Tests/ViewExecutableTest.php @@ -322,18 +322,6 @@ class ViewExecutableTest extends ViewUnitTestBase { $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 9791a887cd60..db01ad888a54 100644 --- a/core/modules/views/src/ViewExecutable.php +++ b/core/modules/views/src/ViewExecutable.php @@ -10,7 +10,9 @@ namespace Drupal\views; 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 @@ class ViewExecutable { $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 @@ class ViewExecutable { 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 @@ class ViewExecutable { } // 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 04f61960144c..0975a48d2939 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 @@ class ViewExecutableFactory { * 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 a289d8e2991a..87c79a3d9c60 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 @@ class ResultTest extends UnitTestCase { $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 959ce3d7e8d4..7b0c14d8809a 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 @@ class CounterTest extends UnitTestCase { $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 e82468e54f0c..17b61c8d091f 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 @@ class ViewExecutableFactoryTest extends UnitTestCase { $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 ecabe3ffa9a8..d980f8237361 100644 --- a/core/modules/views/tests/src/Unit/ViewExecutableUnitTest.php +++ b/core/modules/views/tests/src/Unit/ViewExecutableTest.php @@ -2,42 +2,30 @@ /** * @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 { - - /** - * The mocked views data. - * - * @var \Drupal\views\ViewsData|\PHPUnit_Framework_MockObject_MockObject - */ - protected $viewsData; - - /** - * The mocked user. - * - * @var \Drupal\Core\Session\AccountInterface|\PHPUnit_Framework_MockObject_MockObject - */ - protected $user; +class ViewExecutableTest extends UnitTestCase { /** * A mocked display collection. * * @var \Drupal\views\DisplayPluginCollection|\PHPUnit_Framework_MockObject_MockObject */ - protected $displayCollection; + protected $displayHandlers; /** * The mocked view executable. @@ -46,20 +34,70 @@ class ViewExecutableUnitTest extends UnitTestCase { */ 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. + * + * @var \Drupal\Core\Session\AccountInterface|\PHPUnit_Framework_MockObject_MockObject + */ + protected $user; + + /** + * The mocked views data. + * + * @var \Drupal\views\ViewsData|\PHPUnit_Framework_MockObject_MockObject + */ + protected $viewsData; + + /** + * The mocked display handler. + * + * @var \Drupal\views\Plugin\views\display\DisplayPluginInterface|\PHPUnit_Framework_MockObject_MockObject + */ + protected $displayHandler; + + /** + * The mocked route provider. + * + * @var \Drupal\Core\Routing\RouteProviderInterface|\PHPUnit_Framework_MockObject_MockObject + */ + protected $routeProvider; + /** * {@inheritdoc} */ 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() @@ -72,6 +110,140 @@ class ViewExecutableUnitTest extends UnitTestCase { \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 @@ class ViewExecutableUnitTest extends UnitTestCase { 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 @@ class ViewExecutableUnitTest extends UnitTestCase { '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 @@ class ViewExecutableUnitTest extends UnitTestCase { 'test_hook__default2', 'test_hook__test_view', 'test_hook' - ); + ]; $this->assertEquals($expected, $view->buildThemeFunctions('test_hook')); } @@ -280,7 +452,7 @@ class ViewExecutableUnitTest extends UnitTestCase { ); $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 @@ class ViewExecutableUnitTest extends UnitTestCase { $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 ab9bdf8c9ae4..aa7a8c9c8242 100644 --- a/core/modules/views/tests/src/Unit/ViewsTest.php +++ b/core/modules/views/tests/src/Unit/ViewsTest.php @@ -34,7 +34,8 @@ class ViewsTest extends UnitTestCase { $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 f98ad1c6e3ea..1a01543c6747 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 0e70dd47d33a..dca6e3c3cfde 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); } - $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'; @@ -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 4160d857217a..85f6ec071944 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 b77f5959f1b2..a42a879e22ce 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 @@ class ViewListBuilderTest extends UnitTestCase { $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);