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 bool IncidenceChanger::beginChange( Incidence *incidence,
00038                                     ResourceCalendar *res, const QString &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                                              QWidget *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 QString &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, QWidget *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 QStringList myEmails = KOPrefs::instance()->allEmails();
00127       bool notifyOrganizer = false;
00128       for ( QStringList::ConstIterator it = myEmails.begin(); it != myEmails.end(); ++it ) {
00129         QString 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                                       QWidget *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, QWidget *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                                         QWidget *parent )
00331 {
00332 kdDebug(5850)<<"IncidenceChanger::changeIncidence for incidence \""<<newinc->summary()<<"\" ( old one was \""<<oldinc->summary()<<"\")"<<endl;
00333   if ( incidencesEqual( newinc, oldinc ) ) {
00334     // Don't do anything
00335     kdDebug(5850) << "Incidence not changed\n";
00336   } else {
00337     kdDebug(5850) << "Incidence changed\n";
00338     bool attendeeStatusChanged = myAttendeeStatusChanged( oldinc, newinc );
00339     int revision = newinc->revision();
00340     newinc->setRevision( revision + 1 );
00341     // FIXME: Use a generic method for this! Ideally, have an interface class
00342     //        for group cheduling. Each implementation could then just do what
00343     //        it wants with the event. If no groupware is used,use the null
00344     //        pattern...
00345     bool success = true;
00346     if ( KOPrefs::instance()->mUseGroupwareCommunication ) {
00347       success = KOGroupware::instance()->sendICalMessage(
00348         parent,
00349         KCal::Scheduler::Request,
00350         newinc, KOGlobals::INCIDENCEEDITED, attendeeStatusChanged );
00351     }
00352 
00353     if ( success ) {
00354       // Accept the event changes
00355       emit incidenceChanged( oldinc, newinc, action );
00356     } else {
00357       // revert changes
00358       assignIncidence( newinc, oldinc );
00359       return false;
00360     }
00361   }
00362   return true;
00363 }
00364 
00365 bool IncidenceChanger::addIncidence( Incidence *incidence,
00366                                      ResourceCalendar *res, const QString &subRes,
00367                                      QWidget *parent )
00368 {
00369   CalendarResources *stdcal = dynamic_cast<CalendarResources *>( mCalendar );
00370   if( stdcal && !stdcal->hasCalendarResources() ) {
00371     KMessageBox::sorry(
00372       parent,
00373       i18n( "No calendars found, unable to save %1 \"%2\"." ).
00374       arg( i18n( incidence->type() ) ).
00375       arg( incidence->summary() ) );
00376     kdDebug(5850) << "IncidenceChanger: No calendars found" << endl;
00377     return false;
00378   }
00379 
00380   // FIXME: This is a nasty hack, since we need to set a parent for the
00381   //        resource selection dialog. However, we don't have any UI methods
00382   //        in the calendar, only in the CalendarResources::DestinationPolicy
00383   //        So we need to type-cast it and extract it from the CalendarResources
00384   QWidget *tmpparent = 0;
00385   if ( stdcal ) {
00386     tmpparent = stdcal->dialogParentWidget();
00387     stdcal->setDialogParentWidget( parent );
00388   }
00389 
00390   // If a ResourceCalendar isn't provided, then try to compute one
00391   // along with any subResource from the incidence.
00392   ResourceCalendar *pRes = res;
00393   QString pSubRes = subRes;
00394   QString pResName;
00395   if ( !pRes ) {
00396     if ( stdcal ) {
00397       pRes = stdcal->resource( incidence );
00398       if ( pRes ) {
00399         pResName = pRes->resourceName();
00400         if ( pRes->canHaveSubresources() ) {
00401           pSubRes = pRes->subresourceIdentifier( incidence );
00402           pResName = pRes->labelForSubresource( pSubRes );
00403         }
00404       }
00405     }
00406   }
00407 
00408   bool success = false;
00409   if ( stdcal && pRes && !pRes->readOnly() && pRes->subresourceWritable( pSubRes ) ) {
00410     success = stdcal->addIncidence( incidence, pRes, pSubRes );
00411   } else {
00412     success = mCalendar->addIncidence( incidence );
00413   }
00414 
00415   if ( !success ) {
00416     // We can have a failure if the user pressed [cancel] in the resource
00417     // selectdialog, so check the exception.
00418     ErrorFormat *e = stdcal ? stdcal->exception() : 0;
00419     if ( !e ||
00420          ( e && ( e->errorCode() != KCal::ErrorFormat::UserCancel &&
00421                   e->errorCode() != KCal::ErrorFormat::NoWritableFound ) ) ) {
00422       QString errMessage;
00423       if ( pResName.isEmpty() ) {
00424         errMessage = i18n( "Unable to save %1 \"%2\"." ).
00425                      arg( i18n( incidence->type() ) ).
00426                      arg( incidence->summary() );
00427       } else {
00428         errMessage = i18n( "Unable to save %1 \"%2\" to calendar %3." ).
00429                      arg( i18n( incidence->type() ) ).
00430                      arg( incidence->summary() ).
00431                      arg( pResName );
00432       }
00433       KMessageBox::sorry( parent, errMessage );
00434     }
00435     kdDebug(5850) << "IncidenceChanger: Can't add incidence" << endl;
00436     return false;
00437   }
00438 
00439   if ( KOPrefs::instance()->mUseGroupwareCommunication ) {
00440     if ( !KOGroupware::instance()->sendICalMessage(
00441            parent,
00442            KCal::Scheduler::Request,
00443            incidence, KOGlobals::INCIDENCEADDED, false ) ) {
00444       KMessageBox::sorry(
00445         parent,
00446         i18n( "Attempt to send the scheduling message failed. "
00447               "Please check your Group Scheduling settings. "
00448               "Contact your system administrator for more help.") );
00449     }
00450   }
00451 
00452   emit incidenceAdded( incidence );
00453   return true;
00454 }
00455 
00456 
00457 #include "incidencechanger.moc"
00458 #include "incidencechangerbase.moc"
KDE Home | KDE Accessibility Home | Description of Access Keys