diff --git a/plugins/vcard_attachments/localization/en_GB.inc b/plugins/vcard_attachments/localization/en_GB.inc index 08c275d41..b82c2bc10 100644 --- a/plugins/vcard_attachments/localization/en_GB.inc +++ b/plugins/vcard_attachments/localization/en_GB.inc @@ -19,4 +19,5 @@ $labels['addvcardmsg'] = 'Add vCard to addressbook'; $labels['vcardsavefailed'] = 'Unable to save vCard'; $labels['attachvcard'] = 'Attach vCard'; $labels['vcard'] = 'vCard'; +$labels['forwardvcard'] = 'Forward vCard'; ?> \ No newline at end of file diff --git a/plugins/vcard_attachments/localization/en_US.inc b/plugins/vcard_attachments/localization/en_US.inc index 1ae4ecc55..99af44509 100644 --- a/plugins/vcard_attachments/localization/en_US.inc +++ b/plugins/vcard_attachments/localization/en_US.inc @@ -22,5 +22,6 @@ $labels['vcardsavefailed'] = 'Unable to save vCard'; $labels['attachvcard'] = 'Attach vCard'; $labels['vcardattached'] = 'vCard file attached.'; $labels['vcard'] = 'vCard'; +$labels['forwardvcard'] = 'Forward vCard'; ?> \ No newline at end of file diff --git a/plugins/vcard_attachments/skins/classic/style.css b/plugins/vcard_attachments/skins/classic/style.css index 8ad250e58..d2bbf914a 100644 --- a/plugins/vcard_attachments/skins/classic/style.css +++ b/plugins/vcard_attachments/skins/classic/style.css @@ -39,3 +39,8 @@ p.vcardattachment button { .messagelist tr .attachment span.attachment.vcard { background: url(vcard.png) 0 center no-repeat; } + +#contactmenu li a.vcard { + background: url(../../../../skins/classic/images/messageicons.png) no-repeat 7px 0; + background-position: 6px -168px; +} diff --git a/plugins/vcard_attachments/skins/larry/listicons.png b/plugins/vcard_attachments/skins/larry/listicons.png new file mode 100644 index 000000000..4ac29671f Binary files /dev/null and b/plugins/vcard_attachments/skins/larry/listicons.png differ diff --git a/plugins/vcard_attachments/skins/larry/style.css b/plugins/vcard_attachments/skins/larry/style.css index 8c0fb2d95..ed70aab2a 100644 --- a/plugins/vcard_attachments/skins/larry/style.css +++ b/plugins/vcard_attachments/skins/larry/style.css @@ -33,3 +33,8 @@ a.listbutton.vcard .inner .messagelist tbody tr .attachment span.attachment.vcard { background: url(vcard.png) 0 center no-repeat; } + +ul.toolbarmenu li span.vcard { + background: url(listicons.png) no-repeat 7px 0; + background-position: 0 -3px; +} diff --git a/plugins/vcard_attachments/vcard_attachments.php b/plugins/vcard_attachments/vcard_attachments.php index 330e4ae89..c666bd826 100644 --- a/plugins/vcard_attachments/vcard_attachments.php +++ b/plugins/vcard_attachments/vcard_attachments.php @@ -9,7 +9,7 @@ */ class vcard_attachments extends rcube_plugin { - public $task = 'mail'; + public $task = 'mail|addressbook'; private $message; private $vcard_parts = array(); @@ -19,37 +19,57 @@ class vcard_attachments extends rcube_plugin { $rcmail = rcmail::get_instance(); - if ($rcmail->action == 'show' || $rcmail->action == 'preview') { - $this->add_hook('message_load', array($this, 'message_load')); - $this->add_hook('message_objects', array($this, 'message_objects')); - $this->add_hook('template_object_messagebody', array($this, 'html_output')); - } - else if ($rcmail->action == 'upload') { - $this->add_hook('attachment_from_uri', array($this, 'attach_vcard')); - } - else if ($rcmail->action == 'compose' && !$rcmail->output->framed) { + if ($rcmail->task == 'addressbook') { $skin_path = $this->local_skin_path(); - $btn_class = strpos($skin_path, 'classic') ? 'button' : 'listbutton'; - $this->add_texts('localization', true); $this->include_stylesheet($skin_path . '/style.css'); $this->include_script('vcardattach.js'); $this->add_button( array( - 'type' => 'link', - 'label' => 'vcard_attachments.vcard', + 'type' => 'link-menuitem', + 'label' => 'vcard_attachments.forwardvcard', 'command' => 'attach-vcard', - 'class' => $btn_class . ' vcard disabled', - 'classact' => $btn_class . ' vcard', - 'title' => 'vcard_attachments.attachvcard', - 'innerclass' => 'inner', + 'class' => 'icon vcard', + 'classact' => 'icon vcard active', + 'innerclass' => 'icon vcard', ), - 'compose-contacts-toolbar'); + 'contactmenu'); } - else if (!$rcmail->output->framed && (!$rcmail->action || $rcmail->action == 'list')) { - $skin_path = $this->local_skin_path(); - $this->include_stylesheet($skin_path . '/style.css'); - $this->include_script('vcardattach.js'); + else { + if ($rcmail->action == 'show' || $rcmail->action == 'preview') { + $this->add_hook('message_load', array($this, 'message_load')); + $this->add_hook('message_objects', array($this, 'message_objects')); + $this->add_hook('template_object_messagebody', array($this, 'html_output')); + } + else if ($rcmail->action == 'upload') { + $this->add_hook('attachment_from_uri', array($this, 'attach_vcard')); + } + else if ($rcmail->action == 'compose' && !$rcmail->output->framed) { + $skin_path = $this->local_skin_path(); + $btn_class = strpos($skin_path, 'classic') ? 'button' : 'listbutton'; + + $this->add_texts('localization', true); + $this->include_stylesheet($skin_path . '/style.css'); + $this->include_script('vcardattach.js'); + $this->add_button( + array( + 'type' => 'link', + 'label' => 'vcard_attachments.vcard', + 'command' => 'attach-vcard', + 'class' => $btn_class . ' vcard disabled', + 'classact' => $btn_class . ' vcard', + 'title' => 'vcard_attachments.attachvcard', + 'innerclass' => 'inner', + ), + 'compose-contacts-toolbar'); + + $this->add_hook('message_compose', array($this, 'message_compose')); + } + else if (!$rcmail->output->framed && (!$rcmail->action || $rcmail->action == 'list')) { + $skin_path = $this->local_skin_path(); + $this->include_stylesheet($skin_path . '/style.css'); + $this->include_script('vcardattach.js'); + } } $this->register_action('plugin.savevcard', array($this, 'save_vcard')); @@ -129,6 +149,20 @@ class vcard_attachments extends rcube_plugin return $p; } + /** + * This callback function adds a vCard to the message when attached from the Address book + */ + function message_compose($p) + { + if (rcube_utils::get_input_value('_attach_vcard', rcube_utils::INPUT_GET) == '1' && ($uri = rcube_utils::get_input_value('_uri', rcube_utils::INPUT_GET))) { + if ($attachment = $this->attach_vcard(array('compose_id' => $p['compose_id'], 'uri' => $uri))) { + $p['attachments'][] = $attachment; + }; + } + + return $p; + } + /** * This callback function removes message part's content * for parts that are vcards @@ -268,7 +302,7 @@ class vcard_attachments extends rcube_plugin public function attach_vcard($args) { if (preg_match('|^vcard://(.+)$|', $args['uri'], $m)) { - list($cid, $source) = explode('-', $m[1]); + list($source, $cid, $email) = explode('-', $m[1]); $vcard = $this->get_contact_vcard($source, $cid, $filename); $params = array( diff --git a/plugins/vcard_attachments/vcardattach.js b/plugins/vcard_attachments/vcardattach.js index 30c529ff5..edd92ed20 100644 --- a/plugins/vcard_attachments/vcardattach.js +++ b/plugins/vcard_attachments/vcardattach.js @@ -34,13 +34,19 @@ function plugin_vcard_attach() { var id, n, contacts = [], ts = new Date().getTime(), - args = {_uploadid: ts, _id: rcmail.env.compose_id}, + args = {_uploadid: ts, _id: rcmail.env.compose_id || null}, selection = rcmail.contact_list.get_selection(); for (n=0; n < selection.length; n++) { - id = selection[n]; - if (id && id.charAt(0) != 'E' && rcmail.env.contactdata[id]) - contacts.push(id); + if (rcmail.env.task == 'addressbook') { + id = selection[n]; + contacts.push(rcmail.env.source + '-' + id + '-0'); + } + else { + id = selection[n]; + if (id && id.charAt(0) != 'E' && rcmail.env.contactdata[id]) + contacts.push(id); + } } if (!contacts.length) @@ -48,19 +54,27 @@ function plugin_vcard_attach() args._uri = 'vcard://' + contacts.join(','); - // add to attachments list - if (!rcmail.add2attachment_list(ts, {name: '', html: rcmail.get_label('attaching'), classname: 'uploading', complete: false})) - rcmail.file_upload_id = rcmail.set_busy(true, 'attaching'); + if (rcmail.env.task == 'addressbook') { + args._attach_vcard = 1; + rcmail.open_compose_step(args); + } + else { + // add to attachments list + if (!rcmail.add2attachment_list(ts, {name: '', html: rcmail.get_label('attaching'), classname: 'uploading', complete: false})) + rcmail.file_upload_id = rcmail.set_busy(true, 'attaching'); - rcmail.http_post('upload', args); + rcmail.http_post('upload', args); + } } window.rcmail && rcmail.addEventListener('init', function(evt) { if (rcmail.gui_objects.messagelist) rcmail.addEventListener('insertrow', function(data, evt) { plugin_vcard_insertrow(data); }); - if (rcmail.env.action == 'compose' && rcmail.gui_objects.contactslist) { - rcmail.env.compose_commands.push('attach-vcard'); + if ((rcmail.env.action == 'compose' || (rcmail.env.task == 'addressbook' && rcmail.env.action == '')) && rcmail.gui_objects.contactslist) { + if (rcmail.env.action == 'compose') + rcmail.env.compose_commands.push('attach-vcard'); + rcmail.register_command('attach-vcard', function() { plugin_vcard_attach(); }); rcmail.contact_list.addEventListener('select', function(list) { // TODO: support attaching more than one at once diff --git a/program/js/app.js b/program/js/app.js index da7e7f920..f1b38edd1 100644 --- a/program/js/app.js +++ b/program/js/app.js @@ -47,6 +47,7 @@ function rcube_webmail() this.group2expand = {}; this.http_request_jobs = {}; this.menu_stack = []; + this.entity_selectors = []; // webmail client settings this.dblclick_time = 500; @@ -979,14 +980,14 @@ function rcube_webmail() if (this.task == 'mail') this.move_messages(props, event); else if (this.task == 'addressbook') - this.move_contacts(props); + this.move_contacts(props, event); break; case 'copy': if (this.task == 'mail') this.copy_messages(props, event); else if (this.task == 'addressbook') - this.copy_contacts(props); + this.copy_contacts(props, event); break; case 'mark': @@ -1233,7 +1234,7 @@ function rcube_webmail() case 'print': if (this.task == 'addressbook') { - if (uid = this.contact_list.get_single_selection()) { + if (uid = this.get_single_cid()) { url = '&_action=print&_cid=' + uid; if (this.env.source) url += '&_source=' + urlencode(this.env.source); @@ -5903,15 +5904,21 @@ function rcube_webmail() } this.env.selection_sources = $.unique(this.env.selection_sources); + + var groupcount = 0; + if (source.groups) + $.each(this.env.contactgroups, function(){ if (this.source === ref.env.source) groupcount++ }); } // 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-assign-selected', groupcount > 0 && selected && writable); this.enable_command('group-remove-selected', this.env.group && selected && writable); this.enable_command('print', selected == 1); this.enable_command('export-selected', 'copy', selected > 0); this.enable_command('edit', id && writable); this.enable_command('delete', 'move', selected && writable); + this.enable_command('qrcode', selected == 1); return false; }; @@ -5953,6 +5960,9 @@ function rcube_webmail() this.env.address_group_stack = index < 0 ? [] : this.env.address_group_stack.slice(0, index); + // remove cached contact group selector + this.destroy_entity_selector('contactgroup-selector'); + // make sure the current group is on top of the stack if (this.env.group) { if (!search) search = {}; @@ -6130,8 +6140,15 @@ function rcube_webmail() }; // copy contact(s) to the specified target (group or directory) - this.copy_contacts = function(to) + this.copy_contacts = function(to, event) { + if (!to) { + return this.addressbook_selector(event, function(to, obj) { + var to = $(obj).data('source') ? ref.env.contactgroups['G' + $(obj).data('source') + $(obj).data('gid')] : ref.env.address_sources[to]; + ref.command('copy', to); + }); + } + var dest = to.type == 'group' ? to.source : to.id, source = this.env.source, group = this.env.group ? this.env.group : '', @@ -6164,8 +6181,15 @@ function rcube_webmail() }; // move contact(s) to the specified target (group or directory) - this.move_contacts = function(to) + this.move_contacts = function(to, event) { + if (!to) { + return this.addressbook_selector(event, function(to, obj) { + var to = $(obj).data('source') ? ref.env.contactgroups['G' + $(obj).data('source') + $(obj).data('gid')] : ref.env.address_sources[to]; + ref.command('move', to); + }); + } + var dest = to.type == 'group' ? to.source : to.id, source = this.env.source, group = this.env.group ? this.env.group : ''; @@ -6392,6 +6416,10 @@ function rcube_webmail() var key = 'G'+prop.source+prop.id; if (this.treelist.remove(key)) { + // make sure there is no cached address book or contact group selectors + this.destroy_entity_selector('addressbook-selector'); + this.destroy_entity_selector('contactgroup-selector'); + this.triggerEvent('group_delete', { source:prop.source, id:prop.id }); delete this.env.contactfolders[key]; delete this.env.contactgroups[key]; @@ -6401,6 +6429,12 @@ function rcube_webmail() this.list_contacts(prop.source, 0); }; + //assign selected contacts to a group + this.group_assign_selected = function(props, obj, event) + { + this.contactgroup_selector(event, function(to) { ref.group_member_change('add', ref.contact_list.get_selection(), ref.env.source, to); }); + }; + //remove selected contacts from current active group this.group_remove_selected = function() { @@ -6416,6 +6450,7 @@ function rcube_webmail() for (n=0; n'); - if (!container) { - var rows = [], - delim = this.env.delimiter, - ul = $(' +
+ +
+
@@ -107,7 +123,6 @@
  • -
  • diff --git a/skins/classic/templates/contact.html b/skins/classic/templates/contact.html index 3835222e1..8be112b49 100644 --- a/skins/classic/templates/contact.html +++ b/skins/classic/templates/contact.html @@ -21,9 +21,6 @@

- - -

diff --git a/skins/larry/addressbook.css b/skins/larry/addressbook.css index da4ef193f..5484adccb 100644 --- a/skins/larry/addressbook.css +++ b/skins/larry/addressbook.css @@ -403,3 +403,41 @@ a.deletebutton { #import-box .propform { max-width: 50em; } + +ul.toolbarmenu li span.qrcode { + background-position: 0 -2408px; +} + +ul.toolbarmenu li span.assigngroup { + background-position: 0 -2358px; +} + +ul.toolbarmenu li span.removegroup { + background-position: 0 -2384px; +} + +#addressbook-selector li a, +#contactgroup-selector li a { + padding-left: 2px; +} + +#addressbook-selector li a span, +#contactgroup-selector li a span { + background: url(images/listicons.png) 4px 20px no-repeat; + display: block; + height: 17px; + min-height: 14px; + padding: 4px 4px 1px 28px; + overflow: hidden; + max-width: 120px; + text-overflow: ellipsis; +} + +#addressbook-selector li a.addressbook span { + background-position: 4px -2222px; +} + +#addressbook-selector li a.contactgroup span, +#contactgroup-selector li a.contactgroup span { + background-position: 4px -2245px; +} diff --git a/skins/larry/images/listicons.png b/skins/larry/images/listicons.png index 29b382653..810b018bf 100644 Binary files a/skins/larry/images/listicons.png and b/skins/larry/images/listicons.png differ diff --git a/skins/larry/templates/addressbook.html b/skins/larry/templates/addressbook.html index 1968d1ead..77168aa03 100644 --- a/skins/larry/templates/addressbook.html +++ b/skins/larry/templates/addressbook.html @@ -26,6 +26,7 @@ +