00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023 #include "scheduler.h"
00024 #include "calendar.h"
00025 #include "event.h"
00026 #include "todo.h"
00027 #include "freebusy.h"
00028 #include "freebusycache.h"
00029 #include "icalformat.h"
00030
00031 #include <klocale.h>
00032 #include <kdebug.h>
00033 #include <kmessagebox.h>
00034 #include <kstandarddirs.h>
00035
00036 using namespace KCal;
00037
00038
00039 class KCal::ScheduleMessage::Private
00040 {
00041 public:
00042 Private() {}
00043
00044 IncidenceBase *mIncidence;
00045 iTIPMethod mMethod;
00046 Status mStatus;
00047 QString mError;
00048 };
00049
00050
00051 ScheduleMessage::ScheduleMessage( IncidenceBase *incidence,
00052 iTIPMethod method,
00053 ScheduleMessage::Status status )
00054 : d( new KCal::ScheduleMessage::Private )
00055 {
00056 d->mIncidence = incidence;
00057 d->mMethod = method;
00058 d->mStatus = status;
00059 }
00060
00061 ScheduleMessage::~ScheduleMessage()
00062 {
00063 delete d;
00064 }
00065
00066 IncidenceBase *ScheduleMessage::event()
00067 {
00068 return d->mIncidence;
00069 }
00070
00071 iTIPMethod ScheduleMessage::method()
00072 {
00073 return d->mMethod;
00074 }
00075
00076 ScheduleMessage::Status ScheduleMessage::status()
00077 {
00078 return d->mStatus;
00079 }
00080
00081 QString ScheduleMessage::statusName( ScheduleMessage::Status status )
00082 {
00083 switch( status ) {
00084 case PublishNew:
00085 return i18nc( "@item new message posting", "New Message Publish" );
00086 case PublishUpdate:
00087 return i18nc( "@item updated message", "Updated Message Published" );
00088 case Obsolete:
00089 return i18nc( "@item obsolete status", "Obsolete" );
00090 case RequestNew:
00091 return i18nc( "@item request new message posting", "Request New Message" );
00092 case RequestUpdate:
00093 return i18nc( "@item request updated posting", "Request Updated Message" );
00094 default:
00095 return i18nc( "@item unknown status", "Unknown Status: %1", status );
00096 }
00097 }
00098
00099 QString ScheduleMessage::error()
00100 {
00101 return d->mError;
00102 }
00103
00104
00105 struct KCal::Scheduler::Private
00106 {
00107 Private()
00108 : mFreeBusyCache( 0 )
00109 {
00110 }
00111 FreeBusyCache *mFreeBusyCache;
00112 };
00113
00114
00115 Scheduler::Scheduler( Calendar *calendar ) : d( new KCal::Scheduler::Private )
00116 {
00117 mCalendar = calendar;
00118 mFormat = new ICalFormat();
00119 mFormat->setTimeSpec( calendar->timeSpec() );
00120 }
00121
00122 Scheduler::~Scheduler()
00123 {
00124 delete mFormat;
00125 delete d;
00126 }
00127
00128 void Scheduler::setFreeBusyCache( FreeBusyCache *c )
00129 {
00130 d->mFreeBusyCache = c;
00131 }
00132
00133 FreeBusyCache *Scheduler::freeBusyCache() const
00134 {
00135 return d->mFreeBusyCache;
00136 }
00137
00138 bool Scheduler::acceptTransaction( IncidenceBase *incidence, iTIPMethod method,
00139 ScheduleMessage::Status status )
00140 {
00141 kDebug() << "method=" << methodName( method );
00142
00143 switch ( method ) {
00144 case iTIPPublish:
00145 return acceptPublish( incidence, status, method );
00146 case iTIPRequest:
00147 return acceptRequest( incidence, status );
00148 case iTIPAdd:
00149 return acceptAdd( incidence, status );
00150 case iTIPCancel:
00151 return acceptCancel( incidence, status );
00152 case iTIPDeclineCounter:
00153 return acceptDeclineCounter( incidence, status );
00154 case iTIPReply:
00155 return acceptReply( incidence, status, method );
00156 case iTIPRefresh:
00157 return acceptRefresh( incidence, status );
00158 case iTIPCounter:
00159 return acceptCounter( incidence, status );
00160 default:
00161 break;
00162 }
00163 deleteTransaction( incidence );
00164 return false;
00165 }
00166
00167 QString Scheduler::methodName( iTIPMethod method )
00168 {
00169 switch ( method ) {
00170 case iTIPPublish:
00171 return QLatin1String( "Publish" );
00172 case iTIPRequest:
00173 return QLatin1String( "Request" );
00174 case iTIPRefresh:
00175 return QLatin1String( "Refresh" );
00176 case iTIPCancel:
00177 return QLatin1String( "Cancel" );
00178 case iTIPAdd:
00179 return QLatin1String( "Add" );
00180 case iTIPReply:
00181 return QLatin1String( "Reply" );
00182 case iTIPCounter:
00183 return QLatin1String( "Counter" );
00184 case iTIPDeclineCounter:
00185 return QLatin1String( "Decline Counter" );
00186 default:
00187 return QLatin1String( "Unknown" );
00188 }
00189 }
00190
00191 QString Scheduler::translatedMethodName( iTIPMethod method )
00192 {
00193 switch ( method ) {
00194 case iTIPPublish:
00195 return i18nc( "@item event, to-do, journal or freebusy posting", "Publish" );
00196 case iTIPRequest:
00197 return i18nc( "@item event, to-do or freebusy scheduling requests", "Request" );
00198 case iTIPReply:
00199 return i18nc( "@item event, to-do or freebusy reply to request", "Reply" );
00200 case iTIPAdd:
00201 return i18nc(
00202 "@item event, to-do or journal additional property request", "Add" );
00203 case iTIPCancel:
00204 return i18nc( "@item event, to-do or journal cancellation notice", "Cancel" );
00205 case iTIPRefresh:
00206 return i18nc( "@item event or to-do description update request", "Refresh" );
00207 case iTIPCounter:
00208 return i18nc( "@item event or to-do submit counter proposal", "Counter" );
00209 case iTIPDeclineCounter:
00210 return i18nc( "@item event or to-do decline a counter proposal", "Decline Counter" );
00211 default:
00212 return i18nc( "@item no method", "Unknown" );
00213 }
00214 }
00215
00216 bool Scheduler::deleteTransaction(IncidenceBase *)
00217 {
00218 return true;
00219 }
00220
00221 bool Scheduler::acceptPublish( IncidenceBase *newIncBase,
00222 ScheduleMessage::Status status,
00223 iTIPMethod method )
00224 {
00225 if( newIncBase->type() == "FreeBusy" ) {
00226 return acceptFreeBusy( newIncBase, method );
00227 }
00228
00229 bool res = false;
00230
00231 kDebug() << "status=" << ScheduleMessage::statusName( status );
00232
00233 Incidence *newInc = static_cast<Incidence *>( newIncBase );
00234 Incidence *calInc = mCalendar->incidence( newIncBase->uid() );
00235 switch ( status ) {
00236 case ScheduleMessage::Unknown:
00237 case ScheduleMessage::PublishNew:
00238 case ScheduleMessage::PublishUpdate:
00239 res = true;
00240 if ( calInc ) {
00241 if ( ( newInc->revision() > calInc->revision() ) ||
00242 ( newInc->revision() == calInc->revision() &&
00243 newInc->lastModified() > calInc->lastModified() ) ) {
00244 mCalendar->deleteIncidence( calInc );
00245 } else {
00246 res = false;
00247 }
00248 }
00249 if ( res ) {
00250 mCalendar->addIncidence( newInc );
00251 }
00252 break;
00253 case ScheduleMessage::Obsolete:
00254 res = true;
00255 break;
00256 default:
00257 break;
00258 }
00259 deleteTransaction( newIncBase );
00260 return res;
00261 }
00262
00263 bool Scheduler::acceptRequest( IncidenceBase *newIncBase, ScheduleMessage::Status )
00264 {
00265 if ( newIncBase->type() == "FreeBusy" ) {
00266
00267 return true;
00268 }
00269 Incidence *newInc = dynamic_cast<Incidence *>( newIncBase );
00270 if ( newInc ) {
00271 bool res = true;
00272 Incidence *exInc = mCalendar->incidenceFromSchedulingID( newIncBase->uid() );
00273 if ( exInc ) {
00274 res = false;
00275 if ( ( newInc->revision() > exInc->revision() ) ||
00276 ( newInc->revision() == exInc->revision() &&
00277 newInc->lastModified()>exInc->lastModified() ) ) {
00278 mCalendar->deleteIncidence( exInc );
00279 res = true;
00280 }
00281 }
00282 if ( res ) {
00283
00284 newInc->setSchedulingID( newInc->uid() );
00285 newInc->setUid( CalFormat::createUniqueId() );
00286
00287 mCalendar->addIncidence( newInc );
00288 }
00289 deleteTransaction( newIncBase );
00290 return res;
00291 }
00292 return false;
00293 }
00294
00295 bool Scheduler::acceptAdd( IncidenceBase *incidence, ScheduleMessage::Status )
00296 {
00297 deleteTransaction(incidence);
00298 return false;
00299 }
00300
00301 bool Scheduler::acceptCancel( IncidenceBase *incidence, ScheduleMessage::Status )
00302 {
00303 bool ret = false;
00304 const IncidenceBase *toDelete = mCalendar->incidenceFromSchedulingID( incidence->uid() );
00305 if ( toDelete ) {
00306 Event *event = mCalendar->event( toDelete->uid() );
00307 if ( event ) {
00308 mCalendar->deleteEvent( event );
00309 ret = true;
00310 } else {
00311 Todo *todo = mCalendar->todo( toDelete->uid() );
00312 if ( todo ) {
00313 mCalendar->deleteTodo( todo );
00314 ret = true;
00315 }
00316 }
00317 }
00318 deleteTransaction( incidence );
00319 return ret;
00320 }
00321
00322 bool Scheduler::acceptDeclineCounter( IncidenceBase *incidence,
00323 ScheduleMessage::Status status )
00324 {
00325 Q_UNUSED( status );
00326 deleteTransaction( incidence );
00327 return false;
00328 }
00329
00330 bool Scheduler::acceptReply( IncidenceBase *incidence,
00331 ScheduleMessage::Status status,
00332 iTIPMethod method )
00333 {
00334 Q_UNUSED( status );
00335 if ( incidence->type() == "FreeBusy" ) {
00336 return acceptFreeBusy( incidence, method );
00337 }
00338 bool ret = false;
00339 Event *ev = mCalendar->event( incidence->uid() );
00340 Todo *to = mCalendar->todo( incidence->uid() );
00341
00342
00343 if ( !ev && !to ) {
00344 const Incidence::List list = mCalendar->incidences();
00345 for ( Incidence::List::ConstIterator it=list.constBegin(), end=list.constEnd();
00346 it != end; ++it ) {
00347 if ( (*it)->schedulingID() == incidence->uid() ) {
00348 ev = dynamic_cast<Event*>( *it );
00349 to = dynamic_cast<Todo*>( *it );
00350 break;
00351 }
00352 }
00353 }
00354
00355 if ( ev || to ) {
00356
00357 kDebug() << "match found!";
00358 Attendee::List attendeesIn = incidence->attendees();
00359 Attendee::List attendeesEv;
00360 Attendee::List attendeesNew;
00361 if ( ev ) {
00362 attendeesEv = ev->attendees();
00363 }
00364 if ( to ) {
00365 attendeesEv = to->attendees();
00366 }
00367 Attendee::List::ConstIterator inIt;
00368 Attendee::List::ConstIterator evIt;
00369 for ( inIt = attendeesIn.constBegin(); inIt != attendeesIn.constEnd(); ++inIt ) {
00370 Attendee *attIn = *inIt;
00371 bool found = false;
00372 for ( evIt = attendeesEv.constBegin(); evIt != attendeesEv.constEnd(); ++evIt ) {
00373 Attendee *attEv = *evIt;
00374 if ( attIn->email().toLower() == attEv->email().toLower() ) {
00375
00376 kDebug() << "update attendee";
00377 attEv->setStatus( attIn->status() );
00378 attEv->setDelegate( attIn->delegate() );
00379 attEv->setDelegator( attIn->delegator() );
00380 ret = true;
00381 found = true;
00382 }
00383 }
00384 if ( !found && attIn->status() != Attendee::Declined ) {
00385 attendeesNew.append( attIn );
00386 }
00387 }
00388
00389 bool attendeeAdded = false;
00390 for ( Attendee::List::ConstIterator it = attendeesNew.constBegin();
00391 it != attendeesNew.constEnd(); ++it ) {
00392 Attendee *attNew = *it;
00393 QString msg =
00394 i18nc( "@info", "%1 wants to attend %2 but was not invited.",
00395 attNew->fullName(),
00396 ( ev ? ev->summary() : to->summary() ) );
00397 if ( !attNew->delegator().isEmpty() ) {
00398 msg = i18nc( "@info", "%1 wants to attend %2 on behalf of %3.",
00399 attNew->fullName(),
00400 ( ev ? ev->summary() : to->summary() ), attNew->delegator() );
00401 }
00402 if ( KMessageBox::questionYesNo(
00403 0, msg, i18nc( "@title", "Uninvited attendee" ),
00404 KGuiItem( i18nc( "@option", "Accept Attendance" ) ),
00405 KGuiItem( i18nc( "@option", "Reject Attendance" ) ) ) != KMessageBox::Yes ) {
00406 KCal::Incidence *cancel = dynamic_cast<Incidence*>( incidence );
00407 if ( cancel ) {
00408 cancel->addComment(
00409 i18nc( "@info",
00410 "The organizer rejected your attendance at this meeting." ) );
00411 }
00412 performTransaction( cancel ? cancel : incidence, iTIPCancel, attNew->fullName() );
00413
00414
00415
00416 continue;
00417 }
00418
00419 Attendee *a = new Attendee( attNew->name(), attNew->email(), attNew->RSVP(),
00420 attNew->status(), attNew->role(), attNew->uid() );
00421 a->setDelegate( attNew->delegate() );
00422 a->setDelegator( attNew->delegator() );
00423 if ( ev ) {
00424 ev->addAttendee( a );
00425 } else if ( to ) {
00426 to->addAttendee( a );
00427 }
00428 ret = true;
00429 attendeeAdded = true;
00430 }
00431
00432
00433 if ( attendeeAdded ) {
00434 if ( ev ) {
00435 ev->setRevision( ev->revision() + 1 );
00436 performTransaction( ev, iTIPRequest );
00437 }
00438 if ( to ) {
00439 to->setRevision( to->revision() + 1 );
00440 performTransaction( to, iTIPRequest );
00441 }
00442 }
00443
00444 if ( ret ) {
00445
00446
00447 if ( ev ) {
00448 ev->updated();
00449 } else if ( to ) {
00450 to->updated();
00451 }
00452 }
00453 if ( to ) {
00454
00455
00456 Todo *update = dynamic_cast<Todo*> ( incidence );
00457 Q_ASSERT( update );
00458 if ( update && ( to->percentComplete() != update->percentComplete() ) ) {
00459 to->setPercentComplete( update->percentComplete() );
00460 to->updated();
00461 }
00462 }
00463 } else {
00464 kError(5800) << "No incidence for scheduling\n";
00465 }
00466
00467 if ( ret ) {
00468 deleteTransaction( incidence );
00469 }
00470 return ret;
00471 }
00472
00473 bool Scheduler::acceptRefresh( IncidenceBase *incidence, ScheduleMessage::Status status )
00474 {
00475 Q_UNUSED( status );
00476
00477 deleteTransaction( incidence );
00478 return false;
00479 }
00480
00481 bool Scheduler::acceptCounter( IncidenceBase *incidence, ScheduleMessage::Status status )
00482 {
00483 Q_UNUSED( status );
00484 deleteTransaction( incidence );
00485 return false;
00486 }
00487
00488 bool Scheduler::acceptFreeBusy( IncidenceBase *incidence, iTIPMethod method )
00489 {
00490 if ( !d->mFreeBusyCache ) {
00491 kError() << "KCal::Scheduler: no FreeBusyCache.";
00492 return false;
00493 }
00494
00495 FreeBusy *freebusy = static_cast<FreeBusy *>(incidence);
00496
00497 kDebug() << "freeBusyDirName:" << freeBusyDir();
00498
00499 Person from;
00500 if( method == iTIPPublish ) {
00501 from = freebusy->organizer();
00502 }
00503 if ( ( method == iTIPReply ) && ( freebusy->attendeeCount() == 1 ) ) {
00504 Attendee *attendee = freebusy->attendees().first();
00505 from.setName( attendee->name() );
00506 from.setEmail( attendee->email() );
00507 }
00508
00509 if ( !d->mFreeBusyCache->saveFreeBusy( freebusy, from ) ) {
00510 return false;
00511 }
00512
00513 deleteTransaction( incidence );
00514 return true;
00515 }