Fixed bug #16738: Add ExtDirect Stateprovider for ExtJs States
authorSteffen Kamper <info@sk-typo3.de>
Fri, 17 Dec 2010 18:05:46 +0000 (18:05 +0000)
committerSteffen Kamper <info@sk-typo3.de>
Fri, 17 Dec 2010 18:05:46 +0000 (18:05 +0000)
git-svn-id: https://svn.typo3.org/TYPO3v4/Core/trunk@9831 709f56b5-9817-0410-a4d7-c38de5d9e867

ChangeLog
t3lib/config_default.php
t3lib/extjs/dataprovider/class.extdirect_dataprovider_state.php [new file with mode: 0644]
t3lib/js/extjs/ExtDirect.StateProvider.js [new file with mode: 0644]
typo3/template.php

index 051635e..9fb10ea 100755 (executable)
--- a/ChangeLog
+++ b/ChangeLog
@@ -14,6 +14,7 @@
 
 2010-12-17  Steffen Kamper  <steffen@typo3.org>
 
+       * Fixed bug #16738: Add ExtDirect Stateprovider for ExtJs States
        * Fixed bug #16706: Use ExtJS for collapseable sections like in reports module
        * Fixed bug #15686: Call the list module through the dispatcher instead directly (Thanks to Helmut Hummel)
        * Fixed bug #16735: Inclusion of js/css for loadResourcesForRegisteredNavigationComponents lacks loading order
