diff --git a/tests/templates/SSViewerTestIncludeScopeInheritance.ss b/tests/templates/SSViewerTestIncludeScopeInheritance.ss new file mode 100644 index 000000000..6c106c703 --- /dev/null +++ b/tests/templates/SSViewerTestIncludeScopeInheritance.ss @@ -0,0 +1,3 @@ +<% loop Items %> + <% include SSViewerTestIncludeScopeInheritanceInclude %> +<% end_loop %> diff --git a/tests/templates/SSViewerTestIncludeScopeInheritanceInclude.ss b/tests/templates/SSViewerTestIncludeScopeInheritanceInclude.ss new file mode 100644 index 000000000..51b2dc190 --- /dev/null +++ b/tests/templates/SSViewerTestIncludeScopeInheritanceInclude.ss @@ -0,0 +1 @@ +$Title <% if ArgA %>- $ArgA <% end_if %>- <%if First %>First-<% end_if %><% if Last %>Last-<% end_if %><%if MultipleOf(2) %>EVEN<% else %>ODD<% end_if %> top:$Top.Title diff --git a/tests/templates/SSViewerTestIncludeScopeInheritanceWithArgs.ss b/tests/templates/SSViewerTestIncludeScopeInheritanceWithArgs.ss new file mode 100644 index 000000000..d897bf044 --- /dev/null +++ b/tests/templates/SSViewerTestIncludeScopeInheritanceWithArgs.ss @@ -0,0 +1,3 @@ +<% loop Items %> + <% include SSViewerTestIncludeScopeInheritanceInclude ArgA=$Title %> +<% end_loop %> diff --git a/tests/view/SSViewerTest.php b/tests/view/SSViewerTest.php index 5e7589165..187bf0504 100644 --- a/tests/view/SSViewerTest.php +++ b/tests/view/SSViewerTest.php @@ -29,7 +29,57 @@ class SSViewerTest extends SapphireTest { $result = $data->renderWith("SSViewerTestPartialTemplate"); $this->assertEquals('Test partial template: var value', trim(preg_replace("//U",'',$result))); } - + + public function testIncludeScopeInheritance() { + $data = $this->getScopeInheritanceTestData(); + $expected = array( + 'Item 1 - First-ODD top:Item 1', + 'Item 2 - EVEN top:Item 2', + 'Item 3 - ODD top:Item 3', + 'Item 4 - EVEN top:Item 4', + 'Item 5 - ODD top:Item 5', + 'Item 6 - Last-EVEN top:Item 6', + ); + + $result = $data->renderWith('SSViewerTestIncludeScopeInheritance'); + $this->assertExpectedStrings($result, $expected); + + // reset results for the tests that include arguments (the title is passed as an arg) + $expected = array( + 'Item 1 - Item 1 - First-ODD top:Item 1', + 'Item 2 - Item 2 - EVEN top:Item 2', + 'Item 3 - Item 3 - ODD top:Item 3', + 'Item 4 - Item 4 - EVEN top:Item 4', + 'Item 5 - Item 5 - ODD top:Item 5', + 'Item 6 - Item 6 - Last-EVEN top:Item 6', + ); + + $result = $data->renderWith('SSViewerTestIncludeScopeInheritanceWithArgs'); + $this->assertExpectedStrings($result, $expected); + } + + private function getScopeInheritanceTestData() { + return new ArrayData(array( + 'Title' => 'TopTitleValue', + 'Items' => new ArrayList(array( + new ArrayData(array('Title' => 'Item 1')), + new ArrayData(array('Title' => 'Item 2')), + new ArrayData(array('Title' => 'Item 3')), + new ArrayData(array('Title' => 'Item 4')), + new ArrayData(array('Title' => 'Item 5')), + new ArrayData(array('Title' => 'Item 6')) + )) + )); + } + + private function assertExpectedStrings($result, $expected) { + foreach ($expected as $expectedStr) { + $this->assertTrue( + (boolean) preg_match("/{$expectedStr}/", $result), + "Didn't find '{$expectedStr}' in:\n{$result}" + ); + } + } /** * Small helper to render templates from strings diff --git a/view/SSTemplateParser.php b/view/SSTemplateParser.php index efe7e27ea..f516aa6b0 100644 --- a/view/SSTemplateParser.php +++ b/view/SSTemplateParser.php @@ -3242,7 +3242,7 @@ class SSTemplateParser extends Parser { $arguments = $res['arguments']; $res['php'] = '$val .= SSViewer::execute_template('.$template.', $scope->getItem(), array(' . - implode(',', $arguments)."));\n"; + implode(',', $arguments)."), \$scope);\n"; if($this->includeDebuggingComments) { // Add include filename comments on dev sites $res['php'] = diff --git a/view/SSTemplateParser.php.inc b/view/SSTemplateParser.php.inc index 3c1349efe..7b789b2af 100644 --- a/view/SSTemplateParser.php.inc +++ b/view/SSTemplateParser.php.inc @@ -701,7 +701,7 @@ class SSTemplateParser extends Parser { $arguments = $res['arguments']; $res['php'] = '$val .= SSViewer::execute_template('.$template.', $scope->getItem(), array(' . - implode(',', $arguments)."));\n"; + implode(',', $arguments)."), \$scope);\n"; if($this->includeDebuggingComments) { // Add include filename comments on dev sites $res['php'] = diff --git a/view/SSViewer.php b/view/SSViewer.php index 40df8f510..f11a335ff 100644 --- a/view/SSViewer.php +++ b/view/SSViewer.php @@ -47,11 +47,17 @@ class SSViewer_Scope { private $localIndex; - public function __construct($item){ + public function __construct($item, $inheritedScope = null) { $this->item = $item; $this->localIndex = 0; $this->localStack = array(); - $this->itemStack[] = array($this->item, null, 0, null, null, 0); + if ($inheritedScope instanceof SSViewer_Scope) { + $this->itemIterator = $inheritedScope->itemIterator; + $this->itemIteratorTotal = $inheritedScope->itemIteratorTotal; + $this->itemStack[] = array($this->item, $this->itemIterator, $this->itemIteratorTotal, null, null, 0); + } else { + $this->itemStack[] = array($this->item, null, 0, null, null, 0); + } } public function getItem(){ @@ -357,8 +363,8 @@ class SSViewer_DataPresenter extends SSViewer_Scope { */ protected $underlay; - public function __construct($item, $overlay = null, $underlay = null){ - parent::__construct($item); + public function __construct($item, $overlay = null, $underlay = null, $inheritedScope = null) { + parent::__construct($item, $inheritedScope); // Build up global property providers array only once per request if (self::$globalProperties === null) { @@ -553,7 +559,7 @@ class SSViewer { * @var boolean $source_file_comments */ private static $source_file_comments = false; - + /** * Set whether HTML comments indicating the source .SS file used to render this page should be * included in the output. This is enabled by default @@ -895,10 +901,11 @@ class SSViewer { * @param Object $item - The item to use as the root scope for the template * @param array|null $overlay - Any variables to layer on top of the scope * @param array|null $underlay - Any variables to layer underneath the scope + * @param Object $inheritedScope - the current scope of a parent template including a sub-template * * @return string - The result of executing the template */ - protected function includeGeneratedTemplate($cacheFile, $item, $overlay, $underlay) { + protected function includeGeneratedTemplate($cacheFile, $item, $overlay, $underlay, $inheritedScope = null) { if(isset($_GET['showtemplate']) && $_GET['showtemplate'] && Permission::check('ADMIN')) { $lines = file($cacheFile); echo "