<?php
/**
 * Kronolith external API interface.
 *
 * $Horde: kronolith/lib/api.php,v 1.126.2.7 2005/10/05 13:59:09 jan Exp $
 *
 * This file defines Kronolith's external API interface. Other
 * applications can interact with Kronolith through this API.
 *
 * @package Kronolith
 */

$_services['show'] = array(
    'link' => '%application%/viewevent.php?calendar=|calendar|' .
              '&eventID=|event|&uid=|uid|'
);

$_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['import'] = array(
    'args' => array('content' => 'string', 'contentType' => 'string', 'calendar' => 'string'),
    'type' => 'int'
);

$_services['export'] = array(
    'args' => array('uid' => '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_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()
{
    require_once dirname(__FILE__) . '/base.php';
    $ids = Kronolith::listEventIds(0, new Horde_Date(array('year' => 9999, 'month' => 12, 'day' => 31)));
    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 integer $timestamp  The time to start the search.
 * @param string $action      The action to check for - add, modify, or delete.
 *
 * @return array  An array of UIDs matching the action and time criteria.
 */
function &_kronolith_listBy($action, $timestamp)
{
    require_once dirname(__FILE__) . '/base.php';
    require_once 'Horde/History.php';

    $history = &Horde_History::singleton();
    $histories = $history->getByTimestamp('>', $timestamp, array(array('op' => '=', 'field' => 'action', 'value' => $action)), 'kronolith');
    if (is_a($histories, 'PEAR_Error')) {
        return $histories;
    }

    return array_keys($histories);
}

/**
 * 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/x-icalendar
 *                             text/x-vcalendar
 *                             text/x-vevent
 *                             </pre>
 * @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':
        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 = &$kronolith->getEvent();
        $event->fromiCalendar($content);
        $eventId = $event->save();
        break;

    default:
        return PEAR::raiseError(_("Unsupported Content-Type."));
    }

    if (is_a($eventId, 'PEAR_Error')) {
        return $eventId;
    }

    $event = &$kronolith->getEvent($eventId);
    return $event->getUID();
}

/**
 * Exports an event, identified by UID, in the requested content type.
 *
 * @param string $uid         Identify the event to export.
 * @param mixed $contentType  What format should the data be in?
 *                            Either a string with one of:
 *                            <pre>
 *                             text/x-icalendar
 *                             text/x-vcalendar
 *                            </pre>
 *                            or an array with options:
 *                            <pre>
 *                             'ContentType': as above
 *                             'ENCODING':    (optional) character encoding
 *                                            for strings fields
 *                             'CHARSET':     (optional) charset. Like UTF-8
 *                            </pre>
 *
 * @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_EDIT))) {
        return PEAR::raiseError(_("Permission Denied"));
    }

    if (is_array($contentType)) {
        $options = $contentType;
        $contentType = $options['ContentType'];
        unset($options['ContentType']);
    } else {
        $options = array();
    }

    switch ($contentType) {
    case 'text/calendar':
    case 'text/x-icalendar':
    case 'text/x-vcalendar':
        $identity = &$kronolith_shares->getIdentityByShare($kronolith_shares->getShare($event->getCalendar()));

        require_once 'Horde/iCalendar.php';
        $iCal = &new Horde_iCalendar();

        // Create a new vEvent.
        $vEvent = &$event->toiCalendar($iCal, $identity);
        $vEvent->setParameter('SUMMARY', $options);
        $vEvent->setParameter('DESCRIPTION', $options);
        $vEvent->setParameter('LOCATION', $options);

        $iCal->addComponent($vEvent);

        return $iCal->exportvCalendar();

    default:
        return PEAR::raiseError(_("Unsupported Content-Type."));
    }
}

/**
 * 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/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:
                $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[] = $eventId;
                    }
                }
                return $ids;
            }
        }

        $event->fromiCalendar($content);
        $eventId = $event->save();
        break;

    default:
        return PEAR::raiseError(_("Unsupported Content-Type."));
    }

    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:', '', $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);
}
