File indexing completed on 2024-11-10 04:40:45
0001 /* 0002 SPDX-FileCopyrightText: 2009 Constantin Berzan <exit3219@gmail.com> 0003 0004 SPDX-License-Identifier: LGPL-2.0-or-later 0005 */ 0006 0007 #include "specialcollections.h" 0008 #include "akonadicore_debug.h" 0009 #include "specialcollectionattribute.h" 0010 #include "specialcollections_p.h" 0011 0012 #include "agentinstance.h" 0013 #include "agentmanager.h" 0014 #include "collectionfetchjob.h" 0015 #include "collectionfetchscope.h" 0016 #include "collectionmodifyjob.h" 0017 #include "monitor.h" 0018 0019 #include <KCoreConfigSkeleton> 0020 0021 #include <QHash> 0022 0023 using namespace Akonadi; 0024 0025 SpecialCollectionsPrivate::SpecialCollectionsPrivate(KCoreConfigSkeleton *settings, SpecialCollections *qq) 0026 : q(qq) 0027 , mSettings(settings) 0028 , mBatchMode(false) 0029 { 0030 mMonitor = new Monitor(q); 0031 mMonitor->setObjectName(QLatin1StringView("SpecialCollectionsMonitor")); 0032 mMonitor->fetchCollectionStatistics(true); 0033 0034 /// In order to know if items are added or deleted 0035 /// from one of our specialcollection folders, 0036 /// we have to watch all mail item add/move/delete notifications 0037 /// and check for the parent to see if it is one we care about 0038 QObject::connect(mMonitor, &Monitor::collectionRemoved, q, [this](const Akonadi::Collection &col) { 0039 collectionRemoved(col); 0040 }); 0041 QObject::connect(mMonitor, &Monitor::collectionStatisticsChanged, q, [this](Akonadi::Collection::Id id, const Akonadi::CollectionStatistics &statistics) { 0042 collectionStatisticsChanged(id, statistics); 0043 }); 0044 } 0045 0046 SpecialCollectionsPrivate::~SpecialCollectionsPrivate() 0047 { 0048 } 0049 0050 QString SpecialCollectionsPrivate::defaultResourceId() const 0051 { 0052 if (mDefaultResourceId.isEmpty()) { 0053 mSettings->load(); 0054 const KConfigSkeletonItem *item = mSettings->findItem(QStringLiteral("DefaultResourceId")); 0055 Q_ASSERT(item); 0056 0057 mDefaultResourceId = item->property().toString(); 0058 } 0059 return mDefaultResourceId; 0060 } 0061 0062 void SpecialCollectionsPrivate::emitChanged(const QString &resourceId) 0063 { 0064 if (mBatchMode) { 0065 mToEmitChangedFor.insert(resourceId); 0066 } else { 0067 qCDebug(AKONADICORE_LOG) << "Emitting changed for" << resourceId; 0068 const AgentInstance agentInstance = AgentManager::self()->instance(resourceId); 0069 Q_EMIT q->collectionsChanged(agentInstance); 0070 // first compare with local value then with config value (which also updates the local value) 0071 if (resourceId == mDefaultResourceId || resourceId == defaultResourceId()) { 0072 qCDebug(AKONADICORE_LOG) << "Emitting defaultFoldersChanged."; 0073 Q_EMIT q->defaultCollectionsChanged(); 0074 } 0075 } 0076 } 0077 0078 void SpecialCollectionsPrivate::collectionRemoved(const Collection &collection) 0079 { 0080 qCDebug(AKONADICORE_LOG) << "Collection" << collection.id() << "resource" << collection.resource(); 0081 if (mFoldersForResource.contains(collection.resource())) { 0082 // Retrieve the list of special folders for the resource the collection belongs to 0083 QHash<QByteArray, Collection> &folders = mFoldersForResource[collection.resource()]; 0084 { 0085 QMutableHashIterator<QByteArray, Collection> it(folders); 0086 while (it.hasNext()) { 0087 it.next(); 0088 if (it.value() == collection) { 0089 // The collection to be removed is a special folder 0090 it.remove(); 0091 emitChanged(collection.resource()); 0092 } 0093 } 0094 } 0095 0096 if (folders.isEmpty()) { 0097 // This resource has no more folders, so remove it completely. 0098 mFoldersForResource.remove(collection.resource()); 0099 } 0100 } 0101 } 0102 0103 void SpecialCollectionsPrivate::collectionStatisticsChanged(Akonadi::Collection::Id collectionId, const Akonadi::CollectionStatistics &statistics) 0104 { 0105 // need to get the name of the collection in order to be able to check if we are storing it, 0106 // but we have the id from the monitor, so fetch the name. 0107 auto fetchJob = new Akonadi::CollectionFetchJob(Collection(collectionId), Akonadi::CollectionFetchJob::Base); 0108 fetchJob->fetchScope().setAncestorRetrieval(Akonadi::CollectionFetchScope::None); 0109 fetchJob->setProperty("statistics", QVariant::fromValue(statistics)); 0110 0111 q->connect(fetchJob, &CollectionFetchJob::result, q, [this](KJob *job) { 0112 collectionFetchJobFinished(job); 0113 }); 0114 } 0115 0116 void SpecialCollectionsPrivate::collectionFetchJobFinished(KJob *job) 0117 { 0118 if (job->error()) { 0119 qCWarning(AKONADICORE_LOG) << "Error fetching collection to get name from id for statistics updating in specialcollections!"; 0120 return; 0121 } 0122 0123 const Akonadi::CollectionFetchJob *fetchJob = qobject_cast<Akonadi::CollectionFetchJob *>(job); 0124 0125 Q_ASSERT(!fetchJob->collections().empty()); 0126 const Akonadi::Collection collection = fetchJob->collections().at(0); 0127 const auto statistics = fetchJob->property("statistics").value<Akonadi::CollectionStatistics>(); 0128 0129 mFoldersForResource[collection.resource()][collection.name().toUtf8()].setStatistics(statistics); 0130 } 0131 0132 void SpecialCollectionsPrivate::beginBatchRegister() 0133 { 0134 Q_ASSERT(!mBatchMode); 0135 mBatchMode = true; 0136 Q_ASSERT(mToEmitChangedFor.isEmpty()); 0137 } 0138 0139 void SpecialCollectionsPrivate::endBatchRegister() 0140 { 0141 Q_ASSERT(mBatchMode); 0142 mBatchMode = false; 0143 0144 for (const QString &resourceId : std::as_const(mToEmitChangedFor)) { 0145 emitChanged(resourceId); 0146 } 0147 0148 mToEmitChangedFor.clear(); 0149 } 0150 0151 void SpecialCollectionsPrivate::forgetFoldersForResource(const QString &resourceId) 0152 { 0153 if (mFoldersForResource.contains(resourceId)) { 0154 const auto folders = mFoldersForResource[resourceId]; 0155 for (const auto &collection : folders) { 0156 mMonitor->setCollectionMonitored(collection, false); 0157 } 0158 0159 mFoldersForResource.remove(resourceId); 0160 emitChanged(resourceId); 0161 } 0162 } 0163 0164 AgentInstance SpecialCollectionsPrivate::defaultResource() const 0165 { 0166 const QString identifier = defaultResourceId(); 0167 return AgentManager::self()->instance(identifier); 0168 } 0169 0170 SpecialCollections::SpecialCollections(KCoreConfigSkeleton *settings, QObject *parent) 0171 : QObject(parent) 0172 , d(new SpecialCollectionsPrivate(settings, this)) 0173 { 0174 } 0175 0176 SpecialCollections::~SpecialCollections() = default; 0177 0178 bool SpecialCollections::hasCollection(const QByteArray &type, const AgentInstance &instance) const 0179 { 0180 return d->mFoldersForResource.value(instance.identifier()).contains(type); 0181 } 0182 0183 Akonadi::Collection SpecialCollections::collection(const QByteArray &type, const AgentInstance &instance) const 0184 { 0185 return d->mFoldersForResource.value(instance.identifier()).value(type); 0186 } 0187 0188 void SpecialCollections::setSpecialCollectionType(const QByteArray &type, const Akonadi::Collection &collection) 0189 { 0190 if (!collection.hasAttribute<SpecialCollectionAttribute>() || collection.attribute<SpecialCollectionAttribute>()->collectionType() != type) { 0191 Collection attributeCollection(collection); 0192 auto attribute = attributeCollection.attribute<SpecialCollectionAttribute>(Collection::AddIfMissing); 0193 attribute->setCollectionType(type); 0194 new CollectionModifyJob(attributeCollection); 0195 } 0196 } 0197 0198 void SpecialCollections::unsetSpecialCollection(const Akonadi::Collection &collection) 0199 { 0200 if (collection.hasAttribute<SpecialCollectionAttribute>()) { 0201 Collection attributeCollection(collection); 0202 attributeCollection.removeAttribute<SpecialCollectionAttribute>(); 0203 new CollectionModifyJob(attributeCollection); 0204 } 0205 } 0206 0207 bool SpecialCollections::unregisterCollection(const Collection &collection) 0208 { 0209 if (!collection.isValid()) { 0210 qCWarning(AKONADICORE_LOG) << "Invalid collection."; 0211 return false; 0212 } 0213 0214 const QString &resourceId = collection.resource(); 0215 if (resourceId.isEmpty()) { 0216 qCWarning(AKONADICORE_LOG) << "Collection has empty resourceId."; 0217 return false; 0218 } 0219 0220 unsetSpecialCollection(collection); 0221 0222 d->mMonitor->setCollectionMonitored(collection, false); 0223 // Remove from list of collection 0224 d->collectionRemoved(collection); 0225 return true; 0226 } 0227 0228 bool SpecialCollections::registerCollection(const QByteArray &type, const Collection &collection) 0229 { 0230 if (!collection.isValid()) { 0231 qCWarning(AKONADICORE_LOG) << "Invalid collection."; 0232 return false; 0233 } 0234 0235 const QString &resourceId = collection.resource(); 0236 if (resourceId.isEmpty()) { 0237 qCWarning(AKONADICORE_LOG) << "Collection has empty resourceId."; 0238 return false; 0239 } 0240 0241 setSpecialCollectionType(type, collection); 0242 0243 const Collection oldCollection = d->mFoldersForResource.value(resourceId).value(type); 0244 if (oldCollection != collection) { 0245 if (oldCollection.isValid()) { 0246 d->mMonitor->setCollectionMonitored(oldCollection, false); 0247 } 0248 d->mMonitor->setCollectionMonitored(collection, true); 0249 d->mFoldersForResource[resourceId].insert(type, collection); 0250 d->emitChanged(resourceId); 0251 } 0252 0253 return true; 0254 } 0255 0256 bool SpecialCollections::hasDefaultCollection(const QByteArray &type) const 0257 { 0258 return hasCollection(type, d->defaultResource()); 0259 } 0260 0261 Akonadi::Collection SpecialCollections::defaultCollection(const QByteArray &type) const 0262 { 0263 return collection(type, d->defaultResource()); 0264 } 0265 0266 #include "moc_specialcollections.cpp"