diff -u b/core/lib/Drupal/Core/Extension/ThemeHandler.php b/core/lib/Drupal/Core/Extension/ThemeHandler.php --- b/core/lib/Drupal/Core/Extension/ThemeHandler.php +++ b/core/lib/Drupal/Core/Extension/ThemeHandler.php @@ -490,7 +490,8 @@ $themes = $this->listInfo(); if (isset($themes[$name])) { if (!empty($themes[$name]->info['hidden'])) { - return $name == $this->getDefault(); + $theme_config = $this->configFactory->get('system.theme'); + return $name == $theme_config->get('default') || $name == $theme_config->get('admin'); } return TRUE; } diff -u b/core/lib/Drupal/Core/Extension/ThemeHandlerInterface.php b/core/lib/Drupal/Core/Extension/ThemeHandlerInterface.php --- b/core/lib/Drupal/Core/Extension/ThemeHandlerInterface.php +++ b/core/lib/Drupal/Core/Extension/ThemeHandlerInterface.php @@ -212,7 +212,7 @@ * Determines if a theme should be shown in the user interface. * * To be shown in the UI the theme has to be installed. If the theme is hidden - * it will not be shown unless it is the default theme. + * it will not be shown unless it is the default or admin theme. * * @param string $name * The name of the theme to check. diff -u b/core/modules/block/src/Tests/BlockUiTest.php b/core/modules/block/src/Tests/BlockUiTest.php --- b/core/modules/block/src/Tests/BlockUiTest.php +++ b/core/modules/block/src/Tests/BlockUiTest.php @@ -155,6 +155,14 @@ $this->drupalGet('admin/structure/block/list/stable'); $this->assertResponse(404, 'Placing blocks through UI is not possible for a hidden base theme.'); + + \Drupal::configFactory()->getEditable('system.theme')->set('admin', 'stable')->save(); + \Drupal::service('router.builder')->rebuildIfNeeded(); + $this->drupalPlaceBlock('local_tasks_block', ['region' => 'header', 'theme' => 'stable']); + $this->drupalGet('admin/structure/block'); + $this->assertLink($theme_handler->getName('stable')); + $this->drupalGet('admin/structure/block/list/stable'); + $this->assertResponse(200, 'Placing blocks through UI is possible for a hidden base theme that is the admin theme.'); } /** diff -u b/core/modules/system/src/Tests/System/ThemeTest.php b/core/modules/system/src/Tests/System/ThemeTest.php --- b/core/modules/system/src/Tests/System/ThemeTest.php +++ b/core/modules/system/src/Tests/System/ThemeTest.php @@ -201,6 +201,15 @@ $this->assertLink($theme_handler->getName('classy')); $this->assertLink($theme_handler->getName('bartik')); $this->assertNoLink($theme_handler->getName('stable')); + + // If a hidden theme is an admin theme it should be viewable. + \Drupal::configFactory()->getEditable('system.theme')->set('admin', 'stable')->save(); + \Drupal::service('router.builder')->rebuildIfNeeded(); + $this->drupalPlaceBlock('local_tasks_block', ['region' => 'header', 'theme' => 'stable']); + $this->drupalGet('admin/appearance/settings'); + $this->assertLink($theme_handler->getName('stable')); + $this->drupalGet('admin/appearance/settings/stable'); + $this->assertResponse(200, 'The theme settings form URL for a hidden theme that is the admin theme are avialable.'); } /** only in patch2: unchanged: --- a/core/modules/system/src/SystemConfigSubscriber.php +++ b/core/modules/system/src/SystemConfigSubscriber.php @@ -7,8 +7,10 @@ namespace Drupal\system; +use Drupal\Core\Config\ConfigCrudEvent; use Drupal\Core\Config\ConfigEvents; use Drupal\Core\Config\ConfigImporterEvent; +use Drupal\Core\Routing\RouteBuilderInterface; use Drupal\Core\StringTranslation\StringTranslationTrait; use Symfony\Component\EventDispatcher\EventSubscriberInterface; @@ -19,6 +21,35 @@ class SystemConfigSubscriber implements EventSubscriberInterface { use StringTranslationTrait; /** + * The router builder. + * + * @var \Drupal\Core\Routing\RouteBuilderInterface + */ + protected $routerBuilder; + + /** + * Constructs the SystemConfigSubscriber. + * + * @param \Drupal\Core\Routing\RouteBuilderInterface $route_builder + * The router builder service. + */ + public function __construct(RouteBuilderInterface $router_builder) { + $this->routerBuilder = $router_builder; + } + + /** + * Rebuilds the router when the default or admin theme is changed. + * + * @param \Drupal\Core\Config\ConfigCrudEvent $event + */ + public function onConfigSave(ConfigCrudEvent $event) { + $saved_config = $event->getConfig(); + if ($saved_config->getName() == 'system.theme' && ($event->isChanged('admin') || $event->isChanged('default'))) { + $this->routerBuilder->setRebuildNeeded(); + } + } + + /** * Checks that the configuration synchronization is valid. * * This event listener prevents deleting all configuration. If there is @@ -55,6 +86,7 @@ public function onConfigImporterValidateSiteUUID(ConfigImporterEvent $event) { * {@inheritdoc} */ public static function getSubscribedEvents() { + $events[ConfigEvents::SAVE][] = array('onConfigSave', 0); // The empty check has a high priority so that is can stop propagation if // there is no configuration to import. $events[ConfigEvents::IMPORT_VALIDATE][] = array('onConfigImporterValidateNotEmpty', 512); only in patch2: unchanged: --- a/core/modules/system/system.services.yml +++ b/core/modules/system/system.services.yml @@ -35,6 +35,7 @@ services: - { name: theme_negotiator, priority: 100 } system.config_subscriber: class: Drupal\system\SystemConfigSubscriber + arguments: ['@router.builder'] tags: - { name: event_subscriber } system.config_cache_tag: only in patch2: unchanged: --- a/core/modules/system/tests/src/Unit/Menu/SystemLocalTasksTest.php +++ b/core/modules/system/tests/src/Unit/Menu/SystemLocalTasksTest.php @@ -44,6 +44,10 @@ protected function setUp() { ->will($this->returnValue(array( 'bartik' => $theme, ))); + $this->themeHandler->expects($this->any()) + ->method('hasUi') + ->with('bartik') + ->willReturn(TRUE); $this->container->set('theme_handler', $this->themeHandler); }