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