Commit 83fce77e authored by catch's avatar catch

Issue #2466197 by alexpott, heddn, dawehner, pjcdawkins: Staging directory...

Issue #2466197 by alexpott, heddn, dawehner, pjcdawkins: Staging directory should not have to be writeable
parent 0107be6a
......@@ -153,6 +153,7 @@ function config_get_config_directory($type) {
if (!empty($config_directories[$type])) {
return $config_directories[$type];
}
// @todo https://www.drupal.org/node/2696103 Throw a more specific exception.
throw new \Exception("The configuration directory type '$type' does not exist");
}
......
......@@ -315,7 +315,22 @@ function file_ensure_htaccess() {
file_save_htaccess('private://', TRUE);
}
file_save_htaccess('temporary://', TRUE);
file_save_htaccess(config_get_config_directory(CONFIG_SYNC_DIRECTORY), TRUE);
// If a staging directory exists then it should contain a .htaccess file.
// @todo https://www.drupal.org/node/2696103 catch a more specific exception
// and simplify this code.
try {
$staging = config_get_config_directory(CONFIG_SYNC_DIRECTORY);
}
catch (\Exception $e) {
$staging = FALSE;
}
if ($staging) {
// Note that we log an error here if we can't write the .htaccess file. This
// can occur if the staging directory is read-only. If it is then it is the
// user's responsibility to create the .htaccess file.
file_save_htaccess($staging, TRUE);
}
}
/**
......
......@@ -361,7 +361,13 @@ function install_begin_request($class_loader, &$install_state) {
\Drupal::setContainer($container);
// Determine whether base system services are ready to operate.
$install_state['config_verified'] = install_ensure_config_directory(CONFIG_SYNC_DIRECTORY);
try {
$sync_directory = config_get_config_directory(CONFIG_SYNC_DIRECTORY);
$install_state['config_verified'] = file_exists($sync_directory);
}
catch (Exception $e) {
$install_state['config_verified'] = FALSE;
}
$install_state['database_verified'] = install_verify_database_settings($site_path);
$install_state['settings_verified'] = $install_state['config_verified'] && $install_state['database_verified'];
......
......@@ -486,16 +486,12 @@ function drupal_install_config_directories() {
// Add a randomized config directory name to settings.php, unless it was
// manually defined in the existing already.
$settings = [];
$config_directories_hash = Crypt::randomBytesBase64(55);
if (empty($config_directories[CONFIG_SYNC_DIRECTORY])) {
$config_directories[CONFIG_SYNC_DIRECTORY] = \Drupal::service('site.path') . '/files/config_' . Crypt::randomBytesBase64(55) . '/sync';
$settings['config_directories'][CONFIG_SYNC_DIRECTORY] = (object) [
'value' => \Drupal::service('site.path') . '/files/config_' . $config_directories_hash . '/sync',
'value' => $config_directories[CONFIG_SYNC_DIRECTORY],
'required' => TRUE,
];
}
if (!empty($settings)) {
// Rewrite settings.php, which also sets the value as global variable.
drupal_rewrite_settings($settings);
}
......@@ -506,19 +502,21 @@ function drupal_install_config_directories() {
// public files directory, which has already been verified to be writable
// itself. But if it somehow fails anyway, the installation cannot proceed.
// Bail out using a similar error message as in system_requirements().
if (!install_ensure_config_directory(CONFIG_SYNC_DIRECTORY)) {
throw new Exception(t('The directory %directory could not be created or could not be made writable. To proceed with the installation, either create the directory and modify its permissions manually or ensure that the installer has the permissions to create it automatically. For more information, see the <a href=":handbook_url">online handbook</a>.', array(
if (!file_prepare_directory($config_directories[CONFIG_SYNC_DIRECTORY], FILE_CREATE_DIRECTORY)
&& !file_exists($config_directories[CONFIG_SYNC_DIRECTORY])) {
throw new Exception(t('The directory %directory could not be created. To proceed with the installation, either create the directory or ensure that the installer has the permissions to create it automatically. For more information, see the <a href=":handbook_url">online handbook</a>.', array(
'%directory' => config_get_config_directory(CONFIG_SYNC_DIRECTORY),
':handbook_url' => 'https://www.drupal.org/server-permissions',
)));
}
// Put a README.txt into the sync config directory. This is required so that
// they can later be added to git. Since this directory is auto-created, we
// have to write out the README rather than just adding it to the drupal core
// repo.
$text = 'This directory contains configuration to be imported into your Drupal site. To make this configuration active, visit admin/config/development/configuration/sync.' . ' For information about deploying configuration between servers, see https://www.drupal.org/documentation/administer/config';
file_put_contents(config_get_config_directory(CONFIG_SYNC_DIRECTORY) . '/README.txt', $text);
elseif (is_writable($config_directories[CONFIG_SYNC_DIRECTORY])) {
// Put a README.txt into the sync config directory. This is required so that
// they can later be added to git. Since this directory is auto-created, we
// have to write out the README rather than just adding it to the drupal core
// repo.
$text = 'This directory contains configuration to be imported into your Drupal site. To make this configuration active, visit admin/config/development/configuration/sync.' . ' For information about deploying configuration between servers, see https://www.drupal.org/documentation/administer/config';
file_put_contents(config_get_config_directory(CONFIG_SYNC_DIRECTORY) . '/README.txt', $text);
}
}
/**
......@@ -529,6 +527,9 @@ function drupal_install_config_directories() {
*
* @return bool
* TRUE if the config directory exists and is writable.
*
* @deprecated in Drupal 8.1.x, will be removed before Drupal 9.0.x. Use
* config_get_config_directory() and file_prepare_directory() instead.
*/
function install_ensure_config_directory($type) {
// The config directory must be defined in settings.php.
......
<?php
/**
* @file
* Install, update and uninstall functions for the config module.
*/
/**
* Implements hook_requirements().
*/
function config_requirements($phase) {
$requirements = [];
try {
$directory = config_get_config_directory(CONFIG_SYNC_DIRECTORY);
}
catch (\Exception $e) {
// system_requirements() guarantees that the CONFIG_SYNC_DIRECTORY exists
// as the config.storage.staging service relies on it.
$directory = FALSE;
}
// Ensure the configuration sync directory is writable. This is only a warning
// because only configuration import from a tarball requires the folder to be
// web writable.
if ($phase !== 'install' && !is_writable($directory)) {
$requirements['config directory ' . CONFIG_SYNC_DIRECTORY] = [
'title' => t('Configuration directory: %type', ['%type' => CONFIG_SYNC_DIRECTORY]),
'description' => t('The directory %directory is not writable.', ['%directory' => $directory]),
'severity' => REQUIREMENT_WARNING,
];
}
return $requirements;
}
......@@ -50,6 +50,11 @@ public function getFormId() {
* {@inheritdoc}
*/
public function buildForm(array $form, FormStateInterface $form_state) {
$directory = config_get_config_directory(CONFIG_SYNC_DIRECTORY);
$directory_is_writable = is_writable($directory);
if (!$directory_is_writable) {
drupal_set_message($this->t('The directory %directory is not writable.', ['%directory' => $directory]), 'error');
}
$form['import_tarball'] = array(
'#type' => 'file',
'#title' => $this->t('Configuration archive'),
......@@ -59,6 +64,7 @@ public function buildForm(array $form, FormStateInterface $form_state) {
$form['submit'] = array(
'#type' => 'submit',
'#value' => $this->t('Upload'),
'#disabled' => !$directory_is_writable,
);
return $form;
}
......
......@@ -45,6 +45,16 @@ function testImport() {
$edit = array('files[import_tarball]' => drupal_realpath($text_file->uri));
$this->drupalPostForm('admin/config/development/configuration/full/import', $edit, t('Upload'));
$this->assertText(t('Could not extract the contents of the tar file'));
// Make the sync directory read-only.
$directory = config_get_config_directory(CONFIG_SYNC_DIRECTORY);
\Drupal::service('file_system')->chmod($directory, 0555);
$this->drupalGet('admin/config/development/configuration/full/import');
$this->assertRaw(t('The directory %directory is not writable.', ['%directory' => $directory]));
// Ensure submit button for \Drupal\config\Form\ConfigImportForm is
// disabled.
$submit_is_disabled = $this->cssSelect('form.config-import-form input[type="submit"]:disabled');
$this->assertTrue(count($submit_is_disabled) === 1, 'The submit button is disabled.');
}
}
......@@ -26,7 +26,7 @@ class ConfigInstallWebTest extends WebTestBase {
protected function setUp() {
parent::setUp();
$this->adminUser = $this->drupalCreateUser(array('administer modules', 'administer themes'));
$this->adminUser = $this->drupalCreateUser(array('administer modules', 'administer themes', 'administer site configuration'));
// Ensure the global variable being asserted by this test does not exist;
// a previous test executed in this request/process might have set it.
......@@ -188,4 +188,22 @@ public function testUnmetDependenciesInstall() {
$this->assertTrue(entity_load('config_test', 'other_module_test_with_dependency'), 'The config_test.dynamic.other_module_test_with_dependency configuration has been created during install.');
}
/**
* Tests config_requirements().
*/
public function testConfigModuleRequirements() {
$this->drupalLogin($this->adminUser);
$this->drupalPostForm('admin/modules', array('modules[Core][config][enable]' => TRUE), t('Install'));
$directory = config_get_config_directory(CONFIG_SYNC_DIRECTORY);
file_unmanaged_delete_recursive($directory);
$this->drupalGet('/admin/reports/status');
$this->assertRaw(t('The directory %directory does not exist.', array('%directory' => $directory)));
file_prepare_directory($directory, FILE_CREATE_DIRECTORY);
\Drupal::service('file_system')->chmod($directory, 0555);
$this->drupalGet('/admin/reports/status');
$this->assertRaw(t('The directory %directory is not writable.', ['%directory' => $directory]));
}
}
......@@ -11,12 +11,21 @@
*/
class InstallerExistingConfigDirectoryTest extends InstallerTestBase {
/**
* The expected file perms of the folder.
*
* @var int
*/
protected $expectedFilePerms;
/**
* {@inheritdoc}
*/
protected function setUp() {
mkdir($this->siteDirectory . '/config_read_only', 0444);
$this->expectedFilePerms = fileperms($this->siteDirectory . '/config_read_only');
$this->settings['config_directories'][CONFIG_SYNC_DIRECTORY] = (object) array(
'value' => $this->siteDirectory . '/config',
'value' => $this->siteDirectory . '/config_read_only',
'required' => TRUE,
);
parent::setUp();
......@@ -28,6 +37,8 @@ protected function setUp() {
public function testInstaller() {
$this->assertUrl('user/1');
$this->assertResponse(200);
$this->assertEqual($this->expectedFilePerms, fileperms($this->siteDirectory . '/config_read_only'));
$this->assertEqual([], glob($this->siteDirectory . '/config_read_only/*'), 'The sync directory is empty after install because it is read-only.');
}
}
......@@ -24,6 +24,16 @@ class StatusTest extends WebTestBase {
protected function setUp() {
parent::setUp();
// Unset the sync directory in settings.php to trigger $config_directories
// error.
$settings['config_directories'] = array(
CONFIG_SYNC_DIRECTORY => (object) array(
'value' => '',
'required' => TRUE,
),
);
$this->writeSettings($settings);
$admin_user = $this->drupalCreateUser(array(
'administer site configuration',
));
......@@ -60,6 +70,9 @@ public function testStatusPage() {
// If a module is fully installed no pending updates exists.
$this->assertNoText(t('Out of date'));
// The global $config_directories is not properly formed.
$this->assertRaw(t('Your %file file must define the $config_directories variable as an array containing the names of directories in which configuration files can be found. It must contain a %sync_key key.', array('%file' => $this->siteDirectory . '/settings.php', '%sync_key' => CONFIG_SYNC_DIRECTORY)));
// Set the schema version of update_test_postupdate to a lower version, so
// update_test_postupdate_update_8001() needs to be executed.
drupal_set_installed_schema_version('update_test_postupdate', 8000);
......
......@@ -549,15 +549,22 @@ function system_requirements($phase) {
// defined, the installer will create a valid config directory later, but
// during runtime we must always display an error.
if (!empty($GLOBALS['config_directories'])) {
foreach ($GLOBALS['config_directories'] as $type => $directory) {
$directories[] = config_get_config_directory($type);
foreach (array_keys(array_filter($GLOBALS['config_directories'])) as $type) {
$directory = config_get_config_directory($type);
if (!is_dir($directory)) {
$requirements['config directory ' . $type] = array(
'title' => t('Configuration directory: %type', ['%type' => $type]),
'description' => t('The directory %directory does not exist.', array('%directory' => $directory)),
'severity' => REQUIREMENT_ERROR,
);
}
}
}
elseif ($phase != 'install') {
if ($phase != 'install' && (empty($GLOBALS['config_directories']) || empty($GLOBALS['config_directories'][CONFIG_SYNC_DIRECTORY]) )) {
$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' => $site_path . '/settings.php')),
'description' => t('Your %file file must define the $config_directories variable as an array containing the names of directories in which configuration files can be found. It must contain a %sync_key key.', array('%file' => $site_path . '/settings.php', '%sync_key' => CONFIG_SYNC_DIRECTORY)),
'severity' => REQUIREMENT_ERROR,
);
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment