Skip to content

Instantly share code, notes, and snippets.

@amcgowanca
Last active December 22, 2015 08:19
Show Gist options
  • Save amcgowanca/6444553 to your computer and use it in GitHub Desktop.
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.
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