diff --git a/CMakeLists.txt b/CMakeLists.txt index 436f78659..3a251e820 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,172 +1,172 @@ cmake_minimum_required(VERSION 3.5) set(PIM_VERSION "5.11.40") set(KDEPIM_RUNTIME_VERSION_NUMBER ${PIM_VERSION}) project(kdepim-runtime VERSION ${KDEPIM_RUNTIME_VERSION_NUMBER}) if (POLICY CMP0053) cmake_policy(SET CMP0053 NEW) endif() ############### KDEPIM-Runtime version ################ # KDEPIM_RUNTIME_VERSION # Version scheme: "x.y.z build". # # x is the version number. # y is the major release number. # z is the minor release number. # # "x.y.z" follow the kdelibs version kdepim is released with. # # If "z" is 0, it the version is "x.y" # # KDEPIM_DEV_VERSION # is empty for final versions. For development versions "build" is # something like "pre", "", "alpha2", "beta1", "beta2", "rc1", "rc2". # # Examples in chronological order: # # 3.0 # 3.0.1 # 3.1 # 3.1 beta1 # 3.1 beta2 # 3.1 rc1 # 3.1 # 3.1.1 # 3.2 pre # 3.2 set(KDEPIM_DEV_VERSION alpha) # add an extra space if(DEFINED KDEPIM_DEV_VERSION) set(KDEPIM_DEV_VERSION " ${KDEPIM_DEV_VERSION}") endif() set(KDEPIM_RUNTIME_VERSION "${KDEPIM_RUNTIME_VERSION_NUMBER}${KDEPIM_DEV_VERSION}") configure_file(kdepim-runtime-version.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/kdepim-runtime-version.h @ONLY) set(KF5_MIN_VERSION "5.58.0") find_package(ECM ${KF5_MIN_VERSION} CONFIG REQUIRED) set(CMAKE_MODULE_PATH ${kdepim-runtime_SOURCE_DIR}/cmake/ ${ECM_MODULE_PATH}) include(ECMSetupVersion) include(FeatureSummary) include(KDEInstallDirs) include(KDECMakeSettings) include(KDEFrameworkCompilerSettings NO_POLICY_SCOPE) include(ECMInstallIcons) include(ECMQtDeclareLoggingCategory) include(GenerateExportHeader) set(QT_REQUIRED_VERSION "5.10.0") set(KDEPIMRUNTIME_LIB_VERSION "${KDEPIM_RUNTIME_VERSION_NUMBER}") set(KDEPIMRUNTIME_LIB_SOVERSION "5") set(AKONADI_VERSION "5.11.40") set(KCONTACTS_LIB_VERSION "5.11.40") -set(KCALENDARCORE_LIB_VERSION "5.11.40") +set(KCALENDARCORE_LIB_VERSION "5.11.42") set(IDENTITYMANAGEMENT_LIB_VERSION "5.11.40") set(KMAILTRANSPORT_LIB_VERSION "5.11.40") set(CALENDARUTILS_LIB_VERSION "5.11.40") set(KDAV_LIB_VERSION "5.11.40") set(KIMAP_LIB_VERSION "5.11.40") set(KMBOX_LIB_VERSION "5.11.40") set(AKONADICALENDAR_LIB_VERSION "5.11.40") set(KONTACTINTERFACE_LIB_VERSION "5.11.40") set(AKONADIKALARM_LIB_VERSION "5.11.40") set(KMIME_LIB_VERSION "5.11.40") set(XMLRPCCLIENT_LIB_VERSION "5.11.40") set(KCONTACTS_LIB_VERSION "5.11.40") set(AKONADIMIME_LIB_VERSION "5.11.40") set(AKONADICONTACT_LIB_VERSION "5.11.40") set(AKONADINOTE_LIB_VERSION "5.11.40") set(PIMCOMMON_LIB_VERSION "5.11.40") set(KGAPI_LIB_VERSION "5.11.40") set( SharedMimeInfo_MINIMUM_VERSION "1.3" ) find_package(SharedMimeInfo ${SharedMimeInfo_MINIMUM_VERSION} REQUIRED) find_package(Sasl2) set_package_properties(Sasl2 PROPERTIES TYPE REQUIRED) find_package(Qca-qt5) option(KDEPIM_RUN_AKONADI_TEST "Enable autotest based on Akonadi." TRUE) # QT5 package find_package(Qt5 ${QT_REQUIRED_VERSION} CONFIG REQUIRED TextToSpeech Network Widgets Test XmlPatterns DBus WebEngineWidgets NetworkAuth) if (NOT Qca-qt5_FOUND) message(STATUS "QCA not found, public key authentication will not be supported") else() add_definitions(-DHAVE_QCA) endif() # KF5 package find_package(KF5Config ${KF5_MIN_VERSION} CONFIG REQUIRED) find_package(KF5ConfigWidgets ${KF5_MIN_VERSION} CONFIG REQUIRED) find_package(KF5NotifyConfig ${KF5_MIN_VERSION} CONFIG REQUIRED) find_package(KF5KIO ${KF5_MIN_VERSION} CONFIG REQUIRED) find_package(KF5ItemModels ${KF5_MIN_VERSION} CONFIG REQUIRED) find_package(KF5Codecs ${KF5_MIN_VERSION} CONFIG REQUIRED) find_package(KF5WindowSystem ${KF5_MIN_VERSION} CONFIG REQUIRED) find_package(KF5TextWidgets ${KF5_MIN_VERSION} CONFIG REQUIRED) # for KPluralHandlingSpinBox find_package(KF5Notifications ${KF5_MIN_VERSION} CONFIG REQUIRED) # pop3, ews find_package(KF5DocTools ${KF5_MIN_VERSION} CONFIG REQUIRED) # pop3 find_package(KF5Holidays ${KF5_MIN_VERSION} CONFIG REQUIRED) find_package(KF5DBusAddons ${KF5_MIN_VERSION} CONFIG REQUIRED) find_package(KF5IconThemes ${KF5_MIN_VERSION} CONFIG REQUIRED) find_package(KF5KDELibs4Support ${KF5_MIN_VERSION} CONFIG REQUIRED) # KdepimLibs package find_package(KF5Akonadi ${AKONADI_VERSION} CONFIG REQUIRED) find_package(KPimKDAV ${KDAV_LIB_VERSION} CONFIG REQUIRED) find_package(KF5Mime ${KMIME_LIB_VERSION} CONFIG REQUIRED) find_package(KF5AkonadiMime ${AKONADIMIME_LIB_VERSION} CONFIG REQUIRED) find_package(KF5MailTransportAkonadi ${KMAILTRANSPORT_LIB_VERSION} CONFIG REQUIRED) find_package(KF5IdentityManagement ${IDENTITYMANAGEMENT_LIB_VERSION} CONFIG REQUIRED) find_package(KF5AkonadiContact ${AKONADICONTACT_LIB_VERSION} CONFIG REQUIRED) find_package(KF5Contacts ${KCONTACTS_LIB_VERSION} CONFIG REQUIRED) find_package(KF5AlarmCalendar ${AKONADIKALARM_LIB_VERSION} CONFIG REQUIRED) find_package(KF5CalendarCore ${KCALENDARCORE_LIB_VERSION} CONFIG REQUIRED) find_package(KF5CalendarUtils ${CALENDARUTILS_LIB_VERSION} CONFIG REQUIRED) find_package(KF5Mbox ${KMBOX_LIB_VERSION} CONFIG REQUIRED) find_package(KF5IMAP ${KIMAP_LIB_VERSION} CONFIG REQUIRED) find_package(KF5AkonadiNotes ${AKONADINOTE_LIB_VERSION} CONFIG REQUIRED) find_package(KF5AkonadiCalendar ${AKONADICALENDAR_LIB_VERSION} CONFIG REQUIRED) find_package(KF5PimCommon ${PIMCOMMON_LIB_VERSION} CONFIG REQUIRED) find_package(KPimGAPI ${KGAPI_LIB_VERSION} CONFIG REQUIRED) option(KDEPIM_RUN_ISOLATED_TESTS "Run the isolated tests." FALSE) option(NO_REGENERATE_MIME "Don't regenerate mime file (developer-only option)" FALSE ) #add_definitions( -DQT_DISABLE_DEPRECATED_BEFORE=0x060000 ) find_package(Xsltproc) set_package_properties(Xsltproc PROPERTIES DESCRIPTION "XSLT processor from libxslt" TYPE REQUIRED PURPOSE "Required to generate D-Bus interfaces for all Akonadi resources.") add_subdirectory(resources) add_subdirectory(agents) add_subdirectory(defaultsetup) add_subdirectory(kioslave) add_subdirectory(migration) add_subdirectory(doc) ## install the MIME type spec file for KDEPIM specific MIME types install(FILES kdepim-mime.xml DESTINATION ${KDE_INSTALL_MIMEDIR}) if (NOT NO_REGENERATE_MIME) update_xdg_mimetypes(${KDE_INSTALL_MIMEDIR}) endif() install( FILES kdepim-runtime.renamecategories kdepim-runtime.categories DESTINATION ${KDE_INSTALL_CONFDIR} ) feature_summary(WHAT ALL INCLUDE_QUIET_PACKAGES FATAL_ON_MISSING_REQUIRED_PACKAGES ) diff --git a/resources/kolab/pimkolab/autotests/kcalconversiontest.cpp b/resources/kolab/pimkolab/autotests/kcalconversiontest.cpp index 3988d0e5d..bfb1a7802 100644 --- a/resources/kolab/pimkolab/autotests/kcalconversiontest.cpp +++ b/resources/kolab/pimkolab/autotests/kcalconversiontest.cpp @@ -1,582 +1,582 @@ /* * Copyright (C) 2012 Christian Mollekopf * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #include "kcalconversiontest.h" #include #include #include #include #include #include #include "conversion/kcalconversion.h" #include "conversion/kcalconversion.cpp" #include "conversion/kabcconversion.h" #include "testhelpers.h" using namespace Kolab::Conversion; template void comparePointerVectors(const QVector &list, const QVector &other) { QCOMPARE(list.size(), other.size()); for (int i = 0; i < list.size(); i++) { QCOMPARE(*list.at(i), *other.at(i)); } } void compareAttendeesVectors(const KCalCore::Attendee::List &list, const KCalCore::Attendee::List &other) { QCOMPARE(list.size(), other.size()); for (int i = 0; i < list.size(); i++) { KCalCore::Attendee::Ptr at1 = list.at(i).constCast(); at1->setUid(QString()); KCalCore::Attendee::Ptr at2 = other.at(i).constCast(); at2->setUid(QString()); QCOMPARE(*at1, *at2); } } void KCalConversionTest::initTestCase() { } void KCalConversionTest::testDate_data() { QTest::addColumn("input"); QTest::addColumn("result"); QTest::newRow("datetime with tz") << Kolab::cDateTime("Europe/Zurich", 2006, 1, 8, 12, 0, 0) << QDateTime(QDate(2006, 1, 8), QTime(12, 0, 0), QTimeZone("Europe/Zurich")); QTest::newRow("floating datetime") << Kolab::cDateTime(2006, 1, 8, 12, 0, 0, false) << QDateTime(QDate(2006, 1, 8), QTime(12, 0, 0)); QTest::newRow("utc datetime") << Kolab::cDateTime(2006, 1, 8, 12, 0, 0, true) << QDateTime(QDate(2006, 1, 8), QTime(12, 0, 0), Qt::UTC); QTest::newRow("date only") << Kolab::cDateTime(2006, 1, 8) << QDateTime(QDate(2006, 1, 8), {}); } void KCalConversionTest::testDate() { QFETCH(Kolab::cDateTime, input); QFETCH(QDateTime, result); const QDateTime &r = Kolab::Conversion::toDate(input); QCOMPARE(r, result); const Kolab::cDateTime &r2 = Kolab::Conversion::fromDate(result, input.isDateOnly()); QCOMPARE(r2, input); } void KCalConversionTest::testDuration_data() { QTest::addColumn("input"); QTest::addColumn("result"); QTest::addColumn("fromResult"); QTest::newRow("seconds") << Kolab::Duration(0, 0, 0, 30, false) << KCalCore::Duration(30, KCalCore::Duration::Seconds) << Kolab::Duration(0, 0, 0, 30, false); QTest::newRow("minutes") << Kolab::Duration(0, 0, 1, 30, false) << KCalCore::Duration(90, KCalCore::Duration::Seconds) << Kolab::Duration(0, 0, 0, 90, false); QTest::newRow("hours") << Kolab::Duration(0, 1, 1, 30, false) << KCalCore::Duration(60*60+90, KCalCore::Duration::Seconds) << Kolab::Duration(0, 0, 0, 60*60+90, false); QTest::newRow("days") << Kolab::Duration(1, 1, 1, 30, false) << KCalCore::Duration(24*60*60+60*60+90, KCalCore::Duration::Seconds) << Kolab::Duration(0, 0, 0, 24*60*60+60*60+90, false); QTest::newRow("daysonly") << Kolab::Duration(30, 0, 0, 0, false) << KCalCore::Duration(30, KCalCore::Duration::Days) << Kolab::Duration(30, 0, 0, 0, false); QTest::newRow("weeks") << Kolab::Duration(30, false) << KCalCore::Duration(30*7, KCalCore::Duration::Days) << Kolab::Duration(30*7, 0, 0, 0, false); } void KCalConversionTest::testDuration() { QFETCH(Kolab::Duration, input); QFETCH(KCalCore::Duration, result); QFETCH(Kolab::Duration, fromResult); const KCalCore::Duration &r = Kolab::Conversion::toDuration(input); QCOMPARE(r, result); const Kolab::Duration &r2 = Kolab::Conversion::fromDuration(result); QCOMPARE(r2, fromResult); } void KCalConversionTest::testDateTZ_data() { QTest::addColumn("input"); QTest::addColumn("result"); QTest::newRow("berlin") << Kolab::cDateTime("Europe/Berlin", 2006, 1, 8, 12, 0, 0) << QDateTime(QDate(2006, 1, 8), QTime(12, 0, 0), QTimeZone("Europe/Berlin")); } void KCalConversionTest::testDateTZ() { QFETCH(Kolab::cDateTime, input); QFETCH(QDateTime, result); const QDateTime &r = Kolab::Conversion::toDate(input); QCOMPARE(QString::fromUtf8(result.timeZone().id()), QString::fromStdString(input.timezone())); QCOMPARE(r.timeZone().offsetFromUtc(QDateTime::currentDateTimeUtc()), result.timeZone().offsetFromUtc(QDateTime::currentDateTimeUtc())); const Kolab::cDateTime &r2 = Kolab::Conversion::fromDate(result, input.isDateOnly()); QCOMPARE(QString::fromStdString(r2.timezone()), QString::fromUtf8(result.timeZone().id())); } void KCalConversionTest::testConversion_data() { QTest::addColumn("kcal"); QTest::addColumn("kolab"); Kolab::cDateTime date(2011, 2, 2, 12, 11, 10, true); Kolab::cDateTime date2(2011, 2, 2, 12, 12, 10, true); Kolab::cDateTime date3(2012, 2, 2, 12, 12, 10, true); std::vector intVector; intVector.push_back(1); intVector.push_back(-3); intVector.push_back(2); std::vector stringVector; stringVector.push_back("cat1"); stringVector.push_back("cat2"); stringVector.push_back("parent/child"); { KCalCore::Event kcal; kcal.setUid(QStringLiteral("uid")); kcal.setCreated(toDate(date)); kcal.setLastModified(toDate(date)); kcal.setRevision(3); kcal.setSecrecy(KCalCore::Incidence::SecrecyConfidential); kcal.setCategories(toStringList(stringVector)); kcal.setDtStart(toDate(date)); kcal.setDtEnd(toDate(date2)); kcal.setAllDay(date.isDateOnly()); kcal.setTransparency(KCalCore::Event::Transparent); kcal.setRecurrenceId(toDate(date2)); //TODO THISANDFUTURE kcal.recurrence()->setDaily(3); kcal.recurrence()->setDuration(5); kcal.recurrence()->addRDateTime(toDate(date2)); kcal.recurrence()->addRDate(toDate(date2).date()); kcal.recurrence()->addExDateTime(toDate(date3)); kcal.recurrence()->addExDate(toDate(date3).date()); KCalCore::RecurrenceRule *rr = kcal.recurrence()->defaultRRule(true); QList intList = QVector::fromStdVector(intVector).toList(); rr->setBySeconds(intList); rr->setByMinutes(intList); rr->setByHours(intList); rr->setByDays(QList() << KCalCore::RecurrenceRule::WDayPos(3, 1) << KCalCore::RecurrenceRule::WDayPos(5, 4)); rr->setByMonthDays(intList); rr->setByYearDays(intList); rr->setByMonths(intList); rr->setByWeekNumbers(intList); kcal.setSummary(QStringLiteral("summary")); kcal.setDescription(QStringLiteral("description")); kcal.setPriority(3); kcal.setStatus(KCalCore::Incidence::StatusConfirmed); kcal.setLocation(QStringLiteral("location")); - kcal.setOrganizer(KCalCore::Person::Ptr(new KCalCore::Person(QStringLiteral("organizer"), QStringLiteral("organizer@email")))); + kcal.setOrganizer(KCalCore::Person(QStringLiteral("organizer"), QStringLiteral("organizer@email"))); //Url kcal.setNonKDECustomProperty("X-KOLAB-URL", QStringLiteral("http://test.org")); KCalCore::Attendee::Ptr att(new KCalCore::Attendee(QStringLiteral("attendee"), QStringLiteral("attendee@email"), false, KCalCore::Attendee::NeedsAction, KCalCore::Attendee::ReqParticipant)); att->setDelegate(QStringLiteral("mailto:delegatee")); att->setDelegator(QStringLiteral("mailto:delegator")); kcal.addAttendee(att); kcal.addAttachment(KCalCore::Attachment::Ptr(new KCalCore::Attachment(QStringLiteral("uri"), QStringLiteral("mimetype/mime")))); KCalCore::Alarm::Ptr alarm = KCalCore::Alarm::Ptr(new KCalCore::Alarm(&kcal)); KCalCore::Person::List addressees; - addressees.append(KCalCore::Person::Ptr(new KCalCore::Person(QStringLiteral("name"), QStringLiteral("email@email")))); + addressees.append(KCalCore::Person(QStringLiteral("name"), QStringLiteral("email@email"))); alarm->setEmailAlarm(QStringLiteral("subject"), QStringLiteral("text"), addressees, QStringList()); //No support for attachments kcal.addAlarm(alarm); //TODO alarms kcal.setNonKDECustomProperty("X-KOLAB-key1", QStringLiteral("value1")); kcal.setNonKDECustomProperty("X-KOLAB-key2", QStringLiteral("value2")); kcal.setCustomProperty("SOMEOTHERAPP", "key2", QStringLiteral("value2")); Q_ASSERT(kcal.nonKDECustomProperty("X-KOLAB-key1") == "value1"); Kolab::Event kolab; kolab.setUid("uid"); kolab.setCreated(date); kolab.setLastModified(date); kolab.setSequence(3); kolab.setClassification(Kolab::ClassConfidential); kolab.setCategories(stringVector); kolab.setStart(date); kolab.setEnd(date2); kolab.setTransparency(true); Kolab::RecurrenceRule rrule; rrule.setInterval(3); rrule.setFrequency(Kolab::RecurrenceRule::Daily); rrule.setCount(5); rrule.setBysecond(intVector); rrule.setByminute(intVector); rrule.setByhour(intVector); rrule.setByday(std::vector() << Kolab::DayPos(3, Kolab::Monday) << Kolab::DayPos(5, Kolab::Thursday)); rrule.setBymonthday(intVector); rrule.setByyearday(intVector); rrule.setByweekno(intVector); rrule.setBymonth(intVector); kolab.setRecurrenceRule(rrule); kolab.setRecurrenceID(date2, true); kolab.setRecurrenceDates(std::vector() << date2 << Kolab::cDateTime(date2.year(), date2.month(), date2.day())); kolab.setExceptionDates(std::vector() << date3 << Kolab::cDateTime(date3.year(), date3.month(), date3.day())); kolab.setSummary("summary"); kolab.setDescription("description"); kolab.setPriority(3); kolab.setStatus(Kolab::StatusConfirmed); kolab.setLocation("location"); kolab.setOrganizer(Kolab::ContactReference(Kolab::ContactReference::EmailReference, "organizer@email", "organizer")); //TODO uid kolab.setUrl("http://test.org"); Kolab::Attendee a(Kolab::ContactReference(Kolab::ContactReference::EmailReference, "attendee@email", "attendee"));//TODO uid a.setDelegatedTo(std::vector() << Kolab::ContactReference(Kolab::ContactReference::EmailReference, "delegatee@email", "delegatee")); a.setDelegatedFrom(std::vector() << Kolab::ContactReference(Kolab::ContactReference::EmailReference, "delegator@email", "delegator")); a.setCutype(Kolab::CutypeIndividual); kolab.setAttendees(std::vector() << a); Kolab::Attachment attach; attach.setUri("uri", "mimetype/mime"); kolab.setAttachments(std::vector() << attach); // std::vector receipents; // receipents.push_back("email@email"); // Kolab::Alarm alarm2("summary", "description", receipents); // kolab.setAlarms(std::vector() << alarm2); //The sorting is random, just sort them here how we think they should arrive so we don't have to sort during compare (due to lazyness). std::vector customproperties; customproperties.push_back(Kolab::CustomProperty("X-KDE-SOMEOTHERAPP-key2", "value2")); customproperties.push_back(Kolab::CustomProperty("key1", "value1")); customproperties.push_back(Kolab::CustomProperty("key2", "value2")); kolab.setCustomProperties(customproperties); QTest::newRow("with endDate and recurrence duration") << kcal << kolab; } { KCalCore::Event kcal; kcal.setUid(QStringLiteral("uid")); kcal.setCreated(toDate(date)); kcal.setLastModified(toDate(date)); kcal.setRevision(3); kcal.setDtStart(toDate(date)); kcal.setAllDay(date.isDateOnly()); kcal.setDuration(KCalCore::Duration(toDate(date), toDate(date2))); kcal.recurrence()->setDaily(3); kcal.recurrence()->setEndDateTime(toDate(date3)); Kolab::Event kolab; kolab.setUid("uid"); kolab.setCreated(date); kolab.setLastModified(date); kolab.setSequence(3); kolab.setStart(date); kolab.setDuration(Kolab::Duration(0, 0, 1, 0)); Kolab::RecurrenceRule rrule; rrule.setInterval(3); rrule.setFrequency(Kolab::RecurrenceRule::Daily); rrule.setEnd(date3); kolab.setRecurrenceRule(rrule); QTest::newRow("with duration and recurrence endDate") << kcal << kolab; } { Kolab::cDateTime start(2011, 1, 1); Kolab::cDateTime end(2011, 1, 3); KCalCore::Event kcal; kcal.setUid(QStringLiteral("uid")); kcal.setCreated(toDate(date)); kcal.setLastModified(toDate(date)); kcal.setDtStart(toDate(start)); kcal.setDtEnd(toDate(end)); kcal.setAllDay(start.isDateOnly()); kcal.recurrence()->setDaily(3); kcal.recurrence()->setEndDateTime(toDate(end)); Kolab::Event kolab; kolab.setUid("uid"); kolab.setCreated(date); kolab.setLastModified(date); kolab.setStart(start); kolab.setEnd(end); Kolab::RecurrenceRule rrule; rrule.setInterval(3); rrule.setFrequency(Kolab::RecurrenceRule::Daily); rrule.setEnd(end); kolab.setRecurrenceRule(rrule); QTest::newRow("date only dates") << kcal << kolab; } { KCalCore::Event kcal; kcal.setUid(QStringLiteral("uid")); kcal.setCreated(toDate(date)); kcal.setLastModified(toDate(date)); kcal.setDtStart(toDate(date)); kcal.setAllDay(date.isDateOnly()); kcal.setSummary(QStringLiteral("äöü%@$£é¤¼²°€Š�")); Kolab::Event kolab; kolab.setUid("uid"); kolab.setCreated(date); kolab.setLastModified(date); kolab.setStart(date); kolab.setSummary(std::string(QStringLiteral("äöü%@$£é¤¼²°€Š�").toUtf8().constData())); QTest::newRow("latin1+Unicode") << kcal << kolab; } } void KCalConversionTest::testConversion() { QFETCH(KCalCore::Event, kcal); QFETCH(Kolab::Event, kolab); KCalCore::Event::Ptr e = toKCalCore(kolab); const Kolab::Event &b = fromKCalCore(kcal); QCOMPARE(e->uid(), kcal.uid()); QCOMPARE(e->created(), kcal.created()); QCOMPARE(e->lastModified(), kcal.lastModified()); QCOMPARE(e->revision(), kcal.revision()); QCOMPARE(e->secrecy(), kcal.secrecy()); QCOMPARE(e->categories(), kcal.categories()); QCOMPARE(e->dtStart(), kcal.dtStart()); QCOMPARE(e->dtEnd(), kcal.dtEnd()); QCOMPARE(e->duration(), kcal.duration()); QCOMPARE(e->transparency(), kcal.transparency()); QCOMPARE(*e->recurrence(), *kcal.recurrence()); QCOMPARE(e->recurrenceId(), kcal.recurrenceId()); QCOMPARE(e->recurrenceType(), kcal.recurrenceType()); QCOMPARE(e->summary(), kcal.summary()); QCOMPARE(e->description(), kcal.description()); QCOMPARE(e->priority(), kcal.priority()); QCOMPARE(e->status(), kcal.status()); QCOMPARE(e->location(), kcal.location()); - QCOMPARE(e->organizer()->name(), kcal.organizer()->name()); - QCOMPARE(e->organizer()->email(), kcal.organizer()->email()); + QCOMPARE(e->organizer().name(), kcal.organizer().name()); + QCOMPARE(e->organizer().email(), kcal.organizer().email()); QCOMPARE(e->nonKDECustomProperty("X-KOLAB-URL"), kcal.nonKDECustomProperty("X-KOLAB-URL")); //otherwise we'd break the customProperties comparison e->removeNonKDECustomProperty("X-KOLAB-URL"); kcal.removeNonKDECustomProperty("X-KOLAB-URL"); compareAttendeesVectors(e->attendees(), kcal.attendees()); comparePointerVectors(e->attachments(), kcal.attachments()); // QCOMPARE(e->alarms(), kcal.alarms()); //TODO QCOMPARE(e->customProperties(), kcal.customProperties()); // QBENCHMARK { // toKCalCore(kolab); // } QCOMPARE(b.uid(), kolab.uid()); QCOMPARE(b.created(), kolab.created()); QCOMPARE(b.lastModified(), kolab.lastModified()); QCOMPARE(b.sequence(), kolab.sequence()); QCOMPARE(b.classification(), kolab.classification()); QCOMPARE(b.categories(), kolab.categories()); QCOMPARE(b.start(), kolab.start()); QCOMPARE(b.end(), kolab.end()); QCOMPARE(b.duration(), kolab.duration()); QCOMPARE(b.transparency(), kolab.transparency()); QCOMPARE(b.recurrenceRule(), kolab.recurrenceRule()); QCOMPARE(b.recurrenceID(), kolab.recurrenceID()); QCOMPARE(b.recurrenceDates(), kolab.recurrenceDates()); QCOMPARE(b.exceptionDates(), kolab.exceptionDates()); QCOMPARE(b.summary(), kolab.summary()); QCOMPARE(b.description(), kolab.description()); QCOMPARE(b.status(), kolab.status()); QCOMPARE(b.location(), kolab.location()); QCOMPARE(b.organizer(), kolab.organizer()); QCOMPARE(b.url(), kolab.url()); QCOMPARE(b.attendees(), kolab.attendees()); QCOMPARE(b.attachments(), kolab.attachments()); QCOMPARE(b.customProperties(), kolab.customProperties()); } void KCalConversionTest::testTodoConversion_data() { QTest::addColumn("kcal"); QTest::addColumn("kolab"); Kolab::cDateTime date(2011, 2, 2, 12, 11, 10, true); Kolab::cDateTime date2(2011, 2, 2, 12, 12, 10, true); { KCalCore::Todo kcal; kcal.setUid(QStringLiteral("uid")); kcal.setDtStart(toDate(date)); kcal.setDtDue(toDate(date2)); kcal.setAllDay(date.isDateOnly()); kcal.setRelatedTo(QStringLiteral("uid2"), KCalCore::Incidence::RelTypeParent); Kolab::Todo kolab; kolab.setUid("uid"); kolab.setStart(date); kolab.setDue(date2); std::vector relateds; relateds.push_back("uid2"); kolab.setRelatedTo(relateds); QTest::newRow("todo") << kcal << kolab; } } void KCalConversionTest::testTodoConversion() { QFETCH(KCalCore::Todo, kcal); QFETCH(Kolab::Todo, kolab); const KCalCore::Todo::Ptr e = toKCalCore(kolab); QCOMPARE(e->uid(), kcal.uid()); QCOMPARE(e->dtStart(), kcal.dtStart()); QCOMPARE(e->dtDue(), kcal.dtDue()); QCOMPARE(e->relatedTo(KCalCore::Incidence::RelTypeParent), kcal.relatedTo(KCalCore::Incidence::RelTypeParent)); const Kolab::Todo &b = fromKCalCore(kcal); QCOMPARE(b.uid(), kolab.uid()); QCOMPARE(b.start(), kolab.start()); QCOMPARE(b.due(), kolab.due()); QCOMPARE(b.relatedTo(), kolab.relatedTo()); } void KCalConversionTest::testJournalConversion_data() { QTest::addColumn("kcal"); QTest::addColumn("kolab"); Kolab::cDateTime date(2011, 2, 2, 12, 11, 10, true); Kolab::cDateTime date2(2011, 2, 2, 12, 12, 10, true); { KCalCore::Journal kcal; kcal.setUid(QStringLiteral("uid")); kcal.setDtStart(toDate(date)); kcal.setSummary(QStringLiteral("summary")); Kolab::Journal kolab; kolab.setUid("uid"); kolab.setStart(date); kolab.setSummary("summary"); QTest::newRow("journal") << kcal << kolab; } } void KCalConversionTest::testJournalConversion() { QFETCH(KCalCore::Journal, kcal); QFETCH(Kolab::Journal, kolab); const KCalCore::Journal::Ptr e = toKCalCore(kolab); QCOMPARE(e->uid(), kcal.uid()); QCOMPARE(e->dtStart(), kcal.dtStart()); QCOMPARE(e->summary(), kcal.summary()); const Kolab::Journal &b = fromKCalCore(kcal); QCOMPARE(b.uid(), kolab.uid()); QCOMPARE(b.start(), kolab.start()); QCOMPARE(b.summary(), kolab.summary()); } void KCalConversionTest::testContactConversion_data() { QTest::addColumn("kcal"); QTest::addColumn("kolab"); { KContacts::Addressee kcal; kcal.setUid(QStringLiteral("uid")); kcal.setFormattedName(QStringLiteral("name")); Kolab::Contact kolab; kolab.setUid("uid"); kolab.setName("name"); QTest::newRow("basic") << kcal << kolab; } { KContacts::Addressee kcal; kcal.setUid(QStringLiteral("uid")); kcal.setFormattedName(QStringLiteral("name")); kcal.setBirthday(QDateTime(QDate(2012, 2, 2))); //Because QDateTime doesn't know date-only values we always end up with a date-time Kolab::Contact kolab; kolab.setUid("uid"); kolab.setName("name"); kolab.setBDay(Kolab::cDateTime(2012, 2, 2, 0, 0, 0)); QTest::newRow("bday") << kcal << kolab; } { KContacts::Addressee kcal; kcal.setUid(QStringLiteral("uid")); //The first address is always the preferred kcal.setEmails(QStringList() << QStringLiteral("email1@example.org") << QStringLiteral("email2@example.org")); kcal.insertCustom(QStringLiteral("KOLAB"), QStringLiteral("EmailTypesemail1@example.org"), QStringLiteral("home,work")); Kolab::Contact kolab; kolab.setUid("uid"); Kolab::Email email1("email1@example.org", Kolab::Email::Work|Kolab::Email::Home); Kolab::Email email2("email2@example.org"); std::vector emails; emails.push_back(email1); emails.push_back(email2); kolab.setEmailAddresses(emails, 0); QTest::newRow("emailTypesAndPreference") << kcal << kolab; } } void KCalConversionTest::testContactConversion() { QFETCH(KContacts::Addressee, kcal); QFETCH(Kolab::Contact, kolab); const KContacts::Addressee &e = toKABC(kolab); QCOMPARE(e.uid(), kcal.uid()); QCOMPARE(e.formattedName(), kcal.formattedName()); QCOMPARE(e.emails(), kcal.emails()); QCOMPARE(e.preferredEmail(), kcal.preferredEmail()); foreach (const QString &mail, e.emails()) { QCOMPARE(e.custom(QLatin1String("KOLAB"), QString::fromLatin1("EmailTypes%1").arg(mail)), kcal.custom(QLatin1String("KOLAB"), QString::fromLatin1("EmailTypes%1").arg(mail))); } QCOMPARE(e.birthday(), kcal.birthday()); const Kolab::Contact &b = fromKABC(kcal); QCOMPARE(b.uid(), kolab.uid()); QCOMPARE(b.name(), kolab.name()); QCOMPARE(b.emailAddresses(), kolab.emailAddresses()); QCOMPARE(b.emailAddressPreferredIndex(), kolab.emailAddressPreferredIndex()); QCOMPARE(b.bDay(), kolab.bDay()); } // void KCalConversionTest::BenchmarkRoundtripKCAL() // { // const Kolab::Event &event = Kolab::readEvent(TEST_DATA_PATH "/testfiles/icalEvent.xml", true); // std::string result = Kolab::writeEvent(event); // QBENCHMARK { // Kolab::Conversion::toKCalCore(Kolab::readEvent(result, false)); // } // } QTEST_MAIN(KCalConversionTest) diff --git a/resources/kolab/pimkolab/autotests/kolabobjecttest.cpp b/resources/kolab/pimkolab/autotests/kolabobjecttest.cpp index 67869d286..779f37e15 100644 --- a/resources/kolab/pimkolab/autotests/kolabobjecttest.cpp +++ b/resources/kolab/pimkolab/autotests/kolabobjecttest.cpp @@ -1,175 +1,174 @@ /* * Copyright (C) 2012 Christian Mollekopf * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #include "kolabobjecttest.h" #include #include "kolabformat/kolabobject.h" #include void KolabObjectTest::preserveLatin1() { KCalCore::Event::Ptr event(new KCalCore::Event()); event->setDtStart(QDateTime(QDate(2014, 1, 1), {})); event->setAllDay(true); const QString summary(QLatin1String("äöü%@$£é¤¼²°")); event->setSummary(summary); QCOMPARE(event->summary(), summary); //std::cout << event->summary().toStdString() << std::endl; KMime::Message::Ptr msg = Kolab::KolabObjectWriter::writeEvent(event); // qDebug() << msg->encodedContent(); KCalCore::Event::Ptr readEvent = Kolab::KolabObjectReader(msg).getEvent(); QVERIFY(readEvent); // std::cout << readEvent->summary().toStdString() << std::endl; QCOMPARE(readEvent->summary(), summary); } void KolabObjectTest::preserveUnicode() { KCalCore::Event::Ptr event(new KCalCore::Event()); event->setDtStart(QDateTime(QDate(2014, 1, 1), {})); event->setAllDay(true); QString summary(QStringLiteral("€Š�ـأبـ☺")); event->setSummary(summary); QCOMPARE(event->summary(), summary); // std::cout << event->summary().toStdString() << std::endl; KMime::Message::Ptr msg = Kolab::KolabObjectWriter::writeEvent(event); // qDebug() << msg->encodedContent(); KCalCore::Event::Ptr readEvent = Kolab::KolabObjectReader(msg).getEvent(); QVERIFY(readEvent); // std::cout << readEvent->summary().toStdString() << std::endl; QCOMPARE(readEvent->summary(), summary); } void KolabObjectTest::dontCrashWithEmptyOrganizer() { KCalCore::Event::Ptr event(new KCalCore::Event()); - event->setOrganizer(KCalCore::Person::Ptr()); event->setDtStart(QDateTime(QDate(2012, 11, 11), {})); event->setAllDay(true); Kolab::KolabObjectWriter::writeEvent(event, Kolab::KolabV2); QCOMPARE(Kolab::ErrorHandler::instance().error(), Kolab::ErrorHandler::Debug); Kolab::KolabObjectWriter::writeEvent(event); qDebug() << Kolab::ErrorHandler::instance().error(); QCOMPARE(Kolab::ErrorHandler::instance().error(), Kolab::ErrorHandler::Debug); } void KolabObjectTest::dontCrashWithEmptyIncidence() { Kolab::KolabObjectWriter::writeEvent(KCalCore::Event::Ptr()); QCOMPARE(Kolab::ErrorHandler::instance().error(), Kolab::ErrorHandler::Critical); Kolab::KolabObjectWriter::writeTodo(KCalCore::Todo::Ptr()); QCOMPARE(Kolab::ErrorHandler::instance().error(), Kolab::ErrorHandler::Critical); Kolab::KolabObjectWriter::writeJournal(KCalCore::Journal::Ptr()); QCOMPARE(Kolab::ErrorHandler::instance().error(), Kolab::ErrorHandler::Critical); Kolab::KolabObjectWriter::writeIncidence(KCalCore::Event::Ptr()); QCOMPARE(Kolab::ErrorHandler::instance().error(), Kolab::ErrorHandler::Critical); Kolab::KolabObjectWriter::writeNote(KMime::Message::Ptr()); QCOMPARE(Kolab::ErrorHandler::instance().error(), Kolab::ErrorHandler::Critical); } void KolabObjectTest::parseRelationMembers() { { QString memberString( QStringLiteral( "imap:/user/jan.aachen%40lhm.klab.cc/INBOX/20?message-id=%3Cf06aa3345a25005380b47547ad161d36%40lhm.klab.cc%3E&subject=Re%3A+test&date=Tue%2C+12+Aug+2014+20%3A42%3A59+%2B0200")); Kolab::RelationMember member = Kolab::parseMemberUrl(memberString); QString result = Kolab::generateMemberUrl(member); qDebug() << result; result.replace(QLatin1String("%20"), QLatin1String("+")); QEXPECT_FAIL("", "This is currently failing, probably a bug in the recent changes regarding the encoding.", Continue); QCOMPARE(result, memberString); } //user namespace by uid { Kolab::RelationMember member; member.uid = 20; member.mailbox = QList() << "INBOX"; member.user = QStringLiteral("john.doe@example.org"); member.messageId = QStringLiteral("messageid"); member.date = QStringLiteral("date"); member.subject = QStringLiteral("subject"); QString url = Kolab::generateMemberUrl(member); qDebug() << url; Kolab::RelationMember result = Kolab::parseMemberUrl(url); QCOMPARE(result.uid, member.uid); QCOMPARE(result.mailbox, member.mailbox); QEXPECT_FAIL("", "This is currently failing, probably a bug in the recent changes regarding the encoding.", Continue); QCOMPARE(result.user, member.user); QCOMPARE(result.messageId, member.messageId); QCOMPARE(result.date, member.date); QCOMPARE(result.subject, member.subject); } //shared namespace by uid { Kolab::RelationMember member; member.uid = 20; member.mailbox = QList() << "foo" << "bar"; member.messageId = QStringLiteral("messageid"); member.date = QStringLiteral("date"); member.subject = QStringLiteral("subject"); QString url = Kolab::generateMemberUrl(member); qDebug() << url; Kolab::RelationMember result = Kolab::parseMemberUrl(url); QCOMPARE(result.uid, member.uid); QCOMPARE(result.mailbox, member.mailbox); QVERIFY(result.user.isEmpty()); QCOMPARE(result.messageId, member.messageId); QCOMPARE(result.date, member.date); QCOMPARE(result.subject, member.subject); } //by uuid/gid { Kolab::RelationMember member; member.gid = QStringLiteral("fooobar"); QString url = Kolab::generateMemberUrl(member); qDebug() << url; Kolab::RelationMember result = Kolab::parseMemberUrl(url); QCOMPARE(result.gid, member.gid); } // chars to en/decode { Kolab::RelationMember member; member.uid = 20; member.mailbox = QList() << "spaces in folders" << "+^,:@"; member.user = QStringLiteral("john.doe:^@example.org"); member.messageId = QStringLiteral("messageid+^,:@"); member.date = QStringLiteral("date+^,:@"); member.subject = QStringLiteral("subject+^,:@"); QString url = Kolab::generateMemberUrl(member); qDebug() << url; Kolab::RelationMember result = Kolab::parseMemberUrl(url); QCOMPARE(result.uid, member.uid); QEXPECT_FAIL("", "This is currently failing, probably a bug in the recent changes regarding the encoding.", Continue); QCOMPARE(result.mailbox, member.mailbox); QEXPECT_FAIL("", "This is currently failing, probably a bug in the recent changes regarding the encoding.", Continue); QCOMPARE(result.user, member.user); QCOMPARE(result.messageId, member.messageId); QCOMPARE(result.date, member.date); QCOMPARE(result.subject, member.subject); } } QTEST_MAIN(KolabObjectTest) diff --git a/resources/kolab/pimkolab/autotests/timezonetest.cpp b/resources/kolab/pimkolab/autotests/timezonetest.cpp index 14f6b7981..68ec6c41a 100644 --- a/resources/kolab/pimkolab/autotests/timezonetest.cpp +++ b/resources/kolab/pimkolab/autotests/timezonetest.cpp @@ -1,179 +1,178 @@ /* * Copyright (C) 2012 Christian Mollekopf * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #include "timezonetest.h" #include #include #include #include #include "testutils.h" #include #include #include void TimezoneTest::initTestCase() { } void TimezoneTest::testFromName() { TimezoneConverter converter; const QString timezone = converter.normalizeTimezone(QStringLiteral("(GMT+01.00) Sarajevo/Warsaw/Zagreb")); QCOMPARE(timezone, QLatin1String("Europe/Sarajevo")); } void TimezoneTest::testFromHardcodedList_data() { QTest::addColumn("timezone"); QTest::newRow("1") << QStringLiteral("(GMT+01:00) West Central Africa"); QTest::newRow("2") << QStringLiteral("(GMT-04:00) Atlantic Time (Canada)"); QTest::newRow("3") << QStringLiteral("(GMT-06:00) Saskatchewan"); QTest::newRow("4") << QStringLiteral("(GMT-01:00) Cape Verde Islands"); QTest::newRow("5") << QStringLiteral("(GMT-06:00) Central America"); QTest::newRow("6") << QStringLiteral("(GMT-06:00) Central Time (US and Canada)"); // QTest::newRow( "7" ) << QString::fromLatin1("(GMT-12:00) International Date Line West"); //Not mappable QTest::newRow("8") << QStringLiteral("(GMT-05:00) Eastern Time (US and Canada)"); // QTest::newRow( "9" ) << QString::fromLatin1("(GMT-02:00) Mid-Atlantic"); //Not mappable QTest::newRow("10") << QStringLiteral("(GMT-07:00) Mountain Time (US and Canada)"); QTest::newRow("11") << QStringLiteral("(GMT-03:30) Newfoundland and Labrador"); QTest::newRow("12") << QStringLiteral("(GMT-08:00) Pacific Time (US and Canada); Tijuana"); QTest::newRow("13") << QStringLiteral("(GMT-11:00) Midway Island, Samoa"); QTest::newRow("14") << QStringLiteral("W. Europe Standard Time"); QTest::newRow("15") << QStringLiteral("(GMT+1.00) Sarajevo/Warsaw/Zagreb"); //Lotus notes uses it's own set of specifiers // QTest::newRow( "Lotus Notes" ) << QString::fromLatin1("W. Europe"); // QTest::newRow( "Google UTC offset" ) << QString::fromLatin1("2013-10-23T04:00:00+02:00"); } void TimezoneTest::testFromHardcodedList() { TimezoneConverter converter; QFETCH(QString, timezone); const QString tz = converter.normalizeTimezone(timezone); qDebug() << tz; QVERIFY(!tz.isEmpty()); QVERIFY(tz != timezone); } void TimezoneTest::testKolabObjectWriter() { KCalCore::Event::Ptr event(new KCalCore::Event()); - event->setOrganizer(KCalCore::Person::Ptr()); event->setDtStart(QDateTime(QDate(2012, 11, 11), QTime(1, 1), QTimeZone(QTimeZone::windowsIdToDefaultIanaId("(GMT+01:00) West Central Africa")))); KMime::Message::Ptr msg = Kolab::KolabObjectWriter::writeEvent(event); Kolab::KolabObjectReader reader(msg); KCalCore::Event::Ptr result = reader.getEvent(); qDebug() << result->dtStart().timeZone().id(); QCOMPARE(result->dtStart().timeZone().id(), QTimeZone("Africa/Lagos").id()); } // void TimezoneTest::testKolabObjectReader() // { // const Kolab::Version version = Kolab::KolabV3; // const Kolab::ObjectType type = Kolab::EventObject; // QString icalFileName = TESTFILEDIR+QString::fromLatin1("timezone/windowsTimezone.ics"); //To compare // QString mimeFileName = TESTFILEDIR+QString::fromLatin1("timezone/windowsTimezoneV3.mime"); //For parsing // // //Parse mime message // bool ok = false; // const KMime::Message::Ptr &msg = readMimeFile( mimeFileName, ok ); // QVERIFY(ok); // Kolab::KolabObjectReader reader; // Kolab::ObjectType t = reader.parseMimeMessage(msg); // QCOMPARE(t, type); // QCOMPARE(reader.getVersion(), version); // QCOMPARE(Kolab::ErrorHandler::instance().error(), Kolab::ErrorHandler::Debug); // // KCalCore::Incidence::Ptr convertedIncidence = reader.getIncidence(); // qDebug() << "read incidence"; // // //Parse ICalFile for comparison // QFile icalFile( icalFileName ); // QVERIFY( icalFile.open( QFile::ReadOnly ) ); // KCalCore::ICalFormat format; // KCalCore::Incidence::Ptr realIncidence( format.fromString( QString::fromUtf8( icalFile.readAll() ) ) ); // // // fix up the converted incidence for comparisson // normalizeIncidence(convertedIncidence); // normalizeIncidence(realIncidence); // // // recurrence objects are created on demand, but KCalCore::Incidence::operator==() doesn't take that into account // // so make sure both incidences have one // realIncidence->recurrence(); // convertedIncidence->recurrence(); // // realIncidence->setLastModified(convertedIncidence->lastModified()); // // //The following test is just for debugging and not really relevant // if ( *(realIncidence.data()) != *(convertedIncidence.data()) ) { // showDiff(format.toString( realIncidence ), format.toString( convertedIncidence )); // } // QVERIFY( *(realIncidence.data()) == *(convertedIncidence.data()) ); // } void TimezoneTest::testFindLegacyTimezone() { const QString normalized = TimezoneConverter::normalizeTimezone(QStringLiteral("US/Pacific")); qDebug() << normalized; QEXPECT_FAIL("", "Currently broken", Continue); QVERIFY(!normalized.isEmpty()); } void TimezoneTest::testIgnoreInvalidTimezone() { const QString normalized = TimezoneConverter::normalizeTimezone(QStringLiteral("FOOOOBAR")); qDebug() << normalized; QVERIFY(normalized.isEmpty()); } void TimezoneTest::testUTCOffset() { const Kolab::cDateTime expected(2013, 10, 23, 2, 0, 0, true); const QDateTime input(QDateTime::fromString(QStringLiteral("2013-10-23T04:00:00+02:00"), Qt::ISODate)); const Kolab::cDateTime result = Kolab::Conversion::fromDate(input, false); QVERIFY(!Kolab::ErrorHandler::instance().errorOccured()); QCOMPARE(result, expected); } void TimezoneTest::localTimezone() { { const Kolab::cDateTime result = Kolab::Conversion::fromDate(QDateTime(QDate(2013, 10, 10), QTime(2, 0, 0), Qt::LocalTime), false); QVERIFY(!result.timezone().empty()); QVERIFY(!Kolab::ErrorHandler::instance().errorOccured()); } { const Kolab::cDateTime result = Kolab::Conversion::fromDate(QDateTime(QDate(2013, 10, 10), QTime(2, 0, 0)), false); QVERIFY(!Kolab::ErrorHandler::instance().errorOccured()); } { const Kolab::cDateTime result = Kolab::Conversion::fromDate(QDateTime(QDate(2013, 10, 10), QTime(2, 0, 0), QTimeZone()), false); QVERIFY(result.timezone().empty()); QVERIFY(!Kolab::ErrorHandler::instance().errorOccured()); } { QDateTime dt(QDate(2013, 10, 10), QTime(2, 0, 0), QTimeZone("/etc/localzone")); const Kolab::cDateTime result = Kolab::Conversion::fromDate(dt, false); qDebug() << result.timezone(); QVERIFY(result.timezone().empty()); QVERIFY(!Kolab::ErrorHandler::instance().errorOccured()); } } QTEST_MAIN(TimezoneTest) diff --git a/resources/kolab/pimkolab/conversion/kcalconversion.cpp b/resources/kolab/pimkolab/conversion/kcalconversion.cpp index 5a1716dff..ac797094e 100644 --- a/resources/kolab/pimkolab/conversion/kcalconversion.cpp +++ b/resources/kolab/pimkolab/conversion/kcalconversion.cpp @@ -1,855 +1,855 @@ /* * Copyright (C) 2011 Christian Mollekopf * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #include "kcalconversion.h" #include #include #include #include #include #include #include #include "commonconversion.h" #include "pimkolab_debug.h" namespace Kolab { namespace Conversion { //The uid of a contact which refers to the uuid of a contact in the addressbook #define CUSTOM_KOLAB_CONTACT_UUID "X_KOLAB_CONTACT_UUID" #define CUSTOM_KOLAB_CONTACT_CUTYPE "X_KOLAB_CONTACT_CUTYPE" #define CUSTOM_KOLAB_URL "X-KOLAB-URL" KCalCore::Duration toDuration(const Kolab::Duration &d) { int value = 0; if (d.hours() || d.minutes() || d.seconds()) { value = ((((d.weeks() * 7 + d.days()) * 24 + d.hours()) * 60 + d.minutes()) * 60 + d.seconds()); if (d.isNegative()) { value = -value; } return KCalCore::Duration(value); } value = d.weeks() * 7 + d.days(); if (d.isNegative()) { value = -value; } return KCalCore::Duration(value, KCalCore::Duration::Days); } Kolab::Duration fromDuration(const KCalCore::Duration &d) { int value = d.value(); bool isNegative = false; if (value < 0) { isNegative = true; value = -value; } //We don't know how the seconds/days were distributed before, so no point in distributing them (probably) if (d.isDaily()) { int days = value; return Kolab::Duration(days, 0, 0, 0, isNegative); } int seconds = value; // int minutes = seconds / 60; // seconds = seconds % 60; // int hours = minutes / 60; // minutes = minutes % 60; return Kolab::Duration(0, 0, 0, seconds, isNegative); } KCalCore::Incidence::Secrecy toSecrecy(Kolab::Classification c) { switch (c) { case Kolab::ClassPublic: return KCalCore::Incidence::SecrecyPublic; case Kolab::ClassPrivate: return KCalCore::Incidence::SecrecyPrivate; case Kolab::ClassConfidential: return KCalCore::Incidence::SecrecyConfidential; default: qCCritical(PIMKOLAB_LOG) << "unhandled"; Q_ASSERT(0); } return KCalCore::Incidence::SecrecyPublic; } Kolab::Classification fromSecrecy(KCalCore::Incidence::Secrecy c) { switch (c) { case KCalCore::Incidence::SecrecyPublic: return Kolab::ClassPublic; case KCalCore::Incidence::SecrecyPrivate: return Kolab::ClassPrivate; case KCalCore::Incidence::SecrecyConfidential: return Kolab::ClassConfidential; default: qCCritical(PIMKOLAB_LOG) << "unhandled"; Q_ASSERT(0); } return Kolab::ClassPublic; } int toPriority(int priority) { //Same mapping return priority; } int fromPriority(int priority) { //Same mapping return priority; } KCalCore::Incidence::Status toStatus(Kolab::Status s) { switch (s) { case Kolab::StatusUndefined: return KCalCore::Incidence::StatusNone; case Kolab::StatusNeedsAction: return KCalCore::Incidence::StatusNeedsAction; case Kolab::StatusCompleted: return KCalCore::Incidence::StatusCompleted; case Kolab::StatusInProcess: return KCalCore::Incidence::StatusInProcess; case Kolab::StatusCancelled: return KCalCore::Incidence::StatusCanceled; case Kolab::StatusTentative: return KCalCore::Incidence::StatusTentative; case Kolab::StatusConfirmed: return KCalCore::Incidence::StatusConfirmed; case Kolab::StatusDraft: return KCalCore::Incidence::StatusDraft; case Kolab::StatusFinal: return KCalCore::Incidence::StatusFinal; default: qCCritical(PIMKOLAB_LOG) << "unhandled"; Q_ASSERT(0); } return KCalCore::Incidence::StatusNone; } Kolab::Status fromStatus(KCalCore::Incidence::Status s) { switch (s) { case KCalCore::Incidence::StatusNone: return Kolab::StatusUndefined; case KCalCore::Incidence::StatusNeedsAction: return Kolab::StatusNeedsAction; case KCalCore::Incidence::StatusCompleted: return Kolab::StatusCompleted; case KCalCore::Incidence::StatusInProcess: return Kolab::StatusInProcess; case KCalCore::Incidence::StatusCanceled: return Kolab::StatusCancelled; case KCalCore::Incidence::StatusTentative: return Kolab::StatusTentative; case KCalCore::Incidence::StatusConfirmed: return Kolab::StatusConfirmed; case KCalCore::Incidence::StatusDraft: return Kolab::StatusDraft; case KCalCore::Incidence::StatusFinal: return Kolab::StatusFinal; default: qCCritical(PIMKOLAB_LOG) << "unhandled"; Q_ASSERT(0); } return Kolab::StatusUndefined; } KCalCore::Attendee::PartStat toPartStat(Kolab::PartStatus p) { switch (p) { case Kolab::PartNeedsAction: return KCalCore::Attendee::NeedsAction; case Kolab::PartAccepted: return KCalCore::Attendee::Accepted; case Kolab::PartDeclined: return KCalCore::Attendee::Declined; case Kolab::PartTentative: return KCalCore::Attendee::Tentative; case Kolab::PartDelegated: return KCalCore::Attendee::Delegated; default: qCCritical(PIMKOLAB_LOG) << "unhandled"; Q_ASSERT(0); } return KCalCore::Attendee::NeedsAction; } Kolab::PartStatus fromPartStat(KCalCore::Attendee::PartStat p) { switch (p) { case KCalCore::Attendee::NeedsAction: return Kolab::PartNeedsAction; case KCalCore::Attendee::Accepted: return Kolab::PartAccepted; case KCalCore::Attendee::Declined: return Kolab::PartDeclined; case KCalCore::Attendee::Tentative: return Kolab::PartTentative; case KCalCore::Attendee::Delegated: return Kolab::PartDelegated; default: qCCritical(PIMKOLAB_LOG) << "unhandled"; Q_ASSERT(0); } return Kolab::PartNeedsAction; } KCalCore::Attendee::Role toRole(Kolab::Role r) { switch (r) { case Kolab::Required: return KCalCore::Attendee::ReqParticipant; case Kolab::Chair: return KCalCore::Attendee::Chair; case Kolab::Optional: return KCalCore::Attendee::OptParticipant; case Kolab::NonParticipant: return KCalCore::Attendee::NonParticipant; default: qCCritical(PIMKOLAB_LOG) << "unhandled"; Q_ASSERT(0); } return KCalCore::Attendee::ReqParticipant; } Kolab::Role fromRole(KCalCore::Attendee::Role r) { switch (r) { case KCalCore::Attendee::ReqParticipant: return Kolab::Required; case KCalCore::Attendee::Chair: return Kolab::Chair; case KCalCore::Attendee::OptParticipant: return Kolab::Optional; case KCalCore::Attendee::NonParticipant: return Kolab::NonParticipant; default: qCCritical(PIMKOLAB_LOG) << "unhandled"; Q_ASSERT(0); } return Kolab::Required; } template QString getCustomProperty(const QString &id, const T &e) { std::vector &props = e.customProperties(); foreach (const Kolab::CustomProperty &p, props) { if (fromStdString(p.identifier) == id) { return fromStdString(p.value); } } } template void setIncidence(KCalCore::Incidence &i, const T &e) { if (!e.uid().empty()) { i.setUid(fromStdString(e.uid())); } i.setCreated(toDate(e.created())); i.setLastModified(toDate(e.lastModified())); i.setRevision(e.sequence()); i.setSecrecy(toSecrecy(e.classification())); i.setCategories(toStringList(e.categories())); if (e.start().isValid()) { i.setDtStart(toDate(e.start())); i.setAllDay(e.start().isDateOnly()); } i.setSummary(fromStdString(e.summary())); //TODO detect richtext i.setDescription(fromStdString(e.description())); //TODO detect richtext i.setStatus(toStatus(e.status())); foreach (const Kolab::Attendee &a, e.attendees()) { /* * KCalCore always sets a UID if empty, but that's just a pointer, and not the uid of a real contact. * Since that means the semantics of the two are different, we have to store the kolab uid as a custom property. */ KCalCore::Attendee::Ptr attendee = KCalCore::Attendee::Ptr(new KCalCore::Attendee(fromStdString(a.contact().name()), fromStdString(a.contact().email()), a.rsvp(), toPartStat(a.partStat()), toRole(a.role()))); if (!a.contact().uid().empty()) { //TODO Identify contact from addressbook based on uid attendee->customProperties().setNonKDECustomProperty(CUSTOM_KOLAB_CONTACT_UUID, fromStdString(a.contact().uid())); } if (!a.delegatedTo().empty()) { if (a.delegatedTo().size() > 1) { qCWarning(PIMKOLAB_LOG) << "multiple delegatees are not supported"; } attendee->setDelegate(toMailto(a.delegatedTo().front().email(), a.delegatedTo().front().name()).toString()); } if (!a.delegatedFrom().empty()) { if (a.delegatedFrom().size() > 1) { qCWarning(PIMKOLAB_LOG) << "multiple delegators are not supported"; } attendee->setDelegator(toMailto(a.delegatedFrom().front().email(), a.delegatedFrom().front().name()).toString()); } if (a.cutype() != Kolab::CutypeIndividual) { attendee->customProperties().setNonKDECustomProperty(CUSTOM_KOLAB_CONTACT_CUTYPE, QString::number(a.cutype())); } i.addAttendee(attendee); } foreach (const Kolab::Attachment &a, e.attachments()) { KCalCore::Attachment::Ptr ptr; if (!a.uri().empty()) { ptr = KCalCore::Attachment::Ptr(new KCalCore::Attachment(fromStdString(a.uri()), fromStdString(a.mimetype()))); } else { ptr = KCalCore::Attachment::Ptr(new KCalCore::Attachment(QByteArray::fromRawData(a.data().c_str(), a.data().size()).toBase64(), fromStdString(a.mimetype()))); } if (!a.label().empty()) { ptr->setLabel(fromStdString(a.label())); } i.addAttachment(ptr); } QMap props; foreach (const Kolab::CustomProperty &prop, e.customProperties()) { QString key; if (prop.identifier.compare(0, 5, "X-KDE")) { key.append(QLatin1String("X-KOLAB-")); } key.append(fromStdString(prop.identifier)); props.insert(key.toLatin1(), fromStdString(prop.value)); // i.setCustomProperty("KOLAB", fromStdString(prop.identifier).toLatin1(), fromStdString(prop.value)); } i.setCustomProperties(props); } template void getIncidence(T &i, const I &e) { i.setUid(toStdString(e.uid())); i.setCreated(fromDate(e.created(), false)); i.setLastModified(fromDate(e.lastModified(), false)); i.setSequence(e.revision()); i.setClassification(fromSecrecy(e.secrecy())); i.setCategories(fromStringList(e.categories())); i.setStart(fromDate(e.dtStart(), e.allDay())); i.setSummary(toStdString(e.summary())); i.setDescription(toStdString(e.description())); i.setStatus(fromStatus(e.status())); std::vector attendees; foreach (const KCalCore::Attendee::Ptr &ptr, e.attendees()) { const QString &uid = ptr->customProperties().nonKDECustomProperty(CUSTOM_KOLAB_CONTACT_UUID); Kolab::Attendee a(Kolab::ContactReference(toStdString(ptr->email()), toStdString(ptr->name()), toStdString(uid))); a.setRSVP(ptr->RSVP()); a.setPartStat(fromPartStat(ptr->status())); a.setRole(fromRole(ptr->role())); if (!ptr->delegate().isEmpty()) { std::string name; const std::string &email = fromMailto(QUrl(ptr->delegate()), name); a.setDelegatedTo(std::vector() << Kolab::ContactReference(email, name)); } if (!ptr->delegator().isEmpty()) { std::string name; const std::string &email = fromMailto(QUrl(ptr->delegator()), name); a.setDelegatedFrom(std::vector() << Kolab::ContactReference(email, name)); } const QString &cutype = ptr->customProperties().nonKDECustomProperty(CUSTOM_KOLAB_CONTACT_CUTYPE); if (!cutype.isEmpty()) { a.setCutype(static_cast(cutype.toInt())); } attendees.push_back(a); } i.setAttendees(attendees); std::vector attachments; foreach (const KCalCore::Attachment::Ptr &ptr, e.attachments()) { Kolab::Attachment a; if (ptr->isUri()) { a.setUri(toStdString(ptr->uri()), toStdString(ptr->mimeType())); } else { a.setData(std::string(ptr->decodedData().data(), ptr->decodedData().size()), toStdString(ptr->mimeType())); } a.setLabel(toStdString(ptr->label())); attachments.push_back(a); } i.setAttachments(attachments); std::vector customProperties; const QMap &props = e.customProperties(); for (QMap::const_iterator it = props.begin(), end(props.end()); it != end; ++it) { QString key(it.key()); if (key == QLatin1String(CUSTOM_KOLAB_URL)) { continue; } customProperties.push_back(Kolab::CustomProperty(toStdString(key.remove(QStringLiteral("X-KOLAB-"))), toStdString(it.value()))); } i.setCustomProperties(customProperties); } int toWeekDay(Kolab::Weekday wday) { switch (wday) { case Kolab::Monday: return 1; case Kolab::Tuesday: return 2; case Kolab::Wednesday: return 3; case Kolab::Thursday: return 4; case Kolab::Friday: return 5; case Kolab::Saturday: return 6; case Kolab::Sunday: return 7; default: qCCritical(PIMKOLAB_LOG) << "unhandled"; Q_ASSERT(0); } return 1; } Kolab::Weekday fromWeekDay(int wday) { switch (wday) { case 1: return Kolab::Monday; case 2: return Kolab::Tuesday; case 3: return Kolab::Wednesday; case 4: return Kolab::Thursday; case 5: return Kolab::Friday; case 6: return Kolab::Saturday; case 7: return Kolab::Sunday; default: qCCritical(PIMKOLAB_LOG) << "unhandled"; Q_ASSERT(0); } return Kolab::Monday; } KCalCore::RecurrenceRule::PeriodType toRecurrenceType(Kolab::RecurrenceRule::Frequency freq) { switch (freq) { case Kolab::RecurrenceRule::FreqNone: qCWarning(PIMKOLAB_LOG) <<"no recurrence?"; break; case Kolab::RecurrenceRule::Yearly: return KCalCore::RecurrenceRule::rYearly; case Kolab::RecurrenceRule::Monthly: return KCalCore::RecurrenceRule::rMonthly; case Kolab::RecurrenceRule::Weekly: return KCalCore::RecurrenceRule::rWeekly; case Kolab::RecurrenceRule::Daily: return KCalCore::RecurrenceRule::rDaily; case Kolab::RecurrenceRule::Hourly: return KCalCore::RecurrenceRule::rHourly; case Kolab::RecurrenceRule::Minutely: return KCalCore::RecurrenceRule::rMinutely; case Kolab::RecurrenceRule::Secondly: return KCalCore::RecurrenceRule::rSecondly; default: qCCritical(PIMKOLAB_LOG) << "unhandled"; Q_ASSERT(0); } return KCalCore::RecurrenceRule::rNone; } Kolab::RecurrenceRule::Frequency fromRecurrenceType(KCalCore::RecurrenceRule::PeriodType freq) { switch (freq) { case KCalCore::RecurrenceRule::rNone: qCWarning(PIMKOLAB_LOG) <<"no recurrence?"; break; case KCalCore::RecurrenceRule::rYearly: return Kolab::RecurrenceRule::Yearly; case KCalCore::RecurrenceRule::rMonthly: return Kolab::RecurrenceRule::Monthly; case KCalCore::RecurrenceRule::rWeekly: return Kolab::RecurrenceRule::Weekly; case KCalCore::RecurrenceRule::rDaily: return Kolab::RecurrenceRule::Daily; case KCalCore::RecurrenceRule::rHourly: return Kolab::RecurrenceRule::Hourly; case KCalCore::RecurrenceRule::rMinutely: return Kolab::RecurrenceRule::Minutely; case KCalCore::RecurrenceRule::rSecondly: return Kolab::RecurrenceRule::Secondly; default: qCCritical(PIMKOLAB_LOG) << "unhandled"; Q_ASSERT(0); } return Kolab::RecurrenceRule::FreqNone; } KCalCore::RecurrenceRule::WDayPos toWeekDayPos(const Kolab::DayPos &dp) { return KCalCore::RecurrenceRule::WDayPos(dp.occurence(), toWeekDay(dp.weekday())); } Kolab::DayPos fromWeekDayPos(const KCalCore::RecurrenceRule::WDayPos &dp) { return Kolab::DayPos(dp.pos(), fromWeekDay(dp.day())); } template void setRecurrence(KCalCore::Incidence &e, const T &event) { const Kolab::RecurrenceRule &rrule = event.recurrenceRule(); if (rrule.isValid()) { KCalCore::Recurrence *rec = e.recurrence(); KCalCore::RecurrenceRule *defaultRR = rec->defaultRRule(true); Q_ASSERT(defaultRR); defaultRR->setWeekStart(toWeekDay(rrule.weekStart())); defaultRR->setRecurrenceType(toRecurrenceType(rrule.frequency())); defaultRR->setFrequency(rrule.interval()); if (rrule.end().isValid()) { rec->setEndDateTime(toDate(rrule.end())); //TODO date/datetime setEndDate(). With date-only the start date has to be taken into account. } else { rec->setDuration(rrule.count()); } if (!rrule.bysecond().empty()) { defaultRR->setBySeconds(QVector::fromStdVector(rrule.bysecond()).toList()); } if (!rrule.byminute().empty()) { defaultRR->setByMinutes(QVector::fromStdVector(rrule.byminute()).toList()); } if (!rrule.byhour().empty()) { defaultRR->setByHours(QVector::fromStdVector(rrule.byhour()).toList()); } if (!rrule.byday().empty()) { QList daypos; foreach (const Kolab::DayPos &dp, rrule.byday()) { daypos.append(toWeekDayPos(dp)); } defaultRR->setByDays(daypos); } if (!rrule.bymonthday().empty()) { defaultRR->setByMonthDays(QVector::fromStdVector(rrule.bymonthday()).toList()); } if (!rrule.byyearday().empty()) { defaultRR->setByYearDays(QVector::fromStdVector(rrule.byyearday()).toList()); } if (!rrule.byweekno().empty()) { defaultRR->setByWeekNumbers(QVector::fromStdVector(rrule.byweekno()).toList()); } if (!rrule.bymonth().empty()) { defaultRR->setByMonths(QVector::fromStdVector(rrule.bymonth()).toList()); } } foreach (const Kolab::cDateTime &dt, event.recurrenceDates()) { const QDateTime &date = toDate(dt); if (dt.isDateOnly()) { e.recurrence()->addRDate(date.date()); } else { e.recurrence()->addRDateTime(date); } } foreach (const Kolab::cDateTime &dt, event.exceptionDates()) { const QDateTime &date = toDate(dt); if (dt.isDateOnly()) { e.recurrence()->addExDate(date.date()); } else { e.recurrence()->addExDateTime(date); } } } template void getRecurrence(T &i, const I &e) { if (!e.recurs()) { return; } KCalCore::Recurrence *rec = e.recurrence(); KCalCore::RecurrenceRule *defaultRR = rec->defaultRRule(false); if (!defaultRR) { qCWarning(PIMKOLAB_LOG) <<"no recurrence"; return; } Q_ASSERT(defaultRR); Kolab::RecurrenceRule rrule; rrule.setWeekStart(fromWeekDay(defaultRR->weekStart())); rrule.setFrequency(fromRecurrenceType(defaultRR->recurrenceType())); rrule.setInterval(defaultRR->frequency()); if (defaultRR->duration() != 0) { //Inidcates if end date is set or not if (defaultRR->duration() > 0) { rrule.setCount(defaultRR->duration()); } } else { rrule.setEnd(fromDate(defaultRR->endDt(), e.allDay())); } rrule.setBysecond(defaultRR->bySeconds().toVector().toStdVector()); rrule.setByminute(defaultRR->byMinutes().toVector().toStdVector()); rrule.setByhour(defaultRR->byHours().toVector().toStdVector()); std::vector daypos; daypos.reserve(defaultRR->byDays().count()); foreach (const KCalCore::RecurrenceRule::WDayPos &dp, defaultRR->byDays()) { daypos.push_back(fromWeekDayPos(dp)); } rrule.setByday(daypos); rrule.setBymonthday(defaultRR->byMonthDays().toVector().toStdVector()); rrule.setByyearday(defaultRR->byYearDays().toVector().toStdVector()); rrule.setByweekno(defaultRR->byWeekNumbers().toVector().toStdVector()); rrule.setBymonth(defaultRR->byMonths().toVector().toStdVector()); i.setRecurrenceRule(rrule); std::vector rdates; foreach (const QDateTime &dt, rec->rDateTimes()) { rdates.push_back(fromDate(dt, e.allDay())); } foreach (const QDate &dt, rec->rDates()) { rdates.push_back(fromDate(QDateTime(dt, {}), true)); } i.setRecurrenceDates(rdates); std::vector exdates; foreach (const QDateTime &dt, rec->exDateTimes()) { exdates.push_back(fromDate(dt, e.allDay())); } foreach (const QDate &dt, rec->exDates()) { exdates.push_back(fromDate(QDateTime(dt, {}), true)); } i.setExceptionDates(exdates); if (!rec->exRules().empty()) { qCWarning(PIMKOLAB_LOG) <<"exrules are not supported"; } } template void setTodoEvent(KCalCore::Incidence &i, const T &e) { i.setPriority(toPriority(e.priority())); if (!e.location().empty()) { i.setLocation(fromStdString(e.location())); //TODO detect richtext } if (e.organizer().isValid()) { - i.setOrganizer(KCalCore::Person::Ptr(new KCalCore::Person(fromStdString(e.organizer().name()), fromStdString(e.organizer().email())))); //TODO handle uid too + i.setOrganizer(KCalCore::Person(fromStdString(e.organizer().name()), fromStdString(e.organizer().email()))); //TODO handle uid too } if (!e.url().empty()) { i.setNonKDECustomProperty(CUSTOM_KOLAB_URL, fromStdString(e.url())); } if (e.recurrenceID().isValid()) { i.setRecurrenceId(toDate(e.recurrenceID())); //TODO THISANDFUTURE } setRecurrence(i, e); foreach (const Kolab::Alarm &a, e.alarms()) { KCalCore::Alarm::Ptr alarm = KCalCore::Alarm::Ptr(new KCalCore::Alarm(&i)); switch (a.type()) { case Kolab::Alarm::EMailAlarm: { KCalCore::Person::List receipents; foreach (Kolab::ContactReference c, a.attendees()) { - KCalCore::Person::Ptr person = KCalCore::Person::Ptr(new KCalCore::Person(fromStdString(c.name()), fromStdString(c.email()))); + KCalCore::Person person(fromStdString(c.name()), fromStdString(c.email())); receipents.append(person); } alarm->setEmailAlarm(fromStdString(a.summary()), fromStdString(a.description()), receipents); break; } case Kolab::Alarm::DisplayAlarm: alarm->setDisplayAlarm(fromStdString(a.text())); break; case Kolab::Alarm::AudioAlarm: alarm->setAudioAlarm(fromStdString(a.audioFile().uri())); break; default: qCCritical(PIMKOLAB_LOG) << "invalid alarm"; } if (a.start().isValid()) { alarm->setTime(toDate(a.start())); } else if (a.relativeStart().isValid()) { if (a.relativeTo() == Kolab::End) { alarm->setEndOffset(toDuration(a.relativeStart())); } else { alarm->setStartOffset(toDuration(a.relativeStart())); } } alarm->setSnoozeTime(toDuration(a.duration())); alarm->setRepeatCount(a.numrepeat()); alarm->setEnabled(true); i.addAlarm(alarm); } } template void getTodoEvent(T &i, const I &e) { i.setPriority(fromPriority(e.priority())); i.setLocation(toStdString(e.location())); - if (e.organizer() && !e.organizer()->email().isEmpty()) { - i.setOrganizer(Kolab::ContactReference(Kolab::ContactReference::EmailReference, toStdString(e.organizer()->email()), toStdString(e.organizer()->name()))); //TODO handle uid too + if (!e.organizer().email().isEmpty()) { + i.setOrganizer(Kolab::ContactReference(Kolab::ContactReference::EmailReference, toStdString(e.organizer().email()), toStdString(e.organizer().name()))); //TODO handle uid too } i.setUrl(toStdString(e.nonKDECustomProperty(CUSTOM_KOLAB_URL))); i.setRecurrenceID(fromDate(e.recurrenceId(), e.allDay()), false); //TODO THISANDFUTURE getRecurrence(i, e); std::vector alarms; foreach (const KCalCore::Alarm::Ptr &a, e.alarms()) { Kolab::Alarm alarm; //TODO KCalCore disables alarms using KCalCore::Alarm::enabled() (X-KDE-KCALCORE-ENABLED) We should either delete the alarm, or store the attribute . //Ideally we would store the alarm somewhere and temporarily delete it, so we can restore it when parsing. For now we just remove disabled alarms. if (!a->enabled()) { qCWarning(PIMKOLAB_LOG) <<"skipping disabled alarm"; continue; } switch (a->type()) { case KCalCore::Alarm::Display: alarm = Kolab::Alarm(toStdString(a->text())); break; case KCalCore::Alarm::Email: { std::vector receipents; - foreach (const KCalCore::Person::Ptr &p, a->mailAddresses()) { - receipents.push_back(Kolab::ContactReference(toStdString(p->email()), toStdString(p->name()))); + foreach (const KCalCore::Person &p, a->mailAddresses()) { + receipents.push_back(Kolab::ContactReference(toStdString(p.email()), toStdString(p.name()))); } alarm = Kolab::Alarm(toStdString(a->mailSubject()), toStdString(a->mailText()), receipents); break; } case KCalCore::Alarm::Audio: { Kolab::Attachment audioFile; audioFile.setUri(toStdString(a->audioFile()), std::string()); alarm = Kolab::Alarm(audioFile); break; } default: qCCritical(PIMKOLAB_LOG) << "unhandled alarm"; } if (a->hasTime()) { alarm.setStart(fromDate(a->time(), false)); } else if (a->hasStartOffset()) { alarm.setRelativeStart(fromDuration(a->startOffset()), Kolab::Start); } else if (a->hasEndOffset()) { alarm.setRelativeStart(fromDuration(a->endOffset()), Kolab::End); } else { qCCritical(PIMKOLAB_LOG) << "alarm trigger is missing"; continue; } alarm.setDuration(fromDuration(a->snoozeTime()), a->repeatCount()); alarms.push_back(alarm); } i.setAlarms(alarms); } KCalCore::Event::Ptr toKCalCore(const Kolab::Event &event) { KCalCore::Event::Ptr e(new KCalCore::Event); setIncidence(*e, event); setTodoEvent(*e, event); if (event.end().isValid()) { e->setDtEnd(toDate(event.end())); } if (event.duration().isValid()) { e->setDuration(toDuration(event.duration())); } if (event.transparency()) { e->setTransparency(KCalCore::Event::Transparent); } else { e->setTransparency(KCalCore::Event::Opaque); } return e; } Kolab::Event fromKCalCore(const KCalCore::Event &event) { Kolab::Event e; getIncidence(e, event); getTodoEvent(e, event); if (event.hasEndDate()) { e.setEnd(fromDate(event.dtEnd(), event.allDay())); } else if (event.hasDuration()) { e.setDuration(fromDuration(event.duration())); } if (event.transparency() == KCalCore::Event::Transparent) { e.setTransparency(true); } else { e.setTransparency(false); } return e; } KCalCore::Todo::Ptr toKCalCore(const Kolab::Todo &todo) { KCalCore::Todo::Ptr e(new KCalCore::Todo); setIncidence(*e, todo); setTodoEvent(*e, todo); if (todo.due().isValid()) { e->setDtDue(toDate(todo.due())); } if (!todo.relatedTo().empty()) { e->setRelatedTo(Kolab::Conversion::fromStdString(todo.relatedTo().front()), KCalCore::Incidence::RelTypeParent); if (todo.relatedTo().size() > 1) { qCCritical(PIMKOLAB_LOG) << "only one relation support but got multiple"; } } e->setPercentComplete(todo.percentComplete()); return e; } Kolab::Todo fromKCalCore(const KCalCore::Todo &todo) { Kolab::Todo t; getIncidence(t, todo); getTodoEvent(t, todo); t.setDue(fromDate(todo.dtDue(true), todo.allDay())); t.setPercentComplete(todo.percentComplete()); const QString relatedTo = todo.relatedTo(KCalCore::Incidence::RelTypeParent); if (!relatedTo.isEmpty()) { std::vector relateds; relateds.push_back(Kolab::Conversion::toStdString(relatedTo)); t.setRelatedTo(relateds); } return t; } KCalCore::Journal::Ptr toKCalCore(const Kolab::Journal &journal) { KCalCore::Journal::Ptr e(new KCalCore::Journal); setIncidence(*e, journal); //TODO contacts return e; } Kolab::Journal fromKCalCore(const KCalCore::Journal &journal) { Kolab::Journal j; getIncidence(j, journal); //TODO contacts return j; } } } diff --git a/resources/kolab/pimkolab/freebusy/freebusy.cpp b/resources/kolab/pimkolab/freebusy/freebusy.cpp index ef7224a88..2e2d6ab38 100644 --- a/resources/kolab/pimkolab/freebusy/freebusy.cpp +++ b/resources/kolab/pimkolab/freebusy/freebusy.cpp @@ -1,314 +1,314 @@ /* * Copyright (C) 2012 Christian Mollekopf * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #include "freebusy.h" #include "pimkolab_debug.h" #include "conversion/kcalconversion.h" #include "conversion/commonconversion.h" #include "libkolab-version.h" #include #include #include #include // namespace KCalCore { // struct KCalFreebusy // { // // void init( const Event::List &eventList, const KDateTime &start, const KDateTime &end ) // { // mDtStart = start.toUtc(); // mDtEnd = end.toUtc(); // // // Loops through every event in the calendar // Event::List::ConstIterator it; // for ( it = eventList.constBegin(); it != eventList.constEnd(); ++it ) { // Event::Ptr event = *it; // // // If this event is transparent it shouldn't be in the freebusy list. // if ( event->transparency() == Event::Transparent ) { // continue; // } // // if ( event->hasRecurrenceId() ) { // continue; //TODO apply special period exception (duration could be different) // } // // const KDateTime eventStart = event->dtStart().toUtc(); // const KDateTime eventEnd = event->dtEnd().toUtc(); // // if ( event->recurs() ) { // const KCalCore::Duration duration( eventStart, eventEnd ); // const KCalCore::DateTimeList list = event->recurrence()->timesInInterval(start, end); // foreach (const KDateTime &dt, list) { // const KDateTime utc = dt.toUtc(); // addLocalPeriod(utc, duration.end(utc) ); // } // } else { // addLocalPeriod( eventStart, eventEnd ); // } // } // // // q->sortList(); // } // // bool addLocalPeriod( // const KDateTime &eventStart, // const KDateTime &eventEnd ) // { // KDateTime tmpStart; // KDateTime tmpEnd; // // //Check to see if the start *or* end of the event is // //between the start and end of the freebusy dates. // if ( !( ( ( mDtStart.secsTo( eventStart ) >= 0 ) && // ( eventStart.secsTo( mDtEnd ) >= 0 ) ) || // ( ( mDtStart.secsTo( eventEnd ) >= 0 ) && // ( eventEnd.secsTo( mDtEnd ) >= 0 ) ) ) ) { // qCDebug(PIMKOLAB_LOG) << "out of scope"; // return false; // } // // // qCDebug(PIMKOLAB_LOG) << eventStart.date().toString() << eventStart.time().toString() << mDtStart.toString(); // if ( eventStart < mDtStart ) { //eventStart is before start // // qCDebug(PIMKOLAB_LOG) << "use start"; // tmpStart = mDtStart; // } else { // tmpStart = eventStart; // } // // qCDebug(PIMKOLAB_LOG) << eventEnd.date().toString() << eventEnd.time().toString() << mDtEnd.toString(); // if ( eventEnd > mDtEnd ) { //event end is after dtEnd // // qCDebug(PIMKOLAB_LOG) << "use end"; // tmpEnd = mDtEnd; // } else { // tmpEnd = eventEnd; // } // // // qCDebug(PIMKOLAB_LOG) << "########## " << tmpStart.isValid(); // Q_ASSERT(tmpStart.isValid()); // Q_ASSERT(tmpEnd.isValid()); // // qCDebug(PIMKOLAB_LOG) << tmpStart.date().toString() << tmpStart.time().toString() << tmpStart.toString(); // // FreeBusyPeriod p( tmpStart, tmpEnd ); // mBusyPeriods.append( p ); // // return true; // } // // KDateTime mDtStart; // KDateTime mDtEnd; // end datetime // FreeBusyPeriod::List mBusyPeriods; // list of periods // // }; // // } // Namespace namespace Kolab { namespace FreebusyUtils { static QString createUuid() { const QString uuid = QUuid::createUuid().toString(); return uuid.mid(1, uuid.size()-2); } Kolab::Period addLocalPeriod(const QDateTime &eventStart, const QDateTime &eventEnd, const QDateTime &mDtStart, const QDateTime &mDtEnd, bool allDay) { QDateTime tmpStart; QDateTime tmpEnd; //Check to see if the start *or* end of the event is //between the start and end of the freebusy dates. if (!(((mDtStart <= eventStart) && (eventStart <= mDtEnd)) || ((mDtStart <= eventEnd) && (eventEnd <= mDtEnd)))) { qCDebug(PIMKOLAB_LOG) << "event is not within the fb range, skipping"; return Kolab::Period(); } if (eventStart < mDtStart) { //eventStart is before start tmpStart = mDtStart; } else { tmpStart = eventStart; } // qCDebug(PIMKOLAB_LOG) << eventEnd.date().toString() << eventEnd.time().toString() << mDtEnd.toString(); if (eventEnd > mDtEnd) { //event end is after dtEnd tmpEnd = mDtEnd; } else { tmpEnd = eventEnd; } Q_ASSERT(tmpStart.isValid()); Q_ASSERT(tmpEnd.isValid()); if (allDay) { tmpStart.setTime(QTime(0, 0, 0, 0)); } if (allDay) { tmpEnd.setTime(QTime(23, 59, 59, 999)); //The window is inclusive } return Kolab::Period(Kolab::Conversion::fromDate(tmpStart, allDay), Kolab::Conversion::fromDate(tmpEnd, allDay)); } Freebusy generateFreeBusy(const std::vector< Event > &events, const cDateTime &startDate, const cDateTime &endDate) { QList list; list.reserve(events.size()); for (const Kolab::Event &e : events) { list.append(Kolab::Conversion::toKCalCore(e)); } - KCalCore::Person::Ptr person(new KCalCore::Person(QStringLiteral("dummyname"), QStringLiteral("dummyemail"))); + KCalCore::Person person(QStringLiteral("dummyname"), QStringLiteral("dummyemail")); return generateFreeBusy(list, Kolab::Conversion::toDate(startDate), Kolab::Conversion::toDate(endDate), person, startDate.isDateOnly()); } -Freebusy generateFreeBusy(const QList &events, const QDateTime &startDate, const QDateTime &endDate, const KCalCore::Person::Ptr &organizer, bool allDay) +Freebusy generateFreeBusy(const QList &events, const QDateTime &startDate, const QDateTime &endDate, const KCalCore::Person &organizer, bool allDay) { /* * TODO the conversion of date-only values to date-time is only necessary because xCal doesn't allow date only. iCalendar doesn't seem to make this restriction so it looks like a bug. */ QDateTime start = startDate.toUTC(); if (allDay) { start.setTime(QTime(0, 0, 0, 0)); } QDateTime end = endDate.toUTC(); if (allDay) { end = end.addDays(1); end.setTime(QTime(0, 0, 0, 0)); //The window is inclusive } //TODO try to merge that with KCalCore::Freebusy std::vector freebusyPeriods; Q_FOREACH (const KCalCore::Event::Ptr &event, events) { // If this event is transparent it shouldn't be in the freebusy list. if (event->transparency() == KCalCore::Event::Transparent) { continue; } if (event->hasRecurrenceId()) { continue; //TODO apply special period exception (duration could be different) } const QDateTime eventStart = event->dtStart().toUTC(); const QDateTime eventEnd = event->dtEnd().toUTC(); std::vector periods; if (event->recurs()) { const KCalCore::Duration duration(eventStart, eventEnd); const auto list = event->recurrence()->timesInInterval(start, end); for (const auto &dt : list) { const auto utc = dt.toUTC(); const Kolab::Period &period = addLocalPeriod(utc, duration.end(utc), start, end, allDay); if (period.isValid()) { periods.push_back(period); } } } else { const Kolab::Period &period = addLocalPeriod(eventStart, eventEnd, start, end, allDay); if (period.isValid()) { periods.push_back(period); } } if (!periods.empty()) { Kolab::FreebusyPeriod period; period.setPeriods(periods); //TODO get busy type from event (out-of-office, tentative) period.setType(Kolab::FreebusyPeriod::Busy); period.setEvent(Kolab::Conversion::toStdString(event->uid()), Kolab::Conversion::toStdString(event->summary()), Kolab::Conversion::toStdString(event->location())); freebusyPeriods.push_back(period); } } Kolab::Freebusy freebusy; freebusy.setStart(Kolab::Conversion::fromDate(start, allDay)); freebusy.setEnd(Kolab::Conversion::fromDate(end, allDay)); freebusy.setPeriods(freebusyPeriods); freebusy.setUid(createUuid().toStdString()); freebusy.setTimestamp(Kolab::Conversion::fromDate(QDateTime::currentDateTimeUtc(), false)); - if (organizer) { - freebusy.setOrganizer(ContactReference(Kolab::ContactReference::EmailReference, Kolab::Conversion::toStdString(organizer->email()), Kolab::Conversion::toStdString(organizer->name()))); + if (!organizer.isEmpty()) { + freebusy.setOrganizer(ContactReference(Kolab::ContactReference::EmailReference, Kolab::Conversion::toStdString(organizer.email()), Kolab::Conversion::toStdString(organizer.name()))); } return freebusy; } Freebusy aggregateFreeBusy(const std::vector< Freebusy > &fbList, const std::string &organizerEmail, const std::string &organizerName, bool simple) { std::vector periods; QDateTime start; QDateTime end; bool allDay = false; Q_FOREACH (const Freebusy &fb, fbList) { const QDateTime &tmpStart = Kolab::Conversion::toDate(fb.start()); if (!start.isValid() || tmpStart < start) { start = tmpStart; allDay |= fb.start().isDateOnly(); } const QDateTime &tmpEnd = Kolab::Conversion::toDate(fb.end()); if (!end.isValid() || tmpEnd > end) { end = tmpEnd; allDay |= fb.start().isDateOnly(); } Q_FOREACH (const Kolab::FreebusyPeriod &period, fb.periods()) { Kolab::FreebusyPeriod simplifiedPeriod; simplifiedPeriod.setPeriods(period.periods()); simplifiedPeriod.setType(period.type()); if (!simple) { //Don't copy and reset to avoid unintentional information leaking into simple lists simplifiedPeriod.setEvent(period.eventSummary(), period.eventUid(), period.eventLocation()); } periods.push_back(simplifiedPeriod); } } Freebusy aggregateFB; aggregateFB.setStart(Kolab::Conversion::fromDate(start, allDay)); aggregateFB.setEnd(Kolab::Conversion::fromDate(end, allDay)); aggregateFB.setPeriods(periods); aggregateFB.setUid(createUuid().toStdString()); aggregateFB.setTimestamp(Kolab::Conversion::fromDate(QDateTime::currentDateTimeUtc(), false)); aggregateFB.setOrganizer(ContactReference(Kolab::ContactReference::EmailReference, organizerEmail, organizerName)); return aggregateFB; } std::string toIFB(const Kolab::Freebusy &freebusy) { KCalCore::FreeBusy::Ptr fb(new KCalCore::FreeBusy(Kolab::Conversion::toDate(freebusy.start()), Kolab::Conversion::toDate(freebusy.end()))); KCalCore::FreeBusyPeriod::List list; Q_FOREACH (const Kolab::FreebusyPeriod &fbPeriod, freebusy.periods()) { Q_FOREACH (const Kolab::Period &p, fbPeriod.periods()) { KCalCore::FreeBusyPeriod period(Kolab::Conversion::toDate(p.start), Kolab::Conversion::toDate(p.end)); // period.setSummary("summary"); Doesn't even work. X-SUMMARY is read though (just not written out)s //TODO list.append(period); } } fb->addPeriods(list); fb->setUid(QString::fromStdString(freebusy.uid())); - fb->setOrganizer(KCalCore::Person::Ptr(new KCalCore::Person(Conversion::fromStdString(freebusy.organizer().name()), Conversion::fromStdString(freebusy.organizer().email())))); + fb->setOrganizer(KCalCore::Person(Conversion::fromStdString(freebusy.organizer().name()), Conversion::fromStdString(freebusy.organizer().email()))); fb->setLastModified(Kolab::Conversion::toDate(freebusy.timestamp())); KCalCore::ICalFormat format; format.setApplication(QStringLiteral("libkolab"), QStringLiteral(LIBKOLAB_LIB_VERSION_STRING)); QString data = format.createScheduleMessage(fb, KCalCore::iTIPPublish); return Conversion::toStdString(data); } } } diff --git a/resources/kolab/pimkolab/freebusy/freebusy.h b/resources/kolab/pimkolab/freebusy/freebusy.h index d6d8c7e75..4d7e9c636 100644 --- a/resources/kolab/pimkolab/freebusy/freebusy.h +++ b/resources/kolab/pimkolab/freebusy/freebusy.h @@ -1,36 +1,36 @@ /* * Copyright (C) 2012 Christian Mollekopf * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #ifndef FREEBUSY_H #define FREEBUSY_H #include "kolab_export.h" #include #include #include namespace Kolab { namespace FreebusyUtils { -KOLAB_EXPORT Freebusy generateFreeBusy(const QList &events, const QDateTime &startDate, const QDateTime &endDate, const KCalCore::Person::Ptr &organizer, bool allDay); +KOLAB_EXPORT Freebusy generateFreeBusy(const QList &events, const QDateTime &startDate, const QDateTime &endDate, const KCalCore::Person &organizer, bool allDay); KOLAB_EXPORT std::string toIFB(const Kolab::Freebusy &); Kolab::Freebusy generateFreeBusy(const std::vector &events, const Kolab::cDateTime &startDate, const Kolab::cDateTime &endDate); KOLAB_EXPORT Kolab::Freebusy aggregateFreeBusy(const std::vector &fbs, const std::string &organizerEmail, const std::string &organizerName, bool simple = true); } } #endif // FREEBUSY_H diff --git a/resources/kolab/pimkolab/icalendar/imip.cpp b/resources/kolab/pimkolab/icalendar/imip.cpp index bcdf001eb..50c62da54 100644 --- a/resources/kolab/pimkolab/icalendar/imip.cpp +++ b/resources/kolab/pimkolab/icalendar/imip.cpp @@ -1,229 +1,229 @@ /* Copyright (C) 2012 Christian Mollekopf This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "imip.h" #include "pimkolab_debug.h" #include #include #include #include /* * The code in here is copy paste work from kdepim/calendarsupport. * * We need to refactor the code there and move the relevant parts to kdepimlibs to make it reusable. * * */ //From MailClient::send KMime::Message::Ptr createMessage(const QString &from, const QString &_to, const QString &cc, const QString &subject, const QString &body, bool hidden, bool bccMe, const QString &attachment /*, const QString &mailTransport */) { Q_UNUSED(hidden); const bool outlookConformInvitation = false; QString userAgent = QStringLiteral("libkolab"); // We must have a recipients list for most MUAs. Thus, if the 'to' list // is empty simply use the 'from' address as the recipient. QString to = _to; if (to.isEmpty()) { to = from; } qCDebug(PIMKOLAB_LOG) << "\nFrom:" << from << "\nTo:" << to << "\nCC:" << cc << "\nSubject:" << subject << "\nBody: \n" << body << "\nAttachment:\n" << attachment /*<< "\nmailTransport: " << mailTransport*/; // Now build the message we like to send. The message KMime::Message::Ptr instance // will be the root message that has 2 additional message. The body itself and // the attached cal.ics calendar file. KMime::Message::Ptr message = KMime::Message::Ptr(new KMime::Message); message->contentTransferEncoding()->clear(); // 7Bit, decoded. // Set the headers message->userAgent()->fromUnicodeString(userAgent, "utf-8"); message->from()->fromUnicodeString(from, "utf-8"); message->to()->fromUnicodeString(to, "utf-8"); message->cc()->fromUnicodeString(cc, "utf-8"); if (bccMe) { message->bcc()->fromUnicodeString(from, "utf-8"); //from==me, right? } message->date()->setDateTime(QDateTime::currentDateTime()); message->subject()->fromUnicodeString(subject, "utf-8"); if (outlookConformInvitation) { message->contentType()->setMimeType("text/calendar"); message->contentType()->setCharset("utf-8"); message->contentType()->setName(QStringLiteral("cal.ics"), "utf-8"); message->contentType()->setParameter(QStringLiteral("method"), QStringLiteral("request")); if (!attachment.isEmpty()) { KMime::Headers::ContentDisposition *disposition = new KMime::Headers::ContentDisposition(); disposition->setDisposition(KMime::Headers::CDinline); message->setHeader(disposition); message->contentTransferEncoding()->setEncoding(KMime::Headers::CEquPr); message->setBody(KMime::CRLFtoLF(attachment.toUtf8())); } } else { // We need to set following 4 lines by hand else KMime::Content::addContent // will create a new Content instance for us to attach the main message // what we don't need cause we already have the main message instance where // 2 additional messages are attached. KMime::Headers::ContentType *ct = message->contentType(); ct->setMimeType("multipart/mixed"); ct->setBoundary(KMime::multiPartBoundary()); ct->setCategory(KMime::Headers::CCcontainer); // Set the first multipart, the body message. KMime::Content *bodyMessage = new KMime::Content; KMime::Headers::ContentDisposition *bodyDisposition = new KMime::Headers::ContentDisposition(); bodyDisposition->setDisposition(KMime::Headers::CDinline); bodyMessage->contentType()->setMimeType("text/plain"); bodyMessage->contentType()->setCharset("utf-8"); bodyMessage->contentTransferEncoding()->setEncoding(KMime::Headers::CEquPr); bodyMessage->setBody(KMime::CRLFtoLF(body.toUtf8())); message->addContent(bodyMessage); // Set the sedcond multipart, the attachment. if (!attachment.isEmpty()) { KMime::Content *attachMessage = new KMime::Content; KMime::Headers::ContentDisposition *attachDisposition = new KMime::Headers::ContentDisposition(); attachDisposition->setDisposition(KMime::Headers::CDattachment); attachMessage->contentType()->setMimeType("text/calendar"); attachMessage->contentType()->setCharset("utf-8"); attachMessage->contentType()->setName(QStringLiteral("cal.ics"), "utf-8"); attachMessage->contentType()->setParameter(QStringLiteral("method"), QStringLiteral("request")); attachMessage->setHeader(attachDisposition); attachMessage->contentTransferEncoding()->setEncoding(KMime::Headers::CEquPr); attachMessage->setBody(KMime::CRLFtoLF(attachment.toUtf8())); message->addContent(attachMessage); } } // Job done, attach the both multiparts and assemble the message. message->assemble(); return message; } //From MailClient::mailAttendees QByteArray mailAttendees(const KCalCore::IncidenceBase::Ptr &incidence, // const KPIMIdentities::Identity &identity, bool bccMe, const QString &attachment /*const QString &mailTransport */) { KCalCore::Attendee::List attendees = incidence->attendees(); if (attendees.isEmpty()) { qCWarning(PIMKOLAB_LOG) << "There are no attendees to e-mail"; return QByteArray(); } - const QString from = incidence->organizer()->fullName(); - const QString organizerEmail = incidence->organizer()->email(); + const QString from = incidence->organizer().fullName(); + const QString organizerEmail = incidence->organizer().email(); QStringList toList; QStringList ccList; const int numberOfAttendees(attendees.count()); for (int i = 0; i < numberOfAttendees; ++i) { KCalCore::Attendee::Ptr a = attendees.at(i); const QString email = a->email(); if (email.isEmpty()) { continue; } // In case we (as one of our identities) are the organizer we are sending // this mail. We could also have added ourselves as an attendee, in which // case we don't want to send ourselves a notification mail. if (organizerEmail == email) { continue; } // Build a nice address for this attendee including the CN. QString tname, temail; const QString username = KEmailAddress::quoteNameIfNecessary(a->name()); // ignore the return value from extractEmailAddressAndName() because // it will always be false since tusername does not contain "@domain". KEmailAddress::extractEmailAddressAndName(username, temail /*byref*/, tname /*byref*/); tname += QLatin1String(" <") + email + QLatin1Char('>'); // Optional Participants and Non-Participants are copied on the email if (a->role() == KCalCore::Attendee::OptParticipant || a->role() == KCalCore::Attendee::NonParticipant) { ccList << tname; } else { toList << tname; } } if (toList.isEmpty() && ccList.isEmpty()) { // Not really to be called a groupware meeting, eh qCWarning(PIMKOLAB_LOG) << "There are really no attendees to e-mail"; return QByteArray(); } QString to; if (!toList.isEmpty()) { to = toList.join(QLatin1String(", ")); } QString cc; if (!ccList.isEmpty()) { cc = ccList.join(QLatin1String(", ")); } QString subject; if (incidence->type() != KCalCore::Incidence::TypeFreeBusy) { KCalCore::Incidence::Ptr inc = incidence.staticCast(); subject = inc->summary(); } else { subject = QStringLiteral("Free Busy Object"); } const QString body = KCalUtils::IncidenceFormatter::mailBodyStr(incidence); return createMessage(/* identity, */ from, to, cc, subject, body, false, bccMe, attachment /*, mailTransport */)->encodedContent(); } QByteArray mailOrganizer(const KCalCore::IncidenceBase::Ptr &incidence, // const KPIMIdentities::Identity &identity, const QString &from, bool bccMe, const QString &attachment, const QString &sub /*, const QString &mailTransport*/) { - const QString to = incidence->organizer()->fullName(); + const QString to = incidence->organizer().fullName(); QString subject = sub; if (incidence->type() != KCalCore::Incidence::TypeFreeBusy) { KCalCore::Incidence::Ptr inc = incidence.staticCast(); if (subject.isEmpty()) { subject = inc->summary(); } } else { subject = QStringLiteral("Free Busy Message"); } QString body = KCalUtils::IncidenceFormatter::mailBodyStr(incidence); return createMessage(/*identity, */ from, to, QString(), subject, body, false, bccMe, attachment /*, mailTransport */)->encodedContent(); } diff --git a/resources/kolab/pimkolab/kolabformatV2/incidence.cpp b/resources/kolab/pimkolab/kolabformatV2/incidence.cpp index 36770e698..651fe5f12 100644 --- a/resources/kolab/pimkolab/kolabformatV2/incidence.cpp +++ b/resources/kolab/pimkolab/kolabformatV2/incidence.cpp @@ -1,1056 +1,1056 @@ /* This file is part of the kolab resource - the implementation of the Kolab storage format. See www.kolab.org for documentation on this. Copyright (c) 2004 Bo Thorsen This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. In addition, as a special exception, the copyright holders give permission to link the code of this program with any edition of the Qt library by Trolltech AS, Norway (or with modified versions of Qt that use the same license as Qt), and distribute linked combinations including the two. You must obey the GNU General Public License in all respects for all of the code used other than Qt. If you modify this file, you may extend this exception to your version of the file, but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. */ #include "incidence.h" #include "libkolab-version.h" #include "utils/porting.h" #include "pimkolab_debug.h" #include #include #include #include using namespace KolabV2; Incidence::Incidence(const QString &tz, const KCalCore::Incidence::Ptr &incidence) : KolabBase(tz) , mFloatingStatus(Unset) , mHasAlarm(false) , mPriority(0) { Q_UNUSED(incidence); } Incidence::~Incidence() { } void Incidence::setPriority(int priority) { mPriority = priority; } int Incidence::priority() const { return mPriority; } void Incidence::setSummary(const QString &summary) { mSummary = summary; } QString Incidence::summary() const { return mSummary; } void Incidence::setLocation(const QString &location) { mLocation = location; } QString Incidence::location() const { return mLocation; } void Incidence::setOrganizer(const Email &organizer) { mOrganizer = organizer; } KolabBase::Email Incidence::organizer() const { return mOrganizer; } void Incidence::setStartDate(const KDateTime &startDate) { mStartDate = startDate; if (mFloatingStatus == AllDay) { qCDebug(PIMKOLAB_LOG) <<"ERROR: Time on start date but no time on the event"; } mFloatingStatus = HasTime; } void Incidence::setStartDate(const QDate &startDate) { mStartDate = KDateTime(startDate); if (mFloatingStatus == HasTime) { qCDebug(PIMKOLAB_LOG) <<"ERROR: No time on start date but time on the event"; } mFloatingStatus = AllDay; } void Incidence::setStartDate(const QString &startDate) { if (startDate.length() > 10) { // This is a date + time setStartDate(stringToDateTime(startDate)); } else { // This is only a date setStartDate(stringToDate(startDate)); } } KDateTime Incidence::startDate() const { return mStartDate; } void Incidence::setAlarm(float alarm) { mAlarm = alarm; mHasAlarm = true; } float Incidence::alarm() const { return mAlarm; } Incidence::Recurrence Incidence::recurrence() const { return mRecurrence; } void Incidence::addAttendee(const Attendee &attendee) { mAttendees.append(attendee); } QList &Incidence::attendees() { return mAttendees; } const QList &Incidence::attendees() const { return mAttendees; } void Incidence::setInternalUID(const QString &iuid) { mInternalUID = iuid; } QString Incidence::internalUID() const { return mInternalUID; } bool Incidence::loadAttendeeAttribute(QDomElement &element, Attendee &attendee) { for (QDomNode n = element.firstChild(); !n.isNull(); n = n.nextSibling()) { if (n.isComment()) { continue; } if (n.isElement()) { QDomElement e = n.toElement(); QString tagName = e.tagName(); if (tagName == QLatin1String("display-name")) { attendee.displayName = e.text(); } else if (tagName == QLatin1String("smtp-address")) { attendee.smtpAddress = e.text(); } else if (tagName == QLatin1String("status")) { attendee.status = e.text(); } else if (tagName == QLatin1String("request-response")) { // This sets reqResp to false, if the text is "false". Otherwise it // sets it to true. This means the default setting is true. attendee.requestResponse = (e.text().toLower() != QLatin1String("false")); } else if (tagName == QLatin1String("invitation-sent")) { // Like above, only this defaults to false attendee.invitationSent = (e.text().toLower() != QLatin1String("true")); } else if (tagName == QLatin1String("role")) { attendee.role = e.text(); } else if (tagName == QLatin1String("delegated-to")) { attendee.delegate = e.text(); } else if (tagName == QLatin1String("delegated-from")) { attendee.delegator = e.text(); } else { // TODO: Unhandled tag - save for later storage qCDebug(PIMKOLAB_LOG) <<"Warning: Unhandled tag" << e.tagName(); } } else { qCDebug(PIMKOLAB_LOG) <<"Node is not a comment or an element???"; } } return true; } void Incidence::saveAttendeeAttribute(QDomElement &element, const Attendee &attendee) const { QDomElement e = element.ownerDocument().createElement(QStringLiteral("attendee")); element.appendChild(e); writeString(e, QStringLiteral("display-name"), attendee.displayName); writeString(e, QStringLiteral("smtp-address"), attendee.smtpAddress); writeString(e, QStringLiteral("status"), attendee.status); writeString(e, QStringLiteral("request-response"), (attendee.requestResponse ? QStringLiteral("true") : QStringLiteral("false"))); writeString(e, QStringLiteral("invitation-sent"), (attendee.invitationSent ? QStringLiteral("true") : QStringLiteral("false"))); writeString(e, QStringLiteral("role"), attendee.role); writeString(e, QStringLiteral("delegated-to"), attendee.delegate); writeString(e, QStringLiteral("delegated-from"), attendee.delegator); } void Incidence::saveAttendees(QDomElement &element) const { foreach (const Attendee &attendee, mAttendees) { saveAttendeeAttribute(element, attendee); } } void Incidence::saveAttachments(QDomElement &element) const { foreach (KCalCore::Attachment::Ptr a, mAttachments) { if (a->isUri()) { writeString(element, QStringLiteral("link-attachment"), a->uri()); } else if (a->isBinary()) { writeString(element, QStringLiteral("inline-attachment"), a->label()); } } } void Incidence::saveAlarms(QDomElement &element) const { if (mAlarms.isEmpty()) { return; } QDomElement list = element.ownerDocument().createElement(QStringLiteral("advanced-alarms")); element.appendChild(list); foreach (KCalCore::Alarm::Ptr a, mAlarms) { QDomElement e = list.ownerDocument().createElement(QStringLiteral("alarm")); list.appendChild(e); writeString(e, QStringLiteral("enabled"), a->enabled() ? QStringLiteral("1") : QStringLiteral("0")); if (a->hasStartOffset()) { writeString(e, QStringLiteral("start-offset"), QString::number(a->startOffset().asSeconds()/60)); } if (a->hasEndOffset()) { writeString(e, QStringLiteral("end-offset"), QString::number(a->endOffset().asSeconds()/60)); } if (a->repeatCount()) { writeString(e, QStringLiteral("repeat-count"), QString::number(a->repeatCount())); writeString(e, QStringLiteral("repeat-interval"), QString::number(a->snoozeTime().asSeconds())); } switch (a->type()) { case KCalCore::Alarm::Invalid: break; case KCalCore::Alarm::Display: e.setAttribute(QStringLiteral("type"), QStringLiteral("display")); writeString(e, QStringLiteral("text"), a->text()); break; case KCalCore::Alarm::Procedure: e.setAttribute(QStringLiteral("type"), QStringLiteral("procedure")); writeString(e, QStringLiteral("program"), a->programFile()); writeString(e, QStringLiteral("arguments"), a->programArguments()); break; case KCalCore::Alarm::Email: { e.setAttribute(QStringLiteral("type"), QStringLiteral("email")); QDomElement addresses = e.ownerDocument().createElement(QStringLiteral("addresses")); e.appendChild(addresses); - foreach (const KCalCore::Person::Ptr &person, a->mailAddresses()) { - writeString(addresses, QStringLiteral("address"), person->fullName()); + foreach (const KCalCore::Person &person, a->mailAddresses()) { + writeString(addresses, QStringLiteral("address"), person.fullName()); } writeString(e, QStringLiteral("subject"), a->mailSubject()); writeString(e, QStringLiteral("mail-text"), a->mailText()); QDomElement attachments = e.ownerDocument().createElement(QStringLiteral("attachments")); e.appendChild(attachments); foreach (const QString &attachment, a->mailAttachments()) { writeString(attachments, QStringLiteral("attachment"), attachment); } break; } case KCalCore::Alarm::Audio: e.setAttribute(QStringLiteral("type"), QStringLiteral("audio")); writeString(e, QStringLiteral("file"), a->audioFile()); break; default: qCWarning(PIMKOLAB_LOG) << "Unhandled alarm type:" << a->type(); break; } } } void Incidence::saveRecurrence(QDomElement &element) const { QDomElement e = element.ownerDocument().createElement(QStringLiteral("recurrence")); element.appendChild(e); e.setAttribute(QStringLiteral("cycle"), mRecurrence.cycle); if (!mRecurrence.type.isEmpty()) { e.setAttribute(QStringLiteral("type"), mRecurrence.type); } writeString(e, QStringLiteral("interval"), QString::number(mRecurrence.interval)); foreach (const QString &recurrence, mRecurrence.days) { writeString(e, QStringLiteral("day"), recurrence); } if (!mRecurrence.dayNumber.isEmpty()) { writeString(e, QStringLiteral("daynumber"), mRecurrence.dayNumber); } if (!mRecurrence.month.isEmpty()) { writeString(e, QStringLiteral("month"), mRecurrence.month); } if (!mRecurrence.rangeType.isEmpty()) { QDomElement range = element.ownerDocument().createElement(QStringLiteral("range")); e.appendChild(range); range.setAttribute(QStringLiteral("type"), mRecurrence.rangeType); QDomText t = element.ownerDocument().createTextNode(mRecurrence.range); range.appendChild(t); } foreach (const QDate &date, mRecurrence.exclusions) { writeString(e, QStringLiteral("exclusion"), dateToString(date)); } } void Incidence::loadRecurrence(const QDomElement &element) { mRecurrence.interval = 0; mRecurrence.cycle = element.attribute(QStringLiteral("cycle")); mRecurrence.type = element.attribute(QStringLiteral("type")); for (QDomNode n = element.firstChild(); !n.isNull(); n = n.nextSibling()) { if (n.isComment()) { continue; } if (n.isElement()) { QDomElement e = n.toElement(); QString tagName = e.tagName(); if (tagName == QLatin1String("interval")) { //kolab/issue4229, sometimes the interval value can be empty if (e.text().isEmpty() || e.text().toInt() <= 0) { mRecurrence.interval = 1; } else { mRecurrence.interval = e.text().toInt(); } } else if (tagName == QLatin1String("day")) { // can be present multiple times mRecurrence.days.append(e.text()); } else if (tagName == QLatin1String("daynumber")) { mRecurrence.dayNumber = e.text(); } else if (tagName == QLatin1String("month")) { mRecurrence.month = e.text(); } else if (tagName == QLatin1String("range")) { mRecurrence.rangeType = e.attribute(QStringLiteral("type")); mRecurrence.range = e.text(); } else if (tagName == QLatin1String("exclusion")) { mRecurrence.exclusions.append(stringToDate(e.text())); } else { // TODO: Unhandled tag - save for later storage qCDebug(PIMKOLAB_LOG) <<"Warning: Unhandled tag" << e.tagName(); } } } } static void loadAddressesHelper(const QDomElement &element, const KCalCore::Alarm::Ptr &a) { for (QDomNode n = element.firstChild(); !n.isNull(); n = n.nextSibling()) { if (n.isComment()) { continue; } if (n.isElement()) { QDomElement e = n.toElement(); QString tagName = e.tagName(); if (tagName == QLatin1String("address")) { a->addMailAddress(KCalCore::Person::fromFullName(e.text())); } else { qCWarning(PIMKOLAB_LOG) << "Unhandled tag" << tagName; } } } } static void loadAttachmentsHelper(const QDomElement &element, const KCalCore::Alarm::Ptr &a) { for (QDomNode n = element.firstChild(); !n.isNull(); n = n.nextSibling()) { if (n.isComment()) { continue; } if (n.isElement()) { QDomElement e = n.toElement(); QString tagName = e.tagName(); if (tagName == QLatin1String("attachment")) { a->addMailAttachment(e.text()); } else { qCWarning(PIMKOLAB_LOG) << "Unhandled tag" << tagName; } } } } static void loadAlarmHelper(const QDomElement &element, const KCalCore::Alarm::Ptr &a) { for (QDomNode n = element.firstChild(); !n.isNull(); n = n.nextSibling()) { if (n.isComment()) { continue; } if (n.isElement()) { QDomElement e = n.toElement(); QString tagName = e.tagName(); if (tagName == QLatin1String("start-offset")) { a->setStartOffset(e.text().toInt()*60); } else if (tagName == QLatin1String("end-offset")) { a->setEndOffset(e.text().toInt()*60); } else if (tagName == QLatin1String("repeat-count")) { a->setRepeatCount(e.text().toInt()); } else if (tagName == QLatin1String("repeat-interval")) { a->setSnoozeTime(e.text().toInt()); } else if (tagName == QLatin1String("text")) { a->setText(e.text()); } else if (tagName == QLatin1String("program")) { a->setProgramFile(e.text()); } else if (tagName == QLatin1String("arguments")) { a->setProgramArguments(e.text()); } else if (tagName == QLatin1String("addresses")) { loadAddressesHelper(e, a); } else if (tagName == QLatin1String("subject")) { a->setMailSubject(e.text()); } else if (tagName == QLatin1String("mail-text")) { a->setMailText(e.text()); } else if (tagName == QLatin1String("attachments")) { loadAttachmentsHelper(e, a); } else if (tagName == QLatin1String("file")) { a->setAudioFile(e.text()); } else if (tagName == QLatin1String("enabled")) { a->setEnabled(e.text().toInt() != 0); } else { qCWarning(PIMKOLAB_LOG) << "Unhandled tag" << tagName; } } } } void Incidence::loadAlarms(const QDomElement &element) { for (QDomNode n = element.firstChild(); !n.isNull(); n = n.nextSibling()) { if (n.isComment()) { continue; } if (n.isElement()) { QDomElement e = n.toElement(); QString tagName = e.tagName(); if (tagName == QLatin1String("alarm")) { KCalCore::Alarm::Ptr a = KCalCore::Alarm::Ptr(new KCalCore::Alarm(nullptr)); a->setEnabled(true); // default to enabled, unless some XML attribute says otherwise. QString type = e.attribute(QStringLiteral("type")); if (type == QLatin1String("display")) { a->setType(KCalCore::Alarm::Display); } else if (type == QLatin1String("procedure")) { a->setType(KCalCore::Alarm::Procedure); } else if (type == QLatin1String("email")) { a->setType(KCalCore::Alarm::Email); } else if (type == QLatin1String("audio")) { a->setType(KCalCore::Alarm::Audio); } else { qCWarning(PIMKOLAB_LOG) << "Unhandled alarm type:" << type; } loadAlarmHelper(e, a); mAlarms << a; } else { qCWarning(PIMKOLAB_LOG) << "Unhandled tag" << tagName; } } } } bool Incidence::loadAttribute(QDomElement &element) { QString tagName = element.tagName(); if (tagName == QLatin1String("priority")) { bool ok; int p = element.text().toInt(&ok); if (!ok || p < 1 || p > 9) { qCWarning(PIMKOLAB_LOG) << "Invalid \"priority\" value:" << element.text(); } else { setPriority(p); } } else if (tagName == QLatin1String("x-kcal-priority")) { //for backwards compat bool ok; int p = element.text().toInt(&ok); if (!ok || p < 0 || p > 9) { qCWarning(PIMKOLAB_LOG) << "Invalid \"x-kcal-priority\" value:" << element.text(); } else { if (priority() == 0) { setPriority(p); } } } else if (tagName == QLatin1String("summary")) { setSummary(element.text()); } else if (tagName == QLatin1String("location")) { setLocation(element.text()); } else if (tagName == QLatin1String("organizer")) { Email email; if (loadEmailAttribute(element, email)) { setOrganizer(email); return true; } else { return false; } } else if (tagName == QLatin1String("start-date")) { setStartDate(element.text()); } else if (tagName == QLatin1String("recurrence")) { loadRecurrence(element); } else if (tagName == QLatin1String("attendee")) { Attendee attendee; if (loadAttendeeAttribute(element, attendee)) { addAttendee(attendee); return true; } else { return false; } } else if (tagName == QLatin1String("link-attachment")) { mAttachments.push_back(KCalCore::Attachment::Ptr(new KCalCore::Attachment(element.text()))); } else if (tagName == QLatin1String("alarm")) { // Alarms should be minutes before. Libkcal uses event time + alarm time setAlarm(-element.text().toInt()); } else if (tagName == QLatin1String("advanced-alarms")) { loadAlarms(element); } else if (tagName == QLatin1String("x-kde-internaluid")) { setInternalUID(element.text()); } else if (tagName == QLatin1String("x-custom")) { loadCustomAttributes(element); } else if (tagName == QLatin1String("inline-attachment")) { // we handle that separately later on, so no need to create a KolabUnhandled entry for it } else { bool ok = KolabBase::loadAttribute(element); if (!ok) { // Unhandled tag - save for later storage qCDebug(PIMKOLAB_LOG) <<"Saving unhandled tag" << element.tagName(); Custom c; c.key = QByteArray("X-KDE-KolabUnhandled-") + element.tagName().toLatin1(); c.value = element.text(); mCustomList.append(c); } } // We handled this return true; } bool Incidence::saveAttributes(QDomElement &element) const { // Save the base class elements KolabBase::saveAttributes(element); if (priority() != 0) { writeString(element, QStringLiteral("priority"), QString::number(priority())); } if (mFloatingStatus == HasTime) { writeString(element, QStringLiteral("start-date"), dateTimeToString(startDate())); } else { writeString(element, QStringLiteral("start-date"), dateToString(startDate().date())); } writeString(element, QStringLiteral("summary"), summary()); writeString(element, QStringLiteral("location"), location()); saveEmailAttribute(element, organizer(), QStringLiteral("organizer")); if (!mRecurrence.cycle.isEmpty()) { saveRecurrence(element); } saveAttendees(element); saveAttachments(element); if (mHasAlarm) { // Alarms should be minutes before. Libkcal uses event time + alarm time int alarmTime = qRound(-alarm()); writeString(element, QStringLiteral("alarm"), QString::number(alarmTime)); } saveAlarms(element); writeString(element, QStringLiteral("x-kde-internaluid"), internalUID()); saveCustomAttributes(element); return true; } void Incidence::saveCustomAttributes(QDomElement &element) const { foreach (const Custom &custom, mCustomList) { QString key(custom.key); Q_ASSERT(!key.isEmpty()); if (key.startsWith(QLatin1String("X-KDE-KolabUnhandled-"))) { key = key.mid(strlen("X-KDE-KolabUnhandled-")); writeString(element, key, custom.value); } else { // Let's use attributes so that other tag-preserving-code doesn't need sub-elements QDomElement e = element.ownerDocument().createElement(QStringLiteral("x-custom")); element.appendChild(e); e.setAttribute(QStringLiteral("key"), key); e.setAttribute(QStringLiteral("value"), custom.value); } } } void Incidence::loadCustomAttributes(QDomElement &element) { Custom custom; custom.key = element.attribute(QStringLiteral("key")).toLatin1(); custom.value = element.attribute(QStringLiteral("value")); mCustomList.append(custom); } static KCalCore::Attendee::PartStat attendeeStringToStatus(const QString &s) { if (s == QLatin1String("none")) { return KCalCore::Attendee::NeedsAction; } if (s == QLatin1String("tentative")) { return KCalCore::Attendee::Tentative; } if (s == QLatin1String("declined")) { return KCalCore::Attendee::Declined; } if (s == QLatin1String("delegated")) { return KCalCore::Attendee::Delegated; } // Default: return KCalCore::Attendee::Accepted; } static QString attendeeStatusToString(KCalCore::Attendee::PartStat status) { switch (status) { case KCalCore::Attendee::NeedsAction: return QStringLiteral("none"); case KCalCore::Attendee::Accepted: return QStringLiteral("accepted"); case KCalCore::Attendee::Declined: return QStringLiteral("declined"); case KCalCore::Attendee::Tentative: return QStringLiteral("tentative"); case KCalCore::Attendee::Delegated: return QStringLiteral("delegated"); case KCalCore::Attendee::Completed: case KCalCore::Attendee::InProcess: // These don't have any meaning in the Kolab format, so just use: return QStringLiteral("accepted"); default: // Default for the case that there are more added later: return QStringLiteral("accepted"); } } static KCalCore::Attendee::Role attendeeStringToRole(const QString &s) { if (s == QLatin1String("optional")) { return KCalCore::Attendee::OptParticipant; } if (s == QLatin1String("resource")) { return KCalCore::Attendee::NonParticipant; } return KCalCore::Attendee::ReqParticipant; } static QString attendeeRoleToString(KCalCore::Attendee::Role role) { switch (role) { case KCalCore::Attendee::ReqParticipant: return QStringLiteral("required"); case KCalCore::Attendee::OptParticipant: return QStringLiteral("optional"); case KCalCore::Attendee::Chair: // We don't have the notion of chair, so use return QStringLiteral("required"); case KCalCore::Attendee::NonParticipant: // In Kolab, a non-participant is a resource return QStringLiteral("resource"); } // Default for the case that there are more added later: return QStringLiteral("required"); } static const char *s_weekDayName[] = { "monday", "tuesday", "wednesday", "thursday", "friday", "saturday", "sunday" }; static const char *s_monthName[] = { "january", "february", "march", "april", "may", "june", "july", "august", "september", "october", "november", "december" }; void Incidence::setRecurrence(KCalCore::Recurrence *recur) { mRecurrence.interval = recur->frequency(); switch (recur->recurrenceType()) { case KCalCore::Recurrence::rMinutely: // Not handled by the kolab XML mRecurrence.cycle = QStringLiteral("minutely"); break; case KCalCore::Recurrence::rHourly: // Not handled by the kolab XML mRecurrence.cycle = QStringLiteral("hourly"); break; case KCalCore::Recurrence::rDaily: mRecurrence.cycle = QStringLiteral("daily"); break; case KCalCore::Recurrence::rWeekly: // every X weeks mRecurrence.cycle = QStringLiteral("weekly"); { QBitArray arr = recur->days(); for (int idx = 0; idx < 7; ++idx) { if (arr.testBit(idx)) { mRecurrence.days.append(s_weekDayName[idx]); } } } break; case KCalCore::Recurrence::rMonthlyPos: { mRecurrence.cycle = QStringLiteral("monthly"); mRecurrence.type = QStringLiteral("weekday"); QList monthPositions = recur->monthPositions(); if (!monthPositions.isEmpty()) { KCalCore::RecurrenceRule::WDayPos monthPos = monthPositions.first(); // TODO: Handle multiple days in the same week mRecurrence.dayNumber = QString::number(monthPos.pos()); mRecurrence.days.append(s_weekDayName[ monthPos.day()-1 ]); // Not (properly) handled(?): monthPos.negative (nth days before end of month) } break; } case KCalCore::Recurrence::rMonthlyDay: { mRecurrence.cycle = QStringLiteral("monthly"); mRecurrence.type = QStringLiteral("daynumber"); QList monthDays = recur->monthDays(); // ####### Kolab XML limitation: only the first month day is used if (!monthDays.isEmpty()) { mRecurrence.dayNumber = QString::number(monthDays.first()); } break; } case KCalCore::Recurrence::rYearlyMonth: // (day n of Month Y) { mRecurrence.cycle = QStringLiteral("yearly"); mRecurrence.type = QStringLiteral("monthday"); QList rmd = recur->yearDates(); int day = !rmd.isEmpty() ? rmd.first() : recur->startDate().day(); mRecurrence.dayNumber = QString::number(day); QList months = recur->yearMonths(); if (!months.isEmpty()) { mRecurrence.month = s_monthName[ months.first() - 1 ]; // #### Kolab XML limitation: only one month specified } break; } case KCalCore::Recurrence::rYearlyDay: // YearlyDay (day N of the year). Not supported by Outlook mRecurrence.cycle = QStringLiteral("yearly"); mRecurrence.type = QStringLiteral("yearday"); mRecurrence.dayNumber = QString::number(recur->yearDays().first()); break; case KCalCore::Recurrence::rYearlyPos: // (weekday X of week N of month Y) mRecurrence.cycle = QStringLiteral("yearly"); mRecurrence.type = QStringLiteral("weekday"); QList months = recur->yearMonths(); if (!months.isEmpty()) { mRecurrence.month = s_monthName[ months.first() - 1 ]; // #### Kolab XML limitation: only one month specified } QList monthPositions = recur->yearPositions(); if (!monthPositions.isEmpty()) { KCalCore::RecurrenceRule::WDayPos monthPos = monthPositions.first(); // TODO: Handle multiple days in the same week mRecurrence.dayNumber = QString::number(monthPos.pos()); mRecurrence.days.append(s_weekDayName[ monthPos.day()-1 ]); //mRecurrence.dayNumber = QString::number( *recur->yearNums().getFirst() ); // Not handled: monthPos.negative (nth days before end of month) } break; } int howMany = recur->duration(); if (howMany > 0) { mRecurrence.rangeType = QStringLiteral("number"); mRecurrence.range = QString::number(howMany); } else if (howMany == 0) { mRecurrence.rangeType = QStringLiteral("date"); mRecurrence.range = dateToString(recur->endDate()); } else { mRecurrence.rangeType = QStringLiteral("none"); } } void Incidence::setFields(const KCalCore::Incidence::Ptr &incidence) { KolabBase::setFields(incidence); setPriority(incidence->priority()); if (incidence->allDay()) { // This is a all-day event. Don't timezone move this one mFloatingStatus = AllDay; setStartDate(incidence->dtStart().date()); } else { mFloatingStatus = HasTime; setStartDate(localToUTC(Porting::q2k(incidence->dtStart()))); } setSummary(incidence->summary()); setLocation(incidence->location()); // Alarm mHasAlarm = false; // Will be set to true, if we actually have one if (incidence->hasEnabledAlarms()) { const KCalCore::Alarm::List &alarms = incidence->alarms(); if (!alarms.isEmpty()) { const KCalCore::Alarm::Ptr alarm = alarms.first(); if (alarm->hasStartOffset()) { int dur = alarm->startOffset().asSeconds(); setAlarm((float)dur / 60.0); } } } - if (incidence->organizer()) { - Email org(incidence->organizer()->name(), incidence->organizer()->email()); + if (!incidence->organizer().isEmpty()) { + Email org(incidence->organizer().name(), incidence->organizer().email()); setOrganizer(org); } // Attendees: const KCalCore::Attendee::List attendees = incidence->attendees(); foreach (const KCalCore::Attendee::Ptr &kcalAttendee, attendees) { Attendee attendee; attendee.displayName = kcalAttendee->name(); attendee.smtpAddress = kcalAttendee->email(); attendee.status = attendeeStatusToString(kcalAttendee->status()); attendee.requestResponse = kcalAttendee->RSVP(); // TODO: KCalCore::Attendee::mFlag is not accessible // attendee.invitationSent = kcalAttendee->mFlag; // DF: Hmm? mFlag is set to true and never used at all.... Did you mean another field? attendee.role = attendeeRoleToString(kcalAttendee->role()); attendee.delegate = kcalAttendee->delegate(); attendee.delegator = kcalAttendee->delegator(); addAttendee(attendee); } mAttachments.clear(); // Attachments const KCalCore::Attachment::List attachments = incidence->attachments(); mAttachments.reserve(attachments.size()); for (const KCalCore::Attachment::Ptr &a : attachments) { mAttachments.push_back(a); } mAlarms.clear(); // Alarms const KCalCore::Alarm::List alarms = incidence->alarms(); mAlarms.reserve(alarms.count()); for (const KCalCore::Alarm::Ptr &a : alarms) { mAlarms.push_back(a); } if (incidence->recurs()) { setRecurrence(incidence->recurrence()); mRecurrence.exclusions = incidence->recurrence()->exDates(); } // Handle the scheduling ID if (incidence->schedulingID() == incidence->uid()) { // There is no scheduling ID setInternalUID(QString()); //krazy:exclude=nullstrassign for old broken gcc } else { // We've internally been using a different uid, so save that as the // temporary (internal) uid and restore the original uid, the one that // is used in the folder and the outside world setUid(incidence->schedulingID()); setInternalUID(incidence->uid()); } // Unhandled tags and other custom properties (see libkcal/customproperties.h) const QMap map = incidence->customProperties(); QMap::ConstIterator cit = map.begin(); QMap::ConstIterator cend = map.end(); for (; cit != cend; ++cit) { Custom c; c.key = cit.key(); c.value = cit.value(); mCustomList.append(c); } } static QBitArray daysListToBitArray(const QStringList &days) { QBitArray arr(7); arr.fill(false); foreach (const QString &day, days) { for (int i = 0; i < 7; ++i) { if (day == s_weekDayName[i]) { arr.setBit(i, true); } } } return arr; } void Incidence::saveTo(const KCalCore::Incidence::Ptr &incidence) { KolabBase::saveTo(incidence); incidence->setPriority(priority()); if (mFloatingStatus == AllDay) { // This is an all-day event. Don't timezone move this one incidence->setDtStart(Porting::k2q(startDate())); incidence->setAllDay(true); } else { incidence->setDtStart(Porting::k2q(utcToLocal(startDate()))); incidence->setAllDay(false); } incidence->setSummary(summary()); incidence->setLocation(location()); if (mHasAlarm && mAlarms.isEmpty()) { KCalCore::Alarm::Ptr alarm = incidence->newAlarm(); alarm->setStartOffset(qRound(mAlarm * 60.0)); alarm->setEnabled(true); alarm->setType(KCalCore::Alarm::Display); } else if (!mAlarms.isEmpty()) { foreach (KCalCore::Alarm::Ptr a, mAlarms) { a->setParent(incidence.data()); incidence->addAlarm(a); } } if (organizer().displayName.isEmpty()) { incidence->setOrganizer(organizer().smtpAddress); } else { incidence->setOrganizer(organizer().displayName + QLatin1Char('<') + organizer().smtpAddress + QLatin1Char('>')); } incidence->clearAttendees(); foreach (const Attendee &attendee, mAttendees) { KCalCore::Attendee::PartStat status = attendeeStringToStatus(attendee.status); KCalCore::Attendee::Role role = attendeeStringToRole(attendee.role); KCalCore::Attendee::Ptr a(new KCalCore::Attendee(attendee.displayName, attendee.smtpAddress, attendee.requestResponse, status, role)); a->setDelegate(attendee.delegate); a->setDelegator(attendee.delegator); incidence->addAttendee(a); } incidence->clearAttachments(); foreach (const KCalCore::Attachment::Ptr &a, mAttachments) { // TODO should we copy? incidence->addAttachment(a); } if (!mRecurrence.cycle.isEmpty()) { KCalCore::Recurrence *recur = incidence->recurrence(); // yeah, this creates it // done below recur->setFrequency( mRecurrence.interval ); if (mRecurrence.cycle == QLatin1String("minutely")) { recur->setMinutely(mRecurrence.interval); } else if (mRecurrence.cycle == QLatin1String("hourly")) { recur->setHourly(mRecurrence.interval); } else if (mRecurrence.cycle == QLatin1String("daily")) { recur->setDaily(mRecurrence.interval); } else if (mRecurrence.cycle == QLatin1String("weekly")) { QBitArray rDays = daysListToBitArray(mRecurrence.days); recur->setWeekly(mRecurrence.interval, rDays); } else if (mRecurrence.cycle == QLatin1String("monthly")) { recur->setMonthly(mRecurrence.interval); if (mRecurrence.type == QLatin1String("weekday")) { recur->addMonthlyPos(mRecurrence.dayNumber.toInt(), daysListToBitArray(mRecurrence.days)); } else if (mRecurrence.type == QLatin1String("daynumber")) { recur->addMonthlyDate(mRecurrence.dayNumber.toInt()); } else { qCWarning(PIMKOLAB_LOG) <<"Unhandled monthly recurrence type" << mRecurrence.type; } } else if (mRecurrence.cycle == QLatin1String("yearly")) { recur->setYearly(mRecurrence.interval); if (mRecurrence.type == QLatin1String("monthday")) { recur->addYearlyDate(mRecurrence.dayNumber.toInt()); for (int i = 0; i < 12; ++i) { if (s_monthName[ i ] == mRecurrence.month) { recur->addYearlyMonth(i+1); } } } else if (mRecurrence.type == QLatin1String("yearday")) { recur->addYearlyDay(mRecurrence.dayNumber.toInt()); } else if (mRecurrence.type == QLatin1String("weekday")) { for (int i = 0; i < 12; ++i) { if (s_monthName[ i ] == mRecurrence.month) { recur->addYearlyMonth(i+1); } } recur->addYearlyPos(mRecurrence.dayNumber.toInt(), daysListToBitArray(mRecurrence.days)); } else { qCWarning(PIMKOLAB_LOG) <<"Unhandled yearly recurrence type" << mRecurrence.type; } } else { qCWarning(PIMKOLAB_LOG) <<"Unhandled recurrence cycle" << mRecurrence.cycle; } if (mRecurrence.rangeType == QLatin1String("number")) { recur->setDuration(mRecurrence.range.toInt()); } else if (mRecurrence.rangeType == QLatin1String("date")) { recur->setEndDate(stringToDate(mRecurrence.range)); } // "none" is default since tje set*ly methods set infinite recurrence incidence->recurrence()->setExDates(mRecurrence.exclusions); } /* If we've stored a uid to be used internally instead of the real one * (to deal with duplicates of events in different folders) before, then * restore it, so it does not change. Keep the original uid around for * scheduling purposes. */ if (!internalUID().isEmpty()) { incidence->setUid(internalUID()); incidence->setSchedulingID(uid()); } foreach (const Custom &custom, mCustomList) { incidence->setNonKDECustomProperty(custom.key, custom.value); } } QString Incidence::productID() const { return QStringLiteral("%1, Kolab resource").arg(LIBKOLAB_LIB_VERSION_STRING); } // Unhandled KCalCore::Incidence fields: // revision, status (unused), attendee.uid, // mComments, mReadOnly diff --git a/resources/openxchange/oxa/incidenceutils.cpp b/resources/openxchange/oxa/incidenceutils.cpp index 3dda45316..3c71ca7b1 100644 --- a/resources/openxchange/oxa/incidenceutils.cpp +++ b/resources/openxchange/oxa/incidenceutils.cpp @@ -1,592 +1,592 @@ /* This file is part of oxaccess. Copyright (c) 2009 Tobias Koenig This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "incidenceutils.h" #include "davmanager.h" #include "davutils.h" #include "oxutils.h" #include "users.h" #include #include #include #include #include using namespace OXA; static void parseMembersAttribute(const QDomElement &element, const KCalCore::Incidence::Ptr &incidence) { incidence->clearAttendees(); for (QDomElement child = element.firstChildElement(); !child.isNull(); child = child.nextSiblingElement()) { if (child.tagName() == QLatin1String("user")) { const QString uid = child.text(); const User user = Users::self()->lookupUid(uid.toLongLong()); QString name; QString email; KCalCore::Attendee::Ptr attendee = incidence->attendeeByUid(uid); if (!user.isValid()) { if (attendee) { continue; } name = uid; email = uid + QLatin1Char('@') + DavManager::self()->baseUrl().host(); } else { name = user.name(); email = user.email(); } if (attendee) { attendee->setName(name); attendee->setEmail(email); } else { attendee = KCalCore::Attendee::Ptr(new KCalCore::Attendee(name, email)); attendee->setUid(uid); incidence->addAttendee(attendee); } const QString status = child.attribute(QStringLiteral("confirm")); if (!status.isEmpty()) { if (status == QLatin1String("accept")) { attendee->setStatus(KCalCore::Attendee::Accepted); } else if (status == QLatin1String("decline")) { attendee->setStatus(KCalCore::Attendee::Declined); } else { attendee->setStatus(KCalCore::Attendee::NeedsAction); } } } } } static void parseIncidenceAttribute(const QDomElement &element, const KCalCore::Incidence::Ptr &incidence) { const QString tagName = element.tagName(); const QString text = OXUtils::readString(element.text()); if (tagName == QLatin1String("title")) { incidence->setSummary(text); } else if (tagName == QLatin1String("note")) { incidence->setDescription(text); } else if (tagName == QLatin1String("alarm")) { const int minutes = OXUtils::readNumber(element.text()); if (minutes != 0) { KCalCore::Alarm::List alarms = incidence->alarms(); KCalCore::Alarm::Ptr alarm; if (alarms.isEmpty()) { alarm = incidence->newAlarm(); } else { alarm = alarms.first(); } if (alarm->type() == KCalCore::Alarm::Invalid) { alarm->setType(KCalCore::Alarm::Display); } KCalCore::Duration duration(minutes * -60); alarm->setStartOffset(duration); alarm->setEnabled(true); } else { // 0 reminder -> disable alarm incidence->clearAlarms(); } } else if (tagName == QLatin1String("created_by")) { const User user = Users::self()->lookupUid(OXUtils::readNumber(element.text())); - incidence->setOrganizer(KCalCore::Person::Ptr(new KCalCore::Person(user.name(), user.email()))); + incidence->setOrganizer(KCalCore::Person(user.name(), user.email())); } else if (tagName == QLatin1String("participants")) { parseMembersAttribute(element, incidence); } else if (tagName == QLatin1String("private_flag")) { if (OXUtils::readBoolean(element.text()) == true) { incidence->setSecrecy(KCalCore::Incidence::SecrecyPrivate); } else { incidence->setSecrecy(KCalCore::Incidence::SecrecyPublic); } } else if (tagName == QLatin1String("categories")) { incidence->setCategories(text.split(QRegExp(QLatin1String(",\\s*")))); } } static void parseEventAttribute(const QDomElement &element, const KCalCore::Event::Ptr &event) { const QString tagName = element.tagName(); const QString text = OXUtils::readString(element.text()); if (tagName == QLatin1String("start_date")) { QDateTime dateTime = OXUtils::readDateTime(element.text()); event->setDtStart(dateTime); } else if (tagName == QLatin1String("end_date")) { QDateTime dateTime = OXUtils::readDateTime(element.text()); if (event->allDay()) { dateTime = dateTime.addSecs(-1); } event->setDtEnd(dateTime); } else if (tagName == QLatin1String("location")) { event->setLocation(text); } } static void parseTodoAttribute(const QDomElement &element, const KCalCore::Todo::Ptr &todo) { const QString tagName = element.tagName(); const QString text = OXUtils::readString(element.text()); if (tagName == QLatin1String("start_date")) { const QDateTime dateTime = OXUtils::readDateTime(element.text()); if (dateTime.isValid()) { todo->setDtStart(dateTime); } } else if (tagName == QLatin1String("end_date")) { const QDateTime dateTime = OXUtils::readDateTime(element.text()); if (dateTime.isValid()) { todo->setDtDue(dateTime); } } else if (tagName == QLatin1String("priority")) { const int priorityNumber = OXUtils::readNumber(element.text()); if (priorityNumber < 1 || priorityNumber > 3) { qDebug() << "Unknown priority:" << text; } else { int priority = 0; switch (priorityNumber) { case 1: priority = 9; break; case 2: priority = 5; break; case 3: priority = 1; break; } todo->setPriority(priority); } } else if (tagName == QLatin1String("percent_completed")) { todo->setPercentComplete(OXUtils::readNumber(element.text())); } } static void parseRecurrence(const QDomElement &element, const KCalCore::Incidence::Ptr &incidence) { QString type; int dailyValue = -1; QDateTime endDate; int weeklyValue = -1; QBitArray days(7); // days, starting with monday bool daysSet = false; int monthlyValueDay = -1; int monthlyValueMonth = -1; int yearlyValueDay = -1; int yearlyMonth = -1; int monthly2Recurrency = 0; int monthly2ValueMonth = -1; int yearly2Recurrency = 0; int yearly2Day = 0; int yearly2Month = -1; KCalCore::DateList deleteExceptions; for (QDomElement child = element.firstChildElement(); !child.isNull(); child = child.nextSiblingElement()) { const QString tagName = child.tagName(); const QString text = OXUtils::readString(child.text()); if (tagName == QLatin1String("recurrence_type")) { type = text; } else if (tagName == QLatin1String("interval")) { dailyValue = text.toInt(); weeklyValue = text.toInt(); monthlyValueMonth = text.toInt(); monthly2ValueMonth = text.toInt(); } else if (tagName == QLatin1String("days")) { int tmp = text.toInt(); // OX encodes days binary: 1=Su, 2=Mo, 4=Tu, ... for (int i = 0; i < 7; ++i) { if (tmp & (1 << i)) { days.setBit((i + 6) % 7); } } daysSet = true; } else if (tagName == QLatin1String("day_in_month")) { monthlyValueDay = text.toInt(); monthly2Recurrency = text.toInt(); yearlyValueDay = text.toInt(); yearly2Recurrency = text.toInt(); } else if (tagName == QLatin1String("month")) { yearlyMonth = text.toInt() + 1; // starts at 0 yearly2Month = text.toInt() + 1; } else if ((tagName == QLatin1String("deleteexceptions")) || (tagName == QLatin1String("changeexceptions"))) { const QStringList exceptionDates = text.split(QLatin1Char(',')); deleteExceptions.reserve(exceptionDates.count()); for (const QString &date : exceptionDates) { deleteExceptions.append(OXUtils::readDate(date)); } } else if (tagName == QLatin1String("until")) { endDate = OXUtils::readDateTime(child.text()); } //TODO: notification } if (daysSet && type == QLatin1String("monthly")) { type = QStringLiteral("monthly2"); // HACK: OX doesn't cleanly distinguish between monthly and monthly2 } if (daysSet && type == QLatin1String("yearly")) { type = QStringLiteral("yearly2"); } KCalCore::Recurrence *recurrence = incidence->recurrence(); if (type == QLatin1String("daily")) { recurrence->setDaily(dailyValue); } else if (type == QLatin1String("weekly")) { recurrence->setWeekly(weeklyValue, days); } else if (type == QLatin1String("monthly")) { recurrence->setMonthly(monthlyValueMonth); recurrence->addMonthlyDate(monthlyValueDay); } else if (type == QLatin1String("yearly")) { recurrence->setYearly(1); recurrence->addYearlyDate(yearlyValueDay); recurrence->addYearlyMonth(yearlyMonth); } else if (type == QLatin1String("monthly2")) { recurrence->setMonthly(monthly2ValueMonth); QBitArray _days(7); if (daysSet) { _days = days; } else { _days.setBit(incidence->dtStart().date().dayOfWeek()); } recurrence->addMonthlyPos(monthly2Recurrency, _days); } else if (type == QLatin1String("yearly2")) { recurrence->setYearly(1); recurrence->addYearlyMonth(yearly2Month); QBitArray _days(7); if (daysSet) { _days = days; } else { _days.setBit((yearly2Day + 5) % 7); } recurrence->addYearlyPos(yearly2Recurrency, _days); } if (endDate.isValid()) { recurrence->setEndDate(endDate.date()); } recurrence->setExDates(deleteExceptions); } static void createIncidenceAttributes(QDomDocument &document, QDomElement &parent, const KCalCore::Incidence::Ptr &incidence) { DAVUtils::addOxElement(document, parent, QStringLiteral("title"), OXUtils::writeString(incidence->summary())); DAVUtils::addOxElement(document, parent, QStringLiteral("note"), OXUtils::writeString(incidence->description())); if (incidence->attendeeCount() > 0) { QDomElement members = DAVUtils::addOxElement(document, parent, QStringLiteral("participants")); const KCalCore::Attendee::List attendees = incidence->attendees(); for (const KCalCore::Attendee::Ptr &attendee : attendees) { const User user = Users::self()->lookupEmail(attendee->email()); if (!user.isValid()) { continue; } QString status; switch (attendee->status()) { case KCalCore::Attendee::Accepted: status = QStringLiteral("accept"); break; case KCalCore::Attendee::Declined: status = QStringLiteral("decline"); break; default: status = QStringLiteral("none"); break; } QDomElement element = DAVUtils::addOxElement(document, members, QStringLiteral("user"), OXUtils::writeNumber(user.uid())); DAVUtils::setOxAttribute(element, QStringLiteral("confirm"), status); } } if (incidence->secrecy() == KCalCore::Incidence::SecrecyPublic) { DAVUtils::addOxElement(document, parent, QStringLiteral("private_flag"), OXUtils::writeBoolean(false)); } else { DAVUtils::addOxElement(document, parent, QStringLiteral("private_flag"), OXUtils::writeBoolean(true)); } // set reminder as the number of minutes to the start of the event const KCalCore::Alarm::List alarms = incidence->alarms(); if (!alarms.isEmpty() && alarms.first()->hasStartOffset() && alarms.first()->enabled()) { DAVUtils::addOxElement(document, parent, QStringLiteral("alarm_flag"), OXUtils::writeBoolean(true)); DAVUtils::addOxElement(document, parent, QStringLiteral("alarm"), OXUtils::writeNumber((-1) * alarms.first()->startOffset().asSeconds() / 60)); } else { DAVUtils::addOxElement(document, parent, QStringLiteral("alarm_flag"), OXUtils::writeBoolean(false)); DAVUtils::addOxElement(document, parent, QStringLiteral("alarm"), QStringLiteral("0")); } // categories DAVUtils::addOxElement(document, parent, QStringLiteral("categories"), OXUtils::writeString(incidence->categories().join(QStringLiteral(", ")))); } static void createEventAttributes(QDomDocument &document, QDomElement &parent, const KCalCore::Event::Ptr &event) { if (event->allDay()) { DAVUtils::addOxElement(document, parent, QStringLiteral("start_date"), OXUtils::writeDate(event->dtStart().date())); if (event->hasEndDate()) { DAVUtils::addOxElement(document, parent, QStringLiteral("end_date"), OXUtils::writeDate(event->dtEnd().date())); } } else { DAVUtils::addOxElement(document, parent, QStringLiteral("start_date"), OXUtils::writeDateTime(event->dtStart())); if (event->hasEndDate()) { DAVUtils::addOxElement(document, parent, QStringLiteral("end_date"), OXUtils::writeDateTime(event->dtEnd())); } } if (!event->hasEndDate()) { DAVUtils::addOxElement(document, parent, QStringLiteral("end_date")); } DAVUtils::addOxElement(document, parent, QStringLiteral("location"), OXUtils::writeString(event->location())); DAVUtils::addOxElement(document, parent, QStringLiteral("full_time"), OXUtils::writeBoolean(event->allDay())); if (event->transparency() == KCalCore::Event::Transparent) { DAVUtils::addOxElement(document, parent, QStringLiteral("shown_as"), OXUtils::writeNumber(4)); } else if (event->transparency() == KCalCore::Event::Opaque) { DAVUtils::addOxElement(document, parent, QStringLiteral("shown_as"), OXUtils::writeNumber(1)); } } static void createTaskAttributes(QDomDocument &document, QDomElement &parent, const KCalCore::Todo::Ptr &todo) { if (todo->hasStartDate()) { DAVUtils::addOxElement(document, parent, QStringLiteral("start_date"), OXUtils::writeDateTime(todo->dtStart())); } else { DAVUtils::addOxElement(document, parent, QStringLiteral("start_date")); } if (todo->hasDueDate()) { DAVUtils::addOxElement(document, parent, QStringLiteral("end_date"), OXUtils::writeDateTime(todo->dtDue())); } else { DAVUtils::addOxElement(document, parent, QStringLiteral("end_date")); } QString priority; switch (todo->priority()) { case 9: case 8: priority = QStringLiteral("1"); break; case 2: case 1: priority = QStringLiteral("3"); break; default: priority = QStringLiteral("2"); break; } DAVUtils::addOxElement(document, parent, QStringLiteral("priority"), priority); DAVUtils::addOxElement(document, parent, QStringLiteral("percent_completed"), OXUtils::writeNumber(todo->percentComplete())); } static void createRecurrenceAttributes(QDomDocument &document, QDomElement &parent, const KCalCore::Incidence::Ptr &incidence) { if (!incidence->recurs()) { DAVUtils::addOxElement(document, parent, QStringLiteral("recurrence_type"), QStringLiteral("none")); return; } const KCalCore::Recurrence *recurrence = incidence->recurrence(); int monthOffset = -1; switch (recurrence->recurrenceType()) { case KCalCore::Recurrence::rDaily: DAVUtils::addOxElement(document, parent, QStringLiteral("recurrence_type"), QStringLiteral("daily")); DAVUtils::addOxElement(document, parent, QStringLiteral("interval"), OXUtils::writeNumber(recurrence->frequency())); break; case KCalCore::Recurrence::rWeekly: { DAVUtils::addOxElement(document, parent, QStringLiteral("recurrence_type"), QStringLiteral("weekly")); DAVUtils::addOxElement(document, parent, QStringLiteral("interval"), OXUtils::writeNumber(recurrence->frequency())); int days = 0; for (int i = 0; i < 7; ++i) { if (recurrence->days()[i]) { days += 1 << ((i + 1) % 7); } } DAVUtils::addOxElement(document, parent, QStringLiteral("days"), OXUtils::writeNumber(days)); break; } case KCalCore::Recurrence::rMonthlyDay: DAVUtils::addOxElement(document, parent, QStringLiteral("recurrence_type"), QStringLiteral("monthly")); DAVUtils::addOxElement(document, parent, QStringLiteral("interval"), OXUtils::writeNumber(recurrence->frequency())); DAVUtils::addOxElement(document, parent, QStringLiteral("day_in_month"), OXUtils::writeNumber(recurrence->monthDays().first())); break; case KCalCore::Recurrence::rMonthlyPos: { const KCalCore::RecurrenceRule::WDayPos wdp = recurrence->monthPositions().constFirst(); DAVUtils::addOxElement(document, parent, QStringLiteral("recurrence_type"), QStringLiteral("monthly")); DAVUtils::addOxElement(document, parent, QStringLiteral("interval"), OXUtils::writeNumber(recurrence->frequency())); DAVUtils::addOxElement(document, parent, QStringLiteral("days"), OXUtils::writeNumber(1 << wdp.day())); DAVUtils::addOxElement(document, parent, QStringLiteral("day_in_month"), OXUtils::writeNumber(wdp.pos())); break; } case KCalCore::Recurrence::rYearlyMonth: DAVUtils::addOxElement(document, parent, QStringLiteral("recurrence_type"), QStringLiteral("yearly")); DAVUtils::addOxElement(document, parent, QStringLiteral("interval"), QStringLiteral("1")); DAVUtils::addOxElement(document, parent, QStringLiteral("day_in_month"), OXUtils::writeNumber(recurrence->yearDates().constFirst())); DAVUtils::addOxElement(document, parent, QStringLiteral("month"), OXUtils::writeNumber(recurrence->yearMonths().constFirst() + monthOffset)); break; case KCalCore::Recurrence::rYearlyPos: { const KCalCore::RecurrenceRule::WDayPos wdp = recurrence->monthPositions().constFirst(); DAVUtils::addOxElement(document, parent, QStringLiteral("recurrence_type"), QStringLiteral("yearly")); DAVUtils::addOxElement(document, parent, QStringLiteral("interval"), QStringLiteral("1")); DAVUtils::addOxElement(document, parent, QStringLiteral("days"), OXUtils::writeNumber(1 << wdp.day())); DAVUtils::addOxElement(document, parent, QStringLiteral("day_in_month"), OXUtils::writeNumber(wdp.pos())); DAVUtils::addOxElement(document, parent, QStringLiteral("month"), OXUtils::writeNumber(recurrence->yearMonths().constFirst() + monthOffset)); break; } default: qDebug() << "unsupported recurrence type:" << recurrence->recurrenceType(); } if (recurrence->endDateTime().isValid()) { DAVUtils::addOxElement(document, parent, QStringLiteral("until"), OXUtils::writeDateTime(recurrence->endDateTime())); } else { DAVUtils::addOxElement(document, parent, QStringLiteral("until")); } // delete exceptions const KCalCore::DateList exceptionList = recurrence->exDates(); QStringList dates; dates.reserve(exceptionList.count()); for (const QDate &date : exceptionList) { dates.append(OXUtils::writeDate(date)); } DAVUtils::addOxElement(document, parent, QStringLiteral("deleteexceptions"), dates.join(QLatin1Char(','))); //TODO: changeexceptions } void OXA::IncidenceUtils::parseEvent(const QDomElement &propElement, Object &object) { KCalCore::Event::Ptr event(new KCalCore::Event); const QDomElement fullTimeElement = propElement.firstChildElement(QStringLiteral("full_time")); if (!fullTimeElement.isNull()) { event->setAllDay(OXUtils::readBoolean(fullTimeElement.text())); } const QDomElement ShowAsElement = propElement.firstChildElement(QStringLiteral("shown_as")); if (!ShowAsElement.isNull()) { int showAs = OXUtils::readNumber(ShowAsElement.text()); switch (showAs) { case 1: event->setTransparency(KCalCore::Event::Transparent); break; case 4: event->setTransparency(KCalCore::Event::Opaque); break; default: event->setTransparency(KCalCore::Event::Opaque); } } bool doesRecur = false; const QDomElement recurrenceTypeElement = propElement.firstChildElement(QStringLiteral("recurrence_type")); if (!recurrenceTypeElement.isNull() && recurrenceTypeElement.text() != QLatin1String("none")) { doesRecur = true; } QDomElement element = propElement.firstChildElement(); while (!element.isNull()) { parseIncidenceAttribute(element, event); parseEventAttribute(element, event); element = element.nextSiblingElement(); } if (doesRecur) { parseRecurrence(propElement, event); } else { event->recurrence()->unsetRecurs(); } object.setEvent(KCalCore::Incidence::Ptr(event)); } void OXA::IncidenceUtils::parseTask(const QDomElement &propElement, Object &object) { KCalCore::Todo::Ptr todo(new KCalCore::Todo); todo->setSecrecy(KCalCore::Incidence::SecrecyPrivate); bool doesRecur = false; const QDomElement recurrenceTypeElement = propElement.firstChildElement(QStringLiteral("recurrence_type")); if (!recurrenceTypeElement.isNull() && recurrenceTypeElement.text() != QLatin1String("none")) { doesRecur = true; } QDomElement element = propElement.firstChildElement(); while (!element.isNull()) { parseIncidenceAttribute(element, todo); parseTodoAttribute(element, todo); element = element.nextSiblingElement(); } if (doesRecur) { parseRecurrence(propElement, todo); } else { todo->recurrence()->unsetRecurs(); } object.setTask(KCalCore::Incidence::Ptr(todo)); } void OXA::IncidenceUtils::addEventElements(QDomDocument &document, QDomElement &propElement, const Object &object) { createIncidenceAttributes(document, propElement, object.event()); createEventAttributes(document, propElement, object.event().staticCast()); createRecurrenceAttributes(document, propElement, object.event()); } void OXA::IncidenceUtils::addTaskElements(QDomDocument &document, QDomElement &propElement, const Object &object) { createIncidenceAttributes(document, propElement, object.task()); createTaskAttributes(document, propElement, object.task().staticCast()); createRecurrenceAttributes(document, propElement, object.task()); }