Fix various compatibility issues with PHP 8

This commit is contained in:
Frédéric Guillot
2022-02-08 21:44:00 -08:00
committed by Frédéric Guillot
parent f5bb55bdb8
commit 4bf3b0d459
411 changed files with 99 additions and 26046 deletions

184
libs/ical/Component.php Normal file
View File

@@ -0,0 +1,184 @@
<?php
/*
* This file is part of the eluceo/iCal package.
*
* (c) Markus Poerschke <markus@eluceo.de>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Eluceo\iCal;
use Eluceo\iCal\Util\ComponentUtil;
/**
* Abstract Calender Component.
*/
abstract class Component
{
/**
* Array of Components.
*
* @var Component[]
*/
protected $components = [];
/**
* The order in which the components will be rendered during build.
*
* Not defined components will be appended at the end.
*
* @var array
*/
private $componentsBuildOrder = ['VTIMEZONE', 'DAYLIGHT', 'STANDARD'];
/**
* The type of the concrete Component.
*
* @abstract
*
* @return string
*/
abstract public function getType();
/**
* Building the PropertyBag.
*
* @abstract
*
* @return PropertyBag
*/
abstract public function buildPropertyBag();
/**
* Adds a Component.
*
* If $key is given, the component at $key will be replaced else the component will be append.
*
* @param Component $component The Component that will be added
* @param null $key The key of the Component
*/
public function addComponent(self $component, $key = null)
{
if (null == $key) {
$this->components[] = $component;
} else {
$this->components[$key] = $component;
}
}
/**
* Set all Components.
*
* @param Component[] $components The array of Component that will be set
* @param null $key The key of the Component
*/
public function setComponents(array $components)
{
$this->components = $components;
return $this;
}
/**
* Renders an array containing the lines of the iCal file.
*
* @return array
*/
public function build()
{
$lines = [];
$lines[] = sprintf('BEGIN:%s', $this->getType());
/** @var $property Property */
foreach ($this->buildPropertyBag() as $property) {
foreach ($property->toLines() as $l) {
$lines[] = $l;
}
}
$this->buildComponents($lines);
$lines[] = sprintf('END:%s', $this->getType());
$ret = [];
foreach ($lines as $line) {
foreach (ComponentUtil::fold($line) as $l) {
$ret[] = $l;
}
}
return $ret;
}
/**
* Renders the output.
*
* @return string
*/
public function render()
{
return implode("\r\n", $this->build());
}
/**
* Renders the output when treating the class as a string.
*
* @return string
*/
public function __toString()
{
return $this->render();
}
/**
* @param $lines
*
* @return array
*/
private function buildComponents(array &$lines)
{
$componentsByType = [];
/** @var $component Component */
foreach ($this->components as $component) {
$type = $component->getType();
if (!isset($componentsByType[$type])) {
$componentsByType[$type] = [];
}
$componentsByType[$type][] = $component;
}
// render ordered components
foreach ($this->componentsBuildOrder as $type) {
if (!isset($componentsByType[$type])) {
continue;
}
foreach ($componentsByType[$type] as $component) {
$this->addComponentLines($lines, $component);
}
unset($componentsByType[$type]);
}
// render all other
foreach ($componentsByType as $components) {
foreach ($components as $component) {
$this->addComponentLines($lines, $component);
}
}
}
/**
* @param Component $component
*/
private function addComponentLines(array &$lines, self $component)
{
foreach ($component->build() as $l) {
$lines[] = $l;
}
}
}

View File

@@ -0,0 +1,150 @@
<?php
/*
* This file is part of the eluceo/iCal package.
*
* (c) Markus Poerschke <markus@eluceo.de>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Eluceo\iCal\Component;
use Eluceo\iCal\Component;
use Eluceo\iCal\PropertyBag;
/**
* Implementation of the VALARM component.
*/
class Alarm extends Component
{
/**
* Alarm ACTION property.
*
* According to RFC 5545: 3.8.6.1. Action
*
* @see http://tools.ietf.org/html/rfc5545#section-3.8.6.1
*/
const ACTION_AUDIO = 'AUDIO';
const ACTION_DISPLAY = 'DISPLAY';
const ACTION_EMAIL = 'EMAIL';
protected $action;
protected $repeat;
protected $duration;
protected $description;
protected $attendee;
protected $trigger;
public function getType()
{
return 'VALARM';
}
public function getAction()
{
return $this->action;
}
public function getRepeat()
{
return $this->repeat;
}
public function getDuration()
{
return $this->duration;
}
public function getDescription()
{
return $this->description;
}
public function getAttendee()
{
return $this->attendee;
}
public function getTrigger()
{
return $this->trigger;
}
public function setAction($action)
{
$this->action = $action;
return $this;
}
public function setRepeat($repeat)
{
$this->repeat = $repeat;
return $this;
}
public function setDuration($duration)
{
$this->duration = $duration;
return $this;
}
public function setDescription($description)
{
$this->description = $description;
return $this;
}
public function setAttendee($attendee)
{
$this->attendee = $attendee;
return $this;
}
public function setTrigger($trigger)
{
$this->trigger = $trigger;
return $this;
}
/**
* {@inheritdoc}
*/
public function buildPropertyBag()
{
$propertyBag = new PropertyBag();
if (null != $this->trigger) {
$propertyBag->set('TRIGGER', $this->trigger);
}
if (null != $this->action) {
$propertyBag->set('ACTION', $this->action);
}
if (null != $this->repeat) {
$propertyBag->set('REPEAT', $this->repeat);
}
if (null != $this->duration) {
$propertyBag->set('DURATION', $this->duration);
}
if (null != $this->description) {
$propertyBag->set('DESCRIPTION', $this->description);
}
if (null != $this->attendee) {
$propertyBag->set('ATTENDEE', $this->attendee);
}
return $propertyBag;
}
}

View File

@@ -0,0 +1,324 @@
<?php
/*
* This file is part of the eluceo/iCal package.
*
* (c) Markus Poerschke <markus@eluceo.de>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Eluceo\iCal\Component;
use Eluceo\iCal\Component;
use Eluceo\iCal\PropertyBag;
class Calendar extends Component
{
/**
* Methods for calendar components.
*
* According to RFC 5545: 3.7.2. Method
*
* @see http://tools.ietf.org/html/rfc5545#section-3.7.2
*
* And then according to RFC 2446: 3 APPLICATION PROTOCOL ELEMENTS
* @see https://tools.ietf.org/html/rfc2446#section-3.2
*/
const METHOD_PUBLISH = 'PUBLISH';
const METHOD_REQUEST = 'REQUEST';
const METHOD_REPLY = 'REPLY';
const METHOD_ADD = 'ADD';
const METHOD_CANCEL = 'CANCEL';
const METHOD_REFRESH = 'REFRESH';
const METHOD_COUNTER = 'COUNTER';
const METHOD_DECLINECOUNTER = 'DECLINECOUNTER';
/**
* This property defines the calendar scale used for the calendar information specified in the iCalendar object.
*
* According to RFC 5545: 3.7.1. Calendar Scale
*
* @see http://tools.ietf.org/html/rfc5545#section-3.7
*/
const CALSCALE_GREGORIAN = 'GREGORIAN';
/**
* The Product Identifier.
*
* According to RFC 5545: 3.7.3 Product Identifier
*
* This property specifies the identifier for the product that created the Calendar object.
*
* @see https://tools.ietf.org/html/rfc5545#section-3.7.3
*
* @var string
*/
protected $prodId = null;
protected $method = null;
protected $name = null;
protected $description = null;
protected $timezone = null;
/**
* This property defines the calendar scale used for the
* calendar information specified in the iCalendar object.
*
* Also identifies the calendar type of a non-Gregorian recurring appointment.
*
* @var string
*
* @see http://tools.ietf.org/html/rfc5545#section-3.7
* @see http://msdn.microsoft.com/en-us/library/ee237520(v=exchg.80).aspx
*/
protected $calendarScale = null;
/**
* Specifies whether or not the iCalendar file only contains one appointment.
*
* @var bool
*
* @see http://msdn.microsoft.com/en-us/library/ee203486(v=exchg.80).aspx
*/
protected $forceInspectOrOpen = false;
/**
* Specifies a globally unique identifier for the calendar.
*
* @var string
*
* @see http://msdn.microsoft.com/en-us/library/ee179588(v=exchg.80).aspx
*/
protected $calId = null;
/**
* Specifies a suggested iCalendar file download frequency for clients and
* servers with sync capabilities.
*
* For example you can set the value to 'P1W' if the calendar should be
* synced once a week. Use 'P3H' to sync the file every 3 hours.
*
* @var string
*
* @see http://msdn.microsoft.com/en-us/library/ee178699(v=exchg.80).aspx
*/
protected $publishedTTL = null;
/**
* Specifies a color for the calendar in calendar for Apple/Outlook.
*
* @var string
*
* @see http://msdn.microsoft.com/en-us/library/ee179588(v=exchg.80).aspx
*/
protected $calendarColor = null;
public function __construct($prodId)
{
if (empty($prodId)) {
throw new \UnexpectedValueException('PRODID cannot be empty');
}
$this->prodId = $prodId;
}
/**
* {@inheritdoc}
*/
public function getType()
{
return 'VCALENDAR';
}
/**
* @param $method
*
* @return $this
*/
public function setMethod($method)
{
$this->method = $method;
return $this;
}
/**
* @param $name
*
* @return $this
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* @param $description
*
* @return $this
*/
public function setDescription($description)
{
$this->description = $description;
return $this;
}
/**
* @param $timezone
*
* @return $this
*/
public function setTimezone($timezone)
{
$this->timezone = $timezone;
return $this;
}
/**
* @param $calendarColor
*
* @return $this
*/
public function setCalendarColor($calendarColor)
{
$this->calendarColor = $calendarColor;
return $this;
}
/**
* @param $calendarScale
*
* @return $this
*/
public function setCalendarScale($calendarScale)
{
$this->calendarScale = $calendarScale;
return $this;
}
/**
* @param bool $forceInspectOrOpen
*
* @return $this
*/
public function setForceInspectOrOpen($forceInspectOrOpen)
{
$this->forceInspectOrOpen = $forceInspectOrOpen;
return $this;
}
/**
* @param string $calId
*
* @return $this
*/
public function setCalId($calId)
{
$this->calId = $calId;
return $this;
}
/**
* @param string $ttl
*
* @return $this
*/
public function setPublishedTTL($ttl)
{
$this->publishedTTL = $ttl;
return $this;
}
/**
* {@inheritdoc}
*/
public function buildPropertyBag()
{
$propertyBag = new PropertyBag();
$propertyBag->set('VERSION', '2.0');
$propertyBag->set('PRODID', $this->prodId);
if ($this->method) {
$propertyBag->set('METHOD', $this->method);
}
if ($this->calendarColor) {
$propertyBag->set('X-APPLE-CALENDAR-COLOR', $this->calendarColor);
$propertyBag->set('X-OUTLOOK-COLOR', $this->calendarColor);
$propertyBag->set('X-FUNAMBOL-COLOR', $this->calendarColor);
}
if ($this->calendarScale) {
$propertyBag->set('CALSCALE', $this->calendarScale);
$propertyBag->set('X-MICROSOFT-CALSCALE', $this->calendarScale);
}
if ($this->name) {
$propertyBag->set('X-WR-CALNAME', $this->name);
}
if ($this->description) {
$propertyBag->set('X-WR-CALDESC', $this->description);
}
if ($this->timezone) {
if ($this->timezone instanceof Timezone) {
$propertyBag->set('X-WR-TIMEZONE', $this->timezone->getZoneIdentifier());
$this->addComponent($this->timezone);
} else {
$propertyBag->set('X-WR-TIMEZONE', $this->timezone);
$this->addComponent(new Timezone($this->timezone));
}
}
if ($this->forceInspectOrOpen) {
$propertyBag->set('X-MS-OLK-FORCEINSPECTOROPEN', $this->forceInspectOrOpen);
}
if ($this->calId) {
$propertyBag->set('X-WR-RELCALID', $this->calId);
}
if ($this->publishedTTL) {
$propertyBag->set('X-PUBLISHED-TTL', $this->publishedTTL);
}
return $propertyBag;
}
/**
* Adds an Event to the Calendar.
*
* Wrapper for addComponent()
*
* @see Eluceo\iCal::addComponent
* @deprecated Please, use public method addComponent() from abstract Component class
*/
public function addEvent(Event $event)
{
$this->addComponent($event);
}
/**
* @return string|null
*/
public function getProdId()
{
return $this->prodId;
}
public function getMethod()
{
return $this->method;
}
}

View File

