Issue #1740378 by xjm, Désiré, alexpott | heyrocker: Implement renames in the import cycle.
							parent
							
								
									7ec9480ea9
								
							
						
					
					
						commit
						37250bb5bb
					
				| 
						 | 
				
			
			@ -11,6 +11,7 @@ use Drupal\Core\Extension\ModuleHandlerInterface;
 | 
			
		|||
use Drupal\Core\Extension\ThemeHandlerInterface;
 | 
			
		||||
use Drupal\Component\Utility\String;
 | 
			
		||||
use Drupal\Core\Config\Entity\ImportableEntityStorageInterface;
 | 
			
		||||
use Drupal\Core\Config\ConfigEvents;
 | 
			
		||||
use Drupal\Core\DependencyInjection\DependencySerialization;
 | 
			
		||||
use Drupal\Core\Entity\EntityStorageException;
 | 
			
		||||
use Drupal\Core\Lock\LockBackendInterface;
 | 
			
		||||
| 
						 | 
				
			
			@ -263,12 +264,12 @@ class ConfigImporter extends DependencySerialization {
 | 
			
		|||
   *
 | 
			
		||||
   * @param array $ops
 | 
			
		||||
   *   The operations to check for changes. Defaults to all operations, i.e.
 | 
			
		||||
   *   array('delete', 'create', 'update').
 | 
			
		||||
   *   array('delete', 'create', 'update', 'rename').
 | 
			
		||||
   *
 | 
			
		||||
   * @return bool
 | 
			
		||||
   *   TRUE if there are changes to process and FALSE if not.
 | 
			
		||||
   */
 | 
			
		||||
  public function hasUnprocessedConfigurationChanges($ops = array('delete', 'create', 'update')) {
 | 
			
		||||
  public function hasUnprocessedConfigurationChanges($ops = array('delete', 'create', 'rename', 'update')) {
 | 
			
		||||
    foreach ($ops as $op) {
 | 
			
		||||
      if (count($this->getUnprocessedConfiguration($op))) {
 | 
			
		||||
        return TRUE;
 | 
			
		||||
| 
						 | 
				
			
			@ -291,7 +292,7 @@ class ConfigImporter extends DependencySerialization {
 | 
			
		|||
   * Sets a change as processed.
 | 
			
		||||
   *
 | 
			
		||||
   * @param string $op
 | 
			
		||||
   *   The change operation performed, either delete, create or update.
 | 
			
		||||
   *   The change operation performed, either delete, create, rename, or update.
 | 
			
		||||
   * @param string $name
 | 
			
		||||
   *   The name of the configuration processed.
 | 
			
		||||
   */
 | 
			
		||||
| 
						 | 
				
			
			@ -304,7 +305,7 @@ class ConfigImporter extends DependencySerialization {
 | 
			
		|||
   *
 | 
			
		||||
   * @param string $op
 | 
			
		||||
   *   The change operation to get the unprocessed list for, either delete,
 | 
			
		||||
   *   create or update.
 | 
			
		||||
   *   create, rename, or update.
 | 
			
		||||
   *
 | 
			
		||||
   * @return array
 | 
			
		||||
   *   An array of configuration names.
 | 
			
		||||
| 
						 | 
				
			
			@ -586,7 +587,7 @@ class ConfigImporter extends DependencySerialization {
 | 
			
		|||
    // into account.
 | 
			
		||||
    if ($this->totalConfigurationToProcess == 0) {
 | 
			
		||||
      $this->storageComparer->reset();
 | 
			
		||||
      foreach (array('delete', 'create', 'update') as $op) {
 | 
			
		||||
      foreach (array('delete', 'create', 'rename', 'update') as $op) {
 | 
			
		||||
        foreach ($this->getUnprocessedConfiguration($op) as $name) {
 | 
			
		||||
          $this->totalConfigurationToProcess += count($this->getUnprocessedConfiguration($op));
 | 
			
		||||
        }
 | 
			
		||||
| 
						 | 
				
			
			@ -662,7 +663,7 @@ class ConfigImporter extends DependencySerialization {
 | 
			
		|||
  protected function getNextConfigurationOperation() {
 | 
			
		||||
    // The order configuration operations is processed is important. Deletes
 | 
			
		||||
    // have to come first so that recreates can work.
 | 
			
		||||
    foreach (array('delete', 'create', 'update') as $op) {
 | 
			
		||||
    foreach (array('delete', 'create', 'rename', 'update') as $op) {
 | 
			
		||||
      $config_names = $this->getUnprocessedConfiguration($op);
 | 
			
		||||
      if (!empty($config_names)) {
 | 
			
		||||
        return array(
 | 
			
		||||
| 
						 | 
				
			
			@ -685,6 +686,19 @@ class ConfigImporter extends DependencySerialization {
 | 
			
		|||
   */
 | 
			
		||||
  public function validate() {
 | 
			
		||||
    if (!$this->validated) {
 | 
			
		||||
      // Validate renames.
 | 
			
		||||
      foreach ($this->getUnprocessedConfiguration('rename') as $name) {
 | 
			
		||||
        $names = $this->storageComparer->extractRenameNames($name);
 | 
			
		||||
        $old_entity_type_id = $this->configManager->getEntityTypeIdByName($names['old_name']);
 | 
			
		||||
        $new_entity_type_id = $this->configManager->getEntityTypeIdByName($names['new_name']);
 | 
			
		||||
        if ($old_entity_type_id != $new_entity_type_id) {
 | 
			
		||||
          $this->logError($this->t('Entity type mismatch on rename. !old_type not equal to !new_type for existing configuration !old_name and staged configuration !new_name.', array('old_type' => $old_entity_type_id, 'new_type' => $new_entity_type_id, 'old_name' => $names['old_name'], 'new_name' => $names['new_name'])));
 | 
			
		||||
        }
 | 
			
		||||
        // Has to be a configuration entity.
 | 
			
		||||
        if (!$old_entity_type_id) {
 | 
			
		||||
          $this->logError($this->t('Rename operation for simple configuration. Existing configuration !old_name and staged configuration !new_name.', array('old_name' => $names['old_name'], 'new_name' => $names['new_name'])));
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      $this->eventDispatcher->dispatch(ConfigEvents::IMPORT_VALIDATE, new ConfigImporterEvent($this));
 | 
			
		||||
      if (count($this->getErrors())) {
 | 
			
		||||
        throw new ConfigImporterException('There were errors validating the config synchronization.');
 | 
			
		||||
| 
						 | 
				
			
			@ -787,6 +801,20 @@ class ConfigImporter extends DependencySerialization {
 | 
			
		|||
   *   TRUE is to continue processing, FALSE otherwise.
 | 
			
		||||
   */
 | 
			
		||||
  protected function checkOp($op, $name) {
 | 
			
		||||
    if ($op == 'rename') {
 | 
			
		||||
      $names = $this->storageComparer->extractRenameNames($name);
 | 
			
		||||
      $target_exists = $this->storageComparer->getTargetStorage()->exists($names['new_name']);
 | 
			
		||||
      if ($target_exists) {
 | 
			
		||||
        // If the target exists, the rename has already occurred as the
 | 
			
		||||
        // result of a secondary configuration write. Change the operation
 | 
			
		||||
        // into an update. This is the desired behavior since renames often
 | 
			
		||||
        // have to occur together. For example, renaming a node type must
 | 
			
		||||
        // also result in renaming its field instances and entity displays.
 | 
			
		||||
        $this->storageComparer->moveRenameToUpdate($name);
 | 
			
		||||
        return FALSE;
 | 
			
		||||
      }
 | 
			
		||||
      return TRUE;
 | 
			
		||||
    }
 | 
			
		||||
    $target_exists = $this->storageComparer->getTargetStorage()->exists($name);
 | 
			
		||||
    switch ($op) {
 | 
			
		||||
      case 'delete':
 | 
			
		||||
| 
						 | 
				
			
			@ -862,7 +890,7 @@ class ConfigImporter extends DependencySerialization {
 | 
			
		|||
   *
 | 
			
		||||
   * @param string $op
 | 
			
		||||
   *   The change operation to get the unprocessed list for, either delete,
 | 
			
		||||
   *   create or update.
 | 
			
		||||
   *   create, rename, or update.
 | 
			
		||||
   * @param string $name
 | 
			
		||||
   *   The name of the configuration to process.
 | 
			
		||||
   *
 | 
			
		||||
| 
						 | 
				
			
			@ -875,6 +903,10 @@ class ConfigImporter extends DependencySerialization {
 | 
			
		|||
   *   otherwise.
 | 
			
		||||
   */
 | 
			
		||||
  protected function importInvokeOwner($op, $name) {
 | 
			
		||||
    // Renames are handled separately.
 | 
			
		||||
    if ($op == 'rename') {
 | 
			
		||||
      return $this->importInvokeRename($name);
 | 
			
		||||
    }
 | 
			
		||||
    // Validate the configuration object name before importing it.
 | 
			
		||||
    // Config::validateName($name);
 | 
			
		||||
    if ($entity_type = $this->configManager->getEntityTypeIdByName($name)) {
 | 
			
		||||
| 
						 | 
				
			
			@ -900,6 +932,45 @@ class ConfigImporter extends DependencySerialization {
 | 
			
		|||
      $this->setProcessedConfiguration($op, $name);
 | 
			
		||||
      return TRUE;
 | 
			
		||||
    }
 | 
			
		||||
    return FALSE;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Imports a configuration entity rename.
 | 
			
		||||
   *
 | 
			
		||||
   * @param string $rename_name
 | 
			
		||||
   *   The rename configuration name, as provided by
 | 
			
		||||
   *   \Drupal\Core\Config\StorageComparer::createRenameName().
 | 
			
		||||
   *
 | 
			
		||||
   * @return bool
 | 
			
		||||
   *   TRUE if the configuration was imported as a configuration entity. FALSE
 | 
			
		||||
   *   otherwise.
 | 
			
		||||
   *
 | 
			
		||||
   * @see \Drupal\Core\Config\ConfigImporter::createRenameName()
 | 
			
		||||
   */
 | 
			
		||||
  protected function importInvokeRename($rename_name) {
 | 
			
		||||
    $names = $this->storageComparer->extractRenameNames($rename_name);
 | 
			
		||||
    $entity_type_id = $this->configManager->getEntityTypeIdByName($names['old_name']);
 | 
			
		||||
    $old_config = new Config($names['old_name'], $this->storageComparer->getTargetStorage(), $this->eventDispatcher, $this->typedConfigManager);
 | 
			
		||||
    if ($old_data = $this->storageComparer->getTargetStorage()->read($names['old_name'])) {
 | 
			
		||||
      $old_config->initWithData($old_data);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    $data = $this->storageComparer->getSourceStorage()->read($names['new_name']);
 | 
			
		||||
    $new_config = new Config($names['new_name'], $this->storageComparer->getTargetStorage(), $this->eventDispatcher, $this->typedConfigManager);
 | 
			
		||||
    if ($data !== FALSE) {
 | 
			
		||||
      $new_config->setData($data);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    $entity_storage = $this->configManager->getEntityManager()->getStorage($entity_type_id);
 | 
			
		||||
    // Call to the configuration entity's storage to handle the configuration
 | 
			
		||||
    // change.
 | 
			
		||||
    if (!($entity_storage instanceof ImportableEntityStorageInterface)) {
 | 
			
		||||
      throw new EntityStorageException(String::format('The entity storage "@storage" for the "@entity_type" entity type does not support imports', array('@storage' => get_class($entity_storage), '@entity_type' => $entity_type_id)));
 | 
			
		||||
    }
 | 
			
		||||
    $entity_storage->importRename($names['old_name'], $new_config, $old_config);
 | 
			
		||||
    $this->setProcessedConfiguration('rename', $rename_name);
 | 
			
		||||
    return TRUE;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -100,7 +100,10 @@ class ConfigManager implements ConfigManagerInterface {
 | 
			
		|||
  /**
 | 
			
		||||
   * {@inheritdoc}
 | 
			
		||||
   */
 | 
			
		||||
  public function diff(StorageInterface $source_storage, StorageInterface $target_storage, $name) {
 | 
			
		||||
  public function diff(StorageInterface $source_storage, StorageInterface $target_storage, $source_name, $target_name = NULL) {
 | 
			
		||||
    if (!isset($target_name)) {
 | 
			
		||||
      $target_name = $source_name;
 | 
			
		||||
    }
 | 
			
		||||
    // @todo Replace with code that can be autoloaded.
 | 
			
		||||
    //   https://drupal.org/node/1848266
 | 
			
		||||
    require_once __DIR__ . '/../../Component/Diff/DiffEngine.php';
 | 
			
		||||
| 
						 | 
				
			
			@ -111,8 +114,8 @@ class ConfigManager implements ConfigManagerInterface {
 | 
			
		|||
    $dumper = new Dumper();
 | 
			
		||||
    $dumper->setIndentation(2);
 | 
			
		||||
 | 
			
		||||
    $source_data = explode("\n", $dumper->dump($source_storage->read($name), PHP_INT_MAX));
 | 
			
		||||
    $target_data = explode("\n", $dumper->dump($target_storage->read($name), PHP_INT_MAX));
 | 
			
		||||
    $source_data = explode("\n", $dumper->dump($source_storage->read($source_name), PHP_INT_MAX));
 | 
			
		||||
    $target_data = explode("\n", $dumper->dump($target_storage->read($target_name), PHP_INT_MAX));
 | 
			
		||||
 | 
			
		||||
    // Check for new or removed files.
 | 
			
		||||
    if ($source_data === array('false')) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -46,15 +46,18 @@ interface ConfigManagerInterface {
 | 
			
		|||
   *   The storage to diff configuration from.
 | 
			
		||||
   * @param \Drupal\Core\Config\StorageInterface $target_storage
 | 
			
		||||
   *   The storage to diff configuration to.
 | 
			
		||||
   * @param string $name
 | 
			
		||||
   *   The name of the configuration object to diff.
 | 
			
		||||
   * @param string $source_name
 | 
			
		||||
   *   The name of the configuration object in the source storage to diff.
 | 
			
		||||
   * @param string $target_name
 | 
			
		||||
   *   (optional) The name of the configuration object in the target storage.
 | 
			
		||||
   *   If omitted, the source name is used.
 | 
			
		||||
   *
 | 
			
		||||
   * @return core/lib/Drupal/Component/Diff
 | 
			
		||||
   *   A formatted string showing the difference between the two storages.
 | 
			
		||||
   *
 | 
			
		||||
   * @todo Make renderer injectable
 | 
			
		||||
   */
 | 
			
		||||
  public function diff(StorageInterface $source_storage, StorageInterface $target_storage, $name);
 | 
			
		||||
  public function diff(StorageInterface $source_storage, StorageInterface $target_storage, $source_name, $target_name = NULL);
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Creates a configuration snapshot following a successful import.
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -455,4 +455,19 @@ class ConfigEntityStorage extends EntityStorageBase implements ConfigEntityStora
 | 
			
		|||
    return TRUE;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * {@inheritdoc}
 | 
			
		||||
   */
 | 
			
		||||
  public function importRename($old_name, Config $new_config, Config $old_config) {
 | 
			
		||||
    $id = static::getIDFromConfigName($old_name, $this->entityType->getConfigPrefix());
 | 
			
		||||
    $entity = $this->load($id);
 | 
			
		||||
    $entity->setSyncing(TRUE);
 | 
			
		||||
    $data = $new_config->get();
 | 
			
		||||
    foreach ($data as $key => $value) {
 | 
			
		||||
      $entity->set($key, $value);
 | 
			
		||||
    }
 | 
			
		||||
    $entity->save();
 | 
			
		||||
    return TRUE;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -56,4 +56,16 @@ interface ImportableEntityStorageInterface {
 | 
			
		|||
   */
 | 
			
		||||
  public function importDelete($name, Config $new_config, Config $old_config);
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Renames entities upon synchronizing configuration changes.
 | 
			
		||||
   *
 | 
			
		||||
   * @param string $old_name
 | 
			
		||||
   *   The original name of the configuration object.
 | 
			
		||||
   * @param \Drupal\Core\Config\Config $new_config
 | 
			
		||||
   *   A configuration object containing the new configuration data.
 | 
			
		||||
   * @param \Drupal\Core\Config\Config $old_config
 | 
			
		||||
   *   A configuration object containing the old configuration data.
 | 
			
		||||
   */
 | 
			
		||||
  public function importRename($old_name, Config $new_config, Config $old_config);
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -100,6 +100,7 @@ class StorageComparer implements StorageComparerInterface {
 | 
			
		|||
      'create' => array(),
 | 
			
		||||
      'update' => array(),
 | 
			
		||||
      'delete' => array(),
 | 
			
		||||
      'rename' => array(),
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -117,7 +118,7 @@ class StorageComparer implements StorageComparerInterface {
 | 
			
		|||
   * Adds changes to the changelist.
 | 
			
		||||
   *
 | 
			
		||||
   * @param string $op
 | 
			
		||||
   *   The change operation performed. Either delete, create or update.
 | 
			
		||||
   *   The change operation performed. Either delete, create, rename, or update.
 | 
			
		||||
   * @param array $changes
 | 
			
		||||
   *   Array of changes to add to the changelist.
 | 
			
		||||
   * @param array $sort_order
 | 
			
		||||
| 
						 | 
				
			
			@ -147,6 +148,7 @@ class StorageComparer implements StorageComparerInterface {
 | 
			
		|||
    $this->addChangelistCreate();
 | 
			
		||||
    $this->addChangelistUpdate();
 | 
			
		||||
    $this->addChangelistDelete();
 | 
			
		||||
    $this->addChangelistRename();
 | 
			
		||||
    $this->sourceData = NULL;
 | 
			
		||||
    $this->targetData = NULL;
 | 
			
		||||
    return $this;
 | 
			
		||||
| 
						 | 
				
			
			@ -208,6 +210,80 @@ class StorageComparer implements StorageComparerInterface {
 | 
			
		|||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Creates the rename changelist.
 | 
			
		||||
   *
 | 
			
		||||
   * The list of renames is created from the different source and target names
 | 
			
		||||
   * with same UUID. These changes will be removed from the create and delete
 | 
			
		||||
   * lists.
 | 
			
		||||
   */
 | 
			
		||||
  protected function addChangelistRename() {
 | 
			
		||||
    // Renames will be present in both the create and delete lists.
 | 
			
		||||
    $create_list = $this->getChangelist('create');
 | 
			
		||||
    $delete_list = $this->getChangelist('delete');
 | 
			
		||||
    if (empty($create_list) || empty($delete_list)) {
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    $create_uuids = array();
 | 
			
		||||
    foreach ($this->sourceData as $id => $data) {
 | 
			
		||||
      if (isset($data['uuid']) && in_array($id, $create_list)) {
 | 
			
		||||
        $create_uuids[$data['uuid']] = $id;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    if (empty($create_uuids)) {
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    $renames = array();
 | 
			
		||||
 | 
			
		||||
    // Renames should be ordered so that dependencies are renamed last. This
 | 
			
		||||
    // ensures that if there is logic in the configuration entity class to keep
 | 
			
		||||
    // names in sync it will still work. $this->targetNames is in the desired
 | 
			
		||||
    // order due to the use of configuration dependencies in
 | 
			
		||||
    // \Drupal\Core\Config\StorageComparer::getAndSortConfigData().
 | 
			
		||||
    // Node type is a good example of a configuration entity that renames other
 | 
			
		||||
    // configuration when it is renamed.
 | 
			
		||||
    // @see \Drupal\node\Entity\NodeType::postSave()
 | 
			
		||||
    foreach ($this->targetNames as $name) {
 | 
			
		||||
      $data = $this->targetData[$name];
 | 
			
		||||
      if (isset($data['uuid']) && isset($create_uuids[$data['uuid']])) {
 | 
			
		||||
        // Remove the item from the create list.
 | 
			
		||||
        $this->removeFromChangelist('create', $create_uuids[$data['uuid']]);
 | 
			
		||||
        // Remove the item from the delete list.
 | 
			
		||||
        $this->removeFromChangelist('delete', $name);
 | 
			
		||||
        // Create the rename name.
 | 
			
		||||
        $renames[] = $this->createRenameName($name, $create_uuids[$data['uuid']]);
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    $this->addChangeList('rename', $renames);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Removes the entry from the given operation changelist for the given name.
 | 
			
		||||
   *
 | 
			
		||||
   * @param string $op
 | 
			
		||||
   *   The changelist to act on. Either delete, create, rename or update.
 | 
			
		||||
   * @param string $name
 | 
			
		||||
   *   The name of the configuration to remove.
 | 
			
		||||
   */
 | 
			
		||||
  protected function removeFromChangelist($op, $name) {
 | 
			
		||||
    $key = array_search($name, $this->changelist[$op]);
 | 
			
		||||
    if ($key !== FALSE) {
 | 
			
		||||
      unset($this->changelist[$op][$key]);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * {@inheritdoc}
 | 
			
		||||
   */
 | 
			
		||||
  public function moveRenameToUpdate($rename) {
 | 
			
		||||
    $names = $this->extractRenameNames($rename);
 | 
			
		||||
    $this->removeFromChangelist('rename', $rename);
 | 
			
		||||
    $this->addChangeList('update', array($names['new_name']), $this->sourceNames);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * {@inheritdoc}
 | 
			
		||||
   */
 | 
			
		||||
| 
						 | 
				
			
			@ -220,7 +296,7 @@ class StorageComparer implements StorageComparerInterface {
 | 
			
		|||
  /**
 | 
			
		||||
   * {@inheritdoc}
 | 
			
		||||
   */
 | 
			
		||||
  public function hasChanges($ops = array('delete', 'create', 'update')) {
 | 
			
		||||
  public function hasChanges($ops = array('delete', 'create', 'update', 'rename')) {
 | 
			
		||||
    foreach ($ops as $op) {
 | 
			
		||||
      if (!empty($this->changelist[$op])) {
 | 
			
		||||
        return TRUE;
 | 
			
		||||
| 
						 | 
				
			
			@ -249,4 +325,31 @@ class StorageComparer implements StorageComparerInterface {
 | 
			
		|||
    $this->sourceNames = $dependency_manager->setData($this->sourceData)->sortAll();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Creates a rename name from the old and new names for the object.
 | 
			
		||||
   *
 | 
			
		||||
   * @param string $old_name
 | 
			
		||||
   *   The old configuration object name.
 | 
			
		||||
   * @param string $new_name
 | 
			
		||||
   *   The new configuration object name.
 | 
			
		||||
   *
 | 
			
		||||
   * @return string
 | 
			
		||||
   *   The configuration change name that encodes both the old and the new name.
 | 
			
		||||
   *
 | 
			
		||||
   * @see \Drupal\Core\Config\StorageComparerInterface::extractRenameNames()
 | 
			
		||||
   */
 | 
			
		||||
  protected function createRenameName($name1, $name2) {
 | 
			
		||||
    return $name1 . '::' . $name2;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * {@inheritdoc}
 | 
			
		||||
   */
 | 
			
		||||
  public function extractRenameNames($name) {
 | 
			
		||||
    $names = explode('::', $name, 2);
 | 
			
		||||
    return array(
 | 
			
		||||
      'old_name' => $names[0],
 | 
			
		||||
      'new_name' => $names[1],
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -80,4 +80,30 @@ interface StorageComparerInterface {
 | 
			
		|||
   */
 | 
			
		||||
  public function validateSiteUuid();
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Moves a rename operation to an update.
 | 
			
		||||
   *
 | 
			
		||||
   * @param string $rename
 | 
			
		||||
   *   The rename name, as provided by ConfigImporter::createRenameName().
 | 
			
		||||
   *
 | 
			
		||||
   * @see \Drupal\Core\Config\ConfigImporter::createRenameName()
 | 
			
		||||
   */
 | 
			
		||||
  public function moveRenameToUpdate($rename);
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Extracts old and new configuration names from a configuration change name.
 | 
			
		||||
   *
 | 
			
		||||
   * @param string $name
 | 
			
		||||
   *   The configuration change name, as provided by
 | 
			
		||||
   *   ConfigImporter::createRenameName().
 | 
			
		||||
   *
 | 
			
		||||
   * @return array
 | 
			
		||||
   *   An associative array of configuration names. The array keys are
 | 
			
		||||
   *   'old_name' and and 'new_name' representing the old and name configuration
 | 
			
		||||
   *   object names during a rename operation.
 | 
			
		||||
   *
 | 
			
		||||
   * @see \Drupal\Core\Config\StorageComparer::createRenameNames()
 | 
			
		||||
   */
 | 
			
		||||
  public function extractRenameNames($name);
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -7,9 +7,10 @@ config.sync:
 | 
			
		|||
    _permission: 'synchronize configuration'
 | 
			
		||||
 | 
			
		||||
config.diff:
 | 
			
		||||
  path: '/admin/config/development/configuration/sync/diff/{config_file}'
 | 
			
		||||
  path: '/admin/config/development/configuration/sync/diff/{source_name}/{target_name}'
 | 
			
		||||
  defaults:
 | 
			
		||||
    _content: '\Drupal\config\Controller\ConfigController::diff'
 | 
			
		||||
    target_name: NULL
 | 
			
		||||
  requirements:
 | 
			
		||||
    _permission: 'synchronize configuration'
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -105,15 +105,15 @@ class ConfigController implements ContainerInjectionInterface {
 | 
			
		|||
   * @return string
 | 
			
		||||
   *   Table showing a two-way diff between the active and staged configuration.
 | 
			
		||||
   */
 | 
			
		||||
  public function diff($config_file) {
 | 
			
		||||
  public function diff($source_name, $target_name = NULL) {
 | 
			
		||||
 | 
			
		||||
    $diff = $this->configManager->diff($this->targetStorage, $this->sourceStorage, $config_file);
 | 
			
		||||
    $diff = $this->configManager->diff($this->targetStorage, $this->sourceStorage, $source_name, $target_name);
 | 
			
		||||
    $formatter = new \DrupalDiffFormatter();
 | 
			
		||||
    $formatter->show_header = FALSE;
 | 
			
		||||
 | 
			
		||||
    $build = array();
 | 
			
		||||
 | 
			
		||||
    $build['#title'] = t('View changes of @config_file', array('@config_file' => $config_file));
 | 
			
		||||
    $build['#title'] = t('View changes of @config_file', array('@config_file' => $source_name));
 | 
			
		||||
    // Add the CSS for the inline diff.
 | 
			
		||||
    $build['#attached']['css'][] = drupal_get_path('module', 'system') . '/css/system.diff.css';
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -184,8 +184,8 @@ class ConfigSync extends FormBase {
 | 
			
		|||
    // Add the AJAX library to the form for dialog support.
 | 
			
		||||
    $form['#attached']['library'][] = 'core/drupal.ajax';
 | 
			
		||||
 | 
			
		||||
    foreach ($storage_comparer->getChangelist() as $config_change_type => $config_files) {
 | 
			
		||||
      if (empty($config_files)) {
 | 
			
		||||
    foreach ($storage_comparer->getChangelist() as $config_change_type => $config_names) {
 | 
			
		||||
      if (empty($config_names)) {
 | 
			
		||||
        continue;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -197,15 +197,19 @@ class ConfigSync extends FormBase {
 | 
			
		|||
      );
 | 
			
		||||
      switch ($config_change_type) {
 | 
			
		||||
        case 'create':
 | 
			
		||||
          $form[$config_change_type]['heading']['#value'] = format_plural(count($config_files), '@count new', '@count new');
 | 
			
		||||
          $form[$config_change_type]['heading']['#value'] = format_plural(count($config_names), '@count new', '@count new');
 | 
			
		||||
          break;
 | 
			
		||||
 | 
			
		||||
        case 'update':
 | 
			
		||||
          $form[$config_change_type]['heading']['#value'] = format_plural(count($config_files), '@count changed', '@count changed');
 | 
			
		||||
          $form[$config_change_type]['heading']['#value'] = format_plural(count($config_names), '@count changed', '@count changed');
 | 
			
		||||
          break;
 | 
			
		||||
 | 
			
		||||
        case 'delete':
 | 
			
		||||
          $form[$config_change_type]['heading']['#value'] = format_plural(count($config_files), '@count removed', '@count removed');
 | 
			
		||||
          $form[$config_change_type]['heading']['#value'] = format_plural(count($config_names), '@count removed', '@count removed');
 | 
			
		||||
          break;
 | 
			
		||||
 | 
			
		||||
        case 'rename':
 | 
			
		||||
          $form[$config_change_type]['heading']['#value'] = format_plural(count($config_names), '@count renamed', '@count renamed');
 | 
			
		||||
          break;
 | 
			
		||||
      }
 | 
			
		||||
      $form[$config_change_type]['list'] = array(
 | 
			
		||||
| 
						 | 
				
			
			@ -213,10 +217,18 @@ class ConfigSync extends FormBase {
 | 
			
		|||
        '#header' => array('Name', 'Operations'),
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      foreach ($config_files as $config_file) {
 | 
			
		||||
      foreach ($config_names as $config_name) {
 | 
			
		||||
        if ($config_change_type == 'rename') {
 | 
			
		||||
          $names = $storage_comparer->extractRenameNames($config_name);
 | 
			
		||||
          $href = $this->urlGenerator->getPathFromRoute('config.diff', array('source_name' => $names['old_name'], 'target_name' => $names['new_name']));
 | 
			
		||||
          $config_name = $this->t('!source_name to !target_name', array('!source_name' => $names['old_name'], '!target_name' => $names['new_name']));
 | 
			
		||||
        }
 | 
			
		||||
        else {
 | 
			
		||||
          $href = $this->urlGenerator->getPathFromRoute('config.diff', array('source_name' => $config_name));
 | 
			
		||||
        }
 | 
			
		||||
        $links['view_diff'] = array(
 | 
			
		||||
          'title' => $this->t('View differences'),
 | 
			
		||||
          'href' => $this->urlGenerator->getPathFromRoute('config.diff', array('config_file' => $config_file)),
 | 
			
		||||
          'href' => $href,
 | 
			
		||||
          'attributes' => array(
 | 
			
		||||
            'class' => array('use-ajax'),
 | 
			
		||||
            'data-accepts' => 'application/vnd.drupal-modal',
 | 
			
		||||
| 
						 | 
				
			
			@ -226,7 +238,7 @@ class ConfigSync extends FormBase {
 | 
			
		|||
          ),
 | 
			
		||||
        );
 | 
			
		||||
        $form[$config_change_type]['list']['#rows'][] = array(
 | 
			
		||||
          'name' => $config_file,
 | 
			
		||||
          'name' => $config_name,
 | 
			
		||||
          'operations' => array(
 | 
			
		||||
            'data' => array(
 | 
			
		||||
              '#type' => 'operations',
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -84,6 +84,33 @@ class ConfigDiffTest extends DrupalUnitTestBase {
 | 
			
		|||
    $this->assertEqual($diff->edits[1]->type, 'add', 'The second item in the diff is an add.');
 | 
			
		||||
    $this->assertFalse($diff->edits[1]->orig, format_string("The key '%add_key' does not exist in active.", array('%add_key' => $add_key)));
 | 
			
		||||
    $this->assertEqual($diff->edits[1]->closing[0], $add_key . ': ' . $add_data, format_string("The staging value for key '%add_key' is '%add_data'.", array('%add_key' => $add_key, '%add_data' => $add_data)));
 | 
			
		||||
 | 
			
		||||
    // Test diffing a renamed config entity.
 | 
			
		||||
    $test_entity_id = $this->randomName();
 | 
			
		||||
    $test_entity = entity_create('config_test', array(
 | 
			
		||||
      'id' => $test_entity_id,
 | 
			
		||||
      'label' => $this->randomName(),
 | 
			
		||||
    ));
 | 
			
		||||
    $test_entity->save();
 | 
			
		||||
    $data = $active->read('config_test.dynamic.' . $test_entity_id);
 | 
			
		||||
    $staging->write('config_test.dynamic.' . $test_entity_id, $data);
 | 
			
		||||
    $config_name = 'config_test.dynamic.' . $test_entity_id;
 | 
			
		||||
    $diff = \Drupal::service('config.manager')->diff($active, $staging, $config_name, $config_name);
 | 
			
		||||
    // Prove the fields match.
 | 
			
		||||
    $this->assertEqual($diff->edits[0]->type, 'copy',  'The first item in the diff is a copy.');
 | 
			
		||||
    $this->assertEqual(count($diff->edits), 1, 'There is one item in the diff');
 | 
			
		||||
 | 
			
		||||
    // Rename the entity.
 | 
			
		||||
    $new_test_entity_id = $this->randomName();
 | 
			
		||||
    $test_entity->set('id', $new_test_entity_id);
 | 
			
		||||
    $test_entity->save();
 | 
			
		||||
 | 
			
		||||
    $diff = \Drupal::service('config.manager')->diff($active, $staging, 'config_test.dynamic.' . $new_test_entity_id, $config_name);
 | 
			
		||||
    $this->assertEqual($diff->edits[0]->type, 'change',  'The second item in the diff is a copy.');
 | 
			
		||||
    $this->assertEqual($diff->edits[0]->orig, array('id: ' . $new_test_entity_id));
 | 
			
		||||
    $this->assertEqual($diff->edits[0]->closing, array('id: ' . $test_entity_id));
 | 
			
		||||
    $this->assertEqual($diff->edits[1]->type, 'copy',  'The second item in the diff is a copy.');
 | 
			
		||||
    $this->assertEqual(count($diff->edits), 2, 'There are two items in the diff.');
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,168 @@
 | 
			
		|||
<?php
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @file
 | 
			
		||||
 * Contains \Drupal\config\Tests\ConfigImportRenameValidationTest.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
namespace Drupal\config\Tests;
 | 
			
		||||
 | 
			
		||||
use Drupal\Component\Utility\String;
 | 
			
		||||
use Drupal\Component\Utility\Unicode;
 | 
			
		||||
use Drupal\Component\Uuid\Php;
 | 
			
		||||
use Drupal\Core\Config\ConfigImporter;
 | 
			
		||||
use Drupal\Core\Config\ConfigImporterException;
 | 
			
		||||
use Drupal\Core\Config\StorageComparer;
 | 
			
		||||
use Drupal\simpletest\DrupalUnitTestBase;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Tests validating renamed configuration in a configuration import.
 | 
			
		||||
 */
 | 
			
		||||
class ConfigImportRenameValidationTest extends DrupalUnitTestBase {
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Config Importer object used for testing.
 | 
			
		||||
   *
 | 
			
		||||
   * @var \Drupal\Core\Config\ConfigImporter
 | 
			
		||||
   */
 | 
			
		||||
  protected $configImporter;
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Modules to enable.
 | 
			
		||||
   *
 | 
			
		||||
   * @var array
 | 
			
		||||
   */
 | 
			
		||||
  public static $modules = array('system', 'node', 'field', 'text', 'entity', 'config_test');
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * {@inheritdoc}
 | 
			
		||||
   */
 | 
			
		||||
  public static function getInfo() {
 | 
			
		||||
    return array(
 | 
			
		||||
      'name' => 'Configuration import rename validation',
 | 
			
		||||
      'description' => 'Tests validating renamed configuration in a configuration import.',
 | 
			
		||||
      'group' => 'Configuration',
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * {@inheritdoc}
 | 
			
		||||
   */
 | 
			
		||||
  public function setUp() {
 | 
			
		||||
    parent::setUp();
 | 
			
		||||
 | 
			
		||||
    $this->installSchema('system', 'config_snapshot');
 | 
			
		||||
    $this->installSchema('node', 'node');
 | 
			
		||||
 | 
			
		||||
    // Set up the ConfigImporter object for testing.
 | 
			
		||||
    $storage_comparer = new StorageComparer(
 | 
			
		||||
      $this->container->get('config.storage.staging'),
 | 
			
		||||
      $this->container->get('config.storage')
 | 
			
		||||
    );
 | 
			
		||||
    $this->configImporter = new ConfigImporter(
 | 
			
		||||
      $storage_comparer->createChangelist(),
 | 
			
		||||
      $this->container->get('event_dispatcher'),
 | 
			
		||||
      $this->container->get('config.manager'),
 | 
			
		||||
      $this->container->get('lock'),
 | 
			
		||||
      $this->container->get('config.typed'),
 | 
			
		||||
      $this->container->get('module_handler'),
 | 
			
		||||
      $this->container->get('theme_handler'),
 | 
			
		||||
      $this->container->get('string_translation')
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Tests configuration renaming validation.
 | 
			
		||||
   */
 | 
			
		||||
  public function testRenameValidation() {
 | 
			
		||||
    // Create a test entity.
 | 
			
		||||
    $test_entity_id = $this->randomName();
 | 
			
		||||
    $test_entity = entity_create('config_test', array(
 | 
			
		||||
      'id' => $test_entity_id,
 | 
			
		||||
      'label' => $this->randomName(),
 | 
			
		||||
    ));
 | 
			
		||||
    $test_entity->save();
 | 
			
		||||
    $uuid = $test_entity->uuid();
 | 
			
		||||
 | 
			
		||||
    // Stage the test entity and then delete it from the active storage.
 | 
			
		||||
    $active = $this->container->get('config.storage');
 | 
			
		||||
    $staging = $this->container->get('config.storage.staging');
 | 
			
		||||
    $this->copyConfig($active, $staging);
 | 
			
		||||
    $test_entity->delete();
 | 
			
		||||
 | 
			
		||||
    // Create a content type with a matching UUID in the active storage.
 | 
			
		||||
    $content_type = entity_create('node_type', array(
 | 
			
		||||
      'type' => Unicode::strtolower($this->randomName(16)),
 | 
			
		||||
      'name' => $this->randomName(),
 | 
			
		||||
      'uuid' => $uuid,
 | 
			
		||||
    ));
 | 
			
		||||
    $content_type->save();
 | 
			
		||||
 | 
			
		||||
    // Confirm that the staged configuration is detected as a rename since the
 | 
			
		||||
    // UUIDs match.
 | 
			
		||||
    $this->configImporter->reset();
 | 
			
		||||
    $expected = array(
 | 
			
		||||
      'node.type.' . $content_type->id() . '::config_test.dynamic.' . $test_entity_id,
 | 
			
		||||
    );
 | 
			
		||||
    $renames = $this->configImporter->getUnprocessedConfiguration('rename');
 | 
			
		||||
    $this->assertIdentical($expected, $renames);
 | 
			
		||||
 | 
			
		||||
    // Try to import the configuration. We expect an exception to be thrown
 | 
			
		||||
    // because the staged entity is of a different type.
 | 
			
		||||
    try {
 | 
			
		||||
      $this->configImporter->import();
 | 
			
		||||
      $this->fail('Expected ConfigImporterException thrown when a renamed configuration entity does not match the existing entity type.');
 | 
			
		||||
    }
 | 
			
		||||
    catch (ConfigImporterException $e) {
 | 
			
		||||
      $this->pass('Expected ConfigImporterException thrown when a renamed configuration entity does not match the existing entity type.');
 | 
			
		||||
      $expected = array(
 | 
			
		||||
        String::format('Entity type mismatch on rename. !old_type not equal to !new_type for existing configuration !old_name and staged configuration !new_name.', array('old_type' => 'node_type', 'new_type' => 'config_test', 'old_name' => 'node.type.' . $content_type->id(), 'new_name' => 'config_test.dynamic.' . $test_entity_id))
 | 
			
		||||
      );
 | 
			
		||||
      $this->assertIdentical($expected, $this->configImporter->getErrors());
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Tests configuration renaming validation for simple configuration.
 | 
			
		||||
   */
 | 
			
		||||
  public function testRenameSimpleConfigValidation() {
 | 
			
		||||
    $uuid = new Php();
 | 
			
		||||
    // Create a simple configuration with a UUID.
 | 
			
		||||
    $config = \Drupal::config('config_test.new');
 | 
			
		||||
    $uuid_value = $uuid->generate();
 | 
			
		||||
    $config->set('uuid', $uuid_value)->save();
 | 
			
		||||
 | 
			
		||||
    $active = $this->container->get('config.storage');
 | 
			
		||||
    $staging = $this->container->get('config.storage.staging');
 | 
			
		||||
    $this->copyConfig($active, $staging);
 | 
			
		||||
    $config->delete();
 | 
			
		||||
 | 
			
		||||
    // Create another simple configuration with the same UUID.
 | 
			
		||||
    $config = \Drupal::config('config_test.old');
 | 
			
		||||
    $config->set('uuid', $uuid_value)->save();
 | 
			
		||||
 | 
			
		||||
    // Confirm that the staged configuration is detected as a rename since the
 | 
			
		||||
    // UUIDs match.
 | 
			
		||||
    $this->configImporter->reset();
 | 
			
		||||
    $expected = array(
 | 
			
		||||
      'config_test.old::config_test.new'
 | 
			
		||||
    );
 | 
			
		||||
    $renames = $this->configImporter->getUnprocessedConfiguration('rename');
 | 
			
		||||
    $this->assertIdentical($expected, $renames);
 | 
			
		||||
 | 
			
		||||
    // Try to import the configuration. We expect an exception to be thrown
 | 
			
		||||
    // because the rename is for simple configuration.
 | 
			
		||||
    try {
 | 
			
		||||
      $this->configImporter->import();
 | 
			
		||||
      $this->fail('Expected ConfigImporterException thrown when simple configuration is renamed.');
 | 
			
		||||
    }
 | 
			
		||||
    catch (ConfigImporterException $e) {
 | 
			
		||||
      $this->pass('Expected ConfigImporterException thrown when simple configuration is renamed.');
 | 
			
		||||
      $expected = array(
 | 
			
		||||
        String::format('Rename operation for simple configuration. Existing configuration !old_name and staged configuration !new_name.', array('old_name' => 'config_test.old', 'new_name' => 'config_test.new'))
 | 
			
		||||
      );
 | 
			
		||||
      $this->assertIdentical($expected, $this->configImporter->getErrors());
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,122 @@
 | 
			
		|||
<?php
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @file
 | 
			
		||||
 * Contains \Drupal\node\Tests\NodeTypeRenameConfigImportTest.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
namespace Drupal\node\Tests;
 | 
			
		||||
 | 
			
		||||
use Drupal\Component\Utility\String;
 | 
			
		||||
use Drupal\Component\Utility\Unicode;
 | 
			
		||||
use Drupal\Core\Config\Entity\ConfigEntityStorage;
 | 
			
		||||
use Drupal\simpletest\WebTestBase;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Tests importing renamed node type via configuration synchronisation.
 | 
			
		||||
 */
 | 
			
		||||
class NodeTypeRenameConfigImportTest extends WebTestBase {
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Modules to enable.
 | 
			
		||||
   *
 | 
			
		||||
   * @var array
 | 
			
		||||
   */
 | 
			
		||||
  public static $modules = array('node', 'text', 'config');
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * {@inheritdoc}
 | 
			
		||||
   */
 | 
			
		||||
  public static function getInfo() {
 | 
			
		||||
    return array(
 | 
			
		||||
      'name' => 'Import renamed node type',
 | 
			
		||||
      'description' => 'Tests importing renamed node type via configuration synchronisation.',
 | 
			
		||||
      'group' => 'Configuration',
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * {@inheritdoc}
 | 
			
		||||
   */
 | 
			
		||||
  public function setUp() {
 | 
			
		||||
    parent::setUp();
 | 
			
		||||
    $this->web_user = $this->drupalCreateUser(array('synchronize configuration'));
 | 
			
		||||
    $this->drupalLogin($this->web_user);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Tests configuration renaming.
 | 
			
		||||
   */
 | 
			
		||||
  public function testConfigurationRename() {
 | 
			
		||||
    $content_type = entity_create('node_type', array(
 | 
			
		||||
      'type' => Unicode::strtolower($this->randomName(16)),
 | 
			
		||||
      'name' => $this->randomName(),
 | 
			
		||||
    ));
 | 
			
		||||
    $content_type->save();
 | 
			
		||||
    $staged_type = $content_type->type;
 | 
			
		||||
    $active = $this->container->get('config.storage');
 | 
			
		||||
    $staging = $this->container->get('config.storage.staging');
 | 
			
		||||
 | 
			
		||||
    $config_name = $content_type->getEntityType()->getConfigPrefix() . '.' . $content_type->id();
 | 
			
		||||
    // Emulate a staging operation.
 | 
			
		||||
    $this->copyConfig($active, $staging);
 | 
			
		||||
 | 
			
		||||
    // Change the machine name of the content type.
 | 
			
		||||
    $content_type->type = Unicode::strtolower($this->randomName(8));
 | 
			
		||||
    $content_type->save();
 | 
			
		||||
    $active_type = $content_type->type;
 | 
			
		||||
    $renamed_config_name = $content_type->getEntityType()->getConfigPrefix() . '.' . $content_type->id();
 | 
			
		||||
    $this->assertTrue($active->exists($renamed_config_name), 'The content type has the new name in the active store.');
 | 
			
		||||
    $this->assertFalse($active->exists($config_name), "The content type's old name does not exist active store.");
 | 
			
		||||
 | 
			
		||||
    $this->configImporter()->reset();
 | 
			
		||||
    $this->assertEqual(0, count($this->configImporter()->getUnprocessedConfiguration('create')), 'There are no configuration items to create.');
 | 
			
		||||
    $this->assertEqual(0, count($this->configImporter()->getUnprocessedConfiguration('delete')), 'There are no configuration items to delete.');
 | 
			
		||||
    $this->assertEqual(0, count($this->configImporter()->getUnprocessedConfiguration('update')), 'There are no configuration items to update.');
 | 
			
		||||
 | 
			
		||||
    // We expect that changing the machine name of the content type will
 | 
			
		||||
    // rename five configuration entities: the node type, the body field
 | 
			
		||||
    // instance, two entity form displays, and the entity view display.
 | 
			
		||||
    // @see \Drupal\node\Entity\NodeType::postSave()
 | 
			
		||||
    $expected = array(
 | 
			
		||||
      'node.type.' . $active_type . '::node.type.' . $staged_type,
 | 
			
		||||
      'entity.form_display.node.' . $active_type . '.default::entity.form_display.node.' . $staged_type . '.default',
 | 
			
		||||
      'entity.view_display.node.' . $active_type . '.default::entity.view_display.node.' . $staged_type . '.default',
 | 
			
		||||
      'entity.view_display.node.' . $active_type . '.teaser::entity.view_display.node.' . $staged_type . '.teaser',
 | 
			
		||||
      'field.instance.node.' . $active_type . '.body::field.instance.node.' . $staged_type . '.body',
 | 
			
		||||
    );
 | 
			
		||||
    $renames = $this->configImporter()->getUnprocessedConfiguration('rename');
 | 
			
		||||
    $this->assertIdentical($expected, $renames);
 | 
			
		||||
 | 
			
		||||
    $this->drupalGet('admin/config/development/configuration');
 | 
			
		||||
    foreach ($expected as $rename) {
 | 
			
		||||
      $names = $this->configImporter()->getStorageComparer()->extractRenameNames($rename);
 | 
			
		||||
      $this->assertText(String::format('!source_name to !target_name', array('!source_name' => $names['old_name'], '!target_name' => $names['new_name'])));
 | 
			
		||||
      // Test that the diff link is present for each renamed item.
 | 
			
		||||
      $href = \Drupal::urlGenerator()->getPathFromRoute('config.diff', array('source_name' => $names['old_name'], 'target_name' => $names['new_name']));
 | 
			
		||||
      $this->assertLinkByHref($href);
 | 
			
		||||
      $hrefs[$rename] = $href;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Ensure that the diff works for each renamed item.
 | 
			
		||||
    foreach ($hrefs as $rename => $href) {
 | 
			
		||||
      $this->drupalGet($href);
 | 
			
		||||
      $names = $this->configImporter()->getStorageComparer()->extractRenameNames($rename);
 | 
			
		||||
      $config_entity_type = \Drupal::service('config.manager')->getEntityTypeIdByName($names['old_name']);
 | 
			
		||||
      $entity_type = \Drupal::entityManager()->getDefinition($config_entity_type);
 | 
			
		||||
      $old_id = ConfigEntityStorage::getIDFromConfigName($names['old_name'], $entity_type->getConfigPrefix());
 | 
			
		||||
      $new_id = ConfigEntityStorage::getIDFromConfigName($names['new_name'], $entity_type->getConfigPrefix());
 | 
			
		||||
      $this->assertText('-' . $entity_type->getKey('id') . ': ' . $old_id);
 | 
			
		||||
      $this->assertText('+' . $entity_type->getKey('id') . ': ' . $new_id);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Run the import.
 | 
			
		||||
    $this->drupalPostForm('admin/config/development/configuration', array(), t('Import all'));
 | 
			
		||||
    $this->assertText(t('There are no configuration changes.'));
 | 
			
		||||
 | 
			
		||||
    $this->assertFalse(entity_load('node_type', $active_type), 'The content no longer exists with the old name.');
 | 
			
		||||
    $content_type = entity_load('node_type', $staged_type);
 | 
			
		||||
    $this->assertIdentical($staged_type, $content_type->type);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
		Reference in New Issue