File indexing completed on 2024-05-19 05:11:07

0001 /*
0002   SPDX-FileCopyrightText: 2013 Sérgio Martins <iamsergio@gmail.com>
0003 
0004   SPDX-License-Identifier: GPL-2.0-or-later WITH Qt-Commercial-exception-1.0
0005 */
0006 
0007 #include "backuper.h"
0008 
0009 #include <KCalendarCore/FileStorage>
0010 #include <KCalendarCore/Incidence>
0011 
0012 #include <Akonadi/CalendarUtils>
0013 #include <Akonadi/CollectionFetchJob>
0014 #include <Akonadi/CollectionFetchScope>
0015 #include <Akonadi/ItemFetchJob>
0016 #include <Akonadi/ItemFetchScope>
0017 
0018 #include <KJob>
0019 #include <KLocalizedString>
0020 #include <QDebug>
0021 #include <QTimeZone>
0022 
0023 #include <QCoreApplication>
0024 
0025 static void printOut(const QString &message)
0026 {
0027     QTextStream out(stdout);
0028     out << message << "\n";
0029 }
0030 
0031 void Backuper::emitFinished(bool success, const QString &message)
0032 {
0033     if (success) {
0034         printOut(
0035             QLatin1Char('\n')
0036             + i18np("Backup was successful. %1 incidence was saved.", "Backup was successful. %1 incidences were saved.", m_calendar->incidences().count()));
0037     } else {
0038         printOut(message);
0039     }
0040 
0041     m_calendar.clear();
0042 
0043     Q_EMIT finished(success, message);
0044     qApp->exit(success ? 0 : -1); // TODO: If we move this class to kdepimlibs, remove this
0045 }
0046 
0047 Backuper::Backuper(QObject *parent)
0048     : QObject(parent)
0049     , m_backupInProgress(false)
0050 {
0051 }
0052 
0053 void Backuper::backup(const QString &filename, const QList<Akonadi::Collection::Id> &collectionIds)
0054 {
0055     if (filename.isEmpty()) {
0056         emitFinished(false, i18n("File is empty."));
0057         return;
0058     }
0059 
0060     if (m_backupInProgress) {
0061         emitFinished(false, i18n("A backup is already in progress."));
0062         return;
0063     }
0064     printOut(i18n("Backing up your calendar data..."));
0065     m_calendar = KCalendarCore::MemoryCalendar::Ptr(new KCalendarCore::MemoryCalendar(QTimeZone::systemTimeZone()));
0066     m_requestedCollectionIds = collectionIds;
0067     m_backupInProgress = true;
0068     m_filename = filename;
0069 
0070     auto job = new Akonadi::CollectionFetchJob(Akonadi::Collection::root(), Akonadi::CollectionFetchJob::Recursive);
0071 
0072     job->fetchScope().setContentMimeTypes(KCalendarCore::Incidence::mimeTypes());
0073     connect(job, &Akonadi::CollectionFetchJob::result, this, &Backuper::onCollectionsFetched);
0074     job->start();
0075 }
0076 
0077 void Backuper::onCollectionsFetched(KJob *job)
0078 {
0079     if (job->error() == 0) {
0080         const QStringList mimetypes = KCalendarCore::Incidence::mimeTypes();
0081         QSet<QString> mimeTypeSet = QSet<QString>(mimetypes.begin(), mimetypes.end());
0082         auto cfj = qobject_cast<Akonadi::CollectionFetchJob *>(job);
0083         const auto collections = cfj->collections();
0084         for (const Akonadi::Collection &collection : collections) {
0085             if (!m_requestedCollectionIds.isEmpty() && !m_requestedCollectionIds.contains(collection.id())) {
0086                 continue;
0087             }
0088             const QStringList contentMimeTypesLst = collection.contentMimeTypes();
0089             QSet<QString> collectionMimeTypeSet = QSet<QString>(contentMimeTypesLst.begin(), contentMimeTypesLst.end());
0090             if (!mimeTypeSet.intersect(collectionMimeTypeSet).isEmpty()) {
0091                 m_collections << collection;
0092                 loadCollection(collection);
0093             }
0094         }
0095 
0096         if (m_collections.isEmpty()) {
0097             emitFinished(false, i18n("No data to backup."));
0098         }
0099     } else {
0100         qCritical() << job->errorString();
0101         m_backupInProgress = false;
0102         emitFinished(false, job->errorString());
0103     }
0104 }
0105 
0106 void Backuper::loadCollection(const Akonadi::Collection &collection)
0107 {
0108     printOut(i18n("Processing collection %1 (id=%2)...", collection.displayName(), collection.id()));
0109     auto ifj = new Akonadi::ItemFetchJob(collection, this);
0110     ifj->setProperty("collectionId", collection.id());
0111     ifj->fetchScope().fetchFullPayload(true);
0112     connect(ifj, &Akonadi::ItemFetchJob::result, this, &Backuper::onCollectionLoaded);
0113     m_pendingCollections << collection.id();
0114 }
0115 
0116 void Backuper::onCollectionLoaded(KJob *job)
0117 {
0118     if (job->error()) {
0119         m_backupInProgress = false;
0120         m_calendar.clear();
0121         emitFinished(false, job->errorString());
0122     } else {
0123         auto ifj = qobject_cast<Akonadi::ItemFetchJob *>(job);
0124         Akonadi::Collection::Id id = ifj->property("collectionId").toInt();
0125         Q_ASSERT(id != -1);
0126         const Akonadi::Item::List items = ifj->items();
0127         m_pendingCollections.removeAll(id);
0128 
0129         for (const Akonadi::Item &item : items) {
0130             KCalendarCore::Incidence::Ptr incidence = Akonadi::CalendarUtils::incidence(item);
0131             Q_ASSERT(incidence);
0132             m_calendar->addIncidence(incidence);
0133         }
0134 
0135         if (m_pendingCollections.isEmpty()) { // We're done
0136             KCalendarCore::FileStorage storage(m_calendar, m_filename);
0137             bool success = storage.save();
0138             QString message = success ? QString() : i18n("An error occurred");
0139             emitFinished(success, message);
0140         }
0141     }
0142 }
0143 
0144 #include "moc_backuper.cpp"