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