public static function sendITipNotifications(Kronolith_Event $event, Horde_Notification_Handler $notification, $action, Horde_Date $instance = null, $range = null, Kronolith_Attendee_List $cancellations = null)
{
global $injector, $prefs, $registry;
if (!count($event->attendees) || $prefs->getValue('itip_silent')) {
return;
}
$ident = $injector->getInstance('Horde_Core_Factory_Identity')->create($event->creator);
if (!$ident->getValue('from_addr')) {
$notification->push(sprintf(_("You do not have an email address configured in your Personal Information Preferences. You must set one %shere%s before event notifications can be sent."), $registry->getServiceLink('prefs', 'kronolith')->add(array('app' => 'horde', 'group' => 'identities'))->link(), '</a>'), 'horde.error', array('content.raw'));
return;
}
// Generate image mime part first and only once, because we
// need the Content-ID.
$image = self::getImagePart('big_invitation.png');
$share = $injector->getInstance('Kronolith_Shares')->getShare($event->calendar);
$view = new Horde_View(array('templatePath' => KRONOLITH_TEMPLATES . '/itip'));
new Horde_View_Helper_Text($view);
$view->identity = $ident;
$view->event = $event;
$view->imageId = $image->getContentId();
if ($action == self::ITIP_CANCEL && count($cancellations)) {
$mail_attendees = $cancellations;
} elseif ($event->organizer && !self::isUserEmail($event->creator, $event->organizer)) {
/* Only send updates to organizer if the user is not the
* organizer */
if (isset($event->attendees['email:' . $event->organizer])) {
$organizer = $event->attendees['email:' . $event->organizer];
} else {
$organizer = new Kronolith_Attendee(array('email' => $event->organizer));
}
$mail_attendees = new Kronolith_Attendee_List(array($organizer));
} else {
$mail_attendees = $event->attendees;
}
foreach ($mail_attendees as $attendee) {
/* Don't send notifications to the ORGANIZER if this is the
* ORGANIZER's copy of the event. */
if (!$event->organizer && Kronolith::isUserEmail($event->creator, $attendee->email)) {
continue;
}
/* Don't bother sending an invitation/update if the recipient does
* not need to participate, or has declined participating, or
* doesn't have an email address. */
if (strpos($attendee->email, '@') === false || $attendee->response == self::RESPONSE_DECLINED) {
continue;
}
/* Determine all notification-specific strings. */
switch ($action) {
case self::ITIP_CANCEL:
/* Cancellation. */
$method = 'CANCEL';
$filename = 'event-cancellation.ics';
$view->subject = sprintf(_("Cancelled: %s"), $event->getTitle());
if (empty($instance)) {
$view->header = sprintf(_("%s has cancelled \"%s\"."), $ident->getName(), $event->getTitle());
} else {
$view->header = sprintf(_("%s has cancelled an instance of the recurring \"%s\"."), $ident->getName(), $event->getTitle());
}
break;
case self::ITIP_REPLY:
$filename = 'event-reply.ics';
$events = $event->toiCalendar(new Horde_Icalendar());
$vEvent = array_shift($events);
$itipIdentity = new Horde_Itip_Resource_Identity($ident, $vEvent->getAttribute('ATTENDEE'), (string) $ident->getFromAddress());
/* Find which of the creator's mail addresses is used here */
foreach ($event->attendees as $attendee) {
if (self::isUserEmail($event->creator, $attendee->email)) {
switch ($attendee->response) {
case self::RESPONSE_ACCEPTED:
$type = new Horde_Itip_Response_Type_Accept($itipIdentity);
break;
case self::RESPONSE_DECLINED:
$type = new Horde_Itip_Response_Type_Decline($itipIdentity);
break;
case self::RESPONSE_TENTATIVE:
$type = new Horde_Itip_Response_Type_Tentative($itipIdentity);
break;
default:
return;
}
try {
// Send the reply.
Horde_Itip::factory($vEvent, $itipIdentity)->sendMultiPartResponse($type, new Horde_Core_Itip_Response_Options_Horde('UTF-8', array()), $injector->getInstance('Horde_Mail'));
} catch (Horde_Itip_Exception $e) {
$notification->push(sprintf(_("Error sending reply: %s."), $e->getMessage()), 'horde.error');
}
}
}
return;
case self::ITIP_REQUEST:
default:
$method = 'REQUEST';
if ($attendee->response == self::RESPONSE_NONE) {
/* Invitation. */
$filename = 'event-invitation.ics';
$view->subject = $event->getTitle();
$view->header = sprintf(_("%s wishes to make you aware of \"%s\"."), $ident->getName(), $event->getTitle());
} else {
/* Update. */
$filename = 'event-update.ics';
$view->subject = sprintf(_("Updated: %s."), $event->getTitle());
$view->header = sprintf(_("%s wants to notify you about changes of \"%s\"."), $ident->getName(), $event->getTitle());
}
break;
}
$view->organizer = $registry->convertUserName($event->creator, false);
if ($action == self::ITIP_REQUEST) {
$attend_link = Horde::url('attend.php', true, -1)->add(array('c' => $event->calendar, 'e' => $event->id, 'u' => $attendee->email));
$view->linkAccept = (string) $attend_link->add('a', 'accept');
$view->linkTentative = (string) $attend_link->add('a', 'tentative');
$view->linkDecline = (string) $attend_link->add('a', 'decline');
}
/* Build the iCalendar data */
$iCal = new Horde_Icalendar();
$iCal->setAttribute('METHOD', $method);
$iCal->setAttribute('X-WR-CALNAME', $share->get('name'));
$vevent = $event->toiCalendar($iCal);
if ($action == self::ITIP_CANCEL && !empty($instance)) {
// Recurring event instance deletion, need to specify the
// RECURRENCE-ID but NOT the EXDATE.
foreach ($vevent as &$ve) {
try {
$uid = $ve->getAttribute('UID');
} catch (Horde_Icalendar_Exception $e) {
continue;
}
if ($event->uid == $uid) {
$ve->setAttribute('RECURRENCE-ID', $instance);
if (!empty($range)) {
$ve->setParameter('RECURRENCE-ID', array('RANGE' => $range));
}
$ve->setAttribute('DTSTART', $instance, array(), false);
$diff = $event->end->timestamp() - $event->start->timestamp();
$end = clone $instance;
$end->sec += $diff;
$ve->setAttribute('DTEND', $end, array(), false);
$ve->removeAttribute('EXDATE');
break;
}
}
}
$iCal->addComponent($vevent);
/* text/calendar part */
$ics = new Horde_Mime_Part();
$ics->setType('text/calendar');
$ics->setContents($iCal->exportvCalendar());
$ics->setName($filename);
$ics->setContentTypeParameter('method', $method);
$ics->setCharset('UTF-8');
$ics->setEOL("\r\n");
/* application/ics part */
$ics2 = clone $ics;
$ics2->setType('application/ics');
/* multipart/mixed part */
$multipart = new Horde_Mime_Part();
$multipart->setType('multipart/mixed');
$inner = self::buildMimeMessage($view, 'notification', $image);
$inner->addPart($ics);
$multipart->addPart($inner);
$multipart->addPart($ics2);
$recipient = $attendee->addressObject;
$mail = new Horde_Mime_Mail(array('Subject' => $view->subject, 'To' => $recipient, 'From' => $ident->getDefaultFromAddress(true), 'User-Agent' => 'Kronolith ' . $registry->getVersion()));
$mail->setBasePart($multipart);
try {
$mail->send($injector->getInstance('Horde_Mail'));
$notification->push(sprintf(_("The event notification to %s was successfully sent."), $recipient), 'horde.success');
} catch (Horde_Mime_Exception $e) {
$notification->push(sprintf(_("There was an error sending an event notification to %s: %s"), $recipient, $e->getMessage(), $e->getCode()), 'horde.error');
}
}
}