From 1bbf8c48868efb87baab7ae71721f2c9ad408e65 Mon Sep 17 00:00:00 2001
From: Thomas Bruederli <thomas@roundcube.net>
Date: Mon, 20 Jan 2014 04:05:36 -0500
Subject: [PATCH] - Make search scope selectable in UI - Disable thread mode when searching over multiple folders - Encode UID values for HTML message row identifiers

---
 program/js/list.js                    |   29 +++++----
 skins/larry/templates/mail.html       |   13 ++--
 program/steps/mail/search.inc         |   13 +++
 program/lib/Roundcube/rcube_imap.php  |    4 +
 program/steps/mail/func.inc           |    3 +
 program/localization/en_US/labels.inc |    4 +
 program/js/app.js                     |   45 +++++++++-----
 skins/larry/ui.js                     |   27 ++++++--
 8 files changed, 93 insertions(+), 45 deletions(-)

diff --git a/program/js/app.js b/program/js/app.js
index 5eae823..2401e1e 100644
--- a/program/js/app.js
+++ b/program/js/app.js
@@ -217,6 +217,7 @@
           this.gui_objects.messagelist.parentNode.onmousedown = function(e){ return p.click_on_list(e); };
 
           this.enable_command('toggle_status', 'toggle_flag', 'sort', true);
+          this.enable_command('set-listmode', this.env.threads && !this.env.search_request);
 
           // load messages
           this.command('list');
@@ -707,6 +708,10 @@
         }
         else if (this.task == 'addressbook')
           this.list_contacts(props);
+        break;
+
+      case 'set-listmode':
+        this.set_list_options(null, undefined, undefined, props == 'threads' ? 1 : 0);
         break;
 
       case 'sort':
