diff --git a/core/lib/Drupal/Core/Extension/Extension.php b/core/lib/Drupal/Core/Extension/Extension.php index 0b3fc6c57976a5b7aae7329942c0dee0cd4c1cc4..17ca3fe7dc3d6fabc5aedec246aa4b4a5cf0653d 100644 --- a/core/lib/Drupal/Core/Extension/Extension.php +++ b/core/lib/Drupal/Core/Extension/Extension.php @@ -192,4 +192,21 @@ public function __wakeup() { $this->root = $container && $container->hasParameter('app.root') ? $container->getParameter('app.root') : DRUPAL_ROOT; } + /** + * Checks if an extension is marked as experimental. + * + * @return bool + * TRUE if an extension is marked as experimental, FALSE otherwise. + */ + public function isExperimental(): bool { + // Currently, this function checks for both the key/value pairs + // 'experimental: true' and 'lifecycle: experimental' to determine if an + // extension is marked as experimental. + // @todo Remove the check for 'experimental: true' as part of + // https://www.drupal.org/node/3250342 + return (isset($this->info['experimental']) && $this->info['experimental']) + || (isset($this->info[ExtensionLifecycle::LIFECYCLE_IDENTIFIER]) + && $this->info[ExtensionLifecycle::LIFECYCLE_IDENTIFIER] === ExtensionLifecycle::EXPERIMENTAL); + } + } diff --git a/core/modules/system/src/Controller/SystemController.php b/core/modules/system/src/Controller/SystemController.php index 41e351faf81b714a4daa1e63beb65714872fdf56..83b2bcce57943cbc93412b4f2e3dc08bbc6a2f6b 100644 --- a/core/modules/system/src/Controller/SystemController.php +++ b/core/modules/system/src/Controller/SystemController.php @@ -225,7 +225,6 @@ public function themesPage() { } $theme->is_default = ($theme->getName() == $theme_default); $theme->is_admin = ($theme->getName() == $admin_theme || ($theme->is_default && empty($admin_theme))); - $theme->is_experimental = isset($theme->info['experimental']) && $theme->info['experimental']; // Identify theme screenshot. $theme->screenshot = NULL; @@ -330,7 +329,7 @@ public function themesPage() { 'attributes' => ['title' => $this->t('Set @theme as default theme', ['@theme' => $theme->info['name']])], ]; } - $admin_theme_options[$theme->getName()] = $theme->info['name'] . ($theme->is_experimental ? ' (' . t('Experimental') . ')' : ''); + $admin_theme_options[$theme->getName()] = $theme->info['name'] . ($theme->isExperimental() ? ' (' . t('Experimental') . ')' : ''); } else { $theme->operations[] = [ @@ -357,7 +356,7 @@ public function themesPage() { if ($theme->is_admin) { $theme->notes[] = $this->t('administration theme'); } - if ($theme->is_experimental) { + if ($theme->isExperimental()) { $theme->notes[] = $this->t('experimental theme'); } diff --git a/core/modules/system/src/Controller/ThemeController.php b/core/modules/system/src/Controller/ThemeController.php index 1f3e61865cb8f66dc298f6957f3e9880d0e6183f..21fae3ccc5a346bec753ec85a9c15bf203179094 100644 --- a/core/modules/system/src/Controller/ThemeController.php +++ b/core/modules/system/src/Controller/ThemeController.php @@ -187,7 +187,7 @@ protected function willInstallExperimentalTheme($theme) { $themes_to_enable = array_merge([$theme], $dependencies); foreach ($themes_to_enable as $name) { - if (!empty($all_themes[$name]->info['experimental']) && $all_themes[$name]->status === 0) { + if (isset($all_themes[$name]) && $all_themes[$name]->isExperimental() && $all_themes[$name]->status === 0) { return TRUE; } } diff --git a/core/modules/system/src/Form/ThemeExperimentalConfirmForm.php b/core/modules/system/src/Form/ThemeExperimentalConfirmForm.php index c9c9937751613dffcb36314a75ce9ab47b944e31..9982ac76237781f7cc89fdc748de303bc0e2728e 100644 --- a/core/modules/system/src/Form/ThemeExperimentalConfirmForm.php +++ b/core/modules/system/src/Form/ThemeExperimentalConfirmForm.php @@ -104,7 +104,7 @@ public function buildForm(array $form, FormStateInterface $form_state) { $dependencies = array_keys($all_themes[$theme]->requires); $themes = array_merge([$theme], $dependencies); $is_experimental = function ($theme) use ($all_themes) { - return isset($all_themes[$theme]) && isset($all_themes[$theme]->info['experimental']) && $all_themes[$theme]->info['experimental']; + return isset($all_themes[$theme]) && $all_themes[$theme]->isExperimental(); }; $get_label = function ($theme) use ($all_themes) { return $all_themes[$theme]->info['name']; diff --git a/core/modules/system/system.install b/core/modules/system/system.install index b24d7d1651aefff286c7a4860ae684dd7d80e2da..28335c75d08f377c588b739b2afb36ee1efe2b5b 100644 --- a/core/modules/system/system.install +++ b/core/modules/system/system.install @@ -33,8 +33,12 @@ function system_requirements($phase) { global $install_state; // Reset the extension lists. - \Drupal::service('extension.list.module')->reset(); - \Drupal::service('extension.list.theme')->reset(); + /** @var \Drupal\Core\Extension\ModuleExtensionList $module_extension_list */ + $module_extension_list = \Drupal::service('extension.list.module'); + $module_extension_list->reset(); + /** @var \Drupal\Core\Extension\ThemeExtensionList $theme_extension_list */ + $theme_extension_list = \Drupal::service('extension.list.theme'); + $theme_extension_list->reset(); $requirements = []; // Report Drupal version @@ -50,7 +54,7 @@ function system_requirements($phase) { // is not running the default installation profile. $profile = \Drupal::installProfile(); if ($profile != 'standard') { - $info = \Drupal::service('extension.list.module')->getExtensionInfo($profile); + $info = $module_extension_list->getExtensionInfo($profile); $requirements['install_profile'] = [ 'title' => t('Installation profile'), 'value' => t('%profile_name (%profile-%version)', [ @@ -63,15 +67,27 @@ function system_requirements($phase) { ]; } - // Warn if any experimental modules are installed. + // Gather all obsolete and experimental modules being enabled. + $obsolete_extensions = []; + $deprecated_modules = []; $experimental_modules = []; $enabled_modules = \Drupal::moduleHandler()->getModuleList(); foreach ($enabled_modules as $module => $data) { - $info = \Drupal::service('extension.list.module')->getExtensionInfo($module); - if (isset($info[ExtensionLifecycle::LIFECYCLE_IDENTIFIER]) && $info[ExtensionLifecycle::LIFECYCLE_IDENTIFIER] === ExtensionLifecycle::EXPERIMENTAL) { - $experimental_modules[$module] = $info['name']; + $info = $module_extension_list->getExtensionInfo($module); + if (isset($info[ExtensionLifecycle::LIFECYCLE_IDENTIFIER])) { + if ($info[ExtensionLifecycle::LIFECYCLE_IDENTIFIER] === ExtensionLifecycle::EXPERIMENTAL) { + $experimental_modules[$module] = $info['name']; + } + elseif ($info[ExtensionLifecycle::LIFECYCLE_IDENTIFIER] === ExtensionLifecycle::DEPRECATED) { + $deprecated_modules[] = ['name' => $info['name'], 'lifecycle_link' => $info['lifecycle_link']]; + } + elseif ($info[ExtensionLifecycle::LIFECYCLE_IDENTIFIER] === ExtensionLifecycle::OBSOLETE) { + $obsolete_extensions[$module] = ['name' => $info['name'], 'lifecycle_link' => $info['lifecycle_link']]; + } } } + + // Warn if any experimental modules are installed. if (!empty($experimental_modules)) { $requirements['experimental_modules'] = [ 'title' => t('Experimental modules enabled'), @@ -79,14 +95,48 @@ function system_requirements($phase) { 'severity' => REQUIREMENT_WARNING, ]; } - // Warn if any experimental themes are installed. + // Warn if any deprecated modules are installed. + if (!empty($deprecated_modules)) { + foreach ($deprecated_modules as $deprecated_module) { + $deprecated_modules_link_list[] = (string) Link::fromTextAndUrl($deprecated_module['name'], Url::fromUri($deprecated_module['lifecycle_link']))->toString(); + } + $requirements['deprecated_modules'] = [ + 'title' => t('Deprecated modules enabled'), + 'value' => t('Deprecated modules found: %module_list.', [ + '%module_list' => t(implode(',', $deprecated_modules_link_list)), + ]), + 'severity' => REQUIREMENT_WARNING, + ]; + } + + // Gather all obsolete and experimental themes being enabled. $experimental_themes = []; + $deprecated_themes = []; $installed_themes = \Drupal::service('theme_handler')->listInfo(); foreach ($installed_themes as $theme => $data) { + $info = $theme_extension_list->getExtensionInfo($theme); + if (isset($info[ExtensionLifecycle::LIFECYCLE_IDENTIFIER])) { + if ($info[ExtensionLifecycle::LIFECYCLE_IDENTIFIER] === ExtensionLifecycle::EXPERIMENTAL) { + $experimental_themes[$theme] = $info['name']; + } + elseif ($info[ExtensionLifecycle::LIFECYCLE_IDENTIFIER] === ExtensionLifecycle::DEPRECATED) { + $deprecated_themes[] = ['name' => $info['name'], 'lifecycle_link' => $info['lifecycle_link']]; + } + elseif ($info[ExtensionLifecycle::LIFECYCLE_IDENTIFIER] === ExtensionLifecycle::OBSOLETE) { + $obsolete_extensions[$theme] = ['name' => $info['name'], 'lifecycle_link' => $info['lifecycle_link']]; + } + } + // Currently, we check for both the key/value pairs 'experimental: true' + // and 'lifecycle: experimental' to determine if an extension is marked as + // experimental. + // @todo Remove the check for 'experimental: true' as part of + // https://www.drupal.org/node/3250342 if (isset($data->info['experimental']) && $data->info['experimental']) { $experimental_themes[$theme] = $data->info['name']; } } + + // Warn if any experimental themes are enabled. if (!empty($experimental_themes)) { $requirements['experimental_themes'] = [ 'title' => t('Experimental themes enabled'), @@ -94,6 +144,35 @@ function system_requirements($phase) { 'severity' => REQUIREMENT_WARNING, ]; } + + // Warn if any deprecated themes are enabled. + if (!empty($deprecated_themes)) { + foreach ($deprecated_themes as $deprecated_theme) { + $deprecated_themes_link_list[] = (string) Link::fromTextAndUrl($deprecated_theme['name'], Url::fromUri($deprecated_theme['lifecycle_link']))->toString(); + + } + $requirements['deprecated_themes'] = [ + 'title' => t('Deprecated themes enabled'), + 'value' => t('Deprecated themes found: %theme_list.', [ + '%theme_list' => t(implode(',', $deprecated_themes_link_list)), + ]), + 'severity' => REQUIREMENT_WARNING, + ]; + } + + // Warn if any obsolete extensions (themes or modules) are enabled. + if (!empty($obsolete_extensions)) { + foreach ($obsolete_extensions as $obsolete_extension) { + $obsolete_extensions_link_list[] = (string) Link::fromTextAndUrl($obsolete_extension['name'], Url::fromUri($obsolete_extension['lifecycle_link']))->toString(); + } + $requirements['obsolete_extensions'] = [ + 'title' => t('Obsolete extensions enabled'), + 'value' => t('Obsolete extensions found: %extensions. Obsolete extensions are provided only so that they can be uninstalled cleanly. You should immediately uninstall these extensions since they may be removed in a future release.', [ + '%extension_list' => t(implode(', ', $obsolete_extensions_link_list)), + ]), + 'severity' => REQUIREMENT_WARNING, + ]; + } _system_advisories_requirements($requirements); } @@ -931,8 +1010,8 @@ function system_requirements($phase) { ]; }; $profile = \Drupal::installProfile(); - $files = \Drupal::service('extension.list.module')->getList(); - $files += \Drupal::service('extension.list.theme')->getList(); + $files = $module_extension_list->getList(); + $files += $theme_extension_list->getList(); $core_incompatible_extensions = []; $php_incompatible_extensions = []; foreach ($files as $extension_name => $file) { @@ -1048,10 +1127,8 @@ function system_requirements($phase) { // Look for invalid modules. $extension_config = \Drupal::configFactory()->get('core.extension'); - /** @var \Drupal\Core\Extension\ExtensionList $extension_list */ - $extension_list = \Drupal::service('extension.list.module'); - $is_missing_extension = function ($extension_name) use (&$extension_list) { - return !$extension_list->exists($extension_name); + $is_missing_extension = function ($extension_name) use (&$module_extension_list) { + return !$module_extension_list->exists($extension_name); }; $invalid_modules = array_filter(array_keys($extension_config->get('module')), $is_missing_extension); @@ -1073,8 +1150,10 @@ function system_requirements($phase) { } // Look for invalid themes. - $extension_list = \Drupal::service('extension.list.theme'); - $invalid_themes = array_filter(array_keys($extension_config->get('theme')), $is_missing_extension); + $is_missing_theme = function ($extension_name) use (&$theme_extension_list) { + return !$theme_extension_list->exists($extension_name); + }; + $invalid_themes = array_filter(array_keys($extension_config->get('theme')), $is_missing_theme); if (!empty($invalid_themes)) { $requirements['invalid_theme'] = $create_extension_incompatibility_list( $invalid_themes, @@ -1283,7 +1362,7 @@ function system_requirements($phase) { if ($last_removed && $last_removed > $update_registry->getInstalledVersion($module)) { /** @var \Drupal\Core\Extension\Extension $module_info */ - $module_info = \Drupal::service('extension.list.module')->get($module); + $module_info = $module_extension_list->get($module); $module_list[$module] = [ 'name' => $module_info->info['name'], 'last_removed' => $last_removed, @@ -1327,7 +1406,6 @@ function system_requirements($phase) { $existing_updates = \Drupal::service('keyvalue')->get('post_update')->get('existing_updates', []); $post_update_registry = \Drupal::service('update.post_update_registry'); $modules = \Drupal::moduleHandler()->getModuleList(); - $module_extension_list = \Drupal::service('extension.list.module'); foreach ($modules as $module => $extension) { $module_info = $module_extension_list->get($module); $removed_post_updates = $post_update_registry->getRemovedPostUpdates($module); diff --git a/core/modules/system/tests/src/Functional/System/StatusTest.php b/core/modules/system/tests/src/Functional/System/StatusTest.php index f2d9fb479eb2f85cf655f8c5da499138fb6384a0..3eb7ee334dcab57032d576371b83ff0e35de92cb 100644 --- a/core/modules/system/tests/src/Functional/System/StatusTest.php +++ b/core/modules/system/tests/src/Functional/System/StatusTest.php @@ -99,6 +99,45 @@ public function testStatusPage() { ]); $this->assertCount(1, $elements); $this->assertStringStartsWith('Available', $elements[0]->getParent()->getText()); + + // Test the page with deprecated extensions. + $module_installer = \Drupal::service('module_installer'); + $session = $this->assertSession(); + + // Install a deprecated module. + $module_installer->install(['deprecated_module']); + $this->drupalGet('admin/reports/status'); + + // Confirm warning messages are displayed for the deprecated module. + $session->pageTextContains('Deprecated modules enabled'); + $session->pageTextContains('Deprecated modules found: Deprecated module.'); + + // Check that the deprecated module link was rendered correctly. + $this->assertSession()->elementExists('xpath', "//a[contains(@href, 'http://example.com/deprecated')]"); + + // Uninstall a deprecated module and confirm the warning is not displayed. + $module_installer->uninstall(['deprecated_module']); + $this->drupalGet('admin/reports/status'); + $session->pageTextNotContains('Deprecated modules enabled'); + $session->pageTextNotContains('Deprecated modules found: Deprecated module.'); + $this->assertSession()->elementNotExists('xpath', "//a[contains(@href, 'http://example.com/deprecated')]"); + + // Install deprecated theme and confirm warning message is displayed. + $theme_installer = \Drupal::service('theme_installer'); + $theme_installer->install(['test_deprecated_theme']); + $this->drupalGet('admin/reports/status'); + $session->pageTextContains('Deprecated themes enabled'); + $session->pageTextContains('Deprecated themes found: Test deprecated theme.'); + + // Check that the deprecated theme link was rendered correctly. + $this->assertSession()->elementExists('xpath', "//a[contains(@href, 'http://example.com/deprecated_theme')]"); + + // Uninstall a deprecated theme and confirm the warning is not displayed. + $theme_installer->uninstall(['test_deprecated_theme']); + $this->drupalGet('admin/reports/status'); + $session->pageTextNotContains('Deprecated themes enabled'); + $session->pageTextNotContains('Deprecated themes found: Test deprecated theme.'); + $this->assertSession()->elementNotExists('xpath', "//a[contains(@href, 'http://example.com/deprecated_theme')]"); } } diff --git a/core/modules/system/tests/src/Functional/Theme/ExperimentalThemeTest.php b/core/modules/system/tests/src/Functional/Theme/ExperimentalThemeTest.php index 58a6e3933efb8811e0635d2676544a0db0759e1e..d3cee2a02d07875258ed118e2c4c921a43b55440 100644 --- a/core/modules/system/tests/src/Functional/Theme/ExperimentalThemeTest.php +++ b/core/modules/system/tests/src/Functional/Theme/ExperimentalThemeTest.php @@ -5,7 +5,7 @@ use Drupal\Tests\BrowserTestBase; /** - * Tests the installation of themes. + * Tests the installation of experimental themes. * * @group Theme */ @@ -37,16 +37,17 @@ protected function setUp(): void { /** * Tests installing experimental themes and dependencies in the UI. + * + * @dataProvider providerTestExperimentalConfirmForm */ - public function testExperimentalConfirmForm() { + public function testExperimentalConfirmForm(string $theme_name, string $dependency_theme_name, string $machine_theme_name, string $machine_dependency_theme_name): void { // Only experimental themes should be marked as such with a parenthetical. $this->drupalGet('admin/appearance'); - $this->assertSession()->responseContains(sprintf('Experimental test %s (experimental theme)', \Drupal::VERSION)); - $this->assertSession()->responseContains(sprintf('Experimental dependency test %s', \Drupal::VERSION)); + $this->assertSession()->responseContains(sprintf($theme_name . ' %s (experimental theme)', \Drupal::VERSION)); + $this->assertSession()->responseContains(sprintf($dependency_theme_name . ' %s', \Drupal::VERSION)); // First, test installing a non-experimental theme with no dependencies. // There should be no confirmation form and no experimental theme warning. - $this->drupalGet('admin/appearance'); $this->cssSelect('a[title="Install <strong>Test theme</strong> theme"]')[0]->click(); $this->assertSession()->pageTextContains('The <strong>Test theme</strong> theme has been installed.'); $this->assertSession()->pageTextNotContains('Experimental modules are provided for testing purposes only.'); @@ -55,12 +56,12 @@ public function testExperimentalConfirmForm() { // There should be a confirmation form with an experimental warning, but no // list of dependencies. $this->drupalGet('admin/appearance'); - $this->cssSelect('a[title="Install Experimental test theme"]')[0]->click(); + $this->cssSelect('a[title="Install ' . $theme_name . ' theme"]')[0]->click(); $this->assertSession()->pageTextContains('Experimental themes are provided for testing purposes only. Use at your own risk.'); // The module should not be enabled and there should be a warning and a // list of the experimental modules with only this one. - $this->assertSession()->pageTextNotContains('The Experimental Test theme has been installed.'); + $this->assertSession()->pageTextNotContains('The ' . $theme_name . ' theme has been installed.'); $this->assertSession()->pageTextContains('Experimental themes are provided for testing purposes only.'); // There should be no message about enabling dependencies. @@ -68,69 +69,104 @@ public function testExperimentalConfirmForm() { // Enable the theme and confirm that it worked. $this->submitForm([], 'Continue'); - $this->assertSession()->pageTextContains('The Experimental test theme has been installed.'); + $this->assertSession()->pageTextContains('The ' . $theme_name . ' theme has been installed.'); // Setting it as the default should not ask for another confirmation. - $this->cssSelect('a[title="Set Experimental test as default theme"]')[0]->click(); + $this->cssSelect('a[title="Set ' . $theme_name . ' as default theme"]')[0]->click(); $this->assertSession()->pageTextNotContains('Experimental themes are provided for testing purposes only. Use at your own risk.'); - $this->assertSession()->pageTextContains('Experimental test is now the default theme.'); - $this->assertSession()->pageTextNotContains(sprintf('Experimental test %s (experimental theme)', \Drupal::VERSION)); - $this->assertSession()->responseContains(sprintf('Experimental test %s (default theme, administration theme, experimental theme)', \Drupal::VERSION)); + $this->assertSession()->pageTextContains($theme_name . ' is now the default theme.'); + $this->assertSession()->pageTextNotContains(sprintf($theme_name . ' %s (experimental theme)', \Drupal::VERSION)); + $this->assertSession()->responseContains(sprintf($theme_name . ' %s (default theme, administration theme, experimental theme)', \Drupal::VERSION)); // Uninstall the theme. $this->config('system.theme')->set('default', 'test_theme')->save(); \Drupal::service('theme_handler')->refreshInfo(); - \Drupal::service('theme_installer')->uninstall(['experimental_theme_test']); + \Drupal::service('theme_installer')->uninstall([$machine_theme_name]); // Reinstall the same experimental theme, but this time immediately set it // as the default. This should again trigger a confirmation form with an // experimental warning. $this->drupalGet('admin/appearance'); - $this->cssSelect('a[title="Install Experimental test as default theme"]')[0]->click(); + $this->cssSelect('a[title="Install ' . $theme_name . ' as default theme"]')[0]->click(); $this->assertSession()->pageTextContains('Experimental themes are provided for testing purposes only. Use at your own risk.'); // Test enabling a theme that is not itself experimental, but that depends // on an experimental module. $this->drupalGet('admin/appearance'); - $this->cssSelect('a[title="Install Experimental dependency test theme"]')[0]->click(); + $this->cssSelect('a[title="Install ' . $dependency_theme_name . ' theme"]')[0]->click(); // The theme should not be enabled and there should be a warning and a // list of the experimental modules with only this one. - $this->assertSession()->pageTextNotContains('The Experimental dependency test theme has been installed.'); + $this->assertSession()->pageTextNotContains('The ' . $dependency_theme_name . ' theme has been installed.'); $this->assertSession()->pageTextContains('Experimental themes are provided for testing purposes only. Use at your own risk.'); - $this->assertSession()->pageTextContains('The following themes are experimental: Experimental test'); + $this->assertSession()->pageTextContains('The following themes are experimental: ' . $theme_name); // Ensure the non-experimental theme is not listed as experimental. - $this->assertSession()->pageTextNotContains('The following themes are experimental: Experimental test, Experimental dependency test'); - $this->assertSession()->pageTextNotContains('The following themes are experimental: Experimental dependency test'); + $this->assertSession()->pageTextNotContains('The following themes are experimental: ' . $theme_name . ', ' . $dependency_theme_name); + $this->assertSession()->pageTextNotContains('The following themes are experimental: ' . $dependency_theme_name); // There should be a message about enabling dependencies. - $this->assertSession()->pageTextContains('You must enable the Experimental test theme to install Experimental dependency test'); + $this->assertSession()->pageTextContains('You must enable the ' . $theme_name . ' theme to install ' . $dependency_theme_name); // Enable the theme and confirm that it worked. $this->submitForm([], 'Continue'); - $this->assertSession()->pageTextContains('The Experimental dependency test theme has been installed.'); - $this->assertSession()->responseContains(sprintf('Experimental test %s (experimental theme)', \Drupal::VERSION)); - $this->assertSession()->responseContains(sprintf('Experimental dependency test %s', \Drupal::VERSION)); + $this->assertSession()->pageTextContains('The ' . $dependency_theme_name . ' theme has been installed.'); + $this->assertSession()->responseContains(sprintf($theme_name . ' %s (experimental theme)', \Drupal::VERSION)); + $this->assertSession()->responseContains(sprintf($dependency_theme_name . ' %s', \Drupal::VERSION)); // Setting it as the default should not ask for another confirmation. - $this->cssSelect('a[title="Set Experimental dependency test as default theme"]')[0]->click(); + $this->cssSelect('a[title="Set ' . $dependency_theme_name . ' as default theme"]')[0]->click(); $this->assertSession()->pageTextNotContains('Experimental themes are provided for testing purposes only. Use at your own risk.'); - $this->assertSession()->pageTextContains('Experimental dependency test is now the default theme.'); - $this->assertSession()->responseContains(sprintf('Experimental test %s (experimental theme)', \Drupal::VERSION)); - $this->assertSession()->responseContains(sprintf('Experimental dependency test %s (default theme, administration theme)', \Drupal::VERSION)); + $this->assertSession()->pageTextContains($dependency_theme_name . ' is now the default theme.'); + $this->assertSession()->responseContains(sprintf($theme_name . ' %s (experimental theme)', \Drupal::VERSION)); + $this->assertSession()->responseContains(sprintf($dependency_theme_name . ' %s (default theme, administration theme)', \Drupal::VERSION)); // Uninstall the theme. $this->config('system.theme')->set('default', 'test_theme')->save(); \Drupal::service('theme_handler')->refreshInfo(); - \Drupal::service('theme_installer')->uninstall(['experimental_theme_test', 'experimental_theme_dependency_test']); + \Drupal::service('theme_installer')->uninstall( + [$machine_theme_name, $machine_dependency_theme_name] + ); // Reinstall the same theme, but this time immediately set it as the // default. This should again trigger a confirmation form with an // experimental warning for its dependency. $this->drupalGet('admin/appearance'); - $this->cssSelect('a[title="Install Experimental dependency test as default theme"]')[0]->click(); + $this->cssSelect('a[title="Install ' . $dependency_theme_name . ' as default theme"]')[0]->click(); $this->assertSession()->pageTextContains('Experimental themes are provided for testing purposes only. Use at your own risk.'); } + /** + * Data provider for experimental test themes. + * + * @return string[][] + * An array with four items: + * - The theme name. + * - The dependency theme name. + * - The machine theme name. + * - The machine dependency theme name. + * + * @todo Turn the check for 'Testing legacy Key/Value pair + * "experimental: true"' into a @legacy test triggering a deprecation as part + * of https://www.drupal.org/node/3250342 + */ + public function providerTestExperimentalConfirmForm(): array { + return [ + 'Testing Key/Value pair "lifecycle: experimental"' => + [ + 'Experimental test', + 'Experimental dependency test', + 'experimental_theme_test', + 'experimental_theme_dependency_test', + ], + 'Testing legacy Key/Value pair "experimental: true"' => + [ + 'Legacy experimental test', + 'Legacy experimental dependency test', + 'legacy_experimental_theme_test', + 'legacy_experimental_theme_dependency_test', + ], + ]; + } + } diff --git a/core/modules/system/tests/themes/experimental_theme_test/experimental_theme_test.info.yml b/core/modules/system/tests/themes/experimental_theme_test/experimental_theme_test.info.yml index a2f4463902fccf852683df6371d5de452a2d832d..587ceb13aa238d408e5f8a1ee12631fc67d73d82 100644 --- a/core/modules/system/tests/themes/experimental_theme_test/experimental_theme_test.info.yml +++ b/core/modules/system/tests/themes/experimental_theme_test/experimental_theme_test.info.yml @@ -3,4 +3,4 @@ type: theme description: 'Experimental test theme.' version: VERSION base theme: false -experimental: true +lifecycle: experimental diff --git a/core/modules/system/tests/themes/legacy_experimental_theme_dependency_test/legacy_experimental_theme_dependency_test.info.yml b/core/modules/system/tests/themes/legacy_experimental_theme_dependency_test/legacy_experimental_theme_dependency_test.info.yml new file mode 100644 index 0000000000000000000000000000000000000000..dc6173c130af429a7ccad8d83907eb2cf9b2ccea --- /dev/null +++ b/core/modules/system/tests/themes/legacy_experimental_theme_dependency_test/legacy_experimental_theme_dependency_test.info.yml @@ -0,0 +1,5 @@ +name: 'Legacy experimental dependency test' +type: theme +description: 'Legacy experimental dependency test theme.' +version: VERSION +base theme: legacy_experimental_theme_test diff --git a/core/modules/system/tests/themes/legacy_experimental_theme_test/legacy_experimental_theme_test.info.yml b/core/modules/system/tests/themes/legacy_experimental_theme_test/legacy_experimental_theme_test.info.yml new file mode 100644 index 0000000000000000000000000000000000000000..448d85442498284eb623080fac0c5e7c1bee2ac2 --- /dev/null +++ b/core/modules/system/tests/themes/legacy_experimental_theme_test/legacy_experimental_theme_test.info.yml @@ -0,0 +1,6 @@ +name: 'Legacy experimental test' +type: theme +description: 'Legacy experimental test theme.' +version: VERSION +base theme: false +experimental: true diff --git a/core/modules/system/tests/themes/test_deprecated_theme/test_deprecated_theme.info.yml b/core/modules/system/tests/themes/test_deprecated_theme/test_deprecated_theme.info.yml new file mode 100644 index 0000000000000000000000000000000000000000..92c3ad51ea7812f477e4b4535edf6e80317d7e4c --- /dev/null +++ b/core/modules/system/tests/themes/test_deprecated_theme/test_deprecated_theme.info.yml @@ -0,0 +1,7 @@ +name: 'Test deprecated theme' +type: theme +description: 'Support module for testing deprecated themes.' +version: VERSION +base theme: false +lifecycle: deprecated +lifecycle_link: 'http://example.com/deprecated_theme' diff --git a/core/themes/claro/claro.info.yml b/core/themes/claro/claro.info.yml index 07be922f74d4e1454de37f9486fc8c4423213e67..94c8f9b9c2f8d2883aaa15b6ae76218721ea222d 100644 --- a/core/themes/claro/claro.info.yml +++ b/core/themes/claro/claro.info.yml @@ -17,7 +17,7 @@ description: 'A clean, accessible, and powerful Drupal administration theme.' alt text: 'Screenshot of Claro, Drupal administration theme.' package: Core version: VERSION -experimental: true +lifecycle: experimental libraries: - core/drupal.message - core/normalize