Akonadi

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

KDE's Doxygen guidelines are available online.