korganizer Library API Documentation

kogroupware.cpp

00001 /*
00002   This file is part of the Groupware/KOrganizer integration.
00003 
00004   Requires the Qt and KDE widget libraries, available at no cost at
00005   http://www.trolltech.com and http://www.kde.org respectively
00006 
00007   Copyright (c) 2002-2004 Klarälvdalens Datakonsult AB
00008         <info@klaralvdalens-datakonsult.se>
00009 
00010   This program is free software; you can redistribute it and/or modify
00011   it under the terms of the GNU General Public License as published by
00012   the Free Software Foundation; either version 2 of the License, or
00013   (at your option) any later version.
00014 
00015   This program is distributed in the hope that it will be useful,
00016   but WITHOUT ANY WARRANTY; without even the implied warranty of
00017   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
00018   GNU General Public License for more details.
00019 
00020   You should have received a copy of the GNU General Public License
00021   along with this program; if not, write to the Free Software
00022   Foundation, Inc., 59 Temple Place - Suite 330, Boston,
00023   MA  02111-1307, USA.
00024 
00025   In addition, as a special exception, the copyright holders give
00026   permission to link the code of this program with any edition of
00027   the Qt library by Trolltech AS, Norway (or with modified versions
00028   of Qt that use the same license as Qt), and distribute linked
00029   combinations including the two.  You must obey the GNU General
00030   Public License in all respects for all of the code used other than
00031   Qt.  If you modify this file, you may extend this exception to
00032   your version of the file, but you are not obligated to do so.  If
00033   you do not wish to do so, delete this exception statement from
00034   your version.
00035 */
00036 
00037 #include "kogroupware.h"
00038 #include "freebusymanager.h"
00039 #include "calendarview.h"
00040 #include "mailscheduler.h"
00041 #include "koprefs.h"
00042 #include <libkdepim/email.h>
00043 #include <libkcal/attendee.h>
00044 #include <libkcal/journal.h>
00045 #include <kdebug.h>
00046 #include <kmessagebox.h>
00047 #include <kstandarddirs.h>
00048 #include <kdirwatch.h>
00049 #include <qfile.h>
00050 #include <qregexp.h>
00051 #include <qdir.h>
00052 #include <qtimer.h>
00053 
00054 FreeBusyManager *KOGroupware::mFreeBusyManager = 0;
00055 
00056 KOGroupware *KOGroupware::mInstance = 0;
00057 
00058 KOGroupware *KOGroupware::create( CalendarView *view,
00059                                   KCal::CalendarResources *calendar )
00060 {
00061   if( !mInstance )
00062     mInstance = new KOGroupware( view, calendar );
00063   return mInstance;
00064 }
00065 
00066 KOGroupware *KOGroupware::instance()
00067 {
00068   // Doesn't create, that is the task of create()
00069   Q_ASSERT( mInstance );
00070   return mInstance;
00071 }
00072 
00073 
00074 KOGroupware::KOGroupware( CalendarView* view, KCal::CalendarResources* cal )
00075   : QObject( 0, "kmgroupware_instance" ), mView( view ), mCalendar( cal )
00076 {
00077   // Temporary hack: Wait one minute before starting the scheduling handling
00078   // This is because we need the full calendars to be loaded first
00079   // TODO: GET RID OF THIS!!! The real fix is to listen for the resources
00080   // being all done loading and start the scheduling after that. Even
00081   // better fix would be that we would be able to locate the resource
00082   // an event is saved in even though the resource is deactivated
00083   //QTimer::singleShot( 60000, this, SLOT( slotStartScheduling() ) );
00084 
00085   // It could be that the hack ^ is not needed after all. Test that for
00086   // a while. *sigh* - experimenting programming is bad
00087   slotStartScheduling();
00088 }
00089 
00090 FreeBusyManager *KOGroupware::freeBusyManager()
00091 {
00092   if ( !mFreeBusyManager ) {
00093     mFreeBusyManager = new FreeBusyManager( this, "freebusymanager" );
00094     mFreeBusyManager->setCalendar( mCalendar );
00095     connect( mCalendar, SIGNAL( calendarChanged() ),
00096              mFreeBusyManager, SLOT( slotPerhapsUploadFB() ) );
00097   }
00098 
00099   return mFreeBusyManager;
00100 }
00101 
00102 void KOGroupware::slotStartScheduling()
00103 {
00104   // Set up the dir watch of the three incoming dirs
00105   KDirWatch* watcher = KDirWatch::self();
00106   watcher->addDir( locateLocal( "data", "korganizer/income.accepted/" ) );
00107   watcher->addDir( locateLocal( "data", "korganizer/income.tentative/" ) );
00108   watcher->addDir( locateLocal( "data", "korganizer/income.cancel/" ) );
00109   watcher->addDir( locateLocal( "data", "korganizer/income.reply/" ) );
00110   connect( watcher, SIGNAL( dirty( const QString& ) ),
00111            this, SLOT( incomingDirChanged( const QString& ) ) );
00112   // Now set the ball rolling
00113   incomingDirChanged( locateLocal( "data", "korganizer/income.accepted/" ) );
00114   incomingDirChanged( locateLocal( "data", "korganizer/income.tentative/" ) );
00115   incomingDirChanged( locateLocal( "data", "korganizer/income.cancel/" ) );
00116   incomingDirChanged( locateLocal( "data", "korganizer/income.reply/" ) );
00117 }
00118 
00119 void KOGroupware::incomingDirChanged( const QString& path )
00120 {
00121   const QString incomingDirName = locateLocal( "data","korganizer/" )
00122                                   + "income.";
00123   if ( !path.startsWith( incomingDirName ) ) {
00124     //kdDebug(5850) << "incomingDirChanged: Wrong dir " << path << endl;
00125     return;
00126   }
00127   QString action = path.mid( incomingDirName.length() );
00128   while ( action.length() > 0 && action[ action.length()-1 ] == '/' )
00129     // Strip slashes at the end
00130     action.truncate( action.length()-1 );
00131 
00132   // Handle accepted invitations
00133   QDir dir( path );
00134   const QStringList files = dir.entryList( QDir::Files );
00135   if ( files.isEmpty() )
00136     // No more files here
00137     return;
00138 
00139   // Read the file and remove it
00140   QFile f( path + "/" + files[0] );
00141   if (!f.open(IO_ReadOnly)) {
00142     kdError(5850) << "Can't open file '" << files[0] << "'" << endl;
00143     return;
00144   }
00145   QTextStream t(&f);
00146   t.setEncoding( QTextStream::UnicodeUTF8 );
00147   // Read the receiver email address
00148   QString receiver = t.readLine();
00149   if ( receiver == "Receiver Not Searched" )
00150     // The receiver was never checked
00151     receiver = QString::null;
00152   else
00153     // There should be an email address in here
00154     receiver = KPIM::getEmailAddr( receiver );
00155   // Read the iCal
00156   QString iCal = t.read();
00157 
00158   f.remove();
00159 
00160   kdDebug(5850) << "Received this for scheduling (" << action << ")\n"
00161                 << "Receiver: " << receiver << "\niCal:\n" << iCal << endl;
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   KCal::MailScheduler scheduler( mCalendar );
00181   if ( action.startsWith( "accepted" ) || action.startsWith( "tentative" ) ) {
00182     // Find myself and set my status. This can't be done in the scheduler,
00183     // since this does not know the choice I made in the KMail bpf
00184     KCal::Attendee::List attendees = incidence->attendees();
00185     KCal::Attendee::List::ConstIterator it;
00186     for ( it = attendees.begin(); it != attendees.end(); ++it ) {
00187       if( (*it)->email() == receiver ) {
00188         if ( action.startsWith( "accepted" ) )
00189           (*it)->setStatus( KCal::Attendee::Accepted );
00190         else
00191           (*it)->setStatus( KCal::Attendee::Tentative );
00192         break;
00193       }
00194     }
00195     scheduler.acceptTransaction( incidence, method, status, receiver );
00196   } else if ( action.startsWith( "cancel" ) )
00197     // Delete the old incidence, if one is present
00198     scheduler.acceptTransaction( incidence, KCal::Scheduler::Cancel, status );
00199   else if ( action.startsWith( "reply" ) )
00200     scheduler.acceptTransaction( incidence, method, status );
00201   else
00202     kdError(5850) << "Unknown incoming action " << action << endl;
00203   mView->updateView();
00204 }
00205 
00206 /* This function sends mails if necessary, and makes sure the user really
00207  * want to change his calendar.
00208  *
00209  * Return true means accept the changes
00210  * Return false means revert the changes
00211  */
00212 bool KOGroupware::sendICalMessage( QWidget* parent,
00213                                    KCal::Scheduler::Method method,
00214                                    Incidence* incidence, bool isDeleting,
00215                                    bool statusChanged )
00216 {
00217   // If there are no attendees, don't bother
00218   if( incidence->attendees().isEmpty() )
00219     return true;
00220 
00221   bool isOrganizer = KOPrefs::instance()->thatIsMe( incidence->organizer().email() );
00222   int rc = 0;
00223   /*
00224    * There are two scenarios:
00225    * o "we" are the organizer, where "we" means any of the identities or mail
00226    *   addresses known to Kontact/PIM. If there are attendees, we need to mail
00227    *   them all, even if one or more of them are also "us". Otherwise there
00228    *   would be no way to invite a resource or our boss, other identities we
00229    *   also manage.
00230    * o "we: are not the organizer, which means we changed the completion status
00231    *   of a todo, or we changed our attendee status from, say, tentative to
00232    *   accepted. In both cases we only mail the organizer. All other changes
00233    *   bring us out of sync with the organizer, so we won't mail, if the user
00234    *   insists on applying them.
00235    */
00236 
00237   if ( isOrganizer ) {
00238     /* We are the organizer. If there is more than one attendee, or if there is
00239      * only one, and it's not the same as the organizer, ask the user to send
00240      * mail. */
00241     if ( incidence->attendees().count() > 1
00242         || incidence->attendees().first()->email() != incidence->organizer().email() ) {
00243       QString type;
00244       if( incidence->type() == "Event") type = i18n("event");
00245       else if( incidence->type() == "Todo" ) type = i18n("task");
00246       else if( incidence->type() == "Journal" ) type = i18n("journal entry");
00247       else type = incidence->type();
00248       QString txt = i18n( "This %1 includes other people. "
00249           "Should email be sent out to the attendees?" )
00250         .arg( type );
00251       rc = KMessageBox::questionYesNoCancel( parent, txt,
00252           i18n("Group scheduling email") );
00253     } else {
00254       return true;
00255     }
00256   } else if( incidence->type() == "Todo" ) {
00257     if( method == Scheduler::Request )
00258       // This is an update to be sent to the organizer
00259       method = Scheduler::Reply;
00260 
00261     // Ask if the user wants to tell the organizer about the current status
00262     QString txt = i18n( "Do you want to send a status update to the "
00263                         "organizer of this task?");
00264     rc = KMessageBox::questionYesNo( parent, txt );
00265   } else if( incidence->type() == "Event" ) {
00266     QString txt;
00267     if ( statusChanged && method == Scheduler::Request ) {
00268       txt = i18n( "Your status as an attendee of this event "
00269           "changed. Do you want to send a status update to the "
00270           "organizer of this event?" );
00271       method = Scheduler::Reply;
00272       rc = KMessageBox::questionYesNo( parent, txt );
00273     } else {
00274       if( isDeleting )
00275         txt = i18n( "You are not the organizer of this event. "
00276             "Deleting it will bring your calendar out of sync "
00277             "with the organizers calendar. Do you really want "
00278             "to delete it?" );
00279       else
00280         txt = i18n( "You are not the organizer of this event. "
00281             "Editing it will bring your calendar out of sync "
00282             "with the organizers calendar. Do you really want "
00283             "to edit it?" );
00284       rc = KMessageBox::questionYesNo( parent, txt );
00285       return ( rc == KMessageBox::Yes );
00286     }
00287   } else {
00288     kdWarning(5850) << "Groupware messages for Journals are not implemented yet!" << endl;
00289     return true;
00290   }
00291   if( rc == KMessageBox::Yes ) {
00292     // We will be sending out a message here. Now make sure there is
00293     // some summary
00294     if( incidence->summary().isEmpty() )
00295       incidence->setSummary( i18n("<No summary given>") );
00296 
00297     // Send the mail
00298     KCal::MailScheduler scheduler( mCalendar );
00299     scheduler.performTransaction( incidence, method );
00300 
00301     return true;
00302   } else if( rc == KMessageBox::No )
00303     return true;
00304   else
00305     return false;
00306 }
00307 
00308 
00309 #include "kogroupware.moc"
KDE Logo
This file is part of the documentation for korganizer Library Version 3.3.2.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Thu May 3 20:24:55 2007 by doxygen 1.4.2 written by Dimitri van Heesch, © 1997-2003