22 #include "scheduler.h"
27 using namespace KCalCore;
31 #include <KMessageBox>
33 using namespace KCalUtils;
36 struct KCalUtils::Scheduler::Private
39 Private() : mFreeBusyCache( 0 )
53 Scheduler::~Scheduler()
61 d->mFreeBusyCache = c;
66 return d->mFreeBusyCache;
72 kDebug() <<
"method=" << ScheduleMessage::methodName( method );
76 return acceptPublish( incidence, status, method );
78 return acceptRequest( incidence, status, email );
80 return acceptAdd( incidence, status );
82 return acceptCancel( incidence, status, email );
84 return acceptDeclineCounter( incidence, status );
86 return acceptReply( incidence, status, method );
88 return acceptRefresh( incidence, status );
90 return acceptCounter( incidence, status );
94 deleteTransaction( incidence );
98 bool Scheduler::deleteTransaction(
const IncidenceBase::Ptr & )
106 if ( newIncBase->type() == IncidenceBase::TypeFreeBusy ) {
107 return acceptFreeBusy( newIncBase, method );
112 kDebug() <<
"status=" << Stringify::scheduleMessageStatus( status );
114 Incidence::Ptr newInc = newIncBase.staticCast<
Incidence>() ;
115 Incidence::Ptr calInc = mCalendar->incidence( newIncBase->uid() );
117 case ScheduleMessage::Unknown:
118 case ScheduleMessage::PublishNew:
119 case ScheduleMessage::PublishUpdate:
120 if ( calInc && newInc ) {
121 if ( ( newInc->revision() > calInc->revision() ) ||
122 ( newInc->revision() == calInc->revision() &&
123 newInc->lastModified() > calInc->lastModified() ) ) {
124 const QString oldUid = calInc->uid();
126 if ( calInc->type() != newInc->type() ) {
127 kError() <<
"assigning different incidence types";
132 calInc->setSchedulingID( newInc->uid(), oldUid );
138 case ScheduleMessage::Obsolete:
144 deleteTransaction( newIncBase );
148 bool Scheduler::acceptRequest(
const IncidenceBase::Ptr &incidence,
150 const QString &email )
152 Incidence::Ptr inc = incidence.staticCast<
Incidence>() ;
154 kWarning() <<
"Accept what?";
157 if ( inc->type() == IncidenceBase::TypeFreeBusy ) {
162 const Incidence::List existingIncidences = mCalendar->incidencesFromSchedulingID( inc->uid() );
163 kDebug() <<
"status=" << Stringify::scheduleMessageStatus( status )
164 <<
": found " << existingIncidences.count()
165 <<
" incidences with schedulingID " << inc->schedulingID()
166 <<
"; uid was = " << inc->uid();
168 if ( existingIncidences.isEmpty() ) {
171 kDebug() <<
"incidence not found; calendar = " << mCalendar.data()
172 <<
"; incidence count = " << mCalendar->incidences().count();
174 Incidence::List::ConstIterator incit = existingIncidences.begin();
175 for ( ; incit != existingIncidences.end() ; ++incit ) {
176 Incidence::Ptr existingIncidence = *incit;
177 kDebug() <<
"Considering this found event ("
178 << ( existingIncidence->isReadOnly() ?
"readonly" :
"readwrite" )
179 <<
") :" << mFormat->
toString( existingIncidence );
181 if ( existingIncidence->isReadOnly() ) {
184 if ( existingIncidence->revision() <= inc->revision() ) {
186 bool isUpdate =
true;
192 kDebug() <<
"looking in " << existingIncidence->uid() <<
"'s attendees";
197 Attendee::List::ConstIterator ait;
198 for ( ait = attendees.begin(); ait != attendees.end(); ++ait ) {
199 if( (*ait)->email() == email && (*ait)->status() == Attendee::NeedsAction ) {
202 kDebug() <<
"ignoring " << existingIncidence->uid()
203 <<
" since I'm still NeedsAction there";
209 if ( existingIncidence->revision() == inc->revision() &&
210 existingIncidence->lastModified() > inc->lastModified() ) {
212 kDebug() <<
"This isn't an update - the found incidence was modified more recently";
213 deleteTransaction( existingIncidence );
216 kDebug() <<
"replacing existing incidence " << existingIncidence->uid();
218 const QString oldUid = existingIncidence->uid();
219 if ( existingIncidence->type() != inc->type() ) {
220 kError() <<
"assigning different incidence types";
223 IncidenceBase *existingIncidenceBase = existingIncidence.data();
225 *existingIncidenceBase = *incBase;
226 existingIncidence->setSchedulingID( inc->uid(), oldUid );
228 deleteTransaction( incidence );
233 kDebug() <<
"This isn't an update - the found incidence has a bigger revision number";
234 deleteTransaction( incidence );
240 inc->setSchedulingID( inc->uid(), CalFormat::createUniqueId() );
242 if ( existingIncidences.count() == 0 && inc->revision() > 0 ) {
243 KMessageBox::information(
246 "<para>You accepted an invitation update, but an earlier version of the "
247 "item could not be found in your calendar.</para>"
248 "<para>This may have occurred because:<list>"
249 "<item>the organizer did not include you in the original invitation</item>"
250 "<item>you did not accept the original invitation yet</item>"
251 "<item>you deleted the original invitation from your calendar</item>"
252 "<item>you no longer have access to the calendar containing the invitation</item>"
254 "<para>This is not a problem, but we thought you should know.</para>" ),
255 i18nc(
"@title",
"Cannot find invitation to be updated" ),
"AcceptCantFindIncidence" );
257 kDebug() <<
"Storing new incidence with scheduling uid=" << inc->schedulingID()
258 <<
" and uid=" << inc->uid();
259 const bool result = mCalendar->addIncidence( inc );
261 deleteTransaction( incidence );
265 bool Scheduler::acceptAdd(
const IncidenceBase::Ptr &incidence,
268 deleteTransaction( incidence );
272 bool Scheduler::acceptCancel(
const IncidenceBase::Ptr &incidence,
274 const QString &attendee )
276 Incidence::Ptr inc = incidence.staticCast<
Incidence>();
281 if ( inc->type() == IncidenceBase::TypeFreeBusy ) {
286 const Incidence::List existingIncidences = mCalendar->incidencesFromSchedulingID( inc->uid() );
287 kDebug() <<
"Scheduler::acceptCancel="
288 << Stringify::scheduleMessageStatus( status )
289 <<
": found " << existingIncidences.count()
290 <<
" incidences with schedulingID " << inc->schedulingID();
293 Incidence::List::ConstIterator incit = existingIncidences.begin();
294 for ( ; incit != existingIncidences.end() ; ++incit ) {
295 Incidence::Ptr i = *incit;
296 kDebug() <<
"Considering this found event ("
297 << ( i->isReadOnly() ?
"readonly" :
"readwrite" )
301 if ( i->isReadOnly() ) {
311 kDebug() <<
"looking in " << i->uid() <<
"'s attendees";
318 Attendee::List::ConstIterator ait;
319 for ( ait = attendees.begin(); ait != attendees.end(); ++ait ) {
320 if ( (*ait)->email() == attendee &&
321 (*ait)->status() == Attendee::NeedsAction ) {
324 kDebug() <<
"ignoring " << i->uid()
325 <<
" since I'm still NeedsAction there";
332 kDebug() <<
"removing existing incidence " << i->uid();
333 if ( i->type() == IncidenceBase::TypeEvent ) {
334 Event::Ptr event = mCalendar->event( i->uid() );
335 ret = (
event && mCalendar->deleteEvent( event ) );
336 }
else if ( i->type() == IncidenceBase::TypeTodo ) {
337 Todo::Ptr todo = mCalendar->todo( i->uid() );
338 ret = ( todo && mCalendar->deleteTodo( todo ) );
340 deleteTransaction( incidence );
346 if ( existingIncidences.count() > 0 && inc->revision() > 0 ) {
350 "The event or task could not be removed from your calendar. "
351 "Maybe it has already been deleted or is not owned by you. "
352 "Or it might belong to a read-only or disabled calendar." ) );
354 deleteTransaction( incidence );
358 bool Scheduler::acceptDeclineCounter(
const IncidenceBase::Ptr &incidence,
362 deleteTransaction( incidence );
370 if ( incidence->type() == IncidenceBase::TypeFreeBusy ) {
371 return acceptFreeBusy( incidence, method );
374 Event::Ptr ev = mCalendar->event( incidence->uid() );
375 Todo::Ptr to = mCalendar->todo( incidence->uid() );
379 const Incidence::List list = mCalendar->incidences();
380 for ( Incidence::List::ConstIterator it=list.constBegin(), end=list.constEnd();
382 if ( (*it)->schedulingID() == incidence->uid() ) {
383 ev = ( *it ).dynamicCast<
Event>();
384 to = ( *it ).dynamicCast<
Todo>();
392 kDebug() <<
"match found!";
397 attendeesEv = ev->attendees();
400 attendeesEv = to->attendees();
402 Attendee::List::ConstIterator inIt;
403 Attendee::List::ConstIterator evIt;
404 for ( inIt = attendeesIn.constBegin(); inIt != attendeesIn.constEnd(); ++inIt ) {
407 for ( evIt = attendeesEv.constBegin(); evIt != attendeesEv.constEnd(); ++evIt ) {
409 if ( attIn->email().toLower() == attEv->email().toLower() ) {
411 kDebug() <<
"update attendee";
412 attEv->setStatus( attIn->status() );
413 attEv->setDelegate( attIn->delegate() );
414 attEv->setDelegator( attIn->delegator() );
419 if ( !found && attIn->status() != Attendee::Declined ) {
420 attendeesNew.append( attIn );
424 bool attendeeAdded =
false;
425 for ( Attendee::List::ConstIterator it = attendeesNew.constBegin();
426 it != attendeesNew.constEnd(); ++it ) {
429 i18nc(
"@info",
"%1 wants to attend %2 but was not invited.",
431 ( ev ? ev->summary() : to->summary() ) );
432 if ( !attNew->delegator().isEmpty() ) {
433 msg = i18nc(
"@info",
"%1 wants to attend %2 on behalf of %3.",
435 ( ev ? ev->summary() : to->summary() ), attNew->delegator() );
437 if ( KMessageBox::questionYesNo(
438 0, msg, i18nc(
"@title",
"Uninvited attendee" ),
439 KGuiItem( i18nc(
"@option",
"Accept Attendance" ) ),
440 KGuiItem( i18nc(
"@option",
"Reject Attendance" ) ) ) != KMessageBox::Yes ) {
441 Incidence::Ptr cancel = incidence.dynamicCast<
Incidence>();
445 "The organizer rejected your attendance at this meeting." ) );
455 attNew->status(), attNew->role(), attNew->uid() ) );
457 a->setDelegate( attNew->delegate() );
458 a->setDelegator( attNew->delegator() );
460 ev->addAttendee( a );
462 to->addAttendee( a );
465 attendeeAdded =
true;
469 if ( attendeeAdded ) {
470 bool sendMail =
false;
472 if ( KMessageBox::questionYesNo(
475 "An attendee was added to the incidence. "
476 "Do you want to email the attendees an update message?" ),
477 i18nc(
"@title",
"Attendee Added" ),
478 KGuiItem( i18nc(
"@option",
"Send Messages" ) ),
479 KGuiItem( i18nc(
"@option",
"Do Not Send" ) ) ) == KMessageBox::Yes ) {
485 ev->setRevision( ev->revision() + 1 );
491 to->setRevision( to->revision() + 1 );
512 if ( update && ( to->percentComplete() != update->percentComplete() ) ) {
513 to->setPercentComplete( update->percentComplete() );
518 kError() <<
"No incidence for scheduling.";
522 deleteTransaction( incidence );
531 deleteTransaction( incidence );
538 deleteTransaction( incidence );
542 bool Scheduler::acceptFreeBusy(
const IncidenceBase::Ptr &incidence,
iTIPMethod method )
544 if ( !d->mFreeBusyCache ) {
545 kError() <<
"Scheduler: no FreeBusyCache.";
554 if( method == iTIPPublish ) {
555 from = freebusy->organizer();
557 if ( ( method == iTIPReply ) && ( freebusy->attendeeCount() == 1 ) ) {
559 from->setName( attendee->name() );
560 from->setEmail( attendee->email() );
563 if ( !d->mFreeBusyCache->saveFreeBusy( freebusy, from ) ) {
567 deleteTransaction( incidence );