summaryrefslogtreecommitdiffstats
path: root/tderesources/kolab/kcal/resourcekolab.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'tderesources/kolab/kcal/resourcekolab.cpp')
-rw-r--r--tderesources/kolab/kcal/resourcekolab.cpp1342
1 files changed, 1342 insertions, 0 deletions
diff --git a/tderesources/kolab/kcal/resourcekolab.cpp b/tderesources/kolab/kcal/resourcekolab.cpp
new file mode 100644
index 00000000..44c13fd2
--- /dev/null
+++ b/tderesources/kolab/kcal/resourcekolab.cpp
@@ -0,0 +1,1342 @@
+/*
+ This file is part of the kolab resource - the implementation of the
+ Kolab storage format. See www.kolab.org for documentation on this.
+
+ Copyright (c) 2004 Bo Thorsen <bo@sonofthor.dk>
+ 2004 Till Adam <till@klaralvdalens-datakonsult.se>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+
+ In addition, as a special exception, the copyright holders give
+ permission to link the code of this program with any edition of
+ the TQt library by Trolltech AS, Norway (or with modified versions
+ of TQt that use the same license as TQt), and distribute linked
+ combinations including the two. You must obey the GNU General
+ Public License in all respects for all of the code used other than
+ TQt. If you modify this file, you may extend this exception to
+ your version of the file, but you are not obligated to do so. If
+ you do not wish to do so, delete this exception statement from
+ your version.
+*/
+
+#include "resourcekolab.h"
+#include "event.h"
+#include "task.h"
+#include "journal.h"
+
+#include <kio/observer.h>
+#include <kio/uiserver_stub.h>
+#include <kapplication.h>
+#include <dcopclient.h>
+#include <libtdepim/kincidencechooser.h>
+#include <kabc/locknull.h>
+#include <kmainwindow.h>
+#include <klocale.h>
+#include <kinputdialog.h>
+#include <ktempfile.h>
+#include <kmdcodec.h>
+
+#include <tqfile.h>
+#include <tqobject.h>
+#include <tqtimer.h>
+#include <tqapplication.h>
+
+#include <assert.h>
+
+using namespace KCal;
+using namespace Kolab;
+
+static const char* kmailCalendarContentsType = "Calendar";
+static const char* kmailTodoContentsType = "Task";
+static const char* kmailJournalContentsType = "Journal";
+static const char* eventAttachmentMimeType = "application/x-vnd.kolab.event";
+static const char* todoAttachmentMimeType = "application/x-vnd.kolab.task";
+static const char* journalAttachmentMimeType = "application/x-vnd.kolab.journal";
+static const char* incidenceInlineMimeType = "text/calendar";
+
+
+ResourceKolab::ResourceKolab( const TDEConfig *config )
+ : ResourceCalendar( config ), ResourceKolabBase( "ResourceKolab-libkcal" ),
+ mCalendar( TQString::fromLatin1("UTC") ), mOpen( false ),mResourceChangedTimer( 0,
+ "mResourceChangedTimer" ), mBatchAddingInProgress( false )
+{
+ if ( !config ) {
+ setResourceName( i18n( "Kolab Server" ) );
+ }
+ setType( "imap" );
+ connect( &mResourceChangedTimer, TQT_SIGNAL( timeout() ),
+ this, TQT_SLOT( slotEmitResourceChanged() ) );
+}
+
+ResourceKolab::~ResourceKolab()
+{
+ // The resource is deleted on exit (StdAddressBook's KStaticDeleter),
+ // and it wasn't closed before that, so close here to save the config.
+ if ( mOpen ) {
+ close();
+ }
+}
+
+void ResourceKolab::loadSubResourceConfig( TDEConfig& config,
+ const TQString& name,
+ const TQString& label,
+ bool writable,
+ bool alarmRelevant,
+ ResourceMap& subResource )
+{
+ TDEConfigGroup group( &config, name );
+ bool active = group.readBoolEntry( "Active", true );
+ subResource.insert( name, Kolab::SubResource( active, writable,
+ alarmRelevant, label ) );
+}
+
+bool ResourceKolab::openResource( TDEConfig& config, const char* contentType,
+ ResourceMap& map )
+{
+ // Read the subresource entries from KMail
+ TQValueList<KMailICalIface::SubResource> subResources;
+ if ( !kmailSubresources( subResources, contentType ) )
+ return false;
+ map.clear();
+ TQValueList<KMailICalIface::SubResource>::ConstIterator it;
+ for ( it = subResources.begin(); it != subResources.end(); ++it )
+ loadSubResourceConfig( config, (*it).location, (*it).label, (*it).writable,
+ (*it).alarmRelevant, map );
+ return true;
+}
+
+bool ResourceKolab::doOpen()
+{
+ if ( mOpen )
+ // Already open
+ return true;
+ mOpen = true;
+
+ TDEConfig config( configFile() );
+ config.setGroup( "General" );
+ mProgressDialogIncidenceLimit = config.readNumEntry("ProgressDialogIncidenceLimit", 200);
+
+ return openResource( config, kmailCalendarContentsType, mEventSubResources )
+ && openResource( config, kmailTodoContentsType, mTodoSubResources )
+ && openResource( config, kmailJournalContentsType, mJournalSubResources );
+}
+
+static void writeResourceConfig( TDEConfig& config, ResourceMap& map )
+{
+ ResourceMap::ConstIterator it;
+ for ( it = map.begin(); it != map.end(); ++it ) {
+ config.setGroup( it.key() );
+ config.writeEntry( "Active", it.data().active() );
+ }
+}
+
+void ResourceKolab::doClose()
+{
+ if ( !mOpen )
+ // Not open
+ return;
+ mOpen = false;
+
+ writeConfig();
+}
+
+bool ResourceKolab::loadSubResource( const TQString& subResource,
+ const char* mimetype )
+{
+ int count = 0;
+ if ( !kmailIncidencesCount( count, mimetype, subResource ) ) {
+ kdError(5650) << "Communication problem in ResourceKolab::load()\n";
+ return false;
+ }
+
+ if ( !count )
+ return true;
+
+ const int nbMessages = 200; // read 200 mails at a time (see kabc resource)
+
+ const TQString labelTxt = !strcmp(mimetype, "application/x-vnd.kolab.task") ? i18n( "Loading tasks..." )
+ : !strcmp(mimetype, "application/x-vnd.kolab.journal") ? i18n( "Loading journals..." )
+ : i18n( "Loading events..." );
+ const bool useProgress = tqApp && tqApp->type() != TQApplication::Tty && count > mProgressDialogIncidenceLimit;
+ if ( useProgress )
+ (void)::Observer::self(); // ensure kio_uiserver is running
+ UIServer_stub uiserver( "kio_uiserver", "UIServer" );
+ int progressId = 0;
+ if ( useProgress ) {
+ progressId = uiserver.newJob( kapp->dcopClient()->appId(), true );
+ uiserver.totalFiles( progressId, count );
+ uiserver.infoMessage( progressId, labelTxt );
+ uiserver.transferring( progressId, labelTxt );
+ }
+
+ for ( int startIndex = 0; startIndex < count; startIndex += nbMessages ) {
+ TQMap<TQ_UINT32, TQString> lst;
+ if ( !kmailIncidences( lst, mimetype, subResource, startIndex, nbMessages ) ) {
+ kdError(5650) << "Communication problem in ResourceKolab::load()\n";
+ if ( progressId )
+ uiserver.jobFinished( progressId );
+ return false;
+ }
+
+ { // for RAII scoping below
+ TemporarySilencer t( this );
+ for( TQMap<TQ_UINT32, TQString>::ConstIterator it = lst.begin(); it != lst.end(); ++it ) {
+ addIncidence( mimetype, it.data(), subResource, it.key() );
+ }
+ }
+ if ( progressId ) {
+ uiserver.processedFiles( progressId, startIndex );
+ uiserver.percent( progressId, 100 * startIndex / count );
+ }
+
+// if ( progress.wasCanceled() ) {
+// uiserver.jobFinished( progressId );
+// return false;
+// }
+ }
+
+ if ( progressId )
+ uiserver.jobFinished( progressId );
+ return true;
+}
+
+bool ResourceKolab::doLoad()
+{
+ if (!mUidMap.isEmpty() ) {
+ emit resourceLoaded( this );
+ return true;
+ }
+ mUidMap.clear();
+
+ bool result = loadAllEvents() & loadAllTodos() & loadAllJournals();
+ if ( result ) {
+ emit resourceLoaded( this );
+ } else {
+ // FIXME: anyone know if the resource correctly calls loadError()
+ // if it has one?
+ }
+
+ return result;
+}
+
+bool ResourceKolab::doLoadAll( ResourceMap& map, const char* mimetype )
+{
+ bool rc = true;
+ for ( ResourceMap::ConstIterator it = map.begin(); it != map.end(); ++it ) {
+ if ( !it.data().active() )
+ // This resource is disabled
+ continue;
+
+ rc &= loadSubResource( it.key(), mimetype );
+ }
+ return rc;
+}
+
+bool ResourceKolab::loadAllEvents()
+{
+ removeIncidences( "Event" );
+ mCalendar.deleteAllEvents();
+ bool kolabStyle = doLoadAll( mEventSubResources, eventAttachmentMimeType );
+ bool icalStyle = doLoadAll( mEventSubResources, incidenceInlineMimeType );
+ return kolabStyle && icalStyle;
+}
+
+bool ResourceKolab::loadAllTodos()
+{
+ removeIncidences( "Todo" );
+ mCalendar.deleteAllTodos();
+ bool kolabStyle = doLoadAll( mTodoSubResources, todoAttachmentMimeType );
+ bool icalStyle = doLoadAll( mTodoSubResources, incidenceInlineMimeType );
+
+ return kolabStyle && icalStyle;
+}
+
+bool ResourceKolab::loadAllJournals()
+{
+ removeIncidences( "Journal" );
+ mCalendar.deleteAllJournals();
+ bool kolabStyle = doLoadAll( mJournalSubResources, journalAttachmentMimeType );
+ bool icalStyle = doLoadAll( mJournalSubResources, incidenceInlineMimeType );
+
+ return kolabStyle && icalStyle;
+}
+
+void ResourceKolab::removeIncidences( const TQCString& incidenceType )
+{
+ Kolab::UidMap::Iterator mapIt = mUidMap.begin();
+ while ( mapIt != mUidMap.end() )
+ {
+ Kolab::UidMap::Iterator it = mapIt++;
+ // Check the type of this uid: event, todo or journal.
+ // Need to look up in mCalendar for that. Given the implementation of incidence(uid),
+ // better call event(uid), todo(uid) etc. directly.
+
+ // A faster but hackish way would probably be to check the type of the resource,
+ // like mEventSubResources.find( it.data().resource() ) != mEventSubResources.end() ?
+ const TQString& uid = it.key();
+ if ( incidenceType == "Event" && mCalendar.event( uid ) )
+ mUidMap.remove( it );
+ else if ( incidenceType == "Todo" && mCalendar.todo( uid ) )
+ mUidMap.remove( it );
+ else if ( incidenceType == "Journal" && mCalendar.journal( uid ) )
+ mUidMap.remove( it );
+ }
+}
+
+bool ResourceKolab::doSave()
+{
+ return true;
+ /*
+ return kmailTriggerSync( kmailCalendarContentsType )
+ && kmailTriggerSync( kmailTodoContentsType )
+ && kmailTriggerSync( kmailJournalContentsType );
+ */
+}
+void ResourceKolab::incidenceUpdatedSilent( KCal::IncidenceBase* incidencebase )
+{
+ const TQString uid = incidencebase->uid();
+ //kdDebug() << k_funcinfo << uid << endl;
+
+ if ( mUidsPendingUpdate.contains( uid ) || mUidsPendingAdding.contains( uid ) ) {
+ /* We are currently processing this event ( removing and readding or
+ * adding it ). If so, ignore this update. Keep the last of these around
+ * and process once we hear back from KMail on this event. */
+ mPendingUpdates.remove( uid );
+ mPendingUpdates.insert( uid, incidencebase );
+ return;
+ }
+
+ { // start optimization
+ /**
+ KOrganizer and libkcal like calling two Incidence::updated()
+ for only one user change. That's because after a change,
+ IncidenceChanger calls incidence->setRevision( rev++ );
+ which also calls Incidence::updated().
+
+ Lets ignore the first updated() and only send to kmail
+ the second. This makes things faster.
+ */
+
+ //IncidenceBase doesn't have revision(), downcast needed.
+ Incidence *i = dynamic_cast<Incidence*>( incidencebase );
+
+ if ( i ) {
+ bool ignoreThisUpdate = false;
+
+ if ( !mLastKnownRevisions.contains( uid ) ) {
+ mLastKnownRevisions[uid] = i->revision();
+ }
+
+ // update the last known revision
+ if ( mLastKnownRevisions[uid] < i->revision() ) {
+ mLastKnownRevisions[uid] = i->revision();
+ } else {
+ ignoreThisUpdate = true;
+ }
+
+ if ( ignoreThisUpdate ) {
+ return;
+ }
+ }
+ } // end optimization
+
+ TQString subResource;
+ TQ_UINT32 sernum = 0;
+ if ( mUidMap.contains( uid ) ) {
+ subResource = mUidMap[ uid ].resource();
+ sernum = mUidMap[ uid ].serialNumber();
+ mUidsPendingUpdate.append( uid );
+ }
+
+ sendKMailUpdate( incidencebase, subResource, sernum );
+}
+void ResourceKolab::incidenceUpdated( KCal::IncidenceBase* incidencebase )
+{
+ if ( incidencebase->isReadOnly() ) {
+ return;
+ }
+
+ incidencebase->setSyncStatusSilent( KCal::Event::SYNCMOD );
+ incidencebase->setLastModified( TQDateTime::currentDateTime() );
+
+ // we should probably update the revision number here,
+ // or internally in the Event itself when certain things change.
+ // need to verify with ical documentation.
+ incidenceUpdatedSilent( incidencebase );
+}
+
+void ResourceKolab::resolveConflict( KCal::Incidence* inc, const TQString& subresource, TQ_UINT32 sernum )
+{
+ if ( !inc ) {
+ return;
+ }
+
+ if ( !mResolveConflict ) {
+ // we should do no conflict resolution
+ delete inc;
+ return;
+ }
+ const TQString origUid = inc->uid();
+ Incidence* local = mCalendar.incidence( origUid );
+ Incidence* localIncidence = 0;
+ Incidence* addedIncidence = 0;
+ Incidence* result = 0;
+ if ( local ) {
+ if ( *local == *inc ) {
+ // real duplicate, remove the second one
+ result = local;
+ } else {
+ KIncidenceChooser* ch = new KIncidenceChooser();
+ ch->setIncidence( local ,inc );
+ if ( KIncidenceChooser::chooseMode == KIncidenceChooser::ask ) {
+ connect ( this, TQT_SIGNAL( useGlobalMode() ), ch, TQT_SLOT ( useGlobalMode() ) );
+ if ( ch->exec() ) {
+ if ( KIncidenceChooser::chooseMode != KIncidenceChooser::ask ) {
+ emit useGlobalMode() ;
+ }
+ }
+ }
+ result = ch->getIncidence();
+ delete ch;
+ }
+ } else {
+ // nothing there locally, just take the new one. Can't Happen (TM)
+ result = inc;
+ }
+ if ( result == local ) {
+ delete inc;
+ localIncidence = local;
+ } else if ( result == inc ) {
+ addedIncidence = inc;
+ } else if ( result == 0 ) { // take both
+ addedIncidence = inc;
+ addedIncidence->setSummary( i18n("Copy of: %1").arg( addedIncidence->summary() ) );
+ addedIncidence->setUid( CalFormat::createUniqueId() );
+ localIncidence = local;
+ }
+ const bool silent = mSilent;
+ mSilent = false;
+ if ( !localIncidence ) {
+ deleteIncidence( local ); // remove local from kmail
+ }
+ mUidsPendingDeletion.append( origUid );
+ if ( addedIncidence ) {
+ sendKMailUpdate( addedIncidence, subresource, sernum );
+ } else {
+ kmailDeleteIncidence( subresource, sernum );// remove new from kmail
+ }
+ mSilent = silent;
+}
+void ResourceKolab::addIncidence( const char* mimetype, const TQString& data,
+ const TQString& subResource, TQ_UINT32 sernum )
+{
+ // This uses pointer comparison, so it only works if we use the static
+ // objects defined in the top of the file
+ if ( mimetype == eventAttachmentMimeType )
+ addEvent( data, subResource, sernum );
+ else if ( mimetype == todoAttachmentMimeType )
+ addTodo( data, subResource, sernum );
+ else if ( mimetype == journalAttachmentMimeType )
+ addJournal( data, subResource, sernum );
+ else if ( mimetype == incidenceInlineMimeType ) {
+ Incidence *inc = mFormat.fromString( data );
+ addIncidence( inc, subResource, sernum );
+ }
+}
+
+
+bool ResourceKolab::sendKMailUpdate( KCal::IncidenceBase* incidencebase, const TQString& subresource,
+ TQ_UINT32 sernum )
+{
+ const TQString& type = incidencebase->type();
+ const char* mimetype = 0;
+ TQString data;
+ bool isXMLStorageFormat = kmailStorageFormat( subresource ) == KMailICalIface::StorageXML;
+ if ( type == "Event" ) {
+ if( isXMLStorageFormat ) {
+ mimetype = eventAttachmentMimeType;
+ data = Kolab::Event::eventToXML( static_cast<KCal::Event *>(incidencebase),
+ mCalendar.timeZoneId() );
+ } else {
+ mimetype = incidenceInlineMimeType;
+ data = mFormat.createScheduleMessage( static_cast<KCal::Event *>(incidencebase),
+ Scheduler::Request );
+ }
+ } else if ( type == "Todo" ) {
+ if( isXMLStorageFormat ) {
+ mimetype = todoAttachmentMimeType;
+ data = Kolab::Task::taskToXML( static_cast<KCal::Todo *>(incidencebase),
+ mCalendar.timeZoneId() );
+ } else {
+ mimetype = incidenceInlineMimeType;
+ data = mFormat.createScheduleMessage( static_cast<KCal::Todo *>(incidencebase),
+ Scheduler::Request );
+ }
+ } else if ( type == "Journal" ) {
+ if( isXMLStorageFormat ) {
+ mimetype = journalAttachmentMimeType;
+ data = Kolab::Journal::journalToXML( static_cast<KCal::Journal *>(incidencebase ),
+ mCalendar.timeZoneId() );
+ } else {
+ mimetype = incidenceInlineMimeType;
+ data = mFormat.createScheduleMessage( static_cast<KCal::Journal *>(incidencebase),
+ Scheduler::Request );
+ }
+ } else {
+ kdWarning(5006) << "Can't happen: unhandled type=" << type << endl;
+ }
+
+// kdDebug() << k_funcinfo << "Data string:\n" << data << endl;
+
+ KCal::Incidence* incidence = static_cast<KCal::Incidence *>( incidencebase );
+
+ KCal::Attachment::List atts = incidence->attachments();
+ TQStringList attURLs, attMimeTypes, attNames;
+ TQValueList<KTempFile*> tmpFiles;
+ for ( KCal::Attachment::List::ConstIterator it = atts.constBegin(); it != atts.constEnd(); ++it ) {
+ if ( (*it)->isUri() ) {
+ continue;
+ }
+ KTempFile *tempFile = new KTempFile;
+ if ( tempFile->status() == 0 ) { // open ok
+ const TQByteArray decoded = (*it)->decodedData() ;
+
+ tempFile->file()->writeBlock( decoded.data(), decoded.count() );
+ KURL url;
+ url.setPath( tempFile->name() );
+ attURLs.append( url.url() );
+ attMimeTypes.append( (*it)->mimeType() );
+ attNames.append( (*it)->label() );
+ tempFile->close();
+ tmpFiles.append( tempFile );
+ } else {
+ kdWarning(5006) << "Cannot open temporary file for attachment";
+ }
+ }
+ TQStringList deletedAtts;
+ if ( kmailListAttachments( deletedAtts, subresource, sernum ) ) {
+ for ( TQStringList::ConstIterator it = attNames.constBegin(); it != attNames.constEnd(); ++it ) {
+ deletedAtts.remove( *it );
+ }
+ }
+ CustomHeaderMap customHeaders;
+ if ( incidence->schedulingID() != incidence->uid() ) {
+ customHeaders.insert( "X-Kolab-SchedulingID", incidence->schedulingID() );
+ }
+
+ TQString subject = incidencebase->uid();
+ if ( !isXMLStorageFormat ) subject.prepend( "iCal " ); // conform to the old style
+
+ // behold, sernum is an in-parameter
+ const bool rc = kmailUpdate( subresource, sernum, data, mimetype, subject, customHeaders, attURLs, attMimeTypes, attNames, deletedAtts );
+ // update the serial number
+ if ( mUidMap.contains( incidencebase->uid() ) ) {
+ mUidMap[ incidencebase->uid() ].setSerialNumber( sernum );
+ }
+
+ for( TQValueList<KTempFile *>::Iterator it = tmpFiles.begin(); it != tmpFiles.end(); ++it ) {
+ (*it)->setAutoDelete( true );
+ delete (*it);
+ }
+
+ return rc;
+}
+
+bool ResourceKolab::addIncidence( KCal::Incidence* incidence, const TQString& _subresource,
+ TQ_UINT32 sernum )
+{
+ Q_ASSERT( incidence );
+ if ( !incidence ) {
+ return false;
+ }
+
+ kdDebug() << "Resourcekolab, adding incidence "
+ << incidence->summary()
+ << "; subresource = " << _subresource
+ << "; sernum = " << sernum
+ << "; mBatchAddingInProgress = " << mBatchAddingInProgress
+ << endl;
+
+ TQString uid = incidence->uid();
+ TQString subResource = _subresource;
+
+ Kolab::ResourceMap *map = &mEventSubResources; // don't use a ref here!
+
+ const TQString& type = incidence->type();
+ if ( type == "Event" ) {
+ map = &mEventSubResources;
+ } else if ( type == "Todo" ) {
+ map = &mTodoSubResources;
+ } else if ( type == "Journal" ) {
+ map = &mJournalSubResources;
+ } else {
+ kdWarning() << "unknown type " << type << endl;
+ }
+
+ if ( !mSilent ) { /* We got this one from the user, tell KMail. */
+ // Find out if this event was previously stored in KMail
+ bool newIncidence = _subresource.isEmpty();
+ if ( newIncidence ) {
+ ResourceType type = Incidences;
+ // Add a description of the incidence
+ TQString text = "<b><font size=\"+1\">";
+ if ( incidence->type() == "Event" ) {
+ type = Events;
+ text += i18n( "Choose the folder where you want to store this event" );
+ } else if ( incidence->type() == "Todo" ) {
+ type = Tasks;
+ text += i18n( "Choose the folder where you want to store this task" );
+ } else {
+ text += i18n( "Choose the folder where you want to store this incidence" );
+ }
+ text += "<font></b><br>";
+ if ( !incidence->summary().isEmpty() )
+ text += i18n( "<b>Summary:</b> %1" ).arg( incidence->summary() ) + "<br>";
+ if ( !incidence->location().isEmpty() )
+ text += i18n( "<b>Location:</b> %1" ).arg( incidence->location() );
+ text += "<br>";
+ if ( !incidence->doesFloat() )
+ text += i18n( "<b>Start:</b> %1, %2" )
+ .arg( incidence->dtStartDateStr(), incidence->dtStartTimeStr() );
+ else
+ text += i18n( "<b>Start:</b> %1" ).arg( incidence->dtStartDateStr() );
+ text += "<br>";
+ if ( incidence->type() == "Event" ) {
+ Event* event = static_cast<Event*>( incidence );
+ if ( event->hasEndDate() ) {
+ if ( !event->doesFloat() ) {
+ text += i18n( "<b>End:</b> %1, %2" )
+ .arg( event->dtEndDateStr(), event->dtEndTimeStr() );
+ } else {
+ text += i18n( "<b>End:</b> %1" ).arg( event->dtEndDateStr() );
+ }
+ }
+ text += "<br>";
+ }
+
+ // Lets not warn the user 100 times that there's no writable resource
+ // and not ask 100 times which resource to use
+ if ( !mBatchAddingInProgress || !mLastUsedResources.contains( type ) ) {
+ subResource = findWritableResource( type, *map, text );
+ mLastUsedResources[type] = subResource;
+ } else {
+ subResource = mLastUsedResources[type];
+ }
+
+ if ( subResource.isEmpty() ) {
+ switch( mErrorCode ) {
+ case NoWritableFound:
+ setException( new ErrorFormat( ErrorFormat::NoWritableFound ) );
+ break;
+ case UserCancel:
+ setException( new ErrorFormat( ErrorFormat::UserCancel ) );
+ break;
+ case NoError:
+ break;
+ }
+ }
+ }
+
+ if ( subResource.isEmpty() ) {
+ endAddingIncidences(); // cleanup
+ kdDebug(5650) << "ResourceKolab: subResource is empty" << endl;
+ return false;
+ }
+
+ mNewIncidencesMap.insert( uid, subResource );
+
+ if ( !sendKMailUpdate( incidence, subResource, sernum ) ) {
+ kdError(5650) << "Communication problem in ResourceKolab::addIncidence()\n";
+ endAddingIncidences(); // cleanup
+ return false;
+ } else {
+ // KMail is doing it's best to add the event now, put a sticker on it,
+ // so we know it's one of our transient ones
+ mUidsPendingAdding.append( uid );
+
+ /* Add to the cache immediately if this is a new event coming from
+ * KOrganizer. It relies on the incidence being in the calendar when
+ * addIncidence returns. */
+ if ( newIncidence || sernum == 0 ) {
+ mCalendar.addIncidence( incidence );
+ incidence->registerObserver( this );
+ }
+ }
+ } else { /* KMail told us */
+ const bool ourOwnUpdate = mUidsPendingUpdate.contains( uid );
+ kdDebug( 5650 ) << "addIncidence: ourOwnUpdate " << ourOwnUpdate << endl;
+ /* Check if we updated this one, which means kmail deleted and added it.
+ * We know the new state, so lets just not do much at all. The old incidence
+ * in the calendar remains valid, but the serial number changed, so we need to
+ * update that */
+ if ( ourOwnUpdate ) {
+ mUidsPendingUpdate.remove( uid );
+ mUidMap.remove( uid );
+ mUidMap[ uid ] = StorageReference( subResource, sernum );
+ } else {
+ /* This is a real add, from KMail, we didn't trigger this ourselves.
+ * If this uid already exists in this folder, do conflict resolution,
+ * unless the folder is read-only, in which case the user should not be
+ * offered a means of putting mails in a folder she'll later be unable to
+ * upload. Skip the incidence, in this case. */
+ if ( mUidMap.contains( uid ) ) {
+ if ( mUidMap[ uid ].resource() == subResource ) {
+ if ( (*map)[ subResource ].writable() ) {
+ kdDebug( 5650 ) << "lets resolve the conflict " << endl;
+ resolveConflict( incidence, subResource, sernum );
+ } else {
+ kdWarning( 5650 ) << "Duplicate event in a read-only folder detected! "
+ "Please inform the owner of the folder. " << endl;
+ }
+ return true;
+ } else {
+ // duplicate uid in a different folder, do the internal-uid tango
+ incidence->setSchedulingID( uid );
+
+ incidence->setUid( CalFormat::createUniqueId( ) );
+ uid = incidence->uid();
+
+ /* Will be needed when kmail triggers a delete, so we don't delete the inocent
+ * incidence that's sharing the uid with this one */
+ mOriginalUID2fakeUID[tqMakePair( incidence->schedulingID(), subResource )] = uid;
+ }
+ }
+ /* Add to the cache if the add didn't come from KOrganizer, in which case
+ * we've already added it, and listen to updates from KOrganizer for it. */
+ if ( !mUidsPendingAdding.contains( uid ) ) {
+ mCalendar.addIncidence( incidence );
+ incidence->registerObserver( this );
+ }
+ if ( !subResource.isEmpty() && sernum != 0 ) {
+ mUidMap[ uid ] = StorageReference( subResource, sernum );
+ incidence->setReadOnly( !(*map)[ subResource ].writable() );
+ }
+ }
+ /* Check if there are updates for this uid pending and if so process them. */
+ if ( KCal::IncidenceBase *update = mPendingUpdates.find( uid ) ) {
+ mSilent = false; // we do want to tell KMail
+ mPendingUpdates.remove( uid );
+ incidenceUpdated( update );
+ } else {
+ /* If the uid was added by KMail, KOrganizer needs to be told, so
+ * schedule emitting of the resourceChanged signal. */
+ if ( !mUidsPendingAdding.contains( uid ) ) {
+ if ( !ourOwnUpdate ) mResourceChangedTimer.changeInterval( 100 );
+ } else {
+ mUidsPendingAdding.remove( uid );
+ }
+ }
+
+ mNewIncidencesMap.remove( uid );
+ }
+ return true;
+}
+
+bool ResourceKolab::addEvent( KCal::Event *event )
+{
+ return addEvent( event, TQString() );
+}
+
+bool ResourceKolab::addEvent( KCal::Event *event, const TQString &subResource )
+{
+ if ( mUidMap.contains( event->uid() ) ) {
+ return true; //noop
+ } else {
+ return addIncidence( event, subResource, 0 );
+ }
+}
+
+void ResourceKolab::addEvent( const TQString& xml, const TQString& subresource,
+ TQ_UINT32 sernum )
+{
+ KCal::Event* event = Kolab::Event::xmlToEvent( xml, mCalendar.timeZoneId(), this, subresource, sernum );
+ Q_ASSERT( event );
+ if ( event ) {
+ addIncidence( event, subresource, sernum );
+ }
+}
+
+bool ResourceKolab::deleteIncidence( KCal::Incidence* incidence )
+{
+ if ( incidence->isReadOnly() ) {
+ return false;
+ }
+
+ const TQString uid = incidence->uid();
+ if( !mUidMap.contains( uid ) ) return false; // Odd
+ /* The user told us to delete, tell KMail */
+ if ( !mSilent ) {
+ kmailDeleteIncidence( mUidMap[ uid ].resource(),
+ mUidMap[ uid ].serialNumber() );
+ mUidsPendingDeletion.append( uid );
+ incidence->unRegisterObserver( this );
+ mCalendar.deleteIncidence( incidence );
+ mUidMap.remove( uid );
+ } else {
+ assert( false ); // If this still happens, something is very wrong
+ }
+ return true;
+}
+
+bool ResourceKolab::deleteEvent( KCal::Event* event )
+{
+ return deleteIncidence( event );
+}
+
+KCal::Event* ResourceKolab::event( const TQString& uid )
+{
+ return mCalendar.event(uid);
+}
+
+KCal::Event::List ResourceKolab::rawEvents( EventSortField sortField, SortDirection sortDirection )
+{
+ return mCalendar.rawEvents( sortField, sortDirection );
+}
+
+KCal::Event::List ResourceKolab::rawEventsForDate( const TQDate& date,
+ EventSortField sortField,
+ SortDirection sortDirection )
+{
+ return mCalendar.rawEventsForDate( date, sortField, sortDirection );
+}
+
+KCal::Event::List ResourceKolab::rawEventsForDate( const TQDateTime& qdt )
+{
+ return mCalendar.rawEventsForDate( qdt );
+}
+
+KCal::Event::List ResourceKolab::rawEvents( const TQDate& start,
+ const TQDate& end,
+ bool inclusive )
+{
+ return mCalendar.rawEvents( start, end, inclusive );
+}
+
+bool ResourceKolab::addTodo( KCal::Todo *todo )
+{
+ return addTodo( todo, TQString() );
+}
+
+bool ResourceKolab::addTodo( KCal::Todo *todo, const TQString &subResource )
+{
+ if ( mUidMap.contains( todo->uid() ) ) {
+ return true; //noop
+ } else {
+ return addIncidence( todo, subResource, 0 );
+ }
+}
+
+void ResourceKolab::addTodo( const TQString& xml, const TQString& subresource,
+ TQ_UINT32 sernum )
+{
+ KCal::Todo* todo = Kolab::Task::xmlToTask( xml, mCalendar.timeZoneId(), this, subresource, sernum );
+ Q_ASSERT( todo );
+ if ( todo ) {
+ addIncidence( todo, subresource, sernum );
+ }
+}
+
+bool ResourceKolab::deleteTodo( KCal::Todo* todo )
+{
+ return deleteIncidence( todo );
+}
+
+KCal::Todo* ResourceKolab::todo( const TQString& uid )
+{
+ return mCalendar.todo( uid );
+}
+
+KCal::Todo::List ResourceKolab::rawTodos( TodoSortField sortField, SortDirection sortDirection )
+{
+ return mCalendar.rawTodos( sortField, sortDirection );
+}
+
+KCal::Todo::List ResourceKolab::rawTodosForDate( const TQDate& date )
+{
+ return mCalendar.rawTodosForDate( date );
+}
+
+bool ResourceKolab::addJournal( KCal::Journal *journal )
+{
+ return addJournal( journal, TQString() );
+}
+
+bool ResourceKolab::addJournal( KCal::Journal *journal, const TQString &subResource )
+{
+ if ( mUidMap.contains( journal->uid() ) )
+ return true; //noop
+ else
+ return addIncidence( journal, subResource, 0 );
+}
+
+void ResourceKolab::addJournal( const TQString& xml, const TQString& subresource,
+ TQ_UINT32 sernum )
+{
+ KCal::Journal* journal =
+ Kolab::Journal::xmlToJournal( xml, mCalendar.timeZoneId() );
+ Q_ASSERT( journal );
+ if( journal ) {
+ addIncidence( journal, subresource, sernum );
+ }
+}
+
+bool ResourceKolab::deleteJournal( KCal::Journal* journal )
+{
+ return deleteIncidence( journal );
+}
+
+KCal::Journal* ResourceKolab::journal( const TQString& uid )
+{
+ return mCalendar.journal(uid);
+}
+
+KCal::Journal::List ResourceKolab::rawJournals( JournalSortField sortField, SortDirection sortDirection )
+{
+ return mCalendar.rawJournals( sortField, sortDirection );
+}
+
+KCal::Journal::List ResourceKolab::rawJournalsForDate( const TQDate &date )
+{
+ return mCalendar.rawJournalsForDate( date );
+}
+
+KCal::Alarm::List ResourceKolab::relevantAlarms( const KCal::Alarm::List &alarms )
+{
+ KCal::Alarm::List relevantAlarms;
+ KCal::Alarm::List::ConstIterator it( alarms.begin() );
+ while ( it != alarms.end() ) {
+ KCal::Alarm *a = (*it);
+ ++it;
+ const TQString &uid = a->parent()->uid();
+ if ( mUidMap.contains( uid ) ) {
+ const TQString &sr = mUidMap[ uid ].resource();
+ Kolab::SubResource *subResource = 0;
+ if ( mEventSubResources.contains( sr ) )
+ subResource = &( mEventSubResources[ sr ] );
+ else if ( mTodoSubResources.contains( sr ) )
+ subResource = &( mTodoSubResources[ sr ] );
+ assert( subResource );
+ if ( subResource->alarmRelevant() )
+ relevantAlarms.append ( a );
+ else {
+ kdDebug(5650) << "Alarm skipped, not relevant." << endl;
+ }
+ }
+ }
+ return relevantAlarms;
+}
+
+
+
+KCal::Alarm::List ResourceKolab::alarms( const TQDateTime& from,
+ const TQDateTime& to )
+{
+ return relevantAlarms( mCalendar.alarms( from, to ) );
+}
+
+KCal::Alarm::List ResourceKolab::alarmsTo( const TQDateTime& to )
+{
+ return relevantAlarms( mCalendar.alarmsTo(to) );
+}
+
+void ResourceKolab::setTimeZoneId( const TQString& tzid )
+{
+ mCalendar.setTimeZoneId( tzid );
+ mFormat.setTimeZone( mCalendar.timeZoneId(), !mCalendar.isLocalTime() );
+}
+
+bool ResourceKolab::fromKMailAddIncidence( const TQString& type,
+ const TQString& subResource,
+ TQ_UINT32 sernum,
+ int format,
+ const TQString& data )
+{
+ bool rc = true;
+ TemporarySilencer t( this ); // RAII
+ if ( type != kmailCalendarContentsType && type != kmailTodoContentsType
+ && type != kmailJournalContentsType ) {
+ // Not ours
+ return false;
+ }
+
+ if ( !subresourceActive( subResource ) ) {
+ return true;
+ }
+
+ if ( format == KMailICalIface::StorageXML ) {
+ // If this data file is one of ours, load it here
+ if ( type == kmailCalendarContentsType ) {
+ addEvent( data, subResource, sernum );
+ } else if ( type == kmailTodoContentsType ) {
+ addTodo( data, subResource, sernum );
+ } else if ( type == kmailJournalContentsType ) {
+ addJournal( data, subResource, sernum );
+ } else {
+ rc = false;
+ }
+ } else {
+ Incidence *inc = mFormat.fromString( data );
+ if ( inc ) {
+ addIncidence( inc, subResource, sernum );
+ } else {
+ rc = false;
+ }
+ }
+ return rc;
+}
+
+void ResourceKolab::fromKMailDelIncidence( const TQString& type,
+ const TQString& subResource,
+ const TQString& uid )
+{
+ if ( type != kmailCalendarContentsType && type != kmailTodoContentsType
+ && type != kmailJournalContentsType )
+ // Not ours
+ return;
+ if ( !subresourceActive( subResource ) ) return;
+
+ // Can't be in both, by contract
+ if ( mUidsPendingDeletion.find( uid ) != mUidsPendingDeletion.end() ) {
+ mUidsPendingDeletion.remove( mUidsPendingDeletion.find( uid ) );
+ } else if ( mUidsPendingUpdate.contains( uid ) ) {
+ // It's good to know if was deleted, but we are waiting on a new one to
+ // replace it, so let's just sit tight.
+ } else {
+ TQString uidToUse;
+
+ TQPair<TQString, TQString> p( uid, subResource );
+ if ( mOriginalUID2fakeUID.contains( p ) ) {
+ // Incidence with the same uid in a different folder...
+ // use the UID that addIncidence(...) generated
+ uidToUse = mOriginalUID2fakeUID[p];
+ } else {
+ uidToUse = uid;
+ }
+
+ // We didn't trigger this, so KMail did, remove the reference to the uid
+ KCal::Incidence* incidence = mCalendar.incidence( uidToUse );
+ if( incidence ) {
+ incidence->unRegisterObserver( this );
+ mCalendar.deleteIncidence( incidence );
+ }
+ mUidMap.remove( uidToUse );
+ mOriginalUID2fakeUID.remove( p );
+ mResourceChangedTimer.changeInterval( 100 );
+ }
+}
+
+void ResourceKolab::fromKMailRefresh( const TQString& type,
+ const TQString& /*subResource*/ )
+{
+ // TODO: Only load the specified subResource
+ if ( type == "Calendar" )
+ loadAllEvents();
+ else if ( type == "Task" )
+ loadAllTodos();
+ else if ( type == "Journal" )
+ loadAllJournals();
+ else
+ kdWarning(5006) << "KCal Kolab resource: fromKMailRefresh: unknown type " << type << endl;
+ mResourceChangedTimer.changeInterval( 100 );
+}
+
+void ResourceKolab::fromKMailAddSubresource( const TQString& type,
+ const TQString& subResource,
+ const TQString& label,
+ bool writable, bool alarmRelevant )
+{
+ ResourceMap* map = 0;
+ const char* mimetype = 0;
+ if ( type == kmailCalendarContentsType ) {
+ map = &mEventSubResources;
+ mimetype = eventAttachmentMimeType;
+ } else if ( type == kmailTodoContentsType ) {
+ map = &mTodoSubResources;
+ mimetype = todoAttachmentMimeType;
+ } else if ( type == kmailJournalContentsType ) {
+ map = &mJournalSubResources;
+ mimetype = journalAttachmentMimeType;
+ } else
+ // Not ours
+ return;
+
+ if ( map->contains( subResource ) )
+ // Already registered
+ return;
+
+ TDEConfig config( configFile() );
+ config.setGroup( subResource );
+
+ bool active = config.readBoolEntry( subResource, true );
+ (*map)[ subResource ] = Kolab::SubResource( active, writable,
+ alarmRelevant, label );
+ loadSubResource( subResource, mimetype );
+ emit signalSubresourceAdded( this, type, subResource, label );
+}
+
+void ResourceKolab::fromKMailDelSubresource( const TQString& type,
+ const TQString& subResource )
+{
+ ResourceMap* map = subResourceMap( type );
+ if ( !map ) // not ours
+ return;
+ if ( map->contains( subResource ) )
+ map->erase( subResource );
+ else
+ // Not registered
+ return;
+
+ // Delete from the config file
+ TDEConfig config( configFile() );
+ config.deleteGroup( subResource );
+ config.sync();
+
+ unloadSubResource( subResource );
+
+ emit signalSubresourceRemoved( this, type, subResource );
+}
+
+TQStringList ResourceKolab::subresources() const
+{
+ // Workaround: The ResourceView in KOrganizer wants to know this
+ // before it opens the resource :-( Make sure we are open
+ const_cast<ResourceKolab*>( this )->doOpen();
+ return ( mEventSubResources.keys()
+ + mTodoSubResources.keys()
+ + mJournalSubResources.keys() );
+}
+
+const TQString
+ResourceKolab::labelForSubresource( const TQString& subresource ) const
+{
+ if ( mEventSubResources.contains( subresource ) )
+ return mEventSubResources[ subresource ].label();
+ if ( mTodoSubResources.contains( subresource ) )
+ return mTodoSubResources[ subresource ].label();
+ if ( mJournalSubResources.contains( subresource ) )
+ return mJournalSubResources[ subresource ].label();
+ return subresource;
+}
+
+void ResourceKolab::fromKMailAsyncLoadResult( const TQMap<TQ_UINT32, TQString>& map,
+ const TQString& type,
+ const TQString& folder )
+{
+ TemporarySilencer t( this );
+ for( TQMap<TQ_UINT32, TQString>::ConstIterator it = map.begin(); it != map.end(); ++it )
+ addIncidence( type.latin1(), it.data(), folder, it.key() );
+}
+
+bool ResourceKolab::subresourceActive( const TQString& subresource ) const
+{
+ // Workaround: The ResourceView in KOrganizer wants to know this
+ // before it opens the resource :-( Make sure we are open
+ const_cast<ResourceKolab*>( this )->doOpen();
+
+ if ( mEventSubResources.contains( subresource ) )
+ return mEventSubResources[ subresource ].active();
+ if ( mTodoSubResources.contains( subresource ) )
+ return mTodoSubResources[ subresource ].active();
+ if ( mJournalSubResources.contains( subresource ) )
+ return mJournalSubResources[ subresource ].active();
+
+ // Safe default bet:
+ kdDebug(5650) << "subresourceActive( " << subresource << " ): Safe bet\n";
+
+ return true;
+}
+
+void ResourceKolab::setSubresourceActive( const TQString &subresource, bool v )
+{
+ ResourceMap *map = 0;
+ const char* mimeType = 0;
+ if ( mEventSubResources.contains( subresource ) ) {
+ map = &mEventSubResources;
+ mimeType = eventAttachmentMimeType;
+ }
+ if ( mTodoSubResources.contains( subresource ) ) {
+ map = &mTodoSubResources;
+ mimeType = todoAttachmentMimeType;
+ }
+ if ( mJournalSubResources.contains( subresource ) ) {
+ map = &mJournalSubResources;
+ mimeType = journalAttachmentMimeType;
+ }
+
+ if ( map && ( ( *map )[ subresource ].active() != v ) ) {
+ ( *map )[ subresource ].setActive( v );
+ if ( v ) {
+ loadSubResource( subresource, mimeType );
+ } else {
+ unloadSubResource( subresource );
+ }
+ mResourceChangedTimer.changeInterval( 100 );
+ }
+ TQTimer::singleShot( 0, this, TQT_SLOT(writeConfig()) );
+}
+
+bool ResourceKolab::subresourceWritable( const TQString& subresource ) const
+{
+ // Workaround: The ResourceView in KOrganizer wants to know this
+ // before it opens the resource :-( Make sure we are open
+ const_cast<ResourceKolab*>( this )->doOpen();
+
+ if ( mEventSubResources.contains( subresource ) )
+ return mEventSubResources[ subresource ].writable();
+ if ( mTodoSubResources.contains( subresource ) )
+ return mTodoSubResources[ subresource ].writable();
+ if ( mJournalSubResources.contains( subresource ) )
+ return mJournalSubResources[ subresource ].writable();
+
+ return false; //better a safe default
+}
+
+void ResourceKolab::slotEmitResourceChanged()
+{
+ kdDebug(5650) << "KCal Kolab resource: emitting resource changed " << endl;
+ mResourceChangedTimer.stop();
+ emit resourceChanged( this );
+}
+
+KABC::Lock* ResourceKolab::lock()
+{
+ return new KABC::LockNull( true );
+}
+
+Kolab::ResourceMap* ResourceKolab::subResourceMap( const TQString& contentsType )
+{
+ if ( contentsType == kmailCalendarContentsType ) {
+ return &mEventSubResources;
+ } else if ( contentsType == kmailTodoContentsType ) {
+ return &mTodoSubResources;
+ } else if ( contentsType == kmailJournalContentsType ) {
+ return &mJournalSubResources;
+ }
+ // Not ours
+ return 0;
+}
+
+
+/*virtual*/
+bool ResourceKolab::addSubresource( const TQString& resource, const TQString& parent )
+{
+ kdDebug(5650) << "KCal Kolab resource - adding subresource: " << resource << endl;
+ TQString contentsType = kmailCalendarContentsType;
+ if ( !parent.isEmpty() ) {
+ if ( mEventSubResources.contains( parent ) )
+ contentsType = kmailCalendarContentsType;
+ else if ( mTodoSubResources.contains( parent ) )
+ contentsType = kmailTodoContentsType;
+ else if ( mJournalSubResources.contains( parent ) )
+ contentsType = kmailJournalContentsType;
+ } else {
+ TQStringList contentTypeChoices;
+ contentTypeChoices << i18n("Calendar") << i18n("Tasks") << i18n("Journals");
+ const TQString caption = i18n("Which kind of subresource should this be?");
+ const TQString choice = KInputDialog::getItem( caption, TQString(), contentTypeChoices );
+ if ( choice == contentTypeChoices[0] )
+ contentsType = kmailCalendarContentsType;
+ else if ( choice == contentTypeChoices[1] )
+ contentsType = kmailTodoContentsType;
+ else if ( choice == contentTypeChoices[2] )
+ contentsType = kmailJournalContentsType;
+ }
+
+ return kmailAddSubresource( resource, parent, contentsType );
+}
+
+/*virtual*/
+bool ResourceKolab::removeSubresource( const TQString& resource )
+{
+ kdDebug(5650) << "KCal Kolab resource - removing subresource: " << resource << endl;
+ return kmailRemoveSubresource( resource );
+}
+
+/*virtual*/
+TQString ResourceKolab::subresourceIdentifier( Incidence *incidence )
+{
+ TQString uid = incidence->uid();
+ if ( mUidMap.contains( uid ) )
+ return mUidMap[ uid ].resource();
+ else
+ if ( mNewIncidencesMap.contains( uid ) )
+ return mNewIncidencesMap[ uid ];
+ else
+ return TQString();
+}
+
+
+bool ResourceKolab::unloadSubResource( const TQString& subResource )
+{
+ const bool silent = mSilent;
+ mSilent = true;
+ Kolab::UidMap::Iterator mapIt = mUidMap.begin();
+ TQPtrList<KCal::Incidence> incidences;
+ while ( mapIt != mUidMap.end() )
+ {
+ Kolab::UidMap::Iterator it = mapIt++;
+ const StorageReference ref = it.data();
+ if ( ref.resource() != subResource ) continue;
+ // FIXME incidence() is expensive
+ KCal::Incidence* incidence = mCalendar.incidence( it.key() );
+ if( incidence ) {
+ // register all observers first before actually deleting them
+ // in case of inter-incidence relations the other part will get
+ // the change notification otherwise
+ incidence->unRegisterObserver( this );
+ incidences.append( incidence );
+ }
+ mUidMap.remove( it );
+ }
+ TQPtrListIterator<KCal::Incidence> it( incidences );
+ for ( ; it.current(); ++it ) {
+ mCalendar.deleteIncidence( it.current() );
+ }
+ mSilent = silent;
+ return true;
+}
+
+TQString ResourceKolab::subresourceType( const TQString &resource )
+{
+ if ( mEventSubResources.contains( resource ) )
+ return "event";
+ if ( mTodoSubResources.contains( resource ) )
+ return "todo";
+ if ( mJournalSubResources.contains( resource ) )
+ return "journal";
+ return TQString();
+}
+
+void ResourceKolab::writeConfig()
+{
+ TDEConfig config( configFile() );
+ writeResourceConfig( config, mEventSubResources );
+ writeResourceConfig( config, mTodoSubResources );
+ writeResourceConfig( config, mJournalSubResources );
+}
+
+void ResourceKolab::beginAddingIncidences()
+{
+ mBatchAddingInProgress = true;
+}
+
+void ResourceKolab::endAddingIncidences()
+{
+ mBatchAddingInProgress = false;
+ mLastUsedResources.clear();
+}
+
+#include "resourcekolab.moc"