File indexing completed on 2024-05-12 05:11:10

0001 /*
0002     SPDX-FileCopyrightText: 2010 Klarälvdalens Datakonsult AB, a KDAB Group company, <info@kdab.com>
0003     SPDX-FileCopyrightText: 2010 Andras Mantia <andras@kdab.com>
0004 
0005     SPDX-License-Identifier: LGPL-2.1-or-later
0006 */
0007 
0008 #include "markascommand.h"
0009 #include "markascommandhelper_p.h"
0010 #include "util_p.h"
0011 #include <Akonadi/CollectionFetchJob>
0012 #include <Akonadi/ItemFetchJob>
0013 #include <Akonadi/ItemFetchScope>
0014 #include <Akonadi/ItemModifyJob>
0015 
0016 #include <KLocalizedString>
0017 #include <KMessageBox>
0018 
0019 using namespace Akonadi;
0020 
0021 class Akonadi::MarkAsCommandPrivate
0022 {
0023 public:
0024     MarkAsCommandPrivate() = default;
0025 
0026     Akonadi::Collection::List mFolders;
0027     Akonadi::Item::List mMessages;
0028     Akonadi::MessageStatus mTargetStatus;
0029     int mMarkJobCount = 0;
0030     int mFolderListJobCount = 0;
0031     int mInvertMark = 0;
0032     bool mRecursive = false;
0033 };
0034 
0035 MarkAsCommand::MarkAsCommand(Akonadi::MessageStatus targetStatus, const Akonadi::Item::List &msgList, bool invert, QObject *parent)
0036     : CommandBase(parent)
0037     , d(new Akonadi::MarkAsCommandPrivate())
0038 {
0039     d->mInvertMark = invert;
0040     d->mMessages = msgList;
0041     d->mTargetStatus = targetStatus;
0042     d->mFolderListJobCount = 0;
0043     d->mMarkJobCount = 0;
0044 }
0045 
0046 MarkAsCommand::MarkAsCommand(Akonadi::MessageStatus targetStatus, const Akonadi::Collection::List &folders, bool invert, bool recursive, QObject *parent)
0047     : CommandBase(parent)
0048     , d(new Akonadi::MarkAsCommandPrivate())
0049 {
0050     d->mInvertMark = invert;
0051     d->mFolders = folders;
0052     d->mTargetStatus = targetStatus;
0053     d->mFolderListJobCount = d->mFolders.size();
0054     d->mMarkJobCount = 0;
0055     d->mRecursive = recursive;
0056 }
0057 
0058 MarkAsCommand::~MarkAsCommand() = default;
0059 
0060 void MarkAsCommand::slotCollectionFetchDone(KJob *job)
0061 {
0062     if (job->error()) {
0063         Util::showJobError(job);
0064         emitResult(Failed);
0065         return;
0066     }
0067 
0068     auto fjob = static_cast<Akonadi::CollectionFetchJob *>(job);
0069     d->mFolders += fjob->collections();
0070     d->mFolderListJobCount = d->mFolders.size();
0071 
0072     // We have the subtree now so act as if we were passed the collections in ctor
0073     d->mRecursive = false;
0074     execute();
0075 }
0076 
0077 void MarkAsCommand::slotFetchDone(KJob *job)
0078 {
0079     d->mFolderListJobCount--;
0080 
0081     if (job->error()) {
0082         // handle errors
0083         Util::showJobError(job);
0084         emitResult(Failed);
0085         return;
0086     }
0087 
0088     auto fjob = static_cast<Akonadi::ItemFetchJob *>(job);
0089     d->mMessages.clear();
0090     const auto items = fjob->items();
0091     for (const Akonadi::Item &item : items) {
0092         Akonadi::MessageStatus status;
0093         status.setStatusFromFlags(item.flags());
0094         if (d->mInvertMark) {
0095             if (status & d->mTargetStatus) {
0096                 d->mMessages.append(item);
0097             }
0098         } else if (!(status & d->mTargetStatus)) {
0099             d->mMessages.append(item);
0100         }
0101     }
0102     if (d->mMessages.empty()) {
0103         if (d->mFolderListJobCount == 0) {
0104             emitResult(OK);
0105             return;
0106         }
0107     } else {
0108         markMessages();
0109     }
0110     if (d->mFolderListJobCount > 0) {
0111         auto fetchJob = new Akonadi::ItemFetchJob(d->mFolders[d->mFolderListJobCount - 1], parent());
0112         fetchJob->fetchScope().setAncestorRetrieval(Akonadi::ItemFetchScope::Parent);
0113         connect(fetchJob, &Akonadi::ItemFetchJob::result, this, &MarkAsCommand::slotFetchDone);
0114     }
0115 }
0116 
0117 void MarkAsCommand::execute()
0118 {
0119     if (d->mRecursive && !d->mFolders.isEmpty()) {
0120         if (KMessageBox::questionTwoActions(qobject_cast<QWidget *>(parent()),
0121                                             i18n("Are you sure you want to mark all messages in this folder and all its subfolders?"),
0122                                             i18n("Mark All Recursively"),
0123                                             KGuiItem(i18nc("@action:button", "Mark All")),
0124                                             KStandardGuiItem::cancel())
0125             == KMessageBox::ButtonCode::PrimaryAction) {
0126             auto job = new Akonadi::CollectionFetchJob(d->mFolders.constFirst());
0127             connect(job, &Akonadi::CollectionFetchJob::result, this, &MarkAsCommand::slotCollectionFetchDone);
0128         } else {
0129             emitResult(Canceled);
0130         }
0131     } else if (!d->mFolders.isEmpty()) {
0132         // yes, we go backwards, shouldn't matter
0133         auto job = new Akonadi::ItemFetchJob(d->mFolders[d->mFolderListJobCount - 1], parent());
0134         job->fetchScope().setAncestorRetrieval(Akonadi::ItemFetchScope::Parent);
0135         connect(job, &Akonadi::ItemFetchJob::result, this, &MarkAsCommand::slotFetchDone);
0136     } else if (!d->mMessages.isEmpty()) {
0137         d->mFolders << d->mMessages.first().parentCollection();
0138         markMessages();
0139     } else {
0140         emitResult(OK);
0141     }
0142 }
0143 
0144 void MarkAsCommand::markMessages()
0145 {
0146     d->mMarkJobCount = 0;
0147 
0148     QSet<QByteArray> flags = d->mTargetStatus.statusFlags();
0149     Q_ASSERT(flags.size() == 1);
0150     Akonadi::Item::Flag flag;
0151     if (!flags.isEmpty()) {
0152         flag = *(flags.begin());
0153     }
0154 
0155     Akonadi::Item::List itemsToModify;
0156     for (const Akonadi::Item &it : std::as_const(d->mMessages)) {
0157         Akonadi::Item item(it);
0158 
0159         // be careful to only change the flags we want to change, not to overwrite them
0160         // otherwise ItemModifyJob will not do what we expect
0161         if (d->mInvertMark) {
0162             if (item.hasFlag(flag)) {
0163                 item.clearFlag(flag);
0164                 itemsToModify.push_back(item);
0165             }
0166         } else {
0167             if (!item.hasFlag(flag)) {
0168                 item.setFlag(flag);
0169                 itemsToModify.push_back(item);
0170             }
0171         }
0172     }
0173 
0174     d->mMarkJobCount++;
0175     if (itemsToModify.isEmpty()) {
0176         slotModifyItemDone(); // pretend we did something
0177     } else {
0178         auto helper = new Akonadi::MarkAsCommandHelper(this);
0179         helper->setItemsToModify(itemsToModify);
0180         connect(helper, &Akonadi::MarkAsCommandHelper::emitResult, this, &MarkAsCommand::slotHelperDone);
0181         helper->start();
0182     }
0183 }
0184 
0185 void MarkAsCommand::slotHelperDone(Akonadi::CommandBase::Result result)
0186 {
0187     d->mMarkJobCount--;
0188     if (result == Akonadi::CommandBase::Failed) {
0189         emitResult(Failed);
0190     }
0191     slotModifyItemDone();
0192 }
0193 
0194 void MarkAsCommand::slotModifyItemDone()
0195 {
0196     if (d->mMarkJobCount == 0 && d->mFolderListJobCount == 0) {
0197         emitResult(OK);
0198     }
0199 }
0200 
0201 #include "moc_markascommand.cpp"