/** * Virtual "sleep" that doesn't actually slow execution, only advances DBDateTime::now() * * @param int $minutes */ protected function sleep($minutes) { $now = DBDatetime::now(); $date = DateTime::createFromFormat('Y-m-d H:i:s', $now->getValue()); $date->modify("+{$minutes} minutes"); DBDatetime::set_mock_now($date->format('Y-m-d H:i:s')); }
public function testNowWithMockDate() { // Test setting $mockDate = '2001-12-31 22:10:59'; DBDatetime::set_mock_now($mockDate); $systemDatetime = DBField::create_field('Datetime', date('Y-m-d H:i:s')); $nowDatetime = DBDatetime::now(); $this->assertNotEquals($systemDatetime->Date(), $nowDatetime->Date()); $this->assertEquals($nowDatetime->getValue(), $mockDate); // Test clearing DBDatetime::clear_mock_now(); $systemDatetime = DBField::create_field('Datetime', date('Y-m-d H:i:s')); $nowDatetime = DBDatetime::now(); $this->assertEquals($systemDatetime->Date(), $nowDatetime->Date()); }
/** * Set a cookie * * @param string $name The name of the cookie * @param string $value The value for the cookie to hold * @param int $expiry The number of days until expiry; 0 indicates a cookie valid for the current session * @param string $path The path to save the cookie on (falls back to site base) * @param string $domain The domain to make the cookie available on * @param boolean $secure Can the cookie only be sent over SSL? * @param boolean $httpOnly Prevent the cookie being accessible by JS */ public function set($name, $value, $expiry = 90, $path = null, $domain = null, $secure = false, $httpOnly = true) { //are we setting or clearing a cookie? false values are reserved for clearing cookies (see PHP manual) $clear = false; if ($value === false || $value === '' || $expiry < 0) { $clear = true; $value = false; } //expiry === 0 is a special case where we set a cookie for the current user session if ($expiry !== 0) { //don't do the maths if we are clearing $expiry = $clear ? -1 : DBDatetime::now()->Format('U') + 86400 * $expiry; } //set the path up $path = $path ? $path : Director::baseURL(); //send the cookie $this->outputCookie($name, $value, $expiry, $path, $domain, $secure, $httpOnly); //keep our variables in check if ($clear) { unset($this->new[$name], $this->current[$name]); } else { $this->new[$name] = $this->current[$name] = $value; } }
/** * Writes all changes to this object to the database. * - It will insert a record whenever ID isn't set, otherwise update. * - All relevant tables will be updated. * - $this->onBeforeWrite() gets called beforehand. * - Extensions such as Versioned will ammend the database-write to ensure that a version is saved. * * @uses DataExtension->augmentWrite() * * @param boolean $showDebug Show debugging information * @param boolean $forceInsert Run INSERT command rather than UPDATE, even if record already exists * @param boolean $forceWrite Write to database even if there are no changes * @param boolean $writeComponents Call write() on all associated component instances which were previously * retrieved through {@link getComponent()}, {@link getComponents()} or * {@link getManyManyComponents()} (Default: false) * @return int The ID of the record * @throws ValidationException Exception that can be caught and handled by the calling function */ public function write($showDebug = false, $forceInsert = false, $forceWrite = false, $writeComponents = false) { $now = DBDatetime::now()->Rfc2822(); // Execute pre-write tasks $this->preWrite(); // Check if we are doing an update or an insert $isNewRecord = !$this->isInDB() || $forceInsert; // Check changes exist, abort if there are none $hasChanges = $this->updateChanges($isNewRecord); if ($hasChanges || $forceWrite || $isNewRecord) { // New records have their insert into the base data table done first, so that they can pass the // generated primary key on to the rest of the manipulation $baseTable = $this->baseTable(); $this->writeBaseRecord($baseTable, $now); // Write the DB manipulation for all changed fields $this->writeManipulation($baseTable, $now, $isNewRecord); // If there's any relations that couldn't be saved before, save them now (we have an ID here) $this->writeRelations(); $this->onAfterWrite(); $this->changed = array(); } else { if ($showDebug) { Debug::message("no changes for DataObject"); } // Used by DODs to clean up after themselves, eg, Versioned $this->invokeWithExtensions('onAfterSkippedWrite'); } // Ensure Created and LastEdited are populated if (!isset($this->record['Created'])) { $this->record['Created'] = $now; } $this->record['LastEdited'] = $now; // Write relations as necessary if ($writeComponents) { $this->writeComponents(true); } // Clears the cache for this object so get_one returns the correct object. $this->flushCache(); return $this->record['ID']; }
/** * Test that objects are correctly published recursively */ public function testRecursivePublish() { /** @var VersionedOwnershipTest_Subclass $parent */ $parent = $this->objFromFixture('VersionedOwnershipTest_Subclass', 'subclass1_published'); $parentID = $parent->ID; $banner1 = $this->objFromFixture('VersionedOwnershipTest_RelatedMany', 'relatedmany1_published'); $banner2 = $this->objFromFixture('VersionedOwnershipTest_RelatedMany', 'relatedmany2_published'); $banner2ID = $banner2->ID; // Modify, Add, and Delete banners on stage $banner1->Title = 'Renamed Banner 1'; $banner1->write(); $banner2->delete(); $banner4 = new VersionedOwnershipTest_RelatedMany(); $banner4->Title = 'New Banner'; $parent->Banners()->add($banner4); // Check state of objects before publish $oldLiveBanners = [['Title' => 'Related Many 1'], ['Title' => 'Related Many 2']]; $newBanners = [['Title' => 'Renamed Banner 1'], ['Title' => 'Related Many 3'], ['Title' => 'New Banner']]; $parentDraft = Versioned::get_by_stage('VersionedOwnershipTest_Subclass', Versioned::DRAFT)->byID($parentID); $this->assertDOSEquals($newBanners, $parentDraft->Banners()); $parentLive = Versioned::get_by_stage('VersionedOwnershipTest_Subclass', Versioned::LIVE)->byID($parentID); $this->assertDOSEquals($oldLiveBanners, $parentLive->Banners()); // On publishing of owner, all children should now be updated $now = DBDatetime::now(); DBDatetime::set_mock_now($now); // Lock 'now' to predictable time $parent->publishRecursive(); // Now check each object has the correct state $parentDraft = Versioned::get_by_stage('VersionedOwnershipTest_Subclass', Versioned::DRAFT)->byID($parentID); $this->assertDOSEquals($newBanners, $parentDraft->Banners()); $parentLive = Versioned::get_by_stage('VersionedOwnershipTest_Subclass', Versioned::LIVE)->byID($parentID); $this->assertDOSEquals($newBanners, $parentLive->Banners()); // Check that the deleted banner hasn't actually been deleted from the live stage, // but in fact has been unlinked. $banner2Live = Versioned::get_by_stage('VersionedOwnershipTest_RelatedMany', Versioned::LIVE)->byID($banner2ID); $this->assertEmpty($banner2Live->PageID); // Test that a changeset was created /** @var ChangeSet $changeset */ $changeset = ChangeSet::get()->sort('"ChangeSet"."ID" DESC')->first(); $this->assertNotEmpty($changeset); // Test that this changeset is inferred $this->assertTrue((bool) $changeset->IsInferred); $this->assertEquals("Generated by publish of 'Subclass 1' at " . $now->Nice(), $changeset->getTitle()); // Test that this changeset contains all items $this->assertDOSContains([['ObjectID' => $parent->ID, 'ObjectClass' => $parent->baseClass(), 'Added' => ChangeSetItem::EXPLICITLY], ['ObjectID' => $banner1->ID, 'ObjectClass' => $banner1->baseClass(), 'Added' => ChangeSetItem::IMPLICITLY], ['ObjectID' => $banner4->ID, 'ObjectClass' => $banner4->baseClass(), 'Added' => ChangeSetItem::IMPLICITLY]], $changeset->Changes()); // Objects that are unlinked should not need to be a part of the changeset $this->assertNotDOSContains([['ObjectID' => $banner2ID, 'ObjectClass' => $banner2->baseClass()]], $changeset->Changes()); }
/** * Returns true if date is today. * @return boolean */ public function IsToday() { return date('Y-m-d', strtotime($this->value)) == DBDatetime::now()->Format('Y-m-d'); }
/** * Generates a new login hash associated with a device * The device is assigned a globally unique device ID * The returned login hash stores the hashed token in the * database, for this device and this member * @param Member $member The logged in user * @return RememberLoginHash The generated login hash */ public static function generate(Member $member) { if (!$member->exists()) { return null; } if (static::config()->force_single_token) { RememberLoginHash::get()->filter('MemberID', $member->ID)->removeAll(); } /** @var RememberLoginHash $rememberLoginHash */ $rememberLoginHash = RememberLoginHash::create(); do { $deviceID = $rememberLoginHash->getNewDeviceID(); } while (RememberLoginHash::get()->filter('DeviceID', $deviceID)->count()); $rememberLoginHash->DeviceID = $deviceID; $rememberLoginHash->Hash = $rememberLoginHash->getNewHash($member); $rememberLoginHash->MemberID = $member->ID; $now = DBDatetime::now(); $expiryDate = new DateTime($now->Rfc2822()); $tokenExpiryDays = static::config()->token_expiry_days; $expiryDate->add(new DateInterval('P' . $tokenExpiryDays . 'D')); $rememberLoginHash->ExpiryDate = $expiryDate->format('Y-m-d H:i:s'); $rememberLoginHash->extend('onAfterGenerateToken'); $rememberLoginHash->write(); return $rememberLoginHash; }
/** * Export core. * * @param GridField $gridField * @return ArrayData */ public function generatePrintData(GridField $gridField) { $printColumns = $this->getPrintColumnsForGridField($gridField); $header = null; if ($this->printHasHeader) { $header = new ArrayList(); foreach ($printColumns as $field => $label) { $header->push(new ArrayData(array("CellString" => $label))); } } $items = $gridField->getManipulatedList(); $itemRows = new ArrayList(); /** @var DataObject $item */ foreach ($items->limit(null) as $item) { $itemRow = new ArrayList(); foreach ($printColumns as $field => $label) { $value = $gridField->getDataFieldValue($item, $field); if ($item->escapeTypeForField($field) != 'xml') { $value = Convert::raw2xml($value); } $itemRow->push(new ArrayData(array("CellString" => $value))); } $itemRows->push(new ArrayData(array("ItemRow" => $itemRow))); if ($item->hasMethod('destroy')) { $item->destroy(); } } $ret = new ArrayData(array("Title" => $this->getTitle($gridField), "Header" => $header, "ItemRows" => $itemRows, "Datetime" => DBDatetime::now(), "Member" => Member::currentUser())); return $ret; }
public function testChangePasswordFromLostPassword() { $admin = $this->objFromFixture('SilverStripe\\Security\\Member', 'test'); $admin->FailedLoginCount = 99; $admin->LockedOutUntil = DBDatetime::now()->Format('Y-m-d H:i:s'); $admin->write(); $this->assertNull($admin->AutoLoginHash, 'Hash is empty before lost password'); // Request new password by email $response = $this->get('Security/lostpassword'); $response = $this->post('Security/LostPasswordForm', array('Email' => '*****@*****.**')); $this->assertEmailSent('*****@*****.**'); // Load password link from email $admin = DataObject::get_by_id('SilverStripe\\Security\\Member', $admin->ID); $this->assertNotNull($admin->AutoLoginHash, 'Hash has been written after lost password'); // We don't have access to the token - generate a new token and hash pair. $token = $admin->generateAutologinTokenAndStoreHash(); // Check. $response = $this->get('Security/changepassword/?m=' . $admin->ID . '&t=' . $token); $this->assertEquals(302, $response->getStatusCode()); $this->assertEquals(Director::baseUrl() . 'Security/changepassword', $response->getHeader('Location')); // Follow redirection to form without hash in GET parameter $response = $this->get('Security/changepassword'); $changedResponse = $this->doTestChangepasswordForm('1nitialPassword', 'changedPassword'); $this->assertEquals($this->idFromFixture('SilverStripe\\Security\\Member', 'test'), $this->session()->inst_get('loggedInAs')); // Check if we can login with the new password $goodResponse = $this->doTestLoginForm('*****@*****.**', 'changedPassword'); $this->assertEquals(302, $goodResponse->getStatusCode()); $this->assertEquals($this->idFromFixture('SilverStripe\\Security\\Member', 'test'), $this->session()->inst_get('loggedInAs')); $admin = DataObject::get_by_id('SilverStripe\\Security\\Member', $admin->ID, false); $this->assertNull($admin->LockedOutUntil); $this->assertEquals(0, $admin->FailedLoginCount); }
/** * Tell this member that someone made a failed attempt at logging in as them. * This can be used to lock the user out temporarily if too many failed attempts are made. */ public function registerFailedLogin() { if (self::config()->lock_out_after_incorrect_logins) { // Keep a tally of the number of failed log-ins so that we can lock people out $this->FailedLoginCount = $this->FailedLoginCount + 1; if ($this->FailedLoginCount >= self::config()->lock_out_after_incorrect_logins) { $lockoutMins = self::config()->lock_out_delay_mins; $this->LockedOutUntil = date('Y-m-d H:i:s', DBDatetime::now()->Format('U') + $lockoutMins * 60); $this->FailedLoginCount = 0; } } $this->extend('registerFailedLogin'); $this->write(); }
/** * Publish this object and all owned objects to Live * * @return bool */ public function publishRecursive() { // Create a new changeset for this item and publish it $changeset = ChangeSet::create(); $changeset->IsInferred = true; $changeset->Name = _t('Versioned.INFERRED_TITLE', "Generated by publish of '{title}' at {created}", ['title' => $this->owner->Title, 'created' => DBDatetime::now()->Nice()]); $changeset->write(); $changeset->addObject($this->owner); return $changeset->publish(); }