From 166bf43f2d22018c2332dcb778797b9abde7a05a Mon Sep 17 00:00:00 2001 From: Thomas Date: Wed, 24 Aug 2011 17:45:51 +0200 Subject: [PATCH] Invitaton handling: show current invitation status in mail view (asynchronously) --- plugins/calendar/calendar.php | 101 +++++++++++++----- plugins/calendar/calendar_base.js | 24 +++++ .../calendar/drivers/kolab/kolab_driver.php | 16 ++- plugins/calendar/localization/en_US.inc | 3 + plugins/calendar/skins/default/calendar.css | 23 ++++ 5 files changed, 140 insertions(+), 27 deletions(-) diff --git a/plugins/calendar/calendar.php b/plugins/calendar/calendar.php index 4545d02b..6d4f30bc 100644 --- a/plugins/calendar/calendar.php +++ b/plugins/calendar/calendar.php @@ -600,6 +600,37 @@ class calendar extends rcube_plugin break; + case "rsvp-status": + $status = 'unknown'; + $action = 'rsvp'; + $html = html::div('rsvp-status', $this->gettext('acceptinvitation')); + $this->load_driver(); + if ($existing = $this->driver->get_event($event)) { + $emails = $this->get_user_emails(); + foreach ($existing['attendees'] as $i => $attendee) { + if ($attendee['email'] && in_array($attendee['email'], $emails)) { + $status = $attendee['status']; + break; + } + } + + if (in_array($status, array('ACCEPTED','TENTATIVE','DECLINED'))) { + $html = html::div('rsvp-status ' . strtolower($status), $this->gettext('youhave'.strtolower($status))); + if ($event['changed'] < $existing['changed']) { + $action = ''; + } + } + } + + $this->rc->output->command('plugin.update_event_rsvp_status', array( + 'uid' => $event['uid'], + 'id' => asciiwords($event['uid'], true), + 'status' => $status, + 'action' => $action, + 'html' => $html, + )); + return; + case "rsvp": $ev = $this->driver->get_event($event); $ev['attendees'] = $event['attendees']; @@ -774,6 +805,7 @@ class calendar extends rcube_plugin $identity = $rec; $identity['emails'][] = $rec['email']; } + $identity['emails'][] = $this->rc->user->get_username(); $settings['identity'] = array('name' => $identity['name'], 'email' => $identity['email'], 'emails' => ';' . join(';', $identity['emails'])); } @@ -1577,25 +1609,28 @@ class calendar extends rcube_plugin else if ($this->ical->method == 'REQUEST') { $title = $event['SEQUENCE'] > 0 ? $this->gettext('itipupdate') : $this->gettext('itipinvitation'); - // TODO: check for a copy in my calendar in order to show the right options - if (!$event['SEQUENCE']) { - foreach (array('accepted','tentative','declined') as $method) { - $buttons .= html::tag('input', array( - 'type' => 'button', - 'class' => 'button', - 'onclick' => "rcube_calendar.add_event_from_mail('" . JQ($mime_id.':'.$idx) . "', '$method')", - 'value' => $this->gettext('itip' . $method), - )); - } - } - else { - $buttons = html::tag('input', array( + // add (hidden) buttons and activate them from asyncronous request + foreach (array('accepted','tentative','declined') as $method) { + $rsvp_buttons .= html::tag('input', array( 'type' => 'button', 'class' => 'button', - 'onclick' => "rcube_calendar.add_event_from_mail('" . JQ($mime_id.':'.$idx) . "')", - 'value' => $this->gettext('importtocalendar'), + 'onclick' => "rcube_calendar.add_event_from_mail('" . JQ($mime_id.':'.$idx) . "', '$method')", + 'value' => $this->gettext('itip' . $method), )); } + $import_button = html::tag('input', array( + 'type' => 'button', + 'class' => 'button', + 'onclick' => "rcube_calendar.add_event_from_mail('" . JQ($mime_id.':'.$idx) . "')", + 'value' => $this->gettext('importtocalendar'), + )); + + $dom_id = asciiwords($event['uid'], true); + $buttons = html::div(array('id' => 'rsvp-'.$dom_id, 'style' => 'display:none'), $rsvp_buttons); + $buttons .= html::div(array('id' => 'import-'.$dom_id, 'style' => 'display:none'), $import_button); + $buttons_pre = html::div(array('id' => 'loading-'.$dom_id, 'class' => 'rsvp-status loading'), $this->gettext('loading')); + + $this->rc->output->add_script('rcube_calendar.fetch_event_rsvp_status(' . json_serialize(array('uid' => $event['uid'], 'changed' => $event['changed'])) . ')', 'docready'); } else if ($this->ical->method == 'CANCEL') { $title = $this->gettext('itipcancellation'); @@ -1627,7 +1662,7 @@ class calendar extends rcube_plugin } // add box below messsage body - $html .= html::div('calendar-invitebox', $table->show() . html::div('rsvp-buttons', $buttons)); + $html .= html::div('calendar-invitebox', $table->show() . $buttons_pre . html::div('rsvp-buttons', $buttons)); // limit listing if ($idx >= 3) @@ -1643,8 +1678,8 @@ class calendar extends rcube_plugin return $p; } - - + + /** * Handler for POST request to import an event attached to a mail message */ @@ -1691,10 +1726,8 @@ class calendar extends rcube_plugin // update my attendee status according to submitted method if (!empty($status)) { - $emails = array($this->rc->user->get_username()); - foreach ($this->rc->user->list_identities() as $identity) - $emails[] = $identity['email']; $organizer = null; + $emails = $this->get_user_emails(); foreach ($event['attendees'] as $i => $attendee) { if ($attendee['role'] == 'ORGANIZER') { $organizer = $attendee; @@ -1706,7 +1739,7 @@ class calendar extends rcube_plugin } // save to calendar - if ($calendar && !$calendar['readonly'] && $status != 'declined') { + if ($calendar && !$calendar['readonly']) { $event['id'] = $event['uid']; $event['calendar'] = $calendar['id']; @@ -1751,14 +1784,21 @@ class calendar extends rcube_plugin } // import the (newer) event // TODO: compare SEQUENCE numbers instead of changed dates - else if ($event['changed'] >= $existing['changed']) + else if ($event['changed'] >= $existing['changed']) { $success = $this->driver->edit_event($event); + } + else if (!empty($status)) { + $existing['attendees'] = $event['attendees']; + $success = $this->driver->edit_event($existing); + } else $error_msg = $this->gettext('newerversionexists'); } - else if (!$existing) { + else if (!$existing && $status != 'declined') { $success = $this->driver->new_event($event); } + else if ($status == 'declined') + $error_msg = null; } else if ($status == 'declined') $error_msg = null; @@ -1896,5 +1936,18 @@ class calendar extends rcube_plugin ); } + + /** + * Get a list of email addresses of the current user (from login and identities) + */ + private function get_user_emails() + { + $emails = array($this->rc->user->get_username()); + foreach ($this->rc->user->list_identities() as $identity) + $emails[] = $identity['email']; + + return array_unique($emails); + } + } diff --git a/plugins/calendar/calendar_base.js b/plugins/calendar/calendar_base.js index f22e4689..0a5cca14 100644 --- a/plugins/calendar/calendar_base.js +++ b/plugins/calendar/calendar_base.js @@ -171,6 +171,19 @@ rcube_calendar.add_event_from_mail = function(mime_id, status) return false; }; +rcube_calendar.fetch_event_rsvp_status = function(event) +{ +/* + var id = event.uid.replace(rcmail.identifier_expr, ''); + $('#import-'+id+', #rsvp-'+id+', div.rsvp-status').hide(); + $('#loading-'+id).show(); +*/ + rcmail.http_post('calendar/event', { + e:event, + action:'rsvp-status' + }); +}; + // extend jQuery (function($){ @@ -187,8 +200,19 @@ rcube_calendar.add_event_from_mail = function(mime_id, status) window.rcmail && rcmail.addEventListener('init', function(evt) { if (rcmail.task != 'calendar') { var cal = new rcube_calendar(rcmail.env.calendar_settings); + rcmail.addEventListener('plugin.display_alarms', function(alarms){ cal.display_alarms(alarms); }); + + rcmail.addEventListener('plugin.update_event_rsvp_status', function(p){ + if (p.html) + $('#loading-'+p.id).hide().after(p.html); + else + $('#loading-'+p.id).hide(); + + $('#'+p.action+'-'+p.id).show(); + }); } + rcmail.addEventListener('plugin.ping_url', function(p){ var action = p.action; p.action = p.event = null; diff --git a/plugins/calendar/drivers/kolab/kolab_driver.php b/plugins/calendar/drivers/kolab/kolab_driver.php index a1c653ea..edf1708f 100644 --- a/plugins/calendar/drivers/kolab/kolab_driver.php +++ b/plugins/calendar/drivers/kolab/kolab_driver.php @@ -311,15 +311,25 @@ class kolab_driver extends calendar_driver /** - * Move a single event + * Fetch a single event * * @see calendar_driver::get_event() * @return array Hash array with event properties, false if not found */ public function get_event($event) { - if ($storage = $this->calendars[$event['calendar']]) - return $storage->get_event($event['id']); + $id = $event['id'] ? $event['id'] : $event['uid']; + if ($event['calendar'] && ($storage = $this->calendars[$event['calendar']])) { + return $storage->get_event($id); + } + // iterate over all calendar folders and search for the event ID + else if (!$event['calendar']) { + foreach ($this->calendars as $storage) { + if ($result = $storage->get_event($id)) { + return $result; + } + } + } return false; } diff --git a/plugins/calendar/localization/en_US.inc b/plugins/calendar/localization/en_US.inc index c31fb6b0..cca5b592 100644 --- a/plugins/calendar/localization/en_US.inc +++ b/plugins/calendar/localization/en_US.inc @@ -138,6 +138,9 @@ $labels['itipmailbodydeclined'] = "\$sender has declined the invitation to the f $labels['importtocalendar'] = 'Save to my calendar'; $labels['updateattendeestatus'] = 'Update the participant\'s status'; $labels['acceptinvitation'] = 'Do you accept this invitation?'; +$labels['youhaveaccepted'] = 'You have accepted this invitation'; +$labels['youhavetentative'] = 'You have tentatively accepted this invitation'; +$labels['youhavedeclined'] = 'You have declined this invitation'; // event dialog tabs $labels['tabsummary'] = 'Summary'; diff --git a/plugins/calendar/skins/default/calendar.css b/plugins/calendar/skins/default/calendar.css index 210936c7..27e644dd 100644 --- a/plugins/calendar/skins/default/calendar.css +++ b/plugins/calendar/skins/default/calendar.css @@ -1111,6 +1111,7 @@ div.calendar-invitebox td.label { } #event-rsvp .rsvp-buttons, +div.calendar-invitebox .rsvp-status, div.calendar-invitebox .rsvp-buttons { margin-top: 0.5em; } @@ -1120,3 +1121,25 @@ div.calendar-invitebox input.button { font-size: 11px; margin-right: 0.5em; } + +div.calendar-invitebox .rsvp-status.loading { + color: #666; + padding: 1px 0 2px 24px; + background: url('images/loading-small.gif') top left no-repeat; +} + +div.calendar-invitebox .rsvp-status.declined, +div.calendar-invitebox .rsvp-status.tentative, +div.calendar-invitebox .rsvp-status.accepted { + padding: 0 0 1px 22px; + background: url('images/attendee-status.gif') 2px -20px no-repeat; +} + +div.calendar-invitebox .rsvp-status.declined { + background-position: 2px -40px; +} + +div.calendar-invitebox .rsvp-status.tentative { + background-position: 2px -60px; +} +