0011666: Some fixes for Expressodriver
authorFlávio Gomes da Silva Lisboa <flavio.lisboa@serpro.gov.br>
Thu, 10 Mar 2016 13:39:14 +0000 (10:39 -0300)
committerPhilipp Schüle <p.schuele@metaways.de>
Fri, 11 Mar 2016 08:23:53 +0000 (09:23 +0100)
- Fix session namespace for Expressodriver
- Reload tree node after credentials input
- Add dialog for Expresso Drive credentials

Change-Id: I26eb5e3c34d4b51b0d6b440680a40a2259dbaf05
Reviewed-on: https://gerrit.tine20.org/tine20/3295
Tested-by: jenkins user
Reviewed-by: Philipp Schüle <p.schuele@metaways.de>
15 files changed:
tine20/Expressodriver/Backend/Storage/Adapter/Webdav.php
tine20/Expressodriver/Controller.php
tine20/Expressodriver/Controller/Node.php
tine20/Expressodriver/Exception.php
tine20/Expressodriver/Exception/CredentialsRequired.php [new file with mode: 0644]
tine20/Expressodriver/Expressodriver.jsb2
tine20/Expressodriver/Frontend/Json.php
tine20/Expressodriver/Session.php [new file with mode: 0644]
tine20/Expressodriver/Setup/setup.xml
tine20/Expressodriver/js/CredentialsDialog.js [new file with mode: 0644]
tine20/Expressodriver/js/ExceptionHandler.js
tine20/Expressodriver/js/NodeGridPanel.js
tine20/Expressodriver/translations/de.po
tine20/Expressodriver/translations/pt_BR.po
tine20/Expressodriver/translations/template.pot

index 6323134..e071dde 100644 (file)
  */
 class Expressodriver_Backend_Storage_Adapter_Webdav extends Expressodriver_Backend_Storage_Abstract implements Expressodriver_Backend_Storage_Adapter_Interface, Expressodriver_Backend_Storage_Capabilities
 {
+    /**
+     * Constant for Expresso drive cache key
+     */
+    const GETEXPRESSODRIVEETAGS = 'getExpressodriveEtags';
+
+    /**
+     * Constant for Expresso drive cache entry tag
+     */
+    const EXPRESSODRIVEETAGS = 'expressodriverEtags';
 
     /**
      * @var string password
@@ -689,15 +698,20 @@ class Expressodriver_Backend_Storage_Adapter_Webdav extends Expressodriver_Backe
     private function getNodesFromCache($path, $etag)
     {
         $cache = Tinebase_Core::get('cache');
-        $cacheId = Tinebase_Helper::convertCacheId('getExpressodriveEtags' . sha1(Tinebase_Core::getUser()->getId() . $this->encodePath($path)));
+        $cacheId = Tinebase_Helper::arrayToCacheId(
+                array(
+                    self::GETEXPRESSODRIVEETAGS,
+                    sha1(Tinebase_getUser()->getId()) . $this->encodePath($path)
+                )
+            );
         $result = $cache->load($cacheId);
         if (!$result) {
             $result = $this->getNodesFromBackend($path);
-            $cache->save($result, $cacheId, array('expressodriverEtags'), $this->cacheLifetime);
+            $cache->save($result, $cacheId, array(self::EXPRESSODRIVEETAGS), $this->cacheLifetime);
         } else {
             if ($result[0]['hash'] != $etag) {
                 $result = $this->getNodesFromBackend($path);
-                $cache->save($result, $cacheId, array('expressodriverEtags'), $this->cacheLifetime);
+                $cache->save($result, $cacheId, array(self::EXPRESSODRIVEETAGS), $this->cacheLifetime);
             }
         }
         return $result;
@@ -749,4 +763,27 @@ class Expressodriver_Backend_Storage_Adapter_Webdav extends Expressodriver_Backe
         );
         return $filetmp;
     }
+
+    /**
+     * check if webdav credentials are valid
+     *
+     * @param string $url
+     * @param string $username
+     * @param string $password
+     * @return boolean
+     */
+    public function checkCredentials($url, $username, $password)
+    {
+        $arr = explode('://', $url, 2);
+        list($webdavauth_protocol, $webdavauth_url_path) = $arr;
+        $url = $webdavauth_protocol.'://'.urlencode($username).':'.urlencode($password).'@'.$webdavauth_url_path;
+
+        $headers = get_headers($url);
+        if ($headers == false) {
+            return false;
+        }
+        $returncode = substr($headers[0], 9, 3);
+
+        return substr($returncode, 0, 1) === '2';
+    }
 }
