File indexing completed on 2024-12-22 04:55:36
0001 /* 0002 * akonadicollectionsearch.cpp - Search Akonadi Collections 0003 * Program: kalarm 0004 * SPDX-FileCopyrightText: 2014-2022 David Jarvie <djarvie@kde.org> 0005 * 0006 * SPDX-License-Identifier: GPL-2.0-or-later 0007 */ 0008 0009 #include "akonadicollectionsearch.h" 0010 0011 #include "akonadiplugin_debug.h" 0012 0013 #include <Akonadi/AgentInstance> 0014 #include <Akonadi/AgentManager> 0015 #include <Akonadi/CollectionFetchJob> 0016 #include <Akonadi/CollectionFetchScope> 0017 #include <Akonadi/ItemDeleteJob> 0018 #include <Akonadi/ItemFetchJob> 0019 #include <Akonadi/ItemFetchScope> 0020 #include <KCalendarCore/Event> 0021 using namespace KCalendarCore; 0022 0023 #include <QTimer> 0024 0025 using namespace Akonadi; 0026 0027 /****************************************************************************** 0028 * Constructor. 0029 * Creates jobs to fetch all collections for resources containing the mime type. 0030 * Its subsequent actions depend on the parameters: 0031 * - If 'remove' is true, it will locate all Items with the specified 'gid' and 0032 * delete them. The deleted() signal will be emitted. 0033 * - Otherwise, if 'gid' is specified, it will Q_EMIT the signal items() to 0034 * notify all Items with that GID. 0035 * - Otherwise, it will Q_EMIT the signal collections() to notify all Collections. 0036 */ 0037 AkonadiCollectionSearch::AkonadiCollectionSearch(const QString& mimeType, const QString& gid, const QString& uid, bool remove) 0038 : mMimeType(mimeType) 0039 , mGid(gid) 0040 , mUid(uid) 0041 , mDelete(remove && (!mGid.isEmpty() || !mUid.isEmpty())) 0042 { 0043 const AgentInstance::List agents = AgentManager::self()->instances(); 0044 for (const AgentInstance& agent : agents) 0045 { 0046 if (agent.type().mimeTypes().contains(mimeType)) 0047 { 0048 CollectionFetchJob* job = new CollectionFetchJob(Collection::root(), CollectionFetchJob::Recursive); 0049 job->fetchScope().setResource(agent.identifier()); 0050 mCollectionJobs << job; 0051 connect(job, &CollectionFetchJob::result, this, &AkonadiCollectionSearch::collectionFetchResult); 0052 } 0053 } 0054 0055 if (mCollectionJobs.isEmpty()) 0056 { 0057 // There are no resources containing the mime type, so ensure that a 0058 // signal is emitted after construction. 0059 QTimer::singleShot(0, this, &AkonadiCollectionSearch::finish); //NOLINT(clang-analyzer-cplusplus.NewDeleteLeaks) 0060 } 0061 } 0062 0063 /****************************************************************************** 0064 * Called when a CollectionFetchJob has completed. 0065 */ 0066 void AkonadiCollectionSearch::collectionFetchResult(KJob* j) 0067 { 0068 auto job = qobject_cast<CollectionFetchJob*>(j); 0069 if (j->error()) 0070 qCCritical(AKONADIPLUGIN_LOG) << "AkonadiCollectionSearch::collectionFetchResult: CollectionFetchJob" << job->fetchScope().resource()<< "error: " << j->errorString(); 0071 else 0072 { 0073 const Collection::List collections = job->collections(); 0074 for (const Collection& c : collections) 0075 { 0076 if (c.contentMimeTypes().contains(mMimeType)) 0077 { 0078 ItemFetchJob* ijob; 0079 if (!mGid.isEmpty()) 0080 { 0081 // Search for all Items with the specified GID 0082 Item item; 0083 item.setGid(mGid); 0084 ijob = new ItemFetchJob(item, this); 0085 ijob->setCollection(c); 0086 } 0087 else if (!mUid.isEmpty()) 0088 { 0089 // Search for all Events with the specified UID 0090 ijob = new ItemFetchJob(c, this); 0091 ijob->fetchScope().fetchFullPayload(true); 0092 } 0093 else 0094 { 0095 mCollections << c; 0096 continue; 0097 } 0098 mItemFetchJobs[ijob] = c.id(); 0099 connect(ijob, &ItemFetchJob::result, this, &AkonadiCollectionSearch::itemFetchResult); 0100 } 0101 } 0102 } 0103 mCollectionJobs.removeAll(job); 0104 0105 if (mCollectionJobs.isEmpty()) 0106 { 0107 // All collections have now been fetched 0108 if (mGid.isEmpty() && mUid.isEmpty()) 0109 finish(); 0110 } 0111 } 0112 0113 /****************************************************************************** 0114 * Called when an ItemFetchJob has completed. 0115 */ 0116 void AkonadiCollectionSearch::itemFetchResult(KJob* j) 0117 { 0118 auto job = qobject_cast<ItemFetchJob*>(j); 0119 if (j->error()) 0120 { 0121 if (!mUid.isEmpty()) 0122 qCDebug(AKONADIPLUGIN_LOG) << "AkonadiCollectionSearch::itemFetchResult: ItemFetchJob: collection" << mItemFetchJobs[job] << "UID" << mUid << "error: " << j->errorString(); 0123 else 0124 qCDebug(AKONADIPLUGIN_LOG) << "AkonadiCollectionSearch::itemFetchResult: ItemFetchJob: collection" << mItemFetchJobs[job] << "GID" << mGid << "error: " << j->errorString(); 0125 } 0126 else 0127 { 0128 if (mDelete) 0129 { 0130 const Item::List items = job->items(); 0131 for (const Item& item : items) 0132 { 0133 if (!mUid.isEmpty()) 0134 { 0135 if (item.mimeType() == mMimeType && item.hasPayload<KCalendarCore::Event::Ptr>()) 0136 { 0137 const KCalendarCore::Event::Ptr kcalEvent = item.payload<KCalendarCore::Event::Ptr>(); 0138 if (kcalEvent->uid() != mUid) 0139 continue; 0140 } 0141 } 0142 else if (mGid.isEmpty()) 0143 continue; 0144 auto djob = new ItemDeleteJob(item, this); 0145 mItemDeleteJobs[djob] = mItemFetchJobs.value(job); 0146 connect(djob, &ItemDeleteJob::result, this, &AkonadiCollectionSearch::itemDeleteResult); 0147 } 0148 } 0149 else 0150 mItems << job->items(); 0151 } 0152 mItemFetchJobs.remove(job); 0153 0154 if (mItemFetchJobs.isEmpty() && mItemDeleteJobs.isEmpty() && mCollectionJobs.isEmpty()) 0155 finish(); // all Items have now been fetched or deleted, so notify the result 0156 } 0157 0158 /****************************************************************************** 0159 * Called when an ItemDeleteJob has completed. 0160 */ 0161 void AkonadiCollectionSearch::itemDeleteResult(KJob* j) 0162 { 0163 auto job = static_cast<ItemDeleteJob*>(j); 0164 if (j->error()) 0165 { 0166 if (!mUid.isEmpty()) 0167 qCDebug(AKONADIPLUGIN_LOG) << "AkonadiCollectionSearch::itemDeleteResult: ItemDeleteJob: resource" << mItemDeleteJobs[job] << "UID" << mUid << "error: " << j->errorString(); 0168 else 0169 qCDebug(AKONADIPLUGIN_LOG) << "AkonadiCollectionSearch::itemDeleteResult: ItemDeleteJob: resource" << mItemDeleteJobs[job] << "GID" << mGid << "error: " << j->errorString(); 0170 } 0171 else 0172 ++mDeleteCount; 0173 mItemDeleteJobs.remove(job); 0174 0175 if (mItemFetchJobs.isEmpty() && mItemDeleteJobs.isEmpty() && mCollectionJobs.isEmpty()) 0176 finish(); // all Items have now been deleted, so notify the result 0177 } 0178 0179 /****************************************************************************** 0180 * Notify the result of the search/delete operation, and delete this instance. 0181 */ 0182 void AkonadiCollectionSearch::finish() 0183 { 0184 if (mDelete) 0185 Q_EMIT deleted(mDeleteCount); 0186 else if (mGid.isEmpty() && mUid.isEmpty()) 0187 Q_EMIT collections(mCollections); 0188 else 0189 Q_EMIT items(mItems); 0190 deleteLater(); 0191 } 0192 0193 #include "moc_akonadicollectionsearch.cpp" 0194 0195 // vim: et sw=4: