Verified Commit c2dc9ad5 authored by Andrei Mateescu's avatar Andrei Mateescu
Browse files

fix: #3614602 Modules required by an active theme can be uninstalled

By: anybody
By: longwave
By: catch
(cherry picked from commit 3c5ba5ef)
parent 2df7b188
Loading
Loading
Loading
Loading
Loading
+5 −1
Changes for core/lib/Drupal/Core/Extension/ModuleRequiredByThemesUninstallValidator.php: 5 added lines, 1 removed line.
Original line number Diff line number Diff line
@@ -96,9 +96,13 @@ public function validateConfigImport(string $module, StorageInterface $source_st
  protected function getThemesDependingOnModule($module) {
    $installed_themes = $this->themeExtensionList->getAllInstalledInfo();
    $themes_depending_on_module = array_map(function ($theme) use ($module) {
      if (in_array($module, $theme['dependencies'])) {
      foreach ($theme['dependencies'] as $dependency) {
        // A dependency may be prefixed with its project, for example
        // 'drupal:node'. Compare only the dependency name.
        if (Dependency::createFromString($dependency)->getName() === $module) {
          return $theme['name'];
        }
      }
    }, $installed_themes);

    return array_filter($themes_depending_on_module);
+6 −2
Changes for core/modules/system/src/Theme/SystemAdminThemePreprocess.php: 6 added lines, 2 removed lines.
Original line number Diff line number Diff line
@@ -4,6 +4,7 @@

use Drupal\Component\Utility\Html;
use Drupal\Component\Utility\Xss;
use Drupal\Core\Extension\Dependency;
use Drupal\Core\Extension\ModuleExtensionList;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Extension\ThemeExtensionList;
@@ -160,9 +161,12 @@ public function preprocessSystemModulesDetails(array &$variables): void {
    $themes = $this->themeExtensionList->getList();
    foreach ($themes as $theme) {
      foreach ($theme->info['dependencies'] as $dependency) {
        if (isset($form[$dependency])) {
        // A dependency may be prefixed with its project, for example
        // 'drupal:node'. Compare only the dependency name.
        $dependency_name = Dependency::createFromString($dependency)->getName();
        if (isset($form[$dependency_name])) {
          // Add themes to the module's required by list.
          $form[$dependency]['#required_by'][] = $theme->status ? $this->t('@theme (theme)', ['@theme' => $theme->info['name']]) : $this->t('@theme (theme) (<span class="admin-disabled">disabled</span>)', ['@theme' => $theme->info['name']]);
          $form[$dependency_name]['#required_by'][] = $theme->status ? $this->t('@theme (theme)', ['@theme' => $theme->info['name']]) : $this->t('@theme (theme) (<span class="admin-disabled">disabled</span>)', ['@theme' => $theme->info['name']]);
        }
      }
    }
+36 −0
Changes for core/tests/Drupal/Tests/Core/Extension/ModuleRequiredByThemesUninstallValidatorTest.php: 36 added lines, 0 removed lines.
Original line number Diff line number Diff line
@@ -156,4 +156,40 @@ public function testValidateTwoThemeDependencies(): void {
    $this->assertEquals($expected, $reasons);
  }

  /**
   * Tests theme dependencies with a project prefix.
   *
   * Theme dependencies may be declared with a project prefix, for example
   * 'drupal:node', and must still be matched to the module.
   */
  public function testValidateOneThemeDependencyWithProjectPrefix(): void {
    $module = 'single_module';
    $theme_name = 'One Theme';
    $this->themeExtensionList->getAllInstalledInfo()->willReturn([
      'stark' => [
        'name' => 'Stark',
        'dependencies' => [],
      ],
      'claro' => [
        'name' => 'Claro',
        'dependencies' => [],
      ],
      'one_theme' => [
        'name' => $theme_name,
        'dependencies' => [
          'drupal:' . $module,
        ],
      ],
    ]);

    $this->moduleExtensionList->get($module)->willReturn((object) [
      'info' => [
        'name' => 'Single Module',
      ],
    ]);

    $reasons = $this->moduleRequiredByThemeUninstallValidator->validate($module);
    $this->assertEquals(["Required by the theme: $theme_name"], $reasons);
  }

}