Checks if an email address belongs to a user.
public static isUserEmail ( string $uid, string $email ) | ||
$uid | string | user id to check |
string | email address to check |
/** */ public function davDeleteObject($collection, $object) { $dav = $GLOBALS['injector']->getInstance('Horde_Dav_Storage'); $internal = $dav->getInternalCollectionId($collection, 'calendar') ?: $collection; if (!Kronolith::hasPermission($internal, Horde_Perms::DELETE)) { throw new Kronolith_Exception(_("Calendar does not exist or no permission to delete")); } try { $object = $dav->getInternalObjectId($object, $internal) ?: preg_replace('/\\.ics$/', '', $object); } catch (Horde_Dav_Exception $e) { } $kronolith_driver = Kronolith::getDriver(null, $internal); $event = $kronolith_driver->getEvent($object); $kronolith_driver->deleteEvent($object); try { $dav->deleteExternalObjectId($object, $internal); } catch (Horde_Dav_Exception $e) { } // Send iTip messages unless organizer is external. // Notifications will get lost, there is no way to return messages to // clients. if ($event->organizer && !Kronolith::isUserEmail($event->creator, $event->organizer)) { return; } Kronolith::sendITipNotifications($event, new Horde_Notification_Handler(new Horde_Notification_Storage_Object()), Kronolith::ITIP_CANCEL); }
/** * Imports a backend specific event object. * * @param array $event Backend specific event object that this object * will represent. */ public function fromDriver($event) { $this->uid = $event['uid']; $this->id = Horde_Url::uriB64Encode($event['uid']); if (isset($event['summary'])) { $this->title = $event['summary']; } if (isset($event['body'])) { $this->description = $event['body']; } if (isset($event['location'])) { $this->location = $event['location']; } if (isset($event['sensitivity']) && ($event['sensitivity'] == 'private' || $event['sensitivity'] == 'confidential')) { $this->private = true; } if (isset($event['organizer']['smtp-address'])) { if (Kronolith::isUserEmail($GLOBALS['registry']->getAuth(), $event['organizer']['smtp-address'])) { $this->creator = $GLOBALS['registry']->getAuth(); } else { $this->creator = $event['organizer']['smtp-address']; } } if (isset($event['alarm'])) { $this->alarm = $event['alarm']; } if (isset($event['horde-alarm-methods'])) { $this->methods = @unserialize($event['horde-alarm-methods']); } $tz_local = date_default_timezone_get(); $this->start = new Horde_Date($event['start-date']); $this->start->setTimezone($tz_local); $this->end = new Horde_Date($event['end-date']); $this->end->setTimezone($tz_local); $this->durMin = ($this->end->timestamp() - $this->start->timestamp()) / 60; if (!empty($event['creation-date'])) { $this->created = new Horde_Date($event['creation-date']); } if (!empty($event['last-modification-date'])) { $this->modified = new Horde_Date($event['last-modification-date']); } if (isset($event['show-time-as'])) { switch ($event['show-time-as']) { case 'free': $this->status = Kronolith::STATUS_FREE; break; case 'tentative': $this->status = Kronolith::STATUS_TENTATIVE; break; case 'busy': case 'outofoffice': default: $this->status = Kronolith::STATUS_CONFIRMED; } } else { $this->status = Kronolith::STATUS_CONFIRMED; } // Recurrence if (isset($event['recurrence'])) { if (isset($event['recurrence']['exclusion'])) { $exceptions = array(); foreach ($event['recurrence']['exclusion'] as $exclusion) { if (!empty($exclusion)) { $exceptions[] = $exclusion->format('Ymd'); } } $event['recurrence']['exceptions'] = $exceptions; } if (isset($event['recurrence']['complete'])) { $completions = array(); foreach ($event['recurrence']['complete'] as $complete) { if (!empty($complete)) { $completions[] = $complete->format('Ymd'); } } $event['recurrence']['completions'] = $completions; } $this->recurrence = new Horde_Date_Recurrence($this->start); $this->recurrence->fromKolab($event['recurrence']); } // Attendees $attendee_count = 0; if (!empty($event['attendee'])) { foreach ($event['attendee'] as $attendee) { $name = $attendee['display-name']; $email = $attendee['smtp-address']; $role = $attendee['role']; switch ($role) { case 'optional': $role = Kronolith::PART_OPTIONAL; break; case 'resource': $role = Kronolith::PART_NONE; break; case 'required': default: $role = Kronolith::PART_REQUIRED; break; } $status = $attendee['status']; switch ($status) { case 'accepted': $status = Kronolith::RESPONSE_ACCEPTED; break; case 'declined': $status = Kronolith::RESPONSE_DECLINED; break; case 'tentative': $status = Kronolith::RESPONSE_TENTATIVE; break; case 'none': default: $status = Kronolith::RESPONSE_NONE; break; } $this->addAttendee($email, $role, $status, $name); } } // Tags if (isset($event['categories'])) { $this->_internaltags = $event['categories']; } $this->initialized = true; $this->stored = true; }
/** * Sends out iTip event notifications to all attendees of a specific * event. * * Can be used to send event invitations, event updates as well as event * cancellations. * * @param Kronolith_Event $event * The event in question. * @param Horde_Notification_Handler $notification * A notification object used to show result status. * @param integer $action * The type of notification to send. One of the Kronolith::ITIP_* * values. * @param Horde_Date $instance * If cancelling a single instance of a recurring event, the date of * this instance. * @param string $range The range parameter if this is a recurring event. * Possible values are self::RANGE_THISANDFUTURE * @param Kronolith_Attendee_List $cancellations If $action is 'CANCEL', * but it is due to removing * attendees and not * canceling the entire * event, these are the * uninvited attendees and * are the ONLY people that * will receive the CANCEL * iTIP. @since 4.2.10 */ 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'); } } }
protected function _postSave(Kronolith_Event $event) { global $registry; if (!$this->_dav->getInternalObjectId($this->_params['object'], $this->_calendar)) { $this->_dav->addObjectMap($event->id, $this->_params['object'], $this->_calendar); } // Send iTip messages if necessary. $type = Kronolith::ITIP_REQUEST; if ($event->organizer && !Kronolith::isUserEmail($event->creator, $event->organizer)) { $type = Kronolith::ITIP_REPLY; } $event_copy = clone $event; $event_copy->attendees = $event->attendees->without($this->_noItips); $notification = new Horde_Notification_Handler(new Horde_Notification_Storage_Object()); Kronolith::sendITipNotifications($event_copy, $notification, $type); // Send ITIP_CANCEL to any attendee that was removed, but only if this // is the ORGANZIER's copy of the event. if (empty($event->organizer) || $registry->getAuth() == $event->creator && Kronolith::isUserEmail($event->creator, $event->organizer)) { $removed_attendees = new Kronolith_Attendee_List(); foreach ($this->_oldAttendees as $old_attendee) { if (!$event->attendees->has($old_attendee)) { $removed_attendees->add($old_attendee); } } if (count($removed_attendees)) { $cancelEvent = clone $event; Kronolith::sendITipNotifications($cancelEvent, $notification, Kronolith::ITIP_CANCEL, null, null, $removed_attendees); } } }
/** * Reads form/post data and updates this event's properties. * * @param Kronolith_Event|null $existing If this is an exception event * this is taken as the base event. * @since 4.2.6 * */ public function readForm(Kronolith_Event $existing = null) { global $notification, $prefs, $registry, $session; // Event owner. $targetcalendar = Horde_Util::getFormData('targetcalendar'); if (strpos($targetcalendar, '\\')) { list(, $this->creator) = explode('\\', $targetcalendar, 2); } elseif (!isset($this->_id)) { $this->creator = $registry->getAuth(); } // Basic fields. $this->title = Horde_Util::getFormData('title', $this->title); $this->description = Horde_Util::getFormData('description', $this->description); $this->location = Horde_Util::getFormData('location', $this->location); $this->timezone = Horde_Util::getFormData('timezone', $this->timezone); $this->private = (bool) Horde_Util::getFormData('private'); // if the field is empty you are the organizer (and so organizer should be null) $this->organizer = Horde_Util::getFormData('organizer', $this->organizer) ?: null; // URL. $url = Horde_Util::getFormData('eventurl', $this->url); if (strlen($url)) { // Analyze and re-construct. $url = @parse_url($url); if ($url) { if (function_exists('http_build_url')) { if (empty($url['path'])) { $url['path'] = '/'; } $url = http_build_url($url); } else { $new_url = ''; if (isset($url['scheme'])) { $new_url .= $url['scheme'] . '://'; } if (isset($url['user'])) { $new_url .= $url['user']; if (isset($url['pass'])) { $new_url .= ':' . $url['pass']; } $new_url .= '@'; } if (isset($url['host'])) { // Convert IDN hosts to ASCII. if (function_exists('idn_to_ascii')) { $url['host'] = @idn_to_ascii($url['host']); } elseif (Horde_Mime::is8bit($url['host'])) { //throw new Kronolith_Exception(_("Invalid character in URL.")); $url['host'] = ''; } $new_url .= $url['host']; } if (isset($url['path'])) { $new_url .= $url['path']; } if (isset($url['query'])) { $new_url .= '?' . $url['query']; } if (isset($url['fragment'])) { $new_url .= '#' . $url['fragment']; } $url = $new_url; } } } $this->url = $url; // Status. $this->status = Horde_Util::getFormData('status', $this->status); // Attendees. $attendees = $session->get('kronolith', 'attendees'); if (!$attendees) { $attendees = new Kronolith_Attendee_List(); } $newattendees = Horde_Util::getFormData('attendees'); $userattendees = Horde_Util::getFormData('users'); if (!is_null($newattendees) || !is_null($userattendees)) { if ($newattendees) { $newattendees = Kronolith_Attendee_List::parse(trim($newattendees), $notification); } else { $newattendees = new Kronolith_Attendee_List(); } if ($userattendees) { foreach (explode(',', $userattendees) as $user) { if (!($newUser = Kronolith::validateUserAttendee($user))) { $notification->push(sprintf(_("The user \"%s\" does not exist."), $newUser), 'horde.error'); } else { $newattendees->add($newUser); } } } // First add new attendees missing in the current list. foreach ($newattendees as $attendee) { if (!$attendees->has($attendee)) { $attendees->add($attendee); } } // Now check for attendees in the current list that don't exist in // the new attendee list anymore. $finalAttendees = new Kronolith_Attendee_List(); foreach ($attendees as $attendee) { if (!$newattendees->has($attendee)) { continue; } if (Kronolith::isUserEmail($this->creator, $attendee->email)) { $attendee->response = Horde_Util::getFormData('attendance'); } $finalAttendees->add($attendee); } $attendees = $finalAttendees; } $this->attendees = $attendees; // Event start. $allDay = Horde_Util::getFormData('whole_day'); if ($start_date = Horde_Util::getFormData('start_date')) { // From ajax interface. $this->start = Kronolith::parseDate($start_date . ' ' . Horde_Util::getFormData('start_time'), true, $this->timezone); if ($allDay) { $this->start->hour = $this->start->min = $this->start->sec = 0; } } elseif ($start = Horde_Util::getFormData('start')) { // From traditional interface. $start_year = $start['year']; $start_month = $start['month']; $start_day = $start['day']; $start_hour = Horde_Util::getFormData('start_hour'); $start_min = Horde_Util::getFormData('start_min'); $am_pm = Horde_Util::getFormData('am_pm'); if (!$prefs->getValue('twentyFour')) { if ($am_pm == 'PM') { if ($start_hour != 12) { $start_hour += 12; } } elseif ($start_hour == 12) { $start_hour = 0; } } if (Horde_Util::getFormData('end_or_dur') == 1) { if ($allDay) { $start_hour = 0; $start_min = 0; $dur_day = 0; $dur_hour = 24; $dur_min = 0; } else { $dur_day = (int) Horde_Util::getFormData('dur_day'); $dur_hour = (int) Horde_Util::getFormData('dur_hour'); $dur_min = (int) Horde_Util::getFormData('dur_min'); } } $this->start = new Horde_Date(array('hour' => $start_hour, 'min' => $start_min, 'month' => $start_month, 'mday' => $start_day, 'year' => $start_year), $this->timezone); } // Event end. if ($end_date = Horde_Util::getFormData('end_date')) { // From ajax interface. $this->end = Kronolith::parseDate($end_date . ' ' . Horde_Util::getFormData('end_time'), true, $this->timezone); if ($allDay) { $this->end->hour = $this->end->min = $this->end->sec = 0; $this->end->mday++; } } elseif (Horde_Util::getFormData('end_or_dur') == 1) { // Event duration from traditional interface. $this->end = new Horde_Date(array('hour' => $start_hour + $dur_hour, 'min' => $start_min + $dur_min, 'month' => $start_month, 'mday' => $start_day + $dur_day, 'year' => $start_year)); } elseif ($end = Horde_Util::getFormData('end')) { // From traditional interface. $end_year = $end['year']; $end_month = $end['month']; $end_day = $end['day']; $end_hour = Horde_Util::getFormData('end_hour'); $end_min = Horde_Util::getFormData('end_min'); $end_am_pm = Horde_Util::getFormData('end_am_pm'); if (!$prefs->getValue('twentyFour')) { if ($end_am_pm == 'PM') { if ($end_hour != 12) { $end_hour += 12; } } elseif ($end_hour == 12) { $end_hour = 0; } } $this->end = new Horde_Date(array('hour' => $end_hour, 'min' => $end_min, 'month' => $end_month, 'mday' => $end_day, 'year' => $end_year), $this->timezone); if ($this->end->compareDateTime($this->start) < 0) { $this->end = new Horde_Date($this->start); } } $this->allday = false; // Alarm. if (!is_null($alarm = Horde_Util::getFormData('alarm'))) { if ($alarm) { $value = Horde_Util::getFormData('alarm_value'); $unit = Horde_Util::getFormData('alarm_unit'); if ($value == 0) { $value = $unit = 1; } $this->alarm = $value * $unit; // Notification. if (Horde_Util::getFormData('alarm_change_method')) { $types = Horde_Util::getFormData('event_alarms'); $methods = array(); if (!empty($types)) { foreach ($types as $type) { $methods[$type] = array(); switch ($type) { case 'notify': $methods[$type]['sound'] = Horde_Util::getFormData('event_alarms_sound'); break; case 'mail': $methods[$type]['email'] = Horde_Util::getFormData('event_alarms_email'); break; case 'popup': break; } } } $this->methods = $methods; } else { $this->methods = array(); } } else { $this->alarm = 0; $this->methods = array(); } } // Recurrence. $this->recurrence = $this->readRecurrenceForm($this->start, $this->timezone, $this->recurrence); // Convert to local timezone. $this->setTimezone(false); $this->_handleResources($existing); // Tags. $this->tags = Horde_Util::getFormData('tags', $this->tags); // Geolocation if (Horde_Util::getFormData('lat') && Horde_Util::getFormData('lon')) { $this->geoLocation = array('lat' => Horde_Util::getFormData('lat'), 'lon' => Horde_Util::getFormData('lon'), 'zoom' => Horde_Util::getFormData('zoom')); } $this->initialized = true; }