diff --git a/includes/install.core.inc b/includes/install.core.inc
index 8fcb5ca71491d9cad2083b35cef044d76675111a..7c4bc10575350172cd38c33f1762ca8b3cf7d367 100644
--- a/includes/install.core.inc
+++ b/includes/install.core.inc
@@ -1493,8 +1493,7 @@ function install_finished(&$install_state) {
  * Batch callback for batch installation of modules.
  */
 function _install_module_batch($module, $module_name, &$context) {
-  _drupal_install_module($module);
-  // We enable the installed module right away, so that the module will be
+  // Install and enable the module right away, so that the module will be
   // loaded by drupal_bootstrap in subsequent batch requests, and other
   // modules possibly depending on it can safely perform their installation
   // steps.
diff --git a/includes/install.inc b/includes/install.inc
index c3e0536c357d957467e47df2ca27fbc219bef53a..17225f5623d4aeb00435a7b2386c0e7707bd0e43 100644
--- a/includes/install.inc
+++ b/includes/install.inc
@@ -541,31 +541,6 @@ function drupal_verify_profile($install_state) {
   return $requirements;
 }
 
-/**
- * Callback to install an individual install profile module.
- *
- * Used during installation to install modules one at a time and then
- * enable them, or to install a number of modules at one time
- * from admin/modules.
- *
- * @param $module
- *   The machine name of the module to install.
- * @return
- *   TRUE if the module got installed.
- */
-function _drupal_install_module($module) {
-  if (drupal_get_installed_schema_version($module, TRUE) == SCHEMA_UNINSTALLED) {
-    drupal_load('module', $module);
-    drupal_install_schema($module);
-    // Now allow the module to perform install tasks.
-    module_invoke($module, 'install');
-    $versions = drupal_get_schema_versions($module);
-    drupal_set_installed_schema_version($module, $versions ? max($versions) : SCHEMA_INSTALLED);
-    system_list_reset();
-    return TRUE;
-  }
-}
-
 /**
  * Manually include all files for the active database.
  *
diff --git a/includes/module.inc b/includes/module.inc
index 313f712531f22c6c588bea2965c105c385d18e10..f5f66d7e9e5a5fca89c975a2b805fc397f1848f7 100644
--- a/includes/module.inc
+++ b/includes/module.inc
@@ -336,63 +336,69 @@ function module_enable($module_list, $enable_dependencies = TRUE, $disable_modul
     $module_list = array_keys($module_list);
   }
 
-  $invoke_modules = array();
-
-  // Required for _drupal_install_module().
+  // Required for module installation checks.
   include_once DRUPAL_ROOT . '/includes/install.inc';
-  // Try to install the enabled modules and collect which were installed.
-  // $module_list is not changed and already installed modules are ignored.
-  $modules_installed = array_filter($module_list, '_drupal_install_module');
+
+  $modules_installed = array();
+  $modules_enabled = array();
   foreach ($module_list as $module) {
+    // Only process modules that are not already enabled.
     $existing = db_query("SELECT status FROM {system} WHERE type = :type AND name = :name", array(
       ':type' => 'module',
       ':name' => $module))
       ->fetchObject();
     if ($existing->status == 0) {
+      // Load the module's code.
+      drupal_load('module', $module);
       module_load_install($module);
+
+      // Update the database and module list to reflect the new module. This
+      // needs to be done first so that the module's hook implementations,
+      // hook_schema() in particular, can be called while it is being
+      // installed.
       db_update('system')
         ->fields(array('status' => 1))
         ->condition('type', 'module')
         ->condition('name', $module)
         ->execute();
-      drupal_load('module', $module);
-      $invoke_modules[] = $module;
-      watchdog('system', '%module module enabled.', array('%module' => $module), WATCHDOG_INFO);
-    }
-  }
+      // Refresh the module list to include it.
+      system_list_reset();
+      module_list(TRUE);
+      module_implements('', FALSE, TRUE);
+      _system_update_bootstrap_status();
+      // Update the registry to include it.
+      registry_update();
+      // Refresh the schema to include it.
+      drupal_get_schema(NULL, TRUE);
+
+      // Now install the module if necessary.
+      if (drupal_get_installed_schema_version($module, TRUE) == SCHEMA_UNINSTALLED) {
+        drupal_install_schema($module);
+        // Allow the module to perform install tasks.
+        module_invoke($module, 'install');
+        $versions = drupal_get_schema_versions($module);
+        drupal_set_installed_schema_version($module, $versions ? max($versions) : SCHEMA_INSTALLED);
+        // Record the fact that it was installed.
+        $modules_installed[] = $module;
+      }
 
-  if (!empty($invoke_modules)) {
-    // Refresh the module list to exclude the disabled modules.
-    system_list_reset();
-    module_list(TRUE);
-    module_implements('', FALSE, TRUE);
-    // Update the registry to include the new enabled module.
-    registry_update();
-    // Refresh the schema to include the new enabled module.
-    drupal_get_schema(NULL, TRUE);
+      // Enable the module.
+      module_invoke($module, 'enable');
 
-    // If any modules were newly installed, execute the hook for them.
-    if (!$disable_modules_installed_hook && !empty($modules_installed)) {
-      module_invoke_all('modules_installed', $modules_installed);
+      // Record the fact that it was enabled.
+      $modules_enabled[] = $module;
+      watchdog('system', '%module module enabled.', array('%module' => $module), WATCHDOG_INFO);
     }
-    _system_update_bootstrap_status();
   }
 
-  foreach ($invoke_modules as $module) {
-    module_invoke($module, 'enable');
-    // Check if node_access table needs rebuilding.
-    // We check for the existence of node_access_needs_rebuild() since
-    // at install time, module_enable() could be called while node.module
-    // is not enabled yet.
-    if (function_exists('node_access_needs_rebuild') && !node_access_needs_rebuild() && module_hook($module, 'node_grants')) {
-      node_access_needs_rebuild(TRUE);
-    }
+  // If any modules were newly installed, invoke hook_modules_installed().
+  if (!$disable_modules_installed_hook && !empty($modules_installed)) {
+    module_invoke_all('modules_installed', $modules_installed);
   }
 
-  if (!empty($invoke_modules)) {
-    // Invoke hook_modules_enabled after all the modules have been
-    // enabled.
-    module_invoke_all('modules_enabled', $invoke_modules);
+  // If any modules were newly enabled, invoke hook_modules_enabled().
+  if (!empty($modules_enabled)) {
+    module_invoke_all('modules_enabled', $modules_enabled);
   }
 
   return TRUE;
diff --git a/modules/node/node.module b/modules/node/node.module
index 9e6b76045e02a525d05bfc5dad0fd9dd85a6975f..36cc76dcd987ffb358c8ef0e8c5b9d636bf6f348 100644
--- a/modules/node/node.module
+++ b/modules/node/node.module
@@ -3535,6 +3535,17 @@ function node_requirements($phase) {
   return $requirements;
 }
 
+/**
+ * Implements hook_modules_enabled().
+ */
+function node_modules_enabled($modules) {
+  // Check if any of the newly enabled modules require the node_access table to
+  // be rebuilt.
+  if (!node_access_needs_rebuild() && array_intersect($modules, module_implements('node_grants'))) {
+    node_access_needs_rebuild(TRUE);
+  }
+}
+
 /**
  * Controller class for nodes.
  *
diff --git a/modules/rdf/rdf.install b/modules/rdf/rdf.install
index ec553fcda57b20c14799e7e8ff1b55c09fa5360b..905c247714569a0827a2e193ac7f14dd5f05ee7c 100644
--- a/modules/rdf/rdf.install
+++ b/modules/rdf/rdf.install
@@ -43,8 +43,9 @@ function rdf_schema() {
  * Implements hook_install().
  */
 function rdf_install() {
-  // The installer does not trigger hook_modules_installed(), so it needs to be
-  // triggered manually for modules defining RDF mappings.
+  // Collect any RDF mappings that were declared by modules installed before
+  // this one.
   $modules = module_implements('rdf_mapping');
   rdf_modules_installed($modules);
 }
+
diff --git a/modules/shortcut/shortcut.install b/modules/shortcut/shortcut.install
index 634ee51fb3b581496b784951b0c9651b4fdd0534..5c49322eeefaea9bd1d32cbaf863081c82d47d06 100644
--- a/modules/shortcut/shortcut.install
+++ b/modules/shortcut/shortcut.install
@@ -7,14 +7,9 @@
  */
 
 /**
- * Implements hook_enable().
+ * Implements hook_install().
  */
-function shortcut_enable() {
-  if (shortcut_set_load(SHORTCUT_DEFAULT_SET_NAME)) {
-    // Quit out; this module has already been installed before.
-    return;
-  }
-
+function shortcut_install() {
   $t = get_t();
   // Create an initial default shortcut set.
   $shortcut_set = new StdClass();
diff --git a/modules/simpletest/tests/module.test b/modules/simpletest/tests/module.test
index 242910c7f2932d3e0ff84b7989a7a995be416acb..31c0bb0287d276f8d15d49ce4d2f89e1e879a176 100644
--- a/modules/simpletest/tests/module.test
+++ b/modules/simpletest/tests/module.test
@@ -125,6 +125,43 @@ class ModuleUnitTest extends DrupalWebTestCase {
   }
 }
 
+/**
+ * Unit tests for module installation.
+ */
+class ModuleInstallTestCase extends DrupalWebTestCase {
+  public static function getInfo() {
+    return array(
+      'name' => 'Module installation',
+      'description' => 'Tests the installation of modules.',
+      'group' => 'Module',
+    );
+  }
+
+  function setUp() {
+    parent::setUp('module_test');
+  }
+
+  /**
+   * Test that calls to drupal_write_record() work during module installation.
+   *
+   * This is a useful function to test because modules often use it to insert
+   * initial data in their database tables when they are being installed or
+   * enabled. Furthermore, drupal_write_record() relies on the module schema
+   * information being available, so this also checks that the data from one of
+   * the module's hook implementations, in particular hook_schema(), is
+   * properly available during this time. Therefore, this test helps ensure
+   * that modules are fully functional while Drupal is installing and enabling
+   * them.
+   */
+  function testDrupalWriteRecord() {
+    // Check for data that was inserted using drupal_write_record() while the
+    // 'module_test' module was being installed and enabled.
+    $data = db_query("SELECT data FROM {module_test}")->fetchCol();
+    $this->assertTrue(in_array('Data inserted in hook_install()', $data), t('Data inserted using drupal_write_record() in hook_install() is correctly saved.'));
+    $this->assertTrue(in_array('Data inserted in hook_enable()', $data), t('Data inserted using drupal_write_record() in hook_enable() is correctly saved.'));
+  }
+}
+
 /**
  * Unit tests for module uninstallation and related hooks.
  */
@@ -132,7 +169,7 @@ class ModuleUninstallTestCase extends DrupalWebTestCase {
   public static function getInfo() {
     return array(
       'name' => 'Module uninstallation',
-      'description' => 'Checks module uninstallation',
+      'description' => 'Tests the uninstallation of modules.',
       'group' => 'Module',
     );
   }
@@ -147,6 +184,7 @@ class ModuleUninstallTestCase extends DrupalWebTestCase {
   function testUserPermsUninstalled() {
     // Uninstalls the module_test module, so hook_modules_uninstalled()
     // is executed.
+    module_disable(array('module_test'));
     drupal_uninstall_modules(array('module_test'));
 
     // Are the perms defined by module_test removed from {role_permission}.
diff --git a/modules/simpletest/tests/module_test.install b/modules/simpletest/tests/module_test.install
new file mode 100644
index 0000000000000000000000000000000000000000..5f8e76b702ea06febb8b912753570a3f0762d8ef
--- /dev/null
+++ b/modules/simpletest/tests/module_test.install
@@ -0,0 +1,43 @@
+<?php
+// $Id$
+
+/**
+ * @file
+ * Install, update and uninstall functions for the module_test module.
+ */
+
+/**
+ * Implements hook_schema().
+ */
+function module_test_schema() {
+  $schema['module_test'] = array(
+    'description' => 'Dummy table to test the behavior of hook_schema() during module installation.',
+    'fields' => array(
+      'data' => array(
+        'type' => 'varchar',
+        'length' => 255,
+        'not null' => TRUE,
+        'default' => '',
+        'description' => 'An example data column for the module.',
+      ),
+    ),
+  );
+  return $schema;
+}
+
+/**
+ * Implements hook_install().
+ */
+function module_test_install() {
+  $record = array('data' => 'Data inserted in hook_install()');
+  drupal_write_record('module_test', $record);
+}
+
+/**
+ * Implements hook_enable().
+ */
+function module_test_enable() {
+  $record = array('data' => 'Data inserted in hook_enable()');
+  drupal_write_record('module_test', $record);
+}
+
diff --git a/modules/system/system.module b/modules/system/system.module
index 18ada5af76c71d75797a8d736466d4a472b7c1da..f598fc590a8972a3bda31dcfbb03f3108c858653 100644
--- a/modules/system/system.module
+++ b/modules/system/system.module
@@ -2226,6 +2226,8 @@ function _system_update_bootstrap_status() {
     $query->condition('name', $bootstrap_modules, 'NOT IN');
   }
   $query->execute();
+  // Reset the cached list of bootstrap modules.
+  system_list_reset();
 }
 
 /**