Last active
December 22, 2015 08:19
-
-
Save amcgowanca/6444553 to your computer and use it in GitHub Desktop.
Allows for a multiple inheritance of installation profiles to exist in Drupal 7.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff --git a/includes/common.inc b/includes/common.inc | |
index 262e1c5..b38425a 100644 | |
--- a/includes/common.inc | |
+++ b/includes/common.inc | |
@@ -232,6 +232,24 @@ function drupal_get_profile() { | |
return $profile; | |
} | |
+/** | |
+ * Returns an array of the installation profile hierarchy. | |
+ * | |
+ * @return $profiles | |
+ * An array of installation profiles. | |
+ */ | |
+function drupal_get_profiles() { | |
+ global $install_state; | |
+ | |
+ if (!empty($install_state['profiles'])) { | |
+ $profiles = $install_state['profiles']; | |
+ } | |
+ else { | |
+ $profiles = variable_get('install_profiles', array(drupal_get_profile())); | |
+ } | |
+ | |
+ return $profiles; | |
+} | |
/** | |
* Sets the breadcrumb trail for the current page. | |
@@ -5342,25 +5360,26 @@ function drupal_system_listing($mask, $directory, $key = 'name', $min_depth = 1) | |
$searchdir = array($directory); | |
$files = array(); | |
- | |
+ $installed_profiles = array_reverse(drupal_get_profiles()); | |
// The 'profiles' directory contains pristine collections of modules and | |
// themes as organized by a distribution. It is pristine in the same way | |
// that /modules is pristine for core; users should avoid changing anything | |
// there in favor of sites/all or sites/<domain> directories. | |
$profiles = array(); | |
- $profile = drupal_get_profile(); | |
// For SimpleTest to be able to test modules packaged together with a | |
// distribution we need to include the profile of the parent site (in which | |
// test runs are triggered). | |
if (drupal_valid_test_ua()) { | |
$testing_profile = variable_get('simpletest_parent_profile', FALSE); | |
- if ($testing_profile && $testing_profile != $profile) { | |
+ if ($testing_profile && !in_array($testing_profile, $installed_profiles)) { | |
$profiles[] = $testing_profile; | |
} | |
} | |
// In case both profile directories contain the same extension, the actual | |
// profile always has precedence. | |
- $profiles[] = $profile; | |
+ | |
+ $profiles = array_merge($profiles, $installed_profiles); | |
+ | |
foreach ($profiles as $profile) { | |
if (file_exists("profiles/$profile/$directory")) { | |
$searchdir[] = "profiles/$profile/$directory"; | |
diff --git a/includes/install.core.inc b/includes/install.core.inc | |
index 7a694e9..75996f5 100644 | |
--- a/includes/install.core.inc | |
+++ b/includes/install.core.inc | |
@@ -162,8 +162,10 @@ function install_state_defaults() { | |
// An array of information about the chosen installation profile. This will | |
// be filled in based on the profile's .info file. | |
'profile_info' => array(), | |
- // An array of available installation profiles. | |
+ // An array of the profile hiearchy. | |
'profiles' => array(), | |
+ // An array of available installation profiles. | |
+ 'profiles_available' => array(), | |
// An array of server variables that will be substituted into the global | |
// $_SERVER array via drupal_override_server_variables(). Used by | |
// non-interactive installations only. | |
@@ -526,14 +528,14 @@ function install_tasks($install_state) { | |
$tasks = array( | |
'install_select_profile' => array( | |
'display_name' => st('Choose profile'), | |
- 'display' => count($install_state['profiles']) != 1, | |
+ 'display' => count($install_state['profiles_available']) != 1, | |
'run' => INSTALL_TASK_RUN_IF_REACHED, | |
), | |
- 'install_select_locale' => array( | |
- 'display_name' => st('Choose language'), | |
+ 'install_load_profile' => array( | |
'run' => INSTALL_TASK_RUN_IF_REACHED, | |
), | |
- 'install_load_profile' => array( | |
+ 'install_select_locale' => array( | |
+ 'display_name' => st('Choose language'), | |
'run' => INSTALL_TASK_RUN_IF_REACHED, | |
), | |
'install_verify_requirements' => array( | |
@@ -550,7 +552,7 @@ function install_tasks($install_state) { | |
'run' => INSTALL_TASK_RUN_IF_REACHED, | |
), | |
'install_profile_modules' => array( | |
- 'display_name' => count($install_state['profiles']) == 1 ? st('Install site') : st('Install profile'), | |
+ 'display_name' => count($install_state['profiles_available']) == 1 ? st('Install site') : st('Install profile'), | |
'type' => 'batch', | |
), | |
'install_import_locales' => array( | |
@@ -565,19 +567,21 @@ function install_tasks($install_state) { | |
), | |
); | |
- // Now add any tasks defined by the installation profile. | |
- 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_file = DRUPAL_ROOT . '/profiles/' . $install_state['parameters']['profile'] . '/' . $install_state['parameters']['profile'] . '.install'; | |
- if (file_exists($profile_install_file)) { | |
- include_once $profile_install_file; | |
- } | |
- $function = $install_state['parameters']['profile'] . '_install_tasks'; | |
- if (function_exists($function)) { | |
- $result = $function($install_state); | |
- if (is_array($result)) { | |
- $tasks += $result; | |
+ // Now add any tasks defined by the installation profiles. | |
+ if (!empty($install_state['profiles'])) { | |
+ $profiles = array_reverse($install_state['profiles']); | |
+ foreach ($profiles as $profile) { | |
+ $profile_install_file = DRUPAL_ROOT . '/profiles/' . $profile . '/' . $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; | |
+ } | |
} | |
} | |
} | |
@@ -595,14 +599,17 @@ function install_tasks($install_state) { | |
), | |
); | |
- // Allow the installation profile to modify the full list of tasks. | |
- if (!empty($install_state['parameters']['profile'])) { | |
- $profile_file = DRUPAL_ROOT . '/profiles/' . $install_state['parameters']['profile'] . '/' . $install_state['parameters']['profile'] . '.profile'; | |
- if (file_exists($profile_file)) { | |
- include_once $profile_file; | |
- $function = $install_state['parameters']['profile'] . '_install_tasks_alter'; | |
- if (function_exists($function)) { | |
- $function($tasks, $install_state); | |
+ // Allow for the installation profiles to modify the full list of tasks. | |
+ if (!empty($install_state['profiles'])) { | |
+ $profiles = array_reverse($install_state['profiles']); | |
+ foreach ($profiles as $profile) { | |
+ $profile_file = DRUPAL_ROOT . '/profiles/' . $profile . '/' . $profile . '.profile'; | |
+ if (file_exists($profile_file)) { | |
+ include_once $profile_file; | |
+ $function = $profile . '_install_tasks_alter'; | |
+ if (function_exists($function)) { | |
+ $function($tasks, $install_state); | |
+ } | |
} | |
} | |
} | |
@@ -772,11 +779,26 @@ function install_system_module(&$install_state) { | |
// Save the list of other modules to install for the upcoming tasks. | |
// variable_set() can be used now that system.module is installed. | |
- $modules = $install_state['profile_info']['dependencies']; | |
+ $modules = array(); | |
+ $profiles = array($install_state['parameters']['profile']); | |
+ $profile_info = $install_state['profile_info']; | |
+ do { | |
+ $modules = array_merge($modules, $profile_info['dependencies']); | |
+ if (!empty($profile_info['base profile']) && isset($profile_info[$profile_info['base profile']])) { | |
+ $profiles[] = $profile_info['base profile']; | |
+ $profile_info = $profile_info[$profile_info['base profile']]; | |
+ } | |
+ else { | |
+ $profile_info = NULL; | |
+ } | |
+ } | |
+ while ($profile_info); | |
// The installation profile is also a module, which needs to be installed | |
// after all the dependencies have been installed. | |
- $modules[] = drupal_get_profile(); | |
+ foreach ($profiles as $profile) { | |
+ $modules[] = $profile; | |
+ } | |
variable_set('install_profile_modules', array_diff($modules, array('system'))); | |
$install_state['database_tables_exist'] = TRUE; | |
@@ -1014,10 +1036,10 @@ function install_find_profiles() { | |
* thrown if a profile cannot be chosen automatically. | |
*/ | |
function install_select_profile(&$install_state) { | |
- $install_state['profiles'] += install_find_profiles(); | |
+ $install_state['profiles_available'] += install_find_profiles(); | |
if (empty($install_state['parameters']['profile'])) { | |
// Try to find a profile. | |
- $profile = _install_select_profile($install_state['profiles']); | |
+ $profile = _install_select_profile($install_state['profiles_available']); | |
if (empty($profile)) { | |
// We still don't have a profile, so display a form for selecting one. | |
// Only do this in the case of interactive installations, since this is | |
@@ -1027,7 +1049,7 @@ function install_select_profile(&$install_state) { | |
if ($install_state['interactive']) { | |
include_once DRUPAL_ROOT . '/includes/form.inc'; | |
drupal_set_title(st('Select an installation profile')); | |
- $form = drupal_get_form('install_select_profile_form', $install_state['profiles']); | |
+ $form = drupal_get_form('install_select_profile_form', $install_state['profiles_available']); | |
return drupal_render($form); | |
} | |
else { | |
@@ -1194,9 +1216,14 @@ function install_find_locales($profilename) { | |
* locale cannot be chosen automatically. | |
*/ | |
function install_select_locale(&$install_state) { | |
+ $locales = array(); | |
+ $profiles = array_reverse($install_state['profiles']); | |
// Find all available locales. | |
- $profilename = $install_state['parameters']['profile']; | |
- $locales = install_find_locales($profilename); | |
+ foreach ($profiles as $profile) { | |
+ $profilelocales = install_find_locales($profile); | |
+ $locales = array_merge($locales, $profilelocales); | |
+ } | |
+ $locales = array_unique($locales); | |
$install_state['locales'] += $locales; | |
if (!empty($_POST['locale'])) { | |
@@ -1208,6 +1235,7 @@ function install_select_locale(&$install_state) { | |
} | |
} | |
+ $profilename = $install_state['parameters']['profile']; | |
if (empty($install_state['parameters']['locale'])) { | |
// If only the built-in (English) language is available, and we are | |
// performing an interactive installation, inform the user that the | |
@@ -1247,14 +1275,16 @@ function install_select_locale(&$install_state) { | |
} | |
else { | |
// Allow profile to pre-select the language, skipping the selection. | |
- $function = $profilename . '_profile_details'; | |
- if (function_exists($function)) { | |
- $details = $function(); | |
- if (isset($details['language'])) { | |
- foreach ($locales as $locale) { | |
- if ($details['language'] == $locale->name) { | |
- $install_state['parameters']['locale'] = $locale->name; | |
- return; | |
+ foreach ($profiles as $profile) { | |
+ $function = $profile . '_profile_details'; | |
+ if (function_exists($function)) { | |
+ $details = $function(); | |
+ if (isset($details['language'])) { | |
+ foreach ($locales as $locale) { | |
+ if ($details['language'] == $locale->name) { | |
+ $install_state['parameters']['locale'] = $locale->name; | |
+ return; | |
+ } | |
} | |
} | |
} | |
@@ -1342,10 +1372,25 @@ function install_load_profile(&$install_state) { | |
$profile_file = DRUPAL_ROOT . '/profiles/' . $install_state['parameters']['profile'] . '/' . $install_state['parameters']['profile'] . '.profile'; | |
if (file_exists($profile_file)) { | |
include_once $profile_file; | |
- $install_state['profile_info'] = install_profile_info($install_state['parameters']['profile'], $install_state['parameters']['locale']); | |
+ $install_state['profiles'][] = $install_state['parameters']['profile']; | |
+ $install_state['profile_info'] = install_profile_info($install_state['parameters']['profile']); | |
+ $profile_info = &$install_state['profile_info']; | |
+ while (isset($profile_info['base profile']) && $profile_info['base profile']) { | |
+ $profile = $profile_info['base profile']; | |
+ $profile_file = DRUPAL_ROOT . '/profiles/' . $profile . '/' . $profile . '.profile'; | |
+ if (file_exists($profile_file)) { | |
+ include_once $profile_file; | |
+ $install_state['profiles'][] = $profile; | |
+ $profile_info[$profile] = install_profile_info($profile); | |
+ $profile_info = &$profile_info[$profile]; | |
+ } | |
+ else { | |
+ throw new Exception(install_no_profile_error()); | |
+ } | |
+ } | |
} | |
else { | |
- throw new Exception(st('Sorry, the profile you have chosen cannot be loaded.')); | |
+ throw new Exception(install_no_profile_error()); | |
} | |
} | |
@@ -1380,7 +1425,10 @@ 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 ($install_state['profiles'] as $profile) { | |
+ $files[$profile]->info['required'] = FALSE; | |
+ } | |
+ | |
// Add modules that other modules depend on. | |
foreach ($modules as $module) { | |
if ($files[$module]->requires) { | |
@@ -1541,13 +1589,19 @@ function install_finished(&$install_state) { | |
// Remember the profile which was used. | |
variable_set('install_profile', drupal_get_profile()); | |
+ variable_set('install_profiles', drupal_get_profiles()); | |
// Installation profiles are always loaded last | |
- db_update('system') | |
- ->fields(array('weight' => 1000)) | |
- ->condition('type', 'module') | |
- ->condition('name', drupal_get_profile()) | |
- ->execute(); | |
+ $profiles = drupal_get_profiles(); | |
+ $weight = 1000 + count($profiles); | |
+ foreach ($profiles as $profile) { | |
+ db_update('system') | |
+ ->fields(array('weight' => $weight)) | |
+ ->condition('type', 'module') | |
+ ->condition('name', $profile) | |
+ ->execute(); | |
+ $weight = $weight - 1; | |
+ } | |
// Cache a fully-built schema. | |
drupal_get_schema(NULL, TRUE); | |
@@ -1586,10 +1640,12 @@ function _install_profile_modules_finished($success, $results, $operations) { | |
* Checks installation requirements and reports any errors. | |
*/ | |
function install_check_requirements($install_state) { | |
- $profile = $install_state['parameters']['profile']; | |
+ $requirements = array(); | |
// Check the profile requirements. | |
- $requirements = drupal_check_profile($profile); | |
+ foreach ($install_state['profiles'] as $profile) { | |
+ $requirements += drupal_check_profile($profile); | |
+ } | |
// If Drupal is not set up already, we need to create a settings file. | |
if (!$install_state['settings_verified']) { | |
diff --git a/includes/install.inc b/includes/install.inc | |
index 3631c36..60b976c 100644 | |
--- a/includes/install.inc | |
+++ b/includes/install.inc | |
@@ -669,18 +669,31 @@ function drupal_rewrite_settings($settings = array(), $prefix = '') { | |
* The list of modules to install. | |
*/ | |
function drupal_verify_profile($install_state) { | |
- $profile = $install_state['parameters']['profile']; | |
+ $profiles = $install_state['profiles']; | |
$locale = $install_state['parameters']['locale']; | |
include_once DRUPAL_ROOT . '/includes/file.inc'; | |
include_once DRUPAL_ROOT . '/includes/common.inc'; | |
- $profile_file = DRUPAL_ROOT . "/profiles/$profile/$profile.profile"; | |
- | |
- if (!isset($profile) || !file_exists($profile_file)) { | |
+ if (!empty($profiles)) { | |
+ foreach ($profiles as $profile) { | |
+ $profile_file = DRUPAL_ROOT . '/profiles/' . $profile . '/' . $profile . '.profile'; | |
+ if (!file_exists($profile_file)) { | |
+ throw new Exception(install_no_profile_error()); | |
+ } | |
+ } | |
+ } | |
+ else { | |
throw new Exception(install_no_profile_error()); | |
} | |
+ | |
$info = $install_state['profile_info']; | |
+ $dependencies = $info['dependencies']; | |
+ | |
+ while (isset($info['base profile']) && $info[$info['base profile']]) { | |
+ $info = $info[$info['base profile']]; | |
+ $dependencies += $info['dependencies']; | |
+ } | |
// Get a list of modules that exist in Drupal's assorted subdirectories. | |
$present_modules = array(); | |
@@ -690,10 +703,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, drupal_get_profiles()); | |
// Verify that all of the profile's required modules are present. | |
- $missing_modules = array_diff($info['dependencies'], $present_modules); | |
+ $missing_modules = array_diff($dependencies, $present_modules); | |
$requirements = array(); | |
@@ -1287,6 +1300,7 @@ function install_profile_info($profile, $locale = 'en') { | |
'dependencies' => array(), | |
'description' => '', | |
'distribution_name' => 'Drupal', | |
+ 'base profile' => NULL, | |
'version' => NULL, | |
'hidden' => FALSE, | |
'php' => DRUPAL_MINIMUM_PHP, | |
diff --git a/modules/system/system.module b/modules/system/system.module | |
index 2bbcd7f..ab5e996 100644 | |
--- a/modules/system/system.module | |
+++ b/modules/system/system.module | |
@@ -2363,14 +2363,20 @@ function _system_rebuild_module_data() { | |
$modules = drupal_system_listing('/^' . DRUPAL_PHP_FUNCTION_PATTERN . '\.module$/', 'modules', 'name', 0); | |
// Include the installation profile in modules that are loaded. | |
- $profile = drupal_get_profile(); | |
- $modules[$profile] = new stdClass(); | |
- $modules[$profile]->name = $profile; | |
- $modules[$profile]->uri = 'profiles/' . $profile . '/' . $profile . '.profile'; | |
- $modules[$profile]->filename = $profile . '.profile'; | |
+ $profiles = drupal_get_profiles(); | |
+ $weight = 1000 + count($profiles); | |
+ | |
+ foreach ($profiles as $profile) { | |
+ $modules[$profile] = new stdClass(); | |
+ $modules[$profile]->name = $profile; | |
+ $modules[$profile]->uri = 'profiles/' . $profile . '/' . $profile . '.profile'; | |
+ $modules[$profile]->filename = $profile . '.profile'; | |
+ | |
+ // Installation profile hooks are always executed last. | |
+ $modules[$profile]->weight = $weight; | |
+ $weight = $weight - 1; | |
+ } | |
- // Installation profile hooks are always executed last. | |
- $modules[$profile]->weight = 1000; | |
// Set defaults for module info. | |
$defaults = array( | |
@@ -2422,7 +2428,11 @@ function _system_rebuild_module_data() { | |
drupal_alter('system_info', $modules[$key]->info, $modules[$key], $type); | |
} | |
- if (isset($modules[$profile])) { | |
+ foreach ($profiles as $profile) { | |
+ if (!isset($modules[$profile])) { | |
+ continue; | |
+ } | |
+ | |
// 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 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment