• Skip to content
  • Skip to link menu
KDE 4.1 API Reference
  • KDE API Reference
  • KDE-PIM Libraries
  • Sitemap
  • Contact Us
 

KCal Library

scheduler.cpp

00001 /*
00002   This file is part of the kcal library.
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 "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 //@cond PRIVATE
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 //@endcond
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 //@cond PRIVATE
00105 struct KCal::Scheduler::Private
00106 {
00107   Private()
00108     : mFreeBusyCache( 0 )
00109     {
00110     }
00111     FreeBusyCache *mFreeBusyCache;
00112 };
00113 //@endcond
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 /* status */)
00264 {
00265   if ( newIncBase->type() == "FreeBusy" ) {
00266     // reply to this request is handled in korganizer's incomingdialog
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       // Move the uid to be the schedulingID and make a unique UID
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 /* status */)
00296 {
00297   deleteTransaction(incidence);
00298   return false;
00299 }
00300 
00301 bool Scheduler::acceptCancel( IncidenceBase *incidence, ScheduleMessage::Status /* 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   // try harder to find the correct incidence
00343   if ( !ev && !to ) {
00344     const Incidence::List list = mCalendar->incidences();
00345     for ( Incidence::List::ConstIterator it = list.begin(), end = list.end(); it != end; ++it ) {
00346       if ( (*it)->schedulingID() == incidence->uid() ) {
00347         ev = dynamic_cast<Event*>( *it );
00348         to = dynamic_cast<Todo*>( *it );
00349         break;
00350       }
00351     }
00352   }
00353 
00354   if ( ev || to ) {
00355     //get matching attendee in calendar
00356     kDebug() << "match found!";
00357     Attendee::List attendeesIn = incidence->attendees();
00358     Attendee::List attendeesEv;
00359     Attendee::List attendeesNew;
00360     if ( ev ) {
00361       attendeesEv = ev->attendees();
00362     }
00363     if ( to ) {
00364       attendeesEv = to->attendees();
00365     }
00366     Attendee::List::ConstIterator inIt;
00367     Attendee::List::ConstIterator evIt;
00368     for ( inIt = attendeesIn.begin(); inIt != attendeesIn.end(); ++inIt ) {
00369       Attendee *attIn = *inIt;
00370       bool found = false;
00371       for ( evIt = attendeesEv.begin(); evIt != attendeesEv.end(); ++evIt ) {
00372         Attendee *attEv = *evIt;
00373         if ( attIn->email().toLower() == attEv->email().toLower() ) {
00374           //update attendee-info
00375           kDebug() << "update attendee";
00376           attEv->setStatus( attIn->status() );
00377           attEv->setDelegate( attIn->delegate() );
00378           attEv->setDelegator( attIn->delegator() );
00379           ret = true;
00380           found = true;
00381         }
00382       }
00383       if ( !found && attIn->status() != Attendee::Declined ) {
00384         attendeesNew.append( attIn );
00385       }
00386     }
00387 
00388     bool attendeeAdded = false;
00389     for ( Attendee::List::ConstIterator it = attendeesNew.constBegin();
00390           it != attendeesNew.constEnd(); ++it ) {
00391       Attendee *attNew = *it;
00392       QString msg =
00393         i18nc( "@info", "%1 wants to attend %2 but was not invited.",
00394                attNew->fullName(),
00395                ( ev ? ev->summary() : to->summary() ) );
00396       if ( !attNew->delegator().isEmpty() ) {
00397         msg = i18nc( "@info", "%1 wants to attend %2 on behalf of %3.",
00398                      attNew->fullName(),
00399                      ( ev ? ev->summary() : to->summary() ), attNew->delegator() );
00400       }
00401       if ( KMessageBox::questionYesNo(
00402              0, msg, i18nc( "@title", "Uninvited attendee" ),
00403              KGuiItem( i18nc( "@option", "Accept Attendance" ) ),
00404              KGuiItem( i18nc( "@option", "Reject Attendance" ) ) ) != KMessageBox::Yes ) {
00405         KCal::Incidence *cancel = dynamic_cast<Incidence*>( incidence );
00406         if ( cancel ) {
00407           cancel->addComment(
00408             i18nc( "@info",
00409                    "The organizer rejected your attendance at this meeting." ) );
00410         }
00411         performTransaction( cancel ? cancel : incidence, iTIPCancel, attNew->fullName() );
00412         // ### can't delete cancel here because it is aliased to incidence which
00413         // is accessed in the next loop iteration (CID 4232)
00414         // delete cancel;
00415         continue;
00416       }
00417 
00418       Attendee *a = new Attendee( attNew->name(), attNew->email(), attNew->RSVP(),
00419                                   attNew->status(), attNew->role(), attNew->uid() );
00420       a->setDelegate( attNew->delegate() );
00421       a->setDelegator( attNew->delegator() );
00422       if ( ev ) {
00423         ev->addAttendee( a );
00424       } else if ( to ) {
00425         to->addAttendee( a );
00426       }
00427       ret = true;
00428       attendeeAdded = true;
00429     }
00430 
00431     // send update about new participants
00432     if ( attendeeAdded ) {
00433       if ( ev ) {
00434         ev->setRevision( ev->revision() + 1 );
00435         performTransaction( ev, iTIPRequest );
00436       }
00437       if ( to ) {
00438         to->setRevision( to->revision() + 1 );
00439         performTransaction( to, iTIPRequest );
00440       }
00441     }
00442 
00443     if ( ret ) {
00444       // We set at least one of the attendees, so the incidence changed
00445       // Note: This should not result in a sequence number bump
00446       if ( ev ) {
00447         ev->updated();
00448       } else if ( to ) {
00449         to->updated();
00450       }
00451     }
00452     if ( to ) {
00453       // for VTODO a REPLY can be used to update the completion status of
00454       // a to-do. see RFC2446 3.4.3
00455       Todo *update = dynamic_cast<Todo*> ( incidence );
00456       Q_ASSERT( update );
00457       if ( update && ( to->percentComplete() != update->percentComplete() ) ) {
00458         to->setPercentComplete( update->percentComplete() );
00459         to->updated();
00460       }
00461     }
00462   } else {
00463     kError(5800) << "No incidence for scheduling\n";
00464   }
00465 
00466   if ( ret ) {
00467     deleteTransaction( incidence );
00468   }
00469   return ret;
00470 }
00471 
00472 bool Scheduler::acceptRefresh( IncidenceBase *incidence, ScheduleMessage::Status status )
00473 {
00474   Q_UNUSED( status );
00475   // handled in korganizer's IncomingDialog
00476   deleteTransaction( incidence );
00477   return false;
00478 }
00479 
00480 bool Scheduler::acceptCounter( IncidenceBase *incidence, ScheduleMessage::Status status )
00481 {
00482   Q_UNUSED( status );
00483   deleteTransaction( incidence );
00484   return false;
00485 }
00486 
00487 bool Scheduler::acceptFreeBusy( IncidenceBase *incidence, iTIPMethod method )
00488 {
00489   if ( !d->mFreeBusyCache ) {
00490     kError() << "KCal::Scheduler: no FreeBusyCache.";
00491     return false;
00492   }
00493 
00494   FreeBusy *freebusy = static_cast<FreeBusy *>(incidence);
00495 
00496   kDebug() << "freeBusyDirName:" << freeBusyDir();
00497 
00498   Person from;
00499   if( method == iTIPPublish ) {
00500     from = freebusy->organizer();
00501   }
00502   if ( ( method == iTIPReply ) && ( freebusy->attendeeCount() == 1 ) ) {
00503     Attendee *attendee = freebusy->attendees().first();
00504     from.setName( attendee->name() );
00505     from.setEmail( attendee->email() );
00506   }
00507 
00508   if ( !d->mFreeBusyCache->saveFreeBusy( freebusy, from ) ) {
00509     return false;
00510   }
00511 
00512   deleteTransaction( incidence );
00513   return true;
00514 }

KCal Library

Skip menu "KCal Library"
  • Main Page
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

KDE-PIM Libraries

Skip menu "KDE-PIM Libraries"
  • akonadi
  • kabc
  • kblog
  • kcal
  • kimap
  • kioslave
  •   imap4
  •   mbox
  • kldap
  • kmime
  • kpimidentities
  • kpimutils
  • kresources
  • ktnef
  • kxmlrpcclient
  • mailtransport
  • qgpgme
  • syndication
  •   atom
  •   rdf
  •   rss2
Generated for KDE-PIM Libraries by doxygen 1.5.7.1
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal