ff8811ab6f7df94b741bf8363fb01c45aba3c18d
[Packages/TYPO3.CMS.git] / typo3 / classes / class.typo3ajax.php
1 <?php
2 /***************************************************************
3 * Copyright notice
4 *
5 * (c) 2008-2011 Benjamin Mack <mack@xnos.org>
6 * All rights reserved
7 *
8 * This script is part of the TYPO3 project. The TYPO3 project is
9 * free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * The GNU General Public License can be found at
15 * http://www.gnu.org/copyleft/gpl.html.
16 * A copy is found in the textfile GPL.txt and important notices to the license
17 * from the author is found in LICENSE.txt distributed with these scripts.
18 *
19 *
20 * This script is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 * GNU General Public License for more details.
24 *
25 * This copyright notice MUST APPEAR in all copies of the script!
26 ***************************************************************/
27
28 /**
29 * class to hold all the information about an AJAX call and send
30 * the right headers for the request type
31 *
32 * @author Benjamin Mack <mack@xnos.org>
33 * @package TYPO3
34 * @subpackage core
35 */
36 class TYPO3AJAX {
37 protected $ajaxId = NULL;
38 protected $errorMessage = NULL;
39 protected $isError = FALSE;
40 protected $content = array();
41 protected $contentFormat = 'plain';
42 protected $charset = 'utf-8';
43 protected $requestCharset = 'utf-8';
44 protected $javascriptCallbackWrap = '
45 <script type="text/javascript">
46 /*<![CDATA[*/
47 response = |;
48 /*]]>*/
49 </script>
50 ';
51
52 /**
53 * sets the charset and the ID for the AJAX call
54 * due to some charset limitations in Javascript (prototype uses encodeURIcomponent, which converts
55 * all data to utf-8), we need to detect if the encoding of the request differs from the
56 * backend encoding, and then convert all incoming data (_GET and _POST)
57 * in the expected backend encoding.
58 *
59 * @param string the AJAX id
60 * @return void
61 */
62 public function __construct($ajaxId) {
63
64 // get charset from current AJAX request (which is expected to be utf-8)
65 preg_match('/;\s*charset\s*=\s*([a-zA-Z0-9_-]*)/i', $_SERVER['CONTENT_TYPE'], $contenttype);
66 $charset = $GLOBALS['LANG']->csConvObj->parse_charset($contenttype[1]);
67 if ($charset && $charset != $this->requestCharset) {
68 $this->requestCharset = $charset;
69 }
70
71 // if the AJAX request does not have the same encoding like the backend
72 // we need to convert the POST and GET parameters in the right charset
73 if ($this->charset != $this->requestCharset) {
74 $GLOBALS['LANG']->csConvObj->convArray($_POST, $this->requestCharset, $this->charset);
75 $GLOBALS['LANG']->csConvObj->convArray($_GET, $this->requestCharset, $this->charset);
76 }
77
78 $this->ajaxId = $ajaxId;
79 }
80
81
82 /**
83 * returns the ID for the AJAX call
84 *
85 * @return string the AJAX id
86 */
87 public function getAjaxID() {
88 return $this->ajaxId;
89 }
90
91
92 /**
93 * overwrites the existing content with the first parameter
94 *
95 * @param array the new content
96 * @return mixed the old content as array; if the new content was not an array, FALSE is returned
97 */
98 public function setContent($content) {
99 $oldcontent = FALSE;
100 if (is_array($content)) {
101 $oldcontent = $this->content;
102 $this->content = $content;
103 }
104 return $oldcontent;
105 }
106
107
108 /**
109 * adds new content
110 *
111 * @param string the new content key where the content should be added in the content array
112 * @param string the new content to add
113 * @return mixed the old content; if the old content didn't exist before, FALSE is returned
114 */
115 public function addContent($key, $content) {
116 $oldcontent = FALSE;
117 if (array_key_exists($key, $this->content)) {
118 $oldcontent = $this->content[$key];
119 }
120 if (!isset($content) || empty($content)) {
121 unset($this->content[$key]);
122 } elseif (!isset($key) || empty($key)) {
123 $this->content[] = $content;
124 } else {
125 $this->content[$key] = $content;
126 }
127 return $oldcontent;
128 }
129
130
131 /**
132 * returns the content for the ajax call
133 *
134 * @return mixed the content for a specific key or the whole content
135 */
136 public function getContent($key = '') {
137 return ($key && array_key_exists($key, $this->content) ? $this->content[$key] : $this->content);
138 }
139
140
141 /**
142 * sets the content format for the ajax call
143 *
144 * @param string can be one of 'plain' (default), 'xml', 'json', 'javascript', 'jsonbody' or 'jsonhead'
145 * @return void
146 */
147 public function setContentFormat($format) {
148 if (t3lib_div::inArray(array('plain', 'xml', 'json', 'jsonhead', 'jsonbody', 'javascript'), $format)) {
149 $this->contentFormat = $format;
150 }
151 }
152
153 /**
154 * Specifies the wrap to be used if contentFormat is "javascript".
155 * The wrap used by default stores the results in a variable "response" and
156 * adds <script>-Tags around it.
157 *
158 * @param string $javascriptCallbackWrap the javascript callback wrap to be used
159 * @return void
160 */
161 public function setJavascriptCallbackWrap($javascriptCallbackWrap) {
162 $this->javascriptCallbackWrap = $javascriptCallbackWrap;
163 }
164
165 /**
166 * sets an error message and the error flag
167 *
168 * @param string the error message
169 * @return void
170 */
171 public function setError($errorMsg = '') {
172 $this->errorMessage = $errorMsg;
173 $this->isError = TRUE;
174 }
175
176
177 /**
178 * checks whether an error occured during the execution or not
179 *
180 * @return boolean whether this AJAX call had errors
181 */
182 public function isError() {
183 return $this->isError;
184 }
185
186
187 /**
188 * renders the AJAX call based on the $contentFormat variable and exits the request
189 *
190 * @return void
191 */
192 public function render() {
193 if ($this->isError) {
194 $this->renderAsError();
195 exit;
196 }
197 switch ($this->contentFormat) {
198 case 'jsonhead':
199 case 'jsonbody':
200 case 'json':
201 $this->renderAsJSON();
202 break;
203 case 'javascript':
204 $this->renderAsJavascript();
205 break;
206 case 'xml':
207 $this->renderAsXML();
208 break;
209 default:
210 $this->renderAsPlain();
211 }
212 exit;
213 }
214
215
216 /**
217 * renders the AJAX call in XML error style to handle with JS
218 * the "responseXML" of the transport object will be filled with the error message then
219 *
220 * @return void
221 */
222 protected function renderAsError() {
223 header(t3lib_utility_Http::HTTP_STATUS_500 . ' (AJAX)');
224 header('Content-type: text/xml; charset='.$this->charset);
225 header('X-JSON: false');
226 die('<t3err>'.htmlspecialchars($this->errorMessage).'</t3err>');
227 }
228
229
230 /**
231 * renders the AJAX call with text/html headers
232 * the content will be available in the "responseText" value of the transport object
233 *
234 * @return void
235 */
236 protected function renderAsPlain() {
237 header('Content-type: text/html; charset='.$this->charset);
238 header('X-JSON: true');
239 echo implode('', $this->content);
240 }
241
242
243 /**
244 * renders the AJAX call with text/xml headers
245 * the content will be available in the "responseXML" value of the transport object
246 *
247 * @return void
248 */
249 protected function renderAsXML() {
250 header('Content-type: text/xml; charset='.$this->charset);
251 header('X-JSON: true');
252 echo implode('', $this->content);
253 }
254
255
256 /**
257 * renders the AJAX call with JSON evaluated headers
258 * note that you need to have requestHeaders: {Accept: 'application/json'},
259 * in your AJAX options of your AJAX request object in JS
260 *
261 * the content will be available
262 * - in the second parameter of the onSuccess / onComplete callback (except when contentFormat = 'jsonbody')
263 * - and in the xhr.responseText as a string (except when contentFormat = 'jsonhead')
264 * you can evaluate this in JS with xhr.responseText.evalJSON();
265 *
266 * @return void
267 */
268 protected function renderAsJSON() {
269 // if the backend does not run in UTF-8 then we need to convert it to unicode as
270 // the json_encode method will return empty otherwise
271 if ($this->charset != $this->requestCharset) {
272 $GLOBALS['LANG']->csConvObj->convArray($this->content, $this->charset, $this->requestCharset);
273 }
274
275 $content = json_encode($this->content);
276
277 header('Content-type: application/json; charset='.$this->requestCharset);
278 header('X-JSON: '.($this->contentFormat != 'jsonbody' ? $content : TRUE));
279
280 // bring content in xhr.responseText except when in "json head only" mode
281 if ($this->contentFormat != 'jsonhead') {
282 echo $content;
283 }
284 }
285
286 /**
287 * Renders the AJAX call as inline JSON inside a script tag. This is useful
288 * when an iframe is used as the AJAX transport.
289 *
290 * @return void
291 */
292 protected function renderAsJavascript() {
293 // if the backend does not run in UTF-8 then we need to convert it to unicode as
294 // the json_encode method will return empty otherwise
295 if ($this->charset != $this->requestCharset) {
296 $GLOBALS['LANG']->csConvObj->convArray($this->content, $this->charset, $this->requestCharset);
297 }
298
299 $content = str_replace('|', json_encode($this->content), $this->javascriptCallbackWrap);
300
301 header('Content-type: text/html; charset=' . $this->requestCharset);
302 echo $content;
303 }
304 }
305 ?>