diff --git a/core/includes/common.inc b/core/includes/common.inc index aff7665..e59526c 100644 --- a/core/includes/common.inc +++ b/core/includes/common.inc @@ -286,6 +286,26 @@ function drupal_get_profile() { } /** + * Returns a list of related installation profiles in descending order of their + * dependencies. + */ +function drupal_get_profiles($profile) { + if (!function_exists('install_get_base_profiles')) { + require_once DRUPAL_ROOT . '/core/includes/install.inc'; + } + + $profiles = array(); + + if (!empty($profile)) { + $profiles = array_reverse(install_get_base_profiles($profile)); + $profiles[] = $profile; + } + + return $profiles; +} + + +/** * Adds output to the HEAD tag of the HTML page. * * This function can be called as long as the headers aren't sent. Pass no diff --git a/core/includes/install.core.inc b/core/includes/install.core.inc index b44c59a..1cd8238 100644 --- a/core/includes/install.core.inc +++ b/core/includes/install.core.inc @@ -199,6 +199,9 @@ function install_state_defaults() { // An array of information about the chosen installation profile. This will // be filled in based on the profile's .info.yml file. 'profile_info' => array(), + // An array of information for each of the base profiles of the chosen + // installation profile. + 'base_profiles_info' => array(), // An array of available installation profiles. 'profiles' => array(), // An array of server variables that will be substituted into the global @@ -843,16 +846,17 @@ function install_tasks($install_state) { if (!empty($install_state['parameters']['profile'])) { // Load the profile install file, because it is not always loaded when // hook_install_tasks() is invoked (e.g. batch processing). - $profile = $install_state['parameters']['profile']; - $profile_install_file = dirname($install_state['profiles'][$profile]->uri) . '/' . $profile . '.install'; - if (file_exists($profile_install_file)) { - include_once DRUPAL_ROOT . '/' . $profile_install_file; - } - $function = $install_state['parameters']['profile'] . '_install_tasks'; - if (function_exists($function)) { - $result = $function($install_state); - if (is_array($result)) { - $tasks += $result; + foreach (drupal_get_profiles($install_state['parameters']['profile']) as $profile) { + $profile_install_file = dirname($install_state['profiles'][$profile]->uri). '/' . $profile . '.install'; + if (file_exists($profile_install_file)) { + include_once $profile_install_file; + } + $function = $profile . '_install_tasks'; + if (function_exists($function)) { + $result = $function($install_state); + if (is_array($result)) { + $tasks += $result; + } } } } @@ -878,13 +882,14 @@ function install_tasks($install_state) { // Allow the installation profile to modify the full list of tasks. if (!empty($install_state['parameters']['profile'])) { - $profile = $install_state['parameters']['profile']; - $profile_file = $install_state['profiles'][$profile]->uri; - if (file_exists($profile_file)) { - include_once DRUPAL_ROOT . '/' . $profile_file; - $function = $install_state['parameters']['profile'] . '_install_tasks_alter'; - if (function_exists($function)) { - $function($tasks, $install_state); + foreach (drupal_get_profiles($install_state['parameters']['profile']) as $profile) { + $profile_file = $install_state['profiles'][$profile]->uri; + if (file_exists($profile_file)) { + include_once $profile_file; + $function = $profile . '_install_tasks_alter'; + if (function_exists($function)) { + $function($tasks, $install_state); + } } } } @@ -1046,11 +1051,19 @@ function install_base_system(&$install_state) { // Save the list of other modules to install for the upcoming tasks. // State can be set to the database now that system.module is installed. - $modules = $install_state['profile_info']['dependencies']; + $profiles = drupal_get_profiles($install_state['parameters']['profile']); + $profile_dependencies = array(); + + foreach ($profiles as $profile) { + $info = install_profile_info($profile); + $profile_dependencies = array_unique(array_merge($profile_dependencies, $info['dependencies'])); + } + + $modules = $profile_dependencies; // The installation profile is also a module, which needs to be installed // after all the dependencies have been installed. - $modules[] = drupal_get_profile(); + $modules = array_merge($modules, $profiles); \Drupal::state()->set('install_profile_modules', array_diff($modules, array('system'))); $install_state['database_tables_exist'] = TRUE; @@ -1913,14 +1926,21 @@ function install_already_done_error() { * the profile cannot be loaded. */ function install_load_profile(&$install_state) { - $profile = $install_state['parameters']['profile']; - $profile_file = $install_state['profiles'][$profile]->uri; - if (file_exists($profile_file)) { - include_once DRUPAL_ROOT . '/' . $profile_file; - $install_state['profile_info'] = install_profile_info($install_state['parameters']['profile'], $install_state['parameters']['langcode']); - } - else { - throw new Exception(t('Sorry, the profile you have chosen cannot be loaded.')); + foreach (drupal_get_profiles($install_state['parameters']['profile']) as $profile) { + $profile_file = $install_state['profiles'][$profile]->uri; + if (file_exists($profile_file)) { + include_once $profile_file; + + if ($profile == $install_state['parameters']['profile']) { + $install_state['profile_info'] = install_profile_info($profile, $install_state['parameters']['langcode']); + } + else { + $install_state['base_profiles_info'][] = install_profile_info($profile, $install_state['parameters']['langcode']); + } + } + else { + throw new Exception(t('Sorry, the profile you have chosen cannot be loaded.')); + } } } @@ -1957,7 +1977,9 @@ function install_profile_modules(&$install_state) { // Although the profile module is marked as required, it needs to go after // every dependency, including non-required ones. So clear its required // flag for now to allow it to install late. - $files[$install_state['parameters']['profile']]->info['required'] = FALSE; + foreach (drupal_get_profiles($install_state['parameters']['profile']) as $profile) { + $files[$profile]->info['required'] = FALSE; + } // Add modules that other modules depend on. foreach ($modules as $module) { if ($files[$module]->requires) { diff --git a/core/includes/install.inc b/core/includes/install.inc index 256c79e..09d6601 100644 --- a/core/includes/install.inc +++ b/core/includes/install.inc @@ -569,10 +569,23 @@ function drupal_verify_profile($install_state) { include_once __DIR__ . '/file.inc'; include_once __DIR__ . '/common.inc'; - $profile = $install_state['parameters']['profile']; - $profile_file = $install_state['profiles'][$profile]->uri; + $selected_profile = $install_state['parameters']['profile']; + $profiles = drupal_get_profiles($selected_profile); - if (!isset($profile) || !file_exists($profile_file)) { + if (!empty($profiles)) { + $profile_dependencies = array(); + + foreach ($profiles as $profile) { + $profile_file = $install_state['profiles'][$profile]->uri; + + if (!file_exists($profile_file)) { + throw new Exception(install_no_profile_error()); + } + $info = install_profile_info($profile); + $profile_dependencies = array_unique(array_merge($profile_dependencies, $info['dependencies'])); + } + } + else { throw new Exception(install_no_profile_error()); } $info = $install_state['profile_info']; @@ -585,10 +598,10 @@ function drupal_verify_profile($install_state) { // The installation profile is also a module, which needs to be installed // after all the other dependencies have been installed. - $present_modules[] = drupal_get_profile(); + $present_modules = array_merge($present_modules, $profiles); // Verify that all of the profile's required modules are present. - $missing_modules = array_diff($info['dependencies'], $present_modules); + $missing_modules = array_diff($profile_dependencies, $present_modules); $requirements = array(); @@ -951,23 +964,33 @@ function drupal_requirements_url($severity) { function drupal_check_profile($profile, array $install_state) { include_once __DIR__ . '/file.inc'; - $profile_file = $install_state['profiles'][$profile]->uri; - - if (!isset($profile) || !file_exists($profile_file)) { - throw new Exception(install_no_profile_error()); - } + $requirements = array(); + if (isset($profile)) { + $profile_dependencies = array(); - $info = install_profile_info($profile); + // Loop through all active installation profiles to collect dependencies. + foreach (drupal_get_profiles($profile) as $profile) { + $profile_file = $install_state['profiles'][$profile]->uri; - // Collect requirement testing results. - $requirements = array(); - foreach ($info['dependencies'] as $module) { - module_load_install($module); - $function = $module . '_requirements'; - if (function_exists($function)) { - $requirements = array_merge($requirements, $function('install')); + if (!file_exists($profile_file)) { + throw new Exception(install_no_profile_error()); + } + $info = install_profile_info($profile); + $profile_dependencies = array_unique(array_merge($profile_dependencies, $info['dependencies'])); + } + // Collect requirement testing results. + foreach ($profile_dependencies as $module) { + module_load_install($module); + $function = $module . '_requirements'; + if (function_exists($function)) { + $requirements = array_merge($requirements, $function('install')); + } } } + else { + throw new Exception(install_no_profile_error()); + } + return $requirements; } @@ -1095,6 +1118,31 @@ function install_profile_info($profile, $langcode = 'en') { } /** + * + * Returns a list of base installation profiles associated with the specified + * installation profile. + * + * @return + * An array of profile names. An empty array if installation profile has no + * parent profiles. + */ +function install_get_base_profiles($profile) { + $cache = &drupal_static(__FUNCTION__, array()); + + if (!isset($cache[$profile])) { + $profile_file = drupal_get_path('profile', $profile) . "/$profile.info.yml"; + $info = drupal_parse_info_file($profile_file); + $cache[$profile] = array(); + + while (!empty($info) && isset($info['base'])) { + $cache[$profile][] = $info['base']; + $info = install_profile_info($info['base']); + } + } + + return $cache[$profile]; +} + +/** * Ensures the environment for a Drupal database on a predefined connection. * * This will run tasks that check that Drupal can perform all of the functions diff --git a/core/includes/module.inc b/core/includes/module.inc index 429f5db..119ae20 100644 --- a/core/includes/module.inc +++ b/core/includes/module.inc @@ -289,7 +289,7 @@ function drupal_required_modules() { $required = array(); // An installation profile is required and one must always be loaded. - $required[] = drupal_get_profile(); + $required = array_merge($required, drupal_get_profiles(drupal_get_profile())); foreach ($files as $name => $file) { $info = \Drupal::service('info_parser')->parse($file->uri); diff --git a/core/lib/Drupal/Core/Extension/ModuleHandler.php b/core/lib/Drupal/Core/Extension/ModuleHandler.php index 92cf2a4..b01e180 100644 --- a/core/lib/Drupal/Core/Extension/ModuleHandler.php +++ b/core/lib/Drupal/Core/Extension/ModuleHandler.php @@ -693,7 +693,7 @@ public function uninstall(array $module_list, $uninstall_dependents = TRUE) { } // Skip already uninstalled modules. - if (isset($installed_modules[$dependent]) && !isset($module_list[$dependent]) && $dependent != $profile) { + if (isset($installed_modules[$dependent]) && !isset($module_list[$dependent]) && !in_array($dependent, $profiles)) { $module_list[$dependent] = TRUE; } } diff --git a/core/modules/system/lib/Drupal/system/Form/ModulesUninstallForm.php b/core/modules/system/lib/Drupal/system/Form/ModulesUninstallForm.php index 711f934..0fb87a4 100644 --- a/core/modules/system/lib/Drupal/system/Form/ModulesUninstallForm.php +++ b/core/modules/system/lib/Drupal/system/Form/ModulesUninstallForm.php @@ -82,7 +82,7 @@ public function buildForm(array $form, array &$form_state) { return $form; } - $profile = drupal_get_profile(); + $profiles = drupal_get_profiles(drupal_get_profile()); // Sort all modules by their name. $this->moduleHandler->loadInclude('system', 'inc', 'system.admin'); @@ -105,7 +105,7 @@ public function buildForm(array $form, array &$form_state) { // we can allow this module to be uninstalled. (The installation profile // is excluded from this list.) foreach (array_keys($module->required_by) as $dependent) { - if ($dependent != $profile && drupal_get_installed_schema_version($dependent) != SCHEMA_UNINSTALLED) { + if (!in_array($dependent, $profiles) && drupal_get_installed_schema_version($dependent) != SCHEMA_UNINSTALLED) { $name = isset($modules[$dependent]->info['name']) ? $modules[$dependent]->info['name'] : $dependent; $form['modules'][$module->name]['#required_by'][] = $name; $form['uninstall'][$module->name]['#disabled'] = TRUE; diff --git a/core/modules/system/system.install b/core/modules/system/system.install index 14153a0..d36d0a9 100644 --- a/core/modules/system/system.install +++ b/core/modules/system/system.install @@ -453,11 +453,11 @@ function system_requirements($phase) { // Display an error if a newly introduced dependency in a module is not resolved. if ($phase == 'update') { - $profile = drupal_get_profile(); + $profile = drupal_get_profiles(drupal_get_profile()); $files = system_rebuild_module_data(); foreach ($files as $module => $file) { // Ignore disabled modules and installation profiles. - if (!$file->status || $module == $profile) { + if (!$file->status || in_array($module, $profiles)) { continue; } // Check the module's PHP version. diff --git a/core/modules/system/system.module b/core/modules/system/system.module index d922cd9..da1a47a 100644 --- a/core/modules/system/system.module +++ b/core/modules/system/system.module @@ -2332,12 +2332,17 @@ function _system_rebuild_module_data() { // Find installation profiles. $profiles = drupal_system_listing('/^' . DRUPAL_PHP_FUNCTION_PATTERN . '\.profile$/', 'profiles', 'name', 0); - // Include the installation profile in modules that are loaded. - $profile = drupal_get_profile(); - $modules[$profile] = $profiles[$profile]; + // Include the active installation profile(s) in modules that are loaded. + $profile_weight = 1000; + $base_profiles = drupal_get_profiles(drupal_get_profile()); // Installation profile hooks are always executed last. - $modules[$profile]->weight = 1000; + foreach ($base_profiles as $profile) { + $modules[$profile] = $profiles[$profile]; + + // Installation profile hooks are always executed last. + $modules[$profile]->weight = $profile_weight++; + } // Set defaults for module info. $defaults = array( @@ -2383,13 +2388,15 @@ function _system_rebuild_module_data() { drupal_alter('system_info', $modules[$key]->info, $modules[$key], $type); } - if (isset($modules[$profile])) { - // The installation profile is required, if it's a valid module. - $modules[$profile]->info['required'] = TRUE; - // Add a default distribution name if the profile did not provide one. This - // matches the default value used in install_profile_info(). - if (!isset($modules[$profile]->info['distribution_name'])) { - $modules[$profile]->info['distribution_name'] = 'Drupal'; + foreach ($base_profiles as $profile) { + if (isset($modules[$profile])) { + // The installation profile is required, if it's a valid module. + $modules[$profile]->info['required'] = TRUE; + // Add a default distribution name if the profile did not provide one. + // This matches the default value used in install_profile_info(). + if (in_array($key, $profiles) && !isset($modules[$key]->info['hidden'])) { + $modules[$profile]->info['distribution_name'] = 'Drupal'; + } } }