• Skip to content
  • Skip to link menu
KDE API Reference
  • KDE API Reference
  • kdepimlibs API Reference
  • KDE Home
  • Contact Us
 

akonadi

  • sources
  • kde-4.14
  • kdepimlibs
  • akonadi
collectionsync.cpp
1 /*
2  Copyright (c) 2007, 2009 Volker Krause <vkrause@kde.org>
3 
4  This library is free software; you can redistribute it and/or modify it
5  under the terms of the GNU Library General Public License as published by
6  the Free Software Foundation; either version 2 of the License, or (at your
7  option) any later version.
8 
9  This library is distributed in the hope that it will be useful, but WITHOUT
10  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
12  License for more details.
13 
14  You should have received a copy of the GNU Library General Public License
15  along with this library; see the file COPYING.LIB. If not, write to the
16  Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17  02110-1301, USA.
18 */
19 
20 #include "collectionsync_p.h"
21 #include "collection.h"
22 
23 #include "collectioncreatejob.h"
24 #include "collectiondeletejob.h"
25 #include "collectionfetchjob.h"
26 #include "collectionmodifyjob.h"
27 #include "collectionfetchscope.h"
28 #include "collectionmovejob.h"
29 
30 #include "cachepolicy.h"
31 
32 #include <kdebug.h>
33 #include <KLocalizedString>
34 #include <QtCore/QVariant>
35 
36 using namespace Akonadi;
37 
38 struct RemoteNode;
39 
43 struct LocalNode
44 {
45  LocalNode(const Collection &col)
46  : collection(col)
47  , processed(false)
48  {}
49 
50  ~LocalNode()
51  {
52  qDeleteAll(childNodes);
53  qDeleteAll(pendingRemoteNodes);
54  }
55 
56  Collection collection;
57  QList<LocalNode *> childNodes;
58  QHash<QString, LocalNode *> childRidMap;
62  QList<RemoteNode *> pendingRemoteNodes;
63  bool processed;
64 };
65 
66 Q_DECLARE_METATYPE(LocalNode *)
67 static const char LOCAL_NODE[] = "LocalNode";
68 
73 struct RemoteNode
74 {
75  RemoteNode(const Collection &col)
76  : collection(col)
77  {}
78 
79  Collection collection;
80 };
81 
82 Q_DECLARE_METATYPE(RemoteNode *)
83 static const char REMOTE_NODE[] = "RemoteNode";
84 
85 static const char CONTENTMIMETYPES[] = "CONTENTMIMETYPES";
86 
90 class CollectionSync::Private
91 {
92 public:
93  Private(CollectionSync *parent)
94  : q(parent)
95  , pendingJobs(0)
96  , progress(0)
97  , localRoot(0)
98  , currentTransaction(0)
99  , knownLocalCollections(0)
100  , incremental(false)
101  , streaming(false)
102  , hierarchicalRIDs(false)
103  , localListDone(false)
104  , deliveryDone(false)
105  {
106  }
107 
108  ~Private()
109  {
110  delete localRoot;
111  qDeleteAll(rootRemoteNodes);
112  }
113 
115  void resetNodeTree()
116  {
117  delete localRoot;
118  localRoot = new LocalNode(Collection::root());
119  localRoot->processed = true; // never try to delete that one
120  if (currentTransaction) {
121  // we are running the update transaction, initialize pending remote nodes
122  localRoot->pendingRemoteNodes.swap(rootRemoteNodes);
123  }
124 
125  localUidMap.clear();
126  localRidMap.clear();
127  localUidMap.insert(localRoot->collection.id(), localRoot);
128  if (!hierarchicalRIDs) {
129  localRidMap.insert(QString(), localRoot);
130  }
131  }
132 
134  LocalNode *createLocalNode(const Collection &col)
135  {
136  LocalNode *node = new LocalNode(col);
137  Q_ASSERT(!localUidMap.contains(col.id()));
138  localUidMap.insert(node->collection.id(), node);
139  if (!hierarchicalRIDs && !col.remoteId().isEmpty()) {
140  localRidMap.insert(node->collection.remoteId(), node);
141  }
142 
143  // add already existing children
144  if (localPendingCollections.contains(col.id())) {
145  QVector<Collection::Id> childIds = localPendingCollections.take(col.id());
146  foreach (Collection::Id childId, childIds) {
147  Q_ASSERT(localUidMap.contains(childId));
148  LocalNode *childNode = localUidMap.value(childId);
149  node->childNodes.append(childNode);
150  if (!childNode->collection.remoteId().isEmpty()) {
151  node->childRidMap.insert(childNode->collection.remoteId(), childNode);
152  }
153  }
154  }
155 
156  // set our parent and add ourselves as child
157  if (localUidMap.contains(col.parentCollection().id())) {
158  LocalNode *parentNode = localUidMap.value(col.parentCollection().id());
159  parentNode->childNodes.append(node);
160  if (!node->collection.remoteId().isEmpty()) {
161  parentNode->childRidMap.insert(node->collection.remoteId(), node);
162  }
163  } else {
164  localPendingCollections[col.parentCollection().id()].append(col.id());
165  }
166 
167  return node;
168  }
169 
171  void createRemoteNode(const Collection &col)
172  {
173  if (col.remoteId().isEmpty()) {
174  kWarning() << "Collection '" << col.name() << "' does not have a remote identifier - skipping";
175  return;
176  }
177  RemoteNode *node = new RemoteNode(col);
178  rootRemoteNodes.append(node);
179  }
180 
182  void localCollectionsReceived(const Akonadi::Collection::List &localCols)
183  {
184  foreach (const Collection &c, localCols) {
185  createLocalNode(c);
186  knownLocalCollections++;
187  }
188  }
189 
191  void localCollectionFetchResult(KJob *job)
192  {
193  if (job->error()) {
194  return; // handled by the base class
195  }
196 
197  // safety check: the local tree has to be connected
198  if (!localPendingCollections.isEmpty()) {
199  q->setError(Unknown);
200  q->setErrorText(i18n("Inconsistent local collection tree detected."));
201  q->emitResult();
202  return;
203  }
204 
205  localListDone = true;
206  execute();
207  }
208 
214  LocalNode *findLocalChildNodeByName(LocalNode *localParentNode, const QString &name) const
215  {
216  if (name.isEmpty()) { // shouldn't happen...
217  return 0;
218  }
219 
220  if (localParentNode == localRoot) { // possibly non-unique names on top-level
221  return 0;
222  }
223 
224  foreach (LocalNode *childNode, localParentNode->childNodes) {
225  // the restriction on empty RIDs can possibly removed, but for now I only understand the implication for this case
226  if (childNode->collection.name() == name && childNode->collection.remoteId().isEmpty()) {
227  return childNode;
228  }
229  }
230  return 0;
231  }
232 
237  LocalNode *findMatchingLocalNode(const Collection &collection) const
238  {
239  if (!hierarchicalRIDs) {
240  if (localRidMap.contains(collection.remoteId())) {
241  return localRidMap.value(collection.remoteId());
242  }
243  return 0;
244  } else {
245  if (collection.id() == Collection::root().id() || collection.remoteId() == Collection::root().remoteId()) {
246  return localRoot;
247  }
248  LocalNode *localParent = 0;
249  if (collection.parentCollection().id() < 0 && collection.parentCollection().remoteId().isEmpty()) {
250  kWarning() << "Remote collection without valid parent found: " << collection;
251  return 0;
252  }
253  if (collection.parentCollection().id() == Collection::root().id() || collection.parentCollection().remoteId() == Collection::root().remoteId()) {
254  localParent = localRoot;
255  } else {
256  localParent = findMatchingLocalNode(collection.parentCollection());
257  }
258 
259  if (localParent) {
260  if (localParent->childRidMap.contains(collection.remoteId())) {
261  return localParent->childRidMap.value(collection.remoteId());
262  }
263  // check if we have a local folder with a matching name and no RID, if so let's use that one
264  // we would get an error if we don't do this anyway, as we'd try to create two sibling nodes with the same name
265  if (LocalNode *recoveredLocalNode = findLocalChildNodeByName(localParent, collection.name())) {
266  kDebug() << "Recovering collection with lost RID:" << collection << recoveredLocalNode->collection;
267  return recoveredLocalNode;
268  }
269  }
270  return 0;
271  }
272  }
273 
279  LocalNode *findBestLocalAncestor(const Collection &collection, bool *exactMatch = 0)
280  {
281  if (!hierarchicalRIDs) {
282  return localRoot;
283  }
284  if (collection == Collection::root()) {
285  if (exactMatch) {
286  *exactMatch = true;
287  }
288  return localRoot;
289  }
290  if (collection.parentCollection().id() < 0 && collection.parentCollection().remoteId().isEmpty()) {
291  kWarning() << "Remote collection without valid parent found: " << collection;
292  return 0;
293  }
294  bool parentIsExact = false;
295  LocalNode *localParent = findBestLocalAncestor(collection.parentCollection(), &parentIsExact);
296  if (!parentIsExact) {
297  if (exactMatch) {
298  *exactMatch = false;
299  }
300  return localParent;
301  }
302  if (localParent->childRidMap.contains(collection.remoteId())) {
303  if (exactMatch) {
304  *exactMatch = true;
305  }
306  return localParent->childRidMap.value(collection.remoteId());
307  }
308  if (exactMatch) {
309  *exactMatch = false;
310  }
311  return localParent;
312  }
313 
317  bool checkPendingRemoteNodes() const
318  {
319  if (rootRemoteNodes.size() != knownLocalCollections) {
320  return true;
321  }
322 
323  foreach (RemoteNode *remoteNode, rootRemoteNodes) {
324  // every remote note should have a local node already
325  LocalNode *localNode = findMatchingLocalNode(remoteNode->collection);
326  if (localNode) {
327  if (checkLocalCollection(localNode, remoteNode)) {
328  return true;
329  }
330  } else {
331  return true;
332  }
333  }
334  return false;
335  }
336 
342  void processPendingRemoteNodes(LocalNode *_localRoot)
343  {
344  QList<RemoteNode *> pendingRemoteNodes(_localRoot->pendingRemoteNodes);
345  _localRoot->pendingRemoteNodes.clear();
346  QHash<LocalNode *, QList<RemoteNode *> > pendingCreations;
347  foreach (RemoteNode *remoteNode, pendingRemoteNodes) {
348  // step 1: see if we have a matching local node already
349  LocalNode *localNode = findMatchingLocalNode(remoteNode->collection);
350  if (localNode) {
351  Q_ASSERT(!localNode->processed);
352  updateLocalCollection(localNode, remoteNode);
353  continue;
354  }
355  // step 2: check if we have the parent at least, then we can create it
356  localNode = findMatchingLocalNode(remoteNode->collection.parentCollection());
357  if (localNode) {
358  pendingCreations[localNode].append(remoteNode);
359  continue;
360  }
361  // step 3: find the best matching ancestor and enqueue it for later processing
362  localNode = findBestLocalAncestor(remoteNode->collection);
363  if (!localNode) {
364  q->setError(Unknown);
365  q->setErrorText(i18n("Remote collection without root-terminated ancestor chain provided, resource is broken."));
366  q->emitResult();
367  return;
368  }
369  localNode->pendingRemoteNodes.append(remoteNode);
370  }
371 
372  // process the now possible collection creations
373  for (QHash<LocalNode *, QList<RemoteNode *> >::const_iterator it = pendingCreations.constBegin();
374  it != pendingCreations.constEnd(); ++it) {
375  createLocalCollections(it.key(), it.value());
376  }
377  }
378 
382  bool checkLocalCollection(LocalNode *localNode, RemoteNode *remoteNode) const
383  {
384  const Collection &localCollection = localNode->collection;
385  const Collection &remoteCollection = remoteNode->collection;
386 
387  if (!keepLocalChanges.contains(CONTENTMIMETYPES)) {
388  if (localCollection.contentMimeTypes().size() != remoteCollection.contentMimeTypes().size()) {
389  return true;
390  } else {
391  for (int i = 0; i < remoteCollection.contentMimeTypes().size(); i++) {
392  const QString &m = remoteCollection.contentMimeTypes().at(i);
393  if (!localCollection.contentMimeTypes().contains(m)) {
394  return true;
395  }
396  }
397  }
398  }
399 
400  if (localCollection.parentCollection().remoteId() != remoteCollection.parentCollection().remoteId()) {
401  return true;
402  }
403  if (localCollection.name() != remoteCollection.name()) {
404  return true;
405  }
406  if (localCollection.remoteId() != remoteCollection.remoteId()) {
407  return true;
408  }
409  if (localCollection.remoteRevision() != remoteCollection.remoteRevision()) {
410  return true;
411  }
412  if (!(localCollection.cachePolicy() == remoteCollection.cachePolicy())) {
413  return true;
414  }
415 
416  // CollectionModifyJob adds the remote attributes to the local collection
417  foreach (const Attribute *attr, remoteCollection.attributes()) {
418  const Attribute *localAttr = localCollection.attribute(attr->type());
419  if (localAttr && keepLocalChanges.contains(attr->type())) {
420  continue;
421  }
422  // The attribute must both exist and have equal contents
423  if (!localAttr || localAttr->serialized() != attr->serialized()) {
424  return true;
425  }
426  }
427 
428  return false;
429  }
430 
434  void updateLocalCollection(LocalNode *localNode, RemoteNode *remoteNode)
435  {
436  Collection upd(remoteNode->collection);
437  Q_ASSERT(!upd.remoteId().isEmpty());
438  Q_ASSERT(currentTransaction);
439  upd.setId(localNode->collection.id());
440  if (keepLocalChanges.contains(CONTENTMIMETYPES)) {
441  upd.setContentMimeTypes(localNode->collection.contentMimeTypes());
442  }
443  foreach (Attribute *remoteAttr, upd.attributes()) {
444  if (keepLocalChanges.contains(remoteAttr->type()) && localNode->collection.hasAttribute(remoteAttr->type())) {
445  //We don't want to overwrite the attribute changes with the defaults provided by the resource.
446  Attribute *localAttr = localNode->collection.attribute(remoteAttr->type());
447  upd.removeAttribute(localAttr->type());
448  upd.addAttribute(localAttr->clone());
449  }
450  }
451 
452  {
453  // ### HACK to work around the implicit move attempts of CollectionModifyJob
454  // which we do explicitly below
455  Collection c(upd);
456  c.setParentCollection(localNode->collection.parentCollection());
457  ++pendingJobs;
458  CollectionModifyJob *mod = new CollectionModifyJob(c, currentTransaction);
459  connect(mod, SIGNAL(result(KJob*)), q, SLOT(updateLocalCollectionResult(KJob*)));
460  }
461 
462  // detecting moves is only possible with global RIDs
463  if (!hierarchicalRIDs) {
464  LocalNode *oldParent = localUidMap.value(localNode->collection.parentCollection().id());
465  LocalNode *newParent = findMatchingLocalNode(remoteNode->collection.parentCollection());
466  // TODO: handle the newParent == 0 case correctly, ie. defer the move until the new
467  // local parent has been created
468  if (newParent && oldParent != newParent) {
469  ++pendingJobs;
470  CollectionMoveJob *move = new CollectionMoveJob(upd, newParent->collection, currentTransaction);
471  connect(move, SIGNAL(result(KJob*)), q, SLOT(updateLocalCollectionResult(KJob*)));
472  }
473  }
474 
475  localNode->processed = true;
476  delete remoteNode;
477  }
478 
479  void updateLocalCollectionResult(KJob *job)
480  {
481  --pendingJobs;
482  if (job->error()) {
483  return; // handled by the base class
484  }
485  if (qobject_cast<CollectionModifyJob *>(job)) {
486  ++progress;
487  }
488  checkDone();
489  }
490 
495  void createLocalCollections(LocalNode *localParent, QList<RemoteNode *> remoteNodes)
496  {
497  foreach (RemoteNode *remoteNode, remoteNodes) {
498  ++pendingJobs;
499  Collection col(remoteNode->collection);
500  Q_ASSERT(!col.remoteId().isEmpty());
501  col.setParentCollection(localParent->collection);
502  CollectionCreateJob *create = new CollectionCreateJob(col, currentTransaction);
503  create->setProperty(LOCAL_NODE, QVariant::fromValue(localParent));
504  create->setProperty(REMOTE_NODE, QVariant::fromValue(remoteNode));
505  connect(create, SIGNAL(result(KJob*)), q, SLOT(createLocalCollectionResult(KJob*)));
506  }
507  }
508 
509  void createLocalCollectionResult(KJob *job)
510  {
511  --pendingJobs;
512  if (job->error()) {
513  return; // handled by the base class
514  }
515 
516  const Collection newLocal = static_cast<CollectionCreateJob *>(job)->collection();
517  LocalNode *localNode = createLocalNode(newLocal);
518  localNode->processed = true;
519 
520  LocalNode *localParent = job->property(LOCAL_NODE).value<LocalNode *>();
521  Q_ASSERT(localParent->childNodes.contains(localNode));
522  RemoteNode *remoteNode = job->property(REMOTE_NODE).value<RemoteNode *>();
523  delete remoteNode;
524  ++progress;
525 
526  processPendingRemoteNodes(localParent);
527  if (!hierarchicalRIDs) {
528  processPendingRemoteNodes(localRoot);
529  }
530 
531  checkDone();
532  }
533 
537  bool hasProcessedChildren(LocalNode *localNode) const
538  {
539  if (localNode->processed) {
540  return true;
541  }
542  foreach (LocalNode *child, localNode->childNodes) {
543  if (hasProcessedChildren(child)) {
544  return true;
545  }
546  }
547  return false;
548  }
549 
554  Collection::List findUnprocessedLocalCollections(LocalNode *localNode) const
555  {
556  Collection::List rv;
557  if (!localNode->processed) {
558  if (hasProcessedChildren(localNode)) {
559  kWarning() << "Found unprocessed local node with processed children, excluding from deletion";
560  kWarning() << localNode->collection;
561  return rv;
562  }
563  if (localNode->collection.remoteId().isEmpty()) {
564  kWarning() << "Found unprocessed local node without remoteId, excluding from deletion";
565  kWarning() << localNode->collection;
566  return rv;
567  }
568  rv.append(localNode->collection);
569  return rv;
570  }
571 
572  foreach (LocalNode *child, localNode->childNodes) {
573  rv.append(findUnprocessedLocalCollections(child));
574  }
575  return rv;
576  }
577 
581  void deleteUnprocessedLocalNodes()
582  {
583  if (incremental) {
584  return;
585  }
586  const Collection::List cols = findUnprocessedLocalCollections(localRoot);
587  deleteLocalCollections(cols);
588  }
589 
594  void deleteLocalCollections(const Collection::List &cols)
595  {
596  q->setTotalAmount(KJob::Bytes, q->totalAmount(KJob::Bytes) + cols.size());
597  foreach (const Collection &col, cols) {
598  Q_ASSERT(!col.remoteId().isEmpty()); // empty RID -> stuff we haven't even written to the remote side yet
599 
600  ++pendingJobs;
601  Q_ASSERT(currentTransaction);
602  CollectionDeleteJob *job = new CollectionDeleteJob(col, currentTransaction);
603  connect(job, SIGNAL(result(KJob*)), q, SLOT(deleteLocalCollectionsResult(KJob*)));
604 
605  // It can happen that the groupware servers report us deleted collections
606  // twice, in this case this collection delete job will fail on the second try.
607  // To avoid a rollback of the complete transaction we gracefully allow the job
608  // to fail :)
609  currentTransaction->setIgnoreJobFailure(job);
610  }
611  }
612 
613  void deleteLocalCollectionsResult(KJob *)
614  {
615  --pendingJobs;
616 
617  ++progress;
618  checkDone();
619  }
620 
624  void checkUpdateNecessity()
625  {
626  bool updateNeeded = checkPendingRemoteNodes();
627  if (!updateNeeded) {
628  // We can end right now
629  q->emitResult();
630  return;
631  }
632 
633  // Since there are differences with the remote collections we need to sync. Start a transaction here.
634  Q_ASSERT(!currentTransaction);
635  currentTransaction = new TransactionSequence(q);
636  currentTransaction->setAutomaticCommittingEnabled(false);
637  q->connect(currentTransaction, SIGNAL(result(KJob*)), SLOT(transactionSequenceResult(KJob*)));
638 
639  // Now that a transaction is started we need to fetch local collections again and do the update
640  q->doStart();
641  }
642 
644  void transactionSequenceResult(KJob *job)
645  {
646  if (job->error()) {
647  return; // handled by the base class
648  }
649 
650  q->emitResult();
651  }
652 
656  void execute()
657  {
658  kDebug() << Q_FUNC_INFO << "localListDone: " << localListDone << " deliveryDone: " << deliveryDone;
659  if (!localListDone || !deliveryDone) {
660  return;
661  }
662 
663  // If a transaction is not started yet we are still checking if the update is
664  // actually needed.
665  if (!currentTransaction) {
666  checkUpdateNecessity();
667  return;
668  }
669 
670  // Since the transaction is already running we need to execute the update.
671  processPendingRemoteNodes(localRoot);
672 
673  if (!incremental && deliveryDone) {
674  deleteUnprocessedLocalNodes();
675  }
676 
677  if (!hierarchicalRIDs) {
678  deleteLocalCollections(removedRemoteCollections);
679  } else {
680  Collection::List localCols;
681  foreach (const Collection &c, removedRemoteCollections) {
682  LocalNode *node = findMatchingLocalNode(c);
683  if (node) {
684  localCols.append(node->collection);
685  }
686  }
687  deleteLocalCollections(localCols);
688  }
689  removedRemoteCollections.clear();
690 
691  checkDone();
692  }
693 
697  QList<RemoteNode *> findPendingRemoteNodes(LocalNode *localNode)
698  {
699  QList<RemoteNode *> rv;
700  rv.append(localNode->pendingRemoteNodes);
701  foreach (LocalNode *child, localNode->childNodes) {
702  rv.append(findPendingRemoteNodes(child));
703  }
704  return rv;
705  }
706 
711  void checkDone()
712  {
713  q->setProcessedAmount(KJob::Bytes, progress);
714 
715  // still running jobs or not fully delivered local/remote state
716  if (!deliveryDone || pendingJobs > 0 || !localListDone) {
717  return;
718  }
719 
720  // safety check: there must be no pending remote nodes anymore
721  QList<RemoteNode *> orphans = findPendingRemoteNodes(localRoot);
722  if (!orphans.isEmpty()) {
723  q->setError(Unknown);
724  q->setErrorText(i18n("Found unresolved orphan collections"));
725  foreach (RemoteNode *orphan, orphans) {
726  kDebug() << "found orphan collection:" << orphan->collection;
727  }
728  q->emitResult();
729  return;
730  }
731 
732  kDebug() << Q_FUNC_INFO << "q->commit()";
733  Q_ASSERT(currentTransaction);
734  currentTransaction->commit();
735  }
736 
737  CollectionSync *q;
738 
739  QString resourceId;
740 
741  int pendingJobs;
742  int progress;
743 
744  LocalNode *localRoot;
745  TransactionSequence *currentTransaction;
746  QHash<Collection::Id, LocalNode *> localUidMap;
747  QHash<QString, LocalNode *> localRidMap;
748 
749  // temporary during build-up of the local node tree, must be empty afterwards
750  QHash<Collection::Id, QVector<Collection::Id> > localPendingCollections;
751 
752  // removed remote collections in incremental mode
753  Collection::List removedRemoteCollections;
754 
755  // used to store the list of remote nodes passed by the user
756  QList<RemoteNode *> rootRemoteNodes;
757 
758  // keep track of the total number of local collections that are known
759  // only used during the preliminary check to see if updating is needed
760  int knownLocalCollections;
761 
762  bool incremental;
763  bool streaming;
764  bool hierarchicalRIDs;
765 
766  bool localListDone;
767  bool deliveryDone;
768 
769  // List of parts where local changes should not be overwritten
770  QSet<QByteArray> keepLocalChanges;
771 };
772 
773 CollectionSync::CollectionSync(const QString &resourceId, QObject *parent)
774  : Job(parent)
775  , d(new Private(this))
776 {
777  d->resourceId = resourceId;
778  setTotalAmount(KJob::Bytes, 0);
779 }
780 
781 CollectionSync::~CollectionSync()
782 {
783  delete d;
784 }
785 
786 void CollectionSync::setRemoteCollections(const Collection::List &remoteCollections)
787 {
788  setTotalAmount(KJob::Bytes, totalAmount(KJob::Bytes) + remoteCollections.count());
789  foreach (const Collection &c, remoteCollections) {
790  d->createRemoteNode(c);
791  }
792 
793  if (!d->streaming) {
794  d->deliveryDone = true;
795  }
796  d->execute();
797 }
798 
799 void CollectionSync::setRemoteCollections(const Collection::List &changedCollections, const Collection::List &removedCollections)
800 {
801  setTotalAmount(KJob::Bytes, totalAmount(KJob::Bytes) + changedCollections.count());
802  d->incremental = true;
803  foreach (const Collection &c, changedCollections) {
804  d->createRemoteNode(c);
805  }
806  d->removedRemoteCollections += removedCollections;
807 
808  if (!d->streaming) {
809  d->deliveryDone = true;
810  }
811  d->execute();
812 }
813 
814 void CollectionSync::doStart()
815 {
816  d->resetNodeTree();
817  Job *parent = (d->currentTransaction ? static_cast<Job *>(d->currentTransaction) : static_cast<Job *>(this));
818  CollectionFetchJob *job = new CollectionFetchJob(Collection::root(), CollectionFetchJob::Recursive, parent);
819  job->fetchScope().setResource(d->resourceId);
820  job->fetchScope().setIncludeUnsubscribed(true);
821  job->fetchScope().setAncestorRetrieval(CollectionFetchScope::Parent);
822  connect(job, SIGNAL(collectionsReceived(Akonadi::Collection::List)),
823  SLOT(localCollectionsReceived(Akonadi::Collection::List)));
824  connect(job, SIGNAL(result(KJob*)), SLOT(localCollectionFetchResult(KJob*)));
825 }
826 
827 void CollectionSync::setStreamingEnabled(bool streaming)
828 {
829  d->streaming = streaming;
830 }
831 
832 void CollectionSync::retrievalDone()
833 {
834  d->deliveryDone = true;
835  d->execute();
836 }
837 
838 void CollectionSync::setHierarchicalRemoteIds(bool hierarchical)
839 {
840  d->hierarchicalRIDs = hierarchical;
841 }
842 
843 void CollectionSync::rollback()
844 {
845  if (d->currentTransaction) {
846  d->currentTransaction->rollback();
847  }
848 }
849 
850 void CollectionSync::setKeepLocalChanges(const QSet<QByteArray> &parts)
851 {
852  d->keepLocalChanges = parts;
853 }
854 
855 #include "moc_collectionsync_p.cpp"
Akonadi::CollectionModifyJob
Job that modifies a collection in the Akonadi storage.
Definition: collectionmodifyjob.h:82
Akonadi::CollectionSync::rollback
void rollback()
Do a rollback operation if needed.
Definition: collectionsync.cpp:843
Akonadi::CollectionFetchScope::setAncestorRetrieval
void setAncestorRetrieval(AncestorRetrieval ancestorDepth)
Sets how many levels of ancestor collections should be included in the retrieval. ...
Definition: collectionfetchscope.cpp:138
Akonadi::CollectionSync::setKeepLocalChanges
void setKeepLocalChanges(const QSet< QByteArray > &parts)
Allows to specify parts of the collection that should not be changed if locally available.
Definition: collectionsync.cpp:850
Akonadi::Collection::name
QString name() const
Returns the i18n'ed name of the collection.
Definition: collection.cpp:81
Akonadi::CollectionFetchScope::setIncludeUnsubscribed
AKONADI_DEPRECATED void setIncludeUnsubscribed(bool include)
Sets whether unsubscribed collections should be included in the collection listing.
Definition: collectionfetchscope.cpp:94
Akonadi::CollectionMoveJob
Job that moves a collection in the Akonadi storage to a new parent collection.
Definition: collectionmovejob.h:50
Akonadi::CollectionFetchJob::fetchScope
CollectionFetchScope & fetchScope()
Returns the collection fetch scope.
Definition: collectionfetchjob.cpp:439
Akonadi::CollectionFetchScope::setResource
void setResource(const QString &resource)
Sets a resource filter, that is only collections owned by the specified resource are retrieved...
Definition: collectionfetchscope.cpp:118
Akonadi::Collection
Represents a collection of PIM items.
Definition: collection.h:75
QList::at
const T & at(int i) const
Akonadi::CollectionFetchJob
Job that fetches collections from the Akonadi storage.
Definition: collectionfetchjob.h:53
QStringList::contains
bool contains(const QString &str, Qt::CaseSensitivity cs) const
Akonadi::Entity::Id
qint64 Id
Describes the unique id type.
Definition: entity.h:65
Akonadi::Job
Base class for all actions in the Akonadi storage.
Definition: job.h:86
Akonadi::CollectionSync::~CollectionSync
~CollectionSync()
Destroys this job.
Definition: collectionsync.cpp:781
Akonadi::Attribute
Provides interface for custom attributes for Entity.
Definition: attribute.h:138
Akonadi::CollectionFetchScope::Parent
Only retrieve the immediate parent collection.
Definition: collectionfetchscope.h:76
Akonadi::Entity::setParentCollection
void setParentCollection(const Collection &parent)
Set the parent collection of this object.
Definition: entity.cpp:194
Akonadi::CollectionSync::setHierarchicalRemoteIds
void setHierarchicalRemoteIds(bool hierarchical)
Indicate whether the resource supplies collections with hierarchical or global remote identifiers...
Definition: collectionsync.cpp:838
Akonadi::CollectionSync::setRemoteCollections
void setRemoteCollections(const Collection::List &remoteCollections)
Sets the result of a full remote collection listing.
Definition: collectionsync.cpp:786
QList::size
int size() const
QVector::value
T value(int i) const
Akonadi::Entity::attribute
Attribute * attribute(const QByteArray &name) const
Returns the attribute of the given type name if available, 0 otherwise.
Definition: entity.cpp:167
QList::count
int count(const T &value) const
QList::append
void append(const T &value)
QHash::constEnd
const_iterator constEnd() const
Akonadi::Entity::parentCollection
Collection parentCollection() const
Returns the parent collection of this object.
Definition: entity.cpp:185
QHash< QString, LocalNode * >
QObject
Akonadi::CollectionSync::doStart
void doStart()
This method must be reimplemented in the concrete jobs.
Definition: collectionsync.cpp:814
Akonadi::Entity::remoteId
QString remoteId() const
Returns the remote id of the entity.
Definition: entity.cpp:82
QList::isEmpty
bool isEmpty() const
QString::isEmpty
bool isEmpty() const
Akonadi::CollectionSync::retrievalDone
void retrievalDone()
Indicate that all collections have been retrieved in streaming mode.
Definition: collectionsync.cpp:832
Akonadi::Entity::remoteRevision
QString remoteRevision() const
Returns the remote revision of the entity.
Definition: entity.cpp:92
Akonadi::Collection::root
static Collection root()
Returns the root collection.
Definition: collection.cpp:192
Akonadi::CollectionDeleteJob
Job that deletes a collection in the Akonadi storage.
Definition: collectiondeletejob.h:63
QSet< QByteArray >
QString
QList< LocalNode * >
Akonadi::Entity::id
Id id() const
Returns the unique identifier of the entity.
Definition: entity.cpp:72
Akonadi::CollectionSync::CollectionSync
CollectionSync(const QString &resourceId, QObject *parent=0)
Creates a new collection synchronzier.
Definition: collectionsync.cpp:773
QVariant::fromValue
QVariant fromValue(const T &value)
QHash::constBegin
const_iterator constBegin() const
Akonadi::TransactionSequence
Base class for jobs that need to run a sequence of sub-jobs in a transaction.
Definition: transactionsequence.h:69
QVector
Definition: kcolumnfilterproxymodel_p.h:27
Akonadi::CollectionSync
Definition: collectionsync_p.h:53
Akonadi::Collection::contentMimeTypes
QStringList contentMimeTypes() const
Returns a list of possible content mimetypes, e.g.
Definition: collection.cpp:115
Akonadi::Collection::cachePolicy
CachePolicy cachePolicy() const
Returns the cache policy of the collection.
Definition: collection.cpp:249
Akonadi::Attribute::serialized
virtual QByteArray serialized() const =0
Returns a QByteArray representation of the attribute which will be storaged.
Akonadi::CollectionCreateJob
Job that creates a new collection in the Akonadi storage.
Definition: collectioncreatejob.h:52
Akonadi::Attribute::clone
virtual Attribute * clone() const =0
Creates a copy of this attribute.
Akonadi::Attribute::type
virtual QByteArray type() const =0
Returns the type of the attribute.
Akonadi::CollectionFetchJob::Recursive
List all sub-collections.
Definition: collectionfetchjob.h:64
Akonadi::CollectionSync::setStreamingEnabled
void setStreamingEnabled(bool streaming)
Enables streaming, that is not all collections are delivered at once.
Definition: collectionsync.cpp:827
Akonadi::Entity::attributes
Attribute::List attributes() const
Returns a list of all attributes of the entity.
Definition: entity.cpp:153
This file is part of the KDE documentation.
Documentation copyright © 1996-2020 The KDE developers.
Generated on Mon Jun 22 2020 13:38:02 by doxygen 1.8.7 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

akonadi

Skip menu "akonadi"
  • Main Page
  • Namespace List
  • Namespace Members
  • Alphabetical List
  • Class List
  • Class Hierarchy
  • Class Members
  • File List
  • Modules
  • Related Pages

kdepimlibs API Reference

Skip menu "kdepimlibs API Reference"
  • akonadi
  •   contact
  •   kmime
  •   socialutils
  • kabc
  • kalarmcal
  • kblog
  • kcal
  • kcalcore
  • kcalutils
  • kholidays
  • kimap
  • kioslave
  •   imap4
  •   mbox
  •   nntp
  • kldap
  • kmbox
  • kmime
  • kontactinterface
  • kpimidentities
  • kpimtextedit
  • kpimutils
  • kresources
  • ktnef
  • kxmlrpcclient
  • mailtransport
  • microblog
  • qgpgme
  • syndication
  •   atom
  •   rdf
  •   rss2

Search



Report problems with this website to our bug tracking system.
Contact the specific authors with questions and comments about the page contents.

KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal