File 0001-Add-support-for-exceptions.patch of Package libkolab

From ad9c52c79a3b5de87bfa7a3f45e207e6d6ee1d4b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Sandro=20Knau=C3=9F?= <mail@sandroknauss.de>
Date: Wed, 18 Feb 2015 16:54:47 +0100
Subject: [PATCH 1/2] Add support for exceptions.

Read/Write Exceptions to iCal.

KOLAB: #4618
---
 icalendar/icalendar.cpp                       | 34 +++++++++++++++--
 tests/icalendartest.cpp                       | 53 ++++++++++++++++++++++++++-
 tests/icalendartest.h                         |  3 ++
 tests/testfiles/v3/event/errorswithsameid.ics | 45 +++++++++++++++++++++++
 tests/testfiles/v3/event/exceptions.ics       | 35 ++++++++++++++++++
 5 files changed, 166 insertions(+), 4 deletions(-)
 create mode 100644 tests/testfiles/v3/event/errorswithsameid.ics
 create mode 100644 tests/testfiles/v3/event/exceptions.ics

diff --git a/icalendar/icalendar.cpp b/icalendar/icalendar.cpp
index be7c0e6..3ed41ae 100644
--- a/icalendar/icalendar.cpp
+++ b/icalendar/icalendar.cpp
@@ -39,13 +39,21 @@ std::string toICal(const std::vector<Event> &events)
     KCalCore::Calendar::Ptr calendar(new KCalCore::MemoryCalendar(Kolab::Conversion::getTimeSpec(true, std::string())));
     foreach (const Event &event, events) {
         KCalCore::Event::Ptr kcalEvent = Conversion::toKCalCore(event);
-        kcalEvent->setCreated(KDateTime::currentUtcDateTime()); //sets dtstamp
+        if (!kcalEvent->created().isValid()) {
+            kcalEvent->setCreated(KDateTime::currentUtcDateTime()); //sets dtstamp
+        }
         calendar->addEvent(kcalEvent);
+        foreach(const Event &exception, event.exceptions()) {
+            KCalCore::Event::Ptr kcalException = Conversion::toKCalCore(exception);
+            if (!kcalException->created().isValid()) {
+                kcalException->setCreated(KDateTime::currentUtcDateTime()); //sets dtstamp
+            }
+            calendar->addEvent(kcalException);
+        }
     }
     KCalCore::ICalFormat format;
     format.setApplication("libkolab", LIBKOLAB_LIB_VERSION_STRING);
 //     qDebug() << format.createScheduleMessage(calendar->events().first(), KCalCore::iTIPRequest);
-
     return Conversion::toStdString(format.toString(calendar));
     
 }
@@ -57,8 +65,28 @@ std::vector< Event > fromICalEvents(const std::string &input)
     format.setApplication("libkolab", LIBKOLAB_LIB_VERSION_STRING);
     format.fromString(calendar, Conversion::fromStdString(input));
     std::vector<Event> events;
+    QMap< QString, int > uidMap;
     foreach (const KCalCore::Event::Ptr &event, calendar->events()) {
-        events.push_back(Conversion::fromKCalCore(*event));
+        Event e = Conversion::fromKCalCore(*event);
+        if (!e.recurrenceID().isValid()) {
+            if (uidMap.contains(event->uid())) {
+                e.setExceptions(events.at(uidMap[event->uid()]).exceptions());
+                events[uidMap[event->uid()]] = e;
+            } else {
+                events.push_back(e);
+                uidMap.insert(event->uid(), events.size()-1);
+            }
+        } else {
+            if (!uidMap.contains(event->uid())) {
+                Event e;
+                e.setUid("");
+                events.push_back(e);
+                uidMap.insert(event->uid(), events.size()-1);
+            }
+            std::vector<Event> exceptions = events.at( uidMap[event->uid()]).exceptions();
+            exceptions.push_back(e);
+            events.at(uidMap[event->uid()]).setExceptions(exceptions);
+        }
     }
     return events;
 }
diff --git a/tests/icalendartest.cpp b/tests/icalendartest.cpp
index 1e13d65..b1dd60b 100644
--- a/tests/icalendartest.cpp
+++ b/tests/icalendartest.cpp
@@ -24,6 +24,8 @@
 #include "icalendar/icalendar.h"
 
 #include "testhelpers.h"
