user_modules_uninistalled() is missing

agentrickard - September 9, 2008 - 14:32
Project:Drupal
Version:7.x-dev
Component:user.module
Category:bug report
Priority:critical
Assigned:Unassigned
Status:closed
Description

This is a small patch inspired by bjaspan's call for better developer experience.

The patch adds a small function module_permission_uninstall($module) that is automatically invoked when a module is uninstalled. This has the benefit of automatically cleaning the {role_permission} table when modules are uninstalled.

AttachmentSizeStatusTest resultOperations
permission_uninstall.patch1.71 KBIdleFailed: Failed to apply patch.View details | Re-test

#1

agentrickard - September 9, 2008 - 14:33

TODO: Convert to the DBTNG format for the delete.

#2

Dave Reid - September 9, 2008 - 15:09

Should this also be expanded to deleting node types, blocks, and actions (things that are automatically defined by the module and not the module.install?)

#3

agentrickard - September 9, 2008 - 15:11

Possibly, but not in this patch. Generally, things like node types are handled by the module's own implementation of hook_uninstall().

However, since permissions are handled through a universal hook_perm(), it seems natural to do this automatically instead of forcing developers to delete them with extra code.

#4

Dave Reid - September 9, 2008 - 15:15

Actually since node types are handled through hook_node_info, it would be smart to uninstall those automatically. Same with hook_block().

#5

moshe weitzman - September 9, 2008 - 16:19
Status:needs review» needs work

Good idea about perms (and blocks) ... We should only issue one delete query with an IN clause. For example, DELETE WHERE perm IN ('administer foo', 'manage foo') ...

#6

agentrickard - September 9, 2008 - 16:58

Argh! Why is there no Drupal function for creating an IN query?!?

As to node types and blocks -- separate patches for those, please. Unless we want to turn this into a function that checks all the "known" uninstall items.

#7

moshe weitzman - September 9, 2008 - 18:08

ah, but now we do. from one of your favorite functions:

<?php
   $query
= db_delete('node_access')->condition('nid', $node->nid);
    if (
$realm) {
     
$query->condition('realm', array($realm, 'all'), 'IN');
    }
   
$query->execute();
?>

#8

agentrickard - September 9, 2008 - 18:12
Status:needs work» needs review

OK, I take that last rant back a bit. The new db_query accepts arrays. Nifty.

New patch attached.

Let's open other issues for blocks and such.

AttachmentSizeStatusTest resultOperations
permissions_uninstall.patch1.75 KBIdleFailed: Failed to apply patch.View details | Re-test

#9

agentrickard - September 9, 2008 - 18:28

#10

agentrickard - September 9, 2008 - 18:34

@moshe

Cool! Here's a new version using DBTNG!

AttachmentSizeStatusTest resultOperations
permissions_uninstall.patch1.69 KBIdleFailed: Failed to apply patch.View details | Re-test

#11

Dave Reid - September 9, 2008 - 20:33

Hah! I actually submitted an issue for a new hook that would help with exactly that: #253569: DX: Add hook_modules to act on other module status changes. Would need to get that into core first.

#12

agentrickard - September 9, 2008 - 22:07

I am perfectly ok with rolling this patch into #306151: Automatically install/uninstall schema after #253569: DX: Add hook_modules to act on other module status changes lands.

In that case, this patch would go into user.module as user_module() with $op uninstall.

#13

earnie - September 10, 2008 - 19:36

subscribe

#14

agentrickard - September 27, 2008 - 23:08

New function would be:

