Newer
Older
<?php
/**
* Indicates that a module has not been installed yet.
*/
Steven Wittens
committed
define('SCHEMA_UNINSTALLED', -1);
/**
* Indicates that a module has been installed.
*/
Steven Wittens
committed
define('SCHEMA_INSTALLED', 0);
/**
* Requirement severity -- Informational message only.
*/
/**
* Requirement severity -- Requirement successfully met.
*/
Steven Wittens
committed
define('REQUIREMENT_OK', 0);
/**
* Requirement severity -- Warning condition; proceed but flag warning.
*/
Steven Wittens
committed
define('REQUIREMENT_WARNING', 1);
/**
* Requirement severity -- Error condition; abort installation.
*/
Steven Wittens
committed
define('REQUIREMENT_ERROR', 2);
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
/**
* File permission check -- File exists.
*/
define('FILE_EXIST', 1);
/**
* File permission check -- File is readable.
*/
define('FILE_READABLE', 2);
/**
* File permission check -- File is writable.
*/
define('FILE_WRITABLE', 4);
/**
* File permission check -- File is executable.
*/
define('FILE_EXECUTABLE', 8);
/**
* File permission check -- File does not exist.
*/
define('FILE_NOT_EXIST', 16);
/**
* File permission check -- File is not readable.
*/
define('FILE_NOT_READABLE', 32);
/**
* File permission check -- File is not writable.
*/
define('FILE_NOT_WRITABLE', 64);
/**
* File permission check -- File is not executable.
*/
Dries Buytaert
committed
define('FILE_NOT_EXECUTABLE', 128);
/**
* Initialize the update system by loading all installed module's .install files.
*/
function drupal_load_updates() {
foreach (drupal_get_installed_schema_version(NULL, FALSE, TRUE) as $module => $schema_version) {
if ($schema_version > -1) {
module_load_install($module);
}
}
}
/**
* Returns an array of available schema versions for a module.
*
* @param $module
* A module name.
* @return
* If the module has updates, an array of available updates sorted by version.
Dries Buytaert
committed
* Otherwise, FALSE.
*/
function drupal_get_schema_versions($module) {
Dries Buytaert
committed
$updates = &drupal_static(__FUNCTION__, NULL);
Dries Buytaert
committed
if (!isset($updates[$module])) {
Dries Buytaert
committed
$updates = array();
foreach (module_list() as $loaded_module) {
$updates[$loaded_module] = array();
}
Dries Buytaert
committed
// Prepare regular expression to match all possible defined hook_update_N().
Dries Buytaert
committed
$regexp = '/^(?P<module>.+)_update_(?P<version>\d+)$/';
Dries Buytaert
committed
$functions = get_defined_functions();
// Narrow this down to functions ending with an integer, since all
// hook_update_N() functions end this way, and there are other
// possible functions which match '_update_'. We use preg_grep() here
// instead of foreaching through all defined functions, since the loop
// through all PHP functions can take significant page execution time
// and this function is called on every administrative page via
// system_requirements().
foreach (preg_grep('/_\d+$/', $functions['user']) as $function) {
// If this function is a module update function, add it to the list of
// module updates.
if (preg_match($regexp, $function, $matches)) {
$updates[$matches['module']][] = $matches['version'];
Dries Buytaert
committed
// Ensure that updates are applied in numerical order.
foreach ($updates as &$module_updates) {
sort($module_updates, SORT_NUMERIC);
}
}
return empty($updates[$module]) ? FALSE : $updates[$module];
}
/**
* Returns the currently installed schema version for a module.
*
* @param $module
* A module name.
* @param $reset
* Set to TRUE after modifying the system table.
* @param $array
Dries Buytaert
committed
* Set to TRUE if you want to get information about all modules in the
* system.
* @return
Angie Byron
committed
* The currently installed schema version, or SCHEMA_UNINSTALLED if the
* module is not installed.
*/
function drupal_get_installed_schema_version($module, $reset = FALSE, $array = FALSE) {
static $versions = array();
if ($reset) {
}
if (!$versions) {
$versions = array();
$result = db_query("SELECT name, schema_version FROM {system} WHERE type = :type", array(':type' => 'module'));
foreach ($result as $row) {
$versions[$row->name] = $row->schema_version;
}
}
Angie Byron
committed
if ($array) {
return $versions;
}
else {
return isset($versions[$module]) ? $versions[$module] : SCHEMA_UNINSTALLED;
}
}
/**
* Update the installed version information for a module.
*
* @param $module
* A module name.
* @param $version
* The new schema version.
*/
function drupal_set_installed_schema_version($module, $version) {
db_update('system')
->fields(array('schema_version' => $version))
->condition('name', $module)
->execute();
Angie Byron
committed
// Reset the static cache of module schema versions.
drupal_get_installed_schema_version(NULL, TRUE);
}
Dries Buytaert
committed
/**
* Loads the install profile, extracting its defined distribution name.
Dries Buytaert
committed
*
* @return
* The distribution name defined in the profile's .info file. Defaults to
* "Drupal" if none is explicitly provided by the install profile.
*
* @see install_profile_info()
Dries Buytaert
committed
*/
function drupal_install_profile_distribution_name() {
Angie Byron
committed
// During installation, the profile information is stored in the global
// installation state (it might not be saved anywhere yet).
if (drupal_installation_attempted()) {
global $install_state;
return $install_state['profile_info']['distribution_name'];
}
// At all other times, we load the profile via standard methods.
else {
$profile = drupal_get_profile();
Dries Buytaert
committed
$info = system_get_info('module', $profile);
Angie Byron
committed
return $info['distribution_name'];
}
Dries Buytaert
committed
}
/**
* Auto detect the base_url with PHP predefined variables.
*
* @param $file
* The name of the file calling this function so we can strip it out of
* the URI when generating the base_url.
* @return
* The auto-detected $base_url that should be configured in settings.php
*/
function drupal_detect_baseurl($file = 'install.php') {
$proto = $_SERVER['HTTPS'] ? 'https://' : 'http://';
$host = $_SERVER['SERVER_NAME'];
$port = ($_SERVER['SERVER_PORT'] == 80 ? '' : ':' . $_SERVER['SERVER_PORT']);
Steven Wittens
committed
$uri = preg_replace("/\?.*/", '', $_SERVER['REQUEST_URI']);
Dries Buytaert
committed
$dir = str_replace("/$file", '', $uri);
return "$proto$host$port$dir";
}
/**
* Detect all supported databases that are compiled into PHP.
Dries Buytaert
committed
*
* @return
* An array of database types compiled into PHP.
*/
function drupal_detect_database_types() {
Angie Byron
committed
$databases = drupal_get_database_types();
foreach ($databases as $driver => $installer) {
$databases[$driver] = $installer->name();
}
return $databases;
}
/**
* Return all supported database installer objects that are compiled into PHP.
*
* @return
* An array of database installer objects compiled into PHP.
*/
function drupal_get_database_types() {
Dries Buytaert
committed
$databases = array();
Dries Buytaert
committed
// We define a driver as a directory in /includes/database that in turn
Dries Buytaert
committed
// contains a database.inc file. That allows us to drop in additional drivers
Dries Buytaert
committed
// without modifying the installer.
// Because we have no registry yet, we need to also include the install.inc
// file for the driver explicitly.
Dries Buytaert
committed
require_once DRUPAL_ROOT . '/includes/database/database.inc';
Angie Byron
committed
foreach (file_scan_directory(DRUPAL_ROOT . '/includes/database', '/^[a-z]*$/i', array('recurse' => FALSE)) as $file) {
Angie Byron
committed
if (file_exists($file->uri . '/database.inc') && file_exists($file->uri . '/install.inc')) {
$drivers[$file->filename] = $file->uri;
}
Dries Buytaert
committed
}
foreach ($drivers as $driver => $file) {
Angie Byron
committed
$installer = db_installer_object($driver);
Dries Buytaert
committed
if ($installer->installable()) {
Angie Byron
committed
$databases[$driver] = $installer;
Dries Buytaert
committed
}
}
Dries Buytaert
committed
// Usability: unconditionally put the MySQL driver on top.
Dries Buytaert
committed
if (isset($databases['mysql'])) {
$mysql_database = $databases['mysql'];
unset($databases['mysql']);
$databases = array('mysql' => $mysql_database) + $databases;
}
Dries Buytaert
committed
return $databases;
}
Dries Buytaert
committed
/**
* Database installer structure.
*
* Defines basic Drupal requirements for databases.
*/
abstract class DatabaseTasks {
/**
* Structure that describes each task to run.
*
* @var array
*
* Each value of the tasks array is an associative array defining the function
* to call (optional) and any arguments to be passed to the function.
*/
protected $tasks = array(
array(
'function' => 'checkEngineVersion',
'arguments' => array(),
),
Dries Buytaert
committed
array(
'arguments' => array(
Angie Byron
committed
'CREATE TABLE {drupal_install_test} (id int NULL)',
Dries Buytaert
committed
'Drupal can use CREATE TABLE database commands.',
Dries Buytaert
committed
'Failed to <strong>CREATE</strong> a test table on your database server with the command %query. The server reports the following message: %error.<p>Are you sure the configured username has the necessary permissions to create tables in the database?</p>',
Dries Buytaert
committed
TRUE,
),
Dries Buytaert
committed
),
Dries Buytaert
committed
array(
'arguments' => array(
Angie Byron
committed
'INSERT INTO {drupal_install_test} (id) VALUES (1)',
Dries Buytaert
committed
'Drupal can use INSERT database commands.',
Dries Buytaert
committed
'Failed to <strong>INSERT</strong> a value into a test table on your database server. We tried inserting a value with the command %query and the server reported the following error: %error.',
Dries Buytaert
committed
),
Dries Buytaert
committed
),
Dries Buytaert
committed
array(
'arguments' => array(
Angie Byron
committed
'UPDATE {drupal_install_test} SET id = 2',
Dries Buytaert
committed
'Drupal can use UPDATE database commands.',
Dries Buytaert
committed
'Failed to <strong>UPDATE</strong> a value in a test table on your database server. We tried updating a value with the command %query and the server reported the following error: %error.',
Dries Buytaert
committed
),
Dries Buytaert
committed
),
Dries Buytaert
committed
array(
'arguments' => array(
Angie Byron
committed
'DELETE FROM {drupal_install_test}',
Dries Buytaert
committed
'Drupal can use DELETE database commands.',
Dries Buytaert
committed
'Failed to <strong>DELETE</strong> a value from a test table on your database server. We tried deleting a value with the command %query and the server reported the following error: %error.',
Dries Buytaert
committed
),
Dries Buytaert
committed
),
Dries Buytaert
committed
array(
'arguments' => array(
Angie Byron
committed
'DROP TABLE {drupal_install_test}',
Dries Buytaert
committed
'Drupal can use DROP TABLE database commands.',
Dries Buytaert
committed
'Failed to <strong>DROP</strong> a test table from your database server. We tried dropping a table with the command %query and the server reported the following error %error.',
Dries Buytaert
committed
),
Dries Buytaert
committed
),
);
Angie Byron
committed
Dries Buytaert
committed
/**
* Results from tasks.
*
* @var array
*/
protected $results = array();
Dries Buytaert
committed
Dries Buytaert
committed
/**
* Ensure the PDO driver is supported by the version of PHP in use.
*/
Dries Buytaert
committed
protected function hasPdoDriver() {
return in_array($this->pdoDriver, PDO::getAvailableDrivers());
}
Dries Buytaert
committed
/**
* Assert test as failed.
*/
protected function fail($message) {
$this->results[$message] = FALSE;
}
/**
* Assert test as a pass.
*/
protected function pass($message) {
$this->results[$message] = TRUE;
}
/**
* Check whether Drupal is installable on the database.
*/
Dries Buytaert
committed
public function installable() {
Dries Buytaert
committed
return $this->hasPdoDriver() && empty($this->error);
Dries Buytaert
committed
}
/**
* Return the human-readable name of the driver.
*/
Dries Buytaert
committed
abstract public function name();
/**
* Return the minimum required version of the engine.
*
* @return
* A version string. If not NULL, it will be checked against the version
* reported by the Database engine using version_compare().
*/
public function minimumVersion() {
return NULL;
}
Dries Buytaert
committed
Dries Buytaert
committed
/**
* Run database tasks and tests to see if Drupal can run on the database.
*/
public function runTasks() {
// We need to establish a connection before we can run tests.
if ($this->connect()) {
foreach ($this->tasks as $task) {
if (!isset($task['function'])) {
$task['function'] = 'runTestQuery';
}
if (method_exists($this, $task['function'])) {
// Returning false is fatal. No other tasks can run.
if (FALSE === call_user_func_array(array($this, $task['function']), $task['arguments'])) {
break;
}
}
else {
throw new DatabaseTaskException(st("Failed to run all tasks against the database server. The task %task wasn't found.", array('%task' => $task['function'])));
Dries Buytaert
committed
}
}
Dries Buytaert
committed
}
Dries Buytaert
committed
// Check for failed results and compile message
$message = '';
foreach ($this->results as $result => $success) {
if (!$success) {
$message .= '<p class="error">' . $result . '</p>';
Dries Buytaert
committed
}
}
Dries Buytaert
committed
if (!empty($message)) {
Angie Byron
committed
$message = '<p>In order for Drupal to work, and to continue with the installation process, you must resolve all issues reported below. For more help with configuring your database server, see the <a href="http://drupal.org/getting-started/install">installation handbook</a>. If you are unsure what any of this means you should probably contact your hosting provider.</p>' . $message;
Dries Buytaert
committed
throw new DatabaseTaskException($message);
}
Dries Buytaert
committed
}
Dries Buytaert
committed
/**
* Check if we can connect to the database.
*/
Dries Buytaert
committed
protected function connect() {
Dries Buytaert
committed
try {
Dries Buytaert
committed
// This doesn't actually test the connection.
Dries Buytaert
committed
db_set_active();
Dries Buytaert
committed
// Now actually do a check.
Database::getConnection();
Dries Buytaert
committed
$this->pass('Drupal can CONNECT to the database ok.');
Dries Buytaert
committed
}
catch (Exception $e) {
Dries Buytaert
committed
$this->fail(st('Failed to connect to your database server. The server reports the following message: %error.<ul><li>Is the database server running?</li><li>Does the database exist, and have you entered the correct database name?</li><li>Have you entered the correct username and password?</li><li>Have you entered the correct database hostname?</li></ul>', array('%error' => $e->getMessage())));
Dries Buytaert
committed
return FALSE;
}
Dries Buytaert
committed
return TRUE;
Dries Buytaert
committed
}
Dries Buytaert
committed
/**
* Run SQL tests to ensure the database can execute commands with the current user.
*/
protected function runTestQuery($query, $pass, $fail, $fatal = FALSE) {
Dries Buytaert
committed
try {
db_query($query);
Dries Buytaert
committed
$this->pass(st($pass));
Dries Buytaert
committed
}
catch (Exception $e) {
Dries Buytaert
committed
$this->fail(st($fail, array('%query' => $query, '%error' => $e->getMessage(), '%name' => $this->name())));
return !$fatal;
Dries Buytaert
committed
}
}
/**
* Check the engine version.
*/
protected function checkEngineVersion() {
if ($this->minimumVersion() && version_compare(Database::getConnection()->version(), $this->minimumVersion(), '<')) {
$this->fail(st("The database version %version is less than the minimum required version %minimum_version.", array('%version' => Database::getConnection()->version(), '%minimum_version' => $this->minimumVersion())));
}
}
Angie Byron
committed
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
/**
* Return driver specific configuration options.
*
* @param $database
* An array of driver specific configuration options.
*
* @return
* The options form array.
*/
public function getFormOptions($database) {
$form['database'] = array(
'#type' => 'textfield',
'#title' => st('Database name'),
'#default_value' => empty($database['database']) ? '' : $database['database'],
'#size' => 45,
'#required' => TRUE,
'#description' => st('The name of the database your @drupal data will be stored in. It must exist on your server before @drupal can be installed.', array('@drupal' => drupal_install_profile_distribution_name())),
);
$form['username'] = array(
'#type' => 'textfield',
'#title' => st('Database username'),
'#default_value' => empty($database['username']) ? '' : $database['username'],
'#required' => TRUE,
'#size' => 45,
);
$form['password'] = array(
'#type' => 'password',
'#title' => st('Database password'),
'#default_value' => empty($database['password']) ? '' : $database['password'],
'#required' => FALSE,
'#size' => 45,
);
$form['advanced_options'] = array(
'#type' => 'fieldset',
'#title' => st('Advanced options'),
'#collapsible' => TRUE,
'#collapsed' => TRUE,
'#description' => st("These options are only necessary for some sites. If you're not sure what you should enter here, leave the default settings or check with your hosting provider."),
'#weight' => 10,
);
$profile = drupal_get_profile();
$db_prefix = ($profile == 'standard') ? 'drupal_' : $profile . '_';
$form['advanced_options']['db_prefix'] = array(
'#type' => 'textfield',
'#title' => st('Table prefix'),
'#default_value' => '',
'#size' => 45,
'#description' => st('If more than one application will be sharing this database, enter a table prefix such as %prefix for your @drupal site here.', array('@drupal' => drupal_install_profile_distribution_name(), '%prefix' => $db_prefix)),
'#weight' => 10,
);
$form['advanced_options']['host'] = array(
'#type' => 'textfield',
'#title' => st('Database host'),
'#default_value' => empty($database['host']) ? 'localhost' : $database['host'],
'#size' => 45,
// Hostnames can be 255 characters long.
'#maxlength' => 255,
'#required' => TRUE,
'#description' => st('If your database is located on a different server, change this.'),
);
$form['advanced_options']['port'] = array(
'#type' => 'textfield',
'#title' => st('Database port'),
'#default_value' => empty($database['port']) ? '' : $database['port'],
'#size' => 45,
// The maximum port number is 65536, 5 digits.
'#maxlength' => 5,
'#description' => st('If your database server is listening to a non-standard port, enter its number.'),
);
return $form;
}
/**
* Validates driver specific configuration settings.
*
* Checks to ensure correct basic database settings and that a proper
* connection to the database can be established.
*
* @param $database
* An array of driver specific configuration options.
Dries Buytaert
committed
*
Angie Byron
committed
* @return
* An array of driver configuration errors, keyed by form element name.
*/
public function validateDatabaseSettings($database) {
$errors = array();
// Verify the table prefix.
if (!empty($database['prefix']) && is_string($database['prefix']) && !preg_match('/^[A-Za-z0-9_.]+$/', $database['prefix'])) {
$errors[$database['driver'] . '][advanced_options][db_prefix'] = st('The database table prefix you have entered, %prefix, is invalid. The table prefix can only contain alphanumeric characters, periods, or underscores.', array('%prefix' => $database['prefix']));
}
// Verify the database port.
if (!empty($database['port']) && !is_numeric($database['port'])) {
$errors[$database['driver'] . '][advanced_options][port'] = st('Database port must be a number.');
}
return $errors;
}
Dries Buytaert
committed
}
Angie Byron
committed
Dries Buytaert
committed
/**
Angie Byron
committed
* Exception thrown if the database installer fails.
Dries Buytaert
committed
*/
class DatabaseTaskException extends Exception {
}
Dries Buytaert
committed
/**
* Replace values in settings.php with values in the submitted array.
Dries Buytaert
committed
*
* @param $settings
* An array of settings that need to be updated.
*/
function drupal_rewrite_settings($settings = array(), $prefix = '') {
Angie Byron
committed
$default_settings = 'sites/default/default.settings.php';
Dries Buytaert
committed
drupal_static_reset('conf_path');
$settings_file = conf_path(FALSE) . '/' . $prefix . 'settings.php';
Dries Buytaert
committed
// Build list of setting names and insert the values into the global namespace.
$keys = array();
foreach ($settings as $setting => $data) {
$GLOBALS[$setting] = $data['value'];
$keys[] = $setting;
}
$buffer = NULL;
$first = TRUE;
Angie Byron
committed
if ($fp = fopen(DRUPAL_ROOT . '/' . $default_settings, 'r')) {
Dries Buytaert
committed
// Step line by line through settings.php.
while (!feof($fp)) {
$line = fgets($fp);
if ($first && substr($line, 0, 5) != '<?php') {
$buffer = "<?php\n\n";
}
$first = FALSE;
// Check for constants.
if (substr($line, 0, 7) == 'define(') {
preg_match('/define\(\s*[\'"]([A-Z_-]+)[\'"]\s*,(.*?)\);/', $line, $variable);
if (in_array($variable[1], $keys)) {
$setting = $settings[$variable[1]];
$buffer .= str_replace($variable[2], " '" . $setting['value'] . "'", $line);
Dries Buytaert
committed
unset($settings[$variable[1]]);
unset($settings[$variable[2]]);
}
else {
$buffer .= $line;
}
}
// Check for variables.
elseif (substr($line, 0, 1) == '$') {
preg_match('/\$([^ ]*) /', $line, $variable);
if (in_array($variable[1], $keys)) {
// Write new value to settings.php in the following format:
// $'setting' = 'value'; // 'comment'
$setting = $settings[$variable[1]];
Dries Buytaert
committed
$buffer .= '$' . $variable[1] . " = " . var_export($setting['value'], TRUE) . ";" . (!empty($setting['comment']) ? ' // ' . $setting['comment'] . "\n" : "\n");
Dries Buytaert
committed
unset($settings[$variable[1]]);
}
else {
$buffer .= $line;
}
}
else {
$buffer .= $line;
}
}
fclose($fp);
// Add required settings that were missing from settings.php.
foreach ($settings as $setting => $data) {
if ($data['required']) {
Dries Buytaert
committed
$buffer .= "\$$setting = " . var_export($data['value'], TRUE) . ";\n";
Dries Buytaert
committed
}
}
Angie Byron
committed
$fp = fopen(DRUPAL_ROOT . '/' . $settings_file, 'w');
Dries Buytaert
committed
if ($fp && fwrite($fp, $buffer) === FALSE) {
throw new Exception(st('Failed to modify %settings. Verify the file permissions.', array('%settings' => $settings_file)));
Dries Buytaert
committed
}
}
else {
throw new Exception(st('Failed to open %settings. Verify the file permissions.', array('%settings' => $default_settings)));
Dries Buytaert
committed
}
}
/**
* Verify an install profile for installation.
Dries Buytaert
committed
*
Dries Buytaert
committed
* @param $install_state
* An array of information about the current installation state.
Dries Buytaert
committed
*/
Dries Buytaert
committed
function drupal_verify_profile($install_state) {
$profile = $install_state['parameters']['profile'];
$locale = $install_state['parameters']['locale'];
Angie Byron
committed
include_once DRUPAL_ROOT . '/includes/file.inc';
include_once DRUPAL_ROOT . '/includes/common.inc';
Dries Buytaert
committed
Angie Byron
committed
$profile_file = DRUPAL_ROOT . "/profiles/$profile/$profile.profile";
Dries Buytaert
committed
if (!isset($profile) || !file_exists($profile_file)) {
Dries Buytaert
committed
throw new Exception(install_no_profile_error());
Dries Buytaert
committed
}
Dries Buytaert
committed
$info = $install_state['profile_info'];
Dries Buytaert
committed
// Get a list of modules that exist in Drupal's assorted subdirectories.
$present_modules = array();
Dries Buytaert
committed
foreach (drupal_system_listing('/^' . DRUPAL_PHP_FUNCTION_PATTERN . '\.module$/', 'modules', 'name', 0) as $present_module) {
Dries Buytaert
committed
$present_modules[] = $present_module->name;
}
Angie Byron
committed
// 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();
Dries Buytaert
committed
// Verify that all of the profile's required modules are present.
$missing_modules = array_diff($info['dependencies'], $present_modules);
Angie Byron
committed
$requirements = array();
Dries Buytaert
committed
if (count($missing_modules)) {
Angie Byron
committed
$modules = array();
foreach ($missing_modules as $module) {
Angie Byron
committed
$modules[] = '<span class="admin-missing">' . drupal_ucfirst($module) . '</span>';
Angie Byron
committed
$requirements['required_modules'] = array(
'title' => st('Required modules'),
'value' => st('Required modules not found.'),
'severity' => REQUIREMENT_ERROR,
'description' => st('The following modules are required but were not found. Move them into the appropriate modules subdirectory, such as <em>sites/all/modules</em>. Missing modules: !modules', array('!modules' => implode(', ', $modules))),
Angie Byron
committed
);
Angie Byron
committed
return $requirements;
Gábor Hojtsy
committed
/**
* Callback to install the system module.
*
* Separated from the installation of other modules so core system
* functions can be made available while other modules are installed.
*/
function drupal_install_system() {
Dries Buytaert
committed
$system_path = drupal_get_path('module', 'system');
Angie Byron
committed
require_once DRUPAL_ROOT . '/' . $system_path . '/system.install';
$system_versions = drupal_get_schema_versions('system');
$system_version = $system_versions ? max($system_versions) : SCHEMA_INSTALLED;
db_insert('system')
->fields(array('filename', 'name', 'type', 'owner', 'status', 'schema_version', 'bootstrap'))
->values(array(
'filename' => $system_path . '/system.module',
'name' => 'system',
'type' => 'module',
'owner' => '',
'status' => 1,
'schema_version' => $system_version,
'bootstrap' => 0,
))
->execute();
Angie Byron
committed
system_rebuild_module_data();
Neil Drumm
committed
}
Angie Byron
committed
* Uninstalls a given list of modules.
* @param $module_list
* The modules to uninstall.
Angie Byron
committed
* @param $uninstall_dependents
* 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.
*
* @return
* FALSE if one or more dependent modules are missing from the list, TRUE
* otherwise.
Angie Byron
committed
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
function drupal_uninstall_modules($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);
}
foreach ($module_list as $module) {
// Uninstall the module.
module_load_install($module);
module_invoke($module, 'uninstall');
Dries Buytaert
committed
drupal_uninstall_schema($module);
Dries Buytaert
committed
watchdog('system', '%module module uninstalled.', array('%module' => $module), WATCHDOG_INFO);
drupal_set_installed_schema_version($module, SCHEMA_UNINSTALLED);
if (!empty($module_list)) {
// Call hook_module_uninstall to let other modules act
module_invoke_all('modules_uninstalled', $module_list);
}
Angie Byron
committed
return TRUE;
Dries Buytaert
committed
/**
* Verify the state of the specified file.
*
* @param $file
* The file to check for.
* @param $mask
* An optional bitmask created from various FILE_* constants.
* @param $type
* The type of file. Can be file (default), dir, or link.
* @return
* TRUE on success or FALSE on failure. A message is set for the latter.
Dries Buytaert
committed
*/
function drupal_verify_install_file($file, $mask = NULL, $type = 'file') {
$return = TRUE;
// Check for files that shouldn't be there.
if (isset($mask) && ($mask & FILE_NOT_EXIST) && file_exists($file)) {
return FALSE;
}
// Verify that the file is the type of file it is supposed to be.
if (isset($type) && file_exists($file)) {
$check = 'is_' . $type;
Dries Buytaert
committed
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
if (!function_exists($check) || !$check($file)) {
$return = FALSE;
}
}
// Verify file permissions.
if (isset($mask)) {
$masks = array(FILE_EXIST, FILE_READABLE, FILE_WRITABLE, FILE_EXECUTABLE, FILE_NOT_READABLE, FILE_NOT_WRITABLE, FILE_NOT_EXECUTABLE);
foreach ($masks as $current_mask) {
if ($mask & $current_mask) {
switch ($current_mask) {
case FILE_EXIST:
if (!file_exists($file)) {
if ($type == 'dir') {
drupal_install_mkdir($file, $mask);
}
if (!file_exists($file)) {
$return = FALSE;
}
}
break;
case FILE_READABLE:
if (!is_readable($file) && !drupal_install_fix_file($file, $mask)) {
$return = FALSE;
}
break;
case FILE_WRITABLE:
if (!is_writable($file) && !drupal_install_fix_file($file, $mask)) {
$return = FALSE;
}
break;
case FILE_EXECUTABLE:
if (!is_executable($file) && !drupal_install_fix_file($file, $mask)) {
$return = FALSE;
}
break;
case FILE_NOT_READABLE:
if (is_readable($file) && !drupal_install_fix_file($file, $mask)) {
$return = FALSE;
}
break;
case FILE_NOT_WRITABLE:
if (is_writable($file) && !drupal_install_fix_file($file, $mask)) {
$return = FALSE;
}
break;
case FILE_NOT_EXECUTABLE:
if (is_executable($file) && !drupal_install_fix_file($file, $mask)) {
$return = FALSE;
}
break;
}
}
}
}
return $return;
}
/**
* Create a directory with specified permissions.
*
* @param $file
Dries Buytaert
committed
* The name of the directory to create;
* @param $mask
Dries Buytaert
committed
* The permissions of the directory to create.
* @param $message
* (optional) Whether to output messages. Defaults to TRUE.
* @return
* TRUE/FALSE whether or not the directory was successfully created.
*/
function drupal_install_mkdir($file, $mask, $message = TRUE) {
$mod = 0;
$masks = array(FILE_READABLE, FILE_WRITABLE, FILE_EXECUTABLE, FILE_NOT_READABLE, FILE_NOT_WRITABLE, FILE_NOT_EXECUTABLE);
foreach ($masks as $m) {
if ($mask & $m) {
switch ($m) {
case FILE_READABLE:
Dries Buytaert
committed
$mod |= 0444;
Dries Buytaert
committed
break;
case FILE_WRITABLE:
Dries Buytaert
committed
$mod |= 0222;
Dries Buytaert
committed
break;
case FILE_EXECUTABLE:
Dries Buytaert
committed
$mod |= 0111;
Dries Buytaert
committed
break;
}
}
}
Dries Buytaert
committed
if (@drupal_mkdir($file, $mod)) {
Dries Buytaert
committed
return TRUE;
}
else {
return FALSE;
}
}
/**
* Attempt to fix file permissions.
*
* The general approach here is that, because we do not know the security
* setup of the webserver, we apply our permission changes to all three
* digits of the file permission (i.e. user, group and all).
*
* To ensure that the values behave as expected (and numbers don't carry
* from one digit to the next) we do the calculation on the octal value
* using bitwise operations. This lets us remove, for example, 0222 from
* 0700 and get the correct value of 0500.
*
Dries Buytaert
committed
* @param $file
* The name of the file with permissions to fix.
* @param $mask
* The desired permissions for the file.
* @param $message
* (optional) Whether to output messages. Defaults to TRUE.
* @return
* TRUE/FALSE whether or not we were able to fix the file's permissions.
*/
function drupal_install_fix_file($file, $mask, $message = TRUE) {
Angie Byron
committed
// If $file does not exist, fileperms() issues a PHP warning.
if (!file_exists($file)) {
return FALSE;
}
$mod = fileperms($file) & 0777;
Dries Buytaert
committed
$masks = array(FILE_READABLE, FILE_WRITABLE, FILE_EXECUTABLE, FILE_NOT_READABLE, FILE_NOT_WRITABLE, FILE_NOT_EXECUTABLE);
// FILE_READABLE, FILE_WRITABLE, and FILE_EXECUTABLE permission strings
// can theoretically be 0400, 0200, and 0100 respectively, but to be safe
// we set all three access types in case the administrator intends to
// change the owner of settings.php after installation.
Dries Buytaert
committed
foreach ($masks as $m) {
if ($mask & $m) {
switch ($m) {
case FILE_READABLE:
if (!is_readable($file)) {
$mod |= 0444;
Dries Buytaert
committed
}
break;
case FILE_WRITABLE:
if (!is_writable($file)) {
$mod |= 0222;
Dries Buytaert
committed
}
break;
case FILE_EXECUTABLE:
if (!is_executable($file)) {
$mod |= 0111;
Dries Buytaert
committed
}
break;
case FILE_NOT_READABLE:
if (is_readable($file)) {
$mod &= ~0444;
Dries Buytaert
committed
}
break;
case FILE_NOT_WRITABLE:
if (is_writable($file)) {
$mod &= ~0222;
Dries Buytaert
committed
}
break;
case FILE_NOT_EXECUTABLE:
if (is_executable($file)) {
$mod &= ~0111;
Dries Buytaert
committed
}
break;
}
}
}
// chmod() will work if the web server is running as owner of the file.
// If PHP safe_mode is enabled the currently executing script must also
// have the same owner.
if (@chmod($file, $mod)) {
Dries Buytaert
committed
return TRUE;
}
else {
return FALSE;
}
}