From: Gunnar Wrobel Date: Mon, 16 Aug 2010 22:26:10 +0000 (+0200) Subject: Converted Imp to use the Horde_Itip library. X-Git-Url: https://git.internetallee.de/?a=commitdiff_plain;h=25da03fe4764d08ca7140c93189db439b5d42f1b;p=horde.git Converted Imp to use the Horde_Itip library. --- 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; }