Issue #2069373 by swentel, yched: Race conditions on import if CUD on ConfigEntity A triggers changes in ConfigEntity B.

8.0.x
webchick 2013-10-30 21:30:41 -07:00
parent e0d8e66bf4
commit 9e72283378
7 changed files with 74 additions and 8 deletions

View File

@ -34,6 +34,14 @@ abstract class ConfigEntityBase extends Entity implements ConfigEntityInterface
*/
public $status = TRUE;
/**
* Whether the config is being created, updated or deleted through the
* import process.
*
* @var bool
*/
private $isSyncing = FALSE;
/**
* Overrides Entity::__construct().
*/
@ -90,21 +98,21 @@ abstract class ConfigEntityBase extends Entity implements ConfigEntityInterface
}
/**
* Implements \Drupal\Core\Config\Entity\ConfigEntityInterface::enable().
* {@inheritdoc}
*/
public function enable() {
return $this->setStatus(TRUE);
}
/**
* Implements \Drupal\Core\Config\Entity\ConfigEntityInterface::disable().
* {@inheritdoc}
*/
public function disable() {
return $this->setStatus(FALSE);
}
/**
* Implements \Drupal\Core\Config\Entity\ConfigEntityInterface::setStatus().
* {@inheritdoc}
*/
public function setStatus($status) {
$this->status = (bool) $status;
@ -112,14 +120,28 @@ abstract class ConfigEntityBase extends Entity implements ConfigEntityInterface
}
/**
* Implements \Drupal\Core\Config\Entity\ConfigEntityInterface::status().
* {@inheritdoc}
*/
public function status() {
return !empty($this->status);
}
/**
* Overrides Entity::createDuplicate().
* {@inheritdoc}
*/
public function setSyncing($syncing) {
$this->isSyncing = $syncing;
}
/**
* {@inheritdoc}
*/
public function isSyncing() {
return $this->isSyncing;
}
/**
* {@inheritdoc}
*/
public function createDuplicate() {
$duplicate = parent::createDuplicate();

View File

@ -59,6 +59,14 @@ interface ConfigEntityInterface extends EntityInterface {
*/
public function setStatus($status);
/**
* Sets the status of the isSyncing flag.
*
* @param bool $status
* The status of the sync flag.
*/
public function setSyncing($status);
/**
* Returns whether the configuration entity is enabled.
*
@ -75,6 +83,14 @@ interface ConfigEntityInterface extends EntityInterface {
*/
public function status();
/**
* Returns whether the configuration entity is created, updated or deleted
* through the import process.
*
* @return bool
*/
public function isSyncing();
/**
* Returns the value of a property.
*

View File

@ -484,6 +484,7 @@ class ConfigStorageController extends EntityStorageControllerBase {
*/
public function importCreate($name, Config $new_config, Config $old_config) {
$entity = $this->create($new_config->get());
$entity->setSyncing(TRUE);
$entity->save();
return TRUE;
}
@ -504,6 +505,7 @@ class ConfigStorageController extends EntityStorageControllerBase {
public function importUpdate($name, Config $new_config, Config $old_config) {
$id = static::getIDFromConfigName($name, $this->entityInfo['config_prefix']);
$entity = $this->load($id);
$entity->setSyncing(TRUE);
$entity->original = clone $entity;
foreach ($old_config->get() as $property => $value) {
@ -534,6 +536,7 @@ class ConfigStorageController extends EntityStorageControllerBase {
public function importDelete($name, Config $new_config, Config $old_config) {
$id = static::getIDFromConfigName($name, $this->entityInfo['config_prefix']);
$entity = $this->load($id);
$entity->setSyncing(TRUE);
$entity->delete();
return TRUE;
}

View File

@ -170,8 +170,9 @@ class NodeType extends ConfigEntityBase implements NodeTypeInterface {
entity_invoke_bundle_hook('create', 'node', $this->id());
// Unless disabled, automatically create a Body field for new node types.
if ($this->get('create_body')) {
// Create a body if the create_body property is true and we're not in
// the syncing process.
if ($this->get('create_body') && !$this->isSyncing()) {
$label = $this->get('create_body_label');
node_add_body_field($this, $label);
}

View File

@ -78,6 +78,7 @@ class NodeImportCreateTest extends DrupalUnitTestBase {
// Check that the content type was created.
$node_type = entity_load('node_type', $node_type_id);
$this->assertTrue($node_type, 'Import node type from staging was created.');
$this->assertFalse(field_info_instance('node', 'body', $node_type_id));
}
}

View File

@ -138,6 +138,14 @@ class ViewUI implements ViewStorageInterface {
'reorder-displays' => '\Drupal\views_ui\Form\Ajax\ReorderDisplays',
);
/**
* Whether the config is being created, updated or deleted through the
* import process.
*
* @var bool
*/
private $isSyncing = FALSE;
/**
* Constructs a View UI object.
*
@ -187,6 +195,20 @@ class ViewUI implements ViewStorageInterface {
return '<div class="message">' . t("Click on an item to edit that item's details.") . '</div>';
}
/**
* {@inheritdoc}
*/
public function setSyncing($syncing) {
$this->isSyncing = $syncing;
}
/**
* {@inheritdoc}
*/
public function isSyncing() {
return $this->isSyncing;
}
/**
* Basic submit handler applicable to all 'standard' forms.
*

View File

@ -45,7 +45,8 @@ class ViewUIObjectTest extends UnitTestCase {
// EntityInterface::isNew() is missing from the list of methods, because it
// calls id(), which breaks the ->expect($this->once()) call. Call it later.
if ($reflection_method->getName() != 'isNew') {
// EntityInterface::isSyncing() is only called during syncing process.
if ($reflection_method->getName() != 'isNew' && $reflection_method->getName() != 'isSyncing') {
if (count($reflection_method->getParameters()) == 0) {
$method_args[$reflection_method->getName()] = array();
}