Problem/Motivation

After clicking 'continue' to install an obsolete module one gets:

Steps to reproduce

Create a test module with 'lifecycle: obsolete' and install it via the UI.

Proposed resolution

An obsolete module must not be installed. So the solution is to hide any obsolete modules from the UI. See #13 for more information. Obsolete modules will not be visible at the /admin/modules "Extend" page at all. However, in case an update to uninstall such a thing either wasn't properly released, or hasn't been run, obsolete modules are still visible at the "Uninstall" form (/admin/modules/uninstall), so if a site owner needs to manually remove such a module, they still can.

Remaining tasks

  1. Patch
  2. Test
  3. Review

User interface changes

Before:
before image with obsolete module listed

After:
modules list without any obsolete modules

API changes

A new function isObsolete() added to /core/lib/Drupal/Core/Extension/Extension to validate if an extension is obsolete.

Release notes snippet

  • Extension objects now provide the isObsolete()function to check if an extension is marked obsolete.
  • Obsolete modules are no longer displayed on the "Extend" page (/admin/modules).

Comments

quietone created an issue. See original summary.

spokje’s picture

As far as I can tell this was a decision made on purpose:

Last lines from #3124762-124: Add 'lifecycle' key to .info.yml files:

So, as long as we always add an update to uninstall a module when we make it obsolete (which we have to), then I think the exception is fine here.

From the IS:

Steps to reproduce:
Create a test module with 'lifecycle: obsolete' and install it via the UI.

There is already such a test, which expects the ObsoleteExtensionException being thrown here: https://git.drupalcode.org/project/drupal/-/blob/9.4.x/core/tests/Drupal/KernelTests/Core/Extension/ModuleInstallerTest.php#L143

cilefen’s picture

So that seems works as designed. But we could change our minds about that right here if this is known to be be significant to site owners. Or, we could could close or postpone this until we have more data.

spokje’s picture

Fully agree with preventing enabling an obsolete extension through the UI, instead of a uncaught Exception.

quietone’s picture

Issue summary: View changes
StatusFileSize
new180.65 KB

Apologies, I was not clear. I have updated the IS.

murilohp’s picture

StatusFileSize
new4.35 KB

Hey, I think it's a good idea, instead of show a WSOD, it's better to display the error message. On this new patch, I tried to address this.

murilohp’s picture

Status: Active » Needs review
xjm’s picture

Priority: Normal » Major

This is major because it causes a fatal in the user interface. The intent of the obsolete module API is to allow empty stubs that don't break the site owners site with a fatal on upgrade and then uninstall themselves. So if the API is also causing an uncaught exception, it's not fulfilling its intended purpose. :)

catch’s picture

Status: Needs review » Needs work

Obsolete modules shouldn't be shown in the UI to be installed in the first place, if they are, then I think we should be fixing that instead.

quietone’s picture

Priority: Major » Normal
Status: Needs work » Needs review
Related issues: -#3220893: Add lifecycle property to info.yml +#3124762: Add 'lifecycle' key to .info.yml files

Fix the related issue link.

quietone’s picture

@catch does that mean that the obsolete module should be shown in the module list but somehow not be installable? The same would apply to themes.

catch’s picture

Status: Needs review » Needs work

@quietone: obsolete modules should be completely hidden in the UI and impossible to install, so not in the module list at all. So you shouldn't ever get in a position where you're trying to install it.

catch’s picture

I just applied the patch from #3261957: Properly deprecate migrate_drupal_multilingual for future removal and went to the extend page, and migrate_drupal_multilingual wasn't shown there - that appears to be correct, so I'm not sure how it's possible to get to that confirm form at all? Was the obsolete module a dependency of another module or something?

murilohp’s picture

StatusFileSize
new1.83 KB

