Added feature #13313: ExtDirect API (thanks to Stefan Galinski and the T3UXW09 team)
authorSteffen Kamper <info@sk-typo3.de>
Mon, 25 Jan 2010 22:58:29 +0000 (22:58 +0000)
committerSteffen Kamper <info@sk-typo3.de>
Mon, 25 Jan 2010 22:58:29 +0000 (22:58 +0000)
git-svn-id: https://svn.typo3.org/TYPO3v4/Core/trunk@6818 709f56b5-9817-0410-a4d7-c38de5d9e867

ChangeLog
t3lib/class.t3lib_pagerenderer.php
t3lib/config_default.php
t3lib/extjs/class.t3lib_extjs_extdirectapi.php [new file with mode: 0644]
t3lib/extjs/class.t3lib_extjs_extdirectrouter.php [new file with mode: 0644]
typo3/backend.php
typo3/classes/class.typo3ajax.php

index 6ddadb7..24d830a 100755 (executable)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,7 @@
+2010-01-25  Steffen Kamper  <info@sk-typo3.de>
+
+       * Added feature #13313: ExtDirect API (thanks to Stefan Galinski and the T3UXW09 team)
+
 2010-01-24  Steffen Kamper  <info@sk-typo3.de>
 
        * Fixed bug #13090: 4.4.0 trunk: version warning with CSS styled content
