Start new Itip library.
authorGunnar Wrobel <wrobel@temple.(none)>
Wed, 4 Aug 2010 13:58:44 +0000 (15:58 +0200)
committerGunnar Wrobel <p@rdus.de>
Wed, 25 Aug 2010 17:21:38 +0000 (19:21 +0200)
13 files changed:
framework/Itip/TODO [new file with mode: 0644]
framework/Itip/lib/Horde/Itip.php [new file with mode: 0644]
framework/Itip/lib/Horde/Itip/Event.php [new file with mode: 0644]
framework/Itip/lib/Horde/Itip/Event/Vevent.php [new file with mode: 0644]
framework/Itip/lib/Horde/Itip/Exception.php [new file with mode: 0644]
framework/Itip/lib/Horde/Itip/Resource.php [new file with mode: 0644]
framework/Itip/lib/Horde/Itip/Resource/Base.php [new file with mode: 0644]
framework/Itip/lib/Horde/Itip/Response.php [new file with mode: 0644]
framework/Itip/lib/Horde/Itip/Response/Type.php [new file with mode: 0644]
framework/Itip/lib/Horde/Itip/Response/Type/Accept.php [new file with mode: 0644]
framework/Itip/lib/Horde/Itip/Response/Type/Base.php [new file with mode: 0644]
framework/Itip/lib/Horde/Itip/Response/Type/Decline.php [new file with mode: 0644]
framework/Itip/lib/Horde/Itip/Response/Type/Tentative.php [new file with mode: 0644]

