- Added message status filter + fixes for r2046 (searching with SORT)
| | |
| | | 2008/11/12 (alec) |
| | | ---------- |
| | | - Use SORT for searching on servers with SORT capability |
| | | - Added message status filter |
| | | |
| | | 2008/11/06 (alec) |
| | | ---------- |
| | |
| | | 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 = ''; |
| | |
| | | /** |
| | | * 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; |
| | |
| | | */ |
| | | 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); |
| | | } |
| | | |
| | | |
| | |
| | | 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 |
| | |
| | | |
| | | |
| | | /** |
| | | * 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 |
| | |
| | | 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); |
| | | |
| | | foreach (iil_SortHeaders($a_msg_headers, $this->sort_field, $this->sort_order) as $i => $msg) |
| | | $this->cache[$key][] = $msg->uid; |
| | | 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->id; |
| | | } |
| | | } |
| | | |
| | | // have stored it in RAM |
| | |
| | | |
| | | // 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]; |
| | |
| | | * 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; |
| | | } |
| | | } |
| | | |
| | | |
| | | /** |
| | |
| | | */ |
| | | 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(); |
| | | } |
| | |
| | | |
| | | 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) |
| | |
| | | } |
| | | }; |
| | | |
| | | // 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) |
| | | { |
| | |
| | | }; |
| | | |
| | | // send remote request to search mail or contacts |
| | | this.qsearch = function(value) |
| | | this.qsearch = function(value, addurl) |
| | | { |
| | | if (value != '') |
| | | { |
| | |
| | | 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; |
| | | }; |
| | |
| | | } |
| | | |
| | | |
| | | 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', |
| | |
| | | 'messagecontentframe' => 'rcmail_messagecontent_frame', |
| | | 'messagepartframe' => 'rcmail_message_part_frame', |
| | | 'messagepartcontrols' => 'rcmail_message_part_controls', |
| | | 'searchfilter' => 'rcmail_search_filter', |
| | | 'searchform' => array($OUTPUT, 'search_form'), |
| | | )); |
| | | |
| | |
| | | |
| | | $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); |
| | |
| | | $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)) |
| | |
| | | |
| | | // 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.. |
| | |
| | | { |
| | | // 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 |
| | | { |
| | |
| | | |
| | | 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 |
| | |
| | | #quicksearchbar |
| | | { |
| | | position: absolute; |
| | | top: 60px; |
| | | top: 55px; |
| | | right: 20px; |
| | | width: 182px; |
| | | height: 20px; |
| | |
| | | #messagetoolbar |
| | | { |
| | | position: absolute; |
| | | top: 45px; |
| | | top: 47px; |
| | | left: 200px; |
| | | right: 200px; |
| | | height: 35px; |
| | |
| | | color: #333333; |
| | | } |
| | | |
| | | #messagetoolbar select.searchfilter |
| | | { |
| | | position: relative; |
| | | bottom: 10px; |
| | | } |
| | | |
| | | #messagetoolbar select.mboxlist |
| | | { |
| | | position: absolute; |
| | | left: 375px; |
| | | top: 10px; |
| | | top: 8px; |
| | | } |
| | | |
| | | #messagetoolbar select.mboxlist option |
| | |
| | | <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"> |