From 66233b76c83b8e31bc1ff301352393299130a18c Mon Sep 17 00:00:00 2001
From: Aleksander Machniak <alec@alec.pl>
Date: Fri, 08 Aug 2014 06:52:26 -0400
Subject: [PATCH] Added folder searching in Folder Manager

---
 CHANGELOG                             |    1 
 program/js/treelist.js                |    3 
 skins/classic/templates/folders.html  |   12 +++
 skins/classic/functions.js            |   35 +++++++++++
 skins/larry/settings.css              |   13 ++++
 skins/classic/settings.css            |   10 +++
 program/localization/en_US/labels.inc |   24 ++++---
 skins/classic/common.css              |   19 ++++++
 skins/larry/templates/folders.html    |   14 ++++
 skins/larry/styles.css                |    1 
 program/js/app.js                     |    4 +
 skins/larry/ui.js                     |   32 ++++++++++
 12 files changed, 154 insertions(+), 14 deletions(-)

diff --git a/CHANGELOG b/CHANGELOG
index 35744b4..ea50a53 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,6 +1,7 @@
 CHANGELOG Roundcube Webmail
 ===========================
 
+- 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
 - Implemented 'storage_connected' API hook after successful IMAP login (#1490025)
diff --git a/program/js/app.js b/program/js/app.js
index 8ac5ca2..346c4f7 100644
--- a/program/js/app.js
+++ b/program/js/app.js
@@ -5743,13 +5743,15 @@
         selectable: true,
         id_prefix: 'rcmli',
         id_encode: this.html_identifier_encode,
-        id_decode: this.html_identifier_decode
+        id_decode: this.html_identifier_decode,
+        searchbox: '#foldersearch'
     });
 
     this.subscription_list
       .addEventListener('select', function(node) { ref.subscription_select(node.id); })
       .addEventListener('collapse', function(node) { ref.folder_collapsed(node) })
       .addEventListener('expand', function(node) { ref.folder_collapsed(node) })
+      .addEventListener('search', function(p) { if (p.query) ref.subscription_select(); })
       .draggable({cancel: 'li.mailbox.root'})
       .droppable({
         // @todo: find better way, accept callback is executed for every folder
diff --git a/program/js/treelist.js b/program/js/treelist.js
index 916cf0e..7d13143 100644
--- a/program/js/treelist.js
+++ b/program/js/treelist.js
@@ -525,7 +525,8 @@
             .attr('id', li.attr('id') + '--xsR')
             .attr('class', li.attr('class'))
             .addClass('searchresult__')
-            .append(li.children().first().clone(true, true))
+            // append all elements like links and inputs, but not sub-trees
+            .append(li.children(':not(div.treetoggle,ul)').clone(true, true))
             .appendTo(container);
             hits.push(node.id);
         }
diff --git a/program/localization/en_US/labels.inc b/program/localization/en_US/labels.inc
index 1e179cb..e3fe76b 100644
--- a/program/localization/en_US/labels.inc
+++ b/program/localization/en_US/labels.inc
@@ -528,14 +528,14 @@
 $labels['replyalldefault'] = 'reply to all';
 $labels['replyalllist'] = 'reply to mailing list only (if found)';
 
-$labels['folder']  = 'Folder';
-$labels['folders']  = 'Folders';
-$labels['foldername']  = 'Folder name';
-$labels['subscribed']  = 'Subscribed';
+$labels['folder'] = 'Folder';
+$labels['folders'] = 'Folders';
+$labels['foldername'] = 'Folder name';
+$labels['subscribed'] = 'Subscribed';
 $labels['messagecount'] = 'Messages';
-$labels['create']  = 'Create';
-$labels['createfolder']  = 'Create new folder';
-$labels['managefolders']  = 'Manage folders';
+$labels['create'] = 'Create';
+$labels['createfolder'] = 'Create new folder';
+$labels['managefolders'] = 'Manage folders';
 $labels['specialfolders'] = 'Special Folders';
 $labels['properties'] = 'Properties';
 $labels['folderproperties'] = 'Folder properties';
@@ -545,9 +545,10 @@
 $labels['getfoldersize'] = 'Click to get folder size';
 $labels['changesubscription'] = 'Click to change subscription';
 $labels['foldertype'] = 'Folder Type';
-$labels['personalfolder']  = 'Private Folder';
-$labels['otherfolder']  = 'Other User\'s Folder';
-$labels['sharedfolder']  = 'Public Folder';
+$labels['personalfolder'] = 'Private Folder';
+$labels['otherfolder'] = 'Other User\'s Folder';
+$labels['sharedfolder'] = 'Public Folder';
+$labels['findfolders'] = 'Find folders';
 
 $labels['sortby'] = 'Sort by';
 $labels['sortasc']  = 'Sort ascending';
@@ -597,6 +598,8 @@
 $labels['arialabelcontactsearchform'] = 'Contacts search form';
 $labels['arialabelmailquicksearchbox'] = 'Email search input';
 $labels['arialabelquicksearchbox'] = 'Search input';
+$labels['arialabelfoldersearchfilter'] = 'Folder listing filter';
+$labels['arialabelfoldersearchform'] = 'Folder search form';
 $labels['arialabelfolderlist'] = 'Email folder selection';
 $labels['arialabelmessagelist'] = 'Email Messages Listing';
 $labels['arialabelmailpreviewframe'] = 'Message preview';
@@ -625,6 +628,7 @@
 $labels['arialabelpreferencesform'] = 'Preferences form';
 $labels['arialabelidentityeditfrom'] = 'Identity edit form';
 $labels['arialabelresonseeditfrom'] = 'Response edit form';
+$labels['arialabelsearchterms'] = 'Search terms';
 
 $labels['helplistnavigation'] = 'List keyboard navigation';
 $labels['helplistkeyboardnavigation'] = "Arrows up/down: Move row focus/selection.
diff --git a/skins/classic/common.css b/skins/classic/common.css
index 74de5e4..1edc1fa 100644
--- a/skins/classic/common.css
+++ b/skins/classic/common.css
@@ -1010,6 +1010,25 @@
   width: 97%;
 }
 
