core/core.libraries.yml | 1 + core/core.services.yml | 2 +- .../CurrentPathDestinationLinkResponseFilter.php | 27 ++++++++++++++++++---- core/misc/current-path-destination-link.js | 10 +++++--- ...urrentPathDestinationLinkResponseFilterTest.php | 16 ++++++++----- 5 files changed, 42 insertions(+), 14 deletions(-) diff --git a/core/core.libraries.yml b/core/core.libraries.yml index 3a3771d..e800cd4 100644 --- a/core/core.libraries.yml +++ b/core/core.libraries.yml @@ -84,6 +84,7 @@ drupal.current-path-destination-link: dependencies: - core/drupal - core/drupalSettings + - core/jquery drupal.ajax: version: VERSION diff --git a/core/core.services.yml b/core/core.services.yml index fb77cc5..4c716e0 100644 --- a/core/core.services.yml +++ b/core/core.services.yml @@ -1575,6 +1575,6 @@ services: - { name: event_subscriber } response_filter.current_path_destination_link: class: Drupal\Core\EventSubscriber\CurrentPathDestinationLinkResponseFilter - arguments: ['@current_user', '@path.current'] + arguments: ['@current_user', '@path.current', '@request_stack'] tags: - { name: event_subscriber } diff --git a/core/lib/Drupal/Core/EventSubscriber/CurrentPathDestinationLinkResponseFilter.php b/core/lib/Drupal/Core/EventSubscriber/CurrentPathDestinationLinkResponseFilter.php index 2569f1c..cbad2b8 100644 --- a/core/lib/Drupal/Core/EventSubscriber/CurrentPathDestinationLinkResponseFilter.php +++ b/core/lib/Drupal/Core/EventSubscriber/CurrentPathDestinationLinkResponseFilter.php @@ -7,9 +7,11 @@ namespace Drupal\Core\EventSubscriber; +use Drupal\Component\Utility\UrlHelper; use Drupal\Core\Path\CurrentPathStack; use Drupal\Core\Session\AccountInterface; use Symfony\Component\EventDispatcher\EventSubscriberInterface; +use Symfony\Component\HttpFoundation\RequestStack; use Symfony\Component\HttpKernel\Event\FilterResponseEvent; use Symfony\Component\HttpKernel\KernelEvents; @@ -38,16 +40,26 @@ class CurrentPathDestinationLinkResponseFilter implements EventSubscriberInterfa protected $currentPath; /** + * The request stack. + * + * @var \Symfony\Component\HttpFoundation\RequestStack + */ + protected $requestStack; + + /** * Constructs a new CurrentPathDestinationLinkResponseFilter instance. * * @param \Drupal\Core\Session\AccountInterface $current_user * The current user. * @param \Drupal\Core\Path\CurrentPathStack $current_path * The current path. + * @param \Symfony\Component\HttpFoundation\RequestStack + * The request stack. */ - public function __construct(AccountInterface $current_user, CurrentPathStack $current_path) { + public function __construct(AccountInterface $current_user, CurrentPathStack $current_path, RequestStack $request_stack) { $this->currentUser = $current_user; $this->currentPath = $current_path; + $this->requestStack = $request_stack; } /** @@ -72,7 +84,8 @@ public function onResponse(FilterResponseEvent $event) { $response = $event->getResponse(); $response->setContent(static::setCurrentPathAsDestination( $response->getContent(), - ltrim($this->currentPath->getPath(), '/') + ltrim($this->currentPath->getPath(), '/'), + $this->requestStack->getMasterRequest()->query->all() )); } @@ -86,6 +99,8 @@ public function onResponse(FilterResponseEvent $event) { * The HTML markup to update. * @param string $current_path * The system path of the currently active page. + * @param string[] $current_query + * The query string for the currently active page. * * @return string * The updated HTML markup. @@ -95,8 +110,12 @@ public function onResponse(FilterResponseEvent $event) { * https://www.drupal.org/comment/7938201#comment-7938201) then we can get * rid of this manual parsing and use DOMDocument instead. */ - public static function setCurrentPathAsDestination($html_markup, $current_path) { - $destination = 'destination=' . $current_path; + public static function setCurrentPathAsDestination($html_markup, $current_path, array $current_query) { + $destination_value = $current_path; + if (!empty($current_query)) { + $destination_value .= '?' . UrlHelper::buildQuery($current_query); + } + $destination = 'destination=' . UrlHelper::encodePath($destination_value); $offset = 0; while (($pos_match = strpos($html_markup, 'data-current-path-destination', $offset)) !== FALSE) { diff --git a/core/misc/current-path-destination-link.js b/core/misc/current-path-destination-link.js index de832c1..32c7dd1 100644 --- a/core/misc/current-path-destination-link.js +++ b/core/misc/current-path-destination-link.js @@ -3,7 +3,7 @@ * Attaches behaviors for Drupal's current path destination link query string. */ -(function (Drupal, drupalSettings) { +(function (Drupal, drupalSettings, $) { "use strict"; @@ -14,7 +14,11 @@ */ Drupal.behaviors.currentPathDestinationLink = { attach: function (context) { - var destination = 'destination=' + Drupal.encodePath(drupalSettings.path.currentPath); + var destinationValue = drupalSettings.path.currentPath; + if (drupalSettings.path.currentQuery) { + destinationValue += '?' + $.param(drupalSettings.path.currentQuery); + } + var destination = 'destination=' + Drupal.encodePath(destinationValue); var links = context.querySelectorAll('[data-current-path-destination]'); for (var i = 0; i < links.length; i++) { var link = links[i]; @@ -25,4 +29,4 @@ } }; -})(Drupal, drupalSettings); +})(Drupal, drupalSettings, jQuery); diff --git a/core/tests/Drupal/Tests/Core/EventSubscriber/CurrentPathDestinationLinkResponseFilterTest.php b/core/tests/Drupal/Tests/Core/EventSubscriber/CurrentPathDestinationLinkResponseFilterTest.php index 4ecfa4e..7a2004b 100644 --- a/core/tests/Drupal/Tests/Core/EventSubscriber/CurrentPathDestinationLinkResponseFilterTest.php +++ b/core/tests/Drupal/Tests/Core/EventSubscriber/CurrentPathDestinationLinkResponseFilterTest.php @@ -59,14 +59,16 @@ public function providerTestSetCurrentPathAsDestination() { $situations = [ // Without a "data-current-path-destination" attribute present, nothing // happens. - 0 => ['path' => 'foo', 'attributes' => ['href' => '/some/path'], 'expected href' => '/some/path'], + 0 => ['path' => 'foo/bar', 'attributes' => ['href' => '/some/path'], 'query' => ['llama' => 'awesome', 'kitten' => 'cool'], 'expected href' => '/some/path'], // With a "data-current-path-destination" attribute present, the // "destination" query string is added. - 1 => ['path' => 'foo', 'attributes' => ['href' => '/some/path', 'data-current-path-destination' => TRUE], 'expected href' => '/some/path?destination=foo'], + 1 => ['path' => 'foo/bar', 'attributes' => ['href' => '/some/path', 'data-current-path-destination' => TRUE], 'query' => [], 'expected href' => '/some/path?destination=foo/bar'], + // Same, but now with a query string. + 2 => ['path' => 'foo/bar', 'attributes' => ['href' => '/some/path', 'data-current-path-destination' => TRUE], 'query' => ['llama' => 'awesome', 'kitten' => 'cool'], 'expected href' => '/some/path?destination=foo/bar%3Fllama%3Dawesome%26kitten%3Dcool'], // With a "data-current-path-destination" attribute present on a link that // already has a "destination" query string, we just append it. We do not // babysit broken code in CurrentPathDestinationLinkResponseFilter. - 2 => ['path' => 'foo', 'attributes' => ['href' => '/some/path?destination=/already/there', 'data-current-path-destination' => TRUE], 'expected href' => '/some/path?destination=/already/there&destination=foo'], + 3 => ['path' => 'foo/bar', 'attributes' => ['href' => '/some/path?destination=/already/there', 'data-current-path-destination' => TRUE], 'query' => [], 'expected href' => '/some/path?destination=/already/there&destination=foo/bar'], ]; // Loop over the surrounding HTML variations. @@ -98,7 +100,7 @@ public function providerTestSetCurrentPathAsDestination() { unset($active_attributes['data-current-path-destination']); $target_markup = $create_markup(new Attribute($active_attributes)); - $data[] = [$source_markup, $situation['path'], $target_markup]; + $data[] = [$source_markup, $situation['path'], $situation['query'], $target_markup]; } } } @@ -114,14 +116,16 @@ public function providerTestSetCurrentPathAsDestination() { * The original HTML markup. * @param string $current_path * The system path of the currently active page. + * @param string[] $current_query + * The query string for the currently active page. * @param string $expected_html_markup * The expected updated HTML markup. * * @dataProvider providerTestSetCurrentPathAsDestination * @covers ::setCurrentPathAsDestination */ - public function testSetCurrentPathAsDestination($html_markup, $current_path, $expected_html_markup) { - $this->assertSame($expected_html_markup, CurrentPathDestinationLinkResponseFilter::setCurrentPathAsDestination($html_markup, $current_path)); + public function testSetCurrentPathAsDestination($html_markup, $current_path, array $current_query, $expected_html_markup) { + $this->assertSame($expected_html_markup, CurrentPathDestinationLinkResponseFilter::setCurrentPathAsDestination($html_markup, $current_path, $current_query)); } }