diff --git a/includes/install.inc b/includes/install.inc index c472e1ce199a3ed8aca7ee9ee7d24d74f5673275..164002a9cf1cfffab21414da040a55909a97c83a 100644 --- a/includes/install.inc +++ b/includes/install.inc @@ -506,7 +506,10 @@ function drupal_install_modules($module_list = array()) { } while ($moved); asort($module_list); $module_list = array_keys($module_list); - array_filter($module_list, '_drupal_install_module'); + $modules_installed = array_filter($module_list, '_drupal_install_module'); + if (!empty($modules_installed)) { + module_invoke_all('modules_installed', $modules_installed); + } module_enable($module_list); } @@ -574,46 +577,52 @@ function drupal_install_system() { module_rebuild_cache(); } - /** * Calls the uninstall function and updates the system table for a given module. * - * @param $module - * The machine name of the module to uninstall. - */ -function drupal_uninstall_module($module) { - // First, retrieve all the module's menu paths from db. - drupal_load('module', $module); - $paths = module_invoke($module, 'menu'); - - // Uninstall the module(s). - module_load_install($module); - module_invoke($module, 'uninstall'); - - // Now remove the menu links for all paths declared by this module. - if (!empty($paths)) { - $paths = array_keys($paths); - // Clean out the names of load functions. - foreach ($paths as $index => $path) { - $parts = explode('/', $path, MENU_MAX_PARTS); - foreach ($parts as $k => $part) { - if (preg_match('/^%[a-z_]*$/', $part)) { - $parts[$k] = '%'; + * @param $module_list + * The modules to uninstall. + */ +function drupal_uninstall_modules($module_list = array()) { + foreach ($module_list as $module) { + // First, retrieve all the module's menu paths from db. + drupal_load('module', $module); + $paths = module_invoke($module, 'menu'); + + // Uninstall the module. + module_load_install($module); + module_invoke($module, 'uninstall'); + + // Now remove the menu links for all paths declared by this module. + if (!empty($paths)) { + $paths = array_keys($paths); + // Clean out the names of load functions. + foreach ($paths as $index => $path) { + $parts = explode('/', $path, MENU_MAX_PARTS); + foreach ($parts as $k => $part) { + if (preg_match('/^%[a-z_]*$/', $part)) { + $parts[$k] = '%'; + } } + $paths[$index] = implode('/', $parts); } - $paths[$index] = implode('/', $parts); - } - $placeholders = implode(', ', array_fill(0, count($paths), "'%s'")); + $placeholders = implode(', ', array_fill(0, count($paths), "'%s'")); - $result = db_query('SELECT * FROM {menu_links} WHERE router_path IN (' . $placeholders . ') AND external = 0 ORDER BY depth DESC', $paths); - // Remove all such items. Starting from those with the greatest depth will - // minimize the amount of re-parenting done by menu_link_delete(). - while ($item = db_fetch_array($result)) { - _menu_delete_item($item, TRUE); + $result = db_query('SELECT * FROM {menu_links} WHERE router_path IN (' . $placeholders . ') AND external = 0 ORDER BY depth DESC', $paths); + // Remove all such items. Starting from those with the greatest depth will + // minimize the amount of re-parenting done by menu_link_delete(). + while ($item = db_fetch_array($result)) { + _menu_delete_item($item, TRUE); + } } + + drupal_set_installed_schema_version($module, SCHEMA_UNINSTALLED); } - drupal_set_installed_schema_version($module, SCHEMA_UNINSTALLED); + if (!empty($module_list)) { + // Call hook_module_uninstall to let other modules act + module_invoke_all('modules_uninstalled', $module_list); + } } /** diff --git a/includes/module.inc b/includes/module.inc index 61275488843eafd5083b870915bf98e4f7a8b385..835d77db50905fccd0c04fe3a9137d387ca79eaf 100644 --- a/includes/module.inc +++ b/includes/module.inc @@ -297,6 +297,12 @@ function module_enable($module_list) { node_access_needs_rebuild(TRUE); } } + + if (!empty($invoke_modules)) { + // Invoke the hook_module_enable after all the modules have been + // enabled. + module_invoke_all('modules_enabled', $invoke_modules); + } } /** @@ -322,6 +328,9 @@ function module_disable($module_list) { } if (!empty($invoke_modules)) { + // Invoke hook_module_disable before disabling modules, + // so we can still call module hooks to get information. + module_invoke_all('modules_disabled', $invoke_modules); // Refresh the module list to exclude the disabled modules. module_list(TRUE, FALSE); // Force to regenerate the stored list of hook implementations. diff --git a/modules/simpletest/tests/system_test.module b/modules/simpletest/tests/system_test.module index d3db9b5870c5a20f2f8f3716dd98b412f5c39407..188facc96e2d130a2834acd1a5a7e8dd8f0ed9a1 100644 --- a/modules/simpletest/tests/system_test.module +++ b/modules/simpletest/tests/system_test.module @@ -84,3 +84,39 @@ function system_test_redirect_invalid_scheme() { function system_test_destination() { return 'The destination: ' . drupal_get_destination(); } + +/** + * Implementation of hook_modules_installed(). + */ +function system_test_modules_installed($modules) { + if (in_array('aggregator', $modules)) { + drupal_set_message(t('hook_modules_installed fired for aggregator')); + } +} + +/** + * Implementation of hook_modules_enabled(). + */ +function system_test_modules_enabled($modules) { + if (in_array('aggregator', $modules)) { + drupal_set_message(t('hook_modules_enabled fired for aggregator')); + } +} + +/** + * Implementation of hook_modules_disabled(). + */ +function system_test_modules_disabled($modules) { + if (in_array('aggregator', $modules)) { + drupal_set_message(t('hook_modules_disabled fired for aggregator')); + } +} + +/** + * Implementation of hook_modules_uninstalled(). + */ +function system_test_modules_uninstalled($modules) { + if (in_array('aggregator', $modules)) { + drupal_set_message(t('hook_modules_uninstalled fired for aggregator')); + } +} diff --git a/modules/system/system.admin.inc b/modules/system/system.admin.inc index ae11a4557b79ea8776474b7ce77b7ed1ffabd3ea..307653bda0661421e9910ac018d42338d3e73e2e 100644 --- a/modules/system/system.admin.inc +++ b/modules/system/system.admin.inc @@ -1083,9 +1083,8 @@ function system_modules_uninstall_submit($form, &$form_state) { if (!empty($form['#confirmed'])) { // Call the uninstall routine for each selected module. - foreach (array_filter($form_state['values']['uninstall']) as $module => $value) { - drupal_uninstall_module($module); - } + $modules = array_keys($form_state['values']['uninstall']); + drupal_uninstall_modules($modules); drupal_set_message(t('The selected modules have been uninstalled.')); unset($form_state['storage']); diff --git a/modules/system/system.test b/modules/system/system.test index dbf6c0dbd65f6fdcc9df1157bd067c13e13f2f0f..8934316ac722a8d78ecc4703bcc5235e4298ec9e 100644 --- a/modules/system/system.test +++ b/modules/system/system.test @@ -19,7 +19,7 @@ class EnableDisableCoreTestCase extends DrupalWebTestCase { * Implementation of setUp(). */ function setUp() { - parent::setUp(); + parent::setUp('system_test'); $this->admin_user = $this->drupalCreateUser(array('access administration pages', 'administer site configuration')); $this->drupalLogin($this->admin_user); @@ -28,17 +28,22 @@ class EnableDisableCoreTestCase extends DrupalWebTestCase { /** * Enable a module, check the database for related tables, disable module, * check for related tables, unistall module, check for related tables. + * Also check for invocation of the hook_module_action hook. */ function testEnableDisable() { // Enable aggregator, and check tables. $this->assertModules(array('aggregator'), FALSE); $this->assertTableCount('aggregator', FALSE); + // Install (and enable) aggregator module. $edit = array(); $edit['modules[Core][aggregator][enable]'] = 'aggregator'; $this->drupalPost('admin/build/modules', $edit, t('Save configuration')); $this->assertText(t('The configuration options have been saved.'), t('Modules status has been updated.')); + // Check that hook_modules_installed and hook_modules_enabled hooks were invoked and check tables. + $this->assertText(t('hook_modules_installed fired for aggregator'), t('hook_modules_installed fired.')); + $this->assertText(t('hook_modules_enabled fired for aggregator'), t('hook_modules_enabled fired.')); $this->assertModules(array('aggregator'), TRUE); $this->assertTableCount('aggregator', TRUE); @@ -48,9 +53,12 @@ class EnableDisableCoreTestCase extends DrupalWebTestCase { $this->drupalPost('admin/build/modules', $edit, t('Save configuration')); $this->assertText(t('The configuration options have been saved.'), t('Modules status has been updated.')); + // Check that hook_modules_disabled hook was invoked and check tables. + $this->assertText(t('hook_modules_disabled fired for aggregator'), t('hook_modules_disabled fired.')); $this->assertModules(array('aggregator'), FALSE); $this->assertTableCount('aggregator', TRUE); + // Uninstall the module. $edit = array(); $edit['uninstall[aggregator]'] = 'aggregator'; $this->drupalPost('admin/build/modules/uninstall', $edit, t('Uninstall')); @@ -58,6 +66,8 @@ class EnableDisableCoreTestCase extends DrupalWebTestCase { $this->drupalPost(NULL, NULL, t('Uninstall')); $this->assertText(t('The selected modules have been uninstalled.'), t('Modules status has been updated.')); + // Check that hook_modules_uninstalled hook was invoked and check tables. + $this->assertText(t('hook_modules_uninstalled fired for aggregator'), t('hook_modules_uninstalled fired.')); $this->assertModules(array('aggregator'), FALSE); $this->assertTableCount('aggregator', FALSE); }