TYPO3 API  SVNRelease
class.typo3ajax.php
Go to the documentation of this file.
00001 <?php
00002 /***************************************************************
00003 *  Copyright notice
00004 *
00005 *  (c) 2008-2011 Benjamin Mack <mack@xnos.org>
00006 *  All rights reserved
00007 *
00008 *  This script is part of the TYPO3 project. The TYPO3 project is
00009 *  free software; you can redistribute it and/or modify
00010 *  it under the terms of the GNU General Public License as published by
00011 *  the Free Software Foundation; either version 2 of the License, or
00012 *  (at your option) any later version.
00013 *
00014 *  The GNU General Public License can be found at
00015 *  http://www.gnu.org/copyleft/gpl.html.
00016 *  A copy is found in the textfile GPL.txt and important notices to the license
00017 *  from the author is found in LICENSE.txt distributed with these scripts.
00018 *
00019 *
00020 *  This script is distributed in the hope that it will be useful,
00021 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00022 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00023 *  GNU General Public License for more details.
00024 *
00025 *  This copyright notice MUST APPEAR in all copies of the script!
00026 ***************************************************************/
00027 
00028 /**
00029  * class to hold all the information about an AJAX call and send
00030  * the right headers for the request type
00031  *
00032  * @author  Benjamin Mack <mack@xnos.org>
00033  * @package TYPO3
00034  * @subpackage  core
00035  */
00036 class TYPO3AJAX {
00037     protected $ajaxId        = null;
00038     protected $errorMessage  = null;
00039     protected $isError       = false;
00040     protected $content       = array();
00041     protected $contentFormat = 'plain';
00042     protected $charset       = 'utf-8';
00043     protected $requestCharset = 'utf-8';
00044     protected $javascriptCallbackWrap = '
00045         <script type="text/javascript">
00046             /*<![CDATA[*/
00047             response = |;
00048             /*]]>*/
00049         </script>
00050     ';
00051 
00052     /**
00053      * sets the charset and the ID for the AJAX call
00054      * due some charset limitations in Javascript (prototype uses encodeURIcomponent, which converts
00055      * all data to utf-8), we need to detect if the encoding of the request differs from the
00056      * backend encoding (e.g. forceCharset), and then convert all incoming data (_GET and _POST)
00057      * in the expected backend encoding.
00058      *
00059      * @param   string      the AJAX id
00060      * @return  void
00061      */
00062     public function __construct($ajaxId) {
00063 
00064         if ($GLOBALS['LANG']->charSet != $this->charset) {
00065             $this->charset = $GLOBALS['LANG']->charSet;
00066         }
00067 
00068             // get charset from current AJAX request (which is expected to be utf-8)
00069         preg_match('/;\s*charset\s*=\s*([a-zA-Z0-9_-]*)/i', $_SERVER['CONTENT_TYPE'], $contenttype);
00070         $charset = $GLOBALS['LANG']->csConvObj->parse_charset($contenttype[1]);
00071         if ($charset && $charset != $this->requestCharset) {
00072             $this->requestCharset = $charset;
00073         }
00074 
00075                 // if the AJAX request does not have the same encoding like the backend
00076                 // we need to convert the POST and GET parameters in the right charset
00077         if ($this->charset != $this->requestCharset) {
00078             $GLOBALS['LANG']->csConvObj->convArray($_POST, $this->requestCharset, $this->charset);
00079             $GLOBALS['LANG']->csConvObj->convArray($_GET,  $this->requestCharset, $this->charset);
00080         }
00081 
00082         $this->ajaxId = $ajaxId;
00083     }
00084 
00085 
00086     /**
00087      * returns the ID for the AJAX call
00088      *
00089      * @return  string      the AJAX id
00090      */
00091     public function getAjaxID() {
00092         return $this->ajaxId;
00093     }
00094 
00095 
00096     /**
00097      * overwrites the existing content with the first parameter
00098      *
00099      * @param   array       the new content
00100      * @return  mixed       the old content as array; if the new content was not an array, false is returned
00101      */
00102     public function setContent($content) {
00103         $oldcontent = false;
00104         if (is_array($content)) {
00105             $oldcontent = $this->content;
00106             $this->content = $content;
00107         }
00108         return $oldcontent;
00109     }
00110 
00111 
00112     /**
00113      * adds new content
00114      *
00115      * @param   string      the new content key where the content should be added in the content array
00116      * @param   string      the new content to add
00117      * @return  mixed       the old content; if the old content didn't exist before, false is returned
00118      */
00119     public function addContent($key, $content) {
00120         $oldcontent = false;
00121         if (array_key_exists($key, $this->content)) {
00122             $oldcontent = $this->content[$key];
00123         }
00124         if (!isset($content) || empty($content)) {
00125             unset($this->content[$key]);
00126         } elseif (!isset($key) || empty($key)) {
00127             $this->content[] = $content;
00128         } else {
00129             $this->content[$key] = $content;
00130         }
00131         return $oldcontent;
00132     }
00133 
00134 
00135     /**
00136      * returns the content for the ajax call
00137      *
00138      * @return  mixed       the content for a specific key or the whole content
00139      */
00140     public function getContent($key = '') {
00141         return ($key && array_key_exists($key, $this->content) ? $this->content[$key] : $this->content);
00142     }
00143 
00144 
00145     /**
00146      * sets the content format for the ajax call
00147      *
00148      * @param   string      can be one of 'plain' (default), 'xml', 'json', 'javascript', 'jsonbody' or 'jsonhead'
00149      * @return  void
00150      */
00151     public function setContentFormat($format) {
00152         if (t3lib_div::inArray(array('plain', 'xml', 'json', 'jsonhead', 'jsonbody', 'javascript'), $format)) {
00153             $this->contentFormat = $format;
00154         }
00155     }
00156 
00157     /**
00158      * Specifies the wrap to be used if contentFormat is "javascript".
00159      * The wrap used by default stores the results in a variable "response" and
00160      * adds <script>-Tags around it.
00161      *
00162      * @param string $javascriptCallbackWrap the javascript callback wrap to be used
00163      * @return void
00164      */
00165     public function setJavascriptCallbackWrap($javascriptCallbackWrap) {
00166         $this->javascriptCallbackWrap = $javascriptCallbackWrap;
00167     }
00168 
00169     /**
00170      * sets an error message and the error flag
00171      *
00172      * @param   string      the error message
00173      * @return  void
00174      */
00175     public function setError($errorMsg = '') {
00176         $this->errorMessage = $errorMsg;
00177         $this->isError = true;
00178     }
00179 
00180 
00181     /**
00182      * checks whether an error occured during the execution or not
00183      *
00184      * @return  boolean     whether this AJAX call had errors
00185      */
00186     public function isError() {
00187         return $this->isError;
00188     }
00189 
00190 
00191     /**
00192      * renders the AJAX call based on the $contentFormat variable and exits the request
00193      *
00194      * @return  void
00195      */
00196     public function render() {
00197         if ($this->isError) {
00198             $this->renderAsError();
00199             exit;
00200         }
00201         switch ($this->contentFormat) {
00202             case 'jsonhead':
00203             case 'jsonbody':
00204             case 'json':
00205                 $this->renderAsJSON();
00206                 break;
00207             case 'javascript':
00208                 $this->renderAsJavascript();
00209                 break;
00210             case 'xml':
00211                 $this->renderAsXML();
00212                 break;
00213             default:
00214                 $this->renderAsPlain();
00215         }
00216         exit;
00217     }
00218 
00219 
00220     /**
00221      * renders the AJAX call in XML error style to handle with JS
00222      * the "responseXML" of the transport object will be filled with the error message then
00223      *
00224      * @return  void
00225      */
00226     protected function renderAsError() {
00227         header(t3lib_utility_Http::HTTP_STATUS_500 . ' (AJAX)');
00228         header('Content-type: text/xml; charset='.$this->charset);
00229         header('X-JSON: false');
00230         die('<t3err>'.htmlspecialchars($this->errorMessage).'</t3err>');
00231     }
00232 
00233 
00234     /**
00235      * renders the AJAX call with text/html headers
00236      * the content will be available in the "responseText" value of the transport object
00237      *
00238      * @return  void
00239      */
00240     protected function renderAsPlain() {
00241         header('Content-type: text/html; charset='.$this->charset);
00242         header('X-JSON: true');
00243         echo implode('', $this->content);
00244     }
00245 
00246 
00247     /**
00248      * renders the AJAX call with text/xml headers
00249      * the content will be available in the "responseXML" value of the transport object
00250      *
00251      * @return  void
00252      */
00253     protected function renderAsXML() {
00254         header('Content-type: text/xml; charset='.$this->charset);
00255         header('X-JSON: true');
00256         echo implode('', $this->content);
00257     }
00258 
00259 
00260     /**
00261      * renders the AJAX call with JSON evaluated headers
00262      * note that you need to have requestHeaders: {Accept: 'application/json'},
00263      * in your AJAX options of your AJAX request object in JS
00264      *
00265      * the content will be available
00266      *    - in the second parameter of the onSuccess / onComplete callback (except when contentFormat = 'jsonbody')
00267      *    - and in the xhr.responseText as a string (except when contentFormat = 'jsonhead')
00268      *         you can evaluate this in JS with xhr.responseText.evalJSON();
00269      *
00270      * @return  void
00271      */
00272     protected function renderAsJSON() {
00273             // if the backend does not run in UTF-8 then we need to convert it to unicode as
00274             // the json_encode method will return empty otherwise
00275         if ($this->charset != $this->requestCharset) {
00276             $GLOBALS['LANG']->csConvObj->convArray($this->content, $this->charset, $this->requestCharset);
00277         }
00278 
00279         $content = json_encode($this->content);
00280 
00281         header('Content-type: application/json; charset='.$this->requestCharset);
00282         header('X-JSON: '.($this->contentFormat != 'jsonbody' ? $content : true));
00283 
00284             // bring content in xhr.responseText except when in "json head only" mode
00285         if ($this->contentFormat != 'jsonhead') {
00286             echo $content;
00287         }
00288     }
00289 
00290     /**
00291      * Renders the AJAX call as inline JSON inside a script tag. This is useful
00292      * when an iframe is used as the AJAX transport.
00293      *
00294      * @return   void
00295      */
00296     protected function renderAsJavascript() {
00297             // if the backend does not run in UTF-8 then we need to convert it to unicode as
00298             // the json_encode method will return empty otherwise
00299         if ($this->charset != $this->requestCharset) {
00300             $GLOBALS['LANG']->csConvObj->convArray($this->content, $this->charset, $this->requestCharset);
00301         }
00302 
00303         $content = str_replace('|', json_encode($this->content), $this->javascriptCallbackWrap);
00304 
00305         header('Content-type: text/html; charset=' . $this->requestCharset);
00306         echo $content;
00307     }
00308 }
00309 
00310 
00311 if (defined('TYPO3_MODE') && isset($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['XCLASS']['typo3/classes/class.typo3ajax.php'])) {
00312     include_once($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['XCLASS']['typo3/classes/class.typo3ajax.php']);
00313 }
00314 
00315 ?>