@@ -0,0 +1,941 @@
<?php
/*
* This file is part of the eluceo/iCal package.
*
* (c) Markus Poerschke <markus@eluceo.de>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Eluceo\iCal\Component;
use Eluceo\iCal\Component;
use Eluceo\iCal\Property;
use Eluceo\iCal\Property\DateTimeProperty;
use Eluceo\iCal\Property\DateTimesProperty;
use Eluceo\iCal\Property\Event\Attachment;
use Eluceo\iCal\Property\Event\Attendees;
use Eluceo\iCal\Property\Event\Geo;
use Eluceo\iCal\Property\Event\Organizer;
use Eluceo\iCal\Property\Event\RecurrenceId;
use Eluceo\iCal\Property\Event\RecurrenceRule;
use Eluceo\iCal\Property\RawStringValue;
use Eluceo\iCal\PropertyBag;
/**
* Implementation of the EVENT component.
*/
class Event extends Component
{
const TIME_TRANSPARENCY_OPAQUE = 'OPAQUE';
const TIME_TRANSPARENCY_TRANSPARENT = 'TRANSPARENT';
const STATUS_TENTATIVE = 'TENTATIVE';
const STATUS_CONFIRMED = 'CONFIRMED';
const STATUS_CANCELLED = 'CANCELLED';
const MS_BUSYSTATUS_FREE = 'FREE';
const MS_BUSYSTATUS_TENTATIVE = 'TENTATIVE';
const MS_BUSYSTATUS_BUSY = 'BUSY';
const MS_BUSYSTATUS_OOF = 'OOF';
/**
* @var string
*/
protected $uniqueId;
/**
* The property indicates the date/time that the instance of
* the iCalendar object was created.
*
* The value MUST be specified in the UTC time format.
*
* @var \DateTime
*/
protected $dtStamp;
/**
* @var \DateTime
*/
protected $dtStart;
/**
* Preferentially chosen over the duration if both are set.
*
* @var \DateTime
*/
protected $dtEnd;
/**
* @var \DateInterval
*/
protected $duration;
/**
* @var bool
*/
protected $noTime = false;
/**
* @var string
*/
protected $msBusyStatus = null;
/**
* @var string
*/
protected $url;
/**
* @var string
*/
protected $location;
/**
* @var string
*/
protected $locationTitle;
/**
* @var Geo
*/
protected $locationGeo;
/**
* @var string
*/
protected $summary;
/**
* @var Organizer
*/
protected $organizer;
/**
* @see https://tools.ietf.org/html/rfc5545#section-3.8.2.7
*
* @var string
*/
protected $transparency = self::TIME_TRANSPARENCY_OPAQUE;
/**
* If set to true the timezone will be added to the event.
*
* @var bool
*/
protected $useTimezone = false;
/**
* If set will be used as the timezone identifier.
*
* @var string
*/
protected $timezoneString = '';
/**
* @var int
*/
protected $sequence = 0;
/**
* @var Attendees
*/
protected $attendees;
/**
* @var string
*/
protected $description;
/**
* @var string
*/
protected $descriptionHTML;
/**
* @var string
*/
protected $status;
/**
* @var RecurrenceRule
*/
protected $recurrenceRule;
/**
* @var array
*/
protected $recurrenceRules = [];
/**
* This property specifies the date and time that the calendar
* information was created.
*
* The value MUST be specified in the UTC time format.
*
* @var \DateTime
*/
protected $created;
/**
* The property specifies the date and time that the information
* associated with the calendar component was last revised.
*
* The value MUST be specified in the UTC time format.
*
* @var \DateTime
*/
protected $modified;
/**
* Indicates if the UTC time should be used or not.
*
* @var bool
*/
protected $useUtc = true;
/**
* @var bool
*/
protected $cancelled;
/**
* This property is used to specify categories or subtypes
* of the calendar component. The categories are useful in searching
* for a calendar component of a particular type and category.
*
* @see https://tools.ietf.org/html/rfc5545#section-3.8.1.2
*
* @var array
*/
protected $categories;
/**
* https://tools.ietf.org/html/rfc5545#section-3.8.1.3.
*
* @var bool
*/
protected $isPrivate = false;
/**
* Dates to be excluded from a series of events.
*
* @var \DateTimeInterface[]
*/
protected $exDates = [];
/**
* @var RecurrenceId
*/
protected $recurrenceId;
/**
* @var Attachment[]
*/
protected $attachments = [];
public function __construct(string $uniqueId = null)
{
if (null == $uniqueId) {
$uniqueId = uniqid();
}
$this->uniqueId = $uniqueId;
$this->attendees = new Attendees();
}
/**
* {@inheritdoc}
*/
public function getType()
{
return 'VEVENT';
}
/**
* {@inheritdoc}
*/
public function buildPropertyBag()
{
$propertyBag = new PropertyBag();
// mandatory information
$propertyBag->set('UID', $this->uniqueId);
$propertyBag->add(new DateTimeProperty('DTSTART', $this->dtStart, $this->noTime, $this->useTimezone, $this->useUtc, $this->timezoneString));
$propertyBag->set('SEQUENCE', $this->sequence);
$propertyBag->set('TRANSP', $this->transparency);
if ($this->status) {
$propertyBag->set('STATUS', $this->status);
}
// An event can have a 'dtend' or 'duration', but not both.
if ($this->dtEnd !== null) {
$dtEnd = clone $this->dtEnd;
if ($this->noTime === true) {
$dtEnd = $dtEnd->add(new \DateInterval('P1D'));
}
$propertyBag->add(new DateTimeProperty('DTEND', $dtEnd, $this->noTime, $this->useTimezone, $this->useUtc, $this->timezoneString));
} elseif ($this->duration !== null) {
$propertyBag->set('DURATION', $this->duration->format('P%dDT%hH%iM%sS'));
}
// optional information
if (null != $this->url) {
$propertyBag->set('URL', $this->url);
}
if (null != $this->location) {
$propertyBag->set('LOCATION', $this->location);
if (null != $this->locationGeo) {
$propertyBag->add(
new Property(
'X-APPLE-STRUCTURED-LOCATION',
new RawStringValue('geo:' . $this->locationGeo->getGeoLocationAsString(',')),
[
'VALUE' => 'URI',
'X-ADDRESS' => $this->location,
'X-APPLE-RADIUS' => 49,
'X-TITLE' => $this->locationTitle,
]
)
);
}
}
if (null != $this->locationGeo) {
$propertyBag->add($this->locationGeo);
}
if (null != $this->summary) {
$propertyBag->set('SUMMARY', $this->summary);
}
if (null != $this->attendees) {
$propertyBag->add($this->attendees);
}
$propertyBag->set('CLASS', $this->isPrivate ? 'PRIVATE' : 'PUBLIC');
if (null != $this->description) {
$propertyBag->set('DESCRIPTION', $this->description);
}
if (null != $this->descriptionHTML) {
$propertyBag->add(
new Property(
'X-ALT-DESC',
$this->descriptionHTML,
[
'FMTTYPE' => 'text/html',
]
)
);
}
if (null != $this->recurrenceRule) {
$propertyBag->set('RRULE', $this->recurrenceRule);
}
foreach ($this->recurrenceRules as $recurrenceRule) {
$propertyBag->set('RRULE', $recurrenceRule);
}
if (null != $this->recurrenceId) {
$this->recurrenceId->applyTimeSettings($this->noTime, $this->useTimezone, $this->useUtc, $this->timezoneString);
$propertyBag->add($this->recurrenceId);
}
if (!empty($this->exDates)) {
$propertyBag->add(new DateTimesProperty('EXDATE', $this->exDates, $this->noTime, $this->useTimezone, $this->useUtc, $this->timezoneString));
}
if ($this->cancelled) {
$propertyBag->set('STATUS', 'CANCELLED');
}
if (null != $this->organizer) {
$propertyBag->add($this->organizer);
}
if ($this->noTime) {
$propertyBag->set('X-MICROSOFT-CDO-ALLDAYEVENT', 'TRUE');
}
if (null != $this->msBusyStatus) {
$propertyBag->set('X-MICROSOFT-CDO-BUSYSTATUS', $this->msBusyStatus);
$propertyBag->set('X-MICROSOFT-CDO-INTENDEDSTATUS', $this->msBusyStatus);
}
if (null != $this->categories) {
$propertyBag->set('CATEGORIES', $this->categories);
}
$propertyBag->add(
new DateTimeProperty('DTSTAMP', $this->dtStamp ?: new \DateTimeImmutable(), false, false, true)
);
if ($this->created) {
$propertyBag->add(new DateTimeProperty('CREATED', $this->created, false, false, true));
}
if ($this->modified) {
$propertyBag->add(new DateTimeProperty('LAST-MODIFIED', $this->modified, false, false, true));
}
foreach ($this->attachments as $attachment) {
$propertyBag->add($attachment);
}
return $propertyBag;
}
/**
* @param $dtEnd
*
* @return $this
*/
public function setDtEnd($dtEnd)
{
$this->dtEnd = $dtEnd;
return $this;
}
public function getDtEnd()
{
return $this->dtEnd;
}
public function setDtStart($dtStart)
{
$this->dtStart = $dtStart;
return $this;
}
public function getDtStart()
{
return $this->dtStart;
}
/**
* @param $dtStamp
*
* @return $this
*/
public function setDtStamp($dtStamp)
{
$this->dtStamp = $dtStamp;
return $this;
}
/**
* @param $duration
*
* @return $this
*/
public function setDuration($duration)
{
$this->duration = $duration;
return $this;
}
/**
* @param string $location
* @param string $title
* @param Geo|string $geo
*
* @return $this
*/
public function setLocation($location, $title = '', $geo = null)
{
if (is_scalar($geo)) {
$geo = Geo::fromString($geo);
} elseif (!is_null($geo) && !$geo instanceof Geo) {
$className = get_class($geo);
throw new \InvalidArgumentException("The parameter 'geo' must be a string or an instance of " . Geo::class . " but an instance of {$className} was given.");
}
$this->location = $location;
$this->locationTitle = $title;
$this->locationGeo = $geo;
return $this;
}
/**
* @return $this
*/
public function setGeoLocation(Geo $geoProperty)
{
$this->locationGeo = $geoProperty;
return $this;
}
/**
* @param $noTime
*
* @return $this
*/
public function setNoTime($noTime)
{
$this->noTime = $noTime;
return $this;
}
/**
* @param $msBusyStatus
*
* @return $this
*
* @throws \InvalidArgumentException
*/
public function setMsBusyStatus($msBusyStatus)
{
$msBusyStatus = strtoupper($msBusyStatus);
if ($msBusyStatus == self::MS_BUSYSTATUS_FREE
|| $msBusyStatus == self::MS_BUSYSTATUS_TENTATIVE
|| $msBusyStatus == self::MS_BUSYSTATUS_BUSY
|| $msBusyStatus == self::MS_BUSYSTATUS_OOF
) {
$this->msBusyStatus = $msBusyStatus;
} else {
throw new \InvalidArgumentException('Invalid value for status');
}
return $this;
}
/**
* @return string|null
*/
public function getMsBusyStatus()
{
return $this->msBusyStatus;
}
/**
* @param int $sequence
*
* @return $this
*/
public function setSequence($sequence)
{
$this->sequence = $sequence;
return $this;
}
/**
* @return int
*/
public function getSequence()
{
return $this->sequence;
}
/**
* @return $this
*/
public function setOrganizer(Organizer $organizer)
{
$this->organizer = $organizer;
return $this;
}
/**
* @param $summary
*
* @return $this
*/
public function setSummary($summary)
{
$this->summary = $summary;
return $this;
}
/**
* @param $uniqueId
*
* @return $this
*/
public function setUniqueId($uniqueId)
{
$this->uniqueId = $uniqueId;
return $this;
}
/**
* @return string
*/
public function getUniqueId()
{
return $this->uniqueId;
}
/**
* @param $url
*
* @return $this
*/
public function setUrl($url)
{
$this->url = $url;
return $this;
}
/**
* @param $useTimezone
*
* @return $this
*/
public function setUseTimezone($useTimezone)
{
$this->useTimezone = $useTimezone;
return $this;
}
/**
* @return bool
*/
public function getUseTimezone()
{
return $this->useTimezone;
}
/**
* @param $timezoneString
*
* @return $this
*/
public function setTimezoneString($timezoneString)
{
$this->timezoneString = $timezoneString;
return $this;
}
/**
* @return bool
*/
public function getTimezoneString()
{
return $this->timezoneString;
}
/**
* @return $this
*/
public function setAttendees(Attendees $attendees)
{
$this->attendees = $attendees;
return $this;
}
/**
* @param string $attendee
* @param array $params
*
* @return $this
*/
public function addAttendee($attendee, $params = [])
{
$this->attendees->add($attendee, $params);
return $this;
}
public function getAttendees(): Attendees
{
return $this->attendees;
}
/**
* @param $description
*
* @return $this
*/
public function setDescription($description)
{
$this->description = $description;
return $this;
}
/**
* @param $descriptionHTML
*
* @return $this
*/
public function setDescriptionHTML($descriptionHTML)
{
$this->descriptionHTML = $descriptionHTML;
return $this;
}
/**
* @param bool $useUtc
*
* @return $this
*/
public function setUseUtc($useUtc = true)
{
$this->useUtc = $useUtc;
return $this;
}
/**
* @return string
*/
public function getDescription()
{
return $this->description;
}
/**
* @return string
*/
public function getDescriptionHTML()
{
return $this->descriptionHTML;
}
/**
* @param $status
*
* @return $this
*/
public function setCancelled($status)
{
$this->cancelled = (bool) $status;
return $this;
}
/**
* @param $transparency
*
* @return $this
*
* @throws \InvalidArgumentException
*/
public function setTimeTransparency($transparency)
{
$transparency = strtoupper($transparency);
if ($transparency === self::TIME_TRANSPARENCY_OPAQUE
|| $transparency === self::TIME_TRANSPARENCY_TRANSPARENT
) {
$this->transparency = $transparency;
} else {
throw new \InvalidArgumentException('Invalid value for transparancy');
}
return $this;
}
/**
* @param $status
*
* @return $this
*
* @throws \InvalidArgumentException
*/
public function setStatus($status)
{
$status = strtoupper($status);
if ($status == self::STATUS_CANCELLED
|| $status == self::STATUS_CONFIRMED
|| $status == self::STATUS_TENTATIVE
) {
$this->status = $status;
} else {
throw new \InvalidArgumentException('Invalid value for status');
}
return $this;
}
/**
* @deprecated Deprecated since version 0.11.0, to be removed in 1.0. Use addRecurrenceRule instead.
*
* @return $this
*/
public function setRecurrenceRule(RecurrenceRule $recurrenceRule)
{
@trigger_error('setRecurrenceRule() is deprecated since version 0.11.0 and will be removed in 1.0. Use addRecurrenceRule instead.', E_USER_DEPRECATED);
$this->recurrenceRule = $recurrenceRule;
return $this;
}
/**
* @deprecated Deprecated since version 0.11.0, to be removed in 1.0. Use getRecurrenceRules instead.
*
* @return RecurrenceRule
*/
public function getRecurrenceRule()
{
@trigger_error('getRecurrenceRule() is deprecated since version 0.11.0 and will be removed in 1.0. Use getRecurrenceRules instead.', E_USER_DEPRECATED);
return $this->recurrenceRule;
}
/**
* @return $this
*/
public function addRecurrenceRule(RecurrenceRule $recurrenceRule)
{
$this->recurrenceRules[] = $recurrenceRule;
return $this;
}
/**
* @return array
*/
public function getRecurrenceRules()
{
return $this->recurrenceRules;
}
/**
* @param $dtStamp
*
* @return $this
*/
public function setCreated($dtStamp)
{
$this->created = $dtStamp;
return $this;
}
/**
* @param $dtStamp
*
* @return $this
*/
public function setModified($dtStamp)
{
$this->modified = $dtStamp;
return $this;
}
/**
* @param $categories
*
* @return $this
*/
public function setCategories($categories)
{
$this->categories = $categories;
return $this;
}
/**
* Sets the event privacy.
*
* @param bool $flag
*
* @return $this
*/
public function setIsPrivate($flag)
{
$this->isPrivate = (bool) $flag;
return $this;
}
/**
* @return \Eluceo\iCal\Component\Event
*/
public function addExDate(\DateTimeInterface $dateTime)
{
$this->exDates[] = $dateTime;
return $this;
}
/**
* @return \DateTimeInterface[]
*/
public function getExDates()
{
return $this->exDates;
}
/**
* @param \DateTimeInterface[]
*
* @return \Eluceo\iCal\Component\Event
*/
public function setExDates(array $exDates)
{
$this->exDates = $exDates;
return $this;
}
/**
* @return \Eluceo\iCal\Property\Event\RecurrenceId
*/
public function getRecurrenceId()
{
return $this->recurrenceId;
}
/**
* @return \Eluceo\iCal\Component\Event
*/
public function setRecurrenceId(RecurrenceId $recurrenceId)
{
$this->recurrenceId = $recurrenceId;
return $this;
}
/**
* @param array $attachment
*
* @return $this
*/
public function addAttachment(Attachment $attachment)
{
$this->attachments[] = $attachment;
return $this;
}
/**
* @return array
*/
public function getAttachments()
{
return $this->attachments;
}
public function addUrlAttachment(string $url)
{
$this->addAttachment(new Attachment($url));
}
}