\ No newline at end of file
index 80b9a6e..b9efd38 100644 (file)
@@ -129,4 +129,56 @@ class Expressodriver_Controller extends Tinebase_Controller_Event
 
         return $this->getConfigSettings();
     }
+
+    /**
+     * set credentials for given adapter
+     *
+     * @param string $adapterName
+     * @param string $password
+     * @return array
+     */
+    public function setCredentials($adapterName, $password)
+    {
+
+        $adapter = null;
+        $config = $this->getConfigSettings();
+        foreach ($config['adapters'] as $adapterConfig) {
+            if ($adapterName === $adapterConfig['name']) {
+                $adapter = $adapterConfig;
+            }
+        }
+
+        $url = $adapter['url'];
+        if ($adapter['adapter'] == 'owncloud') {
+            $url = rtrim($url, '/');
+            $url .= '/remote.php/webdav/';
+        }
+        $username = $adapter['useEmailAsLoginName']
+                ? Tinebase_Core::getUser()->accountEmailAddress
+                : Tinebase_Core::getUser()->accountLoginName;
+
+        $options = array(
+            'host' => $adapter['url'],
+            'user' => $username,
+            'password' => $password,
+            'root' => '/',
+            'name' => $adapter['name'],
+            'useCache' => $config['default']['useCache'],
+            'cacheLifetime' => $config['default']['cacheLifetime'],
+        );
+        $adapterInstance = Expressodriver_Backend_Storage_Abstract::factory($adapter['adapter'], $options);
+
+        // check authentication for owncloud/webdav
+        if ($adapterInstance->checkCredentials($url, $username, $password)) {
+            Expressodriver_Session::getSessionNamespace()->password[$adapterName] = $password;
+            return array(
+                'success' => true
+            );
+        } else {
+            return array(
+                'success' => false,
+                'errorMessage' => 'Invalid Credentials'
+            );
+        }
+    }
 }
index 935f221..012e010 100644 (file)
@@ -1078,6 +1078,17 @@ class Expressodriver_Controller_Node
                 $credentialsBackend = Tinebase_Auth_CredentialCache::getInstance();
                 $userCredentialCache = Tinebase_Core::getUserCredentialCache();
                 $credentialsBackend->getCachedCredentials($userCredentialCache);
+
+                $password = !(empty($userCredentialCache->password)) ?
+                        $userCredentialCache->password :
+                        Expressodriver_Session::getSessionNamespace()->password[$adapterName];
+
+                if (empty($password)) {
+                    $exception = new Expressodriver_Exception_CredentialsRequired();
+                    $exception->setAdapterName($adapterName);
+                    throw $exception;
+                }
+
                 $username = $adapter['useEmailAsLoginName']
                         ? Tinebase_Core::getUser()->accountEmailAddress
                         : Tinebase_Core::getUser()->accountLoginName;
