plugins/libkolab

Thomas Brüderli bruederli at kolabsys.com
Wed May 30 08:52:41 CEST 2012


 plugins/libkolab/lib/kolab_format_contact.php          |    2 
 plugins/libkolab/lib/kolab_format_distributionlist.php |    2 
 plugins/libkolab/lib/kolab_format_event.php            |  317 --------------
 plugins/libkolab/lib/kolab_format_task.php             |   91 ++--
 plugins/libkolab/lib/kolab_format_xcal.php             |  361 +++++++++++++++++
 5 files changed, 426 insertions(+), 347 deletions(-)

New commits:
commit b2a7734c34fd5fbe290e2b9517dcb088216d2a4a
Author: Thomas Bruederli <thomas at roundcube.net>
Date:   Wed May 30 08:51:55 2012 +0200

    Little refactoring: new parent class for xcal based Kolab objects such as event and task handling reading/writing of common properties

diff --git a/plugins/libkolab/lib/kolab_format_contact.php b/plugins/libkolab/lib/kolab_format_contact.php
index d2634a0..358e1b1 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 3931c61..e8fae76 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 aec4e8d..6af8f0d 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 <http://www.gnu.org/licenses/>.
  */
 
-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 cf89b53..ff790cf 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 <http://www.gnu.org/licenses/>.
  */
 
-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);
+
+        $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']));
 
-        // TODO: set object propeties
+        $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;
@@ -71,21 +74,6 @@ class kolab_format_task extends kolab_format
     }
 
     /**
-     * Load data from old Kolab2 format
-     */
-    public function fromkolab2($record)
-    {
-        $object = array(
-            'uid'     => $record['uid'],
-            'changed' => $record['last-modification-date'],
-        );
-
-        // TODO: implement this
-
-        $this->data = $object;
-    }
-
-    /**
      * Convert the Configuration object into a hash array data structure
      *
      * @return array  Config object data as hash array
@@ -98,27 +86,25 @@ class kolab_format_task extends kolab_format
 
         $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(),
-        );
+        // read common xcal props
+        $object = parent::to_array();
 
-        // if due date is set
-        if ($dtstart = $this->obj->start()) {
+        $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()) {
+        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
 
@@ -126,4 +112,19 @@ class kolab_format_task extends kolab_format
         return $this->data;
     }
 
+    /**
+     * Load data from old Kolab2 format
+     */
+    public function fromkolab2($record)
+    {
+        $object = array(
+            'uid'     => $record['uid'],
+            'changed' => $record['last-modification-date'],
+        );
+
+        // TODO: implement this
+
+        $this->data = $object;
+    }
+
 }
diff --git a/plugins/libkolab/lib/kolab_format_xcal.php b/plugins/libkolab/lib/kolab_format_xcal.php
new file mode 100644
index 0000000..1eb2acf
--- /dev/null
+++ b/plugins/libkolab/lib/kolab_format_xcal.php
@@ -0,0 +1,361 @@
+<?php
+
+/**
+ * Xcal based Kolab format class wrapping libkolabxml bindings
+ *
+ * Base class for xcal-based Kolab groupware objects such as event, todo, journal
+ *
+ * @version @package_version@
+ * @author Thomas Bruederli <bruederli at kolabsys.com>
+ *
+ * Copyright (C) 2012, Kolab Systems AG <contact at kolabsys.com>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+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





More information about the commits mailing list