/**
  * Compares current draft with live version,
  * and returns true if no live version exists,
  * meaning the page was never published.
  *
  * @return boolean
  */
 public function getIsAddedToStage()
 {
     // new unsaved pages could be never be published
     if ($this->isNew()) {
         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 testRateLimiting()
 {
     // Re-enable locking just for this test
     Config::inst()->update('VersionFeed\\Filters\\RateLimitFilter', 'lock_timeout', 20);
     Config::inst()->update('VersionFeed\\Filters\\CachedContentFilter', 'cache_enabled', true);
     $page1 = $this->createPageWithChanges(array('PublicHistory' => true, 'Title' => 'Page1'));
     $page2 = $this->createPageWithChanges(array('PublicHistory' => true, 'Title' => 'Page2'));
     // Artifically set cache lock
     Config::inst()->update('VersionFeed\\Filters\\RateLimitFilter', 'lock_byuserip', false);
     $cache = SS_Cache::factory('VersionFeed_Controller');
     $cache->setOption('automatic_serialization', true);
     $cache->save(time() + 10, \VersionFeed\Filters\RateLimitFilter::CACHE_PREFIX);
     // Test normal hit
     $response = $this->get($page1->RelativeLink('changes'));
     $this->assertEquals(429, $response->getStatusCode());
     $this->assertGreaterThan(0, $response->getHeader('Retry-After'));
     $response = $this->get($page2->RelativeLink('changes'));
     $this->assertEquals(429, $response->getStatusCode());
     $this->assertGreaterThan(0, $response->getHeader('Retry-After'));
     // Test page specific lock
     Config::inst()->update('VersionFeed\\Filters\\RateLimitFilter', 'lock_bypage', true);
     $key = implode('_', array('changes', $page1->ID, Versioned::get_versionnumber_by_stage('SiteTree', 'Live', $page1->ID, false)));
     $key = \VersionFeed\Filters\RateLimitFilter::CACHE_PREFIX . '_' . md5($key);
     $cache->save(time() + 10, $key);
     $response = $this->get($page1->RelativeLink('changes'));
     $this->assertEquals(429, $response->getStatusCode());
     $this->assertGreaterThan(0, $response->getHeader('Retry-After'));
     $response = $this->get($page2->RelativeLink('changes'));
     $this->assertEquals(200, $response->getStatusCode());
     Config::inst()->update('VersionFeed\\Filters\\RateLimitFilter', 'lock_bypage', false);
     // Test rate limit hit by IP
     Config::inst()->update('VersionFeed\\Filters\\RateLimitFilter', 'lock_byuserip', true);
     $_SERVER['HTTP_CLIENT_IP'] = '127.0.0.1';
     $cache->save(time() + 10, \VersionFeed\Filters\RateLimitFilter::CACHE_PREFIX . '_' . md5('127.0.0.1'));
     $response = $this->get($page1->RelativeLink('changes'));
     $this->assertEquals(429, $response->getStatusCode());
     $this->assertGreaterThan(0, $response->getHeader('Retry-After'));
     // Test rate limit doesn't hit other IP
     $_SERVER['HTTP_CLIENT_IP'] = '127.0.0.20';
     $cache->save(time() + 10, \VersionFeed\Filters\RateLimitFilter::CACHE_PREFIX . '_' . md5('127.0.0.1'));
     $response = $this->get($page1->RelativeLink('changes'));
     $this->assertEquals(200, $response->getStatusCode());
     // Restore setting
     Config::inst()->update('VersionFeed\\Filters\\RateLimitFilter', 'lock_byuserip', false);
     Config::inst()->update('VersionFeed\\Filters\\RateLimitFilter', 'lock_timeout', 0);
     Config::inst()->update('VersionFeed\\Filters\\CachedContentFilter', 'cache_enabled', false);
 }
 public function updateStatusFlags(&$flags)
 {
     static $prepop = true;
     if ($prepop) {
         Versioned::prepopulate_versionnumber_cache('SiteTree', 'Stage');
         Versioned::prepopulate_versionnumber_cache('SiteTree', 'Live');
         $prepop = false;
     }
     $stageVersion = intval(Versioned::get_versionnumber_by_stage('SiteTree', 'Stage', $this->owner->ID));
     $liveVersion = intval(Versioned::get_versionnumber_by_stage('SiteTree', 'Live', $this->owner->ID));
     if ($liveVersion == 0) {
         $flags['status_draft'] = '';
     } elseif ($stageVersion > $liveVersion) {
         $flags['status_draft_published'] = '';
     } else {
         $flags['status_published'] = '';
     }
 }
 /**
  * Determines if the current record is deleted from stage
  * @returns boolean
  */
 public function recordIsDeletedFromStage()
 {
     // for SiteTree records
     if ($this->owner->hasMethod('getIsDeletedFromStage')) {
         return $this->owner->IsDeletedFromStage;
     }
     if (!$this->owner->record->checkVersioned()) {
         return false;
     }
     if (!$this->owner->record->isInDB()) {
         return true;
     }
     $class = $this->owner->record->class;
     $stageVersion = Versioned::get_versionnumber_by_stage($class, 'Stage', $this->owner->record->ID);
     // Return true for both completely deleted pages and for pages just deleted from stage
     return !$stageVersion;
 }
 /**
  * Return if this form has been modified on the stage site and not published.
  * this is used on the workflow module and for a couple highlighting things
  *
  * @return boolean
  */
 public function getIsModifiedOnStage()
 {
     // new unsaved pages could be never be published
     if ($this->isNew()) {
         return false;
     }
     $stageVersion = Versioned::get_versionnumber_by_stage('UserDefinedForm', 'Stage', $this->ID);
     $liveVersion = Versioned::get_versionnumber_by_stage('UserDefinedForm', 'Live', $this->ID);
     $isModified = $stageVersion && $stageVersion != $liveVersion;
     if (!$isModified) {
         if ($this->Fields()) {
             foreach ($this->Fields() as $field) {
                 if ($field->getIsModifiedOnStage()) {
                     $isModified = true;
                     break;
                 }
             }
         }
     }
     return $isModified;
 }
Exemplo n.º 6
0
 /**
  * Generate the CMS fields from the fields from the original page.
  */
 public function getCMSFields()
 {
     $fields = parent::getCMSFields();
     // Setup the linking to the original page.
     $copyContentFromField = new TreeDropdownField("CopyContentFromID", _t('VirtualPage.CHOOSE', "Linked Page"), "SiteTree");
     // filter doesn't let you select children of virtual pages as as source page
     //$copyContentFromField->setFilterFunction(create_function('$item', 'return !($item instanceof VirtualPage);'));
     // Setup virtual fields
     if ($virtualFields = $this->getVirtualFields()) {
         $roTransformation = new ReadonlyTransformation();
         foreach ($virtualFields as $virtualField) {
             if ($fields->dataFieldByName($virtualField)) {
                 $fields->replaceField($virtualField, $fields->dataFieldByName($virtualField)->transform($roTransformation));
             }
         }
     }
     $msgs = array();
     $fields->addFieldToTab("Root.Main", $copyContentFromField, "Title");
     // Create links back to the original object in the CMS
     if ($this->CopyContentFrom()->exists()) {
         $link = "<a class=\"cmsEditlink\" href=\"admin/pages/edit/show/{$this->CopyContentFromID}\">" . _t('VirtualPage.EditLink', 'edit') . "</a>";
         $msgs[] = _t('VirtualPage.HEADERWITHLINK', "This is a virtual page copying content from \"{title}\" ({link})", array('title' => $this->CopyContentFrom()->Title, 'link' => $link));
     } else {
         $msgs[] = _t('VirtualPage.HEADER', "This is a virtual page");
         $msgs[] = _t('SITETREE.VIRTUALPAGEWARNING', 'Please choose a linked page and save first in order to publish this page');
     }
     if ($this->CopyContentFromID && !Versioned::get_versionnumber_by_stage('SiteTree', 'Live', $this->CopyContentFromID)) {
         $msgs[] = _t('SITETREE.VIRTUALPAGEDRAFTWARNING', 'Please publish the linked page in order to publish the virtual page');
     }
     $fields->addFieldToTab("Root.Main", new LiteralField('VirtualPageMessage', '<div class="message notice">' . implode('. ', $msgs) . '.</div>'), 'CopyContentFromID');
     return $fields;
 }
 public function testPublishCreateNewVersion()
 {
     $page1 = $this->objFromFixture('VersionedTest_DataObject', 'page1');
     $page1->Content = 'orig';
     $page1->write();
     $firstVersion = $page1->Version;
     $page1->publish('Stage', '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->publish('Stage', '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');
 }
 /**
  * checks if records is changed on stage
  * @return boolean
  */
 public function getIsModifiedOnStage()
 {
     // new unsaved fields could be never be published
     if ($this->isNew()) {
         return false;
     }
     $stageVersion = Versioned::get_versionnumber_by_stage('EditableFormField', 'Stage', $this->ID);
     $liveVersion = Versioned::get_versionnumber_by_stage('EditableFormField', 'Live', $this->ID);
     return $stageVersion && $stageVersion != $liveVersion;
 }
 /**
  * Returns true if is page is publishable by anyone at all
  * Return false if the source page isn't published yet.
  * 
  * Note that isPublishable doesn't affect ete from live, only publish.
  */
 public function isPublishable()
 {
     // No source
     if (!$this->CopyContentFrom() || !$this->CopyContentFrom()->ID) {
         return false;
     }
     // Unpublished source
     if (!Versioned::get_versionnumber_by_stage('SiteTree', 'Live', $this->CopyContentFromID)) {
         return false;
     }
     // Default - publishable
     return true;
 }
	/**
	 * Versioned objects needs special handling, we can not
	 * handle them reliably as long as they exist on a stage.
	 * 
	 * @param DataObject $object The object which will be
	 * checked.
	 * 
	 * @return boolean True if there are other versions for the
	 * object which prevents relations from being handled.
	 */
	public static function version_exist(DataObject $object) {
		if (Object::has_extension($object->ClassName, 'Versioned')) {
			$ID = (int)$object->ID;
			foreach (self::version_stages($object) as $stage) {
				if (Versioned::get_versionnumber_by_stage($object->ClassName, $stage, $ID))
					return true;
			}
		}
		return false;
	}
Exemplo n.º 11
0
 /**
  * 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
     $mode = $this->owner->getSourceQueryParam("Versioned.mode");
     $stage = $this->owner->getSourceQueryParam("Versioned.stage");
     if ($mode === 'stage' && $stage === static::get_live_stage()) {
         return true;
     }
     // Bypass if site is unsecured
     if (Session::get('unsecuredDraftSite')) {
         return true;
     }
     // If there are less than 2 stages, we can exit early since comparing stages is not needed
     if (count($this->stages) < 2) {
         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($this->owner->class, $this->liveStage, $this->owner->ID);
     if ($latestVersion == $this->owner->Version) {
         // Even if this is loaded from a non-live stage, this is the live version
         return true;
     }
     // Extend versioned behaviour
     $extended = $this->owner->extendedCan('canViewNonLive', $member);
     if ($extended !== null) {
         return (bool) $extended;
     }
     // Fall back to default permission check
     $permissions = Config::inst()->get($this->owner->class, 'non_live_permissions', Config::FIRST_SET);
     $check = Permission::checkMember($member, $permissions);
     return (bool) $check;
 }
 /**
  * 
  * @param Member $member
  * @return boolean
  */
 public function canView($member = null)
 {
     if (!$member || !is_a($member, 'Member') || is_numeric($member)) {
         $member = Member::currentUserID();
     }
     // admin override
     if ($member && Permission::checkMember($member, array("ADMIN", "SITETREE_VIEW_ALL"))) {
         return true;
     }
     // make sure we were loaded off an allowed stage
     // Were we definitely loaded directly off Live during our query?
     $fromLive = true;
     foreach (array('mode' => 'stage', 'stage' => 'live') as $param => $match) {
         $fromLive = $fromLive && strtolower((string) $this->getCachedSourceQueryParam("Versioned.{$param}")) == $match;
     }
     if (!$fromLive && !Session::get('unsecuredDraftSite') && !Permission::checkMember($member, array('CMS_ACCESS_LeftAndMain', 'CMS_ACCESS_CMSMain', 'VIEW_DRAFT_CONTENT'))) {
         // 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
         if (Versioned::get_versionnumber_by_stage($this->ClassName, 'Live', $this->ID) != $this->Version) {
             return false;
         }
     }
     // Orphaned pages (in the current stage) are unavailable, except for admins via the CMS
     if ($this->isOrphaned()) {
         return false;
     }
     // Standard mechanism for accepting permission changes from extensions
     $extended = $this->extendedCan('canView', $member);
     if ($extended !== null) {
         return $extended;
     }
     // check for empty spec
     if (!$this->CanViewType || $this->CanViewType == 'Anyone') {
         return true;
     }
     // check for inherit
     if ($this->CanViewType == 'Inherit') {
         if ($parent = $this->getParent()) {
             return $parent->canView($member);
         } else {
             return $this->getSiteConfig()->canView($member);
         }
     }
     // check for any logged-in users
     if ($this->CanViewType == 'LoggedInUsers' && $member) {
         return true;
     }
     // check for specific groups
     if ($member && is_numeric($member)) {
         $member = DataObject::get_by_id('Member', $member);
     }
     if ($this->CanViewType == 'OnlyTheseUsers' && $member && $member->inGroups($this->ViewerGroups)) {
         return true;
     }
     return false;
 }
Exemplo n.º 13
0
	/**
	 * Compares current draft with live version,
	 * and returns true if no live version exists,
	 * meaning the page was never published.
	 * 
	 * @return boolean
	 */
	public function getIsAddedToStage() {
		// new unsaved pages could be never be published
		if($this->isNew()) return false;
		
		$stageVersion = Versioned::get_versionnumber_by_stage('SiteTree', 'Stage', $this->ID);
		$liveVersion =	Versioned::get_versionnumber_by_stage('SiteTree', 'Live', $this->ID);

		return ($stageVersion && !$liveVersion);
	}
 public function hasApprovedVersion()
 {
     $live_version = Versioned::get_versionnumber_by_stage($this->owner->class, 'Live', $this->owner->ID);
     return (bool) $live_version;
 }
 private static function isObjectModifiedOnStage($object)
 {
     // new unsaved fields could be never be published
     if (self::isNew($object)) {
         return false;
     }
     $stageVersion = Versioned::get_versionnumber_by_stage($object->ClassName, 'Stage', $object->ID);
     $liveVersion = Versioned::get_versionnumber_by_stage($object->ClassName, 'Live', $object->ID);
     return $stageVersion && $stageVersion != $liveVersion || $object->hasMethod('getIsModifiedOnStage') && $object->getIsModifiedOnStage(false);
 }