00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
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/journal.h>
00046 #include <libkcal/incidenceformatter.h>
00047 #include <kdebug.h>
00048 #include <kmessagebox.h>
00049 #include <kstandarddirs.h>
00050 #include <kdirwatch.h>
00051 #include <qfile.h>
00052 #include <qregexp.h>
00053 #include <qdir.h>
00054 #include <qtimer.h>
00055
00056 FreeBusyManager *KOGroupware::mFreeBusyManager = 0;
00057
00058 KOGroupware *KOGroupware::mInstance = 0;
00059
00060 KOGroupware *KOGroupware::create( CalendarView *view,
00061 KCal::CalendarResources *calendar )
00062 {
00063 if( !mInstance )
00064 mInstance = new KOGroupware( view, calendar );
00065 return mInstance;
00066 }
00067
00068 KOGroupware *KOGroupware::instance()
00069 {
00070
00071 Q_ASSERT( mInstance );
00072 return mInstance;
00073 }
00074
00075
00076 KOGroupware::KOGroupware( CalendarView* view, KCal::CalendarResources* cal )
00077 : QObject( 0, "kmgroupware_instance" ), mView( view ), mCalendar( cal ), mDoNotNotify( false )
00078 {
00079
00080 KDirWatch* watcher = KDirWatch::self();
00081 watcher->addDir( locateLocal( "data", "korganizer/income.accepted/" ) );
00082 watcher->addDir( locateLocal( "data", "korganizer/income.tentative/" ) );
00083 watcher->addDir( locateLocal( "data", "korganizer/income.counter/" ) );
00084 watcher->addDir( locateLocal( "data", "korganizer/income.cancel/" ) );
00085 watcher->addDir( locateLocal( "data", "korganizer/income.reply/" ) );
00086 watcher->addDir( locateLocal( "data", "korganizer/income.delegated/" ) );
00087 connect( watcher, SIGNAL( dirty( const QString& ) ),
00088 this, SLOT( incomingDirChanged( const QString& ) ) );
00089
00090 QTimer::singleShot( 0, this, SLOT(initialCheckForChanges()) );
00091
00092
00093 lastUsedDialogAnswer = KMessageBox::Yes;
00094 }
00095
00096 void KOGroupware::initialCheckForChanges()
00097 {
00098 incomingDirChanged( locateLocal( "data", "korganizer/income.accepted/" ) );
00099 incomingDirChanged( locateLocal( "data", "korganizer/income.tentative/" ) );
00100 incomingDirChanged( locateLocal( "data", "korganizer/income.counter/" ) );
00101 incomingDirChanged( locateLocal( "data", "korganizer/income.cancel/" ) );
00102 incomingDirChanged( locateLocal( "data", "korganizer/income.reply/" ) );
00103 incomingDirChanged( locateLocal( "data", "korganizer/income.delegated/" ) );
00104 }
00105
00106 void KOGroupware::slotViewNewIncidenceChanger( IncidenceChangerBase* changer )
00107 {
00108
00109 connect( changer, SIGNAL( incidenceAdded( Incidence* ) ),
00110 mFreeBusyManager, SLOT( slotPerhapsUploadFB() ) );
00111 connect( changer, SIGNAL( incidenceChanged( Incidence*, Incidence*, KOGlobals::WhatChanged ) ),
00112 mFreeBusyManager, SLOT( slotPerhapsUploadFB() ) );
00113 connect( changer, SIGNAL( incidenceDeleted( Incidence * ) ),
00114 mFreeBusyManager, SLOT( slotPerhapsUploadFB() ) );
00115 }
00116
00117 FreeBusyManager *KOGroupware::freeBusyManager()
00118 {
00119 if ( !mFreeBusyManager ) {
00120 mFreeBusyManager = new FreeBusyManager( this, "freebusymanager" );
00121 mFreeBusyManager->setCalendar( mCalendar );
00122 connect( mCalendar, SIGNAL( calendarChanged() ),
00123 mFreeBusyManager, SLOT( slotPerhapsUploadFB() ) );
00124 connect( mView, SIGNAL( newIncidenceChanger( IncidenceChangerBase* ) ),
00125 this, SLOT( slotViewNewIncidenceChanger( IncidenceChangerBase* ) ) );
00126 slotViewNewIncidenceChanger( mView->incidenceChanger() );
00127 }
00128
00129 return mFreeBusyManager;
00130 }
00131
00132 void KOGroupware::incomingDirChanged( const QString& path )
00133 {
00134 const QString incomingDirName = locateLocal( "data","korganizer/" )
00135 + "income.";
00136 if ( !path.startsWith( incomingDirName ) ) {
00137 kdDebug(5850) << "incomingDirChanged: Wrong dir " << path << endl;
00138 return;
00139 }
00140 QString action = path.mid( incomingDirName.length() );
00141 while ( action.length() > 0 && action[ action.length()-1 ] == '/' )
00142
00143 action.truncate( action.length()-1 );
00144
00145
00146 QDir dir( path );
00147 const QStringList files = dir.entryList( QDir::Files );
00148 if ( files.isEmpty() )
00149
00150 return;
00151
00152
00153 QFile f( path + "/" + files[0] );
00154 if (!f.open(IO_ReadOnly)) {
00155 kdError(5850) << "Can't open file '" << files[0] << "'" << endl;
00156 return;
00157 }
00158 QTextStream t(&f);
00159 t.setEncoding( QTextStream::UnicodeUTF8 );
00160 QString receiver = KPIM::getFirstEmailAddress( t.readLine() );
00161 QString iCal = t.read();
00162
00163 f.remove();
00164
00165 ScheduleMessage *message = mFormat.parseScheduleMessage( mCalendar, iCal );
00166 if ( !message ) {
00167 QString errorMessage;
00168 if (mFormat.exception())
00169 errorMessage = i18n( "Error message: %1" ).arg( mFormat.exception()->message() );
00170 kdDebug(5850) << "MailScheduler::retrieveTransactions() Error parsing "
00171 << errorMessage << endl;
00172 KMessageBox::detailedError( mView,
00173 i18n("Error while processing an invitation or update."),
00174 errorMessage );
00175 return;
00176 }
00177
00178 KCal::Scheduler::Method method =
00179 static_cast<KCal::Scheduler::Method>( message->method() );
00180 KCal::ScheduleMessage::Status status = message->status();
00181 KCal::Incidence* incidence =
00182 dynamic_cast<KCal::Incidence*>( message->event() );
00183 if(!incidence) {
00184 delete message;
00185 return;
00186 }
00187 KCal::MailScheduler scheduler( mCalendar );
00188 if ( action.startsWith( "accepted" ) || action.startsWith( "tentative" )
00189 || action.startsWith( "delegated" ) || action.startsWith( "counter" ) ) {
00190
00191
00192 KCal::Attendee::List attendees = incidence->attendees();
00193 KCal::Attendee::List::ConstIterator it;
00194 for ( it = attendees.begin(); it != attendees.end(); ++it ) {
00195 if( (*it)->email() == receiver ) {
00196 if ( action.startsWith( "accepted" ) )
00197 (*it)->setStatus( KCal::Attendee::Accepted );
00198 else if ( action.startsWith( "tentative" ) )
00199 (*it)->setStatus( KCal::Attendee::Tentative );
00200 else if ( KOPrefs::instance()->outlookCompatCounterProposals() && action.startsWith( "counter" ) )
00201 (*it)->setStatus( KCal::Attendee::Tentative );
00202 else if ( action.startsWith( "delegated" ) )
00203 (*it)->setStatus( KCal::Attendee::Delegated );
00204 break;
00205 }
00206 }
00207 if ( KOPrefs::instance()->outlookCompatCounterProposals() || !action.startsWith( "counter" ) )
00208 scheduler.acceptTransaction( incidence, method, status, receiver );
00209 } else if ( action.startsWith( "cancel" ) )
00210
00211 scheduler.acceptTransaction( incidence, KCal::Scheduler::Cancel, status, receiver );
00212 else if ( action.startsWith( "reply" ) ) {
00213 if ( method != Scheduler::Counter ) {
00214 scheduler.acceptTransaction( incidence, method, status );
00215 } else {
00216
00217 scheduler.acceptCounterProposal( incidence );
00218
00219 sendICalMessage( mView, Scheduler::Request, incidence, KOGlobals::INCIDENCEEDITED, false );
00220 }
00221 } else
00222 kdError(5850) << "Unknown incoming action " << action << endl;
00223
00224 if ( action.startsWith( "counter" ) ) {
00225 mView->editIncidence( incidence, QDate(), true );
00226 KOIncidenceEditor *tmp = mView->editorDialog( incidence );
00227 tmp->selectInvitationCounterProposal( true );
00228 }
00229 mView->updateView();
00230 }
00231
00232 class KOInvitationFormatterHelper : public InvitationFormatterHelper
00233 {
00234 public:
00235 virtual QString generateLinkURL( const QString &id ) { return "kmail:groupware_request_" + id; }
00236 };
00237
00238
00239
00240
00241
00242
00243
00244 bool KOGroupware::sendICalMessage( QWidget* parent,
00245 KCal::Scheduler::Method method,
00246 Incidence* incidence,
00247 KOGlobals::HowChanged action,
00248 bool attendeeStatusChanged,
00249 bool useLastDialogAnswer )
00250 {
00251
00252 if ( incidence->attendees().isEmpty() )
00253 return true;
00254
00255 bool isOrganizer = KOPrefs::instance()->thatIsMe( incidence->organizer().email() );
00256 int rc = 0;
00257
00258
00259
00260
00261
00262
00263
00264
00265
00266
00267
00268
00269
00270
00271 if ( isOrganizer ) {
00272
00273
00274
00275 if ( incidence->attendees().count() > 1
00276 || incidence->attendees().first()->email() != incidence->organizer().email() ) {
00277
00278 QString txt;
00279 switch( action ) {
00280 case KOGlobals::INCIDENCEEDITED:
00281 txt = i18n( "You changed the invitation \"%1\".\n"
00282 "Do you want to email the attendees an update message?" ).
00283 arg( incidence->summary() );
00284 break;
00285 case KOGlobals::INCIDENCEDELETED:
00286 Q_ASSERT( incidence->type() == "Event" || incidence->type() == "Todo" );
00287 if ( incidence->type() == "Event" ) {
00288 txt = i18n( "You removed the invitation \"%1\".\n"
00289 "Do you want to email the attendees that the event is canceled?" ).
00290 arg( incidence->summary() );
00291 } else if ( incidence->type() == "Todo" ) {
00292 txt = i18n( "You removed the invitation \"%1\".\n"
00293 "Do you want to email the attendees that the todo is canceled?" ).
00294 arg( incidence->summary() );
00295 }
00296 break;
00297 case KOGlobals::INCIDENCEADDED:
00298 if ( incidence->type() == "Event" ) {
00299 txt = i18n( "The event \"%1\" includes other people.\n"
00300 "Do you want to email the invitation to the attendees?" ).
00301 arg( incidence->summary() );
00302 } else if ( incidence->type() == "Todo" ) {
00303 txt = i18n( "The todo \"%1\" includes other people.\n"
00304 "Do you want to email the invitation to the attendees?" ).
00305 arg( incidence->summary() );
00306 } else {
00307 txt = i18n( "This incidence includes other people. "
00308 "Should an email be sent to the attendees?" );
00309 }
00310 break;
00311 default:
00312 kdError() << "Unsupported HowChanged action" << int( action ) << endl;
00313 break;
00314 }
00315
00316 if ( useLastDialogAnswer ) {
00317 rc = lastUsedDialogAnswer;
00318 } else {
00319 lastUsedDialogAnswer = rc = KMessageBox::questionYesNo(
00320 parent, txt, i18n( "Group Scheduling Email" ),
00321 KGuiItem( i18n( "Send Email" ) ), KGuiItem( i18n( "Do Not Send" ) ) );
00322 }
00323 } else {
00324 return true;
00325 }
00326 } else if( incidence->type() == "Todo" ) {
00327 if( method == Scheduler::Request )
00328
00329 method = Scheduler::Reply;
00330
00331 if ( useLastDialogAnswer ) {
00332 rc = lastUsedDialogAnswer;
00333 } else {
00334
00335 const QString txt = i18n( "Do you want to send a status update to the "
00336 "organizer of this task?");
00337 lastUsedDialogAnswer = rc = KMessageBox::questionYesNo( parent, txt, QString::null, i18n("Send Update"), i18n("Do Not Send") );
00338 }
00339 } else if ( incidence->type() == "Event" ) {
00340 QString txt;
00341 if ( attendeeStatusChanged && method == Scheduler::Request ) {
00342 txt = i18n( "Your status as an attendee of this event changed. "
00343 "Do you want to send a status update to the event organizer?" );
00344 method = Scheduler::Reply;
00345 if ( useLastDialogAnswer ) {
00346 rc = lastUsedDialogAnswer;
00347 } else {
00348 lastUsedDialogAnswer = rc = KMessageBox::questionYesNo( parent, txt, QString::null, i18n("Send Update"), i18n("Do Not Send") );
00349 }
00350 } else {
00351 if( action == KOGlobals::INCIDENCEDELETED ) {
00352 const QStringList myEmails = KOPrefs::instance()->allEmails();
00353 bool askConfirmation = false;
00354 for ( QStringList::ConstIterator it = myEmails.begin(); it != myEmails.end(); ++it ) {
00355 QString email = *it;
00356 Attendee *me = incidence->attendeeByMail(email);
00357 if (me && (me->status()==KCal::Attendee::Accepted || me->status()==KCal::Attendee::Delegated)) {
00358 askConfirmation = true;
00359 break;
00360 }
00361 }
00362
00363 if ( !askConfirmation ) {
00364 return true;
00365 }
00366
00367 txt = i18n( "You had previously accepted an invitation to this event. "
00368 "Do you want to send an updated response to the organizer "
00369 "declining the invitation?" );
00370 if ( useLastDialogAnswer ) {
00371 rc = lastUsedDialogAnswer;
00372 } else {
00373 lastUsedDialogAnswer = rc = KMessageBox::questionYesNo(
00374 parent, txt, i18n( "Group Scheduling Email" ),
00375 KGuiItem( i18n( "Send Update" ) ), KGuiItem( i18n( "Do Not Send" ) ) );
00376 setDoNotNotify( rc == KMessageBox::No );
00377 }
00378 } else {
00379 if ( useLastDialogAnswer ) {
00380 rc = lastUsedDialogAnswer;
00381 } else {
00382 txt = i18n( "You are not the organizer of this event. Editing it will "
00383 "bring your calendar out of sync with the organizer's calendar. "
00384 "Do you really want to edit it?" );
00385 lastUsedDialogAnswer = rc = KMessageBox::warningYesNo( parent, txt );
00386 }
00387 return ( rc == KMessageBox::Yes );
00388 }
00389 }
00390 } else {
00391 kdWarning(5850) << "Groupware messages for Journals are not implemented yet!" << endl;
00392 return true;
00393 }
00394
00395 if ( rc == KMessageBox::Yes ) {
00396
00397
00398 if( incidence->summary().isEmpty() )
00399 incidence->setSummary( i18n("<No summary given>") );
00400
00401
00402 KCal::MailScheduler scheduler( mCalendar );
00403 scheduler.performTransaction( incidence, method );
00404
00405 return true;
00406 } else if ( rc == KMessageBox::No ) {
00407 return true;
00408 } else {
00409 return false;
00410 }
00411 }
00412
00413 void KOGroupware::sendCounterProposal(KCal::Calendar *calendar, KCal::Event * oldEvent, KCal::Event * newEvent) const
00414 {
00415 if ( !oldEvent || !newEvent || *oldEvent == *newEvent || !KOPrefs::instance()->mUseGroupwareCommunication )
00416 return;
00417 if ( KOPrefs::instance()->outlookCompatCounterProposals() ) {
00418 Incidence* tmp = oldEvent->clone();
00419 tmp->setSummary( i18n("Counter proposal: %1").arg( newEvent->summary() ) );
00420 tmp->setDescription( newEvent->description() );
00421 tmp->addComment( i18n("Proposed new meeting time: %1 - %2").
00422 arg( IncidenceFormatter::dateToString( newEvent->dtStart() ),
00423 IncidenceFormatter::dateToString( newEvent->dtEnd() ) ) );
00424 KCal::MailScheduler scheduler( calendar );
00425 scheduler.performTransaction( tmp, Scheduler::Reply );
00426 delete tmp;
00427 } else {
00428 KCal::MailScheduler scheduler( calendar );
00429 scheduler.performTransaction( newEvent, Scheduler::Counter );
00430 }
00431 }
00432
00433 #include "kogroupware.moc"