+.listsearchbox
+{
+  border-bottom: solid 1px #ddd;
+  padding: 2px 4px 1px;
+  display: none;
+  background-color: #f2f2f2;
+}
+
+.listsearchbox a.iconbutton.reset
+{
+  background: url(images/icons/reset.gif) no-repeat 0 0;
+  width: 13px;
+  height: 13px;
+  text-indent: 50000px;
+  position: absolute;
+  top: 3px;
+  left: 165px;
+  overflow: hidden;
+}
 
 /***** roundcube webmail pre-defined classes *****/
 
diff --git a/skins/classic/functions.js b/skins/classic/functions.js
index b521be3..f17f393 100644
--- a/skins/classic/functions.js
+++ b/skins/classic/functions.js
@@ -636,6 +636,36 @@
     var label = rcmail.gettext(p.status ? 'replylist' : 'replyall');
     $('a.button.replyAll').attr('title', label);
   }
+},
+
+folder_search_init: function(container)
+{
+  // animation to unfold list search box
+  $('.boxtitle a.search', container).click(function(e) {
+    var title = $('.boxtitle', container),
+      box = $('.listsearchbox', container),
+      dir = box.is(':visible') ? -1 : 1;
+
+    box.slideToggle({
+      duration: 160,
+      progress: function(animation, progress) {
+        if (dir < 0) progress = 1 - progress;
+          $('.boxlistcontent', container).css('top', (title.outerHeight() + 24 * progress) + 'px');
+      },
+      complete: function() {
+        box.toggleClass('expanded');
+        if (box.is(':visible')) {
+          box.find('input[type=text]').focus();
+        }
+        else {
+          $('a.reset', box).click();
+        }
+        // TODO: save state in cookie
+      }
+    });
+
+    return false;
+  });
 }
 
 };
@@ -1012,5 +1042,10 @@
       rcmail.addEventListener('afterupload-photo', function(){ rcmail_ui.show_popup('uploadform', false); })
         .gui_object('dragmenu', 'dragmenu');
     }
+    else if (rcmail.env.task == 'settings') {
+      if (rcmail.env.action == 'folders') {
+        rcmail_ui.folder_search_init($('#folder-manager'));
+      }
+    }
   });
 }
diff --git a/skins/classic/settings.css b/skins/classic/settings.css
index fb4303f..cd250d5 100644
--- a/skins/classic/settings.css
+++ b/skins/classic/settings.css
@@ -11,6 +11,16 @@
   background-color: #FFFFA6;
 }
 
+#folderlist-title a.iconbutton.search {
+  background: url(images/icons/glass.png) no-repeat 0 0;
+  cursor: pointer;
+  position: absolute;
+  right: 4px;
+  top: 2px;
+  width: 16px;
+  text-indent: 50000px;
+}
+
 #identities-table,
 #sections-table
 {
diff --git a/skins/classic/templates/folders.html b/skins/classic/templates/folders.html
index 7ca4ac4..8ed0b5f 100644
--- a/skins/classic/templates/folders.html
+++ b/skins/classic/templates/folders.html
@@ -19,7 +19,17 @@
 <div id="mainscreen">
 
 <div id="folder-manager">
-<div id="folderlist-title" class="boxtitle"><span class="rightalign"><roundcube:label name="subscribed" /></span><roundcube:label name="folders" /></div>
+<div id="folderlist-title" class="boxtitle">
+    <roundcube:label name="folders" />
+    <a href="#folders" class="iconbutton search" title="<roundcube:label name='findfolders' />" tabindex="0"><roundcube:label name='findfolders' /></a>
+</div>
+<div class="listsearchbox">
+    <div class="searchbox" role="search">
+        <input type="text" name="q" id="foldersearch" placeholder="<roundcube:label name='findfolders' />" />
+        <a class="iconbutton searchicon"></a>
+        <roundcube:button command="reset-foldersearch" id="folderlistsearch-reset" class="iconbutton reset" title="resetsearch" width="13" height="13" />
+    </div>
+</div>
 <div id="folderlist-content" class="boxlistcontent">
     <roundcube:object name="foldersubscription" form="subscriptionform" id="subscription-table"
         summary="Folder subscription table" class="treelist folderlist" />
diff --git a/skins/larry/settings.css b/skins/larry/settings.css
index b5b05c2..cda47b6 100644
--- a/skins/larry/settings.css
+++ b/skins/larry/settings.css
@@ -211,6 +211,19 @@
 	background-position: 4px -550px;
 }
 
+#folderslist .boxtitle a.iconbutton.search {
+	background-position: -2px -317px;
+	cursor: pointer;
+	position: absolute;
+	right: 8px;
+	top: 8px;
+	width: 16px;
+}
+
+#folderslist .listsearchbox + .scroller {
+	top: 34px;
+}
+
 #folderslist,
 #identitieslist {
 	position: absolute;
diff --git a/skins/larry/styles.css b/skins/larry/styles.css
index 529eb19..456ce49 100644
--- a/skins/larry/styles.css
+++ b/skins/larry/styles.css
@@ -1959,6 +1959,7 @@
 	position: relative;
 	padding: 4px;
 	background: #c7e3ef;
+	display: none;
 }
 
 .listsearchbox input {
diff --git a/skins/larry/templates/folders.html b/skins/larry/templates/folders.html
index f48169c..3d016ca 100644
--- a/skins/larry/templates/folders.html
+++ b/skins/larry/templates/folders.html
@@ -17,7 +17,19 @@
 <div id="settings-right" role="main">
 
 <div id="folderslist" class="uibox listbox">
-<h2 id="folderslist-header" class="boxtitle"><span style="float:right"><roundcube:label name="subscribed" /></span><roundcube:label name="folders" /></h2>
+<h2 id="folderslist-header" class="boxtitle">
+	<roundcube:label name="folders" />
+	<a href="#folders" class="iconbutton search" title="<roundcube:label name='findfolders' />" tabindex="0"><roundcube:label name='findfolders' /></a>
+</h2>
+<div class="listsearchbox">
+	<div class="searchbox" role="search" aria-labelledby="aria-label-foldersearchform">
+		<h3 id="aria-label-foldersearchform" class="voice"><roundcube:label name="arialabelfoldersearchform" /></h3>
+		<label for="foldersearch" class="voice"><roundcube:label name="arialabelsearchterms" /></label>
+		<input type="text" name="q" id="foldersearch" placeholder="<roundcube:label name='findfolders' />" />
+		<a class="iconbutton searchicon"></a>
+		<roundcube:button command="reset-foldersearch" id="folderlistsearch-reset" class="iconbutton reset" title="resetsearch" label="resetsearch" />
+	</div>
+</div>
 <div id="folderslist-content" class="scroller withfooter">
 	<roundcube:object name="foldersubscription" form="subscriptionform" id="subscription-table" class="treelist listing folderlist" />
 </div>
diff --git a/skins/larry/ui.js b/skins/larry/ui.js
index f6c3909..1eb8e89 100644
--- a/skins/larry/ui.js
+++ b/skins/larry/ui.js
@@ -47,6 +47,7 @@
   this.update_quota = update_quota;
   this.get_pref = get_pref;
   this.save_pref = save_pref;
+  this.folder_search_init = folder_search_init;
 
 
   // set minimal mode on small screens (don't wait for document.ready)
@@ -255,6 +256,8 @@
           orientation:'v', relative:true, start:266, min:180, size:12 }).init();
 
         rcmail.addEventListener('setquota', update_quota);
+
+        folder_search_init($('#folderslist'));
       }
       else if (rcmail.env.action == 'identities') {
         new rcube_splitter({ id:'identviewsplitter', p1:'#identitieslist', p2:'#identity-details',
@@ -531,6 +534,35 @@
     }
   }
 
+  function folder_search_init(container)
+  {
+    // animation to unfold list search box
+    $('.boxtitle a.search', container).click(function(e) {
+      var title = $('.boxtitle', container),
+        box = $('.listsearchbox', container),
+        dir = box.is(':visible') ? -1 : 1;
+
+      box.slideToggle({
+        duration: 160,
+        progress: function(animation, progress) {
+          if (dir < 0) progress = 1 - progress;
+            $('.scroller', container).css('top', (title.outerHeight() + 34 * progress) + 'px');
+        },
+        complete: function() {
+          box.toggleClass('expanded');
+          if (box.is(':visible')) {
+            box.find('input[type=text]').focus();
+          }
+          else {
+            $('a.reset', box).click();
+          }
+          // TODO: save state in localStorage
+        }
+      });
+
+      return false;
+    });
+  }
 
   function enable_command(p)
   {

--
Gitblit v1.9.1