488 lines
12 KiB
PHP
488 lines
12 KiB
PHP
|
<?php
|
|||
|
|
|||
|
/**
|
|||
|
* Datei: getEvents.php
|
|||
|
* Autor: bernd@nr18.space
|
|||
|
* Letze Änderung: 08.07.2020
|
|||
|
* Kurzbeschreibung:
|
|||
|
* Enthält Funktionen zum Parsen und Ausgeben einer iCalendar Kalenderdatei.
|
|||
|
* Die Bibliothek zum Parsen des iCalendarfiles stammt von ZContent.
|
|||
|
* Der Quelltext liegt auf https://github.com/zcontent/icalendar. Da sie
|
|||
|
* auf calendar.org verlinkt war, dachte ich eigentlich, daß sie gut wäre.
|
|||
|
*/
|
|||
|
require_once("../src/lib/zapcallib.php");
|
|||
|
|
|||
|
|
|||
|
function printEventList(): bool
|
|||
|
{
|
|||
|
/**
|
|||
|
* Die Startfuktion für die Ausgabe der Termine als Liste in index.php.
|
|||
|
* Nach erfolgreicher initialisierung läuft die Funktion durch das Array
|
|||
|
* der Events (ZCalNode´s vom Typ 'VEVENT') ruft für jedes Element erst
|
|||
|
* die Funktion zur Umwandlung in ein assoziatives Array und dann die
|
|||
|
* Ausgabefunktion auf. Gibt einen Boolean zurück.
|
|||
|
*/
|
|||
|
$events = initEvents();
|
|||
|
$helper = new ZDateHelper();
|
|||
|
date_default_timezone_set("UTC");
|
|||
|
|
|||
|
/**
|
|||
|
* Startdatum für wiederkehrende Termine aktualisieren
|
|||
|
*
|
|||
|
* @var ZCiCalNode $event
|
|||
|
*/
|
|||
|
foreach ($events as $event)
|
|||
|
{
|
|||
|
/**
|
|||
|
* @var ZCiCalDataNode $eventDTStart
|
|||
|
*/
|
|||
|
$eventDTStart = $event->data['DTSTART'];
|
|||
|
/** @var string[] $eventDTStartValues */
|
|||
|
$eventDTStartValues = $eventDTStart->value;
|
|||
|
$event_start = $eventDTStartValues[0] ?? '';
|
|||
|
$unix_start = $helper->fromiCaltoUnixDateTime($event_start);
|
|||
|
$rrule = $event->data['RRULE']->value[0];
|
|||
|
|
|||
|
//todo rrule always set
|
|||
|
if (EventIsPast($unix_start) === true AND isset ($rrule))
|
|||
|
{
|
|||
|
$new_unix = calculateNextStart($unix_start, $rrule);
|
|||
|
$date = date('Ymd', $new_unix);
|
|||
|
$time = date('His', $new_unix);
|
|||
|
$connector = 'T';
|
|||
|
$new_start = "{$date}{$connector}{$time}";
|
|||
|
$event->data['DTSTART']->value[0] = $new_start;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* Eventliste nach Startdatum sortieren
|
|||
|
*/
|
|||
|
usort($events, 'compareEventStart');
|
|||
|
|
|||
|
/**
|
|||
|
* Termine ausgeben
|
|||
|
*/
|
|||
|
foreach ($events as $event)
|
|||
|
{
|
|||
|
printListItem($event);
|
|||
|
}
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
function printListItem(ZCiCalNode $event): bool
|
|||
|
/**
|
|||
|
* Die Ausgabefunktion für die Ausgabe der Termine als Liste. Hier wird die
|
|||
|
* Zeitangabe des iCal-Formates in Datum, Uhrzeit und Wochentag umgewandelt.
|
|||
|
* Anschließend erfolgt die Ausgabe der einzelnen Teile.
|
|||
|
*/
|
|||
|
{
|
|||
|
date_default_timezone_set("UTC");
|
|||
|
$helper = new ZDateHelper();
|
|||
|
$hyph = " - ";
|
|||
|
$uhr = " Uhr";
|
|||
|
$komma = ", ";
|
|||
|
|
|||
|
$event_start = $event->data['DTSTART']->value[0];
|
|||
|
$event_url = lowerURL($event->data['URL']->parameter['value']);
|
|||
|
$event_title = $event->data['SUMMARY']->value[0];
|
|||
|
$event_location = trim($event->data['LOCATION']->value[0], '"');
|
|||
|
$event_descr = trim($event->data['DESCRIPTION']->value[0], '"');
|
|||
|
|
|||
|
$unix = $helper->fromiCaltoUnixDateTime($event_start);
|
|||
|
$event_date = date('d.m.Y', $unix);
|
|||
|
$event_time = date('H:i', $unix);
|
|||
|
$event_day = toGerman(date('l', $unix));
|
|||
|
$dateline = "{$event_day}{$komma}{$event_date}{$hyph}{$event_time}{$uhr}";
|
|||
|
|
|||
|
displayHeadline($dateline, $event_title);
|
|||
|
displayLocation($event_location);
|
|||
|
displayDescription($event_descr);
|
|||
|
displayURL($event_url);
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
function displayHeadline(string $dateline, $event_title): bool
|
|||
|
{
|
|||
|
echo "\n<section class='termin'>\n";
|
|||
|
echo "<p class='headline'>" . $dateline . ": " . $event_title . "</p>\n";
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
function displayLocation(string $location): bool
|
|||
|
{
|
|||
|
echo "<ul class='events'>\n";
|
|||
|
echo "<li>$location</li>\n";
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
function displayDescription(string $description): bool
|
|||
|
{
|
|||
|
echo "<li>$description</li>\n";
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
function displayURL(string $url): bool
|
|||
|
{
|
|||
|
$stripped = trim($url, '"');
|
|||
|
echo "<li><a href=" . $url . ">" . $stripped . "</a></li>\n";
|
|||
|
echo "</ul>\n";
|
|||
|
echo "</section>\n";
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* Die Startfuktion für die Ausgabe der Termine als Tabelle in termine.php
|
|||
|
*/
|
|||
|
|
|||
|
function printEventTable(): bool
|
|||
|
{
|
|||
|
$events = initEvents();
|
|||
|
printTableHead();
|
|||
|
foreach ($events as $event)
|
|||
|
{
|
|||
|
$event_array = getEventArray($event);
|
|||
|
printTableItem($event_array);
|
|||
|
}
|
|||
|
echo "\t</tbody>\n";
|
|||
|
echo "</table>\n";
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
function printTableHead()
|
|||
|
{
|
|||
|
echo "\n\r<table class='w3-table w3-bordered'>\n";
|
|||
|
echo "\t<thead>\n";
|
|||
|
echo "\t<tr>\n";
|
|||
|
echo "\t\t<th>Datum</th>\n";
|
|||
|
echo "\t\t<th>Wochentag</th>\n";
|
|||
|
echo "\t\t<th>Zeit</th>\n";
|
|||
|
echo "\t\t<th>Ort</th>\n";
|
|||
|
echo "\t\t<th>Titel</th>\n";
|
|||
|
echo "\t\t<th>Beschreibung</th>\n";
|
|||
|
echo "\t</tr>\n";
|
|||
|
echo "\t</thead>\n";
|
|||
|
echo "\t<tbody>\n";
|
|||
|
}
|
|||
|
|
|||
|
function printTableItem($event_array): bool
|
|||
|
{
|
|||
|
date_default_timezone_set("UTC");
|
|||
|
$time = $event_array['DTSTART'];
|
|||
|
$helper = new ZDateHelper();
|
|||
|
$unix = $helper->fromiCaltoUnixDateTime($time);
|
|||
|
$event_date = date('d.m.Y', $unix);
|
|||
|
$event_time = date('H:i', $unix);
|
|||
|
$event_day = toGerman(date('l', $unix));
|
|||
|
$event_uid = $event_array['UID'];
|
|||
|
$event_url = lowerURL($event_array['URL']);
|
|||
|
$event_title = trim($event_array['SUMMARY'], '"');
|
|||
|
$event_descr = trim($event_array['DESCRIPTION'], '"');
|
|||
|
$event_location = trim($event_array['LOCATION'], '"');
|
|||
|
echo "\t<tr id='" . $event_uid . "'>\n";
|
|||
|
echo "\t\t<td>" . $event_date . "</td>\n";
|
|||
|
echo "\t\t<td>" . $event_day . "</td>\n";
|
|||
|
echo "\t\t<td>" . $event_time . " Uhr</td>\n";
|
|||
|
echo "\t\t<td>" . $event_location . "</td>\n";
|
|||
|
echo "\t\t<td>" . $event_title . "</td>\n";
|
|||
|
echo "\t\t<td>" . $event_descr;
|
|||
|
printURL($event_url);
|
|||
|
echo "\t</tr>\n";
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
function printURL(string $url): bool
|
|||
|
{
|
|||
|
$stripped = trim($url, '"');
|
|||
|
if ($url != '')
|
|||
|
{
|
|||
|
echo "</br><a class='event' href=" . $url . ">" . $stripped . "</a></td>\n";
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
echo "</td>\n";
|
|||
|
}
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* Funktionen, die von beiden Ausgaben benutzt werden.
|
|||
|
*/
|
|||
|
|
|||
|
/**
|
|||
|
* @return ZCiCalNode[]|null
|
|||
|
*/
|
|||
|
function initEvents(): ?array
|
|||
|
/**
|
|||
|
* Allgemeingültige Funktion zur Initialisierung. Enthält die Schritte, die
|
|||
|
* von beiden Ausgaben gleichermaßen gebraucht werden.
|
|||
|
* - Erstellen des iCalendar Objekts (initCalendar).
|
|||
|
* - Schaut, ob der Kalender überhaupt Events enthält (printEventCount).
|
|||
|
* - Sammel alle Events in einer Liste (grabEvents).
|
|||
|
* Gibt zweidimmensionales assoziatives Array oder Null zurück.
|
|||
|
*/
|
|||
|
{
|
|||
|
$iCalObj = initCalendar();
|
|||
|
if (!isset ($iCalObj))
|
|||
|
{
|
|||
|
printError("Fehler beim Initialisieren des Kalenders");
|
|||
|
return null;
|
|||
|
}
|
|||
|
$count = printEventCount($iCalObj);
|
|||
|
if ($count == 0 or $count == false)
|
|||
|
{
|
|||
|
return null;
|
|||
|
}
|
|||
|
$events = grabEvents($iCalObj);
|
|||
|
return $events;
|
|||
|
}
|
|||
|
|
|||
|
function initCalendar(): ?ZCiCal
|
|||
|
/**
|
|||
|
* Erstellt das Kalenderobjekt vom Typ ZCiCal.
|
|||
|
* Gibt das Kalenderobjekt oder Null zurück.
|
|||
|
*/
|
|||
|
{
|
|||
|
$iCalFile = '../public/krautspace.ics';
|
|||
|
$iCalString = file_get_contents($iCalFile);
|
|||
|
if ($iCalString == false)
|
|||
|
{
|
|||
|
printError("Kann Kalenderdatei nicht lesen");
|
|||
|
return null;
|
|||
|
}
|
|||
|
$iCalObj = new ZCiCal($iCalString);
|
|||
|
return $iCalObj;
|
|||
|
}
|
|||
|
|
|||
|
function printEventCount(ZCiCal $iCalObj): ?int
|
|||
|
/**
|
|||
|
* Gibt die Anzahl der Events zurück, die das übergebene
|
|||
|
* Kalenderobjekt enthält. Im Fehlerfall wird Null zurück
|
|||
|
* gegeben.
|
|||
|
*/
|
|||
|
{
|
|||
|
$eventCount = $iCalObj->countEvents();
|
|||
|
if (!isset ($eventCount))
|
|||
|
{
|
|||
|
printError("Fehler beim Parsen des Kalenders");
|
|||
|
return null;
|
|||
|
}
|
|||
|
// echo "<p>$eventCount anstehende Events</p>";
|
|||
|
return $eventCount;
|
|||
|
}
|
|||
|
|
|||
|
function grabEvents(ZCiCal $iCalObj): ?array
|
|||
|
{
|
|||
|
/**
|
|||
|
* Läuft durch das iCalendar objekt und sammelt alle Nodes vom Typ
|
|||
|
* 'VEVENT' ein. Gibt ein Array mit Objekten vom Typ 'ZCiCalNode' zurück.
|
|||
|
* Im Fehlerfall wird Null zurück gegeben.
|
|||
|
*/
|
|||
|
$events = [];
|
|||
|
if (isset ($iCalObj->tree->child))
|
|||
|
{
|
|||
|
foreach ($iCalObj->tree->child as $node)
|
|||
|
{
|
|||
|
if ($node->getName() == "VEVENT")
|
|||
|
{
|
|||
|
$events[] = $node;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
printError("Cant find nodes");
|
|||
|
return null;
|
|||
|
}
|
|||
|
return $events;
|
|||
|
}
|
|||
|
|
|||
|
function getEventArray(ZCiCalNode $node): array
|
|||
|
{
|
|||
|
/**
|
|||
|
* Bekommt eine Event Node vom Typ 'ZiCalNode' übergeben und extrahiert
|
|||
|
* daraus die gewünschten Elemente. Bildet daraus ein zweidimmensionales
|
|||
|
* assoziatives Array. Gibt dieses Array zurück.
|
|||
|
* Wird derzeit nur von der tabellarischen Ausgabe referenziert, welche
|
|||
|
+ momentan nicht genutzt wird.
|
|||
|
*/
|
|||
|
/**
|
|||
|
* @var ZCiCalNode $node
|
|||
|
* @var ZCiCalDataNode $event
|
|||
|
*/
|
|||
|
$event = $node->data;
|
|||
|
$event_array = [];
|
|||
|
$keys = array('DTSTART', 'SUMMARY', 'DESCRIPTION', 'URL', 'LOCATION');
|
|||
|
|
|||
|
foreach ($keys as $key)
|
|||
|
{
|
|||
|
$event_array[$key] = $event[$key]->value[0];
|
|||
|
if ($key === 'DTSTART')
|
|||
|
{
|
|||
|
if (isset ($event[$key]->parameter['tzid']))
|
|||
|
{
|
|||
|
$event_array['TZ'] = $event[$key]->parameter['tzid'];
|
|||
|
}
|
|||
|
}
|
|||
|
else if ($key === 'URL')
|
|||
|
{
|
|||
|
$event_array[$key] = $event[$key]->parameter['value'];
|
|||
|
}
|
|||
|
}
|
|||
|
return $event_array;
|
|||
|
}
|
|||
|
|
|||
|
function printError($errMsg)
|
|||
|
{
|
|||
|
echo "\n\r<p>$errMsg</p>\n\r";
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
function toGerman(string $day): string
|
|||
|
{
|
|||
|
switch ($day)
|
|||
|
{
|
|||
|
case 'Monday':
|
|||
|
return 'Montag';
|
|||
|
case 'Tuesday':
|
|||
|
return 'Dienstag';
|
|||
|
case 'Wednesday':
|
|||
|
return 'Mittwoch';
|
|||
|
case 'Thursday':
|
|||
|
return 'Donnerstag';
|
|||
|
case 'Friday':
|
|||
|
return 'Freitag';
|
|||
|
case 'Saturday':
|
|||
|
return 'Samstag';
|
|||
|
case 'Sunday':
|
|||
|
return 'Sonntag';
|
|||
|
default:
|
|||
|
return '?';
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
function lowerURL(string $url): string
|
|||
|
{
|
|||
|
$old = array('HTTPS', 'HTTP', 'FTP', 'WWW', 'SSH');
|
|||
|
$new = array('https', 'http', 'ftp', 'www', 'ssh');
|
|||
|
$new_url = str_replace($old, $new, $url);
|
|||
|
return $new_url;
|
|||
|
}
|
|||
|
|
|||
|
function calculateNextStart(int $unix_start, string $rrule): int
|
|||
|
/**
|
|||
|
* Berechnet für wiederkehrende Termine den aktuell nächsten Termin.
|
|||
|
* dabei werden zur Zeit nur der der Zeitraum zwischen zwei Terminen
|
|||
|
* und eine mögliche Anzahl der Termine berücksichtigt. Gibt den neuen
|
|||
|
* Termin als Unix-Zeitstempel zurück.
|
|||
|
*/
|
|||
|
{
|
|||
|
$counter = 0;
|
|||
|
|
|||
|
$rule_array = getRuleArray($rrule);
|
|||
|
if (isset ($rule_array['COUNT']))
|
|||
|
{
|
|||
|
$count = $rule_array['COUNT'];
|
|||
|
}
|
|||
|
if (isset ($rule_array['FREQ']))
|
|||
|
{
|
|||
|
$frequency = $rule_array['FREQ'];
|
|||
|
}
|
|||
|
if (isset ($rule_array['INTERVAL']))
|
|||
|
{
|
|||
|
$interval = $rule_array['INTERVAL'];
|
|||
|
}
|
|||
|
if (isset ($rule_array['UNTIL']))
|
|||
|
{
|
|||
|
//todo implement
|
|||
|
$until_string = $rule_array['UNTIL'];
|
|||
|
}
|
|||
|
|
|||
|
$freq_offset = getOffset($frequency);
|
|||
|
if (isset ($interval))
|
|||
|
{
|
|||
|
$offset = $freq_offset * $interval;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
$offset = $freq_offset;
|
|||
|
}
|
|||
|
while ($unix_start <= time())
|
|||
|
{
|
|||
|
if (isset ($count))
|
|||
|
{
|
|||
|
if ($counter >= $count)
|
|||
|
{
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
$unix_start = $unix_start + $offset;
|
|||
|
$counter = $counter + 1;
|
|||
|
}
|
|||
|
return $unix_start;
|
|||
|
}
|
|||
|
|
|||
|
function getOffset(string $frequence): int
|
|||
|
{
|
|||
|
switch ($frequence)
|
|||
|
{
|
|||
|
case 'HOURLY':
|
|||
|
return 3600;
|
|||
|
case 'DAILY':
|
|||
|
return 86400;
|
|||
|
case 'WEEKLY':
|
|||
|
return 604800;
|
|||
|
default:
|
|||
|
return 1;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
function getRuleArray(string $rrule): array
|
|||
|
/**
|
|||
|
* Zerlegt den String einer RRULE und gibt die einzelnen Elemente als
|
|||
|
* assoziatives Array zurück.
|
|||
|
*/
|
|||
|
{
|
|||
|
$rule_array = [];
|
|||
|
$rule_strings = explode(';', $rrule);
|
|||
|
foreach ($rule_strings as $r_string)
|
|||
|
{
|
|||
|
$rule = explode('=', $r_string);
|
|||
|
$rule_array[$rule[0]] = $rule[1];
|
|||
|
}
|
|||
|
return $rule_array;
|
|||
|
}
|
|||
|
|
|||
|
function EventIsPast(int $unix_start): bool
|
|||
|
/**
|
|||
|
* Prüft, ob die übergebenen Unixzeit älter als der aktuelle Tag ist. Gibt
|
|||
|
* Wahr oder Falsch zurück.
|
|||
|
*/
|
|||
|
{
|
|||
|
$event_date = date('d.m.Y', $unix_start);
|
|||
|
$day_end = strtotime($event_date) + 86400;
|
|||
|
$actual_date = time();
|
|||
|
|
|||
|
if ($day_end < $actual_date)
|
|||
|
{
|
|||
|
return true;
|
|||
|
}
|
|||
|
return false;
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* Bekommt zwei Eventnodes übergeben und vergleicht deren Startdatum. Die
|
|||
|
* Funktion wird intern von ausort() benutzt, um das Array mit den Eventnodes
|
|||
|
* nach Datum zu sortieren.
|
|||
|
*
|
|||
|
* @param ZCiCalNode $event_a
|
|||
|
* @param ZCiCalNode $event_b
|
|||
|
* @return int
|
|||
|
*/
|
|||
|
function compareEventStart(ZCiCalNode $event_a, ZCiCalNode $event_b): int
|
|||
|
{
|
|||
|
$a = $event_a->data['DTSTART']->value[0];
|
|||
|
$b = $event_b->data['DTSTART']->value[0];
|
|||
|
return $a <=> $b;
|
|||
|
}
|