korganizer

incidencechanger.cpp

00001 /*
00002     This file is part of KOrganizer.
00003 
00004     Copyright (c) 2004 Reinhold Kainhofer <reinhold@kainhofer.com>
00005 
00006     This program is free software; you can redistribute it and/or modify
00007     it under the terms of the GNU General Public License as published by
00008     the Free Software Foundation; either version 2 of the License, or
00009     (at your option) any later version.
00010 
00011     This program 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
00014     GNU General Public License for more details.
00015 
00016     You should have received a copy of the GNU General Public License
00017     along with this program; if not, write to the Free Software
00018     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00019 
00020     As a special exception, permission is given to link this program
00021     with any edition of Qt, and distribute the resulting executable,
00022     without including the source code for Qt in the source distribution.
00023 */
00024 
00025 #include "incidencechanger.h"
00026 #include "koglobals.h"
00027 #include "koprefs.h"
00028 #include "kogroupware.h"
00029 #include "mailscheduler.h"
00030 
00031 #include <libkcal/freebusy.h>
00032 #include <libkcal/dndfactory.h>
00033 #include <kdebug.h>
00034 #include <kmessagebox.h>
00035 #include <klocale.h>
00036 
00037 
00038 bool IncidenceChanger::beginChange( Incidence *incidence,
00039                                     ResourceCalendar *res, const QString &subRes )
00040 {
00041   if ( !incidence ) {
00042     return false;
00043   }
00044 
00045   kdDebug(5850) << "IncidenceChanger::beginChange for incidence \""
00046                 << incidence->summary() << "\"" << endl;
00047 
00048   CalendarResources *calRes = dynamic_cast<CalendarResources*>( mCalendar );
00049   if ( !calRes ) {
00050     return false;
00051   }
00052 
00053   return calRes->beginChange( incidence, res, subRes );
00054 }
00055 
00056 bool IncidenceChanger::sendGroupwareMessage( Incidence *incidence,
00057                                              KCal::Scheduler::Method method,
00058                                              KOGlobals::HowChanged action,
00059                                              QWidget *parent )
00060 {
00061   if ( KOPrefs::instance()->thatIsMe( incidence->organizer().email() ) &&
00062        ( incidence->attendeeCount() > 0 ) &&
00063        !KOPrefs::instance()->mUseGroupwareCommunication ) {
00064     emit schedule( method, incidence );
00065     return true;
00066   } else if( KOPrefs::instance()->mUseGroupwareCommunication ) {
00067     return
00068       KOGroupware::instance()->sendICalMessage( parent, method, incidence, 0, action, false );
00069   }
00070   return true;
00071 }
00072 
00073 bool IncidenceChanger::endChange( Incidence *incidence,
00074                                   ResourceCalendar *res, const QString &subRes )
00075 {
00076   // FIXME: if that's a groupware incidence, and I'm not the organizer,
00077   // send out a mail to the organizer with a counterproposal instead
00078   // of actually changing the incidence. Then no locking is needed.
00079   // FIXME: if that's a groupware incidence, and the incidence was
00080   // never locked, we can't unlock it with endChange().
00081 
00082   if ( !incidence ) {
00083     return false;
00084   }
00085 
00086   kdDebug(5850) << "IncidenceChanger::endChange for incidence \""
00087                 << incidence->summary() << "\"" << incidence->dtStart() << endl;
00088 
00089   CalendarResources *calRes = dynamic_cast<CalendarResources*>( mCalendar );
00090   if ( !calRes ) {
00091     kdDebug() << "CalRes is null!" << endl;
00092     return false;
00093   }
00094 
00095   return calRes->endChange( incidence, res, subRes );
00096 }
00097 
00098 bool IncidenceChanger::deleteIncidence( Incidence *incidence, QWidget *parent )
00099 {
00100   if ( !incidence ) return true;
00101 kdDebug(5850)<<"IncidenceChanger::deleteIncidence for incidence \""<<incidence->summary()<<"\""<<endl;
00102   bool doDelete = sendGroupwareMessage( incidence, KCal::Scheduler::Cancel,
00103                                         KOGlobals::INCIDENCEDELETED, parent );
00104   if( doDelete ) {
00105     // @TODO: let Calendar::deleteIncidence do the locking...
00106     Incidence* tmp = incidence->clone();
00107     emit incidenceToBeDeleted( incidence );
00108     doDelete = mCalendar->deleteIncidence( incidence );
00109     if ( !KOPrefs::instance()->thatIsMe( tmp->organizer().email() ) ) {
00110       const QStringList myEmails = KOPrefs::instance()->allEmails();
00111       bool notifyOrganizer = false;
00112       for ( QStringList::ConstIterator it = myEmails.begin(); it != myEmails.end(); ++it ) {
00113         QString email = *it;
00114         Attendee *me = tmp->attendeeByMail(email);
00115         if ( me ) {
00116           if ( me->status() == KCal::Attendee::Accepted || me->status() == KCal::Attendee::Delegated )
00117             notifyOrganizer = true;
00118           Attendee *newMe = new Attendee( *me );
00119           newMe->setStatus( KCal::Attendee::Declined );
00120           tmp->clearAttendees();
00121           tmp->addAttendee( newMe );
00122           break;
00123         }
00124       }
00125 
00126       if ( !KOGroupware::instance()->doNotNotify() && notifyOrganizer ) {
00127           KCal::MailScheduler scheduler( mCalendar );
00128           scheduler.performTransaction( tmp, Scheduler::Reply );
00129       }
00130       //reset the doNotNotify flag
00131       KOGroupware::instance()->setDoNotNotify( false );
00132     }
00133     emit incidenceDeleted( incidence );
00134   }
00135   return doDelete;
00136 }
00137 
00138 bool IncidenceChanger::cutIncidences( const Incidence::List &incidences,
00139                                       QWidget *parent )
00140 {
00141   Incidence::List::ConstIterator it;
00142   bool doDelete = true;
00143   Incidence::List incsToCut;
00144   for ( it = incidences.constBegin(); it != incidences.constEnd(); ++it ) {
00145     if ( *it ) {
00146       doDelete = sendGroupwareMessage( *it, KCal::Scheduler::Cancel,
00147                                        KOGlobals::INCIDENCEDELETED, parent );
00148       if ( doDelete ) {
00149         emit incidenceToBeDeleted( *it );
00150         incsToCut.append( *it );
00151       }
00152     }
00153   }
00154 
00155   DndFactory factory( mCalendar );
00156 
00157   if ( factory.cutIncidences( incsToCut ) ) {
00158     for ( it = incsToCut.constBegin(); it != incsToCut.constEnd(); ++it ) {
00159       emit incidenceDeleted( *it );
00160     }
00161     return !incsToCut.isEmpty();
00162   } else {
00163     return false;
00164   }
00165 }
00166 
00167 bool IncidenceChanger::cutIncidence( Incidence *incidence, QWidget *parent )
00168 {
00169   Incidence::List incidences;
00170   incidences.append( incidence );
00171   return cutIncidences( incidences, parent );
00172 }
00173 
00174 class IncidenceChanger::ComparisonVisitor : public IncidenceBase::Visitor
00175 {
00176   public:
00177     ComparisonVisitor() {}
00178     bool act( IncidenceBase *incidence, IncidenceBase *inc2 )
00179     {
00180       mIncidence2 = inc2;
00181       if ( incidence )
00182         return incidence->accept( *this );
00183       else
00184         return (inc2 == 0);
00185     }
00186   protected:
00187     bool visit( Event *event )
00188     {
00189       Event *ev2 = dynamic_cast<Event*>(mIncidence2);
00190       if ( event && ev2 ) {
00191         return *event == *ev2;
00192       } else {
00193         // either both 0, or return false;
00194         return ( ev2 == event );
00195       }
00196     }
00197     bool visit( Todo *todo )
00198     {
00199       Todo *to2 = dynamic_cast<Todo*>( mIncidence2 );
00200       if ( todo && to2 ) {
00201         return *todo == *to2;
00202       } else {
00203         // either both 0, or return false;
00204         return ( todo == to2 );
00205       }
00206     }
00207     bool visit( Journal *journal )
00208     {
00209       Journal *j2 = dynamic_cast<Journal*>( mIncidence2 );
00210       if ( journal && j2 ) {
00211         return *journal == *j2;
00212       } else {
00213         // either both 0, or return false;
00214         return ( journal == j2 );
00215       }
00216     }
00217     bool visit( FreeBusy *fb )
00218     {
00219       FreeBusy *fb2 = dynamic_cast<FreeBusy*>( mIncidence2 );
00220       if ( fb && fb2 ) {
00221         return *fb == *fb2;
00222       } else {
00223         // either both 0, or return false;
00224         return ( fb2 == fb );
00225       }
00226     }
00227 
00228   protected:
00229     IncidenceBase *mIncidence2;
00230 };
00231 
00232 class IncidenceChanger::AssignmentVisitor : public IncidenceBase::Visitor
00233 {
00234   public:
00235     AssignmentVisitor() {}
00236     bool act( IncidenceBase *incidence, IncidenceBase *inc2 )
00237     {
00238       mIncidence2 = inc2;
00239       if ( incidence )
00240         return incidence->accept( *this );
00241       else
00242         return false;
00243     }
00244   protected:
00245     bool visit( Event *event )
00246     {
00247       Event *ev2 = dynamic_cast<Event*>( mIncidence2 );
00248       if ( event && ev2 ) {
00249         *event = *ev2;
00250         return true;
00251       } else {
00252         return false;
00253       }
00254     }
00255     bool visit( Todo *todo )
00256     {
00257       Todo *to2 = dynamic_cast<Todo*>( mIncidence2 );
00258       if ( todo && to2 ) {
00259         *todo = *to2;
00260         return true;
00261       } else {
00262         return false;
00263       }
00264     }
00265     bool visit( Journal *journal )
00266     {
00267       Journal *j2 = dynamic_cast<Journal*>(mIncidence2);
00268       if ( journal && j2 ) {
00269         *journal = *j2;
00270         return true;
00271       } else {
00272         return false;
00273       }
00274     }
00275     bool visit( FreeBusy *fb )
00276     {
00277       FreeBusy *fb2 = dynamic_cast<FreeBusy*>( mIncidence2 );
00278       if ( fb && fb2 ) {
00279         *fb = *fb2;
00280         return true;
00281       } else {
00282         return false;
00283       }
00284     }
00285 
00286   protected:
00287     IncidenceBase *mIncidence2;
00288 };
00289 
00290 bool IncidenceChanger::incidencesEqual( Incidence *inc1, Incidence *inc2 )
00291 {
00292   ComparisonVisitor v;
00293   return ( v.act( inc1, inc2 ) );
00294 }
00295 
00296 bool IncidenceChanger::assignIncidence( Incidence *inc1, Incidence *inc2 )
00297 {
00298   AssignmentVisitor v;
00299   return v.act( inc1, inc2 );
00300 }
00301 
00302 bool IncidenceChanger::myAttendeeStatusChanged( Incidence *oldInc, Incidence *newInc )
00303 {
00304   Attendee *oldMe = oldInc->attendeeByMails( KOPrefs::instance()->allEmails() );
00305   Attendee *newMe = newInc->attendeeByMails( KOPrefs::instance()->allEmails() );
00306   if ( oldMe && newMe && ( oldMe->status() != newMe->status() ) )
00307     return true;
00308 
00309   return false;
00310 }
00311 
00312 bool IncidenceChanger::changeIncidence( Incidence *oldinc, Incidence *newinc,
00313                                         KOGlobals::WhatChanged action,
00314                                         QWidget *parent,
00315                                         bool useLastDialogAnswer )
00316 {
00317 kdDebug(5850)<<"IncidenceChanger::changeIncidence for incidence \""<<newinc->summary()<<"\" ( old one was \""<<oldinc->summary()<<"\")"<<endl;
00318   if ( incidencesEqual( newinc, oldinc ) ) {
00319     // Don't do anything
00320     kdDebug(5850) << "Incidence not changed\n";
00321   } else {
00322     kdDebug(5850) << "Incidence changed\n";
00323     bool attendeeStatusChanged = myAttendeeStatusChanged( oldinc, newinc );
00324     int revision = newinc->revision();
00325     newinc->setRevision( revision + 1 );
00326     // FIXME: Use a generic method for this! Ideally, have an interface class
00327     //        for group cheduling. Each implementation could then just do what
00328     //        it wants with the event. If no groupware is used,use the null
00329     //        pattern...
00330     bool success = true;
00331     if ( KOPrefs::instance()->mUseGroupwareCommunication ) {
00332       success = KOGroupware::instance()->sendICalMessage(
00333         parent,
00334         KCal::Scheduler::Request,
00335         newinc, oldinc,
00336         KOGlobals::INCIDENCEEDITED, attendeeStatusChanged,
00337         useLastDialogAnswer );
00338     }
00339 
00340     if ( success ) {
00341       // Accept the event changes
00342       emit incidenceChanged( oldinc, newinc, action );
00343     } else {
00344       // revert changes
00345       assignIncidence( newinc, oldinc );
00346       return false;
00347     }
00348   }
00349   return true;
00350 }
00351 
00352 bool IncidenceChanger::addIncidence( Incidence *incidence,
00353                                      ResourceCalendar *res, const QString &subRes,
00354                                      QWidget *parent, bool useLastDialogAnswer )
00355 {
00356   CalendarResources *stdcal = dynamic_cast<CalendarResources *>( mCalendar );
00357   if ( stdcal && !stdcal->hasCalendarResources() ) {
00358     KMessageBox::sorry(
00359       parent,
00360       i18n( "No calendars found, unable to save %1 \"%2\"." ).
00361       arg( i18n( incidence->type() ) ).
00362       arg( incidence->summary() ) );
00363     kdDebug(5850) << "IncidenceChanger: No calendars found" << endl;
00364     return false;
00365   }
00366 
00367   // FIXME: This is a nasty hack, since we need to set a parent for the
00368   //        resource selection dialog. However, we don't have any UI methods
00369   //        in the calendar, only in the CalendarResources::DestinationPolicy
00370   //        So we need to type-cast it and extract it from the CalendarResources
00371   QWidget *tmpparent = 0;
00372   if ( stdcal ) {
00373     tmpparent = stdcal->dialogParentWidget();
00374     stdcal->setDialogParentWidget( parent );
00375   }
00376 
00377   // If a ResourceCalendar isn't provided, then try to compute one
00378   // along with any subResource from the incidence.
00379   ResourceCalendar *pRes = res;
00380   QString pSubRes = subRes;
00381   QString pResName;
00382   if ( !pRes ) {
00383     if ( stdcal ) {
00384       pRes = stdcal->resource( incidence );
00385       if ( pRes ) {
00386         pResName = pRes->resourceName();
00387         if ( pRes->canHaveSubresources() ) {
00388           pSubRes = pRes->subresourceIdentifier( incidence );
00389           pResName = pRes->labelForSubresource( pSubRes );
00390         }
00391       }
00392     }
00393   }
00394 
00395   bool success = false;
00396   if ( stdcal && pRes && !pRes->readOnly() && pRes->subresourceWritable( pSubRes ) ) {
00397     success = stdcal->addIncidence( incidence, pRes, pSubRes );
00398   } else {
00399     success = mCalendar->addIncidence( incidence );
00400   }
00401 
00402   if ( !success ) {
00403     // We can have a failure if the user pressed [cancel] in the resource
00404     // selectdialog, so check the exception.
00405     ErrorFormat *e = stdcal ? stdcal->exception() : 0;
00406     if ( !e ||
00407          ( e && ( e->errorCode() != KCal::ErrorFormat::UserCancel &&
00408                   e->errorCode() != KCal::ErrorFormat::NoWritableFound ) ) ) {
00409       QString errMessage;
00410       if ( pResName.isEmpty() ) {
00411         errMessage = i18n( "Unable to save %1 \"%2\"." ).
00412                      arg( i18n( incidence->type() ) ).
00413                      arg( incidence->summary() );
00414       } else {
00415         errMessage = i18n( "Unable to save %1 \"%2\" to calendar %3." ).
00416                      arg( i18n( incidence->type() ) ).
00417                      arg( incidence->summary() ).
00418                      arg( pResName );
00419       }
00420       KMessageBox::sorry( parent, errMessage );
00421     }
00422     kdDebug(5850) << "IncidenceChanger: Can't add incidence" << endl;
00423     return false;
00424   }
00425 
00426   if ( KOPrefs::instance()->mUseGroupwareCommunication ) {
00427     if ( !KOGroupware::instance()->sendICalMessage(
00428            parent,
00429            KCal::Scheduler::Request,
00430            incidence, 0, KOGlobals::INCIDENCEADDED, false, useLastDialogAnswer ) ) {
00431       KMessageBox::sorry(
00432         parent,
00433         i18n( "Attempt to send the scheduling message failed. "
00434               "Please check your Group Scheduling settings. "
00435               "Contact your system administrator for more help.") );
00436     }
00437   }
00438 
00439   emit incidenceAdded( incidence );
00440   return true;
00441 }
00442 
00443 
00444 #include "incidencechanger.moc"
00445 #include "incidencechangerbase.moc"
KDE Home | KDE Accessibility Home | Description of Access Keys