• Skip to content
  • Skip to link menu
KDE 4.2 API Reference
  • KDE API Reference
  • kdepim
  • Sitemap
  • Contact Us
 

korganizer

kogroupware.cpp

Go to the documentation of this file.
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., 51 Franklin Street, Fifth Floor, Boston,
00023   MA  02110-1301, 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 "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   // Doesn't create, that is the task of create()
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   // Set up the dir watch of the three incoming dirs
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   // Now set the ball rolling
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   // Call slot perhapsUploadFB if an incidence was added, changed or removed
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     // Strip slashes at the end
00144     action.truncate( action.length() - 1 );
00145   }
00146 
00147   // Handle accepted invitations
00148   QDir dir( path );
00149   const QStringList files = dir.entryList( QDir::Files );
00150   if ( files.isEmpty() ) {
00151     // No more files here
00152     return;
00153   }
00154   // Read the file and remove it
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     // Find myself and set my status. This can't be done in the scheduler,
00187     // since this does not know the choice I made in the KMail bpf
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     // Delete the old incidence, if one is present
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       // accept counter proposal
00214       scheduler.acceptCounterProposal( incidence );
00215       // send update to all attendees
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 /* This function sends mails if necessary, and makes sure the user really
00238  * want to change his calendar.
00239  *
00240  * Return true means accept the changes
00241  * Return false means revert the changes
00242  */
00243 bool KOGroupware::sendICalMessage( QWidget *parent,
00244                                    KCal::iTIPMethod method,
00245                                    Incidence *incidence, bool isDeleting,
00246                                    bool statusChanged )
00247 {
00248   // If there are no attendees, don't bother
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    * There are two scenarios:
00257    * o "we" are the organizer, where "we" means any of the identities or mail
00258    *   addresses known to Kontact/PIM. If there are attendees, we need to mail
00259    *   them all, even if one or more of them are also "us". Otherwise there
00260    *   would be no way to invite a resource or our boss, other identities we
00261    *   also manage.
00262    * o "we: are not the organizer, which means we changed the completion status
00263    *   of a todo, or we changed our attendee status from, say, tentative to
00264    *   accepted. In both cases we only mail the organizer. All other changes
00265    *   bring us out of sync with the organizer, so we won't mail, if the user
00266    *   insists on applying them.
00267    */
00268 
00269   if ( isOrganizer ) {
00270     /* We are the organizer. If there is more than one attendee, or if there is
00271      * only one, and it's not the same as the organizer, ask the user to send
00272      * mail. */
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       // This is an update to be sent to the organizer
00297       method = iTIPReply;
00298     }
00299     // Ask if the user wants to tell the organizer about the current status
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     // We will be sending out a message here. Now make sure there is
00351     // some summary
00352     if ( incidence->summary().isEmpty() ) {
00353       incidence->setSummary( i18n( "<placeholder>No summary given</placeholder>" ) );
00354     }
00355     // Send the mail
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"

korganizer

Skip menu "korganizer"
  • Main Page
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Class Members

kdepim

Skip menu "kdepim"
  • akonadi
  •   clients
  •   kabc
  •   kcal
  •   kcm
  • akregator
  • console
  •   kabcclient
  •   konsolekalendar
  • kaddressbook
  • kalarm
  •   lib
  • kdgantt
  • kdgantt1
  • kjots
  • kleopatra
  • kmail
  • kmobiletools
  • knode
  • knotes
  • kontact
  • kontactinterfaces
  • korganizer
  •   korgac
  • kpilot
  • ktimetracker
  • libkdepim
  • libkholidays
  • libkleo
  • libkpgp
  • maildir
Generated for kdepim by doxygen 1.5.4
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal