Commit 6a89d34e authored by Benni Mack's avatar Benni Mack Committed by Benni Mack
Browse files

[FEATURE] Allow PSR-7 dispatching for Backend modules

The newly introduced simple dispatcher handles the distribution
of all "traditional" modules through a new option called "routeTarget" when registering a module.

All existing core modules are moved to this new syntax, all left-over
index.php files within Modules/ directories are removed.

Resolves: #69918
Releases: master
Change-Id: I6200a5e0309454fd981c9f1fa277d948974a1fac
Reviewed-on: http://review.typo3.org/43375

Reviewed-by: Wouter Wolters's avatarWouter Wolters <typo3@wouterwolters.nl>
Tested-by: Wouter Wolters's avatarWouter Wolters <typo3@wouterwolters.nl>
Reviewed-by: Anja Leichsenring's avatarAnja Leichsenring <aleichsenring@ab-softlab.de>
Tested-by: Anja Leichsenring's avatarAnja Leichsenring <aleichsenring@ab-softlab.de>
Reviewed-by: Markus Klein's avatarMarkus Klein <markus.klein@typo3.org>
Reviewed-by: Benni Mack's avatarBenni Mack <benni@typo3.org>
Tested-by: Benni Mack's avatarBenni Mack <benni@typo3.org>
parent b979df40
......@@ -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;
......@@ -489,6 +491,23 @@ class PageLayoutController {
}
}
/**
* Injects the request object for the current request or subrequest
* As this controller goes only through the main() method, it is rather simple for now
*
* @param ServerRequestInterface $request the current request
* @param ResponseInterface $response
* @return ResponseInterface the response with the content
*/
public function mainAction(ServerRequestInterface $request, ResponseInterface $response) {
$GLOBALS['SOBE'] = $this;
$this->init();
$this->clearCache();
$this->main();
$response->getBody()->write($this->content);
return $response;
}
/**
* Main function.
* Creates some general objects and calls other functions for the main rendering of module content.
......@@ -1053,8 +1072,10 @@ class PageLayoutController {
* Print accumulated content of module
*
* @return void
* @deprecated since TYPO3 CMS 7, will be removed in TYPO3 CMS 8
*/
public function printContent() {
GeneralUtility::logDeprecatedFunction();
echo $this->content;
}
......
......@@ -20,6 +20,7 @@ use TYPO3\CMS\Core\Core\Bootstrap;
use TYPO3\CMS\Core\FormProtection\BackendFormProtection;
use TYPO3\CMS\Core\FormProtection\FormProtectionFactory;
use TYPO3\CMS\Core\Exception;
use TYPO3\CMS\Core\Http\Dispatcher;
use TYPO3\CMS\Core\Http\RequestHandlerInterface;
use TYPO3\CMS\Core\Http\Response;
use TYPO3\CMS\Core\Utility\GeneralUtility;
......@@ -177,21 +178,31 @@ class BackendModuleRequestHandler implements RequestHandlerInterface {
}
}
$configuration = array(
'extensionName' => $moduleConfiguration['extensionName'],
'pluginName' => $moduleName
);
if (isset($moduleConfiguration['vendorName'])) {
$configuration['vendorName'] = $moduleConfiguration['vendorName'];
}
// Run Extbase
$bootstrap = GeneralUtility::makeInstance(\TYPO3\CMS\Extbase\Core\Bootstrap::class);
$content = $bootstrap->run('', $configuration);
/** @var Response $response */
$response = GeneralUtility::makeInstance(Response::class);
$response->getBody()->write($content);
// Use Core Dispatching
if (isset($moduleConfiguration['routeTarget'])) {
$dispatcher = GeneralUtility::makeInstance(Dispatcher::class);
$this->request = $this->request->withAttribute('target', $moduleConfiguration['routeTarget']);
$response = $dispatcher->dispatch($this->request, $response);
} else {
// extbase module
$configuration = array(
'extensionName' => $moduleConfiguration['extensionName'],
'pluginName' => $moduleName
);
if (isset($moduleConfiguration['vendorName'])) {
$configuration['vendorName'] = $moduleConfiguration['vendorName'];
}
// Run Extbase
$bootstrap = GeneralUtility::makeInstance(\TYPO3\CMS\Extbase\Core\Bootstrap::class);
$content = $bootstrap->run('', $configuration);
$response->getBody()->write($content);
}
return $response;
}
......
......@@ -306,10 +306,11 @@ class ModuleLoader {
}
// Default script setup
if ($setupInformation['configuration']['script'] === '_DISPATCH') {
if ($setupInformation['configuration']['script'] === '_DISPATCH' || isset($setupInformation['configuration']['routeTarget'])) {
if ($setupInformation['configuration']['extbase']) {
$finalModuleConfiguration['script'] = BackendUtility::getModuleUrl('Tx_' . $name);
} else {
// just go through BackendModuleRequestHandler where the routeTarget is resolved
$finalModuleConfiguration['script'] = BackendUtility::getModuleUrl($name);
}
} elseif ($setupInformation['configuration']['script'] && file_exists($setupInformation['path'] . '/' . $setupInformation['configuration']['script'])) {
......
<?php
/*
* 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!
*/
$GLOBALS['SOBE'] = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(\TYPO3\CMS\Backend\Controller\PageLayoutController::class);
$GLOBALS['SOBE']->init();
$GLOBALS['SOBE']->clearCache();
$GLOBALS['SOBE']->main();
$GLOBALS['SOBE']->printContent();
......@@ -7,9 +7,9 @@ if (TYPO3_MODE === 'BE') {
'web',
'layout',
'top',
'EXT:backend/Modules/Layout/',
'',
array(
'script' => '_DISPATCH',
'routeTarget' => \TYPO3\CMS\Backend\Controller\PageLayoutController::class . '::mainAction',
'access' => 'user,group',
'name' => 'web_layout',
'labels' => array(
......
=================================================================
Feature: #69918 - Add PSR-7-based dispatching for Backend Modules
=================================================================
Description
===========
Built on the PSR-7 principle and the routing concepts, it is now possible to register backend modules which are dispatched to a callable string
instead of pointing to an index.php file in EXT:myextension/Modules/MyModule/index.php.
The method which is called, receives a PSR-compatible request and response object and must return a response object which is outputted to the
browser.
An example registration uses the option "routeTarget" to resolve the method to be called when rendering the module:
.. code-block:: typoscript
\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addModule(
'web',
'layout',
'top',
'',
array(
'routeTarget' => \TYPO3\CMS\Backend\Controller\PageLayoutController::class . '::mainAction',
'access' => 'user,group',
'name' => 'web_layout',
'labels' => array(
'tabs_images' => array(
'tab' => 'EXT:backend/Resources/Public/Icons/module-page.svg',
),
'll_ref' => 'LLL:EXT:backend/Resources/Private/Language/locallang_mod.xlf',
),
)
);
\ No newline at end of file
......@@ -14,7 +14,10 @@ namespace TYPO3\CMS\Dbal\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\Utility\GeneralUtility;
/**
* Script class; Backend module for DBAL extension
......@@ -72,9 +75,9 @@ class ModuleController extends \TYPO3\CMS\Backend\Module\BaseScriptClass {
$languageService = $this->getLanguageService();
$this->thisScript = BackendUtility::getModuleUrl($this->MCONF['name']);
// Clean up settings:
$this->MOD_SETTINGS = BackendUtility::getModuleData($this->MOD_MENU, \TYPO3\CMS\Core\Utility\GeneralUtility::_GP('SET'), $this->MCONF['name']);
$this->MOD_SETTINGS = BackendUtility::getModuleData($this->MOD_MENU, GeneralUtility::_GP('SET'), $this->MCONF['name']);
// Draw the header
$this->doc = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(\TYPO3\CMS\Backend\Template\DocumentTemplate::class);
$this->doc = GeneralUtility::makeInstance(\TYPO3\CMS\Backend\Template\DocumentTemplate::class);
$this->doc->form = '<form action="" method="post">';
// DBAL page title:
$this->content .= $this->doc->startPage($languageService->getLL('title'));
......@@ -104,19 +107,38 @@ class ModuleController extends \TYPO3\CMS\Backend\Module\BaseScriptClass {
* Prints out the module HTML
*
* @return string HTML output
* @deprecated since TYPO3 CMS 7, will be removed in TYPO3 CMS 8
*/
public function printContent() {
GeneralUtility::logDeprecatedFunction();
$this->content .= $this->doc->endPage();
echo $this->content;
}
/**
* Injects the request object for the current request or subrequest
* As this controller goes only through the main() method, it is rather simple for now
*
* @param ServerRequestInterface $request the current request
* @param ResponseInterface $response
* @return ResponseInterface the response with the content
*/
public function mainAction(ServerRequestInterface $request, ResponseInterface $response) {
$GLOBALS['SOBE'] = $this;
$this->init();
$this->main();
$response->getBody()->write($this->content);
return $response;
}
/**
* Displays a form to check DBAL SQL methods and parse raw SQL.
*
* @return string HTML output
*/
protected function printSqlCheck() {
$input = \TYPO3\CMS\Core\Utility\GeneralUtility::_GP('tx_dbal');
$input = GeneralUtility::_GP('tx_dbal');
$out = '
<form name="sql_check" action="' . $this->thisScript . '" method="post" enctype="multipart/form-data">
<script type="text/javascript">
......@@ -287,7 +309,7 @@ updateQryForm(\'' . $input['QUERY'] . '\');
*/
protected function printCachedInfo() {
// Get cmd:
if ((string)\TYPO3\CMS\Core\Utility\GeneralUtility::_GP('cmd') === 'clear') {
if ((string)GeneralUtility::_GP('cmd') === 'clear') {
$GLOBALS['TYPO3_DB']->clearCachedFieldInfo();
$GLOBALS['TYPO3_DB']->cacheFieldInfo();
}
......@@ -367,7 +389,7 @@ updateQryForm(\'' . $input['QUERY'] . '\');
// Disable debugging in any case...
$GLOBALS['TYPO3_DB']->debug = FALSE;
// Get cmd:
$cmd = (string)\TYPO3\CMS\Core\Utility\GeneralUtility::_GP('cmd');
$cmd = (string)GeneralUtility::_GP('cmd');
switch ($cmd) {
case 'flush':
$res = $GLOBALS['TYPO3_DB']->exec_TRUNCATEquery('tx_dbal_debuglog');
......@@ -489,7 +511,7 @@ updateQryForm(\'' . $input['QUERY'] . '\');
break;
default:
// Look for request to view specific script exec:
$specTime = \TYPO3\CMS\Core\Utility\GeneralUtility::_GP('specTime');
$specTime = GeneralUtility::_GP('specTime');
if ($specTime) {
$res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('exec_time,errorFlag,table_join,serdata,query', 'tx_dbal_debuglog', 'tstamp=' . (int)$specTime);
$tRows = array();
......@@ -545,7 +567,7 @@ updateQryForm(\'' . $input['QUERY'] . '\');
<a href="' . $this->thisScript . '">LOG</a> -
<a href="' . $this->thisScript . '&amp;cmd=where">WHERE</a> -
<a href="' . htmlspecialchars(\TYPO3\CMS\Core\Utility\GeneralUtility::linkThisScript()) . '" target="tx_debuglog">[New window]</a>
<a href="' . htmlspecialchars(GeneralUtility::linkThisScript()) . '" target="tx_debuglog">[New window]</a>
<hr />
';
return $menu . $outStr;
......
<?php
/*
* 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!
*/
/**
* Module 'DBAL Debug' for the 'dbal' extension.
*/
$GLOBALS['SOBE'] = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(\TYPO3\CMS\Dbal\Controller\ModuleController::class);
$GLOBALS['SOBE']->init();
$GLOBALS['SOBE']->main();
$GLOBALS['SOBE']->printContent();
......@@ -6,9 +6,9 @@ if (TYPO3_MODE === 'BE') {
'tools',
'txdbalM1',
'',
'EXT:dbal/Modules/Dbal/',
'',
array(
'script' => '_DISPATCH',
'routeTarget' => \TYPO3\CMS\Dbal\Controller\ModuleController::class . '::mainAction',
'access' => 'admin',
'name' => 'tools_txdbalM1',
'labels' => array(
......
......@@ -14,6 +14,8 @@ namespace TYPO3\CMS\Func\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\Imaging\Icon;
use TYPO3\CMS\Core\Imaging\IconFactory;
......@@ -64,6 +66,29 @@ class PageFunctionsController extends \TYPO3\CMS\Backend\Module\BaseScriptClass
);
}
/**
* Injects the request object for the current request or subrequest
* Then checks for module functions that have hooked in, and renders menu etc.
*
* @param ServerRequestInterface $request the current request
* @param ResponseInterface $response
* @return ResponseInterface the response with the content
*/
public function mainAction(ServerRequestInterface $request, ResponseInterface $response) {
$GLOBALS['SOBE'] = $this;
$this->init();
// Checking for first level external objects
$this->checkExtObj();
// Checking second level external objects
$this->checkSubExtObj();
$this->main();
$response->getBody()->write($this->content);
return $response;
}
/**
* Initialize module header etc and call extObjContent function
*
......@@ -132,8 +157,10 @@ class PageFunctionsController extends \TYPO3\CMS\Backend\Module\BaseScriptClass
* Print module content (from $this->content)
*
* @return void
* @deprecated since TYPO3 CMS 7, will be removed in TYPO3 CMS 8
*/
public function printContent() {
GeneralUtility::logDeprecatedFunction();
echo $this->content;
}
......
<?php
/*
* 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!
*/
/**
* Module: Advanced functions
* Advanced Functions related to pages
*/
/** @var $SOBE \TYPO3\CMS\Func\Controller\PageFunctionsController */
$GLOBALS['SOBE'] = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(\TYPO3\CMS\Func\Controller\PageFunctionsController::class);
$GLOBALS['SOBE']->init();
// Checking for first level external objects
$GLOBALS['SOBE']->checkExtObj();
// Checking second level external objects
$GLOBALS['SOBE']->checkSubExtObj();
$GLOBALS['SOBE']->main();
$GLOBALS['SOBE']->printContent();
......@@ -6,9 +6,9 @@ if (TYPO3_MODE === 'BE') {
'web',
'func',
'',
'EXT:func/Modules/AdvancedFunctions/',
'',
array(
'script' => '_DISPATCH',
'routeTarget' => \TYPO3\CMS\Func\Controller\PageFunctionsController::class . '::mainAction',
'access' => 'user,group',
'name' => 'web_func',
'labels' => array(
......
......@@ -14,6 +14,8 @@ namespace TYPO3\CMS\Info\Controller;
* The TYPO3 project - inspiring people to share!
*/
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use TYPO3\CMS\Backend\Module\BaseScriptClass;
use TYPO3\CMS\Backend\Utility\BackendUtility;
use TYPO3\CMS\Backend\Utility\IconUtility;
......@@ -131,12 +133,38 @@ class InfoModuleController extends BaseScriptClass {
* Print module content (from $this->content)
*
* @return void
* @deprecated since TYPO3 CMS 7, will be removed in TYPO3 CMS 8
*/
public function printContent() {
GeneralUtility::logDeprecatedFunction();
$this->content = $this->doc->insertStylesAndJS($this->content);
echo $this->content;
}
/**
* Injects the request object for the current request or subrequest
* Then checks for module functions that have hooked in, and renders menu etc.
*
* @param ServerRequestInterface $request the current request
* @param ResponseInterface $response
* @return ResponseInterface the response with the content
*/
public function mainAction(ServerRequestInterface $request, ResponseInterface $response) {
$GLOBALS['SOBE'] = $this;
$this->init();
// Checking for first level external objects
$this->checkExtObj();
// Checking second level external objects
$this->checkSubExtObj();
$this->main();
$this->content = $this->doc->insertStylesAndJS($this->content);
$response->getBody()->write($this->content);
return $response;
}
/**
* Create the panel of buttons for submitting the form or otherwise perform operations.
*
......
<?php
/*
* 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!
*/
/**
* Module: Web>Info
* Presents various page related information from extensions
*/
$GLOBALS['SOBE'] = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(\TYPO3\CMS\Info\Controller\InfoModuleController::class);
$GLOBALS['SOBE']->init();
// Checking for first level external objects
$GLOBALS['SOBE']->checkExtObj();
// Checking second level external objects
$GLOBALS['SOBE']->checkSubExtObj();
$GLOBALS['SOBE']->main();
$GLOBALS['SOBE']->printContent();
......@@ -6,9 +6,9 @@ if (TYPO3_MODE === 'BE') {
'web',
'info',
'',
'EXT:info/Modules/Info/',
'',
array(
'script' => '_DISPATCH',
'routeTarget' => \TYPO3\CMS\Info\Controller\InfoModuleController::class . '::mainAction',
'access' => 'user,group',
'name' => 'web_info',
'labels' => array(
......
......@@ -14,6 +14,8 @@ namespace TYPO3\CMS\Lowlevel\View;
* The TYPO3 project - inspiring people to share!
*/
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use TYPO3\CMS\Backend\Module\BaseScriptClass;
use TYPO3\CMS\Backend\Template\DocumentTemplate;
use TYPO3\CMS\Backend\Utility\BackendUtility;
......@@ -59,6 +61,7 @@ class ConfigurationView extends BaseScriptClass {
$this->doc->setModuleTemplate('EXT:lowlevel/Resources/Private/Templates/config.html');
$this->doc->form = '<form action="" method="post">';
$this->doc->addStyleSheet('module', 'sysext/lowlevel/Resources/Public/Css/styles.css');
$this->doc->bodyTagId = 'ext-lowlevel-Modules-Configuration-index-php';
}
/**
......@@ -203,12 +206,32 @@ class ConfigurationView extends BaseScriptClass {
$this->content = $this->doc->render('Configuration', $this->content);
}
/**
* Injects the request object for the current request or subrequest
* Simply calls main() and init() and outputs the content
*
* @param ServerRequestInterface $request the current request
* @param ResponseInterface $response
* @return ResponseInterface the response with the content
*/
public function mainAction(ServerRequestInterface $request, ResponseInterface $response) {
$GLOBALS['SOBE'] = $this;
$this->init();
$this->main();
$response->getBody()->write($this->content);
return $response;
}
/**
* Print output to browser
*
* @return void
* @deprecated since TYPO3 CMS 7, will be removed in TYPO3 CMS 8
*/
public function printContent() {
GeneralUtility::logDeprecatedFunction();
echo $this->content;
}
......
......@@ -14,6 +14,8 @@ namespace TYPO3\CMS\Lowlevel\View;
* The TYPO3 project - inspiring people to share!
*/
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use TYPO3\CMS\Backend\Module\BaseScriptClass;
use TYPO3\CMS\Backend\Template\DocumentTemplate;
use TYPO3\CMS\Backend\Utility\IconUtility;
......@@ -211,11 +213,30 @@ class DatabaseIntegrityView extends BaseScriptClass {
* Print content
*
* @return void
* @deprecated since TYPO3 CMS 7, will be removed in TYPO3 CMS 8
*/
public function printContent() {
GeneralUtility::logDeprecatedFunction();
echo $this->content;
}
/**
* Injects the request object for the current request or subrequest
* Simply calls main() and init() and outputs the content
*
* @param ServerRequestInterface $request the current request
* @param ResponseInterface $response
* @return ResponseInterface the response with the content
*/
public function mainAction(ServerRequestInterface $request, ResponseInterface $response) {
$GLOBALS['SOBE'] = $this;
$this->init();
$this->main();
$response->getBody()->write($this->content);
return $response;
}
/**
* Create the panel of buttons for submitting the form or otherwise perform operations.
*
......
<?php
/*
* 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.