diff --git a/core/core.services.yml b/core/core.services.yml index 53a2725..c420c71 100644 --- a/core/core.services.yml +++ b/core/core.services.yml @@ -91,6 +91,11 @@ services: arguments: ['@current_user'] tags: - { name: cache.context} + cache_context.user.is_anonymous: + class: Drupal\Core\Cache\IsAnonymousUserCacheContext + arguments: ['@current_user'] + tags: + - { name: cache.context} cache_context.languages: class: Drupal\Core\Cache\LanguagesCacheContext arguments: ['@language_manager'] diff --git a/core/lib/Drupal/Core/Cache/IsAnonymousUserCacheContext.php b/core/lib/Drupal/Core/Cache/IsAnonymousUserCacheContext.php new file mode 100644 index 0000000..651b151 --- /dev/null +++ b/core/lib/Drupal/Core/Cache/IsAnonymousUserCacheContext.php @@ -0,0 +1,29 @@ +user->isAnonymous()) ? '1' : '0'; + } + +} diff --git a/core/modules/language/js/language.switcher.js b/core/modules/language/js/language.switcher.js new file mode 100644 index 0000000..a63bc67 --- /dev/null +++ b/core/modules/language/js/language.switcher.js @@ -0,0 +1,24 @@ +/** + * @file + * Attaches behaviors for the Language switch block. + */ +(function ($, Drupal, drupalSettings) { + + "use strict"; + + Drupal.behaviors.language_switcher = { + attach: function (context) { + if (!drupalSettings.path.isFront) { + var $context = $(context); + // Set the destination parameter on each of the contextual links. + var destination = 'destination=' + Drupal.encodePath(drupalSettings.path.currentPath); + $context.find('.language-switcher a').each(function () { + var url = this.getAttribute('href'); + var glue = (url.indexOf('?') === -1) ? '?' : '&'; + this.setAttribute('href', url + glue + destination); + }); + } + } + } + +})(jQuery, Drupal, drupalSettings); diff --git a/core/modules/language/language.libraries.yml b/core/modules/language/language.libraries.yml index 0c4cc8c..75c4c86 100644 --- a/core/modules/language/language.libraries.yml +++ b/core/modules/language/language.libraries.yml @@ -9,3 +9,12 @@ drupal.language.admin: - core/jquery - core/drupal - core/jquery.once + +drupal.language.switcher: + version: VERSION + js: + js/language.switcher.js: {} + dependencies: + - core/jquery + - core/drupal + - core/drupalSettings diff --git a/core/modules/language/language.routing.yml b/core/modules/language/language.routing.yml index 84740ee..63c199a 100644 --- a/core/modules/language/language.routing.yml +++ b/core/modules/language/language.routing.yml @@ -85,3 +85,10 @@ language.content_settings_page: _form: 'Drupal\language\Form\ContentLanguageSettingsForm' requirements: _permission: 'administer languages' + +language.redirect: + path: '/language-redirect' + defaults: + _controller: '\Drupal\language\Controller\LanguageController::languageSwitcherRedirect' + requirements: + _access: 'TRUE' diff --git a/core/modules/language/src/Controller/LanguageController.php b/core/modules/language/src/Controller/LanguageController.php new file mode 100644 index 0000000..a255132 --- /dev/null +++ b/core/modules/language/src/Controller/LanguageController.php @@ -0,0 +1,33 @@ +query->get('destination'); + if (!isset($destination)) { + $destination = $this->getUrlGenerator()->generateFromRoute(''); + } + + return new RedirectResponse($destination, 301); + } + +} diff --git a/core/modules/language/src/Plugin/Block/LanguageBlock.php b/core/modules/language/src/Plugin/Block/LanguageBlock.php index 7aa9bb8..ecdfc98 100644 --- a/core/modules/language/src/Plugin/Block/LanguageBlock.php +++ b/core/modules/language/src/Plugin/Block/LanguageBlock.php @@ -8,6 +8,8 @@ namespace Drupal\language\Plugin\Block; use Drupal\Core\Block\BlockBase; +use Drupal\Core\Cache\Cache; +use Drupal\Core\Language\LanguageInterface; use Drupal\Core\Path\PathMatcherInterface; use Drupal\Core\Session\AccountInterface; use Drupal\Core\Language\LanguageManagerInterface; @@ -35,6 +37,13 @@ class LanguageBlock extends BlockBase implements ContainerFactoryPluginInterface protected $languageManager; /** + * The current user. + * + * @var \Drupal\Core\Session\AccountInterface + */ + protected $currentUser; + + /** * The path matcher. * * @var \Drupal\Core\Path\PathMatcherInterface @@ -52,12 +61,15 @@ class LanguageBlock extends BlockBase implements ContainerFactoryPluginInterface * The plugin implementation definition. * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager * The language manager. + * @param \Drupal\Core\Session\AccountInterface $current_user + * The current user. * @param \Drupal\Core\Path\PathMatcherInterface $path_matcher * The path matcher. */ - public function __construct(array $configuration, $plugin_id, $plugin_definition, LanguageManagerInterface $language_manager, PathMatcherInterface $path_matcher) { + public function __construct(array $configuration, $plugin_id, $plugin_definition, LanguageManagerInterface $language_manager, AccountInterface $current_user, PathMatcherInterface $path_matcher) { parent::__construct($configuration, $plugin_id, $plugin_definition); $this->languageManager = $language_manager; + $this->currentUser = $current_user; $this->pathMatcher = $path_matcher; } @@ -71,6 +83,7 @@ public static function create(ContainerInterface $container, array $configuratio $plugin_id, $plugin_definition, $container->get('language_manager'), + $container->get('current_user'), $container->get('path.matcher') ); } @@ -88,9 +101,16 @@ protected function blockAccess(AccountInterface $account) { */ public function build() { $build = array(); - $route_name = $this->pathMatcher->isFrontPage() ? '' : ''; $type = $this->getDerivativeId(); - $links = $this->languageManager->getLanguageSwitchLinks($type, Url::fromRoute($route_name)); + + // For authenticated users, we just rely on Javascript for enhancing the + // block with the destination. In case of anonymous, we use + // post_render_cache for replacing a placeholder. + $switch_links_options = []; + if (\Drupal::currentUser()->isAnonymous()) { + $switch_links_options['destination'] = 'data-language-switch-link-placeholder'; + } + $links = $this->languageManager->getLanguageSwitchLinks($type, Url::fromRoute('language.redirect', $switch_links_options)); if (isset($links->links)) { $build = array( @@ -99,21 +119,52 @@ public function build() { '#attributes' => array( 'class' => array( "language-switcher-{$links->method_id}", + "language-switcher" ), ), '#set_active_class' => TRUE, ); + + if (\Drupal::currentUser()->isAuthenticated()) { + $build['#attached']['library'][] = 'language/drupal.language.switcher'; + } + else { + $build['#post_render_cache']['\Drupal\language\Controller\LanguageController::setLanguageSwitchDestination'] = array( + // Collect the current state that determines the destination. + array( + 'path' => \Drupal::routeMatch() + ->getRouteName() ? Url::fromRouteMatch(\Drupal::routeMatch()) + ->getInternalPath() : '', + 'front' => \Drupal::service('path.matcher')->isFrontPage(), + 'language' => \Drupal::languageManager() + ->getCurrentLanguage(LanguageInterface::TYPE_URL)->getId(), + 'query' => \Drupal::request()->query->all(), + ) + ); + } } + return $build; } /** * {@inheritdoc} - * - * @todo Make cacheable in https://drupal.org/node/2232375. + */ + public function getCacheContexts() { + // The "Language switcher" block must be cached per language. + return [ + 'languages', + 'user.is_anonymous' + ]; + } + + /** + * {@inheritdoc} */ public function getCacheMaxAge() { - return 0; + // @todo Ensure whenever language settings change, that appropriate cache + // tags are invalidated, that allows us to cache this block forever. + return Cache::PERMANENT; } }