From b2a7734c34fd5fbe290e2b9517dcb088216d2a4a Mon Sep 17 00:00:00 2001 From: Thomas Bruederli Date: Wed, 30 May 2012 08:51:55 +0200 Subject: [PATCH] Little refactoring: new parent class for xcal based Kolab objects such as event and task handling reading/writing of common properties --- plugins/libkolab/lib/kolab_format_contact.php | 2 +- .../lib/kolab_format_distributionlist.php | 2 +- plugins/libkolab/lib/kolab_format_event.php | 317 +-------------- plugins/libkolab/lib/kolab_format_task.php | 109 +++--- plugins/libkolab/lib/kolab_format_xcal.php | 361 ++++++++++++++++++ 5 files changed, 435 insertions(+), 356 deletions(-) create mode 100644 plugins/libkolab/lib/kolab_format_xcal.php 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