[BUGFIX] Revert BackendActionController in favor of BackendView 97/43797/3
authorHelmut Hummel <helmut.hummel@typo3.org>
Sun, 4 Oct 2015 13:20:37 +0000 (15:20 +0200)
committerBenjamin Kott <info@bk2k.info>
Sun, 4 Oct 2015 19:07:57 +0000 (21:07 +0200)
The module template is a view component and should not clutter
the controller code of a module or at least only in a minimal way.

Now we introduce a BackendView which decorates a fluid template view.
This can be easily used in backend modules by using the defaultViewObjectName API

Releases: master
Resolves: #70358
Change-Id: If164c2e6774bd75e414c6417e0d8a4e989fe08e6
Reviewed-on: http://review.typo3.org/43797
Reviewed-by: Mathias Schreiber <mathias.schreiber@wmdb.de>
Tested-by: Mathias Schreiber <mathias.schreiber@wmdb.de>
Reviewed-by: Michael Oehlhof <typo3@oehlhof.de>
Tested-by: Michael Oehlhof <typo3@oehlhof.de>
Reviewed-by: Benjamin Kott <info@bk2k.info>
Tested-by: Benjamin Kott <info@bk2k.info>
typo3/sysext/backend/Classes/View/BackendTemplateView.php [new file with mode: 0644]
typo3/sysext/extbase/Classes/Mvc/Controller/ActionController.php
typo3/sysext/extbase/Classes/Mvc/Controller/BackendActionController.php [deleted file]
typo3/sysext/fluid/Classes/ViewHelpers/Be/PageRendererViewHelper.php [new file with mode: 0644]

diff --git a/typo3/sysext/backend/Classes/View/BackendTemplateView.php b/typo3/sysext/backend/Classes/View/BackendTemplateView.php
new file mode 100644 (file)
index 0000000..f81526f
--- /dev/null
@@ -0,0 +1,131 @@
+<?php
+namespace TYPO3\CMS\Backend\View;
+
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+use TYPO3\CMS\Backend\Template\ModuleTemplate;
+use TYPO3\CMS\Extbase\Mvc\View\ViewInterface;
+use TYPO3\CMS\Extbase\Mvc\Controller\ControllerContext;
+use TYPO3\CMS\Fluid\View\TemplateView;
+
+/**
+ * Decorates the main template view. Should be used as view if you want Fluid Templating in a backend moduel
+ *
+ * @api
+ */
+class BackendTemplateView implements ViewInterface {
+
+       /**
+        * @var ModuleTemplate
+        */
+       protected $moduleTemplate;
+
+       /**
+        * @var TemplateView
+        */
+       protected $templateView;
+
+       /**
+        * @param ModuleTemplate $moduleTemplate
+        */
+       public function injectModuleTemplate(ModuleTemplate $moduleTemplate) {
+               $this->moduleTemplate = $moduleTemplate;
+       }
+
+       /**
+        * @param TemplateView $templateView
+        */
+       public function injectTemplateView(TemplateView $templateView) {
+               $this->templateView = $templateView;
+       }
+
+       /**
+        * @return ModuleTemplate
+        */
+       public function getModuleTemplate() {
+               return $this->moduleTemplate;
+       }
+
+       /**
+        * Loads the template source and render the template.
+        * If "layoutName" is set in a PostParseFacet callback, it will render the file with the given layout.
+        *
+        * Additionally amends the rendered template with a module template "frame"
+        *
+        * @param string $actionName If set, the view of the specified action will be rendered instead. Default is the action specified in the Request object
+        * @return string Rendered Template
+        * @api
+        */
+       public function render($actionName = NULL) {
+               $actionViewContent = $this->templateView->render($actionName);
+               $this->moduleTemplate->setContent($actionViewContent);
+               return $this->moduleTemplate->renderContent();
+       }
+
+       /**
+        * Sets the current controller context
+        *
+        * @param \TYPO3\CMS\Extbase\Mvc\Controller\ControllerContext $controllerContext Controller context which is available inside the view
+        * @return void
+        * @api
+        */
+       public function setControllerContext(\TYPO3\CMS\Extbase\Mvc\Controller\ControllerContext $controllerContext) {
+               $this->templateView->setControllerContext($controllerContext);
+       }
+
+       /**
+        * Assign a value to the variable container.
+        *
+        * @param string $key The key of a view variable to set
+        * @param mixed $value The value of the view variable
+        * @return \TYPO3\CMS\Fluid\View\AbstractTemplateView the instance of this view to allow chaining
+        * @api
+        */
+       public function assign($key, $value) {
+               $this->templateView->assign($key, $value);
+               return $this;
+       }
+
+       /**
+        * Assigns multiple values to the JSON output.
+        * However, only the key "value" is accepted.
+        *
+        * @param array $values Keys and values - only a value with key "value" is considered
+        * @return \TYPO3\CMS\Fluid\View\AbstractTemplateView the instance of this view to allow chaining
+        * @api
+        */
+       public function assignMultiple(array $values) {
+               $this->templateView->assignMultiple($values);
+               return $this;
+       }
+
+       /**
+        * Checks whether a template can be resolved for the current request context.
+        *
+        * @param ControllerContext $controllerContext Controller context which is available inside the view
+        * @return bool
+        * @api
+        */
+       public function canRender(ControllerContext $controllerContext) {
+               return $this->templateView->canRender($controllerContext);
+       }
+
+       /**
+        * Init view
+        */
+       public function initializeView() {
+               $this->templateView->initializeView();
+       }
+
+}
index 8cb8cd4..85c0e66 100644 (file)
@@ -266,9 +266,49 @@ class ActionController extends AbstractController {
         * @api
         */
        protected function callActionMethod() {
-               $actionResult = $this->processActionResult();
-               $content = $this->resolveActionResult($actionResult);
-               $this->appendContent($content);
+               $preparedArguments = array();
+               /** @var \TYPO3\CMS\Extbase\Mvc\Controller\Argument $argument */
+               foreach ($this->arguments as $argument) {
+                       $preparedArguments[] = $argument->getValue();
+               }
+               $validationResult = $this->arguments->getValidationResults();
+               if (!$validationResult->hasErrors()) {
+                       $this->emitBeforeCallActionMethodSignal($preparedArguments);
+                       $actionResult = call_user_func_array(array($this, $this->actionMethodName), $preparedArguments);
+               } else {
+                       $methodTagsValues = $this->reflectionService->getMethodTagsValues(get_class($this), $this->actionMethodName);
+                       $ignoreValidationAnnotations = array();
+                       if (isset($methodTagsValues['ignorevalidation'])) {
+                               $ignoreValidationAnnotations = $methodTagsValues['ignorevalidation'];
+                       }
+                       // if there exist errors which are not ignored with @ignorevalidation => call error method
+                       // else => call action method
+                       $shouldCallActionMethod = TRUE;
+                       foreach ($validationResult->getSubResults() as $argumentName => $subValidationResult) {
+                               if (!$subValidationResult->hasErrors()) {
+                                       continue;
+                               }
+                               if (array_search('$' . $argumentName, $ignoreValidationAnnotations) !== FALSE) {
+                                       continue;
+                               }
+                               $shouldCallActionMethod = FALSE;
+                               break;
+                       }
+                       if ($shouldCallActionMethod) {
+                               $this->emitBeforeCallActionMethodSignal($preparedArguments);
+                               $actionResult = call_user_func_array(array($this, $this->actionMethodName), $preparedArguments);
+                       } else {
+                               $actionResult = call_user_func(array($this, $this->errorMethodName));
+                       }
+               }
+
+               if ($actionResult === NULL && $this->view instanceof ViewInterface) {
+                       $this->response->appendContent($this->view->render());
+               } elseif (is_string($actionResult) && $actionResult !== '') {
+                       $this->response->appendContent($actionResult);
+               } elseif (is_object($actionResult) && method_exists($actionResult, '__toString')) {
+                       $this->response->appendContent((string)$actionResult);
+               }
        }
 
        /**
@@ -573,76 +613,4 @@ class ActionController extends AbstractController {
                return $result;
        }
 
-       /**
-        * If the action returns a string, it is appended to the content in the
-        * response object. If the action does not return anything and a valid
-        * view exists, the view is rendered automatically.
-        *
-        * @param string|object $actionResult
-        * @return NULL|string
-        */
-       protected function resolveActionResult($actionResult) {
-               if ($actionResult === NULL && $this->view instanceof ViewInterface) {
-                       return $this->view->render();
-               } elseif (is_string($actionResult) && $actionResult !== '') {
-                       return $actionResult;
-               } elseif (is_object($actionResult) && method_exists($actionResult, '__toString')) {
-                       return (string)$actionResult;
-               }
-               return NULL;
-       }
-
-       /**
-        * Appends content to response object.
-        *
-        * @param string $content
-        */
-       protected function appendContent($content) {
-               $this->response->appendContent($content);
-       }
-
-       /**
-        * Calls the specified action method and passes the arguments.
-        *
-       * @return string|object
-        */
-       protected function processActionResult() {
-               $preparedArguments = array();
-               foreach ($this->arguments as $argument) {
-                       $preparedArguments[] = $argument->getValue();
-               }
-               $validationResult = $this->arguments->getValidationResults();
-               if (!$validationResult->hasErrors()) {
-                       $this->emitBeforeCallActionMethodSignal($preparedArguments);
-                       $actionResult = call_user_func_array(array($this, $this->actionMethodName), $preparedArguments);
-                       return $actionResult;
-               } else {
-                       $methodTagsValues = $this->reflectionService->getMethodTagsValues(get_class($this), $this->actionMethodName);
-                       $ignoreValidationAnnotations = array();
-                       if (isset($methodTagsValues['ignorevalidation'])) {
-                               $ignoreValidationAnnotations = $methodTagsValues['ignorevalidation'];
-                       }
-                       // if there exist errors which are not ignored with @ignorevalidation => call error method
-                       // else => call action method
-                       $shouldCallActionMethod = TRUE;
-                       foreach ($validationResult->getSubResults() as $argumentName => $subValidationResult) {
-                               if (!$subValidationResult->hasErrors()) {
-                                       continue;
-                               }
-                               if (array_search('$' . $argumentName, $ignoreValidationAnnotations) !== FALSE) {
-                                       continue;
-                               }
-                               $shouldCallActionMethod = FALSE;
-                               break;
-                       }
-                       if ($shouldCallActionMethod) {
-                               $this->emitBeforeCallActionMethodSignal($preparedArguments);
-                               $actionResult = call_user_func_array(array($this, $this->actionMethodName), $preparedArguments);
-                               return $actionResult;
-                       } else {
-                               $actionResult = call_user_func(array($this, $this->errorMethodName));
-                               return $actionResult;
-                       }
-               }
-       }
-}
\ No newline at end of file
+}
diff --git a/typo3/sysext/extbase/Classes/Mvc/Controller/BackendActionController.php b/typo3/sysext/extbase/Classes/Mvc/Controller/BackendActionController.php
deleted file mode 100644 (file)
index cf5f254..0000000
+++ /dev/null
@@ -1,64 +0,0 @@
-<?php
-namespace TYPO3\CMS\Extbase\Mvc\Controller;
-
-/*
- * This file is part of the TYPO3 CMS project.
- *
- * It is free software; you can redistribute it and/or modify it under
- * the terms of the GNU General Public License, either version 2
- * of the License, or any later version.
- *
- * For the full copyright and license information, please read the
- * LICENSE.txt file that was distributed with this source code.
- *
- * The TYPO3 project - inspiring people to share!
- */
-
-use TYPO3\CMS\Backend\Template\ModuleTemplate;
-use TYPO3\CMS\Core\Imaging\IconFactory;
-
-/**
- * For Usage in Backend Modules.
- *
- * Renders the DocHeader component before the content
- * of the module and serves as generic layout provider.
- */
-class BackendActionController extends ActionController {
-
-       /**
-        * @var ModuleTemplate
-        */
-       protected $moduleTemplate;
-
-       /**
-        * @var IconFactory
-        */
-       protected $iconFactory;
-
-       /**
-        * @param ModuleTemplate $moduleTemplate
-        */
-       public function injectModuleTemplate(ModuleTemplate $moduleTemplate) {
-               $this->moduleTemplate = $moduleTemplate;
-       }
-
-       /**
-        * @param IconFactory $iconFactory
-        */
-       public function injectIconFactory(IconFactory $iconFactory) {
-               $this->iconFactory = $iconFactory;
-       }
-
-       /**
-        * Appends content to response object.
-        * The actual module content is wrapped
-        * in the module template component.
-        *
-        * @param string $content
-        */
-       protected function appendContent($content) {
-               $this->moduleTemplate->setContent($content);
-               $this->response->appendContent($this->moduleTemplate->renderContent());
-       }
-
-}
\ No newline at end of file
diff --git a/typo3/sysext/fluid/Classes/ViewHelpers/Be/PageRendererViewHelper.php b/typo3/sysext/fluid/Classes/ViewHelpers/Be/PageRendererViewHelper.php
new file mode 100644 (file)
index 0000000..7cf6619
--- /dev/null
@@ -0,0 +1,110 @@
+<?php
+namespace TYPO3\CMS\Fluid\ViewHelpers\Be;
+
+/*                                                                        *
+ * This script is backported from the TYPO3 Flow package "TYPO3.Fluid".   *
+ *                                                                        *
+ * It is free software; you can redistribute it and/or modify it under    *
+ * the terms of the GNU Lesser General Public License, either version 3   *
+ *  of the License, or (at your option) any later version.                *
+ *                                                                        *
+ *                                                                        *
+ * This script is distributed in the hope that it will be useful, but     *
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN-    *
+ * TABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser       *
+ * General Public License for more details.                               *
+ *                                                                        *
+ * You should have received a copy of the GNU Lesser General Public       *
+ * License along with the script.                                         *
+ * If not, see http://www.gnu.org/licenses/lgpl.html                      *
+ *                                                                        *
+ * The TYPO3 project - inspiring people to share!                         *
+ *                                                                        */
+
+use TYPO3\CMS\Core\Page\PageRenderer;
+use TYPO3\CMS\Extbase\Utility\LocalizationUtility;
+use TYPO3\CMS\Fluid\Core\ViewHelper\AbstractViewHelper;
+
+/**
+ * View helper which allows you to create extbase based modules in the style of TYPO3 default modules.
+ *
+ * = Examples =
+ *
+ * <code title="All options">
+ * <f:be.pageRenderer pageTitle="foo" loadExtJs="true" loadExtJsTheme="false" extJsAdapter="jQuery" enableExtJsDebug="true" loadJQuery="true" includeCssFiles="0: '{f:uri.resource(path:\'Css/Styles.css\')}'" includeJsFiles="0: '{f:uri.resource(path:\'JavaScript/Library1.js\')}', 1: '{f:uri.resource(path:\'JavaScript/Library2.js\')}'" addJsInlineLabels="{0: 'label1', 1: 'label2'}" />
+ * </code>
+ * <output>
+ *
+ * Custom CSS file EXT:your_extension/Resources/Public/Css/styles.css and
+ * JavaScript files EXT:your_extension/Resources/Public/JavaScript/Library1.js and EXT:your_extension/Resources/Public/JavaScript/Library2.js
+ * will be loaded, plus ExtJS and jQuery and some inline labels for usage in JS code.
+ * </output>
+ */
+class PageRendererViewHelper extends AbstractViewHelper {
+
+       /**
+        * @var PageRenderer
+        */
+       protected $pageRenderer;
+
+       /**
+        * @param PageRenderer $pageRenderer
+        */
+       public function injectPageRenderer(PageRenderer $pageRenderer) {
+               $this->pageRenderer = $pageRenderer;
+       }
+
+       /**
+        * Render start page with \TYPO3\CMS\Backend\Template\DocumentTemplate and pageTitle
+        *
+        * @param string $pageTitle title tag of the module. Not required by default, as BE modules are shown in a frame
+        * @param bool $loadExtJs specifies whether to load ExtJS library. Defaults to FALSE
+        * @param bool $loadExtJsTheme whether to load ExtJS "grey" theme. Defaults to FALSE
+        * @param bool $enableExtJsDebug if TRUE, debug version of ExtJS is loaded. Use this for development only
+        * @param bool $loadJQuery whether to load jQuery library. Defaults to FALSE
+        * @param array $includeCssFiles List of custom CSS file to be loaded
+        * @param array $includeJsFiles List of custom JavaScript file to be loaded
+        * @param array $addJsInlineLabels Custom labels to add to JavaScript inline labels
+        * @param array $includeRequireJsModules List of RequireJS modules to be loaded
+        * @return void
+        */
+       public function render($pageTitle = '', $loadExtJs = FALSE, $loadExtJsTheme = TRUE, $enableExtJsDebug = FALSE, $loadJQuery = FALSE, $includeCssFiles = NULL, $includeJsFiles = NULL, $addJsInlineLabels = NULL, $includeRequireJsModules = NULL) {
+               if ($pageTitle) {
+                       $this->pageRenderer->setTitle($pageTitle);
+               }
+               if ($loadExtJs) {
+                       $this->pageRenderer->loadExtJS(TRUE, $loadExtJsTheme);
+                       if ($enableExtJsDebug) {
+                               $this->pageRenderer->enableExtJsDebug();
+                       }
+               }
+               if ($loadJQuery) {
+                       $this->pageRenderer->loadJquery(NULL, NULL, PageRenderer::JQUERY_NAMESPACE_DEFAULT_NOCONFLICT);
+               }
+               // Include custom CSS and JS files
+               if (is_array($includeCssFiles) && count($includeCssFiles) > 0) {
+                       foreach ($includeCssFiles as $addCssFile) {
+                               $this->pageRenderer->addCssFile($addCssFile);
+                       }
+               }
+               if (is_array($includeJsFiles) && count($includeJsFiles) > 0) {
+                       foreach ($includeJsFiles as $addJsFile) {
+                               $this->pageRenderer->addJsFile($addJsFile);
+                       }
+               }
+               if (is_array($includeRequireJsModules) && count($includeRequireJsModules) > 0) {
+                       foreach ($includeRequireJsModules as $addRequireJsFile) {
+                               $this->pageRenderer->loadRequireJsModule($addRequireJsFile);
+                       }
+               }
+               // Add inline language labels
+               if (is_array($addJsInlineLabels) && count($addJsInlineLabels) > 0) {
+                       $extensionKey = $this->controllerContext->getRequest()->getControllerExtensionKey();
+                       foreach ($addJsInlineLabels as $key) {
+                               $label = LocalizationUtility::translate($key, $extensionKey);
+                               $this->pageRenderer->addInlineLanguageLabel($key, $label);
+                       }
+               }
+       }
+
+}