[!!!][FEATURE] Introduce PSR-7-based Routing for Backend AJAX Requests 65/43365/16
authorBenjamin Mack <benni@typo3.org>
Wed, 16 Sep 2015 06:19:51 +0000 (08:19 +0200)
committerAndreas Fernandez <typo3@scripting-base.de>
Mon, 5 Oct 2015 09:34:02 +0000 (11:34 +0200)
The AjaxRequestHandler now first checks in the Router if an AJAX
route exists. A new flag "ajax" in the routing mechanism allows to call
ajax-based URLs which are then handed to the AJAX Request Handler.

All controllers now receive proper Request and Response objects.

All previous logic still works, but can slowly be migrated to the Routing
concept.

Resolves: #69916
Releases: master
Change-Id: I1e67d5a341a4dd2769247531246c9e1fad900c76
Reviewed-on: http://review.typo3.org/43365
Reviewed-by: Benni Mack <benni@typo3.org>
Tested-by: Benni Mack <benni@typo3.org>
Reviewed-by: Wouter Wolters <typo3@wouterwolters.nl>
Tested-by: Wouter Wolters <typo3@wouterwolters.nl>
Reviewed-by: Andreas Fernandez <typo3@scripting-base.de>
Tested-by: Andreas Fernandez <typo3@scripting-base.de>
92 files changed:
typo3/sysext/backend/Classes/AjaxLoginHandler.php
typo3/sysext/backend/Classes/Backend/ToolbarItems/ShortcutToolbarItem.php
typo3/sysext/backend/Classes/Backend/ToolbarItems/SystemInformationToolbarItem.php
typo3/sysext/backend/Classes/Controller/BackendController.php
typo3/sysext/backend/Classes/Controller/ClickMenuController.php
typo3/sysext/backend/Classes/Controller/File/FileController.php
typo3/sysext/backend/Classes/Controller/FileSystemNavigationFrameController.php
typo3/sysext/backend/Classes/Controller/FormInlineAjaxController.php
typo3/sysext/backend/Classes/Controller/LiveSearchController.php
typo3/sysext/backend/Classes/Controller/OnlineMediaController.php
typo3/sysext/backend/Classes/Controller/PageTreeNavigationController.php
typo3/sysext/backend/Classes/Controller/SimpleDataHandlerController.php
typo3/sysext/backend/Classes/Controller/UserSettingsController.php
typo3/sysext/backend/Classes/Form/Element/ImageManipulationElement.php
typo3/sysext/backend/Classes/Form/Wizard/ImageManipulationWizard.php
typo3/sysext/backend/Classes/Form/Wizard/SuggestWizard.php
typo3/sysext/backend/Classes/Http/AjaxRequestHandler.php
typo3/sysext/backend/Classes/Http/RouteDispatcher.php
typo3/sysext/backend/Classes/Routing/UriBuilder.php
typo3/sysext/backend/Classes/Template/DocumentTemplate.php
typo3/sysext/backend/Classes/Utility/BackendUtility.php
typo3/sysext/backend/Configuration/Backend/AjaxRoutes.php [new file with mode: 0644]
typo3/sysext/backend/Configuration/Backend/Routes.php
typo3/sysext/backend/Resources/Public/JavaScript/AjaxDataHandler.js
typo3/sysext/backend/Resources/Public/JavaScript/ClickMenu.js
typo3/sysext/backend/Resources/Public/JavaScript/ContextHelp.js
typo3/sysext/backend/Resources/Public/JavaScript/DragUploader.js
typo3/sysext/backend/Resources/Public/JavaScript/FormEngineSuggest.js
typo3/sysext/backend/Resources/Public/JavaScript/LegacyTree.js
typo3/sysext/backend/Resources/Public/JavaScript/LiveSearch.js
typo3/sysext/backend/Resources/Public/JavaScript/LoginRefresh.js
typo3/sysext/backend/Resources/Public/JavaScript/OnlineMedia.js
typo3/sysext/backend/Resources/Public/JavaScript/Storage.js
typo3/sysext/backend/Resources/Public/JavaScript/Toolbar/ShortcutMenu.js
typo3/sysext/backend/Resources/Public/JavaScript/Toolbar/SystemInformationMenu.js
typo3/sysext/backend/Resources/Public/JavaScript/jsfunc.inline.js
typo3/sysext/backend/Resources/Public/JavaScript/modulemenu.js
typo3/sysext/backend/Tests/Unit/Controller/File/FileControllerTest.php
typo3/sysext/beuser/Classes/Controller/PermissionAjaxController.php
typo3/sysext/beuser/Configuration/Backend/AjaxRoutes.php [new file with mode: 0644]
typo3/sysext/beuser/Resources/Public/JavaScript/Permissions.js
typo3/sysext/beuser/ext_tables.php
typo3/sysext/context_help/Classes/Controller/ContextHelpAjaxController.php
typo3/sysext/context_help/Configuration/Backend/AjaxRoutes.php [new file with mode: 0644]
typo3/sysext/context_help/ext_tables.php
typo3/sysext/core/Classes/Core/Bootstrap.php
typo3/sysext/core/Classes/ExtDirect/ExtDirectApi.php
typo3/sysext/core/Classes/ExtDirect/ExtDirectRouter.php
typo3/sysext/core/Classes/Page/PageRenderer.php
typo3/sysext/core/Configuration/DefaultConfiguration.php
typo3/sysext/core/Documentation/Changelog/7.4/Feature-67545-AJAXMethodToCheckIfFileExists.rst
typo3/sysext/core/Documentation/Changelog/master/Breaking-69916-HookAjaxSaveCodeOfT3editorChanged.rst [new file with mode: 0644]
typo3/sysext/core/Documentation/Changelog/master/Breaking-69916-RegisteredAJAXHandlersReplacedByRoutes.rst [new file with mode: 0644]
typo3/sysext/core/Documentation/Changelog/master/Breaking-69916-RemovedBackendLogingetRsaPublicKeyAJAXHandler.rst [new file with mode: 0644]
typo3/sysext/core/Documentation/Changelog/master/Feature-69916-PSR-7-basedRoutingForBackendAJAXRequests.rst [new file with mode: 0644]
typo3/sysext/opendocs/Classes/Backend/ToolbarItems/OpendocsToolbarItem.php
typo3/sysext/opendocs/Configuration/Backend/AjaxRoutes.php [new file with mode: 0644]
typo3/sysext/opendocs/Resources/Public/JavaScript/Toolbar/OpendocsMenu.js
typo3/sysext/opendocs/ext_tables.php
typo3/sysext/recordlist/Classes/Browser/ElementBrowser.php
typo3/sysext/recycler/Classes/Controller/RecyclerAjaxController.php
typo3/sysext/recycler/Configuration/Backend/AjaxRoutes.php [new file with mode: 0644]
typo3/sysext/recycler/Resources/Public/JavaScript/Recycler.js
typo3/sysext/recycler/ext_localconf.php
typo3/sysext/rsaauth/Classes/Backend/AjaxLoginHandler.php [deleted file]
typo3/sysext/rsaauth/Classes/RsaEncryptionEncoder.php
typo3/sysext/rsaauth/Configuration/Backend/AjaxRoutes.php [new file with mode: 0644]
typo3/sysext/rsaauth/Resources/Public/JavaScript/RsaEncryptionModule.js
typo3/sysext/rsaauth/ext_localconf.php
typo3/sysext/rtehtmlarea/Classes/BrowseLinks.php
typo3/sysext/rtehtmlarea/Classes/Controller/SpellCheckingController.php
typo3/sysext/rtehtmlarea/Classes/Extension/Spellchecker.php
typo3/sysext/rtehtmlarea/Classes/SelectImage.php
typo3/sysext/rtehtmlarea/Configuration/Backend/Routes.php
typo3/sysext/rtehtmlarea/ext_localconf.php
typo3/sysext/t3editor/Classes/CodeCompletion.php
typo3/sysext/t3editor/Classes/Hook/FileEditHook.php
typo3/sysext/t3editor/Classes/T3editor.php
typo3/sysext/t3editor/Classes/TypoScriptReferenceLoader.php
typo3/sysext/t3editor/Configuration/Backend/AjaxRoutes.php [new file with mode: 0644]
typo3/sysext/t3editor/Resources/Public/JavaScript/Plugins/CodeCompletion/TsCodeCompletion.js
typo3/sysext/t3editor/Resources/Public/JavaScript/Plugins/CodeCompletion/TsRef.js
typo3/sysext/t3editor/Resources/Public/JavaScript/T3editor.js
typo3/sysext/t3editor/ext_tables.php [deleted file]
typo3/sysext/taskcenter/Classes/TaskStatus.php
typo3/sysext/taskcenter/Configuration/Backend/AjaxRoutes.php [new file with mode: 0644]
typo3/sysext/taskcenter/Resources/Public/JavaScript/Taskcenter.js
typo3/sysext/taskcenter/ext_tables.php
typo3/sysext/workspaces/Classes/Controller/AjaxController.php
typo3/sysext/workspaces/Configuration/Backend/AjaxRoutes.php [new file with mode: 0644]
typo3/sysext/workspaces/Resources/Public/JavaScript/Toolbar/WorkspacesMenu.js
typo3/sysext/workspaces/ext_tables.php

index f32e546..86c3c77 100644 (file)
@@ -14,8 +14,9 @@ namespace TYPO3\CMS\Backend;
  * The TYPO3 project - inspiring people to share!
  */
 
+use Psr\Http\Message\ResponseInterface;
+use Psr\Http\Message\ServerRequestInterface;
 use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
-use TYPO3\CMS\Core\Http\AjaxRequestHandler;
 
 /**
  * This is the ajax handler for backend login after timeout.
@@ -29,102 +30,111 @@ class AjaxLoginHandler {
         * a BE user and reset the timer and hide the login window.
         * If it was unsuccessful, we display that and show the login box again.
         *
-        * @param array $parameters Parameters (not used)
-        * @param AjaxRequestHandler $ajaxObj The calling parent AJAX object
-        * @return void
+        * @param ServerRequestInterface $request
+        * @param ResponseInterface $response
+        * @return ResponseInterface
         */
