~ubuntu-branches/ubuntu/trusty/kdepimlibs/trusty

« back to all changes in this revision

Viewing changes to akonadi/collectionsync.cpp

  • Committer: Package Import Robot
  • Author(s): Rohan Garg, Rohan Garg, Philip Muškovac
  • Date: 2013-11-23 17:36:44 UTC
  • mfrom: (1.1.102)
  • Revision ID: package-import@ubuntu.com-20131123173644-p5ow94192ezsny8g
Tags: 4:4.11.80-0ubuntu1
[ Rohan Garg ]
* New upstream beta release
  - Bump akonadi requirement to 1.10.45
  - Update install files
  - Update symbols

[ Philip Muškovac ]
* kdepimlibs-dev/-dbg breaks/replaces kdepim-runtime/-dbg (<< 4:4.11.80)

Show diffs side-by-side

added added

removed removed

Lines of Context:
28
28
#include "collectionmovejob.h"
29
29
#include "entitydisplayattribute.h"
30
30
 
 
31
#include "cachepolicy.h"
 
32
 
31
33
#include <kdebug.h>
32
34
#include <KLocalizedString>
33
35
#include <QtCore/QVariant>
91
93
      q( parent ),
92
94
      pendingJobs( 0 ),
93
95
      progress( 0 ),
 
96
      localRoot( 0 ),
 
97
      currentTransaction( 0 ),
 
98
      knownLocalCollections( 0 ),
94
99
      incremental( false ),
95
100
      streaming( false ),
96
101
      hierarchicalRIDs( false ),
97
102
      localListDone( false ),
98
103
      deliveryDone( false )
99
104
    {
 
105
    }
 
106
 
 
107
    ~Private()
 
108
    {
 
109
      delete localRoot;
 
110
      qDeleteAll( rootRemoteNodes );
 
111
    }
 
112
 
 
113
    /** Utility method to reset the node tree. */
 
114
    void resetNodeTree()
 
115
    {
 
116
      delete localRoot;
100
117
      localRoot = new LocalNode( Collection::root() );
101
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();
102
126
      localUidMap.insert( localRoot->collection.id(), localRoot );
103
127
      if ( !hierarchicalRIDs ) {
104
128
        localRidMap.insert( QString(), localRoot );
105
129
      }
106
130
    }
107
131
 
108
 
    ~Private()
109
 
    {
110
 
      delete localRoot;
111
 
    }
112
 
 
113
132
    /** Create a local node from the given local collection and integrate it into the local tree structure. */
114
133
    LocalNode* createLocalNode( const Collection &col )
115
134
    {
155
174
        return;
156
175
      }
157
176
      RemoteNode *node = new RemoteNode( col );
158
 
      localRoot->pendingRemoteNodes.append( node );
 
177
      rootRemoteNodes.append( node );
159
178
    }
160
179
 
161
180
    /** Create local nodes as we receive the local listing from the Akonadi server. */
162
181
    void localCollectionsReceived( const Akonadi::Collection::List &localCols )
163
182
    {
164
 
      foreach ( const Collection &c, localCols )
 
183
      foreach ( const Collection &c, localCols ) {
165
184
        createLocalNode( c );
 
185
        knownLocalCollections++;
 
186
      }
166
187
    }
167
188
 
168
189
    /** Once the local collection listing finished we can continue with the interesting stuff. */
189
210
     * @note This is used as a fallback if the resource lost the RID update somehow.
190
211
     * This can be used because the Akonadi server enforces unique child collection names inside the hierarchy
191
212
     */
192
 
    LocalNode* findLocalChildNodeByName( LocalNode *localParentNode, const QString &name )
 
213
    LocalNode* findLocalChildNodeByName( LocalNode *localParentNode, const QString &name ) const
