diff --git a/core/includes/file.inc b/core/includes/file.inc index 3e5eb8c..16fdf8a 100644 --- a/core/includes/file.inc +++ b/core/includes/file.inc @@ -332,7 +332,17 @@ 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. + try { + $staging = config_get_config_directory(CONFIG_SYNC_DIRECTORY); + } + catch (\Exception $e) { + $staging = FALSE; + } + if ($staging) { + file_save_htaccess($staging, TRUE); + } } /** diff --git a/core/modules/config/config.install b/core/modules/config/config.install new file mode 100644 index 0000000..c971ae6 --- /dev/null +++ b/core/modules/config/config.install @@ -0,0 +1,32 @@ + t('Configuration directory: %type', ['%type' => CONFIG_SYNC_DIRECTORY]), + 'description' => t('The directory %directory is not writable.', ['%directory' => $directory]), + 'severity' => REQUIREMENT_WARNING, + ]; + } + return $requirements; +} diff --git a/core/modules/config/src/Form/ConfigImportForm.php b/core/modules/config/src/Form/ConfigImportForm.php index c16dfe8..733e1d1 100644 --- a/core/modules/config/src/Form/ConfigImportForm.php +++ b/core/modules/config/src/Form/ConfigImportForm.php @@ -72,6 +72,11 @@ public function buildForm(array $form, FormStateInterface $form_state) { * {@inheritdoc} */ public function validateForm(array &$form, FormStateInterface $form_state) { + $directory = config_get_config_directory(CONFIG_SYNC_DIRECTORY); + if (!is_writable($directory)) { + $form_state->setErrorByName('import_tarball', $this->t('The directory %directory is not writable.', ['%directory' => $directory])); + } + $file_upload = $this->getRequest()->files->get('files[import_tarball]', NULL, TRUE); if ($file_upload && $file_upload->isValid()) { $form_state->setValue('import_tarball', $file_upload->getRealPath()); diff --git a/core/modules/config/src/Tests/ConfigImportUploadTest.php b/core/modules/config/src/Tests/ConfigImportUploadTest.php index 33f6171..da1e159 100644 --- a/core/modules/config/src/Tests/ConfigImportUploadTest.php +++ b/core/modules/config/src/Tests/ConfigImportUploadTest.php @@ -50,6 +50,12 @@ 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->drupalPostForm('admin/config/development/configuration/full/import', $edit, t('Upload')); + $this->assertRaw(t('The directory %directory is not writable.', ['%directory' => $directory])); } } diff --git a/core/modules/config/src/Tests/ConfigInstallWebTest.php b/core/modules/config/src/Tests/ConfigInstallWebTest.php index 221d09f..a192e9b 100644 --- a/core/modules/config/src/Tests/ConfigInstallWebTest.php +++ b/core/modules/config/src/Tests/ConfigInstallWebTest.php @@ -31,7 +31,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. @@ -192,4 +192,23 @@ public function testUnmetDependenciesInstall() { $this->rebuildContainer(); $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])); + } + } diff --git a/core/modules/system/src/Tests/System/StatusTest.php b/core/modules/system/src/Tests/System/StatusTest.php index 5bb9578..130a709 100644 --- a/core/modules/system/src/Tests/System/StatusTest.php +++ b/core/modules/system/src/Tests/System/StatusTest.php @@ -29,6 +29,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', )); @@ -65,6 +75,9 @@ public function testStatusPage() { // If a module is fully installed no pending updates exists. $this->assertNoText(t('Out of date')); + // blah + $this->assertRaw(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. 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); diff --git a/core/modules/system/system.install b/core/modules/system/system.install index 44212b1..d57a1a3 100644 --- a/core/modules/system/system.install +++ b/core/modules/system/system.install @@ -548,15 +548,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 name of a directories in which configuration files can be written. It must contain a %sync_key key.', array('%file' => $site_path . '/settings.php', '%sync_key' => CONFIG_SYNC_DIRECTORY)), 'severity' => REQUIREMENT_ERROR, ); }