Converted Imp to use the Horde_Itip library.
authorGunnar Wrobel <p@rdus.de>
Mon, 16 Aug 2010 22:26:10 +0000 (00:26 +0200)
committerGunnar Wrobel <p@rdus.de>
Wed, 25 Aug 2010 17:21:40 +0000 (19:21 +0200)
17 files changed:
framework/Itip/TODO
framework/Itip/lib/Horde/Itip.php
framework/Itip/lib/Horde/Itip/Event/Vevent.php
framework/Itip/lib/Horde/Itip/Resource.php
framework/Itip/lib/Horde/Itip/Resource/Base.php
framework/Itip/lib/Horde/Itip/Resource/Identity.php [new file with mode: 0644]
framework/Itip/lib/Horde/Itip/Response.php
framework/Itip/lib/Horde/Itip/Response/Options.php [new file with mode: 0644]
framework/Itip/lib/Horde/Itip/Response/Options/Base.php [new file with mode: 0644]
framework/Itip/lib/Horde/Itip/Response/Options/Horde.php [new file with mode: 0644]
framework/Itip/lib/Horde/Itip/Response/Options/Kolab.php [new file with mode: 0644]
framework/Itip/lib/Horde/Itip/Response/Type.php
framework/Itip/lib/Horde/Itip/Response/Type/Base.php
framework/Itip/package.xml
imp/lib/Mime/Viewer/Itip.php
imp/lib/tests/Imp/Stub/Identity.php
imp/lib/tests/Imp/Unit/Mime/Viewer/ItipTest.php

index e60de1a..6e934b0 100644 (file)
@@ -1,3 +1,3 @@
 
- - Merge with Horde Imp
+ - Merge with Horde Imp (Changes [Horde]: Extended message body; improved From, Changes [Kolab]: Do not automatically set the sequence attribute to 0 if it does not exist -> http://bugs.horde.org/ticket/4863; use lower case mailto -> http://bugs.horde.org/ticket/4872; Missing UID in the vEvent causes failure;
  - Complete unit testing
index 229d3dc..79898a1 100644 (file)
@@ -40,13 +40,22 @@ class Horde_Itip
      *
      * @param Horde_Itip_Response $response The iTip response.
      */
-    public function __construct(
-        Horde_Itip_Response $response
-    ) {
+    public function __construct(Horde_Itip_Response $response)
+    {
         $this->_response = $response;
     }
 
     /**
+     * Return the organizer mail address.
+     *
+     * @return string The mail address of the event organizer
+     */
+    public function getOrganizer()
+    {
+        return $this->_response->getRequest()->getOrganizer();
+    }
+
+    /**
      * Return the response as an iCalendar vEvent object.
      *
      * @param Horde_Itip_Response_Type $type The response type.
@@ -86,19 +95,39 @@ class Horde_Itip
      * @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
+        $product_id
     ) {
         return $this->_response->getMessage(
-            $type, $product_id, $subject_comment
+            $type, $product_id
+        );
+    }
+
+    /**
+     * Send the invitation response as a multi part MIME message.
+     *
+     * @param Horde_Itip_Response_Type    $type      The response type.
+     * @param Horde_Itip_Response_Options $options   The options for the response.
+     * @param Horde_Mail_Transport        $transport The mail transport.
+     *
+     * @return NULL
+     */
+    public function sendMultipartResponse(
+        Horde_Itip_Response_Type $type,
+        Horde_Itip_Response_Options $options,
+        Horde_Mail_Transport $transport
+    ) {
+        list($headers, $body) = $this->_response->getMultiPartMessage(
+            $type, $options
+        );
+        $body->send(
+            $this->_response->getRequest()->getOrganizer(),
+            $headers,
+            $transport
         );
     }
 
index 204e243..5af226e 100644 (file)
@@ -75,7 +75,7 @@ implements Horde_Itip_Event
      */
     public function getUid()
     {
-        return $this->_vevent->getAttributeDefault('UID', '');
+        return $this->_vevent->getAttribute('UID');
     }
 
     /**
@@ -150,7 +150,7 @@ implements Horde_Itip_Event
     {
         $this->_vevent->setAttribute(
             'ATTENDEE',
-            'MAILTO:' . $attendee,
+            'mailto:' . $attendee,
             array(
                 'CN' => $common_name,
                 'PARTSTAT' => $status
@@ -351,7 +351,7 @@ implements Horde_Itip_Event
      */
     private function getSequence()
     {
-        return $this->_vevent->getAttributeDefault('SEQUENCE', 0);
+        return $this->_vevent->getAttribute('SEQUENCE');
     }
 
     /**
@@ -372,7 +372,10 @@ implements Horde_Itip_Event
      */
     private function copySequence(Horde_Itip_Event $itip)
     {
-        $itip->setSequence($this->getSequence());
+        try {
+            $itip->setSequence($this->getSequence());
+        } catch (Horde_Icalendar_Exception $e) {
+        }
     }
 
     /**
index e10e642..9528672 100644 (file)
@@ -36,6 +36,13 @@ interface Horde_Itip_Resource
     public function getMailAddress();
 
     /**
+     * Retrieve the reply-to address for the resource.
+     *
+     * @return string The reply-to address.
+     */
+    public function getReplyTo();
+
+    /**
      * Retrieve the common name of the resource.
      *
      * @return string The common name.
index 8a378fa..9e786ae 100644 (file)
@@ -66,6 +66,15 @@ implements Horde_Itip_Resource
     }
 
     /**
+     * Retrieve the reply-to address for the resource.
+     *
+     * @return string The reply-to address.
+     */
+    public function getReplyTo()
+    {
+    }
+
+    /**
      * Retrieve the common name of the resource.
      *
      * @return string The common name.
diff --git a/framework/Itip/lib/Horde/Itip/Resource/Identity.php b/framework/Itip/lib/Horde/Itip/Resource/Identity.php
new file mode 100644 (file)
index 0000000..39fd04d
--- /dev/null
@@ -0,0 +1,119 @@
+<?php
+/**
+ * Horde_Prefs_Identity based 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
+ */
+
+/**
+ * Horde_Prefs_Identity based information provider for an invited resource.
+ *
+ * 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.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_Identity
+implements Horde_Itip_Resource
+{
+    /**
+     * The identity.
+     *
+     * @var IMP_Prefs_Identity
+     */
+    private $_identity;
+
+    /**
+     * The selected identity for replying.
+     *
+     * @var string
+     */
+    private $_reply_to;
+
+    /**
+     * Constructor.
+     *
+     * @param IMP_Prefs_Identity $identity  The IMP identity of the invited
+     *                                      resource.
+     * @param array              $attendees The attendees of the invitation.
+     * @param string             $reply_to  The selected identity for sending the
+     *                                      reply.
+     */
+    public function __construct($identity, $attendees, $reply_to)
+    {
+        $this->_identity = $identity;
+        if (!is_array($attendees)) {
+            $attendees = array($attendees);
+        }
+        foreach ($attendees as $attendee) {
+            $attendee = preg_replace('/mailto:/i', '', $attendee);
+            if (!is_null($id = $identity->getMatchingIdentity($attendee))) {
+                $identity->setDefault($id);
+                break;
+            }
+        }
+        $this->_reply_to = $reply_to;
+    }
+
+    /**
+     * Retrieve the mail address of the resource.
+     *
+     * @return string The mail address.
+     */
+    public function getMailAddress()
+    {
+        return $this->_identity->getFromAddress();
+    }
+
+    /**
+     * Retrieve the reply-to address for the resource.
+     *
+     * @return string The reply-to address.
+     */
+    public function getReplyTo()
+    {
+        $original = $this->_identity->getDefault();
+        $this->_identity->setDefault($this->_reply_to);
+        $reply_to = $this->_identity->getValue('replyto_addr');
+        $this->_identity->setDefault($original);
+        return $reply_to;
+    }
+
+    /**
+     * Retrieve the common name of the resource.
+     *
+     * @return string The common name.
+     */
+    public function getCommonName()
+    {
+        return $this->_identity->getValue('fullname');
+    }
+
+    /**
+     * Retrieve the "From" address for this resource.
+     *
+     * @return string The "From" address.
+     */
+    public function getFrom()
+    {
+        $cn = $this->getCommonName();
+        if (!empty($cn)) {
+            return sprintf("%s <%s>", $cn, $this->getMailAddress());
+        } else {
+            return $this->getMailAddress();
+        }
+    }
+}
\ No newline at end of file
index 63a2625..c174707 100644 (file)
@@ -72,6 +72,16 @@ class Horde_Itip_Response
     }
 
     /**
+     * Return the original request.
+     *
+     * @return Horde_Itip_Event The original request.
+     */
+    public function getRequest()
+    {
+        return $this->_request;
+    }
+
+    /**
      * Return the response as an iCalendar vEvent object.
      *
      * @param Horde_Itip_Response_Type $type The response type.
@@ -85,7 +95,7 @@ class Horde_Itip_Response
         $vCal = false
     ) {
         $itip_reply = new Horde_Itip_Event_Vevent(
-            Horde_iCalendar::newComponent('VEVENT', $vCal)
+            Horde_Icalendar::newComponent('VEVENT', $vCal)
         );
         $this->_request->copyEventInto($itip_reply);
 
@@ -103,17 +113,16 @@ class Horde_Itip_Response
      * 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.
+     * @param Horde_Itip_Response_Options $options The options for the response.
      *
-     * @return Horde_iCalendar The response object.
+     * @return Horde_Icalendar The response object.
      */
     public function getIcalendar(
         Horde_Itip_Response_Type $type,
-        $product_id
+        Horde_Itip_Response_Options $options
     ) {
-        $vCal = new Horde_iCalendar();
-        $vCal->setAttribute('PRODID', $product_id);
+        $vCal = new Horde_Icalendar();
+        $options->prepareIcalendar($vCal);
         $vCal->setAttribute('METHOD', 'REPLY');
         $vCal->addComponent($this->getVevent($type, $vCal));
         return $vCal;
@@ -122,52 +131,70 @@ class Horde_Itip_Response
     /**
      * 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.
+     * @param Horde_Itip_Response_Type    $type    The response type.
+     * @param Horde_Itip_Response_Options $options The options for the response.
      *
      * @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
+        Horde_Itip_Response_Options $options
     ) {
-        $ics = new Horde_Mime_Part();
-        $ics->setType('text/calendar');
-        $ics->setCharset('UTF-8');
-        $ics->setContents(
-            $this->getIcalendar($type, $product_id)->exportvCalendar()
-        );
-        $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 = Horde_Mime_Message::convertMimePart($ics);
         $message = new Horde_Mime_Part();
-        $message->setCharset('UTF-8');
-        $message->setTransferEncoding('quoted-printable');
-        //$message->transferEncodeContents();
+        $message->setType('text/calendar');
+        $options->prepareIcsMimePart($message);
+        $message->setContents(
+            $this->getIcalendar($type, $options)->exportvCalendar()
+        );
+        $message->setName('event-reply.ics');
+        $message->setContentTypeParameter('METHOD', 'REPLY');
 
         // Build the reply headers.
+        $from = $this->_resource->getFrom();
+        $reply_to = $this->_resource->getReplyTo();
         $headers = new Horde_Mime_Headers();
         $headers->addHeader('Date', date('r'));
-        $headers->addHeader('From', $this->_resource->getFrom());
+        $headers->addHeader('From', $from);
         $headers->addHeader('To', $this->_request->getOrganizer());
+        if ($reply_to != $from) {
+            $headers->addHeader('Reply-to', $reply_to);
+        }
         $headers->addHeader(
-            'Subject', $type->getSubject($subject_comment)
+            'Subject', $type->getSubject()
         );
-        //$headers->addMimeHeaders($message);
+
+        $options->prepareResponseMimeHeaders($headers);
+
+        return array($headers, $message);
+    }
+
+    /**
+     * Return the response as a MIME message.
+     *
+     * @param Horde_Itip_Response_Type $type       The response type.
+     * @param Horde_Itip_Response_Options $options The options for the response.
+     *
+     * @return array A list of two object: The mime headers and the mime
+     *               message.
+     */
+    public function getMultiPartMessage(
+        Horde_Itip_Response_Type $type,
+        Horde_Itip_Response_Options $options
+    ) {
+        $message = new Horde_Mime_Part();
+        $message->setType('multipart/alternative');
+
+        list($headers, $ics) = $this->getMessage($type, $options);
+
+        $body = new Horde_Mime_Part();
+        $body->setType('text/plain');
+        $options->prepareMessageMimePart($body);
+        $body->setContents(Horde_String::wrap($type->getMessage(), 76, "\n"));
+
+        $message->addPart($body);
+        $message->addPart($ics);
+
         return array($headers, $message);
     }
 }
