Problem/Motivation

SystemAdminThemePreprocess::preprocessSystemModulesDetails() uses the following code.

    // Identify modules that are depended on by themes.
    // Added here instead of ModuleHandler to avoid recursion.
    $themes = $this->themeExtensionList->getList();
    foreach ($themes as $theme) {
      foreach ($theme->info['dependencies'] as $dependency) {
        if (isset($form[$dependency])) {
          // 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']]);
        }
      }
    }

That code would output, in the Required by list and when the theme is enabled, the literal string @theme, not the theme name, because the first argument passed to $this->t() contains only the @theme placeholder, for which the array passed as second argument does not provide any value.
When the theme is disabled, the theme name is correctly shown.

This bug has been introduced in #474684: Allow themes to declare dependencies on modules, for which a new commit has been done in Drupal 9.0.x and Drupal 8.9.x. It is only evident for themes that depends on modules. Drupal core themes do not depend on modules, but some contributed themes could require some modules.

Proposed resolution

Change the quoted code to the following one.

    // Identify modules that are depended on by themes.
    // Added here instead of ModuleHandler to avoid recursion.
    $themes = $this->themeExtensionList->getList();
    foreach ($themes as $theme) {
      foreach ($theme->info['dependencies'] as $dependency) {
        if (isset($form[$dependency])) {
          // 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']]);
        }
      }
    }

Issue fork drupal-3586099

Command icon Show commands

Start within a Git clone of the project using the version control instructions.

Or, if you do not have SSH keys set up on git.drupalcode.org:

Comments

avpaderno created an issue. See original summary.

avpaderno’s picture

Issue summary: View changes

avpaderno’s picture

Status: Active » Needs review
avpaderno’s picture

Component: theme system » system.module
avpaderno’s picture

Issue summary: View changes
avpaderno’s picture

I could have used the following code, but I decided it was a minimal optimization.

if (isset($form[$dependency])) {
  $args = ['@theme' => $theme->info['name']];
  // Add themes to the module's required by list.
  $form[$dependency]['#required_by'][] = $theme->status ? $this->t('@theme (theme)', $args) : $this->t('@theme (theme) (<span class="admin-disabled">disabled</span>)', $args);
}

It should avoid that '@theme' and '@theme (theme)' are exchanged again, but I do not expect that to happen again.

avpaderno’s picture

The test failure (ConfigInstallProfileOverrideTest.php, line 141) does not seem caused by this change.

    // Installing dblog creates the optional configuration.
    $this->container->get('module_installer')->install(['dblog']);
    $this->rebuildContainer();
    // Line 141
    $this->assertEquals('Override', $config_test_storage->load('override_unmet')->label(), 'The optional config_test entity is overridden by the profile optional configuration and is installed when its dependencies are met.');
    $config_test_new = $config_test_storage->load('completely_new');
    $this->assertEquals('Completely new optional configuration', $config_test_new->label(), 'The optional config_test entity is provided by the profile optional configuration and is installed when its dependencies are met.');
    $config_test_new->delete();
smustgrave’s picture

Status: Needs review » Needs work

Can we update the summary where this was inverted to see if any discussion was had for it.

avpaderno’s picture

Issue summary: View changes
Status: Needs work » Needs review

This is not an intentional change, and it introduces an error. What would be shown as dependency is not the theme name, but the literal string '@theme' because '@theme' is not one of keys in the array passed as second argument (which in fact is ['@theme (theme)' => $theme->info['name']]).
The code I quoted is for themes that depend on modules. Drupal core themes do not depend on modules, so this bug is not evident for them, but it will appear when a contributed theme depends on a module (which does not probably happen so often).

Knowing in which issue this change has been done could serve to know if the same change has been done in more files, in the same commit. For that, it is sufficient to use git grep --fixed-string, which would retrieve similar typos done in other commits too.

smustgrave’s picture

I’ll leave this for others then

avpaderno’s picture

The following code has been added in #474684: Allow themes to declare dependencies on modules.

 // Identify modules that are depended on by themes.
 // Added here instead of ModuleHandler to avoid recursion.
 $themes = \Drupal::service('extension.list.theme')->getList();
 foreach ($themes as $theme) {
   foreach ($theme->info['dependencies'] as $dependency) {
     if (isset($form[$dependency])) {
       // Add themes to the module's required by list.
       $form[$dependency]['#required_by'][] = $theme->status ? t('@theme', ['@theme (theme)' => $theme->info['name']]) : t('@theme (theme) (<span class="admin-disabled">disabled</span>)', ['@theme' => $theme->info['name']]);
     }
   }
 }

None of the comments in that issue says that passing '@theme' as first argument and ['@theme (theme)' => $theme->info['name']] as second argument was intentional. (It should not be intentional, since that would output @theme as literal string, not the theme name.)

avpaderno’s picture

Title: SystemAdminThemePreprocess::preprocessSystemModulesDetails() uses the wrong placeholder for a translatable string » SystemAdminThemePreprocess::preprocessSystemModulesDetails() shows @theme instead of the theme name that depends on a module
Issue summary: View changes

I made the title and the issue summary more explicit about what the bug is. I also added in the issue summary what I discovered about the issue that added the quoted code.

smustgrave’s picture

Status: Needs review » Reviewed & tested by the community
Issue tags: +Bug Smash Initiative

Nice find, change looks good to me.