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