diff -u b/core/core.services.yml b/core/core.services.yml --- b/core/core.services.yml +++ b/core/core.services.yml @@ -151,12 +151,14 @@ plugin.manager.entity: class: Drupal\Core\Entity\EntityManager arguments: ['@container.namespaces'] - config.importer: - class: Drupal\Core\Config\ConfigImporter - arguments: ['config.importer', '@config.storage', '@event_dispatcher', '@config.context.free', '@config.factory', '@plugin.manager.entity', '@lock'] + config.comparer: + class: Drupal\Core\Config\StorageComparerManifest + arguments: ['@config.storage.staging', '@config.storage'] calls: - - [setSourceStorage, ['@config.storage.staging']] - [createChangelist] + config.importer: + class: Drupal\Core\Config\ConfigImporter + arguments: ['config.importer', '@config.comparer', '@event_dispatcher', '@config.factory', '@plugin.manager.entity', '@lock'] plugin.manager.archiver: class: Drupal\Core\Archiver\ArchiverManager arguments: ['@container.namespaces'] diff -u b/core/includes/config.inc b/core/includes/config.inc --- b/core/includes/config.inc +++ b/core/includes/config.inc @@ -5,6 +5,7 @@ use Drupal\Core\Config\ConfigImporter; use Drupal\Core\Config\FileStorage; use Drupal\Core\Config\StorageInterface; +use Drupal\Core\Config\StorageComparer; use Symfony\Component\Yaml\Dumper; /** @@ -30,22 +31,19 @@ $config_dir = drupal_get_path($type, $name) . '/config'; if (is_dir($config_dir)) { $source_storage = new FileStorage($config_dir); + $storage_comparer = new StorageComparer($source_storage, Drupal::service('config.storage')); + // Only import new config. Changed config is from previous enables and + // should not be overwritten. + $storage_comparer->addChangelistCreate(); $installer = new ConfigImporter( 'config.installer', - Drupal::service('config.storage'), + $storage_comparer, Drupal::service('event_dispatcher'), - Drupal::service('config.context.free'), Drupal::service('config.factory'), - Drupal::service('plugin.manager.entity'), - Drupal::service('lock') + Drupal::entityManager(), + Drupal::lock() ); - $installer->setSourceStorage($source_storage) - // Ignore manifest files when creating changelist to import. Only import - // new config. Changed config is from previous enables and should not be - // overwritten. - ->setUseManifest(FALSE) - ->addChangelistCreate() - ->import(); + $installer->import(); } } diff -u b/core/lib/Drupal/Core/Config/ConfigImporter.php b/core/lib/Drupal/Core/Config/ConfigImporter.php --- b/core/lib/Drupal/Core/Config/ConfigImporter.php +++ b/core/lib/Drupal/Core/Config/ConfigImporter.php @@ -8,10 +8,30 @@ namespace Drupal\Core\Config; use Drupal\Component\Plugin\PluginManagerInterface; -use Drupal\Core\Config\Context\ContextInterface; +use Drupal\Core\Config\Context\FreeConfigContext; use Drupal\Core\Lock\LockBackendInterface; -use Symfony\Component\EventDispatcher\EventDispatcher; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; +/** + * Defines a configuration importer. + * + * A config importer imports the changes into the configuration system. To + * determine which changes to import a StorageComparer in used. + * + * @see \Drupal\Core\Config\StorageComparerInterface + * + * Each instance of ConfigImporter has a service name which is used to construct + * event names. The events fired during an import are: + * - '{service name}.validate': Events listening can throw a + * \Drupal\Core\Config\ConfigImporterException to prevent an import from + * occurring. + * @see \Drupal\Core\EventSubscriber\ConfigImportSubscriber + * - '{service name}.import': Events listening can react to a successful + * import. + * @see \Drupal\Core\EventSubscriber\ConfigSnapshotSubscriber + * + * @see \Drupal\Core\Config\ConfigImporterEvent + */ class ConfigImporter { /** @@ -22,18 +42,11 @@ protected $serviceName; /** - * The source storage used to discover configuration changes. + * The storage comparer used to discover configuration changes. * - * @var \Drupal\Core\Config\StorageInterface + * @var \Drupal\Core\Config\StorageComparerInterface */ - protected $sourceStorage; - - /** - * The target storage used to write configuration changes. - * - * @var \Drupal\Core\Config\StorageInterface - */ - protected $targetStorage; + protected $storageComparer; /** * The event dispatcher used to notify subscribers. @@ -71,14 +84,6 @@ protected $lock; /** - * List of changes to import from the source storage to the target - * storage. - * - * @var array - */ - protected $changelist; - - /** * List of changes processed by the import(). * * @var array @@ -86,313 +91,69 @@ protected $processed; /** - * Indicates is the list of changelist has been validated. + * Indicates changes to import have been validated. * * @var bool */ protected $validated; /** - * Indicates whether manifest should be used when creating changelists. - * - * @var bool - */ - protected $useManifest = TRUE; - - /** - * Lists all the configuration object names in the source storage. - * - * @see \Drupal\Core\Config\ConfigImporter::getSourceNames() - * - * @var array - */ - protected $sourceNames = array(); - - /** - * Lists all the configuration object names in the target storage. - * - * @see \Drupal\Core\Config\ConfigImporter::getTargetNames() - * - * @var array - */ - protected $targetNames = array(); - - /** - * List of config entities in the managed by manifests in the source storage. - * - * @see \Drupal\Core\Config\ConfigImporter::getSourceManifestData() - * - * @var array - */ - protected $sourceManifestData = array(); - - /** - * List of config entities in the managed by manifests in the target storage. - * - * @see \Drupal\Core\Config\ConfigImporter::getTargetManifestData() - * - * @var array - */ - protected $targetManifestData = array(); - - /** * Constructs a configuration import object. * * @param string $service_name * The name of the config importer service. Used by notify to dispatch * service specific events. - * @param \Drupal\Core\Config\StorageInterface $target_storage - * A storage controller object to use for writing the configuration changes - * to. - * @param \Symfony\Component\EventDispatcher\EventDispatcher $event_dispatcher - * The event dispatcher used to notify subscribers. - * @param \Drupal\Core\Config\Context\ContextInterface $context - * The config context to use. + * @param \Drupal\Core\Config\StorageComparerInterface $storage_comparer + * A storage comparer object to used determining configuration changes and + * accessing the source and target storage objects. + * @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $event_dispatcher + * The event dispatcher used to notify subscribers of config import events. + * @param \Drupal\Core\Config\ConfigFactory $config_factory + * The config factory that statically caches config objects. + * @param \Drupal\Component\Plugin\PluginManagerInterface $entity_manager + * The entity plugin manager used to import config entities. + * @param \Drupal\Core\Lock\LockBackendInterface + * The lock backend to ensure multiple imports do not occur at the same time. */ - public function __construct($service_name, StorageInterface $target_storage, EventDispatcher $event_dispatcher, ContextInterface $context, ConfigFactory $config_factory, PluginManagerInterface $plugin_manager, LockBackendInterface $lock) { + public function __construct($service_name, StorageComparerInterface $storage_comparer, EventDispatcherInterface $event_dispatcher, ConfigFactory $config_factory, PluginManagerInterface $plugin_manager, LockBackendInterface $lock) { $this->serviceName = $service_name; - $this->targetStorage = $target_storage; + $this->storageComparer = $storage_comparer; $this->eventDispatcher = $event_dispatcher; - $this->context = $context; $this->configFactory = $config_factory; $this->pluginManager = $plugin_manager; $this->lock = $lock; + $this->processed = $this->storageComparer->getEmptyChangelist(); + // Use an override free context for importing so that overrides to do not + // pollute the imported data. The context is hard coded to ensure this is + // the case. + $this->context = new FreeConfigContext($this->eventDispatcher); } /** - * Sets the configuration source storage. + * Gets the configuration storage comparer. * - * @param \Drupal\Core\Config\StorageInterface $sourceStorage - * Storage controller object used to read configuration changes from. - * - * @return \Drupal\Core\Config\ConfigImporter - * The ConfigImporter instance. - */ - public function setSourceStorage(StorageInterface $sourceStorage) { - $this->sourceStorage = $sourceStorage; - $this->resetLists(); - return $this; - } - - /** - * Gets the configuration source storage. - * - * @return \Drupal\Core\Config\StorageInterface - * Storage controller object used to read configuration changes from. - */ - public function getSourceStorage() { - // @todo throw error if not set. - return $this->sourceStorage; - } - - /** - * Sets whether or not to use manifest to compare storages. - * - * @param bool $use_manifest - * Use manifest to compare storages. - */ - public function setUseManifest($use_manifest) { - $this->useManifest = $use_manifest; - return $this; - } - - /** - * Gets an empty changelist. - * - * @return array - * An empty changelist array. - */ - protected function getEmptyChangelist() { - return array( - 'create' => array(), - 'update' => array(), - 'delete' => array(), - ); - } - - /** - * Gets the list of differences to import. - * - * @return array - * An array of config changes that are yet to be imported. + * @return \Drupal\Core\Config\StorageComparerInterface + * Storage comparer object used to calculate configuration changes. */ - public function getChangelist() { - return $this->changelist; - } - - /** - * Add changes to the changelist. - * - * @param string $op - * The change operation performed. Either create, change or delete. - * @param array $changes - * Array of changes to add the changelist. - * - * @return \Drupal\Core\Config\ConfigImporter - * The ConfigImporter instance. - */ - public function addChangeList($op, array $changes) { - // Only add changes that aren't already listed. - $changes = array_diff($changes, $this->changelist[$op]); - $this->changelist[$op] = array_merge($this->changelist[$op], $changes); - $this->validated = FALSE; - return $this; + public function getStorageComparer() { + return $this->storageComparer; } /** - * Resets the changelist and processed list. + * Resets the storage comparer and processed list. * * @return \Drupal\Core\Config\ConfigImporter * The ConfigImporter instance. */ - public function resetLists() { - $this->processed = $this->changelist = $this->getEmptyChangelist(); - $this->sourceManifestData = $this->sourceNames = $this->targetManifestData = $this->targetNames = array(); + public function reset() { + $this->storageComparer->reset(); + $this->processed = $this->storageComparer->getEmptyChangelist(); $this->validated = FALSE; return $this; } /** - * Add differences between source and target configuration storage to changelist. - * - * @return \Drupal\Core\Config\ConfigImporter - * The ConfigImporter instance. - */ - public function createChangelist() { - return $this - ->addChangelistCreate() - ->addChangelistUpdate() - ->addChangelistDelete(); - } - - /** - * Creates the delete changelist. - * - * @return \Drupal\Core\Config\ConfigImporter - * The ConfigImporter instance. - */ - public function addChangelistDelete() { - if ($this->useManifest) { - foreach (array_diff_key($this->getTargetManifestData(), $this->getSourceManifestData()) as $value) { - $this->addChangeList('delete', array($value['name'])); - } - } - else { - $this->addChangeList('delete', array_diff($this->getTargetNames(), $this->getSourceNames())); - } - return $this; - } - - /** - * Creates the create changelist. - * - * @return \Drupal\Core\Config\ConfigImporter - * The ConfigImporter instance. - */ - public function addChangelistCreate() { - if ($this->useManifest) { - foreach (array_diff_key($this->getSourceManifestData(), $this->getTargetManifestData()) as $value) { - $this->addChangeList('create', array($value['name'])); - } - } - else { - $this->addChangeList('create', array_diff($this->getSourceNames(), $this->getTargetNames())); - } - return $this; - } - - /** - * Creates the update changelist. - * - * @return \Drupal\Core\Config\ConfigImporter - * The ConfigImporter instance. - */ - public function addChangelistUpdate() { - foreach (array_intersect($this->getSourceNames(), $this->getTargetNames()) as $name) { - // Ignore manifest files - if (substr($name, 0, 9) != 'manifest.') { - $source_config_data = $this->getSourceStorage()->read($name); - $target_config_data = $this->targetStorage->read($name); - if ($source_config_data !== $target_config_data) { - $this->addChangeList('update', array($name)); - } - } - } - return $this; - } - - /** - * Gets all the configuration names in the source storage. - * - * @return array - * List of all the configuration names in the source storage. - */ - protected function getSourceNames() { - if (empty($this->sourceNames)) { - $this->sourceNames = $this->getSourceStorage()->listAll(); - } - return $this->sourceNames; - } - - /** - * Gets all the configuration names in the target storage. - * - * @return array - * List of all the configuration names in the target storage. - */ - protected function getTargetNames() { - if (empty($this->targetNames)) { - $this->targetNames = $this->targetStorage->listAll(); - } - return $this->targetNames; - } - - /** - * Gets the list of config entities from the source storage's manifest files. - * - * Config entities maintain 'manifest' files that list the objects they are - * currently handling. Each file is a simple indexed array of config object - * names. In order to generate a list of objects that have been created or - * deleted we need to open these files in both the source and target storage, - * generate an array of the objects, and compare them. - * - * @return array - * The list of config entities in the source storage whose entity type has a - * manifest in the source storage. - */ - protected function getSourceManifestData() { - if (empty($this->sourceManifestData)) { - foreach ($this->getSourceStorage()->listAll('manifest') as $name) { - if ($source_manifest_data = $this->getSourceStorage()->read($name)) { - $this->sourceManifestData = array_merge($this->sourceManifestData, $source_manifest_data); - } - } - } - return $this->sourceManifestData; - } - - /** - * Gets the list of config entities from the target storage's manifest files. - * - * @see \Drupal\Core\Config\ConfigImporter::getSourceManifestData() - * - * @return array - * The list of config entities in the target storage whose entity type has a - * manifest in the source storage. - */ - protected function getTargetManifestData() { - if (empty($this->targetManifestData)) { - foreach ($this->getSourceStorage()->listAll('manifest') as $name) { - if ($target_manifest_data = $this->targetStorage->read($name)) { - $this->targetManifestData = array_merge($this->targetManifestData, $target_manifest_data); - } - } - } - return $this->targetManifestData; - } - - /** - * Checks if there is a changelist with changes to process. + * Checks if there are any unprocessed changes. * * @param array $changelists * Changelists to check for changes. Defaults to all changelists, i.e. @@ -437,13 +198,13 @@ * * @param $op * The change operation to get the unprocessed list for. Either create, - * change or delete. + * update or delete. * * @return array * An array of configuration names. */ public function getUnprocessed($op) { - return array_diff($this->changelist[$op], $this->processed[$op]); + return array_diff($this->storageComparer->getChangelist($op), $this->processed[$op]); } /** @@ -469,9 +230,9 @@ // Allow modules to react to a import. $this->notify('import'); - // We're done. + // The import is now complete. $this->lock->release($this->serviceName); - $this->resetLists(); + $this->reset(); // Leave the context used during import and clear the ConfigFactory's // static cache. $this->configFactory->leaveContext()->reset(); @@ -501,12 +262,12 @@ protected function importConfig() { foreach (array('delete', 'create', 'update') as $op) { foreach ($this->getUnprocessed($op) as $name) { - $config = new Config($name, $this->targetStorage, $this->context); + $config = new Config($name, $this->storageComparer->getTargetStorage(), $this->context); if ($op == 'delete') { $config->delete(); } else { - $data = $this->getSourceStorage()->read($name); + $data = $this->storageComparer->getSourceStorage()->read($name); $config->setData($data ? $data : array()); $config->save(); } @@ -535,11 +296,11 @@ // Validate the configuration object name before importing it. // Config::validateName($name); if ($entity_type = config_get_entity_type_by_name($name)) { - $old_config = new Config($name, $this->targetStorage, $this->context); + $old_config = new Config($name, $this->storageComparer->getTargetStorage(), $this->context); $old_config->load(); - $data = $this->getSourceStorage()->read($name); - $new_config = new Config($name, $this->targetStorage, $this->context); + $data = $this->storageComparer->getSourceStorage()->read($name); + $new_config = new Config($name, $this->storageComparer->getTargetStorage(), $this->context); if ($data !== FALSE) { $new_config->setData($data); } @@ -585,2 +346,3 @@ } + } diff -u b/core/modules/config/config.admin.inc b/core/modules/config/config.admin.inc --- b/core/modules/config/config.admin.inc +++ b/core/modules/config/config.admin.inc @@ -45,7 +45,7 @@ // Add the AJAX library to the form for dialog support. $form['#attached']['library'][] = array('system', 'drupal.ajax'); - foreach ($config_import->getChangelist() as $config_change_type => $config_files) { + foreach ($config_import->getStorageComparer()->getChangelist() as $config_change_type => $config_files) { if (empty($config_files)) { continue; } @@ -137,7 +137,7 @@ // Once a sync completes, we empty the staging directory. This prevents // changes from being accidentally overwritten by stray files getting // imported later. - $source_storage = Drupal::service('config.importer')->getSourceStorage(); + $source_storage = Drupal::service('config.importer')->getStorageComparer()->getSourceStorage(); foreach ($source_storage->listAll() as $name) { $source_storage->delete($name); } diff -u b/core/modules/config/lib/Drupal/config/Tests/ConfigCRUDTest.php b/core/modules/config/lib/Drupal/config/Tests/ConfigCRUDTest.php --- b/core/modules/config/lib/Drupal/config/Tests/ConfigCRUDTest.php +++ b/core/modules/config/lib/Drupal/config/Tests/ConfigCRUDTest.php @@ -7,7 +7,6 @@ namespace Drupal\config\Tests; -use Drupal\Core\Config\ConfigImporter; use Drupal\Core\Config\ConfigNameException; use Drupal\simpletest\DrupalUnitTestBase; @@ -204,20 +203,9 @@ // Verify that an exception is thrown when importing. $message = 'Expected ConfigNameException was thrown when attempting to sync invalid configuration.'; - $config_importer = new ConfigImporter( - 'config.importer', - $this->container->get('config.storage'), - $this->container->get('event_dispatcher'), - $this->container->get('config.context.free'), - $this->container->get('config.factory'), - $this->container->get('plugin.manager.entity'), - $this->container->get('lock') - ); try { - $config_importer - ->setSourceStorage($staging) - ->createChangelist() - ->import(); + $this->container->get('config.importer')->import(); + $this->fail($message); } catch (ConfigNameException $e) { $this->pass($message); diff -u b/core/modules/config/lib/Drupal/config/Tests/ConfigImporterTest.php b/core/modules/config/lib/Drupal/config/Tests/ConfigImporterTest.php --- b/core/modules/config/lib/Drupal/config/Tests/ConfigImporterTest.php +++ b/core/modules/config/lib/Drupal/config/Tests/ConfigImporterTest.php @@ -7,7 +7,6 @@ namespace Drupal\config\Tests; -use Drupal\Core\Config\ConfigImporter; use Drupal\simpletest\DrupalUnitTestBase; /** @@ -20,7 +19,7 @@ * * @var \Drupal\Core\Config\ConfigImporter */ - protected $config_importer; + protected $configImporter; /** * Modules to enable. @@ -49,15 +48,7 @@ unset($GLOBALS['hook_config_test']); // Set up the ConfigImporter object for testing. - $this->config_importer = new ConfigImporter( - 'config.importer', - $this->container->get('config.storage'), - $this->container->get('event_dispatcher'), - $this->container->get('config.context.free'), - $this->container->get('config.factory'), - $this->container->get('plugin.manager.entity'), - $this->container->get('lock') - ); + $this->configImporter = $this->container->get('config.importer'); } /** @@ -89,10 +80,7 @@ // Create an empty manifest to delete the configuration object. $staging->write('manifest.config_test.dynamic', array()); // Import. - $this->config_importer - ->setSourceStorage($staging) - ->createChangelist() - ->import(); + $this->configImporter->reset()->import(); // Verify the values have disappeared. $this->assertIdentical($storage->read($dynamic_name), FALSE); @@ -109,7 +97,7 @@ $this->assertTrue(isset($GLOBALS['hook_config_test']['delete'])); // Verify that there is nothing more to import. - $this->assertFalse($this->config_importer->hasChanges()); + $this->assertFalse($this->configImporter->hasChanges()); } /** @@ -145,10 +133,7 @@ $this->assertIdentical($staging->exists($dynamic_name), TRUE, $dynamic_name . ' found.'); // Import. - $this->config_importer - ->setSourceStorage($staging) - ->createChangelist() - ->import(); + $this->configImporter->reset()->import(); // Verify the values appeared. $config = config($dynamic_name); @@ -163,7 +148,7 @@ $this->assertFalse(isset($GLOBALS['hook_config_test']['delete'])); // Verify that there is nothing more to import. - $this->assertFalse($this->config_importer->hasChanges()); + $this->assertFalse($this->configImporter->hasChanges()); } /** @@ -199,10 +184,7 @@ $this->assertIdentical($config->get('label'), 'Default'); // Import. - $this->config_importer - ->setSourceStorage($staging) - ->createChangelist() - ->import(); + $this->configImporter->reset()->import(); // Verify the values were updated. $config = config($name); @@ -223,7 +205,7 @@ $this->assertFalse(isset($GLOBALS['hook_config_test']['delete'])); // Verify that there is nothing more to import. - $this->assertFalse($this->config_importer->hasChanges()); + $this->assertFalse($this->configImporter->hasChanges()); } } diff -u b/core/modules/config/lib/Drupal/config/Tests/ConfigSnapshotTest.php b/core/modules/config/lib/Drupal/config/Tests/ConfigSnapshotTest.php --- b/core/modules/config/lib/Drupal/config/Tests/ConfigSnapshotTest.php +++ b/core/modules/config/lib/Drupal/config/Tests/ConfigSnapshotTest.php @@ -7,7 +7,7 @@ namespace Drupal\config\Tests; -use Drupal\Core\Config\ConfigImporter; +use Drupal\Core\Config\StorageComparer; use Drupal\simpletest\DrupalUnitTestBase; /** @@ -46,32 +46,24 @@ $config_key = 'foo'; $new_data = 'foobar'; - $config_importer = new ConfigImporter( - 'snapshot.comparer', - $snapshot, - drupal_container()->get('event_dispatcher'), - $this->container->get('config.context.free'), - $this->container->get('config.factory'), - $this->container->get('plugin.manager.entity'), - $this->container->get('lock') - ); - $config_importer->setUseManifest(FALSE)->setSourceStorage($active); + $active_snapshot_comparer = new StorageComparer($active, $snapshot); + $staging_snapshot_comparer = new StorageComparer($staging, $snapshot); // Verify that we have an initial snapshot that matches the active // configuration. This has to be true as no config should be installed. - $this->assertFalse($config_importer->createChangelist()->hasChanges()); + $this->assertFalse($active_snapshot_comparer->createChangelist()->hasChanges()); // Install the default config. config_install_default_config('module', 'config_test'); // Although we have imported config this has not affected the snapshot. - $this->assertTrue($config_importer->resetLists()->createChangelist()->hasChanges()); + $this->assertTrue($active_snapshot_comparer->reset()->hasChanges()); // Update the config snapshot. config_import_create_snapshot($active, $snapshot); // The snapshot and active config should now contain the same config // objects. - $this->assertFalse($config_importer->resetLists()->createChangelist()->hasChanges()); + $this->assertFalse($active_snapshot_comparer->reset()->hasChanges()); // Change a configuration value in staging. $staging_data = config($config_name)->get(); @@ -80,8 +72,8 @@ // Verify that active and snapshot match, and that staging doesn't match // active. - $this->assertFalse($config_importer->resetLists()->createChangelist()->hasChanges()); - $this->assertTrue($config_importer->setSourceStorage($staging)->createChangelist()->hasChanges()); + $this->assertFalse($active_snapshot_comparer->reset()->hasChanges()); + $this->assertTrue($staging_snapshot_comparer->createChangelist()->hasChanges()); // Import changed data from staging to active. $this->container->get('config.importer')->import(); @@ -91,7 +83,7 @@ // Verify that a new snapshot was created which and that it matches // the active config. - $this->assertFalse($config_importer->setSourceStorage($active)->createChangelist()->hasChanges()); + $this->assertFalse($active_snapshot_comparer->reset()->hasChanges()); } } diff -u b/core/modules/views/lib/Drupal/views/Tests/ViewTestData.php b/core/modules/views/lib/Drupal/views/Tests/ViewTestData.php --- b/core/modules/views/lib/Drupal/views/Tests/ViewTestData.php +++ b/core/modules/views/lib/Drupal/views/Tests/ViewTestData.php @@ -8,6 +8,7 @@ namespace Drupal\views\Tests; use Drupal\Core\Config\ConfigImporter; +use Drupal\Core\Config\StorageComparer; use Drupal\Core\Config\FileStorage; /** @@ -56,19 +57,20 @@ $views_to_import[] = $config_name; } } + $storage_comparer = new StorageComparer( + $source_storage, + \Drupal::service('config.storage') + ); + $storage_comparer->addChangelist('create', $views_to_import); $installer = new ConfigImporter( 'views.test.installer', - \Drupal::service('config.storage'), + $storage_comparer, \Drupal::service('event_dispatcher'), - \Drupal::service('config.context.free'), \Drupal::service('config.factory'), - \Drupal::service('plugin.manager.entity'), - \Drupal::service('lock') + \Drupal::entityManager(), + \Drupal::lock() ); - $installer - ->setSourceStorage($source_storage) - ->addChangelist('create', $views_to_import) - ->import(); + $installer->import(); } } } only in patch2: unchanged: --- /dev/null +++ b/core/lib/Drupal/Core/Config/StorageComparer.php @@ -0,0 +1,203 @@ +sourceStorage = $source_storage; + $this->targetStorage = $target_storage; + $this->changelist = $this->getEmptyChangelist(); + } + + /** + * {@inheritdoc} + */ + public function getSourceStorage() { + return $this->sourceStorage; + } + + /** + * {@inheritdoc} + */ + public function getTargetStorage() { + return $this->targetStorage; + } + + /** + * {@inheritdoc} + */ + public function getEmptyChangelist() { + return array( + 'create' => array(), + 'update' => array(), + 'delete' => array(), + ); + } + + /** + * {@inheritdoc} + */ + public function getChangelist($op = NULL) { + if ($op) { + return $this->changelist[$op]; + } + return $this->changelist; + } + + /** + * {@inheritdoc} + */ + public function addChangeList($op, array $changes) { + // Only add changes that aren't already listed. + $changes = array_diff($changes, $this->changelist[$op]); + $this->changelist[$op] = array_merge($this->changelist[$op], $changes); + return $this; + } + + /** + * {@inheritdoc} + */ + public function createChangelist() { + return $this + ->addChangelistCreate() + ->addChangelistUpdate() + ->addChangelistDelete(); + } + + /** + * {@inheritdoc} + */ + public function addChangelistDelete() { + return $this->addChangeList('delete', array_diff($this->getTargetNames(), $this->getSourceNames())); + } + + /** + * {@inheritdoc} + */ + public function addChangelistCreate() { + return $this->addChangeList('create', array_diff($this->getSourceNames(), $this->getTargetNames())); + } + + /** + * {@inheritdoc} + */ + public function addChangelistUpdate() { + foreach (array_intersect($this->getSourceNames(), $this->getTargetNames()) as $name) { + // Ignore manifest files + if (substr($name, 0, 9) != 'manifest.') { + $source_config_data = $this->sourceStorage->read($name); + $target_config_data = $this->targetStorage->read($name); + if ($source_config_data !== $target_config_data) { + $this->addChangeList('update', array($name)); + } + } + } + return $this; + } + + /** + * {@inheritdoc} + */ + public function reset() { + $this->changelist = $this->getEmptyChangelist(); + $this->sourceNames = $this->targetNames = array(); + return $this->createChangelist(); + } + + /** + * Checks if there is a changelist with changes to process. + * + * @param array $ops + * Operation to check for changes. Defaults to all operations, i.e. + * array('delete', 'create', 'update'). + * + * @return bool + * TRUE if there are changes to process and FALSE if not. + */ + public function hasChanges($ops = array('delete', 'create', 'update')) { + foreach ($ops as $op) { + if (!empty($this->changelist[$op])) { + return TRUE; + } + } + return FALSE; + } + + /** + * Gets all the configuration names in the source storage. + * + * @return array + * List of all the configuration names in the source storage. + */ + protected function getSourceNames() { + if (empty($this->sourceNames)) { + $this->sourceNames = $this->sourceStorage->listAll(); + } + return $this->sourceNames; + } + + /** + * Gets all the configuration names in the target storage. + * + * @return array + * List of all the configuration names in the target storage. + */ + protected function getTargetNames() { + if (empty($this->targetNames)) { + $this->targetNames = $this->targetStorage->listAll(); + } + return $this->targetNames; + } + +} only in patch2: unchanged: --- /dev/null +++ b/core/lib/Drupal/Core/Config/StorageComparerInterface.php @@ -0,0 +1,130 @@ +getTargetManifestData(), $this->getSourceManifestData()) as $value) { + $this->addChangeList('delete', array($value['name'])); + } + return $this; + } + + /** + * {@inheritdoc} + */ + public function addChangelistCreate() { + foreach (array_diff_key($this->getSourceManifestData(), $this->getTargetManifestData()) as $value) { + $this->addChangeList('create', array($value['name'])); + } + return $this; + } + + /** + * Gets the list of config entities from the source storage's manifest files. + * + * @return array + * The list of config entities in the source storage whose entity type has a + * manifest in the source storage. + */ + protected function getSourceManifestData() { + if (empty($this->sourceManifestData)) { + foreach ($this->getSourceStorage()->listAll('manifest') as $name) { + if ($source_manifest_data = $this->getSourceStorage()->read($name)) { + $this->sourceManifestData = array_merge($this->sourceManifestData, $source_manifest_data); + } + } + } + return $this->sourceManifestData; + } + + /** + * Gets the list of config entities from the target storage's manifest files. + * + * @see \Drupal\Core\Config\ConfigImporter::getSourceManifestData() + * + * @return array + * The list of config entities in the target storage whose entity type has a + * manifest in the source storage. + */ + protected function getTargetManifestData() { + if (empty($this->targetManifestData)) { + foreach ($this->getSourceStorage()->listAll('manifest') as $name) { + if ($target_manifest_data = $this->targetStorage->read($name)) { + $this->targetManifestData = array_merge($this->targetManifestData, $target_manifest_data); + } + } + } + return $this->targetManifestData; + } + + /** + * {@inheritdoc} + */ + public function reset() { + $this->sourceManifestData = $this->targetManifestData = array(); + return parent::reset(); + } +}