libkcal Library API Documentation

scheduler.cpp

00001 /*
00002     This file is part of libkcal.
00003 
00004     Copyright (c) 2001,2004 Cornelius Schumacher <schumacher@kde.org>
00005 
00006     This library is free software; you can redistribute it and/or
00007     modify it under the terms of the GNU Library General Public
00008     License as published by the Free Software Foundation; either
00009     version 2 of the License, or (at your option) any later version.
00010 
00011     This library is distributed in the hope that it will be useful,
00012     but WITHOUT ANY WARRANTY; without even the implied warranty of
00013     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00014     Library General Public License for more details.
00015 
00016     You should have received a copy of the GNU Library General Public License
00017     along with this library; see the file COPYING.LIB.  If not, write to
00018     the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
00019     Boston, MA 02111-1307, USA.
00020 */
00021 
00022 #include <klocale.h>
00023 #include <kdebug.h>
00024 #include <kstandarddirs.h>
00025 #include <kmessagebox.h>
00026 
00027 #include "event.h"
00028 #include "todo.h"
00029 #include "freebusy.h"
00030 #include "icalformat.h"
00031 #include "calendar.h"
00032 #include "freebusycache.h"
00033 
00034 #include "scheduler.h"
00035 
00036 using namespace KCal;
00037 
00038 ScheduleMessage::ScheduleMessage(IncidenceBase *incidence,int method,ScheduleMessage::Status status)
00039 {
00040   mIncidence = incidence;
00041   mMethod = method;
00042   mStatus = status;
00043 }
00044 
00045 QString ScheduleMessage::statusName(ScheduleMessage::Status status)
00046 {
00047   switch (status) {
00048     case PublishUpdate:
00049       return i18n("Updated Publish");
00050     case PublishNew:
00051       return i18n("Publish");
00052     case Obsolete:
00053       return i18n("Obsolete");
00054     case RequestNew:
00055       return i18n("New Request");
00056     case RequestUpdate:
00057       return i18n("Updated Request");
00058     default:
00059       return i18n("Unknown Status: %1").arg(QString::number(status));
00060   }
00061 }
00062 
00063 struct Scheduler::Private
00064 {
00065   Private() : mFreeBusyCache( 0 ) {}
00066 
00067   FreeBusyCache *mFreeBusyCache;
00068 };
00069 
00070 Scheduler::Scheduler(Calendar *calendar)
00071 {
00072   mCalendar = calendar;
00073   mFormat = new ICalFormat();
00074   mFormat->setTimeZone( calendar->timeZoneId(), !calendar->isLocalTime() );
00075 
00076   d = new Private;
00077 }
00078 
00079 Scheduler::~Scheduler()
00080 {
00081   delete d;
00082 
00083   delete mFormat;
00084 }
00085 
00086 void Scheduler::setFreeBusyCache( FreeBusyCache *c )
00087 {
00088   d->mFreeBusyCache = c;
00089 }
00090 
00091 FreeBusyCache *Scheduler::freeBusyCache() const
00092 {
00093   return d->mFreeBusyCache;
00094 }
00095 
00096 bool Scheduler::acceptTransaction( IncidenceBase *incidence,
00097                                    Method method,
00098                                    ScheduleMessage::Status status,
00099                                    const QString &attendee )
00100 {
00101   kdDebug(5800) << "Scheduler::acceptTransaction, method="
00102                 << methodName( method ) << endl;
00103 
00104   switch (method) {
00105     case Publish:
00106       return acceptPublish(incidence, status, method);
00107     case Request:
00108       return acceptRequest( incidence, status, attendee );
00109     case Add:
00110       return acceptAdd(incidence, status);
00111     case Cancel:
00112       return acceptCancel(incidence, status);
00113     case Declinecounter:
00114       return acceptDeclineCounter(incidence, status);
00115     case Reply:
00116       return acceptReply(incidence, status, method);
00117     case Refresh:
00118       return acceptRefresh(incidence, status);
00119     case Counter:
00120       return acceptCounter(incidence, status);
00121     default:
00122       deleteTransaction(incidence);
00123       return false;
00124   }
00125   deleteTransaction(incidence);
00126   return false;
00127 }
00128 
00129 QString Scheduler::methodName(Method method)
00130 {
00131   switch (method) {
00132     case Publish:
00133       return QString::fromLatin1("Publish");
00134     case Request:
00135       return QString::fromLatin1("Request");
00136     case Refresh:
00137       return QString::fromLatin1("Refresh");
00138     case Cancel:
00139       return QString::fromLatin1("Cancel");
00140     case Add:
00141       return QString::fromLatin1("Add");
00142     case Reply:
00143       return QString::fromLatin1("Reply");
00144     case Counter:
00145       return QString::fromLatin1("Counter");
00146     case Declinecounter:
00147       return QString::fromLatin1("Decline Counter");
00148     default:
00149       return QString::fromLatin1("Unknown");
00150   }
00151 }
00152 
00153 QString Scheduler::translatedMethodName(Method method)
00154 {
00155   switch (method) {
00156     case Publish:
00157       return i18n("Publish");
00158     case Request:
00159       return i18n("Request");
00160     case Refresh:
00161       return i18n("Refresh");
00162     case Cancel:
00163       return i18n("Cancel");
00164     case Add:
00165       return i18n("Add");
00166     case Reply:
00167       return i18n("Reply");
00168     case Counter:
00169       return i18n("counter proposal","Counter");
00170     case Declinecounter:
00171       return i18n("decline counter proposal","Decline Counter");
00172     default:
00173       return i18n("Unknown");
00174   }
00175 }
00176 
00177 bool Scheduler::deleteTransaction(IncidenceBase *)
00178 {
00179   return true;
00180 }
00181 
00182 bool Scheduler::acceptPublish( IncidenceBase *incidence,
00183                                ScheduleMessage::Status status, Method method )
00184 {
00185   if( incidence->type() == "FreeBusy" ) {
00186     return acceptFreeBusy( incidence, method );
00187   }
00188   kdDebug(5800) << "Scheduler::acceptPublish, status="
00189             << ScheduleMessage::statusName( status ) << endl;
00190   Incidence *inc = static_cast<Incidence *>( incidence );
00191   Event *even = mCalendar->event( incidence->uid() );
00192   switch ( status ) {
00193     case ScheduleMessage::Unknown:
00194     case ScheduleMessage::PublishNew:
00195     case ScheduleMessage::PublishUpdate:
00196       if ( even ) {
00197         if ( even->revision() <= inc->revision() ) {
00198       if ( even->revision() == inc->revision() &&
00199           even->lastModified() > inc->lastModified() ) {
00200         deleteTransaction( incidence );
00201         return false;
00202       }
00203       mCalendar->deleteEvent( even );
00204     } else {
00205       deleteTransaction( incidence );
00206       return false;
00207     }
00208       }
00209       mCalendar->addIncidence( inc );
00210       deleteTransaction( incidence );
00211       return true;
00212     case ScheduleMessage::Obsolete:
00213       return true;
00214     default:
00215       deleteTransaction( incidence );
00216       return false;
00217   }
00218 }
00219 
00220 bool Scheduler::acceptRequest( IncidenceBase *incidence,
00221                                ScheduleMessage::Status status,
00222                                const QString &attendee )
00223 {
00224   Incidence *inc = static_cast<Incidence *>(incidence);
00225   if (inc->type()=="FreeBusy") {
00226     // reply to this request is handled in korganizer's incomingdialog
00227     return true;
00228   }
00229 
00230   const Incidence::List existingIncidences = mCalendar->incidencesFromSchedulingID( inc->uid() );
00231   kdDebug(5800) << "Scheduler::acceptRequest status=" << ScheduleMessage::statusName( status ) << ": found " << existingIncidences.count() << " incidences with schedulingID " << inc->schedulingID() << endl;
00232   Incidence::List::ConstIterator incit = existingIncidences.begin();
00233   for ( ; incit != existingIncidences.end() ; ++incit ) {
00234     Incidence* const i = *incit;
00235     kdDebug(5800) << "Considering this found event ("
00236                   << ( i->isReadOnly() ? "readonly" : "readwrite" )
00237                   << ") :" << mFormat->toString( i ) << endl;
00238     // If it's readonly, we can't possible update it.
00239     if ( i->isReadOnly() )
00240       continue;
00241     if ( i->revision() <= inc->revision() ) {
00242       // The new incidence might be an update for the found one
00243       bool isUpdate = true;
00244       // Code for new invitations:
00245       // If you think we could check the value of "status" to be RequestNew: we can't.
00246       // It comes from a similar check inside libical, where the event is compared to
00247       // other events in the calendar. But if we have another version of the event around
00248       // (e.g. shared folder for a group), the status could be RequestNew, Obsolete or Updated.
00249       kdDebug(5800) << "looking in " << i->uid() << "'s attendees" << endl;
00250       // This is supposed to be a new request, not an update - however we want to update
00251       // the existing one to handle the "clicking more than once on the invitation" case.
00252       // So check the attendee status of the attendee.
00253       const KCal::Attendee::List attendees = i->attendees();
00254       KCal::Attendee::List::ConstIterator ait;
00255       for ( ait = attendees.begin(); ait != attendees.end(); ++ait ) {
00256         if( (*ait)->email() == attendee && (*ait)->status() == Attendee::NeedsAction ) {
00257           // This incidence wasn't created by me - it's probably in a shared folder
00258           // and meant for someone else, ignore it.
00259           kdDebug(5800) << "ignoring " << i->uid() << " since I'm still NeedsAction there" << endl;
00260           isUpdate = false;
00261           break;
00262         }
00263       }
00264       if ( isUpdate ) {
00265         if ( i->revision() == inc->revision() &&
00266              i->lastModified() > inc->lastModified() ) {
00267           // This isn't an update - the found incidence was modified more recently
00268           kdDebug(5800) << "This isn't an update - the found incidence was modified more recently" << endl;
00269           deleteTransaction(incidence);
00270           return false;
00271         }
00272         kdDebug(5800) << "replacing existing incidence " << i->uid() << endl;
00273         mCalendar->deleteIncidence( i );
00274         break; // replacing one is enough
00275       }
00276     } else {
00277       // This isn't an update - the found incidence has a bigger revision number
00278       kdDebug(5800) << "This isn't an update - the found incidence has a bigger revision number" << endl;
00279       deleteTransaction(incidence);
00280       return false;
00281     }
00282   }
00283 
00284   // Move the uid to be the schedulingID and make a unique UID
00285   inc->setSchedulingID( inc->uid() );
00286   inc->setUid( CalFormat::createUniqueId() );
00287   // in case this is an update and we didn't find the to-be-updated incidence, 
00288   // ask whether we should create a new one, or drop the update
00289   if ( existingIncidences.count() > 0 || inc->revision() == 0 ||
00290           KMessageBox::warningYesNo( 0,
00291               i18n("The event, task or journal to be updated could not be found. "
00292                   "Maybe it has already been deleted, or the calendar that "
00293                   "contains it is disabled. Press continue to create a new "
00294                   "one or 'throw away' to discard this update." ),
00295               i18n("Discard this update?"), i18n("Store"), i18n("Throw away") ) == KMessageBox::Yes ) {
00296     kdDebug(5800) << "Storing new incidence with scheduling uid=" << inc->schedulingID() << " and uid=" << inc->uid() << endl;
00297     mCalendar->addIncidence(inc);
00298   }
00299   deleteTransaction(incidence);
00300   return true;
00301 }
00302 
00303 bool Scheduler::acceptAdd(IncidenceBase *incidence,ScheduleMessage::Status /* status */)
00304 {
00305   deleteTransaction(incidence);
00306   return false;
00307 }
00308 
00309 bool Scheduler::acceptCancel(IncidenceBase *incidence,ScheduleMessage::Status /* status */)
00310 {
00311   bool ret = false;
00312   const IncidenceBase *toDelete = mCalendar->incidenceFromSchedulingID( incidence->uid() );
00313   if ( !toDelete ) {
00314       Incidence *inc = static_cast<Incidence *>(incidence);
00315       if ( inc->revision() > 0 ) // otherwise we are simply denying an initial request
00316         KMessageBox::error( 0,
00317               i18n("The event, task or journal to be canceled could not be found. "
00318                   "Maybe it has already been deleted, or the calendar that "
00319                   "contains it is disabled." ) );
00320   } else {
00321     Event *even = mCalendar->event(toDelete->uid());
00322     if (even) {
00323       mCalendar->deleteEvent(even);
00324       ret = true;
00325     } else {
00326       Todo *todo = mCalendar->todo(toDelete->uid());
00327       if (todo) {
00328         mCalendar->deleteTodo(todo);
00329         ret = true;
00330       }
00331     }
00332   }
00333   deleteTransaction(incidence);
00334   return ret;
00335 }
00336 
00337 bool Scheduler::acceptDeclineCounter(IncidenceBase *incidence,ScheduleMessage::Status /* status */)
00338 {
00339   deleteTransaction(incidence);
00340   return false;
00341 }
00342 
00343 //bool Scheduler::acceptFreeBusy(Incidence *incidence,ScheduleMessage::Status status)
00344 //{
00345 //  deleteTransaction(incidence);
00346 //  return false;
00347 //}
00348 
00349 bool Scheduler::acceptReply(IncidenceBase *incidence,ScheduleMessage::Status /* status */, Method method)
00350 {
00351   if(incidence->type()=="FreeBusy") {
00352     return acceptFreeBusy(incidence, method);
00353   }
00354   bool ret = false;
00355   Event *ev = mCalendar->event(incidence->uid());
00356   Todo *to = mCalendar->todo(incidence->uid());
00357   if (ev || to) {
00358     //get matching attendee in calendar
00359     kdDebug(5800) << "Scheduler::acceptTransaction match found!" << endl;
00360     Attendee::List attendeesIn = incidence->attendees();
00361     Attendee::List attendeesEv;
00362     if (ev) attendeesEv = ev->attendees();
00363     if (to) attendeesEv = to->attendees();
00364     Attendee::List::ConstIterator inIt;
00365     Attendee::List::ConstIterator evIt;
00366     for ( inIt = attendeesIn.begin(); inIt != attendeesIn.end(); ++inIt ) {
00367       Attendee *attIn = *inIt;
00368       for ( evIt = attendeesEv.begin(); evIt != attendeesEv.end(); ++evIt ) {
00369         Attendee *attEv = *evIt;
00370         if (attIn->email().lower()==attEv->email().lower()) {
00371           //update attendee-info
00372           kdDebug(5800) << "Scheduler::acceptTransaction update attendee" << endl;
00373           attEv->setStatus(attIn->status());
00374           ret = true;
00375         }
00376       }
00377     }
00378     if ( !ret ) {
00379       // No attendees found so far, so accept them as new optional attendees
00380       for ( inIt = attendeesIn.begin(); inIt != attendeesIn.end(); ++inIt ) {
00381         Attendee* old = (*inIt);
00382         Attendee* a = new Attendee( old->name(), old->email(), false,
00383                                     old->status(), Attendee::OptParticipant );
00384         if ( ev )
00385           ev->addAttendee( a, false );
00386         else
00387           to->addAttendee( a, false );
00388         ret = true;
00389       }
00390     }
00391 
00392     if ( ret ) {
00393       // We set at least one of the attendees, so the incidence changed
00394       // Note: This should not result in a sequence number bump
00395       if ( ev )
00396         ev->updated();
00397       else if ( to )
00398         to->updated();
00399     }
00400     if ( to ) {
00401       // for VTODO a REPLY can be used to update the completion status of
00402       // a task. see RFC2446 3.4.3
00403       Todo *update = dynamic_cast<Todo*> ( incidence );
00404       Q_ASSERT( update );
00405       if ( update && ( to->percentComplete() != update->percentComplete() ) ) {
00406         to->setPercentComplete( update->percentComplete() );
00407         to->updated();
00408       }
00409     }
00410   } else
00411     kdError(5800) << "No incidence for scheduling\n";
00412   if (ret) deleteTransaction(incidence);
00413   return ret;
00414 }
00415 
00416 bool Scheduler::acceptRefresh(IncidenceBase *incidence,ScheduleMessage::Status /* status */)
00417 {
00418   // handled in korganizer's IncomingDialog
00419   deleteTransaction(incidence);
00420   return false;
00421 }
00422 
00423 bool Scheduler::acceptCounter(IncidenceBase *incidence,ScheduleMessage::Status /* status */)
00424 {
00425   deleteTransaction(incidence);
00426   return false;
00427 }
00428 
00429 bool Scheduler::acceptFreeBusy(IncidenceBase *incidence, Method method)
00430 {
00431   if ( !d->mFreeBusyCache ) {
00432     kdError() << "KCal::Scheduler: no FreeBusyCache." << endl;
00433     return false;
00434   }
00435 
00436   FreeBusy *freebusy = static_cast<FreeBusy *>(incidence);
00437 
00438   kdDebug(5800) << "acceptFreeBusy:: freeBusyDirName: " << freeBusyDir() << endl;
00439 
00440   Person from;
00441   if(method == Scheduler::Publish) {
00442     from = freebusy->organizer();
00443   }
00444   if((method == Scheduler::Reply) && (freebusy->attendeeCount() == 1)) {
00445     Attendee *attendee = freebusy->attendees().first();
00446     from = attendee->email();
00447   }
00448 
00449   if ( !d->mFreeBusyCache->saveFreeBusy( freebusy, from ) ) return false;
00450 
00451   deleteTransaction(incidence);
00452   return true;
00453 }
KDE Logo
This file is part of the documentation for libkcal Library Version 3.3.2.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Thu Aug 2 09:52:31 2007 by doxygen 1.4.2 written by Dimitri van Heesch, © 1997-2003