array(), 'type' => '{urn:horde}stringArray' ); $_services['show'] = array( 'link' => '%application%/viewevent.php?calendar=|calendar|' . '&eventID=|event|&uid=|uid|' ); $_services['browse'] = array( 'args' => array('path' => 'string'), 'type' => '{urn:hash}hashHash', ); $_services['getFreeBusy'] = array( 'args' => array('startstamp' => 'int', 'endstamp' => 'int', 'calendar' => 'string'), 'type' => '{urn:horde}stringArray' ); $_services['listCalendars'] = array( 'args' => array('owneronly' => 'boolean', 'permission' => 'int'), 'type' => '{urn:horde}stringArray' ); $_services['listEvents'] = array( 'args' => array('startstamp' => 'int', 'endstamp' => 'int', 'calendar' => 'string', 'showRecurrence' => 'string'), 'type' => 'array' ); $_services['list'] = array( 'args' => array(), 'type' => '{urn:horde}stringArray' ); $_services['listBy'] = array( 'args' => array('action' => 'string', 'timestamp' => 'int'), 'type' => '{urn:horde}stringArray' ); $_services['getActionTimestamp'] = array( 'args' => array('uid' => 'string', 'timestamp' => 'int'), 'type' => 'int', ); $_services['import'] = array( 'args' => array('content' => 'string', 'contentType' => 'string', 'calendar' => 'string'), 'type' => 'int' ); $_services['export'] = array( 'args' => array('uid' => 'string', 'contentType' => 'string'), 'type' => 'string' ); $_services['exportCalendar'] = array( 'args' => array('calendar' => 'string', 'contentType' => 'string'), 'type' => 'string' ); $_services['delete'] = array( 'args' => array('uid' => 'string'), 'type' => 'boolean' ); $_services['replace'] = array( 'args' => array('uid' => 'string', 'content' => 'string', 'contentType' => 'string'), 'type' => 'boolean' ); // FIXME: create complex type definition for SOAP calls. $_services['eventFromUID'] = array( 'args' => array('uid' => 'string'), 'type' => 'object' ); // FIXME: create complex type definition for SOAP calls. $_services['updateAttendee'] = array( 'args' => array('response' => 'object'), 'type' => 'boolean' ); function _kronolith_perms() { $perms = array(); $perms['tree']['kronolith']['max_events'] = false; $perms['title']['kronolith:max_events'] = _("Maximum Number of Events"); $perms['type']['kronolith:max_events'] = 'int'; return $perms; } function __kronolith_modified($uid) { $modified = _kronolith_getActionTimestamp($uid, 'modify'); if (empty($modified)) { $modified = _kronolith_getActionTimestamp($uid, 'add'); } return $modified; } /** * Browse through Kronolith's object tree. * * @param string $path The level of the tree to browse. * @param array $properties The item properties to return. Defaults to 'name', * 'icon', and 'browseable'. * * @return array The contents of $path */ function _kronolith_browse($path = '', $properties = array()) { require_once dirname(__FILE__) . '/base.php'; global $registry; // Default properties. if (!$properties) { $properties = array('name', 'icon', 'browseable'); } if (substr($path, 0, 9) == 'kronolith') { $path = substr($path, 9); } if (substr($path, 0, 1) == '/') { $path = substr($path, 1); } if (substr($path, -1) == '/') { $path = substr($path, 0, -1); } if (empty($path)) { $calendars = Kronolith::listCalendars(false, PERMS_SHOW); $results = array(); foreach ($calendars as $calendarId => $calendar) { if (in_array('name', $properties)) { $results['kronolith/' . $calendarId]['name'] = $calendar->get('name'); } if (in_array('icon', $properties)) { $results['kronolith/' . $calendarId]['icon'] = $registry->getImageDir() . '/kronolith.png'; } if (in_array('browseable', $properties)) { $results['kronolith/' . $calendarId]['browseable'] = $calendar->hasPermission(Auth::getAuth(), PERMS_READ); } if (in_array('contenttype', $properties)) { $results['kronolith/' . $calendarId]['contenttype'] = 'httpd/unix-directory'; } if (in_array('contentlength', $properties)) { $results['kronolith/' . $calendarId]['contentlength'] = 0; } if (in_array('modified', $properties)) { $results['kronolith/' . $calendarId]['modified'] = time(); } if (in_array('created', $properties)) { $results['kronolith/' . $calendarId]['created'] = 0; } } return $results; } elseif (array_key_exists($path, Kronolith::listCalendars(false, PERMS_READ))) { $events = Kronolith::listEvents(0, new Horde_Date(array('year' => 9999, 'month' => 12, 'day' => 31)), array($path), false); if (is_a($events, 'PEAR_Error')) { return $events; } $results = array(); foreach ($events as $timestamp => $timeevents) { foreach ($timeevents as $eventId => $event) { $key = 'kronolith/' . $path . '/' . $eventId; if (in_array('name', $properties)) { $results[$key]['name'] = $event->getTitle(); } if (in_array('icon', $properties)) { $results[$key]['icon'] = $registry->getImageDir('horde') . '/mime/icalendar.png'; } if (in_array('browseable', $properties)) { $results[$key]['browseable'] = false; } if (in_array('contenttype', $properties)) { $results[$key]['contenttype'] = 'text/calendar'; } if (in_array('contentlength', $properties)) { $data = _kronolith_export($event->getUID(), 'text/calendar'); if (is_a($data, 'PEAR_Error')) { $data = ''; } $results[$key]['contentlength'] = strlen($data); } if (in_array('modified', $properties)) { $results[$key]['modified'] = __kronolith_modified($event->getUID()); } if (in_array('created', $properties)) { $results[$key]['created'] = _kronolith_getActionTimestamp($event->getUID(), 'add'); } } } return $results; } else { $parts = explode('/', $path); if (count($parts) == 2 && array_key_exists($parts[0], Kronolith::listCalendars(false, PERMS_READ))) { global $kronolith; if ($kronolith->getCalendar() != $parts[0]) { $kronolith->close(); $kronolith->open($parts[0]); } $event = &$kronolith->getEvent($parts[1]); if (is_a($event, 'PEAR_Error')) { return $event; } $result = array('data' => _kronolith_export($event->getUID(), 'text/calendar'), 'mimetype' => 'text/calendar'); $modified = __kronolith_modified($event->getUID()); if (!empty($modified)) { $result['mtime'] = $modified; } return $result; } } return PEAR::raiseError($path . ' does not exist or permission denied'); } function _kronolith_listCalendars($owneronly = false, $permission = null) { require_once dirname(__FILE__) . '/base.php'; if (!isset($permission)) { $permission = PERMS_SHOW; } return array_keys(Kronolith::listCalendars($owneronly, $permission)); } function _kronolith_list($calendar = null) { require_once dirname(__FILE__) . '/base.php'; if (empty($calendar)) { $calendar = Kronolith::getDefaultCalendar(); } $ids = Kronolith::listEventIds(0, new Horde_Date(array('year' => 9999, 'month' => 12, 'day' => 31)), $calendar); if (is_a($ids, 'PEAR_Error')) { return $ids; } $uids = array(); foreach ($ids as $cal) { $uids = array_merge($uids, array_keys($cal)); } return $uids; } /** * Returns an array of UIDs for events that have had $action happen since * $timestamp. * * @param string $action The action to check for - add, modify, or delete. * @param integer $timestamp The time to start the search. * @param string $calendar The calendar to search in. * * @return array An array of UIDs matching the action and time criteria. */ function _kronolith_listBy($action, $timestamp, $calendar = null) { require_once dirname(__FILE__) . '/base.php'; if (empty($calendar)) { $calendar = Kronolith::getDefaultCalendar(); } $history = &Horde_History::singleton(); $histories = $history->getByTimestamp('>', $timestamp, array(array('op' => '=', 'field' => 'action', 'value' => $action)), 'kronolith:' . $calendar); if (is_a($histories, 'PEAR_Error')) { return $histories; } // Strip leading kronolith:username:. return preg_replace('/^([^:]*:){2}/', '', array_keys($histories)); } /** * Returns the timestamp of an operation for a given uid an action * * @param string $uid The uid to look for. * @param string $action The action to check for - add, modify, or delete. * @param string $calendar The calendar to search in. * * @return integer The timestamp for this action. */ function _kronolith_getActionTimestamp($uid, $action, $calendar = null) { require_once dirname(__FILE__) . '/base.php'; if (empty($calendar)) { $calendar = Kronolith::getDefaultCalendar(); } $history = &Horde_History::singleton(); return $history->getActionTimestamp('kronolith:' . $calendar . ':' . $uid, $action); } /** * Imports an event represented in the specified content type. * * @param string $content The content of the event. * @param string $contentType What format is the data in? Currently supports: *
 *                             text/calendar
 *                             text/x-icalendar
 *                             text/x-vcalendar
 *                             text/x-vevent
 *                             
* @param string $calendar What calendar should the event be added to? * * @return mixed The event's UID, or a PEAR_Error on failure. */ function _kronolith_import($content, $contentType, $calendar = null) { require_once dirname(__FILE__) . '/base.php'; global $kronolith; if (!isset($calendar)) { $calendar = Kronolith::getDefaultCalendar(PERMS_EDIT); } if (!array_key_exists($calendar, Kronolith::listCalendars(false, PERMS_EDIT))) { return PEAR::raiseError(_("Permission Denied")); } $kronolith->open($calendar); switch ($contentType) { case 'text/calendar': case 'text/x-icalendar': case 'text/x-vcalendar': case 'text/x-vevent': require_once 'Horde/iCalendar.php'; $iCal = new Horde_iCalendar(); if (!is_a($content, 'Horde_iCalendar_vevent')) { if (!$iCal->parsevCalendar($content)) { return PEAR::raiseError(_("There was an error importing the iCalendar data.")); } } else { $iCal->addComponent($content); } $components = $iCal->getComponents(); if (count($components) == 0) { return PEAR::raiseError(_("No iCalendar data was found.")); } $ids = array(); foreach ($components as $content) { if (is_a($content, 'Horde_iCalendar_vevent')) { $event = &$kronolith->getEvent(); $event->fromiCalendar($content); $eventId = $event->save(); if (is_a($eventId, 'PEAR_Error')) { return $eventId; } $ids[] = $event->getUID(); } } if (count($ids) == 0) { return PEAR::raiseError(_("No iCalendar data was found.")); } else if (count($ids) == 1) { return $ids[0]; } return $ids; } return PEAR::raiseError(sprintf(_("Unsupported Content-Type: %s"), $contentType)); } /** * Exports an event, identified by UID, in the requested content type. * * @param string $uid Identify the event to export. * @param string $contentType What format should the data be in? * A string with one of: *
 *                             text/calendar (VCALENDAR 2.0. Recommended as
 *                                            this is specified in rfc2445)
 *                             text/x-vtodo    Seems to be used by horde only.
 *                                             Do we need this?
 *                             text/x-vcalendar (old VCALENDAR 1.0 format.
 *                                              Still in wide use)
 *                             text/x-icalendar
 *                            
