diff --git a/plugins/libkolab/lib/kolab_format_contact.php b/plugins/libkolab/lib/kolab_format_contact.php
index d2634a06..358e1b1e 100644
--- a/plugins/libkolab/lib/kolab_format_contact.php
+++ b/plugins/libkolab/lib/kolab_format_contact.php
@@ -314,8 +314,8 @@ class kolab_format_contact extends kolab_format
// read object properties into local data object
$object = array(
'uid' => $this->obj->uid(),
- # 'changed' => $this->obj->lastModified(),
'name' => $this->obj->name(),
+ 'changed' => self::php_datetime($this->obj->lastModified()),
);
$nc = $this->obj->nameComponents();
diff --git a/plugins/libkolab/lib/kolab_format_distributionlist.php b/plugins/libkolab/lib/kolab_format_distributionlist.php
index 3931c61d..e8fae764 100644
--- a/plugins/libkolab/lib/kolab_format_distributionlist.php
+++ b/plugins/libkolab/lib/kolab_format_distributionlist.php
@@ -117,7 +117,7 @@ class kolab_format_distributionlist extends kolab_format
// read object properties
$object = array(
'uid' => $this->obj->uid(),
- 'changed' => $this->obj->lastModified(),
+ 'changed' => self::php_datetime($this->obj->lastModified()),
'name' => $this->obj->name(),
'member' => array(),
);
diff --git a/plugins/libkolab/lib/kolab_format_event.php b/plugins/libkolab/lib/kolab_format_event.php
index aec4e8d1..6af8f0d9 100644
--- a/plugins/libkolab/lib/kolab_format_event.php
+++ b/plugins/libkolab/lib/kolab_format_event.php
@@ -22,62 +22,13 @@
* along with this program. If not, see .
*/
-class kolab_format_event extends kolab_format
+class kolab_format_event extends kolab_format_xcal
{
- public $CTYPE = 'application/calendar+xml';
-
protected $read_func = 'kolabformat::readEvent';
protected $write_func = 'kolabformat::writeEvent';
public static $fulltext_cols = array('title', 'description', 'location', 'attendees:name', 'attendees:email');
- private $sensitivity_map = array(
- 'public' => kolabformat::ClassPublic,
- 'private' => kolabformat::ClassPrivate,
- 'confidential' => kolabformat::ClassConfidential,
- );
-
- private $role_map = array(
- 'REQ-PARTICIPANT' => kolabformat::Required,
- 'OPT-PARTICIPANT' => kolabformat::Optional,
- 'NON-PARTICIPANT' => kolabformat::NonParticipant,
- 'CHAIR' => kolabformat::Chair,
- );
-
- private $rrule_type_map = array(
- 'MINUTELY' => RecurrenceRule::Minutely,
- 'HOURLY' => RecurrenceRule::Hourly,
- 'DAILY' => RecurrenceRule::Daily,
- 'WEEKLY' => RecurrenceRule::Weekly,
- 'MONTHLY' => RecurrenceRule::Monthly,
- 'YEARLY' => RecurrenceRule::Yearly,
- );
-
- private $weekday_map = array(
- 'MO' => kolabformat::Monday,
- 'TU' => kolabformat::Tuesday,
- 'WE' => kolabformat::Wednesday,
- 'TH' => kolabformat::Thursday,
- 'FR' => kolabformat::Friday,
- 'SA' => kolabformat::Saturday,
- 'SU' => kolabformat::Sunday,
- );
-
- private $alarm_type_map = array(
- 'DISPLAY' => Alarm::DisplayAlarm,
- 'EMAIL' => Alarm::EMailAlarm,
- 'AUDIO' => Alarm::AudioAlarm,
- );
-
- private $status_map = array(
- 'UNKNOWN' => kolabformat::PartNeedsAction,
- 'NEEDS-ACTION' => kolabformat::PartNeedsAction,
- 'TENTATIVE' => kolabformat::PartTentative,
- 'ACCEPTED' => kolabformat::PartAccepted,
- 'DECLINED' => kolabformat::PartDeclined,
- 'DELEGATED' => kolabformat::PartDelegated,
- );
-
private $kolab2_rolemap = array(
'required' => 'REQ-PARTICIPANT',
'optional' => 'OPT-PARTICIPANT',
@@ -103,36 +54,20 @@ class kolab_format_event extends kolab_format
}
/**
- * Set contact properties to the kolabformat object
+ * Set event properties to the kolabformat object
*
- * @param array Contact data as hash array
+ * @param array Event data as hash array
*/
public function set(&$object)
{
$this->init();
- // set some automatic values if missing
- if (!$this->obj->created()) {
- if (!empty($object['created']))
- $object['created'] = new DateTime('now', self::$timezone);
- $this->obj->setCreated(self::get_datetime($object['created']));
- }
-
- if (!empty($object['uid']))
- $this->obj->setUid($object['uid']);
-
- // increment sequence
- $this->obj->setSequence($this->obj->sequence()+1);
+ // set common xcal properties
+ parent::set($object);
// do the hard work of setting object values
$this->obj->setStart(self::get_datetime($object['start'], null, $object['allday']));
$this->obj->setEnd(self::get_datetime($object['end'], null, $object['allday']));
- $this->obj->setSummary($object['title']);
- $this->obj->setLocation($object['location']);
- $this->obj->setDescription($object['description']);
- $this->obj->setPriority($object['priority']);
- $this->obj->setClassification($this->sensitivity_map[$object['sensitivity']]);
- $this->obj->setCategories(self::array2vector($object['categories']));
$this->obj->setTransparency($object['free_busy'] == 'free');
$status = kolabformat::StatusUndefined;
@@ -142,130 +77,6 @@ class kolab_format_event extends kolab_format
$status = kolabformat::StatusCancelled;
$this->obj->setStatus($status);
- // process event attendees
- $organizer = new ContactReference;
- $attendees = new vectorattendee;
- foreach ((array)$object['attendees'] as $attendee) {
- $cr = new ContactReference(ContactReference::EmailReference, $attendee['email']);
- $cr->setName($attendee['name']);
-
- if ($attendee['role'] == 'ORGANIZER') {
- $organizer = $cr;
- }
- else {
- $att = new Attendee;
- $att->setContact($cr);
- $att->setPartStat($this->status_map[$attendee['status']]);
- $att->setRole($this->role_map[$attendee['role']] ? $this->role_map[$attendee['role']] : kolabformat::Required);
- $att->setRSVP((bool)$attendee['rsvp']);
-
- if ($att->isValid()) {
- $attendees->push($att);
- }
- else {
- raise_error(array(
- 'code' => 600, 'type' => 'php',
- 'file' => __FILE__, 'line' => __LINE__,
- 'message' => "Invalid event attendee: " . json_encode($attendee),
- ), true);
- }
- }
- }
- $this->obj->setOrganizer($organizer);
- $this->obj->setAttendees($attendees);
-
- // save recurrence rule
- if ($object['recurrence']) {
- $rr = new RecurrenceRule;
- $rr->setFrequency($this->rrule_type_map[$object['recurrence']['FREQ']]);
-
- if ($object['recurrence']['INTERVAL'])
- $rr->setInterval(intval($object['recurrence']['INTERVAL']));
-
- if ($object['recurrence']['BYDAY']) {
- $byday = new vectordaypos;
- foreach (explode(',', $object['recurrence']['BYDAY']) as $day) {
- $occurrence = 0;
- if (preg_match('/^([\d-]+)([A-Z]+)$/', $day, $m)) {
- $occurrence = intval($m[1]);
- $day = $m[2];
- }
- if (isset($this->weekday_map[$day]))
- $byday->push(new DayPos($occurrence, $this->weekday_map[$day]));
- }
- $rr->setByday($byday);
- }
-
- if ($object['recurrence']['BYMONTHDAY']) {
- $bymday = new vectori;
- foreach (explode(',', $object['recurrence']['BYMONTHDAY']) as $day)
- $bymday->push(intval($day));
- $rr->setBymonthday($bymday);
- }
-
- if ($object['recurrence']['BYMONTH']) {
- $bymonth = new vectori;
- foreach (explode(',', $object['recurrence']['BYMONTH']) as $month)
- $bymonth->push(intval($month));
- $rr->setBymonth($bymonth);
- }
-
- if ($object['recurrence']['COUNT'])
- $rr->setCount(intval($object['recurrence']['COUNT']));
- else if ($object['recurrence']['UNTIL'])
- $rr->setEnd(self::get_datetime($object['recurrence']['UNTIL'], null, true));
-
- if ($rr->isValid()) {
- $this->obj->setRecurrenceRule($rr);
-
- // add exception dates (only if recurrence rule is valid)
- $exdates = new vectordatetime;
- foreach ((array)$object['recurrence']['EXDATE'] as $exdate)
- $exdates->push(self::get_datetime($exdate, null, true));
- $this->obj->setExceptionDates($exdates);
- }
- else {
- raise_error(array(
- 'code' => 600, 'type' => 'php',
- 'file' => __FILE__, 'line' => __LINE__,
- 'message' => "Invalid event recurrence rule: " . json_encode($object['recurrence']),
- ), true);
- }
- }
-
- // save alarm
- $valarms = new vectoralarm;
- if ($object['alarms']) {
- list($offset, $type) = explode(":", $object['alarms']);
-
- if ($type == 'EMAIL') { // email alarms implicitly go to event owner
- $recipients = new vectorcontactref;
- $recipients->push(new ContactReference(ContactReference::EmailReference, $object['_owner']));
- $alarm = new Alarm($object['title'], strval($object['description']), $recipients);
- }
- else { // default: display alarm
- $alarm = new Alarm($object['title']);
- }
-
- if (preg_match('/^@(\d+)/', $offset, $d)) {
- $alarm->setStart(self::get_datetime($d[1], new DateTimeZone('UTC')));
- }
- else if (preg_match('/^([-+]?)(\d+)([SMHDW])/', $offset, $d)) {
- $days = $hours = $minutes = $seconds = 0;
- switch ($d[3]) {
- case 'W': $days = 7*intval($d[2]); break;
- case 'D': $days = intval($d[2]); break;
- case 'H': $hours = intval($d[2]); break;
- case 'M': $minutes = intval($d[2]); break;
- case 'S': $seconds = intval($d[2]); break;
- }
- $alarm->setRelativeStart(new Duration($days, $hours, $minutes, $seconds, $d[1] == '-'), $d[1] == '-' ? kolabformat::Start : kolabformat::End);
- }
-
- $valarms->push($alarm);
- }
- $this->obj->setAlarms($valarms);
-
// save attachments
$vattach = new vectorattachment;
foreach ((array)$object['_attachments'] as $name => $attr) {
@@ -292,9 +103,9 @@ class kolab_format_event extends kolab_format
}
/**
- * Convert the Contact object into a hash array data structure
+ * Convert the Event object into a hash array data structure
*
- * @return array Contact data as hash array
+ * @return array Event data as hash array
*/
public function to_array()
{
@@ -304,24 +115,24 @@ class kolab_format_event extends kolab_format
$this->init();
- $sensitivity_map = array_flip($this->sensitivity_map);
+ // read common xcal props
+ $object = parent::to_array();
// read object properties
- $object = array(
- 'uid' => $this->obj->uid(),
- 'changed' => self::php_datetime($this->obj->lastModified()),
- 'title' => $this->obj->summary(),
- 'location' => $this->obj->location(),
- 'description' => $this->obj->description(),
+ $object += array(
'allday' => $this->obj->start()->isDateOnly(),
'start' => self::php_datetime($this->obj->start()),
'end' => self::php_datetime($this->obj->end()),
- 'categories' => self::vector2array($this->obj->categories()),
'free_busy' => $this->obj->transparency() ? 'free' : 'busy', // TODO: transparency is only boolean
- 'sensitivity' => $sensitivity_map[$this->obj->classification()],
- 'priority' => $this->obj->priority(),
+ 'attendees' => array(),
);
+ // organizer is part of the attendees list in Roundcube
+ if ($object['organizer']) {
+ $object['organizer']['role'] = 'ORGANIZER';
+ array_unshift($object['attendees'], $object['organizer']);
+ }
+
// status defines different event properties...
$status = $this->obj->status();
if ($status == kolabformat::StatusTentative)
@@ -329,100 +140,6 @@ class kolab_format_event extends kolab_format
else if ($status == kolabformat::StatusCancelled)
$objec['cancelled'] = true;
- // read organizer and attendees
- if ($organizer = $this->obj->organizer()) {
- $object['attendees'][] = array(
- 'role' => 'ORGANIZER',
- 'email' => $organizer->email(),
- 'name' => $organizer->name(),
- );
- }
-
- $role_map = array_flip($this->role_map);
- $status_map = array_flip($this->status_map);
- $attvec = $this->obj->attendees();
- for ($i=0; $i < $attvec->size(); $i++) {
- $attendee = $attvec->get($i);
- $cr = $attendee->contact();
- $object['attendees'][] = array(
- 'role' => $role_map[$attendee->role()],
- 'status' => $status_map[$attendee->partStat()],
- 'rsvp' => $attendee->rsvp(),
- 'email' => $cr->email(),
- 'name' => $cr->name(),
- );
- }
-
- // read recurrence rule
- if (($rr = $this->obj->recurrenceRule()) && $rr->isValid()) {
- $rrule_type_map = array_flip($this->rrule_type_map);
- $object['recurrence'] = array('FREQ' => $rrule_type_map[$rr->frequency()]);
-
- if ($intvl = $rr->interval())
- $object['recurrence']['INTERVAL'] = $intvl;
-
- if (($count = $rr->count()) && $count > 0) {
- $object['recurrence']['COUNT'] = $count;
- }
- else if ($until = self::php_datetime($rr->end())) {
- $until->setTime($object['start']->format('G'), $object['start']->format('i'), 0);
- $object['recurrence']['UNTIL'] = $until->format('U');
- }
-
- if (($byday = $rr->byday()) && $byday->size()) {
- $weekday_map = array_flip($this->weekday_map);
- $weekdays = array();
- for ($i=0; $i < $byday->size(); $i++) {
- $daypos = $byday->get($i);
- $prefix = $daypos->occurence();
- $weekdays[] = ($prefix ? $prefix : '') . $weekday_map[$daypos->weekday()];
- }
- $object['recurrence']['BYDAY'] = join(',', $weekdays);
- }
-
- if (($bymday = $rr->bymonthday()) && $bymday->size()) {
- $object['recurrence']['BYMONTHDAY'] = join(',', self::vector2array($bymday));
- }
-
- if (($bymonth = $rr->bymonth()) && $bymonth->size()) {
- $object['recurrence']['BYMONTH'] = join(',', self::vector2array($bymonth));
- }
-
- if ($exceptions = $this->obj->exceptionDates()) {
- for ($i=0; $i < $exceptions->size(); $i++) {
- if ($exdate = self::php_datetime($exceptions->get($i)))
- $object['recurrence']['EXDATE'][] = $exdate->format('U');
- }
- }
- }
-
- // read alarm
- $valarms = $this->obj->alarms();
- $alarm_types = array_flip($this->alarm_type_map);
- for ($i=0; $i < $valarms->size(); $i++) {
- $alarm = $valarms->get($i);
- $type = $alarm_types[$alarm->type()];
-
- if ($type == 'DISPLAY' || $type == 'EMAIL') { // only DISPLAY and EMAIL alarms are supported
- if ($start = self::php_datetime($alarm->start())) {
- $object['alarms'] = '@' . $start->format('U');
- }
- else if ($offset = $alarm->relativeStart()) {
- $value = $alarm->relativeTo() == kolabformat::End ? '+' : '-';
- if ($w = $offset->weeks()) $value .= $w . 'W';
- else if ($d = $offset->days()) $value .= $d . 'D';
- else if ($h = $offset->hours()) $value .= $h . 'H';
- else if ($m = $offset->minutes()) $value .= $m . 'M';
- else if ($s = $offset->seconds()) $value .= $s . 'S';
- else continue;
-
- $object['alarms'] = $value;
- }
- $object['alarms'] .= ':' . $type;
- break;
- }
- }
-
// handle attachments
$vattach = $this->obj->attachments();
for ($i=0; $i < $vattach->size(); $i++) {
diff --git a/plugins/libkolab/lib/kolab_format_task.php b/plugins/libkolab/lib/kolab_format_task.php
index cf89b53d..ff790cfc 100644
--- a/plugins/libkolab/lib/kolab_format_task.php
+++ b/plugins/libkolab/lib/kolab_format_task.php
@@ -22,19 +22,11 @@
* along with this program. If not, see .
*/
-class kolab_format_task extends kolab_format
+class kolab_format_task extends kolab_format_xcal
{
- public $CTYPE = 'application/calendar+xml';
-
protected $read_func = 'kolabformat::readTodo';
protected $write_func = 'kolabformat::writeTodo';
- private $status_map = array(
- 'NEEDS-ACTION' => kolabformat::StatusNeedsAction,
- 'IN-PROCESS' => kolabformat::StatusInProcess,
- 'COMPLETED' => kolabformat::StatusCompleted,
- 'CANCELLED' => kolabformat::StatusCancelled,
- );
function __construct($xmldata = null)
{
@@ -51,11 +43,22 @@ class kolab_format_task extends kolab_format
{
$this->init();
- // set some automatic values if missing
- if (!empty($object['uid']))
- $this->obj->setUid($object['uid']);
+ // set common xcal properties
+ parent::set($object);
- // TODO: set object propeties
+ $this->obj->setPercentComplete(intval($object['complete']));
+
+ if (isset($object['start']))
+ $this->obj->setStart(self::get_datetime($object['start']));
+ if (isset($object['end']))
+ $this->obj->setEnd(self::get_datetime($object['end']));
+
+ $this->obj->setDue(self::get_datetime($object['due'], null, $object['due']->_dateonly));
+
+ $related = new vectors;
+ if (!empty($object['parent_id']))
+ $related->push($object['parent_id']);
+ $this->obj->setRelatedTo($related);
// cache this data
$this->data = $object;
@@ -70,6 +73,45 @@ class kolab_format_task extends kolab_format
return $this->data || (is_object($this->obj) && $this->obj->isValid());
}
+ /**
+ * Convert the Configuration object into a hash array data structure
+ *
+ * @return array Config object data as hash array
+ */
+ public function to_array()
+ {
+ // return cached result
+ if (!empty($this->data))
+ return $this->data;
+
+ $this->init();
+
+ // read common xcal props
+ $object = parent::to_array();
+
+ $object['complete'] = intval($this->obj->percentComplete());
+
+ // if start/end date is set
+ if ($dtstart = $this->obj->start())
+ $object['start'] = self::php_datetime($dtstart);
+ if ($dtend = $this->obj->end())
+ $object['end'] = self::php_datetime($dtend);
+
+ // if due date is set
+ if ($due = $this->obj->due())
+ $object['due'] = self::php_datetime($due);
+
+ // related-to points to parent taks; we only support one relation
+ $related = self::vector2array($this->obj->relatedTo());
+ if (count($related))
+ $object['parent_id'] = $related[0];
+
+ // TODO: map more properties
+
+ $this->data = $object;
+ return $this->data;
+ }
+
/**
* Load data from old Kolab2 format
*/
@@ -85,45 +127,4 @@ class kolab_format_task extends kolab_format
$this->data = $object;
}
- /**
- * Convert the Configuration object into a hash array data structure
- *
- * @return array Config object data as hash array
- */
- public function to_array()
- {
- // return cached result
- if (!empty($this->data))
- return $this->data;
-
- $this->init();
-
- // read object properties
- $status_map = array_flip($this->status_map);
- $object = array(
- 'uid' => $this->obj->uid(),
- 'changed' => $this->obj->lastModified(),
- 'summary' => $this->obj->summary(),
- 'description' => $this->obj->description(),
- 'location' => $this->obj->location(),
- 'status' => $this->status_map[$this->obj->status()],
- 'complete' => intval($this->obj->percentComplete()),
- 'priority' => $this->obj->priority(),
- );
-
- // if due date is set
- if ($dtstart = $this->obj->start()) {
- $object['start'] = self::php_datetime($dtstart);
- }
- // if due date is set
- if ($due = $this->obj->due()) {
- $object['due'] = self::php_datetime($due);
- }
-
- // TODO: map more properties
-
- $this->data = $object;
- return $this->data;
- }
-
}
diff --git a/plugins/libkolab/lib/kolab_format_xcal.php b/plugins/libkolab/lib/kolab_format_xcal.php
new file mode 100644
index 00000000..1eb2acf8
--- /dev/null
+++ b/plugins/libkolab/lib/kolab_format_xcal.php
@@ -0,0 +1,361 @@
+
+ *
+ * Copyright (C) 2012, Kolab Systems AG
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program 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 Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+abstract class kolab_format_xcal extends kolab_format
+{
+ public $CTYPE = 'application/calendar+xml';
+
+ protected $sensitivity_map = array(
+ 'public' => kolabformat::ClassPublic,
+ 'private' => kolabformat::ClassPrivate,
+ 'confidential' => kolabformat::ClassConfidential,
+ );
+
+ protected $role_map = array(
+ 'REQ-PARTICIPANT' => kolabformat::Required,
+ 'OPT-PARTICIPANT' => kolabformat::Optional,
+ 'NON-PARTICIPANT' => kolabformat::NonParticipant,
+ 'CHAIR' => kolabformat::Chair,
+ );
+
+ protected $rrule_type_map = array(
+ 'MINUTELY' => RecurrenceRule::Minutely,
+ 'HOURLY' => RecurrenceRule::Hourly,
+ 'DAILY' => RecurrenceRule::Daily,
+ 'WEEKLY' => RecurrenceRule::Weekly,
+ 'MONTHLY' => RecurrenceRule::Monthly,
+ 'YEARLY' => RecurrenceRule::Yearly,
+ );
+
+ protected $weekday_map = array(
+ 'MO' => kolabformat::Monday,
+ 'TU' => kolabformat::Tuesday,
+ 'WE' => kolabformat::Wednesday,
+ 'TH' => kolabformat::Thursday,
+ 'FR' => kolabformat::Friday,
+ 'SA' => kolabformat::Saturday,
+ 'SU' => kolabformat::Sunday,
+ );
+
+ protected $alarm_type_map = array(
+ 'DISPLAY' => Alarm::DisplayAlarm,
+ 'EMAIL' => Alarm::EMailAlarm,
+ 'AUDIO' => Alarm::AudioAlarm,
+ );
+
+ private $status_map = array(
+ 'NEEDS-ACTION' => kolabformat::StatusNeedsAction,
+ 'IN-PROCESS' => kolabformat::StatusInProcess,
+ 'COMPLETED' => kolabformat::StatusCompleted,
+ 'CANCELLED' => kolabformat::StatusCancelled,
+ );
+
+ protected $part_status_map = array(
+ 'UNKNOWN' => kolabformat::PartNeedsAction,
+ 'NEEDS-ACTION' => kolabformat::PartNeedsAction,
+ 'TENTATIVE' => kolabformat::PartTentative,
+ 'ACCEPTED' => kolabformat::PartAccepted,
+ 'DECLINED' => kolabformat::PartDeclined,
+ 'DELEGATED' => kolabformat::PartDelegated,
+ );
+
+
+ /**
+ * Convert common xcard properties into a hash array data structure
+ *
+ * @return array Object data as hash array
+ */
+ public function to_array()
+ {
+ $status_map = array_flip($this->status_map);
+ $sensitivity_map = array_flip($this->sensitivity_map);
+
+ $object = array(
+ 'uid' => $this->obj->uid(),
+ 'changed' => self::php_datetime($this->obj->lastModified()),
+ 'title' => $this->obj->summary(),
+ 'location' => $this->obj->location(),
+ 'description' => $this->obj->description(),
+ 'status' => $this->status_map[$this->obj->status()],
+ 'sensitivity' => $sensitivity_map[$this->obj->classification()],
+ 'priority' => $this->obj->priority(),
+ 'categories' => self::vector2array($this->obj->categories()),
+ );
+
+ // read organizer and attendees
+ if ($organizer = $this->obj->organizer()) {
+ $object['organizer'] = array(
+ 'email' => $organizer->email(),
+ 'name' => $organizer->name(),
+ );
+ }
+
+ $role_map = array_flip($this->role_map);
+ $part_status_map = array_flip($this->part_status_map);
+ $attvec = $this->obj->attendees();
+ for ($i=0; $i < $attvec->size(); $i++) {
+ $attendee = $attvec->get($i);
+ $cr = $attendee->contact();
+ $object['attendees'][] = array(
+ 'role' => $role_map[$attendee->role()],
+ 'status' => $part_status_map[$attendee->partStat()],
+ 'rsvp' => $attendee->rsvp(),
+ 'email' => $cr->email(),
+ 'name' => $cr->name(),
+ );
+ }
+
+ // read recurrence rule
+ if (($rr = $this->obj->recurrenceRule()) && $rr->isValid()) {
+ $rrule_type_map = array_flip($this->rrule_type_map);
+ $object['recurrence'] = array('FREQ' => $rrule_type_map[$rr->frequency()]);
+
+ if ($intvl = $rr->interval())
+ $object['recurrence']['INTERVAL'] = $intvl;
+
+ if (($count = $rr->count()) && $count > 0) {
+ $object['recurrence']['COUNT'] = $count;
+ }
+ else if ($until = self::php_datetime($rr->end())) {
+ $until->setTime($object['start']->format('G'), $object['start']->format('i'), 0);
+ $object['recurrence']['UNTIL'] = $until->format('U');
+ }
+
+ if (($byday = $rr->byday()) && $byday->size()) {
+ $weekday_map = array_flip($this->weekday_map);
+ $weekdays = array();
+ for ($i=0; $i < $byday->size(); $i++) {
+ $daypos = $byday->get($i);
+ $prefix = $daypos->occurence();
+ $weekdays[] = ($prefix ? $prefix : '') . $weekday_map[$daypos->weekday()];
+ }
+ $object['recurrence']['BYDAY'] = join(',', $weekdays);
+ }
+
+ if (($bymday = $rr->bymonthday()) && $bymday->size()) {
+ $object['recurrence']['BYMONTHDAY'] = join(',', self::vector2array($bymday));
+ }
+
+ if (($bymonth = $rr->bymonth()) && $bymonth->size()) {
+ $object['recurrence']['BYMONTH'] = join(',', self::vector2array($bymonth));
+ }
+
+ if ($exceptions = $this->obj->exceptionDates()) {
+ for ($i=0; $i < $exceptions->size(); $i++) {
+ if ($exdate = self::php_datetime($exceptions->get($i)))
+ $object['recurrence']['EXDATE'][] = $exdate->format('U');
+ }
+ }
+ }
+
+ // read alarm
+ $valarms = $this->obj->alarms();
+ $alarm_types = array_flip($this->alarm_type_map);
+ for ($i=0; $i < $valarms->size(); $i++) {
+ $alarm = $valarms->get($i);
+ $type = $alarm_types[$alarm->type()];
+
+ if ($type == 'DISPLAY' || $type == 'EMAIL') { // only DISPLAY and EMAIL alarms are supported
+ if ($start = self::php_datetime($alarm->start())) {
+ $object['alarms'] = '@' . $start->format('U');
+ }
+ else if ($offset = $alarm->relativeStart()) {
+ $value = $alarm->relativeTo() == kolabformat::End ? '+' : '-';
+ if ($w = $offset->weeks()) $value .= $w . 'W';
+ else if ($d = $offset->days()) $value .= $d . 'D';
+ else if ($h = $offset->hours()) $value .= $h . 'H';
+ else if ($m = $offset->minutes()) $value .= $m . 'M';
+ else if ($s = $offset->seconds()) $value .= $s . 'S';
+ else continue;
+
+ $object['alarms'] = $value;
+ }
+ $object['alarms'] .= ':' . $type;
+ break;
+ }
+ }
+
+ return $object;
+ }
+
+
+ /**
+ * Set common xcal properties to the kolabformat object
+ *
+ * @param array Event data as hash array
+ */
+ public function set(&$object)
+ {
+ // set some automatic values if missing
+ if (!$this->obj->created()) {
+ if (!empty($object['created']))
+ $object['created'] = new DateTime('now', self::$timezone);
+ $this->obj->setCreated(self::get_datetime($object['created']));
+ }
+
+ if (!empty($object['uid']))
+ $this->obj->setUid($object['uid']);
+
+ // increment sequence
+ $this->obj->setSequence($this->obj->sequence()+1);
+
+ $this->obj->setSummary($object['title']);
+ $this->obj->setLocation($object['location']);
+ $this->obj->setDescription($object['description']);
+ $this->obj->setPriority($object['priority']);
+ $this->obj->setClassification($this->sensitivity_map[$object['sensitivity']]);
+ $this->obj->setCategories(self::array2vector($object['categories']));
+
+ // process event attendees
+ $attendees = new vectorattendee;
+ foreach ((array)$object['attendees'] as $attendee) {
+ if ($attendee['role'] == 'ORGANIZER') {
+ $object['organizer'] = $attendee;
+ }
+ else {
+ $cr = new ContactReference(ContactReference::EmailReference, $attendee['email']);
+ $cr->setName($attendee['name']);
+
+ $att = new Attendee;
+ $att->setContact($cr);
+ $att->setPartStat($this->status_map[$attendee['status']]);
+ $att->setRole($this->role_map[$attendee['role']] ? $this->role_map[$attendee['role']] : kolabformat::Required);
+ $att->setRSVP((bool)$attendee['rsvp']);
+
+ if ($att->isValid()) {
+ $attendees->push($att);
+ }
+ else {
+ raise_error(array(
+ 'code' => 600, 'type' => 'php',
+ 'file' => __FILE__, 'line' => __LINE__,
+ 'message' => "Invalid event attendee: " . json_encode($attendee),
+ ), true);
+ }
+ }
+ }
+ $this->obj->setAttendees($attendees);
+
+ if ($object['organizer']) {
+ $organizer = new ContactReference(ContactReference::EmailReference, $object['organizer']['email']);
+ $organizer->setName($object['organizer']['name']);
+ $this->obj->setOrganizer($organizer);
+ }
+
+ // save recurrence rule
+ if ($object['recurrence']) {
+ $rr = new RecurrenceRule;
+ $rr->setFrequency($this->rrule_type_map[$object['recurrence']['FREQ']]);
+
+ if ($object['recurrence']['INTERVAL'])
+ $rr->setInterval(intval($object['recurrence']['INTERVAL']));
+
+ if ($object['recurrence']['BYDAY']) {
+ $byday = new vectordaypos;
+ foreach (explode(',', $object['recurrence']['BYDAY']) as $day) {
+ $occurrence = 0;
+ if (preg_match('/^([\d-]+)([A-Z]+)$/', $day, $m)) {
+ $occurrence = intval($m[1]);
+ $day = $m[2];
+ }
+ if (isset($this->weekday_map[$day]))
+ $byday->push(new DayPos($occurrence, $this->weekday_map[$day]));
+ }
+ $rr->setByday($byday);
+ }
+
+ if ($object['recurrence']['BYMONTHDAY']) {
+ $bymday = new vectori;
+ foreach (explode(',', $object['recurrence']['BYMONTHDAY']) as $day)
+ $bymday->push(intval($day));
+ $rr->setBymonthday($bymday);
+ }
+
+ if ($object['recurrence']['BYMONTH']) {
+ $bymonth = new vectori;
+ foreach (explode(',', $object['recurrence']['BYMONTH']) as $month)
+ $bymonth->push(intval($month));
+ $rr->setBymonth($bymonth);
+ }
+
+ if ($object['recurrence']['COUNT'])
+ $rr->setCount(intval($object['recurrence']['COUNT']));
+ else if ($object['recurrence']['UNTIL'])
+ $rr->setEnd(self::get_datetime($object['recurrence']['UNTIL'], null, true));
+
+ if ($rr->isValid()) {
+ $this->obj->setRecurrenceRule($rr);
+
+ // add exception dates (only if recurrence rule is valid)
+ $exdates = new vectordatetime;
+ foreach ((array)$object['recurrence']['EXDATE'] as $exdate)
+ $exdates->push(self::get_datetime($exdate, null, true));
+ $this->obj->setExceptionDates($exdates);
+ }
+ else {
+ raise_error(array(
+ 'code' => 600, 'type' => 'php',
+ 'file' => __FILE__, 'line' => __LINE__,
+ 'message' => "Invalid event recurrence rule: " . json_encode($object['recurrence']),
+ ), true);
+ }
+ }
+
+ // save alarm
+ $valarms = new vectoralarm;
+ if ($object['alarms']) {
+ list($offset, $type) = explode(":", $object['alarms']);
+
+ if ($type == 'EMAIL') { // email alarms implicitly go to event owner
+ $recipients = new vectorcontactref;
+ $recipients->push(new ContactReference(ContactReference::EmailReference, $object['_owner']));
+ $alarm = new Alarm($object['title'], strval($object['description']), $recipients);
+ }
+ else { // default: display alarm
+ $alarm = new Alarm($object['title']);
+ }
+
+ if (preg_match('/^@(\d+)/', $offset, $d)) {
+ $alarm->setStart(self::get_datetime($d[1], new DateTimeZone('UTC')));
+ }
+ else if (preg_match('/^([-+]?)(\d+)([SMHDW])/', $offset, $d)) {
+ $days = $hours = $minutes = $seconds = 0;
+ switch ($d[3]) {
+ case 'W': $days = 7*intval($d[2]); break;
+ case 'D': $days = intval($d[2]); break;
+ case 'H': $hours = intval($d[2]); break;
+ case 'M': $minutes = intval($d[2]); break;
+ case 'S': $seconds = intval($d[2]); break;
+ }
+ $alarm->setRelativeStart(new Duration($days, $hours, $minutes, $seconds, $d[1] == '-'), $d[1] == '-' ? kolabformat::Start : kolabformat::End);
+ }
+
+ $valarms->push($alarm);
+ }
+ $this->obj->setAlarms($valarms);
+ }
+
+}
\ No newline at end of file