[Pkg-owncloud-commits] [php-sabre-vobject] 02/106: First strides into iTip related functionality.
David Prévot
taffit at moszumanska.debian.org
Fri Aug 22 15:10:53 UTC 2014
This is an automated email from the git hooks/post-receive script.
taffit pushed a commit to branch master
in repository php-sabre-vobject.
commit da85f69362e72cd22fdb7dc92b2352dd255fc49d
Author: Evert Pot <me at evertpot.com>
Date: Wed Jul 16 17:42:53 2014 -0400
First strides into iTip related functionality.
---
lib/Sabre/VObject/ITip/Broker.php | 176 ++++++++++++++++++++++++
lib/Sabre/VObject/ITip/Message.php | 95 +++++++++++++
tests/Sabre/VObject/ITip/BrokerTest.php | 230 ++++++++++++++++++++++++++++++++
3 files changed, 501 insertions(+)
diff --git a/lib/Sabre/VObject/ITip/Broker.php b/lib/Sabre/VObject/ITip/Broker.php
new file mode 100644
index 0000000..f0c2349
--- /dev/null
+++ b/lib/Sabre/VObject/ITip/Broker.php
@@ -0,0 +1,176 @@
+<?php
+
+namespace Sabre\VObject\ITip;
+
+use Sabre\VObject\Component\VCalendar;
+use Sabre\VObject\Reader;
+
+/**
+ * The ITip class is a utility class that helps with processing so-called iTip
+ * messages.
+ *
+ * ITip is defined in rfc5546, stands for iCalendar Transport-Independent
+ * Interoperability Protocol, and describes the underlying mechanism for
+ * using iCalendar for scheduling for for example through email (also known as
+ * IMip) and CalDAV Scheduling.
+ *
+ * This class helps by:
+ *
+ * 1. Creating individual invites based on an iCalendar event for each
+ * attendee.
+ * 2. Generating invite updates based on an iCalendar update. This may result
+ * in new invites, updates and cancellations for attendees, if that list
+ * changed.
+ * 3. On the receiving end, it can create a local iCalendar event based on
+ * a received invite.
+ * 4. It can also process an invite update on a local event, ensuring that any
+ * overridden properties from attendees are retained.
+ * 5. It can create a accepted or declined iTip reply based on an invite.
+ * 6. It can process a reply from an invite and update an events attendee
+ * status based on a reply.
+ *
+ * @copyright Copyright (C) 2007-2014 fruux GmbH. All rights reserved.
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class Broker {
+
+ /**
+ * This function parses a VCALENDAR object, and if the object had an
+ * organizer and attendees, it will generate iTip messages for every
+ * attendee.
+ *
+ * If the passed object did not have any attendees, no messages will be
+ * created.
+ *
+ * @param VCalendar|string $calendar
+ * @return array
+ */
+ public function createEvent($calendar) {
+
+ if (is_string($calendar)) {
+ $calendar = Reader::read($calendar);
+ }
+
+ if (!isset($calendar->VEVENT)) {
+ // We only support events at the moment
+ return array();
+ }
+
+ // Events that don't have an organizer or attendees don't generate
+ // messages.
+ if (!isset($calendar->VEVENT->ORGANIZER) || !isset($calendar->VEVENT->ATTENDEE)) {
+ return array();
+ }
+
+ $uid = null;
+ $organizer = null;
+
+ // Now we need to collect a list of attendees, and which instances they
+ // are a part of.
+ $attendees = array();
+
+ $instances = array();
+
+ foreach($calendar->VEVENT as $vevent) {
+ if (is_null($uid)) {
+ $uid = $vevent->UID->getValue();
+ } else {
+ if ($uid !== $vevent->UID->getValue()) {
+ throw new ITipException('If a calendar contained more than one event, they must have the same UID.');
+ }
+ }
+ if (is_null($organizer)) {
+ $organizer = $vevent->ORGANIZER->getValue();
+ $organizerName = isset($vevent->ORGANIZER['CN'])?$vevent->ORGANIZER['CN']:null;
+ } else {
+ if ($organizer !== $vevent->ORGANIZER->getValue()) {
+ throw new ITipException('Every instance of the event must have the same organizer.');
+ }
+ }
+
+ $value = isset($vevent->{'RECURRENCE-ID'})?$vevent->{'RECURRENCE-ID'}->getValue():'master';
+ foreach($vevent->ATTENDEE as $attendee) {
+
+ if (isset($attendees[$attendee->getValue()])) {
+ $attendees[$attendee->getValue()]['instances'][] = $value;
+ } else {
+ $attendees[$attendee->getValue()] = array(
+ 'href' => $attendee->getValue(),
+ 'instances' => array($value),
+ 'name' => isset($attendee['CN'])?$attendee['CN']:null,
+ );
+ }
+
+ }
+ $instances[$value] = $vevent;
+
+ }
+
+ // Now we generate an iTip message for each attendee.
+ $messages = array();
+
+ foreach($attendees as $attendee) {
+
+ // An organizer can also be an attendee. We should not generate any
+ // messages for those.
+ if ($attendee['href']===$organizer) {
+ continue;
+ }
+
+ $message = new Message();
+ $message->uid = $uid;
+ $message->component = 'VEVENT';
+ $message->method = 'REQUEST';
+ $message->sender = $organizer;
+ $message->senderName = $organizerName;
+ $message->recipient = $attendee['href'];
+ $message->recipientName = $attendee['name'];
+
+ // Creating the new iCalendar body.
+ $icalMsg = new VCalendar();
+ $icalMsg->METHOD = $message->method;
+
+ foreach($attendee['instances'] as $instanceId) {
+
+ $currentEvent = clone $instances[$instanceId];
+ if ($instanceId === 'master') {
+
+ // We need to find a list of events that the attendee
+ // is not a part of to add to the list of exceptions.
+ $exceptions = array();
+ foreach($instances as $instanceId=>$vevent) {
+ if (!in_array($instanceId, $attendee['instances'])) {
+ $exceptions[] = $instanceId;
+ }
+ }
+
+ // If there were exceptions, we need to add it to an
+ // existing EXDATE property, if it exists.
+ if ($exceptions) {
+ if (isset($currentEvent->EXDATE)) {
+ $currentEvent->EXDATE->setParts(array_merge(
+ $currentEvent->EXDATE->getParts(),
+ $exceptions
+ ));
+ } else {
+ $currentEvent->EXDATE = $exceptions;
+ }
+ }
+
+ }
+
+ $icalMsg->add($currentEvent);
+
+ }
+
+ $message->message = $icalMsg;
+ $messages[] = $message;
+
+ }
+
+ return $messages;
+
+ }
+
+}
diff --git a/lib/Sabre/VObject/ITip/Message.php b/lib/Sabre/VObject/ITip/Message.php
new file mode 100644
index 0000000..89b96b5
--- /dev/null
+++ b/lib/Sabre/VObject/ITip/Message.php
@@ -0,0 +1,95 @@
+<?php
+
+namespace Sabre\VObject\ITip;
+
+/**
+ * This class represents an iTip message
+ *
+ * A message holds all the information relevant to the message, including the
+ * object itself.
+ *
+ * It should for the most part be treated as immutable.
+ *
+ * @copyright Copyright (C) 2007-2014 fruux GmbH. All rights reserved.
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class Message {
+
+ /**
+ * The object's UID
+ *
+ * @var string
+ */
+ public $uid;
+
+ /**
+ * The component type, such as VEVENT
+ *
+ * @var string
+ */
+ public $component;
+
+ /**
+ * Contains the ITip method, which is something like REQUEST, REPLY or
+ * CANCEL.
+ *
+ * @var string
+ */
+ public $method;
+
+ /**
+ * The senders' email address.
+ *
+ * Note that this does not imply that this has to be used in a From: field
+ * if the message is sent by email. It may also be populated in Reply-To:
+ * or not at all.
+ *
+ * @var string
+ */
+ public $sender;
+
+ /**
+ * The name of the sender. This is often populated from a CN parameter from
+ * either the ORGANIZER or ATTENDEE, depending on the message.
+ *
+ * @var string|null
+ */
+ public $senderName;
+
+ /**
+ * The recipient's email address.
+ *
+ * @var string
+ */
+ public $recipient;
+
+ /**
+ * The name of the recipient. This is usually populated with the CN
+ * parameter from the ATTENDEE or ORGANIZER property, if it's available.
+ *
+ * @var string|null
+ */
+ public $recipientName;
+
+ /**
+ * After the message has been delivered, this should contain a string such
+ * as : 1.1;Sent or 1.2;Delivered.
+ *
+ * In case of a failure, this will hold the error status code.
+ *
+ * See:
+ * http://tools.ietf.org/html/rfc6638#section-7.3
+ *
+ * @var string
+ */
+ public $scheduleStatus;
+
+ /**
+ * The iCalendar / iTip body.
+ *
+ * @var \Sabre\VObject\Component\VCalendar
+ */
+ public $message;
+
+}
diff --git a/tests/Sabre/VObject/ITip/BrokerTest.php b/tests/Sabre/VObject/ITip/BrokerTest.php
new file mode 100644
index 0000000..d02f62e
--- /dev/null
+++ b/tests/Sabre/VObject/ITip/BrokerTest.php
@@ -0,0 +1,230 @@
+<?php
+
+namespace Sabre\VObject\ITip;
+
+class BrokerTest extends \PHPUnit_Framework_TestCase {
+
+ function testNoAttendee() {
+
+ $message = <<<ICS
+BEGIN:VCALENDAR
+BEGIN:VEVENT
+UID:foobar
+END:VEVENT
+END:VCALENDAR
+ICS;
+
+ $result = $this->parse($message);
+
+ }
+
+ function testVTODO() {
+
+ $message = <<<ICS
+BEGIN:VCALENDAR
+BEGIN:VTODO
+UID:foobar
+END:VTODO
+END:VCALENDAR
+ICS;
+
+ $result = $this->parse($message);
+
+ }
+
+ function testSimpleInvite() {
+
+ $message = <<<ICS
+BEGIN:VCALENDAR
+VERSION:2.0
+BEGIN:VEVENT
+UID:foobar
+ORGANIZER;CN=Strunk:mailto:strunk at example.org
+ATTENDEE;CN=White:mailto:white at example.org
+END:VEVENT
+END:VCALENDAR
+ICS;
+
+ $version = \Sabre\VObject\Version::VERSION;
+ $expectedMessage = <<<ICS
+BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//Sabre//Sabre VObject $version//EN
+CALSCALE:GREGORIAN
+METHOD:REQUEST
+BEGIN:VEVENT
+UID:foobar
+ORGANIZER;CN=Strunk:mailto:strunk at example.org
+ATTENDEE;CN=White:mailto:white at example.org
+END:VEVENT
+END:VCALENDAR
+ICS;
+
+ $expected = array(
+ array(
+ 'uid' => 'foobar',
+ 'method' => 'REQUEST',
+ 'component' => 'VEVENT',
+ 'sender' => 'mailto:strunk at example.org',
+ 'senderName' => 'Strunk',
+ 'recipient' => 'mailto:white at example.org',
+ 'recipientName' => 'White',
+ 'message' => $expectedMessage,
+ ),
+ );
+
+ $result = $this->parse($message, $expected);
+
+ }
+
+ function testRecurrenceInvite() {
+
+ $message = <<<ICS
+BEGIN:VCALENDAR
+VERSION:2.0
+BEGIN:VEVENT
+UID:foobar
+ORGANIZER;CN=Strunk:mailto:strunk at example.org
+ATTENDEE;CN=One:mailto:one at example.org
+ATTENDEE;CN=Two:mailto:two at example.org
+DTSTART:20140716T120000Z
+RRULE:FREQ=DAILY
+EXDATE:20140717T120000Z
+END:VEVENT
+BEGIN:VEVENT
+UID:foobar
+RECURRENCE-ID:20140718T120000Z
+ORGANIZER;CN=Strunk:mailto:strunk at example.org
+ATTENDEE;CN=Two:mailto:two at example.org
+ATTENDEE;CN=Three:mailto:three at example.org
+DTSTART:20140718T120000Z
+END:VEVENT
+END:VCALENDAR
+ICS;
+
+ $version = \Sabre\VObject\Version::VERSION;
+
+ $expected = array(
+ array(
+ 'uid' => 'foobar',
+ 'method' => 'REQUEST',
+ 'component' => 'VEVENT',
+ 'sender' => 'mailto:strunk at example.org',
+ 'senderName' => 'Strunk',
+ 'recipient' => 'mailto:one at example.org',
+ 'recipientName' => 'One',
+ 'message' => <<<ICS
+BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//Sabre//Sabre VObject $version//EN
+CALSCALE:GREGORIAN
+METHOD:REQUEST
+BEGIN:VEVENT
+UID:foobar
+ORGANIZER;CN=Strunk:mailto:strunk at example.org
+ATTENDEE;CN=One:mailto:one at example.org
+ATTENDEE;CN=Two:mailto:two at example.org
+DTSTART:20140716T120000Z
+RRULE:FREQ=DAILY
+EXDATE:20140717T120000Z,20140718T120000Z
+END:VEVENT
+END:VCALENDAR
+ICS
+
+ ),
+ array(
+ 'uid' => 'foobar',
+ 'method' => 'REQUEST',
+ 'component' => 'VEVENT',
+ 'sender' => 'mailto:strunk at example.org',
+ 'senderName' => 'Strunk',
+ 'recipient' => 'mailto:two at example.org',
+ 'recipientName' => 'Two',
+ 'message' => <<<ICS
+BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//Sabre//Sabre VObject $version//EN
+CALSCALE:GREGORIAN
+METHOD:REQUEST
+BEGIN:VEVENT
+UID:foobar
+ORGANIZER;CN=Strunk:mailto:strunk at example.org
+ATTENDEE;CN=One:mailto:one at example.org
+ATTENDEE;CN=Two:mailto:two at example.org
+DTSTART:20140716T120000Z
+RRULE:FREQ=DAILY
+EXDATE:20140717T120000Z
+END:VEVENT
+BEGIN:VEVENT
+UID:foobar
+RECURRENCE-ID:20140718T120000Z
+ORGANIZER;CN=Strunk:mailto:strunk at example.org
+ATTENDEE;CN=Two:mailto:two at example.org
+ATTENDEE;CN=Three:mailto:three at example.org
+DTSTART:20140718T120000Z
+END:VEVENT
+END:VCALENDAR
+ICS
+
+ ),
+ array(
+ 'uid' => 'foobar',
+ 'method' => 'REQUEST',
+ 'component' => 'VEVENT',
+ 'sender' => 'mailto:strunk at example.org',
+ 'senderName' => 'Strunk',
+ 'recipient' => 'mailto:three at example.org',
+ 'recipientName' => 'Three',
+ 'message' => <<<ICS
+BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//Sabre//Sabre VObject $version//EN
+CALSCALE:GREGORIAN
+METHOD:REQUEST
+BEGIN:VEVENT
+UID:foobar
+RECURRENCE-ID:20140718T120000Z
+ORGANIZER;CN=Strunk:mailto:strunk at example.org
+ATTENDEE;CN=Two:mailto:two at example.org
+ATTENDEE;CN=Three:mailto:three at example.org
+DTSTART:20140718T120000Z
+END:VEVENT
+END:VCALENDAR
+ICS
+
+ ),
+ );
+
+ $result = $this->parse($message, $expected);
+
+ }
+
+ function parse($message, $expected = array()) {
+
+ $broker = new Broker();
+ $result = $broker->createEvent($message);
+
+ $this->assertEquals(count($expected), count($result));
+
+ foreach($expected as $index=>$ex) {
+
+ $message = $result[$index];
+
+ foreach($ex as $key=>$val) {
+
+ if ($key==='message') {
+ $this->assertEquals(
+ str_replace("\n", "\r\n", $val),
+ rtrim($message->message->serialize(), "\r\n")
+ );
+ } else {
+ $this->assertEquals($val, $message->$key);
+ }
+
+ }
+
+ }
+
+ }
+
+}
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-owncloud/php-sabre-vobject.git
More information about the Pkg-owncloud-commits
mailing list