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

akonadi

  • sources
  • kde-4.12
  • 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 #include "entitydisplayattribute.h"
30 
31 #include "cachepolicy.h"
32 
33 #include <kdebug.h>
34 #include <KLocalizedString>
35 #include <QtCore/QVariant>
36 
37 using namespace Akonadi;
38 
39 struct RemoteNode;
40 
44 struct LocalNode
45 {
46  LocalNode( const Collection &col ) :
47  collection( col ),
48  processed( false )
49  {}
50 
51  ~LocalNode()
52  {
53  qDeleteAll( childNodes );
54  qDeleteAll( pendingRemoteNodes );
55  }
56 
57  Collection collection;
58  QList<LocalNode*> childNodes;
59  QHash<QString, LocalNode*> childRidMap;
63  QList<RemoteNode*> pendingRemoteNodes;
64  bool processed;
65 };
66 
67 Q_DECLARE_METATYPE( LocalNode* )
68 static const char LOCAL_NODE[] = "LocalNode";
69 
74 struct RemoteNode
75 {
76  RemoteNode( const Collection &col ) :
77  collection( col )
78  {}
79 
80  Collection collection;
81 };
82 
83 Q_DECLARE_METATYPE( RemoteNode* )
84 static const char REMOTE_NODE[] = "RemoteNode";
85 
89 class CollectionSync::Private
90 {
91  public:
92  Private( CollectionSync *parent ) :
93  q( parent ),
94  pendingJobs( 0 ),
95  progress( 0 ),
96  localRoot( 0 ),
97  currentTransaction( 0 ),
98  knownLocalCollections( 0 ),
99  incremental( false ),
100  streaming( false ),
101  hierarchicalRIDs( false ),
102  localListDone( false ),
103  deliveryDone( false )
104  {
105  }
106 
107  ~Private()
108  {
109  delete localRoot;
110  qDeleteAll( rootRemoteNodes );
111  }
112 
114  void resetNodeTree()
115  {
116  delete localRoot;
117  localRoot = new LocalNode( Collection::root() );
118  localRoot->processed = true; // never try to delete that one
119  if ( currentTransaction ) {
120  // we are running the update transaction, initialize pending remote nodes
121  localRoot->pendingRemoteNodes.swap( rootRemoteNodes );
122  }
123 
124  localUidMap.clear();
125  localRidMap.clear();
126  localUidMap.insert( localRoot->collection.id(), localRoot );
127  if ( !hierarchicalRIDs ) {
128  localRidMap.insert( QString(), localRoot );
129  }
130  }
131 
133  LocalNode* createLocalNode( const Collection &col )
134  {
135  LocalNode *node = new LocalNode( col );
136  Q_ASSERT( !localUidMap.contains( col.id() ) );
137  localUidMap.insert( node->collection.id(), node );
138  if ( !hierarchicalRIDs && !col.remoteId().isEmpty() ) {
139  localRidMap.insert( node->collection.remoteId(), node );
140  }
141 
142  // add already existing children
143  if ( localPendingCollections.contains( col.id() ) ) {
144  QVector<Collection::Id> childIds = localPendingCollections.take( col.id() );
145  foreach ( Collection::Id childId, childIds ) {
146  Q_ASSERT( localUidMap.contains( childId ) );
147  LocalNode *childNode = localUidMap.value( childId );
148  node->childNodes.append( childNode );
149  if ( !childNode->collection.remoteId().isEmpty() ) {
150  node->childRidMap.insert( childNode->collection.remoteId(), childNode );
151  }
152  }
153  }
154 
155  // set our parent and add ourselves as child
156  if ( localUidMap.contains( col.parentCollection().id() ) ) {
157  LocalNode* parentNode = localUidMap.value( col.parentCollection().id() );
158  parentNode->childNodes.append( node );
159  if ( !node->collection.remoteId().isEmpty() ) {
160  parentNode->childRidMap.insert( node->collection.remoteId(), node );
161  }
162  } else {
163  localPendingCollections[ col.parentCollection().id() ].append( col.id() );
164  }
165 
166  return node;
167  }
168 
170  void createRemoteNode( const Collection &col )
171  {
172  if ( col.remoteId().isEmpty() ) {
173  kWarning() << "Collection '" << col.name() << "' does not have a remote identifier - skipping";
174  return;
175  }
176  RemoteNode *node = new RemoteNode( col );
177  rootRemoteNodes.append( node );
178  }
179 
181  void localCollectionsReceived( const Akonadi::Collection::List &localCols )
182  {
183  foreach ( const Collection &c, localCols ) {
184  createLocalNode( c );
185  knownLocalCollections++;
186  }
187  }
188 
190  void localCollectionFetchResult( KJob *job )
191  {
192  if ( job->error() ) {
193  return; // handled by the base class
194  }
195 
196  // safety check: the local tree has to be connected
197  if ( !localPendingCollections.isEmpty() ) {
198  q->setError( Unknown );
199  q->setErrorText( i18n( "Inconsistent local collection tree detected." ) );
200  q->emitResult();
201  return;
202  }
203 
204  localListDone = true;
205  execute();
206  }
207 
213  LocalNode* findLocalChildNodeByName( LocalNode *localParentNode, const QString &name ) const
214  {
215  if ( name.isEmpty() ) { // shouldn't happen...
216  return 0;
217  }
218 
219  if ( localParentNode == localRoot ) { // possibly non-unique names on top-level
220  return 0;
221  }
222 
223  foreach ( LocalNode *childNode, localParentNode->childNodes ) {
224  // the restriction on empty RIDs can possibly removed, but for now I only understand the implication for this case
225  if ( childNode->collection.name() == name && childNode->collection.remoteId().isEmpty() ) {
226  return childNode;
227  }
228  }
229  return 0;
230  }
231 
236  LocalNode* findMatchingLocalNode( const Collection &collection ) const
237  {
238  if ( !hierarchicalRIDs ) {
239  if ( localRidMap.contains( collection.remoteId() ) ) {
240  return localRidMap.value( collection.remoteId() );
241  }
242  return 0;
243  } else {
244  if ( collection.id() == Collection::root().id() || collection.remoteId() == Collection::root().remoteId() ) {
245  return localRoot;
246  }
247  LocalNode *localParent = 0;
248  if ( collection.parentCollection().id() < 0 && collection.parentCollection().remoteId().isEmpty() ) {
249  kWarning() << "Remote collection without valid parent found: " << collection;
250  return 0;
251  }
252  if ( collection.parentCollection().id() == Collection::root().id() || collection.parentCollection().remoteId() == Collection::root().remoteId() ) {
253  localParent = localRoot;
254  } else {
255  localParent = findMatchingLocalNode( collection.parentCollection() );
256  }
257 
258  if ( localParent ) {
259  if ( localParent->childRidMap.contains( collection.remoteId() ) ) {
260  return localParent->childRidMap.value( collection.remoteId() );
261  }
262  // check if we have a local folder with a matching name and no RID, if so let's use that one
263  // 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
264  if ( LocalNode *recoveredLocalNode = findLocalChildNodeByName( localParent, collection.name() ) ) {
265  kDebug() << "Recovering collection with lost RID:" << collection << recoveredLocalNode->collection;
266  return recoveredLocalNode;
267  }
268  }
269  return 0;
270  }
271  }
272 
278  LocalNode* findBestLocalAncestor( const Collection &collection, bool *exactMatch = 0 )
279  {
280  if ( !hierarchicalRIDs ) {
281  return localRoot;
282  }
283  if ( collection == Collection::root() ) {
284  if ( exactMatch ) {
285  *exactMatch = true;
286  }
287  return localRoot;
288  }
289  if ( collection.parentCollection().id() < 0 && collection.parentCollection().remoteId().isEmpty() ) {
290  kWarning() << "Remote collection without valid parent found: " << collection;
291  return 0;
292  }
293  bool parentIsExact = false;
294  LocalNode *localParent = findBestLocalAncestor( collection.parentCollection(), &parentIsExact );
295  if ( !parentIsExact ) {
296  if ( exactMatch ) {
297  *exactMatch = false;
298  }
299  return localParent;
300  }
301  if ( localParent->childRidMap.contains( collection.remoteId() ) ) {
302  if ( exactMatch ) {
303  *exactMatch = true;
304  }
305  return localParent->childRidMap.value( collection.remoteId() );
306  }
307  if ( exactMatch ) {
308  *exactMatch = false;
309  }
310  return localParent;
311  }
312 
316  bool checkPendingRemoteNodes() const
317  {
318  if ( rootRemoteNodes.size() != knownLocalCollections ) {
319  return true;
320  }
321 
322  foreach ( RemoteNode *remoteNode, rootRemoteNodes ) {
323  // every remote note should have a local node already
324  LocalNode *localNode = findMatchingLocalNode( remoteNode->collection );
325  if ( localNode ) {
326  if ( checkLocalCollection( localNode, remoteNode ) ) {
327  return true;
328  }
329  } else {
330  return true;
331  }
332  }
333  return false;
334  }
335 
341  void processPendingRemoteNodes( LocalNode *_localRoot )
342  {
343  QList<RemoteNode*> pendingRemoteNodes( _localRoot->pendingRemoteNodes );
344  _localRoot->pendingRemoteNodes.clear();
345  QHash<LocalNode*, QList<RemoteNode*> > pendingCreations;
346  foreach ( RemoteNode *remoteNode, pendingRemoteNodes ) {
347  // step 1: see if we have a matching local node already
348  LocalNode *localNode = findMatchingLocalNode( remoteNode->collection );
349  if ( localNode ) {
350  Q_ASSERT( !localNode->processed );
351  updateLocalCollection( localNode, remoteNode );
352  continue;
353  }
354  // step 2: check if we have the parent at least, then we can create it
355  localNode = findMatchingLocalNode( remoteNode->collection.parentCollection() );
356  if ( localNode ) {
357  pendingCreations[localNode].append( remoteNode );
358  continue;
359  }
360  // step 3: find the best matching ancestor and enqueue it for later processing
361  localNode = findBestLocalAncestor( remoteNode->collection );
362  if ( !localNode ) {
363  q->setError( Unknown );
364  q->setErrorText( i18n( "Remote collection without root-terminated ancestor chain provided, resource is broken." ) );
365  q->emitResult();
366  return;
367  }
368  localNode->pendingRemoteNodes.append( remoteNode );
369  }
370 
371  // process the now possible collection creations
372  for ( QHash<LocalNode*, QList<RemoteNode*> >::const_iterator it = pendingCreations.constBegin();
373  it != pendingCreations.constEnd(); ++it ) {
374  createLocalCollections( it.key(), it.value() );
375  }
376  }
377 
381  bool checkLocalCollection( LocalNode *localNode, RemoteNode *remoteNode ) const
382  {
383  const Collection &localCollection = localNode->collection;
384  const Collection &remoteCollection = remoteNode->collection;
385 
386  if ( localCollection.contentMimeTypes().size() != remoteCollection.contentMimeTypes().size() ) {
387  return true;
388  } else {
389  for ( int i = 0; i < remoteCollection.contentMimeTypes().size(); i++ ) {
390  const QString &m = remoteCollection.contentMimeTypes().at( i );
391  if( !localCollection.contentMimeTypes().contains( m ) ) {
392  return true;
393  }
394  }
395  }
396 
397  if ( localCollection.parentCollection().remoteId() != remoteCollection.parentCollection().remoteId() ) {
398  return true;
399  }
400  if ( localCollection.name() != remoteCollection.name() ) {
401  return true;
402  }
403  if ( localCollection.remoteId() != remoteCollection.remoteId() ) {
404  return true;
405  }
406  if ( localCollection.remoteRevision() != remoteCollection.remoteRevision() ) {
407  return true;
408  }
409  if ( !(localCollection.cachePolicy() == remoteCollection.cachePolicy()) ) {
410  return true;
411  }
412 
413  // CollectionModifyJob adds the remote attributes to the local collection
414  foreach ( const Attribute* attr, remoteCollection.attributes() ) {
415  const Attribute* localAttr = localCollection.attribute( attr->type() );
416  // The attribute must both exist and have equal contents
417  if ( !localAttr || localAttr->serialized() != attr->serialized() ) {
418  return true;
419  }
420  }
421 
422  return false;
423  }
424 
428  void updateLocalCollection( LocalNode *localNode, RemoteNode *remoteNode )
429  {
430  Collection upd( remoteNode->collection );
431  Q_ASSERT( !upd.remoteId().isEmpty() );
432  Q_ASSERT( currentTransaction );
433  upd.setId( localNode->collection.id() );
434  if ( localNode->collection.attribute<EntityDisplayAttribute>() ) {
435  upd.removeAttribute<EntityDisplayAttribute>();
436  upd.addAttribute( localNode->collection.attribute<EntityDisplayAttribute>()->clone() );
437  }
438 
439  {
440  // ### HACK to work around the implicit move attempts of CollectionModifyJob
441  // which we do explicitly below
442  Collection c( upd );
443  c.setParentCollection( localNode->collection.parentCollection() );
444  ++pendingJobs;
445  CollectionModifyJob *mod = new CollectionModifyJob( c, currentTransaction );
446  connect( mod, SIGNAL(result(KJob*)), q, SLOT(updateLocalCollectionResult(KJob*)) );
447  }
448 
449  // detecting moves is only possible with global RIDs
450  if ( !hierarchicalRIDs ) {
451  LocalNode *oldParent = localUidMap.value( localNode->collection.parentCollection().id() );
452  LocalNode *newParent = findMatchingLocalNode( remoteNode->collection.parentCollection() );
453  // TODO: handle the newParent == 0 case correctly, ie. defer the move until the new
454  // local parent has been created
455  if ( newParent && oldParent != newParent ) {
456  ++pendingJobs;
457  CollectionMoveJob *move = new CollectionMoveJob( upd, newParent->collection, currentTransaction );
458  connect( move, SIGNAL(result(KJob*)), q, SLOT(updateLocalCollectionResult(KJob*)) );
459  }
460  }
461 
462  localNode->processed = true;
463  delete remoteNode;
464  }
465 
466  void updateLocalCollectionResult( KJob* job )
467  {
468  --pendingJobs;
469  if ( job->error() ) {
470  return; // handled by the base class
471  }
472  if ( qobject_cast<CollectionModifyJob*>( job ) ) {
473  ++progress;
474  }
475  checkDone();
476  }
477 
482  void createLocalCollections( LocalNode* localParent, QList<RemoteNode*> remoteNodes )
483  {
484  foreach ( RemoteNode *remoteNode, remoteNodes ) {
485  ++pendingJobs;
486  Collection col( remoteNode->collection );
487  Q_ASSERT( !col.remoteId().isEmpty() );
488  col.setParentCollection( localParent->collection );
489  CollectionCreateJob *create = new CollectionCreateJob( col, currentTransaction );
490  create->setProperty( LOCAL_NODE, QVariant::fromValue( localParent ) );
491  create->setProperty( REMOTE_NODE, QVariant::fromValue( remoteNode ) );
492  connect( create, SIGNAL(result(KJob*)), q, SLOT(createLocalCollectionResult(KJob*)) );
493  }
494  }
495 
496  void createLocalCollectionResult( KJob* job )
497  {
498  --pendingJobs;
499  if ( job->error() ) {
500  return; // handled by the base class
501  }
502 
503  const Collection newLocal = static_cast<CollectionCreateJob*>( job )->collection();
504  LocalNode *localNode = createLocalNode( newLocal );
505  localNode->processed = true;
506 
507  LocalNode *localParent = job->property( LOCAL_NODE ).value<LocalNode*>();
508  Q_ASSERT( localParent->childNodes.contains( localNode ) );
509  RemoteNode *remoteNode = job->property( REMOTE_NODE ).value<RemoteNode*>();
510  delete remoteNode;
511  ++progress;
512 
513  processPendingRemoteNodes( localParent );
514  if ( !hierarchicalRIDs ) {
515  processPendingRemoteNodes( localRoot );
516  }
517 
518  checkDone();
519  }
520 
524  bool hasProcessedChildren( LocalNode *localNode ) const
525  {
526  if ( localNode->processed ) {
527  return true;
528  }
529  foreach ( LocalNode *child, localNode->childNodes ) {
530  if ( hasProcessedChildren( child ) ) {
531  return true;
532  }
533  }
534  return false;
535  }
536 
541  Collection::List findUnprocessedLocalCollections( LocalNode *localNode ) const
542  {
543  Collection::List rv;
544  if ( !localNode->processed ) {
545  if ( hasProcessedChildren( localNode ) ) {
546  kWarning() << "Found unprocessed local node with processed children, excluding from deletion";
547  kWarning() << localNode->collection;
548  return rv;
549  }
550  if ( localNode->collection.remoteId().isEmpty() ) {
551  kWarning() << "Found unprocessed local node without remoteId, excluding from deletion";
552  kWarning() << localNode->collection;
553  return rv;
554  }
555  rv.append( localNode->collection );
556  return rv;
557  }
558 
559  foreach ( LocalNode *child, localNode->childNodes ) {
560  rv.append( findUnprocessedLocalCollections( child ) );
561  }
562  return rv;
563  }
564 
568  void deleteUnprocessedLocalNodes()
569  {
570  if ( incremental ) {
571  return;
572  }
573  const Collection::List cols = findUnprocessedLocalCollections( localRoot );
574  deleteLocalCollections( cols );
575  }
576 
581  void deleteLocalCollections( const Collection::List &cols )
582  {
583  q->setTotalAmount( KJob::Bytes, q->totalAmount( KJob::Bytes ) + cols.size() );
584  foreach ( const Collection &col, cols ) {
585  Q_ASSERT( !col.remoteId().isEmpty() ); // empty RID -> stuff we haven't even written to the remote side yet
586 
587  ++pendingJobs;
588  Q_ASSERT( currentTransaction );
589  CollectionDeleteJob *job = new CollectionDeleteJob( col, currentTransaction );
590  connect( job, SIGNAL(result(KJob*)), q, SLOT(deleteLocalCollectionsResult(KJob*)) );
591 
592  // It can happen that the groupware servers report us deleted collections
593  // twice, in this case this collection delete job will fail on the second try.
594  // To avoid a rollback of the complete transaction we gracefully allow the job
595  // to fail :)
596  currentTransaction->setIgnoreJobFailure( job );
597  }
598  }
599 
600  void deleteLocalCollectionsResult( KJob* )
601  {
602  --pendingJobs;
603 
604  ++progress;
605  checkDone();
606  }
607 
611  void checkUpdateNecessity()
612  {
613  bool updateNeeded = checkPendingRemoteNodes();
614  if ( !updateNeeded ) {
615  // We can end right now
616  q->emitResult();
617  return;
618  }
619 
620  // Since there are differences with the remote collections we need to sync. Start a transaction here.
621  Q_ASSERT( !currentTransaction );
622  currentTransaction = new TransactionSequence( q );
623  currentTransaction->setAutomaticCommittingEnabled( false );
624  q->connect( currentTransaction, SIGNAL(result(KJob*)), SLOT(transactionSequenceResult(KJob*)) );
625 
626  // Now that a transaction is started we need to fetch local collections again and do the update
627  q->doStart();
628  }
629 
631  void transactionSequenceResult( KJob *job )
632  {
633  if ( job->error() ) {
634  return; // handled by the base class
635  }
636 
637  q->emitResult();
638  }
639 
643  void execute()
644  {
645  kDebug() << Q_FUNC_INFO << "localListDone: " << localListDone << " deliveryDone: " << deliveryDone;
646  if ( !localListDone ) {
647  return;
648  }
649 
650  // If a transaction is not started yet we are still checking if the update is
651  // actually needed.
652  if ( !currentTransaction ) {
653  checkUpdateNecessity();
654  return;
655  }
656 
657  // Since the transaction is already running we need to execute the update.
658  processPendingRemoteNodes( localRoot );
659 
660  if ( !incremental && deliveryDone ) {
661  deleteUnprocessedLocalNodes();
662  }
663 
664  if ( !hierarchicalRIDs ) {
665  deleteLocalCollections( removedRemoteCollections );
666  } else {
667  Collection::List localCols;
668  foreach ( const Collection &c, removedRemoteCollections ) {
669  LocalNode *node = findMatchingLocalNode( c );
670  if ( node ) {
671  localCols.append( node->collection );
672  }
673  }
674  deleteLocalCollections( localCols );
675  }
676  removedRemoteCollections.clear();
677 
678  checkDone();
679  }
680 
684  QList<RemoteNode*> findPendingRemoteNodes( LocalNode *localNode )
685  {
686  QList<RemoteNode*> rv;
687  rv.append( localNode->pendingRemoteNodes );
688  foreach ( LocalNode *child, localNode->childNodes ) {
689  rv.append( findPendingRemoteNodes( child ) );
690  }
691  return rv;
692  }
693 
698  void checkDone()
699  {
700  q->setProcessedAmount( KJob::Bytes, progress );
701 
702  // still running jobs or not fully delivered local/remote state
703  if ( !deliveryDone || pendingJobs > 0 || !localListDone ) {
704  return;
705  }
706 
707  // safety check: there must be no pending remote nodes anymore
708  QList<RemoteNode*> orphans = findPendingRemoteNodes( localRoot );
709  if ( !orphans.isEmpty() ) {
710  q->setError( Unknown );
711  q->setErrorText( i18n( "Found unresolved orphan collections" ) );
712  foreach ( RemoteNode* orphan, orphans ) {
713  kDebug() << "found orphan collection:" << orphan->collection;
714  }
715  q->emitResult();
716  return;
717  }
718 
719  kDebug() << Q_FUNC_INFO << "q->commit()";
720  Q_ASSERT( currentTransaction );
721  currentTransaction->commit();
722  }
723 
724  CollectionSync *q;
725 
726  QString resourceId;
727 
728  int pendingJobs;
729  int progress;
730 
731  LocalNode* localRoot;
732  TransactionSequence* currentTransaction;
733  QHash<Collection::Id, LocalNode*> localUidMap;
734  QHash<QString, LocalNode*> localRidMap;
735 
736  // temporary during build-up of the local node tree, must be empty afterwards
737  QHash<Collection::Id, QVector<Collection::Id> > localPendingCollections;
738 
739  // removed remote collections in incremental mode
740  Collection::List removedRemoteCollections;
741 
742  // used to store the list of remote nodes passed by the user
743  QList<RemoteNode*> rootRemoteNodes;
744 
745  // keep track of the total number of local collections that are known
746  // only used during the preliminary check to see if updating is needed
747  int knownLocalCollections;
748 
749  bool incremental;
750  bool streaming;
751  bool hierarchicalRIDs;
752 
753  bool localListDone;
754  bool deliveryDone;
755 };
756 
757 CollectionSync::CollectionSync( const QString &resourceId, QObject *parent ) :
758  Job( parent ),
759  d( new Private( this ) )
760 {
761  d->resourceId = resourceId;
762  setTotalAmount( KJob::Bytes, 0 );
763 }
764 
765 CollectionSync::~CollectionSync()
766 {
767  delete d;
768 }
769 
770 void CollectionSync::setRemoteCollections(const Collection::List & remoteCollections)
771 {
772  setTotalAmount( KJob::Bytes, totalAmount( KJob::Bytes ) + remoteCollections.count() );
773  foreach ( const Collection &c, remoteCollections ) {
774  d->createRemoteNode( c );
775  }
776 
777  if ( !d->streaming ) {
778  d->deliveryDone = true;
779  }
780  d->execute();
781 }
782 
783 void CollectionSync::setRemoteCollections(const Collection::List & changedCollections, const Collection::List & removedCollections)
784 {
785  setTotalAmount( KJob::Bytes, totalAmount( KJob::Bytes ) + changedCollections.count() );
786  d->incremental = true;
787  foreach ( const Collection &c, changedCollections ) {
788  d->createRemoteNode( c );
789  }
790  d->removedRemoteCollections += removedCollections;
791 
792  if ( !d->streaming ) {
793  d->deliveryDone = true;
794  }
795  d->execute();
796 }
797 
798 void CollectionSync::doStart()
799 {
800  d->resetNodeTree();
801  Job *parent = ( d->currentTransaction ? static_cast<Job*>( d->currentTransaction ) : static_cast<Job*>( this ) );
802  CollectionFetchJob *job = new CollectionFetchJob( Collection::root(), CollectionFetchJob::Recursive, parent );
803  job->fetchScope().setResource( d->resourceId );
804  job->fetchScope().setIncludeUnsubscribed( true );
805  job->fetchScope().setAncestorRetrieval( CollectionFetchScope::Parent );
806  connect( job, SIGNAL(collectionsReceived(Akonadi::Collection::List)),
807  SLOT(localCollectionsReceived(Akonadi::Collection::List)) );
808  connect( job, SIGNAL(result(KJob*)), SLOT(localCollectionFetchResult(KJob*)) );
809 }
810 
811 void CollectionSync::setStreamingEnabled( bool streaming )
812 {
813  d->streaming = streaming;
814 }
815 
816 void CollectionSync::retrievalDone()
817 {
818  d->deliveryDone = true;
819  d->execute();
820 }
821 
822 void CollectionSync::setHierarchicalRemoteIds( bool hierarchical )
823 {
824  d->hierarchicalRIDs = hierarchical;
825 }
826 
827 void CollectionSync::rollback()
828 {
829  if ( d->currentTransaction ) {
830  d->currentTransaction->rollback();
831  }
832 }
833 
834 #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:827
Akonadi::CollectionFetchScope::setAncestorRetrieval
void setAncestorRetrieval(AncestorRetrieval ancestorDepth)
Sets how many levels of ancestor collections should be included in the retrieval. ...
Definition: collectionfetchscope.cpp:134
Akonadi::Collection::name
QString name() const
Returns the i18n'ed name of the collection.
Definition: collection.cpp:81
Akonadi::CollectionFetchScope::setIncludeUnsubscribed
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:437
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:114
Akonadi::Collection
Represents a collection of PIM items.
Definition: collection.h:75
Akonadi::CollectionFetchJob
Job that fetches collections from the Akonadi storage.
Definition: collectionfetchjob.h:53
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:765
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:195
Akonadi::CollectionSync::setHierarchicalRemoteIds
void setHierarchicalRemoteIds(bool hierarchical)
Indicate whether the resource supplies collections with hierarchical or global remote identifiers...
Definition: collectionsync.cpp:822
Akonadi::CollectionSync::setRemoteCollections
void setRemoteCollections(const Collection::List &remoteCollections)
Sets the result of a full remote collection listing.
Definition: collectionsync.cpp:770
Akonadi::Entity::attribute
Attribute * attribute(const QByteArray &name) const
Returns the attribute of the given type name if available, 0 otherwise.
Definition: entity.cpp:165
Akonadi::Entity::parentCollection
Collection parentCollection() const
Returns the parent collection of this object.
Definition: entity.cpp:186
Akonadi::CollectionSync::doStart
void doStart()
This method must be reimplemented in the concrete jobs.
Definition: collectionsync.cpp:798
Akonadi::Entity::remoteId
QString remoteId() const
Returns the remote id of the entity.
Definition: entity.cpp:82
Akonadi::EntityDisplayAttribute::clone
EntityDisplayAttribute * clone() const
Creates a copy of this attribute.
Definition: entitydisplayattribute.cpp:79
Akonadi::CollectionSync::retrievalDone
void retrievalDone()
Indicate that all collections have been retrieved in streaming mode.
Definition: collectionsync.cpp:816
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
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:757
Akonadi::TransactionSequence
Base class for jobs that need to run a sequence of sub-jobs in a transaction.
Definition: transactionsequence.h:69
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::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:811
Akonadi::Collection::List
QList< Collection > List
Describes a list of collections.
Definition: collection.h:81
Akonadi::EntityDisplayAttribute
Attribute that stores the properties that are used to display an entity.
Definition: entitydisplayattribute.h:39
Akonadi::Entity::attributes
Attribute::List attributes() const
Returns a list of all attributes of the entity.
Definition: entity.cpp:151
This file is part of the KDE documentation.
Documentation copyright © 1996-2014 The KDE developers.
Generated on Tue Oct 14 2014 23:00:26 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
  • kldap
  • kmbox
  • kmime
  • kpimidentities
  • kpimtextedit
  • kresources
  • ktnef
  • kxmlrpcclient
  • microblog

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