Changeset 3367 in subversion
- Timestamp:
- Mar 17, 2010 8:24:09 AM (3 years ago)
- Location:
- trunk/roundcubemail
- Files:
-
- 38 edited
- 3 copied
-
. (modified) (1 prop)
-
CHANGELOG (modified) (1 diff)
-
THREADS (copied) (copied from branches/devel-threads/THREADS) (2 diffs)
-
bin/msgexport.sh (modified) (1 diff)
-
config/main.inc.php.dist (modified) (2 diffs)
-
index.php (modified) (1 diff)
-
program/include/html.php (modified) (2 diffs)
-
program/include/rcmail.php (modified) (1 diff)
-
program/include/rcube_imap.php (modified) (35 diffs)
-
program/include/rcube_shared.inc (modified) (1 diff)
-
program/include/rcube_user.php (modified) (3 diffs)
-
program/js/app.js (modified) (49 diffs)
-
program/js/common.js (modified) (1 diff)
-
program/js/list.js (modified) (12 diffs)
-
program/lib/imap.inc (modified) (8 diffs)
-
program/localization/de_CH/labels.inc (modified) (1 diff)
-
program/localization/en_US/labels.inc (modified) (3 diffs)
-
program/localization/pl_PL/labels.inc (modified) (1 diff)
-
program/steps/mail/check_recent.inc (modified) (3 diffs)
-
program/steps/mail/func.inc (modified) (16 diffs)
-
program/steps/mail/list.inc (modified) (4 diffs)
-
program/steps/mail/mark.inc (modified) (3 diffs)
-
program/steps/mail/move_del.inc (modified) (4 diffs)
-
program/steps/mail/search.inc (modified) (2 diffs)
-
program/steps/mail/show.inc (modified) (2 diffs)
-
program/steps/mail/viewsource.inc (modified) (1 diff)
-
program/steps/settings/func.inc (modified) (2 diffs)
-
program/steps/settings/manage_folders.inc (modified) (10 diffs)
-
program/steps/settings/save_prefs.inc (modified) (2 diffs)
-
skins/default/common.css (modified) (4 diffs)
-
skins/default/functions.js (modified) (4 diffs)
-
skins/default/ie6hacks.css (modified) (2 diffs)
-
skins/default/iehacks.css (modified) (4 diffs)
-
skins/default/images/icons/columnpicker.gif (copied) (copied from branches/devel-threads/skins/default/images/icons/columnpicker.gif)
-
skins/default/images/icons/unread_children.png (copied) (copied from branches/devel-threads/skins/default/images/icons/unread_children.png)
-
skins/default/images/mail_footer.png (modified) (previous)
-
skins/default/images/messageactions.gif (modified) (previous)
-
skins/default/images/messageactions.png (modified) (previous)
-
skins/default/includes/messagemenu.html (modified) (1 diff)
-
skins/default/mail.css (modified) (9 diffs)
-
skins/default/templates/mail.html (modified) (5 diffs)
Legend:
- Unmodified
- Added
- Removed
-
trunk/roundcubemail
-
Property
svn:mergeinfo
set to
False
/branches/devel-threads merged eligible
-
Property
svn:mergeinfo
set to
False
-
trunk/roundcubemail/CHANGELOG
r3359 r3367 2 2 =========================== 3 3 4 - Threaded message listing now available 5 - Added sorting by ARRIVAL and CC 6 - Message list columns configurable by the user 7 - Removed 'index_sort' option, now we're using empty 'message_sort_col' for this 4 8 - virtuser_query: support other identity data (#1486148) 5 9 - Options virtuser_* replaced with virtuser_* plugins -
trunk/roundcubemail/THREADS
r3364 r3367 12 12 - on deleting messages the whole list isn't refreshed 13 13 - added 'expand unread' button 14 15 CHANGES IN RELATION TO TRUNK (for pasting into CHANGELOG after merge)16 - removed 'index_sort' option, now we're using empty 'message_sort_col' for17 this purpose18 - popup menu for messages list column/sorting/view selection19 - added sorting by ARRIVAL20 - added sorting by CC21 14 22 15 TODO (must have): … … 45 38 KNOWN ISSUES: 46 39 - on new message (check_recent) the whole list is reloaded 47 -table header replacement doesn't work on IE40 + table header replacement doesn't work on IE 48 41 - css issues on IE6 49 42 + css issues on IE7 -
trunk/roundcubemail/bin/msgexport.sh
r2229 r3367 90 90 91 91 fwrite($out, sprintf("From %s %s UID %d\n", $from['mailto'], $headers->date, $headers->uid)); 92 fwrite($out, iil_C_FetchPartHeader($IMAP->conn, $ IMAP->mailbox, $i, null));93 fwrite($out, iil_C_HandlePartBody($IMAP->conn, $ IMAP->mailbox, $i, null, 1));92 fwrite($out, iil_C_FetchPartHeader($IMAP->conn, $mbox, $i, null)); 93 fwrite($out, iil_C_HandlePartBody($IMAP->conn, $mbox, $i, null, 1)); 94 94 fwrite($out, "\n\n\n"); 95 95 -
trunk/roundcubemail/config/main.inc.php.dist
r3352 r3367 233 233 // ---------------------------------- 234 234 235 // default sort col 236 $rcmail_config['message_sort_col'] = 'date'; 237 238 // default sort order 235 // default messages sort column. Use empty value for default server's sorting, 236 // or 'arrival', 'date', 'subject', 'from', 'to', 'size', 'cc' 237 $rcmail_config['message_sort_col'] = ''; 238 239 // default messages sort order 239 240 $rcmail_config['message_sort_order'] = 'DESC'; 240 241 … … 462 463 $rcmail_config['display_next'] = false; 463 464 464 // If true, messages list will be sorted by message index instead of message date 465 $rcmail_config['index_sort'] = true; 465 // 0 - Do not expand threads 466 // 1 - Expand all threads automatically 467 // 2 - Expand only threads with unread messages 468 $rcmail_config['autoexpand_threads'] = 0; 466 469 467 470 // When replying place cursor above original message (top posting) -
trunk/roundcubemail/index.php
r3296 r3367 227 227 'subscribe' => 'manage_folders.inc', 228 228 'unsubscribe' => 'manage_folders.inc', 229 'enable-threading' => 'manage_folders.inc', 230 'disable-threading' => 'manage_folders.inc', 229 231 'add-identity' => 'edit_identity.inc', 230 232 ) -
trunk/roundcubemail/program/include/html.php
r3212 r3367 6 6 | | 7 7 | This file is part of the RoundCube Webmail client | 8 | Copyright (C) 2005-20 09, RoundCube Dev, - Switzerland |8 | Copyright (C) 2005-2010, RoundCube Dev, - Switzerland | 9 9 | Licensed under the GNU GPL | 10 10 | | … … 35 35 public static $lc_tags = true; 36 36 public static $common_attrib = array('id','class','style','title','align'); 37 public static $containers = array('iframe','div','span','p','h1','h2','h3', 38 'form','textarea','table','tr','th','td','style','script'); 37 public static $containers = array('iframe','div','span','p','h1','h2','h3','form','textarea','table','thead','tbody','tr','th','td','style','script'); 39 38 40 39 /** -
trunk/roundcubemail/program/include/rcmail.php
r3302 r3367 411 411 $this->imap->debug_level = $this->config->get('debug_level'); 412 412 $this->imap->skip_deleted = $this->config->get('skip_deleted'); 413 $this->imap->index_sort = $this->config->get('index_sort', true);414 413 415 414 // enable caching of imap data -
trunk/roundcubemail/program/include/rcube_imap.php
r3358 r3367 6 6 | | 7 7 | This file is part of the RoundCube Webmail client | 8 | Copyright (C) 2005-20 09, RoundCube Dev. - Switzerland |8 | Copyright (C) 2005-2010, RoundCube Dev. - Switzerland | 9 9 | Licensed under the GNU GPL | 10 10 | | … … 42 42 class rcube_imap 43 43 { 44 var $db; 45 var $conn; 46 var $root_dir = ''; 47 var $mailbox = 'INBOX'; 48 var $list_page = 1; 49 var $page_size = 10; 50 var $sort_field = 'date'; 51 var $sort_order = 'DESC'; 52 var $index_sort = true; 53 var $delimiter = NULL; 54 var $caching_enabled = FALSE; 55 var $default_charset = 'ISO-8859-1'; 56 var $struct_charset = NULL; 57 var $default_folders = array('INBOX'); 58 var $fetch_add_headers = ''; 59 var $cache = array(); 60 var $cache_keys = array(); 61 var $cache_changes = array(); 62 var $uid_id_map = array(); 63 var $msg_headers = array(); 64 var $skip_deleted = FALSE; 65 var $search_set = NULL; 66 var $search_string = ''; 67 var $search_charset = ''; 68 var $search_sort_field = ''; 69 var $debug_level = 1; 70 var $error_code = 0; 71 var $options = array('auth_method' => 'check'); 72 44 public $debug_level = 1; 45 public $error_code = 0; 46 public $skip_deleted = false; 47 public $root_dir = ''; 48 public $page_size = 10; 49 public $list_page = 1; 50 public $delimiter = NULL; 51 public $threading = false; 52 public $fetch_add_headers = ''; 53 public $conn; 54 55 private $db; 56 private $root_ns = ''; 57 private $mailbox = 'INBOX'; 58 private $sort_field = ''; 59 private $sort_order = 'DESC'; 60 private $caching_enabled = false; 61 private $default_charset = 'ISO-8859-1'; 62 private $struct_charset = NULL; 63 private $default_folders = array('INBOX'); 64 private $default_folders_lc = array('inbox'); 65 private $icache = array(); 66 private $cache = array(); 67 private $cache_keys = array(); 68 private $cache_changes = array(); 69 private $uid_id_map = array(); 70 private $msg_headers = array(); 71 public $search_set = NULL; 72 public $search_string = ''; 73 private $search_charset = ''; 74 private $search_sort_field = ''; 75 private $search_threads = false; 76 private $db_header_fields = array('idx', 'uid', 'subject', 'from', 'to', 'cc', 'date', 'size'); 77 private $options = array('auth_method' => 'check'); 73 78 private $host, $user, $pass, $port, $ssl; 74 79 … … 300 305 * @param string Sorting field 301 306 */ 302 function set_search_set($str=null, $msgs=null, $charset=null, $sort_field=null )307 function set_search_set($str=null, $msgs=null, $charset=null, $sort_field=null, $threads=false) 303 308 { 304 309 if (is_array($str) && $msgs == null) 305 list($str, $msgs, $charset, $sort_field ) = $str;310 list($str, $msgs, $charset, $sort_field, $threads) = $str; 306 311 if ($msgs != null && !is_array($msgs)) 307 312 $msgs = explode(',', $msgs); 308 313 309 314 $this->search_string = $str; 310 315 $this->search_set = $msgs; 311 316 $this->search_charset = $charset; 312 317 $this->search_sort_field = $sort_field; 318 $this->search_threads = $threads; 313 319 } 314 320 … … 320 326 function get_search_set() 321 327 { 322 return array($this->search_string, $this->search_set, $this->search_charset, $this->search_sort_field); 328 return array($this->search_string, 329 $this->search_set, 330 $this->search_charset, 331 $this->search_sort_field, 332 $this->search_threads, 333 ); 323 334 } 324 335 … … 346 357 { 347 358 return iil_C_GetCapability($this->conn, strtoupper($cap)); 359 } 360 361 362 /** 363 * Sets threading flag to the best supported THREAD algorithm 364 * 365 * @param boolean TRUE to enable and FALSE 366 * @return string Algorithm or false if THREAD is not supported 367 * @access public 368 */ 369 function set_threading($enable=false) 370 { 371 $this->threading = false; 372 373 if ($enable) { 374 if ($this->get_capability('THREAD=REFS')) 375 $this->threading = 'REFS'; 376 else if ($this->get_capability('THREAD=REFERENCES')) 377 $this->threading = 'REFERENCES'; 378 else if ($this->get_capability('THREAD=ORDEREDSUBJECT')) 379 $this->threading = 'ORDEREDSUBJECT'; 380 } 381 382 return $this->threading; 348 383 } 349 384 … … 481 516 if (empty($mailbox)) 482 517 $mailbox = $this->mailbox; 483 518 484 519 // count search set 485 if ($this->search_string && $mailbox == $this->mailbox && $mode == 'ALL' && !$force) 486 return count((array)$this->search_set); 487 520 if ($this->search_string && $mailbox == $this->mailbox && ($mode == 'ALL' || $mode == 'THREADS') && !$force) { 521 if ($this->search_threads) 522 return $mode == 'ALL' ? count((array)$this->search_set['depth']) : count((array)$this->search_set['tree']); 523 else 524 return count((array)$this->search_set); 525 } 526 488 527 $a_mailbox_cache = $this->get_cache('messagecount'); 489 528 … … 495 534 $a_mailbox_cache[$mailbox] = array(); 496 535 536 if ($mode == 'THREADS') 537 $count = $this->_threadcount($mailbox); 538 497 539 // RECENT count is fetched a bit different 498 if ($mode == 'RECENT')540 else if ($mode == 'RECENT') 499 541 $count = iil_C_CheckForRecent($this->conn, $mailbox); 500 542 … … 531 573 532 574 return (int)$count; 575 } 576 577 578 /** 579 * Private method for getting nr of threads 580 * 581 * @access private 582 * @see rcube_imap::messagecount() 583 */ 584 private function _threadcount($mailbox) 585 { 586 if (!empty($this->icache['threads'])) 587 return count($this->icache['threads']['tree']); 588 589 list ($thread_tree, $msg_depth, $has_children) = $this->_fetch_threads($mailbox); 590 591 // $this->update_thread_cache($mailbox, $thread_tree, $msg_depth, $has_children); 592 return count($thread_tree); 533 593 } 534 594 … … 568 628 return $this->_list_header_set($mailbox, $page, $sort_field, $sort_order, $slice); 569 629 630 if ($this->threading) 631 return $this->_list_thread_headers($mailbox, $page, $sort_field, $sort_order, $recursive, $slice); 632 570 633 $this->_set_sort_order($sort_field, $sort_order); 571 634 … … 594 657 $a_msg_headers = array(); 595 658 596 // use message index sort for sorting by Date(for better performance)597 if ( $this->index_sort && $this->sort_field == 'date')659 // use message index sort as default sorting (for better performance) 660 if (!$this->sort_field) 598 661 { 599 662 if ($this->skip_deleted) { … … 672 735 673 736 /** 737 * Private method for listing message headers using threads 738 * 739 * @access private 740 * @see rcube_imap::list_headers 741 */ 742 private function _list_thread_headers($mailbox, $page=NULL, $sort_field=NULL, $sort_order=NULL, $recursive=FALSE, $slice=0) 743 { 744 $this->_set_sort_order($sort_field, $sort_order); 745 746 $page = $page ? $page : $this->list_page; 747 // $cache_key = $mailbox.'.msg'; 748 // $cache_status = $this->check_cache_status($mailbox, $cache_key); 749 750 // get all threads (default sort order) 751 list ($thread_tree, $msg_depth, $has_children) = $this->_fetch_threads($mailbox); 752 753 if (empty($thread_tree)) 754 return array(); 755 756 $msg_index = $this->_sort_threads($mailbox, $thread_tree); 757 758 return $this->_fetch_thread_headers($mailbox, $thread_tree, $msg_depth, $has_children, 759 $msg_index, $page, $slice); 760 } 761 762 763 /** 764 * Private method for fetching threads data 765 * 766 * @param string Mailbox/folder name 767 * @return array Array with thread data 768 * @access private 769 */ 770 private function _fetch_threads($mailbox) 771 { 772 if (empty($this->icache['threads'])) { 773 // get all threads 774 list ($thread_tree, $msg_depth, $has_children) = iil_C_Thread($this->conn, 775 $mailbox, $this->threading, $this->skip_deleted ? 'UNDELETED' : ''); 776 777 // add to internal (fast) cache 778 $this->icache['threads'] = array(); 779 $this->icache['threads']['tree'] = $thread_tree; 780 $this->icache['threads']['depth'] = $msg_depth; 781 $this->icache['threads']['has_children'] = $has_children; 782 } 783 784 return array( 785 $this->icache['threads']['tree'], 786 $this->icache['threads']['depth'], 787 $this->icache['threads']['has_children'], 788 ); 789 } 790 791 792 /** 793 * Private method for fetching threaded messages headers 794 * 795 * @access private 796 */ 797 private function _fetch_thread_headers($mailbox, $thread_tree, $msg_depth, $has_children, $msg_index, $page, $slice=0) 798 { 799 $cache_key = $mailbox.'.msg'; 800 // now get IDs for current page 801 list($begin, $end) = $this->_get_message_range(count($msg_index), $page); 802 $msg_index = array_slice($msg_index, $begin, $end-$begin); 803 804 if ($slice) 805 $msg_index = array_slice($msg_index, ($this->sort_order == 'DESC' ? 0 : -$slice), $slice); 806 807 if ($this->sort_order == 'DESC') 808 $msg_index = array_reverse($msg_index); 809 810 // flatten threads array 811 // @TODO: fetch children only in expanded mode 812 $all_ids = array(); 813 foreach($msg_index as $root) { 814 $all_ids[] = $root; 815 if (!empty($thread_tree[$root])) 816 $all_ids = array_merge($all_ids, array_keys_recursive($thread_tree[$root])); 817 } 818 819 // fetch reqested headers from server 820 $this->_fetch_headers($mailbox, $all_ids, $a_msg_headers, $cache_key); 821 822 // return empty array if no messages found 823 if (!is_array($a_msg_headers) || empty($a_msg_headers)) 824 return array(); 825 826 // use this class for message sorting 827 $sorter = new rcube_header_sorter(); 828 $sorter->set_sequence_numbers($all_ids); 829 $sorter->sort_headers($a_msg_headers); 830 831 // Set depth, has_children and unread_children fields in headers 832 $this->_set_thread_flags($a_msg_headers, $msg_depth, $has_children); 833 834 return array_values($a_msg_headers); 835 } 836 837 838 /** 839 * Private method for setting threaded messages flags: 840 * depth, has_children and unread_children 841 * 842 * @param array Reference to headers array indexed by message ID 843 * @param array Array of messages depth indexed by message ID 844 * @param array Array of messages children flags indexed by message ID 845 * @return array Message headers array indexed by message ID 846 * @access private 847 */ 848 private function _set_thread_flags(&$headers, $msg_depth, $msg_children) 849 { 850 $parents = array(); 851 852 foreach ($headers as $idx => $header) { 853 $id = $header->id; 854 $depth = $msg_depth[$id]; 855 $parents = array_slice($parents, 0, $depth); 856 857 if (!empty($parents)) { 858 $headers[$idx]->parent_uid = end($parents); 859 if (!$header->seen) 860 $headers[$parents[0]]->unread_children++; 861 } 862 array_push($parents, $header->uid); 863 864 $headers[$idx]->depth = $depth; 865 $headers[$idx]->has_children = $msg_children[$id]; 866 } 867 } 868 869 870 /** 674 871 * Private method for listing a set of message headers (search results) 675 872 * … … 688 885 return array(); 689 886 887 // use saved messages from searching 888 if ($this->threading) 889 return $this->_list_thread_header_set($mailbox, $page, $sort_field, $sort_order, $slice); 890 891 // search set is threaded, we need a new one 892 if ($this->search_threads) 893 $this->search('', $this->search_string, $this->search_charset, $sort_field); 894 690 895 $msgs = $this->search_set; 691 896 $a_msg_headers = array(); … … 695 900 $this->_set_sort_order($sort_field, $sort_order); 696 901 697 // quickest method 698 if ( $this->index_sort && $this->search_sort_field == 'date' && $this->sort_field == 'date')902 // quickest method (default sorting) 903 if (!$this->search_sort_field && !$this->sort_field) 699 904 { 700 905 if ($sort_order == 'DESC') … … 717 922 return array_values($a_msg_headers); 718 923 } 924 719 925 // sorted messages, so we can first slice array and then fetch only wanted headers 720 if ($this->get_capability('sort') && (!$this->index_sort || $this->sort_field != 'date')) // SORT searching result926 if ($this->get_capability('sort')) // SORT searching result 721 927 { 722 928 // reset search set if sorting field has been changed … … 746 952 return array_values($a_msg_headers); 747 953 } 748 else { // SEARCH searchingresult, need sorting954 else { // SEARCH result, need sorting 749 955 $cnt = count($msgs); 750 956 // 300: experimantal value for best result 751 if (($cnt > 300 && $cnt > $this->page_size) || ($this->index_sort && $this->sort_field == 'date')) {957 if (($cnt > 300 && $cnt > $this->page_size) || !$this->sort_field) { 752 958 // use memory less expensive (and quick) method for big result set 753 959 $a_index = $this->message_index('', $this->sort_field, $this->sort_order); … … 788 994 } 789 995 } 996 } 997 998 999 /** 1000 * Private method for listing a set of threaded message headers (search results) 1001 * 1002 * @param string Mailbox/folder name 1003 * @param int Current page to list 1004 * @param string Header field to sort by 1005 * @param string Sort order [ASC|DESC] 1006 * @param boolean Number of slice items to extract from result array 1007 * @return array Indexed array with message header objects 1008 * @access private 1009 * @see rcube_imap::list_header_set() 1010 */ 1011 private function _list_thread_header_set($mailbox, $page=NULL, $sort_field=NULL, $sort_order=NULL, $slice=0) 1012 { 1013 // update search_set if previous data was fetched with disabled threading 1014 if (!$this->search_threads) 1015 $this->search('', $this->search_string, $this->search_charset, $sort_field); 1016 1017 $thread_tree = $this->search_set['tree']; 1018 $msg_depth = $this->search_set['depth']; 1019 $has_children = $this->search_set['children']; 1020 $a_msg_headers = array(); 1021 1022 $page = $page ? $page : $this->list_page; 1023 $start_msg = ($page-1) * $this->page_size; 1024 1025 $this->_set_sort_order($sort_field, $sort_order); 1026 1027 $msg_index = $this->_sort_threads($mailbox, $thread_tree, array_keys($msg_depth)); 1028 1029 return $this->_fetch_thread_headers($mailbox, $thread_tree, $msg_depth, $has_children, $msg_index, $page, $slice=0); 790 1030 } 791 1031 … … 902 1142 function message_index($mbox_name='', $sort_field=NULL, $sort_order=NULL) 903 1143 { 1144 if ($this->threading) 1145 return $this->thread_index($mbox_name, $sort_field, $sort_order); 1146 904 1147 $this->_set_sort_order($sort_field, $sort_order); 905 1148 … … 908 1151 909 1152 // we have a saved search result, get index from there 910 if (!isset($this->cache[$key]) && $this->search_string && $mailbox == $this->mailbox) 1153 if (!isset($this->cache[$key]) && $this->search_string 1154 && !$this->search_threads && $mailbox == $this->mailbox) 911 1155 { 912 1156 $this->cache[$key] = array(); 913 1157 914 // use message index sort for sorting by Date915 if ( $this->index_sort && $this->sort_field == 'date')1158 // use message index sort as default sorting 1159 if (!$this->sort_field) 916 1160 { 917 1161 $msgs = $this->search_set; … … 938 1182 else 939 1183 { 940 $a_index = iil_C_FetchHeaderIndex($this->conn, $mailbox, join(',', $this->search_set), $this->sort_field, $this->skip_deleted); 1184 $a_index = iil_C_FetchHeaderIndex($this->conn, $mailbox, 1185 join(',', $this->search_set), $this->sort_field, $this->skip_deleted); 941 1186 942 1187 if ($this->sort_order=="ASC") … … 964 1209 } 965 1210 966 // use message index sort for sorting by Date967 if ( $this->index_sort && $this->sort_field == 'date')1211 // use message index sort as default sorting 1212 if (!$this->sort_field) 968 1213 { 969 1214 if ($this->skip_deleted) { … … 1003 1248 1004 1249 /** 1250 * Return sorted array of threaded message IDs (not UIDs) 1251 * 1252 * @param string Mailbox to get index from 1253 * @param string Sort column 1254 * @param string Sort order [ASC, DESC] 1255 * @return array Indexed array with message IDs 1256 */ 1257 function thread_index($mbox_name='', $sort_field=NULL, $sort_order=NULL) 1258 { 1259 $this->_set_sort_order($sort_field, $sort_order); 1260 1261 $mailbox = $mbox_name ? $this->mod_mailbox($mbox_name) : $this->mailbox; 1262 $key = "{$mailbox}:{$this->sort_field}:{$this->sort_order}:{$this->search_string}.thi"; 1263 1264 // we have a saved search result, get index from there 1265 if (!isset($this->cache[$key]) && $this->search_string 1266 && $this->search_threads && $mailbox == $this->mailbox) 1267 { 1268 // use message IDs for better performance 1269 $ids = array_keys_recursive($this->search_set['tree']); 1270 $this->cache[$key] = $this->_flatten_threads($mailbox, $this->search_set['tree'], $ids); 1271 } 1272 1273 // have stored it in RAM 1274 if (isset($this->cache[$key])) 1275 return $this->cache[$key]; 1276 /* 1277 // check local cache 1278 $cache_key = $mailbox.'.msg'; 1279 $cache_status = $this->check_cache_status($mailbox, $cache_key); 1280 1281 // cache is OK 1282 if ($cache_status>0) 1283 { 1284 $a_index = $this->get_message_cache_index($cache_key, TRUE, $this->sort_field, $this->sort_order); 1285 return array_keys($a_index); 1286 } 1287 */ 1288 // get all threads (default sort order) 1289 list ($thread_tree) = $this->_fetch_threads($mailbox); 1290 1291 $this->cache[$key] = $this->_flatten_threads($mailbox, $thread_tree); 1292 1293 return $this->cache[$key]; 1294 } 1295 1296 1297 /** 1298 * Return array of threaded messages (all, not only roots) 1299 * 1300 * @param string Mailbox to get index from 1301 * @param array Threaded messages array (see _fetch_threads()) 1302 * @param array Message IDs if we know what we need (e.g. search result) 1303 * for better performance 1304 * @return array Indexed array with message IDs 1305 * 1005 1306 * @access private 1006 1307 */ 1007 function sync_header_index($mailbox) 1308 private function _flatten_threads($mailbox, $thread_tree, $ids=null) 1309 { 1310 if (empty($thread_tree)) 1311 return array(); 1312 1313 $msg_index = $this->_sort_threads($mailbox, $thread_tree, $ids); 1314 1315 if ($this->sort_order == 'DESC') 1316 $msg_index = array_reverse($msg_index); 1317 1318 // flatten threads array 1319 $all_ids = array(); 1320 foreach($msg_index as $root) { 1321 $all_ids[] = $root; 1322 if (!empty($thread_tree[$root])) 1323 $all_ids = array_merge($all_ids, array_keys_recursive($thread_tree[$root])); 1324 } 1325 1326 return $all_ids; 1327 } 1328 1329 1330 /** 1331 * @access private 1332 */ 1333 private function sync_header_index($mailbox) 1008 1334 { 1009 1335 $cache_key = $mailbox.'.msg'; … … 1103 1429 } 1104 1430 1105 $this->set_search_set($str, $results, $charset, $sort_field );1431 $this->set_search_set($str, $results, $charset, $sort_field, (bool)$this->threading); 1106 1432 1107 1433 return $results; … … 1123 1449 $criteria = 'UNDELETED '.$criteria; 1124 1450 1125 if ($sort_field && $this->get_capability('sort') && (!$this->index_sort || $sort_field != 'date')) { 1451 if ($this->threading) { 1452 list ($thread_tree, $msg_depth, $has_children) = iil_C_Thread($this->conn, 1453 $mailbox, $this->threading, $criteria, $charset); 1454 1455 $a_messages = array( 1456 'tree' => $thread_tree, 1457 'depth' => $msg_depth, 1458 'children' => $has_children 1459 ); 1460 } 1461 else if ($sort_field && $this->get_capability('sort')) { 1126 1462 $charset = $charset ? $charset : $this->default_charset; 1127 1463 $a_messages = iil_C_Sort($this->conn, $mailbox, $sort_field, $criteria, FALSE, $charset); … … 1136 1472 1137 1473 // I didn't found that SEARCH always returns sorted IDs 1138 if ( $this->index_sort && $this->sort_field == 'date')1474 if (!$this->sort_field) 1139 1475 sort($a_messages); 1140 1476 } … … 1150 1486 1151 1487 /** 1488 * Sort thread 1489 * 1490 * @param string Mailbox name 1491 * @param array Unsorted thread tree (iil_C_Thread() result) 1492 * @param array Message IDs if we know what we need (e.g. search result) 1493 * @return array Sorted roots IDs 1494 * @access private 1495 */ 1496 private function _sort_threads($mailbox, $thread_tree, $ids=NULL) 1497 { 1498 // THREAD=ORDEREDSUBJECT: sorting by sent date of root message 1499 // THREAD=REFERENCES: sorting by sent date of root message 1500 // THREAD=REFS: sorting by the most recent date in each thread 1501 // default sorting 1502 if (!$this->sort_field || ($this->sort_field == 'date' && $this->threading == 'REFS')) { 1503 return array_keys($thread_tree); 1504 } 1505 // here we'll implement REFS sorting, for performance reason 1506 else { // ($sort_field == 'date' && $this->threading != 'REFS') 1507 // use SORT command 1508 if ($this->get_capability('sort')) { 1509 $a_index = iil_C_Sort($this->conn, $mailbox, $this->sort_field, 1510 !empty($ids) ? $ids : ($this->skip_deleted ? 'UNDELETED' : '')); 1511 } 1512 else { 1513 // fetch specified headers for all messages and sort them 1514 $a_index = iil_C_FetchHeaderIndex($this->conn, $mailbox, !empty($ids) ? $ids : "1:*", 1515 $this->sort_field, $this->skip_deleted); 1516 asort($a_index); // ASC 1517 $a_index = array_values($a_index); 1518 } 1519 1520 return $this->_sort_thread_refs($thread_tree, $a_index); 1521 } 1522 } 1523 1524 1525 /** 1526 * THREAD=REFS sorting implementation 1527 * 1528 * @param array Thread tree array (message identifiers as keys) 1529 * @param array Array of sorted message identifiers 1530 * @return array Array of sorted roots messages 1531 * @access private 1532 */ 1533 private function _sort_thread_refs($tree, $index) 1534 { 1535 if (empty($tree)) 1536 return array(); 1537 1538 $index = array_combine(array_values($index), $index); 1539 1540 // assign roots 1541 foreach ($tree as $idx => $val) { 1542 $index[$idx] = $idx; 1543 if (!empty($val)) { 1544 $idx_arr = array_keys_recursive($tree[$idx]); 1545 foreach ($idx_arr as $subidx) 1546 $index[$subidx] = $idx; 1547 } 1548 } 1549 1550 $index = array_values($index); 1551 1552 // create sorted array of roots 1553 $msg_index = array(); 1554 if ($this->sort_order != 'DESC') { 1555 foreach ($index as $idx) 1556 if (!isset($msg_index[$idx])) 1557 $msg_index[$idx] = $idx; 1558 $msg_index = array_values($msg_index); 1559 } 1560 else { 1561 for ($x=count($index)-1; $x>=0; $x--) 1562 if (!isset($msg_index[$index[$x]])) 1563 $msg_index[$index[$x]] = $index[$x]; 1564 $msg_index = array_reverse($msg_index); 1565 } 1566 1567 return $msg_index; 1568 } 1569 1570 1571 /** 1152 1572 * Refresh saved search set 1153 1573 * … … 1157 1577 { 1158 1578 if (!empty($this->search_string)) 1159 $this->search_set = $this->search('', $this->search_string, $this->search_charset, $this->search_sort_field); 1579 $this->search_set = $this->search('', $this->search_string, $this->search_charset, 1580 $this->search_sort_field, $this->search_threads); 1160 1581 1161 1582 return $this->get_search_set(); … … 1761 2182 */ 1762 2183 function move_message($uids, $to_mbox, $from_mbox='') 1763 {2184 { 1764 2185 $fbox = $from_mbox; 1765 2186 $tbox = $to_mbox; … … 1803 2224 // moving failed 1804 2225 else if ($config->get('delete_always', false) && $tbox == $config->get('trash_mbox')) { 1805 return $this->delete_message($a_uids, $fbox); 1806 } 1807 1808 // remove message ids from search set 1809 if ($moved && $this->search_set && $from_mbox == $this->mailbox) { 1810 foreach ($a_uids as $uid) 1811 $a_mids[] = $this->_uid2id($uid, $from_mbox); 1812 $this->search_set = array_diff($this->search_set, $a_mids); 1813 } 1814 1815 // update cached message headers 1816 $cache_key = $from_mbox.'.msg'; 1817 if ($moved && $start_index = $this->get_message_cache_index_min($cache_key, $a_uids)) { 1818 // clear cache from the lowest index on 1819 $this->clear_message_cache($cache_key, $start_index); 1820 } 2226 $moved = $this->delete_message($a_uids, $fbox); 2227 } 2228 2229 if ($moved) { 2230 // unset threads internal cache 2231 unset($this->icache['threads']); 2232 2233 // remove message ids from search set 2234 if ($this->search_set && $from_mbox == $this->mailbox) { 2235 // threads are too complicated to just remove messages from set 2236 if ($this->search_threads) 2237 $this->refresh_search(); 2238 else { 2239 foreach ($a_uids as $uid) 2240 $a_mids[] = $this->_uid2id($uid, $from_mbox); 2241 $this->search_set = array_diff($this->search_set, $a_mids); 2242 } 2243 } 2244 2245 // update cached message headers 2246 $cache_key = $from_mbox.'.msg'; 2247 if ($start_index = $this->get_message_cache_index_min($cache_key, $a_uids)) { 2248 // clear cache from the lowest index on 2249 $this->clear_message_cache($cache_key, $start_index); 2250 } 2251 } 1821 2252 1822 2253 return $moved; 1823 }2254 } 1824 2255 1825 2256 … … 1832 2263 */ 1833 2264 function delete_message($uids, $mbox_name='') 1834 {2265 { 1835 2266 $mailbox = $mbox_name ? $this->mod_mailbox($mbox_name) : $this->mailbox; 1836 2267 … … 1844 2275 $deleted = iil_C_Delete($this->conn, $mailbox, join(',', $a_uids)); 1845 2276 1846 // send expunge command in order to have the deleted message 1847 // really deleted from the mailbox 1848 if ($deleted) 1849 { 2277 if ($deleted) { 2278 // send expunge command in order to have the deleted message 2279 // really deleted from the mailbox 1850 2280 $this->_expunge($mailbox, FALSE, $a_uids); 1851 2281 $this->_clear_messagecount($mailbox); 1852 2282 unset($this->uid_id_map[$mailbox]); 1853 } 1854 1855 // remove message ids from search set 1856 if ($deleted && $this->search_set && $mailbox == $this->mailbox) { 1857 foreach ($a_uids as $uid) 1858 $a_mids[] = $this->_uid2id($uid, $mailbox); 1859 $this->search_set = array_diff($this->search_set, $a_mids); 1860 } 1861 1862 // remove deleted messages from cache 1863 $cache_key = $mailbox.'.msg'; 1864 if ($deleted && $start_index = $this->get_message_cache_index_min($cache_key, $a_uids)) { 1865 // clear cache from the lowest index on 1866 $this->clear_message_cache($cache_key, $start_index); 1867 } 2283 2284 // unset threads internal cache 2285 unset($this->icache['threads']); 2286 2287 // remove message ids from search set 2288 if ($this->search_set && $mailbox == $this->mailbox) { 2289 // threads are too complicated to just remove messages from set 2290 if ($this->search_threads) 2291 $this->refresh_search(); 2292 else { 2293 foreach ($a_uids as $uid) 2294 $a_mids[] = $this->_uid2id($uid, $mailbox); 2295 $this->search_set = array_diff($this->search_set, $a_mids); 2296 } 2297 } 2298 2299 // remove deleted messages from cache 2300 $cache_key = $mailbox.'.msg'; 2301 if ($start_index = $this->get_message_cache_index_min($cache_key, $a_uids)) { 2302 // clear cache from the lowest index on 2303 $this->clear_message_cache($cache_key, $start_index); 2304 } 2305 } 1868 2306 1869 2307 return $deleted; 1870 }2308 } 1871 2309 1872 2310 … … 2423 2861 { 2424 2862 $cache_key = "$key:$from:$to:$sort_field:$sort_order"; 2425 $db_header_fields = array('idx', 'uid', 'subject', 'from', 'to', 'cc', 'date', 'size');2426 2863 2427 2864 $config = rcmail::get_instance()->config; 2428 2865 2429 // use idx sort for sorting by Date with index_sort=true or for unknown field 2430 if (($sort_field == 'date' && $this->index_sort) 2431 || !in_array($sort_field, $db_header_fields)) { 2866 // use idx sort as default sorting 2867 if (!$sort_field || !in_array($sort_field, $this->db_header_fields)) { 2432 2868 $sort_field = 'idx'; 2433 2869 } … … 2466 2902 private function &get_cached_message($key, $uid) 2467 2903 { 2468 $internal_key = ' __single_msg';2469 2470 if ($this->caching_enabled && !isset($this-> cache[$internal_key][$uid]))2904 $internal_key = 'message'; 2905 2906 if ($this->caching_enabled && !isset($this->icache[$internal_key][$uid])) 2471 2907 { 2472 2908 $sql_result = $this->db->query( … … 2483 2919 { 2484 2920 $this->uid_id_map[preg_replace('/\.msg$/', '', $key)][$uid] = $sql_arr['idx']; 2485 $this-> cache[$internal_key][$uid] = $this->db->decode(unserialize($sql_arr['headers']));2486 if (is_object($this-> cache[$internal_key][$uid]) && !empty($sql_arr['structure']))2487 $this-> cache[$internal_key][$uid]->structure = $this->db->decode(unserialize($sql_arr['structure']));2921 $this->icache[$internal_key][$uid] = $this->db->decode(unserialize($sql_arr['headers'])); 2922 if (is_object($this->icache[$internal_key][$uid]) && !empty($sql_arr['structure'])) 2923 $this->icache[$internal_key][$uid]->structure = $this->db->decode(unserialize($sql_arr['structure'])); 2488 2924 } 2489 2925 } 2490 2926 2491 return $this-> cache[$internal_key][$uid];2927 return $this->icache[$internal_key][$uid]; 2492 2928 } 2493 2929 … … 2506 2942 return $sa_message_index[$key]; 2507 2943 2508 // use idx sort for sorting by Date with index_sort=true2509 if ( $sort_field == 'date' && $this->index_sort)2944 // use idx sort as default 2945 if (!$sort_field || !in_array($sort_field, $this->db_header_fields)) 2510 2946 $sort_field = 'idx'; 2511 2947 … … 2535 2971 2536 2972 // add to internal (fast) cache 2537 $this-> cache['__single_msg'][$headers->uid] = clone $headers;2538 $this-> cache['__single_msg'][$headers->uid]->structure = $struct;2973 $this->icache['message'][$headers->uid] = clone $headers; 2974 $this->icache['message'][$headers->uid]->structure = $struct; 2539 2975 2540 2976 // no further caching -
trunk/roundcubemail/program/include/rcube_shared.inc
r3293 r3367 609 609 610 610 /** 611 * Get all keys from array (recursive) 612 * 613 * @param array Input array 614 * @return array 615 */ 616 function array_keys_recursive($array) 617 { 618 $keys = array(); 619 620 if (!empty($array)) 621 foreach ($array as $key => $child) { 622 $keys[] = $key; 623 if ($children = array_keys_recursive($child)) 624 $keys = array_merge($keys, $children); 625 } 626 return $keys; 627 } 628 629 630 /** 611 631 * mbstring replacement functions 612 632 */ -
trunk/roundcubemail/program/include/rcube_user.php
r3359 r3367 112 112 unset($save_prefs[$key]); 113 113 } 114 114 115 $save_prefs = serialize($save_prefs); 116 115 117 $this->db->query( 116 118 "UPDATE ".get_table_name('users')." … … 118 120 language=? 119 121 WHERE user_id=?", 120 serialize($save_prefs),122 $save_prefs, 121 123 $_SESSION['language'], 122 124 $this->ID); … … 125 127 if ($this->db->affected_rows()) { 126 128 $config->set_user_prefs($a_user_prefs); 129 $this->data['preferences'] = $save_prefs; 127 130 return true; 128 131 } -
trunk/roundcubemail/program/js/app.js
r3343 r3367 4 4 | | 5 5 | This file is part of the RoundCube Webmail client | 6 | Copyright (C) 2005-20 09, RoundCube Dev, - Switzerland |6 | Copyright (C) 2005-2010, RoundCube Dev, - Switzerland | 7 7 | Licensed under the GNU GPL | 8 8 | | 9 9 +-----------------------------------------------------------------------+ 10 10 | Authors: Thomas Bruederli <roundcube@gmail.com> | 11 | Aleksander 'A.L.E.C' Machniak <alec@alec.pl> | 11 12 | Charles McNulty <charles@charlesmcnulty.com> | 12 13 +-----------------------------------------------------------------------+ … … 161 162 { 162 163 case 'mail': 164 // enable mail commands 165 this.enable_command('list', 'checkmail', 'compose', 'add-contact', 'search', 'reset-search', 'collapse-folder', true); 166 163 167 if (this.gui_objects.messagelist) 164 168 { 165 this.message_list = new rcube_list_widget(this.gui_objects.messagelist, {multiselect:true, draggable:true, keyboard:true, dblclick_time:this.dblclick_time}); 169 this.message_list = new rcube_list_widget(this.gui_objects.messagelist, 170 {multiselect:true, multiexpand:true, draggable:true, keyboard:true, dblclick_time:this.dblclick_time}); 166 171 this.message_list.row_init = function(o){ p.init_message_row(o); }; 167 172 this.message_list.addEventListener('dblclick', function(o){ p.msglist_dbl_click(o); }); … … 171 176 this.message_list.addEventListener('dragmove', function(e){ p.drag_move(e); }); 172 177 this.message_list.addEventListener('dragend', function(e){ p.drag_end(e); }); 178 this.message_list.addEventListener('expandcollapse', function(e){ p.msglist_expand(e); }); 173 179 document.onmouseup = function(e){ return p.doc_mouse_up(e); }; 174 180 181 this.set_message_coltypes(this.env.coltypes); 175 182 this.message_list.init(); 176 this.enable_command('toggle_status', 'toggle_flag', true);183 this.enable_command('toggle_status', 'toggle_flag', 'menu-open', 'menu-save', true); 177 184 178 185 if (this.gui_objects.mailcontframe) … … 180 187 else 181 188 this.message_list.focus(); 189 190 // load messages 191 if (this.env.messagecount) 192 this.command('list'); 182 193 } 183 184 if (this.env.coltypes)185 this.set_message_coltypes(this.env.coltypes);186 187 // enable mail commands188 this.enable_command('list', 'checkmail', 'compose', 'add-contact', 'search', 'reset-search', 'collapse-folder', true);189 194 190 195 if (this.env.search_text != null && document.getElementById('quicksearchbox') != null) … … 244 249 } 245 250 246 if (this.env.messagecount) 251 if (this.env.messagecount) { 247 252 this.enable_command('select-all', 'select-none', 'expunge', true); 253 this.enable_command('expand-all', 'expand-unread', 'collapse-all', this.env.threading); 254 } 248 255 249 256 if (this.purge_mailbox_test()) … … 334 341 } 335 342 else if (this.env.action=='folders') 336 this.enable_command('subscribe', 'unsubscribe', 'create-folder', 'rename-folder', 'delete-folder', true);343 this.enable_command('subscribe', 'unsubscribe', 'create-folder', 'rename-folder', 'delete-folder', 'enable-threading', 'disable-threading', true); 337 344 338 345 if (this.gui_objects.identitieslist) … … 352 359 this.sections_list.init(); 353 360 this.sections_list.focus(); 361 this.sections_list.select_first(); 354 362 } 355 363 else if (this.gui_objects.subscriptionlist) … … 405 413 }; 406 414 407 // start interval for keep-alive/recent_check signal408 this.start_keepalive = function()409 {410 if (this.env.keep_alive && !this.env.framed && this.task=='mail' && this.gui_objects.mailboxlist)411 this._int = setInterval(function(){ ref.check_for_recent(false); }, this.env.keep_alive * 1000);412 else if (this.env.keep_alive && !this.env.framed && this.task!='login')413 this._int = setInterval(function(){ ref.send_keep_alive(); }, this.env.keep_alive * 1000);414 }415 416 this.init_message_row = function(row)417 {418 var uid = row.uid;419 if (uid && this.env.messages[uid])420 {421 row.deleted = this.env.messages[uid].deleted ? true : false;422 row.unread = this.env.messages[uid].unread ? true : false;423 row.replied = this.env.messages[uid].replied ? true : false;424 row.flagged = this.env.messages[uid].flagged ? true : false;425 row.forwarded = this.env.messages[uid].forwarded ? true : false;426 }427 428 // set eventhandler to message icon429 if (row.icon = row.obj.getElementsByTagName('td')[0].getElementsByTagName('img')[0])430 {431 var p = this;432 row.icon.id = 'msgicn_'+row.uid;433 row.icon._row = row.obj;434 row.icon.onmousedown = function(e) { p.command('toggle_status', this); };435 }436 437 // global variable 'flagged_col' may be not defined yet438 if (!this.env.flagged_col && this.env.coltypes)439 {440 var found;441 if((found = find_in_array('flag', this.env.coltypes)) >= 0)442 this.set_env('flagged_col', found+1);443 }444 445 // set eventhandler to flag icon, if icon found446 if (this.env.flagged_col && (row.flagged_icon = row.obj.getElementsByTagName('td')[this.env.flagged_col].getElementsByTagName('img')[0]))447 {448 var p = this;449 row.flagged_icon.id = 'flaggedicn_'+row.uid;450 row.flagged_icon._row = row.obj;451 row.flagged_icon.onmousedown = function(e) { p.command('toggle_flag', this); };452 }453 454 this.triggerEvent('insertrow', { uid:uid, row:row });455 };456 457 // init message compose form: set focus and eventhandlers458 this.init_messageform = function()459 {460 if (!this.gui_objects.messageform)461 return false;462 463 //this.messageform = this.gui_objects.messageform;464 var input_from = $("[name='_from']");465 var input_to = $("[name='_to']");466 var input_subject = $("input[name='_subject']");467 var input_message = $("[name='_message']").get(0);468 var html_mode = $("input[name='_is_html']").val() == '1';469 470 // init live search events471 this.init_address_input_events(input_to);472 this.init_address_input_events($("[name='_cc']"));473 this.init_address_input_events($("[name='_bcc']"));474 475 if (!html_mode)476 this.set_caret_pos(input_message, this.env.top_posting ? 0 : $(input_message).val().length);477 478 // add signature according to selected identity479 if (input_from.attr('type') == 'select-one' && $("input[name='_draft_saveid']").val() == ''480 && !html_mode) { // if we have HTML editor, signature is added in callback481 this.change_identity(input_from[0]);482 }483 else if (!html_mode)484 this.set_caret_pos(input_message, this.env.top_posting ? 0 : $(input_message).val().length);485 486 if (input_to.val() == '')487 input_to.focus();488 else if (input_subject.val() == '')489 input_subject.focus();490 else if (input_message && !html_mode)491 input_message.focus();492 493 // get summary of all field values494 this.compose_field_hash(true);495 496 // start the auto-save timer497 this.auto_save_start();498 };499 500 this.init_address_input_events = function(obj)501 {502 var handler = function(e){ return ref.ksearch_keypress(e,this); };503 obj.bind((bw.safari || bw.ie ? 'keydown' : 'keypress'), handler);504 obj.attr('autocomplete', 'off');505 };506 507 415 508 416 /*********************************************************/ … … 581 489 break; 582 490 491 case 'menu-open': 492 case 'menu-save': 493 this.triggerEvent(command, {props:props}); 494 return false; 495 break; 496 583 497 case 'open': 584 498 var uid; … … 626 540 sort_order = 'ASC'; 627 541 628 // set table header class 629 $('#rcm'+this.env.sort_col).removeClass('sorted'+(this.env.sort_order.toUpperCase())); 630 $('#rcm'+sort_col).addClass('sorted'+sort_order); 631 632 // save new sort properties 633 this.env.sort_col = sort_col; 634 this.env.sort_order = sort_order; 542 // set table header and update env 543 this.set_list_sorting(sort_col, sort_order); 635 544 636 545 // reload message list … … 757 666 break; 758 667 759 760 668 // mail task commands 761 669 case 'move': … … 850 758 case 'select-none': 851 759 this.message_list.clear_selection(); 760 break; 761 762 case 'expand-all': 763 this.env.autoexpand_threads = 1; 764 this.message_list.expand_all(); 765 break; 766 767 case 'expand-unread': 768 this.env.autoexpand_threads = 2; 769 this.message_list.collapse_all(); 770 this.expand_unread(); 771 break; 772 773 case 'collapse-all': 774 this.env.autoexpand_threads = 0; 775 this.message_list.collapse_all(); 852 776 break; 853 777 … … 1117 1041 this.unsubscribe_folder(props); 1118 1042 break; 1119 1043 1044 case 'enable-threading': 1045 this.enable_threading(props); 1046 break; 1047 1048 case 'disable-threading': 1049 this.disable_threading(props); 1050 break; 1051 1120 1052 case 'create-folder': 1121 1053 this.create_folder(props); … … 1451 1383 clearTimeout(this.preview_timer); 1452 1384 1453 var selected = list. selection.length==1;1385 var selected = list.get_single_selection() != null; 1454 1386 1455 1387 // Hide certain command buttons when Drafts folder is selected … … 1506 1438 }; 1507 1439 1440 this.msglist_expand = function(row) 1441 { 1442 if (this.env.messages[row.uid]) 1443 this.env.messages[row.uid].expanded = row.expanded; 1444 }; 1445 1508 1446 this.check_droptarget = function(id) 1509 1447 { … … 1521 1459 /*********************************************************/ 1522 1460 1461 this.init_message_row = function(row) 1462 { 1463 var self = this; 1464 var uid = row.uid; 1465 1466 if (uid && this.env.messages[uid]) 1467 $.extend(row, this.env.messages[uid]); 1468 1469 // set eventhandler to message icon 1470 if (this.env.subject_col != null && (row.icon = document.getElementById('msgicn'+row.uid))) { 1471 row.icon._row = row.obj; 1472 row.icon.onmousedown = function(e) { self.command('toggle_status', this); }; 1473 } 1474 1475 // set eventhandler to flag icon, if icon found 1476 if (this.env.flagged_col != null && (row.flagged_icon = document.getElementById('flaggedicn'+row.uid))) { 1477 row.flagged_icon._row = row.obj; 1478 row.flagged_icon.onmousedown = function(e) { self.command('toggle_flag', this); }; 1479 } 1480 1481 var expando; 1482 if (!row.depth && row.has_children && (expando = document.getElementById('rcmexpando'+row.uid))) { 1483 expando.onmousedown = function(e) { return self.expand_message_row(e, uid); }; 1484 } 1485 1486 this.triggerEvent('insertrow', { uid:uid, row:row }); 1487 }; 1488 1489 // create a table row in the message list 1490 this.add_message_row = function(uid, cols, flags, attop) 1491 { 1492 if (!this.gui_objects.messagelist || !this.message_list) 1493 return false; 1494 1495 if (this.message_list.background) 1496 var tbody = this.message_list.background; 1497 else 1498 var tbody = this.gui_objects.messagelist.tBodies[0]; 1499 1500 var rows = this.message_list.rows; 1501 var rowcount = tbody.rows.length; 1502 var even = rowcount%2; 1503 1504 if (!this.env.messages[uid]) 1505 this.env.messages[uid] = {}; 1506 1507 // merge flags over local message object 1508 $.extend(this.env.messages[uid], { 1509 deleted: flags.deleted?1:0, 1510 replied: flags.replied?1:0, 1511 unread: flags.unread?1:0, 1512 forwarded: flags.forwarded?1:0, 1513 flagged: flags.flagged?1:0, 1514 has_children: flags.has_children?1:0, 1515 depth: flags.depth?flags.depth:0, 1516 unread_children: flags.unread_children, 1517 parent_uid: flags.parent_uid 1518 }); 1519 1520 var message = this.env.messages[uid]; 1521 1522 var css_class = 'message' 1523 + (even ? ' even' : ' odd') 1524 + (flags.unread ? ' unread' : '') 1525 + (flags.deleted ? ' deleted' : '') 1526 + (flags.flagged ? ' flagged' : '') 1527 + (flags.unread_children && !flags.unread ? ' unroot' : '') 1528 + (this.message_list.in_selection(uid) ? ' selected' : ''); 1529 1530 // for performance use DOM instead of jQuery here 1531 var row = document.createElement('tr'); 1532 row.id = 'rcmrow'+uid; 1533 row.className = css_class; 1534 1535 var icon = this.env.messageicon; 1536 if (!flags.unread && flags.unread_children > 0 && this.env.unreadchildrenicon) 1537 icon = this.env.unreadchildrenicon; 1538 else if (flags.deleted && this.env.deletedicon) 1539 icon = this.env.deletedicon; 1540 else if (flags.replied && this.env.repliedicon) 1541 { 1542 if (flags.forwarded && this.env.forwardedrepliedicon) 1543 icon = this.env.forwardedrepliedicon; 1544 else 1545 icon = this.env.repliedicon; 1546 } 1547 else if (flags.forwarded && this.env.forwardedicon) 1548 icon = this.env.forwardedicon; 1549 else if(flags.unread && this.env.unreadicon) 1550 icon = this.env.unreadicon; 1551 1552 var tree = expando = ''; 1553 1554 if (this.env.threading) 1555 { 1556 // This assumes that div width is hardcoded to 15px, 1557 var width = message.depth * 15; 1558 if (message.depth) { 1559 if ((this.env.autoexpand_threads == 0 || this.env.autoexpand_threads == 2) && 1560 (!rows[message.parent_uid] || !rows[message.parent_uid].expanded)) { 1561 row.style.display = 'none'; 1562 message.expanded = false; 1563 } 1564 else 1565 message.expanded = true; 1566 } 1567 else if (message.has_children) { 1568 if (typeof(message.expanded) == 'undefined' && (this.env.autoexpand_threads == 1 || (this.env.autoexpand_threads == 2 && message.unread_children))) { 1569 message.expanded = true; 1570 } 1571 } 1572 1573 if (width) 1574 tree += '<span id="rcmtab' + uid + '" class="branch" style="width:' + width + 'px;"> </span>'; 1575 1576 if (message.has_children && !message.depth) 1577 expando = '<div id="rcmexpando' + uid + '" class="' + (message.expanded ? 'expanded' : 'collapsed') + '"> </div>'; 1578 } 1579 1580 tree += icon ? '<img id="msgicn'+uid+'" src="'+icon+'" alt="" class="msgicon" />' : ''; 1581 1582 // first col is always there 1583 var col = document.createElement('td'); 1584 col.className = 'threads'; 1585 col.innerHTML = expando; 1586 row.appendChild(col); 1587 1588 // build subject link 1589 if (!bw.ie && cols.subject) { 1590 var action = flags.mbox == this.env.drafts_mailbox ? 'compose' : 'show'; 1591 var uid_param = flags.mbox == this.env.drafts_mailbox ? '_draft_uid' : '_uid'; 1592 cols.subject = '<a href="./?_task=mail&_action='+action+'&_mbox='+urlencode(flags.mbox)+'&'+uid_param+'='+uid+'"'+ 1593 ' onclick="return rcube_event.cancel(event)">'+cols.subject+'</a>'; 1594 } 1595 1596 // add each submitted col 1597 for (var n = 0; n < this.env.coltypes.length; n++) { 1598 var c = this.env.coltypes[n]; 1599 col = document.createElement('td'); 1600 col.className = String(c).toLowerCase(); 1601 1602 var html; 1603 if (c=='flag') { 1604 if (flags.flagged && this.env.flaggedicon) 1605 html = '<img id="flaggedicn'+uid+'" src="'+this.env.flaggedicon+'" class="flagicon" alt="" />'; 1606 else if(!flags.flagged && this.env.unflaggedicon) 1607 html = '<img id="flaggedicn'+uid+'" src="'+this.env.unflaggedicon+'" class="flagicon" alt="" />'; 1608 } 1609 else if (c=='attachment') 1610 html = flags.attachment && this.env.attachmenticon ? '<img src="'+this.env.attachmenticon+'" alt="" />' : ' '; 1611 else if (c=='subject') 1612 html = tree + cols[c]; 1613 else 1614 html = cols[c]; 1615 1616 col.innerHTML = html; 1617 1618 row.appendChild(col); 1619 } 1620 1621 this.message_list.insert_row(row, attop); 1622 1623 // remove 'old' row 1624 if (attop && this.env.pagesize && this.message_list.rowcount > this.env.pagesize) { 1625 var uid = this.message_list.get_last_row(); 1626 this.message_list.remove_row(uid); 1627 this.message_list.clear_selection(uid); 1628 } 1629 }; 1630 1631 // messages list handling in background (for performance) 1632 this.offline_message_list = function(flag) 1633 { 1634 if (this.message_list) 1635 this.message_list.set_background_mode(flag); 1636 }; 1637 1638 this.set_list_sorting = function(sort_col, sort_order) 1639 { 1640 // set table header class 1641 $('#rcm'+this.env.sort_col).removeClass('sorted'+(this.env.sort_order.toUpperCase())); 1642 if (sort_col) 1643 $('#rcm'+sort_col).addClass('sorted'+sort_order); 1644 1645 this.env.sort_col = sort_col; 1646 this.env.sort_order = sort_order; 1647 } 1648 1649 this.set_list_options = function(cols, sort_col, sort_order, threads) 1650 { 1651 var update, add_url = ''; 1652 1653 if (this.env.sort_col != sort_col || this.env.sort_order != sort_order) { 1654 update = 1; 1655 this.set_list_sorting(sort_col, sort_order); 1656 } 1657 1658 if (this.env.threading != threads) { 1659 update = 1; 1660 add_url += '&_threads=' + threads; 1661 } 1662 1663 if (cols.join() != this.env.coltypes.join()) { 1664 update = 1; 1665 add_url += '&_cols=' + cols.join(','); 1666 } 1667 1668 if (update) 1669 this.list_mailbox('', '', sort_col+'_'+sort_order, add_url); 1670 } 1671 1523 1672 // when user doble-clicks on a row 1524 1673 this.show_message = function(id, safe, preview) … … 1555 1704 { 1556 1705 this.set_message(id, 'unread', false); 1706 this.update_thread_root(id, 'read'); 1557 1707 if (this.env.unread_counts[this.env.mailbox]) 1558 1708 { … … 1622 1772 } 1623 1773 1624 1625 1774 // list messages of a specific mailbox 1626 this.list_mailbox = function(mbox, page, sort )1627 { 1628 var add_url = '';1775 this.list_mailbox = function(mbox, page, sort, add_url) 1776 { 1777 var url = ''; 1629 1778 var target = window; 1630 1779 … … 1632 1781 mbox = this.env.mailbox; 1633 1782 1783 if (add_url) 1784 url += add_url; 1785 1634 1786 // add sort to url if set 1635 1787 if (sort) 1636 add_url += '&_sort=' + sort;1788 url += '&_sort=' + sort; 1637 1789 1638 1790 // also send search request to get the right messages 1639 1791 if (this.env.search_request) 1640 add_url += '&_search='+this.env.search_request;1792 url += '&_search='+this.env.search_request; 1641 1793 1642 1794 // set page=1 if changeing to another mailbox … … 1649 1801 1650 1802 if (mbox != this.env.mailbox || (mbox == this.env.mailbox && !page && !sort)) 1651 add_url += '&_refresh=1';1803 url += '&_refresh=1'; 1652 1804 1653 1805 // unselect selected messages … … 1662 1814 if (this.gui_objects.messagelist) 1663 1815 { 1664 this.list_mailbox_remote(mbox, page, add_url);1816 this.list_mailbox_remote(mbox, page, url); 1665 1817 return; 1666 1818 } … … 1669 1821 { 1670 1822 target = window.frames[this.env.contentframe]; 1671 add_url += '&_framed=1';1823 url += '&_framed=1'; 1672 1824 } 1673 1825 … … 1676 1828 { 1677 1829 this.set_busy(true, 'loading'); 1678 target.location.href = this.env.comm_path+'&_mbox='+urlencode(mbox)+(page ? '&_page='+page : '')+ add_url;1830 target.location.href = this.env.comm_path+'&_mbox='+urlencode(mbox)+(page ? '&_page='+page : '')+url; 1679 1831 } 1680 1832 }; … … 1692 1844 }; 1693 1845 1694 this.expunge_mailbox = function(mbox) 1695 { 1696 var lock = false; 1697 var add_url = ''; 1698 1699 // lock interface if it's the active mailbox 1700 if (mbox == this.env.mailbox) 1701 { 1702 lock = true; 1703 this.set_busy(true, 'loading'); 1704 add_url = '&_reload=1'; 1705 } 1706 1707 // send request to server 1708 var url = '_mbox='+urlencode(mbox); 1709 this.http_post('expunge', url+add_url, lock); 1710 }; 1711 1712 this.purge_mailbox = function(mbox) 1713 { 1714 var lock = false; 1715 var add_url = ''; 1716 1717 if (!confirm(this.get_label('purgefolderconfirm'))) 1718 return false; 1719 1720 // lock interface if it's the active mailbox 1721 if (mbox == this.env.mailbox) 1722 { 1723 lock = true; 1724 this.set_busy(true, 'loading'); 1725 add_url = '&_reload=1'; 1726 } 1727 1728 // send request to server 1729 var url = '_mbox='+urlencode(mbox); 1730 this.http_post('purge', url+add_url, lock); 1731 return true; 1732 }; 1733 1734 // test if purge command is allowed 1735 this.purge_mailbox_test = function() 1736 { 1737 return (this.env.messagecount && (this.env.mailbox == this.env.trash_mailbox || this.env.mailbox == this.env.junk_mailbox 1738 || this.env.mailbox.match('^' + RegExp.escape(this.env.trash_mailbox) + RegExp.escape(this.env.delimiter)) 1739 || this.env.mailbox.match('^' + RegExp.escape(this.env.junk_mailbox) + RegExp.escape(this.env.delimiter)))); 1740 }; 1846 // expand all threads with unread children 1847 this.expand_unread = function() 1848 { 1849 var tbody = this.gui_objects.messagelist.tBodies[0]; 1850 var new_row = tbody.firstChild; 1851 var r; 1852 1853 while (new_row) { 1854 if (new_row.nodeType == 1 && (r = this.message_list.rows[new_row.uid]) 1855 && r.unread_children) { 1856 this.message_list.expand_all(r); 1857 var expando = document.getElementById('rcmexpando' + r.uid); 1858 if (expando) 1859 expando.className = 'expanded'; 1860 this.set_unread_children(r.uid); 1861 } 1862 new_row = new_row.nextSibling; 1863 } 1864 return false; 1865 }; 1866 1867 // thread expanding/collapsing handler 1868 this.expand_message_row = function(e, uid) 1869 { 1870 var row = this.message_list.rows[uid]; 1871 1872 // handle unread_children mark 1873 row.expanded = !row.expanded; 1874 this.set_unread_children(uid); 1875 row.expanded = !row.expanded; 1876 1877 this.message_list.expand_row(e, uid); 1878 }; 1879 1880 // message list expanding 1881 this.expand_threads = function() 1882 { 1883 if (!this.env.threading || !this.env.autoexpand_threads || !this.message_list) 1884 return; 1885 1886 switch (this.env.autoexpand_threads) { 1887 case 2: this.expand_unread(); break; 1888 case 1: this.message_list.expand_all(); break; 1889 } 1890 // this.message_list.expand(null); 1891 } 1892 1893 // update parent in a thread 1894 this.update_thread_root = function(uid, flag) 1895 { 1896 if (!this.env.threading) 1897 return; 1898 1899 var root = this.find_thread_root(uid); 1900 1901 if (uid == root) 1902 return; 1903 1904 var p = this.message_list.rows[root]; 1905 1906 if (flag == 'read' && p.unread_children) { 1907 p.unread_children--; 1908 } else if (flag == 'unread' && p.has_children) { 1909 // unread_children may be undefined 1910 p.unread_children = p.unread_children ? p.unread_children + 1 : 1; 1911 } else { 1912 return; 1913 } 1914 1915 this.set_message_icon(root); 1916 this.set_unread_children(root); 1917 }; 1918 1919 // finds root message for specified thread 1920 this.find_thread_root = function(uid) 1921 { 1922 var r = this.message_list.rows[uid]; 1923 1924 if (r.parent_uid) 1925 return this.find_thread_root(r.parent_uid); 1926 else 1927 return uid; 1928 } 1929 1930 // update thread indicators for all messages in a thread below the specified message 1931 // return number of removed/added root level messages 1932 this.update_thread = function (uid) 1933 { 1934 if (!this.env.threading) 1935 return 0; 1936 1937 var rows = this.message_list.rows; 1938 var row = rows[uid] 1939 var depth = rows[uid].depth; 1940 var r, parent, count = 0; 1941 var roots = new Array(); 1942 1943 if (!row.depth) // root message: decrease roots count 1944 count--; 1945 else if (row.unread) { 1946 // update unread_children for thread root 1947 var parent = this.find_thread_root(uid); 1948 rows[parent].unread_children--; 1949 this.set_unread_children(parent); 1950 } 1951 1952 parent = row.parent_uid; 1953 1954 // childrens 1955 row = row.obj.nextSibling; 1956 while (row) { 1957 if (row.nodeType == 1 && (r = rows[row.uid])) { 1958 if (!r.depth || r.depth <= depth) 1959 break; 1960 1961 r.depth--; // move left 1962 $('#rcmtab'+r.uid).width(r.depth * 15); 1963 if (!r.depth) { // a new root 1964 count++; // increase roots count 1965 r.parent_uid = 0; 1966 if (r.has_children) { 1967 // replace 'leaf' with 'collapsed' 1968 $('#rcmrow'+r.uid+' '+'.leaf:first') 1969 .attr('id', 'rcmexpando' + r.uid) 1970 .attr('class', (r.obj.style.display != 'none' ? 'expanded' : 'collapsed')) 1971 .bind('mousedown', {uid:r.uid, p:this}, 1972 function(e) { return e.data.p.expand_message_row(e, e.data.uid); }); 1973 1974 r.unread_children = 0; 1975 roots[roots.length] = r; 1976 } 1977 // show if it was hidden 1978 if (r.obj.style.display == 'none') 1979 $(r.obj).show(); 1980 } 1981 else { 1982 if (r.depth == depth) 1983 r.parent_uid = parent; 1984 if (r.unread && roots.length) { 1985 roots[roots.length-1].unread_children++; 1986 } 1987 } 1988 } 1989 row = row.nextSibling; 1990 } 1991 1992 // update unread_children for roots 1993 for (var i=0; i<roots.length; i++) 1994 this.set_unread_children(roots[i].uid); 1995 1996 return count; 1997 }; 1998 1999 this.delete_excessive_thread_rows = function() 2000 { 2001 var rows = this.message_list.rows; 2002 var tbody = this.message_list.list.tBodies[0]; 2003 var row = tbody.firstChild; 2004 var cnt = this.env.pagesize + 1; 2005 2006 while (row) { 2007 if (row.nodeType == 1 && (r = rows[row.uid])) { 2008 if (!r.depth && cnt) 2009 cnt--; 2010 2011 if (!cnt) 2012 this.message_list.remove_row(row.uid); 2013 } 2014 row = row.nextSibling; 2015 } 2016 } 1741 2017 1742 2018 // set message icon … … 1748 2024 if (!rows[uid]) 1749 2025 return false; 1750 1751 if (rows[uid].deleted && this.env.deletedicon) 2026 if (!rows[uid].unread && rows[uid].unread_children && this.env.unreadchildrenicon) { 2027 icn_src = this.env.unreadchildrenicon; 2028 } 2029 else if (rows[uid].deleted && this.env.deletedicon) 1752 2030 icn_src = this.env.deletedicon; 1753 2031 else if (rows[uid].replied && this.env.repliedicon) … … 1774 2052 else if (!rows[uid].flagged && this.env.unflaggedicon) 1775 2053 icn_src = this.env.unflaggedicon; 1776 1777 2054 if (rows[uid].flagged_icon && icn_src) 1778 2055 rows[uid].flagged_icon.src = icn_src; … … 1797 2074 rows[uid].flagged = status; 1798 2075 1799 this.env.messages[uid] = rows[uid];2076 // this.env.messages[uid] = rows[uid]; 1800 2077 } 1801 2078 … … 1809 2086 if (flag) 1810 2087 this.set_message_status(uid, flag, status); 1811 2088 1812 2089 var rowobj = $(rows[uid].obj); 1813 if (rows[uid].unread && rows[uid].classname.indexOf('unread')<0) 1814 { 1815 rows[uid].classname += ' unread'; 2090 2091 if (rows[uid].unread && !rowobj.hasClass('unread')) 1816 2092 rowobj.addClass('unread'); 1817 } 1818 else if (!rows[uid].unread && rows[uid].classname.indexOf('unread')>=0) 1819 { 1820 rows[uid].classname = rows[uid].classname.replace(/\s*unread/, ''); 2093 else if (!rows[uid].unread && rowobj.hasClass('unread')) 1821 2094 rowobj.removeClass('unread'); 1822 } 1823 1824 if (rows[uid].deleted && rows[uid].classname.indexOf('deleted')<0) 1825 { 1826 rows[uid].classname += ' deleted'; 2095 2096 if (rows[uid].deleted && !rowobj.hasClass('deleted')) 1827 2097 rowobj.addClass('deleted'); 1828 } 1829 else if (!rows[uid].deleted && rows[uid].classname.indexOf('deleted')>=0) 1830 { 1831 rows[uid].classname = rows[uid].classname.replace(/\s*deleted/, ''); 2098 else if (!rows[uid].deleted && rowobj.hasClass('deleted')) 1832 2099 rowobj.removeClass('deleted'); 1833 } 1834 1835 if (rows[uid].flagged && rows[uid].classname.indexOf('flagged')<0) 1836 { 1837 rows[uid].classname += ' flagged'; 2100 2101 if (rows[uid].flagged && !rowobj.hasClass('flagged')) 1838 2102 rowobj.addClass('flagged'); 1839 } 1840 else if (!rows[uid].flagged && rows[uid].classname.indexOf('flagged')>=0) 1841 { 1842 rows[uid].classname = rows[uid].classname.replace(/\s*flagged/, ''); 2103 else if (!rows[uid].flagged && rowobj.hasClass('flagged')) 1843 2104 rowobj.removeClass('flagged'); 1844 } 1845 2105 2106 this.set_unread_children(uid); 1846 2107 this.set_message_icon(uid); 1847 } 2108 }; 2109 2110 // sets unroot (unread_children) class of parent row 2111 this.set_unread_children = function(uid) 2112 { 2113 var row = this.message_list.rows[uid]; 2114 2115 if (row.parent_uid || !row.has_children) 2116 return; 2117 2118 if (!row.unread && row.unread_children && !row.expanded) 2119 $(row.obj).addClass('unroot'); 2120 else 2121 $(row.obj).removeClass('unroot'); 2122 }; 1848 2123 1849 2124 // move selected messages to the specified mailbox … … 1882 2157 1883 2158 // if config is set to flag for deletion 1884 if (this.env.flag_for_deletion) 2159 if (this.env.flag_for_deletion) { 1885 2160 this.mark_message('delete'); 2161 return false; 2162 } 1886 2163 // if there isn't a defined trash mailbox or we are in it 1887 2164 else if (!this.env.trash_mailbox || this.env.mailbox == this.env.trash_mailbox) … … 1898 2175 this.move_messages(this.env.trash_mailbox); 1899 2176 } 2177 2178 return true; 1900 2179 }; 1901 2180 … … 1911 2190 }; 1912 2191 1913 // Send a specifc request with UIDs of all selected messages2192 // Send a specifc moveto/delete request with UIDs of all selected messages 1914 2193 // @private 1915 this._with_selected_messages = function(action, lock, add_url , remove)2194 this._with_selected_messages = function(action, lock, add_url) 1916 2195 { 1917 2196 var a_uids = new Array(); 2197 var count = 0; 1918 2198 1919 2199 if (this.env.uid) … … 1922 2202 { 1923 2203 var selection = this.message_list.get_selection(); 1924 var rows = this.message_list.rows;1925 2204 var id; 1926 2205 for (var n=0; n<selection.length; n++) { 1927 2206 id = selection[n]; 1928 2207 a_uids[a_uids.length] = id; 2208 count += this.update_thread(id); 1929 2209 this.message_list.remove_row(id, (this.env.display_next && n == selection.length-1)); 1930 2210 } … … 1940 2220 if (this.env.display_next && this.env.next_uid) 1941 2221 add_url += '&_next_uid='+this.env.next_uid; 2222 2223 if (count < 0) 2224 add_url += '&_count='+(count*-1); 2225 else if (count > 0) 2226 // remove threads from the end of the list 2227 this.delete_excessive_thread_rows(); 1942 2228 1943 2229 // send request to server … … 2010 2296 2011 2297 this.http_post('mark', '_uid='+a_uids.join(',')+'&_flag='+flag); 2298 2299 for (var i=0; i<a_uids.length; i++) 2300 this.update_thread_root(a_uids[i], flag); 2012 2301 }; 2013 2302 … … 2072 2361 var r_uids = new Array(); 2073 2362 var rows = this.message_list ? this.message_list.rows : new Array(); 2074 2363 var count = 0; 2364 2075 2365 for (var i=0; i<a_uids.length; i++) 2076 2366 { … … 2081 2371 r_uids[r_uids.length] = uid; 2082 2372 2083 if (this.env.skip_deleted) 2373 if (this.env.skip_deleted) { 2374 count += this.update_thread(uid); 2084 2375 this.message_list.remove_row(uid, (this.env.display_next && i == this.message_list.selection.length-1)); 2376 } 2085 2377 else 2086 2378 this.set_message(uid, 'deleted', true); … … 2089 2381 2090 2382 // make sure there are no selected rows 2091 if (this.env.skip_deleted && !this.env.display_next && this.message_list) 2383 if (this.env.skip_deleted && this.message_list) { 2384 if(!this.env.display_next) 2092 2385 this.message_list.clear_selection(); 2386 if (count < 0) 2387 add_url += '&_count='+(count*-1); 2388 else if (count > 0) 2389 // remove threads from the end of the list 2390 this.delete_excessive_thread_rows(); 2391 } 2093 2392 2094 2393 add_url = '&_from='+(this.env.action ? this.env.action : ''); … … 2127 2426 } 2128 2427 }; 2129 2428 2429 2430 /*********************************************************/ 2431 /********* mailbox folders methods *********/ 2432 /*********************************************************/ 2433 2434 this.expunge_mailbox = function(mbox) 2435 { 2436 var lock = false; 2437 var add_url = ''; 2438 2439 // lock interface if it's the active mailbox 2440 if (mbox == this.env.mailbox) 2441 { 2442 lock = true; 2443 this.set_busy(true, 'loading'); 2444 add_url = '&_reload=1'; 2445 } 2446 2447 // send request to server 2448 var url = '_mbox='+urlencode(mbox); 2449 this.http_post('expunge', url+add_url, lock); 2450 }; 2451 2452 this.purge_mailbox = function(mbox) 2453 { 2454 var lock = false; 2455 var add_url = ''; 2456 2457 if (!confirm(this.get_label('purgefolderconfirm'))) 2458 return false; 2459 2460 // lock interface if it's the active mailbox 2461 if (mbox == this.env.mailbox) 2462 { 2463 lock = true; 2464 this.set_busy(true, 'loading'); 2465 add_url = '&_reload=1'; 2466 } 2467 2468 // send request to server 2469 var url = '_mbox='+urlencode(mbox); 2470 this.http_post('purge', url+add_url, lock); 2471 return true; 2472 }; 2473 2474 // test if purge command is allowed 2475 this.purge_mailbox_test = function() 2476 { 2477 return (this.env.messagecount && (this.env.mailbox == this.env.trash_mailbox || this.env.mailbox == this.env.junk_mailbox 2478 || this.env.mailbox.match('^' + RegExp.escape(this.env.trash_mailbox) + RegExp.escape(this.env.delimiter)) 2479 || this.env.mailbox.match('^' + RegExp.escape(this.env.junk_mailbox) + RegExp.escape(this.env.delimiter)))); 2480 }; 2481 2130 2482 2131 2483 /*********************************************************/ … … 2153 2505 /*********************************************************/ 2154 2506 2507 // init message compose form: set focus and eventhandlers 2508 this.init_messageform = function() 2509 { 2510 if (!this.gui_objects.messageform) 2511 return false; 2512 2513 //this.messageform = this.gui_objects.messageform; 2514 var input_from = $("[name='_from']"); 2515 var input_to = $("[name='_to']"); 2516 var input_subject = $("input[name='_subject']"); 2517 var input_message = $("[name='_message']").get(0); 2518 var html_mode = $("input[name='_is_html']").val() == '1'; 2519 2520 // init live search events 2521 this.init_address_input_events(input_to); 2522 this.init_address_input_events($("[name='_cc']")); 2523 this.init_address_input_events($("[name='_bcc']")); 2524 2525 if (!html_mode) 2526 this.set_caret_pos(input_message, this.env.top_posting ? 0 : $(input_message).val().length); 2527 2528 // add signature according to selected identity 2529 if (input_from.attr('type') == 'select-one' && $("input[name='_draft_saveid']").val() == '' 2530 && !html_mode) { // if we have HTML editor, signature is added in callback 2531 this.change_identity(input_from[0]); 2532 } 2533 else if (!html_mode) 2534 this.set_caret_pos(input_message, this.env.top_posting ? 0 : $(input_message).val().length); 2535 2536 if (input_to.val() == '') 2537 input_to.focus(); 2538 else if (input_subject.val() == '') 2539 input_subject.focus(); 2540 else if (input_message && !html_mode) 2541 input_message.focus(); 2542 2543 // get summary of all field values 2544 this.compose_field_hash(true); 2545 2546 // start the auto-save timer 2547 this.auto_save_start(); 2548 }; 2549 2550 this.init_address_input_events = function(obj) 2551 { 2552 var handler = function(e){ return ref.ksearch_keypress(e,this); }; 2553 obj.bind((bw.safari || bw.ie ? 'keydown' : 'keypress'), handler); 2554 obj.attr('autocomplete', 'off'); 2555 }; 2556 2155 2557 // checks the input fields before sending a message 2156 2558 this.check_compose_input = function() 2157 {2559 { 2158 2560 // check input fields 2159 2561 var input_to = $("[name='_to']"); … … 2190 2592 2191 2593 // display localized warning for missing subject 2192 if (input_subject.val() == '') 2193 { 2594 if (input_subject.val() == '') { 2194 2595 var subject = prompt(this.get_label('nosubjectwarning'), this.get_label('nosubject')); 2195 2596 2196 2597 // user hit cancel, so don't send 2197 if (!subject && subject !== '') 2198 { 2598 if (!subject && subject !== '') { 2199 2599 input_subject.focus(); 2200 2600 return false; 2201 }2601 } 2202 2602 else 2203 {2204 2603 input_subject.val((subject ? subject : this.get_label('nosubject'))); 2205 } 2206 } 2604 } 2207 2605 2208 2606 // check for empty body 2209 2607 if ((!window.tinyMCE || !tinyMCE.get(this.env.composebody)) 2210 && input_message.val() == '' && !confirm(this.get_label('nobodywarning'))) 2211 { 2608 && input_message.val() == '' && !confirm(this.get_label('nobodywarning'))) { 2212 2609 input_message.focus(); 2213 2610 return false; 2214 }2611 } 2215 2612 else if (window.tinyMCE && tinyMCE.get(this.env.composebody) 2216 && !tinyMCE.get(this.env.composebody).getContent() 2217 && !confirm(this.get_label('nobodywarning'))) 2218 { 2613 && !tinyMCE.get(this.env.composebody).getContent() 2614 && !confirm(this.get_label('nobodywarning'))) { 2219 2615 tinyMCE.get(this.env.composebody).focus(); 2220 2616 return false; 2221 }2617 } 2222 2618 2223 2619 // Apply spellcheck changes if spell checker is active … … 2229 2625 2230 2626 return true; 2231 };2627 }; 2232 2628 2233 2629 this.stop_spellchecking = function() 2234 {2630 { 2235 2631 if (this.env.spellcheck && !this.spellcheck_ready) { 2236 2632 $(this.env.spellcheck.spell_span).trigger('click'); 2237 2633 this.set_spellcheck_state('ready'); 2238 }2239 };2634 } 2635 }; 2240 2636 2241 2637 this.display_spellcheck_controls = function(vis) 2242 {2638 { 2243 2639 if (this.env.spellcheck) { 2244 2640 // stop spellchecking process 2245 2641 if (!vis) 2246 this.stop_spellchecking();2642 this.stop_spellchecking(); 2247 2643 2248 2644 $(this.env.spellcheck.spell_container).css('visibility', vis ? 'visible' : 'hidden'); 2249 2645 } 2250 };2646 }; 2251 2647 2252 2648 this.set_spellcheck_state = function(s) … … 3512 3908 this.http_post('unsubscribe', '_mbox='+urlencode(folder)); 3513 3909 }; 3514 3910 3911 this.enable_threading = function(folder) 3912 { 3913 if (folder) 3914 this.http_post('enable-threading', '_mbox='+urlencode(folder)); 3915 }; 3916 3917 this.disable_threading = function(folder) 3918 { 3919 if (folder) 3920 this.http_post('disable-threading', '_mbox='+urlencode(folder)); 3921 }; 3922 3923 3515 3924 // helper method to find a specific mailbox row ID 3516 3925 this.get_folder_row_id = function(folder) … … 3817 4226 }; 3818 4227 3819 // for reordering column array, Konqueror workaround 3820 this.set_message_coltypes = function(coltypes) 4228 // for reordering column array (Konqueror workaround) 4229 // and for setting some message list global variables 4230 this.set_message_coltypes = function(coltypes, repl) 3821 4231 { 3822 this. coltypes = coltypes;4232 this.env.coltypes = coltypes; 3823 4233 3824 4234 // set correct list titles 3825 var cell, col;3826 4235 var thead = this.gui_objects.messagelist ? this.gui_objects.messagelist.tHead : null; 3827 for (var n=0; thead && n<this.coltypes.length; n++) 3828 { 3829 col = this.coltypes[n]; 4236 4237 // replace old column headers 4238 if (thead && repl) { 4239 for (var cell, c=0; c < repl.length; c++) { 4240 cell = thead.rows[0].cells[c]; 4241 if (!cell) { 4242 cell = document.createElement('td'); 4243 thead.rows[0].appendChild(cell); 4244 } 4245 cell.innerHTML = repl[c].html; 4246 if (repl[c].id) cell.id = repl[c].id; 4247 if (repl[c].className) cell.className = repl[c].className; 4248 } 4249 } 4250 4251 var cell, col, n; 4252 for (n=0; thead && n<this.env.coltypes.length; n++) 4253 { 4254 col = this.env.coltypes[n]; 3830 4255 if ((cell = thead.rows[0].cells[n+1]) && (col=='from' || col=='to')) 3831 4256 { … … 3833 4258 if (cell.firstChild && cell.firstChild.tagName.toLowerCase()=='a') 3834 4259 { 3835 cell.firstChild.innerHTML = this.get_label(this. coltypes[n]);4260 cell.firstChild.innerHTML = this.get_label(this.env.coltypes[n]); 3836 4261 cell.firstChild.onclick = function(){ return rcmail.command('sort', this.__col, this); }; 3837 4262 cell.firstChild.__col = col; 3838 4263 } 3839 4264 else 3840 cell.innerHTML = this.get_label(this. coltypes[n]);4265 cell.innerHTML = this.get_label(this.env.coltypes[n]); 3841 4266 3842 4267 cell.id = 'rcm'+col; 3843 4268 } 3844 else if (col == 'subject' && this.message_list) 3845 this.message_list.subject_col = n+1; 3846 } 3847 }; 3848 3849 // create a table row in the message list 3850 this.add_message_row = function(uid, cols, flags, attachment, attop) 3851 { 3852 if (!this.gui_objects.messagelist || !this.message_list) 3853 return false; 3854 3855 if (this.message_list.background) 3856 var tbody = this.message_list.background; 3857 else 3858 var tbody = this.gui_objects.messagelist.tBodies[0]; 3859 3860 var rowcount = tbody.rows.length; 3861 var even = rowcount%2; 3862 3863 this.env.messages[uid] = { 3864 deleted: flags.deleted?1:0, 3865 replied: flags.replied?1:0, 3866 unread: flags.unread?1:0, 3867 forwarded: flags.forwarded?1:0, 3868 flagged:flags.flagged?1:0 3869 }; 3870 3871 var css_class = 'message' 3872 + (even ? ' even' : ' odd') 3873 + (flags.unread ? ' unread' : '') 3874 + (flags.deleted ? ' deleted' : '') 3875 + (flags.flagged ? ' flagged' : '') 3876 + (this.message_list.in_selection(uid) ? ' selected' : ''); 3877 3878 // for performance use DOM instead of jQuery here 3879 var row = document.createElement('tr'); 3880 row.id = 'rcmrow'+uid; 3881 row.className = css_class; 3882 3883 var icon = this.env.messageicon; 3884 if (flags.deleted && this.env.deletedicon) 3885 icon = this.env.deletedicon; 3886 else if (flags.replied && this.env.repliedicon) 3887 { 3888 if (flags.forwarded && this.env.forwardedrepliedicon) 3889 icon = this.env.forwardedrepliedicon; 3890 else 3891 icon = this.env.repliedicon; 3892 } 3893 else if (flags.forwarded && this.env.forwardedicon) 3894 icon = this.env.forwardedicon; 3895 else if(flags.unread && this.env.unreadicon) 3896 icon = this.env.unreadicon; 3897 3898 // add icon col 3899 var col = document.createElement('td'); 3900 col.className = 'icon'; 3901 col.innerHTML = icon ? '<img src="'+icon+'" alt="" />' : ''; 3902 row.appendChild(col); 3903 3904 // build subject link 3905 if (!bw.ie && cols.subject) { 3906 var action = cols.mbox == this.env.drafts_mailbox ? 'compose' : 'show'; 3907 var uid_param = cols.mbox == this.env.drafts_mailbox ? '_draft_uid' : '_uid'; 3908 cols.subject = '<a href="./?_task=mail&_action='+action+'&_mbox='+urlencode(cols.mbox)+'&'+uid_param+'='+uid+'"'+ 3909 ' onclick="return rcube_event.cancel(event)">'+cols.subject+'</a>'; 3910 } 3911 3912 // add each submitted col 3913 for (var n = 0; n < this.coltypes.length; n++) { 3914 var c = this.coltypes[n]; 3915 col = document.createElement('td'); 3916 col.className = String(c).toLowerCase(); 3917 3918 if (c=='flag') { 3919 if (flags.flagged && this.env.flaggedicon) 3920 col.innerHTML = '<img src="'+this.env.flaggedicon+'" alt="" />'; 3921 else if(!flags.flagged && this.env.unflaggedicon) 3922 col.innerHTML = '<img src="'+this.env.unflaggedicon+'" alt="" />'; 3923 } 3924 else if (c=='attachment') 3925 col.innerHTML = (attachment && this.env.attachmenticon ? '<img src="'+this.env.attachmenticon+'" alt="" />' : ' '); 3926 else 3927 col.innerHTML = cols[c]; 3928 3929 row.appendChild(col); 3930 } 3931 3932 this.message_list.insert_row(row, attop); 3933 3934 // remove 'old' row 3935 if (attop && this.env.pagesize && this.message_list.rowcount > this.env.pagesize) { 3936 var uid = this.message_list.get_last_row(); 3937 this.message_list.remove_row(uid); 3938 this.message_list.clear_selection(uid); 3939 } 3940 }; 3941 3942 // messages list handling in background (for performance) 3943 this.offline_message_list = function(flag) 3944 { 4269 } 4270 4271 // remove excessive columns 4272 for (var i=n+1; thead && i<thead.rows[0].cells.length; i++) 4273 thead.rows[0].removeChild(thead.rows[0].cells[i]); 4274 4275 this.env.subject_col = null; 4276 this.env.flagged_col = null; 4277 4278 var found; 4279 if((found = find_in_array('subject', this.env.coltypes)) >= 0) { 4280 this.set_env('subject_col', found); 3945 4281 if (this.message_list) 3946 this.message_list.set_background_mode(flag); 3947 }; 4282 this.message_list.subject_col = found+1; 4283 } 4284 if((found = find_in_array('flag', this.env.coltypes)) >= 0) 4285 this.set_env('flagged_col', found); 4286 }; 3948 4287 3949 4288 // replace content of row count display … … 4261 4600 this.triggerEvent(response.callbacks[i][0], response.callbacks[i][1]); 4262 4601 } 4263 4602 4264 4603 // process the response data according to the sent action 4265 4604 switch (response.action) { … … 4289 4628 this.enable_command('show', 'reply', 'reply-all', 'forward', 'moveto', 'delete', 4290 4629 'mark', 'viewsource', 'open', 'edit', 'download', 'print', 'load-attachment', 4291 'purge', 'expunge', 'select-all', 'select-none', 'sort', false); 4630 'purge', 'expunge', 'select-all', 'select-none', 'sort', 4631 'expand-all', 'expand-unread', 'collapse-all', false); 4292 4632 } 4293 4633 break; … … 4295 4635 case 'check-recent': 4296 4636 case 'getunread': 4637 case 'search': 4297 4638 case 'list': 4298 4639 if (this.task == 'mail') { 4299 if (this.message_list && response.action == 'list')4640 if (this.message_list && (response.action == 'list' || response.action == 'search')) { 4300 4641 this.msglist_select(this.message_list); 4642 this.expand_threads(); 4643 } 4301 4644 this.enable_command('show', 'expunge', 'select-all', 'select-none', 'sort', (this.env.messagecount > 0)); 4302 4645 this.enable_command('purge', this.purge_mailbox_test()); 4303 4646 4647 this.enable_command('expand-all', 'expand-unread', 'collapse-all', this.env.threading && this.env.messagecount); 4648 4304 4649 if (response.action == 'list') 4305 4650 this.triggerEvent('listupdate', { folder:this.env.mailbox, rowcount:this.message_list.rowcount }); … … 4333 4678 this.http_request('keep-alive', '_t='+d.getTime()); 4334 4679 }; 4680 4681 // start interval for keep-alive/recent_check signal 4682 this.start_keepalive = function() 4683 { 4684 if (this.env.keep_alive && !this.env.framed && this.task=='mail' && this.gui_objects.mailboxlist) 4685 this._int = setInterval(function(){ ref.check_for_recent(false); }, this.env.keep_alive * 1000); 4686 else if (this.env.keep_alive && !this.env.framed && this.task!='login') 4687 this._int = setInterval(function(){ ref.send_keep_alive(); }, this.env.keep_alive * 1000); 4688 } 4335 4689 4336 4690 // send periodic request to check for recent messages … … 4434 4788 } // end object rcube_webmail 4435 4789 4436 4437 4790 // copy event engine prototype 4438 4791 rcube_webmail.prototype.addEventListener = rcube_event_engine.prototype.addEventListener; -
trunk/roundcubemail/program/js/common.js
r3310 r3367 293 293 else if (typeof e == 'object') 294 294 e.event = evt; 295 295 296 296 if (this._events && this._events[evt] && !this._event_exec) { 297 297 this._event_exec = true; -
trunk/roundcubemail/program/js/list.js
r3055 r3367 38 38 this.shiftkey = false; 39 39 this.multiselect = false; 40 this.multiexpand = false; 40 41 this.multi_selecting = false; 41 42 this.draggable = false; … … 77 78 { 78 79 row = this.list.tBodies[0].childNodes[r]; 79 while (row && (row.nodeType != 1 || row.style.display == 'none'))80 while (row && row.nodeType != 1) 80 81 { 81 82 row = row.nextSibling; … … 109 110 var uid = RegExp.$1; 110 111 row.uid = uid; 111 this.rows[uid] = {uid:uid, id:row.id, obj:row , classname:row.className};112 this.rows[uid] = {uid:uid, id:row.id, obj:row}; 112 113 113 114 // set eventhandlers to table row … … 320 321 321 322 323 expand_row: function(e, id) 324 { 325 var row = this.rows[id]; 326 var evtarget = rcube_event.get_target(e); 327 var mod_key = rcube_event.get_modifier(e); 328 329 // Don't select this message 330 this.dont_select = true; 331 // Don't treat double click on the expando as double click on the message. 332 row.clicked = 0; 333 334 if (row.expanded) { 335 evtarget.className = "collapsed"; 336 if (mod_key == CONTROL_KEY || this.multiexpand) 337 this.collapse_all(row); 338 else 339 this.collapse(row); 340 } 341 else { 342 evtarget.className = "expanded"; 343 if (mod_key == CONTROL_KEY || this.multiexpand) 344 this.expand_all(row); 345 else 346 this.expand(row); 347 } 348 }, 349 350 collapse: function(row) 351 { 352 row.expanded = false; 353 this.triggerEvent('expandcollapse', { uid:row.uid, expanded:row.expanded }); 354 var depth = row.depth; 355 var new_row = row ? row.obj.nextSibling : null; 356 var r; 357 358 while (new_row) { 359 if (new_row.nodeType == 1) { 360 var r = this.rows[new_row.uid]; 361 if (r && r.depth <= depth) 362 break; 363 $(new_row).hide(); 364 r.expanded = false; 365 this.triggerEvent('expandcollapse', { uid:r.uid, expanded:r.expanded }); 366 } 367 new_row = new_row.nextSibling; 368 } 369 370 return false; 371 }, 372 373 expand: function(row) 374 { 375 var depth, new_row; 376 var last_expanded_parent_depth; 377 378 if (row) { 379 row.expanded = true; 380 depth = row.depth; 381 new_row = row.obj.nextSibling; 382 this.triggerEvent('expandcollapse', { uid:row.uid, expanded:row.expanded }); 383 } 384 else { 385 var tbody = this.list.tBodies[0]; 386 new_row = tbody.firstChild; 387 depth = 0; 388 last_expanded_parent_depth = 0; 389 } 390 391 while (new_row) { 392 if (new_row.nodeType == 1) { 393 var r = this.rows[new_row.uid]; 394 if (r) { 395 if (row && (!r.depth || r.depth <= depth)) 396 break; 397 398 if (r.parent_uid) { 399 var p = this.rows[r.parent_uid]; 400 if (p && p.expanded) { 401 if ((row && p == row) || last_expanded_parent_depth >= p.depth - 1) { 402 last_expanded_parent_depth = p.depth; 403 $(new_row).show(); 404 r.expanded = true; 405 this.triggerEvent('expandcollapse', { uid:r.uid, expanded:r.expanded }); 406 } 407 } 408 else 409 if (row && (! p || p.depth <= depth)) 410 break; 411 } 412 } 413 } 414 new_row = new_row.nextSibling; 415 } 416 417 return false; 418 }, 419 420 421 collapse_all: function(row) 422 { 423 var depth, new_row; 424 var r; 425 426 if (row) { 427 row.expanded = false; 428 depth = row.depth; 429 new_row = row.obj.nextSibling; 430 this.triggerEvent('expandcollapse', { uid:row.uid, expanded:row.expanded }); 431 432 // don't collapse sub-root tree in multiexpand mode 433 if (depth && this.multiexpand) 434 return false; 435 } 436 else { 437 var tbody = this.list.tBodies[0]; 438 new_row = tbody.firstChild; 439 depth = 0; 440 } 441 442 while (new_row) { 443 if (new_row.nodeType == 1) { 444 var r = this.rows[new_row.uid]; 445 if (r) { 446 if (row && (!r.depth || r.depth <= depth)) 447 break; 448 449 if (row || r.depth) 450 $(new_row).hide(); 451 if (r.has_children) { 452 r.expanded = false; 453 var expando = document.getElementById('rcmexpando' + r.uid); 454 if (expando) 455 expando.className = 'collapsed'; 456 this.triggerEvent('expandcollapse', { uid:r.uid, expanded:r.expanded }); 457 } 458 } 459 } 460 new_row = new_row.nextSibling; 461 } 462 463 return false; 464 }, 465 466 expand_all: function(row) 467 { 468 var depth, new_row; 469 var r; 470 471 if (row) { 472 row.expanded = true; 473 depth = row.depth; 474 new_row = row.obj.nextSibling; 475 this.triggerEvent('expandcollapse', { uid:row.uid, expanded:row.expanded }); 476 } 477 else { 478 var tbody = this.list.tBodies[0]; 479 new_row = tbody.firstChild; 480 depth = 0; 481 } 482 483 while (new_row) { 484 if (new_row.nodeType == 1) { 485 var r = this.rows[new_row.uid]; 486 if (r) { 487 if (row && r.depth <= depth) 488 break; 489 490 $(new_row).show(); 491 if (r.has_children) { 492 r.expanded = true; 493 var expando = document.getElementById('rcmexpando' + r.uid); 494 if (expando) 495 expando.className = 'expanded'; 496 this.triggerEvent('expandcollapse', { uid:r.uid, expanded:r.expanded }); 497 } 498 } 499 } 500 new_row = new_row.nextSibling; 501 } 502 return false; 503 }, 504 322 505 /** 323 506 * get first/next/previous/last rows that are not hidden … … 496 679 if ((this.rows[n].obj.rowIndex >= i) && (this.rows[n].obj.rowIndex <= j)) 497 680 { 498 if (!this.in_selection(n)) 681 if (!this.in_selection(n)) { 499 682 this.highlight_row(n, true); 683 } 500 684 } 501 685 else 502 686 { 503 if (this.in_selection(n) && !control) 687 if (this.in_selection(n) && !control) { 504 688 this.highlight_row(n, true); 689 } 505 690 } 506 691 } … … 517 702 return true; 518 703 519 return false; 704 return false; 520 705 }, 521 706 … … 568 753 569 754 for (var n in this.rows) 570 this.highlight_row(n, true); 755 this.highlight_row(n, true); 571 756 572 757 // trigger event if selection changed … … 686 871 rcube_event.cancel(e); 687 872 return this.use_arrow_key(keyCode, mod_key); 873 case 61: 874 case 107: // Plus sign on a numeric keypad (fc11 + firefox 3.5.2) 875 case 109: 876 case 32: 877 // Stop propagation 878 rcube_event.cancel(e); 879 var ret = this.use_plusminus_key(keyCode, mod_key); 880 this.key_pressed = keyCode; 881 this.triggerEvent('keypress'); 882 return ret; 688 883 default: 689 884 this.shiftkey = e.shiftKey; … … 713 908 case 63233: 714 909 case 63232: 910 case 61: 911 case 107: 912 case 109: 913 case 32: 715 914 if (!rcube_event.get_modifier(e) && this.focused) 716 915 return rcube_event.cancel(e); … … 747 946 748 947 /** 948 * Special handling method for +/- keys 949 */ 950 use_plusminus_key: function(keyCode, mod_key) 951 { 952 var selected_row = this.rows[this.last_selected]; 953 if (!selected_row) 954 return; 955 956 if (keyCode == 32) 957 keyCode = selected_row.expanded ? 109 : 61; 958 if (keyCode == 61 || keyCode == 107) 959 if (mod_key == CONTROL_KEY || this.multiexpand) 960 this.expand_all(selected_row); 961 else 962 this.expand(selected_row); 963 else 964 if (mod_key == CONTROL_KEY || this.multiexpand) 965 this.collapse_all(selected_row); 966 else 967 this.collapse(selected_row); 968 969 var expando = document.getElementById('rcmexpando' + selected_row.uid); 970 if (expando) 971 expando.className = selected_row.expanded?'expanded':'collapsed'; 972 973 return false; 974 }, 975 976 977 /** 749 978 * Try to scroll the list to make the specified row visible 750 979 */ … … 780 1009 this.draglayer = $('<div>').attr('id', 'rcmdraglayer').css({ position:'absolute', display:'none', 'z-index':2000 }).appendTo(document.body); 781 1010 782 // get subjects of selected dmessages1011 // get subjects of selected messages 783 1012 var names = ''; 784 var c, i, node,subject, obj;1013 var c, i, subject, obj; 785 1014 for(var n=0; n<this.selection.length; n++) 786 1015 { … … 791 1020 } 792 1021 793 if ( this.rows[this.selection[n]].obj)1022 if (obj = this.rows[this.selection[n]].obj) 794 1023 { 795 obj = this.rows[this.selection[n]].obj;796 1024 subject = ''; 797 1025 798 for (c=0, i=0; i<obj.childNodes.length; i++)1026 for (c=0, i=0; i<obj.childNodes.length; i++) 799 1027 { 800 if (obj.childNodes[i].nodeName == 'TD')1028 if (obj.childNodes[i].nodeName == 'TD') 801 1029 { 802 if (((node = obj.childNodes[i].firstChild) && (node.nodeType==3 || node.nodeName=='A')) && 803 (this.subject_col < 0 || (this.subject_col >= 0 && this.subject_col == c))) 804 { 805 if (n == 0) { 806 if (node.nodeType == 3) 807 this.drag_start_pos = $(obj.childNodes[i]).offset(); 808 else 809 this.drag_start_pos = $(node).offset(); 1030 if (n == 0) 1031 this.drag_start_pos = $(obj.childNodes[i]).offset(); 1032 1033 if (this.subject_col < 0 || (this.subject_col >= 0 && this.subject_col == c)) 1034 { 1035 var node, tmp_node, nodes = obj.childNodes[i].childNodes; 1036 // find text node 1037 for (m=0; m<nodes.length; m++) { 1038 if ((tmp_node = obj.childNodes[i].childNodes[m]) && (tmp_node.nodeType==3 || tmp_node.nodeName=='A')) 1039 node = tmp_node; 810 1040 } 1041 1042 if (!node) 1043 break; 1044 811 1045 subject = node.nodeType==3 ? node.data : node.innerHTML; 812 1046 // remove leading spaces -
trunk/roundcubemail/program/lib/imap.inc
r3353 r3367 174 174 var $junk = false; 175 175 var $flagged = false; 176 var $has_children = false; 177 var $depth = 0; 178 var $unread_children = 0; 176 179 var $others = array(); 177 }178 179 /**180 * @todo Change class vars to public/private181 */182 class iilThreadHeader183 {184 var $id;185 var $sbj;186 var $irt;187 var $mid;188 180 } 189 181 … … 874 866 $fields = array('ARRIVAL' => 1,'CC' => 1,'DATE' => 1, 875 867 'FROM' => 1, 'SIZE' => 1, 'SUBJECT' => 1, 'TO' => 1); 876 868 877 869 if (!$fields[$field]) { 878 870 return false; … … 886 878 $is_uid = $is_uid ? 'UID ' : ''; 887 879 888 if (!empty($add)) { 880 // message IDs 881 if (is_array($add)) 882 $add = iil_CompressMessageSet(join(',', $add)); 883 884 if (!empty($add)) 889 885 $add = " $add"; 890 }891 886 892 887 $command = 's ' . $is_uid . 'SORT (' . $field . ') '; … … 918 913 function iil_C_FetchHeaderIndex(&$conn, $mailbox, $message_set, $index_field='', $skip_deleted=true, $uidfetch=false) { 919 914 920 list($from_idx, $to_idx) = explode(':', $message_set); 921 if (empty($message_set) || 922 (isset($to_idx) && $to_idx != '*' && (int)$from_idx > (int)$to_idx)) { 923 return false; 924 } 925 915 if (is_array($message_set)) { 916 if (!($message_set = iil_CompressMessageSet(join(',', $message_set)))) 917 return false; 918 } else { 919 list($from_idx, $to_idx) = explode(':', $message_set); 920 if (empty($message_set) || 921 (isset($to_idx) && $to_idx != '*' && (int)$from_idx > (int)$to_idx)) { 922 return false; 923 } 924 } 925 926 926 $index_field = empty($index_field) ? 'DATE' : strtoupper($index_field); 927 927 928 928 $fields_a['DATE'] = 1; 929 929 $fields_a['INTERNALDATE'] = 4; 930 $fields_a['ARRIVAL'] = 4; 930 931 $fields_a['FROM'] = 1; 931 932 $fields_a['REPLY-TO'] = 1; 932 933 $fields_a['SENDER'] = 1; 933 934 $fields_a['TO'] = 1; 935 $fields_a['CC'] = 1; 934 936 $fields_a['SUBJECT'] = 1; 935 937 $fields_a['UID'] = 2; … … 1032 1034 } while (!iil_StartsWith($line, $key, true)); 1033 1035 1034 /*1035 //check number of elements...1036 if (is_numeric($from_idx) && is_numeric($to_idx)) {1037 //count how many we should have1038 $should_have = $to_idx - $from_idx + 1;1039 1040 //if we have less, try and fill in the "gaps"1041 if (count($result) < $should_have) {1042 for ($i=$from_idx; $i<=$to_idx; $i++) {1043 if (!isset($result[$i])) {1044 $result[$i] = '';1045 }1046 }1047 }1048 }1049 */1050 1036 return $result; 1051 1037 } … … 1123 1109 } 1124 1110 1125 function iil_SortThreadHeaders($headers, $index_a, $uids) {1126 asort($index_a);1127 $result = array();1128 foreach ($index_a as $mid=>$foobar) {1129 $uid = $uids[$mid];1130 $result[$uid] = $headers[$uid];1131 }1132 return $result;1133 }1134 1135 function iil_C_FetchThreadHeaders(&$conn, $mailbox, $message_set) {1136 global $clock;1137 global $index_a;1138 1139 list($from_idx, $to_idx) = explode(':', $message_set);1140 if (empty($message_set) || (isset($to_idx)1141 && (int)$from_idx > (int)$to_idx)) {1142 return false;1143 }1144 1145 $result = array();1146 $uids = iil_C_FetchUIDs($conn, $mailbox);1147 $debug = false;1148 1149 $message_set = iil_CompressMessageSet($message_set);1150 1151 /* if we're missing any, get them */1152 if ($message_set) {1153 /* FETCH date,from,subject headers */1154 $key = 'fh';1155 $fp = $conn->fp;1156 $request = $key . " FETCH $message_set ";1157 $request .= "(BODY.PEEK[HEADER.FIELDS (SUBJECT MESSAGE-ID IN-REPLY-TO)])";1158 $mid_to_id = array();1159 if (!iil_PutLine($fp, $request)) {1160 return false;1161 }1162 do {1163 $line = chop(iil_ReadLine($fp, 1024));1164 if ($debug) {1165 echo $line . "\n";1166 }1167 if (preg_match('/\{[0-9]+\}$/', $line)) {1168 $a = explode(' ', $line);1169 $new = array();1170 1171 $new_thhd = new iilThreadHeader;1172 $new_thhd->id = $a[1];1173 do {1174 $line = chop(iil_ReadLine($fp, 1024), "\r\n");1175 if (iil_StartsWithI($line, 'Message-ID:')1176 || (iil_StartsWithI($line,'In-Reply-To:'))1177 || (iil_StartsWithI($line,'SUBJECT:'))) {1178 1179 $pos = strpos($line, ':');1180 $field_name = substr($line, 0, $pos);1181 $field_val = substr($line, $pos+1);1182 1183 $new[strtoupper($field_name)] = trim($field_val);1184 1185 } else if (preg_match('/^\s+/', $line)) {1186 $new[strtoupper($field_name)] .= trim($line);1187 }1188 } while ($line[0] != ')');1189 1190 $new_thhd->sbj = $new['SUBJECT'];1191 $new_thhd->mid = substr($new['MESSAGE-ID'], 1, -1);1192 $new_thhd->irt = substr($new['IN-REPLY-TO'], 1, -1);1193 1194 $result[$uids[$new_thhd->id]] = $new_thhd;1195 }1196 } while (!iil_StartsWith($line, 'fh'));1197 }1198 1199 /* sort headers */1200 if (is_array($index_a)) {1201 $result = iil_SortThreadHeaders($result, $index_a, $uids);1202 }1203 1204 //echo 'iil_FetchThreadHeaders:'."\n";1205 //print_r($result);1206 1207 return $result;1208 }1209 1210 function iil_C_BuildThreads2(&$conn, $mailbox, $message_set, &$clock) {1211 global $index_a;1212 1213 list($from_idx, $to_idx) = explode(':', $message_set);1214 if (empty($message_set) || (isset($to_idx)1215 && (int)$from_idx > (int)$to_idx)) {1216 return false;1217 }1218 1219 $result = array();1220 $roots = array();1221 $root_mids = array();1222 $sub_mids = array();1223 $strays = array();1224 $messages = array();1225 $fp = $conn->fp;1226 $debug = false;1227 1228 $sbj_filter_pat = '/[a-z]{2,3}(\[[0-9]*\])?:(\s*)/i';1229 1230 /* Do "SELECT" command */1231 if (!iil_C_Select($conn, $mailbox)) {1232 return false;1233 }1234 1235 /* FETCH date,from,subject headers */1236 $mid_to_id = array();1237 $messages = array();1238 $headers = iil_C_FetchThreadHeaders($conn, $mailbox, $message_set);1239 if ($clock) {1240 $clock->register('fetched headers');1241 }1242 1243 if ($debug) {1244 print_r($headers);1245 }1246 1247 /* go through header records */1248 foreach ($headers as $header) {1249 //$id = $header['i'];1250 //$new = array('id'=>$id, 'MESSAGE-ID'=>$header['m'],1251 // 'IN-REPLY-TO'=>$header['r'], 'SUBJECT'=>$header['s']);1252 $id = $header->id;1253 $new = array('id' => $id, 'MESSAGE-ID' => $header->mid,1254 'IN-REPLY-TO' => $header->irt, 'SUBJECT' => $header->sbj);1255 1256 /* add to message-id -> mid lookup table */1257 $mid_to_id[$new['MESSAGE-ID']] = $id;1258 1259 /* if no subject, use message-id */1260 if (empty($new['SUBJECT'])) {1261 $new['SUBJECT'] = $new['MESSAGE-ID'];1262 }1263 1264 /* if subject contains 'RE:' or has in-reply-to header, it's a reply */1265 $sbj_pre = '';1266 $has_re = false;1267 if (preg_match($sbj_filter_pat, $new['SUBJECT'])) {1268 $has_re = true;1269 }1270 if ($has_re || $new['IN-REPLY-TO']) {1271 $sbj_pre = 'RE:';1272 }1273 1274 /* strip out 're:', 'fw:' etc */1275 if ($has_re) {1276 $sbj = preg_replace($sbj_filter_pat, '', $new['SUBJECT']);1277 } else {1278 $sbj = $new['SUBJECT'];1279 }1280 $new['SUBJECT'] = $sbj_pre.$sbj;1281 1282 1283 /* if subject not a known thread-root, add to list */1284 if ($debug) {1285 echo $id . ' ' . $new['SUBJECT'] . "\t" . $new['MESSAGE-ID'] . "\n";1286 }1287 $root_id = $roots[$sbj];1288 1289 if ($root_id && ($has_re || !$root_in_root[$root_id])) {1290 if ($debug) {1291 echo "\tfound root: $root_id\n";1292 }1293 $sub_mids[$new['MESSAGE-ID']] = $root_id;1294 $result[$root_id][] = $id;1295 } else if (!isset($roots[$sbj]) || (!$has_re && $root_in_root[$root_id])) {1296 /* try to use In-Reply-To header to find root1297 unless subject contains 'Re:' */1298 if ($has_re&&$new['IN-REPLY-TO']) {1299 if ($debug) {1300 echo "\tlooking: ".$new['IN-REPLY-TO']."\n";1301 }1302 //reply to known message?1303 $temp = $sub_mids[$new['IN-REPLY-TO']];1304 1305 if ($temp) {1306 //found it, root:=parent's root1307 if ($debug) {1308 echo "\tfound parent: ".$new['SUBJECT']."\n";1309 }1310 $result[$temp][] = $id;1311 $sub_mids[$new['MESSAGE-ID']] = $temp;1312 $sbj = '';1313 } else {1314 //if we can't find referenced parent, it's a "stray"1315 $strays[$id] = $new['IN-REPLY-TO'];1316 }1317 }1318 1319 //add subject as root1320 if ($sbj) {1321 if ($debug) {1322 echo "\t added to root\n";1323 }1324 $roots[$sbj] = $id;1325 $root_in_root[$id] = !$has_re;1326 $sub_mids[$new['MESSAGE-ID']] = $id;1327 $result[$id] = array($id);1328 }1329 if ($debug) {1330 echo $new['MESSAGE-ID'] . "\t" . $sbj . "\n";1331 }1332 }1333 }1334 1335 //now that we've gone through all the messages,1336 //go back and try and link up the stray threads1337 if (count($strays) > 0) {1338 foreach ($strays as $id=>$irt) {1339 $root_id = $sub_mids[$irt];1340 if (!$root_id || $root_id==$id) {1341 continue;1342 }1343 $result[$root_id] = array_merge($result[$root_id],$result[$id]);1344 unset($result[$id]);1345 }1346 }1347 1348 if ($clock) {1349 $clock->register('data prepped');1350 }1351 1352 if ($debug) {1353 print_r($roots);1354 }1355 1356 return $result;1357 }1358 1359 function iil_SortThreads(&$tree, $index, $sort_order = 'ASC') {1360 if (!is_array($tree) || !is_array($index)) {1361 return false;1362 }1363 1364 //create an id to position lookup table1365 $i = 0;1366 foreach ($index as $id=>$val) {1367 $i++;1368 $index[$id] = $i;1369 }1370 $max = $i+1;1371 1372 //for each tree, set array key to position1373 $itree = array();1374 foreach ($tree as $id=>$node) {1375 if (count($tree[$id])<=1) {1376 //for "threads" with only one message, key is position of that message1377 $n = $index[$id];1378 $itree[$n] = array($n=>$id);1379 } else {1380 //for "threads" with multiple messages,1381 $min = $max;1382 $new_a = array();1383 foreach ($tree[$id] as $mid) {1384 $new_a[$index[$mid]] = $mid; //create new sub-array mapping position to id1385 $pos = $index[$mid];1386 if ($pos&&$pos<$min) {1387 $min = $index[$mid]; //find smallest position1388 }1389 }1390 $n = $min; //smallest position of child is thread position1391 1392 //assign smallest position to root level key1393 //set children array to one created above1394 ksort($new_a);1395 $itree[$n] = $new_a;1396 }1397 }1398 1399 //sort by key, this basically sorts all threads1400 ksort($itree);1401 $i = 0;1402 $out = array();1403 foreach ($itree as $k=>$node) {1404 $out[$i] = $itree[$k];1405 $i++;1406 }1407 1408 return $out;1409 }1410 1411 function iil_IndexThreads(&$tree) {1412 /* creates array mapping mid to thread id */1413 1414 if (!is_array($tree)) {1415 return false;1416 }1417 1418 $t_index = array();1419 foreach ($tree as $pos=>$kids) {1420 foreach ($kids as $kid) $t_index[$kid] = $pos;1421 }1422 1423 return $t_index;1424 }1425 1426 1111 function iil_C_FetchHeaders(&$conn, $mailbox, $message_set, $uidfetch=false, $bodystr=false, $add='') 1427 1112 { … … 1436 1121 return false; 1437 1122 } 1123 1124 if (is_array($message_set)) 1125 $message_set = join(',', $message_set); 1438 1126 1439 1127 $message_set = iil_CompressMessageSet($message_set); … … 1879 1567 } 1880 1568 1569 // Don't be tempted to change $str to pass by reference to speed this up - it will slow it down by about 1570 // 7 times instead :-) See comments on http://uk2.php.net/references and this article: 1571 // http://derickrethans.nl/files/phparch-php-variables-article.pdf 1572 function iil_ParseThread($str, $begin, $end, $root, $parent, $depth, &$depthmap, &$haschildren) { 1573 $node = array(); 1574 if ($str[$begin] != '(') { 1575 $stop = $begin + strspn($str, "1234567890", $begin, $end - $begin); 1576 $msg = substr($str, $begin, $stop - $begin); 1577 if ($msg == 0) 1578 return $node; 1579 if (is_null($root)) 1580 $root = $msg; 1581 $depthmap[$msg] = $depth; 1582 $haschildren[$msg] = false; 1583 if (!is_null($parent)) 1584 $haschildren[$parent] = true; 1585 if ($stop + 1 < $end) 1586 $node[$msg] = iil_ParseThread($str, $stop + 1, $end, $root, $msg, $depth + 1, $depthmap, $haschildren); 1587 else 1588 $node[$msg] = array(); 1589 } else { 1590 $off = $begin; 1591 while ($off < $end) { 1592 $start = $off; 1593 $off++; 1594 $n = 1; 1595 while ($n > 0) { 1596 $p = strpos($str, ')', $off); 1597 if ($p === false) { 1598 error_log('Mismatched brackets parsing IMAP THREAD response:'); 1599 error_log(substr($str, ($begin < 10) ? 0 : ($begin - 10), $end - $begin + 20)); 1600 error_log(str_repeat(' ', $off - (($begin < 10) ? 0 : ($begin - 10)))); 1601 return $node; 1602 } 1603 $p1 = strpos($str, '(', $off); 1604 if ($p1 !== false && $p1 < $p) { 1605 $off = $p1 + 1; 1606 $n++; 1607 } else { 1608 $off = $p + 1; 1609 $n--; 1610 } 1611 } 1612 $node += iil_ParseThread($str, $start + 1, $off - 1, $root, $parent, $depth, $depthmap, $haschildren); 1613 } 1614 } 1615 1616 return $node; 1617 } 1618 1619 function iil_C_Thread(&$conn, $folder, $algorithm='REFERENCES', $criteria='', 1620 $encoding='US-ASCII') { 1621 1622 if (iil_C_Select($conn, $folder)) { 1623 1624 $encoding = $encoding ? trim($encoding) : 'US-ASCII'; 1625 $algorithm = $algorithm ? trim($algorithm) : 'REFERENCES'; 1626 $criteria = $criteria ? 'ALL '.trim($criteria) : 'ALL'; 1627 1628 iil_PutLineC($conn->fp, "thrd1 THREAD $algorithm $encoding $criteria"); 1629 do { 1630 $line = trim(iil_ReadLine($conn->fp, 10000)); 1631 if (preg_match('/^\* THREAD/', $line)) { 1632 $str = trim(substr($line, 8)); 1633 $depthmap = array(); 1634 $haschildren = array(); 1635 $tree = iil_ParseThread($str, 0, strlen($str), null, null, 0, $depthmap, $haschildren); 1636 } 1637 } while (!iil_StartsWith($line, 'thrd1', true)); 1638 1639 $result_code = iil_ParseResult($line); 1640 if ($result_code == 0) { 1641 return array($tree, $depthmap, $haschildren); 1642 } 1643 $conn->error = 'iil_C_Thread: ' . $line . "\n"; 1644 return false; 1645 } 1646 $conn->error = "iil_C_Thread: Couldn't select \"$folder\"\n"; 1647 return false; 1648 } 1649 1881 1650 function iil_C_Search(&$conn, $folder, $criteria) { 1882 1651 -
trunk/roundcubemail/program/localization/de_CH/labels.inc
r3116 r3367 128 128 $labels['invert'] = 'Umkehren'; 129 129 $labels['filter'] = 'Filter'; 130 131 $labels['list'] = 'Liste'; 132 $labels['threads'] = 'Konversationen'; 133 $labels['expand-all'] = 'All aufklappen'; 134 $labels['expand-unread'] = 'Ungelesene aufklappen'; 135 $labels['collapse-all'] = 'Alle zuklappen'; 136 $labels['threaded'] = 'Gruppiert'; 137 138 $labels['autoexpand_threads'] = 'Konversationen aufklappen'; 139 $labels['do_expand'] = 'alle'; 140 $labels['expand_only_unread'] = 'nur ungelesene'; 141 $labels['fromto'] = 'Sender/EmpfÀnger'; 142 $labels['flag'] = 'Markierung'; 143 $labels['attachment'] = 'Anhang'; 144 $labels['nonesort'] = 'Keine'; 145 $labels['sentdate'] = 'Sendedatum'; 146 $labels['arrival'] = 'Empfangsdatum'; 147 $labels['asc'] = 'aufsteigend'; 148 $labels['desc'] = 'absteigend'; 149 $labels['listcolumns'] = 'Spalten'; 150 $labels['listsorting'] = 'Sortierung'; 151 $labels['listorder'] = 'Ordnung'; 152 $labels['listmode'] = 'Anzeigemodus'; 153 130 154 $labels['compact'] = 'Packen'; 131 155 $labels['empty'] = 'Leeren'; -
trunk/roundcubemail/program/localization/en_US/labels.inc
r3327 r3367 57 57 $labels['mailboxlist'] = 'Folders'; 58 58 $labels['messagesfromto'] = 'Messages $from to $to of $count'; 59 $labels['threadsfromto'] = 'Threads $from to $to of $count'; 59 60 $labels['messagenrof'] = 'Message $nr of $count'; 60 61 … … 150 151 $labels['invert'] = 'Invert'; 151 152 $labels['filter'] = 'Filter'; 153 154 $labels['list'] = 'List'; 155 $labels['threads'] = 'Threads'; 156 $labels['expand-all'] = 'Expand All'; 157 $labels['expand-unread'] = 'Expand Unread'; 158 $labels['collapse-all'] = 'Collapse All'; 159 $labels['threaded'] = 'Threaded'; 160 161 $labels['autoexpand_threads'] = 'Expand message threads'; 162 $labels['do_expand'] = 'all threads'; 163 $labels['expand_only_unread'] = 'only with unread messages'; 164 $labels['fromto'] = 'Sender/Recipient'; 165 $labels['flag'] = 'Flag'; 166 $labels['attachment'] = 'Attachment'; 167 $labels['nonesort'] = 'None'; 168 $labels['sentdate'] = 'Sent date'; 169 $labels['arrival'] = 'Arrival date'; 170 $labels['asc'] = 'ascending'; 171 $labels['desc'] = 'descending'; 172 $labels['listcolumns'] = 'List columns'; 173 $labels['listsorting'] = 'Sorting column'; 174 $labels['listorder'] = 'Sorting order'; 175 $labels['listmode'] = 'List view mode'; 152 176 153 177 $labels['compact'] = 'Compact'; … … 309 333 $labels['checkallfolders'] = 'Check all folders for new messages'; 310 334 $labels['displaynext'] = 'After message delete/move display the next message'; 311 $labels['indexsort'] = 'Use message index for sorting by date';312 335 $labels['mainoptions'] = 'Main Options'; 313 336 $labels['section'] = 'Section'; -
trunk/roundcubemail/program/localization/pl_PL/labels.inc
r3327 r3367 141 141 $labels['select'] = 'Zaznacz'; 142 142 $labels['all'] = 'Wszystkie'; 143 $labels['none'] = ' Anuluj';143 $labels['none'] = 'Brak'; 144 144 $labels['unread'] = 'Nieprzeczytane'; 145 145 $labels['flagged'] = 'Oznaczone'; -
trunk/roundcubemail/program/steps/mail/check_recent.inc
r3308 r3367 33 33 34 34 // get overall message count; allow caching because rcube_imap::recent_uids() did a refresh 35 $all_count = $IMAP->messagecount( );35 $all_count = $IMAP->messagecount(NULL, $IMAP->threading ? 'THREADS' : 'ALL'); 36 36 37 37 $unread_count = $IMAP->messagecount(NULL, 'UNSEEN', TRUE); … … 39 39 40 40 $OUTPUT->set_env('messagecount', $all_count); 41 $OUTPUT->set_env('pagesize', $IMAP->page_size);42 41 $OUTPUT->set_env('pagecount', ceil($all_count/$IMAP->page_size)); 43 42 $OUTPUT->command('set_unread_count', $mbox_name, $unread_count, ($mbox_name == 'INBOX')); … … 57 56 continue; 58 57 59 // use SEARCH/SORT to find recent messages 60 $search_str = 'UID '.min($recents).':'.max($recents); 61 if ($search_request) 62 $search_str .= ' '.$IMAP->search_string; 58 if ($IMAP->threading) { 59 $OUTPUT->command('message_list.clear'); 60 $sort_col = isset($_SESSION['sort_col']) ? $_SESSION['sort_col'] : $CONFIG['message_sort_col']; 61 $sort_order = isset($_SESSION['sort_order']) ? $_SESSION['sort_order'] : $CONFIG['message_sort_order']; 62 $result_h = $IMAP->list_headers($mbox_name, NULL, $sort_col, $sort_order); 63 // add to the list 64 rcmail_js_message_list($result_h); 65 } 66 else { 67 // use SEARCH/SORT to find recent messages 68 $search_str = 'UID '.min($recents).':'.max($recents); 69 if ($search_request) 70 $search_str .= ' '.$IMAP->search_string; 63 71 64 if ($IMAP->search($mbox_name, $search_str, NULL, 'date')) { 65 // revert sort order 66 $order = $_SESSION['sort_col'] == 'date' && $_SESSION['sort_order'] == 'DESC' ? 'ASC' : 'DESC'; 67 // get the headers and add them to the list 68 $result_h = $IMAP->list_headers($mbox_name, 1, 'date', $order); 69 rcmail_js_message_list($result_h, true, false); 72 if ($IMAP->search($mbox_name, $search_str, NULL, 'date')) { 73 // revert sort order 74 $order = $_SESSION['sort_col'] == 'date' && $_SESSION['sort_order'] == 'DESC' ? 'ASC' : 'DESC'; 75 // get the headers and add them to the list 76 $result_h = $IMAP->list_headers($mbox_name, 1, 'date', $order); 77 rcmail_js_message_list($result_h, true, false); 78 } 70 79 } 71 80 } -
trunk/roundcubemail/program/steps/mail/func.inc
r3358 r3367 60 60 $_SESSION['sort_order'] = $CONFIG['message_sort_order']; 61 61 62 // set threads mode 63 $a_threading = $RCMAIL->config->get('message_threading', array()); 64 if (isset($_GET['_threads'])) { 65 if ($_GET['_threads']) 66 $a_threading[$_SESSION['mbox']] = true; 67 else 68 unset($a_threading[$_SESSION['mbox']]); 69 $RCMAIL->user->save_prefs(array('message_threading' => $a_threading)); 70 } 71 $IMAP->set_threading($a_threading[$_SESSION['mbox']]); 72 62 73 // set message set for search result 63 74 if (!empty($_REQUEST['_search']) && isset($_SESSION['search'][$_REQUEST['_search']])) … … 89 100 90 101 // make sure the message count is refreshed (for default view) 91 $IMAP->messagecount($mbox_name, 'ALL', true);102 $IMAP->messagecount($mbox_name, $IMAP->threading ? 'THREADS' : 'ALL', true); 92 103 } 93 104 94 // set current mailbox in client environment105 // set current mailbox and some other vars in client environment 95 106 $OUTPUT->set_env('mailbox', $mbox_name); 107 $OUTPUT->set_env('pagesize', $IMAP->page_size); 96 108 $OUTPUT->set_env('quota', $IMAP->get_capability('quota')); 97 109 $OUTPUT->set_env('delimiter', $IMAP->get_hierarchy_delimiter()); 110 $OUTPUT->set_env('threading', (bool) $IMAP->threading); 111 $OUTPUT->set_env('threads', $IMAP->threading 112 || $IMAP->get_capability('thread=references') 113 || $IMAP->get_capability('thread=orderedsubject') 114 || $IMAP->get_capability('thread=refs') 115 ); 98 116 99 117 if ($CONFIG['flag_for_deletion']) … … 124 142 */ 125 143 function rcmail_message_list($attrib) 126 { 127 global $IMAP, $CONFIG, $COMM_PATH, $OUTPUT; 128 129 $skin_path = $CONFIG['skin_path']; 130 $image_tag = '<img src="%s%s" alt="%s" />'; 131 132 // check to see if we have some settings for sorting 133 $sort_col = $_SESSION['sort_col']; 134 $sort_order = $_SESSION['sort_order']; 135 144 { 145 global $IMAP, $CONFIG, $OUTPUT; 146 136 147 // add some labels to client 137 148 $OUTPUT->add_label('from', 'to'); 138 139 // get message headers140 $a_headers = $IMAP->list_headers('', '', $sort_col, $sort_order);141 149 142 150 // add id to message list table if not specified 143 151 if (!strlen($attrib['id'])) 144 152 $attrib['id'] = 'rcubemessagelist'; 145 146 // allow the following attributes to be added to the <table> tag147 $attrib_str = create_attrib_string($attrib, array('style', 'class', 'id', 'cellpadding', 'cellspacing', 'border', 'summary'));148 149 $out = '<table' . $attrib_str . ">\n";150 153 151 154 // define list of cols to be displayed based on parameter or config … … 155 158 $a_show_cols = preg_split('/[\s,;]+/', strip_quotes($attrib['columns'])); 156 159 157 // s tore column list in a session-variable160 // save some variables for use in ajax list 158 161 $_SESSION['list_columns'] = $a_show_cols; 159 160 // define sortable columns 161 $a_sort_cols = array('subject', 'date', 'from', 'to', 'size'); 162 162 $_SESSION['list_attrib'] = $attrib; 163 163 164 $mbox = $IMAP->get_mailbox_name(); 164 165 $delim = $IMAP->get_hierarchy_delimiter(); … … 168 169 && (($f = array_search('from', $a_show_cols)) !== false) && array_search('to', $a_show_cols) === false) 169 170 $a_show_cols[$f] = 'to'; 170 171 // add col definition 172 $out .= '<colgroup>'; 173 $out .= '<col class="icon" />'; 174 175 foreach ($a_show_cols as $col) 176 $out .= ($col!='attachment') ? sprintf('<col class="%s" />', $col) : '<col class="icon" />'; 177 178 $out .= "</colgroup>\n"; 179 180 // add table title 181 $out .= "<thead><tr>\n<td class=\"icon\"> </td>\n"; 182 183 $javascript = ''; 184 foreach ($a_show_cols as $col) 185 { 186 // get column name 187 switch ($col) 188 { 189 case 'flag': 190 $col_name = sprintf($image_tag, $skin_path, $attrib['unflaggedicon'], ''); 191 break; 192 case 'attachment': 193 $col_name = sprintf($image_tag, $skin_path, $attrib['attachmenticon'], ''); 194 break; 195 default: 196 $col_name = Q(rcube_label($col)); 197 } 198 199 // make sort links 200 $sort = ''; 201 if (in_array($col, $a_sort_cols)) 202 { 203 // have buttons configured 204 if (!empty($attrib['sortdescbutton']) || !empty($attrib['sortascbutton'])) 205 { 206 $sort = ' '; 207 208 // asc link 209 if (!empty($attrib['sortascbutton'])) 210 { 211 $sort .= $OUTPUT->button(array( 212 'command' => 'sort', 213 'prop' => $col.'_ASC', 214 'image' => $attrib['sortascbutton'], 215 'align' => 'absmiddle', 216 'title' => 'sortasc')); 217 } 218 219 // desc link 220 if (!empty($attrib['sortdescbutton'])) 221 { 222 $sort .= $OUTPUT->button(array( 223 'command' => 'sort', 224 'prop' => $col.'_DESC', 225 'image' => $attrib['sortdescbutton'], 226 'align' => 'absmiddle', 227 'title' => 'sortdesc')); 228 } 229 } 230 // just add a link tag to the header 231 else 232 { 233 $col_name = sprintf( 234 '<a href="./#sort" onclick="return %s.command(\'sort\',\'%s\',this)" title="%s">%s</a>', 235 JS_OBJECT_NAME, 236 $col, 237 rcube_label('sortby'), 238 $col_name); 239 } 240 } 241 242 $sort_class = $col==$sort_col ? " sorted$sort_order" : ''; 243 244 // put it all together 245 if ($col!='attachment') 246 $out .= '<td class="'.$col.$sort_class.'" id="rcm'.$col.'">' . "$col_name$sort</td>\n"; 247 else 248 $out .= '<td class="icon" id="rcm'.$col.'">' . "$col_name$sort</td>\n"; 249 } 250 251 $out .= "</tr></thead>\n<tbody>\n"; 252 253 // no messages in this mailbox 254 if (!sizeof($a_headers)) 255 $OUTPUT->show_message('nomessagesfound', 'notice'); 256 257 $a_js_message_arr = array(); 258 259 // create row for each message 260 foreach ($a_headers as $i => $header) //while (list($i, $header) = each($a_headers)) 261 { 262 $message_icon = $attach_icon = $flagged_icon = ''; 263 $js_row_arr = array(); 264 $zebra_class = $i%2 ? ' even' : ' odd'; 265 266 // set messag attributes to javascript array 267 if ($header->deleted) 268 $js_row_arr['deleted'] = true; 269 if (!$header->seen) 270 $js_row_arr['unread'] = true; 271 if ($header->answered) 272 $js_row_arr['replied'] = true; 273 if ($header->forwarded) 274 $js_row_arr['forwarded'] = true; 275 if ($header->flagged) 276 $js_row_arr['flagged'] = true; 277 278 // set message icon 279 if ($attrib['deletedicon'] && $header->deleted) 280 $message_icon = $attrib['deletedicon']; 281 else if ($attrib['repliedicon'] && $header->answered) 282 { 283 if ($attrib['forwardedrepliedicon'] && $header->forwarded) 284 $message_icon = $attrib['forwardedrepliedicon']; 285 else 286 $message_icon = $attrib['repliedicon']; 287 } 288 else if ($attrib['forwardedicon'] && $header->forwarded) 289 $message_icon = $attrib['forwardedicon']; 290 else if ($attrib['unreadicon'] && !$header->seen) 291 $message_icon = $attrib['unreadicon']; 292 else if ($attrib['messageicon']) 293 $message_icon = $attrib['messageicon']; 294 295 if ($attrib['flaggedicon'] && $header->flagged) 296 $flagged_icon = $attrib['flaggedicon']; 297 else if ($attrib['unflaggedicon'] && !$header->flagged) 298 $flagged_icon = $attrib['unflaggedicon']; 299 300 // set attachment icon 301 if ($attrib['attachmenticon'] && preg_match("/multipart\/m/i", $header->ctype)) 302 $attach_icon = $attrib['attachmenticon']; 303 304 $out .= sprintf('<tr id="rcmrow%d" class="message%s%s%s%s">'."\n", 305 $header->uid, 306 $header->seen ? '' : ' unread', 307 $header->deleted ? ' deleted' : '', 308 $header->flagged ? ' flagged' : '', 309 $zebra_class); 310 311 $out .= sprintf("<td class=\"icon\">%s</td>\n", $message_icon ? sprintf($image_tag, $skin_path, $message_icon, '') : ''); 312 313 $IMAP->set_charset(!empty($header->charset) ? $header->charset : $CONFIG['default_charset']); 314 315 // format each col 316 foreach ($a_show_cols as $col) 317 { 318 if (in_array($col, array('from', 'to', 'cc', 'replyto'))) 319 $cont = Q(rcmail_address_string($header->$col, 3, false, $attrib['addicon']), 'show'); 320 else if ($col=='subject') 321 { 322 $action = $mbox==$CONFIG['drafts_mbox'] ? 'compose' : 'show'; 323 $uid_param = $mbox==$CONFIG['drafts_mbox'] ? '_draft_uid' : '_uid'; 324 $cont = abbreviate_string(trim($IMAP->decode_header($header->$col)), 160); 325 if (empty($cont)) $cont = rcube_label('nosubject'); 326 $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)); 327 } 328 else if ($col=='flag') 329 $cont = $flagged_icon ? sprintf($image_tag, $skin_path, $flagged_icon, '') : ''; 330 else if ($col=='size') 331 $cont = show_bytes($header->$col); 332 else if ($col=='date') 333 $cont = format_date($header->date); 334 else 335 $cont = Q($header->$col); 336 337 if ($col!='attachment') 338 $out .= '<td class="'.$col.'">' . $cont . "</td>\n"; 339 else 340 $out .= sprintf("<td class=\"icon\">%s</td>\n", $attach_icon ? sprintf($image_tag, $skin_path, $attach_icon, '') : ' '); 341 } 342 343 $out .= "</tr>\n"; 344 345 if (sizeof($js_row_arr)) 346 $a_js_message_arr[$header->uid] = $js_row_arr; 347 } 348 349 // complete message table 350 $out .= "</tbody></table>\n"; 351 352 $message_count = $IMAP->messagecount(); 171 172 $skin_path = $_SESSION['skin_path'] = $CONFIG['skin_path']; 173 $message_count = $IMAP->messagecount(NULL, $IMAP->threading ? 'THREADS' : 'ALL'); 353 174 354 175 // set client env 355 176 $OUTPUT->add_gui_object('mailcontframe', 'mailcontframe'); 356 177 $OUTPUT->add_gui_object('messagelist', $attrib['id']); 178 $OUTPUT->set_env('autoexpand_threads', intval($CONFIG['autoexpand_threads'])); 357 179 $OUTPUT->set_env('messagecount', $message_count); 358 180 $OUTPUT->set_env('current_page', $IMAP->list_page); 359 181 $OUTPUT->set_env('pagecount', ceil($message_count/$IMAP->page_size)); 360 $OUTPUT->set_env('sort_col', $ sort_col);361 $OUTPUT->set_env('sort_order', $ sort_order);182 $OUTPUT->set_env('sort_col', $_SESSION['sort_col']); 183 $OUTPUT->set_env('sort_order', $_SESSION['sort_order']); 362 184 363 185 if ($attrib['messageicon']) … … 379 201 if ($attrib['unflaggedicon']) 380 202 $OUTPUT->set_env('unflaggedicon', $skin_path . $attrib['unflaggedicon']); 381 382 $OUTPUT->set_env('messages', $a_js_message_arr); 203 if ($attrib['unreadchildrenicon']) 204 $OUTPUT->set_env('unreadchildrenicon', $skin_path . $attrib['unreadchildrenicon']); 205 206 $OUTPUT->set_env('messages', array()); 383 207 $OUTPUT->set_env('coltypes', $a_show_cols); 384 208 209 if (!$message_count) 210 $OUTPUT->show_message('nomessagesfound', 'notice'); 211 385 212 $OUTPUT->include_script('list.js'); 386 213 387 return $out; 388 } 214 $thead = ''; 215 foreach (rcmail_message_list_head($attrib, $a_show_cols) as $cell) 216 $thead .= html::tag('td', array('class' => $cell['className'], 'id' => $cell['id']), $cell['html']); 217 218 return html::tag('table', 219 $attrib, 220 html::tag('thead', null, html::tag('tr', null, $thead)) . 221 html::tag('tbody', null, ''), 222 array('style', 'class', 'id', 'cellpadding', 'cellspacing', 'border', 'summary')); 223 } 389 224 390 225 … … 393 228 * or to replace the whole list (IE only) 394 229 */ 395 function rcmail_js_message_list($a_headers, $insert_top=FALSE, $replace=TRUE )396 {230 function rcmail_js_message_list($a_headers, $insert_top=FALSE, $replace=TRUE, $head_replace=FALSE) 231 { 397 232 global $CONFIG, $IMAP, $OUTPUT; 398 233 … … 410 245 $a_show_cols[$f] = 'to'; 411 246 412 $browser = new rcube_browser; 413 414 $OUTPUT->command('set_message_coltypes', $a_show_cols); 247 $thead = $head_replace ? rcmail_message_list_head($_SESSION['list_attrib'], $a_show_cols) : NULL; 248 249 $OUTPUT->command('set_message_coltypes', $a_show_cols, $thead); 250 251 if (empty($a_headers)) 252 return; 415 253 416 254 // remove 'attachment' and 'flag' columns, we don't need them here … … 420 258 unset($a_show_cols[$key]); 421 259 422 if ($ browser->ie && $replace)260 if ($OUTPUT->browser->ie && $replace) 423 261 $OUTPUT->command('offline_message_list', true); 424 262 … … 441 279 else if ($col=='subject') 442 280 { 443 $cont = abbreviate_string(trim($IMAP->decode_header($header->$col)), 160);281 $cont = abbreviate_string(trim($IMAP->decode_header($header->$col)), 160); 444 282 if (!$cont) $cont = rcube_label('nosubject'); 445 $cont = Q($cont); 446 $a_msg_cols['mbox'] = $mbox; 283 $cont = Q($cont); 447 284 } 448 285 else if ($col=='size') … … 456 293 } 457 294 295 if ($header->depth) 296 $a_msg_flags['depth'] = $header->depth; 297 if ($header->parent_uid) 298 $a_msg_flags['parent_uid'] = $header->parent_uid; 299 if ($header->has_children) 300 $a_msg_flags['has_children'] = $header->has_children; 301 if ($header->unread_children) 302 $a_msg_flags['unread_children'] = $header->unread_children; 458 303 if ($header->deleted) 459 304 $a_msg_flags['deleted'] = 1; … … 466 311 if ($header->flagged) 467 312 $a_msg_flags['flagged'] = 1; 468 313 if(preg_match("/multipart\/m/i", $header->ctype)) 314 $a_msg_flags['attachment'] = 1; 315 $a_msg_flags['mbox'] = $mbox; 316 469 317 $OUTPUT->command('add_message_row', 470 318 $header->uid, 471 319 $a_msg_cols, 472 320 $a_msg_flags, 473 preg_match("/multipart\/m/i", $header->ctype),474 321 $insert_top); 475 322 } … … 478 325 $OUTPUT->command('offline_message_list', false); 479 326 } 327 328 329 /* 330 * Creates <THEAD> for message list table 331 */ 332 function rcmail_message_list_head($attrib, $a_show_cols) 333 { 334 global $CONFIG; 335 336 $skin_path = $_SESSION['skin_path']; 337 $image_tag = html::img(array('src' => "%s%s", 'alt' => "%s")); 338 339 // check to see if we have some settings for sorting 340 $sort_col = $_SESSION['sort_col']; 341 $sort_order = $_SESSION['sort_order']; 342 343 // define sortable columns 344 $a_sort_cols = array('subject', 'date', 'from', 'to', 'size', 'cc'); 345 346 if (!empty($attrib['optionsmenuicon'])) 347 $list_menu = html::a( 348 array('href' => '#', 'onclick' => 'return '.JS_OBJECT_NAME.".command('menu-open', 'messagelistmenu')"), 349 html::img(array('src' => $skin_path . $attrib['optionsmenuicon'], 'id' => 'listmenulink', 'title' => rcube_label('listoptions'))) 350 ); 351 else 352 $list_menu = ''; 353 354 $cells = array(array('className' => 'threads', 'html' => $list_menu)); 355 356 foreach ($a_show_cols as $col) { 357 // get column name 358 switch ($col) { 359 case 'flag': 360 $col_name = sprintf($image_tag, $skin_path, $attrib['unflaggedicon'], ''); 361 break; 362 case 'attachment': 363 $col_name = sprintf($image_tag, $skin_path, $attrib['attachmenticon'], ''); 364 break; 365 default: 366 $col_name = Q(rcube_label($col)); 367 } 368 369 // make sort links 370 if (in_array($col, $a_sort_cols)) 371 $col_name = html::a(array('href'=>"./#sort", 'onclick' => 'return '.JS_OBJECT_NAME.".command('sort','".$col."',this)", 'title' => rcube_label('sortby')), $col_name); 372 373 $sort_class = $col == $sort_col ? " sorted$sort_order" : ''; 374 $class_name = $col == 'attachment' ? 'icon' : $col.$sort_class; 375 376 // put it all together 377 $cells[] = array('className' => $class_name, 'id' => "rcm$col", 'html' => $col_name); 378 } 379 380 return $cells; 381 } 480 382 481 383 … … 514 416 function rcmail_quota_display($attrib) 515 417 { 516 global $OUTPUT , $COMM_PATH;418 global $OUTPUT; 517 419 518 420 if (!$attrib['id']) … … 583 485 { 584 486 return rcube_label(array('name' => 'messagenrof', 585 'vars' => array('nr' => $MESSAGE->index+1,586 'count' => $count!==NULL ? $count : $IMAP->messagecount())));487 'vars' => array('nr' => $MESSAGE->index+1, 488 'count' => $count!==NULL ? $count : $IMAP->messagecount(NULL, 'ALL')))); // Only messages, no threads here 587 489 } 588 490 … … 591 493 592 494 $start_msg = ($page-1) * $IMAP->page_size + 1; 593 $max = $count!==NULL ? $count : $IMAP->messagecount( );495 $max = $count!==NULL ? $count : $IMAP->messagecount(NULL, $IMAP->threading ? 'THREADS' : 'ALL'); 594 496 595 497 if ($max==0) 596 498 $out = rcube_label('mailboxempty'); 597 499 else 598 $out = rcube_label(array('name' => 'messagesfromto',599 'vars' => array('from' => $start_msg,600 'to' => min($max, $start_msg + $IMAP->page_size - 1),601 'count' => $max)));500 $out = rcube_label(array('name' => $IMAP->threading ? 'threadsfromto' : 'messagesfromto', 501 'vars' => array('from' => $start_msg, 502 'to' => min($max, $start_msg + $IMAP->page_size - 1), 503 'count' => $max))); 602 504 603 505 return Q($out); -
trunk/roundcubemail/program/steps/mail/list.inc
r2983 r3367 34 34 $_SESSION['sort_col'] = $save_arr['message_sort_col'] = $sort_col; 35 35 $_SESSION['sort_order'] = $save_arr['message_sort_order'] = $sort_order; 36 37 $RCMAIL->user->save_prefs($save_arr);38 36 } 39 37 else … … 43 41 $sort_order = isset($_SESSION['sort_order']) ? $_SESSION['sort_order'] : $CONFIG['message_sort_order']; 44 42 } 43 44 // is there a set of columns for this request? 45 if ($cols = get_input_value('_cols', RCUBE_INPUT_GET)) 46 { 47 $save_arr = array(); 48 $_SESSION['list_columns'] = $save_arr['list_cols'] = explode(',', $cols); 49 } 50 51 if ($save_arr) 52 $RCMAIL->user->save_prefs($save_arr); 45 53 46 54 $mbox_name = $IMAP->get_mailbox_name(); … … 56 64 57 65 // fetch message headers 58 if ($count = $IMAP->messagecount($mbox_name, 'ALL', !empty($_REQUEST['_refresh'])))66 if ($count = $IMAP->messagecount($mbox_name, $IMAP->threading ? 'THREADS' : 'ALL', !empty($_REQUEST['_refresh']))) 59 67 $a_headers = $IMAP->list_headers($mbox_name, NULL, $sort_col, $sort_order); 68 69 // update search set (possible change of threading mode) 70 if (!empty($_REQUEST['_search']) && isset($_SESSION['search'][$_REQUEST['_search']])) 71 $_SESSION['search'][$_REQUEST['_search']] = $IMAP->get_search_set(); 60 72 61 73 // update mailboxlist … … 66 78 $OUTPUT->set_env('messagecount', $count); 67 79 $OUTPUT->set_env('pagecount', $pages); 80 $OUTPUT->set_env('threading', (bool) $IMAP->threading); 68 81 $OUTPUT->command('set_rowcount', rcmail_get_messagecount_text($count)); 69 82 $OUTPUT->command('set_mailboxname', rcmail_get_mailbox_name_text()); 70 83 71 84 // add message rows 85 rcmail_js_message_list($a_headers, FALSE, TRUE, (bool) $cols); 72 86 if (isset($a_headers) && count($a_headers)) 73 87 { 74 rcmail_js_message_list($a_headers);75 88 if ($search_request) 76 89 $OUTPUT->show_message('searchsuccessful', 'confirmation', array('nr' => $count)); -
trunk/roundcubemail/program/steps/mail/mark.inc
r3317 r3367 37 37 if ($flag == 'DELETED' && $CONFIG['skip_deleted'] && $_POST['_from'] != 'show') { 38 38 // count messages before changing anything 39 $old_count = $IMAP->messagecount( );39 $old_count = $IMAP->messagecount(NULL, $IMAP->threading ? 'THREADS' : 'ALL'); 40 40 $old_pages = ceil($old_count / $IMAP->page_size); 41 41 $count = sizeof(explode(',', $uids)); … … 76 76 } 77 77 78 $msg_count = $IMAP->messagecount( );78 $msg_count = $IMAP->messagecount(NULL, $IMAP->threading ? 'THREADS' : 'ALL'); 79 79 $pages = ceil($msg_count / $IMAP->page_size); 80 80 $nextpage_count = $old_count - $IMAP->page_size * $IMAP->list_page; … … 104 104 $OUTPUT->command('set_rowcount', rcmail_get_messagecount_text($msg_count)); 105 105 106 if ($IMAP->threading) 107 $count = get_input_value('_count', RCUBE_INPUT_POST); 108 106 109 // add new rows from next page (if any) 107 if ( ($jump_back || $nextpage_count > 0)) {110 if ($count && ($jump_back || $nextpage_count > 0)) { 108 111 $sort_col = isset($_SESSION['sort_col']) ? $_SESSION['sort_col'] : $CONFIG['message_sort_col']; 109 112 $sort_order = isset($_SESSION['sort_order']) ? $_SESSION['sort_order'] : $CONFIG['message_sort_order']; -
trunk/roundcubemail/program/steps/mail/move_del.inc
r3317 r3367 25 25 26 26 // count messages before changing anything 27 $old_count = $IMAP->messagecount( );27 $old_count = $IMAP->messagecount(NULL, $IMAP->threading ? 'THREADS' : 'ALL'); 28 28 $old_pages = ceil($old_count / $IMAP->page_size); 29 29 … … 51 51 $count = sizeof(explode(',', ($uids = get_input_value('_uid', RCUBE_INPUT_POST)))); 52 52 $mbox = get_input_value('_mbox', RCUBE_INPUT_POST); 53 53 54 $del = $IMAP->delete_message($uids, $mbox); 54 55 … … 83 84 else 84 85 { 85 $msg_count = $IMAP->messagecount( );86 $msg_count = $IMAP->messagecount(NULL, $IMAP->threading ? 'THREADS' : 'ALL'); 86 87 $pages = ceil($msg_count / $IMAP->page_size); 87 88 $nextpage_count = $old_count - $IMAP->page_size * $IMAP->list_page; … … 117 118 $OUTPUT->command('set_rowcount', rcmail_get_messagecount_text($msg_count)); 118 119 120 if ($IMAP->threading) 121 $count = get_input_value('_count', RCUBE_INPUT_POST); 122 119 123 // add new rows from next page (if any) 120 if ($addrows && ($jump_back || $nextpage_count > 0)) {124 if ($addrows && $count && ($jump_back || $nextpage_count > 0)) { 121 125 $sort_col = isset($_SESSION['sort_col']) ? $_SESSION['sort_col'] : $CONFIG['message_sort_col']; 122 126 $sort_order = isset($_SESSION['sort_order']) ? $_SESSION['sort_order'] : $CONFIG['message_sort_order']; -
trunk/roundcubemail/program/steps/mail/search.inc
r3313 r3367 105 105 // Get the headers 106 106 $result_h = $IMAP->list_headers($mbox, 1, $_SESSION['sort_col'], $_SESSION['sort_order']); 107 $count = $IMAP->messagecount( );107 $count = $IMAP->messagecount(NULL, $IMAP->threading ? 'THREADS' : 'ALL'); 108 108 109 109 // save search results in session … … 121 121 rcmail_js_message_list($result_h); 122 122 if ($search_str) 123 $OUTPUT->show_message('searchsuccessful', 'confirmation', array('nr' => $ count));123 $OUTPUT->show_message('searchsuccessful', 'confirmation', array('nr' => $IMAP->messagecount(NULL, 'ALL'))); 124 124 } 125 125 else -
trunk/roundcubemail/program/steps/mail/show.inc
r3238 r3367 105 105 106 106 if ($_SESSION['sort_col'] == 'date' && $_SESSION['sort_order'] != 'DESC' 107 && empty($_REQUEST['_search']) && !$ IMAP->skip_deleted)107 && empty($_REQUEST['_search']) && !$CONFIG['skip_deleted'] && !$IMAP->threading) 108 108 { 109 109 // this assumes that we are sorted by date_DESC … … 143 143 if (!$MESSAGE->headers->seen) 144 144 $RCMAIL->plugins->exec_hook('message_read', array('uid' => $MESSAGE->uid, 145 'mailbox' => $ IMAP->mailbox, 'message' => $MESSAGE));145 'mailbox' => $mbox_name, 'message' => $MESSAGE)); 146 146 } 147 147 -
trunk/roundcubemail/program/steps/mail/viewsource.inc
r3231 r3367 26 26 { 27 27 $headers = $IMAP->get_headers($uid); 28 $charset = $headers->charset ? $headers->charset : $ IMAP->default_charset;28 $charset = $headers->charset ? $headers->charset : $CONFIG['default_charset']; 29 29 header("Content-Type: text/plain; charset={$charset}"); 30 30 -
trunk/roundcubemail/program/steps/settings/func.inc
r3327 r3367 249 249 } 250 250 251 // Show checkbox for toggling 'index_sort'252 if (!isset($no_override['index_sort'])) {253 $field_id = 'rcmfd_indexsort';254 $input_indexsort = new html_checkbox(array('name' => '_index_sort', 'id' => $field_id, 'value' => 1));255 256 $blocks['list']['options']['index_sort'] = array(257 'title' => html::label($field_id, Q(rcube_label('indexsort'))),258 'content' => $input_indexsort->show($config['index_sort']?1:0),259 );260 }261 262 251 // show drop-down for available skins 263 252 if (!isset($no_override['skin'])) { … … 309 298 'title' => html::label($field_id, Q(rcube_label('mdnrequests'))), 310 299 'content' => $select_mdn_requests->show($config['mdn_requests']), 300 ); 301 } 302 303 if (!isset($no_override['autoexpand_threads'])) { 304 $field_id = 'rcmfd_autoexpand_threads'; 305 $select_autoexpand_threads = new html_select(array('name' => '_autoexpand_threads', 'id' => $field_id)); 306 $select_autoexpand_threads->add(rcube_label('never'), 0); 307 $select_autoexpand_threads->add(rcube_label('do_expand'), 1); 308 $select_autoexpand_threads->add(rcube_label('expand_only_unread'), 2); 309 310 $blocks['main']['options']['autoexpand_threads'] = array( 311 'title' => html::label($field_id, Q(rcube_label('autoexpand_threads'))), 312 'content' => $select_autoexpand_threads->show($config['autoexpand_threads']), 311 313 ); 312 314 } -
trunk/roundcubemail/program/steps/settings/manage_folders.inc
r3278 r3367 39 39 } 40 40 41 // enable threading for one or more mailboxes 42 else if ($RCMAIL->action=='enable-threading') 43 { 44 if ($mbox = get_input_value('_mbox', RCUBE_INPUT_POST, false, 'UTF7-IMAP')) 45 rcube_set_threading($mbox, true); 46 } 47 48 // enable threading for one or more mailboxes 49 else if ($RCMAIL->action=='disable-threading') 50 { 51 if ($mbox = get_input_value('_mbox', RCUBE_INPUT_POST, false, 'UTF7-IMAP')) 52 rcube_set_threading($mbox, false); 53 } 54 41 55 // create a new mailbox 42 56 else if ($RCMAIL->action=='create-folder') … … 78 92 79 93 $rename = $IMAP->rename_mailbox($oldname, $name); 94 } 95 96 // update per-folder options for modified folder and its subfolders 97 if ($rename) { 98 $a_threaded = $RCMAIL->config->get('message_threading', array()); 99 $delimiter = $IMAP->get_hierarchy_delimiter(); 100 $oldprefix = '/^' . preg_quote($oldname . $delimiter, '/') . '/'; 101 foreach ($a_threaded as $key => $val) 102 if ($key == $oldname) { 103 unset($a_threaded[$key]); 104 $a_threaded[$name] = true; 105 } 106 else if (preg_match($oldprefix, $key)) { 107 unset($a_threaded[$key]); 108 $a_threaded[preg_replace($oldprefix, $name.$delimiter, $key)] = true; 109 } 110 111 $RCMAIL->user->save_prefs(array('message_threading' => $a_threaded)); 80 112 } 81 113 … … 160 192 function rcube_subscription_form($attrib) 161 193 { 162 global $IMAP, $CONFIG, $OUTPUT; 194 global $RCMAIL, $IMAP, $CONFIG, $OUTPUT; 195 196 $threading_supported = $IMAP->get_capability('thread=references') 197 || $IMAP->get_capability('thread=orderedsubject') 198 || $IMAP->get_capability('thread=refs'); 163 199 164 200 list($form_start, $form_end) = get_form_tags($attrib, 'folders'); … … 174 210 $table->add_header('msgcount', rcube_label('messagecount')); 175 211 $table->add_header('subscribed', rcube_label('subscribed')); 212 if ($threading_supported) 213 $table->add_header('threaded', rcube_label('threaded')); 176 214 $table->add_header('rename', ' '); 177 215 $table->add_header('delete', ' '); 178 216 179 180 217 // get folders from server 181 218 $IMAP->clear_cache('mailboxes'); … … 183 220 $a_unsubscribed = $IMAP->list_unsubscribed(); 184 221 $a_subscribed = $IMAP->list_mailboxes(); 222 $a_threaded = $a_threaded_copy = $RCMAIL->config->get('message_threading', array()); 185 223 $delimiter = $IMAP->get_hierarchy_delimiter(); 186 224 $a_js_folders = $seen_folders = $list_folders = array(); … … 204 242 } 205 243 244 unset($a_threaded_copy[$folder]); 245 206 246 $list_folders[] = array('id' => $folder, 'name' => $name, 'level' => $level); 207 247 $seen[$folder]++; 248 } 249 250 // remove 'message_threading' option for not existing folders 251 if ($a_threaded_copy) { 252 foreach ($a_threaded_copy as $key => $val) 253 unset($a_threaded[$key]); 254 unset($a_threaded_copy); 255 $RCMAIL->user->save_prefs(array('message_threading' => $a_threaded)); 208 256 } 209 257 … … 212 260 'onclick' => JS_OBJECT_NAME.".command(this.checked?'subscribe':'unsubscribe',this.value)", 213 261 )); 262 $checkbox_threaded = new html_checkbox(array( 263 'name' => '_threaded[]', 264 'onclick' => JS_OBJECT_NAME.".command(this.checked?'enable-threading':'disable-threading',this.value)", 265 )); 214 266 215 267 if (!empty($attrib['deleteicon'])) … … 227 279 $idx = $i + 1; 228 280 $subscribed = in_array($folder['id'], $a_subscribed); 281 $threaded = $a_threaded[$folder['id']]; 229 282 $protected = ($CONFIG['protect_default_folders'] == true && in_array($folder['id'], $CONFIG['default_imap_folders'])); 230 283 $classes = array($i%2 ? 'even' : 'odd'); … … 239 292 240 293 $table->add('name', Q($display_folder)); 241 $table->add('msgcount', ($folder['virtual'] ? '' : $IMAP->messagecount($folder['id']))); 294 $table->add('msgcount', ($folder['virtual'] ? '' : $IMAP->messagecount($folder['id']))); // XXX: Use THREADS or ALL? 242 295 $table->add('subscribed', ($protected || $folder['virtual']) ? ($subscribed ? ' •' : ' ') : 243 296 $checkbox_subscribe->show(($subscribed ? $folder_utf8 : ''), array('value' => $folder_utf8))); 297 if ($IMAP->get_capability('thread=references')) { 298 $table->add('threaded', 299 $checkbox_threaded->show(($threaded ? $folder_utf8 : ''), array('value' => $folder_utf8))); 300 } 244 301 245 302 // add rename and delete buttons … … 335 392 return $out; 336 393 } 394 395 396 // (un)set 'threading' for selected folder 397 function rcube_set_threading($mbox, $state=true) 398 { 399 global $RCMAIL; 400 $mbox = (array)$mbox; 401 $a_prefs = (array)$RCMAIL->config->get('message_threading'); 402 403 if ($state) { 404 foreach ($mbox as $box) 405 $a_prefs[$box] = true; 406 } 407 else { 408 foreach ($mbox as $box) 409 unset($a_prefs[$box]); 410 } 411 412 $RCMAIL->user->save_prefs(array('message_threading' => $a_prefs)); 413 } 414 337 415 338 416 $OUTPUT->set_pagetitle(rcube_label('folders')); -
trunk/roundcubemail/program/steps/settings/save_prefs.inc
r3327 r3367 33 33 'dst_active' => isset($_POST['_dst_active']) ? TRUE : FALSE, 34 34 'pagesize' => is_numeric($_POST['_pagesize']) ? max(2, intval($_POST['_pagesize'])) : $CONFIG['pagesize'], 35 'index_sort' => isset($_POST['_index_sort']) ? TRUE : FALSE,36 35 'prettydate' => isset($_POST['_pretty_date']) ? TRUE : FALSE, 37 36 'skin' => isset($_POST['_skin']) ? get_input_value('_skin', RCUBE_INPUT_POST) : $CONFIG['skin'], … … 43 42 'focus_on_new_message' => isset($_POST['_focus_on_new_message']) ? TRUE : FALSE, 44 43 'preview_pane' => isset($_POST['_preview_pane']) ? TRUE : FALSE, 44 'autoexpand_threads' => isset($_POST['_autoexpand_threads']) ? intval($_POST['_autoexpand_threads']) : 0, 45 45 'mdn_requests' => isset($_POST['_mdn_requests']) ? intval($_POST['_mdn_requests']) : 0, 46 46 'keep_alive' => isset($_POST['_keep_alive']) ? intval($_POST['_keep_alive'])*60 : $CONFIG['keep_alive'], -
trunk/roundcubemail/skins/default/common.css
r3248 r3367 538 538 ul.toolbarmenu 539 539 { 540 margin: 0;540 margin: -4px 0 -4px 0; 541 541 padding: 0; 542 542 list-style: none; … … 548 548 white-space: nowrap; 549 549 min-width: 130px; 550 margin: 3px -4px; 550 551 } 551 552 … … 554 555 display: block; 555 556 color: #a0a0a0; 556 padding: 2px 8px 3px 22px;557 padding: 2px 12px 3px 28px; 557 558 text-decoration: none; 558 559 min-height: 14px; … … 598 599 padding-top: 2px; 599 600 } 601 602 .disabled 603 { 604 color: #999; 605 } -
trunk/roundcubemail/skins/default/functions.js
r3337 r3367 125 125 this.searchmenu = $('#searchmenu'); 126 126 this.messagemenu = $('#messagemenu'); 127 this.listmenu = $('#listmenu'); 127 128 } 128 129 … … 187 188 }, 188 189 190 show_listmenu: function(show) 191 { 192 if (typeof show == 'undefined') 193 show = this.listmenu.is(':visible') ? false : true; 194 195 var ref = rcube_find_object('listmenulink'); 196 if (show && ref) { 197 var pos = $(ref).offset(); 198 this.listmenu.css({ left:pos.left, top:(pos.top + ref.offsetHeight + 2)}); 199 // set form values 200 $('input[name="sort_col"][value="'+rcmail.env.sort_col+'"]').attr('checked', 1); 201 $('input[name="sort_ord"][value="DESC"]').attr('checked', rcmail.env.sort_order=='DESC' ? 1 : 0); 202 $('input[name="sort_ord"][value="ASC"]').attr('checked', rcmail.env.sort_order=='DESC' ? 0 : 1); 203 $('input[name="view"][value="thread"]').attr('checked', rcmail.env.threading ? 1 : 0); 204 $('input[name="view"][value="list"]').attr('checked', rcmail.env.threading ? 0 : 1); 205 // list columns 206 var cols = $('input[name="list_col[]"]'); 207 for (var i=0; i<cols.length; i++) { 208 var found = 0; 209 if (cols[i].value != 'from') 210 found = jQuery.inArray(cols[i].value, rcmail.env.coltypes) != -1; 211 else 212 found = (jQuery.inArray('from', rcmail.env.coltypes) != -1 213 || jQuery.inArray('to', rcmail.env.coltypes) != -1); 214 $(cols[i]).attr('checked',found ? 1 : 0); 215 } 216 } 217 218 this.listmenu[show?'show':'hide'](); 219 220 if (show) { 221 var maxheight=0; 222 $('#listmenu fieldset').each(function() { 223 var height = $(this).height(); 224 if (height > maxheight) { 225 maxheight = height; 226 } 227 }); 228 $('#listmenu fieldset').css("min-height", maxheight+"px") 229 // IE6 complains if you set this attribute using either method: 230 //$('#listmenu fieldset').css({'height':'auto !important'}); 231 //$('#listmenu fieldset').css("height","auto !important"); 232 .height(maxheight); 233 }; 234 }, 235 236 open_listmenu: function(e) 237 { 238 this.show_listmenu(); 239 }, 240 241 save_listmenu: function() 242 { 243 this.show_listmenu(); 244 245 var sort = $('input[name="sort_col"]:checked').val(); 246 var ord = $('input[name="sort_ord"]:checked').val(); 247 var thread = $('input[name="view"]:checked').val(); 248 var cols = $('input[name="list_col[]"]:checked') 249 .map(function(){ return this.value; }).get(); 250 251 rcmail.set_list_options(cols, sort, ord, thread == 'thread' ? 1 : 0); 252 }, 253 189 254 body_mouseup: function(evt, p) 190 255 { 191 if (this.markmenu && this.markmenu.is(':visible') && rcube_event.get_target(evt) != rcube_find_object('markreadbutton')) 256 var target = rcube_event.get_target(evt); 257 258 if (this.markmenu && this.markmenu.is(':visible') && target != rcube_find_object('markreadbutton')) 192 259 this.show_markmenu(false); 193 else if (this.messagemenu && this.messagemenu.is(':visible') && rcube_event.get_target(evt)!= rcube_find_object('messagemenulink'))260 else if (this.messagemenu && this.messagemenu.is(':visible') && target != rcube_find_object('messagemenulink')) 194 261 this.show_messagemenu(false); 195 else if (this.searchmenu && this.searchmenu.is(':visible') && rcube_event.get_target(evt) != rcube_find_object('searchmod')) { 262 else if (this.listmenu && this.listmenu.is(':visible') && target != rcube_find_object('listmenulink')) { 263 var menu = rcube_find_object('listmenu'); 264 while (target.parentNode) { 265 if (target.parentNode == menu) 266 return; 267 target = target.parentNode; 268 } 269 this.show_listmenu(false); 270 } 271 else if (this.searchmenu && this.searchmenu.is(':visible') && target != rcube_find_object('searchmod')) { 196 272 var menu = rcube_find_object('searchmenu'); 197 var target = rcube_event.get_target(evt);198 273 while (target.parentNode) { 199 274 if (target.parentNode == menu) … … 214 289 if (this.messagemenu && this.messagemenu.is(':visible')) 215 290 this.show_messagemenu(false); 291 if (this.listmenu && this.listmenu.is(':visible')) 292 this.show_listmenu(false); 216 293 } 217 294 } … … 226 303 rcube_event.add_listener({ object:rcmail_ui, method:'body_mouseup', event:'mouseup' }); 227 304 rcube_event.add_listener({ object:rcmail_ui, method:'body_keypress', event:'keypress' }); 228 } 305 rcmail.addEventListener('menu-open', 'open_listmenu', rcmail_ui); 306 rcmail.addEventListener('menu-save', 'save_listmenu', rcmail_ui); 307 } -
trunk/roundcubemail/skins/default/ie6hacks.css
r3054 r3367 15 15 } 16 16 17 #messagemenu li a 17 #messagemenu li a, 18 #messagelist tr td div.expanded, 19 #messagelist tr td div.collapsed 18 20 { 19 21 background-image: url('images/messageactions.gif'); … … 48 50 } 49 51 50 ul.toolbarmenu li51 {52 width: auto;53 border: 1px solid #f6f6f6;54 }55 56 52 ul.toolbarmenu li a 57 53 { 58 54 clear: left; 59 55 } 56 57 ul.toolbarmenu li.separator_below 58 { 59 padding-bottom: 8px; 60 } -
trunk/roundcubemail/skins/default/iehacks.css
r3095 r3367 25 25 } 26 26 27 #markmessagemenu, 28 #searchmenu, 29 #messagemenu 30 { 31 -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=90)"; 32 filter: alpha(opacity=90); 33 27 .popupmenu 28 { 29 background-color: #ffffff; 34 30 } 35 31 … … 161 157 } 162 158 159 #messagelist tbody tr.unroot td.subject 160 { 161 text-decoration: underline; 162 } 163 163 164 #messageframe 164 165 { … … 244 245 } 245 246 247 ul.toolbarmenu 248 { 249 margin: 0 0 -4px 0; 250 } 251 246 252 ul.toolbarmenu li 247 253 { … … 264 270 height: 19px; 265 271 } 272 273 #listmenu fieldset 274 { 275 margin: 0 4px; 276 padding: 0.8em; 277 } -
trunk/roundcubemail/skins/default/includes/messagemenu.html
r2642 r3367 1 <div id="messagemenu" >1 <div id="messagemenu" class="popupmenu"> 2 2 <ul class="toolbarmenu"> 3 3 <li><roundcube:button class="printlink" command="print" label="printmessage" classAct="printlink active" /></li> -
trunk/roundcubemail/skins/default/mail.css
r3173 r3367 165 165 } 166 166 167 #markmessagemenu, 168 #searchmenu, 169 #messagemenu 167 .popupmenu 170 168 { 171 169 position: absolute; … … 174 172 width: auto; 175 173 display: none; 176 background-color: # F9F9F9;177 b order: 1px solid #CCC;178 padding: 1px;179 opacity: 0.9;174 background-color: #fff; 175 background-color: rgba(255, 255, 255, 0.95); 176 border: 1px solid #999; 177 padding: 4px; 180 178 z-index: 240; 179 border-radius: 3px; 180 -moz-border-radius: 3px; 181 -webkit-border-radius: 3px; 182 -moz-box-shadow: 1px 1px 12px #999; 183 -webkit-box-shadow: #999 1px 1px 12px; 181 184 } 182 185 183 186 #searchmenu 184 187 { 185 width: 172px; 188 width: 160px; 189 } 190 191 #searchmenu ul.toolbarmenu 192 { 193 margin: 0; 194 } 195 196 #searchmenu ul.toolbarmenu li 197 { 198 margin: 1px 4px 1px; 186 199 } 187 200 … … 195 208 #messagemenu li a 196 209 { 197 background: url('images/messageactions.png') no-repeat 1px 0;198 background-position: 0px 20px;210 background: url('images/messageactions.png') no-repeat 7px 0; 211 background-position: 7px 20px; 199 212 } 200 213 201 214 #messagemenu li a.printlink 202 215 { 203 background-position: 1px 1px;216 background-position: 7px 1px; 204 217 } 205 218 206 219 #messagemenu li a.downloadlink 207 220 { 208 background-position: 1px -17px;221 background-position: 7px -17px; 209 222 } 210 223 211 224 #messagemenu li a.sourcelink 212 225 { 213 background-position: 1px -35px;226 background-position: 7px -35px; 214 227 } 215 228 216 229 #messagemenu li a.openlink 217 230 { 218 background-position: 1px -53px;231 background-position: 7px -53px; 219 232 } 220 233 221 234 #messagemenu li a.editlink 222 235 { 223 background-position: 1px -71px;236 background-position: 7px -71px; 224 237 } 225 238 226 239 #markmessagemenu a.readlink 227 240 { 228 background: url('images/icons/dot.png') no-repeat 2px;241 background: url('images/icons/dot.png') no-repeat 7px 2px; 229 242 } 230 243 231 244 #markmessagemenu a.unreadlink 232 245 { 233 background: url('images/icons/unread.png') no-repeat 2px;246 background: url('images/icons/unread.png') no-repeat 7px 2px; 234 247 } 235 248 236 249 #markmessagemenu a.flaggedlink 237 250 { 238 background: url('images/icons/flagged.png') no-repeat 2px;251 background: url('images/icons/flagged.png') no-repeat 7px 2px; 239 252 } 240 253 241 254 #markmessagemenu a.unflaggedlink 242 255 { 243 background: url('images/icons/unflagged.png') no-repeat 2px;256 background: url('images/icons/unflagged.png') no-repeat 7px 2px; 244 257 } 245 258 … … 614 627 } 615 628 629 #listcontrols a.expand-all { 630 background-position: -90px 0; 631 } 632 633 #listcontrols a.expand-allsel { 634 background-position: -90px -15px; 635 } 636 637 #listcontrols a.collapse-all { 638 background-position: -105px 0; 639 } 640 641 #listcontrols a.collapse-allsel { 642 background-position: -105px -15px; 643 } 644 645 #listcontrols a.expand-unread { 646 background-position: -120px 0; 647 } 648 649 #listcontrols a.expand-unreadsel { 650 background-position: -120px -15px; 651 } 652 616 653 #countcontrols 617 654 { … … 721 758 } 722 759 760 #messagelist thead tr td.subject 761 { 762 padding-left: 22px; 763 } 764 723 765 #messagelist thead tr td.icon, 724 #messagelist thead tr td.flag 766 #messagelist thead tr td.flag, 767 #messagelist thead tr td.threads 725 768 { 726 769 width: 22px; … … 729 772 } 730 773 731 #messagelist tbody tr td.icon, 774 #messagelist thead tr td.threads 775 { 776 width: 18px; 777 } 778 732 779 #messagelist tbody tr td.flag 733 780 { … … 737 784 } 738 785 786 #messagelist tr td span.branch 787 { 788 display: inline-block; 789 width: 15px; 790 height: 15px; 791 } 792 793 #messagelist tr td.subject img.msgicon 794 { 795 vertical-align: middle; 796 } 797 798 #messagelist tbody td img.msgicon 799 { 800 position: relative; 801 top: 0px; 802 margin-right: 5px; 803 } 804 805 #messagelist tr td div.collapsed, 806 #messagelist tr td div.expanded, 807 #messagelist tr td img.flagicon, 808 #messagelist tr td img.msgicon 809 { 810 cursor: pointer; 811 } 812 813 #messagelist tr td div.collapsed 814 { 815 display: block; 816 background: url('images/messageactions.png') center -91px no-repeat; 817 } 818 819 #messagelist tr td div.expanded 820 { 821 display: block; 822 background: url('images/messageactions.png') center -109px no-repeat; 823 } 824 739 825 #messagelist tbody tr td.flag img:hover, 740 826 #messagelist thead tr td.flag img … … 748 834 vertical-align: middle; 749 835 width: 99%; 836 } 837 838 /* thread parent message with unread children */ 839 #messagelist tbody tr.unroot td.subject a 840 { 841 text-decoration: underline; 750 842 } 751 843 … … 1268 1360 font-weight: bold; 1269 1361 } 1362 1363 #listmenu 1364 { 1365 padding: 6px; 1366 } 1367 1368 #listmenu legend 1369 { 1370 color: #999999; 1371 } 1372 1373 #listmenu fieldset 1374 { 1375 border: 1px solid #999999; 1376 margin: 0 5px; 1377 float: left; 1378 } 1379 1380 #listmenu div 1381 { 1382 padding: 8px 0 3px 0; 1383 text-align: center; 1384 clear: both; 1385 } -
trunk/roundcubemail/skins/default/templates/mail.html
r3196 r3367 60 60 attachmentIcon="/images/icons/attachment.png" 61 61 flaggedIcon="/images/icons/flagged.png" 62 unflaggedIcon="/images/icons/blank.gif" /> 62 unflaggedIcon="/images/icons/blank.gif" 63 unreadchildrenIcon="" 64 optionsmenuIcon="/images/icons/columnpicker.gif" /> 63 65 </div> 64 66 … … 83 85 <roundcube:button command="select-all" type="link" prop="invert" title="invert" class="buttonPas invert" classAct="button invert" classSel="button invertsel" content=" " /> 84 86 <roundcube:button command="select-none" type="link" title="none" class="buttonPas none" classAct="button none" classSel="button nonesel" content=" " /> 87 <span style="margin-left: 20px"><roundcube:label name="threads" />: </span> 88 <roundcube:button command="expand-all" type="link" title="expand-all" class="buttonPas expand-all" classAct="button expand-all" classSel="button expand-allsel" content=" " /> 89 <roundcube:button command="expand-unread" type="link" title="expand-unread" class="buttonPas expand-unread" classAct="button expand-unread" classSel="button expand-unreadsel" content=" " /> 90 <roundcube:button command="collapse-all" type="link" title="collapse-all" class="buttonPas collapse-all" classAct="button collapse-all" classSel="button collapse-allsel" content=" " /> 85 91 <roundcube:container name="listcontrols" id="listcontrols" /> 86 92 <roundcube:if condition="env:quota" /> … … 112 118 <roundcube:button name="messagemenulink" id="messagemenulink" type="link" class="button messagemenu" title="messageactions" onclick="rcmail_ui.show_messagemenu();return false" content=" " /> 113 119 114 <div id="markmessagemenu" >120 <div id="markmessagemenu" class="popupmenu"> 115 121 <ul class="toolbarmenu"> 116 122 <li><roundcube:button command="mark" prop="read" label="markread" classAct="readlink active" class="readlink" /></li> … … 126 132 </div> 127 133 128 <div id="searchmenu" >134 <div id="searchmenu" class="popupmenu"> 129 135 <ul class="toolbarmenu"> 130 136 <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> … … 147 153 </div> 148 154 155 <div id="listmenu" class="popupmenu"> 156 <fieldset class="thinbordered"><legend><roundcube:label name="listmode" /></legend> 157 <ul class="toolbarmenu"> 158 <li><input type="radio" name="view" value="list" id="view_default" /><label for="view_default"><roundcube:label name="list" /></label></li> 159 <roundcube:if condition="env:threads" /> 160 <li><input type="radio" name="view" value="thread" id="view_thread" /><label for="view_thread"><roundcube:label name="threads" /></label></li> 161 <roundcube:else /> 162 <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> 163 <roundcube:endif /> 164 </ul> 165 </fieldset> 166 <fieldset class="thinbordered"><legend><roundcube:label name="listcolumns" /></legend> 167 <ul class="toolbarmenu"> 168 <li><input type="checkbox" name="list_col[]" value="flag" id="cols_flag" /><label for="cols_flag"><roundcube:label name="flag" /></label></li> 169 <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> 170 <li><input type="checkbox" name="list_col[]" value="from" id="cols_fromto" /><label for="cols_fromto"><roundcube:label name="fromto" /></label></li> 171 <li><input type="checkbox" name="list_col[]" value="replyto" id="cols_replyto" /><label for="cols_replyto"><roundcube:label name="replyto" /></label></li> 172 <li><input type="checkbox" name="list_col[]" value="cc" id="cols_cc" /><label for="cols_cc"><roundcube:label name="cc" /></label></li> 173 <li><input type="checkbox" name="list_col[]" value="date" id="cols_date" /><label for="cols_date"><roundcube:label name="date" /></label></li> 174 <li><input type="checkbox" name="list_col[]" value="size" id="cols_size" /><label for="cols_size"><roundcube:label name="size" /></label></li> 175 <li><input type="checkbox" name="list_col[]" value="attachment" id="cols_attachment" /><label for="cols_attachment"><roundcube:label name="attachment" /></label></li> 176 </ul> 177 </fieldset> 178 <fieldset class="thinbordered"><legend><roundcube:label name="listsorting" /></legend> 179 <ul class="toolbarmenu"> 180 <li><input type="radio" name="sort_col" value="" id="sort_default" /><label for="sort_default"><roundcube:label name="nonesort" /></label></li> 181 <li><input type="radio" name="sort_col" value="arrival" id="sort_arrival" /><label for="sort_arrival"><roundcube:label name="arrival" /></label></li> 182 <li><input type="radio" name="sort_col" value="date" id="sort_date" /><label for="sort_date"><roundcube:label name="sentdate" /></label></li> 183 <li><input type="radio" name="sort_col" value="subject" id="sort_subject" /><label for="sort_subject"><roundcube:label name="subject" /></label></li> 184 <li><input type="radio" name="sort_col" value="from" id="sort_fromto" /><label for="sort_fromto"><roundcube:label name="fromto" /></label></li> 185 <li><input type="radio" name="sort_col" value="to" id="sort_replyto" /><label for="sort_replyto"><roundcube:label name="replyto" /></label></li> 186 <li><input type="radio" name="sort_col" value="cc" id="sort_cc" /><label for="sort_cc"><roundcube:label name="cc" /></label></li> 187 <li><input type="radio" name="sort_col" value="size" id="sort_size" /><label for="sort_size"><roundcube:label name="size" /></label></li> 188 </ul> 189 </fieldset> 190 <fieldset><legend><roundcube:label name="listorder" /></legend> 191 <ul class="toolbarmenu"> 192 <li><input type="radio" name="sort_ord" value="ASC" id="sort_asc" /><label for="sort_asc"><roundcube:label name="asc" /></label></li> 193 <li><input type="radio" name="sort_ord" value="DESC" id="sort_desc" /><label for="sort_desc"><roundcube:label name="desc" /></label></li> 194 </ul> 195 </fieldset> 196 <div> 197 <roundcube:button command="menu-open" id="listmenucancel" type="input" class="button" label="cancel" /> 198 <roundcube:button command="menu-save" id="listmenusave" type="input" class="button mainaction" label="save" /> 199 </div> 200 </div> 201 149 202 </body> 150 203 </html>
Note: See TracChangeset
for help on using the changeset viewer.
