Akonadi

resourcebase.cpp
1 /*
2  SPDX-FileCopyrightText: 2006 Till Adam <[email protected]>
3  SPDX-FileCopyrightText: 2007 Volker Krause <[email protected]>
4 
5  SPDX-License-Identifier: LGPL-2.0-or-later
6 */
7 
8 #include "resourcebase.h"
9 #include "agentbase_p.h"
10 
11 
12 #include "resourceadaptor.h"
13 #include "collectiondeletejob.h"
14 #include "collectionsync_p.h"
15 #include <QDBusConnection>
16 #include "itemsync.h"
17 #include "akonadifull-version.h"
18 #include "tagsync.h"
19 #include "relationsync.h"
20 #include "resourcescheduler_p.h"
21 #include "tracerinterface.h"
22 
23 #include "changerecorder.h"
24 #include "collectionfetchjob.h"
25 #include "collectionfetchscope.h"
26 #include "collectionmodifyjob.h"
27 #include "invalidatecachejob_p.h"
28 #include "itemfetchjob.h"
29 #include "itemfetchscope.h"
30 #include "itemmodifyjob.h"
31 #include "itemmodifyjob_p.h"
32 #include "itemcreatejob.h"
33 #include "session.h"
34 #include "resourceselectjob_p.h"
35 #include "monitor_p.h"
36 #include "servermanager_p.h"
37 #include "recursivemover_p.h"
38 #include "tagmodifyjob.h"
39 #include "specialcollectionattribute.h"
40 #include "favoritecollectionattribute.h"
41 
42 #include "akonadiagentbase_debug.h"
43 
44 #include <cstdlib>
45 #include <iterator>
46 #include <shared/akranges.h>
47 
48 #include <KLocalizedString>
49 #include <KAboutData>
50 
51 #include <QHash>
52 #include <QTimer>
53 #include <QApplication>
54 
55 using namespace Akonadi;
56 using namespace AkRanges;
57 
58 class Akonadi::ResourceBasePrivate : public AgentBasePrivate
59 {
60  Q_OBJECT
61  Q_CLASSINFO("D-Bus Interface", "org.kde.dfaure")
62 
63 public:
64  explicit ResourceBasePrivate(ResourceBase *parent)
65  : AgentBasePrivate(parent)
66  , scheduler(nullptr)
67  , mItemSyncer(nullptr)
68  , mItemSyncFetchScope(nullptr)
69  , mItemTransactionMode(ItemSync::SingleTransaction)
70  , mItemMergeMode(ItemSync::RIDMerge)
71  , mCollectionSyncer(nullptr)
72  , mTagSyncer(nullptr)
73  , mRelationSyncer(nullptr)
74  , mHierarchicalRid(false)
75  , mUnemittedProgress(0)
76  , mAutomaticProgressReporting(true)
77  , mDisableAutomaticItemDeliveryDone(false)
78  , mItemSyncBatchSize(10)
79  , mCurrentCollectionFetchJob(nullptr)
80  , mScheduleAttributeSyncBeforeCollectionSync(false)
81  {
82  Internal::setClientType(Internal::Resource);
83  mStatusMessage = defaultReadyMessage();
84  mProgressEmissionCompressor.setInterval(1000);
85  mProgressEmissionCompressor.setSingleShot(true);
86  // HACK: skip local changes of the EntityDisplayAttribute by default. Remove this for KDE5 and adjust resource implementations accordingly.
87  mKeepLocalCollectionChanges << "ENTITYDISPLAY";
88  }
89 
90  ~ResourceBasePrivate() override
91  {
92  delete mItemSyncFetchScope;
93  }
94 
95  Q_DECLARE_PUBLIC(ResourceBase)
96 
97  void delayedInit() override {
98  const QString serviceId = ServerManager::agentServiceName(ServerManager::Resource, mId);
99  if (!QDBusConnection::sessionBus().registerService(serviceId)) {
101  if (reason.isEmpty()) {
102  reason = QStringLiteral("this service is probably running already.");
103  }
104  qCCritical(AKONADIAGENTBASE_LOG) << "Unable to register service" << serviceId << "at D-Bus:" << reason;
105 
106  if (QThread::currentThread() == QCoreApplication::instance()->thread()) {
108  }
109  } else {
110  AgentBasePrivate::delayedInit();
111  }
112  }
113 
114  void changeProcessed() override {
115  if (m_recursiveMover) {
116  m_recursiveMover->changeProcessed();
117  QTimer::singleShot(0, m_recursiveMover.data(), &RecursiveMover::replayNext);
118  return;
119  }
120 
121  mChangeRecorder->changeProcessed();
122  if (!mChangeRecorder->isEmpty()) {
123  scheduler->scheduleChangeReplay();
124  }
125  scheduler->taskDone();
126  }
127 
128  void slotAbortRequested();
129 
130  void slotDeliveryDone(KJob *job);
131  void slotCollectionSyncDone(KJob *job);
132  void slotLocalListDone(KJob *job);
133  void slotSynchronizeCollection(const Collection &col);
134  void slotItemRetrievalCollectionFetchDone(KJob *job);
135  void slotCollectionListDone(KJob *job);
136  void slotSynchronizeCollectionAttributes(const Collection &col);
137  void slotCollectionListForAttributesDone(KJob *job);
138  void slotCollectionAttributesSyncDone(KJob *job);
139  void slotSynchronizeTags();
140  void slotSynchronizeRelations();
141  void slotAttributeRetrievalCollectionFetchDone(KJob *job);
142 
143  void slotItemSyncDone(KJob *job);
144 
145  void slotPercent(KJob *job, quint64 percent);
146  void slotDelayedEmitProgress();
147  void slotDeleteResourceCollection();
148  void slotDeleteResourceCollectionDone(KJob *job);
149  void slotCollectionDeletionDone(KJob *job);
150 
151  void slotInvalidateCache(const Akonadi::Collection &collection);
152 
153  void slotPrepareItemRetrieval(const Akonadi::Item &item);
154  void slotPrepareItemRetrievalResult(KJob *job);
155 
156  void slotPrepareItemsRetrieval(const QVector<Akonadi::Item> &item);
157  void slotPrepareItemsRetrievalResult(KJob *job);
158 
159  void changeCommittedResult(KJob *job);
160 
161  void slotRecursiveMoveReplay(RecursiveMover *mover);
162  void slotRecursiveMoveReplayResult(KJob *job);
163 
164  void slotTagSyncDone(KJob *job);
165  void slotRelationSyncDone(KJob *job);
166 
167  void slotSessionReconnected()
168  {
169  Q_Q(ResourceBase);
170 
171  new ResourceSelectJob(q->identifier());
172  }
173 
174  void createItemSyncInstanceIfMissing()
175  {
176  Q_Q(ResourceBase);
177  Q_ASSERT_X(scheduler->currentTask().type == ResourceScheduler::SyncCollection,
178  "createItemSyncInstance", "Calling items retrieval methods although no item retrieval is in progress");
179  if (!mItemSyncer) {
180  mItemSyncer = new ItemSync(q->currentCollection());
181  mItemSyncer->setTransactionMode(mItemTransactionMode);
182  mItemSyncer->setBatchSize(mItemSyncBatchSize);
183  mItemSyncer->setMergeMode(mItemMergeMode);
184  if (mItemSyncFetchScope) {
185  mItemSyncer->setFetchScope(*mItemSyncFetchScope);
186  }
187  mItemSyncer->setDisableAutomaticDeliveryDone(mDisableAutomaticItemDeliveryDone);
188  mItemSyncer->setProperty("collection", QVariant::fromValue(q->currentCollection()));
189  connect(mItemSyncer, qOverload<KJob *, unsigned long>(&KJob::percent), this, &ResourceBasePrivate::slotPercent); // NOLINT(google-runtime-int): ulong comes from KJob
190  connect(mItemSyncer, &KJob::result, this, &ResourceBasePrivate::slotItemSyncDone);
192  }
193  Q_ASSERT(mItemSyncer);
194  }
195 
196 public Q_SLOTS:
197  // Dump the state of the scheduler
198  Q_SCRIPTABLE QString dumpToString() const
199  {
200  Q_Q(const ResourceBase);
201  return scheduler->dumpToString() + QLatin1Char('\n') + q->dumpResourceToString();
202  }
203 
204  Q_SCRIPTABLE void dump()
205  {
206  scheduler->dump();
207  }
208 
209  Q_SCRIPTABLE void clear()
210  {
211  scheduler->clear();
212  }
213 
214 protected Q_SLOTS:
215  // reimplementations from AgentbBasePrivate, containing sanity checks that only apply to resources
216  // such as making sure that RIDs are present as well as translations of cross-resource moves
217  // TODO: we could possibly add recovery code for no-RID notifications by re-enquing those to the change recorder
218  // as the corresponding Add notifications, although that contains a risk of endless fail/retry loops
219 
220  void itemAdded(const Akonadi::Item &item, const Akonadi::Collection &collection) override
221  {
222  if (collection.remoteId().isEmpty()) {
223  changeProcessed();
224  return;
225  }
226  AgentBasePrivate::itemAdded(item, collection);
227  }
228 
229  void itemChanged(const Akonadi::Item &item, const QSet<QByteArray> &partIdentifiers) override
230  {
231  if (item.remoteId().isEmpty()) {
232  changeProcessed();
233  return;
234  }
235  AgentBasePrivate::itemChanged(item, partIdentifiers);
236  }
237 
238  void itemsFlagsChanged(const Akonadi::Item::List &items, const QSet<QByteArray> &addedFlags,
239  const QSet<QByteArray> &removedFlags) override
240  {
241  if (addedFlags.isEmpty() && removedFlags.isEmpty()) {
242  changeProcessed();
243  return;
244  }
245 
246  const Item::List validItems = filterValidItems(items);
247  if (validItems.isEmpty()) {
248  changeProcessed();
249  return;
250  }
251 
252  AgentBasePrivate::itemsFlagsChanged(validItems, addedFlags, removedFlags);
253  }
254 
255  void itemsTagsChanged(const Akonadi::Item::List &items, const QSet<Akonadi::Tag> &addedTags, const QSet<Akonadi::Tag> &removedTags) override
256  {
257  if (addedTags.isEmpty() && removedTags.isEmpty()) {
258  changeProcessed();
259  return;
260  }
261 
262  const Item::List validItems = filterValidItems(items);
263  if (validItems.isEmpty()) {
264  changeProcessed();
265  return;
266  }
267 
268  AgentBasePrivate::itemsTagsChanged(validItems, addedTags, removedTags);
269  }
270 
271  // TODO move the move translation code from AgentBasePrivate here, it's wrong for agents
272  void itemMoved(const Akonadi::Item &item, const Akonadi::Collection &source, const Akonadi::Collection &destination) override
273  {
274  if (item.remoteId().isEmpty() || destination.remoteId().isEmpty() || destination == source) {
275  changeProcessed();
276  return;
277  }
278  AgentBasePrivate::itemMoved(item, source, destination);
279  }
280 
281  void itemsMoved(const Akonadi::Item::List &items, const Akonadi::Collection &source, const Akonadi::Collection &destination) override
282  {
283  if (destination.remoteId().isEmpty() || destination == source) {
284  changeProcessed();
285  return;
286  }
287 
288  const Item::List validItems = filterValidItems(items);
289  if (validItems.isEmpty()) {
290  changeProcessed();
291  return;
292  }
293 
294  AgentBasePrivate::itemsMoved(validItems, source, destination);
295  }
296 
297  void itemRemoved(const Akonadi::Item &item) override
298  {
299  if (item.remoteId().isEmpty()) {
300  changeProcessed();
301  return;
302  }
303  AgentBasePrivate::itemRemoved(item);
304  }
305 
306  void itemsRemoved(const Akonadi::Item::List &items) override
307  {
308  const Item::List validItems = filterValidItems(items);
309  if (validItems.isEmpty()) {
310  changeProcessed();
311  return;
312  }
313 
314  AgentBasePrivate::itemsRemoved(validItems);
315  }
316 
317  void collectionAdded(const Akonadi::Collection &collection, const Akonadi::Collection &parent) override
318  {
319  if (parent.remoteId().isEmpty()) {
320  changeProcessed();
321  return;
322  }
323  AgentBasePrivate::collectionAdded(collection, parent);
324  }
325 
326  void collectionChanged(const Akonadi::Collection &collection) override
327  {
328  if (collection.remoteId().isEmpty()) {
329  changeProcessed();
330  return;
331  }
332  AgentBasePrivate::collectionChanged(collection);
333  }
334 
335  void collectionChanged(const Akonadi::Collection &collection, const QSet<QByteArray> &partIdentifiers) override
336  {
337  if (collection.remoteId().isEmpty()) {
338  changeProcessed();
339  return;
340  }
341  AgentBasePrivate::collectionChanged(collection, partIdentifiers);
342  }
343 
344  void collectionMoved(const Akonadi::Collection &collection, const Akonadi::Collection &source, const Akonadi::Collection &destination) override
345  {
346  // unknown destination or source == destination means we can't do/don't have to do anything
347  if (destination.remoteId().isEmpty() || source == destination) {
348  changeProcessed();
349  return;
350  }
351 
352  // inter-resource moves, requires we know which resources the source and destination are in though
353  if (!source.resource().isEmpty() && !destination.resource().isEmpty() && source.resource() != destination.resource()) {
354  if (source.resource() == q_ptr->identifier()) { // moved away from us
355  AgentBasePrivate::collectionRemoved(collection);
356  } else if (destination.resource() == q_ptr->identifier()) { // moved to us
357  scheduler->taskDone(); // stop change replay for now
358  RecursiveMover *mover = new RecursiveMover(this);
359  mover->setCollection(collection, destination);
360  scheduler->scheduleMoveReplay(collection, mover);
361  }
362  return;
363  }
364 
365  // intra-resource move, requires the moved collection to have a valid id though
366  if (collection.remoteId().isEmpty()) {
367  changeProcessed();
368  return;
369  }
370 
371  // intra-resource move, ie. something we can handle internally
372  AgentBasePrivate::collectionMoved(collection, source, destination);
373  }
374 
375  void collectionRemoved(const Akonadi::Collection &collection) override
376  {
377  if (collection.remoteId().isEmpty()) {
378  changeProcessed();
379  return;
380  }
381  AgentBasePrivate::collectionRemoved(collection);
382  }
383 
384  void tagAdded(const Akonadi::Tag &tag) override
385  {
386  if (!tag.isValid()) {
387  changeProcessed();
388  return;
389  }
390 
391  AgentBasePrivate::tagAdded(tag);
392  }
393 
394  void tagChanged(const Akonadi::Tag &tag) override
395  {
396  if (tag.remoteId().isEmpty()) {
397  changeProcessed();
398  return;
399  }
400 
401  AgentBasePrivate::tagChanged(tag);
402  }
403 
404  void tagRemoved(const Akonadi::Tag &tag) override
405  {
406  if (tag.remoteId().isEmpty()) {
407  changeProcessed();
408  return;
409  }
410 
411  AgentBasePrivate::tagRemoved(tag);
412  }
413 
414 private:
415  static Item::List filterValidItems(Item::List items)
416  {
417  items.erase(std::remove_if(items.begin(), items.end(),
418  [](const auto &item) { return item.remoteId().isEmpty(); }),
419  items.end());
420  return items;
421  }
422 
423 public:
424  // synchronize states
425  Collection currentCollection;
426 
427  ResourceScheduler *scheduler = nullptr;
428  ItemSync *mItemSyncer = nullptr;
429  ItemFetchScope *mItemSyncFetchScope = nullptr;
430  ItemSync::TransactionMode mItemTransactionMode;
431  ItemSync::MergeMode mItemMergeMode;
432  CollectionSync *mCollectionSyncer = nullptr;
433  TagSync *mTagSyncer = nullptr;
434  RelationSync *mRelationSyncer = nullptr;
435  bool mHierarchicalRid;
436  QTimer mProgressEmissionCompressor;
437  int mUnemittedProgress;
438  QMap<Akonadi::Collection::Id, QVariantMap> mUnemittedAdvancedStatus;
439  bool mAutomaticProgressReporting;
440  bool mDisableAutomaticItemDeliveryDone;
441  QPointer<RecursiveMover> m_recursiveMover;
442  int mItemSyncBatchSize;
443  QSet<QByteArray> mKeepLocalCollectionChanges;
444  KJob *mCurrentCollectionFetchJob = nullptr;
445  bool mScheduleAttributeSyncBeforeCollectionSync;
446 };
447 
449  : AgentBase(new ResourceBasePrivate(this), id)
450 {
451  Q_D(ResourceBase);
452 
453  qDBusRegisterMetaType<QByteArrayList>();
454 
455  new Akonadi__ResourceAdaptor(this);
456 
457  d->scheduler = new ResourceScheduler(this);
458 
459  d->mChangeRecorder->setChangeRecordingEnabled(true);
460  d->mChangeRecorder->setCollectionMoveTranslationEnabled(false); // we deal with this ourselves
461  connect(d->mChangeRecorder, &ChangeRecorder::changesAdded,
462  d->scheduler, &ResourceScheduler::scheduleChangeReplay);
463 
464  d->mChangeRecorder->setResourceMonitored(d->mId.toLatin1());
465  d->mChangeRecorder->fetchCollection(true);
466 
467  connect(d->scheduler, &ResourceScheduler::executeFullSync, this, &ResourceBase::retrieveCollections);
468  connect(d->scheduler, &ResourceScheduler::executeCollectionTreeSync, this, &ResourceBase::retrieveCollections);
469  connect(d->scheduler, &ResourceScheduler::executeCollectionSync, d, &ResourceBasePrivate::slotSynchronizeCollection);
470  connect(d->scheduler, &ResourceScheduler::executeCollectionAttributesSync, d, &ResourceBasePrivate::slotSynchronizeCollectionAttributes);
471  connect(d->scheduler, &ResourceScheduler::executeTagSync, d, &ResourceBasePrivate::slotSynchronizeTags);
472  connect(d->scheduler, &ResourceScheduler::executeRelationSync, d, &ResourceBasePrivate::slotSynchronizeRelations);
473  connect(d->scheduler, &ResourceScheduler::executeItemFetch, d, &ResourceBasePrivate::slotPrepareItemRetrieval);
474  connect(d->scheduler, &ResourceScheduler::executeItemsFetch, d, &ResourceBasePrivate::slotPrepareItemsRetrieval);
475  connect(d->scheduler, &ResourceScheduler::executeResourceCollectionDeletion, d, &ResourceBasePrivate::slotDeleteResourceCollection);
476  connect(d->scheduler, &ResourceScheduler::executeCacheInvalidation, d, &ResourceBasePrivate::slotInvalidateCache);
477  connect(d->scheduler, &ResourceScheduler::status, this, qOverload<int, const QString &>(&ResourceBase::status));
478  connect(d->scheduler, &ResourceScheduler::executeChangeReplay, d->mChangeRecorder, &ChangeRecorder::replayNext);
479  connect(d->scheduler, &ResourceScheduler::executeRecursiveMoveReplay, d, &ResourceBasePrivate::slotRecursiveMoveReplay);
480  connect(d->scheduler, &ResourceScheduler::fullSyncComplete, this, &ResourceBase::synchronized);
481  connect(d->scheduler, &ResourceScheduler::collectionTreeSyncComplete, this, &ResourceBase::collectionTreeSynchronized);
482  connect(d->mChangeRecorder, &ChangeRecorder::nothingToReplay, d->scheduler, &ResourceScheduler::taskDone);
483  connect(d->mChangeRecorder, &Monitor::collectionRemoved, d->scheduler, &ResourceScheduler::collectionRemoved);
484  connect(this, &ResourceBase::abortRequested, d, &ResourceBasePrivate::slotAbortRequested);
485  connect(this, &ResourceBase::synchronized, d->scheduler, &ResourceScheduler::taskDone);
486  connect(this, &ResourceBase::collectionTreeSynchronized, d->scheduler, &ResourceScheduler::taskDone);
488  connect(&d->mProgressEmissionCompressor, &QTimer::timeout, d, &ResourceBasePrivate::slotDelayedEmitProgress);
489 
490  d->scheduler->setOnline(d->mOnline);
491  if (!d->mChangeRecorder->isEmpty()) {
492  d->scheduler->scheduleChangeReplay();
493  }
494 
495  new ResourceSelectJob(identifier());
496 
497  connect(d->mChangeRecorder->session(), &Session::reconnected, d, &ResourceBasePrivate::slotSessionReconnected);
498 }
499 
500 ResourceBase::~ResourceBase() = default;
501 
503 {
504  d_func()->scheduler->scheduleFullSync();
505 }
506 
508 {
510 }
511 
513 {
514  return AgentBase::agentName();
515 }
516 
517 QString ResourceBase::parseArguments(int argc, char **argv)
518 {
519  Q_UNUSED(argc);
520 
521  QCommandLineOption identifierOption(QStringLiteral("identifier"),
522  i18nc("@label command line option", "Resource identifier"),
523  QStringLiteral("argument"));
524  QCommandLineParser parser;
525  parser.addOption(identifierOption);
526  parser.addHelpOption();
527  parser.addVersionOption();
528  parser.process(*qApp);
529  parser.setApplicationDescription(i18n("Akonadi Resource"));
530 
531  if (!parser.isSet(identifierOption)) {
532  qCDebug(AKONADIAGENTBASE_LOG) << "Identifier argument missing";
533  exit(1);
534  }
535 
536  const QString identifier = parser.value(identifierOption);
537 
538  if (identifier.isEmpty()) {
539  qCDebug(AKONADIAGENTBASE_LOG) << "Identifier is empty";
540  exit(1);
541  }
542 
544  QCoreApplication::setApplicationVersion(QStringLiteral(AKONADI_FULL_VERSION));
545 
546  const QFileInfo fi(QString::fromLocal8Bit(argv[0]));
547  // strip off full path and possible .exe suffix
548  const QString catalog = fi.baseName();
549 
550  QTranslator *translator = new QTranslator();
551  translator->load(catalog);
553 
554  return identifier;
555 }
556 
558 {
560  KAboutData::setApplicationData(r.aboutData());
561  return qApp->exec();
562 }
563 
564 void ResourceBasePrivate::slotAbortRequested()
565 {
566  Q_Q(ResourceBase);
567 
568  scheduler->cancelQueues();
569  q->abortActivity();
570 }
571 
572 void ResourceBase::itemRetrieved(const Item &item)
573 {
574  Q_D(ResourceBase);
575  Q_ASSERT(d->scheduler->currentTask().type == ResourceScheduler::FetchItem);
576  if (!item.isValid()) {
577  d->scheduler->itemFetchDone(i18nc("@info", "Invalid item retrieved"));
578  return;
579  }
580 
581  const QSet<QByteArray> requestedParts = d->scheduler->currentTask().itemParts;
582  for (const QByteArray &part : requestedParts) {
583  if (!item.loadedPayloadParts().contains(part)) {
584  qCWarning(AKONADIAGENTBASE_LOG) << "Item does not provide part" << part;
585  }
586  }
587 
588  ItemModifyJob *job = new ItemModifyJob(item);
589  job->d_func()->setSilent(true);
590  // FIXME: remove once the item with which we call retrieveItem() has a revision number
591  job->disableRevisionCheck();
592  connect(job, &KJob::result, d, &ResourceBasePrivate::slotDeliveryDone);
593 }
594 
595 void ResourceBasePrivate::slotDeliveryDone(KJob *job)
596 {
597  Q_Q(ResourceBase);
598  Q_ASSERT(scheduler->currentTask().type == ResourceScheduler::FetchItem);
599  if (job->error()) {
600  Q_EMIT q->error(i18nc("@info", "Error while creating item: %1", job->errorString()));
601  }
602  scheduler->itemFetchDone(QString());
603 }
604 
606 {
607  Q_D(ResourceBase);
608  Q_ASSERT(d->scheduler->currentTask().type == ResourceScheduler::SyncCollectionAttributes);
609  if (!collection.isValid()) {
610  Q_EMIT attributesSynchronized(d->scheduler->currentTask().collection.id());
611  d->scheduler->taskDone();
612  return;
613  }
614 
615  CollectionModifyJob *job = new CollectionModifyJob(collection);
616  connect(job, &KJob::result, d, &ResourceBasePrivate::slotCollectionAttributesSyncDone);
617 }
618 
619 void ResourceBasePrivate::slotCollectionAttributesSyncDone(KJob *job)
620 {
621  Q_Q(ResourceBase);
622  Q_ASSERT(scheduler->currentTask().type == ResourceScheduler::SyncCollectionAttributes);
623  if (job->error()) {
624  Q_EMIT q->error(i18nc("@info", "Error while updating collection: %1", job->errorString()));
625  }
626  Q_EMIT q->attributesSynchronized(scheduler->currentTask().collection.id());
627  scheduler->taskDone();
628 }
629 
630 void ResourceBasePrivate::slotDeleteResourceCollection()
631 {
632  Q_Q(ResourceBase);
633 
635  job->fetchScope().setResource(q->identifier());
636  connect(job, &KJob::result, this, &ResourceBasePrivate::slotDeleteResourceCollectionDone);
637 }
638 
639 void ResourceBasePrivate::slotDeleteResourceCollectionDone(KJob *job)
640 {
641  Q_Q(ResourceBase);
642  if (job->error()) {
643  Q_EMIT q->error(job->errorString());
644  scheduler->taskDone();
645  } else {
646  const CollectionFetchJob *fetchJob = static_cast<const CollectionFetchJob *>(job);
647 
648  if (!fetchJob->collections().isEmpty()) {
649  CollectionDeleteJob *job = new CollectionDeleteJob(fetchJob->collections().at(0));
650  connect(job, &KJob::result, this, &ResourceBasePrivate::slotCollectionDeletionDone);
651  } else {
652  // there is no resource collection, so just ignore the request
653  scheduler->taskDone();
654  }
655  }
656 }
657 
658 void ResourceBasePrivate::slotCollectionDeletionDone(KJob *job)
659 {
660  Q_Q(ResourceBase);
661  if (job->error()) {
662  Q_EMIT q->error(job->errorString());
663  }
664 
665  scheduler->taskDone();
666 }
667 
668 void ResourceBasePrivate::slotInvalidateCache(const Akonadi::Collection &collection)
669 {
670  Q_Q(ResourceBase);
671  new InvalidateCacheJob(collection, q);
672 }
673 
674 void ResourceBase::changeCommitted(const Item &item)
675 {
676  changesCommitted(Item::List() << item);
677 }
678 
679 void ResourceBase::changesCommitted(const Item::List &items)
680 {
681  Q_D(ResourceBase);
682  TransactionSequence *transaction = new TransactionSequence(this);
683  connect(transaction, &KJob::finished, d, &ResourceBasePrivate::changeCommittedResult);
684 
685  // Modify the items one-by-one, because STORE does not support mass RID change
686  for (const Item &item : items) {
687  ItemModifyJob *job = new ItemModifyJob(item, transaction);
688  job->d_func()->setClean();
689  job->disableRevisionCheck(); // TODO: remove, but where/how do we handle the error?
690  job->setIgnorePayload(true); // we only want to reset the dirty flag and update the remote id
691  }
692 }
693 
695 {
696  Q_D(ResourceBase);
697  CollectionModifyJob *job = new CollectionModifyJob(collection);
698  connect(job, &KJob::result, d, &ResourceBasePrivate::changeCommittedResult);
699 }
700 
701 void ResourceBasePrivate::changeCommittedResult(KJob *job)
702 {
703  if (job->error()) {
704  qCWarning(AKONADIAGENTBASE_LOG) << job->errorText();
705  }
706 
707  Q_Q(ResourceBase);
708  if (qobject_cast<CollectionModifyJob *>(job)) {
709  if (job->error()) {
710  Q_EMIT q->error(i18nc("@info", "Updating local collection failed: %1.", job->errorText()));
711  }
712  mChangeRecorder->d_ptr->invalidateCache(static_cast<CollectionModifyJob *>(job)->collection());
713  } else {
714  if (job->error()) {
715  Q_EMIT q->error(i18nc("@info", "Updating local items failed: %1.", job->errorText()));
716  }
717  // Item and tag cache is invalidated by modify job
718  }
719 
720  changeProcessed();
721 }
722 
724 {
725  Q_D(ResourceBase);
726  TagModifyJob *job = new TagModifyJob(tag);
727  connect(job, &KJob::result, d, &ResourceBasePrivate::changeCommittedResult);
728 }
729 
730 void ResourceBase::requestItemDelivery(const QVector<qint64> &uids, const QByteArrayList &parts)
731 {
732  Q_D(ResourceBase);
733  if (!isOnline()) {
734  const QString errorMsg = i18nc("@info", "Cannot fetch item in offline mode.");
736  Q_EMIT error(errorMsg);
737  return;
738  }
739 
740  setDelayedReply(true);
741 
742  const auto items = uids | Views::transform([](const auto uid) { return Item{uid}; }) | Actions::toQVector;
743  d->scheduler->scheduleItemsFetch(items, QSet<QByteArray>::fromList(parts), message());
744 }
745 
747 {
748  Q_D(ResourceBase);
749  Q_ASSERT_X(d->scheduler->currentTask().type == ResourceScheduler::SyncCollectionTree ||
750  d->scheduler->currentTask().type == ResourceScheduler::SyncAll,
751  "ResourceBase::collectionsRetrieved()",
752  "Calling collectionsRetrieved() although no collection retrieval is in progress");
753  if (!d->mCollectionSyncer) {
754  d->mCollectionSyncer = new CollectionSync(identifier());
755  d->mCollectionSyncer->setHierarchicalRemoteIds(d->mHierarchicalRid);
756  d->mCollectionSyncer->setKeepLocalChanges(d->mKeepLocalCollectionChanges);
757  connect(d->mCollectionSyncer, qOverload<KJob *, unsigned long>(&KJob::percent), d, &ResourceBasePrivate::slotPercent); // NOLINT(google-runtime-int): ulong comes from KJob
758  connect(d->mCollectionSyncer, &KJob::result, d, &ResourceBasePrivate::slotCollectionSyncDone);
759  }
760  d->mCollectionSyncer->setRemoteCollections(collections);
761 }
762 
764  const Collection::List &removedCollections)
765 {
766  Q_D(ResourceBase);
767  Q_ASSERT_X(d->scheduler->currentTask().type == ResourceScheduler::SyncCollectionTree ||
768  d->scheduler->currentTask().type == ResourceScheduler::SyncAll,
769  "ResourceBase::collectionsRetrievedIncremental()",
770  "Calling collectionsRetrievedIncremental() although no collection retrieval is in progress");
771  if (!d->mCollectionSyncer) {
772  d->mCollectionSyncer = new CollectionSync(identifier());
773  d->mCollectionSyncer->setHierarchicalRemoteIds(d->mHierarchicalRid);
774  d->mCollectionSyncer->setKeepLocalChanges(d->mKeepLocalCollectionChanges);
775  connect(d->mCollectionSyncer, qOverload<KJob *, unsigned long>(&KJob::percent), d, &ResourceBasePrivate::slotPercent); // NOLINT(google-runtime-int): ulong comes from KJob
776  connect(d->mCollectionSyncer, &KJob::result, d, &ResourceBasePrivate::slotCollectionSyncDone);
777  }
778  d->mCollectionSyncer->setRemoteCollections(changedCollections, removedCollections);
779 }
780 
782 {
783  Q_D(ResourceBase);
784  Q_ASSERT_X(d->scheduler->currentTask().type == ResourceScheduler::SyncCollectionTree ||
785  d->scheduler->currentTask().type == ResourceScheduler::SyncAll,
786  "ResourceBase::setCollectionStreamingEnabled()",
787  "Calling setCollectionStreamingEnabled() although no collection retrieval is in progress");
788  if (!d->mCollectionSyncer) {
789  d->mCollectionSyncer = new CollectionSync(identifier());
790  d->mCollectionSyncer->setHierarchicalRemoteIds(d->mHierarchicalRid);
791  connect(d->mCollectionSyncer, qOverload<KJob *, unsigned long>(&KJob::percent), d, &ResourceBasePrivate::slotPercent); // NOLINT(google-runtime-int): ulong comes from KJob
792  connect(d->mCollectionSyncer, &KJob::result, d, &ResourceBasePrivate::slotCollectionSyncDone);
793  }
794  d->mCollectionSyncer->setStreamingEnabled(enable);
795 }
796 
798 {
799  Q_D(ResourceBase);
800  Q_ASSERT_X(d->scheduler->currentTask().type == ResourceScheduler::SyncCollectionTree ||
801  d->scheduler->currentTask().type == ResourceScheduler::SyncAll,
802  "ResourceBase::collectionsRetrievalDone()",
803  "Calling collectionsRetrievalDone() although no collection retrieval is in progress");
804  // streaming enabled, so finalize the sync
805  if (d->mCollectionSyncer) {
806  d->mCollectionSyncer->retrievalDone();
807  } else {
808  // user did the sync himself, we are done now
809  // FIXME: we need the same special case for SyncAll as in slotCollectionSyncDone here!
810  d->scheduler->taskDone();
811  }
812 }
813 
815 {
816  Q_D(ResourceBase);
817  d->mKeepLocalCollectionChanges = parts;
818 }
819 
820 void ResourceBasePrivate::slotCollectionSyncDone(KJob *job)
821 {
822  Q_Q(ResourceBase);
823  mCollectionSyncer = nullptr;
824  if (job->error()) {
825  if (job->error() != Job::UserCanceled) {
826  Q_EMIT q->error(job->errorString());
827  }
828  } else {
829  if (scheduler->currentTask().type == ResourceScheduler::SyncAll) {
831  list->setFetchScope(q->changeRecorder()->collectionFetchScope());
833  list->fetchScope().fetchAttribute<FavoriteCollectionAttribute>();
834  list->fetchScope().setResource(mId);
836  connect(list, &KJob::result, this, &ResourceBasePrivate::slotLocalListDone);
837  return;
838  } else if (scheduler->currentTask().type == ResourceScheduler::SyncCollectionTree) {
839  scheduler->scheduleCollectionTreeSyncCompletion();
840  }
841  }
842  scheduler->taskDone();
843 }
844 
845 
846 namespace {
847 
848 bool sortCollectionsForSync(const Collection &l, const Collection &r)
849 {
850  const auto lType = l.hasAttribute<SpecialCollectionAttribute>()
852  : QByteArray();
853  const bool lInbox = (lType == "inbox") || (l.remoteId().midRef(1).compare(QLatin1String("inbox"), Qt::CaseInsensitive) == 0);
854  const bool lFav = l.hasAttribute<FavoriteCollectionAttribute>();
855 
856  const auto rType = r.hasAttribute<SpecialCollectionAttribute>()
858  : QByteArray();
859  const bool rInbox = (rType == "inbox") || (r.remoteId().midRef(1).compare(QLatin1String("inbox"), Qt::CaseInsensitive) == 0);
860  const bool rFav = r.hasAttribute<FavoriteCollectionAttribute>();
861 
862  // inbox is always first
863  if (lInbox) {
864  return true;
865  } else if (rInbox) {
866  return false;
867  }
868 
869  // favorites right after inbox
870  if (lFav) {
871  return !rInbox;
872  } else if (rFav) {
873  return lInbox;
874  }
875 
876  // trash is always last (unless it's favorite)
877  if (lType == "trash") {
878  return false;
879  } else if (rType == "trash") {
880  return true;
881  }
882 
883  // Fallback to sorting by id
884  return l.id() < r.id();
885 }
886 
887 } // namespace
888 
889 void ResourceBasePrivate::slotLocalListDone(KJob *job)
890 {
891  Q_Q(ResourceBase);
892  if (job->error()) {
893  Q_EMIT q->error(job->errorString());
894  } else {
895  Collection::List cols = static_cast<CollectionFetchJob *>(job)->collections();
896  std::sort(cols.begin(), cols.end(), sortCollectionsForSync);
897  for (const Collection &col : qAsConst(cols)) {
898  scheduler->scheduleSync(col);
899  }
900  scheduler->scheduleFullSyncCompletion();
901  }
902  scheduler->taskDone();
903 }
904 
905 void ResourceBasePrivate::slotSynchronizeCollection(const Collection &col)
906 {
907  Q_Q(ResourceBase);
908  currentCollection = col;
909  // This can happen due to FetchHelper::triggerOnDemandFetch() in the akonadi server (not an error).
910  if (!col.remoteId().isEmpty()) {
911  // check if this collection actually can contain anything
913  contentTypes.removeAll(Collection::mimeType());
914  contentTypes.removeAll(Collection::virtualMimeType());
915  if (!contentTypes.isEmpty() || col.isVirtual()) {
916  if (mAutomaticProgressReporting) {
917  Q_EMIT q->status(AgentBase::Running, i18nc("@info:status", "Syncing folder '%1'", currentCollection.displayName()));
918  }
919 
920  qCDebug(AKONADIAGENTBASE_LOG) << "Preparing collection sync of collection"
923  fetchJob->setFetchScope(q->changeRecorder()->collectionFetchScope());
924  connect(fetchJob, &KJob::result, this, &ResourceBasePrivate::slotItemRetrievalCollectionFetchDone);
925  mCurrentCollectionFetchJob = fetchJob;
926  return;
927  }
928  }
929  scheduler->taskDone();
930 }
931 
932 void ResourceBasePrivate::slotItemRetrievalCollectionFetchDone(KJob *job)
933 {
934  Q_Q(ResourceBase);
935  mCurrentCollectionFetchJob = nullptr;
936  if (job->error()) {
937  qCWarning(AKONADIAGENTBASE_LOG) << "Failed to retrieve collection for sync: " << job->errorString();
938  q->cancelTask(i18n("Failed to retrieve collection for sync."));
939  return;
940  }
941  Akonadi::CollectionFetchJob *fetchJob = static_cast<Akonadi::CollectionFetchJob *>(job);
942  const Collection::List collections = fetchJob->collections();
943  if (collections.isEmpty()) {
944  qCWarning(AKONADIAGENTBASE_LOG) << "The fetch job returned empty collection set. This is unexpected.";
945  q->cancelTask(i18n("Failed to retrieve collection for sync."));
946  return;
947  }
948  q->retrieveItems(collections.at(0));
949 }
950 
952 {
953  Q_D(const ResourceBase);
954  return d->mItemSyncBatchSize;
955 }
956 
958 {
959  Q_D(ResourceBase);
960  d->mItemSyncBatchSize = batchSize;
961 }
962 
964 {
965  Q_D(ResourceBase);
966  d->mScheduleAttributeSyncBeforeCollectionSync = enable;
967 }
968 
969 void ResourceBasePrivate::slotSynchronizeCollectionAttributes(const Collection &col)
970 {
971  Q_Q(ResourceBase);
973  fetchJob->setFetchScope(q->changeRecorder()->collectionFetchScope());
974  connect(fetchJob, &KJob::result, this, &ResourceBasePrivate::slotAttributeRetrievalCollectionFetchDone);
975  Q_ASSERT(!mCurrentCollectionFetchJob);
976  mCurrentCollectionFetchJob = fetchJob;
977 }
978 
979 void ResourceBasePrivate::slotAttributeRetrievalCollectionFetchDone(KJob *job)
980 {
981  mCurrentCollectionFetchJob = nullptr;
982  Q_Q(ResourceBase);
983  if (job->error()) {
984  qCWarning(AKONADIAGENTBASE_LOG) << "Failed to retrieve collection for attribute sync: " << job->errorString();
985  q->cancelTask(i18n("Failed to retrieve collection for attribute sync."));
986  return;
987  }
988  Akonadi::CollectionFetchJob *fetchJob = static_cast<Akonadi::CollectionFetchJob *>(job);
989  // FIXME: Why not call q-> directly?
990  QMetaObject::invokeMethod(q, "retrieveCollectionAttributes", Q_ARG(Akonadi::Collection, fetchJob->collections().at(0)));
991 }
992 
993 void ResourceBasePrivate::slotSynchronizeTags()
994 {
995  Q_Q(ResourceBase);
996  QMetaObject::invokeMethod(this, [q] { q->retrieveTags(); });
997 }
998 
999 void ResourceBasePrivate::slotSynchronizeRelations()
1000 {
1001  Q_Q(ResourceBase);
1002  QMetaObject::invokeMethod(this, [q] { q->retrieveRelations(); });
1003 }
1004 
1005 void ResourceBasePrivate::slotPrepareItemRetrieval(const Item &item)
1006 {
1007  Q_Q(ResourceBase);
1008  auto *fetch = new ItemFetchJob(item, this);
1009  // we always need at least parent so we can use ItemCreateJob to merge
1010  fetch->fetchScope().setAncestorRetrieval(qMax(ItemFetchScope::Parent,
1011  q->changeRecorder()->itemFetchScope().ancestorRetrieval()));
1012  fetch->fetchScope().setCacheOnly(true);
1013  fetch->fetchScope().setFetchRemoteIdentification(true);
1014 
1015  // copy list of attributes to fetch
1016  const QSet<QByteArray> attributes = q->changeRecorder()->itemFetchScope().attributes();
1017  for (const auto &attribute : attributes) {
1018  fetch->fetchScope().fetchAttribute(attribute);
1019  }
1020 
1021  connect(fetch, &KJob::result, this, &ResourceBasePrivate::slotPrepareItemRetrievalResult);
1022 }
1023 
1024 void ResourceBasePrivate::slotPrepareItemRetrievalResult(KJob *job)
1025 {
1026  Q_Q(ResourceBase);
1027  Q_ASSERT_X(scheduler->currentTask().type == ResourceScheduler::FetchItem,
1028  "ResourceBasePrivate::slotPrepareItemRetrievalResult()",
1029  "Preparing item retrieval although no item retrieval is in progress");
1030  if (job->error()) {
1031  q->cancelTask(job->errorText());
1032  return;
1033  }
1034  ItemFetchJob *fetch = qobject_cast<ItemFetchJob *>(job);
1035  if (fetch->items().count() != 1) {
1036  q->cancelTask(i18n("The requested item no longer exists"));
1037  return;
1038  }
1039  const QSet<QByteArray> parts = scheduler->currentTask().itemParts;
1040  if (!q->retrieveItem(fetch->items().at(0), parts)) {
1041  q->cancelTask();
1042  }
1043 }
1044 
1045 void ResourceBasePrivate::slotPrepareItemsRetrieval(const QVector<Item> &items)
1046 {
1047  Q_Q(ResourceBase);
1048  ItemFetchJob *fetch = new ItemFetchJob(items, this);
1049  // we always need at least parent so we can use ItemCreateJob to merge
1051  q->changeRecorder()->itemFetchScope().ancestorRetrieval()));
1052  fetch->fetchScope().setCacheOnly(true);
1054  // It's possible that one or more items were removed before this task was
1055  // executed, so ignore it and just handle the rest.
1056  fetch->fetchScope().setIgnoreRetrievalErrors(true);
1057 
1058  // copy list of attributes to fetch
1059  const QSet<QByteArray> attributes = q->changeRecorder()->itemFetchScope().attributes();
1060  for (const auto &attribute : attributes) {
1061  fetch->fetchScope().fetchAttribute(attribute);
1062  }
1063 
1064  connect(fetch, &KJob::result, this, &ResourceBasePrivate::slotPrepareItemsRetrievalResult);
1065 }
1066 
1067 void ResourceBasePrivate::slotPrepareItemsRetrievalResult(KJob *job)
1068 {
1069  Q_Q(ResourceBase);
1070  Q_ASSERT_X(scheduler->currentTask().type == ResourceScheduler::FetchItems,
1071  "ResourceBasePrivate::slotPrepareItemsRetrievalResult()",
1072  "Preparing items retrieval although no items retrieval is in progress");
1073  if (job->error()) {
1074  q->cancelTask(job->errorText());
1075  return;
1076  }
1077  ItemFetchJob *fetch = qobject_cast<ItemFetchJob *>(job);
1078  const auto items = fetch->items();
1079  if (items.isEmpty()) {
1080  q->cancelTask();
1081  return;
1082  }
1083 
1084  const QSet<QByteArray> parts = scheduler->currentTask().itemParts;
1085  Q_ASSERT(items.first().parentCollection().isValid());
1086  if (!q->retrieveItems(items, parts)) {
1087  q->cancelTask();
1088  }
1089 }
1090 
1091 void ResourceBasePrivate::slotRecursiveMoveReplay(RecursiveMover *mover)
1092 {
1093  Q_ASSERT(mover);
1094  Q_ASSERT(!m_recursiveMover);
1095  m_recursiveMover = mover;
1096  connect(mover, &KJob::result, this, &ResourceBasePrivate::slotRecursiveMoveReplayResult);
1097  mover->start();
1098 }
1099 
1100 void ResourceBasePrivate::slotRecursiveMoveReplayResult(KJob *job)
1101 {
1102  Q_Q(ResourceBase);
1103  m_recursiveMover = nullptr;
1104 
1105  if (job->error()) {
1106  q->deferTask();
1107  return;
1108  }
1109 
1110  changeProcessed();
1111 }
1112 
1114 {
1115  Q_D(ResourceBase);
1116  // streaming enabled, so finalize the sync
1117  if (d->mItemSyncer) {
1118  d->mItemSyncer->deliveryDone();
1119  } else {
1120  if (d->scheduler->currentTask().type == ResourceScheduler::FetchItems) {
1121  d->scheduler->currentTask().sendDBusReplies(QString());
1122  }
1123  // user did the sync himself, we are done now
1124  d->scheduler->taskDone();
1125  }
1126 }
1127 
1129 {
1130  Q_D(ResourceBase);
1131  d->scheduler->scheduleResourceCollectionDeletion();
1132 }
1133 
1135 {
1136  Q_D(ResourceBase);
1137  d->scheduler->scheduleCacheInvalidation(collection);
1138 }
1139 
1141 {
1142  Q_D(const ResourceBase);
1143  Q_ASSERT_X(d->scheduler->currentTask().type == ResourceScheduler::SyncCollection,
1144  "ResourceBase::currentCollection()",
1145  "Trying to access current collection although no item retrieval is in progress");
1146  return d->currentCollection;
1147 }
1148 
1150 {
1151  Q_D(const ResourceBase);
1152  Q_ASSERT_X(d->scheduler->currentTask().type == ResourceScheduler::FetchItem,
1153  "ResourceBase::currentItem()",
1154  "Trying to access current item although no item retrieval is in progress");
1155  return d->scheduler->currentTask().items[0];
1156 }
1157 
1158 Item::List ResourceBase::currentItems() const
1159 {
1160  Q_D(const ResourceBase);
1161  Q_ASSERT_X(d->scheduler->currentTask().type == ResourceScheduler::FetchItems,
1162  "ResourceBase::currentItems()",
1163  "Trying to access current items although no items retrieval is in progress");
1164  return d->scheduler->currentTask().items;
1165 }
1166 
1168 {
1169  d_func()->scheduler->scheduleCollectionTreeSync();
1170 }
1171 
1173 {
1174  d_func()->scheduler->scheduleTagSync();
1175 }
1176 
1178 {
1179  d_func()->scheduler->scheduleRelationSync();
1180 }
1181 
1183 {
1184  Q_D(ResourceBase);
1185  if (d->mCurrentCollectionFetchJob) {
1186  d->mCurrentCollectionFetchJob->kill();
1187  d->mCurrentCollectionFetchJob = nullptr;
1188  }
1189  switch (d->scheduler->currentTask().type) {
1190  case ResourceScheduler::FetchItem:
1191  itemRetrieved(Item()); // sends the error reply and
1192  break;
1193  case ResourceScheduler::FetchItems:
1194  itemsRetrieved(Item::List());
1195  break;
1196  case ResourceScheduler::ChangeReplay:
1197  d->changeProcessed();
1198  break;
1199  case ResourceScheduler::SyncCollectionTree:
1200  case ResourceScheduler::SyncAll:
1201  if (d->mCollectionSyncer) {
1202  d->mCollectionSyncer->rollback();
1203  } else {
1204  d->scheduler->taskDone();
1205  }
1206  break;
1207  case ResourceScheduler::SyncCollection:
1208  if (d->mItemSyncer) {
1209  d->mItemSyncer->rollback();
1210  } else {
1211  d->scheduler->taskDone();
1212  }
1213  break;
1214  default:
1215  d->scheduler->taskDone();
1216  }
1217 }
1218 
1220 {
1221  cancelTask();
1222 
1223  Q_EMIT error(msg);
1224 }
1225 
1227 {
1228  Q_D(ResourceBase);
1229  qCDebug(AKONADIAGENTBASE_LOG) << "Deferring task" << d->scheduler->currentTask();
1230  // Deferring a CollectionSync is just not implemented.
1231  // We'd need to d->mItemSyncer->rollback() but also to NOT call taskDone in slotItemSyncDone() here...
1232  Q_ASSERT(!d->mItemSyncer);
1233  d->scheduler->deferTask();
1234 }
1235 
1237 {
1238  d_func()->scheduler->setOnline(state);
1239 }
1240 
1241 void ResourceBase::synchronizeCollection(qint64 collectionId)
1242 {
1243  synchronizeCollection(collectionId, false);
1244 }
1245 
1246 void ResourceBase::synchronizeCollection(qint64 collectionId, bool recursive)
1247 {
1248  Q_D(ResourceBase);
1250  job->setFetchScope(changeRecorder()->collectionFetchScope());
1251  job->fetchScope().setResource(identifier());
1253  connect(job, &KJob::result, d, &ResourceBasePrivate::slotCollectionListDone);
1254 }
1255 
1256 void ResourceBasePrivate::slotCollectionListDone(KJob *job)
1257 {
1258  if (!job->error()) {
1259  const Collection::List list = static_cast<CollectionFetchJob *>(job)->collections();
1260  for (const Collection &collection : list) {
1261  //We also get collections that should not be synced but are part of the tree.
1262  if (collection.shouldList(Collection::ListSync)) {
1263  if (mScheduleAttributeSyncBeforeCollectionSync) {
1264  scheduler->scheduleAttributesSync(collection);
1265  }
1266  scheduler->scheduleSync(collection);
1267  }
1268  }
1269  } else {
1270  qCWarning(AKONADIAGENTBASE_LOG) << "Failed to fetch collection for collection sync: " << job->errorString();
1271  }
1272 }
1273 
1275 {
1276  Q_D(ResourceBase);
1277  d->scheduler->scheduleAttributesSync(col);
1278 }
1279 
1281 {
1282  Q_D(ResourceBase);
1284  job->setFetchScope(changeRecorder()->collectionFetchScope());
1285  job->fetchScope().setResource(identifier());
1286  connect(job, &KJob::result, d, &ResourceBasePrivate::slotCollectionListForAttributesDone);
1287 }
1288 
1289 void ResourceBasePrivate::slotCollectionListForAttributesDone(KJob *job)
1290 {
1291  if (!job->error()) {
1292  const Collection::List list = static_cast<CollectionFetchJob *>(job)->collections();
1293  if (!list.isEmpty()) {
1294  const Collection &col = list.first();
1295  scheduler->scheduleAttributesSync(col);
1296  }
1297  }
1298  // TODO: error handling
1299 }
1300 
1302 {
1303  qCDebug(AKONADIAGENTBASE_LOG) << amount;
1304  Q_D(ResourceBase);
1306  if (d->mItemSyncer) {
1307  d->mItemSyncer->setTotalItems(amount);
1308  }
1309 }
1310 
1312 {
1313  Q_D(ResourceBase);
1314  if (d->mItemSyncer) {
1315  d->mItemSyncer->setDisableAutomaticDeliveryDone(disable);
1316  }
1317  d->mDisableAutomaticItemDeliveryDone = disable;
1318 }
1319 
1321 {
1322  Q_D(ResourceBase);
1323  d->createItemSyncInstanceIfMissing();
1324  if (d->mItemSyncer) {
1325  d->mItemSyncer->setStreamingEnabled(enable);
1326  }
1327 }
1328 
1329 void ResourceBase::itemsRetrieved(const Item::List &items)
1330 {
1331  Q_D(ResourceBase);
1332  if (d->scheduler->currentTask().type == ResourceScheduler::FetchItems) {
1333  auto *trx = new TransactionSequence(this);
1334  connect(trx, &KJob::result, d, &ResourceBasePrivate::slotItemSyncDone);
1335  for (const Item &item : items) {
1336  Q_ASSERT(item.parentCollection().isValid());
1337  if (item.isValid()) { // NOLINT(bugprone-branch-clone)
1338  new ItemModifyJob(item, trx);
1339  } else if (!item.remoteId().isEmpty()) {
1340  auto *job = new ItemCreateJob(item, item.parentCollection(), trx);
1341  job->setMerge(ItemCreateJob::RID);
1342  } else {
1343  // This should not happen, but just to be sure...
1344  new ItemModifyJob(item, trx);
1345  }
1346  }
1347  trx->commit();
1348  } else {
1349  d->createItemSyncInstanceIfMissing();
1350  if (d->mItemSyncer) {
1351  d->mItemSyncer->setFullSyncItems(items);
1352  }
1353  }
1354 }
1355 
1356 void ResourceBase::itemsRetrievedIncremental(const Item::List &changedItems,
1357  const Item::List &removedItems)
1358 {
1359  Q_D(ResourceBase);
1360  d->createItemSyncInstanceIfMissing();
1361  if (d->mItemSyncer) {
1362  d->mItemSyncer->setIncrementalSyncItems(changedItems, removedItems);
1363  }
1364 }
1365 
1366 void ResourceBasePrivate::slotItemSyncDone(KJob *job)
1367 {
1368  mItemSyncer = nullptr;
1369  Q_Q(ResourceBase);
1370  if (job->error() && job->error() != Job::UserCanceled) {
1371  Q_EMIT q->error(job->errorString());
1372  }
1373  if (scheduler->currentTask().type == ResourceScheduler::FetchItems) {
1374  scheduler->currentTask().sendDBusReplies((job->error() && job->error() != Job::UserCanceled) ? job->errorString() : QString());
1375  }
1376  scheduler->taskDone();
1377 }
1378 
1379 void ResourceBasePrivate::slotDelayedEmitProgress()
1380 {
1381  Q_Q(ResourceBase);
1382  if (mAutomaticProgressReporting) {
1383  Q_EMIT q->percent(mUnemittedProgress);
1384 
1385  for (const QVariantMap &statusMap : qAsConst(mUnemittedAdvancedStatus)) {
1386  Q_EMIT q->advancedStatus(statusMap);
1387  }
1388  }
1389  mUnemittedProgress = 0;
1390  mUnemittedAdvancedStatus.clear();
1391 }
1392 
1393 void ResourceBasePrivate::slotPercent(KJob *job, quint64 percent)
1394 {
1395  mUnemittedProgress = static_cast<int>(percent);
1396 
1397  const Collection collection = job->property("collection").value<Collection>();
1398  if (collection.isValid()) {
1399  QVariantMap statusMap;
1400  statusMap.insert(QStringLiteral("key"), QStringLiteral("collectionSyncProgress"));
1401  statusMap.insert(QStringLiteral("collectionId"), collection.id());
1402  statusMap.insert(QStringLiteral("percent"), static_cast<unsigned int>(percent));
1403 
1404  mUnemittedAdvancedStatus[collection.id()] = statusMap;
1405  }
1406  // deliver completion right away, intermediate progress at 1s intervals
1407  if (percent == 100U) {
1408  mProgressEmissionCompressor.stop();
1409  slotDelayedEmitProgress();
1410  } else if (!mProgressEmissionCompressor.isActive()) {
1411  mProgressEmissionCompressor.start();
1412  }
1413 }
1414 
1416 {
1417  Q_D(ResourceBase);
1418  d->mHierarchicalRid = enable;
1419 }
1420 
1421 void ResourceBase::scheduleCustomTask(QObject *receiver, const char *method, const QVariant &argument, SchedulePriority priority)
1422 {
1423  Q_D(ResourceBase);
1424  d->scheduler->scheduleCustomTask(receiver, method, argument, priority);
1425 }
1426 
1428 {
1429  Q_D(ResourceBase);
1430  d->scheduler->taskDone();
1431 }
1432 
1434 {
1435  collectionAttributesRetrieved(collection);
1436 }
1437 
1439 {
1440  Q_D(ResourceBase);
1441  d->scheduler->taskDone();
1442 }
1443 
1445 {
1446  Q_D(ResourceBase);
1447  d->scheduler->taskDone();
1448 }
1449 
1450 bool ResourceBase::retrieveItem(const Akonadi::Item &item, const QSet<QByteArray> &parts)
1451 {
1452  Q_UNUSED(item);
1453  Q_UNUSED(parts);
1454  // retrieveItem() can no longer be pure virtual, because then we could not mark
1455  // it as deprecated (i.e. implementations would still be forced to implement it),
1456  // so instead we assert here.
1457  // NOTE: Don't change to Q_ASSERT_X here: while the macro can be disabled at
1458  // compile time, we want to hit this assert *ALWAYS*.
1459  qt_assert_x("Akonadi::ResourceBase::retrieveItem()",
1460  "The base implementation of retrieveItem() must never be reached. "
1461  "You must implement either retrieveItem() or retrieveItems(Akonadi::Item::List, QSet<QByteArray>) overload "
1462  "to handle item retrieval requests.", __FILE__, __LINE__);
1463  return false;
1464 }
1465 
1466 bool ResourceBase::retrieveItems(const Akonadi::Item::List &items, const QSet<QByteArray> &parts)
1467 {
1468  Q_D(ResourceBase);
1469 
1470  // If we reach this implementation of retrieveItems() then it means that the
1471  // resource is still using the deprecated retrieveItem() method, so we explode
1472  // this to a myriad of tasks in scheduler and let them be processed one by one
1473 
1474  const qint64 id = d->scheduler->currentTask().serial;
1475  for (const auto &item : items) {
1476  d->scheduler->scheduleItemFetch(item, parts, d->scheduler->currentTask().dbusMsgs, id);
1477  }
1478  taskDone();
1479  return true;
1480 }
1481 
1483 {
1484 }
1485 
1487 {
1488  Q_D(ResourceBase);
1489  d->mItemTransactionMode = mode;
1490 }
1491 
1493 {
1494  Q_D(ResourceBase);
1495  if (!d->mItemSyncFetchScope) {
1496  d->mItemSyncFetchScope = new ItemFetchScope;
1497  }
1498  *(d->mItemSyncFetchScope) = fetchScope;
1499 }
1500 
1501 void ResourceBase::setItemMergingMode(ItemSync::MergeMode mode)
1502 {
1503  Q_D(ResourceBase);
1504  d->mItemMergeMode = mode;
1505 }
1506 
1508 {
1509  Q_D(ResourceBase);
1510  d->mAutomaticProgressReporting = enabled;
1511 }
1512 
1514 {
1515  Q_D(const ResourceBase);
1516  return d->dumpNotificationListToString();
1517 }
1518 
1520 {
1521  Q_D(const ResourceBase);
1522  return d->dumpToString();
1523 }
1524 
1526 {
1527  Q_D(const ResourceBase);
1528  d->dumpMemoryInfo();
1529 }
1530 
1532 {
1533  Q_D(const ResourceBase);
1534  return d->dumpMemoryInfoToString();
1535 }
1536 
1537 void ResourceBase::tagsRetrieved(const Tag::List &tags, const QHash<QString, Item::List> &tagMembers)
1538 {
1539  Q_D(ResourceBase);
1540  Q_ASSERT_X(d->scheduler->currentTask().type == ResourceScheduler::SyncTags ||
1541  d->scheduler->currentTask().type == ResourceScheduler::SyncAll ||
1542  d->scheduler->currentTask().type == ResourceScheduler::Custom,
1543  "ResourceBase::tagsRetrieved()",
1544  "Calling tagsRetrieved() although no tag retrieval is in progress");
1545  if (!d->mTagSyncer) {
1546  d->mTagSyncer = new TagSync(this);
1547  connect(d->mTagSyncer, qOverload<KJob *, unsigned long>(&KJob::percent), d, &ResourceBasePrivate::slotPercent); // NOLINT(google-runtime-int): ulong comes from KJob
1548  connect(d->mTagSyncer, &KJob::result, d, &ResourceBasePrivate::slotTagSyncDone);
1549  }
1550  d->mTagSyncer->setFullTagList(tags);
1551  d->mTagSyncer->setTagMembers(tagMembers);
1552 }
1553 
1554 void ResourceBasePrivate::slotTagSyncDone(KJob *job)
1555 {
1556  Q_Q(ResourceBase);
1557  mTagSyncer = nullptr;
1558  if (job->error()) {
1559  if (job->error() != Job::UserCanceled) {
1560  qCWarning(AKONADIAGENTBASE_LOG) << "TagSync failed: " << job->errorString();
1561  Q_EMIT q->error(job->errorString());
1562  }
1563  }
1564 
1565  scheduler->taskDone();
1566 }
1567 
1568 void ResourceBase::relationsRetrieved(const Relation::List &relations)
1569 {
1570  Q_D(ResourceBase);
1571  Q_ASSERT_X(d->scheduler->currentTask().type == ResourceScheduler::SyncRelations ||
1572  d->scheduler->currentTask().type == ResourceScheduler::SyncAll ||
1573  d->scheduler->currentTask().type == ResourceScheduler::Custom,
1574  "ResourceBase::relationsRetrieved()",
1575  "Calling relationsRetrieved() although no relation retrieval is in progress");
1576  if (!d->mRelationSyncer) {
1577  d->mRelationSyncer = new RelationSync(this);
1578  connect(d->mRelationSyncer, qOverload<KJob *, unsigned long>(&KJob::percent), d, &ResourceBasePrivate::slotPercent); // NOLINT(google-runtime-int): ulong comes from KJob
1579  connect(d->mRelationSyncer, &KJob::result, d, &ResourceBasePrivate::slotRelationSyncDone);
1580  }
1581  d->mRelationSyncer->setRemoteRelations(relations);
1582 }
1583 
1584 void ResourceBasePrivate::slotRelationSyncDone(KJob *job)
1585 {
1586  Q_Q(ResourceBase);
1587  mRelationSyncer = nullptr;
1588  if (job->error()) {
1589  if (job->error() != Job::UserCanceled) {
1590  qCWarning(AKONADIAGENTBASE_LOG) << "RelationSync failed: " << job->errorString();
1591  Q_EMIT q->error(job->errorString());
1592  }
1593  }
1594 
1595  scheduler->taskDone();
1596 }
1597 
1598 #include "resourcebase.moc"
1599 #include "moc_resourcebase.cpp"
~ResourceBase() override
Destroys the base resource.
Job that modifies a collection in the Akonadi storage.
void synchronizeRelations()
Refetches Relations.
virtual void retrieveItems(const Akonadi::Collection &collection)=0
Retrieve all (new/changed) items in collection collection.
void fetchAttribute(const QByteArray &type, bool fetch=true)
Sets whether the attribute of the given type should be fetched.
void setIgnoreRetrievalErrors(bool enabled)
Ignore retrieval errors while fetching items, and always deliver what is available.
virtual void retrieveCollectionAttributes(const Akonadi::Collection &collection)
Retrieve the attributes of a single collection from the backend.
virtual void abortActivity()
Abort any activity in progress in the backend.
void disableRevisionCheck()
Disables the check of the revision number.
void percent(int progress)
This signal should be emitted whenever the progress of an action in the agent (e.g.
virtual void retrieveCollections()=0
Retrieve the collection tree from the remote server and supply it via collectionsRetrieved() or colle...
Collection::List collections() const
Returns the list of fetched collection.
bool isValid() const
Returns whether the collection is valid.
Definition: collection.cpp:124
void abortRequested()
Emitted when another application has remotely asked the agent to abort its current operation...
QString dumpNotificationListToString() const
Dump the contents of the current ChangeReplay.
void nothingToReplay()
Emitted when replayNext() was called, but there was no valid change to replay.
QString displayName() const
Returns the display name (EntityDisplayAttribute::displayName()) if set, and Collection::name() other...
Definition: collection.cpp:217
void attributesSynchronized(qlonglong collectionId)
Emitted when a collection attributes synchronization has been completed.
QVector::iterator begin()
virtual AKONADIAGENTBASE_DEPRECATED bool retrieveItem(const Akonadi::Item &item, const QSet< QByteArray > &parts)
Retrieve a single item from the backend.
bool shouldList(ListPurpose purpose) const
Returns whether the collection should be listed or not for the specified purpose Takes enabled state ...
Definition: collection.cpp:401
void collectionsRetrievalDone()
Call this method to indicate you finished synchronizing the collection tree.
void setAutomaticProgressReporting(bool enabled)
Enable or disable automatic progress reporting.
void setCollectionStreamingEnabled(bool enable)
Enable collection streaming, that is collections don&#39;t have to be delivered at once as result of a re...
void synchronizeCollectionAttributes(qint64 id)
This method is called whenever the collection with the given id shall have its attributes synchronize...
virtual QString errorString() const
Only list direct sub-collections of the base collection.
CollectionFetchScope & fetchScope()
Returns the collection fetch scope.
void setResource(const QString &resource)
Sets a resource filter, that is only collections owned by the specified resource are retrieved...
Represents a collection of PIM items.
Definition: collection.h:63
void doSetOnline(bool online) override
Inherited from AgentBase.
Job that fetches collections from the Akonadi storage.
void itemsRetrieved(const Item::List &items)
Call this method to supply the full collection listing from the remote server.
void setListFilter(ListFilter)
Sets a filter for the collections to be listed.
void setDelayedReply(bool enable) const const
bool isEmpty() const const
static QString virtualMimeType()
Returns the mimetype used for virtual collections.
Definition: collection.cpp:300
QCommandLineOption addVersionOption()
void synchronized()
Emitted when a full synchronization has been completed.
QString message() const const
An Attribute that stores the special collection type of a collection.
T value() const const
void agentNameChanged(const QString &name)
This signal is emitted whenever the name of the agent has changed.
static QString mimeType()
Returns the mimetype used for collections.
Definition: collection.cpp:295
QDBusConnection sessionBus()
static void setApplicationDomain(const char *domain)
void setItemSynchronizationFetchScope(const ItemFetchScope &fetchScope)
Set the fetch scope applied for item synchronization.
T & first()
void setKeepLocalCollectionChanges(const QSet< QByteArray > &parts)
Allows to keep locally changed collection parts during the collection sync.
void setItemMergingMode(ItemSync::MergeMode mode)
Set merge mode for item sync&#39;ing.
void synchronizeCollectionTree()
Refetches the Collections.
void setTotalItems(int amount)
Call this method when you want to use the itemsRetrieved() method in streaming mode and indicate the ...
void nameChanged(const QString &name)
This signal is emitted whenever the name of the resource has changed.
void retrieveNextItemSyncBatch(int remainingBatchSize)
Emitted when the item synchronization processed the current batch and is ready for a new one...
Item::List currentItems() const
Returns the items that are currently retrieved.
void changesCommitted(const Item::List &items)
Resets the dirty flag of all given items and updates remote ids.
Item::List items() const
Returns the fetched items.
virtual void retrieveRelations()
Retrieve all relations from the backend.
The base class for all Akonadi agents and resources.
Definition: agentbase.h:71
Only fetch the base collection.
Syncs between items known to a client (usually a resource) and the Akonadi storage.
Definition: itemsync.h:41
void itemsRetrievalDone()
Call this method to indicate you finished synchronizing the current collection.
void deferTask()
Suspends the execution of the current task and tries again to execute it.
ItemFetchScope & fetchScope()
Returns the item fetch scope.
void timeout()
void exit(int returnCode)
void setFetchRemoteIdentification(bool retrieveRid)
Fetch remote identification for items.
void setAgentName(const QString &name)
This method is used to set the name of the agent.
Definition: agentbase.cpp:1225
SchedulePriority
Describes the scheduling priority of a task that has been queued for execution.
Definition: resourcebase.h:769
void synchronize()
This method is called whenever the resource should start synchronize all data.
QString fromLocal8Bit(const char *str, int size)
void setApplicationVersion(const QString &version)
KGuiItem clear()
Clears all queries from current thread.
Definition: querycache.cpp:107
void setScheduleAttributeSyncBeforeItemSync(bool)
Set to true to schedule an attribute sync before every item sync.
void error(const QString &message)
This signal shall be used to report errors.
virtual int status() const
This method returns the current status code of the agent.
void sendErrorReply(const QString &name, const QString &msg) const const
QVariant property(const char *name) const const
void finished(KJob *job)
Only retrieve the immediate parent collection.
QString i18nc(const char *context, const char *text, const TYPE &arg...)
void clearCache()
Call this method to remove all items and collections of the resource from the server cache...
void collectionAttributesRetrieved(const Collection &collection)
Call this method from retrieveCollectionAttributes() once the result is available.
CaseInsensitive
bool isEmpty() const const
bool isEmpty() const const
int removeAll(const T &value)
void collectionsRetrieved(const Collection::List &collections)
Call this to supply the full folder tree retrieved from the remote server.
void changesAdded()
Emitted when new changes are recorded.
void collectionRemoved(const Akonadi::Collection &collection)
This signal is emitted if a monitored collection has been removed from the Akonadi storage...
QCommandLineOption addHelpOption()
static Collection root()
Returns the root collection.
Definition: collection.cpp:290
AKONADIAGENTBASE_DEPRECATED Item currentItem() const
Returns the item that is currently retrieved.
void setApplicationDescription(const QString &description)
Job that deletes a collection in the Akonadi storage.
QCoreApplication * instance()
virtual void retrieveTags()
Retrieve all tags from the backend.
void itemsRetrievedIncremental(const Item::List &changedItems, const Item::List &removedItems)
Call this method to supply incrementally retrieved items from the remote server.
static QString agentServiceName(ServiceAgentType agentType, const QString &identifier)
Returns the namespaced D-Bus service name for an agent of type agentType with agent identifier identi...
bool isSet(const QString &name) const const
Job that creates a new item in the Akonadi storage.
Definition: itemcreatejob.h:62
unsigned long percent() const
Attribute * attribute(const QByteArray &name)
Returns the attribute of the given type name if available, 0 otherwise.
Definition: collection.cpp:179
void collectionTreeSynchronized()
Emitted when a collection tree synchronization has been completed.
void setFetchScope(const CollectionFetchScope &fetchScope)
Sets the collection fetch scope.
int compare(const QString &other, Qt::CaseSensitivity cs) const const
void synchronizeCollection(qint64 id)
This method is called whenever the collection with the given id shall be synchronized.
Specifies which parts of an item should be fetched from the Akonadi storage.
static void setApplicationData(const KAboutData &aboutData)
void setAncestorRetrieval(AncestorRetrieval ancestorDepth)
Sets how many levels of ancestor collections should be included in the retrieval. ...
QStringRef midRef(int position, int n) const const
QVariant fromValue(const T &value)
void reconnected()
This signal is emitted whenever the session has been reconnected to the server (e.g.
bool load(const QString &filename, const QString &directory, const QString &search_delimiters, const QString &suffix)
bool invokeMethod(QObject *obj, const char *member, Qt::ConnectionType type, QGenericReturnArgument ret, QGenericArgument val0, QGenericArgument val1, QGenericArgument val2, QGenericArgument val3, QGenericArgument val4, QGenericArgument val5, QGenericArgument val6, QGenericArgument val7, QGenericArgument val8, QGenericArgument val9)
void setItemTransactionMode(ItemSync::TransactionMode mode)
Set transaction mode for item sync&#39;ing.
void process(const QStringList &arguments)
bool hasAttribute(const QByteArray &name) const
Returns true if the collection has an attribute of the given type name, false otherwise.
Definition: collection.cpp:164
bool installTranslator(QTranslator *translationFile)
void fetchAttribute(const QByteArray &type, bool fetch=true)
Sets whether the attribute of the given type should be fetched.
QString i18n(const char *text, const TYPE &arg...)
QString identifier() const
Returns the instance identifier of this agent.
Definition: agentbase.cpp:1220
void setIgnorePayload(bool ignore)
Sets whether the payload of the modified item shall be omitted from transmission to the Akonadi stora...
const T & at(int i) const const
TransactionMode
Transaction mode used by ItemSync.
Definition: itemsync.h:156
void replayNext()
Replay the next change notification and erase the previous one from the record.
Id id() const
Returns the unique identifier of the collection.
Definition: collection.cpp:99
void changeCommitted(const Item &item)
Resets the dirty flag of the given item and updates the remote id.
Base class for jobs that need to run a sequence of sub-jobs in a transaction.
Definition: item.h:31
ChangeRecorder * changeRecorder() const
Returns the Akonadi::ChangeRecorder object used for monitoring.
Definition: agentbase.cpp:1265
void dumpMemoryInfo() const
Dumps memory usage information to stdout.
bool isEmpty() const const
QThread * currentThread()
void setHierarchicalRemoteIdentifiersEnabled(bool enable)
Indicate the use of hierarchical remote identifiers.
bool isOnline() const
Returns whether the agent is currently online.
void itemRetrieved(const Item &item)
Call this method from retrieveItem() once the result is available.
Helper integration between Akonadi and Qt.
void changeProcessed()
Marks the current change as processes and replays the next change if change recording is enabled (noo...
Definition: agentbase.cpp:1259
Job that modifies an existing item in the Akonadi storage.
Definition: itemmodifyjob.h:83
Job that fetches items from the Akonadi storage.
Definition: itemfetchjob.h:71
Job that modifies a tag in the Akonadi storage.
Definition: tagmodifyjob.h:23
An Akonadi Tag.
Definition: tag.h:26
QString remoteId() const
Returns the remote id of the collection.
Definition: collection.cpp:109
Merge by remote id.
Definition: itemcreatejob.h:89
bool addOption(const QCommandLineOption &option)
bool isEmpty() const const
QByteArray collectionType() const
Returns the special collections type of the collection.
QStringList contentMimeTypes() const
Returns a list of possible content mimetypes, e.g.
Definition: collection.cpp:243
static int init(int argc, char **argv)
Use this method in the main function of your resource application to initialize your resource subclas...
Definition: resourcebase.h:168
int itemSyncBatchSize() const
Returns the batch size used during the item sync.
void readyForNextBatch(int remainingBatchSize)
Signals the resource that new items can be delivered.
void setItemStreamingEnabled(bool enable)
Enable item streaming, which is disabled by default.
QString name() const
Returns the name of the resource.
void taskDone()
Indicate that the current task is finished.
QMap::iterator insert(const Key &key, const T &value)
void cancelTask()
Stops the execution of the current task and continues with the next one.
void setItemSyncBatchSize(int batchSize)
Set the batch size used during the item sync.
Collection currentCollection() const
Returns the collection that is currently synchronized.
QString resource() const
Returns the identifier of the resource owning the collection.
Definition: collection.cpp:305
void result(KJob *job)
QDBusError lastError() const const
The user canceled this job.
Definition: job.h:101
void setDisableAutomaticItemDeliveryDone(bool disable)
Disables the automatic completion of the item sync, based on the number of delivered items...
ResourceBase(const QString &id)
Creates a base resource.
const QDBusMessage & message() const const
The agent is working on something.
Definition: agentbase.h:424
void setTransactionMode(TransactionMode mode)
Set the transaction mode to use for this sync.
Definition: itemsync.cpp:527
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
void collectionsRetrievedIncremental(const Collection::List &changedCollections, const Collection::List &removedCollections)
Call this to supply incrementally retrieved collections from the remote server.
static QString addNamespace(const QString &string)
Adds the multi-instance namespace to string if required (with &#39;_&#39; as separator).
T qobject_cast(QObject *object)
QVector::iterator end()
QString value(const QString &optionName) const const
void setName(const QString &name)
This method is used to set the name of the resource.
Listing for synchronization.
Definition: collection.h:469
QString baseName() const const
QString dumpMemoryInfoToString() const
Returns a string with memory usage information.
QString agentName() const
Returns the name of the agent.
Definition: agentbase.cpp:1249
Q_EMITQ_EMIT
The base class for all Akonadi resources.
Definition: resourcebase.h:135
Only retrieve collections for synchronization, taking the local preference and enabled into account...
QString errorText() const
void setApplicationName(const QString &application)
bool isVirtual() const
Returns whether the collection is virtual, for example a search collection.
Definition: collection.cpp:351
QString dumpSchedulerToString() const
Dump the state of the scheduler.
int error() const
void scheduleCustomTask(QObject *receiver, const char *method, const QVariant &argument, SchedulePriority priority=Append)
Schedules a custom task in the internal scheduler.
void setCacheOnly(bool cacheOnly)
Sets whether payload data should be requested from remote sources or just from the local cache...
void synchronizeTags()
Refetches Tags.
void invalidateCache(const Collection &collection)
Call this method to invalidate all cached content in collection.
This file is part of the KDE documentation.
Documentation copyright © 1996-2020 The KDE developers.
Generated on Sun Aug 2 2020 23:15:24 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.