plugins/kolab_files

Aleksander Machniak machniak at kolabsys.com
Wed Oct 15 16:09:02 CEST 2014


 plugins/kolab_files/kolab_files.js                            |  285 ++++++++--
 plugins/kolab_files/lib/kolab_files_engine.php                |  137 ++++
 plugins/kolab_files/localization/en_US.inc                    |    7 
 plugins/kolab_files/package.xml                               |    4 
 plugins/kolab_files/skins/larry/style.css                     |   38 +
 plugins/kolab_files/skins/larry/templates/compose_plugin.html |    4 
 plugins/kolab_files/skins/larry/templates/files.html          |   14 
 plugins/kolab_files/skins/larry/templates/message_plugin.html |    4 
 8 files changed, 432 insertions(+), 61 deletions(-)

New commits:
commit 35814a47fcd70507de425555395f65ec321e6236
Author: Aleksander Machniak <machniak at kolabsys.com>
Date:   Tue Oct 14 14:12:09 2014 -0400

    Support multi-driver capabilities of Chwala (#3774)

diff --git a/plugins/kolab_files/kolab_files.js b/plugins/kolab_files/kolab_files.js
index a525c39..898a682 100644
--- a/plugins/kolab_files/kolab_files.js
+++ b/plugins/kolab_files/kolab_files.js
@@ -118,6 +118,7 @@ window.rcmail && rcmail.addEventListener('init', function() {
     else {
       file_api.folder_list();
       file_api.browser_capabilities_check();
+      rcmail.enable_command('folder-mount', rcmail.env.external_sources);
     }
   }
 });
@@ -340,11 +341,7 @@ function kolab_files_folder_create_dialog()
   // show dialog window
   kolab_dialog_show(dialog, {
     title: rcmail.gettext('kolab_files.foldercreate'),
-    buttons: buttons,
-    minWidth: 400,
-    minHeight: 300,
-    width: 500,
-    height: 400
+    buttons: buttons
   });
 
   // Fix submitting form with Enter
@@ -365,6 +362,61 @@ function kolab_files_folder_create_dialog()
   });
 };
 
+// folder mounting dialog
+function kolab_files_folder_mount_dialog()
+{
+  var args = {buttons: {}, title: rcmail.gettext('kolab_files.foldermount')},
+    dialog = $('#files-folder-mount-dialog'),
+    input = $('#folder-mount-name').val('');
+
+  args.buttons[rcmail.gettext('kolab_files.save')] = function () {
+    var args = {}, folder = input.val(),
+      driver = $('input[name="driver"]:checked', dialog).val();
+
+    if (!folder || !driver)
+      return;
+
+    args.folder = folder;
+    args.driver = driver;
+
+    $('#source-' + driver + ' input').each(function() {
+      if (this.name.startsWith(driver + '[')) {
+        args[this.name.substring(driver.length + 1, this.name.length - 1)] = this.value;
+      }
+    });
+
+    file_api.folder_mount(args);
+    kolab_dialog_close(this);
+  };
+
+  args.buttons[rcmail.gettext('kolab_files.cancel')] = function () {
+    kolab_dialog_close(this);
+  };
+
+  // close folderoption menu
+  rcmail.hide_menu('folderoptions');
+
+  // initialize drivers list
+  if (!rcmail.drivers_list_initialized) {
+    rcmail.drivers_list_initialized = true;
+
+    $('td.source', dialog).each(function() {
+      $(this).click(function() {
+        $('td.selected', dialog).removeClass('selected');
+        dialog.find('.driverform').hide();
+        $(this).addClass('selected').find('.driverform').show();
+        $('input[type="radio"]', this).prop('checked', true);
+     });
+   });
+  }
+
+  // show dialog window
+  kolab_dialog_show(dialog, args, function() {
+    $('td.source:first', dialog).click();
+    input.focus();
+  });
+};
+
 // file edition dialog
 function kolab_files_file_edit_dialog(file)
 {
@@ -901,6 +953,11 @@ rcube_webmail.prototype.folder_create = function()
   kolab_files_folder_create_dialog();
 };
 
+rcube_webmail.prototype.folder_mount = function()
+{
+  kolab_files_folder_mount_dialog();
+};
+
 
 /**********************************************************/
 /*********          Files API handler            **********/
@@ -968,36 +1025,10 @@ function kolab_files_ui()
 
     elem.html('').append(list);
 
-    this.env.folders = this.folder_list_parse(response.result);
+    this.env.folders = this.folder_list_parse(response.result ? response.result.list : []);
 
     $.each(this.env.folders, function(i, f) {
-      var row = $('<li class="mailbox"><span class="branch"></span></li>');
-
-      row.attr('id', f.id).data('folder', i)
-        .append($('<span class="name"></span>').text(f.name));
-
-      if (f.depth) {
-        $('span.branch', row).width(15 * f.depth);
-        row.addClass('child');
-      }
-
-      if (f.virtual)
-        row.addClass('virtual');
-      else
-        row.attr('tabindex', 0)
-          .keypress(function(e) { if (e.which == 13 || e.which == 32) file_api.folder_select(i); })
-          .click(function() { file_api.folder_select(i); })
-          .mouseenter(function() {
-            if (rcmail.file_list && rcmail.file_list.drag_active && !$(this).hasClass('selected'))
-              $(this).addClass('droptarget');
-          })
-          .mouseleave(function() {
-            if (rcmail.file_list && rcmail.file_list.drag_active)
-              $(this).removeClass('droptarget');
-          });
-
-      list.append(row);
-
+      list.append(file_api.folder_list_row(i, f));
       if (!first)
         first = i;
     });
@@ -1013,16 +1044,19 @@ function kolab_files_ui()
       list.append(row);
     });
 
-   // select first folder?
-   if (this.env.folder)
-     this.folder_select(this.env.folder);
-   else if (this.env.collection)
-     this.folder_select(this.env.collection, true);
-   else if (first)
-     this.folder_select(first);
+    // select first folder?
+    if (this.env.folder)
+      this.folder_select(this.env.folder);
+    else if (this.env.collection)
+      this.folder_select(this.env.collection, true);
+    else if (first)
+      this.folder_select(first);
 
     // add tree icons
     this.folder_list_tree(this.env.folders);
+
+    // handle authentication errors on external sources
+    this.folder_list_auth_errors(response.result);
   };
 
   this.folder_select = function(folder, is_collection)
@@ -1070,6 +1104,36 @@ function kolab_files_ui()
     this.env.collection = null;
   };
 
+  this.folder_list_row = function(i, folder)
+  {
+    var row = $('<li class="mailbox"><span class="branch"></span></li>');
+
+    row.attr('id', folder.id).data('folder', i)
+      .append($('<span class="name"></span>').text(folder.name));
+
+    if (folder.depth) {
+      $('span.branch', row).width(15 * folder.depth);
+      row.addClass('child');
+    }
+
+    if (folder.virtual)
+      row.addClass('virtual');
+    else
+      row.attr('tabindex', 0)
+        .keypress(function(e) { if (e.which == 13 || e.which == 32) file_api.folder_select(i); })
+        .click(function() { file_api.folder_select(i); })
+        .mouseenter(function() {
+          if (rcmail.file_list && rcmail.file_list.drag_active && !$(this).hasClass('selected'))
+            $(this).addClass('droptarget');
+        })
+        .mouseleave(function() {
+          if (rcmail.file_list && rcmail.file_list.drag_active)
+            $(this).removeClass('droptarget');
+        });
+
+    return row;
+  };
+
   // folder create request
   this.folder_create = function(folder)
   {
@@ -1089,6 +1153,25 @@ function kolab_files_ui()
     this.folder_list();
   };
 
