/** * @param mixed Kronolith_Event|string $event The event object or error * string to display. */ public function __construct($event) { if (!$event) { echo '<h3>' . _("Event not found") . '</h3>'; exit; } if (is_string($event)) { echo '<h3>' . $event . '</h3>'; exit; } $iCal = new Horde_Icalendar('2.0'); if ($event->calendarType == 'internal') { try { $share = $GLOBALS['injector']->getInstance('Kronolith_Shares')->getShare($event->calendar); $iCal->setAttribute('X-WR-CALNAME', $share->get('name')); } catch (Exception $e) { } } $iCal->addComponent($event->toiCalendar($iCal)); $content = $iCal->exportvCalendar(); $GLOBALS['browser']->downloadHeaders($event->getTitle() . '.ics', 'text/calendar; charset=UTF-8', true, strlen($content)); echo $content; exit; }
/** * Variables required in form input: * - identity (TODO: ? Code uses it, but it is never set anywhere) * - imple_submit: itip_action(s) * - mime_id * - muid * * @return boolean True on success. */ protected function _handle(Horde_Variables $vars) { global $injector, $notification, $registry; $actions = (array) $vars->imple_submit; $result = false; $vCal = new Horde_Icalendar(); /* Retrieve the calendar data from the message. */ try { $contents = $injector->getInstance('IMP_Factory_Contents')->create(new IMP_Indices_Mailbox($vars)); $mime_part = $contents->getMIMEPart($vars->mime_id); if (empty($mime_part)) { throw new IMP_Exception(_("Cannot retrieve calendar data from message.")); } elseif (!$vCal->parsevCalendar($mime_part->getContents(), 'VCALENDAR', $mime_part->getCharset())) { throw new IMP_Exception(_("The calendar data is invalid")); } $components = $vCal->getComponents(); } catch (Exception $e) { $notification->push($e, 'horde.error'); $actions = array(); } foreach ($actions as $key => $action) { $pos = strpos($key, '['); $key = substr($key, $pos + 1, strlen($key) - $pos - 2); switch ($action) { case 'delete': // vEvent cancellation. if ($registry->hasMethod('calendar/delete')) { $guid = $components[$key]->getAttribute('UID'); $recurrenceId = null; try { // This is a cancellation of a recurring event instance. $recurrenceId = $components[$key]->getAttribute('RECURRENCE-ID'); $atts = $components[$key]->getAttribute('RECURRENCE-ID', true); $range = null; foreach ($atts as $att) { if (array_key_exists('RANGE', $att)) { $range = $att['RANGE']; } } } catch (Horde_Icalendar_Exception $e) { } try { $registry->call('calendar/delete', array($guid, $recurrenceId, $range)); $notification->push(_("Event successfully deleted."), 'horde.success'); $result = true; } catch (Horde_Exception $e) { $notification->push(sprintf(_("There was an error deleting the event: %s"), $e->getMessage()), 'horde.error'); } } else { $notification->push(_("This action is not supported."), 'horde.warning'); } break; case 'update': // vEvent reply. if ($registry->hasMethod('calendar/updateAttendee')) { try { $from = $contents->getHeader()->getOb('from'); $registry->call('calendar/updateAttendee', array($components[$key], $from[0]->bare_address)); $notification->push(_("Respondent Status Updated."), 'horde.success'); $result = true; } catch (Horde_Exception $e) { $notification->push(sprintf(_("There was an error updating the event: %s"), $e->getMessage()), 'horde.error'); } } else { $notification->push(_("This action is not supported."), 'horde.warning'); } break; case 'import': case 'accept-import': // vFreebusy reply. // vFreebusy publish. // vEvent request. // vEvent publish. // vTodo publish. // vJournal publish. switch ($components[$key]->getType()) { case 'vEvent': $result = $this->_handlevEvent($key, $components, $mime_part); // Must check for exceptions. foreach ($components as $k => $component) { try { if ($component->getType() == 'vEvent' && $component->getAttribute('RECURRENCE-ID')) { $uid = $component->getAttribute('UID'); if ($uid == $components[$key]->getAttribute('UID')) { $this->_handlevEvent($k, $components, $mime_part); } } } catch (Horde_Icalendar_Exception $e) { } } break; case 'vFreebusy': // Import into Kronolith. if ($registry->hasMethod('calendar/import_vfreebusy')) { try { $registry->call('calendar/import_vfreebusy', array($components[$key])); $notification->push(_("The user's free/busy information was sucessfully stored."), 'horde.success'); $result = true; } catch (Horde_Exception $e) { $notification->push(sprintf(_("There was an error importing user's free/busy information: %s"), $e->getMessage()), 'horde.error'); } } else { $notification->push(_("This action is not supported."), 'horde.warning'); } break; case 'vTodo': // Import into Nag. if ($registry->hasMethod('tasks/import')) { try { $guid = $registry->call('tasks/import', array($components[$key], $mime_part->getType())); $url = Horde::url($registry->link('tasks/show', array('uid' => $guid))); $notification->push(_("The task has been added to your tasklist.") . ' ' . Horde::link($url, _("View task"), null, '_blank') . Horde_Themes_Image::tag('mime/icalendar.png', array('alt' => _("View task"))) . '</a>', 'horde.success', array('content.raw')); $result = true; } catch (Horde_Exception $e) { $notification->push(sprintf(_("There was an error importing the task: %s"), $e->getMessage()), 'horde.error'); } } else { $notification->push(_("This action is not supported."), 'horde.warning'); } break; case 'vJournal': default: $notification->push(_("This action is not supported."), 'horde.warning'); } if ($action == 'import') { break; } // Fall-through for 'accept-import' // Fall-through for 'accept-import' case 'accept': case 'deny': case 'tentative': // vEvent request. if (isset($components[$key]) && $components[$key]->getType() == 'vEvent') { $vEvent = $components[$key]; $resource = new Horde_Itip_Resource_Identity($injector->getInstance('IMP_Identity'), $vEvent->getAttribute('ATTENDEE'), $vars->identity); switch ($action) { case 'accept': case 'accept-import': $type = new Horde_Itip_Response_Type_Accept($resource); break; case 'deny': $type = new Horde_Itip_Response_Type_Decline($resource); break; case 'tentative': $type = new Horde_Itip_Response_Type_Tentative($resource); break; } try { // Send the reply. Horde_Itip::factory($vEvent, $resource)->sendMultiPartResponse($type, new Horde_Core_Itip_Response_Options_Horde('UTF-8', array()), $injector->getInstance('IMP_Mail')); $notification->push(_("Reply Sent."), 'horde.success'); $result = true; } catch (Horde_Itip_Exception $e) { $notification->push(sprintf(_("Error sending reply: %s."), $e->getMessage()), 'horde.error'); } } else { $notification->push(_("This action is not supported."), 'horde.warning'); } break; case 'send': case 'reply': case 'reply2m': // vfreebusy request. if (isset($components[$key]) && $components[$key]->getType() == 'vFreebusy') { $vFb = $components[$key]; // Get the organizer details. try { $organizer = parse_url($vFb->getAttribute('ORGANIZER')); } catch (Horde_Icalendar_Exception $e) { break; } $organizerEmail = $organizer['path']; $organizer = $vFb->getAttribute('ORGANIZER', true); $organizerFullEmail = new Horde_Mail_Rfc822_Address($organizerEmail); if (isset($organizer['cn'])) { $organizerFullEmail->personal = $organizer['cn']; } if ($action == 'reply2m') { $startStamp = time(); $endStamp = $startStamp + 60 * 24 * 3600; } else { try { $startStamp = $vFb->getAttribute('DTSTART'); } catch (Horde_Icalendar_Exception $e) { $startStamp = time(); } try { $endStamp = $vFb->getAttribute('DTEND'); } catch (Horde_Icalendar_Exception $e) { } if (!$endStamp) { try { $duration = $vFb->getAttribute('DURATION'); $endStamp = $startStamp + $duration; } catch (Horde_Icalendar_Exception $e) { $endStamp = $startStamp + 60 * 24 * 3600; } } } $vfb_reply = $registry->call('calendar/getFreeBusy', array($startStamp, $endStamp)); // Find out who we are and update status. $identity = $injector->getInstance('IMP_Identity'); $email = $identity->getFromAddress(); // Build the reply. $msg_headers = new Horde_Mime_Headers(); $vCal = new Horde_Icalendar(); $vCal->setAttribute('PRODID', '-//The Horde Project//' . $msg_headers->getUserAgent() . '//EN'); $vCal->setAttribute('METHOD', 'REPLY'); $vCal->addComponent($vfb_reply); $message = _("Attached is a reply to a calendar request you sent."); $body = new Horde_Mime_Part(); $body->setType('text/plain'); $body->setCharset('UTF-8'); $body->setContents(Horde_String::wrap($message, 76)); $ics = new Horde_Mime_Part(); $ics->setType('text/calendar'); $ics->setCharset('UTF-8'); $ics->setContents($vCal->exportvCalendar()); $ics->setName('icalendar.ics'); $ics->setContentTypeParameter('METHOD', 'REPLY'); $mime = new Horde_Mime_Part(); $mime->addPart($body); $mime->addPart($ics); // Build the reply headers. $msg_headers->addReceivedHeader(array('dns' => $injector->getInstance('Net_DNS2_Resolver'), 'server' => $conf['server']['name'])); $msg_headers->addMessageIdHeader(); $msg_headers->addHeader('Date', date('r')); $msg_headers->addHeader('From', $email); $msg_headers->addHeader('To', $organizerFullEmail); $identity->setDefault($vars->identity); $replyto = $identity->getValue('replyto_addr'); if (!empty($replyto) && !$email->match($replyto)) { $msg_headers->addHeader('Reply-To', $replyto); } $msg_headers->addHeader('Subject', _("Free/Busy Request Response")); // Send the reply. try { $mime->send($organizerEmail, $msg_headers, $injector->getInstance('IMP_Mail')); $notification->push(_("Reply Sent."), 'horde.success'); $result = true; } catch (Exception $e) { $notification->push(sprintf(_("Error sending reply: %s."), $e->getMessage()), 'horde.error'); } } else { $notification->push(_("Invalid Action selected for this component."), 'horde.warning'); } break; case 'nosup': // vFreebusy request. // vFreebusy request. default: $notification->push(_("This action is not supported."), 'horde.warning'); break; } } return $result; }
/** * Exports a calendar in the requested content type. * * @param string $calendar The calendar to export. * @param string $contentType What format should the data be in? * A string with one of: * <pre> * text/calendar (VCALENDAR 2.0. Recommended as * this is specified in rfc2445) * text/x-vcalendar (old VCALENDAR 1.0 format. * Still in wide use) * </pre> * * @return string The iCalendar representation of the calendar. * @throws Kronolith_Exception */ public function exportCalendar($calendar, $contentType) { if (!Kronolith::hasPermission($calendar, Horde_Perms::READ)) { throw new Horde_Exception_PermissionDenied(); } $kronolith_driver = Kronolith::getDriver(null, $calendar); $events = $kronolith_driver->listEvents(null, null, array('cover_dates' => false, 'hide_exceptions' => true)); $version = '2.0'; switch ($contentType) { case 'text/x-vcalendar': $version = '1.0'; case 'text/calendar': $share = $GLOBALS['injector']->getInstance('Kronolith_Shares')->getShare($calendar); $iCal = new Horde_Icalendar($version); $iCal->setAttribute('X-WR-CALNAME', $share->get('name')); if (strlen($share->get('desc'))) { $iCal->setAttribute('X-WR-CALDESC', $share->get('desc')); } foreach ($events as $dayevents) { foreach ($dayevents as $event) { $iCal->addComponent($event->toiCalendar($iCal)); } } return $iCal->exportvCalendar(); } throw new Kronolith_Exception(sprintf(_("Unsupported Content-Type: %s"), $contentType)); }
$notification->push(sprintf(_("The calendar could not be purged: %s"), $e->getMessage()), 'horde.error'); } } } $recurrences = array(); $ical = null; foreach ($next_step as $row) { if ($max_events !== true && $num_events >= $max_events) { Horde::permissionDeniedError('kronolith', 'max_events', sprintf(_("You are not allowed to create more than %d events."), $perms->hasAppPermission('max_events'))); break; } if ($row instanceof Horde_Icalendar_Vevent) { if (!$ical) { $ical = new Horde_Icalendar(); } $ical->addComponent($row); if ($max_events !== true) { $num_events++; } continue; } try { $event = $kronolith_driver->getEvent(); } catch (Exception $e) { $msg = _("Can't create a new event.") . ' ' . sprintf(_("This is what the server said: %s"), $e->getMessage()); $notification->push($msg, 'horde.error'); $error = true; break; } if ($row instanceof Horde_Icalendar) { // Skip other iCalendar components for now.
/** */ public function davGetObject($collection, $object) { $dav = $GLOBALS['injector']->getInstance('Horde_Dav_Storage'); $internal = $dav->getInternalCollectionId($collection, 'calendar') ?: $collection; if (!Kronolith::hasPermission($internal, Horde_Perms::SHOW)) { throw new Kronolith_Exception(_("Calendar does not exist or no permission to edit")); } $kronolith_driver = Kronolith::getDriver(null, $internal); try { $object = $dav->getInternalObjectId($object, $internal) ?: preg_replace('/\\.ics$/', '', $object); } catch (Horde_Dav_Exception $e) { } $event = $kronolith_driver->getEvent($object); $id = $event->id; try { $id = $dav->getExternalObjectId($id, $internal) ?: $id . '.ics'; } catch (Horde_Dav_Exception $e) { } $event->loadHistory(); $modified = $event->modified ?: $event->created; $share = $GLOBALS['injector']->getInstance('Kronolith_Shares')->getShare($event->calendar); $ical = new Horde_Icalendar('2.0'); $ical->setAttribute('X-WR-CALNAME', $share->get('name')); $ical->addComponent($event->toiCalendar($ical)); $data = $ical->exportvCalendar(); return array('id' => $id, 'calendardata' => $data, 'uri' => $id, 'lastmodified' => $modified, 'etag' => '"' . md5($event->id . '|' . $modified) . '"', 'calendarid' => $collection, 'size' => strlen($data)); }
/** * Updates an existing event in the backend. * * @param Kronolith_Event $event The event to save. * * @return string The event id. * @throws Horde_Mime_Exception * @throws Kronolith_Exception */ protected function _saveEvent($event) { $ical = new Horde_Icalendar(); $ical->addComponent($event->toiCalendar($ical)); $url = trim($this->_getUrl(), '/') . '/' . $event->id; try { return $this->_getClient($url)->request('PUT', '', $ical->exportvCalendar(), array('Content-Type' => 'text/calendar')); } catch (Horde_Dav_Exception $e) { Horde::log($e, 'INFO'); throw new Kronolith_Exception($e); } }
/** */ public function davGetObject($collection, $object) { $dav = $GLOBALS['injector']->getInstance('Horde_Dav_Storage'); $internal = $dav->getInternalCollectionId($collection, 'tasks') ?: $collection; if (!Nag::hasPermission($internal, Horde_Perms::READ)) { throw new Nag_Exception("Task List does not exist or no permission to edit"); } try { $object = $dav->getInternalObjectId($object, $internal) ?: preg_replace('/\\.ics$/', '', $object); } catch (Horde_Dav_Exception $e) { } $task = Nag::getTask($internal, $object); $id = $task->id; $modified = $this->_modified($internal, $task->uid); try { $id = $dav->getExternalObjectId($id, $internal) ?: $id . '.ics'; } catch (Horde_Dav_Exception $e) { } $share = $GLOBALS['nag_shares']->getShare($internal); $ical = new Horde_Icalendar('2.0'); $ical->setAttribute('X-WR-CALNAME', $share->get('name')); $ical->addComponent($task->toiCalendar($ical)); $data = $ical->exportvCalendar(); return array('id' => $id, 'calendardata' => $data, 'uri' => $id, 'lastmodified' => $modified, 'etag' => '"' . md5($task->id . '|' . $modified) . '"', 'calendarid' => $collection, 'size' => strlen($data)); }
public function testTimezone() { $date = new Horde_Date(array('year' => 2010, 'month' => 1, 'mday' => 1, 'hour' => 1, 'min' => 0, 'sec' => 0), 'UTC'); $ical = new Horde_Icalendar(); $event = Horde_Icalendar::newComponent('vevent', $ical); $event->setAttribute('UID', 'uid'); $event->setAttribute('DTSTAMP', $date); $event->setAttribute('DTSTART', $date); $ical->addComponent($event); $this->assertEquals('BEGIN:VCALENDAR VERSION:2.0 PRODID:-//The Horde Project//Horde iCalendar Library//EN BEGIN:VEVENT UID:uid DTSTAMP:20100101T010000Z DTSTART:20100101T010000Z END:VEVENT END:VCALENDAR ', $ical->exportVCalendar()); $ical = new Horde_Icalendar(); $event = Horde_Icalendar::newComponent('vevent', $ical); $event->setAttribute('UID', 'uid'); $event->setAttribute('DTSTAMP', $date); $date->setTimezone('Europe/Berlin'); $event->setAttribute('DTSTART', $date, array('TZID' => 'Europe/Berlin')); $ical->addComponent($event); $this->assertEquals('BEGIN:VCALENDAR VERSION:2.0 PRODID:-//The Horde Project//Horde iCalendar Library//EN BEGIN:VEVENT UID:uid DTSTAMP:20100101T010000Z DTSTART;TZID=Europe/Berlin:20100101T020000 END:VEVENT END:VCALENDAR ', $ical->exportVCalendar()); $ical = new Horde_Icalendar(); $tz = $ical->parsevCalendar('BEGIN:VCALENDAR BEGIN:VTIMEZONE TZID:Europe/Berlin BEGIN:DAYLIGHT TZOFFSETFROM:+0100 TZOFFSETTO:+0200 DTSTART:19800406T010000 RRULE:FREQ=YEARLY;BYMONTH=4;BYDAY=1SU;UNTIL=19800406T00000Z TZNAME:CEST END:DAYLIGHT BEGIN:STANDARD TZOFFSETFROM:+0200 TZOFFSETTO:+0100 DTSTART:19800928T010000 RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=9;UNTIL=19950923T23000Z TZNAME:CE-T END:STANDARD BEGIN:DAYLIGHT TZOFFSETFROM:+0100 TZOFFSETTO:+0200 DTSTART:19810329T010000 RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=3 TZNAME:CEST END:DAYLIGHT BEGIN:STANDARD TZOFFSETFROM:+0200 TZOFFSETTO:+0100 DTSTART:19961027T010000 RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10 TZNAME:CE-T END:STANDARD END:VTIMEZONE END:VCALENDAR '); $tz = $ical->getComponent(0); $ical = new Horde_Icalendar(); $ical->addComponent($tz); $event = Horde_Icalendar::newComponent('vevent', $ical); $event->setAttribute('UID', 'uid'); $event->setAttribute('DTSTAMP', $date); $date->setTimezone('Europe/Berlin'); $event->setAttribute('DTSTART', $date, array('TZID' => 'Europe/Berlin')); $ical->addComponent($event); $ical->addComponent($tz); $event = Horde_Icalendar::newComponent('vevent', $ical); $event->setAttribute('UID', 'uid2'); $event->setAttribute('DTSTAMP', $date); $date->setTimezone('Europe/Berlin'); $start = clone $date; $start->mday++; $event->setAttribute('DTSTART', $start, array('TZID' => 'Europe/Berlin')); $ical->addComponent($event); $this->assertEquals('BEGIN:VCALENDAR VERSION:2.0 PRODID:-//The Horde Project//Horde iCalendar Library//EN BEGIN:VTIMEZONE TZID:Europe/Berlin BEGIN:DAYLIGHT TZOFFSETFROM:+0100 TZOFFSETTO:+0200 DTSTART:19800406T010000 RRULE:FREQ=YEARLY;BYMONTH=4;BYDAY=1SU;UNTIL=19800406T00000Z TZNAME:CEST END:DAYLIGHT BEGIN:STANDARD TZOFFSETFROM:+0200 TZOFFSETTO:+0100 DTSTART:19800928T010000 RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=9;UNTIL=19950923T23000Z TZNAME:CE-T END:STANDARD BEGIN:DAYLIGHT TZOFFSETFROM:+0100 TZOFFSETTO:+0200 DTSTART:19810329T010000 RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=3 TZNAME:CEST END:DAYLIGHT BEGIN:STANDARD TZOFFSETFROM:+0200 TZOFFSETTO:+0100 DTSTART:19961027T010000 RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10 TZNAME:CE-T END:STANDARD END:VTIMEZONE BEGIN:VEVENT UID:uid DTSTAMP:20100101T010000Z DTSTART;TZID=Europe/Berlin:20100101T020000 END:VEVENT BEGIN:VEVENT UID:uid2 DTSTAMP:20100101T010000Z DTSTART;TZID=Europe/Berlin:20100102T020000 END:VEVENT END:VCALENDAR ', $ical->exportVCalendar()); }
/** * Exports a tasklist in the requested content type. * * @param string $tasklist The tasklist to export. * @param string $contentType What format should the data be in? * A string with one of: * <pre> * text/calendar (VCALENDAR 2.0. Recommended as * this is specified in rfc2445) * text/x-vcalendar (old VCALENDAR 1.0 format. * Still in wide use) * </pre> * * @return string The iCalendar representation of the tasklist. */ public function exportTasklist($tasklist, $contentType) { if (!Nag::hasPermission($tasklist, Horde_Perms::READ)) { throw new Horde_Exception_PermissionDenied(); } $tasks = Nag::listTasks(array('tasklists' => array($tasklist), 'completed' => Nag::VIEW_ALL, 'external' => false, 'include_tags' => true)); $version = '2.0'; switch ($contentType) { case 'text/x-vcalendar': $version = '1.0'; case 'text/calendar': $share = $GLOBALS['nag_shares']->getShare($tasklist); $iCal = new Horde_Icalendar($version); $iCal->setAttribute('X-WR-CALNAME', $share->get('name')); $tasks->reset(); while ($task = $tasks->each()) { $iCal->addComponent($task->toiCalendar($iCal)); } return $iCal->exportvCalendar(); } throw new Nag_Exception(sprintf(_("Unsupported Content-Type: %s"), $contentType)); }
public function testTimezone() { $date = new Horde_Date(array('year' => 2010, 'month' => 1, 'mday' => 1, 'hour' => 1, 'min' => 0, 'sec' => 0), 'UTC'); $ical = new Horde_Icalendar(); $event = Horde_Icalendar::newComponent('vevent', $ical); $event->setAttribute('UID', 'uid'); $event->setAttribute('DTSTAMP', $date); $event->setAttribute('DTSTART', $date); $ical->addComponent($event); $this->assertStringEqualsFile(__DIR__ . '/fixtures/timezone1.ics', $ical->exportVCalendar()); $ical = new Horde_Icalendar(); $event = Horde_Icalendar::newComponent('vevent', $ical); $event->setAttribute('UID', 'uid'); $event->setAttribute('DTSTAMP', $date); $date->setTimezone('Europe/Berlin'); $event->setAttribute('DTSTART', $date, array('TZID' => 'Europe/Berlin')); $ical->addComponent($event); $this->assertStringEqualsFile(__DIR__ . '/fixtures/timezone2.ics', $ical->exportVCalendar()); $ical = new Horde_Icalendar(); $tz = $ical->parsevCalendar(file_get_contents(__DIR__ . '/fixtures/timezone3.ics')); $tz = $ical->getComponent(0); $ical = new Horde_Icalendar(); $ical->addComponent($tz); $event = Horde_Icalendar::newComponent('vevent', $ical); $event->setAttribute('UID', 'uid'); $event->setAttribute('DTSTAMP', $date); $date->setTimezone('Europe/Berlin'); $event->setAttribute('DTSTART', $date, array('TZID' => 'Europe/Berlin')); $ical->addComponent($event); $ical->addComponent($tz); $event = Horde_Icalendar::newComponent('vevent', $ical); $event->setAttribute('UID', 'uid2'); $event->setAttribute('DTSTAMP', $date); $date->setTimezone('Europe/Berlin'); $start = clone $date; $start->mday++; $event->setAttribute('DTSTART', $start, array('TZID' => 'Europe/Berlin')); $ical->addComponent($event); $this->assertStringEqualsFile(__DIR__ . '/fixtures/timezone4.ics', $ical->exportVCalendar()); }