korganizer

kogroupware.cpp

00001 /*
00002   This file is part of the Groupware/KOrganizer integration.
00003 
00004   Requires the Qt and KDE widget libraries, available at no cost at
00005   http://www.trolltech.com and http://www.kde.org respectively
00006 
00007   Copyright (c) 2002-2004 Klarälvdalens Datakonsult AB
00008         <info@klaralvdalens-datakonsult.se>
00009 
00010   This program is free software; you can redistribute it and/or modify
00011   it under the terms of the GNU General Public License as published by
00012   the Free Software Foundation; either version 2 of the License, or
00013   (at your option) any later version.
00014 
00015   This program is distributed in the hope that it will be useful,
00016   but WITHOUT ANY WARRANTY; without even the implied warranty of
00017   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
00018   GNU General Public License for more details.
00019 
00020   You should have received a copy of the GNU General Public License
00021   along with this program; if not, write to the Free Software
00022   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
00023   MA  02110-1301, USA.
00024 
00025   In addition, as a special exception, the copyright holders give
00026   permission to link the code of this program with any edition of
00027   the Qt library by Trolltech AS, Norway (or with modified versions
00028   of Qt that use the same license as Qt), and distribute linked
00029   combinations including the two.  You must obey the GNU General
00030   Public License in all respects for all of the code used other than
00031   Qt.  If you modify this file, you may extend this exception to
00032   your version of the file, but you are not obligated to do so.  If
00033   you do not wish to do so, delete this exception statement from
00034   your version.
00035 */
00036 
00037 #include "kogroupware.h"
00038 #include "freebusymanager.h"
00039 #include "calendarview.h"
00040 #include "mailscheduler.h"
00041 #include "koprefs.h"
00042 #include "koincidenceeditor.h"
00043 #include <libemailfunctions/email.h>
00044 #include <libkcal/attendee.h>
00045 #include <libkcal/calhelper.h>
00046 #include <libkcal/journal.h>
00047 #include <libkcal/incidenceformatter.h>
00048 #include <kdebug.h>
00049 #include <kmessagebox.h>
00050 #include <kstandarddirs.h>
00051 #include <kdirwatch.h>
00052 #include <qfile.h>
00053 #include <qregexp.h>
00054 #include <qdir.h>
00055 #include <qtimer.h>
00056 
00057 FreeBusyManager *KOGroupware::mFreeBusyManager = 0;
00058 
00059 KOGroupware *KOGroupware::mInstance = 0;
00060 
00061 KOGroupware *KOGroupware::create( CalendarView *view,
00062                                   KCal::CalendarResources *calendar )
00063 {
00064   if( !mInstance )
00065     mInstance = new KOGroupware( view, calendar );
00066   return mInstance;
00067 }
00068 
00069 void KOGroupware::destroy()
00070 {
00071   delete mInstance;
00072   mInstance = 0;
00073   mFreeBusyManager = 0; // mFreeBusyManager is already deleted when mInstance is deleted
00074 }
00075 
00076 KOGroupware *KOGroupware::instance()
00077 {
00078   // Doesn't create, that is the task of create()
00079   Q_ASSERT( mInstance );
00080   return mInstance;
00081 }
00082 
00083 
00084 KOGroupware::KOGroupware( CalendarView* view, KCal::CalendarResources* cal )
00085    : QObject( 0, "kmgroupware_instance" ), mView( view ), mCalendar( cal ), mDoNotNotify( false )
00086 {
00087   // Set up the dir watch of the three incoming dirs
00088   KDirWatch* watcher = KDirWatch::self();
00089   watcher->addDir( locateLocal( "data", "korganizer/income.accepted/" ) );
00090   watcher->addDir( locateLocal( "data", "korganizer/income.tentative/" ) );
00091   watcher->addDir( locateLocal( "data", "korganizer/income.counter/" ) );
00092   watcher->addDir( locateLocal( "data", "korganizer/income.cancel/" ) );
00093   watcher->addDir( locateLocal( "data", "korganizer/income.reply/" ) );
00094   watcher->addDir( locateLocal( "data", "korganizer/income.delegated/" ) );
00095   connect( watcher, SIGNAL( dirty( const QString& ) ),
00096            this, SLOT( incomingDirChanged( const QString& ) ) );
00097   // Now set the ball rolling
00098   QTimer::singleShot( 0, this, SLOT(initialCheckForChanges()) );
00099 
00100   // Initialize
00101   lastUsedDialogAnswer = KMessageBox::Yes;
00102 }
00103 
00104 void KOGroupware::initialCheckForChanges()
00105 {
00106   incomingDirChanged( locateLocal( "data", "korganizer/income.accepted/" ) );
00107   incomingDirChanged( locateLocal( "data", "korganizer/income.tentative/" ) );
00108   incomingDirChanged( locateLocal( "data", "korganizer/income.counter/" ) );
00109   incomingDirChanged( locateLocal( "data", "korganizer/income.cancel/" ) );
00110   incomingDirChanged( locateLocal( "data", "korganizer/income.reply/" ) );
00111   incomingDirChanged( locateLocal( "data", "korganizer/income.delegated/" ) );
00112 }
00113 
00114 void KOGroupware::slotViewNewIncidenceChanger( IncidenceChangerBase* changer )
00115 {
00116     // Call slot perhapsUploadFB if an incidence was added, changed or removed
00117     connect( changer, SIGNAL( incidenceAdded( Incidence* ) ),
00118              mFreeBusyManager, SLOT( slotPerhapsUploadFB() ) );
00119     connect( changer, SIGNAL( incidenceChanged( Incidence*, Incidence*, KOGlobals::WhatChanged ) ),
00120              mFreeBusyManager, SLOT( slotPerhapsUploadFB() ) );
00121     connect( changer, SIGNAL( incidenceDeleted( Incidence * ) ),
00122              mFreeBusyManager, SLOT( slotPerhapsUploadFB() ) );
00123 }
00124 
00125 FreeBusyManager *KOGroupware::freeBusyManager()
00126 {
00127   if ( !mFreeBusyManager ) {
00128     mFreeBusyManager = new FreeBusyManager( this, "freebusymanager" );
00129     mFreeBusyManager->setCalendar( mCalendar );
00130     connect( mCalendar, SIGNAL( calendarChanged() ),
00131              mFreeBusyManager, SLOT( slotPerhapsUploadFB() ) );
00132     connect( mView, SIGNAL( newIncidenceChanger( IncidenceChangerBase* ) ),
00133              this, SLOT( slotViewNewIncidenceChanger( IncidenceChangerBase* ) ) );
00134     slotViewNewIncidenceChanger( mView->incidenceChanger() );
00135   }
00136 
00137   return mFreeBusyManager;
00138 }
00139 
00140 void KOGroupware::incomingDirChanged( const QString& path )
00141 {
00142   const QString incomingDirName = locateLocal( "data","korganizer/" )
00143                                   + "income.";
00144   if ( !path.startsWith( incomingDirName ) ) {
00145     kdDebug(5850) << "incomingDirChanged: Wrong dir " << path << endl;
00146     return;
00147   }
00148   QString action = path.mid( incomingDirName.length() );
00149   while ( action.length() > 0 && action[ action.length()-1 ] == '/' )
00150     // Strip slashes at the end
00151     action.truncate( action.length()-1 );
00152 
00153   // Handle accepted invitations
00154   QDir dir( path );
00155   const QStringList files = dir.entryList( QDir::Files );
00156   if ( files.isEmpty() )
00157     // No more files here
00158     return;
00159 
00160   // Read the file and remove it
00161   QFile f( path + "/" + files[0] );
00162   if (!f.open(IO_ReadOnly)) {
00163     kdError(5850) << "Can't open file '" << files[0] << "'" << endl;
00164     return;
00165   }
00166   QTextStream t(&f);
00167   t.setEncoding( QTextStream::UnicodeUTF8 );
00168   QString receiver = KPIM::getFirstEmailAddress( t.readLine() );
00169   QString iCal = t.read();
00170 
00171   f.remove();
00172 
00173   ScheduleMessage *message = mFormat.parseScheduleMessage( mCalendar, iCal );
00174   if ( !message ) {
00175     QString errorMessage;
00176     if (mFormat.exception())
00177       errorMessage = i18n( "Error message: %1" ).arg( mFormat.exception()->message() );
00178     kdDebug(5850) << "MailScheduler::retrieveTransactions() Error parsing "
00179                   << errorMessage << endl;
00180     KMessageBox::detailedError( mView,
00181         i18n("Error while processing an invitation or update."),
00182         errorMessage );
00183     return;
00184   }
00185 
00186   KCal::Scheduler::Method method =
00187     static_cast<KCal::Scheduler::Method>( message->method() );
00188   KCal::ScheduleMessage::Status status = message->status();
00189   KCal::Incidence* incidence =
00190     dynamic_cast<KCal::Incidence*>( message->event() );
00191   if(!incidence) {
00192     delete message;
00193     return;
00194   }
00195   KCal::MailScheduler scheduler( mCalendar );
00196   if ( action.startsWith( "accepted" ) || action.startsWith( "tentative" ) ||
00197        action.startsWith( "delegated" ) || action.startsWith( "counter" ) ) {
00198     // Find myself and set my status. This can't be done in the scheduler,
00199     // since this does not know the choice I made in the KMail bpf
00200     KCal::Attendee::List attendees = incidence->attendees();
00201     KCal::Attendee::List::ConstIterator it;
00202     for ( it = attendees.begin(); it != attendees.end(); ++it ) {
00203       if( (*it)->email() == receiver ) {
00204         if ( action.startsWith( "accepted" ) )
00205           (*it)->setStatus( KCal::Attendee::Accepted );
00206         else if ( action.startsWith( "tentative" ) )
00207           (*it)->setStatus( KCal::Attendee::Tentative );
00208         else if ( KOPrefs::instance()->outlookCompatCounterProposals() && action.startsWith( "counter" ) )
00209           (*it)->setStatus( KCal::Attendee::Tentative );
00210         else if ( action.startsWith( "delegated" ) )
00211           (*it)->setStatus( KCal::Attendee::Delegated );
00212         break;
00213       }
00214     }
00215     if ( KOPrefs::instance()->outlookCompatCounterProposals() || !action.startsWith( "counter" ) ) {
00216       if ( scheduler.acceptTransaction( incidence, method, status, receiver ) ) {
00217         mCalendar->save();
00218       }
00219     }
00220   } else if ( action.startsWith( "cancel" ) ) {
00221     // Delete the old incidence, if one is present
00222     if ( scheduler.acceptTransaction( incidence, KCal::Scheduler::Cancel, status, receiver ) ) {
00223       mCalendar->save();
00224     }
00225   } else if ( action.startsWith( "reply" ) ) {
00226     if ( method != Scheduler::Counter ) {
00227       if ( scheduler.acceptTransaction( incidence, method, status ) ) {
00228         mCalendar->save();
00229       }
00230     } else {
00231       // accept counter proposal
00232       if ( scheduler.acceptCounterProposal( incidence ) ) {
00233         mCalendar->save();
00234         // send update to all attendees
00235         sendICalMessage( mView, Scheduler::Request, incidence, 0,
00236                          KOGlobals::INCIDENCEEDITED, false );
00237       }
00238     }
00239   } else {
00240     kdError(5850) << "Unknown incoming action " << action << endl;
00241   }
00242 
00243   if ( action.startsWith( "counter" ) ) {
00244     mView->editIncidence( incidence, QDate(), true );
00245     KOIncidenceEditor *tmp = mView->editorDialog( incidence );
00246     tmp->selectInvitationCounterProposal( true );
00247   }
00248   mView->updateView();
00249 }
00250 
00251 class KOInvitationFormatterHelper : public InvitationFormatterHelper
00252 {
00253   public:
00254     virtual QString generateLinkURL( const QString &id ) { return "kmail:groupware_request_" + id; }
00255 };
00256 
00257 // A string comparison that considers that null and empty are the same
00258 static bool stringCompare( const QString &s1, const QString &s2 )
00259 {
00260   return ( s1.isEmpty() && s2.isEmpty() ) || ( s1 == s2 );
00261 }
00262 
00263 static bool compareIncsExceptAttendees( Incidence *i1, Incidence *i2 )
00264 {
00265   if( i1->alarms().count() != i2->alarms().count() ) {
00266     return false; // no need to check further
00267   }
00268 
00269   Alarm::List::ConstIterator a1 = i1->alarms().begin();
00270   Alarm::List::ConstIterator a2 = i2->alarms().begin();
00271   for( ; a1 != i1->alarms().end() && a2 != i2->alarms().end(); ++a1, ++a2 ) {
00272     if( **a1 == **a2 ) {
00273       continue;
00274     } else {
00275       return false;
00276     }
00277   }
00278 
00279   bool recurrenceEqual = ( i1->recurrence() == 0 && i2->recurrence() == 0 );
00280   if ( !recurrenceEqual ) {
00281     recurrenceEqual = i1->recurrence() != 0 &&
00282                       i2->recurrence() != 0 &&
00283                       i2->recurrence() == i2->recurrence();
00284   }
00285 
00286   return
00287     recurrenceEqual &&
00288     i1->dtStart() == i2->dtStart() &&
00289     i1->organizer() == i2->organizer() &&
00290     i1->doesFloat() == i2->doesFloat() &&
00291     i1->duration() == i2->duration() &&
00292     i2->hasDuration() == i2->hasDuration() &&
00293     stringCompare( i1->description(), i2->description() ) &&
00294     stringCompare( i1->summary(), i2->summary() ) &&
00295     i1->categories() == i2->categories() &&
00296     i1->attachments() == i2->attachments() &&
00297     i1->secrecy() == i2->secrecy() &&
00298     i1->priority() == i2->priority() &&
00299     stringCompare( i1->location(), i2->location() );
00300 }
00301 
00302 static QStringList recipients( Attendee::List attendees )
00303 {
00304   QStringList attStrList;
00305   for ( Attendee::List::ConstIterator it = attendees.begin(); it != attendees.end(); ++it ) {
00306     attStrList << (*it)->fullName();
00307   }
00308   return attStrList;
00309 }
00310 
00311 /* This function sends mails if necessary, and makes sure the user really
00312  * want to change his calendar.
00313  *
00314  * Return true means accept the changes
00315  * Return false means revert the changes
00316  */
00317 bool KOGroupware::sendICalMessage( QWidget* parent,
00318                                    KCal::Scheduler::Method method,
00319                                    Incidence *incidence,
00320                                    Incidence *oldincidence,
00321                                    KOGlobals::HowChanged action,
00322                                    bool attendeeStatusChanged,
00323                                    bool useLastDialogAnswer )
00324 {
00325   // If there are no attendees, don't bother
00326   if ( incidence->attendees().isEmpty() )
00327     return true;
00328 
00329   bool isOrganizer = KOPrefs::instance()->thatIsMe( incidence->organizer().email() );
00330   int rc = 0;
00331   /*
00332    * There are two scenarios:
00333    * o "we" are the organizer, where "we" means any of the identities or mail
00334    *   addresses known to Kontact/PIM. If there are attendees, we need to mail
00335    *   them all, even if one or more of them are also "us". Otherwise there
00336    *   would be no way to invite a resource or our boss, other identities we
00337    *   also manage.
00338    * o "we: are not the organizer, which means we changed the completion status
00339    *   of a todo, or we changed our attendee status from, say, tentative to
00340    *   accepted. In both cases we only mail the organizer. All other changes
00341    *   bring us out of sync with the organizer, so we won't mail, if the user
00342    *   insists on applying them.
00343    */
00344 
00345   if ( isOrganizer ) {
00346     // We are the organizer. If there is more than one attendee, or if there is
00347     // only one, and it's not the same as the organizer, ask the user to send mail.
00348     // Also send a mail if there were more then one attendees in the previous version
00349     // of the incidence.
00350     if ( incidence->attendees().count() > 1
00351         || ( oldincidence && oldincidence->attendees().count() > 1 )
00352         || incidence->attendees().first()->email() != incidence->organizer().email() ) {
00353 
00354       QString txt;
00355       switch( action ) {
00356       case KOGlobals::INCIDENCEEDITED:
00357       {
00358         bool sendUpdate = true;
00359 
00360         Attendee::List attendees = incidence->attendees();
00361         Attendee::List::ConstIterator it;
00362 
00363         // create the list of attendees from the new incidence
00364         Attendee::List sameAtts;
00365         for ( it = attendees.begin(); it != attendees.end(); ++it ) {
00366           if ( (*it)->email() != incidence->organizer().email() ) { //skip organizer
00367             sameAtts << *it;
00368           }
00369         }
00370 
00371         if ( incidence->summary().isEmpty() ) {
00372           incidence->setSummary( i18n( "<No summary given>" ) );
00373         }
00374 
00375         if ( oldincidence ) {
00376           // First we need to determine if the old and new incidences differ in
00377           // the attendee list only. If that's the case, then we need to get the
00378           // list of new attendees and the list of removed attendees and deal
00379           // with those lists separately.
00380 
00381           Attendee::List oldattendees = oldincidence->attendees();
00382           Attendee::List::ConstIterator ot;
00383 
00384           Attendee::List newAtts;  // newly added attendees
00385           Attendee::List remAtts;  // newly removed attendees
00386           sameAtts.clear();
00387 
00388           // make a list of newly added attendees and attendees in both old and new incidence.
00389           for ( it = attendees.begin(); it != attendees.end(); ++it ) {
00390             bool found = false;
00391             for ( ot = oldattendees.begin(); ot != oldattendees.end(); ++ot ) {
00392               if ( (*it)->email() == (*ot)->email() ) {
00393                 found = true;
00394                 break;
00395               }
00396             }
00397             if ( !found ) {
00398               newAtts << *it;
00399             } else {
00400               if ( (*it)->email() != incidence->organizer().email() ) { //skip organizer
00401                 sameAtts << *it;
00402               }
00403             }
00404           }
00405 
00406           // make a list of newly removed attendees
00407           for ( ot = oldattendees.begin(); ot != oldattendees.end(); ++ot ) {
00408             bool found = false;
00409             for ( it = attendees.begin(); it != attendees.end(); ++it ) {
00410               if ( (*it)->email() == (*ot)->email() ) {
00411                 found = true;
00412                 break;
00413               }
00414             }
00415             if ( !found ) {
00416               remAtts << *ot;
00417             }
00418           }
00419 
00420           // let's see if any else changed from the old incidence
00421           if ( compareIncsExceptAttendees( incidence, oldincidence ) ) {
00422             // no change, so no need to send an update to the original attendees list.
00423             sendUpdate = false;
00424           }
00425 
00426           // For new attendees, send them the new incidence as a new invitation.
00427           if ( newAtts.count() > 0 ) {
00428             const QStringList rList = recipients( newAtts );
00429             int newMail = KMessageBox::questionYesNoList(
00430               parent,
00431               i18n( "You are adding new attendees to the invitation \"%1\".\n"
00432                     "Do you want to email an invitation to these new attendees?" ).
00433               arg( incidence->summary() ),
00434               rList,
00435               i18n( "New Attendees" ),
00436               KGuiItem( i18n( "Send Email" ) ), KGuiItem( i18n( "Do Not Send" ) ) );
00437             if ( newMail == KMessageBox::Yes ) {
00438               KCal::MailScheduler scheduler( mCalendar );
00439               incidence->setRevision( 0 );
00440               scheduler.performTransaction( incidence, Scheduler::Request, rList.join( "," ) );
00441             }
00442           }
00443 
00444           // For removed attendees, tell them they are toast and send them a cancel.
00445           if ( remAtts.count() > 0 ) {
00446             const QStringList rList = recipients( remAtts );
00447             int newMail = KMessageBox::questionYesNoList(
00448               parent,
00449               i18n( "You removed attendees from the invitation \"%1\".\n"
00450                     "Do you want to email a cancellation message to these attendees?" ).
00451               arg( incidence->summary() ),
00452               rList,
00453               i18n( "Removed Attendees" ),
00454               KGuiItem( i18n( "Send Email" ) ), KGuiItem( i18n( "Do Not Send" ) ) );
00455             if ( newMail == KMessageBox::Yes ) {
00456               KCal::MailScheduler scheduler( mCalendar );
00457               scheduler.performTransaction( incidence, Scheduler::Cancel, rList.join( "," ) );
00458             }
00459           }
00460         }
00461 
00462         // For existing attendees, skip the update if there are no other changes except attendees
00463         if ( sameAtts.count() > 0 ) {
00464           const QStringList rList = recipients( sameAtts );
00465           int newMail;
00466           if ( sendUpdate ) {
00467             newMail = KMessageBox::questionYesNoList(
00468               parent,
00469               i18n( "You changed the invitation \"%1\".\n"
00470                     "Do you want to email an updated invitation to these attendees?" ).
00471               arg( incidence->summary() ),
00472               rList,
00473               i18n( "Send Invitation Update" ),
00474               KGuiItem( i18n( "Send Email" ) ), KGuiItem( i18n( "Do Not Send" ) ) );
00475           } else {
00476             newMail = KMessageBox::questionYesNoList(
00477               parent,
00478               i18n( "You changed the invitation attendee list only.\n"
00479                     "Do you want to email an updated invitation showing the new attendees?" ),
00480               rList,
00481               i18n( "Send Invitation Update" ),
00482               KGuiItem( i18n( "Send Email" ) ), KGuiItem( i18n( "Do Not Send" ) ) );
00483           }
00484           if ( newMail == KMessageBox::Yes ) {
00485             KCal::MailScheduler scheduler( mCalendar );
00486             incidence->setRevision( incidence->revision() + 1 );
00487             scheduler.performTransaction( incidence, Scheduler::Request, rList.join( "," ) );
00488           }
00489         }
00490         return true;
00491       }
00492 
00493       case KOGlobals::INCIDENCEDELETED:
00494         Q_ASSERT( incidence->type() == "Event" || incidence->type() == "Todo" );
00495         if ( incidence->type() == "Event" ) {
00496           txt = i18n( "You removed the invitation \"%1\".\n"
00497                       "Do you want to email the attendees that the event is canceled?" ).
00498                 arg( incidence->summary() );
00499         } else if ( incidence->type() == "Todo" ) {
00500           txt = i18n( "You removed the invitation \"%1\".\n"
00501                       "Do you want to email the attendees that the todo is canceled?" ).
00502                 arg( incidence->summary() );
00503         }
00504         break;
00505 
00506       case KOGlobals::INCIDENCEADDED:
00507         if ( incidence->type() == "Event" ) {
00508           txt = i18n( "The event \"%1\" includes other people.\n"
00509                       "Do you want to email the invitation to the attendees?" ).
00510                 arg( incidence->summary() );
00511         } else if ( incidence->type() == "Todo" ) {
00512           txt = i18n( "The todo \"%1\" includes other people.\n"
00513                       "Do you want to email the invitation to the attendees?" ).
00514                 arg( incidence->summary() );
00515         } else {
00516           txt = i18n( "This incidence includes other people. "
00517                       "Should an email be sent to the attendees?" );
00518         }
00519         break;
00520 
00521       default:
00522         kdError() << "Unsupported HowChanged action" << int( action ) << endl;
00523         break;
00524       }
00525 
00526       if ( useLastDialogAnswer ) {
00527         rc = lastUsedDialogAnswer;
00528       } else {
00529         lastUsedDialogAnswer = rc = KMessageBox::questionYesNo(
00530           parent, txt, i18n( "Group Scheduling Email" ),
00531           KGuiItem( i18n( "Send Email" ) ), KGuiItem( i18n( "Do Not Send" ) ) );
00532       }
00533     } else {
00534       return true;
00535     }
00536   } else if( incidence->type() == "Todo" ) {
00537     QString txt;
00538     Todo *todo = static_cast<Todo *>( incidence );
00539     Todo *oldtodo = static_cast<Todo *>( oldincidence );
00540     if ( todo && oldtodo &&
00541          todo->percentComplete() != oldtodo->percentComplete() &&
00542          method == Scheduler::Request ) {
00543       txt = i18n( "Your completion status in this task has been changed. "
00544                   "Do you want to send a status update to the task organizer?" );
00545       method = Scheduler::Reply;
00546       if ( useLastDialogAnswer ) {
00547         rc = lastUsedDialogAnswer;
00548       } else {
00549         lastUsedDialogAnswer = rc = KMessageBox::questionYesNo(
00550           parent, txt, i18n( "Group Scheduling Email" ),
00551           KGuiItem( i18n( "Send Update" ) ), KGuiItem( i18n( "Do Not Send" ) ) );
00552       }
00553     } else if ( attendeeStatusChanged && method == Scheduler::Request ) {
00554       txt = i18n( "Your status as a participant in this task changed. "
00555                   "Do you want to send a status update to the task organizer?" );
00556       method = Scheduler::Reply;
00557       if ( useLastDialogAnswer ) {
00558         rc = lastUsedDialogAnswer;
00559       } else {
00560         lastUsedDialogAnswer = rc = KMessageBox::questionYesNo(
00561           parent, txt, i18n( "Group Scheduling Email" ),
00562           KGuiItem( i18n( "Send Update" ) ), KGuiItem( i18n( "Do Not Send" ) ) );
00563       }
00564     } else {
00565       if ( action == KOGlobals::INCIDENCEDELETED ) {
00566         const QStringList myEmails = KOPrefs::instance()->allEmails();
00567         bool askConfirmation = false;
00568         for ( QStringList::ConstIterator it = myEmails.begin(); it != myEmails.end(); ++it ) {
00569           QString email = *it;
00570           Attendee *me = incidence->attendeeByMail(email);
00571           if ( me &&
00572                ( me->status() == KCal::Attendee::Accepted ||
00573                  me->status() == KCal::Attendee::Delegated ) ) {
00574             askConfirmation = true;
00575             break;
00576           }
00577         }
00578 
00579         if ( !askConfirmation ) {
00580           return true;
00581         }
00582 
00583         txt = i18n( "You had previously accepted your participation in this task. "
00584                     "Do you want to send an updated response to the organizer "
00585                     "removing yourself from the task?" );
00586         if ( useLastDialogAnswer ) {
00587           rc = lastUsedDialogAnswer;
00588         } else {
00589           lastUsedDialogAnswer = rc = KMessageBox::questionYesNo(
00590             parent, txt, i18n( "Group Scheduling Email" ),
00591             KGuiItem( i18n( "Send Update" ) ), KGuiItem( i18n( "Do Not Send" ) ) );
00592           setDoNotNotify( rc == KMessageBox::No );
00593         }
00594         return true;
00595       } else {
00596         if ( useLastDialogAnswer ) {
00597           rc = lastUsedDialogAnswer;
00598         } else {
00599           if ( CalHelper::incOrganizerOwnsCalendar( mCalendar, incidence ) ) {
00600             txt = i18n( "<qt>"
00601                         "You are modifying the organizer's task. "
00602                         "Do you really want to edit it?<p>"
00603                         "If \"yes\", all the attendees will be emailed the updated invitation."
00604                         "</qt>" );
00605             lastUsedDialogAnswer = rc = KMessageBox::warningYesNo( parent, txt );
00606             if ( rc == KMessageBox::Yes ) {
00607               KCal::MailScheduler scheduler( mCalendar );
00608               scheduler.performTransaction( incidence, Scheduler::Request,
00609                                             recipients( incidence->attendees() ).join( "," ) );
00610             }
00611           } else {
00612             txt = i18n( "<qt>"
00613                         "You are not the organizer of this task. Editing it will "
00614                         "bring your invitation out of sync with the organizer's invitation. "
00615                         "Do you really want to edit it?<p>"
00616                         "If \"yes\", your local copy of the invitation will be different than "
00617                         "the organizer and any other task participants."
00618                         "</qt>" );
00619             lastUsedDialogAnswer = rc = KMessageBox::warningYesNo( parent, txt );
00620           }
00621         }
00622         return ( rc == KMessageBox::Yes );
00623       }
00624     }
00625   } else if ( incidence->type() == "Event" ) {
00626     QString txt;
00627     if ( attendeeStatusChanged && method == Scheduler::Request ) {
00628       txt = i18n( "Your status as an attendee of this event changed. "
00629                   "Do you want to send a status update to the event organizer?" );
00630       method = Scheduler::Reply;
00631       if ( useLastDialogAnswer ) {
00632         rc = lastUsedDialogAnswer;
00633       } else {
00634         lastUsedDialogAnswer = rc = KMessageBox::questionYesNo(
00635           parent, txt, i18n( "Group Scheduling Email" ),
00636           KGuiItem( i18n( "Send Update" ) ), KGuiItem( i18n( "Do Not Send" ) ) );
00637       }
00638     } else {
00639       if ( action == KOGlobals::INCIDENCEDELETED ) {
00640         const QStringList myEmails = KOPrefs::instance()->allEmails();
00641         bool askConfirmation = false;
00642         for ( QStringList::ConstIterator it = myEmails.begin(); it != myEmails.end(); ++it ) {
00643           QString email = *it;
00644           Attendee *me = incidence->attendeeByMail(email);
00645           if ( me &&
00646                ( me->status() == KCal::Attendee::Accepted ||
00647                  me->status() == KCal::Attendee::Delegated ) ) {
00648             askConfirmation = true;
00649             break;
00650           }
00651         }
00652 
00653         if ( !askConfirmation ) {
00654           return true;
00655         }
00656 
00657         txt = i18n( "You had previously accepted an invitation to this event. "
00658                     "Do you want to send an updated response to the organizer "
00659                     "declining the invitation?" );
00660         if ( useLastDialogAnswer ) {
00661           rc = lastUsedDialogAnswer;
00662         } else {
00663           lastUsedDialogAnswer = rc = KMessageBox::questionYesNo(
00664             parent, txt, i18n( "Group Scheduling Email" ),
00665             KGuiItem( i18n( "Send Update" ) ), KGuiItem( i18n( "Do Not Send" ) ) );
00666           setDoNotNotify( rc == KMessageBox::No );
00667         }
00668         return true;
00669       } else {
00670         if ( useLastDialogAnswer ) {
00671           rc = lastUsedDialogAnswer;
00672         } else {
00673           if ( CalHelper::incOrganizerOwnsCalendar( mCalendar, incidence ) ) {
00674             txt = i18n( "<qt>"
00675                         "You are modifying the organizer's event. "
00676                         "Do you really want to edit it?<p>"
00677                         "If \"yes\", all the attendees will be emailed the updated invitation."
00678                         "</qt>" );
00679             lastUsedDialogAnswer = rc = KMessageBox::warningYesNo( parent, txt );
00680             if ( rc == KMessageBox::Yes ) {
00681               KCal::MailScheduler scheduler( mCalendar );
00682               scheduler.performTransaction( incidence, Scheduler::Request,
00683                                             recipients( incidence->attendees() ).join( "," ) );
00684             }
00685           } else {
00686             txt = i18n( "<qt>"
00687                         "You are not the organizer of this event. Editing it will "
00688                         "bring your invitation out of sync with the organizer's invitation. "
00689                         "Do you really want to edit it?<p>"
00690                         "If \"yes\", your local copy of the invitation will be different than "
00691                         "the organizer and any other event participants."
00692                         "</qt>" );
00693             lastUsedDialogAnswer = rc = KMessageBox::warningYesNo( parent, txt );
00694           }
00695         }
00696         return ( rc == KMessageBox::Yes );
00697       }
00698     }
00699   } else {
00700     kdWarning(5850) << "Groupware messages for Journals are not implemented yet!" << endl;
00701     return true;
00702   }
00703 
00704   if ( rc == KMessageBox::Yes ) {
00705     // We will be sending out a message here. Now make sure there is
00706     // some summary
00707     if( incidence->summary().isEmpty() )
00708       incidence->setSummary( i18n("<No summary given>") );
00709 
00710     // Send the mail
00711     KCal::MailScheduler scheduler( mCalendar );
00712     scheduler.performTransaction( incidence, method );
00713 
00714     return true;
00715   } else if ( rc == KMessageBox::No ) {
00716     return true;
00717   } else {
00718     return false;
00719   }
00720 }
00721 
00722 void KOGroupware::sendCounterProposal(KCal::Calendar *calendar, KCal::Event * oldEvent, KCal::Event * newEvent) const
00723 {
00724   if ( !oldEvent || !newEvent || *oldEvent == *newEvent || !KOPrefs::instance()->mUseGroupwareCommunication )
00725     return;
00726   if ( KOPrefs::instance()->outlookCompatCounterProposals() ) {
00727     Incidence* tmp = oldEvent->clone();
00728     tmp->setSummary( i18n("Counter proposal: %1").arg( newEvent->summary() ) );
00729     tmp->setDescription( newEvent->description() );
00730     tmp->addComment( i18n("Proposed new meeting time: %1 - %2").
00731                      arg( IncidenceFormatter::dateToString( newEvent->dtStart() ),
00732                           IncidenceFormatter::dateToString( newEvent->dtEnd() ) ) );
00733     KCal::MailScheduler scheduler( calendar );
00734     scheduler.performTransaction( tmp, Scheduler::Reply );
00735     delete tmp;
00736   } else {
00737     KCal::MailScheduler scheduler( calendar );
00738     scheduler.performTransaction( newEvent, Scheduler::Counter );
00739   }
00740 }
00741 
00742 #include "kogroupware.moc"
KDE Home | KDE Accessibility Home | Description of Access Keys