From e9ecd49f7460f571e2bf13161038371e2d5f8bfb Mon Sep 17 00:00:00 2001
From: Aleksander Machniak <alec@alec.pl>
Date: Mon, 11 Aug 2014 07:11:10 -0400
Subject: [PATCH] Added namespace filter in Folder Manager

---
 CHANGELOG                             |    1 
 program/js/treelist.js                |    7 +++
 skins/classic/templates/folders.html  |    1 
 program/steps/settings/folders.inc    |   49 +++++++++++++++++++++++-
 skins/classic/functions.js            |    5 +-
 skins/larry/settings.css              |    5 ++
 skins/classic/settings.css            |    5 ++
 program/localization/en_US/labels.inc |    3 +
 skins/larry/templates/folders.html    |    1 
 skins/larry/styles.css                |    1 
 program/js/app.js                     |   36 +++++++++++++++++-
 skins/larry/ui.js                     |    5 +-
 12 files changed, 109 insertions(+), 10 deletions(-)

diff --git a/CHANGELOG b/CHANGELOG
index 5148767..0fe3cec 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,6 +1,7 @@
 CHANGELOG Roundcube Webmail
 ===========================
 
+- Added namespace filter in Folder Manager
 - Added folder searching in Folder Manager
 - Added config option 'imap_log_session' to enable Roundcube <-> IMAP session ID logging
 - Added config option 'log_session_id' to control the lengh of the session identifer in logs
diff --git a/program/js/app.js b/program/js/app.js
index c643188..5a87347 100644
--- a/program/js/app.js
+++ b/program/js/app.js
@@ -5862,8 +5862,9 @@
     row.attr({id: 'rcmli' + this.html_identifier_encode(id), 'class': class_name});
 
     if (!refrow || !refrow.length) {
-      // remove old subfolders and toggle
+      // remove old data, subfolders and toggle
       $('ul,div.treetoggle', row).remove();
+      row.removeData('filtered');
     }
 
     // set folder name
@@ -5990,7 +5991,7 @@
       this.subscription_list.expand(this.folder_id2name(parent.id));
     }
 
-    row = row.get(0);
+    row = row.show().get(0);
     if (row.scrollIntoView)
       row.scrollIntoView();
 
@@ -6134,6 +6135,37 @@
     $('#folder-size').replaceWith(size);
   };
 
+  // filter folders by namespace
+  this.folder_filter = function(prefix)
+  {
+    this.subscription_list.reset_search();
+
+    this.subscription_list.container.children('li').each(function() {
+      var i, folder = ref.folder_id2name(this.id);
+      // show all folders
+      if (prefix == '---') {
+      }
+      // got namespace prefix
+      else if (prefix) {
+        if (folder !== prefix) {
+          $(this).data('filtered', true).hide();
+          return
+        }
+      }
+      // no namespace prefix, filter out all other namespaces
+      else {
+        // first get all namespace roots
+        for (i in ref.env.ns_roots) {
+          if (folder === ref.env.ns_roots[i]) {
+            $(this).data('filtered', true).hide();
+            return;
+          }
+        }
+      }
+
+      $(this).removeData('filtered').show();
+    });
+  };
 
   /*********************************************************/
   /*********           GUI functionality           *********/