-       public function login(array $parameters, AjaxRequestHandler $ajaxObj) {
+       public function loginAction(ServerRequestInterface $request, ResponseInterface $response) {
                if ($this->isAuthorizedBackendSession()) {
-                       $json = array('success' => TRUE);
+                       $result = ['success' => TRUE];
                        if ($this->hasLoginBeenProcessed()) {
                                $formProtection = \TYPO3\CMS\Core\FormProtection\FormProtectionFactory::get();
                                $formProtection->setSessionTokenFromRegistry();
                                $formProtection->persistSessionToken();
                        }
                } else {
-                       $json = array('success' => FALSE);
+                       $result = ['success' => FALSE];
                }
-               $ajaxObj->addContent('login', $json);
-               $ajaxObj->setContentFormat('json');
-       }
 
-       /**
-        * Checks if a user is logged in and the session is active.
-        *
-        * @return bool
-        */
-       protected function isAuthorizedBackendSession() {
-               $backendUser = $this->getBackendUser();
-               return $backendUser !== NULL && $backendUser instanceof BackendUserAuthentication && isset($backendUser->user['uid']);
-       }
-
-       /**
-        * Check whether the user was already authorized or not
-        *
-        * @return bool
-        */
-       protected function hasLoginBeenProcessed() {
-               $loginFormData = $this->getBackendUser()->getLoginFormData();
-               return $loginFormData['status'] === 'login' && !empty($loginFormData['uname']) && !empty($loginFormData['uident']);
+               $response->getBody()->write(json_encode(['login' => $result]));
+               return $response;
        }
 
        /**
         * Logs out the current BE user
         *
-        * @param array $parameters Parameters (not used)
-        * @param AjaxRequestHandler $ajaxObj The calling parent AJAX object
-        * @return void
+        * @param ServerRequestInterface $request
+        * @param ResponseInterface $response
+        * @return ResponseInterface
         */
-       public function logout(array $parameters, AjaxRequestHandler $ajaxObj) {
+       public function logoutAction(ServerRequestInterface $request, ResponseInterface $response) {
                $backendUser = $this->getBackendUser();
                $backendUser->logoff();
-               $ajaxObj->addContent('logout', array(
-                       'success' => !isset($backendUser->user['uid']))
-               );
-               $ajaxObj->setContentFormat('json');
+
+               $response->getBody()->write(json_encode([
+                       'logout' => [
+                               'success' => !isset($backendUser->user['uid'])
+                       ]
+               ]));
+               return $response;
        }
 
        /**
         * Refreshes the login without needing login information. We just refresh the session.
         *
-        * @param array $parameters Parameters (not used)
-        * @param AjaxRequestHandler $ajaxObj The calling parent AJAX object
-        * @return void
+        * @param ServerRequestInterface $request
+        * @param ResponseInterface $response
+        * @return ResponseInterface
         */
-       public function refreshLogin(array $parameters, AjaxRequestHandler $ajaxObj) {
+       public function refreshAction(ServerRequestInterface $request, ResponseInterface $response) {
                $this->getBackendUser()->checkAuthentication();
-               $ajaxObj->addContent('refresh', array('success' => TRUE));
-               $ajaxObj->setContentFormat('json');
+
+               $response->getBody()->write(json_encode([
+                       'refresh' => [
+                               'success' => TRUE
+                       ]
+               ]));
+               return $response;
        }
 
        /**
         * Checks if the user session is expired yet
         *
-        * @param array $parameters Parameters (not used)
-        * @param AjaxRequestHandler $ajaxObj The calling parent AJAX object
-        * @return void
+        * @param ServerRequestInterface $request
+        * @param ResponseInterface $response
+        * @return ResponseInterface
         */
-       public function isTimedOut(array $parameters, AjaxRequestHandler $ajaxObj) {
-               $ajaxObj->setContentFormat('json');
-               $response = array(
+       public function isTimedOutAction(ServerRequestInterface $request, ResponseInterface $response) {
+               $session = [
                        'timed_out' => FALSE,
                        'will_time_out' => FALSE,
                        'locked' => FALSE
-               );
+               ];
                $backendUser = $this->getBackendUser();
                if (@is_file(PATH_typo3conf . 'LOCK_BACKEND')) {
-                       $response['locked'] = TRUE;
+                       $session['locked'] = TRUE;
                } elseif (!isset($backendUser->user['uid'])) {
-                       $response['timed_out'] = TRUE;
+                       $session['timed_out'] = TRUE;
                } else {
                        $backendUser->fetchUserSession(TRUE);
                        $ses_tstamp = $backendUser->user['ses_tstamp'];
                        $timeout = $backendUser->auth_timeout_field;
                        // If 120 seconds from now is later than the session timeout, we need to show the refresh dialog.
                        // 120 is somewhat arbitrary to allow for a little room during the countdown and load times, etc.
-                       $response['will_time_out'] = $GLOBALS['EXEC_TIME'] >= $ses_tstamp + $timeout - 120;
+                       $session['will_time_out'] = $GLOBALS['EXEC_TIME'] >= $ses_tstamp + $timeout - 120;
                }
-               $ajaxObj->addContent('login', $response);
+               $response->getBody()->write(json_encode(['login' => $session]));
+               return $response;
+       }
+
+       /**
+        * Checks if a user is logged in and the session is active.
+        *
+        * @return bool
+        */
+       protected function isAuthorizedBackendSession() {
+               $backendUser = $this->getBackendUser();
+               return $backendUser !== NULL && $backendUser instanceof BackendUserAuthentication && isset($backendUser->user['uid']);
+       }
+
+       /**
+        * Check whether the user was already authorized or not
+        *
+        * @return bool
+        */
+       protected function hasLoginBeenProcessed() {
+               $loginFormData = $this->getBackendUser()->getLoginFormData();
+               return $loginFormData['status'] === 'login' && !empty($loginFormData['uname']) && !empty($loginFormData['uident']);
        }
 
        /**
index 8052c40..b5d9828 100644 (file)
@@ -14,11 +14,12 @@ namespace TYPO3\CMS\Backend\Backend\ToolbarItems;
  * The TYPO3 project - inspiring people to share!
  */
 
+use Psr\Http\Message\ResponseInterface;
+use Psr\Http\Message\ServerRequestInterface;
 use TYPO3\CMS\Backend\Module\ModuleLoader;
 use TYPO3\CMS\Backend\Toolbar\ToolbarItemInterface;
 use TYPO3\CMS\Backend\Utility\BackendUtility;
 use TYPO3\CMS\Core\Database\PreparedStatement;
-use TYPO3\CMS\Core\Http\AjaxRequestHandler;
 use TYPO3\CMS\Core\Imaging\Icon;
 use TYPO3\CMS\Core\Imaging\IconFactory;
 use TYPO3\CMS\Core\Page\PageRenderer;
@@ -193,13 +194,16 @@ class ShortcutToolbarItem implements ToolbarItemInterface {
        /**
         * Renders the menu so that it can be returned as response to an AJAX call
         *
-        * @param array $params Array of parameters from the AJAX interface, currently unused
-        * @param \TYPO3\CMS\Core\Http\AjaxRequestHandler $ajaxObj Object of type AjaxRequestHandler
-        * @return void
+        * @param ServerRequestInterface $request
+        * @param ResponseInterface $response
+        * @return ResponseInterface
         */
-       public function renderAjaxMenu($params = array(), AjaxRequestHandler $ajaxObj = NULL) {
+       public function menuAction(ServerRequestInterface $request, ResponseInterface $response) {
                $menuContent = $this->getDropDown();
-               $ajaxObj->addContent('shortcutMenu', $menuContent);
+
+               $response->getBody()->write($menuContent);
+               $response = $response->withHeader('Content-Type', 'text/html; charset=utf-8');
+               return $response;
        }
 
        /**
@@ -417,15 +421,18 @@ class ShortcutToolbarItem implements ToolbarItemInterface {
        }
 
        /**
-        * gets the available shortcut groups, renders a form so it can be saved lateron
+        * Fetches the available shortcut groups, renders a form so it can be saved later on, usually called via AJAX
         *
-        * @param array $params Array of parameters from the AJAX interface, currently unused
-        * @param \TYPO3\CMS\Core\Http\AjaxRequestHandler $ajaxObj Object of type AjaxRequestHandler
-        * @return void
+        * @param ServerRequestInterface $request
+        * @param ResponseInterface $response
+        * @return ResponseInterface the full HTML for the form
         */
-       public function getAjaxShortcutEditForm($params = array(), AjaxRequestHandler $ajaxObj = NULL) {
-               $selectedShortcutId = (int)GeneralUtility::_GP('shortcutId');
-               $selectedShortcutGroupId = (int)GeneralUtility::_GP('shortcutGroup');
+       public function editFormAction(ServerRequestInterface $request, ResponseInterface $response) {
+               $parsedBody = $request->getParsedBody();
+               $queryParams = $request->getQueryParams();
+
+               $selectedShortcutId = (int)(isset($parsedBody['shortcutId']) ? $parsedBody['shortcutId'] : $queryParams['shortcutId']);
+               $selectedShortcutGroupId = (int)(isset($parsedBody['shortcutGroup']) ? $parsedBody['shortcutGroup'] : $queryParams['shortcutGroup']);
                $selectedShortcut = $this->getShortcutById($selectedShortcutId);
 
                $shortcutGroups = $this->shortcutGroups;
@@ -457,45 +464,52 @@ class ShortcutToolbarItem implements ToolbarItemInterface {
                                <input type="button" class="btn btn-success shortcut-form-save" value="Save">
                        </form>';
 
-               $ajaxObj->addContent('data', $content);
+               $response->getBody()->write($content);
+               $response = $response->withHeader('Content-Type', 'text/html; charset=utf-8');
+               return $response;
        }
 
        /**
         * Deletes a shortcut through an AJAX call
         *
-        * @param array $params Array of parameters from the AJAX interface, currently unused
-        * @param \TYPO3\CMS\Core\Http\AjaxRequestHandler $ajaxObj Object of type AjaxRequestHandler
-        * @return void
+        * @param ServerRequestInterface $request
+        * @param ResponseInterface $response
+        * @return ResponseInterface
         */
-       public function deleteAjaxShortcut($params = array(), AjaxRequestHandler $ajaxObj = NULL) {
+       public function removeShortcutAction(ServerRequestInterface $request, ResponseInterface $response) {
+               $parsedBody = $request->getParsedBody();
+               $queryParams = $request->getQueryParams();
+
                $databaseConnection = $this->getDatabaseConnection();
-               $shortcutId = (int)GeneralUtility::_POST('shortcutId');
+               $shortcutId = (int)(isset($parsedBody['shortcutId']) ? $parsedBody['shortcutId'] : $queryParams['shortcutId']);
                $fullShortcut = $this->getShortcutById($shortcutId);
-               $ajaxReturn = 'failed';
+               $success = FALSE;
                if ($fullShortcut['raw']['userid'] == $this->getBackendUser()->user['uid']) {
                        $databaseConnection->exec_DELETEquery('sys_be_shortcuts', 'uid = ' . $shortcutId);
-                       if ($databaseConnection->sql_affected_rows() == 1) {
-                               $ajaxReturn = 'deleted';
+                       if ($databaseConnection->sql_affected_rows() === 1) {
+                               $success = TRUE;
                        }
                }
-               $ajaxObj->addContent('delete', $ajaxReturn);
+               $response->getBody()->write(json_encode(['success' => $success]));
+               return $response;
        }
 
        /**
         * Creates a shortcut through an AJAX call
         *
-        * @param array $params Array of parameters from the AJAX interface, currently unused
-        * @param AjaxRequestHandler $ajaxObj Oject of type AjaxRequestHandler
-        *
-        * @return void
+        * @param ServerRequestInterface $request
+        * @param ResponseInterface $response
+        * @return ResponseInterface
         */
-       public function createAjaxShortcut($params = array(), AjaxRequestHandler $ajaxObj = NULL) {
+       public function createShortcutAction(ServerRequestInterface $request, ResponseInterface $response) {
                $languageService = $this->getLanguageService();
+               $parsedBody = $request->getParsedBody();
+               $queryParams = $request->getQueryParams();
 
                // Default name
                $shortcutName = 'Shortcut';
                $shortcutNamePrepend = '';
-               $url = GeneralUtility::_POST('url');
+               $url = isset($parsedBody['url']) ? $parsedBody['url'] : $queryParams['url'];
 
                // Determine shortcut type
                $url = rawurldecode($url);
@@ -545,20 +559,19 @@ class ShortcutToolbarItem implements ToolbarItemInterface {
                                }
                        }
 
-                       $this->tryAddingTheShortcut($ajaxObj, $url, $shortcutName);
+                       return $this->tryAddingTheShortcut($response, $url, $shortcutName);
                }
        }
 
        /**
         * Try to adding a shortcut
         *
-        * @param AjaxRequestHandler $ajaxObj Oject of type AjaxRequestHandler
+        * @param ResponseInterface $response
         * @param string $url
         * @param string $shortcutName
-        *
-        * @return void
+        * @return ResponseInterface
         */
-       protected function tryAddingTheShortcut(AjaxRequestHandler $ajaxObj, $url, $shortcutName) {
+       protected function tryAddingTheShortcut(ResponseInterface $response, $url, $shortcutName) {
                $module = GeneralUtility::_POST('module');
                $shortcutCreated = 'failed';
 
@@ -570,7 +583,9 @@ class ShortcutToolbarItem implements ToolbarItemInterface {
                        }
                }
 
-               $ajaxObj->addContent('create', $shortcutCreated);
+               $response->getBody()->write($shortcutCreated);
+               $response = $response->withHeader('Content-Type', 'text/html; charset=utf-8');
+               return $response;
        }
 
        /**
@@ -640,16 +655,19 @@ class ShortcutToolbarItem implements ToolbarItemInterface {
         * Gets called when a shortcut is changed, checks whether the user has
         * permissions to do so and saves the changes if everything is ok
         *
-        * @param array $params Array of parameters from the AJAX interface, currently unused
-        * @param \TYPO3\CMS\Core\Http\AjaxRequestHandler $ajaxObj Object of type AjaxRequestHandler
-        * @return void
+        * @param ServerRequestInterface $request
+        * @param ResponseInterface $response
+        * @return ResponseInterface
         */
-       public function setAjaxShortcut($params = array(), AjaxRequestHandler $ajaxObj = NULL) {
+       public function saveFormAction(ServerRequestInterface $request, ResponseInterface $response) {
+               $parsedBody = $request->getParsedBody();
+               $queryParams = $request->getQueryParams();
+
                $databaseConnection = $this->getDatabaseConnection();
                $backendUser = $this->getBackendUser();
-               $shortcutId = (int)GeneralUtility::_POST('shortcutId');
-               $shortcutName = strip_tags(GeneralUtility::_POST('shortcutTitle'));
-               $shortcutGroupId = (int)GeneralUtility::_POST('shortcutGroup');
+               $shortcutId = (int)(isset($parsedBody['shortcutId']) ? $parsedBody['shortcutId'] : $queryParams['shortcutId']);
+               $shortcutName = strip_tags(isset($parsedBody['shortcutTitle']) ? $parsedBody['shortcutTitle'] : $queryParams['shortcutTitle']);
+               $shortcutGroupId = (int)(isset($parsedBody['shortcutGroup']) ? $parsedBody['shortcutGroup'] : $queryParams['shortcutGroup']);
                // Users can only modify their own shortcuts (except admins)
                $addUserWhere = !$backendUser->isAdmin() ? ' AND userid=' . (int)$backendUser->user['uid'] : '';
                $fieldValues = array(
@@ -662,11 +680,11 @@ class ShortcutToolbarItem implements ToolbarItemInterface {
                $databaseConnection->exec_UPDATEquery('sys_be_shortcuts', 'uid=' . $shortcutId . $addUserWhere, $fieldValues);
                $affectedRows = $databaseConnection->sql_affected_rows();
                if ($affectedRows == 1) {
-                       $ajaxObj->addContent('shortcut', $shortcutName);
+                       $response->getBody()->write($shortcutName);
                } else {
-                       $ajaxObj->addContent('shortcut', 'failed');
+                       $response->getBody()->write('failed');
                }
-               $ajaxObj->setContentFormat('plain');
+               return $response->withHeader('Content-Type', 'html');
        }
 
        /**
index c6c5d08..ddad1e8 100644 (file)
@@ -14,13 +14,14 @@ namespace TYPO3\CMS\Backend\Backend\ToolbarItems;
  * The TYPO3 project - inspiring people to share!
  */
 
+use Psr\Http\Message\ResponseInterface;
+use Psr\Http\Message\ServerRequestInterface;
 use TYPO3\CMS\Backend\Toolbar\ToolbarItemInterface;
 use TYPO3\CMS\Backend\Toolbar\Enumeration\InformationStatus;
 use TYPO3\CMS\Backend\Utility\BackendUtility;
 use TYPO3\CMS\Core\Imaging\Icon;
 use TYPO3\CMS\Core\Imaging\IconFactory;
-use TYPO3\CMS\Core\Http\AjaxRequestHandler;
-use \TYPO3\CMS\Core\Page\PageRenderer;
+use TYPO3\CMS\Core\Page\PageRenderer;
 use TYPO3\CMS\Core\Utility\CommandUtility;
 use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
@@ -122,12 +123,16 @@ class SystemInformationToolbarItem implements ToolbarItemInterface {
        /**
         * Renders the menu for AJAX calls
         *
-        * @param array $params
-        * @param AjaxRequestHandler $ajaxObj
+        * @param ServerRequestInterface $request
+        * @param ResponseInterface $response
+        * @return ResponseInterface
         */
-       public function renderAjax($params = array(), $ajaxObj) {
+       public function renderMenuAction(ServerRequestInterface $request, ResponseInterface $response) {
                $this->collectInformation();
-               $ajaxObj->addContent('systemInformationMenu', $this->getDropDown());
+
+               $response->getBody()->write($this->getDropDown());
+               $response = $response->withHeader('Content-Type', 'text/html; charset=utf-8');
+               return $response;
        }
 
        /**
index d8567bc..074c1b3 100644 (file)
@@ -866,16 +866,17 @@ class BackendController {
        }
 
        /**
-        * Returns the Module menu for the AJAX API
+        * Returns the Module menu for the AJAX request
         *
-        * @param array $params
-        * @param \TYPO3\CMS\Core\Http\AjaxRequestHandler $ajaxRequestHandler
-        * @return void
+        * @param ServerRequestInterface $request
+        * @param ResponseInterface $response
+        * @return ResponseInterface
         */
-       public function getModuleMenuForReload($params, $ajaxRequestHandler) {
+       public function getModuleMenu(ServerRequestInterface $request, ResponseInterface $response) {
                $content = $this->generateModuleMenu();
-               $ajaxRequestHandler->addContent('menu', $content);
-               $ajaxRequestHandler->setContentFormat('json');
+
+               $response->getBody()->write(json_encode(['menu' => $content]));
+               return $response;
        }
 
        /**
index eb0cfeb..c15067a 100644 (file)
@@ -14,11 +14,12 @@ namespace TYPO3\CMS\Backend\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\Backend\Template\DocumentTemplate;
 use TYPO3\CMS\Backend\Clipboard\Clipboard;
 use TYPO3\CMS\Backend\ClickMenu\ClickMenu;
-use TYPO3\CMS\Core\Http\AjaxRequestHandler;
 
 /**
  * Script Class for the Context Sensitive Menu in TYPO3 (rendered in top frame, normally writing content dynamically to list frames).
@@ -125,10 +126,11 @@ class ClickMenuController {
        /**
         * this is an intermediate clickmenu handler
         *
-        * @param array $parameters
-        * @param \TYPO3\CMS\Core\Http\AjaxRequestHandler $ajaxRequestHandler
+        * @param ServerRequestInterface $request
+        * @param ResponseInterface $response
+        * @return ResponseInterface
         */
-       public function printContentForAjaxRequest($parameters, AjaxRequestHandler $ajaxRequestHandler) {
+       public function getContextMenuAction(ServerRequestInterface $request, ResponseInterface $response) {
 
                // XML has to be parsed, no parse errors allowed
                @ini_set('display_errors', 0);
@@ -154,8 +156,9 @@ class ClickMenuController {
 
                // send the data
                $ajaxContent = '<?xml version="1.0"?><t3ajax>' . $ajaxContent . '</t3ajax>';
-               $ajaxRequestHandler->addContent('ClickMenu', $ajaxContent);
-               $ajaxRequestHandler->setContentFormat('xml');
+               $response->getBody()->write($ajaxContent);
+               $response = $response->withHeader('Content-Type', 'text/xml; charset=utf-8');
+               return $response;
        }
 
        /**
index f6ecfd5..1ccc778 100644 (file)
@@ -16,7 +16,6 @@ namespace TYPO3\CMS\Backend\Controller\File;
 
 use Psr\Http\Message\ResponseInterface;
 use Psr\Http\Message\ServerRequestInterface;
-use TYPO3\CMS\Core\Http\AjaxRequestHandler;
 use TYPO3\CMS\Core\Imaging\Icon;
 use TYPO3\CMS\Core\Imaging\IconFactory;
 use TYPO3\CMS\Core\Resource\DuplicationBehavior;
@@ -183,8 +182,9 @@ class FileController {
                BackendUtility::setUpdateSignal('updateFolderTree');
 
                if ($this->redirect) {
-                       $response = $response->withHeader('Location', GeneralUtility::locationHeaderUrl($this->redirect));
-                       return $response->withStatus(303);
+                       return $response
+                                       ->withHeader('Location', GeneralUtility::locationHeaderUrl($this->redirect))
+                                       ->withStatus(303);
                } else {
                        // empty response
                        return $response;
@@ -197,15 +197,16 @@ class FileController {
         * but without calling the "finish" method, thus makes it simpler to deal with the
         * actual return value
         *
-        * @param array $params Always empty.
-        * @param AjaxRequestHandler $ajaxObj The AjaxRequestHandler object used to return content and set content types
-        * @return void
+        * @param ServerRequestInterface $request
+        * @param ResponseInterface $response
+        * @return ResponseInterface
         */
-       public function processAjaxRequest(array $params, AjaxRequestHandler $ajaxObj) {
+       public function processAjaxRequest(ServerRequestInterface $request, ResponseInterface $response) {
                $this->main();
                $errors = $this->fileProcessor->getErrorMessages();
                if (!empty($errors)) {
-                       $ajaxObj->setError(implode(',', $errors));
+                       $response->getBody()->write(implode(',', $errors));
+                       $response = $response->withHeader('Content-Type', 'text/html; charset=utf-8');
                } else {
                        $flatResult = array();
                        foreach ($this->fileData as $action => $results) {
@@ -219,24 +220,25 @@ class FileController {
                                        }
                                }
                        }
-                       $ajaxObj->addContent('result', $flatResult);
+                       $content = ['result' => $flatResult];
                        if ($this->redirect) {
-                               $ajaxObj->addContent('redirect', $this->redirect);
+                               $content['redirect'] = $this->redirect;
                        }
-                       $ajaxObj->setContentFormat('json');
+                       $response->getBody()->write(json_encode($content));
                }
+               return $response;
        }
 
        /**
         * Ajax entry point to check if a file exists in a folder
         *
-        * @param array $params Always empty.
-        * @param AjaxRequestHandler $ajaxObj The AjaxRequestHandler object used to return content and set content types
-        * @return void
+        * @param ServerRequestInterface $request
+        * @param ResponseInterface $response
+        * @return ResponseInterface
         */
-       public function fileExistsAjaxRequest(array $params, AjaxRequestHandler $ajaxObj) {
-               $fileName = GeneralUtility::_GP('fileName');
-               $fileTarget = GeneralUtility::_GP('fileTarget');
+       public function fileExistsInFolderAction(ServerRequestInterface $request, ResponseInterface $response) {
+               $fileName = isset($request->getParsedBody()['fileName']) ? $request->getParsedBody()['fileName'] : $request->getQueryParams()['fileName'];
+               $fileTarget = isset($request->getParsedBody()['fileTarget']) ? $request->getParsedBody()['fileTarget'] : $request->getQueryParams()['fileTarget'];
 
                /** @var \TYPO3\CMS\Core\Resource\ResourceFactory $fileFactory */
                $fileFactory = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Resource\ResourceFactory::class);
@@ -248,8 +250,8 @@ class FileController {
                if ($fileTargetObject->hasFile($processedFileName)) {
                        $result = $this->flattenResultDataValue($fileTargetObject->getStorage()->getFileInFolder($processedFileName, $fileTargetObject));
                }
-               $ajaxObj->addContent('result', $result);
-               $ajaxObj->setContentFormat('json');
+               $response->getBody()->write(json_encode($result));
+               return $response;
        }
 
        /**
index a05e8e6..b4c6651 100644 (file)
@@ -145,7 +145,7 @@ class FileSystemNavigationFrameController {
                $this->doc->showFlashMessages = FALSE;
                // Adding javascript code for drag&drop and the filetree as well as the click menu code
                $dragDropCode = '
-                       Tree.ajaxID = "SC_alt_file_navframe::expandCollapse";
+                       Tree.ajaxID = "sc_alt_file_navframe_expandtoggle";
                        Tree.registerDragDropHandlers()';
                if ($this->doHighlight) {
                        $hlClass = $this->getBackendUser()->workspace === 0 ? 'active' : 'active active-ws wsver' . $GLOBALS['BE_USER']->workspace;
@@ -239,17 +239,20 @@ class FileSystemNavigationFrameController {
         * Makes the AJAX call to expand or collapse the foldertree.
         * Called by typo3/ajax.php
         *
-        * @param array $params Additional parameters (not used here)
-        * @param \TYPO3\CMS\Core\Http\AjaxRequestHandler $ajaxObj The AjaxRequestHandler object of this request
-        * @return void
+        * @param ServerRequestInterface $request
+        * @param ResponseInterface $response
+        * @return ResponseInterface
         */
-       public function ajaxExpandCollapse($params, $ajaxObj) {
+       public function ajaxExpandCollapse(ServerRequestInterface $request, ResponseInterface $response) {
+               $this->init();
                $tree = $this->foldertree->getBrowsableTree();
                if ($this->foldertree->getAjaxStatus() === FALSE) {
-                       $ajaxObj->setError($tree);
+                       $response->withStatus(500);
                } else {
-                       $ajaxObj->addContent('tree', $tree);
+                       $response->getBody()->write(json_encode($tree));
                }
+
+               return $response;
        }
 
        /**
index 0167cfc..36a547d 100644 (file)
@@ -14,6 +14,8 @@ namespace TYPO3\CMS\Backend\Controller;
  * The TYPO3 project - inspiring people to share!
  */
 
+use Psr\Http\Message\ResponseInterface;
+use Psr\Http\Message\ServerRequestInterface;
 use TYPO3\CMS\Backend\Form\Exception\AccessDeniedException;
 use TYPO3\CMS\Backend\Form\FormDataCompiler;
 use TYPO3\CMS\Backend\Form\FormDataGroup\TcaDatabaseRecord;
@@ -23,7 +25,6 @@ use TYPO3\CMS\Backend\Form\NodeFactory;
 use TYPO3\CMS\Backend\Form\Utility\FormEngineUtility;
 use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
 use TYPO3\CMS\Core\DataHandling\DataHandler;
-use TYPO3\CMS\Core\Http\AjaxRequestHandler;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
 use TYPO3\CMS\Core\Utility\MathUtility;
 
@@ -38,61 +39,83 @@ class FormInlineAjaxController {
        protected $inlineStackProcessor;
 
        /**
-        * General processor for AJAX requests concerning IRRE.
-        *
-        * @param array $_ Additional parameters (not used here)
-        * @param AjaxRequestHandler $ajaxObj The AjaxRequestHandler object of this request
-        * @throws \RuntimeException
-        * @return void
+        * Create the inline stack processor
         */
-       public function processInlineAjaxRequest($_, AjaxRequestHandler $ajaxObj) {
+       public function __construct() {
                $this->inlineStackProcessor = GeneralUtility::makeInstance(InlineStackProcessor::class);
-               $ajaxArguments = GeneralUtility::_GP('ajax');
-               $ajaxIdParts = explode('::', $GLOBALS['ajaxID'], 2);
-               if (isset($ajaxArguments) && is_array($ajaxArguments) && !empty($ajaxArguments)) {
-                       $ajaxMethod = $ajaxIdParts[1];
-                       $ajaxObj->setContentFormat('jsonbody');
-                       // @todo: ajaxArguments[2] is "returnUrl" in the first 3 calls - still needed?
-                       switch ($ajaxMethod) {
-                               case 'synchronizeLocalizeRecords':
-                                       $domObjectId = $ajaxArguments[0];
-                                       $type = $ajaxArguments[1];
-                                       // Parse the DOM identifier (string), add the levels to the structure stack (array), load the TCA config:
-                                       $this->inlineStackProcessor->initializeByParsingDomObjectIdString($domObjectId);
-                                       $this->inlineStackProcessor->injectAjaxConfiguration($ajaxArguments['context']);
-                                       $inlineFirstPid = FormEngineUtility::getInlineFirstPidFromDomObjectId($domObjectId);
-                                       $ajaxObj->setContent($this->renderInlineSynchronizeLocalizeRecords($type, $inlineFirstPid));
-                                       break;
-                               case 'createNewRecord':
-                                       $domObjectId = $ajaxArguments[0];
-                                       $createAfterUid = 0;
-                                       if (isset($ajaxArguments[1])) {
-                                               $createAfterUid = $ajaxArguments[1];
-                                       }
-                                       // Parse the DOM identifier (string), add the levels to the structure stack (array), load the TCA config:
-                                       $this->inlineStackProcessor->initializeByParsingDomObjectIdString($domObjectId);
-                                       $this->inlineStackProcessor->injectAjaxConfiguration($ajaxArguments['context']);
-                                       $ajaxObj->setContent($this->renderInlineNewChildRecord($domObjectId, $createAfterUid));
-                                       break;
-                               case 'getRecordDetails':
-                                       $domObjectId = $ajaxArguments[0];
-                                       // Parse the DOM identifier (string), add the levels to the structure stack (array), load the TCA config:
-                                       $this->inlineStackProcessor->initializeByParsingDomObjectIdString($domObjectId);
-                                       $this->inlineStackProcessor->injectAjaxConfiguration($ajaxArguments['context']);
-                                       $ajaxObj->setContent($this->renderInlineChildRecord($domObjectId));
-                                       break;
-                               case 'setExpandedCollapsedState':
-                                       $domObjectId = $ajaxArguments[0];
-                                       // Parse the DOM identifier (string), add the levels to the structure stack (array), don't load TCA config
-                                       $this->inlineStackProcessor->initializeByParsingDomObjectIdString($domObjectId, FALSE);
-                                       $expand = $ajaxArguments[1];
-                                       $collapse = $ajaxArguments[2];
-                                       $this->setInlineExpandedCollapsedState($expand, $collapse);
-                                       break;
-                               default:
-                                       throw new \RuntimeException('Not a valid ajax identifier', 1428227862);
-                       }
-               }
+       }
+
+       /**
+        * Create a new inline child via AJAX.
+        *
+        * @param ServerRequestInterface $request
+        * @param ResponseInterface $response
+        * @return ResponseInterface
+        */
+       public function createAction(ServerRequestInterface $request, ResponseInterface $response) {
+               $ajaxArguments = isset($request->getParsedBody()['ajax']) ? $request->getParsedBody()['ajax'] : $request->getQueryParams()['ajax'];
+               $domObjectId = $ajaxArguments[0];
+               $createAfterUid = isset($ajaxArguments[1]) ? $ajaxArguments[1] : 0;
+               // Parse the DOM identifier (string), add the levels to the structure stack (array), load the TCA config:
+               $this->inlineStackProcessor->initializeByParsingDomObjectIdString($domObjectId);
+               $this->inlineStackProcessor->injectAjaxConfiguration($ajaxArguments['context']);
+               $response->getBody()->write(json_encode($this->renderInlineNewChildRecord($domObjectId, $createAfterUid)));
+               return $response;
+       }
+
+       /**
+        * Show the details of a child record.
+        *
+        * @param ServerRequestInterface $request
+        * @param ResponseInterface $response
+        * @return ResponseInterface
+        */
+       public function detailsAction(ServerRequestInterface $request, ResponseInterface $response) {
+               $ajaxArguments = isset($request->getParsedBody()['ajax']) ? $request->getParsedBody()['ajax'] : $request->getQueryParams()['ajax'];
+               $domObjectId = $ajaxArguments[0];
+               // Parse the DOM identifier (string), add the levels to the structure stack (array), load the TCA config:
+               $this->inlineStackProcessor->initializeByParsingDomObjectIdString($domObjectId);
+               $this->inlineStackProcessor->injectAjaxConfiguration($ajaxArguments['context']);
+               $response->getBody()->write(json_encode($this->renderInlineChildRecord($domObjectId)));
+               return $response;
+       }
+
+       /**
+        * Adds localizations or synchronizes the locations of all child records.
+        *
+        * @param ServerRequestInterface $request the incoming request
+        * @param ResponseInterface $response the empty response
+        * @return ResponseInterface the filled response
+        */
+       public function synchronizeLocalizeAction(ServerRequestInterface $request, ResponseInterface $response) {
+               $ajaxArguments = isset($request->getParsedBody()['ajax']) ? $request->getParsedBody()['ajax'] : $request->getQueryParams()['ajax'];
+               $domObjectId = $ajaxArguments[0];
+               $type = $ajaxArguments[1];
+               // Parse the DOM identifier (string), add the levels to the structure stack (array), load the TCA config:
+               $this->inlineStackProcessor->initializeByParsingDomObjectIdString($domObjectId);
+               $this->inlineStackProcessor->injectAjaxConfiguration($ajaxArguments['context']);
+               $inlineFirstPid = FormEngineUtility::getInlineFirstPidFromDomObjectId($domObjectId);
+               $response->getBody()->write(json_encode($this->renderInlineSynchronizeLocalizeRecords($type, $inlineFirstPid)));
+               return $response;
+       }
+
+       /**
+        * Adds localizations or synchronizes the locations of all child records.
+        *
+        * @param ServerRequestInterface $request the incoming request
+        * @param ResponseInterface $response the empty response
+        * @return ResponseInterface the filled response
+        */
+       public function expandOrCollapseAction(ServerRequestInterface $request, ResponseInterface $response) {
+               $ajaxArguments = isset($request->getParsedBody()['ajax']) ? $request->getParsedBody()['ajax'] : $request->getQueryParams()['ajax'];
+               $domObjectId = $ajaxArguments[0];
+               // Parse the DOM identifier (string), add the levels to the structure stack (array), don't load TCA config
+               $this->inlineStackProcessor->initializeByParsingDomObjectIdString($domObjectId, FALSE);
+               $expand = $ajaxArguments[1];
+               $collapse = $ajaxArguments[2];
+               $this->setInlineExpandedCollapsedState($expand, $collapse);
+               $response->getBody()->write(json_encode(array()));
+               return $response;
        }
 
        /**
index c86603e..a97f158 100644 (file)
@@ -14,6 +14,10 @@ namespace TYPO3\CMS\Backend\Controller;
  * The TYPO3 project - inspiring people to share!
  */
 
+use Psr\Http\Message\ResponseInterface;
+use Psr\Http\Message\ServerRequestInterface;
+use TYPO3\CMS\Backend\Search\LiveSearch\LiveSearch;
+use TYPO3\CMS\Backend\Search\LiveSearch\QueryParser;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
 
 /**
@@ -29,19 +33,20 @@ class LiveSearchController {
        /**
         * Processes all AJAX calls and sends back a JSON object
         *
-        * @param array $parameters
-        * @param \TYPO3\CMS\Core\Http\AjaxRequestHandler $ajaxRequestHandler
+        * @param ServerRequestInterface $request
+        * @param ResponseInterface $response
+        * @return ResponseInterface
         */
-       public function liveSearchAction($parameters, \TYPO3\CMS\Core\Http\AjaxRequestHandler $ajaxRequestHandler) {
-               $queryString = GeneralUtility::_GET('q');
-               $liveSearch = GeneralUtility::makeInstance(\TYPO3\CMS\Backend\Search\LiveSearch\LiveSearch::class);
-               $queryParser = GeneralUtility::makeInstance(\TYPO3\CMS\Backend\Search\LiveSearch\QueryParser::class);
+       public function liveSearchAction(ServerRequestInterface $request, ResponseInterface $response) {
+               $queryString = $request->getQueryParams()['q'];
+               $liveSearch = GeneralUtility::makeInstance(LiveSearch::class);
+               $queryParser = GeneralUtility::makeInstance(QueryParser::class);
 
                $searchResults = array();
                $liveSearch->setQueryString($queryString);
                // Jump & edit - find page and retrieve an edit link (this is only for pages
                if ($queryParser->isValidPageJump($queryString)) {
-                       $searchResults[] = array_merge($liveSearch->findPage($queryString), array('type' => 'pageJump'));
+                       $searchResults[] = array_merge($liveSearch->findPage($queryString), ['type' => 'pageJump']);
                        $commandQuery = $queryParser->getCommandForPageJump($queryString);
                        if ($commandQuery) {
                                $queryString = $commandQuery;
@@ -54,7 +59,7 @@ class LiveSearchController {
                                $searchResults[] = $item;
                        }
                }
-               $ajaxRequestHandler->setContent($searchResults);
-               $ajaxRequestHandler->setContentFormat('json');
+               $response->getBody()->write(json_encode($searchResults));
+               return $response;
        }
 }
index 48d1374..afc324e 100644 (file)
@@ -14,7 +14,6 @@ namespace TYPO3\CMS\Backend\Controller;
  * The TYPO3 project - inspiring people to share!
  */
 
-use TYPO3\CMS\Core\Http\AjaxRequestHandler;
 use TYPO3\CMS\Core\Messaging\FlashMessage;
 use TYPO3\CMS\Core\Messaging\FlashMessageService;
 use TYPO3\CMS\Core\Resource\OnlineMedia\Helpers\OnlineMediaHelperRegistry;
@@ -31,25 +30,28 @@ use Psr\Http\Message\ServerRequestInterface;
 class OnlineMediaController {
 
        /**
-        * @param array $_
-        * @param AjaxRequestHandler $ajaxObj
-        * @return void
+        * AJAX endpoint for storing the URL as a sys_file record
+        *
+        * @param ServerRequestInterface $request
+        * @param ResponseInterface $response
+        * @return ResponseInterface
         */
-       public function addAjaxAction($_, AjaxRequestHandler $ajaxObj = NULL) {
-               $ajaxObj->setContentFormat('json');
-
-               $url = GeneralUtility::_POST('url');
-               $targetFolderIdentifier = GeneralUtility::_POST('targetFolder');
-               $allowedExtensions = GeneralUtility::trimExplode(',', GeneralUtility::_POST('allowed') ?: '');
+       public function createAction(ServerRequestInterface $request, ResponseInterface $response) {
+               $url = $request->getParsedBody()['url'];
+               $targetFolderIdentifier = $request->getParsedBody()['targetFolder'];
+               $allowedExtensions = GeneralUtility::trimExplode(',', $request->getParsedBody()['allowed'] ?: '');
 
                if (!empty($url)) {
+                       $data = [];
                        $file = $this->addMediaFromUrl($url, $targetFolderIdentifier, $allowedExtensions);
                        if ($file !== NULL) {
-                               $ajaxObj->addContent('file', $file->getUid());
+                               $data['file'] = $file->getUid();
                        } else {
-                               $ajaxObj->addContent('error', $this->getLanguageService()->sL('LLL:EXT:lang/locallang_core.xlf:online_media.error.invalid_url'));
+                               $data['error'] = $this->getLanguageService()->sL('LLL:EXT:lang/locallang_core.xlf:online_media.error.invalid_url');
                        }
+                       $response->getBody()->write(json_encode($data));
                }
+               return $response;
        }
 
        /**
@@ -92,6 +94,7 @@ class OnlineMediaController {
                }
 
                $redirect = isset($request->getParsedBody()['redirect']) ? $request->getParsedBody()['redirect'] : $request->getQueryParams()['redirect'];
+               $redirect = GeneralUtility::sanitizeLocalUrl($redirect);
                if ($redirect) {
                        $response = $response
                                ->withHeader('Location', GeneralUtility::locationHeaderUrl($redirect))
index 7b4195b..07980a7 100644 (file)
@@ -14,6 +14,8 @@ namespace TYPO3\CMS\Backend\Controller;
  * The TYPO3 project - inspiring people to share!
  */
 
+use Psr\Http\Message\ResponseInterface;
+use Psr\Http\Message\ServerRequestInterface;
 use TYPO3\CMS\Backend\Template\DocumentTemplate;
 use TYPO3\CMS\Backend\Utility\BackendUtility;
 use TYPO3\CMS\Backend\View\PageTreeView;
@@ -314,18 +316,20 @@ class PageTreeNavigationController {
         * Makes the AJAX call to expand or collapse the pagetree.
         * Called by typo3/ajax.php
         *
-        * @param array $params Additional parameters (not used here)
-        * @param \TYPO3\CMS\Core\Http\AjaxRequestHandler $ajaxObj The AjaxRequestHandler object of this request
-        * @return void
+        * @param ServerRequestInterface $request
+        * @param ResponseInterface $response
+        * @return ResponseInterface
         */
-       public function ajaxExpandCollapse($params, $ajaxObj) {
+       public function ajaxExpandCollapse(ServerRequestInterface $request, ResponseInterface $response) {
                $this->init();
                $tree = $this->pagetree->getBrowsableTree();
                if (!$this->pagetree->ajaxStatus) {
-                       $ajaxObj->setError($tree);
+                       $response->withStatus(500);
                } else {
-                       $ajaxObj->addContent('tree', $tree);
+                       $response->getBody()->write(json_encode($tree));
                }
+
+               return $response;
        }
 
        /**
index 808e387..d7016ef 100644 (file)
@@ -23,7 +23,6 @@ use TYPO3\CMS\Core\Utility\MathUtility;
 use TYPO3\CMS\Backend\Clipboard\Clipboard;
 use TYPO3\CMS\Backend\Utility\BackendUtility;
 use TYPO3\CMS\Core\Utility\HttpUtility;
-use TYPO3\CMS\Core\Http\AjaxRequestHandler;
 use TYPO3\CMS\Core\Messaging\FlashMessageService;
 
 /**
@@ -267,10 +266,11 @@ class SimpleDataHandlerController {
        /**
         * Processes all AJAX calls and returns a JSON formatted string
         *
-        * @param array $parameters
-        * @param \TYPO3\CMS\Core\Http\AjaxRequestHandler $ajaxRequestHandler
+        * @param ServerRequestInterface $request
+        * @param ResponseInterface $response
+        * @return ResponseInterface
         */
-       public function processAjaxRequest($parameters, AjaxRequestHandler $ajaxRequestHandler) {
+       public function processAjaxRequest(ServerRequestInterface $request, ResponseInterface $response) {
                // do the regular / main logic
                $this->initClipboard();
                $this->main();
@@ -303,8 +303,9 @@ class SimpleDataHandlerController {
                                }
                        }
                }
-               $ajaxRequestHandler->setContentFormat('json');
-               $ajaxRequestHandler->setContent($content);
+
+               $response->getBody()->write(json_encode($content));
+               return $response;
        }
 
        /**
index 8266fb4..4c32a30 100644 (file)
@@ -15,9 +15,10 @@ namespace TYPO3\CMS\Backend\Controller;
  * The TYPO3 project - inspiring people to share!
  */
 
+use Psr\Http\Message\ResponseInterface;
+use Psr\Http\Message\ServerRequestInterface;
 use TYPO3\CMS\Core\Utility\ArrayUtility;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
-use TYPO3\CMS\Core\Http\AjaxRequestHandler;
 
 /**
  * A wrapper class to call BE_USER->uc
@@ -28,19 +29,20 @@ class UserSettingsController {
        /**
         * Processes all AJAX calls and returns a JSON for the data
         *
-        * @param array $parameters
-        * @param \TYPO3\CMS\Core\Http\AjaxRequestHandler $ajaxRequestHandler
+        * @param ServerRequestInterface $request
+        * @param ResponseInterface $response
+        * @return ResponseInterface
         */
-       public function processAjaxRequest($parameters, AjaxRequestHandler $ajaxRequestHandler) {
+       public function processAjaxRequest(ServerRequestInterface $request, ResponseInterface $response) {
                // do the regular / main logic, depending on the action parameter
-               $action = GeneralUtility::_GP('action');
-               $key = GeneralUtility::_GP('key');
-               $value = GeneralUtility::_GP('value');
+               $action = isset($request->getParsedBody()['action']) ? $request->getParsedBody()['action'] : $request->getQueryParams()['action'];
+               $key = isset($request->getParsedBody()['key']) ? $request->getParsedBody()['fileName'] : $request->getQueryParams()['key'];
+               $value = isset($request->getParsedBody()['value']) ? $request->getParsedBody()['value'] : $request->getQueryParams()['value'];
 
                $content = $this->process($action, $key, $value);
 
-               $ajaxRequestHandler->setContentFormat('json');
-               $ajaxRequestHandler->setContent($content);
+               $response->getBody()->write(json_encode($content));
+               return $response;
        }
 
        /**
index 2128478..b4afee7 100644 (file)
@@ -106,7 +106,7 @@ class ImageManipulationElement extends AbstractFormElement {
                        $wizardData['token'] = GeneralUtility::hmac(implode('|', $wizardData), 'ImageManipulationWizard');
 
                        $buttonAttributes = array(
-                               'data-url' => BackendUtility::getAjaxUrl('ImageManipulationWizard::getHtmlForImageManipulationWizard', $wizardData),
+                               'data-url' => BackendUtility::getAjaxUrl('wizard_image_manipulation', $wizardData),
                                'data-severity' => 'notice',
                                'data-image-name' => $file->getNameWithoutExtension(),
                                'data-image-uid' => $file->getUid(),
index b940c73..f39365d 100644 (file)
@@ -14,6 +14,8 @@ namespace TYPO3\CMS\Backend\Form\Wizard;
  * The TYPO3 project - inspiring people to share!
  */
 
+use Psr\Http\Message\ResponseInterface;
+use Psr\Http\Message\ServerRequestInterface;
 use TYPO3\CMS\Core\Resource\Exception\FileDoesNotExistException;
 use TYPO3\CMS\Core\Resource\ResourceFactory;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
@@ -32,59 +34,63 @@ class ImageManipulationWizard {
        protected $templatePath = 'EXT:backend/Resources/Private/Templates/';
 
        /**
-        * Returns the html for the AJAX API
+        * Returns the HTML for the wizard inside the modal
         *
-        * @param array $params
-        * @param \TYPO3\CMS\Core\Http\AjaxRequestHandler $ajaxRequestHandler
-        * @return void
+        * @param ServerRequestInterface $request
+        * @param ResponseInterface $response
+        * @return ResponseInterface $response
         */
-       public function getHtmlForImageManipulationWizard($params, $ajaxRequestHandler) {
-               if (!$this->checkHmacToken()) {
-                       HttpUtility::setResponseCodeAndExit(HttpUtility::HTTP_STATUS_403);
-               }
-
-               $fileUid = GeneralUtility::_GET('file');
-               $image = NULL;
-               if (MathUtility::canBeInterpretedAsInteger($fileUid)) {
-                       try {
-                               $image = ResourceFactory::getInstance()->getFileObject($fileUid);
-                       } catch (FileDoesNotExistException $e) {}
-               }
+       public function getWizardAction(ServerRequestInterface $request, ResponseInterface $response) {
+               if ($this->isValidToken($request)) {
+                       $queryParams = $request->getQueryParams();
+                       $fileUid = isset($request->getParsedBody()['file']) ? $request->getParsedBody()['file'] : $queryParams['file'];
+                       $image = NULL;
+                       if (MathUtility::canBeInterpretedAsInteger($fileUid)) {
+                               try {
+                                       $image = ResourceFactory::getInstance()->getFileObject($fileUid);
+                               } catch (FileDoesNotExistException $e) {}
+                       }
 
-               $view = $this->getFluidTemplateObject($this->templatePath . 'Wizards/ImageManipulationWizard.html');
-               $view->assign('image', $image);
-               $view->assign('zoom', (bool)GeneralUtility::_GET('zoom'));
-               $view->assign('ratios', $this->getRatiosArray());
-               $content = $view->render();
+                       $view = $this->getFluidTemplateObject($this->templatePath . 'Wizards/ImageManipulationWizard.html');
+                       $view->assign('image', $image);
+                       $view->assign('zoom', (bool)$queryParams['bool']);
+                       $view->assign('ratios', $this->getAvailableRatios($request));
+                       $content = $view->render();
 
-               $ajaxRequestHandler->addContent('content', $content);
-               $ajaxRequestHandler->setContentFormat('html');
+                       $response->getBody()->write($content);
+                       return $response;
+               } else {
+                       return $response->withStatus(403);
+               }
        }
 
        /**
         * Check if hmac token is correct
         *
+        * @param ServerRequestInterface $request the request with the GET parameters
         * @return bool
         */
-       protected function checkHmacToken() {
-               $parameters = array();
-               if (GeneralUtility::_GET('file')) {
-                       $parameters['file'] = GeneralUtility::_GET('file');
+       protected function isValidToken(ServerRequestInterface $request) {
+               $parameters = [
+                       'zoom'   => $request->getQueryParams()['zoom'] ? '1' : '0',
+                       'ratios' => $request->getQueryParams()['ratios'] ?: ''
+               ];
+               if ($request->getQueryParams()['file']) {
+                       $parameters['file'] = $request->getQueryParams()['file'];
                }
-               $parameters['zoom'] = GeneralUtility::_GET('zoom') ? '1' : '0';
-               $parameters['ratios'] = GeneralUtility::_GET('ratios') ?: '';
 
                $token = GeneralUtility::hmac(implode('|', $parameters), 'ImageManipulationWizard');
-               return $token === GeneralUtility::_GET('token');
+               return $token === $request->getQueryParams()['token'];
        }
 
        /**
         * Get available ratios
         *
+        * @param ServerRequestInterface $request
         * @return array
         */
-       protected function getRatiosArray() {
-               $ratios = json_decode(GeneralUtility::_GET('ratios'));
+       protected function getAvailableRatios(ServerRequestInterface $request) {
+               $ratios = json_decode($request->getQueryParams()['ratios']);
                // Json transforms an array with string keys to an array,
                // we need to transform this to an array for the fluid ForViewHelper
                if (is_object($ratios)) {
index 76060fd..ebe0d30 100644 (file)
@@ -14,6 +14,8 @@ namespace TYPO3\CMS\Backend\Form\Wizard;
  * The TYPO3 project - inspiring people to share!
  */
 
+use Psr\Http\Message\ResponseInterface;
+use Psr\Http\Message\ServerRequestInterface;
 use TYPO3\CMS\Backend\Utility\BackendUtility;
 use TYPO3\CMS\Core\Imaging\Icon;
 use TYPO3\CMS\Core\Imaging\IconFactory;
@@ -123,20 +125,23 @@ class SuggestWizard {
        }
 
        /**
-        * Ajax handler for the "suggest" feature in TCEforms.
+        * Ajax handler for the "suggest" feature in FormEngine.
         *
-        * @param array $params The parameters from the AJAX call
-        * @param \TYPO3\CMS\Core\Http\AjaxRequestHandler $ajaxObj The AJAX object representing the AJAX call
-        * @return void
+        * @param ServerRequestInterface $request
+        * @param ResponseInterface $response
+        * @return ResponseInterface
         */
-       public function processAjaxRequest($params, &$ajaxObj) {
+       public function searchAction(ServerRequestInterface $request, ResponseInterface $response) {
+               $parsedBody = $request->getParsedBody();
+               $queryParams = $request->getQueryParams();
+
                // Get parameters from $_GET/$_POST
-               $search = GeneralUtility::_GP('value');
-               $table = GeneralUtility::_GP('table');
-               $field = GeneralUtility::_GP('field');
-               $uid = GeneralUtility::_GP('uid');
-               $pageId = GeneralUtility::_GP('pid');
-               $newRecordRow = GeneralUtility::_GP('newRecordRow');
+               $search = isset($parsedBody['value']) ? $parsedBody['value'] : $queryParams['value'];
+               $table = isset($parsedBody['table']) ? $parsedBody['table'] : $queryParams['table'];
+               $field = isset($parsedBody['field']) ? $parsedBody['field'] : $queryParams['field'];
+               $uid = (int)(isset($parsedBody['uid']) ? $parsedBody['uid'] : $queryParams['uid']);
+               $pageId = (int)(isset($parsedBody['pid']) ? $parsedBody['pid'] : $queryParams['pid']);
+               $newRecordRow = isset($parsedBody['newRecordRow']) ? $parsedBody['newRecordRow'] : $queryParams['newRecordRow'];
                // If the $uid is numeric, we have an already existing element, so get the
                // TSconfig of the page itself or the element container (for non-page elements)
                // otherwise it's a new element, so use given id of parent page (i.e., don't modify it here)
@@ -216,8 +221,8 @@ class SuggestWizard {
 
                $listItems = $this->createListItemsFromResultRow($resultRows, $maxItems);
 
-               $ajaxObj->setContent($listItems);
-               $ajaxObj->setContentFormat('json');
+               $response->getBody()->write(json_encode($listItems));
+               return $response;
        }
 
        /**
index 9e1acbc..df08ed5 100644 (file)
@@ -14,9 +14,16 @@ namespace TYPO3\CMS\Backend\Http;
  * The TYPO3 project - inspiring people to share!
  */
 
+use TYPO3\CMS\Backend\Routing\Exception\ResourceNotFoundException;
 use TYPO3\CMS\Core\Core\Bootstrap;
+use TYPO3\CMS\Core\FormProtection\FormProtectionFactory;
+use TYPO3\CMS\Core\Http\Response;
 use TYPO3\CMS\Core\Http\RequestHandlerInterface;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
+use TYPO3\CMS\Backend\Routing\Router;
+use TYPO3\CMS\Backend\Routing\Route;
+use TYPO3\CMS\Backend\Routing\Exception\RouteNotFoundException;
+use Psr\Http\Message\ResponseInterface;
 use Psr\Http\Message\ServerRequestInterface;
 
 /**
@@ -44,13 +51,11 @@ class AjaxRequestHandler implements RequestHandlerInterface {
         * @var array
         */
        protected $publicAjaxIds = array(
-               'BackendLogin::login',
-               'BackendLogin::logout',
-               'BackendLogin::refreshLogin',
-               'BackendLogin::isTimedOut',
-               'BackendLogin::getChallenge',
-               'BackendLogin::getRsaPublicKey',
-               'RsaEncryption::getRsaPublicKey'
+               '/ajax/login',
+               '/ajax/logout',
+               '/ajax/login/refresh',
+               '/ajax/login/timedout',
+               '/ajax/rsa/publickey'
        );
 
        /**
@@ -74,47 +79,17 @@ class AjaxRequestHandler implements RequestHandlerInterface {
 
                // used for backwards-compatibility
                $GLOBALS['ajaxID'] = $ajaxID;
-               $this->boot($ajaxID);
-
-               // Finding the script path from the registry
-               $ajaxRegistryEntry = isset($GLOBALS['TYPO3_CONF_VARS']['BE']['AJAX'][$ajaxID]) ? $GLOBALS['TYPO3_CONF_VARS']['BE']['AJAX'][$ajaxID] : NULL;
-               $ajaxScript = NULL;
-               $csrfTokenCheck = FALSE;
-               if ($ajaxRegistryEntry !== NULL && is_array($ajaxRegistryEntry) && isset($ajaxRegistryEntry['callbackMethod'])) {
-                       $ajaxScript = $ajaxRegistryEntry['callbackMethod'];
-                       $csrfTokenCheck = $ajaxRegistryEntry['csrfTokenCheck'];
-               }
-
-               // Instantiating the AJAX object
-               $ajaxObj = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Http\AjaxRequestHandler::class, $ajaxID);
-               $ajaxParams = array('request' => $request);
-
-               // Evaluating the arguments and calling the AJAX method/function
-               if (empty($ajaxID)) {
-                       $ajaxObj->setError('No valid ajaxID parameter given.');
-               } elseif (empty($ajaxScript)) {
-                       $ajaxObj->setError('No backend function registered for ajaxID "' . $ajaxID . '".');
-               } else {
-                       $success = TRUE;
-                       $tokenIsValid = TRUE;
-                       if ($csrfTokenCheck) {
-                               $ajaxToken = $request->getParsedBody()['ajaxToken'] ?: $request->getQueryParams()['ajaxToken'];
-                               $tokenIsValid = \TYPO3\CMS\Core\FormProtection\FormProtectionFactory::get()->validateToken($ajaxToken, 'ajaxCall', $ajaxID);
-                       }
-                       if ($tokenIsValid) {
-                               // Cleanup global variable space
-                               unset($csrfTokenCheck, $ajaxRegistryEntry, $tokenIsValid, $success);
-                               $success = GeneralUtility::callUserFunction($ajaxScript, $ajaxParams, $ajaxObj, FALSE, TRUE);
-                       } else {
-                               $ajaxObj->setError('Invalid CSRF token detected for ajaxID "' . $ajaxID . '"!');
-                       }
-                       if ($success === FALSE) {
-                               $ajaxObj->setError('Registered backend function for ajaxID "' . $ajaxID . '" was not found.');
-                       }
+               $request = $request->withAttribute('routePath', $ajaxID);
+               $proceedIfNoUserIsLoggedIn = $this->isLoggedInBackendUserRequired($ajaxID);
+               $this->boot($proceedIfNoUserIsLoggedIn);
+
+               try {
+                       // Backend Routing - check if a valid route is there, and dispatch
+                       return $this->dispatch($request);
+               } catch (ResourceNotFoundException $e) {
+                       // no Route found, fallback to the traditional AJAX request
                }
-
-               // Outputting the content (and setting the X-JSON-Header)
-               return $ajaxObj->render();
+               return $this->dispatchTraditionalAjaxRequest($request);
        }
 
        /**
@@ -138,14 +113,22 @@ class AjaxRequestHandler implements RequestHandlerInterface {
        }
 
        /**
-        * Start the Backend bootstrap part
+        * Check if the user is required for the request
+        * If we're trying to do an ajax login, don't require a user
         *
-        * @param string $ajaxId Contains the string of the ajaxId used
+        * @param string $ajaxId the Ajax ID to check against
+        * @return bool whether the request can proceed without a login required
         */
-       protected function boot($ajaxId) {
-               // If we're trying to do an ajax login, don't require a user
-               $proceedIfNoUserIsLoggedIn = in_array($ajaxId, $this->publicAjaxIds, TRUE);
+       protected function isLoggedInBackendUserRequired($ajaxId) {
+               return in_array($ajaxId, $this->publicAjaxIds, TRUE);
+       }
 
+       /**
+        * Start the Backend bootstrap part
+        *
+        * @param bool $proceedIfNoUserIsLoggedIn a flag if a backend user is required
+        */
+       protected function boot($proceedIfNoUserIsLoggedIn) {
                $this->bootstrap
                        ->checkLockedBackendAndRedirectOrDie($proceedIfNoUserIsLoggedIn)
                        ->checkBackendIpOrDie()
@@ -161,4 +144,87 @@ class AjaxRequestHandler implements RequestHandlerInterface {
                        ->initializeOutputCompression()
                        ->sendHttpHeaders();
        }
+
+       /**
+        * Creates a response object with JSON headers automatically, and then dispatches to the correct route
+        *
+        * @param ServerRequestInterface $request
+        * @return ResponseInterface $response
+        * @throws ResourceNotFoundException if no valid route was found
+        * @throws RouteNotFoundException if the request could not be verified
+        */
+       protected function dispatch(ServerRequestInterface $request) {
+               /** @var Response $response */
+               $response = GeneralUtility::makeInstance(Response::class, 'php://temp', 200, [
+                       'Content-type' => 'application/json; charset=utf-8',
+                       'X-JSON' => 'true'
+               ]);
+
+               /** @var RouteDispatcher $dispatcher */
+               $dispatcher = GeneralUtility::makeInstance(RouteDispatcher::class);
+               return $dispatcher->dispatch($request, $response);
+       }
+
+       /**
+        * Calls the ajax callback method registered in TYPO3_CONF_VARS[BE][AJAX]
+        *
+        * @param ServerRequestInterface $request
+        * @return ResponseInterface
+        */
+       protected function dispatchTraditionalAjaxRequest($request) {
+               $ajaxID = $request->getAttribute('routePath');
+               // Finding the script path from the registry
+               $ajaxRegistryEntry = isset($GLOBALS['TYPO3_CONF_VARS']['BE']['AJAX'][$ajaxID]) ? $GLOBALS['TYPO3_CONF_VARS']['BE']['AJAX'][$ajaxID] : NULL;
+               $ajaxScript = NULL;
+               $csrfTokenCheck = FALSE;
+               if ($ajaxRegistryEntry !== NULL && is_array($ajaxRegistryEntry) && isset($ajaxRegistryEntry['callbackMethod'])) {
+                       $ajaxScript = $ajaxRegistryEntry['callbackMethod'];
+                       $csrfTokenCheck = $ajaxRegistryEntry['csrfTokenCheck'];
+               }
+
+               // Instantiating the AJAX object
+               /** @var \TYPO3\CMS\Core\Http\AjaxRequestHandler $ajaxObj */
+               $ajaxObj = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Http\AjaxRequestHandler::class, $ajaxID);
+               $ajaxParams = array('request' => $request);
+
+               // Evaluating the arguments and calling the AJAX method/function
+               if (empty($ajaxID)) {
+                       $ajaxObj->setError('No valid ajaxID parameter given.');
+               } elseif (empty($ajaxScript)) {
+                       $ajaxObj->setError('No backend function registered for ajaxID "' . $ajaxID . '".');
+               } elseif ($csrfTokenCheck && !$this->isValidRequest($request)) {
+                       $ajaxObj->setError('Invalid CSRF token detected for ajaxID "' . $ajaxID . '"!');
+               } else {
+                       $success = GeneralUtility::callUserFunction($ajaxScript, $ajaxParams, $ajaxObj, FALSE, TRUE);
+                       if ($success === FALSE) {
+                               $ajaxObj->setError('Registered backend function for ajaxID "' . $ajaxID . '" was not found.');
+                       }
+               }
+
+               // Outputting the content (and setting the X-JSON-Header)
+               return $ajaxObj->render();
+       }
+
+       /**
+        * Wrapper method for static form protection utility
+        *
+        * @return \TYPO3\CMS\Core\FormProtection\AbstractFormProtection
+        */
+       protected function getFormProtection() {
+               return FormProtectionFactory::get();
+       }
+
+       /**
+        * Checks if the request token is valid. This is checked to see if the route is really
+        * created by the same instance. Should be called for all routes in the backend except
+        * for the ones that don't require a login.
+        *
+        * @param ServerRequestInterface $request
+        * @return bool
+        * @see \TYPO3\CMS\Backend\Routing\UriBuilder where the token is generated.
+        */
+       protected function isValidRequest(ServerRequestInterface $request) {
+               $token = (string)(isset($request->getParsedBody()['ajaxToken']) ? $request->getParsedBody()['ajaxToken'] : $request->getQueryParams()['ajaxToken']);
+               return $this->getFormProtection()->validateToken($token, 'ajaxCall', $request->getAttribute('routePath'));
+       }
 }
index 98cf50e..4c7f367 100644 (file)
@@ -39,8 +39,9 @@ class RouteDispatcher extends Dispatcher implements DispatcherInterface {
         * @throws \InvalidArgumentException if the defined target for the route is invalid
         */
        public function dispatch(ServerRequestInterface $request, ResponseInterface $response) {
-               /** @var Route $route */
+               /** @var Router $router */
                $router = GeneralUtility::makeInstance(Router::class);
+               /** @var Route $route */
                $route = $router->matchRequest($request);
                $request = $request->withAttribute('route', $route);
                if (!$this->isValidRequest($request)) {
@@ -71,8 +72,15 @@ class RouteDispatcher extends Dispatcher implements DispatcherInterface {
         * @see \TYPO3\CMS\Backend\Routing\UriBuilder where the token is generated.
         */
        protected function isValidRequest($request) {
-               $token = (string)(isset($request->getParsedBody()['token']) ? $request->getParsedBody()['token'] : $request->getQueryParams()['token']);
                $route = $request->getAttribute('route');
-               return ($route->getOption('access') === 'public' || $this->getFormProtection()->validateToken($token, 'route', $route->getOption('_identifier')));
+               if ($route->getOption('access') === 'public') {
+                       return TRUE;
+               } elseif ($route->getOption('ajax')) {
+                       $token = (string)(isset($request->getParsedBody()['ajaxToken']) ? $request->getParsedBody()['ajaxToken'] : $request->getQueryParams()['ajaxToken']);
+                       return $this->getFormProtection()->validateToken($token, 'ajaxCall', $route->getOption('_identifier'));
+               } else {
+                       $token = (string)(isset($request->getParsedBody()['token']) ? $request->getParsedBody()['token'] : $request->getQueryParams()['token']);
+                       return $this->getFormProtection()->validateToken($token, 'route', $route->getOption('_identifier'));
+               }
        }
 }
\ No newline at end of file
index 041573a..4bf55d5 100644 (file)
@@ -41,7 +41,7 @@ class UriBuilder {
        const ABSOLUTE_PATH = 'absolute';
 
        /**
-        * @var array
+        * @var Route[]
         */
        protected $routes;
 
@@ -73,17 +73,36 @@ class UriBuilder {
 
                $route = $this->routes[$name];
 
-               // If the route has the "public" option set, no token is generated.
-               if ($route->getOption('access') !== 'public') {
+               // The Route is an AJAX route, so the parameters are different in order
+               // for the AjaxRequestHandler to be triggered
+               if ($route->getOption('ajax')) {
+                       // If the route has the "public" option set, no token is generated.
+                       if ($route->getOption('access') !== 'public') {
+                               $parameters = array(
+                                       'ajaxToken' => FormProtectionFactory::get()->generateToken('ajaxCall', $name)
+                               ) + $parameters;
+                       }
+
+                       // Add the Route path as &ajaxID=XYZ
                        $parameters = array(
-                               'token' => FormProtectionFactory::get()->generateToken('route', $name)
+                               'ajaxID' => $route->getPath()
                        ) + $parameters;
+
+               } else {
+                       // If the route has the "public" option set, no token is generated.
+                       if ($route->getOption('access') !== 'public') {
+                               $parameters = array(
+                                       'token' => FormProtectionFactory::get()->generateToken('route', $name)
+                               ) + $parameters;
+                       }
+
+                       // Add the Route path as &route=XYZ
+                       $parameters = array(
+                               'route' => $route->getPath()
+                       ) + $parameters;
+
                }
 
-               // Add the Route path as &route=XYZ
-               $parameters = array(
-                       'route' => $route->getPath()
-               ) + $parameters;
 
                return $this->buildUri($parameters, $referenceType);
        }
index 2f1ee73..8e79a10 100644 (file)
@@ -14,6 +14,8 @@ namespace TYPO3\CMS\Backend\Template;
  * The TYPO3 project - inspiring people to share!
  */
 
+use Psr\Http\Message\ResponseInterface;
+use Psr\Http\Message\ServerRequestInterface;
 use TYPO3\CMS\Backend\Utility\BackendUtility;
 use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
 use TYPO3\CMS\Core\Imaging\Icon;
@@ -1759,13 +1761,13 @@ function jumpToUrl(URL) {
        /**
         * Renders the FlashMessages from queue and returns them as AJAX.
         *
-        * @param array $params Always empty.
-        * @param \TYPO3\CMS\Core\Http\AjaxRequestHandler $ajaxObj The AjaxRequestHandler object used to return content and set content types
-        * @return void
+        * @param ServerRequestInterface $request
+        * @param ResponseInterface $response
+        * @return ResponseInterface
         */
-       public function renderFlashMessages(array $params, \TYPO3\CMS\Core\Http\AjaxRequestHandler $ajaxObj) {
-               $ajaxObj->addContent('result', $this->getFlashMessages());
-               $ajaxObj->setContentFormat('html');
+       public function renderQueuedFlashMessages(ServerRequestInterface $request, ResponseInterface $response) {
+               $response->getBody()->write($this->getFlashMessages());
+               return $response;
        }
 
        /**
index 8c46936..4f038f2 100755 (executable)
@@ -3381,7 +3381,14 @@ class BackendUtility {
        static public function getAjaxUrl($ajaxIdentifier, array $urlParameters = array(), $backPathOverride = FALSE, $returnAbsoluteUrl = FALSE) {
                /** @var UriBuilder $uriBuilder */
                $uriBuilder = GeneralUtility::makeInstance(UriBuilder::class);
-               return (string)$uriBuilder->buildUriFromAjaxId($ajaxIdentifier, $urlParameters, $returnAbsoluteUrl ? UriBuilder::ABSOLUTE_URL : UriBuilder::ABSOLUTE_PATH);
+               try {
+                       $routeIdentifier = 'ajax_' . $ajaxIdentifier;
+                       $uri = $uriBuilder->buildUriFromRoute($routeIdentifier, $urlParameters, $returnAbsoluteUrl ? UriBuilder::ABSOLUTE_URL : UriBuilder::ABSOLUTE_PATH);
+               } catch (\TYPO3\CMS\Backend\Routing\Exception\RouteNotFoundException $e) {
+                       // no route registered, use the fallback logic to check for a module
+                       $uri = $uriBuilder->buildUriFromAjaxId($ajaxIdentifier, $urlParameters, $returnAbsoluteUrl ? UriBuilder::ABSOLUTE_URL : UriBuilder::ABSOLUTE_PATH);
+               }
+               return (string)$uri;
        }
 
        /**
diff --git a/typo3/sysext/backend/Configuration/Backend/AjaxRoutes.php b/typo3/sysext/backend/Configuration/Backend/AjaxRoutes.php
new file mode 100644 (file)
index 0000000..bf71f70
--- /dev/null
@@ -0,0 +1,189 @@
+<?php
+use TYPO3\CMS\Backend\Controller;
+
+/**
+ * Definitions for routes provided by EXT:backend
+ * Contains all AJAX-based routes for entry points
+ *
+ * Currently the "access" property is only used so no token creation + validation is made
+ * but will be extended further.
+ */
+return [
+
+       // Expand or toggle in legacy database tree
+       'sc_alt_db_navframe_expandtoggle' => [
+               'path' => '/record/tree/expand',
+               'target' => Controller\PageTreeNavigationController::class . '::ajaxExpandCollapse'
+       ],
+
+       // Expand or toggle in legacy file tree
+       'sc_alt_file_navframe_expandtoggle' => [
+               'path' => '/folder/tree/expand',
+               'target' => Controller\FileSystemNavigationFrameController::class . '::ajaxExpandCollapse'
+       ],
+
+       // File processing
+       'file_process' => [
+               'path' => '/file/process',
+               'target' => Controller\File\FileController::class . '::processAjaxRequest'
+       ],
+
+       // Check if file exists
+       'file_exists' => [
+               'path' => '/file/exists',
+               'target' => Controller\File\FileController::class . '::fileExistsInFolderAction'
+       ],
+
+       // Get record details of a child record in IRRE
+       'record_inline_details' => [
+               'path' => '/record/inline/details',
+               'target' => Controller\FormInlineAjaxController::class . '::detailsAction'
+       ],
+
+       // Create new inline element
+       'record_inline_create' => [
+               'path' => '/record/inline/create',
+               'target' => Controller\FormInlineAjaxController::class . '::createAction'
+       ],
+
+       // Synchronize localization
+       'record_inline_synchronizelocalize' => [
+               'path' => '/record/inline/synchronizelocalize',
+               'target' => Controller\FormInlineAjaxController::class . '::synchronizeLocalizeAction'
+       ],
+
+       // Expand / Collapse inline record
+       'record_inline_expandcollapse' => [
+               'path' => '/record/inline/expandcollapse',
+               'target' => Controller\FormInlineAjaxController::class . '::expandOrCollapseAction'
+       ],
+
+       // Search records
+       'record_suggest' => [
+               'path' => '/wizard/suggest/search',
+               'target' => \TYPO3\CMS\Backend\Form\Wizard\SuggestWizard::class . '::searchAction'
+       ],
+
+       // Get shortcut edit form
+       'shortcut_editform' => [
+               'path' => '/shortcut/editform',
+               'target' => \TYPO3\CMS\Backend\Backend\ToolbarItems\ShortcutToolbarItem::class . '::editFormAction'
+       ],
+
+       // Save edited shortcut
+       'shortcut_saveform' => [
+               'path' => '/shortcut/saveform',
+               'target' => \TYPO3\CMS\Backend\Backend\ToolbarItems\ShortcutToolbarItem::class . '::saveFormAction'
+       ],
+
+       // Render shortcut toolbar item
+       'shortcut_list' => [
+               'path' => '/shortcut/list',
+               'target' => \TYPO3\CMS\Backend\Backend\ToolbarItems\ShortcutToolbarItem::class . '::menuAction'
+       ],
+
+       // Delete a shortcut
+       'shortcut_remove' => [
+               'path' => '/shortcut/remove',
+               'target' => \TYPO3\CMS\Backend\Backend\ToolbarItems\ShortcutToolbarItem::class . '::removeShortcutAction'
+       ],
+
+       // Create a new shortcut
+       'shortcut_create' => [
+               'path' => '/shortcut/create',
+               'target' => \TYPO3\CMS\Backend\Backend\ToolbarItems\ShortcutToolbarItem::class . '::createShortcutAction'
+       ],
+
+       // Render systeminformtion toolbar item
+       'systeminformation_render' => [
+               'path' => '/system-information/render',
+               'target' => \TYPO3\CMS\Backend\Backend\ToolbarItems\SystemInformationToolbarItem::class . '::renderMenuAction'
+       ],
+
+       // Reload the module menu
+       'modulemenu' => [
+               'path' => '/module-menu',
+               'target' => Controller\BackendController::class . '::getModuleMenu'
+       ],
+
+       // Log in into backend
+       'login' => [
+               'path' => '/login',
+               'target' => \TYPO3\CMS\Backend\AjaxLoginHandler::class . '::loginAction',
+               'access' => 'public'
+       ],
+
+       // Log out from backend
+       'logout' => [
+               'path' => '/logout',
+               'target' => \TYPO3\CMS\Backend\AjaxLoginHandler::class . '::logoutAction',
+               'access' => 'public'
+       ],
+
+       // Refresh login of backend
+       'login_refresh' => [
+               'path' => '/login/refresh',
+               'target' => \TYPO3\CMS\Backend\AjaxLoginHandler::class . '::refreshAction',
+       ],
+
+       // Check if backend session has timed out
+       'login_timedout' => [
+               'path' => '/login/timedout',
+               'target' => \TYPO3\CMS\Backend\AjaxLoginHandler::class . '::isTimedOutAction',
+               'access' => 'public'
+       ],
+
+       // ExtDirect routing
+       'ext_direct_route' => [
+               'path' => '/ext-direct/route',
+               'target' => \TYPO3\CMS\Core\ExtDirect\ExtDirectRouter::class . '::routeAction'
+       ],
+
+       // ExtDirect API
+       'ext_direct_api' => [
+               'path' => '/ext-direct/api',
+               'target' => \TYPO3\CMS\Core\ExtDirect\ExtDirectApi::class . '::getAPI'
+       ],
+
+       // Render flash messages
+       'flashmessages_render' => [
+               'path' => '/flashmessages/render',
+               'target' => \TYPO3\CMS\Backend\Template\DocumentTemplate::class . '::renderQueuedFlashMessages'
+       ],
+
+       // Load context menu for
+       'contextmenu' => [
+               'path' => '/context-menu',
+               'target' => Controller\ClickMenuController::class . '::getContextMenuAction'
+       ],
+
+       // Process data handler commands
+       'record_process' => [
+               'path' => '/record/process',
+               'target' => Controller\SimpleDataHandlerController::class . '::processAjaxRequest'
+       ],
+
+       // Process user settings
+       'usersettings_process' => [
+               'path' => '/usersettings/process',
+               'target' => Controller\UserSettingsController::class . '::processAjaxRequest'
+       ],
+
+       // Open the image manipulation wizard
+       'wizard_image_manipulation' => [
+               'path' => '/wizard/image-manipulation',
+               'target' => \TYPO3\CMS\Backend\Form\Wizard\ImageManipulationWizard::class . '::getWizardAction'
+       ],
+
+       // Save a newly added online media
+       'livesearch' => [
+               'path' => '/livesearch',
+               'target' => Controller\LiveSearchController::class . '::liveSearchAction'
+       ],
+
+       // Save a newly added online media
+       'online_media_create' => [
+               'path' => '/online-media/create',
+               'target' => Controller\OnlineMediaController::class . '::createAction'
+       ]
+];
index 6d704f7..0f1fa6e 100644 (file)
@@ -200,5 +200,5 @@ return [
        'record_edit' => [
                'path' => '/record/edit',
                'target' => Controller\EditDocumentController::class . '::mainAction'
-       ],
+       ]
 ];
index 44582d3..0719121 100644 (file)
@@ -222,7 +222,7 @@ define('TYPO3/CMS/Backend/AjaxDataHandler', ['jquery', 'TYPO3/CMS/Backend/Notifi
         * @private
         */
        AjaxDataHandler._call = function(params) {
-               return $.getJSON(TYPO3.settings.ajaxUrls['DataHandler::process'], params);
+               return $.getJSON(TYPO3.settings.ajaxUrls['record_process'], params);
        };
 
        /**
index 0802c01..0901a6c 100644 (file)
@@ -84,7 +84,7 @@ define('TYPO3/CMS/Backend/ClickMenu', ['jquery'], function($) {
         * @return void
         */
        ClickMenu.fetch = function(parameters) {
-               var url = TYPO3.settings.ajaxUrls['ContextMenu::load'];
+               var url = TYPO3.settings.ajaxUrls['contextmenu'];
                if (parameters) {
                        url += ((url.indexOf('?') == -1) ? '?' : '&') + parameters;
                }
index 531641c..b9c0cdb 100644 (file)
@@ -22,7 +22,7 @@ define('TYPO3/CMS/Backend/ContextHelp', ['jquery', 'TYPO3/CMS/Backend/Popover',
         * @type {{ajaxUrl: *, localCache: {}, openContext: null}}
         */
        var ContextHelp = {
-               ajaxUrl: TYPO3.settings.ajaxUrls['ContextHelpAjaxController::dispatch'],
+               ajaxUrl: TYPO3.settings.ajaxUrls['context_help'],
                localCache: {},
                helpModuleUrl: '',
                trigger: 'click',
index c746614..bb94c81 100644 (file)
@@ -124,17 +124,17 @@ define('TYPO3/CMS/Backend/DragUploader', ['jquery', 'moment', 'nprogress', 'TYPO
                        $.each(files, function(i, file) {
 
                                ajaxCalls[i] = $.ajax({
-                                       url: TYPO3.settings.ajaxUrls['TYPO3_tcefile::fileExists'],
+                                       url: TYPO3.settings.ajaxUrls['file_exists'],
                                        data: {
                                                fileName: file.name,
                                                fileTarget: me.target
                                        },
                                        cache: false,
                                        success: function(response) {
-                                               var fileExists = typeof response.result !== 'undefined';
+                                               var fileExists = response !== false;
                                                if (fileExists) {
                                                        askForOverride.push({
-                                                               original: response.result,
+                                                               original: response,
                                                                uploaded: file,
                                                                action: me.irreObjectUid ? actions.USE_EXISTING : actions.SKIP
                                                        });
@@ -224,7 +224,7 @@ define('TYPO3/CMS/Backend/DragUploader', ['jquery', 'moment', 'nprogress', 'TYPO
                                me.queueLength--;
                                if (me.queueLength === 0) {
                                        $.ajax({
-                                               url: TYPO3.settings.ajaxUrls['DocumentTemplate::getFlashMessages'],
+                                               url: TYPO3.settings.ajaxUrls['flashmessages_render'],
                                                cache: false,
                                                success: function(data) {
                                                        var messages = $('#typo3-messages');
@@ -406,23 +406,23 @@ define('TYPO3/CMS/Backend/DragUploader', ['jquery', 'moment', 'nprogress', 'TYPO
                };
 
                me.uploadSuccess = function(data) {
-                       if (data.result.upload) {
+                       if (data.upload) {
                                me.dragUploader.decrementQueueLength();
                                me.$row.removeClass('uploading');
-                               me.$fileName.text(data.result.upload[0].name);
+                               me.$fileName.text(data.upload[0].name);
                                me.$progressPercentage.text('');
                                me.$progressMessage.text('100%');
                                me.$progressBar.outerWidth('100%');
 
                                // replace file icon
                                if (data.result.upload[0].icon) {
-                                       me.$iconCol.html('<a href="#" class="t3-js-clickmenutrigger" data-table="' + data.result.upload[0].id + '" data-listframe="1">' + data.result.upload[0].icon + '&nbsp;</span></a>');
+                                       me.$iconCol.html('<a href="#" class="t3-js-clickmenutrigger" data-table="' + data.upload[0].id + '" data-listframe="1">' + data.upload[0].icon + '&nbsp;</span></a>');
                                }
 
                                if (me.dragUploader.irreObjectUid) {
                                        DragUploader.addFileToIrre(
                                                me.dragUploader.irreObjectUid,
-                                               data.result.upload[0]
+                                               data.upload[0]
                                        );
                                        setTimeout(function() {
                                                me.$row.remove();
@@ -433,7 +433,7 @@ define('TYPO3/CMS/Backend/DragUploader', ['jquery', 'moment', 'nprogress', 'TYPO
                                        }, 3000);
                                } else {
                                        setTimeout(function() {
-                                               me.showFileInfo(data.result.upload[0]);
+                                               me.showFileInfo(data.upload[0]);
                                                me.dragUploader.$trigger.trigger('uploadSuccess', [me, data]);
                                        }, 3000);
                                }
@@ -511,7 +511,7 @@ define('TYPO3/CMS/Backend/DragUploader', ['jquery', 'moment', 'nprogress', 'TYPO
                        formData.append('upload_1', me.file);
 
                        var s = $.extend(true, {}, $.ajaxSettings, {
-                               url: TYPO3.settings.ajaxUrls['TYPO3_tcefile::process'],
+                               url: TYPO3.settings.ajaxUrls['file_process'],
                                contentType: false,
                                processData: false,
                                data: formData,
index 0c253d9..2e7cc29 100644 (file)
@@ -23,7 +23,7 @@ define('TYPO3/CMS/Backend/FormEngineSuggest', ['jquery', 'jquery/autocomplete'],
                        pid = $searchField.data('pid'),
                        newRecordRow = $searchField.data('recorddata'),
                        minimumCharacters = $searchField.data('minchars'),
-                       url = TYPO3.settings.ajaxUrls['t3lib_TCEforms_suggest::searchRecord'],
+                       url = TYPO3.settings.ajaxUrls['record_suggest'],
                        params = {
                                'table': table,
                                'field': field,
index 6f990af..9996c45 100644 (file)
@@ -97,7 +97,7 @@ define(['jquery'], function($) {
        };
 
        Tree = {
-               ajaxID: 'SC_alt_db_navframe::expandCollapse',   // has to be either "SC_alt_db_navframe::expandCollapse" or "SC_alt_file_navframe::expandCollapse"
+               ajaxID: 'sc_alt_db_navframe_expandtoggle',      // has to be either "sc_alt_db_navframe_expandtoggle" or "sc_alt_file_navframe_expandtoggle"
                frameSetModule: null,
                activateDragDrop: true,
                highlightClass: 'active',
index 23766c5..35be20a 100644 (file)
@@ -18,7 +18,7 @@ define('TYPO3/CMS/Backend/LiveSearch', ['jquery', 'jquery/autocomplete'], functi
 
        var containerSelector = '#typo3-cms-backend-backend-toolbaritems-livesearchtoolbaritem';
        var searchFieldSelector = '.t3js-topbar-navigation-search-field';
-       var url = TYPO3.settings.ajaxUrls['LiveSearch'];
+       var url = TYPO3.settings.ajaxUrls['livesearch'];
        var category = '';
 
        var initialize = function() {
index 0712f3f..dc68d04 100644 (file)
@@ -109,7 +109,7 @@ define('TYPO3/CMS/Backend/LoginRefresh', ['jquery', 'bootstrap'], function($) {
                        }),
                        $('<button />', {class: 'btn btn-primary t3js-active', 'data-action': 'refreshSession'}).text(TYPO3.LLL.core.refresh_login_refresh_button).on('click', function() {
                                $.ajax({
-                                       url: TYPO3.settings.ajaxUrls['BackendLogin::isTimedOut'],
+                                       url: TYPO3.settings.ajaxUrls['login_timedout'],
                                        method: 'GET',
                                        success: function() {
                                                LoginRefresh.hideTimeoutModal();
@@ -196,7 +196,7 @@ define('TYPO3/CMS/Backend/LoginRefresh', ['jquery', 'bootstrap'], function($) {
                LoginRefresh.$loginForm.find('.modal-header h4').text(TYPO3.LLL.core.refresh_login_title);
                LoginRefresh.$loginForm.find('.modal-body').append(
                        $('<p />').text(TYPO3.LLL.core.login_expired),
-                       $('<form />', {id: 'beLoginRefresh', method: 'POST', action: TYPO3.settings.ajaxUrls['BackendLogin::login']}).append(
+                       $('<form />', {id: 'beLoginRefresh', method: 'POST', action: TYPO3.settings.ajaxUrls['login']}).append(
                                $('<div />', {class: 'form-group'}).append(
                                        $('<input />', {type: 'password', name: 'p_field', autofocus: 'autofocus', class: 'form-control', placeholder: TYPO3.LLL.core.refresh_login_password, 'data-rsa-encryption': 't3-loginrefres-userident'})
                                ),
@@ -219,7 +219,7 @@ define('TYPO3/CMS/Backend/LoginRefresh', ['jquery', 'bootstrap'], function($) {
        LoginRefresh.showLoginForm = function() {
                // log off for sure
                $.ajax({
-                       url: TYPO3.settings.ajaxUrls['BackendLogin::logout'],
+                       url: TYPO3.settings.ajaxUrls['logout'],
                        method: 'GET',
                        success: function() {
                                if (TYPO3.configuration.showRefreshLoginPopup) {
@@ -395,7 +395,7 @@ define('TYPO3/CMS/Backend/LoginRefresh', ['jquery', 'bootstrap'], function($) {
         */
        LoginRefresh.checkActiveSession = function() {
                $.ajax({
-                       url: TYPO3.settings.ajaxUrls['BackendLogin::isTimedOut'],
+                       url: TYPO3.settings.ajaxUrls['login_timedout'],
                        data: {
                                skipSessionUpdate: 1
                        },
index 231ee6e..85244f1 100644 (file)
@@ -34,7 +34,7 @@ define('TYPO3/CMS/Backend/OnlineMedia', ['jquery', 'nprogress', 'TYPO3/CMS/Lang/
 
                me.addOnlineMedia = function(url) {
                        NProgress.start();
-                       $.post(TYPO3.settings.ajaxUrls['OnlineMedia::add'],
+                       $.post(TYPO3.settings.ajaxUrls['online_media_create'],
                                {
                                        url: url,
                                        targetFolder: me.target,
index d0d6db1..9d8bfa5 100644 (file)
@@ -69,25 +69,25 @@ define('TYPO3/CMS/Backend/Storage', ['jquery'], function ($) {
        };
 
        Storage.Persistent.addToList = function(key, value) {
-               return $.ajax(TYPO3.settings.ajaxUrls['UserSettings::process'], {data: {'action': 'addToList', key: key, value: value}}).done(function(data) {
+               return $.ajax(TYPO3.settings.ajaxUrls['usersettings_process'], {data: {'action': 'addToList', key: key, value: value}}).done(function(data) {
                        Storage.Persistent._data = data;
                });
        };
 
        Storage.Persistent.removeFromList = function(key, value) {
-               return $.ajax(TYPO3.settings.ajaxUrls['UserSettings::process'], {data: {'action': 'removeFromList', key: key, value: value}}).done(function(data) {
+               return $.ajax(TYPO3.settings.ajaxUrls['usersettings_process'], {data: {'action': 'removeFromList', key: key, value: value}}).done(function(data) {
                        Storage.Persistent._data = data;
                });
        };
 
        Storage.Persistent.unset = function(key) {
-               return $.ajax(TYPO3.settings.ajaxUrls['UserSettings::process'], {data: {'action': 'unset', key: key}}).done(function(data) {
+               return $.ajax(TYPO3.settings.ajaxUrls['usersettings_process'], {data: {'action': 'unset', key: key}}).done(function(data) {
                        Storage.Persistent._data = data;
                });
        };
 
        Storage.Persistent.clear = function() {
-               $.ajax(TYPO3.settings.ajaxUrls['UserSettings::process'], {data: {'action': 'clear'}});
+               $.ajax(TYPO3.settings.ajaxUrls['usersettings_process'], {data: {'action': 'clear'}});
                this._data = false;
        };
 
@@ -113,7 +113,7 @@ define('TYPO3/CMS/Backend/Storage', ['jquery'], function ($) {
         * @private
         */
        Storage.Persistent._loadFromServer = function() {
-               return $.ajax(TYPO3.settings.ajaxUrls['UserSettings::process'], {data: {'action': 'getAll'}, async: false}).done(function(data) {
+               return $.ajax(TYPO3.settings.ajaxUrls['usersettings_process'], {data: {'action': 'getAll'}, async: false}).done(function(data) {
                        Storage.Persistent._data = data;
                });
        };
@@ -125,7 +125,7 @@ define('TYPO3/CMS/Backend/Storage', ['jquery'], function ($) {
         * @private
         */
        Storage.Persistent._storeOnServer = function(key, value) {
-               return $.ajax(TYPO3.settings.ajaxUrls['UserSettings::process'], {data: {'action': 'set', key: key, value: value}}).done(function(data) {
+               return $.ajax(TYPO3.settings.ajaxUrls['usersettings_process'], {data: {'action': 'set', key: key, value: value}}).done(function(data) {
                        Storage.Persistent._data = data;
                });
        };
index 8dd8c72..c718eeb 100644 (file)
@@ -41,7 +41,7 @@ define('TYPO3/CMS/Backend/Toolbar/ShortcutMenu', ['jquery'], function($) {
        ShortcutMenu.editShortcut = function($shortcutRecord) {
                // load the form
                $.ajax({
-                       url: TYPO3.settings.ajaxUrls['ShortcutMenu::getShortcutEditForm'],
+                       url: TYPO3.settings.ajaxUrls['shortcut_editform'],
                        data: {
                                shortcutId: $shortcutRecord.data('shortcutid'),
                                shortcutGroup: $shortcutRecord.data('shortcutgroup')
@@ -57,7 +57,7 @@ define('TYPO3/CMS/Backend/Toolbar/ShortcutMenu', ['jquery'], function($) {
         */
        ShortcutMenu.saveShortcutForm = function($shortcutRecord) {
                $.ajax({
-                       url: TYPO3.settings.ajaxUrls['ShortcutMenu::saveShortcut'],
+                       url: TYPO3.settings.ajaxUrls['shortcut_saveform'],
                        data: {
                                shortcutId: $shortcutRecord.data('shortcutid'),
                                shortcutTitle: $shortcutRecord.find(ShortcutMenu.options.shortcutFormTitleSelector).val(),
@@ -79,7 +79,7 @@ define('TYPO3/CMS/Backend/Toolbar/ShortcutMenu', ['jquery'], function($) {
                top.TYPO3.Modal.confirm('Delete bookmark', 'Do you really want to remove this bookmark?')
                        .on('confirm.button.ok', function() {
                                $.ajax({
-                                       url: TYPO3.settings.ajaxUrls['ShortcutMenu::delete'],
+                                       url: TYPO3.settings.ajaxUrls['shortcut_list'],
                                        data: {
                                                shortcutId: $shortcutRecord.data('shortcutid')
                                        },
@@ -112,7 +112,7 @@ define('TYPO3/CMS/Backend/Toolbar/ShortcutMenu', ['jquery'], function($) {
                                        var $existingItem = $toolbarItemIcon.replaceWith($spinner);
 
                                        $.ajax({
-                                               url: TYPO3.settings.ajaxUrls['ShortcutMenu::create'],
+                                               url: TYPO3.settings.ajaxUrls['shortcut_create'],
                                                type: 'post',
                                                data: {
                                                        module: moduleName,
@@ -143,7 +143,7 @@ define('TYPO3/CMS/Backend/Toolbar/ShortcutMenu', ['jquery'], function($) {
         */
        ShortcutMenu.refreshMenu = function() {
                $.ajax({
-                       url: TYPO3.settings.ajaxUrls['ShortcutMenu::render'],
+                       url: TYPO3.settings.ajaxUrls['shortcut_list'],
                        type: 'get',
                        cache: false
                }).done(function(data) {
index 35a9ca2..5849c7d 100644 (file)
@@ -52,7 +52,7 @@ define('TYPO3/CMS/Backend/Toolbar/SystemInformationMenu', ['jquery', 'TYPO3/CMS/
                }
 
                $.ajax({
-                       url: TYPO3.settings.ajaxUrls['SystemInformationMenu::load'],
+                       url: TYPO3.settings.ajaxUrls['systeminformation_render'],
                        type: 'post',
                        cache: false,
                        success: function(data) {
index c5cb5dd..0299307 100644 (file)
@@ -151,7 +151,7 @@ var inline = {
 
        getRecordDetails: function (objectId, returnURL) {
                var context = this.getContext(this.parseObjectId('full', objectId, 0, 1));
-               inline.makeAjaxCall('getRecordDetails', [objectId, returnURL], true, context);
+               inline.makeAjaxCall('details', [objectId, returnURL], true, context);
                return false;
        },
 
@@ -161,7 +161,7 @@ var inline = {
                        if (recordUid) {
                                objectId += this.structureSeparator + recordUid;
                        }
-                       this.makeAjaxCall('createNewRecord', [objectId], true, context);
+                       this.makeAjaxCall('create', [objectId], true, context);
                } else {
                        var message = TBE_EDITOR.labels.maxItemsAllowed.replace('{0}', this.data.config[objectId].max);
                        var matches = objectId.match(/^(data-\d+-.*?-\d+-.*?)-(.*?)$/);
@@ -177,18 +177,18 @@ var inline = {
        synchronizeLocalizeRecords: function (objectId, type) {
                var context = this.getContext(objectId);
                var parameters = [objectId, type];
-               this.makeAjaxCall('synchronizeLocalizeRecords', parameters, true, context);
+               this.makeAjaxCall('synchronizelocalize', parameters, true, context);
        },
 
        setExpandedCollapsedState: function (objectId, expand, collapse) {
                var context = this.getContext(objectId);
-               this.makeAjaxCall('setExpandedCollapsedState', [objectId, expand, collapse], false, context);
+               this.makeAjaxCall('expandcollapse', [objectId, expand, collapse], false, context);
        },
 
        makeAjaxCall: function (method, params, lock, context) {
                var url = '', urlParams = '', options = {};
                if (method && params && params.length && this.lockAjaxMethod(method, lock)) {
-                       url = TYPO3.settings.ajaxUrls['t3lib_TCEforms_inline::' + method];
+                       url = TYPO3.settings.ajaxUrls['record_inline_' + method];
                        urlParams = '';
                        for (var i = 0, max = params.length; i < max; i++) {
                                urlParams += '&ajax[' + i + ']=' + encodeURIComponent(params[i]);
@@ -319,7 +319,7 @@ var inline = {
                        if (!this.data.unique || !this.data.unique[objectId]) {
                                $selector.find('option').eq(selectedIndex).prop('selected', false);
                        }
-                       this.makeAjaxCall('createNewRecord', [objectId, selectedValue], true, context);
+                       this.makeAjaxCall('create', [objectId, selectedValue], true, context);
                }
                return false;
        },
@@ -327,7 +327,7 @@ var inline = {
        // foreign_selector: used by element browser (type='group/db')
        importElement: function (objectId, table, uid, type) {
                var context = this.getContext(objectId);
-               inline.makeAjaxCall('createNewRecord', [objectId, uid], true, context);
+               inline.makeAjaxCall('create', [objectId, uid], true, context);
        },
 
        importElementMultiple: function (objectId, table, uidArray, type) {
@@ -336,7 +336,7 @@ var inline = {
                });
        },
        delayedImportElement: function (objectId, table, uid, type) {
-               if (inline.lockedAjaxMethod['createNewRecord'] == true) {
+               if (inline.lockedAjaxMethod['create'] == true) {
                        window.setTimeout("inline.delayedImportElement('" + objectId + "','" + table + "'," + uid + ", null );",
                                300);
                } else {
index ba1c996..257ce59 100644 (file)
@@ -203,7 +203,7 @@ TYPO3.ModuleMenu.App = {
 
                // refresh the HTML by fetching the menu again
        refreshMenu: function() {
-               TYPO3.jQuery.ajax(TYPO3.settings.ajaxUrls['ModuleMenu::reload']).done(function(result) {
+               TYPO3.jQuery.ajax(TYPO3.settings.ajaxUrls['modulemenu']).done(function(result) {
                        TYPO3.jQuery('#typo3-menu').replaceWith(result.menu);
                        if (top.currentModuleLoaded) {
                                TYPO3.ModuleMenu.App.highlightModuleMenuItem(top.currentModuleLoaded);
index 9af6f37..a95780f 100644 (file)
@@ -14,6 +14,9 @@ namespace TYPO3\CMS\Backend\Tests\Unit\Controller\File;
  * The TYPO3 project - inspiring people to share!
  */
 
+use TYPO3\CMS\Core\Http\ServerRequest;
+use TYPO3\CMS\Core\Http\Response;
+
 /**
  * Tests for \TYPO3\CMS\Backend\Tests\Unit\Controller\File\FileController
  */
@@ -40,9 +43,14 @@ class FileControllerTest extends \TYPO3\CMS\Core\Tests\UnitTestCase {
        protected $mockFileProcessor;
 
        /**
-        * @var \TYPO3\CMS\Core\Http\AjaxRequestHandler|\PHPUnit_Framework_MockObject_MockObject
+        * @var ServerRequest|\PHPUnit_Framework_MockObject_MockObject
         */
-       protected $mockAjaxRequestHandler;
+       protected $request;
+
+       /**
+        * @var Response|\PHPUnit_Framework_MockObject_MockObject
+        */
+       protected $response;
 
        /**
         * Sets up this test case.
@@ -55,6 +63,9 @@ class FileControllerTest extends \TYPO3\CMS\Core\Tests\UnitTestCase {
                $this->fileResourceMock->expects($this->any())->method('toArray')->will($this->returnValue(array('id' => 'foo')));
                $this->fileResourceMock->expects($this->any())->method('getModificationTime')->will($this->returnValue(123456789));
                $this->fileResourceMock->expects($this->any())->method('getExtension')->will($this->returnValue('html'));
+
+               $this->request = new ServerRequest();
+               $this->response = new Response();
        }
 
        /**
@@ -92,7 +103,6 @@ class FileControllerTest extends \TYPO3\CMS\Core\Tests\UnitTestCase {
         */
        public function processAjaxRequestDeleteProcessActuallyDoesNotChangeFileData() {
                $this->fileController = $this->getAccessibleMock(\TYPO3\CMS\Backend\Controller\File\FileController::class, array('init', 'main'));
-               $this->mockAjaxRequestHandler = $this->getMock(\TYPO3\CMS\Core\Http\AjaxRequestHandler::class, array('addContent', 'setContentFormat'), array(), '', FALSE);
 
                $fileData = array('delete' => array(TRUE));
                $this->fileController->_set('fileProcessor', $this->mockFileProcessor);
@@ -100,10 +110,8 @@ class FileControllerTest extends \TYPO3\CMS\Core\Tests\UnitTestCase {
                $this->fileController->_set('redirect', FALSE);
 
                $this->fileController->expects($this->once())->method('main');
-               $this->mockAjaxRequestHandler->expects($this->once())->method('addContent')->with('result', $fileData);
-               $this->mockAjaxRequestHandler->expects($this->once())->method('setContentFormat')->with('json');
 
-               $this->fileController->processAjaxRequest(array(), $this->mockAjaxRequestHandler);
+               $this->fileController->processAjaxRequest($this->request, $this->response);
        }
 
        /**
@@ -111,7 +119,6 @@ class FileControllerTest extends \TYPO3\CMS\Core\Tests\UnitTestCase {
         */
        public function processAjaxRequestEditFileProcessActuallyDoesNotChangeFileData() {
                $this->fileController = $this->getAccessibleMock(\TYPO3\CMS\Backend\Controller\File\FileController::class, array('init', 'main'));
-               $this->mockAjaxRequestHandler = $this->getMock(\TYPO3\CMS\Core\Http\AjaxRequestHandler::class, array('addContent', 'setContentFormat'), array(), '', FALSE);
 
                $fileData = array('editfile' => array(TRUE));
                $this->fileController->_set('fileProcessor', $this->mockFileProcessor);
@@ -119,10 +126,8 @@ class FileControllerTest extends \TYPO3\CMS\Core\Tests\UnitTestCase {
                $this->fileController->_set('redirect', FALSE);
 
                $this->fileController->expects($this->once())->method('main');
-               $this->mockAjaxRequestHandler->expects($this->once())->method('addContent')->with('result', $fileData);
-               $this->mockAjaxRequestHandler->expects($this->once())->method('setContentFormat')->with('json');
 
-               $this->fileController->processAjaxRequest(array(), $this->mockAjaxRequestHandler);
+               $this->fileController->processAjaxRequest($this->request, $this->response);
        }
 
        /**
@@ -130,7 +135,6 @@ class FileControllerTest extends \TYPO3\CMS\Core\Tests\UnitTestCase {
         */
        public function processAjaxRequestUnzipProcessActuallyDoesNotChangeFileData() {
                $this->fileController = $this->getAccessibleMock(\TYPO3\CMS\Backend\Controller\File\FileController::class, array('init', 'main'));
-               $this->mockAjaxRequestHandler = $this->getMock(\TYPO3\CMS\Core\Http\AjaxRequestHandler::class, array('addContent', 'setContentFormat'), array(), '', FALSE);
 
                $fileData = array('unzip' => array(TRUE));
                $this->fileController->_set('fileProcessor', $this->mockFileProcessor);
@@ -138,10 +142,8 @@ class FileControllerTest extends \TYPO3\CMS\Core\Tests\UnitTestCase {
                $this->fileController->_set('redirect', FALSE);
 
                $this->fileController->expects($this->once())->method('main');
-               $this->mockAjaxRequestHandler->expects($this->once())->method('addContent')->with('result', $fileData);
-               $this->mockAjaxRequestHandler->expects($this->once())->method('setContentFormat')->with('json');
 
-               $this->fileController->processAjaxRequest(array(), $this->mockAjaxRequestHandler);
+               $this->fileController->processAjaxRequest($this->request, $this->response);
        }
 
 }
index b4545fb..61d0d63 100644 (file)
@@ -14,9 +14,10 @@ namespace TYPO3\CMS\Beuser\Controller;
  * The TYPO3 project - inspiring people to share!
  */
 
+use Psr\Http\Message\ResponseInterface;
+use Psr\Http\Message\ServerRequestInterface;
 use TYPO3\CMS\Backend\Utility\BackendUtility;
 use TYPO3\CMS\Core\DataHandling\DataHandler;
-use TYPO3\CMS\Core\Http\AjaxRequestHandler;
 use TYPO3\CMS\Core\Imaging\Icon;
 use TYPO3\CMS\Core\Imaging\IconFactory;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
@@ -67,11 +68,11 @@ class PermissionAjaxController {
        /**
         * The main dispatcher function. Collect data and prepare HTML output.
         *
-        * @param array $params array of parameters from the AJAX interface, currently unused
-        * @param AjaxRequestHandler $ajaxObj object of type AjaxRequestHandler
-        * @return void
+        * @param ServerRequestInterface $request
+        * @param ResponseInterface $response
+        * @return ResponseInterface
         */
-       public function dispatch($params = array(), AjaxRequestHandler $ajaxObj = NULL) {
+       public function dispatch(ServerRequestInterface $request, ResponseInterface $response) {
                $extPath = ExtensionManagementUtility::extPath('beuser');
 
                $view = GeneralUtility::makeInstance(StandaloneView::class);
@@ -106,7 +107,8 @@ class PermissionAjaxController {
                                                $view->assign('username', $usernameArray[$userId]['username']);
                                                $content = $view->render();
                                        } else {
-                                               $ajaxObj->setError('An error occurred: No page owner uid specified.');
+                                               $response->getBody()->write('An error occurred: No page owner uid specified');
+                                               $response = $response->withStatus(500);
                                        }
                                        break;
                                case 'show_change_group_selector':
@@ -128,7 +130,8 @@ class PermissionAjaxController {
                                                $view->assign('groupname', $groupnameArray[$groupId]['title']);
                                                $content = $view->render();
                                        } else {
-                                               $ajaxObj->setError('An error occurred: No page group uid specified.');
+                                               $response->getBody()->write('An error occurred: No page group uid specified');
+                                               $response = $response->withStatus(500);
                                        }
                                        break;
                                case 'toggle_edit_lock':
@@ -159,9 +162,12 @@ class PermissionAjaxController {
                                        $content = $view->render();
                        }
                } else {
-                       $ajaxObj->setError('This script cannot be called directly.');
+                       $response->getBody()->write('This script cannot be called directly');
+                       $response = $response->withStatus(500);
                }
-               $ajaxObj->addContent($this->conf['page'] . '_' . $this->conf['who'], $content);
+               $response->getBody()->write($content);
+               $response = $response->withHeader('Content-Type', 'text/html; charset=utf-8');
+               return $response;
        }
 
        /**
diff --git a/typo3/sysext/beuser/Configuration/Backend/AjaxRoutes.php b/typo3/sysext/beuser/Configuration/Backend/AjaxRoutes.php
new file mode 100644 (file)
index 0000000..8c5cd5e
--- /dev/null
@@ -0,0 +1,12 @@
+<?php
+
+/**
+ * Definitions for routes provided by EXT:beuser
+ */
+return [
+       // Dispatch the permissions actions
+       'user_access_permissions' => [
+               'path' => '/users/access/permissions',
+               'target' => \TYPO3\CMS\Beuser\Controller\PermissionAjaxController::class . '::dispatch'
+       ]
+];
index ff51660..caec925 100644 (file)
@@ -21,7 +21,7 @@ define('TYPO3/CMS/Beuser/Permissions', ['jquery'], function($) {
                        containerSelector: '#typo3-permissionList'
                }
        };
-       var ajaxUrl = TYPO3.settings.ajaxUrls['PermissionAjaxController::dispatch'];
+       var ajaxUrl = TYPO3.settings.ajaxUrls['user_access_permissions'];
 
        /**
         * changes the value of the permissions in the form
index 29bce42..b452716 100644 (file)
@@ -35,8 +35,4 @@ if (TYPO3_MODE === 'BE') {
                        'navigationComponentId' => 'typo3-pagetree'
                )
        );
-
-       // Register AJAX Controller
-       \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::registerAjaxHandler('PermissionAjaxController::dispatch',
-               \TYPO3\CMS\Beuser\Controller\PermissionAjaxController::class . '->dispatch');
 }
index f0fec36..8de02ad 100644 (file)
@@ -14,7 +14,8 @@ namespace TYPO3\CMS\ContextHelp\Controller;
  * The TYPO3 project - inspiring people to share!
  */
 
-use TYPO3\CMS\Core\Http\AjaxRequestHandler;
+use Psr\Http\Message\ResponseInterface;
+use Psr\Http\Message\ServerRequestInterface;
 use TYPO3\CMS\Core\Imaging\Icon;
 use TYPO3\CMS\Core\Imaging\IconFactory;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
@@ -28,19 +29,21 @@ class ContextHelpAjaxController {
        /**
         * The main dispatcher function. Collect data and prepare HTML output.
         *
-        * @param array $params array of parameters, currently unused
-        * @param AjaxRequestHandler $ajaxObj object of type AjaxRequestHandler
-        * @return void
+        * @param ServerRequestInterface $request
+        * @param ResponseInterface $response
+        * @return ResponseInterface
         */
-       public function dispatch($params = array(), AjaxRequestHandler $ajaxObj = NULL) {
-               $params = GeneralUtility::_GP('params');
+       public function getHelpAction(ServerRequestInterface $request, ResponseInterface $response) {
+               $params = isset($request->getParsedBody()['params']) ? $request->getParsedBody()['params'] : $request->getQueryParams()['params'];
                if ($params['action'] === 'getContextHelp') {
                        $result = $this->getContextHelp($params['table'], $params['field']);
-                       $ajaxObj->addContent('title', $result['title']);
-                       $ajaxObj->addContent('content', $result['description']);
-                       $ajaxObj->addContent('link', $result['moreInfo']);
-                       $ajaxObj->setContentFormat('json');
+                       $response->getBody()->write(json_encode([
+                               'title' => $result['title'],
+                               'content' => $result['description'],
+                               'link' => $result['moreInfo']
+                       ]));
                }
+               return $response;
        }
 
        /**
diff --git a/typo3/sysext/context_help/Configuration/Backend/AjaxRoutes.php b/typo3/sysext/context_help/Configuration/Backend/AjaxRoutes.php
new file mode 100644 (file)
index 0000000..26fffbe
--- /dev/null
@@ -0,0 +1,12 @@
+<?php
+
+/**
+ * Definitions for routes provided by EXT:context_help
+ */
+return [
+       // Fetch data about the context help
+       'context_help' => [
+               'path' => '/context-help',
+               'target' => \TYPO3\CMS\ContextHelp\Controller\ContextHelpAjaxController::class . '::getHelpAction'
+       ]
+];
index 4a484e0..9ea3934 100644 (file)
@@ -13,9 +13,3 @@ defined('TYPO3_MODE') or die();
 // Labels for TYPO3 4.5 and greater.  These labels override the ones set above, while still falling back to the original labels if no translation is available.
 $GLOBALS['TYPO3_CONF_VARS']['SYS']['locallangXMLOverride']['EXT:context_help/Resources/Private/Language/locallang_csh_pages.xlf'][] = 'EXT:context_help/Resources/Private/Language/4.5/locallang_csh_pages.xlf';
 $GLOBALS['TYPO3_CONF_VARS']['SYS']['locallangXMLOverride']['EXT:context_help/Resources/Private/Language/locallang_csh_ttcontent.xlf'][] = 'EXT:context_help/Resources/Private/Language/4.5/locallang_csh_ttcontent.xlf';
-
-if (TYPO3_MODE === 'BE') {
-       // Register AJAX Controller
-       \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::registerAjaxHandler('ContextHelpAjaxController::dispatch',
-               \TYPO3\CMS\ContextHelp\Controller\ContextHelpAjaxController::class . '->dispatch');
-}
index ef9047c..5dd61e4 100644 (file)
@@ -1031,6 +1031,18 @@ class Bootstrap {
                                                $routesFromPackages += $definedRoutesInPackage;
                                        }
                                }
+                               $routesFileNameForPackage = $package->getPackagePath() . 'Configuration/Backend/AjaxRoutes.php';
+                               if (file_exists($routesFileNameForPackage)) {
+                                       $definedRoutesInPackage = require $routesFileNameForPackage;
+                                       if (is_array($definedRoutesInPackage)) {
+                                               foreach ($definedRoutesInPackage as $routeIdentifier => $routeOptions) {
+                                                       // prefix the route with "ajax_" as "namespace"
+                                                       $routeOptions['path'] = '/ajax' . $routeOptions['path'];
+                                                       $routesFromPackages['ajax_' . $routeIdentifier] = $routeOptions;
+                                                       $routesFromPackages['ajax_' . $routeIdentifier]['ajax'] = TRUE;
+                                               }
+                                       }
+                               }
                        }
                        // Store the data from all packages in the cache
                        $codeCache->set($cacheIdentifier, serialize($routesFromPackages));
index 2504cd5..87b99b5 100755 (executable)
@@ -14,6 +14,8 @@ namespace TYPO3\CMS\Core\ExtDirect;
  * The TYPO3 project - inspiring people to share!
  */
 
+use Psr\Http\Message\ResponseInterface;
+use Psr\Http\Message\ServerRequestInterface;
 use TYPO3\CMS\Backend\Utility\BackendUtility;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
 
@@ -46,12 +48,13 @@ class ExtDirectApi {
         * the API and are required by ExtDirect. The result is cached to improve the overall
         * performance.
         *
-        * @param array $ajaxParams Ajax parameters
-        * @param \TYPO3\CMS\Core\Http\AjaxRequestHandler $ajaxObj Ajax object
-        * @return void
+        * @param ServerRequestInterface $request
+        * @param ResponseInterface $response
+        * @return ResponseInterface
         */
-       public function getAPI($ajaxParams, \TYPO3\CMS\Core\Http\AjaxRequestHandler $ajaxObj) {
-               $ajaxObj->setContent(array());
+       public function getAPI(ServerRequestInterface $request, ResponseInterface $response) {
+               $response->getBody()->write(json_encode([]));
+               return $response;
        }
 
        /**
@@ -134,7 +137,7 @@ class ExtDirectApi {
                if (TYPO3_MODE === 'FE') {
                        $url = GeneralUtility::locationHeaderUrl('?eID=ExtDirect&action=route&namespace=' . rawurlencode($namespace));
                } else {
-                       $url = BackendUtility::getAjaxUrl('ExtDirect::route', array('namespace' => $namespace));
+                       $url = BackendUtility::getAjaxUrl('ext_direct_route', array('namespace' => $namespace));
                }
                return $url;
        }
index e42fe06..595f080 100755 (executable)
@@ -14,6 +14,8 @@ namespace TYPO3\CMS\Core\ExtDirect;
  * The TYPO3 project - inspiring people to share!
  */
 
+use Psr\Http\Message\ResponseInterface;
+use Psr\Http\Message\ServerRequestInterface;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
 
 /**
@@ -24,48 +26,48 @@ class ExtDirectRouter {
        /**
         * Dispatches the incoming calls to methods about the ExtDirect API.
         *
-        * @param aray $ajaxParams Ajax parameters
-        * @param \TYPO3\CMS\Core\Http\AjaxRequestHandler $ajaxObj Ajax object
-        * @return void
+        * @param ServerRequestInterface $request
+        * @param ResponseInterface $response
+        * @return ResponseInterface
         */
-       public function route($ajaxParams, \TYPO3\CMS\Core\Http\AjaxRequestHandler $ajaxObj) {
+       public function routeAction(ServerRequestInterface $request, ResponseInterface $response) {
                $GLOBALS['error'] = GeneralUtility::makeInstance(\TYPO3\CMS\Core\ExtDirect\ExtDirectDebug::class);
                $isForm = FALSE;
                $isUpload = FALSE;
                $rawPostData = file_get_contents('php://input');
-               $postParameters = GeneralUtility::_POST();
-               $namespace = GeneralUtility::_GET('namespace');
-               $response = array();
-               $request = NULL;
+               $postParameters = $request->getParsedBody();
+               $namespace = isset($request->getParsedBody()['namespace']) ? $request->getParsedBody()['namespace'] : $request->getQueryParams()['namespace'];
+               $extResponse = array();
+               $extRequest = NULL;
                $isValidRequest = TRUE;
                if (!empty($postParameters['extAction'])) {
                        $isForm = TRUE;
                        $isUpload = $postParameters['extUpload'] === 'true';
-                       $request = new \stdClass();
-                       $request->action = $postParameters['extAction'];
-                       $request->method = $postParameters['extMethod'];
-                       $request->tid = $postParameters['extTID'];
+                       $extRequest = new \stdClass();
+                       $extRequest->action = $postParameters['extAction'];
+                       $extRequest->method = $postParameters['extMethod'];
+                       $extRequest->tid = $postParameters['extTID'];
                        unset($_POST['securityToken']);
-                       $request->data = array($_POST + $_FILES);
-                       $request->data[] = $postParameters['securityToken'];
+                       $extRequest->data = array($_POST + $_FILES);
+                       $extRequest->data[] = $postParameters['securityToken'];
                } elseif (!empty($rawPostData)) {
-                       $request = json_decode($rawPostData);
+                       $extRequest = json_decode($rawPostData);
                } else {
-                       $response[] = array(
+                       $extResponse[] = array(
                                'type' => 'exception',
                                'message' => 'Something went wrong with an ExtDirect call!',
                                'code' => 'router'
                        );
                        $isValidRequest = FALSE;
                }
-               if (!is_array($request)) {
-                       $request = array($request);
+               if (!is_array($extRequest)) {
+                       $extRequest = array($extRequest);
                }
                if ($isValidRequest) {
                        $validToken = FALSE;
                        $firstCall = TRUE;
-                       foreach ($request as $index => $singleRequest) {
-                               $response[$index] = array(
+                       foreach ($extRequest as $index => $singleRequest) {
+                               $extResponse[$index] = array(
                                        'tid' => $singleRequest->tid,
                                        'action' => $singleRequest->action,
                                        'method' => $singleRequest->method
@@ -80,27 +82,27 @@ class ExtDirectRouter {
                                        if (!$validToken) {
                                                throw new \TYPO3\CMS\Core\FormProtection\Exception('ExtDirect: Invalid Security Token!');
                                        }
-                                       $response[$index]['type'] = 'rpc';
-                                       $response[$index]['result'] = $this->processRpc($singleRequest, $namespace);
-                                       $response[$index]['debug'] = $GLOBALS['error']->toString();
+                                       $extResponse[$index]['type'] = 'rpc';
+                                       $extResponse[$index]['result'] = $this->processRpc($singleRequest, $namespace);
+                                       $extResponse[$index]['debug'] = $GLOBALS['error']->toString();
                                } catch (\Exception $exception) {
-                                       $response[$index]['type'] = 'exception';
-                                       $response[$index]['message'] = $exception->getMessage();
-                                       $response[$index]['code'] = 'router';
+                                       $extResponse[$index]['type'] = 'exception';
+                                       $extResponse[$index]['message'] = $exception->getMessage();
+                                       $extResponse[$index]['code'] = 'router';
                                }
                        }
                }
                if ($isForm && $isUpload) {
-                       $ajaxObj->setContentFormat('plain');
-                       $response = json_encode($response);
-                       $response = preg_replace('/&quot;/', '\\&quot;', $response);
-                       $response = array(
-                               '<html><body><textarea>' . $response . '</textarea></body></html>'
+                       $extResponse = json_encode($extResponse);
+                       $extResponse = preg_replace('/&quot;/', '\\&quot;', $extResponse);
+                       $extResponse = array(
+                               '<html><body><textarea>' . $extResponse . '</textarea></body></html>'
                        );
                } else {
-                       $ajaxObj->setContentFormat('jsonbody');
+                       $extResponse = json_encode($extResponse);
                }
-               $ajaxObj->setContent($response);
+               $response->getBody()->write($extResponse);
+               return $response;
        }
 
        /**
index a4f2df3..b838172 100644 (file)
@@ -14,6 +14,8 @@ namespace TYPO3\CMS\Core\Page;
  * The TYPO3 project - inspiring people to share!
  */
 
+use TYPO3\CMS\Backend\Routing\Router;
+use TYPO3\CMS\Backend\Routing\UriBuilder;
 use TYPO3\CMS\Backend\Utility\BackendUtility;
 use TYPO3\CMS\Core\Localization\LocalizationFactory;
 use TYPO3\CMS\Core\Service\MarkerBasedTemplateService;
@@ -2064,6 +2066,22 @@ class PageRenderer implements \TYPO3\CMS\Core\SingletonInterface {
                foreach ($GLOBALS['TYPO3_CONF_VARS']['BE']['AJAX'] as $ajaxHandler => $_) {
                        $ajaxUrls[$ajaxHandler] = BackendUtility::getAjaxUrl($ajaxHandler);
                }
+
+               // also add the ajax-based routes
+               /** @var UriBuilder $uriBuilder */
+               $uriBuilder = GeneralUtility::makeInstance(UriBuilder::class);
+               /** @var Router $router */
+               $router = GeneralUtility::makeInstance(Router::class);
+               $routes = $router->getRoutes();
+               foreach ($routes as $routeIdentifier => $route) {
+                       if ($route->getOption('ajax')) {
+                               $uri = (string)$uriBuilder->buildUriFromRoute($routeIdentifier);
+                               // use the shortened value in order to use this in JavaScript
+                               $routeIdentifier = str_replace('ajax_', '', $routeIdentifier);
+                               $ajaxUrls[$routeIdentifier] = $uri;
+                       }
+               }
+
                $this->inlineSettings['ajaxUrls'] = $ajaxUrls;
        }
 
index 661e1a6..367066e 100644 (file)
@@ -873,130 +873,7 @@ return array(
                'versionNumberInFilename' => FALSE,                                     // <p>Boolean: If TRUE, included CSS and JS files will have the timestamp embedded in the filename, ie. filename.1269312081.js. This will make browsers and proxies reload the files if they change (thus avoiding caching issues). IMPORTANT: this feature requires extra .htaccess rules to work (please refer to _.htaccess or the _.htaccess file from the dummy package)</p><p>If FALSE the filemtime will be appended as a query-string.</p>
                'spriteIconGenerator_handler' => \TYPO3\CMS\Backend\Sprite\SimpleSpriteHandler::class,          // String: Used to register own/other spriteGenerating Handler, they have to implement the interface \TYPO3\CMS\Backend\Sprite\SpriteIconGeneratorInterface. If set to "\TYPO3\CMS\Backend\Sprite\SpriteBuildingHandler" icons from extensions will automatically merged into sprites.
                'debug' => FALSE,                                                                       // Boolean: If set, the loginrefresh is disabled and pageRenderer is set to debug mode. Use this to debug the backend only!
-               'AJAX' => array(                                                                        // array of key-value pairs for a unified use of AJAX calls in the TYPO3 backend. Keys are the unique ajaxIDs where the value will be resolved to call a method in an object. See ajax.php for more information.
-                       'SC_alt_db_navframe::expandCollapse' => array(
-                               'callbackMethod' => \TYPO3\CMS\Backend\Controller\PageTreeNavigationController::class . '->ajaxExpandCollapse',
-                               'csrfTokenCheck' => TRUE
-                       ),
-                       'SC_alt_file_navframe::expandCollapse' => array(
-                               'callbackMethod' => \TYPO3\CMS\Backend\Controller\FileSystemNavigationFrameController::class . '->ajaxExpandCollapse',
-                               'csrfTokenCheck' => TRUE
-                       ),
-                       'TYPO3_tcefile::process' => array(
-                               'callbackMethod' => \TYPO3\CMS\Backend\Controller\File\FileController::class . '->processAjaxRequest',
-                               'csrfTokenCheck' => TRUE
-                       ),
-                       'TYPO3_tcefile::fileExists' => array(
-                               'callbackMethod' => \TYPO3\CMS\Backend\Controller\File\FileController::class . '->fileExistsAjaxRequest',
-                               'csrfTokenCheck' => TRUE
-                       ),
-                       't3lib_TCEforms_inline::createNewRecord' => array(
-                               'callbackMethod' => \TYPO3\CMS\Backend\Controller\FormInlineAjaxController::class . '->processInlineAjaxRequest',
-                               'csrfTokenCheck' => TRUE
-                       ),
-                       't3lib_TCEforms_inline::getRecordDetails' => array(
-                               'callbackMethod' => \TYPO3\CMS\Backend\Controller\FormInlineAjaxController::class . '->processInlineAjaxRequest',
-                               'csrfTokenCheck' => TRUE
-                       ),
-                       't3lib_TCEforms_inline::synchronizeLocalizeRecords' => array(
-                               'callbackMethod' => \TYPO3\CMS\Backend\Controller\FormInlineAjaxController::class . '->processInlineAjaxRequest',
-                               'csrfTokenCheck' => TRUE
-                       ),
-                       't3lib_TCEforms_inline::setExpandedCollapsedState' => array(
-                               'callbackMethod' => \TYPO3\CMS\Backend\Controller\FormInlineAjaxController::class . '->processInlineAjaxRequest',
-                               'csrfTokenCheck' => TRUE
-                       ),
-                       't3lib_TCEforms_suggest::searchRecord' => array(
-                               'callbackMethod' => \TYPO3\CMS\Backend\Form\Wizard\SuggestWizard::class . '->processAjaxRequest',
-                               'csrfTokenCheck' => TRUE
-                       ),
-                       'ShortcutMenu::getShortcutEditForm' => array(
-                               'callbackMethod' => \TYPO3\CMS\Backend\Backend\ToolbarItems\ShortcutToolbarItem::class . '->getAjaxShortcutEditForm',
-                               'csrfTokenCheck' => TRUE
-                       ),
-                       'ShortcutMenu::saveShortcut' => array(
-                               'callbackMethod' => \TYPO3\CMS\Backend\Backend\ToolbarItems\ShortcutToolbarItem::class . '->setAjaxShortcut',
-                               'csrfTokenCheck' => TRUE
-                       ),
-                       'ShortcutMenu::render' => array(
-                               'callbackMethod' => \TYPO3\CMS\Backend\Backend\ToolbarItems\ShortcutToolbarItem::class . '->renderAjaxMenu',
-                               'csrfTokenCheck' => TRUE
-                       ),
-                       'ShortcutMenu::delete' => array(
-                               'callbackMethod' => \TYPO3\CMS\Backend\Backend\ToolbarItems\ShortcutToolbarItem::class . '->deleteAjaxShortcut',
-                               'csrfTokenCheck' => TRUE
-                       ),
-                       'ShortcutMenu::create' => array(
-                               'callbackMethod' => \TYPO3\CMS\Backend\Backend\ToolbarItems\ShortcutToolbarItem::class . '->createAjaxShortcut',
-                               'csrfTokenCheck' => TRUE
-                       ),
-                       'SystemInformationMenu::load' => array(
-                               'callbackMethod' => \TYPO3\CMS\Backend\Backend\ToolbarItems\SystemInformationToolbarItem::class . '->renderAjax',
-                               'csrfTokenCheck' => TRUE
-                       ),
-                       'ModuleMenu::reload' => array(
-                               'callbackMethod' => \TYPO3\CMS\Backend\Controller\BackendController::class . '->getModuleMenuForReload',
-                               'csrfTokenCheck' => TRUE
-                       ),
-                       'BackendLogin::login' => array(
-                               'callbackMethod' => \TYPO3\CMS\Backend\AjaxLoginHandler::class . '->login',
-                               // Needs to be unprotected
-                               'csrfTokenCheck' => FALSE
-                       ),
-                       'BackendLogin::logout' => array(
-                               'callbackMethod' => \TYPO3\CMS\Backend\AjaxLoginHandler::class . '->logout',
-                               // Needs to be unprotected
-                               'csrfTokenCheck' => FALSE
-                       ),
-                       'BackendLogin::refreshLogin' => array(
-                               'callbackMethod' => \TYPO3\CMS\Backend\AjaxLoginHandler::class . '->refreshLogin',
-                               // Needs to be unprotected
-                               'csrfTokenCheck' => FALSE
-                       ),
-                       'BackendLogin::isTimedOut' => array(
-                               'callbackMethod' => \TYPO3\CMS\Backend\AjaxLoginHandler::class . '->isTimedOut',
-                               // Needs to be unprotected
-                               'csrfTokenCheck' => FALSE
-                       ),
-                       'ExtDirect::getAPI' => array(
-                               'callbackMethod' => \TYPO3\CMS\Core\ExtDirect\ExtDirectApi::class . '->getAPI',
-                               // No need to be CSRF protected
-                               'csrfTokenCheck' => FALSE
-                       ),
-                       'ExtDirect::route' => array(
-                               'callbackMethod' => \TYPO3\CMS\Core\ExtDirect\ExtDirectRouter::class . '->route',
-                               // All ExtJS calls are CSRF protected with another token
-                               'csrfTokenCheck' => FALSE
-                       ),
-                       'DocumentTemplate::getFlashMessages' => array(
-                               'callbackMethod' => \TYPO3\CMS\Backend\Template\DocumentTemplate::class . '->renderFlashMessages',
-                               'csrfTokenCheck' => TRUE
-                       ),
-                       'ContextMenu::load' => array(
-                               'callbackMethod' => \TYPO3\CMS\Backend\Controller\ClickMenuController::class . '->printContentForAjaxRequest',
-                               'csrfTokenCheck' => TRUE
-                       ),
-                       'DataHandler::process' => array(
-                               'callbackMethod' => \TYPO3\CMS\Backend\Controller\SimpleDataHandlerController::class . '->processAjaxRequest',
-                               'csrfTokenCheck' => TRUE
-                       ),
-                       'UserSettings::process' => array(
-                               'callbackMethod' => \TYPO3\CMS\Backend\Controller\UserSettingsController::class . '->processAjaxRequest',
-                               'csrfTokenCheck' => TRUE
-                       ),
-                       'ImageManipulationWizard::getHtmlForImageManipulationWizard' => array(
-                               'callbackMethod' => \TYPO3\CMS\Backend\Form\Wizard\ImageManipulationWizard::class . '->getHtmlForImageManipulationWizard',
-                               'csrfTokenCheck' => TRUE
-                       ),
-                       'LiveSearch' => array(
-                               'callbackMethod' => \TYPO3\CMS\Backend\Controller\LiveSearchController::class . '->liveSearchAction',
-                               'csrfTokenCheck' => TRUE
-                       ),
-                       'OnlineMedia::add' => array(
-                               'callbackMethod' => \TYPO3\CMS\Backend\Controller\OnlineMediaController::class . '->addAjaxAction',
-                               'csrfTokenCheck' => TRUE
-                       )
-               ),
+               'AJAX' => array(),                                                                      // array of key-value pairs for a unified use of AJAX calls in the TYPO3 backend. Keys are the unique ajaxIDs where the value will be resolved to call a method in an object. See ajax.php for more information.
                'toolbarItems' => array(), // Array: Registered toolbar items classes
                'HTTP' => array(
                        'Response' => array(
index 6b7a17e..1d7bd5a 100644 (file)
@@ -11,7 +11,7 @@ A Backend AJAX call to check whether a file exists has been added. The call need
 Impact
 ======
 
-The method can be called with ``TYPO3.settings.ajaxUrls['TYPO3_tcefile::fileExists']``.
+The method can be called with ``TYPO3.settings.ajaxUrls['file_exists']``.
 The parameters ``fileName`` and ``fileTarget`` are required:
 
 * fileName: Name of the file
diff --git a/typo3/sysext/core/Documentation/Changelog/master/Breaking-69916-HookAjaxSaveCodeOfT3editorChanged.rst b/typo3/sysext/core/Documentation/Changelog/master/Breaking-69916-HookAjaxSaveCodeOfT3editorChanged.rst
new file mode 100644 (file)
index 0000000..5174b11
--- /dev/null
@@ -0,0 +1,27 @@
+========================================================
+Breaking: #69916 - Hook ajaxSaveCode of t3editor changed
+========================================================
+
+Description
+===========
+
+The ``$ajaxObj`` parameter has been replaced by PSR-7-compliant ``$request`` and ``$response`` objects.
+
+
+Impact
+======
+
+Using the ``$ajaxObj`` parameter will result in a fatal error.
+
+
+Affected Installations
+======================
+
+All 3rd party extensions using the ``$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/t3editor/classes/class.tx_t3editor.php']['ajaxSaveCode']``
+hook are affected.
+
+
+Migration
+=========
+
+Make use of ServerRequestInterface and ResponseInterface, see :file:`typo3/sysext/t3editor/Classes/Hook/FileEditHook.php` for reference.
\ No newline at end of file
diff --git a/typo3/sysext/core/Documentation/Changelog/master/Breaking-69916-RegisteredAJAXHandlersReplacedByRoutes.rst b/typo3/sysext/core/Documentation/Changelog/master/Breaking-69916-RegisteredAJAXHandlersReplacedByRoutes.rst
new file mode 100644 (file)
index 0000000..aba5472
--- /dev/null
@@ -0,0 +1,156 @@
+==============================================================
+Breaking: #69916 - Registered AJAX handlers replaced by routes
+==============================================================
+
+Description
+===========
+
+AJAX handlers registered in the core by ``ExtensionManagementUtility::registerAjaxHandler()`` have been replaced
+by AJAX routes, which are registered inside any extension under Configuration/Backend/AjaxRoutes.php.
+
+The routes registered in AjaxRoutes.php are available via JavaScript via ``TYPO3.settings.ajaxUrls[routeIdentifier]``.
+
+Impact
+======
+
+Calling removed AJAX identifiers will result in an error. Please see the table below for migration.
+
+
+Affected Installations
+======================
+
+All 3rd party extensions using one of the removed handlers is affected.
+
+
+Migration
+=========
+
+Please see the table to get the new AJAX identifier.
+
+EXT:backend
+^^^^^^^^^^^
+
+==========================================================   =================================   ==================================
+Old identifier                                               New identifier                      New AJAX ID
+==========================================================   =================================   ==================================
+SC_alt_db_navframe::expandCollapse                           sc_alt_db_navframe_expandtoggle     /ajax/sc-alt-db-navframe/expandtoggle
+SC_alt_file_navframe::expandCollapse                         sc_alt_file_navframe_expandtoggle   /ajax/sc-alt-file-navframe/expandtoggle
+TYPO3_tcefile::process                                       file_process                        /ajax/file/process
+TYPO3_tcefile::fileExists                                    file_exists                         /ajax/file/exists
+t3lib_TCEforms_inline::createNewRecord                       record_inline_create                /ajax/inline/create
+t3lib_TCEforms_inline::getRecordDetails                      record_inline_details               /ajax/inline/record-details
+t3lib_TCEforms_inline::synchronizeLocalizeRecords            record_inline_synchronizelocalize   /ajax/inline/synchronizelocalize
+t3lib_TCEforms_inline::setExpandedCollapsedState             record_inline_expandcollapse        /ajax/inline/expandcollapse
+t3lib_TCEforms_suggest::searchRecord                         record_suggest                      /ajax/wizard/suggest/search
+ShortcutMenu::getShortcutEditForm                            shortcut_editform                   /ajax/shortcut/editform
+ShortcutMenu::saveShortcut                                   shortcut_saveform                   /ajax/shortcut/saveform
+ShortcutMenu::render                                         shortcut_list                       /ajax/shortcut/list
+ShortcutMenu::delete                                         shortcut_remove                     /ajax/shortcut/remove
+ShortcutMenu::create                                         shortcut_create                     /ajax/shortcut/create
+SystemInformationMenu::load                                  systeminformation_render            /ajax/system-information/render
+ModuleMenu::reload                                           modulemenu                          /ajax/module-menu
+BackendLogin::login                                          login                               /ajax/login
+BackendLogin::logout                                         logout                              /ajax/logout
+BackendLogin::refreshLogin                                   login_refresh                       /ajax/login/refresh
+BackendLogin::isTimedOut                                     login_timedout                      /ajax/login/timedout
+ExtDirect::getAPI                                            ext_direct_api                      /ajax/ext-direct/api
+ExtDirect::route                                             ext_direct_route                    /ajax/ext-direct/route
+DocumentTemplate::getFlashMessages                           flashmessages_render                /ajax/flashmessages/render
+ContextMenu::load                                            contextmenu                         /ajax/context-menu
+DataHandler::process                                         record_process                      /ajax/record/process
+UserSettings::process                                        usersettings_process                /ajax/user-settings/process
+ImageManipulationWizard::getHtmlForImageManipulationWizard   wizard_image_manipulation           /ajax/wizard/image-manipulation
+LiveSearch                                                   livesearch                          /ajax/livesearch
+OnlineMedia::add                                             online_media_create                 /ajax/online-media/create
+==========================================================   =================================   ==================================
+
+EXT:beuser
+^^^^^^^^^^
+
+==================================   =======================   =========================
+Old identifier                       New identifier            New AJAX ID
+==================================   =======================   =========================
+PermissionAjaxController::dispatch   user_access_permissions   /users/access/permissions
+==================================   =======================   =========================
+
+EXT:context_help
+^^^^^^^^^^^^^^^^
+
+===================================   =====================   ======================
+Old identifier                        New identifier          New AJAX ID
+===================================   =====================   ======================
+ContextHelpAjaxController::dispatch   context_help            /context-help
+===================================   =====================   ======================
+
+EXT:opendocs
+^^^^^^^^^^^^
+
+===================================   =====================   ======================
+Old identifier                        New identifier          New AJAX ID
+===================================   =====================   ======================
+TxOpendocs::renderMenu                opendocs_menu           /opendocs/menu
+TxOpendocs::closeDocument             opendocs_close          /opendocs/close
+===================================   =====================   ======================
+
+EXT:recycler
+^^^^^^^^^^^^
+
+===================================   =====================   ======================
+Old identifier                        New identifier          New AJAX ID
+===================================   =====================   ======================
+RecyclerAjaxController::dispatch      recycler                /recycler
+===================================   =====================   ======================
+
+EXT:rsaauth
+^^^^^^^^^^^
+
+===================================   =====================   ======================
+Old identifier                        New identifier          New AJAX ID
+===================================   =====================   ======================
+BackendLogin::getRsaPublicKey         rsa_publickey           /rsa/publickey
+RsaEncryption::getRsaPublicKey        rsa_publickey           /rsa/publickey
+===================================   =====================   ======================
+
+EXT:rtehtmlarea
+^^^^^^^^^^^^^^^
+
+===================================   ========================   ======================
+Old identifier                        New identifier             New AJAX ID
+===================================   ========================   ======================
+rtehtmlarea::spellchecker             rtehtmlarea_spellchecker   /rte/spellchecker
+===================================   ========================   ======================
+
+EXT:t3editor
+^^^^^^^^^^^^
+
+====================================   =====================================   =======================================
+Old identifier                         New identifier                          New AJAX ID
+====================================   =====================================   =======================================
+T3Editor::saveCode                     t3editor_save                           /t3editor/save
+T3Editor::getPlugins                   t3editor_get_plugins                    /t3editor/get-plugins
+T3Editor_TSrefLoader::getTypes         t3editor_tsref                          /t3editor/tsref
+T3Editor_TSrefLoader::getDescription   t3editor_tsref                          /t3editor/tsref
+CodeCompletion::loadTemplates          t3editor_codecompletion_loadtemplates   /t3editor/codecompletion/load-templates
+====================================   =====================================   =======================================
+
+* T3Editor_TSrefLoader::getTypes and T3Editor_TSrefLoader::getDescription have been combined. The separation is done by
+the new parameter ``fetch`` being either "types" or "description".
+
+EXT:taskcenter
+^^^^^^^^^^^^^^
+
+===================================   ========================   ======================
+Old identifier                        New identifier             New AJAX ID
+===================================   ========================   ======================
+Taskcenter::saveCollapseState         taskcenter_collapse        /taskcenter/collapse
+Taskcenter::saveSortingState          taskcenter_sort            /taskcenter/sort
+===================================   ========================   ======================
+
+EXT:workspaces
+^^^^^^^^^^^^^^
+
+===================================   ========================   ======================
+Old identifier                        New identifier             New AJAX ID
+===================================   ========================   ======================
+Workspaces::setWorkspace              workspace_switch           /workspaces/switch
+===================================   ========================   ======================
\ No newline at end of file
diff --git a/typo3/sysext/core/Documentation/Changelog/master/Breaking-69916-RemovedBackendLogingetRsaPublicKeyAJAXHandler.rst b/typo3/sysext/core/Documentation/Changelog/master/Breaking-69916-RemovedBackendLogingetRsaPublicKeyAJAXHandler.rst
new file mode 100644 (file)
index 0000000..8917370
--- /dev/null
@@ -0,0 +1,28 @@
+=====================================================================
+Breaking: #69916 - Removed BackendLogin::getRsaPublicKey AJAX handler
+=====================================================================
+
+Description
+===========
+
+The deprecated AJAX handler ``BackendLogin::getRsaPublicKey`` has been removed in favor of ``rsa_publickey``. As
+``getRsaPublicKey`` was the only method in this class, the file
+:file:`typo3/sysext/rsaauth/Classes/Backend/AjaxLoginHandler.php` has been removed without substitution.
+
+
+Impact
+======
+
+Calling the removed handler will result in an error.
+
+
+Affected Installations
+======================
+
+All 3rd party extensions using the removed handler are affected.
+
+
+Migration
+=========
+
+Use the AJAX handler ``rsa_publickey`` instead of ``BackendLogin::getRsaPublicKey``.
\ No newline at end of file
diff --git a/typo3/sysext/core/Documentation/Changelog/master/Feature-69916-PSR-7-basedRoutingForBackendAJAXRequests.rst b/typo3/sysext/core/Documentation/Changelog/master/Feature-69916-PSR-7-basedRoutingForBackendAJAXRequests.rst
new file mode 100644 (file)
index 0000000..8e2fcf7
--- /dev/null
@@ -0,0 +1,33 @@
+===============================================================
+Feature: #69916 - PSR-7-based Routing for Backend AJAX Requests
+===============================================================
+
+Description
+===========
+
+Support for PSR-7-based Routing for Backend AJAX requests has been added.
+
+
+Impact
+======
+
+To add a route for an AJAX request, create the :file:`Configuration/Backend/AjaxRoutes.php` of your extension:
+
+.. code-block:: php
+
+       return [
+               // Does something
+               'unique_route_name' => [
+                       'path' => '/toolcollection/some-action',
+                       'target' => \ACME\Controller\SomeController::class . '::myAction',
+               ]
+       ];
+
+The unique_route_name (route identifier) parameter acts as the previously known key to
+call ``BackendUtility::getAjaxUrl()`` passed as parameter to the action refers to the route path,
+**not** to the route identifier itself. AJAX handlers configured in :file:`AjaxRoutes.php` are **not** compatible
+with definitions in :file:`ext_localconf.php` registered by ``ExtensionManagementUtility::registerAjaxHandler()``
+due to different method signatures in the target actions, using PSR-7.
+
+The route identifier is used in ``BackendUtility::getAjaxUrl()`` as `$ajaxIdentifier`` and as key in the global
+``TYPO3.settings.ajaxUrls`` JavaScript object.
\ No newline at end of file
index 8d5bcb4..f8a3876 100644 (file)
@@ -14,6 +14,8 @@ namespace TYPO3\CMS\Opendocs\Backend\ToolbarItems;
  * The TYPO3 project - inspiring people to share!
  */
 
+use Psr\Http\Message\ResponseInterface;
+use Psr\Http\Message\ServerRequestInterface;
 use TYPO3\CMS\Backend\Toolbar\ToolbarItemInterface;
 use TYPO3\CMS\Core\Imaging\Icon;
 use TYPO3\CMS\Core\Imaging\IconFactory;
@@ -212,13 +214,13 @@ class OpendocsToolbarItem implements ToolbarItemInterface {
        /**
         * Closes a document in the session and
         *
-        * @param array $params Array of parameters from the AJAX interface, currently unused
-        * @param \TYPO3\CMS\Core\Http\AjaxRequestHandler $ajaxObj Object of type AjaxRequestHandler
-        * @return string List item HTML attributes
+        * @param ServerRequestInterface $request
+        * @param ResponseInterface $response
+        * @return ResponseInterface
         */
-       public function closeDocument($params = array(), \TYPO3\CMS\Core\Http\AjaxRequestHandler $ajaxObj = NULL) {
+       public function closeDocument(ServerRequestInterface $request, ResponseInterface $response) {
                $backendUser = $this->getBackendUser();
-               $md5sum = \TYPO3\CMS\Core\Utility\GeneralUtility::_GP('md5sum');
+               $md5sum = isset($request->getParsedBody()['md5sum']) ? $request->getParsedBody()['md5sum'] : $request->getQueryParams()['md5sum'];
                if ($md5sum && isset($this->openDocs[$md5sum])) {
                        // Add the document to be closed to the recent documents
                        $this->recentDocs = array_merge(array($md5sum => $this->openDocs[$md5sum]), $this->recentDocs);
@@ -232,18 +234,20 @@ class OpendocsToolbarItem implements ToolbarItemInterface {
                        $backendUser->pushModuleData('FormEngine', array($this->openDocs, $docDat));
                        $backendUser->pushModuleData('opendocs::recent', $this->recentDocs);
                }
-               $this->renderAjax($params, $ajaxObj);
+               return $this->renderMenu($request, $response);
        }
 
        /**
         * Renders the menu so that it can be returned as response to an AJAX call
         *
-        * @param array $params Array of parameters from the AJAX interface, currently unused
-        * @param \TYPO3\CMS\Core\Http\AjaxRequestHandler $ajaxObj Object of type AjaxRequestHandler
-        * @return void
+        * @param ServerRequestInterface $request
+        * @param ResponseInterface $response
+        * @return ResponseInterface
         */
-       public function renderAjax($params = array(), \TYPO3\CMS\Core\Http\AjaxRequestHandler $ajaxObj = NULL) {
-               $ajaxObj->addContent('opendocsMenu', $this->getDropDown());
+       public function renderMenu(ServerRequestInterface $request, ResponseInterface $response) {
+               $response->getBody()->write($this->getDropDown());
+               $response = $response->withHeader('Content-Type', 'html');
+               return $response;
        }
 
        /**
diff --git a/typo3/sysext/opendocs/Configuration/Backend/AjaxRoutes.php b/typo3/sysext/opendocs/Configuration/Backend/AjaxRoutes.php
new file mode 100644 (file)
index 0000000..e48a197
--- /dev/null
@@ -0,0 +1,18 @@
+<?php
+
+/**
+ * Definitions for routes provided by EXT:opendocs
+ */
+return [
+       // Render the opendocs toolbar item
+       'opendocs_menu' => [
+               'path' => '/opendocs/menu',
+               'target' => \TYPO3\CMS\Opendocs\Backend\ToolbarItems\OpendocsToolbarItem::class . '::renderMenu'
+       ],
+
+       // Close a document
+       'opendocs_closedoc' => [
+               'path' => '/opendocs/close',
+               'target' => \TYPO3\CMS\Opendocs\Backend\ToolbarItems\OpendocsToolbarItem::class . '::closeDocument'
+       ],
+];
index 3e908d0..694df37 100644 (file)
@@ -58,7 +58,7 @@ define('TYPO3/CMS/Opendocs/Toolbar/OpendocsMenu', ['jquery'], function($) {
                var $existingIcon = $toolbarItemIcon.replaceWith($spinnerIcon);
 
                $.ajax({
-                       url: TYPO3.settings.ajaxUrls['TxOpendocs::renderMenu'],
+                       url: TYPO3.settings.ajaxUrls['opendocs_menu'],
                        type: 'post',
                        cache: false,
                        success: function(data) {
@@ -85,7 +85,7 @@ define('TYPO3/CMS/Opendocs/Toolbar/OpendocsMenu', ['jquery'], function($) {
         */
        OpendocsMenu.closeDocument = function(md5sum) {
                $.ajax({
-                       url: TYPO3.settings.ajaxUrls['TxOpendocs::closeDocument'],
+                       url: TYPO3.settings.ajaxUrls['opendocs_close'],
                        type: 'post',
                        cache: false,
                        data: {
index 7a107ab..6b47a56 100644 (file)
@@ -2,9 +2,6 @@
 defined('TYPO3_MODE') or die();
 
 if (TYPO3_MODE === 'BE') {
-       // Register AJAX calls
-       \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::registerAjaxHandler('TxOpendocs::renderMenu', \TYPO3\CMS\Opendocs\Backend\ToolbarItems\OpendocsToolbarItem::class . '->renderAjax');
-       \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::registerAjaxHandler('TxOpendocs::closeDocument', \TYPO3\CMS\Opendocs\Backend\ToolbarItems\OpendocsToolbarItem::class . '->closeDocument');
        // Register update signal to update the number of open documents
        $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_befunc.php']['updateSignalHook']['OpendocsController::updateNumber'] = \TYPO3\CMS\Opendocs\Backend\ToolbarItems\OpendocsToolbarItem::class . '->updateNumberOfOpenDocsHook';
 }
index 3160e49..34b07fe 100755 (executable)
@@ -910,7 +910,7 @@ class ElementBrowser {
        protected function main_rte($wiz = FALSE) {
                // needs to be executed before doc->startPage()
                if (in_array($this->act, array('file', 'folder'))) {
-                       $this->doc->getDragDropCode('folders', 'Tree.ajaxID = "SC_alt_file_navframe::expandCollapse"');
+                       $this->doc->getDragDropCode('folders', 'Tree.ajaxID = "sc_alt_file_navframe_expandtoggle"');
                } elseif ($this->act === 'page') {
                        $this->doc->getDragDropCode('pages');
                }
@@ -1406,7 +1406,7 @@ class ElementBrowser {
         */
        protected function main_file() {
                // include JS files and set prefs for foldertree
-               $this->doc->getDragDropCode('folders', 'Tree.ajaxID = "SC_alt_file_navframe::expandCollapse"');
+               $this->doc->getDragDropCode('folders', 'Tree.ajaxID = "sc_alt_file_navframe_expandtoggle"');
                // Starting content:
                $content = $this->doc->startPage('TBE file selector');
                // Add the FlashMessages if any
@@ -1541,7 +1541,7 @@ class ElementBrowser {
        protected function main_folder() {
                // include JS files
                // Setting prefs for foldertree
-               $this->doc->getDragDropCode('folders', 'Tree.ajaxID = "SC_alt_file_navframe::expandCollapse";');
+               $this->doc->getDragDropCode('folders', 'Tree.ajaxID = "sc_alt_file_navframe_expandtoggle";');
                // Starting content:
                $content = $this->doc->startPage('TBE folder selector');
                // Add the FlashMessages if any
index 28bc822..5ecd497 100644 (file)
@@ -14,7 +14,8 @@ namespace TYPO3\CMS\Recycler\Controller;
  * The TYPO3 project - inspiring people to share!
  */
 
-use TYPO3\CMS\Core\Http\AjaxRequestHandler;
+use Psr\Http\Message\ResponseInterface;
+use Psr\Http\Message\ServerRequestInterface;
 use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
 use TYPO3\CMS\Extbase\Utility\LocalizationUtility;
@@ -53,11 +54,11 @@ class RecyclerAjaxController {
        /**
         * The main dispatcher function. Collect data and prepare HTML output.
         *
-        * @param array $params array of parameters from the AJAX interface, currently unused
-        * @param AjaxRequestHandler $ajaxObj object of type AjaxRequestHandler
-        * @return void
+        * @param ServerRequestInterface $request
+        * @param ResponseInterface $response
+        * @return ResponseInterface
         */
-       public function dispatch($params = array(), AjaxRequestHandler $ajaxObj = NULL) {
+       public function dispatch(ServerRequestInterface $request, ResponseInterface $response) {
                $extPath = ExtensionManagementUtility::extPath('recycler');
                /* @var $view StandaloneView */
                $view = GeneralUtility::makeInstance(StandaloneView::class);
@@ -141,8 +142,8 @@ class RecyclerAjaxController {
                                );
                                break;
                }
-               $ajaxObj->setContentFormat('json');
-               $ajaxObj->setContent($content);
+               $response->getBody()->write(json_encode($content));
+               return $response;
        }
 
        /**
diff --git a/typo3/sysext/recycler/Configuration/Backend/AjaxRoutes.php b/typo3/sysext/recycler/Configuration/Backend/AjaxRoutes.php
new file mode 100644 (file)
index 0000000..6a3863f
--- /dev/null
@@ -0,0 +1,12 @@
+<?php
+
+/**
+ * Definitions for routes provided by EXT:recycler
+ */
+return [
+       // Startup the recycler module
+       'recycler' => [
+               'path' => '/recycler',
+               'target' => \TYPO3\CMS\Recycler\Controller\RecyclerAjaxController::class . '::dispatch'
+       ],
+];
index 55fcca7..f0a54ad 100644 (file)
@@ -254,7 +254,7 @@ define(['jquery', 'nprogress', 'TYPO3/CMS/Backend/jquery.clearable'], function($
         */
        Recycler.loadAvailableTables = function() {
                $.ajax({
-                       url: TYPO3.settings.ajaxUrls['RecyclerAjaxController::dispatch'],
+                       url: TYPO3.settings.ajaxUrls['recycler'],
                        dataType: 'json',
                        async: false,
                        data: {
@@ -300,7 +300,7 @@ define(['jquery', 'nprogress', 'TYPO3/CMS/Backend/jquery.clearable'], function($
         */
        Recycler.loadDeletedElements = function() {
                $.ajax({
-                       url: TYPO3.settings.ajaxUrls['RecyclerAjaxController::dispatch'],
+                       url: TYPO3.settings.ajaxUrls['recycler_dispatch'],
                        dataType: 'json',
                        data: {
                                action: 'getDeletedRecords',
@@ -427,7 +427,7 @@ define(['jquery', 'nprogress', 'TYPO3/CMS/Backend/jquery.clearable'], function($
                }
 
                $.ajax({
-                       url: TYPO3.settings.ajaxUrls['RecyclerAjaxController::dispatch'],
+                       url: TYPO3.settings.ajaxUrls['recycler_dispatch'],
                        dataType: 'json',
                        data: data,
                        beforeSend: function() {
index a665fc3..fc8c42e 100644 (file)
@@ -1,9 +1,6 @@
 <?php
 defined('TYPO3_MODE') or die();
 
-if (TYPO3_MODE === 'BE') {
-       \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::registerAjaxHandler('RecyclerAjaxController::dispatch', \TYPO3\CMS\Recycler\Controller\RecyclerAjaxController::class . '->dispatch');
-}
 \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::registerAjaxHandler(
        'RecyclerAjaxController::init',
        \TYPO3\CMS\Recycler\Task\CleanerTask::class . '->init'
diff --git a/typo3/sysext/rsaauth/Classes/Backend/AjaxLoginHandler.php b/typo3/sysext/rsaauth/Classes/Backend/AjaxLoginHandler.php
deleted file mode 100644 (file)
index f12cc3c..0000000
+++ /dev/null
@@ -1,35 +0,0 @@
-<?php
-namespace TYPO3\CMS\Rsaauth\Backend;
-
-/*
- * 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!
- */
-
-/**
- * Class AjaxLoginHandler
- */
-class AjaxLoginHandler {
-
-       /**
-        * Gets RSA Public Key.
-        *
-        * @param array $parameters Parameters (not used)
-        * @param \TYPO3\CMS\Core\Http\AjaxRequestHandler $parent The calling parent AJAX object
-        * @deprecated since TYPO3 CMS 7, will be removed in TYPO3 CMS 8. Please use RsaEncryption::getRsaPublicKey as ajax handler instead.
-        */
-       public function getRsaPublicKey(array $parameters, \TYPO3\CMS\Core\Http\AjaxRequestHandler $parent) {
-               \TYPO3\CMS\Core\Utility\GeneralUtility::logDeprecatedFunction();
-               $rsaEncryptionEncoder = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(\TYPO3\CMS\Rsaauth\RsaEncryptionEncoder::class);
-               $rsaEncryptionEncoder->getRsaPublicKeyAjaxHandler($parameters, $parent);
-       }
-
-}
\ No newline at end of file
index 35997d4..55f1c33 100644 (file)
@@ -14,8 +14,8 @@ namespace TYPO3\CMS\Rsaauth;
  * The TYPO3 project - inspiring people to share!
  */
 
-use TYPO3\CMS\Backend\Utility\BackendUtility;
-use TYPO3\CMS\Core\Http\AjaxRequestHandler;
+use Psr\Http\Message\ResponseInterface;
+use Psr\Http\Message\ServerRequestInterface;
 use TYPO3\CMS\Core\Page\PageRenderer;
 use TYPO3\CMS\Core\SingletonInterface;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
@@ -97,19 +97,24 @@ class RsaEncryptionEncoder implements SingletonInterface {
        /**
         * Ajax handler to return a RSA public key.
         *
-        * @param array $parameters Parameters (not used)
-        * @param AjaxRequestHandler $parent The calling parent AJAX object
+        * @param ServerRequestInterface $request
+        * @param ResponseInterface $response
+        * @return ResponseInterface
         */
-       public function getRsaPublicKeyAjaxHandler(array $parameters, AjaxRequestHandler $parent) {
+       public function getRsaPublicKeyAjaxHandler(ServerRequestInterface $request, ResponseInterface $response) {
                $keyPair = $this->getRsaPublicKey();
                if ($keyPair !== NULL) {
-                       $parent->addContent('publicKeyModulus', $keyPair->getPublicKeyModulus());
-                       $parent->addContent('spacer', ':');
-                       $parent->addContent('exponent', sprintf('%x', $keyPair->getExponent()));
-                       $parent->setContentFormat('plain');
+                       $response->getBody()->write(implode('', [
+                               'publicKeyModulus' => $keyPair->getPublicKeyModulus(),
+                               'spacer' => ':',
+                               'exponent' => sprintf('%x', $keyPair->getExponent())
+                       ]));
+                       $response = $response->withHeader('Content-Type', 'text/html; charset=utf-8');
                } else {
-                       $parent->setError('No OpenSSL backend could be obtained for rsaauth.');
+                       $response->getBody()->write('No OpenSSL backend could be obtained for rsaauth.');
+                       $response = $response->withStatus(500);
                }
+               return $response;
        }
 
 }
diff --git a/typo3/sysext/rsaauth/Configuration/Backend/AjaxRoutes.php b/typo3/sysext/rsaauth/Configuration/Backend/AjaxRoutes.php
new file mode 100644 (file)
index 0000000..3f3aab8
--- /dev/null
@@ -0,0 +1,12 @@
+<?php
+
+/**
+ * Definitions for routes provided by EXT:rsaauth
+ */
+return [
+       // Get RSA public key
+       'rsa_publickey' => [
+               'path' => '/rsa/publickey',
+               'target' => \TYPO3\CMS\Rsaauth\RsaEncryptionEncoder::class . '::getRsaPublicKeyAjaxHandler'
+       ],
+];
index 17373f4..9ccaa85 100644 (file)
@@ -63,7 +63,7 @@ define('TYPO3/CMS/Rsaauth/RsaEncryptionModule', ['jquery', './RsaLibrary'], func
                                RsaEncryption.$currentForm = $(this);
 
                                $.ajax({
-                                       url: TYPO3.settings.ajaxUrls['RsaEncryption::getRsaPublicKey'],
+                                       url: TYPO3.settings.ajaxUrls['rsa_publickey'],
                                        data: {'skipSessionUpdate': 1},
                                        success: RsaEncryption.handlePublicKeyResponse
                                });
index ea7a75c..b99197c 100644 (file)
@@ -25,18 +25,6 @@ $GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['felogin']['loginFormOnSubmitFuncs']['rsa
 // Add a hook to show Backend warnings
 $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_befunc.php']['displayWarningMessages']['rsaauth'] = \TYPO3\CMS\Rsaauth\BackendWarnings::class;
 
-\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::registerAjaxHandler(
-       'BackendLogin::getRsaPublicKey',
-       \TYPO3\CMS\Rsaauth\Backend\AjaxLoginHandler::class . '->getRsaPublicKey',
-       FALSE
-);
-
-\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::registerAjaxHandler(
-       'RsaEncryption::getRsaPublicKey',
-       \TYPO3\CMS\Rsaauth\RsaEncryptionEncoder::class . '->getRsaPublicKeyAjaxHandler',
-       FALSE
-);
-
 // eID for FrontendLoginRsaPublicKey
 $GLOBALS['TYPO3_CONF_VARS']['FE']['eID_include']['RsaPublicKeyGenerationController'] = \TYPO3\CMS\Rsaauth\Controller\RsaPublicKeyGenerationController::class . '::processRequest';
 
index 2235312..d88fec3 100644 (file)
@@ -146,7 +146,7 @@ class BrowseLinks extends ElementBrowser {
                $pageRenderer = $this->getPageRenderer();
                $pageRenderer->addCssFile(ExtensionManagementUtility::extRelPath('t3skin') . 'rtehtmlarea/htmlarea.css');
                $pageRenderer->loadRequireJsModule('TYPO3/CMS/Backend/LegacyTree', 'function(Tree) {
-                       Tree.ajaxID = "SC_alt_file_navframe::expandCollapse";
+                       Tree.ajaxID = "sc_alt_file_navframe_expandtoggle";
                }');
        }
 
index 107e707..d43cd79 100644 (file)
@@ -148,16 +148,13 @@ class SpellCheckingController {
        /**
         * AJAX entry point
         *
-        * @param array $ajaxParams
-        * @return void
+        * @param ServerRequestInterface $request
+        * @param ResponseInterface $response
+        * @return ResponseInterface
         * @throws \UnexpectedValueException
         */
-       public function main(array $ajaxParams) {
-               /** @var Response $response */
-               $response = GeneralUtility::makeInstance(Response::class);
-               $this->processRequest($ajaxParams['request'], $response);
-               header('Content-Type: text/html; charset=' . strtoupper($this->parserCharset));
-               echo $this->result;
+       public function main(ServerRequestInterface $request, ResponseInterface $response) {
+               return $this->processRequest($request, $response);
        }
 
        /**
index ef3a039..5b4c421 100644 (file)
@@ -92,7 +92,7 @@ class Spellchecker extends RteHtmlAreaApi {
                        $jsArray[] = 'RTEarea[editornumber].buttons.' . $button . '.contentISOLanguage = "' . $this->configuration['contentISOLanguage'] . '";';
                        $jsArray[] = 'RTEarea[editornumber].buttons.' . $button . '.spellCheckerMode = "' . $spellCheckerMode . '";';
                        $jsArray[] = 'RTEarea[editornumber].buttons.' . $button . '.enablePersonalDicts = ' . ($enablePersonalDicts ? 'true' : 'false') . ';';
-                       $jsArray[] = 'RTEarea[editornumber].buttons.' . $button . '.path = "' . ($this->isFrontend() || $this->isFrontendEditActive() ? ($GLOBALS['TSFE']->absRefPrefix ? $GLOBALS['TSFE']->absRefPrefix : '') . 'index.php?eID=rtehtmlarea_spellchecker' : BackendUtility::getAjaxUrl('rtehtmlarea::spellchecker')) . '";';
+                       $jsArray[] = 'RTEarea[editornumber].buttons.' . $button . '.path = "' . ($this->isFrontend() || $this->isFrontendEditActive() ? ($GLOBALS['TSFE']->absRefPrefix ? $GLOBALS['TSFE']->absRefPrefix : '') . 'index.php?eID=rtehtmlarea_spellchecker' : BackendUtility::getAjaxUrl('rtehtmlarea_spellchecker')) . '";';
                }
                return implode(LF, $jsArray);
        }
index d5c594b..140b250 100644 (file)
@@ -177,7 +177,7 @@ class SelectImage extends ElementBrowser {
                $pageRenderer = $this->getPageRenderer();
                $pageRenderer->addCssFile(ExtensionManagementUtility::extRelPath('t3skin') . 'rtehtmlarea/htmlarea.css');
                $pageRenderer->loadRequireJsModule('TYPO3/CMS/Backend/LegacyTree', 'function(Tree) {
-                       Tree.ajaxID = "SC_alt_file_navframe::expandCollapse";
+                       Tree.ajaxID = "sc_alt_file_navframe_expandtoggle";
                }');
                $pageRenderer->loadRequireJsModule('TYPO3/CMS/Rtehtmlarea/Modules/SelectImage', 'function(SelectImage) {
                        SelectImage.editorNo = ' . GeneralUtility::quoteJSvalue($this->editorNo) . ';
index 86c631f..aa83724 100644 (file)
@@ -24,4 +24,9 @@ return [
                'path' => '/rte/wizard/parsehtml',
                'target' => \TYPO3\CMS\Rtehtmlarea\Controller\ParseHtmlController::class . '::mainAction'
        ],
+       // Spellchecker
+       'rtehtmlarea_spellchecker' => [
+               'path' => '/rte/spellchecker',
+               'target' => \TYPO3\CMS\Rtehtmlarea\Controller\SpellCheckingController::class . '::main'
+       ],
 ];
index 0c15287..384a93b 100644 (file)
@@ -158,7 +158,6 @@ $GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['rtehtmlarea']['plugins']['Language']['di
 
 // Spell checking configuration
 $GLOBALS['TYPO3_CONF_VARS']['FE']['eID_include']['rtehtmlarea_spellchecker'] = \TYPO3\CMS\Rtehtmlarea\Controller\SpellCheckingController::class . '::processRequest';
-\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::registerAjaxHandler('rtehtmlarea::spellchecker', \TYPO3\CMS\Rtehtmlarea\Controller\SpellCheckingController::class . '->main');
 
 $GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['rtehtmlarea']['plugins']['SpellChecker'] = array();
 $GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['rtehtmlarea']['plugins']['SpellChecker']['objectReference'] = \TYPO3\CMS\Rtehtmlarea\Extension\Spellchecker::class;
index 895aca3..b994d65 100644 (file)
@@ -13,6 +13,9 @@ namespace TYPO3\CMS\T3editor;
  *
  * The TYPO3 project - inspiring people to share!
  */
+use Psr\Http\Message\ResponseInterface;
+use Psr\Http\Message\ServerRequestInterface;
+use TYPO3\CMS\Core\Utility\GeneralUtility;
 
 /**
  * Code completion for t3editor
@@ -35,20 +38,13 @@ class CodeCompletion {
         * General processor for AJAX requests.
         * (called by typo3/ajax.php)
         *
-        * @param array $params Additional parameters (not used here)
-        * @param \TYPO3\CMS\Core\Http\AjaxRequestHandler &$ajaxObj The AjaxRequestHandler object of this request
-        * @return void
+        * @param ServerRequestInterface $request
+        * @param ResponseInterface $response
+        * @return ResponseInterface
         */
-       public function processAjaxRequest($params, \TYPO3\CMS\Core\Http\AjaxRequestHandler &$ajaxObj) {
-               $this->ajaxObj = $ajaxObj;
-               $ajaxIdParts = explode('::', $ajaxObj->getAjaxID(), 2);
-               $ajaxMethod = $ajaxIdParts[1];
-               $response = array();
-               // Process the AJAX requests:
-               if ($ajaxMethod == 'loadTemplates') {
-                       $ajaxObj->setContent($this->loadTemplates((int)\TYPO3\CMS\Core\Utility\GeneralUtility::_GP('pageId')));
-                       $ajaxObj->setContentFormat('jsonbody');
-               }
+       public function processAjaxRequest(ServerRequestInterface $request, ResponseInterface $response) {
+               $pageId = (int)(isset($request->getParsedBody()['pageId']) ? $request->getParsedBody()['pageId'] : $request->getQueryParams()['pageId']);
+               return $this->loadTemplates($pageId);
        }
 
        /**
@@ -56,23 +52,26 @@ class CodeCompletion {
         * cleans parts that are not required for the t3editor codecompletion.
         *
         * @param int $pageId ID of the page
-        * @param int $templateId Currently unused (default: 0)
-        * @return array Cleaned array of TypoScript information
+        * @return ResponseInterface
         */
-       protected function loadTemplates($pageId, $templateId = 0) {
-               $templates = array();
+       protected function loadTemplates($pageId) {
+               /** @var \TYPO3\CMS\Core\Http\Response $response */
+               $response = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Http\Response::class);
+
                // Check whether access is granted (only admin have access to sys_template records):
                if ($GLOBALS['BE_USER']->isAdmin()) {
                        // Check whether there is a pageId given:
                        if ($pageId) {
-                               $templates = $this->getMergedTemplates($pageId);
+                               $response->getBody()->write(json_encode($this->getMergedTemplates($pageId)));
                        } else {
-                               $this->ajaxObj->setError($GLOBALS['LANG']->getLL('pageIDInteger'));
+                               $response->getBody()->write($GLOBALS['LANG']->getLL('pageIDInteger'));
+                               $response = $response->withStatus(500);
                        }
                } else {
-                       $this->ajaxObj->setError($GLOBALS['LANG']->getLL('noPermission'));
+                       $response->getBody()->write($GLOBALS['LANG']->getLL('noPermission'));
+                       $response = $response->withStatus(500);
                }
-               return $templates;
+               return $response;
        }
 
        /**
index 6391757..91244d8 100644 (file)
@@ -103,10 +103,11 @@ class FileEditHook {
         */
        public function save($parameters, $pObj) {
                $savingsuccess = FALSE;
-               if ($parameters['type'] == $this->ajaxSaveType) {
+               if ($parameters['type'] === $this->ajaxSaveType) {
+                       /** @var \TYPO3\CMS\Backend\Controller\File\FileController $tceFile */
                        $tceFile = GeneralUtility::makeInstance(\TYPO3\CMS\Backend\Controller\File\FileController::class);
-                       $tceFile->processAjaxRequest(array(), $parameters['ajaxObj']);
-                       $result = $parameters['ajaxObj']->getContent('result');
+                       $response = $tceFile->processAjaxRequest($parameters['request'], $parameters['response']);
+                       $result = json_decode((string)$response->getBody(), TRUE)['result'];
                        $savingsuccess = is_array($result) && $result['editfile'][0];
                }
                return $savingsuccess;
index 2eb85ea..441701a 100644 (file)
@@ -14,6 +14,8 @@ namespace TYPO3\CMS\T3editor;
  * The TYPO3 project - inspiring people to share!
  */
 
+use Psr\Http\Message\ResponseInterface;
+use Psr\Http\Message\ServerRequestInterface;
 use TYPO3\CMS\Core\Page\PageRenderer;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
 
@@ -342,50 +344,59 @@ class T3editor implements \TYPO3\CMS\Core\SingletonInterface {
        /**
         * Save the content from t3editor retrieved via Ajax
         *
-        * @param array $params Parameters (not used yet)
-        * @param \TYPO3\CMS\Core\Http\AjaxRequestHandler $ajaxObj AjaxRequestHandler to handle response
+        * @param ServerRequestInterface $request
+        * @param ResponseInterface $response
+        * @return ResponseInterface
         */
-       public function ajaxSaveCode($params, $ajaxObj) {
+       public function ajaxSaveCode(ServerRequestInterface $request, ResponseInterface $response) {
                // cancel if its not an Ajax request
                if (TYPO3_REQUESTTYPE & TYPO3_REQUESTTYPE_AJAX) {
-                       $ajaxObj->setContentFormat('json');
-                       $codeType = GeneralUtility::_GP('t3editor_savetype');
+                       $codeType = isset($request->getParsedBody()['t3editor_savetype']) ? $request->getParsedBody()['t3editor_savetype'] : $request->getQueryParams()['t3editor_savetype'];
                        $savingsuccess = FALSE;
                        try {
                                if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/t3editor/classes/class.tx_t3editor.php']['ajaxSaveCode'])) {
                                        $_params = array(
                                                'pObj' => &$this,
                                                'type' => $codeType,
-                                               'ajaxObj' => &$ajaxObj
+                                               'request' => $request,
+                                               'response' => $response
                                        );
                                        foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/t3editor/classes/class.tx_t3editor.php']['ajaxSaveCode'] as $key => $_funcRef) {
                                                $savingsuccess = GeneralUtility::callUserFunction($_funcRef, $_params, $this) || $savingsuccess;
                                        }
                                }
+                               $responseContent = array('result' => $savingsuccess);
                        } catch (\Exception $e) {
-                               $ajaxObj->setContent(array('result' => FALSE, 'exceptionMessage' => htmlspecialchars($e->getMessage()), 'exceptionCode' => $e->getCode()));
-                               return;
+                               $responseContent = array(
+                                       'result' => FALSE,
+                                       'exceptionMessage' => htmlspecialchars($e->getMessage()),
+                                       'exceptionCode' => $e->getCode()
+                               );
                        }
-                       $ajaxObj->setContent(array('result' => $savingsuccess));
+                       /** @var \TYPO3\CMS\Core\Http\Response $response */
+                       $response = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Http\Response::class);
+                       $response->getBody()->write(json_encode($responseContent));
                }
+
+               return $response;
        }
 
        /**
         * Gets plugins that are defined at $TYPO3_CONF_VARS['EXTCONF']['t3editor']['plugins']
         * (called by typo3/ajax.php)
         *
-        * @param array $params additional parameters (not used here)
-        * @param \TYPO3\CMS\Core\Http\AjaxRequestHandler &$ajaxObj The AjaxRequestHandler object of this request
-        * @return void
+        * @param ServerRequestInterface $request
+        * @param ResponseInterface $response
+        * @return ResponseInterface
         */
-       public function getPlugins($params, \TYPO3\CMS\Core\Http\AjaxRequestHandler &$ajaxObj) {
+       public function getPlugins(ServerRequestInterface $request, ResponseInterface $response) {
                $result = array();
                $plugins = &$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['t3editor']['plugins'];
                if (is_array($plugins)) {
                        $result = array_values($plugins);
                }
-               $ajaxObj->setContent($result);
-               $ajaxObj->setContentFormat('jsonbody');
+               $request->getBody()->write(json_encode($result));
+               return $request;
        }
 
        /**
index 69bf9d1..244c052 100644 (file)
@@ -13,6 +13,9 @@ namespace TYPO3\CMS\T3editor;
  *
  * The TYPO3 project - inspiring people to share!
  */
+use Psr\Http\Message\ResponseInterface;
+use Psr\Http\Message\ServerRequestInterface;
+use TYPO3\CMS\Core\Utility\GeneralUtility;
 
 /**
  * Loads TSref information from a XML file an responds to an AJAX call.
@@ -25,11 +28,6 @@ class TypoScriptReferenceLoader {
        protected $xmlDoc;
 
        /**
-        * @var \TYPO3\CMS\Core\Http\AjaxRequestHandler
-        */
-       protected $ajaxObj;
-
-       /**
         * Default constructor
         */
        public function __construct() {
@@ -40,25 +38,31 @@ class TypoScriptReferenceLoader {
         * General processor for AJAX requests.
         * (called by typo3/ajax.php)
         *
-        * @param array $params Additional parameters (not used here)
-        * @param \TYPO3\CMS\Core\Http\AjaxRequestHandler &$ajaxObj The AjaxRequestHandler object of this request
-        * @return void
+        * @param ServerRequestInterface $request
+        * @param ResponseInterface $response
+        * @return ResponseInterface
         */
-       public function processAjaxRequest($params, \TYPO3\CMS\Core\Http\AjaxRequestHandler &$ajaxObj) {
-               $this->ajaxObj = $ajaxObj;
+       public function processAjaxRequest(ServerRequestInterface $request, ResponseInterface $response) {
+               $parsedBody = $request->getParsedBody();
+               $queryParams = $request->getQueryParams();
+
                // Load the TSref XML information:
                $this->loadFile(\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::extPath('t3editor') . 'Resources/Private/tsref.xml');
-               $ajaxIdParts = explode('::', $ajaxObj->getAjaxID(), 2);
-               $ajaxMethod = $ajaxIdParts[1];
-               $response = array();
-               // Process the AJAX requests:
-               if ($ajaxMethod == 'getTypes') {
-                       $ajaxObj->setContent($this->getTypes());
-                       $ajaxObj->setContentFormat('jsonbody');
-               } elseif ($ajaxMethod == 'getDescription') {
-                       $ajaxObj->addContent('', $this->getDescription(\TYPO3\CMS\Core\Utility\GeneralUtility::_GP('typeId'), \TYPO3\CMS\Core\Utility\GeneralUtility::_GP('parameterName')));
-                       $ajaxObj->setContentFormat('plain');
+               $fetch = isset($parsedBody['fetch']) ? $parsedBody['fetch'] : $queryParams['fetch'];
+               $content = null;
+
+               switch ($fetch) {
+                       case 'types':
+                               $response->getBody()->write(json_encode($this->getTypes()));
+                               break;
+                       case 'description':
+                               $typeId = isset($parsedBody['typeId']) ? $parsedBody['typeId'] : $queryParams['typeId'];
+                               $parameterName = isset($parsedBody['parameterName']) ? $parsedBody['parameterName'] : $queryParams['parameterName'];
+                               $response = $this->getDescription($typeId, $parameterName);
+                               $response->withHeader('Content-Type', 'text/html; charset=utf8');
+                               break;
                }
+               return $response;
        }
 
        /**
@@ -111,12 +115,15 @@ class TypoScriptReferenceLoader {
         *
         * @param string $typeId
         * @param string $parameterName
-        * @return string
+        * @return ResponseInterface
         */
        protected function getDescription($typeId, $parameterName = '') {
+               /** @var \TYPO3\CMS\Core\Http\Response $response */
+               $response = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Http\Response::class);
                if (!$typeId) {
-                       $this->ajaxObj->setError($GLOBALS['LANG']->getLL('typeIDMissing'));
-                       return '';
+                       $response->getBody()->write($GLOBALS['LANG']->getLL('typeIDMissing'));
+                       $response = $response->withStatus(500);
+                       return $response;
                }
                // getElementById does only work with schema
                $type = $this->getType($typeId);
@@ -131,12 +138,13 @@ class TypoScriptReferenceLoader {
                                                $description = $descriptions->item(0)->textContent;
                                                $description = htmlspecialchars($description);
                                                $description = nl2br($description);
-                                               return $description;
+                                               $response->getBody()->write($description);
+                                               break;
                                        }
                                }
                        }
                }
-               return '';
+               return $response;
        }
 
        /**
diff --git a/typo3/sysext/t3editor/Configuration/Backend/AjaxRoutes.php b/typo3/sysext/t3editor/Configuration/Backend/AjaxRoutes.php
new file mode 100644 (file)
index 0000000..b01d176
--- /dev/null
@@ -0,0 +1,30 @@
+<?php
+
+/**
+ * Definitions for routes provided by EXT:t3editor
+ */
+return [
+       // Save the TypoScript
+       't3editor_save' => [
+               'path' => '/t3editor/save',
+               'target' => \TYPO3\CMS\T3editor\T3editor::class . '::ajaxSaveCode'
+       ],
+
+       // Get plugins
+       't3editor_get_plugins' => [
+               'path' => '/t3editor/get-plugins',
+               'target' => \TYPO3\CMS\T3editor\T3editor::class . '::getPlugins'
+       ],
+
+       // Get TSRef
+       't3editor_tsref' => [
+               'path' => '/t3editor/tsref',
+               'target' => \TYPO3\CMS\T3editor\TypoScriptReferenceLoader::class . '::processAjaxRequest'
+       ],
+
+       // Load code completion templates
+       't3editor_codecompletion_loadtemplates' => [
+               'path' => '/t3editor/codecompletion/load-templates',
+               'target' => \TYPO3\CMS\T3editor\CodeCompletion::class . '::processAjaxRequest'
+       ]
+];
\ No newline at end of file
index d663cdc..25820a0 100644 (file)
@@ -61,7 +61,7 @@ define('TYPO3/CMS/T3editor/Plugins/CodeCompletion/TsCodeCompletion', [
                        return;
                }
                $.ajax({
-                       url: TYPO3.settings.ajaxUrls['CodeCompletion::loadTemplates'],
+                       url: TYPO3.settings.ajaxUrls['t3editor_codecompletion_loadtemplates'],
                        data: {
                                pageId: id
                        },
@@ -77,7 +77,7 @@ define('TYPO3/CMS/T3editor/Plugins/CodeCompletion/TsCodeCompletion', [
         */
        TsCodeCompletion.loadPluginArray = function() {
                $.ajax({
-                       url: TYPO3.settings.ajaxUrls['T3Editor::getPlugins'],
+                       url: TYPO3.settings.ajaxUrls['t3editor_get_plugins'],
                        success: function(response) {
                                TsCodeCompletion.plugins = $.merge(TsCodeCompletion.plugins, response);
                                // register an internal plugin
index e61e02f..5b7514e 100644 (file)
@@ -51,8 +51,9 @@ define('TYPO3/CMS/T3editor/Plugins/CodeCompletion/TsRef', ['jquery'], function (
                this.getDescription = function(callBack) {
                        var urlParameters = '&typeId=' + this.parentType + '&parameterName=' + this.name;
                        $.ajax({
-                               url: TYPO3.settings.ajaxUrls['T3Editor_TSrefLoader::getDescription'],
+                               url: TYPO3.settings.ajaxUrls['t3editor_tsref'],
                                data: {
+                                       fetch: 'description',
                                        typeId: this.parentType,
                                        parameterName: this.name
                                },
@@ -68,7 +69,10 @@ define('TYPO3/CMS/T3editor/Plugins/CodeCompletion/TsRef', ['jquery'], function (
         */
        TsRef.loadTsrefAsync = function() {
                $.ajax({
-                       url: TYPO3.settings.ajaxUrls['T3Editor_TSrefLoader::getTypes'],
+                       url: TYPO3.settings.ajaxUrls['t3editor_tsref'],
+                       data: {
+                               fetch: 'types'
+                       },
                        success: function(response) {
                                TsRef.doc = response;
                                TsRef.buildTree();
index 7e1a5ba..f18b645 100644 (file)
@@ -91,16 +91,17 @@ define('TYPO3/CMS/T3editor/T3editor', ['jquery'], function ($) {
                                }, data.parameters);
 
                                $.ajax({
-                                       url: TYPO3.settings.ajaxUrls['T3Editor::saveCode'],
+                                       url: TYPO3.settings.ajaxUrls['t3editor_save'],
                                        data: params,
                                        method: 'POST',
                                        beforeSend: function() {
                                                codemirror.options.originalTextarea.parent().find('.t3e_modalOverlay').fadeIn();
                                        },
                                        complete: function(jqXHR) {
-                                               var wasSuccessful = jqXHR.status === 200 && jqXHR.responseJSON.result === true;
+                                               var response = JSON.parse(jqXHR.responseText);
+                                               var wasSuccessful = jqXHR.status === 200 && response.result === true;
                                                codemirror.options.originalTextarea.parent().find('.t3e_modalOverlay').fadeOut();
-                                               T3editor.saveFunctionComplete(codemirror, wasSuccessful, jqXHR.responseJSON);
+                                               T3editor.saveFunctionComplete(codemirror, wasSuccessful, response);
                                        }
                                });
                        });
diff --git a/typo3/sysext/t3editor/ext_tables.php b/typo3/sysext/t3editor/ext_tables.php
deleted file mode 100644 (file)
index abe38ba..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-<?php
-defined('TYPO3_MODE') or die();
-
-if (TYPO3_MODE === 'BE') {
-       // Register AJAX handlers:
-       \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::registerAjaxHandler('T3Editor::saveCode', \TYPO3\CMS\T3editor\T3editor::class . '->ajaxSaveCode');
-       \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::registerAjaxHandler('T3Editor::getPlugins', \TYPO3\CMS\T3editor\T3editor::class . '->getPlugins');
-       \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::registerAjaxHandler('T3Editor_TSrefLoader::getTypes', \TYPO3\CMS\T3editor\TypoScriptReferenceLoader::class . '->processAjaxRequest');
-       \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::registerAjaxHandler('T3Editor_TSrefLoader::getDescription', \TYPO3\CMS\T3editor\TypoScriptReferenceLoader::class . '->processAjaxRequest');
-       \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::registerAjaxHandler('CodeCompletion::loadTemplates', \TYPO3\CMS\T3editor\CodeCompletion::class . '->processAjaxRequest');
-}
index bf7a769..70cfcab 100644 (file)
@@ -14,8 +14,8 @@ namespace TYPO3\CMS\Taskcenter;
  * The TYPO3 project - inspiring people to share!
  */
 
-use TYPO3\CMS\Core\Http\AjaxRequestHandler;
-use TYPO3\CMS\Core\Utility\GeneralUtility;
+use Psr\Http\Message\ResponseInterface;
+use Psr\Http\Message\ServerRequestInterface;
 
 /**
  * Status of tasks
@@ -25,33 +25,41 @@ class TaskStatus {
        /**
         * Saves the section toggle state of tasks in the backend user's uc
         *
-        * @param array $params Array of parameters from the AJAX interface, currently unused
-        * @param AjaxRequestHandler $ajaxObj Object of type AjaxRequestHandler
-        * @return void
+        * @param ServerRequestInterface $request
+        * @param ResponseInterface $response
+        * @return ResponseInterface
         */
-       public function saveCollapseState(array $params, AjaxRequestHandler $ajaxObj) {
+       public function saveCollapseState(ServerRequestInterface $request, ResponseInterface $response) {
                // Remove 'el_' in the beginning which is needed for the saveSortingState()
-               $item = substr(htmlspecialchars(GeneralUtility::_POST('item')), 3);
-               $state = (bool)GeneralUtility::_POST('state');
+               $item = isset($request->getParsedBody()['item']) ? $request->getParsedBody()['item'] : $request->getQueryParams()['item'];
+               $item = substr(htmlspecialchars($item), 3);
+               $state = (bool)(isset($request->getParsedBody()['state']) ? $request->getParsedBody()['state'] : $request->getQueryParams()['state']);
+
                $this->getBackendUserAuthentication()->uc['taskcenter']['states'][$item] = $state;
                $this->getBackendUserAuthentication()->writeUC();
+
+               return $response;
        }
 
        /**
         * Saves the sorting order of tasks in the backend user's uc
         *
-        * @param array $params Array of parameters from the AJAX interface, currently unused
-        * @param AjaxRequestHandler $ajaxObj Object of type AjaxRequestHandler
-        * @return void
+        * @param ServerRequestInterface $request
+        * @param ResponseInterface $response
+        * @return ResponseInterface
         */
-       public function saveSortingState(array $params, AjaxRequestHandler $ajaxObj) {
+       public function saveSortingState(ServerRequestInterface $request, ResponseInterface $response) {
                $sort = array();
-               $items = explode('&', GeneralUtility::_POST('data'));
+               $data = isset($request->getParsedBody()['data']) ? $request->getParsedBody()['data'] : $request->getQueryParams()['data'];
+
+               $items = explode('&', $data);
                foreach ($items as $item) {
                        $sort[] = substr($item, 12);
                }
                $this->getBackendUserAuthentication()->uc['taskcenter']['sorting'] = serialize($sort);
                $this->getBackendUserAuthentication()->writeUC();
+
+               return $response;
        }
 
        /**
diff --git a/typo3/sysext/taskcenter/Configuration/Backend/AjaxRoutes.php b/typo3/sysext/taskcenter/Configuration/Backend/AjaxRoutes.php
new file mode 100644 (file)
index 0000000..d2b1bd6
--- /dev/null
@@ -0,0 +1,18 @@
+<?php
+
+/**
+ * Definitions for routes provided by EXT:taskcenter
+ */
+return [
+       // Collapse
+       'taskcenter_collapse' => [
+               'path' => '/taskcenter/collapse',
+               'target' => \TYPO3\CMS\Taskcenter\TaskStatus::class . '::saveCollapseState'
+       ],
+
+       // Sort
+       'taskcenter_sort' => [
+               'path' => '/taskcenter/sort',
+               'target' => \TYPO3\CMS\Taskcenter\TaskStatus::class . '::saveSortingState'
+       ]
+];
\ No newline at end of file
index a49b212..aeb54c8 100644 (file)
@@ -45,7 +45,7 @@ define('TYPO3/CMS/Taskcenter/Taskcenter', ['jquery', 'jquery-ui/sortable'], func
                }
 
                $.ajax({
-                       url: TYPO3.settings.ajaxUrls['Taskcenter::saveCollapseState'],
+                       url: TYPO3.settings.ajaxUrls['taskcenter_collapse'],
                        type: 'post',
                        cache: false,
                        data: {
@@ -59,7 +59,7 @@ define('TYPO3/CMS/Taskcenter/Taskcenter', ['jquery', 'jquery-ui/sortable'], func
                $('#task-list').sortable({
                        update: function(event, ui) {
                                $.ajax({
-                                       url: TYPO3.settings.ajaxUrls['Taskcenter::saveSortingState'],
+                                       url: TYPO3.settings.ajaxUrls['taskcenter_sort'],
                                        type: 'post',
                                        cache: false,
                                        data: {
index 2448748..2e9da61 100644 (file)
@@ -19,14 +19,4 @@ if (TYPO3_MODE === 'BE') {
                        ),
                )
        );
-
-       \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::registerAjaxHandler(
-               'Taskcenter::saveCollapseState',
-               \TYPO3\CMS\Taskcenter\TaskStatus::class . '->saveCollapseState'
-       );
-
-       \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::registerAjaxHandler(
-               'Taskcenter::saveSortingState',
-               \TYPO3\CMS\Taskcenter\TaskStatus::class . '->saveSortingState'
-       );
 }
index 9583b12..5cd397e 100644 (file)
@@ -14,6 +14,8 @@ namespace TYPO3\CMS\Workspaces\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\Backend\Utility\BackendUtility;
 
@@ -26,13 +28,15 @@ class AjaxController {
         * Sets the TYPO3 Backend context to a certain workspace,
         * called by the Backend toolbar menu
         *
-        * @param array $parameters
-        * @param \TYPO3\CMS\Core\Http\AjaxRequestHandler $ajaxRequestHandler
-        * @return void
+        * @param ServerRequestInterface $request
+        * @param ResponseInterface $response
+        * @return ResponseInterface
         */
-       public function setWorkspace($parameters, \TYPO3\CMS\Core\Http\AjaxRequestHandler $ajaxRequestHandler) {
-               $workspaceId = (int)GeneralUtility::_GP('workspaceId');
-               $pageId = (int)GeneralUtility::_GP('pageId');
+       public function switchWorkspaceAction(ServerRequestInterface $request, ResponseInterface $response) {
+               $parsedBody = $request->getParsedBody();
+               $queryParams = $request->getQueryParams();
+               $workspaceId = (int)(isset($parsedBody['workspaceId']) ? $parsedBody['workspaceId'] : $queryParams['workspaceId']);
+               $pageId = (int)(isset($parsedBody['pageId']) ? $parsedBody['pageId'] : $queryParams['pageId']);
                $finalPageUid = 0;
                $originalPageId = $pageId;
 
@@ -55,13 +59,13 @@ class AjaxController {
                        $finalPageUid = (int)$page['uid'];
                }
 
-               $response = array(
+               $ajaxResponse = array(
                        'title'       => \TYPO3\CMS\Workspaces\Service\WorkspaceService::getWorkspaceTitle($workspaceId),
                        'workspaceId' => $workspaceId,
                        'pageId'      => ($finalPageUid && $originalPageId == $finalPageUid) ? NULL : $finalPageUid
                );
-               $ajaxRequestHandler->setContent($response);
-               $ajaxRequestHandler->setContentFormat('json');
+               $response->getBody()->write(json_encode($ajaxResponse));
+               return $response;
        }
 
        /**
diff --git a/typo3/sysext/workspaces/Configuration/Backend/AjaxRoutes.php b/typo3/sysext/workspaces/Configuration/Backend/AjaxRoutes.php
new file mode 100644 (file)
index 0000000..4e75211
--- /dev/null
@@ -0,0 +1,12 @@
+<?php
+
+/**
+ * Definitions for routes provided by EXT:workspaces
+ */
+return [
+       // Set the workspace
+       'workspace_switch' => [
+               'path' => '/workspace/switch',
+               'target' => \TYPO3\CMS\Workspaces\Controller\AjaxController::class . '::switchWorkspaceAction'
+       ]
+];
index 84cfbc0..542c7bf 100644 (file)
@@ -53,7 +53,7 @@ define('TYPO3/CMS/Workspaces/Toolbar/WorkspacesMenu', ['jquery'], function($) {
         */
        WorkspacesMenu.switchWorkspace = function(workspaceId) {
                $.ajax({
-                       url: TYPO3.settings.ajaxUrls['Workspaces::setWorkspace'],
+                       url: TYPO3.settings.ajaxUrls['workspace_switch'],
                        type: 'post',
                        data: {
                                workspaceId: workspaceId,
index af1dfda..dcae414 100644 (file)
@@ -41,8 +41,6 @@ if (TYPO3_MODE === 'BE' && !(TYPO3_REQUESTTYPE & TYPO3_REQUESTTYPE_INSTALL)) {
                'web_WorkspacesWorkspaces',
                'user,group'
        );
-
-       \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::registerAjaxHandler('Workspaces::setWorkspace', \TYPO3\CMS\Workspaces\Controller\AjaxController::class . '->setWorkspace');
 }
 
 // @todo move icons to Core sprite or keep them here and remove the todo note ;)