Hey @catch, I was able to reproduce the error using a recent version of D9.4(so I have #3261957 fixed), I created a new obsolete contrib module, using the following data on .info.yml file:

name: 'Module example'
type: module
description: 'Obsolete.'
lifecycle: obsolete
lifecycle_link: 'https://example.com/obsolete'
package: contrib
core_version_requirement: ^8 || ^9

It is possible to hide the module on the extend page using hidden:true on .info file, but this key is not mandatory when a module is obsolete, I like the idea of removing obsoletes modules from the extend page. I created a new patch removing the obsoletes modules just from the UI, the modules will keep being discovered.

I'll keep this to needs works to see the testbot and check if it's the best solution to just hide it from the UI.

Thanks!

quietone’s picture

@murilohp, thanks for the patch.

#14. migrate_drupal_multilingual has been marked hidden since 8.9.x and it is also dependent on migrate_drupal.

I think the class comment for ModulesListForm and ModuleListNonStableConfirm need to be updated to explain that obsolete modules are excluded.

And some suggestions for the comments.

  1. +++ b/core/lib/Drupal/Core/Extension/Extension.php
    @@ -209,4 +209,18 @@ public function isExperimental(): bool {
    +    // This function checks for both the key/value pairs
    +    // 'lifecycle: obsolete' to determine if an
    +    // extension is marked as obsolete.
    

    This can be simpler.

    "This function checks for 'lifecycle: obsolete' to determine if an extension is marked as obsolete."

  2. +++ b/core/modules/system/src/Form/ModulesListForm.php
    @@ -172,6 +172,12 @@ public function buildForm(array $form, FormStateInterface $form_state) {
    +      // https://www.drupal.org/project/drupal/issues/3258782
    

    Not needed.

  3. +++ b/core/modules/system/src/Form/ModulesListForm.php
    @@ -172,6 +172,12 @@ public function buildForm(array $form, FormStateInterface $form_state) {
    +      // Remove the obsoletes modules from the UI.
    

    'Remove obsolete modules' is sufficient here.

murilohp’s picture

Status: Needs work » Needs review
StatusFileSize
new3.57 KB
new2.89 KB

Thanks for the review @quietone!

I think the class comment for ModulesListForm and ModuleListNonStableConfirm need to be updated to explain that obsolete modules are excluded.

I updated the class comment on both classes.

  1. Fixed
  2. Fixed
  3. Fixed

I also added a new test to validate if none obsolete modules are displayed on the page.

Thanks!

quietone’s picture

Status: Needs review » Needs work
Issue tags: +Bug Smash Initiative

@murilohp, yes, that is better, thanks.

  1. +++ b/core/modules/system/src/Form/ModulesListForm.php
    @@ -25,10 +25,10 @@
    + * The list of stable, experimental and deprecated modules gets populated by
    

    This block doesn't read well to me. I'd rather it say 'all modules except obsolete', much easier to grasp the intention. What about something like this?

     * The list of modules includes all modules, except obsolete modules. The list
     * is generated from the data in the info.yml file for each module, which
     * includes the module name, description and other modules it requires.
  2. +++ b/core/modules/system/src/Form/ModulesListForm.php
    @@ -25,10 +25,10 @@
    + * See \Drupal\Core\Extension\InfoParser for info on module.info.yml descriptors.
    

    This should following the coding standard for @see,

quietone’s picture

I forgot one item.

+++ b/core/modules/system/tests/src/Functional/Form/ModulesListFormWebTest.php
@@ -208,4 +208,12 @@ public function testInstalledIncompatibleModule() {
+  public function testDisplayObsoleteModules() {

Nice to add the test but there is no need to add the overhead of a new Functional test, the assertion and a comment can be added to testModuleListForm

murilohp’s picture

Status: Needs work » Needs review
StatusFileSize
new3.8 KB
new1.94 KB

Addressing #18 and #19 here, thank you @quietone for the suggested message.

quietone’s picture

Status: Needs review » Needs work

@murilohp, almost there!

+++ b/core/modules/system/src/Form/ModulesListForm.php
@@ -25,10 +25,11 @@
+ * @see \Drupal\Core\Extension\InfoParser for info on module.info.yml descriptors.

Not quite, still not following the Drupal coding standards for @see. Check the last paragraph of @see on the API documentation and comment standards page.

murilohp’s picture

Assigned: Unassigned » murilohp
murilohp’s picture

StatusFileSize
new3.76 KB
new540 bytes

My bad @quietone, I removed the sentence after the class, now I think it's good.

Thanks!

murilohp’s picture

Assigned: murilohp » Unassigned
Status: Needs work » Needs review
quietone’s picture

Title: Error when installing an obsolete module » Do not display obsolete modules at admin/modules
Status: Needs review » Needs work
Issue tags: +Needs issue summary update

That looks good to me.

The issue summary needs an update to reflect what this issue is actually doing. I have already updated the title. This also should have a fail patch. Setting to NW.

murilohp’s picture

Issue summary: View changes
Issue tags: -Needs issue summary update
StatusFileSize
new54.45 KB
new54.08 KB
murilohp’s picture

Status: Needs work » Needs review
StatusFileSize
new887 bytes

Hey, I updated the IS and here's a new test only patch! Moving back to NR.

Status: Needs review » Needs work

The last submitted patch, 28: 3258782-28-test-only.patch, failed testing. View results

murilohp’s picture

Status: Needs work » Needs review
dww’s picture

Issue summary: View changes

This mostly looks great, thanks! I cleaned up the release note and a few other things in the summary. Looking at the patch in #23:

+++ b/core/modules/system/src/Form/ModulesListForm.php
@@ -25,10 +25,11 @@
+ * includes the module name, description and other modules it requires.

If we're re-wording this message already, seems a little weird to only mention name, description and requirements, especially given the context of a function that understands the obsolete status of a module. How about:

"... which includes the module name, description, dependencies and other information."

? We don't necessarily have to say "lifecycle" but the point is the .info.yml file defines a bunch of stuff, go see how that works...

Not worth NW over this, but does anyone wanna quick re-roll and I'll RTBC?

dww’s picture

dww’s picture

StatusFileSize
new52.68 KB

While obsolete modules are supposed to be uninstalled via an upgrade hook, and while I agree we should hide them from /admin/modules, it's a little too bad that they're also gone from /admin/modules/uninstall. E.g. with an obsolete contrib module enabled that doesn't provide the right upgrade path, or where I haven't run it yet, I see this on my status report with the patch applied:

Status report warning about obsolete modules installed that you can't do anything about

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.

The broken %extensions placeholder needs a follow-up, if there's not already a bug report for that. But more to the point for this issue, there's no longer any way to do anything about this warning except use something outside of core like drush. It'd be great if the "uninstall these extensions" was a link to /admin/modules/uninstall and if all the obsolete stuff still appeared there, perhaps even at the top of the page in some high-lighted fashion.

I don't want to derail this with scope creep, but it seems like if we're hiding these, we should really provide a way to recover directly with core alone. Seems like leaving them visible at the uninstall form is a good solution, but I'm open to other ideas.

Thanks/sorry!
-Derek

dww’s picture

Priority: Normal » Major

Also, if #3265362: Do not display obsolete themes at admin/appearance is major, seems like this should be, too. It's preventing a WSOD triggered via a relatively easy path through the UI...

quietone’s picture

murilohp’s picture

Thanks for the reroll @quietone, now the #32 is addressed, regarding #33, thanks @dww for the feedback.

I created a new issue for the %extensions bug, it would be nice to see your thoughts on #3266308: %extensions placeholder not extension names printed on the Status report warning about obsolete extensions , and I agree with you, we should allow the user to uninstall the modules using the admin/modules/uninstall page, in fact this is already happening.

if all the obsolete stuff still appeared there, perhaps even at the top of the page in some high-lighted fashion

This would be nice, today the obsolete modules appears on the admin/modules/uninstall, the patch just hide the modules at the list form, but the obsolete modules should appear high-lighted on uninstall, what do you think about having another issue to address this?

daffie’s picture

Status: Needs review » Reviewed & tested by the community

All the code changes look good.
I have tested on my local machine that the changed test will fail when the fix is removed.
For me it is RTBC.

Edit: In comment #28 there is a patch that tests the same thing.

murilohp’s picture

Status: Reviewed & tested by the community » Needs review
StatusFileSize
new2.87 KB
new600 bytes

Ooops my bad I thought #35 have addressed the suggestion of #31 on my previous comment, here's a new patch addressing it.

Just some more info here, regarding #33:

it's a little too bad that they're also gone from /admin/modules/uninstall

Actually the obsoletes modules still appears on /admin/modules/uninstall, just to let things clear, I've done the following test:

  1. Create a new module test and install it, you can use the following .info.yml file:
    name: 'Test module'
    type: module
    description: 'Test module'
    package: Testing
    version: VERSION
    core_version_requirement: ^8 || ^9
      
  2. Go to /admin/modules and install the module
  3. Change the info file and add the lifecycle obsolete(lifecycle: obsolete and lifecycle_link: 'https://example.com/obsolete'
  4. Rebuild the cache
  5. Go to /admin/modules/uninstall and module will be there, and you can uninstall it.
dww’s picture

Issue summary: View changes

#35 re-roll is great since core now has Extension::isObsolete() so we don't need to re-add it here.

Re: #36 Thanks for opening #3266308: %extensions placeholder not extension names printed on the Status report warning about obsolete extensions, following now. I agree that's better to fix in a separate issue for scope management.

Re: #37: Not so fast! 😉

Re: #38: Thanks for fixing #31. Thanks also for re-testing the uninstall form. I realized I had been switching branches and sites and my local wasn't in the state I thought it was when I converted something to be obsolete. I tried again just now, being more careful and intentional. I can confirm I had done it wrong when I wrote #33, and that #38 is true. Obsolete modules do still appear at /admin/modules/uninstall. 🎉 I was a little confused with myself, since it seemed like the code here wouldn't impact the uninstall form. Now it's all clear... a little PEBCAK goes a long way. 😉

One final concern: Is there now dead code in the handling of the admin/modules form to give a special warning to obsolete modules? E.g. the stuff that generates what you see inside the red box in the "before" screenshot in the summary? Shouldn't all that dead code now be removed? I need to look more closely at the surrounding code. I spun off #3266397: Highlight non-stable modules on the admin/modules/uninstall form as another separate issue to fully address #33. That's the perfect home for any code we remove from admin/modules to handle obsolete warnings...

Thanks everyone!
-Derek

dww’s picture

Status: Needs review » Reviewed & tested by the community

core/modules/system/src/Form/ModulesListForm.php:

  protected function buildRow(array $modules, Extension $module, $distribution) {
...
    $lifecycle = $module->info[ExtensionLifecycle::LIFECYCLE_IDENTIFIER];
    $row['name']['#markup'] = $module->info['name'];
    if ($lifecycle !== ExtensionLifecycle::STABLE && !empty($module->info[ExtensionLifecycle::LIFECYCLE_LINK_IDENTIFIER])) {
      $row['name']['#markup'] .= ' ' . Link::fromTextAndUrl('(' . $this->t('@lifecycle', ['@lifecycle' => ucfirst($lifecycle)]) . ')',
          Url::fromUri($module->info[ExtensionLifecycle::LIFECYCLE_LINK_IDENTIFIER], [
            'attributes' =>
              [
                'class' => 'module-link--non-stable',
                'aria-label' => $this->t('View information on the @lifecycle status of the module @module', [
                  '@lifecycle' => ucfirst($lifecycle),
                  '@module' => $module->info['name'],
                ]),
              ],
          ])
        )->toString();
    }
...

That's not dead, even though it was handling the obsolete case. Nothing else to remove. RTBC! Sorry for not checking before I posted earlier.

Thanks!
-Derek

p.s. #3266397: Highlight non-stable modules on the admin/modules/uninstall form means we want to share the above between both forms, so we can sort out how to refactor it over there.

benjifisher’s picture

We discussed this issue at #3266551: Drupal Usability Meeting 2022-03-04. That issue will have a link to the recording, and it already has a rough transcript.

There is not a lot to say. We agree with the solution in this issue: do not show obsolete modules on /admin/modules.

As I write that, I wonder about /admin/appearance. Oh, #3265362: Do not display obsolete themes at admin/appearance is a sibling issue. I will add it as a related issue, too.

For the record, the participants at the usability meeting were

benjifisher, rkoller, AaronMcHale, andregp, Antoniya, ckrina, guilherme-rabelo, guilherme-rabelo, kimberlly_amaral, victoria-marina, shaal, tmaiochi, worldlinemine

  • catch committed 8777a76 on 10.0.x
    Issue #3258782 by murilohp, quietone, dww, catch, Spokje, xjm, daffie,...

  • catch committed f3fea02 on 9.4.x
    Issue #3258782 by murilohp, quietone, dww, catch, Spokje, xjm, daffie,...

catch credited AaronMcHale.

catch credited Antoniya.

catch credited andregp.

catch credited ckrina.

catch credited rkoller.

catch’s picture

Status: Reviewed & tested by the community » Fixed

One thing that's not mentioned here is the uninstall page. However, I think it's OK to still show obsolete modules on the uninstall page, since if you do get into a situation where one is enabled, you'd be able to uninstall it from the UI, otherwise you'd be stuck except for drush.

Committed/pushed to 10.0.x and cherry-picked to 9.4.x, thanks!

dww’s picture

Thanks for committing this, @catch!

One thing that's not mentioned here is the uninstall page.

Not in the summary, but I mentioned uninstall page from #33 on, and we’ve got #3266397: Highlight non-stable modules on the admin/modules/uninstall form for improvements there...

Thanks again,
-Derek

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.