View File

@@ -0,0 +1,57 @@
<?php
/*
* This file is part of the eluceo/iCal package.
*
* (c) Markus Poerschke <markus@eluceo.de>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Eluceo\iCal\Component;
use Eluceo\iCal\Component;
use Eluceo\iCal\PropertyBag;
/**
* Implementation of the TIMEZONE component.
*/
class Timezone extends Component
{
/**
* @var string
*/
protected $timezone;
public function __construct($timezone)
{
$this->timezone = $timezone;
}
/**
* {@inheritdoc}
*/
public function getType()
{
return 'VTIMEZONE';
}
/**
* {@inheritdoc}
*/
public function buildPropertyBag()
{
$propertyBag = new PropertyBag();
$propertyBag->set('TZID', $this->timezone);
$propertyBag->set('X-LIC-LOCATION', $this->timezone);
return $propertyBag;
}
public function getZoneIdentifier()
{
return $this->timezone;
}
}

View File

@@ -0,0 +1,211 @@
<?php
/*
* This file is part of the eluceo/iCal package.
*
* (c) Markus Poerschke <markus@eluceo.de>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Eluceo\iCal\Component;
use Eluceo\iCal\Component;
use Eluceo\iCal\Property\Event\RecurrenceRule;
use Eluceo\iCal\PropertyBag;
/**
* Implementation of Standard Time and Daylight Saving Time observances (or rules)
* which define the TIMEZONE component.
*/
class TimezoneRule extends Component
{
const TYPE_DAYLIGHT = 'DAYLIGHT';
const TYPE_STANDARD = 'STANDARD';
/**
* @var string
*/
protected $type;
/**
* @var string
*/
protected $tzOffsetFrom;
/**
* @var string
*/
protected $tzOffsetTo;
/**
* @var string
*/
protected $tzName;
/**
* @var \DateTimeInterface
*/
protected $dtStart;
/**
* @var RecurrenceRule
*/
protected $recurrenceRule;
/**
* create new Timezone Rule object by giving a rule type identifier.
*
* @param string $ruleType one of DAYLIGHT or STANDARD
*
* @throws \InvalidArgumentException
*/
public function __construct($ruleType)
{
$ruleType = strtoupper($ruleType);
if ($ruleType === self::TYPE_DAYLIGHT || $ruleType === self::TYPE_STANDARD) {
$this->type = $ruleType;
} else {
throw new \InvalidArgumentException('Invalid value for timezone rule type');
}
}
/**
* {@inheritdoc}
*/
public function buildPropertyBag()
{
$propertyBag = new PropertyBag();
if ($this->getTzName()) {
$propertyBag->set('TZNAME', $this->getTzName());
}
if ($this->getTzOffsetFrom()) {
$propertyBag->set('TZOFFSETFROM', $this->getTzOffsetFrom());
}
if ($this->getTzOffsetTo()) {
$propertyBag->set('TZOFFSETTO', $this->getTzOffsetTo());
}
if ($this->getDtStart()) {
$propertyBag->set('DTSTART', $this->getDtStart());
}
if ($this->recurrenceRule) {
$propertyBag->set('RRULE', $this->recurrenceRule);
}
return $propertyBag;
}
/**
* @param $offset
*
* @return $this
*/
public function setTzOffsetFrom($offset)
{
$this->tzOffsetFrom = $offset;
return $this;
}
/**
* @param $offset
*
* @return $this
*/
public function setTzOffsetTo($offset)
{
$this->tzOffsetTo = $offset;
return $this;
}
/**
* @param $name
*
* @return $this
*/
public function setTzName($name)
{
$this->tzName = $name;
return $this;
}
/**
* @return $this
*/
public function setDtStart(\DateTimeInterface $dtStart)
{
$this->dtStart = $dtStart;
return $this;
}
/**
* @return $this
*/
public function setRecurrenceRule(RecurrenceRule $recurrenceRule)
{
$this->recurrenceRule = $recurrenceRule;
return $this;
}
/**
* {@inheritdoc}
*/
public function getType()
{
return $this->type;
}
/**
* @return string
*/
public function getTzOffsetFrom()
{
return $this->tzOffsetFrom;
}
/**
* @return string
*/
public function getTzOffsetTo()
{
return $this->tzOffsetTo;
}
/**
* @return string
*/
public function getTzName()
{
return $this->tzName;
}
/**
* @return RecurrenceRule
*/
public function getRecurrenceRule()
{
return $this->recurrenceRule;
}
/**
* @return mixed return string representation of start date or NULL if no date was given
*/
public function getDtStart()
{
if ($this->dtStart) {
return $this->dtStart->format('Ymd\THis');
}
return;
}
}

