[TASK] Make Condition ViewHelpers fully static compileable 80/39180/5
authorChristian Müller <christian@kitsunet.de>
Sat, 2 May 2015 06:03:18 +0000 (08:03 +0200)
committerAnja Leichsenring <aleichsenring@ab-softlab.de>
Sat, 2 May 2015 15:21:40 +0000 (17:21 +0200)
All Condition view helpers are now fully compileable and
the default implementation allows for easily implementing
custom conditions while still keeping it compileable.

Releases: master
Resolves: #66746
Change-Id: I09ac78af05fa785657eef2ba80f880bac2b189da
Reviewed-on: http://review.typo3.org/39180
Reviewed-by: Christian Kuhn <lolli@schwarzbu.ch>
Tested-by: Christian Kuhn <lolli@schwarzbu.ch>
Reviewed-by: Wouter Wolters <typo3@wouterwolters.nl>
Tested-by: Wouter Wolters <typo3@wouterwolters.nl>
Reviewed-by: Anja Leichsenring <aleichsenring@ab-softlab.de>
Tested-by: Anja Leichsenring <aleichsenring@ab-softlab.de>
typo3/sysext/documentation/Classes/ViewHelpers/Be/Security/IfAdminViewHelper.php
typo3/sysext/fluid/Classes/Core/ViewHelper/AbstractConditionViewHelper.php
typo3/sysext/fluid/Classes/ViewHelpers/Be/Security/IfAuthenticatedViewHelper.php
typo3/sysext/fluid/Classes/ViewHelpers/Be/Security/IfHasRoleViewHelper.php
typo3/sysext/fluid/Classes/ViewHelpers/IfViewHelper.php
typo3/sysext/fluid/Tests/Unit/Core/ViewHelper/AbstractConditionViewHelperTest.php
typo3/sysext/fluid/Tests/Unit/ViewHelpers/Be/IfHasRoleViewHelperTest.php
typo3/sysext/fluid/Tests/Unit/ViewHelpers/IfViewHelperTest.php

