diff --git a/core/Object.php b/core/Object.php index 9157f53d9..f49585d6c 100755 --- a/core/Object.php +++ b/core/Object.php @@ -160,6 +160,9 @@ abstract class Object { * If any extra values are discovered, they are then merged with the default PHP static values, or in some cases * completely replace the default PHP static when you set $replace = true, and do not define extra data on any child * classes + * + * Note that from SilverStripe 2.3.2, Object::get_static() can only be used to get public + * static variables, not protected ones. * * @param string $class * @param string $name the property name @@ -238,6 +241,9 @@ abstract class Object { /** * Get an uninherited static variable - a variable that is explicity set in this class, and not in the parent class. * + * Note that from SilverStripe 2.3.2, Object::uninherited_static() can only be used to get public + * static variables, not protected ones. + * * @todo Recursively filter out parent statics, currently only inspects the parent class * * @param string $class diff --git a/core/Requirements.php b/core/Requirements.php index 99ececd11..d78937c3e 100644 --- a/core/Requirements.php +++ b/core/Requirements.php @@ -617,13 +617,12 @@ class Requirements_Backend { $jsRequirements = preg_replace('/>\n*/', '>', $jsRequirements); // We put script tags into the body, for performance. - // If your template already has script tags in the body, then we put our script tags at the top of the body. - // Otherwise, we put it at the bottom. + // If your template already has script tags in the body, then we put our script + // tags just before those. Otherwise, we put it at the bottom. $p1 = strripos($content, ' $p2) { - user_error("You have a script tag in the body, moving requirements to top of for compatibilty. I recommend removing the script tag from your template's body.", E_USER_NOTICE); - $content = eregi_replace("(]*>)", "\\1" . $jsRequirements, $content); + $content = substr($content,0,$p1) . $jsRequirements . substr($content,$p1); } else { $content = eregi_replace("(]*>)", $jsRequirements . "\\1", $content); } diff --git a/core/model/Translatable.php b/core/model/Translatable.php index 17bfd0d25..b67e14733 100755 --- a/core/model/Translatable.php +++ b/core/model/Translatable.php @@ -725,11 +725,12 @@ class Translatable extends DataObjectDecorator { * @return DataObject */ function alternateGetByUrl($urlSegment, $extraFilter, $cache = null, $orderby = null) { - $SQL_URLSegment = Convert::raw2sql($urlSegment); + $filter = sprintf("\"SiteTree\".\"URLSegment\" = '%s'", Convert::raw2sql($urlSegment)); + if($extraFilter) $filter .= " AND $extraFilter"; self::$enable_lang_filter = false; - $record = DataObject::get_one('SiteTree', "\"URLSegment\" = '{$SQL_URLSegment}'", false); + $record = DataObject::get_one('SiteTree', $filter); self::$enable_lang_filter = true; - + return $record; } diff --git a/core/model/fieldtypes/DBField.php b/core/model/fieldtypes/DBField.php index e47bbf325..5a304f8fe 100644 --- a/core/model/fieldtypes/DBField.php +++ b/core/model/fieldtypes/DBField.php @@ -26,7 +26,7 @@ abstract class DBField extends ViewableData { * * @var string */ - protected static $default_search_filter_class = 'PartialMatchFilter'; + public static $default_search_filter_class = 'PartialMatchFilter'; /** * @var $default mixed Default-value in the database. diff --git a/core/model/fieldtypes/ForeignKey.php b/core/model/fieldtypes/ForeignKey.php index 9d7aa888b..3042af1cb 100644 --- a/core/model/fieldtypes/ForeignKey.php +++ b/core/model/fieldtypes/ForeignKey.php @@ -19,7 +19,7 @@ class ForeignKey extends Int { */ protected $object; - protected static $default_search_filter_class = 'ExactMatchMultiFilter'; + public static $default_search_filter_class = 'ExactMatchMultiFilter'; function __construct($name, $object = null) { $this->object = $object; diff --git a/core/model/fieldtypes/PrimaryKey.php b/core/model/fieldtypes/PrimaryKey.php index 4b59bb20e..a3c4d318d 100644 --- a/core/model/fieldtypes/PrimaryKey.php +++ b/core/model/fieldtypes/PrimaryKey.php @@ -13,7 +13,7 @@ class PrimaryKey extends Int { */ protected $object; - protected static $default_search_filter_class = 'ExactMatchMultiFilter'; + public static $default_search_filter_class = 'ExactMatchMultiFilter'; /** * @param string $name diff --git a/dev/CliTestReporter.php b/dev/CliTestReporter.php index e902397bc..35fdd89b5 100644 --- a/dev/CliTestReporter.php +++ b/dev/CliTestReporter.php @@ -30,7 +30,10 @@ class CliTestReporter extends SapphireTestReporter { echo SSCli::text(" AT LEAST ONE FAILURE ", "white", "red"); } echo "\n\n$testCount tests run: " . SSCli::text("$passCount passes", null) . ", ". SSCli::text("$failCount fails", null) . ", and 0 exceptions\n"; - echo "Maximum memory usage: " . number_format(memory_get_peak_usage()/(1024*1024), 1) . "M\n\n"; + + if(function_exists('memory_get_peak_usage')) { + echo "Maximum memory usage: " . number_format(memory_get_peak_usage()/(1024*1024), 1) . "M\n\n"; + } $totalTime = array_sum($this->testSpeeds); echo "Total time: " . round($totalTime,3) . " seconds\n"; diff --git a/dev/FunctionalTest.php b/dev/FunctionalTest.php index 45ee853c7..b58689295 100644 --- a/dev/FunctionalTest.php +++ b/dev/FunctionalTest.php @@ -251,7 +251,19 @@ class FunctionalTest extends SapphireTest { . "Instead the following elements were found:\n'" . implode("'\n'", $actuals) . "'" ); } - } + } + + /** + * Log in as the given member + * @param $member The ID, fixture codename, or Member object of the member that you want to log in + */ + function logInAs($member) { + if(is_object($member)) $memberID = $member->ID; + elseif(is_numeric($member)) $memberID = $member; + else $memberID = $this->idFromFixture('Member', $member); + + $this->session()->inst_set('loggedInAs', $memberID); + } /** * Use the draft (stage) site for testing. diff --git a/dev/TestRunner.php b/dev/TestRunner.php index 7773935a9..0fe955352 100644 --- a/dev/TestRunner.php +++ b/dev/TestRunner.php @@ -126,10 +126,10 @@ class TestRunner extends Controller { * Run only a single test class or a comma-separated list of tests */ function only($request) { - if ($request->param('TestCase') == 'all') { + if($request->param('TestCase') == 'all') { $this->all(); } else { - $classNames = explode(',',$request->param('TestCase')); + $classNames = explode(',', $request->param('TestCase')); foreach($classNames as $className) { if(!class_exists($className) || !(singleton($className) instanceof SapphireTest)) { user_error("TestRunner::only(): Invalid TestCase '$className', cannot find matching class", E_USER_ERROR); diff --git a/email/Email.php b/email/Email.php index 7e2702389..1a5bce900 100755 --- a/email/Email.php +++ b/email/Email.php @@ -335,6 +335,8 @@ class Email extends ViewableData { * and it won't be plain email :) */ protected function parseVariables($isPlain = false) { + SSViewer::set_source_file_comments(false); + if(!$this->parseVariables_done) { $this->parseVariables_done = true; diff --git a/forms/ComplexTableField.php b/forms/ComplexTableField.php index 5f2e50e82..263f1f990 100755 --- a/forms/ComplexTableField.php +++ b/forms/ComplexTableField.php @@ -1023,6 +1023,7 @@ class ComplexTableField_Popup extends Form { $this->dataObject = $dataObject; Requirements::clear(); + Requirements::unblock_all(); $actions = new FieldSet(); if(!$readonly) { diff --git a/tests/SiteTreeTest.php b/tests/SiteTreeTest.php index 59817509c..8bc8372af 100644 --- a/tests/SiteTreeTest.php +++ b/tests/SiteTreeTest.php @@ -206,6 +206,19 @@ class SiteTreeTest extends SapphireTest { $this->assertEquals('Page', $requeriedPage->class); } + + /** + * Test SiteTree::get_by_url() + */ + function testGetByURL() { + // Test basic get by url + $this->assertEquals($this->idFromFixture('Page', 'home'), SiteTree::get_by_url("home")->ID); + + // Test the extraFilter argument + // Note: One day, it would be more appropriate to return null instead of false for queries such as these + $this->assertFalse(SiteTree::get_by_url("home", "1 = 2")); + } + } // We make these extend page since that's what all page types are expected to do diff --git a/tests/model/HierarchyTest.php b/tests/model/HierarchyTest.php new file mode 100644 index 000000000..9d8da92b7 --- /dev/null +++ b/tests/model/HierarchyTest.php @@ -0,0 +1,57 @@ +objFromFixture('Page', 'page2b')->delete(); + $this->objFromFixture('Page', 'page3a')->delete(); + $this->objFromFixture('Page', 'page3')->delete(); + + // Check that page1-3 appear at the top level of the AllHistoricalChildren tree + $this->assertEquals(array("Page 1", "Page 2", "Page 3"), + singleton('Page')->AllHistoricalChildren()->column('Title')); + + // Check that both page 2 children are returned + $page2 = $this->objFromFixture('Page', 'page2'); + $this->assertEquals(array("Page 2a", "Page 2b"), + $page2->AllHistoricalChildren()->column('Title')); + + // Page 3 has been deleted; let's bring it back from the grave + $page3 = Versioned::get_including_deleted("SiteTree", "Title = 'Page 3'")->First(); + + // Check that both page 3 children are returned + $this->assertEquals(array("Page 3a", "Page 3b"), + $page3->AllHistoricalChildren()->column('Title')); + + } + + /** + * Test that you can call Hierarchy::markExpanded/Unexpanded/Open() on a page, and that + * calling Hierarchy::isMarked() on a different instance of that object will return true. + */ + function testItemMarkingIsntRestrictedToSpecificInstance() { + // Mark a few pages + $this->objFromFixture('Page', 'page2')->markExpanded(); + $this->objFromFixture('Page', 'page2a')->markExpanded(); + $this->objFromFixture('Page', 'page2b')->markExpanded(); + $this->objFromFixture('Page', 'page3')->markUnexpanded(); + + // Query some pages in a different context and check their m + $pages = DataObject::get("Page"); + $marked = $expanded = array(); + foreach($pages as $page) { + if($page->isMarked()) $marked[] = $page->Title; + if($page->isExpanded()) $expanded[] = $page->Title; + } + + $this->assertEquals(array('Page 2', 'Page 3', 'Page 2a', 'Page 2b'), $marked); + $this->assertEquals(array('Page 2', 'Page 2a', 'Page 2b'), $expanded); + + } + +} \ No newline at end of file diff --git a/tests/model/HierarchyTest.yml b/tests/model/HierarchyTest.yml new file mode 100644 index 000000000..74d94eb70 --- /dev/null +++ b/tests/model/HierarchyTest.yml @@ -0,0 +1,19 @@ +Page: + page1: + Title: Page 1 + page2: + Title: Page 2 + page3: + Title: Page 3 + page2a: + Parent: =>Page.page2 + Title: Page 2a + page2b: + Parent: =>Page.page2 + Title: Page 2b + page3a: + Parent: =>Page.page3 + Title: Page 3a + page3b: + Parent: =>Page.page3 + Title: Page 3b diff --git a/tests/model/VersionedTest.php b/tests/model/VersionedTest.php new file mode 100644 index 000000000..062a3789a --- /dev/null +++ b/tests/model/VersionedTest.php @@ -0,0 +1,30 @@ +objFromFixture('Page', 'page3')->delete(); + + // Get all items, ignoring deleted + $remainingPages = DataObject::get("SiteTree", "ParentID = 0"); + // Check that page 3 has gone + $this->assertEquals(array("Page 1", "Page 2"), $remainingPages->column('Title')); + + // Get all including deleted + $allPages = Versioned::get_including_deleted("SiteTree", "ParentID = 0"); + // Check that page 3 is still there + $this->assertEquals(array("Page 1", "Page 2", "Page 3"), $allPages->column('Title')); + + // Check that this still works if we switch to reading the other stage + Versioned::reading_stage("Live"); + $allPages = Versioned::get_including_deleted("SiteTree", "ParentID = 0"); + $this->assertEquals(array("Page 1", "Page 2", "Page 3"), $allPages->column('Title')); + + } + +} \ No newline at end of file diff --git a/tests/model/VersionedTest.yml b/tests/model/VersionedTest.yml new file mode 100644 index 000000000..74d94eb70 --- /dev/null +++ b/tests/model/VersionedTest.yml @@ -0,0 +1,19 @@ +Page: + page1: + Title: Page 1 + page2: + Title: Page 2 + page3: + Title: Page 3 + page2a: + Parent: =>Page.page2 + Title: Page 2a + page2b: + Parent: =>Page.page2 + Title: Page 2b + page3a: + Parent: =>Page.page3 + Title: Page 3a + page3b: + Parent: =>Page.page3 + Title: Page 3b