\ No newline at end of file
diff --git a/framework/Itip/lib/Horde/Itip/Response/Options.php b/framework/Itip/lib/Horde/Itip/Response/Options.php
new file mode 100644 (file)
index 0000000..9fa8308
--- /dev/null
@@ -0,0 +1,82 @@
+<?php
+/**
+ * Holds iTip response options.
+ *
+ * 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
+ */
+
+/**
+ * Holds iTip response options.
+ *
+ * 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_Response_Options
+{
+    /**
+     * Prepare the iCalendar part of the response object.
+     *
+     * @param Horde_Icalendar $ical The iCalendar response object.
+     *
+     * @return NULL
+     */
+    public function prepareIcalendar(Horde_Icalendar $ical);
+
+    /**
+     * Prepare the iCalendar MIME part of the response message.
+     *
+     * @param Horde_Mime_Part $ics The iCalendar MIME part of the response
+     *                             message.
+     *
+     * @return NULL
+     */
+    public function prepareResponseMimeHeaders(Horde_Mime_Headers $headers);
+
+    /**
+     * Prepare the iCalendar MIME part of the response message.
+     *
+     * @param Horde_Mime_Part $ics The iCalendar MIME part of the response
+     *                             message.
+     *
+     * @return NULL
+     */
+    public function prepareIcsMimePart(Horde_Mime_Part $ics);
+
+    /**
+     * Prepare the message MIME part of the response.
+     *
+     * @param Horde_Mime_Part $message The message MIME part of the response.
+     *
+     * @return NULL
+     */
+    public function prepareMessageMimePart(Horde_Mime_Part $message);
+
+    /**
+     * Get the character set for the response mime parts.
+     *
+     * @return string The character set.
+     */
+    public function getCharacterSet();
+
+    /**
+     * Get the product ID of the iCalendar object embedded in the MIME response.
+     *
+     * @return string The product ID.
+     */
+    public function getProductId();
+}
\ No newline at end of file
diff --git a/framework/Itip/lib/Horde/Itip/Response/Options/Base.php b/framework/Itip/lib/Horde/Itip/Response/Options/Base.php
new file mode 100644 (file)
index 0000000..e2a759b
--- /dev/null
@@ -0,0 +1,68 @@
+<?php
+/**
+ * Holds iTip response options.
+ *
+ * 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
+ */
+
+/**
+ * Holds iTip response options.
+ *
+ * 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
+ */
+abstract class Horde_Itip_Response_Options_Base
+implements Horde_Itip_Response_Options
+{
+    /**
+     * Prepare the iCalendar part of the response object.
+     *
+     * @param Horde_Icalendar $ical The iCalendar response object.
+     *
+     * @return NULL
+     */
+    public function prepareIcalendar(Horde_Icalendar $ical)
+    {
+        $ical->setAttribute('PRODID', $this->getProductId());
+    }
+
+    /**
+     * Prepare the iCalendar MIME part of the response message.
+     *
+     * @param Horde_Mime_Part $ics The iCalendar MIME part of the response
+     *                             message.
+     *
+     * @return NULL
+     */
+    public function prepareIcsMimePart(Horde_Mime_Part $ics)
+    {
+        $ics->setCharset($this->getCharacterSet());
+    }
+
+    /**
+     * Prepare the message MIME part of the response.
+     *
+     * @param Horde_Mime_Part $message The message MIME part of the response.
+     *
+     * @return NULL
+     */
+    public function prepareMessageMimePart(Horde_Mime_Part $message)
+    {
+        $message->setCharset($this->getCharacterSet());
+    }
+}
\ No newline at end of file
diff --git a/framework/Itip/lib/Horde/Itip/Response/Options/Horde.php b/framework/Itip/lib/Horde/Itip/Response/Options/Horde.php
new file mode 100644 (file)
index 0000000..340c4fd
--- /dev/null
@@ -0,0 +1,93 @@
+<?php
+/**
+ * Handles iTip response options for Horde iTip 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 response options for Horde iTip responses.
+ *
+ * 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_Response_Options_Horde
+extends Horde_Itip_Response_Options_Base
+{
+    /**
+     * The MIME character set.
+     *
+     * @var string
+     */
+    private $_charset;
+
+    /**
+     * Options for setting the "Received" MIME header.
+     *
+     * @var array
+     */
+    private $_received_options;
+
+    /**
+     * Constructor.
+     *
+     * @param string $charset          The MIME character set that should be
+     *                                 used.
+     * @param array  $received_options Options for setting the "Received" MIME
+     *                                 header.
+     */
+    public function __construct($charset, $received_options)
+    {
+        $this->_charset          = $charset;
+        $this->_received_options = $received_options;
+    }
+    /**
+     * Prepare the iCalendar MIME part of the response message.
+     *
+     * @param Horde_Mime_Part $ics The iCalendar MIME part of the response
+     *                             message.
+     *
+     * @return NULL
+     */
+    public function prepareResponseMimeHeaders(Horde_Mime_Headers $headers)
+    {
+        $headers->addReceivedHeader($this->_received_options);
+        $headers->addMessageIdHeader();
+    }
+
+    /**
+     * Get the character set for the response mime parts.
+     *
+     * @return string The character set.
+     */
+    public function getCharacterSet()
+    {
+        return $this->_charset;
+    }
+
+    /**
+     * Get the product ID of the iCalendar object embedded in the MIME response.
+     *
+     * @return string The product ID.
+     */
+    public function getProductId()
+    {
+        $headers = new Horde_Mime_Headers();
+        return '-//The Horde Project//' . $headers->getUserAgent() . '//EN';
+    }
+}
\ No newline at end of file
diff --git a/framework/Itip/lib/Horde/Itip/Response/Options/Kolab.php b/framework/Itip/lib/Horde/Itip/Response/Options/Kolab.php
new file mode 100644 (file)
index 0000000..90fe627
--- /dev/null
@@ -0,0 +1,63 @@
+<?php
+/**
+ * Handles iTip response options for Kolab iTip 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 response options for Kolab iTip responses.
+ *
+ * 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_Response_Options_Kolab
+extends Horde_Itip_Response_Options_Base
+{
+    /**
+     * Prepare the iCalendar MIME part of the response message.
+     *
+     * @param Horde_Mime_Part $ics The iCalendar MIME part of the response
+     *                             message.
+     *
+     * @return NULL
+     */
+    public function prepareResponseMimeHeaders(Horde_Mime_Headers $headers)
+    {
+    }
+
+    /**
+     * Get the character set for the response mime parts.
+     *
+     * @return string The character set.
+     */
+    public function getCharacterSet()
+    {
+        return 'UTF-8';
+    }
+
+    /**
+     * Get the product ID of the iCalendar object embedded in the MIME response.
+     *
+     * @return string The product ID.
+     */
+    public function getProductId()
+    {
+        return '-//kolab.org//NONSGML Kolab Server 2//EN';
+    }
+}
\ No newline at end of file
index 9a3ffd5..73d53c3 100644 (file)
@@ -36,30 +36,32 @@ interface Horde_Itip_Response_Type
     public function getStatus();
 
     /**
-     * Return the abbreviated subject of the response.
+     * Return the core subject of the response.
      *
      * @return string The short subject.
      */
     public function getShortSubject();
 
     /**
-     * Return the subject of the response.
+     * Return the subject of the response without using the comment.
      *
-     * @param string $comment An optional comment that should appear in the
-     *                        response subject.
+     * @return string The subject.
+     */
+    public function getBriefSubject();
+
+    /**
+     * Return the subject of the response.
      *
      * @return string The subject.
      */
