summaryrefslogtreecommitdiffstats
path: root/kresources/kolab/kcal
diff options
context:
space:
mode:
Diffstat (limited to 'kresources/kolab/kcal')
-rw-r--r--kresources/kolab/kcal/event.cpp7
-rw-r--r--kresources/kolab/kcal/incidence.cpp248
-rw-r--r--kresources/kolab/kcal/incidence.h9
-rw-r--r--kresources/kolab/kcal/kolab.desktop1
-rw-r--r--kresources/kolab/kcal/resourcekolab.cpp437
-rw-r--r--kresources/kolab/kcal/resourcekolab.h64
-rw-r--r--kresources/kolab/kcal/task.cpp171
-rw-r--r--kresources/kolab/kcal/task.h16
8 files changed, 762 insertions, 191 deletions
diff --git a/kresources/kolab/kcal/event.cpp b/kresources/kolab/kcal/event.cpp
index 0f25eb73..e1d58a13 100644
--- a/kresources/kolab/kcal/event.cpp
+++ b/kresources/kolab/kcal/event.cpp
@@ -190,7 +190,9 @@ void Event::setFields( const KCal::Event* event )
{
Incidence::setFields( event );
- if ( event->hasEndDate() ) {
+ // note: if hasEndDate() is false and hasDuration() is true
+ // dtEnd() returns start+duration
+ if ( event->hasEndDate() || event->hasDuration() ) {
if ( event->doesFloat() ) {
// This is a floating event. Don't timezone move this one
mFloatingStatus = AllDay;
@@ -199,8 +201,9 @@ void Event::setFields( const KCal::Event* event )
mFloatingStatus = HasTime;
setEndDate( localToUTC( event->dtEnd() ) );
}
- } else
+ } else {
mHasEndDate = false;
+ }
setTransparency( event->transparency() );
}
diff --git a/kresources/kolab/kcal/incidence.cpp b/kresources/kolab/kcal/incidence.cpp
index 74f41fd8..de076eb9 100644
--- a/kresources/kolab/kcal/incidence.cpp
+++ b/kresources/kolab/kcal/incidence.cpp
@@ -39,6 +39,8 @@
#include <libkcal/journal.h>
#include <korganizer/version.h>
+#include <libemailfunctions/email.h>
+
#include <kdebug.h>
#include <kmdcodec.h>
#include <kurl.h>
@@ -50,7 +52,6 @@ using namespace Kolab;
Incidence::Incidence( KCal::ResourceKolab *res, const TQString &subResource, Q_UINT32 sernum,
const TQString& tz )
: KolabBase( tz ), mFloatingStatus( Unset ), mHasAlarm( false ),
- mRevision( 0 ),
mResource( res ),
mSubResource( subResource ),
mSernum( sernum )
@@ -163,16 +164,6 @@ TQString Incidence::internalUID() const
return mInternalUID;
}
-void Incidence::setRevision( int revision )
-{
- mRevision = revision;
-}
-
-int Incidence::revision() const
-{
- return mRevision;
-}
-
bool Incidence::loadAttendeeAttribute( TQDomElement& element,
Attendee& attendee )
{
@@ -183,8 +174,16 @@ bool Incidence::loadAttendeeAttribute( TQDomElement& element,
TQDomElement e = n.toElement();
TQString tagName = e.tagName();
- if ( tagName == "display-name" )
- attendee.displayName = e.text();
+ if ( tagName == "display-name" ) {
+ // Quote the text in case it contains commas or other quotable chars.
+ TQString tusername = KPIM::quoteNameIfNecessary( e.text() );
+
+ TQString tname, temail;
+ // ignore the return value because it will always be false since
+ // tusername does not contain "@domain".
+ KPIM::getNameAndMail( tusername, tname, temail );
+ attendee.displayName = tname;
+ }
else if ( tagName == "smtp-address" )
attendee.smtpAddress = e.text();
else if ( tagName == "status" )
@@ -249,6 +248,69 @@ void Incidence::saveAttachments( TQDomElement& element ) const
}
}
+void Incidence::saveAlarms( TQDomElement& element ) const
+{
+ if ( mAlarms.isEmpty() ) return;
+
+ TQDomElement list = element.ownerDocument().createElement( "advanced-alarms" );
+ element.appendChild( list );
+ for ( KCal::Alarm::List::ConstIterator it = mAlarms.constBegin(); it != mAlarms.constEnd(); ++it ) {
+ KCal::Alarm* a = *it;
+ TQDomElement e = list.ownerDocument().createElement( "alarm" );
+ list.appendChild( e );
+
+ writeString( e, "enabled", a->enabled() ? "1" : "0" );
+ if ( a->hasStartOffset() ) {
+ writeString( e, "start-offset", TQString::number( a->startOffset().asSeconds()/60 ) );
+ }
+ if ( a->hasEndOffset() ) {
+ writeString( e, "end-offset", TQString::number( a->endOffset().asSeconds()/60 ) );
+ }
+ if ( a->repeatCount() ) {
+ writeString( e, "repeat-count", TQString::number( a->repeatCount() ) );
+ writeString( e, "repeat-interval", TQString::number( a->snoozeTime() ) );
+ }
+
+ switch ( a->type() ) {
+ case KCal::Alarm::Invalid:
+ break;
+ case KCal::Alarm::Display:
+ e.setAttribute( "type", "display" );
+ writeString( e, "text", a->text() );
+ break;
+ case KCal::Alarm::Procedure:
+ e.setAttribute( "type", "procedure" );
+ writeString( e, "program", a->programFile() );
+ writeString( e, "arguments", a->programArguments() );
+ break;
+ case KCal::Alarm::Email:
+ {
+ e.setAttribute( "type", "email" );
+ TQDomElement addresses = e.ownerDocument().createElement( "addresses" );
+ e.appendChild( addresses );
+ for ( TQValueList<KCal::Person>::ConstIterator it = a->mailAddresses().constBegin(); it != a->mailAddresses().constEnd(); ++it ) {
+ writeString( addresses, "address", (*it).fullName() );
+ }
+ writeString( e, "subject", a->mailSubject() );
+ writeString( e, "mail-text", a->mailText() );
+ TQDomElement attachments = e.ownerDocument().createElement( "attachments" );
+ e.appendChild( attachments );
+ for ( TQStringList::ConstIterator it = a->mailAttachments().constBegin(); it != a->mailAttachments().constEnd(); ++it ) {
+ writeString( attachments, "attachment", *it );
+ }
+ break;
+ }
+ case KCal::Alarm::Audio:
+ e.setAttribute( "type", "audio" );
+ writeString( e, "file", a->audioFile() );
+ break;
+ default:
+ kdWarning() << "Unhandled alarm type:" << a->type() << endl;
+ break;
+ }
+ }
+}
+
void Incidence::saveRecurrence( TQDomElement& element ) const
{
TQDomElement e = element.ownerDocument().createElement( "recurrence" );
@@ -289,8 +351,14 @@ void Incidence::loadRecurrence( const TQDomElement& element )
TQDomElement e = n.toElement();
TQString tagName = e.tagName();
- if ( tagName == "interval" )
- mRecurrence.interval = e.text().toInt();
+ if ( tagName == "interval" ) {
+ //kolab/issue4229, sometimes the interval value can be empty
+ if ( e.text().isEmpty() || e.text().toInt() <= 0 ) {
+ mRecurrence.interval = 1;
+ } else {
+ mRecurrence.interval = e.text().toInt();
+ }
+ }
else if ( tagName == "day" ) // can be present multiple times
mRecurrence.days.append( e.text() );
else if ( tagName == "daynumber" )
@@ -309,6 +377,118 @@ void Incidence::loadRecurrence( const TQDomElement& element )
}
}
+static void loadAddressesHelper( const TQDomElement& element, KCal::Alarm* a )
+{
+ for ( TQDomNode n = element.firstChild(); !n.isNull(); n = n.nextSibling() ) {
+ if ( n.isComment() )
+ continue;
+ if ( n.isElement() ) {
+ TQDomElement e = n.toElement();
+ TQString tagName = e.tagName();
+
+ if ( tagName == "address" ) {
+ a->addMailAddress( KCal::Person( e.text() ) );
+ } else {
+ kdWarning() << "Unhandled tag" << tagName << endl;
+ }
+ }
+ }
+}
+
+static void loadAttachmentsHelper( const TQDomElement& element, KCal::Alarm* a )
+{
+ for ( TQDomNode n = element.firstChild(); !n.isNull(); n = n.nextSibling() ) {
+ if ( n.isComment() )
+ continue;
+ if ( n.isElement() ) {
+ TQDomElement e = n.toElement();
+ TQString tagName = e.tagName();
+
+ if ( tagName == "attachment" ) {
+ a->addMailAttachment( e.text() );
+ } else {
+ kdWarning() << "Unhandled tag" << tagName << endl;
+ }
+ }
+ }
+}
+
+static void loadAlarmHelper( const TQDomElement& element, KCal::Alarm* a )
+{
+ for ( TQDomNode n = element.firstChild(); !n.isNull(); n = n.nextSibling() ) {
+ if ( n.isComment() )
+ continue;
+ if ( n.isElement() ) {
+ TQDomElement e = n.toElement();
+ TQString tagName = e.tagName();
+
+ if ( tagName == "start-offset" ) {
+ a->setStartOffset( e.text().toInt()*60 );
+ } else if ( tagName == "end-offset" ) {
+ a->setEndOffset( e.text().toInt()*60 );
+ } else if ( tagName == "repeat-count" ) {
+ a->setRepeatCount( e.text().toInt() );
+ } else if ( tagName == "repeat-interval" ) {
+ a->setSnoozeTime( e.text().toInt() );
+ } else if ( tagName == "text" ) {
+ a->setText( e.text() );
+ } else if ( tagName == "program" ) {
+ a->setProgramFile( e.text() );
+ } else if ( tagName == "arguments" ) {
+ a->setProgramArguments( e.text() );
+ } else if ( tagName == "addresses" ) {
+ loadAddressesHelper( e, a );
+ } else if ( tagName == "subject" ) {
+ a->setMailSubject( e.text() );
+ } else if ( tagName == "mail-text" ) {
+ a->setMailText( e.text() );
+ } else if ( tagName == "attachments" ) {
+ loadAttachmentsHelper( e, a );
+ } else if ( tagName == "file" ) {
+ a->setAudioFile( e.text() );
+ } else if ( tagName == "enabled" ) {
+ a->setEnabled( e.text().toInt() != 0 );
+ } else {
+ kdWarning() << "Unhandled tag" << tagName << endl;
+ }
+ }
+ }
+}
+
+void Incidence::loadAlarms( const TQDomElement& element )
+{
+ for ( TQDomNode n = element.firstChild(); !n.isNull(); n = n.nextSibling() ) {
+ if ( n.isComment() )
+ continue;
+ if ( n.isElement() ) {
+ TQDomElement e = n.toElement();
+ TQString tagName = e.tagName();
+
+ if ( tagName == "alarm" ) {
+ KCal::Alarm *a = new KCal::Alarm( 0 );
+ a->setEnabled( true ); // default to enabled, unless some XML attribute says otherwise.
+ TQString type = e.attribute( "type" );
+ if ( type == "display" ) {
+ a->setType( KCal::Alarm::Display );
+ } else if ( type == "procedure" ) {
+ a->setType( KCal::Alarm::Procedure );
+ } else if ( type == "email" ) {
+ a->setType( KCal::Alarm::Email );
+ } else if ( type == "audio" ) {
+ a->setType( KCal::Alarm::Audio );
+ } else {
+ kdWarning() << "Unhandled alarm type:" << type << endl;
+ }
+
+ loadAlarmHelper( e, a );
+ mAlarms << a;
+ } else {
+ kdWarning() << "Unhandled tag" << tagName << endl;
+ }
+ }
+ }
+}
+
bool Incidence::loadAttribute( TQDomElement& element )
{
TQString tagName = element.tagName();
@@ -340,20 +520,17 @@ bool Incidence::loadAttribute( TQDomElement& element )
} else if ( tagName == "alarm" )
// Alarms should be minutes before. Libkcal uses event time + alarm time
setAlarm( - element.text().toInt() );
+ else if ( tagName == "advanced-alarms" )
+ loadAlarms( element );
else if ( tagName == "x-kde-internaluid" )
setInternalUID( element.text() );
- else if ( tagName == "revision" ) {
- bool ok;
- int revision = element.text().toInt( &ok );
- if ( ok )
- setRevision( revision );
- } else if ( tagName == "x-custom" )
+ else if ( tagName == "x-custom" )
loadCustomAttributes( element );
else {
bool ok = KolabBase::loadAttribute( element );
if ( !ok ) {
// Unhandled tag - save for later storage
- kdDebug() << "Saving unhandled tag " << element.tagName() << endl;
+ //kdDebug() << "Saving unhandled tag " << element.tagName() << endl;
Custom c;
c.key = TQCString( "X-KDE-KolabUnhandled-" ) + element.tagName().latin1();
c.value = element.text();
@@ -385,8 +562,8 @@ bool Incidence::saveAttributes( TQDomElement& element ) const
int alarmTime = qRound( -alarm() );
writeString( element, "alarm", TQString::number( alarmTime ) );
}
+ saveAlarms( element );
writeString( element, "x-kde-internaluid", internalUID() );
- writeString( element, "revision", TQString::number( revision() ) );
saveCustomAttributes( element );
return true;
}
@@ -424,13 +601,15 @@ static KCal::Attendee::PartStat attendeeStringToStatus( const TQString& s )
return KCal::Attendee::NeedsAction;
if ( s == "tentative" )
return KCal::Attendee::Tentative;
+ if ( s == "accepted" )
+ return KCal::Attendee::Accepted;
if ( s == "declined" )
return KCal::Attendee::Declined;
if ( s == "delegated" )
return KCal::Attendee::Delegated;
// Default:
- return KCal::Attendee::Accepted;
+ return KCal::Attendee::None;
}
static TQString attendeeStatusToString( KCal::Attendee::PartStat status )
@@ -649,6 +828,14 @@ void Incidence::setFields( const KCal::Incidence* incidence )
mAttachments.push_back( a );
}
+ mAlarms.clear();
+
+ // Alarms
+ const KCal::Alarm::List alarms = incidence->alarms();
+ for ( KCal::Alarm::List::ConstIterator it = alarms.begin(); it != alarms.end(); ++it ) {
+ mAlarms.push_back( *it );
+ }
+
if ( incidence->doesRecur() ) {
setRecurrence( incidence->recurrence() );
mRecurrence.exclusions = incidence->recurrence()->exDates();
@@ -711,10 +898,17 @@ void Incidence::saveTo( KCal::Incidence* incidence )
incidence->setSummary( summary() );
incidence->setLocation( location() );
- if ( mHasAlarm ) {
+ if ( mHasAlarm && mAlarms.isEmpty() ) {
KCal::Alarm* alarm = incidence->newAlarm();
alarm->setStartOffset( qRound( mAlarm * 60.0 ) );
alarm->setEnabled( true );
+ alarm->setType( KCal::Alarm::Display );
+ } else if ( !mAlarms.isEmpty() ) {
+ for ( KCal::Alarm::List::ConstIterator it = mAlarms.constBegin(); it != mAlarms.constEnd(); ++it ) {
+ KCal::Alarm *alarm = *it;
+ alarm->setParent( incidence );
+ incidence->addAlarm( alarm );
+ }
}
if ( organizer().displayName.isEmpty() )
@@ -803,7 +997,7 @@ void Incidence::saveTo( KCal::Incidence* incidence )
if ( hasPilotSyncStatus() )
incidence->setSyncStatus( pilotSyncStatus() );
- for( TQValueList<Custom>::ConstIterator it = mCustomList.begin(); it != mCustomList.end(); ++it ) {
+ for( TQValueList<Custom>::ConstIterator it = mCustomList.constBegin(); it != mCustomList.constEnd(); ++it ) {
incidence->setNonKDECustomProperty( (*it).key, (*it).value );
}
@@ -836,7 +1030,7 @@ void Incidence::loadAttachments()
TQString Incidence::productID() const
{
- return TQString( "KOrganizer " ) + korgVersion + ", Kolab resource";
+ return TQString( "KOrganizer %1, Kolab resource" ).arg( korgVersion );
}
// Unhandled KCal::Incidence fields:
diff --git a/kresources/kolab/kcal/incidence.h b/kresources/kolab/kcal/incidence.h
index 32b112aa..582d34c3 100644
--- a/kresources/kolab/kcal/incidence.h
+++ b/kresources/kolab/kcal/incidence.h
@@ -41,6 +41,7 @@ class TQDomElement;
namespace KCal {
class Incidence;
class Recurrence;
+ class Alarm;
class Attachment;
class ResourceKolab;
}
@@ -115,9 +116,6 @@ public:
void setInternalUID( const TQString& iuid );
TQString internalUID() const;
- virtual void setRevision( int );
- virtual int revision() const;
-
// Load the attributes of this class
virtual bool loadAttribute( TQDomElement& );
@@ -136,6 +134,9 @@ protected:
void saveAttendees( TQDomElement& element ) const;
void saveAttachments( TQDomElement& element ) const;
+ void loadAlarms( const TQDomElement& element );
+ void saveAlarms( TQDomElement& element ) const;
+
void loadRecurrence( const TQDomElement& element );
void saveRecurrence( TQDomElement& element ) const;
void saveCustomAttributes( TQDomElement& element ) const;
@@ -154,9 +155,9 @@ protected:
bool mHasAlarm;
Recurrence mRecurrence;
TQValueList<Attendee> mAttendees;
+ TQValueList<KCal::Alarm*> mAlarms;
TQValueList<KCal::Attachment*> mAttachments;
TQString mInternalUID;
- int mRevision;
struct Custom {
TQCString key;
diff --git a/kresources/kolab/kcal/kolab.desktop b/kresources/kolab/kcal/kolab.desktop
index 579e6406..c0a7daa5 100644
--- a/kresources/kolab/kcal/kolab.desktop
+++ b/kresources/kolab/kcal/kolab.desktop
@@ -21,7 +21,6 @@ Name[hu]=IMAP-kiszolgálón tárolt naptár a KMailen keresztül
Name[is]=Dagatal á IMAP þjóni gegnum KMail
Name[it]=Calendario su server IMAP via KMail
Name[ja]=KMail 経由 IMAP サーバのカレンダー
-Name[ka]=კალენდარი IMAP სერვერზე KMail-ის საშუალებით
Name[kk]=KMail арқылы IMAP серверіндегі күнтізбе
Name[km]=ប្រតិទិន​លើ​ម៉ាស៊ីន​បម្រើ IMAP តាម​រយៈ KMail
Name[lt]=Kalendorius IMAP serveryje per KMail
diff --git a/kresources/kolab/kcal/resourcekolab.cpp b/kresources/kolab/kcal/resourcekolab.cpp
index b61b1211..1f5f486f 100644
--- a/kresources/kolab/kcal/resourcekolab.cpp
+++ b/kresources/kolab/kcal/resourcekolab.cpp
@@ -41,7 +41,6 @@
#include <kio/uiserver_stub.h>
#include <kapplication.h>
#include <dcopclient.h>
-#include <libkcal/icalformat.h>
#include <libkdepim/kincidencechooser.h>
#include <kabc/locknull.h>
#include <kmainwindow.h>
@@ -72,8 +71,11 @@ static const char* incidenceInlineMimeType = "text/calendar";
ResourceKolab::ResourceKolab( const KConfig *config )
: ResourceCalendar( config ), ResourceKolabBase( "ResourceKolab-libkcal" ),
mCalendar( TQString::fromLatin1("UTC") ), mOpen( false ),mResourceChangedTimer( 0,
- "mResourceChangedTimer" )
+ "mResourceChangedTimer" ), mBatchAddingInProgress( false )
{
+ if ( !config ) {
+ setResourceName( i18n( "Kolab Server" ) );
+ }
setType( "imap" );
connect( &mResourceChangedTimer, TQT_SIGNAL( timeout() ),
this, TQT_SLOT( slotEmitResourceChanged() ) );
@@ -132,7 +134,7 @@ bool ResourceKolab::doOpen()
&& openResource( config, kmailJournalContentsType, mJournalSubResources );
}
-static void closeResource( KConfig& config, ResourceMap& map )
+static void writeResourceConfig( KConfig& config, ResourceMap& map )
{
ResourceMap::ConstIterator it;
for ( it = map.begin(); it != map.end(); ++it ) {
@@ -148,10 +150,7 @@ void ResourceKolab::doClose()
return;
mOpen = false;
- KConfig config( configFile() );
- closeResource( config, mEventSubResources );
- closeResource( config, mTodoSubResources );
- closeResource( config, mJournalSubResources );
+ writeConfig();
}
bool ResourceKolab::loadSubResource( const TQString& subResource,
@@ -217,11 +216,20 @@ bool ResourceKolab::loadSubResource( const TQString& subResource,
bool ResourceKolab::doLoad()
{
if (!mUidMap.isEmpty() ) {
+ emit resourceLoaded( this );
return true;
}
mUidMap.clear();
- return loadAllEvents() & loadAllTodos() & loadAllJournals();
+ 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 )
@@ -297,19 +305,54 @@ bool ResourceKolab::doSave()
&& kmailTriggerSync( kmailJournalContentsType );
*/
}
-void ResourceKolab::incidenceUpdatedSilent( KCal::IncidenceBase* incidencebase)
+void ResourceKolab::incidenceUpdatedSilent( KCal::IncidenceBase* incidencebase )
{
- const TQString uid = incidencebase->uid();
+ 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.replace( uid, incidencebase );
+ 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;
Q_UINT32 sernum = 0;
if ( mUidMap.contains( uid ) ) {
@@ -317,78 +360,85 @@ void ResourceKolab::incidenceUpdatedSilent( KCal::IncidenceBase* incidencebase)
sernum = mUidMap[ uid ].serialNumber();
mUidsPendingUpdate.append( uid );
}
- sendKMailUpdate( incidencebase, subResource, sernum );
+ sendKMailUpdate( incidencebase, subResource, sernum );
}
void ResourceKolab::incidenceUpdated( KCal::IncidenceBase* incidencebase )
{
- if ( incidencebase->isReadOnly() ) return;
+ 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, Q_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;
- }
- 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;
+ 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, Q_UINT32 sernum )
@@ -457,15 +507,24 @@ bool ResourceKolab::sendKMailUpdate( KCal::IncidenceBase* incidencebase, const T
TQStringList attURLs, attMimeTypes, attNames;
TQValueList<KTempFile*> tmpFiles;
for ( KCal::Attachment::List::ConstIterator it = atts.constBegin(); it != atts.constEnd(); ++it ) {
- KTempFile* tempFile = new KTempFile;
- TQCString decoded = KCodecs::base64Decode( TQCString( (*it)->data() ) );
- tempFile->file()->writeBlock( decoded.data(), decoded.length() );
- tempFile->close();
- KURL url;
- url.setPath( tempFile->name() );
- attURLs.append( url.url() );
- attMimeTypes.append( (*it)->mimeType() );
- attNames.append( (*it)->label() );
+ 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 ) ) {
@@ -474,8 +533,9 @@ bool ResourceKolab::sendKMailUpdate( KCal::IncidenceBase* incidencebase, const T
}
}
CustomHeaderMap customHeaders;
- if ( incidence->schedulingID() != incidence->uid() )
+ 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
@@ -499,34 +559,49 @@ bool ResourceKolab::addIncidence( KCal::Incidence* incidence, const TQString& _s
Q_UINT32 sernum )
{
Q_ASSERT( incidence );
- if ( !incidence ) return false;
+ 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" )
+ if ( type == "Event" ) {
map = &mEventSubResources;
- else if ( type == "Todo" )
+ } else if ( type == "Todo" ) {
map = &mTodoSubResources;
- else if ( type == "Journal" )
+ } else if ( type == "Journal" ) {
map = &mJournalSubResources;
- else
+ } 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" )
+ if ( incidence->type() == "Event" ) {
+ type = Events;
text += i18n( "Choose the folder where you want to store this event" );
- else if ( incidence->type() == "Todo" )
+ } else if ( incidence->type() == "Todo" ) {
+ type = Tasks;
text += i18n( "Choose the folder where you want to store this task" );
- else
+ } 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>";
@@ -541,24 +616,51 @@ bool ResourceKolab::addIncidence( KCal::Incidence* incidence, const TQString& _s
text += "<br>";
if ( incidence->type() == "Event" ) {
Event* event = static_cast<Event*>( incidence );
- if ( event->hasEndDate() )
- if ( !event->doesFloat() )
+ if ( event->hasEndDate() ) {
+ if ( !event->doesFloat() ) {
text += i18n( "<b>End:</b> %1, %2" )
.arg( event->dtEndDateStr(), event->dtEndTimeStr() );
- else
+ } else {
text += i18n( "<b>End:</b> %1" ).arg( event->dtEndDateStr() );
+ }
+ }
text += "<br>";
}
- subResource = findWritableResource( *map, text );
+
+ // 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() )
+ 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,
@@ -568,13 +670,14 @@ bool ResourceKolab::addIncidence( KCal::Incidence* incidence, const TQString& _s
/* 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 ) {
+ if ( newIncidence || sernum == 0 ) {
mCalendar.addIncidence( incidence );
- incidence->registerObserver( this );
+ incidence->registerObserver( this );
}
}
} else { /* KMail told us */
- bool ourOwnUpdate = mUidsPendingUpdate.contains( uid );
+ 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
@@ -592,6 +695,7 @@ bool ResourceKolab::addIncidence( KCal::Incidence* incidence, const TQString& _s
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! "
@@ -601,8 +705,13 @@ bool ResourceKolab::addIncidence( KCal::Incidence* incidence, const TQString& _s
} else {
// duplicate uid in a different folder, do the internal-uid tango
incidence->setSchedulingID( uid );
- incidence->setUid(CalFormat::createUniqueId( ) );
+
+ 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[qMakePair( incidence->schedulingID(), subResource )] = uid;
}
}
/* Add to the cache if the add didn't come from KOrganizer, in which case
@@ -636,13 +745,18 @@ bool ResourceKolab::addIncidence( KCal::Incidence* incidence, const TQString& _s
return true;
}
+bool ResourceKolab::addEvent( KCal::Event *event )
+{
+ return addEvent( event, TQString() );
+}
-bool ResourceKolab::addEvent( KCal::Event* event )
+bool ResourceKolab::addEvent( KCal::Event *event, const TQString &subResource )
{
- if ( mUidMap.contains( event->uid() ) )
+ if ( mUidMap.contains( event->uid() ) ) {
return true; //noop
- else
- return addIncidence( event, TQString::null, 0 );
+ } else {
+ return addIncidence( event, subResource, 0 );
+ }
}
void ResourceKolab::addEvent( const TQString& xml, const TQString& subresource,
@@ -650,14 +764,16 @@ void ResourceKolab::addEvent( const TQString& xml, const TQString& subresource,
{
KCal::Event* event = Kolab::Event::xmlToEvent( xml, mCalendar.timeZoneId(), this, subresource, sernum );
Q_ASSERT( event );
- if( event ) {
+ if ( event ) {
addIncidence( event, subresource, sernum );
}
}
bool ResourceKolab::deleteIncidence( KCal::Incidence* incidence )
{
- if ( incidence->isReadOnly() ) return false;
+ if ( incidence->isReadOnly() ) {
+ return false;
+ }
const TQString uid = incidence->uid();
if( !mUidMap.contains( uid ) ) return false; // Odd
@@ -709,12 +825,18 @@ KCal::Event::List ResourceKolab::rawEvents( const TQDate& start,
return mCalendar.rawEvents( start, end, inclusive );
}
-bool ResourceKolab::addTodo( KCal::Todo* todo )
+bool ResourceKolab::addTodo( KCal::Todo *todo )
{
- if ( mUidMap.contains( todo->uid() ) )
+ 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, TQString::null, 0 );
+ } else {
+ return addIncidence( todo, subResource, 0 );
+ }
}
void ResourceKolab::addTodo( const TQString& xml, const TQString& subresource,
@@ -722,8 +844,9 @@ void ResourceKolab::addTodo( const TQString& xml, const TQString& subresource,
{
KCal::Todo* todo = Kolab::Task::xmlToTask( xml, mCalendar.timeZoneId(), this, subresource, sernum );
Q_ASSERT( todo );
- if( todo )
- addIncidence( todo, subresource, sernum );
+ if ( todo ) {
+ addIncidence( todo, subresource, sernum );
+ }
}
bool ResourceKolab::deleteTodo( KCal::Todo* todo )
@@ -746,12 +869,17 @@ KCal::Todo::List ResourceKolab::rawTodosForDate( const TQDate& date )
return mCalendar.rawTodosForDate( date );
}
-bool ResourceKolab::addJournal( KCal::Journal* journal )
+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, TQString::null, 0 );
+ return addIncidence( journal, subResource, 0 );
}
void ResourceKolab::addJournal( const TQString& xml, const TQString& subresource,
@@ -839,27 +967,33 @@ bool ResourceKolab::fromKMailAddIncidence( const TQString& type,
bool rc = true;
TemporarySilencer t( this ); // RAII
if ( type != kmailCalendarContentsType && type != kmailTodoContentsType
- && type != kmailJournalContentsType )
+ && type != kmailJournalContentsType ) {
// Not ours
return false;
- if ( !subresourceActive( subResource ) ) return true;
+ }
+
+ if ( !subresourceActive( subResource ) ) {
+ return true;
+ }
if ( format == KMailICalIface::StorageXML ) {
// If this data file is one of ours, load it here
- if ( type == kmailCalendarContentsType )
+ if ( type == kmailCalendarContentsType ) {
addEvent( data, subResource, sernum );
- else if ( type == kmailTodoContentsType )
+ } else if ( type == kmailTodoContentsType ) {
addTodo( data, subResource, sernum );
- else if ( type == kmailJournalContentsType )
+ } else if ( type == kmailJournalContentsType ) {
addJournal( data, subResource, sernum );
- else
+ } else {
rc = false;
+ }
} else {
Incidence *inc = mFormat.fromString( data );
- if ( !inc )
- rc = false;
- else
+ if ( inc ) {
addIncidence( inc, subResource, sernum );
+ } else {
+ rc = false;
+ }
}
return rc;
}
@@ -881,13 +1015,25 @@ void ResourceKolab::fromKMailDelIncidence( const TQString& type,
// 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;
+
+ QPair<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( uid );
+ KCal::Incidence* incidence = mCalendar.incidence( uidToUse );
if( incidence ) {
incidence->unRegisterObserver( this );
mCalendar.deleteIncidence( incidence );
}
- mUidMap.remove( uid );
+ mUidMap.remove( uidToUse );
+ mOriginalUID2fakeUID.remove( p );
mResourceChangedTimer.changeInterval( 100 );
}
}
@@ -1039,6 +1185,23 @@ void ResourceKolab::setSubresourceActive( const TQString &subresource, bool v )
}
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()
@@ -1053,7 +1216,6 @@ KABC::Lock* ResourceKolab::lock()
return new KABC::LockNull( true );
}
-
Kolab::ResourceMap* ResourceKolab::subResourceMap( const TQString& contentsType )
{
if ( contentsType == kmailCalendarContentsType ) {
@@ -1122,6 +1284,7 @@ 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++;
@@ -1130,11 +1293,18 @@ bool ResourceKolab::unloadSubResource( const TQString& subResource )
// FIXME incidence() is expensive
KCal::Incidence* incidence = mCalendar.incidence( it.key() );
if( incidence ) {
- incidence->unRegisterObserver( this );
- mCalendar.deleteIncidence( 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;
}
@@ -1150,4 +1320,23 @@ TQString ResourceKolab::subresourceType( const TQString &resource )
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"
diff --git a/kresources/kolab/kcal/resourcekolab.h b/kresources/kolab/kcal/resourcekolab.h
index e68c2c6b..dda5ba32 100644
--- a/kresources/kolab/kcal/resourcekolab.h
+++ b/kresources/kolab/kcal/resourcekolab.h
@@ -70,8 +70,9 @@ public:
void doClose();
// The libkcal functions. See the resource for descriptions
- bool addEvent( KCal::Event* anEvent );
- bool deleteEvent( KCal::Event* );
+ KDE_DEPRECATED bool addEvent( KCal::Event *event );
+ bool addEvent( KCal::Event *event, const TQString &subResource );
+ bool deleteEvent( KCal::Event * );
KCal::Event* event( const TQString &UniqueStr );
KCal::Event::List rawEvents( EventSortField sortField = EventSortUnsorted, SortDirection sortDirection = SortDirectionAscending );
KCal::Event::List rawEventsForDate(
@@ -82,15 +83,17 @@ public:
KCal::Event::List rawEvents( const TQDate& start, const TQDate& end,
bool inclusive = false );
- bool addTodo( KCal::Todo* todo );
- bool deleteTodo( KCal::Todo* );
- KCal::Todo* todo( const TQString& uid );
+ KDE_DEPRECATED bool addTodo( KCal::Todo * todo );
+ bool addTodo( KCal::Todo *todo, const TQString &subResource );
+ bool deleteTodo( KCal::Todo * );
+ KCal::Todo* todo( const TQString &uid );
KCal::Todo::List rawTodos( TodoSortField sortField = TodoSortUnsorted, SortDirection sortDirection = SortDirectionAscending );
KCal::Todo::List rawTodosForDate( const TQDate& date );
- bool addJournal( KCal::Journal* );
- bool deleteJournal( KCal::Journal* );
- KCal::Journal* journal( const TQString& uid );
+ KDE_DEPRECATED bool addJournal( KCal::Journal * );
+ bool addJournal( KCal::Journal *, const TQString &subResource );
+ bool deleteJournal( KCal::Journal * );
+ KCal::Journal* journal( const TQString &uid );
KCal::Journal::List rawJournals( JournalSortField sortField = JournalSortUnsorted, SortDirection sortDirection = SortDirectionAscending );
KCal::Journal::List rawJournalsForDate( const TQDate &date );
@@ -128,6 +131,9 @@ public:
/** (De)activate the subresource */
virtual void setSubresourceActive( const TQString &, bool );
+ /** Is this subresource writable? */
+ bool subresourceWritable( const TQString& ) const;
+
/** What is the label for this subresource? */
virtual const TQString labelForSubresource( const TQString& resource ) const;
@@ -140,10 +146,16 @@ public:
KABC::Lock* lock();
+ void beginAddingIncidences();
+
+ void endAddingIncidences();
+
signals:
void useGlobalMode();
protected slots:
void slotEmitResourceChanged();
+ void writeConfig();
+
protected:
/**
* Return list of alarms which are relevant for the current user. These
@@ -157,7 +169,11 @@ private:
void addIncidence( const char* mimetype, const TQString& xml,
const TQString& subResource, Q_UINT32 sernum );
- bool addIncidence( KCal::Incidence* i, const TQString& subresource,
+
+ /**
+ Caller guarantees i is not null.
+ */
+ bool addIncidence( KCal::Incidence *i, const TQString& subresource,
Q_UINT32 sernum );
void addEvent( const TQString& xml, const TQString& subresource,
@@ -215,6 +231,36 @@ private:
*/
TQMap<TQString, TQString> mNewIncidencesMap;
int mProgressDialogIncidenceLimit;
+
+ /**
+ * If a user has a subresource for viewing another user's folder then it can happen
+ * that addIncidence(...) adds an incidence with an already existing UID.
+ *
+ * When this happens, addIncidence(...) sets a new random UID and stores the
+ * original UID using incidence->setSchedulingID(uid) because KCal doesn't
+ * allow two incidences to have the same UID.
+ *
+ * This map keeps track of the generated UIDs (which are local) so we can delete the
+ * right incidence inside fromKMailDelIncidence(...) whenever we sync.
+ *
+ * The key is originalUID,subResource and the value is the fake UID.
+ */
+ TQMap< QPair<TQString, TQString>, TQString > mOriginalUID2fakeUID;
+
+ bool mBatchAddingInProgress;
+ TQMap<Kolab::ResourceType,TQString> mLastUsedResources;
+
+ /**
+ Indexed by uid, it holds the last known revision of an incidence.
+ If we receive an update where the incidence still has the same
+ revision as the last known, we ignore it and don't send it to kmail,
+ because shortly after, IncidenceChanger will increment the revision
+ and that will trigger another update.
+
+ If we didn't discard the first update, kmail would have been updated twice.
+ */
+ TQMap<TQString,int> mLastKnownRevisions;
+
};
struct TemporarySilencer {
diff --git a/kresources/kolab/kcal/task.cpp b/kresources/kolab/kcal/task.cpp
index 7bbdba97..36876b7d 100644
--- a/kresources/kolab/kcal/task.cpp
+++ b/kresources/kolab/kcal/task.cpp
@@ -38,6 +38,41 @@
using namespace Kolab;
+// Kolab Storage Specification:
+// "The priority can be a number between 1 and 5, with 1 being the highest priority."
+// iCalendar (RFC 2445):
+// "The priority is specified as an integer in the range
+// zero to nine. A value of zero specifies an
+// undefined priority. A value of one is the
+// highest priority. A value of nine is the lowest
+// priority."
+
+static int kcalPriorityToKolab( const int kcalPriority )
+{
+ if ( kcalPriority >= 0 && kcalPriority <= 9 ) {
+ // We'll map undefined (0) to 3 (default)
+ // 0 1 2 3 4 5 6 7 8 9
+ static const int priorityMap[10] = { 3, 1, 1, 2, 2, 3, 3, 4, 4, 5 };
+ return priorityMap[kcalPriority];
+ }
+ else {
+ kdWarning() << "kcalPriorityToKolab(): Got invalid priority " << kcalPriority << endl;
+ return 3;
+ }
+}
+
+static int kolabPrioritytoKCal( const int kolabPriority )
+{
+ if ( kolabPriority >= 1 && kolabPriority <= 5 ) {
+ // 1 2 3 4 5
+ static const int priorityMap[5] = { 1, 3, 5, 7, 9 };
+ return priorityMap[kolabPriority - 1];
+ }
+ else {
+ kdWarning() << "kolabPrioritytoKCal(): Got invalid priority " << kolabPriority << endl;
+ return 5;
+ }
+}
KCal::Todo* Task::xmlToTask( const TQString& xml, const TQString& tz, KCal::ResourceKolab *res,
const TQString& subResource, Q_UINT32 sernum )
@@ -115,6 +150,26 @@ void Task::setDueDate( const TQDateTime& date )
{
mDueDate = date;
mHasDueDate = true;
+ mFloatingStatus = HasTime;
+}
+
+void Task::setDueDate( const TQDate &date )
+{
+ mDueDate = date;
+ mHasDueDate = true;
+ mFloatingStatus = AllDay;
+}
+
+
+void Task::setDueDate( const TQString &date )
+{
+ if ( date.length() > 10 ) {
+ // This is a date + time
+ setDueDate( stringToDateTime( date ) );
+ } else {
+ // This is only a date
+ setDueDate( stringToDate( date ) );
+ }
}
TQDateTime Task::dueDate() const
@@ -159,10 +214,18 @@ bool Task::loadAttribute( TQDomElement& element )
if ( tagName == "priority" ) {
bool ok;
- int priority = element.text().toInt( &ok );
- if ( !ok || priority < 0 || priority > 9 )
- priority = 5;
- setPriority( priority );
+ mKolabPriorityFromDom = element.text().toInt( &ok );
+ if ( !ok || mKolabPriorityFromDom < 1 || mKolabPriorityFromDom > 5 ) {
+ kdWarning() << "loadAttribute(): Invalid \"priority\" value: " << element.text() << endl;
+ mKolabPriorityFromDom = -1;
+ }
+ } else if ( tagName == "x-kcal-priority" ) {
+ bool ok;
+ mKCalPriorityFromDom = element.text().toInt( &ok );
+ if ( !ok || mKCalPriorityFromDom < 0 || mKCalPriorityFromDom > 9 ) {
+ kdWarning() << "loadAttribute(): Invalid \"x-kcal-priority\" value: " << element.text() << endl;
+ mKCalPriorityFromDom = -1;
+ }
} else if ( tagName == "completed" ) {
bool ok;
int percent = element.text().toInt( &ok );
@@ -182,13 +245,13 @@ bool Task::loadAttribute( TQDomElement& element )
else
// Default
setStatus( KCal::Incidence::StatusNone );
- } else if ( tagName == "due-date" )
- setDueDate( stringToDateTime( element.text() ) );
- else if ( tagName == "parent" )
+ } else if ( tagName == "due-date" ) {
+ setDueDate( element.text() );
+ } else if ( tagName == "parent" ) {
setParent( element.text() );
- else if ( tagName == "x-completed-date" )
+ } else if ( tagName == "x-completed-date" ) {
setCompletedDate( stringToDateTime( element.text() ) );
- else if ( tagName == "start-date" ) {
+ } else if ( tagName == "start-date" ) {
setHasStartDate( true );
setStartDate( element.text() );
} else
@@ -203,7 +266,11 @@ bool Task::saveAttributes( TQDomElement& element ) const
// Save the base class elements
Incidence::saveAttributes( element );
- writeString( element, "priority", TQString::number( priority() ) );
+ // We need to save x-kcal-priority as well, since the Kolab priority can only save values from
+ // 1 to 5, but we have values from 0 to 9, and do not want to loose them
+ writeString( element, "priority", TQString::number( kcalPriorityToKolab( priority() ) ) );
+ writeString( element, "x-kcal-priority", TQString::number( priority() ) );
+
writeString( element, "completed", TQString::number( percentCompleted() ) );
switch( status() ) {
@@ -232,14 +299,21 @@ bool Task::saveAttributes( TQDomElement& element ) const
break;
}
- if ( hasDueDate() )
- writeString( element, "due-date", dateTimeToString( dueDate() ) );
+ if ( hasDueDate() ) {
+ if ( mFloatingStatus == HasTime ) {
+ writeString( element, "due-date", dateTimeToString( dueDate() ) );
+ } else {
+ writeString( element, "due-date", dateToString( dueDate().date() ) );
+ }
+ }
- if ( !parent().isNull() )
+ if ( !parent().isNull() ) {
writeString( element, "parent", parent() );
+ }
- if ( hasCompletedDate() && percentCompleted() == 100)
+ if ( hasCompletedDate() && percentCompleted() == 100 ) {
writeString( element, "x-completed-date", dateTimeToString( completedDate() ) );
+ }
return true;
}
@@ -247,6 +321,9 @@ bool Task::saveAttributes( TQDomElement& element ) const
bool Task::loadXML( const TQDomDocument& document )
{
+ mKolabPriorityFromDom = -1;
+ mKCalPriorityFromDom = -1;
+
TQDomElement top = document.documentElement();
if ( top.tagName() != "task" ) {
@@ -269,6 +346,7 @@ bool Task::loadXML( const TQDomDocument& document )
}
loadAttachments();
+ decideAndSetPriority();
return true;
}
@@ -278,7 +356,7 @@ TQString Task::saveXML() const
TQDomElement element = document.createElement( "task" );
element.setAttribute( "version", "1.0" );
saveAttributes( element );
- if ( !hasStartDate() ) {
+ if ( !hasStartDate() && startDate().isValid() ) {
// events and journals always have a start date, but tasks don't.
// Remove the entry done by the inherited save above, because we
// don't have one.
@@ -299,21 +377,68 @@ void Task::setFields( const KCal::Todo* task )
setStatus( task->status() );
setHasStartDate( task->hasStartDate() );
- if ( task->hasDueDate() )
+ if ( task->hasDueDate() ) {
setDueDate( localToUTC( task->dtDue() ) );
- else
+ if ( task->doesFloat() ) {
+ // This is a floating task. Don't timezone move this one
+ mFloatingStatus = AllDay;
+ setDueDate( task->dtDue().date() );
+ } else {
+ mFloatingStatus = HasTime;
+ setDueDate( localToUTC( task->dtDue() ) );
+ }
+ } else {
mHasDueDate = false;
- if ( task->relatedTo() )
+ }
+
+ if ( task->relatedTo() ) {
setParent( task->relatedTo()->uid() );
- else if ( !task->relatedToUid().isEmpty() )
- setParent( task->relatedToUid() );
- else
+ } else if ( !task->relatedToUid().isEmpty() ) {
+ setParent( task->relatedToUid( ) );
+ } else {
setParent( TQString::null );
+ }
- if ( task->hasCompletedDate() && task->percentComplete() == 100 )
+ if ( task->hasCompletedDate() && task->percentComplete() == 100 ) {
setCompletedDate( localToUTC( task->completed() ) );
- else
+ } else {
mHasCompletedDate = false;
+ }
+}
+
+void Task::decideAndSetPriority()
+{
+ // If we have both Kolab and KCal values in the XML, we prefer the KCal value, but only if the
+ // values are still in sync
+ if ( mKolabPriorityFromDom != -1 && mKCalPriorityFromDom != -1 ) {
+ const bool inSync = ( kcalPriorityToKolab( mKCalPriorityFromDom ) == mKolabPriorityFromDom );
+ if ( inSync ) {
+ setPriority( mKCalPriorityFromDom );
+ }
+ else {
+ // Out of sync, some other client changed the Kolab priority, so we have to ignore our
+ // KCal priority
+ setPriority( kolabPrioritytoKCal( mKolabPriorityFromDom ) );
+ }
+ }
+
+ // Only KCal priority set, use that.
+ else if ( mKolabPriorityFromDom == -1 && mKCalPriorityFromDom != -1 ) {
+ kdWarning() << "decideAndSetPriority(): No Kolab priority found, only the KCal priority!" << endl;
+ setPriority( mKCalPriorityFromDom );
+ }
+
+ // Only Kolab priority set, use that
+ else if ( mKolabPriorityFromDom != -1 && mKCalPriorityFromDom == -1 ) {
+ setPriority( kolabPrioritytoKCal( mKolabPriorityFromDom ) );
+ }
+
+ // No priority set, use the default
+ else {
+ // According the RFC 2445, we should use 0 here, for undefined priority, but AFAIK KOrganizer
+ // doesn't support that, so we'll use 5.
+ setPriority( 5 );
+ }
}
void Task::saveTo( KCal::Todo* task )
diff --git a/kresources/kolab/kcal/task.h b/kresources/kolab/kcal/task.h
index 5dfb5585..f7e7c6d5 100644
--- a/kresources/kolab/kcal/task.h
+++ b/kresources/kolab/kcal/task.h
@@ -86,7 +86,9 @@ public:
virtual void setHasStartDate( bool );
virtual bool hasStartDate() const;
- virtual void setDueDate( const TQDateTime& date );
+ virtual void setDueDate( const TQDateTime &date );
+ virtual void setDueDate( const TQString &date );
+ virtual void setDueDate( const TQDate &date );
virtual TQDateTime dueDate() const;
virtual bool hasDueDate() const;
@@ -110,7 +112,19 @@ protected:
// Read all known fields from this ical todo
void setFields( const KCal::Todo* );
+ // This sets the priority of this task by looking at mKolabPriorityFromDom and
+ // mKCalPriorityFromDom.
+ void decideAndSetPriority();
+
+ // This is the KCal priority, not the Kolab priority.
+ // See kcalPriorityToKolab() and kolabPrioritytoKCal().
int mPriority;
+
+ // Those priority values are the raw values read by loadAttribute().
+ // They will be converted later in decideAndSetPriority().
+ int mKolabPriorityFromDom;
+ int mKCalPriorityFromDom;
+
int mPercentCompleted;
KCal::Incidence::Status mStatus;
TQString mParent;