public function testFileDelete()
 {
     Versioned::set_stage(Versioned::DRAFT);
     /** @var AssetControlExtensionTest_VersionedObject $object1 */
     $object1 = AssetControlExtensionTest_VersionedObject::get()->filter('Title', 'My object')->first();
     /** @var AssetControlExtensionTest_Object $object2 */
     $object2 = AssetControlExtensionTest_Object::get()->filter('Title', 'Unversioned')->first();
     /** @var AssetControlExtensionTest_ArchivedObject $object3 */
     $object3 = AssetControlExtensionTest_ArchivedObject::get()->filter('Title', 'Archived')->first();
     $this->assertTrue($object1->Download->exists());
     $this->assertTrue($object1->Header->exists());
     $this->assertTrue($object2->Image->exists());
     $this->assertTrue($object3->Header->exists());
     $this->assertEquals(AssetStore::VISIBILITY_PUBLIC, $object1->Download->getVisibility());
     $this->assertEquals(AssetStore::VISIBILITY_PUBLIC, $object1->Header->getVisibility());
     $this->assertEquals(AssetStore::VISIBILITY_PUBLIC, $object2->Image->getVisibility());
     $this->assertEquals(AssetStore::VISIBILITY_PUBLIC, $object3->Header->getVisibility());
     // Check live stage for versioned objects
     $object1Live = Versioned::get_one_by_stage('AssetControlExtensionTest_VersionedObject', 'Live', array('"ID"' => $object1->ID));
     $object3Live = Versioned::get_one_by_stage('AssetControlExtensionTest_ArchivedObject', 'Live', array('"ID"' => $object3->ID));
     $this->assertTrue($object1Live->Download->exists());
     $this->assertTrue($object1Live->Header->exists());
     $this->assertTrue($object3Live->Header->exists());
     $this->assertEquals(AssetStore::VISIBILITY_PUBLIC, $object1Live->Download->getVisibility());
     $this->assertEquals(AssetStore::VISIBILITY_PUBLIC, $object1Live->Header->getVisibility());
     $this->assertEquals(AssetStore::VISIBILITY_PUBLIC, $object3Live->Header->getVisibility());
     // Delete live records; Should cause versioned records to be protected
     $object1Live->deleteFromStage('Live');
     $object3Live->deleteFromStage('Live');
     $this->assertTrue($object1->Download->exists());
     $this->assertTrue($object1->Header->exists());
     $this->assertTrue($object3->Header->exists());
     $this->assertTrue($object1Live->Download->exists());
     $this->assertTrue($object1Live->Header->exists());
     $this->assertTrue($object3Live->Header->exists());
     $this->assertEquals(AssetStore::VISIBILITY_PROTECTED, $object1->Download->getVisibility());
     $this->assertEquals(AssetStore::VISIBILITY_PROTECTED, $object1->Header->getVisibility());
     $this->assertEquals(AssetStore::VISIBILITY_PROTECTED, $object3->Header->getVisibility());
     // Delete draft record; Should remove all records
     // Archived assets only should remain
     $object1->delete();
     $object2->delete();
     $object3->delete();
     $this->assertFalse($object1->Download->exists());
     $this->assertFalse($object1->Header->exists());
     $this->assertFalse($object2->Image->exists());
     $this->assertTrue($object3->Header->exists());
     $this->assertFalse($object1Live->Download->exists());
     $this->assertFalse($object1Live->Header->exists());
     $this->assertTrue($object3Live->Header->exists());
     $this->assertNull($object1->Download->getVisibility());
     $this->assertNull($object1->Header->getVisibility());
     $this->assertNull($object2->Image->getVisibility());
     $this->assertEquals(AssetStore::VISIBILITY_PROTECTED, $object3->Header->getVisibility());
 }
 /**
  * Checks all stages other than the current stage, and check the visibility
  * of assets attached to those records.
  *
  * @param AssetManipulationList $manipulation Set of manipulations to add assets to
  */
 protected function addAssetsFromOtherStages(AssetManipulationList $manipulation)
 {
     // Skip unversioned or unsaved assets
     if (!$this->isVersioned() || !$this->owner->isInDB()) {
         return;
     }
     // Unauthenticated member to use for checking visibility
     $baseClass = $this->owner->baseClass();
     $baseTable = $this->owner->baseTable();
     $filter = array("\"{$baseTable}\".\"ID\"" => $this->owner->ID);
     $stages = $this->owner->getVersionedStages();
     // {@see Versioned::getVersionedStages}
     foreach ($stages as $stage) {
         // Skip current stage; These should be handled explicitly
         if ($stage === Versioned::get_stage()) {
             continue;
         }
         // Check if record exists in this stage
         $record = Versioned::get_one_by_stage($baseClass, $stage, $filter);
         if (!$record) {
             continue;
         }
         // Check visibility of this record, and record all attached assets
         $state = $this->getRecordState($record);
         $this->addAssetsFromRecord($manipulation, $record, $state);
     }
 }
 public function testCanView()
 {
     $public1ID = $this->idFromFixture('VersionedTest_PublicStage', 'public1');
     $public2ID = $this->idFromFixture('VersionedTest_PublicViaExtension', 'public2');
     $privateID = $this->idFromFixture('VersionedTest_DataObject', 'page1');
     $singleID = $this->idFromFixture('VersionedTest_SingleStage', 'single');
     // Test that all (and only) public pages are viewable in stage mode
     Session::clear("loggedInAs");
     Versioned::set_stage(Versioned::DRAFT);
     $public1 = Versioned::get_one_by_stage('VersionedTest_PublicStage', 'Stage', array('"ID"' => $public1ID));
     $public2 = Versioned::get_one_by_stage('VersionedTest_PublicViaExtension', 'Stage', array('"ID"' => $public2ID));
     $private = Versioned::get_one_by_stage('VersionedTest_DataObject', 'Stage', array('"ID"' => $privateID));
     // Also test an object that has just a single-stage (eg. is only versioned)
     $single = Versioned::get_one_by_stage('VersionedTest_SingleStage', 'Stage', array('"ID"' => $singleID));
     $this->assertTrue($public1->canView());
     $this->assertTrue($public2->canView());
     $this->assertFalse($private->canView());
     $this->assertFalse($single->canView());
     // Adjusting the current stage should not allow objects loaded in stage to be viewable
     Versioned::set_stage(Versioned::LIVE);
     $this->assertTrue($public1->canView());
     $this->assertTrue($public2->canView());
     $this->assertFalse($private->canView());
     $this->assertFalse($single->canView());
     // Writing the private page to live should be fine though
     $private->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE);
     $privateLive = Versioned::get_one_by_stage('VersionedTest_DataObject', 'Live', array('"ID"' => $privateID));
     $this->assertTrue($private->canView());
     $this->assertTrue($privateLive->canView());
     // But if the private version becomes different to the live version, it's once again disallowed
     Versioned::set_stage(Versioned::DRAFT);
     $private->Title = 'Secret Title';
     $private->write();
     $this->assertFalse($private->canView());
     $this->assertTrue($privateLive->canView());
     // And likewise, viewing a live page (when mode is draft) should be ok
     Versioned::set_stage(Versioned::DRAFT);
     $this->assertFalse($private->canView());
     $this->assertTrue($privateLive->canView());
     // Logging in as admin should allow all permissions
     $this->logInWithPermission('ADMIN');
     Versioned::set_stage(Versioned::DRAFT);
     $this->assertTrue($public1->canView());
     $this->assertTrue($public2->canView());
     $this->assertTrue($private->canView());
     $this->assertTrue($single->canView());
 }
 /**
  * Move a database record from one stage to the other.
  *
  * @param int|string $fromStage Place to copy from.  Can be either a stage name or a version number.
  * @param string $toStage Place to copy to.  Must be a stage name.
  * @param bool $createNewVersion Set this to true to create a new version number.
  * By default, the existing version number will be copied over.
  */
 public function copyVersionToStage($fromStage, $toStage, $createNewVersion = false)
 {
     $owner = $this->owner;
     $owner->invokeWithExtensions('onBeforeVersionedPublish', $fromStage, $toStage, $createNewVersion);
     $baseClass = $owner->baseClass();
     $baseTable = $owner->baseTable();
     /** @var Versioned|DataObject $from */
     if (is_numeric($fromStage)) {
         $from = Versioned::get_version($baseClass, $owner->ID, $fromStage);
     } else {
         $owner->flushCache();
         $from = Versioned::get_one_by_stage($baseClass, $fromStage, array("\"{$baseTable}\".\"ID\" = ?" => $owner->ID));
     }
     if (!$from) {
         throw new InvalidArgumentException("Can't find {$baseClass}#{$owner->ID} in stage {$fromStage}");
     }
     $from->forceChange();
     if ($createNewVersion) {
         // Clear version to be automatically created on write
         $from->Version = null;
     } else {
         $from->migrateVersion($from->Version);
         // Mark this version as having been published at some stage
         $publisherID = isset(Member::currentUser()->ID) ? Member::currentUser()->ID : 0;
         $extTable = $this->extendWithSuffix($baseTable);
         DB::prepared_query("UPDATE \"{$extTable}_versions\"\n\t\t\t\tSET \"WasPublished\" = ?, \"PublisherID\" = ?\n\t\t\t\tWHERE \"RecordID\" = ? AND \"Version\" = ?", array(1, $publisherID, $from->ID, $from->Version));
     }
     // Change to new stage, write, and revert state
     $oldMode = Versioned::get_reading_mode();
     Versioned::set_stage($toStage);
     // Migrate stage prior to write
     $from->setSourceQueryParam('Versioned.mode', 'stage');
     $from->setSourceQueryParam('Versioned.stage', $toStage);
     $conn = DB::get_conn();
     if (method_exists($conn, 'allowPrimaryKeyEditing')) {
         $conn->allowPrimaryKeyEditing($baseTable, true);
         $from->write();
         $conn->allowPrimaryKeyEditing($baseTable, false);
     } else {
         $from->write();
     }
     $from->destroy();
     Versioned::set_reading_mode($oldMode);
     $owner->invokeWithExtensions('onAfterVersionedPublish', $fromStage, $toStage, $createNewVersion);
 }