public function testPublish()
 {
     $this->publishAllFixtures();
     $base = $this->objFromFixture(ChangeSetTest_Base::class, 'base');
     $baseID = $base->ID;
     $baseBefore = $base->Version;
     $end1 = $this->objFromFixture(ChangeSetTest_End::class, 'end1');
     $end1ID = $end1->ID;
     $end1Before = $end1->Version;
     // Create a new changest
     $changeset = new ChangeSet();
     $changeset->write();
     $changeset->addObject($base);
     $changeset->addObject($end1);
     // Make a lot of changes
     // - ChangeSetTest_Base.base modified
     // - ChangeSetTest_End.end1 deleted
     // - new ChangeSetTest_Mid added
     $base->Foo = 343;
     $base->write();
     $baseAfter = $base->Version;
     $midNew = new ChangeSetTest_Mid();
     $midNew->Bar = 39;
     $midNew->write();
     $midNewID = $midNew->ID;
     $midNewAfter = $midNew->Version;
     $end1->delete();
     $changeset->addObject($midNew);
     // Publish
     $this->logInWithPermission('ADMIN');
     $this->assertTrue($changeset->canPublish());
     $this->assertTrue($changeset->isSynced());
     $changeset->publish();
     $this->assertEquals(ChangeSet::STATE_PUBLISHED, $changeset->State);
     // Check each item has the correct before/after version applied
     $baseChange = $changeset->Changes()->filter(['ObjectClass' => ChangeSetTest_Base::class, 'ObjectID' => $baseID])->first();
     $this->assertEquals((int) $baseBefore, (int) $baseChange->VersionBefore);
     $this->assertEquals((int) $baseAfter, (int) $baseChange->VersionAfter);
     $this->assertEquals((int) $baseChange->VersionBefore + 1, (int) $baseChange->VersionAfter);
     $this->assertEquals((int) $baseChange->VersionAfter, (int) Versioned::get_versionnumber_by_stage(ChangeSetTest_Base::class, Versioned::LIVE, $baseID));
     $end1Change = $changeset->Changes()->filter(['ObjectClass' => ChangeSetTest_End::class, 'ObjectID' => $end1ID])->first();
     $this->assertEquals((int) $end1Before, (int) $end1Change->VersionBefore);
     $this->assertEquals(0, (int) $end1Change->VersionAfter);
     $this->assertEquals(0, (int) Versioned::get_versionnumber_by_stage(ChangeSetTest_End::class, Versioned::LIVE, $end1ID));
     $midNewChange = $changeset->Changes()->filter(['ObjectClass' => ChangeSetTest_Mid::class, 'ObjectID' => $midNewID])->first();
     $this->assertEquals(0, (int) $midNewChange->VersionBefore);
     $this->assertEquals((int) $midNewAfter, (int) $midNewChange->VersionAfter);
     $this->assertEquals((int) $midNewAfter, (int) Versioned::get_versionnumber_by_stage(ChangeSetTest_Mid::class, Versioned::LIVE, $midNewID));
     // Test trying to re-publish is blocked
     $this->setExpectedException('BadMethodCallException', "ChangeSet can't be published if it has been already published or reverted.");
     $changeset->publish();
 }
 public function testPublishCreateNewVersion()
 {
     $page1 = $this->objFromFixture('VersionedTest_DataObject', 'page1');
     $page1->Content = 'orig';
     $page1->write();
     $firstVersion = $page1->Version;
     $page1->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE, false);
     $this->assertEquals($firstVersion, $page1->Version, 'publish() with $createNewVersion=FALSE does not create a new version');
     $page1->Content = 'changed';
     $page1->write();
     $secondVersion = $page1->Version;
     $this->assertTrue($firstVersion < $secondVersion, 'write creates new version');
     $page1->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE, true);
     $thirdVersion = Versioned::get_latest_version('VersionedTest_DataObject', $page1->ID)->Version;
     $liveVersion = Versioned::get_versionnumber_by_stage('VersionedTest_DataObject', 'Live', $page1->ID);
     $stageVersion = Versioned::get_versionnumber_by_stage('VersionedTest_DataObject', 'Stage', $page1->ID);
     $this->assertTrue($secondVersion < $thirdVersion, 'publish() with $createNewVersion=TRUE creates a new version');
     $this->assertEquals($liveVersion, $thirdVersion, 'publish() with $createNewVersion=TRUE publishes to live');
     $this->assertEquals($stageVersion, $secondVersion, 'publish() with $createNewVersion=TRUE does not affect stage');
 }
 /**
  * Publish this item, then close it.
  *
  * Note: Unlike Versioned::doPublish() and Versioned::doUnpublish, this action is not recursive.
  */
 public function publish()
 {
     // Logical checks prior to publish
     if (!$this->canPublish()) {
         throw new Exception("The current member does not have permission to publish this ChangeSetItem.");
     }
     if ($this->VersionBefore || $this->VersionAfter) {
         throw new BadMethodCallException("This ChangeSetItem has already been published");
     }
     // Record state changed
     $this->VersionAfter = Versioned::get_versionnumber_by_stage($this->ObjectClass, Versioned::DRAFT, $this->ObjectID, false);
     $this->VersionBefore = Versioned::get_versionnumber_by_stage($this->ObjectClass, Versioned::LIVE, $this->ObjectID, false);
     switch ($this->getChangeType()) {
         case static::CHANGE_NONE:
             break;
         case static::CHANGE_DELETED:
             // Non-recursive delete
             $object = $this->getObjectInStage(Versioned::LIVE);
             $object->deleteFromStage(Versioned::LIVE);
             break;
         case static::CHANGE_MODIFIED:
         case static::CHANGE_CREATED:
             // Non-recursive publish
             $object = $this->getObjectInStage(Versioned::DRAFT);
             $object->publishSingle();
             break;
     }
     $this->write();
 }
 /**
  * Determine if there are any additional restrictions on this object for the given reading version.
  *
  * Override this in a subclass to customise any additional effect that Versioned applies to canView.
  *
  * This is expected to be called by canView, and thus is only responsible for denying access if
  * the default canView would otherwise ALLOW access. Thus it should not be called in isolation
  * as an authoritative permission check.
  *
  * This has the following extension points:
  *  - canViewDraft is invoked if Mode = stage and Stage = stage
  *  - canViewArchived is invoked if Mode = archive
  *
  * @param Member $member
  * @return bool False is returned if the current viewing mode denies visibility
  */
 public function canViewVersioned($member = null)
 {
     // Bypass when live stage
     $owner = $this->owner;
     $mode = $owner->getSourceQueryParam("Versioned.mode");
     $stage = $owner->getSourceQueryParam("Versioned.stage");
     if ($mode === 'stage' && $stage === static::LIVE) {
         return true;
     }
     // Bypass if site is unsecured
     if (Session::get('unsecuredDraftSite')) {
         return true;
     }
     // Bypass if record doesn't have a live stage
     if (!$this->hasStages()) {
         return true;
     }
     // If we weren't definitely loaded from live, and we can't view non-live content, we need to
     // check to make sure this version is the live version and so can be viewed
     $latestVersion = Versioned::get_versionnumber_by_stage($owner->class, static::LIVE, $owner->ID);
     if ($latestVersion == $owner->Version) {
         // Even if this is loaded from a non-live stage, this is the live version
         return true;
     }
     // Extend versioned behaviour
     $extended = $owner->extendedCan('canViewNonLive', $member);
     if ($extended !== null) {
         return (bool) $extended;
     }
     // Fall back to default permission check
     $permissions = Config::inst()->get($owner->class, 'non_live_permissions', Config::FIRST_SET);
     $check = Permission::checkMember($member, $permissions);
     return (bool) $check;
 }