diff --git a/core/includes/module.inc b/core/includes/module.inc index bc91311..714c9b8 100644 --- a/core/includes/module.inc +++ b/core/includes/module.inc @@ -5,11 +5,6 @@ * API for loading and interacting with Drupal modules. */ -use Drupal\Component\Utility\NestedArray; -use Drupal\Core\Cache\CacheBackendInterface; -use Drupal\Core\DependencyInjection\ContainerBuilder; -use Symfony\Component\DependencyInjection\ContainerInterface; -use Symfony\Component\Yaml\Parser; /** * Builds a list of bootstrap modules and enabled modules and themes. @@ -238,188 +233,14 @@ function module_load_include($type, $module, $name = NULL) { * @return * FALSE if one or more dependencies are missing, TRUE otherwise. * + * @deprecated * @see hook_install() * @see hook_enable() * @see hook_modules_installed() * @see hook_modules_enabled() */ function module_enable($module_list, $enable_dependencies = TRUE) { - if ($enable_dependencies) { - // Get all module data so we can find dependencies and sort. - $module_data = system_rebuild_module_data(); - // Create an associative array with weights as values. - $module_list = array_flip(array_values($module_list)); - - while (list($module) = each($module_list)) { - if (!isset($module_data[$module])) { - // This module is not found in the filesystem, abort. - return FALSE; - } - if ($module_data[$module]->status) { - // Skip already enabled modules. - unset($module_list[$module]); - continue; - } - $module_list[$module] = $module_data[$module]->sort; - - // Add dependencies to the list, with a placeholder weight. - // The new modules will be processed as the while loop continues. - foreach (array_keys($module_data[$module]->requires) as $dependency) { - if (!isset($module_list[$dependency])) { - $module_list[$dependency] = 0; - } - } - } - - if (!$module_list) { - // Nothing to do. All modules already enabled. - return TRUE; - } - - // Sort the module list by pre-calculated weights. - arsort($module_list); - $module_list = array_keys($module_list); - } - - // Required for module installation checks. - include_once __DIR__ . '/install.inc'; - - $modules_installed = array(); - $modules_enabled = array(); - $schema_store = Drupal::keyValue('system.schema'); - $module_config = config('system.module'); - $disabled_config = config('system.module.disabled'); - $module_handler = drupal_container()->get('module_handler'); - foreach ($module_list as $module) { - // Only process modules that are not already enabled. - // A module is only enabled if it is configured as enabled. Custom or - // overridden module handlers might contain the module already, which means - // that it might be loaded, but not necessarily installed or enabled. - $enabled = $module_config->get("enabled.$module") !== NULL; - if (!$enabled) { - $weight = $disabled_config->get($module); - if ($weight === NULL) { - $weight = 0; - } - $module_config - ->set("enabled.$module", $weight) - ->set('enabled', module_config_sort($module_config->get('enabled'))) - ->save(); - $disabled_config - ->clear($module) - ->save(); - - // Prepare the new module list, sorted by weight, including filenames. - // This list is used for both the ModuleHandler and DrupalKernel. It needs - // to be kept in sync between both. A DrupalKernel reboot or rebuild will - // automatically re-instantiate a new ModuleHandler that uses the new - // module list of the kernel. However, DrupalKernel does not cause any - // modules to be loaded. - // Furthermore, the currently active (fixed) module list can be different - // from the configured list of enabled modules. For all active modules not - // contained in the configured enabled modules, we assume a weight of 0. - $current_module_filenames = $module_handler->getModuleList(); - $current_modules = array_fill_keys(array_keys($current_module_filenames), 0); - $current_modules = module_config_sort(array_merge($current_modules, $module_config->get('enabled'))); - $module_filenames = array(); - foreach ($current_modules as $name => $weight) { - if (isset($current_module_filenames[$name])) { - $filename = $current_module_filenames[$name]; - } - else { - $filename = drupal_get_filename('module', $name); - } - $module_filenames[$name] = $filename; - } - - // Update the module handler in order to load the module's code. - // This allows the module to participate in hooks and its existence to be - // discovered by other modules. - // The current ModuleHandler instance is obsolete with the kernel rebuild - // below. - $module_handler->setModuleList($module_filenames); - $module_handler->load($module); - module_load_install($module); - - // Flush theme info caches, since (testing) modules can implement - // hook_system_theme_info() to register additional themes. - system_list_reset(); - - // Update the kernel to include it. - // This reboots the kernel to register the module's bundle and its - // services in the service container. The $module_filenames argument is - // taken over as %container.modules% parameter, which is passed to a fresh - // ModuleHandler instance upon first retrieval. - // @todo install_begin_request() creates a container without a kernel. - if ($kernel = drupal_container()->get('kernel', ContainerInterface::NULL_ON_INVALID_REFERENCE)) { - $kernel->updateModules($module_filenames, $module_filenames); - } - - // Refresh the list of modules that implement bootstrap hooks. - // @see bootstrap_hooks() - _system_update_bootstrap_status(); - - // Refresh the schema to include it. - drupal_get_schema(NULL, TRUE); - // Update the theme registry to include it. - drupal_theme_rebuild(); - - // Allow modules to react prior to the installation of a module. - module_invoke_all('modules_preinstall', array($module)); - - // Clear the entity info cache before importing new configuration. - entity_info_cache_clear(); - - // Now install the module if necessary. - if (drupal_get_installed_schema_version($module, TRUE) == SCHEMA_UNINSTALLED) { - drupal_install_schema($module); - - // Set the schema version to the number of the last update provided - // by the module. - $versions = drupal_get_schema_versions($module); - $version = $versions ? max($versions) : SCHEMA_INSTALLED; - - // Install default configuration of the module. - config_install_default_config('module', $module); - - // If the module has no current updates, but has some that were - // previously removed, set the version to the value of - // hook_update_last_removed(). - if ($last_removed = module_invoke($module, 'update_last_removed')) { - $version = max($version, $last_removed); - } - drupal_set_installed_schema_version($module, $version); - // Allow the module to perform install tasks. - module_invoke($module, 'install'); - // Record the fact that it was installed. - $modules_installed[] = $module; - watchdog('system', '%module module installed.', array('%module' => $module), WATCHDOG_INFO); - } - - // Allow modules to react prior to the enabling of a module. - entity_info_cache_clear(); - module_invoke_all('modules_preenable', array($module)); - - // Enable the module. - module_invoke($module, 'enable'); - - // Record the fact that it was enabled. - $modules_enabled[] = $module; - watchdog('system', '%module module enabled.', array('%module' => $module), WATCHDOG_INFO); - } - } - - // If any modules were newly installed, invoke hook_modules_installed(). - if (!empty($modules_installed)) { - module_invoke_all('modules_installed', $modules_installed); - } - - // If any modules were newly enabled, invoke hook_modules_enabled(). - if (!empty($modules_enabled)) { - module_invoke_all('modules_enabled', $modules_enabled); - } - - return TRUE; + return Drupal::moduleHandler()->enable($module_list, $enable_dependencies); } /** @@ -433,92 +254,7 @@ function module_enable($module_list, $enable_dependencies = TRUE) { * if you know $module_list is already complete and in the correct order. */ function module_disable($module_list, $disable_dependents = TRUE) { - if ($disable_dependents) { - // Get all module data so we can find dependents and sort. - $module_data = system_rebuild_module_data(); - // Create an associative array with weights as values. - $module_list = array_flip(array_values($module_list)); - - $profile = drupal_get_profile(); - while (list($module) = each($module_list)) { - if (!isset($module_data[$module]) || !$module_data[$module]->status) { - // This module doesn't exist or is already disabled, skip it. - unset($module_list[$module]); - continue; - } - $module_list[$module] = $module_data[$module]->sort; - - // Add dependent modules to the list, with a placeholder weight. - // The new modules will be processed as the while loop continues. - foreach ($module_data[$module]->required_by as $dependent => $dependent_data) { - if (!isset($module_list[$dependent]) && $dependent != $profile) { - $module_list[$dependent] = 0; - } - } - } - - // Sort the module list by pre-calculated weights. - asort($module_list); - $module_list = array_keys($module_list); - } - - $invoke_modules = array(); - - $module_config = config('system.module'); - $disabled_config = config('system.module.disabled'); - $module_handler = drupal_container()->get('module_handler'); - foreach ($module_list as $module) { - // Only process modules that are enabled. - // A module is only enabled if it is configured as enabled. Custom or - // overridden module handlers might contain the module already, which means - // that it might be loaded, but not necessarily installed or enabled. - $enabled = $module_config->get("enabled.$module") !== NULL; - if ($enabled) { - module_load_install($module); - module_invoke($module, 'disable'); - - $disabled_config - ->set($module, $module_config->get($module)) - ->save(); - $module_config - ->clear("enabled.$module") - ->save(); - - // Update the module handler to remove the module. - // The current ModuleHandler instance is obsolete with the kernel rebuild - // below. - $module_filenames = $module_handler->getModuleList(); - unset($module_filenames[$module]); - $module_handler->setModuleList($module_filenames); - - // Record the fact that it was disabled. - $invoke_modules[] = $module; - watchdog('system', '%module module disabled.', array('%module' => $module), WATCHDOG_INFO); - } - } - - if (!empty($invoke_modules)) { - // @todo Most of the following should happen in above loop already. - - // Refresh the system list to exclude the disabled modules. - // @todo Only needed to rebuild theme info. - // @see system_list_reset() - system_list_reset(); - - entity_info_cache_clear(); - - // Invoke hook_modules_disabled before disabling modules, - // so we can still call module hooks to get information. - module_invoke_all('modules_disabled', $invoke_modules); - _system_update_bootstrap_status(); - - // Update the kernel to exclude the disabled modules. - $enabled = $module_handler->getModuleList(); - drupal_container()->get('kernel')->updateModules($enabled, $enabled); - - // Update the theme registry to remove the newly-disabled module. - drupal_theme_rebuild(); - } + Drupal::moduleHandler()->disable($module_list, $disable_dependents); } /** @@ -545,93 +281,7 @@ function module_disable($module_list, $disable_dependents = TRUE) { * @see module_disable() */ function module_uninstall($module_list = array(), $uninstall_dependents = TRUE) { - if ($uninstall_dependents) { - // Get all module data so we can find dependents and sort. - $module_data = system_rebuild_module_data(); - // Create an associative array with weights as values. - $module_list = array_flip(array_values($module_list)); - - $profile = drupal_get_profile(); - while (list($module) = each($module_list)) { - if (!isset($module_data[$module]) || drupal_get_installed_schema_version($module) == SCHEMA_UNINSTALLED) { - // This module doesn't exist or is already uninstalled. Skip it. - unset($module_list[$module]); - continue; - } - $module_list[$module] = $module_data[$module]->sort; - - // If the module has any dependents which are not already uninstalled and - // not included in the passed-in list, abort. It is not safe to uninstall - // them automatically because uninstalling a module is a destructive - // operation. - foreach (array_keys($module_data[$module]->required_by) as $dependent) { - if (!isset($module_list[$dependent]) && drupal_get_installed_schema_version($dependent) != SCHEMA_UNINSTALLED && $dependent != $profile) { - return FALSE; - } - } - } - - // Sort the module list by pre-calculated weights. - asort($module_list); - $module_list = array_keys($module_list); - } - - $schema_store = Drupal::keyValue('system.schema'); - $disabled_config = config('system.module.disabled'); - foreach ($module_list as $module) { - // Uninstall the module. - module_load_install($module); - module_invoke($module, 'uninstall'); - drupal_uninstall_schema($module); - - // Remove all configuration belonging to the module. - config_uninstall_default_config('module', $module); - - // Remove any cache bins defined by the module. - $service_yaml_file = drupal_get_path('module', $module) . "/$module.services.yml"; - if (file_exists($service_yaml_file)) { - $parser = new Parser; - $definitions = $parser->parse(file_get_contents($service_yaml_file)); - if (isset($definitions['services'])) { - foreach ($definitions['services'] as $id => $definition) { - if (isset($definition['tags'])) { - foreach ($definition['tags'] as $tag) { - // This works for the default cache registration and even in some - // cases when a non-default "super" factory is used. That should - // be extremely rare. - if ($tag['name'] == 'cache.bin' && isset($definition['factory_service']) && isset($definition['factory_method']) && !empty($definition['arguments'])) { - try { - $factory = Drupal::service($definition['factory_service']); - if (method_exists($factory, $definition['factory_method'])) { - $backend = call_user_func_array(array($factory, $definition['factory_method']), $definition['arguments']); - if ($backend instanceof CacheBackendInterface) { - $backend->removeBin(); - } - } - } - catch (Exception $e) { - watchdog_exception('system', $e, 'Failed to remove cache bin defined by the service %id.', array('%id' => $id)); - } - } - } - } - } - } - } - - watchdog('system', '%module module uninstalled.', array('%module' => $module), WATCHDOG_INFO); - $schema_store->delete($module); - $disabled_config->clear($module); - } - $disabled_config->save(); - drupal_get_installed_schema_version(NULL, TRUE); - - if (!empty($module_list)) { - // Let other modules react. - module_invoke_all('modules_uninstalled', $module_list); - } - - return TRUE; + return Drupal::moduleHandler()->uninstall($module_list, $uninstall_dependents); } /** diff --git a/core/includes/update.inc b/core/includes/update.inc index a5feb50..cdeac90 100644 --- a/core/includes/update.inc +++ b/core/includes/update.inc @@ -15,6 +15,7 @@ use Drupal\Core\DrupalKernel; use Drupal\Component\Uuid\Uuid; use Drupal\Component\Utility\NestedArray; +use Symfony\Component\HttpFoundation\Request; /** * Minimum schema version of Drupal 7 required for upgrade to Drupal 8. @@ -103,7 +104,7 @@ function update_prepare_d8_bootstrap() { new Settings($settings); // Enable UpdateBundle service overrides. - $GLOBALS['conf']['container_bundles'][] = 'Drupal\Core\DependencyInjection\UpdateBundle'; + $GLOBALS['conf']['container_bundles']['UpdateBundle'] = 'Drupal\Core\DependencyInjection\UpdateBundle'; // Check whether settings.php needs to be rewritten. $settings_exist = !empty($GLOBALS['config_directories']); @@ -452,6 +453,13 @@ function update_prepare_stored_includes() { */ function update_prepare_d8_language() { if (db_table_exists('languages')) { + + // Install/enable the language module. We need to use the update specific + // version of this function to ensure schema conflicts don't happen due to + // our updated data. + $modules = array('language'); + module_enable($modules); + $languages = db_select('languages', 'l') ->fields('l') ->execute(); @@ -490,12 +498,6 @@ function update_prepare_d8_language() { // Rename the languages table to language. db_rename_table('languages', 'language'); - // Install/enable the language module. We need to use the update specific - // version of this function to ensure schema conflicts don't happen due to - // our updated data. - $modules = array('language'); - update_module_enable($modules); - // Rename language column to langcode and set it again as the primary key. if (db_field_exists('language', 'language')) { db_drop_primary_key('language'); @@ -633,7 +635,7 @@ function update_fix_d8_requirements() { // Make sure that file.module is enabled as it is required for the user // picture upgrade path. - update_module_enable(array('file')); + module_enable(array('file')); $schema = array( 'description' => 'Generic key/value storage table with an expiration.', @@ -673,73 +675,33 @@ function update_fix_d8_requirements() { ); db_create_table('key_value_expire', $schema); + // Views module is required to convert all previously existing listings into + // views configurations. + // Like any other module APIs and services, Views' services are not available + // in update.php. Existing listings are migrated into configuration, using + // the limited standard tools of raw database queries and config(). + module_enable(array('views')); + update_variable_set('update_d8_requirements', TRUE); } } /** - * Installs a new module in Drupal 8 via hook_update_N(). + * Forces a module to a given schema version. * - * @param array $modules - * List of modules to enable. Dependencies are not checked and must be - * ensured by the caller. - * @param bool $update_schema_version - * (optional) The schema version the module should be set to. Defaults to 0 - * which works well for completely new modules. + * This function is rarely necessary. * - * @return array - * List of the old schema versions keyed by the module names, - * SCHEMA_UNINSTALLED if it was not installed before. Additional manual - * installation steps like installing default configuration might be necessary - * if the module was not installed before. + * @param $mdoule + * Name of the module. + * @param $schema_version + * The schema version the module should be set to. */ -function update_module_enable(array $modules, $schema_version = 0) { - $schema_store = Drupal::keyValue('system.schema'); - $old_schema = array(); - foreach ($modules as $module) { - // Check for initial schema and install it. The schema version of a newly - // installed module is always 0. Using 8000 here would be inconsistent - // since $module_update_8000() may involve a schema change, and we want - // to install the schema as it was before any updates were added. - module_load_install($module); - $function = $module . '_schema_0'; - if (function_exists($function)) { - $schema = $function(); - foreach ($schema as $table => $spec) { - db_create_table($table, $spec); - } - } - // Enable the module with a weight of 0. - $module_config = config('system.module'); - $module_config - ->set("enabled.$module", 0) - ->set('enabled', module_config_sort($module_config->get('enabled'))) - ->save(); - // Ensure the module is not contained in disabled modules. - config('system.module.disabled') - ->clear($module) - ->save(); - - $current_schema = $schema_store->get($module); - // Set the schema version if the module was not just disabled before. - if ($current_schema === NULL || $current_schema === SCHEMA_UNINSTALLED) { - // Change the schema version to the given value (defaults to 0), so any - // module updates since the module's inception are executed in a core - // upgrade. - $schema_store->set($module, $schema_version); - $old_schema[$module] = SCHEMA_UNINSTALLED; - } - else { - $old_schema[$module] = $current_schema; - } - - // system_list_reset() is in module.inc but that would only be available - // once the variable bootstrap is done. - require_once __DIR__ . '/module.inc'; - system_list_reset(); - // @todo: figure out what to do about hook_install() and hook_enable(). - } - return $old_schema; +function update_set_schema($module, $schema_version) { + Drupal::keyValue('system.schema')->set($module, $schema_version); + // system_list_reset() is in module.inc but that would only be available + // once the variable bootstrap is done. + require_once __DIR__ . '/module.inc'; + system_list_reset(); } /** @@ -937,7 +899,7 @@ function update_batch($start, $redirect = NULL, $url = NULL, $batch = array(), $ */ function update_finished($success, $results, $operations) { // Clear the caches in case the data has been updated. - drupal_flush_all_caches(); + update_flush_all_caches(); $_SESSION['update_results'] = $results; $_SESSION['update_success'] = $success; @@ -1273,7 +1235,7 @@ function update_retrieve_dependencies() { // Nothing to upgrade. continue; } - $function = $module . '_update_dependencies'; + $function = $module . '_update_dependencies'; // Ensure install file is loaded. module_load_install($module); if (function_exists($function)) { diff --git a/core/lib/Drupal/Core/Cache/DatabaseBackend.php b/core/lib/Drupal/Core/Cache/DatabaseBackend.php index 913a43b..a45d338 100644 --- a/core/lib/Drupal/Core/Cache/DatabaseBackend.php +++ b/core/lib/Drupal/Core/Cache/DatabaseBackend.php @@ -218,7 +218,7 @@ public function deleteMultiple(array $cids) { * Implements Drupal\Core\Cache\CacheBackendInterface::deleteTags(). */ public function deleteTags(array $tags) { - $tag_cache = &drupal_static('Drupal\Core\Cache\CacheBackendInterface::tagCache'); + $tag_cache = &drupal_static('Drupal\Core\Cache\CacheBackendInterface::tagCache', array()); foreach ($this->flattenTags($tags) as $tag) { unset($tag_cache[$tag]); try { @@ -277,7 +277,7 @@ public function invalidateMultiple(array $cids) { */ public function invalidateTags(array $tags) { try { - $tag_cache = &drupal_static('Drupal\Core\Cache\CacheBackendInterface::tagCache'); + $tag_cache = &drupal_static('Drupal\Core\Cache\CacheBackendInterface::tagCache', array()); foreach ($this->flattenTags($tags) as $tag) { unset($tag_cache[$tag]); $this->connection->merge('cache_tags') diff --git a/core/lib/Drupal/Core/DependencyInjection/UpdateBundle.php b/core/lib/Drupal/Core/DependencyInjection/UpdateBundle.php index 06a9522..76b58ab 100644 --- a/core/lib/Drupal/Core/DependencyInjection/UpdateBundle.php +++ b/core/lib/Drupal/Core/DependencyInjection/UpdateBundle.php @@ -32,6 +32,13 @@ public function build(SymfonyContainerBuilder $container) { $container ->register('config.storage', 'Drupal\Core\Config\FileStorage') ->addArgument(config_get_config_directory(CONFIG_ACTIVE_DIRECTORY)); + $container->register('module_handler', 'Drupal\Core\Extension\UpdateModuleHandler') + ->addArgument('%container.modules%'); + $container + ->register("cache_factory", 'Drupal\Core\Cache\MemoryBackendFactory'); + $container + ->register('router.builder', 'Drupal\Core\Routing\RouteBuilderStatic'); + } } diff --git a/core/lib/Drupal/Core/Extension/ModuleHandler.php b/core/lib/Drupal/Core/Extension/ModuleHandler.php index dffcbe0..f0e8495 100644 --- a/core/lib/Drupal/Core/Extension/ModuleHandler.php +++ b/core/lib/Drupal/Core/Extension/ModuleHandler.php @@ -8,9 +8,11 @@ namespace Drupal\Core\Extension; use Drupal\Component\Graph\Graph; +use Symfony\Component\Yaml\Parser; use Drupal\Component\Utility\NestedArray; use Drupal\Core\Cache\CacheBackendInterface; use Drupal\Core\KeyValueStore\KeyValueStoreInterface; +use Symfony\Component\DependencyInjection\ContainerInterface; /** * Class that manages enabled modules in a Drupal installation. @@ -523,4 +525,366 @@ protected function parseDependency($dependency) { } return $value; } + + public function enable($module_list, $enable_dependencies = TRUE) { + if ($enable_dependencies) { + // Get all module data so we can find dependencies and sort. + $module_data = system_rebuild_module_data(); + // Create an associative array with weights as values. + $module_list = array_flip(array_values($module_list)); + + while (list($module) = each($module_list)) { + if (!isset($module_data[$module])) { + // This module is not found in the filesystem, abort. + return FALSE; + } + if ($module_data[$module]->status) { + // Skip already enabled modules. + unset($module_list[$module]); + continue; + } + $module_list[$module] = $module_data[$module]->sort; + + // Add dependencies to the list, with a placeholder weight. + // The new modules will be processed as the while loop continues. + foreach (array_keys($module_data[$module]->requires) as $dependency) { + if (!isset($module_list[$dependency])) { + $module_list[$dependency] = 0; + } + } + } + + if (!$module_list) { + // Nothing to do. All modules already enabled. + return TRUE; + } + + // Sort the module list by pre-calculated weights. + arsort($module_list); + $module_list = array_keys($module_list); + } + + // Required for module installation checks. + include_once DRUPAL_ROOT . '/core/includes/install.inc'; + + $modules_installed = array(); + $modules_enabled = array(); + $module_config = config('system.module'); + $disabled_config = config('system.module.disabled'); + foreach ($module_list as $module) { + // Only process modules that are not already enabled. + // A module is only enabled if it is configured as enabled. Custom or + // overridden module handlers might contain the module already, which means + // that it might be loaded, but not necessarily installed or enabled. + $enabled = $module_config->get("enabled.$module") !== NULL; + if (!$enabled) { + $weight = $disabled_config->get($module); + if ($weight === NULL) { + $weight = 0; + } + $module_config + ->set("enabled.$module", $weight) + ->set('enabled', module_config_sort($module_config->get('enabled'))) + ->save(); + $disabled_config + ->clear($module) + ->save(); + + // Prepare the new module list, sorted by weight, including filenames. + // This list is used for both the ModuleHandler and DrupalKernel. It needs + // to be kept in sync between both. A DrupalKernel reboot or rebuild will + // automatically re-instantiate a new ModuleHandler that uses the new + // module list of the kernel. However, DrupalKernel does not cause any + // modules to be loaded. + // Furthermore, the currently active (fixed) module list can be different + // from the configured list of enabled modules. For all active modules not + // contained in the configured enabled modules, we assume a weight of 0. + $current_module_filenames = $this->getModuleList(); + $current_modules = array_fill_keys(array_keys($current_module_filenames), 0); + $current_modules = module_config_sort(array_merge($current_modules, $module_config->get('enabled'))); + $module_filenames = array(); + foreach ($current_modules as $name => $weight) { + if (isset($current_module_filenames[$name])) { + $filename = $current_module_filenames[$name]; + } + else { + $filename = drupal_get_filename('module', $name); + } + $module_filenames[$name] = $filename; + } + + // Update the module handler in order to load the module's code. + // This allows the module to participate in hooks and its existence to be + // discovered by other modules. + // The current ModuleHandler instance is obsolete with the kernel rebuild + // below. + $this->setModuleList($module_filenames); + $this->load($module); + module_load_install($module); + + // Flush theme info caches, since (testing) modules can implement + // hook_system_theme_info() to register additional themes. + system_list_reset(); + + // Update the kernel to include it. + // This reboots the kernel to register the module's bundle and its + // services in the service container. The $module_filenames argument is + // taken over as %container.modules% parameter, which is passed to a fresh + // ModuleHandler instance upon first retrieval. + // @todo install_begin_request() creates a container without a kernel. + if ($kernel = drupal_container()->get('kernel', ContainerInterface::NULL_ON_INVALID_REFERENCE)) { + $kernel->updateModules($module_filenames, $module_filenames); + } + + // Refresh the list of modules that implement bootstrap hooks. + // @see bootstrap_hooks() + _system_update_bootstrap_status(); + + // Refresh the schema to include it. + drupal_get_schema(NULL, TRUE); + // Update the theme registry to include it. + drupal_theme_rebuild(); + + // Allow modules to react prior to the installation of a module. + $this->invokeAll('modules_preinstall', array(array($module))); + + // Clear the entity info cache before importing new configuration. + entity_info_cache_clear(); + + // Now install the module if necessary. + if (drupal_get_installed_schema_version($module, TRUE) == SCHEMA_UNINSTALLED) { + drupal_install_schema($module); + + // Set the schema version to the number of the last update provided + // by the module. + $versions = drupal_get_schema_versions($module); + $version = $versions ? max($versions) : SCHEMA_INSTALLED; + + // Install default configuration of the module. + config_install_default_config('module', $module); + + // If the module has no current updates, but has some that were + // previously removed, set the version to the value of + // hook_update_last_removed(). + if ($last_removed = $this->invoke($module, 'update_last_removed')) { + $version = max($version, $last_removed); + } + drupal_set_installed_schema_version($module, $version); + // Allow the module to perform install tasks. + $this->invoke($module, 'install'); + // Record the fact that it was installed. + $modules_installed[] = $module; + watchdog('system', '%module module installed.', array('%module' => $module), WATCHDOG_INFO); + } + + // Allow modules to react prior to the enabling of a module. + entity_info_cache_clear(); + $this->invokeAll('modules_preenable', array(array($module))); + + // Enable the module. + $this->invoke($module, 'enable'); + + // Record the fact that it was enabled. + $modules_enabled[] = $module; + watchdog('system', '%module module enabled.', array('%module' => $module), WATCHDOG_INFO); + } + } + + // If any modules were newly installed, invoke hook_modules_installed(). + if (!empty($modules_installed)) { + $this->invokeAll('modules_installed', array($modules_installed)); + } + + // If any modules were newly enabled, invoke hook_modules_enabled(). + if (!empty($modules_enabled)) { + $this->invokeAll('modules_enabled', array($modules_enabled)); + } + + return TRUE; + } + + /** + * {@inheritdoc} + */ + function disable($module_list, $disable_dependents = TRUE) { + if ($disable_dependents) { + // Get all module data so we can find dependents and sort. + $module_data = system_rebuild_module_data(); + // Create an associative array with weights as values. + $module_list = array_flip(array_values($module_list)); + + $profile = drupal_get_profile(); + while (list($module) = each($module_list)) { + if (!isset($module_data[$module]) || !$module_data[$module]->status) { + // This module doesn't exist or is already disabled, skip it. + unset($module_list[$module]); + continue; + } + $module_list[$module] = $module_data[$module]->sort; + + // Add dependent modules to the list, with a placeholder weight. + // The new modules will be processed as the while loop continues. + foreach ($module_data[$module]->required_by as $dependent => $dependent_data) { + if (!isset($module_list[$dependent]) && $dependent != $profile) { + $module_list[$dependent] = 0; + } + } + } + + // Sort the module list by pre-calculated weights. + asort($module_list); + $module_list = array_keys($module_list); + } + + $invoke_modules = array(); + + $module_config = config('system.module'); + $disabled_config = config('system.module.disabled'); + foreach ($module_list as $module) { + // Only process modules that are enabled. + // A module is only enabled if it is configured as enabled. Custom or + // overridden module handlers might contain the module already, which means + // that it might be loaded, but not necessarily installed or enabled. + $enabled = $module_config->get("enabled.$module") !== NULL; + if ($enabled) { + module_load_install($module); + module_invoke($module, 'disable'); + + $disabled_config + ->set($module, $module_config->get($module)) + ->save(); + $module_config + ->clear("enabled.$module") + ->save(); + + // Update the module handler to remove the module. + // The current ModuleHandler instance is obsolete with the kernel rebuild + // below. + $module_filenames = $this->getModuleList(); + unset($module_filenames[$module]); + $this->setModuleList($module_filenames); + + // Record the fact that it was disabled. + $invoke_modules[] = $module; + watchdog('system', '%module module disabled.', array('%module' => $module), WATCHDOG_INFO); + } + } + + if (!empty($invoke_modules)) { + // @todo Most of the following should happen in above loop already. + + // Refresh the system list to exclude the disabled modules. + // @todo Only needed to rebuild theme info. + // @see system_list_reset() + system_list_reset(); + + entity_info_cache_clear(); + + // Invoke hook_modules_disabled before disabling modules, + // so we can still call module hooks to get information. + $this->invokeAll('modules_disabled', array($invoke_modules)); + _system_update_bootstrap_status(); + + // Update the kernel to exclude the disabled modules. + $enabled = $this->getModuleList(); + drupal_container()->get('kernel')->updateModules($enabled, $enabled); + + // Update the theme registry to remove the newly-disabled module. + drupal_theme_rebuild(); + } + } + + /** + * {@inheritdoc} + */ + public function uninstall($module_list = array(), $uninstall_dependents = TRUE) { + if ($uninstall_dependents) { + // Get all module data so we can find dependents and sort. + $module_data = system_rebuild_module_data(); + // Create an associative array with weights as values. + $module_list = array_flip(array_values($module_list)); + + $profile = drupal_get_profile(); + while (list($module) = each($module_list)) { + if (!isset($module_data[$module]) || drupal_get_installed_schema_version($module) == SCHEMA_UNINSTALLED) { + // This module doesn't exist or is already uninstalled. Skip it. + unset($module_list[$module]); + continue; + } + $module_list[$module] = $module_data[$module]->sort; + + // If the module has any dependents which are not already uninstalled and + // not included in the passed-in list, abort. It is not safe to uninstall + // them automatically because uninstalling a module is a destructive + // operation. + foreach (array_keys($module_data[$module]->required_by) as $dependent) { + if (!isset($module_list[$dependent]) && drupal_get_installed_schema_version($dependent) != SCHEMA_UNINSTALLED && $dependent != $profile) { + return FALSE; + } + } + } + + // Sort the module list by pre-calculated weights. + asort($module_list); + $module_list = array_keys($module_list); + } + + $schema_store = \Drupal::keyValue('system.schema'); + $disabled_config = config('system.module.disabled'); + foreach ($module_list as $module) { + // Uninstall the module. + module_load_install($module); + $this->invoke($module, 'uninstall'); + drupal_uninstall_schema($module); + + // Remove all configuration belonging to the module. + config_uninstall_default_config('module', $module); + + // Remove any cache bins defined by the module. + $service_yaml_file = drupal_get_path('module', $module) . "/$module.services.yml"; + if (file_exists($service_yaml_file)) { + $parser = new Parser; + $definitions = $parser->parse(file_get_contents($service_yaml_file)); + if (isset($definitions['services'])) { + foreach ($definitions['services'] as $id => $definition) { + if (isset($definition['tags'])) { + foreach ($definition['tags'] as $tag) { + // This works for the default cache registration and even in some + // cases when a non-default "super" factory is used. That should + // be extremely rare. + if ($tag['name'] == 'cache.bin' && isset($definition['factory_service']) && isset($definition['factory_method']) && !empty($definition['arguments'])) { + try { + $factory = \Drupal::service($definition['factory_service']); + if (method_exists($factory, $definition['factory_method'])) { + $backend = call_user_func_array(array($factory, $definition['factory_method']), $definition['arguments']); + if ($backend instanceof CacheBackendInterface) { + $backend->removeBin(); + } + } + } + catch (\Exception $e) { + watchdog_exception('system', $e, 'Failed to remove cache bin defined by the service %id.', array('%id' => $id)); + } + } + } + } + } + } + } + + watchdog('system', '%module module uninstalled.', array('%module' => $module), WATCHDOG_INFO); + $schema_store->delete($module); + $disabled_config->clear($module); + } + $disabled_config->save(); + drupal_get_installed_schema_version(NULL, TRUE); + + if (!empty($module_list)) { + // Let other modules react. + $this->invokeAll('modules_uninstalled', array($module_list)); + } + + return TRUE; + } + } diff --git a/core/lib/Drupal/Core/Extension/ModuleHandlerInterface.php b/core/lib/Drupal/Core/Extension/ModuleHandlerInterface.php index 71c5dd9..7af9ab4 100644 --- a/core/lib/Drupal/Core/Extension/ModuleHandlerInterface.php +++ b/core/lib/Drupal/Core/Extension/ModuleHandlerInterface.php @@ -262,4 +262,81 @@ public function invokeAll($hook, $args = array()); */ public function alter($type, &$data, &$context1 = NULL, &$context2 = NULL); + /** + * Enables or installs a given list of modules. + * + * Definitions: + * - "Enabling" is the process of activating a module for use by Drupal. + * - "Disabling" is the process of deactivating a module. + * - "Installing" is the process of enabling it for the first time or after it + * has been uninstalled. + * - "Uninstalling" is the process of removing all traces of a module. + * + * Order of events: + * - Gather and add module dependencies to $module_list (if applicable). + * - For each module that is being enabled: + * - Install module schema and update system registries and caches. + * - If the module is being enabled for the first time or had been + * uninstalled, invoke hook_install() and add it to the list of installed + * modules. + * - Invoke hook_enable(). + * - Invoke hook_modules_installed(). + * - Invoke hook_modules_enabled(). + * + * @param $module_list + * An array of module names. + * @param $enable_dependencies + * If TRUE, dependencies will automatically be added and enabled in the + * correct order. This incurs a significant performance cost, so use FALSE + * if you know $module_list is already complete and in the correct order. + * + * @return + * FALSE if one or more dependencies are missing, TRUE otherwise. + * + * @see hook_install() + * @see hook_enable() + * @see hook_modules_installed() + * @see hook_modules_enabled() + */ + public function enable($module_list, $enable_dependencies = TRUE); + + /** + * Disables a given set of modules. + * + * @param $module_list + * An array of module names. + * @param $disable_dependents + * If TRUE, dependent modules will automatically be added and disabled in the + * correct order. This incurs a significant performance cost, so use FALSE + * if you know $module_list is already complete and in the correct order. + * @return + * + */ + public function disable($module_list, $disable_dependents = TRUE); + + /** + * Uninstalls a given list of disabled modules. + * + * @param array $module_list + * The modules to uninstall. It is the caller's responsibility to ensure that + * all modules in this list have already been disabled before this function + * is called. + * @param bool $uninstall_dependents + * (optional) If TRUE, the function will check that all modules which depend + * on the passed-in module list either are already uninstalled or contained in + * the list, and it will ensure that the modules are uninstalled in the + * correct order. This incurs a significant performance cost, so use FALSE if + * you know $module_list is already complete and in the correct order. + * Defaults to TRUE. + * + * @return bool + * Returns TRUE if the operation succeeds or FALSE if it aborts due to an + * unsafe condition, namely, $uninstall_dependents is TRUE and a module in + * $module_list has dependents which are not already uninstalled and not also + * included in $module_list). + * + * @see module_disable() + */ + public function uninstall($module_list = array(), $uninstall_dependents = TRUE); + } diff --git a/core/lib/Drupal/Core/Extension/UpdateModuleHandler.php b/core/lib/Drupal/Core/Extension/UpdateModuleHandler.php new file mode 100644 index 0000000..a4cc866 --- /dev/null +++ b/core/lib/Drupal/Core/Extension/UpdateModuleHandler.php @@ -0,0 +1,132 @@ + $spec) { + db_create_table($table, $spec); + } + } + // Enable the module with a weight of 0. + $module_config = config('system.module'); + $module_config + ->set("enabled.$module", 0) + ->set('enabled', module_config_sort($module_config->get('enabled'))) + ->save(); + // Ensure the module is not contained in disabled modules. + config('system.module.disabled') + ->clear($module) + ->save(); + + $current_schema = $schema_store->get($module); + // Set the schema version if the module was not just disabled before. + if ($current_schema === NULL || $current_schema === SCHEMA_UNINSTALLED) { + // Change the schema version to the given value (defaults to 0), so any + // module updates since the module's inception are executed in a core + // upgrade. + $schema_store->set($module, 0); + $old_schema[$module] = SCHEMA_UNINSTALLED; + } + else { + $old_schema[$module] = $current_schema; + } + + // Copy the default configuration of the module into the active storage. + // The default configuration is not altered in any way, and since the module + // is just being installed, none of its configuration can exist already, so + // this is a plain copy operation from one storage to another. + $module_config_path = drupal_get_path('module', $module) . '/config'; + if (is_dir($module_config_path)) { + $module_filestorage = new FileStorage($module_config_path); + $config_storage = drupal_container()->get('config.storage'); + foreach ($module_filestorage->listAll() as $config_name) { + // If this file already exists, something in the upgrade path went + // completely wrong and we want to know. + if ($config_storage->exists($config_name)) { + throw new ConfigException(format_string('Default configuration file @name of @module module unexpectedly exists already before the module was installed.', array( + '@module' => $module, + '@name' => $config_name, + ))); + } + $config_storage->write($config_name, $module_filestorage->read($config_name)); + } + } + + // system_list_reset() is in module.inc but that would only be available + // once the variable bootstrap is done. + require_once DRUPAL_ROOT . '/core/includes/module.inc'; + system_list_reset(); + $this->moduleList[$module] = drupal_get_filename('module', $module); + $this->load($module); + drupal_classloader_register($module, dirname($this->moduleList[$module])); + // @todo: figure out what to do about hook_install() and hook_enable(). + } + return $old_schema; + } + + /** + * {@inheritdoc} + */ + public function disable($module_list, $disable_dependents = TRUE) { + throw new \LogicException('Disabling modules is not supported during updates'); + } + + /** + * {@inheritdoc} + */ + public function uninstall($module_list = array(), $uninstall_dependents = TRUE) { + throw new \LogicException('Uninstalling modules is not supported during updates'); + } + +} diff --git a/core/lib/Drupal/Core/Routing/RouteBuilderStatic.php b/core/lib/Drupal/Core/Routing/RouteBuilderStatic.php new file mode 100644 index 0000000..9c2df6d --- /dev/null +++ b/core/lib/Drupal/Core/Routing/RouteBuilderStatic.php @@ -0,0 +1,14 @@ +condition('aid', 'system_block_ip_action') ->execute(); // Enable the new Ban module. - update_module_enable(array('ban')); + module_enable(array('ban')); } else { // Drop old table. @@ -1551,7 +1552,7 @@ function system_update_8020() { */ function system_update_8021() { // Enable the module without re-installing the schema. - update_module_enable(array('action')); + module_enable(array('action')); // Rename former System module actions. $map = array( 'system_message_action' => 'action_message_action', @@ -1856,7 +1857,7 @@ function system_update_8041() { * Enable the new Entity module. */ function system_update_8042() { - update_module_enable(array('entity')); + module_enable(array('entity')); } /** @@ -1921,18 +1922,21 @@ function system_update_8045() { function system_update_8046() { $front_page = config('system.site')->get('page.front'); if (!isset($front_page) || $front_page == 'node') { - update_module_enable(array('views')); - - // Register views to the container, so views can use it's services. - $module_list = drupal_container()->getParameter('container.modules'); - drupal_load('module', 'views'); - - drupal_container()->get('kernel')->updateModules($module_list, array('views' => 'core/modules/views/views.module')); - - // This does not fire a hook just calls views. - config_install_default_config('module', 'views'); // This imports the node frontpage view. - config_install_default_config('module', 'node'); + $module = 'node'; + $config_name = 'views.view.frontpage'; + $module_config_path = drupal_get_path('module', $module) . '/config'; + $module_filestorage = new FileStorage($module_config_path); + $config_storage = drupal_container()->get('config.storage'); + // If this file already exists, something in the upgrade path went + // completely wrong and we want to know. + if ($config_storage->exists($config_name)) { + throw new ConfigException(format_string('Default configuration file @name of @module module unexpectedly exists already before the module was installed.', array( + '@module' => $module, + '@name' => $config_name, + ))); + } + $config_storage->write($config_name, $module_filestorage->read($config_name)); } } @@ -1959,7 +1963,7 @@ function system_update_8047() { */ function system_update_8048() { // Enable the module without re-installing the schema. - update_module_enable(array('menu_link')); + module_enable(array('menu_link')); // Add the langcode column if it doesn't exist. if (!db_field_exists('menu_inks', 'langcode')) { diff --git a/core/modules/toolbar/toolbar.install b/core/modules/toolbar/toolbar.install index 6732bb9..3d4f517 100644 --- a/core/modules/toolbar/toolbar.install +++ b/core/modules/toolbar/toolbar.install @@ -18,7 +18,7 @@ */ function toolbar_update_8000() { // Enable the modules without re-installing the schema. - update_module_enable(array('breakpoint')); + module_enable(array('breakpoint')); } /** diff --git a/core/modules/user/user.install b/core/modules/user/user.install index 4f22958..3fba4b2 100644 --- a/core/modules/user/user.install +++ b/core/modules/user/user.install @@ -658,7 +658,8 @@ function user_update_8011() { if (!module_exists('image')) { // Install image.module with schema version 8002 as a previous version // would have to create tables that would be removed again. - $old_schema = update_module_enable(array('image'), 8002); + $old_schema = module_enable(array('image')); + update_set_schema('image', 8002); if ($old_schema['image'] == SCHEMA_UNINSTALLED) { // If image.module was not installed before, install default // configuration and run the install hook. diff --git a/core/modules/views/views.install b/core/modules/views/views.install index aea8d0c..d3c0dee 100644 --- a/core/modules/views/views.install +++ b/core/modules/views/views.install @@ -17,7 +17,7 @@ function views_install() { /** * Provide an initial schema. * - * @see update_module_enable(). + * @see UpdateModuleHandler::enable(). */ function views_schema_0() { module_load_install('system'); diff --git a/core/update.php b/core/update.php index cee23c4..47af383 100644 --- a/core/update.php +++ b/core/update.php @@ -15,6 +15,7 @@ */ use Symfony\Component\HttpFoundation\Request; +use Drupal\Core\DrupalKernel; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\DependencyInjection\Reference; @@ -134,7 +135,7 @@ function update_script_selection_form($form, &$form_state) { ); // No updates to run, so caches won't get flushed later. Clear them now. - drupal_flush_all_caches(); + update_flush_all_caches(); } else { $form['help'] = array( @@ -179,6 +180,20 @@ function update_helpful_links() { } /** + * Remove update overrides and flush all caches. + * + * This needs to be ran once all (if any) updates are ran. Do not call this + * while updates are running. + */ +function update_flush_all_caches() { + unset($GLOBALS['conf']['container_bundles']['UpdateBundle']); + Drupal::service('kernel')->updateModules(Drupal::moduleHandler()->getModuleList()); + + // No updates to run, so caches won't get flushed later. Clear them now. + drupal_flush_all_caches(); +} + +/** * Displays results of the update script with any accompanying errors. */ function update_results_page() {