diff --git a/plugins/calendar/calendar.php b/plugins/calendar/calendar.php --- a/plugins/calendar/calendar.php +++ b/plugins/calendar/calendar.php @@ -1239,23 +1239,31 @@ $noreply = intval($noreply) || $status == 'needs-action' || $itip_sending === 0; $reload = $event['calendar'] != $ev['calendar'] || !empty($event['recurrence']) ? 2 : 1; $emails = $this->get_user_emails(); + $ownedResourceEmails = $this->owned_resources_emails($emails); $organizer = null; + $resourceConfirmation = false; foreach ($event['attendees'] as $i => $attendee) { if ($attendee['role'] == 'ORGANIZER') { $organizer = $attendee; } - else if (!empty($attendee['email']) && in_array(strtolower($attendee['email']), $emails)) { + else if (!empty($attendee['email']) && in_array_nocase($attendee['email'], $emails)) { $reply_sender = $attendee['email']; } + else if (!empty($attendee['cutype']) && $attendee['cutype'] == 'RESOURCE' && !empty($attendee['email']) && in_array_nocase($attendee['email'], $ownedResourceEmails)) { + $resourceConfirmation = true; + // Note on behalf of which resource this update is going to be sent out + $event['_resource'] = $attendee['email']; + } } if (!$noreply) { $itip = $this->load_itip(); $itip->set_sender_email($reply_sender); $event['thisandfuture'] = $event['_savemode'] == 'future'; + $bodytextprefix = $resourceConfirmation ? 'itipmailbodyresource' : 'itipmailbody'; - if ($organizer && $itip->send_itip_message($event, 'REPLY', $organizer, 'itipsubject' . $status, 'itipmailbody' . $status)) { + if ($organizer && $itip->send_itip_message($event, 'REPLY', $organizer, 'itipsubject' . $status, $bodytextprefix . $status)) { $mailto = !empty($organizer['name']) ? $organizer['name'] : $organizer['email']; $msg = $this->gettext(['name' => 'sentresponseto', 'vars' => ['mailto' => $mailto]]); @@ -2006,10 +2014,12 @@ } $identity['emails'][] = $this->rc->user->get_username(); + $identity['ownedResources'] = $this->owned_resources_emails($identity['emails']); $settings['identity'] = [ 'name' => $identity['name'], 'email' => strtolower($identity['email']), - 'emails' => ';' . strtolower(join(';', $identity['emails'])) + 'emails' => ';' . strtolower(join(';', $identity['emails'])), + 'ownedResources' => ';' . strtolower(join(';', $identity['ownedResources'])) ]; } @@ -2868,6 +2878,21 @@ exit; } + /** + * List email addressed of owned resources + */ + private function owned_resources_emails($identities) + { + $results = []; + if ($directory = $this->resources_directory()) { + foreach ($directory->load_owned_resources($identities) as $rec) { + $results[] = $rec['email']; + } + } + return $results; + } + + /**** Event invitation plugin hooks ****/ /** diff --git a/plugins/calendar/calendar_ui.js b/plugins/calendar/calendar_ui.js --- a/plugins/calendar/calendar_ui.js +++ b/plugins/calendar/calendar_ui.js @@ -460,7 +460,7 @@ for (var j=0; j < num_attendees; j++) { data = event.attendees[j]; if (data.email) { - if (data.role != 'ORGANIZER' && settings.identity.emails.indexOf(';'+data.email) >= 0) { + if (data.role != 'ORGANIZER' && is_this_me(data.email)) { mystatus = (data.status || 'UNKNOWN').toLowerCase(); if (data.status == 'NEEDS-ACTION' || data.status == 'TENTATIVE' || data.rsvp) rsvp = mystatus; @@ -2380,6 +2380,14 @@ add_attendee($.extend({ role:'REQ-PARTICIPANT', status:'NEEDS-ACTION', cutype:'RESOURCE' }, resource)); } + var is_this_me = function(email) + { + if (settings.identity.emails.indexOf(';'+email) >= 0 || settings.identity.ownedResources.indexOf(';'+email) >= 0) { + return true; + } + return false; + }; + // when the user accepts or declines an event invitation var event_rsvp = function(response, delegate, replymode, event) { @@ -2414,7 +2422,8 @@ attendees = []; for (var data, i=0; i < me.selected_event.attendees.length; i++) { data = me.selected_event.attendees[i]; - if (settings.identity.emails.indexOf(';'+String(data.email).toLowerCase()) >= 0) { + //FIXME this can only work if there is a single resource per invitation + if (is_this_me(String(data.email).toLowerCase())) { data.status = response.toUpperCase(); data.rsvp = 0; // unset RSVP flag diff --git a/plugins/calendar/drivers/ldap/resources_driver_ldap.php b/plugins/calendar/drivers/ldap/resources_driver_ldap.php --- a/plugins/calendar/drivers/ldap/resources_driver_ldap.php +++ b/plugins/calendar/drivers/ldap/resources_driver_ldap.php @@ -71,6 +71,41 @@ return $results; } + /** + * Fetch resource objects filtered by owner email addresses + * + * @param array $emails List of email addresses of the owner + * @param int $num Max size of the result + * + * @return array List of resource records + */ + public function load_owned_resources($emails, $num = 5000) + { + if (!($ldap = $this->connect())) { + return []; + } + + $ldap->set_pagesize($num); + + $ownerDns = []; + $results = $ldap->search('email', $emails, 0, true, true); + if ($results instanceof ArrayAccess) { + foreach ($results as $i => $rec) { + $ownerDns[] = $rec['dn']; + } + } + + $results = $ldap->search('owner', $ownerDns, 0, true, true); + + if ($results instanceof ArrayAccess) { + foreach ($results as $i => $rec) { + $results[$i] = $this->decode_resource($rec); + } + } + + return $results; + } + /** * Return properties of a single resource * diff --git a/plugins/calendar/drivers/resources_driver.php b/plugins/calendar/drivers/resources_driver.php --- a/plugins/calendar/drivers/resources_driver.php +++ b/plugins/calendar/drivers/resources_driver.php @@ -45,6 +45,19 @@ */ abstract public function load_resources($query = null); + /** + * Fetch resource objects filtered by owner email addresses + * + * @param array $emails List of email addresses of the owner + * @param int $num Max size of the result + * + * @return array List of resource records + */ + public function load_owned_resources($emails, $num = 5000) + { + return []; + } + /** * Return properties of a single resource * diff --git a/plugins/calendar/localization/en_US.inc b/plugins/calendar/localization/en_US.inc --- a/plugins/calendar/localization/en_US.inc +++ b/plugins/calendar/localization/en_US.inc @@ -207,6 +207,10 @@ $labels['itipmailbodydelegated'] = "\$sender has delegated the participation in the following event:\n\n*\$title*\n\nWhen: \$date"; $labels['itipmailbodydelegatedto'] = "\$sender has delegated the participation in the following event to you:\n\n*\$title*\n\nWhen: \$date"; +$labels['itipmailbodyresourceaccepted'] = "\$sender has accepted the following resource booking:\n\n*\$title*\n\nWhen: \$date\n\nInvitees: \$attendees"; +$labels['itipmailbodyresourcetentative'] = "\$sender has tentatively accepted the following resource booking:\n\n*\$title*\n\nWhen: \$date\n\nInvitees: \$attendees"; +$labels['itipmailbodyresourcedeclined'] = "\$sender has declined the the following resource booking:\n\n*\$title*\n\nWhen: \$date\n\nInvitees: \$attendees"; + $labels['itipdeclineevent'] = 'Do you want to decline your invitation to this event?'; $labels['declinedeleteconfirm'] = 'Do you also want to delete this declined event from your calendar?'; $labels['itipcomment'] = 'Invitation/notification comment'; diff --git a/plugins/libcalendaring/lib/libcalendaring_itip.php b/plugins/libcalendaring/lib/libcalendaring_itip.php --- a/plugins/libcalendaring/lib/libcalendaring_itip.php +++ b/plugins/libcalendaring/lib/libcalendaring_itip.php @@ -219,6 +219,11 @@ if ($attendee['role'] == 'ORGANIZER') { $reply_attendees[] = $attendee; } + // we accept on behalf of a resource + else if (strcasecmp($attendee['email'], $event['_resource']) == 0) { + $replying_attendee = $attendee; + $replying_attendee['sent-by'] = 'mailto:' . $from_utf; + } else if (strcasecmp($attendee['email'], $from) == 0 || strcasecmp($attendee['email'], $from_utf) == 0) { $replying_attendee = $attendee; if ($attendee['status'] != 'DELEGATED') {