File indexing completed on 2024-11-24 04:44:12
0001 /* 0002 * SPDX-FileCopyrightText: 2011 Christian Mollekopf <mollekopf@kolabsys.com> 0003 * 0004 * SPDX-License-Identifier: LGPL-3.0-or-later 0005 */ 0006 0007 #include "kcalconversion.h" 0008 0009 #include <KCalendarCore/Recurrence> 0010 #include <QDate> 0011 #include <QList> 0012 #include <QUrl> 0013 #include <vector> 0014 0015 #include "commonconversion.h" 0016 #include "pimkolab_debug.h" 0017 namespace Kolab 0018 { 0019 namespace Conversion 0020 { 0021 // The uid of a contact which refers to the uuid of a contact in the addressbook 0022 #define CUSTOM_KOLAB_CONTACT_UUID "X_KOLAB_CONTACT_UUID" 0023 #define CUSTOM_KOLAB_CONTACT_CUTYPE "X_KOLAB_CONTACT_CUTYPE" 0024 #define CUSTOM_KOLAB_URL "X-KOLAB-URL" 0025 0026 KCalendarCore::Duration toDuration(const Kolab::Duration &d) 0027 { 0028 int value = 0; 0029 if (d.hours() || d.minutes() || d.seconds()) { 0030 value = ((((d.weeks() * 7 + d.days()) * 24 + d.hours()) * 60 + d.minutes()) * 60 + d.seconds()); 0031 if (d.isNegative()) { 0032 value = -value; 0033 } 0034 return {value}; 0035 } 0036 value = d.weeks() * 7 + d.days(); 0037 if (d.isNegative()) { 0038 value = -value; 0039 } 0040 return {value, KCalendarCore::Duration::Days}; 0041 } 0042 0043 Kolab::Duration fromDuration(const KCalendarCore::Duration &d) 0044 { 0045 int value = d.value(); 0046 bool isNegative = false; 0047 if (value < 0) { 0048 isNegative = true; 0049 value = -value; 0050 } 0051 // We don't know how the seconds/days were distributed before, so no point in distributing them (probably) 0052 if (d.isDaily()) { 0053 int days = value; 0054 return {days, 0, 0, 0, isNegative}; 0055 } 0056 int seconds = value; 0057 // int minutes = seconds / 60; 0058 // seconds = seconds % 60; 0059 // int hours = minutes / 60; 0060 // minutes = minutes % 60; 0061 return {0, 0, 0, seconds, isNegative}; 0062 } 0063 0064 KCalendarCore::Incidence::Secrecy toSecrecy(Kolab::Classification c) 0065 { 0066 switch (c) { 0067 case Kolab::ClassPublic: 0068 return KCalendarCore::Incidence::SecrecyPublic; 0069 case Kolab::ClassPrivate: 0070 return KCalendarCore::Incidence::SecrecyPrivate; 0071 case Kolab::ClassConfidential: 0072 return KCalendarCore::Incidence::SecrecyConfidential; 0073 default: 0074 qCCritical(PIMKOLAB_LOG) << "unhandled"; 0075 Q_ASSERT(0); 0076 } 0077 return KCalendarCore::Incidence::SecrecyPublic; 0078 } 0079 0080 Kolab::Classification fromSecrecy(KCalendarCore::Incidence::Secrecy c) 0081 { 0082 switch (c) { 0083 case KCalendarCore::Incidence::SecrecyPublic: 0084 return Kolab::ClassPublic; 0085 case KCalendarCore::Incidence::SecrecyPrivate: 0086 return Kolab::ClassPrivate; 0087 case KCalendarCore::Incidence::SecrecyConfidential: 0088 return Kolab::ClassConfidential; 0089 default: 0090 qCCritical(PIMKOLAB_LOG) << "unhandled"; 0091 Q_ASSERT(0); 0092 } 0093 return Kolab::ClassPublic; 0094 } 0095 0096 int toPriority(int priority) 0097 { 0098 // Same mapping 0099 return priority; 0100 } 0101 0102 int fromPriority(int priority) 0103 { 0104 // Same mapping 0105 return priority; 0106 } 0107 0108 KCalendarCore::Incidence::Status toStatus(Kolab::Status s) 0109 { 0110 switch (s) { 0111 case Kolab::StatusUndefined: 0112 return KCalendarCore::Incidence::StatusNone; 0113 case Kolab::StatusNeedsAction: 0114 return KCalendarCore::Incidence::StatusNeedsAction; 0115 case Kolab::StatusCompleted: 0116 return KCalendarCore::Incidence::StatusCompleted; 0117 case Kolab::StatusInProcess: 0118 return KCalendarCore::Incidence::StatusInProcess; 0119 case Kolab::StatusCancelled: 0120 return KCalendarCore::Incidence::StatusCanceled; 0121 case Kolab::StatusTentative: 0122 return KCalendarCore::Incidence::StatusTentative; 0123 case Kolab::StatusConfirmed: 0124 return KCalendarCore::Incidence::StatusConfirmed; 0125 case Kolab::StatusDraft: 0126 return KCalendarCore::Incidence::StatusDraft; 0127 case Kolab::StatusFinal: 0128 return KCalendarCore::Incidence::StatusFinal; 0129 default: 0130 qCCritical(PIMKOLAB_LOG) << "unhandled"; 0131 Q_ASSERT(0); 0132 } 0133 return KCalendarCore::Incidence::StatusNone; 0134 } 0135 0136 Kolab::Status fromStatus(KCalendarCore::Incidence::Status s) 0137 { 0138 switch (s) { 0139 case KCalendarCore::Incidence::StatusNone: 0140 return Kolab::StatusUndefined; 0141 case KCalendarCore::Incidence::StatusNeedsAction: 0142 return Kolab::StatusNeedsAction; 0143 case KCalendarCore::Incidence::StatusCompleted: 0144 return Kolab::StatusCompleted; 0145 case KCalendarCore::Incidence::StatusInProcess: 0146 return Kolab::StatusInProcess; 0147 case KCalendarCore::Incidence::StatusCanceled: 0148 return Kolab::StatusCancelled; 0149 case KCalendarCore::Incidence::StatusTentative: 0150 return Kolab::StatusTentative; 0151 case KCalendarCore::Incidence::StatusConfirmed: 0152 return Kolab::StatusConfirmed; 0153 case KCalendarCore::Incidence::StatusDraft: 0154 return Kolab::StatusDraft; 0155 case KCalendarCore::Incidence::StatusFinal: 0156 return Kolab::StatusFinal; 0157 default: 0158 qCCritical(PIMKOLAB_LOG) << "unhandled"; 0159 Q_ASSERT(0); 0160 } 0161 return Kolab::StatusUndefined; 0162 } 0163 0164 KCalendarCore::Attendee::PartStat toPartStat(Kolab::PartStatus p) 0165 { 0166 switch (p) { 0167 case Kolab::PartNeedsAction: 0168 return KCalendarCore::Attendee::NeedsAction; 0169 case Kolab::PartAccepted: 0170 return KCalendarCore::Attendee::Accepted; 0171 case Kolab::PartDeclined: 0172 return KCalendarCore::Attendee::Declined; 0173 case Kolab::PartTentative: 0174 return KCalendarCore::Attendee::Tentative; 0175 case Kolab::PartDelegated: 0176 return KCalendarCore::Attendee::Delegated; 0177 default: 0178 qCCritical(PIMKOLAB_LOG) << "unhandled"; 0179 Q_ASSERT(0); 0180 } 0181 return KCalendarCore::Attendee::NeedsAction; 0182 } 0183 0184 Kolab::PartStatus fromPartStat(KCalendarCore::Attendee::PartStat p) 0185 { 0186 switch (p) { 0187 case KCalendarCore::Attendee::NeedsAction: 0188 return Kolab::PartNeedsAction; 0189 case KCalendarCore::Attendee::Accepted: 0190 return Kolab::PartAccepted; 0191 case KCalendarCore::Attendee::Declined: 0192 return Kolab::PartDeclined; 0193 case KCalendarCore::Attendee::Tentative: 0194 return Kolab::PartTentative; 0195 case KCalendarCore::Attendee::Delegated: 0196 return Kolab::PartDelegated; 0197 default: 0198 qCCritical(PIMKOLAB_LOG) << "unhandled"; 0199 Q_ASSERT(0); 0200 } 0201 return Kolab::PartNeedsAction; 0202 } 0203 0204 KCalendarCore::Attendee::Role toRole(Kolab::Role r) 0205 { 0206 switch (r) { 0207 case Kolab::Required: 0208 return KCalendarCore::Attendee::ReqParticipant; 0209 case Kolab::Chair: 0210 return KCalendarCore::Attendee::Chair; 0211 case Kolab::Optional: 0212 return KCalendarCore::Attendee::OptParticipant; 0213 case Kolab::NonParticipant: 0214 return KCalendarCore::Attendee::NonParticipant; 0215 default: 0216 qCCritical(PIMKOLAB_LOG) << "unhandled"; 0217 Q_ASSERT(0); 0218 } 0219 return KCalendarCore::Attendee::ReqParticipant; 0220 } 0221 0222 Kolab::Role fromRole(KCalendarCore::Attendee::Role r) 0223 { 0224 switch (r) { 0225 case KCalendarCore::Attendee::ReqParticipant: 0226 return Kolab::Required; 0227 case KCalendarCore::Attendee::Chair: 0228 return Kolab::Chair; 0229 case KCalendarCore::Attendee::OptParticipant: 0230 return Kolab::Optional; 0231 case KCalendarCore::Attendee::NonParticipant: 0232 return Kolab::NonParticipant; 0233 default: 0234 qCCritical(PIMKOLAB_LOG) << "unhandled"; 0235 Q_ASSERT(0); 0236 } 0237 return Kolab::Required; 0238 } 0239 0240 template<typename T> 0241 QString getCustomProperty(const QString &id, const T &e) 0242 { 0243 const std::vector<Kolab::CustomProperty> &props = e.customProperties(); 0244 for (const Kolab::CustomProperty &p : props) { 0245 if (fromStdString(p.identifier) == id) { 0246 return fromStdString(p.value); 0247 } 0248 } 0249 } 0250 0251 template<typename T> 0252 void setIncidence(KCalendarCore::Incidence &i, const T &e) 0253 { 0254 if (!e.uid().empty()) { 0255 i.setUid(fromStdString(e.uid())); 0256 } 0257 0258 i.setCreated(toDate(e.created())); 0259 i.setLastModified(toDate(e.lastModified())); 0260 i.setRevision(e.sequence()); 0261 i.setSecrecy(toSecrecy(e.classification())); 0262 i.setCategories(toStringList(e.categories())); 0263 0264 if (e.start().isValid()) { 0265 i.setDtStart(toDate(e.start())); 0266 i.setAllDay(e.start().isDateOnly()); 0267 } 0268 0269 i.setSummary(fromStdString(e.summary())); // TODO detect richtext 0270 i.setDescription(fromStdString(e.description())); // TODO detect richtext 0271 i.setStatus(toStatus(e.status())); 0272 const auto attendees{e.attendees()}; 0273 for (const Kolab::Attendee &a : attendees) { 0274 /* 0275 * KCalendarCore always sets a UID if empty, but that's just a pointer, and not the uid of a real contact. 0276 * Since that means the semantics of the two are different, we have to store the kolab uid as a custom property. 0277 */ 0278 KCalendarCore::Attendee attendee(fromStdString(a.contact().name()), 0279 fromStdString(a.contact().email()), 0280 a.rsvp(), 0281 toPartStat(a.partStat()), 0282 toRole(a.role())); 0283 if (!a.contact().uid().empty()) { // TODO Identify contact from addressbook based on uid 0284 attendee.customProperties().setNonKDECustomProperty(CUSTOM_KOLAB_CONTACT_UUID, fromStdString(a.contact().uid())); 0285 } 0286 if (!a.delegatedTo().empty()) { 0287 if (a.delegatedTo().size() > 1) { 0288 qCWarning(PIMKOLAB_LOG) << "multiple delegatees are not supported"; 0289 } 0290 attendee.setDelegate(toMailto(a.delegatedTo().front().email(), a.delegatedTo().front().name()).toString()); 0291 } 0292 if (!a.delegatedFrom().empty()) { 0293 if (a.delegatedFrom().size() > 1) { 0294 qCWarning(PIMKOLAB_LOG) << "multiple delegators are not supported"; 0295 } 0296 attendee.setDelegator(toMailto(a.delegatedFrom().front().email(), a.delegatedFrom().front().name()).toString()); 0297 } 0298 if (a.cutype() != Kolab::CutypeIndividual) { 0299 attendee.customProperties().setNonKDECustomProperty(CUSTOM_KOLAB_CONTACT_CUTYPE, QString::number(a.cutype())); 0300 } 0301 i.addAttendee(attendee); 0302 } 0303 const auto attachments{e.attachments()}; 0304 for (const Kolab::Attachment &a : attachments) { 0305 KCalendarCore::Attachment att; 0306 if (!a.uri().empty()) { 0307 att = KCalendarCore::Attachment(fromStdString(a.uri()), fromStdString(a.mimetype())); 0308 } else { 0309 att = KCalendarCore::Attachment(QByteArray::fromRawData(a.data().c_str(), a.data().size()).toBase64(), fromStdString(a.mimetype())); 0310 } 0311 if (!a.label().empty()) { 0312 att.setLabel(fromStdString(a.label())); 0313 } 0314 i.addAttachment(att); 0315 } 0316 0317 QMap<QByteArray, QString> props; 0318 const auto customProperties{e.customProperties()}; 0319 for (const Kolab::CustomProperty &prop : customProperties) { 0320 QString key; 0321 if (prop.identifier.compare(0, 5, "X-KDE")) { 0322 key.append(QLatin1StringView("X-KOLAB-")); 0323 } 0324 key.append(fromStdString(prop.identifier)); 0325 props.insert(key.toLatin1(), fromStdString(prop.value)); 0326 // i.setCustomProperty("KOLAB", fromStdString(prop.identifier).toLatin1(), fromStdString(prop.value)); 0327 } 0328 i.setCustomProperties(props); 0329 } 0330 0331 template<typename T, typename I> 0332 void getIncidence(T &i, const I &e) 0333 { 0334 i.setUid(toStdString(e.uid())); 0335 i.setCreated(fromDate(e.created(), false)); 0336 i.setLastModified(fromDate(e.lastModified(), false)); 0337 i.setSequence(e.revision()); 0338 i.setClassification(fromSecrecy(e.secrecy())); 0339 i.setCategories(fromStringList(e.categories())); 0340 0341 i.setStart(fromDate(e.dtStart(), e.allDay())); 0342 i.setSummary(toStdString(e.summary())); 0343 i.setDescription(toStdString(e.description())); 0344 i.setStatus(fromStatus(e.status())); 0345 std::vector<Kolab::Attendee> attendees; 0346 const auto eAttendees{e.attendees()}; 0347 for (const KCalendarCore::Attendee &ptr : eAttendees) { 0348 const QString &uid = ptr.customProperties().nonKDECustomProperty(CUSTOM_KOLAB_CONTACT_UUID); 0349 Kolab::Attendee a(Kolab::ContactReference(toStdString(ptr.email()), toStdString(ptr.name()), toStdString(uid))); 0350 a.setRSVP(ptr.RSVP()); 0351 a.setPartStat(fromPartStat(ptr.status())); 0352 a.setRole(fromRole(ptr.role())); 0353 if (!ptr.delegate().isEmpty()) { 0354 std::string name; 0355 const std::string &email = fromMailto(QUrl(ptr.delegate()), name); 0356 a.setDelegatedTo(std::vector<Kolab::ContactReference>() << Kolab::ContactReference(email, name)); 0357 } 0358 if (!ptr.delegator().isEmpty()) { 0359 std::string name; 0360 const std::string &email = fromMailto(QUrl(ptr.delegator()), name); 0361 a.setDelegatedFrom(std::vector<Kolab::ContactReference>() << Kolab::ContactReference(email, name)); 0362 } 0363 const QString &cutype = ptr.customProperties().nonKDECustomProperty(CUSTOM_KOLAB_CONTACT_CUTYPE); 0364 if (!cutype.isEmpty()) { 0365 a.setCutype(static_cast<Kolab::Cutype>(cutype.toInt())); 0366 } 0367 0368 attendees.push_back(a); 0369 } 0370 i.setAttendees(attendees); 0371 std::vector<Kolab::Attachment> attachments; 0372 const auto eAttachments{e.attachments()}; 0373 for (const KCalendarCore::Attachment &att : eAttachments) { 0374 Kolab::Attachment a; 0375 if (att.isUri()) { 0376 a.setUri(toStdString(att.uri()), toStdString(att.mimeType())); 0377 } else { 0378 a.setData(std::string(att.decodedData().data(), att.decodedData().size()), toStdString(att.mimeType())); 0379 } 0380 a.setLabel(toStdString(att.label())); 0381 attachments.push_back(a); 0382 } 0383 i.setAttachments(attachments); 0384 0385 std::vector<Kolab::CustomProperty> customProperties; 0386 const QMap<QByteArray, QString> &props = e.customProperties(); 0387 for (QMap<QByteArray, QString>::const_iterator it = props.cbegin(), end(props.cend()); it != end; ++it) { 0388 QString key(QString::fromUtf8(it.key())); 0389 if (key == QLatin1StringView(CUSTOM_KOLAB_URL)) { 0390 continue; 0391 } 0392 customProperties.push_back(Kolab::CustomProperty(toStdString(key.remove(QStringLiteral("X-KOLAB-"))), toStdString(it.value()))); 0393 } 0394 i.setCustomProperties(customProperties); 0395 } 0396 0397 int toWeekDay(Kolab::Weekday wday) 0398 { 0399 switch (wday) { 0400 case Kolab::Monday: 0401 return 1; 0402 case Kolab::Tuesday: 0403 return 2; 0404 case Kolab::Wednesday: 0405 return 3; 0406 case Kolab::Thursday: 0407 return 4; 0408 case Kolab::Friday: 0409 return 5; 0410 case Kolab::Saturday: 0411 return 6; 0412 case Kolab::Sunday: 0413 return 7; 0414 default: 0415 qCCritical(PIMKOLAB_LOG) << "unhandled"; 0416 Q_ASSERT(0); 0417 } 0418 return 1; 0419 } 0420 0421 Kolab::Weekday fromWeekDay(int wday) 0422 { 0423 switch (wday) { 0424 case 1: 0425 return Kolab::Monday; 0426 case 2: 0427 return Kolab::Tuesday; 0428 case 3: 0429 return Kolab::Wednesday; 0430 case 4: 0431 return Kolab::Thursday; 0432 case 5: 0433 return Kolab::Friday; 0434 case 6: 0435 return Kolab::Saturday; 0436 case 7: 0437 return Kolab::Sunday; 0438 default: 0439 qCCritical(PIMKOLAB_LOG) << "unhandled"; 0440 Q_ASSERT(0); 0441 } 0442 return Kolab::Monday; 0443 } 0444 0445 KCalendarCore::RecurrenceRule::PeriodType toRecurrenceType(Kolab::RecurrenceRule::Frequency freq) 0446 { 0447 switch (freq) { 0448 case Kolab::RecurrenceRule::FreqNone: 0449 qCWarning(PIMKOLAB_LOG) << "no recurrence?"; 0450 break; 0451 case Kolab::RecurrenceRule::Yearly: 0452 return KCalendarCore::RecurrenceRule::rYearly; 0453 case Kolab::RecurrenceRule::Monthly: 0454 return KCalendarCore::RecurrenceRule::rMonthly; 0455 case Kolab::RecurrenceRule::Weekly: 0456 return KCalendarCore::RecurrenceRule::rWeekly; 0457 case Kolab::RecurrenceRule::Daily: 0458 return KCalendarCore::RecurrenceRule::rDaily; 0459 case Kolab::RecurrenceRule::Hourly: 0460 return KCalendarCore::RecurrenceRule::rHourly; 0461 case Kolab::RecurrenceRule::Minutely: 0462 return KCalendarCore::RecurrenceRule::rMinutely; 0463 case Kolab::RecurrenceRule::Secondly: 0464 return KCalendarCore::RecurrenceRule::rSecondly; 0465 default: 0466 qCCritical(PIMKOLAB_LOG) << "unhandled"; 0467 Q_ASSERT(0); 0468 } 0469 return KCalendarCore::RecurrenceRule::rNone; 0470 } 0471 0472 Kolab::RecurrenceRule::Frequency fromRecurrenceType(KCalendarCore::RecurrenceRule::PeriodType freq) 0473 { 0474 switch (freq) { 0475 case KCalendarCore::RecurrenceRule::rNone: 0476 qCWarning(PIMKOLAB_LOG) << "no recurrence?"; 0477 break; 0478 case KCalendarCore::RecurrenceRule::rYearly: 0479 return Kolab::RecurrenceRule::Yearly; 0480 case KCalendarCore::RecurrenceRule::rMonthly: 0481 return Kolab::RecurrenceRule::Monthly; 0482 case KCalendarCore::RecurrenceRule::rWeekly: 0483 return Kolab::RecurrenceRule::Weekly; 0484 case KCalendarCore::RecurrenceRule::rDaily: 0485 return Kolab::RecurrenceRule::Daily; 0486 case KCalendarCore::RecurrenceRule::rHourly: 0487 return Kolab::RecurrenceRule::Hourly; 0488 case KCalendarCore::RecurrenceRule::rMinutely: 0489 return Kolab::RecurrenceRule::Minutely; 0490 case KCalendarCore::RecurrenceRule::rSecondly: 0491 return Kolab::RecurrenceRule::Secondly; 0492 default: 0493 qCCritical(PIMKOLAB_LOG) << "unhandled"; 0494 Q_ASSERT(0); 0495 } 0496 return Kolab::RecurrenceRule::FreqNone; 0497 } 0498 0499 KCalendarCore::RecurrenceRule::WDayPos toWeekDayPos(Kolab::DayPos dp) 0500 { 0501 return KCalendarCore::RecurrenceRule::WDayPos(dp.occurence(), toWeekDay(dp.weekday())); 0502 } 0503 0504 Kolab::DayPos fromWeekDayPos(KCalendarCore::RecurrenceRule::WDayPos dp) 0505 { 0506 return {dp.pos(), fromWeekDay(dp.day())}; 0507 } 0508 0509 template<typename T> 0510 void setRecurrence(KCalendarCore::Incidence &e, const T &event) 0511 { 0512 const Kolab::RecurrenceRule &rrule = event.recurrenceRule(); 0513 if (rrule.isValid()) { 0514 KCalendarCore::Recurrence *rec = e.recurrence(); 0515 0516 KCalendarCore::RecurrenceRule *defaultRR = rec->defaultRRule(true); 0517 Q_ASSERT(defaultRR); 0518 0519 defaultRR->setWeekStart(toWeekDay(rrule.weekStart())); 0520 defaultRR->setRecurrenceType(toRecurrenceType(rrule.frequency())); 0521 defaultRR->setFrequency(rrule.interval()); 0522 0523 if (rrule.end().isValid()) { 0524 rec->setEndDateTime(toDate(rrule.end())); // TODO date/datetime setEndDate(). With date-only the start date has to be taken into account. 0525 } else { 0526 rec->setDuration(rrule.count()); 0527 } 0528 0529 if (!rrule.bysecond().empty()) { 0530 const std::vector<int> bySecond = rrule.bysecond(); 0531 const QList<int> stdVector = QList<int>(bySecond.begin(), bySecond.end()); 0532 defaultRR->setBySeconds(stdVector.toList()); 0533 } 0534 if (!rrule.byminute().empty()) { 0535 const std::vector<int> byMinutes = rrule.byminute(); 0536 const QList<int> stdVector = QList<int>(byMinutes.begin(), byMinutes.end()); 0537 defaultRR->setByMinutes(stdVector.toList()); 0538 } 0539 if (!rrule.byhour().empty()) { 0540 const std::vector<int> byHours = rrule.byhour(); 0541 const QList<int> stdVector = QList<int>(byHours.begin(), byHours.end()); 0542 defaultRR->setByHours(stdVector.toList()); 0543 } 0544 if (!rrule.byday().empty()) { 0545 QList<KCalendarCore::RecurrenceRule::WDayPos> daypos; 0546 const auto bydays{rrule.byday()}; 0547 for (const Kolab::DayPos &dp : bydays) { 0548 daypos.append(toWeekDayPos(dp)); 0549 } 0550 defaultRR->setByDays(daypos); 0551 } 0552 if (!rrule.bymonthday().empty()) { 0553 const std::vector<int> byMonthDays = rrule.bymonthday(); 0554 const QList<int> stdVector = QList<int>(byMonthDays.begin(), byMonthDays.end()); 0555 defaultRR->setByMonthDays(stdVector.toList()); 0556 } 0557 if (!rrule.byyearday().empty()) { 0558 const std::vector<int> byYearDays = rrule.byyearday(); 0559 const QList<int> stdVector = QList<int>(byYearDays.begin(), byYearDays.end()); 0560 defaultRR->setByYearDays(stdVector.toList()); 0561 } 0562 if (!rrule.byweekno().empty()) { 0563 const std::vector<int> byWeekNumbers = rrule.byweekno(); 0564 const QList<int> stdVector = QList<int>(byWeekNumbers.begin(), byWeekNumbers.end()); 0565 defaultRR->setByWeekNumbers(stdVector.toList()); 0566 } 0567 if (!rrule.bymonth().empty()) { 0568 const std::vector<int> byMonths = rrule.bymonth(); 0569 const QList<int> stdVector = QList<int>(byMonths.begin(), byMonths.end()); 0570 defaultRR->setByMonths(stdVector.toList()); 0571 } 0572 } 0573 const auto recurrenceDates{event.recurrenceDates()}; 0574 for (const Kolab::cDateTime &dt : recurrenceDates) { 0575 const QDateTime &date = toDate(dt); 0576 if (dt.isDateOnly()) { 0577 e.recurrence()->addRDate(date.date()); 0578 } else { 0579 e.recurrence()->addRDateTime(date); 0580 } 0581 } 0582 const auto exceptionDates{event.exceptionDates()}; 0583 for (const Kolab::cDateTime &dt : exceptionDates) { 0584 const QDateTime &date = toDate(dt); 0585 if (dt.isDateOnly()) { 0586 e.recurrence()->addExDate(date.date()); 0587 } else { 0588 e.recurrence()->addExDateTime(date); 0589 } 0590 } 0591 } 0592 0593 template<typename T, typename I> 0594 void getRecurrence(T &i, const I &e) 0595 { 0596 if (!e.recurs()) { 0597 return; 0598 } 0599 KCalendarCore::Recurrence *rec = e.recurrence(); 0600 KCalendarCore::RecurrenceRule *defaultRR = rec->defaultRRule(false); 0601 if (!defaultRR) { 0602 qCWarning(PIMKOLAB_LOG) << "no recurrence"; 0603 return; 0604 } 0605 Q_ASSERT(defaultRR); 0606 0607 Kolab::RecurrenceRule rrule; 0608 rrule.setWeekStart(fromWeekDay(defaultRR->weekStart())); 0609 rrule.setFrequency(fromRecurrenceType(defaultRR->recurrenceType())); 0610 rrule.setInterval(defaultRR->frequency()); 0611 0612 if (defaultRR->duration() != 0) { // Inidcates if end date is set or not 0613 if (defaultRR->duration() > 0) { 0614 rrule.setCount(defaultRR->duration()); 0615 } 0616 } else { 0617 rrule.setEnd(fromDate(defaultRR->endDt(), e.allDay())); 0618 } 0619 0620 const QList<int> bySecondsVector = defaultRR->bySeconds().toVector(); 0621 const auto stdVectorBySeconds = std::vector<int>(bySecondsVector.begin(), bySecondsVector.end()); 0622 rrule.setBysecond(stdVectorBySeconds); 0623 0624 const QList<int> byMinutesVector = defaultRR->byMinutes().toVector(); 0625 const auto stdVectorByMinutes = std::vector<int>(byMinutesVector.begin(), byMinutesVector.end()); 0626 rrule.setByminute(stdVectorByMinutes); 0627 0628 const QList<int> byHoursVector = defaultRR->byHours().toVector(); 0629 const auto stdVectorByHours = std::vector<int>(byHoursVector.begin(), byHoursVector.end()); 0630 rrule.setByhour(stdVectorByHours); 0631 0632 std::vector<Kolab::DayPos> daypos; 0633 const auto defaultRRByDays{defaultRR->byDays()}; 0634 daypos.reserve(defaultRRByDays.count()); 0635 0636 for (const KCalendarCore::RecurrenceRule::WDayPos &dp : defaultRRByDays) { 0637 daypos.push_back(fromWeekDayPos(dp)); 0638 } 0639 rrule.setByday(daypos); 0640 0641 const QList<int> bymonthdayVector = defaultRR->byMonthDays().toVector(); 0642 const auto stdByMonthDayVector = std::vector<int>(bymonthdayVector.begin(), bymonthdayVector.end()); 0643 rrule.setBymonthday(stdByMonthDayVector); 0644 0645 const QList<int> byYearDaysVector = defaultRR->byYearDays().toVector(); 0646 const auto stdByYearDayVector = std::vector<int>(byYearDaysVector.begin(), byYearDaysVector.end()); 0647 rrule.setByyearday(stdByYearDayVector); 0648 0649 const QList<int> byWeekNumberVector = defaultRR->byWeekNumbers().toVector(); 0650 const auto stdWeekNumberVector = std::vector<int>(byWeekNumberVector.begin(), byWeekNumberVector.end()); 0651 rrule.setByweekno(stdWeekNumberVector); 0652 0653 const QList<int> byMonthVector = defaultRR->byMonths().toVector(); 0654 const auto stdByMonthVector = std::vector<int>(byMonthVector.begin(), byMonthVector.end()); 0655 rrule.setBymonth(stdByMonthVector); 0656 0657 i.setRecurrenceRule(rrule); 0658 0659 std::vector<Kolab::cDateTime> rdates; 0660 const auto rDateTimes{rec->rDateTimes()}; 0661 for (const QDateTime &dt : rDateTimes) { 0662 rdates.push_back(fromDate(dt, e.allDay())); 0663 } 0664 const auto recRDates{rec->rDates()}; 0665 for (const QDate &dt : recRDates) { 0666 rdates.push_back(fromDate(QDateTime(dt, {}), true)); 0667 } 0668 i.setRecurrenceDates(rdates); 0669 0670 std::vector<Kolab::cDateTime> exdates; 0671 const auto recExDateTimes{rec->exDateTimes()}; 0672 for (const QDateTime &dt : recExDateTimes) { 0673 exdates.push_back(fromDate(dt, e.allDay())); 0674 } 0675 const auto exDates = rec->exDates(); 0676 for (const QDate &dt : exDates) { 0677 exdates.push_back(fromDate(QDateTime(dt, {}), true)); 0678 } 0679 i.setExceptionDates(exdates); 0680 0681 if (!rec->exRules().empty()) { 0682 qCWarning(PIMKOLAB_LOG) << "exrules are not supported"; 0683 } 0684 } 0685 0686 template<typename T> 0687 void setTodoEvent(KCalendarCore::Incidence &i, const T &e) 0688 { 0689 i.setPriority(toPriority(e.priority())); 0690 if (!e.location().empty()) { 0691 i.setLocation(fromStdString(e.location())); // TODO detect richtext 0692 } 0693 if (e.organizer().isValid()) { 0694 i.setOrganizer(KCalendarCore::Person(fromStdString(e.organizer().name()), fromStdString(e.organizer().email()))); // TODO handle uid too 0695 } 0696 if (!e.url().empty()) { 0697 i.setNonKDECustomProperty(CUSTOM_KOLAB_URL, fromStdString(e.url())); 0698 } 0699 if (e.recurrenceID().isValid()) { 0700 i.setRecurrenceId(toDate(e.recurrenceID())); // TODO THISANDFUTURE 0701 } 0702 setRecurrence(i, e); 0703 const auto alarms{e.alarms()}; 0704 for (const Kolab::Alarm &a : alarms) { 0705 KCalendarCore::Alarm::Ptr alarm = KCalendarCore::Alarm::Ptr(new KCalendarCore::Alarm(&i)); 0706 switch (a.type()) { 0707 case Kolab::Alarm::EMailAlarm: { 0708 KCalendarCore::Person::List receipents; 0709 const auto aAttendees{a.attendees()}; 0710 for (Kolab::ContactReference c : aAttendees) { 0711 KCalendarCore::Person person(fromStdString(c.name()), fromStdString(c.email())); 0712 receipents.append(person); 0713 } 0714 alarm->setEmailAlarm(fromStdString(a.summary()), fromStdString(a.description()), receipents); 0715 break; 0716 } 0717 case Kolab::Alarm::DisplayAlarm: 0718 alarm->setDisplayAlarm(fromStdString(a.text())); 0719 break; 0720 case Kolab::Alarm::AudioAlarm: 0721 alarm->setAudioAlarm(fromStdString(a.audioFile().uri())); 0722 break; 0723 default: 0724 qCCritical(PIMKOLAB_LOG) << "invalid alarm"; 0725 } 0726 0727 if (a.start().isValid()) { 0728 alarm->setTime(toDate(a.start())); 0729 } else if (a.relativeStart().isValid()) { 0730 if (a.relativeTo() == Kolab::End) { 0731 alarm->setEndOffset(toDuration(a.relativeStart())); 0732 } else { 0733 alarm->setStartOffset(toDuration(a.relativeStart())); 0734 } 0735 } 0736 0737 alarm->setSnoozeTime(toDuration(a.duration())); 0738 alarm->setRepeatCount(a.numrepeat()); 0739 alarm->setEnabled(true); 0740 i.addAlarm(alarm); 0741 } 0742 } 0743 0744 template<typename T, typename I> 0745 void getTodoEvent(T &i, const I &e) 0746 { 0747 i.setPriority(fromPriority(e.priority())); 0748 i.setLocation(toStdString(e.location())); 0749 if (!e.organizer().email().isEmpty()) { 0750 i.setOrganizer(Kolab::ContactReference(Kolab::ContactReference::EmailReference, 0751 toStdString(e.organizer().email()), 0752 toStdString(e.organizer().name()))); // TODO handle uid too 0753 } 0754 i.setUrl(toStdString(e.nonKDECustomProperty(CUSTOM_KOLAB_URL))); 0755 i.setRecurrenceID(fromDate(e.recurrenceId(), e.allDay()), false); // TODO THISANDFUTURE 0756 getRecurrence(i, e); 0757 std::vector<Kolab::Alarm> alarms; 0758 const auto eAlarms{e.alarms()}; 0759 for (const KCalendarCore::Alarm::Ptr &a : eAlarms) { 0760 Kolab::Alarm alarm; 0761 // TODO KCalendarCore disables alarms using KCalendarCore::Alarm::enabled() (X-KDE-KCALCORE-ENABLED) We should either delete the alarm, or store the 0762 // 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 0763 // alarms. 0764 if (!a->enabled()) { 0765 qCWarning(PIMKOLAB_LOG) << "skipping disabled alarm"; 0766 continue; 0767 } 0768 switch (a->type()) { 0769 case KCalendarCore::Alarm::Display: 0770 alarm = Kolab::Alarm(toStdString(a->text())); 0771 break; 0772 case KCalendarCore::Alarm::Email: { 0773 std::vector<Kolab::ContactReference> receipents; 0774 const auto mailAddresses = a->mailAddresses(); 0775 for (const KCalendarCore::Person &p : mailAddresses) { 0776 receipents.emplace_back(toStdString(p.email()), toStdString(p.name())); 0777 } 0778 alarm = Kolab::Alarm(toStdString(a->mailSubject()), toStdString(a->mailText()), receipents); 0779 break; 0780 } 0781 case KCalendarCore::Alarm::Audio: { 0782 Kolab::Attachment audioFile; 0783 audioFile.setUri(toStdString(a->audioFile()), std::string()); 0784 alarm = Kolab::Alarm(audioFile); 0785 break; 0786 } 0787 default: 0788 qCCritical(PIMKOLAB_LOG) << "unhandled alarm"; 0789 } 0790 0791 if (a->hasTime()) { 0792 alarm.setStart(fromDate(a->time(), false)); 0793 } else if (a->hasStartOffset()) { 0794 alarm.setRelativeStart(fromDuration(a->startOffset()), Kolab::Start); 0795 } else if (a->hasEndOffset()) { 0796 alarm.setRelativeStart(fromDuration(a->endOffset()), Kolab::End); 0797 } else { 0798 qCCritical(PIMKOLAB_LOG) << "alarm trigger is missing"; 0799 continue; 0800 } 0801 0802 alarm.setDuration(fromDuration(a->snoozeTime()), a->repeatCount()); 0803 0804 alarms.push_back(alarm); 0805 } 0806 i.setAlarms(alarms); 0807 } 0808 0809 KCalendarCore::Event::Ptr toKCalendarCore(const Kolab::Event &event) 0810 { 0811 KCalendarCore::Event::Ptr e(new KCalendarCore::Event); 0812 setIncidence(*e, event); 0813 setTodoEvent(*e, event); 0814 if (event.end().isValid()) { 0815 e->setDtEnd(toDate(event.end())); 0816 } 0817 if (event.duration().isValid()) { 0818 e->setDuration(toDuration(event.duration())); 0819 } 0820 if (event.transparency()) { 0821 e->setTransparency(KCalendarCore::Event::Transparent); 0822 } else { 0823 e->setTransparency(KCalendarCore::Event::Opaque); 0824 } 0825 return e; 0826 } 0827 0828 Kolab::Event fromKCalendarCore(const KCalendarCore::Event &event) 0829 { 0830 Kolab::Event e; 0831 getIncidence(e, event); 0832 getTodoEvent(e, event); 0833 if (event.hasEndDate()) { 0834 e.setEnd(fromDate(event.dtEnd(), event.allDay())); 0835 } else if (event.hasDuration()) { 0836 e.setDuration(fromDuration(event.duration())); 0837 } 0838 if (event.transparency() == KCalendarCore::Event::Transparent) { 0839 e.setTransparency(true); 0840 } else { 0841 e.setTransparency(false); 0842 } 0843 return e; 0844 } 0845 0846 KCalendarCore::Todo::Ptr toKCalendarCore(const Kolab::Todo &todo) 0847 { 0848 KCalendarCore::Todo::Ptr e(new KCalendarCore::Todo); 0849 setIncidence(*e, todo); 0850 setTodoEvent(*e, todo); 0851 if (todo.due().isValid()) { 0852 e->setDtDue(toDate(todo.due())); 0853 } 0854 if (!todo.relatedTo().empty()) { 0855 e->setRelatedTo(Kolab::Conversion::fromStdString(todo.relatedTo().front()), KCalendarCore::Incidence::RelTypeParent); 0856 if (todo.relatedTo().size() > 1) { 0857 qCCritical(PIMKOLAB_LOG) << "only one relation support but got multiple"; 0858 } 0859 } 0860 e->setPercentComplete(todo.percentComplete()); 0861 return e; 0862 } 0863 0864 Kolab::Todo fromKCalendarCore(const KCalendarCore::Todo &todo) 0865 { 0866 Kolab::Todo t; 0867 getIncidence(t, todo); 0868 getTodoEvent(t, todo); 0869 t.setDue(fromDate(todo.dtDue(true), todo.allDay())); 0870 t.setPercentComplete(todo.percentComplete()); 0871 const QString relatedTo = todo.relatedTo(KCalendarCore::Incidence::RelTypeParent); 0872 if (!relatedTo.isEmpty()) { 0873 std::vector<std::string> relateds; 0874 relateds.push_back(Kolab::Conversion::toStdString(relatedTo)); 0875 t.setRelatedTo(relateds); 0876 } 0877 return t; 0878 } 0879 0880 KCalendarCore::Journal::Ptr toKCalendarCore(const Kolab::Journal &journal) 0881 { 0882 KCalendarCore::Journal::Ptr e(new KCalendarCore::Journal); 0883 setIncidence(*e, journal); 0884 // TODO contacts 0885 return e; 0886 } 0887 0888 Kolab::Journal fromKCalendarCore(const KCalendarCore::Journal &journal) 0889 { 0890 Kolab::Journal j; 0891 getIncidence(j, journal); 0892 // TODO contacts 0893 return j; 0894 } 0895 } 0896 }