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(5800) << "Scheduler::acceptTransaction, 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(5800) << "Scheduler::acceptPublish, status="
00232 << ScheduleMessage::statusName( status );
00233
00234 Incidence *newInc = static_cast<Incidence *>( newIncBase );
00235 Incidence *calInc = mCalendar->incidence( newIncBase->uid() );
00236 switch ( status ) {
00237 case ScheduleMessage::Unknown:
00238 case ScheduleMessage::PublishNew:
00239 case ScheduleMessage::PublishUpdate:
00240 res = true;
00241 if ( calInc ) {
00242 if ( ( newInc->revision() > calInc->revision() ) ||
00243 ( newInc->revision() == calInc->revision() &&
00244 newInc->lastModified() > calInc->lastModified() ) ) {
00245 mCalendar->deleteIncidence( calInc );
00246 } else {
00247 res = false;
00248 }
00249 }
00250 if ( res ) {
00251 mCalendar->addIncidence( newInc );
00252 }
00253 break;
00254 case ScheduleMessage::Obsolete:
00255 res = true;
00256 break;
00257 default:
00258 break;
00259 }
00260 deleteTransaction( newIncBase );
00261 return res;
00262 }
00263
00264 bool Scheduler::acceptRequest( IncidenceBase *newIncBase, ScheduleMessage::Status )
00265 {
00266 if ( newIncBase->type() == "FreeBusy" ) {
00267
00268 return true;
00269 }
00270 Incidence *newInc = dynamic_cast<Incidence *>( newIncBase );
00271 if ( newInc ) {
00272 bool res = true;
00273 Incidence *exInc = mCalendar->incidenceFromSchedulingID( newIncBase->uid() );
00274 if ( exInc ) {
00275 res = false;
00276 if ( ( newInc->revision() > exInc->revision() ) ||
00277 ( newInc->revision() == exInc->revision() &&
00278 newInc->lastModified()>exInc->lastModified() ) ) {
00279 mCalendar->deleteIncidence( exInc );
00280 res = true;
00281 }
00282 }
00283 if ( res ) {
00284
00285 newInc->setSchedulingID( newInc->uid() );
00286 newInc->setUid( CalFormat::createUniqueId() );
00287
00288 mCalendar->addIncidence( newInc );
00289 }
00290 deleteTransaction( newIncBase );
00291 return res;
00292 }
00293 return false;
00294 }
00295
00296 bool Scheduler::acceptAdd( IncidenceBase *incidence, ScheduleMessage::Status )
00297 {
00298 deleteTransaction(incidence);
00299 return false;
00300 }
00301
00302 bool Scheduler::acceptCancel( IncidenceBase *incidence, ScheduleMessage::Status )
00303 {
00304 bool ret = false;
00305 const IncidenceBase *toDelete = mCalendar->incidenceFromSchedulingID( incidence->uid() );
00306 if ( toDelete ) {
00307 Event *event = mCalendar->event( toDelete->uid() );
00308 if ( event ) {
00309 mCalendar->deleteEvent( event );
00310 ret = true;
00311 } else {
00312 Todo *todo = mCalendar->todo( toDelete->uid() );
00313 if ( todo ) {
00314 mCalendar->deleteTodo( todo );
00315 ret = true;
00316 }
00317 }
00318 }
00319 deleteTransaction( incidence );
00320 return ret;
00321 }
00322
00323 bool Scheduler::acceptDeclineCounter( IncidenceBase *incidence,
00324 ScheduleMessage::Status status )
00325 {
00326 Q_UNUSED( status );
00327 deleteTransaction( incidence );
00328 return false;
00329 }
00330
00331 bool Scheduler::acceptReply( IncidenceBase *incidence,
00332 ScheduleMessage::Status status,
00333 iTIPMethod method )
00334 {
00335 Q_UNUSED( status );
00336 if ( incidence->type() == "FreeBusy" ) {
00337 return acceptFreeBusy( incidence, method );
00338 }
00339 bool ret = false;
00340 Event *ev = mCalendar->event( incidence->uid() );
00341 Todo *to = mCalendar->todo( incidence->uid() );
00342
00343
00344 if ( !ev && !to ) {
00345 Incidence::List list = mCalendar->incidences();
00346 for ( Incidence::List::ConstIterator it = list.constBegin(); it != list.constEnd(); ++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(5800) << "Scheduler::acceptTransaction 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.begin(); inIt != attendeesIn.end(); ++inIt ) {
00370 Attendee *attIn = *inIt;
00371 bool found = false;
00372 for ( evIt = attendeesEv.begin(); evIt != attendeesEv.end(); ++evIt ) {
00373 Attendee *attEv = *evIt;
00374 if ( attIn->email().toLower() == attEv->email().toLower() ) {
00375
00376 kDebug(5800) << "Scheduler::acceptTransaction 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 delete cancel;
00414 continue;
00415 }
00416
00417 Attendee *a = new Attendee( attNew->name(), attNew->email(), attNew->RSVP(),
00418 attNew->status(), attNew->role(), attNew->uid() );
00419 a->setDelegate( attNew->delegate() );
00420 a->setDelegator( attNew->delegator() );
00421 if ( ev ) {
00422 ev->addAttendee( a );
00423 } else if ( to ) {
00424 to->addAttendee( a );
00425 }
00426 ret = true;
00427 attendeeAdded = true;
00428 }
00429
00430
00431 if ( attendeeAdded ) {
00432 if ( ev ) {
00433 ev->setRevision( ev->revision() + 1 );
00434 performTransaction( ev, iTIPRequest );
00435 }
00436 if ( to ) {
00437 to->setRevision( to->revision() + 1 );
00438 performTransaction( to, iTIPRequest );
00439 }
00440 }
00441
00442 if ( ret ) {
00443
00444
00445 if ( ev ) {
00446 ev->updated();
00447 } else if ( to ) {
00448 to->updated();
00449 }
00450 }
00451 if ( to ) {
00452
00453
00454 Todo *update = dynamic_cast<Todo*> ( incidence );
00455 Q_ASSERT( update );
00456 if ( update && ( to->percentComplete() != update->percentComplete() ) ) {
00457 to->setPercentComplete( update->percentComplete() );
00458 to->updated();
00459 }
00460 }
00461 } else {
00462 kError(5800) << "No incidence for scheduling\n";
00463 }
00464
00465 if ( ret ) {
00466 deleteTransaction( incidence );
00467 }
00468 return ret;
00469 }
00470
00471 bool Scheduler::acceptRefresh( IncidenceBase *incidence, ScheduleMessage::Status status )
00472 {
00473 Q_UNUSED( status );
00474
00475 deleteTransaction( incidence );
00476 return false;
00477 }
00478
00479 bool Scheduler::acceptCounter( IncidenceBase *incidence, ScheduleMessage::Status status )
00480 {
00481 Q_UNUSED( status );
00482 deleteTransaction( incidence );
00483 return false;
00484 }
00485
00486 bool Scheduler::acceptFreeBusy( IncidenceBase *incidence, iTIPMethod method )
00487 {
00488 if ( !d->mFreeBusyCache ) {
00489 kError() << "KCal::Scheduler: no FreeBusyCache.";
00490 return false;
00491 }
00492
00493 FreeBusy *freebusy = static_cast<FreeBusy *>(incidence);
00494
00495 kDebug(5800) << "acceptFreeBusy:: freeBusyDirName:" << freeBusyDir();
00496
00497 Person from;
00498 if( method == iTIPPublish ) {
00499 from = freebusy->organizer();
00500 }
00501 if ( ( method == iTIPReply ) && ( freebusy->attendeeCount() == 1 ) ) {
00502 Attendee *attendee = freebusy->attendees().first();
00503 from.setName( attendee->name() );
00504 from.setEmail( attendee->email() );
00505 }
00506
00507 if ( !d->mFreeBusyCache->saveFreeBusy( freebusy, from ) ) {
00508 return false;
00509 }
00510
00511 deleteTransaction( incidence );
00512 return true;
00513 }