diff --git a/README.md b/README.md index 212dc66..5ee5d19 100644 --- a/README.md +++ b/README.md @@ -70,13 +70,11 @@ echo $link->webOffice(); // Generate a data URI for an ics file (for iCal & Outlook) echo $link->ics(); echo $link->ics(['URL' => 'https://my-page.com', 'UID' => 'custom-id']); // -echo $link->ics([], ['format' => 'file']); // e.g. to attach ics as a file to an email. - -// Generate a data uri for an ics file with default reminder (for iCal & Outlook) +// Generate a data uri for an ics file with the default reminder (for iCal & Outlook) echo $link->ics(['REMINDER' => []]); - // Generate a data uri for an ics file with a custom reminder (for iCal & Outlook) -echo $link->ics(['REMINDER' => ['DESCRIPTION' => 'Remind me', 'TIME' => DateTime::createFromFormat('Y-m-d H:i', '2018-02-01 08:15', new DateTimeZone('UTC'))]]); +echo $link->ics(['REMINDER' => ['DESCRIPTION' => 'Remind me', 'TIME' => 1706264894]]); +echo $link->ics([], ['format' => 'file']); // e.g. to attach ics as a file to an email. // Generate a data URI using arbitrary generator: echo $link->formatWith(new \Your\Generator()); diff --git a/src/Generators/Ics.php b/src/Generators/Ics.php index 0103d32..248b674 100644 --- a/src/Generators/Ics.php +++ b/src/Generators/Ics.php @@ -7,6 +7,7 @@ /** * @see https://icalendar.org/RFC-Specifications/iCalendar-RFC-5545/ + * @psalm-type IcsOptions = array{UID?: string, URL?: string, REMINDER?: array{DESCRIPTION?: string, TIME?: \DateTimeInterface}} */ class Ics implements Generator { @@ -15,17 +16,18 @@ class Ics implements Generator /** @var string {@see https://www.php.net/manual/en/function.date.php} */ protected $dateFormat = 'Ymd'; + /** @var string */ protected $dateTimeFormat = 'Ymd\THis\Z'; - /** @var array */ + /** @var IcsOptions */ protected $options = []; /** @var array{format?: self::FORMAT_*} */ protected $presentationOptions = []; /** - * @param array $options Optional ICS properties and components + * @param IcsOptions $options Optional ICS properties and components * @param array{format?: self::FORMAT_*} $presentationOptions */ public function __construct(array $options = [], array $presentationOptions = []) @@ -69,22 +71,8 @@ public function generate(Link $link): string $url[] = 'URL;VALUE=URI:'.$this->options['URL']; } - if (isset($this->options['REMINDER'])) { - $description = 'Reminder: '.$this->escapeString($link->title); - if (isset($this->options['REMINDER']['DESCRIPTION'])) { - $description = $this->escapeString($this->options['REMINDER']['DESCRIPTION']); - } - - $trigger = '-PT15M'; - if (isset($this->options[ 'REMINDER'][ 'TIME'])) { - $trigger = 'VALUE=DATE-TIME:'.gmdate($dateTimeFormat, $this->options[ 'REMINDER'][ 'TIME']->getTimestamp()); - } - - $url[] = 'BEGIN:VALARM'; - $url[] = 'ACTION:DISPLAY'; - $url[] = 'DESCRIPTION:'.$description; - $url[] = 'TRIGGER:'.$trigger; - $url[] = 'END:VALARM'; + if (is_array($this->options['REMINDER'] ?? null)) { + $url = [...$url, ...$this->generateAlertComponent($link)]; } $url[] = 'END:VEVENT'; @@ -125,4 +113,30 @@ protected function generateEventUid(Link $link): string $link->address )); } + + /** + * @param \Spatie\CalendarLinks\Link $link + * @return list + */ + private function generateAlertComponent(Link $link): array + { + $description = $this->options['REMINDER']['DESCRIPTION'] ?? null; + if (! is_string($description)) { + $description = 'Reminder: '.$this->escapeString($link->title); + } + + $trigger = '-PT15M'; + if (($reminderTime = $this->options['REMINDER']['TIME'] ?? null) instanceof \DateTimeInterface) { + $trigger = 'VALUE=DATE-TIME:'.gmdate($this->dateTimeFormat, $reminderTime->getTimestamp()); + } + + $alarmComponent = []; + $alarmComponent[] = 'BEGIN:VALARM'; + $alarmComponent[] = 'ACTION:DISPLAY'; + $alarmComponent[] = 'DESCRIPTION:'.$description; + $alarmComponent[] = 'TRIGGER:'.$trigger; + $alarmComponent[] = 'END:VALARM'; + + return $alarmComponent; + } } diff --git a/src/Link.php b/src/Link.php index e584e0c..9230480 100644 --- a/src/Link.php +++ b/src/Link.php @@ -16,6 +16,7 @@ * @property-read string $description * @property-read string $address * @property-read bool $allDay + * @psalm-import-type IcsOptions from \Spatie\CalendarLinks\Generators\Ics */ class Link { @@ -128,6 +129,7 @@ public function google(): string } /** + * @psalm-param IcsOptions $options ICS specific properties and components * @param array $options ICS specific properties and components * @param array{format?: \Spatie\CalendarLinks\Generators\Ics::FORMAT_*} $presentationOptions * @return string