@@ -1753,7 +1758,7 @@
   this.init_message_row = function(row)
   {
     var i, fn = {}, self = this, uid = row.uid,
-      status_icon = (this.env.status_col != null ? 'status' : 'msg') + 'icn' + row.uid;
+      status_icon = (this.env.status_col != null ? 'status' : 'msg') + 'icn' + row.id;
 
     if (uid && this.env.messages[uid])
       $.extend(row, this.env.messages[uid]);
@@ -1765,17 +1770,17 @@
 
     // save message icon position too
     if (this.env.status_col != null)
-      row.msgicon = document.getElementById('msgicn'+row.uid);
+      row.msgicon = document.getElementById('msgicn'+row.id);
     else
       row.msgicon = row.icon;
 
     // set eventhandler to flag icon
-    if (this.env.flagged_col != null && (row.flagicon = document.getElementById('flagicn'+row.uid))) {
+    if (this.env.flagged_col != null && (row.flagicon = document.getElementById('flagicn'+row.id))) {
       fn.flagicon = function(e) { self.command('toggle_flag', uid); };
     }
 
     // set event handler to thread expand/collapse icon
-    if (!row.depth && row.has_children && (row.expando = document.getElementById('rcmexpando'+row.uid))) {
+    if (!row.depth && row.has_children && (row.expando = document.getElementById('rcmexpando'+row.id))) {
       fn.expando = function(e) { self.expand_message_row(e, uid); };
     }
 
@@ -1837,7 +1842,7 @@
         + (flags.deleted ? ' deleted' : '')
         + (flags.flagged ? ' flagged' : '')
         + (message.selected ? ' selected' : ''),
-      row = { cols:[], style:{}, id:'rcmrow'+uid };
+      row = { cols:[], style:{}, id:'rcmrow'+this.html_identifier(uid,true), uid:uid };
 
     // message status icons
     css_class = 'msgicon';
@@ -1863,7 +1868,7 @@
     if (this.env.threading) {
       if (message.depth) {
         // This assumes that div width is hardcoded to 15px,
-        tree += '<span id="rcmtab' + uid + '" class="branch" style="width:' + (message.depth * 15) + 'px;">&nbsp;&nbsp;</span>';
+        tree += '<span id="rcmtab' + row.id + '" class="branch" style="width:' + (message.depth * 15) + 'px;">&nbsp;&nbsp;</span>';
 
         if ((rows[message.parent_uid] && rows[message.parent_uid].expanded === false)
           || ((this.env.autoexpand_threads == 0 || this.env.autoexpand_threads == 2) &&
@@ -1882,7 +1887,7 @@
           message.expanded = true;
         }
 
-        expando = '<div id="rcmexpando' + uid + '" class="' + (message.expanded ? 'expanded' : 'collapsed') + '">&nbsp;&nbsp;</div>';
+        expando = '<div id="rcmexpando' + row.id + '" class="' + (message.expanded ? 'expanded' : 'collapsed') + '">&nbsp;&nbsp;</div>';
         row_class += ' thread' + (message.expanded? ' expanded' : '');
       }
 
@@ -1890,14 +1895,14 @@
         row_class += ' unroot';
     }
 
-    tree += '<span id="msgicn'+uid+'" class="'+css_class+'">&nbsp;</span>';
+    tree += '<span id="msgicn'+row.id+'" class="'+css_class+'">&nbsp;</span>';
     row.className = row_class;
 
     // build subject link 
     if (!bw.ie && cols.subject) {
       var action = flags.mbox == this.env.drafts_mailbox ? 'compose' : 'show';
       var uid_param = flags.mbox == this.env.drafts_mailbox ? '_draft_uid' : '_uid';
-      cols.subject = '<a href="./?_task=mail&_action='+action+'&_mbox='+urlencode(flags.mbox)+'&'+uid_param+'='+uid+'"'+
+      cols.subject = '<a href="./?_task=mail&_action='+action+'&_mbox='+urlencode(flags.mbox)+'&'+uid_param+'='+urlencode(uid)+'"'+
         ' onclick="return rcube_event.cancel(event)" onmouseover="rcube_webmail.long_subject_title(this,'+(message.depth+1)+')">'+cols.subject+'</a>';
     }
 
@@ -1908,7 +1913,7 @@
 
       if (c == 'flag') {
         css_class = (flags.flagged ? 'flagged' : 'unflagged');
-        html = '<span id="flagicn'+uid+'" class="'+css_class+'">&nbsp;</span>';
+        html = '<span id="flagicn'+row.id+'" class="'+css_class+'">&nbsp;</span>';
       }
       else if (c == 'attachment') {
         if (/application\/|multipart\/(m|signed)/.test(flags.ctype))
@@ -1927,7 +1932,7 @@
           css_class = 'unreadchildren';
         else
           css_class = 'msgicon';
-        html = '<span id="statusicn'+uid+'" class="'+css_class+'">&nbsp;</span>';
+        html = '<span id="statusicn'+row.id+'" class="'+css_class+'">&nbsp;</span>';
       }
       else if (c == 'threads')
         html = expando;
@@ -2390,7 +2395,7 @@
     }
 
     if (html)
-      $('#rcmtab'+uid).html(html);
+      $('#rcmtab'+this.html_identifier(uid, true)).html(html);
   };
 
   // update parent in a thread
@@ -2454,14 +2459,14 @@
 
         r.depth--; // move left
         // reset width and clear the content of a tab, icons will be added later
-        $('#rcmtab'+r.uid).width(r.depth * 15).html('');
+        $('#rcmtab'+r.id).width(r.depth * 15).html('');
         if (!r.depth) { // a new root
           count++; // increase roots count
           r.parent_uid = 0;
           if (r.has_children) {
             // replace 'leaf' with 'collapsed'
-            $('#rcmrow'+r.uid+' '+'.leaf:first')
-              .attr('id', 'rcmexpando' + r.uid)
+            $('#'+r.id+' .leaf:first')
+              .attr('id', 'rcmexpando' + r.id)
               .attr('class', (r.obj.style.display != 'none' ? 'expanded' : 'collapsed'))
               .bind('mousedown', {uid:r.uid, p:this},
                 function(e) { return e.data.p.expand_message_row(e, e.data.uid); });
@@ -4118,6 +4123,7 @@
       r = this.http_request(action, url, lock);
 
       this.env.qsearch = {lock: lock, request: r};
+      this.enable_command('set-listmode', this.env.threads && (this.env.search_scope || 'base') == 'base');
     }
   };
 
@@ -4126,7 +4132,8 @@
   {
     var n, url = {}, mods_arr = [],
       mods = this.env.search_mods,
-      mbox = this.env.mailbox;
+      mbox = this.env.mailbox,
+      scope = this.env.search_scope || 'base';
 
     if (!filter && this.gui_objects.search_filter)
       filter = this.gui_objects.search_filter.value;
@@ -4150,7 +4157,9 @@
       }
     }
 
-    if (mbox)
+    if (scope)
+      url._scope = scope;
+    if (mbox && scope != 'all')
       url._mbox = mbox;
 
     return url;
@@ -4168,6 +4177,8 @@
     this.env.qsearch = null;
     this.env.search_request = null;
     this.env.search_id = null;
+
+    this.enable_command('set-listmode', this.env.threads);
   };
 
   this.sent_successfully = function(type, msg, folders)