19
libs/ical/LICENSE Normal file
View File

@@ -0,0 +1,19 @@
Copyright (c) 2012-2019 Markus Poerschke
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is furnished
to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

107
libs/ical/ParameterBag.php Normal file
View File

@@ -0,0 +1,107 @@
<?php
/*
* This file is part of the eluceo/iCal package.
*
* (c) Markus Poerschke <markus@eluceo.de>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Eluceo\iCal;
class ParameterBag
{
/**
* The params.
*
* @var array
*/
protected $params;
public function __construct($params = [])
{
$this->params = $params;
}
/**
* @param string $name
* @param mixed $value
*/
public function setParam($name, $value)
{
$this->params[$name] = $value;
}
/**
* @param $name
*
* @return array|mixed
*/
public function getParam($name)
{
if (isset($this->params[$name])) {
return $this->params[$name];
}
return null;
}
/**
* Checks if there are any params.
*/
public function hasParams(): bool
{
return count($this->params) > 0;
}
public function toString(): string
{
$line = '';
foreach ($this->params as $param => $paramValues) {
if (!is_array($paramValues)) {
$paramValues = [$paramValues];
}
foreach ($paramValues as $k => $v) {
$paramValues[$k] = $this->escapeParamValue($v);
}
if ('' != $line) {
$line .= ';';
}
$line .= $param . '=' . implode(',', $paramValues);
}
return $line;
}
/**
* Returns an escaped string for a param value.
*
* @param string $value
*
* @return string
*/
private function escapeParamValue($value)
{
$count = 0;
$value = str_replace('\\', '\\\\', $value);
$value = str_replace('"', '\"', $value, $count);
$value = str_replace("\n", '\\n', $value);
if (false !== strpos($value, ';') || false !== strpos($value, ',') || false !== strpos($value, ':') || $count) {
$value = '"' . $value . '"';
}
return $value;
}
/**
* @return string
*/
public function __toString()
{
return $this->toString();
}
}

147
libs/ical/Property.php Normal file
View File

@@ -0,0 +1,147 @@
<?php
/*
* This file is part of the eluceo/iCal package.
*
* (c) Markus Poerschke <markus@eluceo.de>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Eluceo\iCal;
use Eluceo\iCal\Property\ArrayValue;
use Eluceo\iCal\Property\StringValue;
use Eluceo\iCal\Property\ValueInterface;
/**
* The Property Class represents a property as defined in RFC 5545.
*
* The content of a line (unfolded) will be rendered in this class.
*
* @see https://tools.ietf.org/html/rfc5545#section-3.5
*/
class Property
{
/**
* The value of the Property.
*
* @var ValueInterface
*/
protected $value;
/**
* The params of the Property.
*
* @var ParameterBag
*/
protected $parameterBag;
/**
* @var string
*/
protected $name;
/**
* @param $name
* @param $value
* @param array $params
*/
public function __construct($name, $value, $params = [])
{
$this->name = $name;
$this->setValue($value);
$this->parameterBag = new ParameterBag($params);
}
/**
* Renders an unfolded line.
*
* @return string
*/
public function toLine()
{
// Property-name
$line = $this->getName();
// Adding params
//@todo added check for $this->parameterBag because doctrine/orm proxies won't execute constructor - ok?
if ($this->parameterBag && $this->parameterBag->hasParams()) {
$line .= ';' . $this->parameterBag->toString();
}
// Property value
$line .= ':' . $this->value->getEscapedValue();
return $line;
}
/**
* Get all unfolded lines.
*
* @return array
*/
public function toLines()
{
return [$this->toLine()];
}
/**
* @param string $name
* @param mixed $value
*
* @return $this
*/
public function setParam($name, $value)
{
$this->parameterBag->setParam($name, $value);
return $this;
}
/**
* @param $name
*/
public function getParam($name)
{
return $this->parameterBag->getParam($name);
}
/**
* @param mixed $value
*
* @return $this
*
* @throws \Exception
*/
public function setValue($value)
{
if (is_scalar($value)) {
$this->value = new StringValue($value);
} elseif (is_array($value)) {
$this->value = new ArrayValue($value);
} else {
if (!$value instanceof ValueInterface) {
throw new \Exception('The value must implement the ValueInterface.');
} else {
$this->value = $value;
}
}
return $this;
}
/**
* @return mixed
*/
public function getValue()
{
return $this->value;
}
public function getName(): string
{
return $this->name;
}
}

View File

@@ -0,0 +1,41 @@
<?php
/*
* This file is part of the eluceo/iCal package.
*
* (c) Markus Poerschke <markus@eluceo.de>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Eluceo\iCal\Property;
class ArrayValue implements ValueInterface
{
/**
* The value.
*
* @var array
*/
protected $values;
public function __construct(array $values)
{
$this->values = $values;
}
public function setValues(array $values)
{
$this->values = $values;
return $this;
}
public function getEscapedValue(): string
{
return implode(',', array_map(function (string $value): string {
return (new StringValue($value))->getEscapedValue();
}, $this->values));
}
}

View File

@@ -0,0 +1,40 @@
<?php
/*
* This file is part of the eluceo/iCal package.
*
* (c) Markus Poerschke <markus@eluceo.de>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Eluceo\iCal\Property;
use Eluceo\iCal\Property;
use Eluceo\iCal\Util\DateUtil;
class DateTimeProperty extends Property
{
/**
* @param string $name
* @param \DateTimeInterface $dateTime
* @param bool $noTime
* @param bool $useTimezone
* @param bool $useUtc
* @param string $timezoneString
*/
public function __construct(
$name,
\DateTimeInterface $dateTime = null,
$noTime = false,
$useTimezone = false,
$useUtc = false,
$timezoneString = ''
) {
$dateString = DateUtil::getDateString($dateTime, $noTime, $useTimezone, $useUtc);
$params = DateUtil::getDefaultParams($dateTime, $noTime, $useTimezone, $timezoneString);
parent::__construct($name, $dateString, $params);
}
}

View File

@@ -0,0 +1,46 @@
<?php
/*
* This file is part of the eluceo/iCal package.
*
* (c) Markus Poerschke <markus@eluceo.de>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Eluceo\iCal\Property;
use Eluceo\iCal\Property;
use Eluceo\iCal\Util\DateUtil;
class DateTimesProperty extends Property
{
/**
* @param string $name
* @param \DateTimeInterface[] $dateTimes
* @param bool $noTime
* @param bool $useTimezone
* @param bool $useUtc
* @param string $timezoneString
*/
public function __construct(
$name,
$dateTimes = [],
$noTime = false,
$useTimezone = false,
$useUtc = false,
$timezoneString = ''
) {
$dates = [];
$dateTime = new \DateTimeImmutable();
foreach ($dateTimes as $dateTime) {
$dates[] = DateUtil::getDateString($dateTime, $noTime, $useTimezone, $useUtc);
}
//@todo stop this triggering an E_NOTICE when $dateTimes is empty
$params = DateUtil::getDefaultParams($dateTime, $noTime, $useTimezone, $timezoneString);
parent::__construct($name, $dates, $params);
}
}

View File

