[!!!][FEATURE] Introduce PSR-7-based Routing for Backend AJAX Requests
[Packages/TYPO3.CMS.git] / typo3 / sysext / core / Classes / ExtDirect / ExtDirectRouter.php
1 <?php
2 namespace TYPO3\CMS\Core\ExtDirect;
3
4 /*
5 * This file is part of the TYPO3 CMS project.
6 *
7 * It is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU General Public License, either version 2
9 * of the License, or any later version.
10 *
11 * For the full copyright and license information, please read the
12 * LICENSE.txt file that was distributed with this source code.
13 *
14 * The TYPO3 project - inspiring people to share!
15 */
16
17 use Psr\Http\Message\ResponseInterface;
18 use Psr\Http\Message\ServerRequestInterface;
19 use TYPO3\CMS\Core\Utility\GeneralUtility;
20
21 /**
22 * Ext Direct Router
23 */
24 class ExtDirectRouter {
25
26 /**
27 * Dispatches the incoming calls to methods about the ExtDirect API.
28 *
29 * @param ServerRequestInterface $request
30 * @param ResponseInterface $response
31 * @return ResponseInterface
32 */
33 public function routeAction(ServerRequestInterface $request, ResponseInterface $response) {
34 $GLOBALS['error'] = GeneralUtility::makeInstance(\TYPO3\CMS\Core\ExtDirect\ExtDirectDebug::class);
35 $isForm = FALSE;
36 $isUpload = FALSE;
37 $rawPostData = file_get_contents('php://input');
38 $postParameters = $request->getParsedBody();
39 $namespace = isset($request->getParsedBody()['namespace']) ? $request->getParsedBody()['namespace'] : $request->getQueryParams()['namespace'];
40 $extResponse = array();
41 $extRequest = NULL;
42 $isValidRequest = TRUE;
43 if (!empty($postParameters['extAction'])) {
44 $isForm = TRUE;
45 $isUpload = $postParameters['extUpload'] === 'true';
46 $extRequest = new \stdClass();
47 $extRequest->action = $postParameters['extAction'];
48 $extRequest->method = $postParameters['extMethod'];
49 $extRequest->tid = $postParameters['extTID'];
50 unset($_POST['securityToken']);
51 $extRequest->data = array($_POST + $_FILES);
52 $extRequest->data[] = $postParameters['securityToken'];
53 } elseif (!empty($rawPostData)) {
54 $extRequest = json_decode($rawPostData);
55 } else {
56 $extResponse[] = array(
57 'type' => 'exception',
58 'message' => 'Something went wrong with an ExtDirect call!',
59 'code' => 'router'
60 );
61 $isValidRequest = FALSE;
62 }
63 if (!is_array($extRequest)) {
64 $extRequest = array($extRequest);
65 }
66 if ($isValidRequest) {
67 $validToken = FALSE;
68 $firstCall = TRUE;
69 foreach ($extRequest as $index => $singleRequest) {
70 $extResponse[$index] = array(
71 'tid' => $singleRequest->tid,
72 'action' => $singleRequest->action,
73 'method' => $singleRequest->method
74 );
75 $token = array_pop($singleRequest->data);
76 if ($firstCall) {
77 $firstCall = FALSE;
78 $formprotection = \TYPO3\CMS\Core\FormProtection\FormProtectionFactory::get();
79 $validToken = $formprotection->validateToken($token, 'extDirect');
80 }
81 try {
82 if (!$validToken) {
83 throw new \TYPO3\CMS\Core\FormProtection\Exception('ExtDirect: Invalid Security Token!');
84 }
85 $extResponse[$index]['type'] = 'rpc';
86 $extResponse[$index]['result'] = $this->processRpc($singleRequest, $namespace);
87 $extResponse[$index]['debug'] = $GLOBALS['error']->toString();
88 } catch (\Exception $exception) {
89 $extResponse[$index]['type'] = 'exception';
90 $extResponse[$index]['message'] = $exception->getMessage();
91 $extResponse[$index]['code'] = 'router';
92 }
93 }
94 }
95 if ($isForm && $isUpload) {
96 $extResponse = json_encode($extResponse);
97 $extResponse = preg_replace('/&quot;/', '\\&quot;', $extResponse);
98 $extResponse = array(
99 '<html><body><textarea>' . $extResponse . '</textarea></body></html>'
100 );
101 } else {
102 $extResponse = json_encode($extResponse);
103 }
104 $response->getBody()->write($extResponse);
105 return $response;
106 }
107
108 /**
109 * Processes an incoming extDirect call by executing the defined method. The configuration
110 * array "$GLOBALS['TYPO3_CONF_VARS']['BE']['ExtDirect']" is taken to find the class/method
111 * information.
112 *
113 * @param \stdClass $singleRequest request object from extJS
114 * @param string $namespace namespace like TYPO3.Backend
115 * @return mixed return value of the called method
116 * @throws \UnexpectedValueException if the remote method couldn't be found
117 */
118 protected function processRpc($singleRequest, $namespace) {
119 $endpointName = $namespace . '.' . $singleRequest->action;
120 if (!isset($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ExtDirect'][$endpointName])) {
121 throw new \UnexpectedValueException('ExtDirect: Call to undefined endpoint: ' . $endpointName, 1294586450);
122 }
123 if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ExtDirect'][$endpointName])) {
124 if (!isset($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ExtDirect'][$endpointName]['callbackClass'])) {
125 throw new \UnexpectedValueException('ExtDirect: Call to undefined endpoint: ' . $endpointName, 1294586451);
126 }
127 $callbackClass = $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ExtDirect'][$endpointName]['callbackClass'];
128 $configuration = $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ExtDirect'][$endpointName];
129 if (!is_null($configuration['moduleName']) && !is_null($configuration['accessLevel'])) {
130 $GLOBALS['BE_USER']->modAccess(array(
131 'name' => $configuration['moduleName'],
132 'access' => $configuration['accessLevel']
133 ), TRUE);
134 }
135 }
136 $endpointObject = GeneralUtility::getUserObj($callbackClass);
137 return call_user_func_array(array($endpointObject, $singleRequest->method), is_array($singleRequest->data) ? $singleRequest->data : array());
138 }
139
140 }