-    public function getSubject($comment = null);
+    public function getSubject();
 
     /**
      * 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);
+    public function getMessage($is_update = false);
 }
\ No newline at end of file
index cd65a29..2b61dad 100644 (file)
@@ -37,11 +37,38 @@ implements Horde_Itip_Response_Type
     private $_request;
 
     /**
+     * The invited resource.
+     *
+     * @var Horde_Itip_Resource
+     */
+    private $_resource;
+
+    /**
+     * An optional comment that should appear in the response subject.
+     *
+     * @var string
+     */
+    private $_comment;
+
+    /**
+     * Constructor.
+     *
+     * @param Horde_Itip_Resource $resource The invited resource. 
+     * @param string              $comment  A comment for the subject line.
+     */
+    public function __construct(
+        Horde_Itip_Resource $resource,
+        $comment = null
+    ) {
+        $this->_resource = $resource;
+        $this->_comment  = $comment;
+    }
+
+    /**
      * Set the request.
      *
-     * @param Horde_Itip_Event $request  The request this
-     *                                                  instance will respond
-     *                                                    to.
+     * @param Horde_Itip_Event $request The request this instance will respond
+     *                                  to.
      *
      * @return NULL
      */
@@ -71,26 +98,32 @@ implements Horde_Itip_Response_Type
     }
 
     /**
-     * Return the subject of the response.
+     * Return the subject of the response without using the comment.
      *
-     * @param string $comment An optional comment that should appear in the
-     *                        response subject.
+     * @return string The subject.
+     */
+    public function getBriefSubject()
+    {
+        return sprintf(
+            '%s: %s',
+            $this->getShortSubject(),
+            $this->getRequest()->getSummary()
+        );
+    }
+    /**
+     * Return the subject of the response.
      *
      * @return string The subject.
      */
