diff --git a/core/lib/Drupal/Core/Theme/ThemeAccessCheck.php b/core/lib/Drupal/Core/Theme/ThemeAccessCheck.php index 7f4cefa..fdd6c77 100644 --- a/core/lib/Drupal/Core/Theme/ThemeAccessCheck.php +++ b/core/lib/Drupal/Core/Theme/ThemeAccessCheck.php @@ -47,17 +47,17 @@ public function access($theme) { } /** - * Checks access based on whether it is installed or is hidden. + * Indicates whether the theme is accessible based on whether it is installed. * * @param string $theme * The name of a theme. * * @return bool - * TRUE if the theme is installed and not hidden, FALSE otherwise. + * TRUE if the theme is installed, FALSE otherwise. */ public function checkAccess($theme) { $themes = $this->themeHandler->listInfo(); - return !empty($themes[$theme]->status) && empty($themes[$theme]->info['hidden']); + return !empty($themes[$theme]->status); } } diff --git a/core/modules/block/src/Controller/BlockController.php b/core/modules/block/src/Controller/BlockController.php index 2779a9b..a67b66d 100644 --- a/core/modules/block/src/Controller/BlockController.php +++ b/core/modules/block/src/Controller/BlockController.php @@ -11,6 +11,7 @@ use Drupal\Core\Controller\ControllerBase; use Drupal\Core\Extension\ThemeHandlerInterface; use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; /** * Controller routines for admin block routes. @@ -53,6 +54,14 @@ public static function create(ContainerInterface $container) { * A #type 'page' render array containing the block region demo. */ public function demo($theme) { + // Ensure the theme is installed and not hidden. + $themes = $this->themeHandler->listInfo(); + + // Deny access if the theme is not installed or not found. + if (empty($themes[$theme]) || !$themes[$theme]->status || !empty($themes[$theme]->info['hidden'])) { + throw new NotFoundHttpException(); + } + $page = [ '#title' => Html::escape($this->themeHandler->getName($theme)), '#type' => 'page', diff --git a/core/modules/block/src/Controller/BlockListController.php b/core/modules/block/src/Controller/BlockListController.php index 72aa445..e7bb5d8 100644 --- a/core/modules/block/src/Controller/BlockListController.php +++ b/core/modules/block/src/Controller/BlockListController.php @@ -8,7 +8,10 @@ namespace Drupal\block\Controller; use Drupal\Core\Entity\Controller\EntityListController; +use Drupal\Core\Extension\ThemeHandlerInterface; +use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; /** * Defines a controller to list blocks. @@ -16,6 +19,32 @@ class BlockListController extends EntityListController { /** + * The theme handler. + * + * @var \Drupal\Core\Extension\ThemeHandlerInterface + */ + protected $themeHandler; + + /** + * Constructs the BlockListController. + * + * @param \Drupal\Core\Extension\ThemeHandlerInterface $theme_handler + * The theme handler. + */ + public function __construct(ThemeHandlerInterface $theme_handler) { + $this->themeHandler = $theme_handler; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container) { + return new static( + $container->get('theme_handler') + ); + } + + /** * Shows the block administration page. * * @param string|null $theme @@ -28,6 +57,14 @@ class BlockListController extends EntityListController { */ public function listing($theme = NULL, Request $request = NULL) { $theme = $theme ?: $this->config('system.theme')->get('default'); + // Ensure the theme is installed and not hidden. + $themes = $this->themeHandler->listInfo(); + + // Deny access if the theme is not installed or not found. + if (empty($themes[$theme]) || !$themes[$theme]->status || !empty($themes[$theme]->info['hidden'])) { + throw new NotFoundHttpException(); + } + return $this->entityManager()->getListBuilder('block')->render($theme, $request); } diff --git a/core/modules/block/src/Plugin/Derivative/ThemeLocalTask.php b/core/modules/block/src/Plugin/Derivative/ThemeLocalTask.php index 4e337db..c5ff9d5 100644 --- a/core/modules/block/src/Plugin/Derivative/ThemeLocalTask.php +++ b/core/modules/block/src/Plugin/Derivative/ThemeLocalTask.php @@ -50,7 +50,7 @@ public function getDerivativeDefinitions($base_plugin_definition) { $default_theme = $this->themeHandler->getDefault(); foreach ($this->themeHandler->listInfo() as $theme_name => $theme) { - if ($theme->status) { + if ($theme->status && empty($theme->info['hidden'])) { $this->derivatives[$theme_name] = $base_plugin_definition; $this->derivatives[$theme_name]['title'] = $theme->info['name']; $this->derivatives[$theme_name]['route_parameters'] = array('theme' => $theme_name); diff --git a/core/modules/block/src/Tests/BlockUiTest.php b/core/modules/block/src/Tests/BlockUiTest.php index e64c060..e920d12 100644 --- a/core/modules/block/src/Tests/BlockUiTest.php +++ b/core/modules/block/src/Tests/BlockUiTest.php @@ -92,7 +92,7 @@ public function testBlockDemoUiPage() { $this->assertEscaped('Test theme'); $this->drupalGet('admin/structure/block/demo/classy'); - $this->assertResponse(403, 'Hidden themes are not supported by the block demo screen'); + $this->assertResponse(404, 'Hidden themes are not supported by the block demo screen'); } /** @@ -152,7 +152,7 @@ function testBlockAdminUiPage() { $this->assertNoLink($theme_handler->getName('classy')); $this->drupalGet('admin/structure/block/list/classy'); - $this->assertResponse(403, 'Placing blocks through UI is not possible for the base theme classy.'); + $this->assertResponse(404, 'Placing blocks through UI is not possible for the base theme classy.'); } /** diff --git a/core/modules/system/src/Form/ThemeSettingsForm.php b/core/modules/system/src/Form/ThemeSettingsForm.php index 395c743..e9d810f 100644 --- a/core/modules/system/src/Form/ThemeSettingsForm.php +++ b/core/modules/system/src/Form/ThemeSettingsForm.php @@ -109,7 +109,7 @@ public function buildForm(array $form, FormStateInterface $form_state, $theme = $themes = $this->themeHandler->listInfo(); // Deny access if the theme is not installed or not found. - if (!empty($theme) && (empty($themes[$theme]) || !$themes[$theme]->status)) { + if (!empty($theme) && (empty($themes[$theme]) || !$themes[$theme]->status || !empty($themes[$theme]->info['hidden']))) { throw new NotFoundHttpException(); } diff --git a/core/modules/system/src/Plugin/Derivative/ThemeLocalTask.php b/core/modules/system/src/Plugin/Derivative/ThemeLocalTask.php index 9c9de19..aae7885 100644 --- a/core/modules/system/src/Plugin/Derivative/ThemeLocalTask.php +++ b/core/modules/system/src/Plugin/Derivative/ThemeLocalTask.php @@ -48,7 +48,7 @@ public static function create(ContainerInterface $container, $base_plugin_id) { */ public function getDerivativeDefinitions($base_plugin_definition) { foreach ($this->themeHandler->listInfo() as $theme_name => $theme) { - if ($theme->status) { + if ($theme->status && empty($theme->info['hidden'])) { $this->derivatives[$theme_name] = $base_plugin_definition; $this->derivatives[$theme_name]['title'] = $theme->info['name']; $this->derivatives[$theme_name]['route_parameters'] = array('theme' => $theme_name); diff --git a/core/modules/system/src/Tests/System/ThemeTest.php b/core/modules/system/src/Tests/System/ThemeTest.php index 94dd537..d92ed1b 100644 --- a/core/modules/system/src/Tests/System/ThemeTest.php +++ b/core/modules/system/src/Tests/System/ThemeTest.php @@ -52,6 +52,9 @@ function testThemeSettings() { $this->assertResponse(404, 'The theme settings form URL for a uninstalled theme could not be found.'); $this->drupalGet('admin/appearance/settings/' . $this->randomMachineName()); $this->assertResponse(404, 'The theme settings form URL for a non-existent theme could not be found.'); + $this->assertTrue(\Drupal::service('theme_handler')->themeExists('classy')); + $this->drupalGet('admin/appearance/settings/classy'); + $this->assertResponse(404, 'The theme settings form URL for a hidden theme are unavailable.'); // Specify a filesystem path to be used for the logo. $file = current($this->drupalGetTestFiles('image')); @@ -190,6 +193,14 @@ function testThemeSettings() { // The logo field should only be present on the global theme settings form. $this->assertNoFieldByName('logo_path'); $this->drupalPostForm(NULL, [], t('Save configuration')); + + // Ensure only vlaid themes are listed in the local tasks. + $this->drupalPlaceBlock('local_tasks_block', ['region' => 'header']); + $this->drupalGet('admin/appearance/settings'); + $theme_handler = \Drupal::service('theme_handler'); + $this->assertLink($theme_handler->getName('test_classy')); + $this->assertLink($theme_handler->getName('bartik')); + $this->assertNoLink($theme_handler->getName('classy')); } /**