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

« back to all changes in this revision

Viewing changes to akonadi/calendar/incidencechanger.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:
26
26
#include <akonadi/itemmodifyjob.h>
27
27
#include <akonadi/itemdeletejob.h>
28
28
#include <akonadi/transactionsequence.h>
29
 
#include <akonadi/collectiondialog.h>
30
29
 
31
30
#include <KJob>
32
31
#include <KLocalizedString>
34
33
#include <KMessageBox>
35
34
#include <KStandardGuiItem>
36
35
 
 
36
#include <QBitArray>
 
37
 
37
38
using namespace Akonadi;
38
39
using namespace KCalCore;
39
40
 
 
41
#ifdef PLEASE_TEST_INVITATIONS
 
42
# define RUNNING_UNIT_TESTS true
 
43
#else
 
44
# define RUNNING_UNIT_TESTS false
 
45
#endif
 
46
 
40
47
ITIPHandlerHelper::Action actionFromStatus( ITIPHandlerHelper::SendResult result )
41
48
{
42
49
  //enum SendResult {
58
65
}
59
66
 
60
67
namespace Akonadi {
61
 
  static Akonadi::Collection selectCollection( QWidget *parent,
62
 
                                               int &dialogCode,
63
 
                                               const QStringList &mimeTypes,
64
 
                                               const Akonadi::Collection &defCollection )
65
 