function user_modules_uninstalled($module_list) {
  $perms = array();
  foreach ($module_list as $module) {
     // Get the module permissions.
    $perms = array_merge($perms, module_invoke($module, 'perm'));
    if (!empty($perms)) {
      $perms = array_keys($perms);
      $placeholders = db_placeholders($perms, 'text');
      db_query("DELETE FROM {role_permission} WHERE permission IN ($placeholders)", $perms);
   }
}

#15

drewish - September 29, 2008 - 22:06

#16

agentrickard - September 30, 2008 - 13:48
Status:needs review» needs work

Yes. It should. The function in #14 is dependent on that patch.

Setting to CNW.

#17

Dave Reid - October 12, 2008 - 20:13
Status:needs work» needs review

Alright! We can finally get this in now! Although we should probably get some tests written for this.

AttachmentSizeStatusTest resultOperations
hook-modules-user-D7.patch870 bytesIdleFailed: Failed to install HEAD.View details | Re-test

#18

moshe weitzman - October 12, 2008 - 20:40
Status:needs review» needs work

That issues a delete query for each module. We can do a single delete for all modules by moving the query below the foreach().

#19

agentrickard - October 12, 2008 - 22:22

Dave - see the patch in #10 for the syntax that Moshe refers to. DBTNG lets us pass arrays of deletes.

The drupal_load() is not needed either, since those modules get loaded by drupal_uninstall_modules() during the same page load cycle. So the functions are all still in memory.

#20

moshe weitzman - October 12, 2008 - 22:44

Nope, #10 isnt it. That does a query per module as well. You can do this with one big IN clause.

#21

agentrickard - October 12, 2008 - 23:31

Moshe is right. :-(

#22

drewish - October 13, 2008 - 01:27
Status:needs work» needs review

was this more what you had in mind? we should probably add a test to ensure this works as advertised.

AttachmentSizeStatusTest resultOperations
user_306027.patch1.19 KBIdleFailed: Failed to install HEAD.View details | Re-test

#23

agentrickard - October 13, 2008 - 01:48

The drupal_load() there is redundant. This hook is invoked at the end of http://api.drupal.org/api/function/drupal_uninstall_modules/7

#24

Dave Reid - October 13, 2008 - 02:01

Actually, we don't need to have the drupal_load in drupal_uninstall modules anymore. That was only used for the menu paths uninstalling, but that would depend on #320303: DX: Remove module menus on uninstall.

#25

drewish - October 13, 2008 - 02:31

well either of you could have re-rolled it without that ;) so tag now you guys get to write the tests ;)

AttachmentSizeStatusTest resultOperations
user_306027.patch1.09 KBIdleFailed: Failed to install HEAD.View details | Re-test

#26

Dave Reid - October 13, 2008 - 03:42

Sorry I've been only on to just check a few update statuses. I'm actually getting the hang at writing tests, so I'll get started on that.

But I have a question I wanted to ask everyone about these newtests for hook_modules_uninstalled: should they be done by the respective module or should we create/use a simpletest dummy module that implements one block, filter, permission, etc and be able to combine all these new tests in one test, much like the module list test in system.test. Ideas or thoughts?

#27

agentrickard - October 13, 2008 - 15:01

Well, I think we want to keep the drupal_load() in drupal_uninstall, otherwise, we have to fire it every time we write a hook_modules_uninstalled().

No opinion about testing.

#28

moshe weitzman - November 1, 2008 - 12:29

Progress, anyone?

#29

System Message - November 16, 2008 - 21:55
Status:needs review» needs work

The last submitted patch failed testing.

#30

Dave Reid - November 17, 2008 - 06:30
Status:needs work» needs review

Re-rolled. Any suggestions for which module could host the appropriate test files? Build off of the module enable/disable test?

AttachmentSizeStatusTest resultOperations
306027-user-modules-uninstalled-D7.patch867 bytesIdleFailed: Failed to run tests.View details | Re-test

#31

drewish - November 17, 2008 - 07:24

i think we could add a module.test and module_test.module to handle this. once there's an obvious place for it i think we'd be well served to add have some unit tests for module.inc.

#32

dereine - January 4, 2009 - 22:16

i added tests to this patch, not sure wether this is the right way to check it

AttachmentSizeStatusTest resultOperations
306027-user-modules-uninstalled-D7_with_test.patch2.26 KBIdleFailed: 11648 passes, 0 fails, 5 exceptionsView details | Re-test

#33

drewish - January 5, 2009 - 02:44
Status:needs review» needs work

I think the test could be a little simpler:

<?php
  
// Checks whether the permissions where removed.
 
$count = db_query("SELECT COUNT(rid) FROM {role_permission} WHERE permission = :perm", array(':perm' => 'module_test perm'))->fetchField();
 
$this->assertEqual(0, $count, t('Permissions were all removed.'));
?>

#34

drewish - January 5, 2009 - 02:45

Also all the comments should be "Proper sentences."