+#include "testutils.h"
+#include <kolabformat/kolabobject.h>
 
 void ICalendarTest::testFromICalEvent()
 {
@@ -32,11 +34,60 @@ void ICalendarTest::testFromICalEvent()
     ev1.setStart(Kolab::cDateTime(2011,10,10,12,1,1,true));
     ev1.setEnd(Kolab::cDateTime(2011,10,11,12,1,1,true));
     events.push_back(ev1);
-    events.push_back(ev1);
     const std::vector<Kolab::Event> &result = Kolab::fromICalEvents(Kolab::toICal(events));
     qDebug() << QString::fromStdString(Kolab::toICal(result));
 }
 
+void ICalendarTest::testFromICalEventWithExceptions()
+{
+    QFile icalFile( getPath("v3/event/exceptions.ics") );
+    QVERIFY( icalFile.open( QFile::ReadOnly ) );
+    std::vector<Kolab::Event> events = Kolab::fromICalEvents(icalFile.readAll().constData());
+    QCOMPARE((int)events.size(), 1);
+    Kolab::Event out = events.at(0);
+    QCOMPARE((int)out.exceptions().size(), 2);
+}
+
+void ICalendarTest::testFromICalEventErrorsWithSameID()
+{
+    /* This is not a really usecase, it should make sure,
+     * that the underlying KCalCore strips out events with same uid/or recurrenceID.
+     * 2015/02/18: KCaclCore is only returns one event with one exception for the icalFile
+     * testdata/v3/event/errorswithsameid.ics
+     */
+    QFile icalFile( getPath("v3/event/errorswithsameid.ics") );
+    QVERIFY( icalFile.open( QFile::ReadOnly ) );
+    std::vector<Kolab::Event> events = Kolab::fromICalEvents(icalFile.readAll().constData());
+    QCOMPARE((int)events.size(), 1);
+    Kolab::Event out = events.at(0);
+    QCOMPARE((int)out.exceptions().size(), 1);
+}
+
+void ICalendarTest::testReadWriteForEventWithExceptions()
+{
+    std::vector<Kolab::Event> events;
+    Kolab::Event ev1;
+    ev1.setUid("uid");
+    ev1.setStart(Kolab::cDateTime(2011,10,10,12,1,1,true));
+    ev1.setEnd(Kolab::cDateTime(2011,10,11,12,1,1,true));
+    std::vector<Kolab::Event> exceptions;
+    Kolab::Event ex1;
+    ex1.setUid(ev1.uid());
+    ex1.setRecurrenceID(Kolab::cDateTime(2011,10,11,12,1,1,true), false);
+    exceptions.push_back(ex1);
+    ev1.setExceptions(exceptions);
+    Kolab::RecurrenceRule rrule;
+    rrule.setInterval(1);
+    rrule.setFrequency(Kolab::RecurrenceRule::Daily);
+    ev1.setRecurrenceRule(rrule);
+    events.push_back(ev1);
+    const std::vector<Kolab::Event> &result = Kolab::fromICalEvents(Kolab::toICal(events));
+    Kolab::Event out = result.at(0);
+    QCOMPARE((int)result.size(), 1);
+    QCOMPARE((int)out.exceptions().size(), 1);
+    QCOMPARE(out.exceptions().at(0).recurrenceID(), ex1.recurrenceID());
+}
+
 void ICalendarTest::testToICal()
 {
     std::vector<Kolab::Event> events;
diff --git a/tests/icalendartest.h b/tests/icalendartest.h
index 4f82ecc..7e1f405 100644
--- a/tests/icalendartest.h
+++ b/tests/icalendartest.h
@@ -27,6 +27,9 @@ private slots:
 //     void testEventConflict_data();
     void testToICal();
     void testFromICalEvent();
+    void testFromICalEventWithExceptions();
+    void testReadWriteForEventWithExceptions();
+    void testFromICalEventErrorsWithSameID();
 
     void testToITip();
     void testToIMip();
diff --git a/tests/testfiles/v3/event/errorswithsameid.ics b/tests/testfiles/v3/event/errorswithsameid.ics
new file mode 100644
index 0000000..1aa12a3
--- /dev/null
+++ b/tests/testfiles/v3/event/errorswithsameid.ics
@@ -0,0 +1,45 @@
+BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//Test//
+CALSCALE:GREGORIAN
+BEGIN:VEVENT
+UID:D5A98F4AD36FE911EC7FB8F89209C4E3-FCBB6C4091F28CA0
+DTSTAMP;VALUE=DATE-TIME:20150216T000000Z
+CREATED;VALUE=DATE-TIME:20150216T000000Z
+LAST-MODIFIED;VALUE=DATE-TIME:20150216T000000Z
+RRULE:FREQ=DAILY;INTERVAL=1
+SUMMARY:Daily
+DTSTART;VALUE=DATE-TIME:20150101T010000Z
+DTEND;VALUE=DATE-TIME:20150101T020000Z
+END:VEVENT
+BEGIN:VEVENT
+UID:D5A98F4AD36FE911EC7FB8F89209C4E3-FCBB6C4091F28CA0
+DTSTAMP;VALUE=DATE-TIME:20150216T000000Z
+CREATED;VALUE=DATE-TIME:20150216T000000Z
+LAST-MODIFIED;VALUE=DATE-TIME:20150216T000000Z
+RECURRENCE-ID;VALUE=DATE-TIME;RANGE=THISANDFUTURE:20150103T000000Z
+SUMMARY:thisandfuture
+DTSTART;VALUE=DATE-TIME:20150103T020000Z
+DTEND;VALUE=DATE-TIME:20150103T030000Z
+END:VEVENT
+BEGIN:VEVENT
+UID:D5A98F4AD36FE911EC7FB8F89209C4E3-FCBB6C4091F28CA0
+DTSTAMP;VALUE=DATE-TIME:20150216T000000Z
+CREATED;VALUE=DATE-TIME:20150216T000000Z
+LAST-MODIFIED;VALUE=DATE-TIME:20150216T000000Z
+RRULE:FREQ=DAILY;INTERVAL=1
+SUMMARY:Invalid second incidence
+DTSTART;VALUE=DATE-TIME:20150101T050000Z
+DTEND;VALUE=DATE-TIME:20150101T060000Z
+END:VEVENT
+BEGIN:VEVENT
+UID:D5A98F4AD36FE911EC7FB8F89209C4E3-FCBB6C4091F28CA0
+DTSTAMP;VALUE=DATE-TIME:20150216T000000Z
+CREATED;VALUE=DATE-TIME:20150216T000000Z
+LAST-MODIFIED;VALUE=DATE-TIME:20150216T000000Z
+RECURRENCE-ID;VALUE=DATE-TIME:20150103T000000Z
+SUMMARY: same recurence id like the one with THISANDFUTURE
+DTSTART;VALUE=DATE-TIME:20150103T030000Z
+DTEND;VALUE=DATE-TIME:20150103T040000Z
+END:VEVENT
+END:VCALENDAR
\ No newline at end of file
diff --git a/tests/testfiles/v3/event/exceptions.ics b/tests/testfiles/v3/event/exceptions.ics
new file mode 100644
index 0000000..60776c6
--- /dev/null
+++ b/tests/testfiles/v3/event/exceptions.ics
@@ -0,0 +1,35 @@
+BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//Test//
+CALSCALE:GREGORIAN
+BEGIN:VEVENT
+UID:D5A98F4AD36FE911EC7FB8F89209C4E3-FCBB6C4091F28CA0
+DTSTAMP;VALUE=DATE-TIME:20150216T000000Z
+CREATED;VALUE=DATE-TIME:20150216T000000Z
+LAST-MODIFIED;VALUE=DATE-TIME:20150216T000000Z
+RRULE:FREQ=DAILY;INTERVAL=1
+SUMMARY:Daily
+DTSTART;VALUE=DATE-TIME:20150101T010000Z
+DTEND;VALUE=DATE-TIME:20150101T020000Z
+END:VEVENT
+BEGIN:VEVENT
+UID:D5A98F4AD36FE911EC7FB8F89209C4E3-FCBB6C4091F28CA0
+DTSTAMP;VALUE=DATE-TIME:20150216T000000Z
+CREATED;VALUE=DATE-TIME:20150216T000000Z
+LAST-MODIFIED;VALUE=DATE-TIME:20150216T000000Z
+RECURRENCE-ID;VALUE=DATE-TIME;RANGE=THISANDFUTURE:20150103T000000Z
+SUMMARY:thisandfuture
+DTSTART;VALUE=DATE-TIME:20150103T020000Z
+DTEND;VALUE=DATE-TIME:20150103T030000Z
+END:VEVENT
+BEGIN:VEVENT
+UID:D5A98F4AD36FE911EC7FB8F89209C4E3-FCBB6C4091F28CA0
+DTSTAMP;VALUE=DATE-TIME:20150216T000000Z
+CREATED;VALUE=DATE-TIME:20150216T000000Z
+LAST-MODIFIED;VALUE=DATE-TIME:20150216T000000Z
+RECURRENCE-ID;VALUE=DATE-TIME:20150104T000000Z
+SUMMARY: exception
+DTSTART;VALUE=DATE-TIME:20150104T030000Z
+DTEND;VALUE=DATE-TIME:20150104T040000Z
+END:VEVENT
+END:VCALENDAR
\ No newline at end of file
-- 
2.1.0

openSUSE Build Service is sponsored by