• Skip to content
  • Skip to link menu
KDE 4.0 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(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 /* status */)
00265 {
00266   if ( newIncBase->type() == "FreeBusy" ) {
00267     // reply to this request is handled in korganizer's incomingdialog
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       // Move the uid to be the schedulingID and make a unique UID
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 /* status */)
00297 {
00298   deleteTransaction(incidence);
00299   return false;
00300 }
00301 
00302 bool Scheduler::acceptCancel( IncidenceBase *incidence, ScheduleMessage::Status /* 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   // try harder to find the correct incidence
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     //get matching attendee in calendar
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           //update attendee-info
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     // send update about new participants
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       // We set at least one of the attendees, so the incidence changed
00444       // Note: This should not result in a sequence number bump
00445       if ( ev ) {
00446         ev->updated();
00447       } else if ( to ) {
00448         to->updated();
00449       }
00450     }
00451     if ( to ) {
00452       // for VTODO a REPLY can be used to update the completion status of
00453       // a to-do. see RFC2446 3.4.3
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   // handled in korganizer's IncomingDialog
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 }

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"
  • 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.5
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