@@ -0,0 +1,39 @@
<?php
/*
* This file is part of the eluceo/iCal package.
*
* (c) Markus Poerschke <markus@eluceo.de>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Eluceo\iCal\Property\Event;
use Eluceo\iCal\Property;
/**
* Class Attachment.
*/
class Attachment extends Property
{
/**
* @param string $value
* @param array $params
*/
public function __construct($value, $params = [])
{
parent::__construct('ATTACH', $value, $params);
}
/**
* @param $url
*
* @throws \Exception
*/
public function setUrl($url)
{
$this->setValue($url);
}
}

View File

@@ -0,0 +1,95 @@
<?php
/*
* This file is part of the eluceo/iCal package.
*
* (c) Markus Poerschke <markus@eluceo.de>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Eluceo\iCal\Property\Event;
use Eluceo\iCal\Property;
class Attendees extends Property
{
/**
* @var Property[]
*/
protected $attendees = [];
public function __construct()
{
$this->name = 'ATTENDEES';
// prevent super constructor to be called
}
/**
* @param $value
* @param array $params
*
* @return $this
*/
public function add($value, $params = [])
{
$this->attendees[] = new Property('ATTENDEE', $value, $params);
return $this;
}
/**
* @param Property[] $value
*
* @return $this
*/
public function setValue($value)
{
$this->attendees = $value;
return $this;
}
/**
* @return Property[]
*/
public function getValue()
{
return $this->attendees;
}
/**
* {@inheritdoc}
*/
public function toLines()
{
$lines = [];
foreach ($this->attendees as $attendee) {
$lines[] = $attendee->toLine();
}
return $lines;
}
/**
* @param string $name
* @param mixed $value
*
* @throws \BadMethodCallException
*/
public function setParam($name, $value)
{
throw new \BadMethodCallException('Cannot call setParam on Attendees Property');
}
/**
* @param $name
*
* @throws \BadMethodCallException
*/
public function getParam($name)
{
throw new \BadMethodCallException('Cannot call getParam on Attendees Property');
}
}

View File

@@ -0,0 +1,82 @@
<?php
/*
* This file is part of the eluceo/iCal package.
*
* (c) Markus Poerschke <markus@eluceo.de>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Eluceo\iCal\Property\Event;
use Eluceo\iCal\Property;
/**
* GEO property.
*
* @see https://tools.ietf.org/html/rfc5545#section-3.8.1.6
*/
class Geo extends Property
{
/**
* @var float
*/
private $latitude;
/**
* @var float
*/
private $longitude;
public function __construct(float $latitude, float $longitude)
{
$this->latitude = $latitude;
$this->longitude = $longitude;
if ($this->latitude < -90 || $this->latitude > 90) {
throw new \InvalidArgumentException("The geographical latitude must be a value between -90 and 90 degrees. '{$this->latitude}' was given.");
}
if ($this->longitude < -180 || $this->longitude > 180) {
throw new \InvalidArgumentException("The geographical longitude must be a value between -180 and 180 degrees. '{$this->longitude}' was given.");
}
parent::__construct('GEO', new Property\RawStringValue($this->getGeoLocationAsString()));
}
/**
* @deprecated This method is used to allow backwards compatibility for Event::setLocation
*
* @return Geo
*/
public static function fromString(string $geoLocationString): self
{
$geoLocationString = str_replace(',', ';', $geoLocationString);
$geoLocationString = str_replace('GEO:', '', $geoLocationString);
$parts = explode(';', $geoLocationString);
return new static((float) $parts[0], (float) $parts[1]);
}
/**
* Returns the coordinates as a string.
*
* @example 37.386013;-122.082932
*/
public function getGeoLocationAsString(string $separator = ';'): string
{
return number_format($this->latitude, 6) . $separator . number_format($this->longitude, 6);
}
public function getLatitude(): float
{
return $this->latitude;
}
public function getLongitude(): float
{
return $this->longitude;
}
}

View File

@@ -0,0 +1,29 @@
<?php
/*
* This file is part of the eluceo/iCal package.
*
* (c) Markus Poerschke <markus@eluceo.de>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Eluceo\iCal\Property\Event;
use Eluceo\iCal\Property;
/**
* Class Organizer.
*/
class Organizer extends Property
{
/**
* @param string $value
* @param array $params
*/
public function __construct($value, $params = [])
{
parent::__construct('ORGANIZER', $value, $params);
}
}

View File

@@ -0,0 +1,121 @@
<?php
/*
* This file is part of the eluceo/iCal package.
*
* (c) Markus Poerschke <markus@eluceo.de>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Eluceo\iCal\Property\Event;
use Eluceo\iCal\ParameterBag;
use Eluceo\iCal\Property;
use Eluceo\iCal\Property\ValueInterface;
use Eluceo\iCal\Util\DateUtil;
/**
* Implementation of Recurrence Id.
*
* @see https://tools.ietf.org/html/rfc5545#section-3.8.4.4
*/
class RecurrenceId extends Property
{
/**
* The effective range of recurrence instances from the instance
* specified by the recurrence identifier specified by the property.
*/
const RANGE_THISANDPRIOR = 'THISANDPRIOR';
const RANGE_THISANDFUTURE = 'THISANDFUTURE';
/**
* The dateTime to identify a particular instance of a recurring event which is getting modified.
*
* @var \DateTimeInterface
*/
protected $dateTime;
/**
* Specify the effective range of recurrence instances from the instance.
*
* @var string
*/
protected $range;
public function __construct(\DateTimeInterface $dateTime = null)
{
$this->name = 'RECURRENCE-ID';
$this->parameterBag = new ParameterBag();
if (isset($dateTime)) {
$this->dateTime = $dateTime;
}
}
public function applyTimeSettings($noTime = false, $useTimezone = false, $useUtc = false, $timezoneString = '')
{
$params = DateUtil::getDefaultParams($this->dateTime, $noTime, $useTimezone, $timezoneString);
foreach ($params as $name => $value) {
$this->parameterBag->setParam($name, $value);
}
if ($this->range) {
$this->parameterBag->setParam('RANGE', $this->range);
}
$this->setValue(DateUtil::getDateString($this->dateTime, $noTime, $useTimezone, $useUtc));
}
/**
* @return \DateTimeInterface
*/
public function getDatetime()
{
return $this->dateTime;
}
/**
* @return \Eluceo\iCal\Property\Event\RecurrenceId
*/
public function setDatetime(\DateTimeInterface $dateTime)
{
$this->dateTime = $dateTime;
return $this;
}
/**
* @return string
*/
public function getRange()
{
return $this->range;
}
/**
* @param string $range
*
* @return \Eluceo\iCal\Property\Event\RecurrenceId
*/
public function setRange($range)
{
$this->range = $range;
return $this;
}
/**
* Get all unfolded lines.
*
* @return array
*/
public function toLines()
{
if (!$this->value instanceof ValueInterface) {
throw new \Exception('The value must implement the ValueInterface. Call RecurrenceId::applyTimeSettings() before adding RecurrenceId.');
} else {
return parent::toLines();
}
}
}

View File

