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