Akonadi

specialcollectionsrequestjob.cpp
1 /*
2  SPDX-FileCopyrightText: 2009 Constantin Berzan <[email protected]>
3 
4  SPDX-License-Identifier: LGPL-2.0-or-later
5 */
6 
7 #include "specialcollectionsrequestjob.h"
8 
9 #include "specialcollectionattribute.h"
10 #include "specialcollections.h"
11 #include "specialcollections_p.h"
12 #include "specialcollectionshelperjobs_p.h"
13 
14 #include "agentmanager.h"
15 #include "collectioncreatejob.h"
16 #include "entitydisplayattribute.h"
17 
18 
19 #include "akonadicore_debug.h"
20 
21 #include <QVariant>
22 
23 using namespace Akonadi;
24 
28 class Akonadi::SpecialCollectionsRequestJobPrivate
29 {
30 public:
31  SpecialCollectionsRequestJobPrivate(SpecialCollections *collections, SpecialCollectionsRequestJob *qq);
32 
33  bool isEverythingReady() const;
34  void lockResult(KJob *job); // slot
35  void releaseLock(); // slot
36  void nextResource();
37  void resourceScanResult(KJob *job); // slot
38  void createRequestedFolders(ResourceScanJob *job, QHash<QByteArray, bool> &requestedFolders);
39  void collectionCreateResult(KJob *job); // slot
40 
42  SpecialCollections *mSpecialCollections = nullptr;
43  int mPendingCreateJobs;
44 
45  QByteArray mRequestedType;
46  AgentInstance mRequestedResource;
47 
48  // Input:
49  QHash<QByteArray, bool> mDefaultFolders;
50  bool mRequestingDefaultFolders;
51  QHash< QString, QHash<QByteArray, bool> > mFoldersForResource;
52  QString mDefaultResourceType;
53  QVariantMap mDefaultResourceOptions;
54  QList<QByteArray> mKnownTypes;
55  QMap<QByteArray, QString> mNameForTypeMap;
56  QMap<QByteArray, QString> mIconForTypeMap;
57 
58  // Output:
59  QStringList mToForget;
61 };
62 
63 SpecialCollectionsRequestJobPrivate::SpecialCollectionsRequestJobPrivate(SpecialCollections *collections,
65  : q(qq)
66  , mSpecialCollections(collections)
67  , mPendingCreateJobs(0)
68  , mRequestingDefaultFolders(false)
69 {
70 }
71 
72 bool SpecialCollectionsRequestJobPrivate::isEverythingReady() const
73 {
74  // check if all requested folders are known already
75  if (mRequestingDefaultFolders) {
76  for (auto it = mDefaultFolders.cbegin(), end = mDefaultFolders.cend(); it != end; ++it) {
77  if (it.value() && !mSpecialCollections->hasDefaultCollection(it.key())) {
78  return false;
79  }
80  }
81  }
82 
83  for (auto resourceIt = mFoldersForResource.cbegin(), end = mFoldersForResource.cend(); resourceIt != end; ++resourceIt) {
84  const QHash<QByteArray, bool> &requested = resourceIt.value();
85  for (auto it = requested.cbegin(), end = requested.cend(); it != end; ++it) {
86  if (it.value() && !mSpecialCollections->hasCollection(it.key(), AgentManager::self()->instance(resourceIt.key()))) {
87  return false;
88  }
89  }
90  }
91 
92  return true;
93 }
94 
95 void SpecialCollectionsRequestJobPrivate::lockResult(KJob *job)
96 {
97  if (job->error()) {
98  qCWarning(AKONADICORE_LOG) << "Failed to get lock:" << job->errorString();
99  q->setError(job->error());
100  q->setErrorText(job->errorString());
101  q->emitResult();
102  return;
103  }
104 
105  if (mRequestingDefaultFolders) {
106  // If default folders are requested, deal with that first.
107  DefaultResourceJob *resjob = new DefaultResourceJob(mSpecialCollections->d->mSettings, q);
108  resjob->setDefaultResourceType(mDefaultResourceType);
109  resjob->setDefaultResourceOptions(mDefaultResourceOptions);
110  resjob->setTypes(mKnownTypes);
111  resjob->setNameForTypeMap(mNameForTypeMap);
112  resjob->setIconForTypeMap(mIconForTypeMap);
113  QObject::connect(resjob, &KJob::result, q, [this](KJob *job) { resourceScanResult(job); });
114  } else {
115  // If no default folders are requested, go straight to the next step.
116  nextResource();
117  }
118 }
119 
120 void SpecialCollectionsRequestJobPrivate::releaseLock()
121 {
122  const bool ok = Akonadi::releaseLock();
123  if (!ok) {
124  qCWarning(AKONADICORE_LOG) << "WTF, can't release lock.";
125  }
126 }
127 
128 void SpecialCollectionsRequestJobPrivate::nextResource()
129 {
130  if (mFoldersForResource.isEmpty()) {
131  qCDebug(AKONADICORE_LOG) << "All done! Committing.";
132 
133  mSpecialCollections->d->beginBatchRegister();
134 
135  // Forget everything we knew before about these resources.
136  for (const QString &resourceId : qAsConst(mToForget)) {
137  mSpecialCollections->d->forgetFoldersForResource(resourceId);
138  }
139 
140  // Register all the collections that we fetched / created.
141  typedef QPair<Collection, QByteArray> RegisterPair;
142  for (const RegisterPair &pair : qAsConst(mToRegister)) {
143  const bool ok = mSpecialCollections->registerCollection(pair.second, pair.first);
144  Q_ASSERT(ok);
145  Q_UNUSED(ok);
146  }
147 
148  mSpecialCollections->d->endBatchRegister();
149 
150  // Release the lock once the transaction has been committed.
151  QObject::connect(q, &KJob::result, q, [this]() { releaseLock(); });
152 
153  // We are done!
154  q->commit();
155 
156  } else {
157  const QString resourceId = mFoldersForResource.cbegin().key();
158  qCDebug(AKONADICORE_LOG) << "A resource is done," << mFoldersForResource.count()
159  << "more to do. Now doing resource" << resourceId;
160  ResourceScanJob *resjob = new ResourceScanJob(resourceId, mSpecialCollections->d->mSettings, q);
161  QObject::connect(resjob, &KJob::result, q, [this](KJob *job) { resourceScanResult(job); });
162  }
163 }
164 
165 void SpecialCollectionsRequestJobPrivate::resourceScanResult(KJob *job)
166 {
167  ResourceScanJob *resjob = qobject_cast<ResourceScanJob *>(job);
168  Q_ASSERT(resjob);
169 
170  const QString resourceId = resjob->resourceId();
171  qCDebug(AKONADICORE_LOG) << "resourceId" << resourceId;
172 
173  if (job->error()) {
174  qCWarning(AKONADICORE_LOG) << "Failed to request resource" << resourceId << ":" << job->errorString();
175  return;
176  }
177 
178  if (qobject_cast<DefaultResourceJob *>(job)) {
179  // This is the default resource.
180  if (resourceId != mSpecialCollections->d->defaultResourceId()) {
181  qCWarning(AKONADICORE_LOG) << "Resource id's don't match: " << resourceId
182  << mSpecialCollections->d->defaultResourceId();
183  Q_ASSERT(false);
184  }
185  //mToForget.append( mSpecialCollections->defaultResourceId() );
186  createRequestedFolders(resjob, mDefaultFolders);
187  } else {
188  // This is not the default resource.
189  QHash<QByteArray, bool> requestedFolders = mFoldersForResource[resourceId];
190  mFoldersForResource.remove(resourceId);
191  createRequestedFolders(resjob, requestedFolders);
192  }
193 }
194 
195 void SpecialCollectionsRequestJobPrivate::createRequestedFolders(ResourceScanJob *scanJob,
196  QHash<QByteArray, bool> &requestedFolders)
197 {
198  // Remove from the request list the folders which already exist.
199  const Akonadi::Collection::List lstSpecialCols = scanJob->specialCollections();
200  for (const Collection &collection : lstSpecialCols) {
201  Q_ASSERT(collection.hasAttribute<SpecialCollectionAttribute>());
202  const SpecialCollectionAttribute *attr = collection.attribute<SpecialCollectionAttribute>();
203  const QByteArray type = attr->collectionType();
204 
205  if (!type.isEmpty()) {
206  mToRegister.append(qMakePair(collection, type));
207  requestedFolders.insert(type, false);
208  }
209  }
210  mToForget.append(scanJob->resourceId());
211 
212  // Folders left in the request list must be created.
213  Q_ASSERT(mPendingCreateJobs == 0);
214  Q_ASSERT(scanJob->rootResourceCollection().isValid());
215 
216  QHashIterator<QByteArray, bool> it(requestedFolders);
217  while (it.hasNext()) {
218  it.next();
219 
220  if (it.value()) {
221  Collection collection;
222  collection.setParentCollection(scanJob->rootResourceCollection());
223  collection.setName(mNameForTypeMap.value(it.key()));
224 
225  setCollectionAttributes(collection, it.key(), mNameForTypeMap, mIconForTypeMap);
226 
227  CollectionCreateJob *createJob = new CollectionCreateJob(collection, q);
228  createJob->setProperty("type", it.key());
229  QObject::connect(createJob, &KJob::result, q, [this](KJob *job) { collectionCreateResult(job); });
230 
231  mPendingCreateJobs++;
232  }
233  }
234 
235  if (mPendingCreateJobs == 0) {
236  nextResource();
237  }
238 }
239 
240 void SpecialCollectionsRequestJobPrivate::collectionCreateResult(KJob *job)
241 {
242  if (job->error()) {
243  qCWarning(AKONADICORE_LOG) << "Failed CollectionCreateJob." << job->errorString();
244  return;
245  }
246 
248  Q_ASSERT(createJob);
249 
250  const Collection collection = createJob->collection();
251  mToRegister.append(qMakePair(collection, createJob->property("type").toByteArray()));
252 
253  Q_ASSERT(mPendingCreateJobs > 0);
254  mPendingCreateJobs--;
255  qCDebug(AKONADICORE_LOG) << "mPendingCreateJobs now" << mPendingCreateJobs;
256 
257  if (mPendingCreateJobs == 0) {
258  nextResource();
259  }
260 }
261 
262 // TODO KDE5: do not inherit from TransactionSequence
264  : TransactionSequence(parent)
265  , d(new SpecialCollectionsRequestJobPrivate(collections, this))
266 {
267  setProperty("transactionsDisabled", true);
268 }
269 
271 {
272  delete d;
273 }
274 
276 {
277  d->mDefaultFolders[type] = true;
278  d->mRequestingDefaultFolders = true;
279  d->mRequestedType = type;
280 }
281 
283 {
284  d->mFoldersForResource[instance.identifier()][type] = true;
285  d->mRequestedType = type;
286  d->mRequestedResource = instance;
287 }
288 
290 {
291  if (d->mRequestedResource.isValid()) {
292  return d->mSpecialCollections->collection(d->mRequestedType, d->mRequestedResource);
293  } else {
294  return d->mSpecialCollections->defaultCollection(d->mRequestedType);
295  }
296 }
297 
299 {
300  d->mDefaultResourceType = type;
301 }
302 
304 {
305  d->mDefaultResourceOptions = options;
306 }
307 
309 {
310  d->mKnownTypes = types;
311 }
312 
314 {
315  d->mNameForTypeMap = map;
316 }
317 
319 {
320  d->mIconForTypeMap = map;
321 }
322 
324 {
325  if (d->isEverythingReady()) {
326  emitResult();
327  } else {
328  GetLockJob *lockJob = new GetLockJob(this);
329  connect(lockJob, &GetLockJob::result, this, [this](KJob*job) { d->lockResult(job);});
330  lockJob->start();
331  }
332 }
333 
334 void SpecialCollectionsRequestJob::slotResult(KJob *job)
335 {
336  if (job->error()) {
337  // If we failed, let others try.
338  qCWarning(AKONADICORE_LOG) << "Failed SpecialCollectionsRequestJob::slotResult" << job->errorString();
339 
340  d->releaseLock();
341  }
342  TransactionSequence::slotResult(job);
343 }
344 
345 #include "moc_specialcollectionsrequestjob.cpp"
QByteArray toByteArray() const const
Type type(const QString &mimeType)
QHash::iterator insert(const Key &key, const T &value)
An interface to special collections.
void emitResult()
void setIconForTypeMap(const QMap< QByteArray, QString > &map)
Sets the map of special collection types to icon names.
virtual QString errorString() const
~SpecialCollectionsRequestJob() override
Destroys the special collections request job.
Represents a collection of PIM items.
Definition: collection.h:63
An Attribute that stores the special collection type of a collection.
QString identifier() const
Returns the unique identifier of the agent instance.
QHash::const_iterator cend() const const
void setName(const QString &name)
Sets the i18n&#39;ed name of the collection.
Definition: collection.cpp:224
void setParentCollection(const Collection &parent)
Set the parent collection of this object.
Definition: collection.cpp:207
QVariant property(const char *name) const const
QHash::const_iterator cbegin() const const
AgentInstance instance(const QString &identifier) const
Returns the agent instance with the given identifier or an invalid agent instance if the identifier d...
void doStart() override
This method must be reimplemented in the concrete jobs.
int remove(const Key &key)
QByteArray & append(char ch)
const T value(const Key &key) const const
void requestCollection(const QByteArray &type, const AgentInstance &instance)
Requests a special collection of the given type in the given resource instance.
void setTypes(const QList< QByteArray > &types)
Sets the list of well known special collection types.
const QList< QKeySequence > & end()
QString::const_iterator cbegin() const const
void requestDefaultCollection(const QByteArray &type)
Requests a special collection of the given type in the default resource.
Base class for jobs that need to run a sequence of sub-jobs in a transaction.
Definition: item.h:31
Helper integration between Akonadi and Qt.
void setDefaultResourceType(const QString &type)
Sets the type of the resource that shall be created if the requested special collection does not exis...
SpecialCollectionsRequestJob(SpecialCollections *collections, QObject *parent=nullptr)
Creates a new special collections request job.
QByteArray collectionType() const
Returns the special collections type of the collection.
static AgentManager * self()
Returns the global instance of the agent manager.
bool setProperty(const char *name, const QVariant &value)
Job that creates a new collection in the Akonadi storage.
A representation of an agent instance.
void result(KJob *job)
Collection collection() const
Returns the requested collection.
void setNameForTypeMap(const QMap< QByteArray, QString > &map)
Sets the map of special collection types to display names.
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
T qobject_cast(QObject *object)
QObject * parent() const const
void setDefaultResourceOptions(const QVariantMap &options)
Sets the configuration options that shall be applied to the new resource that is created if the reque...
A job to request SpecialCollections.
int error() const
Collection collection() const
Returns the created collection if the job was executed successfully.
This file is part of the KDE documentation.
Documentation copyright © 1996-2020 The KDE developers.
Generated on Thu Jul 9 2020 23:15:24 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.