#35

dereine - January 5, 2009 - 09:52
Status:needs work» needs review

thx for the review!
here is a better patch, i hope :)

AttachmentSizeStatusTest resultOperations
306027-user-modules-uninstalled-D7_with_test2.patch2.3 KBIdleFailed: Failed to install HEAD.View details | Re-test

#36

drewish - January 5, 2009 - 18:10
Status:needs review» needs work

+    // Uninstalles the module_test module, so hook_modules_uninstalled is executed.

should be:
+    // Uninstalls the module_test module, so hook_modules_uninstalled() is executed.

The ModuleUninstallTest class needs a PHPDoc block.

Other than that this is looking good.

#37

dereine - January 5, 2009 - 18:21
Status:needs work» needs review

thx for taking the time to review

so there is another patch

AttachmentSizeStatusTest resultOperations
306027-user-modules-uninstalled-D7_with_test3.patch2.38 KBIdleFailed: Failed to install HEAD.View details | Re-test

#38

drewish - January 5, 2009 - 19:30

Any reason this testcase isn't in ModuleUnitTest?

#39

Dave Reid - January 5, 2009 - 19:32

Probably because the ModuleUnitTest has a different setUp requirement than the uninstall test case. Will do further review shortly.

#40

dereine - January 5, 2009 - 19:38

not really, i thought because of user module, but this is silly
because user module is loaded every time

just wonders whether the function should be under or above assertModuleList()

AttachmentSizeStatusTest resultOperations
306027-user-modules-uninstalled-D7_with_test_4_above.patch2.19 KBIdleFailed: Failed to install HEAD.View details | Re-test
306027-user-modules-uninstalled-D7_with_test_4_unter.patch2.02 KBIdleFailed: Failed to install HEAD.View details | Re-test

#41

dereine - January 5, 2009 - 19:38
Status:needs review» needs work

damn this cannot work!

#42

dereine - January 5, 2009 - 19:42
Status:needs work» needs review

sry for the 3 posts

so this time with a setup function

AttachmentSizeStatusTest resultOperations
306027-user-modules-uninstalled-D7_test_5_above.patch2.39 KBIdleFailed: Failed to install HEAD.View details | Re-test

#43

drewish - January 5, 2009 - 19:47

didn't test it but visually it's fine by me.

#44

dereine - January 5, 2009 - 20:01
Status:needs review» needs work

sry but this cannot work

$this>assertModuleList() gets all active modules, and check with expected modules, (but there are path module_test and user, and not just path)
so we have to add module_test and user to this expected values or we have to add another testclass

#45

drewish - January 5, 2009 - 20:14

then modify assertModuleList() to add module_test and user in there.

#46

Dave Reid - January 5, 2009 - 20:33
Status:needs work» needs review

You don't need to call setUp with the user module. It is automatically installed since it's a required core module.

#47

Dave Reid - January 5, 2009 - 20:37
Status:needs review» needs work

IMHO I think this should be in a separate test class, since eventually we're going to be adding more uninstall tests. The current ModuleUnitTest tests "low-level API functions" which is not what we're testing. We should have a new "class ModuleUninstallUnitTest" to fit the core test class name standards.

#48

dereine - January 5, 2009 - 20:47

i tryed to solve this, but i couldn't do it, anyone else know know how to solve it?

at least this patch reduces the fails to 4

AttachmentSizeStatusTest resultOperations
user_modules_uninstalled_with_tests_6.patch2.89 KBIdleFailed: Failed to install HEAD.View details | Re-test

#49

dereine - January 5, 2009 - 20:53
Status:needs work» needs review

#47 sounds logical for me

so i took #37 and fixed a code style problem at the beginning of the file and added a better comment

AttachmentSizeStatusTest resultOperations
user_modules_uninstalled_with_tests_7.patch2.44 KBIdleFailed: Failed to install HEAD.View details | Re-test

#50

Dave Reid - January 6, 2009 - 21:34

#51

System Message - January 8, 2009 - 01:05
Status:needs review» needs work

The last submitted patch failed testing.

#52

agentrickard - January 11, 2009 - 17:21

Attached is an updated patch, but the tests all fail because there is no 'module_test.module' file.

AttachmentSizeStatusTest resultOperations
user_modules_uninstalled_with_tests_8.patch2.55 KBIdleFailed: Failed to apply patch.View details | Re-test

#53

dereine - January 11, 2009 - 21:31
Status:needs work» needs review

arg, i created this module before, but i didn't added it to the patch file :(

AttachmentSizeStatusTest resultOperations
user_modules_uninstalled_with_tests_9.patch3.49 KBIdleFailed: Failed to apply patch.View details | Re-test

#54

agentrickard - January 12, 2009 - 15:19
Status:needs review» reviewed & tested by the community

Excellent. Having the module_test.module will help with all the related DX patches, too, since we can dump dummy hooks there.

#55

dereine - January 12, 2009 - 15:53

so what other things could be removed automatically

  • Blocks defined by hook_block_list | not sure whether this is in core from the {block} table
  • Block roles in {block_role}, same as before
  • filter in {filter}

sry for not checking them, whether they get removed

#56

agentrickard - January 12, 2009 - 16:43

#57

dereine - January 12, 2009 - 17:42

whats the best way to handle the waiting to get something like this commited?
should i wait to create the other testspatches untill this is commited?

#58

agentrickard - January 12, 2009 - 21:31

Or note the dependency in the other issues, which may get more attention on this one.

#59

Dave Reid - January 12, 2009 - 22:07
Status:reviewed & tested by the community» needs work

Patch review:

class ModuleUninstallTest extends DrupalWebTestCase
Please rename to ModuleUninstallUnitTest as per the testing naming standards.

