calendarimporter 1.1 final:

- ics exporter
 - improved ics fileparser
 - fixed ExtJS Problem in chrome

KNOWN PROBLEMS: 
 - importer ignores some fields (priority, status...)
 - attechments are ignored
 - timezone handling is not perfect
 - recurrent events are not handled properly (im/export)
This commit is contained in:
Christoph Haas 2012-12-09 21:39:49 +00:00
parent d7ea948372
commit c0e51e294b
4 changed files with 10466 additions and 74 deletions

10
changelog.txt Normal file
View File

@ -0,0 +1,10 @@
calendarimporter 1.1 final:
- ics exporter
- improved ics fileparser
- fixed ExtJS Problem in chrome
KNOWN PROBLEMS:
- importer ignores some fields (priority, status...)
- attechments are ignored
- timezone handling is not perfect
- recurrent events are not handled properly (im/export)

10338
php/ical/class.icalcreator.php Normal file

File diff suppressed because it is too large Load Diff

View File

@ -61,6 +61,7 @@ class ICal {
foreach ($lines as $line) { foreach ($lines as $line) {
$line = trim($line); $line = trim($line);
$add = $this->keyValueFromString($line); $add = $this->keyValueFromString($line);
error_log("line: " . $line);
if ($add === false) { if ($add === false) {
$this->addCalendarComponentWithKeyAndValue($type, false, $line); $this->addCalendarComponentWithKeyAndValue($type, false, $line);
continue; continue;
@ -121,47 +122,48 @@ class ICal {
$keyword = $this->last_keyword; $keyword = $this->last_keyword;
switch ($component) { switch ($component) {
case 'VEVENT': case 'VEVENT':
if (stristr($keyword, "DTSTART") or stristr($keyword, "DTEND")) { if (stristr($keyword, "DTSTART") or stristr($keyword, "DTEND")) {
$ts = $this->iCalDateToUnixTimestamp($value); $ts = $this->iCalDateToUnixTimestamp($value);
$value = $ts * 1000; $value = $ts * 1000;
} }
$value = str_replace("\\n", "\n", $value); $value = str_replace("\\n", "\n", $value);
$value = $this->cal[$component][$this->event_count - 1] $value = $this->cal[$component][$this->event_count - 1]
[$keyword].$value; [$keyword].$value;
break; break;
case 'VTODO' : case 'VTODO' :
$value = $this->cal[$component][$this->todo_count - 1] $value = $this->cal[$component][$this->todo_count - 1]
[$keyword].$value; [$keyword].$value;
break; break;
} }
} }
if (stristr($keyword, "DTSTART") or stristr($keyword, "DTEND")) { //always strip additional content....
$keyword = explode(";", $keyword); //if (stristr($keyword, "DTSTART") or stristr($keyword, "DTEND")) {
$keyword = $keyword[0]; // remove additional content like VALUE=DATE $keyword = explode(";", $keyword);
} $keyword = $keyword[0]; // remove additional content like VALUE=DATE
//}
if (stristr($keyword, "TIMEZONE")) { if (stristr($keyword, "TIMEZONE")) {
$this->default_timezone = $value; // store the calendertimezone $this->default_timezone = $value; // store the calendertimezone
} }
switch ($component) { switch ($component) {
case "VTODO": case "VTODO":
$this->cal[$component][$this->todo_count - 1][$keyword] = $value; $this->cal[$component][$this->todo_count - 1][$keyword] = $value;
//$this->cal[$component][$this->todo_count]['Unix'] = $unixtime; //$this->cal[$component][$this->todo_count]['Unix'] = $unixtime;
break; break;
case "VEVENT": case "VEVENT":
if (stristr($keyword, "DTSTART") or stristr($keyword, "DTEND")) { if (stristr($keyword, "DTSTART") or stristr($keyword, "DTEND")) {
$ts = $this->iCalDateToUnixTimestamp($value); $ts = $this->iCalDateToUnixTimestamp($value);
$value = $ts * 1000; $value = $ts * 1000;
} }
$value = str_replace("\\n", "\n", $value); $value = str_replace("\\n", "\n", $value);
$this->cal[$component][$this->event_count - 1][$keyword] = $value; $this->cal[$component][$this->event_count - 1][$keyword] = $value;
break; break;
default: default:
$this->cal[$component][$keyword] = $value; $this->cal[$component][$keyword] = $value;
break; break;
} }
$this->last_keyword = $keyword; $this->last_keyword = $keyword;
} }
@ -174,10 +176,13 @@ class ICal {
* @return {array} array("VCALENDAR", "Begin") * @return {array} array("VCALENDAR", "Begin")
*/ */
public function keyValueFromString($text) { public function keyValueFromString($text) {
preg_match("/(^[^a-z:]+)[:]([\w\W]*)/", $text, $matches); preg_match("/(^[^a-z:]+[;a-zA-Z=\/]*)[:]([\w\W]*)/", $text, $matches);
error_log("macthes: " . count($matches). " " . $text);
if (count($matches) == 0) { if (count($matches) == 0) {
return false; return false;
} }
$matches = array_splice($matches, 1, 2); $matches = array_splice($matches, 1, 2);
return $matches; return $matches;
} }

View File

@ -28,6 +28,7 @@
*/ */
include_once('mapi/class.recurrence.php'); include_once('mapi/class.recurrence.php');
include_once('plugins/calendarimporter/php/ical/class.icalcreator.php');
class CalendarexporterModule extends Module { class CalendarexporterModule extends Module {
@ -102,53 +103,46 @@ class CalendarexporterModule extends Module {
fclose($fh); fclose($fh);
} }
private function writeICSHead($fh, $calname) {
$icshead = "BEGIN:VCALENDAR\nVERSION:2.0\nPRODID:-//Zarafa Webapp//Zarafa Calendar Exporter//DE\nMETHOD:PUBLISH\nX-WR-CALNAME:" . $calname. "\nX-WR-TIMEZONE:" . date("e") . "\n";
fwrite($fh, $icshead);
}
private function writeICSEnd($fh) {
$icsend = "END:VCALENDAR";
fwrite($fh, $icsend);
}
private function getIcalDate($time, $incl_time = true) { private function getIcalDate($time, $incl_time = true) {
return $incl_time ? date('Ymd\THis', $time) : date('Ymd', $time); return $incl_time ? date('Ymd\THis', $time) : date('Ymd', $time);
} }
private function writeEvent($fh, $event) { private function addEvent(&$vevent, $event) {
$head = "BEGIN:VEVENT\n";
$end = "END:VEVENT\n";
$busystate = array("FREE", "TENTATIVE", "BUSY", "OOF"); $busystate = array("FREE", "TENTATIVE", "BUSY", "OOF");
$zlabel = array("NONE", "IMPORTANT", "WORK", "PERSONAL", "HOLIDAY", "REQUIRED", "TRAVEL REQUIRED", "PREPARATION REQUIERED", "BIRTHDAY", "SPECIAL DATE", "PHONE INTERVIEW");
$fields = array( $vevent->setProperty("LOCATION", $event["location"]); // property name - case independent
"UID" => $this->randomstring(10) . "-" . $this->randomstring(5) . "-ics@zarafa-export-plugin", // generate uid $vevent->setProperty("SUMMARY", $event["subject"]);
"DTSTART" => $this->getIcalDate($event["commonstart"]) . "Z", // this times are utc! $vevent->setProperty("DESCRIPTION", str_replace("\n", "\\n",$event["description"]));
"DTEND" => $this->getIcalDate($event["commonend"]) . "Z", $vevent->setProperty("COMMENT", "Exported from Zarafa" );
"DTSTAMP" => $this->getIcalDate($event["creation_time"]) . "Z", $vevent->setProperty("ORGANIZER", $event["sent_representing_email_address"]);
"CREATED" => $this->getIcalDate($event["creation_time"]) . "Z", $vevent->setProperty("DTSTART", $this->getIcalDate($event["commonstart"]) . "Z");
"X-MICROSOFT-CDO-BUSYSTATUS" => $busystate[$event["busystatus"]], $vevent->setProperty("DTEND", $this->getIcalDate($event["commonend"]) . "Z");
"LAST-MODIFIED" => $this->getIcalDate($event["last_modification_time"]), $vevent->setProperty("DTSTAMP", $this->getIcalDate($event["creation_time"]) . "Z");
"DESCRIPTION" => str_replace("\n", "\\n",$event["description"]), $vevent->setProperty("CREATED", $this->getIcalDate($event["creation_time"]) . "Z");
"LOCATION" => $event["location"], $vevent->setProperty("LAST-MODIFIED", $this->getIcalDate($event["last_modification_time"]) . "Z");
"SUMMARY" => $event["subject"], $vevent->setProperty("X-MICROSOFT-CDO-BUSYSTATUS", $busystate[$event["busystatus"]]);
$vevent->setProperty("X-ZARAFA-LABEL", $zlabel[$event["label"]]);
// some static content... $vevent->setProperty("PRIORITY", $event["importance"]);
"TRANSP" => "OPAQUE", $vevent->setProperty("CLASS", $event["private"] ? "PRIVATE" : "PUBLIC");
"SEQUENCE" => "0"
);
fwrite($fh, $head); // ATTENDEES
if(count($event["attendees"]) > 0) {
foreach($event["attendees"] as $attendee) {
$vevent->setProperty("ATTENDEE", $attendee["props"]["smtp_address"]);
}
}
// event fields: // REMINDERS
foreach ($fields as $key => $value) { if($event["reminder"]) {
fwrite($fh, $key . ":" . $value . "\n"); $valarm = & $vevent->newComponent("valarm"); // create an event alarm
$valarm->setProperty("action", "DISPLAY" );
$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)
} }
unset($fields);
fwrite($fh, $end);
} }
private function loadEventDescription($event) { private function loadEventDescription($event) {
@ -276,23 +270,68 @@ class CalendarexporterModule extends Module {
return $data['item']['props']['body']; return $data['item']['props']['body'];
} }
private function loadAttendees($event) {
$entryid = $this->getActionEntryID($event);
$store = $this->getActionStore($event);
$basedate = null;
$properties = $GLOBALS['properties']->getAppointmentProperties();
$plaintext = true;
$data = array();
if($store && $entryid) {
$message = $GLOBALS['operations']->openMessage($store, $entryid);
// add all standard properties from the series/normal message
$data['item'] = $GLOBALS['operations']->getMessageProps($store, $message, $properties, (isset($plaintext) && $plaintext));
}
return $data['item']['recipients']['item'];
}
private function exportCalendar($actionType, $actionData) { private function exportCalendar($actionType, $actionData) {
$secid = $this->randomstring(); $secid = $this->randomstring();
$this->createSecIDFile($secid); $this->createSecIDFile($secid);
$tmpname = stripslashes($actionData["calendar"] . ".ics." . $this->randomstring(8)); $tmpname = stripslashes($actionData["calendar"] . ".ics." . $this->randomstring(8));
$filename = TMP_PATH . "/" . $tmpname . "." . $secid; $filename = TMP_PATH . "/" . $tmpname . "." . $secid;
// create ics file.... $tz = date("e"); // use php timezone (maybe set up in php.ini, date.timezone)
$fh = fopen($filename, 'w') or die("can't open ics file");
$this->writeICSHead($fh, $actionData["calendar"]); if($this->DEBUG) {
error_log("PHP Timezone: " . $tz);
}
$config = array(
"language" => substr($GLOBALS["settings"]->get("zarafa/v1/main/language"),0,2),
"directory" => TMP_PATH . "/",
"filename" => $tmpname . "." . $secid,
"unique_id" => "zarafa-export-plugin",
"TZID" => $tz
);
$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("X-WR-TIMEZONE", $tz);
$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"]["item"] as $event) {
$event["props"]["description"] = $this->loadEventDescription($event); $event["props"]["description"] = $this->loadEventDescription($event);
$this->writeEvent($fh, $event["props"]); $event["props"]["attendees"] = $this->loadAttendees($event);
$vevent = & $v->newComponent("vevent"); // create a new event object
$this->addEvent($vevent, $event["props"]);
} }
$this->writeICSEnd($fh); $v->saveCalendar();
fclose($fh);
$response['status'] = true; $response['status'] = true;
$response['fileid'] = $tmpname; // number of entries that will be exported $response['fileid'] = $tmpname; // number of entries that will be exported