[!!!][TASK] Change EXT:form wizard to use AJAX routes 81/43881/8
authorBenjamin Mack <benni@typo3.org>
Wed, 7 Oct 2015 07:28:02 +0000 (09:28 +0200)
committerWouter Wolters <typo3@wouterwolters.nl>
Sat, 31 Oct 2015 13:14:06 +0000 (14:14 +0100)
The ExtJS wizard of EXT:form in the backend is using
an old "module" to load and to save the data from the wizard,
but is misusing this as AJAX responses.

The patch uses PSR-7 Ajax Routes to load the content,
and also removes the now obsolete WizardView classes.

Resolves: #70574
Releases: master
Change-Id: I714b514ea75f618db58c18f3e0ac1235cfd40f7e
Reviewed-on: https://review.typo3.org/43881
Reviewed-by: Markus Klein <markus.klein@typo3.org>
Reviewed-by: Morton Jonuschat <m.jonuschat@mojocode.de>
Tested-by: Morton Jonuschat <m.jonuschat@mojocode.de>
Reviewed-by: Stefan Neufeind <typo3.neufeind@speedpartner.de>
Reviewed-by: Wouter Wolters <typo3@wouterwolters.nl>
Tested-by: Wouter Wolters <typo3@wouterwolters.nl>
typo3/sysext/core/Documentation/Changelog/master/Breaking-70574-FormWizardSaveHandlingChanged.rst [new file with mode: 0644]
typo3/sysext/form/Classes/Controller/WizardController.php
typo3/sysext/form/Classes/View/Wizard/AbstractWizardView.php [deleted file]
typo3/sysext/form/Classes/View/Wizard/LoadWizardView.php [deleted file]
typo3/sysext/form/Classes/View/Wizard/SaveWizardView.php [deleted file]
typo3/sysext/form/Classes/View/Wizard/WizardView.php
typo3/sysext/form/Configuration/Backend/AjaxRoutes.php [new file with mode: 0644]
typo3/sysext/form/Modules/Wizards/FormWizard/index.php
typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport.js
typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Right.js

diff --git a/typo3/sysext/core/Documentation/Changelog/master/Breaking-70574-FormWizardSaveHandlingChanged.rst b/typo3/sysext/core/Documentation/Changelog/master/Breaking-70574-FormWizardSaveHandlingChanged.rst
new file mode 100644 (file)
index 0000000..e673a0a
--- /dev/null
@@ -0,0 +1,33 @@
+====================================================
+Breaking: #70574 - Form Wizard Save Handling Changed
+====================================================
+
+Description
+===========
+
+The ExtJS wizard of EXT:form in the backend has been using an old "module" to load and to save the data from the wizard, but has been misusing this functionality as AJAX responses.
+All AJAX requests for the wizard are now built with AJAX Routes and PSR-7-based Request/Response objects.
+
+All obsolete WizardView PHP classes are removed without substitution:
+
+ * \TYPO3\CMS\Form\View\Wizard\AbstractWizardView
+ * \TYPO3\CMS\Form\View\Wizard\LoadWizardView
+ * \TYPO3\CMS\Form\View\Wizard\SaveWizardView
+
+
+Impact
+======
+
+Using these now non-existant PHP classes will result in fatal errors or wrong results when calling them directly.
+
+
+Affected Installations
+======================
+
+Any installations with extensions that hook into the wizard views of EXT:form.
+
+
+Migration
+=========
+
+Use the AJAX routes available via ``TYPO3.settings.ajaxUrls['formwizard_load']`` and ``TYPO3.settings.ajaxUrls['formwizard_save']``.
\ No newline at end of file
index 60d92cd..5f93f7c 100644 (file)
@@ -14,7 +14,10 @@ namespace TYPO3\CMS\Form\Controller;
  * The TYPO3 project - inspiring people to share!
  */
 
+use Psr\Http\Message\ResponseInterface;
+use Psr\Http\Message\ServerRequestInterface;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
+use TYPO3\CMS\Form\Domain\Repository\ContentRepository;
 
 /**
  * The form wizard controller
@@ -22,24 +25,11 @@ use TYPO3\CMS\Core\Utility\GeneralUtility;
 class WizardController
 {
     /**
-     * Dispatch on action
-     *
-     * Calls the requested action
-     *
-     * @return void
+     * The constructor to load the LL file
      */