@@ -0,0 +1,546 @@
<?php
/*
* This file is part of the eluceo/iCal package.
*
* (c) Markus Poerschke <markus@eluceo.de>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Eluceo\iCal\Property\Event;
use Eluceo\iCal\ParameterBag;
use Eluceo\iCal\Property\ValueInterface;
use InvalidArgumentException;
/**
* Implementation of Recurrence Rule.
*
* @see https://tools.ietf.org/html/rfc5545#section-3.8.5.3
*/
class RecurrenceRule implements ValueInterface
{
const FREQ_YEARLY = 'YEARLY';
const FREQ_MONTHLY = 'MONTHLY';
const FREQ_WEEKLY = 'WEEKLY';
const FREQ_DAILY = 'DAILY';
const FREQ_HOURLY = 'HOURLY';
const FREQ_MINUTELY = 'MINUTELY';
const FREQ_SECONDLY = 'SECONDLY';
const WEEKDAY_SUNDAY = 'SU';
const WEEKDAY_MONDAY = 'MO';
const WEEKDAY_TUESDAY = 'TU';
const WEEKDAY_WEDNESDAY = 'WE';
const WEEKDAY_THURSDAY = 'TH';
const WEEKDAY_FRIDAY = 'FR';
const WEEKDAY_SATURDAY = 'SA';
/**
* The frequency of an Event.
*
* @var string
*/
protected $freq = self::FREQ_YEARLY;
/**
* BYSETPOS must require use of other BY*.
*
* @var bool
*/
protected $canUseBySetPos = false;
/**
* @var int|null
*/
protected $interval = 1;
/**
* @var int|null
*/
protected $count = null;
/**
* @var \DateTimeInterface|null
*/
protected $until = null;
/**
* @var string|null
*/
protected $wkst;
/**
* @var array|null
*/
protected $bySetPos = null;
/**
* @var string|null
*/
protected $byMonth;
/**
* @var string|null
*/
protected $byWeekNo;
/**
* @var string|null
*/
protected $byYearDay;
/**
* @var string|null
*/
protected $byMonthDay;
/**
* @var string|null
*/
protected $byDay;
/**
* @var string|null
*/
protected $byHour;
/**
* @var string|null
*/
protected $byMinute;
/**
* @var string|null
*/
protected $bySecond;
public function getEscapedValue(): string
{
return $this->buildParameterBag()->toString();
}
/**
* @return ParameterBag
*/
protected function buildParameterBag()
{
$parameterBag = new ParameterBag();
$parameterBag->setParam('FREQ', $this->freq);
if (null !== $this->interval) {
$parameterBag->setParam('INTERVAL', $this->interval);
}
if (null !== $this->count) {
$parameterBag->setParam('COUNT', $this->count);
}
if (null != $this->until) {
$parameterBag->setParam('UNTIL', $this->until->format('Ymd\THis\Z'));
}
if (null !== $this->wkst) {
$parameterBag->setParam('WKST', $this->wkst);
}
if (null !== $this->bySetPos && $this->canUseBySetPos) {
$parameterBag->setParam('BYSETPOS', $this->bySetPos);
}
if (null !== $this->byMonth) {
$parameterBag->setParam('BYMONTH', explode(',', $this->byMonth));
}
if (null !== $this->byWeekNo) {
$parameterBag->setParam('BYWEEKNO', explode(',', $this->byWeekNo));
}
if (null !== $this->byYearDay) {
$parameterBag->setParam('BYYEARDAY', explode(',', $this->byYearDay));
}
if (null !== $this->byMonthDay) {
$parameterBag->setParam('BYMONTHDAY', explode(',', $this->byMonthDay));
}
if (null !== $this->byDay) {
$parameterBag->setParam('BYDAY', explode(',', $this->byDay));
}
if (null !== $this->byHour) {
$parameterBag->setParam('BYHOUR', explode(',', $this->byHour));
}
if (null !== $this->byMinute) {
$parameterBag->setParam('BYMINUTE', explode(',', $this->byMinute));
}
if (null !== $this->bySecond) {
$parameterBag->setParam('BYSECOND', explode(',', $this->bySecond));
}
return $parameterBag;
}
/**
* @param int|null $count
*
* @return $this
*/
public function setCount($count)
{
$this->count = $count;
return $this;
}
/**
* @return int|null
*/
public function getCount()
{
return $this->count;
}
/**
* @return $this
*/
public function setUntil(\DateTimeInterface $until = null)
{
$this->until = $until;
return $this;
}
/**
* @return \DateTimeInterface|null
*/
public function getUntil()
{
return $this->until;
}
/**
* The FREQ rule part identifies the type of recurrence rule. This
* rule part MUST be specified in the recurrence rule. Valid values
* include.
*
* SECONDLY, to specify repeating events based on an interval of a second or more;
* MINUTELY, to specify repeating events based on an interval of a minute or more;
* HOURLY, to specify repeating events based on an interval of an hour or more;
* DAILY, to specify repeating events based on an interval of a day or more;
* WEEKLY, to specify repeating events based on an interval of a week or more;
* MONTHLY, to specify repeating events based on an interval of a month or more;
* YEARLY, to specify repeating events based on an interval of a year or more.
*
* @param string $freq
*
* @return $this
*
* @throws \InvalidArgumentException
*/
public function setFreq($freq)
{
if (@constant('static::FREQ_' . $freq) !== null) {
$this->freq = $freq;
} else {
throw new \InvalidArgumentException("The Frequency {$freq} is not supported.");
}
return $this;
}
/**
* @return string
*/
public function getFreq()
{
return $this->freq;
}
/**
* The INTERVAL rule part contains a positive integer representing at
* which intervals the recurrence rule repeats.
*
* @param int|null $interval
*
* @return $this
*/
public function setInterval($interval)
{
$this->interval = $interval;
return $this;
}
/**
* @return int|null
*/
public function getInterval()
{
return $this->interval;
}
/**
* The WKST rule part specifies the day on which the workweek starts.
* Valid values are MO, TU, WE, TH, FR, SA, and SU.
*
* @param string $value
*
* @return $this
*/
public function setWkst($value)
{
$this->wkst = $value;
return $this;
}
/**
* The BYSETPOS filters one interval of events by the specified position.
* A positive position will start from the beginning and go forward while
* a negative position will start at the end and move backward.
*
* Valid values are a comma separated string or an array of integers
* from 1 to 366 or negative integers from -1 to -366.
*
* @param int|string|array|null $value
*
* @throws InvalidArgumentException
*
* @return $this
*/
public function setBySetPos($value)
{
if (null === $value) {
$this->bySetPos = $value;
return $this;
}
if (!(is_string($value) || is_array($value) || is_int($value))) {
throw new InvalidArgumentException('Invalid value for BYSETPOS');
}
$list = $value;
if (is_int($value)) {
if ($value === 0 || $value < -366 || $value > 366) {
throw new InvalidArgumentException('Invalid value for BYSETPOS');
}
$this->bySetPos = [$value];
return $this;
}
if (is_string($value)) {
$list = explode(',', $value);
}
$output = [];
foreach ($list as $item) {
if (is_string($item)) {
if (!preg_match('/^ *-?[0-9]* *$/', $item)) {
throw new InvalidArgumentException('Invalid value for BYSETPOS');
}
$item = intval($item);
}
if (!is_int($item) || $item === 0 || $item < -366 || $item > 366) {
throw new InvalidArgumentException('Invalid value for BYSETPOS');
}
$output[] = $item;
}
$this->bySetPos = $output;
return $this;
}
/**
* The BYMONTH rule part specifies a COMMA-separated list of months of the year.
* Valid values are 1 to 12.
*
* @param int $month
*
* @throws \InvalidArgumentException
*
* @return $this
*/
public function setByMonth($month)
{
if (!is_integer($month) || $month <= 0 || $month > 12) {
throw new InvalidArgumentException('Invalid value for BYMONTH');
}
$this->byMonth = $month;
$this->canUseBySetPos = true;
return $this;
}
/**
* The BYWEEKNO rule part specifies a COMMA-separated list of ordinals specifying weeks of the year.
* Valid values are 1 to 53 or -53 to -1.
*
* @param int $value
*
* @throws \InvalidArgumentException
*
* @return $this
*/
public function setByWeekNo($value)
{
if (!is_integer($value) || $value > 53 || $value < -53 || $value === 0) {
throw new InvalidArgumentException('Invalid value for BYWEEKNO');
}
$this->byWeekNo = $value;
$this->canUseBySetPos = true;
return $this;
}
/**
* The BYYEARDAY rule part specifies a COMMA-separated list of days of the year.
* Valid values are 1 to 366 or -366 to -1.
*
* @param int $day
*
* @throws \InvalidArgumentException
*
* @return $this
*/
public function setByYearDay($day)
{
if (!is_integer($day) || $day > 366 || $day < -366 || $day === 0) {
throw new InvalidArgumentException('Invalid value for BYYEARDAY');
}
$this->byYearDay = $day;
$this->canUseBySetPos = true;
return $this;
}
/**
* The BYMONTHDAY rule part specifies a COMMA-separated list of days of the month.
* Valid values are 1 to 31 or -31 to -1.
*
* @param int $day
*
* @return $this
*
* @throws \InvalidArgumentException
*/
public function setByMonthDay($day)
{
if (!is_integer($day) || $day > 31 || $day < -31 || $day === 0) {
throw new InvalidArgumentException('Invalid value for BYMONTHDAY');
}
$this->byMonthDay = $day;
$this->canUseBySetPos = true;
return $this;
}
/**
* The BYDAY rule part specifies a COMMA-separated list of days of the week;.
*
* SU indicates Sunday; MO indicates Monday; TU indicates Tuesday;
* WE indicates Wednesday; TH indicates Thursday; FR indicates Friday; and SA indicates Saturday.
*
* Each BYDAY value can also be preceded by a positive (+n) or negative (-n) integer.
* If present, this indicates the nth occurrence of a specific day within the MONTHLY or YEARLY "RRULE".
*
* @return $this
*/
public function setByDay(string $day)
{
$this->byDay = $day;
$this->canUseBySetPos = true;
return $this;
}
/**
* The BYHOUR rule part specifies a COMMA-separated list of hours of the day.
* Valid values are 0 to 23.
*
* @param int $value
*
* @return $this
*
* @throws \InvalidArgumentException
*/
public function setByHour($value)
{
if (!is_integer($value) || $value < 0 || $value > 23) {
throw new \InvalidArgumentException('Invalid value for BYHOUR');
}
$this->byHour = $value;
$this->canUseBySetPos = true;
return $this;
}
/**
* The BYMINUTE rule part specifies a COMMA-separated list of minutes within an hour.
* Valid values are 0 to 59.
*
* @param int $value
*
* @return $this
*
* @throws \InvalidArgumentException
*/
public function setByMinute($value)
{
if (!is_integer($value) || $value < 0 || $value > 59) {
throw new \InvalidArgumentException('Invalid value for BYMINUTE');
}
$this->byMinute = $value;
$this->canUseBySetPos = true;
return $this;
}
/**
* The BYSECOND rule part specifies a COMMA-separated list of seconds within a minute.
* Valid values are 0 to 60.
*
* @param int $value
*
* @return $this
*
* @throws \InvalidArgumentException
*/
public function setBySecond($value)
{
if (!is_integer($value) || $value < 0 || $value > 60) {
throw new \InvalidArgumentException('Invalid value for BYSECOND');
}
$this->bySecond = $value;
$this->canUseBySetPos = true;
return $this;
}
}

