Index: t3lib/config_default.php
===================================================================
--- t3lib/config_default.php	(revision 2868)
+++ t3lib/config_default.php	(working copy)
@@ -171,6 +171,9 @@
 		'flexFormXMLincludeDiffBase' => TRUE,	// If set, an additional tag with index "vXX.vDEFbase" is created for translations in flexforms holding the value of the default language when translation was changed. Used to show diff of value. This setting will change whether the system thinks flexform XML looks clean. For example when FALSE XX.vDEFbase fields will be removed in cleaning while accepted if TRUE (of course)
 		'compactFlexFormXML' => 0,				// If set, the flexform XML will not contain indentation spaces making XML more compact
 		'elementVersioningOnly' => FALSE,		// If true, only element versioning is allowed in the backend. This is recommended for new installations of TYPO3 4.2+ since "page" and "branch" versioning types are known for the drawbacks of loosing ids and "element" type versions supports moving now.
+		'AJAX' => array(				// array of key-value pairs for a unified use of AJAX calls in the TYPO3 backend. Keys are the unique ajaxIDs where the value will be resolved to call a method in an object. See ajax.php and the classes/class.typo3ajax.php for more information.
+			'pagetreeExpandCollapse' => 'typo3/alt_db_navframe.php:SC_alt_db_navframe->ajaxExpandCollapse',
+		),
 	),
 	'FE' => Array(			// Configuration for the TypoScript frontend (FE). Nothing here relates to the administration backend!
 		'png_to_gif' => 0,						// Boolean. Enables conversion back to gif of all png-files generated in the frontend libraries. Notice that this leaves an increased number of temporary files in typo3temp/
Index: t3lib/class.t3lib_div.php
===================================================================
--- t3lib/class.t3lib_div.php	(revision 2868)
+++ t3lib/class.t3lib_div.php	(working copy)
@@ -3961,11 +3961,12 @@
 	 * @param	mixed		Reference to be passed along (typically "$this" - being a reference to the calling object) (REFERENCE!)
 	 * @param	string		Required prefix of class or function name
 	 * @param	boolean		If set, no debug() error message is shown if class/function is not present.
-	 * @return	mixed		Content from method/function call
+	 * @return	mixed		Content from method/function call or false if the class/method/function was not found
 	 * @see getUserObj()
 	 */
 	function callUserFunction($funcName,&$params,&$ref,$checkPrefix='user_',$silent=0)	{
 		global $TYPO3_CONF_VARS;
+		$content = false;
 
 			// Check persistent object and if found, call directly and exit.
 		if (is_array($GLOBALS['T3_VAR']['callUserFunction'][$funcName]))	{
Index: typo3/class.webpagetree.php
===================================================================
--- typo3/class.webpagetree.php	(revision 2868)
+++ typo3/class.webpagetree.php	(working copy)
@@ -189,7 +189,7 @@
 		$PM = t3lib_div::_GP('PM');
 		if(($PMpos = strpos($PM, '#')) !== false) { $PM = substr($PM, 0, $PMpos); }
 		$PM = explode('_', $PM);
-		if(($isAjaxCall = t3lib_div::_GP('ajax')) && is_array($PM) && count($PM)==4)	{
+		if(TYPO3_AJAX && is_array($PM) && count($PM)==4)	{
 			if($PM[1])	{
 				$expandedPageUid = $PM[2];
 				$ajaxOutput = '';
Index: typo3/tree.js
===================================================================
--- typo3/tree.js	(revision 2868)
+++ typo3/tree.js	(working copy)
@@ -74,7 +74,7 @@
 
 
 var Tree = {
-	thisScript: null,
+	thisScript: top.TS.PATH_typo3 + 'ajax.php',
 	frameSetModule: null,
 	activateDragDrop: true,
 	highlightClass: 'active',
@@ -104,16 +104,17 @@
 
 		new Ajax.Request(this.thisScript, {
 			method: 'get',
-			parameters: 'ajax=1&PM=' + params,
-			onComplete: function(xhr, status) {
-				// if this is not a valid ajax response, the whole page gets refreshed
-				if (!status) return this.refresh();
-
+			parameters: 'ajaxID=pagetreeExpandCollapse&PM=' + params,
+			onComplete: function(xhr) {
 				// the parent node needs to be overwritten, not the object
 				$(obj.parentNode).replace(xhr.responseText);
 				this.registerDragDropHandlers();
 				this.reSelectActiveItem();
 				filter($('_livesearch').value);
+			}.bind(this),
+			onT3Error: function(xhr) {
+				// if this is not a valid ajax response, the whole page gets refreshed
+				this.refresh();
 			}.bind(this)
 		});
 	},
Index: typo3/js/common.js
===================================================================
--- typo3/js/common.js	(revision 0)
+++ typo3/js/common.js	(revision 0)
@@ -0,0 +1,70 @@
+/***************************************************************
+*
+*  javascript functions regarding the TYPO3 wrapper
+*  for the javascript library "prototype".
+*
+*  Copyright notice
+*
+*  (c) 2008 Benjamin Mack <www.xnos.org>
+*  All rights reserved
+*
+*  This script is part of the TYPO3 backend provided by
+*  Kasper Skaarhoj <kasper@typo3.com> together with TYPO3
+*
+*  Released under GNU/GPL (see license file in /typo3/)
+*
+*  This script is distributed in the hope that it will be useful,
+*  but WITHOUT ANY WARRANTY; without even the implied warranty of
+*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+*
+*  This copyright notice MUST APPEAR in all copies of this script
+*
+***************************************************************/
+
+// Please make sure that prototype.js is loaded before loading this
+// file in your script, the responder is only added if prototype was loaded
+
+if (Prototype) {
+	// adding generic a responder to use when a AJAX request is done
+	Ajax.Responders.register({
+		onCreate: function(request, transport) {
+
+			// if the TYPO3 AJAX backend is used, 
+			// the onSuccess & onComplete callbacks are hooked
+			if (request.url.indexOf("ajax.php") == -1) {
+				return;
+			}
+
+			var origSuccess = request.options.onSuccess, origComplete = request.options.onComplete;
+
+			// hooking "onSuccess"
+			if (origSuccess) {
+				request.options.onSuccess = function(xhr, json) {
+					if (!json) {
+						T3AJAX.showError(xhr);	
+					} else {
+						origSuccess(xhr);
+					}
+				}
+			}
+
+			// hooking "onComplete", using T3Error handler if available
+			if (origComplete) {
+				request.options.onComplete = function(xhr, json) {
+					if (!json && request.options.onT3Error) {
+						request.options.onT3Error(xhr);
+					} else if (!json) {
+						T3AJAX.showError(xhr);
+					} else {
+						origComplete(xhr);
+					}
+				}
+			}
+		}
+	});
+}
+
+var T3AJAX = new Object();
+T3AJAX.showError = function(xhr) {
+	alert(xhr.responseText);
+}

Property changes on: typo3/js/common.js
___________________________________________________________________
Name: svn:executable
   + *

Index: typo3/classes/class.typo3ajax.php
===================================================================
--- typo3/classes/class.typo3ajax.php	(revision 0)
+++ typo3/classes/class.typo3ajax.php	(revision 0)
@@ -0,0 +1,155 @@
+<?php
+/***************************************************************
+*  Copyright notice
+*
+*  (c) 2008 Benjamin Mack <mack@xnos.org>
+*  All rights reserved
+*
+*  This script is part of the TYPO3 project. The TYPO3 project is
+*  free software; you can redistribute it and/or modify
+*  it under the terms of the GNU General Public License as published by
+*  the Free Software Foundation; either version 2 of the License, or
+*  (at your option) any later version.
+*
+*  The GNU General Public License can be found at
+*  http://www.gnu.org/copyleft/gpl.html.
+*  A copy is found in the textfile GPL.txt and important notices to the license
+*  from the author is found in LICENSE.txt distributed with these scripts.
+*
+*
+*  This script is distributed in the hope that it will be useful,
+*  but WITHOUT ANY WARRANTY; without even the implied warranty of
+*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+*  GNU General Public License for more details.
+*
+*  This copyright notice MUST APPEAR in all copies of the script!
+***************************************************************/
+/**
+ * class to hold the information about an AJAX call
+ *
+ * @author	Benjamin Mack <mack@xnos.org>
+ * @package	TYPO3
+ * @subpackage	core
+ */
+class TYPO3AJAX {
+	private $id;
+	private $errorMessage;
+	private $isError;
+	private $content;
+
+
+	/**
+	 * constructor with the ajaxID
+	 *
+	 * @return void
+	 */
+	public function __construct() {
+		$this->id = null;
+		$this->content = null;
+		$this->isError = false;
+		$this->errorMessage = null;
+	}
+
+
+	/**
+	 * sets the ID for the AJAX call
+	 *
+	 * @param	string	the AJAX id
+	 * @return	void
+	 */
+	public function setID($id) {
+		$this->id = $id;
+	}
+
+
+	/**
+	 * returns the ID for the AJAX call
+	 *
+	 * @return	string	the AJAX id
+	 */
+	public function getID() {
+		return $this->id;
+	}
+
+
+	/**
+	 * overwrites the existing content with the first parameter
+	 *
+	 * @param	string	the new Content
+	 * @return	string	the old content
+	 */
+	public function setContent($content) {
+		$oldcontent = $this->content;
+		$this->content = $content;
+		return $oldcontent;
+	}
+
+
+	/**
+	 * adds new content to the existing content
+	 *
+	 * @param	string	the new content to add
+	 * @return	string	the old content
+	 */
+	public function addContent($content) {
+		$oldcontent = $this->content;
+		$this->content .= $content;
+		return $oldcontent;
+	}
+
+
+	/**
+	 * returns the content for the ajax call
+	 *
+	 * @return	string	the content
+	 */
+	public function getContent() {
+		return $this->content;
+	}
+
+
+	/**
+	 * sets an error message and the error flag
+	 *
+	 * @param	string	the error message
+	 * @return	void
+	 */
+	public function setError($errorMsg = '') {
+		$this->errorMessage = $errorMsg;
+		$this->isError = true;
+	}
+
+
+	/**
+	 * checks whether an error occured during the execution or not
+	 *
+	 * @return	boolean	whether this AJAX call 
+	 */
+	public function isError() {
+		return $this->isError;
+	}
+
+
+	/**
+	 * renders the AJAX call and exits the request
+	 *
+	 * @return	void
+	 */
+	public function output()	{
+		if ($this->isError)	{
+			header('X-JSON: false');
+			die('<t3err>'.$this->errorMessage.'</t3err>');
+		} else {
+			header('X-JSON: true');
+			echo $this->content;
+			exit;
+		}
+	}
+}
+
+
+if (defined('TYPO3_MODE') && $TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['typo3/classes/class.typo3ajax.php'])	{
+	include_once($TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['typo3/classes/class.typo3ajax.php']);
+}
+
+?>
Index: typo3/template.php
===================================================================
--- typo3/template.php	(revision 2868)
+++ typo3/template.php	(working copy)
@@ -1840,13 +1840,13 @@
 // The backend language engine is started (ext: "lang")
 // ******************************************************
 require_once(PATH_typo3.'sysext/lang/lang.php');
-$LANG = t3lib_div::makeInstance('language');
-$LANG->init($BE_USER->uc['lang']);
+$GLOBALS['LANG'] = t3lib_div::makeInstance('language');
+$GLOBALS['LANG']->init($BE_USER->uc['lang']);
 
 
 
 // ******************************
 // The template is loaded
 // ******************************
-$TBE_TEMPLATE = t3lib_div::makeInstance('template');
+$GLOBALS['TBE_TEMPLATE'] = t3lib_div::makeInstance('template');
 ?>
Index: typo3/alt_db_navframe.php
===================================================================
--- typo3/alt_db_navframe.php	(revision 2868)
+++ typo3/alt_db_navframe.php	(working copy)
@@ -53,7 +53,7 @@
 
 
 $BACK_PATH = '';
-require('init.php');
+require_once('init.php');
 require('template.php');
 require_once('class.webpagetree.php');
 
@@ -81,11 +81,11 @@
 	var $backPath;
 
 		// Internal, static: GPvar:
-	var $ajax;							// Is set, if an AJAX call should be handled.
 	var $currentSubScript;
 	var $cMR;
 	var $setTempDBmount;			// If not '' (blank) then it will clear (0) or set (>0) Temporary DB mount.
 
+
 	/**
 	 * Initialiation of the class
 	 *
@@ -98,7 +98,6 @@
 		$this->backPath = $BACK_PATH;
 
 			// Setting GPvars:
-		$this->ajax = t3lib_div::_GP('ajax');
 		$this->cMR = t3lib_div::_GP('cMR');
 		$this->currentSubScript = t3lib_div::_GP('currentSubScript');
 		$this->setTempDBmount = t3lib_div::_GP('setTempDBmount');
@@ -122,59 +121,72 @@
 
 			// Temporary DB mounts:
 		$this->initializeTemporaryDBmount();
+	}
 
-			// Use template rendering only if this is a non-AJAX call:
-		if (!$this->ajax) {
-				// Setting highlight mode:
-			$this->doHighlight = !$BE_USER->getTSConfigVal('options.pageTree.disableTitleHighlight');
 
-				// If highlighting is active, define the CSS class for the active item depending on the workspace
-			if ($this->doHighlight) {
-				if ($BE_USER->workspace === 0) $hlClass = 'active';
-				else $hlClass = 'active active-ws wsver'.$BE_USER->workspace; 
-			}
+	/**
+	 * function that renders the page for the pagetree, this is only for setting
+	 * up the display options for the page
+	 * Using only when it's not an AJAX call
+	 * 
+	 * @return	void
+	 */
+	public function initPage()	{
+		global $BE_USER;
 
-				// Create template object:
-			$this->doc = t3lib_div::makeInstance('template');
-			$this->doc->backPath = $BACK_PATH;
-			$this->doc->docType = 'xhtml_trans';
+			// Setting highlight mode:
+		$this->doHighlight = !$BE_USER->getTSConfigVal('options.pageTree.disableTitleHighlight');
 
-				// Adding javascript code for AJAX (prototype), drag&drop and the pagetree
-			$this->doc->loadJavascriptLib('contrib/prototype/prototype.js');
-			$this->doc->loadJavascriptLib('tree.js');
+			// If highlighting is active, define the CSS class for the active item depending on the workspace
+		if ($this->doHighlight) {
+			$hlClass = ($BE_USER->workspace === 0 ? 'active' : 'active active-ws wsver'.$BE_USER->workspace); 
+		}
 
-			$this->doc->JScode .= $this->doc->wrapScriptTags(
-			($this->currentSubScript?'top.currentSubScript=unescape("'.rawurlencode($this->currentSubScript).'");':'').'
-			// setting prefs for pagetree and drag & drop
-			Tree.thisScript    = "'.$this->pagetree->thisScript.'";
-			'.($this->doHighlight ? 'Tree.highlightClass = "'.$hlClass.'";' : '').'
 
-			DragDrop.changeURL = "'.$this->backPath.'alt_clickmenu.php";
-			DragDrop.backPath  = "'.t3lib_div::shortMD5(''.'|'.$GLOBALS['TYPO3_CONF_VARS']['SYS']['encryptionKey']).'";
-			DragDrop.table     = "pages";
+			// Create template object:
+		$this->doc = t3lib_div::makeInstance('template');
+		$this->doc->backPath = $BACK_PATH;
+		$this->doc->docType = 'xhtml_trans';
 
-			// Function, loading the list frame from navigation tree:
-			function jumpTo(id, linkObj, highlightID, bank)	{ //
-				var theUrl = top.TS.PATH_typo3 + top.currentSubScript + "?id=" + id;
-				top.fsMod.currentBank = bank;
 
-				if (top.condensedMode) top.content.location.href = theUrl;
-				else                   parent.list_frame.location.href=theUrl;
+			// Adding javascript code for AJAX (prototype), drag&drop and the pagetree
+		$this->doc->loadJavascriptLib('contrib/prototype/prototype.js');
+		$this->doc->loadJavascriptLib('js/common.js');
+		$this->doc->loadJavascriptLib('tree.js');
 
-				'.($this->doHighlight ? 'Tree.highlightActiveItem("web", highlightID + "_" + bank);' : '').'
-				'.(!$GLOBALS['CLIENT']['FORMSTYLE'] ? '' : 'if (linkObj) linkObj.blur(); ').'
-				return false;
-			}
-			'.($this->cMR?"jumpTo(top.fsMod.recentIds['web'],'');":'').'
-			');
 
-				// Click menu code is added:
-			$this->doc->getContextMenuCode();
-			$this->doc->bodyTagId = 'bodyTag';
+		$this->doc->JScode .= $this->doc->wrapScriptTags(
+		($this->currentSubScript?'top.currentSubScript=unescape("'.rawurlencode($this->currentSubScript).'");':'').'
+		// setting prefs for pagetree and drag & drop
+		'.($this->doHighlight ? 'Tree.highlightClass = "'.$hlClass.'";' : '').'
+
+		DragDrop.changeURL = "'.$this->backPath.'alt_clickmenu.php";
+		DragDrop.backPath  = "'.t3lib_div::shortMD5(''.'|'.$GLOBALS['TYPO3_CONF_VARS']['SYS']['encryptionKey']).'";
+		DragDrop.table     = "pages";
+
+		// Function, loading the list frame from navigation tree:
+		function jumpTo(id, linkObj, highlightID, bank)	{ //
+			var theUrl = top.TS.PATH_typo3 + top.currentSubScript + "?id=" + id;
+			top.fsMod.currentBank = bank;
+
+			if (top.condensedMode) top.content.location.href = theUrl;
+			else                   parent.list_frame.location.href=theUrl;
+
+			'.($this->doHighlight ? 'Tree.highlightActiveItem("web", highlightID + "_" + bank);' : '').'
+			'.(!$GLOBALS['CLIENT']['FORMSTYLE'] ? '' : 'if (linkObj) linkObj.blur(); ').'
+			return false;
 		}
+		'.($this->cMR?"jumpTo(top.fsMod.recentIds['web'],'');":'').'
+		');
+
+
+			// Click menu code is added:
+		$this->doc->getContextMenuCode();
+		$this->doc->bodyTagId = 'bodyTag';
 	}
 
 
+
 	/**
 	 * Main function, rendering the browsable page tree
 	 *
@@ -186,12 +198,6 @@
 			// Produce browse-tree:
 		$tree = $this->pagetree->getBrowsableTree();
 
-			// Output only the tree if this is an AJAX call:
-		if ($this->ajax) {
-			$this->content = $LANG->csConvObj->utf8_encode($tree, $LANG->charSet);
-			return;
-		}
-
 			// Start page:
 		$this->content = $this->doc->startPage('TYPO3 Page Tree');
 		
@@ -267,15 +273,8 @@
 	 * @return	void
 	 */
 	function printContent()	{
-			// If we handle an AJAX call, send headers:
-		if ($this->ajax) {
-			header('X-JSON: ('.($this->pagetree->ajaxStatus?'true':'false').')');
-			header('Content-type: text/html; charset=utf-8');
-			// If it's the regular call to fully output the tree:
-		} else {
-			$this->content.= $this->doc->endPage();
-			$this->content = $this->doc->insertStylesAndJS($this->content);
-		}
+		$this->content.= $this->doc->endPage();
+		$this->content = $this->doc->insertStylesAndJS($this->content);
 		echo $this->content;
 	}
 
@@ -330,10 +329,35 @@
 	 * @return	void
 	 */
 	function settingTemporaryMountPoint($pageId)	{
-		global $BE_USER;
+		$GLOBALS['BE_USER']->setAndSaveSessionData('pageTree_temporaryMountPoint',intval($pageId));
+	}
 
-			// Setting temporary mount point ID:
-		$BE_USER->setAndSaveSessionData('pageTree_temporaryMountPoint',intval($pageId));
+
+	/**********************************
+	 *
+	 * AJAX Calls
+	 *
+	 **********************************/
+
+	/**
+	 * Makes the AJAX call to expand or collapse the pagetree.
+	 * Called by typo3/ajax.php
+	 * 
+	 * @param	array	additional parameters (not used here)
+	 * @param	object	the TYPO3AJAX object of this request
+	 */
+	public function ajaxExpandCollapse($params, &$ajaxObj)	{
+		global $LANG;
+
+		$this->init();
+		$this->initPage();
+		$tree = $this->pagetree->getBrowsableTree();
+		$content = $LANG->csConvObj->utf8_encode($tree, $LANG->charSet);
+		if (!$this->pagetree->ajaxStatus)	{
+			$ajaxObj->setError($content);
+		} else	{
+			$ajaxObj->setContent($content);
+		}
 	}
 }
 
@@ -344,13 +368,13 @@
 }
 
 
+// Make instance if it is not an AJAX call
+if (!TYPO3_AJAX) {
+	$SOBE = t3lib_div::makeInstance('SC_alt_db_navframe');
+	$SOBE->init();
+	$SOBE->initPage();
+	$SOBE->main();
+	$SOBE->printContent();
+}
 
-
-
-// Make instance:
-$SOBE = t3lib_div::makeInstance('SC_alt_db_navframe');
-$SOBE->init();
-$SOBE->main();
-$SOBE->printContent();
-
 ?>
Index: typo3/ajax.php
===================================================================
--- typo3/ajax.php	(revision 0)
+++ typo3/ajax.php	(revision 0)
@@ -0,0 +1,68 @@
+<?php
+/***************************************************************
+*  Copyright notice
+*
+*  (c) 2007-2008 Benjamin Mack
+*  All rights reserved
+*
+*  This script is part of the TYPO3 project. The TYPO3 project is
+*  free software; you can redistribute it and/or modify
+*  it under the terms of the GNU General Public License as published by
+*  the Free Software Foundation; either version 2 of the License, or
+*  (at your option) any later version.
+*
+*  The GNU General Public License can be found at
+*  http://www.gnu.org/copyleft/gpl.html.
+*  A copy is found in the textfile GPL.txt and important notices to the license
+*  from the author is found in LICENSE.txt distributed with these scripts.
+*
+*
+*  This script is distributed in the hope that it will be useful,
+*  but WITHOUT ANY WARRANTY; without even the implied warranty of
+*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+*  GNU General Public License for more details.
+*
+*  This copyright notice MUST APPEAR in all copies of the script!
+***************************************************************/
+/**
+ * AJAX dispatcher
+ * @author	Benjamin Mack <mack@xnos.org>
+ * @package	TYPO3
+ */
+
+define('TYPO3_AJAX', true);
+
+require('init.php');
+require('classes/class.typo3ajax.php');
+
+
+	// setting the charset
+header('Content-type: text/html; charset='.($TYPO3_CONF_VARS['BE']['forceCharset'] ? $TYPO3_CONF_VARS['BE']['forceCharset'] : 'utf-8'));
+
+	// finding the script path from the variable
+$ajaxID = (string) t3lib_div::_GP('ajaxID');
+$ajaxScript = $TYPO3_CONF_VARS['BE']['AJAX'][$ajaxID];
+
+
+	// instantiating the AJAX object
+$ajaxObj =& t3lib_div::makeInstance('TYPO3AJAX');
+$ajaxParams = array();
+
+
+	// evaluating the arguments and calling the AJAX method/function
+if (empty($ajaxID))	{
+	$ajaxObj->setError('No valid ajaxID parameter given.');
+} else if (empty($ajaxScript))	{
+	$ajaxObj->setError('Registered backend function for ajaxID "'.$ajaxID.'" was not found.');
+} else {
+	$ajaxObj->setID($ajaxID);
+	$ret = t3lib_div::callUserFunction($ajaxScript, $ajaxParams, $ajaxObj, false, true);
+	if ($ret === false)	{
+		$ajaxObj->setError('Registered backend function for ajaxID "'.$ajaxID.'" was not found.');
+	}
+}
+
+	// outputting the content (and setting the X-JSON-Header)
+$ajaxObj->output();
+
+?>
Index: typo3/init.php
===================================================================
--- typo3/init.php	(revision 2868)
+++ typo3/init.php	(working copy)
@@ -343,7 +343,14 @@
 	*/
 }
 
+// *****************************
+// Check if it is an AJAX call
+// *****************************
+if (!defined('TYPO3_AJAX'))	{
+	define('TYPO3_AJAX', false);
+}
 
+
 // *************************
 // Connect to the database
 // *************************
