187 lines
6.8 KiB
PHP
187 lines
6.8 KiB
PHP
<?php
|
|
/**
|
|
* Copyright 2015-2017 Horde LLC (http://www.horde.org/)
|
|
*
|
|
* See the enclosed file COPYING for license information (GPL). If you
|
|
* did not receive this file, see http://www.horde.org/licenses/gpl.
|
|
*
|
|
* @author Michael J Rubinsky <mrubinsk@horde.org>
|
|
* @license http://www.horde.org/licenses/gpl GPL
|
|
* @package Kronolith
|
|
* @category Horde
|
|
*/
|
|
|
|
/**
|
|
* Wraps logic responsible for importing iCalendar data via DAV taking into
|
|
* account necessary steps to deal with recurrence series and exceptions.
|
|
*
|
|
* @author Michael J Rubinsky <mrubinsk@horde.org>
|
|
* @license http://www.horde.org/licenses/gpl GPL
|
|
* @package Kronolith
|
|
* @category Horde
|
|
*/
|
|
class Kronolith_Icalendar_Handler_Dav extends Kronolith_Icalendar_Handler_Base
|
|
{
|
|
/**
|
|
* The DAV storage driver.
|
|
*
|
|
* @var Horde_Dav_Storage_Base
|
|
*/
|
|
protected $_dav;
|
|
|
|
/**
|
|
* The calendar id to be imported into.
|
|
*
|
|
* @var string
|
|
*/
|
|
protected $_calendar;
|
|
|
|
/**
|
|
* Temporary cache of the existing copy of an event being replaced.
|
|
*
|
|
* @var Kronolith_Event
|
|
*/
|
|
protected $_existingEvent;
|
|
|
|
/**
|
|
* List of attendees that should not be sent iTip notifications.
|
|
*
|
|
* @var array
|
|
*/
|
|
protected $_noItips = array();
|
|
|
|
/**
|
|
*
|
|
* @param Horde_Icalendar $iCal The iCalendar data.
|
|
* @param Kronolith_Driver $driver The Kronolith driver.
|
|
* @param array $params Any additional parameters needed for
|
|
* the importer. For this driver we
|
|
* require: 'object' - contains the DAV
|
|
* identifier for the (base) event.
|
|
*/
|
|
public function __construct(
|
|
Horde_Icalendar $iCal, Kronolith_Driver $driver, $params = array())
|
|
{
|
|
parent::__construct($iCal, $driver, $params);
|
|
$this->_dav = $GLOBALS['injector']->getInstance('Horde_Dav_Storage');
|
|
$this->_calendar = $this->_driver->calendar;
|
|
}
|
|
|
|
/**
|
|
* Responsible for any logic needed before the event is saved. Called for
|
|
* EVERY component in the iCalendar object. Returning false from this method
|
|
* will cause the current component to be ignored. Returning true causes it
|
|
* to be processed.
|
|
*
|
|
* @param Horde_Icalendar $component The iCalendar component.
|
|
*
|
|
* @return boolean True to continue processing, false to ignore.
|
|
*/
|
|
protected function _preSave($component)
|
|
{
|
|
// Short circuit if we know we don't pass the parent test.
|
|
if (!parent::_preSave($component)) {
|
|
return false;
|
|
}
|
|
|
|
// Ensure we start with a fresh state.
|
|
$this->_existingEvent = null;
|
|
|
|
// Get the internal id of the existing copy of the event, if it exists.
|
|
try {
|
|
$existing_id = $this->_dav->getInternalObjectId($this->_params['object'], $this->_calendar)
|
|
?: preg_replace('/\.ics$/', '', $this->_params['object']);
|
|
} catch (Horde_Dav_Exception $e) {
|
|
$existing_id = $this->_params['object'];
|
|
}
|
|
|
|
// Check that we don't have newer information already on the server.
|
|
try {
|
|
// Exception event, so we can't compare timestamps using ids.
|
|
// Instead look for baseid/recurrence-id.
|
|
$rid = $component->getAttribute('RECURRENCE-ID');
|
|
$uid = $component->getAttribute('UID');
|
|
if (!empty($rid) && !empty($uid)) {
|
|
$search = new stdClass();
|
|
$search->baseid = $uid;
|
|
$search->recurrenceid = $rid;
|
|
$results = $this->_driver->search($search);
|
|
foreach ($results as $days) {
|
|
foreach ($days as $exception) {
|
|
// Should only be one...
|
|
$modified = $exception->modified
|
|
?: $exception->created;
|
|
}
|
|
}
|
|
}
|
|
} catch (Horde_Icalendar_Exception $e) {
|
|
// Base event or event with no recurrence.
|
|
try {
|
|
$this->_existingEvent = $this->_driver->getEvent($existing_id);
|
|
$this->_existingEvent->loadHistory();
|
|
$modified = $this->_existingEvent->modified
|
|
?: $this->_existingEvent->created;
|
|
} catch (Horde_Exception_NotFound $e) {
|
|
$this->_existingEvent = null;
|
|
}
|
|
}
|
|
|
|
try {
|
|
if (!empty($modified) &&
|
|
$component->getAttribute('LAST-MODIFIED') < $modified->timestamp()) {
|
|
/* LAST-MODIFIED timestamp of existing entry is newer:
|
|
* don't replace it. */
|
|
return false;
|
|
}
|
|
} catch (Horde_Icalendar_Exception $e) {}
|
|
|
|
try {
|
|
$organizer = $component->getAttribute('ORGANIZER');
|
|
$organizer_params = $component->getAttribute('ORGANIZER', true);
|
|
if (!empty($organizer_params[0]['SCHEDULE-AGENT']) &&
|
|
($organizer_params[0]['SCHEDULE-AGENT'] == 'CLIENT' ||
|
|
$organizer_params[0]['SCHEDULE-AGENT'] == 'NONE')) {
|
|
$tmp = str_replace(array('MAILTO:', 'mailto:'), '', $organizer);
|
|
$tmp = new Horde_Mail_Rfc822_Address($tmp);
|
|
$this->_noItips[] = $tmp->bare_address;
|
|
}
|
|
} catch (Horde_Icalendar_Exception $e) {}
|
|
try {
|
|
$attendee = $component->getAttribute('ATTENDEE');
|
|
if (!is_array($attendee)) {
|
|
$attendee = array($attendee);
|
|
}
|
|
$params = $component->getAttribute('ATTENDEE', true);
|
|
for ($i = 0; $i < count($attendee); ++$i) {
|
|
if (!empty($params[$i]['SCHEDULE-AGENT']) &&
|
|
($params[$i]['SCHEDULE-AGENT'] == 'CLIENT' ||
|
|
$params[$i]['SCHEDULE-AGENT'] == 'NONE')) {
|
|
$tmp = str_replace(array('MAILTO:', 'mailto:'), '', $attendee[$i]);
|
|
$tmp = new Horde_Mail_Rfc822_Address($tmp);
|
|
$this->_noItips[] = $tmp->bare_address;
|
|
}
|
|
}
|
|
} catch (Horde_Icalendar_Exception $e) {}
|
|
|
|
return true;
|
|
}
|
|
|
|
protected function _postSave(Kronolith_Event $event)
|
|
{
|
|
if (!$this->_dav->getInternalObjectId($this->_params['object'], $this->_calendar)) {
|
|
$this->_dav->addObjectMap($event->id, $this->_params['object'], $this->_calendar);
|
|
}
|
|
|
|
// Send iTip messages.
|
|
// Notifications will get lost, there is no way to return messages
|
|
// to clients.
|
|
$event_copy = clone($event);
|
|
$event_copy->attendees = array_diff_key($event->attendees, array_flip($this->_noItips));
|
|
Kronolith::sendITipNotifications(
|
|
$event_copy,
|
|
new Horde_Notification_Handler(new Horde_Notification_Storage_Object()),
|
|
Kronolith::ITIP_REQUEST
|
|
);
|
|
}
|
|
|
|
} |