View File

@@ -0,0 +1,20 @@
<?php
/*
* This file is part of the eluceo/iCal package.
*
* (c) Markus Poerschke <markus@eluceo.de>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Eluceo\iCal\Property;
class RawStringValue extends StringValue
{
public function getEscapedValue(): string
{
return $this->getValue();
}
}

View File

@@ -0,0 +1,67 @@
<?php
/*
* This file is part of the eluceo/iCal package.
*
* (c) Markus Poerschke <markus@eluceo.de>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Eluceo\iCal\Property;
class StringValue implements ValueInterface
{
/**
* The value.
*
* @var string
*/
protected $value;
public function __construct($value)
{
$this->value = $value;
}
public function getEscapedValue(): string
{
$value = $this->value;
$value = str_replace('\\', '\\\\', $value);
$value = str_replace('"', '\\"', $value);
$value = str_replace(',', '\\,', $value);
$value = str_replace(';', '\\;', $value);
$value = str_replace("\n", '\\n', $value);
$value = str_replace([
"\x00", "\x01", "\x02", "\x03", "\x04", "\x05", "\x06", "\x07",
"\x08", "\x09", /* \n*/ "\x0B", "\x0C", "\x0D", "\x0E", "\x0F",
"\x10", "\x11", "\x12", "\x13", "\x14", "\x15", "\x16", "\x17",
"\x18", "\x19", "\x1A", "\x1B", "\x1C", "\x1D", "\x1E", "\x1F",
"\x7F",
], '', $value);
return $value;
}
/**
* @param string $value
*
* @return $this
*/
public function setValue($value)
{
$this->value = $value;
return $this;
}
/**
* @return string
*/
public function getValue()
{
return $this->value;
}
}

View File

@@ -0,0 +1,24 @@
<?php
/*
* This file is part of the eluceo/iCal package.
*
* (c) Markus Poerschke <markus@eluceo.de>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Eluceo\iCal\Property;
interface ValueInterface
{
/**
* Return the value of the Property as an escaped string.
*
* Escape values as per RFC 5545.
*
* @see https://tools.ietf.org/html/rfc5545#section-3.3.11
*/
public function getEscapedValue(): string;
}

74
libs/ical/PropertyBag.php Normal file
View File

@@ -0,0 +1,74 @@
<?php
/*
* This file is part of the eluceo/iCal package.
*
* (c) Markus Poerschke <markus@eluceo.de>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Eluceo\iCal;
class PropertyBag implements \IteratorAggregate
{
/**
* @var array
*/
protected $elements = [];
/**
* Creates a new Property with $name, $value and $params.
*
* @param $name
* @param $value
* @param array $params
*
* @return $this
*/
public function set($name, $value, $params = [])
{
$this->add(new Property($name, $value, $params));
return $this;
}
/**
* @return Property|null
*/
public function get(string $name)
{
if (isset($this->elements[$name])) {
return $this->elements[$name];
}
return null;
}
/**
* Adds a Property. If Property already exists an Exception will be thrown.
*
* @return $this
*
* @throws \Exception
*/
public function add(Property $property)
{
$name = $property->getName();
if (isset($this->elements[$name])) {
throw new \Exception("Property with name '{$name}' already exists");
}
$this->elements[$name] = $property;
return $this;
}
#[\ReturnTypeWillChange]
public function getIterator()
{
return new \ArrayObject($this->elements);
}
}

View File

@@ -0,0 +1,62 @@
<?php
/*
* This file is part of the eluceo/iCal package.
*
* (c) Markus Poerschke <markus@eluceo.de>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Eluceo\iCal\Util;
class ComponentUtil
{
/**
* Folds a single line.
*
* According to RFC 5545, all lines longer than 75 characters should be folded
*
* @see https://tools.ietf.org/html/rfc5545#section-5
* @see https://tools.ietf.org/html/rfc5545#section-3.1
*
* @param string $string
*
* @return array
*/
public static function fold($string)
{
$lines = [];
if (function_exists('mb_strcut')) {
while (strlen($string) > 0) {
if (strlen($string) > 75) {
$lines[] = mb_strcut($string, 0, 75, 'utf-8');
$string = ' ' . mb_strcut($string, 75, strlen($string), 'utf-8');
} else {
$lines[] = $string;
$string = '';
break;
}
}
} else {
$array = preg_split('/(?<!^)(?!$)/u', $string);
$line = '';
$lineNo = 0;
foreach ($array as $char) {
$charLen = strlen($char);
$lineLen = strlen($line);
if ($lineLen + $charLen > 75) {
$line = ' ' . $char;
++$lineNo;
} else {
$line .= $char;
}
$lines[$lineNo] = $line;
}
}
return $lines;
}
}

View File

@@ -0,0 +1,77 @@
<?php
/*
* This file is part of the eluceo/iCal package.
*
* (c) Markus Poerschke <markus@eluceo.de>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace Eluceo\iCal\Util;
class DateUtil
{
public static function getDefaultParams(\DateTimeInterface $dateTime = null, $noTime = false, $useTimezone = false, $timezoneString = '')
{
$params = [];
if ($useTimezone && $noTime === false) {
$timeZone = $timezoneString === '' ? $dateTime->getTimezone()->getName() : $timezoneString;
$params['TZID'] = $timeZone;
}
if ($noTime) {
$params['VALUE'] = 'DATE';
}
return $params;
}
/**
* Returns a formatted date string.
*
* @param \DateTimeInterface|null $dateTime The DateTime object
* @param bool $noTime Indicates if the time will be added
* @param bool $useTimezone
* @param bool $useUtc
*
* @return mixed
*/
public static function getDateString(\DateTimeInterface $dateTime = null, $noTime = false, $useTimezone = false, $useUtc = false)
{
if (empty($dateTime)) {
$dateTime = new \DateTimeImmutable();
}
// Only convert the DateTime to UTC if there is a time present. For date-only the
// timezone is meaningless and converting it might shift it to the wrong date.
// Do not convert DateTime to UTC if a timezone it specified, as it should be local time.
if (!$noTime && $useUtc && !$useTimezone) {
$dateTime = clone $dateTime;
$dateTime = $dateTime->setTimezone(new \DateTimeZone('UTC'));
}
return $dateTime->format(self::getDateFormat($noTime, $useTimezone, $useUtc));
}
/**
* Returns the date format that can be passed to DateTime::format().
*
* @param bool $noTime Indicates if the time will be added
* @param bool $useTimezone
* @param bool $useUtc
*
* @return string
*/
public static function getDateFormat($noTime = false, $useTimezone = false, $useUtc = false)
{
// Do not use UTC time (Z) if timezone support is enabled.
if ($useTimezone || !$useUtc) {
return $noTime ? 'Ymd' : 'Ymd\THis';
}
return $noTime ? 'Ymd' : 'Ymd\THis\Z';
}
}