Newer
Older
<?php
// $Id$
/**
* 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);
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
71
/**
* 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();
$info = install_profile_info($profile);
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() {
$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';
spl_autoload_register('db_autoload');
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) {
$class = 'DatabaseTasks_' . $driver;
$installer = new $class();
Dries Buytaert
committed
if ($installer->installable()) {
$databases[$driver] = $installer->name();
}
}
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(
'CREATE TABLE drupal_install_test (id int NULL)',
'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(
'INSERT INTO drupal_install_test (id) VALUES (1)',
'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(
'UPDATE drupal_install_test SET id = 2',
'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(
'DELETE FROM drupal_install_test',
'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(
'DROP TABLE drupal_install_test',
'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
),
);
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())));
}
}
Dries Buytaert
committed
}
Dries Buytaert
committed
/**
* @class Exception class used to throw error if the DatabaseInstaller fails.
*/
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
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
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) {
// First, retrieve all the module's menu paths from db.
drupal_load('module', $module);
$paths = module_invoke($module, 'menu');
// 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);
// Now remove the menu links for all paths declared by this module.
if (!empty($paths)) {
$paths = array_keys($paths);
// Clean out the names of load functions.
foreach ($paths as $index => $path) {
$parts = explode('/', $path, MENU_MAX_PARTS);
foreach ($parts as $k => $part) {
if (preg_match('/^%[a-z_]*$/', $part)) {
$parts[$k] = '%';
}
$paths[$index] = implode('/', $parts);
$result = db_select('menu_links')
->fields('menu_links')
->condition('router_path', $paths, 'IN')
->condition('external', 0)
->orderBy('depth')
->execute();
// Remove all such items. Starting from those with the greatest depth will
// minimize the amount of re-parenting done by menu_link_delete().
foreach ($result as $item) {
_menu_delete_item($item, TRUE);
}
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
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
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
784
785
786
787
788
789
790
791
792
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;
}
}
/**
* Send the user to a different installer page.
*
* This issues an on-site HTTP redirect. Messages (and errors) are erased.
*
* @param $path
* An installer path.
*/
function install_goto($path) {
Dries Buytaert
committed
global $base_url;
include_once DRUPAL_ROOT . '/includes/common.inc';
header('Location: ' . $base_url . '/' . $path);
Gábor Hojtsy
committed
header('Cache-Control: no-cache'); // Not a permanent redirect.
Dries Buytaert
committed
drupal_exit();
Dries Buytaert
committed
/**
* Functional equivalent of t(), used when some systems are not available.
*
* Used during the install process, when database, theme, and localization
Gábor Hojtsy
committed
* system is possibly not yet available.
*
* @see t()
Dries Buytaert
committed
* @ingroup sanitization
Dries Buytaert
committed
*/
function st($string, array $args = array(), array $options = array()) {
Dries Buytaert
committed
global $install_state;
if (empty($options['context'])) {
$options['context'] = '';
}
if (!isset($locale_strings)) {
$locale_strings = array();
Dries Buytaert
committed
if (isset($install_state['parameters']['profile']) && isset($install_state['parameters']['locale'])) {
Angie Byron
committed
$filename = 'profiles/' . $install_state['parameters']['profile'] . '/translations/' . $install_state['parameters']['locale'] . '.po';
if (file_exists(DRUPAL_ROOT . '/' . $filename)) {
require_once DRUPAL_ROOT . '/includes/locale.inc';
$file = (object) array('uri' => $filename);
_locale_import_read_po('mem-store', $file);
$locale_strings = _locale_import_one_string('mem-report');
}
Dries Buytaert
committed
}
Angie Byron
committed
require_once DRUPAL_ROOT . '/includes/theme.inc';
// Transform arguments before inserting them
foreach ($args as $key => $value) {
Neil Drumm
committed
switch ($key[0]) {
// Escaped only
case '@':
$args[$key] = check_plain($value);
break;
// Escaped and placeholder
case '%':
default:
$args[$key] = '<em>' . check_plain($value) . '</em>';
Neil Drumm
committed
break;
// Pass-through
case '!':
}
return strtr((!empty($locale_strings[$options['context']][$string]) ? $locale_strings[$options['context']][$string] : $string), $args);
Dries Buytaert
committed
}
Steven Wittens
committed
/**
* Check an install profile's requirements.
Steven Wittens
committed
*
* @param $profile
* Name of install profile to check.
* @return
* Array of the install profile's requirements.
Steven Wittens
committed
*/
function drupal_check_profile($profile) {
Angie Byron
committed
include_once DRUPAL_ROOT . '/includes/file.inc';
Steven Wittens
committed
Angie Byron
committed
$profile_file = DRUPAL_ROOT . "/profiles/$profile/$profile.profile";
Steven Wittens
committed
if (!isset($profile) || !file_exists($profile_file)) {
Dries Buytaert
committed
throw new Exception(install_no_profile_error());
Steven Wittens
committed
}
$info = install_profile_info($profile);
Steven Wittens
committed
Angie Byron
committed
// Collect requirement testing results.
Steven Wittens
committed
$requirements = array();
Angie Byron
committed
foreach ($info['dependencies'] as $module) {
module_load_install($module);