CHANGELOG
@@ -1,6 +1,7 @@ CHANGELOG Roundcube Webmail =========================== - Implemented menu actions to copy/move messages, added folder-selector widget (#1484086) - Fix regression where only first new folder was placed in correct place on the list (#1489472) - Fix issue where children of selected and collapsed thread were skipped on various actions (#1489457) - Fix issue where groups were not deleted when "Replace entire addressbook" option on contacts import was used (#1489420) program/include/rcmail.php
@@ -1282,13 +1282,22 @@ } else { $js_mailboxlist = array(); $out = html::tag('ul', $attrib, $rcmail->render_folder_tree_html($a_mailboxes, $mbox_name, $js_mailboxlist, $attrib), html::$common_attrib); $tree = $rcmail->render_folder_tree_html($a_mailboxes, $mbox_name, $js_mailboxlist, $attrib); if ($type != 'js') { $out = html::tag('ul', $attrib, $tree, html::$common_attrib); $rcmail->output->include_script('treelist.js'); $rcmail->output->add_gui_object('mailboxlist', $attrib['id']); $rcmail->output->set_env('mailboxes', $js_mailboxlist); $rcmail->output->set_env('unreadwrap', $attrib['unreadwrap']); $rcmail->output->set_env('collapsed_folders', (string)$rcmail->config->get('collapsed_folders')); } $rcmail->output->set_env('mailboxes', $js_mailboxlist); // we can't use object keys in javascript because they are unordered // we need sorted folders list for folder-selector widget $rcmail->output->set_env('mailboxes_list', array_keys($js_mailboxlist)); } return $out; @@ -1473,9 +1482,13 @@ $jslist[$folder['id']] = array( 'id' => $folder['id'], 'name' => $foldername, 'virtual' => $folder['virtual'] 'virtual' => $folder['virtual'], ); if (!empty($folder_class)) { $jslist[$folder['id']]['class'] = $folder_class; } if (!empty($folder['folders'])) { $out .= html::tag('ul', array('style' => ($is_collapsed ? "display:none;" : null)), $this->render_folder_tree_html($folder['folders'], $mbox_name, $jslist, $attrib, $nestLevel+1)); program/js/app.js
@@ -842,14 +842,14 @@ case 'move': case 'moveto': // deprecated if (this.task == 'mail') this.move_messages(props); this.move_messages(props, obj); else if (this.task == 'addressbook') this.move_contacts(props); break; case 'copy': if (this.task == 'mail') this.copy_messages(props); this.copy_messages(props, obj); else if (this.task == 'addressbook') this.copy_contacts(props); break; @@ -1411,8 +1411,6 @@ this.drag_start = function(list) { var model = this.task == 'mail' ? this.env.mailboxes : this.env.contactfolders; this.drag_active = true; if (this.preview_timer) @@ -2612,10 +2610,12 @@ }; // copy selected messages to the specified mailbox this.copy_messages = function(mbox) this.copy_messages = function(mbox, obj) { if (mbox && typeof mbox === 'object') mbox = mbox.id; else if (!mbox) return this.folder_selector(obj, function(folder) { ref.command('copy', folder); }); // exit if current or no mailbox specified if (!mbox || mbox == this.env.mailbox) @@ -2632,10 +2632,12 @@ }; // move selected messages to the specified mailbox this.move_messages = function(mbox) this.move_messages = function(mbox, obj) { if (mbox && typeof mbox === 'object') mbox = mbox.id; else if (!mbox) return this.folder_selector(obj, function(folder) { ref.command('move', folder); }); // exit if current or no mailbox specified if (!mbox || mbox == this.env.mailbox) @@ -6553,6 +6555,105 @@ elem.onclick = function() { rcmail.command('show-headers', '', elem); }; }; // create folder selector popup, position and display it this.folder_selector = function(obj, callback) { var container = this.folder_selector_element; if (!container) { var rows = [], delim = this.env.delimiter, ul = $('<ul class="toolbarmenu iconized">'), li = document.createElement('li'), link = document.createElement('a'), span = document.createElement('span'); container = $('<div id="folder-selector" class="popupmenu"></div>'); link.href = '#'; link.className = 'icon'; // loop over sorted folders list $.each(this.env.mailboxes_list, function() { var tmp, n = 0, s = 0, folder = ref.env.mailboxes[this], id = folder.id, a = link.cloneNode(false), row = li.cloneNode(false); if (folder.virtual) a.className += ' virtual'; else { a.className += ' active'; a.onclick = function() { container.hide().data('callback')(folder.id); }; } if (folder['class']) a.className += ' ' + folder['class']; // calculate/set indentation level while ((s = id.indexOf(delim, s)) >= 0) { n++; s++; } a.style.paddingLeft = n ? (n * 16) + 'px' : 0; // add folder name element tmp = span.cloneNode(false); $(tmp).text(folder.name); a.appendChild(tmp); row.appendChild(a); rows.push(row); }); ul.append(rows).appendTo(container); // temporarily show element to calculate its size container.css({left: '-1000px', top: '-1000px'}) .appendTo($('body')).show(); // set max-height if the list is long if (rows.length > 10) container.css('max-height', $('li', container)[0].offsetHeight * 10 + 9) // hide selector on click out of selector element var fn = function(e) { if (e.target != container.get(0)) container.hide(); }; $(document.body).on('mouseup', fn); $('iframe').contents().on('mouseup', fn) .load(function(e) { try { $(this).contents().on('mouseup', fn); } catch(e) {}; }); this.folder_selector_element = container; } // position menu on the screen this.element_position(container, obj); container.show().data('callback', callback); }; // position a menu element on the screen in relation to other object this.element_position = function(element, obj) { var obj = $(obj), win = $(window), width = obj.width(), height = obj.height(), win_height = win.height(), elem_height = $(element).height(), elem_width = $(element).width(), pos = obj.offset(), top = pos.top, left = pos.left + width; if (top + elem_height > win_height) { top -= elem_height - height; if (top < 0) top = Math.max(0, (win_height - elem_height) / 2); } if (left + elem_width > win.width()) left -= elem_width + width; element.css({left: left + 'px', top: top + 'px'}); }; /********************************************************/ /********* html to text conversion functions *********/ program/localization/en_US/labels.inc
@@ -64,6 +64,7 @@ $labels['copy'] = 'Copy'; $labels['move'] = 'Move'; $labels['moveto'] = 'Move to...'; $labels['copyto'] = 'Copy to...'; $labels['download'] = 'Download'; $labels['open'] = 'Open'; $labels['showattachment'] = 'Show'; skins/classic/common.css
@@ -965,6 +965,57 @@ } /*** folder selector ***/ #folder-selector li a { padding: 0; } #folder-selector li a span { background: url(images/icons/folders.png) no-repeat 6px 0; display: block; height: 15px; min-height: 14px; padding: 2px 4px 2px 28px; overflow: hidden; max-width: 120px; text-overflow: ellipsis; } #folder-selector li a.virtual { color: #A0A0A0; } #folder-selector li a.active:hover span { color: white; } #folder-selector li a.inbox span { background-position: 6px -18px; } #folder-selector li a.drafts span { background-position: 6px -37px; } #folder-selector li a.sent span { background-position: 6px -54px; } #folder-selector li a.trash span { background-position: 6px -91px; } #folder-selector li a.junk span { background-position: 6px -73px; } /***** tabbed interface elements *****/ div.tabsbar, skins/classic/functions.js
@@ -419,6 +419,7 @@ && !this.popups[i].toggle && (!this.popups[i].editable || !this.target_overlaps(target, this.popups[i].id)) && (!this.popups[i].sticky || !rcube_mouse_is_over(evt, rcube_find_object(this.popups[i].id))) && !$(target).is('.folder-selector-link') && !$(target).children('.folder-selector-link').length ) { window.setTimeout('rcmail_ui.show_popup("'+i+'",false);', 50); } skins/classic/includes/messagetoolbar.html
@@ -20,9 +20,6 @@ <roundcube:container name="toolbar" id="messagetoolbar" /> <roundcube:button name="markmenulink" id="markmenulink" type="link" class="button markmessage" title="markmessages" onclick="rcmail_ui.show_popup('markmenu');return false" content=" " /> <roundcube:button name="messagemenulink" id="messagemenulink" type="link" class="button messagemenu" title="moreactions" onclick="rcmail_ui.show_popup('messagemenu');return false" content=" " /> <roundcube:if condition="template:name == 'message'" /> <roundcube:object name="mailboxlist" type="select" noSelection="moveto" maxlength="25" onchange="rcmail.command('move', this.options[this.selectedIndex].value)" class="mboxlist" folder_filter="mail" /> <roundcube:endif /> </div> <div id="forwardmenu" class="popupmenu"> @@ -46,6 +43,8 @@ <li><roundcube:button class="printlink" command="print" label="printmessage" classAct="printlink active" /></li> <li><roundcube:button class="downloadlink" command="download" label="emlsave" classAct="downloadlink active" /></li> <li><roundcube:button class="editlink" command="edit" prop="new" label="editasnew" classAct="editlink active" /></li> <li><roundcube:button class="movelink" command="move" label="moveto" classAct="copylink active" innerclass="folder-selector-link" /></li> <li><roundcube:button class="copylink" command="copy" label="copyto" classAct="movelink active" innerclass="folder-selector-link" /></li> <li class="separator_below"><roundcube:button class="sourcelink" command="viewsource" label="viewsource" classAct="sourcelink active" /></li> <li><roundcube:button class="openlink" command="open" label="openinextwin" target="_blank" classAct="openlink active" /></li> <roundcube:container name="messagemenu" id="messagemenu" /> skins/classic/mail.css
@@ -175,23 +175,6 @@ background-position: -512px 0; } #messagetoolbar select.mboxlist { position: relative; margin: 0 8px; top: 7px; } #messagetoolbar select.mboxlist option { padding-left: 15px; } #messagetoolbar select.mboxlist option[value=""] { padding-left: 2px; } #messagemenu li a.active:hover, #attachmentmenu li a.active:hover, #markmessagemenu li a.active:hover skins/classic/templates/message.html
@@ -30,11 +30,13 @@ <div id="mailboxlist-container"> <div id="mailboxlist-title" class="boxtitle"><roundcube:label name="mailboxlist" /></div> <div class="boxlistcontent"> <roundcube:object name="mailboxlist" id="mailboxlist" class="treelist" maxlength="25" /> <roundcube:object name="mailboxlist" id="mailboxlist" class="treelist" folder_filter="mail" /> </div> <div class="boxfooter"></div> </div> </div> <roundcube:else /> <roundcube:object name="mailboxlist" folder_filter="mail" type="js" /> <roundcube:endif /> <div id="messageframe"> skins/larry/images/listicons.pngskins/larry/includes/mailtoolbar.html
@@ -39,6 +39,8 @@ <li><roundcube:button command="download" label="emlsave" class="icon" classAct="icon active" innerclass="icon download" /></li> <li><roundcube:button command="edit" prop="new" label="editasnew" class="icon" classAct="icon active" innerclass="icon edit" /></li> <li><roundcube:button command="viewsource" label="viewsource" class="icon" classAct="icon active" innerclass="icon viewsource" /></li> <li><roundcube:button command="move" label="moveto" class="icon" classAct="icon active" innerclass="icon move folder-selector-link" /></li> <li><roundcube:button command="copy" label="copyto" class="icon" classAct="icon active" innerclass="icon copy folder-selector-link" /></li> <li><roundcube:button command="open" label="openinextwin" target="_blank" class="icon" classAct="icon active" innerclass="icon extwin" /></li> <roundcube:container name="messagemenu" id="messagemenu" /> </ul> skins/larry/mail.css
@@ -417,12 +417,6 @@ right: 0; } #messagetoolbar .toolbarselect { position: absolute; bottom: 6px; right: 3px; } #messagesearchtools { position: absolute; right: 0; skins/larry/styles.css
@@ -2329,6 +2329,38 @@ z-index: 255; } /*** folder selector ***/ #folder-selector li a span { background: url("images/listicons.png") 4px -2021px no-repeat; display: block; height: 17px; min-height: 14px; padding: 4px 4px 1px 28px; overflow: hidden; max-width: 120px; text-overflow: ellipsis; } #folder-selector li a.virtual { opacity: .2; } #folder-selector li a.inbox span { background-position: 4px -2049px; } #folder-selector li a.drafts span { background-position: 4px -1388px; } #folder-selector li a.sent span { background-position: 4px -2074px; } #folder-selector li a.trash span { background-position: 4px -1508px; } #folder-selector li a.junk span { background-position: 4px -2100px; } /*** attachment list ***/ skins/larry/templates/message.html
@@ -16,9 +16,6 @@ <roundcube:button command="list" type="link" class="button back disabled" classAct="button back" classSel="button back pressed" label="back" /> <roundcube:endif /> <roundcube:include file="/includes/mailtoolbar.html" /> <div class="toolbarselect"> <roundcube:object name="mailboxlist" type="select" noSelection="moveto" maxlength="25" onchange="rcmail.command('move', this.options[this.selectedIndex].value)" class="mailboxlist decorated" folder_filter="mail" /> </div> </div> <roundcube:if condition="!env:extwin" /> @@ -36,6 +33,7 @@ <div id="mailview-right" class="offset uibox"> <roundcube:else /> <roundcube:object name="mailboxlist" folder_filter="mail" type="js" /> <div id="mailview-right" class="offset fullwidth uibox"> <roundcube:endif /> skins/larry/ui.js
@@ -369,8 +369,10 @@ function body_mouseup(e) { var config, obj, target = e.target; if (target.className == 'inner') target = e.target.parentNode; for (var id in popups) { obj = popups[id]; config = popupconfig[id]; @@ -379,9 +381,10 @@ && !config.toggle && (!config.editable || !target_overlaps(target, obj.get(0))) && (!config.sticky || !rcube_mouse_is_over(e, obj.get(0))) && !$(target).is('.folder-selector-link') ) { var myid = id+''; window.setTimeout(function(){ show_popupmenu(myid, false) }, 10); window.setTimeout(function() { show_popupmenu(myid, false); }, 10); } } }