File: /var/www/intranet.kauko.lt/wp-content/plugins/dpProEventCalendar/includes/ical_parser.php
<?php
/**
* This PHP-Class should only read a iCal-File (*.ics), parse it and give an
* array with its content.
*
* PHP Version 5
*
* @category Parser
* @package Ics-parser
* @author Martin Thoma <info@martin-thoma.de>
* @license http://www.opensource.org/licenses/mit-license.php MIT License
* @version SVN: <svn_id>
* @link http://code.google.com/p/ics-parser/
* @example $ical = new ical('MyCal.ics');
* print_r( $ical->events() );
*/
//error_reporting(E_ALL);
/**
* This is the iCal-class
*
* @category Parser
* @package Ics-parser
* @author Martin Thoma <info@martin-thoma.de>
* @license http://www.opensource.org/licenses/mit-license.php MIT License
* @link http://code.google.com/p/ics-parser/
*
* @param {string} filename The name of the file which should be parsed
* @constructor
*/
class ICal
{
/* How many ToDos are in this ical? */
public /** @type {int} */ $todo_count = 0;
/* How many events are in this ical? */
public /** @type {int} */ $event_count = 0;
/* The parsed calendar */
public /** @type {Array} */ $cal;
/* Which keyword has been added to cal at last? */
private /** @type {string} */ $_lastKeyWord;
/**
* Creates the iCal-Object
*
* @param {string} $filename The path to the iCal-file
*
* @return Object The iCal-Object
*/
public function __construct($filename)
{
if (!$filename) {
return false;
}
if(substr($filename, 0, 1) == "/") {
$filename = "file://".$filename;
}
if(substr($filename, 0, 2) == "C:") {
$filename = "file://".$filename;
}
if( strpos($filename, 'facebook.com') === -1 && ini_get('allow_url_fopen') ) {
$lines = file($filename, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
} else {
// Get cURL resource
$curl = curl_init();
// Set some options - we are passing in a useragent too here
curl_setopt_array($curl, array(
CURLOPT_RETURNTRANSFER => 1,
CURLOPT_URL => $filename,
CURLOPT_FOLLOWLOCATION => 1,
CURLOPT_USERAGENT => "Mozilla/5.0 (Windows NT 5.1; rv:31.0) Gecko/20100101 Firefox/31.0"
));
// Send the request & save response to $resp
$resp = curl_exec($curl);
/*if (curl_error($curl)) {
$error_msg = curl_error($curl);
echo $error_msg;
}*/
$lines_tmp = explode(PHP_EOL, $resp);
$lines = array();
foreach ($lines_tmp as $line) {
$lines[] = rtrim($line);
}
// Close request to clear up some resources
curl_close($curl);
}
if (stristr($lines[0], 'BEGIN:VCALENDAR') === false) {
return false;
} else {
// TODO: Fix multiline-description problem (see http://tools.ietf.org/html/rfc2445#section-4.8.1.5)
$skipnextline = false;
$is_apple = false;
foreach ($lines as $line) {
if($line == "") {
continue;
}
$line = str_replace('\n', '<br>', $line);
$line = str_replace("’", "'", $line);
if(strpos($line, 'X-APPLE-') !== false) {
$is_apple = true;
continue;
}
if(substr($line, 0, 1) == " ") {
if(!$is_apple) {
$line = substr($line, 1);
//echo $line.'<br>';
$this->addCalendarComponentWithKeyAndValue('VEVENT',
false,
$line);
} else {
}
continue;
} else {
$is_apple = false;
//$skipnextline = true;
//continue;
}
if($skipnextline) {
$skipnextline = false;
continue;
}
$line = ltrim($line);
$add = $this->keyValueFromString($line);
if ($add === false) {
$this->addCalendarComponentWithKeyAndValue($type, false, $line);
continue;
}
list($keyword, $value) = $add;
switch ($line) {
// http://www.kanzaki.com/docs/ical/vtodo.html
case "BEGIN:VTODO":
$this->todo_count++;
$type = "VTODO";
break;
// http://www.kanzaki.com/docs/ical/vevent.html
case "BEGIN:VEVENT":
//echo "vevent gematcht";
$this->event_count++;
$type = "VEVENT";
break;
//all other special strings
case "BEGIN:VCALENDAR":
case "BEGIN:DAYLIGHT":
// http://www.kanzaki.com/docs/ical/vtimezone.html
case "BEGIN:VTIMEZONE":
case "BEGIN:STANDARD":
$type = $value;
break;
case "END:VTODO": // end special text - goto VCALENDAR key
case "END:VEVENT":
case "END:VCALENDAR":
case "END:DAYLIGHT":
case "END:VTIMEZONE":
case "END:STANDARD":
$type = "VCALENDAR";
break;
default:
$this->addCalendarComponentWithKeyAndValue($type,
$keyword,
$value);
break;
}
}
/*echo '<pre>';
print_r($this->cal);
echo '</pre>';*/
return $this->cal;
}
}
/**
* Add to $this->ical array one value and key.
*
* @param {string} $component This could be VTODO, VEVENT, VCALENDAR, ...
* @param {string} $keyword The keyword, for example DTSTART
* @param {string} $value The value, for example 20110105T090000Z
*
* @return {None}
*/
public function addCalendarComponentWithKeyAndValue($component,
$keyword,
$value)
{
$concatenate = true;
if ($keyword == false) {
$keyword = $this->last_keyword;
switch ($component) {
case 'VEVENT':
$value = $this->cal[$component][$this->event_count - 1]
[$keyword].$value;
$concatenate = false;
break;
case 'VTODO' :
$value = $this->cal[$component][$this->todo_count - 1]
[$keyword].$value;
$concatenate = false;
break;
}
}
$dstart_complete = "";
if (stristr($keyword, "DTSTART") or stristr($keyword, "DTEND")) {
if(strpos($keyword, "TZID") !== false) {
$dstart_complete = substr($keyword, strpos($keyword, "TZID"));
$dstart_complete = str_replace("TZID=", "", $dstart_complete);
}
$keyword = explode(";", $keyword);
$keyword = $keyword[0];
}
switch ($component) {
case "VTODO":
$this->cal[$component][$this->todo_count - 1][$keyword] = $value;
//$this->cal[$component][$this->todo_count]['Unix'] = $unixtime;
break;
case "VEVENT":
if(!isset($this->cal[$component][$this->event_count - 1][$keyword])) {
if(($keyword == 'DTSTART' || $keyword == 'DTEND') && strpos($value, ':') !== false) {
$this->cal[$component][$this->event_count - 1][$keyword] = substr($value, stripos($value, ':') + 1);
} else {
$this->cal[$component][$this->event_count - 1][$keyword] = $value;
if($dstart_complete != "") {
$this->cal[$component][$this->event_count - 1]["TZID"] = $dstart_complete;
}
}
} else {
if($concatenate) {
$this->cal[$component][$this->event_count - 1][$keyword] .= $value;
} else {
$this->cal[$component][$this->event_count - 1][$keyword] = $value;
}
}
break;
default:
if($component == 'VTIMEZONE' || $component == 'STANDARD') {
if($component == 'STANDARD') {
if($keyword != 'TZOFFSETTO') {
continue;
}
$this->cal[$component][][$keyword] = $value;
} else {
//echo $keyword;
$this->cal[$component][][$keyword] = $value;
}
} else {
$this->cal[$component][$keyword] = $value;
}
break;
}
$this->last_keyword = $keyword;
}
/**
* Get a key-value pair of a string.
*
* @param {string} $text which is like "VCALENDAR:Begin" or "LOCATION:"
*
* @return {array} array("VCALENDAR", "Begin")
*/
public function keyValueFromString($text)
{
preg_match("/([^:]+)[:]([\w\W]*)/", $text, $matches);
if (count($matches) == 0) {
return false;
}
$matches = array_splice($matches, 1, 2);
return $matches;
}
/**
* Return Unix timestamp from ical date time format
*
* @param {string} $icalDate A Date in the format YYYYMMDD[T]HHMMSS[Z] or
* YYYYMMDD[T]HHMMSS
*
* @return {int}
*/
public function iCalDateToUnixTimestamp($icalDate)
{
$icalDate = str_replace('T', '', $icalDate);
$icalDate = str_replace('Z', '', $icalDate);
$pattern = '/([0-9]{4})'; // 1: YYYY
$pattern .= '([0-9]{2})'; // 2: MM
$pattern .= '([0-9]{2})'; // 3: DD
$pattern .= '([0-9]{0,2})'; // 4: HH
$pattern .= '([0-9]{0,2})'; // 5: MM
$pattern .= '([0-9]{0,2})/'; // 6: SS
preg_match($pattern, $icalDate, $date);
// Unix timestamp can't represent dates before 1970
if ($date[1] <= 1970) {
return false;
}
// Unix timestamps after 03:14:07 UTC 2038-01-19 might cause an overflow
// if 32 bit integers are used.
$timestamp = mktime((int)$date[4],
(int)$date[5],
(int)$date[6],
(int)$date[2],
(int)$date[3],
(int)$date[1]);
return $timestamp;
}
/**
* Returns an array of arrays with all events. Every event is an associative
* array and each property is an element it.
*
* @return {array}
*/
public function events()
{
$array = $this->cal;
return $array['VEVENT'];
}
/**
* Returns a boolean value whether thr current calendar has events or not
*
* @return {boolean}
*/
public function hasEvents()
{
return ( count($this->events()) > 0 ? true : false );
}
/**
* Returns false when the current calendar has no events in range, else the
* events.
*
* Note that this function makes use of a UNIX timestamp. This might be a
* problem on January the 29th, 2038.
* See http://en.wikipedia.org/wiki/Unix_time#Representing_the_number
*
* @param {boolean} $rangeStart Either true or false
* @param {boolean} $rangeEnd Either true or false
*
* @return {mixed}
*/
public function eventsFromRange($rangeStart = false, $rangeEnd = false)
{
$events = $this->sortEventsWithOrder($this->events(), SORT_ASC);
if (!$events) {
return false;
}
$extendedEvents = array();
if ($rangeStart !== false) {
$rangeStart = new DateTime();
}
if ($rangeEnd !== false or $rangeEnd <= 0) {
$rangeEnd = new DateTime('2038/01/18');
} else {
$rangeEnd = new DateTime($rangeEnd);
}
$rangeStart = $rangeStart->format('U');
$rangeEnd = $rangeEnd->format('U');
// loop through all events by adding two new elements
foreach ($events as $anEvent) {
$timestamp = $this->iCalDateToUnixTimestamp($anEvent['DTSTART']);
if ($timestamp >= $rangeStart && $timestamp <= $rangeEnd) {
$extendedEvents[] = $anEvent;
}
}
return $extendedEvents;
}
/**
* Returns a boolean value whether thr current calendar has events or not
*
* @param {array} $events An array with events.
* @param {array} $sortOrder Either SORT_ASC, SORT_DESC, SORT_REGULAR,
* SORT_NUMERIC, SORT_STRING
*
* @return {boolean}
*/
public function sortEventsWithOrder($events, $sortOrder = SORT_ASC)
{
$extendedEvents = array();
// loop through all events by adding two new elements
foreach ($events as $anEvent) {
if (!array_key_exists('UNIX_TIMESTAMP', $anEvent)) {
$anEvent['UNIX_TIMESTAMP'] =
$this->iCalDateToUnixTimestamp($anEvent['DTSTART']);
}
if (!array_key_exists('REAL_DATETIME', $anEvent)) {
$anEvent['REAL_DATETIME'] =
date("d.m.Y", $anEvent['UNIX_TIMESTAMP']);
}
$extendedEvents[] = $anEvent;
}
foreach ($extendedEvents as $key => $value) {
$timestamp[$key] = $value['UNIX_TIMESTAMP'];
}
array_multisort($timestamp, $sortOrder, $extendedEvents);
return $extendedEvents;
}
}
?>