File indexing completed on 2024-05-05 05:28:19
0001 /* 0002 * SPDX-FileCopyrightText: 2020 Dimitris Kardarakos <dimkard@posteo.net> 0003 * 0004 * SPDX-License-Identifier: GPL-3.0-or-later 0005 */ 0006 0007 #include "calendarcontroller.h" 0008 #include "localcalendar.h" 0009 #include <QStandardPaths> 0010 #include <QDebug> 0011 #include <QFile> 0012 #include <QStringLiteral> 0013 #include <KLocalizedString> 0014 #include <KCalendarCore/ICalFormat> 0015 #include <KCalendarCore/Calendar> 0016 #include <KCalendarCore/MemoryCalendar> 0017 #include <KCalendarCore/Attendee> 0018 #include <KCalendarCore/Person> 0019 #include "attendeesmodel.h" 0020 #include "calindoriconfig.h" 0021 0022 CalendarController &CalendarController::instance() 0023 { 0024 static CalendarController instance; 0025 return instance; 0026 } 0027 0028 CalendarController::CalendarController(QObject *parent) : QObject {parent}, m_events {}, m_todos {} 0029 { 0030 m_calendar = std::make_unique<LocalCalendar>(); 0031 m_calendar->setName(CalindoriConfig::instance().activeCalendar()); 0032 0033 connect(&CalindoriConfig::instance(), &CalindoriConfig::activeCalendarChanged, this, [this]{ 0034 m_calendar->setName(CalindoriConfig::instance().activeCalendar()); 0035 Q_EMIT activeCalendarChanged(); 0036 }); 0037 } 0038 0039 void CalendarController::importCalendarData(const QByteArray &data) 0040 { 0041 KCalendarCore::MemoryCalendar::Ptr importedCalendar {new KCalendarCore::MemoryCalendar {QTimeZone::systemTimeZoneId()}}; 0042 KCalendarCore::ICalFormat icalFormat {}; 0043 auto readResult = icalFormat.fromRawString(importedCalendar, data); 0044 0045 if (!readResult) { 0046 qDebug() << "The file read was not a valid calendar"; 0047 Q_EMIT statusMessageChanged(i18n("The url or file given does not contain valid calendar data"), MessageType::NegativeAnswer); 0048 return; 0049 } 0050 0051 m_events = importedCalendar->rawEvents(); 0052 m_todos = importedCalendar->rawTodos(); 0053 0054 if (m_events.isEmpty() && m_todos.isEmpty()) { 0055 qDebug() << "No events or tasks found."; 0056 Q_EMIT statusMessageChanged(i18n("The url or file given does not contain any event or task"), MessageType::NegativeAnswer); 0057 return; 0058 } 0059 0060 QString confirmMsg {}; 0061 0062 if (!m_events.isEmpty() && m_todos.isEmpty()) { 0063 confirmMsg = i18np("1 event will be added", "%1 events will be added", m_events.count()); 0064 } else if (m_events.isEmpty() && !m_todos.isEmpty()) { 0065 confirmMsg = i18np("1 task will be added", "%1 tasks will be added", m_todos.count()); 0066 } else { 0067 auto incidenceCount = m_events.count() + m_todos.count(); 0068 confirmMsg = i18n("%1 incidences will be added", incidenceCount); 0069 } 0070 0071 Q_EMIT statusMessageChanged(confirmMsg, MessageType::Question); 0072 } 0073 0074 void CalendarController::importFromBuffer(LocalCalendar *localCalendar) 0075 { 0076 auto calendar = localCalendar->calendar(); 0077 0078 for (const auto &event : qAsConst(m_events)) { 0079 calendar->addEvent(event); 0080 } 0081 0082 for (const auto &todo : qAsConst(m_todos)) { 0083 calendar->addTodo(todo); 0084 } 0085 0086 bool result = localCalendar->save(); 0087 0088 if (!m_events.isEmpty()) { 0089 Q_EMIT localCalendar->eventsChanged(); 0090 m_events.clear(); 0091 } 0092 0093 if (!m_todos.isEmpty()) { 0094 Q_EMIT localCalendar->todosChanged(); 0095 m_todos.clear(); 0096 } 0097 0098 sendMessage(result); 0099 } 0100 0101 void CalendarController::importFromBuffer(const QString &targetCalendar) 0102 { 0103 auto filePath = CalindoriConfig::instance().calendarFile(targetCalendar); 0104 QFile calendarFile {filePath}; 0105 if (!calendarFile.exists()) { 0106 sendMessage(false); 0107 return; 0108 } 0109 0110 KCalendarCore::Calendar::Ptr calendar {new KCalendarCore::MemoryCalendar(QTimeZone::systemTimeZoneId())}; 0111 KCalendarCore::FileStorage::Ptr storage {new KCalendarCore::FileStorage {calendar}}; 0112 storage->setFileName(filePath); 0113 if (!storage->load()) { 0114 sendMessage(false); 0115 return; 0116 } 0117 0118 for (const auto &event : qAsConst(m_events)) { 0119 calendar->addEvent(event); 0120 } 0121 0122 for (const auto &todo : qAsConst(m_todos)) { 0123 calendar->addTodo(todo); 0124 } 0125 0126 sendMessage(storage->save()); 0127 } 0128 0129 void CalendarController::sendMessage(const bool positive) 0130 { 0131 if (positive) { 0132 Q_EMIT statusMessageChanged(i18n("Import completed successfully"), MessageType::PositiveAnswer); 0133 } else { 0134 Q_EMIT statusMessageChanged(i18n("An error has occurred during import"), MessageType::NegativeAnswer); 0135 } 0136 } 0137 0138 void CalendarController::abortImporting() 0139 { 0140 m_events.clear(); 0141 m_todos.clear(); 0142 } 0143 0144 void CalendarController::removeEvent(LocalCalendar *localCalendar, const QVariantMap &eventData) 0145 { 0146 KCalendarCore::Calendar::Ptr calendar = localCalendar->calendar(); 0147 QString uid = eventData[QStringLiteral("uid")].toString(); 0148 KCalendarCore::Event::Ptr event = calendar->event(uid); 0149 calendar->deleteEvent(event); 0150 bool deleted = localCalendar->save(); 0151 Q_EMIT localCalendar->eventsChanged(); 0152 0153 qDebug() << "Event " << uid << " deleted: " << deleted; 0154 } 0155 0156 void CalendarController::upsertEvent(const QVariantMap &eventData, const QVariantList &attendeesList) 0157 { 0158 qDebug() << "\naddEdit:\tCreating event"; 0159 0160 KCalendarCore::Calendar::Ptr calendar = CalendarController::instance().activeCalendar()->calendar(); 0161 QDateTime now = QDateTime::currentDateTime(); 0162 QString uid = eventData[QStringLiteral("uid")].toString(); 0163 QString summary = eventData[QStringLiteral("summary")].toString(); 0164 bool clearPartStatus {false}; 0165 0166 QDate startDate = eventData[QStringLiteral("startDate")].toDate(); 0167 int startHour = eventData[QStringLiteral("startHour")].value<int>(); 0168 int startMinute = eventData[QStringLiteral("startMinute")].value<int>(); 0169 0170 QDate endDate = eventData[QStringLiteral("endDate")].toDate(); 0171 int endHour = eventData[QStringLiteral("endHour")].value<int>(); 0172 int endMinute = eventData[QStringLiteral("endMinute")].value<int>(); 0173 0174 QDateTime startDateTime; 0175 QDateTime endDateTime; 0176 bool allDayFlg = eventData[QStringLiteral("allDay")].toBool(); 0177 0178 if (allDayFlg) { 0179 startDateTime = startDate.startOfDay(); 0180 endDateTime = endDate.startOfDay(); 0181 } else { 0182 startDateTime = QDateTime(startDate, QTime(startHour, startMinute, 0, 0), QTimeZone::systemTimeZone()); 0183 endDateTime = QDateTime(endDate, QTime(endHour, endMinute, 0, 0), QTimeZone::systemTimeZone()); 0184 } 0185 0186 KCalendarCore::Event::Ptr event; 0187 if (uid.isEmpty()) { 0188 event = KCalendarCore::Event::Ptr(new KCalendarCore::Event()); 0189 event->setUid(summary.at(0) + now.toString(QStringLiteral("yyyyMMddhhmmsszzz"))); 0190 } else { 0191 event = calendar->event(uid); 0192 event->setUid(uid); 0193 clearPartStatus = (event->dtStart() != startDateTime) || (event->dtEnd() != endDateTime) || (event->allDay() != allDayFlg); 0194 } 0195 0196 event->setDtStart(startDateTime); 0197 event->setDtEnd(endDateTime); 0198 event->setDescription(eventData[QStringLiteral("description")].toString()); 0199 event->setSummary(summary); 0200 event->setAllDay(allDayFlg); 0201 event->setLocation(eventData[QStringLiteral("location")].toString()); 0202 0203 event->clearAttendees(); 0204 for (auto &a : qAsConst(attendeesList)) { 0205 auto attendee = a.value<KCalendarCore::Attendee>(); 0206 if (clearPartStatus) { 0207 qDebug() << "Participants need to be informed"; 0208 attendee.setRSVP(true); 0209 attendee.setStatus(KCalendarCore::Attendee::PartStat::NeedsAction); 0210 } 0211 event->addAttendee(attendee); 0212 } 0213 0214 if (!attendeesList.isEmpty()) { 0215 event->setOrganizer(KCalendarCore::Person { CalendarController::instance().activeCalendar()->ownerName(), CalendarController::instance().activeCalendar()->ownerEmail()}); 0216 } 0217 0218 event->clearAlarms(); 0219 QVariantList newAlarms = eventData[QStringLiteral("alarms")].value<QVariantList>(); 0220 QVariantList::const_iterator itr = newAlarms.constBegin(); 0221 while (itr != newAlarms.constEnd()) { 0222 KCalendarCore::Alarm::Ptr newAlarm = event->newAlarm(); 0223 QHash<QString, QVariant> newAlarmHashMap = (*itr).value<QHash<QString, QVariant>>(); 0224 int startOffsetValue = newAlarmHashMap[QStringLiteral("startOffsetValue")].value<int>(); 0225 int startOffsetType = newAlarmHashMap[QStringLiteral("startOffsetType")].value<int>(); 0226 int actionType = newAlarmHashMap[QStringLiteral("actionType")].value<int>(); 0227 0228 qDebug() << "addEdit:\tAdding alarm with start offset value " << startOffsetValue; 0229 newAlarm->setStartOffset(KCalendarCore::Duration(startOffsetValue, static_cast<KCalendarCore::Duration::Type>(startOffsetType))); 0230 newAlarm->setType(static_cast<KCalendarCore::Alarm::Type>(actionType)); 0231 newAlarm->setEnabled(true); 0232 newAlarm->setText((event->summary()).isEmpty() ? event->description() : event->summary()); 0233 ++itr; 0234 } 0235 0236 ushort newPeriod = static_cast<ushort>(eventData[QStringLiteral("periodType")].toInt()); 0237 0238 //Bother with recurrences only if a recurrence has been found, either existing or new 0239 if ((event->recurrenceType() != KCalendarCore::Recurrence::rNone) || (newPeriod != KCalendarCore::Recurrence::rNone)) { 0240 //WORKAROUND: When changing an event from non-recurring to recurring, duplicate events are displayed 0241 if (!uid.isEmpty()) { 0242 calendar->deleteEvent(event); 0243 } 0244 0245 switch (newPeriod) { 0246 case KCalendarCore::Recurrence::rYearlyDay: 0247 case KCalendarCore::Recurrence::rYearlyMonth: 0248 case KCalendarCore::Recurrence::rYearlyPos: 0249 event->recurrence()->setYearly(eventData[QStringLiteral("repeatEvery")].toInt()); 0250 break; 0251 case KCalendarCore::Recurrence::rMonthlyDay: 0252 case KCalendarCore::Recurrence::rMonthlyPos: 0253 event->recurrence()->setMonthly(eventData[QStringLiteral("repeatEvery")].toInt()); 0254 break; 0255 case KCalendarCore::Recurrence::rWeekly: 0256 event->recurrence()->setWeekly(eventData[QStringLiteral("repeatEvery")].toInt()); 0257 break; 0258 case KCalendarCore::Recurrence::rDaily: 0259 event->recurrence()->setDaily(eventData[QStringLiteral("repeatEvery")].toInt()); 0260 break; 0261 default: 0262 event->recurrence()->clear(); 0263 } 0264 0265 if (newPeriod != KCalendarCore::Recurrence::rNone) { 0266 int stopAfter = eventData[QStringLiteral("stopAfter")].toInt() > 0 ? eventData[QStringLiteral("stopAfter")].toInt() : -1; 0267 event->recurrence()->setDuration(stopAfter); 0268 event->recurrence()->setAllDay(allDayFlg); 0269 } 0270 0271 if (!uid.isEmpty()) { 0272 calendar->addEvent(event); 0273 } 0274 } 0275 0276 event->setStatus(eventData[QStringLiteral("status")].value<KCalendarCore::Incidence::Status>()); 0277 0278 if (uid.isEmpty()) { 0279 calendar->addEvent(event); 0280 } 0281 0282 bool merged = CalendarController::instance().activeCalendar()->save(); 0283 Q_EMIT CalendarController::instance().activeCalendar()->eventsChanged(); 0284 0285 qDebug() << "Event upsert: " << merged; 0286 } 0287 0288 QDateTime CalendarController::localSystemDateTime() const 0289 { 0290 return QDateTime::currentDateTime(); 0291 } 0292 0293 QVariantMap CalendarController::validateEvent(const QVariantMap &eventMap) const 0294 { 0295 QVariantMap result {}; 0296 0297 QDate startDate = eventMap[QStringLiteral("startDate")].toDate(); 0298 bool validStartHour {false}; 0299 int startHour = eventMap[QStringLiteral("startHour")].toInt(&validStartHour); 0300 bool validStartMinutes {false}; 0301 int startMinute = eventMap[QStringLiteral("startMinute")].toInt(&validStartMinutes); 0302 QDate endDate = eventMap[QStringLiteral("endDate")].toDate(); 0303 bool validEndHour {false}; 0304 int endHour = eventMap[QStringLiteral("endHour")].toInt(&validEndHour); 0305 bool validEndMinutes {false}; 0306 int endMinutes = eventMap[QStringLiteral("endMinute")].toInt(&validEndMinutes); 0307 bool allDayFlg = eventMap[QStringLiteral("allDay")].toBool(); 0308 0309 if (startDate.isValid() && validStartHour && validStartMinutes && endDate.isValid() && validEndHour && validEndMinutes) { 0310 if (allDayFlg && (endDate != startDate)) { 0311 result[QStringLiteral("success")] = false; 0312 result[QStringLiteral("reason")] = i18n("In case of all day events, start date and end date should be equal"); 0313 0314 return result; 0315 } 0316 0317 auto startDateTime = QDateTime(startDate, QTime(startHour, startMinute, 0, 0), QTimeZone::systemTimeZone()); 0318 auto endDateTime = QDateTime(endDate, QTime(endHour, endMinutes, 0, 0), QTimeZone::systemTimeZone()); 0319 0320 if (!allDayFlg && (startDateTime > endDateTime)) { 0321 result[QStringLiteral("success")] = false; 0322 result[QStringLiteral("reason")] = i18n("End date time should be equal to or greater than the start date time"); 0323 return result; 0324 } 0325 0326 auto validPeriodType {false}; 0327 auto periodType = static_cast<ushort>(eventMap[QStringLiteral("periodType")].toInt(&validPeriodType)); 0328 auto eventDuration = startDateTime.secsTo(endDateTime); 0329 auto validRepeatEvery {false}; 0330 auto repeatEvery = static_cast<ushort>(eventMap[QStringLiteral("repeatEvery")].toInt(&validRepeatEvery)); 0331 0332 if (validPeriodType && (periodType == KCalendarCore::Recurrence::rDaily) && validRepeatEvery && (repeatEvery == 1) && eventDuration > 86400) { 0333 result[QStringLiteral("success")] = false; 0334 result[QStringLiteral("reason")] = i18n("Daily events should not span multiple days"); 0335 return result; 0336 } 0337 } 0338 0339 result[QStringLiteral("success")] = true; 0340 result[QStringLiteral("reason")] = QString(); 0341 0342 return result; 0343 0344 } 0345 0346 void CalendarController::upsertTodo(LocalCalendar *localCalendar, const QVariantMap &todo) 0347 { 0348 KCalendarCore::Calendar::Ptr calendar = localCalendar->calendar(); 0349 KCalendarCore::Todo::Ptr vtodo; 0350 QDateTime now = QDateTime::currentDateTime(); 0351 QString uid = todo[QStringLiteral("uid")].toString(); 0352 QString summary = todo[QStringLiteral("summary")].toString(); 0353 QDate startDate = todo[QStringLiteral("startDate")].toDate(); 0354 int startHour = todo[QStringLiteral("startHour")].value<int>(); 0355 int startMinute = todo[QStringLiteral("startMinute")].value<int>(); 0356 bool allDayFlg = todo[QStringLiteral("allDay")].toBool(); 0357 0358 if (uid.isEmpty()) { 0359 vtodo = KCalendarCore::Todo::Ptr(new KCalendarCore::Todo()); 0360 vtodo->setUid(summary.at(0) + now.toString(QStringLiteral("yyyyMMddhhmmsszzz"))); 0361 } else { 0362 vtodo = calendar->todo(uid); 0363 vtodo->setUid(uid); 0364 } 0365 0366 QDateTime startDateTime; 0367 if (allDayFlg) { 0368 startDateTime = startDate.startOfDay(); 0369 } else { 0370 startDateTime = QDateTime(startDate, QTime(startHour, startMinute, 0, 0), QTimeZone::systemTimeZone()); 0371 } 0372 0373 vtodo->setDtStart(startDateTime); 0374 0375 QDate dueDate = todo[QStringLiteral("dueDate")].toDate(); 0376 0377 bool validDueHour {false}; 0378 int dueHour = todo[QStringLiteral("dueHour")].toInt(&validDueHour); 0379 0380 bool validDueMinutes {false}; 0381 int dueMinute = todo[QStringLiteral("dueMinute")].toInt(&validDueMinutes); 0382 0383 QDateTime dueDateTime = QDateTime(); 0384 if (dueDate.isValid() && validDueHour && validDueMinutes && !allDayFlg) { 0385 dueDateTime = QDateTime(dueDate, QTime(dueHour, dueMinute, 0, 0), QTimeZone::systemTimeZone()); 0386 } else if (dueDate.isValid() && allDayFlg) { 0387 dueDateTime = dueDate.startOfDay(); 0388 } 0389 0390 vtodo->setDtDue(dueDateTime); 0391 vtodo->setDescription(todo[QStringLiteral("description")].toString()); 0392 vtodo->setSummary(summary); 0393 vtodo->setAllDay((startDate.isValid() || dueDate.isValid()) ? allDayFlg : false); 0394 vtodo->setLocation(todo[QStringLiteral("location")].toString()); 0395 vtodo->setCompleted(todo[QStringLiteral("completed")].toBool()); 0396 0397 vtodo->clearAlarms(); 0398 QVariantList newAlarms = todo[QStringLiteral("alarms")].value<QVariantList>(); 0399 QVariantList::const_iterator itr = newAlarms.constBegin(); 0400 while (itr != newAlarms.constEnd()) { 0401 KCalendarCore::Alarm::Ptr newAlarm = vtodo->newAlarm(); 0402 QHash<QString, QVariant> newAlarmHashMap = (*itr).value<QHash<QString, QVariant>>(); 0403 int startOffsetValue = newAlarmHashMap[QStringLiteral("startOffsetValue")].value<int>(); 0404 int startOffsetType = newAlarmHashMap[QStringLiteral("startOffsetType")].value<int>(); 0405 int actionType = newAlarmHashMap[QStringLiteral("actionType")].value<int>(); 0406 0407 newAlarm->setStartOffset(KCalendarCore::Duration(startOffsetValue, static_cast<KCalendarCore::Duration::Type>(startOffsetType))); 0408 newAlarm->setType(static_cast<KCalendarCore::Alarm::Type>(actionType)); 0409 newAlarm->setEnabled(true); 0410 newAlarm->setText((vtodo->summary()).isEmpty() ? vtodo->description() : vtodo->summary()); 0411 ++itr; 0412 } 0413 0414 calendar->addTodo(vtodo); 0415 bool merged = localCalendar->save(); 0416 0417 Q_EMIT localCalendar->todosChanged(); 0418 0419 qDebug() << "Todo upsert: " << merged; 0420 } 0421 0422 void CalendarController::removeTodo(LocalCalendar *localCalendar, const QVariantMap &todo) 0423 { 0424 KCalendarCore::Calendar::Ptr calendar = localCalendar->calendar(); 0425 QString uid = todo[QStringLiteral("uid")].toString(); 0426 KCalendarCore::Todo::Ptr vtodo = calendar->todo(uid); 0427 0428 calendar->deleteTodo(vtodo); 0429 bool removed = localCalendar->save(); 0430 0431 Q_EMIT localCalendar->todosChanged(); 0432 qDebug() << "Todo deleted: " << removed; 0433 } 0434 0435 QVariantMap CalendarController::validateTodo(const QVariantMap &todo) const 0436 { 0437 QVariantMap result {}; 0438 0439 QDate startDate = todo[QStringLiteral("startDate")].toDate(); 0440 bool validStartHour {false}; 0441 int startHour = todo[QStringLiteral("startHour")].toInt(&validStartHour); 0442 bool validStartMinutes {false}; 0443 int startMinute = todo[QStringLiteral("startMinute")].toInt(&validStartMinutes); 0444 QDate dueDate = todo[QStringLiteral("dueDate")].toDate(); 0445 bool validDueHour {false}; 0446 int dueHour = todo[QStringLiteral("dueHour")].toInt(&validDueHour); 0447 bool validDueMinutes {false}; 0448 int dueMinute = todo[QStringLiteral("dueMinute")].toInt(&validDueMinutes); 0449 bool allDayFlg = todo[QStringLiteral("allDay")].toBool(); 0450 0451 if (startDate.isValid() && validStartHour && validStartMinutes && dueDate.isValid() && validDueHour && validDueMinutes) { 0452 if (allDayFlg && (dueDate != startDate)) { 0453 result[QStringLiteral("success")] = false; 0454 result[QStringLiteral("reason")] = i18n("In case of all day tasks, start date and due date should be equal"); 0455 0456 return result; 0457 } 0458 0459 if (!allDayFlg && (QDateTime(startDate, QTime(startHour, startMinute, 0, 0), QTimeZone::systemTimeZone()) > QDateTime(dueDate, QTime(dueHour, dueMinute, 0, 0), QTimeZone::systemTimeZone()))) { 0460 result[QStringLiteral("success")] = false; 0461 result[QStringLiteral("reason")] = i18n("Due date time should be equal to or greater than the start date time"); 0462 return result; 0463 } 0464 } 0465 0466 result[QStringLiteral("success")] = true; 0467 result[QStringLiteral("reason")] = QString(); 0468 0469 return result; 0470 } 0471 0472 QString CalendarController::fileNameFromUrl(const QUrl &sourcePath) 0473 { 0474 return sourcePath.fileName(); 0475 } 0476 0477 QVariantMap CalendarController::exportData(const QString &calendarName) 0478 { 0479 auto filePath = CalindoriConfig::instance().calendarFile(calendarName); 0480 QFile calendarFile {filePath}; 0481 if (!calendarFile.exists()) { 0482 return { 0483 { QStringLiteral("success"), false }, 0484 { QStringLiteral("reason"), i18n("Cannot read calendar. Export failed.") } 0485 }; 0486 } 0487 0488 KCalendarCore::Calendar::Ptr calendar {new KCalendarCore::MemoryCalendar(QTimeZone::systemTimeZoneId())}; 0489 KCalendarCore::FileStorage::Ptr storage {new KCalendarCore::FileStorage {calendar}}; 0490 storage->setFileName(filePath); 0491 if (!storage->load()) { 0492 return { 0493 { QStringLiteral("success"), false }, 0494 { QStringLiteral("reason"), i18n("Cannot load calendar. Export failed.") } 0495 }; 0496 } 0497 0498 auto dirPath = QStandardPaths::writableLocation(QStandardPaths::DownloadLocation); 0499 QFile targetFile {dirPath + QStringLiteral("/calindori_") + calendarName + QStringLiteral(".ics")}; 0500 auto fileSuffix {1}; 0501 while (targetFile.exists()) { 0502 targetFile.setFileName(dirPath + QStringLiteral("/calindori_") + calendarName + QStringLiteral("(") + QString::number(fileSuffix++) + QStringLiteral(").ics")); 0503 } 0504 0505 storage->setFileName(targetFile.fileName()); 0506 if (!(storage->save())) { 0507 return { 0508 { QStringLiteral("success"), false }, 0509 { QStringLiteral("reason"), i18n("Cannot save calendar file. Export failed.") } 0510 }; 0511 0512 } 0513 0514 return { 0515 { QStringLiteral("success"), true }, 0516 { QStringLiteral("reason"), i18n("Export completed successfully") }, 0517 { QStringLiteral("targetFolder"), QUrl {QStringLiteral("file://") + dirPath} } 0518 }; 0519 } 0520 0521 LocalCalendar *CalendarController::activeCalendar() const 0522 { 0523 return m_calendar.get(); 0524 }