2 Copyright (C) 2004 Reinhold Kainhofer <reinhold@kainhofer.com>
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License along
15 with this program; if not, write to the Free Software Foundation, Inc.,
16 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 As a special exception, permission is given to link this program
19 with any edition of Qt, and distribute the resulting executable,
20 without including the source code for Qt in the source distribution.
22 #include "incidencechanger.h"
23 #include "incidencechanger_p.h"
25 #include "calendaradaptor.h"
26 #include "dndfactory.h"
27 #include "kcalprefs.h"
28 #include "mailscheduler.h"
31 #include <Akonadi/ItemCreateJob>
32 #include <Akonadi/ItemDeleteJob>
33 #include <Akonadi/ItemModifyJob>
35 #include <KMessageBox>
37 using namespace CalendarSupport
;
39 InvitationHandler::Action
actionFromStatus( InvitationHandler::SendResult result
)
42 // Canceled, /**< Sending was canceled by the user, meaning there are
43 // local changes of which other attendees are not aware. */
44 // FailKeepUpdate, /**< Sending failed, the changes to the incidence must be kept. */
45 // FailAbortUpdate, /**< Sending failed, the changes to the incidence must be undone. */
46 // NoSendingNeeded, /**< In some cases it is not needed to send an invitation
47 // (e.g. when we are the only attendee) */
50 case InvitationHandler::ResultCanceled
:
51 return InvitationHandler::ActionDontSendMessage
;
52 case InvitationHandler::ResultSuccess
:
53 return InvitationHandler::ActionSendMessage
;
55 return InvitationHandler::ActionAsk
;
59 bool IncidenceChanger::Private::myAttendeeStatusChanged( const KCalCore::Incidence::Ptr
&newInc
,
60 const KCalCore::Incidence::Ptr
&oldInc
)
64 KCalCore::Attendee::Ptr oldMe
= oldInc
->attendeeByMails( KCalPrefs::instance()->allEmails() );
65 KCalCore::Attendee::Ptr newMe
= newInc
->attendeeByMails( KCalPrefs::instance()->allEmails() );
67 return oldMe
&& newMe
&& ( oldMe
->status() != newMe
->status() );
70 void IncidenceChanger::Private::queueChange( Change
*change
)
73 // If there's already a change queued we just discard it
74 // and send the newer change, which already includes
75 // previous modifications
76 const Akonadi::Item::Id id
= change
->newItem
.id();
77 if ( mQueuedChanges
.contains( id
) ) {
78 delete mQueuedChanges
.take( id
);
81 mQueuedChanges
[id
] = change
;
84 void IncidenceChanger::Private::cancelChanges( Akonadi::Item::Id id
)
86 delete mQueuedChanges
.take( id
);
87 delete mCurrentChanges
.take( id
);
90 void IncidenceChanger::Private::performNextChange( Akonadi::Item::Id id
)
92 delete mCurrentChanges
.take( id
);
94 if ( mQueuedChanges
.contains( id
) ) {
95 performChange( mQueuedChanges
.take( id
) );
99 bool IncidenceChanger::Private::performChange( Change
*change
)
102 Akonadi::Item newItem
= change
->newItem
;
103 const KCalCore::Incidence::Ptr oldinc
= change
->oldInc
;
104 const KCalCore::Incidence::Ptr newinc
= CalendarSupport::incidence( newItem
);
106 kDebug() << "id=" << newItem
.id()
107 << "uid=" << newinc
->uid()
108 << "version=" << newItem
.revision()
109 << "summary=" << newinc
->summary()
110 << "old summary" << oldinc
->summary()
111 << "type=" << int( newinc
->type() )
112 << "storageCollectionId=" << newItem
.storageCollectionId();
114 // There's not any job modifying this item, so mCurrentChanges[item.id] can't exist
115 Q_ASSERT( !mCurrentChanges
.contains( newItem
.id() ) );
117 // Check if the item was deleted, we already check in changeIncidence() but
118 // this change could be already in the queue when the item was deleted
119 if ( !mCalendar
->incidence( newItem
.id() ).isValid() ||
120 mDeletedItemIds
.contains( newItem
.id() ) ) {
121 kDebug() << "Incidence deleted";
122 // return true, the user doesn't want to see errors because he was too fast
126 if ( newinc
.data() == oldinc
.data() ) {
128 kDebug() << "Incidence not changed";
132 if ( mLatestRevisionByItemId
.contains( newItem
.id() ) &&
133 mLatestRevisionByItemId
[newItem
.id()] > newItem
.revision() ) {
134 /* When a ItemModifyJob ends, the application can still modify the old items if the user
135 * is quick because the ETM wasn't updated yet, and we'll get a STORE error, because
136 * we are not modifying the latest revision.
138 * When a job ends, we keep the new revision in m_latestVersionByItemId
139 * so we can update the item's revision
141 newItem
.setRevision( mLatestRevisionByItemId
[newItem
.id()] );
144 kDebug() << "Changing incidence";
145 const int revision
= newinc
->revision();
146 newinc
->setRevision( revision
+ 1 );
147 // FIXME: Use a generic method for this! Ideally, have an interface class
148 // for group cheduling. Each implementation could then just do what
149 // it wants with the event. If no groupware is used,use the null
151 if ( KCalPrefs::instance()->mUseGroupwareCommunication
) {
152 InvitationHandler
handler( mCalendar
);
153 handler
.setDialogParent( change
->parent
);
155 if ( mOperationStatus
.contains( change
->atomicOperationId
) ) {
156 handler
.setDefaultAction(
157 actionFromStatus( mOperationStatus
.value( change
->atomicOperationId
) ) );
160 const bool modify
= handler
.handleIncidenceAboutToBeModified( newinc
);
162 if ( newinc
->type() == oldinc
->type() ) {
163 KCalCore::IncidenceBase
*i1
= newinc
.data();
164 KCalCore::IncidenceBase
*i2
= oldinc
.data();
172 // FIXME: if that's a groupware incidence, and I'm not the organizer,
173 // send out a mail to the organizer with a counterproposal instead
174 // of actually changing the incidence. Then no locking is needed.
175 // FIXME: if that's a groupware incidence, and the incidence was
176 // never locked, we can't unlock it with endChange().
178 mCurrentChanges
[newItem
.id()] = change
;
180 // Don't write back remote revision since we can't make sure it is the current one
181 // fixes problems with DAV resource
182 newItem
.setRemoteRevision( QString() );
184 Akonadi::ItemModifyJob
*job
= new Akonadi::ItemModifyJob( newItem
);
186 // Remove this Qt::QueuedConnection after removing all job.exec()'s inside mailclient.cpp
187 connect( job
, SIGNAL(result(KJob
*)),
188 this, SLOT(changeIncidenceFinished(KJob
*)), Qt::QueuedConnection
);
192 void IncidenceChanger::Private::changeIncidenceFinished( KJob
*j
)
194 // we should probably update the revision number here,or internally in the Event
195 // itself when certain things change. need to verify with ical documentation.
196 const Akonadi::ItemModifyJob
* job
= qobject_cast
<const Akonadi::ItemModifyJob
*>( j
);
199 const Akonadi::Item newItem
= job
->item();
201 if ( !mCurrentChanges
.contains( newItem
.id() ) ) {
202 kDebug() << "Item was deleted? Great.";
203 cancelChanges( newItem
.id() );
204 emit
incidenceChangeFinished( Akonadi::Item(), newItem
, UNKNOWN_MODIFIED
, true );
208 const Private::Change
*change
= mCurrentChanges
[newItem
.id()];
209 const KCalCore::Incidence::Ptr oldInc
= change
->oldInc
;
211 Akonadi::Item oldItem
;
212 oldItem
.setPayload
<KCalCore::Incidence::Ptr
>( oldInc
);
213 oldItem
.setMimeType( oldInc
->mimeType() );
214 oldItem
.setId( newItem
.id() );
215 const KCalCore::Incidence::Ptr newInc
= CalendarSupport::incidence( newItem
);
217 if ( job
->error() ) {
218 kWarning() << "Item modify failed:" << job
->errorString();
220 KMessageBox::sorry( change
->parent
,
221 i18n( "Unable to save changes for incidence %1 \"%2\": %3",
222 i18n( newInc
->typeStr() ),
224 job
->errorString() ) );
225 emit
incidenceChangeFinished( oldItem
, newItem
, change
->action
, false );
227 if ( KCalPrefs::instance()->mUseGroupwareCommunication
) {
228 InvitationHandler
handler( mCalendar
);
229 handler
.setDialogParent( change
->parent
);
231 if ( mOperationStatus
.contains( change
->atomicOperationId
) ) {
232 handler
.setDefaultAction(
233 actionFromStatus( mOperationStatus
.value( change
->atomicOperationId
) ) );
235 const bool attendeeStatusChanged
= myAttendeeStatusChanged( oldInc
, newInc
);
236 InvitationHandler::SendResult result
=
237 handler
.sendIncidenceModifiedMessage( KCalCore::iTIPRequest
,
239 attendeeStatusChanged
);
240 if ( change
->atomicOperationId
) {
241 mOperationStatus
.insert( change
->atomicOperationId
, result
);
245 emit
incidenceChangeFinished( oldItem
, newItem
, change
->action
, true );
248 mLatestRevisionByItemId
[newItem
.id()] = newItem
.revision();
250 // execute any other modification if it exists
251 qRegisterMetaType
<Akonadi::Item::Id
>( "Akonadi::Item::Id" );
252 QMetaObject::invokeMethod( this, "performNextChange",
253 Qt::QueuedConnection
,
254 Q_ARG( Akonadi::Item::Id
, newItem
.id() ) );
257 IncidenceChanger::IncidenceChanger( CalendarSupport::Calendar
*cal
,
259 Akonadi::Entity::Id defaultCollectionId
)
260 : QObject( parent
), d( new Private( defaultCollectionId
, cal
) )
263 SIGNAL(incidenceChangeFinished(Akonadi::Item
,Akonadi::Item
,CalendarSupport::IncidenceChanger::WhatChanged
,bool)),
264 SIGNAL(incidenceChangeFinished(Akonadi::Item
,Akonadi::Item
,CalendarSupport::IncidenceChanger::WhatChanged
,bool)) );
267 IncidenceChanger::~IncidenceChanger()
272 bool IncidenceChanger::sendGroupwareMessage( const Akonadi::Item
&aitem
,
273 KCalCore::iTIPMethod method
,
276 uint atomicOperationId
)
278 const KCalCore::Incidence::Ptr incidence
= CalendarSupport::incidence( aitem
);
280 kDebug() << "Invalid incidence";
283 if ( KCalPrefs::instance()->thatIsMe( incidence
->organizer()->email() ) &&
284 incidence
->attendeeCount() > 0 &&
285 !KCalPrefs::instance()->mUseGroupwareCommunication
) {
286 emit
schedule( method
, aitem
);
288 } else if ( KCalPrefs::instance()->mUseGroupwareCommunication
) {
289 InvitationHandler
handler( d
->mCalendar
);
290 handler
.setDialogParent( parent
);
291 if ( d
->mOperationStatus
.contains( atomicOperationId
) ) {
292 handler
.setDefaultAction(
293 actionFromStatus( d
->mOperationStatus
.value( atomicOperationId
) ) );
295 InvitationHandler::SendResult status
;
298 status
= handler
.sendIncidenceCreatedMessage( method
, incidence
);
300 case INCIDENCEEDITED
:
301 status
= handler
.sendIncidenceModifiedMessage( method
, incidence
, false );
303 case INCIDENCEDELETED
:
304 status
= handler
.sendIncidenceDeletedMessage( method
, incidence
);
309 if ( atomicOperationId
&& action
!= NOCHANGE
) {
310 d
->mOperationStatus
.insert( atomicOperationId
, status
);
312 return ( status
!= InvitationHandler::ResultFailAbortUpdate
);
317 void IncidenceChanger::cancelAttendees( const Akonadi::Item
&aitem
)
319 const KCalCore::Incidence::Ptr incidence
= CalendarSupport::incidence( aitem
);
320 Q_ASSERT( incidence
);
321 if ( KCalPrefs::instance()->mUseGroupwareCommunication
) {
322 if ( KMessageBox::questionYesNo(
324 i18n( "Some attendees were removed from the incidence. "
325 "Shall cancel messages be sent to these attendees?" ),
326 i18n( "Attendees Removed" ), KGuiItem( i18n( "Send Messages" ) ),
327 KGuiItem( i18n( "Do Not Send" ) ) ) == KMessageBox::Yes
) {
328 // don't use Akonadi::Groupware::sendICalMessage here, because that asks just
329 // a very general question "Other people are involved, send message to
330 // them?", which isn't helpful at all in this situation. Afterwards, it
331 // would only call the Akonadi::MailScheduler::performTransaction, so do this
333 // FIXME: Groupware scheduling should be factored out to it's own class
335 CalendarSupport::MailScheduler
scheduler(
336 static_cast<CalendarSupport::Calendar
*>(d
->mCalendar
) );
337 scheduler
.performTransaction( incidence
, KCalCore::iTIPCancel
);
342 bool IncidenceChanger::deleteIncidence( const Akonadi::Item
&aitem
,
343 uint atomicOperationId
,
346 const KCalCore::Incidence::Ptr incidence
= CalendarSupport::incidence( aitem
);
348 kDebug() << "Invalid incidence";
352 kDebug() << "Deleting incidence " << incidence
->summary() << "; id = " << aitem
.id();
354 if ( !isNotDeleted( aitem
.id() ) ) {
355 kDebug() << "Item already deleted, skipping and returning true";
359 if ( !( d
->mCalendar
->hasDeleteRights( aitem
) ) ) {
360 kWarning() << "insufficient rights to delete incidence";
364 const bool doDelete
= sendGroupwareMessage( aitem
, KCalCore::iTIPCancel
,
365 INCIDENCEDELETED
, parent
, atomicOperationId
);
367 kDebug() << "Groupware says no";
371 d
->mDeletedItemIds
.append( aitem
.id() );
373 emit
incidenceToBeDeleted( aitem
);
374 d
->cancelChanges( aitem
.id() ); //abort changes to this incidence cause we will just delete it
375 Akonadi::ItemDeleteJob
*job
= new Akonadi::ItemDeleteJob( aitem
);
376 connect( job
, SIGNAL(result(KJob
*)), this, SLOT(deleteIncidenceFinished(KJob
*)) );
380 void IncidenceChanger::deleteIncidenceFinished( KJob
*j
)
382 // todo, cancel changes?
384 const Akonadi::ItemDeleteJob
*job
= qobject_cast
<const Akonadi::ItemDeleteJob
*>( j
);
386 const Akonadi::Item::List items
= job
->deletedItems();
387 Q_ASSERT( items
.count() == 1 );
388 KCalCore::Incidence::Ptr tmp
= CalendarSupport::incidence( items
.first() );
390 if ( job
->error() ) {
391 KMessageBox::sorry( 0, //PENDING(AKONADI_PORT) set parent
392 i18n( "Unable to delete incidence %1 \"%2\": %3",
393 i18n( tmp
->typeStr() ),
395 job
->errorString() ) );
396 d
->mDeletedItemIds
.removeOne( items
.first().id() );
397 emit
incidenceDeleteFinished( items
.first(), false );
400 if ( !KCalPrefs::instance()->thatIsMe( tmp
->organizer()->email() ) ) {
401 const QStringList myEmails
= KCalPrefs::instance()->allEmails();
402 bool notifyOrganizer
= false;
403 for ( QStringList::ConstIterator it
= myEmails
.begin(); it
!= myEmails
.end(); ++it
) {
405 KCalCore::Attendee::Ptr
me( tmp
->attendeeByMail( email
) );
407 if ( me
->status() == KCalCore::Attendee::Accepted
||
408 me
->status() == KCalCore::Attendee::Delegated
) {
409 notifyOrganizer
= true;
411 KCalCore::Attendee::Ptr
newMe( new KCalCore::Attendee( *me
) );
412 newMe
->setStatus( KCalCore::Attendee::Declined
);
413 tmp
->clearAttendees();
414 tmp
->addAttendee( newMe
);
419 if ( KCalPrefs::instance()->useGroupwareCommunication() && notifyOrganizer
) {
420 CalendarSupport::MailScheduler
scheduler( d
->mCalendar
);
421 scheduler
.performTransaction( tmp
, KCalCore::iTIPReply
);
424 d
->mLatestRevisionByItemId
.remove( items
.first().id() );
425 emit
incidenceDeleteFinished( items
.first(), true );
428 bool IncidenceChanger::cutIncidences( const Akonadi::Item::List
&list
, QWidget
*parent
)
430 Akonadi::Item::List::ConstIterator it
;
431 bool doDelete
= true;
432 Akonadi::Item::List itemsToCut
;
433 const uint atomicOperationId
= startAtomicOperation();
434 for ( it
= list
.constBegin(); it
!= list
.constEnd(); ++it
) {
435 if ( CalendarSupport::hasIncidence( ( *it
) ) ) {
436 doDelete
= sendGroupwareMessage( *it
, KCalCore::iTIPCancel
,
437 INCIDENCEDELETED
, parent
, atomicOperationId
);
440 emit
incidenceToBeDeleted( *it
);
441 itemsToCut
.append( *it
);
446 endAtomicOperation( atomicOperationId
);
448 #ifndef QT_NO_DRAGANDDROP
449 CalendarAdaptor::Ptr
cal( new CalendarAdaptor( d
->mCalendar
, parent
) );
450 CalendarSupport::DndFactory
factory( cal
, true/*delete calendarAdaptor*/ );
452 if ( factory
.cutIncidences( itemsToCut
) ) {
454 for ( it
= itemsToCut
.constBegin(); it
!= itemsToCut
.constEnd(); ++it
) {
455 emit
incidenceDeleteFinished( *it
, true );
457 return !itemsToCut
.isEmpty();
458 #ifndef QT_NO_DRAGANDDROP
465 bool IncidenceChanger::cutIncidence( const Akonadi::Item
&item
, QWidget
*parent
)
467 Akonadi::Item::List items
;
468 items
.append( item
);
469 return cutIncidences( items
, parent
);
472 void IncidenceChanger::setDefaultCollectionId( Akonadi::Entity::Id defaultCollectionId
)
474 d
->mDefaultCollectionId
= defaultCollectionId
;
477 bool IncidenceChanger::changeIncidence( const KCalCore::Incidence::Ptr
&oldinc
,
478 const Akonadi::Item
&newItem
,
481 uint atomicOperationId
)
483 if ( !CalendarSupport::hasIncidence( newItem
) || !newItem
.isValid() ) {
484 kDebug() << "Skipping invalid item id=" << newItem
.id();
488 if ( !d
->mCalendar
->hasChangeRights( newItem
) ) {
489 kWarning() << "insufficient rights to change incidence";
493 if ( !isNotDeleted( newItem
.id() ) ) {
494 kDebug() << "Skipping change, the item got deleted";
498 Private::Change
*change
= new Private::Change();
499 change
->action
= action
;
500 change
->newItem
= newItem
;
501 change
->oldInc
= oldinc
;
502 change
->parent
= parent
;
503 change
->atomicOperationId
= atomicOperationId
;
505 if ( d
->mCurrentChanges
.contains( newItem
.id() ) ) {
506 d
->queueChange( change
);
508 d
->performChange( change
);
513 bool IncidenceChanger::addIncidence( const KCalCore::Incidence::Ptr
&incidence
,
515 Akonadi::Collection
&selectedCollection
,
517 uint atomicOperationId
)
519 const Akonadi::Collection defaultCollection
= d
->mCalendar
->collection( d
->mDefaultCollectionId
);
521 const QString incidenceMimeType
= incidence
->mimeType();
522 const bool defaultIsOk
= defaultCollection
.contentMimeTypes().contains( incidenceMimeType
) &&
523 defaultCollection
.rights() & Akonadi::Collection::CanCreateItem
;
525 if ( d
->mDestinationPolicy
== ASK_DESTINATION
||
526 !defaultCollection
.isValid() ||
528 QStringList
mimeTypes( incidenceMimeType
);
529 selectedCollection
= CalendarSupport::selectCollection( parent
,
534 dialogCode
= QDialog::Accepted
;
535 selectedCollection
= defaultCollection
;
538 if ( selectedCollection
.isValid() ) {
539 return addIncidence( incidence
, selectedCollection
, parent
, atomicOperationId
);
541 kError() << "Selected collection isn't valid.";
546 bool IncidenceChanger::addIncidence( const KCalCore::Incidence::Ptr
&incidence
,
547 const Akonadi::Collection
&collection
,
549 uint atomicOperationId
)
553 if ( !incidence
|| !collection
.isValid() ) {
554 kError() << "Incidence or collection isn't valid. collection.isValid() == "
555 << collection
.isValid();
559 if ( !( collection
.rights() & Akonadi::Collection::CanCreateItem
) ) {
560 kWarning() << "insufficient rights to create incidence";
565 item
.setPayload
<KCalCore::Incidence::Ptr
>( incidence
);
567 item
.setMimeType( incidence
->mimeType() );
568 Akonadi::ItemCreateJob
*job
= new Akonadi::ItemCreateJob( item
, collection
);
570 Private::AddInfo addInfo
;
571 addInfo
.parent
= parent
;
572 addInfo
.atomicOperationId
= atomicOperationId
;
574 // so the jobs sees this info
575 d
->mAddInfoForJob
.insert( job
, addInfo
);
577 // The connection needs to be queued to be sure addIncidenceFinished
578 // is called after the kjob finished it's eventloop. That's needed
579 // because Akonadi::Groupware uses synchronous job->exec() calls.
580 connect( job
, SIGNAL(result(KJob
*)),
581 this, SLOT(addIncidenceFinished(KJob
*)), Qt::QueuedConnection
);
585 void IncidenceChanger::addIncidenceFinished( KJob
*j
)
588 const Akonadi::ItemCreateJob
*job
= qobject_cast
<const Akonadi::ItemCreateJob
*>( j
);
590 KCalCore::Incidence::Ptr incidence
= CalendarSupport::incidence( job
->item() );
592 if ( job
->error() ) {
594 0, //PENDING(AKONADI_PORT) set parent, ideally the one passed in addIncidence...
595 i18n( "Unable to save %1 \"%2\": %3",
596 i18n( incidence
->typeStr() ),
597 incidence
->summary(),
598 job
->errorString() ) );
599 emit
incidenceAddFinished( job
->item(), false );
603 if ( KCalPrefs::instance()->useGroupwareCommunication() ) {
604 Q_ASSERT( incidence
);
605 InvitationHandler
handler( d
->mCalendar
);
606 //handler.setDialogParent( 0 ); // PENDING(AKONADI_PORT) set parent,
607 //ideally the one passed in addIncidence...
608 const InvitationHandler::SendResult status
=
609 handler
.sendIncidenceCreatedMessage( KCalCore::iTIPRequest
, incidence
);
611 if ( status
== InvitationHandler::ResultFailAbortUpdate
) {
612 // TODO: At this point we'd need to delete the incidence again.
613 kError() << "Sending invitations failed, but did not delete the incidence";
616 const uint atomicOperationId
= d
->mAddInfoForJob
[j
].atomicOperationId
;
617 if ( atomicOperationId
) {
618 d
->mOperationStatus
.insert( atomicOperationId
, status
);
621 emit
incidenceAddFinished( job
->item(), true );
624 void IncidenceChanger::setDestinationPolicy( DestinationPolicy destinationPolicy
)
626 d
->mDestinationPolicy
= destinationPolicy
;
629 IncidenceChanger::DestinationPolicy
IncidenceChanger::destinationPolicy() const
631 return d
->mDestinationPolicy
;
634 bool IncidenceChanger::isNotDeleted( Akonadi::Item::Id id
) const
636 if ( d
->mCalendar
->incidence( id
).isValid() ) {
637 // it's inside the calendar, but maybe it's being deleted by a job or was
638 // deleted but the ETM doesn't know yet
639 return !d
->mDeletedItemIds
.contains( id
);
641 // not inside the calendar, i don't know it
646 void IncidenceChanger::setCalendar( CalendarSupport::Calendar
*calendar
)
648 d
->mCalendar
= calendar
;
651 uint
IncidenceChanger::startAtomicOperation()
653 static unsigned int latestAtomicOperationId
= 0;
655 return ++latestAtomicOperationId
;
658 void IncidenceChanger::endAtomicOperation( uint atomicOperationId
)
660 d
->mOperationStatus
.remove( atomicOperationId
);
663 bool IncidenceChanger::changeInProgress( Akonadi::Item::Id id
)
665 return d
->mCurrentChanges
.contains( id
);