@@ -1085,7 +1096,7 @@ class Expressodriver_Controller_Node
                 $options = array(
                     'host' => $adapter['url'],
                     'user' => $username,
-                    'password' => $userCredentialCache->password,
+                    'password' => $password,
                     'root' => '/',
                     'name' => $adapter['name'],
                     'useCache' => $config['default']['useCache'],
index a35d4c1..d8e3d23 100644 (file)
  * @package     Expressodriver
  * @subpackage  Exception
  */
-class Expressodriver_Exception extends Exception
+class Expressodriver_Exception extends Tinebase_Exception
 {
+    /**
+     * the name of the application, this exception belongs to
+     *
+     * @var string
+     */
+    protected $_appName = 'Expressodriver';
 }
diff --git a/tine20/Expressodriver/Exception/CredentialsRequired.php b/tine20/Expressodriver/Exception/CredentialsRequired.php
new file mode 100644 (file)
index 0000000..8c3ba36
--- /dev/null
@@ -0,0 +1,67 @@
+<?php
+/**
+ * Tine 2.0
+ *
+ * @package     Expressodriver
+ * @subpackage  Exception
+ * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
+ * @copyright   Copyright (c) 2007-2014 Metaways Infosystems GmbH (http://www.metaways.de)
+ * @copyright   Copyright (c) 2014 Serpro (http://www.serpro.gov.br)
+ * @author      Marcelo Teixeira <marcelo.teixeira@serpro.gov.br>
+ * @author      Edgar de Lucca <edgar.lucca@serpro.gov.br>
+ */
+
+/**
+ * CredentialsNeeded exception
+ *
+ * @package     Expressodriver
+ * @subpackage  Exception
+ */
+class Expressodriver_Exception_CredentialsRequired extends Expressodriver_Exception
+{
+    /**
+     * the title of the Exception (may be shown in a dialog)
+     *
+     * @var string
+     */
+    protected $_title = 'Credentials required';
+
+    /**
+     * @see SPL Exception
+     */
+    protected $message = 'Your credentials for Expressodriver are required';
+
+    /**
+     * @see SPL Exception
+    */
+    protected $code = 904;
+
+    /**
+     * adapter name where credentials are required
+     *
+     * @var string
+     */
+    protected $adapterName = '';
+
+    /**
+     * set adapter name
+     *
+     * @param string $adapterName
+     */
+    public function setAdapterName($adapterName)
+    {
+        $this->adapterName = $adapterName;
+    }
+
+    /**
+     * returns adapter name info as array
+     *
+     * @return array
+     */
+    public function toArray()
+    {
+        return array(
+            'adaptername' => $this->adapterName
+        );
+    }
+}
index 4330869..955547c 100644 (file)
         {
           "text": "ExternalAdapterEditDialog.js",
           "path": "js/"
+        },
+        {
+          "text": "CredentialsDialog.js",
+          "path": "js/"
         }
       ]
     },
index e4c5879..599eff7 100644 (file)
@@ -216,5 +216,17 @@ class Expressodriver_Frontend_Json extends Tinebase_Frontend_Json_Abstract
         return $registryData;
     }
 
+    /**
+     * set credentials for given adapter
+     *
+     * @param string $adapterName
+     * @param string $password
+     * @return array
+     */
+    public function setCredentials($adapterName, $password)
+    {
+        return Expressodriver_Controller::getInstance()->setCredentials($adapterName, $password);
+    }
+
 
 }
diff --git a/tine20/Expressodriver/Session.php b/tine20/Expressodriver/Session.php
new file mode 100644 (file)
index 0000000..90ef73b
--- /dev/null
@@ -0,0 +1,42 @@
+<?php
+/**
+ * Tine 2.0
+ *
+ * @package     Expressodriver
+ * @subpackage  Session
+ * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
+ * @author      Marcelo Teixeira <marcelo.teixeira@serpro.gov.br>
+ * @copyright   Copyright (c) 2014 Serpro (http://www.serpro.gov.br)
+ *
+ */
+
+/**
+ * Session class for Expressodriver
+ *
+ * @package     Expressodriver
+ * @subpackage  Session
+ */
+class Expressodriver_Session extends Tinebase_Session_Abstract
+{
+    /**
+     * Addressbook Session Namespace
+     */
+    const EXPRESSODRIVER_SESSION_NAMESPACE = 'Expressodriver_Session_Namespace';
+
+    /**
+     * Gets Expressodriver session namespace
+     *
+     * @throws Exception
+     * @return Ambigous <Zend_Session_Namespace, NULL, mixed>
+     */
+    public static function getSessionNamespace()
+    {
+        try {
+            return self::_getSessionNamespace(self::EXPRESSODRIVER_SESSION_NAMESPACE);
+        } catch(Exception $e) {
+            Tinebase_Core::getLogger()->warn(__METHOD__ . '::' . __LINE__ . ' Session error: ' . $e->getMessage());
+            Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . ' ' . $e->getTraceAsString());
+            throw $e;
+        }
+    }
+}
\ No newline at end of file
index a117326..f82dff6 100644 (file)
@@ -3,7 +3,6 @@
     <name>Expressodriver</name>
     <version>1.0</version>
     <order>11</order>
-    <status>disabled</status>
     <depends>
         <application>Admin</application>
     </depends>
diff --git a/tine20/Expressodriver/js/CredentialsDialog.js b/tine20/Expressodriver/js/CredentialsDialog.js
new file mode 100644 (file)
index 0000000..d96b914
--- /dev/null
@@ -0,0 +1,124 @@
+/*
+ * Tine 2.0
+ *
+ * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
+ * @author      Marcelo Teixeira <marcelo.teixeira@serpro.gov.br>
+ * @copyright   Copyright (c) 2015 Metaways Infosystems GmbH (http://www.metaways.de)
+ *
+ */
+
+ Ext.ns('Tine.Expressodriver');
+
+ /**
+  * @namespace  Tine.Expressodriver
+  * @class      Tine.Expressodriver.CredentialsDialog
+  * @extends    Ext.Window
+  */
+Tine.Expressodriver.CredentialsDialog = Ext.extend(Ext.Window, {
+
+    id: 'expressodriverCredentials_window',
+    closeAction: 'close',
+    modal: true,
+    width: 350,
+    height: 230,
+    minWidth: 350,
+    minHeight: 230,
+    layout: 'fit',
+    plain: true,
+    title: null,
+    adapterName: '',
+
+    initComponent: function() {
+        var app = Tine.Tinebase.appMgr.get('Expressodriver');
+        this.title = (this.title !== null) ? this.title : app.i18n._('Credentials for Expressodriver');
+
+        this.items = new Ext.FormPanel({
+            bodyStyle: 'padding:5px;',
+            buttonAlign: 'right',
+            labelAlign: 'top',
+            anchor:'100%',
+            id: 'expressodriverCredentialsPanel',
+            defaults: {
+                xtype: 'textfield',
+                inputType: 'password',
+                anchor: '100%',
+                allowBlank: false
+            },
+            items: [{
+                id: 'password',
+                fieldLabel: _('Password'),
+                name:'password'
+            },{
+                id: 'passwordSecondTime',
+                fieldLabel: _('Repeat Password'),
+                name:'passwordSecondTime'
+            }],
+            buttons: [{
+                text: _('Cancel'),
+                iconCls: 'action_cancel',
+                handler: function() {
+                    Ext.getCmp('expressodriverCredentials_window').close();
+                }
+            }, {
+                text: _('Ok'),
+                iconCls: 'action_saveAndClose',
+                handler: function() {
+                    var form = Ext.getCmp('expressodriverCredentialsPanel').getForm();
+                    var values;
+                    if (form.isValid()) {
+                        values = form.getValues();
+                        if (values.password == values.passwordSecondTime) {
+                            Ext.Ajax.request({
+                                waitTitle: _('Please Wait!'),
+                                waitMsg: _('changing password...'),
+                                params: {
+                                    method: 'Expressodriver.setCredentials',
+                                    adapterName: Ext.getCmp('expressodriverCredentials_window').adapterName,
+                                    password: values.password
+                                },
+                                success: function(_result, _request){
+                                    var response = Ext.util.JSON.decode(_result.responseText);
+                                    if (response.success) {
+                                        Ext.getCmp('expressodriverCredentials_window').close();
+                                        Ext.MessageBox.show({
+                                            title: _('Success'),
+                                            msg: app.i18n._('Authentication success.'),
+                                            buttons: Ext.MessageBox.OK,
+                                            icon: Ext.MessageBox.INFO
+                                        });
+
+                                        // reload grid and treepanel
+                                        app.getMainScreen().getCenterPanel().grid.getStore().reload();
+
+                                        var treeNode = app.getMainScreen().getWestPanel().getContainerTreePanel();
+                                        var selected = treeNode.getSelectionModel().getSelectedNode();
+                                        if (selected) {
+                                            selected.reload();
+                                        }
+
+                                    } else {
+                                        Ext.MessageBox.show({
+                                            title: app.i18n._('Credentials error'),
+                                            msg: app.i18n._(response.errorMessage),
+                                            buttons: Ext.MessageBox.OK,
+                                            icon: Ext.MessageBox.ERROR
+                                        });
+                                    }
+                                }
+                            });
+                        } else {
+                            Ext.MessageBox.show({
+                                title: _('Failure'),
+                                msg: _('The new passwords mismatch, please correct them.'),
+                                buttons: Ext.MessageBox.OK,
+                                icon: Ext.MessageBox.ERROR
+                            });
+                        }
+                    }
+                }
+            }]
+        });
+
+        Tine.Expressodriver.CredentialsDialog.superclass.initComponent.call(this);
+    }
+});
index 8f3ee98..606d7fa 100644 (file)
@@ -35,7 +35,7 @@ Tine.Expressodriver.handleRequestException = function(exception, request) {
                 icon: Ext.MessageBox.WARNING,
                 title: _('Service Unavailable'),
                 msg: String.format(app.i18n._('The Expressodriver is not configured correctly. Please refer to the {0}Tine 2.0 Admin FAQ{1} for configuration advice or contact your administrator.'),
-                    '<a href="http://wiki.tine20.org/Admin_FAQ#The_message_.22filesdir_config_value_not_set.22_appears_in_the_logfile_and_I_can.27t_open_the_Expressodriver" target="_blank">',
+                    '<a href="http://www.tine20.org/wiki/index.php/Admin_FAQ#The_message_.22filesdir_config_value_not_set.22_appears_in_the_logfile_and_I_can.27t_open_the_Expressodriver" target="_blank">',
                     '</a>')
             });
             break;
