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"