Closed (fixed)
Project:
Pagedesigner
Version:
4.2.9
Component:
Code
Priority:
Major
Category:
Bug report
Assigned:
Reporter:
Created:
8 Sep 2026 at 16:18 UTC
Updated:
9 Sep 2026 at 14:22 UTC
Jump to comment: Most recent
All pagedesigner content (revisions, ...) get deleted with no prior confirmation
Have pagedesigner 4.2.9 installed
Visit admin/modules/uninstall
Prevent that the uninstall steps run prior to uninstalling the module
Prevent that the uninstall steps run prior to uninstalling the module
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
Comment #2
guillaumepacilly commentedComment #3
guillaumepacilly commentedI dug into this. Root cause:
PagedesignerUninstallValidator::validate()(added in #3608578) calls_pagedesigner_prepare_uninstall()directly — which truncates every Pagedesigner element table and deletes the module's field storages — as part of aModuleUninstallValidatorInterface::validate()call.The problem is that validate() isn't only invoked during an actual, confirmed uninstall. Drupal core's
ModulesUninstallForm::buildForm()calls ModuleInstaller::validateUninstall() for every installed, non-required module on every plain GET request to admin/modules/uninstall, just to decide which uninstall checkboxes to disable and what reasons to show. That includes Pagedesigner. So merely opening that page runs the destructive cleanup as a side effect of rendering it, with no confirmation step at all — exactly what's reported here.I verified this concretely in a Kernel test: calling
ModuleInstaller::validateUninstall(['pagedesigner'])against the old code (i.e. just what happens when the uninstall list page renders) deletes a field storage and then crashes with Call to undefined function ...drush_backend_batch_process(), because the deletion cascades into a batch operation that assumes a Drush context.Fix (pushed to this issue's branch, commit c13edb0f):
-
PagedesignerUninstallValidator::validate()is now read-only. It only checks whether Pagedesigner still owns field storages and returns a blocking reason (with a link) if so — it no longer mutates or deletes anything.- Added a new confirm form, PagedesignerPrepareUninstallForm (route
/admin/config/pagedesigner/settings/uninstall, permission "administer pagedesigner settings"), that runs the actual purge (_pagedesigner_prepare_uninstall()) only when an admin explicitly requests and confirms it.- Once purged, the uninstall checkbox unblocks and the module uninstalls normally via the existing pagedesigner_uninstall() hook.
- Added
tests/src/Kernel/Uninstall/PagedesignerUninstallValidatorTest.php, which asserts that repeatedly calling the validator (simulating repeated page views) never touches existing content/fields, and that the validator stops blocking only after an explicit purge.Comment #4
guillaumepacilly commentedComment #6
guillaumepacilly commentedComment #9
johannes.heinzerling commented+1 RTBC
I have a small nitpick for the proposed solution, that i think should not block this mr.
The new route requires administer pagedesigner settings, while /admin/modules/uninstall requires the core permission administer modules. In most cases roles will have both or none of these permission. But in the rare case that a person has "administer modules" but not "administer pagedesigner settings" the validator will render a link which will not be usable by this person. That may be wanted but i think it would make more sense to also allow the preparation of uninstalling the module to "administer modules"
Comment #10
guillaumepacilly commentedThanks Johannes, and you are right.
I already released the fix so I pushed your change to 4.x branche https://git.drupalcode.org/project/pagedesigner/-/commit/3f6cd3352e220f0...
This will be released with next update by the end of the month
Comment #11
guillaumepacilly commentedComment #13
guillaumepacilly commented