Branch 'kolab/integration/4.13.0' - 2 commits - akonadi/calendar

Sandro Knauß knauss at kolabsys.com
Sun Aug 10 15:10:07 CEST 2014


 akonadi/calendar/freebusymanager.cpp       |    2 
 akonadi/calendar/incidencechanger.cpp      |  340 +++++++++++++++++++----------
 akonadi/calendar/incidencechanger.h        |   12 +
 akonadi/calendar/incidencechanger_p.cpp    |   10 
 akonadi/calendar/incidencechanger_p.h      |   26 +-
 akonadi/calendar/itiphandler.cpp           |   33 ++
 akonadi/calendar/itiphandler.h             |  178 +++++++++++++++
 akonadi/calendar/itiphandler_p.cpp         |   32 +-
 akonadi/calendar/itiphandler_p.h           |    5 
 akonadi/calendar/itiphandlerhelper_p.cpp   |  207 +++++++++++++----
 akonadi/calendar/itiphandlerhelper_p.h     |   36 +--
 akonadi/calendar/mailclient_p.cpp          |   43 ---
 akonadi/calendar/mailclient_p.h            |   23 +
 akonadi/calendar/mailscheduler_p.cpp       |    6 
 akonadi/calendar/mailscheduler_p.h         |    3 
 akonadi/calendar/tests/itiphandlertest.cpp |   74 +++++-
 akonadi/calendar/tests/mailclienttest.cpp  |   62 ++++-
 17 files changed, 819 insertions(+), 273 deletions(-)

New commits:
commit aebd3e86aaee5d2c42ae7d488c6575a587be6ef8
Author: Sandro Knauß <knauss at kolabsys.com>
Date:   Thu Jul 24 15:25:19 2014 +0200

    DialogInterception for IncienceChanger.
    
    REVIEW: 119434

diff --git a/akonadi/calendar/incidencechanger.cpp b/akonadi/calendar/incidencechanger.cpp
index 79d0f49..0d0bddd 100644
--- a/akonadi/calendar/incidencechanger.cpp
+++ b/akonadi/calendar/incidencechanger.cpp
@@ -43,7 +43,7 @@ using namespace KCalCore;
 # define RUNNING_UNIT_TESTS false
 #endif
 
-ITIPHandlerHelper::Action actionFromStatus(ITIPHandlerHelper::SendResult result)
+ITIPHandlerDialogDelegate::Action actionFromStatus(ITIPHandlerHelper::SendResult result)
 {
     //enum SendResult {
     //      Canceled,        /**< Sending was canceled by the user, meaning there are
@@ -55,11 +55,11 @@ ITIPHandlerHelper::Action actionFromStatus(ITIPHandlerHelper::SendResult result)
     //      Success
     switch (result) {
     case ITIPHandlerHelper::ResultCanceled:
-        return ITIPHandlerHelper::ActionDontSendMessage;
+        return ITIPHandlerDialogDelegate::ActionDontSendMessage;
     case ITIPHandlerHelper::ResultSuccess:
-        return ITIPHandlerHelper::ActionSendMessage;
+        return ITIPHandlerDialogDelegate::ActionSendMessage;
     default:
-        return ITIPHandlerHelper::ActionAsk;
+        return ITIPHandlerDialogDelegate::ActionAsk;
     }
 }
 
@@ -153,6 +153,7 @@ IncidenceChanger::Private::Private(bool enableHistory, ITIPHandlerComponentFacto
     qRegisterMetaType<Akonadi::Item>("Akonadi::Item");
     qRegisterMetaType<Akonadi::IncidenceChanger::ResultCode>(
         "Akonadi::IncidenceChanger::ResultCode");
+    qRegisterMetaType<ITIPHandlerHelper::SendResult>("ITIPHandlerHelper::SendResult");
 }
 
 IncidenceChanger::Private::~Private()
@@ -228,7 +229,6 @@ void IncidenceChanger::Private::performNextModification(Akonadi::Item::Id id)
 
 void IncidenceChanger::Private::handleTransactionJobResult(KJob *job)
 {
-    //kDebug();
     TransactionSequence *transaction = qobject_cast<TransactionSequence*>(job);
     Q_ASSERT(transaction);
     Q_ASSERT(mAtomicOperationByTransaction.contains(transaction));
@@ -259,59 +259,80 @@ void IncidenceChanger::Private::handleTransactionJobResult(KJob *job)
 
 void IncidenceChanger::Private::handleCreateJobResult(KJob *job)
 {
-    //kDebug();
-    QString errorString;
-    ResultCode resultCode = ResultCodeSuccess;
-
     Change::Ptr change = mChangeForJob.take(job);
-    mChangeById.remove(change->id);
 
     const ItemCreateJob *j = qobject_cast<const ItemCreateJob*>(job);
     Q_ASSERT(j);
     Akonadi::Item item = j->item();
 
-    QString description;
-    if (change->atomicOperationId != 0) {
-        AtomicOperation *a = mAtomicOperations[change->atomicOperationId];
-        a->m_numCompletedChanges++;
-        change->completed = true;
-        description = a->m_description;
-    }
 
     if (j->error()) {
+        const QString errorString = j->errorString();
+        ResultCode resultCode = ResultCodeJobError;
         item = change->newItem;
-        resultCode = ResultCodeJobError;
-        errorString = j->errorString();
+
         kError() << errorString;
         if (mShowDialogsOnError) {
             KMessageBox::sorry(change->parentWidget,
                                i18n("Error while trying to create calendar item. Error was: %1",
                                     errorString));
         }
+        mChangeById.remove(change->id);
+        change->errorString = errorString;
+        change->resultCode  = resultCode;
+        // puff, change finally goes out of scope, and emits the incidenceCreated signal.
     } else {
         Q_ASSERT(item.isValid());
         Q_ASSERT(item.hasPayload<KCalCore::Incidence::Ptr>());
         change->newItem = item;
-        handleInvitationsAfterChange(change);
-        // for user undo/redo
-        if (change->recordToHistory) {
-            mHistory->recordCreation(item, description, change->atomicOperationId);
+
+        if (change->useGroupwareCommunication) {
+            connect(change.data(),SIGNAL(dialogClosedAfterChange(int,ITIPHandlerHelper::SendResult)),
+                    SLOT(handleCreateJobResult2(int,ITIPHandlerHelper::SendResult)));
+            handleInvitationsAfterChange(change);
+        } else {
+            handleCreateJobResult2(change->id, ITIPHandlerHelper::ResultSuccess);
         }
     }
+}
+
+void IncidenceChanger::Private::handleCreateJobResult2(int changeId, ITIPHandlerHelper::SendResult status)
+{
+    Change::Ptr change = mChangeById[changeId];
+    Akonadi::Item item = change->newItem;
+
+    mChangeById.remove(changeId);
+
+    if (status == ITIPHandlerHelper::ResultFailAbortUpdate) {
+        kError() << "Sending invitations failed, but did not delete the incidence";
+    }
+
+    const uint atomicOperationId = change->atomicOperationId;
+    if (atomicOperationId != 0) {
+        mInvitationStatusByAtomicOperation.insert(atomicOperationId, status);
+    }
+
+    QString description;
+    if (change->atomicOperationId != 0) {
+        AtomicOperation *a = mAtomicOperations[change->atomicOperationId];
+        ++a->m_numCompletedChanges;
+        change->completed = true;
+        description = a->m_description;
+    }
 
-    change->errorString = errorString;
-    change->resultCode  = resultCode;
+    // for user undo/redo
+    if (change->recordToHistory) {
+        mHistory->recordCreation(item, description, change->atomicOperationId);
+    }
+
+    change->errorString = QString();
+    change->resultCode  = ResultCodeSuccess;
     // puff, change finally goes out of scope, and emits the incidenceCreated signal.
 }
 
 void IncidenceChanger::Private::handleDeleteJobResult(KJob *job)
 {
-    //kDebug();
-    QString errorString;
-    ResultCode resultCode = ResultCodeSuccess;
-
     Change::Ptr change = mChangeForJob.take(job);
-    mChangeById.remove(change->id);
 
     const ItemDeleteJob *j = qobject_cast<const ItemDeleteJob*>(job);
     const Item::List items = j->deletedItems();
@@ -329,8 +350,7 @@ void IncidenceChanger::Private::handleDeleteJobResult(KJob *job)
         description = a->m_description;
     }
     if (j->error()) {
-        resultCode = ResultCodeJobError;
-        errorString = j->errorString();
+        const QString errorString = j->errorString();
         kError() << errorString;
         if (mShowDialogsOnError) {
             KMessageBox::sorry(change->parentWidget,
@@ -342,26 +362,48 @@ void IncidenceChanger::Private::handleDeleteJobResult(KJob *job)
             // Werent deleted due to error
             mDeletedItemIds.remove(mDeletedItemIds.indexOf(item.id()));
         }
+        mChangeById.remove(change->id);
+        change->resultCode = ResultCodeJobError;
+        change->errorString = errorString;
+        change->emitCompletionSignal();
+
     } else { // success
         if (change->recordToHistory) {
             Q_ASSERT(mHistory);
             mHistory->recordDeletions(items, description, change->atomicOperationId);
         }
 
-        handleInvitationsAfterChange(change);
+        if (change->useGroupwareCommunication) {
+            connect(change.data(),SIGNAL(dialogClosedAfterChange(int,ITIPHandlerHelper::SendResult)),
+                    SLOT(handleDeleteJobResult2(int,ITIPHandlerHelper::SendResult)));
+            handleInvitationsAfterChange(change);
+        } else {
+            handleDeleteJobResult2(change->id, ITIPHandlerHelper::ResultSuccess);
+        }
+    }
+}
+
+void  IncidenceChanger::Private::handleDeleteJobResult2(int changeId, ITIPHandlerHelper::SendResult status)
+{
+    Change::Ptr change = mChangeById[changeId];
+    mChangeById.remove(change->id);
+
+    if (status == ITIPHandlerHelper::ResultSuccess) {
+        change->errorString = QString();
+        change->resultCode  = ResultCodeSuccess;
+    } else {
+        change->errorString = i18nc("errormessage for a job ended with an unexpected result", "An unknown error occurred");
+        change->resultCode = ResultCodeJobError;
     }
 
-    change->errorString = errorString;
-    change->resultCode  = resultCode;
     // puff, change finally goes out of scope, and emits the incidenceDeleted signal.
 }
 
+
+
 void IncidenceChanger::Private::handleModifyJobResult(KJob *job)
 {
-    QString errorString;
-    ResultCode resultCode = ResultCodeSuccess;
     Change::Ptr change = mChangeForJob.take(job);
-    mChangeById.remove(change->id);
 
     const ItemModifyJob *j = qobject_cast<const ItemModifyJob*>(job);
     const Item item = j->item();
@@ -377,16 +419,15 @@ void IncidenceChanger::Private::handleModifyJobResult(KJob *job)
         description = a->m_description;
     }
     if (j->error()) {
+        const QString errorString = j->errorString();
+        ResultCode resultCode = ResultCodeJobError;
         if (deleteAlreadyCalled(item.id())) {
             // User deleted the item almost at the same time he changed it. We could just return success
             // but the delete is probably already recorded to History, and that would make undo not work
             // in the proper order.
             resultCode = ResultCodeAlreadyDeleted;
-            errorString = j->errorString();
             kWarning() << "Trying to change item " << item.id() << " while deletion is in progress.";
         } else {
-            resultCode = ResultCodeJobError;
-            errorString = j->errorString();
             kError() << errorString;
         }
         if (mShowDialogsOnError) {
@@ -394,6 +435,16 @@ void IncidenceChanger::Private::handleModifyJobResult(KJob *job)
                                i18n("Error while trying to modify calendar item. Error was: %1",
                                     errorString));
         }
+        mChangeById.remove(change->id);
+        change->errorString = errorString;
+        change->resultCode  = resultCode;
+        // puff, change finally goes out of scope, and emits the incidenceModified signal.
+
+
+        QMetaObject::invokeMethod(this, "performNextModification",
+                              Qt::QueuedConnection,
+                              Q_ARG(Akonadi::Item::Id, item.id()));
+
     } else { // success
         ConflictPreventer::self()->mLatestRevisionByItemId[item.id()] = item.revision();
         change->newItem = item;
@@ -403,16 +454,31 @@ void IncidenceChanger::Private::handleModifyJobResult(KJob *job)
                                          description, change->atomicOperationId);
         }
 
-        handleInvitationsAfterChange(change);
+        if (change->useGroupwareCommunication) {
+            connect(change.data(),SIGNAL(dialogClosedAfterChange(int,ITIPHandlerHelper::SendResult)),
+                    SLOT(handleModifyJobResult2(int,ITIPHandlerHelper::SendResult)));
+            handleInvitationsAfterChange(change);
+        } else {
+            handleModifyJobResult2(change->id, ITIPHandlerHelper::ResultSuccess);
+        }
     }
+}
+
+void IncidenceChanger::Private::handleModifyJobResult2(int changeId, ITIPHandlerHelper::SendResult status)
+{
+    Change::Ptr change = mChangeById[changeId];
 
-    change->errorString = errorString;
-    change->resultCode  = resultCode;
+    mChangeById.remove(changeId);
+    if (change->atomicOperationId != 0) {
+        mInvitationStatusByAtomicOperation.insert(change->atomicOperationId, status);
+    }
+    change->errorString = QString();
+    change->resultCode  = ResultCodeSuccess;
     // puff, change finally goes out of scope, and emits the incidenceModified signal.
 
     QMetaObject::invokeMethod(this, "performNextModification",
                               Qt::QueuedConnection,
-                              Q_ARG(Akonadi::Item::Id, item.id()));
+                              Q_ARG(Akonadi::Item::Id, change->newItem.id()));
 }
 
 bool IncidenceChanger::Private::deleteAlreadyCalled(Akonadi::Item::Id id) const
@@ -420,20 +486,10 @@ bool IncidenceChanger::Private::deleteAlreadyCalled(Akonadi::Item::Id id) const
     return mDeletedItemIds.contains(id);
 }
 
