2005-11-09 Sebastian Kurfuerst <sebastian@garbage-group.de>
authorSebastian Kurfürst <sebastian.kurfuerst@typo3.org>
Wed, 9 Nov 2005 06:40:47 +0000 (06:40 +0000)
committerSebastian Kurfürst <sebastian.kurfuerst@typo3.org>
Wed, 9 Nov 2005 06:40:47 +0000 (06:40 +0000)
* New feature #1577: display clickmenus with XMLHTTP / ajax
* Fixed bug 1066: Shortcut to a invisible page leads to a
frontend error

git-svn-id: https://svn.typo3.org/TYPO3v4/Core/trunk@846 709f56b5-9817-0410-a4d7-c38de5d9e867

ChangeLog
t3lib/class.t3lib_ajax.php [new file with mode: 0644]
t3lib/class.t3lib_page.php
typo3/alt_clickmenu.php
typo3/template.php

index 8d7e833..c900a46 100755 (executable)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,8 @@
+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
diff --git a/t3lib/class.t3lib_ajax.php b/t3lib/class.t3lib_ajax.php
new file mode 100644 (file)
index 0000000..369299a
--- /dev/null
@@ -0,0 +1,128 @@
+<?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']);
+}
+?>
index ee60788..1af1da1 100755 (executable)
@@ -426,6 +426,26 @@ class t3lib_pageSelect {
                                        } 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);
index 0b1837f..f93eeb6 100755 (executable)
 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');
 
 
@@ -142,6 +143,7 @@ class clickMenu {
        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.
@@ -154,18 +156,22 @@ class clickMenu {
        /**
         * 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;
@@ -191,7 +197,7 @@ class clickMenu {
                        }
                }
 
-                       // Return clickmenu conten:
+                       // Return clickmenu content:
                return $CMcontent;
        }
 
@@ -201,7 +207,11 @@ class clickMenu {
         * @return      boolean
         */
        function doDisplayTopFrameCM()  {
-               return !$GLOBALS['SOBE']->doc->isCMlayers() || !$this->dontDisplayTopFrameCM;
+               if($this->ajax) {
+                       return false;
+               } else {
+                       return !$GLOBALS['SOBE']->doc->isCMlayers() || !$this->dontDisplayTopFrameCM;
+               }
        }
 
 
@@ -1029,18 +1039,21 @@ class clickMenu {
 
                                // 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;
        }
 
        /**
@@ -1206,6 +1219,9 @@ if (top.content && top.content'.$frameName.' && top.content'.$frameName.'.setLay
         */
        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);
 
@@ -1328,7 +1344,11 @@ if (top.content && top.content'.$frameName.' && top.content'.$frameName.'.setLay
         * @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;
+               }
        }
 
        /**
@@ -1411,9 +1431,11 @@ class SC_alt_clickmenu {
                }
 
                        // 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');
@@ -1481,6 +1503,8 @@ class SC_alt_clickmenu {
         */
        function main() {
 
+               $this->ajax = t3lib_div::_GP('ajax')?TRUE:FALSE;
+
                        // Initialize Clipboard object:
                $clipObj = t3lib_div::makeInstance('t3lib_clipboard');
                $clipObj->initializeClipboard();
@@ -1502,10 +1526,11 @@ class SC_alt_clickmenu {
                $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();
        }
 
        /**
@@ -1514,8 +1539,12 @@ class SC_alt_clickmenu {
         * @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);
+               }
        }
 }
 
index 9f4b2c3..aafb1cb 100755 (executable)
 
 if (!defined('TYPO3_MODE'))    die("Can't include this file directly.");
 
-
+require_once(PATH_t3lib.'class.t3lib_ajax.php');
 
 
 
@@ -288,8 +288,8 @@ class template {
         * @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('&','&amp;',addcslashes($backPath,'"')).'","'.str_replace('&','&amp;',addcslashes($addParams,'"')).'");return false;';
                return $returnOnClick ? $onClick : '<a href="#" onclick="'.htmlspecialchars($onClick).'"'.($GLOBALS['TYPO3_CONF_VARS']['BE']['useOnContextMenuHandler'] ? ' oncontextmenu="'.htmlspecialchars($onClick).'"' : '').'>'.$str.'</a>';
        }
 
@@ -1231,10 +1231,46 @@ $str.=$this->docBodyTagBegin().
         * @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);
@@ -1390,16 +1426,19 @@ $str.=$this->docBodyTagBegin().
                                                        }
                                                }
                                        }
-                               }
-                       /*]]>*/
-                       </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,'','');
+               }
        }
 
        /**