+2005-11-09 Sebastian Kurfuerst <sebastian@garbage-group.de>
+
+ * New feature #1577: display clickmenus with XMLHTTP / ajax
+ * Fixed bug 1066: Shortcut to a invisible page leads to a frontend error
+
2005-11-08 Sebastian Kurfuerst <sebastian@garbage-group.de>
* Fixed bug #586: User Admin can't create new user
--- /dev/null
+<?php
+/***************************************************************
+* Copyright notice
+*
+* (c) 2004-2005 Kasper Skaarhoj (kasperYYYY@typo3.com)
+* 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!
+***************************************************************/
+/**
+ * Contains the class "t3lib_ajax" containing functions for doing XMLHTTP requests to the TYPO3 backend and as well for generating replys. This technology is also known as ajax.
+ * Call ALL methods without making an object!
+ *
+ * @author Sebastian Kurfuerst <sebastian@garbage-group.de>
+ */
+
+/**
+ * TYPO3 XMLHTTP class (new in TYPO3 4.0.0)
+ * This class contains two main parts:
+ * (1) generation of JavaScript code which creates an XMLHTTP object in a cross-browser manner
+ * (2) generation of XML data as a reply
+ *
+ * @author Sebastian Kurfuerst <sebastian@garbage-group.de>
+ * @package TYPO3
+ * @subpackage t3lib
+ */
+class t3lib_ajax {
+ /**
+ * gets javascript code needed to handle an XMLHTTP request in the frontend.
+ * all JS functions have to call ajax_doRequest(url) to make a request to the server.
+ * USE:
+ * see examples of using this function in template.php -> getContextMenuCode and alt_clickmenu.php -> printContent
+ *
+ * @param string $handlerFunction JS function handling the XML data from the server. That function gets the returned XML data as parameter.
+ * @param string $fallback JS fallback function which is called with the URL of the request in case ajax is not available.
+ * @param boolean $debug If set to 1, the returned XML data is outputted as text in an alert window - useful for debugging, PHP errors are shown there, ...
+ * @return string JavaScript code needed to make and handle an XMLHTTP request
+ */
+ function getJScode($handlerFunction, $fallback = '', $debug=0) {
+ // init xmlhttp request object
+ $code = '
+ function ajax_initObject() {
+ var A;
+ try {
+ A=new ActiveXObject("Msxml2.XMLHTTP");
+ } catch (e) {
+ try {
+ A=new ActiveXObject("Microsoft.XMLHTTP");
+ } catch (oc) {
+ A=null;
+ }
+ }
+ if(!A && typeof XMLHttpRequest != "undefined") {
+ A = new XMLHttpRequest();
+ }
+ return A;
+ }';
+ // in case ajax is not available, fallback function
+ if($fallback) {
+ $fallback .= '(url)';
+ } else {
+ $fallback = 'return';
+ }
+ $code .= '
+ function ajax_doRequest(url) {
+ var x;
+
+ x = ajax_initObject();
+ if(!x) {
+ '.$fallback.';
+ }
+ x.open("GET", url, true);
+
+ x.onreadystatechange = function() {
+ if (x.readyState != 4) {
+ return;
+ }
+ '.($debug?'alert(x.responseText)':'').'
+ var xmldoc = x.responseXML;
+ var t3ajax = xmldoc.getElementsByTagName("t3ajax")[0];
+ '.$handlerFunction.'(t3ajax);
+ }
+ x.send("");
+
+ delete x;
+ }';
+
+ return $code;
+ }
+
+ /**
+ * Function outputting XML data for TYPO3 ajax. The function directly outputs headers and content to the browser.
+ *
+ * @param string $innerXML XML data which will be sent to the browser
+ * @return void
+ */
+ function outputXMLreply($innerXML) {
+ // ajax needs xml data
+ header('Content-Type: text/xml');
+ $xml = '<?xml version="1.0"?>
+<t3ajax>'.$innerXML.'</t3ajax>';
+ echo $xml;
+ }
+
+}
+
+
+if (defined('TYPO3_MODE') && $TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['t3lib/class.t3lib_ajax.php']) {
+ include_once($TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['t3lib/class.t3lib_ajax.php']);
+}
+?>
} else unset($row); // If the mount point could not be fetched with respect to enableFields, unset the row so it does not become a part of the menu!
}
+ // if shortcut, look up if the target exists and is currently visible
+ if ($row['doktype'] == 4 && ($row['shortcut'] || $row['shortcut_mode'])) {
+ if ($row['shortcut_mode'] == 0) {
+ $searchField = 'uid';
+ $searchUid = intval($row['shortcut']);
+ } else { // check subpages - first subpage or random subpage
+ $searchField = 'pid';
+ // If a shortcut mode is set and no valid page is given to select subpags from use the actual page.
+ $searchUid = intval($row['shortcut'])?intval($row['shortcut']):$row['uid'];
+ }
+ $res2 = $GLOBALS['TYPO3_DB']->exec_SELECTquery('uid', 'pages', $searchField.'='.$searchUid.$this->where_hid_del.$this->where_groupAccess.' '.$addWhere, '', $sortField);
+ if (!$GLOBALS['TYPO3_DB']->sql_num_rows($res2)) {
+ unset($row);
+ }
+ $GLOBALS['TYPO3_DB']->sql_free_result($res2);
+ } elseif ($row['doktype'] == 4) {
+ // Neither shortcut target nor mode is set. Remove the page from the menu.
+ unset($row);
+ }
+
// Add to output array after overlaying language:
if (is_array($row)) {
$output[$origUid] = $this->getPageOverlay($row);
require ('init.php');
require ('template.php');
require_once (PATH_t3lib.'class.t3lib_clipboard.php');
+require_once(PATH_t3lib.'class.t3lib_ajax.php');
$LANG->includeLLFile('EXT:lang/locallang_misc.xml');
var $dontDisplayTopFrameCM=0; // If true, the context sensitive menu will not appear in the top frame, only as a layer.
var $leftIcons=0; // If true, Show icons on the left.
var $extClassArray=array(); // Array of classes to be used for user processing of the menu content. This is for the API of adding items to the menu from outside.
+ var $ajax=0; // enable/disable ajax behavior
// Internal, dynamic:
var $elCount=0; // Counter for elements in the menu. Used to number the name / id of the mouse-over icon.
/**
* Initialize click menu
*
- * @param string Input "item" GET var.
* @return string The clickmenu HTML content
*/
- function init($item) {
-
+ function init() {
// Setting GPvars:
$this->cmLevel = intval(t3lib_div::_GP('cmLevel'));
$this->CB = t3lib_div::_GP('CB');
+ if(t3lib_div::_GP('ajax')) {
+ $this->ajax = 1;
+ ini_set('display_errors',0); // XML has to be parsed, no parse errors allowed
+ }
-
- // Explode the incoming command:
- $this->iParts = explode('|',$item);
+ // can be set differently as well
+ $this->iParts[0] = t3lib_div::_GP('table');
+ $this->iParts[1] = t3lib_div::_GP('uid');
+ $this->iParts[2] = t3lib_div::_GP('listFr');
+ $this->iParts[3] = t3lib_div::_GP('enDisItems');
// Setting flags:
if ($this->iParts[2]) $this->listFrame=1;
}
}
- // Return clickmenu conten:
+ // Return clickmenu content:
return $CMcontent;
}
* @return boolean
*/
function doDisplayTopFrameCM() {
- return !$GLOBALS['SOBE']->doc->isCMlayers() || !$this->dontDisplayTopFrameCM;
+ if($this->ajax) {
+ return false;
+ } else {
+ return !$GLOBALS['SOBE']->doc->isCMlayers() || !$this->dontDisplayTopFrameCM;
+ }
}
// Set back path place holder to real back path
$CMtable = str_replace($this->PH_backPath,$this->backPath,$CMtable);
-
+ if($this->ajax) {
+ $innerXML = '<data><clickmenu><htmltable><![CDATA['.$CMtable.']]></htmltable><cmlevel>'.$this->cmLevel.'</cmlevel></clickmenu></data>';
+ return $innerXML;
+ } else {
// Create JavaScript section:
$script=$GLOBALS['TBE_TEMPLATE']->wrapScriptTags('
-if (top.content && top.content'.$frameName.' && top.content'.$frameName.'.setLayerObj) {
- top.content'.$frameName.'.setLayerObj(unescape("'.t3lib_div::rawurlencodeJS($CMtable).'"),'.$this->cmLevel.');
-}
-'.(!$this->doDisplayTopFrameCM()?'hideCM();':'')
-);
+ if (top.content && top.content'.$frameName.' && top.content'.$frameName.'.setLayerObj) {
+ top.content'.$frameName.'.setLayerObj(unescape("'.t3lib_div::rawurlencodeJS($CMtable).'"),'.$this->cmLevel.');
+ }
+ '.(!$this->doDisplayTopFrameCM()?'hideCM();':'')
+ );
+ return $script;
+ }
}
-
- return $script;
}
/**
*/
function linkItem($str,$icon,$onClick,$onlyCM=0,$dontHide=0) {
$this->elCount++;
+ if($this->ajax) {
+ $onClick = str_replace('top.loadTopMenu', 'showClickmenu_raw', $onClick);
+ }
$WHattribs = t3lib_iconWorks::skinImg($BACK_PATH,'gfx/content_client.gif','width="7" height="10"',2);
* @return boolean
*/
function isCMlayers() {
- return $GLOBALS['SOBE']->doc->isCMlayers() && !$this->CB;
+ if($this->ajax) {
+ return !$this->CB;
+ } else {
+ return $GLOBALS['SOBE']->doc->isCMlayers() && !$this->CB;
+ }
}
/**
}
// Initialize template object
- $this->doc = t3lib_div::makeInstance('template');
- $this->doc->docType='xhtml_trans';
- $this->doc->backPath = $BACK_PATH;
+ if(!$this->ajax) {
+ $this->doc = t3lib_div::makeInstance('template');
+ $this->doc->docType='xhtml_trans';
+ $this->doc->backPath = $BACK_PATH;
+ }
// Setting mode for display and background image in the top frame
$this->dontDisplayTopFrameCM= $this->doc->isCMlayers() && !$BE_USER->getTSConfigVal('options.contextMenu.options.alwaysShowClickMenuInTopFrame');
*/
function main() {
+ $this->ajax = t3lib_div::_GP('ajax')?TRUE:FALSE;
+
// Initialize Clipboard object:
$clipObj = t3lib_div::makeInstance('t3lib_clipboard');
$clipObj->initializeClipboard();
$clickMenu->backPath = $this->backPath;
// Start page
- $this->content.=$this->doc->startPage('Context Sensitive Menu');
-
+ if(!$this->ajax) {
+ $this->content.=$this->doc->startPage('Context Sensitive Menu');
+ }
// Set content of the clickmenu with the incoming var, "item"
- $this->content.= $clickMenu->init($this->item);
+ $this->content.= $clickMenu->init();
}
/**
* @return void
*/
function printContent() {
- $this->content.= $this->doc->endPage();
- echo $this->content;
+ if(!$this->ajax) {
+ $this->content.= $this->doc->endPage();
+ echo $this->content;
+ } else {
+ t3lib_ajax::outputXMLreply($this->content);
+ }
}
}
if (!defined('TYPO3_MODE')) die("Can't include this file directly.");
-
+require_once(PATH_t3lib.'class.t3lib_ajax.php');
* @return string The link-wrapped input string.
*/
function wrapClickMenuOnIcon($str,$table,$uid='',$listFr=1,$addParams='',$enDisItems='', $returnOnClick=FALSE) {
- $backPath = '&backPath='.rawurlencode($this->backPath).'|'.t3lib_div::shortMD5($this->backPath.'|'.$GLOBALS['TYPO3_CONF_VARS']['SYS']['encryptionKey']);
- $onClick = 'top.loadTopMenu(\''.$this->backPath.'alt_clickmenu.php?item='.rawurlencode($table.'|'.$uid.'|'.$listFr.'|'.$enDisItems).$backPath.$addParams.'\');'.$this->thisBlur().'return false;';
+ $backPath = rawurlencode($this->backPath).'|'.t3lib_div::shortMD5($this->backPath.'|'.$GLOBALS['TYPO3_CONF_VARS']['SYS']['encryptionKey']);
+ $onClick = 'showClickmenu("'.$table.'","'.$uid.'","'.$listFr.'","'.$enDisItems.'","'.str_replace('&','&',addcslashes($backPath,'"')).'","'.str_replace('&','&',addcslashes($addParams,'"')).'");return false;';
return $returnOnClick ? $onClick : '<a href="#" onclick="'.htmlspecialchars($onClick).'"'.($GLOBALS['TYPO3_CONF_VARS']['BE']['useOnContextMenuHandler'] ? ' oncontextmenu="'.htmlspecialchars($onClick).'"' : '').'>'.$str.'</a>';
}
* @return array If values are present: [0] = A <script> section for the HTML page header, [1] = onmousemove/onload handler for HTML tag or alike, [2] = Two empty <div> layers for the context menu
*/
function getContextMenuCode() {
- if ($this->isCMlayers()) {
- $content='
+ $content = '
<script type="text/javascript">
/*<![CDATA[*/
+ // is called from most clickmenu links
+ function showClickmenu(table, uid, listFr, enDisItems, backPath, addParams) {
+ var url = "'.$this->backPath.'alt_clickmenu.php?table=" + table
+ + "&uid=" + uid
+ + "&listFr=" + listFr
+ + "&enDisItems=" + enDisItems
+ + "&backPath=" + backPath
+ + "&addParams=" + addParams;
+
+ showClickmenu_raw(url);
+ }
+ // switch - either forwards call to ajax or does the request in the top frame
+ function showClickmenu_raw(url) {';
+ if($this->isCMlayers()) { // ajax
+ $content .= '
+ url += "&ajax=1";
+ ajax_doRequest(url);';
+ } else { // no ajax
+ $content .= '
+ showClickmenu_noajax(url);';
+ }
+ $content .= '
+ }
+ function showClickmenu_noajax(url) {
+ top.loadTopMenu(url);
+ }';
+ if ($this->isCMlayers()) {
+ $content .= t3lib_ajax::getJScode('showClickmenu_ajax', 'showClickmenu_noajax');
+ $content.='
+ // opens the clickmenu, is called from ajax_doRequest
+ function showClickmenu_ajax(t3ajax) {
+ var clickmenu = t3ajax.getElementsByTagName("data")[0].getElementsByTagName("clickmenu")[0];
+ var tableData = clickmenu.getElementsByTagName("htmltable")[0].firstChild.data;
+ var cmlevel = clickmenu.getElementsByTagName("cmlevel")[0].firstChild.data;
+ setLayerObj(tableData,cmlevel);
+ }
+
var GLV_gap=10;
var GLV_curLayerX=new Array(0,0);
var GLV_curLayerY=new Array(0,0);
}
}
}
- }
- /*]]>*/
- </script>
- ';
+ }';
+ $content.=' /*]]>*/
+ </script>';
return array(
$content,
' onmousemove="GL_getMouse(event);" onload="initLayer();"',
'<div id="contentMenu0" style="z-index:1; position:absolute;visibility:hidden"></div><div id="contentMenu1" style="z-index:2; position:absolute;visibility:hidden"></div>'
);
- } else return array('','','');
+ } else {
+ $content.=' /*]]>*/
+ </script>';
+ return array($content,'','');
+ }
}
/**