Merged devel-threads branch (r3066:3364) back into trunk
37 files modified
3 files added
| | |
| | | CHANGELOG RoundCube Webmail |
| | | =========================== |
| | | |
| | | - Threaded message listing now available |
| | | - Added sorting by ARRIVAL and CC |
| | | - Message list columns configurable by the user |
| | | - Removed 'index_sort' option, now we're using empty 'message_sort_col' for this |
| | | - virtuser_query: support other identity data (#1486148) |
| | | - Options virtuser_* replaced with virtuser_* plugins |
| | | - Plugin API: Implemented 'email2user' and 'user2email' hooks |
New file |
| | |
| | | CHANGES IN RELATION TO ORIGINAL PATCH |
| | | - don't add nested messages into selection on collapse if parent message |
| | | is in selection |
| | | - some changes in messages highlighting |
| | | - re-written all changes in rcube_imap.php |
| | | - temporary removed threads caching (see TODO) |
| | | - use depth=0 for roots |
| | | - thread expand state is not stored anywhere |
| | | - removed imap_thread_algorithm option, we're using the best algorithm |
| | | supported by server and implement REFS sorting in Roundcube |
| | | - use underlined subject for root with unread children (icon is still supported) |
| | | - on deleting messages the whole list isn't refreshed |
| | | - added 'expand unread' button |
| | | |
| | | TODO (must have): |
| | | - threads caching |
| | | - updating threaded message list on message delete |
| | | - don't reload messages list on check_recent |
| | | |
| | | TODO (other): |
| | | - performance: fetching all messages for list in "expand all" state only, |
| | | if "expand all" is disabled we should fetch only root messages and fetch |
| | | children on-demand (on expand button click), |
| | | Notice: this is not so simple, because we need to fetch children |
| | | to set "unread_children", but we can fetch only flags instead of |
| | | all headers for each child |
| | | - button in #listcontrols to mark all messages in current thread (with selected |
| | | root or child message), |
| | | + thread tree icons |
| | | + thread css: message row height, thread/status icon alignment |
| | | (change size of all list icons to 14x14) |
| | | - remove 'indexsort' label from localization files |
| | | |
| | | TODO (by the way): |
| | | - use jQuery.inArray instead of find_in_array() (common.js) |
| | | + use only one function (js) to generate messages list |
| | | |
| | | KNOWN ISSUES: |
| | | - on new message (check_recent) the whole list is reloaded |
| | | + table header replacement doesn't work on IE |
| | | - css issues on IE6 |
| | | + css issues on IE7 |
| | | |
| | |
| | | $from = current($IMAP->decode_address_list($headers->from, 1, false)); |
| | | |
| | | fwrite($out, sprintf("From %s %s UID %d\n", $from['mailto'], $headers->date, $headers->uid)); |
| | | fwrite($out, iil_C_FetchPartHeader($IMAP->conn, $IMAP->mailbox, $i, null)); |
| | | fwrite($out, iil_C_HandlePartBody($IMAP->conn, $IMAP->mailbox, $i, null, 1)); |
| | | fwrite($out, iil_C_FetchPartHeader($IMAP->conn, $mbox, $i, null)); |
| | | fwrite($out, iil_C_HandlePartBody($IMAP->conn, $mbox, $i, null, 1)); |
| | | fwrite($out, "\n\n\n"); |
| | | |
| | | progress_update($i, $count); |
| | |
| | | // USER INTERFACE |
| | | // ---------------------------------- |
| | | |
| | | // default sort col |
| | | $rcmail_config['message_sort_col'] = 'date'; |
| | | // default messages sort column. Use empty value for default server's sorting, |
| | | // or 'arrival', 'date', 'subject', 'from', 'to', 'size', 'cc' |
| | | $rcmail_config['message_sort_col'] = ''; |
| | | |
| | | // default sort order |
| | | // default messages sort order |
| | | $rcmail_config['message_sort_order'] = 'DESC'; |
| | | |
| | | // These cols are shown in the message list. Available cols are: |
| | |
| | | // If true, after message delete/move, the next message will be displayed |
| | | $rcmail_config['display_next'] = false; |
| | | |
| | | // If true, messages list will be sorted by message index instead of message date |
| | | $rcmail_config['index_sort'] = true; |
| | | // 0 - Do not expand threads |
| | | // 1 - Expand all threads automatically |
| | | // 2 - Expand only threads with unread messages |
| | | $rcmail_config['autoexpand_threads'] = 0; |
| | | |
| | | // When replying place cursor above original message (top posting) |
| | | $rcmail_config['top_posting'] = false; |
| | |
| | | 'delete-folder' => 'manage_folders.inc', |
| | | 'subscribe' => 'manage_folders.inc', |
| | | 'unsubscribe' => 'manage_folders.inc', |
| | | 'enable-threading' => 'manage_folders.inc', |
| | | 'disable-threading' => 'manage_folders.inc', |
| | | 'add-identity' => 'edit_identity.inc', |
| | | ) |
| | | ); |
| | |
| | | | program/include/html.php | |
| | | | | |
| | | | This file is part of the RoundCube Webmail client | |
| | | | Copyright (C) 2005-2009, RoundCube Dev, - Switzerland | |
| | | | Copyright (C) 2005-2010, RoundCube Dev, - Switzerland | |
| | | | Licensed under the GNU GPL | |
| | | | | |
| | | | PURPOSE: | |
| | |
| | | |
| | | public static $lc_tags = true; |
| | | public static $common_attrib = array('id','class','style','title','align'); |
| | | public static $containers = array('iframe','div','span','p','h1','h2','h3', |
| | | 'form','textarea','table','tr','th','td','style','script'); |
| | | public static $containers = array('iframe','div','span','p','h1','h2','h3','form','textarea','table','thead','tbody','tr','th','td','style','script'); |
| | | |
| | | /** |
| | | * Constructor |
| | |
| | | $this->imap = new rcube_imap($this->db); |
| | | $this->imap->debug_level = $this->config->get('debug_level'); |
| | | $this->imap->skip_deleted = $this->config->get('skip_deleted'); |
| | | $this->imap->index_sort = $this->config->get('index_sort', true); |
| | | |
| | | // enable caching of imap data |
| | | if ($this->config->get('enable_caching')) { |
| | |
| | | | program/include/rcube_imap.php | |
| | | | | |
| | | | This file is part of the RoundCube Webmail client | |
| | | | Copyright (C) 2005-2009, RoundCube Dev. - Switzerland | |
| | | | Copyright (C) 2005-2010, RoundCube Dev. - Switzerland | |
| | | | Licensed under the GNU GPL | |
| | | | | |
| | | | PURPOSE: | |
| | |
| | | */ |
| | | class rcube_imap |
| | | { |
| | | var $db; |
| | | var $conn; |
| | | var $root_dir = ''; |
| | | var $mailbox = 'INBOX'; |
| | | var $list_page = 1; |
| | | var $page_size = 10; |
| | | var $sort_field = 'date'; |
| | | var $sort_order = 'DESC'; |
| | | var $index_sort = true; |
| | | var $delimiter = NULL; |
| | | var $caching_enabled = FALSE; |
| | | var $default_charset = 'ISO-8859-1'; |
| | | var $struct_charset = NULL; |
| | | var $default_folders = array('INBOX'); |
| | | var $fetch_add_headers = ''; |
| | | var $cache = array(); |
| | | var $cache_keys = array(); |
| | | var $cache_changes = array(); |
| | | var $uid_id_map = array(); |
| | | var $msg_headers = array(); |
| | | var $skip_deleted = FALSE; |
| | | var $search_set = NULL; |
| | | var $search_string = ''; |
| | | var $search_charset = ''; |
| | | var $search_sort_field = ''; |
| | | var $debug_level = 1; |
| | | var $error_code = 0; |
| | | var $options = array('auth_method' => 'check'); |
| | | |
| | | public $debug_level = 1; |
| | | public $error_code = 0; |
| | | public $skip_deleted = false; |
| | | public $root_dir = ''; |
| | | public $page_size = 10; |
| | | public $list_page = 1; |
| | | public $delimiter = NULL; |
| | | public $threading = false; |
| | | public $fetch_add_headers = ''; |
| | | public $conn; |
| | | |
| | | private $db; |
| | | private $root_ns = ''; |
| | | private $mailbox = 'INBOX'; |
| | | private $sort_field = ''; |
| | | private $sort_order = 'DESC'; |
| | | private $caching_enabled = false; |
| | | private $default_charset = 'ISO-8859-1'; |
| | | private $struct_charset = NULL; |
| | | private $default_folders = array('INBOX'); |
| | | private $default_folders_lc = array('inbox'); |
| | | private $icache = array(); |
| | | private $cache = array(); |
| | | private $cache_keys = array(); |
| | | private $cache_changes = array(); |
| | | private $uid_id_map = array(); |
| | | private $msg_headers = array(); |
| | | public $search_set = NULL; |
| | | public $search_string = ''; |
| | | private $search_charset = ''; |
| | | private $search_sort_field = ''; |
| | | private $search_threads = false; |
| | | private $db_header_fields = array('idx', 'uid', 'subject', 'from', 'to', 'cc', 'date', 'size'); |
| | | private $options = array('auth_method' => 'check'); |
| | | private $host, $user, $pass, $port, $ssl; |
| | | |
| | | |
| | |
| | | * @param string Charset of search string |
| | | * @param string Sorting field |
| | | */ |
| | | function set_search_set($str=null, $msgs=null, $charset=null, $sort_field=null) |
| | | function set_search_set($str=null, $msgs=null, $charset=null, $sort_field=null, $threads=false) |
| | | { |
| | | if (is_array($str) && $msgs == null) |
| | | list($str, $msgs, $charset, $sort_field) = $str; |
| | | list($str, $msgs, $charset, $sort_field, $threads) = $str; |
| | | if ($msgs != null && !is_array($msgs)) |
| | | $msgs = explode(',', $msgs); |
| | | |
| | | |
| | | $this->search_string = $str; |
| | | $this->search_set = $msgs; |
| | | $this->search_charset = $charset; |
| | | $this->search_sort_field = $sort_field; |
| | | $this->search_threads = $threads; |
| | | } |
| | | |
| | | |
| | |
| | | */ |
| | | function get_search_set() |
| | | { |
| | | return array($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, |
| | | $this->search_threads, |
| | | ); |
| | | } |
| | | |
| | | |
| | |
| | | function get_capability($cap) |
| | | { |
| | | return iil_C_GetCapability($this->conn, strtoupper($cap)); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Sets threading flag to the best supported THREAD algorithm |
| | | * |
| | | * @param boolean TRUE to enable and FALSE |
| | | * @return string Algorithm or false if THREAD is not supported |
| | | * @access public |
| | | */ |
| | | function set_threading($enable=false) |
| | | { |
| | | $this->threading = false; |
| | | |
| | | if ($enable) { |
| | | if ($this->get_capability('THREAD=REFS')) |
| | | $this->threading = 'REFS'; |
| | | else if ($this->get_capability('THREAD=REFERENCES')) |
| | | $this->threading = 'REFERENCES'; |
| | | else if ($this->get_capability('THREAD=ORDEREDSUBJECT')) |
| | | $this->threading = 'ORDEREDSUBJECT'; |
| | | } |
| | | |
| | | return $this->threading; |
| | | } |
| | | |
| | | |
| | |
| | | |
| | | if (empty($mailbox)) |
| | | $mailbox = $this->mailbox; |
| | | |
| | | // count search set |
| | | if ($this->search_string && $mailbox == $this->mailbox && $mode == 'ALL' && !$force) |
| | | return count((array)$this->search_set); |
| | | |
| | | // count search set |
| | | if ($this->search_string && $mailbox == $this->mailbox && ($mode == 'ALL' || $mode == 'THREADS') && !$force) { |
| | | if ($this->search_threads) |
| | | return $mode == 'ALL' ? count((array)$this->search_set['depth']) : count((array)$this->search_set['tree']); |
| | | else |
| | | return count((array)$this->search_set); |
| | | } |
| | | |
| | | $a_mailbox_cache = $this->get_cache('messagecount'); |
| | | |
| | | // return cached value |
| | |
| | | if (!is_array($a_mailbox_cache[$mailbox])) |
| | | $a_mailbox_cache[$mailbox] = array(); |
| | | |
| | | if ($mode == 'THREADS') |
| | | $count = $this->_threadcount($mailbox); |
| | | |
| | | // RECENT count is fetched a bit different |
| | | if ($mode == 'RECENT') |
| | | else if ($mode == 'RECENT') |
| | | $count = iil_C_CheckForRecent($this->conn, $mailbox); |
| | | |
| | | // use SEARCH for message counting |
| | |
| | | |
| | | |
| | | /** |
| | | * Private method for getting nr of threads |
| | | * |
| | | * @access private |
| | | * @see rcube_imap::messagecount() |
| | | */ |
| | | private function _threadcount($mailbox) |
| | | { |
| | | if (!empty($this->icache['threads'])) |
| | | return count($this->icache['threads']['tree']); |
| | | |
| | | list ($thread_tree, $msg_depth, $has_children) = $this->_fetch_threads($mailbox); |
| | | |
| | | // $this->update_thread_cache($mailbox, $thread_tree, $msg_depth, $has_children); |
| | | return count($thread_tree); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Public method for listing headers |
| | | * convert mailbox name with root dir first |
| | | * |
| | |
| | | if ($this->search_string && $mailbox == $this->mailbox) |
| | | return $this->_list_header_set($mailbox, $page, $sort_field, $sort_order, $slice); |
| | | |
| | | if ($this->threading) |
| | | return $this->_list_thread_headers($mailbox, $page, $sort_field, $sort_order, $recursive, $slice); |
| | | |
| | | $this->_set_sort_order($sort_field, $sort_order); |
| | | |
| | | $page = $page ? $page : $this->list_page; |
| | |
| | | // retrieve headers from IMAP |
| | | $a_msg_headers = array(); |
| | | |
| | | // use message index sort for sorting by Date (for better performance) |
| | | if ($this->index_sort && $this->sort_field == 'date') |
| | | // use message index sort as default sorting (for better performance) |
| | | if (!$this->sort_field) |
| | | { |
| | | if ($this->skip_deleted) { |
| | | // @TODO: this could be cached |
| | |
| | | |
| | | |
| | | /** |
| | | * Private method for listing message headers using threads |
| | | * |
| | | * @access private |
| | | * @see rcube_imap::list_headers |
| | | */ |
| | | private function _list_thread_headers($mailbox, $page=NULL, $sort_field=NULL, $sort_order=NULL, $recursive=FALSE, $slice=0) |
| | | { |
| | | $this->_set_sort_order($sort_field, $sort_order); |
| | | |
| | | $page = $page ? $page : $this->list_page; |
| | | // $cache_key = $mailbox.'.msg'; |
| | | // $cache_status = $this->check_cache_status($mailbox, $cache_key); |
| | | |
| | | // get all threads (default sort order) |
| | | list ($thread_tree, $msg_depth, $has_children) = $this->_fetch_threads($mailbox); |
| | | |
| | | if (empty($thread_tree)) |
| | | return array(); |
| | | |
| | | $msg_index = $this->_sort_threads($mailbox, $thread_tree); |
| | | |
| | | return $this->_fetch_thread_headers($mailbox, $thread_tree, $msg_depth, $has_children, |
| | | $msg_index, $page, $slice); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Private method for fetching threads data |
| | | * |
| | | * @param string Mailbox/folder name |
| | | * @return array Array with thread data |
| | | * @access private |
| | | */ |
| | | private function _fetch_threads($mailbox) |
| | | { |
| | | if (empty($this->icache['threads'])) { |
| | | // get all threads |
| | | list ($thread_tree, $msg_depth, $has_children) = iil_C_Thread($this->conn, |
| | | $mailbox, $this->threading, $this->skip_deleted ? 'UNDELETED' : ''); |
| | | |
| | | // add to internal (fast) cache |
| | | $this->icache['threads'] = array(); |
| | | $this->icache['threads']['tree'] = $thread_tree; |
| | | $this->icache['threads']['depth'] = $msg_depth; |
| | | $this->icache['threads']['has_children'] = $has_children; |
| | | } |
| | | |
| | | return array( |
| | | $this->icache['threads']['tree'], |
| | | $this->icache['threads']['depth'], |
| | | $this->icache['threads']['has_children'], |
| | | ); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Private method for fetching threaded messages headers |
| | | * |
| | | * @access private |
| | | */ |
| | | private function _fetch_thread_headers($mailbox, $thread_tree, $msg_depth, $has_children, $msg_index, $page, $slice=0) |
| | | { |
| | | $cache_key = $mailbox.'.msg'; |
| | | // now get IDs for current page |
| | | list($begin, $end) = $this->_get_message_range(count($msg_index), $page); |
| | | $msg_index = array_slice($msg_index, $begin, $end-$begin); |
| | | |
| | | if ($slice) |
| | | $msg_index = array_slice($msg_index, ($this->sort_order == 'DESC' ? 0 : -$slice), $slice); |
| | | |
| | | if ($this->sort_order == 'DESC') |
| | | $msg_index = array_reverse($msg_index); |
| | | |
| | | // flatten threads array |
| | | // @TODO: fetch children only in expanded mode |
| | | $all_ids = array(); |
| | | foreach($msg_index as $root) { |
| | | $all_ids[] = $root; |
| | | if (!empty($thread_tree[$root])) |
| | | $all_ids = array_merge($all_ids, array_keys_recursive($thread_tree[$root])); |
| | | } |
| | | |
| | | // fetch reqested headers from server |
| | | $this->_fetch_headers($mailbox, $all_ids, $a_msg_headers, $cache_key); |
| | | |
| | | // return empty array if no messages found |
| | | if (!is_array($a_msg_headers) || empty($a_msg_headers)) |
| | | return array(); |
| | | |
| | | // use this class for message sorting |
| | | $sorter = new rcube_header_sorter(); |
| | | $sorter->set_sequence_numbers($all_ids); |
| | | $sorter->sort_headers($a_msg_headers); |
| | | |
| | | // Set depth, has_children and unread_children fields in headers |
| | | $this->_set_thread_flags($a_msg_headers, $msg_depth, $has_children); |
| | | |
| | | return array_values($a_msg_headers); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Private method for setting threaded messages flags: |
| | | * depth, has_children and unread_children |
| | | * |
| | | * @param array Reference to headers array indexed by message ID |
| | | * @param array Array of messages depth indexed by message ID |
| | | * @param array Array of messages children flags indexed by message ID |
| | | * @return array Message headers array indexed by message ID |
| | | * @access private |
| | | */ |
| | | private function _set_thread_flags(&$headers, $msg_depth, $msg_children) |
| | | { |
| | | $parents = array(); |
| | | |
| | | foreach ($headers as $idx => $header) { |
| | | $id = $header->id; |
| | | $depth = $msg_depth[$id]; |
| | | $parents = array_slice($parents, 0, $depth); |
| | | |
| | | if (!empty($parents)) { |
| | | $headers[$idx]->parent_uid = end($parents); |
| | | if (!$header->seen) |
| | | $headers[$parents[0]]->unread_children++; |
| | | } |
| | | array_push($parents, $header->uid); |
| | | |
| | | $headers[$idx]->depth = $depth; |
| | | $headers[$idx]->has_children = $msg_children[$id]; |
| | | } |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Private method for listing a set of message headers (search results) |
| | | * |
| | | * @param string Mailbox/folder name |
| | |
| | | if (!strlen($mailbox) || empty($this->search_set)) |
| | | return array(); |
| | | |
| | | // use saved messages from searching |
| | | if ($this->threading) |
| | | return $this->_list_thread_header_set($mailbox, $page, $sort_field, $sort_order, $slice); |
| | | |
| | | // search set is threaded, we need a new one |
| | | if ($this->search_threads) |
| | | $this->search('', $this->search_string, $this->search_charset, $sort_field); |
| | | |
| | | $msgs = $this->search_set; |
| | | $a_msg_headers = array(); |
| | | $page = $page ? $page : $this->list_page; |
| | |
| | | |
| | | $this->_set_sort_order($sort_field, $sort_order); |
| | | |
| | | // quickest method |
| | | if ($this->index_sort && $this->search_sort_field == 'date' && $this->sort_field == 'date') |
| | | // quickest method (default sorting) |
| | | if (!$this->search_sort_field && !$this->sort_field) |
| | | { |
| | | if ($sort_order == 'DESC') |
| | | $msgs = array_reverse($msgs); |
| | |
| | | |
| | | return array_values($a_msg_headers); |
| | | } |
| | | |
| | | // sorted messages, so we can first slice array and then fetch only wanted headers |
| | | if ($this->get_capability('sort') && (!$this->index_sort || $this->sort_field != 'date')) // SORT searching result |
| | | if ($this->get_capability('sort')) // SORT searching result |
| | | { |
| | | // reset search set if sorting field has been changed |
| | | if ($this->sort_field && $this->search_sort_field != $this->sort_field) |
| | |
| | | |
| | | return array_values($a_msg_headers); |
| | | } |
| | | else { // SEARCH searching result, need sorting |
| | | else { // SEARCH result, need sorting |
| | | $cnt = count($msgs); |
| | | // 300: experimantal value for best result |
| | | if (($cnt > 300 && $cnt > $this->page_size) || ($this->index_sort && $this->sort_field == 'date')) { |
| | | if (($cnt > 300 && $cnt > $this->page_size) || !$this->sort_field) { |
| | | // use memory less expensive (and quick) method for big result set |
| | | $a_index = $this->message_index('', $this->sort_field, $this->sort_order); |
| | | // get messages uids for one page... |
| | |
| | | return $a_msg_headers; |
| | | } |
| | | } |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Private method for listing a set of threaded message headers (search results) |
| | | * |
| | | * @param string Mailbox/folder name |
| | | * @param int Current page to list |
| | | * @param string Header field to sort by |
| | | * @param string Sort order [ASC|DESC] |
| | | * @param boolean Number of slice items to extract from result array |
| | | * @return array Indexed array with message header objects |
| | | * @access private |
| | | * @see rcube_imap::list_header_set() |
| | | */ |
| | | private function _list_thread_header_set($mailbox, $page=NULL, $sort_field=NULL, $sort_order=NULL, $slice=0) |
| | | { |
| | | // update search_set if previous data was fetched with disabled threading |
| | | if (!$this->search_threads) |
| | | $this->search('', $this->search_string, $this->search_charset, $sort_field); |
| | | |
| | | $thread_tree = $this->search_set['tree']; |
| | | $msg_depth = $this->search_set['depth']; |
| | | $has_children = $this->search_set['children']; |
| | | $a_msg_headers = array(); |
| | | |
| | | $page = $page ? $page : $this->list_page; |
| | | $start_msg = ($page-1) * $this->page_size; |
| | | |
| | | $this->_set_sort_order($sort_field, $sort_order); |
| | | |
| | | $msg_index = $this->_sort_threads($mailbox, $thread_tree, array_keys($msg_depth)); |
| | | |
| | | return $this->_fetch_thread_headers($mailbox, $thread_tree, $msg_depth, $has_children, $msg_index, $page, $slice=0); |
| | | } |
| | | |
| | | |
| | |
| | | */ |
| | | function message_index($mbox_name='', $sort_field=NULL, $sort_order=NULL) |
| | | { |
| | | if ($this->threading) |
| | | return $this->thread_index($mbox_name, $sort_field, $sort_order); |
| | | |
| | | $this->_set_sort_order($sort_field, $sort_order); |
| | | |
| | | $mailbox = $mbox_name ? $this->mod_mailbox($mbox_name) : $this->mailbox; |
| | | $key = "{$mailbox}:{$this->sort_field}:{$this->sort_order}:{$this->search_string}.msgi"; |
| | | |
| | | // we have a saved search result, get index from there |
| | | if (!isset($this->cache[$key]) && $this->search_string && $mailbox == $this->mailbox) |
| | | if (!isset($this->cache[$key]) && $this->search_string |
| | | && !$this->search_threads && $mailbox == $this->mailbox) |
| | | { |
| | | $this->cache[$key] = array(); |
| | | |
| | | // use message index sort for sorting by Date |
| | | if ($this->index_sort && $this->sort_field == 'date') |
| | | // use message index sort as default sorting |
| | | if (!$this->sort_field) |
| | | { |
| | | $msgs = $this->search_set; |
| | | |
| | |
| | | } |
| | | else |
| | | { |
| | | $a_index = iil_C_FetchHeaderIndex($this->conn, $mailbox, join(',', $this->search_set), $this->sort_field, $this->skip_deleted); |
| | | $a_index = iil_C_FetchHeaderIndex($this->conn, $mailbox, |
| | | join(',', $this->search_set), $this->sort_field, $this->skip_deleted); |
| | | |
| | | if ($this->sort_order=="ASC") |
| | | asort($a_index); |
| | |
| | | return array_keys($a_index); |
| | | } |
| | | |
| | | // use message index sort for sorting by Date |
| | | if ($this->index_sort && $this->sort_field == 'date') |
| | | // use message index sort as default sorting |
| | | if (!$this->sort_field) |
| | | { |
| | | if ($this->skip_deleted) { |
| | | $a_index = $this->_search_index($mailbox, 'ALL'); |
| | |
| | | |
| | | |
| | | /** |
| | | * Return sorted array of threaded message IDs (not UIDs) |
| | | * |
| | | * @param string Mailbox to get index from |
| | | * @param string Sort column |
| | | * @param string Sort order [ASC, DESC] |
| | | * @return array Indexed array with message IDs |
| | | */ |
| | | function thread_index($mbox_name='', $sort_field=NULL, $sort_order=NULL) |
| | | { |
| | | $this->_set_sort_order($sort_field, $sort_order); |
| | | |
| | | $mailbox = $mbox_name ? $this->mod_mailbox($mbox_name) : $this->mailbox; |
| | | $key = "{$mailbox}:{$this->sort_field}:{$this->sort_order}:{$this->search_string}.thi"; |
| | | |
| | | // we have a saved search result, get index from there |
| | | if (!isset($this->cache[$key]) && $this->search_string |
| | | && $this->search_threads && $mailbox == $this->mailbox) |
| | | { |
| | | // use message IDs for better performance |
| | | $ids = array_keys_recursive($this->search_set['tree']); |
| | | $this->cache[$key] = $this->_flatten_threads($mailbox, $this->search_set['tree'], $ids); |
| | | } |
| | | |
| | | // have stored it in RAM |
| | | if (isset($this->cache[$key])) |
| | | return $this->cache[$key]; |
| | | /* |
| | | // check local cache |
| | | $cache_key = $mailbox.'.msg'; |
| | | $cache_status = $this->check_cache_status($mailbox, $cache_key); |
| | | |
| | | // cache is OK |
| | | if ($cache_status>0) |
| | | { |
| | | $a_index = $this->get_message_cache_index($cache_key, TRUE, $this->sort_field, $this->sort_order); |
| | | return array_keys($a_index); |
| | | } |
| | | */ |
| | | // get all threads (default sort order) |
| | | list ($thread_tree) = $this->_fetch_threads($mailbox); |
| | | |
| | | $this->cache[$key] = $this->_flatten_threads($mailbox, $thread_tree); |
| | | |
| | | return $this->cache[$key]; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Return array of threaded messages (all, not only roots) |
| | | * |
| | | * @param string Mailbox to get index from |
| | | * @param array Threaded messages array (see _fetch_threads()) |
| | | * @param array Message IDs if we know what we need (e.g. search result) |
| | | * for better performance |
| | | * @return array Indexed array with message IDs |
| | | * |
| | | * @access private |
| | | */ |
| | | function sync_header_index($mailbox) |
| | | private function _flatten_threads($mailbox, $thread_tree, $ids=null) |
| | | { |
| | | if (empty($thread_tree)) |
| | | return array(); |
| | | |
| | | $msg_index = $this->_sort_threads($mailbox, $thread_tree, $ids); |
| | | |
| | | if ($this->sort_order == 'DESC') |
| | | $msg_index = array_reverse($msg_index); |
| | | |
| | | // flatten threads array |
| | | $all_ids = array(); |
| | | foreach($msg_index as $root) { |
| | | $all_ids[] = $root; |
| | | if (!empty($thread_tree[$root])) |
| | | $all_ids = array_merge($all_ids, array_keys_recursive($thread_tree[$root])); |
| | | } |
| | | |
| | | return $all_ids; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * @access private |
| | | */ |
| | | private function sync_header_index($mailbox) |
| | | { |
| | | $cache_key = $mailbox.'.msg'; |
| | | $cache_index = $this->get_message_cache_index($cache_key); |
| | |
| | | $results = $this->search($mbox_name, $res, NULL, $sort_field); |
| | | } |
| | | |
| | | $this->set_search_set($str, $results, $charset, $sort_field); |
| | | $this->set_search_set($str, $results, $charset, $sort_field, (bool)$this->threading); |
| | | |
| | | return $results; |
| | | } |
| | |
| | | if ($this->skip_deleted && !preg_match('/UNDELETED/', $criteria)) |
| | | $criteria = 'UNDELETED '.$criteria; |
| | | |
| | | if ($sort_field && $this->get_capability('sort') && (!$this->index_sort || $sort_field != 'date')) { |
| | | if ($this->threading) { |
| | | list ($thread_tree, $msg_depth, $has_children) = iil_C_Thread($this->conn, |
| | | $mailbox, $this->threading, $criteria, $charset); |
| | | |
| | | $a_messages = array( |
| | | 'tree' => $thread_tree, |
| | | 'depth' => $msg_depth, |
| | | 'children' => $has_children |
| | | ); |
| | | } |
| | | else if ($sort_field && $this->get_capability('sort')) { |
| | | $charset = $charset ? $charset : $this->default_charset; |
| | | $a_messages = iil_C_Sort($this->conn, $mailbox, $sort_field, $criteria, FALSE, $charset); |
| | | } |
| | |
| | | $a_messages = iil_C_Search($this->conn, $mailbox, ($charset ? "CHARSET $charset " : '') . $criteria); |
| | | |
| | | // I didn't found that SEARCH always returns sorted IDs |
| | | if ($this->index_sort && $this->sort_field == 'date') |
| | | if (!$this->sort_field) |
| | | sort($a_messages); |
| | | } |
| | | } |
| | |
| | | |
| | | |
| | | /** |
| | | * Sort thread |
| | | * |
| | | * @param string Mailbox name |
| | | * @param array Unsorted thread tree (iil_C_Thread() result) |
| | | * @param array Message IDs if we know what we need (e.g. search result) |
| | | * @return array Sorted roots IDs |
| | | * @access private |
| | | */ |
| | | private function _sort_threads($mailbox, $thread_tree, $ids=NULL) |
| | | { |
| | | // THREAD=ORDEREDSUBJECT: sorting by sent date of root message |
| | | // THREAD=REFERENCES: sorting by sent date of root message |
| | | // THREAD=REFS: sorting by the most recent date in each thread |
| | | // default sorting |
| | | if (!$this->sort_field || ($this->sort_field == 'date' && $this->threading == 'REFS')) { |
| | | return array_keys($thread_tree); |
| | | } |
| | | // here we'll implement REFS sorting, for performance reason |
| | | else { // ($sort_field == 'date' && $this->threading != 'REFS') |
| | | // use SORT command |
| | | if ($this->get_capability('sort')) { |
| | | $a_index = iil_C_Sort($this->conn, $mailbox, $this->sort_field, |
| | | !empty($ids) ? $ids : ($this->skip_deleted ? 'UNDELETED' : '')); |
| | | } |
| | | else { |
| | | // fetch specified headers for all messages and sort them |
| | | $a_index = iil_C_FetchHeaderIndex($this->conn, $mailbox, !empty($ids) ? $ids : "1:*", |
| | | $this->sort_field, $this->skip_deleted); |
| | | asort($a_index); // ASC |
| | | $a_index = array_values($a_index); |
| | | } |
| | | |
| | | return $this->_sort_thread_refs($thread_tree, $a_index); |
| | | } |
| | | } |
| | | |
| | | |
| | | /** |
| | | * THREAD=REFS sorting implementation |
| | | * |
| | | * @param array Thread tree array (message identifiers as keys) |
| | | * @param array Array of sorted message identifiers |
| | | * @return array Array of sorted roots messages |
| | | * @access private |
| | | */ |
| | | private function _sort_thread_refs($tree, $index) |
| | | { |
| | | if (empty($tree)) |
| | | return array(); |
| | | |
| | | $index = array_combine(array_values($index), $index); |
| | | |
| | | // assign roots |
| | | foreach ($tree as $idx => $val) { |
| | | $index[$idx] = $idx; |
| | | if (!empty($val)) { |
| | | $idx_arr = array_keys_recursive($tree[$idx]); |
| | | foreach ($idx_arr as $subidx) |
| | | $index[$subidx] = $idx; |
| | | } |
| | | } |
| | | |
| | | $index = array_values($index); |
| | | |
| | | // create sorted array of roots |
| | | $msg_index = array(); |
| | | if ($this->sort_order != 'DESC') { |
| | | foreach ($index as $idx) |
| | | if (!isset($msg_index[$idx])) |
| | | $msg_index[$idx] = $idx; |
| | | $msg_index = array_values($msg_index); |
| | | } |
| | | else { |
| | | for ($x=count($index)-1; $x>=0; $x--) |
| | | if (!isset($msg_index[$index[$x]])) |
| | | $msg_index[$index[$x]] = $index[$x]; |
| | | $msg_index = array_reverse($msg_index); |
| | | } |
| | | |
| | | return $msg_index; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Refresh saved search set |
| | | * |
| | | * @return array Current search set |
| | |
| | | function refresh_search() |
| | | { |
| | | if (!empty($this->search_string)) |
| | | $this->search_set = $this->search('', $this->search_string, $this->search_charset, $this->search_sort_field); |
| | | $this->search_set = $this->search('', $this->search_string, $this->search_charset, |
| | | $this->search_sort_field, $this->search_threads); |
| | | |
| | | return $this->get_search_set(); |
| | | } |
| | |
| | | * @return boolean True on success, False on error |
| | | */ |
| | | function move_message($uids, $to_mbox, $from_mbox='') |
| | | { |
| | | { |
| | | $fbox = $from_mbox; |
| | | $tbox = $to_mbox; |
| | | $to_mbox = $this->mod_mailbox($to_mbox); |
| | |
| | | } |
| | | // moving failed |
| | | else if ($config->get('delete_always', false) && $tbox == $config->get('trash_mbox')) { |
| | | return $this->delete_message($a_uids, $fbox); |
| | | $moved = $this->delete_message($a_uids, $fbox); |
| | | } |
| | | |
| | | // remove message ids from search set |
| | | if ($moved && $this->search_set && $from_mbox == $this->mailbox) { |
| | | foreach ($a_uids as $uid) |
| | | $a_mids[] = $this->_uid2id($uid, $from_mbox); |
| | | $this->search_set = array_diff($this->search_set, $a_mids); |
| | | } |
| | | if ($moved) { |
| | | // unset threads internal cache |
| | | unset($this->icache['threads']); |
| | | |
| | | // update cached message headers |
| | | $cache_key = $from_mbox.'.msg'; |
| | | if ($moved && $start_index = $this->get_message_cache_index_min($cache_key, $a_uids)) { |
| | | // clear cache from the lowest index on |
| | | $this->clear_message_cache($cache_key, $start_index); |
| | | } |
| | | // remove message ids from search set |
| | | if ($this->search_set && $from_mbox == $this->mailbox) { |
| | | // threads are too complicated to just remove messages from set |
| | | if ($this->search_threads) |
| | | $this->refresh_search(); |
| | | else { |
| | | foreach ($a_uids as $uid) |
| | | $a_mids[] = $this->_uid2id($uid, $from_mbox); |
| | | $this->search_set = array_diff($this->search_set, $a_mids); |
| | | } |
| | | } |
| | | |
| | | // update cached message headers |
| | | $cache_key = $from_mbox.'.msg'; |
| | | if ($start_index = $this->get_message_cache_index_min($cache_key, $a_uids)) { |
| | | // clear cache from the lowest index on |
| | | $this->clear_message_cache($cache_key, $start_index); |
| | | } |
| | | } |
| | | |
| | | return $moved; |
| | | } |
| | | } |
| | | |
| | | |
| | | /** |
| | |
| | | * @return boolean True on success, False on error |
| | | */ |
| | | function delete_message($uids, $mbox_name='') |
| | | { |
| | | { |
| | | $mailbox = $mbox_name ? $this->mod_mailbox($mbox_name) : $this->mailbox; |
| | | |
| | | // convert the list of uids to array |
| | |
| | | |
| | | $deleted = iil_C_Delete($this->conn, $mailbox, join(',', $a_uids)); |
| | | |
| | | // send expunge command in order to have the deleted message |
| | | // really deleted from the mailbox |
| | | if ($deleted) |
| | | { |
| | | if ($deleted) { |
| | | // send expunge command in order to have the deleted message |
| | | // really deleted from the mailbox |
| | | $this->_expunge($mailbox, FALSE, $a_uids); |
| | | $this->_clear_messagecount($mailbox); |
| | | unset($this->uid_id_map[$mailbox]); |
| | | } |
| | | |
| | | // remove message ids from search set |
| | | if ($deleted && $this->search_set && $mailbox == $this->mailbox) { |
| | | foreach ($a_uids as $uid) |
| | | $a_mids[] = $this->_uid2id($uid, $mailbox); |
| | | $this->search_set = array_diff($this->search_set, $a_mids); |
| | | // unset threads internal cache |
| | | unset($this->icache['threads']); |
| | | |
| | | // remove message ids from search set |
| | | if ($this->search_set && $mailbox == $this->mailbox) { |
| | | // threads are too complicated to just remove messages from set |
| | | if ($this->search_threads) |
| | | $this->refresh_search(); |
| | | else { |
| | | foreach ($a_uids as $uid) |
| | | $a_mids[] = $this->_uid2id($uid, $mailbox); |
| | | $this->search_set = array_diff($this->search_set, $a_mids); |
| | | } |
| | | } |
| | | |
| | | // remove deleted messages from cache |
| | | $cache_key = $mailbox.'.msg'; |
| | | if ($start_index = $this->get_message_cache_index_min($cache_key, $a_uids)) { |
| | | // clear cache from the lowest index on |
| | | $this->clear_message_cache($cache_key, $start_index); |
| | | } |
| | | } |
| | | |
| | | // remove deleted messages from cache |
| | | $cache_key = $mailbox.'.msg'; |
| | | if ($deleted && $start_index = $this->get_message_cache_index_min($cache_key, $a_uids)) { |
| | | // clear cache from the lowest index on |
| | | $this->clear_message_cache($cache_key, $start_index); |
| | | } |
| | | |
| | | return $deleted; |
| | | } |
| | | } |
| | | |
| | | |
| | | /** |
| | |
| | | private function get_message_cache($key, $from, $to, $sort_field, $sort_order) |
| | | { |
| | | $cache_key = "$key:$from:$to:$sort_field:$sort_order"; |
| | | $db_header_fields = array('idx', 'uid', 'subject', 'from', 'to', 'cc', 'date', 'size'); |
| | | |
| | | $config = rcmail::get_instance()->config; |
| | | |
| | | // use idx sort for sorting by Date with index_sort=true or for unknown field |
| | | if (($sort_field == 'date' && $this->index_sort) |
| | | || !in_array($sort_field, $db_header_fields)) { |
| | | // use idx sort as default sorting |
| | | if (!$sort_field || !in_array($sort_field, $this->db_header_fields)) { |
| | | $sort_field = 'idx'; |
| | | } |
| | | |
| | |
| | | */ |
| | | private function &get_cached_message($key, $uid) |
| | | { |
| | | $internal_key = '__single_msg'; |
| | | $internal_key = 'message'; |
| | | |
| | | if ($this->caching_enabled && !isset($this->cache[$internal_key][$uid])) |
| | | if ($this->caching_enabled && !isset($this->icache[$internal_key][$uid])) |
| | | { |
| | | $sql_result = $this->db->query( |
| | | "SELECT idx, headers, structure |
| | |
| | | if ($sql_arr = $this->db->fetch_assoc($sql_result)) |
| | | { |
| | | $this->uid_id_map[preg_replace('/\.msg$/', '', $key)][$uid] = $sql_arr['idx']; |
| | | $this->cache[$internal_key][$uid] = $this->db->decode(unserialize($sql_arr['headers'])); |
| | | if (is_object($this->cache[$internal_key][$uid]) && !empty($sql_arr['structure'])) |
| | | $this->cache[$internal_key][$uid]->structure = $this->db->decode(unserialize($sql_arr['structure'])); |
| | | $this->icache[$internal_key][$uid] = $this->db->decode(unserialize($sql_arr['headers'])); |
| | | if (is_object($this->icache[$internal_key][$uid]) && !empty($sql_arr['structure'])) |
| | | $this->icache[$internal_key][$uid]->structure = $this->db->decode(unserialize($sql_arr['structure'])); |
| | | } |
| | | } |
| | | |
| | | return $this->cache[$internal_key][$uid]; |
| | | return $this->icache[$internal_key][$uid]; |
| | | } |
| | | |
| | | /** |
| | |
| | | if (!empty($sa_message_index[$key]) && !$force) |
| | | return $sa_message_index[$key]; |
| | | |
| | | // use idx sort for sorting by Date with index_sort=true |
| | | if ($sort_field == 'date' && $this->index_sort) |
| | | // use idx sort as default |
| | | if (!$sort_field || !in_array($sort_field, $this->db_header_fields)) |
| | | $sort_field = 'idx'; |
| | | |
| | | $sa_message_index[$key] = array(); |
| | |
| | | return; |
| | | |
| | | // add to internal (fast) cache |
| | | $this->cache['__single_msg'][$headers->uid] = clone $headers; |
| | | $this->cache['__single_msg'][$headers->uid]->structure = $struct; |
| | | $this->icache['message'][$headers->uid] = clone $headers; |
| | | $this->icache['message'][$headers->uid]->structure = $struct; |
| | | |
| | | // no further caching |
| | | if (!$this->caching_enabled) |
| | |
| | | |
| | | |
| | | /** |
| | | * Get all keys from array (recursive) |
| | | * |
| | | * @param array Input array |
| | | * @return array |
| | | */ |
| | | function array_keys_recursive($array) |
| | | { |
| | | $keys = array(); |
| | | |
| | | if (!empty($array)) |
| | | foreach ($array as $key => $child) { |
| | | $keys[] = $key; |
| | | if ($children = array_keys_recursive($child)) |
| | | $keys = array_merge($keys, $children); |
| | | } |
| | | return $keys; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * mbstring replacement functions |
| | | */ |
| | | |
| | |
| | | if (!isset($old_prefs[$key]) && ($value == $config->get($key))) |
| | | unset($save_prefs[$key]); |
| | | } |
| | | |
| | | |
| | | $save_prefs = serialize($save_prefs); |
| | | |
| | | $this->db->query( |
| | | "UPDATE ".get_table_name('users')." |
| | | SET preferences=?, |
| | | language=? |
| | | WHERE user_id=?", |
| | | serialize($save_prefs), |
| | | $save_prefs, |
| | | $_SESSION['language'], |
| | | $this->ID); |
| | | |
| | | $this->language = $_SESSION['language']; |
| | | if ($this->db->affected_rows()) { |
| | | $config->set_user_prefs($a_user_prefs); |
| | | $this->data['preferences'] = $save_prefs; |
| | | return true; |
| | | } |
| | | |
| | |
| | | | RoundCube Webmail Client Script | |
| | | | | |
| | | | This file is part of the RoundCube Webmail client | |
| | | | Copyright (C) 2005-2009, RoundCube Dev, - Switzerland | |
| | | | Copyright (C) 2005-2010, RoundCube Dev, - Switzerland | |
| | | | Licensed under the GNU GPL | |
| | | | | |
| | | +-----------------------------------------------------------------------+ |
| | | | Authors: Thomas Bruederli <roundcube@gmail.com> | |
| | | | Aleksander 'A.L.E.C' Machniak <alec@alec.pl> | |
| | | | Charles McNulty <charles@charlesmcnulty.com> | |
| | | +-----------------------------------------------------------------------+ |
| | | | Requires: jquery.js, common.js, list.js | |
| | |
| | | switch (this.task) |
| | | { |
| | | case 'mail': |
| | | // enable mail commands |
| | | this.enable_command('list', 'checkmail', 'compose', 'add-contact', 'search', 'reset-search', 'collapse-folder', true); |
| | | |
| | | if (this.gui_objects.messagelist) |
| | | { |
| | | this.message_list = new rcube_list_widget(this.gui_objects.messagelist, {multiselect:true, draggable:true, keyboard:true, dblclick_time:this.dblclick_time}); |
| | | this.message_list = new rcube_list_widget(this.gui_objects.messagelist, |
| | | {multiselect:true, multiexpand:true, draggable:true, keyboard:true, dblclick_time:this.dblclick_time}); |
| | | this.message_list.row_init = function(o){ p.init_message_row(o); }; |
| | | this.message_list.addEventListener('dblclick', function(o){ p.msglist_dbl_click(o); }); |
| | | this.message_list.addEventListener('keypress', function(o){ p.msglist_keypress(o); }); |
| | |
| | | this.message_list.addEventListener('dragstart', function(o){ p.drag_start(o); }); |
| | | this.message_list.addEventListener('dragmove', function(e){ p.drag_move(e); }); |
| | | this.message_list.addEventListener('dragend', function(e){ p.drag_end(e); }); |
| | | this.message_list.addEventListener('expandcollapse', function(e){ p.msglist_expand(e); }); |
| | | document.onmouseup = function(e){ return p.doc_mouse_up(e); }; |
| | | |
| | | this.set_message_coltypes(this.env.coltypes); |
| | | this.message_list.init(); |
| | | this.enable_command('toggle_status', 'toggle_flag', true); |
| | | this.enable_command('toggle_status', 'toggle_flag', 'menu-open', 'menu-save', true); |
| | | |
| | | if (this.gui_objects.mailcontframe) |
| | | this.gui_objects.mailcontframe.onmousedown = function(e){ return p.click_on_list(e); }; |
| | | else |
| | | this.message_list.focus(); |
| | | } |
| | | |
| | | if (this.env.coltypes) |
| | | this.set_message_coltypes(this.env.coltypes); |
| | | |
| | | // enable mail commands |
| | | this.enable_command('list', 'checkmail', 'compose', 'add-contact', 'search', 'reset-search', 'collapse-folder', true); |
| | | // load messages |
| | | if (this.env.messagecount) |
| | | this.command('list'); |
| | | } |
| | | |
| | | if (this.env.search_text != null && document.getElementById('quicksearchbox') != null) |
| | | document.getElementById('quicksearchbox').value = this.env.search_text; |
| | |
| | | this.init_messageform(); |
| | | } |
| | | |
| | | if (this.env.messagecount) |
| | | if (this.env.messagecount) { |
| | | this.enable_command('select-all', 'select-none', 'expunge', true); |
| | | this.enable_command('expand-all', 'expand-unread', 'collapse-all', this.env.threading); |
| | | } |
| | | |
| | | if (this.purge_mailbox_test()) |
| | | this.enable_command('purge', true); |
| | |
| | | this.enable_command('save', 'delete', 'edit', true); |
| | | } |
| | | else if (this.env.action=='folders') |
| | | this.enable_command('subscribe', 'unsubscribe', 'create-folder', 'rename-folder', 'delete-folder', true); |
| | | this.enable_command('subscribe', 'unsubscribe', 'create-folder', 'rename-folder', 'delete-folder', 'enable-threading', 'disable-threading', true); |
| | | |
| | | if (this.gui_objects.identitieslist) |
| | | { |
| | |
| | | this.sections_list.addEventListener('select', function(o){ p.section_select(o); }); |
| | | this.sections_list.init(); |
| | | this.sections_list.focus(); |
| | | this.sections_list.select_first(); |
| | | } |
| | | else if (this.gui_objects.subscriptionlist) |
| | | this.init_subscription_list(); |
| | |
| | | // start keep-alive interval |
| | | this.start_keepalive(); |
| | | }; |
| | | |
| | | // start interval for keep-alive/recent_check signal |
| | | this.start_keepalive = function() |
| | | { |
| | | if (this.env.keep_alive && !this.env.framed && this.task=='mail' && this.gui_objects.mailboxlist) |
| | | this._int = setInterval(function(){ ref.check_for_recent(false); }, this.env.keep_alive * 1000); |
| | | else if (this.env.keep_alive && !this.env.framed && this.task!='login') |
| | | this._int = setInterval(function(){ ref.send_keep_alive(); }, this.env.keep_alive * 1000); |
| | | } |
| | | |
| | | this.init_message_row = function(row) |
| | | { |
| | | var uid = row.uid; |
| | | if (uid && this.env.messages[uid]) |
| | | { |
| | | row.deleted = this.env.messages[uid].deleted ? true : false; |
| | | row.unread = this.env.messages[uid].unread ? true : false; |
| | | row.replied = this.env.messages[uid].replied ? true : false; |
| | | row.flagged = this.env.messages[uid].flagged ? true : false; |
| | | row.forwarded = this.env.messages[uid].forwarded ? true : false; |
| | | } |
| | | |
| | | // set eventhandler to message icon |
| | | if (row.icon = row.obj.getElementsByTagName('td')[0].getElementsByTagName('img')[0]) |
| | | { |
| | | var p = this; |
| | | row.icon.id = 'msgicn_'+row.uid; |
| | | row.icon._row = row.obj; |
| | | row.icon.onmousedown = function(e) { p.command('toggle_status', this); }; |
| | | } |
| | | |
| | | // global variable 'flagged_col' may be not defined yet |
| | | if (!this.env.flagged_col && this.env.coltypes) |
| | | { |
| | | var found; |
| | | if((found = find_in_array('flag', this.env.coltypes)) >= 0) |
| | | this.set_env('flagged_col', found+1); |
| | | } |
| | | |
| | | // set eventhandler to flag icon, if icon found |
| | | if (this.env.flagged_col && (row.flagged_icon = row.obj.getElementsByTagName('td')[this.env.flagged_col].getElementsByTagName('img')[0])) |
| | | { |
| | | var p = this; |
| | | row.flagged_icon.id = 'flaggedicn_'+row.uid; |
| | | row.flagged_icon._row = row.obj; |
| | | row.flagged_icon.onmousedown = function(e) { p.command('toggle_flag', this); }; |
| | | } |
| | | |
| | | this.triggerEvent('insertrow', { uid:uid, row:row }); |
| | | }; |
| | | |
| | | // init message compose form: set focus and eventhandlers |
| | | this.init_messageform = function() |
| | | { |
| | | if (!this.gui_objects.messageform) |
| | | return false; |
| | | |
| | | //this.messageform = this.gui_objects.messageform; |
| | | var input_from = $("[name='_from']"); |
| | | var input_to = $("[name='_to']"); |
| | | var input_subject = $("input[name='_subject']"); |
| | | var input_message = $("[name='_message']").get(0); |
| | | var html_mode = $("input[name='_is_html']").val() == '1'; |
| | | |
| | | // init live search events |
| | | this.init_address_input_events(input_to); |
| | | this.init_address_input_events($("[name='_cc']")); |
| | | this.init_address_input_events($("[name='_bcc']")); |
| | | |
| | | if (!html_mode) |
| | | this.set_caret_pos(input_message, this.env.top_posting ? 0 : $(input_message).val().length); |
| | | |
| | | // add signature according to selected identity |
| | | if (input_from.attr('type') == 'select-one' && $("input[name='_draft_saveid']").val() == '' |
| | | && !html_mode) { // if we have HTML editor, signature is added in callback |
| | | this.change_identity(input_from[0]); |
| | | } |
| | | else if (!html_mode) |
| | | this.set_caret_pos(input_message, this.env.top_posting ? 0 : $(input_message).val().length); |
| | | |
| | | if (input_to.val() == '') |
| | | input_to.focus(); |
| | | else if (input_subject.val() == '') |
| | | input_subject.focus(); |
| | | else if (input_message && !html_mode) |
| | | input_message.focus(); |
| | | |
| | | // get summary of all field values |
| | | this.compose_field_hash(true); |
| | | |
| | | // start the auto-save timer |
| | | this.auto_save_start(); |
| | | }; |
| | | |
| | | this.init_address_input_events = function(obj) |
| | | { |
| | | var handler = function(e){ return ref.ksearch_keypress(e,this); }; |
| | | obj.bind((bw.safari || bw.ie ? 'keydown' : 'keypress'), handler); |
| | | obj.attr('autocomplete', 'off'); |
| | | }; |
| | | |
| | | |
| | | /*********************************************************/ |
| | |
| | | parent.location.href = this.env.permaurl; |
| | | break; |
| | | |
| | | case 'menu-open': |
| | | case 'menu-save': |
| | | this.triggerEvent(command, {props:props}); |
| | | return false; |
| | | break; |
| | | |
| | | case 'open': |
| | | var uid; |
| | | if (uid = this.get_single_uid()) |
| | |
| | | else |
| | | sort_order = 'ASC'; |
| | | |
| | | // set table header class |
| | | $('#rcm'+this.env.sort_col).removeClass('sorted'+(this.env.sort_order.toUpperCase())); |
| | | $('#rcm'+sort_col).addClass('sorted'+sort_order); |
| | | |
| | | // save new sort properties |
| | | this.env.sort_col = sort_col; |
| | | this.env.sort_order = sort_order; |
| | | // set table header and update env |
| | | this.set_list_sorting(sort_col, sort_order); |
| | | |
| | | // reload message list |
| | | this.list_mailbox('', '', sort_col+'_'+sort_order); |
| | |
| | | this.delete_identity(); |
| | | break; |
| | | |
| | | |
| | | // mail task commands |
| | | case 'move': |
| | | case 'moveto': |
| | |
| | | |
| | | case 'select-none': |
| | | this.message_list.clear_selection(); |
| | | break; |
| | | |
| | | case 'expand-all': |
| | | this.env.autoexpand_threads = 1; |
| | | this.message_list.expand_all(); |
| | | break; |
| | | |
| | | case 'expand-unread': |
| | | this.env.autoexpand_threads = 2; |
| | | this.message_list.collapse_all(); |
| | | this.expand_unread(); |
| | | break; |
| | | |
| | | case 'collapse-all': |
| | | this.env.autoexpand_threads = 0; |
| | | this.message_list.collapse_all(); |
| | | break; |
| | | |
| | | case 'nextmessage': |
| | |
| | | case 'unsubscribe': |
| | | this.unsubscribe_folder(props); |
| | | break; |
| | | |
| | | |
| | | case 'enable-threading': |
| | | this.enable_threading(props); |
| | | break; |
| | | |
| | | case 'disable-threading': |
| | | this.disable_threading(props); |
| | | break; |
| | | |
| | | case 'create-folder': |
| | | this.create_folder(props); |
| | | break; |
| | |
| | | if (this.preview_timer) |
| | | clearTimeout(this.preview_timer); |
| | | |
| | | var selected = list.selection.length==1; |
| | | var selected = list.get_single_selection() != null; |
| | | |
| | | // Hide certain command buttons when Drafts folder is selected |
| | | if (this.env.mailbox == this.env.drafts_mailbox) |
| | |
| | | this.show_contentframe(false); |
| | | }; |
| | | |
| | | this.msglist_expand = function(row) |
| | | { |
| | | if (this.env.messages[row.uid]) |
| | | this.env.messages[row.uid].expanded = row.expanded; |
| | | }; |
| | | |
| | | this.check_droptarget = function(id) |
| | | { |
| | | if (this.task == 'mail') |
| | |
| | | /*********************************************************/ |
| | | /********* (message) list functionality *********/ |
| | | /*********************************************************/ |
| | | |
| | | this.init_message_row = function(row) |
| | | { |
| | | var self = this; |
| | | var uid = row.uid; |
| | | |
| | | if (uid && this.env.messages[uid]) |
| | | $.extend(row, this.env.messages[uid]); |
| | | |
| | | // set eventhandler to message icon |
| | | if (this.env.subject_col != null && (row.icon = document.getElementById('msgicn'+row.uid))) { |
| | | row.icon._row = row.obj; |
| | | row.icon.onmousedown = function(e) { self.command('toggle_status', this); }; |
| | | } |
| | | |
| | | // set eventhandler to flag icon, if icon found |
| | | if (this.env.flagged_col != null && (row.flagged_icon = document.getElementById('flaggedicn'+row.uid))) { |
| | | row.flagged_icon._row = row.obj; |
| | | row.flagged_icon.onmousedown = function(e) { self.command('toggle_flag', this); }; |
| | | } |
| | | |
| | | var expando; |
| | | if (!row.depth && row.has_children && (expando = document.getElementById('rcmexpando'+row.uid))) { |
| | | expando.onmousedown = function(e) { return self.expand_message_row(e, uid); }; |
| | | } |
| | | |
| | | this.triggerEvent('insertrow', { uid:uid, row:row }); |
| | | }; |
| | | |
| | | // create a table row in the message list |
| | | this.add_message_row = function(uid, cols, flags, attop) |
| | | { |
| | | if (!this.gui_objects.messagelist || !this.message_list) |
| | | return false; |
| | | |
| | | if (this.message_list.background) |
| | | var tbody = this.message_list.background; |
| | | else |
| | | var tbody = this.gui_objects.messagelist.tBodies[0]; |
| | | |
| | | var rows = this.message_list.rows; |
| | | var rowcount = tbody.rows.length; |
| | | var even = rowcount%2; |
| | | |
| | | if (!this.env.messages[uid]) |
| | | this.env.messages[uid] = {}; |
| | | |
| | | // merge flags over local message object |
| | | $.extend(this.env.messages[uid], { |
| | | deleted: flags.deleted?1:0, |
| | | replied: flags.replied?1:0, |
| | | unread: flags.unread?1:0, |
| | | forwarded: flags.forwarded?1:0, |
| | | flagged: flags.flagged?1:0, |
| | | has_children: flags.has_children?1:0, |
| | | depth: flags.depth?flags.depth:0, |
| | | unread_children: flags.unread_children, |
| | | parent_uid: flags.parent_uid |
| | | }); |
| | | |
| | | var message = this.env.messages[uid]; |
| | | |
| | | var css_class = 'message' |
| | | + (even ? ' even' : ' odd') |
| | | + (flags.unread ? ' unread' : '') |
| | | + (flags.deleted ? ' deleted' : '') |
| | | + (flags.flagged ? ' flagged' : '') |
| | | + (flags.unread_children && !flags.unread ? ' unroot' : '') |
| | | + (this.message_list.in_selection(uid) ? ' selected' : ''); |
| | | |
| | | // for performance use DOM instead of jQuery here |
| | | var row = document.createElement('tr'); |
| | | row.id = 'rcmrow'+uid; |
| | | row.className = css_class; |
| | | |
| | | var icon = this.env.messageicon; |
| | | if (!flags.unread && flags.unread_children > 0 && this.env.unreadchildrenicon) |
| | | icon = this.env.unreadchildrenicon; |
| | | else if (flags.deleted && this.env.deletedicon) |
| | | icon = this.env.deletedicon; |
| | | else if (flags.replied && this.env.repliedicon) |
| | | { |
| | | if (flags.forwarded && this.env.forwardedrepliedicon) |
| | | icon = this.env.forwardedrepliedicon; |
| | | else |
| | | icon = this.env.repliedicon; |
| | | } |
| | | else if (flags.forwarded && this.env.forwardedicon) |
| | | icon = this.env.forwardedicon; |
| | | else if(flags.unread && this.env.unreadicon) |
| | | icon = this.env.unreadicon; |
| | | |
| | | var tree = expando = ''; |
| | | |
| | | if (this.env.threading) |
| | | { |
| | | // This assumes that div width is hardcoded to 15px, |
| | | var width = message.depth * 15; |
| | | if (message.depth) { |
| | | if ((this.env.autoexpand_threads == 0 || this.env.autoexpand_threads == 2) && |
| | | (!rows[message.parent_uid] || !rows[message.parent_uid].expanded)) { |
| | | row.style.display = 'none'; |
| | | message.expanded = false; |
| | | } |
| | | else |
| | | message.expanded = true; |
| | | } |
| | | else if (message.has_children) { |
| | | if (typeof(message.expanded) == 'undefined' && (this.env.autoexpand_threads == 1 || (this.env.autoexpand_threads == 2 && message.unread_children))) { |
| | | message.expanded = true; |
| | | } |
| | | } |
| | | |
| | | if (width) |
| | | tree += '<span id="rcmtab' + uid + '" class="branch" style="width:' + width + 'px;"> </span>'; |
| | | |
| | | if (message.has_children && !message.depth) |
| | | expando = '<div id="rcmexpando' + uid + '" class="' + (message.expanded ? 'expanded' : 'collapsed') + '"> </div>'; |
| | | } |
| | | |
| | | tree += icon ? '<img id="msgicn'+uid+'" src="'+icon+'" alt="" class="msgicon" />' : ''; |
| | | |
| | | // first col is always there |
| | | var col = document.createElement('td'); |
| | | col.className = 'threads'; |
| | | col.innerHTML = expando; |
| | | row.appendChild(col); |
| | | |
| | | // 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+'"'+ |
| | | ' onclick="return rcube_event.cancel(event)">'+cols.subject+'</a>'; |
| | | } |
| | | |
| | | // add each submitted col |
| | | for (var n = 0; n < this.env.coltypes.length; n++) { |
| | | var c = this.env.coltypes[n]; |
| | | col = document.createElement('td'); |
| | | col.className = String(c).toLowerCase(); |
| | | |
| | | var html; |
| | | if (c=='flag') { |
| | | if (flags.flagged && this.env.flaggedicon) |
| | | html = '<img id="flaggedicn'+uid+'" src="'+this.env.flaggedicon+'" class="flagicon" alt="" />'; |
| | | else if(!flags.flagged && this.env.unflaggedicon) |
| | | html = '<img id="flaggedicn'+uid+'" src="'+this.env.unflaggedicon+'" class="flagicon" alt="" />'; |
| | | } |
| | | else if (c=='attachment') |
| | | html = flags.attachment && this.env.attachmenticon ? '<img src="'+this.env.attachmenticon+'" alt="" />' : ' '; |
| | | else if (c=='subject') |
| | | html = tree + cols[c]; |
| | | else |
| | | html = cols[c]; |
| | | |
| | | col.innerHTML = html; |
| | | |
| | | row.appendChild(col); |
| | | } |
| | | |
| | | this.message_list.insert_row(row, attop); |
| | | |
| | | // remove 'old' row |
| | | if (attop && this.env.pagesize && this.message_list.rowcount > this.env.pagesize) { |
| | | var uid = this.message_list.get_last_row(); |
| | | this.message_list.remove_row(uid); |
| | | this.message_list.clear_selection(uid); |
| | | } |
| | | }; |
| | | |
| | | // messages list handling in background (for performance) |
| | | this.offline_message_list = function(flag) |
| | | { |
| | | if (this.message_list) |
| | | this.message_list.set_background_mode(flag); |
| | | }; |
| | | |
| | | this.set_list_sorting = function(sort_col, sort_order) |
| | | { |
| | | // set table header class |
| | | $('#rcm'+this.env.sort_col).removeClass('sorted'+(this.env.sort_order.toUpperCase())); |
| | | if (sort_col) |
| | | $('#rcm'+sort_col).addClass('sorted'+sort_order); |
| | | |
| | | this.env.sort_col = sort_col; |
| | | this.env.sort_order = sort_order; |
| | | } |
| | | |
| | | this.set_list_options = function(cols, sort_col, sort_order, threads) |
| | | { |
| | | var update, add_url = ''; |
| | | |
| | | if (this.env.sort_col != sort_col || this.env.sort_order != sort_order) { |
| | | update = 1; |
| | | this.set_list_sorting(sort_col, sort_order); |
| | | } |
| | | |
| | | if (this.env.threading != threads) { |
| | | update = 1; |
| | | add_url += '&_threads=' + threads; |
| | | } |
| | | |
| | | if (cols.join() != this.env.coltypes.join()) { |
| | | update = 1; |
| | | add_url += '&_cols=' + cols.join(','); |
| | | } |
| | | |
| | | if (update) |
| | | this.list_mailbox('', '', sort_col+'_'+sort_order, add_url); |
| | | } |
| | | |
| | | // when user doble-clicks on a row |
| | | this.show_message = function(id, safe, preview) |
| | |
| | | if (action == 'preview' && this.message_list && this.message_list.rows[id] && this.message_list.rows[id].unread) |
| | | { |
| | | this.set_message(id, 'unread', false); |
| | | this.update_thread_root(id, 'read'); |
| | | if (this.env.unread_counts[this.env.mailbox]) |
| | | { |
| | | this.env.unread_counts[this.env.mailbox] -= 1; |
| | |
| | | + (this.env.mailbox ? '&_mbox='+urlencode(this.env.mailbox) : ''), true); |
| | | } |
| | | |
| | | |
| | | // list messages of a specific mailbox |
| | | this.list_mailbox = function(mbox, page, sort) |
| | | this.list_mailbox = function(mbox, page, sort, add_url) |
| | | { |
| | | var add_url = ''; |
| | | var url = ''; |
| | | var target = window; |
| | | |
| | | if (!mbox) |
| | | mbox = this.env.mailbox; |
| | | |
| | | if (add_url) |
| | | url += add_url; |
| | | |
| | | // add sort to url if set |
| | | if (sort) |
| | | add_url += '&_sort=' + sort; |
| | | url += '&_sort=' + sort; |
| | | |
| | | // also send search request to get the right messages |
| | | if (this.env.search_request) |
| | | add_url += '&_search='+this.env.search_request; |
| | | url += '&_search='+this.env.search_request; |
| | | |
| | | // set page=1 if changeing to another mailbox |
| | | if (!page && this.env.mailbox != mbox) |
| | |
| | | } |
| | | |
| | | if (mbox != this.env.mailbox || (mbox == this.env.mailbox && !page && !sort)) |
| | | add_url += '&_refresh=1'; |
| | | url += '&_refresh=1'; |
| | | |
| | | // unselect selected messages |
| | | this.last_selected = 0; |
| | |
| | | // load message list remotely |
| | | if (this.gui_objects.messagelist) |
| | | { |
| | | this.list_mailbox_remote(mbox, page, add_url); |
| | | this.list_mailbox_remote(mbox, page, url); |
| | | return; |
| | | } |
| | | |
| | | if (this.env.contentframe && window.frames && window.frames[this.env.contentframe]) |
| | | { |
| | | target = window.frames[this.env.contentframe]; |
| | | add_url += '&_framed=1'; |
| | | url += '&_framed=1'; |
| | | } |
| | | |
| | | // load message list to target frame/window |
| | | if (mbox) |
| | | { |
| | | this.set_busy(true, 'loading'); |
| | | target.location.href = this.env.comm_path+'&_mbox='+urlencode(mbox)+(page ? '&_page='+page : '')+add_url; |
| | | target.location.href = this.env.comm_path+'&_mbox='+urlencode(mbox)+(page ? '&_page='+page : '')+url; |
| | | } |
| | | }; |
| | | |
| | |
| | | this.http_request('list', url+add_url, true); |
| | | }; |
| | | |
| | | this.expunge_mailbox = function(mbox) |
| | | // expand all threads with unread children |
| | | this.expand_unread = function() |
| | | { |
| | | var lock = false; |
| | | var add_url = ''; |
| | | var tbody = this.gui_objects.messagelist.tBodies[0]; |
| | | var new_row = tbody.firstChild; |
| | | var r; |
| | | |
| | | // lock interface if it's the active mailbox |
| | | if (mbox == this.env.mailbox) |
| | | { |
| | | lock = true; |
| | | this.set_busy(true, 'loading'); |
| | | add_url = '&_reload=1'; |
| | | } |
| | | |
| | | // send request to server |
| | | var url = '_mbox='+urlencode(mbox); |
| | | this.http_post('expunge', url+add_url, lock); |
| | | while (new_row) { |
| | | if (new_row.nodeType == 1 && (r = this.message_list.rows[new_row.uid]) |
| | | && r.unread_children) { |
| | | this.message_list.expand_all(r); |
| | | var expando = document.getElementById('rcmexpando' + r.uid); |
| | | if (expando) |
| | | expando.className = 'expanded'; |
| | | this.set_unread_children(r.uid); |
| | | } |
| | | new_row = new_row.nextSibling; |
| | | } |
| | | return false; |
| | | }; |
| | | |
| | | this.purge_mailbox = function(mbox) |
| | | // thread expanding/collapsing handler |
| | | this.expand_message_row = function(e, uid) |
| | | { |
| | | var lock = false; |
| | | var add_url = ''; |
| | | |
| | | if (!confirm(this.get_label('purgefolderconfirm'))) |
| | | return false; |
| | | |
| | | // lock interface if it's the active mailbox |
| | | if (mbox == this.env.mailbox) |
| | | { |
| | | lock = true; |
| | | this.set_busy(true, 'loading'); |
| | | add_url = '&_reload=1'; |
| | | } |
| | | var row = this.message_list.rows[uid]; |
| | | |
| | | // send request to server |
| | | var url = '_mbox='+urlencode(mbox); |
| | | this.http_post('purge', url+add_url, lock); |
| | | return true; |
| | | // handle unread_children mark |
| | | row.expanded = !row.expanded; |
| | | this.set_unread_children(uid); |
| | | row.expanded = !row.expanded; |
| | | |
| | | this.message_list.expand_row(e, uid); |
| | | }; |
| | | |
| | | // test if purge command is allowed |
| | | this.purge_mailbox_test = function() |
| | | // message list expanding |
| | | this.expand_threads = function() |
| | | { |
| | | if (!this.env.threading || !this.env.autoexpand_threads || !this.message_list) |
| | | return; |
| | | |
| | | switch (this.env.autoexpand_threads) { |
| | | case 2: this.expand_unread(); break; |
| | | case 1: this.message_list.expand_all(); break; |
| | | } |
| | | // this.message_list.expand(null); |
| | | } |
| | | |
| | | // update parent in a thread |
| | | this.update_thread_root = function(uid, flag) |
| | | { |
| | | return (this.env.messagecount && (this.env.mailbox == this.env.trash_mailbox || this.env.mailbox == this.env.junk_mailbox |
| | | || this.env.mailbox.match('^' + RegExp.escape(this.env.trash_mailbox) + RegExp.escape(this.env.delimiter)) |
| | | || this.env.mailbox.match('^' + RegExp.escape(this.env.junk_mailbox) + RegExp.escape(this.env.delimiter)))); |
| | | if (!this.env.threading) |
| | | return; |
| | | |
| | | var root = this.find_thread_root(uid); |
| | | |
| | | if (uid == root) |
| | | return; |
| | | |
| | | var p = this.message_list.rows[root]; |
| | | |
| | | if (flag == 'read' && p.unread_children) { |
| | | p.unread_children--; |
| | | } else if (flag == 'unread' && p.has_children) { |
| | | // unread_children may be undefined |
| | | p.unread_children = p.unread_children ? p.unread_children + 1 : 1; |
| | | } else { |
| | | return; |
| | | } |
| | | |
| | | this.set_message_icon(root); |
| | | this.set_unread_children(root); |
| | | }; |
| | | |
| | | // finds root message for specified thread |
| | | this.find_thread_root = function(uid) |
| | | { |
| | | var r = this.message_list.rows[uid]; |
| | | |
| | | if (r.parent_uid) |
| | | return this.find_thread_root(r.parent_uid); |
| | | else |
| | | return uid; |
| | | } |
| | | |
| | | // update thread indicators for all messages in a thread below the specified message |
| | | // return number of removed/added root level messages |
| | | this.update_thread = function (uid) |
| | | { |
| | | if (!this.env.threading) |
| | | return 0; |
| | | |
| | | var rows = this.message_list.rows; |
| | | var row = rows[uid] |
| | | var depth = rows[uid].depth; |
| | | var r, parent, count = 0; |
| | | var roots = new Array(); |
| | | |
| | | if (!row.depth) // root message: decrease roots count |
| | | count--; |
| | | else if (row.unread) { |
| | | // update unread_children for thread root |
| | | var parent = this.find_thread_root(uid); |
| | | rows[parent].unread_children--; |
| | | this.set_unread_children(parent); |
| | | } |
| | | |
| | | parent = row.parent_uid; |
| | | |
| | | // childrens |
| | | row = row.obj.nextSibling; |
| | | while (row) { |
| | | if (row.nodeType == 1 && (r = rows[row.uid])) { |
| | | if (!r.depth || r.depth <= depth) |
| | | break; |
| | | |
| | | r.depth--; // move left |
| | | $('#rcmtab'+r.uid).width(r.depth * 15); |
| | | 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) |
| | | .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); }); |
| | | |
| | | r.unread_children = 0; |
| | | roots[roots.length] = r; |
| | | } |
| | | // show if it was hidden |
| | | if (r.obj.style.display == 'none') |
| | | $(r.obj).show(); |
| | | } |
| | | else { |
| | | if (r.depth == depth) |
| | | r.parent_uid = parent; |
| | | if (r.unread && roots.length) { |
| | | roots[roots.length-1].unread_children++; |
| | | } |
| | | } |
| | | } |
| | | row = row.nextSibling; |
| | | } |
| | | |
| | | // update unread_children for roots |
| | | for (var i=0; i<roots.length; i++) |
| | | this.set_unread_children(roots[i].uid); |
| | | |
| | | return count; |
| | | }; |
| | | |
| | | this.delete_excessive_thread_rows = function() |
| | | { |
| | | var rows = this.message_list.rows; |
| | | var tbody = this.message_list.list.tBodies[0]; |
| | | var row = tbody.firstChild; |
| | | var cnt = this.env.pagesize + 1; |
| | | |
| | | while (row) { |
| | | if (row.nodeType == 1 && (r = rows[row.uid])) { |
| | | if (!r.depth && cnt) |
| | | cnt--; |
| | | |
| | | if (!cnt) |
| | | this.message_list.remove_row(row.uid); |
| | | } |
| | | row = row.nextSibling; |
| | | } |
| | | } |
| | | |
| | | // set message icon |
| | | this.set_message_icon = function(uid) |
| | |
| | | |
| | | if (!rows[uid]) |
| | | return false; |
| | | |
| | | if (rows[uid].deleted && this.env.deletedicon) |
| | | if (!rows[uid].unread && rows[uid].unread_children && this.env.unreadchildrenicon) { |
| | | icn_src = this.env.unreadchildrenicon; |
| | | } |
| | | else if (rows[uid].deleted && this.env.deletedicon) |
| | | icn_src = this.env.deletedicon; |
| | | else if (rows[uid].replied && this.env.repliedicon) |
| | | { |
| | |
| | | icn_src = this.env.flaggedicon; |
| | | else if (!rows[uid].flagged && this.env.unflaggedicon) |
| | | icn_src = this.env.unflaggedicon; |
| | | |
| | | if (rows[uid].flagged_icon && icn_src) |
| | | rows[uid].flagged_icon.src = icn_src; |
| | | } |
| | |
| | | else if (flag == 'flagged') |
| | | rows[uid].flagged = status; |
| | | |
| | | this.env.messages[uid] = rows[uid]; |
| | | // this.env.messages[uid] = rows[uid]; |
| | | } |
| | | |
| | | // set message row status, class and icon |
| | |
| | | |
| | | if (flag) |
| | | this.set_message_status(uid, flag, status); |
| | | |
| | | |
| | | var rowobj = $(rows[uid].obj); |
| | | if (rows[uid].unread && rows[uid].classname.indexOf('unread')<0) |
| | | { |
| | | rows[uid].classname += ' unread'; |
| | | |
| | | if (rows[uid].unread && !rowobj.hasClass('unread')) |
| | | rowobj.addClass('unread'); |
| | | } |
| | | else if (!rows[uid].unread && rows[uid].classname.indexOf('unread')>=0) |
| | | { |
| | | rows[uid].classname = rows[uid].classname.replace(/\s*unread/, ''); |
| | | else if (!rows[uid].unread && rowobj.hasClass('unread')) |
| | | rowobj.removeClass('unread'); |
| | | } |
| | | |
| | | if (rows[uid].deleted && rows[uid].classname.indexOf('deleted')<0) |
| | | { |
| | | rows[uid].classname += ' deleted'; |
| | | if (rows[uid].deleted && !rowobj.hasClass('deleted')) |
| | | rowobj.addClass('deleted'); |
| | | } |
| | | else if (!rows[uid].deleted && rows[uid].classname.indexOf('deleted')>=0) |
| | | { |
| | | rows[uid].classname = rows[uid].classname.replace(/\s*deleted/, ''); |
| | | else if (!rows[uid].deleted && rowobj.hasClass('deleted')) |
| | | rowobj.removeClass('deleted'); |
| | | } |
| | | |
| | | if (rows[uid].flagged && rows[uid].classname.indexOf('flagged')<0) |
| | | { |
| | | rows[uid].classname += ' flagged'; |
| | | if (rows[uid].flagged && !rowobj.hasClass('flagged')) |
| | | rowobj.addClass('flagged'); |
| | | } |
| | | else if (!rows[uid].flagged && rows[uid].classname.indexOf('flagged')>=0) |
| | | { |
| | | rows[uid].classname = rows[uid].classname.replace(/\s*flagged/, ''); |
| | | else if (!rows[uid].flagged && rowobj.hasClass('flagged')) |
| | | rowobj.removeClass('flagged'); |
| | | } |
| | | |
| | | this.set_unread_children(uid); |
| | | this.set_message_icon(uid); |
| | | } |
| | | }; |
| | | |
| | | // sets unroot (unread_children) class of parent row |
| | | this.set_unread_children = function(uid) |
| | | { |
| | | var row = this.message_list.rows[uid]; |
| | | |
| | | if (row.parent_uid || !row.has_children) |
| | | return; |
| | | |
| | | if (!row.unread && row.unread_children && !row.expanded) |
| | | $(row.obj).addClass('unroot'); |
| | | else |
| | | $(row.obj).removeClass('unroot'); |
| | | }; |
| | | |
| | | // move selected messages to the specified mailbox |
| | | this.move_messages = function(mbox) |
| | |
| | | return; |
| | | |
| | | // if config is set to flag for deletion |
| | | if (this.env.flag_for_deletion) |
| | | if (this.env.flag_for_deletion) { |
| | | this.mark_message('delete'); |
| | | return false; |
| | | } |
| | | // if there isn't a defined trash mailbox or we are in it |
| | | else if (!this.env.trash_mailbox || this.env.mailbox == this.env.trash_mailbox) |
| | | this.permanently_remove_messages(); |
| | |
| | | else |
| | | this.move_messages(this.env.trash_mailbox); |
| | | } |
| | | |
| | | return true; |
| | | }; |
| | | |
| | | // delete the selected messages permanently |
| | |
| | | this._with_selected_messages('delete', false, '&_from='+(this.env.action ? this.env.action : '')); |
| | | }; |
| | | |
| | | // Send a specifc request with UIDs of all selected messages |
| | | // Send a specifc moveto/delete request with UIDs of all selected messages |
| | | // @private |
| | | this._with_selected_messages = function(action, lock, add_url, remove) |
| | | this._with_selected_messages = function(action, lock, add_url) |
| | | { |
| | | var a_uids = new Array(); |
| | | var count = 0; |
| | | |
| | | if (this.env.uid) |
| | | a_uids[0] = this.env.uid; |
| | | else |
| | | { |
| | | var selection = this.message_list.get_selection(); |
| | | var rows = this.message_list.rows; |
| | | var id; |
| | | for (var n=0; n<selection.length; n++) { |
| | | id = selection[n]; |
| | | a_uids[a_uids.length] = id; |
| | | count += this.update_thread(id); |
| | | this.message_list.remove_row(id, (this.env.display_next && n == selection.length-1)); |
| | | } |
| | | // make sure there are no selected rows |
| | |
| | | |
| | | if (this.env.display_next && this.env.next_uid) |
| | | add_url += '&_next_uid='+this.env.next_uid; |
| | | |
| | | if (count < 0) |
| | | add_url += '&_count='+(count*-1); |
| | | else if (count > 0) |
| | | // remove threads from the end of the list |
| | | this.delete_excessive_thread_rows(); |
| | | |
| | | // send request to server |
| | | this.http_post(action, '_uid='+a_uids.join(',')+'&_mbox='+urlencode(this.env.mailbox)+add_url, lock); |
| | |
| | | this.set_message(a_uids[i], 'unread', (flag=='unread' ? true : false)); |
| | | |
| | | this.http_post('mark', '_uid='+a_uids.join(',')+'&_flag='+flag); |
| | | |
| | | for (var i=0; i<a_uids.length; i++) |
| | | this.update_thread_root(a_uids[i], flag); |
| | | }; |
| | | |
| | | // set image to flagged or unflagged |
| | |
| | | var add_url = ''; |
| | | var r_uids = new Array(); |
| | | var rows = this.message_list ? this.message_list.rows : new Array(); |
| | | |
| | | var count = 0; |
| | | |
| | | for (var i=0; i<a_uids.length; i++) |
| | | { |
| | | uid = a_uids[i]; |
| | |
| | | if (rows[uid].unread) |
| | | r_uids[r_uids.length] = uid; |
| | | |
| | | if (this.env.skip_deleted) |
| | | if (this.env.skip_deleted) { |
| | | count += this.update_thread(uid); |
| | | this.message_list.remove_row(uid, (this.env.display_next && i == this.message_list.selection.length-1)); |
| | | } |
| | | else |
| | | this.set_message(uid, 'deleted', true); |
| | | } |
| | | } |
| | | |
| | | // make sure there are no selected rows |
| | | if (this.env.skip_deleted && !this.env.display_next && this.message_list) |
| | | if (this.env.skip_deleted && this.message_list) { |
| | | if(!this.env.display_next) |
| | | this.message_list.clear_selection(); |
| | | if (count < 0) |
| | | add_url += '&_count='+(count*-1); |
| | | else if (count > 0) |
| | | // remove threads from the end of the list |
| | | this.delete_excessive_thread_rows(); |
| | | } |
| | | |
| | | add_url = '&_from='+(this.env.action ? this.env.action : ''); |
| | | |
| | |
| | | this.set_message(uid, 'unread', false); |
| | | } |
| | | }; |
| | | |
| | | |
| | | |
| | | /*********************************************************/ |
| | | /********* mailbox folders methods *********/ |
| | | /*********************************************************/ |
| | | |
| | | this.expunge_mailbox = function(mbox) |
| | | { |
| | | var lock = false; |
| | | var add_url = ''; |
| | | |
| | | // lock interface if it's the active mailbox |
| | | if (mbox == this.env.mailbox) |
| | | { |
| | | lock = true; |
| | | this.set_busy(true, 'loading'); |
| | | add_url = '&_reload=1'; |
| | | } |
| | | |
| | | // send request to server |
| | | var url = '_mbox='+urlencode(mbox); |
| | | this.http_post('expunge', url+add_url, lock); |
| | | }; |
| | | |
| | | this.purge_mailbox = function(mbox) |
| | | { |
| | | var lock = false; |
| | | var add_url = ''; |
| | | |
| | | if (!confirm(this.get_label('purgefolderconfirm'))) |
| | | return false; |
| | | |
| | | // lock interface if it's the active mailbox |
| | | if (mbox == this.env.mailbox) |
| | | { |
| | | lock = true; |
| | | this.set_busy(true, 'loading'); |
| | | add_url = '&_reload=1'; |
| | | } |
| | | |
| | | // send request to server |
| | | var url = '_mbox='+urlencode(mbox); |
| | | this.http_post('purge', url+add_url, lock); |
| | | return true; |
| | | }; |
| | | |
| | | // test if purge command is allowed |
| | | this.purge_mailbox_test = function() |
| | | { |
| | | return (this.env.messagecount && (this.env.mailbox == this.env.trash_mailbox || this.env.mailbox == this.env.junk_mailbox |
| | | || this.env.mailbox.match('^' + RegExp.escape(this.env.trash_mailbox) + RegExp.escape(this.env.delimiter)) |
| | | || this.env.mailbox.match('^' + RegExp.escape(this.env.junk_mailbox) + RegExp.escape(this.env.delimiter)))); |
| | | }; |
| | | |
| | | |
| | | /*********************************************************/ |
| | | /********* login form methods *********/ |
| | |
| | | /********* message compose methods *********/ |
| | | /*********************************************************/ |
| | | |
| | | // init message compose form: set focus and eventhandlers |
| | | this.init_messageform = function() |
| | | { |
| | | if (!this.gui_objects.messageform) |
| | | return false; |
| | | |
| | | //this.messageform = this.gui_objects.messageform; |
| | | var input_from = $("[name='_from']"); |
| | | var input_to = $("[name='_to']"); |
| | | var input_subject = $("input[name='_subject']"); |
| | | var input_message = $("[name='_message']").get(0); |
| | | var html_mode = $("input[name='_is_html']").val() == '1'; |
| | | |
| | | // init live search events |
| | | this.init_address_input_events(input_to); |
| | | this.init_address_input_events($("[name='_cc']")); |
| | | this.init_address_input_events($("[name='_bcc']")); |
| | | |
| | | if (!html_mode) |
| | | this.set_caret_pos(input_message, this.env.top_posting ? 0 : $(input_message).val().length); |
| | | |
| | | // add signature according to selected identity |
| | | if (input_from.attr('type') == 'select-one' && $("input[name='_draft_saveid']").val() == '' |
| | | && !html_mode) { // if we have HTML editor, signature is added in callback |
| | | this.change_identity(input_from[0]); |
| | | } |
| | | else if (!html_mode) |
| | | this.set_caret_pos(input_message, this.env.top_posting ? 0 : $(input_message).val().length); |
| | | |
| | | if (input_to.val() == '') |
| | | input_to.focus(); |
| | | else if (input_subject.val() == '') |
| | | input_subject.focus(); |
| | | else if (input_message && !html_mode) |
| | | input_message.focus(); |
| | | |
| | | // get summary of all field values |
| | | this.compose_field_hash(true); |
| | | |
| | | // start the auto-save timer |
| | | this.auto_save_start(); |
| | | }; |
| | | |
| | | this.init_address_input_events = function(obj) |
| | | { |
| | | var handler = function(e){ return ref.ksearch_keypress(e,this); }; |
| | | obj.bind((bw.safari || bw.ie ? 'keydown' : 'keypress'), handler); |
| | | obj.attr('autocomplete', 'off'); |
| | | }; |
| | | |
| | | // checks the input fields before sending a message |
| | | this.check_compose_input = function() |
| | | { |
| | | { |
| | | // check input fields |
| | | var input_to = $("[name='_to']"); |
| | | var input_cc = $("[name='_cc']"); |
| | |
| | | } |
| | | |
| | | // display localized warning for missing subject |
| | | if (input_subject.val() == '') |
| | | { |
| | | if (input_subject.val() == '') { |
| | | var subject = prompt(this.get_label('nosubjectwarning'), this.get_label('nosubject')); |
| | | |
| | | // user hit cancel, so don't send |
| | | if (!subject && subject !== '') |
| | | { |
| | | if (!subject && subject !== '') { |
| | | input_subject.focus(); |
| | | return false; |
| | | } |
| | | else |
| | | { |
| | | input_subject.val((subject ? subject : this.get_label('nosubject'))); |
| | | } |
| | | } |
| | | else |
| | | input_subject.val((subject ? subject : this.get_label('nosubject'))); |
| | | } |
| | | |
| | | // check for empty body |
| | | if ((!window.tinyMCE || !tinyMCE.get(this.env.composebody)) |
| | | && input_message.val() == '' && !confirm(this.get_label('nobodywarning'))) |
| | | { |
| | | && input_message.val() == '' && !confirm(this.get_label('nobodywarning'))) { |
| | | input_message.focus(); |
| | | return false; |
| | | } |
| | | } |
| | | else if (window.tinyMCE && tinyMCE.get(this.env.composebody) |
| | | && !tinyMCE.get(this.env.composebody).getContent() |
| | | && !confirm(this.get_label('nobodywarning'))) |
| | | { |
| | | && !tinyMCE.get(this.env.composebody).getContent() |
| | | && !confirm(this.get_label('nobodywarning'))) { |
| | | tinyMCE.get(this.env.composebody).focus(); |
| | | return false; |
| | | } |
| | | } |
| | | |
| | | // Apply spellcheck changes if spell checker is active |
| | | this.stop_spellchecking(); |
| | |
| | | tinyMCE.triggerSave(); |
| | | |
| | | return true; |
| | | }; |
| | | }; |
| | | |
| | | this.stop_spellchecking = function() |
| | | { |
| | | { |
| | | if (this.env.spellcheck && !this.spellcheck_ready) { |
| | | $(this.env.spellcheck.spell_span).trigger('click'); |
| | | this.set_spellcheck_state('ready'); |
| | | } |
| | | }; |
| | | } |
| | | }; |
| | | |
| | | this.display_spellcheck_controls = function(vis) |
| | | { |
| | | { |
| | | if (this.env.spellcheck) { |
| | | // stop spellchecking process |
| | | if (!vis) |
| | | this.stop_spellchecking(); |
| | | this.stop_spellchecking(); |
| | | |
| | | $(this.env.spellcheck.spell_container).css('visibility', vis ? 'visible' : 'hidden'); |
| | | } |
| | | }; |
| | | }; |
| | | |
| | | this.set_spellcheck_state = function(s) |
| | | { |
| | |
| | | if (folder) |
| | | this.http_post('unsubscribe', '_mbox='+urlencode(folder)); |
| | | }; |
| | | |
| | | this.enable_threading = function(folder) |
| | | { |
| | | if (folder) |
| | | this.http_post('enable-threading', '_mbox='+urlencode(folder)); |
| | | }; |
| | | |
| | | this.disable_threading = function(folder) |
| | | { |
| | | if (folder) |
| | | this.http_post('disable-threading', '_mbox='+urlencode(folder)); |
| | | }; |
| | | |
| | | |
| | | // helper method to find a specific mailbox row ID |
| | | this.get_folder_row_id = function(folder) |
| | | { |
| | |
| | | return null; |
| | | }; |
| | | |
| | | // for reordering column array, Konqueror workaround |
| | | this.set_message_coltypes = function(coltypes) |
| | | // for reordering column array (Konqueror workaround) |
| | | // and for setting some message list global variables |
| | | this.set_message_coltypes = function(coltypes, repl) |
| | | { |
| | | this.coltypes = coltypes; |
| | | this.env.coltypes = coltypes; |
| | | |
| | | // set correct list titles |
| | | var cell, col; |
| | | var thead = this.gui_objects.messagelist ? this.gui_objects.messagelist.tHead : null; |
| | | for (var n=0; thead && n<this.coltypes.length; n++) |
| | | |
| | | // replace old column headers |
| | | if (thead && repl) { |
| | | for (var cell, c=0; c < repl.length; c++) { |
| | | cell = thead.rows[0].cells[c]; |
| | | if (!cell) { |
| | | cell = document.createElement('td'); |
| | | thead.rows[0].appendChild(cell); |
| | | } |
| | | cell.innerHTML = repl[c].html; |
| | | if (repl[c].id) cell.id = repl[c].id; |
| | | if (repl[c].className) cell.className = repl[c].className; |
| | | } |
| | | } |
| | | |
| | | var cell, col, n; |
| | | for (n=0; thead && n<this.env.coltypes.length; n++) |
| | | { |
| | | col = this.coltypes[n]; |
| | | col = this.env.coltypes[n]; |
| | | if ((cell = thead.rows[0].cells[n+1]) && (col=='from' || col=='to')) |
| | | { |
| | | // if we have links for sorting, it's a bit more complicated... |
| | | if (cell.firstChild && cell.firstChild.tagName.toLowerCase()=='a') |
| | | { |
| | | cell.firstChild.innerHTML = this.get_label(this.coltypes[n]); |
| | | cell.firstChild.innerHTML = this.get_label(this.env.coltypes[n]); |
| | | cell.firstChild.onclick = function(){ return rcmail.command('sort', this.__col, this); }; |
| | | cell.firstChild.__col = col; |
| | | } |
| | | else |
| | | cell.innerHTML = this.get_label(this.coltypes[n]); |
| | | cell.innerHTML = this.get_label(this.env.coltypes[n]); |
| | | |
| | | cell.id = 'rcm'+col; |
| | | } |
| | | else if (col == 'subject' && this.message_list) |
| | | this.message_list.subject_col = n+1; |
| | | } |
| | | }; |
| | | |
| | | // create a table row in the message list |
| | | this.add_message_row = function(uid, cols, flags, attachment, attop) |
| | | { |
| | | if (!this.gui_objects.messagelist || !this.message_list) |
| | | return false; |
| | | |
| | | if (this.message_list.background) |
| | | var tbody = this.message_list.background; |
| | | else |
| | | var tbody = this.gui_objects.messagelist.tBodies[0]; |
| | | |
| | | var rowcount = tbody.rows.length; |
| | | var even = rowcount%2; |
| | | |
| | | this.env.messages[uid] = { |
| | | deleted: flags.deleted?1:0, |
| | | replied: flags.replied?1:0, |
| | | unread: flags.unread?1:0, |
| | | forwarded: flags.forwarded?1:0, |
| | | flagged:flags.flagged?1:0 |
| | | }; |
| | | |
| | | var css_class = 'message' |
| | | + (even ? ' even' : ' odd') |
| | | + (flags.unread ? ' unread' : '') |
| | | + (flags.deleted ? ' deleted' : '') |
| | | + (flags.flagged ? ' flagged' : '') |
| | | + (this.message_list.in_selection(uid) ? ' selected' : ''); |
| | | |
| | | // for performance use DOM instead of jQuery here |
| | | var row = document.createElement('tr'); |
| | | row.id = 'rcmrow'+uid; |
| | | row.className = css_class; |
| | | |
| | | var icon = this.env.messageicon; |
| | | if (flags.deleted && this.env.deletedicon) |
| | | icon = this.env.deletedicon; |
| | | else if (flags.replied && this.env.repliedicon) |
| | | { |
| | | if (flags.forwarded && this.env.forwardedrepliedicon) |
| | | icon = this.env.forwardedrepliedicon; |
| | | else |
| | | icon = this.env.repliedicon; |
| | | } |
| | | else if (flags.forwarded && this.env.forwardedicon) |
| | | icon = this.env.forwardedicon; |
| | | else if(flags.unread && this.env.unreadicon) |
| | | icon = this.env.unreadicon; |
| | | |
| | | // add icon col |
| | | var col = document.createElement('td'); |
| | | col.className = 'icon'; |
| | | col.innerHTML = icon ? '<img src="'+icon+'" alt="" />' : ''; |
| | | row.appendChild(col); |
| | | |
| | | // build subject link |
| | | if (!bw.ie && cols.subject) { |
| | | var action = cols.mbox == this.env.drafts_mailbox ? 'compose' : 'show'; |
| | | var uid_param = cols.mbox == this.env.drafts_mailbox ? '_draft_uid' : '_uid'; |
| | | cols.subject = '<a href="./?_task=mail&_action='+action+'&_mbox='+urlencode(cols.mbox)+'&'+uid_param+'='+uid+'"'+ |
| | | ' onclick="return rcube_event.cancel(event)">'+cols.subject+'</a>'; |
| | | } |
| | | |
| | | // add each submitted col |
| | | for (var n = 0; n < this.coltypes.length; n++) { |
| | | var c = this.coltypes[n]; |
| | | col = document.createElement('td'); |
| | | col.className = String(c).toLowerCase(); |
| | | |
| | | if (c=='flag') { |
| | | if (flags.flagged && this.env.flaggedicon) |
| | | col.innerHTML = '<img src="'+this.env.flaggedicon+'" alt="" />'; |
| | | else if(!flags.flagged && this.env.unflaggedicon) |
| | | col.innerHTML = '<img src="'+this.env.unflaggedicon+'" alt="" />'; |
| | | } |
| | | else if (c=='attachment') |
| | | col.innerHTML = (attachment && this.env.attachmenticon ? '<img src="'+this.env.attachmenticon+'" alt="" />' : ' '); |
| | | else |
| | | col.innerHTML = cols[c]; |
| | | |
| | | row.appendChild(col); |
| | | } |
| | | |
| | | this.message_list.insert_row(row, attop); |
| | | // remove excessive columns |
| | | for (var i=n+1; thead && i<thead.rows[0].cells.length; i++) |
| | | thead.rows[0].removeChild(thead.rows[0].cells[i]); |
| | | |
| | | // remove 'old' row |
| | | if (attop && this.env.pagesize && this.message_list.rowcount > this.env.pagesize) { |
| | | var uid = this.message_list.get_last_row(); |
| | | this.message_list.remove_row(uid); |
| | | this.message_list.clear_selection(uid); |
| | | } |
| | | }; |
| | | this.env.subject_col = null; |
| | | this.env.flagged_col = null; |
| | | |
| | | // messages list handling in background (for performance) |
| | | this.offline_message_list = function(flag) |
| | | { |
| | | var found; |
| | | if((found = find_in_array('subject', this.env.coltypes)) >= 0) { |
| | | this.set_env('subject_col', found); |
| | | if (this.message_list) |
| | | this.message_list.set_background_mode(flag); |
| | | }; |
| | | this.message_list.subject_col = found+1; |
| | | } |
| | | if((found = find_in_array('flag', this.env.coltypes)) >= 0) |
| | | this.set_env('flagged_col', found); |
| | | }; |
| | | |
| | | // replace content of row count display |
| | | this.set_rowcount = function(text) |
| | |
| | | for (var i=0; i < response.callbacks.length; i++) |
| | | this.triggerEvent(response.callbacks[i][0], response.callbacks[i][1]); |
| | | } |
| | | |
| | | |
| | | // process the response data according to the sent action |
| | | switch (response.action) { |
| | | case 'delete': |
| | |
| | | // disable commands useless when mailbox is empty |
| | | this.enable_command('show', 'reply', 'reply-all', 'forward', 'moveto', 'delete', |
| | | 'mark', 'viewsource', 'open', 'edit', 'download', 'print', 'load-attachment', |
| | | 'purge', 'expunge', 'select-all', 'select-none', 'sort', false); |
| | | 'purge', 'expunge', 'select-all', 'select-none', 'sort', |
| | | 'expand-all', 'expand-unread', 'collapse-all', false); |
| | | } |
| | | break; |
| | | |
| | | case 'check-recent': |
| | | case 'getunread': |
| | | case 'search': |
| | | case 'list': |
| | | if (this.task == 'mail') { |
| | | if (this.message_list && response.action == 'list') |
| | | if (this.message_list && (response.action == 'list' || response.action == 'search')) { |
| | | this.msglist_select(this.message_list); |
| | | this.expand_threads(); |
| | | } |
| | | this.enable_command('show', 'expunge', 'select-all', 'select-none', 'sort', (this.env.messagecount > 0)); |
| | | this.enable_command('purge', this.purge_mailbox_test()); |
| | | |
| | | |
| | | this.enable_command('expand-all', 'expand-unread', 'collapse-all', this.env.threading && this.env.messagecount); |
| | | |
| | | if (response.action == 'list') |
| | | this.triggerEvent('listupdate', { folder:this.env.mailbox, rowcount:this.message_list.rowcount }); |
| | | } |
| | |
| | | var d = new Date(); |
| | | this.http_request('keep-alive', '_t='+d.getTime()); |
| | | }; |
| | | |
| | | // start interval for keep-alive/recent_check signal |
| | | this.start_keepalive = function() |
| | | { |
| | | if (this.env.keep_alive && !this.env.framed && this.task=='mail' && this.gui_objects.mailboxlist) |
| | | this._int = setInterval(function(){ ref.check_for_recent(false); }, this.env.keep_alive * 1000); |
| | | else if (this.env.keep_alive && !this.env.framed && this.task!='login') |
| | | this._int = setInterval(function(){ ref.send_keep_alive(); }, this.env.keep_alive * 1000); |
| | | } |
| | | |
| | | // send periodic request to check for recent messages |
| | | this.check_for_recent = function(refresh) |
| | |
| | | }; |
| | | |
| | | } // end object rcube_webmail |
| | | |
| | | |
| | | // copy event engine prototype |
| | | rcube_webmail.prototype.addEventListener = rcube_event_engine.prototype.addEventListener; |
| | |
| | | e = this; |
| | | else if (typeof e == 'object') |
| | | e.event = evt; |
| | | |
| | | |
| | | if (this._events && this._events[evt] && !this._event_exec) { |
| | | this._event_exec = true; |
| | | for (var i=0; i < this._events[evt].length; i++) { |
| | |
| | | this.subject_col = -1; |
| | | this.shiftkey = false; |
| | | this.multiselect = false; |
| | | this.multiexpand = false; |
| | | this.multi_selecting = false; |
| | | this.draggable = false; |
| | | this.keyboard = false; |
| | |
| | | for(var r=0; r<this.list.tBodies[0].childNodes.length; r++) |
| | | { |
| | | row = this.list.tBodies[0].childNodes[r]; |
| | | while (row && (row.nodeType != 1 || row.style.display == 'none')) |
| | | while (row && row.nodeType != 1) |
| | | { |
| | | row = row.nextSibling; |
| | | r++; |
| | |
| | | var p = this; |
| | | var uid = RegExp.$1; |
| | | row.uid = uid; |
| | | this.rows[uid] = {uid:uid, id:row.id, obj:row, classname:row.className}; |
| | | this.rows[uid] = {uid:uid, id:row.id, obj:row}; |
| | | |
| | | // set eventhandlers to table row |
| | | row.onmousedown = function(e){ return p.drag_row(e, this.uid); }; |
| | |
| | | }, |
| | | |
| | | |
| | | expand_row: function(e, id) |
| | | { |
| | | var row = this.rows[id]; |
| | | var evtarget = rcube_event.get_target(e); |
| | | var mod_key = rcube_event.get_modifier(e); |
| | | |
| | | // Don't select this message |
| | | this.dont_select = true; |
| | | // Don't treat double click on the expando as double click on the message. |
| | | row.clicked = 0; |
| | | |
| | | if (row.expanded) { |
| | | evtarget.className = "collapsed"; |
| | | if (mod_key == CONTROL_KEY || this.multiexpand) |
| | | this.collapse_all(row); |
| | | else |
| | | this.collapse(row); |
| | | } |
| | | else { |
| | | evtarget.className = "expanded"; |
| | | if (mod_key == CONTROL_KEY || this.multiexpand) |
| | | this.expand_all(row); |
| | | else |
| | | this.expand(row); |
| | | } |
| | | }, |
| | | |
| | | collapse: function(row) |
| | | { |
| | | row.expanded = false; |
| | | this.triggerEvent('expandcollapse', { uid:row.uid, expanded:row.expanded }); |
| | | var depth = row.depth; |
| | | var new_row = row ? row.obj.nextSibling : null; |
| | | var r; |
| | | |
| | | while (new_row) { |
| | | if (new_row.nodeType == 1) { |
| | | var r = this.rows[new_row.uid]; |
| | | if (r && r.depth <= depth) |
| | | break; |
| | | $(new_row).hide(); |
| | | r.expanded = false; |
| | | this.triggerEvent('expandcollapse', { uid:r.uid, expanded:r.expanded }); |
| | | } |
| | | new_row = new_row.nextSibling; |
| | | } |
| | | |
| | | return false; |
| | | }, |
| | | |
| | | expand: function(row) |
| | | { |
| | | var depth, new_row; |
| | | var last_expanded_parent_depth; |
| | | |
| | | if (row) { |
| | | row.expanded = true; |
| | | depth = row.depth; |
| | | new_row = row.obj.nextSibling; |
| | | this.triggerEvent('expandcollapse', { uid:row.uid, expanded:row.expanded }); |
| | | } |
| | | else { |
| | | var tbody = this.list.tBodies[0]; |
| | | new_row = tbody.firstChild; |
| | | depth = 0; |
| | | last_expanded_parent_depth = 0; |
| | | } |
| | | |
| | | while (new_row) { |
| | | if (new_row.nodeType == 1) { |
| | | var r = this.rows[new_row.uid]; |
| | | if (r) { |
| | | if (row && (!r.depth || r.depth <= depth)) |
| | | break; |
| | | |
| | | if (r.parent_uid) { |
| | | var p = this.rows[r.parent_uid]; |
| | | if (p && p.expanded) { |
| | | if ((row && p == row) || last_expanded_parent_depth >= p.depth - 1) { |
| | | last_expanded_parent_depth = p.depth; |
| | | $(new_row).show(); |
| | | r.expanded = true; |
| | | this.triggerEvent('expandcollapse', { uid:r.uid, expanded:r.expanded }); |
| | | } |
| | | } |
| | | else |
| | | if (row && (! p || p.depth <= depth)) |
| | | break; |
| | | } |
| | | } |
| | | } |
| | | new_row = new_row.nextSibling; |
| | | } |
| | | |
| | | return false; |
| | | }, |
| | | |
| | | |
| | | collapse_all: function(row) |
| | | { |
| | | var depth, new_row; |
| | | var r; |
| | | |
| | | if (row) { |
| | | row.expanded = false; |
| | | depth = row.depth; |
| | | new_row = row.obj.nextSibling; |
| | | this.triggerEvent('expandcollapse', { uid:row.uid, expanded:row.expanded }); |
| | | |
| | | // don't collapse sub-root tree in multiexpand mode |
| | | if (depth && this.multiexpand) |
| | | return false; |
| | | } |
| | | else { |
| | | var tbody = this.list.tBodies[0]; |
| | | new_row = tbody.firstChild; |
| | | depth = 0; |
| | | } |
| | | |
| | | while (new_row) { |
| | | if (new_row.nodeType == 1) { |
| | | var r = this.rows[new_row.uid]; |
| | | if (r) { |
| | | if (row && (!r.depth || r.depth <= depth)) |
| | | break; |
| | | |
| | | if (row || r.depth) |
| | | $(new_row).hide(); |
| | | if (r.has_children) { |
| | | r.expanded = false; |
| | | var expando = document.getElementById('rcmexpando' + r.uid); |
| | | if (expando) |
| | | expando.className = 'collapsed'; |
| | | this.triggerEvent('expandcollapse', { uid:r.uid, expanded:r.expanded }); |
| | | } |
| | | } |
| | | } |
| | | new_row = new_row.nextSibling; |
| | | } |
| | | |
| | | return false; |
| | | }, |
| | | |
| | | expand_all: function(row) |
| | | { |
| | | var depth, new_row; |
| | | var r; |
| | | |
| | | if (row) { |
| | | row.expanded = true; |
| | | depth = row.depth; |
| | | new_row = row.obj.nextSibling; |
| | | this.triggerEvent('expandcollapse', { uid:row.uid, expanded:row.expanded }); |
| | | } |
| | | else { |
| | | var tbody = this.list.tBodies[0]; |
| | | new_row = tbody.firstChild; |
| | | depth = 0; |
| | | } |
| | | |
| | | while (new_row) { |
| | | if (new_row.nodeType == 1) { |
| | | var r = this.rows[new_row.uid]; |
| | | if (r) { |
| | | if (row && r.depth <= depth) |
| | | break; |
| | | |
| | | $(new_row).show(); |
| | | if (r.has_children) { |
| | | r.expanded = true; |
| | | var expando = document.getElementById('rcmexpando' + r.uid); |
| | | if (expando) |
| | | expando.className = 'expanded'; |
| | | this.triggerEvent('expandcollapse', { uid:r.uid, expanded:r.expanded }); |
| | | } |
| | | } |
| | | } |
| | | new_row = new_row.nextSibling; |
| | | } |
| | | return false; |
| | | }, |
| | | |
| | | /** |
| | | * get first/next/previous/last rows that are not hidden |
| | | */ |
| | |
| | | { |
| | | if ((this.rows[n].obj.rowIndex >= i) && (this.rows[n].obj.rowIndex <= j)) |
| | | { |
| | | if (!this.in_selection(n)) |
| | | if (!this.in_selection(n)) { |
| | | this.highlight_row(n, true); |
| | | } |
| | | } |
| | | else |
| | | { |
| | | if (this.in_selection(n) && !control) |
| | | if (this.in_selection(n) && !control) { |
| | | this.highlight_row(n, true); |
| | | } |
| | | } |
| | | } |
| | | }, |
| | |
| | | if (this.selection[n]==id) |
| | | return true; |
| | | |
| | | return false; |
| | | return false; |
| | | }, |
| | | |
| | | |
| | |
| | | var select_before = this.selection.join(','); |
| | | |
| | | for (var n in this.rows) |
| | | this.highlight_row(n, true); |
| | | this.highlight_row(n, true); |
| | | |
| | | // trigger event if selection changed |
| | | if (this.selection.join(',') != select_before) |
| | |
| | | // Stop propagation so that the browser doesn't scroll |
| | | rcube_event.cancel(e); |
| | | return this.use_arrow_key(keyCode, mod_key); |
| | | case 61: |
| | | case 107: // Plus sign on a numeric keypad (fc11 + firefox 3.5.2) |
| | | case 109: |
| | | case 32: |
| | | // Stop propagation |
| | | rcube_event.cancel(e); |
| | | var ret = this.use_plusminus_key(keyCode, mod_key); |
| | | this.key_pressed = keyCode; |
| | | this.triggerEvent('keypress'); |
| | | return ret; |
| | | default: |
| | | this.shiftkey = e.shiftKey; |
| | | this.key_pressed = keyCode; |
| | |
| | | case 38: |
| | | case 63233: |
| | | case 63232: |
| | | case 61: |
| | | case 107: |
| | | case 109: |
| | | case 32: |
| | | if (!rcube_event.get_modifier(e) && this.focused) |
| | | return rcube_event.cancel(e); |
| | | |
| | |
| | | this.select_row(new_row.uid, mod_key, true); |
| | | this.scrollto(new_row.uid); |
| | | } |
| | | |
| | | return false; |
| | | }, |
| | | |
| | | |
| | | /** |
| | | * Special handling method for +/- keys |
| | | */ |
| | | use_plusminus_key: function(keyCode, mod_key) |
| | | { |
| | | var selected_row = this.rows[this.last_selected]; |
| | | if (!selected_row) |
| | | return; |
| | | |
| | | if (keyCode == 32) |
| | | keyCode = selected_row.expanded ? 109 : 61; |
| | | if (keyCode == 61 || keyCode == 107) |
| | | if (mod_key == CONTROL_KEY || this.multiexpand) |
| | | this.expand_all(selected_row); |
| | | else |
| | | this.expand(selected_row); |
| | | else |
| | | if (mod_key == CONTROL_KEY || this.multiexpand) |
| | | this.collapse_all(selected_row); |
| | | else |
| | | this.collapse(selected_row); |
| | | |
| | | var expando = document.getElementById('rcmexpando' + selected_row.uid); |
| | | if (expando) |
| | | expando.className = selected_row.expanded?'expanded':'collapsed'; |
| | | |
| | | return false; |
| | | }, |
| | |
| | | if (!this.draglayer) |
| | | this.draglayer = $('<div>').attr('id', 'rcmdraglayer').css({ position:'absolute', display:'none', 'z-index':2000 }).appendTo(document.body); |
| | | |
| | | // get subjects of selectedd messages |
| | | // get subjects of selected messages |
| | | var names = ''; |
| | | var c, i, node, subject, obj; |
| | | var c, i, subject, obj; |
| | | for(var n=0; n<this.selection.length; n++) |
| | | { |
| | | if (n>12) // only show 12 lines |
| | |
| | | break; |
| | | } |
| | | |
| | | if (this.rows[this.selection[n]].obj) |
| | | if (obj = this.rows[this.selection[n]].obj) |
| | | { |
| | | obj = this.rows[this.selection[n]].obj; |
| | | subject = ''; |
| | | |
| | | for(c=0, i=0; i<obj.childNodes.length; i++) |
| | | for (c=0, i=0; i<obj.childNodes.length; i++) |
| | | { |
| | | if (obj.childNodes[i].nodeName == 'TD') |
| | | if (obj.childNodes[i].nodeName == 'TD') |
| | | { |
| | | if (((node = obj.childNodes[i].firstChild) && (node.nodeType==3 || node.nodeName=='A')) && |
| | | (this.subject_col < 0 || (this.subject_col >= 0 && this.subject_col == c))) |
| | | { |
| | | if (n == 0) { |
| | | if (node.nodeType == 3) |
| | | this.drag_start_pos = $(obj.childNodes[i]).offset(); |
| | | else |
| | | this.drag_start_pos = $(node).offset(); |
| | | if (n == 0) |
| | | this.drag_start_pos = $(obj.childNodes[i]).offset(); |
| | | |
| | | if (this.subject_col < 0 || (this.subject_col >= 0 && this.subject_col == c)) |
| | | { |
| | | var node, tmp_node, nodes = obj.childNodes[i].childNodes; |
| | | // find text node |
| | | for (m=0; m<nodes.length; m++) { |
| | | if ((tmp_node = obj.childNodes[i].childNodes[m]) && (tmp_node.nodeType==3 || tmp_node.nodeName=='A')) |
| | | node = tmp_node; |
| | | } |
| | | |
| | | if (!node) |
| | | break; |
| | | |
| | | subject = node.nodeType==3 ? node.data : node.innerHTML; |
| | | // remove leading spaces |
| | | subject = subject.replace(/^\s+/i, ''); |
| | |
| | | var $forwarded = false; |
| | | var $junk = false; |
| | | var $flagged = false; |
| | | var $has_children = false; |
| | | var $depth = 0; |
| | | var $unread_children = 0; |
| | | var $others = array(); |
| | | } |
| | | |
| | | /** |
| | | * @todo Change class vars to public/private |
| | | */ |
| | | class iilThreadHeader |
| | | { |
| | | var $id; |
| | | var $sbj; |
| | | var $irt; |
| | | var $mid; |
| | | } |
| | | |
| | | function iil_xor($string, $string2) { |
| | |
| | | |
| | | $fields = array('ARRIVAL' => 1,'CC' => 1,'DATE' => 1, |
| | | 'FROM' => 1, 'SIZE' => 1, 'SUBJECT' => 1, 'TO' => 1); |
| | | |
| | | |
| | | if (!$fields[$field]) { |
| | | return false; |
| | | } |
| | |
| | | |
| | | $is_uid = $is_uid ? 'UID ' : ''; |
| | | |
| | | if (!empty($add)) { |
| | | // message IDs |
| | | if (is_array($add)) |
| | | $add = iil_CompressMessageSet(join(',', $add)); |
| | | |
| | | if (!empty($add)) |
| | | $add = " $add"; |
| | | } |
| | | |
| | | $command = 's ' . $is_uid . 'SORT (' . $field . ') '; |
| | | $command .= $encoding . ' ALL' . $add; |
| | |
| | | |
| | | function iil_C_FetchHeaderIndex(&$conn, $mailbox, $message_set, $index_field='', $skip_deleted=true, $uidfetch=false) { |
| | | |
| | | list($from_idx, $to_idx) = explode(':', $message_set); |
| | | if (empty($message_set) || |
| | | (isset($to_idx) && $to_idx != '*' && (int)$from_idx > (int)$to_idx)) { |
| | | return false; |
| | | if (is_array($message_set)) { |
| | | if (!($message_set = iil_CompressMessageSet(join(',', $message_set)))) |
| | | return false; |
| | | } else { |
| | | list($from_idx, $to_idx) = explode(':', $message_set); |
| | | if (empty($message_set) || |
| | | (isset($to_idx) && $to_idx != '*' && (int)$from_idx > (int)$to_idx)) { |
| | | return false; |
| | | } |
| | | } |
| | | |
| | | |
| | | $index_field = empty($index_field) ? 'DATE' : strtoupper($index_field); |
| | | |
| | | $fields_a['DATE'] = 1; |
| | | $fields_a['INTERNALDATE'] = 4; |
| | | $fields_a['ARRIVAL'] = 4; |
| | | $fields_a['FROM'] = 1; |
| | | $fields_a['REPLY-TO'] = 1; |
| | | $fields_a['SENDER'] = 1; |
| | | $fields_a['TO'] = 1; |
| | | $fields_a['CC'] = 1; |
| | | $fields_a['SUBJECT'] = 1; |
| | | $fields_a['UID'] = 2; |
| | | $fields_a['SIZE'] = 2; |
| | |
| | | } |
| | | } while (!iil_StartsWith($line, $key, true)); |
| | | |
| | | /* |
| | | //check number of elements... |
| | | if (is_numeric($from_idx) && is_numeric($to_idx)) { |
| | | //count how many we should have |
| | | $should_have = $to_idx - $from_idx + 1; |
| | | |
| | | //if we have less, try and fill in the "gaps" |
| | | if (count($result) < $should_have) { |
| | | for ($i=$from_idx; $i<=$to_idx; $i++) { |
| | | if (!isset($result[$i])) { |
| | | $result[$i] = ''; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | */ |
| | | return $result; |
| | | } |
| | | |
| | |
| | | return iil_C_FetchHeaderIndex($conn, $mailbox, $message_set, 'UID'); |
| | | } |
| | | |
| | | function iil_SortThreadHeaders($headers, $index_a, $uids) { |
| | | asort($index_a); |
| | | $result = array(); |
| | | foreach ($index_a as $mid=>$foobar) { |
| | | $uid = $uids[$mid]; |
| | | $result[$uid] = $headers[$uid]; |
| | | } |
| | | return $result; |
| | | } |
| | | |
| | | function iil_C_FetchThreadHeaders(&$conn, $mailbox, $message_set) { |
| | | global $clock; |
| | | global $index_a; |
| | | |
| | | list($from_idx, $to_idx) = explode(':', $message_set); |
| | | if (empty($message_set) || (isset($to_idx) |
| | | && (int)$from_idx > (int)$to_idx)) { |
| | | return false; |
| | | } |
| | | |
| | | $result = array(); |
| | | $uids = iil_C_FetchUIDs($conn, $mailbox); |
| | | $debug = false; |
| | | |
| | | $message_set = iil_CompressMessageSet($message_set); |
| | | |
| | | /* if we're missing any, get them */ |
| | | if ($message_set) { |
| | | /* FETCH date,from,subject headers */ |
| | | $key = 'fh'; |
| | | $fp = $conn->fp; |
| | | $request = $key . " FETCH $message_set "; |
| | | $request .= "(BODY.PEEK[HEADER.FIELDS (SUBJECT MESSAGE-ID IN-REPLY-TO)])"; |
| | | $mid_to_id = array(); |
| | | if (!iil_PutLine($fp, $request)) { |
| | | return false; |
| | | } |
| | | do { |
| | | $line = chop(iil_ReadLine($fp, 1024)); |
| | | if ($debug) { |
| | | echo $line . "\n"; |
| | | } |
| | | if (preg_match('/\{[0-9]+\}$/', $line)) { |
| | | $a = explode(' ', $line); |
| | | $new = array(); |
| | | |
| | | $new_thhd = new iilThreadHeader; |
| | | $new_thhd->id = $a[1]; |
| | | do { |
| | | $line = chop(iil_ReadLine($fp, 1024), "\r\n"); |
| | | if (iil_StartsWithI($line, 'Message-ID:') |
| | | || (iil_StartsWithI($line,'In-Reply-To:')) |
| | | || (iil_StartsWithI($line,'SUBJECT:'))) { |
| | | |
| | | $pos = strpos($line, ':'); |
| | | $field_name = substr($line, 0, $pos); |
| | | $field_val = substr($line, $pos+1); |
| | | |
| | | $new[strtoupper($field_name)] = trim($field_val); |
| | | |
| | | } else if (preg_match('/^\s+/', $line)) { |
| | | $new[strtoupper($field_name)] .= trim($line); |
| | | } |
| | | } while ($line[0] != ')'); |
| | | |
| | | $new_thhd->sbj = $new['SUBJECT']; |
| | | $new_thhd->mid = substr($new['MESSAGE-ID'], 1, -1); |
| | | $new_thhd->irt = substr($new['IN-REPLY-TO'], 1, -1); |
| | | |
| | | $result[$uids[$new_thhd->id]] = $new_thhd; |
| | | } |
| | | } while (!iil_StartsWith($line, 'fh')); |
| | | } |
| | | |
| | | /* sort headers */ |
| | | if (is_array($index_a)) { |
| | | $result = iil_SortThreadHeaders($result, $index_a, $uids); |
| | | } |
| | | |
| | | //echo 'iil_FetchThreadHeaders:'."\n"; |
| | | //print_r($result); |
| | | |
| | | return $result; |
| | | } |
| | | |
| | | function iil_C_BuildThreads2(&$conn, $mailbox, $message_set, &$clock) { |
| | | global $index_a; |
| | | |
| | | list($from_idx, $to_idx) = explode(':', $message_set); |
| | | if (empty($message_set) || (isset($to_idx) |
| | | && (int)$from_idx > (int)$to_idx)) { |
| | | return false; |
| | | } |
| | | |
| | | $result = array(); |
| | | $roots = array(); |
| | | $root_mids = array(); |
| | | $sub_mids = array(); |
| | | $strays = array(); |
| | | $messages = array(); |
| | | $fp = $conn->fp; |
| | | $debug = false; |
| | | |
| | | $sbj_filter_pat = '/[a-z]{2,3}(\[[0-9]*\])?:(\s*)/i'; |
| | | |
| | | /* Do "SELECT" command */ |
| | | if (!iil_C_Select($conn, $mailbox)) { |
| | | return false; |
| | | } |
| | | |
| | | /* FETCH date,from,subject headers */ |
| | | $mid_to_id = array(); |
| | | $messages = array(); |
| | | $headers = iil_C_FetchThreadHeaders($conn, $mailbox, $message_set); |
| | | if ($clock) { |
| | | $clock->register('fetched headers'); |
| | | } |
| | | |
| | | if ($debug) { |
| | | print_r($headers); |
| | | } |
| | | |
| | | /* go through header records */ |
| | | foreach ($headers as $header) { |
| | | //$id = $header['i']; |
| | | //$new = array('id'=>$id, 'MESSAGE-ID'=>$header['m'], |
| | | // 'IN-REPLY-TO'=>$header['r'], 'SUBJECT'=>$header['s']); |
| | | $id = $header->id; |
| | | $new = array('id' => $id, 'MESSAGE-ID' => $header->mid, |
| | | 'IN-REPLY-TO' => $header->irt, 'SUBJECT' => $header->sbj); |
| | | |
| | | /* add to message-id -> mid lookup table */ |
| | | $mid_to_id[$new['MESSAGE-ID']] = $id; |
| | | |
| | | /* if no subject, use message-id */ |
| | | if (empty($new['SUBJECT'])) { |
| | | $new['SUBJECT'] = $new['MESSAGE-ID']; |
| | | } |
| | | |
| | | /* if subject contains 'RE:' or has in-reply-to header, it's a reply */ |
| | | $sbj_pre = ''; |
| | | $has_re = false; |
| | | if (preg_match($sbj_filter_pat, $new['SUBJECT'])) { |
| | | $has_re = true; |
| | | } |
| | | if ($has_re || $new['IN-REPLY-TO']) { |
| | | $sbj_pre = 'RE:'; |
| | | } |
| | | |
| | | /* strip out 're:', 'fw:' etc */ |
| | | if ($has_re) { |
| | | $sbj = preg_replace($sbj_filter_pat, '', $new['SUBJECT']); |
| | | } else { |
| | | $sbj = $new['SUBJECT']; |
| | | } |
| | | $new['SUBJECT'] = $sbj_pre.$sbj; |
| | | |
| | | |
| | | /* if subject not a known thread-root, add to list */ |
| | | if ($debug) { |
| | | echo $id . ' ' . $new['SUBJECT'] . "\t" . $new['MESSAGE-ID'] . "\n"; |
| | | } |
| | | $root_id = $roots[$sbj]; |
| | | |
| | | if ($root_id && ($has_re || !$root_in_root[$root_id])) { |
| | | if ($debug) { |
| | | echo "\tfound root: $root_id\n"; |
| | | } |
| | | $sub_mids[$new['MESSAGE-ID']] = $root_id; |
| | | $result[$root_id][] = $id; |
| | | } else if (!isset($roots[$sbj]) || (!$has_re && $root_in_root[$root_id])) { |
| | | /* try to use In-Reply-To header to find root |
| | | unless subject contains 'Re:' */ |
| | | if ($has_re&&$new['IN-REPLY-TO']) { |
| | | if ($debug) { |
| | | echo "\tlooking: ".$new['IN-REPLY-TO']."\n"; |
| | | } |
| | | //reply to known message? |
| | | $temp = $sub_mids[$new['IN-REPLY-TO']]; |
| | | |
| | | if ($temp) { |
| | | //found it, root:=parent's root |
| | | if ($debug) { |
| | | echo "\tfound parent: ".$new['SUBJECT']."\n"; |
| | | } |
| | | $result[$temp][] = $id; |
| | | $sub_mids[$new['MESSAGE-ID']] = $temp; |
| | | $sbj = ''; |
| | | } else { |
| | | //if we can't find referenced parent, it's a "stray" |
| | | $strays[$id] = $new['IN-REPLY-TO']; |
| | | } |
| | | } |
| | | |
| | | //add subject as root |
| | | if ($sbj) { |
| | | if ($debug) { |
| | | echo "\t added to root\n"; |
| | | } |
| | | $roots[$sbj] = $id; |
| | | $root_in_root[$id] = !$has_re; |
| | | $sub_mids[$new['MESSAGE-ID']] = $id; |
| | | $result[$id] = array($id); |
| | | } |
| | | if ($debug) { |
| | | echo $new['MESSAGE-ID'] . "\t" . $sbj . "\n"; |
| | | } |
| | | } |
| | | } |
| | | |
| | | //now that we've gone through all the messages, |
| | | //go back and try and link up the stray threads |
| | | if (count($strays) > 0) { |
| | | foreach ($strays as $id=>$irt) { |
| | | $root_id = $sub_mids[$irt]; |
| | | if (!$root_id || $root_id==$id) { |
| | | continue; |
| | | } |
| | | $result[$root_id] = array_merge($result[$root_id],$result[$id]); |
| | | unset($result[$id]); |
| | | } |
| | | } |
| | | |
| | | if ($clock) { |
| | | $clock->register('data prepped'); |
| | | } |
| | | |
| | | if ($debug) { |
| | | print_r($roots); |
| | | } |
| | | |
| | | return $result; |
| | | } |
| | | |
| | | function iil_SortThreads(&$tree, $index, $sort_order = 'ASC') { |
| | | if (!is_array($tree) || !is_array($index)) { |
| | | return false; |
| | | } |
| | | |
| | | //create an id to position lookup table |
| | | $i = 0; |
| | | foreach ($index as $id=>$val) { |
| | | $i++; |
| | | $index[$id] = $i; |
| | | } |
| | | $max = $i+1; |
| | | |
| | | //for each tree, set array key to position |
| | | $itree = array(); |
| | | foreach ($tree as $id=>$node) { |
| | | if (count($tree[$id])<=1) { |
| | | //for "threads" with only one message, key is position of that message |
| | | $n = $index[$id]; |
| | | $itree[$n] = array($n=>$id); |
| | | } else { |
| | | //for "threads" with multiple messages, |
| | | $min = $max; |
| | | $new_a = array(); |
| | | foreach ($tree[$id] as $mid) { |
| | | $new_a[$index[$mid]] = $mid; //create new sub-array mapping position to id |
| | | $pos = $index[$mid]; |
| | | if ($pos&&$pos<$min) { |
| | | $min = $index[$mid]; //find smallest position |
| | | } |
| | | } |
| | | $n = $min; //smallest position of child is thread position |
| | | |
| | | //assign smallest position to root level key |
| | | //set children array to one created above |
| | | ksort($new_a); |
| | | $itree[$n] = $new_a; |
| | | } |
| | | } |
| | | |
| | | //sort by key, this basically sorts all threads |
| | | ksort($itree); |
| | | $i = 0; |
| | | $out = array(); |
| | | foreach ($itree as $k=>$node) { |
| | | $out[$i] = $itree[$k]; |
| | | $i++; |
| | | } |
| | | |
| | | return $out; |
| | | } |
| | | |
| | | function iil_IndexThreads(&$tree) { |
| | | /* creates array mapping mid to thread id */ |
| | | |
| | | if (!is_array($tree)) { |
| | | return false; |
| | | } |
| | | |
| | | $t_index = array(); |
| | | foreach ($tree as $pos=>$kids) { |
| | | foreach ($kids as $kid) $t_index[$kid] = $pos; |
| | | } |
| | | |
| | | return $t_index; |
| | | } |
| | | |
| | | function iil_C_FetchHeaders(&$conn, $mailbox, $message_set, $uidfetch=false, $bodystr=false, $add='') |
| | | { |
| | | global $IMAP_USE_INTERNAL_DATE; |
| | |
| | | $conn->error = "Couldn't select $mailbox"; |
| | | return false; |
| | | } |
| | | |
| | | if (is_array($message_set)) |
| | | $message_set = join(',', $message_set); |
| | | |
| | | $message_set = iil_CompressMessageSet($message_set); |
| | | |
| | |
| | | return $result; |
| | | } |
| | | |
| | | // Don't be tempted to change $str to pass by reference to speed this up - it will slow it down by about |
| | | // 7 times instead :-) See comments on http://uk2.php.net/references and this article: |
| | | // http://derickrethans.nl/files/phparch-php-variables-article.pdf |
| | | function iil_ParseThread($str, $begin, $end, $root, $parent, $depth, &$depthmap, &$haschildren) { |
| | | $node = array(); |
| | | if ($str[$begin] != '(') { |
| | | $stop = $begin + strspn($str, "1234567890", $begin, $end - $begin); |
| | | $msg = substr($str, $begin, $stop - $begin); |
| | | if ($msg == 0) |
| | | return $node; |
| | | if (is_null($root)) |
| | | $root = $msg; |
| | | $depthmap[$msg] = $depth; |
| | | $haschildren[$msg] = false; |
| | | if (!is_null($parent)) |
| | | $haschildren[$parent] = true; |
| | | if ($stop + 1 < $end) |
| | | $node[$msg] = iil_ParseThread($str, $stop + 1, $end, $root, $msg, $depth + 1, $depthmap, $haschildren); |
| | | else |
| | | $node[$msg] = array(); |
| | | } else { |
| | | $off = $begin; |
| | | while ($off < $end) { |
| | | $start = $off; |
| | | $off++; |
| | | $n = 1; |
| | | while ($n > 0) { |
| | | $p = strpos($str, ')', $off); |
| | | if ($p === false) { |
| | | error_log('Mismatched brackets parsing IMAP THREAD response:'); |
| | | error_log(substr($str, ($begin < 10) ? 0 : ($begin - 10), $end - $begin + 20)); |
| | | error_log(str_repeat(' ', $off - (($begin < 10) ? 0 : ($begin - 10)))); |
| | | return $node; |
| | | } |
| | | $p1 = strpos($str, '(', $off); |
| | | if ($p1 !== false && $p1 < $p) { |
| | | $off = $p1 + 1; |
| | | $n++; |
| | | } else { |
| | | $off = $p + 1; |
| | | $n--; |
| | | } |
| | | } |
| | | $node += iil_ParseThread($str, $start + 1, $off - 1, $root, $parent, $depth, $depthmap, $haschildren); |
| | | } |
| | | } |
| | | |
| | | return $node; |
| | | } |
| | | |
| | | function iil_C_Thread(&$conn, $folder, $algorithm='REFERENCES', $criteria='', |
| | | $encoding='US-ASCII') { |
| | | |
| | | if (iil_C_Select($conn, $folder)) { |
| | | |
| | | $encoding = $encoding ? trim($encoding) : 'US-ASCII'; |
| | | $algorithm = $algorithm ? trim($algorithm) : 'REFERENCES'; |
| | | $criteria = $criteria ? 'ALL '.trim($criteria) : 'ALL'; |
| | | |
| | | iil_PutLineC($conn->fp, "thrd1 THREAD $algorithm $encoding $criteria"); |
| | | do { |
| | | $line = trim(iil_ReadLine($conn->fp, 10000)); |
| | | if (preg_match('/^\* THREAD/', $line)) { |
| | | $str = trim(substr($line, 8)); |
| | | $depthmap = array(); |
| | | $haschildren = array(); |
| | | $tree = iil_ParseThread($str, 0, strlen($str), null, null, 0, $depthmap, $haschildren); |
| | | } |
| | | } while (!iil_StartsWith($line, 'thrd1', true)); |
| | | |
| | | $result_code = iil_ParseResult($line); |
| | | if ($result_code == 0) { |
| | | return array($tree, $depthmap, $haschildren); |
| | | } |
| | | $conn->error = 'iil_C_Thread: ' . $line . "\n"; |
| | | return false; |
| | | } |
| | | $conn->error = "iil_C_Thread: Couldn't select \"$folder\"\n"; |
| | | return false; |
| | | } |
| | | |
| | | function iil_C_Search(&$conn, $folder, $criteria) { |
| | | |
| | | if (iil_C_Select($conn, $folder)) { |
| | |
| | | $labels['deleted'] = 'Gelöschte'; |
| | | $labels['invert'] = 'Umkehren'; |
| | | $labels['filter'] = 'Filter'; |
| | | |
| | | $labels['list'] = 'Liste'; |
| | | $labels['threads'] = 'Konversationen'; |
| | | $labels['expand-all'] = 'All aufklappen'; |
| | | $labels['expand-unread'] = 'Ungelesene aufklappen'; |
| | | $labels['collapse-all'] = 'Alle zuklappen'; |
| | | $labels['threaded'] = 'Gruppiert'; |
| | | |
| | | $labels['autoexpand_threads'] = 'Konversationen aufklappen'; |
| | | $labels['do_expand'] = 'alle'; |
| | | $labels['expand_only_unread'] = 'nur ungelesene'; |
| | | $labels['fromto'] = 'Sender/Empfänger'; |
| | | $labels['flag'] = 'Markierung'; |
| | | $labels['attachment'] = 'Anhang'; |
| | | $labels['nonesort'] = 'Keine'; |
| | | $labels['sentdate'] = 'Sendedatum'; |
| | | $labels['arrival'] = 'Empfangsdatum'; |
| | | $labels['asc'] = 'aufsteigend'; |
| | | $labels['desc'] = 'absteigend'; |
| | | $labels['listcolumns'] = 'Spalten'; |
| | | $labels['listsorting'] = 'Sortierung'; |
| | | $labels['listorder'] = 'Ordnung'; |
| | | $labels['listmode'] = 'Anzeigemodus'; |
| | | |
| | | $labels['compact'] = 'Packen'; |
| | | $labels['empty'] = 'Leeren'; |
| | | $labels['purge'] = 'Aufräumen'; |
| | |
| | | |
| | | $labels['mailboxlist'] = 'Folders'; |
| | | $labels['messagesfromto'] = 'Messages $from to $to of $count'; |
| | | $labels['threadsfromto'] = 'Threads $from to $to of $count'; |
| | | $labels['messagenrof'] = 'Message $nr of $count'; |
| | | |
| | | $labels['moveto'] = 'Move to...'; |
| | |
| | | $labels['deleted'] = 'Deleted'; |
| | | $labels['invert'] = 'Invert'; |
| | | $labels['filter'] = 'Filter'; |
| | | |
| | | $labels['list'] = 'List'; |
| | | $labels['threads'] = 'Threads'; |
| | | $labels['expand-all'] = 'Expand All'; |
| | | $labels['expand-unread'] = 'Expand Unread'; |
| | | $labels['collapse-all'] = 'Collapse All'; |
| | | $labels['threaded'] = 'Threaded'; |
| | | |
| | | $labels['autoexpand_threads'] = 'Expand message threads'; |
| | | $labels['do_expand'] = 'all threads'; |
| | | $labels['expand_only_unread'] = 'only with unread messages'; |
| | | $labels['fromto'] = 'Sender/Recipient'; |
| | | $labels['flag'] = 'Flag'; |
| | | $labels['attachment'] = 'Attachment'; |
| | | $labels['nonesort'] = 'None'; |
| | | $labels['sentdate'] = 'Sent date'; |
| | | $labels['arrival'] = 'Arrival date'; |
| | | $labels['asc'] = 'ascending'; |
| | | $labels['desc'] = 'descending'; |
| | | $labels['listcolumns'] = 'List columns'; |
| | | $labels['listsorting'] = 'Sorting column'; |
| | | $labels['listorder'] = 'Sorting order'; |
| | | $labels['listmode'] = 'List view mode'; |
| | | |
| | | $labels['compact'] = 'Compact'; |
| | | $labels['empty'] = 'Empty'; |
| | |
| | | $labels['focusonnewmessage'] = 'Focus browser window on new message'; |
| | | $labels['checkallfolders'] = 'Check all folders for new messages'; |
| | | $labels['displaynext'] = 'After message delete/move display the next message'; |
| | | $labels['indexsort'] = 'Use message index for sorting by date'; |
| | | $labels['mainoptions'] = 'Main Options'; |
| | | $labels['section'] = 'Section'; |
| | | $labels['maintenance'] = 'Maintenance'; |
| | |
| | | $labels['messageactions'] = 'Więcej akcji...'; |
| | | $labels['select'] = 'Zaznacz'; |
| | | $labels['all'] = 'Wszystkie'; |
| | | $labels['none'] = 'Anuluj'; |
| | | $labels['none'] = 'Brak'; |
| | | $labels['unread'] = 'Nieprzeczytane'; |
| | | $labels['flagged'] = 'Oznaczone'; |
| | | $labels['unanswered'] = 'Bez odpowiedzi'; |
| | |
| | | } |
| | | |
| | | // get overall message count; allow caching because rcube_imap::recent_uids() did a refresh |
| | | $all_count = $IMAP->messagecount(); |
| | | $all_count = $IMAP->messagecount(NULL, $IMAP->threading ? 'THREADS' : 'ALL'); |
| | | |
| | | $unread_count = $IMAP->messagecount(NULL, 'UNSEEN', TRUE); |
| | | $_SESSION['unseen_count'][$mbox_name] = $unread_count; |
| | | |
| | | $OUTPUT->set_env('messagecount', $all_count); |
| | | $OUTPUT->set_env('pagesize', $IMAP->page_size); |
| | | $OUTPUT->set_env('pagecount', ceil($all_count/$IMAP->page_size)); |
| | | $OUTPUT->command('set_unread_count', $mbox_name, $unread_count, ($mbox_name == 'INBOX')); |
| | | $OUTPUT->command('set_rowcount', rcmail_get_messagecount_text($all_count)); |
| | |
| | | if (empty($_GET['_list'])) |
| | | continue; |
| | | |
| | | // use SEARCH/SORT to find recent messages |
| | | $search_str = 'UID '.min($recents).':'.max($recents); |
| | | if ($search_request) |
| | | $search_str .= ' '.$IMAP->search_string; |
| | | if ($IMAP->threading) { |
| | | $OUTPUT->command('message_list.clear'); |
| | | $sort_col = isset($_SESSION['sort_col']) ? $_SESSION['sort_col'] : $CONFIG['message_sort_col']; |
| | | $sort_order = isset($_SESSION['sort_order']) ? $_SESSION['sort_order'] : $CONFIG['message_sort_order']; |
| | | $result_h = $IMAP->list_headers($mbox_name, NULL, $sort_col, $sort_order); |
| | | // add to the list |
| | | rcmail_js_message_list($result_h); |
| | | } |
| | | else { |
| | | // use SEARCH/SORT to find recent messages |
| | | $search_str = 'UID '.min($recents).':'.max($recents); |
| | | if ($search_request) |
| | | $search_str .= ' '.$IMAP->search_string; |
| | | |
| | | if ($IMAP->search($mbox_name, $search_str, NULL, 'date')) { |
| | | // revert sort order |
| | | $order = $_SESSION['sort_col'] == 'date' && $_SESSION['sort_order'] == 'DESC' ? 'ASC' : 'DESC'; |
| | | // get the headers and add them to the list |
| | | $result_h = $IMAP->list_headers($mbox_name, 1, 'date', $order); |
| | | rcmail_js_message_list($result_h, true, false); |
| | | if ($IMAP->search($mbox_name, $search_str, NULL, 'date')) { |
| | | // revert sort order |
| | | $order = $_SESSION['sort_col'] == 'date' && $_SESSION['sort_order'] == 'DESC' ? 'ASC' : 'DESC'; |
| | | // get the headers and add them to the list |
| | | $result_h = $IMAP->list_headers($mbox_name, 1, 'date', $order); |
| | | rcmail_js_message_list($result_h, true, false); |
| | | } |
| | | } |
| | | } |
| | | else { |
| | |
| | | if (!isset($_SESSION['sort_order'])) |
| | | $_SESSION['sort_order'] = $CONFIG['message_sort_order']; |
| | | |
| | | // set threads mode |
| | | $a_threading = $RCMAIL->config->get('message_threading', array()); |
| | | if (isset($_GET['_threads'])) { |
| | | if ($_GET['_threads']) |
| | | $a_threading[$_SESSION['mbox']] = true; |
| | | else |
| | | unset($a_threading[$_SESSION['mbox']]); |
| | | $RCMAIL->user->save_prefs(array('message_threading' => $a_threading)); |
| | | } |
| | | $IMAP->set_threading($a_threading[$_SESSION['mbox']]); |
| | | |
| | | // set message set for search result |
| | | if (!empty($_REQUEST['_search']) && isset($_SESSION['search'][$_REQUEST['_search']])) |
| | | { |
| | |
| | | $OUTPUT->set_env('search_mods', $search_mods); |
| | | |
| | | // make sure the message count is refreshed (for default view) |
| | | $IMAP->messagecount($mbox_name, 'ALL', true); |
| | | $IMAP->messagecount($mbox_name, $IMAP->threading ? 'THREADS' : 'ALL', true); |
| | | } |
| | | |
| | | // set current mailbox in client environment |
| | | // set current mailbox and some other vars in client environment |
| | | $OUTPUT->set_env('mailbox', $mbox_name); |
| | | $OUTPUT->set_env('pagesize', $IMAP->page_size); |
| | | $OUTPUT->set_env('quota', $IMAP->get_capability('quota')); |
| | | $OUTPUT->set_env('delimiter', $IMAP->get_hierarchy_delimiter()); |
| | | $OUTPUT->set_env('threading', (bool) $IMAP->threading); |
| | | $OUTPUT->set_env('threads', $IMAP->threading |
| | | || $IMAP->get_capability('thread=references') |
| | | || $IMAP->get_capability('thread=orderedsubject') |
| | | || $IMAP->get_capability('thread=refs') |
| | | ); |
| | | |
| | | if ($CONFIG['flag_for_deletion']) |
| | | $OUTPUT->set_env('flag_for_deletion', true); |
| | |
| | | * return the message list as HTML table |
| | | */ |
| | | function rcmail_message_list($attrib) |
| | | { |
| | | global $IMAP, $CONFIG, $COMM_PATH, $OUTPUT; |
| | | { |
| | | global $IMAP, $CONFIG, $OUTPUT; |
| | | |
| | | $skin_path = $CONFIG['skin_path']; |
| | | $image_tag = '<img src="%s%s" alt="%s" />'; |
| | | |
| | | // check to see if we have some settings for sorting |
| | | $sort_col = $_SESSION['sort_col']; |
| | | $sort_order = $_SESSION['sort_order']; |
| | | |
| | | // add some labels to client |
| | | $OUTPUT->add_label('from', 'to'); |
| | | |
| | | // get message headers |
| | | $a_headers = $IMAP->list_headers('', '', $sort_col, $sort_order); |
| | | |
| | | // add id to message list table if not specified |
| | | if (!strlen($attrib['id'])) |
| | | $attrib['id'] = 'rcubemessagelist'; |
| | | |
| | | // allow the following attributes to be added to the <table> tag |
| | | $attrib_str = create_attrib_string($attrib, array('style', 'class', 'id', 'cellpadding', 'cellspacing', 'border', 'summary')); |
| | | |
| | | $out = '<table' . $attrib_str . ">\n"; |
| | | |
| | | // define list of cols to be displayed based on parameter or config |
| | | if (empty($attrib['columns'])) |
| | |
| | | else |
| | | $a_show_cols = preg_split('/[\s,;]+/', strip_quotes($attrib['columns'])); |
| | | |
| | | // store column list in a session-variable |
| | | // save some variables for use in ajax list |
| | | $_SESSION['list_columns'] = $a_show_cols; |
| | | $_SESSION['list_attrib'] = $attrib; |
| | | |
| | | // define sortable columns |
| | | $a_sort_cols = array('subject', 'date', 'from', 'to', 'size'); |
| | | |
| | | $mbox = $IMAP->get_mailbox_name(); |
| | | $delim = $IMAP->get_hierarchy_delimiter(); |
| | | |
| | |
| | | if ((strpos($mbox.$delim, $CONFIG['sent_mbox'].$delim)===0 || strpos($mbox.$delim, $CONFIG['drafts_mbox'].$delim)===0) |
| | | && (($f = array_search('from', $a_show_cols)) !== false) && array_search('to', $a_show_cols) === false) |
| | | $a_show_cols[$f] = 'to'; |
| | | |
| | | // add col definition |
| | | $out .= '<colgroup>'; |
| | | $out .= '<col class="icon" />'; |
| | | |
| | | foreach ($a_show_cols as $col) |
| | | $out .= ($col!='attachment') ? sprintf('<col class="%s" />', $col) : '<col class="icon" />'; |
| | | |
| | | $out .= "</colgroup>\n"; |
| | | |
| | | // add table title |
| | | $out .= "<thead><tr>\n<td class=\"icon\"> </td>\n"; |
| | | |
| | | $javascript = ''; |
| | | foreach ($a_show_cols as $col) |
| | | { |
| | | // get column name |
| | | switch ($col) |
| | | { |
| | | case 'flag': |
| | | $col_name = sprintf($image_tag, $skin_path, $attrib['unflaggedicon'], ''); |
| | | break; |
| | | case 'attachment': |
| | | $col_name = sprintf($image_tag, $skin_path, $attrib['attachmenticon'], ''); |
| | | break; |
| | | default: |
| | | $col_name = Q(rcube_label($col)); |
| | | } |
| | | |
| | | // make sort links |
| | | $sort = ''; |
| | | if (in_array($col, $a_sort_cols)) |
| | | { |
| | | // have buttons configured |
| | | if (!empty($attrib['sortdescbutton']) || !empty($attrib['sortascbutton'])) |
| | | { |
| | | $sort = ' '; |
| | | |
| | | // asc link |
| | | if (!empty($attrib['sortascbutton'])) |
| | | { |
| | | $sort .= $OUTPUT->button(array( |
| | | 'command' => 'sort', |
| | | 'prop' => $col.'_ASC', |
| | | 'image' => $attrib['sortascbutton'], |
| | | 'align' => 'absmiddle', |
| | | 'title' => 'sortasc')); |
| | | } |
| | | |
| | | // desc link |
| | | if (!empty($attrib['sortdescbutton'])) |
| | | { |
| | | $sort .= $OUTPUT->button(array( |
| | | 'command' => 'sort', |
| | | 'prop' => $col.'_DESC', |
| | | 'image' => $attrib['sortdescbutton'], |
| | | 'align' => 'absmiddle', |
| | | 'title' => 'sortdesc')); |
| | | } |
| | | } |
| | | // just add a link tag to the header |
| | | else |
| | | { |
| | | $col_name = sprintf( |
| | | '<a href="./#sort" onclick="return %s.command(\'sort\',\'%s\',this)" title="%s">%s</a>', |
| | | JS_OBJECT_NAME, |
| | | $col, |
| | | rcube_label('sortby'), |
| | | $col_name); |
| | | } |
| | | } |
| | | |
| | | $sort_class = $col==$sort_col ? " sorted$sort_order" : ''; |
| | | |
| | | // put it all together |
| | | if ($col!='attachment') |
| | | $out .= '<td class="'.$col.$sort_class.'" id="rcm'.$col.'">' . "$col_name$sort</td>\n"; |
| | | else |
| | | $out .= '<td class="icon" id="rcm'.$col.'">' . "$col_name$sort</td>\n"; |
| | | } |
| | | |
| | | $out .= "</tr></thead>\n<tbody>\n"; |
| | | |
| | | // no messages in this mailbox |
| | | if (!sizeof($a_headers)) |
| | | $OUTPUT->show_message('nomessagesfound', 'notice'); |
| | | |
| | | $a_js_message_arr = array(); |
| | | |
| | | // create row for each message |
| | | foreach ($a_headers as $i => $header) //while (list($i, $header) = each($a_headers)) |
| | | { |
| | | $message_icon = $attach_icon = $flagged_icon = ''; |
| | | $js_row_arr = array(); |
| | | $zebra_class = $i%2 ? ' even' : ' odd'; |
| | | |
| | | // set messag attributes to javascript array |
| | | if ($header->deleted) |
| | | $js_row_arr['deleted'] = true; |
| | | if (!$header->seen) |
| | | $js_row_arr['unread'] = true; |
| | | if ($header->answered) |
| | | $js_row_arr['replied'] = true; |
| | | if ($header->forwarded) |
| | | $js_row_arr['forwarded'] = true; |
| | | if ($header->flagged) |
| | | $js_row_arr['flagged'] = true; |
| | | |
| | | // set message icon |
| | | if ($attrib['deletedicon'] && $header->deleted) |
| | | $message_icon = $attrib['deletedicon']; |
| | | else if ($attrib['repliedicon'] && $header->answered) |
| | | { |
| | | if ($attrib['forwardedrepliedicon'] && $header->forwarded) |
| | | $message_icon = $attrib['forwardedrepliedicon']; |
| | | else |
| | | $message_icon = $attrib['repliedicon']; |
| | | } |
| | | else if ($attrib['forwardedicon'] && $header->forwarded) |
| | | $message_icon = $attrib['forwardedicon']; |
| | | else if ($attrib['unreadicon'] && !$header->seen) |
| | | $message_icon = $attrib['unreadicon']; |
| | | else if ($attrib['messageicon']) |
| | | $message_icon = $attrib['messageicon']; |
| | | |
| | | if ($attrib['flaggedicon'] && $header->flagged) |
| | | $flagged_icon = $attrib['flaggedicon']; |
| | | else if ($attrib['unflaggedicon'] && !$header->flagged) |
| | | $flagged_icon = $attrib['unflaggedicon']; |
| | | |
| | | // set attachment icon |
| | | if ($attrib['attachmenticon'] && preg_match("/multipart\/m/i", $header->ctype)) |
| | | $attach_icon = $attrib['attachmenticon']; |
| | | |
| | | $out .= sprintf('<tr id="rcmrow%d" class="message%s%s%s%s">'."\n", |
| | | $header->uid, |
| | | $header->seen ? '' : ' unread', |
| | | $header->deleted ? ' deleted' : '', |
| | | $header->flagged ? ' flagged' : '', |
| | | $zebra_class); |
| | | |
| | | $out .= sprintf("<td class=\"icon\">%s</td>\n", $message_icon ? sprintf($image_tag, $skin_path, $message_icon, '') : ''); |
| | | |
| | | $IMAP->set_charset(!empty($header->charset) ? $header->charset : $CONFIG['default_charset']); |
| | | |
| | | // format each col |
| | | foreach ($a_show_cols as $col) |
| | | { |
| | | if (in_array($col, array('from', 'to', 'cc', 'replyto'))) |
| | | $cont = Q(rcmail_address_string($header->$col, 3, false, $attrib['addicon']), 'show'); |
| | | else if ($col=='subject') |
| | | { |
| | | $action = $mbox==$CONFIG['drafts_mbox'] ? 'compose' : 'show'; |
| | | $uid_param = $mbox==$CONFIG['drafts_mbox'] ? '_draft_uid' : '_uid'; |
| | | $cont = abbreviate_string(trim($IMAP->decode_header($header->$col)), 160); |
| | | if (empty($cont)) $cont = rcube_label('nosubject'); |
| | | $cont = $OUTPUT->browser->ie ? Q($cont) : sprintf('<a href="%s" onclick="return rcube_event.cancel(event)">%s</a>', Q(rcmail_url($action, array($uid_param=>$header->uid, '_mbox'=>$mbox))), Q($cont)); |
| | | } |
| | | else if ($col=='flag') |
| | | $cont = $flagged_icon ? sprintf($image_tag, $skin_path, $flagged_icon, '') : ''; |
| | | else if ($col=='size') |
| | | $cont = show_bytes($header->$col); |
| | | else if ($col=='date') |
| | | $cont = format_date($header->date); |
| | | else |
| | | $cont = Q($header->$col); |
| | | |
| | | if ($col!='attachment') |
| | | $out .= '<td class="'.$col.'">' . $cont . "</td>\n"; |
| | | else |
| | | $out .= sprintf("<td class=\"icon\">%s</td>\n", $attach_icon ? sprintf($image_tag, $skin_path, $attach_icon, '') : ' '); |
| | | } |
| | | |
| | | $out .= "</tr>\n"; |
| | | |
| | | if (sizeof($js_row_arr)) |
| | | $a_js_message_arr[$header->uid] = $js_row_arr; |
| | | } |
| | | |
| | | // complete message table |
| | | $out .= "</tbody></table>\n"; |
| | | |
| | | $message_count = $IMAP->messagecount(); |
| | | $skin_path = $_SESSION['skin_path'] = $CONFIG['skin_path']; |
| | | $message_count = $IMAP->messagecount(NULL, $IMAP->threading ? 'THREADS' : 'ALL'); |
| | | |
| | | // set client env |
| | | $OUTPUT->add_gui_object('mailcontframe', 'mailcontframe'); |
| | | $OUTPUT->add_gui_object('messagelist', $attrib['id']); |
| | | $OUTPUT->set_env('autoexpand_threads', intval($CONFIG['autoexpand_threads'])); |
| | | $OUTPUT->set_env('messagecount', $message_count); |
| | | $OUTPUT->set_env('current_page', $IMAP->list_page); |
| | | $OUTPUT->set_env('pagecount', ceil($message_count/$IMAP->page_size)); |
| | | $OUTPUT->set_env('sort_col', $sort_col); |
| | | $OUTPUT->set_env('sort_order', $sort_order); |
| | | $OUTPUT->set_env('sort_col', $_SESSION['sort_col']); |
| | | $OUTPUT->set_env('sort_order', $_SESSION['sort_order']); |
| | | |
| | | if ($attrib['messageicon']) |
| | | $OUTPUT->set_env('messageicon', $skin_path . $attrib['messageicon']); |
| | |
| | | $OUTPUT->set_env('flaggedicon', $skin_path . $attrib['flaggedicon']); |
| | | if ($attrib['unflaggedicon']) |
| | | $OUTPUT->set_env('unflaggedicon', $skin_path . $attrib['unflaggedicon']); |
| | | if ($attrib['unreadchildrenicon']) |
| | | $OUTPUT->set_env('unreadchildrenicon', $skin_path . $attrib['unreadchildrenicon']); |
| | | |
| | | $OUTPUT->set_env('messages', $a_js_message_arr); |
| | | $OUTPUT->set_env('messages', array()); |
| | | $OUTPUT->set_env('coltypes', $a_show_cols); |
| | | |
| | | if (!$message_count) |
| | | $OUTPUT->show_message('nomessagesfound', 'notice'); |
| | | |
| | | $OUTPUT->include_script('list.js'); |
| | | |
| | | return $out; |
| | | } |
| | | $thead = ''; |
| | | foreach (rcmail_message_list_head($attrib, $a_show_cols) as $cell) |
| | | $thead .= html::tag('td', array('class' => $cell['className'], 'id' => $cell['id']), $cell['html']); |
| | | |
| | | return html::tag('table', |
| | | $attrib, |
| | | html::tag('thead', null, html::tag('tr', null, $thead)) . |
| | | html::tag('tbody', null, ''), |
| | | array('style', 'class', 'id', 'cellpadding', 'cellspacing', 'border', 'summary')); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * return javascript commands to add rows to the message list |
| | | * or to replace the whole list (IE only) |
| | | */ |
| | | function rcmail_js_message_list($a_headers, $insert_top=FALSE, $replace=TRUE) |
| | | { |
| | | function rcmail_js_message_list($a_headers, $insert_top=FALSE, $replace=TRUE, $head_replace=FALSE) |
| | | { |
| | | global $CONFIG, $IMAP, $OUTPUT; |
| | | |
| | | if (empty($_SESSION['list_columns'])) |
| | |
| | | && (($f = array_search('from', $a_show_cols)) !== false) && array_search('to', $a_show_cols) === false) |
| | | $a_show_cols[$f] = 'to'; |
| | | |
| | | $browser = new rcube_browser; |
| | | $thead = $head_replace ? rcmail_message_list_head($_SESSION['list_attrib'], $a_show_cols) : NULL; |
| | | |
| | | $OUTPUT->command('set_message_coltypes', $a_show_cols, $thead); |
| | | |
| | | $OUTPUT->command('set_message_coltypes', $a_show_cols); |
| | | if (empty($a_headers)) |
| | | return; |
| | | |
| | | // remove 'attachment' and 'flag' columns, we don't need them here |
| | | if(($key = array_search('attachment', $a_show_cols)) !== FALSE) |
| | |
| | | if(($key = array_search('flag', $a_show_cols)) !== FALSE) |
| | | unset($a_show_cols[$key]); |
| | | |
| | | if ($browser->ie && $replace) |
| | | if ($OUTPUT->browser->ie && $replace) |
| | | $OUTPUT->command('offline_message_list', true); |
| | | |
| | | // loop through message headers |
| | |
| | | $cont = Q(rcmail_address_string($header->$col, 3), 'show'); |
| | | else if ($col=='subject') |
| | | { |
| | | $cont = abbreviate_string(trim($IMAP->decode_header($header->$col)), 160); |
| | | $cont = abbreviate_string(trim($IMAP->decode_header($header->$col)), 160); |
| | | if (!$cont) $cont = rcube_label('nosubject'); |
| | | $cont = Q($cont); |
| | | $a_msg_cols['mbox'] = $mbox; |
| | | $cont = Q($cont); |
| | | } |
| | | else if ($col=='size') |
| | | $cont = show_bytes($header->$col); |
| | |
| | | $a_msg_cols[$col] = $cont; |
| | | } |
| | | |
| | | if ($header->depth) |
| | | $a_msg_flags['depth'] = $header->depth; |
| | | if ($header->parent_uid) |
| | | $a_msg_flags['parent_uid'] = $header->parent_uid; |
| | | if ($header->has_children) |
| | | $a_msg_flags['has_children'] = $header->has_children; |
| | | if ($header->unread_children) |
| | | $a_msg_flags['unread_children'] = $header->unread_children; |
| | | if ($header->deleted) |
| | | $a_msg_flags['deleted'] = 1; |
| | | if (!$header->seen) |
| | |
| | | $a_msg_flags['forwarded'] = 1; |
| | | if ($header->flagged) |
| | | $a_msg_flags['flagged'] = 1; |
| | | |
| | | if(preg_match("/multipart\/m/i", $header->ctype)) |
| | | $a_msg_flags['attachment'] = 1; |
| | | $a_msg_flags['mbox'] = $mbox; |
| | | |
| | | $OUTPUT->command('add_message_row', |
| | | $header->uid, |
| | | $a_msg_cols, |
| | | $a_msg_flags, |
| | | preg_match("/multipart\/m/i", $header->ctype), |
| | | $insert_top); |
| | | } |
| | | |
| | | if ($browser->ie && $replace) |
| | | $OUTPUT->command('offline_message_list', false); |
| | | } |
| | | |
| | | |
| | | /* |
| | | * Creates <THEAD> for message list table |
| | | */ |
| | | function rcmail_message_list_head($attrib, $a_show_cols) |
| | | { |
| | | global $CONFIG; |
| | | |
| | | $skin_path = $_SESSION['skin_path']; |
| | | $image_tag = html::img(array('src' => "%s%s", 'alt' => "%s")); |
| | | |
| | | // check to see if we have some settings for sorting |
| | | $sort_col = $_SESSION['sort_col']; |
| | | $sort_order = $_SESSION['sort_order']; |
| | | |
| | | // define sortable columns |
| | | $a_sort_cols = array('subject', 'date', 'from', 'to', 'size', 'cc'); |
| | | |
| | | if (!empty($attrib['optionsmenuicon'])) |
| | | $list_menu = html::a( |
| | | array('href' => '#', 'onclick' => 'return '.JS_OBJECT_NAME.".command('menu-open', 'messagelistmenu')"), |
| | | html::img(array('src' => $skin_path . $attrib['optionsmenuicon'], 'id' => 'listmenulink', 'title' => rcube_label('listoptions'))) |
| | | ); |
| | | else |
| | | $list_menu = ''; |
| | | |
| | | $cells = array(array('className' => 'threads', 'html' => $list_menu)); |
| | | |
| | | foreach ($a_show_cols as $col) { |
| | | // get column name |
| | | switch ($col) { |
| | | case 'flag': |
| | | $col_name = sprintf($image_tag, $skin_path, $attrib['unflaggedicon'], ''); |
| | | break; |
| | | case 'attachment': |
| | | $col_name = sprintf($image_tag, $skin_path, $attrib['attachmenticon'], ''); |
| | | break; |
| | | default: |
| | | $col_name = Q(rcube_label($col)); |
| | | } |
| | | |
| | | // make sort links |
| | | if (in_array($col, $a_sort_cols)) |
| | | $col_name = html::a(array('href'=>"./#sort", 'onclick' => 'return '.JS_OBJECT_NAME.".command('sort','".$col."',this)", 'title' => rcube_label('sortby')), $col_name); |
| | | |
| | | $sort_class = $col == $sort_col ? " sorted$sort_order" : ''; |
| | | $class_name = $col == 'attachment' ? 'icon' : $col.$sort_class; |
| | | |
| | | // put it all together |
| | | $cells[] = array('className' => $class_name, 'id' => "rcm$col", 'html' => $col_name); |
| | | } |
| | | |
| | | return $cells; |
| | | } |
| | | |
| | | |
| | | /** |
| | |
| | | |
| | | function rcmail_quota_display($attrib) |
| | | { |
| | | global $OUTPUT, $COMM_PATH; |
| | | global $OUTPUT; |
| | | |
| | | if (!$attrib['id']) |
| | | $attrib['id'] = 'rcmquotadisplay'; |
| | |
| | | if (isset($MESSAGE->index)) |
| | | { |
| | | return rcube_label(array('name' => 'messagenrof', |
| | | 'vars' => array('nr' => $MESSAGE->index+1, |
| | | 'count' => $count!==NULL ? $count : $IMAP->messagecount()))); |
| | | 'vars' => array('nr' => $MESSAGE->index+1, |
| | | 'count' => $count!==NULL ? $count : $IMAP->messagecount(NULL, 'ALL')))); // Only messages, no threads here |
| | | } |
| | | |
| | | if ($page===NULL) |
| | | $page = $IMAP->list_page; |
| | | |
| | | $start_msg = ($page-1) * $IMAP->page_size + 1; |
| | | $max = $count!==NULL ? $count : $IMAP->messagecount(); |
| | | $max = $count!==NULL ? $count : $IMAP->messagecount(NULL, $IMAP->threading ? 'THREADS' : 'ALL'); |
| | | |
| | | if ($max==0) |
| | | $out = rcube_label('mailboxempty'); |
| | | else |
| | | $out = rcube_label(array('name' => 'messagesfromto', |
| | | 'vars' => array('from' => $start_msg, |
| | | 'to' => min($max, $start_msg + $IMAP->page_size - 1), |
| | | 'count' => $max))); |
| | | $out = rcube_label(array('name' => $IMAP->threading ? 'threadsfromto' : 'messagesfromto', |
| | | 'vars' => array('from' => $start_msg, |
| | | 'to' => min($max, $start_msg + $IMAP->page_size - 1), |
| | | 'count' => $max))); |
| | | |
| | | return Q($out); |
| | | } |
| | |
| | | $save_arr = array(); |
| | | $_SESSION['sort_col'] = $save_arr['message_sort_col'] = $sort_col; |
| | | $_SESSION['sort_order'] = $save_arr['message_sort_order'] = $sort_order; |
| | | |
| | | $RCMAIL->user->save_prefs($save_arr); |
| | | } |
| | | else |
| | | { |
| | |
| | | $sort_col = isset($_SESSION['sort_col']) ? $_SESSION['sort_col'] : $CONFIG['message_sort_col']; |
| | | $sort_order = isset($_SESSION['sort_order']) ? $_SESSION['sort_order'] : $CONFIG['message_sort_order']; |
| | | } |
| | | |
| | | // is there a set of columns for this request? |
| | | if ($cols = get_input_value('_cols', RCUBE_INPUT_GET)) |
| | | { |
| | | $save_arr = array(); |
| | | $_SESSION['list_columns'] = $save_arr['list_cols'] = explode(',', $cols); |
| | | } |
| | | |
| | | if ($save_arr) |
| | | $RCMAIL->user->save_prefs($save_arr); |
| | | |
| | | $mbox_name = $IMAP->get_mailbox_name(); |
| | | |
| | |
| | | } |
| | | |
| | | // fetch message headers |
| | | if ($count = $IMAP->messagecount($mbox_name, 'ALL', !empty($_REQUEST['_refresh']))) |
| | | if ($count = $IMAP->messagecount($mbox_name, $IMAP->threading ? 'THREADS' : 'ALL', !empty($_REQUEST['_refresh']))) |
| | | $a_headers = $IMAP->list_headers($mbox_name, NULL, $sort_col, $sort_order); |
| | | |
| | | // update search set (possible change of threading mode) |
| | | if (!empty($_REQUEST['_search']) && isset($_SESSION['search'][$_REQUEST['_search']])) |
| | | $_SESSION['search'][$_REQUEST['_search']] = $IMAP->get_search_set(); |
| | | |
| | | // update mailboxlist |
| | | rcmail_send_unread_count($mbox_name, !empty($_REQUEST['_refresh'])); |
| | |
| | | $pages = ceil($count/$IMAP->page_size); |
| | | $OUTPUT->set_env('messagecount', $count); |
| | | $OUTPUT->set_env('pagecount', $pages); |
| | | $OUTPUT->set_env('threading', (bool) $IMAP->threading); |
| | | $OUTPUT->command('set_rowcount', rcmail_get_messagecount_text($count)); |
| | | $OUTPUT->command('set_mailboxname', rcmail_get_mailbox_name_text()); |
| | | |
| | | // add message rows |
| | | rcmail_js_message_list($a_headers, FALSE, TRUE, (bool) $cols); |
| | | if (isset($a_headers) && count($a_headers)) |
| | | { |
| | | rcmail_js_message_list($a_headers); |
| | | if ($search_request) |
| | | $OUTPUT->show_message('searchsuccessful', 'confirmation', array('nr' => $count)); |
| | | } |
| | |
| | | |
| | | if ($flag == 'DELETED' && $CONFIG['skip_deleted'] && $_POST['_from'] != 'show') { |
| | | // count messages before changing anything |
| | | $old_count = $IMAP->messagecount(); |
| | | $old_count = $IMAP->messagecount(NULL, $IMAP->threading ? 'THREADS' : 'ALL'); |
| | | $old_pages = ceil($old_count / $IMAP->page_size); |
| | | $count = sizeof(explode(',', $uids)); |
| | | } |
| | |
| | | $_SESSION['search'][$search_request] = $IMAP->refresh_search(); |
| | | } |
| | | |
| | | $msg_count = $IMAP->messagecount(); |
| | | $msg_count = $IMAP->messagecount(NULL, $IMAP->threading ? 'THREADS' : 'ALL'); |
| | | $pages = ceil($msg_count / $IMAP->page_size); |
| | | $nextpage_count = $old_count - $IMAP->page_size * $IMAP->list_page; |
| | | $remaining = $msg_count - $IMAP->page_size * ($IMAP->list_page - 1); |
| | |
| | | } |
| | | $OUTPUT->command('set_rowcount', rcmail_get_messagecount_text($msg_count)); |
| | | |
| | | if ($IMAP->threading) |
| | | $count = get_input_value('_count', RCUBE_INPUT_POST); |
| | | |
| | | // add new rows from next page (if any) |
| | | if (($jump_back || $nextpage_count > 0)) { |
| | | if ($count && ($jump_back || $nextpage_count > 0)) { |
| | | $sort_col = isset($_SESSION['sort_col']) ? $_SESSION['sort_col'] : $CONFIG['message_sort_col']; |
| | | $sort_order = isset($_SESSION['sort_order']) ? $_SESSION['sort_order'] : $CONFIG['message_sort_order']; |
| | | |
| | |
| | | return; |
| | | |
| | | // count messages before changing anything |
| | | $old_count = $IMAP->messagecount(); |
| | | $old_count = $IMAP->messagecount(NULL, $IMAP->threading ? 'THREADS' : 'ALL'); |
| | | $old_pages = ceil($old_count / $IMAP->page_size); |
| | | |
| | | // move messages |
| | |
| | | else if ($RCMAIL->action=='delete' && !empty($_POST['_uid'])) { |
| | | $count = sizeof(explode(',', ($uids = get_input_value('_uid', RCUBE_INPUT_POST)))); |
| | | $mbox = get_input_value('_mbox', RCUBE_INPUT_POST); |
| | | |
| | | $del = $IMAP->delete_message($uids, $mbox); |
| | | |
| | | if (!$del) { |
| | |
| | | } |
| | | else |
| | | { |
| | | $msg_count = $IMAP->messagecount(); |
| | | $msg_count = $IMAP->messagecount(NULL, $IMAP->threading ? 'THREADS' : 'ALL'); |
| | | $pages = ceil($msg_count / $IMAP->page_size); |
| | | $nextpage_count = $old_count - $IMAP->page_size * $IMAP->list_page; |
| | | $remaining = $msg_count - $IMAP->page_size * ($IMAP->list_page - 1); |
| | |
| | | $OUTPUT->command('set_quota', rcmail_quota_content()); |
| | | $OUTPUT->command('set_rowcount', rcmail_get_messagecount_text($msg_count)); |
| | | |
| | | if ($IMAP->threading) |
| | | $count = get_input_value('_count', RCUBE_INPUT_POST); |
| | | |
| | | // add new rows from next page (if any) |
| | | if ($addrows && ($jump_back || $nextpage_count > 0)) { |
| | | if ($addrows && $count && ($jump_back || $nextpage_count > 0)) { |
| | | $sort_col = isset($_SESSION['sort_col']) ? $_SESSION['sort_col'] : $CONFIG['message_sort_col']; |
| | | $sort_order = isset($_SESSION['sort_order']) ? $_SESSION['sort_order'] : $CONFIG['message_sort_order']; |
| | | |
| | |
| | | |
| | | // Get the headers |
| | | $result_h = $IMAP->list_headers($mbox, 1, $_SESSION['sort_col'], $_SESSION['sort_order']); |
| | | $count = $IMAP->messagecount(); |
| | | $count = $IMAP->messagecount(NULL, $IMAP->threading ? 'THREADS' : 'ALL'); |
| | | |
| | | // save search results in session |
| | | if (!is_array($_SESSION['search'])) |
| | |
| | | { |
| | | rcmail_js_message_list($result_h); |
| | | if ($search_str) |
| | | $OUTPUT->show_message('searchsuccessful', 'confirmation', array('nr' => $count)); |
| | | $OUTPUT->show_message('searchsuccessful', 'confirmation', array('nr' => $IMAP->messagecount(NULL, 'ALL'))); |
| | | } |
| | | else |
| | | { |
| | |
| | | $next = $prev = $first = $last = -1; |
| | | |
| | | if ($_SESSION['sort_col'] == 'date' && $_SESSION['sort_order'] != 'DESC' |
| | | && empty($_REQUEST['_search']) && !$IMAP->skip_deleted) |
| | | && empty($_REQUEST['_search']) && !$CONFIG['skip_deleted'] && !$IMAP->threading) |
| | | { |
| | | // this assumes that we are sorted by date_DESC |
| | | $cnt = $IMAP->messagecount(); |
| | |
| | | |
| | | if (!$MESSAGE->headers->seen) |
| | | $RCMAIL->plugins->exec_hook('message_read', array('uid' => $MESSAGE->uid, |
| | | 'mailbox' => $IMAP->mailbox, 'message' => $MESSAGE)); |
| | | 'mailbox' => $mbox_name, 'message' => $MESSAGE)); |
| | | } |
| | | |
| | | |
| | |
| | | if ($uid = get_input_value('_uid', RCUBE_INPUT_GET)) |
| | | { |
| | | $headers = $IMAP->get_headers($uid); |
| | | $charset = $headers->charset ? $headers->charset : $IMAP->default_charset; |
| | | $charset = $headers->charset ? $headers->charset : $CONFIG['default_charset']; |
| | | header("Content-Type: text/plain; charset={$charset}"); |
| | | |
| | | if (!empty($_GET['_save'])) { |
| | |
| | | ); |
| | | } |
| | | |
| | | // Show checkbox for toggling 'index_sort' |
| | | if (!isset($no_override['index_sort'])) { |
| | | $field_id = 'rcmfd_indexsort'; |
| | | $input_indexsort = new html_checkbox(array('name' => '_index_sort', 'id' => $field_id, 'value' => 1)); |
| | | |
| | | $blocks['list']['options']['index_sort'] = array( |
| | | 'title' => html::label($field_id, Q(rcube_label('indexsort'))), |
| | | 'content' => $input_indexsort->show($config['index_sort']?1:0), |
| | | ); |
| | | } |
| | | |
| | | // show drop-down for available skins |
| | | if (!isset($no_override['skin'])) { |
| | | $skins = rcmail_get_skins(); |
| | |
| | | ); |
| | | } |
| | | |
| | | if (!isset($no_override['autoexpand_threads'])) { |
| | | $field_id = 'rcmfd_autoexpand_threads'; |
| | | $select_autoexpand_threads = new html_select(array('name' => '_autoexpand_threads', 'id' => $field_id)); |
| | | $select_autoexpand_threads->add(rcube_label('never'), 0); |
| | | $select_autoexpand_threads->add(rcube_label('do_expand'), 1); |
| | | $select_autoexpand_threads->add(rcube_label('expand_only_unread'), 2); |
| | | |
| | | $blocks['main']['options']['autoexpand_threads'] = array( |
| | | 'title' => html::label($field_id, Q(rcube_label('autoexpand_threads'))), |
| | | 'content' => $select_autoexpand_threads->show($config['autoexpand_threads']), |
| | | ); |
| | | } |
| | | |
| | | if (!isset($no_override['focus_on_new_message'])) { |
| | | $field_id = 'rcmfd_focus_on_new_message'; |
| | | $input_focus_on_new_message = new html_checkbox(array('name' => '_focus_on_new_message', 'id' => $field_id, 'value' => 1)); |
| | |
| | | $IMAP->unsubscribe(array($mbox)); |
| | | } |
| | | |
| | | // enable threading for one or more mailboxes |
| | | else if ($RCMAIL->action=='enable-threading') |
| | | { |
| | | if ($mbox = get_input_value('_mbox', RCUBE_INPUT_POST, false, 'UTF7-IMAP')) |
| | | rcube_set_threading($mbox, true); |
| | | } |
| | | |
| | | // enable threading for one or more mailboxes |
| | | else if ($RCMAIL->action=='disable-threading') |
| | | { |
| | | if ($mbox = get_input_value('_mbox', RCUBE_INPUT_POST, false, 'UTF7-IMAP')) |
| | | rcube_set_threading($mbox, false); |
| | | } |
| | | |
| | | // create a new mailbox |
| | | else if ($RCMAIL->action=='create-folder') |
| | | { |
| | |
| | | $oldname = rcube_charset_convert($oldname_utf8, RCMAIL_CHARSET, 'UTF7-IMAP'); |
| | | |
| | | $rename = $IMAP->rename_mailbox($oldname, $name); |
| | | } |
| | | |
| | | // update per-folder options for modified folder and its subfolders |
| | | if ($rename) { |
| | | $a_threaded = $RCMAIL->config->get('message_threading', array()); |
| | | $delimiter = $IMAP->get_hierarchy_delimiter(); |
| | | $oldprefix = '/^' . preg_quote($oldname . $delimiter, '/') . '/'; |
| | | foreach ($a_threaded as $key => $val) |
| | | if ($key == $oldname) { |
| | | unset($a_threaded[$key]); |
| | | $a_threaded[$name] = true; |
| | | } |
| | | else if (preg_match($oldprefix, $key)) { |
| | | unset($a_threaded[$key]); |
| | | $a_threaded[preg_replace($oldprefix, $name.$delimiter, $key)] = true; |
| | | } |
| | | |
| | | $RCMAIL->user->save_prefs(array('message_threading' => $a_threaded)); |
| | | } |
| | | |
| | | if ($rename && $OUTPUT->ajax_call) |
| | |
| | | // build table with all folders listed by server |
| | | function rcube_subscription_form($attrib) |
| | | { |
| | | global $IMAP, $CONFIG, $OUTPUT; |
| | | global $RCMAIL, $IMAP, $CONFIG, $OUTPUT; |
| | | |
| | | $threading_supported = $IMAP->get_capability('thread=references') |
| | | || $IMAP->get_capability('thread=orderedsubject') |
| | | || $IMAP->get_capability('thread=refs'); |
| | | |
| | | list($form_start, $form_end) = get_form_tags($attrib, 'folders'); |
| | | unset($attrib['form']); |
| | |
| | | $table->add_header('name', rcube_label('foldername')); |
| | | $table->add_header('msgcount', rcube_label('messagecount')); |
| | | $table->add_header('subscribed', rcube_label('subscribed')); |
| | | if ($threading_supported) |
| | | $table->add_header('threaded', rcube_label('threaded')); |
| | | $table->add_header('rename', ' '); |
| | | $table->add_header('delete', ' '); |
| | | |
| | | |
| | | // get folders from server |
| | | $IMAP->clear_cache('mailboxes'); |
| | | |
| | | $a_unsubscribed = $IMAP->list_unsubscribed(); |
| | | $a_subscribed = $IMAP->list_mailboxes(); |
| | | $a_threaded = $a_threaded_copy = $RCMAIL->config->get('message_threading', array()); |
| | | $delimiter = $IMAP->get_hierarchy_delimiter(); |
| | | $a_js_folders = $seen_folders = $list_folders = array(); |
| | | |
| | |
| | | } |
| | | } |
| | | |
| | | unset($a_threaded_copy[$folder]); |
| | | |
| | | $list_folders[] = array('id' => $folder, 'name' => $name, 'level' => $level); |
| | | $seen[$folder]++; |
| | | } |
| | | |
| | | // remove 'message_threading' option for not existing folders |
| | | if ($a_threaded_copy) { |
| | | foreach ($a_threaded_copy as $key => $val) |
| | | unset($a_threaded[$key]); |
| | | unset($a_threaded_copy); |
| | | $RCMAIL->user->save_prefs(array('message_threading' => $a_threaded)); |
| | | } |
| | | |
| | | $checkbox_subscribe = new html_checkbox(array( |
| | | 'name' => '_subscribed[]', |
| | | 'onclick' => JS_OBJECT_NAME.".command(this.checked?'subscribe':'unsubscribe',this.value)", |
| | | )); |
| | | $checkbox_threaded = new html_checkbox(array( |
| | | 'name' => '_threaded[]', |
| | | 'onclick' => JS_OBJECT_NAME.".command(this.checked?'enable-threading':'disable-threading',this.value)", |
| | | )); |
| | | |
| | | if (!empty($attrib['deleteicon'])) |
| | |
| | | foreach ($list_folders as $i => $folder) { |
| | | $idx = $i + 1; |
| | | $subscribed = in_array($folder['id'], $a_subscribed); |
| | | $threaded = $a_threaded[$folder['id']]; |
| | | $protected = ($CONFIG['protect_default_folders'] == true && in_array($folder['id'], $CONFIG['default_imap_folders'])); |
| | | $classes = array($i%2 ? 'even' : 'odd'); |
| | | $folder_js = JQ($folder['id']); |
| | |
| | | $table->add_row(array('id' => 'rcmrow'.$idx, 'class' => join(' ', $classes))); |
| | | |
| | | $table->add('name', Q($display_folder)); |
| | | $table->add('msgcount', ($folder['virtual'] ? '' : $IMAP->messagecount($folder['id']))); |
| | | $table->add('msgcount', ($folder['virtual'] ? '' : $IMAP->messagecount($folder['id']))); // XXX: Use THREADS or ALL? |
| | | $table->add('subscribed', ($protected || $folder['virtual']) ? ($subscribed ? ' •' : ' ') : |
| | | $checkbox_subscribe->show(($subscribed ? $folder_utf8 : ''), array('value' => $folder_utf8))); |
| | | if ($IMAP->get_capability('thread=references')) { |
| | | $table->add('threaded', |
| | | $checkbox_threaded->show(($threaded ? $folder_utf8 : ''), array('value' => $folder_utf8))); |
| | | } |
| | | |
| | | // add rename and delete buttons |
| | | if (!$protected && !$folder['virtual']) { |
| | |
| | | return $out; |
| | | } |
| | | |
| | | |
| | | // (un)set 'threading' for selected folder |
| | | function rcube_set_threading($mbox, $state=true) |
| | | { |
| | | global $RCMAIL; |
| | | $mbox = (array)$mbox; |
| | | $a_prefs = (array)$RCMAIL->config->get('message_threading'); |
| | | |
| | | if ($state) { |
| | | foreach ($mbox as $box) |
| | | $a_prefs[$box] = true; |
| | | } |
| | | else { |
| | | foreach ($mbox as $box) |
| | | unset($a_prefs[$box]); |
| | | } |
| | | |
| | | $RCMAIL->user->save_prefs(array('message_threading' => $a_prefs)); |
| | | } |
| | | |
| | | |
| | | $OUTPUT->set_pagetitle(rcube_label('folders')); |
| | | $OUTPUT->include_script('list.js'); |
| | | |
| | |
| | | 'timezone' => isset($_POST['_timezone']) ? (is_numeric($_POST['_timezone']) ? floatval($_POST['_timezone']) : get_input_value('_timezone', RCUBE_INPUT_POST)) : $CONFIG['timezone'], |
| | | 'dst_active' => isset($_POST['_dst_active']) ? TRUE : FALSE, |
| | | 'pagesize' => is_numeric($_POST['_pagesize']) ? max(2, intval($_POST['_pagesize'])) : $CONFIG['pagesize'], |
| | | 'index_sort' => isset($_POST['_index_sort']) ? TRUE : FALSE, |
| | | 'prettydate' => isset($_POST['_pretty_date']) ? TRUE : FALSE, |
| | | 'skin' => isset($_POST['_skin']) ? get_input_value('_skin', RCUBE_INPUT_POST) : $CONFIG['skin'], |
| | | ); |
| | |
| | | $a_user_prefs = array( |
| | | 'focus_on_new_message' => isset($_POST['_focus_on_new_message']) ? TRUE : FALSE, |
| | | 'preview_pane' => isset($_POST['_preview_pane']) ? TRUE : FALSE, |
| | | 'autoexpand_threads' => isset($_POST['_autoexpand_threads']) ? intval($_POST['_autoexpand_threads']) : 0, |
| | | 'mdn_requests' => isset($_POST['_mdn_requests']) ? intval($_POST['_mdn_requests']) : 0, |
| | | 'keep_alive' => isset($_POST['_keep_alive']) ? intval($_POST['_keep_alive'])*60 : $CONFIG['keep_alive'], |
| | | 'check_all_folders' => isset($_POST['_check_all_folders']) ? TRUE : FALSE, |
| | |
| | | |
| | | ul.toolbarmenu |
| | | { |
| | | margin: 0; |
| | | margin: -4px 0 -4px 0; |
| | | padding: 0; |
| | | list-style: none; |
| | | } |
| | |
| | | font-size: 11px; |
| | | white-space: nowrap; |
| | | min-width: 130px; |
| | | margin: 3px -4px; |
| | | } |
| | | |
| | | ul.toolbarmenu li a |
| | | { |
| | | display: block; |
| | | color: #a0a0a0; |
| | | padding: 2px 8px 3px 22px; |
| | | padding: 2px 12px 3px 28px; |
| | | text-decoration: none; |
| | | min-height: 14px; |
| | | } |
| | |
| | | margin-top: 2px; |
| | | padding-top: 2px; |
| | | } |
| | | |
| | | .disabled |
| | | { |
| | | color: #999; |
| | | } |
| | |
| | | this.markmenu = $('#markmessagemenu'); |
| | | this.searchmenu = $('#searchmenu'); |
| | | this.messagemenu = $('#messagemenu'); |
| | | this.listmenu = $('#listmenu'); |
| | | } |
| | | |
| | | rcube_mail_ui.prototype = { |
| | |
| | | rcmail.env.search_mods[rcmail.env.mailbox][elem.value] = elem.value; |
| | | }, |
| | | |
| | | show_listmenu: function(show) |
| | | { |
| | | if (typeof show == 'undefined') |
| | | show = this.listmenu.is(':visible') ? false : true; |
| | | |
| | | var ref = rcube_find_object('listmenulink'); |
| | | if (show && ref) { |
| | | var pos = $(ref).offset(); |
| | | this.listmenu.css({ left:pos.left, top:(pos.top + ref.offsetHeight + 2)}); |
| | | // set form values |
| | | $('input[name="sort_col"][value="'+rcmail.env.sort_col+'"]').attr('checked', 1); |
| | | $('input[name="sort_ord"][value="DESC"]').attr('checked', rcmail.env.sort_order=='DESC' ? 1 : 0); |
| | | $('input[name="sort_ord"][value="ASC"]').attr('checked', rcmail.env.sort_order=='DESC' ? 0 : 1); |
| | | $('input[name="view"][value="thread"]').attr('checked', rcmail.env.threading ? 1 : 0); |
| | | $('input[name="view"][value="list"]').attr('checked', rcmail.env.threading ? 0 : 1); |
| | | // list columns |
| | | var cols = $('input[name="list_col[]"]'); |
| | | for (var i=0; i<cols.length; i++) { |
| | | var found = 0; |
| | | if (cols[i].value != 'from') |
| | | found = jQuery.inArray(cols[i].value, rcmail.env.coltypes) != -1; |
| | | else |
| | | found = (jQuery.inArray('from', rcmail.env.coltypes) != -1 |
| | | || jQuery.inArray('to', rcmail.env.coltypes) != -1); |
| | | $(cols[i]).attr('checked',found ? 1 : 0); |
| | | } |
| | | } |
| | | |
| | | this.listmenu[show?'show':'hide'](); |
| | | |
| | | if (show) { |
| | | var maxheight=0; |
| | | $('#listmenu fieldset').each(function() { |
| | | var height = $(this).height(); |
| | | if (height > maxheight) { |
| | | maxheight = height; |
| | | } |
| | | }); |
| | | $('#listmenu fieldset').css("min-height", maxheight+"px") |
| | | // IE6 complains if you set this attribute using either method: |
| | | //$('#listmenu fieldset').css({'height':'auto !important'}); |
| | | //$('#listmenu fieldset').css("height","auto !important"); |
| | | .height(maxheight); |
| | | }; |
| | | }, |
| | | |
| | | open_listmenu: function(e) |
| | | { |
| | | this.show_listmenu(); |
| | | }, |
| | | |
| | | save_listmenu: function() |
| | | { |
| | | this.show_listmenu(); |
| | | |
| | | var sort = $('input[name="sort_col"]:checked').val(); |
| | | var ord = $('input[name="sort_ord"]:checked').val(); |
| | | var thread = $('input[name="view"]:checked').val(); |
| | | var cols = $('input[name="list_col[]"]:checked') |
| | | .map(function(){ return this.value; }).get(); |
| | | |
| | | rcmail.set_list_options(cols, sort, ord, thread == 'thread' ? 1 : 0); |
| | | }, |
| | | |
| | | body_mouseup: function(evt, p) |
| | | { |
| | | if (this.markmenu && this.markmenu.is(':visible') && rcube_event.get_target(evt) != rcube_find_object('markreadbutton')) |
| | | var target = rcube_event.get_target(evt); |
| | | |
| | | if (this.markmenu && this.markmenu.is(':visible') && target != rcube_find_object('markreadbutton')) |
| | | this.show_markmenu(false); |
| | | else if (this.messagemenu && this.messagemenu.is(':visible') && rcube_event.get_target(evt) != rcube_find_object('messagemenulink')) |
| | | else if (this.messagemenu && this.messagemenu.is(':visible') && target != rcube_find_object('messagemenulink')) |
| | | this.show_messagemenu(false); |
| | | else if (this.searchmenu && this.searchmenu.is(':visible') && rcube_event.get_target(evt) != rcube_find_object('searchmod')) { |
| | | else if (this.listmenu && this.listmenu.is(':visible') && target != rcube_find_object('listmenulink')) { |
| | | var menu = rcube_find_object('listmenu'); |
| | | while (target.parentNode) { |
| | | if (target.parentNode == menu) |
| | | return; |
| | | target = target.parentNode; |
| | | } |
| | | this.show_listmenu(false); |
| | | } |
| | | else if (this.searchmenu && this.searchmenu.is(':visible') && target != rcube_find_object('searchmod')) { |
| | | var menu = rcube_find_object('searchmenu'); |
| | | var target = rcube_event.get_target(evt); |
| | | while (target.parentNode) { |
| | | if (target.parentNode == menu) |
| | | return; |
| | |
| | | this.show_searchmenu(false); |
| | | if (this.messagemenu && this.messagemenu.is(':visible')) |
| | | this.show_messagemenu(false); |
| | | if (this.listmenu && this.listmenu.is(':visible')) |
| | | this.show_listmenu(false); |
| | | } |
| | | } |
| | | |
| | |
| | | rcmail_ui = new rcube_mail_ui(); |
| | | rcube_event.add_listener({ object:rcmail_ui, method:'body_mouseup', event:'mouseup' }); |
| | | rcube_event.add_listener({ object:rcmail_ui, method:'body_keypress', event:'keypress' }); |
| | | rcmail.addEventListener('menu-open', 'open_listmenu', rcmail_ui); |
| | | rcmail.addEventListener('menu-save', 'save_listmenu', rcmail_ui); |
| | | } |
| | |
| | | background-image: url('images/display/icons.gif'); |
| | | } |
| | | |
| | | #messagemenu li a |
| | | #messagemenu li a, |
| | | #messagelist tr td div.expanded, |
| | | #messagelist tr td div.collapsed |
| | | { |
| | | background-image: url('images/messageactions.gif'); |
| | | } |
| | |
| | | background-image: url('images/abook_toolbar.gif'); |
| | | } |
| | | |
| | | ul.toolbarmenu li |
| | | { |
| | | width: auto; |
| | | border: 1px solid #f6f6f6; |
| | | } |
| | | |
| | | ul.toolbarmenu li a |
| | | { |
| | | clear: left; |
| | | } |
| | | |
| | | ul.toolbarmenu li.separator_below |
| | | { |
| | | padding-bottom: 8px; |
| | | } |
| | |
| | | filter: alpha(opacity=85); |
| | | } |
| | | |
| | | #markmessagemenu, |
| | | #searchmenu, |
| | | #messagemenu |
| | | .popupmenu |
| | | { |
| | | -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=90)"; |
| | | filter: alpha(opacity=90); |
| | | |
| | | background-color: #ffffff; |
| | | } |
| | | |
| | | #tabsbar |
| | |
| | | border-collapse: collapse; |
| | | } |
| | | |
| | | #messagelist tbody tr.unroot td.subject |
| | | { |
| | | text-decoration: underline; |
| | | } |
| | | |
| | | #messageframe |
| | | { |
| | | width: expression((parseInt(this.parentNode.offsetWidth)-180)+'px'); |
| | |
| | | filter: alpha(opacity=70); |
| | | } |
| | | |
| | | ul.toolbarmenu |
| | | { |
| | | margin: 0 0 -4px 0; |
| | | } |
| | | |
| | | ul.toolbarmenu li |
| | | { |
| | | min-width: auto; |
| | |
| | | { |
| | | height: 19px; |
| | | } |
| | | |
| | | #listmenu fieldset |
| | | { |
| | | margin: 0 4px; |
| | | padding: 0.8em; |
| | | } |
| | |
| | | <div id="messagemenu"> |
| | | <div id="messagemenu" class="popupmenu"> |
| | | <ul class="toolbarmenu"> |
| | | <li><roundcube:button class="printlink" command="print" label="printmessage" classAct="printlink active" /></li> |
| | | <li><roundcube:button class="downloadlink" command="download" label="emlsave" classAct="downloadlink active" /></li> |
| | |
| | | padding-left: 2px; |
| | | } |
| | | |
| | | #markmessagemenu, |
| | | #searchmenu, |
| | | #messagemenu |
| | | .popupmenu |
| | | { |
| | | position: absolute; |
| | | top: 32px; |
| | | left: 90px; |
| | | width: auto; |
| | | display: none; |
| | | background-color: #F9F9F9; |
| | | border: 1px solid #CCC; |
| | | padding: 1px; |
| | | opacity: 0.9; |
| | | background-color: #fff; |
| | | background-color: rgba(255, 255, 255, 0.95); |
| | | border: 1px solid #999; |
| | | padding: 4px; |
| | | z-index: 240; |
| | | border-radius: 3px; |
| | | -moz-border-radius: 3px; |
| | | -webkit-border-radius: 3px; |
| | | -moz-box-shadow: 1px 1px 12px #999; |
| | | -webkit-box-shadow: #999 1px 1px 12px; |
| | | } |
| | | |
| | | #searchmenu |
| | | { |
| | | width: 172px; |
| | | width: 160px; |
| | | } |
| | | |
| | | #searchmenu ul.toolbarmenu |
| | | { |
| | | margin: 0; |
| | | } |
| | | |
| | | #searchmenu ul.toolbarmenu li |
| | | { |
| | | margin: 1px 4px 1px; |
| | | } |
| | | |
| | | #messagemenu li a.active:hover, |
| | |
| | | |
| | | #messagemenu li a |
| | | { |
| | | background: url('images/messageactions.png') no-repeat 1px 0; |
| | | background-position: 0px 20px; |
| | | background: url('images/messageactions.png') no-repeat 7px 0; |
| | | background-position: 7px 20px; |
| | | } |
| | | |
| | | #messagemenu li a.printlink |
| | | { |
| | | background-position: 1px 1px; |
| | | background-position: 7px 1px; |
| | | } |
| | | |
| | | #messagemenu li a.downloadlink |
| | | { |
| | | background-position: 1px -17px; |
| | | background-position: 7px -17px; |
| | | } |
| | | |
| | | #messagemenu li a.sourcelink |
| | | { |
| | | background-position: 1px -35px; |
| | | background-position: 7px -35px; |
| | | } |
| | | |
| | | #messagemenu li a.openlink |
| | | { |
| | | background-position: 1px -53px; |
| | | background-position: 7px -53px; |
| | | } |
| | | |
| | | #messagemenu li a.editlink |
| | | { |
| | | background-position: 1px -71px; |
| | | background-position: 7px -71px; |
| | | } |
| | | |
| | | #markmessagemenu a.readlink |
| | | { |
| | | background: url('images/icons/dot.png') no-repeat 2px; |
| | | background: url('images/icons/dot.png') no-repeat 7px 2px; |
| | | } |
| | | |
| | | #markmessagemenu a.unreadlink |
| | | { |
| | | background: url('images/icons/unread.png') no-repeat 2px; |
| | | background: url('images/icons/unread.png') no-repeat 7px 2px; |
| | | } |
| | | |
| | | #markmessagemenu a.flaggedlink |
| | | { |
| | | background: url('images/icons/flagged.png') no-repeat 2px; |
| | | background: url('images/icons/flagged.png') no-repeat 7px 2px; |
| | | } |
| | | |
| | | #markmessagemenu a.unflaggedlink |
| | | { |
| | | background: url('images/icons/unflagged.png') no-repeat 2px; |
| | | background: url('images/icons/unflagged.png') no-repeat 7px 2px; |
| | | } |
| | | |
| | | #searchfilter |
| | |
| | | background-position: -75px -15px; |
| | | } |
| | | |
| | | #listcontrols a.expand-all { |
| | | background-position: -90px 0; |
| | | } |
| | | |
| | | #listcontrols a.expand-allsel { |
| | | background-position: -90px -15px; |
| | | } |
| | | |
| | | #listcontrols a.collapse-all { |
| | | background-position: -105px 0; |
| | | } |
| | | |
| | | #listcontrols a.collapse-allsel { |
| | | background-position: -105px -15px; |
| | | } |
| | | |
| | | #listcontrols a.expand-unread { |
| | | background-position: -120px 0; |
| | | } |
| | | |
| | | #listcontrols a.expand-unreadsel { |
| | | background-position: -120px -15px; |
| | | } |
| | | |
| | | #countcontrols |
| | | { |
| | | height: 15px; |
| | |
| | | vertical-align: middle; |
| | | } |
| | | |
| | | #messagelist thead tr td.subject |
| | | { |
| | | padding-left: 22px; |
| | | } |
| | | |
| | | #messagelist thead tr td.icon, |
| | | #messagelist thead tr td.flag |
| | | #messagelist thead tr td.flag, |
| | | #messagelist thead tr td.threads |
| | | { |
| | | width: 22px; |
| | | padding: 0; |
| | | text-align: center; |
| | | } |
| | | |
| | | #messagelist tbody tr td.icon, |
| | | #messagelist thead tr td.threads |
| | | { |
| | | width: 18px; |
| | | } |
| | | |
| | | #messagelist tbody tr td.flag |
| | | { |
| | | padding: 2px 3px 2px 3px; |
| | | vertical-align: middle; |
| | | cursor: pointer; |
| | | } |
| | | |
| | | #messagelist tr td span.branch |
| | | { |
| | | display: inline-block; |
| | | width: 15px; |
| | | height: 15px; |
| | | } |
| | | |
| | | #messagelist tr td.subject img.msgicon |
| | | { |
| | | vertical-align: middle; |
| | | } |
| | | |
| | | #messagelist tbody td img.msgicon |
| | | { |
| | | position: relative; |
| | | top: 0px; |
| | | margin-right: 5px; |
| | | } |
| | | |
| | | #messagelist tr td div.collapsed, |
| | | #messagelist tr td div.expanded, |
| | | #messagelist tr td img.flagicon, |
| | | #messagelist tr td img.msgicon |
| | | { |
| | | cursor: pointer; |
| | | } |
| | | |
| | | #messagelist tr td div.collapsed |
| | | { |
| | | display: block; |
| | | background: url('images/messageactions.png') center -91px no-repeat; |
| | | } |
| | | |
| | | #messagelist tr td div.expanded |
| | | { |
| | | display: block; |
| | | background: url('images/messageactions.png') center -109px no-repeat; |
| | | } |
| | | |
| | | #messagelist tbody tr td.flag img:hover, |
| | |
| | | overflow: hidden; |
| | | vertical-align: middle; |
| | | width: 99%; |
| | | } |
| | | |
| | | /* thread parent message with unread children */ |
| | | #messagelist tbody tr.unroot td.subject a |
| | | { |
| | | text-decoration: underline; |
| | | } |
| | | |
| | | #messagelist tr td.size |
| | |
| | | { |
| | | font-weight: bold; |
| | | } |
| | | |
| | | #listmenu |
| | | { |
| | | padding: 6px; |
| | | } |
| | | |
| | | #listmenu legend |
| | | { |
| | | color: #999999; |
| | | } |
| | | |
| | | #listmenu fieldset |
| | | { |
| | | border: 1px solid #999999; |
| | | margin: 0 5px; |
| | | float: left; |
| | | } |
| | | |
| | | #listmenu div |
| | | { |
| | | padding: 8px 0 3px 0; |
| | | text-align: center; |
| | | clear: both; |
| | | } |
| | |
| | | forwardedrepliedIcon="/images/icons/forwarded_replied.png" |
| | | attachmentIcon="/images/icons/attachment.png" |
| | | flaggedIcon="/images/icons/flagged.png" |
| | | unflaggedIcon="/images/icons/blank.gif" /> |
| | | unflaggedIcon="/images/icons/blank.gif" |
| | | unreadchildrenIcon="" |
| | | optionsmenuIcon="/images/icons/columnpicker.gif" /> |
| | | </div> |
| | | |
| | | <roundcube:if condition="config:preview_pane == true" /> |
| | |
| | | <roundcube:button command="select-all" type="link" prop="unread" title="unread" class="buttonPas unread" classAct="button unread" classSel="button unreadsel" content=" " /> |
| | | <roundcube:button command="select-all" type="link" prop="invert" title="invert" class="buttonPas invert" classAct="button invert" classSel="button invertsel" content=" " /> |
| | | <roundcube:button command="select-none" type="link" title="none" class="buttonPas none" classAct="button none" classSel="button nonesel" content=" " /> |
| | | <span style="margin-left: 20px"><roundcube:label name="threads" />: </span> |
| | | <roundcube:button command="expand-all" type="link" title="expand-all" class="buttonPas expand-all" classAct="button expand-all" classSel="button expand-allsel" content=" " /> |
| | | <roundcube:button command="expand-unread" type="link" title="expand-unread" class="buttonPas expand-unread" classAct="button expand-unread" classSel="button expand-unreadsel" content=" " /> |
| | | <roundcube:button command="collapse-all" type="link" title="collapse-all" class="buttonPas collapse-all" classAct="button collapse-all" classSel="button collapse-allsel" content=" " /> |
| | | <roundcube:container name="listcontrols" id="listcontrols" /> |
| | | <roundcube:if condition="env:quota" /> |
| | | <span style="margin-left: 20px; margin-right: 5px"><roundcube:label name="quota" />:</span> |
| | |
| | | <roundcube:button name="markreadbutton" id="markreadbutton" type="link" class="button markmessage" title="markmessages" onclick="rcmail_ui.show_markmenu();return false" content=" " /> |
| | | <roundcube:button name="messagemenulink" id="messagemenulink" type="link" class="button messagemenu" title="messageactions" onclick="rcmail_ui.show_messagemenu();return false" content=" " /> |
| | | |
| | | <div id="markmessagemenu"> |
| | | <div id="markmessagemenu" class="popupmenu"> |
| | | <ul class="toolbarmenu"> |
| | | <li><roundcube:button command="mark" prop="read" label="markread" classAct="readlink active" class="readlink" /></li> |
| | | <li><roundcube:button command="mark" prop="unread" label="markunread" classAct="unreadlink active" class="unreadlink" /></li> |
| | |
| | | |
| | | </div> |
| | | |
| | | <div id="searchmenu"> |
| | | <div id="searchmenu" class="popupmenu"> |
| | | <ul class="toolbarmenu"> |
| | | <li><input type="checkbox" name="s_mods[]" value="subject" id="s_mod_subject" onclick="rcmail_ui.set_searchmod(this)" /><label for="s_mod_subject"><roundcube:label name="subject" /></label></li> |
| | | <li><input type="checkbox" name="s_mods[]" value="from" id="s_mod_from" onclick="rcmail_ui.set_searchmod(this)" /><label for="s_mod_from"><roundcube:label name="from" /></label></li> |
| | |
| | | <roundcube:button command="reset-search" id="searchreset" image="/images/icons/reset.gif" title="resetsearch" /> |
| | | </div> |
| | | |
| | | <div id="listmenu" class="popupmenu"> |
| | | <fieldset class="thinbordered"><legend><roundcube:label name="listmode" /></legend> |
| | | <ul class="toolbarmenu"> |
| | | <li><input type="radio" name="view" value="list" id="view_default" /><label for="view_default"><roundcube:label name="list" /></label></li> |
| | | <roundcube:if condition="env:threads" /> |
| | | <li><input type="radio" name="view" value="thread" id="view_thread" /><label for="view_thread"><roundcube:label name="threads" /></label></li> |
| | | <roundcube:else /> |
| | | <li><input type="radio" name="view" value="thread" id="view_thread" disabled="disabled" /><label for="view_thread" class="disabled"><roundcube:label name="threads" /></label></li> |
| | | <roundcube:endif /> |
| | | </ul> |
| | | </fieldset> |
| | | <fieldset class="thinbordered"><legend><roundcube:label name="listcolumns" /></legend> |
| | | <ul class="toolbarmenu"> |
| | | <li><input type="checkbox" name="list_col[]" value="flag" id="cols_flag" /><label for="cols_flag"><roundcube:label name="flag" /></label></li> |
| | | <li><input type="checkbox" name="list_col[]" value="subject" id="cols_subject" checked="checked" disabled="disabled" /><label for="cols_subject" class="disabled"><roundcube:label name="subject" /></label></li> |
| | | <li><input type="checkbox" name="list_col[]" value="from" id="cols_fromto" /><label for="cols_fromto"><roundcube:label name="fromto" /></label></li> |
| | | <li><input type="checkbox" name="list_col[]" value="replyto" id="cols_replyto" /><label for="cols_replyto"><roundcube:label name="replyto" /></label></li> |
| | | <li><input type="checkbox" name="list_col[]" value="cc" id="cols_cc" /><label for="cols_cc"><roundcube:label name="cc" /></label></li> |
| | | <li><input type="checkbox" name="list_col[]" value="date" id="cols_date" /><label for="cols_date"><roundcube:label name="date" /></label></li> |
| | | <li><input type="checkbox" name="list_col[]" value="size" id="cols_size" /><label for="cols_size"><roundcube:label name="size" /></label></li> |
| | | <li><input type="checkbox" name="list_col[]" value="attachment" id="cols_attachment" /><label for="cols_attachment"><roundcube:label name="attachment" /></label></li> |
| | | </ul> |
| | | </fieldset> |
| | | <fieldset class="thinbordered"><legend><roundcube:label name="listsorting" /></legend> |
| | | <ul class="toolbarmenu"> |
| | | <li><input type="radio" name="sort_col" value="" id="sort_default" /><label for="sort_default"><roundcube:label name="nonesort" /></label></li> |
| | | <li><input type="radio" name="sort_col" value="arrival" id="sort_arrival" /><label for="sort_arrival"><roundcube:label name="arrival" /></label></li> |
| | | <li><input type="radio" name="sort_col" value="date" id="sort_date" /><label for="sort_date"><roundcube:label name="sentdate" /></label></li> |
| | | <li><input type="radio" name="sort_col" value="subject" id="sort_subject" /><label for="sort_subject"><roundcube:label name="subject" /></label></li> |
| | | <li><input type="radio" name="sort_col" value="from" id="sort_fromto" /><label for="sort_fromto"><roundcube:label name="fromto" /></label></li> |
| | | <li><input type="radio" name="sort_col" value="to" id="sort_replyto" /><label for="sort_replyto"><roundcube:label name="replyto" /></label></li> |
| | | <li><input type="radio" name="sort_col" value="cc" id="sort_cc" /><label for="sort_cc"><roundcube:label name="cc" /></label></li> |
| | | <li><input type="radio" name="sort_col" value="size" id="sort_size" /><label for="sort_size"><roundcube:label name="size" /></label></li> |
| | | </ul> |
| | | </fieldset> |
| | | <fieldset><legend><roundcube:label name="listorder" /></legend> |
| | | <ul class="toolbarmenu"> |
| | | <li><input type="radio" name="sort_ord" value="ASC" id="sort_asc" /><label for="sort_asc"><roundcube:label name="asc" /></label></li> |
| | | <li><input type="radio" name="sort_ord" value="DESC" id="sort_desc" /><label for="sort_desc"><roundcube:label name="desc" /></label></li> |
| | | </ul> |
| | | </fieldset> |
| | | <div> |
| | | <roundcube:button command="menu-open" id="listmenucancel" type="input" class="button" label="cancel" /> |
| | | <roundcube:button command="menu-save" id="listmenusave" type="input" class="button mainaction" label="save" /> |
| | | </div> |
| | | </div> |
| | | |
| | | </body> |
| | | </html> |