Merge branch 'dev/sabre-vobject3'
Resolves T53
This commit is contained in:
commit
1e2089e2c2
37 changed files with 257 additions and 5610 deletions
|
@ -19,7 +19,8 @@
|
|||
}
|
||||
],
|
||||
"require": {
|
||||
"php": ">=5.3.0",
|
||||
"roundcube/plugin-installer": ">=0.1.3"
|
||||
"php": ">=5.4.0",
|
||||
"roundcube/plugin-installer": ">=0.1.3",
|
||||
"sabre/vobject": "~3.3.3"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,405 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Sabre\VObject;
|
||||
|
||||
/**
|
||||
* VObject Component
|
||||
*
|
||||
* This class represents a VCALENDAR/VCARD component. A component is for example
|
||||
* VEVENT, VTODO and also VCALENDAR. It starts with BEGIN:COMPONENTNAME and
|
||||
* ends with END:COMPONENTNAME
|
||||
*
|
||||
* @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 Component extends Node {
|
||||
|
||||
/**
|
||||
* Name, for example VEVENT
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $name;
|
||||
|
||||
/**
|
||||
* Children properties and components
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $children = array();
|
||||
|
||||
/**
|
||||
* If components are added to this map, they will be automatically mapped
|
||||
* to their respective classes, if parsed by the reader or constructed with
|
||||
* the 'create' method.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
static public $classMap = array(
|
||||
'VALARM' => 'Sabre\\VObject\\Component\\VAlarm',
|
||||
'VCALENDAR' => 'Sabre\\VObject\\Component\\VCalendar',
|
||||
'VCARD' => 'Sabre\\VObject\\Component\\VCard',
|
||||
'VEVENT' => 'Sabre\\VObject\\Component\\VEvent',
|
||||
'VJOURNAL' => 'Sabre\\VObject\\Component\\VJournal',
|
||||
'VTODO' => 'Sabre\\VObject\\Component\\VTodo',
|
||||
'VFREEBUSY' => 'Sabre\\VObject\\Component\\VFreeBusy',
|
||||
);
|
||||
|
||||
/**
|
||||
* Creates the new component by name, but in addition will also see if
|
||||
* there's a class mapped to the property name.
|
||||
*
|
||||
* @param string $name
|
||||
* @param string $value
|
||||
* @return Component
|
||||
*/
|
||||
static public function create($name, $value = null) {
|
||||
|
||||
$name = strtoupper($name);
|
||||
|
||||
if (isset(self::$classMap[$name])) {
|
||||
return new self::$classMap[$name]($name, $value);
|
||||
} else {
|
||||
return new self($name, $value);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new component.
|
||||
*
|
||||
* By default this object will iterate over its own children, but this can
|
||||
* be overridden with the iterator argument
|
||||
*
|
||||
* @param string $name
|
||||
* @param ElementList $iterator
|
||||
*/
|
||||
public function __construct($name, ElementList $iterator = null) {
|
||||
|
||||
$this->name = strtoupper($name);
|
||||
if (!is_null($iterator)) $this->iterator = $iterator;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Turns the object back into a serialized blob.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function serialize() {
|
||||
|
||||
$str = "BEGIN:" . $this->name . "\r\n";
|
||||
|
||||
/**
|
||||
* Gives a component a 'score' for sorting purposes.
|
||||
*
|
||||
* This is solely used by the childrenSort method.
|
||||
*
|
||||
* A higher score means the item will be lower in the list.
|
||||
* To avoid score collisions, each "score category" has a reasonable
|
||||
* space to accomodate elements. The $key is added to the $score to
|
||||
* preserve the original relative order of elements.
|
||||
*
|
||||
* @param int $key
|
||||
* @param array $array
|
||||
* @return int
|
||||
*/
|
||||
$sortScore = function($key, $array) {
|
||||
|
||||
if ($array[$key] instanceof Component) {
|
||||
|
||||
// We want to encode VTIMEZONE first, this is a personal
|
||||
// preference.
|
||||
if ($array[$key]->name === 'VTIMEZONE') {
|
||||
$score=300000000;
|
||||
return $score+$key;
|
||||
} else {
|
||||
$score=400000000;
|
||||
return $score+$key;
|
||||
}
|
||||
} else {
|
||||
// Properties get encoded first
|
||||
// VCARD version 4.0 wants the VERSION property to appear first
|
||||
if ($array[$key] instanceof Property) {
|
||||
if ($array[$key]->name === 'VERSION') {
|
||||
$score=100000000;
|
||||
return $score+$key;
|
||||
} else {
|
||||
// All other properties
|
||||
$score=200000000;
|
||||
return $score+$key;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
$tmp = $this->children;
|
||||
uksort($this->children, function($a, $b) use ($sortScore, $tmp) {
|
||||
|
||||
$sA = $sortScore($a, $tmp);
|
||||
$sB = $sortScore($b, $tmp);
|
||||
|
||||
if ($sA === $sB) return 0;
|
||||
|
||||
return ($sA < $sB) ? -1 : 1;
|
||||
|
||||
});
|
||||
|
||||
foreach($this->children as $child) $str.=$child->serialize();
|
||||
$str.= "END:" . $this->name . "\r\n";
|
||||
|
||||
return $str;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new component or element
|
||||
*
|
||||
* You can call this method with the following syntaxes:
|
||||
*
|
||||
* add(Node $node)
|
||||
* add(string $name, $value, array $parameters = array())
|
||||
*
|
||||
* The first version adds an Element
|
||||
* The second adds a property as a string.
|
||||
*
|
||||
* @param mixed $item
|
||||
* @param mixed $itemValue
|
||||
* @return void
|
||||
*/
|
||||
public function add($item, $itemValue = null, array $parameters = array()) {
|
||||
|
||||
if ($item instanceof Node) {
|
||||
if (!is_null($itemValue)) {
|
||||
throw new \InvalidArgumentException('The second argument must not be specified, when passing a VObject Node');
|
||||
}
|
||||
$item->parent = $this;
|
||||
$this->children[] = $item;
|
||||
} elseif(is_string($item)) {
|
||||
|
||||
$item = Property::create($item,$itemValue, $parameters);
|
||||
$item->parent = $this;
|
||||
$this->children[] = $item;
|
||||
|
||||
} else {
|
||||
|
||||
throw new \InvalidArgumentException('The first argument must either be a \\Sabre\\VObject\\Node or a string');
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an iterable list of children
|
||||
*
|
||||
* @return ElementList
|
||||
*/
|
||||
public function children() {
|
||||
|
||||
return new ElementList($this->children);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array with elements that match the specified name.
|
||||
*
|
||||
* This function is also aware of MIME-Directory groups (as they appear in
|
||||
* vcards). This means that if a property is grouped as "HOME.EMAIL", it
|
||||
* will also be returned when searching for just "EMAIL". If you want to
|
||||
* search for a property in a specific group, you can select on the entire
|
||||
* string ("HOME.EMAIL"). If you want to search on a specific property that
|
||||
* has not been assigned a group, specify ".EMAIL".
|
||||
*
|
||||
* Keys are retained from the 'children' array, which may be confusing in
|
||||
* certain cases.
|
||||
*
|
||||
* @param string $name
|
||||
* @return array
|
||||
*/
|
||||
public function select($name) {
|
||||
|
||||
$group = null;
|
||||
$name = strtoupper($name);
|
||||
if (strpos($name,'.')!==false) {
|
||||
list($group,$name) = explode('.', $name, 2);
|
||||
}
|
||||
|
||||
$result = array();
|
||||
foreach($this->children as $key=>$child) {
|
||||
|
||||
if (
|
||||
strtoupper($child->name) === $name &&
|
||||
(is_null($group) || ( $child instanceof Property && strtoupper($child->group) === $group))
|
||||
) {
|
||||
|
||||
$result[$key] = $child;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
reset($result);
|
||||
return $result;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This method only returns a list of sub-components. Properties are
|
||||
* ignored.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getComponents() {
|
||||
|
||||
$result = array();
|
||||
foreach($this->children as $child) {
|
||||
if ($child instanceof Component) {
|
||||
$result[] = $child;
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
|
||||
$result = array();
|
||||
foreach($this->children as $child) {
|
||||
$result = array_merge($result, $child->validate($options));
|
||||
}
|
||||
return $result;
|
||||
|
||||
}
|
||||
|
||||
/* Magic property accessors {{{ */
|
||||
|
||||
/**
|
||||
* Using 'get' you will either get a property or component,
|
||||
*
|
||||
* If there were no child-elements found with the specified name,
|
||||
* null is returned.
|
||||
*
|
||||
* @param string $name
|
||||
* @return Property
|
||||
*/
|
||||
public function __get($name) {
|
||||
|
||||
$matches = $this->select($name);
|
||||
if (count($matches)===0) {
|
||||
return null;
|
||||
} else {
|
||||
$firstMatch = current($matches);
|
||||
/** @var $firstMatch Property */
|
||||
$firstMatch->setIterator(new ElementList(array_values($matches)));
|
||||
return $firstMatch;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This method checks if a sub-element with the specified name exists.
|
||||
*
|
||||
* @param string $name
|
||||
* @return bool
|
||||
*/
|
||||
public function __isset($name) {
|
||||
|
||||
$matches = $this->select($name);
|
||||
return count($matches)>0;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Using the setter method you can add properties or subcomponents
|
||||
*
|
||||
* You can either pass a Component, Property
|
||||
* object, or a string to automatically create a Property.
|
||||
*
|
||||
* If the item already exists, it will be removed. If you want to add
|
||||
* a new item with the same name, always use the add() method.
|
||||
*
|
||||
* @param string $name
|
||||
* @param mixed $value
|
||||
* @return void
|
||||
*/
|
||||
public function __set($name, $value) {
|
||||
|
||||
$matches = $this->select($name);
|
||||
$overWrite = count($matches)?key($matches):null;
|
||||
|
||||
if ($value instanceof Component || $value instanceof Property) {
|
||||
$value->parent = $this;
|
||||
if (!is_null($overWrite)) {
|
||||
$this->children[$overWrite] = $value;
|
||||
} else {
|
||||
$this->children[] = $value;
|
||||
}
|
||||
} elseif (is_scalar($value)) {
|
||||
$property = Property::create($name,$value);
|
||||
$property->parent = $this;
|
||||
if (!is_null($overWrite)) {
|
||||
$this->children[$overWrite] = $property;
|
||||
} else {
|
||||
$this->children[] = $property;
|
||||
}
|
||||
} else {
|
||||
throw new \InvalidArgumentException('You must pass a \\Sabre\\VObject\\Component, \\Sabre\\VObject\\Property or scalar type');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all properties and components within this component.
|
||||
*
|
||||
* @param string $name
|
||||
* @return void
|
||||
*/
|
||||
public function __unset($name) {
|
||||
|
||||
$matches = $this->select($name);
|
||||
foreach($matches as $k=>$child) {
|
||||
|
||||
unset($this->children[$k]);
|
||||
$child->parent = null;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* }}} */
|
||||
|
||||
/**
|
||||
* This method is automatically called when the object is cloned.
|
||||
* Specifically, this will ensure all child elements are also cloned.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __clone() {
|
||||
|
||||
foreach($this->children as $key=>$child) {
|
||||
$this->children[$key] = clone $child;
|
||||
$this->children[$key]->parent = $this;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -1,108 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Sabre\VObject\Component;
|
||||
use Sabre\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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -1,244 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Sabre\VObject\Component;
|
||||
|
||||
use Sabre\VObject;
|
||||
|
||||
/**
|
||||
* 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 {
|
||||
|
||||
static $defaultName = 'VCALENDAR';
|
||||
|
||||
/**
|
||||
* 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]);
|
||||
|
||||
}
|
||||
|
||||
foreach($newEvents as $newEvent) {
|
||||
|
||||
foreach($newEvent->children as $child) {
|
||||
if ($child instanceof VObject\Property\DateTime &&
|
||||
$child->getDateType() == VObject\Property\DateTime::LOCALTZ) {
|
||||
$child->setDateTime($child->getDateTime(),VObject\Property\DateTime::UTC);
|
||||
}
|
||||
}
|
||||
|
||||
$this->add($newEvent);
|
||||
|
||||
}
|
||||
|
||||
// Removing all VTIMEZONE components
|
||||
unset($this->VTIMEZONE);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 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() {
|
||||
|
||||
$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,
|
||||
);
|
||||
}
|
||||
|
||||
$allowedComponents = array(
|
||||
'VEVENT',
|
||||
'VTODO',
|
||||
'VJOURNAL',
|
||||
'VFREEBUSY',
|
||||
'VTIMEZONE',
|
||||
);
|
||||
$allowedProperties = array(
|
||||
'PRODID',
|
||||
'VERSION',
|
||||
'CALSCALE',
|
||||
'METHOD',
|
||||
);
|
||||
$componentsFound = 0;
|
||||
foreach($this->children as $child) {
|
||||
if($child instanceof Component) {
|
||||
$componentsFound++;
|
||||
if (!in_array($child->name, $allowedComponents)) {
|
||||
$warnings[] = array(
|
||||
'level' => 1,
|
||||
'message' => 'The ' . $child->name . " component is not allowed in the VCALENDAR component",
|
||||
'node' => $this,
|
||||
);
|
||||
}
|
||||
}
|
||||
if ($child instanceof Property) {
|
||||
if (!in_array($child->name, $allowedProperties)) {
|
||||
$warnings[] = array(
|
||||
'level' => 2,
|
||||
'message' => 'The ' . $child->name . " property is not allowed in the VCALENDAR component",
|
||||
'node' => $this,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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()
|
||||
);
|
||||
|
||||
}
|
||||
*/
|
||||
|
||||
}
|
||||
|
|
@ -1,107 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Sabre\VObject\Component;
|
||||
|
||||
use Sabre\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\Component {
|
||||
|
||||
static $defaultName = 'VCARD';
|
||||
|
||||
/**
|
||||
* VCards with version 2.1, 3.0 and 4.0 are found.
|
||||
*
|
||||
* If the VCARD doesn't know its version, 4.0 is assumed.
|
||||
*/
|
||||
const DEFAULT_VERSION = '4.0';
|
||||
|
||||
/**
|
||||
* 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();
|
||||
|
||||
$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 = 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 = '4.0';
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
$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
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,70 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Sabre\VObject\Component;
|
||||
use Sabre\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->getDateType() == VObject\Property\DateTime::DATE) {
|
||||
$effectiveEnd = clone $effectiveStart;
|
||||
$effectiveEnd->modify('+1 day');
|
||||
} else {
|
||||
$effectiveEnd = clone $effectiveStart;
|
||||
}
|
||||
return (
|
||||
($start <= $effectiveEnd) && ($end > $effectiveStart)
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -1,68 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Sabre\VObject\Component;
|
||||
|
||||
use Sabre\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;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,46 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Sabre\VObject\Component;
|
||||
|
||||
use Sabre\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->getDateType() == VObject\Property\DateTime::DATE) {
|
||||
$effectiveEnd->modify('+1 day');
|
||||
}
|
||||
|
||||
return ($start <= $effectiveEnd && $end > $dtstart);
|
||||
|
||||
}
|
||||
return false;
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -1,68 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Sabre\VObject\Component;
|
||||
|
||||
use Sabre\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;
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -1,181 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Sabre\VObject;
|
||||
|
||||
/**
|
||||
* DateTimeParser
|
||||
*
|
||||
* This class is responsible for parsing the several different date and time
|
||||
* formats iCalendar and vCards have.
|
||||
*
|
||||
* @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 DateTimeParser {
|
||||
|
||||
/**
|
||||
* Parses an iCalendar (rfc5545) formatted datetime and returns a DateTime object
|
||||
*
|
||||
* Specifying a reference timezone is optional. It will only be used
|
||||
* if the non-UTC format is used. The argument is used as a reference, the
|
||||
* returned DateTime object will still be in the UTC timezone.
|
||||
*
|
||||
* @param string $dt
|
||||
* @param DateTimeZone $tz
|
||||
* @return DateTime
|
||||
*/
|
||||
static public function parseDateTime($dt,\DateTimeZone $tz = null) {
|
||||
|
||||
// Format is YYYYMMDD + "T" + hhmmss
|
||||
$result = preg_match('/^([1-4][0-9]{3})([0-1][0-9])([0-3][0-9])T([0-2][0-9])([0-5][0-9])([0-5][0-9])([Z]?)$/',$dt,$matches);
|
||||
|
||||
if (!$result) {
|
||||
throw new \LogicException('The supplied iCalendar datetime value is incorrect: ' . $dt);
|
||||
}
|
||||
|
||||
if ($matches[7]==='Z' || is_null($tz)) {
|
||||
$tz = new \DateTimeZone('UTC');
|
||||
}
|
||||
$date = new \DateTime($matches[1] . '-' . $matches[2] . '-' . $matches[3] . ' ' . $matches[4] . ':' . $matches[5] .':' . $matches[6], $tz);
|
||||
|
||||
// Still resetting the timezone, to normalize everything to UTC
|
||||
$date->setTimeZone(new \DateTimeZone('UTC'));
|
||||
return $date;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses an iCalendar (rfc5545) formatted date and returns a DateTime object
|
||||
*
|
||||
* @param string $date
|
||||
* @return DateTime
|
||||
*/
|
||||
static public function parseDate($date) {
|
||||
|
||||
// Format is YYYYMMDD
|
||||
$result = preg_match('/^([1-4][0-9]{3})([0-1][0-9])([0-3][0-9])$/',$date,$matches);
|
||||
|
||||
if (!$result) {
|
||||
throw new \LogicException('The supplied iCalendar date value is incorrect: ' . $date);
|
||||
}
|
||||
|
||||
$date = new \DateTime($matches[1] . '-' . $matches[2] . '-' . $matches[3], new \DateTimeZone('UTC'));
|
||||
return $date;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses an iCalendar (RFC5545) formatted duration value.
|
||||
*
|
||||
* This method will either return a DateTimeInterval object, or a string
|
||||
* suitable for strtotime or DateTime::modify.
|
||||
*
|
||||
* @param string $duration
|
||||
* @param bool $asString
|
||||
* @return DateInterval|string
|
||||
*/
|
||||
static public function parseDuration($duration, $asString = false) {
|
||||
|
||||
$result = preg_match('/^(?P<plusminus>\+|-)?P((?P<week>\d+)W)?((?P<day>\d+)D)?(T((?P<hour>\d+)H)?((?P<minute>\d+)M)?((?P<second>\d+)S)?)?$/', $duration, $matches);
|
||||
if (!$result) {
|
||||
throw new \LogicException('The supplied iCalendar duration value is incorrect: ' . $duration);
|
||||
}
|
||||
|
||||
if (!$asString) {
|
||||
$invert = false;
|
||||
if ($matches['plusminus']==='-') {
|
||||
$invert = true;
|
||||
}
|
||||
|
||||
|
||||
$parts = array(
|
||||
'week',
|
||||
'day',
|
||||
'hour',
|
||||
'minute',
|
||||
'second',
|
||||
);
|
||||
foreach($parts as $part) {
|
||||
$matches[$part] = isset($matches[$part])&&$matches[$part]?(int)$matches[$part]:0;
|
||||
}
|
||||
|
||||
|
||||
// We need to re-construct the $duration string, because weeks and
|
||||
// days are not supported by DateInterval in the same string.
|
||||
$duration = 'P';
|
||||
$days = $matches['day'];
|
||||
if ($matches['week']) {
|
||||
$days+=$matches['week']*7;
|
||||
}
|
||||
if ($days)
|
||||
$duration.=$days . 'D';
|
||||
|
||||
if ($matches['minute'] || $matches['second'] || $matches['hour']) {
|
||||
$duration.='T';
|
||||
|
||||
if ($matches['hour'])
|
||||
$duration.=$matches['hour'].'H';
|
||||
|
||||
if ($matches['minute'])
|
||||
$duration.=$matches['minute'].'M';
|
||||
|
||||
if ($matches['second'])
|
||||
$duration.=$matches['second'].'S';
|
||||
|
||||
}
|
||||
|
||||
if ($duration==='P') {
|
||||
$duration = 'PT0S';
|
||||
}
|
||||
$iv = new \DateInterval($duration);
|
||||
if ($invert) $iv->invert = true;
|
||||
|
||||
return $iv;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
$parts = array(
|
||||
'week',
|
||||
'day',
|
||||
'hour',
|
||||
'minute',
|
||||
'second',
|
||||
);
|
||||
|
||||
$newDur = '';
|
||||
foreach($parts as $part) {
|
||||
if (isset($matches[$part]) && $matches[$part]) {
|
||||
$newDur.=' '.$matches[$part] . ' ' . $part . 's';
|
||||
}
|
||||
}
|
||||
|
||||
$newDur = ($matches['plusminus']==='-'?'-':'+') . trim($newDur);
|
||||
if ($newDur === '+') { $newDur = '+0 seconds'; };
|
||||
return $newDur;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses either a Date or DateTime, or Duration value.
|
||||
*
|
||||
* @param string $date
|
||||
* @param DateTimeZone|string $referenceTZ
|
||||
* @return DateTime|DateInterval
|
||||
*/
|
||||
static public function parse($date, $referenceTZ = null) {
|
||||
|
||||
if ($date[0]==='P' || ($date[0]==='-' && $date[1]==='P')) {
|
||||
return self::parseDuration($date);
|
||||
} elseif (strlen($date)===8) {
|
||||
return self::parseDate($date);
|
||||
} else {
|
||||
return self::parseDateTime($date, $referenceTZ);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -1,109 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Sabre\VObject;
|
||||
|
||||
/**
|
||||
* Document
|
||||
*
|
||||
* A document is just like a component, except that it's also the top level
|
||||
* element.
|
||||
*
|
||||
* Both a VCALENDAR and a VCARD are considered documents.
|
||||
*
|
||||
* This class also provides a registry for document types.
|
||||
*
|
||||
* @copyright Copyright (C) 2007-2013 fruux GmbH. All rights reserved.
|
||||
* @author Evert Pot (http://evertpot.com/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
abstract class Document extends Component {
|
||||
|
||||
/**
|
||||
* The default name for this component.
|
||||
*
|
||||
* This should be 'VCALENDAR' or 'VCARD'.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
static $defaultName;
|
||||
|
||||
/**
|
||||
* Creates a new document.
|
||||
*
|
||||
* We're changing the default behavior slightly here. First, we don't want
|
||||
* to have to specify a name (we already know it), and we want to allow
|
||||
* children to be specified in the first argument.
|
||||
*
|
||||
* But, the default behavior also works.
|
||||
*
|
||||
* So the two sigs:
|
||||
*
|
||||
* new Document(array $children = array());
|
||||
* new Document(string $name, array $children = array())
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct() {
|
||||
|
||||
$args = func_get_args();
|
||||
if (count($args)===0 || is_array($args[0])) {
|
||||
array_unshift($args, static::$defaultName);
|
||||
call_user_func_array(array('parent', '__construct'), $args);
|
||||
} else {
|
||||
call_user_func_array(array('parent', '__construct'), $args);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new component
|
||||
*
|
||||
* This method automatically searches for the correct component class, based
|
||||
* on its name.
|
||||
*
|
||||
* You can specify the children either in key=>value syntax, in which case
|
||||
* properties will automatically be created, or you can just pass a list of
|
||||
* Component and Property object.
|
||||
*
|
||||
* @param string $name
|
||||
* @param array $children
|
||||
* @return Component
|
||||
*/
|
||||
public function createComponent($name, array $children = array()) {
|
||||
|
||||
$component = Component::create($name);
|
||||
foreach($children as $k=>$v) {
|
||||
|
||||
if ($v instanceof Node) {
|
||||
$component->add($v);
|
||||
} else {
|
||||
$component->add($k, $v);
|
||||
}
|
||||
|
||||
}
|
||||
return $component;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory method for creating new properties
|
||||
*
|
||||
* This method automatically searches for the correct property class, based
|
||||
* on its name.
|
||||
*
|
||||
* You can specify the parameters either in key=>value syntax, in which case
|
||||
* parameters will automatically be created, or you can just pass a list of
|
||||
* Parameter objects.
|
||||
*
|
||||
* @param string $name
|
||||
* @param mixed $value
|
||||
* @param array $parameters
|
||||
* @return Property
|
||||
*/
|
||||
public function createProperty($name, $value = null, array $parameters = array()) {
|
||||
|
||||
return Property::create($name, $value, $parameters);
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -1,172 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Sabre\VObject;
|
||||
|
||||
/**
|
||||
* VObject ElementList
|
||||
*
|
||||
* This class represents a list of elements. Lists are the result of queries,
|
||||
* such as doing $vcalendar->vevent where there's multiple VEVENT objects.
|
||||
*
|
||||
* @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 ElementList implements \Iterator, \Countable, \ArrayAccess {
|
||||
|
||||
/**
|
||||
* Inner elements
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $elements = array();
|
||||
|
||||
/**
|
||||
* Creates the element list.
|
||||
*
|
||||
* @param array $elements
|
||||
*/
|
||||
public function __construct(array $elements) {
|
||||
|
||||
$this->elements = $elements;
|
||||
|
||||
}
|
||||
|
||||
/* {{{ Iterator interface */
|
||||
|
||||
/**
|
||||
* Current position
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
private $key = 0;
|
||||
|
||||
/**
|
||||
* Returns current item in iteration
|
||||
*
|
||||
* @return Element
|
||||
*/
|
||||
public function current() {
|
||||
|
||||
return $this->elements[$this->key];
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* To the next item in the iterator
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function next() {
|
||||
|
||||
$this->key++;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current iterator key
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function key() {
|
||||
|
||||
return $this->key;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the current position in the iterator is a valid one
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function valid() {
|
||||
|
||||
return isset($this->elements[$this->key]);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Rewinds the iterator
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function rewind() {
|
||||
|
||||
$this->key = 0;
|
||||
|
||||
}
|
||||
|
||||
/* }}} */
|
||||
|
||||
/* {{{ Countable interface */
|
||||
|
||||
/**
|
||||
* Returns the number of elements
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function count() {
|
||||
|
||||
return count($this->elements);
|
||||
|
||||
}
|
||||
|
||||
/* }}} */
|
||||
|
||||
/* {{{ ArrayAccess Interface */
|
||||
|
||||
|
||||
/**
|
||||
* Checks if an item exists through ArrayAccess.
|
||||
*
|
||||
* @param int $offset
|
||||
* @return bool
|
||||
*/
|
||||
public function offsetExists($offset) {
|
||||
|
||||
return isset($this->elements[$offset]);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an item through ArrayAccess.
|
||||
*
|
||||
* @param int $offset
|
||||
* @return mixed
|
||||
*/
|
||||
public function offsetGet($offset) {
|
||||
|
||||
return $this->elements[$offset];
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets an item through ArrayAccess.
|
||||
*
|
||||
* @param int $offset
|
||||
* @param mixed $value
|
||||
* @return void
|
||||
*/
|
||||
public function offsetSet($offset,$value) {
|
||||
|
||||
throw new \LogicException('You can not add new objects to an ElementList');
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets an item through ArrayAccess.
|
||||
*
|
||||
* This method just forwards the request to the inner iterator
|
||||
*
|
||||
* @param int $offset
|
||||
* @return void
|
||||
*/
|
||||
public function offsetUnset($offset) {
|
||||
|
||||
throw new \LogicException('You can not remove objects from an ElementList');
|
||||
|
||||
}
|
||||
|
||||
/* }}} */
|
||||
|
||||
}
|
|
@ -1,322 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Sabre\VObject;
|
||||
|
||||
/**
|
||||
* This class helps with generating FREEBUSY reports based on existing sets of
|
||||
* objects.
|
||||
*
|
||||
* It only looks at VEVENT and VFREEBUSY objects from the sourcedata, and
|
||||
* generates a single VFREEBUSY object.
|
||||
*
|
||||
* VFREEBUSY components are described in RFC5545, The rules for what should
|
||||
* go in a single freebusy report is taken from RFC4791, section 7.10.
|
||||
*
|
||||
* @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 FreeBusyGenerator {
|
||||
|
||||
/**
|
||||
* Input objects
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $objects;
|
||||
|
||||
/**
|
||||
* Start of range
|
||||
*
|
||||
* @var DateTime|null
|
||||
*/
|
||||
protected $start;
|
||||
|
||||
/**
|
||||
* End of range
|
||||
*
|
||||
* @var DateTime|null
|
||||
*/
|
||||
protected $end;
|
||||
|
||||
/**
|
||||
* VCALENDAR object
|
||||
*
|
||||
* @var Component
|
||||
*/
|
||||
protected $baseObject;
|
||||
|
||||
/**
|
||||
* Creates the generator.
|
||||
*
|
||||
* Check the setTimeRange and setObjects methods for details about the
|
||||
* arguments.
|
||||
*
|
||||
* @param DateTime $start
|
||||
* @param DateTime $end
|
||||
* @param mixed $objects
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(\DateTime $start = null, \DateTime $end = null, $objects = null) {
|
||||
|
||||
if ($start && $end) {
|
||||
$this->setTimeRange($start, $end);
|
||||
}
|
||||
|
||||
if ($objects) {
|
||||
$this->setObjects($objects);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the VCALENDAR object.
|
||||
*
|
||||
* If this is set, it will not be generated for you. You are responsible
|
||||
* for setting things like the METHOD, CALSCALE, VERSION, etc..
|
||||
*
|
||||
* The VFREEBUSY object will be automatically added though.
|
||||
*
|
||||
* @param Component $vcalendar
|
||||
* @return void
|
||||
*/
|
||||
public function setBaseObject(Component $vcalendar) {
|
||||
|
||||
$this->baseObject = $vcalendar;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the input objects
|
||||
*
|
||||
* You must either specify a valendar object as a strong, or as the parse
|
||||
* Component.
|
||||
* It's also possible to specify multiple objects as an array.
|
||||
*
|
||||
* @param mixed $objects
|
||||
* @return void
|
||||
*/
|
||||
public function setObjects($objects) {
|
||||
|
||||
if (!is_array($objects)) {
|
||||
$objects = array($objects);
|
||||
}
|
||||
|
||||
$this->objects = array();
|
||||
foreach($objects as $object) {
|
||||
|
||||
if (is_string($object)) {
|
||||
$this->objects[] = Reader::read($object);
|
||||
} elseif ($object instanceof Component) {
|
||||
$this->objects[] = $object;
|
||||
} else {
|
||||
throw new \InvalidArgumentException('You can only pass strings or \\Sabre\\VObject\\Component arguments to setObjects');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the time range
|
||||
*
|
||||
* Any freebusy object falling outside of this time range will be ignored.
|
||||
*
|
||||
* @param DateTime $start
|
||||
* @param DateTime $end
|
||||
* @return void
|
||||
*/
|
||||
public function setTimeRange(\DateTime $start = null, \DateTime $end = null) {
|
||||
|
||||
$this->start = $start;
|
||||
$this->end = $end;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the input data and returns a correct VFREEBUSY object, wrapped in
|
||||
* a VCALENDAR.
|
||||
*
|
||||
* @return Component
|
||||
*/
|
||||
public function getResult() {
|
||||
|
||||
$busyTimes = array();
|
||||
|
||||
foreach($this->objects as $object) {
|
||||
|
||||
foreach($object->getBaseComponents() as $component) {
|
||||
|
||||
switch($component->name) {
|
||||
|
||||
case 'VEVENT' :
|
||||
|
||||
$FBTYPE = 'BUSY';
|
||||
if (isset($component->TRANSP) && (strtoupper($component->TRANSP) === 'TRANSPARENT')) {
|
||||
break;
|
||||
}
|
||||
if (isset($component->STATUS)) {
|
||||
$status = strtoupper($component->STATUS);
|
||||
if ($status==='CANCELLED') {
|
||||
break;
|
||||
}
|
||||
if ($status==='TENTATIVE') {
|
||||
$FBTYPE = 'BUSY-TENTATIVE';
|
||||
}
|
||||
}
|
||||
|
||||
$times = array();
|
||||
|
||||
if ($component->RRULE) {
|
||||
|
||||
$iterator = new RecurrenceIterator($object, (string)$component->uid);
|
||||
if ($this->start) {
|
||||
$iterator->fastForward($this->start);
|
||||
}
|
||||
|
||||
$maxRecurrences = 200;
|
||||
|
||||
while($iterator->valid() && --$maxRecurrences) {
|
||||
|
||||
$startTime = $iterator->getDTStart();
|
||||
if ($this->end && $startTime > $this->end) {
|
||||
break;
|
||||
}
|
||||
$times[] = array(
|
||||
$iterator->getDTStart(),
|
||||
$iterator->getDTEnd(),
|
||||
);
|
||||
|
||||
$iterator->next();
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
$startTime = $component->DTSTART->getDateTime();
|
||||
if ($this->end && $startTime > $this->end) {
|
||||
break;
|
||||
}
|
||||
$endTime = null;
|
||||
if (isset($component->DTEND)) {
|
||||
$endTime = $component->DTEND->getDateTime();
|
||||
} elseif (isset($component->DURATION)) {
|
||||
$duration = DateTimeParser::parseDuration((string)$component->DURATION);
|
||||
$endTime = clone $startTime;
|
||||
$endTime->add($duration);
|
||||
} elseif ($component->DTSTART->getDateType() === Property\DateTime::DATE) {
|
||||
$endTime = clone $startTime;
|
||||
$endTime->modify('+1 day');
|
||||
} else {
|
||||
// The event had no duration (0 seconds)
|
||||
break;
|
||||
}
|
||||
|
||||
$times[] = array($startTime, $endTime);
|
||||
|
||||
}
|
||||
|
||||
foreach($times as $time) {
|
||||
|
||||
if ($this->end && $time[0] > $this->end) break;
|
||||
if ($this->start && $time[1] < $this->start) break;
|
||||
|
||||
$busyTimes[] = array(
|
||||
$time[0],
|
||||
$time[1],
|
||||
$FBTYPE,
|
||||
);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'VFREEBUSY' :
|
||||
foreach($component->FREEBUSY as $freebusy) {
|
||||
|
||||
$fbType = isset($freebusy['FBTYPE'])?strtoupper($freebusy['FBTYPE']):'BUSY';
|
||||
|
||||
// Skipping intervals marked as 'free'
|
||||
if ($fbType==='FREE')
|
||||
continue;
|
||||
|
||||
$values = explode(',', $freebusy);
|
||||
foreach($values as $value) {
|
||||
list($startTime, $endTime) = explode('/', $value);
|
||||
$startTime = DateTimeParser::parseDateTime($startTime);
|
||||
|
||||
if (substr($endTime,0,1)==='P' || substr($endTime,0,2)==='-P') {
|
||||
$duration = DateTimeParser::parseDuration($endTime);
|
||||
$endTime = clone $startTime;
|
||||
$endTime->add($duration);
|
||||
} else {
|
||||
$endTime = DateTimeParser::parseDateTime($endTime);
|
||||
}
|
||||
|
||||
if($this->start && $this->start > $endTime) continue;
|
||||
if($this->end && $this->end < $startTime) continue;
|
||||
$busyTimes[] = array(
|
||||
$startTime,
|
||||
$endTime,
|
||||
$fbType
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if ($this->baseObject) {
|
||||
$calendar = $this->baseObject;
|
||||
} else {
|
||||
$calendar = Component::create('VCALENDAR');
|
||||
$calendar->version = '2.0';
|
||||
$calendar->prodid = '-//Sabre//Sabre VObject ' . Version::VERSION . '//EN';
|
||||
$calendar->calscale = 'GREGORIAN';
|
||||
}
|
||||
|
||||
$vfreebusy = Component::create('VFREEBUSY');
|
||||
$calendar->add($vfreebusy);
|
||||
|
||||
if ($this->start) {
|
||||
$dtstart = Property::create('DTSTART');
|
||||
$dtstart->setDateTime($this->start,Property\DateTime::UTC);
|
||||
$vfreebusy->add($dtstart);
|
||||
}
|
||||
if ($this->end) {
|
||||
$dtend = Property::create('DTEND');
|
||||
$dtend->setDateTime($this->end,Property\DateTime::UTC);
|
||||
$vfreebusy->add($dtend);
|
||||
}
|
||||
$dtstamp = Property::create('DTSTAMP');
|
||||
$dtstamp->setDateTime(new \DateTime('now'), Property\DateTime::UTC);
|
||||
$vfreebusy->add($dtstamp);
|
||||
|
||||
foreach($busyTimes as $busyTime) {
|
||||
|
||||
$busyTime[0]->setTimeZone(new \DateTimeZone('UTC'));
|
||||
$busyTime[1]->setTimeZone(new \DateTimeZone('UTC'));
|
||||
|
||||
$prop = Property::create(
|
||||
'FREEBUSY',
|
||||
$busyTime[0]->format('Ymd\\THis\\Z') . '/' . $busyTime[1]->format('Ymd\\THis\\Z')
|
||||
);
|
||||
$prop['FBTYPE'] = $busyTime[2];
|
||||
$vfreebusy->add($prop);
|
||||
|
||||
}
|
||||
|
||||
return $calendar;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,187 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Sabre\VObject;
|
||||
|
||||
/**
|
||||
* Base class for all nodes
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
abstract class Node implements \IteratorAggregate, \ArrayAccess, \Countable {
|
||||
|
||||
/**
|
||||
* The following constants are used by the validate() method.
|
||||
*/
|
||||
const REPAIR = 1;
|
||||
|
||||
/**
|
||||
* Turns the object back into a serialized blob.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
abstract function serialize();
|
||||
|
||||
/**
|
||||
* Iterator override
|
||||
*
|
||||
* @var ElementList
|
||||
*/
|
||||
protected $iterator = null;
|
||||
|
||||
/**
|
||||
* A link to the parent node
|
||||
*
|
||||
* @var Node
|
||||
*/
|
||||
public $parent = null;
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
|
||||
return array();
|
||||
|
||||
}
|
||||
|
||||
/* {{{ IteratorAggregator interface */
|
||||
|
||||
/**
|
||||
* Returns the iterator for this object
|
||||
*
|
||||
* @return ElementList
|
||||
*/
|
||||
public function getIterator() {
|
||||
|
||||
if (!is_null($this->iterator))
|
||||
return $this->iterator;
|
||||
|
||||
return new ElementList(array($this));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the overridden iterator
|
||||
*
|
||||
* Note that this is not actually part of the iterator interface
|
||||
*
|
||||
* @param ElementList $iterator
|
||||
* @return void
|
||||
*/
|
||||
public function setIterator(ElementList $iterator) {
|
||||
|
||||
$this->iterator = $iterator;
|
||||
|
||||
}
|
||||
|
||||
/* }}} */
|
||||
|
||||
/* {{{ Countable interface */
|
||||
|
||||
/**
|
||||
* Returns the number of elements
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function count() {
|
||||
|
||||
$it = $this->getIterator();
|
||||
return $it->count();
|
||||
|
||||
}
|
||||
|
||||
/* }}} */
|
||||
|
||||
/* {{{ ArrayAccess Interface */
|
||||
|
||||
|
||||
/**
|
||||
* Checks if an item exists through ArrayAccess.
|
||||
*
|
||||
* This method just forwards the request to the inner iterator
|
||||
*
|
||||
* @param int $offset
|
||||
* @return bool
|
||||
*/
|
||||
public function offsetExists($offset) {
|
||||
|
||||
$iterator = $this->getIterator();
|
||||
return $iterator->offsetExists($offset);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an item through ArrayAccess.
|
||||
*
|
||||
* This method just forwards the request to the inner iterator
|
||||
*
|
||||
* @param int $offset
|
||||
* @return mixed
|
||||
*/
|
||||
public function offsetGet($offset) {
|
||||
|
||||
$iterator = $this->getIterator();
|
||||
return $iterator->offsetGet($offset);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets an item through ArrayAccess.
|
||||
*
|
||||
* This method just forwards the request to the inner iterator
|
||||
*
|
||||
* @param int $offset
|
||||
* @param mixed $value
|
||||
* @return void
|
||||
*/
|
||||
public function offsetSet($offset,$value) {
|
||||
|
||||
$iterator = $this->getIterator();
|
||||
$iterator->offsetSet($offset,$value);
|
||||
|
||||
// @codeCoverageIgnoreStart
|
||||
//
|
||||
// This method always throws an exception, so we ignore the closing
|
||||
// brace
|
||||
}
|
||||
// @codeCoverageIgnoreEnd
|
||||
|
||||
/**
|
||||
* Sets an item through ArrayAccess.
|
||||
*
|
||||
* This method just forwards the request to the inner iterator
|
||||
*
|
||||
* @param int $offset
|
||||
* @return void
|
||||
*/
|
||||
public function offsetUnset($offset) {
|
||||
|
||||
$iterator = $this->getIterator();
|
||||
$iterator->offsetUnset($offset);
|
||||
|
||||
// @codeCoverageIgnoreStart
|
||||
//
|
||||
// This method always throws an exception, so we ignore the closing
|
||||
// brace
|
||||
}
|
||||
// @codeCoverageIgnoreEnd
|
||||
|
||||
/* }}} */
|
||||
|
||||
}
|
|
@ -1,102 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Sabre\VObject;
|
||||
|
||||
/**
|
||||
* VObject Parameter
|
||||
*
|
||||
* This class represents a parameter. A parameter is always tied to a property.
|
||||
* In the case of:
|
||||
* DTSTART;VALUE=DATE:20101108
|
||||
* VALUE=DATE would be the parameter name and value.
|
||||
*
|
||||
* @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 Parameter extends Node {
|
||||
|
||||
/**
|
||||
* Parameter name
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $name;
|
||||
|
||||
/**
|
||||
* Parameter value
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $value;
|
||||
|
||||
/**
|
||||
* Sets up the object
|
||||
*
|
||||
* @param string $name
|
||||
* @param string $value
|
||||
*/
|
||||
public function __construct($name, $value = null) {
|
||||
|
||||
if (!is_scalar($value) && !is_null($value)) {
|
||||
throw new \InvalidArgumentException('The value argument must be a scalar value or null');
|
||||
}
|
||||
|
||||
$this->name = strtoupper($name);
|
||||
$this->value = $value;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the parameter's internal value.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getValue() {
|
||||
|
||||
return $this->value;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Turns the object back into a serialized blob.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function serialize() {
|
||||
|
||||
if (is_null($this->value)) {
|
||||
return $this->name;
|
||||
}
|
||||
$src = array(
|
||||
'\\',
|
||||
"\n",
|
||||
);
|
||||
$out = array(
|
||||
'\\\\',
|
||||
'\n',
|
||||
);
|
||||
|
||||
// quote parameters according to RFC 5545, Section 3.2
|
||||
$quotes = '';
|
||||
if (preg_match('/[:;,]/', $this->value)) {
|
||||
$quotes = '"';
|
||||
}
|
||||
|
||||
return $this->name . '=' . $quotes . str_replace($src, $out, $this->value) . $quotes;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when this object is being cast to a string
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function __toString() {
|
||||
|
||||
return $this->value;
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Sabre\VObject;
|
||||
|
||||
/**
|
||||
* Exception thrown by Reader if an invalid object was attempted to be parsed.
|
||||
*
|
||||
* @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 ParseException extends \Exception { }
|
|
@ -1,453 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Sabre\VObject;
|
||||
|
||||
/**
|
||||
* VObject Property
|
||||
*
|
||||
* A property in VObject is usually in the form PARAMNAME:paramValue.
|
||||
* An example is : SUMMARY:Weekly meeting
|
||||
*
|
||||
* Properties can also have parameters:
|
||||
* SUMMARY;LANG=en:Weekly meeting.
|
||||
*
|
||||
* Parameters can be accessed using the ArrayAccess interface.
|
||||
*
|
||||
* @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 Property extends Node {
|
||||
|
||||
/**
|
||||
* Propertyname
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $name;
|
||||
|
||||
/**
|
||||
* Group name
|
||||
*
|
||||
* This may be something like 'HOME' for vcards.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $group;
|
||||
|
||||
/**
|
||||
* Property parameters
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $parameters = array();
|
||||
|
||||
/**
|
||||
* Property value
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $value;
|
||||
|
||||
/**
|
||||
* If properties are added to this map, they will be automatically mapped
|
||||
* to their respective classes, if parsed by the reader or constructed with
|
||||
* the 'create' method.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
static public $classMap = array(
|
||||
'COMPLETED' => 'Sabre\\VObject\\Property\\DateTime',
|
||||
'CREATED' => 'Sabre\\VObject\\Property\\DateTime',
|
||||
'DTEND' => 'Sabre\\VObject\\Property\\DateTime',
|
||||
'DTSTAMP' => 'Sabre\\VObject\\Property\\DateTime',
|
||||
'DTSTART' => 'Sabre\\VObject\\Property\\DateTime',
|
||||
'DUE' => 'Sabre\\VObject\\Property\\DateTime',
|
||||
'EXDATE' => 'Sabre\\VObject\\Property\\MultiDateTime',
|
||||
'LAST-MODIFIED' => 'Sabre\\VObject\\Property\\DateTime',
|
||||
'RECURRENCE-ID' => 'Sabre\\VObject\\Property\\DateTime',
|
||||
'TRIGGER' => 'Sabre\\VObject\\Property\\DateTime',
|
||||
'N' => 'Sabre\\VObject\\Property\\Compound',
|
||||
'ORG' => 'Sabre\\VObject\\Property\\Compound',
|
||||
'ADR' => 'Sabre\\VObject\\Property\\Compound',
|
||||
'CATEGORIES' => 'Sabre\\VObject\\Property\\Compound',
|
||||
);
|
||||
|
||||
/**
|
||||
* Creates the new property by name, but in addition will also see if
|
||||
* there's a class mapped to the property name.
|
||||
*
|
||||
* Parameters can be specified with the optional third argument. Parameters
|
||||
* must be a key->value map of the parameter name, and value. If the value
|
||||
* is specified as an array, it is assumed that multiple parameters with
|
||||
* the same name should be added.
|
||||
*
|
||||
* @param string $name
|
||||
* @param string $value
|
||||
* @param array $parameters
|
||||
* @return Property
|
||||
*/
|
||||
static public function create($name, $value = null, array $parameters = array()) {
|
||||
|
||||
$name = strtoupper($name);
|
||||
$shortName = $name;
|
||||
$group = null;
|
||||
if (strpos($shortName,'.')!==false) {
|
||||
list($group, $shortName) = explode('.', $shortName);
|
||||
}
|
||||
|
||||
if (isset(self::$classMap[$shortName])) {
|
||||
return new self::$classMap[$shortName]($name, $value, $parameters);
|
||||
} else {
|
||||
return new self($name, $value, $parameters);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new property object
|
||||
*
|
||||
* Parameters can be specified with the optional third argument. Parameters
|
||||
* must be a key->value map of the parameter name, and value. If the value
|
||||
* is specified as an array, it is assumed that multiple parameters with
|
||||
* the same name should be added.
|
||||
*
|
||||
* @param string $name
|
||||
* @param string $value
|
||||
* @param array $parameters
|
||||
*/
|
||||
public function __construct($name, $value = null, array $parameters = array()) {
|
||||
|
||||
if (!is_scalar($value) && !is_null($value)) {
|
||||
throw new \InvalidArgumentException('The value argument must be scalar or null');
|
||||
}
|
||||
|
||||
$name = strtoupper($name);
|
||||
$group = null;
|
||||
if (strpos($name,'.')!==false) {
|
||||
list($group, $name) = explode('.', $name);
|
||||
}
|
||||
$this->name = $name;
|
||||
$this->group = $group;
|
||||
$this->setValue($value);
|
||||
|
||||
foreach($parameters as $paramName => $paramValues) {
|
||||
|
||||
if (!is_array($paramValues)) {
|
||||
$paramValues = array($paramValues);
|
||||
}
|
||||
|
||||
foreach($paramValues as $paramValue) {
|
||||
$this->add($paramName, $paramValue);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the internal value
|
||||
*
|
||||
* @param string $value
|
||||
* @return void
|
||||
*/
|
||||
public function setValue($value) {
|
||||
|
||||
$this->value = $value;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the internal value
|
||||
*
|
||||
* @param string $value
|
||||
* @return string
|
||||
*/
|
||||
public function getValue() {
|
||||
|
||||
return $this->value;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Turns the object back into a serialized blob.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function serialize() {
|
||||
|
||||
$str = $this->name;
|
||||
if ($this->group) $str = $this->group . '.' . $this->name;
|
||||
|
||||
foreach($this->parameters as $param) {
|
||||
|
||||
$str.=';' . $param->serialize();
|
||||
|
||||
}
|
||||
|
||||
$src = array(
|
||||
'\\',
|
||||
"\n",
|
||||
"\r",
|
||||
);
|
||||
$out = array(
|
||||
'\\\\',
|
||||
'\n',
|
||||
'',
|
||||
);
|
||||
|
||||
// avoid double-escaping of \, and \; from Compound properties
|
||||
if (method_exists($this, 'setParts')) {
|
||||
$src[] = '\\\\,';
|
||||
$out[] = '\\,';
|
||||
$src[] = '\\\\;';
|
||||
$out[] = '\\;';
|
||||
}
|
||||
|
||||
$str.=':' . str_replace($src, $out, $this->value);
|
||||
|
||||
$out = '';
|
||||
while(strlen($str)>0) {
|
||||
if (strlen($str)>75) {
|
||||
$out.= mb_strcut($str,0,75,'utf-8') . "\r\n";
|
||||
$str = ' ' . mb_strcut($str,75,strlen($str),'utf-8');
|
||||
} else {
|
||||
$out.=$str . "\r\n";
|
||||
$str='';
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $out;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new componenten or element
|
||||
*
|
||||
* You can call this method with the following syntaxes:
|
||||
*
|
||||
* add(Parameter $element)
|
||||
* add(string $name, $value)
|
||||
*
|
||||
* The first version adds an Parameter
|
||||
* The second adds a property as a string.
|
||||
*
|
||||
* @param mixed $item
|
||||
* @param mixed $itemValue
|
||||
* @return void
|
||||
*/
|
||||
public function add($item, $itemValue = null) {
|
||||
|
||||
if ($item instanceof Parameter) {
|
||||
if (!is_null($itemValue)) {
|
||||
throw new \InvalidArgumentException('The second argument must not be specified, when passing a VObject');
|
||||
}
|
||||
$item->parent = $this;
|
||||
$this->parameters[] = $item;
|
||||
} elseif(is_string($item)) {
|
||||
|
||||
$parameter = new Parameter($item,$itemValue);
|
||||
$parameter->parent = $this;
|
||||
$this->parameters[] = $parameter;
|
||||
|
||||
} else {
|
||||
|
||||
throw new \InvalidArgumentException('The first argument must either be a Node a string');
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* ArrayAccess interface {{{ */
|
||||
|
||||
/**
|
||||
* Checks if an array element exists
|
||||
*
|
||||
* @param mixed $name
|
||||
* @return bool
|
||||
*/
|
||||
public function offsetExists($name) {
|
||||
|
||||
if (is_int($name)) return parent::offsetExists($name);
|
||||
|
||||
$name = strtoupper($name);
|
||||
|
||||
foreach($this->parameters as $parameter) {
|
||||
if ($parameter->name == $name) return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a parameter, or parameter list.
|
||||
*
|
||||
* @param string $name
|
||||
* @return Node
|
||||
*/
|
||||
public function offsetGet($name) {
|
||||
|
||||
if (is_int($name)) return parent::offsetGet($name);
|
||||
$name = strtoupper($name);
|
||||
|
||||
$result = array();
|
||||
foreach($this->parameters as $parameter) {
|
||||
if ($parameter->name == $name)
|
||||
$result[] = $parameter;
|
||||
}
|
||||
|
||||
if (count($result)===0) {
|
||||
return null;
|
||||
} elseif (count($result)===1) {
|
||||
return $result[0];
|
||||
} else {
|
||||
$result[0]->setIterator(new ElementList($result));
|
||||
return $result[0];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new parameter
|
||||
*
|
||||
* @param string $name
|
||||
* @param mixed $value
|
||||
* @return void
|
||||
*/
|
||||
public function offsetSet($name, $value) {
|
||||
|
||||
if (is_int($name)) parent::offsetSet($name, $value);
|
||||
|
||||
if (is_scalar($value)) {
|
||||
if (!is_string($name))
|
||||
throw new \InvalidArgumentException('A parameter name must be specified. This means you cannot use the $array[]="string" to add parameters.');
|
||||
|
||||
$this->offsetUnset($name);
|
||||
$parameter = new Parameter($name, $value);
|
||||
$parameter->parent = $this;
|
||||
$this->parameters[] = $parameter;
|
||||
|
||||
} elseif ($value instanceof Parameter) {
|
||||
if (!is_null($name))
|
||||
throw new \InvalidArgumentException('Don\'t specify a parameter name if you\'re passing a \\Sabre\\VObject\\Parameter. Add using $array[]=$parameterObject.');
|
||||
|
||||
$value->parent = $this;
|
||||
$this->parameters[] = $value;
|
||||
} else {
|
||||
throw new \InvalidArgumentException('You can only add parameters to the property object');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes one or more parameters with the specified name
|
||||
*
|
||||
* @param string $name
|
||||
* @return void
|
||||
*/
|
||||
public function offsetUnset($name) {
|
||||
|
||||
if (is_int($name)) parent::offsetUnset($name);
|
||||
$name = strtoupper($name);
|
||||
|
||||
foreach($this->parameters as $key=>$parameter) {
|
||||
if ($parameter->name == $name) {
|
||||
$parameter->parent = null;
|
||||
unset($this->parameters[$key]);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* }}} */
|
||||
|
||||
/**
|
||||
* Called when this object is being cast to a string
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function __toString() {
|
||||
|
||||
return (string)$this->value;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is automatically called when the object is cloned.
|
||||
* Specifically, this will ensure all child elements are also cloned.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __clone() {
|
||||
|
||||
foreach($this->parameters as $key=>$child) {
|
||||
$this->parameters[$key] = clone $child;
|
||||
$this->parameters[$key]->parent = $this;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 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();
|
||||
|
||||
// Checking if our value is UTF-8
|
||||
if (!StringUtil::isUTF8($this->value)) {
|
||||
$warnings[] = array(
|
||||
'level' => 1,
|
||||
'message' => 'Property is not valid UTF-8!',
|
||||
'node' => $this,
|
||||
);
|
||||
if ($options & self::REPAIR) {
|
||||
$this->value = StringUtil::convertToUTF8($this->value);
|
||||
}
|
||||
}
|
||||
|
||||
// Checking if the propertyname does not contain any invalid bytes.
|
||||
if (!preg_match('/^([A-Z0-9-]+)$/', $this->name)) {
|
||||
$warnings[] = array(
|
||||
'level' => 1,
|
||||
'message' => 'The propertyname: ' . $this->name . ' contains invalid characters. Only A-Z, 0-9 and - are allowed',
|
||||
'node' => $this,
|
||||
);
|
||||
if ($options & self::REPAIR) {
|
||||
// Uppercasing and converting underscores to dashes.
|
||||
$this->name = strtoupper(
|
||||
str_replace('_', '-', $this->name)
|
||||
);
|
||||
// Removing every other invalid character
|
||||
$this->name = preg_replace('/([^A-Z0-9-])/u', '', $this->name);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Validating inner parameters
|
||||
foreach($this->parameters as $param) {
|
||||
$warnings = array_merge($warnings, $param->validate($options));
|
||||
}
|
||||
|
||||
return $warnings;
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -1,125 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Sabre\VObject\Property;
|
||||
|
||||
use Sabre\VObject;
|
||||
|
||||
/**
|
||||
* Compound property.
|
||||
*
|
||||
* This class adds (de)serialization of compound properties to/from arrays.
|
||||
*
|
||||
* Currently the following properties from RFC 6350 are mapped to use this
|
||||
* class:
|
||||
*
|
||||
* N: Section 6.2.2
|
||||
* ADR: Section 6.3.1
|
||||
* ORG: Section 6.6.4
|
||||
* CATEGORIES: Section 6.7.1
|
||||
*
|
||||
* In order to use this correctly, you must call setParts and getParts to
|
||||
* retrieve and modify dates respectively.
|
||||
*
|
||||
* @author Thomas Tanghus (http://tanghus.net/)
|
||||
* @author Lars Kneschke
|
||||
* @author Evert Pot (http://evertpot.com/)
|
||||
* @copyright Copyright (C) 2007-2013 fruux GmbH (https://fruux.com/).
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
class Compound extends VObject\Property {
|
||||
|
||||
/**
|
||||
* If property names are added to this map, they will be (de)serialised as arrays
|
||||
* using the getParts() and setParts() methods.
|
||||
* The keys are the property names, values are delimiter chars.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
static public $delimiterMap = array(
|
||||
'N' => ';',
|
||||
'ADR' => ';',
|
||||
'ORG' => ';',
|
||||
'CATEGORIES' => ',',
|
||||
);
|
||||
|
||||
/**
|
||||
* The currently used delimiter.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $delimiter = null;
|
||||
|
||||
/**
|
||||
* Get a compound value as an array.
|
||||
*
|
||||
* @param $name string
|
||||
* @return array
|
||||
*/
|
||||
public function getParts() {
|
||||
|
||||
if (is_null($this->value)) {
|
||||
return array();
|
||||
}
|
||||
|
||||
$delimiter = $this->getDelimiter();
|
||||
|
||||
// split by any $delimiter which is NOT prefixed by a slash.
|
||||
// Note that this is not a a perfect solution. If a value is prefixed
|
||||
// by two slashes, it should actually be split anyway.
|
||||
//
|
||||
// Hopefully we can fix this better in a future version, where we can
|
||||
// break compatibility a bit.
|
||||
$compoundValues = preg_split("/(?<!\\\)$delimiter/", $this->value);
|
||||
|
||||
// remove slashes from any semicolon and comma left escaped in the single values
|
||||
$compoundValues = array_map(
|
||||
function($val) {
|
||||
return strtr($val, array('\,' => ',', '\;' => ';'));
|
||||
}, $compoundValues);
|
||||
|
||||
return $compoundValues;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the delimiter for this property.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getDelimiter() {
|
||||
|
||||
if (!$this->delimiter) {
|
||||
if (isset(self::$delimiterMap[$this->name])) {
|
||||
$this->delimiter = self::$delimiterMap[$this->name];
|
||||
} else {
|
||||
// To be a bit future proof, we are going to default the
|
||||
// delimiter to ;
|
||||
$this->delimiter = ';';
|
||||
}
|
||||
}
|
||||
return $this->delimiter;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a compound value as an array.
|
||||
*
|
||||
*
|
||||
* @param $name string
|
||||
* @return array
|
||||
*/
|
||||
public function setParts(array $values) {
|
||||
|
||||
// add slashes to all semicolons and commas in the single values
|
||||
$values = array_map(
|
||||
function($val) {
|
||||
return strtr($val, array(',' => '\,', ';' => '\;'));
|
||||
}, $values);
|
||||
|
||||
$this->setValue(
|
||||
implode($this->getDelimiter(), $values)
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -1,245 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Sabre\VObject\Property;
|
||||
|
||||
use Sabre\VObject;
|
||||
|
||||
/**
|
||||
* DateTime property
|
||||
*
|
||||
* This element is used for iCalendar properties such as the DTSTART property.
|
||||
* It basically provides a few helper functions that make it easier to deal
|
||||
* with these. It supports both DATE-TIME and DATE values.
|
||||
*
|
||||
* In order to use this correctly, you must call setDateTime and getDateTime to
|
||||
* retrieve and modify dates respectively.
|
||||
*
|
||||
* If you use the 'value' or properties directly, this object does not keep
|
||||
* reference and results might appear incorrectly.
|
||||
*
|
||||
* @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 DateTime extends VObject\Property {
|
||||
|
||||
/**
|
||||
* Local 'floating' time
|
||||
*/
|
||||
const LOCAL = 1;
|
||||
|
||||
/**
|
||||
* UTC-based time
|
||||
*/
|
||||
const UTC = 2;
|
||||
|
||||
/**
|
||||
* Local time plus timezone
|
||||
*/
|
||||
const LOCALTZ = 3;
|
||||
|
||||
/**
|
||||
* Only a date, time is ignored
|
||||
*/
|
||||
const DATE = 4;
|
||||
|
||||
/**
|
||||
* DateTime representation
|
||||
*
|
||||
* @var \DateTime
|
||||
*/
|
||||
protected $dateTime;
|
||||
|
||||
/**
|
||||
* dateType
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $dateType;
|
||||
|
||||
/**
|
||||
* Updates the Date and Time.
|
||||
*
|
||||
* @param \DateTime $dt
|
||||
* @param int $dateType
|
||||
* @return void
|
||||
*/
|
||||
public function setDateTime(\DateTime $dt, $dateType = self::LOCALTZ) {
|
||||
|
||||
switch($dateType) {
|
||||
|
||||
case self::LOCAL :
|
||||
$this->setValue($dt->format('Ymd\\THis'));
|
||||
$this->offsetUnset('VALUE');
|
||||
$this->offsetUnset('TZID');
|
||||
$this->offsetSet('VALUE','DATE-TIME');
|
||||
break;
|
||||
case self::UTC :
|
||||
$dt->setTimeZone(new \DateTimeZone('UTC'));
|
||||
$this->setValue($dt->format('Ymd\\THis\\Z'));
|
||||
$this->offsetUnset('VALUE');
|
||||
$this->offsetUnset('TZID');
|
||||
$this->offsetSet('VALUE','DATE-TIME');
|
||||
break;
|
||||
case self::LOCALTZ :
|
||||
$this->setValue($dt->format('Ymd\\THis'));
|
||||
$this->offsetUnset('VALUE');
|
||||
$this->offsetUnset('TZID');
|
||||
$this->offsetSet('VALUE','DATE-TIME');
|
||||
$this->offsetSet('TZID', $dt->getTimeZone()->getName());
|
||||
break;
|
||||
case self::DATE :
|
||||
$this->setValue($dt->format('Ymd'));
|
||||
$this->offsetUnset('VALUE');
|
||||
$this->offsetUnset('TZID');
|
||||
$this->offsetSet('VALUE','DATE');
|
||||
break;
|
||||
default :
|
||||
throw new \InvalidArgumentException('You must pass a valid dateType constant');
|
||||
|
||||
}
|
||||
$this->dateTime = $dt;
|
||||
$this->dateType = $dateType;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current DateTime value.
|
||||
*
|
||||
* If no value was set, this method returns null.
|
||||
*
|
||||
* @return \DateTime|null
|
||||
*/
|
||||
public function getDateTime() {
|
||||
|
||||
if ($this->dateTime)
|
||||
return $this->dateTime;
|
||||
|
||||
list(
|
||||
$this->dateType,
|
||||
$this->dateTime
|
||||
) = self::parseData($this->value, $this);
|
||||
return $this->dateTime;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the type of Date format.
|
||||
*
|
||||
* This method returns one of the format constants. If no date was set,
|
||||
* this method will return null.
|
||||
*
|
||||
* @return int|null
|
||||
*/
|
||||
public function getDateType() {
|
||||
|
||||
if ($this->dateType)
|
||||
return $this->dateType;
|
||||
|
||||
list(
|
||||
$this->dateType,
|
||||
$this->dateTime,
|
||||
) = self::parseData($this->value, $this);
|
||||
return $this->dateType;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This method will return true, if the property had a date and a time, as
|
||||
* opposed to only a date.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasTime() {
|
||||
|
||||
return $this->getDateType()!==self::DATE;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the internal data structure to figure out what the current date
|
||||
* and time is.
|
||||
*
|
||||
* The returned array contains two elements:
|
||||
* 1. A 'DateType' constant (as defined on this class), or null.
|
||||
* 2. A DateTime object (or null)
|
||||
*
|
||||
* @param string|null $propertyValue The string to parse (yymmdd or
|
||||
* ymmddThhmmss, etc..)
|
||||
* @param \Sabre\VObject\Property|null $property The instance of the
|
||||
* property we're parsing.
|
||||
* @return array
|
||||
*/
|
||||
static public function parseData($propertyValue, VObject\Property $property = null) {
|
||||
|
||||
if (is_null($propertyValue)) {
|
||||
return array(null, null);
|
||||
}
|
||||
|
||||
$date = '(?P<year>[1-2][0-9]{3})(?P<month>[0-1][0-9])(?P<date>[0-3][0-9])';
|
||||
$time = '(?P<hour>[0-2][0-9])(?P<minute>[0-5][0-9])(?P<second>[0-5][0-9])';
|
||||
$regex = "/^$date(T$time(?P<isutc>Z)?)?$/";
|
||||
|
||||
if (!preg_match($regex, $propertyValue, $matches)) {
|
||||
throw new \InvalidArgumentException($propertyValue . ' is not a valid \DateTime or Date string');
|
||||
}
|
||||
|
||||
if (!isset($matches['hour'])) {
|
||||
// Date-only
|
||||
return array(
|
||||
self::DATE,
|
||||
new \DateTime($matches['year'] . '-' . $matches['month'] . '-' . $matches['date'] . ' 00:00:00', new \DateTimeZone('UTC')),
|
||||
);
|
||||
}
|
||||
|
||||
$dateStr =
|
||||
$matches['year'] .'-' .
|
||||
$matches['month'] . '-' .
|
||||
$matches['date'] . ' ' .
|
||||
$matches['hour'] . ':' .
|
||||
$matches['minute'] . ':' .
|
||||
$matches['second'];
|
||||
|
||||
if (isset($matches['isutc'])) {
|
||||
$dt = new \DateTime($dateStr,new \DateTimeZone('UTC'));
|
||||
$dt->setTimeZone(new \DateTimeZone('UTC'));
|
||||
return array(
|
||||
self::UTC,
|
||||
$dt
|
||||
);
|
||||
}
|
||||
|
||||
// Finding the timezone.
|
||||
$tzid = $property['TZID'];
|
||||
if (!$tzid) {
|
||||
// This was a floating time string. This implies we use the
|
||||
// timezone from date_default_timezone_set / date.timezone ini
|
||||
// setting.
|
||||
return array(
|
||||
self::LOCAL,
|
||||
new \DateTime($dateStr)
|
||||
);
|
||||
}
|
||||
|
||||
// To look up the timezone, we must first find the VCALENDAR component.
|
||||
$root = $property;
|
||||
while($root->parent) {
|
||||
$root = $root->parent;
|
||||
}
|
||||
if ($root->name === 'VCALENDAR') {
|
||||
$tz = VObject\TimeZoneUtil::getTimeZone((string)$tzid, $root);
|
||||
} else {
|
||||
$tz = VObject\TimeZoneUtil::getTimeZone((string)$tzid);
|
||||
}
|
||||
|
||||
$dt = new \DateTime($dateStr, $tz);
|
||||
$dt->setTimeZone($tz);
|
||||
|
||||
return array(
|
||||
self::LOCALTZ,
|
||||
$dt
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -1,180 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Sabre\VObject\Property;
|
||||
|
||||
use Sabre\VObject;
|
||||
|
||||
/**
|
||||
* Multi-DateTime property
|
||||
*
|
||||
* This element is used for iCalendar properties such as the EXDATE property.
|
||||
* It basically provides a few helper functions that make it easier to deal
|
||||
* with these. It supports both DATE-TIME and DATE values.
|
||||
*
|
||||
* In order to use this correctly, you must call setDateTimes and getDateTimes
|
||||
* to retrieve and modify dates respectively.
|
||||
*
|
||||
* If you use the 'value' or properties directly, this object does not keep
|
||||
* reference and results might appear incorrectly.
|
||||
*
|
||||
* @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 MultiDateTime extends VObject\Property {
|
||||
|
||||
/**
|
||||
* DateTime representation
|
||||
*
|
||||
* @var DateTime[]
|
||||
*/
|
||||
protected $dateTimes;
|
||||
|
||||
/**
|
||||
* dateType
|
||||
*
|
||||
* This is one of the Sabre\VObject\Property\DateTime constants.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $dateType;
|
||||
|
||||
/**
|
||||
* Updates the value
|
||||
*
|
||||
* @param array $dt Must be an array of DateTime objects.
|
||||
* @param int $dateType
|
||||
* @return void
|
||||
*/
|
||||
public function setDateTimes(array $dt, $dateType = VObject\Property\DateTime::LOCALTZ) {
|
||||
|
||||
foreach($dt as $i)
|
||||
if (!$i instanceof \DateTime)
|
||||
throw new \InvalidArgumentException('You must pass an array of DateTime objects');
|
||||
|
||||
$this->offsetUnset('VALUE');
|
||||
$this->offsetUnset('TZID');
|
||||
switch($dateType) {
|
||||
|
||||
case DateTime::LOCAL :
|
||||
$val = array();
|
||||
foreach($dt as $i) {
|
||||
$val[] = $i->format('Ymd\\THis');
|
||||
}
|
||||
$this->setValue(implode(',',$val));
|
||||
$this->offsetSet('VALUE','DATE-TIME');
|
||||
break;
|
||||
case DateTime::UTC :
|
||||
$val = array();
|
||||
foreach($dt as $i) {
|
||||
$i->setTimeZone(new \DateTimeZone('UTC'));
|
||||
$val[] = $i->format('Ymd\\THis\\Z');
|
||||
}
|
||||
$this->setValue(implode(',',$val));
|
||||
$this->offsetSet('VALUE','DATE-TIME');
|
||||
break;
|
||||
case DateTime::LOCALTZ :
|
||||
$val = array();
|
||||
foreach($dt as $i) {
|
||||
$val[] = $i->format('Ymd\\THis');
|
||||
}
|
||||
$this->setValue(implode(',',$val));
|
||||
$this->offsetSet('VALUE','DATE-TIME');
|
||||
$this->offsetSet('TZID', $dt[0]->getTimeZone()->getName());
|
||||
break;
|
||||
case DateTime::DATE :
|
||||
$val = array();
|
||||
foreach($dt as $i) {
|
||||
$val[] = $i->format('Ymd');
|
||||
}
|
||||
$this->setValue(implode(',',$val));
|
||||
$this->offsetSet('VALUE','DATE');
|
||||
break;
|
||||
default :
|
||||
throw new \InvalidArgumentException('You must pass a valid dateType constant');
|
||||
|
||||
}
|
||||
$this->dateTimes = $dt;
|
||||
$this->dateType = $dateType;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current DateTime value.
|
||||
*
|
||||
* If no value was set, this method returns null.
|
||||
*
|
||||
* @return array|null
|
||||
*/
|
||||
public function getDateTimes() {
|
||||
|
||||
if ($this->dateTimes)
|
||||
return $this->dateTimes;
|
||||
|
||||
$dts = array();
|
||||
|
||||
if (!$this->value) {
|
||||
$this->dateTimes = null;
|
||||
$this->dateType = null;
|
||||
return null;
|
||||
}
|
||||
|
||||
foreach(explode(',',$this->value) as $val) {
|
||||
list(
|
||||
$type,
|
||||
$dt
|
||||
) = DateTime::parseData($val, $this);
|
||||
$dts[] = $dt;
|
||||
$this->dateType = $type;
|
||||
}
|
||||
$this->dateTimes = $dts;
|
||||
return $this->dateTimes;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the type of Date format.
|
||||
*
|
||||
* This method returns one of the format constants. If no date was set,
|
||||
* this method will return null.
|
||||
*
|
||||
* @return int|null
|
||||
*/
|
||||
public function getDateType() {
|
||||
|
||||
if ($this->dateType)
|
||||
return $this->dateType;
|
||||
|
||||
if (!$this->value) {
|
||||
$this->dateTimes = null;
|
||||
$this->dateType = null;
|
||||
return null;
|
||||
}
|
||||
|
||||
$dts = array();
|
||||
foreach(explode(',',$this->value) as $val) {
|
||||
list(
|
||||
$type,
|
||||
$dt
|
||||
) = DateTime::parseData($val, $this);
|
||||
$dts[] = $dt;
|
||||
$this->dateType = $type;
|
||||
}
|
||||
$this->dateTimes = $dts;
|
||||
return $this->dateType;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This method will return true, if the property had a date and a time, as
|
||||
* opposed to only a date.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasTime() {
|
||||
|
||||
return $this->getDateType()!==DateTime::DATE;
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -1,223 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Sabre\VObject;
|
||||
|
||||
/**
|
||||
* VCALENDAR/VCARD reader
|
||||
*
|
||||
* This class reads the vobject file, and returns a full element tree.
|
||||
*
|
||||
* TODO: this class currently completely works 'statically'. This is pointless,
|
||||
* and defeats OOP principals. Needs refactoring in a future version.
|
||||
*
|
||||
* @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 Reader {
|
||||
|
||||
/**
|
||||
* If this option is passed to the reader, it will be less strict about the
|
||||
* validity of the lines.
|
||||
*
|
||||
* Currently using this option just means, that it will accept underscores
|
||||
* in property names.
|
||||
*/
|
||||
const OPTION_FORGIVING = 1;
|
||||
|
||||
/**
|
||||
* If this option is turned on, any lines we cannot parse will be ignored
|
||||
* by the reader.
|
||||
*/
|
||||
const OPTION_IGNORE_INVALID_LINES = 2;
|
||||
|
||||
/**
|
||||
* Parses the file and returns the top component
|
||||
*
|
||||
* The options argument is a bitfield. Pass any of the OPTIONS constant to
|
||||
* alter the parsers' behaviour.
|
||||
*
|
||||
* @param string $data
|
||||
* @param int $options
|
||||
* @return Node
|
||||
*/
|
||||
static function read($data, $options = 0) {
|
||||
|
||||
// Normalizing newlines
|
||||
$data = str_replace(array("\r","\n\n"), array("\n","\n"), $data);
|
||||
|
||||
$lines = explode("\n", $data);
|
||||
|
||||
// Unfolding lines
|
||||
$lines2 = array();
|
||||
foreach($lines as $line) {
|
||||
|
||||
// Skipping empty lines
|
||||
if (!$line) continue;
|
||||
|
||||
if ($line[0]===" " || $line[0]==="\t") {
|
||||
$lines2[count($lines2)-1].=substr($line,1);
|
||||
} else {
|
||||
$lines2[] = $line;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
unset($lines);
|
||||
|
||||
reset($lines2);
|
||||
|
||||
return self::readLine($lines2, $options);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads and parses a single line.
|
||||
*
|
||||
* This method receives the full array of lines. The array pointer is used
|
||||
* to traverse.
|
||||
*
|
||||
* This method returns null if an invalid line was encountered, and the
|
||||
* IGNORE_INVALID_LINES option was turned on.
|
||||
*
|
||||
* @param array $lines
|
||||
* @param int $options See the OPTIONS constants.
|
||||
* @return Node
|
||||
*/
|
||||
static private function readLine(&$lines, $options = 0) {
|
||||
|
||||
$line = current($lines);
|
||||
$lineNr = key($lines);
|
||||
next($lines);
|
||||
|
||||
// Components
|
||||
if (strtoupper(substr($line,0,6)) === "BEGIN:") {
|
||||
|
||||
$componentName = strtoupper(substr($line,6));
|
||||
$obj = Component::create($componentName);
|
||||
|
||||
$nextLine = current($lines);
|
||||
|
||||
while(strtoupper(substr($nextLine,0,4))!=="END:") {
|
||||
|
||||
$parsedLine = self::readLine($lines, $options);
|
||||
$nextLine = current($lines);
|
||||
|
||||
if (is_null($parsedLine)) {
|
||||
continue;
|
||||
}
|
||||
$obj->add($parsedLine);
|
||||
|
||||
if ($nextLine===false)
|
||||
throw new ParseException('Invalid VObject. Document ended prematurely.');
|
||||
|
||||
}
|
||||
|
||||
// Checking component name of the 'END:' line.
|
||||
if (substr($nextLine,4)!==$obj->name) {
|
||||
throw new ParseException('Invalid VObject, expected: "END:' . $obj->name . '" got: "' . $nextLine . '"');
|
||||
}
|
||||
next($lines);
|
||||
|
||||
return $obj;
|
||||
|
||||
}
|
||||
|
||||
// Properties
|
||||
//$result = preg_match('/(?P<name>[A-Z0-9-]+)(?:;(?P<parameters>^(?<!:):))(.*)$/',$line,$matches);
|
||||
|
||||
if ($options & self::OPTION_FORGIVING) {
|
||||
$token = '[A-Z0-9-\._]+';
|
||||
} else {
|
||||
$token = '[A-Z0-9-\.]+';
|
||||
}
|
||||
$parameters = "(?:;(?P<parameters>([^:^\"]|\"([^\"]*)\")*))?";
|
||||
$regex = "/^(?P<name>$token)$parameters:(?P<value>.*)$/i";
|
||||
|
||||
$result = preg_match($regex,$line,$matches);
|
||||
|
||||
if (!$result) {
|
||||
if ($options & self::OPTION_IGNORE_INVALID_LINES) {
|
||||
return null;
|
||||
} else {
|
||||
throw new ParseException('Invalid VObject, line ' . ($lineNr+1) . ' did not follow the icalendar/vcard format');
|
||||
}
|
||||
}
|
||||
|
||||
$propertyName = strtoupper($matches['name']);
|
||||
$propertyValue = preg_replace_callback('#(\\\\(\\\\|N|n))#',function($matches) {
|
||||
if ($matches[2]==='n' || $matches[2]==='N') {
|
||||
return "\n";
|
||||
} else {
|
||||
return $matches[2];
|
||||
}
|
||||
}, $matches['value']);
|
||||
|
||||
$obj = Property::create($propertyName, $propertyValue);
|
||||
|
||||
if ($matches['parameters']) {
|
||||
|
||||
foreach(self::readParameters($matches['parameters']) as $param) {
|
||||
$obj->add($param);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return $obj;
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a parameter list from a property
|
||||
*
|
||||
* This method returns an array of Parameter
|
||||
*
|
||||
* @param string $parameters
|
||||
* @return array
|
||||
*/
|
||||
static private function readParameters($parameters) {
|
||||
|
||||
$token = '[A-Z0-9-]+';
|
||||
|
||||
$paramValue = '(?P<paramValue>[^\"^;]*|"[^"]*")';
|
||||
|
||||
$regex = "/(?<=^|;)(?P<paramName>$token)(=$paramValue(?=$|;))?/i";
|
||||
preg_match_all($regex, $parameters, $matches, PREG_SET_ORDER);
|
||||
|
||||
$params = array();
|
||||
foreach($matches as $match) {
|
||||
|
||||
if (!isset($match['paramValue'])) {
|
||||
|
||||
$value = null;
|
||||
|
||||
} else {
|
||||
|
||||
$value = $match['paramValue'];
|
||||
|
||||
if (isset($value[0]) && $value[0]==='"') {
|
||||
// Stripping quotes, if needed
|
||||
$value = substr($value,1,strlen($value)-2);
|
||||
}
|
||||
|
||||
$value = preg_replace_callback('#(\\\\(\\\\|N|n|;|,))#',function($matches) {
|
||||
if ($matches[2]==='n' || $matches[2]==='N') {
|
||||
return "\n";
|
||||
} else {
|
||||
return $matches[2];
|
||||
}
|
||||
}, $value);
|
||||
|
||||
}
|
||||
|
||||
$params[] = new Parameter($match['paramName'], $value);
|
||||
|
||||
}
|
||||
|
||||
return $params;
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
File diff suppressed because it is too large
Load diff
|
@ -1,111 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Sabre\VObject\Splitter;
|
||||
|
||||
use Sabre\VObject;
|
||||
|
||||
/**
|
||||
* Splitter
|
||||
*
|
||||
* This class is responsible for splitting up iCalendar objects.
|
||||
*
|
||||
* This class expects a single VCALENDAR object with one or more
|
||||
* calendar-objects inside. Objects with identical UID's will be combined into
|
||||
* a single object.
|
||||
*
|
||||
* @copyright Copyright (C) 2007-2013 fruux GmbH (https://fruux.com/).
|
||||
* @author Dominik Tobschall
|
||||
* @author Armin Hackmann
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
class ICalendar implements SplitterInterface {
|
||||
|
||||
/**
|
||||
* Timezones
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $vtimezones = array();
|
||||
|
||||
/**
|
||||
* iCalendar objects
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $objects = array();
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* The splitter should receive an readable file stream as it's input.
|
||||
*
|
||||
* @param resource $input
|
||||
*/
|
||||
public function __construct($input) {
|
||||
|
||||
$data = VObject\Reader::read(stream_get_contents($input));
|
||||
$vtimezones = array();
|
||||
$components = array();
|
||||
|
||||
foreach($data->children as $component) {
|
||||
if (!$component instanceof VObject\Component) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Get all timezones
|
||||
if ($component->name === 'VTIMEZONE') {
|
||||
$this->vtimezones[(string)$component->TZID] = $component;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Get component UID for recurring Events search
|
||||
if($component->UID) {
|
||||
$uid = (string)$component->UID;
|
||||
} else {
|
||||
// Generating a random UID
|
||||
$uid = sha1(microtime()) . '-vobjectimport';
|
||||
}
|
||||
|
||||
// Take care of recurring events
|
||||
if (!array_key_exists($uid, $this->objects)) {
|
||||
$this->objects[$uid] = VObject\Component::create('VCALENDAR');
|
||||
}
|
||||
|
||||
$this->objects[$uid]->add(clone $component);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Every time getNext() is called, a new object will be parsed, until we
|
||||
* hit the end of the stream.
|
||||
*
|
||||
* When the end is reached, null will be returned.
|
||||
*
|
||||
* @return Sabre\VObject\Component|null
|
||||
*/
|
||||
public function getNext() {
|
||||
|
||||
if($object=array_shift($this->objects)) {
|
||||
|
||||
// create our baseobject
|
||||
$object->version = '2.0';
|
||||
$object->prodid = '-//Sabre//Sabre VObject ' . VObject\Version::VERSION . '//EN';
|
||||
$object->calscale = 'GREGORIAN';
|
||||
|
||||
// add vtimezone information to obj (if we have it)
|
||||
foreach ($this->vtimezones as $vtimezone) {
|
||||
$object->add($vtimezone);
|
||||
}
|
||||
|
||||
return $object;
|
||||
|
||||
} else {
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -1,39 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Sabre\VObject\Splitter;
|
||||
|
||||
/**
|
||||
* VObject splitter
|
||||
*
|
||||
* The splitter is responsible for reading a large vCard or iCalendar object,
|
||||
* and splitting it into multiple objects.
|
||||
*
|
||||
* This is for example for Card and CalDAV, which require every event and vcard
|
||||
* to exist in their own objects, instead of one large one.
|
||||
*
|
||||
* @copyright Copyright (C) 2007-2013 fruux GmbH (https://fruux.com/).
|
||||
* @author Dominik Tobschall
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
interface SplitterInterface {
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* The splitter should receive an readable file stream as it's input.
|
||||
*
|
||||
* @param resource $input
|
||||
*/
|
||||
function __construct($input);
|
||||
|
||||
/**
|
||||
* Every time getNext() is called, a new object will be parsed, until we
|
||||
* hit the end of the stream.
|
||||
*
|
||||
* When the end is reached, null will be returned.
|
||||
*
|
||||
* @return Sabre\VObject\Component|null
|
||||
*/
|
||||
function getNext();
|
||||
|
||||
}
|
|
@ -1,76 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Sabre\VObject\Splitter;
|
||||
|
||||
use Sabre\VObject;
|
||||
|
||||
/**
|
||||
* Splitter
|
||||
*
|
||||
* This class is responsible for splitting up VCard objects.
|
||||
*
|
||||
* It is assumed that the input stream contains 1 or more VCARD objects. This
|
||||
* class checks for BEGIN:VCARD and END:VCARD and parses each encountered
|
||||
* component individually.
|
||||
*
|
||||
* @copyright Copyright (C) 2007-2013 fruux GmbH (https://fruux.com/).
|
||||
* @author Dominik Tobschall
|
||||
* @author Armin Hackmann
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
class VCard implements SplitterInterface {
|
||||
|
||||
/**
|
||||
* File handle
|
||||
*
|
||||
* @var resource
|
||||
*/
|
||||
protected $input;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* The splitter should receive an readable file stream as it's input.
|
||||
*
|
||||
* @param resource $input
|
||||
*/
|
||||
public function __construct($input) {
|
||||
|
||||
$this->input = $input;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Every time getNext() is called, a new object will be parsed, until we
|
||||
* hit the end of the stream.
|
||||
*
|
||||
* When the end is reached, null will be returned.
|
||||
*
|
||||
* @return Sabre\VObject\Component|null
|
||||
*/
|
||||
public function getNext() {
|
||||
|
||||
$vcard = '';
|
||||
|
||||
do {
|
||||
|
||||
if (feof($this->input)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$line = fgets($this->input);
|
||||
$vcard .= $line;
|
||||
|
||||
} while(strtoupper(substr($line,0,4))!=="END:");
|
||||
|
||||
$object = VObject\Reader::read($vcard);
|
||||
|
||||
if($object->name !== 'VCARD') {
|
||||
throw new \InvalidArgumentException("Thats no vCard!", 1);
|
||||
}
|
||||
|
||||
return $object;
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -1,61 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Sabre\VObject;
|
||||
|
||||
/**
|
||||
* Useful utilities for working with various strings.
|
||||
*
|
||||
* @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 StringUtil {
|
||||
|
||||
/**
|
||||
* Returns true or false depending on if a string is valid UTF-8
|
||||
*
|
||||
* @param string $str
|
||||
* @return bool
|
||||
*/
|
||||
static function isUTF8($str) {
|
||||
|
||||
// First check.. mb_check_encoding
|
||||
if (!mb_check_encoding($str, 'UTF-8')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Control characters
|
||||
if (preg_match('%(?:[\x00-\x08\x0B-\x0C\x0E\x0F])%', $str)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This method tries its best to convert the input string to UTF-8.
|
||||
*
|
||||
* Currently only ISO-5991-1 input and UTF-8 input is supported, but this
|
||||
* may be expanded upon if we receive other examples.
|
||||
*
|
||||
* @param string $str
|
||||
* @return string
|
||||
*/
|
||||
static function convertToUTF8($str) {
|
||||
|
||||
$encoding = mb_detect_encoding($str , array('UTF-8','ISO-8859-1'), true);
|
||||
|
||||
if ($encoding === 'ISO-8859-1') {
|
||||
$newStr = utf8_encode($str);
|
||||
} else {
|
||||
$newStr = $str;
|
||||
}
|
||||
|
||||
// Removing any control characters
|
||||
return (preg_replace('%(?:[\x00-\x08\x0B-\x0C\x0E\x0F])%', '', $newStr));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,482 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Sabre\VObject;
|
||||
|
||||
/**
|
||||
* Time zone name translation
|
||||
*
|
||||
* This file translates well-known time zone names into "Olson database" time zone names.
|
||||
*
|
||||
* @copyright Copyright (C) 2007-2013 fruux GmbH (https://fruux.com/).
|
||||
* @author Frank Edelhaeuser (fedel@users.sourceforge.net)
|
||||
* @author Evert Pot (http://evertpot.com/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
class TimeZoneUtil {
|
||||
|
||||
public static $map = array(
|
||||
|
||||
// from http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/zone_tzid.html
|
||||
// snapshot taken on 2012/01/16
|
||||
|
||||
// windows
|
||||
'AUS Central Standard Time'=>'Australia/Darwin',
|
||||
'AUS Eastern Standard Time'=>'Australia/Sydney',
|
||||
'Afghanistan Standard Time'=>'Asia/Kabul',
|
||||
'Alaskan Standard Time'=>'America/Anchorage',
|
||||
'Arab Standard Time'=>'Asia/Riyadh',
|
||||
'Arabian Standard Time'=>'Asia/Dubai',
|
||||
'Arabic Standard Time'=>'Asia/Baghdad',
|
||||
'Argentina Standard Time'=>'America/Buenos_Aires',
|
||||
'Armenian Standard Time'=>'Asia/Yerevan',
|
||||
'Atlantic Standard Time'=>'America/Halifax',
|
||||
'Azerbaijan Standard Time'=>'Asia/Baku',
|
||||
'Azores Standard Time'=>'Atlantic/Azores',
|
||||
'Bangladesh Standard Time'=>'Asia/Dhaka',
|
||||
'Canada Central Standard Time'=>'America/Regina',
|
||||
'Cape Verde Standard Time'=>'Atlantic/Cape_Verde',
|
||||
'Caucasus Standard Time'=>'Asia/Yerevan',
|
||||
'Cen. Australia Standard Time'=>'Australia/Adelaide',
|
||||
'Central America Standard Time'=>'America/Guatemala',
|
||||
'Central Asia Standard Time'=>'Asia/Almaty',
|
||||
'Central Brazilian Standard Time'=>'America/Cuiaba',
|
||||
'Central Europe Standard Time'=>'Europe/Budapest',
|
||||
'Central European Standard Time'=>'Europe/Warsaw',
|
||||
'Central Pacific Standard Time'=>'Pacific/Guadalcanal',
|
||||
'Central Standard Time'=>'America/Chicago',
|
||||
'Central Standard Time (Mexico)'=>'America/Mexico_City',
|
||||
'China Standard Time'=>'Asia/Shanghai',
|
||||
'Dateline Standard Time'=>'Etc/GMT+12',
|
||||
'E. Africa Standard Time'=>'Africa/Nairobi',
|
||||
'E. Australia Standard Time'=>'Australia/Brisbane',
|
||||
'E. Europe Standard Time'=>'Europe/Minsk',
|
||||
'E. South America Standard Time'=>'America/Sao_Paulo',
|
||||
'Eastern Standard Time'=>'America/New_York',
|
||||
'Egypt Standard Time'=>'Africa/Cairo',
|
||||
'Ekaterinburg Standard Time'=>'Asia/Yekaterinburg',
|
||||
'FLE Standard Time'=>'Europe/Kiev',
|
||||
'Fiji Standard Time'=>'Pacific/Fiji',
|
||||
'GMT Standard Time'=>'Europe/London',
|
||||
'GTB Standard Time'=>'Europe/Istanbul',
|
||||
'Georgian Standard Time'=>'Asia/Tbilisi',
|
||||
'Greenland Standard Time'=>'America/Godthab',
|
||||
'Greenwich Standard Time'=>'Atlantic/Reykjavik',
|
||||
'Hawaiian Standard Time'=>'Pacific/Honolulu',
|
||||
'India Standard Time'=>'Asia/Calcutta',
|
||||
'Iran Standard Time'=>'Asia/Tehran',
|
||||
'Israel Standard Time'=>'Asia/Jerusalem',
|
||||
'Jordan Standard Time'=>'Asia/Amman',
|
||||
'Kamchatka Standard Time'=>'Asia/Kamchatka',
|
||||
'Korea Standard Time'=>'Asia/Seoul',
|
||||
'Magadan Standard Time'=>'Asia/Magadan',
|
||||
'Mauritius Standard Time'=>'Indian/Mauritius',
|
||||
'Mexico Standard Time'=>'America/Mexico_City',
|
||||
'Mexico Standard Time 2'=>'America/Chihuahua',
|
||||
'Mid-Atlantic Standard Time'=>'Etc/GMT-2',
|
||||
'Middle East Standard Time'=>'Asia/Beirut',
|
||||
'Montevideo Standard Time'=>'America/Montevideo',
|
||||
'Morocco Standard Time'=>'Africa/Casablanca',
|
||||
'Mountain Standard Time'=>'America/Denver',
|
||||
'Mountain Standard Time (Mexico)'=>'America/Chihuahua',
|
||||
'Myanmar Standard Time'=>'Asia/Rangoon',
|
||||
'N. Central Asia Standard Time'=>'Asia/Novosibirsk',
|
||||
'Namibia Standard Time'=>'Africa/Windhoek',
|
||||
'Nepal Standard Time'=>'Asia/Katmandu',
|
||||
'New Zealand Standard Time'=>'Pacific/Auckland',
|
||||
'Newfoundland Standard Time'=>'America/St_Johns',
|
||||
'North Asia East Standard Time'=>'Asia/Irkutsk',
|
||||
'North Asia Standard Time'=>'Asia/Krasnoyarsk',
|
||||
'Pacific SA Standard Time'=>'America/Santiago',
|
||||
'Pacific Standard Time'=>'America/Los_Angeles',
|
||||
'Pacific Standard Time (Mexico)'=>'America/Santa_Isabel',
|
||||
'Pakistan Standard Time'=>'Asia/Karachi',
|
||||
'Paraguay Standard Time'=>'America/Asuncion',
|
||||
'Romance Standard Time'=>'Europe/Paris',
|
||||
'Russian Standard Time'=>'Europe/Moscow',
|
||||
'SA Eastern Standard Time'=>'America/Cayenne',
|
||||
'SA Pacific Standard Time'=>'America/Bogota',
|
||||
'SA Western Standard Time'=>'America/La_Paz',
|
||||
'SE Asia Standard Time'=>'Asia/Bangkok',
|
||||
'Samoa Standard Time'=>'Pacific/Apia',
|
||||
'Singapore Standard Time'=>'Asia/Singapore',
|
||||
'South Africa Standard Time'=>'Africa/Johannesburg',
|
||||
'Sri Lanka Standard Time'=>'Asia/Colombo',
|
||||
'Syria Standard Time'=>'Asia/Damascus',
|
||||
'Taipei Standard Time'=>'Asia/Taipei',
|
||||
'Tasmania Standard Time'=>'Australia/Hobart',
|
||||
'Tokyo Standard Time'=>'Asia/Tokyo',
|
||||
'Tonga Standard Time'=>'Pacific/Tongatapu',
|
||||
'US Eastern Standard Time'=>'America/Indianapolis',
|
||||
'US Mountain Standard Time'=>'America/Phoenix',
|
||||
'UTC+12'=>'Etc/GMT-12',
|
||||
'UTC-02'=>'Etc/GMT+2',
|
||||
'UTC-11'=>'Etc/GMT+11',
|
||||
'Ulaanbaatar Standard Time'=>'Asia/Ulaanbaatar',
|
||||
'Venezuela Standard Time'=>'America/Caracas',
|
||||
'Vladivostok Standard Time'=>'Asia/Vladivostok',
|
||||
'W. Australia Standard Time'=>'Australia/Perth',
|
||||
'W. Central Africa Standard Time'=>'Africa/Lagos',
|
||||
'W. Europe Standard Time'=>'Europe/Berlin',
|
||||
'West Asia Standard Time'=>'Asia/Tashkent',
|
||||
'West Pacific Standard Time'=>'Pacific/Port_Moresby',
|
||||
'Yakutsk Standard Time'=>'Asia/Yakutsk',
|
||||
|
||||
// Microsoft exchange timezones
|
||||
// Source:
|
||||
// http://msdn.microsoft.com/en-us/library/ms988620%28v=exchg.65%29.aspx
|
||||
//
|
||||
// Correct timezones deduced with help from:
|
||||
// http://en.wikipedia.org/wiki/List_of_tz_database_time_zones
|
||||
'Universal Coordinated Time' => 'UTC',
|
||||
'Casablanca, Monrovia' => 'Africa/Casablanca',
|
||||
'Greenwich Mean Time: Dublin, Edinburgh, Lisbon, London' => 'Europe/Lisbon',
|
||||
'Greenwich Mean Time; Dublin, Edinburgh, London' => 'Europe/London',
|
||||
'Amsterdam, Berlin, Bern, Rome, Stockholm, Vienna' => 'Europe/Berlin',
|
||||
'Belgrade, Pozsony, Budapest, Ljubljana, Prague' => 'Europe/Prague',
|
||||
'Brussels, Copenhagen, Madrid, Paris' => 'Europe/Paris',
|
||||
'Paris, Madrid, Brussels, Copenhagen' => 'Europe/Paris',
|
||||
'Prague, Central Europe' => 'Europe/Prague',
|
||||
'Sarajevo, Skopje, Sofija, Vilnius, Warsaw, Zagreb' => 'Europe/Sarajevo',
|
||||
'West Central Africa' => 'Africa/Luanda', // This was a best guess
|
||||
'Athens, Istanbul, Minsk' => 'Europe/Athens',
|
||||
'Bucharest' => 'Europe/Bucharest',
|
||||
'Cairo' => 'Africa/Cairo',
|
||||
'Harare, Pretoria' => 'Africa/Harare',
|
||||
'Helsinki, Riga, Tallinn' => 'Europe/Helsinki',
|
||||
'Israel, Jerusalem Standard Time' => 'Asia/Jerusalem',
|
||||
'Baghdad' => 'Asia/Baghdad',
|
||||
'Arab, Kuwait, Riyadh' => 'Asia/Kuwait',
|
||||
'Moscow, St. Petersburg, Volgograd' => 'Europe/Moscow',
|
||||
'East Africa, Nairobi' => 'Africa/Nairobi',
|
||||
'Tehran' => 'Asia/Tehran',
|
||||
'Abu Dhabi, Muscat' => 'Asia/Muscat', // Best guess
|
||||
'Baku, Tbilisi, Yerevan' => 'Asia/Baku',
|
||||
'Kabul' => 'Asia/Kabul',
|
||||
'Ekaterinburg' => 'Asia/Yekaterinburg',
|
||||
'Islamabad, Karachi, Tashkent' => 'Asia/Karachi',
|
||||
'Kolkata, Chennai, Mumbai, New Delhi, India Standard Time' => 'Asia/Calcutta',
|
||||
'Kathmandu, Nepal' => 'Asia/Kathmandu',
|
||||
'Almaty, Novosibirsk, North Central Asia' => 'Asia/Almaty',
|
||||
'Astana, Dhaka' => 'Asia/Dhaka',
|
||||
'Sri Jayawardenepura, Sri Lanka' => 'Asia/Colombo',
|
||||
'Rangoon' => 'Asia/Rangoon',
|
||||
'Bangkok, Hanoi, Jakarta' => 'Asia/Bangkok',
|
||||
'Krasnoyarsk' => 'Asia/Krasnoyarsk',
|
||||
'Beijing, Chongqing, Hong Kong SAR, Urumqi' => 'Asia/Shanghai',
|
||||
'Irkutsk, Ulaan Bataar' => 'Asia/Irkutsk',
|
||||
'Kuala Lumpur, Singapore' => 'Asia/Singapore',
|
||||
'Perth, Western Australia' => 'Australia/Perth',
|
||||
'Taipei' => 'Asia/Taipei',
|
||||
'Osaka, Sapporo, Tokyo' => 'Asia/Tokyo',
|
||||
'Seoul, Korea Standard time' => 'Asia/Seoul',
|
||||
'Yakutsk' => 'Asia/Yakutsk',
|
||||
'Adelaide, Central Australia' => 'Australia/Adelaide',
|
||||
'Darwin' => 'Australia/Darwin',
|
||||
'Brisbane, East Australia' => 'Australia/Brisbane',
|
||||
'Canberra, Melbourne, Sydney, Hobart (year 2000 only)' => 'Australia/Sydney',
|
||||
'Guam, Port Moresby' => 'Pacific/Guam',
|
||||
'Hobart, Tasmania' => 'Australia/Hobart',
|
||||
'Vladivostok' => 'Asia/Vladivostok',
|
||||
'Magadan, Solomon Is., New Caledonia' => 'Asia/Magadan',
|
||||
'Auckland, Wellington' => 'Pacific/Auckland',
|
||||
'Fiji Islands, Kamchatka, Marshall Is.' => 'Pacific/Fiji',
|
||||
'Nuku\'alofa, Tonga' => 'Pacific/Tongatapu',
|
||||
'Azores' => 'Atlantic/Azores',
|
||||
'Cape Verde Is.' => 'Atlantic/Cape_Verde',
|
||||
'Mid-Atlantic' => 'America/Noronha',
|
||||
'Brasilia' => 'America/Sao_Paulo', // Best guess
|
||||
'Buenos Aires' => 'America/Argentina/Buenos_Aires',
|
||||
'Greenland' => 'America/Godthab',
|
||||
'Newfoundland' => 'America/St_Johns',
|
||||
'Atlantic Time (Canada)' => 'America/Halifax',
|
||||
'Caracas, La Paz' => 'America/Caracas',
|
||||
'Santiago' => 'America/Santiago',
|
||||
'Bogota, Lima, Quito' => 'America/Bogota',
|
||||
'Eastern Time (US & Canada)' => 'America/New_York',
|
||||
'Indiana (East)' => 'America/Indiana/Indianapolis',
|
||||
'Central America' => 'America/Guatemala',
|
||||
'Central Time (US & Canada)' => 'America/Chicago',
|
||||
'Mexico City, Tegucigalpa' => 'America/Mexico_City',
|
||||
'Saskatchewan' => 'America/Edmonton',
|
||||
'Arizona' => 'America/Phoenix',
|
||||
'Mountain Time (US & Canada)' => 'America/Denver', // Best guess
|
||||
'Pacific Time (US & Canada); Tijuana' => 'America/Los_Angeles', // Best guess
|
||||
'Alaska' => 'America/Anchorage',
|
||||
'Hawaii' => 'Pacific/Honolulu',
|
||||
'Midway Island, Samoa' => 'Pacific/Midway',
|
||||
'Eniwetok, Kwajalein, Dateline Time' => 'Pacific/Kwajalein',
|
||||
|
||||
// The following list are timezone names that could be generated by
|
||||
// Lotus / Domino
|
||||
'Dateline' => 'Etc/GMT-12',
|
||||
'Samoa' => 'Pacific/Apia',
|
||||
'Hawaiian' => 'Pacific/Honolulu',
|
||||
'Alaskan' => 'America/Anchorage',
|
||||
'Pacific' => 'America/Los_Angeles',
|
||||
'Pacific Standard Time' => 'America/Los_Angeles',
|
||||
'Mexico Standard Time 2' => 'America/Chihuahua',
|
||||
'Mountain' => 'America/Denver',
|
||||
'Mountain Standard Time' => 'America/Chihuahua',
|
||||
'US Mountain' => 'America/Phoenix',
|
||||
'Canada Central' => 'America/Edmonton',
|
||||
'Central America' => 'America/Guatemala',
|
||||
'Central' => 'America/Chicago',
|
||||
'Central Standard Time' => 'America/Mexico_City',
|
||||
'Mexico' => 'America/Mexico_City',
|
||||
'Eastern' => 'America/New_York',
|
||||
'SA Pacific' => 'America/Bogota',
|
||||
'US Eastern' => 'America/Indiana/Indianapolis',
|
||||
'Venezuela' => 'America/Caracas',
|
||||
'Atlantic' => 'America/Halifax',
|
||||
'Central Brazilian' => 'America/Manaus',
|
||||
'Pacific SA' => 'America/Santiago',
|
||||
'SA Western' => 'America/La_Paz',
|
||||
'Newfoundland' => 'America/St_Johns',
|
||||
'Argentina' => 'America/Argentina/Buenos_Aires',
|
||||
'E. South America' => 'America/Belem',
|
||||
'Greenland' => 'America/Godthab',
|
||||
'Montevideo' => 'America/Montevideo',
|
||||
'SA Eastern' => 'America/Belem',
|
||||
'Mid-Atlantic' => 'Etc/GMT-2',
|
||||
'Azores' => 'Atlantic/Azores',
|
||||
'Cape Verde' => 'Atlantic/Cape_Verde',
|
||||
'Greenwich' => 'Atlantic/Reykjavik', // No I'm serious.. Greenwich is not GMT.
|
||||
'Morocco' => 'Africa/Casablanca',
|
||||
'Central Europe' => 'Europe/Prague',
|
||||
'Central European' => 'Europe/Sarajevo',
|
||||
'Romance' => 'Europe/Paris',
|
||||
'W. Central Africa' => 'Africa/Lagos', // Best guess
|
||||
'W. Europe' => 'Europe/Amsterdam',
|
||||
'E. Europe' => 'Europe/Minsk',
|
||||
'Egypt' => 'Africa/Cairo',
|
||||
'FLE' => 'Europe/Helsinki',
|
||||
'GTB' => 'Europe/Athens',
|
||||
'Israel' => 'Asia/Jerusalem',
|
||||
'Jordan' => 'Asia/Amman',
|
||||
'Middle East' => 'Asia/Beirut',
|
||||
'Namibia' => 'Africa/Windhoek',
|
||||
'South Africa' => 'Africa/Harare',
|
||||
'Arab' => 'Asia/Kuwait',
|
||||
'Arabic' => 'Asia/Baghdad',
|
||||
'E. Africa' => 'Africa/Nairobi',
|
||||
'Georgian' => 'Asia/Tbilisi',
|
||||
'Russian' => 'Europe/Moscow',
|
||||
'Iran' => 'Asia/Tehran',
|
||||
'Arabian' => 'Asia/Muscat',
|
||||
'Armenian' => 'Asia/Yerevan',
|
||||
'Azerbijan' => 'Asia/Baku',
|
||||
'Caucasus' => 'Asia/Yerevan',
|
||||
'Mauritius' => 'Indian/Mauritius',
|
||||
'Afghanistan' => 'Asia/Kabul',
|
||||
'Ekaterinburg' => 'Asia/Yekaterinburg',
|
||||
'Pakistan' => 'Asia/Karachi',
|
||||
'West Asia' => 'Asia/Tashkent',
|
||||
'India' => 'Asia/Calcutta',
|
||||
'Sri Lanka' => 'Asia/Colombo',
|
||||
'Nepal' => 'Asia/Kathmandu',
|
||||
'Central Asia' => 'Asia/Dhaka',
|
||||
'N. Central Asia' => 'Asia/Almaty',
|
||||
'Myanmar' => 'Asia/Rangoon',
|
||||
'North Asia' => 'Asia/Krasnoyarsk',
|
||||
'SE Asia' => 'Asia/Bangkok',
|
||||
'China' => 'Asia/Shanghai',
|
||||
'North Asia East' => 'Asia/Irkutsk',
|
||||
'Singapore' => 'Asia/Singapore',
|
||||
'Taipei' => 'Asia/Taipei',
|
||||
'W. Australia' => 'Australia/Perth',
|
||||
'Korea' => 'Asia/Seoul',
|
||||
'Tokyo' => 'Asia/Tokyo',
|
||||
'Yakutsk' => 'Asia/Yakutsk',
|
||||
'AUS Central' => 'Australia/Darwin',
|
||||
'Cen. Australia' => 'Australia/Adelaide',
|
||||
'AUS Eastern' => 'Australia/Sydney',
|
||||
'E. Australia' => 'Australia/Brisbane',
|
||||
'Tasmania' => 'Australia/Hobart',
|
||||
'Vladivostok' => 'Asia/Vladivostok',
|
||||
'West Pacific' => 'Pacific/Guam',
|
||||
'Central Pacific' => 'Asia/Magadan',
|
||||
'Fiji' => 'Pacific/Fiji',
|
||||
'New Zealand' => 'Pacific/Auckland',
|
||||
'Tonga' => 'Pacific/Tongatapu',
|
||||
);
|
||||
|
||||
/**
|
||||
* List of microsoft exchange timezone ids.
|
||||
*
|
||||
* Source: http://msdn.microsoft.com/en-us/library/aa563018(loband).aspx
|
||||
*/
|
||||
public static $microsoftExchangeMap = array(
|
||||
0 => 'UTC',
|
||||
31 => 'Africa/Casablanca',
|
||||
|
||||
// Insanely, id #2 is used for both Europe/Lisbon, and Europe/Sarajevo.
|
||||
// I'm not even kidding.. We handle this special case in the
|
||||
// getTimeZone method.
|
||||
2 => 'Europe/Lisbon',
|
||||
1 => 'Europe/London',
|
||||
4 => 'Europe/Berlin',
|
||||
6 => 'Europe/Prague',
|
||||
3 => 'Europe/Paris',
|
||||
69 => 'Africa/Luanda', // This was a best guess
|
||||
7 => 'Europe/Athens',
|
||||
5 => 'Europe/Bucharest',
|
||||
49 => 'Africa/Cairo',
|
||||
50 => 'Africa/Harare',
|
||||
59 => 'Europe/Helsinki',
|
||||
27 => 'Asia/Jerusalem',
|
||||
26 => 'Asia/Baghdad',
|
||||
74 => 'Asia/Kuwait',
|
||||
51 => 'Europe/Moscow',
|
||||
56 => 'Africa/Nairobi',
|
||||
25 => 'Asia/Tehran',
|
||||
24 => 'Asia/Muscat', // Best guess
|
||||
54 => 'Asia/Baku',
|
||||
48 => 'Asia/Kabul',
|
||||
58 => 'Asia/Yekaterinburg',
|
||||
47 => 'Asia/Karachi',
|
||||
23 => 'Asia/Calcutta',
|
||||
62 => 'Asia/Kathmandu',
|
||||
46 => 'Asia/Almaty',
|
||||
71 => 'Asia/Dhaka',
|
||||
66 => 'Asia/Colombo',
|
||||
61 => 'Asia/Rangoon',
|
||||
22 => 'Asia/Bangkok',
|
||||
64 => 'Asia/Krasnoyarsk',
|
||||
45 => 'Asia/Shanghai',
|
||||
63 => 'Asia/Irkutsk',
|
||||
21 => 'Asia/Singapore',
|
||||
73 => 'Australia/Perth',
|
||||
75 => 'Asia/Taipei',
|
||||
20 => 'Asia/Tokyo',
|
||||
72 => 'Asia/Seoul',
|
||||
70 => 'Asia/Yakutsk',
|
||||
19 => 'Australia/Adelaide',
|
||||
44 => 'Australia/Darwin',
|
||||
18 => 'Australia/Brisbane',
|
||||
76 => 'Australia/Sydney',
|
||||
43 => 'Pacific/Guam',
|
||||
42 => 'Australia/Hobart',
|
||||
68 => 'Asia/Vladivostok',
|
||||
41 => 'Asia/Magadan',
|
||||
17 => 'Pacific/Auckland',
|
||||
40 => 'Pacific/Fiji',
|
||||
67 => 'Pacific/Tongatapu',
|
||||
29 => 'Atlantic/Azores',
|
||||
53 => 'Atlantic/Cape_Verde',
|
||||
30 => 'America/Noronha',
|
||||
8 => 'America/Sao_Paulo', // Best guess
|
||||
32 => 'America/Argentina/Buenos_Aires',
|
||||
60 => 'America/Godthab',
|
||||
28 => 'America/St_Johns',
|
||||
9 => 'America/Halifax',
|
||||
33 => 'America/Caracas',
|
||||
65 => 'America/Santiago',
|
||||
35 => 'America/Bogota',
|
||||
10 => 'America/New_York',
|
||||
34 => 'America/Indiana/Indianapolis',
|
||||
55 => 'America/Guatemala',
|
||||
11 => 'America/Chicago',
|
||||
37 => 'America/Mexico_City',
|
||||
36 => 'America/Edmonton',
|
||||
38 => 'America/Phoenix',
|
||||
12 => 'America/Denver', // Best guess
|
||||
13 => 'America/Los_Angeles', // Best guess
|
||||
14 => 'America/Anchorage',
|
||||
15 => 'Pacific/Honolulu',
|
||||
16 => 'Pacific/Midway',
|
||||
39 => 'Pacific/Kwajalein',
|
||||
);
|
||||
|
||||
/**
|
||||
* This method will try to find out the correct timezone for an iCalendar
|
||||
* date-time value.
|
||||
*
|
||||
* You must pass the contents of the TZID parameter, as well as the full
|
||||
* calendar.
|
||||
*
|
||||
* If the lookup fails, this method will return the default PHP timezone
|
||||
* (as configured using date_default_timezone_set, or the date.timezone ini
|
||||
* setting).
|
||||
*
|
||||
* Alternatively, if $failIfUncertain is set to true, it will throw an
|
||||
* exception if we cannot accurately determine the timezone.
|
||||
*
|
||||
* @param string $tzid
|
||||
* @param Sabre\VObject\Component $vcalendar
|
||||
* @return DateTimeZone
|
||||
*/
|
||||
static public function getTimeZone($tzid, Component $vcalendar = null, $failIfUncertain = false) {
|
||||
|
||||
// First we will just see if the tzid is a support timezone identifier.
|
||||
try {
|
||||
return new \DateTimeZone($tzid);
|
||||
} catch (\Exception $e) {
|
||||
}
|
||||
|
||||
// Next, we check if the tzid is somewhere in our tzid map.
|
||||
if (isset(self::$map[$tzid])) {
|
||||
return new \DateTimeZone(self::$map[$tzid]);
|
||||
}
|
||||
|
||||
// Maybe the author was hyper-lazy and just included an offset. We
|
||||
// support it, but we aren't happy about it.
|
||||
if (preg_match('/^GMT(\+|-)([0-9]{4})$/', $tzid, $matches)) {
|
||||
return new \DateTimeZone('Etc/GMT' . $matches[1] . ltrim(substr($matches[2],0,2),'0'));
|
||||
}
|
||||
|
||||
if ($vcalendar) {
|
||||
|
||||
// If that didn't work, we will scan VTIMEZONE objects
|
||||
foreach($vcalendar->select('VTIMEZONE') as $vtimezone) {
|
||||
|
||||
if ((string)$vtimezone->TZID === $tzid) {
|
||||
|
||||
// Some clients add 'X-LIC-LOCATION' with the olson name.
|
||||
if (isset($vtimezone->{'X-LIC-LOCATION'})) {
|
||||
|
||||
$lic = (string)$vtimezone->{'X-LIC-LOCATION'};
|
||||
|
||||
// Libical generators may specify strings like
|
||||
// "SystemV/EST5EDT". For those we must remove the
|
||||
// SystemV part.
|
||||
if (substr($lic,0,8)==='SystemV/') {
|
||||
$lic = substr($lic,8);
|
||||
}
|
||||
|
||||
try {
|
||||
return new \DateTimeZone($lic);
|
||||
} catch (\Exception $e) {
|
||||
}
|
||||
|
||||
}
|
||||
// Microsoft may add a magic number, which we also have an
|
||||
// answer for.
|
||||
if (isset($vtimezone->{'X-MICROSOFT-CDO-TZID'})) {
|
||||
$cdoId = (int)$vtimezone->{'X-MICROSOFT-CDO-TZID'}->value;
|
||||
|
||||
// 2 can mean both Europe/Lisbon and Europe/Sarajevo.
|
||||
if ($cdoId===2 && strpos((string)$vtimezone->TZID, 'Sarajevo')!==false) {
|
||||
return new \DateTimeZone('Europe/Sarajevo');
|
||||
}
|
||||
|
||||
if (isset(self::$microsoftExchangeMap[$cdoId])) {
|
||||
return new \DateTimeZone(self::$microsoftExchangeMap[$cdoId]);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if ($failIfUncertain) {
|
||||
throw new \InvalidArgumentException('We were unable to determine the correct PHP timezone for tzid: ' . $tzid);
|
||||
}
|
||||
|
||||
// If we got all the way here, we default to UTC.
|
||||
return new \DateTimeZone(date_default_timezone_get());
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -1,24 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Sabre\VObject;
|
||||
|
||||
/**
|
||||
* This class contains the version number for the VObject package
|
||||
*
|
||||
* @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 Version {
|
||||
|
||||
/**
|
||||
* Full version number
|
||||
*/
|
||||
const VERSION = '2.1.3';
|
||||
|
||||
/**
|
||||
* Stability : alpha, beta, stable
|
||||
*/
|
||||
const STABILITY = 'stable';
|
||||
|
||||
}
|
|
@ -1,41 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Includes file
|
||||
*
|
||||
* This file includes the entire VObject library in one go.
|
||||
* The benefit is that an autoloader is not needed, which is often faster.
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
|
||||
// Begin includes
|
||||
include __DIR__ . '/DateTimeParser.php';
|
||||
include __DIR__ . '/ElementList.php';
|
||||
include __DIR__ . '/FreeBusyGenerator.php';
|
||||
include __DIR__ . '/Node.php';
|
||||
include __DIR__ . '/Parameter.php';
|
||||
include __DIR__ . '/ParseException.php';
|
||||
include __DIR__ . '/Property.php';
|
||||
include __DIR__ . '/Reader.php';
|
||||
include __DIR__ . '/RecurrenceIterator.php';
|
||||
include __DIR__ . '/Splitter/SplitterInterface.php';
|
||||
include __DIR__ . '/StringUtil.php';
|
||||
include __DIR__ . '/TimeZoneUtil.php';
|
||||
include __DIR__ . '/Version.php';
|
||||
include __DIR__ . '/Splitter/VCard.php';
|
||||
include __DIR__ . '/Component.php';
|
||||
include __DIR__ . '/Document.php';
|
||||
include __DIR__ . '/Property/Compound.php';
|
||||
include __DIR__ . '/Property/DateTime.php';
|
||||
include __DIR__ . '/Property/MultiDateTime.php';
|
||||
include __DIR__ . '/Splitter/ICalendar.php';
|
||||
include __DIR__ . '/Component/VAlarm.php';
|
||||
include __DIR__ . '/Component/VCalendar.php';
|
||||
include __DIR__ . '/Component/VEvent.php';
|
||||
include __DIR__ . '/Component/VFreeBusy.php';
|
||||
include __DIR__ . '/Component/VJournal.php';
|
||||
include __DIR__ . '/Component/VTodo.php';
|
||||
// End includes
|
|
@ -1,12 +0,0 @@
|
|||
#!/bin/sh
|
||||
|
||||
# Download and install the Sabre\Vobject library for this plugin
|
||||
|
||||
wget 'https://github.com/fruux/sabre-vobject/archive/2.1.0.tar.gz' -O sabre-vobject-2.1.0.tar.gz
|
||||
tar xf sabre-vobject-2.1.0.tar.gz
|
||||
|
||||
mv sabre-vobject-2.1.0/lib/* .
|
||||
rm -rf sabre-vobject-2.1.0
|
||||
|
||||
cd lib/Sabre/VObject && wget --no-check-certificate -O Property.php https://raw2.github.com/thomascube/sabre-vobject/84b64c65f9a94f7ec5a5e327bab3cc1335dd613c/lib/Sabre/VObject/Property.php
|
||||
|
Binary file not shown.
|
@ -22,19 +22,12 @@
|
|||
*/
|
||||
|
||||
use \Sabre\VObject;
|
||||
|
||||
// load Sabre\VObject classes
|
||||
if (!class_exists('\Sabre\VObject\Reader')) {
|
||||
require_once __DIR__ . '/lib/Sabre/VObject/includes.php';
|
||||
}
|
||||
use \Sabre\VObject\DateTimeParser;
|
||||
|
||||
/**
|
||||
* Class to parse and build vCalendar (iCalendar) files
|
||||
*
|
||||
* Uses the SabreTooth VObject library, version 2.1.
|
||||
*
|
||||
* Download from https://github.com/fruux/sabre-vobject/archive/2.1.0.zip
|
||||
* and place the lib files in this plugin's lib directory
|
||||
* Uses the Sabre VObject library, version 3.x.
|
||||
*
|
||||
*/
|
||||
class libvcalendar implements Iterator
|
||||
|
@ -43,8 +36,16 @@ class libvcalendar implements Iterator
|
|||
private $attach_uri = null;
|
||||
private $prodid = '-//Roundcube libcalendaring//Sabre//Sabre VObject//EN';
|
||||
private $type_component_map = array('event' => 'VEVENT', 'task' => 'VTODO');
|
||||
private $attendee_keymap = array('name' => 'CN', 'status' => 'PARTSTAT', 'role' => 'ROLE',
|
||||
'cutype' => 'CUTYPE', 'rsvp' => 'RSVP', 'delegated-from' => 'DELEGATED-FROM', 'delegated-to' => 'DELEGATED-TO');
|
||||
private $attendee_keymap = array(
|
||||
'name' => 'CN',
|
||||
'status' => 'PARTSTAT',
|
||||
'role' => 'ROLE',
|
||||
'cutype' => 'CUTYPE',
|
||||
'rsvp' => 'RSVP',
|
||||
'delegated-from' => 'DELEGATED-FROM',
|
||||
'delegated-to' => 'DELEGATED-TO',
|
||||
'schedule-status' => 'SCHEDULE-STATUS',
|
||||
);
|
||||
private $iteratorkey = 0;
|
||||
private $charset;
|
||||
private $forward_exceptions;
|
||||
|
@ -311,7 +312,7 @@ class libvcalendar implements Iterator
|
|||
$this->method = strval($vobject->METHOD);
|
||||
$this->agent = strval($vobject->PRODID);
|
||||
|
||||
foreach ($vobject->getBaseComponents() ?: $vobject->getComponents() as $ve) {
|
||||
foreach ($vobject->getComponents() as $ve) {
|
||||
if ($ve->name == 'VEVENT' || $ve->name == 'VTODO') {
|
||||
// convert to hash array representation
|
||||
$object = $this->_to_array($ve);
|
||||
|
@ -414,6 +415,8 @@ class libvcalendar implements Iterator
|
|||
if (!($prop instanceof VObject\Property))
|
||||
continue;
|
||||
|
||||
$value = strval($prop);
|
||||
|
||||
switch ($prop->name) {
|
||||
case 'DTSTART':
|
||||
case 'DTEND':
|
||||
|
@ -423,31 +426,30 @@ class libvcalendar implements Iterator
|
|||
break;
|
||||
|
||||
case 'TRANSP':
|
||||
$event['free_busy'] = $prop->value == 'TRANSPARENT' ? 'free' : 'busy';
|
||||
$event['free_busy'] = strval($prop) == 'TRANSPARENT' ? 'free' : 'busy';
|
||||
break;
|
||||
|
||||
case 'STATUS':
|
||||
if ($prop->value == 'TENTATIVE')
|
||||
if ($value == 'TENTATIVE')
|
||||
$event['free_busy'] = 'tentative';
|
||||
else if ($prop->value == 'CANCELLED')
|
||||
else if ($value == 'CANCELLED')
|
||||
$event['cancelled'] = true;
|
||||
else if ($prop->value == 'COMPLETED')
|
||||
else if ($value == 'COMPLETED')
|
||||
$event['complete'] = 100;
|
||||
|
||||
$event['status'] = strval($prop->value);
|
||||
$event['status'] = $value;
|
||||
break;
|
||||
|
||||
case 'PRIORITY':
|
||||
if (is_numeric($prop->value))
|
||||
$event['priority'] = $prop->value;
|
||||
if (is_numeric($value))
|
||||
$event['priority'] = $value;
|
||||
break;
|
||||
|
||||
case 'RRULE':
|
||||
$params = is_array($event['recurrence']) ? $event['recurrence'] : array();
|
||||
// parse recurrence rule attributes
|
||||
foreach (explode(';', $prop->value) as $par) {
|
||||
list($k, $v) = explode('=', $par);
|
||||
$params[$k] = $v;
|
||||
foreach ($prop->getParts() as $k => $v) {
|
||||
$params[strtoupper($k)] = $v;
|
||||
}
|
||||
if ($params['UNTIL'])
|
||||
$params['UNTIL'] = date_create($params['UNTIL']);
|
||||
|
@ -458,13 +460,17 @@ class libvcalendar implements Iterator
|
|||
break;
|
||||
|
||||
case 'EXDATE':
|
||||
if (!empty($prop->value))
|
||||
$event['recurrence']['EXDATE'] = array_merge((array)$event['recurrence']['EXDATE'], self::convert_datetime($prop, true));
|
||||
if (!empty($value)) {
|
||||
$exdates = array_map(function($_) { return is_array($_) ? $_[0] : $_; }, self::convert_datetime($prop, true));
|
||||
$event['recurrence']['EXDATE'] = array_merge((array)$event['recurrence']['EXDATE'], $exdates);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'RDATE':
|
||||
if (!empty($prop->value))
|
||||
$event['recurrence']['RDATE'] = array_merge((array)$event['recurrence']['RDATE'], self::convert_datetime($prop, true));
|
||||
if (!empty($value)) {
|
||||
$rdates = array_map(function($_) { return is_array($_) ? $_[0] : $_; }, self::convert_datetime($prop, true));
|
||||
$event['recurrence']['RDATE'] = array_merge((array)$event['recurrence']['RDATE'], $rdates);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'RECURRENCE-ID':
|
||||
|
@ -477,16 +483,16 @@ class libvcalendar implements Iterator
|
|||
case 'RELATED-TO':
|
||||
$reltype = $prop->offsetGet('RELTYPE');
|
||||
if ($reltype == 'PARENT' || $reltype === null) {
|
||||
$event['parent_id'] = $prop->value;
|
||||
$event['parent_id'] = $value;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'SEQUENCE':
|
||||
$event['sequence'] = intval($prop->value);
|
||||
$event['sequence'] = intval($value);
|
||||
break;
|
||||
|
||||
case 'PERCENT-COMPLETE':
|
||||
$event['complete'] = intval($prop->value);
|
||||
$event['complete'] = intval($value);
|
||||
break;
|
||||
|
||||
case 'LOCATION':
|
||||
|
@ -503,27 +509,28 @@ class libvcalendar implements Iterator
|
|||
|
||||
case 'CLASS':
|
||||
case 'X-CALENDARSERVER-ACCESS':
|
||||
$event['sensitivity'] = strtolower($prop->value);
|
||||
$event['sensitivity'] = strtolower($value);
|
||||
break;
|
||||
|
||||
case 'X-MICROSOFT-CDO-BUSYSTATUS':
|
||||
if ($prop->value == 'OOF')
|
||||
if ($value == 'OOF')
|
||||
$event['free_busy'] = 'outofoffice';
|
||||
else if (in_array($prop->value, array('FREE', 'BUSY', 'TENTATIVE')))
|
||||
$event['free_busy'] = strtolower($prop->value);
|
||||
else if (in_array($value, array('FREE', 'BUSY', 'TENTATIVE')))
|
||||
$event['free_busy'] = strtolower($value);
|
||||
break;
|
||||
|
||||
case 'ATTENDEE':
|
||||
case 'ORGANIZER':
|
||||
$params = array('rsvp' => false);
|
||||
foreach ($prop->parameters as $param) {
|
||||
switch ($param->name) {
|
||||
case 'RSVP': $params[$param->name] = strtolower($param->value) == 'true'; break;
|
||||
default: $params[$param->name] = $param->value; break;
|
||||
foreach ($prop->parameters() as $pname => $pvalue) {
|
||||
switch ($pname) {
|
||||
case 'RSVP': $params[$pname] = strtolower($pvalue) == 'true'; break;
|
||||
case 'CN': $params[$pname] = self::unescape($pvalue); break;
|
||||
default: $params[$pname] = strval($pvalue); break;
|
||||
}
|
||||
}
|
||||
$attendee = self::map_keys($params, array_flip($this->attendee_keymap));
|
||||
$attendee['email'] = preg_replace('/^mailto:/i', '', $prop->value);
|
||||
$attendee['email'] = preg_replace('!^mailto:!i', '', $value);
|
||||
|
||||
if ($prop->name == 'ORGANIZER') {
|
||||
$attendee['role'] = 'ORGANIZER';
|
||||
|
@ -537,20 +544,20 @@ class libvcalendar implements Iterator
|
|||
|
||||
case 'ATTACH':
|
||||
$params = self::parameters_array($prop);
|
||||
if (substr($prop->value, 0, 4) == 'http' && !strpos($prop->value, ':attachment:')) {
|
||||
$event['links'][] = $prop->value;
|
||||
if (substr($value, 0, 4) == 'http' && !strpos($value, ':attachment:')) {
|
||||
$event['links'][] = $value;
|
||||
}
|
||||
else if (strlen($prop->value) && strtoupper($params['VALUE']) == 'BINARY') {
|
||||
else if (strlen($value) && strtoupper($params['VALUE']) == 'BINARY') {
|
||||
$attachment = self::map_keys($params, array('FMTTYPE' => 'mimetype', 'X-LABEL' => 'name'));
|
||||
$attachment['data'] = base64_decode($prop->value);
|
||||
$attachment['size'] = strlen($attachment['data']);
|
||||
$attachment['data'] = $value;
|
||||
$attachment['size'] = strlen($value);
|
||||
$event['attachments'][] = $attachment;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
if (substr($prop->name, 0, 2) == 'X-')
|
||||
$event['x-custom'][] = array($prop->name, strval($prop->value));
|
||||
$event['x-custom'][] = array($prop->name, strval($value));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -602,20 +609,20 @@ class libvcalendar implements Iterator
|
|||
$alarm = array();
|
||||
|
||||
foreach ($valarm->children as $prop) {
|
||||
$value = strval($prop);
|
||||
|
||||
switch ($prop->name) {
|
||||
case 'TRIGGER':
|
||||
foreach ($prop->parameters as $param) {
|
||||
if ($param->name == 'VALUE' && $param->value == 'DATE-TIME') {
|
||||
if ($prop['VALUE'] == 'DATE-TIME') {
|
||||
$trigger = '@' . $prop->getDateTime()->format('U');
|
||||
$alarm['trigger'] = $prop->getDateTime();
|
||||
}
|
||||
}
|
||||
if (!$trigger && ($values = libcalendaring::parse_alarm_value($prop->value))) {
|
||||
if (!$trigger && ($values = libcalendaring::parse_alarm_value($value))) {
|
||||
$trigger = $values[2];
|
||||
}
|
||||
|
||||
if (!$alarm['trigger']) {
|
||||
$alarm['trigger'] = rtrim(preg_replace('/([A-Z])0[WDHMS]/', '\\1', $prop->value), 'T');
|
||||
$alarm['trigger'] = rtrim(preg_replace('/([A-Z])0[WDHMS]/', '\\1', $value), 'T');
|
||||
// if all 0-values have been stripped, assume 'at time'
|
||||
if ($alarm['trigger'] == 'P')
|
||||
$alarm['trigger'] = 'PT0S';
|
||||
|
@ -623,7 +630,7 @@ class libvcalendar implements Iterator
|
|||
break;
|
||||
|
||||
case 'ACTION':
|
||||
$action = $alarm['action'] = strtoupper($prop->value);
|
||||
$action = $alarm['action'] = strtoupper($value);
|
||||
break;
|
||||
|
||||
case 'SUMMARY':
|
||||
|
@ -633,18 +640,18 @@ class libvcalendar implements Iterator
|
|||
break;
|
||||
|
||||
case 'REPEAT':
|
||||
$alarm['repeat'] = intval($prop->value);
|
||||
$alarm['repeat'] = intval($value);
|
||||
break;
|
||||
|
||||
case 'ATTENDEE':
|
||||
$alarm['attendees'][] = preg_replace('/^mailto:/i', '', $prop->value);
|
||||
$alarm['attendees'][] = preg_replace('!^mailto:!i', '', $value);
|
||||
break;
|
||||
|
||||
case 'ATTACH':
|
||||
$params = self::parameters_array($prop);
|
||||
if (strlen($prop->value) && (preg_match('/^[a-z]+:/', $prop->value) || strtoupper($params['VALUE']) == 'URI')) {
|
||||
if (strlen($value) && (preg_match('/^[a-z]+:/', $value) || strtoupper($params['VALUE']) == 'URI')) {
|
||||
// we only support URI-type of attachments here
|
||||
$alarm['uri'] = $prop->value;
|
||||
$alarm['uri'] = $value;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -676,6 +683,11 @@ class libvcalendar implements Iterator
|
|||
unset($event['end']);
|
||||
}
|
||||
|
||||
// some iTip CANCEL messages only contain the start date
|
||||
if (!$event['end'] && $event['start'] && $this->method == 'CANCEL') {
|
||||
$event['end'] = clone $event['start'];
|
||||
}
|
||||
|
||||
// minimal validation
|
||||
if (empty($event['uid']) || ($event['_type'] == 'event' && empty($event['start']) != empty($event['end']))) {
|
||||
throw new VObject\ParseException('Object validation failed: missing mandatory object properties');
|
||||
|
@ -696,6 +708,8 @@ class libvcalendar implements Iterator
|
|||
if (!($prop instanceof VObject\Property))
|
||||
continue;
|
||||
|
||||
$value = strval($prop);
|
||||
|
||||
switch ($prop->name) {
|
||||
case 'CREATED':
|
||||
case 'LAST-MODIFIED':
|
||||
|
@ -707,16 +721,16 @@ class libvcalendar implements Iterator
|
|||
break;
|
||||
|
||||
case 'ORGANIZER':
|
||||
$this->freebusy['organizer'] = preg_replace('/^mailto:/i', '', $prop->value);
|
||||
$this->freebusy['organizer'] = preg_replace('!^mailto:!i', '', $value);
|
||||
break;
|
||||
|
||||
case 'FREEBUSY':
|
||||
// The freebusy component can hold more than 1 value, separated by commas.
|
||||
$periods = explode(',', $prop->value);
|
||||
$periods = explode(',', $value);
|
||||
$fbtype = strval($prop['FBTYPE']) ?: 'BUSY';
|
||||
|
||||
// skip dupes
|
||||
if ($seen[$prop->value.':'.$fbtype]++)
|
||||
if ($seen[$value.':'.$fbtype]++)
|
||||
continue;
|
||||
|
||||
foreach ($periods as $period) {
|
||||
|
@ -725,8 +739,8 @@ class libvcalendar implements Iterator
|
|||
// duration (relative) value.
|
||||
list($busyStart, $busyEnd) = explode('/', $period);
|
||||
|
||||
$busyStart = VObject\DateTimeParser::parse($busyStart);
|
||||
$busyEnd = VObject\DateTimeParser::parse($busyEnd);
|
||||
$busyStart = DateTimeParser::parse($busyStart);
|
||||
$busyEnd = DateTimeParser::parse($busyEnd);
|
||||
if ($busyEnd instanceof \DateInterval) {
|
||||
$tmp = clone $busyStart;
|
||||
$tmp->add($busyEnd);
|
||||
|
@ -739,7 +753,7 @@ class libvcalendar implements Iterator
|
|||
break;
|
||||
|
||||
case 'COMMENT':
|
||||
$this->freebusy['comment'] = $prop->value;
|
||||
$this->freebusy['comment'] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -751,7 +765,15 @@ class libvcalendar implements Iterator
|
|||
*/
|
||||
public static function convert_string($prop)
|
||||
{
|
||||
return str_replace('\,', ',', strval($prop->value));
|
||||
return strval($prop);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public static function unescape($prop)
|
||||
{
|
||||
return str_replace('\,', ',', strval($prop));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -762,44 +784,47 @@ class libvcalendar implements Iterator
|
|||
if (empty($prop)) {
|
||||
return $as_array ? array() : null;
|
||||
}
|
||||
else if ($prop instanceof VObject\Property\MultiDateTime) {
|
||||
|
||||
else if ($prop instanceof VObject\Property\iCalendar\DateTime) {
|
||||
if (count($prop->getDateTimes()) > 1) {
|
||||
$dt = array();
|
||||
$dateonly = ($prop->getDateType() & VObject\Property\DateTime::DATE);
|
||||
$dateonly = !$prop->hasTime();
|
||||
foreach ($prop->getDateTimes() as $item) {
|
||||
$item->_dateonly = $dateonly;
|
||||
$dt[] = $item;
|
||||
}
|
||||
}
|
||||
else if ($prop instanceof VObject\Property\DateTime) {
|
||||
else {
|
||||
$dt = $prop->getDateTime();
|
||||
if ($prop->getDateType() & VObject\Property\DateTime::DATE) {
|
||||
if (!$prop->hasTime()) {
|
||||
$dt->_dateonly = true;
|
||||
}
|
||||
}
|
||||
else if ($prop instanceof VObject\Property && ($prop['VALUE'] == 'DATE' || $prop['VALUE'] == 'DATE-TIME')) {
|
||||
try {
|
||||
list($type, $dt) = VObject\Property\DateTime::parseData($prop->value, $prop);
|
||||
$dt->_dateonly = ($type & VObject\Property\DateTime::DATE);
|
||||
}
|
||||
catch (Exception $e) {
|
||||
// ignore date parse errors
|
||||
}
|
||||
}
|
||||
else if ($prop instanceof VObject\Property && $prop['VALUE'] == 'PERIOD') {
|
||||
else if ($prop instanceof VObject\Property\iCalendar\Period) {
|
||||
$dt = array();
|
||||
foreach(explode(',', $prop->value) as $val) {
|
||||
foreach ($prop->getParts() as $val) {
|
||||
try {
|
||||
list($start, $end) = explode('/', $val);
|
||||
list($type, $item) = VObject\Property\DateTime::parseData($start, $prop);
|
||||
$item->_dateonly = ($type & VObject\Property\DateTime::DATE);
|
||||
$dt[] = $item;
|
||||
$start = DateTimeParser::parseDateTime($start);
|
||||
|
||||
// This is a duration value.
|
||||
if ($end[0] === 'P') {
|
||||
$dur = DateTimeParser::parseDuration($end);
|
||||
$end = clone $start;
|
||||
$end->add($dur);
|
||||
}
|
||||
else {
|
||||
$end = DateTimeParser::parseDateTime($end);
|
||||
}
|
||||
$dt[] = array($start, $end);
|
||||
}
|
||||
catch (Exception $e) {
|
||||
// ignore single date parse errors
|
||||
}
|
||||
}
|
||||
}
|
||||
else if ($prop instanceof DateTime) {
|
||||
else if ($prop instanceof \DateTime) {
|
||||
$dt = $prop;
|
||||
}
|
||||
|
||||
|
@ -815,16 +840,30 @@ class libvcalendar implements Iterator
|
|||
/**
|
||||
* Create a Sabre\VObject\Property instance from a PHP DateTime object
|
||||
*
|
||||
* @param object VObject\Document parent node to create property for
|
||||
* @param string Property name
|
||||
* @param object DateTime
|
||||
* @param boolean Set as UTC date
|
||||
* @param boolean Set as VALUE=DATE property
|
||||
*/
|
||||
public function datetime_prop($name, $dt, $utc = false, $dateonly = null)
|
||||
public function datetime_prop($cal, $name, $dt, $utc = false, $dateonly = null, $set_type = false)
|
||||
{
|
||||
$is_utc = $utc || (($tz = $dt->getTimezone()) && in_array($tz->getName(), array('UTC','GMT','Z')));
|
||||
if ($utc) {
|
||||
$dt->setTimeZone(new \DateTimeZone('UTC'));
|
||||
$is_utc = true;
|
||||
}
|
||||
else {
|
||||
$is_utc = ($tz = $dt->getTimezone()) && in_array($tz->getName(), array('UTC','GMT','Z'));
|
||||
}
|
||||
$is_dateonly = $dateonly === null ? (bool)$dt->_dateonly : (bool)$dateonly;
|
||||
$vdt = new VObject\Property\DateTime($name);
|
||||
$vdt->setDateTime($dt, $is_dateonly ? VObject\Property\DateTime::DATE :
|
||||
($is_utc ? VObject\Property\DateTime::UTC : VObject\Property\DateTime::LOCALTZ));
|
||||
$vdt = $cal->createProperty($name, $dt, null, $is_dateonly ? 'DATE' : 'DATE-TIME');
|
||||
|
||||
if ($is_dateonly) {
|
||||
$vdt['VALUE'] = 'DATE';
|
||||
}
|
||||
else if ($set_type) {
|
||||
$vdt['VALUE'] = 'DATE-TIME';
|
||||
}
|
||||
|
||||
// register timezone for VTIMEZONE block
|
||||
if (!$is_utc && !$dateonly && $tz && ($tzname = $tz->getName())) {
|
||||
|
@ -860,8 +899,8 @@ class libvcalendar implements Iterator
|
|||
private static function parameters_array($prop)
|
||||
{
|
||||
$params = array();
|
||||
foreach ($prop->parameters as $param) {
|
||||
$params[strtoupper($param->name)] = $param->value;
|
||||
foreach ($prop->parameters() as $name => $value) {
|
||||
$params[strtoupper($name)] = $value;
|
||||
}
|
||||
return $params;
|
||||
}
|
||||
|
@ -882,10 +921,10 @@ class libvcalendar implements Iterator
|
|||
$this->method = $method;
|
||||
|
||||
// encapsulate in VCALENDAR container
|
||||
$vcal = VObject\Component::create('VCALENDAR');
|
||||
$vcal->version = '2.0';
|
||||
$vcal->prodid = $this->prodid;
|
||||
$vcal->calscale = 'GREGORIAN';
|
||||
$vcal = new VObject\Component\VCalendar();
|
||||
$vcal->VERSION = '2.0';
|
||||
$vcal->PRODID = $this->prodid;
|
||||
$vcal->CALSCALE = 'GREGORIAN';
|
||||
|
||||
if (!empty($method)) {
|
||||
$vcal->METHOD = $method;
|
||||
|
@ -903,7 +942,7 @@ class libvcalendar implements Iterator
|
|||
// include timezone information
|
||||
if ($with_timezones || !empty($method)) {
|
||||
foreach ($this->vtimezones as $tzid => $range) {
|
||||
$vt = self::get_vtimezone($tzid, $range[0], $range[1]);
|
||||
$vt = self::get_vtimezone($tzid, $range[0], $range[1], $vcal);
|
||||
if (empty($vt)) {
|
||||
continue; // no timezone information found
|
||||
}
|
||||
|
@ -937,12 +976,14 @@ class libvcalendar implements Iterator
|
|||
private function _to_ical($event, $vcal, $get_attachment, $recurrence_id = null)
|
||||
{
|
||||
$type = $event['_type'] ?: 'event';
|
||||
$ve = VObject\Component::create($this->type_component_map[$type]);
|
||||
$ve->add('UID', $event['uid']);
|
||||
|
||||
$cal = $vcal ?: new VObject\Component\VCalendar();
|
||||
$ve = $cal->create($this->type_component_map[$type]);
|
||||
$ve->UID = $event['uid'];
|
||||
|
||||
// set DTSTAMP according to RFC 5545, 3.8.7.2.
|
||||
$dtstamp = !empty($event['changed']) && !empty($this->method) ? $event['changed'] : new DateTime();
|
||||
$ve->add($this->datetime_prop('DTSTAMP', $dtstamp, true));
|
||||
$dtstamp = !empty($event['changed']) && !empty($this->method) ? $event['changed'] : new DateTime('now', new \DateTimeZone('UTC'));
|
||||
$ve->DTSTAMP = $dtstamp;
|
||||
|
||||
// all-day events end the next day
|
||||
if ($event['allday'] && !empty($event['end'])) {
|
||||
|
@ -951,30 +992,31 @@ class libvcalendar implements Iterator
|
|||
$event['end']->_dateonly = true;
|
||||
}
|
||||
if (!empty($event['created']))
|
||||
$ve->add($this->datetime_prop('CREATED', $event['created'], true));
|
||||
$ve->add($this->datetime_prop($cal, 'CREATED', $event['created'], true));
|
||||
if (!empty($event['changed']))
|
||||
$ve->add($this->datetime_prop('LAST-MODIFIED', $event['changed'], true));
|
||||
$ve->add($this->datetime_prop($cal, 'LAST-MODIFIED', $event['changed'], true));
|
||||
if (!empty($event['start']))
|
||||
$ve->add($this->datetime_prop('DTSTART', $event['start'], false, (bool)$event['allday']));
|
||||
$ve->add($this->datetime_prop($cal, 'DTSTART', $event['start'], false, (bool)$event['allday']));
|
||||
if (!empty($event['end']))
|
||||
$ve->add($this->datetime_prop('DTEND', $event['end'], false, (bool)$event['allday']));
|
||||
$ve->add($this->datetime_prop($cal, 'DTEND', $event['end'], false, (bool)$event['allday']));
|
||||
if (!empty($event['due']))
|
||||
$ve->add($this->datetime_prop('DUE', $event['due'], false));
|
||||
$ve->add($this->datetime_prop($cal, 'DUE', $event['due'], false));
|
||||
|
||||
// we're exporting a recurrence instance only
|
||||
if (!$recurrence_id && $event['recurrence_date'] && $event['recurrence_date'] instanceof DateTime) {
|
||||
$recurrence_id = $this->datetime_prop('RECURRENCE-ID', $event['recurrence_date'], false, (bool)$event['allday']);
|
||||
$recurrence_id = $this->datetime_prop($cal, 'RECURRENCE-ID', $event['recurrence_date'], false, (bool)$event['allday']);
|
||||
if ($event['thisandfuture'])
|
||||
$recurrence_id->add('RANGE', 'THISANDFUTURE');
|
||||
}
|
||||
|
||||
if ($recurrence_id)
|
||||
if ($recurrence_id) {
|
||||
$ve->add($recurrence_id);
|
||||
}
|
||||
|
||||
$ve->add('SUMMARY', $event['title']);
|
||||
|
||||
if ($event['location'])
|
||||
$ve->add($this->is_apple() ? new vobject_location_property('LOCATION', $event['location']) : new VObject\Property('LOCATION', $event['location']));
|
||||
$ve->add($this->is_apple() ? new vobject_location_property($cal, 'LOCATION', $event['location']) : $cal->create('LOCATION', $event['location']));
|
||||
if ($event['description'])
|
||||
$ve->add('DESCRIPTION', strtr($event['description'], array("\r\n" => "\n", "\r" => "\n"))); // normalize line endings
|
||||
|
||||
|
@ -1003,21 +1045,20 @@ class libvcalendar implements Iterator
|
|||
$exd = clone $event['start'];
|
||||
$exd->setDate($ex->format('Y'), $ex->format('n'), $ex->format('j'));
|
||||
$exd->setTimeZone(new \DateTimeZone('UTC'));
|
||||
$ve->add(new VObject\Property('EXDATE', $exd->format('Ymd\\THis\\Z')));
|
||||
$ve->add($this->datetime_prop($cal, 'EXDATE', $exd, true));
|
||||
}
|
||||
}
|
||||
}
|
||||
// add RDATEs
|
||||
if (is_array($rdates) && !empty($rdates)) {
|
||||
$sample = $this->datetime_prop('RDATE', $rdates[0]);
|
||||
$rdprop = new VObject\Property\MultiDateTime('RDATE', null);
|
||||
$rdprop->setDateTimes($rdates, $sample->getDateType());
|
||||
$ve->add($rdprop);
|
||||
if (!empty($rdates)) {
|
||||
foreach ((array)$rdates as $rdate) {
|
||||
$ve->add($this->datetime_prop($cal, 'RDATE', $rdate));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($event['categories']) {
|
||||
$cat = VObject\Property::create('CATEGORIES');
|
||||
$cat = $cal->create('CATEGORIES');
|
||||
$cat->setParts((array)$event['categories']);
|
||||
$ve->add($cat);
|
||||
}
|
||||
|
@ -1050,15 +1091,15 @@ class libvcalendar implements Iterator
|
|||
$ve->add('PERCENT-COMPLETE', intval($event['complete']));
|
||||
// Apple iCal required the COMPLETED date to be set in order to consider a task complete
|
||||
if ($event['complete'] == 100)
|
||||
$ve->add($this->datetime_prop('COMPLETED', $event['changed'] ?: new DateTime('now - 1 hour'), true));
|
||||
$ve->add($this->datetime_prop($cal, 'COMPLETED', $event['changed'] ?: new DateTime('now - 1 hour'), true));
|
||||
}
|
||||
|
||||
if ($event['valarms']) {
|
||||
foreach ($event['valarms'] as $alarm) {
|
||||
$va = VObject\Component::create('VALARM');
|
||||
$va = $cal->createComponent('VALARM');
|
||||
$va->action = $alarm['action'];
|
||||
if ($alarm['trigger'] instanceof DateTime) {
|
||||
$va->add($this->datetime_prop('TRIGGER', $alarm['trigger'], true));
|
||||
$va->add($this->datetime_prop($cal, 'TRIGGER', $alarm['trigger'], true, null, true));
|
||||
}
|
||||
else {
|
||||
$va->add('TRIGGER', $alarm['trigger']);
|
||||
|
@ -1087,13 +1128,13 @@ class libvcalendar implements Iterator
|
|||
}
|
||||
// legacy support
|
||||
else if ($event['alarms']) {
|
||||
$va = VObject\Component::create('VALARM');
|
||||
$va = $cal->createComponent('VALARM');
|
||||
list($trigger, $va->action) = explode(':', $event['alarms']);
|
||||
$val = libcalendaring::parse_alarm_value($trigger);
|
||||
if ($val[3])
|
||||
$va->add('TRIGGER', $val[3]);
|
||||
else if ($val[0] instanceof DateTime)
|
||||
$va->add($this->datetime_prop('TRIGGER', $val[0]));
|
||||
$va->add($this->datetime_prop($cal, 'TRIGGER', $val[0], true, null, true));
|
||||
$ve->add($va);
|
||||
}
|
||||
|
||||
|
@ -1105,12 +1146,14 @@ class libvcalendar implements Iterator
|
|||
else if (!empty($attendee['email'])) {
|
||||
if (isset($attendee['rsvp']))
|
||||
$attendee['rsvp'] = $attendee['rsvp'] ? 'TRUE' : null;
|
||||
$ve->add('ATTENDEE', 'mailto:' . $attendee['email'], array_filter(self::map_keys($attendee, $this->attendee_keymap)));
|
||||
$ve->add('ATTENDEE', 'mailto:' . $attendee['email'],
|
||||
array_filter(self::map_keys($attendee, $this->attendee_keymap)));
|
||||
}
|
||||
}
|
||||
|
||||
if ($event['organizer']) {
|
||||
$ve->add('ORGANIZER', 'mailto:' . $event['organizer']['email'], self::map_keys($event['organizer'], array('name' => 'CN')));
|
||||
$ve->add('ORGANIZER', 'mailto:' . $event['organizer']['email'],
|
||||
array_filter(self::map_keys($event['organizer'], array('name' => 'CN'))));
|
||||
}
|
||||
|
||||
foreach ((array)$event['url'] as $url) {
|
||||
|
@ -1141,7 +1184,7 @@ class libvcalendar implements Iterator
|
|||
if (is_callable($get_attachment) && ($data = call_user_func($get_attachment, $attach['id'], $event))) {
|
||||
// embed attachments for iCal
|
||||
$ve->add('ATTACH',
|
||||
base64_encode($data),
|
||||
$data,
|
||||
array_filter(array('VALUE' => 'BINARY', 'ENCODING' => 'BASE64', 'FMTTYPE' => $attach['mimetype'], 'X-LABEL' => $attach['name'])));
|
||||
unset($data); // attempt to free memory
|
||||
}
|
||||
|
@ -1179,7 +1222,7 @@ class libvcalendar implements Iterator
|
|||
if (is_array($event['recurrence']) && $event['recurrence']['EXCEPTIONS']) {
|
||||
foreach ($event['recurrence']['EXCEPTIONS'] as $ex) {
|
||||
$exdate = $ex['recurrence_date'] ?: $ex['start'];
|
||||
$recurrence_id = $this->datetime_prop('RECURRENCE-ID', $exdate, false, (bool)$event['allday']);
|
||||
$recurrence_id = $this->datetime_prop($cal, 'RECURRENCE-ID', $exdate, false, (bool)$event['allday']);
|
||||
if ($ex['thisandfuture'])
|
||||
$recurrence_id->add('RANGE', 'THISANDFUTURE');
|
||||
$this->_to_ical($ex, $vcal, $get_attachment, $recurrence_id);
|
||||
|
@ -1198,10 +1241,11 @@ class libvcalendar implements Iterator
|
|||
* @return mixed A Sabre\VObject\Component object representing a VTIMEZONE definition
|
||||
* or false if no timezone information is available
|
||||
*/
|
||||
public static function get_vtimezone($tzid, $from = 0, $to = 0)
|
||||
public static function get_vtimezone($tzid, $from = 0, $to = 0, $cal = null)
|
||||
{
|
||||
if (!$from) $from = time();
|
||||
if (!$to) $to = $from;
|
||||
if (!$cal) $cal = new VObject\Component\VCalendar();
|
||||
|
||||
if (is_string($tzid)) {
|
||||
try {
|
||||
|
@ -1222,7 +1266,7 @@ class libvcalendar implements Iterator
|
|||
$year = 86400 * 360;
|
||||
$transitions = $tz->getTransitions($from - $year, $to + $year);
|
||||
|
||||
$vt = new VObject\Component('VTIMEZONE');
|
||||
$vt = $cal->createComponent('VTIMEZONE');
|
||||
$vt->TZID = $tz->getName();
|
||||
|
||||
$std = null; $dst = null;
|
||||
|
@ -1236,12 +1280,12 @@ class libvcalendar implements Iterator
|
|||
|
||||
if ($trans['isdst']) {
|
||||
$t_dst = $trans['ts'];
|
||||
$dst = new VObject\Component('DAYLIGHT');
|
||||
$dst = $cal->createComponent('DAYLIGHT');
|
||||
$cmp = $dst;
|
||||
}
|
||||
else {
|
||||
$t_std = $trans['ts'];
|
||||
$std = new VObject\Component('STANDARD');
|
||||
$std = $cal->createComponent('STANDARD');
|
||||
$cmp = $std;
|
||||
}
|
||||
|
||||
|
@ -1315,48 +1359,25 @@ class libvcalendar implements Iterator
|
|||
|
||||
|
||||
/**
|
||||
* Override Sabre\VObject\Property that quotes commas in the location property
|
||||
* Override Sabre\VObject\Property\Text that quotes commas in the location property
|
||||
* because Apple clients treat that property as list.
|
||||
*/
|
||||
class vobject_location_property extends VObject\Property
|
||||
class vobject_location_property extends VObject\Property\Text
|
||||
{
|
||||
/**
|
||||
* Turns the object back into a serialized blob.
|
||||
* List of properties that are considered 'structured'.
|
||||
*
|
||||
* @return string
|
||||
* @var array
|
||||
*/
|
||||
public function serialize()
|
||||
{
|
||||
$str = $this->name;
|
||||
|
||||
foreach ($this->parameters as $param) {
|
||||
$str.=';' . $param->serialize();
|
||||
}
|
||||
|
||||
$src = array(
|
||||
'\\',
|
||||
"\n",
|
||||
',',
|
||||
protected $structuredValues = array(
|
||||
// vCard
|
||||
'N',
|
||||
'ADR',
|
||||
'ORG',
|
||||
'GENDER',
|
||||
'LOCATION',
|
||||
// iCalendar
|
||||
'REQUEST-STATUS',
|
||||
);
|
||||
$out = array(
|
||||
'\\\\',
|
||||
'\n',
|
||||
'\,',
|
||||
);
|
||||
$str.=':' . str_replace($src, $out, $this->value);
|
||||
|
||||
$out = '';
|
||||
while (strlen($str) > 0) {
|
||||
if (strlen($str) > 75) {
|
||||
$out.= mb_strcut($str, 0, 75, 'utf-8') . "\r\n";
|
||||
$str = ' ' . mb_strcut($str, 75, strlen($str), 'utf-8');
|
||||
} else {
|
||||
$out.= $str . "\r\n";
|
||||
$str = '';
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $out;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
*
|
||||
* @author Thomas Bruederli <bruederli@kolabsys.com>
|
||||
*
|
||||
* Copyright (C) 2013, Kolab Systems AG <contact@kolabsys.com>
|
||||
* Copyright (C) 2014, Kolab Systems AG <contact@kolabsys.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
|
@ -98,7 +98,7 @@ class libvcalendar_test extends PHPUnit_Framework_TestCase
|
|||
$event = $events[0];
|
||||
|
||||
$this->assertEquals(1, count($events), "Import event data");
|
||||
$this->assertFalse(array_key_exists('created', $event), "No created date field");
|
||||
$this->assertInstanceOf('DateTime', $event['created'], "Created date field");
|
||||
$this->assertFalse(array_key_exists('changed', $event), "No changed date field");
|
||||
}
|
||||
|
||||
|
@ -122,7 +122,7 @@ class libvcalendar_test extends PHPUnit_Framework_TestCase
|
|||
$this->assertEquals('REQUEST', $ical->method, "iTip method");
|
||||
|
||||
// attendees
|
||||
$this->assertEquals(2, count($event['attendees']), "Attendees list (including organizer)");
|
||||
$this->assertEquals(3, count($event['attendees']), "Attendees list (including organizer)");
|
||||
$organizer = $event['attendees'][0];
|
||||
$this->assertEquals('ORGANIZER', $organizer['role'], 'Organizer ROLE');
|
||||
$this->assertEquals('Rolf Test', $organizer['name'], 'Organizer name');
|
||||
|
@ -131,8 +131,17 @@ class libvcalendar_test extends PHPUnit_Framework_TestCase
|
|||
$this->assertEquals('REQ-PARTICIPANT', $attendee['role'], 'Attendee ROLE');
|
||||
$this->assertEquals('NEEDS-ACTION', $attendee['status'], 'Attendee STATUS');
|
||||
$this->assertEquals('rolf2@mykolab.com', $attendee['email'], 'Attendee mailto:');
|
||||
$this->assertEquals('carl@mykolab.com', $attendee['delegated-from'], 'Attendee delegated-from');
|
||||
$this->assertTrue($attendee['rsvp'], 'Attendee RSVP');
|
||||
|
||||
$delegator = $event['attendees'][2];
|
||||
$this->assertEquals('NON-PARTICIPANT', $delegator['role'], 'Delegator ROLE');
|
||||
$this->assertEquals('DELEGATED', $delegator['status'], 'Delegator STATUS');
|
||||
$this->assertEquals('INDIVIDUAL', $delegator['cutype'], 'Delegator CUTYPE');
|
||||
$this->assertEquals('carl@mykolab.com', $delegator['email'], 'Delegator mailto:');
|
||||
$this->assertEquals('rolf2@mykolab.com', $delegator['delegated-to'], 'Delegator delegated-to');
|
||||
$this->assertFalse($delegator['rsvp'], 'Delegator RSVP');
|
||||
|
||||
// attachments
|
||||
$this->assertEquals(1, count($event['attachments']), "Embedded attachments");
|
||||
$attachment = $event['attachments'][0];
|
||||
|
@ -154,6 +163,14 @@ class libvcalendar_test extends PHPUnit_Framework_TestCase
|
|||
$this->assertEquals(2, count($rrule['EXDATE']), "Recurrence EXDATEs");
|
||||
$this->assertInstanceOf('DateTime', $rrule['EXDATE'][0], "Recurrence EXDATE as DateTime");
|
||||
|
||||
$this->assertTrue(is_array($rrule['EXCEPTIONS']));
|
||||
$this->assertEquals(1, count($rrule['EXCEPTIONS']), "Recurrence Exceptions");
|
||||
|
||||
$exception = $rrule['EXCEPTIONS'][0];
|
||||
$this->assertEquals($event['uid'], $event['uid'], "Exception UID");
|
||||
$this->assertEquals('Recurring Test (Exception)', $exception['title'], "Exception title");
|
||||
$this->assertInstanceOf('DateTime', $exception['start'], "Exception start");
|
||||
|
||||
// categories, class
|
||||
$this->assertEquals('libcalendaring tests', join(',', (array)$event['categories']), "Event categories");
|
||||
$this->assertEquals('confidential', $event['sensitivity'], "Class/sensitivity = confidential");
|
||||
|
@ -201,13 +218,14 @@ class libvcalendar_test extends PHPUnit_Framework_TestCase
|
|||
$this->assertEquals('DISPLAY', $event['valarms'][0]['action'], "First alarm action");
|
||||
$this->assertEquals('This is the first event reminder', $event['valarms'][0]['description'], "First alarm text");
|
||||
|
||||
$this->assertEquals(2, count($event['valarms']), "List all VALARM blocks");
|
||||
$this->assertEquals(3, count($event['valarms']), "List all VALARM blocks");
|
||||
|
||||
$valarm = $event['valarms'][1];
|
||||
$this->assertEquals(1, count($valarm['attendees']), "Email alarm attendees");
|
||||
$this->assertEquals('EMAIL', $valarm['action'], "Second alarm item (action)");
|
||||
$this->assertEquals('-P1D', $valarm['trigger'], "Second alarm item (trigger)");
|
||||
$this->assertEquals('This is the reminder message', $valarm['summary'], "Email alarm text");
|
||||
$this->assertInstanceOf('DateTime', $event['valarms'][2]['trigger'], "Absolute trigger date/time");
|
||||
|
||||
// test alarms export
|
||||
$ics = $ical->export(array($event));
|
||||
|
@ -216,6 +234,7 @@ class libvcalendar_test extends PHPUnit_Framework_TestCase
|
|||
$this->assertContains('DESCRIPTION:This is the first event reminder', $ics, "Alarm description");
|
||||
$this->assertContains('SUMMARY:This is the reminder message', $ics, "Email alarm summary");
|
||||
$this->assertContains('ATTENDEE:mailto:reminder-recipient@example.org', $ics, "Email alarm recipient");
|
||||
$this->assertContains('TRIGGER;VALUE=DATE-TIME:20130812', $ics, "Date-Time trigger");
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -283,6 +302,7 @@ class libvcalendar_test extends PHPUnit_Framework_TestCase
|
|||
|
||||
$this->assertEquals(9, count($event['recurrence']['RDATE']));
|
||||
$this->assertInstanceOf('DateTime', $event['recurrence']['RDATE'][0]);
|
||||
$this->assertInstanceOf('DateTime', $event['recurrence']['RDATE'][1]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -297,7 +317,9 @@ class libvcalendar_test extends PHPUnit_Framework_TestCase
|
|||
$this->assertInstanceOf('DateTime', $freebusy['start'], "'start' property is DateTime object");
|
||||
$this->assertInstanceOf('DateTime', $freebusy['end'], "'end' property is DateTime object");
|
||||
$this->assertEquals(11, count($freebusy['periods']), "Number of freebusy periods defined");
|
||||
$this->assertEquals(9, count($ical->get_busy_periods()), "Number of busy periods found");
|
||||
$periods = $ical->get_busy_periods();
|
||||
$this->assertEquals(9, count($periods), "Number of busy periods found");
|
||||
$this->assertEquals('BUSY-TENTATIVE', $periods[8][2], "FBTYPE=BUSY-TENTATIVE");
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -369,7 +391,7 @@ class libvcalendar_test extends PHPUnit_Framework_TestCase
|
|||
$this->assertRegExp('/ATTENDEE.*;ROLE=REQ-PARTICIPANT/', $ics, "Export Attendee ROLE");
|
||||
$this->assertRegExp('/ATTENDEE.*;PARTSTAT=NEEDS-ACTION/', $ics, "Export Attendee Status");
|
||||
$this->assertRegExp('/ATTENDEE.*;RSVP=TRUE/', $ics, "Export Attendee RSVP");
|
||||
$this->assertRegExp('/ATTENDEE.*:mailto:rolf2@/', $ics, "Export Attendee mailto:");
|
||||
$this->assertRegExp('/:mailto:rolf2@/', $ics, "Export Attendee mailto:");
|
||||
|
||||
$rrule = $event['recurrence'];
|
||||
$this->assertRegExp('/RRULE:.*FREQ='.$rrule['FREQ'].'/', $ics, "Export Recurrence Frequence");
|
||||
|
@ -421,6 +443,8 @@ class libvcalendar_test extends PHPUnit_Framework_TestCase
|
|||
|
||||
// add exceptions
|
||||
$event = $events[0];
|
||||
unset($event['recurrence']['EXCEPTIONS']);
|
||||
|
||||
$exception1 = $event;
|
||||
$exception1['start'] = clone $event['start'];
|
||||
$exception1['start']->setDate(2013, 8, 14);
|
||||
|
@ -443,8 +467,8 @@ class libvcalendar_test extends PHPUnit_Framework_TestCase
|
|||
$this->assertEquals($num, substr_count($ics, 'UID:'.$event['uid']), "Recurrence Exceptions with same UID");
|
||||
$this->assertEquals($num, substr_count($ics, 'END:VEVENT'), "VEVENT encapsulation END");
|
||||
|
||||
$this->assertContains('RECURRENCE-ID;VALUE=DATE-TIME;TZID=Europe/Zurich:20130814', $ics, "Recurrence-ID (1) being the exception date");
|
||||
$this->assertContains('RECURRENCE-ID;VALUE=DATE-TIME;TZID=Europe/Zurich:20131113', $ics, "Recurrence-ID (2) being the exception date");
|
||||
$this->assertContains('RECURRENCE-ID;TZID=Europe/Zurich:20130814', $ics, "Recurrence-ID (1) being the exception date");
|
||||
$this->assertContains('RECURRENCE-ID;TZID=Europe/Zurich:20131113', $ics, "Recurrence-ID (2) being the exception date");
|
||||
$this->assertContains('SUMMARY:'.$exception2['title'], $ics, "Exception title");
|
||||
}
|
||||
|
||||
|
@ -478,7 +502,7 @@ class libvcalendar_test extends PHPUnit_Framework_TestCase
|
|||
$events = $ical->import_from_file(__DIR__ . '/resources/multiple-rdate.ics', 'UTF-8');
|
||||
$ics = $ical->export($events, null, false);
|
||||
|
||||
$this->assertContains('RDATE;VALUE=DATE-TIME:20140520T020000Z', $ics, "VALUE=PERIOD is translated into single DATE-TIME values");
|
||||
$this->assertContains('RDATE:20140520T020000Z', $ics, "VALUE=PERIOD is translated into single DATE-TIME values");
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -505,10 +529,11 @@ class libvcalendar_test extends PHPUnit_Framework_TestCase
|
|||
function test_datetime()
|
||||
{
|
||||
$ical = new libvcalendar();
|
||||
$localtime = $ical->datetime_prop('DTSTART', new DateTime('2013-09-01 12:00:00', new DateTimeZone('Europe/Berlin')));
|
||||
$localdate = $ical->datetime_prop('DTSTART', new DateTime('2013-09-01', new DateTimeZone('Europe/Berlin')), false, true);
|
||||
$utctime = $ical->datetime_prop('DTSTART', new DateTime('2013-09-01 12:00:00', new DateTimeZone('UTC')));
|
||||
$asutctime = $ical->datetime_prop('DTSTART', new DateTime('2013-09-01 12:00:00', new DateTimeZone('Europe/Berlin')), true);
|
||||
$cal = new \Sabre\VObject\Component\VCalendar();
|
||||
$localtime = $ical->datetime_prop($cal, 'DTSTART', new DateTime('2013-09-01 12:00:00', new DateTimeZone('Europe/Berlin')));
|
||||
$localdate = $ical->datetime_prop($cal, 'DTSTART', new DateTime('2013-09-01', new DateTimeZone('Europe/Berlin')), false, true);
|
||||
$utctime = $ical->datetime_prop($cal, 'DTSTART', new DateTime('2013-09-01 12:00:00', new DateTimeZone('UTC')));
|
||||
$asutctime = $ical->datetime_prop($cal, 'DTSTART', new DateTime('2013-09-01 12:00:00', new DateTimeZone('Europe/Berlin')), true);
|
||||
|
||||
$this->assertContains('TZID=Europe/Berlin', $localtime->serialize());
|
||||
$this->assertContains('VALUE=DATE', $localdate->serialize());
|
||||
|
|
|
@ -46,6 +46,11 @@ ATTENDEE:mailto:reminder-recipient@example.org
|
|||
SUMMARY:This is the reminder message
|
||||
DESCRIPTION:This is the second event reminder
|
||||
END:VALARM
|
||||
BEGIN:VALARM
|
||||
ACTION:DISPLAY
|
||||
DESCRIPTION:An absolute reminder
|
||||
TRIGGER;VALUE=DATE-TIME:20130812T160000Z
|
||||
END:VALARM
|
||||
END:VEVENT
|
||||
|
||||
END:VCALENDAR
|
||||
|
|
|
@ -7,7 +7,9 @@ BEGIN:VEVENT
|
|||
ORGANIZER;CN="Rolf Test":MAILTO:rolf@mykolab.com
|
||||
DTSTAMP:20130628T190056Z
|
||||
ATTENDEE;RSVP=TRUE;PARTSTAT=NEEDS-ACTION;ROLE=REQ-PARTICIPANT;
|
||||
X-UID=208889384:mailto:rolf2@mykolab.com
|
||||
DELEGATED-FROM=carl@mykolab.com;X-UID=208889384:mailto:rolf2@mykolab.com
|
||||
ATTENDEE;RSVP=FALSE;PARTSTAT=DELEGATED;ROLE=NON-PARTICIPANT;CUTYPE=INDIVIDUAL;
|
||||
DELEGATED-TO=rolf2@mykolab.com:mailto:carl@mykolab.com
|
||||
CREATED:20130628T190032Z
|
||||
UID:ac6b0aee-2519-4e5c-9a25-48c57064c9f0
|
||||
LAST-MODIFIED:20130628T190032Z
|
||||
|
|
|
@ -40,4 +40,12 @@ TRIGGER:-PT12H
|
|||
ACTION:DISPLAY
|
||||
END:VALARM
|
||||
END:VEVENT
|
||||
BEGIN:VEVENT
|
||||
DTSTART;TZID="Europe/Zurich":20140521T100000
|
||||
DTEND;TZID="Europe/Zurich":20140521T150000
|
||||
RECURRENCE-ID:20140521T080000Z
|
||||
UID:7e93e8e8eef16f28aa33b78cd73613eb
|
||||
DTSTAMP:20130718T082032Z
|
||||
SUMMARY:Recurring Test (Exception)
|
||||
END:VEVENT
|
||||
END:VCALENDAR
|
||||
|
|
|
@ -262,6 +262,8 @@ class kolab_storage
|
|||
*/
|
||||
public static function get_freebusy_server()
|
||||
{
|
||||
self::setup();
|
||||
|
||||
$url = 'https://' . $_SESSION['imap_host'] . '/freebusy';
|
||||
$url = self::$config->get('kolab_freebusy_server', $url);
|
||||
$url = rcube_utils::resolve_url($url);
|
||||
|
|
Loading…
Add table
Reference in a new issue