+  // folder mount (external storage) request
+  this.folder_mount = function(data)
+  {
+    this.req = this.set_busy(true, 'kolab_files.foldermounting');
+    this.request('folder_create', data, 'folder_mount_response');
+  };
+
+  // folder create response handler
+  this.folder_mount_response = function(response)
+  {
+    if (!this.response(response))
+      return;
+
+    this.display_message('kolab_files.foldermountnotice', 'confirmation');
+
+    // refresh folders list
+    this.folder_list();
+  };
+
   // folder delete request
   this.folder_delete = function(folder)
   {
@@ -1727,4 +1810,126 @@ function kolab_files_ui()
     }
   };
 
+  // handle auth errors on folder list
+  this.folder_list_auth_errors = function(result)
+  {
+    if (result && result.auth_errors) {
+      if (!this.auth_errors)
+        this.auth_errors = {};
+
+      $.extend(this.auth_errors, result.auth_errors);
+    }
+
+    // ask for password to the first storage on the list
+    $.each(this.auth_errors || [], function(i, v) {
+      file_api.folder_list_auth_dialog(i, v);
+      return false;
+    });
+  };
+
+  // create dialog for user credentials of external storage
+  this.folder_list_auth_dialog = function(label, driver)
+  {
+    var args = {width: 400, height: 300, buttons: {}},
+      dialog = $('#files-folder-auth-dialog'),
+      content = this.folder_list_auth_form(driver);
+
+    dialog.find('table.propform').remove();
+    dialog.append(content);
+
+    args.buttons[this.t('kolab_files.save')] = function() {
+      var data = {folder: label, list: 1};
+
+      $('input', dialog).each(function() {
+        data[this.name] = this.value;
+      });
+
+      file_api.open_dialog = this;
+      file_api.req = file_api.set_busy(true, 'kolab_files.authenticating');
+      file_api.request('folder_auth', data, 'folder_auth_response');
+    };
+
+    args.buttons[this.t('kolab_files.cancel')] = function() {
+      delete file_api.auth_errors[label];
+      kolab_dialog_close(this);
+      // go to the next one
+      file_api.folder_list_auth_errors();
+    };
+
+    args.title = this.t('kolab_files.folderauthtitle').replace('$title', label);
+
+    // show dialog window
+    kolab_dialog_show(dialog, args, function() {
+      // focus first empty input
+      $('input', dialog).each(function() {
+        if (!this.value) {
+          this.focus();
+          return false;
+        }
+      });
+    });
+  };
+
+  // folder_auth handler
+  this.folder_auth_response = function(response)
+  {
+    if (!this.response(response))
+      return;
+
+    var cnt = 0, folders,
+      folder = response.result.folder,
+      parent = $('#' + this.env.folders[folder].id);
+
+    // try parent window if the folder element does not exist
+    if (!parent.length && window.parent && window.parent.rcmail) {
+      parent = $('#' + this.env.folders[folder].id, window.parent.document.body);
+    }
+
+    delete this.auth_errors[folder];
+    kolab_dialog_close(this.open_dialog);
+
+    // go to the next one
+    this.folder_list_auth_errors();
+
+    // count folders on the list
+    $.each(this.env.folders, function() { cnt++; });
+
+    // parse result
+    folders = this.folder_list_parse(response.result.list, cnt);
+    delete folders[folder]; // remove root added in folder_list_parse()
+
+    // add folders from the external source to the list
+    $.each(folders, function(i, f) {
+      var row = file_api.folder_list_row(i, f);
+      parent.after(row);
+      parent = row;
+    });
+
+    // add tree icons
+    this.folder_list_tree(folders);
+
+    $.extend(this.env.folders, folders);
+  };
+
+  // returns content of the external storage authentication form
+  this.folder_list_auth_form = function(driver)
+  {
+    var rows = [];
+
+    $.each(driver.form, function(fi, fv) {
+      var id = 'authinput' + fi,
+        attrs = {type: fi.match(/pass/) ? 'password' : 'text', size: 25, name: fi, id: id},
+        input = $('<input>').attr(attrs);
+
+      if (driver.form_values && driver.form_values[fi])
+        input.attr({value: driver.form_values[fi]});
+
+      rows.push($('<tr>')
+        .append($('<td class="title">').append($('<label>').attr('for', id).text(fv)))
+        .append($('<td>').append(input))
+      );
+    });
+
+    return $('<table class="propform">').append(rows);
+  };
 };
diff --git a/plugins/kolab_files/lib/kolab_files_engine.php b/plugins/kolab_files/lib/kolab_files_engine.php
index 8cb408b..4dce8eb 100644
--- a/plugins/kolab_files/lib/kolab_files_engine.php
+++ b/plugins/kolab_files/lib/kolab_files_engine.php
@@ -26,7 +26,7 @@ class kolab_files_engine
 {
     private $plugin;
     private $rc;
-    private $timeout = 60;
+    private $timeout = 600;
     private $sort_cols = array('name', 'mtime', 'size');
 
     /**
@@ -34,9 +34,10 @@ class kolab_files_engine
      */
     public function __construct($plugin, $url)
     {
-        $this->url    = rcube_utils::resolve_url($url);
-        $this->plugin = $plugin;
-        $this->rc     = $plugin->rc;
+        $this->url     = rcube_utils::resolve_url($url);
+        $this->plugin  = $plugin;
+        $this->rc      = $plugin->rc;
+        $this->timeout = $this->rc->config->get('session_lifetime') * 60;
     }
 
     /**
@@ -85,11 +86,15 @@ class kolab_files_engine
 
             $this->plugin->add_label('save', 'cancel', 'saveto',
                 'saveall', 'fromcloud', 'attachsel', 'selectfiles', 'attaching',
-                'collection_audio', 'collection_video', 'collection_image', 'collection_document'
+                'collection_audio', 'collection_video', 'collection_image', 'collection_document',
+                'folderauthtitle', 'authenticating'
             );
         }
         else if ($this->rc->task == 'files') {
             $template = 'files';
+
+            // get list of external sources
+            $this->get_external_storage_drivers();
         }
 
         // add taskbar button
@@ -114,6 +119,7 @@ class kolab_files_engine
             // register template objects for dialogs (and main interface)
             $this->rc->output->add_handlers(array(
                 'folder-create-form' => array($this, 'folder_create_form'),
+                'folder-mount-form'  => array($this, 'folder_mount_form'),
                 'file-search-form'   => array($this, 'file_search_form'),
                 'file-edit-form'     => array($this, 'file_edit_form'),
                 'filelist'           => array($this, 'file_list'),
@@ -165,9 +171,9 @@ class kolab_files_engine
         $select_parent = new html_select(array('id' => 'folder-parent', 'name' => 'parent'));
         $table         = new html_table(array('cols' => 2, 'class' => 'propform'));
 
-        $table->add('title', html::label('folder-name', Q($this->plugin->gettext('foldername'))));
+        $table->add('title', html::label('folder-name', rcube::Q($this->plugin->gettext('foldername'))));
         $table->add(null, $input_name->show());
-        $table->add('title', html::label('folder-parent', Q($this->plugin->gettext('folderinside'))));
+        $table->add('title', html::label('folder-parent', rcube::Q($this->plugin->gettext('folderinside'))));
         $table->add(null, $select_parent->show());
 
         $out = $table->show();
@@ -184,6 +190,72 @@ class kolab_files_engine
     }
 
     /**
+     * Template object for folder mounting form
+     */
+    public function folder_mount_form($attrib)
+    {
+        $sources = $this->rc->output->get_env('external_sources');
+
+        if (empty($sources) || !is_array($sources)) {
+            return '';
+        }
+
+        $attrib['name'] = 'folder-mount-form';
+        if (empty($attrib['id'])) {
+            $attrib['id'] = 'folder-mount-form';
+        }
+
+        // build form content
+        $table        = new html_table(array('cols' => 2, 'class' => 'propform'));
+        $input_name   = new html_inputfield(array('id' => 'folder-mount-name', 'name' => 'name', 'size' => 30));
+        $input_driver = new html_radiobutton(array('name' => 'driver', 'size' => 30));
+
+        $table->add('title', html::label('folder-mount-name', rcube::Q($this->plugin->gettext('name'))));
+        $table->add(null, $input_name->show());
+
+        foreach ($sources as $key => $source) {
+            $id    = 'source-' . $key;
+            $form  = new html_table(array('cols' => 2, 'class' => 'propform driverform'));
+
+            foreach ((array) $source['form'] as $idx => $label) {
+                $iid = $id . '-' . $idx;
+                $type  = stripos($idx, 'pass') !== false ? 'html_passwordfield' : 'html_inputfield';
+                $input = new $type(array('size' => 30));
+
+                $form->add('title', html::label($iid, rcube::Q($label)));
+                $form->add(null, $input->show('', array(
+                        'id'   => $iid,
+                        'name' => $key . '[' . $idx . ']'
+                )));
+            }
+
+            $row = $input_driver->show(null, array('value' => $key))
+                . html::img(array('src' => $source['image'], 'alt' => $key, 'title' => $source['name']))
+                . html::div(null, html::span('name', rcube::Q($source['name']))
+                    . html::br()
+                    . html::span('description', rcube::Q($source['description']))
+                    . $form->show()
+                );
+
+            $table->add(array('id' => $id, 'colspan' => 2, 'class' => 'source'), $row);
+        }
+
+        $out = $table->show();
+
+        // add form tag around text field
+        if (empty($attrib['form'])) {
+            $out = $this->rc->output->form_tag($attrib, $out);
+        }
+
+        $this->plugin->add_label('foldermounting', 'foldermountnotice', 'foldermount',
+            'save', 'cancel', 'folderauthtitle', 'authenticating'
+        );
+        $this->rc->output->add_gui_object('folder-mount-form', $attrib['id']);
+
+        return $out;
+    }
+
+    /**
      * Template object for file_edit form
      */
     public function file_edit_form($attrib)
@@ -193,10 +265,10 @@ class kolab_files_engine
             $attrib['id'] = 'file-edit-form';
         }
 
