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