Commit 52c99083 authored by tlyngej's avatar tlyngej Committed by tlyngej

Issue #2845286 by tlyngej, jzavrl: Should the module suppress configuration override notices?

parent 2b89ba3e
<?php
/**
* @file
* Overloads some Drush config related commands.
*/
use Drupal\Core\Config\ConfigManagerInterface;
use Drupal\Core\Config\FileStorage;
use Drush\Config\StorageWrapper;
use Drupal\Core\Config\StorageComparer;
use Drupal\config_ignore\ConfigImporterIgnore;
/**
* Print out a message regarding what config that will be ignored.
*
* There are some repeating operations, copied from `drush_config_import`, where
* we figure out what config that has changed.
*
* @param string $source
* What config source to use.
*/
function drush_config_ignore_pre_config_import($source = NULL) {
// Determine source directory.
if ($target = drush_get_option('source')) {
$source_dir = $target;
}
else {
$source = CONFIG_SYNC_DIRECTORY;
$source_dir = config_get_config_directory($source);
}
/** @var \Drupal\Core\Config\StorageInterface $active_storage */
$active_storage = \Drupal::service('config.storage');
$source_storage = new FileStorage($source_dir);
$storage_filters = drush_config_get_storage_filters();
if (!empty($storage_filters)) {
$source_storage = new StorageWrapper($source_storage, $storage_filters);
}
/** @var ConfigManagerInterface $config_manager */
$config_manager = \Drupal::service('config.manager');
$storage_comparer = new StorageComparer($source_storage, $active_storage, $config_manager);
$storage_comparer->createChangelist()->hasChanges();
$change_list = array();
foreach ($storage_comparer->getAllCollectionNames() as $collection) {
$change_list[$collection] = $storage_comparer->getChangelist(NULL, $collection);
}
$update_changes = $change_list['']['update'];
if (!empty($update_changes)) {
$red = "\033[31;40m\033[1m%s\033[0m";
$yellow = "\033[1;33;40m\033[1m%s\033[0m";
$green = "\033[1;32;40m\033[1m%s\033[0m";
drush_print();
drush_print(sprintf($green, 'Message from Config Ignore'), 1);
drush_print('The following list of config will be ignore if you chose to import', 1);
foreach ($update_changes as $config_name) {
if (ConfigImporterIgnore::matchConfigName($config_name)) {
drush_print(sprintf($red, $config_name), 3);
}
}
drush_print();
}
}
......@@ -6,6 +6,9 @@
*/
use Drupal\Core\Config\ConfigImporter;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Config\StorageComparer;
use Drupal\config_ignore\ConfigImporterIgnore;
/**
* Implements hook_config_import_steps_alter().
......@@ -19,3 +22,31 @@ function config_ignore_config_import_steps_alter(&$sync_steps, ConfigImporter $c
// workflow.
array_push($sync_steps, ['Drupal\config_ignore\ConfigImporterIgnore', 'postImport']);
}
/**
* Implements hook_form_FORM_ID_alter().
*/
function config_ignore_form_config_admin_import_form_alter(&$form, FormStateInterface $form_state, $form_id) {
// Load the needed services.
$storage_sync = \Drupal::service('config.storage.sync');
$storage = \Drupal::service('config.storage');
$config_manager = \Drupal::service('config.manager');
$storage_compare = new StorageComparer($storage_sync, $storage, $config_manager);
foreach ($storage_compare->getAllCollectionNames() as $collection) {
// Add a new header.
$form[$collection]['update']['list']['#header'][] = t('Ignored');
// Now check if the rows match any of the ignored entities.
if (isset($form[$collection]['update']['list']['#rows']) && !empty($form[$collection]['update']['list']['#rows'])) {
foreach ($form[$collection]['update']['list']['#rows'] as $key => $row) {
if (ConfigImporterIgnore::matchConfigName($row['name'])) {
$form[$collection]['update']['list']['#rows'][$key]['ignored'] = t('✔');
}
else {
$form[$collection]['update']['list']['#rows'][$key]['ignored'] = t('✖');
}
}
}
}
}
......@@ -26,34 +26,16 @@ class ConfigImporterIgnore {
*/
public static function preImport(array &$context, ConfigImporter $config_importer) {
$config_to_ignore = [];
$config_ignore_settings = \Drupal::config('config_ignore.settings')->get('ignored_config_entities');
\Drupal::moduleHandler()->invokeAll('config_ignore_settings_alter', [&$config_ignore_settings]);
foreach (['delete', 'create', 'rename', 'update'] as $op) {
// For now, we only support updates.
if ($op == 'update') {
foreach ($config_importer->getUnprocessedConfiguration($op) as $config) {
foreach ($config_ignore_settings as $config_ignore_setting) {
// Check if the last character in the string is an asterisk.
// If so, it means that it is a wildcard.
if (Unicode::substr($config_ignore_setting, -1) == '*') {
// Remove the asterisk character from the end of the string.
$config_ignore_setting = rtrim($config_ignore_setting, '*');
// Test if the start of the config, we are checking, are matching
// the $config_ignore_setting string. If it is a match, mark
// that config name to be ignored.
if (Unicode::substr($config, 0, strlen($config_ignore_setting)) == $config_ignore_setting) {
$config_to_ignore[$op][$config] = \Drupal::config($config)->getRawData();
}
}
// If string does not contain an asterisk in the end, just compare
// the two strings, and if they match, mark that config name to be
// ignored.
elseif ($config == $config_ignore_setting) {
if (self::matchConfigName($config)) {
$config_to_ignore[$op][$config] = \Drupal::config($config)->getRawData();
}
}
}
}
// We do not support core.extension.
unset($config_to_ignore[$op]['core.extension']);
}
......@@ -77,15 +59,84 @@ class ConfigImporterIgnore {
/** @var SharedTempStore $temp_store */
$temp_store = \Drupal::service('user.shared_tempstore')->get('config_ignore');
$config_to_ignore = $temp_store->get('config_to_ignore');
$config_names_ignored = [];
foreach ($config_to_ignore as $op) {
foreach ($op as $config_name => $config) {
/** @var \Drupal\Core\Config\Config $config_to_restore */
$config_to_restore = \Drupal::service('config.factory')->getEditable($config_name);
$config_to_restore->setData($config)->save();
$config_names_ignored[] = $config_name;
}
}
$context['finished'] = 1;
$temp_store->delete('config_to_ignore');
// Inform about the config entities ignored.
// We have two formats, one for browser output and one for terminal.
if (!empty($config_names_ignored)) {
// The list of names looks different depending on output medium.
// If terminal (CLI), then no markup.
if (php_sapi_name() == 'cli' || isset($_SERVER['argc']) && is_numeric($_SERVER['argc'] && $_SERVER['argc'] > 0)) {
$names_list = "\n\r " . implode("\n\r ", $config_names_ignored);
}
else {
$output = [
'#theme' => 'item_list',
'#list_type' => 'ul',
'#items' => $config_names_ignored,
];
$names_list = render($output);
}
// `PluralTranslatableMarkup` does not seem to handle HTML as well as
// plain t() does. It will not allow the <ul> list in the browser, and
// renders the lists HTML as clear text.
if (count($config_names_ignored) == 1) {
$message = t('The following config entity was ignored: @list', ['@list' => $names_list]);
}
else {
$message = t('The following @count config entities was ignored: @list', ['@count' => count($config_names_ignored), '@list' => $names_list]);
}
drupal_set_message($message, 'warning');
}
}
/**
* Match a config entity name against the list of ignored config entities.
*
* @param string $config_name
* The name of the config entity to match against all ignored entities.
*
* @return bool
* True, if the config entity is to be ignored, false otherwise.
*/
public static function matchConfigName($config_name) {
$config_ignore_settings = \Drupal::config('config_ignore.settings')->get('ignored_config_entities');
\Drupal::moduleHandler()->invokeAll('config_ignore_settings_alter', [&$config_ignore_settings]);
foreach ($config_ignore_settings as $config_ignore_setting) {
// Check if the last character in the string is an asterisk.
// If so, it means that it is a wildcard.
if (Unicode::substr($config_ignore_setting, -1) == '*') {
// Remove the asterisk character from the end of the string.
$config_ignore_setting = rtrim($config_ignore_setting, '*');
// Test if the start of the config, we are checking, are matching
// the $config_ignore_setting string. If it is a match, mark
// that config name to be ignored.
if (Unicode::substr($config_name, 0, strlen($config_ignore_setting)) == $config_ignore_setting) {
return TRUE;
}
}
// If string does not contain an asterisk in the end, just compare
// the two strings, and if they match, mark that config name to be
// ignored.
elseif ($config_name == $config_ignore_setting) {
return TRUE;
}
}
return FALSE;
}
}
......@@ -61,6 +61,9 @@ class ConfigIgnoreTest extends WebTestBase {
// Validate if the title from the imported config was rejected.
$this->assertText('Test import');
// Validate that the user gets a message about what has been ignored.
$this->assertText('The following config entity was ignored');
}
/**
......@@ -92,6 +95,10 @@ class ConfigIgnoreTest extends WebTestBase {
// Validate if the title from the imported config was rejected.
$this->assertText('Test import');
// Validate that the user gets a message about what has been ignored.
$this->assertText('The following config entity was ignored');
}
}
<?php
namespace Drupal\Tests\config_ignore\Functional;
use Drupal\Core\Config\CachedStorage;
use Drupal\Core\Config\FileStorage;
use Drupal\Tests\BrowserTestBase;
/**
* Class ConfigIgnoreTest.
*
* @package Drupal\Tests\config_ignore\Functional
*
* @group config_ignore
* @runTestsInSeparateProcesses
* @preserveGlobalState disabled
*/
class ConfigIgnoreTest extends BrowserTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['config_ignore'];
/**
* Verify that the Sync. table gets update with appropriate ignore actions.
*/
public function testSyncTableUpdate() {
// Setup a config sync. dir with a, more or less, know set of config
// entities. This is a full blown export of yaml files, written to the disk.
$this->config('system.site')->set('name', 'Test import')->save();
$this->config('system.date')->set('first_day', '0')->save();
$this->config('config_ignore.settings')->set('ignored_config_entities', ['system.site'])->save();
$destination = CONFIG_SYNC_DIRECTORY;
$destination_dir = config_get_config_directory($destination);
/** @var CachedStorage $source_storage */
$source_storage = \Drupal::service('config.storage');
$destination_storage = new FileStorage($destination_dir);
foreach ($source_storage->listAll() as $name) {
$destination_storage->write($name, $source_storage->read($name));
}
// Login with a user that has permission to sync. config.
$this->drupalLogin($this->drupalCreateUser(['synchronize configuration']));
// Change the site name, which is supposed to look as an ignored change
// in on the sync. page.
$this->config('system.site')->set('name', 'Test import with changed title')->save();
$this->config('system.date')->set('first_day', '1')->save();
// Validate that the sync. table informs the user that the config will be
// ignored.
$this->drupalGet('admin/config/development/configuration');
$this->assertSession()->responseContains('✔');
$this->assertSession()->responseContains('✖');
}
}
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