diff --git a/core/core.services.yml b/core/core.services.yml index f3715856ba6628a2adb3560cd49d6e80f658bd09..6104db91d26f1ac3aff6b6678dffe3fd4d43fdb5 100644 --- a/core/core.services.yml +++ b/core/core.services.yml @@ -165,21 +165,6 @@ services: calls: - [addSubscriber, ['@http_client_simpletest_subscriber']] - [setUserAgent, ['Drupal (+http://drupal.org/)']] - theme.negotiator: - class: Drupal\Core\Theme\ThemeNegotiator - arguments: ['@access_check.theme'] - calls: - - [setRequest, ['@request']] - theme.negotiator.default: - class: Drupal\Core\Theme\DefaultNegotiator - arguments: ['@config.factory'] - tags: - - { name: theme_negotiator, priority: -100 } - theme.negotiator.ajax_base_page: - class: Drupal\Core\Theme\AjaxBasePageNegotiator - arguments: ['@csrf_token', '@config.factory'] - tags: - - { name: theme_negotiator, priority: 1000 } container.namespaces: class: ArrayObject arguments: [ '%container.namespaces%' ] diff --git a/core/includes/ajax.inc b/core/includes/ajax.inc index 3ff6c176751068b4b9734a68f1a261c2cea2ebd2..3f2389f57e9591de612230c2f74359a01f7ccd5c 100644 --- a/core/includes/ajax.inc +++ b/core/includes/ajax.inc @@ -302,6 +302,42 @@ function ajax_render($commands = array()) { return drupal_json_encode($commands); } +/** + * Theme callback: Returns the correct theme for an Ajax request. + * + * Many different pages can invoke an Ajax request to system/ajax or another + * generic Ajax path. It is almost always desired for an Ajax response to be + * rendered using the same theme as the base page, because most themes are built + * with the assumption that they control the entire page, so if the CSS for two + * themes are both loaded for a given page, they may conflict with each other. + * For example, Bartik is Drupal's default theme, and Seven is Drupal's default + * administration theme. Depending on whether the "Use the administration theme + * when editing or creating content" checkbox is checked, the node edit form may + * be displayed in either theme, but the Ajax response to the Field module's + * "Add another item" button should be rendered using the same theme as the rest + * of the page. Therefore, system_menu() sets the 'theme callback' for + * 'system/ajax' to this function, and it is recommended that modules + * implementing other generic Ajax paths do the same. + * + * @see system_menu() + * @see file_menu() + */ +function ajax_base_page_theme() { + if (!empty($_POST['ajax_page_state']['theme']) && !empty($_POST['ajax_page_state']['theme_token'])) { + $theme = $_POST['ajax_page_state']['theme']; + $token = $_POST['ajax_page_state']['theme_token']; + + // 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 === \Drupal::config('system.theme')->get('default') || drupal_valid_token($token, $theme)) { + return $theme; + } + } +} + /** * Converts the return value of a page callback into an Ajax commands array. * diff --git a/core/includes/common.inc b/core/includes/common.inc index fc46b9b389e548b11f32b25370a5be5a8389f6bb..36e069db66558aa49b2b3816bde4022bc3c09767 100644 --- a/core/includes/common.inc +++ b/core/includes/common.inc @@ -2331,7 +2331,7 @@ function drupal_get_js($scope = 'header', $javascript = NULL, $skip_alter = FALS global $theme_key; // Provide the page with information about the theme that's used, so that // a later AJAX request can be rendered using the same theme. - // @see \Drupal\Core\Theme\AjaxBasePageNegotiator + // @see ajax_base_page_theme() $setting['ajaxPageState']['theme'] = $theme_key; // Checks that the DB is available before filling theme_token. if (!defined('MAINTENANCE_MODE')) { @@ -3148,6 +3148,7 @@ function _drupal_bootstrap_full($skip = FALSE) { // Let all modules take action before the menu system handles the request. // We do not want this while running update.php. if (!defined('MAINTENANCE_MODE') || MAINTENANCE_MODE != 'update') { + menu_set_custom_theme(); drupal_theme_initialize(); } } diff --git a/core/includes/menu.inc b/core/includes/menu.inc index 2e2c50d2ea674e8d5613217c7bab45578ef8cb45..a8a30b6c9a3fbc53453473289b35faaa1057c0e1 100644 --- a/core/includes/menu.inc +++ b/core/includes/menu.inc @@ -463,8 +463,8 @@ function menu_set_item($path, $router_item) { * menu_router table. The value corresponding to the key 'map' holds the * loaded objects. The value corresponding to the key 'access' is TRUE if the * current user can access this page. The values corresponding to the keys - * 'title', 'page_arguments', and 'access_arguments', will be filled in based - * on the database values and the objects loaded. + * 'title', 'page_arguments', 'access_arguments', and 'theme_arguments' will + * be filled in based on the database values and the objects loaded. */ function menu_get_item($path = NULL, $router_item = NULL) { $router_items = &drupal_static(__FUNCTION__); @@ -501,6 +501,7 @@ function menu_get_item($path = NULL, $router_item = NULL) { if ($router_item['access']) { $router_item['map'] = $map; $router_item['page_arguments'] = array_merge(menu_unserialize($router_item['page_arguments'], $map), array_slice($map, $router_item['number_parts'])); + $router_item['theme_arguments'] = array_merge(menu_unserialize($router_item['theme_arguments'], $map), array_slice($map, $router_item['number_parts'])); } } $router_items[$path] = $router_item; @@ -1794,6 +1795,51 @@ function drupal_help_arg($arg = array()) { return $arg + array('', '', '', '', '', '', '', '', '', '', '', ''); } +/** + * Gets the custom theme for the current page, if there is one. + * + * @param $initialize + * This parameter should only be used internally; it is set to TRUE in order + * to force the custom theme to be initialized for the current page request. + * + * @return + * The machine-readable name of the custom theme, if there is one. + * + * @see menu_set_custom_theme() + */ +function menu_get_custom_theme($initialize = FALSE) { + $custom_theme = &drupal_static(__FUNCTION__); + // Skip this if the site is offline or being installed or updated, since the + // menu system may not be correctly initialized then. + if ($initialize && !_menu_site_is_offline(TRUE) && (!defined('MAINTENANCE_MODE') || (MAINTENANCE_MODE != 'update' && MAINTENANCE_MODE != 'install'))) { + // First allow modules to dynamically set a custom theme for the current + // page. Since we can only have one, the last module to return a valid + // theme takes precedence. + $custom_themes = array_filter(\Drupal::moduleHandler()->invokeAll('custom_theme'), 'drupal_theme_access'); + if (!empty($custom_themes)) { + $custom_theme = array_pop($custom_themes); + } + // If there is a theme callback function for the current page, execute it. + // If this returns a valid theme, it will override any theme that was set + // by a hook_custom_theme() implementation above. + $router_item = menu_get_item(); + if (!empty($router_item['access']) && !empty($router_item['theme_callback'])) { + $theme_name = call_user_func_array($router_item['theme_callback'], $router_item['theme_arguments']); + if (drupal_theme_access($theme_name)) { + $custom_theme = $theme_name; + } + } + } + return $custom_theme; +} + +/** + * Sets a custom theme for the current page, if there is one. + */ +function menu_set_custom_theme() { + menu_get_custom_theme(TRUE); +} + /** * Returns an array containing the names of system-defined (default) menus. */ @@ -3020,6 +3066,13 @@ function _menu_router_build($callbacks, $save = FALSE) { } } } + // Same for theme callbacks. + if (!isset($item['theme callback']) && isset($parent['theme callback'])) { + $item['theme callback'] = $parent['theme callback']; + if (!isset($item['theme arguments']) && isset($parent['theme arguments'])) { + $item['theme arguments'] = $parent['theme arguments']; + } + } // Same for load arguments: if a loader doesn't have any explict // arguments, try to find arguments in the parent. if (!isset($item['load arguments'])) { @@ -3056,6 +3109,8 @@ function _menu_router_build($callbacks, $save = FALSE) { 'page callback' => '', 'title arguments' => array(), 'title callback' => 't', + 'theme arguments' => array(), + 'theme callback' => '', 'description' => '', 'description arguments' => array(), 'description callback' => 't', @@ -3120,6 +3175,8 @@ function _menu_router_save($menu, $masks) { 'title', 'title_callback', 'title_arguments', + 'theme_callback', + 'theme_arguments', 'type', 'description', 'description_callback', @@ -3150,6 +3207,8 @@ function _menu_router_save($menu, $masks) { 'title' => $item['title'], 'title_callback' => $item['title callback'], 'title_arguments' => ($item['title arguments'] ? serialize($item['title arguments']) : ''), + 'theme_callback' => $item['theme callback'], + 'theme_arguments' => serialize($item['theme arguments']), 'type' => $item['type'], 'description' => $item['description'], 'description_callback' => $item['description callback'], diff --git a/core/includes/theme.inc b/core/includes/theme.inc index 98be6deb80e43c1323181df780b0b2e76828e4fb..a6d9362165742850860452ad582d062f65b4c98f 100644 --- a/core/includes/theme.inc +++ b/core/includes/theme.inc @@ -92,14 +92,16 @@ function drupal_theme_initialize() { } drupal_bootstrap(DRUPAL_BOOTSTRAP_DATABASE); - $themes = list_themes(); - // @todo Let the theme.negotiator listen to the kernel request event. - // Determine the active theme for the theme negotiator service. This includes - // the default theme as well as really specific ones like the ajax base theme. - $request = \Drupal::request(); - $theme = \Drupal::service('theme.negotiator')->determineActiveTheme($request) ?: 'stark'; + // Only select the user selected theme if it is available in the + // list of themes that can be accessed. + $theme = !empty($user->theme) && drupal_theme_access($user->theme) ? $user->theme : \Drupal::config('system.theme')->get('default'); + + // Allow modules to override the theme. Validation has already been performed + // inside menu_get_custom_theme(), so we do not need to check it again here. + $custom_theme = menu_get_custom_theme(); + $theme = !empty($custom_theme) ? $custom_theme : $theme; // Store the identifier for retrieving theme settings with. $theme_key = $theme; @@ -112,6 +114,9 @@ function drupal_theme_initialize() { $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_static_reset('drupal_alter'); } /** diff --git a/core/lib/Drupal/Core/CoreServiceProvider.php b/core/lib/Drupal/Core/CoreServiceProvider.php index 098b1653aa770df172baf485241ec29f52149a1c..8a525c9abdbcd96eaa0d80cfe5642ba41408399c 100644 --- a/core/lib/Drupal/Core/CoreServiceProvider.php +++ b/core/lib/Drupal/Core/CoreServiceProvider.php @@ -23,7 +23,6 @@ use Drupal\Core\DependencyInjection\Compiler\RegisterBreadcrumbBuilderPass; use Drupal\Core\DependencyInjection\Compiler\RegisterAuthenticationPass; use Drupal\Core\DependencyInjection\Compiler\RegisterTwigExtensionsPass; -use Drupal\Core\Theme\ThemeNegotiatorPass; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\DependencyInjection\Reference; use Symfony\Component\DependencyInjection\Definition; @@ -72,9 +71,6 @@ public function register(ContainerBuilder $container) { // Add the compiler pass that will process the tagged breadcrumb builder // services. $container->addCompilerPass(new RegisterBreadcrumbBuilderPass()); - // Add the compiler pass that will process the tagged theme negotiator - // service. - $container->addCompilerPass(new ThemeNegotiatorPass()); // Add the compiler pass that lets service providers modify existing // service definitions. $container->addCompilerPass(new ModifyServiceDefinitionsPass()); diff --git a/core/lib/Drupal/Core/EventSubscriber/LegacyRequestSubscriber.php b/core/lib/Drupal/Core/EventSubscriber/LegacyRequestSubscriber.php index 2a495f4dd19405fb47aa8f31ca36ab113e343a0d..39828ef352f73e0c4b44e5ca476d9bddbd858374 100644 --- a/core/lib/Drupal/Core/EventSubscriber/LegacyRequestSubscriber.php +++ b/core/lib/Drupal/Core/EventSubscriber/LegacyRequestSubscriber.php @@ -27,6 +27,9 @@ class LegacyRequestSubscriber implements EventSubscriberInterface { */ public function onKernelRequestLegacy(GetResponseEvent $event) { if ($event->getRequestType() == HttpKernelInterface::MASTER_REQUEST) { + menu_set_custom_theme(); + drupal_theme_initialize(); + // Tell Drupal it is now fully bootstrapped (for the benefit of code that // calls drupal_get_bootstrap_phase()), but without having // _drupal_bootstrap_full() do anything, since we've already done the @@ -36,16 +39,6 @@ public function onKernelRequestLegacy(GetResponseEvent $event) { } } - /** - * Initializes the theme system after the routing system. - * - * @param \Symfony\Component\HttpKernel\Event\GetResponseEvent $event - * The Event to process. - */ - public function onKernelRequestLegacyAfterRouting(GetResponseEvent $event) { - drupal_theme_initialize(); - } - /** * Registers the methods in this class that should be listeners. * @@ -54,8 +47,6 @@ public function onKernelRequestLegacyAfterRouting(GetResponseEvent $event) { */ static function getSubscribedEvents() { $events[KernelEvents::REQUEST][] = array('onKernelRequestLegacy', 90); - // Initialize the theme system after the routing system. - $events[KernelEvents::REQUEST][] = array('onKernelRequestLegacyAfterRouting', 30); return $events; } diff --git a/core/lib/Drupal/Core/Theme/AjaxBasePageNegotiator.php b/core/lib/Drupal/Core/Theme/AjaxBasePageNegotiator.php deleted file mode 100644 index d2d2004f2e0a53411bc384454d62efd56c86b841..0000000000000000000000000000000000000000 --- a/core/lib/Drupal/Core/Theme/AjaxBasePageNegotiator.php +++ /dev/null @@ -1,84 +0,0 @@ -csrfGenerator = $token_generator; - $this->configFactory = $config_factory; - } - - /** - * {@inheritdoc} - */ - public function determineActiveTheme(Request $request) { - // Check whether the route was configured to use the base page theme. - if (!(($route = $request->attributes->get(RouteObjectInterface::ROUTE_OBJECT)) && $route->hasOption('_theme') && $route->getOption('_theme') == 'ajax_base_page')) { - return NULL; - } - if (($ajax_page_state = $request->request->get('ajax_page_state')) && !empty($ajax_page_state['theme']) && !empty($ajax_page_state['theme_token'])) { - $theme = $ajax_page_state['theme']; - $token = $ajax_page_state['theme_token']; - - // 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 --git a/core/lib/Drupal/Core/Theme/DefaultNegotiator.php b/core/lib/Drupal/Core/Theme/DefaultNegotiator.php deleted file mode 100644 index d59667806acca18b6a07ce81c3f878c81f9d21b9..0000000000000000000000000000000000000000 --- a/core/lib/Drupal/Core/Theme/DefaultNegotiator.php +++ /dev/null @@ -1,42 +0,0 @@ -config = $config_factory->get('system.theme'); - } - - /** - * {@inheritdoc} - */ - public function determineActiveTheme(Request $request) { - return $this->config->get('default'); - } - -} diff --git a/core/lib/Drupal/Core/Theme/ThemeNegotiator.php b/core/lib/Drupal/Core/Theme/ThemeNegotiator.php deleted file mode 100644 index 96216ca159d87788215f2dfdbbf3628c53be93da..0000000000000000000000000000000000000000 --- a/core/lib/Drupal/Core/Theme/ThemeNegotiator.php +++ /dev/null @@ -1,133 +0,0 @@ -themeAccess = $theme_access; - } - - /** - * Sets the request object to use. - * - * @param \Symfony\Component\HttpFoundation\Request $request - * The request object. - */ - public function setRequest(Request $request) { - $this->request = $request; - } - - /** - * Adds a active theme negotiation service. - * - * @param \Drupal\Core\Theme\ThemeNegotiatorInterface $negotiator - * The theme negotiator to add. - * @param int $priority - * Priority of the breadcrumb builder. - */ - public function addNegotiator(ThemeNegotiatorInterface $negotiator, $priority) { - $this->negotiators[$priority][] = $negotiator; - // Force the negotiators to be re-sorted. - $this->sortedNegotiators = NULL; - } - - /** - * Returns the sorted array of theme negotiators. - * - * @return array|\Drupal\Core\Theme\ThemeNegotiatorInterface[] - * An array of breadcrumb builder objects. - */ - protected function getSortedNegotiators() { - if (!isset($this->sortedNegotiators)) { - // Sort the negotiators according to priority. - krsort($this->negotiators); - // Merge nested negotiators from $this->negotiators into - // $this->sortedNegotiators. - $this->sortedNegotiators = array(); - foreach ($this->negotiators as $builders) { - $this->sortedNegotiators = array_merge($this->sortedNegotiators, $builders); - } - } - return $this->sortedNegotiators; - } - - /** - * Get the current active theme. - * - * @return string - * The current active string. - */ - public function getActiveTheme() { - if (!$this->request->attributes->has('_theme_active')) { - $this->determineActiveTheme($this->request); - } - return $this->request->attributes->get('_theme_active'); - } - - /** - * {@inheritdoc} - */ - public function determineActiveTheme(Request $request) { - foreach ($this->getSortedNegotiators() as $negotiator) { - $theme = $negotiator->determineActiveTheme($request); - if ($theme !== NULL && $this->themeAccess->checkAccess($theme)) { - $request->attributes->set('_theme_active', $theme); - return $request->attributes->get('_theme_active'); - } - } - } - -} diff --git a/core/lib/Drupal/Core/Theme/ThemeNegotiatorInterface.php b/core/lib/Drupal/Core/Theme/ThemeNegotiatorInterface.php deleted file mode 100644 index 55deaebf838d983470fb4aab02be5e08b6e2f531..0000000000000000000000000000000000000000 --- a/core/lib/Drupal/Core/Theme/ThemeNegotiatorInterface.php +++ /dev/null @@ -1,41 +0,0 @@ -hasDefinition('theme.negotiator')) { - return; - } - $manager = $container->getDefinition('theme.negotiator'); - foreach ($container->findTaggedServiceIds('theme_negotiator') as $id => $attributes) { - $priority = isset($attributes[0]['priority']) ? $attributes[0]['priority'] : 0; - $manager->addMethodCall('addNegotiator', array(new Reference($id), $priority)); - } - } - -} diff --git a/core/misc/ajax.js b/core/misc/ajax.js index e9baf942c5afb995e0d2c57b7b8b8d82a2b69519..a586945fddea762a139bb289b9fefa7499d280b3 100644 --- a/core/misc/ajax.js +++ b/core/misc/ajax.js @@ -382,7 +382,7 @@ Drupal.ajax.prototype.beforeSerialize = function (element, options) { // Allow Drupal to return new JavaScript and CSS files to load without // returning the ones already loaded. - // @see \Drupal\Core\Theme\AjaxBasePageNegotiator + // @see ajax_base_page_theme() // @see drupal_get_css() // @see drupal_get_js() var pageState = drupalSettings.ajaxPageState; diff --git a/core/modules/block/block.module b/core/modules/block/block.module index afe58625a6c2417b24dabc0138dc6afee547ffdb..e1bc631887adf88467a80faadf7d3d9d4d0b5031 100644 --- a/core/modules/block/block.module +++ b/core/modules/block/block.module @@ -109,9 +109,41 @@ function block_menu() { 'type' => MENU_VISIBLE_IN_BREADCRUMB, 'route_name' => 'block.admin_add', ); + // Block administration is tied to the theme and plugin definition so + // that the plugin can appropriately attach to this URL structure. + // @todo D8: Use dynamic % arguments instead of static, hard-coded theme names + // and plugin IDs to decouple the routes from these dependencies. + // @see http://drupal.org/node/1067408 + foreach (list_themes() as $key => $theme) { + $items["admin/structure/block/demo/$key"] = array( + 'route_name' => 'block.admin_demo', + 'type' => MENU_CALLBACK, + 'theme callback' => '_block_custom_theme', + 'theme arguments' => array($key), + ); + } return $items; } +/** + * Theme callback: Uses the theme specified in the parameter. + * + * @param $theme + * The theme whose blocks are being configured. If not set, the default theme + * is assumed. + * + * @return + * The theme that should be used for the block configuration page, or NULL + * to indicate that the default theme should be used. + * + * @see block_menu() + */ +function _block_custom_theme($theme = NULL) { + // We return exactly what was passed in, to guarantee that the page will + // always be displayed using the theme whose blocks are being configured. + return $theme; +} + /** * Implements hook_page_build(). * diff --git a/core/modules/block/block.services.yml b/core/modules/block/block.services.yml index 29a31c26d3b3d0e3571d3417c0a88b0f435a506a..b6bf1f41d373427fe61239426e9ceb29a5956aeb 100644 --- a/core/modules/block/block.services.yml +++ b/core/modules/block/block.services.yml @@ -9,8 +9,3 @@ services: factory_method: get factory_service: cache_factory arguments: [block] - theme.negotiator.block.admin_demo: - class: Drupal\block\Theme\AdminDemoNegotiator - tags: - - { name: theme_negotiator, priority: 1000 } - diff --git a/core/modules/block/lib/Drupal/block/Theme/AdminDemoNegotiator.php b/core/modules/block/lib/Drupal/block/Theme/AdminDemoNegotiator.php deleted file mode 100644 index d3dcb337605b10cdbc49faf15102e324d0d7a074..0000000000000000000000000000000000000000 --- a/core/modules/block/lib/Drupal/block/Theme/AdminDemoNegotiator.php +++ /dev/null @@ -1,30 +0,0 @@ -attributes->get(RouteObjectInterface::ROUTE_NAME) == 'block.admin_demo') { - return $request->attributes->get('theme'); - } - } - -} diff --git a/core/modules/content_translation/content_translation.module b/core/modules/content_translation/content_translation.module index 46ed263bad3e101b5258f85a8f3b0154d4971d76..43127b0d7a37adc05dc8d1acf32dc649a2e6c63a 100644 --- a/core/modules/content_translation/content_translation.module +++ b/core/modules/content_translation/content_translation.module @@ -156,7 +156,7 @@ function content_translation_menu() { if (content_translation_enabled($entity_type)) { $path = _content_translation_link_to_router_path($entity_type, $info['links']['canonical']); $entity_position = count(explode('/', $path)) - 1; - $keys = array_flip(array('load_arguments')); + $keys = array_flip(array('theme_callback', 'theme_arguments', 'load_arguments')); $menu_info = array_intersect_key($info['translation']['content_translation'], $keys) + array('file' => 'content_translation.pages.inc'); $item = array(); diff --git a/core/modules/contextual/contextual.module b/core/modules/contextual/contextual.module index 9f793cb61b02ca52716158dce878b18d6c6c9434..e405f206f40cf79cd1db2ae50ed578f3cd290d4b 100644 --- a/core/modules/contextual/contextual.module +++ b/core/modules/contextual/contextual.module @@ -6,6 +6,21 @@ * Adds contextual links to perform actions related to elements on a page. */ +/** + * Implements hook_menu(). + */ +function contextual_menu() { + // @todo Remove this menu item in http://drupal.org/node/1954892 when theme + // callbacks are replaced with something else. + $items['contextual/render'] = array( + 'route_name' => 'contextual.render', + 'theme callback' => 'ajax_base_page_theme', + 'type' => MENU_CALLBACK, + ); + + return $items; +} + /** * Implements hook_toolbar(). */ diff --git a/core/modules/contextual/contextual.routing.yml b/core/modules/contextual/contextual.routing.yml index b44d61920ea8f9c15370038d305fce1a6695c601..8ab2f2887bf5eeff1e15d5b05967bd7af585e845 100644 --- a/core/modules/contextual/contextual.routing.yml +++ b/core/modules/contextual/contextual.routing.yml @@ -2,7 +2,5 @@ contextual.render: path: '/contextual/render' defaults: _controller: '\Drupal\contextual\ContextualController::render' - options: - _theme: ajax_base_page requirements: _permission: 'access contextual links' diff --git a/core/modules/edit/edit.module b/core/modules/edit/edit.module index 291c4be6e8422880b98b6290305e8e0517367f85..9e4d1326203239e950232f8414f46d958b479a70 100644 --- a/core/modules/edit/edit.module +++ b/core/modules/edit/edit.module @@ -17,6 +17,26 @@ use Drupal\entity\Entity\EntityDisplay; use Drupal\user\TempStoreFactory; +/** + * Implements hook_menu(). + */ +function edit_menu() { + // @todo Remove these menu items in http://drupal.org/node/1954892 when theme + // callbacks are replaced with something else. + $items['edit/metadata'] = array( + 'route_name' => 'edit.metadata', + 'theme callback' => 'ajax_base_page_theme', + 'type' => MENU_CALLBACK, + ); + $items['edit/form/%/%/%/%/%'] = array( + 'route_name' => 'edit.field_form', + 'theme callback' => 'ajax_base_page_theme', + 'type' => MENU_CALLBACK, + ); + + return $items; +} + /** * Implements hook_permission(). */ diff --git a/core/modules/edit/edit.routing.yml b/core/modules/edit/edit.routing.yml index 80fbbf8e4f3eca843e81183f35fede728b1d5bf7..41acaacc090a3d4d00b32dbb5ba3282c7574815c 100644 --- a/core/modules/edit/edit.routing.yml +++ b/core/modules/edit/edit.routing.yml @@ -2,10 +2,9 @@ edit.metadata: path: '/edit/metadata' defaults: _controller: '\Drupal\edit\EditController::metadata' - options: - _theme: ajax_base_page requirements: _permission: 'access in-place editing' + edit.attachments: path: '/edit/attachments' defaults: @@ -17,9 +16,6 @@ edit.field_form: path: '/edit/form/{entity_type}/{entity}/{field_name}/{langcode}/{view_mode_id}' defaults: _controller: '\Drupal\edit\EditController::fieldForm' - options: - _access_mode: 'ALL' - _theme: ajax_base_page requirements: _permission: 'access in-place editing' _access_edit_entity_field: 'TRUE' diff --git a/core/modules/editor/editor.module b/core/modules/editor/editor.module index 07fc1b30b04d7695fa6071224a8b667d7940760c..4a560ee7c22d6a640b3a92a8e274781deb3229a2 100644 --- a/core/modules/editor/editor.module +++ b/core/modules/editor/editor.module @@ -139,6 +139,21 @@ function editor_library_info() { return $libraries; } +/** + * Implements hook_menu(). + */ +function editor_menu() { + // @todo Remove this menu item in http://drupal.org/node/1954892 when theme + // callbacks are replaced with something else. + $items['editor/%/%/%/%/%'] = array( + 'route_name' => 'editor.field_untransformed_text', + 'theme callback' => 'ajax_base_page_theme', + 'type' => MENU_CALLBACK, + ); + + return $items; +} + /** * Implements hook_form_FORM_ID_alter(). */ diff --git a/core/modules/editor/editor.routing.yml b/core/modules/editor/editor.routing.yml index bf9d3607c52d26dce28d6bc5579932dbc51ad49e..3308dd07efc44e94d49379a14c2f23b8ff195272 100644 --- a/core/modules/editor/editor.routing.yml +++ b/core/modules/editor/editor.routing.yml @@ -2,8 +2,6 @@ editor.field_untransformed_text: path: '/editor/{entity_type}/{entity}/{field_name}/{langcode}/{view_mode_id}' defaults: _controller: '\Drupal\editor\EditorController::getUntransformedText' - options: - _theme: ajax_base_page requirements: _permission: 'access in-place editing' _access_edit_entity_field: 'TRUE' diff --git a/core/modules/file/file.module b/core/modules/file/file.module index e96a4c1f71a9e5b4bb1f6c9b6a222572dee50837..9bc1d858684572df262241da751310ad62021763 100644 --- a/core/modules/file/file.module +++ b/core/modules/file/file.module @@ -37,6 +37,21 @@ function file_help($path, $arg) { } } +/** + * Implements hook_menu(). + */ +function file_menu() { + $items = array(); + + $items['file/ajax'] = array( + 'route_name' => 'file.ajax_upload', + 'theme callback' => 'ajax_base_page_theme', + 'type' => MENU_CALLBACK, + ); + + return $items; +} + /** * Implements hook_element_info(). * diff --git a/core/modules/file/file.routing.yml b/core/modules/file/file.routing.yml index d9d4efa0aff61727f0fea37e414b384e606cba06..8bf971c9a21e2ebacc36c54b399e118fbd0da60f 100644 --- a/core/modules/file/file.routing.yml +++ b/core/modules/file/file.routing.yml @@ -2,8 +2,6 @@ file.ajax_upload: path: '/file/ajax' defaults: _controller: '\Drupal\file\Controller\FileWidgetAjaxController::upload' - options: - _theme: ajax_base_page requirements: _permission: 'access content' diff --git a/core/modules/search/lib/Drupal/search/Tests/SearchPageOverrideTest.php b/core/modules/search/lib/Drupal/search/Tests/SearchPageOverrideTest.php index 101b745395f7d35e4ae9cd439136cf1c5cf36248..ab3f9135c86638a241f14d2e937c103dc113fdaa 100644 --- a/core/modules/search/lib/Drupal/search/Tests/SearchPageOverrideTest.php +++ b/core/modules/search/lib/Drupal/search/Tests/SearchPageOverrideTest.php @@ -38,7 +38,7 @@ function setUp() { // Enable the extra type module for searching. \Drupal::config('search.settings')->set('active_plugins', array('node_search', 'user_search', 'search_extra_type_search'))->save(); - \Drupal::service('router.builder')->rebuild(); + \Drupal::state()->set('menu_rebuild_needed', TRUE); } function testSearchPageHook() { diff --git a/core/modules/shortcut/lib/Drupal/shortcut/Tests/ShortcutLinksTest.php b/core/modules/shortcut/lib/Drupal/shortcut/Tests/ShortcutLinksTest.php index b747d793b062b7171a66c2876828ede99e261ba7..bd2d194c2503ef5899e18d3a6e37e7760ed69ec9 100644 --- a/core/modules/shortcut/lib/Drupal/shortcut/Tests/ShortcutLinksTest.php +++ b/core/modules/shortcut/lib/Drupal/shortcut/Tests/ShortcutLinksTest.php @@ -145,7 +145,6 @@ function testShortcutLinkDelete() { */ function testNoShortcutLink() { // Change to a theme that displays shortcuts. - theme_enable(array('seven')); \Drupal::config('system.theme') ->set('default', 'seven') ->save(); @@ -158,9 +157,8 @@ function testNoShortcutLink() { $this->assertNoRaw('add-shortcut', 'Add to shortcuts link was not shown on a page the user does not have access to.'); // Verify that the testing mechanism works by verifying the shortcut - // link appears on admin/people. - $this->drupalGet('admin/people'); + // link appears on admin/content/node. + $this->drupalGet('admin/content/node'); $this->assertRaw('add-shortcut', 'Add to shortcuts link was shown on a page the user does have access to.'); } - } diff --git a/core/modules/shortcut/lib/Drupal/shortcut/Tests/ShortcutTestBase.php b/core/modules/shortcut/lib/Drupal/shortcut/Tests/ShortcutTestBase.php index e0bdb31cf5af4022beb18b163ae11ece0b30af49..06088ef4b68c1b935a0f0760dd6723f5ed139ea7 100644 --- a/core/modules/shortcut/lib/Drupal/shortcut/Tests/ShortcutTestBase.php +++ b/core/modules/shortcut/lib/Drupal/shortcut/Tests/ShortcutTestBase.php @@ -69,7 +69,7 @@ function setUp() { } // Create users. - $this->admin_user = $this->drupalCreateUser(array('access toolbar', 'administer shortcuts', 'view the administration theme', 'create article content', 'create page content', 'access content overview', 'administer users')); + $this->admin_user = $this->drupalCreateUser(array('access toolbar', 'administer shortcuts', 'view the administration theme', 'create article content', 'create page content', 'access content overview')); $this->shortcut_user = $this->drupalCreateUser(array('customize shortcut links', 'switch shortcut sets')); // Create a node. diff --git a/core/modules/system/lib/Drupal/system/Tests/Menu/MenuRouterTest.php b/core/modules/system/lib/Drupal/system/Tests/Menu/MenuRouterTest.php index ccc327dcba383bfdf82f4b525909a17b6adf1c71..71cf0c4d771354404724cdee79ec788b45d08c78 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Menu/MenuRouterTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Menu/MenuRouterTest.php @@ -481,9 +481,18 @@ public function testThemeIntegration() { $this->initializeTestThemeConfiguration(); $this->doTestThemeCallbackFakeTheme(); + $this->initializeTestThemeConfiguration(); + $this->doTestHookCustomTheme(); + + $this->initializeTestThemeConfiguration(); + $this->doTestThemeCallbackHookCustomTheme(); + $this->initializeTestThemeConfiguration(); $this->doTestThemeCallbackAdministrative(); + $this->initializeTestThemeConfiguration(); + $this->doTestThemeCallbackInheritance(); + $this->initializeTestThemeConfiguration(); $this->doTestThemeCallbackNoThemeRequested(); @@ -507,17 +516,27 @@ protected function initializeTestThemeConfiguration() { } /** - * Test the theme negotiation when it is set to use an administrative theme. + * Test the theme callback when it is set to use an administrative theme. */ protected function doTestThemeCallbackAdministrative() { theme_enable(array($this->admin_theme)); $this->drupalGet('menu-test/theme-callback/use-admin-theme'); - $this->assertText('Active theme: seven. Actual theme: seven.', 'The administrative theme can be correctly set in a theme negotiation.'); + $this->assertText('Custom theme: seven. Actual theme: seven.', 'The administrative theme can be correctly set in a theme callback.'); + $this->assertRaw('seven/style.css', "The administrative theme's CSS appears on the page."); + } + + /** + * Test that the theme callback is properly inherited. + */ + protected function doTestThemeCallbackInheritance() { + theme_enable(array($this->admin_theme)); + $this->drupalGet('menu-test/theme-callback/use-admin-theme/inheritance'); + $this->assertText('Custom theme: seven. Actual theme: seven. Theme callback inheritance is being tested.', 'Theme callback inheritance correctly uses the administrative theme.'); $this->assertRaw('seven/style.css', "The administrative theme's CSS appears on the page."); } /** - * Test the theme negotiation when the site is in maintenance mode. + * Test the theme callback when the site is in maintenance mode. */ protected function doTestThemeCallbackMaintenanceMode() { $this->container->get('state')->set('system.maintenance_mode', TRUE); @@ -532,44 +551,76 @@ protected function doTestThemeCallbackMaintenanceMode() { $admin_user = $this->drupalCreateUser(array('access site in maintenance mode')); $this->drupalLogin($admin_user); $this->drupalGet('menu-test/theme-callback/use-admin-theme'); - $this->assertText('Active theme: seven. Actual theme: seven.', 'The theme negotiation system is correctly triggered for an administrator when the site is in maintenance mode.'); + $this->assertText('Custom theme: seven. Actual theme: seven.', 'The theme callback system is correctly triggered for an administrator when the site is in maintenance mode.'); $this->assertRaw('seven/style.css', "The administrative theme's CSS appears on the page."); $this->container->get('state')->set('system.maintenance_mode', FALSE); } /** - * Test the theme negotiation when it is set to use an optional theme. + * Test the theme callback when it is set to use an optional theme. */ protected function doTestThemeCallbackOptionalTheme() { // Request a theme that is not enabled. $this->drupalGet('menu-test/theme-callback/use-stark-theme'); - $this->assertText('Active theme: bartik. Actual theme: bartik.', 'The theme negotiation system falls back on the default theme when a theme that is not enabled is requested.'); + $this->assertText('Custom theme: NONE. Actual theme: bartik.', 'The theme callback system falls back on the default theme when a theme that is not enabled is requested.'); $this->assertRaw('bartik/css/style.css', "The default theme's CSS appears on the page."); // Now enable the theme and request it again. theme_enable(array($this->alternate_theme)); $this->drupalGet('menu-test/theme-callback/use-stark-theme'); - $this->assertText('Active theme: stark. Actual theme: stark.', 'The theme negotiation system uses an optional theme once it has been enabled.'); + $this->assertText('Custom theme: stark. Actual theme: stark.', 'The theme callback system uses an optional theme once it has been enabled.'); $this->assertRaw('stark/css/layout.css', "The optional theme's CSS appears on the page."); } /** - * Test the theme negotiation when it is set to use a theme that does not exist. + * Test the theme callback when it is set to use a theme that does not exist. */ protected function doTestThemeCallbackFakeTheme() { $this->drupalGet('menu-test/theme-callback/use-fake-theme'); - $this->assertText('Active theme: bartik. Actual theme: bartik.', 'The theme negotiation system falls back on the default theme when a theme that does not exist is requested.'); + $this->assertText('Custom theme: NONE. Actual theme: bartik.', 'The theme callback system falls back on the default theme when a theme that does not exist is requested.'); $this->assertRaw('bartik/css/style.css', "The default theme's CSS appears on the page."); } /** - * Test the theme negotiation when no theme is requested. + * Test the theme callback when no theme is requested. */ protected function doTestThemeCallbackNoThemeRequested() { $this->drupalGet('menu-test/theme-callback/no-theme-requested'); - $this->assertText('Active theme: bartik. Actual theme: bartik.', 'The theme negotiation system falls back on the default theme when no theme is requested.'); + $this->assertText('Custom theme: NONE. Actual theme: bartik.', 'The theme callback system falls back on the default theme when no theme is requested.'); $this->assertRaw('bartik/css/style.css', "The default theme's CSS appears on the page."); } + /** + * Test that hook_custom_theme() can control the theme of a page. + */ + protected function doTestHookCustomTheme() { + // Trigger hook_custom_theme() to dynamically request the Stark theme for + // the requested page. + \Drupal::state()->set('menu_test.hook_custom_theme_name', $this->alternate_theme); + theme_enable(array($this->alternate_theme, $this->admin_theme)); + + // Visit a page that does not implement a theme callback. The above request + // should be honored. + $this->drupalGet('menu-test/no-theme-callback'); + $this->assertText('Custom theme: stark. Actual theme: stark.', 'The result of hook_custom_theme() is used as the theme for the current page.'); + $this->assertRaw('stark/css/layout.css', "The Stark theme's CSS appears on the page."); + } + + /** + * Test that the theme callback wins out over hook_custom_theme(). + */ + protected function doTestThemeCallbackHookCustomTheme() { + // Trigger hook_custom_theme() to dynamically request the Stark theme for + // the requested page. + \Drupal::state()->set('menu_test.hook_custom_theme_name', $this->alternate_theme); + theme_enable(array($this->alternate_theme, $this->admin_theme)); + + // The menu "theme callback" should take precedence over a value set in + // hook_custom_theme(). + $this->drupalGet('menu-test/theme-callback/use-admin-theme'); + $this->assertText('Custom theme: seven. Actual theme: seven.', 'The result of hook_custom_theme() does not override what was set in a theme callback.'); + $this->assertRaw('seven/style.css', "The Seven theme's CSS appears on the page."); + } + } diff --git a/core/modules/system/lib/Drupal/system/Tests/System/ThemeTest.php b/core/modules/system/lib/Drupal/system/Tests/System/ThemeTest.php index 554450accbc60ec837543b70b74e90294e675f4e..9a13acbc0e4d8a1e0c0ebc40a36b29da76e3d5d9 100644 --- a/core/modules/system/lib/Drupal/system/Tests/System/ThemeTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/System/ThemeTest.php @@ -178,7 +178,7 @@ function testThemeSettings() { * Test the administration theme functionality. */ function testAdministrationTheme() { - theme_enable(array('bartik', 'seven')); + theme_enable(array('seven')); // Enable an administration theme and show it on the node admin pages. $edit = array( diff --git a/core/modules/system/lib/Drupal/system/Tests/Theme/ThemeTest.php b/core/modules/system/lib/Drupal/system/Tests/Theme/ThemeTest.php index 7263a022939a2d75d676c6b12fb81210eef7e691..069960c943d9ea152cfc857241156cc6dbff82c4 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Theme/ThemeTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Theme/ThemeTest.php @@ -118,17 +118,6 @@ function testPreprocessForSuggestions() { } } - /** - * Tests the priority of some theme negotiators. - */ - public function testNegotiatorPriorities() { - $this->drupalGet('theme-test/priority'); - - // Ensure that the custom theme negotiator was not able to set the theme. - - $this->assertNoText('Theme hook implementor=test_theme_theme_test__suggestion(). Foo=template_preprocess_theme_test', 'Theme hook suggestion ran with data available from a preprocess function for the base hook.'); - } - /** * Ensure page-front template suggestion is added when on front page. */ @@ -279,4 +268,5 @@ function testPreprocessHtml() { $this->assertText('theme test page bottom markup', 'Modules are able to set the page bottom region.'); } + } diff --git a/core/modules/system/lib/Drupal/system/Theme/BatchNegotiator.php b/core/modules/system/lib/Drupal/system/Theme/BatchNegotiator.php deleted file mode 100644 index a10374d5b8e06fdbdd030910d41e89dbde6a9d01..0000000000000000000000000000000000000000 --- a/core/modules/system/lib/Drupal/system/Theme/BatchNegotiator.php +++ /dev/null @@ -1,54 +0,0 @@ -batchStorage = $batch_storage; - } - - /** - * {@inheritdoc} - */ - public function determineActiveTheme(Request $request) { - if ($request->attributes->get(RouteObjectInterface::ROUTE_NAME) == 'system.batch_page') { - // Retrieve the current state of the batch. - $batch = &batch_get(); - if (!$batch && $request->request->has('id')) { - $batch = $this->batchStorage->load($request->request->get('id')); - } - // Use the same theme as the page that started the batch. - if (!empty($batch['theme'])) { - return $batch['theme']; - } - } - } - -} diff --git a/core/modules/system/system.api.php b/core/modules/system/system.api.php index 67c48f356313ed649d591dd375fd371e6c44e91f..2bff21fd560443e862d2df476dfc97e14dfd86ea 100644 --- a/core/modules/system/system.api.php +++ b/core/modules/system/system.api.php @@ -1425,6 +1425,36 @@ function hook_template_preprocess_default_variables_alter(&$variables) { $variables['is_admin'] = user_access('access administration pages'); } +/** + * Return the machine-readable name of the theme to use for the current page. + * + * This hook can be used to dynamically set the theme for the current page + * request. It should be used by modules which need to override the theme + * based on dynamic conditions (for example, a module which allows the theme to + * be set based on the current user's role). The return value of this hook will + * be used on all pages except those which have a valid per-page or per-section + * theme set via a theme callback function in hook_menu(); the themes on those + * pages can only be overridden using hook_menu_alter(). + * + * Note that returning different themes for the same path may not work with page + * caching. This is most likely to be a problem if an anonymous user on a given + * path could have different themes returned under different conditions. + * + * Since only one theme can be used at a time, the last (i.e., highest + * weighted) module which returns a valid theme name from this hook will + * prevail. + * + * @return + * The machine-readable name of the theme that should be used for the current + * page request. The value returned from this function will only have an + * effect if it corresponds to a currently-active theme on the site. Do not + * return a value if you do not wish to set a custom theme. + */ +function hook_custom_theme() { + // Allow the user to request a particular theme via a query parameter. + return \Drupal::request()->query->get('theme'); +} + /** * Log an event message. * diff --git a/core/modules/system/system.install b/core/modules/system/system.install index f0cc453cc9083b9ae8e2737503325045e0d2c2c3..4f848ce9b19019de9b9f3397f330c5abaed64acd 100644 --- a/core/modules/system/system.install +++ b/core/modules/system/system.install @@ -824,6 +824,20 @@ function system_schema() { 'not null' => TRUE, 'default' => '', ), + 'theme_callback' => array( + 'description' => 'A function which returns the name of the theme that will be used to render this page. If left empty, the default theme will be used.', + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + 'default' => '', + ), + 'theme_arguments' => array( + 'description' => 'A serialized array of arguments for the theme callback.', + 'type' => 'varchar', + 'length' => 255, + 'not null' => TRUE, + 'default' => '', + ), 'type' => array( 'description' => 'Numeric representation of the type of the menu item, like MENU_LOCAL_TASK.', 'type' => 'int', diff --git a/core/modules/system/system.module b/core/modules/system/system.module index fdf17374bb3b01b6aa8103a2e0700aa647e03746..40c3234681028ca9a903c837d30fb8fb6321b1cd 100644 --- a/core/modules/system/system.module +++ b/core/modules/system/system.module @@ -613,6 +613,12 @@ function system_element_info() { * Implements hook_menu(). */ function system_menu() { + $items['system/ajax'] = array( + 'title' => 'AHAH callback', + 'route_name' => 'system.ajax', + 'theme callback' => 'ajax_base_page_theme', + 'type' => MENU_CALLBACK, + ); $items['admin'] = array( 'title' => 'Administration', 'route_name' => 'system.admin', @@ -806,6 +812,13 @@ function system_menu() { 'route_name' => 'system.status', ); + // Default page for batch operations. + $items['batch'] = array( + 'route_name' => 'system.batch_page', + 'theme callback' => '_system_batch_theme', + 'type' => MENU_CALLBACK, + ); + return $items; } @@ -857,6 +870,21 @@ function system_theme_suggestions_region(array $variables) { return $suggestions; } +/** + * Theme callback for the default batch page. + */ +function _system_batch_theme() { + // Retrieve the current state of the batch. + $batch = &batch_get(); + if (!$batch && isset($_REQUEST['id'])) { + $batch = \Drupal::service('batch.storage')->load($_REQUEST['id']); + } + // Use the same theme as the page that started the batch. + if (!empty($batch['theme'])) { + return $batch['theme']; + } +} + /** * Implements hook_library_info(). */ @@ -2103,6 +2131,19 @@ function system_page_build(&$page) { } } +/** + * Implements hook_custom_theme(). + */ +function system_custom_theme() { + if (drupal_container()->isScopeActive('request')) { + $request = \Drupal::request(); + $path = $request->attributes->get('_system_path'); + if (user_access('view the administration theme') && path_is_admin($path)) { + return \Drupal::config('system.theme')->get('admin'); + } + } +} + /** * Implements hook_form_FORM_ID_alter(). */ diff --git a/core/modules/system/system.routing.yml b/core/modules/system/system.routing.yml index a0aadb6e5b2fe0ce7a90230db78d51ceff91e229..77a741943c8e54583a8663956d4c5aa7902e021b 100644 --- a/core/modules/system/system.routing.yml +++ b/core/modules/system/system.routing.yml @@ -2,8 +2,6 @@ system.ajax: path: '/system/ajax' defaults: _controller: '\Drupal\system\Controller\FormAjaxController::content' - options: - _theme: ajax_base_page requirements: _access: 'TRUE' diff --git a/core/modules/system/system.services.yml b/core/modules/system/system.services.yml index 32210489ed284fa23161dcc933d76c493d5d0f40..3371dad2c684f60932e6919f4474afd2689e77ca 100644 --- a/core/modules/system/system.services.yml +++ b/core/modules/system/system.services.yml @@ -19,8 +19,3 @@ services: class: Drupal\system\PathProcessor\PathProcessorFiles tags: - { name: path_processor_inbound, priority: 200 } - theme.negotiator.system.batch: - class: Drupal\system\Theme\BatchNegotiator - arguments: ['@batch.storage'] - tags: - - { name: theme_negotiator, priority: 1000 } diff --git a/core/modules/system/tests/modules/ajax_test/ajax_test.module b/core/modules/system/tests/modules/ajax_test/ajax_test.module index 5fb833805f8ddb0559f024c013d0f3488e0e2715..f42844fb7f0b2c9226b16964188e1db44feb7096 100644 --- a/core/modules/system/tests/modules/ajax_test/ajax_test.module +++ b/core/modules/system/tests/modules/ajax_test/ajax_test.module @@ -12,6 +12,19 @@ use Drupal\Core\Ajax\CloseDialogCommand; use Drupal\Core\Ajax\HtmlCommand; +/** + * Implements hook_menu(). + */ +function ajax_test_menu() { + $items['ajax-test/order'] = array( + 'title' => 'AJAX commands order', + 'route_name' => 'ajax_test.order', + 'theme callback' => 'ajax_base_page_theme', + 'type' => MENU_CALLBACK, + ); + return $items; +} + /** * Implements hook_system_theme_info(). */ diff --git a/core/modules/system/tests/modules/ajax_test/ajax_test.routing.yml b/core/modules/system/tests/modules/ajax_test/ajax_test.routing.yml index 270ef51a754d845e05437e767f1e30642cea980e..9453bef47a9c82efb53ee3175f7a574609afb93d 100644 --- a/core/modules/system/tests/modules/ajax_test/ajax_test.routing.yml +++ b/core/modules/system/tests/modules/ajax_test/ajax_test.routing.yml @@ -37,8 +37,6 @@ ajax_test.order: path: '/ajax-test/order' defaults: _controller: '\Drupal\ajax_test\Controller\AjaxTestController::order' - options: - _theme: ajax_base_page requirements: _access: 'TRUE' diff --git a/core/modules/system/tests/modules/menu_test/lib/Drupal/menu_test/EventSubscriber/ActiveTrailSubscriber.php b/core/modules/system/tests/modules/menu_test/lib/Drupal/menu_test/EventSubscriber/ActiveTrailSubscriber.php deleted file mode 100644 index 4bb35956e72817fc66350592e5bb46e64a2b43e0..0000000000000000000000000000000000000000 --- a/core/modules/system/tests/modules/menu_test/lib/Drupal/menu_test/EventSubscriber/ActiveTrailSubscriber.php +++ /dev/null @@ -1,68 +0,0 @@ -state = $state; - } - - /** - * Tracks the active trail. - * - * @param \Symfony\Component\HttpKernel\Event\GetResponseEvent $event - * The event to process. - */ - public function onKernelRequest(GetResponseEvent $event) { - // When requested by one of the MenuTrailTestCase tests, record the initial - // active trail during Drupal's bootstrap (before the user is redirected to - // a custom 403 or 404 page). - if (!$this->trail && $this->state->get('menu_test.record_active_trail') ?: FALSE) { - $this->trail = menu_get_active_trail(); - $this->state->set('menu_test.active_trail_initial', $this->trail); - } - } - - /** - * {@inheritdoc} - */ - public static function getSubscribedEvents() { - $events[KernelEvents::REQUEST][] = array('onKernelRequest'); - return $events; - } - -} diff --git a/core/modules/system/tests/modules/menu_test/lib/Drupal/menu_test/Theme/TestThemeNegotiator.php b/core/modules/system/tests/modules/menu_test/lib/Drupal/menu_test/Theme/TestThemeNegotiator.php deleted file mode 100644 index b324d8d48b212d93aba4439aba8301437e64ccbd..0000000000000000000000000000000000000000 --- a/core/modules/system/tests/modules/menu_test/lib/Drupal/menu_test/Theme/TestThemeNegotiator.php +++ /dev/null @@ -1,43 +0,0 @@ -attributes->get('inherited'); - // Test using the variable administrative theme. - if ($argument == 'use-admin-theme') { - return \Drupal::config('system.theme')->get('admin'); - } - // Test using a theme that exists, but may or may not be enabled. - elseif ($argument == 'use-stark-theme') { - return 'stark'; - } - // Test using a theme that does not exist. - elseif ($argument == 'use-fake-theme') { - return 'fake_theme'; - } - // For any other value of the URL argument, do not return anything. This - // allows us to test that returning nothing from a theme negotiation - // causes the page to correctly fall back on using the main site theme. - } - -} diff --git a/core/modules/system/tests/modules/menu_test/menu_test.module b/core/modules/system/tests/modules/menu_test/menu_test.module index df5b4904e0d98ade288de3f07ffcb68c2f77efa9..ab2ef302894bf941f5c1ee699acd89784dc26adb 100644 --- a/core/modules/system/tests/modules/menu_test/menu_test.module +++ b/core/modules/system/tests/modules/menu_test/menu_test.module @@ -64,12 +64,18 @@ function menu_test_menu() { 'route_name' => 'menu_test.hierarchy_parent_child2', ); // Theme callback tests. + $items['menu-test/theme-callback/%'] = array( + 'title' => 'Page that displays different themes', + 'route_name' => 'menu_test.theme_callback', + 'theme callback' => 'menu_test_theme_callback', + 'theme arguments' => array(2), + ); $items['menu-test/theme-callback/%/inheritance'] = array( - 'title' => 'Page that tests theme negotiation inheritance.', + 'title' => 'Page that tests theme callback inheritance.', 'route_name' => 'menu_test.theme_callback_inheritance', ); $items['menu-test/no-theme-callback'] = array( - 'title' => 'Page that displays different themes without using a theme negotiation.', + 'title' => 'Page that displays different themes without using a theme callback.', 'route_name' => 'menu_test.no_theme_callback', ); // Path containing "exotic" characters. @@ -433,7 +439,7 @@ function menu_test_custom_403_404_callback() { } /** - * Page callback: Tests the theme negotiation functionality. + * Page callback: Tests the theme callback functionality. * * @param bool $inherited * (optional) TRUE when the requested page is intended to inherit @@ -451,15 +457,66 @@ function menu_test_theme_page_callback($inherited = FALSE) { global $theme_key; // Initialize the theme system so that $theme_key will be populated. drupal_theme_initialize(); - // Now we check what the theme negotiator service returns. - $active_theme = \Drupal::service('theme.negotiator')->getActiveTheme('getActiveTheme'); - $output = "Active theme: $active_theme. Actual theme: $theme_key."; + // Now check both the requested custom theme and the actual theme being used. + $custom_theme = menu_get_custom_theme(); + $requested_theme = empty($custom_theme) ? 'NONE' : $custom_theme; + $output = "Custom theme: $requested_theme. Actual theme: $theme_key."; if ($inherited) { - $output .= ' Theme negotiation inheritance is being tested.'; + $output .= ' Theme callback inheritance is being tested.'; } return $output; } +/** + * Theme callback: Tests the theme callback functionality. + * + * Retrieves the theme key of the theme to use for the current request based on + * the theme name provided in the URL. + * + * @param string $argument + * The argument passed in from the URL. + * + * @return string + * The name of the custom theme to request for the current page. + * + * @see menu_test_menu(). + */ +function menu_test_theme_callback($argument) { + // Test using the variable administrative theme. + if ($argument == 'use-admin-theme') { + return \Drupal::config('system.theme')->get('admin'); + } + // Test using a theme that exists, but may or may not be enabled. + elseif ($argument == 'use-stark-theme') { + return 'stark'; + } + // Test using a theme that does not exist. + elseif ($argument == 'use-fake-theme') { + return 'fake_theme'; + } + // For any other value of the URL argument, do not return anything. This + // allows us to test that returning nothing from a theme callback function + // causes the page to correctly fall back on using the main site theme. +} + +/** + * Implements hook_custom_theme(). + * + * If an appropriate variable has been set in the database, request the theme + * that is stored there. Otherwise, do not attempt to dynamically set the theme. + */ +function menu_test_custom_theme() { + // When requested by one of the MenuTrailTestCase tests, record the initial + // active trail during Drupal's bootstrap (before the user is redirected to a + // custom 403 or 404 page). See menu_test_custom_403_404_callback(). + if (\Drupal::state()->get('menu_test.record_active_trail') ?: FALSE) { + \Drupal::state()->set('menu_test.active_trail_initial', menu_get_active_trail()); + } + if ($theme = \Drupal::state()->get('menu_test.hook_custom_theme_name') ?: FALSE) { + return $theme; + } +} + /** * Sets a static variable for the testMenuName() test. * diff --git a/core/modules/system/tests/modules/menu_test/menu_test.routing.yml b/core/modules/system/tests/modules/menu_test/menu_test.routing.yml index d37f47b33cdf5d5fb177a9b4a45fe11923a0769d..5ab1a945cf2d57c61755b10bedc3eb6d2a426835 100644 --- a/core/modules/system/tests/modules/menu_test/menu_test.routing.yml +++ b/core/modules/system/tests/modules/menu_test/menu_test.routing.yml @@ -490,7 +490,7 @@ menu_test.theme_callback: menu_test.no_theme_callback: path: '/menu-test/no-theme-callback' defaults: - _title: 'Page that displays different themes without using a theme negotiation.' + _title: 'Page that displays different themes without using a theme callback.' _content: '\Drupal\menu_test\Controller\MenuTestController::themePage' inherited: false requirements: @@ -511,7 +511,7 @@ menu_test.exotic_path: menu_test.theme_callback_inheritance: path: '/menu-test/theme-callback/{inherited}/inheritance' defaults: - _title: 'Page that tests theme negotiation inheritance.' + _title: 'Page that tests theme callback inheritance.' _content: '\Drupal\menu_test\Controller\MenuTestController::themePage' requirements: _permission: 'access content' diff --git a/core/modules/system/tests/modules/menu_test/menu_test.services.yml b/core/modules/system/tests/modules/menu_test/menu_test.services.yml index 3de0169ad1071675d4c9ff2675a8f3477a353113..097ddf201b9275a84ca6545d648f701d3730cae9 100644 --- a/core/modules/system/tests/modules/menu_test/menu_test.services.yml +++ b/core/modules/system/tests/modules/menu_test/menu_test.services.yml @@ -3,14 +3,3 @@ services: class: Drupal\menu_test\EventSubscriber\MaintenanceModeSubscriber tags: - { name: event_subscriber } - - menu_test.active_trail_subscriber: - class: Drupal\menu_test\EventSubscriber\ActiveTrailSubscriber - arguments: ['@state'] - tags: - - { name: event_subscriber } - - theme.negotiator.test_theme: - class: Drupal\menu_test\Theme\TestThemeNegotiator - tags: - - { name: theme_negotiator } diff --git a/core/modules/system/tests/modules/theme_test/lib/Drupal/theme_test/Theme/CustomThemeNegotiator.php b/core/modules/system/tests/modules/theme_test/lib/Drupal/theme_test/Theme/CustomThemeNegotiator.php deleted file mode 100644 index 9c0396eedcd02e130c52d4ec5ad4565a58346413..0000000000000000000000000000000000000000 --- a/core/modules/system/tests/modules/theme_test/lib/Drupal/theme_test/Theme/CustomThemeNegotiator.php +++ /dev/null @@ -1,29 +0,0 @@ -attributes->get(RouteObjectInterface::ROUTE_OBJECT)) && $route_object instanceof Route && $route_object->hasOption('_custom_theme')) { - return $route_object->getOption('_custom_theme'); - } - } - -} diff --git a/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 deleted file mode 100644 index 2bb5cf6dd4143af88ac413988aabbd1dd504cf38..0000000000000000000000000000000000000000 --- a/core/modules/system/tests/modules/theme_test/lib/Drupal/theme_test/Theme/HighPriorityThemeNegotiator.php +++ /dev/null @@ -1,28 +0,0 @@ -attributes->get(RouteObjectInterface::ROUTE_NAME)) && $route_name == 'theme_test.priority') { - return 'stark'; - } - } - -} diff --git a/core/modules/system/tests/modules/theme_test/theme_test.module b/core/modules/system/tests/modules/theme_test/theme_test.module index 52bff218b40d34e6e2d9b72c4ae393f597ec06a0..87e26b8922836ee8eb3d44e01e835903db091629 100644 --- a/core/modules/system/tests/modules/theme_test/theme_test.module +++ b/core/modules/system/tests/modules/theme_test/theme_test.module @@ -80,6 +80,13 @@ function theme_test_menu() { return $items; } +/** + * Custom theme callback. + */ +function _theme_custom_theme() { + return 'test_theme'; +} + /** * Implements hook_preprocess_HOOK() for HTML document templates. */ diff --git a/core/modules/system/tests/modules/theme_test/theme_test.routing.yml b/core/modules/system/tests/modules/theme_test/theme_test.routing.yml index 9f08d5b0c56ae2ded56233dcf65b5eb8f13d57c5..b4a7fd64fe98576a8308ac7eafc5f8cdd868eb13 100644 --- a/core/modules/system/tests/modules/theme_test/theme_test.routing.yml +++ b/core/modules/system/tests/modules/theme_test/theme_test.routing.yml @@ -1,7 +1,5 @@ theme_test.function_template_override: path: '/theme-test/function-template-overridden' - options: - _custom_theme: 'test_theme' defaults: _content: '\Drupal\theme_test\ThemeTestController::functionTemplateOverridden' requirements: @@ -23,18 +21,6 @@ theme_test.template_test: theme_test.suggestion: path: '/theme-test/suggestion' - options: - _custom_theme: 'test_theme' - defaults: - _content: '\Drupal\theme_test\ThemeTestController::testSuggestion' - _title: 'Suggestion' - requirements: - _access: 'TRUE' - -theme_test.priority: - path: '/theme-test/priority' - options: - _custom_theme: 'test_theme' defaults: _content: '\Drupal\theme_test\ThemeTestController::testSuggestion' _title: 'Suggestion' @@ -43,8 +29,6 @@ theme_test.priority: theme_test.alter: path: '/theme-test/alter' - options: - _custom_theme: 'test_theme' defaults: _content: '\Drupal\theme_test\ThemeTestController::testAlter' _title: 'Suggestion' diff --git a/core/modules/system/tests/modules/theme_test/theme_test.services.yml b/core/modules/system/tests/modules/theme_test/theme_test.services.yml index 69fd3ca7005764b3b11c653eb88a2b145ec238c8..8e442df969599c7cdc56bd5db1eb7c6de1c9f4dc 100644 --- a/core/modules/system/tests/modules/theme_test/theme_test.services.yml +++ b/core/modules/system/tests/modules/theme_test/theme_test.services.yml @@ -3,13 +3,3 @@ services: class: Drupal\theme_test\EventSubscriber\ThemeTestSubscriber tags: - { name: event_subscriber } - - theme.negotiator.test_custom_theme: - class: Drupal\theme_test\Theme\CustomThemeNegotiator - tags: - - { name: theme_negotiator } - - theme.negotiator.high_priority: - class: Drupal\theme_test\Theme\HighPriorityThemeNegotiator - tags: - - { name: theme_negotiator, priority: 1000 } diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/ThemeTest.php b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/ThemeTest.php index ee8cc38b3f38a91e144ac88ede73d32f7efcdaa1..452e33ff6242c32d81e24614077816429967ba0d 100644 --- a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/ThemeTest.php +++ b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/ThemeTest.php @@ -25,11 +25,11 @@ function setUp() { // Make sure we are using distinct default and administrative themes for // the duration of these tests. - theme_enable(array('bartik', 'seven')); \Drupal::config('system.theme') ->set('default', 'bartik') - ->set('admin', 'seven') ->save(); + theme_enable(array('seven')); + \Drupal::config('system.theme')->set('admin', 'seven')->save(); // Create and log in as a user who has permission to add and edit taxonomy // terms and view the administrative theme. diff --git a/core/modules/text/lib/Drupal/text/Tests/Formatter/TextPlainUnitTest.php b/core/modules/text/lib/Drupal/text/Tests/Formatter/TextPlainUnitTest.php index 3cd5dcadd051f2179b98d7fec68390acda04b218..72c0fe9769ba6cea8846548bb5ae1d1ff4858eb2 100644 --- a/core/modules/text/lib/Drupal/text/Tests/Formatter/TextPlainUnitTest.php +++ b/core/modules/text/lib/Drupal/text/Tests/Formatter/TextPlainUnitTest.php @@ -49,7 +49,6 @@ function setUp() { // Configure the theme system. $this->installConfig(array('system', 'field')); $this->installSchema('entity_test', 'entity_test'); - $this->installSchema('user', 'users'); // @todo Add helper methods for all of the following. diff --git a/core/modules/user/lib/Drupal/user/Theme/AdminNegotiator.php b/core/modules/user/lib/Drupal/user/Theme/AdminNegotiator.php deleted file mode 100644 index 4658713303b0688b2fa757ceff78b2f06dd2a4a8..0000000000000000000000000000000000000000 --- a/core/modules/user/lib/Drupal/user/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 --git a/core/modules/user/lib/Drupal/user/Theme/UserNegotiator.php b/core/modules/user/lib/Drupal/user/Theme/UserNegotiator.php deleted file mode 100644 index 17c1e33bebcd593f97a89c7c3ac92f8b4d9e75bb..0000000000000000000000000000000000000000 --- a/core/modules/user/lib/Drupal/user/Theme/UserNegotiator.php +++ /dev/null @@ -1,61 +0,0 @@ -userStorageController = $entity_manager->getStorageController('user'); - $this->currentUser = $current_user; - } - - /** - * {@inheritdoc} - */ - public function determineActiveTheme(Request $request) { - if ($user = $this->userStorageController->load($this->currentUser->id())) {; - // Only select the user selected theme if it is available in the - // list of themes that can be accessed. - if (!empty($user->theme) && drupal_theme_access($user->theme)) { - return $user->theme; - } - } - } - -} diff --git a/core/modules/user/user.services.yml b/core/modules/user/user.services.yml index bc348fa302f5613157428aac0325d7455ee0b59b..6fb7d47eeabff9fe3c4f9858ab936f107490b8e2 100644 --- a/core/modules/user/user.services.yml +++ b/core/modules/user/user.services.yml @@ -25,13 +25,3 @@ services: class: Drupal\user\EventSubscriber\MaintenanceModeSubscriber tags: - { name: event_subscriber } - theme.negotiator.user: - class: Drupal\user\Theme\UserNegotiator - arguments: ['@plugin.manager.entity', '@current_user'] - tags: - - { name: theme_negotiator, priority: -50 } - theme.negotiator.admin_theme: - class: Drupal\user\Theme\AdminNegotiator - arguments: ['@current_user', '@config.factory', '@entity.manager'] - tags: - - { name: theme_negotiator, priority: -40 } diff --git a/core/modules/views/lib/Drupal/views/Tests/Handler/FieldCounterTest.php b/core/modules/views/lib/Drupal/views/Tests/Handler/FieldCounterTest.php index 897bfd923de07ad43cad144d215b13183a0fa770..87732485d4588b5eb293fdda1902cac2cf1184e4 100644 --- a/core/modules/views/lib/Drupal/views/Tests/Handler/FieldCounterTest.php +++ b/core/modules/views/lib/Drupal/views/Tests/Handler/FieldCounterTest.php @@ -19,7 +19,7 @@ class FieldCounterTest extends ViewUnitTestBase { * * @var array */ - public static $modules = array('user', 'field'); + public static $modules = array('user'); /** * Views used by this test. @@ -36,12 +36,6 @@ public static function getInfo() { ); } - protected function setUp() { - parent::setUp(); - - $this->installSchema('user', 'users'); - } - function testSimple() { $view = views_get_view('test_view'); $view->setDisplay(); diff --git a/core/modules/views/lib/Drupal/views/Tests/Handler/FieldUnitTest.php b/core/modules/views/lib/Drupal/views/Tests/Handler/FieldUnitTest.php index 9c500021959884b40a29f058a64d199580e4ecde..eff89294d3dc191a2a101b9fbc072d33aa9e321a 100644 --- a/core/modules/views/lib/Drupal/views/Tests/Handler/FieldUnitTest.php +++ b/core/modules/views/lib/Drupal/views/Tests/Handler/FieldUnitTest.php @@ -17,7 +17,7 @@ */ class FieldUnitTest extends ViewUnitTestBase { - public static $modules = array('user', 'field'); + public static $modules = array('user'); /** * Views used by this test. @@ -38,12 +38,6 @@ public static function getInfo() { ); } - protected function setUp() { - parent::setUp(); - - $this->installSchema('user', 'users'); - } - /** * Overrides Drupal\views\Tests\ViewTestBase::viewsData(). */ diff --git a/core/modules/views/lib/Drupal/views/Tests/Plugin/DisplayPageTest.php b/core/modules/views/lib/Drupal/views/Tests/Plugin/DisplayPageTest.php index b4e343d407208b1f1c066f9ca94153d03765205f..48c116e201dc9e4ffd7a5b4b310d3fa3ff73505e 100644 --- a/core/modules/views/lib/Drupal/views/Tests/Plugin/DisplayPageTest.php +++ b/core/modules/views/lib/Drupal/views/Tests/Plugin/DisplayPageTest.php @@ -59,7 +59,6 @@ protected function setUp() { // Setup the needed tables in order to make the drupal router working. $this->installSchema('system', array('router', 'menu_router', 'url_alias')); $this->installSchema('menu_link', 'menu_links'); - $this->installSchema('user', 'users'); } /** diff --git a/core/modules/views/lib/Drupal/views/ViewExecutable.php b/core/modules/views/lib/Drupal/views/ViewExecutable.php index 413962bd2beb781150f9748c2f87ae59c02cc1fb..435da780cd607391b10d6e40740ab37a714e08f4 100644 --- a/core/modules/views/lib/Drupal/views/ViewExecutable.php +++ b/core/modules/views/lib/Drupal/views/ViewExecutable.php @@ -1302,6 +1302,8 @@ public function render($display_id = NULL) { return; } + drupal_theme_initialize(); + $exposed_form = $this->display_handler->getPlugin('exposed_form'); $exposed_form->preRender($this->result); @@ -1363,14 +1365,12 @@ public function render($display_id = NULL) { $module_handler->invokeAll('views_pre_render', array($this)); // Let the themes play too, because pre render is a very themey thing. - if (isset($GLOBALS['base_theme_info']) && isset($GLOBALS['theme'])) { - foreach ($GLOBALS['base_theme_info'] as $base) { - $module_handler->invoke($base, 'views_pre_render', array($this)); - } - - $module_handler->invoke($GLOBALS['theme'], 'views_pre_render', array($this)); + foreach ($GLOBALS['base_theme_info'] as $base) { + $module_handler->invoke($base, 'views_pre_render', array($this)); } + $module_handler->invoke($GLOBALS['theme'], 'views_pre_render', array($this)); + $this->display_handler->output = $this->display_handler->render(); if ($cache) { $cache->cacheSet('output'); @@ -1387,14 +1387,12 @@ public function render($display_id = NULL) { $module_handler->invokeAll('views_post_render', array($this, &$this->display_handler->output, $cache)); // Let the themes play too, because post render is a very themey thing. - if (isset($GLOBALS['base_theme_info']) && isset($GLOBALS['theme'])) { - foreach ($GLOBALS['base_theme_info'] as $base) { - $module_handler->invoke($base, 'views_post_render', array($this)); - } - - $module_handler->invoke($GLOBALS['theme'], 'views_post_render', array($this)); + foreach ($GLOBALS['base_theme_info'] as $base) { + $module_handler->invoke($base, 'views_post_render', array($this)); } + $module_handler->invoke($GLOBALS['theme'], 'views_post_render', array($this)); + return $this->display_handler->output; } diff --git a/core/modules/views/views.module b/core/modules/views/views.module index 11dbebba0405d1312e51990739f1695672c737b6..5bea350b132873ed8aba0d65ab5b1312096ed2c6 100644 --- a/core/modules/views/views.module +++ b/core/modules/views/views.module @@ -246,6 +246,20 @@ function views_permission() { ); } +/** + * 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(). */ diff --git a/core/modules/views/views.routing.yml b/core/modules/views/views.routing.yml index 691640d00ea0a7076cd76902288d3e948d97a0de..9abe5d59142a7dfc22bc93e83289765f9ed94583 100644 --- a/core/modules/views/views.routing.yml +++ b/core/modules/views/views.routing.yml @@ -2,7 +2,5 @@ views.ajax: path: '/views/ajax' defaults: _controller: '\Drupal\views\Controller\ViewAjaxController::ajaxView' - options: - _theme: ajax_base_page requirements: _access: 'TRUE' diff --git a/core/tests/Drupal/Tests/Core/Theme/ThemeNegotiatorTest.php b/core/tests/Drupal/Tests/Core/Theme/ThemeNegotiatorTest.php deleted file mode 100644 index 9779e63822363f620ffd604e915fbbd4342191b5..0000000000000000000000000000000000000000 --- a/core/tests/Drupal/Tests/Core/Theme/ThemeNegotiatorTest.php +++ /dev/null @@ -1,141 +0,0 @@ - 'Theme negotiator', - 'description' => 'Tests the theme negotiator.', - 'group' => 'Theme', - ); - } - - protected function setUp() { - $this->themeAccessCheck = $this->getMockBuilder('\Drupal\Core\Theme\ThemeAccessCheck') - ->disableOriginalConstructor() - ->getMock(); - $this->themeNegotiator = new ThemeNegotiator($this->themeAccessCheck); - } - - /** - * Tests determining the theme. - * - * @see \Drupal\Core\Theme\ThemeNegotiator::determineActiveTheme() - */ - public function testDetermineActiveTheme() { - $negotiator = $this->getMock('Drupal\Core\Theme\ThemeNegotiatorInterface'); - $negotiator->expects($this->once()) - ->method('determineActiveTheme') - ->will($this->returnValue('example_test')); - - $this->themeNegotiator->addNegotiator($negotiator, 0); - - $this->themeAccessCheck->expects($this->any()) - ->method('checkAccess') - ->will($this->returnValue(TRUE)); - - $request = Request::create('/test-route'); - $theme = $this->themeNegotiator->determineActiveTheme($request); - - $this->assertEquals('example_test', $theme); - $this->assertEquals('example_test', $request->attributes->get('_theme_active')); - } - - /** - * Tests determining with two negotiators checking the priority. - * - * @see \Drupal\Core\Theme\ThemeNegotiator::determineActiveTheme() - */ - public function testDetermineActiveThemeWithPriority() { - $negotiator = $this->getMock('Drupal\Core\Theme\ThemeNegotiatorInterface'); - $negotiator->expects($this->once()) - ->method('determineActiveTheme') - ->will($this->returnValue('example_test')); - - $this->themeNegotiator->addNegotiator($negotiator, 10); - - $negotiator = $this->getMock('Drupal\Core\Theme\ThemeNegotiatorInterface'); - $negotiator->expects($this->never()) - ->method('determineActiveTheme'); - - $this->themeNegotiator->addNegotiator($negotiator, 0); - - $this->themeAccessCheck->expects($this->any()) - ->method('checkAccess') - ->will($this->returnValue(TRUE)); - - $request = Request::create('/test-route'); - $theme = $this->themeNegotiator->determineActiveTheme($request); - - $this->assertEquals('example_test', $theme); - $this->assertEquals('example_test', $request->attributes->get('_theme_active')); - } - - /** - * Tests determining with two negotiators of which just one returns access. - * - * @see \Drupal\Core\Theme\ThemeNegotiator::determineActiveTheme() - */ - public function testDetermineActiveThemeWithAccessCheck() { - $negotiator = $this->getMock('Drupal\Core\Theme\ThemeNegotiatorInterface'); - $negotiator->expects($this->once()) - ->method('determineActiveTheme') - ->will($this->returnValue('example_test')); - - $this->themeNegotiator->addNegotiator($negotiator, 10); - - $negotiator = $this->getMock('Drupal\Core\Theme\ThemeNegotiatorInterface'); - $negotiator->expects($this->once()) - ->method('determineActiveTheme') - ->will($this->returnValue('example_test2')); - - $this->themeNegotiator->addNegotiator($negotiator, 0); - - $this->themeAccessCheck->expects($this->at(0)) - ->method('checkAccess') - ->with('example_test') - ->will($this->returnValue(FALSE)); - - $this->themeAccessCheck->expects($this->at(1)) - ->method('checkAccess') - ->with('example_test2') - ->will($this->returnValue(TRUE)); - - $request = Request::create('/test-route'); - $theme = $this->themeNegotiator->determineActiveTheme($request); - - $this->assertEquals('example_test2', $theme); - $this->assertEquals('example_test2', $request->attributes->get('_theme_active')); - } - -}