diff -u b/core/core.services.yml b/core/core.services.yml --- b/core/core.services.yml +++ b/core/core.services.yml @@ -177,7 +177,7 @@ - { name: theme_negotiator, priority: -100 } theme.negotiator.ajax_base_page: class: Drupal\Core\Theme\AjaxBasePageNegotiator - arguments: ['@csrf_token'] + arguments: ['@csrf_token', '@config.factory'] tags: - { name: theme_negotiator, priority: 1000 } container.namespaces: diff -u b/core/includes/theme.inc b/core/includes/theme.inc --- b/core/includes/theme.inc +++ b/core/includes/theme.inc @@ -113,9 +113,6 @@ $base_theme[] = $themes[$ancestor]; } _drupal_theme_initialize($themes[$theme], array_reverse($base_theme)); - - // Themes can have alter functions, so reset the drupal_alter() cache. - \Drupal::moduleHandler()->resetImplementations(); } /** diff -u b/core/lib/Drupal/Core/Theme/AjaxBasePageNegotiator.php b/core/lib/Drupal/Core/Theme/AjaxBasePageNegotiator.php --- b/core/lib/Drupal/Core/Theme/AjaxBasePageNegotiator.php +++ b/core/lib/Drupal/Core/Theme/AjaxBasePageNegotiator.php @@ -8,6 +8,7 @@ namespace Drupal\Core\Theme; use Drupal\Core\Access\CsrfTokenGenerator; +use Drupal\Core\Config\ConfigFactory; use Symfony\Cmf\Component\Routing\RouteObjectInterface; use Symfony\Component\HttpFoundation\Request; @@ -38,13 +39,23 @@ protected $csrfGenerator; /** + * The config factory. + * + * @var \Drupal\Core\Config\ConfigFactory + */ + protected $configFactory; + + /** * Constructs a new AjaxBasePageNegotiator. * * @param \Drupal\Core\Access\CsrfTokenGenerator $token_generator * The CSRF token generator. + * @param \Drupal\Core\Config\ConfigFactory $config_factory + * The config factory. */ - public function __construct(CsrfTokenGenerator $token_generator) { + public function __construct(CsrfTokenGenerator $token_generator, ConfigFactory $config_factory) { $this->csrfGenerator = $token_generator; + $this->configFactory = $config_factory; } /** @@ -59,8 +70,12 @@ $theme = $ajax_page_state['theme']; $token = $ajax_page_state['theme_token']; - // Ensure that the user only access a theme they are allowed to see. - if ($this->csrfGenerator->validate($token, $theme)) { + // Prevent a request forgery from giving a person access to a theme they + // shouldn't be otherwise allowed to see. However, since everyone is allowed + // to see the default theme, token validation isn't required for that, and + // bypassing it allows most use-cases to work even when accessed from the + // page cache. + if ($theme === $this->configFactory->get('system.theme')->get('default') || $this->csrfGenerator->validate($token, $theme)) { return $theme; } } diff -u b/core/modules/system/lib/Drupal/system/Tests/Theme/ThemeTest.php b/core/modules/system/lib/Drupal/system/Tests/Theme/ThemeTest.php --- b/core/modules/system/lib/Drupal/system/Tests/Theme/ThemeTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Theme/ThemeTest.php @@ -287,3 +287,2 @@ - } reverted: --- b/core/modules/system/lib/Drupal/system/Theme/AdminNegotiator.php +++ /dev/null @@ -1,71 +0,0 @@ -user = $user; - $this->configFactory = $config_factory; - $this->entityManager = $entity_manager; - } - - /** - * {@inheritdoc} - */ - public function determineActiveTheme(Request $request) { - $path = $request->attributes->get('_system_path'); - - // Don't break if the user_role entity is not available in order to decouple - // system and user module. - if ($this->entityManager->hasController('user_role', 'storage') && $this->user->hasPermission('view the administration theme') && path_is_admin($path)) { - return $this->configFactory->get('system.theme')->get('admin'); - } - } - -} diff -u b/core/modules/system/system.services.yml b/core/modules/system/system.services.yml --- b/core/modules/system/system.services.yml +++ b/core/modules/system/system.services.yml @@ -27,5 +26,0 @@ - theme.negotiator.admin_theme: - class: Drupal\system\Theme\AdminNegotiator - arguments: ['@current_user', '@config.factory', '@entity.manager'] - tags: - - { name: theme_negotiator, priority: -40 } diff -u b/core/modules/system/tests/modules/theme_test/lib/Drupal/theme_test/Theme/HighPriorityThemeNegotiator.php b/core/modules/system/tests/modules/theme_test/lib/Drupal/theme_test/Theme/HighPriorityThemeNegotiator.php --- b/core/modules/system/tests/modules/theme_test/lib/Drupal/theme_test/Theme/HighPriorityThemeNegotiator.php +++ b/core/modules/system/tests/modules/theme_test/lib/Drupal/theme_test/Theme/HighPriorityThemeNegotiator.php @@ -28 +28 @@ -} +} diff -u b/core/modules/user/user.services.yml b/core/modules/user/user.services.yml --- b/core/modules/user/user.services.yml +++ b/core/modules/user/user.services.yml @@ -33 +33,5 @@ - +ActiveTrailSubscriber theme.negotiator.admin_theme: + class: Drupal\system\Theme\AdminNegotiator + arguments: ['@current_user', '@config.factory', '@entity.manager'] + tags: + - { name: theme_negotiator, priority: -40 } diff -u b/core/modules/views/views.module b/core/modules/views/views.module --- b/core/modules/views/views.module +++ b/core/modules/views/views.module @@ -247,20 +247,6 @@ } /** - * Implement hook_menu(). - */ -function views_menu() { - $items = array(); - $items['views/ajax'] = array( - 'title' => 'Views', - 'theme callback' => 'ajax_base_page_theme', - 'route_name' => 'views.ajax', - 'type' => MENU_CALLBACK, - ); - return $items; -} - -/** * Implement hook_menu_alter(). */ function views_menu_alter(&$callbacks) { @@ -278,6 +264,20 @@ } /** + * Implement hook_menu(). + */ +function views_menu() { + $items = array(); + $items['views/ajax'] = array( + 'title' => 'Views', + 'theme callback' => 'ajax_base_page_theme', + 'route_name' => 'views.ajax', + 'type' => MENU_CALLBACK, + ); + return $items; +} + +/** * Implement hook_menu_alter(). */ function views_menu_alter(&$callbacks) { only in patch2: unchanged: --- /dev/null +++ b/core/modules/user/lib/Drupal/user/Theme/AdminNegotiator.php @@ -0,0 +1,71 @@ +user = $user; + $this->configFactory = $config_factory; + $this->entityManager = $entity_manager; + } + + /** + * {@inheritdoc} + */ + public function determineActiveTheme(Request $request) { + $path = $request->attributes->get('_system_path'); + + // Don't break if the user_role entity is not available in order to decouple + // system and user module. + if ($this->entityManager->hasController('user_role', 'storage') && $this->user->hasPermission('view the administration theme') && path_is_admin($path)) { + return $this->configFactory->get('system.theme')->get('admin'); + } + } + +}