diff --git a/program/js/list.js b/program/js/list.js
index 319807e..c026ccb 100644
--- a/program/js/list.js
+++ b/program/js/list.js
@@ -107,11 +107,15 @@
  */
 init_row: function(row)
 {
-  // make references in internal array and set event handlers
-  if (row && String(row.id).match(this.id_regexp)) {
-    var self = this,
-      uid = RegExp.$1;
+  var uid;
+  if (row && (uid = $(row).data('uid')))
     row.uid = uid;
+  else if (row && String(row.id).match(this.id_regexp))
+    row.uid = RegExp.$1;
+
+  // make references in internal array and set event handlers
+  if (row && row.uid) {
+    var self = this, uid = row.uid;
     this.rows[uid] = {uid:uid, id:row.id, obj:row};
 
     // set eventhandlers to table row
@@ -291,6 +295,7 @@
     if (row.id) domrow.id = row.id;
     if (row.className) domrow.className = row.className;
     if (row.style) $.extend(domrow.style, row.style);
+    if (row.uid) $(domrow).data('uid', row.uid);
 
     for (var domcell, col, i=0; row.cols && i < row.cols.length; i++) {
       col = row.cols[i];
@@ -589,7 +594,7 @@
     row.expanded = true;
     depth = row.depth;
     new_row = row.obj.nextSibling;
-    this.update_expando(row.uid, true);
+    this.update_expando(row.id, true);
     this.triggerEvent('expandcollapse', { uid:row.uid, expanded:row.expanded, obj:row.obj });
   }
   else {
@@ -639,7 +644,7 @@
     row.expanded = false;
     depth = row.depth;
     new_row = row.obj.nextSibling;
-    this.update_expando(row.uid);
+    this.update_expando(row.id);
     this.triggerEvent('expandcollapse', { uid:row.uid, expanded:row.expanded, obj:row.obj });
 
     // don't collapse sub-root tree in multiexpand mode 
@@ -661,7 +666,7 @@
           $(new_row).css('display', 'none');
         if (r.has_children && r.expanded) {
           r.expanded = false;
-          this.update_expando(r.uid, false);
+          this.update_expando(r.id, false);
           this.triggerEvent('expandcollapse', { uid:r.uid, expanded:r.expanded, obj:new_row });
         }
       }
@@ -683,7 +688,7 @@
     row.expanded = true;
     depth = row.depth;
     new_row = row.obj.nextSibling;
-    this.update_expando(row.uid, true);
+    this.update_expando(row.id, true);
     this.triggerEvent('expandcollapse', { uid:row.uid, expanded:row.expanded, obj:row.obj });
   }
   else {
@@ -700,7 +705,7 @@
         $(new_row).css('display', '');
         if (r.has_children && !r.expanded) {
           r.expanded = true;
-          this.update_expando(r.uid, true);
+          this.update_expando(r.id, true);
           this.triggerEvent('expandcollapse', { uid:r.uid, expanded:r.expanded, obj:new_row });
         }
       }
@@ -714,9 +719,9 @@
 },
 
 
-update_expando: function(uid, expanded)
+update_expando: function(id, expanded)
 {
-  var expando = document.getElementById('rcmexpando' + uid);
+  var expando = document.getElementById('rcmexpando' + id);
   if (expando)
     expando.className = expanded ? 'expanded' : 'collapsed';
 },
@@ -1267,7 +1272,7 @@
         this.collapse(selected_row);
     }
 
-    this.update_expando(selected_row.uid, selected_row.expanded);
+    this.update_expando(selected_row.id, selected_row.expanded);
 
     return false;
   }
diff --git a/program/lib/Roundcube/rcube_imap.php b/program/lib/Roundcube/rcube_imap.php
index 698d0da..dd0501c 100644
--- a/program/lib/Roundcube/rcube_imap.php
+++ b/program/lib/Roundcube/rcube_imap.php
@@ -953,6 +953,7 @@
 
             $this->sort_field = null;
             $this->page_size = 1000;  // fetch up to 1000 matching messages per folder
