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 KCal::MailScheduler scheduler( mCalendar );
00183 if ( action.startsWith( "accepted" ) || action.startsWith( "tentative" )
00184 || action.startsWith( "delegated" ) || action.startsWith( "counter" ) ) {
00185
00186
00187 KCal::Attendee::List attendees = incidence->attendees();
00188 KCal::Attendee::List::ConstIterator it;
00189 for ( it = attendees.begin(); it != attendees.end(); ++it ) {
00190 if( (*it)->email() == receiver ) {
00191 if ( action.startsWith( "accepted" ) )
00192 (*it)->setStatus( KCal::Attendee::Accepted );
00193 else if ( action.startsWith( "tentative" ) )
00194 (*it)->setStatus( KCal::Attendee::Tentative );
00195 else if ( KOPrefs::instance()->outlookCompatCounterProposals() && action.startsWith( "counter" ) )
00196 (*it)->setStatus( KCal::Attendee::Tentative );
00197 else if ( action.startsWith( "delegated" ) )
00198 (*it)->setStatus( KCal::Attendee::Delegated );
00199 break;
00200 }
00201 }
00202 if ( KOPrefs::instance()->outlookCompatCounterProposals() || !action.startsWith( "counter" ) )
00203 scheduler.acceptTransaction( incidence, method, status );
00204 } else if ( action.startsWith( "cancel" ) )
00205
00206 scheduler.acceptTransaction( incidence, KCal::Scheduler::Cancel, status );
00207 else if ( action.startsWith( "reply" ) ) {
00208 if ( method != Scheduler::Counter ) {
00209 scheduler.acceptTransaction( incidence, method, status );
00210 } else {
00211
00212 scheduler.acceptCounterProposal( incidence );
00213
00214 sendICalMessage( mView, Scheduler::Request, incidence );
00215 }
00216 } else
00217 kdError(5850) << "Unknown incoming action " << action << endl;
00218
00219 if ( action.startsWith( "counter" ) ) {
00220 mView->editIncidence( incidence, true );
00221 KOIncidenceEditor *tmp = mView->editorDialog( incidence );
00222 tmp->selectInvitationCounterProposal( true );
00223 }
00224 mView->updateView();
00225 }
00226
00227 class KOInvitationFormatterHelper : public InvitationFormatterHelper
00228 {
00229 public:
00230 virtual QString generateLinkURL( const QString &id ) { return "kmail:groupware_request_" + id; }
00231 };
00232
00233
00234
00235
00236
00237
00238
00239 bool KOGroupware::sendICalMessage( QWidget* parent,
00240 KCal::Scheduler::Method method,
00241 Incidence* incidence, bool isDeleting,
00242 bool statusChanged )
00243 {
00244
00245 if( incidence->attendees().isEmpty() )
00246 return true;
00247
00248 bool isOrganizer = KOPrefs::instance()->thatIsMe( incidence->organizer().email() );
00249 int rc = 0;
00250
00251
00252
00253
00254
00255
00256
00257
00258
00259
00260
00261
00262
00263
00264 if ( isOrganizer ) {
00265
00266
00267
00268 if ( incidence->attendees().count() > 1
00269 || incidence->attendees().first()->email() != incidence->organizer().email() ) {
00270 QString type;
00271 if( incidence->type() == "Event") type = i18n("event");
00272 else if( incidence->type() == "Todo" ) type = i18n("task");
00273 else if( incidence->type() == "Journal" ) type = i18n("journal entry");
00274 else type = incidence->type();
00275 QString txt = i18n( "This %1 includes other people. "
00276 "Should email be sent out to the attendees?" )
00277 .arg( type );
00278 rc = KMessageBox::questionYesNoCancel( parent, txt,
00279 i18n("Group Scheduling Email"), i18n("Send Email"), i18n("Do Not Send") );
00280 } else {
00281 return true;
00282 }
00283 } else if( incidence->type() == "Todo" ) {
00284 if( method == Scheduler::Request )
00285
00286 method = Scheduler::Reply;
00287
00288
00289 QString txt = i18n( "Do you want to send a status update to the "
00290 "organizer of this task?");
00291 rc = KMessageBox::questionYesNo( parent, txt, QString::null, i18n("Send Update"), i18n("Do Not Send") );
00292 } else if( incidence->type() == "Event" ) {
00293 QString txt;
00294 if ( statusChanged && method == Scheduler::Request ) {
00295 txt = i18n( "Your status as an attendee of this event "
00296 "changed. Do you want to send a status update to the "
00297 "organizer of this event?" );
00298 method = Scheduler::Reply;
00299 rc = KMessageBox::questionYesNo( parent, txt, QString::null, i18n("Send Update"), i18n("Do Not Send") );
00300 } else {
00301 if( isDeleting )
00302 txt = i18n( "You are not the organizer of this event. "
00303 "Deleting it will bring your calendar out of sync "
00304 "with the organizers calendar. Do you really want "
00305 "to delete it?" );
00306 else
00307 txt = i18n( "You are not the organizer of this event. "
00308 "Editing it will bring your calendar out of sync "
00309 "with the organizers calendar. Do you really want "
00310 "to edit it?" );
00311 rc = KMessageBox::warningYesNo( parent, txt );
00312 return ( rc == KMessageBox::Yes );
00313 }
00314 } else {
00315 kdWarning(5850) << "Groupware messages for Journals are not implemented yet!" << endl;
00316 return true;
00317 }
00318
00319 if( rc == KMessageBox::Yes ) {
00320
00321
00322 if( incidence->summary().isEmpty() )
00323 incidence->setSummary( i18n("<No summary given>") );
00324
00325
00326 KCal::MailScheduler scheduler( mCalendar );
00327 scheduler.performTransaction( incidence, method );
00328
00329 return true;
00330 } else if( rc == KMessageBox::No )
00331 return true;
00332 else
00333 return false;
00334 }
00335
00336 void KOGroupware::sendCounterProposal(KCal::Calendar *calendar, KCal::Event * oldEvent, KCal::Event * newEvent) const
00337 {
00338 if ( !oldEvent || !newEvent || *oldEvent == *newEvent || !KOPrefs::instance()->mUseGroupwareCommunication )
00339 return;
00340 if ( KOPrefs::instance()->outlookCompatCounterProposals() ) {
00341 Incidence* tmp = oldEvent->clone();
00342 tmp->setSummary( i18n("Counter proposal: %1").arg( newEvent->summary() ) );
00343 tmp->setDescription( newEvent->description() );
00344 tmp->addComment( i18n("Proposed new meeting time: %1 - %2").arg( newEvent->dtStartStr(), newEvent->dtEndStr() ) );
00345 KCal::MailScheduler scheduler( calendar );
00346 scheduler.performTransaction( tmp, Scheduler::Reply );
00347 delete tmp;
00348 } else {
00349 KCal::MailScheduler scheduler( calendar );
00350 scheduler.performTransaction( newEvent, Scheduler::Counter );
00351 }
00352 }
00353
00354 #include "kogroupware.moc"