diff --git a/program/js/treelist.js b/program/js/treelist.js
index cc1880d..5e6d326 100644
--- a/program/js/treelist.js
+++ b/program/js/treelist.js
@@ -522,6 +522,11 @@
         var li, sli;
         if (!node.virtual && !node.deleted && String(node.text).toLowerCase().indexOf(q) >= 0 && hits.indexOf(node.id) < 0) {
           li = id2dom(node.id);
+
+          // skip already filtered nodes
+          if (li.data('filtered'))
+            return;
+
           sli = $('<li>')
             .attr('id', li.attr('id') + '--xsR')
             .attr('class', li.attr('class'))
@@ -566,7 +571,7 @@
       searchfield.val('');
 
     $(container).children('li.searchresult__').remove();
-    $(container).children('li').show();
+    $(container).children('li').filter(function() { return !$(this).data('filtered'); }).show();
 
     search_active = false;
 
diff --git a/program/localization/en_US/labels.inc b/program/localization/en_US/labels.inc
index e3fe76b..449b278 100644
--- a/program/localization/en_US/labels.inc
+++ b/program/localization/en_US/labels.inc
@@ -549,6 +549,9 @@
 $labels['otherfolder'] = 'Other User\'s Folder';
 $labels['sharedfolder'] = 'Public Folder';
 $labels['findfolders'] = 'Find folders';
+$labels['namespace.personal'] = 'Personal';
+$labels['namespace.other'] = 'Other users';
+$labels['namespace.shared'] = 'Shared';
 
 $labels['sortby'] = 'Sort by';
 $labels['sortasc']  = 'Sort ascending';
diff --git a/program/steps/settings/folders.inc b/program/steps/settings/folders.inc
index 061cd88..14e41d6 100644
--- a/program/steps/settings/folders.inc
+++ b/program/steps/settings/folders.inc
@@ -181,8 +181,9 @@
 
 // register UI objects
 $OUTPUT->add_handlers(array(
-    'foldersubscription' => 'rcube_subscription_form',
+    'foldersubscription' => 'rcmail_subscription_form',
     'folderframe'        => 'rcmail_folder_frame',
+    'folderfilter'       => 'rcmail_folder_filter',
     'quotadisplay'       => array($RCMAIL, 'quota_display'),
 ));
 
@@ -190,7 +191,7 @@
 
 
 // build table with all folders listed by server
-function rcube_subscription_form($attrib)
+function rcmail_subscription_form($attrib)
 {
     global $RCMAIL, $OUTPUT;
 
@@ -424,6 +425,50 @@
     return $OUTPUT->frame($attrib, true);
 }
 
+function rcmail_folder_filter($attrib)
+{
+    global $RCMAIL;
+
+    $storage   = $RCMAIL->get_storage();
+    $namespace = $storage->get_namespace();
+
+    if (empty($namespace['personal']) && empty($namespace['shared']) && empty($namespace['other'])) {
+        return '';
+    }
+
+    if (!$attrib['id']) {
+        $attrib['id'] = 'rcmfolderfilter';
+    }
+
+    $attrib['onchange'] = rcmail_output::JS_OBJECT_NAME . '.folder_filter(this.value)';
+
+    $roots  = array();
+    $select = new html_select($attrib);
+    $select->add($RCMAIL->gettext('all'), '---');
+
+    foreach (array_keys($namespace) as $type) {
+        foreach ((array)$namespace[$type] as $ns) {
+            $root  = rtrim($ns[0], $ns[1]);
+            $label = $RCMAIL->gettext('namespace.' . $type);
+
+            if (count($namespace[$type]) > 1) {
+                $label .= ' (' . rcube_charset::convert($root, 'UTF7-IMAP', RCUBE_CHARSET) . ')';
+            }
+
+            $select->add($label, $root);
+
+            if (strlen($root)) {
+                $roots[] = $root;
+            }
+        }
+    }
+
+    $RCMAIL->output->add_gui_object('foldersfilter', $attrib['id']);
+    $RCMAIL->output->set_env('ns_roots', $roots);
+
+    return $select->show();
+}
+
 function rcmail_rename_folder($oldname, $newname)
 {
     global $RCMAIL;
diff --git a/skins/classic/functions.js b/skins/classic/functions.js
index f17f393..2bf1538 100644
--- a/skins/classic/functions.js
+++ b/skins/classic/functions.js
@@ -644,13 +644,14 @@
   $('.boxtitle a.search', container).click(function(e) {
     var title = $('.boxtitle', container),
       box = $('.listsearchbox', container),
-      dir = box.is(':visible') ? -1 : 1;
+      dir = box.is(':visible') ? -1 : 1,
+      height = 24 + ($('select', box).length ? 24 : 0);
 
     box.slideToggle({
       duration: 160,
       progress: function(animation, progress) {
         if (dir < 0) progress = 1 - progress;
-          $('.boxlistcontent', container).css('top', (title.outerHeight() + 24 * progress) + 'px');
+          $('.boxlistcontent', container).css('top', (title.outerHeight() + height * progress) + 'px');
       },
       complete: function() {
         box.toggleClass('expanded');
diff --git a/skins/classic/settings.css b/skins/classic/settings.css
index cd250d5..9dda7bb 100644
--- a/skins/classic/settings.css
+++ b/skins/classic/settings.css
@@ -21,6 +21,11 @@
   text-indent: 50000px;
 }
 
+.listsearchbox select {
+  width: 100%;
+  margin: 1px 0;
+}
+
 #identities-table,
 #sections-table
 {
diff --git a/skins/classic/templates/folders.html b/skins/classic/templates/folders.html
index 8ed0b5f..ac82430 100644
--- a/skins/classic/templates/folders.html
+++ b/skins/classic/templates/folders.html
@@ -29,6 +29,7 @@
         <a class="iconbutton searchicon"></a>
         <roundcube:button command="reset-foldersearch" id="folderlistsearch-reset" class="iconbutton reset" title="resetsearch" width="13" height="13" />
     </div>
+    <roundcube:object name="folderfilter" id="folderlist-filter" />
 </div>
 <div id="folderlist-content" class="boxlistcontent">
     <roundcube:object name="foldersubscription" form="subscriptionform" id="subscription-table"
diff --git a/skins/larry/settings.css b/skins/larry/settings.css
index cda47b6..b277998 100644
--- a/skins/larry/settings.css
+++ b/skins/larry/settings.css
@@ -224,6 +224,11 @@
 	top: 34px;
 }
 
+.listsearchbox select {
+	width: 100%;
+	margin: 3px 0;
+}
+
 #folderslist,
 #identitieslist {
 	position: absolute;
diff --git a/skins/larry/styles.css b/skins/larry/styles.css
index d7fd588..b2b4f9a 100644
--- a/skins/larry/styles.css
+++ b/skins/larry/styles.css
@@ -1957,7 +1957,6 @@
 }
 
 .listsearchbox {
-	position: relative;
 	padding: 4px;
 	background: #c7e3ef;
 	display: none;
diff --git a/skins/larry/templates/folders.html b/skins/larry/templates/folders.html
index 3d016ca..977dff4 100644
--- a/skins/larry/templates/folders.html
+++ b/skins/larry/templates/folders.html
@@ -29,6 +29,7 @@
 		<a class="iconbutton searchicon"></a>
 		<roundcube:button command="reset-foldersearch" id="folderlistsearch-reset" class="iconbutton reset" title="resetsearch" label="resetsearch" />
 	</div>
+	<roundcube:object name="folderfilter" id="folderlist-filter" />
 </div>
 <div id="folderslist-content" class="scroller withfooter">
 	<roundcube:object name="foldersubscription" form="subscriptionform" id="subscription-table" class="treelist listing folderlist" />
diff --git a/skins/larry/ui.js b/skins/larry/ui.js
index 1eb8e89..7b08aea 100644
--- a/skins/larry/ui.js
+++ b/skins/larry/ui.js
@@ -540,13 +540,14 @@
     $('.boxtitle a.search', container).click(function(e) {
       var title = $('.boxtitle', container),
         box = $('.listsearchbox', container),
-        dir = box.is(':visible') ? -1 : 1;
+        dir = box.is(':visible') ? -1 : 1,
+        height = 34 + ($('select', box).length ? 24 : 0);
 
       box.slideToggle({
         duration: 160,
         progress: function(animation, progress) {
           if (dir < 0) progress = 1 - progress;
-            $('.scroller', container).css('top', (title.outerHeight() + 34 * progress) + 'px');
+            $('.scroller', container).css('top', (title.outerHeight() + height * progress) + 'px');
         },
         complete: function() {
           box.toggleClass('expanded');

--
Gitblit v1.9.1