-    public function dispatch()
+    public function __construct()
     {
-        switch (GeneralUtility::_GP('action')) {
-            case 'save':
-                $this->saveAction();
-                break;
-            case 'load':
-                $this->loadAction();
-                break;
-            default:
-                $this->indexAction();
-        }
+        $this->getLanguageService()->includeLLFile('EXT:form/Resources/Private/Language/locallang_wizard.xlf');
     }
 
     /**
@@ -49,7 +39,7 @@ class WizardController
      *
      * @return void
      */
-    protected function indexAction()
+    public function indexAction()
     {
         /** @var $view \TYPO3\CMS\Form\View\Wizard\WizardView */
         $view = GeneralUtility::makeInstance(\TYPO3\CMS\Form\View\Wizard\WizardView::class, $this->getRepository());
@@ -57,40 +47,81 @@ class WizardController
     }
 
     /**
-     * The save action
+     * The save action called via AJAX
      *
      * The action which should be taken when the form in the wizard is saved
      *
-     * @return void
+     * @param ServerRequestInterface $request
+     * @param ResponseInterface $response
+     * @return ResponseInterface returns a 500 error or a valid JSON response
      */
-    protected function saveAction()
+    public function saveAction(ServerRequestInterface $request, ResponseInterface $response)
     {
-        /** @var $view \TYPO3\CMS\Form\View\Wizard\SaveWizardView */
-        $view = GeneralUtility::makeInstance(\TYPO3\CMS\Form\View\Wizard\SaveWizardView::class, $this->getRepository());
-        $view->render();
+        $repository = $this->getRepository();
+        $success = false;
+        // Check if the referenced record is available
+        if ($repository->hasRecord()) {
+            // Save the data
+            $success = $repository->save();
+        }
+
+        if (!$success) {
+            $response = $response->withStatus(500);
+            $message = $this->getLanguageService()->getLL('action_save_message_failed', false);
+        } else {
+            $message = $this->getLanguageService()->getLL('action_save_message_saved', false);
+        }
+        $response->getBody()->write(json_encode(['message' => $message]));
+        return $response
+                ->withHeader('Expires', 'Mon, 26 Jul 1997 05:00:00 GMT')
+                ->withHeader('Last-Modified', gmdate('D, d M Y H:i:s') . 'GMT')
+                ->withHeader('Cache-Control', 'no-cache, must-revalidate')
+                ->withHeader('Pragma', 'no-cache');
     }
 
     /**
-     * The load action
+     * The load action called via AJAX
      *
      * The action which should be taken when the form in the wizard is loaded
      *
-     * @return void
+     * @param ServerRequestInterface $request
+     * @param ResponseInterface $response the response object
+     * @return ResponseInterface returns a 500 error or a valid JSON response
      */
-    protected function loadAction()
+    public function loadAction(ServerRequestInterface $request, ResponseInterface $response)
     {
-        /** @var $view \TYPO3\CMS\Form\View\Wizard\LoadWizardView */
-        $view = GeneralUtility::makeInstance(\TYPO3\CMS\Form\View\Wizard\LoadWizardView::class, $this->getRepository());
-        $view->render();
+        $result = $this->getRepository()->getRecordAsJson();
+        if (!$result) {
+            $response = $response->withStatus(500);
+            $result = ['message' => $this->getLanguageService()->getLL('action_load_message_failed', false)];
+        } else {
+            $result = ['configuration' => $result];
+        }
+        $response->getBody()->write(json_encode($result));
+        return $response
+                ->withHeader('Expires', 'Mon, 26 Jul 1997 05:00:00 GMT')
+                ->withHeader('Last-Modified', gmdate('D, d M Y H:i:s') . 'GMT')
+                ->withHeader('Cache-Control', 'no-cache, must-revalidate')
+                ->withHeader('Pragma', 'no-cache');
     }
 
     /**
      * Gets the repository object.
      *
-     * @return \TYPO3\CMS\Form\Domain\Repository\ContentRepository
+     * @return ContentRepository
      */
     protected function getRepository()
     {
-        return GeneralUtility::makeInstance(\TYPO3\CMS\Form\Domain\Repository\ContentRepository::class);
+        return GeneralUtility::makeInstance(ContentRepository::class);
+    }
+
+    /**
+     * Returns an instance of LanguageService
+     *
+     * @return \TYPO3\CMS\Lang\LanguageService
+     */
+    protected function getLanguageService()
+    {
+        return $GLOBALS['LANG'];
     }
 }
diff --git a/typo3/sysext/form/Classes/View/Wizard/AbstractWizardView.php b/typo3/sysext/form/Classes/View/Wizard/AbstractWizardView.php
deleted file mode 100644 (file)
index ce4f0f5..0000000
+++ /dev/null
@@ -1,74 +0,0 @@
-<?php
-namespace TYPO3\CMS\Form\View\Wizard;
-
-/*
- * 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\Form\Domain\Repository\ContentRepository;
-
-/**
- * The form wizard load view
- */
-abstract class AbstractWizardView
-{
-    /**
-     * Is the referenced record available
-     *
-     * @var bool TRUE if available, FALSE if not
-     */
-    protected $recordIsAvailable = false;
-
-    /**
-     * @var ContentRepository
-     */
-    protected $repository;
-
-    /**
-     * Returns an instance of LanguageService
-     *
-     * @return \TYPO3\CMS\Lang\LanguageService
-     */
-    protected function getLanguageService()
-    {
-        return $GLOBALS['LANG'];
-    }
-
-    /**
-     * Creates the object and calls the initialize() method.
-     *
-     * @param ContentRepository $repository
-     */
-    public function __construct(ContentRepository $repository)
-    {
-        $this->setRepository($repository);
-        $this->getLanguageService()->includeLLFile('EXT:form/Resources/Private/Language/locallang_wizard.xlf');
-    }
-
-    /**
-     * Sets the content repository to be used.
-     *
-     * @param ContentRepository $repository
-     * @return void
-     */
-    public function setRepository(ContentRepository $repository)
-    {
-        $this->repository = $repository;
-    }
-
-    /**
-     * The main render method
-     *
-     * @return void
-     */
-    abstract public function render();
-}
diff --git a/typo3/sysext/form/Classes/View/Wizard/LoadWizardView.php b/typo3/sysext/form/Classes/View/Wizard/LoadWizardView.php
deleted file mode 100644 (file)
index 6650266..0000000
+++ /dev/null
@@ -1,60 +0,0 @@
-<?php
-namespace TYPO3\CMS\Form\View\Wizard;
-
-/*
- * 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!
- */
-
-/**
- * The form wizard load view
- */
-class LoadWizardView extends \TYPO3\CMS\Form\View\Wizard\AbstractWizardView
-{
-    /**
-     * The main render method
-     *
-     * Gathers all content and echos it to the screen
-     *
-     * @return void
-     */
-    public function render()
-    {
-        $jsonObject = $this->repository->getRecordAsJson();
-        $this->headerOutput($jsonObject);
-    }
-
-    /**
-     * Construct the response header
-     *
-     * @param mixed $jsonObject JSON string, FALSE if not succeeded
-     * @return void
-     */
-    protected function headerOutput($jsonObject)
-    {
-        if (!$jsonObject) {
-            header('HTTP/1.1 500 Internal Server Error');
-            $jsonArray = array('message' => $this->getLanguageService()->getLL('action_load_message_failed', false));
-        } else {
-            $jsonArray = array('configuration' => $jsonObject);
-        }
-        $json = json_encode($jsonArray);
-        header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
-        header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . 'GMT');
-        header('Cache-Control: no-cache, must-revalidate');
-        header('Pragma: no-cache');
-        header('Content-Length: ' . strlen($json));
-        header('Content-Type: application/json; charset=utf-8');
-        header('Content-Transfer-Encoding: 8bit');
-        echo $json;
-        die;
-    }
-}
diff --git a/typo3/sysext/form/Classes/View/Wizard/SaveWizardView.php b/typo3/sysext/form/Classes/View/Wizard/SaveWizardView.php
deleted file mode 100644 (file)
index ad697e1..0000000
+++ /dev/null
@@ -1,66 +0,0 @@
-<?php
-namespace TYPO3\CMS\Form\View\Wizard;
-
-/*
- * 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!
- */
-
-/**
- * The form wizard save view
- */
-class SaveWizardView extends \TYPO3\CMS\Form\View\Wizard\AbstractWizardView
-{
-    /**
-     * The main render method
-     *
-     * Gathers all content and echos it to the screen
-     *
-     * @return void
-     */
-    public function render()
-    {
-        $success = false;
-        // Check if the referenced record is available
-        $this->recordIsAvailable = $this->repository->hasRecord();
-        if ($this->recordIsAvailable) {
-            // Save the data
-            $success = $this->repository->save();
-        }
-        $this->headerOutput($success);
-    }
-
-    /**
-     * Construct the response header
-     *
-     * @param string $success JSON string
-     * @return void
-     */
-    protected function headerOutput($success)
-    {
-        if (!$success) {
-            header('HTTP/1.1 500 Internal Server Error');
-            $jsonArray = array('message' => $this->getLanguageService()->getLL('action_save_message_failed', false));
-        } else {
-            $jsonArray = array('message' => $this->getLanguageService()->getLL('action_save_message_saved', false));
-        }
-        $json = json_encode($jsonArray);
-        header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
-        header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . 'GMT');
-        header('Cache-Control: no-cache, must-revalidate');
-        header('Pragma: no-cache');
-        header('Content-Length: ' . strlen($json));
-        header('Content-Type: application/json; charset=utf-8');
-        header('Content-Transfer-Encoding: 8bit');
-        echo $json;
-        die;
-    }
-}
index 2459f1e..b284050 100644 (file)
@@ -21,13 +21,26 @@ use TYPO3\CMS\Core\Messaging\FlashMessage;
 use TYPO3\CMS\Core\Page\PageRenderer;
 use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
+use TYPO3\CMS\Form\Domain\Repository\ContentRepository;
 
 /**
  * The form wizard view
  */
-class WizardView extends \TYPO3\CMS\Form\View\Wizard\AbstractWizardView
+class WizardView
 {
     /**
+     * Is the referenced record available
+     *
+     * @var bool TRUE if available, FALSE if not
+     */
+    protected $recordIsAvailable = false;
+
+    /**
+     * @var ContentRepository
+     */
+    protected $repository;
+
+    /**
      * The document template object
      *
      * Needs to be a local variable of the class.
@@ -50,12 +63,13 @@ class WizardView extends \TYPO3\CMS\Form\View\Wizard\AbstractWizardView
      *
      * Defines the document template object.
      *
-     * @param \TYPO3\CMS\Form\Domain\Repository\ContentRepository $repository
+     * @param ContentRepository $repository
      * @see \TYPO3\CMS\Backend\Template\DocumentTemplate
      */
-    public function __construct(\TYPO3\CMS\Form\Domain\Repository\ContentRepository $repository)
+    public function __construct(ContentRepository $repository)
     {
-        parent::__construct($repository);
+        $this->setRepository($repository);
+        $this->getLanguageService()->includeLLFile('EXT:form/Resources/Private/Language/locallang_wizard.xlf');
         $GLOBALS['SOBE'] = $this;
         // Define the document template object
         $this->doc = GeneralUtility::makeInstance(\TYPO3\CMS\Backend\Template\DocumentTemplate::class);
@@ -63,6 +77,16 @@ class WizardView extends \TYPO3\CMS\Form\View\Wizard\AbstractWizardView
     }
 
     /**
+     * Sets the content repository to be used.
+     *
+     * @param ContentRepository $repository
+     * @return void
+     */
+    public function setRepository(ContentRepository $repository)
+    {
+        $this->repository = $repository;
+    }
+    /**
      * The main render method
      *
      * Gathers all content and echos it to the screen
@@ -377,4 +401,14 @@ class WizardView extends \TYPO3\CMS\Form\View\Wizard\AbstractWizardView
 
         return $this->pageRenderer;
     }
+
+    /**
+     * Returns an instance of LanguageService
+     *
+     * @return \TYPO3\CMS\Lang\LanguageService
+     */
+    protected function getLanguageService()
+    {
+        return $GLOBALS['LANG'];
+    }
 }
diff --git a/typo3/sysext/form/Configuration/Backend/AjaxRoutes.php b/typo3/sysext/form/Configuration/Backend/AjaxRoutes.php
new file mode 100644 (file)
index 0000000..6942f36
--- /dev/null
@@ -0,0 +1,19 @@
+<?php
+use TYPO3\CMS\Form\Controller\WizardController;
+
+/**
+ * Definitions for AJAX routes provided by EXT:form
+ */
+return [
+    // Loads the current form wizard data
+    'formwizard_load' => [
+        'path' => '/wizard/form/load',
+        'target' => WizardController::class . '::loadAction'
+    ],
+
+    // Save the current form wizard
+    'formwizard_save' => [
+        'path' => '/wizard/form/save',
+        'target' => WizardController::class . '::saveAction'
+    ],
+];
index cf40901..e885c0f 100644 (file)
@@ -16,4 +16,4 @@
  * @var $wizard \TYPO3\CMS\Form\Controller\WizardController
  */
 $wizard = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(\TYPO3\CMS\Form\Controller\WizardController::class);
-$wizard->dispatch();
+$wizard->indexAction();
index c9057e5..2b9ee09 100644 (file)
@@ -153,12 +153,13 @@ TYPO3.Form.Wizard.Viewport = Ext.extend(Ext.Container, {
         */
        save: function(event, element, object) {
                var configuration = Ext.getCmp('formwizard-right').getConfiguration();
+               var url = document.location.href.substring(document.location.href.indexOf('&P'));
+               url = TYPO3.settings.ajaxUrls['formwizard_save'] + url;
 
                Ext.Ajax.request({
-                       url: document.location.href,
+                       url: url,
                        method: 'POST',
                        params: {
-                               action: 'save',
                                configuration: Ext.encode(configuration)
                        },
                        success: function(response, opts) {
@@ -169,7 +170,6 @@ TYPO3.Form.Wizard.Viewport = Ext.extend(Ext.Container, {
                                );
                        },
                        failure: function(response, opts) {
-                               var responseObject = Ext.decode(response.responseText);
                                Ext.MessageBox.alert(
                                        TYPO3.l10n.localize('action_save'),
                                        TYPO3.l10n.localize('action_save_error') + ' ' + response.status
@@ -188,12 +188,12 @@ TYPO3.Form.Wizard.Viewport = Ext.extend(Ext.Container, {
         */
        saveAndClose: function(event, element, object) {
                var configuration = Ext.getCmp('formwizard-right').getConfiguration();
-
+               var url = document.location.href.substring(document.location.href.indexOf('&P'));
+               url = TYPO3.settings.ajaxUrls['formwizard_save'] + url;
                Ext.Ajax.request({
-                       url: document.location.href,
+                       url: url,
                        method: 'POST',
                        params: {
-                               action: 'save',
                                configuration: Ext.encode(configuration)
                        },
                        success: function(response, opts) {
@@ -201,7 +201,6 @@ TYPO3.Form.Wizard.Viewport = Ext.extend(Ext.Container, {
                                document.location = urlParameters['P[returnUrl]'];
                        },
                        failure: function(response, opts) {
-                               var responseObject = Ext.decode(response.responseText);
                                Ext.MessageBox.alert(
                                        TYPO3.l10n.localize('action_save'),
                                        TYPO3.l10n.localize('action_save_error') + ' ' + response.status
index 0fa176b..b70fb78 100644 (file)
@@ -80,19 +80,17 @@ TYPO3.Form.Wizard.Viewport.Right = Ext.extend(Ext.Container, {
         * Loads the configuration and initializes the history
         */
        loadForm: function() {
+               var url = document.location.href.substring(document.location.href.indexOf('&P'));
+               url = TYPO3.settings.ajaxUrls['formwizard_load'] + url;
                Ext.Ajax.request({
-                       url: document.location.href,
+                       url: url,
                        method: 'POST',
-                       params: {
-                               action: 'load'
-                       },
                        success: function(response, opts) {
                                var responseObject = Ext.decode(response.responseText);
                                this.loadConfiguration(responseObject.configuration);
                                this.initializeHistory();
                        },
                        failure: function(response, opts) {
-                               var responseObject = Ext.decode(response.responseText);
                                Ext.MessageBox.alert(
                                        'Loading form',
                                        'Server-side failure with status code ' + response.status
@@ -158,4 +156,4 @@ TYPO3.Form.Wizard.Viewport.Right = Ext.extend(Ext.Container, {
        }
 });
 
-Ext.reg('typo3-form-wizard-viewport-right', TYPO3.Form.Wizard.Viewport.Right);
\ No newline at end of file
+Ext.reg('typo3-form-wizard-viewport-right', TYPO3.Form.Wizard.Viewport.Right);