From 25da03fe4764d08ca7140c93189db439b5d42f1b Mon Sep 17 00:00:00 2001
From: Gunnar Wrobel
Date: Tue, 17 Aug 2010 00:26:10 +0200
Subject: [PATCH] Converted Imp to use the Horde_Itip library.
---
framework/Itip/TODO | 2 +-
framework/Itip/lib/Horde/Itip.php | 47 +++++--
framework/Itip/lib/Horde/Itip/Event/Vevent.php | 11 +-
framework/Itip/lib/Horde/Itip/Resource.php | 7 ++
framework/Itip/lib/Horde/Itip/Resource/Base.php | 9 ++
.../Itip/lib/Horde/Itip/Resource/Identity.php | 119 ++++++++++++++++++
framework/Itip/lib/Horde/Itip/Response.php | 103 ++++++++++------
framework/Itip/lib/Horde/Itip/Response/Options.php | 82 ++++++++++++
.../Itip/lib/Horde/Itip/Response/Options/Base.php | 68 ++++++++++
.../Itip/lib/Horde/Itip/Response/Options/Horde.php | 93 ++++++++++++++
.../Itip/lib/Horde/Itip/Response/Options/Kolab.php | 63 ++++++++++
framework/Itip/lib/Horde/Itip/Response/Type.php | 18 +--
.../Itip/lib/Horde/Itip/Response/Type/Base.php | 75 +++++++----
framework/Itip/package.xml | 12 ++
imp/lib/Mime/Viewer/Itip.php | 137 ++++-----------------
imp/lib/tests/Imp/Stub/Identity.php | 9 +-
imp/lib/tests/Imp/Unit/Mime/Viewer/ItipTest.php | 46 +++++--
17 files changed, 691 insertions(+), 210 deletions(-)
create mode 100644 framework/Itip/lib/Horde/Itip/Resource/Identity.php
create mode 100644 framework/Itip/lib/Horde/Itip/Response/Options.php
create mode 100644 framework/Itip/lib/Horde/Itip/Response/Options/Base.php
create mode 100644 framework/Itip/lib/Horde/Itip/Response/Options/Horde.php
create mode 100644 framework/Itip/lib/Horde/Itip/Response/Options/Kolab.php
diff --git a/framework/Itip/TODO b/framework/Itip/TODO
index e60de1ac6..6e934b060 100644
--- a/framework/Itip/TODO
+++ b/framework/Itip/TODO
@@ -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
diff --git a/framework/Itip/lib/Horde/Itip.php b/framework/Itip/lib/Horde/Itip.php
index 229d3dce5..79898a128 100644
--- a/framework/Itip/lib/Horde/Itip.php
+++ b/framework/Itip/lib/Horde/Itip.php
@@ -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
);
}
diff --git a/framework/Itip/lib/Horde/Itip/Event/Vevent.php b/framework/Itip/lib/Horde/Itip/Event/Vevent.php
index 204e24321..5af226ed5 100644
--- a/framework/Itip/lib/Horde/Itip/Event/Vevent.php
+++ b/framework/Itip/lib/Horde/Itip/Event/Vevent.php
@@ -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) {
+ }
}
/**
diff --git a/framework/Itip/lib/Horde/Itip/Resource.php b/framework/Itip/lib/Horde/Itip/Resource.php
index e10e642af..95286721d 100644
--- a/framework/Itip/lib/Horde/Itip/Resource.php
+++ b/framework/Itip/lib/Horde/Itip/Resource.php
@@ -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.
diff --git a/framework/Itip/lib/Horde/Itip/Resource/Base.php b/framework/Itip/lib/Horde/Itip/Resource/Base.php
index 8a378faf5..9e786aecd 100644
--- a/framework/Itip/lib/Horde/Itip/Resource/Base.php
+++ b/framework/Itip/lib/Horde/Itip/Resource/Base.php
@@ -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
index 000000000..39fd04dbe
--- /dev/null
+++ b/framework/Itip/lib/Horde/Itip/Resource/Identity.php
@@ -0,0 +1,119 @@
+
+ * @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
+ * @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
diff --git a/framework/Itip/lib/Horde/Itip/Response.php b/framework/Itip/lib/Horde/Itip/Response.php
index 63a2625a9..c17470779 100644
--- a/framework/Itip/lib/Horde/Itip/Response.php
+++ b/framework/Itip/lib/Horde/Itip/Response.php
@@ -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
index 000000000..9fa8308ce
--- /dev/null
+++ b/framework/Itip/lib/Horde/Itip/Response/Options.php
@@ -0,0 +1,82 @@
+
+ * @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
+ * @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
index 000000000..e2a759b72
--- /dev/null
+++ b/framework/Itip/lib/Horde/Itip/Response/Options/Base.php
@@ -0,0 +1,68 @@
+
+ * @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
+ * @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
index 000000000..340c4fdd1
--- /dev/null
+++ b/framework/Itip/lib/Horde/Itip/Response/Options/Horde.php
@@ -0,0 +1,93 @@
+
+ * @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
+ * @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
index 000000000..90fe6270c
--- /dev/null
+++ b/framework/Itip/lib/Horde/Itip/Response/Options/Kolab.php
@@ -0,0 +1,63 @@
+
+ * @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
+ * @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
diff --git a/framework/Itip/lib/Horde/Itip/Response/Type.php b/framework/Itip/lib/Horde/Itip/Response/Type.php
index 9a3ffd507..73d53c308 100644
--- a/framework/Itip/lib/Horde/Itip/Response/Type.php
+++ b/framework/Itip/lib/Horde/Itip/Response/Type.php
@@ -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
diff --git a/framework/Itip/lib/Horde/Itip/Response/Type/Base.php b/framework/Itip/lib/Horde/Itip/Response/Type/Base.php
index cd65a2943..2b61dad70 100644
--- a/framework/Itip/lib/Horde/Itip/Response/Type/Base.php
+++ b/framework/Itip/lib/Horde/Itip/Response/Type/Base.php
@@ -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
);
}
}
diff --git a/framework/Itip/package.xml b/framework/Itip/package.xml
index e92eaa8ea..c771a7dd0 100644
--- a/framework/Itip/package.xml
+++ b/framework/Itip/package.xml
@@ -47,8 +47,15 @@
+
+
+
+
+
+
+
@@ -109,6 +116,11 @@
+
+
+
+
+
diff --git a/imp/lib/Mime/Viewer/Itip.php b/imp/lib/Mime/Viewer/Itip.php
index 58b3740a3..afc9256e5 100644
--- a/imp/lib/Mime/Viewer/Itip.php
+++ b/imp/lib/Mime/Viewer/Itip.php
@@ -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."));
}
diff --git a/imp/lib/tests/Imp/Stub/Identity.php b/imp/lib/tests/Imp/Stub/Identity.php
index 6edd15e14..8c54f2528 100644
--- a/imp/lib/tests/Imp/Stub/Identity.php
+++ b/imp/lib/tests/Imp/Stub/Identity.php
@@ -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';
diff --git a/imp/lib/tests/Imp/Unit/Mime/Viewer/ItipTest.php b/imp/lib/tests/Imp/Unit/Mime/Viewer/ItipTest.php
index b137e0751..6d2968816 100644
--- a/imp/lib/tests/Imp/Unit/Mime/Viewer/ItipTest.php
+++ b/imp/lib/tests/Imp/Unit/Mime/Viewer/ItipTest.php
@@ -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" ', $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;
}
--
2.11.0