aboutsummaryrefslogtreecommitdiffstats
path: root/vendor/sabre/vobject/lib/Component
diff options
context:
space:
mode:
authorAndrew Manning <tamanning@zoho.com>2016-05-11 05:54:20 -0400
committerAndrew Manning <tamanning@zoho.com>2016-05-11 05:54:20 -0400
commitd968fc51eab8b0fb259ecbeae517056b99554017 (patch)
tree10e551cff9fefbefbfd7e5031b57320116bb7fce /vendor/sabre/vobject/lib/Component
parentc7698e4dc388b7d9a9db368672cb057c1d4d3a01 (diff)
parent4dd3839c41e18d9724855e7955d8737b6f52dcd6 (diff)
downloadvolse-hubzilla-d968fc51eab8b0fb259ecbeae517056b99554017.tar.gz
volse-hubzilla-d968fc51eab8b0fb259ecbeae517056b99554017.tar.bz2
volse-hubzilla-d968fc51eab8b0fb259ecbeae517056b99554017.zip
Merge remote-tracking branch 'upstream/dev' into plugin-repo
Diffstat (limited to 'vendor/sabre/vobject/lib/Component')
-rw-r--r--vendor/sabre/vobject/lib/Component/Available.php126
-rw-r--r--vendor/sabre/vobject/lib/Component/VAlarm.php142
-rw-r--r--vendor/sabre/vobject/lib/Component/VAvailability.php156
-rw-r--r--vendor/sabre/vobject/lib/Component/VCalendar.php561
-rw-r--r--vendor/sabre/vobject/lib/Component/VCard.php553
-rw-r--r--vendor/sabre/vobject/lib/Component/VEvent.php153
-rw-r--r--vendor/sabre/vobject/lib/Component/VFreeBusy.php102
-rw-r--r--vendor/sabre/vobject/lib/Component/VJournal.php107
-rw-r--r--vendor/sabre/vobject/lib/Component/VTimeZone.php67
-rw-r--r--vendor/sabre/vobject/lib/Component/VTodo.php193
10 files changed, 2160 insertions, 0 deletions
diff --git a/vendor/sabre/vobject/lib/Component/Available.php b/vendor/sabre/vobject/lib/Component/Available.php
new file mode 100644
index 000000000..b3aaf08af
--- /dev/null
+++ b/vendor/sabre/vobject/lib/Component/Available.php
@@ -0,0 +1,126 @@
+<?php
+
+namespace Sabre\VObject\Component;
+
+use Sabre\VObject;
+
+/**
+ * The Available sub-component.
+ *
+ * This component adds functionality to a component, specific for AVAILABLE
+ * components.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Ivan Enderlin
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class Available extends VObject\Component {
+
+ /**
+ * Returns the 'effective start' and 'effective end' of this VAVAILABILITY
+ * component.
+ *
+ * We use the DTSTART and DTEND or DURATION to determine this.
+ *
+ * The returned value is an array containing DateTimeImmutable instances.
+ * If either the start or end is 'unbounded' its value will be null
+ * instead.
+ *
+ * @return array
+ */
+ function getEffectiveStartEnd() {
+
+ $effectiveStart = $this->DTSTART->getDateTime();
+ if (isset($this->DTEND)) {
+ $effectiveEnd = $this->DTEND->getDateTime();
+ } else {
+ $effectiveEnd = $effectiveStart->add(VObject\DateTimeParser::parseDuration($this->DURATION));
+ }
+
+ return [$effectiveStart, $effectiveEnd];
+
+ }
+
+ /**
+ * A simple list of validation rules.
+ *
+ * This is simply a list of properties, and how many times they either
+ * must or must not appear.
+ *
+ * Possible values per property:
+ * * 0 - Must not appear.
+ * * 1 - Must appear exactly once.
+ * * + - Must appear at least once.
+ * * * - Can appear any number of times.
+ * * ? - May appear, but not more than once.
+ *
+ * @var array
+ */
+ function getValidationRules() {
+
+ return [
+ 'UID' => 1,
+ 'DTSTART' => 1,
+ 'DTSTAMP' => 1,
+
+ 'DTEND' => '?',
+ 'DURATION' => '?',
+
+ 'CREATED' => '?',
+ 'DESCRIPTION' => '?',
+ 'LAST-MODIFIED' => '?',
+ 'RECURRENCE-ID' => '?',
+ 'RRULE' => '?',
+ 'SUMMARY' => '?',
+
+ 'CATEGORIES' => '*',
+ 'COMMENT' => '*',
+ 'CONTACT' => '*',
+ 'EXDATE' => '*',
+ 'RDATE' => '*',
+
+ 'AVAILABLE' => '*',
+ ];
+
+ }
+
+ /**
+ * Validates the node for correctness.
+ *
+ * The following options are supported:
+ * Node::REPAIR - May attempt to automatically repair the problem.
+ * Node::PROFILE_CARDDAV - Validate the vCard for CardDAV purposes.
+ * Node::PROFILE_CALDAV - Validate the iCalendar for CalDAV purposes.
+ *
+ * This method returns an array with detected problems.
+ * Every element has the following properties:
+ *
+ * * level - problem level.
+ * * message - A human-readable string describing the issue.
+ * * node - A reference to the problematic node.
+ *
+ * The level means:
+ * 1 - The issue was repaired (only happens if REPAIR was turned on).
+ * 2 - A warning.
+ * 3 - An error.
+ *
+ * @param int $options
+ *
+ * @return array
+ */
+ function validate($options = 0) {
+
+ $result = parent::validate($options);
+
+ if (isset($this->DTEND) && isset($this->DURATION)) {
+ $result[] = [
+ 'level' => 3,
+ 'message' => 'DTEND and DURATION cannot both be present',
+ 'node' => $this
+ ];
+ }
+
+ return $result;
+
+ }
+}
diff --git a/vendor/sabre/vobject/lib/Component/VAlarm.php b/vendor/sabre/vobject/lib/Component/VAlarm.php
new file mode 100644
index 000000000..8cbd572e6
--- /dev/null
+++ b/vendor/sabre/vobject/lib/Component/VAlarm.php
@@ -0,0 +1,142 @@
+<?php
+
+namespace Sabre\VObject\Component;
+
+use Sabre\VObject;
+use Sabre\VObject\InvalidDataException;
+use DateTimeInterface;
+use DateTimeImmutable;
+
+/**
+ * VAlarm component.
+ *
+ * This component contains some additional functionality specific for VALARMs.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/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 DateTimeImmutable
+ */
+ 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 = $parentComponent->$propName->getDateTime();
+ $effectiveTrigger = $effectiveTrigger->add($triggerDuration);
+ } else {
+ if ($parentComponent->name === 'VTODO') {
+ $endProp = 'DUE';
+ } elseif ($parentComponent->name === 'VEVENT') {
+ $endProp = 'DTEND';
+ } else {
+ throw new InvalidDataException('time-range filters on VALARM components are only supported when they are a child of VTODO or VEVENT');
+ }
+
+ if (isset($parentComponent->$endProp)) {
+ $effectiveTrigger = $parentComponent->$endProp->getDateTime();
+ $effectiveTrigger = $effectiveTrigger->add($triggerDuration);
+ } elseif (isset($parentComponent->DURATION)) {
+ $effectiveTrigger = $parentComponent->DTSTART->getDateTime();
+ $duration = VObject\DateTimeParser::parseDuration($parentComponent->DURATION);
+ $effectiveTrigger = $effectiveTrigger->add($duration);
+ $effectiveTrigger = $effectiveTrigger->add($triggerDuration);
+ } else {
+ $effectiveTrigger = $parentComponent->DTSTART->getDateTime();
+ $effectiveTrigger = $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
+ */
+ function isInTimeRange(DateTimeInterface $start, DateTimeInterface $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);
+ }
+
+ }
+
+ /**
+ * A simple list of validation rules.
+ *
+ * This is simply a list of properties, and how many times they either
+ * must or must not appear.
+ *
+ * Possible values per property:
+ * * 0 - Must not appear.
+ * * 1 - Must appear exactly once.
+ * * + - Must appear at least once.
+ * * * - Can appear any number of times.
+ * * ? - May appear, but not more than once.
+ *
+ * @var array
+ */
+ function getValidationRules() {
+
+ return [
+ 'ACTION' => 1,
+ 'TRIGGER' => 1,
+
+ 'DURATION' => '?',
+ 'REPEAT' => '?',
+
+ 'ATTACH' => '?',
+ ];
+
+ }
+
+}
diff --git a/vendor/sabre/vobject/lib/Component/VAvailability.php b/vendor/sabre/vobject/lib/Component/VAvailability.php
new file mode 100644
index 000000000..66b3310c5
--- /dev/null
+++ b/vendor/sabre/vobject/lib/Component/VAvailability.php
@@ -0,0 +1,156 @@
+<?php
+
+namespace Sabre\VObject\Component;
+
+use DateTimeInterface;
+use Sabre\VObject;
+
+/**
+ * The VAvailability component.
+ *
+ * This component adds functionality to a component, specific for VAVAILABILITY
+ * components.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Ivan Enderlin
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class VAvailability 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:
+ *
+ * https://tools.ietf.org/html/draft-daboo-calendar-availability-05#section-3.1
+ *
+ * @param DateTimeInterface $start
+ * @param DateTimeInterface $end
+ *
+ * @return bool
+ */
+ function isInTimeRange(DateTimeInterface $start, DateTimeInterface $end) {
+
+ list($effectiveStart, $effectiveEnd) = $this->getEffectiveStartEnd();
+ return (
+ (is_null($effectiveStart) || $start < $effectiveEnd) &&
+ (is_null($effectiveEnd) || $end > $effectiveStart)
+ );
+
+ }
+
+ /**
+ * Returns the 'effective start' and 'effective end' of this VAVAILABILITY
+ * component.
+ *
+ * We use the DTSTART and DTEND or DURATION to determine this.
+ *
+ * The returned value is an array containing DateTimeImmutable instances.
+ * If either the start or end is 'unbounded' its value will be null
+ * instead.
+ *
+ * @return array
+ */
+ function getEffectiveStartEnd() {
+
+ $effectiveStart = null;
+ $effectiveEnd = null;
+
+ if (isset($this->DTSTART)) {
+ $effectiveStart = $this->DTSTART->getDateTime();
+ }
+ if (isset($this->DTEND)) {
+ $effectiveEnd = $this->DTEND->getDateTime();
+ } elseif ($effectiveStart && isset($this->DURATION)) {
+ $effectiveEnd = $effectiveStart->add(VObject\DateTimeParser::parseDuration($this->DURATION));
+ }
+
+ return [$effectiveStart, $effectiveEnd];
+
+ }
+
+
+ /**
+ * A simple list of validation rules.
+ *
+ * This is simply a list of properties, and how many times they either
+ * must or must not appear.
+ *
+ * Possible values per property:
+ * * 0 - Must not appear.
+ * * 1 - Must appear exactly once.
+ * * + - Must appear at least once.
+ * * * - Can appear any number of times.
+ * * ? - May appear, but not more than once.
+ *
+ * @var array
+ */
+ function getValidationRules() {
+
+ return [
+ 'UID' => 1,
+ 'DTSTAMP' => 1,
+
+ 'BUSYTYPE' => '?',
+ 'CLASS' => '?',
+ 'CREATED' => '?',
+ 'DESCRIPTION' => '?',
+ 'DTSTART' => '?',
+ 'LAST-MODIFIED' => '?',
+ 'ORGANIZER' => '?',
+ 'PRIORITY' => '?',
+ 'SEQUENCE' => '?',
+ 'SUMMARY' => '?',
+ 'URL' => '?',
+ 'DTEND' => '?',
+ 'DURATION' => '?',
+
+ 'CATEGORIES' => '*',
+ 'COMMENT' => '*',
+ 'CONTACT' => '*',
+ ];
+
+ }
+
+ /**
+ * Validates the node for correctness.
+ *
+ * The following options are supported:
+ * Node::REPAIR - May attempt to automatically repair the problem.
+ * Node::PROFILE_CARDDAV - Validate the vCard for CardDAV purposes.
+ * Node::PROFILE_CALDAV - Validate the iCalendar for CalDAV purposes.
+ *
+ * This method returns an array with detected problems.
+ * Every element has the following properties:
+ *
+ * * level - problem level.
+ * * message - A human-readable string describing the issue.
+ * * node - A reference to the problematic node.
+ *
+ * The level means:
+ * 1 - The issue was repaired (only happens if REPAIR was turned on).
+ * 2 - A warning.
+ * 3 - An error.
+ *
+ * @param int $options
+ *
+ * @return array
+ */
+ function validate($options = 0) {
+
+ $result = parent::validate($options);
+
+ if (isset($this->DTEND) && isset($this->DURATION)) {
+ $result[] = [
+ 'level' => 3,
+ 'message' => 'DTEND and DURATION cannot both be present',
+ 'node' => $this
+ ];
+ }
+
+ return $result;
+
+ }
+}
diff --git a/vendor/sabre/vobject/lib/Component/VCalendar.php b/vendor/sabre/vobject/lib/Component/VCalendar.php
new file mode 100644
index 000000000..988db9dc2
--- /dev/null
+++ b/vendor/sabre/vobject/lib/Component/VCalendar.php
@@ -0,0 +1,561 @@
+<?php
+
+namespace Sabre\VObject\Component;
+
+use DateTimeInterface;
+use DateTimeZone;
+use Sabre\VObject;
+use Sabre\VObject\Component;
+use Sabre\VObject\Property;
+use Sabre\VObject\Recur\EventIterator;
+use Sabre\VObject\Recur\NoInstancesException;
+use Sabre\VObject\InvalidDataException;
+
+/**
+ * The VCalendar component.
+ *
+ * This component adds functionality to a component, specific for a VCALENDAR.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class VCalendar extends VObject\Document {
+
+ /**
+ * The default name for this component.
+ *
+ * This should be 'VCALENDAR' or 'VCARD'.
+ *
+ * @var string
+ */
+ static $defaultName = 'VCALENDAR';
+
+ /**
+ * This is a list of components, and which classes they should map to.
+ *
+ * @var array
+ */
+ static $componentMap = [
+ 'VCALENDAR' => 'Sabre\\VObject\\Component\\VCalendar',
+ 'VALARM' => 'Sabre\\VObject\\Component\\VAlarm',
+ 'VEVENT' => 'Sabre\\VObject\\Component\\VEvent',
+ 'VFREEBUSY' => 'Sabre\\VObject\\Component\\VFreeBusy',
+ 'VAVAILABILITY' => 'Sabre\\VObject\\Component\\VAvailability',
+ 'AVAILABLE' => 'Sabre\\VObject\\Component\\Available',
+ 'VJOURNAL' => 'Sabre\\VObject\\Component\\VJournal',
+ 'VTIMEZONE' => 'Sabre\\VObject\\Component\\VTimeZone',
+ 'VTODO' => 'Sabre\\VObject\\Component\\VTodo',
+ ];
+
+ /**
+ * List of value-types, and which classes they map to.
+ *
+ * @var array
+ */
+ static $valueMap = [
+ 'BINARY' => 'Sabre\\VObject\\Property\\Binary',
+ 'BOOLEAN' => 'Sabre\\VObject\\Property\\Boolean',
+ 'CAL-ADDRESS' => 'Sabre\\VObject\\Property\\ICalendar\\CalAddress',
+ 'DATE' => 'Sabre\\VObject\\Property\\ICalendar\\Date',
+ 'DATE-TIME' => 'Sabre\\VObject\\Property\\ICalendar\\DateTime',
+ 'DURATION' => 'Sabre\\VObject\\Property\\ICalendar\\Duration',
+ 'FLOAT' => 'Sabre\\VObject\\Property\\FloatValue',
+ 'INTEGER' => 'Sabre\\VObject\\Property\\IntegerValue',
+ 'PERIOD' => 'Sabre\\VObject\\Property\\ICalendar\\Period',
+ 'RECUR' => 'Sabre\\VObject\\Property\\ICalendar\\Recur',
+ 'TEXT' => 'Sabre\\VObject\\Property\\Text',
+ 'TIME' => 'Sabre\\VObject\\Property\\Time',
+ 'UNKNOWN' => 'Sabre\\VObject\\Property\\Unknown', // jCard / jCal-only.
+ 'URI' => 'Sabre\\VObject\\Property\\Uri',
+ 'UTC-OFFSET' => 'Sabre\\VObject\\Property\\UtcOffset',
+ ];
+
+ /**
+ * List of properties, and which classes they map to.
+ *
+ * @var array
+ */
+ static $propertyMap = [
+ // Calendar properties
+ 'CALSCALE' => 'Sabre\\VObject\\Property\\FlatText',
+ 'METHOD' => 'Sabre\\VObject\\Property\\FlatText',
+ 'PRODID' => 'Sabre\\VObject\\Property\\FlatText',
+ 'VERSION' => 'Sabre\\VObject\\Property\\FlatText',
+
+ // Component properties
+ 'ATTACH' => 'Sabre\\VObject\\Property\\Uri',
+ 'CATEGORIES' => 'Sabre\\VObject\\Property\\Text',
+ 'CLASS' => 'Sabre\\VObject\\Property\\FlatText',
+ 'COMMENT' => 'Sabre\\VObject\\Property\\FlatText',
+ 'DESCRIPTION' => 'Sabre\\VObject\\Property\\FlatText',
+ 'GEO' => 'Sabre\\VObject\\Property\\FloatValue',
+ 'LOCATION' => 'Sabre\\VObject\\Property\\FlatText',
+ 'PERCENT-COMPLETE' => 'Sabre\\VObject\\Property\\IntegerValue',
+ 'PRIORITY' => 'Sabre\\VObject\\Property\\IntegerValue',
+ 'RESOURCES' => 'Sabre\\VObject\\Property\\Text',
+ 'STATUS' => 'Sabre\\VObject\\Property\\FlatText',
+ 'SUMMARY' => 'Sabre\\VObject\\Property\\FlatText',
+
+ // Date and Time Component Properties
+ 'COMPLETED' => 'Sabre\\VObject\\Property\\ICalendar\\DateTime',
+ 'DTEND' => 'Sabre\\VObject\\Property\\ICalendar\\DateTime',
+ 'DUE' => 'Sabre\\VObject\\Property\\ICalendar\\DateTime',
+ 'DTSTART' => 'Sabre\\VObject\\Property\\ICalendar\\DateTime',
+ 'DURATION' => 'Sabre\\VObject\\Property\\ICalendar\\Duration',
+ 'FREEBUSY' => 'Sabre\\VObject\\Property\\ICalendar\\Period',
+ 'TRANSP' => 'Sabre\\VObject\\Property\\FlatText',
+
+ // Time Zone Component Properties
+ 'TZID' => 'Sabre\\VObject\\Property\\FlatText',
+ 'TZNAME' => 'Sabre\\VObject\\Property\\FlatText',
+ 'TZOFFSETFROM' => 'Sabre\\VObject\\Property\\UtcOffset',
+ 'TZOFFSETTO' => 'Sabre\\VObject\\Property\\UtcOffset',
+ 'TZURL' => 'Sabre\\VObject\\Property\\Uri',
+
+ // Relationship Component Properties
+ 'ATTENDEE' => 'Sabre\\VObject\\Property\\ICalendar\\CalAddress',
+ 'CONTACT' => 'Sabre\\VObject\\Property\\FlatText',
+ 'ORGANIZER' => 'Sabre\\VObject\\Property\\ICalendar\\CalAddress',
+ 'RECURRENCE-ID' => 'Sabre\\VObject\\Property\\ICalendar\\DateTime',
+ 'RELATED-TO' => 'Sabre\\VObject\\Property\\FlatText',
+ 'URL' => 'Sabre\\VObject\\Property\\Uri',
+ 'UID' => 'Sabre\\VObject\\Property\\FlatText',
+
+ // Recurrence Component Properties
+ 'EXDATE' => 'Sabre\\VObject\\Property\\ICalendar\\DateTime',
+ 'RDATE' => 'Sabre\\VObject\\Property\\ICalendar\\DateTime',
+ 'RRULE' => 'Sabre\\VObject\\Property\\ICalendar\\Recur',
+ 'EXRULE' => 'Sabre\\VObject\\Property\\ICalendar\\Recur', // Deprecated since rfc5545
+
+ // Alarm Component Properties
+ 'ACTION' => 'Sabre\\VObject\\Property\\FlatText',
+ 'REPEAT' => 'Sabre\\VObject\\Property\\IntegerValue',
+ 'TRIGGER' => 'Sabre\\VObject\\Property\\ICalendar\\Duration',
+
+ // Change Management Component Properties
+ 'CREATED' => 'Sabre\\VObject\\Property\\ICalendar\\DateTime',
+ 'DTSTAMP' => 'Sabre\\VObject\\Property\\ICalendar\\DateTime',
+ 'LAST-MODIFIED' => 'Sabre\\VObject\\Property\\ICalendar\\DateTime',
+ 'SEQUENCE' => 'Sabre\\VObject\\Property\\IntegerValue',
+
+ // Request Status
+ 'REQUEST-STATUS' => 'Sabre\\VObject\\Property\\Text',
+
+ // Additions from draft-daboo-valarm-extensions-04
+ 'ALARM-AGENT' => 'Sabre\\VObject\\Property\\Text',
+ 'ACKNOWLEDGED' => 'Sabre\\VObject\\Property\\ICalendar\\DateTime',
+ 'PROXIMITY' => 'Sabre\\VObject\\Property\\Text',
+ 'DEFAULT-ALARM' => 'Sabre\\VObject\\Property\\Boolean',
+
+ // Additions from draft-daboo-calendar-availability-05
+ 'BUSYTYPE' => 'Sabre\\VObject\\Property\\Text',
+
+ ];
+
+ /**
+ * Returns the current document type.
+ *
+ * @return int
+ */
+ function getDocumentType() {
+
+ return self::ICALENDAR20;
+
+ }
+
+ /**
+ * Returns a list of all 'base components'. For instance, if an Event has
+ * a recurrence rule, and one instance is overridden, the overridden event
+ * will have the same UID, but will be excluded from this list.
+ *
+ * VTIMEZONE components will always be excluded.
+ *
+ * @param string $componentName filter by component name
+ *
+ * @return VObject\Component[]
+ */
+ function getBaseComponents($componentName = null) {
+
+ $isBaseComponent = function($component) {
+
+ if (!$component instanceof VObject\Component) {
+ return false;
+ }
+ if ($component->name === 'VTIMEZONE') {
+ return false;
+ }
+ if (isset($component->{'RECURRENCE-ID'})) {
+ return false;
+ }
+ return true;
+
+ };
+
+ if ($componentName) {
+ // Early exit
+ return array_filter(
+ $this->select($componentName),
+ $isBaseComponent
+ );
+ }
+
+ $components = [];
+ foreach ($this->children as $childGroup) {
+
+ foreach ($childGroup as $child) {
+
+ if (!$child instanceof Component) {
+ // If one child is not a component, they all are so we skip
+ // the entire group.
+ continue 2;
+ }
+ if ($isBaseComponent($child)) {
+ $components[] = $child;
+ }
+
+ }
+
+ }
+ return $components;
+
+ }
+
+ /**
+ * Returns the first component that is not a VTIMEZONE, and does not have
+ * an RECURRENCE-ID.
+ *
+ * If there is no such component, null will be returned.
+ *
+ * @param string $componentName filter by component name
+ *
+ * @return VObject\Component|null
+ */
+ function getBaseComponent($componentName = null) {
+
+ $isBaseComponent = function($component) {
+
+ if (!$component instanceof VObject\Component) {
+ return false;
+ }
+ if ($component->name === 'VTIMEZONE') {
+ return false;
+ }
+ if (isset($component->{'RECURRENCE-ID'})) {
+ return false;
+ }
+ return true;
+
+ };
+
+ if ($componentName) {
+ foreach ($this->select($componentName) as $child) {
+ if ($isBaseComponent($child)) {
+ return $child;
+ }
+ }
+ return null;
+ }
+
+ // Searching all components
+ foreach ($this->children as $childGroup) {
+ foreach ($childGroup as $child) {
+ if ($isBaseComponent($child)) {
+ return $child;
+ }
+ }
+
+ }
+ return null;
+
+ }
+
+ /**
+ * Expand all events in this VCalendar object and return a new VCalendar
+ * with the expanded events.
+ *
+ * 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.
+ *
+ * @param DateTimeInterface $start
+ * @param DateTimeInterface $end
+ * @param DateTimeZone $timeZone reference timezone for floating dates and
+ * times.
+ * @return VCalendar
+ */
+ function expand(DateTimeInterface $start, DateTimeInterface $end, DateTimeZone $timeZone = null) {
+
+ $newChildren = [];
+ $recurringEvents = [];
+
+ if (!$timeZone) {
+ $timeZone = new DateTimeZone('UTC');
+ }
+
+ $stripTimezones = function(Component $component) use ($timeZone, &$stripTimezones) {
+
+ foreach ($component->children() as $componentChild) {
+ if ($componentChild instanceof Property\ICalendar\DateTime && $componentChild->hasTime()) {
+
+ $dt = $componentChild->getDateTimes($timeZone);
+ // We only need to update the first timezone, because
+ // setDateTimes will match all other timezones to the
+ // first.
+ $dt[0] = $dt[0]->setTimeZone(new DateTimeZone('UTC'));
+ $componentChild->setDateTimes($dt);
+ } elseif ($componentChild instanceof Component) {
+ $stripTimezones($componentChild);
+ }
+
+ }
+ return $component;
+
+ };
+
+ foreach ($this->children() as $child) {
+
+ if ($child instanceof Property && $child->name !== 'PRODID') {
+ // We explictly want to ignore PRODID, because we want to
+ // overwrite it with our own.
+ $newChildren[] = clone $child;
+ } elseif ($child instanceof Component && $child->name !== 'VTIMEZONE') {
+
+ // We're also stripping all VTIMEZONE objects because we're
+ // converting everything to UTC.
+ if ($child->name === 'VEVENT' && (isset($child->{'RECURRENCE-ID'}) || isset($child->RRULE) || isset($child->RDATE))) {
+ // Handle these a bit later.
+ $uid = (string)$child->UID;
+ if (!$uid) {
+ throw new InvalidDataException('Every VEVENT object must have a UID property');
+ }
+ if (isset($recurringEvents[$uid])) {
+ $recurringEvents[$uid][] = clone $child;
+ } else {
+ $recurringEvents[$uid] = [clone $child];
+ }
+ } elseif ($child->name === 'VEVENT' && $child->isInTimeRange($start, $end)) {
+ $newChildren[] = $stripTimezones(clone $child);
+ }
+
+ }
+
+ }
+
+ foreach ($recurringEvents as $events) {
+
+ try {
+ $it = new EventIterator($events, $timeZone);
+
+ } catch (NoInstancesException $e) {
+ // This event is recurring, but it doesn't have a single
+ // instance. We are skipping this event from the output
+ // entirely.
+ continue;
+ }
+ $it->fastForward($start);
+
+ while ($it->valid() && $it->getDTStart() < $end) {
+
+ if ($it->getDTEnd() > $start) {
+
+ $newChildren[] = $stripTimezones($it->getEventObject());
+
+ }
+ $it->next();
+
+ }
+
+ }
+
+ return new self($newChildren);
+
+ }
+
+ /**
+ * This method should return a list of default property values.
+ *
+ * @return array
+ */
+ protected function getDefaults() {
+
+ return [
+ 'VERSION' => '2.0',
+ 'PRODID' => '-//Sabre//Sabre VObject ' . VObject\Version::VERSION . '//EN',
+ 'CALSCALE' => 'GREGORIAN',
+ ];
+
+ }
+
+ /**
+ * A simple list of validation rules.
+ *
+ * This is simply a list of properties, and how many times they either
+ * must or must not appear.
+ *
+ * Possible values per property:
+ * * 0 - Must not appear.
+ * * 1 - Must appear exactly once.
+ * * + - Must appear at least once.
+ * * * - Can appear any number of times.
+ * * ? - May appear, but not more than once.
+ *
+ * @var array
+ */
+ function getValidationRules() {
+
+ return [
+ 'PRODID' => 1,
+ 'VERSION' => 1,
+
+ 'CALSCALE' => '?',
+ 'METHOD' => '?',
+ ];
+
+ }
+
+ /**
+ * Validates the node for correctness.
+ *
+ * The following options are supported:
+ * Node::REPAIR - May attempt to automatically repair the problem.
+ * Node::PROFILE_CARDDAV - Validate the vCard for CardDAV purposes.
+ * Node::PROFILE_CALDAV - Validate the iCalendar for CalDAV purposes.
+ *
+ * This method returns an array with detected problems.
+ * Every element has the following properties:
+ *
+ * * level - problem level.
+ * * message - A human-readable string describing the issue.
+ * * node - A reference to the problematic node.
+ *
+ * The level means:
+ * 1 - The issue was repaired (only happens if REPAIR was turned on).
+ * 2 - A warning.
+ * 3 - An error.
+ *
+ * @param int $options
+ *
+ * @return array
+ */
+ function validate($options = 0) {
+
+ $warnings = parent::validate($options);
+
+ if ($ver = $this->VERSION) {
+ if ((string)$ver !== '2.0') {
+ $warnings[] = [
+ 'level' => 3,
+ 'message' => 'Only iCalendar version 2.0 as defined in rfc5545 is supported.',
+ 'node' => $this,
+ ];
+ }
+
+ }
+
+ $uidList = [];
+ $componentsFound = 0;
+ $componentTypes = [];
+
+ foreach ($this->children() as $child) {
+ if ($child instanceof Component) {
+ $componentsFound++;
+
+ if (!in_array($child->name, ['VEVENT', 'VTODO', 'VJOURNAL'])) {
+ continue;
+ }
+ $componentTypes[] = $child->name;
+
+ $uid = (string)$child->UID;
+ $isMaster = isset($child->{'RECURRENCE-ID'}) ? 0 : 1;
+ if (isset($uidList[$uid])) {
+ $uidList[$uid]['count']++;
+ if ($isMaster && $uidList[$uid]['hasMaster']) {
+ $warnings[] = [
+ 'level' => 3,
+ 'message' => 'More than one master object was found for the object with UID ' . $uid,
+ 'node' => $this,
+ ];
+ }
+ $uidList[$uid]['hasMaster'] += $isMaster;
+ } else {
+ $uidList[$uid] = [
+ 'count' => 1,
+ 'hasMaster' => $isMaster,
+ ];
+ }
+
+ }
+ }
+
+ if ($componentsFound === 0) {
+ $warnings[] = [
+ 'level' => 3,
+ 'message' => 'An iCalendar object must have at least 1 component.',
+ 'node' => $this,
+ ];
+ }
+
+ if ($options & self::PROFILE_CALDAV) {
+ if (count($uidList) > 1) {
+ $warnings[] = [
+ 'level' => 3,
+ 'message' => 'A calendar object on a CalDAV server may only have components with the same UID.',
+ 'node' => $this,
+ ];
+ }
+ if (count($componentTypes) === 0) {
+ $warnings[] = [
+ 'level' => 3,
+ 'message' => 'A calendar object on a CalDAV server must have at least 1 component (VTODO, VEVENT, VJOURNAL).',
+ 'node' => $this,
+ ];
+ }
+ if (count(array_unique($componentTypes)) > 1) {
+ $warnings[] = [
+ 'level' => 3,
+ 'message' => 'A calendar object on a CalDAV server may only have 1 type of component (VEVENT, VTODO or VJOURNAL).',
+ 'node' => $this,
+ ];
+ }
+
+ if (isset($this->METHOD)) {
+ $warnings[] = [
+ 'level' => 3,
+ 'message' => 'A calendar object on a CalDAV server MUST NOT have a METHOD property.',
+ 'node' => $this,
+ ];
+ }
+ }
+
+ return $warnings;
+
+ }
+
+ /**
+ * Returns all components with a specific UID value.
+ *
+ * @return array
+ */
+ function getByUID($uid) {
+
+ return array_filter($this->getComponents(), function($item) use ($uid) {
+
+ if (!$itemUid = $item->select('UID')) {
+ return false;
+ }
+ $itemUid = current($itemUid)->getValue();
+ return $uid === $itemUid;
+
+ });
+
+ }
+
+
+}
diff --git a/vendor/sabre/vobject/lib/Component/VCard.php b/vendor/sabre/vobject/lib/Component/VCard.php
new file mode 100644
index 000000000..3c05191a9
--- /dev/null
+++ b/vendor/sabre/vobject/lib/Component/VCard.php
@@ -0,0 +1,553 @@
+<?php
+
+namespace Sabre\VObject\Component;
+
+use Sabre\VObject;
+use Sabre\Xml;
+
+/**
+ * The VCard component.
+ *
+ * This component represents the BEGIN:VCARD and END:VCARD found in every
+ * vcard.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class VCard extends VObject\Document {
+
+ /**
+ * The default name for this component.
+ *
+ * This should be 'VCALENDAR' or 'VCARD'.
+ *
+ * @var string
+ */
+ static $defaultName = 'VCARD';
+
+ /**
+ * Caching the version number.
+ *
+ * @var int
+ */
+ private $version = null;
+
+ /**
+ * This is a list of components, and which classes they should map to.
+ *
+ * @var array
+ */
+ static $componentMap = [
+ 'VCARD' => 'Sabre\\VObject\\Component\\VCard',
+ ];
+
+ /**
+ * List of value-types, and which classes they map to.
+ *
+ * @var array
+ */
+ static $valueMap = [
+ 'BINARY' => 'Sabre\\VObject\\Property\\Binary',
+ 'BOOLEAN' => 'Sabre\\VObject\\Property\\Boolean',
+ 'CONTENT-ID' => 'Sabre\\VObject\\Property\\FlatText', // vCard 2.1 only
+ 'DATE' => 'Sabre\\VObject\\Property\\VCard\\Date',
+ 'DATE-TIME' => 'Sabre\\VObject\\Property\\VCard\\DateTime',
+ 'DATE-AND-OR-TIME' => 'Sabre\\VObject\\Property\\VCard\\DateAndOrTime', // vCard only
+ 'FLOAT' => 'Sabre\\VObject\\Property\\FloatValue',
+ 'INTEGER' => 'Sabre\\VObject\\Property\\IntegerValue',
+ 'LANGUAGE-TAG' => 'Sabre\\VObject\\Property\\VCard\\LanguageTag',
+ 'TIMESTAMP' => 'Sabre\\VObject\\Property\\VCard\\TimeStamp',
+ 'TEXT' => 'Sabre\\VObject\\Property\\Text',
+ 'TIME' => 'Sabre\\VObject\\Property\\Time',
+ 'UNKNOWN' => 'Sabre\\VObject\\Property\\Unknown', // jCard / jCal-only.
+ 'URI' => 'Sabre\\VObject\\Property\\Uri',
+ 'URL' => 'Sabre\\VObject\\Property\\Uri', // vCard 2.1 only
+ 'UTC-OFFSET' => 'Sabre\\VObject\\Property\\UtcOffset',
+ ];
+
+ /**
+ * List of properties, and which classes they map to.
+ *
+ * @var array
+ */
+ static $propertyMap = [
+
+ // vCard 2.1 properties and up
+ 'N' => 'Sabre\\VObject\\Property\\Text',
+ 'FN' => 'Sabre\\VObject\\Property\\FlatText',
+ 'PHOTO' => 'Sabre\\VObject\\Property\\Binary',
+ 'BDAY' => 'Sabre\\VObject\\Property\\VCard\\DateAndOrTime',
+ 'ADR' => 'Sabre\\VObject\\Property\\Text',
+ 'LABEL' => 'Sabre\\VObject\\Property\\FlatText', // Removed in vCard 4.0
+ 'TEL' => 'Sabre\\VObject\\Property\\FlatText',
+ 'EMAIL' => 'Sabre\\VObject\\Property\\FlatText',
+ 'MAILER' => 'Sabre\\VObject\\Property\\FlatText', // Removed in vCard 4.0
+ 'GEO' => 'Sabre\\VObject\\Property\\FlatText',
+ 'TITLE' => 'Sabre\\VObject\\Property\\FlatText',
+ 'ROLE' => 'Sabre\\VObject\\Property\\FlatText',
+ 'LOGO' => 'Sabre\\VObject\\Property\\Binary',
+ // 'AGENT' => 'Sabre\\VObject\\Property\\', // Todo: is an embedded vCard. Probably rare, so
+ // not supported at the moment
+ 'ORG' => 'Sabre\\VObject\\Property\\Text',
+ 'NOTE' => 'Sabre\\VObject\\Property\\FlatText',
+ 'REV' => 'Sabre\\VObject\\Property\\VCard\\TimeStamp',
+ 'SOUND' => 'Sabre\\VObject\\Property\\FlatText',
+ 'URL' => 'Sabre\\VObject\\Property\\Uri',
+ 'UID' => 'Sabre\\VObject\\Property\\FlatText',
+ 'VERSION' => 'Sabre\\VObject\\Property\\FlatText',
+ 'KEY' => 'Sabre\\VObject\\Property\\FlatText',
+ 'TZ' => 'Sabre\\VObject\\Property\\Text',
+
+ // vCard 3.0 properties
+ 'CATEGORIES' => 'Sabre\\VObject\\Property\\Text',
+ 'SORT-STRING' => 'Sabre\\VObject\\Property\\FlatText',
+ 'PRODID' => 'Sabre\\VObject\\Property\\FlatText',
+ 'NICKNAME' => 'Sabre\\VObject\\Property\\Text',
+ 'CLASS' => 'Sabre\\VObject\\Property\\FlatText', // Removed in vCard 4.0
+
+ // rfc2739 properties
+ 'FBURL' => 'Sabre\\VObject\\Property\\Uri',
+ 'CAPURI' => 'Sabre\\VObject\\Property\\Uri',
+ 'CALURI' => 'Sabre\\VObject\\Property\\Uri',
+ 'CALADRURI' => 'Sabre\\VObject\\Property\\Uri',
+
+ // rfc4770 properties
+ 'IMPP' => 'Sabre\\VObject\\Property\\Uri',
+
+ // vCard 4.0 properties
+ 'SOURCE' => 'Sabre\\VObject\\Property\\Uri',
+ 'XML' => 'Sabre\\VObject\\Property\\FlatText',
+ 'ANNIVERSARY' => 'Sabre\\VObject\\Property\\VCard\\DateAndOrTime',
+ 'CLIENTPIDMAP' => 'Sabre\\VObject\\Property\\Text',
+ 'LANG' => 'Sabre\\VObject\\Property\\VCard\\LanguageTag',
+ 'GENDER' => 'Sabre\\VObject\\Property\\Text',
+ 'KIND' => 'Sabre\\VObject\\Property\\FlatText',
+ 'MEMBER' => 'Sabre\\VObject\\Property\\Uri',
+ 'RELATED' => 'Sabre\\VObject\\Property\\Uri',
+
+ // rfc6474 properties
+ 'BIRTHPLACE' => 'Sabre\\VObject\\Property\\FlatText',
+ 'DEATHPLACE' => 'Sabre\\VObject\\Property\\FlatText',
+ 'DEATHDATE' => 'Sabre\\VObject\\Property\\VCard\\DateAndOrTime',
+
+ // rfc6715 properties
+ 'EXPERTISE' => 'Sabre\\VObject\\Property\\FlatText',
+ 'HOBBY' => 'Sabre\\VObject\\Property\\FlatText',
+ 'INTEREST' => 'Sabre\\VObject\\Property\\FlatText',
+ 'ORG-DIRECTORY' => 'Sabre\\VObject\\Property\\FlatText',
+
+ ];
+
+ /**
+ * Returns the current document type.
+ *
+ * @return int
+ */
+ function getDocumentType() {
+
+ if (!$this->version) {
+
+ $version = (string)$this->VERSION;
+
+ switch ($version) {
+ case '2.1' :
+ $this->version = self::VCARD21;
+ break;
+ case '3.0' :
+ $this->version = self::VCARD30;
+ break;
+ case '4.0' :
+ $this->version = self::VCARD40;
+ break;
+ default :
+ // We don't want to cache the version if it's unknown,
+ // because we might get a version property in a bit.
+ return self::UNKNOWN;
+ }
+ }
+
+ return $this->version;
+
+ }
+
+ /**
+ * Converts the document to a different vcard version.
+ *
+ * Use one of the VCARD constants for the target. This method will return
+ * a copy of the vcard in the new version.
+ *
+ * At the moment the only supported conversion is from 3.0 to 4.0.
+ *
+ * If input and output version are identical, a clone is returned.
+ *
+ * @param int $target
+ *
+ * @return VCard
+ */
+ function convert($target) {
+
+ $converter = new VObject\VCardConverter();
+ return $converter->convert($this, $target);
+
+ }
+
+ /**
+ * VCards with version 2.1, 3.0 and 4.0 are found.
+ *
+ * If the VCARD doesn't know its version, 2.1 is assumed.
+ */
+ const DEFAULT_VERSION = self::VCARD21;
+
+ /**
+ * Validates the node for correctness.
+ *
+ * The following options are supported:
+ * Node::REPAIR - May attempt to automatically repair the problem.
+ *
+ * This method returns an array with detected problems.
+ * Every element has the following properties:
+ *
+ * * level - problem level.
+ * * message - A human-readable string describing the issue.
+ * * node - A reference to the problematic node.
+ *
+ * The level means:
+ * 1 - The issue was repaired (only happens if REPAIR was turned on)
+ * 2 - An inconsequential issue
+ * 3 - A severe issue.
+ *
+ * @param int $options
+ *
+ * @return array
+ */
+ function validate($options = 0) {
+
+ $warnings = [];
+
+ $versionMap = [
+ self::VCARD21 => '2.1',
+ self::VCARD30 => '3.0',
+ self::VCARD40 => '4.0',
+ ];
+
+ $version = $this->select('VERSION');
+ if (count($version) === 1) {
+ $version = (string)$this->VERSION;
+ if ($version !== '2.1' && $version !== '3.0' && $version !== '4.0') {
+ $warnings[] = [
+ 'level' => 3,
+ 'message' => 'Only vcard version 4.0 (RFC6350), version 3.0 (RFC2426) or version 2.1 (icm-vcard-2.1) are supported.',
+ 'node' => $this,
+ ];
+ if ($options & self::REPAIR) {
+ $this->VERSION = $versionMap[self::DEFAULT_VERSION];
+ }
+ }
+ if ($version === '2.1' && ($options & self::PROFILE_CARDDAV)) {
+ $warnings[] = [
+ 'level' => 3,
+ 'message' => 'CardDAV servers are not allowed to accept vCard 2.1.',
+ 'node' => $this,
+ ];
+ }
+
+ }
+ $uid = $this->select('UID');
+ if (count($uid) === 0) {
+ if ($options & self::PROFILE_CARDDAV) {
+ // Required for CardDAV
+ $warningLevel = 3;
+ $message = 'vCards on CardDAV servers MUST have a UID property.';
+ } else {
+ // Not required for regular vcards
+ $warningLevel = 2;
+ $message = 'Adding a UID to a vCard property is recommended.';
+ }
+ if ($options & self::REPAIR) {
+ $this->UID = VObject\UUIDUtil::getUUID();
+ $warningLevel = 1;
+ }
+ $warnings[] = [
+ 'level' => $warningLevel,
+ 'message' => $message,
+ 'node' => $this,
+ ];
+ }
+
+ $fn = $this->select('FN');
+ if (count($fn) !== 1) {
+
+ $repaired = false;
+ 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];
+ }
+ $repaired = true;
+
+ // Otherwise, the ORG property may work
+ } elseif (isset($this->ORG)) {
+ $this->FN = (string)$this->ORG;
+ $repaired = true;
+ }
+
+ }
+ $warnings[] = [
+ 'level' => $repaired ? 1 : 3,
+ 'message' => 'The FN property must appear in the VCARD component exactly 1 time',
+ 'node' => $this,
+ ];
+ }
+
+ return array_merge(
+ parent::validate($options),
+ $warnings
+ );
+
+ }
+
+ /**
+ * A simple list of validation rules.
+ *
+ * This is simply a list of properties, and how many times they either
+ * must or must not appear.
+ *
+ * Possible values per property:
+ * * 0 - Must not appear.
+ * * 1 - Must appear exactly once.
+ * * + - Must appear at least once.
+ * * * - Can appear any number of times.
+ * * ? - May appear, but not more than once.
+ *
+ * @var array
+ */
+ function getValidationRules() {
+
+ return [
+ 'ADR' => '*',
+ 'ANNIVERSARY' => '?',
+ 'BDAY' => '?',
+ 'CALADRURI' => '*',
+ 'CALURI' => '*',
+ 'CATEGORIES' => '*',
+ 'CLIENTPIDMAP' => '*',
+ 'EMAIL' => '*',
+ 'FBURL' => '*',
+ 'IMPP' => '*',
+ 'GENDER' => '?',
+ 'GEO' => '*',
+ 'KEY' => '*',
+ 'KIND' => '?',
+ 'LANG' => '*',
+ 'LOGO' => '*',
+ 'MEMBER' => '*',
+ 'N' => '?',
+ 'NICKNAME' => '*',
+ 'NOTE' => '*',
+ 'ORG' => '*',
+ 'PHOTO' => '*',
+ 'PRODID' => '?',
+ 'RELATED' => '*',
+ 'REV' => '?',
+ 'ROLE' => '*',
+ 'SOUND' => '*',
+ 'SOURCE' => '*',
+ 'TEL' => '*',
+ 'TITLE' => '*',
+ 'TZ' => '*',
+ 'URL' => '*',
+ 'VERSION' => '1',
+ 'XML' => '*',
+
+ // FN is commented out, because it's already handled by the
+ // validate function, which may also try to repair it.
+ // 'FN' => '+',
+ 'UID' => '?',
+ ];
+
+ }
+
+ /**
+ * Returns a preferred field.
+ *
+ * VCards can indicate wether a field such as ADR, TEL or EMAIL is
+ * preferred by specifying TYPE=PREF (vcard 2.1, 3) or PREF=x (vcard 4, x
+ * being a number between 1 and 100).
+ *
+ * If neither of those parameters are specified, the first is returned, if
+ * a field with that name does not exist, null is returned.
+ *
+ * @param string $fieldName
+ *
+ * @return VObject\Property|null
+ */
+ function preferred($propertyName) {
+
+ $preferred = null;
+ $lastPref = 101;
+ foreach ($this->select($propertyName) as $field) {
+
+ $pref = 101;
+ if (isset($field['TYPE']) && $field['TYPE']->has('PREF')) {
+ $pref = 1;
+ } elseif (isset($field['PREF'])) {
+ $pref = $field['PREF']->getValue();
+ }
+
+ if ($pref < $lastPref || is_null($preferred)) {
+ $preferred = $field;
+ $lastPref = $pref;
+ }
+
+ }
+ return $preferred;
+
+ }
+
+ /**
+ * Returns a property with a specific TYPE value (ADR, TEL, or EMAIL).
+ *
+ * This function will return null if the property does not exist. If there are
+ * multiple properties with the same TYPE value, only one will be returned.
+ *
+ * @param string $propertyName
+ * @param string $type
+ *
+ * @return VObject\Property|null
+ */
+ function getByType($propertyName, $type) {
+ foreach ($this->select($propertyName) as $field) {
+ if (isset($field['TYPE']) && $field['TYPE']->has($type)) {
+ return $field;
+ }
+ }
+ }
+
+ /**
+ * This method should return a list of default property values.
+ *
+ * @return array
+ */
+ protected function getDefaults() {
+
+ return [
+ 'VERSION' => '4.0',
+ 'PRODID' => '-//Sabre//Sabre VObject ' . VObject\Version::VERSION . '//EN',
+ 'UID' => 'sabre-vobject-' . VObject\UUIDUtil::getUUID(),
+ ];
+
+ }
+
+ /**
+ * This method returns an array, with the representation as it should be
+ * encoded in json. This is used to create jCard or jCal documents.
+ *
+ * @return array
+ */
+ function jsonSerialize() {
+
+ // A vcard does not have sub-components, so we're overriding this
+ // method to remove that array element.
+ $properties = [];
+
+ foreach ($this->children() as $child) {
+ $properties[] = $child->jsonSerialize();
+ }
+
+ return [
+ strtolower($this->name),
+ $properties,
+ ];
+
+ }
+
+ /**
+ * This method serializes the data into XML. This is used to create xCard or
+ * xCal documents.
+ *
+ * @param Xml\Writer $writer XML writer.
+ *
+ * @return void
+ */
+ function xmlSerialize(Xml\Writer $writer) {
+
+ $propertiesByGroup = [];
+
+ foreach ($this->children() as $property) {
+
+ $group = $property->group;
+
+ if (!isset($propertiesByGroup[$group])) {
+ $propertiesByGroup[$group] = [];
+ }
+
+ $propertiesByGroup[$group][] = $property;
+
+ }
+
+ $writer->startElement(strtolower($this->name));
+
+ foreach ($propertiesByGroup as $group => $properties) {
+
+ if (!empty($group)) {
+
+ $writer->startElement('group');
+ $writer->writeAttribute('name', strtolower($group));
+
+ }
+
+ foreach ($properties as $property) {
+ switch ($property->name) {
+
+ case 'VERSION':
+ continue;
+
+ case 'XML':
+ $value = $property->getParts();
+ $fragment = new Xml\Element\XmlFragment($value[0]);
+ $writer->write($fragment);
+ break;
+
+ default:
+ $property->xmlSerialize($writer);
+ break;
+
+ }
+ }
+
+ if (!empty($group)) {
+ $writer->endElement();
+ }
+
+ }
+
+ $writer->endElement();
+
+ }
+
+ /**
+ * Returns the default class for a property name.
+ *
+ * @param string $propertyName
+ *
+ * @return string
+ */
+ function getClassNameForPropertyName($propertyName) {
+
+ $className = parent::getClassNameForPropertyName($propertyName);
+
+ // In vCard 4, BINARY no longer exists, and we need URI instead.
+ if ($className == 'Sabre\\VObject\\Property\\Binary' && $this->getDocumentType() === self::VCARD40) {
+ return 'Sabre\\VObject\\Property\\Uri';
+ }
+ return $className;
+
+ }
+
+}
diff --git a/vendor/sabre/vobject/lib/Component/VEvent.php b/vendor/sabre/vobject/lib/Component/VEvent.php
new file mode 100644
index 000000000..7f6861190
--- /dev/null
+++ b/vendor/sabre/vobject/lib/Component/VEvent.php
@@ -0,0 +1,153 @@
+<?php
+
+namespace Sabre\VObject\Component;
+
+use DateTimeInterface;
+use Sabre\VObject;
+use Sabre\VObject\Recur\EventIterator;
+use Sabre\VObject\Recur\NoInstancesException;
+
+/**
+ * VEvent component.
+ *
+ * This component contains some additional functionality specific for VEVENT's.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/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 DateTimeInterface $start
+ * @param DateTimeInterface $end
+ *
+ * @return bool
+ */
+ function isInTimeRange(DateTimeInterface $start, DateTimeInterface $end) {
+
+ if ($this->RRULE) {
+
+ try {
+
+ $it = new EventIterator($this, null, $start->getTimezone());
+
+ } catch (NoInstancesException $e) {
+
+ // If we've catched this exception, there are no instances
+ // for the event that fall into the specified time-range.
+ return false;
+
+ }
+
+ $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($start->getTimezone());
+ 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($end->getTimezone());
+
+ } elseif (isset($this->DURATION)) {
+ $effectiveEnd = $effectiveStart->add(VObject\DateTimeParser::parseDuration($this->DURATION));
+ } elseif (!$this->DTSTART->hasTime()) {
+ $effectiveEnd = $effectiveStart->modify('+1 day');
+ } else {
+ $effectiveEnd = $effectiveStart;
+ }
+ return (
+ ($start < $effectiveEnd) && ($end > $effectiveStart)
+ );
+
+ }
+
+ /**
+ * This method should return a list of default property values.
+ *
+ * @return array
+ */
+ protected function getDefaults() {
+
+ return [
+ 'UID' => 'sabre-vobject-' . VObject\UUIDUtil::getUUID(),
+ 'DTSTAMP' => date('Ymd\\THis\\Z'),
+ ];
+
+ }
+
+ /**
+ * A simple list of validation rules.
+ *
+ * This is simply a list of properties, and how many times they either
+ * must or must not appear.
+ *
+ * Possible values per property:
+ * * 0 - Must not appear.
+ * * 1 - Must appear exactly once.
+ * * + - Must appear at least once.
+ * * * - Can appear any number of times.
+ * * ? - May appear, but not more than once.
+ *
+ * @var array
+ */
+ function getValidationRules() {
+
+ $hasMethod = isset($this->parent->METHOD);
+ return [
+ 'UID' => 1,
+ 'DTSTAMP' => 1,
+ 'DTSTART' => $hasMethod ? '?' : '1',
+ 'CLASS' => '?',
+ 'CREATED' => '?',
+ 'DESCRIPTION' => '?',
+ 'GEO' => '?',
+ 'LAST-MODIFIED' => '?',
+ 'LOCATION' => '?',
+ 'ORGANIZER' => '?',
+ 'PRIORITY' => '?',
+ 'SEQUENCE' => '?',
+ 'STATUS' => '?',
+ 'SUMMARY' => '?',
+ 'TRANSP' => '?',
+ 'URL' => '?',
+ 'RECURRENCE-ID' => '?',
+ 'RRULE' => '?',
+ 'DTEND' => '?',
+ 'DURATION' => '?',
+
+ 'ATTACH' => '*',
+ 'ATTENDEE' => '*',
+ 'CATEGORIES' => '*',
+ 'COMMENT' => '*',
+ 'CONTACT' => '*',
+ 'EXDATE' => '*',
+ 'REQUEST-STATUS' => '*',
+ 'RELATED-TO' => '*',
+ 'RESOURCES' => '*',
+ 'RDATE' => '*',
+ ];
+
+ }
+
+}
diff --git a/vendor/sabre/vobject/lib/Component/VFreeBusy.php b/vendor/sabre/vobject/lib/Component/VFreeBusy.php
new file mode 100644
index 000000000..72294cc9f
--- /dev/null
+++ b/vendor/sabre/vobject/lib/Component/VFreeBusy.php
@@ -0,0 +1,102 @@
+<?php
+
+namespace Sabre\VObject\Component;
+
+use DateTimeInterface;
+use Sabre\VObject;
+
+/**
+ * The VFreeBusy component.
+ *
+ * This component adds functionality to a component, specific for VFREEBUSY
+ * components.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class VFreeBusy extends VObject\Component {
+
+ /**
+ * Checks based on the contained FREEBUSY information, if a timeslot is
+ * available.
+ *
+ * @param DateTimeInterface $start
+ * @param DateTimeInterface $end
+ *
+ * @return bool
+ */
+ function isFree(DateTimeInterface $start, DatetimeInterface $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) {
+ $busyEnd = $busyStart->add($busyEnd);
+ }
+
+ if ($start < $busyEnd && $end > $busyStart) {
+ return false;
+ }
+
+ }
+
+ }
+
+ return true;
+
+ }
+
+ /**
+ * A simple list of validation rules.
+ *
+ * This is simply a list of properties, and how many times they either
+ * must or must not appear.
+ *
+ * Possible values per property:
+ * * 0 - Must not appear.
+ * * 1 - Must appear exactly once.
+ * * + - Must appear at least once.
+ * * * - Can appear any number of times.
+ * * ? - May appear, but not more than once.
+ *
+ * @var array
+ */
+ function getValidationRules() {
+
+ return [
+ 'UID' => 1,
+ 'DTSTAMP' => 1,
+
+ 'CONTACT' => '?',
+ 'DTSTART' => '?',
+ 'DTEND' => '?',
+ 'ORGANIZER' => '?',
+ 'URL' => '?',
+
+ 'ATTENDEE' => '*',
+ 'COMMENT' => '*',
+ 'FREEBUSY' => '*',
+ 'REQUEST-STATUS' => '*',
+ ];
+
+ }
+
+}
diff --git a/vendor/sabre/vobject/lib/Component/VJournal.php b/vendor/sabre/vobject/lib/Component/VJournal.php
new file mode 100644
index 000000000..a1b1a863d
--- /dev/null
+++ b/vendor/sabre/vobject/lib/Component/VJournal.php
@@ -0,0 +1,107 @@
+<?php
+
+namespace Sabre\VObject\Component;
+
+use DateTimeInterface;
+use Sabre\VObject;
+
+/**
+ * VJournal component.
+ *
+ * This component contains some additional functionality specific for VJOURNALs.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/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 DateTimeInterface $start
+ * @param DateTimeInterface $end
+ *
+ * @return bool
+ */
+ function isInTimeRange(DateTimeInterface $start, DateTimeInterface $end) {
+
+ $dtstart = isset($this->DTSTART) ? $this->DTSTART->getDateTime() : null;
+ if ($dtstart) {
+ $effectiveEnd = $dtstart;
+ if (!$this->DTSTART->hasTime()) {
+ $effectiveEnd = $effectiveEnd->modify('+1 day');
+ }
+
+ return ($start <= $effectiveEnd && $end > $dtstart);
+
+ }
+ return false;
+
+ }
+
+ /**
+ * A simple list of validation rules.
+ *
+ * This is simply a list of properties, and how many times they either
+ * must or must not appear.
+ *
+ * Possible values per property:
+ * * 0 - Must not appear.
+ * * 1 - Must appear exactly once.
+ * * + - Must appear at least once.
+ * * * - Can appear any number of times.
+ * * ? - May appear, but not more than once.
+ *
+ * @var array
+ */
+ function getValidationRules() {
+
+ return [
+ 'UID' => 1,
+ 'DTSTAMP' => 1,
+
+ 'CLASS' => '?',
+ 'CREATED' => '?',
+ 'DTSTART' => '?',
+ 'LAST-MODIFIED' => '?',
+ 'ORGANIZER' => '?',
+ 'RECURRENCE-ID' => '?',
+ 'SEQUENCE' => '?',
+ 'STATUS' => '?',
+ 'SUMMARY' => '?',
+ 'URL' => '?',
+
+ 'RRULE' => '?',
+
+ 'ATTACH' => '*',
+ 'ATTENDEE' => '*',
+ 'CATEGORIES' => '*',
+ 'COMMENT' => '*',
+ 'CONTACT' => '*',
+ 'DESCRIPTION' => '*',
+ 'EXDATE' => '*',
+ 'RELATED-TO' => '*',
+ 'RDATE' => '*',
+ ];
+
+ }
+
+ /**
+ * This method should return a list of default property values.
+ *
+ * @return array
+ */
+ protected function getDefaults() {
+
+ return [
+ 'UID' => 'sabre-vobject-' . VObject\UUIDUtil::getUUID(),
+ 'DTSTAMP' => date('Ymd\\THis\\Z'),
+ ];
+
+ }
+}
diff --git a/vendor/sabre/vobject/lib/Component/VTimeZone.php b/vendor/sabre/vobject/lib/Component/VTimeZone.php
new file mode 100644
index 000000000..d5d886947
--- /dev/null
+++ b/vendor/sabre/vobject/lib/Component/VTimeZone.php
@@ -0,0 +1,67 @@
+<?php
+
+namespace Sabre\VObject\Component;
+
+use Sabre\VObject;
+
+/**
+ * The VTimeZone component.
+ *
+ * This component adds functionality to a component, specific for VTIMEZONE
+ * components.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class VTimeZone extends VObject\Component {
+
+ /**
+ * Returns the PHP DateTimeZone for this VTIMEZONE component.
+ *
+ * If we can't accurately determine the timezone, this method will return
+ * UTC.
+ *
+ * @return \DateTimeZone
+ */
+ function getTimeZone() {
+
+ return VObject\TimeZoneUtil::getTimeZone((string)$this->TZID, $this->root);
+
+ }
+
+ /**
+ * A simple list of validation rules.
+ *
+ * This is simply a list of properties, and how many times they either
+ * must or must not appear.
+ *
+ * Possible values per property:
+ * * 0 - Must not appear.
+ * * 1 - Must appear exactly once.
+ * * + - Must appear at least once.
+ * * * - Can appear any number of times.
+ * * ? - May appear, but not more than once.
+ *
+ * @var array
+ */
+ function getValidationRules() {
+
+ return [
+ 'TZID' => 1,
+
+ 'LAST-MODIFIED' => '?',
+ 'TZURL' => '?',
+
+ // At least 1 STANDARD or DAYLIGHT must appear, or more. But both
+ // cannot appear in the same VTIMEZONE.
+ //
+ // The validator is not specific yet to pick this up, so these
+ // rules are too loose.
+ 'STANDARD' => '*',
+ 'DAYLIGHT' => '*',
+ ];
+
+ }
+
+}
diff --git a/vendor/sabre/vobject/lib/Component/VTodo.php b/vendor/sabre/vobject/lib/Component/VTodo.php
new file mode 100644
index 000000000..144ced694
--- /dev/null
+++ b/vendor/sabre/vobject/lib/Component/VTodo.php
@@ -0,0 +1,193 @@
+<?php
+
+namespace Sabre\VObject\Component;
+
+use DateTimeInterface;
+use Sabre\VObject;
+
+/**
+ * VTodo component.
+ *
+ * This component contains some additional functionality specific for VTODOs.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/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 DateTimeInterface $start
+ * @param DateTimeInterface $end
+ *
+ * @return bool
+ */
+ function isInTimeRange(DateTimeInterface $start, DateTimeInterface $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 = $dtstart->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;
+
+ }
+
+ /**
+ * A simple list of validation rules.
+ *
+ * This is simply a list of properties, and how many times they either
+ * must or must not appear.
+ *
+ * Possible values per property:
+ * * 0 - Must not appear.
+ * * 1 - Must appear exactly once.
+ * * + - Must appear at least once.
+ * * * - Can appear any number of times.
+ * * ? - May appear, but not more than once.
+ *
+ * @var array
+ */
+ function getValidationRules() {
+
+ return [
+ 'UID' => 1,
+ 'DTSTAMP' => 1,
+
+ 'CLASS' => '?',
+ 'COMPLETED' => '?',
+ 'CREATED' => '?',
+ 'DESCRIPTION' => '?',
+ 'DTSTART' => '?',
+ 'GEO' => '?',
+ 'LAST-MODIFIED' => '?',
+ 'LOCATION' => '?',
+ 'ORGANIZER' => '?',
+ 'PERCENT' => '?',
+ 'PRIORITY' => '?',
+ 'RECURRENCE-ID' => '?',
+ 'SEQUENCE' => '?',
+ 'STATUS' => '?',
+ 'SUMMARY' => '?',
+ 'URL' => '?',
+
+ 'RRULE' => '?',
+ 'DUE' => '?',
+ 'DURATION' => '?',
+
+ 'ATTACH' => '*',
+ 'ATTENDEE' => '*',
+ 'CATEGORIES' => '*',
+ 'COMMENT' => '*',
+ 'CONTACT' => '*',
+ 'EXDATE' => '*',
+ 'REQUEST-STATUS' => '*',
+ 'RELATED-TO' => '*',
+ 'RESOURCES' => '*',
+ 'RDATE' => '*',
+ ];
+
+ }
+
+ /**
+ * Validates the node for correctness.
+ *
+ * The following options are supported:
+ * Node::REPAIR - May attempt to automatically repair the problem.
+ *
+ * This method returns an array with detected problems.
+ * Every element has the following properties:
+ *
+ * * level - problem level.
+ * * message - A human-readable string describing the issue.
+ * * node - A reference to the problematic node.
+ *
+ * The level means:
+ * 1 - The issue was repaired (only happens if REPAIR was turned on)
+ * 2 - An inconsequential issue
+ * 3 - A severe issue.
+ *
+ * @param int $options
+ *
+ * @return array
+ */
+ function validate($options = 0) {
+
+ $result = parent::validate($options);
+ if (isset($this->DUE) && isset($this->DTSTART)) {
+
+ $due = $this->DUE;
+ $dtStart = $this->DTSTART;
+
+ if ($due->getValueType() !== $dtStart->getValueType()) {
+
+ $result[] = [
+ 'level' => 3,
+ 'message' => 'The value type (DATE or DATE-TIME) must be identical for DUE and DTSTART',
+ 'node' => $due,
+ ];
+
+ } elseif ($due->getDateTime() < $dtStart->getDateTime()) {
+
+ $result[] = [
+ 'level' => 3,
+ 'message' => 'DUE must occur after DTSTART',
+ 'node' => $due,
+ ];
+
+ }
+
+ }
+
+ return $result;
+
+ }
+
+ /**
+ * This method should return a list of default property values.
+ *
+ * @return array
+ */
+ protected function getDefaults() {
+
+ return [
+ 'UID' => 'sabre-vobject-' . VObject\UUIDUtil::getUUID(),
+ 'DTSTAMP' => date('Ymd\\THis\\Z'),
+ ];
+
+ }
+
+}