diff --git a/core/INSTALL.txt b/core/INSTALL.txt index b753f3a..9302945 100644 --- a/core/INSTALL.txt +++ b/core/INSTALL.txt @@ -150,12 +150,12 @@ INSTALLATION which is normally in the directory sites/default (to avoid problems when upgrading, Drupal is not packaged with this file). If auto-creation fails, you will need to create this file yourself, using the file - sites/default/default.settings.php as a template. + core/default.settings.php as a template. For example, on a Unix/Linux command line, you can make a copy of the default.settings.php file with the command: - cp sites/default/default.settings.php sites/default/settings.php + cp core/default.settings.php sites/default/settings.php Next, grant write privileges to the file to everyone (including the web server) with the command: diff --git a/core/UPGRADE.txt b/core/UPGRADE.txt index f035b6c..57021fb 100644 --- a/core/UPGRADE.txt +++ b/core/UPGRADE.txt @@ -163,10 +163,6 @@ following the instructions in the INTRODUCTION section at the top of this file: no longer need their data, then you can uninstall them under the Uninstall tab after disabling them. -8. On the command line or in your FTP client, remove the file - - sites/default/default.settings.php - 9. Remove all old core files and directories, except for the 'sites' directory and any custom files you added elsewhere. diff --git a/sites/default/default.settings.php b/core/default.settings.php similarity index 91% rename from sites/default/default.settings.php rename to core/default.settings.php index 5a46b12..ab603d0 100644 --- a/sites/default/default.settings.php +++ b/core/default.settings.php @@ -2,7 +2,7 @@ /** * @file - * Drupal site-specific configuration file. + * Drupal global configuration file. * * IMPORTANT NOTE: * This file may have been set to read-only by the Drupal installation program. @@ -11,10 +11,8 @@ * security risk. * * The configuration file to be loaded is based upon the rules below. However - * if the multisite aliasing file named sites/sites.php is present, it will be - * loaded, and the aliases in the array $sites will override the default - * directory rules below. See sites/example.sites.php for more information about - * aliases. + * if the multisite aliasing is enabled, the aliases in the $sites array will + * override the default directory rules below. * * The configuration directory will be discovered by stripping the website's * hostname from left to right and pathname from right to left. The first @@ -47,10 +45,61 @@ * hostname with that number. For example, * http://www.drupal.org:8080/mysite/test/ could be loaded from * sites/8080.www.drupal.org.mysite.test/. + */ + +/** + * Multi-site functionality and aliases. + * + * Uncomment the $sites variable below to enable Drupal's multi-site + * functionality, which allows to serve multiple different sites from the same + * code-base. + * + * An empty $sites variable just enables the sites directory discovery process. + * You can additionally define aliases that map hostnames, ports, and path names + * to specific site directories. These aliases are applied prior to scanning for + * directories and exempt from the discovery rules. + * + * Aliases are useful on development servers, where the domain name may not be + * the same as the domain of the live server. Since Drupal stores file paths in + * the database (files, system table, etc.) this will ensure the paths are + * correct when the site is deployed to a live server. + * + * Aliases are defined in an associative array named $sites. The array is + * written in the format: '..' => 'directory'. As an + * example, to map http://www.drupal.org:8080/mysite/test to the configuration + * directory sites/example.com, the array should be defined as: + * @code + * $sites = array( + * '8080.www.drupal.org.mysite.test' => 'example.com', + * ); + * @endcode + * The URL, http://www.drupal.org:8080/mysite/test/, could be a symbolic link or + * an Apache Alias directive that points to the Drupal root containing + * index.php. An alias could also be created for a subdomain. See the + * @link http://drupal.org/documentation/install online Drupal installation guide @endlink + * for more information on setting up domains, subdomains, and subdirectories. + * + * The following examples look for a site configuration in sites/example.com: + * @code + * URL: http://dev.drupal.org + * $sites['dev.drupal.org'] = 'example.com'; + * + * URL: http://localhost/example + * $sites['localhost.example'] = 'example.com'; + * + * URL: http://localhost:8080/example + * $sites['8080.localhost.example'] = 'example.com'; + * + * URL: http://www.drupal.org:8080/mysite/test/ + * $sites['8080.www.drupal.org.mysite.test'] = 'example.com'; + * @endcode * - * @see example.sites.php - * @see conf_path() + * @see default.settings.php + * @see \Drupal\Core\Utility\Site::getPath() + * @see http://drupal.org/documentation/install/multi-site */ +# $sites = array(); +# $sites['localhost.example'] = 'example.com'; /** * Database settings: diff --git a/core/includes/bootstrap.inc b/core/includes/bootstrap.inc index 51f4446..f7de151 100644 --- a/core/includes/bootstrap.inc +++ b/core/includes/bootstrap.inc @@ -12,6 +12,7 @@ use Drupal\Core\DependencyInjection\ContainerBuilder; use Drupal\Core\Utility\Title; use Drupal\Core\Utility\Error; +use Drupal\Core\Utility\Site; use Symfony\Component\ClassLoader\ApcClassLoader; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\DependencyInjection\Container; @@ -247,160 +248,6 @@ function timer_stop($name) { } /** - * Returns the appropriate configuration directory. - * - * Returns the configuration path based on the site's hostname, port, and - * pathname. Uses find_conf_path() to find the current configuration directory. - * See default.settings.php for examples on how the URL is converted to a - * directory. - * - * @param bool $require_settings - * Only configuration directories with an existing settings.php file - * will be recognized. Defaults to TRUE. During initial installation, - * this is set to FALSE so that Drupal can detect a matching directory, - * then create a new settings.php file in it. - * @param bool $reset - * Force a full search for matching directories even if one had been - * found previously. Defaults to FALSE. - * - * @return - * The path of the matching directory. - * - * @see default.settings.php - */ -function conf_path($require_settings = TRUE, $reset = FALSE) { - $conf_path = &drupal_static(__FUNCTION__, ''); - - if ($conf_path && !$reset) { - return $conf_path; - } - - // Check for a simpletest override. - if ($simpletest_conf_path = _drupal_simpletest_conf_path()) { - $conf_path = $simpletest_conf_path; - return $conf_path; - } - - // Otherwise, use the normal $conf_path. - $script_name = $_SERVER['SCRIPT_NAME']; - if (!$script_name) { - $script_name = $_SERVER['SCRIPT_FILENAME']; - } - $http_host = $_SERVER['HTTP_HOST']; - $conf_path = find_conf_path($http_host, $script_name, $require_settings); - return $conf_path; -} - -/** - * Determines whether to use an overridden value for conf_path(). - * - * Simpletest may provide a secondary, test-specific settings.php file to load - * after the primary one used by the parent site and override its variables. - * - If the child settings.php does not override $conf_path, then this function - * returns FALSE and conf_path() returns the directory of the primary - * settings.php. - * - If the child settings.php does override $conf_path, then - * _drupal_load_test_overrides() sets the 'simpletest_conf_path' setting, and - * this function returns that to conf_path(), causing installations and - * upgrades to act on that one. - * - * @return string|false - * The overridden $conf_path, or FALSE if the $conf_path should not currently - * be overridden. - * - * @see conf_path() - * @see _drupal_load_test_overrides() - */ -function _drupal_simpletest_conf_path() { - // Ensure that the settings object is available. conf_path() is called once - // before the Settings class is included, and at that point it should still - // load the primary $conf_path. See drupal_settings_initialize(). - if (!class_exists('Drupal\Component\Utility\Settings', FALSE)) { - return FALSE; - } - - // If no $simpletest_conf_path is set, use the normal $conf_path. - if (!($simpletest_conf_path = settings()->get('simpletest_conf_path'))) { - return FALSE; - } - - // Ensure that this is actually a simpletest request. We can't check this - // before settings.php is loaded. - if (!drupal_valid_test_ua()) { - return FALSE; - } - - // When the $simpletest_conf_path is set in a valid test request, - // return that path. - return $simpletest_conf_path; -} - -/** - * Finds the appropriate configuration directory for a given host and path. - * - * Finds a matching configuration directory file by stripping the website's - * hostname from left to right and pathname from right to left. By default, - * the directory must contain a 'settings.php' file for it to match. If the - * parameter $require_settings is set to FALSE, then a directory without a - * 'settings.php' file will match as well. The first configuration - * file found will be used and the remaining ones will be ignored. If no - * configuration file is found, returns a default value '$confdir/default'. See - * default.settings.php for examples on how the URL is converted to a directory. - * - * If a file named sites.php is present in the $confdir, it will be loaded - * prior to scanning for directories. That file can define aliases in an - * associative array named $sites. The array is written in the format - * '..' => 'directory'. As an example, to create a - * directory alias for http://www.drupal.org:8080/mysite/test whose configuration - * file is in sites/example.com, the array should be defined as: - * @code - * $sites = array( - * '8080.www.drupal.org.mysite.test' => 'example.com', - * ); - * @endcode - * - * @param $http_host - * The hostname and optional port number, e.g. "www.example.com" or - * "www.example.com:8080". - * @param $script_name - * The part of the URL following the hostname, including the leading slash. - * @param $require_settings - * Defaults to TRUE. If TRUE, then only match directories with a - * 'settings.php' file. Otherwise match any directory. - * - * @return - * The path of the matching configuration directory. - * - * @see default.settings.php - * @see example.sites.php - * @see conf_path() - */ -function find_conf_path($http_host, $script_name, $require_settings = TRUE) { - // Determine whether multi-site functionality is enabled. - if (!file_exists(DRUPAL_ROOT . '/sites/sites.php')) { - return 'sites/default'; - } - - $sites = array(); - include DRUPAL_ROOT . '/sites/sites.php'; - - $uri = explode('/', $script_name); - $server = explode('.', implode('.', array_reverse(explode(':', rtrim($http_host, '.'))))); - for ($i = count($uri) - 1; $i > 0; $i--) { - for ($j = count($server); $j > 0; $j--) { - $dir = implode('.', array_slice($server, -$j)) . implode('.', array_slice($uri, 0, $i)); - if (isset($sites[$dir]) && file_exists(DRUPAL_ROOT . '/sites/' . $sites[$dir])) { - $dir = $sites[$dir]; - } - if (file_exists(DRUPAL_ROOT . '/sites/' . $dir . '/settings.php') || (!$require_settings && file_exists(DRUPAL_ROOT . '/sites/' . $dir))) { - return "sites/$dir"; - } - } - } - return 'sites/default'; -} - -/** * Returns the path of a configuration directory. * * @param string $type @@ -451,11 +298,12 @@ function config_get_config_directory($type = CONFIG_ACTIVE_DIRECTORY) { * excluding any GET request but including the script name * (e.g., http://www.example.com/mysite/index.php). * - * @see conf_path() + * @see \Drupal\Core\Utility\Site::getPath() * @see request_uri() * @see \Symfony\Component\HttpFoundation\Request::getClientIP() */ function drupal_override_server_variables($variables = array()) { + // @todo \Drupal::request() is not available when this function is called. $request = \Drupal::request(); $server_vars = $request->server->all(); // Allow the provided URL to override any existing values in $_SERVER. @@ -484,7 +332,10 @@ function drupal_override_server_variables($variables = array()) { // Replace elements of the $_SERVER array, as appropriate. $request->server->replace($variables + $server_vars + $defaults); - // @todo remove once conf_path() no longer uses $_SERVER. + // @todo Inject a global Request object for all CLI executions + use the + // manipulated $request->server as basis for any subsequently instantiated + // Request objects. Alternatively, sub-class Request or fix it upstream to + // account for CLI environments. $_SERVER = $request->server->all(); } @@ -523,7 +374,7 @@ function drupal_environment_initialize() { error_reporting(E_STRICT | E_ALL | error_reporting()); // Override PHP settings required for Drupal to work properly. - // sites/default/default.settings.php contains more runtime settings. + // default.settings.php contains more runtime settings. // The .htaccess file contains settings that cannot be changed at runtime. // Deny execution with enabled "magic quotes" (both GPC and runtime). @@ -569,14 +420,25 @@ function drupal_settings_initialize() { global $databases, $cookie_domain, $conf, $db_prefix, $drupal_hash_salt, $base_secure_url, $base_insecure_url, $config_directories; $conf = array(); - // Make conf_path() available as local variable in settings.php. - $conf_path = conf_path(); - if (is_readable(DRUPAL_ROOT . '/' . $conf_path . '/settings.php')) { - include_once DRUPAL_ROOT . '/' . $conf_path . '/settings.php'; + // Read the global /settings.php file. + // Exclude it for test requests to prevent settings of the parent site/ + // test-runner to leak into the test environment. + if (!drupal_valid_test_ua() && is_readable(DRUPAL_ROOT . '/settings.php')) { + require DRUPAL_ROOT . '/settings.php'; + } + + // Discover the site directory. + Site::init(DRUPAL_ROOT, isset($sites) ? $sites : NULL, isset($conf_path) ? $conf_path : NULL); + + // Make $conf_path available as local variable in settings.php. + // Concatenation is safe here, since $conf_path is known to be not empty. + $conf_path = Site::getPath(); + if ($conf_path !== '' && is_readable(DRUPAL_ROOT . '/' . $conf_path . '/settings.php')) { + require DRUPAL_ROOT . '/' . $conf_path . '/settings.php'; } - require_once __DIR__ . '../../lib/Drupal/Component/Utility/Settings.php'; new Settings(isset($settings) ? $settings : array()); + $is_https = isset($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) == 'on'; if (isset($base_url)) { @@ -1956,7 +1818,15 @@ function _drupal_bootstrap_configuration() { drupal_settings_initialize(); // Make sure we are using the test database prefix in child Drupal sites. - _drupal_initialize_db_test_prefix(); + if ($test_prefix = drupal_valid_test_ua()) { + if (!empty($GLOBALS['databases']) && strpos($GLOBALS['databases']['default']['default']['prefix'], $test_prefix) === FALSE) { + throw new Exception('Bad prefix.'); + } + // @todo Move into initial (unset) getter logic of drupal_valid_test_ua()? + $test_info = &$GLOBALS['drupal_test_info']; + $test_info['test_run_id'] = $test_prefix; + $test_info['in_child_site'] = TRUE; + } // Activate the class loader. drupal_classloader(); @@ -2046,39 +1916,6 @@ function _drupal_bootstrap_page_cache() { } /** - * In a test environment, get the test db prefix and set it in $databases. - */ -function _drupal_initialize_db_test_prefix() { - // The user agent header is used to pass a database prefix in the request when - // running tests. However, for security reasons, it is imperative that we - // validate we ourselves made the request. - if ($test_prefix = drupal_valid_test_ua()) { - // Set the test run id for use in other parts of Drupal. - $test_info = &$GLOBALS['drupal_test_info']; - $test_info['test_run_id'] = $test_prefix; - $test_info['in_child_site'] = TRUE; - - foreach ($GLOBALS['databases']['default'] as &$value) { - // Extract the current default database prefix. - if (!isset($value['prefix'])) { - $current_prefix = ''; - } - elseif (is_array($value['prefix'])) { - $current_prefix = $value['prefix']['default']; - } - else { - $current_prefix = $value['prefix']; - } - - // Remove the current database prefix and replace it by our own. - $value['prefix'] = array( - 'default' => $current_prefix . $test_prefix, - ); - } - } -} - -/** * Loads system variables and all enabled bootstrap modules. */ function _drupal_bootstrap_variables() { @@ -2215,84 +2052,56 @@ function drupal_valid_test_ua($new_prefix = NULL) { if (isset($new_prefix)) { $test_prefix = $new_prefix; + // A new prefix is only explicitly set within the parent site/test runner. + // Set the global test run ID and info accordingly. + if ($new_prefix) { + $test_info = &$GLOBALS['drupal_test_info']; + $test_info['test_run_id'] = $test_prefix; + if (!isset($test_info['in_child_site'])) { + $test_info['in_child_site'] = FALSE; + } + } + else { + unset($GLOBALS['drupal_test_info']); + } } if (isset($test_prefix)) { return $test_prefix; } - + $test_prefix = FALSE; if (isset($_SERVER['HTTP_USER_AGENT']) && preg_match("/^(simpletest\d+);(.+);(.+);(.+)$/", $_SERVER['HTTP_USER_AGENT'], $matches)) { list(, $prefix, $time, $salt, $hmac) = $matches; $check_string = $prefix . ';' . $time . ';' . $salt; - // We use the salt from settings.php to make the HMAC key, since - // the database is not yet initialized and we can't access any Drupal variables. + // Read the hash salt prepared by drupal_generate_test_ua(). + require DRUPAL_ROOT . '/sites/simpletest/' . substr($prefix, 10) . '/key.php'; // The file properties add more entropy not easily accessible to others. - $key = drupal_get_hash_salt() . filectime(__FILE__) . fileinode(__FILE__); + $key = $private_key . filectime(__FILE__) . fileinode(__FILE__); $time_diff = REQUEST_TIME - $time; - // We can't use Crypt::hmacBase64() yet because this can be called in very - // early bootstrap when autoloader has not been initialized yet. - $test_hmac = base64_encode(hash_hmac('sha256', $check_string, $key, TRUE)); - $test_hmac = strtr($test_hmac, array('+' => '-', '/' => '_', '=' => '')); + $test_hmac = Crypt::hmacBase64($check_string, $key); // Since we are making a local request a 5 second time window is allowed, // and the HMAC must match. - if ($time_diff >= 0 && $time_diff <= 5 && $hmac == $test_hmac) { + if ($time_diff >= 0 && $time_diff <= 5 && $hmac === $test_hmac) { $test_prefix = $prefix; - _drupal_load_test_overrides($test_prefix); - return $test_prefix; } } - - $test_prefix = FALSE; return $test_prefix; } /** - * Overrides low-level and environment-specific configuration. - * - * Very strictly for internal use only. - * - * Loads settings.php from the simpletest public files directory. These files - * can change the global $conf, the global $config_directories, the return - * value of conf_path(), and settings(). - * - * @param string $test_prefix - * The simpletest prefix. - */ -function _drupal_load_test_overrides($test_prefix) { - global $conf, $config_directories; - - // Do not use the parent site's config directories. Use only the child site's. - // @see \Drupal\simpletest\TestBase::prepareConfigDirectories() - $path_prefix = 'simpletest/' . substr($test_prefix, 10); - $config_directories = array(); - foreach (array(CONFIG_ACTIVE_DIRECTORY, CONFIG_STAGING_DIRECTORY) as $type) { - $config_directories[$type] = conf_path() . '/files/' . $path_prefix . '/config_' . $type; - } - - // Check for and load a settings.php file in the simpletest files directory. - $filename = conf_path() . '/files/' . $path_prefix . '/settings.php'; - if (file_exists($filename)) { - $settings = settings()->getAll(); - $conf_path = &drupal_static('conf_path'); - // This can override $conf, $conf_path, $settings, and $config_directories. - include $filename; - // Keep the overriden $conf_path alive across drupal_static_reset() calls. - // @see conf_path() - $settings['simpletest_conf_path'] = $conf_path; - new Settings($settings); - } -} - -/** * Generates a user agent string with a HMAC and timestamp for simpletest. */ function drupal_generate_test_ua($prefix) { static $key; if (!isset($key)) { - // We use the salt from settings.php to make the HMAC key, since - // the database is not yet initialized and we can't access any Drupal variables. + // Generate and save a hash salt for a test run. + // Consumed by drupal_valid_test_ua() before settings.php is loaded. + $private_key = Crypt::randomStringHashed(55); + $key_file = "get('simpletest_conf_path')) { + // agent, install a new copy of Drupal, and take over the original site. + // The user agent header is used to pass a database prefix in the request when + // running tests. However, for security reasons, it is imperative that no + // installation be permitted using such a prefix. + if ($install_state['interactive'] && !drupal_valid_test_ua()) { header($request->server->get('SERVER_PROTOCOL') . ' 403 Forbidden'); exit; } + // Perform a minimal bootstrap. + // During installation, drupal_settings_initialize() will not attempt to load + // settings.php, so as to prime the site path to be used during installation. + // By not requiring settings.php, a bare site folder can be prepared in the + // /sites directory, which will be used for installing Drupal. + drupal_bootstrap(DRUPAL_BOOTSTRAP_CONFIGURATION); + // If we have a language selected and it is not yet saved in the system // (eg. pre-database data screens we are unable to persistently store // the default language), we should set language_default so the proper @@ -1067,9 +1073,7 @@ function install_verify_database_settings() { global $databases; if (!empty($databases)) { $database = $databases['default']['default']; - drupal_static_reset('conf_path'); - $settings_file = './' . conf_path(FALSE) . '/settings.php'; - $errors = install_database_errors($database, $settings_file); + $errors = install_database_errors($database); if (empty($errors)) { return TRUE; } @@ -1090,9 +1094,6 @@ function install_verify_database_settings() { function install_settings_form($form, &$form_state, &$install_state) { global $databases; - drupal_static_reset('conf_path'); - $conf_path = './' . conf_path(FALSE); - $settings_file = $conf_path . '/settings.php'; $database = isset($databases['default']['default']) ? $databases['default']['default'] : array(); drupal_set_title(t('Database configuration')); @@ -1139,7 +1140,6 @@ function install_settings_form($form, &$form_state, &$install_state) { ); $form['errors'] = array(); - $form['settings_file'] = array('#type' => 'value', '#value' => $settings_file); return $form; } @@ -1154,9 +1154,9 @@ function install_settings_form_validate($form, &$form_state) { $database = $form_state['values'][$driver]; // When testing the interactive installer, copy the database password and // the test prefix. + // @todo Remove this; pass prefix in POST data instead. if ($test_prefix = drupal_valid_test_ua()) { $database['prefix'] = $test_prefix; - $database['password'] = $GLOBALS['databases']['default']['default']['password']; } $drivers = drupal_get_database_types(); $reflection = new \ReflectionClass($drivers[$driver]); @@ -1167,13 +1167,13 @@ function install_settings_form_validate($form, &$form_state) { // @todo PIFR uses 'db_prefix' instead of 'prefix'. Remove this when it gets // fixed. - if (!$test_prefix) { + if (!$test_prefix && isset($database['db_prefix'])) { $database['prefix'] = $database['db_prefix']; } unset($database['db_prefix']); $form_state['storage']['database'] = $database; - $errors = install_database_errors($database, $form_state['values']['settings_file']); + $errors = install_database_errors($database); foreach ($errors as $name => $message) { form_set_error($name, $form_state, $message); } @@ -1182,7 +1182,7 @@ function install_settings_form_validate($form, &$form_state) { /** * Checks a database connection and returns any errors. */ -function install_database_errors($database, $settings_file) { +function install_database_errors($database) { global $databases; $errors = array(); @@ -1190,7 +1190,11 @@ function install_database_errors($database, $settings_file) { $database_types = drupal_get_database_types(); $driver = $database['driver']; if (!isset($database_types[$driver])) { - $errors['driver'] = t("In your %settings_file file you have configured @drupal to use a %driver server, however your PHP installation currently does not support this database type.", array('%settings_file' => $settings_file, '@drupal' => drupal_install_profile_distribution_name(), '%driver' => $driver)); + $errors['driver'] = t("In your %settings_file file you have configured @drupal to use a %driver server, however your PHP installation currently does not support this database type.", array( + '%settings_file' => Site::getPath('settings.php'), + '@drupal' => drupal_install_profile_distribution_name(), + '%driver' => $driver, + )); } else { // Run driver specific validation @@ -1231,33 +1235,15 @@ function install_settings_form_submit($form, &$form_state) { // Update global settings array and save. $settings = array(); $database = $form_state['storage']['database']; - // Ideally, there is no difference between the code executed by the - // automated test browser and an ordinary browser. However, the database - // settings need a different format and also need to skip the password - // when testing. The hash salt also needs to be skipped because the original - // salt is used to verify the validity of the automated test browser. - // Because of these, there's a little difference in the code following but - // it is small and self-contained. - if ($test_prefix = drupal_valid_test_ua()) { - foreach ($form_state['storage']['database'] as $k => $v) { - if ($k != 'password') { - $settings['databases']['default']['default'][$k] = (object) array( - 'value' => $v, - 'required' => TRUE, - ); - } - } - } - else { - $settings['databases']['default']['default'] = (object) array( - 'value' => $database, - 'required' => TRUE, - ); - $settings['drupal_hash_salt'] = (object) array( - 'value' => Crypt::randomStringHashed(55), - 'required' => TRUE, - ); - } + + $settings['databases']['default']['default'] = (object) array( + 'value' => $database, + 'required' => TRUE, + ); + $settings['drupal_hash_salt'] = (object) array( + 'value' => Crypt::randomStringHashed(55), + 'required' => TRUE, + ); // Remember the profile which was used. $settings['settings'] = array( @@ -1488,7 +1474,7 @@ function install_translations_directory() { $directory = $GLOBALS['conf']['locale.settings']['translation.path']; } else { - $directory = conf_path() . '/files/translations'; + $directory = Site::getPath('files/translations'); } return $directory; } @@ -2070,8 +2056,8 @@ function install_configure_form($form, &$form_state, &$install_state) { drupal_set_title(t('Configure site')); // Warn about settings.php permissions risk - $settings_dir = conf_path(); - $settings_file = $settings_dir . '/settings.php'; + $settings_dir = Site::getPath(); + $settings_file = Site::getPath('settings.php'); // Check that $_POST is empty so we only show this message when the form is // first displayed, not on the next page after it is submitted. (We do not // want to repeat it multiple times because it is a general warning that is @@ -2080,7 +2066,7 @@ function install_configure_form($form, &$form_state, &$install_state) { // distract from the message that the Drupal installation has completed // successfully.) $post_params = \Drupal::request()->request->all(); - if (empty($post_params) && (!drupal_verify_install_file(DRUPAL_ROOT . '/' . $settings_file, FILE_EXIST|FILE_READABLE|FILE_NOT_WRITABLE) || !drupal_verify_install_file(DRUPAL_ROOT . '/' . $settings_dir, FILE_NOT_WRITABLE, 'dir'))) { + if (empty($post_params) && (!drupal_verify_install_file(Site::getAbsolutePath('settings.php'), FILE_EXIST|FILE_READABLE|FILE_NOT_WRITABLE) || !drupal_verify_install_file(Site::getAbsolutePath(), FILE_NOT_WRITABLE, 'dir'))) { drupal_set_message(t('All necessary changes to %dir and %file have been made, so you should remove write permissions to them now in order to avoid security risks. If you are unsure how to do so, consult the online handbook.', array('%dir' => $settings_dir, '%file' => $settings_file, '@handbook_url' => 'http://drupal.org/server-permissions')), 'warning'); } @@ -2226,8 +2212,8 @@ function install_check_translations($install_state) { $readable = FALSE; $writable = FALSE; // @todo: Make this configurable. - $files_directory = conf_path() . '/files'; - $translations_directory = conf_path() . '/files/translations'; + $files_directory = Site::getPath('files'); + $translations_directory = Site::getPath('files/translations'); $translations_directory_exists = FALSE; $translation_available = FALSE; $online = FALSE; @@ -2380,15 +2366,14 @@ function install_check_requirements($install_state) { if (!$install_state['settings_verified']) { $readable = FALSE; $writable = FALSE; - $conf_path = './' . conf_path(FALSE, TRUE); - $settings_file = $conf_path . '/settings.php'; - $default_settings_file = './sites/default/default.settings.php'; - $file = $conf_path; + $conf_path = Site::getPath(); + $settings_file = Site::getAbsolutePath('settings.php'); + $default_settings_file = './core/default.settings.php'; + $file = Site::getPath('settings.php'); $exists = FALSE; // Verify that the directory exists. - if (drupal_verify_install_file($conf_path, FILE_EXIST, 'dir')) { + if (drupal_verify_install_file(Site::getAbsolutePath(), FILE_EXIST, 'dir')) { // Check if a settings.php file already exists. - $file = $settings_file; if (drupal_verify_install_file($settings_file, FILE_EXIST)) { // If it does, make sure it is writable. $readable = drupal_verify_install_file($settings_file, FILE_READABLE); @@ -2397,20 +2382,11 @@ function install_check_requirements($install_state) { } } - // If default.settings.php does not exist, or is not readable, throw an - // error. - if (!drupal_verify_install_file($default_settings_file, FILE_EXIST|FILE_READABLE)) { - $requirements['default settings file exists'] = array( - 'title' => t('Default settings file'), - 'value' => t('The default settings file does not exist.'), - 'severity' => REQUIREMENT_ERROR, - 'description' => t('The @drupal installer requires that the %default-file file not be modified in any way from the original download.', array('@drupal' => drupal_install_profile_distribution_name(), '%default-file' => $default_settings_file)), - ); - } // Otherwise, if settings.php does not exist yet, we can try to copy // default.settings.php to create it. - elseif (!$exists) { - $copied = drupal_verify_install_file($conf_path, FILE_EXIST|FILE_WRITABLE, 'dir') && @copy($default_settings_file, $settings_file); + if (!$exists) { + $copied = drupal_verify_install_file(Site::getAbsolutePath(), FILE_EXIST|FILE_WRITABLE, 'dir'); + $copied = $copied && @copy($default_settings_file, $settings_file); if ($copied) { // If the new settings file has the same owner as default.settings.php, // this means default.settings.php is owned by the webserver user. diff --git a/core/includes/install.inc b/core/includes/install.inc index 67c18b7..10fb016 100644 --- a/core/includes/install.inc +++ b/core/includes/install.inc @@ -9,6 +9,7 @@ use Drupal\Component\Utility\Crypt; use Drupal\Core\Database\Database; use Drupal\Core\DrupalKernel; +use Drupal\Core\Utility\Site; /** * Requirement severity -- Informational message only. @@ -195,7 +196,7 @@ function drupal_get_database_types() { */ function drupal_rewrite_settings($settings = array(), $settings_file = NULL) { if (!isset($settings_file)) { - $settings_file = conf_path(FALSE) . '/settings.php'; + $settings_file = Site::getPath('settings.php'); } // Build list of setting names and insert the values into the global namespace. $variable_names = array(); @@ -449,11 +450,11 @@ function drupal_install_config_directories($mode = NULL) { $config_directories_hash = Crypt::randomStringHashed(55); $settings['config_directories'] = array( CONFIG_ACTIVE_DIRECTORY => (object) array( - 'value' => conf_path() . '/files/config_' . $config_directories_hash . '/active', + 'value' => Site::getPath('files/config_' . $config_directories_hash . '/active'), 'required' => TRUE, ), CONFIG_STAGING_DIRECTORY => (object) array( - 'value' => conf_path() . '/files/config_' . $config_directories_hash . '/staging', + 'value' => Site::getPath('files/config_' . $config_directories_hash . '/staging'), 'required' => TRUE, ), ); diff --git a/core/includes/update.inc b/core/includes/update.inc index 24f6583..e56cca1 100644 --- a/core/includes/update.inc +++ b/core/includes/update.inc @@ -15,6 +15,7 @@ use Drupal\Core\Config\ConfigException; use Drupal\Core\DrupalKernel; use Drupal\Core\Utility\Error; +use Drupal\Core\Utility\Site; use Drupal\Component\Uuid\Uuid; use Drupal\Component\Utility\NestedArray; use Symfony\Component\HttpFoundation\Request; @@ -124,7 +125,7 @@ function update_prepare_d8_bootstrap() { // If any of the required settings needs to be written, then settings.php // needs to be writable. if (!$settings_exist) { - $settings_file = conf_path() . '/settings.php'; + $settings_file = Site::getPath('settings.php'); $writable = drupal_verify_install_file($settings_file, FILE_EXIST | FILE_READABLE | FILE_WRITABLE); $requirements['settings file']['title'] = 'Settings file'; if ($writable) { diff --git a/core/lib/Drupal/Core/Database/Driver/sqlite/Install/Tasks.php b/core/lib/Drupal/Core/Database/Driver/sqlite/Install/Tasks.php index 633edc6..3f2147e 100644 --- a/core/lib/Drupal/Core/Database/Driver/sqlite/Install/Tasks.php +++ b/core/lib/Drupal/Core/Database/Driver/sqlite/Install/Tasks.php @@ -11,6 +11,7 @@ use Drupal\Core\Database\Driver\sqlite\Connection; use Drupal\Core\Database\DatabaseNotFoundException; use Drupal\Core\Database\Install\Tasks as InstallTasks; +use Drupal\Core\Utility\Site; /** * Specifies installation tasks for SQLite databases. @@ -49,7 +50,7 @@ public function getFormOptions(array $database) { // Make the text more accurate for SQLite. $form['database']['#title'] = t('Database file'); $form['database']['#description'] = t('The absolute path to the file where @drupal data will be stored. This must be writable by the web server and should exist outside of the web root.', array('@drupal' => drupal_install_profile_distribution_name())); - $default_database = conf_path(FALSE, TRUE) . '/files/.ht.sqlite'; + $default_database = Site::getPath('files/.ht.sqlite'); $form['database']['#default_value'] = empty($database['database']) ? $default_database : $database['database']; return $form; } diff --git a/core/lib/Drupal/Core/DrupalKernel.php b/core/lib/Drupal/Core/DrupalKernel.php index 0160119..d1badfb 100644 --- a/core/lib/Drupal/Core/DrupalKernel.php +++ b/core/lib/Drupal/Core/DrupalKernel.php @@ -342,6 +342,7 @@ public function updateModules(array $module_list, array $module_filenames = arra protected function getClassName() { $parts = array('service_container', $this->environment); // Make sure to use a testing-specific container even in the parent site. + // @todo Obsolete by drupal_valid_test_ua()? if (!empty($GLOBALS['drupal_test_info']['test_run_id'])) { $parts[] = $GLOBALS['drupal_test_info']['test_run_id']; } diff --git a/core/lib/Drupal/Core/StreamWrapper/PublicStream.php b/core/lib/Drupal/Core/StreamWrapper/PublicStream.php index 90f17b3..cbb596c 100644 --- a/core/lib/Drupal/Core/StreamWrapper/PublicStream.php +++ b/core/lib/Drupal/Core/StreamWrapper/PublicStream.php @@ -6,6 +6,7 @@ */ namespace Drupal\Core\StreamWrapper; +use Drupal\Core\Utility\Site; /** * Defines a Drupal public (public://) stream wrapper class. @@ -37,14 +38,7 @@ public function getExternalUrl() { * The base path for public:// typically sites/default/files. */ public static function basePath() { - $base_path = settings()->get('file_public_path', conf_path() . '/files'); - if ($test_prefix = drupal_valid_test_ua()) { - // Append the testing suffix unless already given. - // @see \Drupal\simpletest\WebTestBase::setUp() - if (strpos($base_path, '/simpletest/' . substr($test_prefix, 10)) === FALSE) { - return $base_path . '/simpletest/' . substr($test_prefix, 10); - } - } + $base_path = settings()->get('file_public_path', Site::getPath('files')); return $base_path; } diff --git a/core/lib/Drupal/Core/SystemListing.php b/core/lib/Drupal/Core/SystemListing.php index 20d95cb..6d895a2 100644 --- a/core/lib/Drupal/Core/SystemListing.php +++ b/core/lib/Drupal/Core/SystemListing.php @@ -6,6 +6,7 @@ */ namespace Drupal\Core; +use Drupal\Core\Utility\Site; /** * Returns information about system object files (modules, themes, etc.). @@ -85,7 +86,6 @@ function scan($mask, $directory, $key = 'name') { if (!in_array($key, array('uri', 'filename', 'name'))) { $key = 'uri'; } - $config = conf_path(); // Search for the directory in core. $searchdir = array('core/' . $directory); @@ -100,9 +100,12 @@ function scan($mask, $directory, $key = 'name') { $searchdir[] = $directory; $searchdir[] = 'sites/all/' . $directory; - if (file_exists("$config/$directory")) { - $searchdir[] = "$config/$directory"; + if (Site::getPath() !== '') { + if (file_exists(Site::getPath($directory))) { + $searchdir[] = Site::getPath($directory); + } } + // @todo Find a way to skip ./config directories (but not modules/config). $nomask = '/^(CVS|lib|templates|css|js)$/'; $files = array(); diff --git a/core/lib/Drupal/Core/SystemListingInfo.php b/core/lib/Drupal/Core/SystemListingInfo.php index d302699..3575adc 100644 --- a/core/lib/Drupal/Core/SystemListingInfo.php +++ b/core/lib/Drupal/Core/SystemListingInfo.php @@ -28,6 +28,7 @@ protected function profiles($directory) { // For SimpleTest to be able to test modules packaged together with a // distribution we need to include the profile of the parent site (in // which test runs are triggered). + // @todo !drupal_installation_attempted() defeats the whole purpose? if (drupal_valid_test_ua() && !drupal_installation_attempted()) { $testing_profile = \Drupal::config('simpletest.settings')->get('parent_profile'); if ($testing_profile && $testing_profile != $profile) { diff --git a/core/lib/Drupal/Core/Utility/Site.php b/core/lib/Drupal/Core/Utility/Site.php new file mode 100644 index 0000000..6711562 --- /dev/null +++ b/core/lib/Drupal/Core/Utility/Site.php @@ -0,0 +1,343 @@ +isInstaller()) { + throw new \BadMethodCallException('Site path is initialized already.'); + } + // Force-override the site directory in tests. + if ($test_prefix = drupal_valid_test_ua()) { + $custom_path = 'sites/simpletest/' . substr($test_prefix, 10); + } + self::$instance->initializePath($sites, $custom_path); + } + + /** + * Initializes the Site singleton for the early installer environment. + * + * The installer uses this function to prime the site directory path very + * early in the installer environmnt. This allows the application to be + * installed into a new and empty site directory, which does not contain a + * settings.php yet. + * + * @param string $root_directory + * The root directory to use for absolute paths; i.e., DRUPAL_ROOT. + * + * @see install_begin_request() + */ + public static function initInstaller($root_directory) { + // WebTestBase::setUp() invokes Site::setUpTest() prior to invoking the + // non-interactive installer, in order to override the singleton and prime + // a custom site directory. + if (drupal_valid_test_ua()) { + return; + } + if (isset(self::$instance)) { + throw new \BadMethodCallException('Site path is initialized already.'); + } + // Set a global state flag to denote that we are operating in the special + // installer environment. initializePath() is unnecessary, as init() will be + // called by drupal_settings_initialize(), possibly passing new values from + // the newly created settings.php file. + new self($root_directory, TRUE); + } + + /** + * Re-initializes the Site singleton for a test run. + * + * Called by WebTestBase::setUp() prior to invoking the non-interactive + * installer for a test. + * + * @param string $root_directory + * The root directory to use for absolute paths; i.e., DRUPAL_ROOT. + * @param string $custom_path + * The relative site directory path to use during the test run; e.g., + * 'sites/simpletest/123456'. + */ + public static function setUpTest($root_directory, $custom_path) { + if (!drupal_valid_test_ua()) { + throw new \BadMethodCallException('Site is not executing a test.'); + } + if (!file_exists($root_directory . '/' . $custom_path)) { + throw new \InvalidArgumentException("Test site directory '$custom_path' does not exist."); + } + new self($root_directory, TRUE, $custom_path); + } + + /** + * Reverts the Site singleton to the original after a test run. + */ + public static function tearDownTest() { + if (!isset(self::$instance->original)) { + throw new \RuntimeException('No original Site to revert to. Missing invocation of Site::setUpTest()?'); + } + self::$instance = self::$instance->original; + } + + /** + * Constructs the Site singleton. + */ + private function __construct($root_directory, $is_installer = FALSE, $custom_path = NULL) { + if (isset(self::$instance->original)) { + throw new \RuntimeException('Site is overridden for a test already. Duplicate invocation of Site::setUpTest()?'); + } + if (isset(self::$instance)) { + $this->original = clone self::$instance; + } + $this->root = $root_directory; + $this->isInstaller = $is_installer; + $this->path = $custom_path; + self::$instance = $this; + } + + /** + * Returns whether the Site singleton was instantiated for the installer. + * + * @todo Leverage this to eliminate drupal_installation_attempted()? + */ + private function isInstaller() { + return $this->isInstaller; + } + + /** + * Initializes the site path. + * + * @param array $sites + * (optional) A multi-site mapping, as defined in settings.php. + * @param string $custom_path + * (optional) An explicit site path to set; skipping site negotiation. + */ + private function initializePath(array $sites = NULL, $custom_path = NULL) { + // When executing the non-interactive installer in the parent site/ + // test-runner, WebTestBase::setUp() primes the site directory path via + // Site::setUpTest() already. + // @todo Remove this case by adding a 'site' parameter to drupal_install(). + if (isset($this->path)) { + return; + } + + // An explicitly defined $conf_path in /settings.php takes precedence. + if (isset($custom_path)) { + $this->path = $custom_path; + } + // If the multi-site functionality was enabled in /settings.php, discover + // the path for the current site. + // $sites just needs to be defined; an explicit mapping is not required. + elseif (isset($sites)) { + $this->path = $this->determinePath($sites, !$this->isInstaller()); + } + // If the multi-site functionality is not enabled, the Drupal root + // directory is the site directory. + else { + $this->path = ''; + } + } + + /** + * Finds the appropriate configuration directory for a given host and path. + * + * Finds a matching configuration directory file by stripping the website's + * hostname from left to right and pathname from right to left. By default, + * the directory must contain a 'settings.php' file for it to match. If the + * parameter $require_settings is set to FALSE, then a directory without a + * 'settings.php' file will match as well. The first configuration + * file found will be used and the remaining ones will be ignored. + * + * The settings.php file can define aliases in an associative array named + * $sites. For example, to create a directory alias for + * http://www.drupal.org:8080/mysite/test whose configuration file is in + * sites/example.com, the array should be defined as: + * @code + * $sites = array( + * '8080.www.drupal.org.mysite.test' => 'example.com', + * ); + * @endcode + * + * @see default.settings.php + * + * @param array $sites + * A multi-site mapping, as defined in settings.php. + * @param bool $require_settings + * Only configuration directories with an existing settings.php file + * will be recognized. Defaults to TRUE. During initial installation, + * this is set to FALSE so that Drupal can detect a matching directory, + * then create a new settings.php file in it. + * + * @return string + * The path of the matching configuration directory. May be an empty string, + * in case the site configuration directory is the root directory. + * + * @todo Inject a Request object in instead of relying on globals? + */ + private function determinePath(array $sites, $require_settings) { + // The hostname and optional port number, e.g. "www.example.com" or + // "www.example.com:8080". + $http_host = $_SERVER['HTTP_HOST']; + // The part of the URL following the hostname, including the leading slash. + $script_name = $_SERVER['SCRIPT_NAME'] ?: $_SERVER['SCRIPT_FILENAME']; + + $uri = explode('/', $script_name); + $server = explode('.', implode('.', array_reverse(explode(':', rtrim($http_host, '.'))))); + for ($i = count($uri) - 1; $i > 0; $i--) { + for ($j = count($server); $j > 0; $j--) { + $dir = implode('.', array_slice($server, -$j)) . implode('.', array_slice($uri, 0, $i)); + // Check for an alias in $sites. + if (isset($sites[$dir])) { + $dir = $sites[$dir]; + // A defined site alias from /settings.php should be valid. + // @todo Even skip the settings.php check? + if (!$require_settings) { + return "sites/$dir"; + } + } + if ($require_settings) { + if (file_exists($this->root . '/sites/' . $dir . '/settings.php')) { + return "sites/$dir"; + } + } + elseif (file_exists($this->root . '/sites/' . $dir)) { + return "sites/$dir"; + } + } + } + return ''; + } + + /** + * Prefixes a given filepath with the site directory, if any. + * + * @param string $filepath + * The filepath to prefix. + * + * @return string + * The prefixed filepath. + */ + private function resolvePath($filepath) { + if ($filepath !== '' && $filepath[0] === '/') { + $filepath = substr($filepath, 1); + } + if ($this->path !== '') { + if ($filepath !== '') { + $filepath = $this->path . '/' . $filepath; + } + else { + $filepath = $this->path; + } + } + return $filepath; + } + + /** + * Returns a given path as relative path to the site directory. + * + * Use this function instead of appending strings to the site path manually, + * because the site directory may be the root directory and thus the resulting + * path would be an absolute filesystem path. + * + * @param string $filepath + * (optional) A relative filepath to append to the site path. + * + * @return string + * The given $filepath, potentially prefixed with the site path. + * + * @see \Drupal\Core\Utility\Site::getAbsolutePath() + */ + public static function getPath($filepath = '') { + return self::$instance->resolvePath($filepath); + } + + /** + * Returns a given path as absolute path in the site directory. + * + * @param string $filepath + * (optional) A relative filepath to append to the site path. + * + * @return string + * The given $filepath, potentially prefixed with the site path, as an + * absolute filesystem path. + * + * @see \Drupal\Core\Utility\Site::getPath() + */ + public static function getAbsolutePath($filepath = '') { + $filepath = self::$instance->resolvePath($filepath); + if ($filepath !== '') { + return self::$instance->root . '/' . $filepath; + } + else { + return self::$instance->root; + } + } + +} diff --git a/core/modules/locale/locale.install b/core/modules/locale/locale.install index 960381c..c37fdd7 100644 --- a/core/modules/locale/locale.install +++ b/core/modules/locale/locale.install @@ -6,6 +6,7 @@ */ use Drupal\Core\Language\Language; +use Drupal\Core\Utility\Site; /** * Implements hook_install(). @@ -13,7 +14,7 @@ function locale_install() { // Create the interface translations directory and ensure it's writable. if (!$directory = \Drupal::config('locale.settings')->get('translation.path')) { - $directory = conf_path() . '/files/translations'; + $directory = Site::getPath('files/translations'); \Drupal::config('locale.settings')->set('translation.path', $directory)->save(); } file_prepare_directory($directory, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS); diff --git a/core/modules/simpletest/lib/Drupal/simpletest/TestBase.php b/core/modules/simpletest/lib/Drupal/simpletest/TestBase.php index 560afb1..8fb43bc 100644 --- a/core/modules/simpletest/lib/Drupal/simpletest/TestBase.php +++ b/core/modules/simpletest/lib/Drupal/simpletest/TestBase.php @@ -19,6 +19,7 @@ use Drupal\Core\Language\Language; use Drupal\Core\StreamWrapper\PublicStream; use Drupal\Core\Utility\Error; +use Drupal\Core\Utility\Site; use Symfony\Component\HttpFoundation\Request; /** @@ -36,6 +37,13 @@ protected $testId; /** + * The site directory of this test run. + * + * @var string + */ + protected $siteDirectory = NULL; + + /** * The database prefix of this test run. * * @var string @@ -837,7 +845,9 @@ public function run(array $methods = array()) { * @see WebTestBase::setUp() */ protected function prepareDatabasePrefix() { - $this->databasePrefix = 'simpletest' . mt_rand(1000, 1000000); + $suffix = mt_rand(1000, 1000000); + $this->databasePrefix = 'simpletest' . $suffix; + $this->siteDirectory = 'sites/simpletest/' . $suffix; // As soon as the database prefix is set, the test might start to execute. // All assertions as well as the SimpleTest batch operations are associated @@ -925,7 +935,7 @@ protected function prepareEnvironment() { // Save further contextual information. // Use the original files directory to avoid nesting it within an existing // simpletest directory if a test is executed within a test. - $this->originalFileDirectory = settings()->get('file_public_path', conf_path() . '/files'); + $this->originalFileDirectory = settings()->get('file_public_path', Site::getPath('files')); $this->originalProfile = drupal_get_profile(); $this->originalUser = isset($user) ? clone $user : NULL; @@ -946,10 +956,10 @@ protected function prepareEnvironment() { // Create test directory ahead of installation so fatal errors and debug // information can be logged during installation process. - // Use temporary files directory with the same prefix as the database. - $this->public_files_directory = $this->originalFileDirectory . '/simpletest/' . substr($this->databasePrefix, 10); - $this->private_files_directory = $this->public_files_directory . '/private'; - $this->temp_files_directory = $this->private_files_directory . '/temp'; + // @todo Do not execute this for WebTestBase. + $this->public_files_directory = $this->siteDirectory . '/files'; + $this->private_files_directory = $this->siteDirectory . '/private'; + $this->temp_files_directory = $this->siteDirectory . '/temp'; $this->translation_files_directory = $this->public_files_directory . '/translations'; // Create the directories @@ -960,7 +970,14 @@ protected function prepareEnvironment() { $this->generatedTestFiles = FALSE; // Create and set new configuration directories. - $this->prepareConfigDirectories(); + // @todo Only valid for tests not executing the installer (in any way). + if (!$this instanceof WebTestBase) { + $this->prepareConfigDirectories(); + } + else { + $GLOBALS['config_directories'] = array(); + $this->configDirectories = array(); + } // Reset statics before the old container is replaced so that objects with a // __destruct() method still have access to it. @@ -1013,7 +1030,7 @@ protected function prepareConfigDirectories() { include_once DRUPAL_ROOT . '/core/includes/install.inc'; foreach (array(CONFIG_ACTIVE_DIRECTORY, CONFIG_STAGING_DIRECTORY) as $type) { // Assign the relative path to the global variable. - $path = conf_path() . '/files/simpletest/' . substr($this->databasePrefix, 10) . '/config_' . $type; + $path = $this->siteDirectory . '/config_' . $type; $GLOBALS['config_directories'][$type] = $path; // Ensure the directory can be created and is writeable. if (!install_ensure_config_directory($type)) { @@ -1106,8 +1123,8 @@ protected function tearDown() { // log to pick up any fatal errors. simpletest_log_read($this->testId, $this->databasePrefix, get_class($this), TRUE); - // Delete temporary files directory. - file_unmanaged_delete_recursive($this->originalFileDirectory . '/simpletest/' . substr($this->databasePrefix, 10), array($this, 'filePreDeleteCallback')); + // Delete test site directory. + file_unmanaged_delete_recursive($this->siteDirectory, array($this, 'filePreDeleteCallback')); // Restore original database connection. Database::removeConnection('default'); @@ -1138,6 +1155,9 @@ protected function tearDown() { if (isset($this->originalPrefix)) { drupal_valid_test_ua($this->originalPrefix); } + else { + drupal_valid_test_ua(FALSE); + } // Restore original shutdown callbacks. $callbacks = &drupal_register_shutdown_function(); diff --git a/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php b/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php index 265012e..a17e64b 100644 --- a/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php +++ b/core/modules/simpletest/lib/Drupal/simpletest/WebTestBase.php @@ -17,6 +17,7 @@ use Drupal\Core\Session\AccountInterface; use Drupal\Core\Session\UserSession; use Drupal\Core\StreamWrapper\PublicStream; +use Drupal\Core\Utility\Site; use Drupal\Core\Datetime\DrupalDateTime; use Symfony\Component\HttpFoundation\Request; @@ -748,6 +749,10 @@ protected function setUp() { return FALSE; } + // After preparing the environment and changing the database prefix, we are + // in a valid test environment. + drupal_valid_test_ua($this->databasePrefix); + // Set the 'simpletest_parent_profile' variable to add the parent profile's // search path to the child site's search paths. // @see drupal_system_listing() @@ -781,26 +786,16 @@ protected function setUp() { $this->settingsSet('file_public_path', $this->public_files_directory); // Execute the non-interactive installer. require_once DRUPAL_ROOT . '/core/includes/install.core.inc'; - $this->settingsSet('cache', array('default' => 'cache.backend.memory')); + + Site::setUpTest(DRUPAL_ROOT, $this->siteDirectory); + $parameters = $this->installParameters(); install_drupal($parameters); - // Set the install_profile so that web requests to the requests to the child - // site have the correct profile. - $settings = array( - 'settings' => array( - 'install_profile' => (object) array( - 'value' => $this->profile, - 'required' => TRUE, - ), - ), - ); - $this->writeSettings($settings); // Override install profile in Settings to so the correct profile is used by // tests. $this->settingsSet('install_profile', $this->profile); - $this->settingsSet('cache', array()); $this->rebuildContainer(); // Restore the original Simpletest batch. @@ -863,6 +858,7 @@ protected function setUp() { * @see install_state_defaults() */ protected function installParameters() { + // The prefix is prepared in TestBase::changeDatabasePrefix() already. $connection_info = Database::getConnectionInfo(); $parameters = array( 'interactive' => FALSE, @@ -1026,6 +1022,7 @@ protected function tearDown() { $this->kernel->shutdown(); } parent::tearDown(); + Site::tearDownTest(); // Ensure that internal logged in variable and cURL options are reset. $this->loggedInUser = FALSE; diff --git a/core/modules/simpletest/simpletest.module b/core/modules/simpletest/simpletest.module index bd50676..ba3e9d5 100644 --- a/core/modules/simpletest/simpletest.module +++ b/core/modules/simpletest/simpletest.module @@ -367,7 +367,7 @@ function simpletest_last_test_get($test_id) { * * @param $test_id * The test ID to which the log relates. - * @param $prefix + * @param $database_prefix * The database prefix to which the log relates. * @param $test_class * The test class to which the log relates. @@ -377,8 +377,8 @@ function simpletest_last_test_get($test_id) { * @return * Found any entries in log. */ -function simpletest_log_read($test_id, $prefix, $test_class, $during_test = FALSE) { - $log = 'public://' . ($during_test ? '' : '/simpletest/' . substr($prefix, 10)) . '/error.log'; +function simpletest_log_read($test_id, $database_prefix, $test_class, $during_test = FALSE) { + $log = 'sites/simpletest/' . substr($database_prefix, 10) . '/error.log'; $found = FALSE; if (file_exists($log)) { foreach (file($log) as $line) { @@ -632,14 +632,12 @@ function simpletest_clean_database() { */ function simpletest_clean_temporary_directories() { $count = 0; - if (is_dir('public://simpletest')) { - $files = scandir('public://simpletest'); + if (is_dir('sites/simpletest')) { + $files = scandir('sites/simpletest'); foreach ($files as $file) { - $path = 'public://simpletest/' . $file; - if (is_dir($path) && (is_numeric($file) || strpos($file, 'config_simpletest') !== FALSE)) { - file_unmanaged_delete_recursive($path, array('Drupal\simpletest\TestBase', 'filePreDeleteCallback')); - $count++; - } + $path = 'sites/simpletest/' . $file; + file_unmanaged_delete_recursive($path, array('Drupal\simpletest\TestBase', 'filePreDeleteCallback')); + $count++; } } diff --git a/core/modules/system/lib/Drupal/system/Tests/File/DirectoryTest.php b/core/modules/system/lib/Drupal/system/Tests/File/DirectoryTest.php index 7fd19c5..2cef1a8 100644 --- a/core/modules/system/lib/Drupal/system/Tests/File/DirectoryTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/File/DirectoryTest.php @@ -6,6 +6,7 @@ */ namespace Drupal\system\Tests\File; +use Drupal\Core\Utility\Site; /** * Directory related tests. @@ -23,7 +24,7 @@ public static function getInfo() { * Test local directory handling functions. */ function testFileCheckLocalDirectoryHandling() { - $directory = conf_path() . '/files'; + $directory = Site::getPath('files'); // Check a new recursively created local directory for correct file system // permissions. diff --git a/core/modules/system/lib/Drupal/system/Tests/File/ReadOnlyStreamWrapperTest.php b/core/modules/system/lib/Drupal/system/Tests/File/ReadOnlyStreamWrapperTest.php index ef84796..ee0426a 100644 --- a/core/modules/system/lib/Drupal/system/Tests/File/ReadOnlyStreamWrapperTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/File/ReadOnlyStreamWrapperTest.php @@ -6,6 +6,7 @@ */ namespace Drupal\system\Tests\File; +use Drupal\Core\Utility\Site; /** * Tests that files can not be written using ReadOnlyStreamWrapper functions. @@ -46,7 +47,7 @@ function tearDown() { function testWriteFunctions() { // Generate a test file $filename = $this->randomName(); - $filepath = conf_path() . '/files/' . $filename; + $filepath = Site::getPath('files/' . $filename); file_put_contents($filepath, $filename); // Generate a read-only stream wrapper instance @@ -89,7 +90,7 @@ function testWriteFunctions() { // Test the mkdir() function by attempting to create a directory. $dirname = $this->randomName(); - $dir = conf_path() . '/files/' . $dirname; + $dir = Site::getPath('files/' . $dirname); $readonlydir = $this->scheme . '://' . $dirname; $this->assertFalse(@drupal_mkdir($readonlydir, 0775, 0), 'Unable to create directory with read-only stream wrapper.'); // Create a temporary directory for testing purposes diff --git a/core/modules/system/lib/Drupal/system/Tests/System/SettingsRewriteTest.php b/core/modules/system/lib/Drupal/system/Tests/System/SettingsRewriteTest.php index 74c080d..6256f21 100644 --- a/core/modules/system/lib/Drupal/system/Tests/System/SettingsRewriteTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/System/SettingsRewriteTest.php @@ -7,6 +7,7 @@ namespace Drupal\system\Tests\System; +use Drupal\Core\Utility\Site; use Drupal\simpletest\UnitTestBase; /** @@ -104,7 +105,7 @@ function testDrupalRewriteSettings() { ), ); foreach ($tests as $test) { - $filename = settings()->get('file_public_path', conf_path() . '/files') . '/mock_settings.php'; + $filename = settings()->get('file_public_path', Site::getPath('files')) . '/mock_settings.php'; file_put_contents(DRUPAL_ROOT . '/' . $filename, "assertEqual(file_get_contents(DRUPAL_ROOT . '/' . $filename), " $conf_path)); } foreach (array('settings.php', 'settings.local.php') as $conf_file) { - $full_path = $conf_path . '/' . $conf_file; + $full_path = Site::getPath($conf_file); if (file_exists($full_path) && !drupal_verify_install_file($full_path, FILE_EXIST|FILE_READABLE|FILE_NOT_WRITABLE)) { $conf_errors[] = t("The file %file is not protected from modifications and poses a security risk. You must change the file's permissions to be non-writable.", array('%file' => $full_path)); } @@ -327,9 +328,8 @@ function system_requirements($phase) { } else { // If we are installing Drupal, the settings.php file might not exist yet - // in the intended conf_path() directory, so don't require it. The - // conf_path() cache must also be reset in this case. - $directories[] = conf_path(FALSE, TRUE) . '/files'; + // in the intended site directory, so don't require it. + $directories[] = Site::getPath('files'); } if (!empty($conf['system.file']['path.private'])) { $directories[] = $conf['system.file']['path.private']; @@ -355,7 +355,7 @@ function system_requirements($phase) { $requirements['config directories'] = array( 'title' => t('Configuration directories'), 'value' => t('Not present'), - 'description' => t('Your %file file must define the $config_directories variable as an array containing the name of a directories in which configuration files can be written.', array('%file' => conf_path() . '/settings.php')), + 'description' => t('Your %file file must define the $config_directories variable as an array containing the name of a directories in which configuration files can be written.', array('%file' => Site::getPath('settings.php'))), 'severity' => REQUIREMENT_ERROR, ); } diff --git a/core/modules/update/update.manager.inc b/core/modules/update/update.manager.inc index 19c0dc1..1ea7052 100644 --- a/core/modules/update/update.manager.inc +++ b/core/modules/update/update.manager.inc @@ -38,6 +38,7 @@ use Drupal\Core\Updater\Updater; use Drupal\Core\FileTransfer\Local; +use Drupal\Core\Utility\Site; use Symfony\Component\HttpFoundation\RedirectResponse; /** @@ -470,7 +471,7 @@ function update_manager_update_ready_form_submit($form, &$form_state) { // trying to install the code, there's no need to prompt for FTP/SSH // credentials. Instead, we instantiate a Drupal\Core\FileTransfer\Local and // invoke update_authorize_run_update() directly. - if (fileowner($project_real_location) == fileowner(conf_path())) { + if (fileowner($project_real_location) == fileowner(Site::getPath())) { module_load_include('inc', 'update', 'update.authorize'); $filetransfer = new Local(DRUPAL_ROOT); update_authorize_run_update($filetransfer, $updates); @@ -748,7 +749,7 @@ function update_manager_install_form_submit($form, &$form_state) { // trying to install the code, there's no need to prompt for FTP/SSH // credentials. Instead, we instantiate a Drupal\Core\FileTransfer\Local and // invoke update_authorize_run_install() directly. - if (fileowner($project_real_location) == fileowner(conf_path())) { + if (fileowner($project_real_location) == fileowner(Site::getPath())) { module_load_include('inc', 'update', 'update.authorize'); $filetransfer = new Local(DRUPAL_ROOT); call_user_func_array('update_authorize_run_install', array_merge(array($filetransfer), $arguments)); @@ -947,7 +948,7 @@ function update_manager_local_transfers_allowed() { // the configuration directory to determine if local transfers will be // allowed. $temporary_file = drupal_tempnam('temporary://', 'update_'); - $local_transfers_allowed = fileowner($temporary_file) === fileowner(conf_path()); + $local_transfers_allowed = fileowner($temporary_file) === fileowner(Site::getPath()); // Clean up. If this fails, we can ignore it (since this is just a temporary // file anyway). diff --git a/sites/example.sites.php b/sites/example.sites.php deleted file mode 100644 index 2b00151..0000000 --- a/sites/example.sites.php +++ /dev/null @@ -1,55 +0,0 @@ -..' => 'directory'. As an - * example, to map http://www.drupal.org:8080/mysite/test to the configuration - * directory sites/example.com, the array should be defined as: - * @code - * $sites = array( - * '8080.www.drupal.org.mysite.test' => 'example.com', - * ); - * @endcode - * The URL, http://www.drupal.org:8080/mysite/test/, could be a symbolic link or - * an Apache Alias directive that points to the Drupal root containing - * index.php. An alias could also be created for a subdomain. See the - * @link http://drupal.org/documentation/install online Drupal installation guide @endlink - * for more information on setting up domains, subdomains, and subdirectories. - * - * The following examples look for a site configuration in sites/example.com: - * @code - * URL: http://dev.drupal.org - * $sites['dev.drupal.org'] = 'example.com'; - * - * URL: http://localhost/example - * $sites['localhost.example'] = 'example.com'; - * - * URL: http://localhost:8080/example - * $sites['8080.localhost.example'] = 'example.com'; - * - * URL: http://www.drupal.org:8080/mysite/test/ - * $sites['8080.www.drupal.org.mysite.test'] = 'example.com'; - * @endcode - * - * @see default.settings.php - * @see conf_path() - * @see http://drupal.org/documentation/install/multi-site - */