diff --git a/core/Object.php b/core/Object.php index a9ab5adce..b59d8e6bb 100755 --- a/core/Object.php +++ b/core/Object.php @@ -640,7 +640,7 @@ abstract class Object { $this->class = get_class($this); // Don't bother checking some classes that should never be extended - static $notExtendable = array('Object', 'ViewableData', 'DataObject', 'RequestHandler'); + static $notExtendable = array('Object', 'ViewableData', 'RequestHandler'); if($extensionClasses = ClassInfo::ancestry($this->class)) foreach($extensionClasses as $class) { if(in_array($class, $notExtendable)) continue; diff --git a/core/model/DataObject.php b/core/model/DataObject.php index 6688cfcc5..2a45fc1da 100755 --- a/core/model/DataObject.php +++ b/core/model/DataObject.php @@ -506,8 +506,6 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity * Called by Object::__construct() once per class. */ function defineMethods() { - if($this->class == 'DataObject') return; - parent::defineMethods(); // Define the extra db fields - this is only necessary for extensions added in the @@ -521,6 +519,8 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity } } + if($this->class == 'DataObject') return; + // Set up accessors for joined items if($manyMany = $this->many_many()) { foreach($manyMany as $relationship => $class) { diff --git a/core/model/Database.php b/core/model/Database.php index 25850989c..47f14de79 100755 --- a/core/model/Database.php +++ b/core/model/Database.php @@ -720,6 +720,48 @@ abstract class SS_Database { return $text; } + /** + * Function to return an SQL datetime expression that can be used with the adapter in use + * used for querying a datetime in a certain format + * @param string $date to be formated, can be either 'now', literal datetime like '1973-10-14 10:30:00' or field name, e.g. '"SiteTree"."Created"' + * @param string $format to be used, supported specifiers: + * %Y = Year (four digits) + * %m = Month (01..12) + * %d = Day (01..31) + * %H = Hour (00..23) + * %i = Minutes (00..59) + * %s = Seconds (00..59) + * %U = unix timestamp, can only be used on it's own + * @return string SQL datetime expression to query for a formatted datetime + */ + abstract function formattedDatetimeClause($date, $format); + + /** + * Function to return an SQL datetime expression that can be used with the adapter in use + * used for querying a datetime addition + * @param string $date, can be either 'now', literal datetime like '1973-10-14 10:30:00' or field name, e.g. '"SiteTree"."Created"' + * @param string $interval to be added, use the format [sign][integer] [qualifier], e.g. -1 Day, +15 minutes, +1 YEAR + * supported qualifiers: + * - years + * - months + * - days + * - hours + * - minutes + * - seconds + * This includes the singular forms as well + * @return string SQL datetime expression to query for a datetime (YYYY-MM-DD hh:mm:ss) which is the result of the addition + */ + abstract function datetimeIntervalClause($date, $interval); + + /** + * Function to return an SQL datetime expression that can be used with the adapter in use + * used for querying a datetime substraction + * @param string $date1, can be either 'now', literal datetime like '1973-10-14 10:30:00' or field name, e.g. '"SiteTree"."Created"' + * @param string $date2 to be substracted of $date1, can be either 'now', literal datetime like '1973-10-14 10:30:00' or field name, e.g. '"SiteTree"."Created"' + * @return string SQL datetime expression to query for the interval between $date1 and $date2 in seconds which is the result of the substraction + */ + abstract function datetimeDifferenceClause($date1, $date2); + } /** diff --git a/core/model/SiteTree.php b/core/model/SiteTree.php index 748328731..d53523750 100755 --- a/core/model/SiteTree.php +++ b/core/model/SiteTree.php @@ -295,7 +295,9 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid */ public static function page_type_classes() { $classes = ClassInfo::getValidSubClasses(); - if($baseClassIndex = array_search('SiteTree', $classes)) unset($classes[$baseClassIndex]); + + $baseClassIndex = array_search('SiteTree', $classes); + if($baseClassIndex !== FALSE) unset($classes[$baseClassIndex]); $kill_ancestors = array(); @@ -1396,9 +1398,6 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid // This will have the affect of preserving the versioning $this->migrateVersion($this->Version); } - - - parent::onBeforeWrite(); } function syncLinkTracking() { diff --git a/security/MemberAuthenticator.php b/security/MemberAuthenticator.php index 6c30c8c12..e74cb1599 100755 --- a/security/MemberAuthenticator.php +++ b/security/MemberAuthenticator.php @@ -42,7 +42,12 @@ class MemberAuthenticator extends Authenticator { "Member", "\"" . Member::get_unique_identifier_field() . "\" = '$SQL_user' AND \"Password\" IS NOT NULL" ); - $result = ($member) ? $member->checkPassword($RAW_data['Password']) : false; + + if($member) { + $result = $member->checkPassword($RAW_data['Password']); + } else { + $result = new ValidationResult(false, _t('Member.ERRORWRONGCRED')); + } if($member && !$result->valid()) { $member->registerFailedLogin(); diff --git a/tests/DataObjectDecoratorTest.php b/tests/DataObjectDecoratorTest.php index 71f73af52..8b00dc813 100644 --- a/tests/DataObjectDecoratorTest.php +++ b/tests/DataObjectDecoratorTest.php @@ -10,6 +10,10 @@ class DataObjectDecoratorTest extends SapphireTest { 'DataObjectDecoratorTest_MyObject', ); + protected $requiredExtensions = array( + 'DataObject' => array( 'DataObjectDecoratorTest_AppliedToDO' ), + ); + function testOneToManyAssociationWithDecorator() { // Fails in RestfulServerTest // Error: Object::__call() Method 'RelatedObjects' not found in class 'RestfulServerTest_Comment' @@ -141,6 +145,17 @@ class DataObjectDecoratorTest extends SapphireTest { $this->assertNotNull($member->dbObject('Website')); $this->assertType('Varchar', $member->dbObject('Website')); } + + function testDecoratorCanBeAppliedToDataObject() { + $do = new DataObject(); + $mo = new DataObjectDecoratorTest_MyObject(); + + $this->assertTrue($do->hasMethod('testMethodApplied')); + $this->assertTrue($mo->hasMethod('testMethodApplied')); + + $this->assertEquals("hello world", $mo->testMethodApplied()); + $this->assertEquals("hello world", $do->testMethodApplied()); + } } class DataObjectDecoratorTest_Member extends DataObject implements TestOnly { @@ -287,6 +302,12 @@ class DataObjectDecoratorTest_Faves extends DataObjectDecorator implements TestO } } +class DataObjectDecoratorTest_AppliedToDO extends DataObjectDecorator implements TestOnly { + public function testMethodApplied() { + return "hello world"; + } +} + DataObject::add_extension('DataObjectDecoratorTest_MyObject', 'DataObjectDecoratorTest_Ext1'); DataObject::add_extension('DataObjectDecoratorTest_MyObject', 'DataObjectDecoratorTest_Ext2'); DataObject::add_extension('DataObjectDecoratorTest_MyObject', 'DataObjectDecoratorTest_Faves'); diff --git a/tests/SiteTreeTest.php b/tests/SiteTreeTest.php index 32101f147..b362154b0 100755 --- a/tests/SiteTreeTest.php +++ b/tests/SiteTreeTest.php @@ -685,6 +685,12 @@ class SiteTreeTest extends SapphireTest { } + function testPageTypeClasses() { + $classes = SiteTree::page_type_classes(); + $this->assertNotContains('SiteTree', $classes, 'Page types do not include base class'); + $this->assertContains('Page', $classes, 'Page types do contain subclasses'); + } + } /**#@+ diff --git a/tests/security/SecurityTest.php b/tests/security/SecurityTest.php index 00ef762a2..ccb14bfb3 100644 --- a/tests/security/SecurityTest.php +++ b/tests/security/SecurityTest.php @@ -247,6 +247,9 @@ class SecurityTest extends FunctionalTest { $this->assertTrue(is_object($attempt)); $this->assertEquals($attempt->Status, 'Failure'); $this->assertEquals($attempt->Email, 'wronguser@silverstripe.com'); + $this->assertNotNull( + $this->loginErrorMessage(), 'An invalid email returns a message.' + ); } function testSuccessfulLoginAttempts() {