From 3412e50b54e3daac8745234e21ab6e72be0ed165 Mon Sep 17 00:00:00 2001
From: Thomas Bruederli <thomas@roundcube.net>
Date: Wed, 04 Jun 2014 11:20:33 -0400
Subject: [PATCH] Fix attachment menu structure and aria-attributes
---
program/lib/Roundcube/rcube_imap.php | 318 ++++++++++++++++++++++++++++++++++++++--------------
1 files changed, 230 insertions(+), 88 deletions(-)
diff --git a/program/lib/Roundcube/rcube_imap.php b/program/lib/Roundcube/rcube_imap.php
index 698d0da..4204354 100644
--- a/program/lib/Roundcube/rcube_imap.php
+++ b/program/lib/Roundcube/rcube_imap.php
@@ -332,6 +332,10 @@
$this->search_sort_field = $set[3];
$this->search_sorted = $set[4];
$this->search_threads = is_a($this->search_set, 'rcube_result_thread');
+
+ if (is_a($this->search_set, 'rcube_result_multifolder')) {
+ $this->set_threading(false);
+ }
}
@@ -951,34 +955,56 @@
$sort_field = $this->sort_field;
$search_set = $this->search_set;
- $this->sort_field = null;
- $this->page_size = 1000; // fetch up to 1000 matching messages per folder
-
- $a_msg_headers = array();
- foreach ($search_set->sets as $resultset) {
- if (!$resultset->is_empty()) {
- $this->search_set = $resultset;
- $this->search_threads = $resultset instanceof rcube_result_thread;
- $a_msg_headers = array_merge($a_msg_headers, $this->list_search_messages($resultset->get_parameters('MAILBOX'), 1));
- }
- }
-
- // do sorting and paging
+ // prepare paging
$cnt = $search_set->count();
$from = ($page-1) * $page_size;
$to = $from + $page_size;
+ $slice_length = min($page_size, $cnt - $from);
- // sort headers
- if (!$this->threading && !empty($a_msg_headers)) {
- $a_msg_headers = $this->conn->sortHeaders($a_msg_headers, $sort_field, $this->sort_order);
+ // fetch resultset headers, sort and slice them
+ if (!empty($sort_field)) {
+ $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) {
+ if (!$resultset->is_empty()) {
+ $this->search_set = $resultset;
+ $this->search_threads = $resultset instanceof rcube_result_thread;
+ $a_msg_headers = array_merge($a_msg_headers, $this->list_search_messages($resultset->get_parameters('MAILBOX'), 1));
+ }
+ }
+
+ // sort headers
+ if (!empty($a_msg_headers)) {
+ $a_msg_headers = $this->conn->sortHeaders($a_msg_headers, $sort_field, $this->sort_order);
+ }
+
+ // store (sorted) message index
+ $search_set->set_message_index($a_msg_headers, $sort_field, $this->sort_order);
+
+ // only return the requested part of the set
+ $a_msg_headers = array_slice(array_values($a_msg_headers), $from, $slice_length);
}
+ else {
+ if ($this->sort_order != $search_set->get_parameters('ORDER')) {
+ $search_set->revert();
+ }
- // store (sorted) message index
- $search_set->set_message_index($a_msg_headers, $sort_field, $this->sort_order);
+ // slice resultset first...
+ $fetch = array();
+ foreach (array_slice($search_set->get(), $from, $slice_length) as $msg_id) {
+ list($uid, $folder) = explode('-', $msg_id, 2);
+ $fetch[$folder][] = $uid;
+ }
- // only return the requested part of the set
- $slice_length = min($page_size, $cnt - ($to > $cnt ? $from : $to));
- $a_msg_headers = array_slice(array_values($a_msg_headers), $from, $slice_length);
+ // ... and fetch the requested set of headers
+ $a_msg_headers = array();
+ foreach ($fetch as $folder => $a_index) {
+ $a_msg_headers = array_merge($a_msg_headers, array_values($this->fetch_headers($folder, $a_index)));
+ }
+ }
if ($slice) {
$a_msg_headers = array_slice($a_msg_headers, -$slice, $slice);
@@ -1466,17 +1492,13 @@
* @param string $str Search criteria
* @param string $charset Search charset
* @param string $sort_field Header field to sort by
- *
+ * @return rcube_result_index Search result object
* @todo: Search criteria should be provided in non-IMAP format, eg. array
*/
public function search($folder='', $str='ALL', $charset=NULL, $sort_field=NULL)
{
if (!$str) {
$str = 'ALL';
- }
-
- if (empty($folder)) {
- $folder = $this->folder;
}
// multi-folder search
@@ -1487,7 +1509,20 @@
// 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);
+
+ // set limit to not exceed the client's request timeout
+ $searcher->set_timelimit(60);
+
+ // continue existing incomplete search
+ if (!empty($this->search_set) && $this->search_set->incomplete && $str == $this->search_string) {
+ $searcher->set_results($this->search_set);
+ }
+
+ // execute the search
$results = $searcher->exec(
$folder,
$str,
@@ -1498,11 +1533,16 @@
}
else {
$folder = is_array($folder) ? $folder[0] : $folder;
+ if (!strlen($folder)) {
+ $folder = $this->folder;
+ }
$results = $this->search_index($folder, $str, $charset, $sort_field);
}
$this->set_search_set(array($str, $results, $charset, $sort_field,
$this->threading || $this->search_sorted ? true : false));
+
+ return $results;
}
@@ -1516,19 +1556,26 @@
*/
public function search_once($folder = null, $str = 'ALL')
{
- if (!$str) {
- $str = 'ALL';
- }
-
- if (!strlen($folder)) {
- $folder = $this->folder;
- }
-
if (!$this->check_connection()) {
return new rcube_result_index();
}
- $index = $this->conn->search($folder, $str, true);
+ if (!$str) {
+ $str = 'ALL';
+ }
+
+ // multi-folder search
+ if (is_array($folder) && count($folder) > 1) {
+ $searcher = new rcube_imap_search($this->options, $this->conn);
+ $index = $searcher->exec($folder, $str, $this->default_charset);
+ }
+ else {
+ $folder = is_array($folder) ? $folder[0] : $folder;
+ if (!strlen($folder)) {
+ $folder = $this->folder;
+ }
+ $index = $this->conn->search($folder, $str, true);
+ }
return $index;
}
@@ -1656,11 +1703,28 @@
public function refresh_search()
{
if (!empty($this->search_string)) {
- // FIXME: make this work with saved multi-folder searches
- $this->search('', $this->search_string, $this->search_charset, $this->search_sort_field);
+ $this->search(
+ is_object($this->search_set) ? $this->search_set->get_parameters('MAILBOX') : '',
+ $this->search_string,
+ $this->search_charset,
+ $this->search_sort_field
+ );
}
return $this->get_search_set();
+ }
+
+ /**
+ * Flag certain result subsets as 'incomplete'.
+ * For subsequent refresh_search() calls to only refresh the updated parts.
+ */
+ protected function set_search_dirty($folder)
+ {
+ if ($this->search_set && is_a($this->search_set, 'rcube_result_multifolder')) {
+ if ($subset = $this->search_set->get_set($folder)) {
+ $subset->incomplete = $this->search_set->incomplete = true;
+ }
+ }
}
@@ -1675,13 +1739,13 @@
*/
public function get_message_headers($uid, $folder = null, $force = false)
{
- if (!strlen($folder)) {
- $folder = $this->folder;
+ // decode combined UID-folder identifier
+ if (preg_match('/^\d+-.+/', $uid)) {
+ list($uid, $folder) = explode('-', $uid, 2);
}
- // decode combined UID-folder identifier
- if (preg_match('/^\d+-[^,]+$/', $uid)) {
- list($uid, $folder) = explode('-', $uid);
+ if (!strlen($folder)) {
+ $folder = $this->folder;
}
// get cached headers
@@ -1694,6 +1758,9 @@
else {
$headers = $this->conn->fetchHeader(
$folder, $uid, true, true, $this->get_fetch_headers());
+
+ if (is_object($headers))
+ $headers->folder = $folder;
}
return $headers;
@@ -1716,8 +1783,8 @@
}
// decode combined UID-folder identifier
- if (preg_match('/^\d+-[^,]+$/', $uid)) {
- list($uid, $folder) = explode('-', $uid);
+ if (preg_match('/^\d+-.+/', $uid)) {
+ list($uid, $folder) = explode('-', $uid, 2);
}
// Check internal cache
@@ -2366,6 +2433,8 @@
$this->clear_message_cache($folder, $all_mode ? null : explode(',', $uids));
}
}
+
+ $this->set_search_dirty($folder);
}
return $result;
@@ -2413,6 +2482,17 @@
if ($saved) {
// increase messagecount of the target folder
$this->set_messagecount($folder, 'ALL', 1);
+
+ rcube::get_instance()->plugins->exec_hook('message_saved', array(
+ 'folder' => $folder,
+ 'message' => $message,
+ 'headers' => $headers,
+ 'is_file' => $is_file,
+ 'flags' => $flags,
+ 'date' => $date,
+ 'binary' => $binary,
+ 'result' => $saved,
+ ));
}
return $saved;
@@ -2449,19 +2529,7 @@
return false;
}
- // make sure folder exists
- if ($to_mbox != 'INBOX' && !$this->folder_exists($to_mbox)) {
- if (in_array($to_mbox, $this->default_folders)) {
- if (!$this->create_folder($to_mbox, true)) {
- return false;
- }
- }
- else {
- return false;
- }
- }
-
- $config = rcube::get_instance()->config;
+ $config = rcube::get_instance()->config;
$to_trash = $to_mbox == $config->get('trash_mbox');
// flag messages as read before moving them
@@ -2476,6 +2544,9 @@
if ($moved) {
$this->clear_messagecount($from_mbox);
$this->clear_messagecount($to_mbox);
+
+ $this->set_search_dirty($from_mbox);
+ $this->set_search_dirty($to_mbox);
}
// moving failed
else if ($to_trash && $config->get('delete_always', false)) {
@@ -2492,7 +2563,7 @@
if ($this->search_threads || $all_mode) {
$this->refresh_search();
}
- else {
+ else if (!$this->search_set->incomplete) {
$this->search_set->filter(explode(',', $uids), $this->folder);
}
}
@@ -2530,18 +2601,6 @@
if (!$this->check_connection()) {
return false;
- }
-
- // make sure folder exists
- if ($to_mbox != 'INBOX' && !$this->folder_exists($to_mbox)) {
- if (in_array($to_mbox, $this->default_folders)) {
- if (!$this->create_folder($to_mbox, true)) {
- return false;
- }
- }
- else {
- return false;
- }
}
// copy messages
@@ -2592,13 +2651,15 @@
// unset threads internal cache
unset($this->icache['threads']);
+ $this->set_search_dirty($folder);
+
// remove message ids from search set
if ($this->search_set && $folder == $this->folder) {
// threads are too complicated to just remove messages from set
if ($this->search_threads || $all_mode) {
$this->refresh_search();
}
- else {
+ else if (!$this->search_set->incomplete) {
$this->search_set->filter(explode(',', $uids));
}
}
@@ -3059,16 +3120,17 @@
*
* @param string $folder New folder name
* @param boolean $subscribe True if the new folder should be subscribed
+ * @param string $type Optional folder type (junk, trash, drafts, sent, archive)
*
* @return boolean True on success
*/
- public function create_folder($folder, $subscribe=false)
+ public function create_folder($folder, $subscribe = false, $type = null)
{
if (!$this->check_connection()) {
return false;
}
- $result = $this->conn->createFolder($folder);
+ $result = $this->conn->createFolder($folder, $type ? array("\\" . ucfirst($type)) : null);
// try to subscribe it
if ($result) {
@@ -3193,19 +3255,84 @@
/**
- * Create all folders specified as default
+ * Detect special folder associations stored in storage backend
*/
- public function create_default_folders()
+ public function get_special_folders($forced = false)
{
- // create default folders if they do not exist
- foreach ($this->default_folders as $folder) {
- if (!$this->folder_exists($folder)) {
- $this->create_folder($folder, true);
- }
- else if (!$this->folder_exists($folder, true)) {
- $this->subscribe($folder);
+ $result = parent::get_special_folders();
+
+ if (isset($this->icache['special-use'])) {
+ return array_merge($result, $this->icache['special-use']);
+ }
+
+ if (!$forced || !$this->get_capability('SPECIAL-USE')) {
+ return $result;
+ }
+
+ if (!$this->check_connection()) {
+ return $result;
+ }
+
+ $types = array_map(function($value) { return "\\" . ucfirst($value); }, rcube_storage::$folder_types);
+ $special = array();
+
+ // request \Subscribed flag in LIST response as performance improvement for folder_exists()
+ $folders = $this->conn->listMailboxes('', '*', array('SUBSCRIBED'), array('SPECIAL-USE'));
+
+ foreach ($folders as $folder) {
+ if ($flags = $this->conn->data['LIST'][$folder]) {
+ foreach ($types as $type) {
+ if (in_array($type, $flags)) {
+ $type = strtolower(substr($type, 1));
+ $special[$type] = $folder;
+ }
+ }
}
}
+
+ $this->icache['special-use'] = $special;
+ unset($this->icache['special-folders']);
+
+ return array_merge($result, $special);
+ }
+
+
+ /**
+ * Set special folder associations stored in storage backend
+ */
+ public function set_special_folders($specials)
+ {
+ if (!$this->get_capability('SPECIAL-USE') || !$this->get_capability('METADATA')) {
+ return false;
+ }
+
+ if (!$this->check_connection()) {
+ return false;
+ }
+
+ $folders = $this->get_special_folders(true);
+ $old = (array) $this->icache['special-use'];
+
+ foreach ($specials as $type => $folder) {
+ if (in_array($type, rcube_storage::$folder_types)) {
+ $old_folder = $old[$type];
+ if ($old_folder !== $folder) {
+ // unset old-folder metadata
+ if ($old_folder !== null) {
+ $this->delete_metadata($old_folder, array('/private/specialuse'));
+ }
+ // set new folder metadata
+ if ($folder) {
+ $this->set_metadata($folder, array('/private/specialuse' => "\\" . ucfirst($type)));
+ }
+ }
+ }
+ }
+
+ $this->icache['special-use'] = $specials;
+ unset($this->icache['special-folders']);
+
+ return true;
}
@@ -3217,13 +3344,13 @@
*
* @return boolean TRUE or FALSE
*/
- public function folder_exists($folder, $subscription=false)
+ public function folder_exists($folder, $subscription = false)
{
if ($folder == 'INBOX') {
return true;
}
- $key = $subscription ? 'subscribed' : 'existing';
+ $key = $subscription ? 'subscribed' : 'existing';
if (is_array($this->icache[$key]) && in_array($folder, $this->icache[$key])) {
return true;
@@ -3234,10 +3361,24 @@
}
if ($subscription) {
- $a_folders = $this->conn->listSubscribed('', $folder);
+ // It's possible we already called LIST command, check LIST data
+ if (!empty($this->conn->data['LIST']) && !empty($this->conn->data['LIST'][$folder])
+ && in_array('\\Subscribed', $this->conn->data['LIST'][$folder])
+ ) {
+ $a_folders = array($folder);
+ }
+ else {
+ $a_folders = $this->conn->listSubscribed('', $folder);
+ }
}
else {
- $a_folders = $this->conn->listMailboxes('', $folder);
+ // It's possible we already called LIST command, check LIST data
+ if (!empty($this->conn->data['LIST']) && isset($this->conn->data['LIST'][$folder])) {
+ $a_folders = array($folder);
+ }
+ else {
+ $a_folders = $this->conn->listMailboxes('', $folder);
+ }
}
if (is_array($a_folders) && in_array($folder, $a_folders)) {
@@ -3448,7 +3589,7 @@
$options['name'] = $folder;
$options['attributes'] = $this->folder_attributes($folder, true);
$options['namespace'] = $this->folder_namespace($folder);
- $options['special'] = in_array($folder, $this->default_folders);
+ $options['special'] = $this->is_special_folder($folder);
// Set 'noselect' flag
if (is_array($options['attributes'])) {
@@ -3986,6 +4127,7 @@
$a_out = $a_defaults = $folders = array();
$delimiter = $this->get_hierarchy_delimiter();
+ $specials = array_merge(array('INBOX'), array_values($this->get_special_folders()));
// find default folders and skip folders starting with '.'
foreach ($a_folders as $folder) {
@@ -3993,7 +4135,7 @@
continue;
}
- if (!$skip_default && ($p = array_search($folder, $this->default_folders)) !== false && !$a_defaults[$p]) {
+ if (!$skip_default && ($p = array_search($folder, $specials)) !== false && !$a_defaults[$p]) {
$a_defaults[$p] = $folder;
}
else {
--
Gitblit v1.9.1