forked from friendica/friendica-addons
		
	
		
			
				
	
	
		
			287 lines
		
	
	
		
			No EOL
		
	
	
		
			7.7 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
			
		
		
	
	
			287 lines
		
	
	
		
			No EOL
		
	
	
		
			7.7 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
<?php
 | 
						|
 | 
						|
abstract class Sabre_CalDAV_Backend_Common extends Sabre_CalDAV_Backend_Abstract
 | 
						|
{
 | 
						|
	/**
 | 
						|
	 * @var array
 | 
						|
	 */
 | 
						|
	protected $propertyMap = array(
 | 
						|
		'{DAV:}displayname'                                   => 'displayname',
 | 
						|
		'{urn:ietf:params:xml:ns:caldav}calendar-description' => 'description',
 | 
						|
		'{urn:ietf:params:xml:ns:caldav}calendar-timezone'    => 'timezone',
 | 
						|
		'{http://apple.com/ns/ical/}calendar-order'           => 'calendarorder',
 | 
						|
		'{http://apple.com/ns/ical/}calendar-color'           => 'calendarcolor',
 | 
						|
	);
 | 
						|
 | 
						|
 | 
						|
	/**
 | 
						|
	 * @abstract
 | 
						|
	 * @return int
 | 
						|
	 */
 | 
						|
	abstract public function getNamespace();
 | 
						|
 | 
						|
	/**
 | 
						|
	 * @static
 | 
						|
	 * @abstract
 | 
						|
	 * @return string
 | 
						|
	 */
 | 
						|
	abstract public static function getBackendTypeName();
 | 
						|
 | 
						|
 | 
						|
	/**
 | 
						|
	 * @param int $calendarId
 | 
						|
	 * @param string $sd
 | 
						|
	 * @param string $ed
 | 
						|
	 * @param string $base_path
 | 
						|
	 * @return array
 | 
						|
	 */
 | 
						|
	abstract public function listItemsByRange($calendarId, $sd, $ed, $base_path);
 | 
						|
 | 
						|
 | 
						|
	/**
 | 
						|
	 * @var array
 | 
						|
	 */
 | 
						|
	static private $calendarCache = array();
 | 
						|
 | 
						|
	/**
 | 
						|
	 * @var array
 | 
						|
	 */
 | 
						|
	static private $calendarObjectCache = array();
 | 
						|
 | 
						|
	/**
 | 
						|
	 * @static
 | 
						|
	 * @param int $calendarId
 | 
						|
	 * @return array
 | 
						|
	 */
 | 
						|
	static public function loadCalendarById($calendarId)
 | 
						|
	{
 | 
						|
		if (!isset(self::$calendarCache[$calendarId])) {
 | 
						|
			$c                                = q("SELECT * FROM %s%scalendars WHERE `id` = %d", CALDAV_SQL_DB, CALDAV_SQL_PREFIX, IntVal($calendarId));
 | 
						|
			self::$calendarCache[$calendarId] = $c[0];
 | 
						|
		}
 | 
						|
		return self::$calendarCache[$calendarId];
 | 
						|
	}
 | 
						|
 | 
						|
	/**
 | 
						|
	 * @static
 | 
						|
	 * @param int $obj_id
 | 
						|
	 * @return array
 | 
						|
	 */
 | 
						|
	static public function loadCalendarobjectById($obj_id)
 | 
						|
	{
 | 
						|
		if (!isset(self::$calendarObjectCache[$obj_id])) {
 | 
						|
			$o                                  = q("SELECT * FROM %s%scalendarobjects WHERE `id` = %d",
 | 
						|
				CALDAV_SQL_DB, CALDAV_SQL_PREFIX, IntVal($obj_id)
 | 
						|
			);
 | 
						|
			self::$calendarObjectCache[$obj_id] = $o[0];
 | 
						|
		}
 | 
						|
		return self::$calendarObjectCache[$obj_id];
 | 
						|
	}
 | 
						|
 | 
						|
 | 
						|
	/**
 | 
						|
	 * @static
 | 
						|
	 * @param Sabre\VObject\Component\VEvent $component
 | 
						|
	 * @return int
 | 
						|
	 */
 | 
						|
	public static function getDtEndTimeStamp(&$component)
 | 
						|
	{
 | 
						|
		/** @var Sabre\VObject\Property\DateTime $dtstart */
 | 
						|
		$dtstart = $component->__get("DTSTART");
 | 
						|
		if ($component->__get("DTEND")) {
 | 
						|
			/** @var Sabre\VObject\Property\DateTime $dtend */
 | 
						|
			$dtend = $component->__get("DTEND");
 | 
						|
			return $dtend->getDateTime()->getTimeStamp();
 | 
						|
		} elseif ($component->__get("DURATION")) {
 | 
						|
			$endDate = clone $dtstart->getDateTime();
 | 
						|
			$endDate->add(Sabre\VObject\DateTimeParser::parse($component->__get("DURATION")->value));
 | 
						|
			return $endDate->getTimeStamp();
 | 
						|
		} elseif ($dtstart->getDateType() === Sabre\VObject\Property\DateTime::DATE) {
 | 
						|
			$endDate = clone $dtstart->getDateTime();
 | 
						|
			$endDate->modify('+1 day');
 | 
						|
			return $endDate->getTimeStamp();
 | 
						|
		} else {
 | 
						|
			return $dtstart->getDateTime()->getTimeStamp() + 3600;
 | 
						|
		}
 | 
						|
 | 
						|
	}
 | 
						|
 | 
						|
 | 
						|
	/**
 | 
						|
	 * Parses some information from calendar objects, used for optimized
 | 
						|
	 * calendar-queries.
 | 
						|
	 *
 | 
						|
	 * Returns an array with the following keys:
 | 
						|
	 *   * etag
 | 
						|
	 *   * size
 | 
						|
	 *   * componentType
 | 
						|
	 *   * firstOccurence
 | 
						|
	 *   * lastOccurence
 | 
						|
	 *
 | 
						|
	 * @param string $calendarData
 | 
						|
	 * @throws Sabre_DAV_Exception_BadRequest
 | 
						|
	 * @return array
 | 
						|
	 */
 | 
						|
	protected function getDenormalizedData($calendarData)
 | 
						|
	{
 | 
						|
		/** @var Sabre\VObject\Component\VEvent $vObject */
 | 
						|
		$vObject        = Sabre\VObject\Reader::read($calendarData);
 | 
						|
		$componentType  = null;
 | 
						|
		$component      = null;
 | 
						|
		$firstOccurence = null;
 | 
						|
		$lastOccurence  = null;
 | 
						|
 | 
						|
		foreach ($vObject->getComponents() as $component) {
 | 
						|
			if ($component->name !== 'VTIMEZONE') {
 | 
						|
				$componentType = $component->name;
 | 
						|
				break;
 | 
						|
			}
 | 
						|
		}
 | 
						|
		if (!$componentType) {
 | 
						|
			throw new Sabre_DAV_Exception_BadRequest('Calendar objects must have a VJOURNAL, VEVENT or VTODO component');
 | 
						|
		}
 | 
						|
		if ($componentType === 'VEVENT') {
 | 
						|
			/** @var Sabre\VObject\Component\VEvent $component */
 | 
						|
			/** @var Sabre\VObject\Property\DateTime $dtstart  */
 | 
						|
			$dtstart        = $component->__get("DTSTART");
 | 
						|
			$firstOccurence = $dtstart->getDateTime()->getTimeStamp();
 | 
						|
			// Finding the last occurence is a bit harder
 | 
						|
			if (!$component->__get("RRULE")) {
 | 
						|
				$lastOccurence = self::getDtEndTimeStamp($component);
 | 
						|
			} else {
 | 
						|
				$it      = new Sabre\VObject\RecurrenceIterator($vObject, (string)$component->__get("UID"));
 | 
						|
				$maxDate = new DateTime(CALDAV_MAX_YEAR . "-01-01");
 | 
						|
				if ($it->isInfinite()) {
 | 
						|
					$lastOccurence = $maxDate->getTimeStamp();
 | 
						|
				} else {
 | 
						|
					$end = $it->getDtEnd();
 | 
						|
					while ($it->valid() && $end < $maxDate) {
 | 
						|
						$end = $it->getDtEnd();
 | 
						|
						$it->next();
 | 
						|
 | 
						|
					}
 | 
						|
					$lastOccurence = $end->getTimeStamp();
 | 
						|
				}
 | 
						|
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		return array(
 | 
						|
			'etag'           => md5($calendarData),
 | 
						|
			'size'           => strlen($calendarData),
 | 
						|
			'componentType'  => $componentType,
 | 
						|
			'firstOccurence' => $firstOccurence,
 | 
						|
			'lastOccurence'  => $lastOccurence,
 | 
						|
		);
 | 
						|
 | 
						|
	}
 | 
						|
 | 
						|
	/**
 | 
						|
	 * Updates properties for a calendar.
 | 
						|
	 *
 | 
						|
	 * The mutations array uses the propertyName in clark-notation as key,
 | 
						|
	 * and the array value for the property value. In the case a property
 | 
						|
	 * should be deleted, the property value will be null.
 | 
						|
	 *
 | 
						|
	 * This method must be atomic. If one property cannot be changed, the
 | 
						|
	 * entire operation must fail.
 | 
						|
	 *
 | 
						|
	 * If the operation was successful, true can be returned.
 | 
						|
	 * If the operation failed, false can be returned.
 | 
						|
	 *
 | 
						|
	 * Deletion of a non-existent property is always successful.
 | 
						|
	 *
 | 
						|
	 * Lastly, it is optional to return detailed information about any
 | 
						|
	 * failures. In this case an array should be returned with the following
 | 
						|
	 * structure:
 | 
						|
	 *
 | 
						|
	 * array(
 | 
						|
	 *   403 => array(
 | 
						|
	 *      '{DAV:}displayname' => null,
 | 
						|
	 *   ),
 | 
						|
	 *   424 => array(
 | 
						|
	 *      '{DAV:}owner' => null,
 | 
						|
	 *   )
 | 
						|
	 * )
 | 
						|
	 *
 | 
						|
	 * In this example it was forbidden to update {DAV:}displayname.
 | 
						|
	 * (403 Forbidden), which in turn also caused {DAV:}owner to fail
 | 
						|
	 * (424 Failed Dependency) because the request needs to be atomic.
 | 
						|
	 *
 | 
						|
	 * @param mixed $calendarId
 | 
						|
	 * @param array $mutations
 | 
						|
	 * @return bool|array
 | 
						|
	 */
 | 
						|
	public function updateCalendar($calendarId, array $mutations)
 | 
						|
	{
 | 
						|
 | 
						|
		$newValues = array();
 | 
						|
		$result    = array(
 | 
						|
			200 => array(), // Ok
 | 
						|
			403 => array(), // Forbidden
 | 
						|
			424 => array(), // Failed Dependency
 | 
						|
		);
 | 
						|
 | 
						|
		$hasError = false;
 | 
						|
 | 
						|
		foreach ($mutations as $propertyName=> $propertyValue) {
 | 
						|
 | 
						|
			// We don't know about this property.
 | 
						|
			if (!isset($this->propertyMap[$propertyName])) {
 | 
						|
				$hasError                   = true;
 | 
						|
				$result[403][$propertyName] = null;
 | 
						|
				unset($mutations[$propertyName]);
 | 
						|
				continue;
 | 
						|
			}
 | 
						|
 | 
						|
			$fieldName             = $this->propertyMap[$propertyName];
 | 
						|
			$newValues[$fieldName] = $propertyValue;
 | 
						|
 | 
						|
		}
 | 
						|
 | 
						|
		// If there were any errors we need to fail the request
 | 
						|
		if ($hasError) {
 | 
						|
			// Properties has the remaining properties
 | 
						|
			foreach ($mutations as $propertyName=> $propertyValue) {
 | 
						|
				$result[424][$propertyName] = null;
 | 
						|
			}
 | 
						|
 | 
						|
			// Removing unused statuscodes for cleanliness
 | 
						|
			foreach ($result as $status=> $properties) {
 | 
						|
				if (is_array($properties) && count($properties) === 0) unset($result[$status]);
 | 
						|
			}
 | 
						|
 | 
						|
			return $result;
 | 
						|
 | 
						|
		}
 | 
						|
 | 
						|
		$this->increaseCalendarCtag($calendarId);
 | 
						|
 | 
						|
		$valuesSql = array();
 | 
						|
		foreach ($newValues as $fieldName=> $value) $valuesSql[] = "`" . $fieldName . "` = '" . dbesc($value) . "'";
 | 
						|
		if (count($valuesSql) > 0) {
 | 
						|
			q("UPDATE %s%scalendars SET " . implode(", ", $valuesSql) . " WHERE `id` = %d", CALDAV_SQL_DB, CALDAV_SQL_PREFIX, IntVal($calendarId));
 | 
						|
		}
 | 
						|
 | 
						|
		return true;
 | 
						|
 | 
						|
	}
 | 
						|
 | 
						|
	/**
 | 
						|
	 * @param int $calendarId
 | 
						|
	 */
 | 
						|
	protected function increaseCalendarCtag($calendarId)
 | 
						|
	{
 | 
						|
		q("UPDATE %s%scalendars SET `ctag` = `ctag` + 1 WHERE `id` = '%d'", CALDAV_SQL_DB, CALDAV_SQL_PREFIX, IntVal($calendarId));
 | 
						|
		self::$calendarObjectCache = array();
 | 
						|
	}
 | 
						|
 | 
						|
	/**
 | 
						|
	 * @abstract
 | 
						|
	 * @param int $calendar_id
 | 
						|
	 * @param int $calendarobject_id
 | 
						|
	 * @return string
 | 
						|
	 */
 | 
						|
	abstract function getItemDetailRedirect($calendar_id, $calendarobject_id);
 | 
						|
 | 
						|
} |