Added feature #13899: Add Viewport layout to BE (thanks to Stefan Galinski)
[Packages/TYPO3.CMS.git] / typo3 / classes / class.typo3ajax.php
1 <?php
2 /***************************************************************
3 * Copyright notice
4 *
5 * (c) 2008-2009 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 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 (e.g. forceCharset), 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 if ($GLOBALS['LANG']->charSet != $this->charset) {
65 $this->charset = $GLOBALS['LANG']->charSet;
66 }
67
68 // get charset from current AJAX request (which is expected to be utf-8)
69 preg_match('/;\s*charset\s*=\s*([a-zA-Z0-9_-]*)/i', $_SERVER['CONTENT_TYPE'], $contenttype);
70 $charset = $GLOBALS['LANG']->csConvObj->parse_charset($contenttype[1]);
71 if ($charset && $charset != $this->requestCharset) {
72 $this->requestCharset = $charset;
73 }
74
75 // if the AJAX request does not have the same encoding like the backend
76 // we need to convert the POST and GET parameters in the right charset
77 if ($this->charset != $this->requestCharset) {
78 $GLOBALS['LANG']->csConvObj->convArray($_POST, $this->requestCharset, $this->charset);
79 $GLOBALS['LANG']->csConvObj->convArray($_GET, $this->requestCharset, $this->charset);
80 }
81
82 $this->ajaxId = $ajaxId;
83 }
84
85
86 /**
87 * returns the ID for the AJAX call
88 *
89 * @return string the AJAX id
90 */
91 public function getAjaxID() {
92 return $this->ajaxId;
93 }
94
95
96 /**
97 * overwrites the existing content with the first parameter
98 *
99 * @param array the new content
100 * @return mixed the old content as array; if the new content was not an array, false is returned
101 */
102 public function setContent($content) {
103 $oldcontent = false;
104 if (is_array($content)) {
105 $oldcontent = $this->content;
106 $this->content = $content;
107 }
108 return $oldcontent;
109 }
110
111
112 /**
113 * adds new content
114 *
115 * @param string the new content key where the content should be added in the content array
116 * @param string the new content to add
117 * @return mixed the old content; if the old content didn't exist before, false is returned
118 */
119 public function addContent($key, $content) {
120 $oldcontent = false;
121 if (array_key_exists($key, $this->content)) {
122 $oldcontent = $this->content[$key];
123 }
124 if (!isset($content) || empty($content)) {
125 unset($this->content[$key]);
126 } elseif (!isset($key) || empty($key)) {
127 $this->content[] = $content;
128 } else {
129 $this->content[$key] = $content;
130 }
131 return $oldcontent;
132 }
133
134
135 /**
136 * returns the content for the ajax call
137 *
138 * @return mixed the content for a specific key or the whole content
139 */
140 public function getContent($key = '') {
141 return ($key && array_key_exists($key, $this->content) ? $this->content[$key] : $this->content);
142 }
143
144
145 /**
146 * sets the content format for the ajax call
147 *
148 * @param string can be one of 'plain' (default), 'xml', 'json', 'javascript', 'jsonbody' or 'jsonhead'
149 * @return void
150 */
151 public function setContentFormat($format) {
152 if (t3lib_div::inArray(array('plain', 'xml', 'json', 'jsonhead', 'jsonbody', 'javascript'), $format)) {
153 $this->contentFormat = $format;
154 }
155 }
156
157 /**
158 * Specifies the wrap to be used if contentFormat is "javascript".
159 * The wrap used by default stores the results in a variable "response" and
160 * adds <script>-Tags around it.
161 *
162 * @param string $javascriptCallbackWrap the javascript callback wrap to be used
163 * @return void
164 */
165 public function setJavascriptCallbackWrap($javascriptCallbackWrap) {
166 $this->javascriptCallbackWrap = $javascriptCallbackWrap;
167 }
168
169 /**
170 * sets an error message and the error flag
171 *
172 * @param string the error message
173 * @return void
174 */
175 public function setError($errorMsg = '') {
176 $this->errorMessage = $errorMsg;
177 $this->isError = true;
178 }
179
180
181 /**
182 * checks whether an error occured during the execution or not
183 *
184 * @return boolean whether this AJAX call had errors
185 */
186 public function isError() {
187 return $this->isError;
188 }
189
190
191 /**
192 * renders the AJAX call based on the $contentFormat variable and exits the request
193 *
194 * @return void
195 */
196 public function render() {
197 if ($this->isError) {
198 $this->renderAsError();
199 exit;
200 }
201 switch ($this->contentFormat) {
202 case 'jsonhead':
203 case 'jsonbody':
204 case 'json':
205 $this->renderAsJSON();
206 break;
207 case 'javascript':
208 $this->renderAsJavascript();
209 break;
210 case 'xml':
211 $this->renderAsXML();
212 break;
213 default:
214 $this->renderAsPlain();
215 }
216 exit;
217 }
218
219
220 /**
221 * renders the AJAX call in XML error style to handle with JS
222 * the "responseXML" of the transport object will be filled with the error message then
223 *
224 * @return void
225 */
226 protected function renderAsError() {
227 header(t3lib_utility_Http::HTTP_STATUS_500 . ' (AJAX)');
228 header('Content-type: text/xml; charset='.$this->charset);
229 header('X-JSON: false');
230 die('<t3err>'.htmlspecialchars($this->errorMessage).'</t3err>');
231 }
232
233
234 /**
235 * renders the AJAX call with text/html headers
236 * the content will be available in the "responseText" value of the transport object
237 *
238 * @return void
239 */
240 protected function renderAsPlain() {
241 header('Content-type: text/html; charset='.$this->charset);
242 header('X-JSON: true');
243 echo implode('', $this->content);
244 }
245
246
247 /**
248 * renders the AJAX call with text/xml headers
249 * the content will be available in the "responseXML" value of the transport object
250 *
251 * @return void
252 */
253 protected function renderAsXML() {
254 header('Content-type: text/xml; charset='.$this->charset);
255 header('X-JSON: true');
256 echo implode('', $this->content);
257 }
258
259
260 /**
261 * renders the AJAX call with JSON evaluated headers
262 * note that you need to have requestHeaders: {Accept: 'application/json'},
263 * in your AJAX options of your AJAX request object in JS
264 *
265 * the content will be available
266 * - in the second parameter of the onSuccess / onComplete callback (except when contentFormat = 'jsonbody')
267 * - and in the xhr.responseText as a string (except when contentFormat = 'jsonhead')
268 * you can evaluate this in JS with xhr.responseText.evalJSON();
269 *
270 * @return void
271 */
272 protected function renderAsJSON() {
273 // if the backend does not run in UTF-8 then we need to convert it to unicode as
274 // the json_encode method will return empty otherwise
275 if ($this->charset != $this->requestCharset) {
276 $GLOBALS['LANG']->csConvObj->convArray($this->content, $this->charset, $this->requestCharset);
277 }
278
279 $content = json_encode($this->content);
280
281 header('Content-type: application/json; charset='.$this->requestCharset);
282 header('X-JSON: '.($this->contentFormat != 'jsonbody' ? $content : true));
283
284 // bring content in xhr.responseText except when in "json head only" mode
285 if ($this->contentFormat != 'jsonhead') {
286 echo $content;
287 }
288 }
289
290 /**
291 * Renders the AJAX call as inline JSON inside a script tag. This is useful
292 * when an iframe is used as the AJAX transport.
293 *
294 * @return void
295 */
296 protected function renderAsJavascript() {
297 // if the backend does not run in UTF-8 then we need to convert it to unicode as
298 // the json_encode method will return empty otherwise
299 if ($this->charset != $this->requestCharset) {
300 $GLOBALS['LANG']->csConvObj->convArray($this->content, $this->charset, $this->requestCharset);
301 }
302
303 $content = str_replace('|', json_encode($this->content), $this->javascriptCallbackWrap);
304
305 header('Content-type: text/html; charset=' . $this->requestCharset);
306 echo $content;
307 }
308 }
309
310
311 if (defined('TYPO3_MODE') && $TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['typo3/classes/class.typo3ajax.php']) {
312 include_once($TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['typo3/classes/class.typo3ajax.php']);
313 }
314
315 ?>