parent::setUp('module_test', 'user');
Please exclude 'user' since it is automatically installed (it's a required core module).

We need to test to make sure the permissions are actually found in the {role_permission} table so that we know for sure the uninstall deletion works. Something like:

$count = db_query("SELECT COUNT(rid) FROM {role_permission} WHERE permission = :perm", array(':perm' => 'module_test perm'))->fetchField();
$this->assertTrue($count, t('Module permissions were found.'));

+name = "module test"
+description = "Support module for module system testing."
+package = Testing
+version = VERSION
+core = 7.x
+files[] = module_test.module
+hidden = TRUE
The module name should be "Module test"

The user_modules_uninstalled() should have a call to drupal_load('module', $module) because the functions in module_test.module will not be loaded into the function registry during uninstall since the modules have been disabled.

+  foreach ($modules as $module) {
+    drupal_load('module', $module);
+    $permissions = array_merge($permissions, array_keys(module_invoke($module, 'perm')));
+  }

#60

catch - June 27, 2009 - 14:43
Title:DX: Remove module permissions on uninstall» user_modules_uninistalled() is missing
Component:install system» user.module
Category:feature request» bug report
Priority:normal» critical
Status:needs work» needs review

This is now a critical bug since user_modules_installed() automatically adds permissions to the admin role when modules are installed, but doesn't remove them - leading to PDO errors when you uninstall then reinstall a module.

Attached patch fixes bugs in the most recent one here, and adds additional tests by pp from #503554: user_modules_uninstalled() is missing

AttachmentSizeStatusTest resultOperations
user_modules_uninstalled.patch3.35 KBIdleFailed: 11636 passes, 0 fails, 5 exceptionsView details | Re-test

#61

System Message - June 27, 2009 - 15:10
Status:needs review» needs work

The last submitted patch failed testing.

#62

catch - June 27, 2009 - 15:19
Status:needs work» needs review

Can't reproduce locally either running in the UI or via the command line. Trying once more.

#63

System Message - June 27, 2009 - 19:55
Status:needs review» needs work

The last submitted patch failed testing.

#64

catch - June 27, 2009 - 20:06
Status:needs work» needs review

Forgot the -N flag when creating the patch, whoops.

AttachmentSizeStatusTest resultOperations
user_modules_uninstalled.patch4.39 KBIdleFailed: Failed to apply patch.View details | Re-test

#65

agentrickard - June 28, 2009 - 15:19
Status:needs review» needs work

You do not need to run drupal_load() on each module. drupal_uninstall_modules() does this for you before firing the hook.

http://api.drupal.org/api/function/drupal_uninstall_modules/7

#66

catch - June 28, 2009 - 19:29
Status:needs work» needs review

doh, fixed.

AttachmentSizeStatusTest resultOperations
user_modules_uninstalled.patch4.36 KBIdleFailed: Failed to apply patch.View details | Re-test

#67

agentrickard - June 29, 2009 - 00:30

Sorry. Typos:

+ // Are the perms definced by module_test removed from {role_permission}.

+ * Implementation of hook_modules_uninstalled().

These should be fixed in the attached.

I am not sure what the aggregator bit is doing in the test function. Is that left over from another patch? If it's needed here, just say so.

AttachmentSizeStatusTest resultOperations
user_modules_uninstalled_2.patch4.35 KBIdleFailed: Failed to apply patch.View details | Re-test

#68

catch - June 29, 2009 - 09:09

The aggregator test hunk is from another issue which was a straight bug report, but this issue is the proper fix for it. It tests that you can safely uninstall and reinstall a module (at the moment it throws a lot of PHP warnings due to user_modules_installed() trying to insert permissions for the admin role which were never removed.

#69

agentrickard - June 29, 2009 - 16:07
Status:needs review» reviewed & tested by the community

OK then!

#70

Dries - July 1, 2009 - 08:40
Status:reviewed & tested by the community» needs work

Committed to CVS HEAD. Thanks all!

I'd like to see us rename all instances of 'perm' to 'permission' so marking this 'code needs work'.

#71

catch - July 1, 2009 - 09:04
Status:needs work» fixed

This issue is already pretty long - I've opened #506976: Stop abbreviating permissions to perm so we can continue there.

Also added a short note about this to the 6/7 update documentation.

#72

asimmonds - July 5, 2009 - 02:15
Status:fixed» needs work

The committed patch results in a couple of notices when you uninstall a module that doesn't implement hook_perm eg profile.module.

    * Warning: array_keys(): The first argument should be an array in user_modules_uninstalled() (line 955 of modules\user\user.admin.inc).
    * Warning: array_merge(): Argument #2 is not an array in user_modules_uninstalled() (line 955 of modules\user\user.admin.inc).

#73

catch - July 5, 2009 - 09:21
Status:needs work» needs review

ack, untested patch.

#74

catch - July 5, 2009 - 09:23

Is there a core module with no hook_perm() or maybe one of the test ones? We could add that to the uninstall test.

#75

System Message - July 5, 2009 - 09:25
Status:needs review» needs work

The last submitted patch failed testing.

#76

catch - July 5, 2009 - 09:29
Status:needs work» needs review

And the path that was supposed to come with #53.

AttachmentSizeStatusTest resultOperations
notice.patch1010 bytesIdleFailed: Failed to apply patch.View details | Re-test

#77

System Message - July 6, 2009 - 09:45
Status:needs review» needs work

The last submitted patch failed testing.

#78

agentrickard - July 6, 2009 - 14:36

Patch needs to update to hook_permission()

#79

catch - July 6, 2009 - 15:17
Status:needs work» needs review
AttachmentSizeStatusTest resultOperations
uninstalled.patch949 bytesIdlePassed: 11562 passes, 0 fails, 0 exceptionsView details | Re-test

#80

moshe weitzman - July 6, 2009 - 15:42
Status:needs review» needs work

i think the code would be a tiny bit briefer if you use foreach (module_implements('permission') as $module). you won't loop over unnecessary modules and you can omit the drupal_function_exists().

#81

catch - July 6, 2009 - 16:52
Status:needs work» needs review

We don't want to remove permissions for every module which implements hook_permission(), only those ones which are being uninstalled and given as the parameter to hook_modules_uninstalled() - so module_implements() isn't an option here.

There's other ways to check if we get any permissions from a module before running array_keys() on it (that original hunk of code wasn't mine - I just re-rolled it into the patch which was committed), but they're all fairly equally verbose.

#82

moshe weitzman - July 6, 2009 - 18:56
Status:needs review» reviewed & tested by the community

ah right. makes sense now.

#83

Dries - July 6, 2009 - 19:08
Status:reviewed & tested by the community» fixed

Committed to CVS HEAD. Thanks.

#84

System Message - July 20, 2009 - 19:10
Status:fixed» closed

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

 
 

Drupal is a registered trademark of Dries Buytaert.