|
|
|
/*
|
|
|
|
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 <libkdepim/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 KConfig *config )
|
|
|
|
: ResourceCalendar( config ), ResourceKolabBase( "ResourceKolab-libkcal" ),
|
|
|
|
mCalendar( TQString::tqfromLatin1("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( KConfig& config,
|
|
|
|
const TQString& name,
|
|
|
|
const TQString& label,
|
|
|
|
bool writable,
|
|
|
|
bool alarmRelevant,
|
|
|
|
ResourceMap& subResource )
|
|
|
|
{
|
|
|
|
KConfigGroup group( &config, name );
|
|
|
|
bool active = group.readBoolEntry( "Active", true );
|
|
|
|
subResource.insert( name, Kolab::SubResource( active, writable,
|
|
|
|
alarmRelevant, label ) );
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ResourceKolab::openResource( KConfig& 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;
|
|
|
|
|
|
|
|
KConfig 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( KConfig& 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::tqcurrentDateTime() );
|
|
|
|
|
|
|
|
// 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").tqarg( 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" ).tqarg( incidence->summary() ) + "<br>";
|
|
|
|
if ( !incidence->location().isEmpty() )
|
|
|
|
text += i18n( "<b>Location:</b> %1" ).tqarg( incidence->location() );
|
|
|
|
text += "<br>";
|
|
|
|
if ( !incidence->doesFloat() )
|
|
|
|
text += i18n( "<b>Start:</b> %1, %2" )
|
|
|
|
.tqarg( incidence->dtStartDateStr(), incidence->dtStartTimeStr() );
|
|
|
|
else
|
|
|
|
text += i18n( "<b>Start:</b> %1" ).tqarg( 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" )
|
|
|
|
.tqarg( event->dtEndDateStr(), event->dtEndTimeStr() );
|
|
|
|
} else {
|
|
|
|
text += i18n( "<b>End:</b> %1" ).tqarg( 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->tqparent()->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;
|
|
|
|
|
|
|
|
KConfig 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
|
|
|
|
KConfig 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& tqparent )
|
|
|
|
{
|
|
|
|
kdDebug(5650) << "KCal Kolab resource - adding subresource: " << resource << endl;
|
|
|
|
TQString contentsType = kmailCalendarContentsType;
|
|
|
|
if ( !tqparent.isEmpty() ) {
|
|
|
|
if ( mEventSubResources.contains( tqparent ) )
|
|
|
|
contentsType = kmailCalendarContentsType;
|
|
|
|
else if ( mTodoSubResources.contains( tqparent ) )
|
|
|
|
contentsType = kmailTodoContentsType;
|
|
|
|
else if ( mJournalSubResources.contains( tqparent ) )
|
|
|
|
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, tqparent, 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()
|
|
|
|
{
|
|
|
|
KConfig 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"
|