diff --git a/core/lib/Drupal/Core/Form/ConfigFormBase.php b/core/lib/Drupal/Core/Form/ConfigFormBase.php index e7878a7db0..8f24090750 100644 --- a/core/lib/Drupal/Core/Form/ConfigFormBase.php +++ b/core/lib/Drupal/Core/Form/ConfigFormBase.php @@ -4,6 +4,7 @@ use Drupal\Core\Config\ConfigFactoryInterface; use Symfony\Component\DependencyInjection\ContainerInterface; +use Drupal\Core\Render\Markup; /** * Base class for implementing system configuration forms. @@ -34,6 +35,24 @@ public static function create(ContainerInterface $container) { * {@inheritdoc} */ public function buildForm(array $form, FormStateInterface $form_state) { + $config_overrides = $this->getConfigOverrides(); + + // If there are config overrides, generate a nice message that informs the + // user about this. + if ($config_overrides) { + $text = ''; + // Overrides are grouped by config names (there can be multiple + // ones per form). + foreach ($config_overrides as $config_name => $values) { + $text .= "
" . $config_name . ":"; + foreach ($values as $key => $value) { + $text .= "
- " . $key . ": \"" . key($value) . "\" -> \"" . reset($value) . "\""; + } + } + $text = Markup::create($text); + drupal_set_message(t("This form has values that are overridden either by a module or by settings files.
You can still change them and they will be saved in the config storage, but the changes will maybe not appear within the site.
@overrides", ['@overrides' => $text]), "warning"); + } + $form['actions']['#type'] = 'actions'; $form['actions']['submit'] = [ '#type' => 'submit', diff --git a/core/lib/Drupal/Core/Form/ConfigFormBaseTrait.php b/core/lib/Drupal/Core/Form/ConfigFormBaseTrait.php index 4e60d92690..ddffb3ba34 100644 --- a/core/lib/Drupal/Core/Form/ConfigFormBaseTrait.php +++ b/core/lib/Drupal/Core/Form/ConfigFormBaseTrait.php @@ -3,6 +3,7 @@ namespace Drupal\Core\Form; use Drupal\Core\Config\ConfigFactoryInterface; +use Drupal\Component\Utility\DiffArray; /** * Provides access to configuration for forms. @@ -58,6 +59,96 @@ protected function config($name) { } /** + * Gets an array of all config keys that are overridden. + * + * @return array + * An associative array which contains all config values that are + * overridden by either modules or the settings files. The array is first + * keyed by the name of the config, then the key (concatenated with dots if + * the keys are multidimensional) and last the original string of the + * config, the value is the overridden string. Example: + * 'system.site' => array('page.403' => array('original' => 'overridden')) + */ + protected function getConfigOverrides() { + /** @var \Drupal\Core\Config\ConfigFactoryInterface $config_factory */ + if (method_exists($this, 'configFactory')) { + $config_factory = $this->configFactory(); + } + elseif (property_exists($this, 'configFactory')) { + $config_factory = $this->configFactory; + } + if (!isset($config_factory) || !($config_factory instanceof ConfigFactoryInterface)) { + throw new \LogicException('No config factory available for ConfigFormBaseTrait'); + } + + $diff = []; + + // Forms define which config objects they are using for editing, therefore + // these are also the ones which can be overwritten. + foreach ($this->getEditableConfigNames() as $name) { + // Loading the config with ->get() which means they are loaded overridden. + $config = $config_factory->get($name); + + // Diff the overwritten with the original config, returns a diff with only + // values that have overrides, others are removed. + if (!$config_values = $config->getOriginal('', FALSE)) { + continue; + } + $diff_overwritten = DiffArray::diffAssocRecursive($config->get(), $config_values); + // We are also interested in the original strings, so we do the same on + // the other side around. + $diff_original = DiffArray::diffAssocRecursive($config_values, $config->get()); + + // Config objects are multidimensional. Flatten them with the keys + // concatenated with dots. + $this->flattenArray($diff_overwritten); + $this->flattenArray($diff_original); + + // Loop over the arrays and generate the return array. + foreach ($diff_overwritten as $key => $value) { + $diff[$name][$key] = [$diff_original[$key] => $value]; + } + + } + return $diff; + } + + /** + * Flattens a nested array. + * + * The scheme used is: + * 'key' => array('key2' => array('key3' => 'value')) + * Becomes: + * 'key.key2.key3' => 'value' + * + * This function takes an array by reference and will modify it. + * + * @param array &$messages + * The array that will be flattened. + * @param array $subnode + * Current subnode being parsed, used internally for recursive calls. + * @param string $path + * Current path being parsed, used internally for recursive calls. + */ + private function flattenArray(array &$messages, array $subnode = NULL, $path = NULL) { + if (NULL === $subnode) { + $subnode = &$messages; + } + foreach ($subnode as $key => $value) { + if (is_array($value)) { + $node_path = $path ? $path . '.' . $key : $key; + $this->flattenArray($messages, $value, $node_path); + if (NULL === $path) { + unset($messages[$key]); + } + } + elseif (NULL !== $path) { + $messages[$path . '.' . $key] = $value; + } + } + } + + /** * Gets the configuration names that will be editable. * * @return array