-bool IncidenceChanger::Private::handleInvitationsBeforeChange(const Change::Ptr &change)
+void IncidenceChanger::Private::handleInvitationsBeforeChange(const Change::Ptr &change)
 {
-    bool result = true;
     if (mGroupwareCommunication) {
-        ITIPHandlerHelper handler(mFactory, change->parentWidget); // TODO make async
-
-        if (m_invitationPolicy == InvitationPolicySend) {
-            handler.setDefaultAction(ITIPHandlerHelper::ActionSendMessage);
-        } else if (m_invitationPolicy == InvitationPolicyDontSend) {
-            handler.setDefaultAction(ITIPHandlerHelper::ActionDontSendMessage);
-        } else if (mInvitationStatusByAtomicOperation.contains(change->atomicOperationId)) {
-            handler.setDefaultAction(actionFromStatus(mInvitationStatusByAtomicOperation.value(change->atomicOperationId)));
-        }
-
+        ITIPHandlerHelper::SendResult result = ITIPHandlerHelper::ResultSuccess;
         switch (change->type) {
         case IncidenceChanger::ChangeTypeCreate:
             // nothing needs to be done
@@ -441,7 +497,23 @@ bool IncidenceChanger::Private::handleInvitationsBeforeChange(const Change::Ptr
         case IncidenceChanger::ChangeTypeDelete:
         {
             ITIPHandlerHelper::SendResult status;
+            bool sendOk = true;
             Q_ASSERT(!change->originalItems.isEmpty());
+
+            ITIPHandlerHelper *handler = new ITIPHandlerHelper(mFactory, change->parentWidget);
+            handler->setParent(this);
+
+            if (m_invitationPolicy == InvitationPolicySend) {
+                handler->setDefaultAction(ITIPHandlerDialogDelegate::ActionSendMessage);
+            } else if (m_invitationPolicy == InvitationPolicyDontSend) {
+                handler->setDefaultAction(ITIPHandlerDialogDelegate::ActionDontSendMessage);
+            } else if (mInvitationStatusByAtomicOperation.contains(change->atomicOperationId)) {
+                handler->setDefaultAction(actionFromStatus(mInvitationStatusByAtomicOperation.value(change->atomicOperationId)));
+            }
+
+            connect(handler, SIGNAL(finished(Akonadi::ITIPHandlerHelper::SendResult,QString)),
+                    change.data(), SLOT(emitUserDialogClosedBeforeChange(Akonadi::ITIPHandlerHelper::SendResult)));
+
             foreach(const Akonadi::Item &item, change->originalItems) {
                 Q_ASSERT(item.hasPayload<KCalCore::Incidence::Ptr>());
                 Incidence::Ptr incidence = CalendarUtils::incidence(item);
@@ -451,16 +523,21 @@ bool IncidenceChanger::Private::handleInvitationsBeforeChange(const Change::Ptr
                 // We only send CANCEL if we're the organizer.
                 // If we're not, then we send REPLY with PartStat=Declined in handleInvitationsAfterChange()
                 if (Akonadi::CalendarUtils::thatIsMe(incidence->organizer()->email())) {
-                    status = handler.sendIncidenceDeletedMessage(KCalCore::iTIPCancel, incidence);
+                    //TODO: not to popup all delete message dialogs at once :(
+                    sendOk = false;
+                    handler->sendIncidenceDeletedMessage(KCalCore::iTIPCancel, incidence);
                     if (change->atomicOperationId) {
                         mInvitationStatusByAtomicOperation.insert(change->atomicOperationId, status);
                     }
-                    result = status != ITIPHandlerHelper::ResultFailAbortUpdate;
                     //TODO: with some status we want to break immediately
                 }
             }
+
+            if (sendOk) {
+                change->emitUserDialogClosedBeforeChange(result);
+            }
         }
-        break;
+        return;
         case IncidenceChanger::ChangeTypeModify:
         {
             if (change->originalItems.isEmpty()) {
@@ -481,15 +558,20 @@ bool IncidenceChanger::Private::handleInvitationsBeforeChange(const Change::Ptr
                 // "You're not organizer, do you want to modify event?" dialog in unit-tests, but want
                 // to emulate a "yes" and a "no" press.
                 if (m_invitationPolicy == InvitationPolicySend) {
-                    return true;
+                    change->emitUserDialogClosedBeforeChange(ITIPHandlerHelper::ResultSuccess);
+                    return;
                 } else if (m_invitationPolicy == InvitationPolicyDontSend) {
-                    return false;
+                    change->emitUserDialogClosedBeforeChange(ITIPHandlerHelper::ResultCanceled);
+                    return;
                 }
             }
 
+            ITIPHandlerHelper handler(mFactory, change->parentWidget);
             const bool modify = handler.handleIncidenceAboutToBeModified(newIncidence);
             if (modify) {
                 break;
+            } else {
+                result = ITIPHandlerHelper::ResultCanceled;
             }
 
             if (newIncidence->type() == oldIncidence->type()) {
@@ -497,30 +579,34 @@ bool IncidenceChanger::Private::handleInvitationsBeforeChange(const Change::Ptr
                 IncidenceBase *i2 = oldIncidence.data();
                 *i1 = *i2;
             }
-            result = false;
         }
         break;
         default:
             Q_ASSERT(false);
-            result = false;
+            result = ITIPHandlerHelper::ResultCanceled;
         }
+        change->emitUserDialogClosedBeforeChange(result);
+    } else {
+        change->emitUserDialogClosedBeforeChange(ITIPHandlerHelper::ResultSuccess);
     }
-    return result;
 }
 
-bool IncidenceChanger::Private::handleInvitationsAfterChange(const Change::Ptr &change)
+void IncidenceChanger::Private::handleInvitationsAfterChange(const Change::Ptr &change)
 {
     if (change->useGroupwareCommunication) {
-        ITIPHandlerHelper handler(mFactory, change->parentWidget);   // TODO make async
+        ITIPHandlerHelper *handler = new ITIPHandlerHelper(mFactory, change->parentWidget);
+        connect(handler, SIGNAL(finished(Akonadi::ITIPHandlerHelper::SendResult,QString)),
+                change.data(), SLOT(emitUserDialogClosedAfterChange(Akonadi::ITIPHandlerHelper::SendResult)));
+        handler->setParent(this);
 
         const bool alwaysSend = m_invitationPolicy == InvitationPolicySend;
         const bool neverSend = m_invitationPolicy == InvitationPolicyDontSend;
         if (alwaysSend) {
-            handler.setDefaultAction(ITIPHandlerHelper::ActionSendMessage);
+            handler->setDefaultAction(ITIPHandlerDialogDelegate::ActionSendMessage);
         }
 
         if (neverSend) {
-            handler.setDefaultAction(ITIPHandlerHelper::ActionDontSendMessage);
+            handler->setDefaultAction(ITIPHandlerDialogDelegate::ActionDontSendMessage);
         }
 
         switch (change->type) {
@@ -528,22 +614,15 @@ bool IncidenceChanger::Private::handleInvitationsAfterChange(const Change::Ptr &
         {
             Incidence::Ptr incidence = CalendarUtils::incidence(change->newItem);
             if (incidence->supportsGroupwareCommunication()) {
-                const ITIPHandlerHelper::SendResult status =
-                    handler.sendIncidenceCreatedMessage(KCalCore::iTIPRequest, incidence);
-
-                if (status == ITIPHandlerHelper::ResultFailAbortUpdate) {
-                    kError() << "Sending invitations failed, but did not delete the incidence";
-                }
-
-                const uint atomicOperationId = change->atomicOperationId;
-                if (atomicOperationId != 0) {
-                    mInvitationStatusByAtomicOperation.insert(atomicOperationId, status);
-                }
+                handler->sendIncidenceCreatedMessage(KCalCore::iTIPRequest, incidence);
+                return;
             }
         }
         break;
         case IncidenceChanger::ChangeTypeDelete:
         {
+            handler->deleteLater();
+            handler = 0;
             Q_ASSERT(!change->originalItems.isEmpty());
             foreach(const Akonadi::Item &item, change->originalItems) {
                 Q_ASSERT(item.hasPayload());
@@ -596,28 +675,31 @@ bool IncidenceChanger::Private::handleInvitationsAfterChange(const Change::Ptr &
             }
 
             if (!neverSend && !alwaysSend && mInvitationStatusByAtomicOperation.contains(change->atomicOperationId)) {
-                handler.setDefaultAction(actionFromStatus(mInvitationStatusByAtomicOperation.value(change->atomicOperationId)));
+                handler->setDefaultAction(actionFromStatus(mInvitationStatusByAtomicOperation.value(change->atomicOperationId)));
             }
 
             const bool attendeeStatusChanged = myAttendeeStatusChanged(newIncidence,
                                                oldIncidence,
                                                Akonadi::CalendarUtils::allEmails());
 
-            ITIPHandlerHelper::SendResult status = handler.sendIncidenceModifiedMessage(KCalCore::iTIPRequest,
-                                                   newIncidence,
-                                                   attendeeStatusChanged);
+            handler->sendIncidenceModifiedMessage(KCalCore::iTIPRequest, newIncidence, attendeeStatusChanged);
+            return;
 
-            if (change->atomicOperationId != 0) {
-                mInvitationStatusByAtomicOperation.insert(change->atomicOperationId, status);
-            }
         }
         break;
         default:
+            handler->deleteLater();
+            handler = 0;
             Q_ASSERT(false);
-            return false;
+            change->emitUserDialogClosedAfterChange(ITIPHandlerHelper::ResultCanceled);
+            return;
         }
+        handler->deleteLater();
+        handler  = 0;
+        change->emitUserDialogClosedAfterChange(ITIPHandlerHelper::ResultSuccess);
+    } else {
+        change->emitUserDialogClosedAfterChange(ITIPHandlerHelper::ResultSuccess);
     }
-    return true;
 }
 
 /** static */
@@ -658,7 +740,6 @@ int IncidenceChanger::createIncidence(const Incidence::Ptr &incidence,
                                       const Collection &collection,
                                       QWidget *parent)
 {
-    //kDebug();
     if (!incidence) {
         kWarning() << "An invalid payload is not allowed.";
         d->cancelTransaction();
@@ -702,7 +783,6 @@ int IncidenceChanger::deleteIncidence(const Item &item, QWidget *parent)
 
 int IncidenceChanger::deleteIncidences(const Item::List &items, QWidget *parent)
 {
-    //kDebug();
     if (items.isEmpty()) {
         kError() << "Delete what?";
         d->cancelTransaction();
@@ -774,31 +854,44 @@ int IncidenceChanger::deleteIncidences(const Item::List &items, QWidget *parent)
         return changeId;
     }
     change->originalItems = itemsToDelete;
-    d->handleInvitationsBeforeChange(change);
 
-    ItemDeleteJob *deleteJob = new ItemDeleteJob(itemsToDelete, d->parentJob(change));
-    d->mChangeForJob.insert(deleteJob, change);
     d->mChangeById.insert(changeId, change);
 
-    if (d->mBatchOperationInProgress) {
-        AtomicOperation *atomic = d->mAtomicOperations[atomicOperationId];
+    if (d->mGroupwareCommunication) {
+        connect(change.data(), SIGNAL(dialogClosedBeforeChange(int,ITIPHandlerHelper::SendResult)),
+                d, SLOT(deleteIncidences2(int,ITIPHandlerHelper::SendResult)));
+        d->handleInvitationsBeforeChange(change);
+    } else {
+        d->deleteIncidences2(changeId, ITIPHandlerHelper::ResultSuccess);
+    }
+    return changeId;
+}
+
+void IncidenceChanger::Private::deleteIncidences2(int changeId, ITIPHandlerHelper::SendResult status)
+{
+    Q_UNUSED(status);
+    Change::Ptr change = mChangeById[changeId];
+    const uint atomicOperationId = change->atomicOperationId;
+    ItemDeleteJob *deleteJob = new ItemDeleteJob(change->originalItems, parentJob(change));
+    mChangeForJob.insert(deleteJob, change);
+
+    if (mBatchOperationInProgress) {
+        AtomicOperation *atomic = mAtomicOperations[atomicOperationId];
         Q_ASSERT(atomic);
         atomic->addChange(change);
     }
 
-    foreach(const Item &item, itemsToDelete) {
-        d->mDeletedItemIds << item.id();
+    foreach(const Item &item, change->originalItems) {
+        mDeletedItemIds << item.id();
     }
 
     // Do some cleanup
-    if (d->mDeletedItemIds.count() > 100)
-        d->mDeletedItemIds.remove(0, 50);
+    if (mDeletedItemIds.count() > 100)
+        mDeletedItemIds.remove(0, 50);
 
     // QueuedConnection because of possible sync exec calls.
     connect(deleteJob, SIGNAL(result(KJob*)),
-            d, SLOT(handleDeleteJobResult(KJob*)), Qt::QueuedConnection);
-
-    return changeId;
+            SLOT(handleDeleteJobResult(KJob*)), Qt::QueuedConnection);
 }
 
 int IncidenceChanger::modifyIncidence(const Item &changedItem,
@@ -840,6 +933,7 @@ int IncidenceChanger::modifyIncidence(const Item &changedItem,
 
     if (!d->allowAtomicOperation(atomicOperationId, change)) {
         const QString errorString = d->showErrorDialog(ResultCodeDuplicateId, parent);
+
         change->resultCode = ResultCodeDuplicateId;
         change->errorString = errorString;
         d->cancelTransaction();
@@ -888,15 +982,32 @@ void IncidenceChanger::Private::performModification(Change::Ptr change)
         emitModifyFinished(q, changeId, newItem, ResultCodeRolledback, errorMessage);
         return;
     }
+    if (mGroupwareCommunication) {
+        connect(change.data(), SIGNAL(dialogClosedBeforeChange(int,ITIPHandlerHelper::SendResult)),
+                SLOT(performModification2(int,ITIPHandlerHelper::SendResult)));
+        handleInvitationsBeforeChange(change);
+    } else {
+        performModification2(change->id,  ITIPHandlerHelper::ResultSuccess);
+    }
+}
 
-    const bool userCancelled = !handleInvitationsBeforeChange(change);
-    if (userCancelled) {
+void IncidenceChanger::Private::performModification2(int changeId, ITIPHandlerHelper::SendResult status)
+{
+    Change::Ptr change  =  mChangeById[changeId];
+    const Item::Id id = change->newItem.id();
+    Akonadi::Item &newItem = change->newItem;
+    Q_ASSERT(newItem.isValid());
+    Q_ASSERT(newItem.hasPayload<Incidence::Ptr>());
+    if (status == ITIPHandlerHelper::ResultCanceled) { //TODO:fireout what is right here:)
         // User got a "You're not the organizer, do you really want to send" dialog, and said "no"
         kDebug() << "User cancelled, giving up";
-        emitModifyFinished(q, changeId, newItem, ResultCodeUserCanceled, QString());
+        emitModifyFinished(q, change->id, newItem, ResultCodeUserCanceled, QString());
         return;
     }
 
+    const uint atomicOperationId = change->atomicOperationId;
+    const bool hasAtomicOperationId = atomicOperationId != 0;
+
     QHash<Akonadi::Item::Id, int> &latestRevisionByItemId =
         ConflictPreventer::self()->mLatestRevisionByItemId;
     if (latestRevisionByItemId.contains(id) &&
diff --git a/akonadi/calendar/incidencechanger_p.cpp b/akonadi/calendar/incidencechanger_p.cpp
index 74cf138..063eb2b 100644
--- a/akonadi/calendar/incidencechanger_p.cpp
+++ b/akonadi/calendar/incidencechanger_p.cpp
@@ -30,6 +30,16 @@
 
 using namespace Akonadi;
 
+void Change::emitUserDialogClosedAfterChange(Akonadi::ITIPHandlerHelper::SendResult status)
+{
+    emit dialogClosedAfterChange(id, status);
+}
+
+void Change::emitUserDialogClosedBeforeChange(Akonadi::ITIPHandlerHelper::SendResult status)
+{
+    emit dialogClosedBeforeChange(id , status);
+}
+
 void IncidenceChanger::Private::loadCollections()
 {
     if (isLoadingCollections()) {
diff --git a/akonadi/calendar/incidencechanger_p.h b/akonadi/calendar/incidencechanger_p.h
index eb08eb1..f600970 100644
--- a/akonadi/calendar/incidencechanger_p.h
+++ b/akonadi/calendar/incidencechanger_p.h
@@ -45,8 +45,8 @@ namespace Akonadi {
 class TransactionSequence;
 class CollectionFetchJob;
 
-class Change {
-
+class Change : public QObject {
+    Q_OBJECT
 public:
     typedef QSharedPointer<Change> Ptr;
     typedef QList<Ptr> List;
@@ -97,6 +97,15 @@ public:
     bool completed;
     bool queuedModification;
     bool useGroupwareCommunication;
+
+signals:
+    void dialogClosedBeforeChange(int id, ITIPHandlerHelper::SendResult status);
+    void dialogClosedAfterChange(int id, ITIPHandlerHelper::SendResult status);
+
+public slots:
+    void emitUserDialogClosedAfterChange(Akonadi::ITIPHandlerHelper::SendResult status);
+    void emitUserDialogClosedBeforeChange(Akonadi::ITIPHandlerHelper::SendResult status);
+
 protected:
     IncidenceChanger *const changer;
 };
@@ -285,8 +294,8 @@ public:
     void cleanupTransaction();
     bool allowAtomicOperation(int atomicOperationId, const Change::Ptr &change) const;
 
-    bool handleInvitationsBeforeChange(const Change::Ptr &change);
-    bool handleInvitationsAfterChange(const Change::Ptr &change);
+    void handleInvitationsBeforeChange(const Change::Ptr &change);
+    void handleInvitationsAfterChange(const Change::Ptr &change);
     static bool myAttendeeStatusChanged(const KCalCore::Incidence::Ptr &newIncidence,
                                         const KCalCore::Incidence::Ptr &oldIncidence,
                                         const QStringList &myEmails);
@@ -299,6 +308,12 @@ public Q_SLOTS:
     void performNextModification(Akonadi::Item::Id id);
     void onCollectionsLoaded(KJob*);
 
+    void handleCreateJobResult2(int changeId, ITIPHandlerHelper::SendResult);
+    void handleDeleteJobResult2(int changeId, ITIPHandlerHelper::SendResult);
+    void handleModifyJobResult2(int changeId, ITIPHandlerHelper::SendResult);
+    void performModification2(int changeId, ITIPHandlerHelper::SendResult);
+    void deleteIncidences2(int changeId, ITIPHandlerHelper::SendResult);
+
 public:
     int mLatestChangeId;
     QHash<const KJob*,Change::Ptr> mChangeForJob;
diff --git a/akonadi/calendar/itiphandler.cpp b/akonadi/calendar/itiphandler.cpp
index a4bc9f1..ffaccf5 100644
--- a/akonadi/calendar/itiphandler.cpp
+++ b/akonadi/calendar/itiphandler.cpp
@@ -76,6 +76,11 @@ MailTransport::MessageQueueJob *ITIPHandlerComponentFactory::createMessageQueueJ
     return new MailTransport::MessageQueueJob(parent);
 }
 
+ITIPHandlerDialogDelegate *ITIPHandlerComponentFactory::createITIPHanderDialogDelegate(const KCalCore::Incidence::Ptr &incidence, KCalCore::iTIPMethod method, QWidget *parent)
+{
+    return new ITIPHandlerDialogDelegate(incidence, method, parent);
+}
+
 ITIPHandler::ITIPHandler(QObject *parent) : QObject(parent)
     , d(new Private(/*factory=*/0, this))
 {
diff --git a/akonadi/calendar/itiphandler.h b/akonadi/calendar/itiphandler.h
index dcbc6b6..2182dbe 100644
--- a/akonadi/calendar/itiphandler.h
+++ b/akonadi/calendar/itiphandler.h
@@ -32,6 +32,10 @@
 #include <kcalcore/incidence.h>
 #include <kcalcore/schedulemessage.h>
 
+#include <KGuiItem>
+#include <KLocalizedString>
+
+#include <QObject>
 #include <QString>
 #include <QWidget>
 
@@ -60,7 +64,130 @@ public:
 };
 
 /**
- * @short Factory to create MailTransport::MessageQueueJob jobs.
+ * @short Ui delegate for dialogs raised by the ITIPHandler and IncidenceChanger.
+ * @since 4.15
+ */
+class AKONADI_CALENDAR_EXPORT ITIPHandlerDialogDelegate : public QObject {
+    Q_OBJECT
+public:
+    // Posible default actions
+    enum Action {
+        ActionAsk,              /**< Ask the user for a descision */
+        ActionSendMessage,      /**< Answer with Yes */
+        ActionDontSendMessage   /**< Answer with No */
+    };
+
+    // How will reveive the mail afterwards
+    enum Recipient {
+        Organizer,              /**< the organizer of the incidence */
+        Attendees               /**< the attendees of the incidence */
+    };
+
+    /**
+     * Creates a new AskDelegator
+     */
+    explicit ITIPHandlerDialogDelegate(const KCalCore::Incidence::Ptr &incidence, KCalCore::iTIPMethod method, QWidget *parent = 0);
+
+    /*
+     * Opens a Dialog, when an incidence is created
+     * The function must emit a dialogClosed signal with the user's answer
+     *
+     * @param recipient: to who the mail will be sent afterwards
+     * @param question: dialog's question
+     * @param action: Should the dialog been shown or should a default answer be returned
+     * @param buttonYes: dialog's yes answer
+     * @param buttonNo: dialog's no answer
+     */
+    virtual void openDialogIncidenceCreated(Recipient recipient,
+                                    const QString &question,
+                                    Action action = ActionAsk,
+                                    const KGuiItem &buttonYes = KGuiItem(i18nc("@action:button dialog positive answer", "Send Email")),
+                                    const KGuiItem &buttonNo = KGuiItem(i18nc("@action:button dialog negative answer", "Do Not Send")));
+
+    /*
+     * Opens a Dialog, when an incidence is modified
+     * The function must emit a dialogClosed signal with the user's answer
+     *
+     * @param attendeeStatusChanged: Only the status of the own attendeeStatus is changed
+     * @param recipient: to who the mail will be sent afterwards
+     * @param question: dialog's question
+     * @param action: Should the dialog been shown or should a default answer be returned
+     * @param buttonYes: dialog's yes answer
+     * @param buttonNo: dialog's no answer
+     */
+    virtual void openDialogIncidenceModified(bool attendeeStatusChanged,
+                                     Recipient recipient,
+                                     const QString &question,
+                                     Action action = ActionAsk,
+                                     const KGuiItem &buttonYes = KGuiItem(i18nc("@action:button dialog positive answer", "Send Email")),
+                                     const KGuiItem &buttonNo = KGuiItem(i18nc("@action:button dialog negative answer", "Do Not Send")));
+
+    /*
+     * Opens a Dialog, when an incidence is deleted
+     * The function must emit a dialogClosed signal with the user's answer
+     *
+     * @param recipient: to who the mail will be sent afterwards
+     * @param question: dialog's question
+     * @param action: Should the dialog been shown or should a default answer be returned
+     * @param buttonYes: dialog's yes answer
+     * @param buttonNo: dialog's no answer
+     */
+    virtual void openDialogIncidenceDeleted(Recipient recipient,
+                                    const QString &question,
+                                    Action action = ActionAsk,
+                                    const KGuiItem &buttonYes = KGuiItem(i18nc("@action:button dialog positive answer", "Send Email")),
+                                    const KGuiItem &buttonNo = KGuiItem(i18nc("@action:button dialog negative answer", "Do Not Send")));
+    /*
+     * Opens a Dialog, when mail was sended
+     * The function must emit a dialogClosed signal with the user's answer
+     *
+     * @param question: dialog's question
+     * @param action: Should the dialog been shown or should a default answer be returned
+     * @param buttonYes: dialog's yes answer
+     * @param buttonNo: dialog's no answer
+     */
+    virtual void openDialogSchedulerFinished(const QString &question,
+                                     Action action = ActionAsk,
+                                     const KGuiItem &buttonYes = KGuiItem(i18nc("@action:button dialog positive answer", "Send Email")),
+                                     const KGuiItem &buttonNo = KGuiItem(i18nc("@action:button dialog negative answer", "Do Not Send")));
+
+Q_SIGNALS:
+    /*
+     * Signal is emited, when the user has answered the dialog or the defaultAction is used
+     * @param answer: answer should be part of KMessageBox:ButtonCode, keep in mind that it is a YesNoDialog so normally it should be KMessageBox::Yes or KMessageBox::No
+     * @param method: itip method
+     * @param incidence: purpose of the dialog
+     */
+    void dialogClosed(int answer, KCalCore::iTIPMethod method, const KCalCore::Incidence::Ptr &incidence);
+
+protected:
+    /*
+     * Opens a KMessageBox::questionYesNo with the question
+     *
+     * @return KMessageBox::Yes or KMessageBox::No
+     *
+     * @param question: dialog's question
+     * @param action: Should the dialog been shown or should a default answer be returned
+     * @param buttonYes: dialog's yes answer
+     * @param buttonNo: dialog's no answer
+     */
+    int askUserIfNeeded(const QString &question,
+                        Action action,
+                        const KGuiItem &buttonYes,
+                        const KGuiItem &buttonNo) const;
+
+    // parent of the dialog
+    QWidget *mParent;
+
+    // Incidence related to the dialog
+    KCalCore::Incidence::Ptr mIncidence;
+
+    // ITIPMethod related to the dialog
+    KCalCore::iTIPMethod mMethod;
+};
+
+/**
+ * @short Factory to create MailTransport::MessageQueueJob jobs or ITIPHandlerDialogDelegate objects.
  * @since 4.15
  */
 class AKONADI_CALENDAR_EXPORT ITIPHandlerComponentFactory : public QObject
@@ -84,6 +211,15 @@ public:
      * @param parent of the MailTransport::MessageQueueJob object
      */
     virtual MailTransport::MessageQueueJob* createMessageQueueJob(const KCalCore::IncidenceBase::Ptr &incidence, const KPIMIdentities::Identity &identity, QObject *parent = 0);
+
+    /*
+     * @return A new ITIPHandlerDialogDelegate object
+     * @param incidence the purpose of the dialogs
+     * @param method the ITIPMethod
+     * @parent parent of the AskDelegator
+     *
+     */
+    virtual ITIPHandlerDialogDelegate* createITIPHanderDialogDelegate(const KCalCore::Incidence::Ptr &incidence, KCalCore::iTIPMethod method, QWidget *parent = 0);
 };
 
 /**
diff --git a/akonadi/calendar/itiphandler_p.cpp b/akonadi/calendar/itiphandler_p.cpp
index db1de2f..c8736a3 100644
--- a/akonadi/calendar/itiphandler_p.cpp
+++ b/akonadi/calendar/itiphandler_p.cpp
@@ -41,6 +41,9 @@ ITIPHandler::Private::Private(ITIPHandlerComponentFactory *factory, ITIPHandler
 
     connect(m_helper, SIGNAL(finished(Akonadi::ITIPHandlerHelper::SendResult,QString)),
             SLOT(onHelperFinished(Akonadi::ITIPHandlerHelper::SendResult,QString)));
+
+    connect(m_helper, SIGNAL(sendIncidenceModifiedMessageFinished(ITIPHandlerHelper::SendResult,KCalCore::iTIPMethod,KCalCore::Incidence::Ptr)),
+            SLOT(onHelperModifyDialogClosed(ITIPHandlerHelper::SendResult,KCalCore::iTIPMethod,KCalCore::Incidence::Ptr)));
 }
 
 void ITIPHandler::Private::onSchedulerFinished(Akonadi::Scheduler::Result result,
@@ -127,18 +130,12 @@ void ITIPHandler::Private::finishProcessiTIPMessage(Akonadi::MailScheduler::Resu
         if (success) {
             // send update to all attendees
             Q_ASSERT(m_incidence);
-            ITIPHandlerHelper::SendResult sendResult
-                = m_helper->sendIncidenceModifiedMessage(KCalCore::iTIPRequest,
+            m_helper->setDialogParent(m_parentWidget);
+            m_helper->sendIncidenceModifiedMessage(KCalCore::iTIPRequest,
                         KCalCore::Incidence::Ptr(m_incidence->clone()),
                         false);
             m_incidence.clear();
-            if (sendResult == ITIPHandlerHelper::ResultNoSendingNeeded ||
-                    sendResult == ITIPHandlerHelper::ResultCanceled) {
-                emit q->iTipMessageSent(ResultSuccess, QString());
-            } else {
-                // ITIPHandlerHelper is working hard and slot onHelperFinished will be called soon
-                return;
-            }
+            return;
         } else {
             //fall through
         }
@@ -148,6 +145,15 @@ void ITIPHandler::Private::finishProcessiTIPMessage(Akonadi::MailScheduler::Resu
                                  success ? QString() : i18n("Error: %1", errorMessage));
 }
 
+void ITIPHandler::Private::onHelperModifyDialogClosed(ITIPHandlerHelper::SendResult sendResult, KCalCore::iTIPMethod /*method*/, const KCalCore::Incidence::Ptr &incidence)
+{
+    if (sendResult == ITIPHandlerHelper::ResultNoSendingNeeded ||
+        sendResult == ITIPHandlerHelper::ResultCanceled) {
+            emit q->iTipMessageSent(ResultSuccess, QString());
+    }
+}
+
+
 void ITIPHandler::Private::finishSendiTIPMessage(Akonadi::MailScheduler::Result result,
         const QString &errorMessage)
 {
diff --git a/akonadi/calendar/itiphandler_p.h b/akonadi/calendar/itiphandler_p.h
index 63040b2..aaae4fd 100644
--- a/akonadi/calendar/itiphandler_p.h
+++ b/akonadi/calendar/itiphandler_p.h
@@ -92,6 +92,8 @@ private Q_SLOTS:
     void onHelperFinished(Akonadi::ITIPHandlerHelper::SendResult result,
                           const QString &errorMessage);
 
+    void onHelperModifyDialogClosed(ITIPHandlerHelper::SendResult result, KCalCore::iTIPMethod method, const KCalCore::Incidence::Ptr &incidence);
+
     void onCounterProposalDelegateFinished(bool success, const QString &errorMessage);
 };
 
diff --git a/akonadi/calendar/itiphandlerhelper_p.cpp b/akonadi/calendar/itiphandlerhelper_p.cpp
index d72e89a..2af8f3f 100644
--- a/akonadi/calendar/itiphandlerhelper_p.cpp
+++ b/akonadi/calendar/itiphandlerhelper_p.cpp
@@ -65,28 +65,65 @@ QString proposalComment(const KCalCore::Incidence::Ptr &incidence)
     return comment;
 }
 
-int ITIPHandlerHelper::askUserIfNeeded(const QString &question,
-                                       bool ignoreDefaultAction,
-                                       const KGuiItem &buttonYes,
-                                       const KGuiItem &buttonNo) const
+ITIPHandlerDialogDelegate::ITIPHandlerDialogDelegate(const KCalCore::Incidence::Ptr &incidence, KCalCore::iTIPMethod method, QWidget *parent)
+    : mParent(parent)
+    , mIncidence(incidence)
+    , mMethod(method)
 {
-    Q_ASSERT_X(!question.isEmpty(), "ITIPHandlerHelper::askUser", "ask what?");
+}
 
-    if (ignoreDefaultAction || mDefaultAction == ITIPHandlerHelper::ActionAsk)
-        return KMessageBox::questionYesNo(mParent, question, i18n("Group Scheduling Email"),
-                                          buttonYes, buttonNo);
 
-    switch (mDefaultAction) {
-    case ITIPHandlerHelper::ActionSendMessage:
+int ITIPHandlerDialogDelegate::askUserIfNeeded(const QString &question, Action action,
+                                  const KGuiItem &buttonYes, const KGuiItem &buttonNo) const
+{
+    Q_ASSERT_X(!question.isEmpty(), "ITIPHandlerHelper::askUser", "ask what?");
+
+    switch (action) {
+    case ActionSendMessage:
         return KMessageBox::Yes;
-    case ITIPHandlerHelper::ActionDontSendMessage:
+    case ActionDontSendMessage:
         return KMessageBox::No;
     default:
-        Q_ASSERT(false);
-        return 0;
+        return KMessageBox::questionYesNo(mParent, question, i18n("Group Scheduling Email"),
+                                          buttonYes, buttonNo);
     }
 }
 
+void ITIPHandlerDialogDelegate::openDialogIncidenceCreated(Recipient recipient,
+                                              const QString &question, Action action,
+                                              const KGuiItem &buttonYes, const KGuiItem &buttonNo)
+{
+    Q_UNUSED(recipient);
+    const int messageBoxReturnCode = askUserIfNeeded(question, action, buttonYes, buttonNo);
+    emit dialogClosed(messageBoxReturnCode, mMethod, mIncidence);
+}
+
+void ITIPHandlerDialogDelegate::openDialogIncidenceModified(bool attendeeStatusChanged, Recipient recipient,
+                                               const QString &question, Action action,
+                                               const KGuiItem &buttonYes, const KGuiItem &buttonNo)
+{
+    Q_UNUSED(attendeeStatusChanged);
+    Q_UNUSED(recipient);
+    const int messageBoxReturnCode = askUserIfNeeded(question, action, buttonYes, buttonNo);
+    emit dialogClosed(messageBoxReturnCode, mMethod, mIncidence);
+}
+
+void ITIPHandlerDialogDelegate::openDialogIncidenceDeleted(Recipient recipient,
+                                              const QString &question, Action action,
+                                              const KGuiItem &buttonYes, const KGuiItem &buttonNo)
+{
+    Q_UNUSED(recipient);
+    const int messageBoxReturnCode = askUserIfNeeded(question, action, buttonYes, buttonNo);
+    emit dialogClosed(messageBoxReturnCode, mMethod, mIncidence);
+}
+
+void ITIPHandlerDialogDelegate::openDialogSchedulerFinished(const QString &question, Action action,
+                                               const KGuiItem &buttonYes, const KGuiItem &buttonNo)
+{
+    const int messageBoxReturnCode = askUserIfNeeded(question, action, buttonYes, buttonNo);
+    emit dialogClosed(messageBoxReturnCode, mMethod, mIncidence);
+}
+
 ITIPHandlerHelper::SendResult
 ITIPHandlerHelper::sentInvitation(int messageBoxReturnCode,
                                   const KCalCore::Incidence::Ptr &incidence,
@@ -114,9 +151,11 @@ ITIPHandlerHelper::sentInvitation(int messageBoxReturnCode,
         return ITIPHandlerHelper::ResultSuccess;
 
     } else if (messageBoxReturnCode == KMessageBox::No) {
+        emit finished(ITIPHandlerHelper::ResultCanceled, QString());
         return ITIPHandlerHelper::ResultCanceled;
     } else {
         Q_ASSERT(false);   // TODO: Figure out if/how this can happen (by closing the dialog with x??)
+        emit finished(ITIPHandlerHelper::ResultCanceled, QString());
         return ITIPHandlerHelper::ResultCanceled;
     }
 }
@@ -149,9 +188,11 @@ bool ITIPHandlerHelper::weNeedToSendMailFor(const KCalCore::Incidence::Ptr &inci
 }
 
 ITIPHandlerHelper::ITIPHandlerHelper(ITIPHandlerComponentFactory *factory, QWidget *parent)
-    : mDefaultAction(ITIPHandlerHelper::ActionAsk)
+    : QObject(parent)
+    , mDefaultAction(ITIPHandlerDialogDelegate::ActionAsk)
     , mParent(parent)
-    , m_scheduler(new MailScheduler(factory, parent))
+    , m_factory(factory ? factory : new ITIPHandlerComponentFactory(this))
+    , m_scheduler(new MailScheduler(m_factory, this))
     , m_status(StatusNone)
 {
     connect(m_scheduler, SIGNAL(transactionFinished(Akonadi::Scheduler::Result,QString)),
@@ -167,13 +208,12 @@ void ITIPHandlerHelper::setDialogParent(QWidget *parent)
     mParent = parent;
 }
 
-void ITIPHandlerHelper::setDefaultAction(Action action)
+void ITIPHandlerHelper::setDefaultAction(ITIPHandlerDialogDelegate::Action action)
 {
     mDefaultAction = action;
 }
 
-ITIPHandlerHelper::SendResult
-ITIPHandlerHelper::sendIncidenceCreatedMessage(KCalCore::iTIPMethod method,
+void ITIPHandlerHelper::sendIncidenceCreatedMessage(KCalCore::iTIPMethod method,
         const KCalCore::Incidence::Ptr &incidence)
 {
     /// When we created the incidence, we *must* be the organizer.
@@ -182,12 +222,15 @@ ITIPHandlerHelper::sendIncidenceCreatedMessage(KCalCore::iTIPMethod method,
         kError() << "We should be the organizer of this incidence!"
                  << "; email= "       << incidence->organizer()->email()
                  << "; thatIsMe() = " << Akonadi::CalendarUtils::thatIsMe(incidence->organizer()->email());
-        Q_ASSERT(false);
-        return ITIPHandlerHelper::ResultFailAbortUpdate;
+        emit sendIncidenceCreatedMessageFinished(ITIPHandlerHelper::ResultFailAbortUpdate, method, incidence);
+        emit finished(ITIPHandlerHelper::ResultFailAbortUpdate, QString());
+        return;
     }
 
     if (!weNeedToSendMailFor(incidence)) {
-        return ITIPHandlerHelper::ResultNoSendingNeeded;
+        emit sendIncidenceCreatedMessageFinished(ITIPHandlerHelper::ResultNoSendingNeeded, method, incidence);
+        emit finished(ITIPHandlerHelper::ResultNoSendingNeeded, QString());
+        return;
     }
 
     QString question;
@@ -204,8 +247,16 @@ ITIPHandlerHelper::sendIncidenceCreatedMessage(KCalCore::iTIPMethod method,
                         "Should an email be sent to the attendees?");
     }
 
-    const int messageBoxReturnCode = askUserIfNeeded(question, false);
-    return sentInvitation(messageBoxReturnCode, incidence, method);
+    ITIPHandlerDialogDelegate *askDelegator = m_factory->createITIPHanderDialogDelegate(incidence, method, mParent);
+    connect(askDelegator, SIGNAL(dialogClosed(int, KCalCore::iTIPMethod, KCalCore::Incidence::Ptr)),
+            SLOT(slotIncidenceCreatedDialogClosed(int, KCalCore::iTIPMethod, KCalCore::Incidence::Ptr)));
+    askDelegator->openDialogIncidenceCreated(ITIPHandlerDialogDelegate::Attendees, question, mDefaultAction);
+}
+
+void ITIPHandlerHelper::slotIncidenceCreatedDialogClosed(int messageBoxReturnCode, KCalCore::iTIPMethod method, const KCalCore::Incidence::Ptr &incidence)
+{
+    ITIPHandlerHelper::SendResult status = sentInvitation(messageBoxReturnCode, incidence, method);
+    emit sendIncidenceCreatedMessageFinished(status, method, incidence);
 }
 
 bool ITIPHandlerHelper::handleIncidenceAboutToBeModified(const KCalCore::Incidence::Ptr &incidence)
@@ -239,11 +290,14 @@ bool ITIPHandlerHelper::handleIncidenceAboutToBeModified(const KCalCore::Inciden
     }
 }
 
-ITIPHandlerHelper::SendResult
-ITIPHandlerHelper::sendIncidenceModifiedMessage(KCalCore::iTIPMethod method,
+void ITIPHandlerHelper::sendIncidenceModifiedMessage(KCalCore::iTIPMethod method,
         const KCalCore::Incidence::Ptr &incidence,
         bool attendeeStatusChanged)
 {
+    ITIPHandlerDialogDelegate *askDelegator = m_factory->createITIPHanderDialogDelegate(incidence, method, mParent);
+
+    connect(askDelegator, SIGNAL(dialogClosed(int,KCalCore::iTIPMethod,KCalCore::Incidence::Ptr)),
+            SLOT(slotIncidenceModifiedDialogClosed(int,KCalCore::iTIPMethod,KCalCore::Incidence::Ptr)));
     // For a modified incidence, either we are the organizer or someone else.
     if (weAreOrganizerOf(incidence)) {
         if (weNeedToSendMailFor(incidence)) {
@@ -251,10 +305,13 @@ ITIPHandlerHelper::sendIncidenceModifiedMessage(KCalCore::iTIPMethod method,
                                           "Do you want to email the attendees an update message?",
                                           incidence->summary());
 
-            const int messageBoxReturnCode = askUserIfNeeded(question, false, KGuiItem(i18n("Send Update")));
-            return sentInvitation(messageBoxReturnCode, incidence, method);
+            askDelegator->openDialogIncidenceModified(attendeeStatusChanged, ITIPHandlerDialogDelegate::Attendees, question, mDefaultAction, KGuiItem(i18n("Send Update")));
+            return;
         } else {
-            return ResultNoSendingNeeded;
+            emit sendIncidenceModifiedMessageFinished(ITIPHandlerHelper::ResultNoSendingNeeded, method, incidence);
+            emit finished(ITIPHandlerHelper::ResultNoSendingNeeded, QString());
+            delete askDelegator;
+            return;
         }
 
     } else if (incidence->type() == KCalCore::Incidence::TypeTodo) {
@@ -266,8 +323,8 @@ ITIPHandlerHelper::sendIncidenceModifiedMessage(KCalCore::iTIPMethod method,
 
         const QString question = i18n("Do you want to send a status update to the "
                                       "organizer of this task?");
-        const int messageBoxReturnCode = askUserIfNeeded(question, false, KGuiItem(i18n("Send Update")));
-        return sentInvitation(messageBoxReturnCode, incidence, method);
+        askDelegator->openDialogIncidenceModified(attendeeStatusChanged, ITIPHandlerDialogDelegate::Organizer, question, mDefaultAction, KGuiItem(i18n("Send Update")));
+        return;
 
     } else if (incidence->type() == KCalCore::Incidence::TypeEvent) {
         if (attendeeStatusChanged && method == KCalCore::iTIPRequest) {
@@ -275,21 +332,37 @@ ITIPHandlerHelper::sendIncidenceModifiedMessage(KCalCore::iTIPMethod method,
             const QString question =
                 i18n("Your status as an attendee of this event changed. "
                      "Do you want to send a status update to the event organizer?");
-            const int messageBoxReturnCode = askUserIfNeeded(question, false, KGuiItem(i18n("Send Update")));
-            return sentInvitation(messageBoxReturnCode, incidence, method);
+
+                askDelegator->openDialogIncidenceModified(attendeeStatusChanged, ITIPHandlerDialogDelegate::Organizer, question, mDefaultAction, KGuiItem(i18n("Send Update")));
+            return;
         } else {
-            return sentInvitation(KMessageBox::Yes, incidence, method);
+            slotIncidenceModifiedDialogClosed(KMessageBox::Yes, method, incidence);
+            delete askDelegator;
+            return;
         }
     }
     Q_ASSERT(false);   // Shouldn't happen.
-    return ResultNoSendingNeeded;
+    emit sendIncidenceModifiedMessageFinished(ITIPHandlerHelper::ResultNoSendingNeeded, method, incidence);
+    emit finished(ITIPHandlerHelper::ResultNoSendingNeeded, QString());
+    delete askDelegator;
 }
 
-ITIPHandlerHelper::SendResult
-ITIPHandlerHelper::sendIncidenceDeletedMessage(KCalCore::iTIPMethod method,
+void ITIPHandlerHelper::slotIncidenceModifiedDialogClosed(int messageBoxReturnCode, KCalCore::iTIPMethod method, const KCalCore::Incidence::Ptr &incidence)
+{
+    ITIPHandlerHelper::SendResult status = sentInvitation(messageBoxReturnCode, incidence, method);
+    emit sendIncidenceModifiedMessageFinished(status, method, incidence);
+}
+
+void ITIPHandlerHelper::sendIncidenceDeletedMessage(KCalCore::iTIPMethod method,
         const KCalCore::Incidence::Ptr &incidence)
 {
     Q_ASSERT(incidence);
+
+    ITIPHandlerDialogDelegate *askDelegator = m_factory->createITIPHanderDialogDelegate(incidence, method, mParent);
+
+    connect(askDelegator, SIGNAL(dialogClosed(int,KCalCore::iTIPMethod,KCalCore::Incidence::Ptr)),
+            SLOT(slotIncidenceDeletedDialogClosed(int,KCalCore::iTIPMethod,KCalCore::Incidence::Ptr)));
+
     // For a modified incidence, either we are the organizer or someone else.
     if (weAreOrganizerOf(incidence)) {
         if (weNeedToSendMailFor(incidence)) {
@@ -307,11 +380,13 @@ ITIPHandlerHelper::sendIncidenceDeletedMessage(KCalCore::iTIPMethod method,
                                 "Do you want to email the attendees that the journal is canceled?",
                                 incidence->summary());
             }
-
-            const int messageBoxReturnCode = askUserIfNeeded(question, false);
-            return sentInvitation(messageBoxReturnCode, incidence, method);
+            askDelegator->openDialogIncidenceDeleted(ITIPHandlerDialogDelegate::Attendees, question, mDefaultAction);
+            return;
         } else {
-            return ResultNoSendingNeeded;
+            emit sendIncidenceDeletedMessageFinished(ITIPHandlerHelper::ResultNoSendingNeeded, method, incidence);
+            emit finished(ITIPHandlerHelper::ResultNoSendingNeeded, QString());
+            delete askDelegator;
+            return;
         }
 
     } else if (incidence->type() != KCalCore::Incidence::TypeEvent) {
@@ -326,9 +401,9 @@ ITIPHandlerHelper::sendIncidenceDeletedMessage(KCalCore::iTIPMethod method,
                                       "organizer of this task?") :
                                  i18n("Do you want to send a status update to the "
                                       "organizer of this journal?");
-
-        const int messageBoxReturnCode = askUserIfNeeded(question, false, KGuiItem(i18n("Send Update")));
-        return sentInvitation(messageBoxReturnCode, incidence, method);
+        askDelegator->openDialogIncidenceDeleted(ITIPHandlerDialogDelegate::Organizer, question, mDefaultAction,
+                                                  KGuiItem(i18nc("@action:button dialog positive answer","Send Update")));
+        return;
     } else if (incidence->type() == KCalCore::Incidence::TypeEvent) {
 
         const QStringList myEmails = Akonadi::CalendarUtils::allEmails();
@@ -347,17 +422,29 @@ ITIPHandlerHelper::sendIncidenceDeletedMessage(KCalCore::iTIPMethod method,
             QString question = i18n("You had previously accepted an invitation to this event. "
                                     "Do you want to send an updated response to the organizer "
                                     "declining the invitation?");
-            int messageBoxReturnCode = askUserIfNeeded(question, false, KGuiItem(i18n("Send Update")));
-            return sentInvitation(messageBoxReturnCode, incidence, method);
+            askDelegator->openDialogIncidenceDeleted(ITIPHandlerDialogDelegate::Organizer, question, mDefaultAction,
+                                                     KGuiItem(i18nc("@action:button dialog positive answer","Send Update")));
+            return;
         } else {
             // We did not accept the event before and delete it from our calendar agian,
             // so there is no need to notify people.
-            return ITIPHandlerHelper::ResultNoSendingNeeded;
+            emit sendIncidenceDeletedMessageFinished(ITIPHandlerHelper::ResultNoSendingNeeded, method, incidence);
+            emit finished(ITIPHandlerHelper::ResultNoSendingNeeded, QString());
+            delete askDelegator;
+            return;
         }
     }
 
     Q_ASSERT(false);   // Shouldn't happen.
-    return ResultNoSendingNeeded;
+    emit sendIncidenceDeletedMessageFinished(ITIPHandlerHelper::ResultNoSendingNeeded, method, incidence);
+    emit finished(ITIPHandlerHelper::ResultNoSendingNeeded, QString());
+    delete askDelegator;
+}
+
+void ITIPHandlerHelper::slotIncidenceDeletedDialogClosed(const int messageBoxReturnCode, KCalCore::iTIPMethod method, const KCalCore::Incidence::Ptr &incidence)
+{
+    ITIPHandlerHelper::SendResult status = sentInvitation(messageBoxReturnCode, incidence, method);
+    emit sendIncidenceDeletedMessageFinished(status, method, incidence);
 }
 
 ITIPHandlerHelper::SendResult
@@ -391,12 +478,13 @@ void ITIPHandlerHelper::onSchedulerFinished(Akonadi::Scheduler::Result result,
         m_status = StatusNone;
         if (!success) {
             const QString question(i18n("Sending group scheduling email failed."));
-            const int code = askUserIfNeeded(question, true, KGuiItem(i18n("Abort Update")));
-            if (code == KMessageBox::Yes) {
-                emit finished(ResultFailAbortUpdate, QString());
-            } else {
-                emit finished(ResultFailKeepUpdate, QString());
-            }
+
+            ITIPHandlerDialogDelegate *askDelegator = m_factory->createITIPHanderDialogDelegate(KCalCore::Incidence::Ptr(), KCalCore::iTIPNoMethod, mParent);
+
+            connect(askDelegator, SIGNAL(dialogClosed(int,KCalCore::iTIPMethod,KCalCore::Incidence::Ptr)),
+                    SLOT(slotSchedulerFinishDialog(int,KCalCore::iTIPMethod,KCalCore::Incidence::Ptr)));
+            askDelegator->openDialogSchedulerFinished(question,  ITIPHandlerDialogDelegate::ActionAsk,
+                                                      KGuiItem(i18nc("@action:button dialog positive answer to abort question","Abort Update")));
             return;
         } else {
             //fall through
@@ -407,3 +495,14 @@ void ITIPHandlerHelper::onSchedulerFinished(Akonadi::Scheduler::Result result,
                   success ? QString() : i18n("Error: %1", errorMsg));
 }
 
+void ITIPHandlerHelper::slotSchedulerFinishDialog(const int result, KCalCore::iTIPMethod method, const KCalCore::Incidence::Ptr &incidence)
+{
+    Q_UNUSED(method);
+    Q_UNUSED(incidence);
+    if (result == KMessageBox::Yes) {
+        emit finished(ResultFailAbortUpdate, QString());
+    } else {
+        emit finished(ResultFailKeepUpdate, QString());
+    }
+}
+
diff --git a/akonadi/calendar/itiphandlerhelper_p.h b/akonadi/calendar/itiphandlerhelper_p.h
index 6b8ee6d..152f46c 100644
--- a/akonadi/calendar/itiphandlerhelper_p.h
+++ b/akonadi/calendar/itiphandlerhelper_p.h
@@ -78,14 +78,10 @@ public:
         ResultNoSendingNeeded, /**< In some cases it is not needed to send an invitation
                                 (e.g. when we are the only attendee) */
         ResultError,           /**< An unexpected error occurred */
-        ResultSuccess          /**< The invitation was sent to all attendees. */
+        ResultSuccess,         /**< The invitation was sent to all attendees. */
+        ResultPending          /**< The user has been asked about one detail, waiting for him to anwser it */
     };
 
-    enum Action {
-        ActionAsk,
-        ActionSendMessage,
-        ActionDontSendMessage
-    };
 
     /**
       When an Incidence is created/modified/deleted the user can choose to send
@@ -96,7 +92,7 @@ public:
       sendIncidence*Message() methods.
       @param action the action to set as default
      */
-    void setDefaultAction(Action action);
+    void setDefaultAction(ITIPHandlerDialogDelegate::Action action);
 
     /**
       Before an invitation is sent the user is asked for confirmation by means of
@@ -111,7 +107,7 @@ public:
       Kontact/PIM) are the organizer.
       @param incidence The new incidence.
      */
-    ITIPHandlerHelper::SendResult sendIncidenceCreatedMessage(KCalCore::iTIPMethod method,
+    void sendIncidenceCreatedMessage(KCalCore::iTIPMethod method,
             const KCalCore::Incidence::Ptr &incidence);
 
     /**
@@ -131,7 +127,7 @@ public:
       @param incidence The modified incidence.
       @param attendeeStatusChanged if @c true and @p method is #iTIPRequest ask the user whether to send a status update as well
      */
-    ITIPHandlerHelper::SendResult sendIncidenceModifiedMessage(KCalCore::iTIPMethod method,
+    void sendIncidenceModifiedMessage(KCalCore::iTIPMethod method,
             const KCalCore::Incidence::Ptr &incidence,
             bool attendeeStatusChanged);
 
@@ -139,7 +135,7 @@ public:
       Handles sending of ivitations for deleted incidences.
       @param incidence The deleted incidence.
      */
-    ITIPHandlerHelper::SendResult sendIncidenceDeletedMessage(KCalCore::iTIPMethod method,
+    void sendIncidenceDeletedMessage(KCalCore::iTIPMethod method,
             const KCalCore::Incidence::Ptr &incidence);
 
     /**
@@ -157,19 +153,24 @@ Q_SIGNALS:
     void finished(Akonadi::ITIPHandlerHelper::SendResult result,
                   const QString &errorMessage);
 
+    void sendIncidenceDeletedMessageFinished(ITIPHandlerHelper::SendResult, KCalCore::iTIPMethod method, const KCalCore::Incidence::Ptr &incidence);
+    void sendIncidenceModifiedMessageFinished(ITIPHandlerHelper::SendResult, KCalCore::iTIPMethod method, const KCalCore::Incidence::Ptr &incidence);
+    void sendIncidenceCreatedMessageFinished(ITIPHandlerHelper::SendResult, KCalCore::iTIPMethod method, const KCalCore::Incidence::Ptr &incidence);
+
 private Q_SLOTS:
     void onSchedulerFinished(Akonadi::Scheduler::Result result, const QString &errorMsg);
 
+    void slotIncidenceDeletedDialogClosed(const int, KCalCore::iTIPMethod method, const KCalCore::Incidence::Ptr &incidence);
+    void slotIncidenceModifiedDialogClosed(const int, KCalCore::iTIPMethod method, const KCalCore::Incidence::Ptr &incidence);
+    void slotIncidenceCreatedDialogClosed(const int, KCalCore::iTIPMethod method, const KCalCore::Incidence::Ptr &incidence);
+
+    void slotSchedulerFinishDialog(const int result, KCalCore::iTIPMethod method, const KCalCore::Incidence::Ptr &incidence);
+
 private:
     ITIPHandlerHelper::SendResult sentInvitation(int messageBoxReturnCode,
             const KCalCore::Incidence::Ptr &incidence,
             KCalCore::iTIPMethod method);
 
-    int askUserIfNeeded(const QString &question,
-                        bool ignoreDefaultAction = true,
-                        const KGuiItem &buttonYes = KGuiItem(i18n("Send Email")),
-                        const KGuiItem &buttonNo = KGuiItem(i18n("Do Not Send"))) const;
-
     /**
       We are the organizer. If there is more than one attendee, or if there is
       only one, and it's not the same as the organizer, ask the user to send
@@ -184,8 +185,9 @@ private:
      */
     bool weNeedToSendMailFor(const KCalCore::Incidence::Ptr &incidence);
 
-    ITIPHandlerHelper::Action mDefaultAction;
+    ITIPHandlerDialogDelegate::Action mDefaultAction;
     QWidget *mParent;
+    ITIPHandlerComponentFactory *m_factory;
     MailScheduler *m_scheduler;
     Status m_status;
 };


commit eafe617ed73a296e55a94e9b84de7975ea98f48a
Author: Sandro Knauß <knauss at kolabsys.com>
Date:   Tue Jul 1 16:36:02 2014 +0200

    Make MailClient interceptable
    
    REVIEW: 119432

diff --git a/akonadi/calendar/freebusymanager.cpp b/akonadi/calendar/freebusymanager.cpp
index bf64dab..28359ac 100644
--- a/akonadi/calendar/freebusymanager.cpp
+++ b/akonadi/calendar/freebusymanager.cpp
@@ -913,7 +913,7 @@ void FreeBusyManager::mailFreeBusy(int daysToPublish, QWidget *parentWidget)
     QPointer<PublishDialog> publishdlg = new PublishDialog();
     if (publishdlg->exec() == QDialog::Accepted) {
         // Send the mail
-        MailScheduler *scheduler = new MailScheduler();
+        MailScheduler *scheduler = new MailScheduler(/*factory=*/0, this);
         connect(scheduler, SIGNAL(transactionFinished(Akonadi::Scheduler::Result,QString))
                 , d, SLOT(processMailSchedulerResult(Akonadi::Scheduler::Result,QString)));
         d->mParentWidgetForMailling = parentWidget;
diff --git a/akonadi/calendar/incidencechanger.cpp b/akonadi/calendar/incidencechanger.cpp
index aafa8d5..79d0f49 100644
--- a/akonadi/calendar/incidencechanger.cpp
+++ b/akonadi/calendar/incidencechanger.cpp
@@ -132,10 +132,11 @@ ConflictPreventer* ConflictPreventer::self()
     return &sConflictPreventerPrivate->instance;
 }
 
-IncidenceChanger::Private::Private(bool enableHistory, IncidenceChanger *qq) : q(qq)
+IncidenceChanger::Private::Private(bool enableHistory, ITIPHandlerComponentFactory *factory, IncidenceChanger *qq) : q(qq)
 {
     mLatestChangeId = 0;
     mShowDialogsOnError = true;
+    mFactory = factory ? factory : new ITIPHandlerComponentFactory(this);
     mHistory = enableHistory ? new History(this) : 0;
     mUseHistory = enableHistory;
     mDestinationPolicy = DestinationPolicyDefault;
@@ -423,7 +424,7 @@ bool IncidenceChanger::Private::handleInvitationsBeforeChange(const Change::Ptr
 {
     bool result = true;
     if (mGroupwareCommunication) {
-        ITIPHandlerHelper handler(change->parentWidget);    // TODO make async
+        ITIPHandlerHelper handler(mFactory, change->parentWidget); // TODO make async
 
         if (m_invitationPolicy == InvitationPolicySend) {
             handler.setDefaultAction(ITIPHandlerHelper::ActionSendMessage);
@@ -510,7 +511,7 @@ bool IncidenceChanger::Private::handleInvitationsBeforeChange(const Change::Ptr
 bool IncidenceChanger::Private::handleInvitationsAfterChange(const Change::Ptr &change)
 {
     if (change->useGroupwareCommunication) {
-        ITIPHandlerHelper handler(change->parentWidget);   // TODO make async
+        ITIPHandlerHelper handler(mFactory, change->parentWidget);   // TODO make async
 
         const bool alwaysSend = m_invitationPolicy == InvitationPolicySend;
         const bool neverSend = m_invitationPolicy == InvitationPolicyDontSend;
@@ -571,7 +572,7 @@ bool IncidenceChanger::Private::handleInvitationsAfterChange(const Change::Ptr &
                     }
 
                     if (notifyOrganizer) {
-                        MailScheduler scheduler; // TODO make async
+                        MailScheduler scheduler(mFactory, change->parentWidget); // TODO make async
                         scheduler.performTransaction(incidence, KCalCore::iTIPReply);
                     }
                 }
@@ -633,12 +634,18 @@ bool IncidenceChanger::Private::myAttendeeStatusChanged(const Incidence::Ptr &ne
 }
 
 IncidenceChanger::IncidenceChanger(QObject *parent) : QObject(parent)
-    , d(new Private(/**history=*/true, this))
+    , d(new Private(/**history=*/true, /*factory=*/0, this))
+{
+}
+
+
+IncidenceChanger::IncidenceChanger(ITIPHandlerComponentFactory *factory, QObject *parent) : QObject(parent)
+    , d(new Private(/**history=*/true, factory, this))
 {
 }
 
 IncidenceChanger::IncidenceChanger(bool enableHistory, QObject *parent) : QObject(parent)
-    , d(new Private(enableHistory, this))
+    , d(new Private(enableHistory, /*factory=*/0, this))
 {
 }
 
@@ -1030,7 +1037,7 @@ void IncidenceChanger::setHistoryEnabled(bool enable)
     if (d->mUseHistory != enable) {
         d->mUseHistory = enable;
         if (enable && !d->mHistory)
-            d->mHistory = new History(this);
+            d->mHistory = new History(d);
     }
 }
 
diff --git a/akonadi/calendar/incidencechanger.h b/akonadi/calendar/incidencechanger.h
index 858e487..da1a8e7 100644
--- a/akonadi/calendar/incidencechanger.h
+++ b/akonadi/calendar/incidencechanger.h
@@ -22,6 +22,8 @@
 
 #include "akonadi-calendar_export.h"
 
+#include "itiphandler.h"
+
 #include <akonadi/item.h>
 #include <akonadi/collection.h>
 #include <kcalcore/incidence.h>
@@ -134,11 +136,21 @@ public:
 
     /**
       * Creates a new IncidenceChanger instance.
+      * creates a default ITIPHandlerComponentFactory object.
       * @param parent parent QObject
       */
     explicit IncidenceChanger(QObject *parent = 0);
 
     /**
+     * Creates a new IncidenceChanger instance.
+     * @param factory factory for creating dialogs and the mail transport job. To create a default
+     * factory set factory == 0
+     * @param parent parent QObject
+     * @since 4.15
+     */
+    explicit IncidenceChanger(ITIPHandlerComponentFactory *factory, QObject *parent);
+
+    /**
       * Destroys this IncidenceChanger instance.
       */
     ~IncidenceChanger();
diff --git a/akonadi/calendar/incidencechanger_p.h b/akonadi/calendar/incidencechanger_p.h
index 82bdfa1..eb08eb1 100644
--- a/akonadi/calendar/incidencechanger_p.h
+++ b/akonadi/calendar/incidencechanger_p.h
@@ -250,7 +250,7 @@ class IncidenceChanger::Private : public QObject
 {
     Q_OBJECT
 public:
-    explicit Private(bool enableHistory, IncidenceChanger *mIncidenceChanger);
+    explicit Private(bool enableHistory, ITIPHandlerComponentFactory *factory, IncidenceChanger *mIncidenceChanger);
     ~Private();
 
     void loadCollections();  // async-loading of list of writable collections
@@ -350,6 +350,7 @@ public:
 
     IncidenceChanger::InvitationPolicy m_invitationPolicy;
 
+    ITIPHandlerComponentFactory *mFactory;
 private:
     IncidenceChanger *q;
 };
diff --git a/akonadi/calendar/itiphandler.cpp b/akonadi/calendar/itiphandler.cpp
index 2b72199..a4bc9f1 100644
--- a/akonadi/calendar/itiphandler.cpp
+++ b/akonadi/calendar/itiphandler.cpp
@@ -38,6 +38,7 @@
 #include <kcalutils/stringify.h>
 
 #include <kpimidentities/identitymanager.h>
+#include <mailtransport/messagequeuejob.h>
 #include <mailtransport/transportmanager.h>
 
 #include <KMessageBox>
@@ -59,8 +60,31 @@ GroupwareUiDelegate::~GroupwareUiDelegate()
 {
 }
 
+ITIPHandlerComponentFactory::ITIPHandlerComponentFactory(QObject *parent)
+  : QObject(parent)
+{
+}
+
+ITIPHandlerComponentFactory::~ITIPHandlerComponentFactory()
+{
+}
+
+MailTransport::MessageQueueJob *ITIPHandlerComponentFactory::createMessageQueueJob(const KCalCore::IncidenceBase::Ptr &incidence, const KPIMIdentities::Identity &identity, QObject *parent)
+{
+    Q_UNUSED(incidence);
+    Q_UNUSED(identity);
+    return new MailTransport::MessageQueueJob(parent);
+}
+
 ITIPHandler::ITIPHandler(QObject *parent) : QObject(parent)
-    , d(new Private(this))
+    , d(new Private(/*factory=*/0, this))
+{
+    qRegisterMetaType<Akonadi::ITIPHandler::Result>("Akonadi::ITIPHandler::Result");
+}
+
+
+ITIPHandler::ITIPHandler(ITIPHandlerComponentFactory *factory, QObject *parent) : QObject(parent)
+    , d(new Private(factory, this))
 {
     qRegisterMetaType<Akonadi::ITIPHandler::Result>("Akonadi::ITIPHandler::Result");
 }
@@ -347,7 +371,7 @@ void ITIPHandler::sendAsICalendar(const KCalCore::Incidence::Ptr &originalIncide
         const QString from = Akonadi::CalendarUtils::email();
         const bool bccMe = Akonadi::CalendarSettings::self()->bcc();
         const QString messageText = format.createScheduleMessage(incidence, KCalCore::iTIPRequest);
-        MailClient *mailer = new MailClient();
+        MailClient *mailer = new MailClient(d->m_factory);
         d->m_queuedInvitation.incidence = incidence;
         connect(mailer, SIGNAL(finished(Akonadi::MailClient::Result,QString)),
                 d, SLOT(finishSendAsICalendar(Akonadi::MailScheduler::Result,QString)));
diff --git a/akonadi/calendar/itiphandler.h b/akonadi/calendar/itiphandler.h
index 5c581e7..dcbc6b6 100644
--- a/akonadi/calendar/itiphandler.h
+++ b/akonadi/calendar/itiphandler.h
@@ -35,6 +35,14 @@
 #include <QString>
 #include <QWidget>
 
+namespace MailTransport {
+    class MessageQueueJob;
+}
+
+namespace KPIMIdentities {
+    class Identity;
+}
+
 namespace Akonadi {
 
 /**
@@ -52,6 +60,33 @@ public:
 };
 
 /**
+ * @short Factory to create MailTransport::MessageQueueJob jobs.
+ * @since 4.15
+ */
+class AKONADI_CALENDAR_EXPORT ITIPHandlerComponentFactory : public QObject
+{
+    Q_OBJECT
+public:
+    /*
+     * Created a new ITIPHandlerComponentFactory object.
+     */
+    explicit ITIPHandlerComponentFactory(QObject *parent = 0);
+
+    /*
+     * deletes the object.
+     */
+    virtual ~ITIPHandlerComponentFactory();
+
+    /*
+     * @return A new MailTransport::MessageQueueJob object
+     * @param incidence related to the mail
+     * @param identity that is the mail sender
+     * @param parent of the MailTransport::MessageQueueJob object
+     */
+    virtual MailTransport::MessageQueueJob* createMessageQueueJob(const KCalCore::IncidenceBase::Ptr &incidence, const KPIMIdentities::Identity &identity, QObject *parent = 0);
+};
+
+/**
  * @short Handles sending of iTip messages aswell as processing incoming ones.
  * @since 4.11
  */
@@ -67,10 +102,17 @@ public:
 
     /**
      * Creates a new ITIPHandler instance.
+     * creates a default ITIPHandlerComponentFactory object.
      */
     explicit ITIPHandler(QObject *parent = 0);
 
     /**
+     * Create a new ITIPHandler instance.
+     * @param factory is set to 0 a new factory is created.
+     * @since 4.15
+     */
+    explicit ITIPHandler(ITIPHandlerComponentFactory *factory, QObject *parent);
+    /**
      * Destroys this instance.
      */
     ~ITIPHandler();
diff --git a/akonadi/calendar/itiphandler_p.cpp b/akonadi/calendar/itiphandler_p.cpp
index e62b72b..db1de2f 100644
--- a/akonadi/calendar/itiphandler_p.cpp
+++ b/akonadi/calendar/itiphandler_p.cpp
@@ -25,15 +25,17 @@
 
 using namespace Akonadi;
 
-ITIPHandler::Private::Private(ITIPHandler *qq) : m_calendarLoadError(false)
-    , m_scheduler(new MailScheduler(qq))
+ITIPHandler::Private::Private(ITIPHandlerComponentFactory *factory, ITIPHandler *qq) : m_calendarLoadError(false)
+    , m_factory(factory ? factory : new ITIPHandlerComponentFactory(this))
+    , m_scheduler(new MailScheduler(m_factory, qq))
     , m_method(KCalCore::iTIPNoMethod)
-    , m_helper(new ITIPHandlerHelper())   //TODO parent
+    , m_helper(new ITIPHandlerHelper(m_factory))
     , m_currentOperation(OperationNone)
     , m_uiDelegate(0)
     , m_showDialogsOnError(true)
     , q(qq)
 {
+    m_helper->setParent(this);
     connect(m_scheduler, SIGNAL(transactionFinished(Akonadi::Scheduler::Result,QString)),
             SLOT(onSchedulerFinished(Akonadi::Scheduler::Result,QString)));
 
diff --git a/akonadi/calendar/itiphandler_p.h b/akonadi/calendar/itiphandler_p.h
index 35e895a..63040b2 100644
--- a/akonadi/calendar/itiphandler_p.h
+++ b/akonadi/calendar/itiphandler_p.h
@@ -58,7 +58,7 @@ class ITIPHandler::Private : public QObject
 {
     Q_OBJECT
 public:
-    Private(ITIPHandler *q);
+    Private(ITIPHandlerComponentFactory *factory, ITIPHandler *q);
 
     void finishProcessiTIPMessage(Akonadi::MailScheduler::Result, const QString &errorMessage);
     void finishSendiTIPMessage(Akonadi::MailScheduler::Result, const QString &errorMessage);
@@ -74,6 +74,7 @@ public:
     Invitation m_queuedInvitation;
     bool m_calendarLoadError;
     CalendarBase::Ptr m_calendar;
+    ITIPHandlerComponentFactory *m_factory;
     MailScheduler *m_scheduler;
     KCalCore::Incidence::Ptr m_incidence;
     KCalCore::iTIPMethod m_method; // METHOD field of ical rfc of incoming invitation
diff --git a/akonadi/calendar/itiphandlerhelper_p.cpp b/akonadi/calendar/itiphandlerhelper_p.cpp
index 6eaf5f5..d72e89a 100644
--- a/akonadi/calendar/itiphandlerhelper_p.cpp
+++ b/akonadi/calendar/itiphandlerhelper_p.cpp
@@ -148,10 +148,10 @@ bool ITIPHandlerHelper::weNeedToSendMailFor(const KCalCore::Incidence::Ptr &inci
         incidence->attendees().first()->email() != incidence->organizer()->email();
 }
 
-ITIPHandlerHelper::ITIPHandlerHelper(QWidget *parent)
+ITIPHandlerHelper::ITIPHandlerHelper(ITIPHandlerComponentFactory *factory, QWidget *parent)
     : mDefaultAction(ITIPHandlerHelper::ActionAsk)
     , mParent(parent)
-    , m_scheduler(new MailScheduler(parent))
+    , m_scheduler(new MailScheduler(factory, parent))
     , m_status(StatusNone)
 {
     connect(m_scheduler, SIGNAL(transactionFinished(Akonadi::Scheduler::Result,QString)),
diff --git a/akonadi/calendar/itiphandlerhelper_p.h b/akonadi/calendar/itiphandlerhelper_p.h
index 11bc6de..6b8ee6d 100644
--- a/akonadi/calendar/itiphandlerhelper_p.h
+++ b/akonadi/calendar/itiphandlerhelper_p.h
@@ -67,7 +67,7 @@ class ITIPHandlerHelper : public QObject
 {
     Q_OBJECT
 public:
-    explicit ITIPHandlerHelper(QWidget *parent = 0);   // TODO
+    explicit ITIPHandlerHelper(ITIPHandlerComponentFactory *factory, QWidget *parent = 0);
     ~ITIPHandlerHelper();
 
     enum SendResult {
diff --git a/akonadi/calendar/mailclient_p.cpp b/akonadi/calendar/mailclient_p.cpp
index 94accce..fc8466f 100644
--- a/akonadi/calendar/mailclient_p.cpp
+++ b/akonadi/calendar/mailclient_p.cpp
@@ -45,10 +45,9 @@
 
 using namespace Akonadi;
 
-bool Akonadi::MailClient::sRunningUnitTests = false;
-UnitTestResult::List MailClient::sUnitTestResults;
 
-MailClient::MailClient(QObject *parent) : QObject(parent)
+MailClient::MailClient(ITIPHandlerComponentFactory *factory, QObject *parent) : QObject(parent)
+    , mFactory(factory ? factory : new ITIPHandlerComponentFactory(this))
 {
 }
 
@@ -132,7 +131,7 @@ void MailClient::mailAttendees(const KCalCore::IncidenceBase::Ptr &incidence,
     const QString body = KCalUtils::IncidenceFormatter::mailBodyStr(incidence,
                          KSystemTimeZones::local());
 
-    send(identity, from, to, cc, subject, body, false, bccMe, attachment, mailTransport);
+    send(incidence, identity, from, to, cc, subject, body, false, bccMe, attachment, mailTransport);
 }
 
 void MailClient::mailOrganizer(const KCalCore::IncidenceBase::Ptr &incidence,
@@ -156,7 +155,7 @@ void MailClient::mailOrganizer(const KCalCore::IncidenceBase::Ptr &incidence,
     const QString body = KCalUtils::IncidenceFormatter::mailBodyStr(incidence,
                          KSystemTimeZones::local());
 
-    send(identity, from, to, QString(), subject, body, false, bccMe, attachment, mailTransport);
+    send(incidence, identity, from, to, QString(), subject, body, false, bccMe, attachment, mailTransport);
 }
 
 void MailClient::mailTo(const KCalCore::IncidenceBase::Ptr &incidence,
@@ -177,7 +176,7 @@ void MailClient::mailTo(const KCalCore::IncidenceBase::Ptr &incidence,
     const QString body = KCalUtils::IncidenceFormatter::mailBodyStr(incidence,
                          KSystemTimeZones::local());
 
-    send(identity, from, recipients, QString(), subject, body, false,
+    send(incidence, identity, from, recipients, QString(), subject, body, false,
          bccMe, attachment, mailTransport);
 }
 
@@ -192,13 +191,13 @@ static QStringList extractEmailAndNormalize(const QString &email)
     return normalizedEmail;
 }
 
-void MailClient::send(const KPIMIdentities::Identity &identity,
+void MailClient::send(const KCalCore::IncidenceBase::Ptr &incidence,
+                      const KPIMIdentities::Identity &identity,
                       const QString &from, const QString &_to,
                       const QString &cc, const QString &subject,
                       const QString &body, bool hidden, bool bccMe,
                       const QString &attachment, const QString &mailTransport)
 {
-    Q_UNUSED(identity);
     Q_UNUSED(hidden);
 
     UnitTestResult unitTestResult;
@@ -223,9 +222,6 @@ void MailClient::send(const KPIMIdentities::Identity &identity,
              << "\nAttachment:\n" << attachment
              << "\nmailTransport: " << mailTransport;
 
-    QTime timer;
-    timer.start();
-
     MailTransport::Transport *transport =
         MailTransport::TransportManager::self()->transportByName(mailTransport);
 
@@ -335,7 +331,7 @@ void MailClient::send(const KPIMIdentities::Identity &identity,
     message->assemble();
 
     // Put the newly created item in the MessageQueueJob.
-    MailTransport::MessageQueueJob *qjob = new MailTransport::MessageQueueJob(this);
+    MailTransport::MessageQueueJob *qjob = mFactory->createMessageQueueJob(incidence, identity, this);
     qjob->transportAttribute().setTransportId(transportId);
     qjob->sentBehaviourAttribute().setSentBehaviour(
         MailTransport::SentBehaviourAttribute::MoveToDefaultSentCollection);
@@ -367,25 +363,9 @@ void MailClient::send(const KPIMIdentities::Identity &identity,
         qjob->addressAttribute().setBcc(bccStringList);
     }
 
-    if (sRunningUnitTests) {
-        unitTestResult.message     = message;
-        unitTestResult.from        = finalFrom;
-        unitTestResult.to          = toStringList;
-        unitTestResult.cc          = ccStringList;
-        unitTestResult.bcc         = bccStringList;
-        unitTestResult.transportId = transportId;
-        sUnitTestResults << unitTestResult;
-        qjob->deleteLater();
-
-        QMetaObject::invokeMethod(this, "finished", Qt::QueuedConnection,
-                                  Q_ARG(Akonadi::MailClient::Result, ResultSuccess),
-                                  Q_ARG(QString, QString()));
-
-    } else {
-        qjob->setMessage(message);
-        connect(qjob, SIGNAL(finished(KJob*)), SLOT(handleQueueJobFinished(KJob*)));
-        qjob->start();
-    }
+    qjob->setMessage(message);
+    connect(qjob, SIGNAL(finished(KJob*)), SLOT(handleQueueJobFinished(KJob*)));
+    qjob->start();
 }
 
 void MailClient::handleQueueJobFinished(KJob *job)
@@ -395,7 +375,6 @@ void MailClient::handleQueueJobFinished(KJob *job)
         emit finished(ResultQueueJobError, i18n("Error queuing message in outbox: %1",
                                                 job->errorText()));
     } else {
-        kDebug() << "Mail queued";
         emit finished(ResultSuccess, QString());
     }
 }
diff --git a/akonadi/calendar/mailclient_p.h b/akonadi/calendar/mailclient_p.h
index c284806..702a0e0 100644
--- a/akonadi/calendar/mailclient_p.h
+++ b/akonadi/calendar/mailclient_p.h
@@ -23,6 +23,7 @@
 #define AKONADI_MAILCLIENT_P_H
 
 #include "akonadi-calendar_export.h"
+#include "itiphandler.h"
 #include <kcalcore/incidencebase.h>
 #include <kmime/kmime_message.h>
 #include <QObject>
@@ -45,19 +46,19 @@ class Identity;
 class KJob;
 
 namespace Akonadi {
-
 #ifdef PLEASE_TEST_INVITATIONS
 #define EXPORT_MAILCLIENT AKONADI_CALENDAR_EXPORT
 #else
 #define EXPORT_MAILCLIENT
 #endif
 
+class ITIPHandlerComponentFactory;
+
 class EXPORT_MAILCLIENT MailClient : public QObject
 
 {
     Q_OBJECT
 public:
-
     enum Result {
         ResultSuccess,
         ResultNoAttendees,
@@ -67,29 +68,30 @@ public:
         ResultQueueJobError
     };
 
-    explicit MailClient(QObject *parent = 0);
+    explicit MailClient(ITIPHandlerComponentFactory *factory, QObject *parent = 0);
     ~MailClient();
 
-    void mailAttendees(const KCalCore::IncidenceBase::Ptr &,
+    void mailAttendees(const KCalCore::IncidenceBase::Ptr &incidence,
                        const KPIMIdentities::Identity &identity,
                        bool bccMe, const QString &attachment=QString(),
                        const QString &mailTransport = QString());
 
-    void mailOrganizer(const KCalCore::IncidenceBase::Ptr &,
+    void mailOrganizer(const KCalCore::IncidenceBase::Ptr &incidence,
                        const KPIMIdentities::Identity &identity,
                        const QString &from, bool bccMe,
                        const QString &attachment=QString(),
                        const QString &sub=QString(),
                        const QString &mailTransport = QString());
 
-    void mailTo(const KCalCore::IncidenceBase::Ptr &, const KPIMIdentities::Identity &identity,
+    void mailTo(const KCalCore::IncidenceBase::Ptr &incidence, const KPIMIdentities::Identity &identity,
                 const QString &from, bool bccMe, const QString &recipients,
-                const QString &attachment=QString(), const QString &mailTransport = QString());
+                const QString &attachment = QString(), const QString &mailTransport = QString());
 
     /**
       Sends mail with specified from, to and subject field and body as text.
       If bcc is set, send a blind carbon copy to the sender
 
+      @param incidence is the incidence, that is sended
       @param identity is the Identity of the sender
       @param from is the address of the sender of the message
       @param to a list of addresses to receive the message
@@ -103,7 +105,7 @@ public:
       @param mailTransport defines the mail transport method. See here the
       kdepimlibs/mailtransport library.
     */
-    void send(const KPIMIdentities::Identity &identity, const QString &from, const QString &to,
+    void send(const KCalCore::IncidenceBase::Ptr &incidence, const KPIMIdentities::Identity &identity, const QString &from, const QString &to,
               const QString &cc, const QString &subject, const QString &body,
               bool hidden=false, bool bccMe=false, const QString &attachment=QString(),
               const QString &mailTransport = QString());
@@ -116,12 +118,11 @@ Q_SIGNALS:
 
 public:
     // For unit-test usage, since we can't depend on kdepim-runtime on jenkins
-    static UnitTestResult::List sUnitTestResults;
-    static bool sRunningUnitTests;
+    ITIPHandlerComponentFactory *mFactory;
 };
 
 }
 
 Q_DECLARE_METATYPE(Akonadi::MailClient::Result)
 
-#endif
+#endif
\ No newline at end of file
diff --git a/akonadi/calendar/mailscheduler_p.cpp b/akonadi/calendar/mailscheduler_p.cpp
index cfeebd0..907a11a 100644
--- a/akonadi/calendar/mailscheduler_p.cpp
+++ b/akonadi/calendar/mailscheduler_p.cpp
@@ -44,12 +44,12 @@ public:
     MailClient *m_mailer;
 };
 
-MailScheduler::MailScheduler(QObject *parent) : Scheduler(parent)
+MailScheduler::MailScheduler(ITIPHandlerComponentFactory *factory, QObject *parent) : Scheduler(parent)
     , d(new Private())
-
 {
     d->m_identityManager = new IdentityManager(/*ro=*/true, this);
-    d->m_mailer = new MailClient();
+    d->m_mailer = new MailClient(factory, parent);
+
     connect(d->m_mailer, SIGNAL(finished(Akonadi::MailClient::Result,QString)),
             SLOT(onMailerFinished(Akonadi::MailClient::Result,QString)));
 }
diff --git a/akonadi/calendar/mailscheduler_p.h b/akonadi/calendar/mailscheduler_p.h
index cdbdd87..58c8c4f 100644
--- a/akonadi/calendar/mailscheduler_p.h
+++ b/akonadi/calendar/mailscheduler_p.h
@@ -47,7 +47,7 @@ public:
     /**
      * @param calendar Must be a valid and loaded calendar.
      */
-    explicit MailScheduler(QObject *parent = 0);
+    explicit MailScheduler(ITIPHandlerComponentFactory *factory, QObject *parent = 0);
     ~MailScheduler();
 
     /** reimp */
@@ -63,6 +63,7 @@ public:
                             KCalCore::iTIPMethod method,
                             const QString &recipients);
 
+
     /** Returns the directory where the free-busy information is stored */
     /** reimp*/ QString freeBusyDir() const;
 
diff --git a/akonadi/calendar/tests/itiphandlertest.cpp b/akonadi/calendar/tests/itiphandlertest.cpp
index f1167e1..92c9e37 100644
--- a/akonadi/calendar/tests/itiphandlertest.cpp
+++ b/akonadi/calendar/tests/itiphandlertest.cpp
@@ -30,6 +30,7 @@
 #include <akonadi/collectionfetchscope.h>
 #include <akonadi/itemfetchscope.h>
 #include <akonadi/qtest_akonadi.h>
+#include <mailtransport/messagequeuejob.h>
 
 #include <kcalcore/event.h>
 
@@ -47,15 +48,61 @@ Q_DECLARE_METATYPE(QList<int>)
 static const char *s_ourEmail = "unittests at dev.nul"; // change also in kdepimlibs/akonadi/calendar/tests/unittestenv/kdehome/share/config
 static const char *s_outEmail2 = "identity2 at kde.org";
 
+class FakeMessageQueueJob : public MailTransport::MessageQueueJob
+{
+public:
+    explicit FakeMessageQueueJob(QObject *parent = 0) : MailTransport::MessageQueueJob(parent)
+    {
+    }
+
+    virtual void start()
+    {
+        UnitTestResult unitTestResult;
+        unitTestResult.message     = message();
+        unitTestResult.from        = addressAttribute().from();
+        unitTestResult.to          = addressAttribute().to();
+        unitTestResult.cc          = addressAttribute().cc();
+        unitTestResult.bcc         = addressAttribute().bcc();
+        unitTestResult.transportId = transportAttribute().transportId();
+        FakeMessageQueueJob::sUnitTestResults << unitTestResult;
+
+        setError(Akonadi::MailClient::ResultSuccess);
+        setErrorText(QString());
+
+        emitResult();
+    }
+
+    static UnitTestResult::List sUnitTestResults;
+};
+
+
+UnitTestResult::List FakeMessageQueueJob::sUnitTestResults;
+
+class FakeITIPHandlerComponentFactory : public ITIPHandlerComponentFactory
+{
+public:
+    FakeITIPHandlerComponentFactory(QObject *parent = 0) : ITIPHandlerComponentFactory(parent)
+    {
+    }
+
+    virtual MailTransport::MessageQueueJob *createMessageQueueJob(const KCalCore::IncidenceBase::Ptr &incidence, const KPIMIdentities::Identity &identity, QObject *parent = 0)
+    {
+        Q_UNUSED(incidence);
+        Q_UNUSED(identity);
+        return new FakeMessageQueueJob(parent);
+    }
+};
+
+
+
 void ITIPHandlerTest::initTestCase()
 {
     AkonadiTest::checkTestIsIsolated();
     m_pendingItipMessageSignal = 0;
     m_pendingIncidenceChangerSignal = 0;
-    MailClient::sRunningUnitTests = true;
     m_itipHandler = 0;
     m_cancelExpected = false;
-    m_changer = new IncidenceChanger(this);
+    m_changer = new IncidenceChanger(new FakeITIPHandlerComponentFactory(this), this);
     m_changer->setHistoryEnabled(false);
     m_changer->setGroupwareCommunication(true);
     m_changer->setInvitationPolicy(IncidenceChanger::InvitationPolicySend); // don't show dialogs
@@ -190,7 +237,7 @@ void ITIPHandlerTest::testProcessITIPMessage()
     QFETCH(int, expectedNumIncidences);
     QFETCH(KCalCore::Attendee::PartStat, expectedPartStat);
 
-    MailClient::sUnitTestResults.clear();
+    FakeMessageQueueJob::sUnitTestResults.clear();
     createITIPHandler();
 
     m_expectedResult = expectedResult;
@@ -262,7 +309,7 @@ void ITIPHandlerTest::testProcessITIPMessages()
 
     const QString receiver = QLatin1String(s_ourEmail);
 
-    MailClient::sUnitTestResults.clear();
+    FakeMessageQueueJob::sUnitTestResults.clear();
     createITIPHandler();
 
     m_expectedResult = Akonadi::ITIPHandler::ResultSuccess;
@@ -319,7 +366,7 @@ void ITIPHandlerTest::testProcessITIPMessageCancel()
     QFETCH(QString, incidenceUid);
 
     const QString receiver = QLatin1String(s_ourEmail);
-    MailClient::sUnitTestResults.clear();
+    FakeMessageQueueJob::sUnitTestResults.clear();
     createITIPHandler();
 
     m_expectedResult = Akonadi::ITIPHandler::ResultSuccess;
@@ -476,7 +523,7 @@ void ITIPHandlerTest::testOutgoingInvitations()
     KCalCore::Incidence::Ptr incidence = item.payload<KCalCore::Incidence::Ptr>();
 
     m_pendingIncidenceChangerSignal = 1;
-    MailClient::sUnitTestResults.clear();
+    FakeMessageQueueJob::sUnitTestResults.clear();
     m_changer->setInvitationPolicy(invitationPolicy);
 
     m_cancelExpected = userCancels;
@@ -485,7 +532,7 @@ void ITIPHandlerTest::testOutgoingInvitations()
     case IncidenceChanger::ChangeTypeCreate:
         m_changer->createIncidence(incidence, mCollection);
         waitForIt();
-        QCOMPARE(MailClient::sUnitTestResults.count(), expectedEmailCount);
+        QCOMPARE(FakeMessageQueueJob::sUnitTestResults.count(), expectedEmailCount);
         break;
     case IncidenceChanger::ChangeTypeModify: {
         // Create if first, so we have something to modify
@@ -493,7 +540,7 @@ void ITIPHandlerTest::testOutgoingInvitations()
         m_changer->createIncidence(incidence, mCollection);
         waitForIt();
         m_changer->setGroupwareCommunication(true);
-        QCOMPARE(MailClient::sUnitTestResults.count(), 0);
+        QCOMPARE(FakeMessageQueueJob::sUnitTestResults.count(), 0);
         QVERIFY(mLastInsertedItem.isValid());
 
         m_pendingIncidenceChangerSignal = 1;
@@ -502,7 +549,7 @@ void ITIPHandlerTest::testOutgoingInvitations()
         int changeId = m_changer->modifyIncidence(mLastInsertedItem, oldIncidence);
         QVERIFY(changeId != 1);
         waitForIt();
-        QCOMPARE(MailClient::sUnitTestResults.count(), expectedEmailCount);
+        QCOMPARE(FakeMessageQueueJob::sUnitTestResults.count(), expectedEmailCount);
     }
     break;
     case IncidenceChanger::ChangeTypeDelete:
@@ -511,13 +558,13 @@ void ITIPHandlerTest::testOutgoingInvitations()
         m_changer->createIncidence(incidence, mCollection);
         waitForIt();
         m_changer->setGroupwareCommunication(true);
-        QCOMPARE(MailClient::sUnitTestResults.count(), 0);
+        QCOMPARE(FakeMessageQueueJob::sUnitTestResults.count(), 0);
 
         QVERIFY(mLastInsertedItem.isValid());
         m_pendingIncidenceChangerSignal = 1;
         m_changer->deleteIncidence(mLastInsertedItem);
         waitForIt();
-        QCOMPARE(MailClient::sUnitTestResults.count(), expectedEmailCount);
+        QCOMPARE(FakeMessageQueueJob::sUnitTestResults.count(), expectedEmailCount);
         break;
     default:
         Q_ASSERT(false);
@@ -571,7 +618,7 @@ void ITIPHandlerTest::cleanup()
 
 void ITIPHandlerTest::createITIPHandler()
 {
-    m_itipHandler = new Akonadi::ITIPHandler();
+    m_itipHandler = new Akonadi::ITIPHandler(new FakeITIPHandlerComponentFactory(this), this);
     m_itipHandler->setShowDialogsOnError(false);
     connect(m_itipHandler, SIGNAL(iTipMessageProcessed(Akonadi::ITIPHandler::Result,QString)),
             SLOT(oniTipMessageProcessed(Akonadi::ITIPHandler::Result,QString)));
@@ -594,7 +641,7 @@ void ITIPHandlerTest::processItip(const QString &icaldata, const QString &receiv
 
     // 0 e-mails are sent because the status update e-mail is sent by
     // kmail's text_calendar.cpp.
-    QCOMPARE(MailClient::sUnitTestResults.count(), 0);
+    QCOMPARE(FakeMessageQueueJob::sUnitTestResults.count(), 0);
 
     items = calendarItems();
 
@@ -676,7 +723,6 @@ void ITIPHandlerTest::onModifyFinished(int changeId, const Item &item,
     if (m_pendingIncidenceChangerSignal == 0) {
         stopWaiting();
     }
-    kDebug() << "Got result " << resultCode << m_cancelExpected;
     QCOMPARE(resultCode, m_cancelExpected ? IncidenceChanger::ResultCodeUserCanceled
              : IncidenceChanger::ResultCodeSuccess);
 }
diff --git a/akonadi/calendar/tests/mailclienttest.cpp b/akonadi/calendar/tests/mailclienttest.cpp
index 8235b31..4417bfa 100644
--- a/akonadi/calendar/tests/mailclienttest.cpp
+++ b/akonadi/calendar/tests/mailclienttest.cpp
@@ -38,6 +38,50 @@ using namespace Akonadi;
 Q_DECLARE_METATYPE(KPIMIdentities::Identity)
 Q_DECLARE_METATYPE(KCalCore::Incidence::Ptr)
 
+class FakeMessageQueueJob : public MailTransport::MessageQueueJob
+{
+public:
+    explicit FakeMessageQueueJob(QObject *parent = 0) : MailTransport::MessageQueueJob(parent)
+    {
+    }
+
+    virtual void start()
+    {
+        UnitTestResult unitTestResult;
+        unitTestResult.message     = message();
+        unitTestResult.from        = addressAttribute().from();
+        unitTestResult.to          = addressAttribute().to();
+        unitTestResult.cc          = addressAttribute().cc();
+        unitTestResult.bcc         = addressAttribute().bcc();
+        unitTestResult.transportId = transportAttribute().transportId();
+        FakeMessageQueueJob::sUnitTestResults << unitTestResult;
+
+        setError(Akonadi::MailClient::ResultSuccess);
+        setErrorText(QString());
+
+        emitResult();
+    }
+
+    static UnitTestResult::List sUnitTestResults;
+};
+
+UnitTestResult::List FakeMessageQueueJob::sUnitTestResults;
+
+class FakeITIPHandlerComponentFactory : public ITIPHandlerComponentFactory
+{
+public:
+    explicit FakeITIPHandlerComponentFactory(QObject *parent = 0) : ITIPHandlerComponentFactory(parent)
+    {
+    }
+
+    virtual MailTransport::MessageQueueJob *createMessageQueueJob(const KCalCore::IncidenceBase::Ptr &incidence, const KPIMIdentities::Identity &identity, QObject *parent = 0)
+    {
+        Q_UNUSED(incidence);
+        Q_UNUSED(identity);
+        return new FakeMessageQueueJob(parent);
+    }
+};
+
 class MailClientTest : public QObject
 {
     Q_OBJECT
@@ -55,8 +99,7 @@ private Q_SLOTS:
         AkonadiTest::checkTestIsIsolated();
 
         mPendingSignals = 0;
-        mMailClient = new MailClient(this);
-        mMailClient->sRunningUnitTests = true;
+        mMailClient = new MailClient(new FakeITIPHandlerComponentFactory(this), this);
         mLastResult = MailClient::ResultSuccess;
         connect(mMailClient, SIGNAL(finished(Akonadi::MailClient::Result,QString)),
                 SLOT(handleFinished(Akonadi::MailClient::Result,QString)));
@@ -193,7 +236,8 @@ private Q_SLOTS:
         QFETCH(QStringList, expectedToList);
         QFETCH(QStringList, expectedCcList);
         QFETCH(QStringList, expectedBccList);
-        mMailClient->sUnitTestResults.clear();
+
+        FakeMessageQueueJob::sUnitTestResults.clear();
 
         mPendingSignals = 1;
         mMailClient->mailAttendees(incidence, identity, bccMe, attachment, transport);
@@ -206,10 +250,10 @@ private Q_SLOTS:
         }
 
         UnitTestResult unitTestResult;
-        if (mMailClient->sUnitTestResults.isEmpty()) {
+        if (FakeMessageQueueJob::sUnitTestResults.isEmpty()) {
             qDebug() << "mail results are empty";
         } else {
-            unitTestResult = mMailClient->sUnitTestResults.first();
+            unitTestResult = FakeMessageQueueJob::sUnitTestResults.first();
         }
 
         if (expectedTransportId != -1 && unitTestResult.transportId != expectedTransportId) {
@@ -287,14 +331,14 @@ private Q_SLOTS:
         QFETCH(QStringList, expectedToList);
         QFETCH(QStringList, expectedBccList);
         QFETCH(QString, expectedSubject);
-        mMailClient->sUnitTestResults.clear();
+        FakeMessageQueueJob::sUnitTestResults.clear();
 
         mPendingSignals = 1;
         mMailClient->mailOrganizer(incidence, identity, from, bccMe, attachment, subject, transport);
         waitForSignals();
         QCOMPARE(mLastResult, expectedResult);
 
-        UnitTestResult unitTestResult = mMailClient->sUnitTestResults.first();
+        UnitTestResult unitTestResult = FakeMessageQueueJob::sUnitTestResults.first();
         if (expectedTransportId != -1)
             QCOMPARE(unitTestResult.transportId, expectedTransportId);
 
@@ -354,13 +398,13 @@ private Q_SLOTS:
         QFETCH(QString, expectedFrom);
         QFETCH(QStringList, expectedToList);
         QFETCH(QStringList, expectedBccList);
-        mMailClient->sUnitTestResults.clear();
+        FakeMessageQueueJob::sUnitTestResults.clear();
 
         mPendingSignals = 1;
         mMailClient->mailTo(incidence, identity, from, bccMe, recipients, attachment, transport);
         waitForSignals();
         QCOMPARE(mLastResult, expectedResult);
-        UnitTestResult unitTestResult = mMailClient->sUnitTestResults.first();
+        UnitTestResult unitTestResult = FakeMessageQueueJob::sUnitTestResults.first();
         if (expectedTransportId != -1)
             QCOMPARE(unitTestResult.transportId, expectedTransportId);
 




More information about the commits mailing list