  {
66
 
    QPointer<Akonadi::CollectionDialog> dlg( new Akonadi::CollectionDialog( parent ) );
67
 
 
68
 
    kDebug() << "selecting collections with mimeType in " << mimeTypes;
69
 
 
70
 
    dlg->changeCollectionDialogOptions( Akonadi::CollectionDialog::KeepTreeExpanded );
71
 
    dlg->setMimeTypeFilter( mimeTypes );
72
 
    dlg->setAccessRightsFilter( Akonadi::Collection::CanCreateItem );
73
 
    if ( defCollection.isValid() ) {
74
 
      dlg->setDefaultCollection( defCollection );
75
 
    }
76
 
    Akonadi::Collection collection;
77
 
 
78
 
    // FIXME: don't use exec.
79
 
    dialogCode = dlg->exec();
80
 
    if ( dialogCode == QDialog::Accepted ) {
81
 
      collection = dlg->selectedCollection();
82
 
 
83
 
      if ( !collection.isValid() ) {
84
 
        kWarning() <<"An invalid collection was selected!";
85
 
      }
86
 
    }
87
 
    delete dlg;
88
 
 
89
 
    return collection;
90
 
  }
91
 
 
92
68
  // Does a queued emit, with QMetaObject::invokeMethod
93
69
  static void emitCreateFinished( IncidenceChanger *changer,
94
70
                                  int changeId,
168
144
  mGroupwareCommunication = false;
169
145
  mLatestAtomicOperationId = 0;
170
146
  mBatchOperationInProgress = false;
 
147
  mAutoAdjustRecurrence = true;
 
148
  m_collectionFetchJob = 0;
 
149
  m_invitationPolicy = InvitationPolicyAsk;
171
150
 
172
151
  qRegisterMetaType<QVector<Akonadi::Item::Id> >( "QVector<Akonadi::Item::Id>" );
173
152
  qRegisterMetaType<Akonadi::Item::Id>( "Akonadi::Item::Id" );
190
169
{
191
170
  // Changes must be done between startAtomicOperation() and endAtomicOperation()
192
171
  return mAtomicOperations.contains( atomicOperationId ) &&
193
 
         !mAtomicOperations[atomicOperationId]->endCalled;
 
172
         !mAtomicOperations[atomicOperationId]->m_endCalled;
194
173
}
195
174
 
196
175
bool IncidenceChanger::Private::hasRights( const Collection &collection,
217
196
Akonadi::Job* IncidenceChanger::Private::parentJob( const Change::Ptr &change ) const
218
197
{
219
198
  return (mBatchOperationInProgress && !change->queuedModification) ?
220
 
                                       mAtomicOperations[mLatestAtomicOperationId]->transaction : 0;
 
199
              mAtomicOperations[mLatestAtomicOperationId]->transaction() : 0;
221
200
}
222
201
 
223
202
void IncidenceChanger::Private::queueModification( Change::Ptr change )
259
238
  Q_ASSERT( mAtomicOperations.contains(atomicOperationId) );
260
239
  AtomicOperation *operation = mAtomicOperations[atomicOperationId];
261
240
  Q_ASSERT( operation );
262
 
  Q_ASSERT( operation->id == atomicOperationId );
 
241
  Q_ASSERT( operation->m_id == atomicOperationId );
263
242
  if ( job->error() ) {
264
243
    if ( !operation->rolledback() )
265
244
      operation->setRolledback();
266
245
    kError() << "Transaction failed, everything was rolledback. "
267
246
             << job->errorString();
268
247
  } else {
269
 
    Q_ASSERT( operation->endCalled );
 
248
    Q_ASSERT( operation->m_endCalled );
270
249
    Q_ASSERT( !operation->pendingJobs() );
271
250
  }
272
251
 
273
 
  if ( !operation->pendingJobs() && operation->endCalled ) {
 
252
  if ( !operation->pendingJobs() && operation->m_endCalled ) {
274
253
    delete mAtomicOperations.take( atomicOperationId );
275
254
    mBatchOperationInProgress = false;
276
255
  } else {
277
 
    operation->transactionCompleted = true;
 
256
    operation->m_transactionCompleted = true;
278
257
  }
279
258
}
280
259
 
294
273
  QString description;
295
274
  if ( change->atomicOperationId != 0 ) {
296
275
    AtomicOperation *a = mAtomicOperations[change->atomicOperationId];
297
 
    a->numCompletedChanges++;
 
276
    a->m_numCompletedChanges++;
298
277
    change->completed = true;
299
 
    description = a->description;
 
278
    description = a->m_description;
300
279
  }
301
280
 
302
281
  if ( j->error() ) {
345
324
  QString description;
346
325
  if ( change->atomicOperationId != 0 ) {
347
326
    AtomicOperation *a = mAtomicOperations[change->atomicOperationId];
348
 
    a->numCompletedChanges++;
 
327
    a->m_numCompletedChanges++;
349
328
    change->completed = true;
350
 
    description = a->description;
 
329
    description = a->m_description;
351
330
  }
352
331
  if ( j->error() ) {
353
332
    resultCode = ResultCodeJobError;
393
372
  QString description;
394
373
  if ( change->atomicOperationId != 0 ) {
395
374
    AtomicOperation *a = mAtomicOperations[change->atomicOperationId];
396
 
    a->numCompletedChanges++;
 
375
    a->m_numCompletedChanges++;
397
376
    change->completed = true;
398
 
    description = a->description;
 
377
    description = a->m_description;
399
378
  }
400
379
  if ( j->error() ) {
401
380
    if ( deleteAlreadyCalled( item.id() ) ) {
446
425
  bool result = true;
447
426
  if ( mGroupwareCommunication ) {
448
427
    ITIPHandlerHelper handler( change->parentWidget );  // TODO make async
449
 
    if ( mInvitationStatusByAtomicOperation.contains( change->atomicOperationId ) ) {
 
428
 
 
429
    if ( m_invitationPolicy == InvitationPolicySend ) {
 
430
      handler.setDefaultAction( ITIPHandlerHelper::ActionSendMessage );
 
431
    } else if ( m_invitationPolicy == InvitationPolicyDontSend ) {
 
432
      handler.setDefaultAction( ITIPHandlerHelper::ActionDontSendMessage );
 
433
    } else if ( mInvitationStatusByAtomicOperation.contains( change->atomicOperationId ) ) {
450
434
      handler.setDefaultAction( actionFromStatus( mInvitationStatusByAtomicOperation.value( change->atomicOperationId ) ) );
451
435
    }
452
436
 
460
444
        Q_ASSERT( !change->originalItems.isEmpty() );
461
445
        foreach( const Akonadi::Item &item, change->originalItems ) {
462
446
          Q_ASSERT( item.hasPayload<KCalCore::Incidence::Ptr>() );
463
 
          Incidence::Ptr incidence = item.payload<KCalCore::Incidence::Ptr>();
464
 
          if ( !incidence->supportsGroupwareCommunication() )
 
447
          Incidence::Ptr incidence = CalendarUtils::incidence( item );
 
448
          if ( !incidence->supportsGroupwareCommunication() ) {
465
449
            continue;
466
 
          status = handler.sendIncidenceDeletedMessage( KCalCore::iTIPCancel, incidence );
467
 
          if ( change->atomicOperationId ) {
468
 
            mInvitationStatusByAtomicOperation.insert( change->atomicOperationId, status );
469
 
          }
470
 
          result = status != ITIPHandlerHelper::ResultFailAbortUpdate;
471
 
          //TODO: with some status we want to break immediately
 
450
          }
 
451
          // We only send CANCEL if we're the organizer.
 
452
          // If we're not, then we send REPLY with PartStat=Declined in handleInvitationsAfterChange()
 
453
          if ( Akonadi::CalendarUtils::thatIsMe( incidence->organizer()->email() ) ) {
 
454
            status = handler.sendIncidenceDeletedMessage( KCalCore::iTIPCancel, incidence );
 
455
            if ( change->atomicOperationId ) {
 
456
              mInvitationStatusByAtomicOperation.insert( change->atomicOperationId, status );
 
457
            }
 
458
            result = status != ITIPHandlerHelper::ResultFailAbortUpdate;
 
459
            //TODO: with some status we want to break immediately
 
460
          }
472
461
        }
473
462
      }
474
463
      break;
475
464
      case IncidenceChanger::ChangeTypeModify:
476
465
      {
477
 
        if ( !change->originalItems.isEmpty() ) {
478
 
          Q_ASSERT( change->originalItems.count() == 1 );
479
 
          Incidence::Ptr oldIncidence = change->originalItems.first().payload<KCalCore::Incidence::Ptr>();
480
 
          Incidence::Ptr newIncidence = change->newItem.payload<KCalCore::Incidence::Ptr>();
481
 
 
482
 
          if ( oldIncidence->supportsGroupwareCommunication() ) {
483
 
            const bool modify = handler.handleIncidenceAboutToBeModified( newIncidence );
484
 
            if ( !modify ) {
485
 
              if ( newIncidence->type() == oldIncidence->type() ) {
486
 
                IncidenceBase *i1 = newIncidence.data();
487
 
                IncidenceBase *i2 = oldIncidence.data();
488
 
                *i1 = *i2;
489
 
              }
490
 
              result = false;
491
 
            }
 
466
        if ( change->originalItems.isEmpty() ) {
 
467
          break;
 
468
        }
 
469
 
 
470
        Q_ASSERT( change->originalItems.count() == 1 );
 
471
        Incidence::Ptr oldIncidence = CalendarUtils::incidence( change->originalItems.first() );
 
472
        Incidence::Ptr newIncidence = CalendarUtils::incidence( change->newItem );
 
473
 
 
474
        if ( !oldIncidence->supportsGroupwareCommunication() ) {
 
475
          break;
 
476
        }
 
477
 
 
478
        const bool weAreOrganizer = Akonadi::CalendarUtils::thatIsMe( newIncidence->organizer()->email() );
 
479
        if (RUNNING_UNIT_TESTS && !weAreOrganizer ) {
 
480
          // This is a bit of a workaround when running tests. I don't want to show the
 
481
          // "You're not organizer, do you want to modify event?" dialog in unit-tests, but want
 
482
          // to emulate a "yes" and a "no" press.
 
483
          if ( m_invitationPolicy == InvitationPolicySend ) {
 
484
            return true;
 
485
          } else if (m_invitationPolicy == InvitationPolicyDontSend) {
 
486
            return false;
492
487
          }
493
488
        }
 
489
 
 
490
        const bool modify = handler.handleIncidenceAboutToBeModified( newIncidence );
 
491
        if ( modify ) {
 
492
          break;
 
493
        }
 
494
 
 
495
        if ( newIncidence->type() == oldIncidence->type() ) {
 
496
          IncidenceBase *i1 = newIncidence.data();
 
497
          IncidenceBase *i2 = oldIncidence.data();
 
498
          *i1 = *i2;
 
499
        }
 
500
        result = false;
494
501
      }
495
502
      break;
496
503
      default:
505
512
{
506
513
  if ( change->useGroupwareCommunication ) {
507
514
    ITIPHandlerHelper handler( change->parentWidget ); // TODO make async
 
515
 
 
516
    const bool alwaysSend = m_invitationPolicy == InvitationPolicySend;
 
517
    const bool neverSend = m_invitationPolicy == InvitationPolicyDontSend;
 
518
    if ( alwaysSend ) {
 
519
      handler.setDefaultAction( ITIPHandlerHelper::ActionSendMessage );
 
520
    }
 
521
 
 
522
    if ( neverSend ) {
 
523
      handler.setDefaultAction( ITIPHandlerHelper::ActionDontSendMessage );
 
524
    }
 
525
 
508
526
    switch( change->type ) {
509
527
      case IncidenceChanger::ChangeTypeCreate:
510
528
      {
511
 
        Incidence::Ptr incidence = change->newItem.payload<KCalCore::Incidence::Ptr>();
 
529
        Incidence::Ptr incidence = CalendarUtils::incidence( change->newItem );
512
530
        if ( incidence->supportsGroupwareCommunication() ) {
513
531
          const ITIPHandlerHelper::SendResult status =
514
532
            handler.sendIncidenceCreatedMessage( KCalCore::iTIPRequest, incidence );
529
547
        Q_ASSERT( !change->originalItems.isEmpty() );
530
548
        foreach( const Akonadi::Item &item, change->originalItems ) {
531
549
          Q_ASSERT( item.hasPayload() );
532
 
          Incidence::Ptr incidence = item.payload<KCalCore::Incidence::Ptr>();
 
550
          Incidence::Ptr incidence = CalendarUtils::incidence( item );
533
551
          Q_ASSERT( incidence );
534
552
          if ( !incidence->supportsGroupwareCommunication() )
535
553
            continue;
563
581
      break;
564
582
      case IncidenceChanger::ChangeTypeModify:
565
583
      {
566
 
        if ( !change->originalItems.isEmpty() ) {
567
 
          Q_ASSERT( change->originalItems.count() == 1 );
568
 
          Incidence::Ptr oldIncidence = change->originalItems.first().payload<KCalCore::Incidence::Ptr>();
569
 
          Incidence::Ptr newIncidence = change->newItem.payload<KCalCore::Incidence::Ptr>();
570
 
          if ( newIncidence->supportsGroupwareCommunication() ) {
571
 
            if ( mInvitationStatusByAtomicOperation.contains( change->atomicOperationId ) ) {
572
 
              handler.setDefaultAction( actionFromStatus( mInvitationStatusByAtomicOperation.value( change->atomicOperationId ) ) );
573
 
            }
574
 
            const bool attendeeStatusChanged = myAttendeeStatusChanged( newIncidence,
575
 
                                                                        oldIncidence,
576
 
                                                                        Akonadi::CalendarUtils::allEmails() );
577
 
            ITIPHandlerHelper::SendResult status = handler.sendIncidenceModifiedMessage( KCalCore::iTIPRequest,
578
 
                                                                                              newIncidence,
579
 
                                                                                              attendeeStatusChanged );
580
 
 
581
 
            if ( change->atomicOperationId != 0 ) {
582
 
              mInvitationStatusByAtomicOperation.insert( change->atomicOperationId, status );
583
 
            }
584
 
          }
 
584
        if ( change->originalItems.isEmpty() ) {
 
585
          break;
 
586
        }
 
587
 
 
588
        Q_ASSERT( change->originalItems.count() == 1 );
 
589
        Incidence::Ptr oldIncidence = CalendarUtils::incidence( change->originalItems.first() );
 
590
        Incidence::Ptr newIncidence = CalendarUtils::incidence( change->newItem );
 
591
 
 
592
        if ( !newIncidence->supportsGroupwareCommunication() ||
 
593
             !Akonadi::CalendarUtils::thatIsMe( newIncidence->organizer()->email() ) ) {
 
594
          // If we're not the organizer, the user already saw the "Do you really want to do this, incidence will become out of sync"
 
595
          break;
 
596
        }
 
597
 
 
598
        if ( !neverSend && !alwaysSend && mInvitationStatusByAtomicOperation.contains( change->atomicOperationId ) ) {
 
599
          handler.setDefaultAction( actionFromStatus( mInvitationStatusByAtomicOperation.value( change->atomicOperationId ) ) );
 
600
        }
 
601
 
 
602
        const bool attendeeStatusChanged = myAttendeeStatusChanged( newIncidence,
 
603
                                                                    oldIncidence,
 
604
                                                                    Akonadi::CalendarUtils::allEmails() );
 
605
 
 
606
        ITIPHandlerHelper::SendResult status = handler.sendIncidenceModifiedMessage( KCalCore::iTIPRequest,
 
607
                                                                                     newIncidence,
 
608
                                                                                     attendeeStatusChanged );
 
609
 
 
610
        if ( change->atomicOperationId != 0 ) {
 
611
          mInvitationStatusByAtomicOperation.insert( change->atomicOperationId, status );
585
612
        }
586
613
      }
587
614
      break;
636
663
 
637
664
  const Change::Ptr change( new CreationChange( this, ++d->mLatestChangeId,
638
665
                                                atomicOperationId, parent ) );
639
 
  Collection collectionToUse;
640
 
 
641
666
  const int changeId = change->id;
642
667
  Q_ASSERT( !( d->mBatchOperationInProgress && !d->mAtomicOperations.contains( atomicOperationId ) ) );
643
668
  if ( d->mBatchOperationInProgress && d->mAtomicOperations[atomicOperationId]->rolledback() ) {
650
675
    return changeId;
651
676
  }
652
677
 
653
 
  d->handleInvitationsBeforeChange( change );
654
 
 
655
 
  if ( collection.isValid() && d->hasRights( collection, ChangeTypeCreate ) ) {
656
 
    // The collection passed always has priority
657
 
    collectionToUse = collection;
658
 
  } else {
659
 
    switch( d->mDestinationPolicy ) {
660
 
      case DestinationPolicyDefault:
661
 
        if ( d->mDefaultCollection.isValid() &&
662
 
             d->hasRights( d->mDefaultCollection, ChangeTypeCreate ) ) {
663
 
          collectionToUse = d->mDefaultCollection;
664
 
          break;
665
 
        }
666
 
        kWarning() << "Destination policy is to use the default collection."
667
 
                   << "But it's invalid or doesn't have proper ACLs."
668
 
                   << "isValid = "  << d->mDefaultCollection.isValid()
669
 
                   << "has ACLs = " << d->hasRights( d->mDefaultCollection,
670
 
                                                     ChangeTypeCreate );
671
 
        // else fallthrough, and ask the user.
672
 
      case DestinationPolicyAsk:
673
 
      {
674
 
        int dialogCode;
675
 
        const QStringList mimeTypes( incidence->mimeType() );
676
 
        collectionToUse = selectCollection( parent, dialogCode /*by-ref*/, mimeTypes,
677
 
                                            d->mDefaultCollection );
678
 
        if ( dialogCode != QDialog::Accepted ) {
679
 
          kDebug() << "User canceled collection choosing";
680
 
          change->resultCode = ResultCodeUserCanceled;
681
 
          d->cancelTransaction();
682
 
          return changeId;
683
 
        }
684
 
 
685
 
        if ( collectionToUse.isValid() && !d->hasRights( collectionToUse, ChangeTypeCreate ) ) {
686
 
          kWarning() << "No ACLs for incidence creation";
687
 
          const QString errorMessage = d->showErrorDialog( ResultCodePermissions, parent );
688
 
          change->resultCode = ResultCodePermissions;
689
 
          change->errorString = errorMessage;
690
 
          d->cancelTransaction();
691
 
          return changeId;
692
 
        }
693
 
 
694
 
        // TODO: add unit test for these two situations after reviewing API
695
 
        if ( !collectionToUse.isValid() ) {
696
 
          kError() << "Invalid collection selected. Can't create incidence.";
697
 
          change->resultCode = ResultCodeInvalidUserCollection;
698
 
          const QString errorString = d->showErrorDialog( ResultCodeInvalidUserCollection, parent );
699
 
          change->errorString = errorString;
700
 
          d->cancelTransaction();
701
 
          return changeId;
702
 
        }
703
 
      }
704
 
      break;
705
 
      case DestinationPolicyNeverAsk:
706
 
      {
707
 
        const bool hasRights = d->hasRights( d->mDefaultCollection, ChangeTypeCreate );
708
 
        if ( d->mDefaultCollection.isValid() && hasRights ) {
709
 
          collectionToUse = d->mDefaultCollection;
710
 
        } else {
711
 
          const QString errorString = d->showErrorDialog( ResultCodeInvalidDefaultCollection, parent );
712
 
          kError() << errorString << "; rights are " << hasRights;
713
 
          change->resultCode = hasRights ? ResultCodeInvalidDefaultCollection :
714
 
                                           ResultCodePermissions;
715
 
          change->errorString = errorString;
716
 
          d->cancelTransaction();
717
 
          return changeId;
718
 
        }
719
 
      }
720
 
      break;
721
 
    default:
722
 
      // Never happens
723
 
      Q_ASSERT_X( false, "createIncidence()", "unknown destination policy" );
724
 
      d->cancelTransaction();
725
 
      return -1;
726
 
    }
727
 
  }
728
 
 
729
 
  d->mLastCollectionUsed = collectionToUse;
730
 
 
731
678
  Item item;
732
 
  item.setPayload<Incidence::Ptr>( incidence );
 
679
  item.setPayload<KCalCore::Incidence::Ptr>( incidence );
733
680
  item.setMimeType( incidence->mimeType() );
734
681
 
735
 
  ItemCreateJob *createJob = new ItemCreateJob( item, collectionToUse, d->parentJob( change ) );
736
 
  d->mChangeForJob.insert( createJob, change );
737
 
 
738
 
  if ( d->mBatchOperationInProgress ) {
739
 
    AtomicOperation *atomic = d->mAtomicOperations[d->mLatestAtomicOperationId];
740
 
    Q_ASSERT( atomic );
741
 
    atomic->addChange( change );
742
 
  }
743
 
 
744
 
  // QueuedConnection because of possible sync exec calls.
745
 
  connect( createJob, SIGNAL(result(KJob*)),
746
 
           d, SLOT(handleCreateJobResult(KJob*)), Qt::QueuedConnection );
747
 
 
748
 
  d->mChangeById.insert( changeId, change );
 
682
  change->newItem = item;
 
683
 
 
684
  d->step1DetermineDestinationCollection( change, collection );
 
685
 
749
686
  return change->id;
750
687
}
751
688
 
910
847
    d->cleanupTransaction();
911
848
    emitModifyFinished( this, changeId, changedItem, ResultCodeRolledback, errorMessage );
912
849
  } else {
 
850
    d->adjustRecurrence( originalPayload, CalendarUtils::incidence( modificationChange->newItem ) );
913
851
    d->performModification( change );
914
852
  }
915
853
 
945
883
    return;
946
884
  }
947
885
 
948
 
  handleInvitationsBeforeChange( change );
 
886
  const bool userCancelled = !handleInvitationsBeforeChange( change );
 
887
  if ( userCancelled ) {
 
888
    // User got a "You're not the organizer, do you really want to send" dialog, and said "no"
 
889
    kDebug() << "User cancelled, giving up";
 
890
    emitModifyFinished( q, changeId, newItem, ResultCodeUserCanceled, QString() );
 
891
    return;
 
892
  }
949
893
 
950
894
  QHash<Akonadi::Item::Id, int> &latestRevisionByItemId =
951
895
                                                 ConflictPreventer::self()->mLatestRevisionByItemId;
961
905
    newItem.setRevision( latestRevisionByItemId[id] );
962
906
  }
963
907
 
964
 
  Incidence::Ptr incidence = newItem.payload<Incidence::Ptr>();
 
908
  Incidence::Ptr incidence = CalendarUtils::incidence( newItem );
965
909
  { // increment revision ( KCalCore revision, not akonadi )
966
910
    const int revision = incidence->revision();
967
911
    incidence->setRevision( revision + 1 );
1003
947
  ++d->mLatestAtomicOperationId;
1004
948
  d->mBatchOperationInProgress = true;
1005
949
 
1006
 
  AtomicOperation *atomicOperation = new AtomicOperation( d->mLatestAtomicOperationId );
1007
 
  atomicOperation->description = operationDescription;
 
950
  AtomicOperation *atomicOperation = new AtomicOperation( d, d->mLatestAtomicOperationId );
 
951
  atomicOperation->m_description = operationDescription;
1008
952
  d->mAtomicOperations.insert( d->mLatestAtomicOperationId, atomicOperation );
1009
 
  d->mAtomicOperationByTransaction.insert( atomicOperation->transaction, d->mLatestAtomicOperationId );
1010
 
 
1011
 
  d->connect( atomicOperation->transaction, SIGNAL(result(KJob*)),
1012
 
              d, SLOT(handleTransactionJobResult(KJob*)) );
1013
953
}
1014
954
 
1015
955
void IncidenceChanger::endAtomicOperation()
1026
966
  Q_ASSERT( d->mAtomicOperations.contains(d->mLatestAtomicOperationId) );
1027
967
  AtomicOperation *atomicOperation = d->mAtomicOperations[d->mLatestAtomicOperationId];
1028
968
  Q_ASSERT( atomicOperation );
1029
 
  atomicOperation->endCalled = true;
 
969
  atomicOperation->m_endCalled = true;
1030
970
 
1031
971
  const bool allJobsCompleted = !atomicOperation->pendingJobs();
1032
972
 
1033
973
  if ( allJobsCompleted && atomicOperation->rolledback() &&
1034
 
       atomicOperation->transactionCompleted ) {
 
974
       atomicOperation->m_transactionCompleted ) {
1035
975
    // The transaction job already completed, we can cleanup:
1036
976
    delete d->mAtomicOperations.take( d->mLatestAtomicOperationId );
1037
977
    d->mBatchOperationInProgress = false;
1115
1055
  return d->mGroupwareCommunication;
1116
1056
}
1117
1057
 
 
1058
void IncidenceChanger::setAutoAdjustRecurrence( bool enable )
 
1059
{
 
1060
  d->mAutoAdjustRecurrence = enable;
 
1061
}
 
1062
 
 
1063
bool IncidenceChanger::autoAdjustRecurrence() const
 
1064
{
 
1065
    return d->mAutoAdjustRecurrence;
 
1066
}
 
1067
 
 
1068
void IncidenceChanger::setInvitationPolicy( IncidenceChanger::InvitationPolicy policy )
 
1069
{
 
1070
  d->m_invitationPolicy = policy;
 
1071
}
 
1072
 
 
1073
IncidenceChanger::InvitationPolicy IncidenceChanger::invitationPolicy() const
 
1074
{
 
1075
  return d->m_invitationPolicy;
 
1076
}
 
1077
 
1118
1078
Akonadi::Collection IncidenceChanger::lastCollectionUsed() const
1119
1079
{
1120
1080
  return d->mLastCollectionUsed;
1154
1114
  return errorString;
1155
1115
}
1156
1116
 
 
1117
void IncidenceChanger::Private::adjustRecurrence( const KCalCore::Incidence::Ptr &originalIncidence,
 
1118
                                                  const KCalCore::Incidence::Ptr &incidence )
 
1119
{
 
1120
  if ( !originalIncidence || !incidence->recurs() || incidence->hasRecurrenceId() || !mAutoAdjustRecurrence
 
1121
       || !incidence->dirtyFields().contains( KCalCore::Incidence::FieldDtStart ) ) {
 
1122
    return;
 
1123
  }
 
1124
 
 
1125
  const QDate originalDate = originalIncidence->dtStart().date();
 
1126
  const QDate newStartDate = incidence->dtStart().date();
 
1127
 
 
1128
  if ( !originalDate.isValid() || !newStartDate.isValid() || originalDate == newStartDate )
 
1129
    return;
 
1130
 
 
1131
  KCalCore::Recurrence *recurrence = incidence->recurrence();
 
1132
  switch( recurrence->recurrenceType() ) {
 
1133
  case KCalCore::Recurrence::rWeekly: {
 
1134
    QBitArray days = recurrence->days();
 
1135
    const int oldIndex = originalDate.dayOfWeek()-1; // QDate returns [1-7];
 
1136
    const int newIndex = newStartDate.dayOfWeek()-1;
 
1137
    if ( oldIndex != newIndex ) {
 
1138
      days.clearBit( oldIndex );
 
1139
      days.setBit( newIndex );
 
1140
      recurrence->setWeekly( recurrence->frequency(), days );
 
1141
    }
 
1142
  }
 
1143
  default:
 
1144
    break;   // Other types not implemented
 
1145
  }
 
1146
 
 
1147
  // Now fix cases where dtstart would be bigger than the recurrence end rendering it impossible for a view to show it:
 
1148
  // To retrieve the recurrence end don't trust Recurrence::endDt() since it returns dtStart if the rrule's end is < than dtstart,
 
1149
  // it seems someone made Recurrence::endDt() more robust, but getNextOccurrences() still craps out. So lets fix it here
 
1150
  // there's no reason to write bogus ical to disk.
 
1151
  const QDate recurrenceEndDate = recurrence->defaultRRule() ? recurrence->defaultRRule()->endDt().date() : QDate();
 
1152
  if ( recurrenceEndDate.isValid() && recurrenceEndDate < newStartDate ) {
 
1153
    recurrence->setEndDate( newStartDate );
 
1154
  }
 
1155
}
 
1156
 
1157
1157
void IncidenceChanger::Private::cancelTransaction()
1158
1158
{
1159
1159
  if ( mBatchOperationInProgress ) {
1167
1167
  AtomicOperation *operation = mAtomicOperations[mLatestAtomicOperationId];
1168
1168
  Q_ASSERT( operation );
1169
1169
  Q_ASSERT( operation->rolledback() );
1170
 
  if ( !operation->pendingJobs() && operation->endCalled && operation->transactionCompleted ) {
 
1170
  if ( !operation->pendingJobs() && operation->m_endCalled && operation->m_transactionCompleted ) {
1171
1171
    delete mAtomicOperations.take(mLatestAtomicOperationId);
1172
1172
    mBatchOperationInProgress = false;
1173
1173
  }
1184
1184
    if ( change->type == ChangeTypeCreate ) {
1185
1185
      allow = true;
1186
1186
    } else if ( change->type == ChangeTypeModify ) {
1187
 
      allow = !operation->mItemIdsInOperation.contains( change->newItem.id() );
 
1187
      allow = !operation->m_itemIdsInOperation.contains( change->newItem.id() );
1188
1188
    } else if ( change->type == ChangeTypeDelete ) {
1189
1189
      DeletionChange::Ptr deletion = change.staticCast<DeletionChange>();
1190
1190
      foreach( Akonadi::Item::Id id, deletion->mItemIds ) {
1191
 
        if ( operation->mItemIdsInOperation.contains( id ) ) {
 
1191
        if ( operation->m_itemIdsInOperation.contains( id ) ) {
1192
1192
          allow = false;
1193
1193
          break;
1194
1194
        }
1254
1254
}
1255
1255
 
1256
1256
*/
 
1257
 
 
1258
AtomicOperation::AtomicOperation( IncidenceChanger::Private *icp,
 
1259
                                  uint ident ) : m_id ( ident )
 
1260
                                               , m_endCalled( false )
 
1261
                                               , m_numCompletedChanges( 0 )
 
1262
                                               , m_transactionCompleted( false )
 
1263
                                               , m_wasRolledback( false )
 
1264
                                               , m_transaction( 0 )
 
1265
                                               , m_incidenceChangerPrivate( icp )
 
1266
 
 
1267
{
 
1268
  Q_ASSERT( m_id != 0 );
 
1269
}
 
1270
 
 
1271
Akonadi::TransactionSequence *AtomicOperation::transaction()
 
1272
{
 
1273
  if ( !m_transaction ) {
 
1274
    m_transaction = new Akonadi::TransactionSequence;
 
1275
    m_transaction->setAutomaticCommittingEnabled( true );
 
1276
 
 
1277
    m_incidenceChangerPrivate->mAtomicOperationByTransaction.insert( m_transaction, m_id );
 
1278
 
 
1279
    QObject::connect( m_transaction, SIGNAL(result(KJob*)),
 
1280
                      m_incidenceChangerPrivate, SLOT(handleTransactionJobResult(KJob*)) );
 
1281
  }
 
1282
 
 
1283
  return m_transaction;
 
1284
}