@@ -123,6 +123,11 @@ Tine.Expressodriver.handleRequestException = function(exception, request) {
                 msg: app.i18n._(exception.message)
             });
             break;
+        case 904: // Expressodriver_Exception_CredentialsRequired
+            var passwordDialog = new Tine.Expressodriver.CredentialsDialog();
+            passwordDialog.adapterName = exception.adaptername;
+            passwordDialog.show();
+            break;
         default:
             Tine.Tinebase.ExceptionHandler.handleRequestException(exception);
             break;
index 186a472..f3fb915 100644 (file)
@@ -383,9 +383,41 @@ Tine.Expressodriver.NodeGridPanel = Ext.extend(Tine.widgets.grid.GridPanel, {
      */
     getContextMenu: function(grid, row, e) {
         var r = this.store.getAt(row),
-            type = r ? r.get('type') : null;
+            type = r ? r.get('type') : null,
+            pathParts = r ? r.get('path') : null;
 
-        return type === 'folder' ? this.folderContextMenu : this.contextMenu;
+        var isRootFolder = pathParts ? pathParts.split('/').length < 3 : false;
+        if (isRootFolder) {
+            return;
+        } else if (type === 'folder') {
+            return this.folderContextMenu;
+        } else {
+            return this.contextMenu;
+        }
+    },
+
+    /**
+     * called on row context click
+     *
+     * @param {Ext.grid.GridPanel} grid
+     * @param {Number} row
+     * @param {Ext.EventObject} e
+     */
+    onRowContextMenu: function(grid, row, e) {
+        e.stopEvent();
+        var selModel = grid.getSelectionModel();
+        if (!selModel.isSelected(row)) {
+            // disable preview update if config option is set to false
+            this.updateOnSelectionChange = this.updateDetailsPanelOnCtxMenu;
+            selModel.selectRow(row);
+        }
+
+        var contextMenu = this.getContextMenu(grid, row, e);
+        if (contextMenu) {
+            contextMenu.showAt(e.getXY())
+        }
+        // reset preview update
+        this.updateOnSelectionChange = true;
     },
 
     /**
index c77948e..3baf728 100644 (file)
@@ -143,7 +143,7 @@ msgstr "Typ"
 
 #: js/Expressodriver.js:36
 msgid "Expressodriver"
-msgstr "Expresso-Dateimanager"
+msgstr "Dateimanager"
 
 #: js/Expressodriver.js:91
 msgid "Service Unavailable"
index 605091d..9980104 100644 (file)
@@ -144,7 +144,7 @@ msgstr "Tipo de conteúdo"
 
 #: js/Expressodriver.js:36
 msgid "Expressodriver"
-msgstr "Expressodriver"
+msgstr "Expresso Drive"
 
 #: js/Expressodriver.js:91
 msgid "Service Unavailable"
@@ -261,3 +261,19 @@ msgstr "Deletando itens..."
 #: js/ExternalAdapter:156
 msgid "Use e-mail as login name"
 msgstr "Usar e-mail como login"
+
+#: js/CredentialsDialog:33
+msgid "Credentials for Expressodriver"
+msgstr "Credenciais para o Expresso Drive"
+
+#: js/CredentialsDialog:85
+msgid "Authentication success."
+msgstr "Autenticação realizada com sucesso."
+
+#: js/CredentialsDialog:96
+msgid "Credentials error"
+msgstr "Erro de credenciais"
+
+#: js/CredentialsDialog:97
+msgid "Invalid Credentials"
+msgstr "Credenciais inválidas"
\ No newline at end of file
index 98e9ee2..21701c8 100644 (file)
@@ -13,259 +13,260 @@ msgstr ""
 "X-Poedit-SourceCharset: utf-8\n"
 "Plural-Forms: nplurals=2; plural=n != 1;\n"
 
-#: Acl/Rights.php:98
+#: Acl/Rights.php:97
 msgid "manage shared folders"
 msgstr ""
 
-#: Acl/Rights.php:99
+#: Acl/Rights.php:98
 msgid "Create new shared folders"
 msgstr ""
 
-#: js/NodeEditDialog.js:50 js/NodeGridPanel.js:333 js/Expressodriver.js:46
-msgid "Save locally"
-msgstr ""
-
-#: js/NodeEditDialog.js:104 js/NodeEditDialog.js:117
-msgid "Node"
-msgstr ""
-
-#: js/NodeEditDialog.js:123 js/ExternalAdapterEditDialog.js:81
-#: js/ExternalAdapter.js:137 js/NodeGridPanel.js:134
-msgid "Name"
-msgstr ""
-
-#: js/NodeEditDialog.js:130
-msgid "Type"
+#: Controller.php:102
+#, python-format
+msgid "%s's personal files"
 msgstr ""
 
-#: js/NodeEditDialog.js:138
-msgid "Modified By"
+#: js/PathFilterModel.js:50
+msgid "path"
 msgstr ""
 
-#: js/NodeEditDialog.js:141
-msgid "Last Modified"
+#: js/NodeEditDialog.js:44 js/Expressodriver.js:44 js/NodeGridPanel.js:346
+msgid "Save locally"
 msgstr ""
 
-#: js/NodeTreePanel.js:697 js/NodeTreePanel.js:816 js/NodeGridPanel.js:630
-#: js/NodeGridPanel.js:782
-msgid "Upload Failed"
+#: js/NodeEditDialog.js:95 js/NodeEditDialog.js:108
+msgid "Node"
 msgstr ""
 
-#: js/NodeTreePanel.js:698 js/NodeGridPanel.js:631
-msgid ""
-"Could not upload file. Filesize could be too big. Please notify your "
-"Administrator. Max upload size:"
+#: js/NodeEditDialog.js:114 js/NodeGridPanel.js:135
+msgid "Name"
 msgstr ""
 
-#: js/NodeTreePanel.js:721 js/GridContextMenu.js:72 js/GridContextMenu.js:175
-#: js/Model.js:210 js/Model.js:294 js/NodeGridPanel.js:495
-msgid "Please wait"
+#: js/NodeEditDialog.js:121 js/Model.js:530
+msgid "Type"
 msgstr ""
 
-#: js/NodeTreePanel.js:721 js/GridContextMenu.js:72
-msgid "Renaming nodes..."
+#: js/NodeEditDialog.js:129 js/NodeGridPanel.js:189
+msgid "Created By"
 msgstr ""
 
-#: js/NodeTreePanel.js:817 js/NodeGridPanel.js:783
-msgid "Putting files in this folder is not allowed!"
+#: js/NodeEditDialog.js:132 js/Model.js:532 js/NodeGridPanel.js:181
+msgid "Creation Time"
 msgstr ""
 
-#: js/AdminPanel.js:45
-msgid "External Storage Adapters"
+#: js/NodeEditDialog.js:141
+msgid "Modified By"
 msgstr ""
 
-#: js/AdminPanel.js:53
-msgid "Default"
+#: js/NodeEditDialog.js:144
+msgid "Last Modified"
 msgstr ""
 
-#: js/AdminPanel.js:62
-msgid "Enable cache for adapters"
+#: js/NodeEditDialog.js:167
+msgid "Description"
 msgstr ""
 
-#: js/AdminPanel.js:67
-msgid "Cache lifetime"
+#: js/NodeEditDialog.js:181
+msgid "Enter description"
 msgstr ""
 
-#: js/AdminPanel.js:89
-#, python-brace-format
-msgid "Change settings for application {0}"
+#: js/NodeTreePanel.js:660 js/NodeTreePanel.js:770 js/NodeGridPanel.js:653
+#: js/NodeGridPanel.js:805
+msgid "Upload Failed"
 msgstr ""
 
-#: js/AdminPanel.js:158 js/ExternalAdapterEditDialog.js:48
-msgid "Errors"
+#: js/NodeTreePanel.js:661 js/NodeGridPanel.js:654
+msgid ""
+"Could not upload file. Filesize could be too big. Please notify your "
+"Administrator. Max upload size: "
 msgstr ""
 
-#: js/GridContextMenu.js:38
-msgid "Rename"
+#: js/NodeTreePanel.js:771 js/NodeGridPanel.js:806
+msgid "Putting files in this folder is not allowed!"
 msgstr ""
 
-#: js/GridContextMenu.js:39
-#, python-brace-format
+#: js/GridContextMenu.js:34
 msgid "Please enter the new name of the {0}:"
 msgstr ""
 
-#: js/GridContextMenu.js:45 js/GridContextMenu.js:49
-#, python-brace-format
-msgid "Not renamed {0}"
-msgstr ""
-
-#: js/GridContextMenu.js:45 js/NodeGridPanel.js:492
-#, python-brace-format
-msgid "You have to supply a {0} name!"
-msgstr ""
-
 #: js/GridContextMenu.js:49
 msgid "You have to supply a different name!"
 msgstr ""
 
-#: js/GridContextMenu.js:154 js/NodeGridPanel.js:533
-msgid "Do you really want to delete the following files?"
+#: js/GridContextMenu.js:40
+msgid "Not renamed {0}"
 msgstr ""
 
-#: js/GridContextMenu.js:175 js/Model.js:210
-msgid "Deleting nodes..."
+#: js/GridContextMenu.js:40 js/NodeGridPanel.js:505
+msgid "You have to supply a {0} name!"
 msgstr ""
 
-#: js/ExternalAdapterEditDialog.js:48
-msgid "Please fix the errors noted."
+#: js/GridContextMenu.js:144 js/NodeGridPanel.js:546
+msgid "Do you really want to delete the following files?"
 msgstr ""
 
-#: js/ExternalAdapterEditDialog.js:61
-msgid "External Adapter"
-msgstr ""
+#: js/Model.js:40
+msgid "File"
+msgid_plural "Files"
+msgstr[0] ""
+msgstr[1] ""
 
-#: js/ExternalAdapterEditDialog.js:87 js/ExternalAdapter.js:143
-msgid "Adapter"
-msgstr ""
+#: js/Model.js:43 js/NodeGridPanel.js:157
+msgid "Folder"
+msgid_plural "Folders"
+msgstr[0] ""
+msgstr[1] ""
 
-#: js/ExternalAdapterEditDialog.js:94 js/ExternalAdapter.js:151
-msgid "Url"
+#: js/Model.js:281 js/Model.js:297
+msgid "Copying data .. {0}"
 msgstr ""
 
-#: js/ExternalAdapterEditDialog.js:99 js/ExternalAdapter.js:156
-msgid "Use e-mail as login name"
+#: js/Model.js:284 js/Model.js:299
+msgid "Moving data .. {0}"
 msgstr ""
 
-#: js/PathFilterModel.js:53
-msgid "path"
+#: js/Model.js:303
+msgid "Please wait"
 msgstr ""
 
-#: js/Model.js:272 js/Model.js:288
-#, python-brace-format
-msgid "Copying data .. {0}"
+#: js/Model.js:529
+msgid "Quick Search"
 msgstr ""
 
-#: js/Model.js:275 js/Model.js:290
-#, python-brace-format
-msgid "Moving data .. {0}"
+#: js/Model.js:531 js/NodeGridPanel.js:149
+msgid "Contenttype"
 msgstr ""
 
-#: js/Model.js:520
-msgid "Quick Search"
+#: js/Expressodriver.js:36
+msgid "Expressodriver"
 msgstr ""
 
-#: js/ExceptionHandler.js:36
+#: js/Expressodriver.js:91
 msgid "Service Unavailable"
 msgstr ""
 
-#: js/ExceptionHandler.js:37
-#, python-brace-format
+#: js/Expressodriver.js:92
 msgid ""
-"The Expressodriver is not configured correctly. Please refer to the {0}Tine "
-"2.0 Admin FAQ{1} for configuration advice or contact your administrator."
+"The Expressodriver is not configured correctly. Please refer to the {0}Tine 2.0 "
+"Admin FAQ{1} for configuration advice or contact your administrator."
 msgstr ""
 
-#: js/ExceptionHandler.js:59
+#: js/Expressodriver.js:114
 msgid "Files already exists"
 msgstr ""
 
-#: js/ExceptionHandler.js:59
+#: js/Expressodriver.js:114
 msgid "Do you want to replace the following file(s)?"
 msgstr ""
 
-#: js/ExceptionHandler.js:103
+#: js/Expressodriver.js:147
 msgid "Failure on create folder"
 msgstr ""
 
-#: js/ExceptionHandler.js:104
+#: js/Expressodriver.js:148
 msgid "Item with this name already exists!"
 msgstr ""
 
-#: js/NodeGridPanel.js:126
+#: js/NodeGridPanel.js:127
 msgid "Tags"
 msgstr ""
 
-#: js/NodeGridPanel.js:141
+#: js/NodeGridPanel.js:142
 msgid "Size"
 msgstr ""
 
-#: js/NodeGridPanel.js:148
-msgid "Contenttype"
-msgstr ""
-
-#: js/NodeGridPanel.js:156
-msgid "Folder"
-msgstr ""
-
-#: js/NodeGridPanel.js:164
-msgid "Creation Time"
-msgstr ""
-
-#: js/NodeGridPanel.js:173
-msgid "Created By"
+#: js/NodeGridPanel.js:166
+msgid "Revision"
 msgstr ""
 
-#: js/NodeGridPanel.js:182
+#: js/NodeGridPanel.js:196
 msgid "Last Modified Time"
 msgstr ""
 
-#: js/NodeGridPanel.js:189
+#: js/NodeGridPanel.js:203
 msgid "Last Modified By"
 msgstr ""
 
-#: js/NodeGridPanel.js:238
-#, python-brace-format
+#: js/NodeGridPanel.js:251
 msgid "The max. Upload Filesize is {0} MB"
 msgstr ""
 
-#: js/NodeGridPanel.js:276
+#: js/NodeGridPanel.js:289
 msgid "Upload"
 msgstr ""
 
-#: js/NodeGridPanel.js:300
+#: js/NodeGridPanel.js:313
 msgid "Properties"
 msgstr ""
 
-#: js/NodeGridPanel.js:311
+#: js/NodeGridPanel.js:324
 msgid "Create Folder"
 msgstr ""
 
-#: js/NodeGridPanel.js:322
+#: js/NodeGridPanel.js:335
 msgid "Folder Up"
 msgstr ""
 
-#: js/NodeGridPanel.js:343 js/NodeGridPanel.js:344 js/NodeGridPanel.js:346
+#: js/NodeGridPanel.js:356 js/NodeGridPanel.js:357 js/NodeGridPanel.js:359
 msgid "Delete"
 msgstr ""
 
-#: js/NodeGridPanel.js:488
+#: js/GridPanel.js:366 js/GridPanel.js:367 js/GridPanel.js:369
+msgid "Rename"
+msgstr ""
+
+#: js/NodeGridPanel.js:501
 msgid "New Folder"
 msgstr ""
 
-#: js/NodeGridPanel.js:488
+#: js/NodeGridPanel.js:501
 msgid "Please enter the name of the new folder:"
 msgstr ""
 
-#: js/NodeGridPanel.js:492
-#, python-brace-format
+#: js/NodeGridPanel.js:505
 msgid "No {0} added"
 msgstr ""
 
-#: js/NodeGridPanel.js:495
-#, python-brace-format
-msgid "Creating {0}..."
+#: Controller/Node.php:318
+msgid "My folders"
 msgstr ""
 
-#: js/Expressodriver.js:38
-msgid "Expressodriver"
+#: Controller/Node.php:325
+msgid "Shared folders"
+msgstr ""
+
+#: Controller/Node.php:332
+msgid "Other users folders"
+msgstr ""
+
+#: Controller/Node/Filesystem.php:348
+msgid "External folders"
+msgstr ""
+
+#: js/GridContextMenu:72
+msgid "Renaming nodes..."
+msgstr ""
+
+#: js/GridContextMenu:175
+msgid "Deleting nodes..."
+msgstr ""
+
+#: js/ExternalAdapter:156
+msgid "Use e-mail as login name"
+msgstr ""
+
+#: js/CredentialsDialog:33
+msgid "Credentials for Expressodriver"
+msgstr ""
+
+#: js/CredentialsDialog:85
+msgid "Authentication success."
+msgstr ""
+
+#: js/CredentialsDialog:96
+msgid "Credentials error"
+msgstr ""
+
+#: js/CredentialsDialog:97
+msgid "Invalid Credentials"
 msgstr ""