diff --git a/framework/Itip/TODO b/framework/Itip/TODO
new file mode 100644 (file)
index 0000000..e60de1a
--- /dev/null
@@ -0,0 +1,3 @@
+
+ - Merge with Horde Imp
+ - Complete unit testing
diff --git a/framework/Itip/lib/Horde/Itip.php b/framework/Itip/lib/Horde/Itip.php
new file mode 100644 (file)
index 0000000..2e13a4d
--- /dev/null
@@ -0,0 +1,141 @@
+<?php
+/**
+ * Handles iTip invitation requests/responses.
+ *
+ * PHP version 5
+ *
+ * @category Horde
+ * @package  Itip
+ * @author   Gunnar Wrobel <wrobel@pardus.de>
+ * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link     http://pear.horde.org/index.php?package=Itip
+ */
+
+/**
+ * Handles iTip invitation requests/responses.
+ *
+ * Copyright 2010 Klarälvdalens Datakonsult AB
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you did not
+ * receive this file, see
+ * http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+ *
+ * @category Horde
+ * @package  Itip
+ * @author   Gunnar Wrobel <wrobel@pardus.de>
+ * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link     http://pear.horde.org/index.php?package=Itip
+ */
+class Horde_Itip
+{
+    /**
+     * The iTip response.
+     *
+     * @var Horde_Itip_Response
+     */
+    private $_response;
+
+    /**
+     * Constructor.
+     *
+     * @param Horde_Itip_Response $response The iTip response.
+     */
+    public function __construct(
+        Horde_Itip_Response $response
+    ) {
+        $this->_response = $response;
+    }
+
+    /**
+     * Return the response as an iCalendar vEvent object.
+     *
+     * @param Horde_Itip_Response_Type $type The response type.
+     *
+     * @return Horde_iCalendar_vevent The response object.
+     */
+    public function getVeventResponse(
+        Horde_Itip_Response_Type $type
+    ) {
+        return $this->_response->getVevent(
+            $type, false
+        );
+    }
+
+    /**
+     * Return the response as an iCalendar object.
+     *
+     * @param Horde_Itip_Response_Type $type       The response type.
+     * @param string                   $product_id The ID that should be set as
+     *                                             the iCalendar product id.
+     *
+     * @return Horde_iCalendar The response object.
+     */
+    public function getIcalendarResponse(
+        Horde_Itip_Response_Type $type,
+        $product_id
+    ) {
+        return $this->_response->getIcalendar(
+            $type, $product_id
+        );
+    }
+
+    /**
+     * Return the response as a MIME message.
+     *
+     * @param Horde_Itip_Response_Type $type            The response type.
+     * @param string                   $product_id      The ID that should be set
+     *                                                  as the iCalendar product
+     *                                                  id.
+     * @param string                   $subject_comment An optional comment on
+     *                                                  the subject line.
+     *
+     * @return array A list of two object: The mime headers and the mime
+     *               message.
+     */
+    public function getMessageResponse(
+        Horde_Itip_Response_Type $type,
+        $product_id,
+        $subject_comment = null
+    ) {
+        return $this->_response->getMessage(
+            $type, $product_id, $subject_comment
+        );
+    }
+
+    /**
+     * Factory for generating a response object for an iCalendar invitation.
+     *
+     * @param Horde_iCalendar_vevent $vevent   The iCalendar request.
+     * @param Horde_Itip_Resource    $resource The invited resource.
+     *
+     * @return Horde_Itip_Response The prepared response.
+     */
+    static public function prepareResponse(
+        Horde_iCalendar_vevent $vevent,
+        Horde_Itip_Resource $resource
+    ) {
+        return new Horde_Itip_Response(
+            new Horde_Itip_Event_Vevent(
+                $vevent
+            ),
+            $resource
+        );
+    }
+
+    /**
+     * Factory for generating an iTip handler for an iCalendar invitation.
+     *
+     * @param Horde_iCalendar_vevent $vevent   The iCalendar request.
+     * @param Horde_Itip_Resource    $resource The invited resource.
+     *
+     * @return Horde_Itip The iTip handler.
+     */
+    static public function factory(
+        Horde_iCalendar_vevent $vevent,
+        Horde_Itip_Resource $resource
+    ) {
+        return new Horde_Itip(
+            self::prepareResponse($vevent, $resource)
+        );
+    }
+}
\ No newline at end of file
diff --git a/framework/Itip/lib/Horde/Itip/Event.php b/framework/Itip/lib/Horde/Itip/Event.php
new file mode 100644 (file)
index 0000000..c11b61d
--- /dev/null
@@ -0,0 +1,103 @@
+<?php
+/**
+ * Defines the event interface required for iTip-Handling / resource booking.
+ *
+ * PHP version 5
+ *
+ * @category Horde
+ * @package  Itip
+ * @author   Gunnar Wrobel <wrobel@pardus.de>
+ * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link     http://pear.horde.org/index.php?package=Itip
+ */
+
+/**
+ * Defines the event interface required for iTip-Handling / resource booking.
+ *
+ * Copyright 2010 Klarälvdalens Datakonsult AB
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you did not
+ * receive this file, see
+ * http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+ *
+ * @category Horde
+ * @package  Itip
+ * @author   Gunnar Wrobel <wrobel@pardus.de>
+ * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link     http://pear.horde.org/index.php?package=Itip
+ */
+interface Horde_Itip_Event
+{
+    /**
+     * Returns the event as vEvent.
+     *
+     * @return Horde_iCalendar_vevent The wrapped event.
+     */
+    public function getVevent();
+
+    /**
+     * Return the method of the iTip request.
+     *
+     * @return string The method of the request.
+     */
+    public function getMethod();
+
+    /**
+     * Return the uid of the iTip event.
+     *
+     * @return string The uid of the event.
+     */
+    public function getUid();
+
+    /**
+     * Return the summary for the event.
+     *
+     * @return string The summary.
+     */
+    public function getSummary();
+
+    /**
+     * Return the start of the iTip event.
+     *
+     * @return string The start of the event.
+     */
+    public function getStart();
+
+    /**
+     * Return the end of the iTip event.
+     *
+     * @return string The end of the event.
+     */
+    public function getEnd();
+
+    /**
+     * Return the organizer of the iTip event.
+     *
+     * @return string The organizer of the event.
+     */
+    public function getOrganizer();
+
+    /**
+     * Copy the details from an event into this one.
+     *
+     * @param Horde_Itip_Event $event The event to copy from.
+     *
+     * @return NULL
+     */
+    public function copyEventInto(Horde_Itip_Event $event);
+
+    /**
+     * Set the attendee parameters.
+     *
+     * @param string $attendee    The mail address of the attendee.
+     * @param string $common_name Common name of the attendee.
+     * @param string $status      Attendee status (ACCPETED, DECLINED, TENTATIVE)
+     *
+     * @return NULL
+     */
+    public function setAttendee($attendee, $common_name, $status);
+
+    public function getKolabObject();
+
+    public function setAccepted($resource);
+}
\ No newline at end of file
diff --git a/framework/Itip/lib/Horde/Itip/Event/Vevent.php b/framework/Itip/lib/Horde/Itip/Event/Vevent.php
new file mode 100644 (file)
index 0000000..f5741b0
--- /dev/null
@@ -0,0 +1,615 @@
+<?php
+/**
+ * A wrapper for vEvent iCalender data.
+ *
+ * PHP version 5
+ *
+ * @category Kolab
+ * @package  Kolab_Filter
+ * @author   Gunnar Wrobel <wrobel@pardus.de>
+ * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link     http://pear.horde.org/index.php?package=Kolab_Server
+ */
+
+/**
+ * A wrapper for vEvent iCalender data.
+ *
+ * Copyright 2010 Klarälvdalens Datakonsult AB
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you did not
+ * receive this file, see
+ * http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+ *
+ * @category Kolab
+ * @package  Kolab_Filter
+ * @author   Gunnar Wrobel <wrobel@pardus.de>
+ * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link     http://pear.horde.org/index.php?package=Kolab_Server
+ */
+class Horde_Itip_Event_Vevent
+implements Horde_Itip_Event
+{
+    /**
+     * The wrapped vEvent.
+     *
+     * @var Horde_iCalendar_vevent
+     */
+    private $_vevent;
+
+    /**
+     * Constructor.
+     *
+     * @param Horde_iCalendar_vevent $vevent The iCalendar object that will be
+     *                                       wrapped by this instance.
+     */
+    public function __construct(Horde_iCalendar_vevent $vevent)
+    {
+        $this->_vevent = $vevent;
+    }
+
+    /**
+     * Returns the wrapped vEvent.
+     *
+     * @return Horde_iCalendar_vevent The wrapped event.
+     */
+    public function getVevent()
+    {
+        return $this->_vevent;
+    }
+
+    /**
+     * Return the method of the iTip request.
+     *
+     * @return string The method of the request.
+     */
+    public function getMethod()
+    {
+        return $this->_vevent->getAttributeDefault('METHOD', 'REQUEST');
+    }
+
+    /**
+     * Return the uid of the iTip event.
+     *
+     * @return string The uid of the event.
+     */
+    public function getUid()
+    {
+        return $this->_vevent->getAttributeDefault('UID', '');
+    }
+
+    /**
+     * Return the summary for the event.
+     *
+     * @return string|PEAR_Error The summary.
+     */
+    public function getSummary()
+    {
+        return $this->_vevent->getAttributeDefault('SUMMARY', _("No summary available"));
+    }
+
+    /**
+     * Return the start of the iTip event.
+     *
+     * @return string The start of the event.
+     */
+    public function getStart()
+    {
+        return $this->_vevent->getAttributeDefault('DTSTART', 0);
+    }
+
+    /**
+     * Return the end of the iTip event.
+     *
+     * @return string The end of the event.
+     */
+    public function getEnd()
+    {
+        return $this->_vevent->getAttributeDefault('DTEND', 0);
+    }
+
+    /**
+     * Return the organizer of the iTip event.
+     *
+     * @return string The organizer of the event.
+     */
+    public function getOrganizer()
+    {
+        return preg_replace('/^mailto:\s*/i', '', $this->_vevent->getAttributeDefault('ORGANIZER', ''));
+    }
+
+    /**
+     * Copy the details from an event into this one.
+     *
+     * @param Horde_Itip_Event $event The event to copy from.
+     *
+     * @return NULL
+     */
+    public function copyEventInto(Horde_Itip_Event $event)
+    {
+        $this->copyUid($event);
+        $this->copySummary($event);
+        $this->copyDescription($event);
+        $this->copyStart($event);
+        $this->copyEndOrDuration($event);
+        $this->copySequence($event);
+        $this->copyLocation($event);
+        $this->copyOrganizer($event);
+    }
+
+    /**
+     * Set the attendee parameters.
+     *
+     * @param string $attendee    The mail address of the attendee.
+     * @param string $common_name Common name of the attendee.
+     * @param string $status      Attendee status (ACCPETED, DECLINED, TENTATIVE)
+     *
+     * @return NULL
+     */
+    public function setAttendee($attendee, $common_name, $status)
+    {
+        $this->_vevent->setAttribute(
+            'ATTENDEE',
+            'MAILTO:' . $attendee,
+            array(
+                'CN' => $common_name,
+                'PARTSTAT' => $status
+            )
+        );
+    }
+
+    public function getKolabObject()
+    {
+        $object = array();
+        $object['uid'] = $this->getUid();
+
+        $org_params = $this->_vevent->getAttribute('ORGANIZER', true);
+        if (!is_a( $org_params, 'PEAR_Error')) {
+            if (!empty($org_params[0]['CN'])) {
+                $object['organizer']['display-name'] = $org_params[0]['CN'];
+            }
+            $orgemail = $this->_vevent->getAttributeDefault('ORGANIZER', '');
+            if (preg_match('/mailto:(.*)/i', $orgemail, $regs )) {
+                $orgemail = $regs[1];
+            }
+            $object['organizer']['smtp-address'] = $orgemail;
+        }
+        $object['summary'] = $this->_vevent->getAttributeDefault('SUMMARY', '');
+        $object['location'] = $this->_vevent->getAttributeDefault('LOCATION', '');
+        $object['body'] = $this->_vevent->getAttributeDefault('DESCRIPTION', '');
+        $dtend = $this->_vevent->getAttributeDefault('DTEND', '');
+        if (is_array($dtend)) {
+            $object['_is_all_day'] = true;
+        }
+        $start = new Horde_Kolab_Resource_Epoch($this->getStart());
+        $object['start-date'] = $start->getEpoch();
+        $end = new Horde_Kolab_Resource_Epoch($dtend);
+        $object['end-date'] = $end->getEpoch();
+
+        $attendees = $this->_vevent->getAttribute('ATTENDEE');
+        if (!is_a( $attendees, 'PEAR_Error')) {
+            $attendees_params = $this->_vevent->getAttribute('ATTENDEE', true);
+            if (!is_array($attendees)) {
+                $attendees = array($attendees);
+            }
+            if (!is_array($attendees_params)) {
+                $attendees_params = array($attendees_params);
+            }
+
+            $object['attendee'] = array();
+            for ($i = 0; $i < count($attendees); $i++) {
+                $attendee = array();
+                if (isset($attendees_params[$i]['CN'])) {
+                    $attendee['display-name'] = $attendees_params[$i]['CN'];
+                }
+
+                $attendeeemail = $attendees[$i];
+                if (preg_match('/mailto:(.*)/i', $attendeeemail, $regs)) {
+                    $attendeeemail = $regs[1];
+                }
+                $attendee['smtp-address'] = $attendeeemail;
+
+                if (!isset($attendees_params[$i]['RSVP'])
+                    || $attendees_params[$i]['RSVP'] == 'FALSE') {
+                    $attendee['request-response'] = false;
+                } else {
+                    $attendee['request-response'] = true;
+                }
+
+                if (isset($attendees_params[$i]['ROLE'])) {
+                    $attendee['role'] = $attendees_params[$i]['ROLE'];
+                }
+
+                if (isset($attendees_params[$i]['PARTSTAT'])) {
+                    $status = strtolower($attendees_params[$i]['PARTSTAT']);
+                    switch ($status) {
+                    case 'needs-action':
+                    case 'delegated':
+                        $attendee['status'] = 'none';
+                        break;
+                    default:
+                        $attendee['status'] = $status;
+                        break;
+                    }
+                }
+
+                $object['attendee'][] = $attendee;
+            }
+        }
+
+        // Alarm
+        $valarm = $this->_vevent->findComponent('VALARM');
+        if ($valarm) {
+            $trigger = $valarm->getAttribute('TRIGGER');
+            if (!is_a($trigger, 'PEAR_Error')) {
+                $p = $valarm->getAttribute('TRIGGER', true);
+                if ($trigger < 0) {
+                    // All OK, enter the alarm into the XML
+                    // NOTE: The Kolab XML format seems underspecified
+                    // wrt. alarms currently...
+                    $object['alarm'] = -$trigger / 60;
+                }
+            }
+        }
+
+        // Recurrence
+        $rrule_str = $this->_vevent->getAttribute('RRULE');
+        if (!is_a($rrule_str, 'PEAR_Error')) {
+            require_once 'Horde/Date/Recurrence.php';
+            $recurrence = new Horde_Date_Recurrence(time());
+            $recurrence->fromRRule20($rrule_str);
+            $object['recurrence'] = $recurrence->toHash();
+        }
+
+        return $object;
+    }
+
+    public function setAccepted($resource)
+    {
+        // Update our status within the iTip request and send the reply
+        $this->_vevent->setAttribute('STATUS', 'CONFIRMED', array(), false);
+        $attendees = $this->_vevent->getAttribute('ATTENDEE');
+        if (!is_array($attendees)) {
+            $attendees = array($attendees);
+        }
+        $attparams = $this->_vevent->getAttribute('ATTENDEE', true);
+        foreach ($attendees as $i => $attendee) {
+            $attendee = preg_replace('/^mailto:\s*/i', '', $attendee);
+            if ($attendee != $resource) {
+                continue;
+            }
+
+            $attparams[$i]['PARTSTAT'] = 'ACCEPTED';
+            if (array_key_exists('RSVP', $attparams[$i])) {
+                unset($attparams[$i]['RSVP']);
+            }
+        }
+
+        // Re-add all the attendees to the event, using our updates status info
+        $firstatt = array_pop($attendees);
+        $firstattparams = array_pop($attparams);
+        $this->_vevent->setAttribute('ATTENDEE', $firstatt, $firstattparams, false);
+        foreach ($attendees as $i => $attendee) {
+            $this->_vevent->setAttribute('ATTENDEE', $attendee, $attparams[$i]);
+        }
+    }
+
+    /**
+     * Set the uid of the iTip event.
+     *
+     * @param string $uid The uid of the event.
+     *
+     * @return NULL
+     */
+    private function setUid($uid)
+    {
+        $this->_vevent->setAttribute('UID', $uid);
+    }
+
+    /**
+     * Copy the uid from the request into the provided iTip instance.
+     *
+     * @return NULL
+     */
+    private function copyUid(Horde_Itip_Event $itip)
+    {
+        $itip->setUid($this->getUid());
+    }
+
+    /**
+     * Set the summary for the event.
+     *
+     * @param string $summary The summary.
+     *
+     * @return NULL
+     */
+    private function setSummary($summary)
+    {
+        $this->_vevent->setAttribute('SUMMARY', $summary);
+    }
+
+    /**
+     * Copy the summary from the request into the provided iTip instance.
+     *
+     * @return NULL
+     */
+    private function copySummary(Horde_Itip_Event $itip)
+    {
+        $itip->setSummary($this->getSummary());
+    }
+
+    /**
+     * Does the event have a description?
+     *
+     * @return boolean True if it has a description, false otherwise.
+     */
+    private function hasDescription()
+    {
+        return !($this->_vevent->getAttribute('DESCRIPTION') instanceOf PEAR_Error);
+    }
+
+    /**
+     * Return the description for the event.
+     *
+     * @return string|PEAR_Error The description.
+     */
+    private function getDescription()
+    {
+        return $this->_vevent->getAttribute('DESCRIPTION');
+    }
+
+    /**
+     * Set the description for the event.
+     *
+     * @param string $description The description.
+     *
+     * @return NULL
+     */
+    private function setDescription($description)
+    {
+        $this->_vevent->setAttribute('DESCRIPTION', $description);
+    }
+
+    /**
+     * Copy the description from the request into the provided iTip instance.
+     *
+     * @return NULL
+     */
+    private function copyDescription(Horde_Itip_Event $itip)
+    {
+        if ($this->hasDescription()) {
+            $itip->setDescription($this->getDescription());
+        }
+    }
+
+    /**
+     * Return the start parameters of the iTip event.
+     *
+     * @return array The start parameters of the event.
+     */
+    public function getStartParameters()
+    {
+        return array_pop($this->_vevent->getAttribute('DTSTART', true));
+    }
+
+    /**
+     * Set the start of the iTip event.
+     *
+     * @param string $start      The start of the event.
+     * @param array  $parameters Additional parameters.
+     *
+     * @return NULL
+     */
+    private function setStart($start, $parameters)
+    {
+        $this->_vevent->setAttribute('DTSTART', $start, $parameters);
+    }
+
+    /**
+     * Copy the start time from the request into the provided iTip instance.
+     *
+     * @return NULL
+     */
+    private function copyStart(Horde_Itip_Event $itip)
+    {
+        $itip->setStart($this->getStart(), $this->getStartParameters());
+    }
+
+    /**
+     * Does the event have an end?
+     *
+     * @return boolean True if it has an end, false otherwise.
+     */
+    private function hasEnd()
+    {
+        return !($this->_vevent->getAttribute('DTEND') instanceOf PEAR_Error);
+    }
+
+    /**
+     * Return the end parameters of the iTip event.
+     *
+     * @return array The end parameters of the event.
+     */
+    private function getEndParameters()
+    {
+        return array_pop($this->_vevent->getAttribute('DTEND', true));
+    }
+
+    /**
+     * Set the end of the iTip event.
+     *
+     * @param string $end        The end of the event.
+     * @param array  $parameters Additional parameters.
+     *
+     * @return NULL
+     */
+    private function setEnd($end, $parameters)
+    {
+        $this->_vevent->setAttribute('DTEND', $end, $parameters);
+    }
+
+    /**
+     * Return the duration for the event.
+     *
+     * @return string|PEAR_Error The duration of the event.
+     */
+    private function getDuration()
+    {
+        return $this->_vevent->getAttribute('DURATION');
+    }
+
+    /**
+     * Return the duration parameters of the iTip event.
+     *
+     * @return array The duration parameters of the event.
+     */
+    private function getDurationParameters()
+    {
+        return array_pop($this->_vevent->getAttribute('DURATION', true));
+    }
+
+    /**
+     * Set the duration of the iTip event.
+     *
+     * @param string $duration   The duration of the event.
+     * @param array  $parameters Additional parameters.
+     *
+     * @return NULL
+     */
+    private function setDuration($duration, $parameters)
+    {
+        $this->_vevent->setAttribute('DURATION', $duration, $parameters);
+    }
+
+    /**
+     * Copy the end time or event duration from the request into the provided
+     * iTip instance.
+     *
+     * @return NULL
+     */
+    private function copyEndOrDuration(Horde_Itip_Event $itip)
+    {
+        if ($this->hasEnd()) {
+            $itip->setEnd($this->getEnd(), $this->getEndParameters());
+        } else {
+            $itip->setDuration($this->getDuration(), $this->getDurationParameters());
+        }
+    }
+
+    /**
+     * Return the sequence for the event.
+     *
+     * @return string|PEAR_Error The sequence.
+     */
+    private function getSequence()
+    {
+        return $this->_vevent->getAttributeDefault('SEQUENCE', 0);
+    }
+
+    /**
+     * Set the sequence for the event.
+     *
+     * @param string $sequence The sequence.
+     *
+     * @return NULL
+     */
+    private function setSequence($sequence)
+    {
+        $this->_vevent->setAttribute('SEQUENCE', $sequence);
+    }
+    /**
+     * Copy the sequence from the request into the provided iTip instance.
+     *
+     * @return NULL
+     */
+    private function copySequence(Horde_Itip_Event $itip)
+    {
+        $itip->setSequence($this->getSequence());
+    }
+
+    /**
+     * Does the event have a location?
+     *
+     * @return boolean True if it has a location, false otherwise.
+     */
+    private function hasLocation()
+    {
+        return !($this->_vevent->getAttribute('LOCATION') instanceOf PEAR_Error);
+    }
+
+    /**
+     * Return the location for the event.
+     *
+     * @return string|PEAR_Error The location.
+     */
+    private function getLocation()
+    {
+        return $this->_vevent->getAttribute('LOCATION');
+    }
+
+    /**
+     * Set the location for the event.
+     *
+     * @param string $location The location.
+     *
+     * @return NULL
+     */
+    private function setLocation($location)
+    {
+        $this->_vevent->setAttribute('LOCATION', $location);
+    }
+
+    /**
+     * Copy the location from the request into the provided iTip instance.
+     *
+     * @return NULL
+     */
+    private function copyLocation(Horde_Itip_Event $itip)
+    {
+        if ($this->hasLocation()) {
+            $itip->setLocation($this->getLocation());
+        }
+    }
+
+    /**
+     * Return the organizer for the event.
+     *
+     * @return string|PEAR_Error The organizer of the event.
+     */
+    private function getRawOrganizer()
+    {
+        return $this->_vevent->getAttribute('ORGANIZER');
+    }
+
+    /**
+     * Return the organizer parameters of the iTip event.
+     *
+     * @return array The organizer parameters of the event.
+     */
+    private function getOrganizerParameters()
+    {
+        return array_pop($this->_vevent->getAttribute('ORGANIZER', true));
+    }
+
+    /**
+     * Set the organizer of the iTip event.
+     *
+     * @param string $organizer  The organizer of the event.
+     * @param array  $parameters Additional parameters.
+     *
+     * @return NULL
+     */
+    private function setOrganizer($organizer, $parameters)
+    {
+        $this->_vevent->setAttribute('ORGANIZER', $organizer, $parameters);
+    }
+
+    /**
+     * Copy the organizer from the request into the provided iTip instance.
+     *
+     * @return NULL
+     */
+    private function copyOrganizer(Horde_Itip_Event $itip)
+    {
+        $itip->setOrganizer($this->getRawOrganizer(), $this->getOrganizerParameters());
+    }
+}
\ No newline at end of file
diff --git a/framework/Itip/lib/Horde/Itip/Exception.php b/framework/Itip/lib/Horde/Itip/Exception.php
new file mode 100644 (file)
index 0000000..9c82e41
--- /dev/null
@@ -0,0 +1,30 @@
+<?php
+/**
+ * This class provides the standard error class for Horde_Itip exceptions.
+ *
+ * PHP version 5
+ *
+ * @category Horde
+ * @package  Itip
+ * @author   Gunnar Wrobel <wrobel@pardus.de>
+ * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link     http://pear.horde.org/index.php?package=Itip
+ */
+
+/**
+ * This class provides the standard error class for Horde_Itip exceptions.
+ *
+ * Copyright 2010 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
+ *
+ * @category Horde
+ * @package  Itip
+ * @author   Gunnar Wrobel <wrobel@pardus.de>
+ * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link     http://pear.horde.org/index.php?package=Itip
+ */
+class Horde_Itip_Exception extends Exception
+{
+}
diff --git a/framework/Itip/lib/Horde/Itip/Resource.php b/framework/Itip/lib/Horde/Itip/Resource.php
new file mode 100644 (file)
index 0000000..e10e642
--- /dev/null
@@ -0,0 +1,51 @@
+<?php
+/**
+ * Provides information about an invited resource.
+ *
+ * PHP version 5
+ *
+ * @category Horde
+ * @package  Itip
+ * @author   Gunnar Wrobel <wrobel@pardus.de>
+ * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link     http://pear.horde.org/index.php?package=Itip
+ */
+
+/**
+ * Provides information about an invited resource.
+ *
+ * Copyright 2010 Kolab Systems AG
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you did not
+ * receive this file, see
+ * http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+ *
+ * @category Horde
+ * @package  Itip
+ * @author   Gunnar Wrobel <wrobel@pardus.de>
+ * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link     http://pear.horde.org/index.php?package=Itip
+ */
+interface Horde_Itip_Resource
+{
+    /**
+     * Retrieve the mail address of the resource.
+     *
+     * @return string The mail address.
+     */
+    public function getMailAddress();
+
+    /**
+     * Retrieve the common name of the resource.
+     *
+     * @return string The common name.
+     */
+    public function getCommonName();
+
+    /**
+     * Retrieve the "From" address for this resource.
+     *
+     * @return string The "From" address.
+     */
+    public function getFrom();
+}
\ No newline at end of file
diff --git a/framework/Itip/lib/Horde/Itip/Resource/Base.php b/framework/Itip/lib/Horde/Itip/Resource/Base.php
new file mode 100644 (file)
index 0000000..8a378fa
--- /dev/null
@@ -0,0 +1,87 @@
+<?php
+/**
+ * Simple information provider for an invited resource.
+ *
+ * PHP version 5
+ *
+ * @category Horde
+ * @package  Itip
+ * @author   Gunnar Wrobel <wrobel@pardus.de>
+ * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link     http://pear.horde.org/index.php?package=Itip
+ */
+
+/**
+ * Simple information provider for an invited resource.
+ *
+ * Copyright 2010 Kolab Systems AG
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you did not
+ * receive this file, see
+ * http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+ *
+ * @category Horde
+ * @package  Itip
+ * @author   Gunnar Wrobel <wrobel@pardus.de>
+ * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link     http://pear.horde.org/index.php?package=Itip
+ */
+class Horde_Itip_Resource_Base
+implements Horde_Itip_Resource
+{
+    /**
+     * The mail address.
+     *
+     * @var string
+     */
+    private $_mail;
+
+    /**
+     * The common name.
+     *
+     * @var string
+     */
+    private $_common_name;
+
+    /**
+     * Constructor.
+     *
+     * @param string $mail        The mail address.
+     * @param string $common_name The common name.
+     */
+    public function __construct($mail, $common_name)
+    {
+        $this->_mail        = $mail;
+        $this->_common_name = $common_name;
+    }
+
+    /**
+     * Retrieve the mail address of the resource.
+     *
+     * @return string The mail address.
+     */
+    public function getMailAddress()
+    {
+        return $this->_mail;
+    }
+
+    /**
+     * Retrieve the common name of the resource.
+     *
+     * @return string The common name.
+     */
+    public function getCommonName()
+    {
+        return $this->_common_name;
+    }
+
+    /**
+     * Retrieve the "From" address for this resource.
+     *
+     * @return string The "From" address.
+     */
+    public function getFrom()
+    {
+        return sprintf("%s <%s>", $this->_common_name, $this->_mail);
+    }
+}
\ No newline at end of file
diff --git a/framework/Itip/lib/Horde/Itip/Response.php b/framework/Itip/lib/Horde/Itip/Response.php
new file mode 100644 (file)
index 0000000..3fea90c
--- /dev/null
@@ -0,0 +1,172 @@
+<?php
+/**
+ * Handles Itip response data.
+ *
+ * PHP version 5
+ *
+ * @category Horde
+ * @package  Itip
+ * @author   Steffen Hansen <steffen@klaralvdalens-datakonsult.se>
+ * @author   Gunnar Wrobel <wrobel@pardus.de>
+ * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link     http://pear.horde.org/index.php?package=Itip
+ */
+
+/**
+ * Handles Itip response data.
+ *
+ * Copyright 2004-2010 Klarälvdalens Datakonsult AB
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you did not
+ * receive this file, see
+ * http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+ *
+ * @category Horde
+ * @package  Itip
+ * @author   Steffen Hansen <steffen@klaralvdalens-datakonsult.se>
+ * @author   Gunnar Wrobel <wrobel@pardus.de>
+ * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link     http://pear.horde.org/index.php?package=Itip
+ */
+class Horde_Itip_Response
+{
+    /**
+     * The request we are going to answer.
+     *
+     * @var Horde_Itip_Event
+     */
+    private $_request;
+
+    /**
+     * The requested resource.
+     *
+     * @var Horde_Itip_Resource
+     */
+    private $_resource;
+
+    /**
+     * The status type of this response.
+     *
+     * @var Horde_Itip_Response_Type
+     */
+    private $_type;
+
+    /**
+     * Constructor.
+     *
+     * @param Horde_Itip_Event    $request  The request this
+     *                                                     instance will respond
+     *                                                     to.
+     * @param Horde_Itip_Resource $resource The requested
+     *                                                     resource.
+     */
+    public function __construct(
+        Horde_Itip_Event $request,
+        Horde_Itip_Resource $resource
+    ) {
+        $this->_request  = $request;
+        $this->_resource = $resource;
+    }
+
+    /**
+     * Return the response as an iCalendar vveEnt object.
+     *
+     * @param Horde_Itip_Response_Type $type The response type.
+     * @param Horde_iCalendar|boolean  $vCal The parent container or false if not
+     *                                       provided.
+     *
+     * @return Horde_iCalendar_vevent The response object.
+     */
+    public function getVevent(
+        Horde_Itip_Response_Type $type,
+        $vCal = false
+    ) {
+        $itip_reply = new Horde_Itip_Event_Vevent(
+            Horde_iCalendar::newComponent('VEVENT', $vCal)
+        );
+        $this->_request->copyEventInto($itip_reply);
+
+        $type->setRequest($this->_request);
+
+        $itip_reply->setAttendee(
+            $this->_resource->getMailAddress(),
+            $this->_resource->getCommonName(),
+            $type->getStatus()
+        );
+        return $itip_reply->getVevent();
+    }
+
+    /**
+     * Return the response as an iCalendar object.
+     *
+     * @param Horde_Itip_Response_Type $type       The response
+     *                                                            type.
+     * @param string                                  $product_id The ID that
+     *                                                            should be set
+     *                                                            as the iCalendar
+     *                                                            product id.
+     *
+     * @return Horde_iCalendar The response object.
+     */
+    public function getIcalendar(
+        Horde_Itip_Response_Type $type,
+        $product_id
+    ) {
+        $vCal = new Horde_iCalendar();
+        $vCal->setAttribute('PRODID', $product_id);
+        $vCal->setAttribute('METHOD', 'REPLY');
+        $vCal->addComponent($this->getVevent($type, $vCal));
+        return $vCal;
+    }
+
+    /**
+     * Return the response as a MIME message.
+     *
+     * @param Horde_Itip_Response_Type $type       The response
+     *                                                            type.
+     * @param string                                  $product_id The ID that
+     *                                                            should be set
+     *                                                            as the iCalendar
+     *                                                            product id.
+     * @param string                                  $subject_comment An optional comment on the subject line.
+     *
+     * @return array A list of two object: The mime headers and the mime
+     *               message.
+     */
+    public function getMessage(
+        Horde_Itip_Response_Type $type,
+        $product_id,
+        $subject_comment = null
+    ) {
+        $ics = new MIME_Part(
+            'text/calendar',
+            $this->getIcalendar($type, $product_id)->exportvCalendar(),
+            'UTF-8'
+        );
+        $ics->setContentTypeParameter('method', 'REPLY');
+
+        //$mime->addPart($body);
+        //$mime->addPart($ics);
+        // The following was ::convertMimePart($mime). This was removed so that we
+        // send out single-part MIME replies that have the iTip file as the body,
+        // with the correct mime-type header set, etc. The reason we want to do this
+        // is so that Outlook interprets the messages as it does Outlook-generated
+        // responses, i.e. double-clicking a reply will automatically update your
+        // meetings, showing different status icons in the UI, etc.
+        $message = MIME_Message::convertMimePart($ics);
+        $message->setCharset('UTF-8');
+        $message->setTransferEncoding('quoted-printable');
+        $message->transferEncodeContents();
+
+        // Build the reply headers.
+        $headers = new MIME_Headers();
+        $headers->addHeader('Date', date('r'));
+        $headers->addHeader('From', $this->_resource->getFrom());
+        $headers->addHeader('To', $this->_request->getOrganizer());
+        $headers->addHeader(
+            'Subject', $type->getSubject($subject_comment)
+        );
+        $headers->addMIMEHeaders($message);
+        return array($headers, $message);
+    }
+}
\ No newline at end of file
diff --git a/framework/Itip/lib/Horde/Itip/Response/Type.php b/framework/Itip/lib/Horde/Itip/Response/Type.php
new file mode 100644 (file)
index 0000000..9a3ffd5
--- /dev/null
@@ -0,0 +1,65 @@
+<?php
+/**
+ * Marks the response type.
+ *
+ * PHP version 5
+ *
+ * @category Kolab
+ * @package  Kolab_Filter
+ * @author   Gunnar Wrobel <wrobel@pardus.de>
+ * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link     http://pear.horde.org/index.php?package=Kolab_Server
+ */
+
+/**
+ * Marks the response type.
+ *
+ * Copyright 2010 Klarälvdalens Datakonsult AB
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you did not
+ * receive this file, see
+ * http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+ *
+ * @category Kolab
+ * @package  Kolab_Filter
+ * @author   Gunnar Wrobel <wrobel@pardus.de>
+ * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link     http://pear.horde.org/index.php?package=Kolab_Server
+ */
+interface Horde_Itip_Response_Type
+{
+    /**
+     * Return the status of the response.
+     *
+     * @return string The status.
+     */
+    public function getStatus();
+
+    /**
+     * Return the abbreviated subject of the response.
+     *
+     * @return string The short subject.
+     */
+    public function getShortSubject();
+
+    /**
+     * Return the subject of the response.
+     *
+     * @param string $comment An optional comment that should appear in the
+     *                        response subject.
+     *
+     * @return string The subject.
+     */
+    public function getSubject($comment = null);
+
+    /**
+     * Return an additional message for the response.
+     *
+     * @param boolean $is_update Indicates if the request was an update.
+     * @param string  $comment   An optional comment that should appear in the
+     *                           response message.
+     *
+     * @return string The message.
+     */
+    public function getMessage($is_update = false, $comment = null);
+}
\ No newline at end of file
diff --git a/framework/Itip/lib/Horde/Itip/Response/Type/Accept.php b/framework/Itip/lib/Horde/Itip/Response/Type/Accept.php
new file mode 100644 (file)
index 0000000..96a4c92
--- /dev/null
@@ -0,0 +1,65 @@
+<?php
+/**
+ * Indicates an accepted invitation.
+ *
+ * PHP version 5
+ *
+ * @category Kolab
+ * @package  Kolab_Filter
+ * @author   Gunnar Wrobel <wrobel@pardus.de>
+ * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link     http://pear.horde.org/index.php?package=Kolab_Server
+ */
+
+/**
+ * Indicates an accepted invitation.
+ *
+ * Copyright 2010 Klarälvdalens Datakonsult AB
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you did not
+ * receive this file, see
+ * http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+ *
+ * @category Kolab
+ * @package  Kolab_Filter
+ * @author   Gunnar Wrobel <wrobel@pardus.de>
+ * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link     http://pear.horde.org/index.php?package=Kolab_Server
+ */
+class Horde_Itip_Response_Type_Accept
+extends Horde_Itip_Response_Type_Base
+{
+    /**
+     * Return the status of the response.
+     *
+     * @return string The status.
+     */
+    public function getStatus()
+    {
+        return 'ACCEPTED';
+    }
+
+    /**
+     * Return the abbreviated subject of the response.
+     *
+     * @return string The short subject.
+     */
+    public function getShortSubject()
+    {
+        return _("Accepted");
+    }
+
+    /**
+     * Return the short message for the response.
+     *
+     * @param boolean $is_update Indicates if the request was an update.
+     *
+     * @return string The short message.
+     */
+    public function getShortMessage($is_update = false)
+    {
+        return $is_update
+            ? _("has accepted the update to the following event")
+            : _("has accepted the invitation to the following event");
+    }
+}
\ No newline at end of file
diff --git a/framework/Itip/lib/Horde/Itip/Response/Type/Base.php b/framework/Itip/lib/Horde/Itip/Response/Type/Base.php
new file mode 100644 (file)
index 0000000..cd65a29
--- /dev/null
@@ -0,0 +1,125 @@
+<?php
+/**
+ * Basic iTip response type definition.
+ *
+ * PHP version 5
+ *
+ * @category Kolab
+ * @package  Kolab_Filter
+ * @author   Gunnar Wrobel <wrobel@pardus.de>
+ * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link     http://pear.horde.org/index.php?package=Kolab_Server
+ */
+
+/**
+ * Basic iTip response type definition.
+ *
+ * Copyright 2010 Klarälvdalens Datakonsult AB
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you did not
+ * receive this file, see
+ * http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+ *
+ * @category Kolab
+ * @package  Kolab_Filter
+ * @author   Gunnar Wrobel <wrobel@pardus.de>
+ * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link     http://pear.horde.org/index.php?package=Kolab_Server
+ */
+abstract class Horde_Itip_Response_Type_Base
+implements Horde_Itip_Response_Type
+{
+    /**
+     * The request we are going to answer.
+     *
+     * @var Horde_Itip_Event
+     */
+    private $_request;
+
+    /**
+     * Set the request.
+     *
+     * @param Horde_Itip_Event $request  The request this
+     *                                                  instance will respond
+     *                                                    to.
+     *
+     * @return NULL
+     */
+    public function setRequest(
+        Horde_Itip_Event $request
+    ) {
+        $this->_request  = $request;
+    }
+
+    /**
+     * Get the request for this response.
+     *
+     * @return Horde_Itip_Event The request this instance will
+     *                                         respond to.
+     *
+     * @throws Horde_Itip_Exception If the request has not been
+     *                                             set yet.
+     */
+    public function getRequest()
+    {
+        if (empty($this->_request)) {
+            throw new Horde_Itip_Exception(
+                'The iTip request is still undefined!'
+            );
+        }
+        return $this->_request;
+    }
+
+    /**
+     * Return the subject of the response.
+     *
+     * @param string $comment An optional comment that should appear in the
+     *                        response subject.
+     *
+     * @return string The subject.
+     */
+    public function getSubject($comment = null)
+    {
+        if ($comment === null) {
+            return sprintf(
+                '%s: %s',
+                $this->getShortSubject(),
+                $this->getRequest()->getSummary()
+            );
+        } else {
+            return sprintf(
+                '%s [%s]: %s',
+                $this->getShortSubject(),
+                $comment,
+                $this->getRequest()->getSummary()
+            );
+        }
+    }
+
+    /**
+     * Return an additional message for the response.
+     *
+     * @param boolean $is_update Indicates if the request was an update.
+     * @param string  $comment   An optional comment that should appear in the
+     *                           response message.
+     *
+     * @return string The message.
+     */
+    public function getMessage($is_update = false, $comment = null)
+    {
+        if ($comment === null) {
+            return sprintf(
+                "%s %s:\n\n%s",
+                $this->getShortMessage($update),
+                $this->getRequest()->getSummary()
+            );
+        } else {
+            return sprintf(
+                "%s %s:\n\n%s\n\n%s",
+                $this->getShortMessage($update),
+                $this->getRequest()->getSummary(),
+                $comment
+            );
+        }
+    }
+}
\ No newline at end of file
diff --git a/framework/Itip/lib/Horde/Itip/Response/Type/Decline.php b/framework/Itip/lib/Horde/Itip/Response/Type/Decline.php
new file mode 100644 (file)
index 0000000..3e6b429
--- /dev/null
@@ -0,0 +1,65 @@
+<?php
+/**
+ * Indicates a declined invitation.
+ *
+ * PHP version 5
+ *
+ * @category Kolab
+ * @package  Kolab_Filter
+ * @author   Gunnar Wrobel <wrobel@pardus.de>
+ * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link     http://pear.horde.org/index.php?package=Kolab_Server
+ */
+
+/**
+ * Indicates a declined invitation.
+ *
+ * Copyright 2010 Klarälvdalens Datakonsult AB
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you did not
+ * receive this file, see
+ * http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+ *
+ * @category Kolab
+ * @package  Kolab_Filter
+ * @author   Gunnar Wrobel <wrobel@pardus.de>
+ * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link     http://pear.horde.org/index.php?package=Kolab_Server
+ */
+class Horde_Itip_Response_Type_Decline
+extends Horde_Itip_Response_Type_Base
+{
+    /**
+     * Return the status of the response.
+     *
+     * @return string The status.
+     */
+    public function getStatus()
+    {
+        return 'DECLINED';
+    }
+
+    /**
+     * Return the abbreviated subject of the response.
+     *
+     * @return string The short subject.
+     */
+    public function getShortSubject()
+    {
+        return _("Declined");
+    }
+
+    /**
+     * Return the short message for the response.
+     *
+     * @param boolean $is_update Indicates if the request was an update.
+     *
+     * @return string The short message.
+     */
+    public function getShortMessage($is_update = false)
+    {
+        return $is_update
+            ? _("has declined the update to the following event")
+            : _("has declined the invitation to the following event");
+    }
+}
\ No newline at end of file
diff --git a/framework/Itip/lib/Horde/Itip/Response/Type/Tentative.php b/framework/Itip/lib/Horde/Itip/Response/Type/Tentative.php
new file mode 100644 (file)
index 0000000..8da0b3f
--- /dev/null
@@ -0,0 +1,65 @@
+<?php
+/**
+ * Indicates a tentatively accepted invitation.
+ *
+ * PHP version 5
+ *
+ * @category Kolab
+ * @package  Kolab_Filter
+ * @author   Gunnar Wrobel <wrobel@pardus.de>
+ * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link     http://pear.horde.org/index.php?package=Kolab_Server
+ */
+
+/**
+ * Indicates a tentatively accepted invitation.
+ *
+ * Copyright 2010 Klarälvdalens Datakonsult AB
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you did not
+ * receive this file, see
+ * http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+ *
+ * @category Kolab
+ * @package  Kolab_Filter
+ * @author   Gunnar Wrobel <wrobel@pardus.de>
+ * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link     http://pear.horde.org/index.php?package=Kolab_Server
+ */
+class Horde_Itip_Response_Type_Tentative
+extends Horde_Itip_Response_Type_Base
+{
+    /**
+     * Return the status of the response.
+     *
+     * @return string The status.
+     */
+    public function getStatus()
+    {
+        return 'TENTATIVE';
+    }
+
+    /**
+     * Return the abbreviated subject of the response.
+     *
+     * @return string The short subject.
+     */
+    public function getShortSubject()
+    {
+        return _("Tentative");
+    }
+
+    /**
+     * Return the short message for the response.
+     *
+     * @param boolean $is_update Indicates if the request was an update.
+     *
+     * @return string The short message.
+     */
+    public function getShortMessage($is_update = false)
+    {
+        return $is_update
+            ? _("has tentatively accepted the update to the following event")
+            : _("has tentatively accepted the invitation to the following event");
+    }
+}
\ No newline at end of file