File indexing completed on 2024-11-10 04:40:32
0001 /* 0002 SPDX-FileCopyrightText: 2009 Constantin Berzan <exit3219@gmail.com> 0003 0004 SPDX-License-Identifier: LGPL-2.0-or-later 0005 */ 0006 0007 #include "specialcollectionsrequestjob.h" 0008 0009 #include "specialcollectionattribute.h" 0010 #include "specialcollections_p.h" 0011 #include "specialcollectionshelperjobs_p.h" 0012 0013 #include "agentmanager.h" 0014 #include "collectioncreatejob.h" 0015 0016 #include "akonadicore_debug.h" 0017 0018 using namespace Akonadi; 0019 0020 /** 0021 @internal 0022 */ 0023 class Akonadi::SpecialCollectionsRequestJobPrivate 0024 { 0025 public: 0026 SpecialCollectionsRequestJobPrivate(SpecialCollections *collections, SpecialCollectionsRequestJob *qq); 0027 0028 bool isEverythingReady() const; 0029 void lockResult(KJob *job); // slot 0030 void releaseLock(); // slot 0031 void nextResource(); 0032 void resourceScanResult(KJob *job); // slot 0033 void createRequestedFolders(ResourceScanJob *job, QHash<QByteArray, bool> &requestedFolders); 0034 void collectionCreateResult(KJob *job); // slot 0035 0036 SpecialCollectionsRequestJob *const q; 0037 SpecialCollections *mSpecialCollections = nullptr; 0038 int mPendingCreateJobs = 0; 0039 0040 QByteArray mRequestedType; 0041 AgentInstance mRequestedResource; 0042 0043 // Input: 0044 QHash<QByteArray, bool> mDefaultFolders; 0045 bool mRequestingDefaultFolders = false; 0046 QHash<QString, QHash<QByteArray, bool>> mFoldersForResource; 0047 QString mDefaultResourceType; 0048 QVariantMap mDefaultResourceOptions; 0049 QList<QByteArray> mKnownTypes; 0050 QMap<QByteArray, QString> mNameForTypeMap; 0051 QMap<QByteArray, QString> mIconForTypeMap; 0052 0053 // Output: 0054 QStringList mToForget; 0055 QList<QPair<Collection, QByteArray>> mToRegister; 0056 }; 0057 0058 SpecialCollectionsRequestJobPrivate::SpecialCollectionsRequestJobPrivate(SpecialCollections *collections, SpecialCollectionsRequestJob *qq) 0059 : q(qq) 0060 , mSpecialCollections(collections) 0061 { 0062 } 0063 0064 bool SpecialCollectionsRequestJobPrivate::isEverythingReady() const 0065 { 0066 // check if all requested folders are known already 0067 if (mRequestingDefaultFolders) { 0068 for (auto it = mDefaultFolders.cbegin(), end = mDefaultFolders.cend(); it != end; ++it) { 0069 if (it.value() && !mSpecialCollections->hasDefaultCollection(it.key())) { 0070 return false; 0071 } 0072 } 0073 } 0074 0075 for (auto resourceIt = mFoldersForResource.cbegin(), end = mFoldersForResource.cend(); resourceIt != end; ++resourceIt) { 0076 const QHash<QByteArray, bool> &requested = resourceIt.value(); 0077 for (auto it = requested.cbegin(), end = requested.cend(); it != end; ++it) { 0078 if (it.value() && !mSpecialCollections->hasCollection(it.key(), AgentManager::self()->instance(resourceIt.key()))) { 0079 return false; 0080 } 0081 } 0082 } 0083 0084 return true; 0085 } 0086 0087 void SpecialCollectionsRequestJobPrivate::lockResult(KJob *job) 0088 { 0089 if (job->error()) { 0090 qCWarning(AKONADICORE_LOG) << "Failed to get lock:" << job->errorString(); 0091 q->setError(job->error()); 0092 q->setErrorText(job->errorString()); 0093 q->emitResult(); 0094 return; 0095 } 0096 0097 if (mRequestingDefaultFolders) { 0098 // If default folders are requested, deal with that first. 0099 auto resjob = new DefaultResourceJob(mSpecialCollections->d->mSettings, q); 0100 resjob->setDefaultResourceType(mDefaultResourceType); 0101 resjob->setDefaultResourceOptions(mDefaultResourceOptions); 0102 resjob->setTypes(mKnownTypes); 0103 resjob->setNameForTypeMap(mNameForTypeMap); 0104 resjob->setIconForTypeMap(mIconForTypeMap); 0105 QObject::connect(resjob, &KJob::result, q, [this](KJob *job) { 0106 resourceScanResult(job); 0107 }); 0108 } else { 0109 // If no default folders are requested, go straight to the next step. 0110 nextResource(); 0111 } 0112 } 0113 0114 void SpecialCollectionsRequestJobPrivate::releaseLock() 0115 { 0116 const bool ok = Akonadi::releaseLock(); 0117 if (!ok) { 0118 qCWarning(AKONADICORE_LOG) << "WTF, can't release lock."; 0119 } 0120 } 0121 0122 void SpecialCollectionsRequestJobPrivate::nextResource() 0123 { 0124 if (mFoldersForResource.isEmpty()) { 0125 qCDebug(AKONADICORE_LOG) << "All done! Committing."; 0126 0127 mSpecialCollections->d->beginBatchRegister(); 0128 0129 // Forget everything we knew before about these resources. 0130 for (const QString &resourceId : std::as_const(mToForget)) { 0131 mSpecialCollections->d->forgetFoldersForResource(resourceId); 0132 } 0133 0134 // Register all the collections that we fetched / created. 0135 using RegisterPair = QPair<Collection, QByteArray>; 0136 for (const RegisterPair &pair : std::as_const(mToRegister)) { 0137 const bool ok = mSpecialCollections->registerCollection(pair.second, pair.first); 0138 Q_ASSERT(ok); 0139 Q_UNUSED(ok) 0140 } 0141 0142 mSpecialCollections->d->endBatchRegister(); 0143 0144 // Release the lock once the transaction has been committed. 0145 QObject::connect(q, &KJob::result, q, [this]() { 0146 releaseLock(); 0147 }); 0148 0149 // We are done! 0150 q->commit(); 0151 0152 } else { 0153 const QString resourceId = mFoldersForResource.cbegin().key(); 0154 qCDebug(AKONADICORE_LOG) << "A resource is done," << mFoldersForResource.count() << "more to do. Now doing resource" << resourceId; 0155 auto resjob = new ResourceScanJob(resourceId, mSpecialCollections->d->mSettings, q); 0156 QObject::connect(resjob, &KJob::result, q, [this](KJob *job) { 0157 resourceScanResult(job); 0158 }); 0159 } 0160 } 0161 0162 void SpecialCollectionsRequestJobPrivate::resourceScanResult(KJob *job) 0163 { 0164 auto resjob = qobject_cast<ResourceScanJob *>(job); 0165 Q_ASSERT(resjob); 0166 0167 const QString resourceId = resjob->resourceId(); 0168 qCDebug(AKONADICORE_LOG) << "resourceId" << resourceId; 0169 0170 if (job->error()) { 0171 qCWarning(AKONADICORE_LOG) << "Failed to request resource" << resourceId << ":" << job->errorString(); 0172 return; 0173 } 0174 0175 if (qobject_cast<DefaultResourceJob *>(job)) { 0176 // This is the default resource. 0177 if (resourceId != mSpecialCollections->d->defaultResourceId()) { 0178 qCWarning(AKONADICORE_LOG) << "Resource id's don't match: " << resourceId << mSpecialCollections->d->defaultResourceId(); 0179 Q_ASSERT(false); 0180 } 0181 // mToForget.append( mSpecialCollections->defaultResourceId() ); 0182 createRequestedFolders(resjob, mDefaultFolders); 0183 } else { 0184 // This is not the default resource. 0185 QHash<QByteArray, bool> requestedFolders = mFoldersForResource[resourceId]; 0186 mFoldersForResource.remove(resourceId); 0187 createRequestedFolders(resjob, requestedFolders); 0188 } 0189 } 0190 0191 void SpecialCollectionsRequestJobPrivate::createRequestedFolders(ResourceScanJob *scanJob, QHash<QByteArray, bool> &requestedFolders) 0192 { 0193 // Remove from the request list the folders which already exist. 0194 const Akonadi::Collection::List lstSpecialCols = scanJob->specialCollections(); 0195 for (const Collection &collection : lstSpecialCols) { 0196 Q_ASSERT(collection.hasAttribute<SpecialCollectionAttribute>()); 0197 const auto attr = collection.attribute<SpecialCollectionAttribute>(); 0198 const QByteArray type = attr->collectionType(); 0199 0200 if (!type.isEmpty()) { 0201 mToRegister.append(qMakePair(collection, type)); 0202 requestedFolders.insert(type, false); 0203 } 0204 } 0205 mToForget.append(scanJob->resourceId()); 0206 0207 // Folders left in the request list must be created. 0208 Q_ASSERT(mPendingCreateJobs == 0); 0209 Q_ASSERT(scanJob->rootResourceCollection().isValid()); 0210 0211 QHashIterator<QByteArray, bool> it(requestedFolders); 0212 while (it.hasNext()) { 0213 it.next(); 0214 0215 if (it.value()) { 0216 Collection collection; 0217 collection.setParentCollection(scanJob->rootResourceCollection()); 0218 collection.setName(mNameForTypeMap.value(it.key())); 0219 0220 setCollectionAttributes(collection, it.key(), mNameForTypeMap, mIconForTypeMap); 0221 0222 auto createJob = new CollectionCreateJob(collection, q); 0223 createJob->setProperty("type", it.key()); 0224 QObject::connect(createJob, &KJob::result, q, [this](KJob *job) { 0225 collectionCreateResult(job); 0226 }); 0227 0228 mPendingCreateJobs++; 0229 } 0230 } 0231 0232 if (mPendingCreateJobs == 0) { 0233 nextResource(); 0234 } 0235 } 0236 0237 void SpecialCollectionsRequestJobPrivate::collectionCreateResult(KJob *job) 0238 { 0239 if (job->error()) { 0240 qCWarning(AKONADICORE_LOG) << "Failed CollectionCreateJob." << job->errorString(); 0241 return; 0242 } 0243 0244 auto createJob = qobject_cast<CollectionCreateJob *>(job); 0245 Q_ASSERT(createJob); 0246 0247 const Collection collection = createJob->collection(); 0248 mToRegister.append(qMakePair(collection, createJob->property("type").toByteArray())); 0249 0250 Q_ASSERT(mPendingCreateJobs > 0); 0251 mPendingCreateJobs--; 0252 qCDebug(AKONADICORE_LOG) << "mPendingCreateJobs now" << mPendingCreateJobs; 0253 0254 if (mPendingCreateJobs == 0) { 0255 nextResource(); 0256 } 0257 } 0258 0259 // TODO KDE5: do not inherit from TransactionSequence 0260 SpecialCollectionsRequestJob::SpecialCollectionsRequestJob(SpecialCollections *collections, QObject *parent) 0261 : TransactionSequence(parent) 0262 , d(new SpecialCollectionsRequestJobPrivate(collections, this)) 0263 { 0264 setProperty("transactionsDisabled", true); 0265 } 0266 0267 SpecialCollectionsRequestJob::~SpecialCollectionsRequestJob() = default; 0268 0269 void SpecialCollectionsRequestJob::requestDefaultCollection(const QByteArray &type) 0270 { 0271 d->mDefaultFolders[type] = true; 0272 d->mRequestingDefaultFolders = true; 0273 d->mRequestedType = type; 0274 } 0275 0276 void SpecialCollectionsRequestJob::requestCollection(const QByteArray &type, const AgentInstance &instance) 0277 { 0278 d->mFoldersForResource[instance.identifier()][type] = true; 0279 d->mRequestedType = type; 0280 d->mRequestedResource = instance; 0281 } 0282 0283 Akonadi::Collection SpecialCollectionsRequestJob::collection() const 0284 { 0285 if (d->mRequestedResource.isValid()) { 0286 return d->mSpecialCollections->collection(d->mRequestedType, d->mRequestedResource); 0287 } else { 0288 return d->mSpecialCollections->defaultCollection(d->mRequestedType); 0289 } 0290 } 0291 0292 void SpecialCollectionsRequestJob::setDefaultResourceType(const QString &type) 0293 { 0294 d->mDefaultResourceType = type; 0295 } 0296 0297 void SpecialCollectionsRequestJob::setDefaultResourceOptions(const QVariantMap &options) 0298 { 0299 d->mDefaultResourceOptions = options; 0300 } 0301 0302 void SpecialCollectionsRequestJob::setTypes(const QList<QByteArray> &types) 0303 { 0304 d->mKnownTypes = types; 0305 } 0306 0307 void SpecialCollectionsRequestJob::setNameForTypeMap(const QMap<QByteArray, QString> &map) 0308 { 0309 d->mNameForTypeMap = map; 0310 } 0311 0312 void SpecialCollectionsRequestJob::setIconForTypeMap(const QMap<QByteArray, QString> &map) 0313 { 0314 d->mIconForTypeMap = map; 0315 } 0316 0317 void SpecialCollectionsRequestJob::doStart() 0318 { 0319 if (d->isEverythingReady()) { 0320 emitResult(); 0321 } else { 0322 auto lockJob = new GetLockJob(this); 0323 connect(lockJob, &GetLockJob::result, this, [this](KJob *job) { 0324 d->lockResult(job); 0325 }); 0326 lockJob->start(); 0327 } 0328 } 0329 0330 void SpecialCollectionsRequestJob::slotResult(KJob *job) 0331 { 0332 if (job->error()) { 0333 // If we failed, let others try. 0334 qCWarning(AKONADICORE_LOG) << "Failed SpecialCollectionsRequestJob::slotResult" << job->errorString(); 0335 0336 d->releaseLock(); 0337 } 0338 TransactionSequence::slotResult(job); 0339 } 0340 0341 #include "moc_specialcollectionsrequestjob.cpp"