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 )
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 );
00208 } else if ( action.startsWith( "cancel" ) )
00209
00210 scheduler.acceptTransaction( incidence, KCal::Scheduler::Cancel, status );
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 "
00300 "changed. Do you want to send a status update to the "
00301 "organizer of this event?" );
00302 method = Scheduler::Reply;
00303 rc = KMessageBox::questionYesNo( parent, txt, QString::null, i18n("Send Update"), i18n("Do Not Send") );
00304 } else {
00305 if( isDeleting ) {
00306 const QStringList myEmails = KOPrefs::instance()->allEmails();
00307 bool askConfirmation = false;
00308 for ( QStringList::ConstIterator it = myEmails.begin(); it != myEmails.end(); ++it ) {
00309 QString email = *it;
00310 Attendee *me = incidence->attendeeByMail(email);
00311 if (me && (me->status()==KCal::Attendee::Accepted || me->status()==KCal::Attendee::Delegated)) {
00312 askConfirmation = true;
00313 break;
00314 }
00315 }
00316
00317 if ( !askConfirmation ) {
00318 return true;
00319 }
00320
00321 txt = i18n( "You are not the organizer of this event, "
00322 "but you were supposed to attend. Do you really want "
00323 "to delete it and notify the organizer?" );
00324 } else {
00325 txt = i18n( "You are not the organizer of this event. "
00326 "Editing it will bring your calendar out of sync "
00327 "with the organizers calendar. Do you really want "
00328 "to edit it?" );
00329 }
00330 rc = KMessageBox::warningYesNo( parent, txt );
00331 return ( rc == KMessageBox::Yes );
00332 }
00333 } else {
00334 kdWarning(5850) << "Groupware messages for Journals are not implemented yet!" << endl;
00335 return true;
00336 }
00337
00338 if( rc == KMessageBox::Yes ) {
00339
00340
00341 if( incidence->summary().isEmpty() )
00342 incidence->setSummary( i18n("<No summary given>") );
00343
00344
00345 KCal::MailScheduler scheduler( mCalendar );
00346 scheduler.performTransaction( incidence, method );
00347
00348 return true;
00349 } else if( rc == KMessageBox::No )
00350 return true;
00351 else
00352 return false;
00353 }
00354
00355 void KOGroupware::sendCounterProposal(KCal::Calendar *calendar, KCal::Event * oldEvent, KCal::Event * newEvent) const
00356 {
00357 if ( !oldEvent || !newEvent || *oldEvent == *newEvent || !KOPrefs::instance()->mUseGroupwareCommunication )
00358 return;
00359 if ( KOPrefs::instance()->outlookCompatCounterProposals() ) {
00360 Incidence* tmp = oldEvent->clone();
00361 tmp->setSummary( i18n("Counter proposal: %1").arg( newEvent->summary() ) );
00362 tmp->setDescription( newEvent->description() );
00363 tmp->addComment( i18n("Proposed new meeting time: %1 - %2").arg( newEvent->dtStartStr(), newEvent->dtEndStr() ) );
00364 KCal::MailScheduler scheduler( calendar );
00365 scheduler.performTransaction( tmp, Scheduler::Reply );
00366 delete tmp;
00367 } else {
00368 KCal::MailScheduler scheduler( calendar );
00369 scheduler.performTransaction( newEvent, Scheduler::Counter );
00370 }
00371 }
00372
00373 #include "kogroupware.moc"