2005-12-08 08:40:10 +00:00
< ? php
// $Id$
2008-05-26 17:12:55 +00:00
/**
* Indicates that a module has not been installed yet .
*/
2006-01-21 01:42:52 +00:00
define ( 'SCHEMA_UNINSTALLED' , - 1 );
2008-05-26 17:12:55 +00:00
/**
* Indicates that a module has been installed .
*/
2006-01-21 01:42:52 +00:00
define ( 'SCHEMA_INSTALLED' , 0 );
2005-12-08 08:40:10 +00:00
2008-05-26 17:12:55 +00:00
/**
* Requirement severity -- Informational message only .
*/
2006-09-01 09:23:45 +00:00
define ( 'REQUIREMENT_INFO' , - 1 );
2008-05-26 17:12:55 +00:00
/**
* Requirement severity -- Requirement successfully met .
*/
2006-09-01 08:44:53 +00:00
define ( 'REQUIREMENT_OK' , 0 );
2008-05-26 17:12:55 +00:00
/**
* Requirement severity -- Warning condition ; proceed but flag warning .
*/
2006-09-01 08:44:53 +00:00
define ( 'REQUIREMENT_WARNING' , 1 );
2008-05-26 17:12:55 +00:00
/**
* Requirement severity -- Error condition ; abort installation .
*/
2006-09-01 08:44:53 +00:00
define ( 'REQUIREMENT_ERROR' , 2 );
2005-12-08 08:40:10 +00:00
2008-05-26 17:12:55 +00:00
/**
* 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 .
*/
2006-07-13 13:14:25 +00:00
define ( 'FILE_NOT_EXECUTABLE' , 128 );
2005-12-08 08:40:10 +00:00
2006-07-31 19:24:16 +00:00
/**
* Initialize the update system by loading all installed module ' s . install files .
*/
function drupal_load_updates () {
2007-12-17 12:23:01 +00:00
foreach ( drupal_get_installed_schema_version ( NULL , FALSE , TRUE ) as $module => $schema_version ) {
if ( $schema_version > - 1 ) {
module_load_install ( $module );
}
2005-12-08 08:40:10 +00:00
}
}
/**
2006-04-11 11:33:15 +00:00
* Returns an array of available schema versions for a module .
2005-12-08 08:40:10 +00:00
*
* @ param $module
* A module name .
* @ return
2008-11-11 16:49:38 +00:00
* If the module has updates , an array of available updates sorted by version .
2008-08-14 09:25:48 +00:00
* Otherwise , FALSE .
2005-12-08 08:40:10 +00:00
*/
function drupal_get_schema_versions ( $module ) {
2009-09-23 15:04:34 +00:00
$updates = & drupal_static ( __FUNCTION__ , NULL );
2009-09-25 14:54:49 +00:00
if ( ! isset ( $updates [ $module ])) {
2009-09-23 15:04:34 +00:00
$updates = array ();
2010-03-07 06:53:26 +00:00
foreach ( module_list () as $loaded_module ) {
$updates [ $loaded_module ] = array ();
}
2009-09-23 15:04:34 +00:00
// Prepare regular expression to match all possible defined hook_update_N().
2009-09-25 14:54:49 +00:00
$regexp = '/^(?P<module>.+)_update_(?P<version>\d+)$/' ;
2009-09-23 15:04:34 +00:00
$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' ];
2005-12-09 16:14:26 +00:00
}
2005-12-09 15:46:47 +00:00
}
2009-09-23 15:04:34 +00:00
// Ensure that updates are applied in numerical order.
foreach ( $updates as & $module_updates ) {
sort ( $module_updates , SORT_NUMERIC );
}
2005-12-08 08:40:10 +00:00
}
2010-03-07 06:53:26 +00:00
return empty ( $updates [ $module ]) ? FALSE : $updates [ $module ];
2005-12-08 08:40:10 +00:00
}
/**
* Returns the currently installed schema version for a module .
*
* @ param $module
* A module name .
2007-12-17 12:23:01 +00:00
* @ param $reset
* Set to TRUE after modifying the system table .
* @ param $array
2007-12-18 10:31:42 +00:00
* Set to TRUE if you want to get information about all modules in the
2007-12-17 12:23:01 +00:00
* system .
2005-12-08 08:40:10 +00:00
* @ return
2010-02-03 18:16:23 +00:00
* The currently installed schema version , or SCHEMA_UNINSTALLED if the
* module is not installed .
2005-12-08 08:40:10 +00:00
*/
2007-12-17 12:23:01 +00:00
function drupal_get_installed_schema_version ( $module , $reset = FALSE , $array = FALSE ) {
2007-02-04 21:21:44 +00:00
static $versions = array ();
2005-12-08 08:40:10 +00:00
if ( $reset ) {
2007-02-04 21:21:44 +00:00
$versions = array ();
2005-12-08 08:40:10 +00:00
}
if ( ! $versions ) {
$versions = array ();
2009-01-04 18:50:23 +00:00
$result = db_query ( " SELECT name, schema_version FROM { system} WHERE type = :type " , array ( ':type' => 'module' ));
foreach ( $result as $row ) {
2005-12-08 08:40:10 +00:00
$versions [ $row -> name ] = $row -> schema_version ;
}
}
2010-02-03 18:16:23 +00:00
if ( $array ) {
return $versions ;
}
else {
return isset ( $versions [ $module ]) ? $versions [ $module ] : SCHEMA_UNINSTALLED ;
}
2005-12-08 08:40:10 +00:00
}
/**
* 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 ) {
2009-01-04 18:50:23 +00:00
db_update ( 'system' )
-> fields ( array ( 'schema_version' => $version ))
-> condition ( 'name' , $module )
-> execute ();
2005-12-08 08:40:10 +00:00
}
2006-07-13 13:14:25 +00:00
/**
2009-12-07 06:19:20 +00:00
* Loads the install profile , extracting its defined distribution name .
2006-07-13 13:14:25 +00:00
*
* @ return
2009-12-07 06:19:20 +00:00
* 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 ()
2006-07-13 13:14:25 +00:00
*/
2009-12-07 06:19:20 +00:00
function drupal_install_profile_distribution_name () {
2010-06-21 02:27:47 +00:00
// 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' ];
}
2006-07-13 13:14:25 +00:00
}
/**
* 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' ];
2008-04-14 17:48:46 +00:00
$port = ( $_SERVER [ 'SERVER_PORT' ] == 80 ? '' : ':' . $_SERVER [ 'SERVER_PORT' ]);
2006-09-01 08:44:53 +00:00
$uri = preg_replace ( " / \ ?.*/ " , '' , $_SERVER [ 'REQUEST_URI' ]);
2006-07-13 13:14:25 +00:00
$dir = str_replace ( " / $file " , '' , $uri );
return " $proto $host $port $dir " ;
}
/**
2008-09-14 01:58:17 +00:00
* Detect all supported databases that are compiled into PHP .
2006-07-13 13:14:25 +00:00
*
* @ return
* An array of database types compiled into PHP .
*/
function drupal_detect_database_types () {
$databases = array ();
2008-08-22 12:43:53 +00:00
// We define a driver as a directory in /includes/database that in turn
2008-12-20 18:24:41 +00:00
// contains a database.inc file. That allows us to drop in additional drivers
2008-08-22 12:43:53 +00:00
// without modifying the installer.
// Because we have no registry yet, we need to also include the install.inc
// file for the driver explicitly.
2009-07-28 12:13:47 +00:00
require_once DRUPAL_ROOT . '/includes/database/database.inc' ;
2010-04-11 17:16:45 +00:00
spl_autoload_register ( 'db_autoload' );
2009-02-18 15:07:27 +00:00
foreach ( file_scan_directory ( DRUPAL_ROOT . '/includes/database' , '/^[a-z]*$/i' , array ( 'recurse' => FALSE )) as $file ) {
2010-07-31 12:29:31 +00:00
if ( file_exists ( $file -> uri . '/database.inc' ) && file_exists ( $file -> uri . '/install.inc' )) {
$drivers [ $file -> filename ] = $file -> uri ;
}
2008-08-22 12:43:53 +00:00
}
foreach ( $drivers as $driver => $file ) {
2009-07-27 19:42:56 +00:00
$class = 'DatabaseTasks_' . $driver ;
2008-08-21 19:36:39 +00:00
$installer = new $class ();
if ( $installer -> installable ()) {
$databases [ $driver ] = $installer -> name ();
}
}
2008-08-22 12:43:53 +00:00
2008-12-30 16:43:20 +00:00
// Usability: unconditionally put the MySQL driver on top.
2008-08-28 08:40:33 +00:00
if ( isset ( $databases [ 'mysql' ])) {
$mysql_database = $databases [ 'mysql' ];
unset ( $databases [ 'mysql' ]);
$databases = array ( 'mysql' => $mysql_database ) + $databases ;
}
2008-11-11 16:49:38 +00:00
2008-08-21 19:36:39 +00:00
return $databases ;
}
2009-07-27 19:42:56 +00:00
/**
* 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 (
'arguments' => array (
'CREATE TABLE drupal_install_test (id int NULL)' ,
'Drupal can use CREATE TABLE database commands.' ,
2010-07-30 01:59:14 +00:00
'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>' ,
2009-07-27 19:42:56 +00:00
TRUE ,
),
2008-08-21 19:36:39 +00:00
),
2009-07-27 19:42:56 +00:00
array (
'arguments' => array (
'INSERT INTO drupal_install_test (id) VALUES (1)' ,
'Drupal can use INSERT database commands.' ,
2010-07-30 01:59:14 +00:00
'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.' ,
2009-07-27 19:42:56 +00:00
),
2008-08-21 19:36:39 +00:00
),
2009-07-27 19:42:56 +00:00
array (
'arguments' => array (
'UPDATE drupal_install_test SET id = 2' ,
'Drupal can use UPDATE database commands.' ,
2010-07-30 01:59:14 +00:00
'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.' ,
2009-07-27 19:42:56 +00:00
),
2008-08-21 19:36:39 +00:00
),
2009-07-27 19:42:56 +00:00
array (
'arguments' => array (
'DELETE FROM drupal_install_test' ,
'Drupal can use DELETE database commands.' ,
2010-07-30 01:59:14 +00:00
'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.' ,
2009-07-27 19:42:56 +00:00
),
2008-08-21 19:36:39 +00:00
),
2009-07-27 19:42:56 +00:00
array (
'arguments' => array (
'DROP TABLE drupal_install_test' ,
'Drupal can use DROP TABLE database commands.' ,
2010-07-30 01:59:14 +00:00
'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.' ,
2009-07-27 19:42:56 +00:00
),
2008-08-21 19:36:39 +00:00
),
);
2009-07-27 19:42:56 +00:00
/**
* Results from tasks .
*
* @ var array
*/
protected $results = array ();
2008-08-21 19:36:39 +00:00
2009-07-27 19:42:56 +00:00
/**
* Ensure the PDO driver is supported by the version of PHP in use .
*/
2008-08-21 19:36:39 +00:00
protected function hasPdoDriver () {
return in_array ( $this -> pdoDriver , PDO :: getAvailableDrivers ());
}
2009-07-27 19:42:56 +00:00
/**
* 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 .
*/
2008-08-21 19:36:39 +00:00
public function installable () {
2009-07-27 19:42:56 +00:00
return $this -> hasPdoDriver () && empty ( $this -> error );
2008-08-21 19:36:39 +00:00
}
abstract public function name ();
2009-07-27 19:42:56 +00:00
/**
* 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 {
2009-07-28 19:02:38 +00:00
throw new DatabaseTaskException ( st ( " Failed to run all tasks against the database server. The task %task wasn't found. " , array ( '%task' => $task [ 'function' ])));
2009-07-27 19:42:56 +00:00
}
}
2008-08-21 19:36:39 +00:00
}
2009-07-27 19:42:56 +00:00
// Check for failed results and compile message
$message = '' ;
foreach ( $this -> results as $result => $success ) {
if ( ! $success ) {
$message .= '<p class="error">' . $result . '</p>' ;
2006-07-13 13:14:25 +00:00
}
}
2009-07-27 19:42:56 +00:00
if ( ! empty ( $message )) {
2010-04-30 05:20:36 +00:00
$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 ;
2009-07-27 19:42:56 +00:00
throw new DatabaseTaskException ( $message );
}
2006-07-13 13:14:25 +00:00
}
2008-08-21 19:36:39 +00:00
/**
* Check if we can connect to the database .
*/
2009-07-27 19:42:56 +00:00
protected function connect () {
2008-08-21 19:36:39 +00:00
try {
2009-06-08 04:33:35 +00:00
// This doesn't actually test the connection.
2008-08-21 19:36:39 +00:00
db_set_active ();
2009-06-08 04:33:35 +00:00
// Now actually do a check.
Database :: getConnection ();
2009-07-27 19:42:56 +00:00
$this -> pass ( 'Drupal can CONNECT to the database ok.' );
2008-08-21 19:36:39 +00:00
}
catch ( Exception $e ) {
2010-07-30 01:59:14 +00:00
$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 ())));
2008-08-21 19:36:39 +00:00
return FALSE ;
}
2009-07-27 19:42:56 +00:00
return TRUE ;
2008-08-21 19:36:39 +00:00
}
2009-07-27 19:42:56 +00:00
/**
* Run SQL tests to ensure the database can execute commands with the current user .
*/
protected function runTestQuery ( $query , $pass , $fail , $fatal = FALSE ) {
2008-08-21 19:36:39 +00:00
try {
db_query ( $query );
2009-07-27 19:42:56 +00:00
$this -> pass ( st ( $pass ));
2008-08-21 19:36:39 +00:00
}
catch ( Exception $e ) {
2009-07-27 19:42:56 +00:00
$this -> fail ( st ( $fail , array ( '%query' => $query , '%error' => $e -> getMessage (), '%name' => $this -> name ())));
return ! $fatal ;
2008-08-21 19:36:39 +00:00
}
}
2006-07-13 13:14:25 +00:00
}
2009-07-27 19:42:56 +00:00
/**
* @ class Exception class used to throw error if the DatabaseInstaller fails .
*/
class DatabaseTaskException extends Exception {
}
2006-07-13 13:14:25 +00:00
/**
2008-09-14 01:58:17 +00:00
* Replace values in settings . php with values in the submitted array .
2006-07-13 13:14:25 +00:00
*
* @ param $settings
* An array of settings that need to be updated .
*/
function drupal_rewrite_settings ( $settings = array (), $prefix = '' ) {
2008-09-20 20:22:25 +00:00
$default_settings = 'sites/default/default.settings.php' ;
2009-04-02 20:39:45 +00:00
drupal_static_reset ( 'conf_path' );
$settings_file = conf_path ( FALSE ) . '/' . $prefix . 'settings.php' ;
2006-07-13 13:14:25 +00:00
// 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 ;
2008-09-20 20:22:25 +00:00
if ( $fp = fopen ( DRUPAL_ROOT . '/' . $default_settings , 'r' )) {
2006-07-13 13:14:25 +00:00
// 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 ]];
2008-04-14 17:48:46 +00:00
$buffer .= str_replace ( $variable [ 2 ], " ' " . $setting [ 'value' ] . " ' " , $line );
2006-07-13 13:14:25 +00:00
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 ]];
2008-08-21 19:36:39 +00:00
$buffer .= '$' . $variable [ 1 ] . " = " . var_export ( $setting [ 'value' ], TRUE ) . " ; " . ( ! empty ( $setting [ 'comment' ]) ? ' // ' . $setting [ 'comment' ] . " \n " : " \n " );
2006-07-13 13:14:25 +00:00
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' ]) {
2008-08-21 19:36:39 +00:00
$buffer .= " \$ $setting = " . var_export ( $data [ 'value' ], TRUE ) . " ; \n " ;
2006-07-13 13:14:25 +00:00
}
}
2008-09-20 20:22:25 +00:00
$fp = fopen ( DRUPAL_ROOT . '/' . $settings_file , 'w' );
2006-07-13 13:14:25 +00:00
if ( $fp && fwrite ( $fp , $buffer ) === FALSE ) {
2010-01-09 23:03:22 +00:00
throw new Exception ( st ( 'Failed to modify %settings. Verify the file permissions.' , array ( '%settings' => $settings_file )));
2006-07-13 13:14:25 +00:00
}
}
else {
2010-01-09 23:03:22 +00:00
throw new Exception ( st ( 'Failed to open %settings. Verify the file permissions.' , array ( '%settings' => $default_settings )));
2006-07-13 13:14:25 +00:00
}
}
/**
2008-09-14 01:58:17 +00:00
* Verify an install profile for installation .
2006-07-13 13:14:25 +00:00
*
2009-07-28 12:13:47 +00:00
* @ param $install_state
* An array of information about the current installation state .
2006-08-03 01:02:51 +00:00
* @ return
* The list of modules to install .
2006-07-13 13:14:25 +00:00
*/
2009-07-28 12:13:47 +00:00
function drupal_verify_profile ( $install_state ) {
$profile = $install_state [ 'parameters' ][ 'profile' ];
$locale = $install_state [ 'parameters' ][ 'locale' ];
2008-09-20 20:22:25 +00:00
include_once DRUPAL_ROOT . '/includes/file.inc' ;
include_once DRUPAL_ROOT . '/includes/common.inc' ;
2006-07-13 13:14:25 +00:00
2008-09-20 20:22:25 +00:00
$profile_file = DRUPAL_ROOT . " /profiles/ $profile / $profile .profile " ;
2006-07-13 13:14:25 +00:00
if ( ! isset ( $profile ) || ! file_exists ( $profile_file )) {
2009-07-28 12:13:47 +00:00
throw new Exception ( install_no_profile_error ());
2006-07-13 13:14:25 +00:00
}
2009-07-28 12:13:47 +00:00
$info = $install_state [ 'profile_info' ];
2006-08-03 07:06:36 +00:00
2006-10-23 06:45:17 +00:00
// Get a list of modules that exist in Drupal's assorted subdirectories.
$present_modules = array ();
2008-09-20 03:49:24 +00:00
foreach ( drupal_system_listing ( '/\.module$/' , 'modules' , 'name' , 0 ) as $present_module ) {
2006-10-23 06:45:17 +00:00
$present_modules [] = $present_module -> name ;
}
2009-08-21 07:50:08 +00:00
// 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 ();
2006-10-23 06:45:17 +00:00
// Verify that all of the profile's required modules are present.
2009-07-15 02:08:41 +00:00
$missing_modules = array_diff ( $info [ 'dependencies' ], $present_modules );
2008-10-01 00:27:29 +00:00
$requirements = array ();
2008-11-11 16:49:38 +00:00
2006-10-23 06:45:17 +00:00
if ( count ( $missing_modules )) {
2008-10-01 00:27:29 +00:00
$modules = array ();
2007-01-02 05:05:38 +00:00
foreach ( $missing_modules as $module ) {
2008-10-01 00:27:29 +00:00
$modules [] = '<span class="admin-missing">' . drupal_ucfirst ( $module ) . '</span>' ;
2006-08-03 01:02:51 +00:00
}
2008-10-01 00:27:29 +00:00
$requirements [ 'required_modules' ] = array (
'title' => st ( 'Required modules' ),
'value' => st ( 'Required modules not found.' ),
'severity' => REQUIREMENT_ERROR ,
2010-01-09 23:03:22 +00:00
'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 ))),
2008-10-01 00:27:29 +00:00
);
2006-08-03 01:02:51 +00:00
}
2008-10-01 00:27:29 +00:00
return $requirements ;
2006-08-03 01:02:51 +00:00
}
2006-07-29 17:56:41 +00:00
2007-12-08 15:15:25 +00:00
/**
* 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 () {
2010-05-18 18:11:13 +00:00
$system_path = drupal_get_path ( 'module' , 'system' );
2008-09-20 20:22:25 +00:00
require_once DRUPAL_ROOT . '/' . $system_path . '/system.install' ;
2006-08-03 01:02:51 +00:00
module_invoke ( 'system' , 'install' );
2008-11-11 16:49:38 +00:00
2006-08-03 01:02:51 +00:00
$system_versions = drupal_get_schema_versions ( 'system' );
$system_version = $system_versions ? max ( $system_versions ) : SCHEMA_INSTALLED ;
2009-01-04 18:50:23 +00:00
db_insert ( 'system' )
2009-08-24 00:14:23 +00:00
-> fields ( array ( 'filename' , 'name' , 'type' , 'owner' , 'status' , 'schema_version' , 'bootstrap' ))
2009-01-04 18:50:23 +00:00
-> values ( array (
'filename' => $system_path . '/system.module' ,
'name' => 'system' ,
'type' => 'module' ,
'owner' => '' ,
'status' => 1 ,
2009-08-24 00:14:23 +00:00
'schema_version' => $system_version ,
'bootstrap' => 0 ,
))
2009-01-04 18:50:23 +00:00
-> execute ();
2009-10-13 05:26:57 +00:00
system_rebuild_module_data ();
2006-07-19 07:45:35 +00:00
}
2006-09-01 07:40:08 +00:00
/**
* Calls the uninstall function and updates the system table for a given module .
*
2008-10-11 22:46:22 +00:00
* @ param $module_list
* The modules to uninstall .
2006-09-01 07:40:08 +00:00
*/
2008-10-11 22:46:22 +00:00
function drupal_uninstall_modules ( $module_list = array ()) {
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' );
2009-09-10 06:38:20 +00:00
drupal_uninstall_schema ( $module );
2009-05-12 18:08:43 +00:00
watchdog ( 'system' , '%module module uninstalled.' , array ( '%module' => $module ), WATCHDOG_INFO );
2008-10-11 22:46:22 +00:00
// 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 ] = '%' ;
}
2007-08-29 20:46:18 +00:00
}
2008-10-11 22:46:22 +00:00
$paths [ $index ] = implode ( '/' , $parts );
2007-08-29 20:46:18 +00:00
}
2007-08-30 19:54:22 +00:00
2009-01-04 18:50:23 +00:00
$result = db_select ( 'menu_links' )
2009-05-24 17:39:35 +00:00
-> fields ( 'menu_links' )
-> condition ( 'router_path' , $paths , 'IN' )
-> condition ( 'external' , 0 )
-> orderBy ( 'depth' )
-> execute ();
2008-10-11 22:46:22 +00:00
// Remove all such items. Starting from those with the greatest depth will
// minimize the amount of re-parenting done by menu_link_delete().
2009-01-04 18:50:23 +00:00
foreach ( $result as $item ) {
2008-10-11 22:46:22 +00:00
_menu_delete_item ( $item , TRUE );
}
2007-08-29 20:46:18 +00:00
}
2008-10-11 22:46:22 +00:00
drupal_set_installed_schema_version ( $module , SCHEMA_UNINSTALLED );
2007-08-29 20:46:18 +00:00
}
2008-10-11 22:46:22 +00:00
if ( ! empty ( $module_list )) {
// Call hook_module_uninstall to let other modules act
module_invoke_all ( 'modules_uninstalled' , $module_list );
}
2006-09-01 07:40:08 +00:00
}
2006-07-13 13:14:25 +00:00
/**
* 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
2006-12-05 05:47:37 +00:00
* TRUE on success or FALSE on failure . A message is set for the latter .
2006-07-13 13:14:25 +00:00
*/
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 )) {
2008-04-14 17:48:46 +00:00
$check = 'is_' . $type ;
2006-07-13 13:14:25 +00:00
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 .
*
2008-09-14 01:58:17 +00:00
* @ param $file
2006-07-13 13:14:25 +00:00
* The name of the directory to create ;
2008-09-14 01:58:17 +00:00
* @ param $mask
2006-07-13 13:14:25 +00:00
* 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 :
2010-07-26 13:20:01 +00:00
$mod |= 0444 ;
2006-07-13 13:14:25 +00:00
break ;
case FILE_WRITABLE :
2010-07-26 13:20:01 +00:00
$mod |= 0222 ;
2006-07-13 13:14:25 +00:00
break ;
case FILE_EXECUTABLE :
2010-07-26 13:20:01 +00:00
$mod |= 0111 ;
2006-07-13 13:14:25 +00:00
break ;
}
}
}
2010-07-26 13:20:01 +00:00
if ( @ drupal_mkdir ( $file , $mod )) {
2006-07-13 13:14:25 +00:00
return TRUE ;
}
else {
return FALSE ;
}
}
/**
* Attempt to fix file permissions .
*
2006-12-08 11:54:04 +00:00
* 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.
*
2006-07-13 13:14:25 +00:00
* @ 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 ) {
2008-09-27 20:03:35 +00:00
// If $file does not exist, fileperms() issues a PHP warning.
if ( ! file_exists ( $file )) {
return FALSE ;
}
2006-12-08 11:54:04 +00:00
$mod = fileperms ( $file ) & 0777 ;
2006-07-13 13:14:25 +00:00
$masks = array ( FILE_READABLE , FILE_WRITABLE , FILE_EXECUTABLE , FILE_NOT_READABLE , FILE_NOT_WRITABLE , FILE_NOT_EXECUTABLE );
2006-12-08 11:54:04 +00:00
// 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.
2006-07-13 13:14:25 +00:00
foreach ( $masks as $m ) {
if ( $mask & $m ) {
switch ( $m ) {
case FILE_READABLE :
if ( ! is_readable ( $file )) {
2006-12-08 11:54:04 +00:00
$mod |= 0444 ;
2006-07-13 13:14:25 +00:00
}
break ;
case FILE_WRITABLE :
if ( ! is_writable ( $file )) {
2006-12-08 11:54:04 +00:00
$mod |= 0222 ;
2006-07-13 13:14:25 +00:00
}
break ;
case FILE_EXECUTABLE :
if ( ! is_executable ( $file )) {
2006-12-08 11:54:04 +00:00
$mod |= 0111 ;
2006-07-13 13:14:25 +00:00
}
break ;
case FILE_NOT_READABLE :
if ( is_readable ( $file )) {
2006-12-08 11:54:04 +00:00
$mod &= ~ 0444 ;
2006-07-13 13:14:25 +00:00
}
break ;
case FILE_NOT_WRITABLE :
if ( is_writable ( $file )) {
2006-12-08 11:54:04 +00:00
$mod &= ~ 0222 ;
2006-07-13 13:14:25 +00:00
}
break ;
case FILE_NOT_EXECUTABLE :
if ( is_executable ( $file )) {
2006-12-08 11:54:04 +00:00
$mod &= ~ 0111 ;
2006-07-13 13:14:25 +00:00
}
break ;
}
}
}
2006-12-08 11:54:04 +00:00
// 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 )) {
2006-07-13 13:14:25 +00:00
return TRUE ;
}
else {
return FALSE ;
}
}
2006-07-31 19:24:16 +00:00
/**
2008-11-11 16:49:38 +00:00
* Send the user to a different installer page .
2008-09-14 01:58:17 +00:00
*
* This issues an on - site HTTP redirect . Messages ( and errors ) are erased .
2006-07-31 19:24:16 +00:00
*
* @ param $path
* An installer path .
*/
function install_goto ( $path ) {
2006-10-12 15:20:08 +00:00
global $base_url ;
2010-01-21 04:24:14 +00:00
include_once DRUPAL_ROOT . '/includes/common.inc' ;
2008-04-14 17:48:46 +00:00
header ( 'Location: ' . $base_url . '/' . $path );
2007-09-07 10:48:24 +00:00
header ( 'Cache-Control: no-cache' ); // Not a permanent redirect.
2009-10-13 21:16:44 +00:00
drupal_exit ();
2006-07-31 19:24:16 +00:00
}
2006-07-13 13:14:25 +00:00
/**
2008-09-14 01:58:17 +00:00
* Functional equivalent of t (), used when some systems are not available .
*
* Used during the install process , when database , theme , and localization
2007-11-11 16:14:45 +00:00
* system is possibly not yet available .
2008-09-14 01:58:17 +00:00
*
* @ see t ()
2010-04-24 14:53:59 +00:00
* @ ingroup sanitization
2006-07-13 13:14:25 +00:00
*/
2010-04-28 05:12:43 +00:00
function st ( $string , array $args = array (), array $options = array ()) {
2006-09-01 05:38:40 +00:00
static $locale_strings = NULL ;
2009-07-28 12:13:47 +00:00
global $install_state ;
2006-09-01 05:38:40 +00:00
2010-04-28 05:12:43 +00:00
if ( empty ( $options [ 'context' ])) {
$options [ 'context' ] = '' ;
}
2006-09-01 05:38:40 +00:00
if ( ! isset ( $locale_strings )) {
$locale_strings = array ();
2009-07-28 12:13:47 +00:00
if ( isset ( $install_state [ 'parameters' ][ 'profile' ]) && isset ( $install_state [ 'parameters' ][ 'locale' ])) {
2009-08-24 03:13:44 +00:00
$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' );
}
2009-07-28 12:13:47 +00:00
}
2006-09-01 05:38:40 +00:00
}
2008-09-20 20:22:25 +00:00
require_once DRUPAL_ROOT . '/includes/theme.inc' ;
2006-08-18 12:17:00 +00:00
// Transform arguments before inserting them
2007-01-02 05:05:38 +00:00
foreach ( $args as $key => $value ) {
2006-09-07 08:23:54 +00:00
switch ( $key [ 0 ]) {
// Escaped only
case '@' :
$args [ $key ] = check_plain ( $value );
break ;
// Escaped and placeholder
case '%' :
default :
2008-04-14 17:48:46 +00:00
$args [ $key ] = '<em>' . check_plain ( $value ) . '</em>' ;
2006-09-07 08:23:54 +00:00
break ;
// Pass-through
case '!' :
}
2006-08-18 12:17:00 +00:00
}
2010-04-28 05:12:43 +00:00
return strtr (( ! empty ( $locale_strings [ $options [ 'context' ]][ $string ]) ? $locale_strings [ $options [ 'context' ]][ $string ] : $string ), $args );
2006-08-08 21:18:04 +00:00
}
2006-09-01 08:44:53 +00:00
/**
2008-09-14 01:58:17 +00:00
* Check an install profile ' s requirements .
2006-09-01 08:44:53 +00:00
*
2008-09-14 01:58:17 +00:00
* @ param $profile
* Name of install profile to check .
* @ return
* Array of the install profile ' s requirements .
2006-09-01 08:44:53 +00:00
*/
function drupal_check_profile ( $profile ) {
2008-09-20 20:22:25 +00:00
include_once DRUPAL_ROOT . '/includes/file.inc' ;
2006-09-01 08:44:53 +00:00
2008-09-20 20:22:25 +00:00
$profile_file = DRUPAL_ROOT . " /profiles/ $profile / $profile .profile " ;
2006-09-01 08:44:53 +00:00
if ( ! isset ( $profile ) || ! file_exists ( $profile_file )) {
2009-07-28 12:13:47 +00:00
throw new Exception ( install_no_profile_error ());
2006-09-01 08:44:53 +00:00
}
2009-07-15 02:08:41 +00:00
$info = install_profile_info ( $profile );
2006-09-01 08:44:53 +00:00
2009-12-27 03:37:54 +00:00
// Collect requirement testing results.
2006-09-01 08:44:53 +00:00
$requirements = array ();
2009-12-27 03:37:54 +00:00
foreach ( $info [ 'dependencies' ] as $module ) {
module_load_install ( $module );
$function = $module . '_requirements' ;
2008-05-06 12:18:54 +00:00
if ( function_exists ( $function )) {
$requirements = array_merge ( $requirements , $function ( 'install' ));
2006-09-05 02:28:11 +00:00
}
2006-09-01 08:44:53 +00:00
}
return $requirements ;
}
/**
* Extract highest severity from requirements array .
2008-09-14 01:58:17 +00:00
*
* @ param $requirements
2008-11-11 16:49:38 +00:00
* An array of requirements , in the same format as is returned by
2008-09-14 01:58:17 +00:00
* hook_requirements () .
* @ return
* The highest severity in the array .
2006-09-01 08:44:53 +00:00
*/
function drupal_requirements_severity ( & $requirements ) {
$severity = REQUIREMENT_OK ;
foreach ( $requirements as $requirement ) {
if ( isset ( $requirement [ 'severity' ])) {
$severity = max ( $severity , $requirement [ 'severity' ]);
}
}
return $severity ;
}
/**
* Check a module ' s requirements .
2008-09-14 01:58:17 +00:00
*
* @ param $module
* Machine name of module to check .
* @ return
* TRUE / FALSE depending on the requirements are in place .
2006-09-01 08:44:53 +00:00
*/
function drupal_check_module ( $module ) {
2009-12-27 03:37:54 +00:00
module_load_install ( $module );
if ( module_hook ( $module , 'requirements' )) {
2006-09-01 08:44:53 +00:00
// Check requirements
$requirements = module_invoke ( $module , 'requirements' , 'install' );
if ( is_array ( $requirements ) && drupal_requirements_severity ( $requirements ) == REQUIREMENT_ERROR ) {
// Print any error messages
foreach ( $requirements as $requirement ) {
if ( isset ( $requirement [ 'severity' ]) && $requirement [ 'severity' ] == REQUIREMENT_ERROR ) {
2007-12-19 11:15:18 +00:00
$message = $requirement [ 'description' ];
if ( isset ( $requirement [ 'value' ]) && $requirement [ 'value' ]) {
2008-04-14 17:48:46 +00:00
$message .= ' (' . t ( 'Currently using !item !version' , array ( '!item' => $requirement [ 'title' ], '!version' => $requirement [ 'value' ])) . ')' ;
2007-12-19 11:15:18 +00:00
}
drupal_set_message ( $message , 'error' );
2006-09-01 08:44:53 +00:00
}
}
return FALSE ;
}
}
return TRUE ;
}
2009-07-15 02:08:41 +00:00
/**
* Retrieve info about an install profile from its . info file .
2009-07-19 04:48:10 +00:00
*
2009-12-07 06:19:20 +00:00
* The information stored in a profile . info file is similar to that stored in
* a normal Drupal module . info file . For example :
2009-07-19 04:48:10 +00:00
* - name : The real name of the install profile for display purposes .
* - description : A brief description of the profile .
* - dependencies : An array of shortnames of other modules this install profile requires .
*
2009-12-07 06:19:20 +00:00
* Additional , less commonly - used information that can appear in a profile . info
* file but not in a normal Drupal module . info file includes :
* - distribution_name : The name of the Drupal distribution that is being
* installed , to be shown throughout the installation process . Defaults to
* 'Drupal' .
*
2009-07-19 04:48:10 +00:00
* Example of . info file :
2010-01-04 16:20:20 +00:00
* @ code
2009-12-07 06:19:20 +00:00
* name = Minimal
* description = Start fresh , with only a few modules enabled .
2009-07-19 04:48:10 +00:00
* dependencies [] = block
* dependencies [] = dblog
2010-01-04 16:20:20 +00:00
* @ endcode
2009-07-19 04:48:10 +00:00
*
* @ param profile
* Name of profile .
* @ param locale
* Name of locale used ( if any ) .
* @ return
* The info array .
2009-07-15 02:08:41 +00:00
*/
function install_profile_info ( $profile , $locale = 'en' ) {
2009-07-19 04:48:10 +00:00
$cache = & drupal_static ( __FUNCTION__ , array ());
if ( ! isset ( $cache [ $profile ])) {
// Set defaults for module info.
$defaults = array (
'dependencies' => array (),
'description' => '' ,
2009-12-07 06:19:20 +00:00
'distribution_name' => 'Drupal' ,
2009-07-19 04:48:10 +00:00
'version' => NULL ,
'php' => DRUPAL_MINIMUM_PHP ,
);
$info = drupal_parse_info_file ( " profiles/ $profile / $profile .info " ) + $defaults ;
$info [ 'dependencies' ] = array_unique ( array_merge (
2009-07-30 10:16:48 +00:00
drupal_required_modules (),
$info [ 'dependencies' ],
2009-07-19 04:48:10 +00:00
( $locale != 'en' && ! empty ( $locale ) ? array ( 'locale' ) : array ()))
);
2009-08-21 07:50:08 +00:00
// 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' ]);
2009-07-19 04:48:10 +00:00
$cache [ $profile ] = $info ;
}
return $cache [ $profile ];
2009-07-15 02:08:41 +00:00
}
2009-07-30 10:16:48 +00:00
/**
* Ensures the environment for a Drupal database on a predefined connection .
*
* This will run tasks that check that Drupal can perform all of the functions
* on a database , that Drupal needs . Tasks include simple checks like CREATE
2010-01-25 10:38:35 +00:00
* TABLE to database specific functions like stored procedures and client
2009-07-30 10:16:48 +00:00
* encoding .
*/
function db_run_tasks ( $driver ) {
$task_class = 'DatabaseTasks_' . $driver ;
$DatabaseTasks = new $task_class ();
$DatabaseTasks -> runTasks ();
2010-07-22 23:54:32 +00:00
return TRUE ;
2009-07-30 10:16:48 +00:00
}