* * @return string The requested data. */ function _kronolith_export($uid, $contentType) { require_once dirname(__FILE__) . '/base.php'; global $kronolith, $kronolith_shares; $event = $kronolith->getByUID($uid); if (is_a($event, 'PEAR_Error')) { return $event; } if (!array_key_exists($event->getCalendar(), Kronolith::listCalendars(false, PERMS_READ))) { return PEAR::raiseError(_("Permission Denied")); } $version = '2.0'; switch ($contentType) { case 'text/x-vcalendar': $version = '1.0'; case 'text/calendar': case 'text/x-icalendar': case 'text/x-vtodo': $share = &$kronolith_shares->getShare($event->getCalendar()); $identity = &$kronolith_shares->getIdentityByShare($share); require_once 'Horde/iCalendar.php'; $iCal = new Horde_iCalendar($version); $iCal->setAttribute('X-WR-CALNAME', $share->get('name')); // Create a new vEvent. $vEvent = &$event->toiCalendar($iCal, $identity); $iCal->addComponent($vEvent); return $iCal->exportvCalendar(); } return PEAR::raiseError(sprintf(_("Unsupported Content-Type: %s"), $contentType));; } /** * 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: *
 *                             text/calendar (VCALENDAR 2.0. Recommended as
 *                                            this is specified in rfc2445)
 *                             text/x-vtodo    Seems to be used by horde only.
 *                                             Do we need this?
 *                             text/x-vcalendar (old VCALENDAR 1.0 format.
 *                                              Still in wide use)
 *                             text/x-icalendar
 *                             
* * @return string The iCalendar representation of the calendar. */ function _kronolith_exportCalendar($calendar, $contentType) { require_once dirname(__FILE__) . '/base.php'; global $kronolith, $kronolith_shares; if (!array_key_exists($calendar, Kronolith::listCalendars(false, PERMS_READ))) { return PEAR::raiseError(_("Permission Denied")); } if ($kronolith->getCalendar() != $calendar) { $kronolith->close(); $kronolith->open($calendar); } $events = $kronolith->listEvents(null, null); $version = '2.0'; switch ($contentType) { case 'text/x-vcalendar': $version = '1.0'; case 'text/calendar': case 'text/x-icalendar': case 'text/x-vtodo': $share = &$kronolith_shares->getShare($calendar); $identity = &$kronolith_shares->getIdentityByShare($share); require_once 'Horde/iCalendar.php'; $iCal = new Horde_iCalendar($version); $iCal->setAttribute('X-WR-CALNAME', $share->get('name')); foreach ($events as $id) { $event = &$kronolith->getEvent($id); $vEvent = &$event->toiCalendar($iCal, $identity); $iCal->addComponent($vEvent); } return $iCal->exportvCalendar(); } return PEAR::raiseError(sprintf(_("Unsupported Content-Type: %s"), $contentType)); } /** * Deletes an event identified by UID. * * @param string|array $uid A single UID or an array identifying the event(s) * to delete. * * @return boolean Success or failure. */ function _kronolith_delete($uid) { /* Handle an arrray of UIDs for convenience of deleting multiple events at * once. */ if (is_array($uid)) { foreach ($uid as $g) { $result = _kronolith_delete($g); if (is_a($result, 'PEAR_Error')) { return $result; } } return true; } require_once dirname(__FILE__) . '/base.php'; global $kronolith; $event = $kronolith->getByUID($uid); if (is_a($event, 'PEAR_Error')) { return $event; } if (!array_key_exists($event->getCalendar(), Kronolith::listCalendars(false, PERMS_DELETE))) { return PEAR::raiseError(_("Permission Denied")); } return $kronolith->deleteEvent($event->getID()); } /** * Replaces the event identified by UID with the content represented in the * specified contentType. * * @param string $uid Idenfity the event to replace. * @param string $content The content of the event. * @param string $contentType What format is the data in? Currently supports: * text/calendar * text/x-icalendar * text/x-vcalendar * text/x-vevent * * @return mixed True on success, PEAR_Error otherwise. */ function _kronolith_replace($uid, $content, $contentType) { require_once dirname(__FILE__) . '/base.php'; global $kronolith; $event = $kronolith->getByUID($uid); if (is_a($event, 'PEAR_Error')) { return $event; } if (!array_key_exists($event->getCalendar(), Kronolith::listCalendars(false, PERMS_EDIT))) { return PEAR::raiseError(_("Permission Denied")); } switch ($contentType) { case 'text/calendar': case 'text/x-icalendar': case 'text/x-vcalendar': case 'text/x-vevent': if (!is_a($content, 'Horde_iCalendar_vevent')) { require_once 'Horde/iCalendar.php'; $iCal = new Horde_iCalendar(); if (!$iCal->parsevCalendar($content)) { return PEAR::raiseError(_("There was an error importing the iCalendar data.")); } $components = $iCal->getComponents(); switch (count($components)) { case 0: return PEAR::raiseError(_("No iCalendar data was found.")); case 1: $content = $components[0]; if (!is_a($content, 'Horde_iCalendar_vevent')) { return PEAR::raiseError(_("vEvent not found.")); } break; default: return PEAR::raiseError(_("Multiple iCalendar components found; only one vEvent is supported.")); } } $event->fromiCalendar($content); $eventId = $event->save(); break; default: return PEAR::raiseError(sprintf(_("Unsupported Content-Type: %s"), $contentType)); } return is_a($eventId, 'PEAR_Error') ? $eventId : true; } /** * Generates free/busy information for a given time period. * * @param integer $startstamp The start of the time period to retrieve. * @param integer $endstamp The end of the time period to retrieve. * @param string $calendar The calendar to view free/busy slots for. * Defaults to the user's default calendar. * * @return Horde_iCalendar_vfreebusy A freebusy object that covers the * specified time period. */ function _kronolith_getFreeBusy($startstamp = null, $endstamp = null, $calendar = null) { require_once dirname(__FILE__) . '/base.php'; if (is_null($calendar)) { $calendar = Kronolith::getDefaultCalendar(); } return Kronolith::generateFreeBusy($calendar, $startstamp, $endstamp, true); } /** * Retrieves a Kronolith_Event object, given an event UID. * * @param string $uid The event's UID. * * @return Kronolith_Event A valid Kronolith_Event on success, or a PEAR_Error * on failure. */ function &_kronolith_eventFromUID($uid) { require_once dirname(__FILE__) . '/base.php'; global $kronolith; $event = $kronolith->getByUID($uid); if (is_a($event, 'PEAR_Error')) { return $event; } if (!array_key_exists($event->getCalendar(), Kronolith::listCalendars(false, PERMS_SHOW))) { return PEAR::raiseError(_("Permission Denied")); } return $event; } /** * Updates an attendee's response status for a specified event. * * @param Horde_iCalender_vevent $response A Horde_iCalender_vevent object, * with a valid UID attribute that * points to an existing event. * This is typically the vEvent * portion of an iTip meeting-request * response, with the attendee's * response in an ATTENDEE parameter. * * @return mixed True on success, PEAR_Error on failure. */ function _kronolith_updateAttendee($response) { require_once dirname(__FILE__) . '/base.php'; global $kronolith; $uid = $response->getAttribute('UID'); if (is_a($uid, 'PEAR_Error')) { return $uid; } $event = $kronolith->getByUID($uid); if (is_a($event, 'PEAR_Error')) { return $event; } if (!array_key_exists($event->getCalendar(), Kronolith::listCalendars(false, PERMS_EDIT))) { return PEAR::raiseError(_("Permission Denied")); } $atnames = $response->getAttribute('ATTENDEE'); $atparms = $response->getAttribute('ATTENDEE', true); if (!is_array($atnames)) { $atnames = array($atnames); } foreach ($atnames as $index => $attendee) { $attendee = str_replace('mailto:', '', String::lower($attendee)); if ($event->hasAttendee($attendee)) { $event->addAttendee($attendee, KRONOLITH_PART_IGNORE, Kronolith::responseFromICal($atparms[$index]['PARTSTAT'])); } } $result = $event->save(); if (is_a($result, 'PEAR_Error')) { return $result; } return true; } /** * Lists events for a given time period. * * @param integer $startstamp The start of the time period to retrieve. * @param integer $endstamp The end of the time period to retrieve. * @param string $calendar The calendar to view free/busy slots for. * Defaults to the user's default calendar. * @param boolean $showRecurrence Return every instance of a recurring event? * If false, will only return recurring events * once inside the $startDate - $endDate range. * * @return array An array of UIDs */ function _kronolith_listEvents($startstamp = null, $endstamp = null, $calendar = null, $showRecurrence = true) { require_once dirname(__FILE__) . '/base.php'; if (is_null($calendar)) { $calendar = $GLOBALS['prefs']->getValue('default_share'); } return Kronolith::listEvents($startstamp, $endstamp, $calendar, $showRecurrence); }