index 6246046..81b8802 100644 (file)
@@ -385,7 +385,8 @@ $TYPO3_CONF_VARS = array(
                'ExtDirect' => array(   // array of key value pairs (provider -> location:className) that holds the classes for the ExtDirect functionality
                        'TYPO3.CSH.ExtDirect' => 't3lib/extjs/dataprovider/class.extdirect_dataprovider_contexthelp.php:extDirect_DataProvider_ContextHelp',
                        'TYPO3.LiveSearchActions.ExtDirect' => 't3lib/extjs/dataprovider/class.extdirect_dataprovider_backendlivesearch.php:extDirect_DataProvider_BackendLiveSearch',
-                       'TYPO3.BackendUserSettings.ExtDirect' => 't3lib/extjs/dataprovider/class.extdirect_dataprovider_beusersettings.php:extDirect_DataProvider_BackendUserSettings'
+                       'TYPO3.BackendUserSettings.ExtDirect' => 't3lib/extjs/dataprovider/class.extdirect_dataprovider_beusersettings.php:extDirect_DataProvider_BackendUserSettings',
+                       'TYPO3.ExtDirectStateProvider.ExtDirect' => 't3lib/extjs/dataprovider/class.extdirect_dataprovider_state.php:extDirect_DataProvider_State',
                ),
        ),
        'EXTCONF' => array(             // Here you may add manually set configuration options for your extensions. Eg. $TYPO3_CONF_VARS['EXTCONF']['my_extension_key']['my_option'] = 'my_value';
diff --git a/t3lib/extjs/dataprovider/class.extdirect_dataprovider_state.php b/t3lib/extjs/dataprovider/class.extdirect_dataprovider_state.php
new file mode 100644 (file)
index 0000000..77bb5e6
--- /dev/null
@@ -0,0 +1,86 @@
+<?php
+/***************************************************************
+ *  Copyright notice
+ *
+ *  (c) 2010 Steffen Kamper <steffen@typo3.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!
+ ***************************************************************/
+
+/**
+ * ExtDirect DataProvider for State
+ */
+class extDirect_DataProvider_State {
+
+       /** @var extDirect_DataProvider_BackenduserSettings */
+       protected $userSettings;
+
+       /**
+        * Constructor
+        *
+        * @return void
+        */
+       public function __construct() {
+                       //all data is saved in BE_USER->uc
+               $this->userSettings = t3lib_div::makeInstance('extDirect_DataProvider_BackenduserSettings');
+       }
+
+       /**
+        * Gets state for given key
+        *
+        * @param  stdClass $parameter
+        * @return array
+        */
+       public function getState($parameter) {
+               $key = $parameter->params->key;
+               $data = $this->userSettings->get($key);
+
+               return array(
+                       'success' => TRUE,
+                       'data' =>  $data
+               );
+       }
+
+       /**
+        * Save the state for a given key
+        *
+        * @param  stdClass $parameter
+        * @return array
+        */
+       public function setState($parameter) {
+               $key = $parameter->params->key;
+               $data = json_decode($parameter->params->data);
+
+               $this->userSettings->set($key . '.' . $data[0]->name, $data[0]->value);
+               return array(
+                       'success' => TRUE,
+                       'params' => $parameter
+               );
+       }
+
+
+}
+
+if (defined('TYPO3_MODE') && isset($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['XCLASS']['t3lib/extjs/dataprovider/class.extdirect_dataprovider_state.php'])) {
+       include_once($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['XCLASS']['t3lib/extjs/dataprovider/class.extdirect_dataprovider_state.php']);
+}
+
+?>
\ No newline at end of file
diff --git a/t3lib/js/extjs/ExtDirect.StateProvider.js b/t3lib/js/extjs/ExtDirect.StateProvider.js
new file mode 100644 (file)
index 0000000..df4dd22
--- /dev/null
@@ -0,0 +1,383 @@
+/***************************************************************
+ *  Copyright notice
+ *
+ *  (c) 2010 Steffen Kamper <steffen@typo3.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!
+ ***************************************************************/
+
+Ext.ns('TYPO3.state');
+
+/**
+ * Creates new ExtDirectProvider
+ * @constructor
+ * @param {Object} config Configuration object
+ * @author Jozef Sakalos
+ * @author Steffen Kamper
+ */
+
+TYPO3.state.ExtDirectProvider = function(config) {
+
+       this.addEvents(
+               /**
+                * @event readsuccess
+                * Fires after state has been successfully received from server and restored
+                * @param {HttpProvider} this
+                */
+                       'readsuccess',
+               /**
+                * @event readfailure
+                * Fires in the case of an error when attempting to read state from server
+                * @param {HttpProvider} this
+                */
+                       'readfailure',
+               /**
+                * @event savesuccess
+                * Fires after the state has been successfully saved to server
+                * @param {HttpProvider} this
+                */
+                       'savesuccess',
+               /**
+                * @event savefailure
+                * Fires in the case of an error when attempting to save state to the server
+                * @param {HttpProvider} this
+                */
+                       'savefailure'
+                       );
+
+               // call parent
+       TYPO3.state.ExtDirectProvider.superclass.constructor.call(this);
+
+       Ext.apply(this, config, {
+               // defaults
+               delay: 750, // buffer changes for 750 ms
+               dirty: false,
+               started: false,
+               autoStart: true,
+               autoRead: true,
+               key: 'States.General',
+               logFailure: false,
+               logSuccess: false,
+               queue: [],
+               saveBaseParams: {},
+               readBaseParams: {},
+               paramNames:{
+                       key: 'key',
+                       name: 'name',
+                       value: 'value',
+                       data: 'data'
+               }
+       });
+
+       if (this.autoRead) {
+               this.readState();
+       }
+
+       this.dt = new Ext.util.DelayedTask(this.submitState, this);
+       if (this.autoStart) {
+               this.start();
+       }
+};
+
+
+Ext.extend(TYPO3.state.ExtDirectProvider, Ext.state.Provider, {
+
+               // localizable texts
+       saveSuccessText: 'Save Success',
+       saveFailureText: 'Save Failure',
+       readSuccessText: 'Read Success',
+       readFailureText: 'Read Failure',
+       dataErrorText: 'Data Error',
+
+
+
+       /**
+        * Initializes state from the passed state object or array.
+        * Use this with loading page using initial state in TYPO3.settings
+        *
+        * @param {Array/Object} state State to initialize state manager with
+        */
+       initState: function(state) {
+               if (Ext.isArray(state)) {
+                       Ext.each(state, function(item) {
+                               this.state[item.name] = this.decodeValue(item[this.paramNames.value]);
+                       }, this);
+               } else if (Ext.isObject(state)) {
+                       Ext.iterate(state, function(key, value){
+                               this.state[key] = this.decodeValue(value);
+                       }, this);
+               } else {
+                       this.state = {};
+               }
+       },
+
+       /**
+        * Sets the passed state variable name to the passed value and queues the change
+        * @param {String} name Name of the state variable
+        * @param {Mixed} value Value of the state variable
+        */
+       set: function(name, value) {
+               if (!name) {
+                       return;
+               }
+               this.queueChange(name, value);
+       },
+
+
+       /**
+        * Starts submitting state changes to server
+        */
+       start: function() {
+               this.dt.delay(this.delay);
+               this.started = true;
+       },
+
+
+       /**
+        * Stops submitting state changes
+        */
+       stop: function() {
+               this.dt.cancel();
+               this.started = false;
+       },
+
+
+       /**
+        * private, queues the state change if state has changed
+        */
+       queueChange: function(name, value) {
+               var o = {};
+               var i;
+               var found = false;
+
+
+               var lastValue = this.state[name];
+               for (i = 0; i < this.queue.length; i++) {
+                       if (this.queue[i].name === name) {
+                               lastValue = this.decodeValue(this.queue[i].value);
+                       }
+               }
+               var changed = undefined === lastValue || lastValue !== value;
+
+               if (changed) {
+                       o[this.paramNames.name] = name;
+                       o[this.paramNames.value] = this.encodeValue(value);
+                       for (i = 0; i < this.queue.length; i++) {
+                               if (this.queue[i].name === o.name) {
+                                       this.queue[i] = o;
+                                       found = true;
+                               }
+                       }
+                       if (false === found) {
+                               this.queue.push(o);
+                       }
+                       this.dirty = true;
+               }
+               if (this.started) {
+                       this.start();
+               }
+               return changed;
+       },
+
+
+       /**
+        * private, submits state to server by asynchronous Ajax request
+        */
+       submitState: function() {
+               if (!this.dirty) {
+                       this.dt.delay(this.delay);
+                       return;
+               }
+               this.dt.cancel();
+
+               var o = {
+                       scope: this,
+                       success: this.onSaveSuccess,
+                       failure: this.onSaveFailure,
+                       queue: this.queue, //this.clone(this.queue),
+                       params: {}
+               };
+
+               var params = Ext.apply({}, this.saveBaseParams);
+               params[this.paramNames.key] = this.key;
+               params[this.paramNames.data] = Ext.encode(o.queue);
+
+               Ext.apply(o.params, params);
+
+               // be optimistic
+               this.dirty = false;
+
+          TYPO3.ExtDirectStateProvider.ExtDirect.setState(o, function(response, options) {
+                  if (response.success) {
+                               this.onSaveSuccess(response, options);
+                  } else {
+                               this.onSaveFailure(response, options);
+                  }
+          }, this);
+       },
+
+
+       /**
+        * Clears the state variable
+        * @param {String} name Name of the variable to clear
+        */
+       clear: function(name) {
+               this.set(name, undefined);
+       },
+
+
+       /**
+        * private, save success callback
+        */
+       onSaveSuccess: function(response, options) {
+               var o = response;
+               if (!o.success) {
+                       if (this.logFailure) {
+                               this.log(this.saveFailureText, o, response);
+                       }
+                       this.dirty = true;
+               } else {
+                       Ext.each(response.params.queue, function(item) {
+                               if (!item) {
+                                       return;
+                               }
+                               var name = item[this.paramNames.name];
+                               var value = this.decodeValue(item[this.paramNames.value]);
+
+                               if (value === undefined  || value === null) {
+                                       TYPO3.state.ExtDirectProvider.superclass.clear.call(this, name);
+                               } else {
+                                               // parent sets value and fires event
+                                       TYPO3.state.ExtDirectProvider.superclass.set.call(this, name, value);
+                               }
+                       }, this);
+                       if (!this.dirty) {
+                               this.queue = [];
+                       }else {
+                               var i, j, found;
+                               for (i = 0; i < response.params.queue.length; i++) {
+                                       found = false;
+                                       for (j = 0; j < this.queue.length; j++) {
+                                               if (response.params.queue[i].name === this.queue[j].name) {
+                                                       found = true;
+                                                       break;
+                                               }
+                                       }
+                                       if (found && this.encodeValue(response.params.queue[i].value) === this.encodeValue(this.queue[j].value)) {
+                                               this.queue.remove(this.queue[j]);
+                                       }
+                               }
+                       }
+                       if (this.logSuccess) {
+                               this.log(this.saveSuccessText, o, response);
+                       }
+                       this.fireEvent('savesuccess', this);
+               }
+       },
+
+
+       /**
+        * private, save failure callback
+        */
+       onSaveFailure: function(response, options) {
+               if (true === this.logFailure) {
+                       this.log(this.saveFailureText, response);
+               }
+               this.dirty = true;
+               this.fireEvent('savefailure', this);
+       },
+
+
+       /**
+        * private, read state callback
+        */
+       onReadFailure: function(response, options) {
+               if (this.logFailure) {
+                       this.log(this.readFailureText, response);
+               }
+               this.fireEvent('readfailure', this);
+
+       },
+
+
+       /**
+        * private, read success callback
+        */
+       onReadSuccess: function(response, options) {
+               var o = response, data;
+               if (!o.success) {
+                       if (this.logFailure) {
+                               this.log(this.readFailureText, o, response);
+                       }
+               } else {
+                       data = o[this.paramNames.data];
+                       Ext.iterate(data, function(key, value) {
+                               this.state[key] = this.decodeValue(value);
+                       }, this);
+                       this.queue = [];
+                       this.dirty = false;
+                       if (this.logSuccess) {
+                               this.log(this.readSuccessText, data, response);
+                       }
+                       this.fireEvent('readsuccess', this);
+               }
+       },
+
+
+       /**
+        * Reads saved state from server by sending asynchronous Ajax request and processing the response
+        */
+       readState: function() {
+               var o = {
+                       scope: this,
+                       params:{}
+               };
+
+               var params = Ext.apply({}, this.readBaseParams);
+               params[this.paramNames.key] = this.key;
+
+               Ext.apply(o.params, params);
+               TYPO3.ExtDirectStateProvider.ExtDirect.getState(o, function(response, options) {
+                  if (response.success) {
+                               this.onReadSuccess(response, options);
+                  } else {
+                               this.onReadFailure(response, options);
+                  }
+          }, this);
+       },
+
+
+       /**
+        * private, logs errors or successes
+        */
+       log: function() {
+               if (console) {
+                       console.log.apply(console, arguments);
+               }
+       },
+
+       logState: function() {
+          if (console) {
+                       console.log(this.state);
+               }
+       }
+
+});
index 230e9dc..4155373 100644 (file)
@@ -244,6 +244,8 @@ class template {
        protected $pageRenderer;
        protected $pageHeaderFooterTemplateFile = '';   // alternative template file
 
+       protected $extDirectStateProvider = FALSE;
+
        /**
         * Whether flashmessages should be rendered or not
         *
@@ -337,7 +339,14 @@ class template {
 
 
 
-
+   /**
+        * Sets inclusion of StateProvider
+        *
+        * @return void
+        */
+       public function setExtDirectStateProvider() {
+               $this->extDirectStateProvider = TRUE;
+       }
 
 
 
@@ -808,6 +817,14 @@ class template {
                // add docstyles
                $this->docStyle();
 
+          if ($this->extDirectStateProvider) {
+                       $this->pageRenderer->addJsFile(
+                               $this->backPath . 'ajax.php?ajaxID=ExtDirect::getAPI&namespace=TYPO3.ExtDirectStateProvider',
+                               NULL,
+                               FALSE
+                       );
+                       $this->pageRenderer->addJsFile($this->backPath . '../t3lib/js/extjs/ExtDirect.StateProvider.js');
+               }
 
                        // add jsCode for overriding the console with a debug panel connection
                $this->pageRenderer->addJsInlineCode(