[TASK] Migrate eID scripts to use PSR-7
[Packages/TYPO3.CMS.git] / typo3 / sysext / core / Classes / Http / AjaxRequestHandler.php
1 <?php
2 namespace TYPO3\CMS\Core\Http;
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\ArrayUtility;
20 use TYPO3\CMS\Core\Utility\GeneralUtility;
21
22 /**
23 * Class to hold all the information about an AJAX call and send
24 * the right headers for the request type
25 */
26 class AjaxRequestHandler {
27
28 /**
29 * @var string|NULL
30 */
31 protected $ajaxId = NULL;
32
33 /**
34 * @var string|NULL
35 */
36 protected $errorMessage = NULL;
37
38 /**
39 * @var bool
40 */
41 protected $isError = FALSE;
42
43 /**
44 * @var array
45 */
46 protected $content = array();
47
48 /**
49 * @var string
50 */
51 protected $contentFormat = 'plain';
52
53 /**
54 * @var string
55 */
56 protected $javascriptCallbackWrap = '
57 <script type="text/javascript">
58 /*<![CDATA[*/
59 response = |;
60 /*]]>*/
61 </script>
62 ';
63
64 /**
65 * Sets the ID for the AJAX call
66 *
67 * @param string $ajaxId The AJAX id
68 */
69 public function __construct($ajaxId) {
70 $this->ajaxId = $ajaxId;
71 }
72
73 /**
74 * Returns the ID for the AJAX call
75 *
76 * @return string The AJAX id
77 */
78 public function getAjaxID() {
79 return $this->ajaxId;
80 }
81
82 /**
83 * Overwrites the existing content with the data supplied
84 *
85 * @param array $content The new content
86 * @return mixed The old content as array; if the new content was not an array, FALSE is returned
87 */
88 public function setContent($content) {
89 $oldcontent = FALSE;
90 if (is_array($content)) {
91 $oldcontent = $this->content;
92 $this->content = $content;
93 }
94 return $oldcontent;
95 }
96
97 /**
98 * Adds new content
99 *
100 * @param string $key The new content key where the content should be added in the content array
101 * @param string $content The new content to add
102 * @return mixed The old content; if the old content didn't exist before, FALSE is returned
103 */
104 public function addContent($key, $content) {
105 $oldcontent = FALSE;
106 if (array_key_exists($key, $this->content)) {
107 $oldcontent = $this->content[$key];
108 }
109 if (!isset($content) || empty($content)) {
110 unset($this->content[$key]);
111 } elseif (!isset($key) || empty($key)) {
112 $this->content[] = $content;
113 } else {
114 $this->content[$key] = $content;
115 }
116 return $oldcontent;
117 }
118
119 /**
120 * Returns the content for the ajax call
121 *
122 * @return mixed The content for a specific key or the whole content
123 */
124 public function getContent($key = '') {
125 return $key && array_key_exists($key, $this->content) ? $this->content[$key] : $this->content;
126 }
127
128 /**
129 * Sets the content format for the ajax call
130 *
131 * @param string $format Can be one of 'plain' (default), 'xml', 'json', 'javascript', 'jsonbody' or 'jsonhead'
132 * @return void
133 */
134 public function setContentFormat($format) {
135 if (ArrayUtility::inArray(array('plain', 'xml', 'json', 'jsonhead', 'jsonbody', 'javascript'), $format)) {
136 $this->contentFormat = $format;
137 }
138 }
139
140 /**
141 * Specifies the wrap to be used if contentFormat is "javascript".
142 * The wrap used by default stores the results in a variable "response" and
143 * adds <script>-Tags around it.
144 *
145 * @param string $javascriptCallbackWrap The javascript callback wrap to be used
146 * @return void
147 */
148 public function setJavascriptCallbackWrap($javascriptCallbackWrap) {
149 $this->javascriptCallbackWrap = $javascriptCallbackWrap;
150 }
151
152 /**
153 * Sets an error message and the error flag
154 *
155 * @param string $errorMsg The error message
156 * @return void
157 */
158 public function setError($errorMsg = '') {
159 $this->errorMessage = $errorMsg;
160 $this->isError = TRUE;
161 }
162
163 /**
164 * Checks whether an error occurred during the execution or not
165 *
166 * @return bool Whether this AJAX call had errors
167 */
168 public function isError() {
169 return $this->isError;
170 }
171
172 /**
173 * Renders the AJAX call based on the $contentFormat variable and exits the request
174 *
175 * @return ResponseInterface|NULL
176 */
177 public function render() {
178 if ($this->isError) {
179 return $this->renderAsError();
180 }
181 switch ($this->contentFormat) {
182 case 'jsonhead':
183 case 'jsonbody':
184 case 'json':
185 return $this->renderAsJSON();
186 break;
187 case 'javascript':
188 return $this->renderAsJavascript();
189 break;
190 case 'xml':
191 return $this->renderAsXML();
192 break;
193 default:
194 return $this->renderAsPlain();
195 }
196 }
197
198 /**
199 * Renders the AJAX call in XML error style to handle with JS
200 * the "responseXML" of the transport object will be filled with the error message then
201 *
202 * @return ResponseInterface
203 */
204 protected function renderAsError() {
205 /** @var Response $response */
206 $response = GeneralUtility::makeInstance(Response::class);
207 $response = $response
208 ->withStatus(500, ' (AJAX)')
209 ->withHeader('Content-type', 'text/xml; charset=utf-8')
210 ->withHeader('X-JSON', 'false');
211
212 $response->getBody()->write('<t3err>' . htmlspecialchars($this->errorMessage) . '</t3err>');
213 return $response;
214 }
215
216 /**
217 * Renders the AJAX call with text/html headers
218 * the content will be available in the "responseText" value of the transport object
219 *
220 * @return ResponseInterface
221 * @throws \InvalidArgumentException
222 */
223 protected function renderAsPlain() {
224 /** @var Response $response */
225 $response = GeneralUtility::makeInstance(Response::class);
226 $response = $response
227 ->withHeader('Content-type', 'text/html; charset=utf-8')
228 ->withHeader('X-JSON', 'true');
229
230 $response->getBody()->write(implode('', $this->content));
231 return $response;
232 }
233
234 /**
235 * Renders the AJAX call with text/xml headers
236 * the content will be available in the "responseXML" value of the transport object
237 *
238 * @return ResponseInterface
239 * @throws \InvalidArgumentException
240 */
241 protected function renderAsXML() {
242 /** @var Response $response */
243 $response = GeneralUtility::makeInstance(Response::class);
244 $response = $response
245 ->withHeader('Content-type', 'text/xml; charset=utf-8')
246 ->withHeader('X-JSON', 'true');
247
248 $response->getBody()->write(implode('', $this->content));
249 return $response;
250 }
251
252 /**
253 * Renders the AJAX call with JSON evaluated headers
254 * note that you need to have requestHeaders: {Accept: 'application/json'},
255 * in your AJAX options of your AJAX request object in JS
256 *
257 * the content will be available
258 * - in the second parameter of the onSuccess / onComplete callback
259 * - and in the xhr.responseText as a string (except when contentFormat = 'jsonhead')
260 * you can evaluate this in JS with xhr.responseText.evalJSON();
261 *
262 * @return ResponseInterface
263 * @throws \InvalidArgumentException
264 */
265 protected function renderAsJSON() {
266 /** @var Response $response */
267 $response = GeneralUtility::makeInstance(Response::class);
268 $response = $response->withHeader('Content-type', 'application/json; charset=utf-8');
269
270 $content = json_encode($this->content);
271 // Bring content in xhr.responseText except when in "json head only" mode
272 if ($this->contentFormat === 'jsonhead') {
273 $response = $response->withHeader('X-JSON', $content);
274 } else {
275 $response = $response->withHeader('X-JSON', 'true');
276 $response->getBody()->write($content);
277 }
278 return $response;
279 }
280
281 /**
282 * Renders the AJAX call as inline JSON inside a script tag. This is useful
283 * when an iframe is used as the AJAX transport.
284 *
285 * @return ResponseInterface
286 * @throws \InvalidArgumentException
287 */
288 protected function renderAsJavascript() {
289 $response = GeneralUtility::makeInstance(Response::class);
290 $response = $response->withHeader('Content-type', 'text/html; charset=utf-8');
291 $response->getBody()->write(str_replace('|', json_encode($this->content), $this->javascriptCallbackWrap));
292 return $response;
293 }
294
295 }