merged 2.0 branch
This commit is contained in:
@@ -1,4 +1,25 @@
|
||||
<?php
|
||||
/**
|
||||
* download.php, zarafa calender to ics im/exporter
|
||||
*
|
||||
* Author: Christoph Haas <christoph.h@sprinternet.at>
|
||||
* Copyright (C) 2012-2013 Christoph Haas
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
$basedir = $_GET["basedir"];
|
||||
$secid = $_GET["secid"];
|
||||
$fileid = $_GET["fileid"];
|
||||
|
@@ -1,216 +0,0 @@
|
||||
BEGIN:VCALENDAR
|
||||
PRODID:-//Google Inc//Google Calendar 70.9054//EN
|
||||
VERSION:2.0
|
||||
CALSCALE:GREGORIAN
|
||||
METHOD:PUBLISH
|
||||
X-WR-CALNAME:Testkalender
|
||||
X-WR-TIMEZONE:Europe/Berlin
|
||||
X-WR-CALDESC:Nur zum testen vom Google Kalender
|
||||
BEGIN:VEVENT
|
||||
DTSTART:20121105T090000Z
|
||||
DTEND:20121107T173000Z
|
||||
DTSTAMP:20110121T195741Z
|
||||
UID:15lc1nvupht8dtfiptenljoiv4@google.com
|
||||
CREATED:20110121T195616Z
|
||||
DESCRIPTION:This is a short description\nwith a new line. Some "special" 's
|
||||
igns' may be interesting, too.
|
||||
LAST-MODIFIED:20110121T195729Z
|
||||
LOCATION:Kansas
|
||||
SEQUENCE:2
|
||||
STATUS:CONFIRMED
|
||||
SUMMARY:My Holidays
|
||||
TRANSP:TRANSPARENT
|
||||
END:VEVENT
|
||||
BEGIN:VEVENT
|
||||
DTSTART;VALUE=DATE:20110112
|
||||
DTEND;VALUE=DATE:20110116
|
||||
DTSTAMP:20110121T195741Z
|
||||
UID:1koigufm110c5hnq6ln57murd4@google.com
|
||||
CREATED:20110119T142901Z
|
||||
DESCRIPTION:Project xyz Review Meeting Minutes\n
|
||||
Agenda\n1. Review of project version 1.0 requirements.\n2.
|
||||
Definition
|
||||
of project processes.\n3. Review of project schedule.\n
|
||||
Participants: John Smith, Jane Doe, Jim Dandy\n-It was
|
||||
decided that the requirements need to be signed off by
|
||||
product marketing.\n-Project processes were accepted.\n
|
||||
-Project schedule needs to account for scheduled holidays
|
||||
and employee vacation time. Check with HR for specific
|
||||
dates.\n-New schedule will be distributed by Friday.\n-
|
||||
Next weeks meeting is cancelled. No meeting until 3/23.
|
||||
LAST-MODIFIED:20110119T152216Z
|
||||
LOCATION:
|
||||
SEQUENCE:2
|
||||
STATUS:CONFIRMED
|
||||
SUMMARY:test 11
|
||||
TRANSP:TRANSPARENT
|
||||
END:VEVENT
|
||||
BEGIN:VEVENT
|
||||
DTSTART;VALUE=DATE:20110118
|
||||
DTEND;VALUE=DATE:20110120
|
||||
DTSTAMP:20110121T195741Z
|
||||
UID:4dnsuc3nknin15kv25cn7ridss@google.com
|
||||
CREATED:20110119T142059Z
|
||||
DESCRIPTION:
|
||||
LAST-MODIFIED:20110119T142106Z
|
||||
LOCATION:
|
||||
SEQUENCE:0
|
||||
STATUS:CONFIRMED
|
||||
SUMMARY:test 9
|
||||
TRANSP:TRANSPARENT
|
||||
END:VEVENT
|
||||
BEGIN:VEVENT
|
||||
DTSTART;VALUE=DATE:20110117
|
||||
DTEND;VALUE=DATE:20110122
|
||||
DTSTAMP:20110121T195741Z
|
||||
UID:h6f7sdjbpt47v3dkral8lnsgcc@google.com
|
||||
CREATED:20110119T142040Z
|
||||
DESCRIPTION:
|
||||
LAST-MODIFIED:20110119T142040Z
|
||||
LOCATION:
|
||||
SEQUENCE:0
|
||||
STATUS:CONFIRMED
|
||||
SUMMARY:
|
||||
TRANSP:TRANSPARENT
|
||||
END:VEVENT
|
||||
BEGIN:VEVENT
|
||||
DTSTART;VALUE=DATE:20110117
|
||||
DTEND;VALUE=DATE:20110118
|
||||
DTSTAMP:20110121T195741Z
|
||||
UID:up56hlrtkpqdum73rk6tl10ook@google.com
|
||||
CREATED:20110119T142034Z
|
||||
DESCRIPTION:
|
||||
LAST-MODIFIED:20110119T142034Z
|
||||
LOCATION:
|
||||
SEQUENCE:0
|
||||
STATUS:CONFIRMED
|
||||
SUMMARY:test 8
|
||||
TRANSP:TRANSPARENT
|
||||
END:VEVENT
|
||||
BEGIN:VEVENT
|
||||
DTSTART;VALUE=DATE:20110118
|
||||
DTEND;VALUE=DATE:20110120
|
||||
DTSTAMP:20110121T195741Z
|
||||
UID:8ltm205uhshsbc1huv0ooeg4nc@google.com
|
||||
CREATED:20110119T142014Z
|
||||
DESCRIPTION:
|
||||
LAST-MODIFIED:20110119T142023Z
|
||||
LOCATION:
|
||||
SEQUENCE:0
|
||||
STATUS:CONFIRMED
|
||||
SUMMARY:test 7
|
||||
TRANSP:TRANSPARENT
|
||||
END:VEVENT
|
||||
BEGIN:VEVENT
|
||||
DTSTART;VALUE=DATE:20110119
|
||||
DTEND;VALUE=DATE:20110121
|
||||
DTSTAMP:20110121T195741Z
|
||||
UID:opklai3nm8enffdf5vpna4o5fo@google.com
|
||||
CREATED:20110119T141918Z
|
||||
DESCRIPTION:
|
||||
LAST-MODIFIED:20110119T142005Z
|
||||
LOCATION:
|
||||
SEQUENCE:0
|
||||
STATUS:CONFIRMED
|
||||
SUMMARY:test 5
|
||||
TRANSP:TRANSPARENT
|
||||
END:VEVENT
|
||||
BEGIN:VEVENT
|
||||
DTSTART;VALUE=DATE:20110119
|
||||
DTEND;VALUE=DATE:20110120
|
||||
DTSTAMP:20110121T195741Z
|
||||
UID:kmbj764g57tcvua11hir61c4b8@google.com
|
||||
CREATED:20110119T141923Z
|
||||
DESCRIPTION:
|
||||
LAST-MODIFIED:20110119T141923Z
|
||||
LOCATION:
|
||||
SEQUENCE:0
|
||||
STATUS:CONFIRMED
|
||||
SUMMARY:test 6
|
||||
TRANSP:TRANSPARENT
|
||||
END:VEVENT
|
||||
BEGIN:VEVENT
|
||||
DTSTART;VALUE=DATE:20110119
|
||||
DTEND;VALUE=DATE:20110120
|
||||
DTSTAMP:20110121T195741Z
|
||||
UID:shvr7hvqdag08vjqlmj5lj0i2s@google.com
|
||||
CREATED:20110119T141913Z
|
||||
DESCRIPTION:
|
||||
LAST-MODIFIED:20110119T141913Z
|
||||
LOCATION:
|
||||
SEQUENCE:0
|
||||
STATUS:CONFIRMED
|
||||
SUMMARY:test 4
|
||||
TRANSP:TRANSPARENT
|
||||
END:VEVENT
|
||||
BEGIN:VEVENT
|
||||
DTSTART;VALUE=DATE:20110119
|
||||
DTEND;VALUE=DATE:20110120
|
||||
DTSTAMP:20110121T195741Z
|
||||
UID:77gpemlb9es0r0gtjolv3mtap0@google.com
|
||||
CREATED:20110119T141909Z
|
||||
DESCRIPTION:
|
||||
LAST-MODIFIED:20110119T141909Z
|
||||
LOCATION:
|
||||
SEQUENCE:0
|
||||
STATUS:CONFIRMED
|
||||
SUMMARY:test 3
|
||||
TRANSP:TRANSPARENT
|
||||
END:VEVENT
|
||||
BEGIN:VEVENT
|
||||
DTSTART;VALUE=DATE:20110119
|
||||
DTEND;VALUE=DATE:20110120
|
||||
DTSTAMP:20110121T195741Z
|
||||
UID:rq8jng4jgq0m1lvpj8486fttu0@google.com
|
||||
CREATED:20110119T141904Z
|
||||
DESCRIPTION:
|
||||
LAST-MODIFIED:20110119T141904Z
|
||||
LOCATION:
|
||||
SEQUENCE:0
|
||||
STATUS:CONFIRMED
|
||||
SUMMARY:test 2
|
||||
TRANSP:TRANSPARENT
|
||||
END:VEVENT
|
||||
BEGIN:VEVENT
|
||||
DTSTART;VALUE=DATE:20110119
|
||||
DTEND;VALUE=DATE:20110120
|
||||
DTSTAMP:20110121T195741Z
|
||||
UID:dh3fki5du0opa7cs5n5s87ca00@google.com
|
||||
CREATED:20110119T141901Z
|
||||
DESCRIPTION:
|
||||
LAST-MODIFIED:20110119T141901Z
|
||||
LOCATION:
|
||||
SEQUENCE:0
|
||||
STATUS:CONFIRMED
|
||||
SUMMARY:test 1
|
||||
TRANSP:TRANSPARENT
|
||||
END:VEVENT
|
||||
BEGIN:VEVENT
|
||||
DTSTART;VALUE=DATE:20400201
|
||||
DTEND;VALUE=DATE:20400202
|
||||
DTSTAMP:20400101T195741Z
|
||||
UID:dh3fki5du0opa7cs5n5s87ca01@google.com
|
||||
CREATED:20400101T141901Z
|
||||
DESCRIPTION:
|
||||
LAST-MODIFIED:20400101T141901Z
|
||||
LOCATION:
|
||||
SEQUENCE:0
|
||||
STATUS:CONFIRMED
|
||||
SUMMARY:Year 2038 problem test
|
||||
TRANSP:TRANSPARENT
|
||||
END:VEVENT
|
||||
BEGIN:VEVENT
|
||||
DTSTART;VALUE=DATE:19410512
|
||||
DTEND;VALUE=DATE:19410512
|
||||
DTSTAMP:19410512T195741Z
|
||||
UID:dh3fki5du0opa7cs5n5s87ca02@google.com
|
||||
CREATED:20400101T141901Z
|
||||
DESCRIPTION:
|
||||
LAST-MODIFIED:20400101T141901Z
|
||||
LOCATION:
|
||||
SEQUENCE:0
|
||||
STATUS:CONFIRMED
|
||||
SUMMARY:Before 1970-Test: Konrad Zuse invents the Z3, the first digital Computer
|
||||
TRANSP:TRANSPARENT
|
||||
END:VEVENT
|
||||
END:VCALENDAR
|
@@ -1,33 +0,0 @@
|
||||
BEGIN:VCALENDAR
|
||||
PRODID:-//Google Inc//Google Calendar 70.9054//EN
|
||||
VERSION:2.0
|
||||
CALSCALE:GREGORIAN
|
||||
METHOD:PUBLISH
|
||||
X-WR-CALNAME:Testkalender
|
||||
X-WR-TIMEZONE:Europe/Berlin
|
||||
X-WR-CALDESC:Nur zum testen vom Google Kalender
|
||||
BEGIN:VEVENT
|
||||
DTSTART;TZID="W. Europe":20121227T100000
|
||||
DTEND;TZID="W. Europe":20121227T120000
|
||||
DTSTAMP:20110121T195741Z
|
||||
UID:1koigufm110c5hnq6ln57murd4@google.com
|
||||
CREATED:20110119T142901Z
|
||||
DESCRIPTION:Project xyz Review Meeting Minutes\n
|
||||
Agenda\n1. Review of project version 1.0 requirements.\n2.
|
||||
Definition
|
||||
of project processes.\n3. Review of project schedule.\n
|
||||
Participants: John Smith, Jane Doe, Jim Dandy\n-It was
|
||||
decided that the requirements need to be signed off by
|
||||
product marketing.\n-Project processes were accepted.\n
|
||||
-Project schedule needs to account for scheduled holidays
|
||||
and employee vacation time. Check with HR for specific
|
||||
dates.\n-New schedule will be distributed by Friday.\n-
|
||||
Next weeks meeting is cancelled. No meeting until 3/23.
|
||||
LAST-MODIFIED:20110119T152216Z
|
||||
LOCATION:
|
||||
SEQUENCE:2
|
||||
STATUS:CONFIRMED
|
||||
SUMMARY:test 11
|
||||
TRANSP:TRANSPARENT
|
||||
END:VEVENT
|
||||
END:VCALENDAR
|
File diff suppressed because it is too large
Load Diff
@@ -1,21 +1,30 @@
|
||||
<?php
|
||||
/**
|
||||
* Parse ics file content to array.
|
||||
* class.icalparser.php zarafa calender to ics im/exporter
|
||||
* http://code.google.com/p/ics-parser/
|
||||
*
|
||||
* PHP Version 5
|
||||
* Author: Martin Thoma , Christoph Haas <christoph.h@sprinternet.at>
|
||||
* Copyright (C) 2012-2013 Christoph Haas
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
* @category Parser
|
||||
* @author Martin Thoma
|
||||
* @author Christoph Haas <mail@h44z.net>
|
||||
* @modified 17.11.2012 by Christoph Haas (original at http://code.google.com/p/ics-parser/)
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
* @version SVN: 62
|
||||
* @example $ical = new ical('calendar.ics');
|
||||
* print_r( $ical->events() );
|
||||
*/
|
||||
|
||||
/**
|
||||
* This is the iCal-class
|
||||
* Parse ics file content to array.
|
||||
*
|
||||
* @param {string} filename The name of the file which should be parsed
|
||||
* @constructor
|
||||
@@ -28,7 +37,7 @@ class ICal {
|
||||
public /** @type {int} */ $event_count = 0;
|
||||
|
||||
/* Currently editing an alarm? */
|
||||
private /** @type {int} */ $isalarm = false;
|
||||
private /** @type {boolean} */ $isalarm = false;
|
||||
|
||||
/* The parsed calendar */
|
||||
public /** @type {Array} */ $cal;
|
||||
@@ -41,7 +50,13 @@ class ICal {
|
||||
|
||||
/* The default timezone, used to convert UTC Time */
|
||||
private /** @type {string} */ $default_timezone = "Europe/Vienna";
|
||||
|
||||
|
||||
/* The default timezone, used to convert UTC Time */
|
||||
private /** @type {boolean} */ $timezone_set = false;
|
||||
|
||||
/* Ignore Daylight Saving Time */
|
||||
private /** @type {boolean} */ $ignore_dst = false;
|
||||
|
||||
/**
|
||||
* Creates the iCal-Object
|
||||
*
|
||||
@@ -49,12 +64,23 @@ class ICal {
|
||||
*
|
||||
* @return Object The iCal-Object
|
||||
*/
|
||||
public function __construct($filename) {
|
||||
public function __construct($filename, $default_timezone, $timezone = false, $igndst = false) {
|
||||
if (!$filename) {
|
||||
$this->errors = "No filename specified";
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->default_timezone = $default_timezone;
|
||||
|
||||
if(isset($timezone) && $timezone != false) {
|
||||
$this->default_timezone = $timezone;
|
||||
$this->timezone_set = true;
|
||||
}
|
||||
|
||||
if(isset($igndst) && $igndst != false) {
|
||||
$this->ignore_dst = true;
|
||||
}
|
||||
|
||||
$lines = file($filename, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
|
||||
if (stristr($lines[0], 'BEGIN:VCALENDAR') === false) {
|
||||
$this->errors = "Not a valid ical file";
|
||||
@@ -69,7 +95,7 @@ class ICal {
|
||||
continue;
|
||||
}
|
||||
|
||||
list($keyword, $prop, $propvalue, $value) = $add;
|
||||
list($keyword, $dummy, $prop, $propvalue, $value) = $add;
|
||||
|
||||
switch ($line) {
|
||||
// http://www.kanzaki.com/docs/ical/vtodo.html
|
||||
@@ -137,8 +163,11 @@ class ICal {
|
||||
if (stristr($keyword, "DTSTART") or stristr($keyword, "DTEND") or stristr($keyword, "TRIGGER")) {
|
||||
$ts = $this->iCalDateToUnixTimestamp($value, $prop, $propvalue);
|
||||
$value = $ts * 1000;
|
||||
}
|
||||
$value = str_replace("\\n", "\n", $value);
|
||||
}
|
||||
$value = str_replace("\\n", "\n", $value);
|
||||
|
||||
$value = $this->customFilters($keyword, $value);
|
||||
|
||||
if(!$this->isalarm) {
|
||||
$value = $this->cal[$component][$this->event_count - 1][$keyword].$value;
|
||||
} else {
|
||||
@@ -159,7 +188,7 @@ class ICal {
|
||||
//$keyword = $keyword[0]; // remove additional content like VALUE=DATE
|
||||
//}
|
||||
|
||||
if (stristr($keyword, "TIMEZONE")) {
|
||||
if (stristr($keyword, "TIMEZONE") && !$this->timezone_set) { // check if timezone already set...
|
||||
$this->default_timezone = $value; // store the calendertimezone
|
||||
}
|
||||
|
||||
@@ -175,6 +204,8 @@ class ICal {
|
||||
}
|
||||
$value = str_replace("\\n", "\n", $value);
|
||||
|
||||
$value = $this->customFilters($keyword, $value);
|
||||
|
||||
if(!$this->isalarm) {
|
||||
$this->cal[$component][$this->event_count - 1][$keyword] = $value;
|
||||
} else {
|
||||
@@ -188,33 +219,56 @@ class ICal {
|
||||
$this->last_keyword = $keyword;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter some chars out of the value.
|
||||
*
|
||||
* @param {string} $keyword keyword to which the filter is applied
|
||||
* @param {string} $value to filter
|
||||
* @return {string} filtered value
|
||||
*/
|
||||
private function customFilters($keyword, $value) {
|
||||
if (stristr($keyword, "SUMMARY")) {
|
||||
$value = str_replace("\n", " ", $value); // we don't need linebreaks in the summary...
|
||||
}
|
||||
|
||||
if (stristr($keyword, "SUMMARY")) {
|
||||
$value = str_replace("\,", ",", $value); // strange escaped comma
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a key-value pair of a string.
|
||||
*
|
||||
* @param {string} $text which is like "VCALENDAR:Begin" or "LOCATION:"
|
||||
*
|
||||
* @return {array} array("VCALENDAR", "Begin", "Optional Props")
|
||||
* @return {array} array("Argument", "Optional Arg/Val", "Optional Arg", "Optional Value", "Value")
|
||||
*/
|
||||
public function keyValueFromString($text) {
|
||||
preg_match("/(^[^a-z:;]+)[;]*([a-zA-Z]*)[=]*(.*)[:]([\w\W]*)/", $text, $matches);
|
||||
|
||||
preg_match('/(^[^a-z:;]+)([;]+([a-zA-Z]*)[=]*([^:"]*|"[\w\W]*"))?[:]([\w\W]*)/', $text, $matches);
|
||||
|
||||
// this regex has problems with multiple attributes... ATTENDEE;RSVP=TRUE;ROLE=REQ-PARTICIPANT:mailto:jsmith@example.com
|
||||
// TODO: fix this
|
||||
|
||||
if (count($matches) == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$matches = array_splice($matches, 1, 4);
|
||||
$matches = array_splice($matches, 1, 5); // 0 = Arg, 1 = Complete Optional Arg/Val, 2 = Optional Arg, 3 = Optional Val, 4 = Value
|
||||
return $matches;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return Unix timestamp from ical date time format
|
||||
* Return UTC 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, $prop, $propvalue) {
|
||||
private function iCalDateToUTCUnixTimestamp($icalDate, $prop, $propvalue) {
|
||||
|
||||
$timezone = false;
|
||||
|
||||
@@ -256,7 +310,6 @@ class ICal {
|
||||
(int)$date[3],
|
||||
(int)$date[1]);
|
||||
|
||||
|
||||
if(!$utc) {
|
||||
$tz = $this->default_timezone;
|
||||
if($timezone != false) {
|
||||
@@ -294,6 +347,53 @@ class ICal {
|
||||
return ($timestamp_utc);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a timezone specific timestamp
|
||||
* @param {int} $timestamp_utc UTC Timestamp to convert
|
||||
* @param {string} $timezone Timezone
|
||||
* @return {int}
|
||||
*/
|
||||
private function UTCTimestampToTZTimestamp($timestamp_utc, $timezone, $ignore_dst = false) {
|
||||
$this_tz = false;
|
||||
try { // Try using the default calendar timezone
|
||||
$this_tz = new DateTimeZone($this->default_timezone);
|
||||
} catch(Exception $e) {
|
||||
error_log($e->getMessage());
|
||||
$timestamp_utc = $timestamp; // if that fails, we cannot do anymore
|
||||
}
|
||||
if($this_tz != false) {
|
||||
$transition = $this_tz->getTransitions($timestamp_utc,$timestamp_utc);
|
||||
$trans_offset = $transition[0]['offset'];
|
||||
$isdst = $transition[0]['isdst'];
|
||||
|
||||
$tz_now = new DateTime("now", $this_tz);
|
||||
$tz_offset = $this_tz->getOffset($tz_now);
|
||||
|
||||
if(!$ignore_dst) {
|
||||
$tz_offset = $trans_offset; // normaly use dst
|
||||
}
|
||||
|
||||
return $timestamp_utc + $tz_offset;
|
||||
}
|
||||
return $timestamp_utc; // maybe timezone conversion will fail...
|
||||
}
|
||||
|
||||
/**
|
||||
* Return Timezone specific 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, $prop, $propvalue) {
|
||||
$timestamp = $this->iCalDateToUTCUnixTimestamp($icalDate, $prop, $propvalue);
|
||||
|
||||
$timestamp = $this->UTCTimestampToTZTimestamp($timestamp, $this->default_timezone, $this->ignore_dst);
|
||||
|
||||
return $timestamp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of arrays with all events. Every event is an associative
|
||||
* array and each property is an element it.
|
||||
@@ -387,13 +487,11 @@ class ICal {
|
||||
// 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']);
|
||||
$anEvent['UNIX_TIMESTAMP'] = $this->iCalDateToUnixTimestamp($anEvent['DTSTART']);
|
||||
}
|
||||
|
||||
if (!array_key_exists('REAL_DATETIME', $anEvent)) {
|
||||
$anEvent['REAL_DATETIME'] =
|
||||
date("d.m.Y", $anEvent['UNIX_TIMESTAMP']);
|
||||
$anEvent['REAL_DATETIME'] = date("d.m.Y", $anEvent['UNIX_TIMESTAMP']);
|
||||
}
|
||||
|
||||
$extendedEvents[] = $anEvent;
|
||||
|
@@ -1,9 +1,9 @@
|
||||
<?php
|
||||
/**
|
||||
* class.calendarexporter.php, zarafa calender to ics exporter
|
||||
* class.calendar.php, zarafa calender to ics im/exporter
|
||||
*
|
||||
* Author: Christoph Haas <christoph.h@sprinternet.at>
|
||||
* Copyright (C) 2012 Christoph Haas
|
||||
* Copyright (C) 2012-2013 Christoph Haas
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
@@ -23,8 +23,9 @@
|
||||
|
||||
include_once('mapi/class.recurrence.php');
|
||||
include_once('plugins/calendarimporter/php/ical/class.icalcreator.php');
|
||||
include_once('plugins/calendarimporter/php/ical/class.icalparser.php');
|
||||
|
||||
class CalendarexporterModule extends Module {
|
||||
class CalendarModule extends Module {
|
||||
|
||||
private $DEBUG = false; // enable error_log debugging
|
||||
|
||||
@@ -44,8 +45,13 @@ class CalendarexporterModule extends Module {
|
||||
*/
|
||||
public function execute() {
|
||||
$result = false;
|
||||
|
||||
foreach($this->data as $actionType => $actionData) {
|
||||
|
||||
if(!$this->DEBUG) {
|
||||
/* disable error printing - otherwise json communication might break... */
|
||||
ini_set('display_errors', '0');
|
||||
}
|
||||
|
||||
foreach($this->data as $actionType => $actionData) {
|
||||
if(isset($actionType)) {
|
||||
try {
|
||||
if($this->DEBUG) {
|
||||
@@ -54,7 +60,13 @@ class CalendarexporterModule extends Module {
|
||||
switch($actionType) {
|
||||
case "export":
|
||||
$result = $this->exportCalendar($actionType, $actionData);
|
||||
break;
|
||||
break;
|
||||
case "import":
|
||||
$result = $this->importCalendar($actionType, $actionData);
|
||||
break;
|
||||
case "attachmentpath":
|
||||
$result = $this->getAttachmentPath($actionType, $actionData);
|
||||
break;
|
||||
default:
|
||||
$this->handleUnknownActionType($actionType);
|
||||
}
|
||||
@@ -74,12 +86,18 @@ class CalendarexporterModule extends Module {
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a random string with variable length.
|
||||
* @param $length the lenght of the generated string
|
||||
* @return string a random string
|
||||
*/
|
||||
private function randomstring($length = 6) {
|
||||
// $chars - all allowed charakters
|
||||
$chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
|
||||
|
||||
srand((double)microtime()*1000000);
|
||||
$i = 0;
|
||||
$pass = "";
|
||||
while ($i < $length) {
|
||||
$num = rand() % strlen($chars);
|
||||
$tmp = substr($chars, $num, 1);
|
||||
@@ -89,6 +107,10 @@ class CalendarexporterModule extends Module {
|
||||
return $pass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the secid file (used to verify the download path)
|
||||
* @param $secid the secid, a random security token
|
||||
*/
|
||||
private function createSecIDFile($secid) {
|
||||
$lockFile = TMP_PATH . "/secid." . $secid;
|
||||
$fh = fopen($lockFile, 'w') or die("can't open secid file");
|
||||
@@ -97,10 +119,21 @@ class CalendarexporterModule extends Module {
|
||||
fclose($fh);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the secid file (used to verify the download path)
|
||||
* @param $time a timestamp
|
||||
* @param $incl_time true if date should include time
|
||||
* @ return date object
|
||||
*/
|
||||
private function getIcalDate($time, $incl_time = true) {
|
||||
return $incl_time ? date('Ymd\THis', $time) : date('Ymd', $time);
|
||||
}
|
||||
|
||||
/**
|
||||
* adds an event to the exported calendar)
|
||||
* @param $vevent pointer to the eventstore
|
||||
* @param $event the event to add
|
||||
*/
|
||||
private function addEvent(&$vevent, $event) {
|
||||
|
||||
$busystate = array("FREE", "TENTATIVE", "BUSY", "OOF");
|
||||
@@ -135,10 +168,13 @@ class CalendarexporterModule extends Module {
|
||||
$valarm->setProperty("description", $vevent->getProperty("SUMMARY")); // reuse the event summary
|
||||
$valarm->setProperty("trigger", $this->getIcalDate($event["reminder_time"]) . "Z"); // create alarm trigger (in UTC datetime)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the descriptiontext of an event
|
||||
* @param $event
|
||||
* @return array with event description/body
|
||||
*/
|
||||
private function loadEventDescription($event) {
|
||||
$entryid = $this->getActionEntryID($event);
|
||||
$store = $this->getActionStore($event);
|
||||
@@ -264,6 +300,11 @@ class CalendarexporterModule extends Module {
|
||||
return $data['item']['props']['body'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the attendees of an event
|
||||
* @param $event
|
||||
* @return array with event attendees
|
||||
*/
|
||||
private function loadAttendees($event) {
|
||||
$entryid = $this->getActionEntryID($event);
|
||||
$store = $this->getActionStore($event);
|
||||
@@ -275,7 +316,7 @@ class CalendarexporterModule extends Module {
|
||||
|
||||
$data = array();
|
||||
|
||||
if($store && $entryid) {
|
||||
if($store && $entryid) {
|
||||
$message = $GLOBALS['operations']->openMessage($store, $entryid);
|
||||
|
||||
|
||||
@@ -287,12 +328,21 @@ class CalendarexporterModule extends Module {
|
||||
return $data['item']['recipients']['item'];
|
||||
}
|
||||
|
||||
/**
|
||||
* The main export function, creates the ics file for download
|
||||
* @param $actionType
|
||||
* @param $actionData
|
||||
*/
|
||||
private function exportCalendar($actionType, $actionData) {
|
||||
$secid = $this->randomstring();
|
||||
$this->createSecIDFile($secid);
|
||||
$tmpname = stripslashes($actionData["calendar"] . ".ics." . $this->randomstring(8));
|
||||
$filename = TMP_PATH . "/" . $tmpname . "." . $secid;
|
||||
|
||||
if(!is_writable(TMP_PATH . "/")) {
|
||||
error_log("could not write to export tmp directory!");
|
||||
}
|
||||
|
||||
$tz = date("e"); // use php timezone (maybe set up in php.ini, date.timezone)
|
||||
|
||||
if($this->DEBUG) {
|
||||
@@ -308,37 +358,215 @@ class CalendarexporterModule extends Module {
|
||||
);
|
||||
|
||||
$v = new vcalendar($config);
|
||||
$v->setProperty("method", "PUBLISH"); // required of some calendar software
|
||||
$v->setProperty("x-wr-calname", $actionData["calendar"]); // required of some calendar software
|
||||
$v->setProperty("X-WR-CALDESC", "Exported Zarafa Calendar"); // required of some calendar software
|
||||
$v->setProperty("method", "PUBLISH"); // required of some calendar software
|
||||
$v->setProperty("x-wr-calname", $actionData["calendar"]); // required of some calendar software
|
||||
$v->setProperty("X-WR-CALDESC", "Exported Zarafa Calendar"); // required of some calendar software
|
||||
$v->setProperty("X-WR-TIMEZONE", $tz);
|
||||
|
||||
$xprops = array("X-LIC-LOCATION" => $tz); // required of some calendar software
|
||||
$xprops = array("X-LIC-LOCATION" => $tz); // required of some calendar software
|
||||
iCalUtilityFunctions::createTimezone($v, $tz, $xprops); // create timezone object in calendar
|
||||
|
||||
|
||||
foreach($actionData["data"]["item"] as $event) {
|
||||
foreach($actionData["data"] as $event) {
|
||||
$event["props"]["description"] = $this->loadEventDescription($event);
|
||||
$event["props"]["attendees"] = $this->loadAttendees($event);
|
||||
|
||||
$vevent = & $v->newComponent("vevent"); // create a new event object
|
||||
$vevent = & $v->newComponent("vevent"); // create a new event object
|
||||
$this->addEvent($vevent, $event["props"]);
|
||||
}
|
||||
|
||||
$v->saveCalendar();
|
||||
|
||||
$response['status'] = true;
|
||||
$response['fileid'] = $tmpname; // number of entries that will be exported
|
||||
$response['basedir'] = TMP_PATH;
|
||||
$response['secid'] = $secid;
|
||||
$response['realname'] = $actionData["calendar"];
|
||||
$response['status'] = true;
|
||||
$response['fileid'] = $tmpname; // number of entries that will be exported
|
||||
$response['basedir'] = TMP_PATH;
|
||||
$response['secid'] = $secid;
|
||||
$response['realname'] = $actionData["calendar"];
|
||||
$this->addActionData($actionType, $response);
|
||||
$GLOBALS["bus"]->addData($this->getResponseData());
|
||||
|
||||
if($this->DEBUG) {
|
||||
error_log("export done, bus data written!");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The main import function, parses the uploaded ics file
|
||||
* @param $actionType
|
||||
* @param $actionData
|
||||
*/
|
||||
private function importCalendar($actionType, $actionData) {
|
||||
if($this->DEBUG) {
|
||||
error_log("PHP Timezone: " . $tz);
|
||||
}
|
||||
|
||||
if(is_readable ($actionData["ics_filepath"])) {
|
||||
$ical = new ICal($actionData["ics_filepath"], $GLOBALS["settings"]->get("zarafa/v1/plugins/calendarimporter/default_timezone"), $actionData["timezone"], $actionData["ignore_dst"]); // Parse it!
|
||||
|
||||
if(isset($ical->errors)) {
|
||||
$response['status'] = false;
|
||||
$response['message']= $ical->errors;
|
||||
} else if(!$ical->hasEvents()) {
|
||||
$response['status'] = false;
|
||||
$response['message']= "No events in ics file";
|
||||
} else {
|
||||
$response['status'] = true;
|
||||
$response['parsed_file']= $actionData["ics_filepath"];
|
||||
$response['parsed'] = array (
|
||||
'calendar' => $ical->calendar(),
|
||||
'events' => $ical->events()
|
||||
);
|
||||
}
|
||||
} else {
|
||||
$response['status'] = false;
|
||||
$response['message']= "File could not be read by server";
|
||||
}
|
||||
|
||||
$this->addActionData($actionType, $response);
|
||||
$GLOBALS["bus"]->addData($this->getResponseData());
|
||||
|
||||
if($this->DEBUG) {
|
||||
error_log("parsing done, bus data written!");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Store the file to a temporary directory, prepare it for oc upload
|
||||
* @param $actionType
|
||||
* @param $actionData
|
||||
* @private
|
||||
*/
|
||||
private function getAttachmentPath($actionType, $actionData) {
|
||||
// Get store id
|
||||
$storeid = false;
|
||||
if(isset($actionData["store"])) {
|
||||
$storeid = $actionData["store"];
|
||||
}
|
||||
|
||||
// Get message entryid
|
||||
$entryid = false;
|
||||
if(isset($actionData["entryid"])) {
|
||||
$entryid = $actionData["entryid"];
|
||||
}
|
||||
|
||||
// Check which type isset
|
||||
$openType = "attachment";
|
||||
|
||||
// Get number of attachment which should be opened.
|
||||
$attachNum = false;
|
||||
if(isset($actionData["attachNum"])) {
|
||||
$attachNum = $actionData["attachNum"];
|
||||
}
|
||||
|
||||
// Check if storeid and entryid isset
|
||||
if($storeid && $entryid) {
|
||||
// Open the store
|
||||
$store = $GLOBALS["mapisession"]->openMessageStore(hex2bin($storeid));
|
||||
|
||||
if($store) {
|
||||
// Open the message
|
||||
$message = mapi_msgstore_openentry($store, hex2bin($entryid));
|
||||
|
||||
if($message) {
|
||||
$attachment = false;
|
||||
|
||||
// Check if attachNum isset
|
||||
if($attachNum) {
|
||||
// Loop through the attachNums, message in message in message ...
|
||||
for($i = 0; $i < (count($attachNum) - 1); $i++)
|
||||
{
|
||||
// Open the attachment
|
||||
$tempattach = mapi_message_openattach($message, (int) $attachNum[$i]);
|
||||
if($tempattach) {
|
||||
// Open the object in the attachment
|
||||
$message = mapi_attach_openobj($tempattach);
|
||||
}
|
||||
}
|
||||
|
||||
// Open the attachment
|
||||
$attachment = mapi_message_openattach($message, (int) $attachNum[(count($attachNum) - 1)]);
|
||||
}
|
||||
|
||||
// Check if the attachment is opened
|
||||
if($attachment) {
|
||||
|
||||
// Get the props of the attachment
|
||||
$props = mapi_attach_getprops($attachment, array(PR_ATTACH_LONG_FILENAME, PR_ATTACH_MIME_TAG, PR_DISPLAY_NAME, PR_ATTACH_METHOD));
|
||||
// Content Type
|
||||
$contentType = "application/octet-stream";
|
||||
// Filename
|
||||
$filename = "ERROR";
|
||||
|
||||
// Set filename
|
||||
if(isset($props[PR_ATTACH_LONG_FILENAME])) {
|
||||
$filename = $props[PR_ATTACH_LONG_FILENAME];
|
||||
} else if(isset($props[PR_ATTACH_FILENAME])) {
|
||||
$filename = $props[PR_ATTACH_FILENAME];
|
||||
} else if(isset($props[PR_DISPLAY_NAME])) {
|
||||
$filename = $props[PR_DISPLAY_NAME];
|
||||
}
|
||||
|
||||
// Set content type
|
||||
if(isset($props[PR_ATTACH_MIME_TAG])) {
|
||||
$contentType = $props[PR_ATTACH_MIME_TAG];
|
||||
} else {
|
||||
// Parse the extension of the filename to get the content type
|
||||
if(strrpos($filename, ".") !== false) {
|
||||
$extension = strtolower(substr($filename, strrpos($filename, ".")));
|
||||
$contentType = "application/octet-stream";
|
||||
if (is_readable("mimetypes.dat")){
|
||||
$fh = fopen("mimetypes.dat","r");
|
||||
$ext_found = false;
|
||||
while (!feof($fh) && !$ext_found){
|
||||
$line = fgets($fh);
|
||||
preg_match("/(\.[a-z0-9]+)[ \t]+([^ \t\n\r]*)/i", $line, $result);
|
||||
if ($extension == $result[1]){
|
||||
$ext_found = true;
|
||||
$contentType = $result[2];
|
||||
}
|
||||
}
|
||||
fclose($fh);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$tmpname = tempnam(TMP_PATH, stripslashes($filename));
|
||||
|
||||
// Open a stream to get the attachment data
|
||||
$stream = mapi_openpropertytostream($attachment, PR_ATTACH_DATA_BIN);
|
||||
$stat = mapi_stream_stat($stream);
|
||||
// File length = $stat["cb"]
|
||||
|
||||
$fhandle = fopen($tmpname,'w');
|
||||
$buffer = null;
|
||||
for($i = 0; $i < $stat["cb"]; $i += BLOCK_SIZE) {
|
||||
// Write stream
|
||||
$buffer = mapi_stream_read($stream, BLOCK_SIZE);
|
||||
fwrite($fhandle,$buffer,strlen($buffer));
|
||||
}
|
||||
fclose($fhandle);
|
||||
|
||||
$response = array();
|
||||
$response['tmpname'] = $tmpname;
|
||||
$response['filename'] = $filename;
|
||||
$response['status'] = true;
|
||||
$this->addActionData($actionType, $response);
|
||||
$GLOBALS["bus"]->addData($this->getResponseData());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$response['status'] = false;
|
||||
$response['message'] = "Store could not be opened!";
|
||||
$this->addActionData($actionType, $response);
|
||||
$GLOBALS["bus"]->addData($this->getResponseData());
|
||||
}
|
||||
} else {
|
||||
$response['status'] = false;
|
||||
$response['message'] = "Wrong call, store and entryid have to be set!";
|
||||
$this->addActionData($actionType, $response);
|
||||
$GLOBALS["bus"]->addData($this->getResponseData());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@@ -48,7 +48,8 @@ class Plugincalendarimporter extends Plugin {
|
||||
'calendarimporter' => Array(
|
||||
'enable' => PLUGIN_CALENDARIMPORTER_USER_DEFAULT_ENABLE,
|
||||
'enable_export' => PLUGIN_CALENDARIMPORTER_USER_DEFAULT_ENABLE_EXPORT,
|
||||
'default_calendar' => PLUGIN_CALENDARIMPORTER_DEFAULT
|
||||
'default_calendar' => PLUGIN_CALENDARIMPORTER_DEFAULT,
|
||||
'default_timezone' => PLUGIN_CALENDARIMPORTER_DEFAULT_TIMEZONE
|
||||
)
|
||||
)
|
||||
)
|
||||
|
@@ -1,34 +1,72 @@
|
||||
<?php
|
||||
/**
|
||||
* Handle the upload request from the gui
|
||||
*
|
||||
* PHP Version 5
|
||||
*
|
||||
* @author Christoph Haas <mail@h44z.net>
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
* @version SVN: 13
|
||||
*/
|
||||
/**
|
||||
* upload.php, zarafa calender to ics exporter
|
||||
*
|
||||
* Author: Christoph Haas <christoph.h@sprinternet.at>
|
||||
* Copyright (C) 2012-2013 Christoph Haas
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
|
||||
require_once("../config.php");
|
||||
|
||||
function respondJSON($arr) {
|
||||
|
||||
echo json_encode($arr);
|
||||
/* disable error printing - otherwise json communication might break... */
|
||||
ini_set('display_errors', '0');
|
||||
|
||||
/**
|
||||
* respond/echo JSON
|
||||
* @param $arr
|
||||
*/
|
||||
function respondJSON($arr) {
|
||||
echo json_encode($arr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a random string with variable length.
|
||||
* @param $length the lenght of the generated string
|
||||
* @return string a random string
|
||||
*/
|
||||
function randomstring($length = 6) {
|
||||
// $chars - all allowed charakters
|
||||
$chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
|
||||
|
||||
srand((double)microtime()*1000000);
|
||||
$i = 0;
|
||||
$pass = "";
|
||||
while ($i < $length) {
|
||||
$num = rand() % strlen($chars);
|
||||
$tmp = substr($chars, $num, 1);
|
||||
$pass = $pass . $tmp;
|
||||
$i++;
|
||||
}
|
||||
return $pass;
|
||||
}
|
||||
|
||||
$destpath = PLUGIN_CALENDARIMPORTER_TMP_UPLOAD;
|
||||
$destpath .= $_FILES['icsdata']['name'] . randomstring();
|
||||
|
||||
if(is_readable ($_FILES['icsdata']['tmp_name'])) {
|
||||
$result = move_uploaded_file($_FILES['icsdata']['tmp_name'],$destpath);
|
||||
|
||||
require_once("ical/class.icalparser.php");
|
||||
|
||||
$filepath = $_FILES['icsdata']['tmp_name'];
|
||||
|
||||
if(is_readable ($filepath)) {
|
||||
$ical = new ICal($filepath); // do not init with a file.. we set the content later
|
||||
|
||||
if(isset($ical->errors)) {
|
||||
respondJSON(array ('success'=>false,'errors'=>array ('parser'=>$ical->errors, 'type'=>'parser')));
|
||||
} else if(!$ical->hasEvents()) {
|
||||
respondJSON(array ('success'=>false,'errors'=>array ('parser'=>"No events in ics file", 'type'=>'parser')));
|
||||
} else {
|
||||
respondJSON(array ('success'=>true, 'response'=>array ('tmp_file'=>$filepath, 'calendar'=>$ical->calendar(), 'events'=>$ical->events())));
|
||||
}
|
||||
if($result) {
|
||||
respondJSON(array ('success'=>true, 'ics_file'=>$destpath));
|
||||
} else {
|
||||
respondJSON(array ('success'=>false,'errors'=>array ('reader'=>"File could not be read by server", 'type'=>'reader')));
|
||||
respondJSON(array ('success'=>false,'error'=>"File could not be moved to TMP path! Check plugin config and folder permissions!"));
|
||||
}
|
||||
} else {
|
||||
respondJSON(array ('success'=>false,'error'=>"File could not be read by server, upload error!"));
|
||||
}
|
||||
?>
|
Reference in New Issue
Block a user