diff --git a/application/controllers/BasketController.php b/application/controllers/BasketController.php index 9d8507aa..1a44a3ea 100644 --- a/application/controllers/BasketController.php +++ b/application/controllers/BasketController.php @@ -282,6 +282,14 @@ class BasketController extends ActionController } $currentExport = $current->export(); $fieldResolver->tweakTargetIds($currentExport); + + // Ignore originalId + if (isset($currentExport->originalId)) { + unset($currentExport->originalId); + } + if (isset($object->originalId)) { + unset($object->originalId); + } $hasChanged = Json::encode($currentExport) !== Json::encode($object); $table->addNameValueRow( $key, diff --git a/library/Director/DirectorObject/Automation/Basket.php b/library/Director/DirectorObject/Automation/Basket.php index 27a93a7b..f1991327 100644 --- a/library/Director/DirectorObject/Automation/Basket.php +++ b/library/Director/DirectorObject/Automation/Basket.php @@ -24,6 +24,8 @@ class Basket extends DbObject implements ExportInterface protected $chosenObjects = []; + protected $protectedFormerChosenObjects; + protected $defaultProperties = [ 'uuid' => null, 'basket_name' => null, @@ -118,6 +120,7 @@ class Basket extends DbObject implements ExportInterface if (empty($objects)) { $this->chosenObjects = []; } else { + $this->protectedFormerChosenObjects = $this->chosenObjects; $this->chosenObjects = []; foreach ((array) $objects as $type => $object) { $this->addObjects($type, $object); @@ -141,14 +144,18 @@ class Basket extends DbObject implements ExportInterface $objects = true; } elseif ($objects === null || $objects === 'IGNORE') { return; - } elseif ($objects === '[]' || is_array($objects)) { - if (isset($this->chosenObjects[$type])) { - if (! is_array($this->chosenObjects[$type])) { - $this->chosenObjects[$type] = []; - } - } else { + } elseif ($objects === '[]' || is_array($objects)) { + if (! is_array($this->chosenObjects[$type])) { $this->chosenObjects[$type] = []; } + if (isset($this->protectedFormerChosenObjects[$type])) { + if (is_array($this->protectedFormerChosenObjects[$type])) { + $this->chosenObjects[$type] = $this->protectedFormerChosenObjects; + } else { + $this->chosenObjects[$type] = []; + } + } + if ($objects === '[]') { $objects = []; } diff --git a/library/Director/DirectorObject/Automation/BasketSnapshot.php b/library/Director/DirectorObject/Automation/BasketSnapshot.php index 3db9c38d..050ddd2f 100644 --- a/library/Director/DirectorObject/Automation/BasketSnapshot.php +++ b/library/Director/DirectorObject/Automation/BasketSnapshot.php @@ -54,7 +54,6 @@ class BasketSnapshot extends DbObject 'ServiceSet', 'Notification', 'Dependency', - 'DataList', 'ImportSource', 'SyncRule', 'DirectorJob', @@ -213,53 +212,76 @@ class BasketSnapshot extends DbObject $db = $connection->getDbAdapter(); $db->beginTransaction(); $fieldResolver = new BasketSnapshotFieldResolver($all, $connection); + $this->restoreType($all, 'DataList', $fieldResolver, $connection, $replace); $fieldResolver->storeNewFields(); foreach ($this->restoreOrder as $typeName) { - if (isset($all->$typeName)) { - $objects = (array) $all->$typeName; - $class = static::getClassForType($typeName); - - $changed = []; - foreach ($objects as $key => $object) { - /** @var DbObject $new */ - $new = $class::import($object, $connection, $replace); - if ($new->hasBeenModified()) { - if ($new instanceof IcingaObject && $new->supportsImports()) { - /** @var ExportInterface $new */ - $changed[$new->getUniqueIdentifier()] = $new; - } else { - $new->store(); - // Linking fields right now, as we're not in $changed - if ($new instanceof IcingaObject) { - $fieldResolver->relinkObjectFields($new, $object); - } - } - } else { - // No modification on the object, still, fields might have - // been changed - if ($new instanceof IcingaObject) { - $fieldResolver->relinkObjectFields($new, $object); - } - } - $allObjects[spl_object_hash($new)] = $object; - } - - /** @var IcingaObject $object */ - foreach ($changed as $object) { - $this->recursivelyStore($object, $changed); - } - foreach ($changed as $key => $new) { - // Store related fields. As objects might have formerly been - // unstored, let's to it right here - if ($new instanceof IcingaObject) { - $fieldResolver->relinkObjectFields($new, $objects[$key]); - } - } - } + $this->restoreType($all, $typeName, $fieldResolver, $connection, $replace); } $db->commit(); } + /** + * @param $all + * @param $typeName + * @param BasketSnapshotFieldResolver $fieldResolver + * @param Db $connection + * @param $replace + * @throws \Icinga\Exception\NotFoundError + * @throws \Icinga\Module\Director\Exception\DuplicateKeyException + * @throws \Zend_Db_Adapter_Exception + */ + protected function restoreType( + & $all, + $typeName, + BasketSnapshotFieldResolver $fieldResolver, + Db $connection, + $replace + ) { + if (isset($all->$typeName)) { + $objects = (array) $all->$typeName; + } else { + return; + } + $class = static::getClassForType($typeName); + + $changed = []; + foreach ($objects as $key => $object) { + /** @var DbObject $new */ + $new = $class::import($object, $connection, $replace); + if ($new->hasBeenModified()) { + if ($new instanceof IcingaObject && $new->supportsImports()) { + /** @var ExportInterface $new */ + $changed[$new->getUniqueIdentifier()] = $new; + } else { + $new->store(); + // Linking fields right now, as we're not in $changed + if ($new instanceof IcingaObject) { + $fieldResolver->relinkObjectFields($new, $object); + } + } + } else { + // No modification on the object, still, fields might have + // been changed + if ($new instanceof IcingaObject) { + $fieldResolver->relinkObjectFields($new, $object); + } + } + $allObjects[spl_object_hash($new)] = $object; + } + + /** @var IcingaObject $object */ + foreach ($changed as $object) { + $this->recursivelyStore($object, $changed); + } + foreach ($changed as $key => $new) { + // Store related fields. As objects might have formerly been + // un-stored, let's to it right here + if ($new instanceof IcingaObject) { + $fieldResolver->relinkObjectFields($new, $objects[$key]); + } + } + } + /** * @param IcingaObject $object * @param $list diff --git a/library/Director/Objects/DirectorDatafield.php b/library/Director/Objects/DirectorDatafield.php index 27d6d67b..87bde7f4 100644 --- a/library/Director/Objects/DirectorDatafield.php +++ b/library/Director/Objects/DirectorDatafield.php @@ -93,12 +93,13 @@ class DirectorDatafield extends DbObjectWithSettings } if (isset($properties['settings']->datalist)) { + // Just try to load the list, import should fail if missing $list = DirectorDatalist::load( $properties['settings']->datalist, $db ); - $properties['settings']->datalist_id = $list->get('id'); - unset($properties['settings']->datalist); + } else { + $list = null; } $encoded = Json::encode($properties); @@ -113,6 +114,11 @@ class DirectorDatafield extends DbObjectWithSettings } } + if ($list) { + unset($properties['settings']->datalist); + $properties['settings']->datalist_id = $list->get('id'); + } + $dba = $db->getDbAdapter(); $query = $dba->select() ->from('director_datafield') diff --git a/library/Director/Objects/DirectorDatalist.php b/library/Director/Objects/DirectorDatalist.php index 93cdc918..a4120da5 100644 --- a/library/Director/Objects/DirectorDatalist.php +++ b/library/Director/Objects/DirectorDatalist.php @@ -21,6 +21,9 @@ class DirectorDatalist extends DbObject implements ExportInterface 'owner' => null ); + /** @var DirectorDatalistEntry[] */ + protected $storedEntries; + public function getUniqueIdentifier() { return $this->get('list_name'); @@ -38,7 +41,6 @@ class DirectorDatalist extends DbObject implements ExportInterface { $properties = (array) $plain; if (isset($properties['originalId'])) { - $id = $properties['originalId']; unset($properties['originalId']); } else { $id = null; @@ -56,13 +58,83 @@ class DirectorDatalist extends DbObject implements ExportInterface $object = static::create([], $db); } $object->setProperties($properties); - if ($id !== null) { - $object->reallySet('id', $id); - } return $object; } + public function setEntries($entries) + { + $existing = $this->getStoredEntries(); + + $new = []; + $seen = []; + $modified = false; + + foreach ($entries as $entry) { + $name = $entry->entry_name; + $entry = DirectorDatalistEntry::create((array) $entry); + $seen[$name] = true; + if (isset($existing[$name])) { + $existing[$name]->replaceWith($entry); + if (! $modified && $existing[$name]->hasBeenModified()) { + $modified = true; + } + } else { + $modified = true; + $new[] = $entry; + } + } + + foreach (array_keys($existing) as $key) { + if (! isset($seen[$key])) { + $existing[$key]->markForRemoval(); + $modified = true; + } + } + + foreach ($new as $entry) { + $existing[$entry->get('entry_name')] = $entry; + } + + if ($modified) { + $this->hasBeenModified = true; + } + + $this->storedEntries = $existing; + ksort($this->storedEntries); + + return $this; + } + + /** + * @throws DuplicateKeyException + */ + public function onStore() + { + if ($this->storedEntries) { + $db = $this->getConnection(); + $removedKeys = []; + $myId = $this->get('id'); + + foreach ($this->storedEntries as $key => $entry) { + if ($entry->shouldBeRemoved()) { + $entry->delete(); + $removedKeys[] = $key; + } else { + if (! $entry->hasBeenLoadedFromDb()) { + $entry->set('list_id', $myId); + } + $entry->set('list_id', $myId); + $entry->store($db); + } + } + + foreach ($removedKeys as $key) { + unset($this->storedEntries[$key]); + } + } + } + public function export() { $plain = (object) $this->getProperties(); @@ -70,8 +142,10 @@ class DirectorDatalist extends DbObject implements ExportInterface unset($plain->id); $plain->entries = []; - $entries = DirectorDatalistEntry::loadAllForList($this); - foreach ($entries as $key => $entry) { + foreach ($this->getStoredEntries() as $key => $entry) { + if ($entry->shouldBeRemoved()) { + continue; + } $plainEntry = (object) $entry->getProperties(); unset($plainEntry->list_id); @@ -80,4 +154,18 @@ class DirectorDatalist extends DbObject implements ExportInterface return $plain; } + + protected function getStoredEntries() + { + if ($this->storedEntries === null) { + if ($id = $this->get('id')) { + $this->storedEntries = DirectorDatalistEntry::loadAllForList($this); + ksort($this->storedEntries); + } else { + $this->storedEntries = []; + } + } + + return $this->storedEntries; + } }