Compare commits
No commits in common. "96f9038932d1839ecb9e44d07ed158f995d662e4" and "07ce4c708a8b93e54e8ca4ee873cfe0b50697f12" have entirely different histories.
96f9038932
...
07ce4c708a
11 changed files with 3132 additions and 270 deletions
15
README.md
15
README.md
|
@ -1,15 +0,0 @@
|
|||
# Webseite
|
||||
|
||||
Das Repository enthält die Webseite das Vereins "Hackspace Jena". Der größte
|
||||
Teil der Webseite ist statisches HTML, ergänzt um einige dynamische
|
||||
Elemente zur Anzeige des Raumstatuses und der aktuellen Termine.
|
||||
|
||||
## Installation
|
||||
|
||||
Zum Betrieb der Webseite ist ist in Webserver und PHP notwendig. Die
|
||||
Auswertung der Termine erfolgt mittels der VObject Bibliothek von
|
||||
[https://sabre.io](https://sabre.io), welche auch in Nextcloud enthalten
|
||||
ist. Debian stellt die Bibliothe in dem Paket php-sabre-vobject bereit. Nach
|
||||
der Installation und Konfiguration des Websevers inklusive PHP wird das Repo
|
||||
in das Dokumentroot des Webservers kopiert.
|
||||
|
|
@ -14,13 +14,13 @@ BEGIN:VEVENT
|
|||
SUMMARY:"Chaostreff"
|
||||
UID:0000000002@kraut.space
|
||||
CLASS:PUBLIC
|
||||
DESCRIPTION:Offene Runde
|
||||
DESCRIPTION:"Offene Runde"
|
||||
DTEND;TZID=CET:20200623T235900
|
||||
DTSTAMP;TZID=CET:20200623T125900
|
||||
DTSTART;TZID=CET:20200623T200000
|
||||
GEO:50.9292035\,11.5825763
|
||||
LOCATION:Krautgasse 26, 07743 Jena und online unter https://kraut.space/chaostreff
|
||||
URL:https://wiki.kraut.space/hswiki:veranstaltungen:regelmaessige:chaostreff:start
|
||||
LOCATION:"Krautgasse 26\, 07743 Jena"
|
||||
URL;VALUE="HTTPS://kraut.space/chaostreff"
|
||||
ORGANIZER;CN="VORSTAND HACKSPACE JENA E.V.":MAILTO:office@kraut.space
|
||||
REFRESH-INTERVAL;VALUE=DURATION:P1W
|
||||
RRULE:FREQ=WEEKLY
|
||||
|
@ -29,28 +29,28 @@ BEGIN:VEVENT
|
|||
SUMMARY:"Brettspielrunde"
|
||||
UID:0000000003@kraut.space
|
||||
CLASS:PUBLIC
|
||||
DESCRIPTION:Brettspielerei
|
||||
DTSTAMP;TZID=CET:20221116T125900
|
||||
DTSTART;TZID=CET:20221116T200000
|
||||
DTEND;TZID=CET:20221116T235900
|
||||
DESCRIPTION:"Brettspielerei"
|
||||
DTEND;TZID=CET:20220518T235900
|
||||
DTSTAMP;TZID=CET:20220518T125900
|
||||
DTSTART;TZID=CET:20220518T200000
|
||||
GEO:50.9292035\,11.5825763
|
||||
LOCATION:Krautgasse 26, 07743 Jena
|
||||
URL:https://wiki.kraut.space/hswiki:veranstaltungen:regelmaessige:brettspielerei
|
||||
LOCATION:"Krautgasse 26\, 07743 Jena"
|
||||
URL;VALUE="HTTPS://kraut.space/brettspielerei"
|
||||
ORGANIZER;CN="VORSTAND HACKSPACE JENA E.V.":MAILTO:office@kraut.space
|
||||
REFRESH-INTERVAL;VALUE=DURATION:P1W
|
||||
RRULE:FREQ=WEEKLY;INTERVAL=2;WKST=MO;BYDAY=WE;BYWEEKNO=2,4,6,8,10,12,14,16,18,20,22,24,26,28,30,32,36,38,40,42,44,46,48,50,52
|
||||
RRULE:FREQ=WEEKLY;INTERVAL=2;WKST=MO;BYDAY=FR;BYWEEKNO=2,4,6,8,10,12,14,16,18,20,22,24,26,28,30,32,36,38,40,42,44,46,48,50,52
|
||||
END:VEVENT
|
||||
BEGIN:VEVENT
|
||||
SUMMARY:"Linux User Group"
|
||||
UID:0000000004@kraut.space
|
||||
CLASS:PUBLIC
|
||||
DESCRIPTION:Alles rund um Linux
|
||||
DESCRIPTION:"Alles rund um Linux"
|
||||
DTEND;TZID=CET:20200625T235900
|
||||
DTSTAMP;TZID=CET:20200623T125900
|
||||
DTSTART;TZID=CET:20200625T200000
|
||||
GEO:50.9292035\,11.5825763
|
||||
LOCATION:Krautgasse 26, 07743 Jena und online unter https://kraut.space/lug
|
||||
URL:https://wiki.kraut.space/hswiki:veranstaltungen:regelmaessige:lug:start
|
||||
LOCATION:"Krautgasse 26\, 07743 Jena"
|
||||
URL;VALUE="HTTPS://kraut.space/lug"
|
||||
ORGANIZER;CN="VORSTAND HACKSPACE JENA E.V.":MAILTO:office@kraut.space
|
||||
REFRESH-INTERVAL;VALUE=DURATION:P1W
|
||||
RRULE:FREQ=WEEKLY
|
||||
|
@ -59,13 +59,13 @@ BEGIN:VEVENT
|
|||
SUMMARY:"Gaming am Freitag"
|
||||
UID:0000000005@kraut.space
|
||||
CLASS:PUBLIC
|
||||
DESCRIPTION:Der Stammtisch für Videospielkultur
|
||||
DESCRIPTION:"Der Stammtisch für Videospielkultur"
|
||||
DTEND;TZID=CET:20220527T235900
|
||||
DTSTAMP;TZID=CET:20220527T125900
|
||||
DTSTART;TZID=CET:20220527T190000
|
||||
GEO:50.9292035\,11.5825763
|
||||
LOCATION:Krautgasse 26, 07743 Jena
|
||||
URL:https://wiki.kraut.space/hswiki:veranstaltungen:regelmaessige:gaming
|
||||
LOCATION:"Krautgasse 26\, 07743 Jena"
|
||||
URL;VALUE="HTTPS://kraut.space/gamingamfreitag"
|
||||
ORGANIZER;CN="VORSTAND HACKSPACE JENA E.V.":MAILTO:office@kraut.space
|
||||
REFRESH-INTERVAL;VALUE=DURATION:P1W
|
||||
RRULE:FREQ=WEEKLY;INTERVAL=2;WKST=MO;BYDAY=FR;BYWEEKNO=1,3,5,7,9,11,13,15,17,19,21,23,25,27,29,31,33,35,37,39,41,43,45,47,49,51,53
|
||||
|
@ -74,15 +74,15 @@ BEGIN:VEVENT
|
|||
SUMMARY:"Kinderbasteln"
|
||||
UID:0000000006@kraut.space
|
||||
CLASS:PUBLIC
|
||||
DESCRIPTION:Natur und Technik für Vor- und Grundschulkinder
|
||||
DESCRIPTION:"Natur und Technik für Vor- und Grundschulkinder"
|
||||
DTEND;TZID=CET:20220611T120000
|
||||
DTSTAMP;TZID=CET:20220611T100000
|
||||
DTSTART;TZID=CET:20220611T100000
|
||||
GEO:50.9292035\,11.5825763
|
||||
LOCATION:Krautgasse 26, 07743 Jena
|
||||
URL:https://wiki.kraut.space/hswiki:veranstaltungen:regelmaessige:kinderbasteln
|
||||
LOCATION:"Krautgasse 26\, 07743 Jena"
|
||||
URL;VALUE="HTTPS://kraut.space/kinderbasteln"
|
||||
ORGANIZER;CN="VORSTAND HACKSPACE JENA E.V.":MAILTO:basteln@kraut.space
|
||||
REFRESH-INTERVAL;VALUE=DURATION:P1W
|
||||
RRULE:WKST=MO;FREQ=WEEKLY;BYDAY=2SA,4SA
|
||||
RRULE:FREQ=WEEKLY;INTERVAL=2;WKST=MO;BYDAY=FR;BYWEEKNO=1,3,5,7,9,11,13,15,17,19,21,23,25,27,29,31,33,35,37,39,41,43,45,47,49,51,53
|
||||
END:VEVENT
|
||||
END:VCALENDAR
|
||||
|
|
|
@ -1,192 +1,344 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* file: getEvents.php
|
||||
* date: 20.11.2022
|
||||
* user: bernd@nr18.space
|
||||
* 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");
|
||||
|
||||
ini_set('log_errors', 1);
|
||||
ini_set('error_log', '../log/events.log');
|
||||
error_reporting(E_ALL & ~E_NOTICE & ~E_WARNING & ~E_DEPRECATED);
|
||||
|
||||
use Sabre\VObject;
|
||||
|
||||
include '/usr/share/php/Sabre/VObject/autoload.php';
|
||||
|
||||
define ("ICAL_FILE", "krautspace.ics");
|
||||
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");
|
||||
|
||||
/**
|
||||
* Reads the content of the given ical file and creates with this string a
|
||||
* vcalendar object.
|
||||
* Startdatum für wiederkehrende Termine aktualisieren
|
||||
*
|
||||
* @return vCalendar object | null
|
||||
* @var ZCiCalNode $event
|
||||
*/
|
||||
function initCalendar(): ?object {
|
||||
try {
|
||||
$data = file_get_contents(ICAL_FILE);
|
||||
$vcalendar = VObject\Reader::read(
|
||||
$data
|
||||
);
|
||||
return $vcalendar;
|
||||
} catch (Throwable $th) {
|
||||
error_log("Failed to read calendar file.");
|
||||
error_log($th->getMessage(), 0);
|
||||
return null;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Grabs all VEVENTS from vCalendar object, append they to a list and
|
||||
* returns the list.
|
||||
*
|
||||
* @param vCalendar object
|
||||
* @return list | null
|
||||
* Eventliste nach Startdatum sortieren
|
||||
*/
|
||||
function grabEvents(object $vcalendar): ?array {
|
||||
$eventlist = [];
|
||||
try {
|
||||
foreach ($vcalendar->VEVENT as $event) {
|
||||
$eventlist[] = $event;
|
||||
}
|
||||
} catch (Throwable $th) {
|
||||
error_log("Failed to grab evens.");
|
||||
error_log($th->getMessage(), 0);
|
||||
return null;
|
||||
}
|
||||
return $eventlist;
|
||||
}
|
||||
usort($events, 'compareEventStart');
|
||||
|
||||
/**
|
||||
* Becomes an instance from recurrency rule iterator and an datetime object.
|
||||
* Until the iterators date is in future, we call for the next date.
|
||||
*
|
||||
* @param iterator object
|
||||
* @param datetime
|
||||
* @return datetime | null
|
||||
* Termine ausgeben
|
||||
*/
|
||||
function getNextDate(object $rriterator, DateTime $now): ?DateTime {
|
||||
foreach ($rriterator as $item) {
|
||||
if ($item > $now) {
|
||||
return $item;
|
||||
foreach ($events as $event)
|
||||
{
|
||||
printListItem($event);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
return true;
|
||||
}
|
||||
|
||||
function printListItem(ZCiCalNode $event): bool
|
||||
/**
|
||||
* Main function to display the events.
|
||||
*
|
||||
* @param array
|
||||
* @param object
|
||||
* 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.
|
||||
*/
|
||||
function printEvent(array $eventarray, object $date_helper) {
|
||||
$start_datetime = $eventarray[0];
|
||||
$event = $eventarray[1];
|
||||
$day = toGerman($start_datetime->format("l"));
|
||||
$start = $start_datetime->format("d.m.Y H:i");
|
||||
{
|
||||
date_default_timezone_set("UTC");
|
||||
$helper = new ZDateHelper();
|
||||
$hyph = " - ";
|
||||
$uhr = " Uhr";
|
||||
$komma = ", ";
|
||||
$dateline = "{$day}{$komma}{$start}{$uhr}{$hyph}";
|
||||
|
||||
if ($event->URL != "") {
|
||||
printHeadline($dateline, $event->SUMMARY, $event->URL);
|
||||
} else {
|
||||
printHeadline($dateline, $event->SUMMARY);
|
||||
$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;
|
||||
}
|
||||
if ($event->DESCRIPTION != "") {
|
||||
printDescription($event->DESCRIPTION);
|
||||
|
||||
function displayHeadline(string $dateline, $event_title): bool
|
||||
{
|
||||
echo "\n<section class='termin'>\n";
|
||||
echo "<p class='headline'>" . $dateline . ": " . $event_title . "</p>\n";
|
||||
return true;
|
||||
}
|
||||
if ($event->LOCATION != "") {
|
||||
printLocation($event->LOCATION);
|
||||
|
||||
function displayLocation(string $location): bool
|
||||
{
|
||||
echo "<ul class='events'>\n";
|
||||
echo "<li>$location</li>\n";
|
||||
return true;
|
||||
}
|
||||
printBottomline();
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Outputs the opening tags for section and ul, the date and time, and
|
||||
* summary. ifi a url is given, the summary is a link to the corresponding
|
||||
* wiki page.
|
||||
*
|
||||
* @param string
|
||||
* @param string
|
||||
* Die Startfuktion für die Ausgabe der Termine als Tabelle in termine.php
|
||||
*/
|
||||
function printHeadline(string $dateline, string $summary, string $url=null) {
|
||||
echo("\n<section class='termin'>");
|
||||
if ($url == null) {
|
||||
echo("\n<p class='headline'>$dateline$summary</p>");
|
||||
} else {
|
||||
echo("\n<p class='headline'>$dateline<a href='$url'>$summary</a></p>");
|
||||
|
||||
function printEventTable(): bool
|
||||
{
|
||||
$events = initEvents();
|
||||
printTableHead();
|
||||
foreach ($events as $event)
|
||||
{
|
||||
$event_array = getEventArray($event);
|
||||
printTableItem($event_array);
|
||||
}
|
||||
echo("\n<ul class='events'>");
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Outputs the events description.
|
||||
*
|
||||
* @param string
|
||||
* Funktionen, die von beiden Ausgaben benutzt werden.
|
||||
*/
|
||||
function printDescription(string $description) {
|
||||
echo("\n<li>$description</li>");
|
||||
}
|
||||
|
||||
/**
|
||||
* Outputs the events location.
|
||||
*
|
||||
* @param string
|
||||
* @return ZCiCalNode[]|null
|
||||
*/
|
||||
function printLocation(string $location) {
|
||||
$location = makeLinks($location);
|
||||
echo("\n<li>$location</li>");
|
||||
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
|
||||
/**
|
||||
* Outputs the closing tags for ul and section.
|
||||
* Erstellt das Kalenderobjekt vom Typ ZCiCal.
|
||||
* Gibt das Kalenderobjekt oder Null zurück.
|
||||
*/
|
||||
function printBottomline() {
|
||||
echo("\n</ul>");
|
||||
echo("\n</section>\n");
|
||||
{
|
||||
$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
|
||||
/**
|
||||
* Helper function to sort the events chronologically. Compares the first
|
||||
* entry from the event array (DateTime object).
|
||||
*
|
||||
* @param array
|
||||
* @param array
|
||||
* @return integer
|
||||
* Gibt die Anzahl der Events zurück, die das übergebene
|
||||
* Kalenderobjekt enthält. Im Fehlerfall wird Null zurück
|
||||
* gegeben.
|
||||
*/
|
||||
function compareEventStart(array $event_a, array $event_b): int {
|
||||
// echo("Compare " . $event_a[0] . " and " . $event_b[0] . "\n");
|
||||
$a = $event_a[0];
|
||||
$b = $event_b[0];
|
||||
return $a <=> $b;
|
||||
{
|
||||
$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
|
||||
{
|
||||
/**
|
||||
* Becomes the description string and makes urls contained in this string
|
||||
* clickable. If anything goes wrong it returns the origin string.
|
||||
*
|
||||
* @param string
|
||||
* @return string
|
||||
* 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.
|
||||
*/
|
||||
function makeLinks(string $description) {
|
||||
try {
|
||||
$reg_pattern = "/(((http|https)\:\/\/)|(www\.))[a-zA-Z0-9\-\.]+\.[a-zA-Z]{2,5}(\:[a-zA-Z0-9]+)?(\/\S*)?/";
|
||||
return $new = preg_replace($reg_pattern, '<a href="$0" target="_blank" rel="noopener noreferrer">$0</a>', $description);
|
||||
} catch (Throwable $th) {
|
||||
return $description;
|
||||
$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
|
||||
{
|
||||
/**
|
||||
* Translates the english weekday names to german.
|
||||
*
|
||||
* @param string
|
||||
* @return string | null
|
||||
* 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.
|
||||
*/
|
||||
function toGerman(string $day): ?string
|
||||
/**
|
||||
* @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)
|
||||
{
|
||||
|
@ -205,91 +357,131 @@ function toGerman(string $day): ?string
|
|||
case 'Sunday':
|
||||
return 'Sonntag';
|
||||
default:
|
||||
return null;
|
||||
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;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
/// start des programmes
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
function calculateNextStart(int $unix_start, string $rrule): int
|
||||
/**
|
||||
* The main function.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
function printEventList(): bool {
|
||||
error_log("getEvents called");
|
||||
date_default_timezone_set("Europe/Berlin");
|
||||
$today_datetime = new DateTime();
|
||||
date_time_set($today_datetime, 0, 0, 0, 0);
|
||||
$date_helper = new VObject\DateTimeParser();
|
||||
$eventlist = [];
|
||||
$next_events = [];
|
||||
{
|
||||
$counter = 0;
|
||||
|
||||
// einlesen der kalenderdatei
|
||||
|
||||
$vcalendar = initCalendar();
|
||||
if ($vcalendar == null) {
|
||||
error_log("getEvents unsuccessful terminated");
|
||||
return false;
|
||||
$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'];
|
||||
}
|
||||
|
||||
// die events aus dem kalender herausziehen
|
||||
|
||||
$eventlist = grabEvents($vcalendar);
|
||||
if ($eventlist == null) {
|
||||
error_log("gabEvent returns null");
|
||||
return false;
|
||||
$freq_offset = getOffset($frequency);
|
||||
if (isset ($interval))
|
||||
{
|
||||
$offset = $freq_offset * $interval;
|
||||
}
|
||||
elseif (count($eventlist) == 0) {
|
||||
echo("\n<p>Keine Termine gefunden</p>\n");
|
||||
return false;
|
||||
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;
|
||||
}
|
||||
|
||||
// durch die liste der events laufen. wir holen uns den starttermin und
|
||||
// wandeln ihn in ein datetime objekt um. liegt das in der vergangenheit
|
||||
// und der termin hat eine rrule, holen wir uns einen iterator über die
|
||||
// rrule (dazu brauchen wir die uid) und versuche den nächsten gültigen
|
||||
// termin zu bekommen. gibt es einen gültigen termin, wird das event mit
|
||||
// dem neuen starttermin an die liste der relevanten termine angehängt.
|
||||
// ist der starttermin von anfang an in der zukunft, dann wird er
|
||||
// natürlich auch angehängt.
|
||||
|
||||
foreach ($eventlist as $event) {
|
||||
$next_event = [];
|
||||
$e_uid = $event->UID;
|
||||
$e_start = $event->DTSTART;
|
||||
$e_summary = $event->SUMMARY;
|
||||
$e_description = $event->DESCRIPTION;
|
||||
$e_location = $event->LOCATION;
|
||||
$e_url = $event->URL;
|
||||
$start_datetime = $date_helper->parseDateTime($e_start);
|
||||
if ($start_datetime < $today_datetime) {
|
||||
if (isset($event->RRULE)) {
|
||||
$rriter = new VObject\RecurrenceIterator($vcalendar, $e_uid);
|
||||
$start_datetime = getNextDate($rriter, $today_datetime);
|
||||
if ($start_datetime == null) {
|
||||
continue;
|
||||
} else {
|
||||
array_push($next_events, array($start_datetime, $event));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
array_push($next_events, array($start_datetime, $event));
|
||||
function getOffset(string $frequence): int
|
||||
{
|
||||
switch ($frequence)
|
||||
{
|
||||
case 'HOURLY':
|
||||
return 3600;
|
||||
case 'DAILY':
|
||||
return 86400;
|
||||
case 'WEEKLY':
|
||||
return 604800;
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
// liste der anstehenden events nach dem begin sortieren
|
||||
|
||||
usort($next_events, "compareEventStart");
|
||||
|
||||
// die sortierte liste ausgeben
|
||||
|
||||
foreach ($next_events as $event) {
|
||||
printEvent($event, $date_helper);
|
||||
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];
|
||||
}
|
||||
error_log("getEvents successful terminated");
|
||||
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;
|
||||
}
|
||||
|
|
127
src/lib/README.md
Normal file
127
src/lib/README.md
Normal file
|
@ -0,0 +1,127 @@
|
|||
# Zap Calendar iCalendar Library
|
||||
|
||||
(https://github.com/zcontent/icalendar)
|
||||
|
||||
The Zap Calendar iCalendar Library is a PHP library for supporting the iCalendar (RFC 5545) standard.
|
||||
|
||||
This PHP library is for reading and writing iCalendar formatted feeds and
|
||||
files. Features of the library include:
|
||||
|
||||
- Read AND write support for iCalendar files
|
||||
- Object based creation and manipulation of iCalendar files
|
||||
- Supports expansion of RRULE to a list of repeating dates
|
||||
- Supports adding timezone info to iCalendar file
|
||||
|
||||
All iCalendar data is stored in a PHP object tree.
|
||||
This allows any property to be added to the iCalendar feed without
|
||||
requiring specialized library function calls.
|
||||
With power comes responsibility. Missing or invalid properties can cause
|
||||
the resulting iCalendar file to be invalid. Visit [iCalendar.org](http://icalendar.org) to view valid
|
||||
properties and test your feed using the site's [iCalendar validator tool](http://icalendar.org/validator.html).
|
||||
|
||||
Library API documentation can be found at http://icalendar.org/zapcallibdocs
|
||||
|
||||
See the examples folder for programs that read and write iCalendar
|
||||
files. At its simpliest, you need to include the library at the top of your program:
|
||||
|
||||
```php
|
||||
require_once($path_to_library . "/zapcallib.php");
|
||||
```
|
||||
|
||||
Create an ical object using the ZCiCal object:
|
||||
|
||||
```php
|
||||
$icalobj = new ZCiCal();
|
||||
```
|
||||
|
||||
Add an event object:
|
||||
|
||||
```php
|
||||
$eventobj = new ZCiCalNode("VEVENT", $icalobj->curnode);
|
||||
```
|
||||
|
||||
Add a start and end date to the event:
|
||||
|
||||
```php
|
||||
// add start date
|
||||
$eventobj->addNode(new ZCiCalDataNode("DTSTART:" . ZCiCal::fromSqlDateTime("2020-01-01 12:00:00")));
|
||||
|
||||
// add end date
|
||||
$eventobj->addNode(new ZCiCalDataNode("DTEND:" . ZCiCal::fromSqlDateTime("2020-01-01 13:00:00")));
|
||||
```
|
||||
|
||||
Write the object in iCalendar format using the export() function call:
|
||||
|
||||
```php
|
||||
echo $icalobj->export();
|
||||
```
|
||||
|
||||
This example will not validate since it is missing some required elements.
|
||||
Look at the simpleevent.php example for the minimum # of elements
|
||||
needed for a validated iCalendar file.
|
||||
|
||||
To create a multi-event iCalendar file, simply create multiple event objects. For example:
|
||||
|
||||
```php
|
||||
$icalobj = new ZCiCal();
|
||||
$eventobj1 = new ZCiCalNode("VEVENT", $icalobj->curnode);
|
||||
$eventobj1->addNode(new ZCiCalDataNode("SUMMARY:Event 1"));
|
||||
...
|
||||
$eventobj2 = new ZCiCalNode("VEVENT", $icalobj->curnode);
|
||||
$eventobj2->addNode(new ZCiCalDataNode("SUMMARY:Event 2"));
|
||||
...
|
||||
```
|
||||
|
||||
To read an existing iCalendar file/feed, create the ZCiCal object with a string representing the contents of the iCalendar file:
|
||||
|
||||
```php
|
||||
$icalobj = new ZCiCal($icalstring);
|
||||
```
|
||||
|
||||
Large iCalendar files can be read in chunks to reduce the amount of memory needed to hold the iCalendar feed in memory. This example reads 500 events at a time:
|
||||
|
||||
```php
|
||||
$icalobj = null;
|
||||
$eventcount = 0;
|
||||
$maxevents = 500;
|
||||
do
|
||||
{
|
||||
$icalobj = newZCiCal($icalstring, $maxevents, $eventcount);
|
||||
...
|
||||
$eventcount +=$maxevents;
|
||||
}
|
||||
while($icalobj->countEvents() >= $eventcount);
|
||||
```
|
||||
|
||||
You can read the events from an imported (or created) iCalendar object in this manner:
|
||||
|
||||
```php
|
||||
foreach($icalobj->tree->child as $node)
|
||||
{
|
||||
if($node->getName() == "VEVENT")
|
||||
{
|
||||
foreach($node->data as $key => $value)
|
||||
{
|
||||
if($key == "SUMMARY")
|
||||
{
|
||||
echo "event title: " . $value->getValues() . "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Known Limitations
|
||||
|
||||
- Since the library utilizes objects to read and write iCalendar data, the
|
||||
size of the iCalendar data is limited to the amount of available memory on the machine.
|
||||
The ZCiCal() object supports reading a range of events to minimize memory
|
||||
space.
|
||||
- The library ignores timezone info when importing files, instead utilizing PHP's timezone
|
||||
library for calculations (timezones are supported when exporting files).
|
||||
Imported timezones need to be aliased to a [PHP supported timezone](http://php.net/manual/en/timezones.php).
|
||||
- At this time, the library does not support the "BYSETPOS" option in RRULE items.
|
||||
- At this time, the maximum date supported is 2036 to avoid date math issues
|
||||
with 32 bit systems.
|
||||
- Repeating events are limited to a maximum of 5,000 dates to avoid memory or infinite loop issues
|
||||
|
568
src/lib/includes/date.php
Normal file
568
src/lib/includes/date.php
Normal file
|
@ -0,0 +1,568 @@
|
|||
<?php
|
||||
/**
|
||||
* date.php - date helper class
|
||||
*
|
||||
* @package ZapCalLib
|
||||
* @author Dan Cogliano <http://zcontent.net>
|
||||
* @copyright Copyright (C) 2006 - 2017 by Dan Cogliano
|
||||
* @license GNU GPLv3 <http://www.gnu.org/licenses/gpl.html>
|
||||
* @link http://icalendar.org/php-library.html
|
||||
*/
|
||||
|
||||
// No direct access
|
||||
defined('_ZAPCAL') or die( 'Restricted access' );
|
||||
|
||||
/**
|
||||
* Zap Calendar Date Helper Class
|
||||
*
|
||||
* Helper class for various date functions
|
||||
*/
|
||||
class ZDateHelper {
|
||||
|
||||
/**
|
||||
* Find the number of days in a month
|
||||
*
|
||||
* @param int $month Month is between 1 and 12 inclusive
|
||||
*
|
||||
* @param int $year is between 1 and 32767 inclusive
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
static function DayInMonth($month, $year) {
|
||||
$daysInMonth = array(31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31);
|
||||
if ($month != 2) return $daysInMonth[$month - 1];
|
||||
return (checkdate($month, 29, $year)) ? 29 : 28;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is given date today?
|
||||
*
|
||||
* @param int $date date in Unix timestamp format
|
||||
*
|
||||
* @param int $tzid PHP recognized timezone (default is UTC)
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
static function isToday($date, $tzid = "UTC") {
|
||||
$dtz = new DateTimeZone($tzid);
|
||||
$dt = new DateTime("now", $dtz);
|
||||
$now = time() + $dtz->getOffset($dt);
|
||||
return gmdate('Y-m-d', $date) == gmdate('Y-m-d', $now);
|
||||
}
|
||||
|
||||
/**
|
||||
* Is given date before today?
|
||||
*
|
||||
* @param int $date date in Unix timestamp format
|
||||
*
|
||||
* @param int $tzid PHP recognized timezone (default is UTC)
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
static function isBeforeToday($date, $tzid = "UTC"){
|
||||
$dtz = new DateTimeZone($tzid);
|
||||
$dt = new DateTime("now", $dtz);
|
||||
$now = time() + $dtz->getOffset($dt);
|
||||
return mktime(0,0,0,date('m',$now),date('d',$now),date('Y',$now)) >
|
||||
mktime(0,0,0,date('m',$date),date('d',$date),date('Y',$now));
|
||||
}
|
||||
|
||||
/**
|
||||
* Is given date after today?
|
||||
*
|
||||
* @param int $date date in Unix timestamp format
|
||||
*
|
||||
* @param int $tzid PHP recognized timezone (default is UTC)
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
static function isAfterToday($date, $tzid = "UTC"){
|
||||
$dtz = new DateTimeZone($tzid);
|
||||
$dt = new DateTime("now", $dtz);
|
||||
$now = time() + $dtz->getOffset($dt);
|
||||
return mktime(0,0,0,date('m',$now),date('d',$now),date('Y',$now)) <
|
||||
mktime(0,0,0,date('m',$date),date('d',$date),date('Y',$now));
|
||||
}
|
||||
|
||||
/**
|
||||
* Is given date tomorrow?
|
||||
*
|
||||
* @param int $date date in Unix timestamp format
|
||||
*
|
||||
* @param int $tzid PHP recognized timezone (default is UTC)
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
static function isTomorrow($date, $tzid = "UTC") {
|
||||
$dtz = new DateTimeZone($tzid);
|
||||
$dt = new DateTime("now", $dtz);
|
||||
$now = time() + $dtz->getOffset($dt);
|
||||
return gmdate('Y-m-d', $date) == gmdate('Y-m-d', $now + 60 * 60 * 24);
|
||||
}
|
||||
|
||||
/**
|
||||
* Is given date in the future?
|
||||
*
|
||||
* This routine differs from isAfterToday() in that isFuture() will
|
||||
* return true for date-time values later in the same day.
|
||||
*
|
||||
* @param int $date date in Unix timestamp format
|
||||
*
|
||||
* @param int $tzid PHP recognized timezone (default is UTC)
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
static function isFuture($date, $tzid = "UTC"){
|
||||
$dtz = new DateTimeZone($tzid);
|
||||
$dt = new DateTime("now", $dtz);
|
||||
$now = time() + $dtz->getOffset($dt);
|
||||
return $date > $now;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is given date in the past?
|
||||
*
|
||||
* This routine differs from isBeforeToday() in that isPast() will
|
||||
* return true for date-time values earlier in the same day.
|
||||
*
|
||||
* @param int $date date in Unix timestamp format
|
||||
*
|
||||
* @param int $tzid PHP recognized timezone (default is UTC)
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
static function isPast($date, $tzid = "UTC") {
|
||||
$dtz = new DateTimeZone($tzid);
|
||||
$dt = new DateTime("now", $dtz);
|
||||
$now = time() + $dtz->getOffset($dt);
|
||||
return $date < $now;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return current Unix timestamp in local timezone
|
||||
*
|
||||
* @param string $tzid PHP recognized timezone
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
static function now($tzid = "UTC"){
|
||||
$dtz = new DateTimeZone($tzid);
|
||||
$dt = new DateTime("now", $dtz);
|
||||
$now = time() + $dtz->getOffset($dt);
|
||||
return $now;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is given date fall on a weekend?
|
||||
*
|
||||
* @param int $date Unix timestamp
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
static function isWeekend($date) {
|
||||
$dow = gmdate('w',$date);
|
||||
return $dow == 0 || $dow == 6;
|
||||
}
|
||||
|
||||
/**
|
||||
* Format Unix timestamp to SQL date-time
|
||||
*
|
||||
* @param int $t Unix timestamp
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
static function toSqlDateTime($t = 0)
|
||||
{
|
||||
date_default_timezone_set('GMT');
|
||||
if($t == 0)
|
||||
return gmdate('Y-m-d H:i:s',self::now());
|
||||
return gmdate('Y-m-d H:i:s', $t);
|
||||
}
|
||||
|
||||
/**
|
||||
* Format Unix timestamp to SQL date
|
||||
*
|
||||
* @param int $t Unix timestamp
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
static function toSqlDate($t = 0)
|
||||
{
|
||||
date_default_timezone_set('GMT');
|
||||
if($t == 0)
|
||||
return gmdate('Y-m-d',self::now());
|
||||
return gmdate('Y-m-d', $t);
|
||||
}
|
||||
|
||||
/**
|
||||
* Format iCal date-time string to Unix timestamp
|
||||
*
|
||||
* @param string $datetime in iCal time format ( YYYYMMDD or YYYYMMDDTHHMMSS or YYYYMMDDTHHMMSSZ )
|
||||
*
|
||||
* @return int Unix timestamp
|
||||
*/
|
||||
static function fromiCaltoUnixDateTime($datetime) {
|
||||
// first check format
|
||||
$formats = array();
|
||||
$formats[] = "/[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]/";
|
||||
$formats[] = "/[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]T[0-9][0-9][0-9][0-9][0-9][0-9]/";
|
||||
$formats[] = "/[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]T[0-9][0-9][0-9][0-9][0-9][0-9]Z/";
|
||||
$ok = false;
|
||||
foreach($formats as $format){
|
||||
if(preg_match($format,$datetime)){
|
||||
$ok = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(!$ok)
|
||||
return null;
|
||||
$year = substr($datetime,0,4);
|
||||
$month = substr($datetime,4,2);
|
||||
$day = substr($datetime,6,2);
|
||||
$hour = 0;
|
||||
$minute = 0;
|
||||
$second = 0;
|
||||
if(strlen($datetime) > 8 && $datetime[8] == "T") {
|
||||
$hour = substr($datetime,9,2);
|
||||
$minute = substr($datetime,11,2);
|
||||
$second = substr($datetime,13,2);
|
||||
}
|
||||
return gmmktime($hour, $minute, $second, $month, $day, $year);
|
||||
}
|
||||
|
||||
/**
|
||||
* Format Unix timestamp to iCal date-time string
|
||||
*
|
||||
* @param int $datetime Unix timestamp
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
static function fromUnixDateTimetoiCal($datetime){
|
||||
date_default_timezone_set('GMT');
|
||||
return gmdate("Ymd\THis",$datetime);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert iCal duration string to # of seconds
|
||||
*
|
||||
* @param string $duration iCal duration string
|
||||
*
|
||||
* return int
|
||||
*/
|
||||
static function iCalDurationtoSeconds($duration) {
|
||||
$secs = 0;
|
||||
if($duration[0] == "P") {
|
||||
$duration = str_replace(array("H","M","S","T","D","W","P"),array("H,","M,","S,","","D,","W,",""),$duration);
|
||||
$dur2 = explode(",",$duration);
|
||||
foreach($dur2 as $dur){
|
||||
$val=intval($dur);
|
||||
if(strlen($dur) > 0){
|
||||
switch($dur{strlen($dur) - 1}) {
|
||||
case "H":
|
||||
$secs += 60*60 * $val;
|
||||
break;
|
||||
case "M":
|
||||
$secs += 60 * $val;
|
||||
break;
|
||||
case "S":
|
||||
$secs += $val;
|
||||
break;
|
||||
case "D":
|
||||
$secs += 60*60*24 * $val;
|
||||
break;
|
||||
case "W":
|
||||
$secs += 60*60*24*7 * $val;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return $secs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if day falls within date range
|
||||
*
|
||||
* @param int $daystart start of day in Unix timestamp format
|
||||
*
|
||||
* @param int $begin Unix timestamp of starting date range
|
||||
*
|
||||
* @param int $end Unix timestamp of end date range
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
static function inDay($daystart, $begin, $end)
|
||||
{
|
||||
//$dayend = $daystart + 60*60*24 - 60;
|
||||
// add 1 day to determine end of day
|
||||
// don't use 24 hours, since twice a year DST Sundays are 23 hours and 25 hours in length
|
||||
// adding 1 day takes this into account
|
||||
$dayend = self::addDate($daystart, 0,0,0,0,1,0);
|
||||
|
||||
$end = max($begin, $end); // $end can't be less than $begin
|
||||
$inday =
|
||||
($daystart <= $begin && $begin < $dayend)
|
||||
||($daystart < $end && $end < $dayend)
|
||||
||($begin <= $daystart && $end > $dayend)
|
||||
;
|
||||
return $inday;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert SQL date or date-time to Unix timestamp
|
||||
*
|
||||
* @param string $datetime SQL date or date-time
|
||||
*
|
||||
* @return int Unix date-time timestamp
|
||||
*/
|
||||
static function toUnixDate($datetime)
|
||||
{
|
||||
$year = substr($datetime,0,4);
|
||||
$month = substr($datetime,5,2);
|
||||
$day = substr($datetime,8,2);
|
||||
|
||||
return mktime(0, 0, 0, $month, $day, $year);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert SQL date or date-time to Unix date timestamp
|
||||
*
|
||||
* @param string $datetime SQL date or date-time
|
||||
*
|
||||
* @return int Unix timestamp
|
||||
*/
|
||||
static function toUnixDateTime($datetime)
|
||||
{
|
||||
// convert to absolute dates if neccessary
|
||||
$datetime = self::getAbsDate($datetime);
|
||||
$year = substr($datetime,0,4);
|
||||
$month = substr($datetime,5,2);
|
||||
$day = substr($datetime,8,2);
|
||||
$hour = 0;
|
||||
$minute = 0;
|
||||
$second = 0;
|
||||
if(strlen($datetime) > 10) {
|
||||
$hour = substr($datetime,11,2);
|
||||
$minute = substr($datetime,14,2);
|
||||
$second = substr($datetime,17,2);
|
||||
}
|
||||
return gmmktime($hour, $minute, $second, $month, $day, $year);
|
||||
}
|
||||
|
||||
/**
|
||||
* Date math: add or substract from current date to get a new date
|
||||
*
|
||||
* @param int $date date to add or subtract from
|
||||
*
|
||||
* @param int $hour add or subtract hours from date
|
||||
*
|
||||
* @param int $min add or subtract minutes from date
|
||||
*
|
||||
* @param int $sec add or subtract seconds from date
|
||||
*
|
||||
* @param int $month add or subtract months from date
|
||||
*
|
||||
* @param int $day add or subtract days from date
|
||||
*
|
||||
* @param int $year add or subtract years from date
|
||||
*
|
||||
* @param string $tzid PHP recognized timezone (default is UTC)
|
||||
*/
|
||||
static function addDate($date, $hour, $min, $sec, $month, $day, $year, $tzid = "UTC") {
|
||||
date_default_timezone_set($tzid);
|
||||
$sqldate = self::toSQLDateTime($date);
|
||||
$tdate = array();
|
||||
$tdate["year"] = substr($sqldate,0,4);
|
||||
$tdate["mon"] = substr($sqldate,5,2);
|
||||
$tdate["mday"] = substr($sqldate,8,2);
|
||||
$tdate["hours"] = substr($sqldate,11,2);
|
||||
$tdate["minutes"] = substr($sqldate,14,2);
|
||||
$tdate["seconds"] = substr($sqldate,17,2);
|
||||
$newdate=mktime($tdate["hours"] + $hour, $tdate["minutes"] + $min, $tdate["seconds"] + $sec, $tdate["mon"] + $month, $tdate["mday"] + $day, $tdate["year"] + $year);
|
||||
date_default_timezone_set("UTC");
|
||||
//echo self::toSQLDateTime($date) . " => " . self::toSQLDateTime($newdate) . " ($hour:$min:$sec $month/$day/$year)<br/>\n";
|
||||
return $newdate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Date math: get date from week and day in specifiec month
|
||||
*
|
||||
* This routine finds actual dates for the second Tuesday of the month, last Friday of the month, etc.
|
||||
* For second Tuesday, use $week = 1, $wday = 2
|
||||
* for last Friday, use $week = -1, $wday = 5
|
||||
*
|
||||
* @param int $date Unix timestamp
|
||||
*
|
||||
* @param int $week week number, 0 is first week, -1 is last
|
||||
*
|
||||
* @param int $wday day of week, 0 is Sunday, 6 is Saturday
|
||||
*
|
||||
* @param string $tzid PHP supported timezone
|
||||
*
|
||||
* @return int Unix timestamp
|
||||
*/
|
||||
static function getDateFromDay($date, $week, $wday,$tzid="UTC") {
|
||||
//echo "getDateFromDay(" . self::toSqlDateTime($date) . ",$week,$wday)<br/>\n";
|
||||
// determine first day in month
|
||||
$tdate = getdate($date);
|
||||
$monthbegin = gmmktime(0,0,0, $tdate["mon"],1,$tdate["year"]);
|
||||
$monthend = self::addDate($monthbegin, 0,0,0,1,-1,0,$tzid); // add 1 month and subtract 1 day
|
||||
$day = self::addDate($date,0,0,0,0,1 - $tdate["mday"],0,$tzid);
|
||||
$month = array(array());
|
||||
while($day <= $monthend) {
|
||||
$tdate=getdate($day);
|
||||
$month[$tdate["wday"]][]=$day;
|
||||
//echo self::toSQLDateTime($day) . "<br/>\n";
|
||||
$day = self::addDate($day, 0,0,0,0,1,0,$tzid); // add 1 day
|
||||
}
|
||||
$dayinmonth=0;
|
||||
if($week >= 0)
|
||||
$dayinmonth = $month[$wday][$week];
|
||||
else
|
||||
$dayinmonth = $month[$wday][count($month[$wday]) - 1];
|
||||
//echo "return " . self::toSQLDateTime($dayinmonth);
|
||||
//exit;
|
||||
return $dayinmonth;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert UTC date-time to local date-time
|
||||
*
|
||||
* @param string $sqldate SQL date-time string
|
||||
*
|
||||
* @param string $tzid PHP recognized timezone (default is "UTC")
|
||||
*
|
||||
* @return string SQL date-time string
|
||||
*/
|
||||
static function toLocalDateTime($sqldate, $tzid = "UTC" ){
|
||||
try
|
||||
{
|
||||
$timezone = new DateTimeZone($tzid);
|
||||
}
|
||||
catch(Exception $e)
|
||||
{
|
||||
// bad time zone specified
|
||||
return $sqldate;
|
||||
}
|
||||
$udate = self::toUnixDateTime($sqldate);
|
||||
$daydatetime = new DateTime("@" . $udate);
|
||||
$tzoffset = $timezone->getOffset($daydatetime);
|
||||
return self::toSqlDateTime($udate + $tzoffset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert local date-time to UTC date-time
|
||||
*
|
||||
* @param string $sqldate SQL date-time string
|
||||
*
|
||||
* @param string $tzid PHP recognized timezone (default is "UTC")
|
||||
*
|
||||
* @return string SQL date-time string
|
||||
*/
|
||||
static function toUTCDateTime($sqldate, $tzid = "UTC" ){
|
||||
|
||||
date_default_timezone_set("UTC");
|
||||
try
|
||||
{
|
||||
$date = new DateTime($sqldate, $tzid);
|
||||
}
|
||||
catch(Exception $e)
|
||||
{
|
||||
// bad time zone specified
|
||||
return $sqldate;
|
||||
}
|
||||
$offset = $date->getOffsetFromGMT();
|
||||
if($offset >= 0)
|
||||
$date->sub(new DateInterval("PT".$offset."S"));
|
||||
else
|
||||
$date->add(new DateInterval("PT".abs($offset)."S"));
|
||||
return $date->toSql(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert from a relative date to an absolute date
|
||||
*
|
||||
* Examples of relative dates are "-2y" for 2 years ago, "18m"
|
||||
* for 18 months after today. Relative date uses "y", "m" and "d" for
|
||||
* year, month and day. Relative date can be combined into comma
|
||||
* separated list, i.e., "-1y,-1d" for 1 year and 1 day ago.
|
||||
*
|
||||
* @param string $date relative date string (i.e. "1y" for 1 year from today)
|
||||
*
|
||||
* @param string $rdate reference date, or blank for current date (in SQL date-time format)
|
||||
*
|
||||
* @return string in SQL date-time format
|
||||
*/
|
||||
static function getAbsDate($date,$rdate = ""){
|
||||
if(str_replace(array("y","m","d","h","n"),"",strtolower($date)) != strtolower($date)){
|
||||
date_default_timezone_set("UTC");
|
||||
if($rdate == "")
|
||||
$udate = time();
|
||||
else
|
||||
$udate = self::toUnixDateTime($rdate);
|
||||
$values=explode(",",strtolower($date));
|
||||
$y = 0;
|
||||
$m = 0;
|
||||
$d = 0;
|
||||
$h = 0;
|
||||
$n = 0;
|
||||
foreach($values as $value){
|
||||
$rtype = substr($value,strlen($value)-1);
|
||||
$rvalue = intval(substr($value,0,strlen($value) - 1));
|
||||
switch($rtype){
|
||||
case 'y':
|
||||
$y = $rvalue;
|
||||
break;
|
||||
case 'm':
|
||||
$m = $rvalue;
|
||||
break;
|
||||
case 'd':
|
||||
$d = $rvalue;
|
||||
break;
|
||||
case 'h':
|
||||
$h = $rvalue;
|
||||
break;
|
||||
case 'n':
|
||||
$n = $rvalue;
|
||||
break;
|
||||
}
|
||||
// for "-" values, move to start of day , otherwise, move to end of day
|
||||
if($rvalue[0] == '-')
|
||||
$udate = mktime(0,0,0,date('m',$udate),date('d',$udate),date('Y',$udate));
|
||||
else
|
||||
$udate = mktime(0,-1,0,date('m',$udate),date('d',$udate)+1,date('Y',$udate));
|
||||
$udate = self::addDate($udate,$h,$n,0,$m,$d,$y);
|
||||
}
|
||||
$date = self::toSqlDateTime($udate);
|
||||
}
|
||||
return $date;
|
||||
}
|
||||
|
||||
/**
|
||||
* Format Unix timestamp to iCal date-time format
|
||||
*
|
||||
* @param int $datetime Unix timestamp
|
||||
*
|
||||
* @return string iCal date-time string
|
||||
*/
|
||||
static function toiCalDateTime($datetime = null){
|
||||
date_default_timezone_set('UTC');
|
||||
if($datetime == null)
|
||||
$datetime = time();
|
||||
return gmdate("Ymd\THis",$datetime);
|
||||
}
|
||||
|
||||
/**
|
||||
* Format Unix timestamp to iCal date format
|
||||
*
|
||||
* @param int $datetime Unix timestamp
|
||||
*
|
||||
* @return string iCal date-time string
|
||||
*/
|
||||
static function toiCalDate($datetime = null){
|
||||
date_default_timezone_set('UTC');
|
||||
if($datetime == null)
|
||||
$datetime = time();
|
||||
return gmdate("Ymd",$datetime);
|
||||
}
|
||||
}
|
32
src/lib/includes/framework.php
Normal file
32
src/lib/includes/framework.php
Normal file
|
@ -0,0 +1,32 @@
|
|||
<?php
|
||||
/**
|
||||
* framework.php - framework file
|
||||
*
|
||||
* @package ZapCalLib
|
||||
* @author Dan Cogliano <http://zcontent.net>
|
||||
* @copyright Copyright (C) 2006 - 2017 by Dan Cogliano
|
||||
* @license GNU GPLv3 <http://www.gnu.org/licenses/gpl.html>
|
||||
* @link http://icalendar.org/php-library.html
|
||||
*/
|
||||
|
||||
// No direct access
|
||||
defined('_ZAPCAL') or die( 'Restricted access' );
|
||||
|
||||
/**
|
||||
* set MAXYEAR to 2036 for 32 bit systems, can be higher for 64 bit systems
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
define('_ZAPCAL_MAXYEAR', 2036);
|
||||
|
||||
/**
|
||||
* set MAXREVENTS to maximum # of repeating events
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
define('_ZAPCAL_MAXREVENTS', 5000);
|
||||
|
||||
require_once(_ZAPCAL_BASE . '/includes/date.php');
|
||||
require_once(_ZAPCAL_BASE . '/includes/recurringdate.php');
|
||||
require_once(_ZAPCAL_BASE . '/includes/ical.php');
|
||||
require_once(_ZAPCAL_BASE . '/includes/timezone.php');
|
986
src/lib/includes/ical.php
Normal file
986
src/lib/includes/ical.php
Normal file
|
@ -0,0 +1,986 @@
|
|||
<?php
|
||||
/**
|
||||
* ical.php create iCalendar data structure
|
||||
*
|
||||
* @package ZapCalLib
|
||||
* @author Dan Cogliano <http://zcontent.net>
|
||||
* @copyright Copyright (C) 2006 - 2017 by Dan Cogliano
|
||||
* @license GNU GPLv3 <http://www.gnu.org/licenses/gpl.html>
|
||||
* @link http://icalendar.org/php-library.html
|
||||
*/
|
||||
|
||||
// No direct access
|
||||
defined('_ZAPCAL') or die( 'Restricted access' );
|
||||
|
||||
/**
|
||||
* Object for storing an unfolded iCalendar line
|
||||
*
|
||||
* The ZCiCalDataNode class contains data from an unfolded iCalendar line
|
||||
*
|
||||
*/
|
||||
class ZCiCalDataNode {
|
||||
/**
|
||||
* The name of the node
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
var $name = "";
|
||||
|
||||
/**
|
||||
* Node parameters (before the colon ":")
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
var $parameter=array();
|
||||
|
||||
/**
|
||||
* Node values (after the colon ":")
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
var $value=array();
|
||||
|
||||
/**
|
||||
* Create an object from an unfolded iCalendar line
|
||||
*
|
||||
* @param string $line An unfolded iCalendar line
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
*/
|
||||
function __construct( $line ) {
|
||||
//echo "ZCiCalDataNode($line)<br/>\n";
|
||||
//separate line into parameters and value
|
||||
// look for colon separating name or parameter and value
|
||||
// first change any escaped colons temporarily to make it easier
|
||||
$tline = str_replace("\\:", "`~", $line);
|
||||
// see if first colon is inside a quoted string
|
||||
$i = 0;
|
||||
$datafind = false;
|
||||
$inquotes = false;
|
||||
while(!$datafind && ($i < strlen($tline))) {
|
||||
//echo "$i: " . $tline[$i] . ", ord() = " . ord($tline{$i}) . "<br>\n";
|
||||
if(!$inquotes && $tline[$i] == ':')
|
||||
$datafind=true;
|
||||
else{
|
||||
$i += 1;
|
||||
if(substr($tline,$i,1) == '"')
|
||||
$inquotes = !$inquotes;
|
||||
}
|
||||
}
|
||||
if($datafind){
|
||||
$value = str_replace("`~","\\:",substr($line,$i+1));
|
||||
// fix escaped characters (don't see double quotes in spec but Apple apparently uses it in iCal)
|
||||
$value = str_replace(array('\\N' , '\\n', '\\"' ), array("\n", "\n" , '"'), $value);
|
||||
$tvalue = str_replace("\\,", "`~", $value);
|
||||
//echo "value: " . $tvalue . "<br>\n";
|
||||
$tvalue = explode(",",$tvalue);
|
||||
$value = str_replace("`~","\\,",$tvalue);
|
||||
$this->value = $value;
|
||||
}
|
||||
|
||||
$parameter = trim(substr($line,0,$i));
|
||||
|
||||
$parameter = str_replace("\\;", "`~", $parameter);
|
||||
$parameters = explode(";", $parameter);
|
||||
$parameters = str_replace("`~", "\\;", $parameters);
|
||||
$this->name = array_shift($parameters);
|
||||
foreach($parameters as $parameter){
|
||||
$pos = strpos($parameter,"=");
|
||||
if($pos > 0){
|
||||
$param = substr($parameter,0,$pos);
|
||||
$paramvalue = substr($parameter,$pos+1);
|
||||
$tvalue = str_replace("\\,", "`~", $paramvalue);
|
||||
//$tvalue = explode(",",$tvalue);
|
||||
$paramvalue = str_replace("`~","\\,",$tvalue);
|
||||
$this->parameter[strtolower($param)] = $paramvalue;
|
||||
//$this->paramvalue[] = $paramvalue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* getName()
|
||||
*
|
||||
* Return the name of the object
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
function getName(){
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get $ith parameter from array
|
||||
* @param int $i
|
||||
*
|
||||
* @return var
|
||||
*/
|
||||
function getParameter($i){
|
||||
return $this->parameter[$i];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get parameter array
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
function getParameters(){
|
||||
return $this->parameter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get comma separated values
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
function getValues(){
|
||||
return implode(",",$this->value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Object for storing a list of unfolded iCalendar lines (ZCiCalDataNode objects)
|
||||
*
|
||||
* @property object $parentnode Parent of this node
|
||||
*
|
||||
* @property array $child Array of children for this node
|
||||
*
|
||||
* @property data $data Array of data for this node
|
||||
*
|
||||
* @property object $next Next sibling of this node
|
||||
*
|
||||
* @property object $prev Previous sibling of this node
|
||||
*/
|
||||
|
||||
class ZCiCalNode {
|
||||
/**
|
||||
* The name of the node
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
var $name="";
|
||||
|
||||
/**
|
||||
* The parent of this node
|
||||
*
|
||||
* @var object
|
||||
*/
|
||||
var $parentnode=null;
|
||||
|
||||
/**
|
||||
* Array of children for this node
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
var $child= array();
|
||||
|
||||
/**
|
||||
* Array of $data for this node
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
var $data= array();
|
||||
|
||||
|
||||
/**
|
||||
* Next sibling of this node
|
||||
*
|
||||
* @var object
|
||||
*/
|
||||
var $next=null;
|
||||
|
||||
/**
|
||||
* Previous sibling of this node
|
||||
*
|
||||
* @var object
|
||||
*/
|
||||
var $prev=null;
|
||||
|
||||
/**
|
||||
* Create ZCiCalNode
|
||||
*
|
||||
* @param string $_name Name of node
|
||||
*
|
||||
* @param object $_parent Parent node for this node
|
||||
*
|
||||
* @param bool $first Is this the first child for this parent?
|
||||
*/
|
||||
function __construct( $_name, & $_parent, $first = false) {
|
||||
$this->name = $_name;
|
||||
$this->parentnode = $_parent;
|
||||
if($_parent != null){
|
||||
if(count($this->parentnode->child) > 0) {
|
||||
if($first)
|
||||
{
|
||||
$first = & $this->parentnode->child[0];
|
||||
$first->prev = & $this;
|
||||
$this->next = & $first;
|
||||
}
|
||||
else
|
||||
{
|
||||
$prev =& $this->parentnode->child[count($this->parentnode->child)-1];
|
||||
$prev->next =& $this;
|
||||
$this->prev =& $prev;
|
||||
}
|
||||
}
|
||||
if($first)
|
||||
{
|
||||
array_unshift($this->parentnode->child, $this);
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->parentnode->child[] =& $this;
|
||||
}
|
||||
}
|
||||
/*
|
||||
echo "creating " . $this->getName();
|
||||
if($_parent != null)
|
||||
echo " child of " . $_parent->getName() . "/" . count($this->parentnode->child);
|
||||
echo "<br/>";
|
||||
*/
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the name of the object
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
function getName() {
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add node to list
|
||||
*
|
||||
* @param object $node
|
||||
*
|
||||
*/
|
||||
function addNode($node) {
|
||||
if(array_key_exists($node->getName(), $this->data))
|
||||
{
|
||||
if(!is_array($this->data[$node->getName()]))
|
||||
{
|
||||
$this->data[$node->getName()] = array($this->data[$node->getName()]);
|
||||
}
|
||||
$this->data[$node->getName()][] = $node;
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->data[$node->getName()] = $node;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Attribute
|
||||
*
|
||||
* @param int $i array id of attribute to get
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
function getAttrib($i) {
|
||||
return $this->attrib[$i];
|
||||
}
|
||||
|
||||
/**
|
||||
* Set Attribute
|
||||
*
|
||||
* @param string $value value of attribute to set
|
||||
*
|
||||
*/
|
||||
function setAttrib($value) {
|
||||
$this->attrib[] = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the parent object of this object
|
||||
*
|
||||
* @return object parent of this object
|
||||
*/
|
||||
function &getParent() {
|
||||
return $this->parentnode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the first child of this object
|
||||
*
|
||||
* @return object The first child
|
||||
*/
|
||||
function &getFirstChild(){
|
||||
static $nullguard = null;
|
||||
if(count($this->child) > 0) {
|
||||
//echo "moving from " . $this->getName() . " to " . $this->child[0]->getName() . "<br/>";
|
||||
return $this->child[0];
|
||||
}
|
||||
else
|
||||
return $nullguard;
|
||||
}
|
||||
|
||||
/**
|
||||
* Print object tree in HTML for debugging purposes
|
||||
*
|
||||
* @param object $node select part of tree to print, or leave blank for full tree
|
||||
*
|
||||
* @param int $level Level of recursion (usually leave this blank)
|
||||
*
|
||||
* @return string - HTML formatted display of object tree
|
||||
*/
|
||||
function printTree(& $node=null, $level=1){
|
||||
$level += 1;
|
||||
$html = "";
|
||||
if($node == null)
|
||||
$node = $this->parentnode;
|
||||
if($level > 5)
|
||||
{
|
||||
die("levels nested too deep<br/>\n");
|
||||
//return;
|
||||
}
|
||||
for($i = 0 ; $i < $level; $i ++)
|
||||
$html .= "+";
|
||||
$html .= $node->getName() . "<br/>\n";
|
||||
foreach ($node->child as $c){
|
||||
$html .= $node->printTree($c,$level);
|
||||
}
|
||||
$level -= 1;
|
||||
return $html;
|
||||
}
|
||||
|
||||
/**
|
||||
* export tree to icalendar format
|
||||
*
|
||||
* @param object $node Top level node to export
|
||||
*
|
||||
* @param int $level Level of recursion (usually leave this blank)
|
||||
*
|
||||
* @return string iCalendar formatted output
|
||||
*/
|
||||
function export(& $node=null, $level=0){
|
||||
$txtstr = "";
|
||||
if($node == null)
|
||||
$node = $this;
|
||||
if($level > 5)
|
||||
{
|
||||
//die("levels nested too deep<br/>\n");
|
||||
throw new Exception("levels nested too deep");
|
||||
}
|
||||
$txtstr .= "BEGIN:" . $node->getName() . "\r\n";
|
||||
if(property_exists($node,"data"))
|
||||
foreach ($node->data as $d){
|
||||
if(is_array($d))
|
||||
{
|
||||
foreach ($d as $c)
|
||||
{
|
||||
//$txtstr .= $node->export($c,$level + 1);
|
||||
$p = "";
|
||||
$params = @$c->getParameters();
|
||||
if(count($params) > 0)
|
||||
{
|
||||
foreach($params as $key => $value){
|
||||
$p .= ";" . strtoupper($key) . "=" . $value;
|
||||
}
|
||||
}
|
||||
$txtstr .= $this->printDataLine($c, $p);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$p = "";
|
||||
$params = @$d->getParameters();
|
||||
if(count($params) > 0)
|
||||
{
|
||||
foreach($params as $key => $value){
|
||||
$p .= ";" . strtoupper($key) . "=" . $value;
|
||||
}
|
||||
}
|
||||
$txtstr .= $this->printDataLine($d, $p);
|
||||
/*
|
||||
$values = $d->getValues();
|
||||
// don't think we need this, Sunbird does not like it in the EXDATE field
|
||||
//$values = str_replace(",", "\\,", $values);
|
||||
|
||||
$line = $d->getName() . $p . ":" . $values;
|
||||
$line = str_replace(array("<br>","<BR>","<br/>","<BR/"),"\\n",$line);
|
||||
$line = str_replace(array("\r\n","\n\r","\n","\r"),'\n',$line);
|
||||
//$line = str_replace(array(',',';','\\'), array('\\,','\\;','\\\\'),$line);
|
||||
//$line =strip_tags($line);
|
||||
$linecount = 0;
|
||||
while (strlen($line) > 0) {
|
||||
$linewidth = ($linecount == 0? 75 : 74);
|
||||
$linesize = (strlen($line) > $linewidth? $linewidth: strlen($line));
|
||||
if($linecount > 0)
|
||||
$txtstr .= " ";
|
||||
$txtstr .= substr($line,0,$linesize) . "\r\n";
|
||||
$linecount += 1;
|
||||
$line = substr($line,$linewidth);
|
||||
}
|
||||
*/
|
||||
}
|
||||
//echo $line . "\n";
|
||||
}
|
||||
if(property_exists($node,"child"))
|
||||
foreach ($node->child as $c){
|
||||
$txtstr .= $node->export($c,$level + 1);
|
||||
}
|
||||
$txtstr .= "END:" . $node->getName() . "\r\n";
|
||||
return $txtstr;
|
||||
}
|
||||
|
||||
/**
|
||||
* print an attribute line
|
||||
|
||||
* @param object $d attributes
|
||||
* @param object $p properties
|
||||
*
|
||||
*/
|
||||
function printDataLine($d, $p)
|
||||
{
|
||||
$txtstr = "";
|
||||
|
||||
$values = $d->getValues();
|
||||
// don't think we need this, Sunbird does not like it in the EXDATE field
|
||||
//$values = str_replace(",", "\\,", $values);
|
||||
|
||||
$line = $d->getName() . $p . ":" . $values;
|
||||
$line = str_replace(array("<br>","<BR>","<br/>","<BR/"),"\\n",$line);
|
||||
$line = str_replace(array("\r\n","\n\r","\n","\r"),'\n',$line);
|
||||
//$line = str_replace(array(',',';','\\'), array('\\,','\\;','\\\\'),$line);
|
||||
//$line =strip_tags($line);
|
||||
$linecount = 0;
|
||||
while (strlen($line) > 0) {
|
||||
$linewidth = ($linecount == 0? 75 : 74);
|
||||
$linesize = (strlen($line) > $linewidth? $linewidth: strlen($line));
|
||||
if($linecount > 0)
|
||||
$txtstr .= " ";
|
||||
$txtstr .= substr($line,0,$linesize) . "\r\n";
|
||||
$linecount += 1;
|
||||
$line = substr($line,$linewidth);
|
||||
}
|
||||
return $txtstr;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* The main iCalendar object containing ZCiCalDataNodes and ZCiCalNodes.
|
||||
*
|
||||
*/
|
||||
class ZCiCal {
|
||||
/**
|
||||
* The root node of the object tree
|
||||
*
|
||||
* @var object
|
||||
*/
|
||||
var $tree=null;
|
||||
/**
|
||||
* The most recently created node in the tree
|
||||
*
|
||||
* @var object
|
||||
*/
|
||||
var $curnode=null;
|
||||
|
||||
/**
|
||||
* The main iCalendar object containing ZCiCalDataNodes and ZCiCalNodes.
|
||||
*
|
||||
* use maxevents and startevent to read events in multiple passes (to save memory)
|
||||
*
|
||||
* @param string $data icalendar feed string (empty if creating new feed)
|
||||
*
|
||||
* @param int $maxevents maximum # of events to read
|
||||
*
|
||||
* @param int $startevent starting event to read
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
*
|
||||
*/
|
||||
function __construct($data = "", $maxevents = 1000000, $startevent = 0) {
|
||||
|
||||
if($data != ""){
|
||||
// unfold lines
|
||||
// first change all eol chars to "\n"
|
||||
$data = str_replace(array("\r\n", "\n\r", "\n", "\r"), "\n", $data);
|
||||
// now unfold lines
|
||||
//$data = str_replace(array("\n ", "\n "),"!?", $data);
|
||||
$data = str_replace(array("\n ", "\n "),"", $data);
|
||||
// replace special iCal chars
|
||||
$data = str_replace(array("\\\\","\,"),array("\\",","), $data);
|
||||
|
||||
// parse each line
|
||||
$lines = explode("\n", $data);
|
||||
|
||||
$linecount = 0;
|
||||
$eventcount = 0;
|
||||
$eventpos = 0;
|
||||
foreach($lines as $line) {
|
||||
//$line = str_replace("!?", "\n", $line); // add nl back into descriptions
|
||||
// echo ($linecount + 1) . ": " . $line . "<br/>";
|
||||
if(substr($line,0,6) == "BEGIN:") {
|
||||
// start new object
|
||||
$name = substr($line,6);
|
||||
if($name == "VEVENT")
|
||||
{
|
||||
if($eventcount < $maxevents && $eventpos >= $startevent)
|
||||
{
|
||||
$this->curnode = new ZCiCalNode($name, $this->curnode);
|
||||
if($this->tree == null)
|
||||
$this->tree = $this->curnode;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->curnode = new ZCiCalNode($name, $this->curnode);
|
||||
if($this->tree == null)
|
||||
$this->tree = $this->curnode;
|
||||
}
|
||||
//echo "new node: " . $this->curnode->name . "<br/>\n";
|
||||
/*
|
||||
if($this->curnode->getParent() != null)
|
||||
echo "parent of " . $this->curnode->getName() . " is " . $this->curnode->getParent()->getName() . "<br/>";
|
||||
else
|
||||
echo "parent of " . $this->curnode->getName() . " is null<br/>";
|
||||
*/
|
||||
}
|
||||
else if(substr($line,0,4) == "END:") {
|
||||
$name = substr($line,4);
|
||||
if($name == "VEVENT")
|
||||
{
|
||||
if($eventcount < $maxevents && $eventpos >= $startevent)
|
||||
{
|
||||
$eventcount++;
|
||||
if($this->curnode->getName() != $name) {
|
||||
//panic, mismatch in iCal structure
|
||||
//die("Can't read iCal file structure, expecting " . $this->curnode->getName() . " but reading $name instead");
|
||||
throw new Exception("Can't read iCal file structure, expecting " . $this->curnode->getName() . " but reading $name instead");
|
||||
}
|
||||
if($this->curnode->getParent() != null) {
|
||||
//echo "moving up from " . $this->curnode->getName() ;
|
||||
$this->curnode = & $this->curnode->getParent();
|
||||
//echo " to " . $this->curnode->getName() . "<br/>";
|
||||
//echo $this->curnode->getName() . " has " . count($this->curnode->child) . " children<br/>";
|
||||
}
|
||||
}
|
||||
$eventpos++;
|
||||
}
|
||||
else
|
||||
{
|
||||
if($this->curnode->getName() != $name) {
|
||||
//panic, mismatch in iCal structure
|
||||
//die("Can't read iCal file structure, expecting " . $this->curnode->getName() . " but reading $name instead");
|
||||
throw new Exception("Can't read iCal file structure, expecting " . $this->curnode->getName() . " but reading $name instead");
|
||||
}
|
||||
if($this->curnode->getParent() != null) {
|
||||
//echo "moving up from " . $this->curnode->getName() ;
|
||||
$this->curnode = & $this->curnode->getParent();
|
||||
//echo " to " . $this->curnode->getName() . "<br/>";
|
||||
//echo $this->curnode->getName() . " has " . count($this->curnode->child) . " children<br/>";
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
$datanode = new ZCiCalDataNode($line);
|
||||
if($this->curnode->getName() == "VEVENT")
|
||||
{
|
||||
if($eventcount < $maxevents && $eventpos >= $startevent)
|
||||
{
|
||||
if($datanode->getName() == "EXDATE")
|
||||
{
|
||||
if(!array_key_exists($datanode->getName(),$this->curnode->data))
|
||||
{
|
||||
$this->curnode->data[$datanode->getName()] = $datanode;
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->curnode->data[$datanode->getName()]->value[] = $datanode->value[0];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(!array_key_exists($datanode->getName(),$this->curnode->data))
|
||||
{
|
||||
$this->curnode->data[$datanode->getName()] = $datanode;
|
||||
}
|
||||
else
|
||||
{
|
||||
$tnode = $this->curnode->data[$datanode->getName()];
|
||||
$this->curnode->data[$datanode->getName()] = array();
|
||||
$this->curnode->data[$datanode->getName()][] = $tnode;
|
||||
$this->curnode->data[$datanode->getName()][] = $datanode;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if($datanode->getName() == "EXDATE")
|
||||
{
|
||||
if(!array_key_exists($datanode->getName(),$this->curnode->data))
|
||||
{
|
||||
$this->curnode->data[$datanode->getName()] = $datanode;
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->curnode->data[$datanode->getName()]->value[] = $datanode->value[0];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(!array_key_exists($datanode->getName(),$this->curnode->data))
|
||||
{
|
||||
$this->curnode->data[$datanode->getName()] = $datanode;
|
||||
}
|
||||
else
|
||||
{
|
||||
$tnode = $this->curnode->data[$datanode->getName()];
|
||||
$this->curnode->data[$datanode->getName()] = array();
|
||||
$this->curnode->data[$datanode->getName()][] = $tnode;
|
||||
$this->curnode->data[$datanode->getName()][] = $datanode;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
$linecount++;
|
||||
}
|
||||
}
|
||||
else {
|
||||
$name = "VCALENDAR";
|
||||
$this->curnode = new ZCiCalNode($name, $this->curnode);
|
||||
$this->tree = $this->curnode;
|
||||
$datanode = new ZCiCalDataNode("VERSION:2.0");
|
||||
$this->curnode->data[$datanode->getName()] = $datanode;
|
||||
|
||||
$datanode = new ZCiCalDataNode("PRODID:-//ZContent.net//ZapCalLib 1.0//EN");
|
||||
$this->curnode->data[$datanode->getName()] = $datanode;
|
||||
$datanode = new ZCiCalDataNode("CALSCALE:GREGORIAN");
|
||||
$this->curnode->data[$datanode->getName()] = $datanode;
|
||||
$datanode = new ZCiCalDataNode("METHOD:PUBLISH");
|
||||
$this->curnode->data[$datanode->getName()] = $datanode;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* CountEvents()
|
||||
*
|
||||
* Return the # of VEVENTs in the object
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
|
||||
function countEvents() {
|
||||
$count = 0;
|
||||
if(isset($this->tree->child)){
|
||||
foreach($this->tree->child as $child){
|
||||
if($child->getName() == "VEVENT")
|
||||
$count++;
|
||||
}
|
||||
}
|
||||
return $count;
|
||||
}
|
||||
|
||||
/**
|
||||
* CountVenues()
|
||||
*
|
||||
* Return the # of VVENUEs in the object
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
|
||||
function countVenues() {
|
||||
$count = 0;
|
||||
if(isset($this->tree->child)){
|
||||
foreach($this->tree->child as $child){
|
||||
if($child->getName() == "VVENUE")
|
||||
$count++;
|
||||
}
|
||||
}
|
||||
return $count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Export object to string
|
||||
*
|
||||
* This function exports all objects to an iCalendar string
|
||||
*
|
||||
* @return string an iCalendar formatted string
|
||||
*/
|
||||
|
||||
function export() {
|
||||
return $this->tree->export($this->tree);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get first event in object list
|
||||
* Use getNextEvent() to navigate through list
|
||||
*
|
||||
* @return object The first event, or null
|
||||
*/
|
||||
function &getFirstEvent() {
|
||||
static $nullguard = null;
|
||||
if ($this->countEvents() > 0){
|
||||
$child = $this->tree->child[0];
|
||||
$event=false;
|
||||
while(!$event && $child != null){
|
||||
if($child->getName() == "VEVENT")
|
||||
$event = true;
|
||||
else
|
||||
$child = $child->next;
|
||||
}
|
||||
return $child;
|
||||
}
|
||||
else
|
||||
return $nullguard;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get next event in object list
|
||||
*
|
||||
* @param object $event The current event object
|
||||
*
|
||||
* @return object Returns the next event or null if past last event
|
||||
*/
|
||||
function &getNextEvent($event){
|
||||
do{
|
||||
$event = $event->next;
|
||||
} while($event != null && $event->getName() != "VEVENT");
|
||||
return $event;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get first venue in object list
|
||||
* Use getNextVenue() to navigate through list
|
||||
*
|
||||
* @return object The first venue, or null
|
||||
*/
|
||||
function &getFirstVenue() {
|
||||
static $nullguard = null;
|
||||
if ($this->countVenues() > 0){
|
||||
$child = $this->tree->child[0];
|
||||
$event=false;
|
||||
while(!$event && $child != null){
|
||||
if($child->getName() == "VVENUE")
|
||||
$event = true;
|
||||
else
|
||||
$child = $child->next;
|
||||
}
|
||||
return $child;
|
||||
}
|
||||
else
|
||||
return $nullguard;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get next venue in object list
|
||||
*
|
||||
* @param object $venue The current venue object
|
||||
*
|
||||
* @return object Returns the next venue or null if past last venue
|
||||
*/
|
||||
function &getNextVenue($venue){
|
||||
do{
|
||||
$venue = $venue->next;
|
||||
} while($venue != null && $venue->getName() != "VVENUE");
|
||||
return $venue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get first child in object list
|
||||
* Use getNextSibling() and getPreviousSibling() to navigate through list
|
||||
*
|
||||
* @param object $thisnode The parent object
|
||||
*
|
||||
* @return object The child object
|
||||
*/
|
||||
function &getFirstChild(& $thisnode){
|
||||
$nullvalue = null;
|
||||
if(count($thisnode->child) > 0) {
|
||||
//echo "moving from " . $thisnode->getName() . " to " . $thisnode->child[0]->getName() . "<br/>";
|
||||
return $thisnode->child[0];
|
||||
}
|
||||
else
|
||||
return $nullvalue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get next sibling in object list
|
||||
*
|
||||
* @param object $thisnode The current object
|
||||
*
|
||||
* @return object Returns the next sibling
|
||||
*/
|
||||
function &getNextSibling(& $thisnode){
|
||||
return $thisnode->next;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get previous sibling in object list
|
||||
*
|
||||
* @param object $thisnode The current object
|
||||
*
|
||||
* @return object Returns the previous sibling
|
||||
*/
|
||||
function &getPrevSibling(& $thisnode){
|
||||
return $thisnode->prev;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read date/time in iCal formatted string
|
||||
*
|
||||
* @param string iCal formated date/time string
|
||||
*
|
||||
* @return int Unix timestamp
|
||||
* @deprecated Use ZDateHelper::toUnixDateTime() instead
|
||||
*/
|
||||
|
||||
function toUnixDateTime($datetime){
|
||||
$year = substr($datetime,0,4);
|
||||
$month = substr($datetime,4,2);
|
||||
$day = substr($datetime,6,2);
|
||||
$hour = 0;
|
||||
$minute = 0;
|
||||
$second = 0;
|
||||
if(strlen($datetime) > 8 && $datetime[8] == "T") {
|
||||
$hour = substr($datetime,9,2);
|
||||
$minute = substr($datetime,11,2);
|
||||
$second = substr($datetime,13,2);
|
||||
}
|
||||
$d1 = mktime($hour, $minute, $second, $month, $day, $year);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* fromUnixDateTime()
|
||||
*
|
||||
* Take Unix timestamp and format to iCal date/time string
|
||||
*
|
||||
* @param int $datetime Unix timestamp, leave blank for current date/time
|
||||
*
|
||||
* @return string formatted iCal date/time string
|
||||
* @deprecated Use ZDateHelper::fromUnixDateTimetoiCal() instead
|
||||
*/
|
||||
|
||||
static function fromUnixDateTime($datetime = null){
|
||||
date_default_timezone_set('UTC');
|
||||
if($datetime == null)
|
||||
$datetime = time();
|
||||
return date("Ymd\THis",$datetime);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* fromUnixDate()
|
||||
*
|
||||
* Take Unix timestamp and format to iCal date string
|
||||
*
|
||||
* @param int $datetime Unix timestamp, leave blank for current date/time
|
||||
*
|
||||
* @return string formatted iCal date string
|
||||
* @deprecated Use ZDateHelper::fromUnixDateTimetoiCal() instead
|
||||
*/
|
||||
|
||||
static function fromUnixDate($datetime = null){
|
||||
date_default_timezone_set('UTC');
|
||||
if($datetime == null)
|
||||
$datetime = time();
|
||||
return date("Ymd",$datetime);
|
||||
}
|
||||
|
||||
/**
|
||||
* Format into iCal time format from SQL date or SQL date-time format
|
||||
*
|
||||
* @param string $datetime SQL date or SQL date-time string
|
||||
*
|
||||
* @return string iCal formatted string
|
||||
* @deprecated Use ZDateHelper::fromSqlDateTime() instead
|
||||
*/
|
||||
static function fromSqlDateTime($datetime = ""){
|
||||
if($datetime == "")
|
||||
$datetime = ZDateHelper::toSqlDateTime();
|
||||
if(strlen($datetime) > 10)
|
||||
return sprintf('%04d%02d%02dT%02d%02d%02d',substr($datetime,0,4),substr($datetime,5,2),substr($datetime,8,2),
|
||||
substr($datetime,11,2),substr($datetime,14,2),substr($datetime,17,2));
|
||||
else
|
||||
return sprintf('%04d%02d%02d',substr($datetime,0,4),substr($datetime,5,2),substr($datetime,8,2));
|
||||
}
|
||||
|
||||
/**
|
||||
* Format iCal time format to either SQL date or SQL date-time format
|
||||
*
|
||||
* @param string $datetime icalendar formatted date or date-time
|
||||
* @return string SQL date or SQL date-time string
|
||||
* @deprecated Use ZDateHelper::toSqlDateTime() instead
|
||||
*/
|
||||
static function toSqlDateTime($datetime = ""){
|
||||
if($datetime == "")
|
||||
return ZDateHelper::toSqlDateTime();
|
||||
if(strlen($datetime) > 10)
|
||||
return sprintf('%04d-%02d-%02d %02d:%02d:%02d',substr($datetime,0,4),substr($datetime,5,2),substr($datetime,8,2),
|
||||
substr($datetime,11,2),substr($datetime,14,2),substr($datetime,17,2));
|
||||
else
|
||||
return sprintf('%04d-%02d-%02d',substr($datetime,0,4),substr($datetime,5,2),substr($datetime,8,2));
|
||||
}
|
||||
|
||||
/**
|
||||
* Pull timezone data from node and put in array
|
||||
*
|
||||
* Returning array contains the following array keys: tzoffsetfrom, tzoffsetto, tzname, dtstart, rrule
|
||||
*
|
||||
* @param array $node timezone object
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
static function getTZValues($node){
|
||||
$tzvalues = array();
|
||||
|
||||
$tnode = @$node->data['TZOFFSETFROM'];
|
||||
if($tnode != null){
|
||||
$tzvalues["tzoffsetfrom"] = $tnode->getValues();
|
||||
}
|
||||
|
||||
$tnode = @$node->data['TZOFFSETTO'];
|
||||
if($tnode != null){
|
||||
$tzvalues["tzoffsetto"] = $tnode->getValues();
|
||||
}
|
||||
|
||||
$tnode = @$node->data['TZNAME'];
|
||||
if($tnode != null){
|
||||
$tzvalues["tzname"] = $tnode->getValues();
|
||||
}
|
||||
else
|
||||
$tzvalues["tzname"] = "";
|
||||
|
||||
$tnode = @$node->data['DTSTART'];
|
||||
if($tnode != null){
|
||||
$tzvalues["dtstart"] = ZDateHelper::fromiCaltoUnixDateTime($tnode->getValues());
|
||||
}
|
||||
|
||||
$tnode = @$node->data['RRULE'];
|
||||
if($tnode != null){
|
||||
$tzvalues["rrule"] = $tnode->getValues();
|
||||
//echo "rule: " . $tzvalues["rrule"] . "<br/>\n";
|
||||
}
|
||||
else{
|
||||
// no rule specified, let's create one from based on the date
|
||||
$date = getdate($tzvalues["dtstart"]);
|
||||
$month = $date["mon"];
|
||||
$day = $date["mday"];
|
||||
$tzvalues["rrule"] = "FREQ=YEARLY;INTERVAL=1;BYMONTH=$month;BYMONTHDAY=$day";
|
||||
}
|
||||
|
||||
return $tzvalues;
|
||||
}
|
||||
|
||||
/**
|
||||
* Escape slashes, commas and semicolons in strings
|
||||
*
|
||||
* @param string $content
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
static function formatContent($content)
|
||||
{
|
||||
$content = str_replace(array('\\' , ',' , ';' ), array('\\\\' , '\\,' , '\\;' ),$content);
|
||||
return $content;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
?>
|
6
src/lib/includes/index.html
Normal file
6
src/lib/includes/index.html
Normal file
|
@ -0,0 +1,6 @@
|
|||
<html>
|
||||
<head>
|
||||
</head>
|
||||
<body>
|
||||
</body>
|
||||
</html>
|
796
src/lib/includes/recurringdate.php
Normal file
796
src/lib/includes/recurringdate.php
Normal file
|
@ -0,0 +1,796 @@
|
|||
<?php
|
||||
/**
|
||||
* recurringdate.php - create list of dates from recurring rule
|
||||
*
|
||||
* @package ZapCalLib
|
||||
* @author Dan Cogliano <http://zcontent.net>
|
||||
* @copyright Copyright (C) 2006 - 2017 by Dan Cogliano
|
||||
* @license GNU GPLv3 <http://www.gnu.org/licenses/gpl.html>
|
||||
* @link http://icalendar.org/php-library.html
|
||||
*/
|
||||
|
||||
// No direct access
|
||||
defined('_ZAPCAL') or die( 'Restricted access' );
|
||||
|
||||
/**
|
||||
* Zap Calendar Recurring Date Helper Class
|
||||
*
|
||||
* Class to expand recurring rule to a list of dates
|
||||
*/
|
||||
class ZCRecurringDate {
|
||||
/**
|
||||
* rules string
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
var $rules = "";
|
||||
|
||||
/**
|
||||
* start date in Unix Timestamp format (local timezone)
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
var $startdate = null;
|
||||
|
||||
/**
|
||||
* repeating frequency type (i.e. "y" for yearly, "m" for monthly)
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
var $freq = null;
|
||||
|
||||
/**
|
||||
* timezone of event (using PHP timezones)
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
var $tzid = null;
|
||||
|
||||
/**
|
||||
* repeat mode ('c': count, 'u': until)
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
var $repeatmode=null;
|
||||
|
||||
/**
|
||||
* repeat until date (in UTC Unix Timestamp format)
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
var $until=null;
|
||||
|
||||
/**
|
||||
* repeat count when repeat mode is 'c'
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
var $count=0;
|
||||
|
||||
/**
|
||||
* array of repeat by seconds values
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
var $bysecond=array();
|
||||
|
||||
/**
|
||||
* array of repeat by minutes values
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
var $byminute=array();
|
||||
|
||||
/**
|
||||
* array of repeat by hour values
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
var $byhour=array();
|
||||
|
||||
/**
|
||||
* array of repeat by day values
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
var $byday=array();
|
||||
|
||||
/**
|
||||
* array of repeat by month day values
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
var $bymonthday=array();
|
||||
|
||||
/**
|
||||
* array of repeat by month values
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
var $bymonth=array();
|
||||
|
||||
/**
|
||||
* array of repeat by year values
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
var $byyear=array();
|
||||
|
||||
/**
|
||||
* array of repeat by setpos values
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
var $bysetpos=array();
|
||||
|
||||
/**
|
||||
* inteval of repeating event (i.e. every 2 weeks, every 6 months)
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
var $interval = 1;
|
||||
|
||||
/**
|
||||
* debug level (for testing only)
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
var $debug = 0;
|
||||
|
||||
/**
|
||||
* error string (future use)
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
var $error;
|
||||
|
||||
/**
|
||||
* array of exception dates in Unix Timestamp format (UTC dates)
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
var $exdates=array();
|
||||
|
||||
/**
|
||||
* Expand recurring rule to a list of dates
|
||||
*
|
||||
* @param string $rules iCalendar rules string
|
||||
* @param integer $startdate start date in Unix Timestamp format
|
||||
* @param array $exdates array of exception dates
|
||||
* @param string $tzid timezone of event (using PHP timezones)
|
||||
*/
|
||||
function __construct($rules, $startdate, $exdates = array(),$tzid = "UTC"){
|
||||
if(strlen($rules) > 0){
|
||||
//move exdates to event timezone for comparing with event date
|
||||
for($i = 0; $i < count($exdates); $i++)
|
||||
{
|
||||
$exdates[$i] = ZDateHelper::toUnixDateTime(ZDateHelper::toLocalDateTime(ZDateHelper::toSQLDateTime($exdates[$i]),$tzid));
|
||||
}
|
||||
|
||||
$rules=str_replace("\'","",$rules);
|
||||
$this->rules = $rules;
|
||||
if($startdate == null){
|
||||
// if not specified, use start date of beginning of last year
|
||||
$tdate=getdate();
|
||||
$startdate=mktime(0,0,0,1,1,$tdate["year"] - 1);
|
||||
}
|
||||
$this->startdate = $startdate;
|
||||
$this->tzid = $tzid;
|
||||
$this->exdates = $exdates;
|
||||
|
||||
$rules=explode(";", $rules);
|
||||
$ruletype = "";
|
||||
foreach($rules as $rule){
|
||||
$item=explode("=",$rule);
|
||||
//echo $item[0] . "=" . $item[1] . "<br/>\n";
|
||||
switch($item[0]){
|
||||
case "FREQ":
|
||||
switch($item[1]){
|
||||
case "YEARLY":
|
||||
$this->freq="y";
|
||||
break;
|
||||
case "MONTHLY":
|
||||
$this->freq="m";
|
||||
break;
|
||||
case "WEEKLY":
|
||||
$this->freq="w";
|
||||
break;
|
||||
case "DAILY":
|
||||
$this->freq="d";
|
||||
break;
|
||||
case "HOURLY":
|
||||
$this->freq="h";
|
||||
break;
|
||||
case "MINUTELY":
|
||||
$this->freq="i";
|
||||
break;
|
||||
case "SECONDLY":
|
||||
$this->freq="s";
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case "INTERVAL":
|
||||
$this->interval = $item[1];
|
||||
break;
|
||||
case "BYSECOND":
|
||||
$this->bysecond = explode(",",$item[1]);
|
||||
$ruletype = $item[0];
|
||||
break;
|
||||
case "BYMINUTE":
|
||||
$this->byminute = explode(",",$item[1]);
|
||||
$ruletype = $item[0];
|
||||
break;
|
||||
case "BYHOUR":
|
||||
$this->byhour = explode(",",$item[1]);
|
||||
$ruletype = $item[0];
|
||||
break;
|
||||
case "BYDAY":
|
||||
$this->byday = explode(",",$item[1]);
|
||||
$ruletype = $item[0];
|
||||
break;
|
||||
case "BYMONTHDAY":
|
||||
$this->bymonthday = explode(",",$item[1]);
|
||||
$ruletype = $item[0];
|
||||
break;
|
||||
case "BYMONTH":
|
||||
$this->bymonth = explode(",",$item[1]);
|
||||
$ruletype = $item[0];
|
||||
break;
|
||||
case "BYYEAR":
|
||||
$this->byyear = explode(",",$item[1]);
|
||||
$ruletype = $item[0];
|
||||
break;
|
||||
case "COUNT":
|
||||
$this->count = intval($item[1]);
|
||||
$this->repeatmode = "c";
|
||||
break;
|
||||
case "BYSETPOS":
|
||||
$this->bysetpos = explode(",",$item[1]);
|
||||
break;
|
||||
case "UNTIL":
|
||||
$this->until = ZDateHelper::fromiCaltoUnixDateTime($item[1]);
|
||||
$this->repeatmode = "u";
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(count($this->bysetpos) > 0){
|
||||
switch($ruletype){
|
||||
case "BYYEAR":
|
||||
$this->byyear = $this->bySetPos($this->byyear,$this->bysetpos);
|
||||
break;
|
||||
case "BYMONTH":
|
||||
$this->bymonth = $this->bySetPos($this->bymonth,$this->bysetpos);
|
||||
break;
|
||||
case "BYMONTHDAY":
|
||||
$this->bymonthday = $this->bySetPos($this->bymonthday,$this->bysetpos);
|
||||
break;
|
||||
case "BYDAY":
|
||||
$this->byday = $this->bySetPos($this->byday,$this->bysetpos);
|
||||
break;
|
||||
case "BYHOUR":
|
||||
$this->byhour = $this->bySetPos($this->byhour,$this->bysetpos);
|
||||
break;
|
||||
case "BYMINUTE":
|
||||
$this->byminute = $this->bySetPos($this->byminute,$this->bysetpos);
|
||||
break;
|
||||
case "BYSECOND":
|
||||
$this->bysecond = $this->bySetPos($this->bysecond,$this->bysetpos);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* bysetpos rule support
|
||||
*
|
||||
* @param array $bytype
|
||||
* @param array $bysetpos
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
function bySetPos($bytype, $bysetpos){
|
||||
$result = array();
|
||||
for($i=0; $i < count($bysetpos); $i++){
|
||||
for($j=0; $j < count($bytype); $j++){
|
||||
$result[] = $bysetpos[$i] . $bytype[$j];
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* save error
|
||||
*
|
||||
* @param string $msg
|
||||
*/
|
||||
function setError($msg){
|
||||
$this->error = $msg;
|
||||
}
|
||||
|
||||
/**
|
||||
* get error message
|
||||
*
|
||||
* @return string error message
|
||||
*/
|
||||
function getError(){
|
||||
return $this->error;
|
||||
}
|
||||
|
||||
/**
|
||||
* set debug level (0: none, 1: minimal, 2: more output)
|
||||
*
|
||||
* @param integer $level
|
||||
*
|
||||
*/
|
||||
function setDebug($level)
|
||||
{
|
||||
$this->debug = $level;
|
||||
}
|
||||
|
||||
/**
|
||||
* display debug message
|
||||
*
|
||||
* @param integer $level
|
||||
* @param string $msg
|
||||
*/
|
||||
function debug($level, $msg){
|
||||
if($this->debug >= $level)
|
||||
echo $msg . "<br/>\n";
|
||||
}
|
||||
|
||||
/**
|
||||
* Get repeating dates by year
|
||||
*
|
||||
* @param integer $startdate start date of repeating events, in Unix timestamp format
|
||||
* @param integer $enddate end date of repeating events, in Unix timestamp format
|
||||
* @param array $rdates array to contain expanded repeating dates
|
||||
* @param string $tzid timezone of event (using PHP timezones)
|
||||
*
|
||||
* @return integer count of dates
|
||||
*/
|
||||
private function byYear($startdate, $enddate, &$rdates, $tzid="UTC"){
|
||||
self::debug(1,"byYear(" . ZDateHelper::toSqlDateTime($startdate) . ","
|
||||
. ZDateHelper::toSqlDateTime($enddate) . "," . count($rdates) . " dates)");
|
||||
$count = 0;
|
||||
if(count($this->byyear) > 0){
|
||||
foreach($this->byyear as $year){
|
||||
$t = getdate($startdate);
|
||||
$wdate = mktime($t[hours],$t[minutes],$t[seconds],$t[month],$t[mday],$year);
|
||||
if($startdate <= $wdate && $wdate < $enddate && !$this->maxDates($rdates)){
|
||||
$count = $this->byMonth($wdate, $enddate, $rdates, $tzid);
|
||||
if($count == 0) {
|
||||
$rdates[] = $wdate;
|
||||
$count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(!$this->maxDates($rdates))
|
||||
$count = $this->byMonth($startdate, $enddate, $rdates, $tzid);
|
||||
self::debug(1,"byYear() returned " . $count );
|
||||
return $count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get repeating dates by month
|
||||
*
|
||||
* @param integer $startdate start date of repeating events, in Unix timestamp format
|
||||
* @param integer $enddate end date of repeating events, in Unix timestamp format
|
||||
* @param array $rdates array to contain expanded repeating dates
|
||||
* @param string $tzid timezone of event (using PHP timezones)
|
||||
*
|
||||
* @return integer count of dates
|
||||
*/
|
||||
private function byMonth($startdate, $enddate, &$rdates, $tzid="UTC"){
|
||||
self::debug(1,"byMonth(" . ZDateHelper::toSqlDateTime($startdate) . ","
|
||||
. ZDateHelper::toSqlDateTime($enddate) . "," . count($rdates) . " dates)");
|
||||
$count = 0;
|
||||
if(count($this->bymonth) > 0){
|
||||
foreach($this->bymonth as $month){
|
||||
$t = getdate($startdate);
|
||||
$wdate = mktime($t["hours"],$t["minutes"],$t["seconds"],$month,$t["mday"],$t["year"]);
|
||||
if($startdate <= $wdate && $wdate < $enddate && !$this->maxDates($rdates)){
|
||||
$count = $this->byMonthDay($wdate, $enddate, $rdates, $tzid);
|
||||
if($count == 0) {
|
||||
$rdates[] = $wdate;
|
||||
$count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(!$this->maxDates($rdates))
|
||||
$count = $this->byMonthDay($startdate, $enddate, $rdates, $tzid);
|
||||
self::debug(1,"byMonth() returned " . $count );
|
||||
return $count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get repeating dates by month day
|
||||
*
|
||||
* @param integer $startdate start date of repeating events, in Unix timestamp format
|
||||
* @param integer $enddate end date of repeating events, in Unix timestamp format
|
||||
* @param array $rdates array to contain expanded repeating dates
|
||||
* @param string $tzid timezone of event (using PHP timezones)
|
||||
*
|
||||
* @return integer count of dates
|
||||
*/
|
||||
private function byMonthDay($startdate, $enddate, &$rdates, $tzid="UTC"){
|
||||
self::debug(1,"byMonthDay(" . ZDateHelper::toSqlDateTime($startdate) . ","
|
||||
. ZDateHelper::toSqlDateTime($enddate) . "," . count($rdates) . " dates)");
|
||||
$count = 0;
|
||||
self::debug(1,"start date: " . ZDateHelper::toSqlDateTime($startdate));
|
||||
if(count($this->bymonthday) > 0){
|
||||
foreach($this->bymonthday as $day){
|
||||
$day = intval($day);
|
||||
$t = getdate($startdate);
|
||||
$wdate = mktime($t['hours'],$t['minutes'],$t['seconds'],$t['mon'],$day,$t['year']);
|
||||
self::debug(2,"mktime(" . $t['hours'] . ", " . $t['minutes']
|
||||
. ", " . $t['mon'] . ", " . $day . ", " . $t['year'] . ") returned $wdate");
|
||||
if($startdate <= $wdate && $wdate < $enddate && !$this->maxDates($rdates)){
|
||||
$count = $this->byDay($wdate, $enddate, $rdates, $tzid);
|
||||
if($count == 0) {
|
||||
$rdates[] = $wdate;
|
||||
$count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(!$this->maxDates($rdates)) {
|
||||
self::debug(1,"start date: " . ZDateHelper::toSqlDateTime($startdate));
|
||||
$count = $this->byDay($startdate, $enddate, $rdates, $tzid);
|
||||
}
|
||||
self::debug(1,"byMonthDay() returned " . $count );
|
||||
return $count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get repeating dates by day
|
||||
*
|
||||
* @param integer $startdate start date of repeating events, in Unix timestamp format
|
||||
* @param integer $enddate end date of repeating events, in Unix timestamp format
|
||||
* @param array $rdates array to contain expanded repeating dates
|
||||
* @param string $tzid timezone of event (using PHP timezones)
|
||||
*
|
||||
* @return integer count of dates
|
||||
*/
|
||||
private function byDay($startdate, $enddate, &$rdates, $tzid="UTC"){
|
||||
self::debug(1,"byDay(" . ZDateHelper::toSqlDateTime($startdate) . ","
|
||||
. ZDateHelper::toSqlDateTime($enddate) . "," . count($rdates) . " dates)");
|
||||
$days = array(
|
||||
"SU" => 0,
|
||||
"MO" => 1,
|
||||
"TU" => 2,
|
||||
"WE" => 3,
|
||||
"TH" => 4,
|
||||
"FR" => 5,
|
||||
"SA" => 6);
|
||||
$idays = array(
|
||||
0 => "SU",
|
||||
1 => "MO",
|
||||
2 => "TU",
|
||||
3 => "WE",
|
||||
4 => "TH",
|
||||
5 => "FR",
|
||||
6 => "SA");
|
||||
|
||||
$count = 0;
|
||||
if(count($this->byday) > 0){
|
||||
if(empty($this->byday[0]))
|
||||
{
|
||||
$this->byday[0] = $idays[date("w",$startdate)];
|
||||
}
|
||||
foreach($this->byday as $tday){
|
||||
$t = getdate($startdate);
|
||||
$day = substr($tday,strlen($tday) - 2);
|
||||
if(strlen($day) < 2)
|
||||
{
|
||||
// missing start day, use current date for DOW
|
||||
$day = $idays[date("w",$startdate)];
|
||||
}
|
||||
if(strlen($tday) > 2) {
|
||||
$imin = 1;
|
||||
$imax = 5; // max # of occurances in a month
|
||||
if(strlen($tday) > 2)
|
||||
$imin = $imax = substr($tday,0,strlen($tday) - 2);
|
||||
self::debug(2,"imin: $imin, imax: $imax, tday: $tday, day: $day, daynum: {$days[$day]}");
|
||||
for($i = $imin; $i <= $imax; $i++){
|
||||
$wdate = ZDateHelper::getDateFromDay($startdate,$i-1,$days[$day],$tzid);
|
||||
self::debug(2,"getDateFromDay(" . ZDateHelper::toSqlDateTime($startdate)
|
||||
. ",$i,{$days[$day]}) returned " . ZDateHelper::toSqlDateTime($wdate));
|
||||
if($startdate <= $wdate && $wdate < $enddate && !$this->maxDates($rdates)){
|
||||
$count = $this->byHour($wdate, $enddate, $rdates);
|
||||
if($count == 0){
|
||||
$rdates[] = $wdate;
|
||||
$count++;
|
||||
//break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
// day of week version
|
||||
$startdate_dow = date("w",$startdate);
|
||||
$datedelta = $days[$day] - $startdate_dow;
|
||||
self::debug(2, "start_dow: $startdate_dow, datedelta: $datedelta");
|
||||
if($datedelta >= 0)
|
||||
{
|
||||
$wdate = ZDateHelper::addDate($startdate,0,0,0,0,$datedelta,0,$this->tzid);
|
||||
self::debug(2, "wdate: " . ZDateHelper::toSqlDateTime($wdate));
|
||||
if($startdate <= $wdate && $wdate < $enddate && !$this->maxDates($rdates)){
|
||||
$count = $this->byHour($wdate, $enddate, $rdates);
|
||||
if($count == 0){
|
||||
$rdates[] = $wdate;
|
||||
$count++;
|
||||
self::debug(2,"adding date " . ZDateHelper::toSqlDateTime($wdate) );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(!$this->maxDates($rdates))
|
||||
$count = $this->byHour($startdate, $enddate, $rdates);
|
||||
self::debug(1,"byDay() returned " . $count );
|
||||
return $count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get repeating dates by hour
|
||||
*
|
||||
* @param integer $startdate start date of repeating events, in Unix timestamp format
|
||||
* @param integer $enddate end date of repeating events, in Unix timestamp format
|
||||
* @param array $rdates array to contain expanded repeating dates
|
||||
* @param string $tzid timezone of event (using PHP timezones)
|
||||
*
|
||||
* @return integer count of dates
|
||||
*/
|
||||
private function byHour($startdate, $enddate, &$rdates, $tzid="UTC"){
|
||||
self::debug(1,"byHour(" . ZDateHelper::toSqlDateTime($startdate) . ","
|
||||
. ZDateHelper::toSqlDateTime($enddate) . "," . count($rdates) . " dates)");
|
||||
$count = 0;
|
||||
if(count($this->byhour) > 0){
|
||||
foreach($this->byhour as $hour){
|
||||
$t = getdate($startdate);
|
||||
$wdate = mktime($hour,$t["minutes"],$t["seconds"],$t["mon"],$t["mday"],$t["year"]);
|
||||
self::debug(2,"checking date/time " . ZDateHelper::toSqlDateTime($wdate));
|
||||
if($startdate <= $wdate && $wdate < $enddate && !$this->maxDates($rdates)){
|
||||
$count = $this->byMinute($wdate, $enddate, $rdates);
|
||||
if($count == 0) {
|
||||
$rdates[] = $wdate;
|
||||
$count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(!$this->maxDates($rdates))
|
||||
$count = $this->byMinute($startdate, $enddate, $rdates);
|
||||
self::debug(1,"byHour() returned " . $count );
|
||||
return $count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get repeating dates by minute
|
||||
*
|
||||
* @param integer $startdate start date of repeating events, in Unix timestamp format
|
||||
* @param integer $enddate end date of repeating events, in Unix timestamp format
|
||||
* @param array $rdates array to contain expanded repeating dates
|
||||
* @param string $tzid timezone of event (using PHP timezones)
|
||||
*
|
||||
* @return integer count of dates
|
||||
*/
|
||||
private function byMinute($startdate, $enddate, &$rdates, $tzid="UTC"){
|
||||
self::debug(1,"byMinute(" . ZDateHelper::toSqlDateTime($startdate) . ","
|
||||
. ZDateHelper::toSqlDateTime($enddate) . "," . count($rdates) . " dates)");
|
||||
$count = 0;
|
||||
if(count($this->byminute) > 0){
|
||||
foreach($this->byminute as $minute){
|
||||
$t = getdate($startdate);
|
||||
$wdate = mktime($t["hours"],$minute,$t["seconds"],$t["mon"],$t["mday"],$t["year"]);
|
||||
if($startdate <= $wdate && $wdate < $enddate && !$this->maxDates($rdates)){
|
||||
$count = $this->bySecond($wdate, $enddate, $rdates);
|
||||
if($count == 0) {
|
||||
$rdates[] = $wdate;
|
||||
$count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(!$this->maxDates($rdates))
|
||||
$count = $this->bySecond($startdate, $enddate, $rdates);
|
||||
self::debug(1,"byMinute() returned " . $count );
|
||||
return $count;
|
||||
}
|
||||
/**
|
||||
* Get repeating dates by second
|
||||
*
|
||||
* @param integer $startdate start date of repeating events, in Unix timestamp format
|
||||
* @param integer $enddate end date of repeating events, in Unix timestamp format
|
||||
* @param array $rdates array to contain expanded repeating dates
|
||||
* @param string $tzid timezone of event (using PHP timezones)
|
||||
*
|
||||
* @return integer count of dates
|
||||
*/
|
||||
private function bySecond($startdate, $enddate, &$rdates, $tzid="UTC"){
|
||||
self::debug(1,"bySecond(" . ZDateHelper::toSqlDateTime($startdate) . ","
|
||||
. ZDateHelper::toSqlDateTime($enddate) . "," . count($rdates) . " dates)");
|
||||
$count = 0;
|
||||
if(count($this->bysecond) > 0){
|
||||
foreach($this->bysecond as $second){
|
||||
$t = getdate($startdate);
|
||||
$wdate = mktime($t["hours"],$t["minutes"],$second,$t["mon"],$t["mday"],$t["year"]);
|
||||
if($startdate <= $wdate && $wdate < $enddate && !$this->maxDates($rdates)){
|
||||
$rdates[] = $wdate;
|
||||
$count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
self::debug(1,"bySecond() returned " . $count );
|
||||
return $count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the loop has reached the end date
|
||||
*
|
||||
* @param array $rdates array of repeating dates
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
private function maxDates($rdates){
|
||||
if($this->repeatmode == "c" && count($rdates) >= $this->count)
|
||||
return true; // exceeded count
|
||||
else if(count($rdates) > 0 && $this->repeatmode == "u" && $rdates[count($rdates) - 1] > $this->until){
|
||||
return true; //past date
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get array of dates from recurring rule
|
||||
*
|
||||
* @param $maxdate integer maximum date to appear in repeating dates in Unix timestamp format
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getDates($maxdate = null){
|
||||
//$this->debug = 2;
|
||||
self::debug(1,"getDates()");
|
||||
$nextdate = $enddate = $this->startdate;
|
||||
$rdates = array();
|
||||
$done = false;
|
||||
$eventcount = 0;
|
||||
$loopcount = 0;
|
||||
self::debug(2,"freq: " . $this->freq . ", interval: " . $this->interval);
|
||||
while(!$done){
|
||||
self::debug(1,"<b>*** Frequency ({$this->freq}) loop pass $loopcount ***</b>");
|
||||
switch($this->freq){
|
||||
case "y":
|
||||
if($eventcount > 0)
|
||||
{
|
||||
$nextdate = ZDateHelper::addDate($nextdate,0,0,0,0,0,$this->interval,$this->tzid);
|
||||
self::debug(2,"addDate() returned " . ZDateHelper::toSqlDateTime($nextdate));
|
||||
if(!empty($this->byday)){
|
||||
$t = getdate($nextdate);
|
||||
$nextdate = gmmktime($t["hours"],$t["minutes"],$t["seconds"],$t["mon"],1,$t["year"]);
|
||||
}
|
||||
self::debug(2,"nextdate set to $nextdate (". ZDateHelper::toSQLDateTime($nextdate) . ")");
|
||||
}
|
||||
$enddate=ZDateHelper::addDate($nextdate,0,0,0,0,0,1);
|
||||
break;
|
||||
case "m":
|
||||
if($eventcount > 0)
|
||||
{
|
||||
|
||||
$nextdate = ZDateHelper::addDate($nextdate,0,0,0,$this->interval,0,0,$this->tzid);
|
||||
self::debug(2,"addDate() returned " . ZDateHelper::toSqlDateTime($nextdate));
|
||||
}
|
||||
if(count($this->byday) > 0)
|
||||
{
|
||||
$t = getdate($nextdate);
|
||||
if($t["mday"] > 28)
|
||||
{
|
||||
//check for short months when using month by day, make sure we do not overshoot the counter and skip a month
|
||||
$nextdate = ZDateHelper::addDate($nextdate,0,0,0,$this->interval,0,0,$this->tzid);
|
||||
$t2 = getdate($nextdate);
|
||||
if($t2["mday"] < $t["mday"])
|
||||
{
|
||||
// oops, skipped a month, backup to previous month
|
||||
$nextdate = ZDateHelper::addDate($nextdate,0,0,0,0,$t2["mday"] - $t["mday"],0,$this->tzid);
|
||||
}
|
||||
}
|
||||
$t = getdate($nextdate);
|
||||
$nextdate = mktime($t["hours"],$t["minutes"],$t["seconds"],$t["mon"],1,$t["year"]);
|
||||
}
|
||||
self::debug(2,"nextdate set to $nextdate (". ZDateHelper::toSQLDateTime($nextdate) . ")");
|
||||
$enddate=ZDateHelper::addDate($nextdate,0,0,0,$this->interval,0,0);
|
||||
break;
|
||||
case "w":
|
||||
if($eventcount == 0)
|
||||
$nextdate=$nextdate;
|
||||
else {
|
||||
$nextdate = ZDateHelper::addDate($nextdate,0,0,0,0,$this->interval*7,0,$this->tzid);
|
||||
if(count($this->byday) > 0){
|
||||
$dow = date("w", $nextdate);
|
||||
// move to beginning of week (Sunday)
|
||||
$bow = 0;
|
||||
$diff = $bow - $dow;
|
||||
if($diff > 0)
|
||||
$diff = $diff - 7;
|
||||
$nextdate = ZDateHelper::addDate($nextdate,0,0,0,0,$diff,0);
|
||||
}
|
||||
self::debug(2,"nextdate set to $nextdate (". ZDateHelper::toSQLDateTime($nextdate) . ")");
|
||||
}
|
||||
$enddate=ZDateHelper::addDate($nextdate,0,0,0,0,$this->interval*7,0);
|
||||
break;
|
||||
case "d":
|
||||
$nextdate=($eventcount==0?$nextdate:
|
||||
ZDateHelper::addDate($nextdate,0,0,0,0,$this->interval,0,$this->tzid));
|
||||
$enddate=ZDateHelper::addDate($nextdate,0,0,0,0,1,0);
|
||||
break;
|
||||
}
|
||||
|
||||
$count = $this->byYear($nextdate,$enddate,$rdates,$this->tzid);
|
||||
$eventcount += $count;
|
||||
if($maxdate > 0 && $maxdate < $nextdate)
|
||||
{
|
||||
array_pop($rdates);
|
||||
$done = true;
|
||||
}
|
||||
else if($count == 0 && !$this->maxDates($rdates)){
|
||||
$rdates[] = $nextdate;
|
||||
$eventcount++;
|
||||
}
|
||||
if($this->maxDates($rdates))
|
||||
$done = true;
|
||||
|
||||
$year = date("Y", $nextdate);
|
||||
if($year > _ZAPCAL_MAXYEAR)
|
||||
{
|
||||
$done = true;
|
||||
}
|
||||
$loopcount++;
|
||||
if($loopcount > _ZAPCAL_MAXYEAR){
|
||||
$done = true;
|
||||
throw new Exception("Infinite loop detected in getDates()");
|
||||
}
|
||||
}
|
||||
if($this->repeatmode == "u" && $rdates[count($rdates) - 1] > $this->until){
|
||||
// erase last item
|
||||
array_pop($rdates);
|
||||
}
|
||||
$count1 = count($rdates);
|
||||
$rdates = array_unique($rdates);
|
||||
$count2 = count($rdates);
|
||||
$dups = $count1 - $count2;
|
||||
$excount = 0;
|
||||
|
||||
foreach($this->exdates as $exdate)
|
||||
{
|
||||
if($pos = array_search($exdate,$rdates))
|
||||
{
|
||||
array_splice($rdates,$pos,1);
|
||||
$excount++;
|
||||
}
|
||||
}
|
||||
self::debug(1,"getDates() returned " . count($rdates) . " dates, removing $dups duplicates, $excount exceptions");
|
||||
|
||||
|
||||
if($this->debug >= 2)
|
||||
{
|
||||
self::debug(2,"Recurring Dates:");
|
||||
foreach($rdates as $rdate)
|
||||
{
|
||||
$d = getdate($rdate);
|
||||
self::debug(2,ZDateHelper::toSQLDateTime($rdate) . " " . $d["wday"] );
|
||||
}
|
||||
self::debug(2,"Exception Dates:");
|
||||
foreach($this->exdates as $exdate)
|
||||
{
|
||||
self::debug(2, ZDateHelper::toSQLDateTime($exdate));
|
||||
}
|
||||
//exit;
|
||||
}
|
||||
|
||||
return $rdates;
|
||||
}
|
||||
}
|
142
src/lib/includes/timezone.php
Normal file
142
src/lib/includes/timezone.php
Normal file
|
@ -0,0 +1,142 @@
|
|||
<?php
|
||||
/**
|
||||
* timezone.php - create timezone data for use in icalendar file
|
||||
*
|
||||
* @package ZapCalLib
|
||||
* @author Dan Cogliano <http://zcontent.net>
|
||||
* @copyright Copyright (C) 2006 - 2017 by Dan Cogliano
|
||||
* @license GNU GPLv3 <http://www.gnu.org/licenses/gpl.html>
|
||||
* @link http://icalendar.org/php-library.html
|
||||
*/
|
||||
|
||||
// No direct access
|
||||
defined('_ZAPCAL') or die( 'Restricted access' );
|
||||
|
||||
/**
|
||||
* Zap Calendar Time Zone Helper Class
|
||||
*
|
||||
* Class to help create timezone section of iCalendar file
|
||||
*
|
||||
* @copyright Copyright (C) 2006 - 2016 by Dan Cogliano
|
||||
* @license GNU General Public License version 2 or later; see LICENSE.txt
|
||||
*/
|
||||
class ZCTimeZoneHelper {
|
||||
|
||||
/**
|
||||
* getTZNode creates VTIMEZONE section in an iCalendar file
|
||||
*
|
||||
* @param @startyear int start year of date range
|
||||
*
|
||||
* @param @endyear int end year of date range
|
||||
*
|
||||
* @param $tzid string PHP timezone, use underscore for multiple words (i.e. "New_York" for "New York")
|
||||
*
|
||||
* @param $parentnode object iCalendar object where VTIMEZONE will be created
|
||||
*
|
||||
* @return object return VTIMEZONE object
|
||||
*/
|
||||
static function getTZNode($startyear, $endyear, $tzid, $parentnode)
|
||||
{
|
||||
$tzmins = array();
|
||||
$tzmaxs = array();
|
||||
if(!array_key_exists($tzid,$tzmins) || $tzmins[$tzid] > $startyear)
|
||||
{
|
||||
$tzmins[$tzid] = $startyear;
|
||||
}
|
||||
if(!array_key_exists($tzid,$tzmaxs) || $tzmaxs[$tzid] < $endyear)
|
||||
{
|
||||
$tzmaxs[$tzid] = $endyear;
|
||||
}
|
||||
|
||||
foreach(array_keys($tzmins) as $tzid)
|
||||
{
|
||||
$tmin = $tzmins[$tzid] - 1;
|
||||
if(array_key_exists($tzid,$tzmaxs))
|
||||
{
|
||||
$tmax = $tzmaxs[$tzid] + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
$tmax = $tzmins[$tzid] + 1;
|
||||
}
|
||||
$tstart = gmmktime(0,0,0,1,1,$tmin);
|
||||
$tend = gmmktime(23,59,59,12,31,$tmax);
|
||||
$tz = new DateTimeZone($tzid);
|
||||
$transitions = $tz->getTransitions($tstart,$tend);
|
||||
$tzobj = new ZCiCalNode("VTIMEZONE", $parentnode, true);
|
||||
$datanode = new ZCiCalDataNode("TZID:" . str_replace("_"," ",$tzid));
|
||||
$tzobj->data[$datanode->getName()] = $datanode;
|
||||
$count = 0;
|
||||
$lasttransition = null;
|
||||
if(count($transitions) == 1)
|
||||
{
|
||||
// not enough transitions found, probably UTC
|
||||
// lets add fake transition at end for those systems that need it (i.e. Outlook)
|
||||
|
||||
$t2 = array();
|
||||
$t2["isdst"] = $transitions[0]["isdst"];
|
||||
$t2["offset"] = $transitions[0]["offset"];
|
||||
$t2["ts"] = $tstart;
|
||||
$t2["abbr"] = $transitions[0]["abbr"];
|
||||
$transitions[] = $t2;
|
||||
}
|
||||
foreach($transitions as $transition)
|
||||
{
|
||||
$count++;
|
||||
if($count == 1)
|
||||
{
|
||||
$lasttransition = $transition;
|
||||
continue; // skip first item
|
||||
}
|
||||
if($transition["isdst"] == 1)
|
||||
{
|
||||
$tobj = new ZCiCalNode("DAYLIGHT", $tzobj);
|
||||
}
|
||||
else
|
||||
{
|
||||
$tobj = new ZCiCalNode("STANDARD", $tzobj);
|
||||
}
|
||||
//$tzobj->data[$tobj->getName()] == $tobj;
|
||||
|
||||
// convert timestamp to local time zone
|
||||
$ts = ZDateHelper::toUnixDateTime(ZDateHelper::toLocalDateTime(ZDateHelper::toSQLDateTime($transition["ts"]),$tzid));
|
||||
$datanode = new ZCiCalDataNode("DTSTART:".ZDateHelper::toICalDateTime($ts));
|
||||
$tobj->data[$datanode->getName()] = $datanode;
|
||||
//echo $ts . " => " . ZDateHelper::toICalDateTime($ts) . "<br/>\n"; exit;
|
||||
$toffset = $lasttransition["offset"];
|
||||
$thours = intval($toffset/60/60);
|
||||
$tmins = abs($toffset)/60 - intval(abs($toffset)/60/60)*60;
|
||||
if($thours < 0)
|
||||
{
|
||||
$offset = sprintf("%03d%02d",$thours,$tmins);
|
||||
}
|
||||
else
|
||||
{
|
||||
$offset = sprintf("+%02d%02d",$thours,$tmins);
|
||||
}
|
||||
$datanode = new ZCiCalDataNode("TZOFFSETFROM:".$offset);
|
||||
$tobj->data[$datanode->getName()] = $datanode;
|
||||
|
||||
$toffset = $transition["offset"];
|
||||
$thours = intval($toffset/60/60);
|
||||
$tmins = abs($toffset)/60 - intval(abs($toffset)/60/60)*60;
|
||||
if($thours < 0)
|
||||
{
|
||||
$offset = sprintf("%03d%02d",$thours,$tmins);
|
||||
}
|
||||
else
|
||||
{
|
||||
$offset = sprintf("+%02d%02d",$thours,$tmins);
|
||||
}
|
||||
$datanode = new ZCiCalDataNode("TZOFFSETTO:".$offset);
|
||||
$tobj->data[$datanode->getName()] = $datanode;
|
||||
|
||||
$datanode = new ZCiCalDataNode("TZNAME:".$transition["abbr"]);
|
||||
$tobj->data[$datanode->getName()] = $datanode;
|
||||
|
||||
$lasttransition = $transition;
|
||||
}
|
||||
}
|
||||
return $tzobj;
|
||||
}
|
||||
}
|
28
src/lib/zapcallib.php
Normal file
28
src/lib/zapcallib.php
Normal file
|
@ -0,0 +1,28 @@
|
|||
<?php
|
||||
/**
|
||||
* zapcallib.php
|
||||
*
|
||||
* @package ZapCalLib
|
||||
* @author Dan Cogliano <http://zcontent.net>
|
||||
* @copyright Copyright (C) 2006 - 2017 by Dan Cogliano
|
||||
* @license GNU GPLv3 <http://www.gnu.org/licenses/gpl.html>
|
||||
* @link http://icalendar.org/php-library.html
|
||||
*/
|
||||
|
||||
/**
|
||||
* used by ZapCalLib
|
||||
* @var integer
|
||||
*/
|
||||
define('_ZAPCAL',1);
|
||||
|
||||
if(!defined('_ZAPCAL_BASE'))
|
||||
{
|
||||
/**
|
||||
* the base folder of the library
|
||||
* @var string
|
||||
*/
|
||||
define('_ZAPCAL_BASE',__DIR__);
|
||||
}
|
||||
|
||||
require_once(_ZAPCAL_BASE . '/includes/framework.php');
|
||||
|
Loading…
Reference in a new issue