diff --git a/core/lib/Drupal/Core/Extension/ExtensionNameReservedException.php b/core/lib/Drupal/Core/Extension/ExtensionNameReservedException.php new file mode 100644 index 0000000000..42569dd627 --- /dev/null +++ b/core/lib/Drupal/Core/Extension/ExtensionNameReservedException.php @@ -0,0 +1,8 @@ +get('theme') ?: []; + if (isset($installed_themes[$module])) { + throw new ExtensionNameReservedException("Module name $module is already in use by an enabled theme."); + } + // Load a new config object for each iteration, otherwise changes made // in hook_install() are not reflected in $extension_config. $extension_config = \Drupal::configFactory()->getEditable('core.extension'); diff --git a/core/lib/Drupal/Core/Extension/ThemeInstaller.php b/core/lib/Drupal/Core/Extension/ThemeInstaller.php index d377627243..e7246bb19a 100644 --- a/core/lib/Drupal/Core/Extension/ThemeInstaller.php +++ b/core/lib/Drupal/Core/Extension/ThemeInstaller.php @@ -207,6 +207,12 @@ public function install(array $theme_list, $install_dependencies = TRUE) { throw new ExtensionNameLengthException("Theme name $key is over the maximum allowed length of " . DRUPAL_EXTENSION_NAME_MAX_LENGTH . ' characters.'); } + // Throw an exception if a module with the same name is enabled. + $installed_modules = $extension_config->get('module') ?: []; + if (isset($installed_modules[$key])) { + throw new ExtensionNameReservedException("Theme name $key is already in use by an enabled module."); + } + // Validate default configuration of the theme. If there is existing // configuration then stop installing. $this->configInstaller->checkConfigurationToInstall('theme', $key); diff --git a/core/modules/system/tests/modules/test_theme_module_name_collision/test_theme_module_name_collision.info.yml b/core/modules/system/tests/modules/test_theme_module_name_collision/test_theme_module_name_collision.info.yml new file mode 100644 index 0000000000..87594d69be --- /dev/null +++ b/core/modules/system/tests/modules/test_theme_module_name_collision/test_theme_module_name_collision.info.yml @@ -0,0 +1,5 @@ +name: 'Same Name' +type: module +description: 'Test module with same name as a theme.' +package: Testing +version: VERSION diff --git a/core/modules/system/tests/src/Functional/Module/InstallTest.php b/core/modules/system/tests/src/Functional/Module/InstallTest.php index 0bc323ea29..77297ac9fe 100644 --- a/core/modules/system/tests/src/Functional/Module/InstallTest.php +++ b/core/modules/system/tests/src/Functional/Module/InstallTest.php @@ -99,4 +99,22 @@ public function testModuleNameLength() { } } + /** + * Tests installing a module with same name as enabled theme. + */ + public function testInstallModuleSameNameAsTheme() { + $name = 'test_theme_module_name_collision'; + + // Install and uninstall the module. + $this->container->get('module_installer')->install([$name]); + $this->container->get('module_installer')->uninstall([$name]); + + // Install the theme then module. + $this->container->get('theme_installer')->install([$name]); + $message = "Module name $name is already in use by an enabled theme."; + $this->expectException(ExtensionNameReservedException::class); + $this->expectExceptionMessage($message); + $this->container->get('module_installer')->install([$name]); + } + } diff --git a/core/modules/system/tests/themes/test_theme_module_name_collision/test_theme_module_name_collision.info.yml b/core/modules/system/tests/themes/test_theme_module_name_collision/test_theme_module_name_collision.info.yml new file mode 100644 index 0000000000..e08568bd28 --- /dev/null +++ b/core/modules/system/tests/themes/test_theme_module_name_collision/test_theme_module_name_collision.info.yml @@ -0,0 +1,5 @@ +name: 'Same Name' +type: theme +description: 'Test module with same name as a theme.' +base theme: stable +version: VERSION diff --git a/core/tests/Drupal/KernelTests/Core/Theme/ThemeInstallerTest.php b/core/tests/Drupal/KernelTests/Core/Theme/ThemeInstallerTest.php index 5f92213c30..d0582507f4 100644 --- a/core/tests/Drupal/KernelTests/Core/Theme/ThemeInstallerTest.php +++ b/core/tests/Drupal/KernelTests/Core/Theme/ThemeInstallerTest.php @@ -4,6 +4,7 @@ use Drupal\Core\DependencyInjection\ContainerBuilder; use Drupal\Core\Extension\ExtensionNameLengthException; +use Drupal\Core\Extension\ExtensionNameReservedException; use Drupal\Core\Extension\MissingDependencyException; use Drupal\Core\Extension\ModuleUninstallValidatorException; use Drupal\Core\Extension\Exception\UnknownExtensionException; @@ -139,6 +140,24 @@ public function testInstallNameTooLong() { } } + /** + * Tests installing a theme with same name as enabled module.. + */ + public function testInstallThemeSameNameAsModule() { + $name = 'test_theme_module_name_collision'; + + // Install and uninstall the theme. + $this->themeInstaller()->install([$name]); + $this->themeInstaller()->uninstall([$name]); + + // Install the module then the theme. + $this->moduleInstaller()->install([$name]); + $message = "Theme name $name is already in use by an enabled module."; + $this->expectException(ExtensionNameReservedException::class); + $this->expectExceptionMessage($message); + $this->themeInstaller()->install([$name]); + } + /** * Tests installing a theme with unmet module dependencies. *