index 034e8a4..6d8d7d4 100644 (file)
@@ -687,12 +687,17 @@ class t3lib_PageRenderer implements t3lib_Singleton {
        /**
         * Adds Ext.onready code, which will be wrapped in Ext.onReady(function() {...});
         *
-        * @param string $block
+        * @param string $block javascript code
+        * @param boolean $forceOnTop position of the javascript code (TRUE for putting it on top, default is FALSE = bottom)
         * @return void
         */
-       public function addExtOnReadyCode($block) {
+       public function addExtOnReadyCode($block, $forceOnTop = FALSE) {
                if (!in_array($block, $this->extOnReadyCode)) {
-                       $this->extOnReadyCode[] = $block;
+                       if ($forceOnTop) {
+                               array_unshift($this->extOnReadyCode, $block);
+                       } else {
+                               $this->extOnReadyCode[] = $block;
+                       }
                }
        }
 
index 3cc6fca..1376a90 100644 (file)
@@ -279,8 +279,11 @@ $TYPO3_CONF_VARS = array(
                        'BackendLogin::isTimedOut'                      => 'typo3/classes/class.ajaxlogin.php:AjaxLogin->isTimedOut',
                        'BackendLogin::getChallenge'            => 'typo3/classes/class.ajaxlogin.php:AjaxLogin->getChallenge',
                        'WorkspaceMenu::toggleWorkspacePreview' => 'typo3/classes/class.workspaceselector.php:WorkspaceSelector->toggleWorkspacePreview',
-                       'WorkspaceMenu::setWorkspace'           => 'typo3/classes/class.workspaceselector.php:WorkspaceSelector->setWorkspace'
+                       'WorkspaceMenu::setWorkspace'           => 'typo3/classes/class.workspaceselector.php:WorkspaceSelector->setWorkspace',
+                       'ExtDirect::getAPI' => 't3lib/extjs/class.t3lib_extjs_extdirectapi.php:t3lib_ExtJs_ExtDirectAPI->getAPI',
+                       'ExtDirect::route' => 't3lib/extjs/class.t3lib_extjs_extdirectrouter.php:t3lib_ExtJs_ExtDirectRouter->route',
                ),
+               'ExtDirect' => array(),                                 // array of key value pairs (provider -> location:className) that holds the classes for the ExtDirect functionality
                'XCLASS' => array(),                                    // See 'Inside TYPO3' document for more information.
        ),
        'FE' => array(                  // Configuration for the TypoScript frontend (FE). Nothing here relates to the administration backend!
diff --git a/t3lib/extjs/class.t3lib_extjs_extdirectapi.php b/t3lib/extjs/class.t3lib_extjs_extdirectapi.php
new file mode 100644 (file)
index 0000000..30232c8
--- /dev/null
@@ -0,0 +1,105 @@
+<?php\r
+/***************************************************************\r
+*  Copyright notice\r
+*\r
+*  (c) 2010 Sebastian Kurfuerst <sebastian@typo3.org>\r
+*  All rights reserved\r
+*\r
+*  This script is part of the TYPO3 project. The TYPO3 project is\r
+*  free software; you can redistribute it and/or modify\r
+*  it under the terms of the GNU General Public License as published by\r
+*  the Free Software Foundation; either version 2 of the License, or\r
+*  (at your option) any later version.\r
+*\r
+*  The GNU General Public License can be found at\r
+*  http://www.gnu.org/copyleft/gpl.html.\r
+*  A copy is found in the textfile GPL.txt and important notices to the license\r
+*  from the author is found in LICENSE.txt distributed with these scripts.\r
+*\r
+*\r
+*  This script is distributed in the hope that it will be useful,\r
+*  but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+*  GNU General Public License for more details.\r
+*\r
+*  This copyright notice MUST APPEAR in all copies of the script!\r
+***************************************************************/\r
+\r
+/**\r
+ * Ext Direct API Generator\r
+ *\r
+ * @author     Sebastian Kurfuerst <sebastian@typo3.org>\r
+ * @author     Stefan Galinski <stefan.galinski@gmail.com>\r
+ * @package    TYPO3\r
+ */\r
+class t3lib_ExtJs_ExtDirectAPI {\r
+       /**\r
+        * Parses the ExtDirect configuration array "$GLOBALS['TYPO3_CONF_VARS']['BE']['ExtDirect']"\r
+        * and feeds the given typo3ajax instance with the resulting informations. The get parameter\r
+        * "namespace" will be used to filter the configuration.\r
+        *\r
+        * This method makes usage of the reflection mechanism to fetch the methods inside the\r
+        * defined classes together with their amount of parameters. This informations are building\r
+        * the API and are required by ExtDirect.\r
+        *\r
+        * @param array $ajaxParams ajax parameters\r
+        * @param TYPO3AJAX $ajaxObj typo3ajax instance\r
+        * @return void\r
+        */\r
+       public function getAPI($ajaxParams, TYPO3AJAX $ajaxObj) {\r
+               $filterNamespace = t3lib_div::_GET('namespace');\r
+\r
+               $javascriptNamespaces = array();\r
+               if (is_array($GLOBALS['TYPO3_CONF_VARS']['BE']['ExtDirect'])) {\r
+                       foreach ($GLOBALS['TYPO3_CONF_VARS']['BE']['ExtDirect'] as $javascriptName => $className) {\r
+                               $splittedJavascriptName = explode('.', $javascriptName);\r
+                               $javascriptObjectName = array_pop($splittedJavascriptName);\r
+                               $javascriptNamespace = implode('.', $splittedJavascriptName);\r
+\r
+                               // only items inside the wanted namespace\r
+                               if (strpos($javascriptNamespace, $filterNamespace) !== 0) {\r
+                                       continue;\r
+                               }\r
+\r
+                               if (!isset($javascriptNamespaces[$javascriptNamespace])) {\r
+                                       $javascriptNamespaces[$javascriptNamespace] = array(\r
+                                           'url' => 'ajax.php?ajaxID=ExtDirect::route&namespace=' . rawurlencode($javascriptNamespace),\r
+                                           'type' => 'remoting',\r
+                                           'actions' => array(),\r
+                                           'namespace' => $javascriptNamespace\r
+                                       );\r
+                               }\r
+\r
+                               $serverObject = t3lib_div::getUserObj($className, FALSE);\r
+                               $javascriptNamespaces[$javascriptNamespace]['actions'][$javascriptObjectName] = array();\r
+                               foreach (get_class_methods($serverObject) as $methodName) {\r
+                                       $reflectionMethod = new ReflectionMethod($serverObject, $methodName);\r
+                                       $numberOfParameters = $reflectionMethod->getNumberOfParameters();\r
+\r
+                                       $javascriptNamespaces[$javascriptNamespace]['actions'][$javascriptObjectName][] = array(\r
+                                               'name' => $methodName,\r
+                                               'len' => $numberOfParameters\r
+                                       );      \r
+                               }\r
+                       }\r
+               }\r
+\r
+               if (count($javascriptNamespaces)) {\r
+                       $setup = '\r
+                               if (typeof Ext.app.ExtDirectAPI != "object") {\r
+                                       Ext.app.ExtDirectAPI = {};\r
+                               }\r
+                       ';\r
+\r
+                       $ajaxObj->setContent($javascriptNamespaces);\r
+                       $ajaxObj->setContentFormat('javascript');\r
+                       $ajaxObj->setJavascriptCallbackWrap($setup . 'Ext.app.ExtDirectAPI = Object.extend(Ext.app.ExtDirectAPI, |);');\r
+               }\r
+       }\r
+}\r
+\r
+if (defined('TYPO3_MODE') && $TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['t3lib/class.t3lib_extjs_extdirectapi.php'])       {\r
+       include_once($TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['t3lib/class.t3lib_extjs_extdirectapi.php']);\r
+}\r
+\r
+?>\r
diff --git a/t3lib/extjs/class.t3lib_extjs_extdirectrouter.php b/t3lib/extjs/class.t3lib_extjs_extdirectrouter.php
new file mode 100644 (file)
index 0000000..cb25f76
--- /dev/null
@@ -0,0 +1,131 @@
+<?php\r
+/***************************************************************\r
+*  Copyright notice\r
+*\r
+*  (c) 2010 Sebastian Kurfuerst <sebastian@typo3.org>\r
+*  All rights reserved\r
+*\r
+*  This script is part of the TYPO3 project. The TYPO3 project is\r
+*  free software; you can redistribute it and/or modify\r
+*  it under the terms of the GNU General Public License as published by\r
+*  the Free Software Foundation; either version 2 of the License, or\r
+*  (at your option) any later version.\r
+*\r
+*  The GNU General Public License can be found at\r
+*  http://www.gnu.org/copyleft/gpl.html.\r
+*  A copy is found in the textfile GPL.txt and important notices to the license\r
+*  from the author is found in LICENSE.txt distributed with these scripts.\r
+*\r
+*\r
+*  This script is distributed in the hope that it will be useful,\r
+*  but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+*  GNU General Public License for more details.\r
+*\r
+*  This copyright notice MUST APPEAR in all copies of the script!\r
+***************************************************************/\r
+\r
+/**\r
+ * Ext Direct Router\r
+ *\r
+ * @author     Sebastian Kurfuerst <sebastian@typo3.org>\r
+ * @author     Stefan Galinski <stefan.galinski@gmail.com>\r
+ * @package    TYPO3\r
+ */\r
+class t3lib_ExtJs_ExtDirectRouter {\r
+       /**\r
+        * Dispatches the incoming calls to methods about the ExtDirect API.\r
+        *\r
+        * @param aray $ajaxParams ajax parameters\r
+        * @param TYPO3AJAX $ajaxObj typo3ajax instance\r
+        * @return void\r
+        */\r
+       public function route($ajaxParams, TYPO3AJAX $ajaxObj) {\r
+               try {\r
+                       $isForm = false;\r
+                       $isUpload = false;\r
+                       $rawPostData = file_get_contents('php://input');\r
+                       $namespace = t3lib_div::_GET('namespace');\r
+\r
+                       if (!empty($rawPostData)) {\r
+                               $ajaxObj->setContentFormat('jsonbody');\r
+                               $request = json_decode($rawPostData);\r
+                       } else {\r
+                               throw new t3lib_error_Exception('ExtDirect: Missing Parameters!');\r
+                       }\r
+\r
+                       $response = null;\r
+                       if (is_array($request)) {\r
+                               $response = array();\r
+                               foreach ($request as $singleRequest) {\r
+                                       $response[] = $this->processRpc($singleRequest, $namespace);\r
+                               }\r
+                       } else {\r
+                               $response = $this->processRpc($request, $namespace);\r
+                       }\r
+\r
+               } catch (t3lib_error_Exception $exception) {\r
+                       $response = array(\r
+                               'type' => 'exception',\r
+                               'message' => $exception->getMessage(),\r
+                               'where' => $exception->getTraceAsString()\r
+                       );\r
+               }\r
+\r
+               $ajaxObj->setContent($response);\r
+       }\r
+\r
+\r
+       /**\r
+        * Processes an incoming extDirect call by executing the defined method. The configuration\r
+        * array "$GLOBALS['TYPO3_CONF_VARS']['BE']['ExtDirect']" is taken to find the class/method\r
+        * information.\r
+        *\r
+        * @param object $singleRequest request object from extJS\r
+        * @param string $namespace namespace like TYPO3.Backend\r
+        * @return mixed return value of the called method\r
+        */\r
+       protected function processRpc($singleRequest, $namespace) {\r
+               try {\r
+                       $endpointName = $namespace . '.' . $singleRequest->action;\r
+                       \r
+                       // theoretically this can never happen, because of an javascript error on\r
+                       // the client side due the missing namespace/endpoint\r
+                       if (!isset($GLOBALS['TYPO3_CONF_VARS']['BE']['ExtDirect'][$endpointName])) {\r
+                               throw new t3lib_error_Exception('ExtDirect: Call to undefined endpoint: ' . $endpointName);\r
+                       }\r
+\r
+                       $response = array(\r
+                               'type' => 'rpc',\r
+                               'tid' => $singleRequest->tid,\r
+                               'action' => $singleRequest->action,\r
+                               'method' => $singleRequest->method\r
+                       );\r
+\r
+                       $endpointObject = t3lib_div::getUserObj(\r
+                               $GLOBALS['TYPO3_CONF_VARS']['BE']['ExtDirect'][$endpointName],\r
+                               FALSE\r
+                       );\r
+                       \r
+                       $response['result'] = call_user_func_array(\r
+                               array($endpointObject, $singleRequest->method),\r
+                               is_array($singleRequest->data) ? $singleRequest->data : array()\r
+                       );\r
+\r
+               } catch (t3lib_error_Exception $exception) {\r
+                       $response = array(\r
+                               'type' => 'exception',\r
+                               'message' => $exception->getMessage(),\r
+                               'where' => $exception->getTraceAsString()\r
+                       );\r
+               }\r
+\r
+               return $response;\r
+       }\r
+}\r
+\r
+if (defined('TYPO3_MODE') && $TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['t3lib/class.t3lib_extjs_extdirectrouter.php'])    {\r
+       include_once($TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['t3lib/class.t3lib_extjs_extdirectrouter.php']);\r
+}\r
+\r
+?>\r
index 3f846b2..56e5f4f 100644 (file)
@@ -102,11 +102,13 @@ class TYPO3backend {
                        'js/modulemenu.js',
                        'js/iecompatibility.js',
                        'js/flashupload.js',
-                       '../t3lib/jsfunc.evalfield.js'
+                       '../t3lib/jsfunc.evalfield.js',
+                       'ajax.php?ajaxID=ExtDirect::getAPI&namespace=TYPO3.Backend'
                );
+
                $this->jsFilesAfterInline = array(
                        'js/backend.js',
-                               'js/loginrefresh.js',
+                       'js/loginrefresh.js',
                );
                        // add default BE css
                $this->css      = '';
@@ -210,6 +212,16 @@ class TYPO3backend {
                $pageRenderer->loadScriptaculous('builder,effects,controls,dragdrop');
                $pageRenderer->loadExtJS();
 
+                       // register the extDirect API providers
+                       // Note: we need to iterate thru the object, because the addProvider method
+                       // does this only with multiple arguments
+               $pageRenderer->addExtOnReadyCode(
+                       'for (var api in Ext.app.ExtDirectAPI) {
+                               Ext.Direct.addProvider(Ext.app.ExtDirectAPI[api]);
+                       }',
+                       TRUE
+               );
+
                        // remove duplicate entries
                $this->jsFiles = array_unique($this->jsFiles);
 
index d94eaae..955d50e 100644 (file)
@@ -41,6 +41,13 @@ class TYPO3AJAX {
        protected $contentFormat = 'plain';
        protected $charset       = 'utf-8';
        protected $requestCharset = 'utf-8';
+       protected $javascriptCallbackWrap = '
+               <script type="text/javascript">
+                       /*<![CDATA[*/
+                       response = |;
+                       /*]]>*/
+               </script>
+       ';
 
        /**
         * sets the charset and the ID for the AJAX call
@@ -138,7 +145,7 @@ class TYPO3AJAX {
        /**
         * sets the content format for the ajax call
         *
-        * @param       string          can be one of 'plain' (default), 'xml', 'json', 'jsonbody' or 'jsonhead'
+        * @param       string          can be one of 'plain' (default), 'xml', 'json', 'javascript', 'jsonbody' or 'jsonhead'
         * @return      void
         */
        public function setContentFormat($format) {
@@ -147,6 +154,17 @@ class TYPO3AJAX {
                }
        }
 
+       /**
+        * Specifies the wrap to be used if contentFormat is "javascript".
+        * The wrap used by default stores the results in a variable "response" and
+        * adds <script>-Tags around it.
+        *
+        * @param string $javascriptCallbackWrap the javascript callback wrap to be used
+        * @return void
+        */
+       public function setJavascriptCallbackWrap($javascriptCallbackWrap) {
+               $this->javascriptCallbackWrap = $javascriptCallbackWrap;
+       }
 
        /**
         * sets an error message and the error flag
@@ -282,13 +300,7 @@ class TYPO3AJAX {
                        $GLOBALS['LANG']->csConvObj->convArray($this->content, $this->charset, $this->requestCharset);
                }
 
-               $content = '<script type="text/javascript">
-                                       /*<![CDATA[*/
-
-                                       response = ' . json_encode($this->content) . ';
-
-                                       /*]]>*/
-                                       </script>';
+               $content = str_replace('|', json_encode($this->content), $this->javascriptCallbackWrap);
 
                header('Content-type: text/html; charset=' . $this->requestCharset);
                echo $content;