Skip to content

Instantly share code, notes, and snippets.

@shirish87
Last active January 2, 2016 23:49
Show Gist options
  • Save shirish87/8379156 to your computer and use it in GitHub Desktop.
Save shirish87/8379156 to your computer and use it in GitHub Desktop.
<?php
/**
* Kronolith external API interface.
*
* This file defines Kronolith's external API interface. Other applications
* can interact with Kronolith through this API.
*
* @package Kronolith
*/
class Kronolith_Api extends Horde_Registry_Api
{
/**
* Links.
*
* @var array
*/
protected $_links = array(
'show' => '%application%/event.php?calendar=|calendar|&eventID=|event|&uid=|uid|'
);
/**
* Returns the share helper prefix
*
* @return string
*/
public function shareHelp()
{
return 'shares';
}
/**
* Returns the last modification timestamp for the given uid.
*
* @param string $uid The uid to look for.
*
* @return integer The timestamp for the last modification of $uid.
*/
public function modified($uid)
{
$modified = $this->getActionTimestamp($uid, 'modify');
if (empty($modified)) {
$modified = $this->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
* @throws Kronolith_Exception
*/
public function browse($path = '', $properties = array())
{
// Default properties.
if (!$properties) {
$properties = array('name', 'icon', 'browseable');
}
if (substr($path, 0, 9) == 'kronolith') {
$path = substr($path, 9);
}
$path = trim($path, '/');
$parts = explode('/', $path);
if (empty($path)) {
// This request is for a list of all users who have calendars
// visible to the requesting user.
$calendars = Kronolith::listInternalCalendars(false, Horde_Perms::READ);
$owners = array();
foreach ($calendars as $calendar) {
$owners[$calendar->get('owner') ? $calendar->get('owner') : '-system-'] = true;
}
$results = array();
foreach (array_keys($owners) as $owner) {
$path = 'kronolith/' . $owner;
if (in_array('name', $properties)) {
$results[$path]['name'] = $owner;
}
if (in_array('icon', $properties)) {
$results[$path]['icon'] = Horde_Themes::img('user.png');
}
if (in_array('browseable', $properties)) {
$results[$path]['browseable'] = true;
}
if (in_array('contenttype', $properties)) {
$results[$path]['contenttype'] =
'httpd/unix-directory';
}
if (in_array('contentlength', $properties)) {
$results[$path]['contentlength'] = 0;
}
if (in_array('modified', $properties)) {
$results[$path]['modified'] =
$_SERVER['REQUEST_TIME'];
}
if (in_array('created', $properties)) {
$results[$path]['created'] = 0;
}
// CalDAV Properties from RFC 4791 and
// draft-desruisseaux-caldav-sched-03
$caldavns = 'urn:ietf:params:xml:ns:caldav';
$kronolith_rpc_base = $GLOBALS['registry']->get('webroot', 'horde') . '/rpc/kronolith/';
if (in_array($caldavns . ':calendar-home-set', $properties)) {
$results[$path][$caldavns . ':calendar-home-set'] = Horde::url($kronolith_rpc_base . urlencode($owner), true);
}
if (in_array($caldavns . ':calendar-user-address-set', $properties)) {
// FIXME: Add the calendar owner's email address from
// their Horde Identity
}
}
return $results;
} elseif (count($parts) == 1) {
// This request is for all calendars owned by the requested user
$owner = $parts[0] == '-system-' ? '' : $parts[0];
$calendars = $GLOBALS['injector']->getInstance('Kronolith_Shares')->listShares(
$GLOBALS['registry']->getAuth(),
array('perm' => Horde_Perms::SHOW,
'attributes' => $owner));
$results = array();
foreach ($calendars as $calendarId => $calendar) {
if ($parts[0] == '-system-' && $calendar->get('owner')) {
continue;
}
$retpath = 'kronolith/' . $parts[0] . '/' . $calendarId;
if (in_array('name', $properties)) {
$results[$retpath]['name'] = sprintf(_("Events from %s"), Kronolith::getLabel($calendar));
$results[$retpath . '.ics']['name'] = Kronolith::getLabel($calendar);
}
if (in_array('displayname', $properties)) {
$results[$retpath]['displayname'] = rawurlencode(Kronolith::getLabel($calendar));
$results[$retpath . '.ics']['displayname'] = rawurlencode(Kronolith::getLabel($calendar)) . '.ics';
}
if (in_array('icon', $properties)) {
$results[$retpath]['icon'] = Horde_Themes::img('kronolith.png');
$results[$retpath . '.ics']['icon'] = Horde_Themes::img('mime/icalendar.png');
}
if (in_array('browseable', $properties)) {
$results[$retpath]['browseable'] = $calendar->hasPermission($GLOBALS['registry']->getAuth(), Horde_Perms::READ);
$results[$retpath . '.ics']['browseable'] = false;
}
if (in_array('contenttype', $properties)) {
$results[$retpath]['contenttype'] = 'httpd/unix-directory';
$results[$retpath . '.ics']['contenttype'] = 'text/calendar';
}
if (in_array('contentlength', $properties)) {
$results[$retpath]['contentlength'] = 0;
// FIXME: This is a hack. If the content length is longer
// than the actual data then some WebDAV clients will
// report an error when the file EOF is received. Ideally
// we should determine the actual size of the .ics and
// report it here, but the performance hit may be
// prohibitive. This requires further investigation.
$results[$retpath . '.ics']['contentlength'] = 1;
}
if (in_array('modified', $properties)) {
$results[$retpath]['modified'] = $_SERVER['REQUEST_TIME'];
$results[$retpath . '.ics']['modified'] = $_SERVER['REQUEST_TIME'];
}
if (in_array('created', $properties)) {
$results[$retpath]['created'] = 0;
$results[$retpath . '.ics']['created'] = 0;
}
}
return $results;
} elseif (count($parts) == 2 &&
array_key_exists($parts[1], Kronolith::listInternalCalendars(false, Horde_Perms::READ))) {
// This request is browsing into a specific calendar. Generate
// the list of items and represent them as files within the
// directory.
$kronolith_driver = Kronolith::getDriver(null, $parts[1]);
$events = $kronolith_driver->listEvents();
$icon = Horde_Themes::img('mime/icalendar.png');
$results = array();
foreach ($events as $dayevents) {
foreach ($dayevents as $event) {
$key = 'kronolith/' . $path . '/' . $event->id;
if (in_array('name', $properties)) {
$results[$key]['name'] = $event->getTitle();
}
if (in_array('icon', $properties)) {
$results[$key]['icon'] = $icon;
}
if (in_array('browseable', $properties)) {
$results[$key]['browseable'] = false;
}
if (in_array('contenttype', $properties)) {
$results[$key]['contenttype'] = 'text/calendar';
}
if (in_array('contentlength', $properties)) {
// FIXME: This is a hack. If the content length is
// longer than the actual data then some WebDAV
// clients will report an error when the file EOF is
// received. Ideally we should determine the actual
// size of the data and report it here, but the
// performance hit may be prohibitive. This requires
// further investigation.
$results[$key]['contentlength'] = 1;
}
if (in_array('modified', $properties)) {
$results[$key]['modified'] = $this->modified($event->uid);
}
if (in_array('created', $properties)) {
$results[$key]['created'] = $this->getActionTimestamp($event->uid, 'add');
}
}
}
return $results;
} else {
// The only valid request left is for either a specific event or
// for the entire calendar.
if (count($parts) == 3 &&
array_key_exists($parts[1], Kronolith::listInternalCalendars(false, Horde_Perms::READ))) {
// This request is for a specific item within a given calendar.
$event = Kronolith::getDriver(null, $parts[1])->getEvent($parts[2]);
$result = array(
'data' => $this->export($event->uid, 'text/calendar'),
'mimetype' => 'text/calendar');
$modified = $this->modified($event->uid);
if (!empty($modified)) {
$result['mtime'] = $modified;
}
return $result;
} elseif (count($parts) == 2 &&
substr($parts[1], -4, 4) == '.ics' &&
array_key_exists(substr($parts[1], 0, -4), Kronolith::listInternalCalendars(false, Horde_Perms::READ))) {
// This request is for an entire calendar (calendar.ics).
$ical_data = $this->exportCalendar(substr($parts[1], 0, -4), 'text/calendar');
$result = array('data' => $ical_data,
'mimetype' => 'text/calendar',
'contentlength' => strlen($ical_data),
'mtime' => $_SERVER['REQUEST_TIME']);
return $result;
} else {
// All other requests are a 404: Not Found
return false;
}
}
}
/**
* Saves a file into the Kronolith tree.
*
* @param string $path The path where to PUT the file.
* @param string $content The file content.
* @param string $content_type The file's content type.
*
* @return array The event UIDs.
* @throws Kronolith_Exception
*/
public function put($path, $content, $content_type)
{
if (substr($path, 0, 9) == 'kronolith') {
$path = substr($path, 9);
}
$path = trim($path, '/');
$parts = explode('/', $path);
if (count($parts) == 2 && substr($parts[1], -4) == '.ics') {
// Workaround for WebDAV clients that are not smart enough to send
// the right content type. Assume text/calendar.
if ($content_type == 'application/octet-stream') {
$content_type = 'text/calendar';
}
$calendar = substr($parts[1], 0, -4);
} elseif (count($parts) == 3) {
$calendar = $parts[1];
// Workaround for WebDAV clients that are not smart enough to send
// the right content type. Assume text/calendar.
if ($content_type == 'application/octet-stream') {
$content_type = 'text/calendar';
}
} else {
throw new Kronolith_Exception("Invalid calendar data supplied.");
}
if (!Kronolith::hasPermission($calendar, Horde_Perms::EDIT)) {
// FIXME: Should we attempt to create a calendar based on the
// filename in the case that the requested calendar does not
// exist?
throw new Kronolith_Exception("Calendar does not exist or no permission to edit");
}
// Store all currently existings UIDs. Use this info to delete UIDs not
// present in $content after processing.
$ids = array();
$uids_remove = array_flip($this->listUids($calendar));
switch ($content_type) {
case 'text/calendar':
case 'text/x-vcalendar':
$iCal = new Horde_Icalendar();
if (!($content instanceof Horde_Icalendar_Vevent)) {
if (!$iCal->parsevCalendar($content)) {
throw new Kronolith_Exception(_("There was an error importing the iCalendar data."));
}
} else {
$iCal->addComponent($content);
}
$kronolith_driver = Kronolith::getDriver();
foreach ($iCal->getComponents() as $content) {
if ($content instanceof Horde_Icalendar_Vevent) {
$event = $kronolith_driver->getEvent();
$event->fromiCalendar($content);
$uid = $event->uid;
// Remove from uids_remove list so we won't delete in the
// end.
if (isset($uids_remove[$uid])) {
unset($uids_remove[$uid]);
}
try {
$existing_event = $kronolith_driver->getByUID($uid, array($calendar));
// Check if our event is newer then the existing - get
// the event's history.
$created = $modified = null;
try {
$history = $GLOBALS['injector']->getInstance('Horde_History');
$created = $history->getActionTimestamp(
'kronolith:' . $calendar . ':' . $uid, 'add');
$modified = $history->getActionTimestamp(
'kronolith:' . $calendar . ':' . $uid, 'modify');
/* The history driver returns 0 for not found. If 0
* or null does not matter, strip this */
if ($created == 0) {
$created = null;
}
if ($modified == 0) {
$modified = null;
}
} catch (Horde_Exception $e) {
}
if (empty($modified) && !empty($created)) {
$modified = $created;
}
try {
if (!empty($modified) &&
$modified >= $content->getAttribute('LAST-MODIFIED')) {
// LAST-MODIFIED timestamp of existing entry
// is newer: don't replace it.
continue;
}
} catch (Horde_Icalendar_Exception $e) {
}
// Don't change creator/owner.
$event->creator = $existing_event->creator;
} catch (Horde_Exception_NotFound $e) {
}
// Save entry.
$event->save();
$ids[] = $event->uid;
}
}
break;
default:
throw new Kronolith_Exception(sprintf(_("Unsupported Content-Type: %s"), $content_type));
}
if (Kronolith::hasPermission($calendar, Horde_Perms::DELETE)) {
foreach (array_keys($uids_remove) as $uid) {
$this->delete($uid);
}
}
return $ids;
}
/**
* Deletes a file from the Kronolith tree.
*
* @param string $path The path to the file.
*
* @throws Kronolith_Exception
*/
public function path_delete($path)
{
if (substr($path, 0, 9) == 'kronolith') {
$path = substr($path, 9);
}
$path = trim($path, '/');
$parts = explode('/', $path);
if (substr($parts[1], -4) == '.ics') {
$calendarId = substr($parts[1], 0, -4);
} else {
$calendarId = $parts[1];
}
if (!(count($parts) == 2 || count($parts) == 3) ||
!Kronolith::hasPermission($calendarId, Horde_Perms::DELETE)) {
throw new Kronolith_Exception("Calendar does not exist or no permission to delete");
}
if (count($parts) == 3) {
// Delete just a single entry
Kronolith::getDriver(null, $calendarId)->deleteEvent($parts[2]);
} else {
// Delete the entire calendar
try {
Kronolith::getDriver()->delete($calendarId);
// Remove share and all groups/permissions.
$kronolith_shares = $GLOBALS['injector']->getInstance('Kronolith_Shares');
$share = $kronolith_shares->getShare($calendarId);
$kronolith_shares->removeShare($share);
} catch (Exception $e) {
throw new Kronolith_Exception(sprintf(_("Unable to delete calendar \"%s\": %s"), $calendarId, $e->getMessage()));
}
}
}
/**
* Returns all calendars a user has access to, according to several
* parameters/permission levels.
*
* @param boolean $owneronly Only return calendars that this user owns?
* Defaults to false.
* @param integer $permission The permission to filter calendars by.
*
* @return array The calendar list.
*/
public function listCalendars($owneronly = false, $permission = null)
{
if (is_null($permission)) {
$permission = Horde_Perms::SHOW;
}
return array_keys(Kronolith::listInternalCalendars($owneronly, $permission));
}
/**
* Returns the ids of all the events that happen within a time period.
* Only includes recurring events once per time period, and does not include
* events that represent exceptions, making this method useful for syncing
* purposes. For more control, use the listEvents method.
*
* @param string $calendars The calendar to check for events.
* @param object $startstamp The start of the time range.
* @param object $endstamp The end of the time range.
*
* @return array The event ids happening in this time period.
* @throws Kronolith_Exception
*/
public function listUids($calendars = null, $startstamp = 0, $endstamp = 0)
{
if (empty($calendars)) {
$calendars = Kronolith::getSyncCalendars();
} elseif (!is_array($calendars)) {
$calendars = array($calendars);
}
$driver = Kronolith::getDriver();
$results = array();
foreach ($calendars as $calendar) {
if (!Kronolith::hasPermission($calendar, Horde_Perms::READ)) {
Horde::logMessage(sprintf(
_("Permission Denied or Calendar Not Found: %s - skipping."),
$calendar));
continue;
}
try {
$driver->open($calendar);
$events = $driver->listEvents(
$startstamp ? new Horde_Date($startstamp) : null,
$endstamp ? new Horde_Date($endstamp) : null,
array('cover_dates' => false,
'hide_exceptions' => true)
);
Kronolith::mergeEvents($results, $events);
} catch (Kronolith_Exception $e) {
Horde::logMessage($e);
}
}
$uids = array();
foreach ($results as $dayevents) {
foreach ($dayevents as $event) {
$uids[] = $event->uid;
}
}
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.
* @param integer $end The optional ending timestamp
* @param boolean $isModSeq If true, $timestamp and $end are modification
* sequences and not timestamps. @since 4.1.1
*
* @return array An array of UIDs matching the action and time criteria.
*
* @throws Kronolith_Exception
* @throws Horde_History_Exception
* @throws InvalidArgumentException
*/
public function listBy($action, $timestamp, $calendar = null, $end = null, $isModSeq = false)
{
if (empty($calendar)) {
$cs = Kronolith::getSyncCalendars($action == 'delete');
$results = array();
foreach ($cs as $c) {
$results = array_merge(
$results, $this->listBy($action, $timestamp, $c, $end, $isModSeq));
}
return $results;
}
$filter = array(array('op' => '=', 'field' => 'action', 'value' => $action));
if (!empty($end) && !$isModSeq) {
$filter[] = array('op' => '<', 'field' => 'ts', 'value' => $end);
}
if (!$isModSeq) {
$histories = $GLOBALS['injector']
->getInstance('Horde_History')
->getByTimestamp('>', $timestamp, $filter, 'kronolith:' . $calendar);
} else {
$histories = $GLOBALS['injector']
->getInstance('Horde_History')
->getByModSeq($timestamp, $end, $filter, 'kronolith:' . $calendar);
}
// Strip leading kronolith:username:.
return preg_replace('/^([^:]*:){2}/', '', array_keys($histories));
}
/**
* Method for obtaining all server changes between two timestamps. Basically
* a wrapper around listBy(), but returns an array containing all adds,
* edits and deletions. If $ignoreExceptions is true, events representing
* recurring event exceptions will not be included in the results.
*
* @param integer $start The starting timestamp
* @param integer $end The ending timestamp.
* @param boolean $ignoreExceptions Do not include exceptions in results.
* @param boolean $isModSeq If true, $timestamp and $end are
* modification sequences and not
* timestamps. @since 4.1.1
*
* @return array An hash with 'add', 'modify' and 'delete' arrays.
* @throws Horde_Exception_PermissionDenied
* @throws Kronolith_Exception
*/
public function getChanges($start, $end, $ignoreExceptions = true, $isModSeq = false)
{
// Only get the calendar once
$cs = Kronolith::getSyncCalendars();
$changes = array(
'add' => array(),
'modify' => array(),
'delete' => array());
foreach ($cs as $c) {
// New events
$uids = $this->listBy('add', $start, $c, $end, $isModSeq);
if ($ignoreExceptions) {
foreach ($uids as $uid) {
try {
$event = Kronolith::getDriver()->getByUID($uid, array($c));
} catch (Exception $e) {
continue;
}
if (empty($event->baseid)) {
$changes['add'][] = $uid;
}
}
} else {
$changes['add'] = array_keys(array_flip(array_merge($changes['add'], $uids)));
}
// Edits
$uids = $this->listBy('modify', $start, $c, $end, $isModSeq);
if ($ignoreExceptions) {
foreach ($uids as $uid) {
try {
$event = Kronolith::getDriver()->getByUID($uid, array($c));
} catch (Exception $e) {
continue;
}
if (empty($event->baseid)) {
$changes['modify'][] = $uid;
}
}
} else {
$changes['modify'] = array_keys(array_flip(array_merge($changes['modify'], $uids)));
}
// No way to figure out if this was an exception, so we must include all
$changes['delete'] = array_keys(
array_flip(array_merge($changes['delete'], $this->listBy('delete', $start, $c, $end, $isModSeq))));
}
return $changes;
}
/**
* Return all changes occuring between the specified modification
* sequences.
*
* @param integer $start The starting modseq.
* @param integer $end The ending modseq.
*
* @return array The changes @see getChanges()
* @since 4.1.1
*/
public function getChangesByModSeq($start, $end)
{
return $this->getChanges($start, $end, true, true);
}
/**
* 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.
* @param boolean $modSeq Request a modification sequence instead of a
* timestamp. @since 4.1.1
*
* @return integer The timestamp or modseq for this action.
*
* @throws Kronolith_Exception
* @throws InvalidArgumentException
*/
public function getActionTimestamp($uid, $action, $calendar = null, $modSeq = false)
{
if (empty($calendar)) {
$calendar = Kronolith::getDefaultCalendar();
} elseif (!Kronolith::hasPermission($calendar, Horde_Perms::READ)) {
throw new Horde_Exception_PermissionDenied();
}
if (!$modSeq) {
return $GLOBALS['injector']->getInstance('Horde_History')->getActionTimestamp('kronolith:' . $calendar . ':' . $uid, $action);
}
return $GLOBALS['injector']->getInstance('Horde_History')->getActionModSeq('kronolith:' . $calendar . ':' . $uid, $action);
}
/**
* Return the largest modification sequence from the history backend.
*
* @return integer The modseq.
* @since 4.1.1
*/
public function getHighestModSeq()
{
return $GLOBALS['injector']->getInstance('Horde_History')->getHighestModSeq('kronolith');
}
/**
* 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:
* <pre>
* text/calendar
* text/x-vcalendar
* activesync
* </pre>
* @param string $calendar What calendar should the event be added to?
*
* @return array The event's UID.
* @throws Kronolith_Exception
*/
public function import($content, $contentType, $calendar = null)
{
if (!isset($calendar)) {
$calendar = Kronolith::getDefaultCalendar(Horde_Perms::EDIT);
} elseif (!Kronolith::hasPermission($calendar, Horde_Perms::EDIT)) {
throw new Horde_Exception_PermissionDenied();
}
$kronolith_driver = Kronolith::getDriver(null, $calendar);
switch ($contentType) {
case 'text/calendar':
case 'text/x-vcalendar':
$iCal = new Horde_Icalendar();
if (!($content instanceof Horde_Icalendar_Vevent)) {
if (!$iCal->parsevCalendar($content)) {
throw new Kronolith_Exception(_("There was an error importing the iCalendar data."));
}
} else {
$iCal->addComponent($content);
}
$components = $iCal->getComponents();
if (count($components) == 0) {
throw new Kronolith_Exception(_("No iCalendar data was found."));
}
$ids = array();
$recurrences = array();
foreach ($components as $content) {
if ($content instanceof Horde_Icalendar_Vevent) {
// Need to ensure that the original recurring event is
// added before any of the instance exceptions. Easiest way
// to do that is just add all the recurrence-id entries last
try {
$content->getAttribute('RECURRENCE-ID');
$recurrences[] = $content;
} catch (Horde_Icalendar_Exception $e) {
$ids[] = $this->_addiCalEvent($content, $kronolith_driver);
}
}
}
if (count($ids) == 0) {
throw new Kronolith_Exception(_("No iCalendar data was found."));
} else if (count($ids) == 1) {
return $ids[0];
}
// Now add all the exception instances
foreach ($recurrences as $recurrence) {
$ids[] = $this->_addiCalEvent($recurrence, $kronolith_driver);
}
return $ids;
case 'activesync':
$event = $kronolith_driver->getEvent();
$event->fromASAppointment($content);
$event->save();
return $event->uid;
}
throw new Kronolith_Exception(sprintf(_("Unsupported Content-Type: %s"), $contentType));
}
/**
* Imports a single vEvent part to storage.
*
* @param Horde_Icalendar_Vevent $content The vEvent part
* @param Kronolith_Driver $driver The kronolith driver
*
* @return string The new event's uid
*/
protected function _addiCalEvent($content, $driver)
{
$event = $driver->getEvent();
$event->fromiCalendar($content);
// Check if the entry already exists in the data source,
// first by UID.
$uid = $event->uid;
try {
$driver->getByUID($uid, array($driver->calendar));
throw new Kronolith_Exception(sprintf(_("%s Already Exists"), $uid));
} catch (Horde_Exception $e) {}
$result = $driver->search($event);
// Check if the match really is an exact match:
if (is_array($result) && count($result) > 0) {
foreach($result as $match) {
if ($match->start == $event->start &&
$match->end == $event->end &&
$match->title == $event->title &&
$match->location == $event->location &&
$match->hasPermission(Horde_Perms::EDIT)) {
throw new Kronolith_Exception(sprintf(_("%s Already Exists"), $match->uid));
}
}
}
$event->save();
return $event->uid;
}
/**
* Imports an event parsed from a string.
*
* @param string $text The text to parse into an event
* @param string $calendar The calendar into which the event will be
* imported. If 'null', the user's default
* calendar will be used.
*
* @return array The UID of all events that were added.
* @throws Kronolith_Exception
*/
public function quickAdd($text, $calendar = null)
{
if (!isset($calendar)) {
$calendar = Kronolith::getDefaultCalendar(Horde_Perms::EDIT);
} elseif (!Kronolith::hasPermission($calendar, Horde_Perms::EDIT)) {
throw new Horde_Exception_PermissionDenied();
}
$event = Kronolith::quickAdd($text, $calendar);
return $event->uid;
}
/**
* 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:
* <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)
* activesync (Horde_ActiveSync_Message_Appointment)
* </pre>
* @param array $optinos Any additional options to be passed to the
* exporter.
*
* @return string The requested data.
* @throws Kronolith_Exception
* @throws Horde_Exception_NotFound
*/
public function export($uid, $contentType, array $options = array())
{
$event = Kronolith::getDriver()->getByUID($uid);
if (!$event->hasPermission(Horde_Perms::READ)) {
throw new Horde_Exception_PermissionDenied();
}
$version = '2.0';
switch ($contentType) {
case 'text/x-vcalendar':
$version = '1.0';
case 'text/calendar':
$share = $GLOBALS['injector']->getInstance('Kronolith_Shares')->getShare($event->calendar);
$iCal = new Horde_Icalendar($version);
$iCal->setAttribute('X-WR-CALNAME', $share->get('name'));
// Create a new vEvent.
$iCal->addComponent($event->toiCalendar($iCal));
return $iCal->exportvCalendar();
case 'activesync':
return $event->toASAppointment($options);
}
throw new Kronolith_Exception(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:
* <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)
);
}
/**
* Deletes an event identified by UID.
*
* @param string|array $uid A single UID or an array identifying the
* event(s) to delete.
*
* @param string $recurrenceId The reccurenceId for the event instance, if
* this is a deletion of a recurring event
* instance ($uid must not be an array).
*
* @throws Kronolith_Exception
*/
public function delete($uid, $recurrenceId = null)
{
// Handle an array of UIDs for convenience of deleting multiple events
// at once.
if (is_array($uid)) {
foreach ($uid as $g) {
$this->delete($g);
}
return;
}
$kronolith_driver = Kronolith::getDriver();
$events = $kronolith_driver->getByUID($uid, null, true);
$event = null;
if ($GLOBALS['registry']->isAdmin()) {
$event = $events[0];
}
// First try the user's own calendars.
if (empty($event)) {
$ownerCalendars = Kronolith::listInternalCalendars(true, Horde_Perms::DELETE);
foreach ($events as $ev) {
if ($GLOBALS['registry']->isAdmin() || isset($ownerCalendars[$ev->calendar])) {
$kronolith_driver->open($ev->calendar);
$event = $ev;
break;
}
}
}
// If not successful, try all calendars the user has access to.
if (empty($event)) {
$deletableCalendars = Kronolith::listInternalCalendars(false, Horde_Perms::DELETE);
foreach ($events as $ev) {
if (isset($deletableCalendars[$ev->calendar])) {
$kronolith_driver->open($ev->calendar);
$event = $ev;
break;
}
}
}
if (empty($event)) {
throw new Horde_Exception_PermissionDenied();
}
if ($recurrenceId && $event->recurs()) {
$deleteDate = new Horde_Date($recurrenceId);
$event->recurrence->addException($deleteDate->format('Y'), $deleteDate->format('m'), $deleteDate->format('d'));
$event->save();
} elseif ($recurrenceId) {
throw new Kronolith_Exception(_("Unable to delete event. An exception date was provided but the event does not seem to be recurring."));
} else {
$kronolith_driver->deleteEvent($event->id);
}
}
/**
* Replaces the event identified by UID with the content represented in the
* specified contentType.
*
* @param string $uid Idenfity the event to replace.
* @param mixed $content The content of the event. String or
* Horde_Icalendar_Vevent
* @param string $contentType What format is the data in? Currently supports:
* text/calendar
* text/x-vcalendar
* (Ignored if content is Horde_Icalendar_Vevent)
* activesync (Horde_ActiveSync_Message_Appointment)
*
* @throws Kronolith_Exception
*/
public function replace($uid, $content, $contentType)
{
$event = Kronolith::getDriver()->getByUID($uid);
if (!$event->hasPermission(Horde_Perms::EDIT) ||
($event->private && $event->creator != $GLOBALS['registry']->getAuth())) {
throw new Horde_Exception_PermissionDenied();
}
if ($content instanceof Horde_Icalendar_Vevent) {
$component = $content;
} elseif ($content instanceof Horde_ActiveSync_Message_Appointment) {
$event->fromASAppointment($content);
$event->save();
$event->uid = $uid;
return;
} else {
switch ($contentType) {
case 'text/calendar':
case 'text/x-vcalendar':
if (!($content instanceof Horde_Icalendar_Vevent)) {
$iCal = new Horde_Icalendar();
if (!$iCal->parsevCalendar($content)) {
throw new Kronolith_Exception(_("There was an error importing the iCalendar data."));
}
$components = $iCal->getComponents();
$component = null;
foreach ($components as $content) {
if ($content instanceof Horde_Icalendar_Vevent) {
if ($component !== null) {
throw new Kronolith_Exception(_("Multiple iCalendar components found; only one vEvent is supported."));
}
$component = $content;
}
}
if ($component === null) {
throw new Kronolith_Exception(_("No iCalendar data was found."));
}
}
break;
default:
throw new Kronolith_Exception(sprintf(_("Unsupported Content-Type: %s"), $contentType));
}
}
$event->fromiCalendar($component);
// Ensure we keep the original UID, even when content does not
// contain one and fromiCalendar creates a new one.
$event->uid = $uid;
$event->save();
}
/**
* 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.
* @throws Kronolith_Exception
*/
public function getFreeBusy($startstamp = null, $endstamp = null,
$calendar = null)
{
if (is_null($calendar)) {
$calendar = Kronolith::getDefaultCalendar();
}
// Free/Busy information is globally available; no permission
// check is needed.
return Kronolith_FreeBusy::generate($calendar, $startstamp, $endstamp, true);
}
/**
* Attempt to lookup the free/busy information for the given email address.
*
* @param string $email The email to lookup free/busy information for.
* @param boolean $json Return the data in a simple json format. If false,
* returns the vCalander object.
* @since 4.1.0
*/
public function lookupFreeBusy($email, $json = false)
{
return Kronolith_FreeBusy::get($email, $json);
}
/**
* Retrieves a Kronolith_Event object, given an event UID.
*
* @param string $uid The event's UID.
*
* @return Kronolith_Event A valid Kronolith_Event.
* @throws Kronolith_Exception
*/
public function eventFromUID($uid)
{
$event = Kronolith::getDriver()->getByUID($uid);
if (!$event->hasPermission(Horde_Perms::SHOW)) {
throw new Horde_Exception_PermissionDenied();
}
return $event;
}
/**
* Updates an attendee's response status for a specified event.
*
* @param Horde_Icalendar_Vevent $response A Horde_Icalendar_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.
* @param string $sender The email address of the
* person initiating the
* update. Attendees are only
* updated if this address
* matches.
*
* @throws Kronolith_Exception
*/
public function updateAttendee($response, $sender = null)
{
try {
$uid = $response->getAttribute('UID');
} catch (Horde_Icalendar_Exception $e) {
throw new Kronolith_Exception($e);
}
$events = Kronolith::getDriver()->getByUID($uid, null, true);
/* First try the user's own calendars. */
$ownerCalendars = Kronolith::listInternalCalendars(true, Horde_Perms::EDIT);
$event = null;
foreach ($events as $ev) {
if (isset($ownerCalendars[$ev->calendar])) {
$event = $ev;
break;
}
}
/* If not successful, try all calendars the user has access to. */
if (empty($event)) {
$editableCalendars = Kronolith::listInternalCalendars(false, Horde_Perms::EDIT);
foreach ($events as $ev) {
if (isset($editableCalendars[$ev->calendar])) {
$event = $ev;
break;
}
}
}
if (empty($event) ||
($event->private && $event->creator != $GLOBALS['registry']->getAuth())) {
throw new Horde_Exception_PermissionDenied();
}
$atnames = $response->getAttribute('ATTENDEE');
if (!is_array($atnames)) {
$atnames = array($atnames);
}
$atparms = $response->getAttribute('ATTENDEE', true);
$found = false;
$error = _("No attendees have been updated because none of the provided email addresses have been found in the event's attendees list.");
$sender_lcase = Horde_String::lower($sender);
foreach ($atnames as $index => $attendee) {
if ($response->getAttribute('VERSION') < 2) {
$addr_ob = new Horde_Mail_Rfc822_Address($attendee);
if (!$addr_ob->valid) {
continue;
}
$attendee = Horde_String::lower($addr_ob->bare_address);
$name = $addr_ob->personal;
} else {
$attendee = str_replace('mailto:', '', Horde_String::lower($attendee));
$name = isset($atparms[$index]['CN']) ? $atparms[$index]['CN'] : null;
}
if ($event->hasAttendee($attendee)) {
if (is_null($sender) || $sender_lcase == $attendee) {
$event->addAttendee($attendee, Kronolith::PART_IGNORE, Kronolith::responseFromICal($atparms[$index]['PARTSTAT']), $name);
$found = true;
} else {
$error = _("The attendee hasn't been updated because the update was not sent from the attendee.");
}
}
}
$event->save();
if (!$found) {
throw new Kronolith_Exception($error);
}
}
/**
* 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 array $calendars The calendars to view events from.
* 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.
* @param boolean $alarmsOnly Filter results for events with alarms.
* Defaults to false.
* @param boolean $showRemote Return events from remote calendars and
* listTimeObject API as well?
*
* @param boolean $hideExceptions Hide events that represent exceptions to
* a recurring event (events with baseid
* set)?
* @param boolean $coverDates Add multi-day events to all dates?
*
* @return array A list of event hashes.
* @throws Kronolith_Exception
*/
public function listEvents($startstamp = null, $endstamp = null,
$calendars = null, $showRecurrence = true,
$alarmsOnly = false, $showRemote = true,
$hideExceptions = false, $coverDates = true,
$fetchTags = false)
{
if (!isset($calendars)) {
$calendars = array($GLOBALS['prefs']->getValue('default_share'));
} elseif (!is_array($calendars)) {
$calendars = array($calendars);
}
foreach ($calendars as &$calendar) {
$calendar = str_replace('internal_', '', $calendar);
if (!Kronolith::hasPermission($calendar, Horde_Perms::READ)) {
throw new Horde_Exception_PermissionDenied();
}
}
return Kronolith::listEvents(
new Horde_Date($startstamp),
new Horde_Date($endstamp),
$calendars, array(
'show_recurrence' => $showRecurrence,
'has_alarm' => $alarmsOnly,
'show_remote' => $showRemote,
'hide_exceptions' => $hideExceptions,
'cover_dates' => $coverDates,
'fetch_tags' => $fetchTags)
);
}
/**
* Subscribe to a calendar.
*
* @param array $calendar Calendar description hash, with required 'type'
* parameter. Currently supports 'http' and
* 'webcal' for remote calendars.
*
* @throws Kronolith_Exception
*/
public function subscribe($calendar)
{
if (!isset($calendar['type'])) {
throw new Kronolith_Exception(_("Unknown calendar protocol"));
}
switch ($calendar['type']) {
case 'http':
case 'webcal':
Kronolith::subscribeRemoteCalendar($calendar);
break;
case 'external':
$cals = unserialize($GLOBALS['prefs']->getValue('display_external_cals'));
if (array_search($calendar['name'], $cals) === false) {
$cals[] = $calendar['name'];
$GLOBALS['prefs']->setValue('display_external_cals', serialize($cals));
}
default:
throw new Kronolith_Exception(_("Unknown calendar protocol"));
}
}
/**
* Unsubscribe from a calendar.
*
* @param array $calendar Calendar description array, with required 'type'
* parameter. Currently supports 'http' and
* 'webcal' for remote calendars.
*
* @throws Kronolith_Exception
*/
public function unsubscribe($calendar)
{
if (!isset($calendar['type'])) {
throw new Kronolith_Exception('Unknown calendar specification');
}
switch ($calendar['type']) {
case 'http':
case 'webcal':
Kronolith::subscribeRemoteCalendar($calendar['url']);
break;
case 'external':
$cals = unserialize($GLOBALS['prefs']->getValue('display_external_cals'));
if (($key = array_search($calendar['name'], $cals)) !== false) {
unset($cals[$key]);
$GLOBALS['prefs']->setValue('display_external_cals', serialize($cals));
}
default:
throw new Kronolith_Exception('Unknown calendar specification');
}
}
/**
* Places an exclusive lock for a calendar or an event.
*
* @param string $calendar The id of the calendar to lock
* @param string $event The uid for the event to lock
*
* @return mixed A lock ID on success, false if:
* - The calendar is already locked
* - The event is already locked
* - A calendar lock was requested and an event is
* already locked in the calendar
* @throws Kronolith_Exception
*/
public function lock($calendar, $event = null)
{
if (!Kronolith::hasPermission($calendar, Horde_Perms::EDIT)) {
throw new Horde_Exception_PermissionDenied();
}
if (!empty($event)) {
$uid = $calendar . ':' . $event;
}
return $GLOBALS['injector']->getInstance('Kronolith_Shares')->getShare($calendar)->lock($GLOBALS['injector']->getInstance('Horde_Lock'), $uid);
}
/**
* Releases a lock.
*
* @param array $calendar The event to lock.
* @param array $lockid The lock id to unlock.
*
* @throws Kronolith_Exception
*/
public function unlock($calendar, $lockid)
{
if (!Kronolith::hasPermission($calendar, Horde_Perms::EDIT)) {
throw new Horde_Exception_PermissionDenied();
}
return $GLOBALS['injector']->getInstance('Kronolith_Shares')->getShare($calendar)->unlock($GLOBALS['injector']->getInstance('Horde_Lock'), $lockid);
}
/**
* Check for existing calendar or event locks.
*
* @param array $calendar The calendar to check locks for.
* @param array $event The event to check locks for.
*
* @throws Kronolith_Exception
*/
public function checkLocks($calendar, $event = null)
{
if (!Kronolith::hasPermission($calendar, Horde_Perms::READ)) {
throw new Horde_Exception_PermissionDenied();
}
if (!empty($event)) {
$uid = $calendar . ':' . $event;
}
return $GLOBALS['injector']->getInstance('Kronolith_Shares')->getShare($calendar)->checkLocks($GLOBALS['injector']->getInstance('Horde_Lock'), $uid);
}
/**
*
* @return array A list of calendars used to display free/busy information
*/
public function getFbCalendars()
{
return (unserialize($GLOBALS['prefs']->getValue('fb_cals')));
}
/**
* Retrieve the list of used tag_names, tag_ids and the total number
* of resources that are linked to that tag.
*
* @param array $tags An optional array of tag_ids. If omitted, all tags
* will be included.
* @param string $user Restrict result to those tagged by $user.
*
* @return array An array containing tag_name, and total
*/
public function listTagInfo($tags = null, $user = null)
{
return $GLOBALS['injector']
->getInstance('Kronolith_Tagger')->getTagInfo($tags, 500, null, $user);
}
/**
* SearchTags API:
* Returns an application-agnostic array (useful for when doing a tag search
* across multiple applications)
*
* The 'raw' results array can be returned instead by setting $raw = true.
*
* @param array $names An array of tag_names to search for.
* @param integer $max The maximum number of resources to return.
* @param integer $from The number of the resource to start with.
* @param string $resource_type The resource type [event, calendar, '']
* @param string $user Restrict results to resources owned by $user.
* @param boolean $raw Return the raw data?
*
* @return array An array of results:
* <pre>
* 'title' - The title for this resource.
* 'desc' - A terse description of this resource.
* 'view_url' - The URL to view this resource.
* 'app' - The Horde application this resource belongs to.
* </pre>
*/
public function searchTags($names, $max = 10, $from = 0,
$resource_type = '', $user = null, $raw = false)
{
// TODO: $max, $from, $resource_type not honored
$results = $GLOBALS['injector']
->getInstance('Kronolith_Tagger')
->search(
$names,
array('type' => 'event', 'user' => $user));
// Check for error or if we requested the raw data array.
if ($raw) {
return $results;
}
$return = array();
if (!empty($results['events'])) {
foreach ($results['events'] as $event_id) {
$driver = Kronolith::getDriver();
$event = $driver->getByUid($event_id);
$view_url = $event->getViewUrl();
$return[] = array(
'title' => $event->title,
'desc'=> $event->start->strftime($GLOBALS['prefs']->getValue('date_format_mini')) . ' ' . $event->start->strftime($GLOBALS['prefs']->getValue('time_format')),
'view_url' => $view_url,
'app' => 'kronolith'
);
}
}
return $return;
}
/**
* Creates a default calendar/share for the specified user, and adds
* the user to the specified group.
*
* @param string $group Group name for the group to which
* the user should be added.
*
* @param string $user username of the user.
*
* @param createGroupIfNotExists boolean Create group if it doesn't exist
*
* @return Object The requested data.
*/
public function initUserCalendar($group, $user = null, $createGroupIfNotExists = false)
{
$return = new stdClass();
$return->success = false;
if (empty($group)) {
$return->error = 'no group specified';
return $return;
}
if (!$GLOBALS['registry']->isAdmin()) {
$return->error = 'insufficient permissions';
return $return;
}
$gid = 0;
$user = is_null($user) ? $GLOBALS['registry']->getAuth() : $user;
try {
$groups = $GLOBALS['injector']->getInstance('Horde_Group');
$gids = $groups->search($group);
if (reset($gids) == $group) {
$gid = key($gids);
}
if ($gid == 0 && $createGroupIfNotExists) {
$gid = $groups->create($group);
}
} catch (Horde_Group_Exception $e) {
$return->exception = $e;
}
if ($gid == 0 || $return->exception) {
$return->error = 'group error';
return $return;
}
$calendarDriver = self::getCalendarDriver($user);
$return->defaultCalendar = self::getDefaultCalendar($user);
$share = null;
try {
if (is_null($return->defaultCalendar)) {
$share = $calendarDriver->createDefaultShare();
self::getPrefs($user)->setValue('default_share', $share->getName());
$return->defaultCalendar = $share->getName();
} else {
$share = $GLOBALS['injector']
->getInstance('Kronolith_Shares')
->getShare($return->defaultCalendar);
}
} catch (Horde_Exception $e) {
$return->exception = $e;
Horde::logMessage($e);
}
if (is_null($share)) {
$return->error = 'unable to setup calendar';
return $return;
}
/* Calendar auto-sharing with specified group */
$perm_value = Horde_Perms::READ | Horde_Perms::SHOW | Horde_Perms::EDIT | Horde_Perms::DELETE;
if (isset($share->data['perm']) &&
isset($share->data['perm']['groups']) &&
isset($share->data['perm']['groups'][$gid]) &&
$share->data['perm']['groups'][$gid] == $perm_value) {
$return->success = true;
return $return;
}
try {
$perm = $share->getPermission();
$perm->addGroupPermission($gid, $perm_value, false);
$share->setPermission($perm);
} catch (Horde_Group_Exception $e) {
$return->exception = $e;
$return->error = 'group permission error';
return $return;
}
$return->calendar = $share;
$return->success = true;
return $return;
}
/**
* Returns all calendars a user has access to, according to several
* parameters/permission levels.
*
* @param string $user username of the user.
*
* @param integer $permission The permission to filter calendars by.
*
* @return Object The requested data.
*/
public function listUserCalendars($user = null, $permission = null)
{
if (is_null($permission)) {
$permission = Horde_Perms::SHOW;
}
$return = new stdClass();
$return->success = false;
if (!$GLOBALS['registry']->isAdmin()) {
$return->error = 'insufficient permissions';
return $return;
}
$return->user = ($user == null) ? $GLOBALS['registry']->getAuth() : $user;
$return->defaultCalendar = self::getDefaultCalendar($user);
$return->calendars = array();
$calendars = array();
try {
$calendars = self::getCalendars($return->user, $permission);
} catch (Horde_Exception $e) {
$return->exception = $e;
$return->error = 'not found';
return $return;
}
foreach ($calendars as $calendar) {
$cal = new stdClass();
$cal->id = $calendar->data['share_name'];
$cal->name = $calendar->data['attribute_name'];
$cal->owner = $calendar->data['share_owner'];
$cal->embedCode = Kronolith::embedCode($calendar->data['share_name']);
array_push($return->calendars, $cal);
}
$return->success = true;
return $return;
}
/**
* Returns a specific calendar a user has access to, according to several
* parameters/permission levels.
*
* @param string $user username of the user.
*
* @param integer $permission The permission to filter calendars by.
*
* @return Object The requested data.
*/
public function getUserCalendar($calendar, $user = null, $permission = null)
{
if (is_null($permission)) {
$permission = Horde_Perms::SHOW;
}
$return = new stdClass();
$return->success = false;
if (empty($calendar)) {
$return->error = 'no calendar specified';
return $return;
}
if (!$GLOBALS['registry']->isAdmin()) {
$return->error = 'insufficient permissions';
return $return;
}
$return->user = ($user == null) ? $GLOBALS['registry']->getAuth() : $user;
try {
$share = $GLOBALS['injector']->getInstance('Kronolith_Shares')->getShare($calendar);
} catch (Horde_Exception $e) {
$return->exception = $e;
$return->error = 'not found';
return $return;
}
$cal = new stdClass();
$cal->id = $share->data['share_name'];
$cal->name = $share->data['attribute_name'];
$cal->owner = $share->data['share_owner'];
$cal->embedCode = Kronolith::embedCode($share->data['share_name']);
$return->calendar = $cal;
$return->success = true;
return $return;
}
/**
* Returns events for a calendar within specified time interval,
* according to several parameters/permission levels.
*
* @param string $calendar Calendar for listing events.
*
* @param integer $startstamp The start of the time period to
* retrieve.
* @param integer $endstamp The end of the time period to retrieve.
* @param array $calendars The calendars to view events from.
* 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.
* @param boolean $alarmsOnly Filter results for events with alarms.
* Defaults to false.
* @param boolean $showRemote Return events from remote calendars and
* listTimeObject API as well?
*
* @param boolean $hideExceptions Hide events that represent exceptions to
* a recurring event (events with baseid
* set)?
* @param boolean $coverDates Add multi-day events to all dates?
*
*
* @return Object The requested data.
*/
public function listCalendarEvents($calendar,
$startstamp = null, $endstamp = null,
$showRecurrence = true,
$alarmsOnly = false, $showRemote = true,
$hideExceptions = false, $coverDates = true,
$fetchTags = false)
{
$return = new stdClass();
$return->success = false;
if (empty($calendar)) {
$return->error = 'no calendar specified';
return $return;
}
if (!$GLOBALS['registry']->isAdmin()) {
$return->error = 'insufficient permissions';
return $return;
}
$return->success = true;
$return->events = Kronolith::listEvents(
new Horde_Date($startstamp),
new Horde_Date($endstamp),
array($calendars), array(
'show_recurrence' => $showRecurrence,
'has_alarm' => $alarmsOnly,
'show_remote' => $showRemote,
'hide_exceptions' => $hideExceptions,
'cover_dates' => $coverDates,
'fetch_tags' => $fetchTags)
);
return $return;
}
/**
* Returns calendars a user has access to, according to several
* parameters/permission levels.
*
* @param string $user username of the user.
*
* @param integer $permission The permission to filter calendars by.
*
*
* @return array List of shares/calendars for the user.
*/
private static function getCalendars($user = null, $permission = null) {
$user = is_null($user) ? $GLOBALS['registry']->getAuth() : $user;
$permission = is_null($permission) ? Horde_Perms::SHOW : $permission;
$calendars = array();
try {
$calendars = $GLOBALS['injector']
->getInstance('Kronolith_Shares')
->listShares(
$user,
array('perm' => $permission,
'attributes' => $user,
'sort_by' => 'name'));
} catch (Horde_Share_Exception $e) {
Horde::logMessage($e);
throw $e;
}
return $calendars;
}
/**
* Returns the shares/calendar driver for the user
*
* @param string $user username of the user.
*
*
* @return Kronolith_Calendars_* Driver object.
*/
private static function getCalendarDriver($user = null) {
$calDriver = null;
$user = is_null($user) ? $GLOBALS['registry']->getAuth() : $user;
if (!isset($GLOBALS['conf']['calendars']['driver'])) {
$driver = 'Default';
} else {
$driver = Horde_String::ucfirst($GLOBALS['conf']['calendars']['driver']);
}
$class = 'Kronolith_Calendars_' . $driver;
if (class_exists($class)) {
$params = array();
switch ($driver) {
case 'Default':
$params['identity'] = $GLOBALS['injector']->getInstance('Horde_Core_Factory_Identity')->create($user);
break;
}
$calDriver = new $class(
$GLOBALS['injector']->getInstance('Kronolith_Shares'),
$user,
$params
);
} else {
throw new Kronolith_Exception(sprintf('Unable to load the definition of %s.', $class));
}
Horde::logMessage(get_class($calDriver));
return $calDriver;
}
/**
* Returns the default share/calendar for the user.
*
* @param string $user username of the user.
*
* @return string calendar id.
*/
private static function getDefaultCalendar($user = null) {
$user = is_null($user) ? $GLOBALS['registry']->getAuth() : $user;
$calendars = self::getCalendars($user);
$defaultShare = self::getPrefs($user)->getValue('default_share');
if (isset($calendars[$defaultShare])) {
return $defaultShare;
}
return null;
}
/**
* Returns the default share/calendar for the user.
*
* @param string $user username of the user.
*
*
* @return Horde_Prefs Prefs for the specified user.
*/
private static function getPrefs($user = null) {
$user = is_null($user) ? $GLOBALS['registry']->getAuth() : $user;
$pref = $GLOBALS['injector']
->getInstance('Horde_Core_Factory_Prefs')
->create('kronolith', array(
'cache' => false,
'user' => $user
));
return $pref;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment