export calendar - modifica impostazioni
This commit is contained in:
@@ -0,0 +1,212 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
use App\Models\Evento;
|
||||
use Carbon\Carbon;
|
||||
|
||||
class IcsExportService
|
||||
{
|
||||
private const DATETIME_FORMAT = 'Ymd\THis';
|
||||
private const DATE_FORMAT = 'Ymd';
|
||||
private const TIMEZONE = 'Europe/Rome';
|
||||
|
||||
private const DAY_MAP = [
|
||||
0 => 'SU',
|
||||
1 => 'MO',
|
||||
2 => 'TU',
|
||||
3 => 'WE',
|
||||
4 => 'TH',
|
||||
5 => 'FR',
|
||||
6 => 'SA',
|
||||
];
|
||||
|
||||
public function generateSingle(Evento $evento): string
|
||||
{
|
||||
return $this->generateCollection(collect([$evento]));
|
||||
}
|
||||
|
||||
public function generateCollection(iterable $eventi): string
|
||||
{
|
||||
$lines = [
|
||||
'BEGIN:VCALENDAR',
|
||||
'VERSION:2.0',
|
||||
'PRODID:-//Glastree//Eventi//IT',
|
||||
'CALSCALE:GREGORIAN',
|
||||
'METHOD:PUBLISH',
|
||||
'X-WR-CALNAME:' . config('app.name') . ' - Eventi',
|
||||
];
|
||||
|
||||
foreach ($eventi as $evento) {
|
||||
array_push($lines, ...$this->buildVEvent($evento));
|
||||
}
|
||||
|
||||
$lines[] = 'END:VCALENDAR';
|
||||
|
||||
return $this->fold(implode("\r\n", $lines));
|
||||
}
|
||||
|
||||
private function buildVEvent(Evento $evento): array
|
||||
{
|
||||
$uid = 'evento-' . $evento->id . '@' . preg_replace('/[^a-z0-9]/', '', strtolower(config('app.name')));
|
||||
$lines = [
|
||||
'BEGIN:VEVENT',
|
||||
'UID:' . $uid,
|
||||
'DTSTAMP:' . now()->format(self::DATETIME_FORMAT) . 'Z',
|
||||
];
|
||||
|
||||
if ($evento->isSingolo() && $evento->data_specifica) {
|
||||
$date = $evento->data_specifica instanceof Carbon
|
||||
? $evento->data_specifica
|
||||
: Carbon::parse($evento->data_specifica);
|
||||
|
||||
if ($evento->ora_inizio) {
|
||||
$start = $date->copy()->setTimeFrom($evento->ora_inizio instanceof Carbon ? $evento->ora_inizio : Carbon::parse($evento->ora_inizio));
|
||||
$durata = $evento->durata_minuti ?? 60;
|
||||
$end = $start->copy()->addMinutes((int) $durata);
|
||||
$lines[] = 'DTSTART;TZID=' . self::TIMEZONE . ':' . $start->format(self::DATETIME_FORMAT);
|
||||
$lines[] = 'DTEND;TZID=' . self::TIMEZONE . ':' . $end->format(self::DATETIME_FORMAT);
|
||||
} else {
|
||||
$lines[] = 'DTSTART;VALUE=DATE:' . $date->format(self::DATE_FORMAT);
|
||||
$lines[] = 'DTEND;VALUE=DATE:' . $date->copy()->addDay()->format(self::DATE_FORMAT);
|
||||
}
|
||||
} else {
|
||||
$rrule = $this->buildRRule($evento);
|
||||
if ($rrule) {
|
||||
$lines[] = $rrule;
|
||||
}
|
||||
|
||||
if ($evento->ora_inizio) {
|
||||
$time = $evento->ora_inizio instanceof Carbon ? $evento->ora_inizio : Carbon::parse($evento->ora_inizio);
|
||||
$lines[] = 'DTSTART;TZID=' . self::TIMEZONE . ':' . $time->format('His');
|
||||
$durata = $evento->durata_minuti ?? 60;
|
||||
$endTime = $time->copy()->addMinutes((int) $durata);
|
||||
$lines[] = 'DTEND;TZID=' . self::TIMEZONE . ':' . $endTime->format('His');
|
||||
}
|
||||
}
|
||||
|
||||
$lines[] = 'SUMMARY:' . $this->escapeText($evento->nome_evento);
|
||||
|
||||
if ($evento->descrizione_evento || $evento->descrizione) {
|
||||
$desc = $evento->descrizione_evento
|
||||
? ($evento->descrizione ? $evento->descrizione_evento . '\n\n' . $evento->descrizione : $evento->descrizione_evento)
|
||||
: $evento->descrizione;
|
||||
$lines[] = 'DESCRIPTION:' . $this->escapeText($desc);
|
||||
}
|
||||
|
||||
if ($evento->luogo_indirizzo) {
|
||||
$lines[] = 'LOCATION:' . $this->escapeText($evento->luogo_indirizzo);
|
||||
}
|
||||
|
||||
$lines[] = 'END:VEVENT';
|
||||
|
||||
return $lines;
|
||||
}
|
||||
|
||||
private function buildRRule(Evento $evento): ?string
|
||||
{
|
||||
return match ($evento->tipo_recorrenza) {
|
||||
'settimanale' => $this->rruleSettimanale($evento),
|
||||
'mensile' => $this->rruleMensile($evento),
|
||||
'annuale' => $this->rruleAnnuale($evento),
|
||||
'altro' => $this->rruleAltro($evento),
|
||||
default => null,
|
||||
};
|
||||
}
|
||||
|
||||
private function rruleSettimanale(Evento $evento): ?string
|
||||
{
|
||||
if ($evento->giorno_settimana === null) {
|
||||
return null;
|
||||
}
|
||||
return 'RRULE:FREQ=WEEKLY;BYDAY=' . (self::DAY_MAP[(int) $evento->giorno_settimana] ?? 'MO');
|
||||
}
|
||||
|
||||
private function rruleMensile(Evento $evento): ?string
|
||||
{
|
||||
if (!$evento->occorrenza_mese) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$parts = explode(',', $evento->occorrenza_mese);
|
||||
if (count($parts) !== 2) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$occorrenza = (int) $parts[0];
|
||||
$giorno = (int) $parts[1];
|
||||
$byDay = self::DAY_MAP[$giorno] ?? 'MO';
|
||||
|
||||
$rrule = 'RRULE:FREQ=MONTHLY;BYSETPOS=' . $occorrenza . ';BYDAY=' . $byDay;
|
||||
|
||||
if ($evento->mesi_recorrenza) {
|
||||
$mesi = explode(',', $evento->mesi_recorrenza);
|
||||
$rrule .= ';BYMONTH=' . implode(',', $mesi);
|
||||
}
|
||||
|
||||
return $rrule;
|
||||
}
|
||||
|
||||
private function rruleAnnuale(Evento $evento): ?string
|
||||
{
|
||||
if (!$evento->mese_annuale) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$rrule = 'RRULE:FREQ=YEARLY;BYMONTH=' . $evento->mese_annuale;
|
||||
|
||||
$giorno = (int) ($evento->giorno_mese ?? 1);
|
||||
$rrule .= ';BYMONTHDAY=' . $giorno;
|
||||
|
||||
return $rrule;
|
||||
}
|
||||
|
||||
private function rruleAltro(Evento $evento): ?string
|
||||
{
|
||||
return $this->rruleSettimanale($evento);
|
||||
}
|
||||
|
||||
private function escapeText(?string $text): string
|
||||
{
|
||||
if ($text === null) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$text = str_replace(
|
||||
['\\', ';', ',', "\n"],
|
||||
['\\\\', '\\;', '\\,', '\\n'],
|
||||
$text
|
||||
);
|
||||
|
||||
return $text;
|
||||
}
|
||||
|
||||
private function fold(string $ics): string
|
||||
{
|
||||
$lines = explode("\r\n", $ics);
|
||||
$folded = [];
|
||||
|
||||
foreach ($lines as $line) {
|
||||
while (mb_strlen($line) > 75) {
|
||||
$folded[] = mb_substr($line, 0, 75);
|
||||
$line = ' ' . mb_substr($line, 75);
|
||||
}
|
||||
$folded[] = $line;
|
||||
}
|
||||
|
||||
return implode("\r\n", $folded);
|
||||
}
|
||||
|
||||
public function getFilename(Evento $evento): string
|
||||
{
|
||||
$name = str_replace(['/', '\\', ' '], '_', $evento->nome_evento);
|
||||
return $name . '.ics';
|
||||
}
|
||||
|
||||
public function getCollectionFilename(): string
|
||||
{
|
||||
return 'eventi_' . now()->format('Ymd_His') . '.ics';
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user