From a45f9b7bf58475ccc812e819f159638403c00419 Mon Sep 17 00:00:00 2001 From: Aleksander Machniak <alec@alec.pl> Date: Mon, 01 Jul 2013 04:22:14 -0400 Subject: [PATCH] Contacts drag-n-drop default action is to move contacts (#1488751) Added possibility to choose to move or copy contacts from drag-n-drop menu (#1488751) Use consistent naming: 'moveto' -> 'move' --- program/js/app.js | 317 +++++++++++++++++++++++++++++++++++++++------------- 1 files changed, 236 insertions(+), 81 deletions(-) diff --git a/program/js/app.js b/program/js/app.js index cb08ce2..917fd8a 100644 --- a/program/js/app.js +++ b/program/js/app.js @@ -229,7 +229,7 @@ this.set_button_titles(); this.env.message_commands = ['show', 'reply', 'reply-all', 'reply-list', - 'moveto', 'copy', 'delete', 'open', 'mark', 'edit', 'viewsource', + 'move', 'copy', 'delete', 'open', 'mark', 'edit', 'viewsource', 'print', 'load-attachment', 'download-attachment', 'show-headers', 'hide-headers', 'download', 'forward', 'forward-inline', 'forward-attachment', 'change-format']; @@ -255,7 +255,8 @@ } } else if (this.env.action == 'compose') { - this.env.compose_commands = ['send-attachment', 'remove-attachment', 'send', 'cancel', 'toggle-editor', 'list-adresses', 'search', 'reset-search', 'extwin']; + this.env.address_group_stack = []; + this.env.compose_commands = ['send-attachment', 'remove-attachment', 'send', 'cancel', 'toggle-editor', 'list-adresses', 'pushgroup', 'search', 'reset-search', 'extwin']; if (this.env.drafts_mailbox) this.env.compose_commands.push('savedraft') @@ -276,6 +277,8 @@ // init message compose form this.init_messageform(); } + else if (this.env.action == 'get') + this.enable_command('download', 'print', true); // show printing dialog else if (this.env.action == 'print' && this.env.uid) { if (bw.safari) @@ -323,11 +326,13 @@ break; case 'addressbook': + this.env.address_group_stack = []; + if (this.gui_objects.folderlist) this.env.contactfolders = $.extend($.extend({}, this.env.address_sources), this.env.contactgroups); this.enable_command('add', 'import', this.env.writable_source); - this.enable_command('list', 'listgroup', 'listsearch', 'advanced-search', true); + this.enable_command('list', 'listgroup', 'pushgroup', 'popgroup', 'listsearch', 'advanced-search', true); if (this.gui_objects.contactslist) { this.contact_list = new rcube_list_widget(this.gui_objects.contactslist, @@ -371,7 +376,7 @@ } if (this.gui_objects.qsearchbox) - this.enable_command('search', 'reset-search', 'moveto', true); + this.enable_command('search', 'reset-search', true); break; @@ -790,16 +795,18 @@ // mail task commands case 'move': - case 'moveto': + case 'moveto': // deprecated if (this.task == 'mail') this.move_messages(props); else if (this.task == 'addressbook') - this.copy_contact(null, props); + this.move_contacts(props); break; case 'copy': if (this.task == 'mail') this.copy_messages(props); + else if (this.task == 'addressbook') + this.copy_contacts(props); break; case 'mark': @@ -861,7 +868,7 @@ // open attachment in frame if it's of a supported mimetype if (command != 'download-attachment' && mimetype && this.env.mimetypes && $.inArray(mimetype, this.env.mimetypes) >= 0) { - if (this.open_window(this.env.comm_path+'&_action=get&'+qstring+'&_frame=1', true, true)) + if (this.open_window(this.env.comm_path+'&_action=get&'+qstring+'&_frame=1')) break; } @@ -1045,7 +1052,10 @@ break; case 'print': - if (uid = this.get_single_uid()) { + if (this.env.action == 'get') { + this.gui_objects.messagepartframe.contentWindow.print(); + } + else if (uid = this.get_single_uid()) { ref.printwin = this.open_window(this.env.comm_path+'&_action=print&_uid='+uid+'&_mbox='+urlencode(this.env.mailbox)+(this.env.safemode ? '&_safe=1' : ''), true, true); if (this.printwin) { if (this.env.action != 'show') @@ -1060,7 +1070,10 @@ break; case 'download': - if (uid = this.get_single_uid()) + if (this.env.action == 'get') { + location.href = location.href.replace(/_frame=/, '_download='); + } + else if (uid = this.get_single_uid()) this.goto_url('viewsource', { _uid: uid, _mbox: this.env.mailbox, _save: 1 }); break; @@ -1097,9 +1110,23 @@ } break; + case 'pushgroup': + // add group ID to stack + this.env.address_group_stack.push(props.id); + if (obj && event) + rcube_event.cancel(event); + case 'listgroup': this.reset_qsearch(); this.list_contacts(props.source, props.id); + break; + + case 'popgroup': + if (this.env.address_group_stack.length > 1) { + this.env.address_group_stack.pop(); + this.reset_qsearch(); + this.list_contacts(props.source, this.env.address_group_stack[this.env.address_group_stack.length-1]); + } break; case 'import-messages': @@ -1325,7 +1352,7 @@ this.drag_menu = function(e, target) { var modkey = rcube_event.get_modifier(e), - menu = this.gui_objects.message_dragmenu; + menu = this.gui_objects.dragmenu; if (menu && modkey == SHIFT_KEY && this.commands['copy']) { var pos = rcube_event.get_mouse_pos(e); @@ -1339,7 +1366,7 @@ this.drag_menu_action = function(action) { - var menu = this.gui_objects.message_dragmenu; + var menu = this.gui_objects.dragmenu; if (menu) { $(menu).hide(); } @@ -1454,8 +1481,12 @@ list.draglayer.hide(); this.drag_end(e); - if (!this.drag_menu(e, target)) - this.command('moveto', target); + if (this.contact_list) { + if (!this.contacts_drag_menu(e, target)) + this.command('move', target); + } + else if (!this.drag_menu(e, target)) + this.command('move', target); } // reset 'pressed' buttons @@ -1502,7 +1533,7 @@ } } // Multi-message commands - this.enable_command('delete', 'moveto', 'copy', 'mark', 'forward', 'forward-attachment', list.selection.length > 0); + this.enable_command('delete', 'move', 'copy', 'mark', 'forward', 'forward-attachment', list.selection.length > 0); // reset all-pages-selection if (selected || (list.selection.length && list.selection.length != list.rowcount)) @@ -1605,28 +1636,28 @@ this.check_droptarget = function(id) { - if (this.task == 'mail') - return (this.env.mailboxes[id] && this.env.mailboxes[id].id != this.env.mailbox && !this.env.mailboxes[id].virtual) ? 1 : 0; + switch (this.task) { + case 'mail': + return (this.env.mailboxes[id] && this.env.mailboxes[id].id != this.env.mailbox && !this.env.mailboxes[id].virtual) ? 1 : 0; - if (this.task == 'settings') - return id != this.env.mailbox ? 1 : 0; + case 'settings': + return id != this.env.mailbox ? 1 : 0; - if (this.task == 'addressbook') { - if (id != this.env.source && this.env.contactfolders[id]) { - // droptarget is a group - contact add to group action - if (this.env.contactfolders[id].type == 'group') { - var target_abook = this.env.contactfolders[id].source; - if (this.env.contactfolders[id].id != this.env.group && !this.env.contactfolders[target_abook].readonly) { - // search result may contain contacts from many sources - return (this.env.selection_sources.length > 1 || $.inArray(target_abook, this.env.selection_sources) == -1) ? 2 : 1; + case 'addressbook': + var target; + if (id != this.env.source && (target = this.env.contactfolders[id])) { + // droptarget is a group + if (target.type == 'group') { + if (target.id != this.env.group && !this.env.contactfolders[target.source].readonly) { + var is_other = this.env.selection_sources.length > 1 || $.inArray(target.source, this.env.selection_sources) == -1; + return !is_other || this.commands.move ? 1 : 2; + } + } + // droptarget is a (writable) addressbook and it's not the source + else if (!target.readonly && (this.env.selection_sources.length > 1 || $.inArray(id, this.env.selection_sources) == -1)) { + return this.commands.move ? 1 : 2; } } - // droptarget is a (writable) addressbook - contact copy action - else if (!this.env.contactfolders[id].readonly) { - // search result may contain contacts from many sources - return (this.env.selection_sources.length > 1 || $.inArray(id, this.env.selection_sources) == -1) ? 2 : 0; - } - } } return 0; @@ -2118,12 +2149,12 @@ this.clear_message_list = function() { - this.env.messages = {}; - this.last_selected = 0; + this.env.messages = {}; + this.last_selected = 0; - this.show_contentframe(false); - if (this.message_list) - this.message_list.clear(true); + this.show_contentframe(false); + if (this.message_list) + this.message_list.clear(true); }; // send remote request to load message list @@ -2568,7 +2599,7 @@ // Hide message command buttons until a message is selected this.enable_command(this.env.message_commands, false); - this._with_selected_messages('moveto', post_data, lock); + this._with_selected_messages('move', post_data, lock); }; // delete selected messages from the current mailbox @@ -2627,7 +2658,7 @@ this._with_selected_messages('delete', post_data); }; - // Send a specifc moveto/delete request with UIDs of all selected messages + // Send a specifc move/delete request with UIDs of all selected messages // @private this._with_selected_messages = function(action, post_data, lock) { @@ -2669,7 +2700,7 @@ this.delete_excessive_thread_rows(); if (!lock) { - msg = action == 'moveto' ? 'movingmessage' : 'deletingmessage'; + msg = action == 'move' ? 'movingmessage' : 'deletingmessage'; lock = this.display_message(this.get_label(msg), 'loading'); } @@ -3091,7 +3122,13 @@ this.compose_recipient_select = function(list) { - this.enable_command('add-recipient', list.selection.length > 0); + var id, n, recipients = 0; + for (n=0; n < list.selection.length; n++) { + id = list.selection[n]; + if (this.env.contactdata[id]) + recipients++; + } + this.enable_command('add-recipient', recipients); }; this.compose_add_recipient = function(field) @@ -4109,7 +4146,7 @@ if (this.preview_timer) clearTimeout(this.preview_timer); - var n, id, sid, ref = this, writable = false, + var n, id, sid, contact, ref = this, writable = false, source = this.env.source ? this.env.address_sources[this.env.source] : null; // we don't have dblclick handler here, so use 200 instead of this.dblclick_time @@ -4119,34 +4156,45 @@ this.show_contentframe(false); if (list.selection.length) { + list.draggable = false; + // no source = search result, we'll need to detect if any of // selected contacts are in writable addressbook to enable edit/delete // we'll also need to know sources used in selection for copy // and group-addmember operations (drag&drop) this.env.selection_sources = []; - if (!source) { - for (n in list.selection) { + + if (source) { + this.env.selection_sources.push(this.env.source); + } + + for (n in list.selection) { + contact = list.data[list.selection[n]]; + if (!source) { sid = String(list.selection[n]).replace(/^[^-]+-/, ''); if (sid && this.env.address_sources[sid]) { - writable = writable || !this.env.address_sources[sid].readonly; + writable = writable || (!this.env.address_sources[sid].readonly && !contact.readonly); this.env.selection_sources.push(sid); } } - this.env.selection_sources = $.unique(this.env.selection_sources); + else { + writable = writable || (!source.readonly && !contact.readonly); + } + + if (contact._type != 'group') + list.draggable = true; } - else { - this.env.selection_sources.push(this.env.source); - writable = !source.readonly; - } + + this.env.selection_sources = $.unique(this.env.selection_sources); } // if a group is currently selected, and there is at least one contact selected // thend we can enable the group-remove-selected command - this.enable_command('group-remove-selected', this.env.group && list.selection.length > 0); + this.enable_command('group-remove-selected', this.env.group && list.selection.length > 0 && writable); this.enable_command('compose', this.env.group || list.selection.length > 0); - this.enable_command('export-selected', list.selection.length > 0); + this.enable_command('export-selected', 'copy', list.selection.length > 0); this.enable_command('edit', id && writable); - this.enable_command('delete', list.selection.length && writable); + this.enable_command('delete', 'move', list.selection.length > 0 && writable); return false; }; @@ -4174,10 +4222,28 @@ else if (!this.env.search_request) folder = group ? 'G'+src+group : src; - this.select_folder(folder, '', true); - this.env.source = src; this.env.group = group; + + // truncate groups listing stack + var index = $.inArray(this.env.group, this.env.address_group_stack); + if (index < 0) + this.env.address_group_stack = []; + else + this.env.address_group_stack = this.env.address_group_stack.slice(0,index); + + // make sure the current group is on top of the stack + if (this.env.group) { + this.env.address_group_stack.push(this.env.group); + + // mark the first group on the stack as selected in the directory list + folder = 'G'+src+this.env.address_group_stack[0]; + } + else if (this.gui_objects.addresslist_title) { + $(this.gui_objects.addresslist_title).html(this.get_label('contacts')); + } + + this.select_folder(folder, '', true); // load contacts remotely if (this.gui_objects.contactslist) { @@ -4233,16 +4299,38 @@ this.list_contacts_clear = function() { + this.contact_list.data = {}; this.contact_list.clear(true); this.show_contentframe(false); - this.enable_command('delete', false); + this.enable_command('delete', 'move', 'copy', false); this.enable_command('compose', this.env.group ? true : false); + }; + + this.set_group_prop = function(prop) + { + if (this.gui_objects.addresslist_title) { + var boxtitle = $(this.gui_objects.addresslist_title).html(''); // clear contents + + // add link to pop back to parent group + if (this.env.address_group_stack.length > 1) { + $('<a href="#list">...</a>') + .addClass('poplink') + .appendTo(boxtitle) + .click(function(e){ return ref.command('popgroup','',this); }); + boxtitle.append(' » '); + } + + boxtitle.append($('<span>'+prop.name+'</span>')); + } + + this.triggerEvent('groupupdate', prop); }; // load contact record this.load_contact = function(cid, action, framed) { - var win, url = {}, target = window; + var win, url = {}, target = window, + rec = this.contact_list ? this.contact_list.data[cid] : null; if (win = this.get_frame_window(this.env.contentframe)) { url._framed = 1; @@ -4252,7 +4340,9 @@ // load dummy content, unselect selected row(s) if (!cid) this.contact_list.clear_selection(); - this.enable_command('delete', 'compose', 'export-selected', cid); + + this.enable_command('compose', rec && rec.email); + this.enable_command('export-selected', rec && rec._type != 'group'); } else if (framed) return false; @@ -4282,14 +4372,38 @@ this.http_post('group-'+what+'members', post_data, lock); }; - // copy a contact to the specified target (group or directory) - this.copy_contact = function(cid, to) + this.contacts_drag_menu = function(e, to) + { + var dest = to.type == 'group' ? to.source : to.id, + source = this.env.source; + + if (!this.env.address_sources[dest] || this.env.address_sources[dest].readonly) + return true; + + // search result may contain contacts from many sources, but if there is only one... + if (source == '' && this.env.selection_sources.length == 1) + source = this.env.selection_sources[0]; + + if (to.type == 'group' && dest == source) { + var cid = this.contact_list.get_selection().join(','); + this.group_member_change('add', cid, dest, to.id); + return true; + } + // move action is not possible, "redirect" to copy if menu wasn't requested + else if (!this.commands.move && rcube_event.get_modifier(e) != SHIFT_KEY) { + this.copy_contacts(to); + return true; + } + + return this.drag_menu(e, to); + }; + + // copy contact(s) to the specified target (group or directory) + this.copy_contacts = function(to) { var n, dest = to.type == 'group' ? to.source : to.id, source = this.env.source, - group = this.env.group ? this.env.group : ''; - - if (!cid) + group = this.env.group ? this.env.group : '', cid = this.contact_list.get_selection().join(','); if (!cid || !this.env.address_sources[dest] || this.env.address_sources[dest].readonly) @@ -4302,13 +4416,12 @@ // tagret is a group if (to.type == 'group') { if (dest == source) - this.group_member_change('add', cid, dest, to.id); - else { - var lock = this.display_message(this.get_label('copyingcontact'), 'loading'), - post_data = {_cid: cid, _source: this.env.source, _to: dest, _togid: to.id, _gid: group}; + return; - this.http_post('copy', post_data, lock); - } + var lock = this.display_message(this.get_label('copyingcontact'), 'loading'), + post_data = {_cid: cid, _source: this.env.source, _to: dest, _togid: to.id, _gid: group}; + + this.http_post('copy', post_data, lock); } // target is an addressbook else if (to.id != source) { @@ -4319,19 +4432,53 @@ } }; - this.delete_contacts = function() + // move contact(s) to the specified target (group or directory) + this.move_contacts = function(to) { - var selection = this.contact_list.get_selection(), - undelete = this.env.source && this.env.address_sources[this.env.source].undelete; + var dest = to.type == 'group' ? to.source : to.id, + source = this.env.source, + group = this.env.group ? this.env.group : ''; - // exit if no mailbox specified or if selection is empty - if (!(selection.length || this.env.cid) || (!undelete && !confirm(this.get_label('deletecontactconfirm')))) + if (!this.env.address_sources[dest] || this.env.address_sources[dest].readonly) return; - var id, n, a_cids = [], - post_data = {_source: this.env.source, _from: (this.env.action ? this.env.action : '')}, - lock = this.display_message(this.get_label('contactdeleting'), 'loading'); + // search result may contain contacts from many sources, but if there is only one... + if (source == '' && this.env.selection_sources.length == 1) + source = this.env.selection_sources[0]; + if (to.type == 'group') { + if (dest == source) + return; + + this._with_selected_contacts('move', {_to: dest, _togid: to.id}); + } + // target is an addressbook + else if (to.id != source) + this._with_selected_contacts('move', {_to: to.id}); + }; + + // delete contact(s) + this.delete_contacts = function() + { + var undelete = this.env.source && this.env.address_sources[this.env.source].undelete; + + if (!undelete && !confirm(this.get_label('deletecontactconfirm'))) + return; + + return this._with_selected_contacts('delete'); + }; + + this._with_selected_contacts = function(action, post_data) + { + var selection = this.contact_list ? this.contact_list.get_selection() : []; + + // exit if no mailbox specified or if selection is empty + if (!selection.length && !this.env.cid) + return; + + var n, a_cids = [], + label = action == 'delete' ? 'contactdeleting' : 'movingcontact', + lock = this.display_message(this.get_label(label), 'loading'); if (this.env.cid) a_cids.push(this.env.cid); else { @@ -4346,6 +4493,11 @@ this.show_contentframe(false); } + if (!post_data) + post_data = {}; + + post_data._source = this.env.source; + post_data._from = this.env.action; post_data._cid = a_cids.join(','); if (this.env.group) @@ -4356,13 +4508,13 @@ post_data._search = this.env.search_request; // send request to server - this.http_post('delete', post_data, lock) + this.http_post(action, post_data, lock) return true; }; // update a contact record in the list - this.update_contact_row = function(cid, cols_arr, newcid, source) + this.update_contact_row = function(cid, cols_arr, newcid, source, data) { var c, row, list = this.contact_list; @@ -4376,10 +4528,11 @@ } list.update_row(cid, cols_arr, newcid, true); + list.data[cid] = data; }; // add row to contacts list - this.add_contact_row = function(cid, cols, classes) + this.add_contact_row = function(cid, cols, classes, data) { if (!this.gui_objects.contactslist) return false; @@ -4401,6 +4554,8 @@ row.cols.push(col); } + // store data in list member + list.data[cid] = data; list.insert_row(row); this.enable_command('export', list.rowcount > 0); @@ -6228,7 +6383,7 @@ this.enable_command('export-selected', false); } - case 'moveto': + case 'move': if (this.env.action == 'show') { // re-enable commands on move/delete error this.enable_command(this.env.message_commands, true); -- Gitblit v1.9.1