-        $input_name    = new html_inputfield(array('id' => 'file-name', 'name' => 'name', 'size' => 30));
-        $table         = new html_table(array('cols' => 2, 'class' => 'propform'));
+        $input_name = new html_inputfield(array('id' => 'file-name', 'name' => 'name', 'size' => 30));
+        $table      = new html_table(array('cols' => 2, 'class' => 'propform'));
 
-        $table->add('title', html::label('file-name', Q($this->plugin->gettext('filename'))));
+        $table->add('title', html::label('file-name', rcube::Q($this->plugin->gettext('filename'))));
         $table->add(null, $input_name->show());
 
         $out = $table->show();
@@ -367,7 +439,7 @@ class kolab_files_engine
                 $col_name = $list_menu;
                 break;
             default:
-                $col_name = Q($this->plugin->gettext($col));
+                $col_name = rcube::Q($this->plugin->gettext($col));
             }
 
             // make sort links
@@ -511,6 +583,7 @@ class kolab_files_engine
             }
         }
         catch (Exception $e) {
+            rcube::raise_error($e, true, false);
             $quota = array('total' => 0, 'percent' => 0);
         }
 
@@ -530,12 +603,12 @@ class kolab_files_engine
         $time  = $_SESSION['kolab_files_time'];
 
         if ($token && time() - $this->timeout < $time) {
-            return $token;
+            if (time() - $time <= $this->timeout / 2) {
+                return $token;
+            }
         }
 
-        if (!($request = $this->get_request())) {
-            return $token;
-        }
+        $request = $this->get_request(array('method' => 'ping'), $token);
 
         try {
             $url = $request->getUrl();
@@ -950,7 +1023,7 @@ class kolab_files_engine
                     ));
                 }
                 else {
-                    $button = Q($this->rc->gettext('delete'));
+                    $button = rcube::Q($this->rc->gettext('delete'));
                 }
 
                 $content = html::a(array(
@@ -960,7 +1033,7 @@ class kolab_files_engine
                     'class' => 'delete',
                 ), $button);
 
-                $content .= Q($attachment['name']);
+                $content .= rcube::Q($attachment['name']);
 
                 $this->rc->output->command('add2attachment_list', "rcmfile$id", array(
                     'html'      => $content,
@@ -1017,4 +1090,34 @@ class kolab_files_engine
 
         return $mimetypes;
     }
+
+    /**
+     * Get list of available external storage drivers
+     */
+    protected function get_external_storage_drivers()
+    {
+        // first get configured sources from Chwala
+        $token   = $this->get_api_token();
+        $request = $this->get_request(array('method' => 'folder_types'), $token);
+
+        // send request to the API
+        try {
+            $response = $request->send();
+            $status   = $response->getStatus();
+            $body     = @json_decode($response->getBody(), true);
+
+            if ($status == 200 && $body['status'] == 'OK') {
+                $sources = $body['result'];
+            }
+            else {
+                throw new Exception($body['reason']);
+            }
+        }
+        catch (Exception $e) {
+            rcube::raise_error($e, true, false);
+            return;
+        }
+
+        $this->rc->output->set_env('external_sources', $sources);
+    }
 }
diff --git a/plugins/kolab_files/localization/en_US.inc b/plugins/kolab_files/localization/en_US.inc
index a1fc814..3ecd1fc 100644
--- a/plugins/kolab_files/localization/en_US.inc
+++ b/plugins/kolab_files/localization/en_US.inc
@@ -20,6 +20,7 @@ $labels['fromcloud'] = 'From cloud...';
 $labels['selectfiles'] = 'Select file(s) to attach...';
 $labels['attachsel'] = 'Attach selected';
 $labels['foldercreate'] = 'Create folder';
+$labels['foldermount'] = 'Add storage';
 $labels['folderrename'] = 'Rename folder';
 $labels['folderdelete'] = 'Delete folder';
 
@@ -51,11 +52,15 @@ $labels['collection_document'] = 'Documents';
 
 $labels['uploading'] = 'Uploading file(s)...';
 $labels['attaching'] = 'Attaching file(s)...';
+$labels['authenticating'] = 'Authenticating...';
 $labels['foldercreating'] = 'Creating folder...';
+$labels['foldermounting'] = 'Adding external storage...';
 $labels['folderdeleting'] = 'Deleting folder...';
 $labels['folderdeleteconfirm'] = 'Are you sure you want to delete selected folder?';
 $labels['folderdeletenotice'] = 'Folder deleted successfully.';
 $labels['foldercreatenotice'] = 'Folder created successfully.';
+$labels['foldermountnotice'] = 'Storage added successfully.';
+$labels['folderauthtitle'] = 'Logon to $title';
 $labels['saveallnotice'] = 'Successfully saved $n file(s).';
 $labels['saveallerror'] = 'Saving $n file(s) failed.';
 $labels['attacherror'] = 'Failed to attach file(s) from the cloud';
@@ -82,6 +87,8 @@ $labels['arialabellistoptions'] = 'Files list options';
 $labels['arialabelfolderoptions'] = 'Folder actions';
 $labels['arialabelfileeditform'] = 'File editing form';
 $labels['arialabelfoldercreateform'] = 'Folder creation form';
+$labels['arialabelfoldermountform'] = 'External storage form';
+$labels['arialabelfolderauthform'] = 'External storage authentication form';
 $labels['arialabelfolderlist'] = 'Folder/Collection selection';
 $labels['arialabelfileselectdialog'] = 'File selection dialog';
 $labels['arialabelattachmentoptions'] = 'Attachment save options';