-    public function getSubject($comment = null)
+    public function getSubject()
     {
-        if ($comment === null) {
-            return sprintf(
-                '%s: %s',
-                $this->getShortSubject(),
-                $this->getRequest()->getSummary()
-            );
+        if ($this->_comment === null) {
+            return $this->getBriefSubject();
         } else {
             return sprintf(
                 '%s [%s]: %s',
                 $this->getShortSubject(),
-                $comment,
+                $this->_comment,
                 $this->getRequest()->getSummary()
             );
         }
@@ -100,25 +133,25 @@ implements Horde_Itip_Response_Type
      * 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)
+    public function getMessage($is_update = false)
     {
-        if ($comment === null) {
+        if ($this->_comment === null) {
             return sprintf(
                 "%s %s:\n\n%s",
-                $this->getShortMessage($update),
+                $this->_resource->getCommonName(),
+                $this->getShortMessage($is_update),
                 $this->getRequest()->getSummary()
             );
         } else {
             return sprintf(
                 "%s %s:\n\n%s\n\n%s",
-                $this->getShortMessage($update),
+                $this->_resource->getCommonName(),
+                $this->getShortMessage($is_update),
                 $this->getRequest()->getSummary(),
-                $comment
+                $this->_comment
             );
         }
     }
index e92eaa8..c771a7d 100644 (file)
       </dir> <!-- /lib/Horde/Itip/Event -->
       <dir name="Resource">
        <file name="Base.php" role="php" />
+       <file name="Identity.php" role="php" />
       </dir> <!-- /lib/Horde/Itip/Resource -->
       <dir name="Response">
+       <dir name="Options">
+        <file name="Base.php" role="php" />
+        <file name="Horde.php" role="php" />
+        <file name="Kolab.php" role="php" />
+       </dir> <!-- /lib/Horde/Itip/Response/Options -->
+       <file name="Options.php" role="php" />
        <dir name="Type">
         <file name="Accept.php" role="php" />
         <file name="Base.php" role="php" />
    <install as="Horde/Itip/Response.php" name="lib/Horde/Itip/Response.php" />
    <install as="Horde/Itip/Event/Vevent.php" name="lib/Horde/Itip/Event/Vevent.php" />
    <install as="Horde/Itip/Resource/Base.php" name="lib/Horde/Itip/Resource/Base.php" />
+   <install as="Horde/Itip/Resource/Identity.php" name="lib/Horde/Itip/Resource/Identity.php" />
+   <install as="Horde/Itip/Response/Options.php" name="lib/Horde/Itip/Response/Options.php" />
+   <install as="Horde/Itip/Response/Options/Base.php" name="lib/Horde/Itip/Response/Options/Base.php" />
+   <install as="Horde/Itip/Response/Options/Horde.php" name="lib/Horde/Itip/Response/Options/Horde.php" />
+   <install as="Horde/Itip/Response/Options/Kolab.php" name="lib/Horde/Itip/Response/Options/Kolab.php" />
    <install as="Horde/Itip/Response/Type.php" name="lib/Horde/Itip/Response/Type.php" />
    <install as="Horde/Itip/Response/Type/Accept.php" name="lib/Horde/Itip/Response/Type/Accept.php" />
    <install as="Horde/Itip/Response/Type/Base.php" name="lib/Horde/Itip/Response/Type/Base.php" />
index 58b3740..afc9256 100644 (file)
@@ -245,137 +245,46 @@ class IMP_Mime_Viewer_Itip extends Horde_Mime_Viewer_Base
                 // vEvent request.
                 if (isset($components[$key]) &&
                     $components[$key]->getType() == 'vEvent') {
-                    $vEvent = $components[$key];
-
-                    // Get the organizer details.
-                    try {
-                        $organizer = $vEvent->getAttribute('ORGANIZER');
-                    } catch (Horde_Icalendar_Exception $e) {
-                        break;
-                    }
-                    $organizer = parse_url($organizer);
-                    $organizerEmail = $organizer['path'];
-                    $organizer = $vEvent->getAttribute('ORGANIZER', true);
-                    $organizerName = isset($organizer['cn']) ? $organizer['cn'] : '';
-
-                    // Build the reply.
-                    $msg_headers = new Horde_Mime_Headers();
-                    $vCal = new Horde_Icalendar();
-                    $vCal->setAttribute('PRODID', '-//The Horde Project//' . $msg_headers->getUserAgent() . '//EN');
-                    $vCal->setAttribute('METHOD', 'REPLY');
 
-                    $vEvent_reply = Horde_Icalendar::newComponent('vevent', $vCal);
-                    $vEvent_reply->setAttribute('UID', $vEvent->getAttribute('UID'));
-                    try {
-                        $vEvent->getAttribute('SUMMARY');
-                        $vEvent_reply->setAttribute('SUMMARY', $vEvent->getAttribute('SUMMARY'));
-                    } catch (Horde_Icalendar_Exception $e) {}
-                    try {
-                        $vEvent->getAttribute('DESCRIPTION');
-                        $vEvent_reply->setAttribute('DESCRIPTION', $vEvent->getAttribute('DESCRIPTION'));
-                    } catch (Horde_Icalendar_Exception $e) {}
-                    $dtstart = $vEvent->getAttribute('DTSTART', true);
-                    $vEvent_reply->setAttribute('DTSTART', $vEvent->getAttribute('DTSTART'), array_pop($dtstart));
-                    try {
-                        $vEvent->getAttribute('DTEND');
-                        $dtend = $vEvent->getAttribute('DTEND', true);
-                        $vEvent_reply->setAttribute('DTEND', $vEvent->getAttribute('DTEND'), array_pop($dtend));
-                    } catch (Horde_Icalendar_Exception $e) {
-                        $duration = $vEvent->getAttribute('DURATION', true);
-                        $vEvent_reply->setAttribute('DURATION', $vEvent->getAttribute('DURATION'), array_pop($duration));
-                    }
-                    try {
-                        $vEvent->getAttribute('SEQUENCE');
-                        $vEvent_reply->setAttribute('SEQUENCE', $vEvent->getAttribute('SEQUENCE'));
-                    } catch (Horde_Icalendar_Exception $e) {}
-                    $vEvent_reply->setAttribute('ORGANIZER', $vEvent->getAttribute('ORGANIZER'), array_pop($organizer));
+                    $vEvent = $components[$key];
 
-                    // Find out who we are and update status.
-                    $identity = $GLOBALS['injector']->getInstance('IMP_Identity');
-                    $attendees = $vEvent->getAttribute('ATTENDEE');
-                    if (!is_array($attendees)) {
-                        $attendees = array($attendees);
-                    }
-                    foreach ($attendees as $attendee) {
-                        $attendee = preg_replace('/mailto:/i', '', $attendee);
-                        if (!is_null($id = $identity->getMatchingIdentity($attendee))) {
-                            $identity->setDefault($id);
-                            break;
-                        }
-                    }
-                    $name = $email = $identity->getFromAddress();
-                    $params = array();
-                    $cn = $identity->getValue('fullname');
-                    if (!empty($cn)) {
-                        $name = $params['CN'] = $cn;
-                    }
+                    $resource = new Horde_Itip_Resource_Identity(
+                        $GLOBALS['injector']->getInstance('IMP_Identity'),
+                        $vEvent->getAttribute('ATTENDEE'),
+                        Horde_Util::getFormData('identity')
+                    );
 
                     switch ($action) {
                     case 'accept':
                     case 'accept-import':
-                        $message = sprintf(_("%s has accepted."), $name);
-                        $subject = _("Accepted: ") . $vEvent->getAttribute('SUMMARY');
-                        $params['PARTSTAT'] = 'ACCEPTED';
+                        $type = new Horde_Itip_Response_Type_Accept($resource);
                         break;
-
                     case 'deny':
-                        $message = sprintf(_("%s has declined."), $name);
-                        $subject = _("Declined: ") . $vEvent->getAttribute('SUMMARY');
-                        $params['PARTSTAT'] = 'DECLINED';
+                        $type = new Horde_Itip_Response_Type_Decline($resource);
                         break;
-
                     case 'tentative':
-                        $message = sprintf(_("%s has tentatively accepted."), $name);
-                        $subject = _("Tentative: ") . $vEvent->getAttribute('SUMMARY');
-                        $params['PARTSTAT'] = 'TENTATIVE';
+                        $type = new Horde_Itip_Response_Type_Tentative($resource);
                         break;
                     }
 
-                    $vEvent_reply->setAttribute('ATTENDEE', 'mailto:' . $email, $params);
-                    $vCal->addComponent($vEvent_reply);
-
-                    $mime = new Horde_Mime_Part();
-                    $mime->setType('multipart/alternative');
-
-                    $body = new Horde_Mime_Part();
-                    $body->setType('text/plain');
-                    $body->setCharset($charset);
-                    $body->setContents(Horde_String::wrap($message, 76, "\n"));
-
-                    $ics = new Horde_Mime_Part();
-                    $ics->setType('text/calendar');
-                    $ics->setCharset($charset);
-                    $ics->setContents($vCal->exportvCalendar());
-                    $ics->setName('event-reply.ics');
-                    $ics->setContentTypeParameter('METHOD', 'REPLY');
-
-                    $mime->addPart($body);
-                    $mime->addPart($ics);
-
-                    // Build the reply headers.
-                    $msg_headers->addReceivedHeader(array(
-                        'dns' => $GLOBALS['injector']->getInstance('Net_DNS_Resolver'),
-                        'server' => $GLOBALS['conf']['server']['name']
-                    ));
-                    $msg_headers->addMessageIdHeader();
-                    $msg_headers->addHeader('Date', date('r'));
-                    $msg_headers->addHeader('From', $email);
-                    $msg_headers->addHeader('To', $organizerEmail);
-
-                    $identity->setDefault(Horde_Util::getFormData('identity'));
-                    $replyto = $identity->getValue('replyto_addr');
-                    if (!empty($replyto) && ($replyto != $email)) {
-                        $msg_headers->addHeader('Reply-to', $replyto);
-                    }
-                    $msg_headers->addHeader('Subject', Horde_Mime::encode($subject, $charset));
-
-                    // Send the reply.
                     try {
-                        $mime->send($organizerEmail, $msg_headers, $GLOBALS['injector']->getInstance('IMP_Mail'));
+                        Horde_Itip::factory($vEvent, $resource)->sendMultiPartResponse(
+                            $type,
+                            new Horde_Itip_Response_Options_Horde(
+                                $charset,
+                                array(
+                                    'dns' => $GLOBALS['injector']->getInstance('Net_DNS_Resolver'),
+                                    'server' => $GLOBALS['conf']['server']['name']
+                                )
+                            ),
+                            $GLOBALS['injector']->getInstance('IMP_Mail')
+                        );
                         $msgs[] = array('success', _("Reply Sent."));
-                    } catch (Exception $e) {
+                    } catch (Horde_Itip_Exception $e) {
                         $msgs[] = array('error', sprintf(_("Error sending reply: %s."), $e->getMessage()));
                     }
+
+                    // Send the reply.
                 } else {
                     $msgs[] = array('warning', _("This action is not supported."));
                 }
index 6edd15e..8c54f25 100644 (file)
@@ -2,7 +2,7 @@
 
 class IMP_Stub_Identity
 {
-    private $id;
+    private $_id = 'default';
 
     public function getMatchingIdentity($mail)
     {
@@ -13,12 +13,17 @@ class IMP_Stub_Identity
 
     public function setDefault($id)
     {
-        if ($id != 'test' && $id != 'other') {
+        if ($id != 'test' && $id != 'other' && $id != 'default') {
             throw new Exception("Unexpected default $id!");
         }
         $this->_id = $id;
     }
 
+    public function getDefault()
+    {
+        return $this->_id;
+    }
+
     public function getFromAddress()
     {
         return 'test@example.org';
index b137e07..6d29688 100644 (file)
@@ -122,10 +122,8 @@ extends PHPUnit_Framework_TestCase
         $_GET['itip_action'] = array(0 => 'accept');
         $vCal = new Horde_Icalendar();
         $vCal->setAttribute('METHOD', 'REQUEST');
-        $inv = Horde_Icalendar::newComponent('VEVENT', $vCal);
-        $inv->setAttribute('ORGANIZER', 'somebody@example.com');
-        $viewer = $this->_getViewer($inv->exportvCalendar());
-        $this->assertEquals(array(), $viewer->render('inline'));
+        $viewer = $this->_getViewer("BEGIN:VEVENT\nORGANIZER:somebody@example.com\nDTSTAMP:20100816T143648Z\nDTSTART:20100816T143648Z\nEND:VEVENT");
+        $this->assertSame(array(), $viewer->render('inline'));
     }
 
     public function testResultMessageContainsCopiedSummary()
@@ -323,7 +321,7 @@ extends PHPUnit_Framework_TestCase
         $_GET['itip_action'] = array(0 => 'accept');
         $viewer = $this->_getViewer($this->_getInvitation()->exportvCalendar());
         $viewer->render('inline');
-        $this->assertEquals('Mr. Test has accepted.', $this->_getMimeMessage()->getPart(1)->getContents());
+        $this->assertEquals("Mr. Test has accepted the invitation to the following event:\n\nTest Invitation", $this->_getMimeMessage()->getPart(1)->getContents());
     }
 
     public function testDenyResultContainsDeclineMimeMessage()
@@ -331,7 +329,7 @@ extends PHPUnit_Framework_TestCase
         $_GET['itip_action'] = array(0 => 'deny');
         $viewer = $this->_getViewer($this->_getInvitation()->exportvCalendar());
         $viewer->render('inline');
-        $this->assertEquals('Mr. Test has declined.', $this->_getMimeMessage()->getPart(1)->getContents());
+        $this->assertEquals("Mr. Test has declined the invitation to the following event:\n\nTest Invitation", $this->_getMimeMessage()->getPart(1)->getContents());
     }
 
     public function testTentativeResultContainsTentativeMimeMessage()
@@ -339,7 +337,7 @@ extends PHPUnit_Framework_TestCase
         $_GET['itip_action'] = array(0 => 'tentative');
         $viewer = $this->_getViewer($this->_getInvitation()->exportvCalendar());
         $viewer->render('inline');
-        $this->assertEquals('Mr. Test has tentatively accepted.', $this->_getMimeMessage()->getPart(1)->getContents());
+        $this->assertEquals("Mr. Test has tentatively accepted the invitation to the following event:\n\nTest Invitation", $this->_getMimeMessage()->getPart(1)->getContents());
     }
 
     public function testResultMimeMessagePartOneHasRegistryCharset()
@@ -357,7 +355,11 @@ extends PHPUnit_Framework_TestCase
         $_GET['itip_action'] = array(0 => 'accept');
         $viewer = $this->_getViewer($this->_getInvitation()->exportvCalendar());
         $viewer->render('inline');
-        $this->assertEquals('BIG5', $this->_getMimeMessage()->getPart(2)->getCharset());
+        $ics = $this->_getMimeMessage()->getPart(2);
+        if (!$ics) {
+            $this->fail('Missing second message part!');
+        }
+        $this->assertEquals('BIG5', $ics->getCharset());
     }
 
     public function testResultMimeMessagePartTwoHasFileName()
@@ -365,7 +367,11 @@ extends PHPUnit_Framework_TestCase
         $_GET['itip_action'] = array(0 => 'accept');
         $viewer = $this->_getViewer($this->_getInvitation()->exportvCalendar());
         $viewer->render('inline');
-        $this->assertEquals('event-reply.ics', $this->_getMimeMessage()->getPart(2)->getName());
+        $ics = $this->_getMimeMessage()->getPart(2);
+        if (!$ics) {
+            $this->fail('Missing second message part!');
+        }
+        $this->assertEquals('event-reply.ics', $ics->getName());
     }
 
     public function testResultMimeMessagePartTwoHasContentTypeParameter()
