* Kronolith_Driver defines an API for implementing storage backends for
* Kronolith.
*
+ * Copyright 1999-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
* @author Chuck Hagenbuch <chuck@horde.org>
* @author Jan Schneider <jan@horde.org>
* @package Kronolith
*/
public function nextRecurrence($eventId, $afterDate)
{
- $event = &$this->getEvent($eventId);
+ $event = $this->getEvent($eventId);
if (is_a($event, 'PEAR_Error')) {
return $event;
}
$params = Horde::getDriverConfig('calendar', $driver);
}
- include_once dirname(__FILE__) . '/Driver/' . $driver . '.php';
+ $driver = String::ucfirst($driver);
+
$class = 'Kronolith_Driver_' . $driver;
if (class_exists($class)) {
$driver = new $class($params);
}
}
-
-/**
- * Kronolith_Event defines a generic API for events.
- *
- * @author Chuck Hagenbuch <chuck@horde.org>
- * @author Jan Schneider <jan@horde.org>
- * @package Kronolith
- */
-class Kronolith_Event
-{
- /**
- * Flag that is set to true if this event has data from either a storage
- * backend or a form or other import method.
- *
- * @var boolean
- */
- public $initialized = false;
-
- /**
- * Flag that is set to true if this event exists in a storage driver.
- *
- * @var boolean
- */
- public $stored = false;
-
- /**
- * The driver unique identifier for this event.
- *
- * @var string
- */
- public $eventID = null;
-
- /**
- * The UID for this event.
- *
- * @var string
- */
- protected $_uid = null;
-
- /**
- * The iCalendar SEQUENCE for this event.
- *
- * @var integer
- */
- protected $_sequence = null;
-
- /**
- * The user id of the creator of the event.
- *
- * @var string
- */
- public $creatorID = null;
-
- /**
- * The title of this event.
- *
- * @var string
- */
- public $title = '';
-
- /**
- * The location this event occurs at.
- *
- * @var string
- */
- public $location = '';
-
- /**
- * The status of this event.
- *
- * @var integer
- */
- public $status = Kronolith::STATUS_CONFIRMED;
-
- /**
- * The description for this event
- *
- * @var string
- */
- public $description = '';
-
- /**
- * Remote description of this event (URL).
- *
- * @var string
- */
- public $remoteUrl = '';
-
- /**
- * Remote calendar name.
- *
- * @var string
- */
- public $remoteCal = '';
-
- /**
- * Whether the event is private.
- *
- * @var boolean
- */
- public $private = false;
-
- /**
- * This tag's events.
- *
- * @var mixed Array of tags or comma delimited string.
- */
- public $tags = array();
-
- /**
- * All the attendees of this event.
- *
- * This is an associative array where the keys are the email addresses
- * of the attendees, and the values are also associative arrays with
- * keys 'attendance' and 'response' pointing to the attendees' attendance
- * and response values, respectively.
- *
- * @var array
- */
- public $attendees = array();
-
- /**
- * The start time of the event.
- *
- * @var Horde_Date
- */
- public $start;
-
- /**
- * The end time of the event.
- *
- * @var Horde_Date
- */
- public $end;
-
- /**
- * The duration of this event in minutes
- *
- * @var integer
- */
- public $durMin = 0;
-
- /**
- * Whether this is an all-day event.
- *
- * @var boolean
- */
- public $allday = false;
-
- /**
- * Number of minutes before the event starts to trigger an alarm.
- *
- * @var integer
- */
- public $alarm = 0;
-
- /**
- * The particular alarm methods overridden for this event.
- *
- * @var array
- */
- public $methods;
-
- /**
- * The identifier of the calender this event exists on.
- *
- * @var string
- */
- protected $_calendar;
-
- /**
- * The HTML background color to be used for this event.
- *
- * @var string
- */
- protected $_backgroundColor;
-
- /**
- * The HTML foreground color to be used for this event.
- *
- * @var string
- */
- protected $_foregroundColor;
-
- /**
- * The VarRenderer class to use for printing select elements.
- *
- * @var Horde_UI_VarRenderer
- */
- private $_varRenderer;
-
- /**
- * The Horde_Date_Recurrence class for this event.
- *
- * @var Horde_Date_Recurrence
- */
- public $recurrence;
-
- /**
- * Constructor.
- *
- * @param Kronolith_Driver $driver The backend driver that this event is
- * stored in.
- * @param mixed $eventObject Backend specific event object
- * that this will represent.
- */
- public function __construct(&$driver, $eventObject = null)
- {
- static $alarm;
-
- /* Set default alarm value. */
- if (!isset($alarm) && isset($GLOBALS['prefs'])) {
- $alarm = $GLOBALS['prefs']->getValue('default_alarm');
- }
- $this->alarm = $alarm;
-
- $this->_calendar = $driver->getCalendar();
- if (!empty($this->_calendar)) {
- $share = $GLOBALS['all_calendars'][$this->_calendar];
- $this->_backgroundColor = $share->get('color');
- if (empty($this->_backgroundColor)) {
- $this->_backgroundColor = '#dddddd';
- }
- $this->_foregroundColor = Horde_Image::brightness($this->_backgroundColor) < 128 ? '#f6f6f6' : '#000';
- }
-
- if ($eventObject !== null) {
- $this->fromDriver($eventObject);
- $tagger = Kronolith::getTagger();
- $this->tags = $tagger->getTags($this->getUID(), 'event');
- }
- }
-
- /**
- * Returns a reference to a driver that's valid for this event.
- *
- * @return Kronolith_Driver A driver that this event can use to save
- * itself, etc.
- */
- public function getDriver()
- {
- global $kronolith_driver;
- if ($kronolith_driver->getCalendar() != $this->_calendar) {
- $kronolith_driver->open($this->_calendar);
- }
-
- return $kronolith_driver;
- }
-
- /**
- * Returns the share this event belongs to.
- *
- * @return Horde_Share This event's share.
- */
- public function getShare()
- {
- if (isset($GLOBALS['all_calendars'][$this->getCalendar()])) {
- $share = $GLOBALS['all_calendars'][$this->getCalendar()];
- } else {
- $share = PEAR::raiseError('Share not found');
- }
- return $share;
- }
-
- /**
- * Encapsulates permissions checking.
- *
- * @param integer $permission The permission to check for.
- * @param string $user The user to check permissions for.
- *
- * @return boolean
- */
- public function hasPermission($permission, $user = null)
- {
- if ($user === null) {
- $user = Auth::getAuth();
- }
-
- if ($this->remoteCal) {
- switch ($permission) {
- case PERMS_SHOW:
- case PERMS_READ:
- case PERMS_EDIT:
- return true;
-
- default:
- return false;
- }
- }
-
- return (!is_a($share = &$this->getShare(), 'PEAR_Error') &&
- $share->hasPermission($user, $permission, $this->getCreatorId()));
- }
-
- /**
- * Saves changes to this event.
- *
- * @return mixed True or a PEAR_Error on failure.
- */
- public function save()
- {
- if (!$this->isInitialized()) {
- return PEAR::raiseError('Event not yet initialized');
- }
-
- $this->toDriver();
- $driver = &$this->getDriver();
- $result = $driver->saveEvent($this);
- if (!is_a($result, 'PEAR_Error') &&
- !empty($GLOBALS['conf']['alarms']['driver'])) {
- $alarm = $this->toAlarm(new Horde_Date($_SERVER['REQUEST_TIME']));
- if ($alarm) {
- $alarm['start'] = new Horde_Date($alarm['start']);
- $alarm['end'] = new Horde_Date($alarm['end']);
- $horde_alarm = Horde_Alarm::factory();
- $horde_alarm->set($alarm);
- }
- }
-
- return $result;
- }
-
- /**
- * Exports this event in iCalendar format.
- *
- * @param Horde_iCalendar &$calendar A Horde_iCalendar object that acts as
- * a container.
- *
- * @return Horde_iCalendar_vevent The vEvent object for this event.
- */
- public function toiCalendar(&$calendar)
- {
- $vEvent = &Horde_iCalendar::newComponent('vevent', $calendar);
- $v1 = $calendar->getAttribute('VERSION') == '1.0';
-
- if ($this->isAllDay()) {
- $vEvent->setAttribute('DTSTART', $this->start, array('VALUE' => 'DATE'));
- $vEvent->setAttribute('DTEND', $this->end, array('VALUE' => 'DATE'));
- } else {
- $vEvent->setAttribute('DTSTART', $this->start);
- $vEvent->setAttribute('DTEND', $this->end);
- }
-
- $vEvent->setAttribute('DTSTAMP', $_SERVER['REQUEST_TIME']);
- $vEvent->setAttribute('UID', $this->_uid);
-
- /* Get the event's history. */
- $history = &Horde_History::singleton();
- $created = $modified = null;
- $log = $history->getHistory('kronolith:' . $this->_calendar . ':' . $this->_uid);
- if ($log && !is_a($log, 'PEAR_Error')) {
- foreach ($log->getData() as $entry) {
- switch ($entry['action']) {
- case 'add':
- $created = $entry['ts'];
- break;
-
- case 'modify':
- $modified = $entry['ts'];
- break;
- }
- }
- }
- if (!empty($created)) {
- $vEvent->setAttribute($v1 ? 'DCREATED' : 'CREATED', $created);
- if (empty($modified)) {
- $modified = $created;
- }
- }
- if (!empty($modified)) {
- $vEvent->setAttribute('LAST-MODIFIED', $modified);
- }
-
- $vEvent->setAttribute('SUMMARY', $v1 ? $this->getTitle() : String::convertCharset($this->getTitle(), NLS::getCharset(), 'utf-8'));
- $name = Kronolith::getUserName($this->getCreatorId());
- if (!$v1) {
- $name = String::convertCharset($name, NLS::getCharset(), 'utf-8');
- }
- $vEvent->setAttribute('ORGANIZER',
- 'mailto:' . Kronolith::getUserEmail($this->getCreatorId()),
- array('CN' => $name));
- if (!$this->isPrivate() || $this->getCreatorId() == Auth::getAuth()) {
- if (!empty($this->description)) {
- $vEvent->setAttribute('DESCRIPTION', $v1 ? $this->description : String::convertCharset($this->description, NLS::getCharset(), 'utf-8'));
- }
-
- // Tags
- $tags = $this->tags;
- if (is_array($tags)) {
- $tags = implode(', ', $tags);
- }
- if (!empty($tags)) {
- $vEvent->setAttribute('CATEGORIES', $v1 ? $tags : String::convertCharset($tags, NLS::getCharset(), 'utf-8'));
- }
-
- // Location
- if (!empty($this->location)) {
- $vEvent->setAttribute('LOCATION', $v1 ? $this->location : String::convertCharset($this->location, NLS::getCharset(), 'utf-8'));
- }
- }
- $vEvent->setAttribute('CLASS', $this->isPrivate() ? 'PRIVATE' : 'PUBLIC');
-
- // Status.
- switch ($this->getStatus()) {
- case Kronolith::STATUS_FREE:
- // This is not an official iCalendar value, but we need it for
- // synchronization.
- $vEvent->setAttribute('STATUS', 'FREE');
- $vEvent->setAttribute('TRANSP', $v1 ? 1 : 'TRANSPARENT');
- break;
- case Kronolith::STATUS_TENTATIVE:
- $vEvent->setAttribute('STATUS', 'TENTATIVE');
- $vEvent->setAttribute('TRANSP', $v1 ? 0 : 'OPAQUE');
- break;
- case Kronolith::STATUS_CONFIRMED:
- $vEvent->setAttribute('STATUS', 'CONFIRMED');
- $vEvent->setAttribute('TRANSP', $v1 ? 0 : 'OPAQUE');
- break;
- case Kronolith::STATUS_CANCELLED:
- if ($v1) {
- $vEvent->setAttribute('STATUS', 'DECLINED');
- $vEvent->setAttribute('TRANSP', 1);
- } else {
- $vEvent->setAttribute('STATUS', 'CANCELLED');
- $vEvent->setAttribute('TRANSP', 'TRANSPARENT');
- }
- break;
- }
-
- // Attendees.
- foreach ($this->getAttendees() as $email => $status) {
- $params = array();
- switch ($status['attendance']) {
- case Kronolith::PART_REQUIRED:
- if ($v1) {
- $params['EXPECT'] = 'REQUIRE';
- } else {
- $params['ROLE'] = 'REQ-PARTICIPANT';
- }
- break;
-
- case Kronolith::PART_OPTIONAL:
- if ($v1) {
- $params['EXPECT'] = 'REQUEST';
- } else {
- $params['ROLE'] = 'OPT-PARTICIPANT';
- }
- break;
-
- case Kronolith::PART_NONE:
- if ($v1) {
- $params['EXPECT'] = 'FYI';
- } else {
- $params['ROLE'] = 'NON-PARTICIPANT';
- }
- break;
- }
-
- switch ($status['response']) {
- case Kronolith::RESPONSE_NONE:
- if ($v1) {
- $params['STATUS'] = 'NEEDS ACTION';
- $params['RSVP'] = 'YES';
- } else {
- $params['PARTSTAT'] = 'NEEDS-ACTION';
- $params['RSVP'] = 'TRUE';
- }
- break;
-
- case Kronolith::RESPONSE_ACCEPTED:
- if ($v1) {
- $params['STATUS'] = 'ACCEPTED';
- } else {
- $params['PARTSTAT'] = 'ACCEPTED';
- }
- break;
-
- case Kronolith::RESPONSE_DECLINED:
- if ($v1) {
- $params['STATUS'] = 'DECLINED';
- } else {
- $params['PARTSTAT'] = 'DECLINED';
- }
- break;
-
- case Kronolith::RESPONSE_TENTATIVE:
- if ($v1) {
- $params['STATUS'] = 'TENTATIVE';
- } else {
- $params['PARTSTAT'] = 'TENTATIVE';
- }
- break;
- }
-
- if (strpos($email, '@') === false) {
- $email = '';
- }
- if ($v1) {
- if (!empty($status['name'])) {
- if (!empty($email)) {
- $email = ' <' . $email . '>';
- }
- $email = $status['name'] . $email;
- $email = Horde_Mime_Address::trimAddress($email);
- }
- } else {
- if (!empty($status['name'])) {
- $params['CN'] = String::convertCharset($status['name'], NLS::getCharset(), 'utf-8');
- }
- if (!empty($email)) {
- $email = 'mailto:' . $email;
- }
- }
-
- $vEvent->setAttribute('ATTENDEE', $email, $params);
- }
-
- // Alarms.
- if (!empty($this->alarm)) {
- if ($v1) {
- $alarm = new Horde_Date($this->start);
- $alarm->min -= $this->alarm;
- $vEvent->setAttribute('AALARM', $alarm);
- } else {
- $vAlarm = &Horde_iCalendar::newComponent('valarm', $vEvent);
- $vAlarm->setAttribute('ACTION', 'DISPLAY');
- $vAlarm->setAttribute('TRIGGER;VALUE=DURATION', '-PT' . $this->alarm . 'M');
- $vEvent->addComponent($vAlarm);
- }
- }
-
- // Recurrence.
- if ($this->recurs()) {
- if ($v1) {
- $rrule = $this->recurrence->toRRule10($calendar);
- } else {
- $rrule = $this->recurrence->toRRule20($calendar);
- }
- if (!empty($rrule)) {
- $vEvent->setAttribute('RRULE', $rrule);
- }
-
- // Exceptions.
- $exceptions = $this->recurrence->getExceptions();
- foreach ($exceptions as $exception) {
- if (!empty($exception)) {
- list($year, $month, $mday) = sscanf($exception, '%04d%02d%02d');
- $exdate = new Horde_Date(array(
- 'year' => $year,
- 'month' => $month,
- 'mday' => $mday,
- 'hour' => $this->start->hour,
- 'min' => $this->start->min,
- 'sec' => $this->start->sec,
- ));
- $vEvent->setAttribute('EXDATE', array($exdate));
- }
- }
- }
-
- return $vEvent;
- }
-
- /**
- * Updates the properties of this event from a Horde_iCalendar_vevent
- * object.
- *
- * @param Horde_iCalendar_vevent $vEvent The iCalendar data to update
- * from.
- */
- public function fromiCalendar($vEvent)
- {
- // Unique ID.
- $uid = $vEvent->getAttribute('UID');
- if (!empty($uid) && !is_a($uid, 'PEAR_Error')) {
- $this->setUID($uid);
- }
-
- // Sequence.
- $seq = $vEvent->getAttribute('SEQUENCE');
- if (is_int($seq)) {
- $this->_sequence = $seq;
- }
-
- // Title, tags and description.
- $title = $vEvent->getAttribute('SUMMARY');
- if (!is_array($title) && !is_a($title, 'PEAR_Error')) {
- $this->setTitle($title);
- }
-
- // Tags
- $categories = $vEvent->getAttributeValues('CATEGORIES');
- if (!is_a($categories, 'PEAR_Error')) {
- $this->tags = $categories;
- }
-
- // Description
- $desc = $vEvent->getAttribute('DESCRIPTION');
- if (!is_array($desc) && !is_a($desc, 'PEAR_Error')) {
- $this->setDescription($desc);
- }
-
- // Remote Url
- $url = $vEvent->getAttribute('URL');
- if (!is_array($url) && !is_a($url, 'PEAR_Error')) {
- $this->remoteUrl = $url;
- }
-
- // Location
- $location = $vEvent->getAttribute('LOCATION');
- if (!is_array($location) && !is_a($location, 'PEAR_Error')) {
- $this->setLocation($location);
- }
-
- // Class
- $class = $vEvent->getAttribute('CLASS');
- if (!is_array($class) && !is_a($class, 'PEAR_Error')) {
- $class = String::upper($class);
- if ($class == 'PRIVATE' || $class == 'CONFIDENTIAL') {
- $this->setPrivate(true);
- } else {
- $this->setPrivate(false);
- }
- }
-
- // Status.
- $status = $vEvent->getAttribute('STATUS');
- if (!is_array($status) && !is_a($status, 'PEAR_Error')) {
- $status = String::upper($status);
- if ($status == 'DECLINED') {
- $status = 'CANCELLED';
- }
- if (defined('Kronolith::STATUS_' . $status)) {
- $this->setStatus(constant('Kronolith::STATUS_' . $status));
- }
- }
-
- // Start and end date.
- $start = $vEvent->getAttribute('DTSTART');
- if (!is_a($start, 'PEAR_Error')) {
- if (!is_array($start)) {
- // Date-Time field
- $this->start = new Horde_Date($start);
- } else {
- // Date field
- $this->start = new Horde_Date(
- array('year' => (int)$start['year'],
- 'month' => (int)$start['month'],
- 'mday' => (int)$start['mday']));
- }
- }
- $end = $vEvent->getAttribute('DTEND');
- if (!is_a($end, 'PEAR_Error')) {
- if (!is_array($end)) {
- // Date-Time field
- $this->end = new Horde_Date($end);
- // All day events are transferred by many device as
- // DSTART: YYYYMMDDT000000 DTEND: YYYYMMDDT2359(59|00)
- // Convert accordingly
- if (is_object($this->start) && $this->start->hour == 0 &&
- $this->start->min == 0 && $this->start->sec == 0 &&
- $this->end->hour == 23 && $this->end->min == 59) {
- $this->end = new Horde_Date(
- array('year' => (int)$this->end->year,
- 'month' => (int)$this->end->month,
- 'mday' => (int)$this->end->mday + 1));
- }
- } elseif (is_array($end) && !is_a($end, 'PEAR_Error')) {
- // Date field
- $this->end = new Horde_Date(
- array('year' => (int)$end['year'],
- 'month' => (int)$end['month'],
- 'mday' => (int)$end['mday']));
- }
- } else {
- $duration = $vEvent->getAttribute('DURATION');
- if (!is_array($duration) && !is_a($duration, 'PEAR_Error')) {
- $this->end = new Horde_Date($this->start);
- $this->end->sec += $duration;
- } else {
- // End date equal to start date as per RFC 2445.
- $this->end = new Horde_Date($this->start);
- if (is_array($start)) {
- // Date field
- $this->end->mday++;
- }
- }
- }
-
- // vCalendar 1.0 alarms
- $alarm = $vEvent->getAttribute('AALARM');
- if (!is_array($alarm) &&
- !is_a($alarm, 'PEAR_Error') &&
- intval($alarm)) {
- $this->alarm = intval(($this->start->timestamp() - $alarm) / 60);
- }
-
- // @TODO: vCalendar 2.0 alarms
-
- // Attendance.
- // Importing attendance may result in confusion: editing an imported
- // copy of an event can cause invitation updates to be sent from
- // people other than the original organizer. So we don't import by
- // default. However to allow updates by SyncML replication, the custom
- // X-ATTENDEE attribute is used which has the same syntax as
- // ATTENDEE.
- $attendee = $vEvent->getAttribute('X-ATTENDEE');
- if (!is_a($attendee, 'PEAR_Error')) {
-
- if (!is_array($attendee)) {
- $attendee = array($attendee);
- }
- $params = $vEvent->getAttribute('X-ATTENDEE', true);
- if (!is_array($params)) {
- $params = array($params);
- }
- for ($i = 0; $i < count($attendee); ++$i) {
- $attendee[$i] = str_replace(array('MAILTO:', 'mailto:'), '',
- $attendee[$i]);
- $email = Horde_Mime_Address::bareAddress($attendee[$i]);
- // Default according to rfc2445:
- $attendance = Kronolith::PART_REQUIRED;
- // vCalendar 2.0 style:
- if (!empty($params[$i]['ROLE'])) {
- switch($params[$i]['ROLE']) {
- case 'OPT-PARTICIPANT':
- $attendance = Kronolith::PART_OPTIONAL;
- break;
-
- case 'NON-PARTICIPANT':
- $attendance = Kronolith::PART_NONE;
- break;
- }
- }
- // vCalendar 1.0 style;
- if (!empty($params[$i]['EXPECT'])) {
- switch($params[$i]['EXPECT']) {
- case 'REQUEST':
- $attendance = Kronolith::PART_OPTIONAL;
- break;
-
- case 'FYI':
- $attendance = Kronolith::PART_NONE;
- break;
- }
- }
- $response = Kronolith::RESPONSE_NONE;
- if (empty($params[$i]['PARTSTAT']) &&
- !empty($params[$i]['STATUS'])) {
- $params[$i]['PARTSTAT'] = $params[$i]['STATUS'];
- }
-
- if (!empty($params[$i]['PARTSTAT'])) {
- switch($params[$i]['PARTSTAT']) {
- case 'ACCEPTED':
- $response = Kronolith::RESPONSE_ACCEPTED;
- break;
-
- case 'DECLINED':
- $response = Kronolith::RESPONSE_DECLINED;
- break;
-
- case 'TENTATIVE':
- $response = Kronolith::RESPONSE_TENTATIVE;
- break;
- }
- }
- $name = isset($params[$i]['CN']) ? $params[$i]['CN'] : null;
-
- $this->addAttendee($email, $attendance, $response, $name);
- }
- }
-
- // Recurrence.
- $rrule = $vEvent->getAttribute('RRULE');
- if (!is_array($rrule) && !is_a($rrule, 'PEAR_Error')) {
- $this->recurrence = new Horde_Date_Recurrence($this->start);
- if (strpos($rrule, '=') !== false) {
- $this->recurrence->fromRRule20($rrule);
- } else {
- $this->recurrence->fromRRule10($rrule);
- }
-
- // Exceptions.
- $exdates = $vEvent->getAttributeValues('EXDATE');
- if (is_array($exdates)) {
- foreach ($exdates as $exdate) {
- if (is_array($exdate)) {
- $this->recurrence->addException((int)$exdate['year'],
- (int)$exdate['month'],
- (int)$exdate['mday']);
- }
- }
- }
- }
-
- $this->initialized = true;
- }
-
- /**
- * Imports the values for this event from an array of values.
- *
- * @param array $hash Array containing all the values.
- */
- public function fromHash($hash)
- {
- // See if it's a new event.
- if ($this->getId() === null) {
- $this->setCreatorId(Auth::getAuth());
- }
- if (!empty($hash['title'])) {
- $this->setTitle($hash['title']);
- } else {
- return PEAR::raiseError(_("Events must have a title."));
- }
- if (!empty($hash['description'])) {
- $this->setDescription($hash['description']);
- }
- if (!empty($hash['location'])) {
- $this->setLocation($hash['location']);
- }
- if (!empty($hash['start_date'])) {
- $date = explode('-', $hash['start_date']);
- if (empty($hash['start_time'])) {
- $time = array(0, 0, 0);
- } else {
- $time = explode(':', $hash['start_time']);
- if (count($time) == 2) {
- $time[2] = 0;
- }
- }
- if (count($time) == 3 && count($date) == 3) {
- $this->start = new Horde_Date(array('year' => $date[0],
- 'month' => $date[1],
- 'mday' => $date[2],
- 'hour' => $time[0],
- 'min' => $time[1],
- 'sec' => $time[2]));
- }
- } else {
- return PEAR::raiseError(_("Events must have a start date."));
- }
- if (empty($hash['duration'])) {
- if (empty($hash['end_date'])) {
- $hash['end_date'] = $hash['start_date'];
- }
- if (empty($hash['end_time'])) {
- $hash['end_time'] = $hash['start_time'];
- }
- } else {
- $weeks = str_replace('W', '', $hash['duration'][1]);
- $days = str_replace('D', '', $hash['duration'][2]);
- $hours = str_replace('H', '', $hash['duration'][4]);
- $minutes = isset($hash['duration'][5]) ? str_replace('M', '', $hash['duration'][5]) : 0;
- $seconds = isset($hash['duration'][6]) ? str_replace('S', '', $hash['duration'][6]) : 0;
- $hash['duration'] = ($weeks * 60 * 60 * 24 * 7) + ($days * 60 * 60 * 24) + ($hours * 60 * 60) + ($minutes * 60) + $seconds;
- $this->end = new Horde_Date($this->start);
- $this->end->sec += $hash['duration'];
- }
- if (!empty($hash['end_date'])) {
- $date = explode('-', $hash['end_date']);
- if (empty($hash['end_time'])) {
- $time = array(0, 0, 0);
- } else {
- $time = explode(':', $hash['end_time']);
- if (count($time) == 2) {
- $time[2] = 0;
- }
- }
- if (count($time) == 3 && count($date) == 3) {
- $this->end = new Horde_Date(array('year' => $date[0],
- 'month' => $date[1],
- 'mday' => $date[2],
- 'hour' => $time[0],
- 'min' => $time[1],
- 'sec' => $time[2]));
- }
- }
- if (!empty($hash['alarm'])) {
- $this->setAlarm($hash['alarm']);
- } elseif (!empty($hash['alarm_date']) &&
- !empty($hash['alarm_time'])) {
- $date = explode('-', $hash['alarm_date']);
- $time = explode(':', $hash['alarm_time']);
- if (count($time) == 2) {
- $time[2] = 0;
- }
- if (count($time) == 3 && count($date) == 3) {
- $alarm = new Horde_Date(array('hour' => $time[0],
- 'min' => $time[1],
- 'sec' => $time[2],
- 'month' => $date[1],
- 'mday' => $date[2],
- 'year' => $date[0]));
- $this->setAlarm(($this->start->timestamp() - $alarm->timestamp()) / 60);
- }
- }
- if (!empty($hash['recur_type'])) {
- $this->recurrence = new Horde_Date_Recurrence($this->start);
- $this->recurrence->setRecurType($hash['recur_type']);
- if (!empty($hash['recur_end_date'])) {
- $date = explode('-', $hash['recur_end_date']);
- $this->recurrence->setRecurEnd(new Horde_Date(array('year' => $date[0], 'month' => $date[1], 'mday' => $date[2])));
- }
- if (!empty($hash['recur_interval'])) {
- $this->recurrence->setRecurInterval($hash['recur_interval']);
- }
- if (!empty($hash['recur_data'])) {
- $this->recurrence->setRecurOnDay($hash['recur_data']);
- }
- }
-
- $this->initialized = true;
- }
-
- /**
- * Returns an alarm hash of this event suitable for Horde_Alarm.
- *
- * @param Horde_Date $time Time of alarm.
- * @param string $user The user to return alarms for.
- * @param Prefs $prefs A Prefs instance.
- *
- * @return array Alarm hash or null.
- */
- public function toAlarm($time, $user = null, $prefs = null)
- {
- if (!$this->getAlarm()) {
- return;
- }
-
- if ($this->recurs()) {
- $eventDate = $this->recurrence->nextRecurrence($time);
- if ($eventDate && $this->recurrence->hasException($eventDate->year, $eventDate->month, $eventDate->mday)) {
- return;
- }
- }
-
- if (empty($user)) {
- $user = Auth::getAuth();
- }
- if (empty($prefs)) {
- $prefs = $GLOBALS['prefs'];
- }
-
- $methods = !empty($this->methods) ? $this->methods : @unserialize($prefs->getValue('event_alarms'));
- $start = clone $this->start;
- $start->min -= $this->getAlarm();
- if (isset($methods['notify'])) {
- $methods['notify']['show'] = array(
- '__app' => $GLOBALS['registry']->getApp(),
- 'event' => $this->getId(),
- 'calendar' => $this->getCalendar());
- if (!empty($methods['notify']['sound'])) {
- if ($methods['notify']['sound'] == 'on') {
- // Handle boolean sound preferences.
- $methods['notify']['sound'] = $GLOBALS['registry']->get('themesuri') . '/sounds/theetone.wav';
- } else {
- // Else we know we have a sound name that can be
- // served from Horde.
- $methods['notify']['sound'] = $GLOBALS['registry']->get('themesuri', 'horde') . '/sounds/' . $methods['notify']['sound'];
- }
- }
- }
- if (isset($methods['popup'])) {
- $methods['popup']['message'] = $this->getTitle($user);
- $description = $this->getDescription();
- if (!empty($description)) {
- $methods['popup']['message'] .= "\n\n" . $description;
- }
- }
- if (isset($methods['mail'])) {
- $methods['mail']['body'] = sprintf(
- _("We would like to remind you of this upcoming event.\n\n%s\n\nLocation: %s\n\nDate: %s\nTime: %s\n\n%s"),
- $this->getTitle($user),
- $this->location,
- $this->start->strftime($prefs->getValue('date_format')),
- $this->start->format($prefs->getValue('twentyFour') ? 'H:i' : 'h:ia'),
- $this->getDescription());
- }
-
- return array(
- 'id' => $this->getUID(),
- 'user' => $user,
- 'start' => $start->timestamp(),
- 'end' => $this->end->timestamp(),
- 'methods' => array_keys($methods),
- 'params' => $methods,
- 'title' => $this->getTitle($user),
- 'text' => $this->getDescription());
- }
-
- /**
- * Returns a simple object suitable for json transport representing this
- * event.
- *
- * @return object A simple object.
- */
- public function toJSON()
- {
- $json = new stdClass;
- $json->t = $this->getTitle();
- $json->c = $this->getCalendar();
- $json->bg = $this->_backgroundColor;
- $json->fg = $this->_foregroundColor;
- return $json;
- }
-
- /**
- * TODO
- */
- public function isInitialized()
- {
- return $this->initialized;
- }
-
- /**
- * TODO
- */
- public function isStored()
- {
- return $this->stored;
- }
-
- /**
- * Checks if the current event is already present in the calendar.
- *
- * Does the check based on the uid.
- *
- * @return boolean True if event exists, false otherwise.
- */
- public function exists()
- {
- if (!isset($this->_uid) || !isset($this->_calendar)) {
- return false;
- }
-
- $eventID = $GLOBALS['kronolith_driver']->exists($this->_uid, $this->_calendar);
- if (is_a($eventID, 'PEAR_Error') || !$eventID) {
- return false;
- } else {
- $this->eventID = $eventID;
- return true;
- }
- }
-
- public function getDuration()
- {
- static $duration = null;
- if (isset($duration)) {
- return $duration;
- }
-
- if ($this->start && $this->end) {
- $dur_day_match = Date_Calc::dateDiff($this->start->mday,
- $this->start->month,
- $this->start->year,
- $this->end->mday,
- $this->end->month,
- $this->end->year);
- $dur_hour_match = $this->end->hour - $this->start->hour;
- $dur_min_match = $this->end->min - $this->start->min;
- while ($dur_min_match < 0) {
- $dur_min_match += 60;
- --$dur_hour_match;
- }
- while ($dur_hour_match < 0) {
- $dur_hour_match += 24;
- --$dur_day_match;
- }
- if ($dur_hour_match == 0 && $dur_min_match == 0 &&
- $this->end->mday - $this->start->mday == 1) {
- $dur_day_match = 1;
- $dur_hour_match = 0;
- $dur_min_match = 0;
- $whole_day_match = true;
- } else {
- $whole_day_match = false;
- }
- } else {
- $dur_day_match = 0;
- $dur_hour_match = 1;
- $dur_min_match = 0;
- $whole_day_match = false;
- }
-
- $duration = new stdClass;
- $duration->day = $dur_day_match;
- $duration->hour = $dur_hour_match;
- $duration->min = $dur_min_match;
- $duration->wholeDay = $whole_day_match;
-
- return $duration;
- }
-
- /**
- * Returns whether this event is a recurring event.
- *
- * @return boolean True if this is a recurring event.
- */
- public function recurs()
- {
- return isset($this->recurrence) &&
- !$this->recurrence->hasRecurType(Horde_Date_Recurrence::RECUR_NONE);
- }
-
- /**
- * Returns a description of this event's recurring type.
- *
- * @return string Human readable recurring type.
- */
- public function getRecurName()
- {
- return $this->recurs()
- ? $this->recurrence->getRecurName()
- : _("No recurrence");
- }
-
- /**
- * Returns a correcty formatted exception date for recurring events and a
- * link to delete this exception.
- *
- * @param string $date Exception in the format Ymd.
- *
- * @return string The formatted date and delete link.
- */
- public function exceptionLink($date)
- {
- if (!preg_match('/(\d{4})(\d{2})(\d{2})/', $date, $match)) {
- return '';
- }
- $horde_date = new Horde_Date(array('year' => $match[1],
- 'month' => $match[2],
- 'mday' => $match[3]));
- $formatted = $horde_date->strftime($GLOBALS['prefs']->getValue('date_format'));
- return $formatted
- . Horde::link(Util::addParameter(Horde::applicationUrl('edit.php'), array('calendar' => $this->getCalendar(), 'eventID' => $this->eventID, 'del_exception' => $date, 'url' => Util::getFormData('url'))), sprintf(_("Delete exception on %s"), $formatted))
- . Horde::img('delete-small.png', _("Delete"), '', $GLOBALS['registry']->getImageDir('horde'))
- . '</a>';
- }
-
- /**
- * Returns a list of exception dates for recurring events including links
- * to delete them.
- *
- * @return string List of exception dates and delete links.
- */
- public function exceptionsList()
- {
- return implode(', ', array_map(array($this, 'exceptionLink'), $this->recurrence->getExceptions()));
- }
-
- public function getCalendar()
- {
- return $this->_calendar;
- }
-
- public function setCalendar($calendar)
- {
- $this->_calendar = $calendar;
- }
-
- public function isRemote()
- {
- return (bool)$this->remoteCal;
- }
-
- /**
- * Returns the locally unique identifier for this event.
- *
- * @return string The local identifier for this event.
- */
- public function getId()
- {
- return $this->eventID;
- }
-
- /**
- * Sets the locally unique identifier for this event.
- *
- * @param string $eventId The local identifier for this event.
- */
- public function setId($eventId)
- {
- if (substr($eventId, 0, 10) == 'kronolith:') {
- $eventId = substr($eventId, 10);
- }
- $this->eventID = $eventId;
- }
-
- /**
- * Returns the global UID for this event.
- *
- * @return string The global UID for this event.
- */
- public function getUID()
- {
- return $this->_uid;
- }
-
- /**
- * Sets the global UID for this event.
- *
- * @param string $uid The global UID for this event.
- */
- public function setUID($uid)
- {
- $this->_uid = $uid;
- }
-
- /**
- * Returns the iCalendar SEQUENCE for this event.
- *
- * @return integer The sequence for this event.
- */
- public function getSequence()
- {
- return $this->_sequence;
- }
-
- /**
- * Returns the id of the user who created the event.
- *
- * @return string The creator id
- */
- public function getCreatorId()
- {
- return !empty($this->creatorID) ? $this->creatorID : Auth::getAuth();
- }
-
- /**
- * Sets the id of the creator of the event.
- *
- * @param string $creatorID The user id for the user who created the event
- */
- public function setCreatorId($creatorID)
- {
- $this->creatorID = $creatorID;
- }
-
- /**
- * Returns the title of this event.
- *
- * @param string $user The current user.
- *
- * @return string The title of this event.
- */
- public function getTitle($user = null)
- {
- if (isset($this->external) ||
- isset($this->contactID) ||
- $this->remoteCal) {
- return !empty($this->title) ? $this->title : _("[Unnamed event]");
- }
-
- if (!$this->isInitialized()) {
- return '';
- }
-
- if ($user === null) {
- $user = Auth::getAuth();
- }
-
- $twentyFour = $GLOBALS['prefs']->getValue('twentyFour');
- $start = $this->start->format($twentyFour ? 'G:i' : 'g:ia');
- $end = $this->end->format($twentyFour ? 'G:i' : 'g:ia');
-
- // We explicitly allow admin access here for the alarms notifications.
- if (!Auth::isAdmin() && $this->isPrivate() &&
- $this->getCreatorId() != $user) {
- return sprintf(_("Private Event from %s to %s"), $start, $end);
- } elseif (Auth::isAdmin() || $this->hasPermission(PERMS_READ, $user)) {
- return strlen($this->title) ? $this->title : _("[Unnamed event]");
- } else {
- return sprintf(_("Event from %s to %s"), $start, $end);
- }
- }
-
- /**
- * Sets the title of this event.
- *
- * @param string The new title for this event.
- */
- public function setTitle($title)
- {
- $this->title = $title;
- }
-
- /**
- * Returns the description of this event.
- *
- * @return string The description of this event.
- */
- public function getDescription()
- {
- return $this->description;
- }
-
- /**
- * Sets the description of this event.
- *
- * @param string $description The new description for this event.
- */
- public function setDescription($description)
- {
- $this->description = $description;
- }
-
- /**
- * Returns the location this event occurs at.
- *
- * @return string The location of this event.
- */
- public function getLocation()
- {
- return $this->location;
- }
-
- /**
- * Sets the location this event occurs at.
- *
- * @param string $location The new location for this event.
- */
- public function setLocation($location)
- {
- $this->location = $location;
- }
-
- /**
- * Returns whether this event is private.
- *
- * @return boolean Whether this even is private.
- */
- public function isPrivate()
- {
- return $this->private;
- }
-
- /**
- * Sets the private flag of this event.
- *
- * @param boolean $private Whether this event should be marked private.
- */
- public function setPrivate($private)
- {
- $this->private = !empty($private);
- }
-
- /**
- * Returns the event status.
- *
- * @return integer The status of this event.
- */
- public function getStatus()
- {
- return $this->status;
- }
-
- /**
- * Checks whether the events status is the same as the specified value.
- *
- * @param integer $status The status value to check against.
- *
- * @return boolean True if the events status is the same as $status.
- */
- public function hasStatus($status)
- {
- return ($status == $this->status);
- }
-
- /**
- * Sets the status of this event.
- *
- * @param integer $status The new event status.
- */
- public function setStatus($status)
- {
- $this->status = $status;
- }
-
- /**
- * Returns the entire attendees array.
- *
- * @return array A copy of the attendees array.
- */
- public function getAttendees()
- {
- return $this->attendees;
- }
-
- /**
- * Checks to see whether the specified attendee is associated with the
- * current event.
- *
- * @param string $email The email address of the attendee.
- *
- * @return boolean True if the specified attendee is present for this
- * event.
- */
- public function hasAttendee($email)
- {
- $email = String::lower($email);
- return isset($this->attendees[$email]);
- }
-
- /**
- * Sets the entire attendee array.
- *
- * @param array $attendees The new attendees array. This should be of the
- * correct format to avoid driver problems.
- */
- public function setAttendees($attendees)
- {
- $this->attendees = array_change_key_case($attendees);
- }
-
- /**
- * Adds a new attendee to the current event.
- *
- * This will overwrite an existing attendee if one exists with the same
- * email address.
- *
- * @param string $email The email address of the attendee.
- * @param integer $attendance The attendance code of the attendee.
- * @param integer $response The response code of the attendee.
- * @param string $name The name of the attendee.
- */
- public function addAttendee($email, $attendance, $response, $name = null)
- {
- $email = String::lower($email);
- if ($attendance == Kronolith::PART_IGNORE) {
- if (isset($this->attendees[$email])) {
- $attendance = $this->attendees[$email]['attendance'];
- } else {
- $attendance = Kronolith::PART_REQUIRED;
- }
- }
- if (empty($name) && isset($this->attendees[$email]) &&
- !empty($this->attendees[$email]['name'])) {
- $name = $this->attendees[$email]['name'];
- }
-
- $this->attendees[$email] = array(
- 'attendance' => $attendance,
- 'response' => $response,
- 'name' => $name
- );
- }
-
- /**
- * Removes the specified attendee from the current event.
- *
- * @param string $email The email address of the attendee.
- */
- public function removeAttendee($email)
- {
- $email = String::lower($email);
- if (isset($this->attendees[$email])) {
- unset($this->attendees[$email]);
- }
- }
-
- public function isAllDay()
- {
- return $this->allday ||
- ($this->start->hour == 0 && $this->start->min == 0 && $this->start->sec == 0 &&
- (($this->end->hour == 0 && $this->end->min == 0 && $this->end->sec == 0) ||
- ($this->end->hour == 23 && $this->end->min == 59)) &&
- ($this->end->mday > $this->start->mday ||
- $this->end->month > $this->start->month ||
- $this->end->year > $this->start->year));
- }
-
- public function getAlarm()
- {
- return $this->alarm;
- }
-
- public function setAlarm($alarm)
- {
- $this->alarm = $alarm;
- }
-
- public function readForm()
- {
- global $prefs, $cManager;
-
- // Event owner.
- $targetcalendar = Util::getFormData('targetcalendar');
- if (strpos($targetcalendar, ':')) {
- list(, $creator) = explode(':', $targetcalendar, 2);
- } else {
- $creator = isset($this->eventID) ? $this->getCreatorId() : Auth::getAuth();
- }
- $this->setCreatorId($creator);
-
- // Basic fields.
- $this->setTitle(Util::getFormData('title', $this->title));
- $this->setDescription(Util::getFormData('description', $this->description));
- $this->setLocation(Util::getFormData('location', $this->location));
- $this->setPrivate(Util::getFormData('private'));
-
- // Status.
- $this->setStatus(Util::getFormData('status', $this->status));
-
- // Attendees.
- if (isset($_SESSION['kronolith']['attendees']) && is_array($_SESSION['kronolith']['attendees'])) {
- $this->setAttendees($_SESSION['kronolith']['attendees']);
- }
-
- // Event start.
- $start = Util::getFormData('start');
- $start_year = $start['year'];
- $start_month = $start['month'];
- $start_day = $start['day'];
- $start_hour = Util::getFormData('start_hour');
- $start_min = Util::getFormData('start_min');
- $am_pm = Util::getFormData('am_pm');
-
- if (!$prefs->getValue('twentyFour')) {
- if ($am_pm == 'PM') {
- if ($start_hour != 12) {
- $start_hour += 12;
- }
- } elseif ($start_hour == 12) {
- $start_hour = 0;
- }
- }
-
- if (Util::getFormData('end_or_dur') == 1) {
- if (Util::getFormData('whole_day') == 1) {
- $start_hour = 0;
- $start_min = 0;
- $dur_day = 0;
- $dur_hour = 24;
- $dur_min = 0;
- } else {
- $dur_day = (int)Util::getFormData('dur_day');
- $dur_hour = (int)Util::getFormData('dur_hour');
- $dur_min = (int)Util::getFormData('dur_min');
- }
- }
-
- $this->start = new Horde_Date(array('hour' => $start_hour,
- 'min' => $start_min,
- 'month' => $start_month,
- 'mday' => $start_day,
- 'year' => $start_year));
-
- if (Util::getFormData('end_or_dur') == 1) {
- // Event duration.
- $this->end = new Horde_Date(array('hour' => $start_hour + $dur_hour,
- 'min' => $start_min + $dur_min,
- 'month' => $start_month,
- 'mday' => $start_day + $dur_day,
- 'year' => $start_year));
- } else {
- // Event end.
- $end = Util::getFormData('end');
- $end_year = $end['year'];
- $end_month = $end['month'];
- $end_day = $end['day'];
- $end_hour = Util::getFormData('end_hour');
- $end_min = Util::getFormData('end_min');
- $end_am_pm = Util::getFormData('end_am_pm');
-
- if (!$prefs->getValue('twentyFour')) {
- if ($end_am_pm == 'PM') {
- if ($end_hour != 12) {
- $end_hour += 12;
- }
- } elseif ($end_hour == 12) {
- $end_hour = 0;
- }
- }
-
- $this->end = new Horde_Date(array('hour' => $end_hour,
- 'min' => $end_min,
- 'month' => $end_month,
- 'mday' => $end_day,
- 'year' => $end_year));
- if ($this->end->compareDateTime($this->start) < 0) {
- $this->end = new Horde_Date($this->start);
- }
- }
-
- // Alarm.
- if (Util::getFormData('alarm') == 1) {
- $this->setAlarm(Util::getFormData('alarm_value') * Util::getFormData('alarm_unit'));
- // Notification.
- if (Util::getFormData('alarm_change_method')) {
- $types = Util::getFormData('event_alarms');
- if (!empty($types)) {
- $methods = array();
- foreach ($types as $type) {
- $methods[$type] = array();
- switch ($type){
- case 'notify':
- $methods[$type]['sound'] = Util::getFormData('event_alarms_sound');
- break;
- case 'mail':
- $methods[$type]['email'] = Util::getFormData('event_alarms_email');
- break;
- case 'popup':
- break;
- }
- }
- $this->methods = $methods;
- }
- } else {
- $this->methods = array();
- }
- } else {
- $this->setAlarm(0);
- $this->methods = array();
- }
-
- // Recurrence.
- $recur = Util::getFormData('recur');
- if ($recur !== null && $recur !== '') {
- if (!isset($this->recurrence)) {
- $this->recurrence = new Horde_Date_Recurrence($this->start);
- }
- if (Util::getFormData('recur_enddate_type') == 'date') {
- $recur_enddate = Util::getFormData('recur_enddate');
- if ($this->recurrence->hasRecurEnd()) {
- $recurEnd = $this->recurrence->recurEnd;
- $recurEnd->month = $recur_enddate['month'];
- $recurEnd->mday = $recur_enddate['day'];
- $recurEnd->year = $recur_enddate['year'];
- } else {
- $recurEnd = new Horde_Date(
- array('hour' => 23,
- 'min' => 59,
- 'sec' => 59,
- 'month' => $recur_enddate['month'],
- 'mday' => $recur_enddate['day'],
- 'year' => $recur_enddate['year']));
- }
- $this->recurrence->setRecurEnd($recurEnd);
- } elseif (Util::getFormData('recur_enddate_type') == 'count') {
- $this->recurrence->setRecurCount(Util::getFormData('recur_count'));
- } elseif (Util::getFormData('recur_enddate_type') == 'none') {
- $this->recurrence->setRecurCount(0);
- $this->recurrence->setRecurEnd(null);
- }
-
- $this->recurrence->setRecurType($recur);
- switch ($recur) {
- case Horde_Date_Recurrence::RECUR_DAILY:
- $this->recurrence->setRecurInterval(Util::getFormData('recur_daily_interval', 1));
- break;
-
- case Horde_Date_Recurrence::RECUR_WEEKLY:
- $weekly = Util::getFormData('weekly');
- $weekdays = 0;
- if (is_array($weekly)) {
- foreach ($weekly as $day) {
- $weekdays |= $day;
- }
- }
-
- if ($weekdays == 0) {
- // Sunday starts at 0.
- switch ($this->start->dayOfWeek()) {
- case 0: $weekdays |= Horde_Date::MASK_SUNDAY; break;
- case 1: $weekdays |= Horde_Date::MASK_MONDAY; break;
- case 2: $weekdays |= Horde_Date::MASK_TUESDAY; break;
- case 3: $weekdays |= Horde_Date::MASK_WEDNESDAY; break;
- case 4: $weekdays |= Horde_Date::MASK_THURSDAY; break;
- case 5: $weekdays |= Horde_Date::MASK_FRIDAY; break;
- case 6: $weekdays |= Horde_Date::MASK_SATURDAY; break;
- }
- }
-
- $this->recurrence->setRecurInterval(Util::getFormData('recur_weekly_interval', 1));
- $this->recurrence->setRecurOnDay($weekdays);
- break;
-
- case Horde_Date_Recurrence::RECUR_MONTHLY_DATE:
- $this->recurrence->setRecurInterval(Util::getFormData('recur_day_of_month_interval', 1));
- break;
-
- case Horde_Date_Recurrence::RECUR_MONTHLY_WEEKDAY:
- $this->recurrence->setRecurInterval(Util::getFormData('recur_week_of_month_interval', 1));
- break;
-
- case Horde_Date_Recurrence::RECUR_YEARLY_DATE:
- $this->recurrence->setRecurInterval(Util::getFormData('recur_yearly_interval', 1));
- break;
-
- case Horde_Date_Recurrence::RECUR_YEARLY_DAY:
- $this->recurrence->setRecurInterval(Util::getFormData('recur_yearly_day_interval', 1));
- break;
-
- case Horde_Date_Recurrence::RECUR_YEARLY_WEEKDAY:
- $this->recurrence->setRecurInterval(Util::getFormData('recur_yearly_weekday_interval', 1));
- break;
- }
- }
-
- // Tags.
- $this->tags = Util::getFormData('tags');
-
- $this->initialized = true;
- }
-
- public function html($property)
- {
- global $prefs;
-
- $options = array();
- $attributes = '';
- $sel = false;
- $label = '';
-
- switch ($property) {
- case 'start[year]':
- return '<label for="' . $this->_formIDEncode($property) . '" class="hidden">' . _("Start Year") . '</label>' .
- '<input name="' . $property . '" value="' . $this->start->year .
- '" type="text" onchange="' . $this->js($property) .
- '" id="' . $this->_formIDEncode($property) . '" size="4" maxlength="4" />';
-
- case 'start[month]':
- $sel = $this->start->month;
- for ($i = 1; $i < 13; ++$i) {
- $options[$i] = strftime('%b', mktime(1, 1, 1, $i, 1));
- }
- $attributes = ' onchange="' . $this->js($property) . '"';
- $label = _("Start Month");
- break;
-
- case 'start[day]':
- $sel = $this->start->mday;
- for ($i = 1; $i < 32; ++$i) {
- $options[$i] = $i;
- }
- $attributes = ' onchange="' . $this->js($property) . '"';
- $label = _("Start Day");
- break;
-
- case 'start_hour':
- $sel = $this->start->format($prefs->getValue('twentyFour') ? 'G' : 'g');
- $hour_min = $prefs->getValue('twentyFour') ? 0 : 1;
- $hour_max = $prefs->getValue('twentyFour') ? 24 : 13;
- for ($i = $hour_min; $i < $hour_max; ++$i) {
- $options[$i] = $i;
- }
- $attributes = ' onchange="document.eventform.whole_day.checked = false; KronolithEventForm.updateEndDate();"';
- $label = _("Start Hour");
- break;
-
- case 'start_min':
- $sel = sprintf('%02d', $this->start->min);
- for ($i = 0; $i < 12; ++$i) {
- $min = sprintf('%02d', $i * 5);
- $options[$min] = $min;
- }
- $attributes = ' onchange="document.eventform.whole_day.checked = false; KronolithEventForm.updateEndDate();"';
- $label = _("Start Minute");
- break;
-
- case 'end[year]':
- return '<label for="' . $this->_formIDEncode($property) . '" class="hidden">' . _("End Year") . '</label>' .
- '<input name="' . $property . '" value="' . $this->end->year .
- '" type="text" onchange="' . $this->js($property) .
- '" id="' . $this->_formIDEncode($property) . '" size="4" maxlength="4" />';
-
- case 'end[month]':
- $sel = $this->isInitialized() ? $this->end->month : $this->start->month;
- for ($i = 1; $i < 13; ++$i) {
- $options[$i] = strftime('%b', mktime(1, 1, 1, $i, 1));
- }
- $attributes = ' onchange="' . $this->js($property) . '"';
- $label = _("End Month");
- break;
-
- case 'end[day]':
- $sel = $this->isInitialized() ? $this->end->mday : $this->start->mday;
- for ($i = 1; $i < 32; ++$i) {
- $options[$i] = $i;
- }
- $attributes = ' onchange="' . $this->js($property) . '"';
- $label = _("End Day");
- break;
-
- case 'end_hour':
- $sel = $this->isInitialized() ?
- $this->end->format($prefs->getValue('twentyFour') ? 'G' : 'g') :
- $this->start->format($prefs->getValue('twentyFour') ? 'G' : 'g') + 1;
- $hour_min = $prefs->getValue('twentyFour') ? 0 : 1;
- $hour_max = $prefs->getValue('twentyFour') ? 24 : 13;
- for ($i = $hour_min; $i < $hour_max; ++$i) {
- $options[$i] = $i;
- }
- $attributes = ' onchange="KronolithEventForm.updateDuration(); document.eventform.end_or_dur[0].checked = true"';
- $label = _("End Hour");
- break;
-
- case 'end_min':
- $sel = $this->isInitialized() ? $this->end->min : $this->start->min;
- $sel = sprintf('%02d', $sel);
- for ($i = 0; $i < 12; ++$i) {
- $min = sprintf('%02d', $i * 5);
- $options[$min] = $min;
- }
- $attributes = ' onchange="KronolithEventForm.updateDuration(); document.eventform.end_or_dur[0].checked = true"';
- $label = _("End Minute");
- break;
-
- case 'dur_day':
- $dur = $this->getDuration();
- return '<label for="' . $property . '" class="hidden">' . _("Duration Day") . '</label>' .
- '<input name="' . $property . '" value="' . $dur->day .
- '" type="text" onchange="' . $this->js($property) .
- '" id="' . $property . '" size="4" maxlength="4" />';
-
- case 'dur_hour':
- $dur = $this->getDuration();
- $sel = $dur->hour;
- for ($i = 0; $i < 24; ++$i) {
- $options[$i] = $i;
- }
- $attributes = ' onchange="' . $this->js($property) . '"';
- $label = _("Duration Hour");
- break;
-
- case 'dur_min':
- $dur = $this->getDuration();
- $sel = $dur->min;
- for ($i = 0; $i < 13; ++$i) {
- $min = sprintf('%02d', $i * 5);
- $options[$min] = $min;
- }
- $attributes = ' onchange="' . $this->js($property) . '"';
- $label = _("Duration Minute");
- break;
-
- case 'recur_enddate[year]':
- if ($this->isInitialized()) {
- $end = ($this->recurs() && $this->recurrence->hasRecurEnd())
- ? $this->recurrence->recurEnd->year
- : $this->end->year;
- } else {
- $end = $this->start->year;
- }
- return '<label for="' . $this->_formIDEncode($property) . '" class="hidden">' . _("Recurrence End Year") . '</label>' .
- '<input name="' . $property . '" value="' . $end .
- '" type="text" onchange="' . $this->js($property) .
- '" id="' . $this->_formIDEncode($property) . '" size="4" maxlength="4" />';
-
- case 'recur_enddate[month]':
- if ($this->isInitialized()) {
- $sel = ($this->recurs() && $this->recurrence->hasRecurEnd())
- ? $this->recurrence->recurEnd->month
- : $this->end->month;
- } else {
- $sel = $this->start->month;
- }
- for ($i = 1; $i < 13; ++$i) {
- $options[$i] = strftime('%b', mktime(1, 1, 1, $i, 1));
- }
- $attributes = ' onchange="' . $this->js($property) . '"';
- $label = _("Recurrence End Month");
- break;
-
- case 'recur_enddate[day]':
- if ($this->isInitialized()) {
- $sel = ($this->recurs() && $this->recurrence->hasRecurEnd())
- ? $this->recurrence->recurEnd->mday
- : $this->end->mday;
- } else {
- $sel = $this->start->mday;
- }
- for ($i = 1; $i < 32; ++$i) {
- $options[$i] = $i;
- }
- $attributes = ' onchange="' . $this->js($property) . '"';
- $label = _("Recurrence End Day");
- break;
- }
-
- if (!$this->_varRenderer) {
- $this->_varRenderer = Horde_UI_VarRenderer::factory('html');
- }
-
- return '<label for="' . $this->_formIDEncode($property) . '" class="hidden">' . $label . '</label>' .
- '<select name="' . $property . '"' . $attributes . ' id="' . $this->_formIDEncode($property) . '">' .
- $this->_varRenderer->_selectOptions($options, $sel) .
- '</select>';
- }
-
- public function js($property)
- {
- switch ($property) {
- case 'start[month]':
- case 'start[year]':
- case 'start[day]':
- case 'start':
- return 'KronolithEventForm.updateWday(\'start_wday\'); document.eventform.whole_day.checked = false; KronolithEventForm.updateEndDate();';
-
- case 'end[month]':
- case 'end[year]':
- case 'end[day]':
- case 'end':
- return 'KronolithEventForm.updateWday(\'end_wday\'); updateDuration(); document.eventform.end_or_dur[0].checked = true;';
-
- case 'recur_enddate[month]':
- case 'recur_enddate[year]':
- case 'recur_enddate[day]':
- case 'recur_enddate':
- return 'KronolithEventForm.updateWday(\'recur_end_wday\'); document.eventform.recur_enddate_type[1].checked = true;';
-
- case 'dur_day':
- case 'dur_hour':
- case 'dur_min':
- return 'document.eventform.whole_day.checked = false; KronolithEventForm.updateEndDate(); document.eventform.end_or_dur[1].checked = true;';
- }
- }
-
- /**
- * @param array $params
- *
- * @return string
- */
- public function getViewUrl($params = array(), $full = false)
- {
- $params['eventID'] = $this->eventID;
- if ($this->remoteUrl) {
- return $this->remoteUrl;
- } elseif ($this->remoteCal) {
- $params['calendar'] = '**remote';
- $params['remoteCal'] = $this->remoteCal;
- } else {
- $params['calendar'] = $this->getCalendar();
- }
-
- return Horde::applicationUrl(Util::addParameter('event.php', $params), $full);
- }
-
- /**
- * @param array $params
- *
- * @return string
- */
- public function getEditUrl($params = array())
- {
- $params['view'] = 'EditEvent';
- $params['eventID'] = $this->eventID;
- if ($this->remoteCal) {
- $params['calendar'] = '**remote';
- $params['remoteCal'] = $this->remoteCal;
- } else {
- $params['calendar'] = $this->getCalendar();
- }
-
- return Horde::applicationUrl(Util::addParameter('event.php', $params));
- }
-
- /**
- * @param array $params
- *
- * @return string
- */
- public function getDeleteUrl($params = array())
- {
- $params['view'] = 'DeleteEvent';
- $params['eventID'] = $this->eventID;
- $params['calendar'] = $this->getCalendar();
- return Horde::applicationUrl(Util::addParameter('event.php', $params));
- }
-
- /**
- * @param array $params
- *
- * @return string
- */
- public function getExportUrl($params = array())
- {
- $params['view'] = 'ExportEvent';
- $params['eventID'] = $this->eventID;
- if ($this->remoteCal) {
- $params['calendar'] = '**remote';
- $params['remoteCal'] = $this->remoteCal;
- } else {
- $params['calendar'] = $this->getCalendar();
- }
-
- return Horde::applicationUrl(Util::addParameter('event.php', $params));
- }
-
- public function getLink($datetime = null, $icons = true, $from_url = null, $full = false)
- {
- global $prefs, $registry;
-
- if (is_null($datetime)) {
- $datetime = $this->start;
- }
- if (is_null($from_url)) {
- $from_url = Horde::selfUrl(true, false, true);
- }
-
- $link = '';
- $event_title = $this->getTitle();
- if (isset($this->external)) {
- $link = $registry->link($this->external . '/show', $this->external_params);
- $link = Horde::linkTooltip(Horde::url($link), '', 'event-tentative', '', '', String::wrap($this->description));
- } elseif (isset($this->eventID) && $this->hasPermission(PERMS_READ)) {
- $link = Horde::linkTooltip($this->getViewUrl(array('datetime' => $datetime->strftime('%Y%m%d%H%M%S'), 'url' => $from_url), $full),
- $event_title,
- $this->getStatusClass(), '', '',
- $this->getTooltip(),
- '',
- array('style' => $this->getCSSColors(false)));
- }
-
- $link .= @htmlspecialchars($event_title, ENT_QUOTES, NLS::getCharset());
-
- if ($this->hasPermission(PERMS_READ) &&
- (isset($this->eventID) ||
- isset($this->external))) {
- $link .= '</a>';
- }
-
- if ($icons && $prefs->getValue('show_icons')) {
- $icon_color = $this->_foregroundColor == '#000' ? '000' : 'fff';
- $status = '';
- if ($this->alarm) {
- if ($this->alarm % 10080 == 0) {
- $alarm_value = $this->alarm / 10080;
- $title = $alarm_value == 1 ?
- _("Alarm 1 week before") :
- sprintf(_("Alarm %d weeks before"), $alarm_value);
- } elseif ($this->alarm % 1440 == 0) {
- $alarm_value = $this->alarm / 1440;
- $title = $alarm_value == 1 ?
- _("Alarm 1 day before") :
- sprintf(_("Alarm %d days before"), $alarm_value);
- } elseif ($this->alarm % 60 == 0) {
- $alarm_value = $this->alarm / 60;
- $title = $alarm_value == 1 ?
- _("Alarm 1 hour before") :
- sprintf(_("Alarm %d hours before"), $alarm_value);
- } else {
- $alarm_value = $this->alarm;
- $title = $alarm_value == 1 ?
- _("Alarm 1 minute before") :
- sprintf(_("Alarm %d minutes before"), $alarm_value);
- }
- $status .= Horde::img('alarm-' . $icon_color . '.png', $title,
- array('title' => $title,
- 'class' => 'iconAlarm'),
- Horde::url($registry->getImageDir(), true, -1));
- }
-
- if ($this->recurs()) {
- $title = Kronolith::recurToString($this->recurrence->getRecurType());
- $status .= Horde::img('recur-' . $icon_color . '.png', $title,
- array('title' => $title,
- 'class' => 'iconRecur'),
- Horde::url($registry->getImageDir(), true, -1));
- }
-
- if ($this->isPrivate()) {
- $title = _("Private event");
- $status .= Horde::img('private-' . $icon_color . '.png', $title,
- array('title' => $title,
- 'class' => 'iconPrivate'),
- Horde::url($registry->getImageDir(), true, -1));
- }
-
- if (!empty($this->attendees)) {
- $title = count($this->attendees) == 1
- ? _("1 attendee")
- : sprintf(_("%s attendees"), count($this->attendees));
- $status .= Horde::img('attendees.png', $title,
- array('title' => $title,
- 'class' => 'iconPeople'),
- Horde::url($registry->getImageDir(), true, -1));
- }
-
- if (!empty($status)) {
- $link .= ' ' . $status;
- }
-
- if (!$this->eventID || !empty($this->external)) {
- return $link;
- }
-
- $edit = '';
- $delete = '';
- if ((!$this->isPrivate() || $this->getCreatorId() == Auth::getAuth())
- && $this->hasPermission(PERMS_EDIT)) {
- $editurl = $this->getEditUrl(array('datetime' => $datetime->strftime('%Y%m%d%H%M%S'),
- 'url' => $from_url));
- $edit = Horde::link($editurl, sprintf(_("Edit %s"), $event_title), 'iconEdit')
- . Horde::img('edit-' . $icon_color . '.png', _("Edit"), '', Horde::url($registry->getImageDir(), true, -1))
- . '</a>';
- }
- if ($this->hasPermission(PERMS_DELETE)) {
- $delurl = $this->getDeleteUrl(array('datetime' => $datetime->strftime('%Y%m%d%H%M%S'),
- 'url' => $from_url));
- $delete = Horde::link($delurl, sprintf(_("Delete %s"), $event_title), 'iconDelete')
- . Horde::img('delete-' . $icon_color . '.png', _("Delete"), '', Horde::url($registry->getImageDir(), true, -1))
- . '</a>';
- }
-
- if ($edit || $delete) {
- $link .= $edit . $delete;
- }
- }
-
- return $link;
- }
-
- /**
- * Returns the CSS color definition for this event.
- *
- * @param boolean $with_attribute Whether to wrap the colors inside a
- * "style" attribute.
- *
- * @return string A CSS string with color definitions.
- */
- public function getCSSColors($with_attribute = true)
- {
- $css = 'background-color:' . $this->_backgroundColor . ';color:' . $this->_foregroundColor;
- if ($with_attribute) {
- $css = ' style="' . $css . '"';
- }
- return $css;
- }
-
- /**
- * @return string A tooltip for quick descriptions of this event.
- */
- public function getTooltip()
- {
- $tooltip = $this->getTimeRange()
- . "\n" . sprintf(_("Owner: %s"), ($this->getCreatorId() == Auth::getAuth() ?
- _("Me") : Kronolith::getUserName($this->getCreatorId())));
-
- if (!$this->isPrivate() || $this->getCreatorId() == Auth::getAuth()) {
- if ($this->location) {
- $tooltip .= "\n" . _("Location") . ': ' . $this->location;
- }
-
- if ($this->description) {
- $tooltip .= "\n\n" . String::wrap($this->description);
- }
- }
-
- return $tooltip;
- }
-
- /**
- * @return string The time range of the event ("All Day", "1:00pm-3:00pm",
- * "08:00-22:00").
- */
- public function getTimeRange()
- {
- if ($this->isAllDay()) {
- return _("All day");
- } elseif (($cmp = $this->start->compareDate($this->end)) > 0) {
- $df = $GLOBALS['prefs']->getValue('date_format');
- if ($cmp > 0) {
- return $this->end->strftime($df) . '-'
- . $this->start->strftime($df);
- } else {
- return $this->start->strftime($df) . '-'
- . $this->end->strftime($df);
- }
- } else {
- $twentyFour = $GLOBALS['prefs']->getValue('twentyFour');
- return $this->start->format($twentyFour ? 'G:i' : 'g:ia')
- . '-'
- . $this->end->format($twentyFour ? 'G:i' : 'g:ia');
- }
- }
-
- /**
- * @return string The CSS class for the event based on its status.
- */
- public function getStatusClass()
- {
- switch ($this->status) {
- case Kronolith::STATUS_CANCELLED:
- return 'event-cancelled';
-
- case Kronolith::STATUS_TENTATIVE:
- case Kronolith::STATUS_FREE:
- return 'event-tentative';
- }
-
- return 'event';
- }
-
- private function _formIDEncode($id)
- {
- return str_replace(array('[', ']'),
- array('_', ''),
- $id);
- }
-
-}
--- /dev/null
+<?php
+/**
+ * The Kronolith_Driver_Holidays implements support for the PEAR package
+ * Date_Holidays.
+ *
+ * Copyright 2006-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * @see http://pear.php.net/packages/Date_Holidays
+ * @author Stephan Hohmann <webmaster@dasourcerer.net>
+ * @package Kronolith
+ */
+class Kronolith_Driver_Holidays extends Kronolith_Driver
+{
+
+ public function listAlarms($date, $fullevent = false)
+ {
+ return array();
+ }
+
+ /**
+ * Returns a list of all holidays occuring between <code>$startDate</code>
+ * and <code>$endDate</code>.
+ *
+ * @param int|Horde_Date $startDate The start of the datespan to be
+ * checked. Defaults to the current date.
+ * @param int|Horde_Date $endDate The end of the datespan. Defaults to
+ * the current date.
+ * @param bool $hasAlarm Left in for compatibility reasons and
+ * has no effect on this function.
+ * Defaults to <code>false</code>
+ *
+ * @return array An array of all holidays within the given datespan.
+ */
+ public function listEvents($startDate = null, $endDate = null, $hasAlarm = false)
+ {
+ global $language;
+
+ $events = array();
+
+ if (is_null($startDate)) {
+ $startDate = new Horde_Date($_SERVER['REQUEST_TIME']);
+ }
+
+ if (is_null($endDate)) {
+ $endDate = new Horde_Date($_SERVER['REQUEST_TIME']);
+ }
+
+ Date_Holidays::staticSetProperty('DIE_ON_MISSING_LOCALE', false);
+ foreach (unserialize($GLOBALS['prefs']->getValue('holiday_drivers')) as $driver) {
+ for ($year = $startDate->year; $year <= $endDate->year; $year++) {
+ $dh = Date_Holidays::factory($driver, $year, $language);
+ if (Date_Holidays::isError($dh)) {
+ Horde::logMessage(sprintf('Factory was unable to produce driver object for driver %s in year %s with locale %s',
+ $driver, $year, $language),
+ __FILE__, __LINE__, PEAR_LOG_ERR);
+ continue;
+ }
+ $dh->addTranslation($language);
+ $events = array_merge($events, $this->_getEvents($dh, $startDate, $endDate));
+ }
+ }
+
+ return $events;
+ }
+
+ private function _getEvents($dh, $startDate, $endDate)
+ {
+ $events = array();
+ for ($date = new Horde_Date($startDate);
+ $date->compareDate($endDate) <= 0;
+ $date->mday++) {
+ $holidays = $dh->getHolidayForDate($date->format('Y-m-d'), null, true);
+ if (Date_Holidays::isError($holidays)) {
+ Horde::logMessage(sprintf('Unable to retrieve list of holidays from %s to %s',
+ (string)$startDate, (string)$endDate), __FILE__, __LINE__);
+ continue;
+ }
+
+ if (is_null($holidays)) {
+ continue;
+ }
+
+ foreach ($holidays as $holiday) {
+ $event = new Kronolith_Event_Holidays($this);
+ $event->fromDriver($holiday);
+ $events[] = $event;
+ }
+ }
+ return $events;
+ }
+
+ private function _getTranslationFile($driver)
+ {
+ static $data_dir;
+ if (!isset($data_dir)) {
+ include_once 'PEAR/Config.php';
+ $pear_config = new PEAR_Config();
+ $data_dir = $pear_config->get('data_dir');
+ }
+ if (empty($data_dir)) {
+ return;
+ }
+
+ foreach (array('', '_' . $driver) as $pkg_ext) {
+ foreach (array('ser', 'xml') as $format) {
+ $location = $data_dir . '/Date_Holidays' . $pkg_ext . '/lang/'
+ . $driver . '/' . $GLOBALS['language'] . '.' . $format;
+ if (file_exists($location)) {
+ return array($format, $location);
+ }
+ }
+ }
+
+ return array(null, null);
+ }
+
+}
--- /dev/null
+<?php
+/**
+ * The Kronolith_Driver_Ical class implements the Kronolith_Driver API for
+ * iCalendar data.
+ *
+ * Possible driver parameters:
+ * - url: The location of the remote calendar.
+ * - proxy: A hash with HTTP proxy information.
+ * - user: The user name for HTTP Basic Authentication.
+ * - password: The password for HTTP Basic Authentication.
+ *
+ * Copyright 2004-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ * @author Jan Schneider <jan@horde.org>
+ * @package Kronolith
+ */
+class Kronolith_Driver_Ical extends Kronolith_Driver
+{
+ /**
+ * Cache events as we fetch them to avoid fetching or parsing the same
+ * event twice.
+ *
+ * @var array
+ */
+ private $_cache = array();
+
+ public function listAlarms($date, $fullevent = false)
+ {
+ return array();
+ }
+
+ /**
+ * Lists all events in the time range, optionally restricting
+ * results to only events with alarms.
+ *
+ * @param Horde_Date $startInterval Start of range date object.
+ * @param Horde_Date $endInterval End of range data object.
+ * @param boolean $hasAlarm Only return events with alarms?
+ * Defaults to all events.
+ *
+ * @return array Events in the given time range.
+ */
+ public function listEvents($startDate = null, $endDate = null,
+ $hasAlarm = false)
+ {
+ $data = $this->_getRemoteCalendar();
+ if (is_a($data, 'PEAR_Error')) {
+ return $data;
+ }
+
+ $iCal = new Horde_iCalendar();
+ if (!$iCal->parsevCalendar($data)) {
+ return array();
+ }
+
+ $components = $iCal->getComponents();
+ $events = array();
+ $count = count($components);
+ $exceptions = array();
+ for ($i = 0; $i < $count; $i++) {
+ $component = $components[$i];
+ if ($component->getType() == 'vEvent') {
+ $event = new Kronolith_Event_Ical($this);
+ $event->status = Kronolith::STATUS_FREE;
+ $event->fromiCalendar($component);
+ $event->remoteCal = $this->_params['url'];
+ $event->eventID = $i;
+
+ /* Catch RECURRENCE-ID attributes which mark single recurrence
+ * instances. */
+ $recurrence_id = $component->getAttribute('RECURRENCE-ID');
+ if (is_int($recurrence_id) &&
+ is_string($uid = $component->getAttribute('UID')) &&
+ is_int($seq = $component->getAttribute('SEQUENCE'))) {
+ $exceptions[$uid][$seq] = $recurrence_id;
+ }
+
+ /* Ignore events out of the period. */
+ if (
+ /* Starts after the period. */
+ $event->start->compareDateTime($endDate) > 0 ||
+ /* End before the period and doesn't recur. */
+ (!$event->recurs() &&
+ $event->end->compareDateTime($startDate) < 0) ||
+ /* Recurs and ... */
+ ($event->recurs() &&
+ /* ... has a recurrence end before the period. */
+ ($event->recurrence->hasRecurEnd() &&
+ $event->recurrence->recurEnd->compareDateTime($startDate) < 0))) {
+ continue;
+ }
+
+ $events[] = $event;
+ }
+ }
+
+ /* Loop through all explicitly defined recurrence intances and create
+ * exceptions for those in the event with the matchin recurrence. */
+ foreach ($events as $key => $event) {
+ if ($event->recurs() &&
+ isset($exceptions[$event->getUID()][$event->getSequence()])) {
+ $timestamp = $exceptions[$event->getUID()][$event->getSequence()];
+ $events[$key]->recurrence->addException(date('Y', $timestamp), date('m', $timestamp), date('d', $timestamp));
+ }
+ }
+
+ return $events;
+ }
+
+ public function getEvent($eventId = null)
+ {
+ $data = $this->_getRemoteCalendar();
+ if (is_a($data, 'PEAR_Error')) {
+ return $data;
+ }
+
+ $iCal = new Horde_iCalendar();
+ if (!$iCal->parsevCalendar($data)) {
+ return array();
+ }
+
+ $components = $iCal->getComponents();
+ if (isset($components[$eventId]) &&
+ $components[$eventId]->getType() == 'vEvent') {
+ $event = new Kronolith_Event_Ical($this);
+ $event->status = Kronolith::STATUS_FREE;
+ $event->fromiCalendar($components[$eventId]);
+ $event->remoteCal = $this->_params['url'];
+ $event->eventID = $eventId;
+
+ return $event;
+ }
+
+ return false;
+ }
+
+ /**
+ * Fetches a remote calendar into the session and return the data.
+ *
+ * @return mixed Either the calendar data, or an error on failure.
+ */
+ private function _getRemoteCalendar()
+ {
+ $url = trim($this->_params['url']);
+
+ /* Treat webcal:// URLs as http://. */
+ if (substr($url, 0, 9) == 'webcal://') {
+ $url = str_replace('webcal://', 'http://', $url);
+ }
+
+ if (empty($_SESSION['kronolith']['remote'][$url])) {
+ $options['method'] = 'GET';
+ $options['timeout'] = 5;
+ $options['allowRedirects'] = true;
+
+ if (isset($this->_params['proxy'])) {
+ $options = array_merge($options, $this->_params['proxy']);
+ }
+
+ $http = new HTTP_Request($url, $options);
+ if (!empty($this->_params['user'])) {
+ $http->setBasicAuth($this->_params['user'],
+ $this->_params['password']);
+ }
+ @$http->sendRequest();
+ if ($http->getResponseCode() != 200) {
+ Horde::logMessage(sprintf('Failed to retrieve remote calendar: url = "%s", status = %s',
+ $url, $http->getResponseCode()),
+ __FILE__, __LINE__, PEAR_LOG_ERR);
+ return PEAR::raiseError(sprintf(_("Could not open %s."), $url));
+ }
+ $_SESSION['kronolith']['remote'][$url] = $http->getResponseBody();
+
+ /* Log fetch at DEBUG level. */
+ Horde::logMessage(sprintf('Retrieved remote calendar for %s: url = "%s"',
+ Auth::getAuth(), $url),
+ __FILE__, __LINE__, PEAR_LOG_DEBUG);
+ }
+
+ return $_SESSION['kronolith']['remote'][$url];
+ }
+
+}
--- /dev/null
+<?php
+
+require_once 'Horde/Kolab.php';
+require_once 'Horde/Identity.php';
+
+/**
+ * Horde Kronolith driver for the Kolab IMAP Server.
+ *
+ * Copyright 2004-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * @author Thomas Jarosch <thomas.jarosch@intra2net.com>
+ * @author Gunnar Wrobel <wrobel@pardus.de>
+ * @author Stuart Binge <omicron@mighty.co.za>
+ * @package Kronolith
+ */
+class Kronolith_Driver_Kolab extends Kronolith_Driver
+{
+ /**
+ * Our Kolab server connection.
+ *
+ * @var Kolab
+ */
+ private $_kolab = null;
+
+ /**
+ * Internal cache of Kronolith_Event_Kolab. eventID/UID is key
+ *
+ * @var array
+ */
+ private $_events_cache;
+
+ /**
+ * Indicates if we have synchronized this folder
+ *
+ * @var boolean
+ */
+ private $_synchronized;
+
+ /**
+ * Shortcut to the imap connection
+ *
+ * @var Kolab_IMAP
+ */
+ private $_store;
+
+ /**
+ * Attempts to open a Kolab Groupware folder.
+ *
+ * @return boolean True on success, PEAR_Error on failure.
+ */
+ public function initialize()
+ {
+ $this->_kolab = new Kolab();
+ $this->reset();
+ return true;
+ }
+
+ /**
+ * Change current calendar
+ */
+ public function open($calendar)
+ {
+ if ($this->_calendar != $calendar) {
+ $this->_calendar = $calendar;
+ $this->reset();
+ }
+
+ return true;
+ }
+
+ /**
+ * Reset internal variable on share change
+ */
+ public function reset()
+ {
+ $this->_events_cache = array();
+ $this->_synchronized = false;
+ }
+
+ // We delay initial synchronization to the first use
+ // so multiple calendars don't add to the total latency.
+ // This function must be called before all internal driver functions
+ public function synchronize($force = false)
+ {
+ if ($this->_synchronized && !$force) {
+ return;
+ }
+
+ // Connect to the Kolab backend
+ $result = $this->_kolab->open($this->_calendar, 1);
+ if (is_a($result, 'PEAR_Error')) {
+ return $result;
+ }
+ $this->_store = $this->_kolab->_storage;
+
+ // build internal event cache
+ $this->_events_cache = array();
+ $events = $this->_store->getObjects();
+ foreach ($events as $event) {
+ $this->_events_cache[$event['uid']] = new Kronolith_Event_Kolab($this, $event);
+ }
+
+ $this->_synchronized = true;
+ }
+
+ public function listAlarms($date, $fullevent = false)
+ {
+ $allevents = $this->listEvents($date, null, true);
+ $events = array();
+
+ foreach ($allevents as $eventId) {
+ $event = $this->getEvent($eventId);
+ if (is_a($event, 'PEAR_Error')) {
+ return $event;
+ }
+
+ if (!$event->recurs()) {
+ $start = new Horde_Date($event->start);
+ $start->min -= $event->getAlarm();
+ if ($start->compareDateTime($date) <= 0 &&
+ $date->compareDateTime($event->end) <= -1) {
+ $events[] = $fullevent ? $event : $eventId;
+ }
+ } else {
+ if ($next = $event->recurrence->nextRecurrence($date)) {
+ if ($event->recurrence->hasException($next->year, $next->month, $next->mday)) {
+ continue;
+ }
+ $start = new Horde_Date($next);
+ $start->min -= $event->getAlarm();
+ $end = new Horde_Date(array('year' => $next->year,
+ 'month' => $next->month,
+ 'mday' => $next->mday,
+ 'hour' => $event->end->hour,
+ 'min' => $event->end->min,
+ 'sec' => $event->end->sec));
+ if ($start->compareDateTime($date) <= 0 &&
+ $date->compareDateTime($end) <= -1) {
+ if ($fullevent) {
+ $event->start = $start;
+ $event->end = $end;
+ $events[] = $event;
+ } else {
+ $events[] = $eventId;
+ }
+ }
+ }
+ }
+ }
+
+ return is_array($events) ? $events : array();
+ }
+
+ /**
+ * Checks if the event's UID already exists and returns all event
+ * ids with that UID.
+ *
+ * @param string $uid The event's uid.
+ * @param string $calendar_id Calendar to search in.
+ *
+ * @return string|boolean Returns a string with event_id or false if
+ * not found.
+ */
+ public function exists($uid, $calendar_id = null)
+ {
+ // Log error if someone uses this function in an unsupported way
+ if ($calendar_id != $this->_calendar) {
+ Horde::logMessage(sprintf("Kolab::exists called for calendar %s. Currently active is %s.", $calendar_id, $this->_calendar), __FILE__, __LINE__, PEAR_LOG_ERR);
+ return PEAR::raiseError(sprintf("Kolab::exists called for calendar %s. Currently active is %s.", $calendar_id, $this->_calendar));
+ }
+
+ $result = $this->synchronize();
+ if (is_a($result, 'PEAR_Error')) {
+ return $result;
+ }
+
+ if ($this->_store->objectUidExists($uid)) {
+ return $uid;
+ }
+
+ return false;
+ }
+
+ /**
+ * Lists all events in the time range, optionally restricting
+ * results to only events with alarms.
+ *
+ * @param Horde_Date $startInterval Start of range date object.
+ * @param Horde_Date $endInterval End of range data object.
+ * @param boolean $hasAlarm Only return events with alarms?
+ * Defaults to all events.
+ *
+ * @return array Events in the given time range.
+ */
+ public function listEvents($startDate = null, $endDate = null, $hasAlarm = false)
+ {
+ $result = $this->synchronize();
+ if (is_a($result, 'PEAR_Error')) {
+ return $result;
+ }
+
+ if (is_null($startDate)) {
+ $startDate = new Horde_Date(array('mday' => 1, 'month' => 1, 'year' => 0000));
+ }
+ if (is_null($endDate)) {
+ $endDate = new Horde_Date(array('mday' => 31, 'month' => 12, 'year' => 9999));
+ }
+
+ $ids = array();
+
+ foreach($this->_events_cache as $event) {
+ if ($hasAlarm && !$event->getAlarm()) {
+ continue;
+ }
+
+ $keep_event = false;
+ /* check if event period intersects with given period */
+ if (!(($endDate->compareDateTime($event->start) < 0) ||
+ ($startDate->compareDateTime($event->end) > 0))) {
+ $keep_event = true;
+ }
+
+ /* do recurrence expansion if not keeping anyway */
+ if (!$keep_event && $event->recurs()) {
+ $next = $event->recurrence->nextRecurrence($startDate);
+ while ($next !== false &&
+ $event->recurrence->hasException($next->year, $next->month, $next->mday)) {
+ $next->mday++;
+ $next = $event->recurrence->nextRecurrence($next);
+ }
+
+ if ($next !== false) {
+ $duration = $next->timestamp() - $event->start->timestamp();
+ $next_end = new Horde_Date($event->end->timestamp() + $duration);
+
+ if ((!(($endDate->compareDateTime($next) < 0) ||
+ ($startDate->compareDateTime($next_end) > 0)))) {
+ $keep_event = true;
+ }
+ }
+ }
+
+ if ($keep_event) {
+ $ids[$event->getUID()] = $event->getUID();
+ }
+ }
+
+ return $ids;
+ }
+
+ public function getEvent($eventId = null)
+ {
+ if (is_null($eventId)) {
+ return new Kronolith_Event_Kolab($this);
+ }
+
+ $result = $this->synchronize();
+ if (is_a($result, 'PEAR_Error')) {
+ return $result;
+ }
+
+ if (array_key_exists($eventId, $this->_events_cache)) {
+ return $this->_events_cache[$eventId];
+ }
+
+ return PEAR::raiseError(sprintf(_("Event not found: %s"), $eventId));
+ }
+
+ /**
+ * Get an event or events with the given UID value.
+ *
+ * @param string $uid The UID to match
+ * @param array $calendars A restricted array of calendar ids to search
+ * @param boolean $getAll Return all matching events? If this is false,
+ * an error will be returned if more than one event is found.
+ *
+ * @return Kronolith_Event
+ */
+ public function getByUID($uid, $calendars = null, $getAll = false)
+ {
+ if (!is_array($calendars)) {
+ $calendars = array_keys(Kronolith::listCalendars(true, PERMS_READ));
+ }
+
+ foreach ($calendars as $calendar) {
+ $this->open($calendar);
+ $this->synchronize();
+
+ if (!array_key_exists($uid, $this->_events_cache)) {
+ continue;
+ }
+
+ // Ok, found event
+ $event = $this->_events_cache[$uid];
+
+ if ($getAll) {
+ $events = array();
+ $events[] = $event;
+ return $events;
+ } else {
+ return $event;
+ }
+ }
+
+ return PEAR::raiseError(sprintf(_("Event not found: %s"), $uid));
+ }
+
+ /**
+ * Saves an event in the backend.
+ * If it is a new event, it is added, otherwise the event is updated.
+ *
+ * @param Kronolith_Event $event The event to save.
+ *
+ * @return mixed UID on success, a PEAR error otherwise
+ */
+ public function saveEvent(&$event)
+ {
+ $result = $this->synchronize();
+ if (is_a($result, 'PEAR_Error')) {
+ return $result;
+ }
+
+ $uid = $event->getUID();
+ if ($uid == null) {
+ $event->setUID($this->_store->generateUID());
+ }
+
+ $attributes = $event->toDriver();
+
+ $edit = false;
+ $stored_uid = null;
+ if ($event->isStored() || $event->exists()) {
+ $stored_uid = $attributes['uid'];
+ $action = array('action' => 'modify');
+ $edit = true;
+ } else {
+ $action = array('action' => 'add');
+ }
+
+ $result = $this->_store->save($attributes, $stored_uid);
+ if (is_a($result, 'PEAR_Error')) {
+ return $result;
+ }
+
+ /* Deal with tags */
+ $tagger = Kronolith::getTagger();
+ if (!empty($edit)) {
+ $tagger->replaceTags($event->getUID(), $event->tags, 'event');
+ } else {
+ $tagger->tag($event->getUID(), $event->tags, 'event');
+ }
+
+ /* Notify about the changed event. */
+ $result = Kronolith::sendNotification($event, $edit ? 'edit' : 'add');
+ if (is_a($result, 'PEAR_Error')) {
+ Horde::logMessage($result, __FILE__, __LINE__, PEAR_LOG_ERR);
+ }
+
+ /* Log the creation/modification of this item in the history log. */
+ $history = Horde_History::singleton();
+ $history->log('kronolith:' . $event->getCalendar() . ':' . $event->getUID(), $action, true);
+
+ // refresh IMAP cache
+ $this->synchronize(true);
+
+ if (is_callable('Kolab', 'triggerFreeBusyUpdate')) {
+ Kolab::triggerFreeBusyUpdate($this->_store->parseFolder($event->getCalendar()));
+ }
+
+ return $event->getUID();
+ }
+
+ /**
+ * Move an event to a new calendar.
+ *
+ * @param string $eventId The event to move.
+ * @param string $newCalendar The new calendar.
+ */
+ public function move($eventId, $newCalendar)
+ {
+ $event = $this->getEvent($eventId);
+
+ $result = $this->synchronize();
+ if (is_a($result, 'PEAR_Error')) {
+ return $result;
+ }
+
+ global $kronolith_shares;
+ $target = $kronolith_shares->getShare($newCalendar);
+ $folder = $target->get('folder');
+
+ $result = $this->_store->move($eventId, $folder);
+ if ($result) {
+ unset($this->_events_cache[$eventId]);
+ }
+
+ if (is_callable('Kolab', 'triggerFreeBusyUpdate')) {
+ Kolab::triggerFreeBusyUpdate($this->_store->parseFolder($this->_calendar));
+ Kolab::triggerFreeBusyUpdate($this->_store->parseFolder($newCalendar));
+ }
+
+ /* Log the moving of this item in the history log. */
+ $uid = $event->getUID();
+ $history = Horde_History::singleton();
+ $history->log('kronolith:' . $event->getCalendar() . ':' . $uid, array('action' => 'delete'), true);
+ $history->log('kronolith:' . $newCalendar . ':' . $uid, array('action' => 'add'), true);
+
+ return $result;
+ }
+
+ /**
+ * Delete a calendar and all its events.
+ *
+ * @param string $calendar The name of the calendar to delete.
+ *
+ * @return mixed True or a PEAR_Error on failure.
+ */
+ public function delete($calendar)
+ {
+ $this->open($calendar);
+ $result = $this->synchronize();
+ if (is_a($result, 'PEAR_Error')) {
+ return $result;
+ }
+
+ $result = $this->_store->deleteAll($calendar);
+ if (is_callable('Kolab', 'triggerFreeBusyUpdate')) {
+ Kolab::triggerFreeBusyUpdate($this->_store->parseFolder($calendar));
+ }
+ return true;
+ }
+
+ /**
+ * Rename a calendar.
+ *
+ * @param string $from The current name of the calendar.
+ * @param string $to The new name of the calendar.
+ *
+ * @return mixed True or a PEAR_Error on failure.
+ */
+ public function rename($from, $to)
+ {
+ // FIXME: We can't do much here. We'd need to be called after
+ // renaming the share here. This needs to be checked again.
+ // kolab/issue2249 ([horde/kronolith] Kronolith is unable to
+ // trigger a free/busy update on a folder rename)
+ return true;
+ }
+
+ /**
+ * Delete an event.
+ *
+ * @param string $eventId The ID of the event to delete.
+ *
+ * @return mixed True or a PEAR_Error on failure.
+ */
+ public function deleteEvent($eventId, $silent = false)
+ {
+ $result = $this->synchronize();
+ if (is_a($result, 'PEAR_Error')) {
+ return $result;
+ }
+
+ if (!$this->_store->objectUidExists($eventId)) {
+ return PEAR::raiseError(sprintf(_("Event not found: %s"), $eventId));
+ }
+
+ $event = $this->getEvent($eventId);
+
+ if ($this->_store->delete($eventId)) {
+ // Notify about the deleted event.
+ if (!$silent) {
+ $result = Kronolith::sendNotification($event, 'delete');
+ if (is_a($result, 'PEAR_Error')) {
+ Horde::logMessage($result, __FILE__, __LINE__, PEAR_LOG_ERR);
+ }
+ }
+
+ /* Log the deletion of this item in the history log. */
+ $history = Horde_History::singleton();
+ $history->log('kronolith:' . $event->getCalendar() . ':' . $event->getUID(), array('action' => 'delete'), true);
+
+ if (is_callable('Kolab', 'triggerFreeBusyUpdate')) {
+ Kolab::triggerFreeBusyUpdate($this->_store->parseFolder($event->getCalendar()));
+ }
+
+ unset($this->_events_cache[$eventId]);
+ } else {
+ return PEAR::raiseError(sprintf(_("Cannot delete event: %s"), $eventId));
+ }
+
+ return true;
+ }
+
+}
--- /dev/null
+<?php
+/**
+ * The Kronolith_Driver_Sql class implements the Kronolith_Driver API for a
+ * SQL backend.
+ *
+ * Copyright 1999-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * @author Luc Saillard <luc.saillard@fr.alcove.com>
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ * @author Jan Schneider <jan@horde.org>
+ * @package Kronolith
+ */
+class Kronolith_Driver_Sql extends Kronolith_Driver
+{
+ /**
+ * The object handle for the current database connection.
+ *
+ * @var DB
+ */
+ private $_db;
+
+ /**
+ * Handle for the current database connection, used for writing. Defaults
+ * to the same handle as $_db if a separate write database is not required.
+ *
+ * @var DB
+ */
+ private $_write_db;
+
+ /**
+ * Cache events as we fetch them to avoid fetching the same event from the
+ * DB twice.
+ *
+ * @var array
+ */
+ private $_cache = array();
+
+ public function listAlarms($date, $fullevent = false)
+ {
+ require_once 'Date/Calc.php';
+
+ $allevents = $this->listEvents($date, null, true);
+ if (is_a($allevents, 'PEAR_Error')) {
+ return $allevents;
+ }
+
+ $events = array();
+ foreach ($allevents as $eventId) {
+ $event = $this->getEvent($eventId);
+ if (is_a($event, 'PEAR_Error')) {
+ continue;
+ }
+
+ if (!$event->recurs()) {
+ $start = new Horde_Date($event->start);
+ $start->min -= $event->getAlarm();
+ if ($start->compareDateTime($date) <= 0 &&
+ $date->compareDateTime($event->end) <= -1) {
+ $events[] = $fullevent ? $event : $eventId;
+ }
+ } else {
+ if ($next = $event->recurrence->nextRecurrence($date)) {
+ if ($event->recurrence->hasException($next->year, $next->month, $next->mday)) {
+ continue;
+ }
+ $start = new Horde_Date($next);
+ $start->min -= $event->getAlarm();
+ $diff = Date_Calc::dateDiff($event->start->mday,
+ $event->start->month,
+ $event->start->year,
+ $event->end->mday,
+ $event->end->month,
+ $event->end->year);
+ if ($diff == -1) {
+ $diff = 0;
+ }
+ $end = new Horde_Date(array('year' => $next->year,
+ 'month' => $next->month,
+ 'mday' => $next->mday + $diff,
+ 'hour' => $event->end->hour,
+ 'min' => $event->end->min,
+ 'sec' => $event->end->sec));
+ if ($start->compareDateTime($date) <= 0 &&
+ $date->compareDateTime($end) <= -1) {
+ if ($fullevent) {
+ $event->start = $start;
+ $event->end = $end;
+ $events[] = $event;
+ } else {
+ $events[] = $eventId;
+ }
+ }
+ }
+ }
+ }
+
+ return is_array($events) ? $events : array();
+ }
+
+ public function search($query)
+ {
+ require_once 'Horde/SQL.php';
+
+ /* Build SQL conditions based on the query string. */
+ $cond = '((';
+ $values = array();
+
+ if (!empty($query->title)) {
+ $binds = Horde_SQL::buildClause($this->_db, 'event_title', 'LIKE', $this->convertToDriver($query->title), true);
+ if (is_array($binds)) {
+ $cond .= $binds[0] . ' AND ';
+ $values = array_merge($values, $binds[1]);
+ } else {
+ $cond .= $binds;
+ }
+ }
+ if (!empty($query->location)) {
+ $binds = Horde_SQL::buildClause($this->_db, 'event_location', 'LIKE', $this->convertToDriver($query->location), true);
+ if (is_array($binds)) {
+ $cond .= $binds[0] . ' AND ';
+ $values = array_merge($values, $binds[1]);
+ } else {
+ $cond .= $binds;
+ }
+ }
+ if (!empty($query->description)) {
+ $binds = Horde_SQL::buildClause($this->_db, 'event_description', 'LIKE', $this->convertToDriver($query->description), true);
+ if (is_array($binds)) {
+ $cond .= $binds[0] . ' AND ';
+ $values = array_merge($values, $binds[1]);
+ } else {
+ $cond .= $binds;
+ }
+ }
+ if (isset($query->status)) {
+ $binds = Horde_SQL::buildClause($this->_db, 'event_status', '=', $query->status, true);
+ if (is_array($binds)) {
+ $cond .= $binds[0] . ' AND ';
+ $values = array_merge($values, $binds[1]);
+ } else {
+ $cond .= $binds;
+ }
+ }
+
+ if (!empty($query->creatorID)) {
+ $binds = Horde_SQL::buildClause($this->_db, 'event_creator_id', '=', $query->creatorID, true);
+ if (is_array($binds)) {
+ $cond .= $binds[0] . ' AND ';
+ $values = array_merge($values, $binds[1]);
+ } else {
+ $cond .= $binds;
+ }
+ }
+
+ if ($cond == '((') {
+ $cond = '';
+ } else {
+ $cond = substr($cond, 0, strlen($cond) - 5) . '))';
+ }
+
+ $eventIds = $this->listEventsConditional($query->start,
+ empty($query->end)
+ ? new Horde_Date(array('mday' => 31, 'month' => 12, 'year' => 9999))
+ : $query->end,
+ $cond,
+ $values);
+ if (is_a($eventIds, 'PEAR_Error')) {
+ return $eventIds;
+ }
+
+ $events = array();
+ foreach ($eventIds as $eventId) {
+ $event = $this->getEvent($eventId);
+ if (is_a($event, 'PEAR_Error')) {
+ return $event;
+ }
+ $events[] = $event;
+ }
+
+ return $events;
+ }
+
+ /**
+ * Checks if the event's UID already exists and returns all event
+ * ids with that UID.
+ *
+ * @param string $uid The event's uid.
+ * @param string $calendar_id Calendar to search in.
+ *
+ * @return string|boolean Returns a string with event_id or false if
+ * not found.
+ */
+ public function exists($uid, $calendar_id = null)
+ {
+ $query = 'SELECT event_id FROM ' . $this->_params['table'] . ' WHERE event_uid = ?';
+ $values = array($uid);
+
+ if (!is_null($calendar_id)) {
+ $query .= ' AND calendar_id = ?';
+ $values[] = $calendar_id;
+ }
+
+ /* Log the query at a DEBUG log level. */
+ Horde::logMessage(sprintf('Kronolith_Driver_Sql::exists(): user = "%s"; query = "%s"',
+ Auth::getAuth(), $query),
+ __FILE__, __LINE__, PEAR_LOG_DEBUG);
+
+ $event = $this->_db->getRow($query, $values, DB_FETCHMODE_ASSOC);
+ if (is_a($event, 'PEAR_Error')) {
+ Horde::logMessage($result, __FILE__, __LINE__, PEAR_LOG_ERR);
+ return $event;
+ }
+
+ if ($event) {
+ return $event['event_id'];
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Lists all events in the time range, optionally restricting
+ * results to only events with alarms.
+ *
+ * @param Horde_Date $startInterval Start of range date object.
+ * @param Horde_Date $endInterval End of range data object.
+ * @param boolean $hasAlarm Only return events with alarms?
+ * Defaults to all events.
+ *
+ * @return array Events in the given time range.
+ */
+ public function listEvents($startDate = null, $endDate = null,
+ $hasAlarm = false)
+ {
+ if (empty($endDate)) {
+ $endInterval = new Horde_Date(array('mday' => 31, 'month' => 12,
+ 'year' => 9999));
+ } else {
+ $endInterval = clone $endDate;
+ $endInterval->mday++;
+ }
+
+ $startInterval = null;
+ if (empty($startDate)) {
+ $startInterval = new Horde_Date(array('mday' => 1, 'month' => 1,
+ 'year' => 0000));
+ } else {
+ $startInterval = clone $startDate;
+ }
+
+ return $this->listEventsConditional($startInterval, $endInterval,
+ $hasAlarm ? 'event_alarm > ?' : '',
+ $hasAlarm ? array(0) : array());
+ }
+
+ /**
+ * Lists all events that satisfy the given conditions.
+ *
+ * @param Horde_Date $startInterval Start of range date object.
+ * @param Horde_Date $endInterval End of range data object.
+ * @param string $conditions Conditions, given as SQL clauses.
+ * @param array $vals SQL bind variables for use with
+ * $conditions clauses.
+ *
+ * @return array Events in the given time range satisfying the given
+ * conditions.
+ */
+ public function listEventsConditional($startInterval, $endInterval,
+ $conditions = '', $vals = array())
+ {
+ $q = 'SELECT event_id, event_uid, event_description, event_location,' .
+ ' event_private, event_status, event_attendees,' .
+ ' event_title, event_recurcount,' .
+ ' event_recurtype, event_recurenddate, event_recurinterval,' .
+ ' event_recurdays, event_start, event_end, event_allday,' .
+ ' event_alarm, event_alarm_methods, event_modified,' .
+ ' event_exceptions, event_creator_id' .
+ ' FROM ' . $this->_params['table'] .
+ ' WHERE calendar_id = ? AND ((';
+ $values = array($this->_calendar);
+
+ if ($conditions) {
+ $q .= $conditions . ')) AND ((';
+ $values = array_merge($values, $vals);
+ }
+
+ $etime = $endInterval->format('Y-m-d H:i:s');
+ $stime = null;
+ if (isset($startInterval)) {
+ $stime = $startInterval->format('Y-m-d H:i:s');
+ $q .= 'event_end > ? AND ';
+ $values[] = $stime;
+ }
+ $q .= 'event_start < ?) OR (';
+ $values[] = $etime;
+ if (isset($stime)) {
+ $q .= 'event_recurenddate >= ? AND ';
+ $values[] = $stime;
+ }
+ $q .= 'event_start <= ?' .
+ ' AND event_recurtype <> ?))';
+ array_push($values, $etime, Horde_Date_Recurrence::RECUR_NONE);
+
+ /* Log the query at a DEBUG log level. */
+ Horde::logMessage(sprintf('Kronolith_Driver_Sql::listEventsConditional(): user = "%s"; query = "%s"; values = "%s"',
+ Auth::getAuth(), $q, implode(',', $values)),
+ __FILE__, __LINE__, PEAR_LOG_DEBUG);
+
+ /* Run the query. */
+ $qr = $this->_db->query($q, $values);
+ if (is_a($qr, 'PEAR_Error')) {
+ Horde::logMessage($qr, __FILE__, __LINE__, PEAR_LOG_ERR);
+ return $qr;
+ }
+
+ $events = array();
+ $row = $qr->fetchRow(DB_FETCHMODE_ASSOC);
+ while ($row && !is_a($row, 'PEAR_Error')) {
+ /* If the event did not have a UID before, we need to give
+ * it one. */
+ if (empty($row['event_uid'])) {
+ $row['event_uid'] = $this->generateUID();
+
+ /* Save the new UID for data integrity. */
+ $query = 'UPDATE ' . $this->_params['table'] . ' SET event_uid = ? WHERE event_id = ?';
+ $values = array($row['event_uid'], $row['event_id']);
+
+ /* Log the query at a DEBUG log level. */
+ Horde::logMessage(sprintf('Kronolith_Driver_Sql::listEventsConditional(): user = %s; query = "%s"; values = "%s"',
+ Auth::getAuth(), $query, implode(',', $values)),
+ __FILE__, __LINE__, PEAR_LOG_DEBUG);
+
+ $result = $this->_write_db->query($query, $values);
+ if (is_a($result, 'PEAR_Error')) {
+ Horde::logMessage($result, __FILE__, __LINE__, PEAR_LOG_ERR);
+ }
+ }
+
+ /* We have all the information we need to create an event
+ * object for this event, so go ahead and cache it. */
+ $this->_cache[$this->_calendar][$row['event_id']] = new Kronolith_Event_Sql($this, $row);
+ if ($row['event_recurtype'] == Horde_Date_Recurrence::RECUR_NONE) {
+ $events[$row['event_uid']] = $row['event_id'];
+ } else {
+ $next = $this->nextRecurrence($row['event_id'], $startInterval);
+ if ($next && $next->compareDate($endInterval) < 0) {
+ $events[$row['event_uid']] = $row['event_id'];
+ }
+ }
+
+ $row = $qr->fetchRow(DB_FETCHMODE_ASSOC);
+ }
+
+ return $events;
+ }
+
+ public function getEvent($eventId = null)
+ {
+ if (is_null($eventId)) {
+ return new Kronolith_Event_Sql($this);
+ }
+
+ if (isset($this->_cache[$this->_calendar][$eventId])) {
+ return $this->_cache[$this->_calendar][$eventId];
+ }
+
+ $query = 'SELECT event_id, event_uid, event_description,' .
+ ' event_location, event_private, event_status, event_attendees,' .
+ ' event_title, event_recurcount,' .
+ ' event_recurtype, event_recurenddate, event_recurinterval,' .
+ ' event_recurdays, event_start, event_end, event_allday,' .
+ ' event_alarm, event_alarm_methods, event_modified,' .
+ ' event_exceptions, event_creator_id' .
+ ' FROM ' . $this->_params['table'] . ' WHERE event_id = ? AND calendar_id = ?';
+ $values = array($eventId, $this->_calendar);
+
+ /* Log the query at a DEBUG log level. */
+ Horde::logMessage(sprintf('Kronolith_Driver_Sql::getEvent(): user = "%s"; query = "%s"; values = "%s"',
+ Auth::getAuth(), $query, implode(',', $values)),
+ __FILE__, __LINE__, PEAR_LOG_DEBUG);
+
+ $event = $this->_db->getRow($query, $values, DB_FETCHMODE_ASSOC);
+ if (is_a($event, 'PEAR_Error')) {
+ Horde::logMessage($event, __FILE__, __LINE__, PEAR_LOG_ERR);
+ return $event;
+ }
+
+ if ($event) {
+ $this->_cache[$this->_calendar][$eventId] = new Kronolith_Event_Sql($this, $event);
+ return $this->_cache[$this->_calendar][$eventId];
+ } else {
+ return PEAR::raiseError(_("Event not found"));
+ }
+ }
+
+ /**
+ * Get an event or events with the given UID value.
+ *
+ * @param string $uid The UID to match
+ * @param array $calendars A restricted array of calendar ids to search
+ * @param boolean $getAll Return all matching events? If this is false,
+ * an error will be returned if more than one event is found.
+ *
+ * @return Kronolith_Event
+ */
+ public function getByUID($uid, $calendars = null, $getAll = false)
+ {
+ $query = 'SELECT event_id, event_uid, calendar_id, event_description,' .
+ ' event_location, event_private, event_status, event_attendees,' .
+ ' event_title, event_recurcount,' .
+ ' event_recurtype, event_recurenddate, event_recurinterval,' .
+ ' event_recurdays, event_start, event_end, event_allday,' .
+ ' event_alarm, event_alarm_methods, event_modified,' .
+ ' event_exceptions, event_creator_id' .
+ ' FROM ' . $this->_params['table'] . ' WHERE event_uid = ?';
+ $values = array($uid);
+
+ /* Optionally filter by calendar */
+ if (!is_null($calendars)) {
+ if (!count($calendars)) {
+ return PEAR::raiseError(_("No calendars to search"));
+ }
+ $query .= ' AND calendar_id IN (?' . str_repeat(', ?', count($calendars) - 1) . ')';
+ $values = array_merge($values, $calendars);
+ }
+
+ /* Log the query at a DEBUG log level. */
+ Horde::logMessage(sprintf('Kronolith_Driver_Sql::getByUID(): user = "%s"; query = "%s"; values = "%s"',
+ Auth::getAuth(), $query, implode(',', $values)),
+ __FILE__, __LINE__, PEAR_LOG_DEBUG);
+
+ $events = $this->_db->getAll($query, $values, DB_FETCHMODE_ASSOC);
+ if (is_a($events, 'PEAR_Error')) {
+ Horde::logMessage($events, __FILE__, __LINE__, PEAR_LOG_ERR);
+ return $events;
+ }
+ if (!count($events)) {
+ return PEAR::raiseError($uid . ' not found');
+ }
+
+ $eventArray = array();
+ foreach ($events as $event) {
+ $this->open($event['calendar_id']);
+ $this->_cache[$this->_calendar][$event['event_id']] = new Kronolith_Event_Sql($this, $event);
+ $eventArray[] = $this->_cache[$this->_calendar][$event['event_id']];
+ }
+
+ if ($getAll) {
+ return $eventArray;
+ }
+
+ /* First try the user's own calendars. */
+ $ownerCalendars = Kronolith::listCalendars(true, PERMS_READ);
+ $event = null;
+ foreach ($eventArray as $ev) {
+ if (isset($ownerCalendars[$ev->getCalendar()])) {
+ $event = $ev;
+ break;
+ }
+ }
+
+ /* If not successful, try all calendars the user has access too. */
+ if (empty($event)) {
+ $readableCalendars = Kronolith::listCalendars(false, PERMS_READ);
+ foreach ($eventArray as $ev) {
+ if (isset($readableCalendars[$ev->getCalendar()])) {
+ $event = $ev;
+ break;
+ }
+ }
+ }
+
+ if (empty($event)) {
+ $event = $eventArray[0];
+ }
+
+ return $event;
+ }
+
+ /**
+ * Saves an event in the backend.
+ * If it is a new event, it is added, otherwise the event is updated.
+ *
+ * @param Kronolith_Event $event The event to save.
+ */
+ public function saveEvent($event)
+ {
+ if ($event->isStored() || $event->exists()) {
+ $values = array();
+
+ $query = 'UPDATE ' . $this->_params['table'] . ' SET ';
+
+ foreach ($event->getProperties() as $key => $val) {
+ $query .= " $key = ?,";
+ $values[] = $val;
+ }
+ $query = substr($query, 0, -1);
+ $query .= ' WHERE event_id = ?';
+ $values[] = $event->getId();
+
+ /* Log the query at a DEBUG log level. */
+ Horde::logMessage(sprintf('Kronolith_Driver_Sql::saveEvent(): user = "%s"; query = "%s"; values = "%s"',
+ Auth::getAuth(), $query, implode(',', $values)),
+ __FILE__, __LINE__, PEAR_LOG_DEBUG);
+
+ $result = $this->_write_db->query($query, $values);
+ if (is_a($result, 'PEAR_Error')) {
+ Horde::logMessage($result, __FILE__, __LINE__, PEAR_LOG_ERR);
+ return $result;
+ }
+
+ /* Log the modification of this item in the history log. */
+ if ($event->getUID()) {
+ $history = Horde_History::singleton();
+ $history->log('kronolith:' . $this->_calendar . ':' . $event->getUID(), array('action' => 'modify'), true);
+ }
+
+ /* Update tags */
+ $tagger = Kronolith::getTagger();
+ $tagger->replaceTags($event->getUID(), $event->tags, 'event');
+
+ /* Notify users about the changed event. */
+ $result = Kronolith::sendNotification($event, 'edit');
+ if (is_a($result, 'PEAR_Error')) {
+ Horde::logMessage($result, __FILE__, __LINE__, PEAR_LOG_ERR);
+ }
+
+ return $event->getId();
+ } else {
+ if ($event->getId()) {
+ $id = $event->getId();
+ } else {
+ $id = hash('md5', uniqid(mt_rand(), true));
+ $event->setId($id);
+ }
+
+ if ($event->getUID()) {
+ $uid = $event->getUID();
+ } else {
+ $uid = $this->generateUID();
+ $event->setUID($uid);
+ }
+
+ $query = 'INSERT INTO ' . $this->_params['table'];
+ $cols_name = ' (event_id, event_uid,';
+ $cols_values = ' VALUES (?, ?,';
+ $values = array($id, $uid);
+
+ foreach ($event->getProperties() as $key => $val) {
+ $cols_name .= " $key,";
+ $cols_values .= ' ?,';
+ $values[] = $val;
+ }
+
+ $cols_name .= ' calendar_id)';
+ $cols_values .= ' ?)';
+ $values[] = $this->_calendar;
+
+ $query .= $cols_name . $cols_values;
+
+ /* Log the query at a DEBUG log level. */
+ Horde::logMessage(sprintf('Kronolith_Driver_Sql::saveEvent(): user = "%s"; query = "%s"; values = "%s"',
+ Auth::getAuth(), $query, implode(',', $values)),
+ __FILE__, __LINE__, PEAR_LOG_DEBUG);
+
+ $result = $this->_write_db->query($query, $values);
+ if (is_a($result, 'PEAR_Error')) {
+ Horde::logMessage($result, __FILE__, __LINE__, PEAR_LOG_ERR);
+ return $result;
+ }
+
+ /* Log the creation of this item in the history log. */
+ $history = Horde_History::singleton();
+ $history->log('kronolith:' . $this->_calendar . ':' . $uid, array('action' => 'add'), true);
+
+ /* Deal with any tags */
+ $tagger = Kronolith::getTagger();
+ $tagger->tag($event->getUID(), $event->tags, 'event');
+
+ /* Notify users about the new event. */
+ $result = Kronolith::sendNotification($event, 'add');
+ if (is_a($result, 'PEAR_Error')) {
+ Horde::logMessage($result, __FILE__, __LINE__, PEAR_LOG_ERR);
+ }
+
+ return $id;
+ }
+ }
+
+ /**
+ * Move an event to a new calendar.
+ *
+ * @param string $eventId The event to move.
+ * @param string $newCalendar The new calendar.
+ */
+ public function move($eventId, $newCalendar)
+ {
+ /* Fetch the event for later use. */
+ $event = $this->getEvent($eventId);
+ if (is_a($event, 'PEAR_Error')) {
+ return $event;
+ }
+
+ $query = 'UPDATE ' . $this->_params['table'] . ' SET calendar_id = ? WHERE calendar_id = ? AND event_id = ?';
+ $values = array($newCalendar, $this->_calendar, $eventId);
+
+ /* Log the query at a DEBUG log level. */
+ Horde::logMessage(sprintf('Kronolith_Driver_Sql::move(): %s; values = "%s"',
+ $query, implode(',', $values)),
+ __FILE__, __LINE__, PEAR_LOG_DEBUG);
+
+ /* Attempt the move query. */
+ $result = $this->_write_db->query($query, $values);
+ if (is_a($result, 'PEAR_Error')) {
+ Horde::logMessage($result, __FILE__, __LINE__, PEAR_LOG_ERR);
+ return $result;
+ }
+
+ /* Log the moving of this item in the history log. */
+ $uid = $event->getUID();
+ if ($uid) {
+ $history = Horde_History::singleton();
+ $history->log('kronolith:' . $this->_calendar . ':' . $uid, array('action' => 'delete'), true);
+ $history->log('kronolith:' . $newCalendar . ':' . $uid, array('action' => 'add'), true);
+ }
+
+ return true;
+ }
+
+ /**
+ * Delete a calendar and all its events.
+ *
+ * @param string $calendar The name of the calendar to delete.
+ *
+ * @return mixed True or a PEAR_Error on failure.
+ */
+ public function delete($calendar)
+ {
+ $query = 'DELETE FROM ' . $this->_params['table'] . ' WHERE calendar_id = ?';
+ $values = array($calendar);
+
+ /* Log the query at a DEBUG log level. */
+ Horde::logMessage(sprintf('Kronolith_Driver_Sql::delete(): user = "%s"; query = "%s"; values = "%s"',
+ Auth::getAuth(), $query, implode(',', $values)),
+ __FILE__, __LINE__, PEAR_LOG_DEBUG);
+
+ return $this->_write_db->query($query, $values);
+ }
+
+ /**
+ * Delete an event.
+ *
+ * @param string $eventId The ID of the event to delete.
+ * @param boolean $silent Don't send notifications, used when deleting
+ * events in bulk from maintenance tasks.
+ *
+ * @return mixed True or a PEAR_Error on failure.
+ */
+ public function deleteEvent($eventId, $silent = false)
+ {
+ /* Fetch the event for later use. */
+ $event = $this->getEvent($eventId);
+ if (is_a($event, 'PEAR_Error')) {
+ return $event;
+ }
+
+ $query = 'DELETE FROM ' . $this->_params['table'] . ' WHERE event_id = ? AND calendar_id = ?';
+ $values = array($eventId, $this->_calendar);
+
+ /* Log the query at a DEBUG log level. */
+ Horde::logMessage(sprintf('Kronolith_Driver_Sql::deleteEvent(): user = "%s"; query = "%s"; values = "%s"',
+ Auth::getAuth(), $query, implode(',', $values)),
+ __FILE__, __LINE__, PEAR_LOG_DEBUG);
+
+ $result = $this->_write_db->query($query, $values);
+ if (is_a($result, 'PEAR_Error')) {
+ Horde::logMessage($result, __FILE__, __LINE__, PEAR_LOG_ERR);
+ return $result;
+ }
+
+ /* Log the deletion of this item in the history log. */
+ if ($event->getUID()) {
+ $history = Horde_History::singleton();
+ $history->log('kronolith:' . $this->_calendar . ':' . $event->getUID(), array('action' => 'delete'), true);
+ }
+
+ /* Remove any pending alarms. */
+ if (@include_once 'Horde/Alarm.php') {
+ $alarm = Horde_Alarm::factory();
+ $alarm->delete($event->getUID());
+ }
+
+ /* Remove any tags */
+ $tagger = Kronolith::getTagger();
+ $tagger->replaceTags($event->getUID(), array(), 'event');
+
+ /* Notify about the deleted event. */
+ if (!$silent) {
+ $result = Kronolith::sendNotification($event, 'delete');
+ if (is_a($result, 'PEAR_Error')) {
+ Horde::logMessage($result, __FILE__, __LINE__, PEAR_LOG_ERR);
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Attempts to open a connection to the SQL server.
+ *
+ * @return boolean True.
+ */
+ public function initialize()
+ {
+ Horde::assertDriverConfig($this->_params, 'calendar',
+ array('phptype'));
+
+ if (!isset($this->_params['database'])) {
+ $this->_params['database'] = '';
+ }
+ if (!isset($this->_params['username'])) {
+ $this->_params['username'] = '';
+ }
+ if (!isset($this->_params['hostspec'])) {
+ $this->_params['hostspec'] = '';
+ }
+ if (!isset($this->_params['table'])) {
+ $this->_params['table'] = 'kronolith_events';
+ }
+
+ /* Connect to the SQL server using the supplied parameters. */
+ $this->_write_db = DB::connect($this->_params,
+ array('persistent' => !empty($this->_params['persistent']),
+ 'ssl' => !empty($this->_params['ssl'])));
+ if (is_a($this->_write_db, 'PEAR_Error')) {
+ return $this->_write_db;
+ }
+ $this->_initConn($this->_write_db);
+
+ /* Check if we need to set up the read DB connection
+ * seperately. */
+ if (!empty($this->_params['splitread'])) {
+ $params = array_merge($this->_params, $this->_params['read']);
+ $this->_db = DB::connect($params,
+ array('persistent' => !empty($params['persistent']),
+ 'ssl' => !empty($params['ssl'])));
+ if (is_a($this->_db, 'PEAR_Error')) {
+ return $this->_db;
+ }
+ $this->_initConn($this->_db);
+ } else {
+ /* Default to the same DB handle for the writer too. */
+ $this->_db = $this->_write_db;
+ }
+
+ return true;
+ }
+
+ /**
+ */
+ private function _initConn(&$db)
+ {
+ // Set DB portability options.
+ switch ($db->phptype) {
+ case 'mssql':
+ $db->setOption('portability', DB_PORTABILITY_LOWERCASE | DB_PORTABILITY_ERRORS | DB_PORTABILITY_RTRIM);
+ break;
+ default:
+ $db->setOption('portability', DB_PORTABILITY_LOWERCASE | DB_PORTABILITY_ERRORS);
+ }
+
+ /* Handle any database specific initialization code to run. */
+ switch ($db->dbsyntax) {
+ case 'oci8':
+ $query = "ALTER SESSION SET NLS_DATE_FORMAT = 'YYYY-MM-DD HH24:MI:SS'";
+
+ /* Log the query at a DEBUG log level. */
+ Horde::logMessage(sprintf('Kronolith_Driver_Sql::_initConn(): user = "%s"; query = "%s"',
+ Auth::getAuth(), $query),
+ __FILE__, __LINE__, PEAR_LOG_DEBUG);
+
+ $db->query($query);
+ break;
+
+ case 'pgsql':
+ $query = "SET datestyle TO 'iso'";
+
+ /* Log the query at a DEBUG log level. */
+ Horde::logMessage(sprintf('Kronolith_Driver_Sql::_initConn(): user = "%s"; query = "%s"',
+ Auth::getAuth(), $query),
+ __FILE__, __LINE__, PEAR_LOG_DEBUG);
+
+ $db->query($query);
+ break;
+ }
+ }
+
+ /**
+ * Converts a value from the driver's charset to the default
+ * charset.
+ *
+ * @param mixed $value A value to convert.
+ *
+ * @return mixed The converted value.
+ */
+ public function convertFromDriver($value)
+ {
+ return String::convertCharset($value, $this->_params['charset']);
+ }
+
+ /**
+ * Converts a value from the default charset to the driver's
+ * charset.
+ *
+ * @param mixed $value A value to convert.
+ *
+ * @return mixed The converted value.
+ */
+ public function convertToDriver($value)
+ {
+ return String::convertCharset($value, NLS::getCharset(), $this->_params['charset']);
+ }
+
+ /**
+ * Remove all events owned by the specified user in all calendars.
+ *
+ *
+ * @param string $user The user name to delete events for.
+ *
+ * @param mixed True | PEAR_Error
+ */
+ public function removeUserData($user)
+ {
+ if (!Auth::isAdmin()) {
+ return PEAR::raiseError(_("Permission Denied"));
+ }
+
+ $shares = $GLOBALS['kronolith_shares']->listShares($user, PERMS_EDIT);
+ if (is_a($shares, 'PEAR_Error')) {
+ return $shares;
+ }
+
+ foreach (array_keys($shares) as $calendar) {
+ $ids = Kronolith::listEventIds(null, null, $calendar);
+ if (is_a($ids, 'PEAR_Error')) {
+ return $ids;
+ }
+ $uids = array();
+ foreach ($ids as $cal) {
+ $uids = array_merge($uids, array_keys($cal));
+ }
+
+ foreach ($uids as $uid) {
+ $event = $this->getByUID($uid);
+ if (is_a($event, 'PEAR_Error')) {
+ return $event;
+ }
+
+ $this->deleteEvent($event->getId());
+ }
+ }
+ return true;
+ }
+}
+++ /dev/null
-<?php
-/**
- * The Kronolith_Driver_holidays implements support for the PEAR package
- * Date_Holidays.
- *
- * @see http://pear.php.net/packages/Date_Holidays
- * @author Stephan Hohmann <webmaster@dasourcerer.net>
- * @package Kronolith
- */
-
-class Kronolith_Driver_holidays extends Kronolith_Driver
-{
-
- public function listAlarms($date, $fullevent = false)
- {
- return array();
- }
-
- /**
- * Returns a list of all holidays occuring between <code>$startDate</code>
- * and <code>$endDate</code>.
- *
- * @param int|Horde_Date $startDate The start of the datespan to be
- * checked. Defaults to the current date.
- * @param int|Horde_Date $endDate The end of the datespan. Defaults to
- * the current date.
- * @param bool $hasAlarm Left in for compatibility reasons and
- * has no effect on this function.
- * Defaults to <code>false</code>
- *
- * @return array An array of all holidays within the given datespan.
- */
- public function listEvents($startDate = null, $endDate = null, $hasAlarm = false)
- {
- global $language;
-
- $events = array();
-
- if (is_null($startDate)) {
- $startDate = new Horde_Date($_SERVER['REQUEST_TIME']);
- }
-
- if (is_null($endDate)) {
- $endDate = new Horde_Date($_SERVER['REQUEST_TIME']);
- }
-
- Date_Holidays::staticSetProperty('DIE_ON_MISSING_LOCALE', false);
- foreach (unserialize($GLOBALS['prefs']->getValue('holiday_drivers')) as $driver) {
- for ($year = $startDate->year; $year <= $endDate->year; $year++) {
- $dh = Date_Holidays::factory($driver, $year, $language);
- if (Date_Holidays::isError($dh)) {
- Horde::logMessage(sprintf('Factory was unable to produce driver object for driver %s in year %s with locale %s',
- $driver, $year, $language),
- __FILE__, __LINE__, PEAR_LOG_ERR);
- continue;
- }
- $dh->addTranslation($language);
- $events = array_merge($events, $this->_getEvents($dh, $startDate, $endDate));
- }
- }
-
- return $events;
- }
-
- private function _getEvents($dh, $startDate, $endDate)
- {
- $events = array();
- for ($date = new Horde_Date($startDate);
- $date->compareDate($endDate) <= 0;
- $date->mday++) {
- $holidays = $dh->getHolidayForDate($date->format('Y-m-d'), null, true);
- if (Date_Holidays::isError($holidays)) {
- Horde::logMessage(sprintf('Unable to retrieve list of holidays from %s to %s',
- (string)$startDate, (string)$endDate), __FILE__, __LINE__);
- continue;
- }
-
- if (is_null($holidays)) {
- continue;
- }
-
- foreach ($holidays as $holiday) {
- $event = &new Kronolith_Event_holidays($this);
- $event->fromDriver($holiday);
- $events[] = $event;
- }
- }
- return $events;
- }
-
- private function _getTranslationFile($driver)
- {
- static $data_dir;
- if (!isset($data_dir)) {
- include_once 'PEAR/Config.php';
- $pear_config = new PEAR_Config();
- $data_dir = $pear_config->get('data_dir');
- }
- if (empty($data_dir)) {
- return;
- }
-
- foreach (array('', '_' . $driver) as $pkg_ext) {
- foreach (array('ser', 'xml') as $format) {
- $location = $data_dir . '/Date_Holidays' . $pkg_ext . '/lang/'
- . $driver . '/' . $GLOBALS['language'] . '.' . $format;
- if (file_exists($location)) {
- return array($format, $location);
- }
- }
- }
-
- return array(null, null);
- }
-
-}
-
-class Kronolith_Event_holidays extends Kronolith_Event
-{
- /**
- * The status of this event.
- *
- * @var integer
- */
- public $status = Kronolith::STATUS_FREE;
-
- /**
- * Whether this is an all-day event.
- *
- * @var boolean
- */
- public $allday = true;
-
- /**
- * Parse in an event from the driver.
- *
- * @param Date_Holidays_Holiday $dhEvent A holiday returned
- * from the driver
- */
- public function fromDriver($dhEvent)
- {
- $this->stored = true;
- $this->initialized = true;
- $this->setTitle(String::convertCharset($dhEvent->getTitle(), 'UTF-8'));
- $this->setId($dhEvent->getInternalName());
-
- $this->start = new Horde_Date($dhEvent->_date->getTime());
- $this->end = new Horde_Date($this->start);
- $this->end->mday++;
- }
-
- /**
- * Return this events title.
- *
- * @return string The title of this event
- */
- public function getTitle()
- {
- return $this->title;
- }
-
- /**
- * Is this event an all-day event?
- *
- * Since there are no holidays lasting only a few hours, this is always
- * true.
- *
- * @return boolean <code>true</code>
- */
- public function isAllDay()
- {
- return true;
- }
-
-}
+++ /dev/null
-<?php
-/**
- * The Kronolith_Driver_ical:: class implements the Kronolith_Driver
- * API for iCalendar data.
- *
- * Possible driver parameters:
- * - url: The location of the remote calendar.
- * - proxy: A hash with HTTP proxy information.
- * - user: The user name for HTTP Basic Authentication.
- * - password: The password for HTTP Basic Authentication.
- *
- * @author Chuck Hagenbuch <chuck@horde.org>
- * @author Jan Schneider <jan@horde.org>
- * @package Kronolith
- */
-class Kronolith_Driver_ical extends Kronolith_Driver
-{
- /**
- * Cache events as we fetch them to avoid fetching or parsing the same
- * event twice.
- *
- * @var array
- */
- private $_cache = array();
-
- public function listAlarms($date, $fullevent = false)
- {
- return array();
- }
-
- /**
- * Lists all events in the time range, optionally restricting
- * results to only events with alarms.
- *
- * @param Horde_Date $startInterval Start of range date object.
- * @param Horde_Date $endInterval End of range data object.
- * @param boolean $hasAlarm Only return events with alarms?
- * Defaults to all events.
- *
- * @return array Events in the given time range.
- */
- public function listEvents($startDate = null, $endDate = null,
- $hasAlarm = false)
- {
- $data = $this->_getRemoteCalendar();
- if (is_a($data, 'PEAR_Error')) {
- return $data;
- }
-
- $iCal = new Horde_iCalendar();
- if (!$iCal->parsevCalendar($data)) {
- return array();
- }
-
- $components = $iCal->getComponents();
- $events = array();
- $count = count($components);
- $exceptions = array();
- for ($i = 0; $i < $count; $i++) {
- $component = $components[$i];
- if ($component->getType() == 'vEvent') {
- $event = new Kronolith_Event_ical($this);
- $event->status = Kronolith::STATUS_FREE;
- $event->fromiCalendar($component);
- $event->remoteCal = $this->_params['url'];
- $event->eventID = $i;
-
- /* Catch RECURRENCE-ID attributes which mark single recurrence
- * instances. */
- $recurrence_id = $component->getAttribute('RECURRENCE-ID');
- if (is_int($recurrence_id) &&
- is_string($uid = $component->getAttribute('UID')) &&
- is_int($seq = $component->getAttribute('SEQUENCE'))) {
- $exceptions[$uid][$seq] = $recurrence_id;
- }
-
- /* Ignore events out of the period. */
- if (
- /* Starts after the period. */
- $event->start->compareDateTime($endDate) > 0 ||
- /* End before the period and doesn't recur. */
- (!$event->recurs() &&
- $event->end->compareDateTime($startDate) < 0) ||
- /* Recurs and ... */
- ($event->recurs() &&
- /* ... has a recurrence end before the period. */
- ($event->recurrence->hasRecurEnd() &&
- $event->recurrence->recurEnd->compareDateTime($startDate) < 0))) {
- continue;
- }
-
- $events[] = $event;
- }
- }
-
- /* Loop through all explicitly defined recurrence intances and create
- * exceptions for those in the event with the matchin recurrence. */
- foreach ($events as $key => $event) {
- if ($event->recurs() &&
- isset($exceptions[$event->getUID()][$event->getSequence()])) {
- $timestamp = $exceptions[$event->getUID()][$event->getSequence()];
- $events[$key]->recurrence->addException(date('Y', $timestamp), date('m', $timestamp), date('d', $timestamp));
- }
- }
-
- return $events;
- }
-
- public function getEvent($eventId = null)
- {
- $data = $this->_getRemoteCalendar();
- if (is_a($data, 'PEAR_Error')) {
- return $data;
- }
-
- $iCal = new Horde_iCalendar();
- if (!$iCal->parsevCalendar($data)) {
- return array();
- }
-
- $components = $iCal->getComponents();
- if (isset($components[$eventId]) &&
- $components[$eventId]->getType() == 'vEvent') {
- $event = new Kronolith_Event_ical($this);
- $event->status = Kronolith::STATUS_FREE;
- $event->fromiCalendar($components[$eventId]);
- $event->remoteCal = $this->_params['url'];
- $event->eventID = $eventId;
-
- return $event;
- }
-
- return false;
- }
-
- /**
- * Fetches a remote calendar into the session and return the data.
- *
- * @return mixed Either the calendar data, or an error on failure.
- */
- private function _getRemoteCalendar()
- {
- $url = trim($this->_params['url']);
-
- /* Treat webcal:// URLs as http://. */
- if (substr($url, 0, 9) == 'webcal://') {
- $url = str_replace('webcal://', 'http://', $url);
- }
-
- if (empty($_SESSION['kronolith']['remote'][$url])) {
- $options['method'] = 'GET';
- $options['timeout'] = 5;
- $options['allowRedirects'] = true;
-
- if (isset($this->_params['proxy'])) {
- $options = array_merge($options, $this->_params['proxy']);
- }
-
- $http = new HTTP_Request($url, $options);
- if (!empty($this->_params['user'])) {
- $http->setBasicAuth($this->_params['user'],
- $this->_params['password']);
- }
- @$http->sendRequest();
- if ($http->getResponseCode() != 200) {
- Horde::logMessage(sprintf('Failed to retrieve remote calendar: url = "%s", status = %s',
- $url, $http->getResponseCode()),
- __FILE__, __LINE__, PEAR_LOG_ERR);
- return PEAR::raiseError(sprintf(_("Could not open %s."), $url));
- }
- $_SESSION['kronolith']['remote'][$url] = $http->getResponseBody();
-
- /* Log fetch at DEBUG level. */
- Horde::logMessage(sprintf('Retrieved remote calendar for %s: url = "%s"',
- Auth::getAuth(), $url),
- __FILE__, __LINE__, PEAR_LOG_DEBUG);
- }
-
- return $_SESSION['kronolith']['remote'][$url];
- }
-
-}
-
-class Kronolith_Event_ical extends Kronolith_Event
-{
-
- public function fromDriver($vEvent)
- {
- $this->fromiCalendar($vEvent);
- $this->initialized = true;
- $this->stored = true;
- }
-
- public function toDriver()
- {
- return $this->toiCalendar();
- }
-
-}
+++ /dev/null
-<?php
-
-require_once 'Horde/Kolab.php';
-require_once 'Horde/Identity.php';
-
-/**
- * Horde Kronolith driver for the Kolab IMAP Server.
- *
- * Copyright 2004-2009 The Horde Project (http://www.horde.org/)
- *
- * See the enclosed file COPYING for license information (LGPL). If you
- * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
- *
- * @author Thomas Jarosch <thomas.jarosch@intra2net.com>
- * @author Gunnar Wrobel <wrobel@pardus.de>
- * @author Stuart Binge <omicron@mighty.co.za>
- * @package Kronolith
- */
-class Kronolith_Driver_kolab extends Kronolith_Driver
-{
- /**
- * Our Kolab server connection.
- *
- * @var Kolab
- */
- private $_kolab = null;
-
- /**
- * Internal cache of Kronolith_Event_kolab. eventID/UID is key
- *
- * @var array
- */
- private $_events_cache;
-
- /**
- * Indicates if we have synchronized this folder
- *
- * @var boolean
- */
- private $_synchronized;
-
- /**
- * Shortcut to the imap connection
- *
- * @var Kolab_IMAP
- */
- private $_store;
-
- /**
- * Attempts to open a Kolab Groupware folder.
- *
- * @return boolean True on success, PEAR_Error on failure.
- */
- public function initialize()
- {
- $this->_kolab = new Kolab();
- $this->reset();
- return true;
- }
-
- /**
- * Change current calendar
- */
- public function open($calendar)
- {
- if ($this->_calendar != $calendar) {
- $this->_calendar = $calendar;
- $this->reset();
- }
-
- return true;
- }
-
- /**
- * Reset internal variable on share change
- */
- public function reset()
- {
- $this->_events_cache = array();
- $this->_synchronized = false;
- }
-
- // We delay initial synchronization to the first use
- // so multiple calendars don't add to the total latency.
- // This function must be called before all internal driver functions
- public function synchronize($force = false)
- {
- if ($this->_synchronized && !$force) {
- return;
- }
-
- // Connect to the Kolab backend
- $result = $this->_kolab->open($this->_calendar, 1);
- if (is_a($result, 'PEAR_Error')) {
- return $result;
- }
- $this->_store = $this->_kolab->_storage;
-
- // build internal event cache
- $this->_events_cache = array();
- $events = $this->_store->getObjects();
- foreach ($events as $event) {
- $this->_events_cache[$event['uid']] = new Kronolith_Event_kolab($this, $event);
- }
-
- $this->_synchronized = true;
- }
-
- public function listAlarms($date, $fullevent = false)
- {
- $allevents = $this->listEvents($date, null, true);
- $events = array();
-
- foreach ($allevents as $eventId) {
- $event = $this->getEvent($eventId);
- if (is_a($event, 'PEAR_Error')) {
- return $event;
- }
-
- if (!$event->recurs()) {
- $start = new Horde_Date($event->start);
- $start->min -= $event->getAlarm();
- if ($start->compareDateTime($date) <= 0 &&
- $date->compareDateTime($event->end) <= -1) {
- $events[] = $fullevent ? $event : $eventId;
- }
- } else {
- if ($next = $event->recurrence->nextRecurrence($date)) {
- if ($event->recurrence->hasException($next->year, $next->month, $next->mday)) {
- continue;
- }
- $start = new Horde_Date($next);
- $start->min -= $event->getAlarm();
- $end = new Horde_Date(array('year' => $next->year,
- 'month' => $next->month,
- 'mday' => $next->mday,
- 'hour' => $event->end->hour,
- 'min' => $event->end->min,
- 'sec' => $event->end->sec));
- if ($start->compareDateTime($date) <= 0 &&
- $date->compareDateTime($end) <= -1) {
- if ($fullevent) {
- $event->start = $start;
- $event->end = $end;
- $events[] = $event;
- } else {
- $events[] = $eventId;
- }
- }
- }
- }
- }
-
- return is_array($events) ? $events : array();
- }
-
- /**
- * Checks if the event's UID already exists and returns all event
- * ids with that UID.
- *
- * @param string $uid The event's uid.
- * @param string $calendar_id Calendar to search in.
- *
- * @return string|boolean Returns a string with event_id or false if
- * not found.
- */
- public function exists($uid, $calendar_id = null)
- {
- // Log error if someone uses this function in an unsupported way
- if ($calendar_id != $this->_calendar) {
- Horde::logMessage(sprintf("Kolab::exists called for calendar %s. Currently active is %s.", $calendar_id, $this->_calendar), __FILE__, __LINE__, PEAR_LOG_ERR);
- return PEAR::raiseError(sprintf("Kolab::exists called for calendar %s. Currently active is %s.", $calendar_id, $this->_calendar));
- }
-
- $result = $this->synchronize();
- if (is_a($result, 'PEAR_Error')) {
- return $result;
- }
-
- if ($this->_store->objectUidExists($uid)) {
- return $uid;
- }
-
- return false;
- }
-
- /**
- * Lists all events in the time range, optionally restricting
- * results to only events with alarms.
- *
- * @param Horde_Date $startInterval Start of range date object.
- * @param Horde_Date $endInterval End of range data object.
- * @param boolean $hasAlarm Only return events with alarms?
- * Defaults to all events.
- *
- * @return array Events in the given time range.
- */
- public function listEvents($startDate = null, $endDate = null, $hasAlarm = false)
- {
- $result = $this->synchronize();
- if (is_a($result, 'PEAR_Error')) {
- return $result;
- }
-
- if (is_null($startDate)) {
- $startDate = new Horde_Date(array('mday' => 1, 'month' => 1, 'year' => 0000));
- }
- if (is_null($endDate)) {
- $endDate = new Horde_Date(array('mday' => 31, 'month' => 12, 'year' => 9999));
- }
-
- $ids = array();
-
- foreach($this->_events_cache as $event) {
- if ($hasAlarm && !$event->getAlarm()) {
- continue;
- }
-
- $keep_event = false;
- /* check if event period intersects with given period */
- if (!(($endDate->compareDateTime($event->start) < 0) ||
- ($startDate->compareDateTime($event->end) > 0))) {
- $keep_event = true;
- }
-
- /* do recurrence expansion if not keeping anyway */
- if (!$keep_event && $event->recurs()) {
- $next = $event->recurrence->nextRecurrence($startDate);
- while ($next !== false &&
- $event->recurrence->hasException($next->year, $next->month, $next->mday)) {
- $next->mday++;
- $next = $event->recurrence->nextRecurrence($next);
- }
-
- if ($next !== false) {
- $duration = $next->timestamp() - $event->start->timestamp();
- $next_end = new Horde_Date($event->end->timestamp() + $duration);
-
- if ((!(($endDate->compareDateTime($next) < 0) ||
- ($startDate->compareDateTime($next_end) > 0)))) {
- $keep_event = true;
- }
- }
- }
-
- if ($keep_event) {
- $ids[$event->getUID()] = $event->getUID();
- }
- }
-
- return $ids;
- }
-
- public function getEvent($eventId = null)
- {
- if (is_null($eventId)) {
- return new Kronolith_Event_kolab($this);
- }
-
- $result = $this->synchronize();
- if (is_a($result, 'PEAR_Error')) {
- return $result;
- }
-
- if (array_key_exists($eventId, $this->_events_cache)) {
- return $this->_events_cache[$eventId];
- }
-
- return PEAR::raiseError(sprintf(_("Event not found: %s"), $eventId));
- }
-
- /**
- * Get an event or events with the given UID value.
- *
- * @param string $uid The UID to match
- * @param array $calendars A restricted array of calendar ids to search
- * @param boolean $getAll Return all matching events? If this is false,
- * an error will be returned if more than one event is found.
- *
- * @return Kronolith_Event
- */
- public function getByUID($uid, $calendars = null, $getAll = false)
- {
- if (!is_array($calendars)) {
- $calendars = array_keys(Kronolith::listCalendars(true, PERMS_READ));
- }
-
- foreach ($calendars as $calendar) {
- $this->open($calendar);
- $this->synchronize();
-
- if (!array_key_exists($uid, $this->_events_cache)) {
- continue;
- }
-
- // Ok, found event
- $event = $this->_events_cache[$uid];
-
- if ($getAll) {
- $events = array();
- $events[] = $event;
- return $events;
- } else {
- return $event;
- }
- }
-
- return PEAR::raiseError(sprintf(_("Event not found: %s"), $uid));
- }
-
- /**
- * Saves an event in the backend.
- * If it is a new event, it is added, otherwise the event is updated.
- *
- * @param Kronolith_Event $event The event to save.
- *
- * @return mixed UID on success, a PEAR error otherwise
- */
- public function saveEvent(&$event)
- {
- $result = $this->synchronize();
- if (is_a($result, 'PEAR_Error')) {
- return $result;
- }
-
- $uid = $event->getUID();
- if ($uid == null) {
- $event->setUID($this->_store->generateUID());
- }
-
- $attributes = $event->toDriver();
-
- $edit = false;
- $stored_uid = null;
- if ($event->isStored() || $event->exists()) {
- $stored_uid = $attributes['uid'];
- $action = array('action' => 'modify');
- $edit = true;
- } else {
- $action = array('action' => 'add');
- }
-
- $result = $this->_store->save($attributes, $stored_uid);
- if (is_a($result, 'PEAR_Error')) {
- return $result;
- }
-
- /* Deal with tags */
- $tagger = Kronolith::getTagger();
- if (!empty($edit)) {
- $tagger->replaceTags($event->getUID(), $event->tags, 'event');
- } else {
- $tagger->tag($event->getUID(), $event->tags, 'event');
- }
-
- /* Notify about the changed event. */
- $result = Kronolith::sendNotification($event, $edit ? 'edit' : 'add');
- if (is_a($result, 'PEAR_Error')) {
- Horde::logMessage($result, __FILE__, __LINE__, PEAR_LOG_ERR);
- }
-
- /* Log the creation/modification of this item in the history log. */
- $history = Horde_History::singleton();
- $history->log('kronolith:' . $event->getCalendar() . ':' . $event->getUID(), $action, true);
-
- // refresh IMAP cache
- $this->synchronize(true);
-
- if (is_callable('Kolab', 'triggerFreeBusyUpdate')) {
- Kolab::triggerFreeBusyUpdate($this->_store->parseFolder($event->getCalendar()));
- }
-
- return $event->getUID();
- }
-
- /**
- * Move an event to a new calendar.
- *
- * @param string $eventId The event to move.
- * @param string $newCalendar The new calendar.
- */
- public function move($eventId, $newCalendar)
- {
- $event = $this->getEvent($eventId);
-
- $result = $this->synchronize();
- if (is_a($result, 'PEAR_Error')) {
- return $result;
- }
-
- global $kronolith_shares;
- $target = $kronolith_shares->getShare($newCalendar);
- $folder = $target->get('folder');
-
- $result = $this->_store->move($eventId, $folder);
- if ($result) {
- unset($this->_events_cache[$eventId]);
- }
-
- if (is_callable('Kolab', 'triggerFreeBusyUpdate')) {
- Kolab::triggerFreeBusyUpdate($this->_store->parseFolder($this->_calendar));
- Kolab::triggerFreeBusyUpdate($this->_store->parseFolder($newCalendar));
- }
-
- /* Log the moving of this item in the history log. */
- $uid = $event->getUID();
- $history = Horde_History::singleton();
- $history->log('kronolith:' . $event->getCalendar() . ':' . $uid, array('action' => 'delete'), true);
- $history->log('kronolith:' . $newCalendar . ':' . $uid, array('action' => 'add'), true);
-
- return $result;
- }
-
- /**
- * Delete a calendar and all its events.
- *
- * @param string $calendar The name of the calendar to delete.
- *
- * @return mixed True or a PEAR_Error on failure.
- */
- public function delete($calendar)
- {
- $this->open($calendar);
- $result = $this->synchronize();
- if (is_a($result, 'PEAR_Error')) {
- return $result;
- }
-
- $result = $this->_store->deleteAll($calendar);
- if (is_callable('Kolab', 'triggerFreeBusyUpdate')) {
- Kolab::triggerFreeBusyUpdate($this->_store->parseFolder($calendar));
- }
- return true;
- }
-
- /**
- * Rename a calendar.
- *
- * @param string $from The current name of the calendar.
- * @param string $to The new name of the calendar.
- *
- * @return mixed True or a PEAR_Error on failure.
- */
- public function rename($from, $to)
- {
- // FIXME: We can't do much here. We'd need to be called after
- // renaming the share here. This needs to be checked again.
- // kolab/issue2249 ([horde/kronolith] Kronolith is unable to
- // trigger a free/busy update on a folder rename)
- return true;
- }
-
- /**
- * Delete an event.
- *
- * @param string $eventId The ID of the event to delete.
- *
- * @return mixed True or a PEAR_Error on failure.
- */
- public function deleteEvent($eventId, $silent = false)
- {
- $result = $this->synchronize();
- if (is_a($result, 'PEAR_Error')) {
- return $result;
- }
-
- if (!$this->_store->objectUidExists($eventId)) {
- return PEAR::raiseError(sprintf(_("Event not found: %s"), $eventId));
- }
-
- $event = $this->getEvent($eventId);
-
- if ($this->_store->delete($eventId)) {
- // Notify about the deleted event.
- if (!$silent) {
- $result = Kronolith::sendNotification($event, 'delete');
- if (is_a($result, 'PEAR_Error')) {
- Horde::logMessage($result, __FILE__, __LINE__, PEAR_LOG_ERR);
- }
- }
-
- /* Log the deletion of this item in the history log. */
- $history = Horde_History::singleton();
- $history->log('kronolith:' . $event->getCalendar() . ':' . $event->getUID(), array('action' => 'delete'), true);
-
- if (is_callable('Kolab', 'triggerFreeBusyUpdate')) {
- Kolab::triggerFreeBusyUpdate($this->_store->parseFolder($event->getCalendar()));
- }
-
- unset($this->_events_cache[$eventId]);
- } else {
- return PEAR::raiseError(sprintf(_("Cannot delete event: %s"), $eventId));
- }
-
- return true;
- }
-
-}
-
-/**
- * @package Kronolith
- */
-class Kronolith_Event_kolab extends Kronolith_Event
-{
-
- public function fromDriver($event)
- {
- $this->eventID = $event['uid'];
- $this->setUID($this->eventID);
-
- if (isset($event['summary'])) {
- $this->title = $event['summary'];
- }
- if (isset($event['body'])) {
- $this->description = $event['body'];
- }
- if (isset($event['location'])) {
- $this->location = $event['location'];
- }
-
- if (isset($event['sensitivity']) &&
- ($event['sensitivity'] == 'private' || $event['sensitivity'] == 'confidential')) {
- $this->setPrivate(true);
- }
-
- if (isset($event['organizer']['smtp-address'])) {
- if (Kronolith::isUserEmail(Auth::getAuth(), $event['organizer']['smtp-address'])) {
- $this->creatorID = Auth::getAuth();
- } else {
- $this->creatorID = $event['organizer']['smtp-address'];
- }
- }
-
- if (isset($event['alarm'])) {
- $this->alarm = $event['alarm'];
- }
-
- $this->start = new Horde_Date($event['start-date']);
- $this->end = new Horde_Date($event['end-date']);
- $this->durMin = ($this->end->timestamp() - $this->start->timestamp()) / 60;
-
- if (isset($event['show-time-as'])) {
- switch ($event['show-time-as']) {
- case 'free':
- $this->status = Kronolith::STATUS_FREE;
- break;
-
- case 'tentative':
- $this->status = Kronolith::STATUS_TENTATIVE;
- break;
-
- case 'busy':
- case 'outofoffice':
- default:
- $this->status = Kronolith::STATUS_CONFIRMED;
- }
- } else {
- $this->status = Kronolith::STATUS_CONFIRMED;
- }
-
- // Recurrence
- if (isset($event['recurrence'])) {
- $this->recurrence = new Horde_Date_Recurrence($this->start);
- $this->recurrence->fromHash($event['recurrence']);
- }
-
- // Attendees
- $attendee_count = 0;
- foreach($event['attendee'] as $attendee) {
- $name = $attendee['display-name'];
- $email = $attendee['smtp-address'];
-
- $role = $attendee['role'];
- switch ($role) {
- case 'optional':
- $role = Kronolith::PART_OPTIONAL;
- break;
-
- case 'resource':
- $role = Kronolith::PART_NONE;
- break;
-
- case 'required':
- default:
- $role = Kronolith::PART_REQUIRED;
- break;
- }
-
- $status = $attendee['status'];
- switch ($status) {
- case 'accepted':
- $status = Kronolith::RESPONSE_ACCEPTED;
- break;
-
- case 'declined':
- $status = Kronolith::RESPONSE_DECLINED;
- break;
-
- case 'tentative':
- $status = Kronolith::RESPONSE_TENTATIVE;
- break;
-
- case 'none':
- default:
- $status = Kronolith::RESPONSE_NONE;
- break;
- }
-
- // Attendees without an email address get added as incremented number
- if (empty($email)) {
- $email = $attendee_count;
- $attendee_count++;
- }
-
- $this->addAttendee($email, $role, $status, $name);
- }
-
- $this->initialized = true;
- $this->stored = true;
- }
-
- public function toDriver()
- {
- $event = array();
- $event['uid'] = $this->getUID();
- $event['summary'] = $this->title;
- $event['body'] = $this->description;
- $event['location'] = $this->location;
-
- if ($this->isPrivate()) {
- $event['sensitivity'] = 'private';
- } else {
- $event['sensitivity'] = 'public';
- }
-
- // Only set organizer if this is a new event
- if ($this->getID() == null) {
- $organizer = array(
- 'display-name' => Kronolith::getUserName($this->getCreatorId()),
- 'smtp-address' => Kronolith::getUserEmail($this->getCreatorId())
- );
- $event['organizer'] = $organizer;
- }
-
- if ($this->alarm != 0) {
- $event['alarm'] = $this->alarm;
- }
-
- $event['start-date'] = $this->start->timestamp();
- $event['end-date'] = $this->end->timestamp();
- $event['_is_all_day'] = $this->isAllDay();
-
- switch ($this->status) {
- case Kronolith::STATUS_FREE:
- case Kronolith::STATUS_CANCELLED:
- $event['show-time-as'] = 'free';
- break;
-
- case Kronolith::STATUS_TENTATIVE:
- $event['show-time-as'] = 'tentative';
- break;
-
- // No mapping for outofoffice
- case Kronolith::STATUS_CONFIRMED:
- default:
- $event['show-time-as'] = 'busy';
- }
-
- // Recurrence
- if ($this->recurs()) {
- $event['recurrence'] = $this->recurrence->toHash();
- } else {
- $event['recurrence'] = array();
- }
-
-
- // Attendees
- $event['attendee'] = array();
- $attendees = $this->getAttendees();
-
- foreach($attendees as $email => $attendee) {
- $new_attendee = array();
- $new_attendee['display-name'] = $attendee['name'];
-
- // Attendee without an email address
- if (is_int($email)) {
- $new_attendee['smtp-address'] = '';
- } else {
- $new_attendee['smtp-address'] = $email;
- }
-
- switch ($attendee['attendance']) {
- case Kronolith::PART_OPTIONAL:
- $new_attendee['role'] = 'optional';
- break;
-
- case Kronolith::PART_NONE:
- $new_attendee['role'] = 'resource';
- break;
-
- case Kronolith::PART_REQUIRED:
- default:
- $new_attendee['role'] = 'required';
- break;
- }
-
- $new_attendee['request-response'] = '0';
-
- switch ($attendee['response']) {
- case Kronolith::RESPONSE_ACCEPTED:
- $new_attendee['status'] = 'accepted';
- break;
-
- case Kronolith::RESPONSE_DECLINED:
- $new_attendee['status'] = 'declined';
- break;
-
- case Kronolith::RESPONSE_TENTATIVE:
- $new_attendee['status'] = 'tentative';
- break;
-
- case Kronolith::RESPONSE_NONE:
- default:
- $new_attendee['status'] = 'none';
- break;
- }
-
- $event['attendee'][] = $new_attendee;
- }
-
- return $event;
- }
-
-}
+++ /dev/null
-<?php
-/**
- * The Kronolith_Driver_sql:: class implements the Kronolith_Driver API for a
- * SQL backend.
- *
- * @author Luc Saillard <luc.saillard@fr.alcove.com>
- * @author Chuck Hagenbuch <chuck@horde.org>
- * @author Jan Schneider <jan@horde.org>
- * @package Kronolith
- */
-class Kronolith_Driver_sql extends Kronolith_Driver
-{
- /**
- * The object handle for the current database connection.
- *
- * @var DB
- */
- private $_db;
-
- /**
- * Handle for the current database connection, used for writing. Defaults
- * to the same handle as $_db if a separate write database is not required.
- *
- * @var DB
- */
- private $_write_db;
-
- /**
- * Cache events as we fetch them to avoid fetching the same event from the
- * DB twice.
- *
- * @var array
- */
- private $_cache = array();
-
- public function listAlarms($date, $fullevent = false)
- {
- require_once 'Date/Calc.php';
-
- $allevents = $this->listEvents($date, null, true);
- if (is_a($allevents, 'PEAR_Error')) {
- return $allevents;
- }
-
- $events = array();
- foreach ($allevents as $eventId) {
- $event = $this->getEvent($eventId);
- if (is_a($event, 'PEAR_Error')) {
- continue;
- }
-
- if (!$event->recurs()) {
- $start = new Horde_Date($event->start);
- $start->min -= $event->getAlarm();
- if ($start->compareDateTime($date) <= 0 &&
- $date->compareDateTime($event->end) <= -1) {
- $events[] = $fullevent ? $event : $eventId;
- }
- } else {
- if ($next = $event->recurrence->nextRecurrence($date)) {
- if ($event->recurrence->hasException($next->year, $next->month, $next->mday)) {
- continue;
- }
- $start = new Horde_Date($next);
- $start->min -= $event->getAlarm();
- $diff = Date_Calc::dateDiff($event->start->mday,
- $event->start->month,
- $event->start->year,
- $event->end->mday,
- $event->end->month,
- $event->end->year);
- if ($diff == -1) {
- $diff = 0;
- }
- $end = new Horde_Date(array('year' => $next->year,
- 'month' => $next->month,
- 'mday' => $next->mday + $diff,
- 'hour' => $event->end->hour,
- 'min' => $event->end->min,
- 'sec' => $event->end->sec));
- if ($start->compareDateTime($date) <= 0 &&
- $date->compareDateTime($end) <= -1) {
- if ($fullevent) {
- $event->start = $start;
- $event->end = $end;
- $events[] = $event;
- } else {
- $events[] = $eventId;
- }
- }
- }
- }
- }
-
- return is_array($events) ? $events : array();
- }
-
- public function search($query)
- {
- require_once 'Horde/SQL.php';
-
- /* Build SQL conditions based on the query string. */
- $cond = '((';
- $values = array();
-
- if (!empty($query->title)) {
- $binds = Horde_SQL::buildClause($this->_db, 'event_title', 'LIKE', $this->convertToDriver($query->title), true);
- if (is_array($binds)) {
- $cond .= $binds[0] . ' AND ';
- $values = array_merge($values, $binds[1]);
- } else {
- $cond .= $binds;
- }
- }
- if (!empty($query->location)) {
- $binds = Horde_SQL::buildClause($this->_db, 'event_location', 'LIKE', $this->convertToDriver($query->location), true);
- if (is_array($binds)) {
- $cond .= $binds[0] . ' AND ';
- $values = array_merge($values, $binds[1]);
- } else {
- $cond .= $binds;
- }
- }
- if (!empty($query->description)) {
- $binds = Horde_SQL::buildClause($this->_db, 'event_description', 'LIKE', $this->convertToDriver($query->description), true);
- if (is_array($binds)) {
- $cond .= $binds[0] . ' AND ';
- $values = array_merge($values, $binds[1]);
- } else {
- $cond .= $binds;
- }
- }
- if (isset($query->status)) {
- $binds = Horde_SQL::buildClause($this->_db, 'event_status', '=', $query->status, true);
- if (is_array($binds)) {
- $cond .= $binds[0] . ' AND ';
- $values = array_merge($values, $binds[1]);
- } else {
- $cond .= $binds;
- }
- }
-
- if (!empty($query->creatorID)) {
- $binds = Horde_SQL::buildClause($this->_db, 'event_creator_id', '=', $query->creatorID, true);
- if (is_array($binds)) {
- $cond .= $binds[0] . ' AND ';
- $values = array_merge($values, $binds[1]);
- } else {
- $cond .= $binds;
- }
- }
-
- if ($cond == '((') {
- $cond = '';
- } else {
- $cond = substr($cond, 0, strlen($cond) - 5) . '))';
- }
-
- $eventIds = $this->listEventsConditional($query->start,
- empty($query->end)
- ? new Horde_Date(array('mday' => 31, 'month' => 12, 'year' => 9999))
- : $query->end,
- $cond,
- $values);
- if (is_a($eventIds, 'PEAR_Error')) {
- return $eventIds;
- }
-
- $events = array();
- foreach ($eventIds as $eventId) {
- $event = $this->getEvent($eventId);
- if (is_a($event, 'PEAR_Error')) {
- return $event;
- }
- $events[] = $event;
- }
-
- return $events;
- }
-
- /**
- * Checks if the event's UID already exists and returns all event
- * ids with that UID.
- *
- * @param string $uid The event's uid.
- * @param string $calendar_id Calendar to search in.
- *
- * @return string|boolean Returns a string with event_id or false if
- * not found.
- */
- public function exists($uid, $calendar_id = null)
- {
- $query = 'SELECT event_id FROM ' . $this->_params['table'] . ' WHERE event_uid = ?';
- $values = array($uid);
-
- if (!is_null($calendar_id)) {
- $query .= ' AND calendar_id = ?';
- $values[] = $calendar_id;
- }
-
- /* Log the query at a DEBUG log level. */
- Horde::logMessage(sprintf('Kronolith_Driver_sql::exists(): user = "%s"; query = "%s"',
- Auth::getAuth(), $query),
- __FILE__, __LINE__, PEAR_LOG_DEBUG);
-
- $event = $this->_db->getRow($query, $values, DB_FETCHMODE_ASSOC);
- if (is_a($event, 'PEAR_Error')) {
- Horde::logMessage($result, __FILE__, __LINE__, PEAR_LOG_ERR);
- return $event;
- }
-
- if ($event) {
- return $event['event_id'];
- } else {
- return false;
- }
- }
-
- /**
- * Lists all events in the time range, optionally restricting
- * results to only events with alarms.
- *
- * @param Horde_Date $startInterval Start of range date object.
- * @param Horde_Date $endInterval End of range data object.
- * @param boolean $hasAlarm Only return events with alarms?
- * Defaults to all events.
- *
- * @return array Events in the given time range.
- */
- public function listEvents($startDate = null, $endDate = null,
- $hasAlarm = false)
- {
- if (empty($endDate)) {
- $endInterval = new Horde_Date(array('mday' => 31, 'month' => 12,
- 'year' => 9999));
- } else {
- $endInterval = clone $endDate;
- $endInterval->mday++;
- }
-
- $startInterval = null;
- if (empty($startDate)) {
- $startInterval = new Horde_Date(array('mday' => 1, 'month' => 1,
- 'year' => 0000));
- } else {
- $startInterval = clone $startDate;
- }
-
- return $this->listEventsConditional($startInterval, $endInterval,
- $hasAlarm ? 'event_alarm > ?' : '',
- $hasAlarm ? array(0) : array());
- }
-
- /**
- * Lists all events that satisfy the given conditions.
- *
- * @param Horde_Date $startInterval Start of range date object.
- * @param Horde_Date $endInterval End of range data object.
- * @param string $conditions Conditions, given as SQL clauses.
- * @param array $vals SQL bind variables for use with
- * $conditions clauses.
- *
- * @return array Events in the given time range satisfying the given
- * conditions.
- */
- public function listEventsConditional($startInterval, $endInterval,
- $conditions = '', $vals = array())
- {
- $q = 'SELECT event_id, event_uid, event_description, event_location,' .
- ' event_private, event_status, event_attendees,' .
- ' event_title, event_recurcount,' .
- ' event_recurtype, event_recurenddate, event_recurinterval,' .
- ' event_recurdays, event_start, event_end, event_allday,' .
- ' event_alarm, event_alarm_methods, event_modified,' .
- ' event_exceptions, event_creator_id' .
- ' FROM ' . $this->_params['table'] .
- ' WHERE calendar_id = ? AND ((';
- $values = array($this->_calendar);
-
- if ($conditions) {
- $q .= $conditions . ')) AND ((';
- $values = array_merge($values, $vals);
- }
-
- $etime = $endInterval->format('Y-m-d H:i:s');
- $stime = null;
- if (isset($startInterval)) {
- $stime = $startInterval->format('Y-m-d H:i:s');
- $q .= 'event_end > ? AND ';
- $values[] = $stime;
- }
- $q .= 'event_start < ?) OR (';
- $values[] = $etime;
- if (isset($stime)) {
- $q .= 'event_recurenddate >= ? AND ';
- $values[] = $stime;
- }
- $q .= 'event_start <= ?' .
- ' AND event_recurtype <> ?))';
- array_push($values, $etime, Horde_Date_Recurrence::RECUR_NONE);
-
- /* Log the query at a DEBUG log level. */
- Horde::logMessage(sprintf('Kronolith_Driver_sql::listEventsConditional(): user = "%s"; query = "%s"; values = "%s"',
- Auth::getAuth(), $q, implode(',', $values)),
- __FILE__, __LINE__, PEAR_LOG_DEBUG);
-
- /* Run the query. */
- $qr = $this->_db->query($q, $values);
- if (is_a($qr, 'PEAR_Error')) {
- Horde::logMessage($qr, __FILE__, __LINE__, PEAR_LOG_ERR);
- return $qr;
- }
-
- $events = array();
- $row = $qr->fetchRow(DB_FETCHMODE_ASSOC);
- while ($row && !is_a($row, 'PEAR_Error')) {
- /* If the event did not have a UID before, we need to give
- * it one. */
- if (empty($row['event_uid'])) {
- $row['event_uid'] = $this->generateUID();
-
- /* Save the new UID for data integrity. */
- $query = 'UPDATE ' . $this->_params['table'] . ' SET event_uid = ? WHERE event_id = ?';
- $values = array($row['event_uid'], $row['event_id']);
-
- /* Log the query at a DEBUG log level. */
- Horde::logMessage(sprintf('Kronolith_Driver_sql::listEventsConditional(): user = %s; query = "%s"; values = "%s"',
- Auth::getAuth(), $query, implode(',', $values)),
- __FILE__, __LINE__, PEAR_LOG_DEBUG);
-
- $result = $this->_write_db->query($query, $values);
- if (is_a($result, 'PEAR_Error')) {
- Horde::logMessage($result, __FILE__, __LINE__, PEAR_LOG_ERR);
- }
- }
-
- /* We have all the information we need to create an event
- * object for this event, so go ahead and cache it. */
- $this->_cache[$this->_calendar][$row['event_id']] = new Kronolith_Event_sql($this, $row);
- if ($row['event_recurtype'] == Horde_Date_Recurrence::RECUR_NONE) {
- $events[$row['event_uid']] = $row['event_id'];
- } else {
- $next = $this->nextRecurrence($row['event_id'], $startInterval);
- if ($next && $next->compareDate($endInterval) < 0) {
- $events[$row['event_uid']] = $row['event_id'];
- }
- }
-
- $row = $qr->fetchRow(DB_FETCHMODE_ASSOC);
- }
-
- return $events;
- }
-
- public function getEvent($eventId = null)
- {
- if (is_null($eventId)) {
- return new Kronolith_Event_sql($this);
- }
-
- if (isset($this->_cache[$this->_calendar][$eventId])) {
- return $this->_cache[$this->_calendar][$eventId];
- }
-
- $query = 'SELECT event_id, event_uid, event_description,' .
- ' event_location, event_private, event_status, event_attendees,' .
- ' event_title, event_recurcount,' .
- ' event_recurtype, event_recurenddate, event_recurinterval,' .
- ' event_recurdays, event_start, event_end, event_allday,' .
- ' event_alarm, event_alarm_methods, event_modified,' .
- ' event_exceptions, event_creator_id' .
- ' FROM ' . $this->_params['table'] . ' WHERE event_id = ? AND calendar_id = ?';
- $values = array($eventId, $this->_calendar);
-
- /* Log the query at a DEBUG log level. */
- Horde::logMessage(sprintf('Kronolith_Driver_sql::getEvent(): user = "%s"; query = "%s"; values = "%s"',
- Auth::getAuth(), $query, implode(',', $values)),
- __FILE__, __LINE__, PEAR_LOG_DEBUG);
-
- $event = $this->_db->getRow($query, $values, DB_FETCHMODE_ASSOC);
- if (is_a($event, 'PEAR_Error')) {
- Horde::logMessage($event, __FILE__, __LINE__, PEAR_LOG_ERR);
- return $event;
- }
-
- if ($event) {
- $this->_cache[$this->_calendar][$eventId] = new Kronolith_Event_sql($this, $event);
- return $this->_cache[$this->_calendar][$eventId];
- } else {
- return PEAR::raiseError(_("Event not found"));
- }
- }
-
- /**
- * Get an event or events with the given UID value.
- *
- * @param string $uid The UID to match
- * @param array $calendars A restricted array of calendar ids to search
- * @param boolean $getAll Return all matching events? If this is false,
- * an error will be returned if more than one event is found.
- *
- * @return Kronolith_Event
- */
- public function getByUID($uid, $calendars = null, $getAll = false)
- {
- $query = 'SELECT event_id, event_uid, calendar_id, event_description,' .
- ' event_location, event_private, event_status, event_attendees,' .
- ' event_title, event_recurcount,' .
- ' event_recurtype, event_recurenddate, event_recurinterval,' .
- ' event_recurdays, event_start, event_end, event_allday,' .
- ' event_alarm, event_alarm_methods, event_modified,' .
- ' event_exceptions, event_creator_id' .
- ' FROM ' . $this->_params['table'] . ' WHERE event_uid = ?';
- $values = array($uid);
-
- /* Optionally filter by calendar */
- if (!is_null($calendars)) {
- if (!count($calendars)) {
- return PEAR::raiseError(_("No calendars to search"));
- }
- $query .= ' AND calendar_id IN (?' . str_repeat(', ?', count($calendars) - 1) . ')';
- $values = array_merge($values, $calendars);
- }
-
- /* Log the query at a DEBUG log level. */
- Horde::logMessage(sprintf('Kronolith_Driver_sql::getByUID(): user = "%s"; query = "%s"; values = "%s"',
- Auth::getAuth(), $query, implode(',', $values)),
- __FILE__, __LINE__, PEAR_LOG_DEBUG);
-
- $events = $this->_db->getAll($query, $values, DB_FETCHMODE_ASSOC);
- if (is_a($events, 'PEAR_Error')) {
- Horde::logMessage($events, __FILE__, __LINE__, PEAR_LOG_ERR);
- return $events;
- }
- if (!count($events)) {
- return PEAR::raiseError($uid . ' not found');
- }
-
- $eventArray = array();
- foreach ($events as $event) {
- $this->open($event['calendar_id']);
- $this->_cache[$this->_calendar][$event['event_id']] = new Kronolith_Event_sql($this, $event);
- $eventArray[] = $this->_cache[$this->_calendar][$event['event_id']];
- }
-
- if ($getAll) {
- return $eventArray;
- }
-
- /* First try the user's own calendars. */
- $ownerCalendars = Kronolith::listCalendars(true, PERMS_READ);
- $event = null;
- foreach ($eventArray as $ev) {
- if (isset($ownerCalendars[$ev->getCalendar()])) {
- $event = $ev;
- break;
- }
- }
-
- /* If not successful, try all calendars the user has access too. */
- if (empty($event)) {
- $readableCalendars = Kronolith::listCalendars(false, PERMS_READ);
- foreach ($eventArray as $ev) {
- if (isset($readableCalendars[$ev->getCalendar()])) {
- $event = $ev;
- break;
- }
- }
- }
-
- if (empty($event)) {
- $event = $eventArray[0];
- }
-
- return $event;
- }
-
- /**
- * Saves an event in the backend.
- * If it is a new event, it is added, otherwise the event is updated.
- *
- * @param Kronolith_Event $event The event to save.
- */
- public function saveEvent($event)
- {
- if ($event->isStored() || $event->exists()) {
- $values = array();
-
- $query = 'UPDATE ' . $this->_params['table'] . ' SET ';
-
- foreach ($event->getProperties() as $key => $val) {
- $query .= " $key = ?,";
- $values[] = $val;
- }
- $query = substr($query, 0, -1);
- $query .= ' WHERE event_id = ?';
- $values[] = $event->getId();
-
- /* Log the query at a DEBUG log level. */
- Horde::logMessage(sprintf('Kronolith_Driver_sql::saveEvent(): user = "%s"; query = "%s"; values = "%s"',
- Auth::getAuth(), $query, implode(',', $values)),
- __FILE__, __LINE__, PEAR_LOG_DEBUG);
-
- $result = $this->_write_db->query($query, $values);
- if (is_a($result, 'PEAR_Error')) {
- Horde::logMessage($result, __FILE__, __LINE__, PEAR_LOG_ERR);
- return $result;
- }
-
- /* Log the modification of this item in the history log. */
- if ($event->getUID()) {
- $history = Horde_History::singleton();
- $history->log('kronolith:' . $this->_calendar . ':' . $event->getUID(), array('action' => 'modify'), true);
- }
-
- /* Update tags */
- $tagger = Kronolith::getTagger();
- $tagger->replaceTags($event->getUID(), $event->tags, 'event');
-
- /* Notify users about the changed event. */
- $result = Kronolith::sendNotification($event, 'edit');
- if (is_a($result, 'PEAR_Error')) {
- Horde::logMessage($result, __FILE__, __LINE__, PEAR_LOG_ERR);
- }
-
- return $event->getId();
- } else {
- if ($event->getId()) {
- $id = $event->getId();
- } else {
- $id = hash('md5', uniqid(mt_rand(), true));
- $event->setId($id);
- }
-
- if ($event->getUID()) {
- $uid = $event->getUID();
- } else {
- $uid = $this->generateUID();
- $event->setUID($uid);
- }
-
- $query = 'INSERT INTO ' . $this->_params['table'];
- $cols_name = ' (event_id, event_uid,';
- $cols_values = ' VALUES (?, ?,';
- $values = array($id, $uid);
-
- foreach ($event->getProperties() as $key => $val) {
- $cols_name .= " $key,";
- $cols_values .= ' ?,';
- $values[] = $val;
- }
-
- $cols_name .= ' calendar_id)';
- $cols_values .= ' ?)';
- $values[] = $this->_calendar;
-
- $query .= $cols_name . $cols_values;
-
- /* Log the query at a DEBUG log level. */
- Horde::logMessage(sprintf('Kronolith_Driver_sql::saveEvent(): user = "%s"; query = "%s"; values = "%s"',
- Auth::getAuth(), $query, implode(',', $values)),
- __FILE__, __LINE__, PEAR_LOG_DEBUG);
-
- $result = $this->_write_db->query($query, $values);
- if (is_a($result, 'PEAR_Error')) {
- Horde::logMessage($result, __FILE__, __LINE__, PEAR_LOG_ERR);
- return $result;
- }
-
- /* Log the creation of this item in the history log. */
- $history = Horde_History::singleton();
- $history->log('kronolith:' . $this->_calendar . ':' . $uid, array('action' => 'add'), true);
-
- /* Deal with any tags */
- $tagger = Kronolith::getTagger();
- $tagger->tag($event->getUID(), $event->tags, 'event');
-
- /* Notify users about the new event. */
- $result = Kronolith::sendNotification($event, 'add');
- if (is_a($result, 'PEAR_Error')) {
- Horde::logMessage($result, __FILE__, __LINE__, PEAR_LOG_ERR);
- }
-
- return $id;
- }
- }
-
- /**
- * Move an event to a new calendar.
- *
- * @param string $eventId The event to move.
- * @param string $newCalendar The new calendar.
- */
- public function move($eventId, $newCalendar)
- {
- /* Fetch the event for later use. */
- $event = $this->getEvent($eventId);
- if (is_a($event, 'PEAR_Error')) {
- return $event;
- }
-
- $query = 'UPDATE ' . $this->_params['table'] . ' SET calendar_id = ? WHERE calendar_id = ? AND event_id = ?';
- $values = array($newCalendar, $this->_calendar, $eventId);
-
- /* Log the query at a DEBUG log level. */
- Horde::logMessage(sprintf('Kronolith_Driver_sql::move(): %s; values = "%s"',
- $query, implode(',', $values)),
- __FILE__, __LINE__, PEAR_LOG_DEBUG);
-
- /* Attempt the move query. */
- $result = $this->_write_db->query($query, $values);
- if (is_a($result, 'PEAR_Error')) {
- Horde::logMessage($result, __FILE__, __LINE__, PEAR_LOG_ERR);
- return $result;
- }
-
- /* Log the moving of this item in the history log. */
- $uid = $event->getUID();
- if ($uid) {
- $history = Horde_History::singleton();
- $history->log('kronolith:' . $this->_calendar . ':' . $uid, array('action' => 'delete'), true);
- $history->log('kronolith:' . $newCalendar . ':' . $uid, array('action' => 'add'), true);
- }
-
- return true;
- }
-
- /**
- * Delete a calendar and all its events.
- *
- * @param string $calendar The name of the calendar to delete.
- *
- * @return mixed True or a PEAR_Error on failure.
- */
- public function delete($calendar)
- {
- $query = 'DELETE FROM ' . $this->_params['table'] . ' WHERE calendar_id = ?';
- $values = array($calendar);
-
- /* Log the query at a DEBUG log level. */
- Horde::logMessage(sprintf('Kronolith_Driver_sql::delete(): user = "%s"; query = "%s"; values = "%s"',
- Auth::getAuth(), $query, implode(',', $values)),
- __FILE__, __LINE__, PEAR_LOG_DEBUG);
-
- return $this->_write_db->query($query, $values);
- }
-
- /**
- * Delete an event.
- *
- * @param string $eventId The ID of the event to delete.
- * @param boolean $silent Don't send notifications, used when deleting
- * events in bulk from maintenance tasks.
- *
- * @return mixed True or a PEAR_Error on failure.
- */
- public function deleteEvent($eventId, $silent = false)
- {
- /* Fetch the event for later use. */
- $event = $this->getEvent($eventId);
- if (is_a($event, 'PEAR_Error')) {
- return $event;
- }
-
- $query = 'DELETE FROM ' . $this->_params['table'] . ' WHERE event_id = ? AND calendar_id = ?';
- $values = array($eventId, $this->_calendar);
-
- /* Log the query at a DEBUG log level. */
- Horde::logMessage(sprintf('Kronolith_Driver_sql::deleteEvent(): user = "%s"; query = "%s"; values = "%s"',
- Auth::getAuth(), $query, implode(',', $values)),
- __FILE__, __LINE__, PEAR_LOG_DEBUG);
-
- $result = $this->_write_db->query($query, $values);
- if (is_a($result, 'PEAR_Error')) {
- Horde::logMessage($result, __FILE__, __LINE__, PEAR_LOG_ERR);
- return $result;
- }
-
- /* Log the deletion of this item in the history log. */
- if ($event->getUID()) {
- $history = Horde_History::singleton();
- $history->log('kronolith:' . $this->_calendar . ':' . $event->getUID(), array('action' => 'delete'), true);
- }
-
- /* Remove any pending alarms. */
- if (@include_once 'Horde/Alarm.php') {
- $alarm = Horde_Alarm::factory();
- $alarm->delete($event->getUID());
- }
-
- /* Remove any tags */
- $tagger = Kronolith::getTagger();
- $tagger->replaceTags($event->getUID(), array(), 'event');
-
- /* Notify about the deleted event. */
- if (!$silent) {
- $result = Kronolith::sendNotification($event, 'delete');
- if (is_a($result, 'PEAR_Error')) {
- Horde::logMessage($result, __FILE__, __LINE__, PEAR_LOG_ERR);
- }
- }
- return true;
- }
-
- /**
- * Attempts to open a connection to the SQL server.
- *
- * @return boolean True.
- */
- public function initialize()
- {
- Horde::assertDriverConfig($this->_params, 'calendar',
- array('phptype'));
-
- if (!isset($this->_params['database'])) {
- $this->_params['database'] = '';
- }
- if (!isset($this->_params['username'])) {
- $this->_params['username'] = '';
- }
- if (!isset($this->_params['hostspec'])) {
- $this->_params['hostspec'] = '';
- }
- if (!isset($this->_params['table'])) {
- $this->_params['table'] = 'kronolith_events';
- }
-
- /* Connect to the SQL server using the supplied parameters. */
- $this->_write_db = DB::connect($this->_params,
- array('persistent' => !empty($this->_params['persistent']),
- 'ssl' => !empty($this->_params['ssl'])));
- if (is_a($this->_write_db, 'PEAR_Error')) {
- return $this->_write_db;
- }
- $this->_initConn($this->_write_db);
-
- /* Check if we need to set up the read DB connection
- * seperately. */
- if (!empty($this->_params['splitread'])) {
- $params = array_merge($this->_params, $this->_params['read']);
- $this->_db = DB::connect($params,
- array('persistent' => !empty($params['persistent']),
- 'ssl' => !empty($params['ssl'])));
- if (is_a($this->_db, 'PEAR_Error')) {
- return $this->_db;
- }
- $this->_initConn($this->_db);
- } else {
- /* Default to the same DB handle for the writer too. */
- $this->_db = $this->_write_db;
- }
-
- return true;
- }
-
- /**
- */
- private function _initConn(&$db)
- {
- // Set DB portability options.
- switch ($db->phptype) {
- case 'mssql':
- $db->setOption('portability', DB_PORTABILITY_LOWERCASE | DB_PORTABILITY_ERRORS | DB_PORTABILITY_RTRIM);
- break;
- default:
- $db->setOption('portability', DB_PORTABILITY_LOWERCASE | DB_PORTABILITY_ERRORS);
- }
-
- /* Handle any database specific initialization code to run. */
- switch ($db->dbsyntax) {
- case 'oci8':
- $query = "ALTER SESSION SET NLS_DATE_FORMAT = 'YYYY-MM-DD HH24:MI:SS'";
-
- /* Log the query at a DEBUG log level. */
- Horde::logMessage(sprintf('Kronolith_Driver_sql::_initConn(): user = "%s"; query = "%s"',
- Auth::getAuth(), $query),
- __FILE__, __LINE__, PEAR_LOG_DEBUG);
-
- $db->query($query);
- break;
-
- case 'pgsql':
- $query = "SET datestyle TO 'iso'";
-
- /* Log the query at a DEBUG log level. */
- Horde::logMessage(sprintf('Kronolith_Driver_sql::_initConn(): user = "%s"; query = "%s"',
- Auth::getAuth(), $query),
- __FILE__, __LINE__, PEAR_LOG_DEBUG);
-
- $db->query($query);
- break;
- }
- }
-
- /**
- * Converts a value from the driver's charset to the default
- * charset.
- *
- * @param mixed $value A value to convert.
- *
- * @return mixed The converted value.
- */
- public function convertFromDriver($value)
- {
- return String::convertCharset($value, $this->_params['charset']);
- }
-
- /**
- * Converts a value from the default charset to the driver's
- * charset.
- *
- * @param mixed $value A value to convert.
- *
- * @return mixed The converted value.
- */
- public function convertToDriver($value)
- {
- return String::convertCharset($value, NLS::getCharset(), $this->_params['charset']);
- }
-
- /**
- * Remove all events owned by the specified user in all calendars.
- *
- *
- * @param string $user The user name to delete events for.
- *
- * @param mixed True | PEAR_Error
- */
- public function removeUserData($user)
- {
- if (!Auth::isAdmin()) {
- return PEAR::raiseError(_("Permission Denied"));
- }
-
- $shares = $GLOBALS['kronolith_shares']->listShares($user, PERMS_EDIT);
- if (is_a($shares, 'PEAR_Error')) {
- return $shares;
- }
-
- foreach (array_keys($shares) as $calendar) {
- $ids = Kronolith::listEventIds(null, null, $calendar);
- if (is_a($ids, 'PEAR_Error')) {
- return $ids;
- }
- $uids = array();
- foreach ($ids as $cal) {
- $uids = array_merge($uids, array_keys($cal));
- }
-
- foreach ($uids as $uid) {
- $event = $this->getByUID($uid);
- if (is_a($event, 'PEAR_Error')) {
- return $event;
- }
-
- $this->deleteEvent($event->getId());
- }
- }
- return true;
- }
-}
-
-/**
- * @package Kronolith
- */
-class Kronolith_Event_sql extends Kronolith_Event
-{
- /**
- * @var array
- */
- private $_properties = array();
-
- public function fromDriver($SQLEvent)
- {
- $driver = $this->getDriver();
-
- $this->allday = (bool)$SQLEvent['event_allday'];
- if (!$this->allday && $driver->getParam('utc')) {
- $tz_local = date_default_timezone_get();
- $this->start = new Horde_Date($SQLEvent['event_start'], 'UTC');
- $this->start->setTimezone($tz_local);
- $this->end = new Horde_Date($SQLEvent['event_end'], 'UTC');
- $this->end->setTimezone($tz_local);
- } else {
- $this->start = new Horde_Date($SQLEvent['event_start']);
- $this->end = new Horde_Date($SQLEvent['event_end']);
- }
-
- $this->durMin = ($this->end->timestamp() - $this->start->timestamp()) / 60;
-
- $this->title = $driver->convertFromDriver($SQLEvent['event_title']);
- $this->eventID = $SQLEvent['event_id'];
- $this->setUID($SQLEvent['event_uid']);
- $this->creatorID = $SQLEvent['event_creator_id'];
-
- if (!empty($SQLEvent['event_recurtype'])) {
- $this->recurrence = new Horde_Date_Recurrence($this->start);
- $this->recurrence->setRecurType((int)$SQLEvent['event_recurtype']);
- $this->recurrence->setRecurInterval((int)$SQLEvent['event_recurinterval']);
- if (isset($SQLEvent['event_recurenddate'])) {
- if ($driver->getParam('utc')) {
- $recur_end = new Horde_Date($SQLEvent['event_recurenddate'], 'UTC');
- if ($recur_end->min == 0) {
- /* Old recurrence end date format. */
- $recur_end = new Horde_Date($SQLEvent['event_recurenddate']);
- $recur_end->hour = 23;
- $recur_end->min = 59;
- $recur_end->sec = 59;
- } else {
- $recur_end->setTimezone(date_default_timezone_get());
- }
- } else {
- $recur_end = new Horde_Date($SQLEvent['event_recurenddate']);
- $recur_end->hour = 23;
- $recur_end->min = 59;
- $recur_end->sec = 59;
- }
- $this->recurrence->setRecurEnd($recur_end);
- }
- if (isset($SQLEvent['event_recurcount'])) {
- $this->recurrence->setRecurCount((int)$SQLEvent['event_recurcount']);
- }
- if (isset($SQLEvent['event_recurdays'])) {
- $this->recurrence->recurData = (int)$SQLEvent['event_recurdays'];
- }
- if (!empty($SQLEvent['event_exceptions'])) {
- $this->recurrence->exceptions = explode(',', $SQLEvent['event_exceptions']);
- }
- }
-
- if (isset($SQLEvent['event_location'])) {
- $this->location = $driver->convertFromDriver($SQLEvent['event_location']);
- }
- if (isset($SQLEvent['event_private'])) {
- $this->private = (bool)($SQLEvent['event_private']);
- }
- if (isset($SQLEvent['event_status'])) {
- $this->status = $SQLEvent['event_status'];
- }
- if (isset($SQLEvent['event_attendees'])) {
- $this->attendees = array_change_key_case($driver->convertFromDriver(unserialize($SQLEvent['event_attendees'])));
- }
- if (isset($SQLEvent['event_description'])) {
- $this->description = $driver->convertFromDriver($SQLEvent['event_description']);
- }
- if (isset($SQLEvent['event_alarm'])) {
- $this->alarm = (int)$SQLEvent['event_alarm'];
- }
- if (isset($SQLEvent['event_alarm_methods'])) {
- $this->methods = $driver->convertFromDriver(unserialize($SQLEvent['event_alarm_methods']));
- }
- $this->initialized = true;
- $this->stored = true;
- }
-
- public function toDriver()
- {
- $driver = $this->getDriver();
-
- /* Basic fields. */
- $this->_properties['event_creator_id'] = $driver->convertToDriver($this->getCreatorId());
- $this->_properties['event_title'] = $driver->convertToDriver($this->title);
- $this->_properties['event_description'] = $driver->convertToDriver($this->getDescription());
- $this->_properties['event_location'] = $driver->convertToDriver($this->getLocation());
- $this->_properties['event_private'] = (int)$this->isPrivate();
- $this->_properties['event_status'] = $this->getStatus();
- $this->_properties['event_attendees'] = serialize($driver->convertToDriver($this->getAttendees()));
- $this->_properties['event_modified'] = $_SERVER['REQUEST_TIME'];
-
- if ($this->isAllDay()) {
- $this->_properties['event_start'] = $this->start->strftime('%Y-%m-%d %H:%M:%S');
- $this->_properties['event_end'] = $this->end->strftime('%Y-%m-%d %H:%M:%S');
- $this->_properties['event_allday'] = 1;
- } else {
- if ($driver->getParam('utc')) {
- $start = clone $this->start;
- $end = clone $this->end;
- $start->setTimezone('UTC');
- $end->setTimezone('UTC');
- } else {
- $start = $this->start;
- $end = $this->end;
- }
- $this->_properties['event_start'] = $start->strftime('%Y-%m-%d %H:%M:%S');
- $this->_properties['event_end'] = $end->strftime('%Y-%m-%d %H:%M:%S');
- $this->_properties['event_allday'] = 0;
- }
-
- /* Alarm. */
- $this->_properties['event_alarm'] = (int)$this->getAlarm();
-
- /* Alarm Notification Methods. */
- $this->_properties['event_alarm_methods'] = serialize($driver->convertToDriver($this->methods));
-
- /* Recurrence. */
- if (!$this->recurs()) {
- $this->_properties['event_recurtype'] = 0;
- } else {
- $recur = $this->recurrence->getRecurType();
- if ($this->recurrence->hasRecurEnd()) {
- if ($driver->getParam('utc')) {
- $recur_end = clone $this->recurrence->recurEnd;
- $recur_end->setTimezone('UTC');
- } else {
- $recur_end = $this->recurrence->recurEnd;
- }
- } else {
- $recur_end = new Horde_Date(array('year' => 9999, 'month' => 12, 'mday' => 31, 'hour' => 23, 'min' => 59, 'sec' => 59));
- }
-
- $this->_properties['event_recurtype'] = $recur;
- $this->_properties['event_recurinterval'] = $this->recurrence->getRecurInterval();
- $this->_properties['event_recurenddate'] = $recur_end->format('Y-m-d H:i:s');
- $this->_properties['event_recurcount'] = $this->recurrence->getRecurCount();
-
- switch ($recur) {
- case Horde_Date_Recurrence::RECUR_WEEKLY:
- $this->_properties['event_recurdays'] = $this->recurrence->getRecurOnDays();
- break;
- }
- $this->_properties['event_exceptions'] = implode(',', $this->recurrence->getExceptions());
- }
- }
-
- public function getProperties()
- {
- return $this->_properties;
- }
-
-}
--- /dev/null
+<?php
+/**
+ * Kronolith_Event defines a generic API for events.
+ *
+ * Copyright 1999-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ * @author Jan Schneider <jan@horde.org>
+ * @package Kronolith
+ */
+class Kronolith_Event
+{
+ /**
+ * Flag that is set to true if this event has data from either a storage
+ * backend or a form or other import method.
+ *
+ * @var boolean
+ */
+ public $initialized = false;
+
+ /**
+ * Flag that is set to true if this event exists in a storage driver.
+ *
+ * @var boolean
+ */
+ public $stored = false;
+
+ /**
+ * The driver unique identifier for this event.
+ *
+ * @var string
+ */
+ public $eventID = null;
+
+ /**
+ * The UID for this event.
+ *
+ * @var string
+ */
+ protected $_uid = null;
+
+ /**
+ * The iCalendar SEQUENCE for this event.
+ *
+ * @var integer
+ */
+ protected $_sequence = null;
+
+ /**
+ * The user id of the creator of the event.
+ *
+ * @var string
+ */
+ public $creatorID = null;
+
+ /**
+ * The title of this event.
+ *
+ * @var string
+ */
+ public $title = '';
+
+ /**
+ * The location this event occurs at.
+ *
+ * @var string
+ */
+ public $location = '';
+
+ /**
+ * The status of this event.
+ *
+ * @var integer
+ */
+ public $status = Kronolith::STATUS_CONFIRMED;
+
+ /**
+ * The description for this event
+ *
+ * @var string
+ */
+ public $description = '';
+
+ /**
+ * Remote description of this event (URL).
+ *
+ * @var string
+ */
+ public $remoteUrl = '';
+
+ /**
+ * Remote calendar name.
+ *
+ * @var string
+ */
+ public $remoteCal = '';
+
+ /**
+ * Whether the event is private.
+ *
+ * @var boolean
+ */
+ public $private = false;
+
+ /**
+ * This tag's events.
+ *
+ * @var mixed Array of tags or comma delimited string.
+ */
+ public $tags = array();
+
+ /**
+ * All the attendees of this event.
+ *
+ * This is an associative array where the keys are the email addresses
+ * of the attendees, and the values are also associative arrays with
+ * keys 'attendance' and 'response' pointing to the attendees' attendance
+ * and response values, respectively.
+ *
+ * @var array
+ */
+ public $attendees = array();
+
+ /**
+ * The start time of the event.
+ *
+ * @var Horde_Date
+ */
+ public $start;
+
+ /**
+ * The end time of the event.
+ *
+ * @var Horde_Date
+ */
+ public $end;
+
+ /**
+ * The duration of this event in minutes
+ *
+ * @var integer
+ */
+ public $durMin = 0;
+
+ /**
+ * Whether this is an all-day event.
+ *
+ * @var boolean
+ */
+ public $allday = false;
+
+ /**
+ * Number of minutes before the event starts to trigger an alarm.
+ *
+ * @var integer
+ */
+ public $alarm = 0;
+
+ /**
+ * The particular alarm methods overridden for this event.
+ *
+ * @var array
+ */
+ public $methods;
+
+ /**
+ * The identifier of the calender this event exists on.
+ *
+ * @var string
+ */
+ protected $_calendar;
+
+ /**
+ * The HTML background color to be used for this event.
+ *
+ * @var string
+ */
+ protected $_backgroundColor;
+
+ /**
+ * The HTML foreground color to be used for this event.
+ *
+ * @var string
+ */
+ protected $_foregroundColor;
+
+ /**
+ * The VarRenderer class to use for printing select elements.
+ *
+ * @var Horde_UI_VarRenderer
+ */
+ private $_varRenderer;
+
+ /**
+ * The Horde_Date_Recurrence class for this event.
+ *
+ * @var Horde_Date_Recurrence
+ */
+ public $recurrence;
+
+ /**
+ * Constructor.
+ *
+ * @param Kronolith_Driver $driver The backend driver that this event is
+ * stored in.
+ * @param mixed $eventObject Backend specific event object
+ * that this will represent.
+ */
+ public function __construct(&$driver, $eventObject = null)
+ {
+ static $alarm;
+
+ /* Set default alarm value. */
+ if (!isset($alarm) && isset($GLOBALS['prefs'])) {
+ $alarm = $GLOBALS['prefs']->getValue('default_alarm');
+ }
+ $this->alarm = $alarm;
+
+ $this->_calendar = $driver->getCalendar();
+ if (!empty($this->_calendar)) {
+ $share = $GLOBALS['all_calendars'][$this->_calendar];
+ $this->_backgroundColor = $share->get('color');
+ if (empty($this->_backgroundColor)) {
+ $this->_backgroundColor = '#dddddd';
+ }
+ $this->_foregroundColor = Horde_Image::brightness($this->_backgroundColor) < 128 ? '#f6f6f6' : '#000';
+ }
+
+ if ($eventObject !== null) {
+ $this->fromDriver($eventObject);
+ $tagger = Kronolith::getTagger();
+ $this->tags = $tagger->getTags($this->getUID(), 'event');
+ }
+ }
+
+ /**
+ * Returns a reference to a driver that's valid for this event.
+ *
+ * @return Kronolith_Driver A driver that this event can use to save
+ * itself, etc.
+ */
+ public function getDriver()
+ {
+ global $kronolith_driver;
+ if ($kronolith_driver->getCalendar() != $this->_calendar) {
+ $kronolith_driver->open($this->_calendar);
+ }
+
+ return $kronolith_driver;
+ }
+
+ /**
+ * Returns the share this event belongs to.
+ *
+ * @return Horde_Share This event's share.
+ */
+ public function getShare()
+ {
+ if (isset($GLOBALS['all_calendars'][$this->getCalendar()])) {
+ $share = $GLOBALS['all_calendars'][$this->getCalendar()];
+ } else {
+ $share = PEAR::raiseError('Share not found');
+ }
+ return $share;
+ }
+
+ /**
+ * Encapsulates permissions checking.
+ *
+ * @param integer $permission The permission to check for.
+ * @param string $user The user to check permissions for.
+ *
+ * @return boolean
+ */
+ public function hasPermission($permission, $user = null)
+ {
+ if ($user === null) {
+ $user = Auth::getAuth();
+ }
+
+ if ($this->remoteCal) {
+ switch ($permission) {
+ case PERMS_SHOW:
+ case PERMS_READ:
+ case PERMS_EDIT:
+ return true;
+
+ default:
+ return false;
+ }
+ }
+
+ return (!is_a($share = &$this->getShare(), 'PEAR_Error') &&
+ $share->hasPermission($user, $permission, $this->getCreatorId()));
+ }
+
+ /**
+ * Saves changes to this event.
+ *
+ * @return mixed True or a PEAR_Error on failure.
+ */
+ public function save()
+ {
+ if (!$this->isInitialized()) {
+ return PEAR::raiseError('Event not yet initialized');
+ }
+
+ $this->toDriver();
+ $driver = &$this->getDriver();
+ $result = $driver->saveEvent($this);
+ if (!is_a($result, 'PEAR_Error') &&
+ !empty($GLOBALS['conf']['alarms']['driver'])) {
+ $alarm = $this->toAlarm(new Horde_Date($_SERVER['REQUEST_TIME']));
+ if ($alarm) {
+ $alarm['start'] = new Horde_Date($alarm['start']);
+ $alarm['end'] = new Horde_Date($alarm['end']);
+ $horde_alarm = Horde_Alarm::factory();
+ $horde_alarm->set($alarm);
+ }
+ }
+
+ return $result;
+ }
+
+ /**
+ * Exports this event in iCalendar format.
+ *
+ * @param Horde_iCalendar &$calendar A Horde_iCalendar object that acts as
+ * a container.
+ *
+ * @return Horde_iCalendar_vevent The vEvent object for this event.
+ */
+ public function toiCalendar(&$calendar)
+ {
+ $vEvent = &Horde_iCalendar::newComponent('vevent', $calendar);
+ $v1 = $calendar->getAttribute('VERSION') == '1.0';
+
+ if ($this->isAllDay()) {
+ $vEvent->setAttribute('DTSTART', $this->start, array('VALUE' => 'DATE'));
+ $vEvent->setAttribute('DTEND', $this->end, array('VALUE' => 'DATE'));
+ } else {
+ $vEvent->setAttribute('DTSTART', $this->start);
+ $vEvent->setAttribute('DTEND', $this->end);
+ }
+
+ $vEvent->setAttribute('DTSTAMP', $_SERVER['REQUEST_TIME']);
+ $vEvent->setAttribute('UID', $this->_uid);
+
+ /* Get the event's history. */
+ $history = &Horde_History::singleton();
+ $created = $modified = null;
+ $log = $history->getHistory('kronolith:' . $this->_calendar . ':' . $this->_uid);
+ if ($log && !is_a($log, 'PEAR_Error')) {
+ foreach ($log->getData() as $entry) {
+ switch ($entry['action']) {
+ case 'add':
+ $created = $entry['ts'];
+ break;
+
+ case 'modify':
+ $modified = $entry['ts'];
+ break;
+ }
+ }
+ }
+ if (!empty($created)) {
+ $vEvent->setAttribute($v1 ? 'DCREATED' : 'CREATED', $created);
+ if (empty($modified)) {
+ $modified = $created;
+ }
+ }
+ if (!empty($modified)) {
+ $vEvent->setAttribute('LAST-MODIFIED', $modified);
+ }
+
+ $vEvent->setAttribute('SUMMARY', $v1 ? $this->getTitle() : String::convertCharset($this->getTitle(), NLS::getCharset(), 'utf-8'));
+ $name = Kronolith::getUserName($this->getCreatorId());
+ if (!$v1) {
+ $name = String::convertCharset($name, NLS::getCharset(), 'utf-8');
+ }
+ $vEvent->setAttribute('ORGANIZER',
+ 'mailto:' . Kronolith::getUserEmail($this->getCreatorId()),
+ array('CN' => $name));
+ if (!$this->isPrivate() || $this->getCreatorId() == Auth::getAuth()) {
+ if (!empty($this->description)) {
+ $vEvent->setAttribute('DESCRIPTION', $v1 ? $this->description : String::convertCharset($this->description, NLS::getCharset(), 'utf-8'));
+ }
+
+ // Tags
+ $tags = $this->tags;
+ if (is_array($tags)) {
+ $tags = implode(', ', $tags);
+ }
+ if (!empty($tags)) {
+ $vEvent->setAttribute('CATEGORIES', $v1 ? $tags : String::convertCharset($tags, NLS::getCharset(), 'utf-8'));
+ }
+
+ // Location
+ if (!empty($this->location)) {
+ $vEvent->setAttribute('LOCATION', $v1 ? $this->location : String::convertCharset($this->location, NLS::getCharset(), 'utf-8'));
+ }
+ }
+ $vEvent->setAttribute('CLASS', $this->isPrivate() ? 'PRIVATE' : 'PUBLIC');
+
+ // Status.
+ switch ($this->getStatus()) {
+ case Kronolith::STATUS_FREE:
+ // This is not an official iCalendar value, but we need it for
+ // synchronization.
+ $vEvent->setAttribute('STATUS', 'FREE');
+ $vEvent->setAttribute('TRANSP', $v1 ? 1 : 'TRANSPARENT');
+ break;
+ case Kronolith::STATUS_TENTATIVE:
+ $vEvent->setAttribute('STATUS', 'TENTATIVE');
+ $vEvent->setAttribute('TRANSP', $v1 ? 0 : 'OPAQUE');
+ break;
+ case Kronolith::STATUS_CONFIRMED:
+ $vEvent->setAttribute('STATUS', 'CONFIRMED');
+ $vEvent->setAttribute('TRANSP', $v1 ? 0 : 'OPAQUE');
+ break;
+ case Kronolith::STATUS_CANCELLED:
+ if ($v1) {
+ $vEvent->setAttribute('STATUS', 'DECLINED');
+ $vEvent->setAttribute('TRANSP', 1);
+ } else {
+ $vEvent->setAttribute('STATUS', 'CANCELLED');
+ $vEvent->setAttribute('TRANSP', 'TRANSPARENT');
+ }
+ break;
+ }
+
+ // Attendees.
+ foreach ($this->getAttendees() as $email => $status) {
+ $params = array();
+ switch ($status['attendance']) {
+ case Kronolith::PART_REQUIRED:
+ if ($v1) {
+ $params['EXPECT'] = 'REQUIRE';
+ } else {
+ $params['ROLE'] = 'REQ-PARTICIPANT';
+ }
+ break;
+
+ case Kronolith::PART_OPTIONAL:
+ if ($v1) {
+ $params['EXPECT'] = 'REQUEST';
+ } else {
+ $params['ROLE'] = 'OPT-PARTICIPANT';
+ }
+ break;
+
+ case Kronolith::PART_NONE:
+ if ($v1) {
+ $params['EXPECT'] = 'FYI';
+ } else {
+ $params['ROLE'] = 'NON-PARTICIPANT';
+ }
+ break;
+ }
+
+ switch ($status['response']) {
+ case Kronolith::RESPONSE_NONE:
+ if ($v1) {
+ $params['STATUS'] = 'NEEDS ACTION';
+ $params['RSVP'] = 'YES';
+ } else {
+ $params['PARTSTAT'] = 'NEEDS-ACTION';
+ $params['RSVP'] = 'TRUE';
+ }
+ break;
+
+ case Kronolith::RESPONSE_ACCEPTED:
+ if ($v1) {
+ $params['STATUS'] = 'ACCEPTED';
+ } else {
+ $params['PARTSTAT'] = 'ACCEPTED';
+ }
+ break;
+
+ case Kronolith::RESPONSE_DECLINED:
+ if ($v1) {
+ $params['STATUS'] = 'DECLINED';
+ } else {
+ $params['PARTSTAT'] = 'DECLINED';
+ }
+ break;
+
+ case Kronolith::RESPONSE_TENTATIVE:
+ if ($v1) {
+ $params['STATUS'] = 'TENTATIVE';
+ } else {
+ $params['PARTSTAT'] = 'TENTATIVE';
+ }
+ break;
+ }
+
+ if (strpos($email, '@') === false) {
+ $email = '';
+ }
+ if ($v1) {
+ if (!empty($status['name'])) {
+ if (!empty($email)) {
+ $email = ' <' . $email . '>';
+ }
+ $email = $status['name'] . $email;
+ $email = Horde_Mime_Address::trimAddress($email);
+ }
+ } else {
+ if (!empty($status['name'])) {
+ $params['CN'] = String::convertCharset($status['name'], NLS::getCharset(), 'utf-8');
+ }
+ if (!empty($email)) {
+ $email = 'mailto:' . $email;
+ }
+ }
+
+ $vEvent->setAttribute('ATTENDEE', $email, $params);
+ }
+
+ // Alarms.
+ if (!empty($this->alarm)) {
+ if ($v1) {
+ $alarm = new Horde_Date($this->start);
+ $alarm->min -= $this->alarm;
+ $vEvent->setAttribute('AALARM', $alarm);
+ } else {
+ $vAlarm = &Horde_iCalendar::newComponent('valarm', $vEvent);
+ $vAlarm->setAttribute('ACTION', 'DISPLAY');
+ $vAlarm->setAttribute('TRIGGER;VALUE=DURATION', '-PT' . $this->alarm . 'M');
+ $vEvent->addComponent($vAlarm);
+ }
+ }
+
+ // Recurrence.
+ if ($this->recurs()) {
+ if ($v1) {
+ $rrule = $this->recurrence->toRRule10($calendar);
+ } else {
+ $rrule = $this->recurrence->toRRule20($calendar);
+ }
+ if (!empty($rrule)) {
+ $vEvent->setAttribute('RRULE', $rrule);
+ }
+
+ // Exceptions.
+ $exceptions = $this->recurrence->getExceptions();
+ foreach ($exceptions as $exception) {
+ if (!empty($exception)) {
+ list($year, $month, $mday) = sscanf($exception, '%04d%02d%02d');
+ $exdate = new Horde_Date(array(
+ 'year' => $year,
+ 'month' => $month,
+ 'mday' => $mday,
+ 'hour' => $this->start->hour,
+ 'min' => $this->start->min,
+ 'sec' => $this->start->sec,
+ ));
+ $vEvent->setAttribute('EXDATE', array($exdate));
+ }
+ }
+ }
+
+ return $vEvent;
+ }
+
+ /**
+ * Updates the properties of this event from a Horde_iCalendar_vevent
+ * object.
+ *
+ * @param Horde_iCalendar_vevent $vEvent The iCalendar data to update
+ * from.
+ */
+ public function fromiCalendar($vEvent)
+ {
+ // Unique ID.
+ $uid = $vEvent->getAttribute('UID');
+ if (!empty($uid) && !is_a($uid, 'PEAR_Error')) {
+ $this->setUID($uid);
+ }
+
+ // Sequence.
+ $seq = $vEvent->getAttribute('SEQUENCE');
+ if (is_int($seq)) {
+ $this->_sequence = $seq;
+ }
+
+ // Title, tags and description.
+ $title = $vEvent->getAttribute('SUMMARY');
+ if (!is_array($title) && !is_a($title, 'PEAR_Error')) {
+ $this->setTitle($title);
+ }
+
+ // Tags
+ $categories = $vEvent->getAttributeValues('CATEGORIES');
+ if (!is_a($categories, 'PEAR_Error')) {
+ $this->tags = $categories;
+ }
+
+ // Description
+ $desc = $vEvent->getAttribute('DESCRIPTION');
+ if (!is_array($desc) && !is_a($desc, 'PEAR_Error')) {
+ $this->setDescription($desc);
+ }
+
+ // Remote Url
+ $url = $vEvent->getAttribute('URL');
+ if (!is_array($url) && !is_a($url, 'PEAR_Error')) {
+ $this->remoteUrl = $url;
+ }
+
+ // Location
+ $location = $vEvent->getAttribute('LOCATION');
+ if (!is_array($location) && !is_a($location, 'PEAR_Error')) {
+ $this->setLocation($location);
+ }
+
+ // Class
+ $class = $vEvent->getAttribute('CLASS');
+ if (!is_array($class) && !is_a($class, 'PEAR_Error')) {
+ $class = String::upper($class);
+ if ($class == 'PRIVATE' || $class == 'CONFIDENTIAL') {
+ $this->setPrivate(true);
+ } else {
+ $this->setPrivate(false);
+ }
+ }
+
+ // Status.
+ $status = $vEvent->getAttribute('STATUS');
+ if (!is_array($status) && !is_a($status, 'PEAR_Error')) {
+ $status = String::upper($status);
+ if ($status == 'DECLINED') {
+ $status = 'CANCELLED';
+ }
+ if (defined('Kronolith::STATUS_' . $status)) {
+ $this->setStatus(constant('Kronolith::STATUS_' . $status));
+ }
+ }
+
+ // Start and end date.
+ $start = $vEvent->getAttribute('DTSTART');
+ if (!is_a($start, 'PEAR_Error')) {
+ if (!is_array($start)) {
+ // Date-Time field
+ $this->start = new Horde_Date($start);
+ } else {
+ // Date field
+ $this->start = new Horde_Date(
+ array('year' => (int)$start['year'],
+ 'month' => (int)$start['month'],
+ 'mday' => (int)$start['mday']));
+ }
+ }
+ $end = $vEvent->getAttribute('DTEND');
+ if (!is_a($end, 'PEAR_Error')) {
+ if (!is_array($end)) {
+ // Date-Time field
+ $this->end = new Horde_Date($end);
+ // All day events are transferred by many device as
+ // DSTART: YYYYMMDDT000000 DTEND: YYYYMMDDT2359(59|00)
+ // Convert accordingly
+ if (is_object($this->start) && $this->start->hour == 0 &&
+ $this->start->min == 0 && $this->start->sec == 0 &&
+ $this->end->hour == 23 && $this->end->min == 59) {
+ $this->end = new Horde_Date(
+ array('year' => (int)$this->end->year,
+ 'month' => (int)$this->end->month,
+ 'mday' => (int)$this->end->mday + 1));
+ }
+ } elseif (is_array($end) && !is_a($end, 'PEAR_Error')) {
+ // Date field
+ $this->end = new Horde_Date(
+ array('year' => (int)$end['year'],
+ 'month' => (int)$end['month'],
+ 'mday' => (int)$end['mday']));
+ }
+ } else {
+ $duration = $vEvent->getAttribute('DURATION');
+ if (!is_array($duration) && !is_a($duration, 'PEAR_Error')) {
+ $this->end = new Horde_Date($this->start);
+ $this->end->sec += $duration;
+ } else {
+ // End date equal to start date as per RFC 2445.
+ $this->end = new Horde_Date($this->start);
+ if (is_array($start)) {
+ // Date field
+ $this->end->mday++;
+ }
+ }
+ }
+
+ // vCalendar 1.0 alarms
+ $alarm = $vEvent->getAttribute('AALARM');
+ if (!is_array($alarm) &&
+ !is_a($alarm, 'PEAR_Error') &&
+ intval($alarm)) {
+ $this->alarm = intval(($this->start->timestamp() - $alarm) / 60);
+ }
+
+ // @TODO: vCalendar 2.0 alarms
+
+ // Attendance.
+ // Importing attendance may result in confusion: editing an imported
+ // copy of an event can cause invitation updates to be sent from
+ // people other than the original organizer. So we don't import by
+ // default. However to allow updates by SyncML replication, the custom
+ // X-ATTENDEE attribute is used which has the same syntax as
+ // ATTENDEE.
+ $attendee = $vEvent->getAttribute('X-ATTENDEE');
+ if (!is_a($attendee, 'PEAR_Error')) {
+
+ if (!is_array($attendee)) {
+ $attendee = array($attendee);
+ }
+ $params = $vEvent->getAttribute('X-ATTENDEE', true);
+ if (!is_array($params)) {
+ $params = array($params);
+ }
+ for ($i = 0; $i < count($attendee); ++$i) {
+ $attendee[$i] = str_replace(array('MAILTO:', 'mailto:'), '',
+ $attendee[$i]);
+ $email = Horde_Mime_Address::bareAddress($attendee[$i]);
+ // Default according to rfc2445:
+ $attendance = Kronolith::PART_REQUIRED;
+ // vCalendar 2.0 style:
+ if (!empty($params[$i]['ROLE'])) {
+ switch($params[$i]['ROLE']) {
+ case 'OPT-PARTICIPANT':
+ $attendance = Kronolith::PART_OPTIONAL;
+ break;
+
+ case 'NON-PARTICIPANT':
+ $attendance = Kronolith::PART_NONE;
+ break;
+ }
+ }
+ // vCalendar 1.0 style;
+ if (!empty($params[$i]['EXPECT'])) {
+ switch($params[$i]['EXPECT']) {
+ case 'REQUEST':
+ $attendance = Kronolith::PART_OPTIONAL;
+ break;
+
+ case 'FYI':
+ $attendance = Kronolith::PART_NONE;
+ break;
+ }
+ }
+ $response = Kronolith::RESPONSE_NONE;
+ if (empty($params[$i]['PARTSTAT']) &&
+ !empty($params[$i]['STATUS'])) {
+ $params[$i]['PARTSTAT'] = $params[$i]['STATUS'];
+ }
+
+ if (!empty($params[$i]['PARTSTAT'])) {
+ switch($params[$i]['PARTSTAT']) {
+ case 'ACCEPTED':
+ $response = Kronolith::RESPONSE_ACCEPTED;
+ break;
+
+ case 'DECLINED':
+ $response = Kronolith::RESPONSE_DECLINED;
+ break;
+
+ case 'TENTATIVE':
+ $response = Kronolith::RESPONSE_TENTATIVE;
+ break;
+ }
+ }
+ $name = isset($params[$i]['CN']) ? $params[$i]['CN'] : null;
+
+ $this->addAttendee($email, $attendance, $response, $name);
+ }
+ }
+
+ // Recurrence.
+ $rrule = $vEvent->getAttribute('RRULE');
+ if (!is_array($rrule) && !is_a($rrule, 'PEAR_Error')) {
+ $this->recurrence = new Horde_Date_Recurrence($this->start);
+ if (strpos($rrule, '=') !== false) {
+ $this->recurrence->fromRRule20($rrule);
+ } else {
+ $this->recurrence->fromRRule10($rrule);
+ }
+
+ // Exceptions.
+ $exdates = $vEvent->getAttributeValues('EXDATE');
+ if (is_array($exdates)) {
+ foreach ($exdates as $exdate) {
+ if (is_array($exdate)) {
+ $this->recurrence->addException((int)$exdate['year'],
+ (int)$exdate['month'],
+ (int)$exdate['mday']);
+ }
+ }
+ }
+ }
+
+ $this->initialized = true;
+ }
+
+ /**
+ * Imports the values for this event from an array of values.
+ *
+ * @param array $hash Array containing all the values.
+ */
+ public function fromHash($hash)
+ {
+ // See if it's a new event.
+ if ($this->getId() === null) {
+ $this->setCreatorId(Auth::getAuth());
+ }
+ if (!empty($hash['title'])) {
+ $this->setTitle($hash['title']);
+ } else {
+ return PEAR::raiseError(_("Events must have a title."));
+ }
+ if (!empty($hash['description'])) {
+ $this->setDescription($hash['description']);
+ }
+ if (!empty($hash['location'])) {
+ $this->setLocation($hash['location']);
+ }
+ if (!empty($hash['start_date'])) {
+ $date = explode('-', $hash['start_date']);
+ if (empty($hash['start_time'])) {
+ $time = array(0, 0, 0);
+ } else {
+ $time = explode(':', $hash['start_time']);
+ if (count($time) == 2) {
+ $time[2] = 0;
+ }
+ }
+ if (count($time) == 3 && count($date) == 3) {
+ $this->start = new Horde_Date(array('year' => $date[0],
+ 'month' => $date[1],
+ 'mday' => $date[2],
+ 'hour' => $time[0],
+ 'min' => $time[1],
+ 'sec' => $time[2]));
+ }
+ } else {
+ return PEAR::raiseError(_("Events must have a start date."));
+ }
+ if (empty($hash['duration'])) {
+ if (empty($hash['end_date'])) {
+ $hash['end_date'] = $hash['start_date'];
+ }
+ if (empty($hash['end_time'])) {
+ $hash['end_time'] = $hash['start_time'];
+ }
+ } else {
+ $weeks = str_replace('W', '', $hash['duration'][1]);
+ $days = str_replace('D', '', $hash['duration'][2]);
+ $hours = str_replace('H', '', $hash['duration'][4]);
+ $minutes = isset($hash['duration'][5]) ? str_replace('M', '', $hash['duration'][5]) : 0;
+ $seconds = isset($hash['duration'][6]) ? str_replace('S', '', $hash['duration'][6]) : 0;
+ $hash['duration'] = ($weeks * 60 * 60 * 24 * 7) + ($days * 60 * 60 * 24) + ($hours * 60 * 60) + ($minutes * 60) + $seconds;
+ $this->end = new Horde_Date($this->start);
+ $this->end->sec += $hash['duration'];
+ }
+ if (!empty($hash['end_date'])) {
+ $date = explode('-', $hash['end_date']);
+ if (empty($hash['end_time'])) {
+ $time = array(0, 0, 0);
+ } else {
+ $time = explode(':', $hash['end_time']);
+ if (count($time) == 2) {
+ $time[2] = 0;
+ }
+ }
+ if (count($time) == 3 && count($date) == 3) {
+ $this->end = new Horde_Date(array('year' => $date[0],
+ 'month' => $date[1],
+ 'mday' => $date[2],
+ 'hour' => $time[0],
+ 'min' => $time[1],
+ 'sec' => $time[2]));
+ }
+ }
+ if (!empty($hash['alarm'])) {
+ $this->setAlarm($hash['alarm']);
+ } elseif (!empty($hash['alarm_date']) &&
+ !empty($hash['alarm_time'])) {
+ $date = explode('-', $hash['alarm_date']);
+ $time = explode(':', $hash['alarm_time']);
+ if (count($time) == 2) {
+ $time[2] = 0;
+ }
+ if (count($time) == 3 && count($date) == 3) {
+ $alarm = new Horde_Date(array('hour' => $time[0],
+ 'min' => $time[1],
+ 'sec' => $time[2],
+ 'month' => $date[1],
+ 'mday' => $date[2],
+ 'year' => $date[0]));
+ $this->setAlarm(($this->start->timestamp() - $alarm->timestamp()) / 60);
+ }
+ }
+ if (!empty($hash['recur_type'])) {
+ $this->recurrence = new Horde_Date_Recurrence($this->start);
+ $this->recurrence->setRecurType($hash['recur_type']);
+ if (!empty($hash['recur_end_date'])) {
+ $date = explode('-', $hash['recur_end_date']);
+ $this->recurrence->setRecurEnd(new Horde_Date(array('year' => $date[0], 'month' => $date[1], 'mday' => $date[2])));
+ }
+ if (!empty($hash['recur_interval'])) {
+ $this->recurrence->setRecurInterval($hash['recur_interval']);
+ }
+ if (!empty($hash['recur_data'])) {
+ $this->recurrence->setRecurOnDay($hash['recur_data']);
+ }
+ }
+
+ $this->initialized = true;
+ }
+
+ /**
+ * Returns an alarm hash of this event suitable for Horde_Alarm.
+ *
+ * @param Horde_Date $time Time of alarm.
+ * @param string $user The user to return alarms for.
+ * @param Prefs $prefs A Prefs instance.
+ *
+ * @return array Alarm hash or null.
+ */
+ public function toAlarm($time, $user = null, $prefs = null)
+ {
+ if (!$this->getAlarm()) {
+ return;
+ }
+
+ if ($this->recurs()) {
+ $eventDate = $this->recurrence->nextRecurrence($time);
+ if ($eventDate && $this->recurrence->hasException($eventDate->year, $eventDate->month, $eventDate->mday)) {
+ return;
+ }
+ }
+
+ if (empty($user)) {
+ $user = Auth::getAuth();
+ }
+ if (empty($prefs)) {
+ $prefs = $GLOBALS['prefs'];
+ }
+
+ $methods = !empty($this->methods) ? $this->methods : @unserialize($prefs->getValue('event_alarms'));
+ $start = clone $this->start;
+ $start->min -= $this->getAlarm();
+ if (isset($methods['notify'])) {
+ $methods['notify']['show'] = array(
+ '__app' => $GLOBALS['registry']->getApp(),
+ 'event' => $this->getId(),
+ 'calendar' => $this->getCalendar());
+ if (!empty($methods['notify']['sound'])) {
+ if ($methods['notify']['sound'] == 'on') {
+ // Handle boolean sound preferences.
+ $methods['notify']['sound'] = $GLOBALS['registry']->get('themesuri') . '/sounds/theetone.wav';
+ } else {
+ // Else we know we have a sound name that can be
+ // served from Horde.
+ $methods['notify']['sound'] = $GLOBALS['registry']->get('themesuri', 'horde') . '/sounds/' . $methods['notify']['sound'];
+ }
+ }
+ }
+ if (isset($methods['popup'])) {
+ $methods['popup']['message'] = $this->getTitle($user);
+ $description = $this->getDescription();
+ if (!empty($description)) {
+ $methods['popup']['message'] .= "\n\n" . $description;
+ }
+ }
+ if (isset($methods['mail'])) {
+ $methods['mail']['body'] = sprintf(
+ _("We would like to remind you of this upcoming event.\n\n%s\n\nLocation: %s\n\nDate: %s\nTime: %s\n\n%s"),
+ $this->getTitle($user),
+ $this->location,
+ $this->start->strftime($prefs->getValue('date_format')),
+ $this->start->format($prefs->getValue('twentyFour') ? 'H:i' : 'h:ia'),
+ $this->getDescription());
+ }
+
+ return array(
+ 'id' => $this->getUID(),
+ 'user' => $user,
+ 'start' => $start->timestamp(),
+ 'end' => $this->end->timestamp(),
+ 'methods' => array_keys($methods),
+ 'params' => $methods,
+ 'title' => $this->getTitle($user),
+ 'text' => $this->getDescription());
+ }
+
+ /**
+ * Returns a simple object suitable for json transport representing this
+ * event.
+ *
+ * @return object A simple object.
+ */
+ public function toJSON()
+ {
+ $json = new stdClass;
+ $json->t = $this->getTitle();
+ $json->c = $this->getCalendar();
+ $json->bg = $this->_backgroundColor;
+ $json->fg = $this->_foregroundColor;
+ return $json;
+ }
+
+ /**
+ * TODO
+ */
+ public function isInitialized()
+ {
+ return $this->initialized;
+ }
+
+ /**
+ * TODO
+ */
+ public function isStored()
+ {
+ return $this->stored;
+ }
+
+ /**
+ * Checks if the current event is already present in the calendar.
+ *
+ * Does the check based on the uid.
+ *
+ * @return boolean True if event exists, false otherwise.
+ */
+ public function exists()
+ {
+ if (!isset($this->_uid) || !isset($this->_calendar)) {
+ return false;
+ }
+
+ $eventID = $GLOBALS['kronolith_driver']->exists($this->_uid, $this->_calendar);
+ if (is_a($eventID, 'PEAR_Error') || !$eventID) {
+ return false;
+ } else {
+ $this->eventID = $eventID;
+ return true;
+ }
+ }
+
+ public function getDuration()
+ {
+ static $duration = null;
+ if (isset($duration)) {
+ return $duration;
+ }
+
+ if ($this->start && $this->end) {
+ $dur_day_match = Date_Calc::dateDiff($this->start->mday,
+ $this->start->month,
+ $this->start->year,
+ $this->end->mday,
+ $this->end->month,
+ $this->end->year);
+ $dur_hour_match = $this->end->hour - $this->start->hour;
+ $dur_min_match = $this->end->min - $this->start->min;
+ while ($dur_min_match < 0) {
+ $dur_min_match += 60;
+ --$dur_hour_match;
+ }
+ while ($dur_hour_match < 0) {
+ $dur_hour_match += 24;
+ --$dur_day_match;
+ }
+ if ($dur_hour_match == 0 && $dur_min_match == 0 &&
+ $this->end->mday - $this->start->mday == 1) {
+ $dur_day_match = 1;
+ $dur_hour_match = 0;
+ $dur_min_match = 0;
+ $whole_day_match = true;
+ } else {
+ $whole_day_match = false;
+ }
+ } else {
+ $dur_day_match = 0;
+ $dur_hour_match = 1;
+ $dur_min_match = 0;
+ $whole_day_match = false;
+ }
+
+ $duration = new stdClass;
+ $duration->day = $dur_day_match;
+ $duration->hour = $dur_hour_match;
+ $duration->min = $dur_min_match;
+ $duration->wholeDay = $whole_day_match;
+
+ return $duration;
+ }
+
+ /**
+ * Returns whether this event is a recurring event.
+ *
+ * @return boolean True if this is a recurring event.
+ */
+ public function recurs()
+ {
+ return isset($this->recurrence) &&
+ !$this->recurrence->hasRecurType(Horde_Date_Recurrence::RECUR_NONE);
+ }
+
+ /**
+ * Returns a description of this event's recurring type.
+ *
+ * @return string Human readable recurring type.
+ */
+ public function getRecurName()
+ {
+ return $this->recurs()
+ ? $this->recurrence->getRecurName()
+ : _("No recurrence");
+ }
+
+ /**
+ * Returns a correcty formatted exception date for recurring events and a
+ * link to delete this exception.
+ *
+ * @param string $date Exception in the format Ymd.
+ *
+ * @return string The formatted date and delete link.
+ */
+ public function exceptionLink($date)
+ {
+ if (!preg_match('/(\d{4})(\d{2})(\d{2})/', $date, $match)) {
+ return '';
+ }
+ $horde_date = new Horde_Date(array('year' => $match[1],
+ 'month' => $match[2],
+ 'mday' => $match[3]));
+ $formatted = $horde_date->strftime($GLOBALS['prefs']->getValue('date_format'));
+ return $formatted
+ . Horde::link(Util::addParameter(Horde::applicationUrl('edit.php'), array('calendar' => $this->getCalendar(), 'eventID' => $this->eventID, 'del_exception' => $date, 'url' => Util::getFormData('url'))), sprintf(_("Delete exception on %s"), $formatted))
+ . Horde::img('delete-small.png', _("Delete"), '', $GLOBALS['registry']->getImageDir('horde'))
+ . '</a>';
+ }
+
+ /**
+ * Returns a list of exception dates for recurring events including links
+ * to delete them.
+ *
+ * @return string List of exception dates and delete links.
+ */
+ public function exceptionsList()
+ {
+ return implode(', ', array_map(array($this, 'exceptionLink'), $this->recurrence->getExceptions()));
+ }
+
+ public function getCalendar()
+ {
+ return $this->_calendar;
+ }
+
+ public function setCalendar($calendar)
+ {
+ $this->_calendar = $calendar;
+ }
+
+ public function isRemote()
+ {
+ return (bool)$this->remoteCal;
+ }
+
+ /**
+ * Returns the locally unique identifier for this event.
+ *
+ * @return string The local identifier for this event.
+ */
+ public function getId()
+ {
+ return $this->eventID;
+ }
+
+ /**
+ * Sets the locally unique identifier for this event.
+ *
+ * @param string $eventId The local identifier for this event.
+ */
+ public function setId($eventId)
+ {
+ if (substr($eventId, 0, 10) == 'kronolith:') {
+ $eventId = substr($eventId, 10);
+ }
+ $this->eventID = $eventId;
+ }
+
+ /**
+ * Returns the global UID for this event.
+ *
+ * @return string The global UID for this event.
+ */
+ public function getUID()
+ {
+ return $this->_uid;
+ }
+
+ /**
+ * Sets the global UID for this event.
+ *
+ * @param string $uid The global UID for this event.
+ */
+ public function setUID($uid)
+ {
+ $this->_uid = $uid;
+ }
+
+ /**
+ * Returns the iCalendar SEQUENCE for this event.
+ *
+ * @return integer The sequence for this event.
+ */
+ public function getSequence()
+ {
+ return $this->_sequence;
+ }
+
+ /**
+ * Returns the id of the user who created the event.
+ *
+ * @return string The creator id
+ */
+ public function getCreatorId()
+ {
+ return !empty($this->creatorID) ? $this->creatorID : Auth::getAuth();
+ }
+
+ /**
+ * Sets the id of the creator of the event.
+ *
+ * @param string $creatorID The user id for the user who created the event
+ */
+ public function setCreatorId($creatorID)
+ {
+ $this->creatorID = $creatorID;
+ }
+
+ /**
+ * Returns the title of this event.
+ *
+ * @param string $user The current user.
+ *
+ * @return string The title of this event.
+ */
+ public function getTitle($user = null)
+ {
+ if (isset($this->external) ||
+ isset($this->contactID) ||
+ $this->remoteCal) {
+ return !empty($this->title) ? $this->title : _("[Unnamed event]");
+ }
+
+ if (!$this->isInitialized()) {
+ return '';
+ }
+
+ if ($user === null) {
+ $user = Auth::getAuth();
+ }
+
+ $twentyFour = $GLOBALS['prefs']->getValue('twentyFour');
+ $start = $this->start->format($twentyFour ? 'G:i' : 'g:ia');
+ $end = $this->end->format($twentyFour ? 'G:i' : 'g:ia');
+
+ // We explicitly allow admin access here for the alarms notifications.
+ if (!Auth::isAdmin() && $this->isPrivate() &&
+ $this->getCreatorId() != $user) {
+ return sprintf(_("Private Event from %s to %s"), $start, $end);
+ } elseif (Auth::isAdmin() || $this->hasPermission(PERMS_READ, $user)) {
+ return strlen($this->title) ? $this->title : _("[Unnamed event]");
+ } else {
+ return sprintf(_("Event from %s to %s"), $start, $end);
+ }
+ }
+
+ /**
+ * Sets the title of this event.
+ *
+ * @param string The new title for this event.
+ */
+ public function setTitle($title)
+ {
+ $this->title = $title;
+ }
+
+ /**
+ * Returns the description of this event.
+ *
+ * @return string The description of this event.
+ */
+ public function getDescription()
+ {
+ return $this->description;
+ }
+
+ /**
+ * Sets the description of this event.
+ *
+ * @param string $description The new description for this event.
+ */
+ public function setDescription($description)
+ {
+ $this->description = $description;
+ }
+
+ /**
+ * Returns the location this event occurs at.
+ *
+ * @return string The location of this event.
+ */
+ public function getLocation()
+ {
+ return $this->location;
+ }
+
+ /**
+ * Sets the location this event occurs at.
+ *
+ * @param string $location The new location for this event.
+ */
+ public function setLocation($location)
+ {
+ $this->location = $location;
+ }
+
+ /**
+ * Returns whether this event is private.
+ *
+ * @return boolean Whether this even is private.
+ */
+ public function isPrivate()
+ {
+ return $this->private;
+ }
+
+ /**
+ * Sets the private flag of this event.
+ *
+ * @param boolean $private Whether this event should be marked private.
+ */
+ public function setPrivate($private)
+ {
+ $this->private = !empty($private);
+ }
+
+ /**
+ * Returns the event status.
+ *
+ * @return integer The status of this event.
+ */
+ public function getStatus()
+ {
+ return $this->status;
+ }
+
+ /**
+ * Checks whether the events status is the same as the specified value.
+ *
+ * @param integer $status The status value to check against.
+ *
+ * @return boolean True if the events status is the same as $status.
+ */
+ public function hasStatus($status)
+ {
+ return ($status == $this->status);
+ }
+
+ /**
+ * Sets the status of this event.
+ *
+ * @param integer $status The new event status.
+ */
+ public function setStatus($status)
+ {
+ $this->status = $status;
+ }
+
+ /**
+ * Returns the entire attendees array.
+ *
+ * @return array A copy of the attendees array.
+ */
+ public function getAttendees()
+ {
+ return $this->attendees;
+ }
+
+ /**
+ * Checks to see whether the specified attendee is associated with the
+ * current event.
+ *
+ * @param string $email The email address of the attendee.
+ *
+ * @return boolean True if the specified attendee is present for this
+ * event.
+ */
+ public function hasAttendee($email)
+ {
+ $email = String::lower($email);
+ return isset($this->attendees[$email]);
+ }
+
+ /**
+ * Sets the entire attendee array.
+ *
+ * @param array $attendees The new attendees array. This should be of the
+ * correct format to avoid driver problems.
+ */
+ public function setAttendees($attendees)
+ {
+ $this->attendees = array_change_key_case($attendees);
+ }
+
+ /**
+ * Adds a new attendee to the current event.
+ *
+ * This will overwrite an existing attendee if one exists with the same
+ * email address.
+ *
+ * @param string $email The email address of the attendee.
+ * @param integer $attendance The attendance code of the attendee.
+ * @param integer $response The response code of the attendee.
+ * @param string $name The name of the attendee.
+ */
+ public function addAttendee($email, $attendance, $response, $name = null)
+ {
+ $email = String::lower($email);
+ if ($attendance == Kronolith::PART_IGNORE) {
+ if (isset($this->attendees[$email])) {
+ $attendance = $this->attendees[$email]['attendance'];
+ } else {
+ $attendance = Kronolith::PART_REQUIRED;
+ }
+ }
+ if (empty($name) && isset($this->attendees[$email]) &&
+ !empty($this->attendees[$email]['name'])) {
+ $name = $this->attendees[$email]['name'];
+ }
+
+ $this->attendees[$email] = array(
+ 'attendance' => $attendance,
+ 'response' => $response,
+ 'name' => $name
+ );
+ }
+
+ /**
+ * Removes the specified attendee from the current event.
+ *
+ * @param string $email The email address of the attendee.
+ */
+ public function removeAttendee($email)
+ {
+ $email = String::lower($email);
+ if (isset($this->attendees[$email])) {
+ unset($this->attendees[$email]);
+ }
+ }
+
+ public function isAllDay()
+ {
+ return $this->allday ||
+ ($this->start->hour == 0 && $this->start->min == 0 && $this->start->sec == 0 &&
+ (($this->end->hour == 0 && $this->end->min == 0 && $this->end->sec == 0) ||
+ ($this->end->hour == 23 && $this->end->min == 59)) &&
+ ($this->end->mday > $this->start->mday ||
+ $this->end->month > $this->start->month ||
+ $this->end->year > $this->start->year));
+ }
+
+ public function getAlarm()
+ {
+ return $this->alarm;
+ }
+
+ public function setAlarm($alarm)
+ {
+ $this->alarm = $alarm;
+ }
+
+ public function readForm()
+ {
+ global $prefs, $cManager;
+
+ // Event owner.
+ $targetcalendar = Util::getFormData('targetcalendar');
+ if (strpos($targetcalendar, ':')) {
+ list(, $creator) = explode(':', $targetcalendar, 2);
+ } else {
+ $creator = isset($this->eventID) ? $this->getCreatorId() : Auth::getAuth();
+ }
+ $this->setCreatorId($creator);
+
+ // Basic fields.
+ $this->setTitle(Util::getFormData('title', $this->title));
+ $this->setDescription(Util::getFormData('description', $this->description));
+ $this->setLocation(Util::getFormData('location', $this->location));
+ $this->setPrivate(Util::getFormData('private'));
+
+ // Status.
+ $this->setStatus(Util::getFormData('status', $this->status));
+
+ // Attendees.
+ if (isset($_SESSION['kronolith']['attendees']) && is_array($_SESSION['kronolith']['attendees'])) {
+ $this->setAttendees($_SESSION['kronolith']['attendees']);
+ }
+
+ // Event start.
+ $start = Util::getFormData('start');
+ $start_year = $start['year'];
+ $start_month = $start['month'];
+ $start_day = $start['day'];
+ $start_hour = Util::getFormData('start_hour');
+ $start_min = Util::getFormData('start_min');
+ $am_pm = Util::getFormData('am_pm');
+
+ if (!$prefs->getValue('twentyFour')) {
+ if ($am_pm == 'PM') {
+ if ($start_hour != 12) {
+ $start_hour += 12;
+ }
+ } elseif ($start_hour == 12) {
+ $start_hour = 0;
+ }
+ }
+
+ if (Util::getFormData('end_or_dur') == 1) {
+ if (Util::getFormData('whole_day') == 1) {
+ $start_hour = 0;
+ $start_min = 0;
+ $dur_day = 0;
+ $dur_hour = 24;
+ $dur_min = 0;
+ } else {
+ $dur_day = (int)Util::getFormData('dur_day');
+ $dur_hour = (int)Util::getFormData('dur_hour');
+ $dur_min = (int)Util::getFormData('dur_min');
+ }
+ }
+
+ $this->start = new Horde_Date(array('hour' => $start_hour,
+ 'min' => $start_min,
+ 'month' => $start_month,
+ 'mday' => $start_day,
+ 'year' => $start_year));
+
+ if (Util::getFormData('end_or_dur') == 1) {
+ // Event duration.
+ $this->end = new Horde_Date(array('hour' => $start_hour + $dur_hour,
+ 'min' => $start_min + $dur_min,
+ 'month' => $start_month,
+ 'mday' => $start_day + $dur_day,
+ 'year' => $start_year));
+ } else {
+ // Event end.
+ $end = Util::getFormData('end');
+ $end_year = $end['year'];
+ $end_month = $end['month'];
+ $end_day = $end['day'];
+ $end_hour = Util::getFormData('end_hour');
+ $end_min = Util::getFormData('end_min');
+ $end_am_pm = Util::getFormData('end_am_pm');
+
+ if (!$prefs->getValue('twentyFour')) {
+ if ($end_am_pm == 'PM') {
+ if ($end_hour != 12) {
+ $end_hour += 12;
+ }
+ } elseif ($end_hour == 12) {
+ $end_hour = 0;
+ }
+ }
+
+ $this->end = new Horde_Date(array('hour' => $end_hour,
+ 'min' => $end_min,
+ 'month' => $end_month,
+ 'mday' => $end_day,
+ 'year' => $end_year));
+ if ($this->end->compareDateTime($this->start) < 0) {
+ $this->end = new Horde_Date($this->start);
+ }
+ }
+
+ // Alarm.
+ if (Util::getFormData('alarm') == 1) {
+ $this->setAlarm(Util::getFormData('alarm_value') * Util::getFormData('alarm_unit'));
+ // Notification.
+ if (Util::getFormData('alarm_change_method')) {
+ $types = Util::getFormData('event_alarms');
+ if (!empty($types)) {
+ $methods = array();
+ foreach ($types as $type) {
+ $methods[$type] = array();
+ switch ($type){
+ case 'notify':
+ $methods[$type]['sound'] = Util::getFormData('event_alarms_sound');
+ break;
+ case 'mail':
+ $methods[$type]['email'] = Util::getFormData('event_alarms_email');
+ break;
+ case 'popup':
+ break;
+ }
+ }
+ $this->methods = $methods;
+ }
+ } else {
+ $this->methods = array();
+ }
+ } else {
+ $this->setAlarm(0);
+ $this->methods = array();
+ }
+
+ // Recurrence.
+ $recur = Util::getFormData('recur');
+ if ($recur !== null && $recur !== '') {
+ if (!isset($this->recurrence)) {
+ $this->recurrence = new Horde_Date_Recurrence($this->start);
+ }
+ if (Util::getFormData('recur_enddate_type') == 'date') {
+ $recur_enddate = Util::getFormData('recur_enddate');
+ if ($this->recurrence->hasRecurEnd()) {
+ $recurEnd = $this->recurrence->recurEnd;
+ $recurEnd->month = $recur_enddate['month'];
+ $recurEnd->mday = $recur_enddate['day'];
+ $recurEnd->year = $recur_enddate['year'];
+ } else {
+ $recurEnd = new Horde_Date(
+ array('hour' => 23,
+ 'min' => 59,
+ 'sec' => 59,
+ 'month' => $recur_enddate['month'],
+ 'mday' => $recur_enddate['day'],
+ 'year' => $recur_enddate['year']));
+ }
+ $this->recurrence->setRecurEnd($recurEnd);
+ } elseif (Util::getFormData('recur_enddate_type') == 'count') {
+ $this->recurrence->setRecurCount(Util::getFormData('recur_count'));
+ } elseif (Util::getFormData('recur_enddate_type') == 'none') {
+ $this->recurrence->setRecurCount(0);
+ $this->recurrence->setRecurEnd(null);
+ }
+
+ $this->recurrence->setRecurType($recur);
+ switch ($recur) {
+ case Horde_Date_Recurrence::RECUR_DAILY:
+ $this->recurrence->setRecurInterval(Util::getFormData('recur_daily_interval', 1));
+ break;
+
+ case Horde_Date_Recurrence::RECUR_WEEKLY:
+ $weekly = Util::getFormData('weekly');
+ $weekdays = 0;
+ if (is_array($weekly)) {
+ foreach ($weekly as $day) {
+ $weekdays |= $day;
+ }
+ }
+
+ if ($weekdays == 0) {
+ // Sunday starts at 0.
+ switch ($this->start->dayOfWeek()) {
+ case 0: $weekdays |= Horde_Date::MASK_SUNDAY; break;
+ case 1: $weekdays |= Horde_Date::MASK_MONDAY; break;
+ case 2: $weekdays |= Horde_Date::MASK_TUESDAY; break;
+ case 3: $weekdays |= Horde_Date::MASK_WEDNESDAY; break;
+ case 4: $weekdays |= Horde_Date::MASK_THURSDAY; break;
+ case 5: $weekdays |= Horde_Date::MASK_FRIDAY; break;
+ case 6: $weekdays |= Horde_Date::MASK_SATURDAY; break;
+ }
+ }
+
+ $this->recurrence->setRecurInterval(Util::getFormData('recur_weekly_interval', 1));
+ $this->recurrence->setRecurOnDay($weekdays);
+ break;
+
+ case Horde_Date_Recurrence::RECUR_MONTHLY_DATE:
+ $this->recurrence->setRecurInterval(Util::getFormData('recur_day_of_month_interval', 1));
+ break;
+
+ case Horde_Date_Recurrence::RECUR_MONTHLY_WEEKDAY:
+ $this->recurrence->setRecurInterval(Util::getFormData('recur_week_of_month_interval', 1));
+ break;
+
+ case Horde_Date_Recurrence::RECUR_YEARLY_DATE:
+ $this->recurrence->setRecurInterval(Util::getFormData('recur_yearly_interval', 1));
+ break;
+
+ case Horde_Date_Recurrence::RECUR_YEARLY_DAY:
+ $this->recurrence->setRecurInterval(Util::getFormData('recur_yearly_day_interval', 1));
+ break;
+
+ case Horde_Date_Recurrence::RECUR_YEARLY_WEEKDAY:
+ $this->recurrence->setRecurInterval(Util::getFormData('recur_yearly_weekday_interval', 1));
+ break;
+ }
+ }
+
+ // Tags.
+ $this->tags = Util::getFormData('tags');
+
+ $this->initialized = true;
+ }
+
+ public function html($property)
+ {
+ global $prefs;
+
+ $options = array();
+ $attributes = '';
+ $sel = false;
+ $label = '';
+
+ switch ($property) {
+ case 'start[year]':
+ return '<label for="' . $this->_formIDEncode($property) . '" class="hidden">' . _("Start Year") . '</label>' .
+ '<input name="' . $property . '" value="' . $this->start->year .
+ '" type="text" onchange="' . $this->js($property) .
+ '" id="' . $this->_formIDEncode($property) . '" size="4" maxlength="4" />';
+
+ case 'start[month]':
+ $sel = $this->start->month;
+ for ($i = 1; $i < 13; ++$i) {
+ $options[$i] = strftime('%b', mktime(1, 1, 1, $i, 1));
+ }
+ $attributes = ' onchange="' . $this->js($property) . '"';
+ $label = _("Start Month");
+ break;
+
+ case 'start[day]':
+ $sel = $this->start->mday;
+ for ($i = 1; $i < 32; ++$i) {
+ $options[$i] = $i;
+ }
+ $attributes = ' onchange="' . $this->js($property) . '"';
+ $label = _("Start Day");
+ break;
+
+ case 'start_hour':
+ $sel = $this->start->format($prefs->getValue('twentyFour') ? 'G' : 'g');
+ $hour_min = $prefs->getValue('twentyFour') ? 0 : 1;
+ $hour_max = $prefs->getValue('twentyFour') ? 24 : 13;
+ for ($i = $hour_min; $i < $hour_max; ++$i) {
+ $options[$i] = $i;
+ }
+ $attributes = ' onchange="document.eventform.whole_day.checked = false; KronolithEventForm.updateEndDate();"';
+ $label = _("Start Hour");
+ break;
+
+ case 'start_min':
+ $sel = sprintf('%02d', $this->start->min);
+ for ($i = 0; $i < 12; ++$i) {
+ $min = sprintf('%02d', $i * 5);
+ $options[$min] = $min;
+ }
+ $attributes = ' onchange="document.eventform.whole_day.checked = false; KronolithEventForm.updateEndDate();"';
+ $label = _("Start Minute");
+ break;
+
+ case 'end[year]':
+ return '<label for="' . $this->_formIDEncode($property) . '" class="hidden">' . _("End Year") . '</label>' .
+ '<input name="' . $property . '" value="' . $this->end->year .
+ '" type="text" onchange="' . $this->js($property) .
+ '" id="' . $this->_formIDEncode($property) . '" size="4" maxlength="4" />';
+
+ case 'end[month]':
+ $sel = $this->isInitialized() ? $this->end->month : $this->start->month;
+ for ($i = 1; $i < 13; ++$i) {
+ $options[$i] = strftime('%b', mktime(1, 1, 1, $i, 1));
+ }
+ $attributes = ' onchange="' . $this->js($property) . '"';
+ $label = _("End Month");
+ break;
+
+ case 'end[day]':
+ $sel = $this->isInitialized() ? $this->end->mday : $this->start->mday;
+ for ($i = 1; $i < 32; ++$i) {
+ $options[$i] = $i;
+ }
+ $attributes = ' onchange="' . $this->js($property) . '"';
+ $label = _("End Day");
+ break;
+
+ case 'end_hour':
+ $sel = $this->isInitialized() ?
+ $this->end->format($prefs->getValue('twentyFour') ? 'G' : 'g') :
+ $this->start->format($prefs->getValue('twentyFour') ? 'G' : 'g') + 1;
+ $hour_min = $prefs->getValue('twentyFour') ? 0 : 1;
+ $hour_max = $prefs->getValue('twentyFour') ? 24 : 13;
+ for ($i = $hour_min; $i < $hour_max; ++$i) {
+ $options[$i] = $i;
+ }
+ $attributes = ' onchange="KronolithEventForm.updateDuration(); document.eventform.end_or_dur[0].checked = true"';
+ $label = _("End Hour");
+ break;
+
+ case 'end_min':
+ $sel = $this->isInitialized() ? $this->end->min : $this->start->min;
+ $sel = sprintf('%02d', $sel);
+ for ($i = 0; $i < 12; ++$i) {
+ $min = sprintf('%02d', $i * 5);
+ $options[$min] = $min;
+ }
+ $attributes = ' onchange="KronolithEventForm.updateDuration(); document.eventform.end_or_dur[0].checked = true"';
+ $label = _("End Minute");
+ break;
+
+ case 'dur_day':
+ $dur = $this->getDuration();
+ return '<label for="' . $property . '" class="hidden">' . _("Duration Day") . '</label>' .
+ '<input name="' . $property . '" value="' . $dur->day .
+ '" type="text" onchange="' . $this->js($property) .
+ '" id="' . $property . '" size="4" maxlength="4" />';
+
+ case 'dur_hour':
+ $dur = $this->getDuration();
+ $sel = $dur->hour;
+ for ($i = 0; $i < 24; ++$i) {
+ $options[$i] = $i;
+ }
+ $attributes = ' onchange="' . $this->js($property) . '"';
+ $label = _("Duration Hour");
+ break;
+
+ case 'dur_min':
+ $dur = $this->getDuration();
+ $sel = $dur->min;
+ for ($i = 0; $i < 13; ++$i) {
+ $min = sprintf('%02d', $i * 5);
+ $options[$min] = $min;
+ }
+ $attributes = ' onchange="' . $this->js($property) . '"';
+ $label = _("Duration Minute");
+ break;
+
+ case 'recur_enddate[year]':
+ if ($this->isInitialized()) {
+ $end = ($this->recurs() && $this->recurrence->hasRecurEnd())
+ ? $this->recurrence->recurEnd->year
+ : $this->end->year;
+ } else {
+ $end = $this->start->year;
+ }
+ return '<label for="' . $this->_formIDEncode($property) . '" class="hidden">' . _("Recurrence End Year") . '</label>' .
+ '<input name="' . $property . '" value="' . $end .
+ '" type="text" onchange="' . $this->js($property) .
+ '" id="' . $this->_formIDEncode($property) . '" size="4" maxlength="4" />';
+
+ case 'recur_enddate[month]':
+ if ($this->isInitialized()) {
+ $sel = ($this->recurs() && $this->recurrence->hasRecurEnd())
+ ? $this->recurrence->recurEnd->month
+ : $this->end->month;
+ } else {
+ $sel = $this->start->month;
+ }
+ for ($i = 1; $i < 13; ++$i) {
+ $options[$i] = strftime('%b', mktime(1, 1, 1, $i, 1));
+ }
+ $attributes = ' onchange="' . $this->js($property) . '"';
+ $label = _("Recurrence End Month");
+ break;
+
+ case 'recur_enddate[day]':
+ if ($this->isInitialized()) {
+ $sel = ($this->recurs() && $this->recurrence->hasRecurEnd())
+ ? $this->recurrence->recurEnd->mday
+ : $this->end->mday;
+ } else {
+ $sel = $this->start->mday;
+ }
+ for ($i = 1; $i < 32; ++$i) {
+ $options[$i] = $i;
+ }
+ $attributes = ' onchange="' . $this->js($property) . '"';
+ $label = _("Recurrence End Day");
+ break;
+ }
+
+ if (!$this->_varRenderer) {
+ $this->_varRenderer = Horde_UI_VarRenderer::factory('html');
+ }
+
+ return '<label for="' . $this->_formIDEncode($property) . '" class="hidden">' . $label . '</label>' .
+ '<select name="' . $property . '"' . $attributes . ' id="' . $this->_formIDEncode($property) . '">' .
+ $this->_varRenderer->_selectOptions($options, $sel) .
+ '</select>';
+ }
+
+ public function js($property)
+ {
+ switch ($property) {
+ case 'start[month]':
+ case 'start[year]':
+ case 'start[day]':
+ case 'start':
+ return 'KronolithEventForm.updateWday(\'start_wday\'); document.eventform.whole_day.checked = false; KronolithEventForm.updateEndDate();';
+
+ case 'end[month]':
+ case 'end[year]':
+ case 'end[day]':
+ case 'end':
+ return 'KronolithEventForm.updateWday(\'end_wday\'); updateDuration(); document.eventform.end_or_dur[0].checked = true;';
+
+ case 'recur_enddate[month]':
+ case 'recur_enddate[year]':
+ case 'recur_enddate[day]':
+ case 'recur_enddate':
+ return 'KronolithEventForm.updateWday(\'recur_end_wday\'); document.eventform.recur_enddate_type[1].checked = true;';
+
+ case 'dur_day':
+ case 'dur_hour':
+ case 'dur_min':
+ return 'document.eventform.whole_day.checked = false; KronolithEventForm.updateEndDate(); document.eventform.end_or_dur[1].checked = true;';
+ }
+ }
+
+ /**
+ * @param array $params
+ *
+ * @return string
+ */
+ public function getViewUrl($params = array(), $full = false)
+ {
+ $params['eventID'] = $this->eventID;
+ if ($this->remoteUrl) {
+ return $this->remoteUrl;
+ } elseif ($this->remoteCal) {
+ $params['calendar'] = '**remote';
+ $params['remoteCal'] = $this->remoteCal;
+ } else {
+ $params['calendar'] = $this->getCalendar();
+ }
+
+ return Horde::applicationUrl(Util::addParameter('event.php', $params), $full);
+ }
+
+ /**
+ * @param array $params
+ *
+ * @return string
+ */
+ public function getEditUrl($params = array())
+ {
+ $params['view'] = 'EditEvent';
+ $params['eventID'] = $this->eventID;
+ if ($this->remoteCal) {
+ $params['calendar'] = '**remote';
+ $params['remoteCal'] = $this->remoteCal;
+ } else {
+ $params['calendar'] = $this->getCalendar();
+ }
+
+ return Horde::applicationUrl(Util::addParameter('event.php', $params));
+ }
+
+ /**
+ * @param array $params
+ *
+ * @return string
+ */
+ public function getDeleteUrl($params = array())
+ {
+ $params['view'] = 'DeleteEvent';
+ $params['eventID'] = $this->eventID;
+ $params['calendar'] = $this->getCalendar();
+ return Horde::applicationUrl(Util::addParameter('event.php', $params));
+ }
+
+ /**
+ * @param array $params
+ *
+ * @return string
+ */
+ public function getExportUrl($params = array())
+ {
+ $params['view'] = 'ExportEvent';
+ $params['eventID'] = $this->eventID;
+ if ($this->remoteCal) {
+ $params['calendar'] = '**remote';
+ $params['remoteCal'] = $this->remoteCal;
+ } else {
+ $params['calendar'] = $this->getCalendar();
+ }
+
+ return Horde::applicationUrl(Util::addParameter('event.php', $params));
+ }
+
+ public function getLink($datetime = null, $icons = true, $from_url = null, $full = false)
+ {
+ global $prefs, $registry;
+
+ if (is_null($datetime)) {
+ $datetime = $this->start;
+ }
+ if (is_null($from_url)) {
+ $from_url = Horde::selfUrl(true, false, true);
+ }
+
+ $link = '';
+ $event_title = $this->getTitle();
+ if (isset($this->external)) {
+ $link = $registry->link($this->external . '/show', $this->external_params);
+ $link = Horde::linkTooltip(Horde::url($link), '', 'event-tentative', '', '', String::wrap($this->description));
+ } elseif (isset($this->eventID) && $this->hasPermission(PERMS_READ)) {
+ $link = Horde::linkTooltip($this->getViewUrl(array('datetime' => $datetime->strftime('%Y%m%d%H%M%S'), 'url' => $from_url), $full),
+ $event_title,
+ $this->getStatusClass(), '', '',
+ $this->getTooltip(),
+ '',
+ array('style' => $this->getCSSColors(false)));
+ }
+
+ $link .= @htmlspecialchars($event_title, ENT_QUOTES, NLS::getCharset());
+
+ if ($this->hasPermission(PERMS_READ) &&
+ (isset($this->eventID) ||
+ isset($this->external))) {
+ $link .= '</a>';
+ }
+
+ if ($icons && $prefs->getValue('show_icons')) {
+ $icon_color = $this->_foregroundColor == '#000' ? '000' : 'fff';
+ $status = '';
+ if ($this->alarm) {
+ if ($this->alarm % 10080 == 0) {
+ $alarm_value = $this->alarm / 10080;
+ $title = $alarm_value == 1 ?
+ _("Alarm 1 week before") :
+ sprintf(_("Alarm %d weeks before"), $alarm_value);
+ } elseif ($this->alarm % 1440 == 0) {
+ $alarm_value = $this->alarm / 1440;
+ $title = $alarm_value == 1 ?
+ _("Alarm 1 day before") :
+ sprintf(_("Alarm %d days before"), $alarm_value);
+ } elseif ($this->alarm % 60 == 0) {
+ $alarm_value = $this->alarm / 60;
+ $title = $alarm_value == 1 ?
+ _("Alarm 1 hour before") :
+ sprintf(_("Alarm %d hours before"), $alarm_value);
+ } else {
+ $alarm_value = $this->alarm;
+ $title = $alarm_value == 1 ?
+ _("Alarm 1 minute before") :
+ sprintf(_("Alarm %d minutes before"), $alarm_value);
+ }
+ $status .= Horde::img('alarm-' . $icon_color . '.png', $title,
+ array('title' => $title,
+ 'class' => 'iconAlarm'),
+ Horde::url($registry->getImageDir(), true, -1));
+ }
+
+ if ($this->recurs()) {
+ $title = Kronolith::recurToString($this->recurrence->getRecurType());
+ $status .= Horde::img('recur-' . $icon_color . '.png', $title,
+ array('title' => $title,
+ 'class' => 'iconRecur'),
+ Horde::url($registry->getImageDir(), true, -1));
+ }
+
+ if ($this->isPrivate()) {
+ $title = _("Private event");
+ $status .= Horde::img('private-' . $icon_color . '.png', $title,
+ array('title' => $title,
+ 'class' => 'iconPrivate'),
+ Horde::url($registry->getImageDir(), true, -1));
+ }
+
+ if (!empty($this->attendees)) {
+ $title = count($this->attendees) == 1
+ ? _("1 attendee")
+ : sprintf(_("%s attendees"), count($this->attendees));
+ $status .= Horde::img('attendees.png', $title,
+ array('title' => $title,
+ 'class' => 'iconPeople'),
+ Horde::url($registry->getImageDir(), true, -1));
+ }
+
+ if (!empty($status)) {
+ $link .= ' ' . $status;
+ }
+
+ if (!$this->eventID || !empty($this->external)) {
+ return $link;
+ }
+
+ $edit = '';
+ $delete = '';
+ if ((!$this->isPrivate() || $this->getCreatorId() == Auth::getAuth())
+ && $this->hasPermission(PERMS_EDIT)) {
+ $editurl = $this->getEditUrl(array('datetime' => $datetime->strftime('%Y%m%d%H%M%S'),
+ 'url' => $from_url));
+ $edit = Horde::link($editurl, sprintf(_("Edit %s"), $event_title), 'iconEdit')
+ . Horde::img('edit-' . $icon_color . '.png', _("Edit"), '', Horde::url($registry->getImageDir(), true, -1))
+ . '</a>';
+ }
+ if ($this->hasPermission(PERMS_DELETE)) {
+ $delurl = $this->getDeleteUrl(array('datetime' => $datetime->strftime('%Y%m%d%H%M%S'),
+ 'url' => $from_url));
+ $delete = Horde::link($delurl, sprintf(_("Delete %s"), $event_title), 'iconDelete')
+ . Horde::img('delete-' . $icon_color . '.png', _("Delete"), '', Horde::url($registry->getImageDir(), true, -1))
+ . '</a>';
+ }
+
+ if ($edit || $delete) {
+ $link .= $edit . $delete;
+ }
+ }
+
+ return $link;
+ }
+
+ /**
+ * Returns the CSS color definition for this event.
+ *
+ * @param boolean $with_attribute Whether to wrap the colors inside a
+ * "style" attribute.
+ *
+ * @return string A CSS string with color definitions.
+ */
+ public function getCSSColors($with_attribute = true)
+ {
+ $css = 'background-color:' . $this->_backgroundColor . ';color:' . $this->_foregroundColor;
+ if ($with_attribute) {
+ $css = ' style="' . $css . '"';
+ }
+ return $css;
+ }
+
+ /**
+ * @return string A tooltip for quick descriptions of this event.
+ */
+ public function getTooltip()
+ {
+ $tooltip = $this->getTimeRange()
+ . "\n" . sprintf(_("Owner: %s"), ($this->getCreatorId() == Auth::getAuth() ?
+ _("Me") : Kronolith::getUserName($this->getCreatorId())));
+
+ if (!$this->isPrivate() || $this->getCreatorId() == Auth::getAuth()) {
+ if ($this->location) {
+ $tooltip .= "\n" . _("Location") . ': ' . $this->location;
+ }
+
+ if ($this->description) {
+ $tooltip .= "\n\n" . String::wrap($this->description);
+ }
+ }
+
+ return $tooltip;
+ }
+
+ /**
+ * @return string The time range of the event ("All Day", "1:00pm-3:00pm",
+ * "08:00-22:00").
+ */
+ public function getTimeRange()
+ {
+ if ($this->isAllDay()) {
+ return _("All day");
+ } elseif (($cmp = $this->start->compareDate($this->end)) > 0) {
+ $df = $GLOBALS['prefs']->getValue('date_format');
+ if ($cmp > 0) {
+ return $this->end->strftime($df) . '-'
+ . $this->start->strftime($df);
+ } else {
+ return $this->start->strftime($df) . '-'
+ . $this->end->strftime($df);
+ }
+ } else {
+ $twentyFour = $GLOBALS['prefs']->getValue('twentyFour');
+ return $this->start->format($twentyFour ? 'G:i' : 'g:ia')
+ . '-'
+ . $this->end->format($twentyFour ? 'G:i' : 'g:ia');
+ }
+ }
+
+ /**
+ * @return string The CSS class for the event based on its status.
+ */
+ public function getStatusClass()
+ {
+ switch ($this->status) {
+ case Kronolith::STATUS_CANCELLED:
+ return 'event-cancelled';
+
+ case Kronolith::STATUS_TENTATIVE:
+ case Kronolith::STATUS_FREE:
+ return 'event-tentative';
+ }
+
+ return 'event';
+ }
+
+ private function _formIDEncode($id)
+ {
+ return str_replace(array('[', ']'),
+ array('_', ''),
+ $id);
+ }
+
+}
--- /dev/null
+<?php
+/**
+ * Copyright 2006-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * @see http://pear.php.net/packages/Date_Holidays
+ * @author Stephan Hohmann <webmaster@dasourcerer.net>
+ * @package Kronolith
+ */
+class Kronolith_Event_Holidays extends Kronolith_Event
+{
+ /**
+ * The status of this event.
+ *
+ * @var integer
+ */
+ public $status = Kronolith::STATUS_FREE;
+
+ /**
+ * Whether this is an all-day event.
+ *
+ * @var boolean
+ */
+ public $allday = true;
+
+ /**
+ * Parse in an event from the driver.
+ *
+ * @param Date_Holidays_Holiday $dhEvent A holiday returned
+ * from the driver
+ */
+ public function fromDriver($dhEvent)
+ {
+ $this->stored = true;
+ $this->initialized = true;
+ $this->setTitle(String::convertCharset($dhEvent->getTitle(), 'UTF-8'));
+ $this->setId($dhEvent->getInternalName());
+
+ $this->start = new Horde_Date($dhEvent->_date->getTime());
+ $this->end = new Horde_Date($this->start);
+ $this->end->mday++;
+ }
+
+ /**
+ * Return this events title.
+ *
+ * @return string The title of this event
+ */
+ public function getTitle()
+ {
+ return $this->title;
+ }
+
+ /**
+ * Is this event an all-day event?
+ *
+ * Since there are no holidays lasting only a few hours, this is always
+ * true.
+ *
+ * @return boolean <code>true</code>
+ */
+ public function isAllDay()
+ {
+ return true;
+ }
+
+}
--- /dev/null
+<?php
+/**
+ * Copyright 2004-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ * @author Jan Schneider <jan@horde.org>
+ * @package Kronolith
+ */
+class Kronolith_Event_Ical extends Kronolith_Event
+{
+
+ public function fromDriver($vEvent)
+ {
+ $this->fromiCalendar($vEvent);
+ $this->initialized = true;
+ $this->stored = true;
+ }
+
+ public function toDriver()
+ {
+ return $this->toiCalendar();
+ }
+
+}
--- /dev/null
+<?php
+/**
+ * Copyright 2004-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * @author Thomas Jarosch <thomas.jarosch@intra2net.com>
+ * @author Gunnar Wrobel <wrobel@pardus.de>
+ * @author Stuart Binge <omicron@mighty.co.za>
+ * @package Kronolith
+ */
+class Kronolith_Event_Kolab extends Kronolith_Event
+{
+
+ public function fromDriver($event)
+ {
+ $this->eventID = $event['uid'];
+ $this->setUID($this->eventID);
+
+ if (isset($event['summary'])) {
+ $this->title = $event['summary'];
+ }
+ if (isset($event['body'])) {
+ $this->description = $event['body'];
+ }
+ if (isset($event['location'])) {
+ $this->location = $event['location'];
+ }
+
+ if (isset($event['sensitivity']) &&
+ ($event['sensitivity'] == 'private' || $event['sensitivity'] == 'confidential')) {
+ $this->setPrivate(true);
+ }
+
+ if (isset($event['organizer']['smtp-address'])) {
+ if (Kronolith::isUserEmail(Auth::getAuth(), $event['organizer']['smtp-address'])) {
+ $this->creatorID = Auth::getAuth();
+ } else {
+ $this->creatorID = $event['organizer']['smtp-address'];
+ }
+ }
+
+ if (isset($event['alarm'])) {
+ $this->alarm = $event['alarm'];
+ }
+
+ $this->start = new Horde_Date($event['start-date']);
+ $this->end = new Horde_Date($event['end-date']);
+ $this->durMin = ($this->end->timestamp() - $this->start->timestamp()) / 60;
+
+ if (isset($event['show-time-as'])) {
+ switch ($event['show-time-as']) {
+ case 'free':
+ $this->status = Kronolith::STATUS_FREE;
+ break;
+
+ case 'tentative':
+ $this->status = Kronolith::STATUS_TENTATIVE;
+ break;
+
+ case 'busy':
+ case 'outofoffice':
+ default:
+ $this->status = Kronolith::STATUS_CONFIRMED;
+ }
+ } else {
+ $this->status = Kronolith::STATUS_CONFIRMED;
+ }
+
+ // Recurrence
+ if (isset($event['recurrence'])) {
+ $this->recurrence = new Horde_Date_Recurrence($this->start);
+ $this->recurrence->fromHash($event['recurrence']);
+ }
+
+ // Attendees
+ $attendee_count = 0;
+ foreach($event['attendee'] as $attendee) {
+ $name = $attendee['display-name'];
+ $email = $attendee['smtp-address'];
+
+ $role = $attendee['role'];
+ switch ($role) {
+ case 'optional':
+ $role = Kronolith::PART_OPTIONAL;
+ break;
+
+ case 'resource':
+ $role = Kronolith::PART_NONE;
+ break;
+
+ case 'required':
+ default:
+ $role = Kronolith::PART_REQUIRED;
+ break;
+ }
+
+ $status = $attendee['status'];
+ switch ($status) {
+ case 'accepted':
+ $status = Kronolith::RESPONSE_ACCEPTED;
+ break;
+
+ case 'declined':
+ $status = Kronolith::RESPONSE_DECLINED;
+ break;
+
+ case 'tentative':
+ $status = Kronolith::RESPONSE_TENTATIVE;
+ break;
+
+ case 'none':
+ default:
+ $status = Kronolith::RESPONSE_NONE;
+ break;
+ }
+
+ // Attendees without an email address get added as incremented number
+ if (empty($email)) {
+ $email = $attendee_count;
+ $attendee_count++;
+ }
+
+ $this->addAttendee($email, $role, $status, $name);
+ }
+
+ $this->initialized = true;
+ $this->stored = true;
+ }
+
+ public function toDriver()
+ {
+ $event = array();
+ $event['uid'] = $this->getUID();
+ $event['summary'] = $this->title;
+ $event['body'] = $this->description;
+ $event['location'] = $this->location;
+
+ if ($this->isPrivate()) {
+ $event['sensitivity'] = 'private';
+ } else {
+ $event['sensitivity'] = 'public';
+ }
+
+ // Only set organizer if this is a new event
+ if ($this->getID() == null) {
+ $organizer = array(
+ 'display-name' => Kronolith::getUserName($this->getCreatorId()),
+ 'smtp-address' => Kronolith::getUserEmail($this->getCreatorId())
+ );
+ $event['organizer'] = $organizer;
+ }
+
+ if ($this->alarm != 0) {
+ $event['alarm'] = $this->alarm;
+ }
+
+ $event['start-date'] = $this->start->timestamp();
+ $event['end-date'] = $this->end->timestamp();
+ $event['_is_all_day'] = $this->isAllDay();
+
+ switch ($this->status) {
+ case Kronolith::STATUS_FREE:
+ case Kronolith::STATUS_CANCELLED:
+ $event['show-time-as'] = 'free';
+ break;
+
+ case Kronolith::STATUS_TENTATIVE:
+ $event['show-time-as'] = 'tentative';
+ break;
+
+ // No mapping for outofoffice
+ case Kronolith::STATUS_CONFIRMED:
+ default:
+ $event['show-time-as'] = 'busy';
+ }
+
+ // Recurrence
+ if ($this->recurs()) {
+ $event['recurrence'] = $this->recurrence->toHash();
+ } else {
+ $event['recurrence'] = array();
+ }
+
+
+ // Attendees
+ $event['attendee'] = array();
+ $attendees = $this->getAttendees();
+
+ foreach($attendees as $email => $attendee) {
+ $new_attendee = array();
+ $new_attendee['display-name'] = $attendee['name'];
+
+ // Attendee without an email address
+ if (is_int($email)) {
+ $new_attendee['smtp-address'] = '';
+ } else {
+ $new_attendee['smtp-address'] = $email;
+ }
+
+ switch ($attendee['attendance']) {
+ case Kronolith::PART_OPTIONAL:
+ $new_attendee['role'] = 'optional';
+ break;
+
+ case Kronolith::PART_NONE:
+ $new_attendee['role'] = 'resource';
+ break;
+
+ case Kronolith::PART_REQUIRED:
+ default:
+ $new_attendee['role'] = 'required';
+ break;
+ }
+
+ $new_attendee['request-response'] = '0';
+
+ switch ($attendee['response']) {
+ case Kronolith::RESPONSE_ACCEPTED:
+ $new_attendee['status'] = 'accepted';
+ break;
+
+ case Kronolith::RESPONSE_DECLINED:
+ $new_attendee['status'] = 'declined';
+ break;
+
+ case Kronolith::RESPONSE_TENTATIVE:
+ $new_attendee['status'] = 'tentative';
+ break;
+
+ case Kronolith::RESPONSE_NONE:
+ default:
+ $new_attendee['status'] = 'none';
+ break;
+ }
+
+ $event['attendee'][] = $new_attendee;
+ }
+
+ return $event;
+ }
+
+}
--- /dev/null
+<?php
+/**
+ * Copyright 1999-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
+ * @author Luc Saillard <luc.saillard@fr.alcove.com>
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ * @author Jan Schneider <jan@horde.org>
+ * @package Kronolith
+ */
+class Kronolith_Event_Sql extends Kronolith_Event
+{
+ /**
+ * @var array
+ */
+ private $_properties = array();
+
+ public function fromDriver($SQLEvent)
+ {
+ $driver = $this->getDriver();
+
+ $this->allday = (bool)$SQLEvent['event_allday'];
+ if (!$this->allday && $driver->getParam('utc')) {
+ $tz_local = date_default_timezone_get();
+ $this->start = new Horde_Date($SQLEvent['event_start'], 'UTC');
+ $this->start->setTimezone($tz_local);
+ $this->end = new Horde_Date($SQLEvent['event_end'], 'UTC');
+ $this->end->setTimezone($tz_local);
+ } else {
+ $this->start = new Horde_Date($SQLEvent['event_start']);
+ $this->end = new Horde_Date($SQLEvent['event_end']);
+ }
+
+ $this->durMin = ($this->end->timestamp() - $this->start->timestamp()) / 60;
+
+ $this->title = $driver->convertFromDriver($SQLEvent['event_title']);
+ $this->eventID = $SQLEvent['event_id'];
+ $this->setUID($SQLEvent['event_uid']);
+ $this->creatorID = $SQLEvent['event_creator_id'];
+
+ if (!empty($SQLEvent['event_recurtype'])) {
+ $this->recurrence = new Horde_Date_Recurrence($this->start);
+ $this->recurrence->setRecurType((int)$SQLEvent['event_recurtype']);
+ $this->recurrence->setRecurInterval((int)$SQLEvent['event_recurinterval']);
+ if (isset($SQLEvent['event_recurenddate'])) {
+ if ($driver->getParam('utc')) {
+ $recur_end = new Horde_Date($SQLEvent['event_recurenddate'], 'UTC');
+ if ($recur_end->min == 0) {
+ /* Old recurrence end date format. */
+ $recur_end = new Horde_Date($SQLEvent['event_recurenddate']);
+ $recur_end->hour = 23;
+ $recur_end->min = 59;
+ $recur_end->sec = 59;
+ } else {
+ $recur_end->setTimezone(date_default_timezone_get());
+ }
+ } else {
+ $recur_end = new Horde_Date($SQLEvent['event_recurenddate']);
+ $recur_end->hour = 23;
+ $recur_end->min = 59;
+ $recur_end->sec = 59;
+ }
+ $this->recurrence->setRecurEnd($recur_end);
+ }
+ if (isset($SQLEvent['event_recurcount'])) {
+ $this->recurrence->setRecurCount((int)$SQLEvent['event_recurcount']);
+ }
+ if (isset($SQLEvent['event_recurdays'])) {
+ $this->recurrence->recurData = (int)$SQLEvent['event_recurdays'];
+ }
+ if (!empty($SQLEvent['event_exceptions'])) {
+ $this->recurrence->exceptions = explode(',', $SQLEvent['event_exceptions']);
+ }
+ }
+
+ if (isset($SQLEvent['event_location'])) {
+ $this->location = $driver->convertFromDriver($SQLEvent['event_location']);
+ }
+ if (isset($SQLEvent['event_private'])) {
+ $this->private = (bool)($SQLEvent['event_private']);
+ }
+ if (isset($SQLEvent['event_status'])) {
+ $this->status = $SQLEvent['event_status'];
+ }
+ if (isset($SQLEvent['event_attendees'])) {
+ $this->attendees = array_change_key_case($driver->convertFromDriver(unserialize($SQLEvent['event_attendees'])));
+ }
+ if (isset($SQLEvent['event_description'])) {
+ $this->description = $driver->convertFromDriver($SQLEvent['event_description']);
+ }
+ if (isset($SQLEvent['event_alarm'])) {
+ $this->alarm = (int)$SQLEvent['event_alarm'];
+ }
+ if (isset($SQLEvent['event_alarm_methods'])) {
+ $this->methods = $driver->convertFromDriver(unserialize($SQLEvent['event_alarm_methods']));
+ }
+ $this->initialized = true;
+ $this->stored = true;
+ }
+
+ public function toDriver()
+ {
+ $driver = $this->getDriver();
+
+ /* Basic fields. */
+ $this->_properties['event_creator_id'] = $driver->convertToDriver($this->getCreatorId());
+ $this->_properties['event_title'] = $driver->convertToDriver($this->title);
+ $this->_properties['event_description'] = $driver->convertToDriver($this->getDescription());
+ $this->_properties['event_location'] = $driver->convertToDriver($this->getLocation());
+ $this->_properties['event_private'] = (int)$this->isPrivate();
+ $this->_properties['event_status'] = $this->getStatus();
+ $this->_properties['event_attendees'] = serialize($driver->convertToDriver($this->getAttendees()));
+ $this->_properties['event_modified'] = $_SERVER['REQUEST_TIME'];
+
+ if ($this->isAllDay()) {
+ $this->_properties['event_start'] = $this->start->strftime('%Y-%m-%d %H:%M:%S');
+ $this->_properties['event_end'] = $this->end->strftime('%Y-%m-%d %H:%M:%S');
+ $this->_properties['event_allday'] = 1;
+ } else {
+ if ($driver->getParam('utc')) {
+ $start = clone $this->start;
+ $end = clone $this->end;
+ $start->setTimezone('UTC');
+ $end->setTimezone('UTC');
+ } else {
+ $start = $this->start;
+ $end = $this->end;
+ }
+ $this->_properties['event_start'] = $start->strftime('%Y-%m-%d %H:%M:%S');
+ $this->_properties['event_end'] = $end->strftime('%Y-%m-%d %H:%M:%S');
+ $this->_properties['event_allday'] = 0;
+ }
+
+ /* Alarm. */
+ $this->_properties['event_alarm'] = (int)$this->getAlarm();
+
+ /* Alarm Notification Methods. */
+ $this->_properties['event_alarm_methods'] = serialize($driver->convertToDriver($this->methods));
+
+ /* Recurrence. */
+ if (!$this->recurs()) {
+ $this->_properties['event_recurtype'] = 0;
+ } else {
+ $recur = $this->recurrence->getRecurType();
+ if ($this->recurrence->hasRecurEnd()) {
+ if ($driver->getParam('utc')) {
+ $recur_end = clone $this->recurrence->recurEnd;
+ $recur_end->setTimezone('UTC');
+ } else {
+ $recur_end = $this->recurrence->recurEnd;
+ }
+ } else {
+ $recur_end = new Horde_Date(array('year' => 9999, 'month' => 12, 'mday' => 31, 'hour' => 23, 'min' => 59, 'sec' => 59));
+ }
+
+ $this->_properties['event_recurtype'] = $recur;
+ $this->_properties['event_recurinterval'] = $this->recurrence->getRecurInterval();
+ $this->_properties['event_recurenddate'] = $recur_end->format('Y-m-d H:i:s');
+ $this->_properties['event_recurcount'] = $this->recurrence->getRecurCount();
+
+ switch ($recur) {
+ case Horde_Date_Recurrence::RECUR_WEEKLY:
+ $this->_properties['event_recurdays'] = $this->recurrence->getRecurOnDays();
+ break;
+ }
+ $this->_properties['event_exceptions'] = implode(',', $this->recurrence->getExceptions());
+ }
+ }
+
+ public function getProperties()
+ {
+ return $this->_properties;
+ }
+
+}
<?php
/**
+ * Copyright 1999-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (GPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/gpl.html.
+ *
* @package Kronolith
*/
__FILE__, __LINE__, PEAR_LOG_ERR);
return array();
} else {
- $dhDriver = Kronolith_Driver::factory('holidays');
+ $dhDriver = Kronolith_Driver::factory('Holidays');
return $dhDriver->listEvents($startDate, $endDate);
}
}
continue;
}
- $event = &$kronolith_driver->getEvent();
+ $event = $kronolith_driver->getEvent();
$event->eventID = '_' . $api . $eventsListItem['id'];
$event->external = $api;
$event->external_params = $eventsListItem['params'];
/* Remote Calendars. */
foreach ($GLOBALS['display_remote_calendars'] as $url) {
- $driver = self::getDriver('ical', array('url' => $url));
+ $driver = self::getDriver('Ical', array('url' => $url));
$events = $driver->listEvents($startOfPeriod, $endOfPeriod);
if (!is_a($events, 'PEAR_Error')) {
$kronolith_driver->open(Kronolith::getDefaultCalendar(PERMS_SHOW));
if (!isset(self::$_instances[$sig])) {
switch ($driver) {
- case 'ical':
+ case 'Ical':
/* Check for HTTP proxy configuration */
if (!empty($GLOBALS['conf']['http']['proxy']['proxy_host'])) {
$params['proxy'] = $GLOBALS['conf']['http']['proxy'];
require_once KRONOLITH_BASE . '/lib/Views/Event.php';
if (Util::getFormData('calendar') == '**remote') {
- $driver = self::getDriver('ical', array('url' => Util::getFormData('remoteCal')));
+ $driver = self::getDriver('Ical', array('url' => Util::getFormData('remoteCal')));
$event = $driver->getEvent(Util::getFormData('eventID'));
} elseif ($uid = Util::getFormData('uid')) {
- $event = &$GLOBALS['kronolith_driver']->getByUID($uid);
+ $event = $GLOBALS['kronolith_driver']->getByUID($uid);
} else {
$GLOBALS['kronolith_driver']->open(Util::getFormData('calendar'));
- $event = &$GLOBALS['kronolith_driver']->getEvent(
+ $event = $GLOBALS['kronolith_driver']->getEvent(
Util::getFormData('eventID'));
}
if (!is_a($event, 'PEAR_Error') &&
require_once KRONOLITH_BASE . '/lib/Views/EditEvent.php';
if (Util::getFormData('calendar') == '**remote') {
- $driver = self::getDriver('ical', array('url' => Util::getFormData('remoteCal')));
+ $driver = self::getDriver('Ical', array('url' => Util::getFormData('remoteCal')));
$event = $driver->getEvent(Util::getFormData('eventID'));
} else {
$GLOBALS['kronolith_driver']->open(Util::getFormData('calendar'));
- $event = &$GLOBALS['kronolith_driver']->getEvent(
+ $event = $GLOBALS['kronolith_driver']->getEvent(
Util::getFormData('eventID'));
}
if (!is_a($event, 'PEAR_Error') &&
require_once KRONOLITH_BASE . '/lib/Views/DeleteEvent.php';
$GLOBALS['kronolith_driver']->open(Util::getFormData('calendar'));
- $event = &$GLOBALS['kronolith_driver']->getEvent
+ $event = $GLOBALS['kronolith_driver']->getEvent
(Util::getFormData('eventID'));
if (!is_a($event, 'PEAR_Error') &&
!$event->hasPermission(PERMS_DELETE)) {
require_once KRONOLITH_BASE . '/lib/Views/ExportEvent.php';
if (Util::getFormData('calendar') == '**remote') {
- $driver = self::getDriver('ical', array('url' => Util::getFormData('remoteCal')));
+ $driver = self::getDriver('Ical', array('url' => Util::getFormData('remoteCal')));
$event = $driver->getEvent(Util::getFormData('eventID'));
} elseif ($uid = Util::getFormData('uid')) {
- $event = &$GLOBALS['kronolith_driver']->getByUID($uid);
+ $event = $GLOBALS['kronolith_driver']->getByUID($uid);
} else {
$GLOBALS['kronolith_driver']->open(Util::getFormData('calendar'));
- $event = &$GLOBALS['kronolith_driver']->getEvent(
+ $event = $GLOBALS['kronolith_driver']->getEvent(
Util::getFormData('eventID'));
}
if (!is_a($event, 'PEAR_Error') &&
var_dump($kolab->_storage->save($object));
// Check that the driver can be created
-$kron = Kronolith_Driver::factory('kolab');
+$kron = Kronolith_Driver::factory('Kolab');
$kron->open('wrobel@example.org');
$start = &new Horde_Date(86400);