From 380d6523c5db67041721077c58e06f54e46dfe0a Mon Sep 17 00:00:00 2001 From: Damian Mooyman Date: Wed, 5 Oct 2016 14:16:09 +1300 Subject: [PATCH] API Cleaned up versioned status checks --- Assets/File.php | 20 +++++---- ORM/Versioning/Versioned.php | 79 +++++++++++++++------------------- docs/en/04_Changelogs/4.0.0.md | 18 ++++++-- tests/model/VersionedTest.php | 73 +++++++++++++++++++++++++++++++ 4 files changed, 133 insertions(+), 57 deletions(-) diff --git a/Assets/File.php b/Assets/File.php index 0d2f006a0..8c719c43f 100644 --- a/Assets/File.php +++ b/Assets/File.php @@ -475,9 +475,9 @@ class File extends DataObject implements ShortcodeHandler, AssetContainer, Thumb $previewLink = Convert::raw2att($this->PreviewLink()); $image = ""; - + $statusTitle = $this->getStatusTitle(); - $statusFlag = ($statusTitle) ? "{$statusTitle}" : ''; + $statusFlag = ($statusTitle) ? "{$statusTitle}" : ''; $content = Tab::create('Main', HeaderField::create('TitleHeader', $this->Title, 1) @@ -515,18 +515,22 @@ class File extends DataObject implements ShortcodeHandler, AssetContainer, Thumb return $fields; } - - protected function getStatusTitle() { + + /** + * Get title for current file status + * + * @return string + */ + public function getStatusTitle() { $statusTitle = ''; - if ($this->getIsAddedToStage()) { + if ($this->isOnDraftOnly()) { $statusTitle = _t('File.DRAFT', 'Draft'); - } elseif ($this->getIsModifiedOnStage()) { + } elseif ($this->isModifiedOnDraft()) { $statusTitle = _t('File.MODIFIED', 'Modified'); } - return $statusTitle; } - + /** * Returns a category based on the file extension. * This can be useful when grouping files by type, diff --git a/ORM/Versioning/Versioned.php b/ORM/Versioning/Versioned.php index 94de2679b..68775b04b 100644 --- a/ORM/Versioning/Versioned.php +++ b/ORM/Versioning/Versioned.php @@ -1774,9 +1774,8 @@ class Versioned extends DataExtension implements TemplateGlobalProvider { public function stagesDiffer($stage1, $stage2) { $table1 = $this->baseTable($stage1); $table2 = $this->baseTable($stage2); - - $owner = $this->owner; - if(!is_numeric($owner->ID)) { + $id = $this->owner->ID ?: $this->owner->OldID; + if (!$id) { return true; } @@ -1788,7 +1787,7 @@ class Versioned extends DataExtension implements TemplateGlobalProvider { "SELECT CASE WHEN \"$table1\".\"Version\"=\"$table2\".\"Version\" THEN 1 ELSE 0 END FROM \"$table1\" INNER JOIN \"$table2\" ON \"$table1\".\"ID\" = \"$table2\".\"ID\" AND \"$table1\".\"ID\" = ?", - array($owner->ID) + array($id) )->value(); return !$stagesAreEqual; @@ -2292,8 +2291,8 @@ class Versioned extends DataExtension implements TemplateGlobalProvider { * @return bool */ public function isPublished() { - $owner = $this->owner; - if(!$owner->isInDB()) { + $id = $this->owner->ID ?: $this->owner->OldID; + if(!$id) { return false; } @@ -2305,78 +2304,68 @@ class Versioned extends DataExtension implements TemplateGlobalProvider { $table = $this->baseTable(static::LIVE); $result = DB::prepared_query( "SELECT COUNT(*) FROM \"{$table}\" WHERE \"{$table}\".\"ID\" = ?", - array($owner->ID) + array($id) ); return (bool)$result->value(); } + /** + * Check if page doesn't exist on any stage, but used to be + * + * @return bool + */ + public function isArchived() { + $id = $this->owner->ID ?: $this->owner->OldID; + return $id && !$this->isOnDraft() && !$this->isPublished(); + } + /** * Check if this record exists on the draft stage * * @return bool */ public function isOnDraft() { - $owner = $this->owner; - if(!$owner->isInDB()) { + $id = $this->owner->ID ?: $this->owner->OldID; + if(!$id) { return false; } $table = $this->baseTable(); $result = DB::prepared_query( "SELECT COUNT(*) FROM \"{$table}\" WHERE \"{$table}\".\"ID\" = ?", - array($owner->ID) + array($id) ); return (bool)$result->value(); } - + /** * Compares current draft with live version, and returns true if no draft version of this page exists but the page * is still published (eg, after triggering "Delete from draft site" in the CMS). * * @return bool */ - public function getIsDeletedFromStage() { - if(!$this->owner->ID) return true; - - $stageVersion = Versioned::get_versionnumber_by_stage($this->owner->class, Versioned::DRAFT, $this->owner->ID); - - // Return true for both completely deleted pages and for pages just deleted from stage - return !($stageVersion); + public function isOnLiveOnly() { + return $this->isPublished() && !$this->isOnDraft(); } - - /** - * Compares current draft with live version, and returns true if these versions differ, meaning there have been - * unpublished changes to the draft site. - * - * @return bool - */ - public function getIsModifiedOnStage() { - // New unsaved pages could be never be published - if(!$this->owner->ID) return false; - $stageVersion = Versioned::get_versionnumber_by_stage($this->owner->class, 'Stage', $this->owner->ID); - $liveVersion = Versioned::get_versionnumber_by_stage($this->owner->class, 'Live', $this->owner->ID); - - $isModified = ($stageVersion && $stageVersion != $liveVersion); - $this->owner->extend('updateIsModifiedOnStage', $isModified); - - return $isModified; - } - /** * Compares current draft with live version, and returns true if no live version exists, meaning the page was never * published. * * @return bool */ - public function getIsAddedToStage() { - // New unsaved pages could be never be published - if(!$this->owner->ID) return false; - - $stageVersion = Versioned::get_versionnumber_by_stage($this->owner->class, 'Stage', $this->owner->ID); - $liveVersion = Versioned::get_versionnumber_by_stage($this->owner->class, 'Live', $this->owner->ID); - - return ($stageVersion && !$liveVersion); + public function isOnDraftOnly() { + return $this->isOnDraft() && !$this->isPublished(); + } + + /** + * Compares current draft with live version, and returns true if these versions differ, meaning there have been + * unpublished changes to the draft site. + * + * @return bool + */ + public function isModifiedOnDraft() { + return $this->isOnDraft() && $this->stagesDiffer(Versioned::DRAFT, Versioned::LIVE); } /** diff --git a/docs/en/04_Changelogs/4.0.0.md b/docs/en/04_Changelogs/4.0.0.md index 83eca01c3..dc2269c47 100644 --- a/docs/en/04_Changelogs/4.0.0.md +++ b/docs/en/04_Changelogs/4.0.0.md @@ -887,10 +887,18 @@ A very small number of methods were chosen for deprecation, and will be removed * Any extension declared via `versionableExtensions` config on Versioned dataobject must now `VersionableExtension` interface at a minimum. `Translatable` has been removed from default `versionableExtensions` -* A lot of standard versioned API has been refactored from `SiteTree` into `Versioned` extension. Now - all versioned DataObjects have `canPublish()`, `canArchive()`, `canUnpublish()`, `doPublish()`, `doArchive()` - `doUnpublish()`, `isPublished()` and `isonDraft()` out of the box. However, `do*()` methods will no longer - automatically check `can*()` permissions, and must be done by usercode before invocation. +* A lot of standard versioned API has been refactored from `SiteTree` into `Versioned` extension. + * All versioned DataObjects have `canPublish()`, `canArchive()`, `canUnpublish()` permission checks + * All versioned Dataobjects have `doPublish()`, `doArchive()`, `doPublish()`, and `doUnpublish()` actions. + However, `do*()` methods will no longer automatically check `can*()` permissions, and must be done by + usercode before invocation. + * `SiteTree::getIsAddedToStage()` moved to `Versioned` and renamed to `isOnDraftOnly()` + * `SiteTre::getIsModifiedOnStage()` moved to `Versioned` and renamed to `isModifiedOnDraft()` + * `SiteTree::isPublished()` moved to `Versioned`. + * `SiteTree::getExistsOnLive()` removed in favour of `isPublished()` + * `isOnDraft()` added to `Versioned`. + * `isArchived()` added to `Versioned`. + * `isOnLiveOnly()` added to `Versioned`. * `ChangeSet` and `ChangeSetItem` have been added for batch publishing of versioned dataobjects. * `DataObject.table_name` config can now be used to customise the database table for any record. * `DataObjectSchema` class added to assist with mapping between classes and tables. @@ -903,6 +911,8 @@ A very small number of methods were chosen for deprecation, and will be removed * Removed `UpgradeSiteTreePermissionSchemaTask` * Removed `EncryptAllPasswordsTask` * Removed `DBString::LimitWordCountXML()` method. Use `LimitWordCount` for XML safe version. +* Removed `SiteTree::getExistsOnLive()`. Use `isPublished()` instead. +* Removed `SiteTree::getIsDeletedFromStage()`. Use `isOnDraft()` instead (inverse case). ### Filesystem API diff --git a/tests/model/VersionedTest.php b/tests/model/VersionedTest.php index 4919089ef..fa5c25065 100644 --- a/tests/model/VersionedTest.php +++ b/tests/model/VersionedTest.php @@ -1,6 +1,8 @@ assertEquals(2, $version->Version); $this->assertEquals($record->Title, $version->Title); } + + + + public function testStageStates() { + // newly created page + $createdPage = new VersionedTest_DataObject(); + $createdPage->write(); + $this->assertTrue($createdPage->isOnDraft()); + $this->assertFalse($createdPage->isPublished()); + $this->assertTrue($createdPage->isOnDraftOnly()); + $this->assertTrue($createdPage->isModifiedOnDraft()); + + // published page + $publishedPage = new VersionedTest_DataObject(); + $publishedPage->write(); + $publishedPage->copyVersionToStage('Stage','Live'); + $this->assertTrue($publishedPage->isOnDraft()); + $this->assertTrue($publishedPage->isPublished()); + $this->assertFalse($publishedPage->isOnDraftOnly()); + $this->assertFalse($publishedPage->isOnLiveOnly()); + $this->assertFalse($publishedPage->isModifiedOnDraft()); + + // published page, deleted from stage + $deletedFromDraftPage = new VersionedTest_DataObject(); + $deletedFromDraftPage->write(); + $deletedFromDraftPage->copyVersionToStage('Stage','Live'); + $deletedFromDraftPage->deleteFromStage('Stage'); + $this->assertFalse($deletedFromDraftPage->isArchived()); + $this->assertFalse($deletedFromDraftPage->isOnDraft()); + $this->assertTrue($deletedFromDraftPage->isPublished()); + $this->assertFalse($deletedFromDraftPage->isOnDraftOnly()); + $this->assertTrue($deletedFromDraftPage->isOnLiveOnly()); + $this->assertFalse($deletedFromDraftPage->isModifiedOnDraft()); + + // published page, deleted from live + $deletedFromLivePage = new VersionedTest_DataObject(); + $deletedFromLivePage->write(); + $deletedFromLivePage->copyVersionToStage('Stage','Live'); + $deletedFromLivePage->deleteFromStage('Live'); + $this->assertFalse($deletedFromLivePage->isArchived()); + $this->assertTrue($deletedFromLivePage->isOnDraft()); + $this->assertFalse($deletedFromLivePage->isPublished()); + $this->assertTrue($deletedFromLivePage->isOnDraftOnly()); + $this->assertFalse($deletedFromLivePage->isOnLiveOnly()); + $this->assertTrue($deletedFromLivePage->isModifiedOnDraft()); + + // published page, deleted from both stages + $deletedFromAllStagesPage = new VersionedTest_DataObject(); + $deletedFromAllStagesPage->write(); + $deletedFromAllStagesPage->copyVersionToStage('Stage','Live'); + $deletedFromAllStagesPage->doArchive(); + $this->assertTrue($deletedFromAllStagesPage->isArchived()); + $this->assertFalse($deletedFromAllStagesPage->isOnDraft()); + $this->assertFalse($deletedFromAllStagesPage->isPublished()); + $this->assertFalse($deletedFromAllStagesPage->isOnDraftOnly()); + $this->assertFalse($deletedFromAllStagesPage->isOnLiveOnly()); + $this->assertFalse($deletedFromAllStagesPage->isModifiedOnDraft()); + + // published page, modified + $modifiedOnDraftPage = new VersionedTest_DataObject(); + $modifiedOnDraftPage->write(); + $modifiedOnDraftPage->copyVersionToStage('Stage','Live'); + $modifiedOnDraftPage->Content = 'modified'; + $modifiedOnDraftPage->write(); + $this->assertFalse($modifiedOnDraftPage->isArchived()); + $this->assertTrue($modifiedOnDraftPage->isOnDraft()); + $this->assertTrue($modifiedOnDraftPage->isPublished()); + $this->assertFalse($modifiedOnDraftPage->isOnDraftOnly()); + $this->assertFalse($modifiedOnDraftPage->isOnLiveOnly()); + $this->assertTrue($modifiedOnDraftPage->isModifiedOnDraft()); + } }