#3664: allow to configure session backends in setup
authorPhilipp Schüle <p.schuele@metaways.de>
Wed, 20 Feb 2013 14:29:35 +0000 (15:29 +0100)
committerLars Kneschke <l.kneschke@metaways.de>
Thu, 21 Feb 2013 10:02:17 +0000 (11:02 +0100)
- added redis session/queue/cache options to gui
- added actionqueue section to gui
- added memcached backend to setup gui + controller
- check session/actionqueue in setup controller
- marked Tinebase_Redis_Queue as deprecated

https://forge.tine20.org/mantisbt/view.php?id=3664

Change-Id: Ie3db4cb473b1a8bdc723dc5a890e1d152161b0a3
Reviewed-on: https://gerrit.tine20.org/tine20/1657
Tested-by: jenkins user
Reviewed-by: Philipp Schüle <p.schuele@metaways.de>
Reviewed-by: Lars Kneschke <l.kneschke@metaways.de>
tine20/Setup/Controller.php
tine20/Setup/Frontend/Json.php
tine20/Setup/js/ConfigManagerPanel.js
tine20/Tinebase/Core.php
tine20/Tinebase/Redis/Queue.php
tine20/Tinebase/js/widgets/form/ConfigPanel.js

index afce98a..cb42314 100644 (file)
@@ -6,11 +6,9 @@
  * @subpackage  Controller
  * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
  * @author      Lars Kneschke <l.kneschke@metaways.de>
- * @copyright   Copyright (c) 2008-2012 Metaways Infosystems GmbH (http://www.metaways.de)
+ * @copyright   Copyright (c) 2008-2013 Metaways Infosystems GmbH (http://www.metaways.de)
  *
  * @todo        move $this->_db calls to backend class
- * @todo        add role rights (run, admin) to all new installed apps
- * @todo        test user defined paths (logger/cache/tmpdir)
  */
 
 /**
@@ -203,7 +201,7 @@ class Setup_Controller
     }
     
     /**
-     * Check if logger is propperly configured (or not configured at all)
+     * Check if logger is properly configured (or not configured at all)
      *
      * @return boolean
      */
@@ -224,18 +222,114 @@ class Setup_Controller
     }
     
     /**
-     * Check if caching is propperly configured (or not configured at all)
+     * Check if caching is properly configured (or not configured at all)
      *
      * @return boolean
      */
     public function checkConfigCaching()
     {
-        $config = Setup_Core::getConfig();
-        if (!isset($config->caching) || !$config->caching->active) {
-            return true;
+        $result = FALSE;
+        
+        $config = Setup_Core::get(Setup_Core::CONFIG);
+        
+        if (! isset($config->caching) || !$config->caching->active) {
+            $result = TRUE;
+            
+        } else if (ucfirst($config->caching->backend) === 'File') {
+            $result = $this->checkDir('path', 'caching', FALSE);
+            
+        } else if (ucfirst($config->caching->backend) === 'Redis') {
+            $result = $this->_checkRedisConnect(isset($config->caching->redis) ? $config->caching->redis->toArray() : array());
+            
+        } else if (ucfirst($config->caching->backend) === 'Memcached') {
+            $result = $this->_checkMemcacheConnect(isset($config->caching->memcached) ? $config->caching->memcached->toArray() : array());
+            
+        }
+        
+        return $result;
+    }
+    
+    /**
+     * checks redis extension and connection
+     * 
+     * @param array $config
+     * @return boolean
+     */
+    protected function _checkRedisConnect($config)
+    {
+        if (! extension_loaded('redis')) {
+            Setup_Core::getLogger()->warn(__METHOD__ . '::' . __LINE__ . ' redis extension not loaded');
+            return FALSE;
+        }
+        $redis = new Redis;
+        $host = isset($config['host']) ? $config['host'] : 'localhost';
+        $port = isset($config['port']) ? $config['port'] : 6379;
+        
+        $result = $redis->connect($host, $port);
+        if ($result) {
+            $redis->close();
+        } else {
+            Setup_Core::getLogger()->warn(__METHOD__ . '::' . __LINE__ . ' Could not connect to redis server at ' . $host . ':' . $port);
+        }
+        
+        return $result;
+    }
+    
+    /**
+     * checks memcached extension and connection
+     * 
+     * @param array $config
+     * @return boolean
+     */
+    protected function _checkMemcacheConnect($config)
+    {
+        if (! extension_loaded('memcache')) {
+            Setup_Core::getLogger()->warn(__METHOD__ . '::' . __LINE__ . ' memcache extension not loaded');
+            return FALSE;
+        }
+        $memcache = new Memcache;
+        $host = isset($config['host']) ? $config['host'] : 'localhost';
+        $port = isset($config['port']) ? $config['port'] : 11211;
+        $result = $memcache->connect($host, $port);
+        
+        return $result;
+    }
+    
+    /**
+     * Check if queue is properly configured (or not configured at all)
+     *
+     * @return boolean
+     */
+    public function checkConfigQueue()
+    {
+        $config = Setup_Core::get(Setup_Core::CONFIG);
+        if (! isset($config->actionqueue) || ! $config->actionqueue->active) {
+            $result = TRUE;
         } else {
-            return (isset($config->caching->path) && is_writable($config->caching->path));
+            $result = $this->_checkRedisConnect($config->actionqueue->toArray());
         }
+        
+        return $result;
+    }
+    
+    /**
+     * check config session
+     * 
+     * @return boolean
+     */
+    public function checkConfigSession()
+    {
+        $result = FALSE;
+        $config = Setup_Core::get(Setup_Core::CONFIG);
+        if (! isset($config->session) || !$config->session->active) {
+            return TRUE;
+        } else if (ucfirst($config->session->backend) === 'File') {
+            return $this->checkDir('path', 'session', FALSE);
+        } else if (ucfirst($config->session->backend) === 'Redis') {
+            $result = $this->_checkRedisConnect($config->session->toArray());
+        }
+        
+        return $result;
     }
     
     /**
@@ -245,7 +339,7 @@ class Setup_Controller
      * @param string $_group
      * @return boolean
      */
-    public function checkDir($_name, $_group = NULL)
+    public function checkDir($_name, $_group = NULL, $allowEmptyPath = TRUE)
     {
         $config = $this->getConfigData();
         if ($_group !== NULL && array_key_exists($_group, $config)) {
@@ -254,7 +348,7 @@ class Setup_Controller
         
         $path = array_key_exists($_name, $config) ? $config[$_name] : false;
         if (empty($path)) {
-            return true;
+            return $allowEmptyPath;
         } else {
             return @is_writable($path);
         }
@@ -341,7 +435,7 @@ class Setup_Controller
             'updated'  => $this->_updatedApplications,
         );
     }    
-        
+    
     /**
      * load the setup.xml file and returns a simplexml object
      *
@@ -735,7 +829,7 @@ class Setup_Controller
         if (Setup_Core::configFileExists() && !Setup_Core::configFileWritable()) {
             throw new Setup_Exception('Config File is not writeable.');
         }
-            
+        
         if (Setup_Core::configFileExists()) {
             $doLogin = FALSE;
             $filename = Setup_Core::getConfigFilePath();
@@ -1239,7 +1333,7 @@ class Setup_Controller
             }
         }
         $applications = $this->_sortInstallableApplications($applications);
-                
+        
         Setup_Core::getLogger()->info(__METHOD__ . '::' . __LINE__ . ' Installing applications: ' . print_r(array_keys($applications), true));
         
         foreach ($applications as $name => $xml) {
index e3ba276..3557501 100644 (file)
@@ -195,10 +195,7 @@ class Setup_Frontend_Json extends Tinebase_Frontend_Abstract
      */
     public function checkConfig()
     {
-        // check first if db settings have changed?
-        //if (!Setup_Core::get(Setup_Core::CHECKDB))
         Setup_Core::setupDatabaseConnection();
-        
         $checkDB = Setup_Core::get(Setup_Core::CHECKDB);
         
         $result = array(
@@ -207,8 +204,9 @@ class Setup_Frontend_Json extends Tinebase_Frontend_Abstract
             'checkDB'         => $checkDB,
             'checkLogger'     => $this->_controller->checkConfigLogger(),
             'checkCaching'    => $this->_controller->checkConfigCaching(),
+            'checkQueue'      => $this->_controller->checkConfigQueue(),
             'checkTmpDir'     => $this->_controller->checkDir('tmpdir'),
-            'checkSessionDir' => $this->_controller->checkDir('path', 'session'),
+            'checkSession'    => $this->_controller->checkConfigSession(),
             'checkFilesDir'   => $this->_controller->checkDir('filesdir'),
             'setupRequired'      => empty($checkDB) ? TRUE : $this->_controller->setupRequired(),
         );
index 9f83989..ee9a0ea 100644 (file)
@@ -21,7 +21,6 @@ Ext.ns('Tine', 'Tine.Setup');
  * 
  * <p>Configuration Panel</p>
  * <p><pre>
- * TODO         add cache backend config(s)
  * </pre></p>
  * 
  * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
@@ -72,6 +71,8 @@ Tine.Setup.ConfigManagerPanel = Ext.extend(Tine.Tinebase.widgets.form.ConfigPane
     initComponent: function () {
         this.idPrefix                  = Ext.id();
         this.sessionBackendIdPrefix    = this.idPrefix + '-sessionBackend-';
+        this.cacheBackendIdPrefix    = this.idPrefix + '-cacheBackend-';
+        this.queueBackendIdPrefix    = this.idPrefix + '-queueBackend-';
 
         Tine.Setup.ConfigManagerPanel.superclass.initComponent.call(this);
     },
@@ -82,6 +83,20 @@ Tine.Setup.ConfigManagerPanel = Ext.extend(Tine.Tinebase.widgets.form.ConfigPane
     onChangeSessionBackend: function () {
         this.changeCard(this.sessionBackendCombo, this.sessionBackendIdPrefix);
     },
+
+    /**
+     * Change cache card layout depending on selected combo box entry
+     */
+    onChangeCacheBackend: function () {
+        this.changeCard(this.cacheBackendCombo, this.cacheBackendIdPrefix);
+    },
+    
+    /**
+     * Change queue card layout depending on selected combo box entry
+     */
+    onChangeQueueBackend: function () {
+        this.changeCard(this.queueBackendCombo, this.queueBackendIdPrefix);
+    },
     
     /**
      * Change default ports when database adapter gets changed
@@ -131,18 +146,41 @@ Tine.Setup.ConfigManagerPanel = Ext.extend(Tine.Tinebase.widgets.form.ConfigPane
             name: 'session_backend',
             fieldLabel: this.app.i18n._('Backend'),
             value: 'File',
-            // TODO add redis again when we are ready
-            store: [['File', this.app.i18n._('File')]/*, ['Redis','Redis'] */],
+            store: [['File', this.app.i18n._('File')], ['Redis','Redis']],
             listeners: {
                 scope: this,
                 change: this.onChangeSessionBackend,
                 select: this.onChangeSessionBackend
             }
         }, commonComboConfig));
+
+        this.cacheBackendCombo = new Ext.form.ComboBox(Ext.applyIf({
+            name: 'caching_backend',
+            fieldLabel: this.app.i18n._('Backend'),
+            value: 'File',
+            store: [['File', this.app.i18n._('File')], ['Redis','Redis'], ['Memcached', 'Memcached']],
+            listeners: {
+                scope: this,
+                change: this.onChangeCacheBackend,
+                select: this.onChangeCacheBackend
+            }
+        }, commonComboConfig));
+
+        this.queueBackendCombo = new Ext.form.ComboBox(Ext.applyIf({
+            name: 'actionqueue_backend',
+            fieldLabel: this.app.i18n._('Backend'),
+            value: 'Redis',
+            store: [['Redis','Redis']],
+            listeners: {
+                scope: this,
+                change: this.onChangeQueueBackend,
+                select: this.onChangeQueueBackend
+            }
+        }, commonComboConfig));
         
         this.sqlBackendCombo = new Ext.form.ComboBox(Ext.applyIf({
             name: 'database_adapter',
-            fieldLabel: this.app.i18n._('Adapter'),
+            fieldLabel: this.app.i18n._('Backend'),
             value: 'pdo_mysql',
             store: [
                 ['pdo_mysql', 'MySQL'],
@@ -237,27 +275,116 @@ Tine.Setup.ConfigManagerPanel = Ext.extend(Tine.Tinebase.widgets.form.ConfigPane
                 width: 300,
                 tabIndex: this.getTabIndex
             },
-            items: [{
-                name: 'caching_path',
-                fieldLabel: this.app.i18n._('Path')
-            }, {
+            items: [ {
                 xtype: 'numberfield',
                 name: 'caching_lifetime',
                 fieldLabel: this.app.i18n._('Lifetime (seconds)'),
                 minValue: 0,
                 maxValue: 3600
+            }, this.cacheBackendCombo, 
+            {
+                id: this.cacheBackendIdPrefix + 'CardLayout',
+                xtype: 'panel',
+                layout: 'card',
+                activeItem: this.cacheBackendIdPrefix + 'File',
+                border: false,
+                width: '100%',
+                defaults: {border: false},
+                items: [{
+                    id: this.cacheBackendIdPrefix + 'File',
+                    layout: 'form',
+                    autoHeight: 'auto',
+                    defaults: {
+                        width: 300,
+                        xtype: 'textfield',
+                        tabIndex: this.getTabIndex
+                    },
+                    items: [{
+                        name: 'caching_path',
+                        fieldLabel: this.app.i18n._('Path')
+                    }]
+                }, {
+                    id: this.cacheBackendIdPrefix + 'Redis',
+                    layout: 'form',
+                    autoHeight: 'auto',
+                    defaults: {
+                        width: 300,
+                        xtype: 'textfield',
+                        tabIndex: this.getTabIndex
+                    },
+                    items: [{
+                        name: 'caching_redis_host',
+                        fieldLabel: this.app.i18n._('Hostname'),
+                        value: 'localhost'
+                    }, {
+                        name: 'caching_redis_port',
+                        fieldLabel: this.app.i18n._('Port'),
+                        xtype: 'numberfield',
+                        minValue: 0,
+                        value: 6379
+                    }]
+                }, {
+                    id: this.cacheBackendIdPrefix + 'Memcached',
+                    layout: 'form',
+                    autoHeight: 'auto',
+                    defaults: {
+                        width: 300,
+                        xtype: 'textfield',
+                        tabIndex: this.getTabIndex
+                    },
+                    items: [{
+                        name: 'caching_memcached_host',
+                        fieldLabel: this.app.i18n._('Hostname'),
+                        value: 'localhost'
+                    }, {
+                        name: 'caching_memcached_port',
+                        fieldLabel: this.app.i18n._('Port'),
+                        xtype: 'numberfield',
+                        minValue: 0,
+                        value: 11211
+                    }]
+                }]
             }]
         }, {
-            title: this.app.i18n._('Temporary files'),
-            id: 'setup-tmpDir-group',
+            title: this.app.i18n._('Queue'),
+            id: 'setup-actionqueue-group',
+            checkboxToggle: true,
+            collapsed: true,
             defaults: {
                 width: 300,
                 tabIndex: this.getTabIndex
             },
-            items: [{
-                name: 'tmpdir',
-                fieldLabel: this.app.i18n._('Temporary Files Path'),
-                value: Tine.Setup.registry.get(this.registryKey).tmpdir
+            items: [this.queueBackendCombo, 
+            {
+                id: this.queueBackendIdPrefix + 'CardLayout',
+                xtype: 'panel',
+                layout: 'card',
+                activeItem: this.queueBackendIdPrefix + 'Redis',
+                border: false,
+                width: '100%',
+                defaults: {border: false},
+                items: [{
+                    // redis config options
+                    id: this.queueBackendIdPrefix + 'Redis',
+                    layout: 'form',
+                    autoHeight: 'auto',
+                    defaults: {
+                        width: 300,
+                        xtype: 'textfield',
+                        tabIndex: this.getTabIndex
+                    },
+                    items: [{
+                        name: 'actionqueue_host',
+                        fieldLabel: this.app.i18n._('Hostname'),
+                        value: 'localhost'
+                    }, {
+                        name: 'actionqueue_port',
+                        fieldLabel: this.app.i18n._('Port'),
+                        xtype: 'numberfield',
+                        minValue: 0,
+                        value: 6379
+                    }]
+                }]
             }]
         }, {
             title: this.app.i18n._('Session'),
@@ -319,6 +446,18 @@ Tine.Setup.ConfigManagerPanel = Ext.extend(Tine.Tinebase.widgets.form.ConfigPane
                 }]
             }]
         }, {
+            title: this.app.i18n._('Temporary files'),
+            id: 'setup-tmpDir-group',
+            defaults: {
+                width: 300,
+                tabIndex: this.getTabIndex
+            },
+            items: [{
+                name: 'tmpdir',
+                fieldLabel: this.app.i18n._('Temporary Files Path'),
+                value: Tine.Setup.registry.get(this.registryKey).tmpdir
+            }]
+        }, {
             // TODO this should be not saved in the config.inc.php
             title: this.app.i18n._('Filestore directory'),
             id: 'setup-filesDir-group',
@@ -363,8 +502,9 @@ Tine.Setup.ConfigManagerPanel = Ext.extend(Tine.Tinebase.widgets.form.ConfigPane
         Ext.getCmp('setup-database-group').setIconClass(Tine.Setup.registry.get('checkDB') ? 'setup_checks_success' : 'setup_checks_fail');
         Ext.getCmp('setup-logger-group').setIconClass(Tine.Setup.registry.get('checkLogger') ? 'setup_checks_success' : 'setup_checks_fail');
         Ext.getCmp('setup-caching-group').setIconClass(Tine.Setup.registry.get('checkCaching') ? 'setup_checks_success' : 'setup_checks_fail');
+        Ext.getCmp('setup-actionqueue-group').setIconClass(Tine.Setup.registry.get('checkQueue') ? 'setup_checks_success' : 'setup_checks_fail');
         Ext.getCmp('setup-tmpDir-group').setIconClass(Tine.Setup.registry.get('checkTmpDir') ? 'setup_checks_success' : 'setup_checks_fail');
-        Ext.getCmp('setup-session-group').setIconClass(Tine.Setup.registry.get('checkSessionDir') ? 'setup_checks_success' : 'setup_checks_fail');
+        Ext.getCmp('setup-session-group').setIconClass(Tine.Setup.registry.get('checkSession') ? 'setup_checks_success' : 'setup_checks_fail');
         Ext.getCmp('setup-filesDir-group').setIconClass(Tine.Setup.registry.get('checkFilesDir') ? 'setup_checks_success' : 'setup_checks_fail');
     },
     
index 6f3ba31..e31bcea 100644 (file)
@@ -5,7 +5,7 @@
  * @package     Tinebase
  * @subpackage  Server
  * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
- * @copyright   Copyright (c) 2007-2012 Metaways Infosystems GmbH (http://www.metaways.de)
+ * @copyright   Copyright (c) 2007-2013 Metaways Infosystems GmbH (http://www.metaways.de)
  * @author      Philipp Schüle <p.schuele@metaways.de>
  *
  */
@@ -594,19 +594,23 @@ class Tinebase_Core
                         break;
                         
                     case 'Memcached':
+                        $host = $config->caching->host ? $config->caching->host : ($config->caching->memcached->host ? $config->caching->memcached->host : 'localhost');
+                        $port = $config->caching->port ? $config->caching->port : ($config->caching->memcached->port ? $config->caching->memcached->port : 11211);
                         $backendOptions = array(
                             'servers' => array(
-                                'host' => ($config->caching->host) ? $config->caching->host : 'localhost',
-                                'port' => ($config->caching->port) ? $config->caching->port : 11211,
+                                'host' => $host,
+                                'port' => $port,
                                 'persistent' => TRUE
                         ));
                         break;
                         
                     case 'Redis':
+                        $host = $config->caching->host ? $config->caching->host : ($config->caching->redis->host ? $config->caching->redis->host : 'localhost');
+                        $port = $config->caching->port ? $config->caching->port : ($config->caching->redis->port ? $config->caching->redis->port : 6379);
                         $backendOptions = array(
                             'servers' => array(
-                                'host'   => ($config->caching->host) ? $config->caching->host : 'localhost',
-                                'port'   => ($config->caching->port) ? $config->caching->port : 6379,
+                                'host'   => $host,
+                                'port'   => $port,
                                 'prefix' =>  Tinebase_Application::getInstance()->getApplicationByName('Tinebase')->getId() . '_CACHE_'
                         ));
                         break;
@@ -665,7 +669,7 @@ class Tinebase_Core
         Zend_Db_Table_Abstract::setDefaultMetadataCache($cache);
         self::set(self::CACHE, $cache);
     }
-
+    
     /**
      * places user credential cache id from cache adapter (if present) into registry
      */
index f94f8e6..3a91aa6 100644 (file)
@@ -14,6 +14,8 @@
  *
  * @package     Redis
  * @subpackage  Queue
+ * 
+ * @deprecated should be removed and replaced with Tinebase_ActionQueue_Backend_Redis
  */
 class Tinebase_Redis_Queue
 {
@@ -47,9 +49,9 @@ class Tinebase_Redis_Queue
             throw new Tinebase_Exception('The redis extension must be loaded for using redis job queue!');
         }
         
-        $config = Tinebase_Config::getInstance()->get('redis', NULL);
+        $config = Tinebase_Config::getInstance()->get('actionqueue', NULL);
         
-        if ($config === NULL) {
+        if ($config === NULL && $config->adapter !== 'Redis') {
             throw new Tinebase_Exception('No redis config found!');
         } else {
             $this->_config = array_merge($this->_config, $config->toArray(), $additionalConfig);
index 7c5f7d0..adb0d5b 100644 (file)
@@ -299,11 +299,13 @@ Tine.Tinebase.widgets.form.ConfigPanel = Ext.extend(Ext.FormPanel, {
      */
     changeCard: function(combo, backendIdPrefix) {
         var value = combo.getValue();
+        Tine.log.debug('changeCard() -> value: ' + value);
         
         var cardPanel = Ext.getCmp(backendIdPrefix + 'CardLayout'),
             cardLayout = (cardPanel) ? cardPanel.getLayout() : null;
-            
+        
         if (cardLayout && cardLayout !== 'card') {
+            Tine.log.debug('changeCard() -> set active item: ' + backendIdPrefix + value);
             cardLayout.setActiveItem(backendIdPrefix + value);
         }
     }