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