@@ -373,7 +379,11 @@ extends PHPUnit_Framework_TestCase
         $_GET['itip_action'] = array(0 => 'accept');
         $viewer = $this->_getViewer($this->_getInvitation()->exportvCalendar());
         $viewer->render('inline');
-        $this->assertEquals('REPLY', $this->_getMimeMessage()->getPart(2)->getContentTypeParameter('METHOD'));
+        $ics = $this->_getMimeMessage()->getPart(2);
+        if (!$ics) {
+            $this->fail('Missing second message part!');
+        }
+        $this->assertEquals('REPLY', $ics->getContentTypeParameter('METHOD'));
     }
 
     public function testResultMimeMessageHeadersContainsReceivedHeader()
@@ -406,7 +416,7 @@ extends PHPUnit_Framework_TestCase
         $_GET['itip_action'] = array(0 => 'accept');
         $viewer = $this->_getViewer($this->_getInvitation()->exportvCalendar());
         $viewer->render('inline');
-        $this->assertEquals('test@example.org', $this->_getMailHeaders()->getValue('From'));
+        $this->assertEquals('"Mr. Test" <test@example.org>', $this->_getMailHeaders()->getValue('From'));
     }
 
     public function testResultMimeMessageHeadersContainsTo()
@@ -492,10 +502,16 @@ extends PHPUnit_Framework_TestCase
     private function _getMailHeaders()
     {
         if (isset($GLOBALS['injector']->getInstance('IMP_Mail')->sentMessages[0])) {
-            return Horde_Mime_Headers::parseHeaders(
+            $headers = Horde_Mime_Headers::parseHeaders(
                 $GLOBALS['injector']->getInstance('IMP_Mail')->sentMessages[0]['header_text']
             );
+            if (!$headers instanceOf Horde_Mime_Headers) {
+                $this->fail('Failed parsing message headers!');
+                return new Horde_Mime_Headers();
+            }
+            return $headers;
         }
+        $this->fail('No message has been sent!');
     }
 
     private function _getMail()
@@ -520,8 +536,12 @@ extends PHPUnit_Framework_TestCase
     private function _getIcalendar()
     {
         $part = $this->_getMimeMessage();
+        $ics = $part->getPart(2);
+        if (!$ics) {
+            $this->fail('Missing second message part!');
+        }
         $iCal = new Horde_Icalendar();
-        $iCal->parsevCalendar($part->getPart(2)->getContents());
+        $iCal->parsevCalendar($ics->getContents());
         return $iCal;
     }