[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