neue ordnerstruktur, php-code hinzugefügt

die ordner public und src hinzugefügt,
zapcallib.php für iCalendar files hinzugefügt,
getContent.php und getEvent.php hinzugefügt
This commit is contained in:
+++ 2022-02-18 11:59:12 +01:00
parent 592aba679e
commit 2048026c27
19 changed files with 3281 additions and 1 deletions

View file

Before

Width:  |  Height:  |  Size: 311 KiB

After

Width:  |  Height:  |  Size: 311 KiB

View file

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 25 KiB

View file

Before

Width:  |  Height:  |  Size: 400 B

After

Width:  |  Height:  |  Size: 400 B

View file

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<svg width="1792" height="1792" viewBox="0 0 1792 1792" xmlns="http://www.w3.org/2000/svg"><path fill="#333" d="M1664 1344v128q0 26-19 45t-45 19h-1408q-26 0-45-19t-19-45v-128q0-26 19-45t45-19h1408q26 0 45 19t19 45zm0-512v128q0 26-19 45t-45 19h-1408q-26 0-45-19t-19-45v-128q0-26 19-45t45-19h1408q26 0 45 19t19 45zm0-512v128q0 26-19 45t-45 19h-1408q-26 0-45-19t-19-45v-128q0-26 19-45t45-19h1408q26 0 45 19t19 45z"/></svg>

After

Width:  |  Height:  |  Size: 459 B

View file

Before

Width:  |  Height:  |  Size: 772 B

After

Width:  |  Height:  |  Size: 772 B

View file

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<svg width="2048" height="1792" viewBox="0 0 2048 1792" xmlns="http://www.w3.org/2000/svg"><path fill="#006400" d="M657 896q-162 5-265 128h-134q-82 0-138-40.5t-56-118.5q0-353 124-353 6 0 43.5 21t97.5 42.5 119 21.5q67 0 133-23-5 37-5 66 0 139 81 256zm1071 637q0 120-73 189.5t-194 69.5h-874q-121 0-194-69.5t-73-189.5q0-53 3.5-103.5t14-109 26.5-108.5 43-97.5 62-81 85.5-53.5 111.5-20q10 0 43 21.5t73 48 107 48 135 21.5 135-21.5 107-48 73-48 43-21.5q61 0 111.5 20t85.5 53.5 62 81 43 97.5 26.5 108.5 14 109 3.5 103.5zm-1024-1277q0 106-75 181t-181 75-181-75-75-181 75-181 181-75 181 75 75 181zm704 384q0 159-112.5 271.5t-271.5 112.5-271.5-112.5-112.5-271.5 112.5-271.5 271.5-112.5 271.5 112.5 112.5 271.5zm576 225q0 78-56 118.5t-138 40.5h-134q-103-123-265-128 81-117 81-256 0-29-5-66 66 23 133 23 59 0 119-21.5t97.5-42.5 43.5-21q124 0 124 353zm-128-609q0 106-75 181t-181 75-181-75-75-181 75-181 181-75 181 75 75 181z"/></svg>

After

Width:  |  Height:  |  Size: 959 B

View file

Before

Width:  |  Height:  |  Size: 44 KiB

After

Width:  |  Height:  |  Size: 44 KiB

View file

@ -38,7 +38,9 @@
<div id="headline" class="flex-box row">
<div id="header-left" class="flex-box row center">
<div id="status-icon" class="icon">
<img class="icon-img" src="./images/user-times.svg" />
<img class="icon-img"
alt="Raumstatus Icon - Raum ist geschlossen"
src="./images/user-times.svg" />
</div>
<div id="banner-div" class="flex-box column">
<a id="banner" href="#"

102
src/getContent.php Normal file
View file

@ -0,0 +1,102 @@
<?php
/**
* User: vincent
* Date: 14/12/2016
* Time: 2:55 PM
*/
/* include_once("config.php"); */
$status = getStatus();
function getContent() {
$pagename = filter_input(INPUT_GET, 'title', FILTER_SANITIZE_URL);
$page = $root_url . '/content/' . $pagename . '.txt';
$file = fopen($page, 'rb');
if($file) {
echo stream_get_contents($file);
fclose($file);
} else {
echo "Page not found";
}
}
/**
* @return string[][]
*/
function apiRequest()
{
$ch = curl_init('https://status.kraut.space/api');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5);
curl_setopt($ch, CURLOPT_TIMEOUT, 20);
$result = curl_exec($ch);
curl_close($ch);
return json_decode($result, true);
}
/**
* @return bool
*/
function getStatus(): bool
{
try { return apiRequest()['state']['open']; }
catch (Error $e) { return false; }
}
function getKrautButtonColor($status) {
if ($status) {
echo "<img src='./img/icons/users.svg' id='krauticon'>";
} else {
echo "<img src='./img/icons/user-times.svg' id='krauticon'>";
}
}
function getKrautStatus($status) {
if ($status) {
echo "Raum ist offen";
} else {
echo "Raum ist geschlossen";
}
}
function getKrautSidebar($status)
{
if ($status) {
echo <<<END
<div class="sidebar-heading">
<div class="icondiv"><img src="./img/icons/users.svg" class="icon" alt="Person"></div>
<h4>Raum ist geöffnet</h4>
<p>Schaut einfach vorbei</p>
</div>
END;
} else {
echo <<<END
<div class="sidebar-heading">
<div class="icondiv"><img src="./img/icons/user-times.svg" class="icon" alt="niemand"></div>
<h4>Niemand ist im Raum</h4>
<p>Keine Hackerseele anwesend</p>
</div>
END;
}
}
function getKrautButton(bool $status)
{
if ($status) {
echo <<<END
<div id="roombutton" class="krautopen">
<img src="./img/icons/users.svg" id="roomicon" alt="Person">
<p>Raum ist geöffnet</p>
</div>
END;
} else {
echo <<<END
<div id="roombutton" class="krautclose">
<img src="./img/icons/user-times.svg" id="roomicon" alt="niemand">
<p>Raum ist geschlossen</p>
</div>
END;
}
}

487
src/getEvents.php Normal file
View file

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

127
src/lib/README.md Normal file
View 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
View 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);
}
}

View 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
View 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;
}
}
?>

View file

@ -0,0 +1,6 @@
<html>
<head>
</head>
<body>
</body>
</html>

View 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;
}
}

View 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
View 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');