diff --git a/plugins/kolab_files/package.xml b/plugins/kolab_files/package.xml
index 8ac6e5b..e8552ea 100644
--- a/plugins/kolab_files/package.xml
+++ b/plugins/kolab_files/package.xml
@@ -13,9 +13,9 @@
 		<email>machniak at kolabsys.com</email>
 		<active>yes</active>
 	</lead>
-	<date>2013-08-25</date>
+	<date>2014-10-15</date>
 	<version>
-		<release>1.0</release>
+		<release>1.1</release>
 		<api>1.0</api>
 	</version>
 	<stability>
diff --git a/plugins/kolab_files/skins/larry/style.css b/plugins/kolab_files/skins/larry/style.css
index 20621e8..de0735c 100644
--- a/plugins/kolab_files/skins/larry/style.css
+++ b/plugins/kolab_files/skins/larry/style.css
@@ -293,6 +293,8 @@
 #files-dialog,
 #files-compose-dialog,
 #files-file-edit-dialog,
+#files-folder-mount-dialog,
+#files-folder-auth-dialog,
 #files-folder-create-dialog {
   display: none;
 }
@@ -362,3 +364,39 @@ a.filesaveall {
 ul.toolbarmenu li span.saveas {
   background: url(images/buttons.png) -5px -253px no-repeat;
 }
+
+
+table.propform td.source.selected {
+  background-color: #c7e3ef;
+}
+
+table.propform td.source div {
+  display: inline-block;
+  padding-left: 3px;
+  vertical-align: middle;
+}
+
+table.propform td.source .name {
+  font-weight: bold;
+}
+
+table.propform td.source .description {
+  font-size: 9px;
+  color: #666;
+}
+
+table.propform td.source img {
+  vertical-align: middle;
+  background-color: #e0e0e0;
+  border-radius: 3px;
+  margin: 3px;
+  background-image: -moz-linear-gradient(center top, #888, #333);
+  background-image: -webkit-linear-gradient(top, #888, #333);
+  background-image: -ms-linear-gradient(top, #888, #333);
+}
+
+table.propform td.source table.propform td {
+  border-bottom: 0;
+  padding: 2px 10px;
+  background-color: inherit;
+}
diff --git a/plugins/kolab_files/skins/larry/templates/compose_plugin.html b/plugins/kolab_files/skins/larry/templates/compose_plugin.html
index 613a24a..ceb56cc 100644
--- a/plugins/kolab_files/skins/larry/templates/compose_plugin.html
+++ b/plugins/kolab_files/skins/larry/templates/compose_plugin.html
@@ -25,3 +25,7 @@
         <li role="menuitem"><label><input type="checkbox" name="all_folders" value="1" id="search_all_folders" /> <span><roundcube:label name="kolab_files.allfolders" /></span></label></li>
     </ul>
 </div>
+
+<div id="files-folder-auth-dialog" role="dialog" aria-labelledby="aria-label-folderauthform" aria-hidden="true">
+    <h3 id="aria-label-folderauthform" class="voice"><roundcube:label name="kolab_files.arialabelfolderauthform" /></h3>
+</div>
diff --git a/plugins/kolab_files/skins/larry/templates/files.html b/plugins/kolab_files/skins/larry/templates/files.html
index e6e2500..e33d1d0 100644
--- a/plugins/kolab_files/skins/larry/templates/files.html
+++ b/plugins/kolab_files/skins/larry/templates/files.html
@@ -26,7 +26,7 @@
 <div id="quicksearchbar" class="quicksearchbox" role="search" aria-labelledby="aria-label-searchform">
     <h2 id="aria-label-searchform" class="voice"><roundcube:label name="kolab_files.arialabelsearchform" /></h2>
     <label for="quicksearchbox" class="voice"><roundcube:label name="arialabelquicksearchbox" /></label>
-    <roundcube:button name="filesearchmenulink" id="filesearchmenulink" class="iconbutton searchoptions" onclick="return UI.toggle_popup('filesearchmenu', event)" title="searchmod" label="options" aria-haspopup="true" aria-expanded="false" aria-owns="filesearchmenu-menu" />
+    <roundcube:button name="filesearchmenulink" id="filesearchmenulink" class="iconbutton searchoptions" onclick="UI.toggle_popup('filesearchmenu', event); return false" title="searchmod" label="options" aria-haspopup="true" aria-expanded="false" aria-owns="filesearchmenu-menu" />
     <roundcube:object name="file-search-form" id="quicksearchbox" />
     <roundcube:button command="files-search-reset" id="searchreset" class="iconbutton reset" title="resetsearch" label="resetsearch" />
 </div>
@@ -60,6 +60,9 @@
         <li role="menuitem"><roundcube:button command="files-folder-edit" label="edit" classAct="active" /></li>
 -->
         <li role="menuitem"><roundcube:button command="files-folder-delete" label="delete" classAct="active" /></li>
+        <roundcube:if condition="!empty(env:external_sources)" />
+        <li role="menuitem"><roundcube:button command="folder-mount" label="kolab_files.foldermount" classAct="active" /></li>
+        <roundcube:endif />
         <li role="menuitem"><roundcube:button command="folders" task="settings" type="link" label="managefolders" classAct="active" /></li>
         <roundcube:container name="filesfolderoptions" id="folderoptionsmenu" />
     </ul>
@@ -69,14 +72,21 @@
     <h3 id="aria-label-foldercreateform" class="voice"><roundcube:label name="kolab_files.arialabelfoldercreateform" /></h3>
     <roundcube:object name="folder-create-form" />
 </div>
+<div id="files-folder-mount-dialog" role="dialog" aria-labelledby="aria-label-foldermountform" aria-hidden="true">
+    <h3 id="aria-label-foldermountform" class="voice"><roundcube:label name="kolab_files.arialabelfoldermountform" /></h3>
+    <roundcube:object name="folder-mount-form" />
+</div>
 <div id="files-file-edit-dialog" role="dialog" aria-labelledby="aria-label-fileeditform" aria-hidden="true">
     <h3 id="aria-label-fileeditform" class="voice"><roundcube:label name="kolab_files.arialabelfileeditform" /></h3>
     <roundcube:object name="file-edit-form" />
 </div>
+<div id="files-folder-auth-dialog" role="dialog" aria-labelledby="aria-label-folderauthform" aria-hidden="true">
+    <h3 id="aria-label-folderauthform" class="voice"><roundcube:label name="kolab_files.arialabelfolderauthform" /></h3>
+</div>
 
 <div id="listoptions" class="propform popupdialog" data-editable="true" role="dialog" aria-labelledby="aria-label-listoptions" aria-hidden="true">
     <h3 id="aria-label-listoptions" class="voice"><roundcube:label name="kolab_files.arialabellistoptions" /></h3>
-<roundcube:if condition="!in_array('kolab_files_list_cols', (array)config:dont_override)" />
+    <roundcube:if condition="!in_array('kolab_files_list_cols', (array)config:dont_override)" />
     <fieldset class="floating">
         <legend><roundcube:label name="listcolumns" /></legend>
         <ul class="proplist">
diff --git a/plugins/kolab_files/skins/larry/templates/message_plugin.html b/plugins/kolab_files/skins/larry/templates/message_plugin.html
index eeb78fd..26c2525 100644
--- a/plugins/kolab_files/skins/larry/templates/message_plugin.html
+++ b/plugins/kolab_files/skins/larry/templates/message_plugin.html
@@ -18,4 +18,8 @@
     <roundcube:object name="folder-create-form" />
 </div>
 
+<div id="files-folder-auth-dialog" role="dialog" aria-labelledby="aria-label-folderauthform" aria-hidden="true">
+    <h3 id="aria-label-folderauthform" class="voice"><roundcube:label name="kolab_files.arialabelfolderauthform" /></h3>
+</div>
+
 <script src="plugins/kolab_files/skins/larry/ui.js" type="text/javascript"></script>




More information about the commits mailing list