diff --git a/src/RecursivePublishable.php b/src/RecursivePublishable.php index 014ba676..d7eb81a3 100644 --- a/src/RecursivePublishable.php +++ b/src/RecursivePublishable.php @@ -12,6 +12,7 @@ use SilverStripe\ORM\FieldType\DBDatetime; use SilverStripe\ORM\Queries\SQLUpdate; use SilverStripe\Model\List\SS_List; +use SilverStripe\UserForms\Model\EditableCustomRule; /** * Provides owns / owned_by and recursive publishing API for all objects. @@ -63,37 +64,48 @@ public function publishRecursive() $owner = $this->owner; // get the last published version - $original = null; + $liveOwner = null; if ($owner->hasExtension(Versioned::class) && $owner->isPublished()) { - $original = Versioned::get_by_stage($owner->baseClass(), Versioned::LIVE) + $liveOwner = Versioned::get_by_stage($owner->baseClass(), Versioned::LIVE) ->byID($owner->ID); } - $owner->invokeWithExtensions('onBeforePublishRecursive', $original); - - // Create a new changeset for this item and publish it - $changeset = ChangeSet::create(); - $changeset->IsInferred = true; - $changeset->Name = _t( - __CLASS__ . '.INFERRED_TITLE', - "Generated by publish of '{title}' at {created}", - [ - 'title' => $owner->Title, - 'created' => DBDatetime::now()->Nice() - ] - ); + $owner->invokeWithExtensions('onBeforePublishRecursive', $liveOwner); + + $draftObjs = $this->getRecursivelyOwnedVersionedDataObjectsByStage($owner, Versioned::DRAFT); + $liveObjs = $this->getRecursivelyOwnedVersionedDataObjectsByStage($liveOwner, Versioned::LIVE); + $liveOnlyKeys = array_diff(array_keys($liveObjs), array_keys($draftObjs)); + + // Delete any records that were in live but not in draft + foreach ($liveOnlyKeys as $key) { + if (array_key_exists($key, $liveObjs)) { + /** @var DataObject|Versioned $liveObj */ + $liveObj = $liveObjs[$key]; + $liveObj->doUnpublish(); + // $liveObj->suppressDeletedVersion(function () use ($liveObj) { + // $liveObj->doUnpublish(); + // }); + // $liveObj->deleteFromStage(Versioned::LIVE); + // $liveObj->suppressDeletedVersion(function () use ($liveObj) { + // $liveObj->deleteFromStage(Versioned::LIVE); + // }); + // $liveObj->isOnLiveOnly + } + } - $changeset->write(); - $changeset->addObject($owner); + // Recursively publish owned DataObjects, starting with most nested ones + foreach (array_reverse($draftObjs) as $obj) { + $obj->publishSingle(); + } - $result = $changeset->publish(true); - if (!$result) { - return $result; + // Finally, publish this object + if ($owner->hasExtension(Versioned::class)) { + $owner->publishSingle(); } $owner->invokeWithExtensions('onAfterPublishRecursive', $original); - return $result; + return true; }); } @@ -470,4 +482,28 @@ protected function onAfterDelete(): void { RecursiveStagesService::reset(); } + + /** + * Returns a map of {dataClass}.{ID} => DataObject of DataObjectsions that are: + * - Recursively owned by the current object + * - Have the Versioned extension + * - Exist in the same stage as the current object + */ + private function getRecursivelyOwnedVersionedDataObjectsByStage(?DataObject $owner, string $stage): array + { + if (!$owner) { + return []; + } + $objs = Versioned::withVersionedMode(function () use ($owner, $stage) { + Versioned::set_stage($stage); + return array_filter( + $owner->findOwned(true)->toArray(), + fn(DataObject $obj) => $obj->hasExtension(Versioned::class) + ); + }); + return array_combine( + array_map(fn(DataObject $obj) => get_class($obj) . '.' . $obj->ID, $objs), + $objs + ); + } }