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*, int ) ),
00109 mFreeBusyManager, SLOT( slotPerhapsUploadFB() ) );
00110 connect( changer, SIGNAL( incidenceChanged( Incidence*, Incidence* ) ),
00111 mFreeBusyManager, SLOT( slotPerhapsUploadFB() ) ) ;
00112 connect( changer, SIGNAL( incidenceDeleted( Incidence * ) ),
00113 mFreeBusyManager, SLOT( slotPerhapsUploadFB() ) );
00114 }
00115
00116 FreeBusyManager *KOGroupware::freeBusyManager()
00117 {
00118 if ( !mFreeBusyManager ) {
00119 mFreeBusyManager = new FreeBusyManager( this, "freebusymanager" );
00120 mFreeBusyManager->setCalendar( mCalendar );
00121 connect( mCalendar, SIGNAL( calendarChanged() ),
00122 mFreeBusyManager, SLOT( slotPerhapsUploadFB() ) );
00123 connect( mView, SIGNAL( newIncidenceChanger( IncidenceChangerBase* ) ),
00124 this, SLOT( slotViewNewIncidenceChanger( IncidenceChangerBase* ) ) );
00125 slotViewNewIncidenceChanger( mView->incidenceChanger() );
00126 }
00127
00128 return mFreeBusyManager;
00129 }
00130
00131 void KOGroupware::incomingDirChanged( const QString& path )
00132 {
00133 const QString incomingDirName = locateLocal( "data","korganizer/" )
00134 + "income.";
00135 if ( !path.startsWith( incomingDirName ) ) {
00136 kdDebug(5850) << "incomingDirChanged: Wrong dir " << path << endl;
00137 return;
00138 }
00139 QString action = path.mid( incomingDirName.length() );
00140 while ( action.length() > 0 && action[ action.length()-1 ] == '/' )
00141
00142 action.truncate( action.length()-1 );
00143
00144
00145 QDir dir( path );
00146 const QStringList files = dir.entryList( QDir::Files );
00147 if ( files.isEmpty() )
00148
00149 return;
00150
00151
00152 QFile f( path + "/" + files[0] );
00153 if (!f.open(IO_ReadOnly)) {
00154 kdError(5850) << "Can't open file '" << files[0] << "'" << endl;
00155 return;
00156 }
00157 QTextStream t(&f);
00158 t.setEncoding( QTextStream::UnicodeUTF8 );
00159 QString receiver = KPIM::getFirstEmailAddress( t.readLine() );
00160 QString iCal = t.read();
00161
00162 f.remove();
00163
00164 ScheduleMessage *message = mFormat.parseScheduleMessage( mCalendar, iCal );
00165 if ( !message ) {
00166 QString errorMessage;
00167 if (mFormat.exception())
00168 errorMessage = i18n( "Error message: %1" ).arg( mFormat.exception()->message() );
00169 kdDebug(5850) << "MailScheduler::retrieveTransactions() Error parsing "
00170 << errorMessage << endl;
00171 KMessageBox::detailedError( mView,
00172 i18n("Error while processing an invitation or update."),
00173 errorMessage );
00174 return;
00175 }
00176
00177 KCal::Scheduler::Method method =
00178 static_cast<KCal::Scheduler::Method>( message->method() );
00179 KCal::ScheduleMessage::Status status = message->status();
00180 KCal::Incidence* incidence =
00181 dynamic_cast<KCal::Incidence*>( message->event() );
00182 if(!incidence) {
00183 delete message;
00184 return;
00185 }
00186 KCal::MailScheduler scheduler( mCalendar );
00187 if ( action.startsWith( "accepted" ) || action.startsWith( "tentative" )
00188 || action.startsWith( "delegated" ) || action.startsWith( "counter" ) ) {
00189
00190
00191 KCal::Attendee::List attendees = incidence->attendees();
00192 KCal::Attendee::List::ConstIterator it;
00193 for ( it = attendees.begin(); it != attendees.end(); ++it ) {
00194 if( (*it)->email() == receiver ) {
00195 if ( action.startsWith( "accepted" ) )
00196 (*it)->setStatus( KCal::Attendee::Accepted );
00197 else if ( action.startsWith( "tentative" ) )
00198 (*it)->setStatus( KCal::Attendee::Tentative );
00199 else if ( KOPrefs::instance()->outlookCompatCounterProposals() && action.startsWith( "counter" ) )
00200 (*it)->setStatus( KCal::Attendee::Tentative );
00201 else if ( action.startsWith( "delegated" ) )
00202 (*it)->setStatus( KCal::Attendee::Delegated );
00203 break;
00204 }
00205 }
00206 if ( KOPrefs::instance()->outlookCompatCounterProposals() || !action.startsWith( "counter" ) )
00207 scheduler.acceptTransaction( incidence, method, status, receiver );
00208 } else if ( action.startsWith( "cancel" ) )
00209
00210 scheduler.acceptTransaction( incidence, KCal::Scheduler::Cancel, status, receiver );
00211 else if ( action.startsWith( "reply" ) ) {
00212 if ( method != Scheduler::Counter ) {
00213 scheduler.acceptTransaction( incidence, method, status );
00214 } else {
00215
00216 scheduler.acceptCounterProposal( incidence );
00217
00218 sendICalMessage( mView, Scheduler::Request, incidence );
00219 }
00220 } else
00221 kdError(5850) << "Unknown incoming action " << action << endl;
00222
00223 if ( action.startsWith( "counter" ) ) {
00224 mView->editIncidence( incidence, true );
00225 KOIncidenceEditor *tmp = mView->editorDialog( incidence );
00226 tmp->selectInvitationCounterProposal( true );
00227 }
00228 mView->updateView();
00229 }
00230
00231 class KOInvitationFormatterHelper : public InvitationFormatterHelper
00232 {
00233 public:
00234 virtual QString generateLinkURL( const QString &id ) { return "kmail:groupware_request_" + id; }
00235 };
00236
00237
00238
00239
00240
00241
00242
00243 bool KOGroupware::sendICalMessage( QWidget* parent,
00244 KCal::Scheduler::Method method,
00245 Incidence* incidence, bool isDeleting,
00246 bool statusChanged )
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 ( isOrganizer ) {
00269
00270
00271
00272 if ( incidence->attendees().count() > 1
00273 || incidence->attendees().first()->email() != incidence->organizer().email() ) {
00274 QString type;
00275 if( incidence->type() == "Event") type = i18n("event");
00276 else if( incidence->type() == "Todo" ) type = i18n("task");
00277 else if( incidence->type() == "Journal" ) type = i18n("journal entry");
00278 else type = incidence->type();
00279 QString txt = i18n( "This %1 includes other people. "
00280 "Should email be sent out to the attendees?" )
00281 .arg( type );
00282 rc = KMessageBox::questionYesNoCancel( parent, txt,
00283 i18n("Group Scheduling Email"), i18n("Send Email"), i18n("Do Not Send") );
00284 } else {
00285 return true;
00286 }
00287 } else if( incidence->type() == "Todo" ) {
00288 if( method == Scheduler::Request )
00289
00290 method = Scheduler::Reply;
00291
00292
00293 QString txt = i18n( "Do you want to send a status update to the "
00294 "organizer of this task?");
00295 rc = KMessageBox::questionYesNo( parent, txt, QString::null, i18n("Send Update"), i18n("Do Not Send") );
00296 } else if( incidence->type() == "Event" ) {
00297 QString txt;
00298 if ( statusChanged && method == Scheduler::Request ) {
00299 txt = i18n( "Your status as an attendee of this event changed. "
00300 "Do you want to send a status update to the event organizer?" );
00301 method = Scheduler::Reply;
00302 rc = KMessageBox::questionYesNo( parent, txt, QString::null, i18n("Send Update"), i18n("Do Not Send") );
00303 } else {
00304 if( isDeleting ) {
00305 const QStringList myEmails = KOPrefs::instance()->allEmails();
00306 bool askConfirmation = false;
00307 for ( QStringList::ConstIterator it = myEmails.begin(); it != myEmails.end(); ++it ) {
00308 QString email = *it;
00309 Attendee *me = incidence->attendeeByMail(email);
00310 if (me && (me->status()==KCal::Attendee::Accepted || me->status()==KCal::Attendee::Delegated)) {
00311 askConfirmation = true;
00312 break;
00313 }
00314 }
00315
00316 if ( !askConfirmation ) {
00317 return true;
00318 }
00319
00320 txt = i18n( "You had previously accepted an invitation to this event. "
00321 "Do you want to send an updated response to the organizer "
00322 "declining the invitation?" );
00323 rc = KMessageBox::questionYesNo(
00324 parent, txt, i18n( "Group Scheduling Email" ),
00325 KGuiItem( i18n( "Send Update" ) ), KGuiItem( i18n( "Do Not Send" ) ) );
00326 setDoNotNotify( rc == KMessageBox::No );
00327 } else {
00328 txt = i18n( "You are not the organizer of this event. Editing it will "
00329 "bring your calendar out of sync with the organizer's calendar. "
00330 "Do you really want to edit it?" );
00331 rc = KMessageBox::warningYesNo( parent, txt );
00332 return ( rc == KMessageBox::Yes );
00333 }
00334 }
00335 } else {
00336 kdWarning(5850) << "Groupware messages for Journals are not implemented yet!" << endl;
00337 return true;
00338 }
00339
00340 if ( rc == KMessageBox::Yes ) {
00341
00342
00343 if( incidence->summary().isEmpty() )
00344 incidence->setSummary( i18n("<No summary given>") );
00345
00346
00347 KCal::MailScheduler scheduler( mCalendar );
00348 scheduler.performTransaction( incidence, method );
00349
00350 return true;
00351 } else if ( rc == KMessageBox::No ) {
00352 return true;
00353 } else {
00354 return false;
00355 }
00356 }
00357
00358 void KOGroupware::sendCounterProposal(KCal::Calendar *calendar, KCal::Event * oldEvent, KCal::Event * newEvent) const
00359 {
00360 if ( !oldEvent || !newEvent || *oldEvent == *newEvent || !KOPrefs::instance()->mUseGroupwareCommunication )
00361 return;
00362 if ( KOPrefs::instance()->outlookCompatCounterProposals() ) {
00363 Incidence* tmp = oldEvent->clone();
00364 tmp->setSummary( i18n("Counter proposal: %1").arg( newEvent->summary() ) );
00365 tmp->setDescription( newEvent->description() );
00366 tmp->addComment( i18n("Proposed new meeting time: %1 - %2").arg( newEvent->dtStartStr(), newEvent->dtEndStr() ) );
00367 KCal::MailScheduler scheduler( calendar );
00368 scheduler.performTransaction( tmp, Scheduler::Reply );
00369 delete tmp;
00370 } else {
00371 KCal::MailScheduler scheduler( calendar );
00372 scheduler.performTransaction( newEvent, Scheduler::Counter );
00373 }
00374 }
00375
00376 #include "kogroupware.moc"