Commit 716293e0 authored by webchick's avatar webchick

#509398 by adrian: Turned install profiles into modules with full access to...

#509398 by adrian: Turned install profiles into modules with full access to the Drupal API. Almost all WTFs/minute now removed from install profiles. Woohoo! :D
parent d151ea91
......@@ -119,6 +119,32 @@ function drupal_get_region_content($region = NULL, $delimiter = ' ') {
}
}
/**
* Get the name of the currently active install profile.
*
* When this function is called during Drupal's initial installation process,
* the name of the profile that's about to be installed is stored in the global
* installation state. At all other times, the standard Drupal systems variable
* table contains the name of the current profile, and we can call variable_get()
* to determine what one is active.
*
* @return $profile
* The name of the install profile.
*/
function drupal_get_profile() {
global $install_state;
if (isset($install_state['parameters']['profile'])) {
$profile = $install_state['parameters']['profile'];
}
else {
$profile = variable_get('install_profile', 'default');
}
return $profile;
}
/**
* Set the breadcrumb trail for the current page.
*
......@@ -3724,20 +3750,10 @@ function drupal_cron_cleanup() {
* An array of file objects of the specified type.
*/
function drupal_system_listing($mask, $directory, $key = 'name', $min_depth = 1) {
global $install_state;
$config = conf_path();
// When this function is called during Drupal's initial installation process,
// the name of the profile that's about to be installed is stored in the global
// installation state. At all other times, the standard Drupal systems variable
// table contains the name of the current profile, and we can call variable_get()
// to determine what one is active.
if (isset($install_state['parameters']['profile'])) {
$profile = $install_state['parameters']['profile'];
}
else {
$profile = variable_get('install_profile', 'default');
}
$profile = drupal_get_profile();
$searchdir = array($directory);
$files = array();
......
......@@ -166,16 +166,12 @@ function drupal_set_installed_schema_version($module, $version) {
*/
function drupal_install_profile_name() {
global $install_state;
$profile = $install_state['parameters']['profile'];
static $name = NULL;
if (!isset($name)) {
// Load profile details.
$function = $profile . '_profile_details';
if (function_exists($function)) {
$details = $function();
}
$name = isset($details['name']) ? $details['name'] : 'Drupal';
if (isset($install_state['profile_info']['name'])) {
$name = $install_state['profile_info']['name'];
}
else {
$name = 'Drupal';
}
return $name;
......@@ -522,6 +518,10 @@ function drupal_verify_profile($install_state) {
$present_modules[] = $present_module->name;
}
// The install profile is also a module, which needs to be installed after all the other dependencies
// have been installed.
$present_modules[] = drupal_get_profile();
// Verify that all of the profile's required modules are present.
$missing_modules = array_diff($info['dependencies'], $present_modules);
......@@ -1087,6 +1087,11 @@ function install_profile_info($profile, $locale = 'en') {
$info['dependencies'],
($locale != 'en' && !empty($locale) ? array('locale') : array()))
);
// drupal_required_modules() includes the current profile as a dependency.
// Since a module can't depend on itself we remove that element of the array.
array_shift($info['dependencies']);
$cache[$profile] = $info;
}
return $cache[$profile];
......
......@@ -502,11 +502,16 @@ function module_invoke_all() {
function drupal_required_modules() {
$files = drupal_system_listing('/\.info$/', 'modules', 'name', 0);
$required = array();
// An install profile is required and one must always be loaded.
$required[] = drupal_get_profile();
foreach ($files as $name => $file) {
$info = drupal_parse_info_file($file->uri);
if (!empty($info) && !empty($info['required']) && $info['required']) {
$required[] = $name;
}
}
return $required;
}
......@@ -255,9 +255,79 @@ function update_fix_d7_requirements() {
}
}
update_fix_d7_install_profile();
return $ret;
}
/**
* Register the currently installed profile in the system table.
*
* Install profiles are now treated as modules by Drupal, and have an upgrade path
* based on their schema version in the system table.
*
* The install profile will be set to schema_version 0, as it has already been
* installed. Any other hook_update_N functions provided by the install profile
* will be run by update.php.
*/
function update_fix_d7_install_profile() {
$profile = drupal_get_profile();
$results = db_select('system', 's')
->fields('s', array('name', 'schema_version'))
->condition('name', $profile)
->condition('type', 'module')
->execute()
->fetchAll();
if (empty($results)) {
$filename = 'profiles/' . $profile . '/' . $profile . '.profile';
// Read profile info file
$info = drupal_parse_info_file(dirname($filename) . '/' . $profile . '.info');
// Merge in defaults.
$info = $info + array(
'dependencies' => array(),
'dependents' => array(),
'description' => '',
'package' => 'Other',
'version' => NULL,
'php' => DRUPAL_MINIMUM_PHP,
'files' => array(),
);
// The install profile is always required.
$file->info['required'] = TRUE;
$values = array(
'filename' => $filename,
'name' => $profile,
'info' => serialize($info),
'schema_version' => 0,
'type' => 'module',
'status' => 1,
'owner' => '',
);
// Install profile hooks are always executed last by the module system
$values['weight'] = 1000;
// Initializing the system table entry for the install profile
db_insert('system')
->fields(array_keys($values))
->values($values)
->execute();
// Reset the cached schema version.
drupal_get_installed_schema_version($profile, TRUE);
// Load the updates again to make sure the install profile updates are loaded
drupal_load_updates();
}
}
/**
* Parse database connection URLs (in the old, pre-Drupal 7 format) and
* return them as an array of database connection information.
......
......@@ -733,6 +733,11 @@ function install_system_module(&$install_state) {
// variable_set() can be used now that system.module is installed and
// Drupal is bootstrapped.
$modules = $install_state['profile_info']['dependencies'];
// The install profile is also a module, which needs to be installed
// after all the dependencies have been installed.
$modules[] = drupal_get_profile();
variable_set('install_profile_modules', array_diff($modules, array('system')));
$install_state['database_tables_exist'] = TRUE;
}
......@@ -1424,7 +1429,14 @@ function install_finished(&$install_state) {
_drupal_flush_css_js();
// Remember the profile which was used.
variable_set('install_profile', $install_state['parameters']['profile']);
variable_set('install_profile', drupal_get_profile());
// Install profiles are always loaded last
db_update('system')
->fields(array('weight' => 1000))
->condition('type', 'module')
->condition('name', drupal_get_profile())
->execute();
// Cache a fully-built schema.
drupal_get_schema(NULL, TRUE);
......
......@@ -1037,6 +1037,7 @@ protected function setUp() {
$this->originalLanguageDefault = variable_get('language_default');
$this->originalPrefix = $db_prefix;
$this->originalFileDirectory = file_directory_path();
$this->originalProfile = drupal_get_profile();
$clean_url_original = variable_get('clean_url', 0);
// Generate temporary prefixed database to ensure that tests have a clean starting point.
......@@ -1062,7 +1063,7 @@ protected function setUp() {
$this->preloadRegistry();
// Include the default profile
require_once('./profiles/default/default.profile');
variable_set('install_profile', 'default');
$profile_details = install_profile_info('default', 'en');
// Add the specified modules to the list of modules in the default profile.
......@@ -1086,7 +1087,7 @@ protected function setUp() {
// Run default profile tasks.
$install_state = array();
default_profile_site_setup($install_state);
drupal_install_modules(array('default'), TRUE);
// Rebuild caches.
node_types_rebuild();
......
......@@ -25,6 +25,10 @@ class ModuleUnitTest extends DrupalWebTestCase {
// Build a list of modules, sorted alphabetically.
$profile_info = install_profile_info('default', 'en');
$module_list = $profile_info['dependencies'];
// Install profile is a module that is expected to be loaded.
$module_list[] = 'default';
sort($module_list);
// Compare this list to the one returned by module_list(). We expect them
// to match, since all default profile modules have a weight equal to 0
......
......@@ -28,6 +28,23 @@ function system_requirements($phase) {
'severity' => REQUIREMENT_INFO,
'weight' => -10,
);
// Display the currently active install profile, if the site
// is not running the default install profile.
$profile = drupal_get_profile();
if ($profile != 'default') {
$info = install_profile_info($profile);
$requirements['install_profile'] = array(
'title' => $t('Install profile'),
'value' => $t('%profile_name (%profile-%version)', array(
'%profile_name' => $info['name'],
'%profile' => $profile,
'%version' => $info['version']
)),
'severity' => REQUIREMENT_INFO,
'weight' => -9
);
}
}
// Web server information.
......
......@@ -1753,6 +1753,15 @@ function _system_get_module_data() {
// Find modules
$modules = drupal_system_listing('/\.module$/', 'modules', 'name', 0);
// Include the install profile in modules that are loaded.
$profile = drupal_get_profile();
$modules[$profile]->name = $profile;
$modules[$profile]->uri = 'profiles/' . $profile . '/' . $profile . '.profile';
$modules[$profile]->filename = $profile . '.profile';
// Install profile hooks are always executed last.
$modules[$profile]->weight = 1000;
// Set defaults for module info.
$defaults = array(
'dependencies' => array(),
......@@ -1783,6 +1792,9 @@ function _system_get_module_data() {
drupal_alter('system_info', $modules[$key]->info, $modules[$key]);
}
// The install profile is required.
$modules[$profile]->info['required'] = TRUE;
return $modules;
}
......
......@@ -15,3 +15,4 @@ dependencies[] = dblog
dependencies[] = search
dependencies[] = toolbar
dependencies[] = field_ui
files[] = default.profile
<?php
// $Id$
/**
* Implement hook_install().
*
* Perform actions to set up the site for this profile.
*/
function default_install() {
// Enable some standard blocks.
$values = array(
array(
'module' => 'system',
'delta' => 'main',
'theme' => 'garland',
'status' => 1,
'weight' => 0,
'region' => 'content',
'pages' => '',
'cache' => -1,
),
array(
'module' => 'user',
'delta' => 'login',
'theme' => 'garland',
'status' => 1,
'weight' => 0,
'region' => 'sidebar_first',
'pages' => '',
'cache' => -1,
),
array(
'module' => 'system',
'delta' => 'navigation',
'theme' => 'garland',
'status' => 1,
'weight' => 0,
'region' => 'sidebar_first',
'pages' => '',
'cache' => -1,
),
array(
'module' => 'system',
'delta' => 'management',
'theme' => 'garland',
'status' => 1,
'weight' => 1,
'region' => 'sidebar_first',
'pages' => '',
'cache' => -1,
),
array(
'module' => 'system',
'delta' => 'powered-by',
'theme' => 'garland',
'status' => 1,
'weight' => 10,
'region' => 'footer',
'pages' => '',
'cache' => -1,
),
array(
'module' => 'system',
'delta' => 'help',
'theme' => 'garland',
'status' => 1,
'weight' => 0,
'region' => 'help',
'pages' => '',
'cache' => -1,
),
array(
'module' => 'system',
'delta' => 'main',
'theme' => 'seven',
'status' => 1,
'weight' => 0,
'region' => 'content',
'pages' => '',
'cache' => -1,
),
array(
'module' => 'system',
'delta' => 'help',
'theme' => 'seven',
'status' => 1,
'weight' => 0,
'region' => 'help',
'pages' => '',
'cache' => -1,
),
array(
'module' => 'user',
'delta' => 'login',
'theme' => 'seven',
'status' => 1,
'weight' => 10,
'region' => 'content',
'pages' => '',
'cache' => -1,
),
);
$query = db_insert('block')->fields(array('module', 'delta', 'theme', 'status', 'weight', 'region', 'pages', 'cache'));
foreach ($values as $record) {
$query->values($record);
}
$query->execute();
// Insert default user-defined node types into the database. For a complete
// list of available node type attributes, refer to the node type API
// documentation at: http://api.drupal.org/api/HEAD/function/hook_node_info.
$types = array(
array(
'type' => 'page',
'name' => st('Page'),
'base' => 'node_content',
'description' => st("Use <em>pages</em> for your static content, such as an 'About us' page."),
'custom' => 1,
'modified' => 1,
'locked' => 0,
),
array(
'type' => 'article',
'name' => st('Article'),
'base' => 'node_content',
'description' => st('Use <em>articles</em> for time-specific content like news, press releases or blog posts.'),
'custom' => 1,
'modified' => 1,
'locked' => 0,
),
);
foreach ($types as $type) {
$type = node_type_set_defaults($type);
node_type_save($type);
}
// Default page to not be promoted and have comments disabled.
variable_set('node_options_page', array('status'));
variable_set('comment_page', COMMENT_NODE_HIDDEN);
// Don't display date and author information for page nodes by default.
variable_set('node_submitted_page', FALSE);
// Create an image style.
$style = array('name' => 'thumbnail');
$style = image_style_save($style);
$effect = array(
'isid' => $style['isid'],
'name' => 'image_scale_and_crop',
'data' => array('width' => '85', 'height' => '85'),
);
image_effect_save($effect);
// Enable user picture support and set the default to a square thumbnail option.
variable_set('user_pictures', '1');
variable_set('user_picture_dimensions', '1024x1024');
variable_set('user_picture_file_size', '800');
variable_set('user_picture_style', 'thumbnail');
$theme_settings = theme_get_settings();
$theme_settings['toggle_node_user_picture'] = '1';
$theme_settings['toggle_comment_user_picture'] = '1';
variable_set('theme_settings', $theme_settings);
// Create a default vocabulary named "Tags", enabled for the 'article' content type.
$description = st('Use tags to group articles on similar topics into categories.');
$help = st('Enter a comma-separated list of words to describe your content.');
$vid = db_insert('taxonomy_vocabulary')->fields(array(
'name' => 'Tags',
'description' => $description,
'machine_name' => 'tags',
'help' => $help,
'relations' => 0,
'hierarchy' => 0,
'multiple' => 0,
'required' => 0,
'tags' => 1,
'module' => 'taxonomy',
'weight' => 0,
))->execute();
db_insert('taxonomy_vocabulary_node_type')->fields(array('vid' => $vid, 'type' => 'article'))->execute();
// Create a default role for site administrators.
$rid = db_insert('role')->fields(array('name' => 'administrator'))->execute();
// Set this as the administrator role.
variable_set('user_admin_role', $rid);
// Assign all available permissions to this role.
foreach (module_invoke_all('permission') as $key => $value) {
db_insert('role_permission')
->fields(array(
'rid' => $rid,
'permission' => $key,
))->execute();
}
// Update the menu router information.
menu_rebuild();
// Save some default links.
$link = array('link_path' => 'admin/structure/menu-customize/main-menu/add', 'link_title' => 'Add a main menu link', 'menu_name' => 'main-menu');
menu_link_save($link);
// Enable the admin theme.
db_update('system')
->fields(array('status' => 1))
->condition('type', 'theme')
->condition('name', 'seven')
->execute();
variable_set('admin_theme', 'seven');
variable_set('node_admin_theme', '1');
}
<?php
// $Id$
/**
* Implement hook_profile_tasks().
*/
function default_profile_tasks() {
$tasks = array(
'default_profile_site_setup' => array(),
);
return $tasks;
}
/**
* Installation task; perform actions to set up the site for this profile.
*
* This task does not return any output, meaning that control will be passed
* along to the next task without ending the page request.
*
* @param $install_state
* An array of information about the current installation state.
*/
function default_profile_site_setup(&$install_state) {
// Enable some standard blocks.
$values = array(
array(
'module' => 'system',
'delta' => 'main',
'theme' => 'garland',
'status' => 1,
'weight' => 0,
'region' => 'content',
'pages' => '',
'cache' => -1,
),
array(
'module' => 'user',
'delta' => 'login',
'theme' => 'garland',
'status' => 1,
'weight' => 0,
'region' => 'sidebar_first',
'pages' => '',
'cache' => -1,
),
array(
'module' => 'system',
'delta' => 'navigation',
'theme' => 'garland',
'status' => 1,
'weight' => 0,
'region' => 'sidebar_first',
'pages' => '',
'cache' => -1,
),
array(
'module' => 'system',
'delta' => 'management',
'theme' => 'garland',
'status' => 1,
'weight' => 1,
'region' => 'sidebar_first',
'pages' => '',
'cache' => -1,
),
array(
'module' => 'system',
'delta' => 'powered-by',
'theme' => 'garland',
'status' => 1,
'weight' => 10,
'region' => 'footer',
'pages' => '',
'cache' => -1,
),
array(
'module' => 'system',
'delta' => 'help',
'theme' => 'garland',
'status' => 1,
'weight' => 0,
'region' => 'help',
'pages' => '',
'cache' => -1,
),
array(
'module' => 'system',
'delta' => 'main',
'theme' => 'seven',
'status' => 1,
'weight' => 0,
'region' => 'content',
'pages' => '',
'cache' => -1,
),
array(
'module' => 'system',
'delta' => 'help',
'theme' => 'seven',
'status' => 1,
'weight' => 0,
'region' => 'help',
'pages' => '',
'cache' => -1,
),
array(
'module' => 'user',
'delta' => 'login',
'theme' => 'seven',
'status' => 1,
'weight' => 10,
'region' => 'content',
'pages' => '',
'cache' => -1,
),
);
$query = db_insert('block')->fields(array('module', 'delta', 'theme', 'status', 'weight', 'region', 'pages', 'cache'));
foreach ($values as $record) {
$query->values($record);
}
$query->execute();
// Insert default user-defined node types into the database. For a complete
// list of available node type attributes, refer to the node type API
// documentation at: http://api.drupal.org/api/HEAD/function/hook_node_info.
$types = array(
array(
'type' => 'page',
'name' => st('Page'),
'base' => 'node_content',
'description' => st("Use <em>pages</em> for your static content, such as an 'About us' page."),
'custom' => 1,
'modified' => 1,
'locked' => 0,
),
array(
'type' => 'article',
'name' => st('Article'),
'base' => 'node_content',
'description' => st('Use <em>articles</em> for time-specific content like news, press releases or blog posts.'),
'custom' => 1,
'modified' => 1,
'locked' => 0,
),
);
foreach ($types as $type) {
$type = node_type_set_defaults($type);
node_type_save($type);
}