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 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, SIGNAL( incidenceAdded( Incidence* ) ),
00107 mFreeBusyManager, SLOT( slotPerhapsUploadFB() ) );
00108 connect( changer, SIGNAL( incidenceChanged( Incidence*, Incidence*, KOGlobals::WhatChanged ) ),
00109 mFreeBusyManager, SLOT( slotPerhapsUploadFB() ) );
00110 connect( changer, SIGNAL( incidenceDeleted( Incidence * ) ),
00111 mFreeBusyManager, 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, SIGNAL( calendarChanged() ),
00120 mFreeBusyManager, SLOT( slotPerhapsUploadFB() ) );
00121 connect( mView, SIGNAL( newIncidenceChanger( IncidenceChangerBase* ) ),
00122 this, SLOT( slotViewNewIncidenceChanger( IncidenceChangerBase* ) ) );
00123 slotViewNewIncidenceChanger( mView->incidenceChanger() );
00124 }
00125
00126 return mFreeBusyManager;
00127 }
00128
00129 void KOGroupware::incomingDirChanged( const QString& path )
00130 {
00131 const QString incomingDirName = locateLocal( "data","korganizer/" )
00132 + "income.";
00133 if ( !path.startsWith( incomingDirName ) ) {
00134 kdDebug(5850) << "incomingDirChanged: Wrong dir " << path << endl;
00135 return;
00136 }
00137 QString action = path.mid( incomingDirName.length() );
00138 while ( action.length() > 0 && action[ action.length()-1 ] == '/' )
00139
00140 action.truncate( action.length()-1 );
00141
00142
00143 QDir dir( path );
00144 const QStringList files = dir.entryList( QDir::Files );
00145 if ( files.isEmpty() )
00146
00147 return;
00148
00149
00150 QFile f( path + "/" + files[0] );
00151 if (!f.open(IO_ReadOnly)) {
00152 kdError(5850) << "Can't open file '" << files[0] << "'" << endl;
00153 return;
00154 }
00155 QTextStream t(&f);
00156 t.setEncoding( QTextStream::UnicodeUTF8 );
00157 QString receiver = KPIM::getFirstEmailAddress( t.readLine() );
00158 QString iCal = t.read();
00159
00160 f.remove();
00161
00162 ScheduleMessage *message = mFormat.parseScheduleMessage( mCalendar, iCal );
00163 if ( !message ) {
00164 QString 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, QDate(), 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 QString generateLinkURL( const QString &id ) { return "kmail:groupware_request_" + id; }
00233 };
00234
00235
00236
00237
00238
00239
00240
00241 bool KOGroupware::sendICalMessage( QWidget* parent,
00242 KCal::Scheduler::Method method,
00243 Incidence* incidence,
00244 KOGlobals::HowChanged action,
00245 bool attendeeStatusChanged )
00246 {
00247
00248 if( incidence->attendees().isEmpty() )
00249 return true;
00250
00251 bool isOrganizer = KOPrefs::instance()->thatIsMe( incidence->organizer().email() );
00252 int rc = 0;
00253
00254
00255
00256
00257
00258
00259
00260
00261
00262
00263
00264
00265
00266
00267 if ( isOrganizer ) {
00268
00269
00270
00271 if ( incidence->attendees().count() > 1
00272 || incidence->attendees().first()->email() != incidence->organizer().email() ) {
00273
00274 QString txt;
00275 switch( action ) {
00276 case KOGlobals::INCIDENCEEDITED:
00277 txt = i18n( "You changed the invitation \"%1\".\n"
00278 "Do you want to email the attendees an update message?" ).
00279 arg( incidence->summary() );
00280 break;
00281 case KOGlobals::INCIDENCEDELETED:
00282 Q_ASSERT( incidence->type() == "Event" || incidence->type() == "Todo" );
00283 if ( incidence->type() == "Event" ) {
00284 txt = i18n( "You removed the invitation \"%1\".\n"
00285 "Do you want to email the attendees that the event is canceled?" ).
00286 arg( incidence->summary() );
00287 }
00288 else if ( incidence->type() == "Todo" ) {
00289 txt = i18n( "You removed the invitation \"%1\".\n"
00290 "Do you want to email the attendees that the todo is canceled?" ).
00291 arg( incidence->summary() );
00292 }
00293 break;
00294 case KOGlobals::INCIDENCEADDED:
00295 if ( incidence->type() == "Event" ) {
00296 txt = i18n( "The event \"%1\" includes other people.\n"
00297 "Do you want to email the invitation to the attendees?" ).
00298 arg( incidence->summary() );
00299 }
00300 else if ( incidence->type() == "Todo" ) {
00301 txt = i18n( "The todo \"%1\" includes other people.\n"
00302 "Do you want to email the invitation to the attendees?" ).
00303 arg( incidence->summary() );
00304 } else {
00305 QString type = incidence->type();
00306 txt = i18n( "This incidence includes other people. "
00307 "Should an email be sent to the attendees?" );
00308 }
00309 break;
00310 default:
00311 kdError() << "Unsupported HowChanged action" << int( action ) << endl;
00312 break;
00313 }
00314
00315 rc = KMessageBox::questionYesNoCancel(
00316 parent, txt, i18n( "Group Scheduling Email" ),
00317 KGuiItem( i18n( "Send Email" ) ), KGuiItem( i18n( "Do Not Send" ) ) );
00318 } else {
00319 return true;
00320 }
00321 } else if( incidence->type() == "Todo" ) {
00322 if( method == Scheduler::Request )
00323
00324 method = Scheduler::Reply;
00325
00326
00327 QString txt = i18n( "Do you want to send a status update to the "
00328 "organizer of this task?");
00329 rc = KMessageBox::questionYesNo( parent, txt, QString::null, i18n("Send Update"), i18n("Do Not Send") );
00330 } else if( incidence->type() == "Event" ) {
00331 QString txt;
00332 if ( attendeeStatusChanged && method == Scheduler::Request ) {
00333 txt = i18n( "Your status as an attendee of this event changed. "
00334 "Do you want to send a status update to the event organizer?" );
00335 method = Scheduler::Reply;
00336 rc = KMessageBox::questionYesNo( parent, txt, QString::null, i18n("Send Update"), i18n("Do Not Send") );
00337 } else {
00338 if( action == KOGlobals::INCIDENCEDELETED ) {
00339 const QStringList myEmails = KOPrefs::instance()->allEmails();
00340 bool askConfirmation = false;
00341 for ( QStringList::ConstIterator it = myEmails.begin(); it != myEmails.end(); ++it ) {
00342 QString email = *it;
00343 Attendee *me = incidence->attendeeByMail(email);
00344 if (me && (me->status()==KCal::Attendee::Accepted || me->status()==KCal::Attendee::Delegated)) {
00345 askConfirmation = true;
00346 break;
00347 }
00348 }
00349
00350 if ( !askConfirmation ) {
00351 return true;
00352 }
00353
00354 txt = i18n( "You had previously accepted an invitation to this event. "
00355 "Do you want to send an updated response to the organizer "
00356 "declining the invitation?" );
00357 rc = KMessageBox::questionYesNo(
00358 parent, txt, i18n( "Group Scheduling Email" ),
00359 KGuiItem( i18n( "Send Update" ) ), KGuiItem( i18n( "Do Not Send" ) ) );
00360 setDoNotNotify( rc == KMessageBox::No );
00361 } else {
00362 txt = i18n( "You are not the organizer of this event. Editing it will "
00363 "bring your calendar out of sync with the organizer's calendar. "
00364 "Do you really want to edit it?" );
00365 rc = KMessageBox::warningYesNo( parent, txt );
00366 return ( rc == KMessageBox::Yes );
00367 }
00368 }
00369 } else {
00370 kdWarning(5850) << "Groupware messages for Journals are not implemented yet!" << endl;
00371 return true;
00372 }
00373
00374 if ( rc == KMessageBox::Yes ) {
00375
00376
00377 if( incidence->summary().isEmpty() )
00378 incidence->setSummary( i18n("<No summary given>") );
00379
00380
00381 KCal::MailScheduler scheduler( mCalendar );
00382 scheduler.performTransaction( incidence, method );
00383
00384 return true;
00385 } else if ( rc == KMessageBox::No ) {
00386 return true;
00387 } else {
00388 return false;
00389 }
00390 }
00391
00392 void KOGroupware::sendCounterProposal(KCal::Calendar *calendar, KCal::Event * oldEvent, KCal::Event * newEvent) const
00393 {
00394 if ( !oldEvent || !newEvent || *oldEvent == *newEvent || !KOPrefs::instance()->mUseGroupwareCommunication )
00395 return;
00396 if ( KOPrefs::instance()->outlookCompatCounterProposals() ) {
00397 Incidence* tmp = oldEvent->clone();
00398 tmp->setSummary( i18n("Counter proposal: %1").arg( newEvent->summary() ) );
00399 tmp->setDescription( newEvent->description() );
00400 tmp->addComment( i18n("Proposed new meeting time: %1 - %2").arg( newEvent->dtStartStr(), newEvent->dtEndStr() ) );
00401 KCal::MailScheduler scheduler( calendar );
00402 scheduler.performTransaction( tmp, Scheduler::Reply );
00403 delete tmp;
00404 } else {
00405 KCal::MailScheduler scheduler( calendar );
00406 scheduler.performTransaction( newEvent, Scheduler::Counter );
00407 }
00408 }
00409
00410 #include "kogroupware.moc"