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 bool IncidenceChanger::beginChange( Incidence *incidence,
00039                                     ResourceCalendar *res, const QString &subRes )
00040 {
00041   if ( !incidence ) {
00042     return false;
00043   }
00044 
00045   kdDebug(5850) << "IncidenceChanger::beginChange for incidence \""
00046                 << incidence->summary() << "\"" << endl;
00047 
00048   CalendarResources *calRes = dynamic_cast<CalendarResources*>( mCalendar );
00049   if ( !calRes ) {
00050     return false;
00051   }
00052 
00053   return calRes->beginChange( incidence, res, subRes );
00054 }
00055 
00056 bool IncidenceChanger::sendGroupwareMessage( Incidence *incidence,
00057                                              KCal::Scheduler::Method method,
00058                                              KOGlobals::HowChanged action,
00059                                              QWidget *parent )
00060 {
00061   if ( KOPrefs::instance()->thatIsMe( incidence->organizer().email() ) && incidence->attendeeCount()>0
00062       && !KOPrefs::instance()->mUseGroupwareCommunication ) {
00063     emit schedule( method, incidence );
00064     return true;
00065   } else if( KOPrefs::instance()->mUseGroupwareCommunication ) {
00066     return
00067       KOGroupware::instance()->sendICalMessage( parent, method, incidence, action, false );
00068   }
00069   return true;
00070 }
00071 
00072 void IncidenceChanger::cancelAttendees( Incidence *incidence )
00073 {
00074   if ( KOPrefs::instance()->mUseGroupwareCommunication ) {
00075     if ( KMessageBox::questionYesNo( 0, i18n("Some attendees were removed "
00076        "from the incidence. Shall cancel messages be sent to these attendees?"),
00077        i18n( "Attendees Removed" ), i18n("Send Messages"), i18n("Do Not Send") ) == KMessageBox::Yes ) {
00078       // don't use KOGroupware::sendICalMessage here, because that asks just
00079       // a very general question "Other people are involved, send message to
00080       // them?", which isn't helpful at all in this situation. Afterwards, it
00081       // would only call the MailScheduler::performTransaction, so do this
00082       // manually.
00083       // FIXME: Groupware scheduling should be factored out to it's own class
00084       //        anyway
00085       KCal::MailScheduler scheduler( mCalendar );
00086       scheduler.performTransaction( incidence, Scheduler::Cancel );
00087     }
00088   }
00089 }
00090 
00091 bool IncidenceChanger::endChange( Incidence *incidence,
00092                                   ResourceCalendar *res, const QString &subRes )
00093 {
00094   // FIXME: if that's a groupware incidence, and I'm not the organizer,
00095   // send out a mail to the organizer with a counterproposal instead
00096   // of actually changing the incidence. Then no locking is needed.
00097   // FIXME: if that's a groupware incidence, and the incidence was
00098   // never locked, we can't unlock it with endChange().
00099 
00100   if ( !incidence ) {
00101     return false;
00102   }
00103 
00104   kdDebug(5850) << "IncidenceChanger::endChange for incidence \""
00105                 << incidence->summary() << "\"" << incidence->dtStart() << endl;
00106 
00107   CalendarResources *calRes = dynamic_cast<CalendarResources*>( mCalendar );
00108   if ( !calRes ) {
00109     kdDebug() << "CalRes is null!" << endl;
00110     return false;
00111   }
00112 
00113   return calRes->endChange( incidence, res, subRes );
00114 }
00115 
00116 bool IncidenceChanger::deleteIncidence( Incidence *incidence, QWidget *parent )
00117 {
00118   if ( !incidence ) return true;
00119 kdDebug(5850)<<"IncidenceChanger::deleteIncidence for incidence \""<<incidence->summary()<<"\""<<endl;
00120   bool doDelete = sendGroupwareMessage( incidence, KCal::Scheduler::Cancel,
00121                                         KOGlobals::INCIDENCEDELETED, parent );
00122   if( doDelete ) {
00123     // @TODO: let Calendar::deleteIncidence do the locking...
00124     Incidence* tmp = incidence->clone();
00125     emit incidenceToBeDeleted( incidence );
00126     doDelete = mCalendar->deleteIncidence( incidence );
00127     if ( !KOPrefs::instance()->thatIsMe( tmp->organizer().email() ) ) {
00128       const QStringList myEmails = KOPrefs::instance()->allEmails();
00129       bool notifyOrganizer = false;
00130       for ( QStringList::ConstIterator it = myEmails.begin(); it != myEmails.end(); ++it ) {
00131         QString email = *it;
00132         Attendee *me = tmp->attendeeByMail(email);
00133         if ( me ) {
00134           if ( me->status() == KCal::Attendee::Accepted || me->status() == KCal::Attendee::Delegated )
00135             notifyOrganizer = true;
00136           Attendee *newMe = new Attendee( *me );
00137           newMe->setStatus( KCal::Attendee::Declined );
00138           tmp->clearAttendees();
00139           tmp->addAttendee( newMe );
00140           break;
00141         }
00142       }
00143 
00144       if ( !KOGroupware::instance()->doNotNotify() && notifyOrganizer ) {
00145           KCal::MailScheduler scheduler( mCalendar );
00146           scheduler.performTransaction( tmp, Scheduler::Reply );
00147       }
00148       //reset the doNotNotify flag
00149       KOGroupware::instance()->setDoNotNotify( false );
00150     }
00151     emit incidenceDeleted( incidence );
00152   }
00153   return doDelete;
00154 }
00155 
00156 bool IncidenceChanger::cutIncidences( const Incidence::List &incidences,
00157                                       QWidget *parent )
00158 {
00159   Incidence::List::ConstIterator it;
00160   bool doDelete = true;
00161   Incidence::List incsToCut;
00162   for ( it = incidences.constBegin(); it != incidences.constEnd(); ++it ) {
00163     if ( *it ) {
00164       doDelete = sendGroupwareMessage( *it, KCal::Scheduler::Cancel,
00165                                        KOGlobals::INCIDENCEDELETED, parent );
00166       if ( doDelete ) {
00167         emit incidenceToBeDeleted( *it );
00168         incsToCut.append( *it );
00169       }
00170     }
00171   }
00172 
00173   DndFactory factory( mCalendar );
00174 
00175   if ( factory.cutIncidences( incsToCut ) ) {
00176     for ( it = incsToCut.constBegin(); it != incsToCut.constEnd(); ++it ) {
00177       emit incidenceDeleted( *it );
00178     }
00179     return !incsToCut.isEmpty();
00180   } else {
00181     return false;
00182   }
00183 }
00184 
00185 bool IncidenceChanger::cutIncidence( Incidence *incidence, QWidget *parent )
00186 {
00187   Incidence::List incidences;
00188   incidences.append( incidence );
00189   return cutIncidences( incidences, parent );
00190 }
00191 
00192 class IncidenceChanger::ComparisonVisitor : public IncidenceBase::Visitor
00193 {
00194   public:
00195     ComparisonVisitor() {}
00196     bool act( IncidenceBase *incidence, IncidenceBase *inc2 )
00197     {
00198       mIncidence2 = inc2;
00199       if ( incidence )
00200         return incidence->accept( *this );
00201       else
00202         return (inc2 == 0);
00203     }
00204   protected:
00205     bool visit( Event *event )
00206     {
00207       Event *ev2 = dynamic_cast<Event*>(mIncidence2);
00208       if ( event && ev2 ) {
00209         return *event == *ev2;
00210       } else {
00211         // either both 0, or return false;
00212         return ( ev2 == event );
00213       }
00214     }
00215     bool visit( Todo *todo )
00216     {
00217       Todo *to2 = dynamic_cast<Todo*>( mIncidence2 );
00218       if ( todo && to2 ) {
00219         return *todo == *to2;
00220       } else {
00221         // either both 0, or return false;
00222         return ( todo == to2 );
00223       }
00224     }
00225     bool visit( Journal *journal )
00226     {
00227       Journal *j2 = dynamic_cast<Journal*>( mIncidence2 );
00228       if ( journal && j2 ) {
00229         return *journal == *j2;
00230       } else {
00231         // either both 0, or return false;
00232         return ( journal == j2 );
00233       }
00234     }
00235     bool visit( FreeBusy *fb )
00236     {
00237       FreeBusy *fb2 = dynamic_cast<FreeBusy*>( mIncidence2 );
00238       if ( fb && fb2 ) {
00239         return *fb == *fb2;
00240       } else {
00241         // either both 0, or return false;
00242         return ( fb2 == fb );
00243       }
00244     }
00245 
00246   protected:
00247     IncidenceBase *mIncidence2;
00248 };
00249 
00250 class IncidenceChanger::AssignmentVisitor : public IncidenceBase::Visitor
00251 {
00252   public:
00253     AssignmentVisitor() {}
00254     bool act( IncidenceBase *incidence, IncidenceBase *inc2 )
00255     {
00256       mIncidence2 = inc2;
00257       if ( incidence )
00258         return incidence->accept( *this );
00259       else
00260         return false;
00261     }
00262   protected:
00263     bool visit( Event *event )
00264     {
00265       Event *ev2 = dynamic_cast<Event*>( mIncidence2 );
00266       if ( event && ev2 ) {
00267         *event = *ev2;
00268         return true;
00269       } else {
00270         return false;
00271       }
00272     }
00273     bool visit( Todo *todo )
00274     {
00275       Todo *to2 = dynamic_cast<Todo*>( mIncidence2 );
00276       if ( todo && to2 ) {
00277         *todo = *to2;
00278         return true;
00279       } else {
00280         return false;
00281       }
00282     }
00283     bool visit( Journal *journal )
00284     {
00285       Journal *j2 = dynamic_cast<Journal*>(mIncidence2);
00286       if ( journal && j2 ) {
00287         *journal = *j2;
00288         return true;
00289       } else {
00290         return false;
00291       }
00292     }
00293     bool visit( FreeBusy *fb )
00294     {
00295       FreeBusy *fb2 = dynamic_cast<FreeBusy*>( mIncidence2 );
00296       if ( fb && fb2 ) {
00297         *fb = *fb2;
00298         return true;
00299       } else {
00300         return false;
00301       }
00302     }
00303 
00304   protected:
00305     IncidenceBase *mIncidence2;
00306 };
00307 
00308 bool IncidenceChanger::incidencesEqual( Incidence *inc1, Incidence *inc2 )
00309 {
00310   ComparisonVisitor v;
00311   return ( v.act( inc1, inc2 ) );
00312 }
00313 
00314 bool IncidenceChanger::assignIncidence( Incidence *inc1, Incidence *inc2 )
00315 {
00316   AssignmentVisitor v;
00317   return v.act( inc1, inc2 );
00318 }
00319 
00320 bool IncidenceChanger::myAttendeeStatusChanged( Incidence *oldInc, Incidence *newInc )
00321 {
00322   Attendee *oldMe = oldInc->attendeeByMails( KOPrefs::instance()->allEmails() );
00323   Attendee *newMe = newInc->attendeeByMails( KOPrefs::instance()->allEmails() );
00324   if ( oldMe && newMe && ( oldMe->status() != newMe->status() ) )
00325     return true;
00326 
00327   return false;
00328 }
00329 
00330 bool IncidenceChanger::changeIncidence( Incidence *oldinc, Incidence *newinc,
00331                                         KOGlobals::WhatChanged action,
00332                                         QWidget *parent,
00333                                         bool useLastDialogAnswer )
00334 {
00335 kdDebug(5850)<<"IncidenceChanger::changeIncidence for incidence \""<<newinc->summary()<<"\" ( old one was \""<<oldinc->summary()<<"\")"<<endl;
00336   if ( incidencesEqual( newinc, oldinc ) ) {
00337     // Don't do anything
00338     kdDebug(5850) << "Incidence not changed\n";
00339   } else {
00340     kdDebug(5850) << "Incidence changed\n";
00341     bool attendeeStatusChanged = myAttendeeStatusChanged( oldinc, newinc );
00342     int revision = newinc->revision();
00343     newinc->setRevision( revision + 1 );
00344     // FIXME: Use a generic method for this! Ideally, have an interface class
00345     //        for group cheduling. Each implementation could then just do what
00346     //        it wants with the event. If no groupware is used,use the null
00347     //        pattern...
00348     bool success = true;
00349     if ( KOPrefs::instance()->mUseGroupwareCommunication ) {
00350       success = KOGroupware::instance()->sendICalMessage(
00351         parent,
00352         KCal::Scheduler::Request,
00353         newinc, 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, 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