libkcal

scheduler.cpp

Go to the documentation of this file.
00001 /*
00002     This file is part of libkcal.
00003 
00004     Copyright (c) 2001,2004 Cornelius Schumacher <schumacher@kde.org>
00005     Copyright (C) 2004 Reinhold Kainhofer <reinhold@kainhofer.com>
00006 
00007     This library is free software; you can redistribute it and/or
00008     modify it under the terms of the GNU Library General Public
00009     License as published by the Free Software Foundation; either
00010     version 2 of the License, or (at your option) any later version.
00011 
00012     This library is distributed in the hope that it will be useful,
00013     but WITHOUT ANY WARRANTY; without even the implied warranty of
00014     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00015     Library General Public License for more details.
00016 
00017     You should have received a copy of the GNU Library General Public License
00018     along with this library; see the file COPYING.LIB.  If not, write to
00019     the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00020     Boston, MA 02110-1301, USA.
00021 */
00022 
00023 #include <klocale.h>
00024 #include <kdebug.h>
00025 #include <kmessagebox.h>
00026 #include <kstandarddirs.h>
00027 
00028 #include "event.h"
00029 #include "todo.h"
00030 #include "freebusy.h"
00031 #include "icalformat.h"
00032 #include "calendar.h"
00033 #include "freebusycache.h"
00034 
00035 #include "scheduler.h"
00036 
00037 using namespace KCal;
00038 
00039 ScheduleMessage::ScheduleMessage(IncidenceBase *incidence,int method,ScheduleMessage::Status status)
00040 {
00041   mIncidence = incidence;
00042   mMethod = method;
00043   mStatus = status;
00044 }
00045 
00046 QString ScheduleMessage::statusName(ScheduleMessage::Status status)
00047 {
00048   switch (status) {
00049     case PublishUpdate:
00050       return i18n("Updated Publish");
00051     case PublishNew:
00052       return i18n("Publish");
00053     case Obsolete:
00054       return i18n("Obsolete");
00055     case RequestNew:
00056       return i18n("New Request");
00057     case RequestUpdate:
00058       return i18n("Updated Request");
00059     default:
00060       return i18n("Unknown Status: %1").arg(QString::number(status));
00061   }
00062 }
00063 
00064 struct Scheduler::Private
00065 {
00066   Private() : mFreeBusyCache( 0 ) {}
00067 
00068   FreeBusyCache *mFreeBusyCache;
00069 };
00070 
00071 Scheduler::Scheduler(Calendar *calendar)
00072 {
00073   mCalendar = calendar;
00074   mFormat = new ICalFormat();
00075   mFormat->setTimeZone( calendar->timeZoneId(), !calendar->isLocalTime() );
00076 
00077   d = new Private;
00078 }
00079 
00080 Scheduler::~Scheduler()
00081 {
00082   delete d;
00083 
00084   delete mFormat;
00085 }
00086 
00087 void Scheduler::setFreeBusyCache( FreeBusyCache *c )
00088 {
00089   d->mFreeBusyCache = c;
00090 }
00091 
00092 FreeBusyCache *Scheduler::freeBusyCache() const
00093 {
00094   return d->mFreeBusyCache;
00095 }
00096 
00097 bool Scheduler::acceptTransaction(IncidenceBase *incidence,Method method,ScheduleMessage::Status status)
00098 {
00099   kdDebug(5800) << "Scheduler::acceptTransaction, method="
00100                 << methodName( method ) << endl;
00101 
00102   switch (method) {
00103     case Publish:
00104       return acceptPublish(incidence, status, method);
00105     case Request:
00106       return acceptRequest(incidence, status);
00107     case Add:
00108       return acceptAdd(incidence, status);
00109     case Cancel:
00110       return acceptCancel(incidence, status);
00111     case Declinecounter:
00112       return acceptDeclineCounter(incidence, status);
00113     case Reply:
00114       return acceptReply(incidence, status, method);
00115     case Refresh:
00116       return acceptRefresh(incidence, status);
00117     case Counter:
00118       return acceptCounter(incidence, status);
00119     default:
00120       break;
00121   }
00122   deleteTransaction(incidence);
00123   return false;
00124 }
00125 
00126 QString Scheduler::methodName(Method method)
00127 {
00128   switch (method) {
00129     case Publish:
00130       return QString::fromLatin1("Publish");
00131     case Request:
00132       return QString::fromLatin1("Request");
00133     case Refresh:
00134       return QString::fromLatin1("Refresh");
00135     case Cancel:
00136       return QString::fromLatin1("Cancel");
00137     case Add:
00138       return QString::fromLatin1("Add");
00139     case Reply:
00140       return QString::fromLatin1("Reply");
00141     case Counter:
00142       return QString::fromLatin1("Counter");
00143     case Declinecounter:
00144       return QString::fromLatin1("Decline Counter");
00145     default:
00146       return QString::fromLatin1("Unknown");
00147   }
00148 }
00149 
00150 QString Scheduler::translatedMethodName(Method method)
00151 {
00152   switch (method) {
00153     case Publish:
00154       return i18n("Publish");
00155     case Request:
00156       return i18n("Request");
00157     case Refresh:
00158       return i18n("Refresh");
00159     case Cancel:
00160       return i18n("Cancel");
00161     case Add:
00162       return i18n("Add");
00163     case Reply:
00164       return i18n("Reply");
00165     case Counter:
00166       return i18n("counter proposal","Counter");
00167     case Declinecounter:
00168       return i18n("decline counter proposal","Decline Counter");
00169     default:
00170       return i18n("Unknown");
00171   }
00172 }
00173 
00174 bool Scheduler::deleteTransaction(IncidenceBase *)
00175 {
00176   return true;
00177 }
00178 
00179 bool Scheduler::acceptPublish( IncidenceBase *newIncBase,
00180                                ScheduleMessage::Status status, Method method )
00181 {
00182   if( newIncBase->type() == "FreeBusy" ) {
00183     return acceptFreeBusy( newIncBase, method );
00184   }
00185 
00186   bool res = false;
00187   kdDebug(5800) << "Scheduler::acceptPublish, status="
00188             << ScheduleMessage::statusName( status ) << endl;
00189   Incidence *newInc = static_cast<Incidence *>( newIncBase );
00190   Incidence *calInc = mCalendar->incidence( newIncBase->uid() );
00191   switch ( status ) {
00192     case ScheduleMessage::Unknown:
00193     case ScheduleMessage::PublishNew:
00194     case ScheduleMessage::PublishUpdate:
00195       res = true;
00196       if ( calInc ) {
00197         if ( (newInc->revision() > calInc->revision()) ||
00198              (newInc->revision() == calInc->revision() &&
00199                newInc->lastModified() > calInc->lastModified() ) ) {
00200           mCalendar->deleteIncidence( calInc );
00201         } else
00202           res = false;
00203       }
00204       if ( res )
00205         mCalendar->addIncidence( newInc );
00206       break;
00207     case ScheduleMessage::Obsolete:
00208       res = true;
00209       break;
00210     default:
00211       break;
00212   }
00213   deleteTransaction( newIncBase );
00214   return res;
00215 }
00216 
00217 bool Scheduler::acceptRequest(IncidenceBase *newIncBase, ScheduleMessage::Status /* status */)
00218 {
00219   if (newIncBase->type()=="FreeBusy") {
00220     // reply to this request is handled in korganizer's incomingdialog
00221     return true;
00222   }
00223   Incidence *newInc = dynamic_cast<Incidence *>( newIncBase );
00224   if ( newInc ) {
00225     bool res = true;
00226     Incidence *exInc = mCalendar->incidenceFromSchedulingID( newIncBase->uid() );
00227     if ( exInc ) {
00228       res = false;
00229       if ( (newInc->revision() > exInc->revision()) ||
00230            (newInc->revision() == exInc->revision() &&
00231              newInc->lastModified()>exInc->lastModified()) ) {
00232         mCalendar->deleteIncidence( exInc );
00233         res = true;
00234       }
00235     }
00236     if ( res ) {
00237       // Move the uid to be the schedulingID and make a unique UID
00238       newInc->setSchedulingID( newInc->uid() );
00239       newInc->setUid( CalFormat::createUniqueId() );
00240 
00241       mCalendar->addIncidence(newInc);
00242     }
00243     deleteTransaction( newIncBase );
00244     return res;
00245   }
00246   return false;
00247 }
00248 
00249 bool Scheduler::acceptAdd(IncidenceBase *incidence,ScheduleMessage::Status /* status */)
00250 {
00251   deleteTransaction(incidence);
00252   return false;
00253 }
00254 
00255 bool Scheduler::acceptCancel(IncidenceBase *incidence,ScheduleMessage::Status /* status */)
00256 {
00257   bool ret = false;
00258   const IncidenceBase *toDelete = mCalendar->incidenceFromSchedulingID( incidence->uid() );
00259   if ( toDelete ) {
00260     Event *even = mCalendar->event(toDelete->uid());
00261     if (even) {
00262       mCalendar->deleteEvent(even);
00263       ret = true;
00264     } else {
00265       Todo *todo = mCalendar->todo(toDelete->uid());
00266       if (todo) {
00267         mCalendar->deleteTodo(todo);
00268         ret = true;
00269       }
00270     }
00271   }
00272   deleteTransaction(incidence);
00273   return ret;
00274 }
00275 
00276 bool Scheduler::acceptDeclineCounter(IncidenceBase *incidence,ScheduleMessage::Status /* status */)
00277 {
00278   deleteTransaction(incidence);
00279   return false;
00280 }
00281 
00282 //bool Scheduler::acceptFreeBusy(Incidence *incidence,ScheduleMessage::Status status)
00283 //{
00284 //  deleteTransaction(incidence);
00285 //  return false;
00286 //}
00287 
00288 bool Scheduler::acceptReply(IncidenceBase *incidence,ScheduleMessage::Status /* status */, Method method)
00289 {
00290   if(incidence->type()=="FreeBusy") {
00291     return acceptFreeBusy(incidence, method);
00292   }
00293   bool ret = false;
00294   Event *ev = mCalendar->event(incidence->uid());
00295   Todo *to = mCalendar->todo(incidence->uid());
00296 
00297   // try harder to find the correct incidence
00298   if ( !ev && !to ) {
00299     const Incidence::List list = mCalendar->incidences();
00300     for ( Incidence::List::ConstIterator it = list.begin(), end = list.end(); it != end; ++it ) {
00301       if ( (*it)->schedulingID() == incidence->uid() ) {
00302         ev = dynamic_cast<Event*>( *it );
00303         to = dynamic_cast<Todo*>( *it );
00304         break;
00305       }
00306     }
00307   }
00308 
00309   if (ev || to) {
00310     //get matching attendee in calendar
00311     kdDebug(5800) << "Scheduler::acceptTransaction match found!" << endl;
00312     Attendee::List attendeesIn = incidence->attendees();
00313     Attendee::List attendeesEv;
00314     Attendee::List attendeesNew;
00315     if (ev) attendeesEv = ev->attendees();
00316     if (to) attendeesEv = to->attendees();
00317     Attendee::List::ConstIterator inIt;
00318     Attendee::List::ConstIterator evIt;
00319     for ( inIt = attendeesIn.begin(); inIt != attendeesIn.end(); ++inIt ) {
00320       Attendee *attIn = *inIt;
00321       bool found = false;
00322       for ( evIt = attendeesEv.begin(); evIt != attendeesEv.end(); ++evIt ) {
00323         Attendee *attEv = *evIt;
00324         if (attIn->email().lower()==attEv->email().lower()) {
00325           //update attendee-info
00326           kdDebug(5800) << "Scheduler::acceptTransaction update attendee" << endl;
00327           attEv->setStatus(attIn->status());
00328           attEv->setDelegate(attIn->delegate());
00329           attEv->setDelegator(attIn->delegator());
00330           ret = true;
00331           found = true;
00332         }
00333       }
00334       if ( !found && attIn->status() != Attendee::Declined )
00335         attendeesNew.append( attIn );
00336     }
00337 
00338     bool attendeeAdded = false;
00339     for ( Attendee::List::ConstIterator it = attendeesNew.constBegin(); it != attendeesNew.constEnd(); ++it ) {
00340       Attendee* attNew = *it;
00341       QString msg = i18n("%1 wants to attend %2 but was not invited.").arg( attNew->fullName() )
00342           .arg( ev ? ev->summary() : to->summary() );
00343       if ( !attNew->delegator().isEmpty() )
00344         msg = i18n("%1 wants to attend %2 on behalf of %3.").arg( attNew->fullName() )
00345             .arg( ev ? ev->summary() : to->summary() )
00346             .arg( attNew->delegator() );
00347       if ( KMessageBox::questionYesNo( 0, msg, i18n("Uninvited attendee"),
00348            KGuiItem(i18n("Accept Attendance")), KGuiItem(i18n("Reject Attendance")) )
00349            != KMessageBox::Yes )
00350       {
00351         KCal::Incidence *cancel = dynamic_cast<Incidence*>( incidence );
00352         if ( cancel )
00353           cancel->addComment( i18n( "The organizer rejected your attendance at this meeting." ) );
00354         performTransaction( cancel ? cancel : incidence, Scheduler::Cancel, attNew->fullName() );
00355         delete cancel;
00356         continue;
00357       }
00358 
00359       Attendee *a = new Attendee( attNew->name(), attNew->email(), attNew->RSVP(),
00360                                   attNew->status(), attNew->role(), attNew->uid() );
00361       a->setDelegate( attNew->delegate() );
00362       a->setDelegator( attNew->delegator() );
00363       if ( ev )
00364         ev->addAttendee( a );
00365       else if ( to )
00366         to->addAttendee( a );
00367       ret = true;
00368       attendeeAdded = true;
00369     }
00370 
00371     // send update about new participants
00372     if ( attendeeAdded ) {
00373       if ( ev ) {
00374         ev->setRevision( ev->revision() + 1 );
00375         performTransaction( ev, Scheduler::Request );
00376       }
00377       if ( to ) {
00378         to->setRevision( ev->revision() + 1 );
00379         performTransaction( to, Scheduler::Request );
00380       }
00381     }
00382 
00383     if ( ret ) {
00384       // We set at least one of the attendees, so the incidence changed
00385       // Note: This should not result in a sequence number bump
00386       if ( ev )
00387         ev->updated();
00388       else if ( to )
00389         to->updated();
00390     }
00391     if ( to ) {
00392       // for VTODO a REPLY can be used to update the completion status of
00393       // a task. see RFC2446 3.4.3
00394       Todo *update = dynamic_cast<Todo*> ( incidence );
00395       Q_ASSERT( update );
00396       if ( update && ( to->percentComplete() != update->percentComplete() ) ) {
00397         to->setPercentComplete( update->percentComplete() );
00398         to->updated();
00399       }
00400     }
00401   } else
00402     kdError(5800) << "No incidence for scheduling\n";
00403   if (ret) deleteTransaction(incidence);
00404   return ret;
00405 }
00406 
00407 bool Scheduler::acceptRefresh(IncidenceBase *incidence,ScheduleMessage::Status /* status */)
00408 {
00409   // handled in korganizer's IncomingDialog
00410   deleteTransaction(incidence);
00411   return false;
00412 }
00413 
00414 bool Scheduler::acceptCounter(IncidenceBase *incidence,ScheduleMessage::Status /* status */)
00415 {
00416   deleteTransaction(incidence);
00417   return false;
00418 }
00419 
00420 bool Scheduler::acceptFreeBusy(IncidenceBase *incidence, Method method)
00421 {
00422   if ( !d->mFreeBusyCache ) {
00423     kdError() << "KCal::Scheduler: no FreeBusyCache." << endl;
00424     return false;
00425   }
00426 
00427   FreeBusy *freebusy = static_cast<FreeBusy *>(incidence);
00428 
00429   kdDebug(5800) << "acceptFreeBusy:: freeBusyDirName: " << freeBusyDir() << endl;
00430 
00431   Person from;
00432   if(method == Scheduler::Publish) {
00433     from = freebusy->organizer();
00434   }
00435   if((method == Scheduler::Reply) && (freebusy->attendeeCount() == 1)) {
00436     Attendee *attendee = freebusy->attendees().first();
00437     from = attendee->email();
00438   }
00439 
00440   if ( !d->mFreeBusyCache->saveFreeBusy( freebusy, from ) ) return false;
00441 
00442   deleteTransaction(incidence);
00443   return true;
00444 }