55 lines
1.6 KiB
PHP
55 lines
1.6 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Services;
|
|
|
|
use App\Dto\IcsContentDto;
|
|
|
|
class IcsGeneratorService
|
|
{
|
|
public function __construct(
|
|
private readonly string $appName
|
|
) {
|
|
if (empty($this->appName)) {
|
|
throw new \InvalidArgumentException('App name must be provided for ICS generation.');
|
|
}
|
|
}
|
|
|
|
public function generate(IcsContentDto $event): string
|
|
{
|
|
$dateFormat = 'Ymd\THis\Z';
|
|
$lines = [
|
|
'BEGIN:VCALENDAR',
|
|
'VERSION:2.0',
|
|
'PRODID:-//' . $this->appName . '//NONSGML v1.0//EN',
|
|
'BEGIN:VEVENT',
|
|
'UID:' . $event->uid,
|
|
'DTSTAMP:' . now()->setTimezone('UTC')->format($dateFormat),
|
|
'DTSTART:' . $event->startsAt->setTimezone('UTC')->format($dateFormat),
|
|
'DTEND:' . $event->endsAt->setTimezone('UTC')->format($dateFormat),
|
|
'SUMMARY:' . $this->escape($event->summary),
|
|
'DESCRIPTION:' . $this->escape($event->description),
|
|
];
|
|
|
|
if ($event->url) {
|
|
$lines[] = 'URL:' . $this->escape($event->url);
|
|
}
|
|
|
|
$lines[] = 'END:VEVENT';
|
|
$lines[] = 'END:VCALENDAR';
|
|
|
|
// NOTE: ICS files must have CRLF line endings
|
|
return implode("\r\n", $lines) . "\r\n";
|
|
}
|
|
|
|
private function escape(string $text): string
|
|
{
|
|
$text = str_replace('\\', '\\\\', $text);
|
|
$text = str_replace(';', '\;', $text);
|
|
$text = str_replace(',', '\,', $text);
|
|
$text = str_replace("\n", '\n', $text);
|
|
|
|
return $text;
|
|
}
|
|
}
|