diff --git a/core/core.services.yml b/core/core.services.yml index 6f8cce6..4d6d9fe 100644 --- a/core/core.services.yml +++ b/core/core.services.yml @@ -152,6 +152,14 @@ 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 } container.namespaces: class: ArrayObject arguments: [ '%container.namespaces%' ] diff --git a/core/includes/theme.inc b/core/includes/theme.inc index 7bff76e..c686cdb 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 : 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 to a theme negotiator. + // service. // 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 a6cb957..c3c6df5 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; @@ -68,6 +69,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/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..392b385 --- /dev/null +++ b/core/lib/Drupal/Core/Theme/ThemeNegotiator.php @@ -0,0 +1,93 @@ +request = $request; + + $this->determineActiveTheme($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; + } + + /** + * {@inheritdoc} + */ + public function determineActiveTheme(Request $request) { + foreach ($this->getSortedNegotiators() as $negotiator) { + if (($active_theme = $negotiator->determineActiveTheme($request)) && $active_theme !== NULL) { + $request->attributes->set('_theme_active', $active_theme); + } + } + } + +} 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/modules/user/lib/Drupal/user/Theme/UserNegotiator.php b/core/modules/user/lib/Drupal/user/Theme/UserNegotiator.php new file mode 100644 index 0000000..185703b --- /dev/null +++ b/core/modules/user/lib/Drupal/user/Theme/UserNegotiator.php @@ -0,0 +1,52 @@ +userStorageController = $entity_manager->getStorageController('user'); + } + + /** + * {@inheritdoc} + */ + public function determineActiveTheme(Request $request) { + if ($account = $request->attributes->get('account')) {; + if ($user = $this->userStorageController->load($account->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..6b34ff6 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'] + tags: + - { name: theme_negotiator } + diff --git a/core/modules/views/lib/Drupal/views/ViewExecutable.php b/core/modules/views/lib/Drupal/views/ViewExecutable.php index 41d874e..d3a76bd 100644 --- a/core/modules/views/lib/Drupal/views/ViewExecutable.php +++ b/core/modules/views/lib/Drupal/views/ViewExecutable.php @@ -1277,7 +1277,6 @@ public function render($display_id = NULL) { return; } - drupal_theme_initialize(); $config = config('views.settings'); $exposed_form = $this->display_handler->getPlugin('exposed_form'); @@ -1341,11 +1340,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) { @@ -1363,11 +1364,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; }