diff options
-rw-r--r-- | CHANGELOG | 1 | ||||
-rw-r--r-- | program/include/rcube_imap.php | 102 | ||||
-rw-r--r-- | program/js/app.js | 32 | ||||
-rw-r--r-- | program/steps/mail/func.inc | 31 | ||||
-rw-r--r-- | program/steps/mail/list.inc | 13 | ||||
-rw-r--r-- | program/steps/mail/search.inc | 33 | ||||
-rw-r--r-- | program/steps/mail/show.inc | 17 | ||||
-rw-r--r-- | skins/default/common.css | 2 | ||||
-rw-r--r-- | skins/default/mail.css | 10 | ||||
-rw-r--r-- | skins/default/templates/mail.html | 1 |
10 files changed, 173 insertions, 69 deletions
@@ -4,6 +4,7 @@ CHANGELOG RoundCube Webmail 2008/11/12 (alec) ---------- - Use SORT for searching on servers with SORT capability +- Added message status filter 2008/11/06 (alec) ---------- diff --git a/program/include/rcube_imap.php b/program/include/rcube_imap.php index 2ccaf7750..765778a20 100644 --- a/program/include/rcube_imap.php +++ b/program/include/rcube_imap.php @@ -61,7 +61,6 @@ class rcube_imap var $msg_headers = array(); var $skip_deleted = FALSE; var $search_set = NULL; - var $search_subject = ''; var $search_string = ''; var $search_charset = ''; var $search_sort_field = ''; @@ -282,18 +281,18 @@ class rcube_imap /** * Save a set of message ids for future message listing methods * - * @param array List of IMAP fields to search in - * @param string Search string - * @param array List of message ids or NULL if empty + * @param string IMAP Search query + * @param array List of message ids or NULL if empty + * @param string Charset of search string + * @param string Sorting field */ - function set_search_set($subject, $str=null, $msgs=null, $charset=null, $sort_field=null) + function set_search_set($str=null, $msgs=null, $charset=null, $sort_field=null) { - if (is_array($subject) && $str == null && $msgs == null) - list($subject, $str, $msgs, $charset, $sort_field) = $subject; + if ($msgs == null) + list($str, $msgs, $charset, $sort_field) = $str; if ($msgs != null && !is_array($msgs)) $msgs = split(',', $msgs); - $this->search_subject = $subject; $this->search_string = $str; $this->search_set = (array)$msgs; $this->search_charset = $charset; @@ -307,7 +306,7 @@ class rcube_imap */ function get_search_set() { - return array($this->search_subject, $this->search_string, $this->search_set, $this->search_charset, $this->search_sort_field); + return array($this->search_string, $this->search_set, $this->search_charset, $this->search_sort_field); } @@ -654,9 +653,9 @@ class rcube_imap if ($this->get_capability('sort')) // SORT searching result { // reset search set if sorting field has been changed - if ($sort_field && $this->search_sort_field != $sort_field) + if ($this->sort_field && $this->search_sort_field != $this->sort_field) { - $msgs = $this->search('', $this->search_subject, $this->search_string, $this->search_charset, $sort_field); + $msgs = $this->search('', $this->search_string, $this->search_charset, $this->sort_field); } // return empty array if no messages found @@ -783,7 +782,7 @@ class rcube_imap /** - * Return sorted array of message UIDs + * Return sorted array of message IDs (not UIDs) * * @param string Mailbox to get index from * @param string Sort column @@ -801,10 +800,25 @@ class rcube_imap if (!isset($this->cache[$key]) && $this->search_string && $mailbox == $this->mailbox) { $this->cache[$key] = $a_msg_headers = array(); - $this->_fetch_headers($mailbox, join(',', $this->search_set), $a_msg_headers, NULL); + + if ($this->get_capability('sort')) + { + if ($this->sort_field && $this->search_sort_field != $this->sort_field) + $this->search('', $this->search_string, $this->search_charset, $this->sort_field); + + if ($this->sort_order == 'DESC') + $this->cache[$key] = array_reverse($this->search_set); + else + $this->cache[$key] = $this->search_set; + } + else + { + // TODO: see list_header_set (fetch only one header field needed for sorting) + $this->_fetch_headers($mailbox, join(',', $this->search_set), $a_msg_headers, NULL); - foreach (iil_SortHeaders($a_msg_headers, $this->sort_field, $this->sort_order) as $i => $msg) - $this->cache[$key][] = $msg->uid; + foreach (iil_SortHeaders($a_msg_headers, $this->sort_field, $this->sort_order) as $i => $msg) + $this->cache[$key][] = $msg->id; + } } // have stored it in RAM @@ -825,28 +839,23 @@ class rcube_imap // fetch complete message index $msg_count = $this->_messagecount($mailbox); - if ($this->get_capability('sort') && ($a_index = iil_C_Sort($this->conn, $mailbox, $this->sort_field, '', TRUE))) + if ($this->get_capability('sort') && ($a_index = iil_C_Sort($this->conn, $mailbox, $this->sort_field, ''))) { if ($this->sort_order == 'DESC') $a_index = array_reverse($a_index); $this->cache[$key] = $a_index; - } else { $a_index = iil_C_FetchHeaderIndex($this->conn, $mailbox, "1:$msg_count", $this->sort_field); - $a_uids = iil_C_FetchUIDs($this->conn, $mailbox); - + if ($this->sort_order=="ASC") asort($a_index); else if ($this->sort_order=="DESC") arsort($a_index); - $i = 0; - $this->cache[$key] = array(); - foreach ($a_index as $index => $value) - $this->cache[$key][$i++] = $a_uids[$index]; + $this->cache[$key] = $a_index; } return $this->cache[$key]; @@ -906,39 +915,46 @@ class rcube_imap * Invoke search request to IMAP server * * @param string mailbox name to search in - * @param string search criteria (ALL, TO, FROM, SUBJECT, etc) * @param string search string * @param string search string charset * @param string header field to sort by * @return array search results as list of message ids * @access public */ - function search($mbox_name='', $criteria='ALL', $str=NULL, $charset=NULL, $sort_field=NULL) + function search($mbox_name='', $str=NULL, $charset=NULL, $sort_field=NULL) { $mailbox = $mbox_name ? $this->_mod_mailbox($mbox_name) : $this->mailbox; - $search = ''; - - // have an array of criterias => create search string - if (is_array($criteria) && count($criteria) > 1) - $search .= 'OR'; - $criteria = (array) $criteria; - foreach($criteria as $idx => $crit) - if ($str) - $search .= sprintf(" (%s {%d}\r\n%s)", $crit, strlen($str), $str); - else - $search .= '('. $crit .')'; - - $results = $this->_search_index($mailbox, $search, $charset, $sort_field); + $results = $this->_search_index($mailbox, $str, $charset, $sort_field); // try search with ISO charset (should be supported by server) if (empty($results) && !empty($charset) && $charset!='ISO-8859-1') - $results = $this->search($mbox_name, $criteria, rcube_charset_convert($str, $charset, 'ISO-8859-1'), 'ISO-8859-1', $sort_field); + { + // convert strings to ISO-8859-1 + if(preg_match_all('/\{([0-9]+)\}\r\n/', $str, $matches, PREG_OFFSET_CAPTURE)) + { + $last = 0; $res = ''; + foreach($matches[1] as $m) + { + $string_offset = $m[1] + strlen($m[0]) + 4; // {}\r\n + $string = substr($str, $string_offset - 1, $m[0]); + $string = rcube_charset_convert($string, $charset, 'ISO-8859-1'); + $res .= sprintf("%s{%d}\r\n%s", substr($str, $last, $m[1] - $last - 1), strlen($string), $string); + $last = $m[0] + $string_offset - 1; + } + if ($last < strlen($str)) + $res .= substr($str, $last, strlen($str)-$last); + } + else // strings for conversion not found + $res = $str; + + $results = $this->search($mbox_name, $res, 'ISO-8859-1', $sort_field); + } - $this->set_search_set($criteria, $str, $results, $charset, $sort_field); + $this->set_search_set($str, $results, $charset, $sort_field); return $results; - } + } /** @@ -974,8 +990,8 @@ class rcube_imap */ function refresh_search() { - if (!empty($this->search_subject) && !empty($this->search_string)) - $this->search_set = $this->search('', $this->search_subject, $this->search_string, $this->search_charset, $this->search_sort_field); + if (!empty($this->search_string)) + $this->search_set = $this->search('', $this->search_string, $this->search_charset, $this->search_sort_field); return $this->get_search_set(); } diff --git a/program/js/app.js b/program/js/app.js index d22b1fbfb..e7204d040 100644 --- a/program/js/app.js +++ b/program/js/app.js @@ -266,7 +266,7 @@ function rcube_webmail() this.set_page_buttons(); - if (this.env.address_sources && !this.env.address_sources[this.env.source].readonly) + if (this.env.address_sources && this.env.address_sources[this.env.source] && !this.env.address_sources[this.env.source].readonly) this.enable_command('add', true); if (this.env.cid) @@ -1414,6 +1414,24 @@ function rcube_webmail() } }; + // list messages of a specific mailbox using filter + this.filter_mailbox = function(filter) + { + var search; + if (this.gui_objects.qsearchbox) + search = this.gui_objects.qsearchbox.value; + + this.message_list.clear(); + + // reset vars + this.env.current_page = 1; + this.set_busy(true, 'searching'); + this.http_request('search', '_filter='+filter + + (search ? '&_q='+urlencode(search) : '') + + (this.env.mailbox ? '&_mbox='+urlencode(this.env.mailbox) : ''), true); + } + + // list messages of a specific mailbox this.list_mailbox = function(mbox, page, sort) { @@ -2283,7 +2301,7 @@ function rcube_webmail() }; // send remote request to search mail or contacts - this.qsearch = function(value) + this.qsearch = function(value, addurl) { if (value != '') { @@ -2292,12 +2310,18 @@ function rcube_webmail() else if (this.contact_list) { this.contact_list.clear(true); this.show_contentframe(false); - } + } + + if (this.gui_objects.search_filter) + addurl = '&_filter=' + this.gui_objects.search_filter.value; // reset vars this.env.current_page = 1; this.set_busy(true, 'searching'); - this.http_request('search', '_q='+urlencode(value)+(this.env.mailbox ? '&_mbox='+urlencode(this.env.mailbox) : '')+(this.env.source ? '&_source='+urlencode(this.env.source) : ''), true); + this.http_request('search', '_q='+urlencode(value) + +(this.env.mailbox ? '&_mbox='+urlencode(this.env.mailbox) : '') + +(this.env.source ? '&_source='+urlencode(this.env.source) : '') + +(addurl ? addurl : ''), true); } return true; }; diff --git a/program/steps/mail/func.inc b/program/steps/mail/func.inc index ca7c4e727..d2bc6a9be 100644 --- a/program/steps/mail/func.inc +++ b/program/steps/mail/func.inc @@ -1292,6 +1292,36 @@ function rcmail_send_mdn($uid) } +function rcmail_search_filter($attrib) +{ + global $OUTPUT; + + if (!strlen($attrib['id'])) + $attrib['id'] = 'rcmlistfilter'; + + $attrib['onchange'] = JS_OBJECT_NAME.'.filter_mailbox(this.value)'; + + /* + RFC3501 (6.4.4): 'ALL', 'RECENT', + 'ANSWERED', 'DELETED', 'FLAGGED', 'SEEN', + 'UNANSWERED', 'UNDELETED', 'UNFLAGGED', 'UNSEEN', + 'NEW', // = (RECENT UNSEEN) + 'OLD' // = NOT RECENT + */ + + $select_filter = new html_select($attrib); + $select_filter->add(rcube_label('all'), 'ALL'); + $select_filter->add(rcube_label('unread'), 'UNSEEN'); + $select_filter->add(rcube_label('flagged'), 'FLAGGED'); + $select_filter->add(rcube_label('unanswered'), 'UNANSWERED'); + + $out = $select_filter->show($_SESSION['search_filter']); + + $OUTPUT->add_gui_object('search_filter', $attrib['id']); + + return $out; +} + // register UI objects $OUTPUT->add_handlers(array( 'mailboxlist' => 'rcmail_mailbox_list', @@ -1304,6 +1334,7 @@ $OUTPUT->add_handlers(array( 'messagecontentframe' => 'rcmail_messagecontent_frame', 'messagepartframe' => 'rcmail_message_part_frame', 'messagepartcontrols' => 'rcmail_message_part_controls', + 'searchfilter' => 'rcmail_search_filter', 'searchform' => array($OUTPUT, 'search_form'), )); diff --git a/program/steps/mail/list.inc b/program/steps/mail/list.inc index a868f9cc6..2078262eb 100644 --- a/program/steps/mail/list.inc +++ b/program/steps/mail/list.inc @@ -41,6 +41,17 @@ else $mbox_name = $IMAP->get_mailbox_name(); +// initialize searching result if search_filter is used +if ($_SESSION['search_filter'] && $_SESSION['search_filter'] != 'ALL') +{ + $search_request = md5($mbox_name.$_SESSION['search_filter']); + + $IMAP->search($mbox_name, $_SESSION['search_filter'], RCMAIL_CHARSET, $sort_col); + $_SESSION['search'][$search_request] = $IMAP->get_search_set(); + $OUTPUT->set_env('search_request', $search_request); +} + + // fetch message headers if ($IMAP->messagecount($mbox_name, 'ALL', !empty($_REQUEST['_refresh']))) $a_headers = $IMAP->list_headers($mbox_name, NULL, $sort_col, $sort_order); @@ -55,8 +66,6 @@ $OUTPUT->set_env('pagecount', $pages); $OUTPUT->command('set_rowcount', rcmail_get_messagecount_text($count)); $OUTPUT->command('set_mailboxname', rcmail_get_mailbox_name_text()); - - // add message rows if (isset($a_headers) && count($a_headers)) rcmail_js_message_list($a_headers); diff --git a/program/steps/mail/search.inc b/program/steps/mail/search.inc index 6f3676891..18335ba15 100644 --- a/program/steps/mail/search.inc +++ b/program/steps/mail/search.inc @@ -26,51 +26,66 @@ $imap_charset = 'UTF-8'; // get search string $str = get_input_value('_q', RCUBE_INPUT_GET); +$filter = get_input_value('_filter', RCUBE_INPUT_GET); $mbox = get_input_value('_mbox', RCUBE_INPUT_GET); -$search_request = md5($mbox.$str); +$search_request = md5($mbox.$filter.$str); +// add list filter string +$search_str = $filter && $filter != 'ALL' ? $filter : ''; + +$_SESSION['search_filter'] = $filter; // Check the search string for type of search -if (preg_match("/^from:/i", $str)) +if (preg_match("/^from:.*/i", $str)) { list(,$srch) = explode(":", $str); - $subject = "HEADER FROM"; + $subject = "HEADER FROM"; $search = trim($srch); } -else if (preg_match("/^to:/i", $str)) +else if (preg_match("/^to.*:/i", $str)) { list(,$srch) = explode(":", $str); $subject = "HEADER TO"; $search = trim($srch); } -else if (preg_match("/^cc:/i", $str)) +else if (preg_match("/^cc:.*/i", $str)) { list(,$srch) = explode(":", $str); $subject = "HEADER CC"; $search = trim($srch); } -else if (preg_match("/^subject:/i", $str)) +else if (preg_match("/^subject:.*/i", $str)) { list(,$srch) = explode(":", $str); $subject = "HEADER SUBJECT"; $search = trim($srch); } -else if (preg_match("/^body:/i", $str)) +else if (preg_match("/^body:.*/i", $str)) { list(,$srch) = explode(":", $str); $subject = "TEXT"; $search = trim($srch); } // search in subject and sender by default -else +else if(trim($str)) { $from = ($mbox == $CONFIG['sent_mbox'] || $mbox == $CONFIG['drafts_mbox']) ? "TO" : "FROM"; $subject = array("HEADER SUBJECT", "HEADER $from"); $search = trim($str); } +if ($subject && !is_array($subject)) + $search_str .= sprintf(" %s {%d}\r\n%s", $subject, strlen($search), $search); +else if ($subject) { + $search_str .= ' OR'; + foreach($subject as $sub) + $search_str .= sprintf(" (%s {%d}\r\n%s)", $sub, strlen($search), $search); +} + +$search_str = trim($search_str); + // execute IMAP search -$result = $IMAP->search($mbox, $subject, $search, $imap_charset, $_SESSION['sort_col']); +$result = $IMAP->search($mbox, $search_str, $imap_charset, $_SESSION['sort_col']); $count = 0; // Make sure our $result is legit.. diff --git a/program/steps/mail/show.inc b/program/steps/mail/show.inc index 8b36c9acd..15c1c54a6 100644 --- a/program/steps/mail/show.inc +++ b/program/steps/mail/show.inc @@ -110,12 +110,13 @@ if ($_GET['_uid']) { { // Only if we use custom sorting $a_msg_index = $IMAP->message_index(NULL, $_SESSION['sort_col'], $_SESSION['sort_order']); - - $MESSAGE->index = array_search((string)$MESSAGE->uid, $a_msg_index, TRUE); - $prev = isset($a_msg_index[$MESSAGE->index-1]) ? $a_msg_index[$MESSAGE->index-1] : -1 ; - $first = count($a_msg_index)>0 ? $a_msg_index[0] : -1; - $next = isset($a_msg_index[$MESSAGE->index+1]) ? $a_msg_index[$MESSAGE->index+1] : -1 ; - $last = count($a_msg_index)>0 ? $a_msg_index[count($a_msg_index)-1] : -1; + + $MESSAGE->index = array_search($IMAP->get_id($MESSAGE->uid), $a_msg_index); + + $prev = isset($a_msg_index[$MESSAGE->index-1]) ? $IMAP->get_uid($a_msg_index[$MESSAGE->index-1]) : -1 ; + $first = count($a_msg_index)>0 ? $IMAP->get_uid($a_msg_index[0]) : -1; + $next = isset($a_msg_index[$MESSAGE->index+1]) ? $IMAP->get_uid($a_msg_index[$MESSAGE->index+1]) : -1 ; + $last = count($a_msg_index)>0 ? $IMAP->get_uid($a_msg_index[count($a_msg_index)-1]) : -1; } else { @@ -130,11 +131,11 @@ if ($_GET['_uid']) { if ($prev > 0) $OUTPUT->set_env('prev_uid', $prev); - if ($first >0) + if ($first > 0) $OUTPUT->set_env('first_uid', $first); if ($next > 0) $OUTPUT->set_env('next_uid', $next); - if ($last >0) + if ($last > 0) $OUTPUT->set_env('last_uid', $last); // mark message as read diff --git a/skins/default/common.css b/skins/default/common.css index 2029bf794..1225862f1 100644 --- a/skins/default/common.css +++ b/skins/default/common.css @@ -328,7 +328,7 @@ table.records-table tr.unfocused td #quicksearchbar { position: absolute; - top: 60px; + top: 55px; right: 20px; width: 182px; height: 20px; diff --git a/skins/default/mail.css b/skins/default/mail.css index 474a77fc3..5189f6d3f 100644 --- a/skins/default/mail.css +++ b/skins/default/mail.css @@ -4,7 +4,7 @@ #messagetoolbar { position: absolute; - top: 45px; + top: 47px; left: 200px; right: 200px; height: 35px; @@ -24,11 +24,17 @@ color: #333333; } +#messagetoolbar select.searchfilter +{ + position: relative; + bottom: 10px; +} + #messagetoolbar select.mboxlist { position: absolute; left: 375px; - top: 10px; + top: 8px; } #messagetoolbar select.mboxlist option diff --git a/skins/default/templates/mail.html b/skins/default/templates/mail.html index 43ff43bb3..51e1b1f11 100644 --- a/skins/default/templates/mail.html +++ b/skins/default/templates/mail.html @@ -118,6 +118,7 @@ <roundcube:button command="forward" imageSel="/images/buttons/forward_sel.png" imageAct="/images/buttons/forward_act.png" imagePas="/images/buttons/forward_pas.png" width="32" height="32" title="forwardmessage" /> <roundcube:button command="delete" imageSel="/images/buttons/delete_sel.png" imageAct="/images/buttons/delete_act.png" imagePas="/images/buttons/delete_pas.png" width="32" height="32" title="deletemessage" /> <roundcube:button command="print" imageSel="/images/buttons/print_sel.png" imageAct="/images/buttons/print_act.png" imagePas="/images/buttons/print_pas.png" width="32" height="32" title="printmessage" /> +<roundcube:object name="searchfilter" id="searchfilter" class="searchfilter" /> <div id="markmessagemenu"> <ul class="toolbarmenu"> |