diff --git a/core/core.services.yml b/core/core.services.yml index cb83270..f71a5ee 100644 --- a/core/core.services.yml +++ b/core/core.services.yml @@ -153,6 +153,19 @@ services: calls: - [addSubscriber, ['@http_client_simpletest_subscriber']] - [setUserAgent, ['Drupal (+http://drupal.org/)']] + theme.negotiator: + class: Drupal\Core\Theme\ThemeNegotiator + arguments: ['@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'] + tags: + - { name: theme_negotiator } container.namespaces: class: ArrayObject arguments: [ '%container.namespaces%' ] diff --git a/core/includes/theme.inc b/core/includes/theme.inc index 094a339..893971a 100644 --- a/core/includes/theme.inc +++ b/core/includes/theme.inc @@ -92,16 +92,24 @@ function drupal_theme_initialize() { } drupal_bootstrap(DRUPAL_BOOTSTRAP_DATABASE); - $themes = list_themes(); // 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'); + $themes = list_themes(); + + // @todo Let the theme.negotiator listen to the kernel request event. + $request = Drupal::request(); + Drupal::service('theme.negotiator')->determineActiveTheme($request); + // @todo Convert all theme callbacks to theme negotiators. // 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; + if ($custom_theme) { + $request->attributes->set('_theme_active', $custom_theme); + } + + $theme = $request->attributes->get('_theme_active'); // Store the identifier for retrieving theme settings with. $theme_key = $theme; @@ -116,7 +124,7 @@ function drupal_theme_initialize() { _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'); + Drupal::moduleHandler()->resetImplementations(); } /** diff --git a/core/lib/Drupal/Core/CoreServiceProvider.php b/core/lib/Drupal/Core/CoreServiceProvider.php index 9cd8f5a..4bd88ee 100644 --- a/core/lib/Drupal/Core/CoreServiceProvider.php +++ b/core/lib/Drupal/Core/CoreServiceProvider.php @@ -22,6 +22,7 @@ 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; @@ -69,6 +70,9 @@ 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 39828ef..d7ee895 100644 --- a/core/lib/Drupal/Core/EventSubscriber/LegacyRequestSubscriber.php +++ b/core/lib/Drupal/Core/EventSubscriber/LegacyRequestSubscriber.php @@ -27,9 +27,6 @@ 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 diff --git a/core/lib/Drupal/Core/Theme/AjaxBasePageNegotiator.php b/core/lib/Drupal/Core/Theme/AjaxBasePageNegotiator.php new file mode 100644 index 0000000..8c412fe --- /dev/null +++ b/core/lib/Drupal/Core/Theme/AjaxBasePageNegotiator.php @@ -0,0 +1,69 @@ +csrfGenerator = $token_generator; + } + + /** + * {@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('_ajax_base_page_theme'))) { + 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']; + + // Ensure that the user only access a theme they are allowed to see. + if ($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 new file mode 100644 index 0000000..f155b0d --- /dev/null +++ b/core/lib/Drupal/Core/Theme/DefaultNegotiator.php @@ -0,0 +1,35 @@ +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 new file mode 100644 index 0000000..5db1885 --- /dev/null +++ b/core/lib/Drupal/Core/Theme/ThemeNegotiator.php @@ -0,0 +1,115 @@ +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) { + if (($active_theme = $negotiator->determineActiveTheme($request))) { + $request->attributes->set('_theme_active', $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 new file mode 100644 index 0000000..9bb80b1 --- /dev/null +++ b/core/lib/Drupal/Core/Theme/ThemeNegotiatorInterface.php @@ -0,0 +1,28 @@ +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 a586945..e9baf94 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 ajax_base_page_theme() + // @see \Drupal\Core\Theme\AjaxBasePageNegotiator // @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 f1112d6..108f6ba 100644 --- a/core/modules/block/block.module +++ b/core/modules/block/block.module @@ -134,13 +134,6 @@ function block_menu() { 'type' => $key == $default_theme ? MENU_DEFAULT_LOCAL_TASK : MENU_LOCAL_TASK, 'route_name' => "block.admin_display_$key", ); - $items["admin/structure/block/demo/$key"] = array( - 'title' => check_plain($theme->info['name']), - 'route_name' => 'block.admin_demo', - 'type' => MENU_CALLBACK, - 'theme callback' => '_block_custom_theme', - 'theme arguments' => array($key), - ); } return $items; } @@ -162,25 +155,6 @@ function _block_themes_access($theme) { } /** - * 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(). * * Renders blocks into their regions. diff --git a/core/modules/block/block.services.yml b/core/modules/block/block.services.yml index c6704bc..39b414a 100644 --- a/core/modules/block/block.services.yml +++ b/core/modules/block/block.services.yml @@ -13,3 +13,7 @@ services: class: Drupal\block\Routing\RouteSubscriber tags: - { name: event_subscriber} + theme.negotiator.block.admin_demo: + class: Drupal\block\Theme\AdminDemoNegotiator + tags: + - { name: theme_negotiator } diff --git a/core/modules/block/lib/Drupal/block/Theme/AdminDemoNegotiator.php b/core/modules/block/lib/Drupal/block/Theme/AdminDemoNegotiator.php new file mode 100644 index 0000000..2d9d351 --- /dev/null +++ b/core/modules/block/lib/Drupal/block/Theme/AdminDemoNegotiator.php @@ -0,0 +1,30 @@ +attributes->get(RouteObjectInterface::ROUTE_NAME) == 'block.admin_demo') { + return $request->attributes->get('theme'); + } + } + +} diff --git a/core/modules/contextual/contextual.module b/core/modules/contextual/contextual.module index 529fa5c..508b9ae 100644 --- a/core/modules/contextual/contextual.module +++ b/core/modules/contextual/contextual.module @@ -6,21 +6,6 @@ */ /** - * 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(). */ function contextual_toolbar() { diff --git a/core/modules/contextual/contextual.routing.yml b/core/modules/contextual/contextual.routing.yml index 8ab2f28..6947f6a 100644 --- a/core/modules/contextual/contextual.routing.yml +++ b/core/modules/contextual/contextual.routing.yml @@ -2,5 +2,7 @@ contextual.render: path: '/contextual/render' defaults: _controller: '\Drupal\contextual\ContextualController::render' + options: + _ajax_base_page_theme: 'TRUE' requirements: _permission: 'access contextual links' diff --git a/core/modules/edit/edit.module b/core/modules/edit/edit.module index 000f67f..fe4ee15 100644 --- a/core/modules/edit/edit.module +++ b/core/modules/edit/edit.module @@ -18,26 +18,6 @@ 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(). */ function edit_permission() { diff --git a/core/modules/edit/edit.routing.yml b/core/modules/edit/edit.routing.yml index 27bced7..a0ecec2 100644 --- a/core/modules/edit/edit.routing.yml +++ b/core/modules/edit/edit.routing.yml @@ -2,6 +2,8 @@ edit.metadata: path: '/edit/metadata' defaults: _controller: '\Drupal\edit\EditController::metadata' + options: + _ajax_base_page_theme: 'TRUE' requirements: _permission: 'access in-place editing' @@ -18,6 +20,7 @@ edit.field_form: _controller: '\Drupal\edit\EditController::fieldForm' options: _access_mode: 'ALL' + _ajax_base_page_theme: 'TRUE' 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 8282d07..377a79e 100644 --- a/core/modules/editor/editor.module +++ b/core/modules/editor/editor.module @@ -140,21 +140,6 @@ function editor_library_info() { } /** - * 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(). */ function editor_form_filter_admin_overview_alter(&$form, $form_state) { diff --git a/core/modules/editor/editor.routing.yml b/core/modules/editor/editor.routing.yml index 46b4fba..4ad64e3 100644 --- a/core/modules/editor/editor.routing.yml +++ b/core/modules/editor/editor.routing.yml @@ -4,6 +4,7 @@ editor.field_untransformed_text: _controller: '\Drupal\editor\EditorController::getUntransformedText' options: _access_mode: 'ALL' + _ajax_base_page_theme: 'TRUE' 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 3dcbcef..a16aa09 100644 --- a/core/modules/file/file.module +++ b/core/modules/file/file.module @@ -38,21 +38,6 @@ 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(). * * The managed file element may be used anywhere in Drupal. diff --git a/core/modules/file/file.routing.yml b/core/modules/file/file.routing.yml index 8bf971c..2b84612 100644 --- a/core/modules/file/file.routing.yml +++ b/core/modules/file/file.routing.yml @@ -2,6 +2,8 @@ file.ajax_upload: path: '/file/ajax' defaults: _controller: '\Drupal\file\Controller\FileWidgetAjaxController::upload' + options: + _ajax_base_page_theme: 'TRUE' requirements: _permission: 'access content' diff --git a/core/modules/system/lib/Drupal/system/Theme/BatchNegotiator.php b/core/modules/system/lib/Drupal/system/Theme/BatchNegotiator.php new file mode 100644 index 0000000..e6694e9 --- /dev/null +++ b/core/modules/system/lib/Drupal/system/Theme/BatchNegotiator.php @@ -0,0 +1,54 @@ +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.module b/core/modules/system/system.module index 8437e4c..6fc8da3 100644 --- a/core/modules/system/system.module +++ b/core/modules/system/system.module @@ -608,12 +608,6 @@ 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', @@ -878,13 +872,6 @@ 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, - ); - // Localize date formats. if (module_exists('language')) { $items['admin/config/regional/date-time/locale'] = array( @@ -958,21 +945,6 @@ function system_theme_suggestions_region(array $variables) { } /** - * 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(). */ function system_library_info() { diff --git a/core/modules/system/system.routing.yml b/core/modules/system/system.routing.yml index 508d59c..ecb9864 100644 --- a/core/modules/system/system.routing.yml +++ b/core/modules/system/system.routing.yml @@ -2,6 +2,8 @@ system.ajax: path: '/system/ajax' defaults: _controller: '\Drupal\system\Controller\FormAjaxController::content' + options: + _ajax_base_page_theme: 'TRUE' requirements: _access: 'TRUE' diff --git a/core/modules/system/system.services.yml b/core/modules/system/system.services.yml index ae137da..d05632d 100644 --- a/core/modules/system/system.services.yml +++ b/core/modules/system/system.services.yml @@ -23,3 +23,8 @@ services: class: Drupal\system\Routing\RouteSubscriber tags: - { name: event_subscriber } + theme.negotiator.system.batch: + class: Drupal\system\Theme\BatchNegotiator + arguments: ['@batch.storage'] + 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 new file mode 100644 index 0000000..6ca1b71 --- /dev/null +++ b/core/modules/system/tests/modules/theme_test/lib/Drupal/theme_test/Theme/CustomThemeNegotiator.php @@ -0,0 +1,29 @@ +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/theme_test.module b/core/modules/system/tests/modules/theme_test/theme_test.module index 7c205c8..2036faf 100644 --- a/core/modules/system/tests/modules/theme_test/theme_test.module +++ b/core/modules/system/tests/modules/theme_test/theme_test.module @@ -57,26 +57,6 @@ function theme_test_system_theme_info() { } /** - * Implements hook_menu(). - */ -function theme_test_menu() { - $items['theme-test/suggestion'] = array( - 'route_name' => 'theme_test.suggestion', - 'theme callback' => '_theme_custom_theme', - 'type' => MENU_CALLBACK, - ); - $items['theme-test/alter'] = array( - 'theme callback' => '_theme_custom_theme', - 'route_name' => 'theme_test.alter', - 'type' => MENU_CALLBACK, - ); - $items['theme-test/function-template-overridden'] = array( - 'theme callback' => '_theme_custom_theme', - 'route_name' => 'theme_test.function_template_override', - ); - return $items; -} -/** * Fake registry loading callback. */ function _theme_test_load_registry() { @@ -85,13 +65,6 @@ function _theme_test_load_registry() { } /** - * Custom theme callback. - */ -function _theme_custom_theme() { - return 'test_theme'; -} - -/** * Implements hook_preprocess_HOOK() for html.tpl.php. */ function theme_test_preprocess_html(&$variables) { 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 b4a7fd6..8a750f3 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,5 +1,7 @@ theme_test.function_template_override: path: '/theme-test/function-template-overridden' + options: + _custom_theme: 'test_theme' defaults: _content: '\Drupal\theme_test\ThemeTestController::functionTemplateOverridden' requirements: @@ -21,6 +23,8 @@ 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' @@ -29,6 +33,8 @@ theme_test.suggestion: 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 8e442df..29c419d 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,3 +3,8 @@ 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 } diff --git a/core/modules/user/lib/Drupal/user/Theme/UserNegotiator.php b/core/modules/user/lib/Drupal/user/Theme/UserNegotiator.php new file mode 100644 index 0000000..17c1e33 --- /dev/null +++ b/core/modules/user/lib/Drupal/user/Theme/UserNegotiator.php @@ -0,0 +1,61 @@ +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 6fb7d47..c70cad1 100644 --- a/core/modules/user/user.services.yml +++ b/core/modules/user/user.services.yml @@ -25,3 +25,9 @@ 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 } + diff --git a/core/modules/views/lib/Drupal/views/ViewExecutable.php b/core/modules/views/lib/Drupal/views/ViewExecutable.php index d8236cf..37329c7 100644 --- a/core/modules/views/lib/Drupal/views/ViewExecutable.php +++ b/core/modules/views/lib/Drupal/views/ViewExecutable.php @@ -1302,8 +1302,6 @@ public function render($display_id = NULL) { return; } - drupal_theme_initialize(); - $exposed_form = $this->display_handler->getPlugin('exposed_form'); $exposed_form->preRender($this->result); @@ -1365,11 +1363,13 @@ 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. - foreach ($GLOBALS['base_theme_info'] as $base) { - $module_handler->invoke($base, 'views_pre_render', array($this)); - } + 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)); + $module_handler->invoke($GLOBALS['theme'], 'views_pre_render', array($this)); + } $this->display_handler->output = $this->display_handler->render(); if ($cache) { @@ -1387,11 +1387,13 @@ 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. - foreach ($GLOBALS['base_theme_info'] as $base) { - $module_handler->invoke($base, 'views_post_render', array($this)); - } + 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)); + $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 7a6b346..2525c8e 100644 --- a/core/modules/views/views.module +++ b/core/modules/views/views.module @@ -329,20 +329,6 @@ 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(). */ function views_menu_alter(&$callbacks) { @@ -706,6 +692,9 @@ function views_invalidate_cache() { $module_handler = \Drupal::moduleHandler(); + // Reset the RouteSubscriber from views. + \Drupal::getContainer()->get('views.route_subscriber')->reset(); + // Set the router to be rebuild. // @todo Figure out why the cache rebuild is trigged but the route table // does not exist yet. diff --git a/core/modules/views/views.routing.yml b/core/modules/views/views.routing.yml index 9abe5d5..83f2aad 100644 --- a/core/modules/views/views.routing.yml +++ b/core/modules/views/views.routing.yml @@ -2,5 +2,7 @@ views.ajax: path: '/views/ajax' defaults: _controller: '\Drupal\views\Controller\ViewAjaxController::ajaxView' + options: + _ajax_base_page_theme: 'TRUE' requirements: _access: 'TRUE'