iscalendoku = true; $this->plugin = $plugin; $this->explodeText($text); $this->invalid = false; $this->fillRendered(); } function explodeText($text) { $textlines = preg_split('/\n/', $text); $curfield = ''; foreach ($textlines as $line) { if (preg_match('/^ /', $line)) { $line = preg_replace('/^ /', '', $line); if (!strcmp($curfield, '')) continue; $this->icalfields[$curfield] = $this->icalfields[$curfield]."\n".$line; } else { $pregretval = preg_split('/:/', $line, 2); $curfield = $pregretval[0]; $value = $pregretval[1]; $properties = preg_split('/;/', $curfield); $curfield = $properties[0]; if (count($properties) > 1) for ($i = 1; $i < count($properties); $i++) { $propretval = preg_split('/=/', $properties[$i]); $this->properties[$curfield][$propretval[0]] = $propretval[1]; } if (strcmp($this->icalfields[$curfield], '')) continue; $this->icalfields[$curfield] = $value; } } } function parseDateTime($datestr, $tzstring) { if ($this->dttype == 0) { $datetime = preg_split('/T/', $datestr, 2); if (count($datetime) > 1) { $this->dttype = 2; } else { $this->dttype = 1; } } $datetime = preg_split('/T/', $datestr, 2); $year = substr($datetime[0], 0, 4); $month = substr($datetime[0], 4, 2); $day = substr($datetime[0], 6, 2); if ($this->dttype == 2) { $hour = substr($datetime[1], 0, 2); $minute = substr($datetime[1], 2, 2); $second = substr($datetime[1], 4, 2); } else { $hour = 0; $minute = 0; $second = 0; } $date = new DateTime(); $date->setDate(intval($year), intval($month), intval($day)); $date->setTime(intval($hour), intval($minute), intval($second)); if (intval($date->format('U')) < 86400) { $this->invalid = true; return new DateTime(); } /* * Wow, this is annoying! * PHP doesn't have proper support for date/time objects. The timezone * settings are totally confused (you cannot ask a DateTimeZone object * for its offset, but only a DateTime object) and important * functionality is missing (subtracting time and getting a new DateTime * object). Thus, this strange tango here... * * A DateTime object will always have its time set to UTC. If you set a * timezone for the DateTime object, then it will be treated to have the * offset of this timezone. So if you have a time given in a certain * timezone and want to translate it to another one, you have to convert * it to seconds since epoch, generate another object which has the * timezone set, get the offset from it, subtract that, then generate a * new object with these seconds set. * * For time with just any timezone set, we have to translate it back to * UTC by setting the timezone to UTC, taking seconds, subtracting the * offset from the local timezone, then convert it back. */ if (strlen($this->plugin->getConf('defaulttimezone')) > 0) { $mytz = new DateTimeZone($this->plugin->getConf('defaulttimezone')); } else { $mytz = new DateTimeZone(date_default_timezone_get()); } $utctz = new DateTimeZone('UTC'); if ($this->dttype > 1) { if ($this->plugin->getConf('showlocaltime')) { $zpos = strpos($datestr, 'Z'); if ($zpos !== false) { $date->setTimeZone($mytz); } else if (strlen($tzstring) == 0) { $date->setTimeZone($utctz); $datesecs = intval($date->format('U')); $date->setTimeZone($mytz); $datesecs = strval($datesecs - intval($date->getOffset())); $date = DateTime::createFromFormat('U', $datesecs); $date->setTimeZone($mytz); } else { $date->setTimeZone($mytz); $datesecs = strval($date->format('U')) + intval($date->getOffset()); $date = DateTime::createFromFormat('U', $datesecs); $date->setTimeZone($mytz); } } else if (strlen($tzstring) == 0) { $date->setTimeZone($utctz); $datesecs = intval($date->format('U')); $date->setTimeZone($mytz); $datesecs = strval($datesecs - intval($date->getOffset())); $date = DateTime::createFromFormat('U', $datesecs); $date->setTimeZone($mytz); } else { $date->setTimeZone($mytz); $datesecs = strval($date->format('U')) + intval($date->getOffset()); $date = DateTime::createFromFormat('U', $datesecs); $date->setTimeZone($mytz); } } else { $date->setTimeZone($mytz); $datesecs = strval($date->format('U')) + intval($date->getOffset()); $date = DateTime::createFromFormat('U', $datesecs); $date->setTimeZone($mytz); } return $date; } function renderDateTime($date) { if ($this->dttype > 1) { return $date->format($this->plugin->getConf('datetimeformat')); } return $date->format($this->plugin->getConf('dateformat')); } function checkStartEndType() { if (isset($this->properties['DTSTART']['VALUE'])) { if (!strcmp($this->properties['DTSTART']['VALUE'], 'DATE')) { $this->dttype = 1; } else if (!strcmp($this->properties['DTSTART']['VALUE'], 'DATETIME')) { $this->dttype = 2; } } else if (isset($this->properties['DTEND']['VALUE'])) { if (!strcmp($this->properties['DTEND']['VALUE'], 'DATE')) { $this->dttype = 1; } else if (!strcmp($this->properties['DTSTART']['VALUE'], 'DATETIME')) { $this->dttype = 2; } } else { $this->dttype = 0; } } function renderDTSTART() { if (isset($this->icalfields['DTSTART'])) { $this->dtstart = $this->parseDateTime($this->icalfields['DTSTART'], $this->properties['DTSTART']['TZID']); $this->dtstartint = intval($this->dtstart->format('U')); $this->renderfields['DTSTART'] = $this->renderDateTime($this->dtstart); } else { $this->dtstartint = 0; $this->renderfields['DTSTART'] = ''; } } function renderDTEND() { if (isset($this->icalfields['DTEND'])) { $this->dtend = $this->parseDateTime($this->icalfields['DTEND'], $this->properties['DTEND']['TZID']); $this->dtendint = intval($this->dtend->format('U')); $this->renderfields['DTEND'] = $this->renderDateTime($this->dtend); } else { $this->dtendint = 0; $this->renderfields['DTEND'] = ''; } } function renderSTRING($field) { $this->renderfields[$field] = $this->icalfields[$field]; } // XXX function renderMULTISTRING($field, $desc) { if (isset($this->icalfields[$field])) $this->renderfields[$field] = $this->icalfields[$field]; else $this->renderfields[$field] = ''; } // XXX /* * geo = "GEO" geoparam ":" geovalue CRLF * geoparam = *(";" other-param) * geovalue = float ";" float * ;Latitude and Longitude components */ function renderGEO() { $geostr = preg_split('/;/', $this->icalfields['GEO']); $this->renderfields['LATITUDE'] = floatval($geostr[0]); $this->renderfields['LONGITUDE'] = floatval($geostr[1]); } function renderINT() { $this->renderfields[$field] = $this->icalfields[$field]; } function renderCALADDRESS($field) { $this->renderfields[$field] = $this->icalfields[$field]; } function fillRendered() { /* DTSTART/DTEND value */ $this->checkStartEndType(); $this->renderDTSTART(); $this->renderDTEND(); foreach($this->icalfields as $field => $value) { /* DTSTART */ if (!strcmp($field, 'DTSTART')) continue; /* DTEND */ else if (!strcmp($field, 'DTEND')) continue; /* CATEGORIES */ else if (!strcmp($field, 'CATEGORIES')) $this->renderMULTISTRING('CATEGORIES', 'CATEGORY'); /* GEO */ else if (!strcmp($field, 'GEO')) $this->renderGEO(); else if (!strcmp($field, 'PRIORITY')) $this->renderINT('PRIORITY'); else if (!strcmp($field, 'ATTENDEE')) $this->renderCALADDRESS('ATTENDEE'); else if (!strcmp($field, 'ORGANIZER')) $this->renderCALADDRESS('ORGANIZER'); /* SUMMARY * DESCRIPTION * LOCATION * COMMENT * CONTACT * ORGANIZER * UID * CLASS * STATUS * RESOURCES * RELATED-TO * URL. We render it as a string and leave it up to the user to define * a template which makes it a link. */ else $this->renderSTRING($field); // XXX: GEO } } function isWithinTime($startdate, $enddate) { if (!strcmp(($enddate->format('U')), '0')) $enddate = $this->dtstart; if ($this->dtstartint > intval($enddate->format('U')) || intval($startdate->format('U')) > $this->dtendint) return false; return true; } } class CalenDoku_CalendarObject { function __construct($plugin, $page) { $text = preg_replace('/^[\s\n]*BEGIN:VCALENDAR\n(.*)\nEND:VCALENDAR[\n\s]*$/s', '\1', $page, -1, $count); if ($count < 1) return; $this->iscalendoku = true; $this->plugin = $plugin; $this->separateComponents($text); } function separateComponents($text) { /* Get properties of the calendar. */ // XXX /* Get the calendar components. */ $this->components = array(); if (preg_match_all('/BEGIN:VEVENT\n.*?END:VEVENT/s', $text, $vevents)) { foreach ($vevents as $vevent) { $component = new CalenDoku_CalendarComponent($this->plugin, $vevent[0]); array_push($this->components, $component); } } /* XXX: VTODO, VJOURNAL, VFREEBUSY, VTIMEZONE, VALARM. */ } } function reHandleText($text, $pos, $handler) { $instructions = p_get_instructions($text); foreach ($instructions as $instruction) $handler->_addCall($instruction[0], $instruction[1] ? $instruction[1] : array(), $pos); } /** * Guess the time format. We try several date and time formats which are common * and take the first one that fits. * * If we have less than eight numbers, we assume it is a Unix timestamp (with * eight numbers, we might hit YYYYMMDD). This means, we won't be able to serve * dates before Sat Mar 3 10:46:40 CET 1973. We allow no dates with two-digit * years. En exception is there for a '0', which is diretly parsed to epoch. * * Then, we try dates this order: * 1. European date DD.MM.YYYY * 2. European date w/o year DD.MM. * 3. Technical date YYYY-MM-DD * 4. Technical date w/o day YYYY-MM * 5. Terror date YYYYMMDD * 6. Year only YYYY * Since American dates are ambiguous, they are not checked. * * Now check for the time: * 1. 24h time w/o seconds hh:mm * 2. 24h time hh:mm:ss * 3. Terror time hhmmss * * Returns an DateTime object filled with the date from the argument. */ function guessDateTime($datestr) { $rexu = '(0|[0-9]{9,})'; $rexy = '([0-9]{4})'; $rexm = '(0[1-9]|1[12])'; $rexd = '([0-2][0-9]|3[01])'; $rexH = '([0-1][0-9]|2[0-3])'; $rexM = '([0-5][0-9])'; $rexS = '([0-5][0-9]|60)'; $rexsep = '(?=[T\s]*)'; $rexend = '.*?'; /* %s */ if (preg_match('/^'.$rexu.$rexsep.'$/', $datestr)) { $date = DateTime::createFromFormat('U', $datestr); return $date; } $year = date('Y'); $month = date('m'); $day = date('d'); /* DD.MM.YYYY */ if (preg_match('/^'.$rexd.'\.'.$rexm.'\.'.$rexy.$rexsep.'/', $datestr, $match)) { $datestr = preg_replace('/^'.$rexd.'\.'.$rexm.'\.'.$rexy.$rexsep.'/', '', $datestr); $day = $match[1]; $month = $match[2]; $year = $match[3]; /* DD.MM. */ } else if (preg_match('/^'.$rexd.'\.'.$rexm.$rexsep.'/', $datestr, $match)) { $datestr = preg_replace('/^'.$rexd.'\.'.$rexm.$rexsep.'/', '', $datestr); $year = date('Y'); $month = $match[2]; $day = $match[1]; /* YYYY-MM-DD */ } else if (preg_match('/^'.$rexy.'-'.$rexm.'-'.$rexd.$rexsep.'/', $datestr, $match)) { $datestr = preg_replace('/^'.$rexy.'-'.$rexm.'-'.$rexd.$rexsep.'/', '', $datestr); $year = $match[1]; $month = $match[2]; $day = $match[3]; /* YYYY-MM */ } else if (preg_match('/^'.$rexy.'-'.$rexm.$rexsep.'/', $datestr, $match)) { $datestr = preg_replace('/^'.$rexy.'-'.$rexm.$rexsep.'/', '', $datestr); $year = $match[1]; $month = $match[2]; $day = '1'; /* YYYYMMDD */ } else if (preg_match('/^'.$rexy.$rexm.$rexd.$rexsep.'/', $datestr, $match)) { $datestr = preg_replace('/^'.$rexy.$rexm.$rexd.$rexsep.'/', '', $datestr); $year = $match[1]; $month = $match[2]; $day = $match[3]; } else if (preg_match('/^'.$rexy.$rexsep.'/', $datestr, $match)) { $datestr = preg_replace('/^'.$rexy.$rexsep.'/', '', $datestr); $year = $match[1]; $month = '1'; $day = '1'; } $hour = '0'; $mins = '0'; $secs = '0'; /* hh:mm */ if (preg_match('/^'.$rexH.':'.$rexM.$rexend.'$/', $datestr, $match)) { $hour = $match[1]; $mins = $match[2]; $secs = '0'; /* hh:mm:ss */ } else if (preg_match('/^'.$rexH.':'.$rexM.':'.$rexS.$rexend.'$/', $datestr, $match)) { $hour = $match[1]; $mins = $match[2]; $secs = $match[3]; /* hhmmss */ } else if (preg_match('/^'.$rexH.$rexM.$rexS.$rexend.'$/', $datestr, $match)) { $hour = $match[1]; $mins = $match[2]; $secs = $match[3]; } $date = new DateTime(); $date->setDate(intval($year), intval($month), intval($day)); $date->setTime(intval($hour), intval($mins), intval($secs)); return $date; } //Setup VIM: ex: et ts=4 enc=utf-8 :