193
214
    {
194
215
      if ( name.isEmpty() ) { // shouldn't happen...
195
216
        return 0;
212
233
      Find the local node that matches the given remote collection, returns 0
213
234
      if that doesn't exist (yet).
214
235
    */
215
 
    LocalNode* findMatchingLocalNode( const Collection &collection )
 
236
    LocalNode* findMatchingLocalNode( const Collection &collection ) const
216
237
    {
217
238
      if ( !hierarchicalRIDs ) {
218
239
        if ( localRidMap.contains( collection.remoteId() ) ) {
290
311
    }
291
312
 
292
313
    /**
 
314
      Checks if any of the remote nodes is not equal to the current local one. If so return true.
 
315
    */
 
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
 
 
336
    /**
293
337
      Checks the pending remote nodes attached to the given local root node
294
338
      to see if any of them can be processed by now. If not, they are moved to
295
339
      the closest ancestor available.
332
376
    }
333
377
 
334
378
    /**
 
379
      Checks if the given localNode and remoteNode are different
 
380
    */
 
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
 
 
425
    /**
335
426
      Performs a local update for the given node pair.
336
427
    */
337
428
    void updateLocalCollection( LocalNode *localNode, RemoteNode *remoteNode )
338
429
    {
339
430
      Collection upd( remoteNode->collection );
340
431
      Q_ASSERT( !upd.remoteId().isEmpty() );
 
432
      Q_ASSERT( currentTransaction );
341
433
      upd.setId( localNode->collection.id() );
342
434
      if ( localNode->collection.attribute<EntityDisplayAttribute>() ) {
343
435
        upd.removeAttribute<EntityDisplayAttribute>();
350
442
        Collection c( upd );
351
443
        c.setParentCollection( localNode->collection.parentCollection() );
352
444
        ++pendingJobs;
353
 
        CollectionModifyJob *mod = new CollectionModifyJob( c, q );
 
445
        CollectionModifyJob *mod = new CollectionModifyJob( c, currentTransaction );
354
446
        connect( mod, SIGNAL(result(KJob*)), q, SLOT(updateLocalCollectionResult(KJob*)) );
355
447
      }
356
448
 
362
454
        // local parent has been created
363
455
        if ( newParent && oldParent != newParent ) {
364
456
          ++pendingJobs;
365
 
          CollectionMoveJob *move = new CollectionMoveJob( upd, newParent->collection, q );
 
457
          CollectionMoveJob *move = new CollectionMoveJob( upd, newParent->collection, currentTransaction );
366
458
          connect( move, SIGNAL(result(KJob*)), q, SLOT(updateLocalCollectionResult(KJob*)) );
367
459
        }
368
460
      }
394
486
        Collection col( remoteNode->collection );
395
487
        Q_ASSERT( !col.remoteId().isEmpty() );
396
488
        col.setParentCollection( localParent->collection );
397
 
        CollectionCreateJob *create = new CollectionCreateJob( col, q );
 
489
        CollectionCreateJob *create = new CollectionCreateJob( col, currentTransaction );
398
490
        create->setProperty( LOCAL_NODE, QVariant::fromValue( localParent ) );
399
491
        create->setProperty( REMOTE_NODE, QVariant::fromValue( remoteNode ) );
400
492
        connect( create, SIGNAL(result(KJob*)), q, SLOT(createLocalCollectionResult(KJob*)) );
493
585
        Q_ASSERT( !col.remoteId().isEmpty() ); // empty RID -> stuff we haven't even written to the remote side yet
494
586
 
495
587
        ++pendingJobs;
496
 
        CollectionDeleteJob *job = new CollectionDeleteJob( col, q );
 
588
        Q_ASSERT( currentTransaction );
 
589
        CollectionDeleteJob *job = new CollectionDeleteJob( col, currentTransaction );
497
590
        connect( job, SIGNAL(result(KJob*)), q, SLOT(deleteLocalCollectionsResult(KJob*)) );
498
591
 
499
592
        // It can happen that the groupware servers report us deleted collections
500
593
        // twice, in this case this collection delete job will fail on the second try.
501
594
        // To avoid a rollback of the complete transaction we gracefully allow the job
502
595
        // to fail :)
503
 
        q->setIgnoreJobFailure( job );
 
596
        currentTransaction->setIgnoreJobFailure( job );
504
597
      }
505
598
    }
506
599
 
513
606
    }
514
607
 
515
608
    /**
 
609
      Check update necessity.
 
610
    */
 
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
 
 
630
    /** After the transaction has finished report we're done as well. */
 
631
    void transactionSequenceResult( KJob *job )
 
632
    {
 
633
      if ( job->error() ) {
 
634
        return; // handled by the base class
 
635
      }
 
636
 
 
637
      q->emitResult();
 
638
    }
 
639
 
 
640
    /**
516
641
      Process what's currently available.
517
642
    */
518
643
    void execute()
522
647
        return;
523
648
      }
524
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.
525
658
      processPendingRemoteNodes( localRoot );
526
659
 
527
660
      if ( !incremental && deliveryDone ) {
584
717
      }
585
718
 
586
719
      kDebug() << Q_FUNC_INFO << "q->commit()";
587
 
      q->commit();
 
720
      Q_ASSERT( currentTransaction );
 
721
      currentTransaction->commit();
588
722
    }
589
723
 
590
724
    CollectionSync *q;
595
729
    int progress;
596
730
 
597
731
    LocalNode* localRoot;
 
732
    TransactionSequence* currentTransaction;
598
733
    QHash<Collection::Id, LocalNode*> localUidMap;
599
734
    QHash<QString, LocalNode*> localRidMap;
600
735
 
604
739
    // removed remote collections in incremental mode
605
740
    Collection::List removedRemoteCollections;
606
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
 
607
749
    bool incremental;
608
750
    bool streaming;
609
751
    bool hierarchicalRIDs;
613
755
};
614
756
 
615
757
CollectionSync::CollectionSync( const QString &resourceId, QObject *parent ) :
616
 
    TransactionSequence( parent ),
 
758
    Job( parent ),
617
759
    d( new Private( this ) )
618
760
{
619
761
  d->resourceId = resourceId;
655
797
 
656
798
void CollectionSync::doStart()
657
799
{
658
 
  CollectionFetchJob *job = new CollectionFetchJob( Collection::root(), CollectionFetchJob::Recursive, this );
 
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 );
659
803
  job->fetchScope().setResource( d->resourceId );
660
804
  job->fetchScope().setIncludeUnsubscribed( true );
661
805
  job->fetchScope().setAncestorRetrieval( CollectionFetchScope::Parent );
680
824
  d->hierarchicalRIDs = hierarchical;
681
825
}
682
826
 
 
827
void CollectionSync::rollback()
 
828
{
 
829
  if ( d->currentTransaction ) {
 
830
    d->currentTransaction->rollback();
 
831
  }
 
832
}
 
833
 
683
834
#include "moc_collectionsync_p.cpp"