+            $this->threading = false;
 
             $a_msg_headers = array();
             foreach ($search_set->sets as $resultset) {
@@ -1487,6 +1488,9 @@
             // connect IMAP to have all the required classes and settings loaded
             $this->check_connection();
 
+            // disable threading
+            $this->threading = false;
+
             $searcher = new rcube_imap_search($this->options, $this->conn);
             $results = $searcher->exec(
                 $folder,
diff --git a/program/localization/en_US/labels.inc b/program/localization/en_US/labels.inc
index 61890a6..05eab67 100644
--- a/program/localization/en_US/labels.inc
+++ b/program/localization/en_US/labels.inc
@@ -208,6 +208,10 @@
 $labels['body']  = 'Body';
 $labels['type'] = 'Type';
 $labels['namex'] = 'Name';
+$labels['searchscope'] = 'Scope';
+$labels['currentfolder'] = 'Current folder';
+$labels['subfolders'] = 'This and subfolders';
+$labels['allfolders'] = 'All folders';
 
 $labels['openinextwin'] = 'Open in new window';
 $labels['emlsave'] = 'Download (.eml)';
diff --git a/program/steps/mail/func.inc b/program/steps/mail/func.inc
index 45d4242..fd321e2 100644
--- a/program/steps/mail/func.inc
+++ b/program/steps/mail/func.inc
@@ -105,6 +105,9 @@
         }
 
         $OUTPUT->set_env('search_mods', rcmail_search_mods());
+
+        if (!empty($_SESSION['search_scope']))
+            $OUTPUT->set_env('search_scope', $_SESSION['search_scope']);
     }
 
     $threading = (bool) $RCMAIL->storage->get_threading();
diff --git a/program/steps/mail/search.inc b/program/steps/mail/search.inc
index b45cdc0..88bbe6e 100644
--- a/program/steps/mail/search.inc
+++ b/program/steps/mail/search.inc
@@ -35,6 +35,7 @@
 $mbox    = rcube_utils::get_input_value('_mbox', rcube_utils::INPUT_GET, true);
 $filter  = rcube_utils::get_input_value('_filter', rcube_utils::INPUT_GET);
 $headers = rcube_utils::get_input_value('_headers', rcube_utils::INPUT_GET);
+$scope   = rcube_utils::get_input_value('_scope', rcube_utils::INPUT_GET);
 $subject = array();
 
 $search_request = md5($mbox.$filter.$str);
@@ -106,8 +107,15 @@
 $search_str  = trim($search_str);
 $sort_column = rcmail_sort_column();
 
-// TEMPORARY: search all folders
-$mboxes = $RCMAIL->storage->list_folders_subscribed('', '*', 'mail');
+// search all, current or subfolders folders
+if ($scope == 'all') {
+    $mboxes = $RCMAIL->storage->list_folders_subscribed('', '*', 'mail');
+}
+else if ($scope == 'sub') {
+    $mboxes = $RCMAIL->storage->list_folders_subscribed($mbox, '*', 'mail');
+    if ($mbox != 'INBOX' && $mboxes[0] == 'INBOX')
+        array_shift($mboxes);
+}
 
 // execute IMAP search
 if ($search_str) {
@@ -128,6 +136,7 @@
     $_SESSION['last_text_search'] = $str;
 }
 $_SESSION['search_request'] = $search_request;
+$_SESSION['search_scope'] = $scope;
 
 // Add 'folder' column to list
 if ($_SESSION['search'][1]->multi) {
diff --git a/skins/larry/templates/mail.html b/skins/larry/templates/mail.html
index ff5f754..e2bf890 100644
--- a/skins/larry/templates/mail.html
+++ b/skins/larry/templates/mail.html
@@ -76,13 +76,8 @@
 <!-- list footer -->
 <div id="messagelistfooter">
 	<div id="listcontrols">
-		<roundcube:if condition="env:threads" />
-			<a href="#list" class="iconbutton listmode" id="maillistmode" title="<roundcube:label name='list' />">List</a>
-			<a href="#threads" class="iconbutton threadmode" id="mailthreadmode" title="<roundcube:label name='threads' />">Threads</a>
-		<roundcube:else />
-			<a href="#list" class="iconbutton listmode selected" title="<roundcube:label name='list' />" onclick="return false">List</a>
-			<a href="#threads" class="iconbutton threadmode disabled" title="<roundcube:label name='threads' />" onclick="return false">Threads</a>
-		<roundcube:endif />
+		<roundcube:button href="#list" command="set-listmode" prop="list" class="iconbutton listmode disabled" classAct="iconbutton listmode" id="maillistmode" title="list" content="List" />
+		<roundcube:button href="#threads" command="set-listmode" prop="threads" class="iconbutton threadmode disabled" classAct="iconbutton threadmode" id="mailthreadmode" title="threads" content="Threads" />
 	</div>
 	
 	<div id="listselectors">
@@ -132,6 +127,10 @@
 		<li><label><input type="checkbox" name="s_mods[]" value="bcc" id="s_mod_bcc" onclick="UI.set_searchmod(this)" /> <span><roundcube:label name="bcc" /></span></label></li>
 		<li><label><input type="checkbox" name="s_mods[]" value="body" id="s_mod_body" onclick="UI.set_searchmod(this)" /> <span><roundcube:label name="body" /></span></label></li>
 		<li><label><input type="checkbox" name="s_mods[]" value="text" id="s_mod_text" onclick="UI.set_searchmod(this)" /> <span><roundcube:label name="msgtext" /></span></label></li>
+		<li class="separator" id=""><label><roundcube:label name="searchscope" /></label></li>
+		<li><label><input type="radio" name="s_scope" value="base" id="s_scope_base" onclick="UI.set_searchscope(this)" /> <span><roundcube:label name="currentfolder" /></span></label></li>
+		<li><label><input type="radio" name="s_scope" value="sub" id="s_scope_sub" onclick="UI.set_searchscope(this)" /> <span><roundcube:label name="subfolders" /></span></label></li>
+		<li><label><input type="radio" name="s_scope" value="all" id="s_scope_all" onclick="UI.set_searchscope(this)" /> <span><roundcube:label name="allfolders" /></span></label></li>
 	</ul>
 </div>
 
diff --git a/skins/larry/ui.js b/skins/larry/ui.js
index 44fc727..2030b05 100644
--- a/skins/larry/ui.js
+++ b/skins/larry/ui.js
@@ -41,6 +41,7 @@
   this.show_popup = show_popup;
   this.add_popup = add_popup;
   this.set_searchmod = set_searchmod;
+  this.set_searchscope = set_searchscope;
   this.show_uploadform = show_uploadform;
   this.show_header_row = show_header_row;
   this.hide_header_row = hide_header_row;
@@ -731,11 +732,10 @@
    */
   function switch_view_mode(mode)
   {
-    if (rcmail.env.threading != (mode == 'thread'))
-      rcmail.set_list_options(null, undefined, undefined, mode == 'thread' ? 1 : 0);
-
-    $('#maillistmode, #mailthreadmode').removeClass('selected');
-    $('#mail'+mode+'mode').addClass('selected');
+    if (!$('#mail'+mode+'mode').hasClass('disabled')) {
+      $('#maillistmode, #mailthreadmode').removeClass('selected');
+      $('#mail'+mode+'mode').addClass('selected');
+    }
   }
 
 
@@ -761,11 +761,15 @@
         obj = popups['searchmenu'],
         list = $('input:checkbox[name="s_mods[]"]', obj),
         mbox = rcmail.env.mailbox,
-        mods = rcmail.env.search_mods;
+        mods = rcmail.env.search_mods,
+        scope = rcmail.env.search_scope || 'base';
 
       if (rcmail.env.task == 'mail') {
+        if (scope == 'all')
+          mbox = '*';
         mods = mods[mbox] ? mods[mbox] : mods['*'];
         all = 'text';
+        $('#s_scope_'+scope).prop('checked', true);
       }
       else {
         all = '*';
@@ -896,7 +900,11 @@
   {
     var all, m, task = rcmail.env.task,
       mods = rcmail.env.search_mods,
-      mbox = rcmail.env.mailbox;
+      mbox = rcmail.env.mailbox,
+      scope = $('input[name="s_scope"]:checked').val();
+
+    if (scope == 'all')
+      mbox = '*';
 
     if (!mods)
       mods = {};
@@ -937,6 +945,11 @@
     });
   }
 
+  function set_searchscope(elem)
+  {
+    rcmail.env.search_scope = elem.value;
+  }
+
   function push_contactgroup(p)
   {
     // lets the contacts list swipe to the left, nice!

--
Gitblit v1.9.1