mirror of
https://github.com/the-djmaze/snappymail.git
synced 2026-08-31 21:19:21 +03:00
Remote Synchronization (CardDAV) improvements:
now it supports address book url auto detection (and works with Google contacts, icloud and etc.)
This commit is contained in:
parent
92f2caf700
commit
1fb553e77e
249 changed files with 1338 additions and 1453 deletions
|
|
@ -0,0 +1,108 @@
|
|||
<?php
|
||||
|
||||
namespace SabreForRainLoop\VObject\Component;
|
||||
use SabreForRainLoop\VObject;
|
||||
|
||||
/**
|
||||
* VAlarm component
|
||||
*
|
||||
* This component contains some additional functionality specific for VALARMs.
|
||||
*
|
||||
* @copyright Copyright (C) 2007-2013 fruux GmbH (https://fruux.com/).
|
||||
* @author Evert Pot (http://evertpot.com/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
class VAlarm extends VObject\Component {
|
||||
|
||||
/**
|
||||
* Returns a DateTime object when this alarm is going to trigger.
|
||||
*
|
||||
* This ignores repeated alarm, only the first trigger is returned.
|
||||
*
|
||||
* @return DateTime
|
||||
*/
|
||||
public function getEffectiveTriggerTime() {
|
||||
|
||||
$trigger = $this->TRIGGER;
|
||||
if(!isset($trigger['VALUE']) || strtoupper($trigger['VALUE']) === 'DURATION') {
|
||||
$triggerDuration = VObject\DateTimeParser::parseDuration($this->TRIGGER);
|
||||
$related = (isset($trigger['RELATED']) && strtoupper($trigger['RELATED']) == 'END') ? 'END' : 'START';
|
||||
|
||||
$parentComponent = $this->parent;
|
||||
if ($related === 'START') {
|
||||
|
||||
if ($parentComponent->name === 'VTODO') {
|
||||
$propName = 'DUE';
|
||||
} else {
|
||||
$propName = 'DTSTART';
|
||||
}
|
||||
|
||||
$effectiveTrigger = clone $parentComponent->$propName->getDateTime();
|
||||
$effectiveTrigger->add($triggerDuration);
|
||||
} else {
|
||||
if ($parentComponent->name === 'VTODO') {
|
||||
$endProp = 'DUE';
|
||||
} elseif ($parentComponent->name === 'VEVENT') {
|
||||
$endProp = 'DTEND';
|
||||
} else {
|
||||
throw new \LogicException('time-range filters on VALARM components are only supported when they are a child of VTODO or VEVENT');
|
||||
}
|
||||
|
||||
if (isset($parentComponent->$endProp)) {
|
||||
$effectiveTrigger = clone $parentComponent->$endProp->getDateTime();
|
||||
$effectiveTrigger->add($triggerDuration);
|
||||
} elseif (isset($parentComponent->DURATION)) {
|
||||
$effectiveTrigger = clone $parentComponent->DTSTART->getDateTime();
|
||||
$duration = VObject\DateTimeParser::parseDuration($parentComponent->DURATION);
|
||||
$effectiveTrigger->add($duration);
|
||||
$effectiveTrigger->add($triggerDuration);
|
||||
} else {
|
||||
$effectiveTrigger = clone $parentComponent->DTSTART->getDateTime();
|
||||
$effectiveTrigger->add($triggerDuration);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$effectiveTrigger = $trigger->getDateTime();
|
||||
}
|
||||
return $effectiveTrigger;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true or false depending on if the event falls in the specified
|
||||
* time-range. This is used for filtering purposes.
|
||||
*
|
||||
* The rules used to determine if an event falls within the specified
|
||||
* time-range is based on the CalDAV specification.
|
||||
*
|
||||
* @param \DateTime $start
|
||||
* @param \DateTime $end
|
||||
* @return bool
|
||||
*/
|
||||
public function isInTimeRange(\DateTime $start, \DateTime $end) {
|
||||
|
||||
$effectiveTrigger = $this->getEffectiveTriggerTime();
|
||||
|
||||
if (isset($this->DURATION)) {
|
||||
$duration = VObject\DateTimeParser::parseDuration($this->DURATION);
|
||||
$repeat = (string)$this->repeat;
|
||||
if (!$repeat) {
|
||||
$repeat = 1;
|
||||
}
|
||||
|
||||
$period = new \DatePeriod($effectiveTrigger, $duration, (int)$repeat);
|
||||
|
||||
foreach($period as $occurrence) {
|
||||
|
||||
if ($start <= $occurrence && $end > $occurrence) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
} else {
|
||||
return ($start <= $effectiveTrigger && $end > $effectiveTrigger);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,369 @@
|
|||
<?php
|
||||
|
||||
namespace SabreForRainLoop\VObject\Component;
|
||||
|
||||
use
|
||||
SabreForRainLoop\VObject,
|
||||
SabreForRainLoop\VObject\Component;
|
||||
|
||||
/**
|
||||
* The VCalendar component
|
||||
*
|
||||
* This component adds functionality to a component, specific for a VCALENDAR.
|
||||
*
|
||||
* @copyright Copyright (C) 2007-2013 fruux GmbH (https://fruux.com/).
|
||||
* @author Evert Pot (http://evertpot.com/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
class VCalendar extends VObject\Document {
|
||||
|
||||
/**
|
||||
* The default name for this component.
|
||||
*
|
||||
* This should be 'VCALENDAR' or 'VCARD'.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
static public $defaultName = 'VCALENDAR';
|
||||
|
||||
/**
|
||||
* This is a list of components, and which classes they should map to.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
static public $componentMap = array(
|
||||
'VEVENT' => 'SabreForRainLoop\\VObject\\Component\\VEvent',
|
||||
'VFREEBUSY' => 'SabreForRainLoop\\VObject\\Component\\VFreeBusy',
|
||||
'VJOURNAL' => 'SabreForRainLoop\\VObject\\Component\\VJournal',
|
||||
'VTODO' => 'SabreForRainLoop\\VObject\\Component\\VTodo',
|
||||
'VALARM' => 'SabreForRainLoop\\VObject\\Component\\VAlarm',
|
||||
);
|
||||
|
||||
/**
|
||||
* List of value-types, and which classes they map to.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
static public $valueMap = array(
|
||||
'BINARY' => 'SabreForRainLoop\\VObject\\Property\\Binary',
|
||||
'BOOLEAN' => 'SabreForRainLoop\\VObject\\Property\\Boolean',
|
||||
'CAL-ADDRESS' => 'SabreForRainLoop\\VObject\\Property\\ICalendar\\CalAddress',
|
||||
'DATE' => 'SabreForRainLoop\\VObject\\Property\\ICalendar\\Date',
|
||||
'DATE-TIME' => 'SabreForRainLoop\\VObject\\Property\\ICalendar\\DateTime',
|
||||
'DURATION' => 'SabreForRainLoop\\VObject\\Property\\ICalendar\\Duration',
|
||||
'FLOAT' => 'SabreForRainLoop\\VObject\\Property\\Float',
|
||||
'INTEGER' => 'SabreForRainLoop\\VObject\\Property\\Integer',
|
||||
'PERIOD' => 'SabreForRainLoop\\VObject\\Property\\ICalendar\\Period',
|
||||
'RECUR' => 'SabreForRainLoop\\VObject\\Property\\ICalendar\\Recur',
|
||||
'TEXT' => 'SabreForRainLoop\\VObject\\Property\\Text',
|
||||
'TIME' => 'SabreForRainLoop\\VObject\\Property\\Time',
|
||||
'UNKNOWN' => 'SabreForRainLoop\\VObject\\Property\\Unknown', // jCard / jCal-only.
|
||||
'URI' => 'SabreForRainLoop\\VObject\\Property\\Uri',
|
||||
'UTC-OFFSET' => 'SabreForRainLoop\\VObject\\Property\\UtcOffset',
|
||||
);
|
||||
|
||||
/**
|
||||
* List of properties, and which classes they map to.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
static public $propertyMap = array(
|
||||
// Calendar properties
|
||||
'CALSCALE' => 'SabreForRainLoop\\VObject\\Property\\FlatText',
|
||||
'METHOD' => 'SabreForRainLoop\\VObject\\Property\\FlatText',
|
||||
'PRODID' => 'SabreForRainLoop\\VObject\\Property\\FlatText',
|
||||
'VERSION' => 'SabreForRainLoop\\VObject\\Property\\FlatText',
|
||||
|
||||
// Component properties
|
||||
'ATTACH' => 'SabreForRainLoop\\VObject\\Property\\Binary',
|
||||
'CATEGORIES' => 'SabreForRainLoop\\VObject\\Property\\Text',
|
||||
'CLASS' => 'SabreForRainLoop\\VObject\\Property\\FlatText',
|
||||
'COMMENT' => 'SabreForRainLoop\\VObject\\Property\\FlatText',
|
||||
'DESCRIPTION' => 'SabreForRainLoop\\VObject\\Property\\FlatText',
|
||||
'GEO' => 'SabreForRainLoop\\VObject\\Property\\Float',
|
||||
'LOCATION' => 'SabreForRainLoop\\VObject\\Property\\FlatText',
|
||||
'PERCENT-COMPLETE' => 'SabreForRainLoop\\VObject\\Property\\Integer',
|
||||
'PRIORITY' => 'SabreForRainLoop\\VObject\\Property\\Integer',
|
||||
'RESOURCES' => 'SabreForRainLoop\\VObject\\Property\\Text',
|
||||
'STATUS' => 'SabreForRainLoop\\VObject\\Property\\FlatText',
|
||||
'SUMMARY' => 'SabreForRainLoop\\VObject\\Property\\FlatText',
|
||||
|
||||
// Date and Time Component Properties
|
||||
'COMPLETED' => 'SabreForRainLoop\\VObject\\Property\\ICalendar\\DateTime',
|
||||
'DTEND' => 'SabreForRainLoop\\VObject\\Property\\ICalendar\\DateTime',
|
||||
'DUE' => 'SabreForRainLoop\\VObject\\Property\\ICalendar\\DateTime',
|
||||
'DTSTART' => 'SabreForRainLoop\\VObject\\Property\\ICalendar\\DateTime',
|
||||
'DURATION' => 'SabreForRainLoop\\VObject\\Property\\ICalendar\\Duration',
|
||||
'FREEBUSY' => 'SabreForRainLoop\\VObject\\Property\\ICalendar\\Period',
|
||||
'TRANSP' => 'SabreForRainLoop\\VObject\\Property\\FlatText',
|
||||
|
||||
// Time Zone Component Properties
|
||||
'TZID' => 'SabreForRainLoop\\VObject\\Property\\FlatText',
|
||||
'TZNAME' => 'SabreForRainLoop\\VObject\\Property\\FlatText',
|
||||
'TZOFFSETFROM' => 'SabreForRainLoop\\VObject\\Property\\UtcOffset',
|
||||
'TZOFFSETTO' => 'SabreForRainLoop\\VObject\\Property\\UtcOffset',
|
||||
'TZURL' => 'SabreForRainLoop\\VObject\\Property\\Uri',
|
||||
|
||||
// Relationship Component Properties
|
||||
'ATTENDEE' => 'SabreForRainLoop\\VObject\\Property\\ICalendar\\CalAddress',
|
||||
'CONTACT' => 'SabreForRainLoop\\VObject\\Property\\FlatText',
|
||||
'ORGANIZER' => 'SabreForRainLoop\\VObject\\Property\\ICalendar\\CalAddress',
|
||||
'RECURRENCE-ID' => 'SabreForRainLoop\\VObject\\Property\\ICalendar\\DateTime',
|
||||
'RELATED-TO' => 'SabreForRainLoop\\VObject\\Property\\FlatText',
|
||||
'URL' => 'SabreForRainLoop\\VObject\\Property\\Uri',
|
||||
'UID' => 'SabreForRainLoop\\VObject\\Property\\FlatText',
|
||||
|
||||
// Recurrence Component Properties
|
||||
'EXDATE' => 'SabreForRainLoop\\VObject\\Property\\ICalendar\\DateTime',
|
||||
'RDATE' => 'SabreForRainLoop\\VObject\\Property\\ICalendar\\DateTime',
|
||||
'RRULE' => 'SabreForRainLoop\\VObject\\Property\\ICalendar\\Recur',
|
||||
'EXRULE' => 'SabreForRainLoop\\VObject\\Property\\ICalendar\\Recur', // Deprecated since rfc5545
|
||||
|
||||
// Alarm Component Properties
|
||||
'ACTION' => 'SabreForRainLoop\\VObject\\Property\\FlatText',
|
||||
'REPEAT' => 'SabreForRainLoop\\VObject\\Property\\Integer',
|
||||
'TRIGGER' => 'SabreForRainLoop\\VObject\\Property\\ICalendar\\Duration',
|
||||
|
||||
// Change Management Component Properties
|
||||
'CREATED' => 'SabreForRainLoop\\VObject\\Property\\ICalendar\\DateTime',
|
||||
'DTSTAMP' => 'SabreForRainLoop\\VObject\\Property\\ICalendar\\DateTime',
|
||||
'LAST-MODIFIED' => 'SabreForRainLoop\\VObject\\Property\\ICalendar\\DateTime',
|
||||
'SEQUENCE' => 'SabreForRainLoop\\VObject\\Property\\Integer',
|
||||
|
||||
// Request Status
|
||||
'REQUEST-STATUS' => 'SabreForRainLoop\\VObject\\Property\\Text',
|
||||
|
||||
// Additions from draft-daboo-valarm-extensions-04
|
||||
'ALARM-AGENT' => 'SabreForRainLoop\\VObject\\Property\\Text',
|
||||
'ACKNOWLEDGED' => 'SabreForRainLoop\\VObject\\Property\\ICalendar\\DateTime',
|
||||
'PROXIMITY' => 'SabreForRainLoop\\VObject\\Property\\Text',
|
||||
'DEFAULT-ALARM' => 'SabreForRainLoop\\VObject\\Property\\Boolean',
|
||||
|
||||
);
|
||||
|
||||
/**
|
||||
* Returns the current document type.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function getDocumentType() {
|
||||
|
||||
return self::ICALENDAR20;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of all 'base components'. For instance, if an Event has
|
||||
* a recurrence rule, and one instance is overridden, the overridden event
|
||||
* will have the same UID, but will be excluded from this list.
|
||||
*
|
||||
* VTIMEZONE components will always be excluded.
|
||||
*
|
||||
* @param string $componentName filter by component name
|
||||
* @return array
|
||||
*/
|
||||
public function getBaseComponents($componentName = null) {
|
||||
|
||||
$components = array();
|
||||
foreach($this->children as $component) {
|
||||
|
||||
if (!$component instanceof VObject\Component)
|
||||
continue;
|
||||
|
||||
if (isset($component->{'RECURRENCE-ID'}))
|
||||
continue;
|
||||
|
||||
if ($componentName && $component->name !== strtoupper($componentName))
|
||||
continue;
|
||||
|
||||
if ($component->name === 'VTIMEZONE')
|
||||
continue;
|
||||
|
||||
$components[] = $component;
|
||||
|
||||
}
|
||||
|
||||
return $components;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* If this calendar object, has events with recurrence rules, this method
|
||||
* can be used to expand the event into multiple sub-events.
|
||||
*
|
||||
* Each event will be stripped from it's recurrence information, and only
|
||||
* the instances of the event in the specified timerange will be left
|
||||
* alone.
|
||||
*
|
||||
* In addition, this method will cause timezone information to be stripped,
|
||||
* and normalized to UTC.
|
||||
*
|
||||
* This method will alter the VCalendar. This cannot be reversed.
|
||||
*
|
||||
* This functionality is specifically used by the CalDAV standard. It is
|
||||
* possible for clients to request expand events, if they are rather simple
|
||||
* clients and do not have the possibility to calculate recurrences.
|
||||
*
|
||||
* @param DateTime $start
|
||||
* @param DateTime $end
|
||||
* @return void
|
||||
*/
|
||||
public function expand(\DateTime $start, \DateTime $end) {
|
||||
|
||||
$newEvents = array();
|
||||
|
||||
foreach($this->select('VEVENT') as $key=>$vevent) {
|
||||
|
||||
if (isset($vevent->{'RECURRENCE-ID'})) {
|
||||
unset($this->children[$key]);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
if (!$vevent->rrule) {
|
||||
unset($this->children[$key]);
|
||||
if ($vevent->isInTimeRange($start, $end)) {
|
||||
$newEvents[] = $vevent;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
$uid = (string)$vevent->uid;
|
||||
if (!$uid) {
|
||||
throw new \LogicException('Event did not have a UID!');
|
||||
}
|
||||
|
||||
$it = new VObject\RecurrenceIterator($this, $vevent->uid);
|
||||
$it->fastForward($start);
|
||||
|
||||
while($it->valid() && $it->getDTStart() < $end) {
|
||||
|
||||
if ($it->getDTEnd() > $start) {
|
||||
|
||||
$newEvents[] = $it->getEventObject();
|
||||
|
||||
}
|
||||
$it->next();
|
||||
|
||||
}
|
||||
unset($this->children[$key]);
|
||||
|
||||
}
|
||||
|
||||
// Setting all properties to UTC time.
|
||||
foreach($newEvents as $newEvent) {
|
||||
|
||||
foreach($newEvent->children as $child) {
|
||||
if ($child instanceof VObject\Property\ICalendar\DateTime && $child->hasTime()) {
|
||||
$dt = $child->getDateTimes();
|
||||
// We only need to update the first timezone, because
|
||||
// setDateTimes will match all other timezones to the
|
||||
// first.
|
||||
$dt[0]->setTimeZone(new \DateTimeZone('UTC'));
|
||||
$child->setDateTimes($dt);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
$this->add($newEvent);
|
||||
|
||||
}
|
||||
|
||||
// Removing all VTIMEZONE components
|
||||
unset($this->VTIMEZONE);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This method should return a list of default property values.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getDefaults() {
|
||||
|
||||
return array(
|
||||
'VERSION' => '2.0',
|
||||
'PRODID' => '-//Sabre//Sabre VObject ' . VObject\Version::VERSION . '//EN',
|
||||
'CALSCALE' => 'GREGORIAN',
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the node for correctness.
|
||||
* An array is returned with warnings.
|
||||
*
|
||||
* Every item in the array has the following properties:
|
||||
* * level - (number between 1 and 3 with severity information)
|
||||
* * message - (human readable message)
|
||||
* * node - (reference to the offending node)
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function validate($options = 0) {
|
||||
|
||||
$warnings = array();
|
||||
|
||||
$version = $this->select('VERSION');
|
||||
if (count($version)!==1) {
|
||||
$warnings[] = array(
|
||||
'level' => 1,
|
||||
'message' => 'The VERSION property must appear in the VCALENDAR component exactly 1 time',
|
||||
'node' => $this,
|
||||
);
|
||||
} else {
|
||||
if ((string)$this->VERSION !== '2.0') {
|
||||
$warnings[] = array(
|
||||
'level' => 1,
|
||||
'message' => 'Only iCalendar version 2.0 as defined in rfc5545 is supported.',
|
||||
'node' => $this,
|
||||
);
|
||||
}
|
||||
}
|
||||
$version = $this->select('PRODID');
|
||||
if (count($version)!==1) {
|
||||
$warnings[] = array(
|
||||
'level' => 2,
|
||||
'message' => 'The PRODID property must appear in the VCALENDAR component exactly 1 time',
|
||||
'node' => $this,
|
||||
);
|
||||
}
|
||||
if (count($this->CALSCALE) > 1) {
|
||||
$warnings[] = array(
|
||||
'level' => 2,
|
||||
'message' => 'The CALSCALE property must not be specified more than once.',
|
||||
'node' => $this,
|
||||
);
|
||||
}
|
||||
if (count($this->METHOD) > 1) {
|
||||
$warnings[] = array(
|
||||
'level' => 2,
|
||||
'message' => 'The METHOD property must not be specified more than once.',
|
||||
'node' => $this,
|
||||
);
|
||||
}
|
||||
|
||||
$componentsFound = 0;
|
||||
foreach($this->children as $child) {
|
||||
if($child instanceof Component) {
|
||||
$componentsFound++;
|
||||
}
|
||||
}
|
||||
|
||||
if ($componentsFound===0) {
|
||||
$warnings[] = array(
|
||||
'level' => 1,
|
||||
'message' => 'An iCalendar object must have at least 1 component.',
|
||||
'node' => $this,
|
||||
);
|
||||
}
|
||||
|
||||
return array_merge(
|
||||
$warnings,
|
||||
parent::validate()
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,353 @@
|
|||
<?php
|
||||
|
||||
namespace SabreForRainLoop\VObject\Component;
|
||||
|
||||
use
|
||||
SabreForRainLoop\VObject;
|
||||
|
||||
/**
|
||||
* The VCard component
|
||||
*
|
||||
* This component represents the BEGIN:VCARD and END:VCARD found in every
|
||||
* vcard.
|
||||
*
|
||||
* @copyright Copyright (C) 2007-2013 fruux GmbH (https://fruux.com/).
|
||||
* @author Evert Pot (http://evertpot.com/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
class VCard extends VObject\Document {
|
||||
|
||||
/**
|
||||
* The default name for this component.
|
||||
*
|
||||
* This should be 'VCALENDAR' or 'VCARD'.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
static public $defaultName = 'VCARD';
|
||||
|
||||
/**
|
||||
* Caching the version number
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
private $version = null;
|
||||
|
||||
/**
|
||||
* List of value-types, and which classes they map to.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
static public $valueMap = array(
|
||||
'BINARY' => 'SabreForRainLoop\\VObject\\Property\\Binary',
|
||||
'BOOLEAN' => 'SabreForRainLoop\\VObject\\Property\\Boolean',
|
||||
'CONTENT-ID' => 'SabreForRainLoop\\VObject\\Property\\FlatText', // vCard 2.1 only
|
||||
'DATE' => 'SabreForRainLoop\\VObject\\Property\\VCard\\Date',
|
||||
'DATE-TIME' => 'SabreForRainLoop\\VObject\\Property\\VCard\\DateTime',
|
||||
'DATE-AND-OR-TIME' => 'SabreForRainLoop\\VObject\\Property\\VCard\\DateAndOrTime', // vCard only
|
||||
'FLOAT' => 'SabreForRainLoop\\VObject\\Property\\Float',
|
||||
'INTEGER' => 'SabreForRainLoop\\VObject\\Property\\Integer',
|
||||
'LANGUAGE-TAG' => 'SabreForRainLoop\\VObject\\Property\\VCard\\LanguageTag',
|
||||
'TIMESTAMP' => 'SabreForRainLoop\\VObject\\Property\\VCard\\TimeStamp',
|
||||
'TEXT' => 'SabreForRainLoop\\VObject\\Property\\Text',
|
||||
'TIME' => 'SabreForRainLoop\\VObject\\Property\\Time',
|
||||
'UNKNOWN' => 'SabreForRainLoop\\VObject\\Property\\Unknown', // jCard / jCal-only.
|
||||
'URI' => 'SabreForRainLoop\\VObject\\Property\\Uri',
|
||||
'URL' => 'SabreForRainLoop\\VObject\\Property\\Uri', // vCard 2.1 only
|
||||
'UTC-OFFSET' => 'SabreForRainLoop\\VObject\\Property\\UtcOffset',
|
||||
);
|
||||
|
||||
/**
|
||||
* List of properties, and which classes they map to.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
static public $propertyMap = array(
|
||||
|
||||
// vCard 2.1 properties and up
|
||||
'N' => 'SabreForRainLoop\\VObject\\Property\\Text',
|
||||
'FN' => 'SabreForRainLoop\\VObject\\Property\\FlatText',
|
||||
'PHOTO' => 'SabreForRainLoop\\VObject\\Property\\Binary', // Todo: we should add a class for Binary values.
|
||||
'BDAY' => 'SabreForRainLoop\\VObject\\Property\\VCard\\DateAndOrTime',
|
||||
'ADR' => 'SabreForRainLoop\\VObject\\Property\\Text',
|
||||
'LABEL' => 'SabreForRainLoop\\VObject\\Property\\FlatText', // Removed in vCard 4.0
|
||||
'TEL' => 'SabreForRainLoop\\VObject\\Property\\FlatText',
|
||||
'EMAIL' => 'SabreForRainLoop\\VObject\\Property\\FlatText',
|
||||
'MAILER' => 'SabreForRainLoop\\VObject\\Property\\FlatText', // Removed in vCard 4.0
|
||||
'GEO' => 'SabreForRainLoop\\VObject\\Property\\FlatText',
|
||||
'TITLE' => 'SabreForRainLoop\\VObject\\Property\\FlatText',
|
||||
'ROLE' => 'SabreForRainLoop\\VObject\\Property\\FlatText',
|
||||
'LOGO' => 'SabreForRainLoop\\VObject\\Property\\Binary',
|
||||
// 'AGENT' => 'SabreForRainLoop\\VObject\\Property\\', // Todo: is an embedded vCard. Probably rare, so
|
||||
// not supported at the moment
|
||||
'ORG' => 'SabreForRainLoop\\VObject\\Property\\Text',
|
||||
'NOTE' => 'SabreForRainLoop\\VObject\\Property\\FlatText',
|
||||
'REV' => 'SabreForRainLoop\\VObject\\Property\\VCard\\TimeStamp',
|
||||
'SOUND' => 'SabreForRainLoop\\VObject\\Property\\FlatText',
|
||||
'URL' => 'SabreForRainLoop\\VObject\\Property\\Uri',
|
||||
'UID' => 'SabreForRainLoop\\VObject\\Property\\FlatText',
|
||||
'VERSION' => 'SabreForRainLoop\\VObject\\Property\\FlatText',
|
||||
'KEY' => 'SabreForRainLoop\\VObject\\Property\\FlatText',
|
||||
'TZ' => 'SabreForRainLoop\\VObject\\Property\\Text',
|
||||
|
||||
// vCard 3.0 properties
|
||||
'CATEGORIES' => 'SabreForRainLoop\\VObject\\Property\\Text',
|
||||
'SORT-STRING' => 'SabreForRainLoop\\VObject\\Property\\FlatText',
|
||||
'PRODID' => 'SabreForRainLoop\\VObject\\Property\\FlatText',
|
||||
'NICKNAME' => 'SabreForRainLoop\\VObject\\Property\\Text',
|
||||
'CLASS' => 'SabreForRainLoop\\VObject\\Property\\FlatText', // Removed in vCard 4.0
|
||||
|
||||
// rfc2739 properties
|
||||
'FBURL' => 'SabreForRainLoop\\VObject\\Property\\Uri',
|
||||
'CAPURI' => 'SabreForRainLoop\\VObject\\Property\\Uri',
|
||||
'CALURI' => 'SabreForRainLoop\\VObject\\Property\\Uri',
|
||||
|
||||
// rfc4770 properties
|
||||
'IMPP' => 'SabreForRainLoop\\VObject\\Property\\Uri',
|
||||
|
||||
// vCard 4.0 properties
|
||||
'XML' => 'SabreForRainLoop\\VObject\\Property\\FlatText',
|
||||
'ANNIVERSARY' => 'SabreForRainLoop\\VObject\\Property\\VCard\\DateAndOrTime',
|
||||
'CLIENTPIDMAP' => 'SabreForRainLoop\\VObject\\Property\\Text',
|
||||
'LANG' => 'SabreForRainLoop\\VObject\\Property\\VCard\\LanguageTag',
|
||||
'GENDER' => 'SabreForRainLoop\\VObject\\Property\\Text',
|
||||
'KIND' => 'SabreForRainLoop\\VObject\\Property\\FlatText',
|
||||
|
||||
);
|
||||
|
||||
/**
|
||||
* Returns the current document type.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function getDocumentType() {
|
||||
|
||||
if (!$this->version) {
|
||||
$version = (string)$this->VERSION;
|
||||
switch($version) {
|
||||
case '2.1' :
|
||||
$this->version = self::VCARD21;
|
||||
break;
|
||||
case '3.0' :
|
||||
$this->version = self::VCARD30;
|
||||
break;
|
||||
case '4.0' :
|
||||
$this->version = self::VCARD40;
|
||||
break;
|
||||
default :
|
||||
$this->version = self::UNKNOWN;
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return $this->version;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the document to a different vcard version.
|
||||
*
|
||||
* Use one of the VCARD constants for the target. This method will return
|
||||
* a copy of the vcard in the new version.
|
||||
*
|
||||
* At the moment the only supported conversion is from 3.0 to 4.0.
|
||||
*
|
||||
* If input and output version are identical, a clone is returned.
|
||||
*
|
||||
* @param int $target
|
||||
* @return VCard
|
||||
*/
|
||||
public function convert($target) {
|
||||
|
||||
$converter = new VObject\VCardConverter();
|
||||
return $converter->convert($this, $target);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* VCards with version 2.1, 3.0 and 4.0 are found.
|
||||
*
|
||||
* If the VCARD doesn't know its version, 2.1 is assumed.
|
||||
*/
|
||||
const DEFAULT_VERSION = self::VCARD21;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Validates the node for correctness.
|
||||
*
|
||||
* The following options are supported:
|
||||
* - Node::REPAIR - If something is broken, and automatic repair may
|
||||
* be attempted.
|
||||
*
|
||||
* An array is returned with warnings.
|
||||
*
|
||||
* Every item in the array has the following properties:
|
||||
* * level - (number between 1 and 3 with severity information)
|
||||
* * message - (human readable message)
|
||||
* * node - (reference to the offending node)
|
||||
*
|
||||
* @param int $options
|
||||
* @return array
|
||||
*/
|
||||
public function validate($options = 0) {
|
||||
|
||||
$warnings = array();
|
||||
|
||||
$versionMap = array(
|
||||
self::VCARD21 => '2.1',
|
||||
self::VCARD30 => '3.0',
|
||||
self::VCARD40 => '4.0',
|
||||
);
|
||||
|
||||
$version = $this->select('VERSION');
|
||||
if (count($version)!==1) {
|
||||
$warnings[] = array(
|
||||
'level' => 1,
|
||||
'message' => 'The VERSION property must appear in the VCARD component exactly 1 time',
|
||||
'node' => $this,
|
||||
);
|
||||
if ($options & self::REPAIR) {
|
||||
$this->VERSION = $versionMap[self::DEFAULT_VERSION];
|
||||
}
|
||||
} else {
|
||||
$version = (string)$this->VERSION;
|
||||
if ($version!=='2.1' && $version!=='3.0' && $version!=='4.0') {
|
||||
$warnings[] = array(
|
||||
'level' => 1,
|
||||
'message' => 'Only vcard version 4.0 (RFC6350), version 3.0 (RFC2426) or version 2.1 (icm-vcard-2.1) are supported.',
|
||||
'node' => $this,
|
||||
);
|
||||
if ($options & self::REPAIR) {
|
||||
$this->VERSION = $versionMap[self::DEFAULT_VERSION];
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
$fn = $this->select('FN');
|
||||
if (count($fn)!==1) {
|
||||
$warnings[] = array(
|
||||
'level' => 1,
|
||||
'message' => 'The FN property must appear in the VCARD component exactly 1 time',
|
||||
'node' => $this,
|
||||
);
|
||||
if (($options & self::REPAIR) && count($fn) === 0) {
|
||||
// We're going to try to see if we can use the contents of the
|
||||
// N property.
|
||||
if (isset($this->N)) {
|
||||
$value = explode(';', (string)$this->N);
|
||||
if (isset($value[1]) && $value[1]) {
|
||||
$this->FN = $value[1] . ' ' . $value[0];
|
||||
} else {
|
||||
$this->FN = $value[0];
|
||||
}
|
||||
|
||||
// Otherwise, the ORG property may work
|
||||
} elseif (isset($this->ORG)) {
|
||||
$this->FN = (string)$this->ORG;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return array_merge(
|
||||
parent::validate($options),
|
||||
$warnings
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a preferred field.
|
||||
*
|
||||
* VCards can indicate wether a field such as ADR, TEL or EMAIL is
|
||||
* preferred by specifying TYPE=PREF (vcard 2.1, 3) or PREF=x (vcard 4, x
|
||||
* being a number between 1 and 100).
|
||||
*
|
||||
* If neither of those parameters are specified, the first is returned, if
|
||||
* a field with that name does not exist, null is returned.
|
||||
*
|
||||
* @param string $fieldName
|
||||
* @return VObject\Property|null
|
||||
*/
|
||||
public function preferred($propertyName) {
|
||||
|
||||
$preferred = null;
|
||||
$lastPref = 101;
|
||||
foreach($this->select($propertyName) as $field) {
|
||||
|
||||
$pref = 101;
|
||||
if (isset($field['TYPE']) && $field['TYPE']->has('PREF')) {
|
||||
$pref = 1;
|
||||
} elseif (isset($field['PREF'])) {
|
||||
$pref = $field['PREF']->getValue();
|
||||
}
|
||||
|
||||
if ($pref < $lastPref || is_null($preferred)) {
|
||||
$preferred = $field;
|
||||
$lastPref = $pref;
|
||||
}
|
||||
|
||||
}
|
||||
return $preferred;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This method should return a list of default property values.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getDefaults() {
|
||||
|
||||
return array(
|
||||
'VERSION' => '3.0',
|
||||
'PRODID' => '-//Sabre//Sabre VObject ' . VObject\Version::VERSION . '//EN',
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This method returns an array, with the representation as it should be
|
||||
* encoded in json. This is used to create jCard or jCal documents.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function jsonSerialize() {
|
||||
|
||||
// A vcard does not have sub-components, so we're overriding this
|
||||
// method to remove that array element.
|
||||
$properties = array();
|
||||
|
||||
foreach($this->children as $child) {
|
||||
$properties[] = $child->jsonSerialize();
|
||||
}
|
||||
|
||||
return array(
|
||||
strtolower($this->name),
|
||||
$properties,
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the default class for a property name.
|
||||
*
|
||||
* @param string $propertyName
|
||||
* @return string
|
||||
*/
|
||||
public function getClassNameForPropertyName($propertyName) {
|
||||
|
||||
$className = parent::getClassNameForPropertyName($propertyName);
|
||||
// In vCard 4, BINARY no longer exists, and we need URI instead.
|
||||
|
||||
if ($className == 'SabreForRainLoop\\VObject\\Property\\Binary' && $this->getDocumentType()===self::VCARD40) {
|
||||
return 'SabreForRainLoop\\VObject\\Property\\Uri';
|
||||
}
|
||||
return $className;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,70 @@
|
|||
<?php
|
||||
|
||||
namespace SabreForRainLoop\VObject\Component;
|
||||
use SabreForRainLoop\VObject;
|
||||
|
||||
/**
|
||||
* VEvent component
|
||||
*
|
||||
* This component contains some additional functionality specific for VEVENT's.
|
||||
*
|
||||
* @copyright Copyright (C) 2007-2013 fruux GmbH (https://fruux.com/).
|
||||
* @author Evert Pot (http://evertpot.com/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
class VEvent extends VObject\Component {
|
||||
|
||||
/**
|
||||
* Returns true or false depending on if the event falls in the specified
|
||||
* time-range. This is used for filtering purposes.
|
||||
*
|
||||
* The rules used to determine if an event falls within the specified
|
||||
* time-range is based on the CalDAV specification.
|
||||
*
|
||||
* @param \DateTime $start
|
||||
* @param \DateTime $end
|
||||
* @return bool
|
||||
*/
|
||||
public function isInTimeRange(\DateTime $start, \DateTime $end) {
|
||||
|
||||
if ($this->RRULE) {
|
||||
$it = new VObject\RecurrenceIterator($this);
|
||||
$it->fastForward($start);
|
||||
|
||||
// We fast-forwarded to a spot where the end-time of the
|
||||
// recurrence instance exceeded the start of the requested
|
||||
// time-range.
|
||||
//
|
||||
// If the starttime of the recurrence did not exceed the
|
||||
// end of the time range as well, we have a match.
|
||||
return ($it->getDTStart() < $end && $it->getDTEnd() > $start);
|
||||
|
||||
}
|
||||
|
||||
$effectiveStart = $this->DTSTART->getDateTime();
|
||||
if (isset($this->DTEND)) {
|
||||
|
||||
// The DTEND property is considered non inclusive. So for a 3 day
|
||||
// event in july, dtstart and dtend would have to be July 1st and
|
||||
// July 4th respectively.
|
||||
//
|
||||
// See:
|
||||
// http://tools.ietf.org/html/rfc5545#page-54
|
||||
$effectiveEnd = $this->DTEND->getDateTime();
|
||||
|
||||
} elseif (isset($this->DURATION)) {
|
||||
$effectiveEnd = clone $effectiveStart;
|
||||
$effectiveEnd->add( VObject\DateTimeParser::parseDuration($this->DURATION) );
|
||||
} elseif (!$this->DTSTART->hasTime()) {
|
||||
$effectiveEnd = clone $effectiveStart;
|
||||
$effectiveEnd->modify('+1 day');
|
||||
} else {
|
||||
$effectiveEnd = clone $effectiveStart;
|
||||
}
|
||||
return (
|
||||
($start <= $effectiveEnd) && ($end > $effectiveStart)
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
<?php
|
||||
|
||||
namespace SabreForRainLoop\VObject\Component;
|
||||
|
||||
use SabreForRainLoop\VObject;
|
||||
|
||||
/**
|
||||
* The VFreeBusy component
|
||||
*
|
||||
* This component adds functionality to a component, specific for VFREEBUSY
|
||||
* components.
|
||||
*
|
||||
* @copyright Copyright (C) 2007-2013 fruux GmbH (https://fruux.com/).
|
||||
* @author Evert Pot (http://evertpot.com/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
class VFreeBusy extends VObject\Component {
|
||||
|
||||
/**
|
||||
* Checks based on the contained FREEBUSY information, if a timeslot is
|
||||
* available.
|
||||
*
|
||||
* @param DateTime $start
|
||||
* @param Datetime $end
|
||||
* @return bool
|
||||
*/
|
||||
public function isFree(\DateTime $start, \Datetime $end) {
|
||||
|
||||
foreach($this->select('FREEBUSY') as $freebusy) {
|
||||
|
||||
// We are only interested in FBTYPE=BUSY (the default),
|
||||
// FBTYPE=BUSY-TENTATIVE or FBTYPE=BUSY-UNAVAILABLE.
|
||||
if (isset($freebusy['FBTYPE']) && strtoupper(substr((string)$freebusy['FBTYPE'],0,4))!=='BUSY') {
|
||||
continue;
|
||||
}
|
||||
|
||||
// The freebusy component can hold more than 1 value, separated by
|
||||
// commas.
|
||||
$periods = explode(',', (string)$freebusy);
|
||||
|
||||
foreach($periods as $period) {
|
||||
// Every period is formatted as [start]/[end]. The start is an
|
||||
// absolute UTC time, the end may be an absolute UTC time, or
|
||||
// duration (relative) value.
|
||||
list($busyStart, $busyEnd) = explode('/', $period);
|
||||
|
||||
$busyStart = VObject\DateTimeParser::parse($busyStart);
|
||||
$busyEnd = VObject\DateTimeParser::parse($busyEnd);
|
||||
if ($busyEnd instanceof \DateInterval) {
|
||||
$tmp = clone $busyStart;
|
||||
$tmp->add($busyEnd);
|
||||
$busyEnd = $tmp;
|
||||
}
|
||||
|
||||
if($start < $busyEnd && $end > $busyStart) {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
<?php
|
||||
|
||||
namespace SabreForRainLoop\VObject\Component;
|
||||
|
||||
use SabreForRainLoop\VObject;
|
||||
|
||||
/**
|
||||
* VJournal component
|
||||
*
|
||||
* This component contains some additional functionality specific for VJOURNALs.
|
||||
*
|
||||
* @copyright Copyright (C) 2007-2013 fruux GmbH (https://fruux.com/).
|
||||
* @author Evert Pot (http://evertpot.com/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
class VJournal extends VObject\Component {
|
||||
|
||||
/**
|
||||
* Returns true or false depending on if the event falls in the specified
|
||||
* time-range. This is used for filtering purposes.
|
||||
*
|
||||
* The rules used to determine if an event falls within the specified
|
||||
* time-range is based on the CalDAV specification.
|
||||
*
|
||||
* @param DateTime $start
|
||||
* @param DateTime $end
|
||||
* @return bool
|
||||
*/
|
||||
public function isInTimeRange(\DateTime $start, \DateTime $end) {
|
||||
|
||||
$dtstart = isset($this->DTSTART)?$this->DTSTART->getDateTime():null;
|
||||
if ($dtstart) {
|
||||
$effectiveEnd = clone $dtstart;
|
||||
if (!$this->DTSTART->hasTime()) {
|
||||
$effectiveEnd->modify('+1 day');
|
||||
}
|
||||
|
||||
return ($start <= $effectiveEnd && $end > $dtstart);
|
||||
|
||||
}
|
||||
return false;
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
<?php
|
||||
|
||||
namespace SabreForRainLoop\VObject\Component;
|
||||
|
||||
use SabreForRainLoop\VObject;
|
||||
|
||||
/**
|
||||
* VTodo component
|
||||
*
|
||||
* This component contains some additional functionality specific for VTODOs.
|
||||
*
|
||||
* @copyright Copyright (C) 2007-2013 fruux GmbH (https://fruux.com/).
|
||||
* @author Evert Pot (http://evertpot.com/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
class VTodo extends VObject\Component {
|
||||
|
||||
/**
|
||||
* Returns true or false depending on if the event falls in the specified
|
||||
* time-range. This is used for filtering purposes.
|
||||
*
|
||||
* The rules used to determine if an event falls within the specified
|
||||
* time-range is based on the CalDAV specification.
|
||||
*
|
||||
* @param DateTime $start
|
||||
* @param DateTime $end
|
||||
* @return bool
|
||||
*/
|
||||
public function isInTimeRange(\DateTime $start, \DateTime $end) {
|
||||
|
||||
$dtstart = isset($this->DTSTART)?$this->DTSTART->getDateTime():null;
|
||||
$duration = isset($this->DURATION)?VObject\DateTimeParser::parseDuration($this->DURATION):null;
|
||||
$due = isset($this->DUE)?$this->DUE->getDateTime():null;
|
||||
$completed = isset($this->COMPLETED)?$this->COMPLETED->getDateTime():null;
|
||||
$created = isset($this->CREATED)?$this->CREATED->getDateTime():null;
|
||||
|
||||
if ($dtstart) {
|
||||
if ($duration) {
|
||||
$effectiveEnd = clone $dtstart;
|
||||
$effectiveEnd->add($duration);
|
||||
return $start <= $effectiveEnd && $end > $dtstart;
|
||||
} elseif ($due) {
|
||||
return
|
||||
($start < $due || $start <= $dtstart) &&
|
||||
($end > $dtstart || $end >= $due);
|
||||
} else {
|
||||
return $start <= $dtstart && $end > $dtstart;
|
||||
}
|
||||
}
|
||||
if ($due) {
|
||||
return ($start < $due && $end >= $due);
|
||||
}
|
||||
if ($completed && $created) {
|
||||
return
|
||||
($start <= $created || $start <= $completed) &&
|
||||
($end >= $created || $end >= $completed);
|
||||
}
|
||||
if ($completed) {
|
||||
return ($start <= $completed && $end >= $completed);
|
||||
}
|
||||
if ($created) {
|
||||
return ($end > $created);
|
||||
}
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue