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 "koincidenceeditor.h"
00042 #include "koprefs.h"
00043 #include <kpimutils/email.h>
00044 #include <kcal/attendee.h>
00045 #include <kcal/journal.h>
00046 #include <kcal/incidenceformatter.h>
00047 #include <kdebug.h>
00048 #include <kmessagebox.h>
00049 #include <kstandarddirs.h>
00050 #include <kdirwatch.h>
00051 #include <QFile>
00052 #include <QRegExp>
00053 #include <QDir>
00054 #include <QTextStream>
00055 #include <QTimer>
00056
00057 FreeBusyManager *KOGroupware::mFreeBusyManager = 0;
00058
00059 KOGroupware *KOGroupware::mInstance = 0;
00060
00061 KOGroupware *KOGroupware::create( CalendarView *view,
00062 KCal::CalendarResources *calendar )
00063 {
00064 if ( !mInstance ) {
00065 mInstance = new KOGroupware( view, calendar );
00066 }
00067 return mInstance;
00068 }
00069
00070 KOGroupware *KOGroupware::instance()
00071 {
00072
00073 Q_ASSERT( mInstance );
00074 return mInstance;
00075 }
00076
00077 KOGroupware::KOGroupware( CalendarView *view, KCal::CalendarResources *cal )
00078 : QObject( 0 ), mView( view ), mCalendar( cal )
00079 {
00080 setObjectName( "kmgroupware_instance" );
00081
00082 KDirWatch *watcher = KDirWatch::self();
00083 watcher->addDir( KStandardDirs::locateLocal( "data", "korganizer/income.accepted/" ) );
00084 watcher->addDir( KStandardDirs::locateLocal( "data", "korganizer/income.tentative/" ) );
00085 watcher->addDir( KStandardDirs::locateLocal( "data", "korganizer/income.counter/" ) );
00086 watcher->addDir( KStandardDirs::locateLocal( "data", "korganizer/income.cancel/" ) );
00087 watcher->addDir( KStandardDirs::locateLocal( "data", "korganizer/income.reply/" ) );
00088 watcher->addDir( KStandardDirs::locateLocal( "data", "korganizer/income.delegated/" ) );
00089 connect( watcher, SIGNAL(dirty(const QString&)),
00090 this, SLOT(incomingDirChanged(const QString&)) );
00091
00092 QTimer::singleShot( 0, this, SLOT(initialCheckForChanges()) );
00093 }
00094
00095 void KOGroupware::initialCheckForChanges()
00096 {
00097 incomingDirChanged( KStandardDirs::locateLocal( "data", "korganizer/income.accepted/" ) );
00098 incomingDirChanged( KStandardDirs::locateLocal( "data", "korganizer/income.tentative/" ) );
00099 incomingDirChanged( KStandardDirs::locateLocal( "data", "korganizer/income.counter/" ) );
00100 incomingDirChanged( KStandardDirs::locateLocal( "data", "korganizer/income.cancel/" ) );
00101 incomingDirChanged( KStandardDirs::locateLocal( "data", "korganizer/income.reply/" ) );
00102 incomingDirChanged( KStandardDirs::locateLocal( "data", "korganizer/income.delegated/" ) );
00103
00104 if ( !mFreeBusyManager ) {
00105 mFreeBusyManager = new FreeBusyManager( this );
00106 mFreeBusyManager->setObjectName( "freebusymanager" );
00107 mFreeBusyManager->setCalendar( mCalendar );
00108 connect( mCalendar, SIGNAL(calendarChanged()),
00109 mFreeBusyManager, SLOT(slotPerhapsUploadFB()) );
00110 connect( mView, SIGNAL(newIncidenceChanger( IncidenceChangerBase*)),
00111 this, SLOT(slotViewNewIncidenceChanger(IncidenceChangerBase*)) );
00112 slotViewNewIncidenceChanger( mView->incidenceChanger() );
00113 }
00114 }
00115
00116 void KOGroupware::slotViewNewIncidenceChanger( IncidenceChangerBase *changer )
00117 {
00118
00119 connect( changer, SIGNAL(incidenceAdded(Incidence*)),
00120 mFreeBusyManager, SLOT(slotPerhapsUploadFB()) );
00121 connect( changer, SIGNAL(incidenceChanged(Incidence*,Incidence*,int)),
00122 mFreeBusyManager, SLOT(slotPerhapsUploadFB()) );
00123 connect( changer, SIGNAL(incidenceChanged(Incidence*,Incidence*)),
00124 mFreeBusyManager, SLOT(slotPerhapsUploadFB()) ) ;
00125 connect( changer, SIGNAL(incidenceDeleted(Incidence*)),
00126 mFreeBusyManager, SLOT(slotPerhapsUploadFB()) );
00127 }
00128
00129 FreeBusyManager *KOGroupware::freeBusyManager()
00130 {
00131 return mFreeBusyManager;
00132 }
00133
00134 void KOGroupware::incomingDirChanged( const QString &path )
00135 {
00136 const QString incomingDirName = KStandardDirs::locateLocal( "data","korganizer/" ) + "income.";
00137 if ( !path.startsWith( incomingDirName ) ) {
00138 kDebug() << "Wrong dir" << path;
00139 return;
00140 }
00141 QString action = path.mid( incomingDirName.length() );
00142 while ( action.length() > 0 && action[ action.length()-1 ] == '/' ) {
00143
00144 action.truncate( action.length() - 1 );
00145 }
00146
00147
00148 QDir dir( path );
00149 const QStringList files = dir.entryList( QDir::Files );
00150 if ( files.isEmpty() ) {
00151
00152 return;
00153 }
00154
00155 QFile f( path + '/' + files[0] );
00156 if ( !f.open( QIODevice::ReadOnly ) ) {
00157 kError() << "Can't open file '" << files[0] << "'";
00158 return;
00159 }
00160 QTextStream t( &f );
00161 t.setCodec( "UTF-8" );
00162 QString receiver = KPIMUtils::firstEmailAddress( t.readLine() );
00163 QString iCal = t.readAll();
00164
00165 f.remove();
00166
00167 ScheduleMessage *message = mFormat.parseScheduleMessage( mCalendar, iCal );
00168 if ( !message ) {
00169 QString errorMessage;
00170 if ( mFormat.exception() ) {
00171 errorMessage = i18n( "Error message: %1", mFormat.exception()->message() );
00172 }
00173 kDebug() << "Error parsing" << errorMessage;
00174 KMessageBox::detailedError( mView,
00175 i18n( "Error while processing an invitation or update." ),
00176 errorMessage );
00177 return;
00178 }
00179
00180 KCal::iTIPMethod method = static_cast<KCal::iTIPMethod>( message->method() );
00181 KCal::ScheduleMessage::Status status = message->status();
00182 KCal::Incidence *incidence = dynamic_cast<KCal::Incidence*>( message->event() );
00183 KCal::MailScheduler scheduler( mCalendar );
00184 if ( action.startsWith( "accepted" ) || action.startsWith( "tentative" ) ||
00185 action.startsWith( "delegated" ) || action.startsWith( "counter" ) ) {
00186
00187
00188 KCal::Attendee::List attendees = incidence->attendees();
00189 KCal::Attendee::List::ConstIterator it;
00190 for ( it = attendees.begin(); it != attendees.end(); ++it ) {
00191 if ( (*it)->email() == receiver ) {
00192 if ( action.startsWith( "accepted" ) ) {
00193 (*it)->setStatus( KCal::Attendee::Accepted );
00194 } else if ( action.startsWith( "tentative" ) ) {
00195 (*it)->setStatus( KCal::Attendee::Tentative );
00196 } else if ( KOPrefs::instance()->outlookCompatCounterProposals() && action.startsWith( "counter" ) ) {
00197 (*it)->setStatus( KCal::Attendee::Tentative );
00198 } else if ( action.startsWith( "delegated" ) ) {
00199 (*it)->setStatus( KCal::Attendee::Delegated );
00200 }
00201 break;
00202 }
00203 }
00204 if ( KOPrefs::instance()->outlookCompatCounterProposals() || !action.startsWith( "counter" ) )
00205 scheduler.acceptTransaction( incidence, method, status );
00206 } else if ( action.startsWith( "cancel" ) ) {
00207
00208 scheduler.acceptTransaction( incidence, KCal::iTIPCancel, status );
00209 } else if ( action.startsWith( "reply" ) ) {
00210 if ( method != iTIPCounter ) {
00211 scheduler.acceptTransaction( incidence, method, status );
00212 } else {
00213
00214 scheduler.acceptCounterProposal( incidence );
00215
00216 sendICalMessage( mView, iTIPRequest, incidence );
00217 }
00218 } else {
00219 kError() << "Unknown incoming action" << action;
00220 }
00221
00222 if ( action.startsWith( "counter" ) ) {
00223 mView->editIncidence( incidence, true );
00224 KOIncidenceEditor *tmp = mView->editorDialog( incidence );
00225 tmp->selectInvitationCounterProposal( true );
00226 }
00227 mView->updateView();
00228 delete message;
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::iTIPMethod method,
00245 Incidence *incidence, bool isDeleting,
00246 bool statusChanged )
00247 {
00248
00249 if ( incidence->attendees().isEmpty() ) {
00250 return true;
00251 }
00252
00253 bool isOrganizer = KOPrefs::instance()->thatIsMe( incidence->organizer().email() );
00254 int rc = 0;
00255
00256
00257
00258
00259
00260
00261
00262
00263
00264
00265
00266
00267
00268
00269 if ( isOrganizer ) {
00270
00271
00272
00273 if ( incidence->attendees().count() > 1 ||
00274 incidence->attendees().first()->email() != incidence->organizer().email() ) {
00275 QString type;
00276 if ( incidence->type() == "Event" ) {
00277 type = i18nc( "incidence type is event", "event" );
00278 } else if ( incidence->type() == "Todo" ) {
00279 type = i18nc( "incidence type is to-do/task", "task" );
00280 } else if ( incidence->type() == "Journal" ) {
00281 type = i18nc( "incidence type is journal", "journal entry" );
00282 } else {
00283 type = incidence->type();
00284 }
00285 QString txt = i18n( "This %1 includes other people. "
00286 "Should email be sent out to the attendees?",
00287 type );
00288 rc = KMessageBox::questionYesNoCancel(
00289 parent, txt, i18n( "Group Scheduling Email" ),
00290 KGuiItem( i18n( "Send Email" ) ), KGuiItem( i18n( "Do Not Send" ) ) );
00291 } else {
00292 return true;
00293 }
00294 } else if ( incidence->type() == "Todo" ) {
00295 if ( method == iTIPRequest ) {
00296
00297 method = iTIPReply;
00298 }
00299
00300 QString txt = i18n( "Do you want to send a status update to the "
00301 "organizer of this task?" );
00302 rc = KMessageBox::questionYesNo(
00303 parent, txt, QString(),
00304 KGuiItem( i18n( "Send Update" ) ), KGuiItem( i18n( "Do Not Send" ) ) );
00305 } else if ( incidence->type() == "Event" ) {
00306 QString txt;
00307 if ( statusChanged && method == iTIPRequest ) {
00308 txt = i18n( "Your status as an attendee of this event "
00309 "changed. Do you want to send a status update to the "
00310 "organizer of this event?" );
00311 method = iTIPReply;
00312 rc = KMessageBox::questionYesNo(
00313 parent, txt, QString(),
00314 KGuiItem( i18n( "Send Update" ) ), KGuiItem( i18n( "Do Not Send" ) ) );
00315 } else {
00316 if ( isDeleting ) {
00317 const QStringList myEmails = KOPrefs::instance()->allEmails();
00318 bool askConfirmation = false;
00319 for ( QStringList::ConstIterator it = myEmails.begin(); it != myEmails.end(); ++it ) {
00320 QString email = *it;
00321 Attendee *me = incidence->attendeeByMail(email);
00322 if (me && (me->status()==KCal::Attendee::Accepted || me->status()==KCal::Attendee::Delegated)) {
00323 askConfirmation = true;
00324 break;
00325 }
00326 }
00327
00328 if ( !askConfirmation ) {
00329 return true;
00330 }
00331
00332 txt = i18n( "You are not the organizer of this event, "
00333 "but you were supposed to attend. Do you really want "
00334 "to delete it and notify the organizer?" );
00335 } else {
00336 txt = i18n( "You are not the organizer of this event. "
00337 "Editing it will bring your calendar out of sync "
00338 "with the organizers calendar. Do you really want "
00339 "to edit it?" );
00340 }
00341 rc = KMessageBox::warningYesNo( parent, txt );
00342 return rc == KMessageBox::Yes;
00343 }
00344 } else {
00345 kWarning() << "Groupware messages for Journals are not implemented yet!";
00346 return true;
00347 }
00348
00349 if ( rc == KMessageBox::Yes ) {
00350
00351
00352 if ( incidence->summary().isEmpty() ) {
00353 incidence->setSummary( i18n( "<placeholder>No summary given</placeholder>" ) );
00354 }
00355
00356 KCal::MailScheduler scheduler( mCalendar );
00357 scheduler.performTransaction( incidence, method );
00358 return true;
00359 } else if ( rc == KMessageBox::No ) {
00360 return true;
00361 } else {
00362 return false;
00363 }
00364 }
00365
00366 void KOGroupware::sendCounterProposal(KCal::Calendar *calendar, KCal::Event * oldEvent, KCal::Event * newEvent) const
00367 {
00368 if ( !oldEvent || !newEvent || *oldEvent == *newEvent || !KOPrefs::instance()->mUseGroupwareCommunication )
00369 return;
00370 if ( KOPrefs::instance()->outlookCompatCounterProposals() ) {
00371 Incidence* tmp = oldEvent->clone();
00372 tmp->setSummary( i18n( "Counter proposal: %1", newEvent->summary() ) );
00373 tmp->setDescription( newEvent->description() );
00374 tmp->addComment( i18n( "Proposed new meeting time: %1 - %2",
00375 newEvent->dtStartStr(), newEvent->dtEndStr() ) );
00376 KCal::MailScheduler scheduler( calendar );
00377 scheduler.performTransaction( tmp, KCal::iTIPReply );
00378 delete tmp;
00379 } else {
00380 KCal::MailScheduler scheduler( calendar );
00381 scheduler.performTransaction( newEvent, iTIPCounter );
00382 }
00383 }
00384
00385 #include "kogroupware.moc"