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