File indexing completed on 2024-04-21 05:50:56
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 <QDebug> 0010 #include <QDir> 0011 #include <QFile> 0012 #include <QNetworkRequest> 0013 #include <QNetworkReply> 0014 #include <KLocalizedString> 0015 #include <KConfig> 0016 #include <KConfigGroup> 0017 0018 class CalendarController::Private 0019 { 0020 public: 0021 Private() : config {"kongressrc"} {}; 0022 KConfig config; 0023 QNetworkAccessManager *nam = nullptr; 0024 }; 0025 0026 CalendarController::CalendarController(QObject *parent) 0027 : QObject {parent}, m_storages {QMap<QString, KCalendarCore::FileStorage::Ptr> {}}, m_calendars {QMap<QString, KCalendarCore::MemoryCalendar::Ptr>{}}, d {new Private} 0028 { 0029 loadSavedConferences(); 0030 } 0031 0032 void CalendarController::setNetworkAccessManager(QNetworkAccessManager *nam) 0033 { 0034 d->nam = nam; 0035 } 0036 0037 QString CalendarController::calendars() const 0038 { 0039 return d->config.group("general").readEntry("calendars", QString {}); 0040 } 0041 0042 void CalendarController::removeCalendarFromConfig(const QString &calendarId) 0043 { 0044 d->config.reparseConfiguration(); 0045 auto calendarsList = d->config.group("general").readEntry("calendars", QString {}).split(";"); 0046 if (calendarsList.contains(calendarId)) { 0047 qDebug() << "Removing calendar " << calendarId; 0048 calendarsList.removeAll(calendarId); 0049 0050 d->config.deleteGroup(calendarId); 0051 d->config.group("general").writeEntry("calendars", calendarsList.join(";")); 0052 d->config.sync(); 0053 0054 Q_EMIT calendarsChanged(); 0055 } 0056 } 0057 0058 QString CalendarController::calendarFile(const QString &calendarId) 0059 { 0060 if (d->config.hasGroup(calendarId) && d->config.group(calendarId).hasKey("file")) { 0061 return d->config.group(calendarId).readEntry("file"); 0062 } 0063 d->config.group(calendarId).writeEntry("file", filenameToPath(calendarId)); 0064 d->config.sync(); 0065 0066 return filenameToPath(calendarId); 0067 } 0068 0069 QString CalendarController::filenameToPath(const QString &calendarId) 0070 { 0071 auto basePath = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation); 0072 QDir baseFolder {basePath}; 0073 baseFolder.mkpath(QStringLiteral(".")); 0074 0075 return basePath + "/kongress_" + calendarId + ".ics"; 0076 } 0077 0078 KCalendarCore::MemoryCalendar::Ptr CalendarController::createLocalCalendar(const QString &calendarId, const QByteArray &timeZoneId) 0079 { 0080 auto m_fullpath = calendarFile(calendarId); 0081 auto tzId = QTimeZone::availableTimeZoneIds().contains(timeZoneId) ? timeZoneId : QTimeZone::systemTimeZoneId(); 0082 0083 KCalendarCore::MemoryCalendar::Ptr calendar {new KCalendarCore::MemoryCalendar {tzId}}; 0084 qDebug() << "Memory calendar " << calendarId << "(local) with timezone " << tzId << " has been created"; 0085 KCalendarCore::FileStorage::Ptr storage {new KCalendarCore::FileStorage {calendar}}; 0086 storage->setFileName(m_fullpath); 0087 QFile calendarFile {m_fullpath}; 0088 0089 if (!calendarFile.exists()) { 0090 qDebug() << "Creating file" << storage->save(); 0091 } 0092 0093 if (storage->load()) { 0094 m_storages[calendarId] = storage; 0095 m_calendars[calendarId] = calendar; 0096 } 0097 0098 Q_EMIT calendarsChanged(); 0099 0100 return calendar; 0101 } 0102 0103 void CalendarController::deleteCalendar(const QString &calendarId) 0104 { 0105 qDebug() << "Deleting calendar " << calendarId; 0106 0107 auto fileName = m_storages[calendarId]->fileName(); 0108 0109 QFile calendarFile {fileName}; 0110 0111 if (calendarFile.exists()) { 0112 calendarFile.remove(); 0113 } 0114 0115 m_storages.remove(calendarId); 0116 m_calendars.remove(calendarId); 0117 0118 removeCalendarFromConfig(calendarId); 0119 0120 Q_EMIT calendarsChanged(); 0121 } 0122 0123 bool CalendarController::save(const QString &calendarId) 0124 { 0125 if (m_storages[calendarId]->save()) { 0126 Q_EMIT calendarsChanged(); 0127 0128 return true; 0129 } 0130 0131 return false; 0132 } 0133 0134 void CalendarController::createCalendarFromUrl(const QString &calendarId, const QUrl &url, const QByteArray &timeZoneId) 0135 { 0136 if (calendarId.isEmpty() || url.isEmpty()) { 0137 return; 0138 } 0139 qDebug() << "createCalendarFromUrl: calendarId " << calendarId << ", url" << url; 0140 Q_EMIT downlading(calendarId, true); 0141 0142 auto filePath = calendarFile(calendarId); 0143 0144 QNetworkRequest request {url}; 0145 auto reply = d->nam->get(request); 0146 0147 connect(reply, &QNetworkReply::finished, [this, reply, filePath, calendarId, timeZoneId]() { 0148 if (reply->error() == QNetworkReply::NoError) { 0149 if (saveToDisk(filePath, reply->readAll())) { 0150 downloadFinished(calendarId, timeZoneId, filePath); 0151 } 0152 } 0153 reply->deleteLater(); 0154 Q_EMIT downlading(calendarId, false); 0155 }); 0156 0157 } 0158 0159 void CalendarController::downloadFinished(const QString &calendarId, const QByteArray &timeZoneId, const QString &filePath) 0160 { 0161 auto availableTimezones = QTimeZone::availableTimeZoneIds(); 0162 auto tz = availableTimezones.contains(timeZoneId) ? timeZoneId : QTimeZone::systemTimeZoneId(); 0163 addTzIdToConfig(calendarId, tz); 0164 0165 KCalendarCore::MemoryCalendar::Ptr calendar {new KCalendarCore::MemoryCalendar {tz}}; 0166 qDebug() << "Memory calendar " << calendarId << " (online) with timezone " << tz << " has been created"; 0167 KCalendarCore::FileStorage::Ptr storage {new KCalendarCore::FileStorage {calendar}}; 0168 storage->setFileName(filePath); 0169 0170 if (storage->load()) { 0171 m_storages[calendarId] = storage; 0172 m_calendars[calendarId] = calendar; 0173 } 0174 0175 addConferenceToConfig(calendarId); 0176 0177 Q_EMIT calendarsChanged(); 0178 Q_EMIT calendarDownloaded(calendarId); 0179 } 0180 0181 bool CalendarController::saveToDisk(const QString &filename, const QByteArray &data) 0182 { 0183 QFile file {filename}; 0184 if (!file.open(QIODevice::WriteOnly)) { 0185 qDebug() << (QString {"Could not open %1 for writing: %2"}).arg(qPrintable(filename), qPrintable(file.errorString())); 0186 return false; 0187 } 0188 file.write(data); 0189 file.close(); 0190 0191 return true; 0192 } 0193 0194 KCalendarCore::MemoryCalendar::Ptr CalendarController::memoryCalendar(const QString &calendarId) const 0195 { 0196 if (m_calendars.contains(calendarId)) { 0197 return m_calendars[calendarId]; 0198 } 0199 0200 return nullptr; 0201 } 0202 0203 void CalendarController::addConferenceToConfig(const QString &calendarId) 0204 { 0205 0206 const QStringList calendarLists {"conferenceCalendars", "favoritesCalendars"}; 0207 0208 for (const auto &calendarList : calendarLists) { 0209 const auto calendarName = (calendarList == "conferenceCalendars") ? calendarId : (QString {"%1_%2"}).arg("favorites", calendarId); 0210 0211 if (d->config.group("general").readEntry(calendarList, QString {}).isEmpty()) { 0212 d->config.group("general").writeEntry(calendarList, calendarName); 0213 d->config.sync(); 0214 0215 return; 0216 } 0217 0218 auto calendarsList = d->config.group("general").readEntry(calendarList, QString {}).split(";"); 0219 if (!calendarsList.contains(calendarName)) { 0220 calendarsList.append(calendarName); 0221 d->config.group("general").writeEntry(calendarList, calendarsList.join(";")); 0222 d->config.sync(); 0223 } 0224 } 0225 } 0226 0227 void CalendarController::loadSavedConferences() 0228 { 0229 auto onlineCalendarIds = d->config.group("general").readEntry("conferenceCalendars", QString {}); 0230 if (onlineCalendarIds.isEmpty()) { 0231 return; 0232 } 0233 0234 auto calendarsList = onlineCalendarIds.split(";"); 0235 0236 for (const auto &calendarId : calendarsList) { 0237 auto filePath = d->config.group(calendarId).readEntry("file", QString {}); 0238 QFile calendarFile {filePath}; 0239 0240 if (!calendarFile.exists()) { 0241 continue; 0242 } 0243 0244 auto tz = tzIdFromConfig(calendarId); 0245 KCalendarCore::MemoryCalendar::Ptr calendar {new KCalendarCore::MemoryCalendar {tz}}; 0246 KCalendarCore::FileStorage::Ptr storage {new KCalendarCore::FileStorage {calendar}}; 0247 storage->setFileName(filePath); 0248 0249 if (storage->load()) { 0250 m_storages[calendarId] = storage; 0251 m_calendars[calendarId] = calendar; 0252 0253 qDebug() << "Calendar " << calendarId << " loaded succesfully"; 0254 } 0255 } 0256 } 0257 0258 void CalendarController::addTzIdToConfig(const QString &calendarId, const QByteArray &timeZoneId) 0259 { 0260 if (!(d->config.hasGroup(calendarId))) { 0261 return; 0262 } 0263 0264 d->config.group(calendarId).writeEntry("timeZoneId", timeZoneId); 0265 d->config.sync(); 0266 } 0267 0268 QByteArray CalendarController::tzIdFromConfig(const QString &calendarId) const 0269 { 0270 if (!(d->config.hasGroup(calendarId))) { 0271 return QByteArray {}; 0272 } 0273 0274 if (d->config.hasGroup(calendarId) && !(d->config.group(calendarId).hasKey("timeZoneId"))) { 0275 return QByteArray {}; 0276 } 0277 0278 return d->config.group(calendarId).readEntry("timeZoneId").toUtf8(); 0279 } 0280 0281 QVariantMap CalendarController::exportData(const QString &calendarName, LocalCalendar *sourceCalendar) 0282 { 0283 auto sourceEvents = sourceCalendar->memorycalendar()->rawEvents(); 0284 0285 auto dirPath = QStandardPaths::writableLocation(QStandardPaths::DownloadLocation); 0286 QFile targetFile {dirPath + "/kongress_favorites_" + calendarName + ".ics"}; 0287 auto fileSuffix {1}; 0288 while (targetFile.exists()) { 0289 targetFile.setFileName(dirPath + "/kongress_favorites_" + calendarName + "(" + QString::number(fileSuffix++) + ").ics"); 0290 } 0291 KCalendarCore::Calendar::Ptr targetCalendar {new KCalendarCore::MemoryCalendar(QTimeZone::systemTimeZoneId())}; 0292 KCalendarCore::FileStorage::Ptr targetStorage {new KCalendarCore::FileStorage {targetCalendar}}; 0293 targetStorage->setFileName(targetFile.fileName()); 0294 0295 for (const auto &event : std::as_const(sourceEvents)) { 0296 targetCalendar->addEvent(event); 0297 } 0298 0299 if (!(targetStorage->save())) { 0300 return { 0301 { "success", false }, 0302 { "reason", i18n("Cannot save calendar file. Export failed.") } 0303 }; 0304 } 0305 0306 return { 0307 { "success", true }, 0308 { "reason", i18n("Export completed successfully") }, 0309 { "targetFolder", QUrl {QStringLiteral("file://") + dirPath} } 0310 }; 0311 } 0312 0313 #include "moc_calendarcontroller.cpp"