index 95bb112..2bc551b 100644 (file)
@@ -48,18 +48,12 @@ namespace TYPO3\CMS\Documentation\ViewHelpers\Be\Security;
 class IfAdminViewHelper extends \TYPO3\CMS\Fluid\Core\ViewHelper\AbstractConditionViewHelper {
 
        /**
-        * Renders <f:then> child if the current logged in BE user is an admin,
-        * otherwise renders <f:else> child.
+        * This method decides if the condition is TRUE or FALSE. It can be overriden in extending viewhelpers to adjust functionality.
         *
-        * @return string the rendered string
-        * @api
+        * @param array $arguments ViewHelper arguments to evaluate the condition for this ViewHelper, allows for flexiblity in overriding this method.
+        * @return bool
         */
-       public function render() {
-               if ($GLOBALS['BE_USER']->isAdmin()) {
-                       return $this->renderThenChild();
-               } else {
-                       return $this->renderElseChild();
-               }
+       static protected function evaluateCondition($arguments = NULL) {
+               return $GLOBALS['BE_USER']->isAdmin();
        }
-
 }
index 8d4c76b..f020886 100644 (file)
@@ -10,6 +10,9 @@ namespace TYPO3\CMS\Fluid\Core\ViewHelper;
  *                                                                        *
  * The TYPO3 project - inspiring people to share!                         *
  *                                                                        */
+use TYPO3\CMS\Fluid\ViewHelpers\ThenViewHelper;
+use TYPO3\CMS\Fluid\ViewHelpers\ElseViewHelper;
+use TYPO3\CMS\Fluid\Core\Parser\SyntaxTree\ViewHelperNode;
 
 /**
  * This view helper is an abstract ViewHelper which implements an if/else condition.
@@ -59,6 +62,20 @@ abstract class AbstractConditionViewHelper extends \TYPO3\CMS\Fluid\Core\ViewHel
        }
 
        /**
+        * renders <f:then> child if $condition is true, otherwise renders <f:else> child.
+        *
+        * @return string the rendered string
+        * @api
+        */
+       public function render() {
+               if (static::evaluateCondition($this->arguments)) {
+                       return $this->renderThenChild();
+               } else {
+                       return $this->renderElseChild();
+               }
+       }
+
+       /**
         * Returns value of "then" attribute.
         * If then attribute is not set, iterates through child nodes and renders ThenViewHelper.
         * If then attribute is not set and no ThenViewHelper and no ElseViewHelper is found, all child nodes are rendered
@@ -67,25 +84,21 @@ abstract class AbstractConditionViewHelper extends \TYPO3\CMS\Fluid\Core\ViewHel
         * @api
         */
        protected function renderThenChild() {
-               if ($this->hasArgument('then')) {
-                       return $this->arguments['then'];
-               }
-               if ($this->hasArgument('__thenClosure')) {
-                       $thenClosure = $this->arguments['__thenClosure'];
-                       return $thenClosure();
-               } elseif ($this->hasArgument('__elseClosure') || $this->hasArgument('else')) {
-                       return '';
+               $hasEvaluated = TRUE;
+               $result = static::renderStaticThenChild($this->arguments, $hasEvaluated);
+               if ($hasEvaluated) {
+                       return $result;
                }
 
                $elseViewHelperEncountered = FALSE;
                foreach ($this->childNodes as $childNode) {
-                       if ($childNode instanceof \TYPO3\CMS\Fluid\Core\Parser\SyntaxTree\ViewHelperNode
-                               && $childNode->getViewHelperClassName() === \TYPO3\CMS\Fluid\ViewHelpers\ThenViewHelper::class) {
+                       if ($childNode instanceof ViewHelperNode
+                               && $childNode->getViewHelperClassName() === ThenViewHelper::class) {
                                $data = $childNode->evaluate($this->renderingContext);
                                return $data;
                        }
-                       if ($childNode instanceof \TYPO3\CMS\Fluid\Core\Parser\SyntaxTree\ViewHelperNode
-                               && $childNode->getViewHelperClassName() === \TYPO3\CMS\Fluid\ViewHelpers\ElseViewHelper::class) {
+                       if ($childNode instanceof ViewHelperNode
+                               && $childNode->getViewHelperClassName() === ElseViewHelper::class) {
                                $elseViewHelperEncountered = TRUE;
                        }
                }
@@ -98,6 +111,28 @@ abstract class AbstractConditionViewHelper extends \TYPO3\CMS\Fluid\Core\ViewHel
        }
 
        /**
+        * Statically evalute "then" children.
+        * The "$hasEvaluated" argument is there to distinguish the case that "then" returned NULL or was not evaluated.
+        *
+        * @param array $arguments ViewHelper arguments
+        * @param bool $hasEvaluated Can be used to check if the "then" child was actually evaluated by this method.
+        * @return string
+        */
+       protected static function renderStaticThenChild($arguments, &$hasEvaluated) {
+               if (isset($arguments['then'])) {
+                       return $arguments['then'];
+               }
+               if (isset($arguments['__thenClosure'])) {
+                       $thenClosure = $arguments['__thenClosure'];
+                       return $thenClosure();
+               } elseif (isset($arguments['__elseClosure'])) {
+                       return '';
+               }
+
+               $hasEvaluated = FALSE;
+       }
+
+       /**
         * Returns value of "else" attribute.
         * If else attribute is not set, iterates through child nodes and renders ElseViewHelper.
         * If else attribute is not set and no ElseViewHelper is found, an empty string will be returned.
@@ -106,16 +141,15 @@ abstract class AbstractConditionViewHelper extends \TYPO3\CMS\Fluid\Core\ViewHel
         * @api
         */
        protected function renderElseChild() {
-               if ($this->hasArgument('else')) {
-                       return $this->arguments['else'];
-               }
-               if ($this->hasArgument('__elseClosure')) {
-                       $elseClosure = $this->arguments['__elseClosure'];
-                       return $elseClosure();
+               $hasEvaluated = TRUE;
+               $result = static::renderStaticElseChild($this->arguments, $hasEvaluated);
+               if ($hasEvaluated) {
+                       return $result;
                }
+
                foreach ($this->childNodes as $childNode) {
-                       if ($childNode instanceof \TYPO3\CMS\Fluid\Core\Parser\SyntaxTree\ViewHelperNode
-                               && $childNode->getViewHelperClassName() === \TYPO3\CMS\Fluid\ViewHelpers\ElseViewHelper::class) {
+                       if ($childNode instanceof ViewHelperNode
+                               && $childNode->getViewHelperClassName() === ElseViewHelper::class) {
                                return $childNode->evaluate($this->renderingContext);
                        }
                }
@@ -123,6 +157,27 @@ abstract class AbstractConditionViewHelper extends \TYPO3\CMS\Fluid\Core\ViewHel
                return '';
        }
 
+
+       /**
+        * Statically evalute "else" children.
+        * The "$hasEvaluated" argument is there to distinguish the case that "else" returned NULL or was not evaluated.
+        *
+        * @param array $arguments ViewHelper arguments
+        * @param bool $hasEvaluated Can be used to check if the "else" child was actually evaluated by this method.
+        * @return string
+        */
+       protected static function renderStaticElseChild($arguments, &$hasEvaluated) {
+               if (isset($arguments['else'])) {
+                       return $arguments['else'];
+               }
+               if (isset($arguments['__elseClosure'])) {
+                       $elseClosure = $arguments['__elseClosure'];
+                       return $elseClosure();
+               }
+
+               $hasEvaluated = FALSE;
+       }
+
        /**
         * The compiled ViewHelper adds two new ViewHelper arguments: __thenClosure and __elseClosure.
         * These contain closures which are be executed to render the then(), respectively else() case.
@@ -137,20 +192,60 @@ abstract class AbstractConditionViewHelper extends \TYPO3\CMS\Fluid\Core\ViewHel
         */
        public function compile($argumentsVariableName, $renderChildrenClosureVariableName, &$initializationPhpCode, \TYPO3\CMS\Fluid\Core\Parser\SyntaxTree\AbstractNode $syntaxTreeNode, \TYPO3\CMS\Fluid\Core\Compiler\TemplateCompiler $templateCompiler) {
                foreach ($syntaxTreeNode->getChildNodes() as $childNode) {
-                       if ($childNode instanceof \TYPO3\CMS\Fluid\Core\Parser\SyntaxTree\ViewHelperNode
-                               && $childNode->getViewHelperClassName() === \TYPO3\CMS\Fluid\ViewHelpers\ThenViewHelper::class) {
+                       if ($childNode instanceof ViewHelperNode
+                               && $childNode->getViewHelperClassName() === ThenViewHelper::class) {
 
                                $childNodesAsClosure = $templateCompiler->wrapChildNodesInClosure($childNode);
                                $initializationPhpCode .= sprintf('%s[\'__thenClosure\'] = %s;', $argumentsVariableName, $childNodesAsClosure) . LF;
                        }
-                       if ($childNode instanceof \TYPO3\CMS\Fluid\Core\Parser\SyntaxTree\ViewHelperNode
-                               && $childNode->getViewHelperClassName() === \TYPO3\CMS\Fluid\ViewHelpers\ElseViewHelper::class) {
+                       if ($childNode instanceof ViewHelperNode
+                               && $childNode->getViewHelperClassName() === ElseViewHelper::class) {
 
                                $childNodesAsClosure = $templateCompiler->wrapChildNodesInClosure($childNode);
                                $initializationPhpCode .= sprintf('%s[\'__elseClosure\'] = %s;', $argumentsVariableName, $childNodesAsClosure) . LF;
                        }
                }
-               return \TYPO3\CMS\Fluid\Core\Compiler\TemplateCompiler::SHOULD_GENERATE_VIEWHELPER_INVOCATION;
+
+               return sprintf('%s::renderStatic(%s, %s, $renderingContext)',
+                       get_class($this), $argumentsVariableName, $renderChildrenClosureVariableName);
+       }
+
+       /**
+        * Default implementation for CompilableInterface. See CompilableInterface
+        * for a detailed description of this method.
+        *
+        * @param array $arguments
+        * @param \Closure $renderChildrenClosure
+        * @param \TYPO3\CMS\Fluid\Core\Rendering\RenderingContextInterface $renderingContext
+        * @return mixed
+        * @see \TYPO3\CMS\Fluid\Core\ViewHelper\Facets\CompilableInterface
+        */
+       static public function renderStatic(array $arguments, \Closure $renderChildrenClosure, \TYPO3\CMS\Fluid\Core\Rendering\RenderingContextInterface $renderingContext) {
+               $hasEvaluated = TRUE;
+               if (static::evaluateCondition($arguments)) {
+                       $result = static::renderStaticThenChild($arguments, $hasEvaluated);
+                       if ($hasEvaluated) {
+                               return $result;
+                       }
+
+                       return $renderChildrenClosure();
+               } else {
+                       $result = static::renderStaticElseChild($arguments, $hasEvaluated);
+                       if ($hasEvaluated) {
+                               return $result;
+                       }
+               }
+
+               return '';
        }
 
+       /**
+        * This method decides if the condition is TRUE or FALSE. It can be overriden in extending viewhelpers to adjust functionality.
+        *
+        * @param array $arguments ViewHelper arguments to evaluate the condition for this ViewHelper, allows for flexiblity in overriding this method.
+        * @return bool
+        */
+       static protected function evaluateCondition($arguments = NULL) {
+               return (isset($arguments['condition']) && $arguments['condition']);
+       }
 }
index 5650672..ec7c1bf 100644 (file)
@@ -54,16 +54,12 @@ namespace TYPO3\CMS\Fluid\ViewHelpers\Be\Security;
 class IfAuthenticatedViewHelper extends \TYPO3\CMS\Fluid\Core\ViewHelper\AbstractConditionViewHelper {
 
        /**
-        * Renders <f:then> child if any BE user is currently authenticated, otherwise renders <f:else> child.
+        * This method decides if the condition is TRUE or FALSE. It can be overriden in extending viewhelpers to adjust functionality.
         *
-        * @return string the rendered string
-        * @api
+        * @param array $arguments ViewHelper arguments to evaluate the condition for this ViewHelper, allows for flexiblity in overriding this method.
+        * @return bool
         */
-       public function render() {
-               if (isset($GLOBALS['BE_USER']) && $GLOBALS['BE_USER']->user['uid'] > 0) {
-                       return $this->renderThenChild();
-               }
-               return $this->renderElseChild();
+       static protected function evaluateCondition($arguments = NULL) {
+               return (isset($GLOBALS['BE_USER']) && $GLOBALS['BE_USER']->user['uid'] > 0);
        }
-
 }
index 96ff7c3..4f3aefd 100644 (file)
@@ -71,20 +71,17 @@ class IfHasRoleViewHelper extends \TYPO3\CMS\Fluid\Core\ViewHelper\AbstractCondi
         * @api
         */
        public function render($role) {
-               if ($this->backendUserHasRole($role)) {
-                       return $this->renderThenChild();
-               } else {
-                       return $this->renderElseChild();
-               }
+               return parent::render();
        }
 
        /**
-        * Determines whether the currently logged in BE user belongs to the specified usergroup
+        * This method decides if the condition is TRUE or FALSE. It can be overriden in extending viewhelpers to adjust functionality.
         *
-        * @param string $role The usergroup (either the usergroup uid or its title)
-        * @return bool TRUE if the currently logged in BE user belongs to $role
+        * @param array $arguments ViewHelper arguments to evaluate the condition for this ViewHelper, allows for flexiblity in overriding this method.
+        * @return bool
         */
-       protected function backendUserHasRole($role) {
+       static protected function evaluateCondition($arguments = NULL) {
+               $role = $arguments['role'];
                if (!is_array($GLOBALS['BE_USER']->userGroups)) {
                        return FALSE;
                }
@@ -103,5 +100,4 @@ class IfHasRoleViewHelper extends \TYPO3\CMS\Fluid\Core\ViewHelper\AbstractCondi
                }
                return FALSE;
        }
-
 }
index 0dcb7c3..2db8da4 100644 (file)
@@ -91,11 +91,6 @@ class IfViewHelper extends \TYPO3\CMS\Fluid\Core\ViewHelper\AbstractConditionVie
         * @api
         */
        public function render($condition) {
-               if ($condition) {
-                       return $this->renderThenChild();
-               } else {
-                       return $this->renderElseChild();
-               }
+               return parent::render();
        }
-
 }
index ce04a76..4d4fd62 100644 (file)
@@ -81,7 +81,6 @@ class AbstractConditionViewHelperTest extends \TYPO3\CMS\Fluid\Tests\Unit\ViewHe
         * @test
         */
        public function renderThenChildReturnsValueOfThenArgumentIfConditionIsTrue() {
-               $this->viewHelper->expects($this->atLeastOnce())->method('hasArgument')->with('then')->will($this->returnValue(TRUE));
                $this->arguments['then'] = 'ThenArgument';
                $this->injectDependenciesIntoViewHelper($this->viewHelper);
 
@@ -111,7 +110,6 @@ class AbstractConditionViewHelperTest extends \TYPO3\CMS\Fluid\Tests\Unit\ViewHe
 
                $this->viewHelper->setChildNodes(array($mockThenViewHelperNode));
 
-               $this->viewHelper->expects($this->atLeastOnce())->method('hasArgument')->with('then')->will($this->returnValue(TRUE));
                $this->arguments['then'] = 'ThenArgument';
 
                $this->injectDependenciesIntoViewHelper($this->viewHelper);
@@ -124,7 +122,6 @@ class AbstractConditionViewHelperTest extends \TYPO3\CMS\Fluid\Tests\Unit\ViewHe
         * @test
         */
        public function renderReturnsValueOfElseArgumentIfConditionIsFalse() {
-               $this->viewHelper->expects($this->atLeastOnce())->method('hasArgument')->with('else')->will($this->returnValue(TRUE));
                $this->arguments['else'] = 'ElseArgument';
                $this->injectDependenciesIntoViewHelper($this->viewHelper);
 
@@ -142,7 +139,6 @@ class AbstractConditionViewHelperTest extends \TYPO3\CMS\Fluid\Tests\Unit\ViewHe
 
                $this->viewHelper->setChildNodes(array($mockElseViewHelperNode));
 
-               $this->viewHelper->expects($this->atLeastOnce())->method('hasArgument')->with('else')->will($this->returnValue(TRUE));
                $this->arguments['else'] = 'ElseArgument';
                $this->injectDependenciesIntoViewHelper($this->viewHelper);
 
index 18d04a7..36b48ec 100644 (file)
@@ -55,6 +55,9 @@ class IfHasRoleViewHelperTest extends \TYPO3\CMS\Fluid\Tests\Unit\ViewHelpers\Vi
         * @test
         */
        public function viewHelperRendersThenChildIfBeUserWithSpecifiedRoleIsLoggedIn() {
+               $this->arguments['role'] = 'Editor';
+               $this->injectDependenciesIntoViewHelper($this->viewHelper);
+
                $actualResult = $this->viewHelper->render('Editor');
                $this->assertEquals('then child', $actualResult);
        }
@@ -63,6 +66,9 @@ class IfHasRoleViewHelperTest extends \TYPO3\CMS\Fluid\Tests\Unit\ViewHelpers\Vi
         * @test
         */
        public function viewHelperRendersThenChildIfBeUserWithSpecifiedRoleIdIsLoggedIn() {
+               $this->arguments['role'] = 1;
+               $this->injectDependenciesIntoViewHelper($this->viewHelper);
+
                $actualResult = $this->viewHelper->render(1);
                $this->assertEquals('then child', $actualResult);
        }
@@ -71,6 +77,9 @@ class IfHasRoleViewHelperTest extends \TYPO3\CMS\Fluid\Tests\Unit\ViewHelpers\Vi
         * @test
         */
        public function viewHelperRendersElseChildIfBeUserWithSpecifiedRoleIsNotLoggedIn() {
+               $this->arguments['role'] = 'editor';
+               $this->injectDependenciesIntoViewHelper($this->viewHelper);
+
                $actualResult = $this->viewHelper->render('editor');
                $this->assertEquals('else child', $actualResult);
        }
@@ -79,6 +88,9 @@ class IfHasRoleViewHelperTest extends \TYPO3\CMS\Fluid\Tests\Unit\ViewHelpers\Vi
         * @test
         */
        public function viewHelperRendersElseChildIfBeUserWithSpecifiedRoleIdIsNotLoggedIn() {
+               $this->arguments['role'] = 123;
+               $this->injectDependenciesIntoViewHelper($this->viewHelper);
+
                $actualResult = $this->viewHelper->render(123);
                $this->assertEquals('else child', $actualResult);
        }
index f6dfb3d..9700234 100644 (file)
@@ -37,6 +37,9 @@ class IfViewHelperTest extends \TYPO3\CMS\Fluid\Tests\Unit\ViewHelpers\ViewHelpe
         * @test
         */
        public function viewHelperRendersThenChildIfConditionIsTrue() {
+               $this->arguments['condition'] = TRUE;
+               $this->injectDependenciesIntoViewHelper($this->viewHelper);
+
                $this->viewHelper->expects($this->at(0))->method('renderThenChild')->will($this->returnValue('foo'));
 
                $actualResult = $this->viewHelper->render(TRUE);