summaryrefslogtreecommitdiffstats
path: root/tderesources/kolab/kcal
diff options
context:
space:
mode:
Diffstat (limited to 'tderesources/kolab/kcal')
-rw-r--r--tderesources/kolab/kcal/CMakeLists.txt57
-rw-r--r--tderesources/kolab/kcal/Makefile.am27
-rw-r--r--tderesources/kolab/kcal/event.cpp223
-rw-r--r--tderesources/kolab/kcal/event.h102
-rw-r--r--tderesources/kolab/kcal/incidence.cpp1039
-rw-r--r--tderesources/kolab/kcal/incidence.h175
-rw-r--r--tderesources/kolab/kcal/journal.cpp184
-rw-r--r--tderesources/kolab/kcal/journal.h103
-rw-r--r--tderesources/kolab/kcal/kolab.desktop52
-rw-r--r--tderesources/kolab/kcal/resourcekolab.cpp1342
-rw-r--r--tderesources/kolab/kcal/resourcekolab.h284
-rw-r--r--tderesources/kolab/kcal/resourcekolab_plugin.cpp49
-rw-r--r--tderesources/kolab/kcal/task.cpp461
-rw-r--r--tderesources/kolab/kcal/task.h143
14 files changed, 4241 insertions, 0 deletions
diff --git a/tderesources/kolab/kcal/CMakeLists.txt b/tderesources/kolab/kcal/CMakeLists.txt
new file mode 100644
index 000000000..9f932b5e1
--- /dev/null
+++ b/tderesources/kolab/kcal/CMakeLists.txt
@@ -0,0 +1,57 @@
+#################################################
+#
+# (C) 2010-2011 Serghei Amelian
+# serghei (DOT) amelian (AT) gmail.com
+#
+# Improvements and feedback are welcome
+#
+# This file is released under GPL >= 2
+#
+#################################################
+
+include_directories(
+ ${CMAKE_CURRENT_BINARY_DIR}
+ ${CMAKE_CURRENT_SOURCE_DIR}/../shared
+ ${CMAKE_CURRENT_SOURCE_DIR}/../kcal
+ ${CMAKE_SOURCE_DIR}
+ ${CMAKE_SOURCE_DIR}/libtdepim
+ ${TDE_INCLUDE_DIR}
+ ${TQT_INCLUDE_DIRS}
+)
+
+link_directories(
+ ${TQT_LIBRARY_DIRS}
+)
+
+
+##### other data ################################
+
+install(
+ FILES kolab.desktop
+ DESTINATION ${SERVICES_INSTALL_DIR}/tderesources/kcal )
+
+install(
+ FILES ${CMAKE_CURRENT_SOURCE_DIR}/../uninstall.desktop
+ DESTINATION ${SERVICES_INSTALL_DIR}/tderesources/kcal
+ RENAME imap.desktop)
+
+
+##### kcal_kolab (module) #######################
+
+tde_add_kpart( kcal_kolab AUTOMOC
+ SOURCES
+ resourcekolab_plugin.cpp
+ LINK kcalkolab-shared
+ DESTINATION ${PLUGIN_INSTALL_DIR}
+)
+
+
+##### kcalkolab (shared) ########################
+
+tde_add_library( kcalkolab SHARED AUTOMOC
+ SOURCES
+ incidence.cpp event.cpp task.cpp journal.cpp resourcekolab.cpp
+ VERSION 0.0.0
+ LINK resourcekolabshared-static kgroupwarebase-shared
+ DESTINATION ${LIB_INSTALL_DIR}
+)
diff --git a/tderesources/kolab/kcal/Makefile.am b/tderesources/kolab/kcal/Makefile.am
new file mode 100644
index 000000000..a25027919
--- /dev/null
+++ b/tderesources/kolab/kcal/Makefile.am
@@ -0,0 +1,27 @@
+METASOURCES = AUTO
+
+INCLUDES = -I$(top_srcdir)/tderesources/kolab/shared -I$(top_srcdir) \
+ -I$(top_builddir)/libtdepim $(all_includes)
+
+# The kolab wizard links to this library too
+lib_LTLIBRARIES = libkcalkolab.la
+
+libkcalkolab_la_SOURCES = incidence.cpp event.cpp task.cpp journal.cpp resourcekolab.cpp
+libkcalkolab_la_LDFLAGS = $(all_libraries) -no-undefined
+libkcalkolab_la_LIBADD = $(top_builddir)/libkcal/libkcal.la \
+ $(top_builddir)/tderesources/kolab/shared/libresourcekolabshared.la \
+ -ltderesources
+
+kde_module_LTLIBRARIES = kcal_kolab.la
+
+kcal_kolab_la_SOURCES = resourcekolab_plugin.cpp
+kcal_kolab_la_LDFLAGS = $(all_libraries) -module $(KDE_PLUGIN) -no-undefined
+kcal_kolab_la_LIBADD = libkcalkolab.la
+
+servicedir = $(kde_servicesdir)/tderesources/kcal
+service_DATA = kolab.desktop
+
+install-data-local: $(srcdir)/../uninstall.desktop
+ $(mkinstalldirs) $(DESTDIR)$(servicedir)
+ $(INSTALL_DATA) $(srcdir)/../uninstall.desktop $(DESTDIR)$(servicedir)/imap.desktop
+
diff --git a/tderesources/kolab/kcal/event.cpp b/tderesources/kolab/kcal/event.cpp
new file mode 100644
index 000000000..c15c567d8
--- /dev/null
+++ b/tderesources/kolab/kcal/event.cpp
@@ -0,0 +1,223 @@
+/*
+ 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>
+
+ 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 "event.h"
+
+#include <libkcal/event.h>
+#include <kdebug.h>
+
+using namespace Kolab;
+
+
+KCal::Event* Event::xmlToEvent( const TQString& xml, const TQString& tz, KCal::ResourceKolab* res,
+ const TQString& subResource, TQ_UINT32 sernum )
+{
+ Event event( res, subResource, sernum, tz );
+ event.load( xml );
+ KCal::Event* kcalEvent = new KCal::Event();
+ event.saveTo( kcalEvent );
+ return kcalEvent;
+}
+
+TQString Event::eventToXML( KCal::Event* kcalEvent, const TQString& tz )
+{
+ Event event( 0, TQString(), 0, tz, kcalEvent );
+ return event.saveXML();
+}
+
+Event::Event( KCal::ResourceKolab *res, const TQString &subResource, TQ_UINT32 sernum,
+ const TQString& tz, KCal::Event* event )
+ : Incidence( res, subResource, sernum, tz ),
+ mShowTimeAs( KCal::Event::Opaque ), mHasEndDate( false )
+{
+ if ( event )
+ setFields( event );
+}
+
+Event::~Event()
+{
+}
+
+void Event::setTransparency( KCal::Event::Transparency transparency )
+{
+ mShowTimeAs = transparency;
+}
+
+KCal::Event::Transparency Event::transparency() const
+{
+ return mShowTimeAs;
+}
+
+void Event::setEndDate( const TQDateTime& date )
+{
+ mEndDate = date;
+ mHasEndDate = true;
+ if ( mFloatingStatus == AllDay )
+ kdDebug() << "ERROR: Time on end date but no time on the event\n";
+ mFloatingStatus = HasTime;
+}
+
+void Event::setEndDate( const TQDate& date )
+{
+ mEndDate = date;
+ mHasEndDate = true;
+ if ( mFloatingStatus == HasTime )
+ kdDebug() << "ERROR: No time on end date but time on the event\n";
+ mFloatingStatus = AllDay;
+}
+
+void Event::setEndDate( const TQString& endDate )
+{
+ if ( endDate.length() > 10 )
+ // This is a date + time
+ setEndDate( stringToDateTime( endDate ) );
+ else
+ // This is only a date
+ setEndDate( stringToDate( endDate ) );
+}
+
+TQDateTime Event::endDate() const
+{
+ return mEndDate;
+}
+
+bool Event::loadAttribute( TQDomElement& element )
+{
+ // This method doesn't handle the color-label tag yet
+ TQString tagName = element.tagName();
+
+ if ( tagName == "show-time-as" ) {
+ // TODO: Support tentative and outofoffice
+ if ( element.text() == "free" )
+ setTransparency( KCal::Event::Transparent );
+ else
+ setTransparency( KCal::Event::Opaque );
+ } else if ( tagName == "end-date" )
+ setEndDate( element.text() );
+ else
+ return Incidence::loadAttribute( element );
+
+ // We handled this
+ return true;
+}
+
+bool Event::saveAttributes( TQDomElement& element ) const
+{
+ // Save the base class elements
+ Incidence::saveAttributes( element );
+
+ // TODO: Support tentative and outofoffice
+ if ( transparency() == KCal::Event::Transparent )
+ writeString( element, "show-time-as", "free" );
+ else
+ writeString( element, "show-time-as", "busy" );
+ if ( mHasEndDate ) {
+ if ( mFloatingStatus == HasTime )
+ writeString( element, "end-date", dateTimeToString( endDate() ) );
+ else
+ writeString( element, "end-date", dateToString( endDate().date() ) );
+ }
+
+ return true;
+}
+
+
+bool Event::loadXML( const TQDomDocument& document )
+{
+ TQDomElement top = document.documentElement();
+
+ if ( top.tagName() != "event" ) {
+ tqWarning( "XML error: Top tag was %s instead of the expected event",
+ top.tagName().ascii() );
+ return false;
+ }
+
+ for ( TQDomNode n = top.firstChild(); !n.isNull(); n = n.nextSibling() ) {
+ if ( n.isComment() )
+ continue;
+ if ( n.isElement() ) {
+ TQDomElement e = n.toElement();
+ loadAttribute( e );
+ } else
+ kdDebug() << "Node is not a comment or an element???" << endl;
+ }
+
+ loadAttachments();
+ return true;
+}
+
+TQString Event::saveXML() const
+{
+ TQDomDocument document = domTree();
+ TQDomElement element = document.createElement( "event" );
+ element.setAttribute( "version", "1.0" );
+ saveAttributes( element );
+ document.appendChild( element );
+ return document.toString();
+}
+
+void Event::setFields( const KCal::Event* event )
+{
+ Incidence::setFields( event );
+
+ // 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;
+ setEndDate( event->dtEnd().date() );
+ } else {
+ mFloatingStatus = HasTime;
+ setEndDate( localToUTC( event->dtEnd() ) );
+ }
+ } else {
+ mHasEndDate = false;
+ }
+ setTransparency( event->transparency() );
+}
+
+void Event::saveTo( KCal::Event* event )
+{
+ Incidence::saveTo( event );
+
+ event->setHasEndDate( mHasEndDate );
+ if ( mHasEndDate ) {
+ if ( mFloatingStatus == AllDay )
+ // This is a floating event. Don't timezone move this one
+ event->setDtEnd( endDate() );
+ else
+ event->setDtEnd( utcToLocal( endDate() ) );
+ }
+ event->setTransparency( transparency() );
+}
diff --git a/tderesources/kolab/kcal/event.h b/tderesources/kolab/kcal/event.h
new file mode 100644
index 000000000..57aa3358f
--- /dev/null
+++ b/tderesources/kolab/kcal/event.h
@@ -0,0 +1,102 @@
+/*
+ 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>
+
+ 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.
+*/
+
+#ifndef KOLAB_EVENT_H
+#define KOLAB_EVENT_H
+
+#include "incidence.h"
+
+#include <libkcal/event.h>
+
+class TQDomElement;
+
+
+namespace Kolab {
+
+/**
+ * This class represents an event, and knows how to load/save it
+ * from/to XML, and from/to a KCal::Event.
+ * The instances of this class are temporary, only used to convert
+ * one to the other.
+ */
+class Event : public Incidence {
+public:
+ /// Use this to parse an xml string to a event entry
+ /// The caller is responsible for deleting the returned event
+ static KCal::Event* xmlToEvent( const TQString& xml, const TQString& tz, KCal::ResourceKolab* res = 0,
+ const TQString& subResource = TQString(), TQ_UINT32 sernum = 0 );
+
+ /// Use this to get an xml string describing this event entry
+ static TQString eventToXML( KCal::Event*, const TQString& tz );
+
+ /// Create a event object and
+ explicit Event( KCal::ResourceKolab *res, const TQString &subResource, TQ_UINT32 sernum,
+ const TQString& tz, KCal::Event* event = 0 );
+ virtual ~Event();
+
+ void saveTo( KCal::Event* event );
+
+ virtual TQString type() const { return "Event"; }
+
+ virtual void setTransparency( KCal::Event::Transparency transparency );
+ virtual KCal::Event::Transparency transparency() const;
+
+ virtual void setEndDate( const TQDateTime& date );
+ virtual void setEndDate( const TQDate& date );
+ virtual void setEndDate( const TQString& date );
+ virtual TQDateTime endDate() const;
+
+ // Load the attributes of this class
+ virtual bool loadAttribute( TQDomElement& );
+
+ // Save the attributes of this class
+ virtual bool saveAttributes( TQDomElement& ) const;
+
+ // Load this event by reading the XML file
+ virtual bool loadXML( const TQDomDocument& xml );
+
+ // Serialize this event to an XML string
+ virtual TQString saveXML() const;
+
+protected:
+ // Read all known fields from this ical incidence
+ void setFields( const KCal::Event* );
+
+ KCal::Event::Transparency mShowTimeAs;
+ TQDateTime mEndDate;
+ bool mHasEndDate;
+};
+
+}
+
+#endif // KOLAB_EVENT_H
diff --git a/tderesources/kolab/kcal/incidence.cpp b/tderesources/kolab/kcal/incidence.cpp
new file mode 100644
index 000000000..ddc31491b
--- /dev/null
+++ b/tderesources/kolab/kcal/incidence.cpp
@@ -0,0 +1,1039 @@
+/*
+ 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>
+
+ 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 "incidence.h"
+#include "resourcekolab.h"
+
+#include <tqfile.h>
+#include <tqvaluelist.h>
+
+#include <libkcal/journal.h>
+#include <korganizer/version.h>
+#include <libemailfunctions/email.h>
+
+#include <kdebug.h>
+#include <kmdcodec.h>
+#include <kurl.h>
+#include <kio/netaccess.h>
+
+using namespace Kolab;
+
+
+Incidence::Incidence( KCal::ResourceKolab *res, const TQString &subResource, TQ_UINT32 sernum,
+ const TQString& tz )
+ : KolabBase( tz ), mFloatingStatus( Unset ), mHasAlarm( false ),
+ mResource( res ),
+ mSubResource( subResource ),
+ mSernum( sernum )
+{
+}
+
+Incidence::~Incidence()
+{
+}
+
+void Incidence::setSummary( const TQString& summary )
+{
+ mSummary = summary;
+}
+
+TQString Incidence::summary() const
+{
+ return mSummary;
+}
+
+void Incidence::setLocation( const TQString& location )
+{
+ mLocation = location;
+}
+
+TQString Incidence::location() const
+{
+ return mLocation;
+}
+
+void Incidence::setOrganizer( const Email& organizer )
+{
+ mOrganizer = organizer;
+}
+
+KolabBase::Email Incidence::organizer() const
+{
+ return mOrganizer;
+}
+
+void Incidence::setStartDate( const TQDateTime& startDate )
+{
+ mStartDate = startDate;
+ if ( mFloatingStatus == AllDay )
+ kdDebug() << "ERROR: Time on start date but no time on the event\n";
+ mFloatingStatus = HasTime;
+}
+
+void Incidence::setStartDate( const TQDate& startDate )
+{
+ mStartDate = startDate;
+ if ( mFloatingStatus == HasTime )
+ kdDebug() << "ERROR: No time on start date but time on the event\n";
+ mFloatingStatus = AllDay;
+}
+
+void Incidence::setStartDate( const TQString& startDate )
+{
+ if ( startDate.length() > 10 )
+ // This is a date + time
+ setStartDate( stringToDateTime( startDate ) );
+ else
+ // This is only a date
+ setStartDate( stringToDate( startDate ) );
+}
+
+TQDateTime Incidence::startDate() const
+{
+ return mStartDate;
+}
+
+void Incidence::setAlarm( float alarm )
+{
+ mAlarm = alarm;
+ mHasAlarm = true;
+}
+
+float Incidence::alarm() const
+{
+ return mAlarm;
+}
+
+Incidence::Recurrence Incidence::recurrence() const
+{
+ return mRecurrence;
+}
+
+void Incidence::addAttendee( const Attendee& attendee )
+{
+ mAttendees.append( attendee );
+}
+
+TQValueList<Incidence::Attendee>& Incidence::attendees()
+{
+ return mAttendees;
+}
+
+const TQValueList<Incidence::Attendee>& Incidence::attendees() const
+{
+ return mAttendees;
+}
+
+void Incidence::setInternalUID( const TQString& iuid )
+{
+ mInternalUID = iuid;
+}
+
+TQString Incidence::internalUID() const
+{
+ return mInternalUID;
+}
+
+bool Incidence::loadAttendeeAttribute( TQDomElement& element,
+ Attendee& attendee )
+{
+ 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 == "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" )
+ attendee.status = e.text();
+ else if ( tagName == "request-response" )
+ // This sets reqResp to false, if the text is "false". Otherwise it
+ // sets it to true. This means the default setting is true.
+ attendee.requestResponse = ( e.text().lower() != "false" );
+ else if ( tagName == "invitation-sent" )
+ // Like above, only this defaults to false
+ attendee.invitationSent = ( e.text().lower() != "true" );
+ else if ( tagName == "role" )
+ attendee.role = e.text();
+ else if ( tagName == "delegated-to" )
+ attendee.delegate = e.text();
+ else if ( tagName == "delegated-from" )
+ attendee.delegator = e.text();
+ else
+ // TODO: Unhandled tag - save for later storage
+ kdDebug() << "Warning: Unhandled tag " << e.tagName() << endl;
+ } else
+ kdDebug() << "Node is not a comment or an element???" << endl;
+ }
+
+ return true;
+}
+
+void Incidence::saveAttendeeAttribute( TQDomElement& element,
+ const Attendee& attendee ) const
+{
+ TQDomElement e = element.ownerDocument().createElement( "attendee" );
+ element.appendChild( e );
+ writeString( e, "display-name", attendee.displayName );
+ writeString( e, "smtp-address", attendee.smtpAddress );
+ writeString( e, "status", attendee.status );
+ writeString( e, "request-response",
+ ( attendee.requestResponse ? "true" : "false" ) );
+ writeString( e, "invitation-sent",
+ ( attendee.invitationSent ? "true" : "false" ) );
+ writeString( e, "role", attendee.role );
+ writeString( e, "delegated-to", attendee.delegate );
+ writeString( e, "delegated-from", attendee.delegator );
+}
+
+void Incidence::saveAttendees( TQDomElement& element ) const
+{
+ TQValueList<Attendee>::ConstIterator it = mAttendees.begin();
+ for ( ; it != mAttendees.end(); ++it )
+ saveAttendeeAttribute( element, *it );
+}
+
+void Incidence::saveAttachments( TQDomElement& element ) const
+{
+ KCal::Attachment::List::ConstIterator it = mAttachments.begin();
+ for ( ; it != mAttachments.end(); ++it ) {
+ KCal::Attachment *a = (*it);
+ if ( a->isUri() ) {
+ writeString( element, "link-attachment", a->uri() );
+ } else if ( a->isBinary() ) {
+ writeString( element, "inline-attachment", a->label() );
+ }
+ }
+}
+
+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" );
+ element.appendChild( e );
+ e.setAttribute( "cycle", mRecurrence.cycle );
+ if ( !mRecurrence.type.isEmpty() )
+ e.setAttribute( "type", mRecurrence.type );
+ writeString( e, "interval", TQString::number( mRecurrence.interval ) );
+ for( TQStringList::ConstIterator it = mRecurrence.days.begin(); it != mRecurrence.days.end(); ++it ) {
+ writeString( e, "day", *it );
+ }
+ if ( !mRecurrence.dayNumber.isEmpty() )
+ writeString( e, "daynumber", mRecurrence.dayNumber );
+ if ( !mRecurrence.month.isEmpty() )
+ writeString( e, "month", mRecurrence.month );
+ if ( !mRecurrence.rangeType.isEmpty() ) {
+ TQDomElement range = element.ownerDocument().createElement( "range" );
+ e.appendChild( range );
+ range.setAttribute( "type", mRecurrence.rangeType );
+ TQDomText t = element.ownerDocument().createTextNode( mRecurrence.range );
+ range.appendChild( t );
+ }
+ for( TQValueList<TQDate>::ConstIterator it = mRecurrence.exclusions.begin();
+ it != mRecurrence.exclusions.end(); ++it ) {
+ writeString( e, "exclusion", dateToString( *it ) );
+ }
+}
+
+void Incidence::loadRecurrence( const TQDomElement& element )
+{
+ mRecurrence.interval = 0;
+ mRecurrence.cycle = element.attribute( "cycle" );
+ mRecurrence.type = element.attribute( "type" );
+ 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 == "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" )
+ mRecurrence.dayNumber = e.text();
+ else if ( tagName == "month" )
+ mRecurrence.month = e.text();
+ else if ( tagName == "range" ) {
+ mRecurrence.rangeType = e.attribute( "type" );
+ mRecurrence.range = e.text();
+ } else if ( tagName == "exclusion" ) {
+ mRecurrence.exclusions.append( stringToDate( e.text() ) );
+ } else
+ // TODO: Unhandled tag - save for later storage
+ kdDebug() << "Warning: Unhandled tag " << e.tagName() << endl;
+ }
+ }
+}
+
+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();
+
+ if ( tagName == "summary" )
+ setSummary( element.text() );
+ else if ( tagName == "location" )
+ setLocation( element.text() );
+ else if ( tagName == "organizer" ) {
+ Email email;
+ if ( loadEmailAttribute( element, email ) ) {
+ setOrganizer( email );
+ return true;
+ } else
+ return false;
+ } else if ( tagName == "start-date" )
+ setStartDate( element.text() );
+ else if ( tagName == "recurrence" )
+ loadRecurrence( element );
+ else if ( tagName == "attendee" ) {
+ Attendee attendee;
+ if ( loadAttendeeAttribute( element, attendee ) ) {
+ addAttendee( attendee );
+ return true;
+ } else
+ return false;
+ } else if ( tagName == "link-attachment" ) {
+ mAttachments.push_back( new KCal::Attachment( element.text() ) );
+ } 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 == "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;
+ Custom c;
+ c.key = TQCString( "X-TDE-KolabUnhandled-" ) + element.tagName().latin1();
+ c.value = element.text();
+ mCustomList.append( c );
+ }
+ }
+ // We handled this
+ return true;
+}
+
+bool Incidence::saveAttributes( TQDomElement& element ) const
+{
+ // Save the base class elements
+ KolabBase::saveAttributes( element );
+
+ if ( mFloatingStatus == HasTime )
+ writeString( element, "start-date", dateTimeToString( startDate() ) );
+ else
+ writeString( element, "start-date", dateToString( startDate().date() ) );
+ writeString( element, "summary", summary() );
+ writeString( element, "location", location() );
+ saveEmailAttribute( element, organizer(), "organizer" );
+ if ( !mRecurrence.cycle.isEmpty() )
+ saveRecurrence( element );
+ saveAttendees( element );
+ saveAttachments( element );
+ if ( mHasAlarm ) {
+ // Alarms should be minutes before. Libkcal uses event time + alarm time
+ int alarmTime = tqRound( -alarm() );
+ writeString( element, "alarm", TQString::number( alarmTime ) );
+ }
+ saveAlarms( element );
+ writeString( element, "x-kde-internaluid", internalUID() );
+ saveCustomAttributes( element );
+ return true;
+}
+
+void Incidence::saveCustomAttributes( TQDomElement& element ) const
+{
+ TQValueList<Custom>::ConstIterator it = mCustomList.begin();
+ for ( ; it != mCustomList.end(); ++it ) {
+ TQString key = (*it).key;
+ Q_ASSERT( !key.isEmpty() );
+ if ( key.startsWith( "X-TDE-KolabUnhandled-" ) ) {
+ key = key.mid( strlen( "X-TDE-KolabUnhandled-" ) );
+ writeString( element, key, (*it).value );
+ } else {
+ // Let's use attributes so that other tag-preserving-code doesn't need sub-elements
+ TQDomElement e = element.ownerDocument().createElement( "x-custom" );
+ element.appendChild( e );
+ e.setAttribute( "key", key );
+ e.setAttribute( "value", (*it).value );
+ }
+ }
+}
+
+void Incidence::loadCustomAttributes( TQDomElement& element )
+{
+ Custom custom;
+ custom.key = element.attribute( "key" ).latin1();
+ custom.value = element.attribute( "value" );
+ mCustomList.append( custom );
+}
+
+static KCal::Attendee::PartStat attendeeStringToStatus( const TQString& s )
+{
+ if ( s == "none" )
+ 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::None;
+}
+
+static TQString attendeeStatusToString( KCal::Attendee::PartStat status )
+{
+ switch( status ) {
+ case KCal::Attendee::NeedsAction:
+ return "none";
+ case KCal::Attendee::Accepted:
+ return "accepted";
+ case KCal::Attendee::Declined:
+ return "declined";
+ case KCal::Attendee::Tentative:
+ return "tentative";
+ case KCal::Attendee::Delegated:
+ return "delegated";
+ case KCal::Attendee::Completed:
+ case KCal::Attendee::InProcess:
+ // These don't have any meaning in the Kolab format, so just use:
+ return "accepted";
+ }
+
+ // Default for the case that there are more added later:
+ return "accepted";
+}
+
+static KCal::Attendee::Role attendeeStringToRole( const TQString& s )
+{
+ if ( s == "optional" )
+ return KCal::Attendee::OptParticipant;
+ if ( s == "resource" )
+ return KCal::Attendee::NonParticipant;
+ return KCal::Attendee::ReqParticipant;
+}
+
+static TQString attendeeRoleToString( KCal::Attendee::Role role )
+{
+ switch( role ) {
+ case KCal::Attendee::ReqParticipant:
+ return "required";
+ case KCal::Attendee::OptParticipant:
+ return "optional";
+ case KCal::Attendee::Chair:
+ // We don't have the notion of chair, so use
+ return "required";
+ case KCal::Attendee::NonParticipant:
+ // In Kolab, a non-participant is a resource
+ return "resource";
+ }
+
+ // Default for the case that there are more added later:
+ return "required";
+}
+
+static const char *s_weekDayName[] =
+{
+ "monday", "tuesday", "wednesday", "thursday", "friday", "saturday", "sunday"
+};
+
+static const char *s_monthName[] =
+{
+ "january", "february", "march", "april", "may", "june", "july",
+ "august", "september", "october", "november", "december"
+};
+
+void Incidence::setRecurrence( KCal::Recurrence* recur )
+{
+ mRecurrence.interval = recur->frequency();
+ switch ( recur->recurrenceType() ) {
+ case KCal::Recurrence::rMinutely: // Not handled by the kolab XML
+ mRecurrence.cycle = "minutely";
+ break;
+ case KCal::Recurrence::rHourly: // Not handled by the kolab XML
+ mRecurrence.cycle = "hourly";
+ break;
+ case KCal::Recurrence::rDaily:
+ mRecurrence.cycle = "daily";
+ break;
+ case KCal::Recurrence::rWeekly: // every X weeks
+ mRecurrence.cycle = "weekly";
+ {
+ TQBitArray arr = recur->days();
+ for ( uint idx = 0 ; idx < 7 ; ++idx )
+ if ( arr.testBit( idx ) )
+ mRecurrence.days.append( s_weekDayName[idx] );
+ }
+ break;
+ case KCal::Recurrence::rMonthlyPos: {
+ mRecurrence.cycle = "monthly";
+ mRecurrence.type = "weekday";
+ TQValueList<KCal::RecurrenceRule::WDayPos> monthPositions = recur->monthPositions();
+ if ( !monthPositions.isEmpty() ) {
+ KCal::RecurrenceRule::WDayPos monthPos = monthPositions.first();
+ // TODO: Handle multiple days in the same week
+ mRecurrence.dayNumber = TQString::number( monthPos.pos() );
+ mRecurrence.days.append( s_weekDayName[ monthPos.day()-1 ] );
+ // Not (properly) handled(?): monthPos.negative (nth days before end of month)
+ }
+ break;
+ }
+ case KCal::Recurrence::rMonthlyDay: {
+ mRecurrence.cycle = "monthly";
+ mRecurrence.type = "daynumber";
+ TQValueList<int> monthDays = recur->monthDays();
+ // ####### Kolab XML limitation: only the first month day is used
+ if ( !monthDays.isEmpty() )
+ mRecurrence.dayNumber = TQString::number( monthDays.first() );
+ break;
+ }
+ case KCal::Recurrence::rYearlyMonth: // (day n of Month Y)
+ {
+ mRecurrence.cycle = "yearly";
+ mRecurrence.type = "monthday";
+ TQValueList<int> rmd = recur->yearDates();
+ int day = !rmd.isEmpty() ? rmd.first() : recur->startDate().day();
+ mRecurrence.dayNumber = TQString::number( day );
+ TQValueList<int> months = recur->yearMonths();
+ if ( !months.isEmpty() )
+ mRecurrence.month = s_monthName[ months.first() - 1 ]; // #### Kolab XML limitation: only one month specified
+ break;
+ }
+ case KCal::Recurrence::rYearlyDay: // YearlyDay (day N of the year). Not supported by Outlook
+ mRecurrence.cycle = "yearly";
+ mRecurrence.type = "yearday";
+ mRecurrence.dayNumber = TQString::number( recur->yearDays().first() );
+ break;
+ case KCal::Recurrence::rYearlyPos: // (weekday X of week N of month Y)
+ mRecurrence.cycle = "yearly";
+ mRecurrence.type = "weekday";
+ TQValueList<int> months = recur->yearMonths();
+ if ( !months.isEmpty() )
+ mRecurrence.month = s_monthName[ months.first() - 1 ]; // #### Kolab XML limitation: only one month specified
+ TQValueList<KCal::RecurrenceRule::WDayPos> monthPositions = recur->yearPositions();
+ if ( !monthPositions.isEmpty() ) {
+ KCal::RecurrenceRule::WDayPos monthPos = monthPositions.first();
+ // TODO: Handle multiple days in the same week
+ mRecurrence.dayNumber = TQString::number( monthPos.pos() );
+ mRecurrence.days.append( s_weekDayName[ monthPos.day()-1 ] );
+
+ //mRecurrence.dayNumber = TQString::number( *recur->yearNums().getFirst() );
+ // Not handled: monthPos.negative (nth days before end of month)
+ }
+ break;
+ }
+ int howMany = recur->duration();
+ if ( howMany > 0 ) {
+ mRecurrence.rangeType = "number";
+ mRecurrence.range = TQString::number( howMany );
+ } else if ( howMany == 0 ) {
+ mRecurrence.rangeType = "date";
+ mRecurrence.range = dateToString( recur->endDate() );
+ } else {
+ mRecurrence.rangeType = "none";
+ }
+}
+
+void Incidence::setFields( const KCal::Incidence* incidence )
+{
+ KolabBase::setFields( incidence );
+
+ if ( incidence->doesFloat() ) {
+ // This is a floating event. Don't timezone move this one
+ mFloatingStatus = AllDay;
+ setStartDate( incidence->dtStart().date() );
+ } else {
+ mFloatingStatus = HasTime;
+ setStartDate( localToUTC( incidence->dtStart() ) );
+ }
+
+ setSummary( incidence->summary() );
+ setLocation( incidence->location() );
+
+ // Alarm
+ mHasAlarm = false; // Will be set to true, if we actually have one
+ if ( incidence->isAlarmEnabled() ) {
+ const KCal::Alarm::List& alarms = incidence->alarms();
+ if ( !alarms.isEmpty() ) {
+ const KCal::Alarm* alarm = alarms.first();
+ if ( alarm->hasStartOffset() ) {
+ int dur = alarm->startOffset().asSeconds();
+ setAlarm( (float)dur / 60.0 );
+ }
+ }
+ }
+
+ Email org( incidence->organizer().name(), incidence->organizer().email() );
+ setOrganizer( org );
+
+ // Attendees:
+ KCal::Attendee::List attendees = incidence->attendees();
+ KCal::Attendee::List::ConstIterator it;
+ for ( it = attendees.begin(); it != attendees.end(); ++it ) {
+ KCal::Attendee* kcalAttendee = *it;
+ Attendee attendee;
+
+ attendee.displayName = kcalAttendee->name();
+ attendee.smtpAddress = kcalAttendee->email();
+ attendee.status = attendeeStatusToString( kcalAttendee->status() );
+ attendee.requestResponse = kcalAttendee->RSVP();
+ // TODO: KCal::Attendee::mFlag is not accessible
+ // attendee.invitationSent = kcalAttendee->mFlag;
+ // DF: Hmm? mFlag is set to true and never used at all.... Did you mean another field?
+ attendee.role = attendeeRoleToString( kcalAttendee->role() );
+ attendee.delegate = kcalAttendee->delegate();
+ attendee.delegator = kcalAttendee->delegator();
+
+ addAttendee( attendee );
+ }
+
+ mAttachments.clear();
+
+ // Attachments
+ KCal::Attachment::List attachments = incidence->attachments();
+ KCal::Attachment::List::ConstIterator it2;
+ for ( it2 = attachments.begin(); it2 != attachments.end(); ++it2 ) {
+ KCal::Attachment *a = *it2;
+ 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();
+ }
+
+ // Handle the scheduling ID
+ if ( incidence->schedulingID() == incidence->uid() ) {
+ // There is no scheduling ID
+ setInternalUID( TQString() );
+ } else {
+ // We've internally been using a different uid, so save that as the
+ // temporary (internal) uid and restore the original uid, the one that
+ // is used in the folder and the outside world
+ setUid( incidence->schedulingID() );
+ setInternalUID( incidence->uid() );
+ }
+
+ if ( incidence->pilotId() != 0 )
+ setPilotSyncId( incidence->pilotId() );
+
+ setPilotSyncStatus( incidence->syncStatus() );
+
+ // Unhandled tags and other custom properties (see libkcal/customproperties.h)
+ const TQMap<TQCString, TQString> map = incidence->customProperties();
+ TQMap<TQCString, TQString>::ConstIterator cit = map.begin();
+ for ( ; cit != map.end() ; ++cit ) {
+ Custom c;
+ c.key = cit.key();
+ c.value = cit.data();
+ mCustomList.append( c );
+ }
+}
+
+static TQBitArray daysListToBitArray( const TQStringList& days )
+{
+ TQBitArray arr( 7 );
+ arr.fill( false );
+ for( TQStringList::ConstIterator it = days.begin(); it != days.end(); ++it ) {
+ for ( uint i = 0; i < 7 ; ++i )
+ if ( *it == s_weekDayName[i] )
+ arr.setBit( i, true );
+ }
+ return arr;
+}
+
+
+void Incidence::saveTo( KCal::Incidence* incidence )
+{
+ KolabBase::saveTo( incidence );
+
+ if ( mFloatingStatus == AllDay ) {
+ // This is a floating event. Don't timezone move this one
+ incidence->setDtStart( startDate() );
+ incidence->setFloats( true );
+ } else {
+ incidence->setDtStart( utcToLocal( startDate() ) );
+ incidence->setFloats( false );
+ }
+
+ incidence->setSummary( summary() );
+ incidence->setLocation( location() );
+
+ if ( mHasAlarm && mAlarms.isEmpty() ) {
+ KCal::Alarm* alarm = incidence->newAlarm();
+ alarm->setStartOffset( tqRound( 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() )
+ incidence->setOrganizer( organizer().smtpAddress );
+ else
+ incidence->setOrganizer( organizer().displayName + "<"
+ + organizer().smtpAddress + ">" );
+
+ incidence->clearAttendees();
+ TQValueList<Attendee>::ConstIterator it;
+ for ( it = mAttendees.begin(); it != mAttendees.end(); ++it ) {
+ KCal::Attendee::PartStat status = attendeeStringToStatus( (*it).status );
+ KCal::Attendee::Role role = attendeeStringToRole( (*it).role );
+ KCal::Attendee* attendee = new KCal::Attendee( (*it).displayName,
+ (*it).smtpAddress,
+ (*it).requestResponse,
+ status, role );
+ attendee->setDelegate( (*it).delegate );
+ attendee->setDelegator( (*it).delegator );
+ incidence->addAttendee( attendee );
+ }
+
+ incidence->clearAttachments();
+ KCal::Attachment::List::ConstIterator it2;
+ for ( it2 = mAttachments.begin(); it2 != mAttachments.end(); ++it2 ) {
+ KCal::Attachment *a = (*it2);
+ // TODO should we copy?
+ incidence->addAttachment( a );
+ }
+
+ if ( !mRecurrence.cycle.isEmpty() ) {
+ KCal::Recurrence* recur = incidence->recurrence(); // yeah, this creates it
+ // done below recur->setFrequency( mRecurrence.interval );
+ if ( mRecurrence.cycle == "minutely" ) {
+ recur->setMinutely( mRecurrence.interval );
+ } else if ( mRecurrence.cycle == "hourly" ) {
+ recur->setHourly( mRecurrence.interval );
+ } else if ( mRecurrence.cycle == "daily" ) {
+ recur->setDaily( mRecurrence.interval );
+ } else if ( mRecurrence.cycle == "weekly" ) {
+ TQBitArray rDays = daysListToBitArray( mRecurrence.days );
+ recur->setWeekly( mRecurrence.interval, rDays );
+ } else if ( mRecurrence.cycle == "monthly" ) {
+ recur->setMonthly( mRecurrence.interval );
+ if ( mRecurrence.type == "weekday" ) {
+ recur->addMonthlyPos( mRecurrence.dayNumber.toInt(), daysListToBitArray( mRecurrence.days ) );
+ } else if ( mRecurrence.type == "daynumber" ) {
+ recur->addMonthlyDate( mRecurrence.dayNumber.toInt() );
+ } else kdWarning() << "Unhandled monthly recurrence type " << mRecurrence.type << endl;
+ } else if ( mRecurrence.cycle == "yearly" ) {
+ recur->setYearly( mRecurrence.interval );
+ if ( mRecurrence.type == "monthday" ) {
+ recur->addYearlyDate( mRecurrence.dayNumber.toInt() );
+ for ( int i = 0; i < 12; ++i )
+ if ( s_monthName[ i ] == mRecurrence.month )
+ recur->addYearlyMonth( i+1 );
+ } else if ( mRecurrence.type == "yearday" ) {
+ recur->addYearlyDay( mRecurrence.dayNumber.toInt() );
+ } else if ( mRecurrence.type == "weekday" ) {
+ for ( int i = 0; i < 12; ++i )
+ if ( s_monthName[ i ] == mRecurrence.month )
+ recur->addYearlyMonth( i+1 );
+ recur->addYearlyPos( mRecurrence.dayNumber.toInt(), daysListToBitArray( mRecurrence.days ) );
+ } else kdWarning() << "Unhandled yearly recurrence type " << mRecurrence.type << endl;
+ } else kdWarning() << "Unhandled recurrence cycle " << mRecurrence.cycle << endl;
+
+ if ( mRecurrence.rangeType == "number" ) {
+ recur->setDuration( mRecurrence.range.toInt() );
+ } else if ( mRecurrence.rangeType == "date" ) {
+ recur->setEndDate( stringToDate( mRecurrence.range ) );
+ } // "none" is default since tje set*ly methods set infinite recurrence
+
+ incidence->recurrence()->setExDates( mRecurrence.exclusions );
+
+ }
+ /* If we've stored a uid to be used internally instead of the real one
+ * (to deal with duplicates of events in different folders) before, then
+ * restore it, so it does not change. Keep the original uid around for
+ * scheduling purposes. */
+ if ( !internalUID().isEmpty() ) {
+ incidence->setUid( internalUID() );
+ incidence->setSchedulingID( uid() );
+ }
+ if ( hasPilotSyncId() )
+ incidence->setPilotId( pilotSyncId() );
+ if ( hasPilotSyncStatus() )
+ incidence->setSyncStatus( pilotSyncStatus() );
+
+ for( TQValueList<Custom>::ConstIterator it = mCustomList.constBegin(); it != mCustomList.constEnd(); ++it ) {
+ incidence->setNonKDECustomProperty( (*it).key, (*it).value );
+ }
+
+}
+
+void Incidence::loadAttachments()
+{
+ TQStringList attachments;
+ if ( mResource->kmailListAttachments( attachments, mSubResource, mSernum ) ) {
+ for ( TQStringList::ConstIterator it = attachments.constBegin(); it != attachments.constEnd(); ++it ) {
+ TQByteArray data;
+ KURL url;
+ if ( mResource->kmailGetAttachment( url, mSubResource, mSernum, *it ) && !url.isEmpty() ) {
+ TQFile f( url.path() );
+ if ( f.open( IO_ReadOnly ) ) {
+ data = f.readAll();
+ TQString mimeType;
+ if ( !mResource->kmailAttachmentMimetype( mimeType, mSubResource, mSernum, *it ) )
+ mimeType = "application/octet-stream";
+ KCal::Attachment *a = new KCal::Attachment( KCodecs::base64Encode( data ).data(), mimeType );
+ a->setLabel( *it );
+ mAttachments.append( a );
+ f.close();
+ }
+ f.remove();
+ }
+ }
+ }
+}
+
+TQString Incidence::productID() const
+{
+ return TQString( "KOrganizer %1, Kolab resource" ).arg( korgVersion );
+}
+
+// Unhandled KCal::Incidence fields:
+// revision, status (unused), priority (done in tasks), attendee.uid,
+// mComments, mReadOnly
+
diff --git a/tderesources/kolab/kcal/incidence.h b/tderesources/kolab/kcal/incidence.h
new file mode 100644
index 000000000..926ec1e4f
--- /dev/null
+++ b/tderesources/kolab/kcal/incidence.h
@@ -0,0 +1,175 @@
+/*
+ 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>
+
+ 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.
+*/
+
+#ifndef KOLAB_INCIDENCE_H
+#define KOLAB_INCIDENCE_H
+
+#include <kolabbase.h>
+
+class TQDomElement;
+
+namespace KCal {
+ class Incidence;
+ class Recurrence;
+ class Alarm;
+ class Attachment;
+ class ResourceKolab;
+}
+
+namespace Kolab {
+
+/**
+ * This abstract class represents an incidence which has the shared
+ * fields, of events and tasks and knows how to load/save these
+ * from/to XML, and from/to a KCal::Incidence.
+ */
+class Incidence : public KolabBase {
+public:
+ struct Recurrence {
+ TQString cycle;
+ TQString type;
+ int interval;
+ TQStringList days; // list of days-of-the-week
+ TQString dayNumber;
+ TQString month;
+ TQString rangeType;
+ TQString range; // date or number or nothing
+ TQValueList<TQDate> exclusions;
+ };
+
+ struct Attendee : Email {
+ Attendee() : requestResponse( true ), invitationSent( false ) {}
+ TQString status;
+ bool requestResponse;
+ bool invitationSent;
+ TQString role;
+ TQString delegate;
+ TQString delegator;
+ };
+
+ explicit Incidence( KCal::ResourceKolab *res, const TQString &subResource, TQ_UINT32 sernum,
+ const TQString& tz );
+ virtual ~Incidence();
+
+ void saveTo( KCal::Incidence* incidence );
+
+ virtual void setSummary( const TQString& summary );
+ virtual TQString summary() const;
+
+ virtual void setLocation( const TQString& location );
+ virtual TQString location() const;
+
+ virtual void setOrganizer( const Email& organizer );
+ virtual Email organizer() const;
+
+ virtual void setStartDate( const TQDateTime& startDate );
+ virtual void setStartDate( const TQDate& startDate );
+ virtual void setStartDate( const TQString& startDate );
+ virtual TQDateTime startDate() const;
+
+ virtual void setAlarm( float alarm );
+ virtual float alarm() const;
+
+ virtual void setRecurrence( KCal::Recurrence* recur );
+ virtual Recurrence recurrence() const;
+
+ virtual void addAttendee( const Attendee& attendee );
+ TQValueList<Attendee>& attendees();
+ const TQValueList<Attendee>& attendees() const;
+
+ /**
+ * The internal uid is used as the uid inside KOrganizer whenever
+ * two or more events with the same uid appear, which KOrganizer
+ * can't handle. To avoid keep that interal uid from changing all the
+ * time, it is persisted in the XML between a save and the next load.
+ */
+ void setInternalUID( const TQString& iuid );
+ TQString internalUID() const;
+
+ // Load the attributes of this class
+ virtual bool loadAttribute( TQDomElement& );
+
+ // Save the attributes of this class
+ virtual bool saveAttributes( TQDomElement& ) const;
+
+protected:
+ enum FloatingStatus { Unset, AllDay, HasTime };
+
+ // Read all known fields from this ical incidence
+ void setFields( const KCal::Incidence* );
+
+ bool loadAttendeeAttribute( TQDomElement&, Attendee& );
+ void saveAttendeeAttribute( TQDomElement& element,
+ const Attendee& attendee ) const;
+ 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;
+ void loadCustomAttributes( TQDomElement& element );
+
+ void loadAttachments();
+
+ TQString productID() const;
+
+ TQString mSummary;
+ TQString mLocation;
+ Email mOrganizer;
+ TQDateTime mStartDate;
+ FloatingStatus mFloatingStatus;
+ float mAlarm;
+ bool mHasAlarm;
+ Recurrence mRecurrence;
+ TQValueList<Attendee> mAttendees;
+ TQValueList<KCal::Alarm*> mAlarms;
+ TQValueList<KCal::Attachment*> mAttachments;
+ TQString mInternalUID;
+
+ struct Custom {
+ TQCString key;
+ TQString value;
+ };
+ TQValueList<Custom> mCustomList;
+
+ KCal::ResourceKolab *mResource;
+ TQString mSubResource;
+ TQ_UINT32 mSernum;
+};
+
+}
+
+#endif // KOLAB_INCIDENCE_H
diff --git a/tderesources/kolab/kcal/journal.cpp b/tderesources/kolab/kcal/journal.cpp
new file mode 100644
index 000000000..acfc4e71d
--- /dev/null
+++ b/tderesources/kolab/kcal/journal.cpp
@@ -0,0 +1,184 @@
+/*
+ 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>
+
+ 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 "journal.h"
+
+#include <libkcal/journal.h>
+#include <korganizer/version.h>
+#include <kdebug.h>
+
+using namespace Kolab;
+
+
+KCal::Journal* Journal::xmlToJournal( const TQString& xml, const TQString& tz )
+{
+ Journal journal( tz );
+ journal.load( xml );
+ KCal::Journal* kcalJournal = new KCal::Journal();
+ journal.saveTo( kcalJournal );
+ return kcalJournal;
+}
+
+TQString Journal::journalToXML( KCal::Journal* kcalJournal, const TQString& tz )
+{
+ Journal journal( tz, kcalJournal );
+ return journal.saveXML();
+}
+
+Journal::Journal( const TQString& tz, KCal::Journal* journal )
+ : KolabBase( tz )
+{
+ if ( journal )
+ setFields( journal );
+}
+
+Journal::~Journal()
+{
+}
+
+void Journal::setSummary( const TQString& summary )
+{
+ mSummary = summary;
+}
+
+TQString Journal::summary() const
+{
+ return mSummary;
+}
+
+void Journal::setStartDate( const TQDateTime& startDate )
+{
+ mStartDate = startDate;
+}
+
+TQDateTime Journal::startDate() const
+{
+ return mStartDate;
+}
+
+void Journal::setEndDate( const TQDateTime& endDate )
+{
+ mEndDate = endDate;
+}
+
+TQDateTime Journal::endDate() const
+{
+ return mEndDate;
+}
+
+bool Journal::loadAttribute( TQDomElement& element )
+{
+ TQString tagName = element.tagName();
+
+ if ( tagName == "summary" )
+ setSummary( element.text() );
+ else if ( tagName == "start-date" )
+ setStartDate( stringToDateTime( element.text() ) );
+ else
+ // Not handled here
+ return KolabBase::loadAttribute( element );
+
+ // We handled this
+ return true;
+}
+
+bool Journal::saveAttributes( TQDomElement& element ) const
+{
+ // Save the base class elements
+ KolabBase::saveAttributes( element );
+
+ writeString( element, "summary", summary() );
+ writeString( element, "start-date", dateTimeToString( startDate() ) );
+
+ return true;
+}
+
+
+bool Journal::loadXML( const TQDomDocument& document )
+{
+ TQDomElement top = document.documentElement();
+
+ if ( top.tagName() != "journal" ) {
+ tqWarning( "XML error: Top tag was %s instead of the expected Journal",
+ top.tagName().ascii() );
+ return false;
+ }
+
+ for ( TQDomNode n = top.firstChild(); !n.isNull(); n = n.nextSibling() ) {
+ if ( n.isComment() )
+ continue;
+ if ( n.isElement() ) {
+ TQDomElement e = n.toElement();
+ if ( !loadAttribute( e ) ) {
+ // Unhandled tag - save for later storage
+ //tqDebug( "Unhandled tag: %s", e.toCString().data() );
+ }
+ } else
+ tqDebug( "Node is not a comment or an element???" );
+ }
+
+ return true;
+}
+
+TQString Journal::saveXML() const
+{
+ TQDomDocument document = domTree();
+ TQDomElement element = document.createElement( "journal" );
+ element.setAttribute( "version", "1.0" );
+ saveAttributes( element );
+ document.appendChild( element );
+ return document.toString();
+}
+
+void Journal::saveTo( KCal::Journal* journal )
+{
+ KolabBase::saveTo( journal );
+
+ journal->setSummary( summary() );
+ journal->setDtStart( utcToLocal( startDate() ) );
+}
+
+void Journal::setFields( const KCal::Journal* journal )
+{
+ // Set baseclass fields
+ KolabBase::setFields( journal );
+
+ // Set our own fields
+ setSummary( journal->summary() );
+ setStartDate( localToUTC( journal->dtStart() ) );
+}
+
+TQString Journal::productID() const
+{
+ return TQString( "KOrganizer " ) + korgVersion + ", Kolab resource";
+}
diff --git a/tderesources/kolab/kcal/journal.h b/tderesources/kolab/kcal/journal.h
new file mode 100644
index 000000000..cd583c6ca
--- /dev/null
+++ b/tderesources/kolab/kcal/journal.h
@@ -0,0 +1,103 @@
+/*
+ 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>
+
+ 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.
+*/
+
+#ifndef KOLAB_JOURNAL_H
+#define KOLAB_JOURNAL_H
+
+#include <kolabbase.h>
+
+class TQDomElement;
+
+namespace KCal {
+ class Journal;
+}
+
+namespace Kolab {
+
+/**
+ * This class represents a journal entry, and knows how to load/save it
+ * from/to XML, and from/to a KCal::Journal.
+ * The instances of this class are temporary, only used to convert
+ * one to the other.
+ */
+class Journal : public KolabBase {
+public:
+ /// Use this to parse an xml string to a journal entry
+ /// The caller is responsible for deleting the returned journal
+ static KCal::Journal* xmlToJournal( const TQString& xml, const TQString& tz );
+
+ /// Use this to get an xml string describing this journal entry
+ static TQString journalToXML( KCal::Journal*, const TQString& tz );
+
+ explicit Journal( const TQString& tz, KCal::Journal* journal = 0 );
+ virtual ~Journal();
+
+ virtual TQString type() const { return "Journal"; }
+
+ void saveTo( KCal::Journal* journal );
+
+ virtual void setSummary( const TQString& summary );
+ virtual TQString summary() const;
+
+ virtual void setStartDate( const TQDateTime& startDate );
+ virtual TQDateTime startDate() const;
+
+ virtual void setEndDate( const TQDateTime& endDate );
+ virtual TQDateTime endDate() const;
+
+ // Load the attributes of this class
+ virtual bool loadAttribute( TQDomElement& );
+
+ // Save the attributes of this class
+ virtual bool saveAttributes( TQDomElement& ) const;
+
+ // Load this journal by reading the XML file
+ virtual bool loadXML( const TQDomDocument& xml );
+
+ // Serialize this journal to an XML string
+ virtual TQString saveXML() const;
+
+protected:
+ // Read all known fields from this ical journal
+ void setFields( const KCal::Journal* );
+
+ TQString productID() const;
+
+ TQString mSummary;
+ TQDateTime mStartDate;
+ TQDateTime mEndDate;
+};
+
+}
+
+#endif // KOLAB_JOURNAL_H
diff --git a/tderesources/kolab/kcal/kolab.desktop b/tderesources/kolab/kcal/kolab.desktop
new file mode 100644
index 000000000..40bcacb00
--- /dev/null
+++ b/tderesources/kolab/kcal/kolab.desktop
@@ -0,0 +1,52 @@
+[Desktop Entry]
+Name=Calendar on IMAP Server via KMail
+Name[af]=Kalender op IMAP bediener via KMail
+Name[bg]=Календар на Ñървър IMAP през KMail
+Name[br]=Deiziadur en ur servijer IMAP gant KMail
+Name[ca]=Calendari sobre servidor IMAP mitjançant KMail
+Name[cs]=Kalendář na IMAP serveru přes KMail
+Name[da]=Kalender på IMAP-server via KMail
+Name[de]=Kalender auf einem IMAP-Server via KMail
+Name[el]=ΗμεÏολόγιο σε εξυπηÏετητή IMAP μέσω του KMail
+Name[es]=Calendario en servidor IMAP por medio de KMail
+Name[et]=Kalender IMAP-serveris (KMaili vahendusel)
+Name[eu]=Egutegia IMAP zerbitzarian KMail-en bidez
+Name[fa]=تقویم روی کارساز IMAP از طریق KMail
+Name[fi]=Kalenteri IMAP-palvelimella KMailin avulla
+Name[fr]=Agenda sur serveur IMAP (via KMail)
+Name[fy]=Aginda op IMAP-tsjinner fia KMail
+Name[ga]=Féilire ar Fhreastalaí IMAP via KMail
+Name[gl]=Calendario no servidor IMAP mediante KMail
+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[kk]=KMail арқылы IMAP Ñерверіндегі күнтізбе
+Name[km]=ប្រážáž·áž‘ិន​លើ​ម៉ាស៊ីន​បម្រើ IMAP ážáž¶áž˜â€‹ážšáž™áŸˆ KMail
+Name[lt]=Kalendorius IMAP serveryje per KMail
+Name[mk]=Календар на IMAP-Ñервер преку КПошта
+Name[ms]=Kalendar pada pelayan IMAP melalui KMail
+Name[nb]=Kalender på IMAP-tjener via KMail
+Name[nds]=Kalenner op IMAP-Server över KMail
+Name[ne]=केडीई मेल मारà¥à¤«à¤¤ IMAP सरà¥à¤­à¤°à¤®à¤¾ कà¥à¤¯à¤¾à¤²à¥‡à¤¨à¥à¤¡à¤°
+Name[nl]=Agenda op IMAP-server via KMail
+Name[nn]=Kalender på IMAP-tenar via KMail
+Name[pl]=Kalendarz na serwerze IMAP za pośrednictwem KMail
+Name[pt]=Calendário em Servidor IMAP via KMail
+Name[pt_BR]=Calendário em Servidor IMAP via KMail
+Name[ru]=Календарь на Ñервере IMAP через KMail
+Name[sk]=Kalendár na IMAP serveri pomocou KMail
+Name[sl]=Koledar na strežniku IMAP preko KMaila
+Name[sr]=Календар на IMAP Ñерверу преко KMail-а
+Name[sr@Latn]=Kalendar na IMAP serveru preko KMail-a
+Name[sv]=Kalender på IMAP-server via Kmail
+Name[ta]=IMAP சேவையக வழியாக கேஅஞà¯à®šà®²à®¿à®²à¯ நாடà¯à®•à®¾à®Ÿà¯à®Ÿà®¿
+Name[tr]=KMail Aracılığı ile IMAP Sunucusunda Takvim
+Name[uk]=Календар на Ñервері IMAP через KMail
+Name[zh_CN]=通过 KMail 访问 IMAP æœåŠ¡å™¨ä¸Šçš„日历
+Name[zh_TW]=é€éŽ KMail å–å¾— IMAP 伺æœå™¨ä¸Šçš„行事曆
+X-TDE-Library=kcal_kolab
+Type=Service
+ServiceTypes=KResources/Plugin
+X-TDE-ResourceFamily=calendar
+X-TDE-ResourceType=imap
diff --git a/tderesources/kolab/kcal/resourcekolab.cpp b/tderesources/kolab/kcal/resourcekolab.cpp
new file mode 100644
index 000000000..44c13fd2b
--- /dev/null
+++ b/tderesources/kolab/kcal/resourcekolab.cpp
@@ -0,0 +1,1342 @@
+/*
+ This file is part of the kolab resource - the implementation of the
+ Kolab storage format. See www.kolab.org for documentation on this.
+
+ Copyright (c) 2004 Bo Thorsen <bo@sonofthor.dk>
+ 2004 Till Adam <till@klaralvdalens-datakonsult.se>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+
+ In addition, as a special exception, the copyright holders give
+ permission to link the code of this program with any edition of
+ the TQt library by Trolltech AS, Norway (or with modified versions
+ of TQt that use the same license as TQt), and distribute linked
+ combinations including the two. You must obey the GNU General
+ Public License in all respects for all of the code used other than
+ TQt. If you modify this file, you may extend this exception to
+ your version of the file, but you are not obligated to do so. If
+ you do not wish to do so, delete this exception statement from
+ your version.
+*/
+
+#include "resourcekolab.h"
+#include "event.h"
+#include "task.h"
+#include "journal.h"
+
+#include <kio/observer.h>
+#include <kio/uiserver_stub.h>
+#include <kapplication.h>
+#include <dcopclient.h>
+#include <libtdepim/kincidencechooser.h>
+#include <kabc/locknull.h>
+#include <kmainwindow.h>
+#include <klocale.h>
+#include <kinputdialog.h>
+#include <ktempfile.h>
+#include <kmdcodec.h>
+
+#include <tqfile.h>
+#include <tqobject.h>
+#include <tqtimer.h>
+#include <tqapplication.h>
+
+#include <assert.h>
+
+using namespace KCal;
+using namespace Kolab;
+
+static const char* kmailCalendarContentsType = "Calendar";
+static const char* kmailTodoContentsType = "Task";
+static const char* kmailJournalContentsType = "Journal";
+static const char* eventAttachmentMimeType = "application/x-vnd.kolab.event";
+static const char* todoAttachmentMimeType = "application/x-vnd.kolab.task";
+static const char* journalAttachmentMimeType = "application/x-vnd.kolab.journal";
+static const char* incidenceInlineMimeType = "text/calendar";
+
+
+ResourceKolab::ResourceKolab( const TDEConfig *config )
+ : ResourceCalendar( config ), ResourceKolabBase( "ResourceKolab-libkcal" ),
+ mCalendar( TQString::fromLatin1("UTC") ), mOpen( false ),mResourceChangedTimer( 0,
+ "mResourceChangedTimer" ), mBatchAddingInProgress( false )
+{
+ if ( !config ) {
+ setResourceName( i18n( "Kolab Server" ) );
+ }
+ setType( "imap" );
+ connect( &mResourceChangedTimer, TQT_SIGNAL( timeout() ),
+ this, TQT_SLOT( slotEmitResourceChanged() ) );
+}
+
+ResourceKolab::~ResourceKolab()
+{
+ // The resource is deleted on exit (StdAddressBook's KStaticDeleter),
+ // and it wasn't closed before that, so close here to save the config.
+ if ( mOpen ) {
+ close();
+ }
+}
+
+void ResourceKolab::loadSubResourceConfig( TDEConfig& config,
+ const TQString& name,
+ const TQString& label,
+ bool writable,
+ bool alarmRelevant,
+ ResourceMap& subResource )
+{
+ TDEConfigGroup group( &config, name );
+ bool active = group.readBoolEntry( "Active", true );
+ subResource.insert( name, Kolab::SubResource( active, writable,
+ alarmRelevant, label ) );
+}
+
+bool ResourceKolab::openResource( TDEConfig& config, const char* contentType,
+ ResourceMap& map )
+{
+ // Read the subresource entries from KMail
+ TQValueList<KMailICalIface::SubResource> subResources;
+ if ( !kmailSubresources( subResources, contentType ) )
+ return false;
+ map.clear();
+ TQValueList<KMailICalIface::SubResource>::ConstIterator it;
+ for ( it = subResources.begin(); it != subResources.end(); ++it )
+ loadSubResourceConfig( config, (*it).location, (*it).label, (*it).writable,
+ (*it).alarmRelevant, map );
+ return true;
+}
+
+bool ResourceKolab::doOpen()
+{
+ if ( mOpen )
+ // Already open
+ return true;
+ mOpen = true;
+
+ TDEConfig config( configFile() );
+ config.setGroup( "General" );
+ mProgressDialogIncidenceLimit = config.readNumEntry("ProgressDialogIncidenceLimit", 200);
+
+ return openResource( config, kmailCalendarContentsType, mEventSubResources )
+ && openResource( config, kmailTodoContentsType, mTodoSubResources )
+ && openResource( config, kmailJournalContentsType, mJournalSubResources );
+}
+
+static void writeResourceConfig( TDEConfig& config, ResourceMap& map )
+{
+ ResourceMap::ConstIterator it;
+ for ( it = map.begin(); it != map.end(); ++it ) {
+ config.setGroup( it.key() );
+ config.writeEntry( "Active", it.data().active() );
+ }
+}
+
+void ResourceKolab::doClose()
+{
+ if ( !mOpen )
+ // Not open
+ return;
+ mOpen = false;
+
+ writeConfig();
+}
+
+bool ResourceKolab::loadSubResource( const TQString& subResource,
+ const char* mimetype )
+{
+ int count = 0;
+ if ( !kmailIncidencesCount( count, mimetype, subResource ) ) {
+ kdError(5650) << "Communication problem in ResourceKolab::load()\n";
+ return false;
+ }
+
+ if ( !count )
+ return true;
+
+ const int nbMessages = 200; // read 200 mails at a time (see kabc resource)
+
+ const TQString labelTxt = !strcmp(mimetype, "application/x-vnd.kolab.task") ? i18n( "Loading tasks..." )
+ : !strcmp(mimetype, "application/x-vnd.kolab.journal") ? i18n( "Loading journals..." )
+ : i18n( "Loading events..." );
+ const bool useProgress = tqApp && tqApp->type() != TQApplication::Tty && count > mProgressDialogIncidenceLimit;
+ if ( useProgress )
+ (void)::Observer::self(); // ensure kio_uiserver is running
+ UIServer_stub uiserver( "kio_uiserver", "UIServer" );
+ int progressId = 0;
+ if ( useProgress ) {
+ progressId = uiserver.newJob( kapp->dcopClient()->appId(), true );
+ uiserver.totalFiles( progressId, count );
+ uiserver.infoMessage( progressId, labelTxt );
+ uiserver.transferring( progressId, labelTxt );
+ }
+
+ for ( int startIndex = 0; startIndex < count; startIndex += nbMessages ) {
+ TQMap<TQ_UINT32, TQString> lst;
+ if ( !kmailIncidences( lst, mimetype, subResource, startIndex, nbMessages ) ) {
+ kdError(5650) << "Communication problem in ResourceKolab::load()\n";
+ if ( progressId )
+ uiserver.jobFinished( progressId );
+ return false;
+ }
+
+ { // for RAII scoping below
+ TemporarySilencer t( this );
+ for( TQMap<TQ_UINT32, TQString>::ConstIterator it = lst.begin(); it != lst.end(); ++it ) {
+ addIncidence( mimetype, it.data(), subResource, it.key() );
+ }
+ }
+ if ( progressId ) {
+ uiserver.processedFiles( progressId, startIndex );
+ uiserver.percent( progressId, 100 * startIndex / count );
+ }
+
+// if ( progress.wasCanceled() ) {
+// uiserver.jobFinished( progressId );
+// return false;
+// }
+ }
+
+ if ( progressId )
+ uiserver.jobFinished( progressId );
+ return true;
+}
+
+bool ResourceKolab::doLoad()
+{
+ if (!mUidMap.isEmpty() ) {
+ emit resourceLoaded( this );
+ return true;
+ }
+ mUidMap.clear();
+
+ bool result = loadAllEvents() & loadAllTodos() & loadAllJournals();
+ if ( result ) {
+ emit resourceLoaded( this );
+ } else {
+ // FIXME: anyone know if the resource correctly calls loadError()
+ // if it has one?
+ }
+
+ return result;
+}
+
+bool ResourceKolab::doLoadAll( ResourceMap& map, const char* mimetype )
+{
+ bool rc = true;
+ for ( ResourceMap::ConstIterator it = map.begin(); it != map.end(); ++it ) {
+ if ( !it.data().active() )
+ // This resource is disabled
+ continue;
+
+ rc &= loadSubResource( it.key(), mimetype );
+ }
+ return rc;
+}
+
+bool ResourceKolab::loadAllEvents()
+{
+ removeIncidences( "Event" );
+ mCalendar.deleteAllEvents();
+ bool kolabStyle = doLoadAll( mEventSubResources, eventAttachmentMimeType );
+ bool icalStyle = doLoadAll( mEventSubResources, incidenceInlineMimeType );
+ return kolabStyle && icalStyle;
+}
+
+bool ResourceKolab::loadAllTodos()
+{
+ removeIncidences( "Todo" );
+ mCalendar.deleteAllTodos();
+ bool kolabStyle = doLoadAll( mTodoSubResources, todoAttachmentMimeType );
+ bool icalStyle = doLoadAll( mTodoSubResources, incidenceInlineMimeType );
+
+ return kolabStyle && icalStyle;
+}
+
+bool ResourceKolab::loadAllJournals()
+{
+ removeIncidences( "Journal" );
+ mCalendar.deleteAllJournals();
+ bool kolabStyle = doLoadAll( mJournalSubResources, journalAttachmentMimeType );
+ bool icalStyle = doLoadAll( mJournalSubResources, incidenceInlineMimeType );
+
+ return kolabStyle && icalStyle;
+}
+
+void ResourceKolab::removeIncidences( const TQCString& incidenceType )
+{
+ Kolab::UidMap::Iterator mapIt = mUidMap.begin();
+ while ( mapIt != mUidMap.end() )
+ {
+ Kolab::UidMap::Iterator it = mapIt++;
+ // Check the type of this uid: event, todo or journal.
+ // Need to look up in mCalendar for that. Given the implementation of incidence(uid),
+ // better call event(uid), todo(uid) etc. directly.
+
+ // A faster but hackish way would probably be to check the type of the resource,
+ // like mEventSubResources.find( it.data().resource() ) != mEventSubResources.end() ?
+ const TQString& uid = it.key();
+ if ( incidenceType == "Event" && mCalendar.event( uid ) )
+ mUidMap.remove( it );
+ else if ( incidenceType == "Todo" && mCalendar.todo( uid ) )
+ mUidMap.remove( it );
+ else if ( incidenceType == "Journal" && mCalendar.journal( uid ) )
+ mUidMap.remove( it );
+ }
+}
+
+bool ResourceKolab::doSave()
+{
+ return true;
+ /*
+ return kmailTriggerSync( kmailCalendarContentsType )
+ && kmailTriggerSync( kmailTodoContentsType )
+ && kmailTriggerSync( kmailJournalContentsType );
+ */
+}
+void ResourceKolab::incidenceUpdatedSilent( KCal::IncidenceBase* incidencebase )
+{
+ const TQString uid = incidencebase->uid();
+ //kdDebug() << k_funcinfo << uid << endl;
+
+ if ( mUidsPendingUpdate.contains( uid ) || mUidsPendingAdding.contains( uid ) ) {
+ /* We are currently processing this event ( removing and readding or
+ * adding it ). If so, ignore this update. Keep the last of these around
+ * and process once we hear back from KMail on this event. */
+ mPendingUpdates.remove( uid );
+ mPendingUpdates.insert( uid, incidencebase );
+ return;
+ }
+
+ { // start optimization
+ /**
+ KOrganizer and libkcal like calling two Incidence::updated()
+ for only one user change. That's because after a change,
+ IncidenceChanger calls incidence->setRevision( rev++ );
+ which also calls Incidence::updated().
+
+ Lets ignore the first updated() and only send to kmail
+ the second. This makes things faster.
+ */
+
+ //IncidenceBase doesn't have revision(), downcast needed.
+ Incidence *i = dynamic_cast<Incidence*>( incidencebase );
+
+ if ( i ) {
+ bool ignoreThisUpdate = false;
+
+ if ( !mLastKnownRevisions.contains( uid ) ) {
+ mLastKnownRevisions[uid] = i->revision();
+ }
+
+ // update the last known revision
+ if ( mLastKnownRevisions[uid] < i->revision() ) {
+ mLastKnownRevisions[uid] = i->revision();
+ } else {
+ ignoreThisUpdate = true;
+ }
+
+ if ( ignoreThisUpdate ) {
+ return;
+ }
+ }
+ } // end optimization
+
+ TQString subResource;
+ TQ_UINT32 sernum = 0;
+ if ( mUidMap.contains( uid ) ) {
+ subResource = mUidMap[ uid ].resource();
+ sernum = mUidMap[ uid ].serialNumber();
+ mUidsPendingUpdate.append( uid );
+ }
+
+ sendKMailUpdate( incidencebase, subResource, sernum );
+}
+void ResourceKolab::incidenceUpdated( KCal::IncidenceBase* incidencebase )
+{
+ if ( incidencebase->isReadOnly() ) {
+ return;
+ }
+
+ incidencebase->setSyncStatusSilent( KCal::Event::SYNCMOD );
+ incidencebase->setLastModified( TQDateTime::currentDateTime() );
+
+ // we should probably update the revision number here,
+ // or internally in the Event itself when certain things change.
+ // need to verify with ical documentation.
+ incidenceUpdatedSilent( incidencebase );
+}
+
+void ResourceKolab::resolveConflict( KCal::Incidence* inc, const TQString& subresource, TQ_UINT32 sernum )
+{
+ if ( !inc ) {
+ return;
+ }
+
+ if ( !mResolveConflict ) {
+ // we should do no conflict resolution
+ delete inc;
+ return;
+ }
+ const TQString origUid = inc->uid();
+ Incidence* local = mCalendar.incidence( origUid );
+ Incidence* localIncidence = 0;
+ Incidence* addedIncidence = 0;
+ Incidence* result = 0;
+ if ( local ) {
+ if ( *local == *inc ) {
+ // real duplicate, remove the second one
+ result = local;
+ } else {
+ KIncidenceChooser* ch = new KIncidenceChooser();
+ ch->setIncidence( local ,inc );
+ if ( KIncidenceChooser::chooseMode == KIncidenceChooser::ask ) {
+ connect ( this, TQT_SIGNAL( useGlobalMode() ), ch, TQT_SLOT ( useGlobalMode() ) );
+ if ( ch->exec() ) {
+ if ( KIncidenceChooser::chooseMode != KIncidenceChooser::ask ) {
+ emit useGlobalMode() ;
+ }
+ }
+ }
+ result = ch->getIncidence();
+ delete ch;
+ }
+ } else {
+ // nothing there locally, just take the new one. Can't Happen (TM)
+ result = inc;
+ }
+ if ( result == local ) {
+ delete inc;
+ localIncidence = local;
+ } else if ( result == inc ) {
+ addedIncidence = inc;
+ } else if ( result == 0 ) { // take both
+ addedIncidence = inc;
+ addedIncidence->setSummary( i18n("Copy of: %1").arg( addedIncidence->summary() ) );
+ addedIncidence->setUid( CalFormat::createUniqueId() );
+ localIncidence = local;
+ }
+ const bool silent = mSilent;
+ mSilent = false;
+ if ( !localIncidence ) {
+ deleteIncidence( local ); // remove local from kmail
+ }
+ mUidsPendingDeletion.append( origUid );
+ if ( addedIncidence ) {
+ sendKMailUpdate( addedIncidence, subresource, sernum );
+ } else {
+ kmailDeleteIncidence( subresource, sernum );// remove new from kmail
+ }
+ mSilent = silent;
+}
+void ResourceKolab::addIncidence( const char* mimetype, const TQString& data,
+ const TQString& subResource, TQ_UINT32 sernum )
+{
+ // This uses pointer comparison, so it only works if we use the static
+ // objects defined in the top of the file
+ if ( mimetype == eventAttachmentMimeType )
+ addEvent( data, subResource, sernum );
+ else if ( mimetype == todoAttachmentMimeType )
+ addTodo( data, subResource, sernum );
+ else if ( mimetype == journalAttachmentMimeType )
+ addJournal( data, subResource, sernum );
+ else if ( mimetype == incidenceInlineMimeType ) {
+ Incidence *inc = mFormat.fromString( data );
+ addIncidence( inc, subResource, sernum );
+ }
+}
+
+
+bool ResourceKolab::sendKMailUpdate( KCal::IncidenceBase* incidencebase, const TQString& subresource,
+ TQ_UINT32 sernum )
+{
+ const TQString& type = incidencebase->type();
+ const char* mimetype = 0;
+ TQString data;
+ bool isXMLStorageFormat = kmailStorageFormat( subresource ) == KMailICalIface::StorageXML;
+ if ( type == "Event" ) {
+ if( isXMLStorageFormat ) {
+ mimetype = eventAttachmentMimeType;
+ data = Kolab::Event::eventToXML( static_cast<KCal::Event *>(incidencebase),
+ mCalendar.timeZoneId() );
+ } else {
+ mimetype = incidenceInlineMimeType;
+ data = mFormat.createScheduleMessage( static_cast<KCal::Event *>(incidencebase),
+ Scheduler::Request );
+ }
+ } else if ( type == "Todo" ) {
+ if( isXMLStorageFormat ) {
+ mimetype = todoAttachmentMimeType;
+ data = Kolab::Task::taskToXML( static_cast<KCal::Todo *>(incidencebase),
+ mCalendar.timeZoneId() );
+ } else {
+ mimetype = incidenceInlineMimeType;
+ data = mFormat.createScheduleMessage( static_cast<KCal::Todo *>(incidencebase),
+ Scheduler::Request );
+ }
+ } else if ( type == "Journal" ) {
+ if( isXMLStorageFormat ) {
+ mimetype = journalAttachmentMimeType;
+ data = Kolab::Journal::journalToXML( static_cast<KCal::Journal *>(incidencebase ),
+ mCalendar.timeZoneId() );
+ } else {
+ mimetype = incidenceInlineMimeType;
+ data = mFormat.createScheduleMessage( static_cast<KCal::Journal *>(incidencebase),
+ Scheduler::Request );
+ }
+ } else {
+ kdWarning(5006) << "Can't happen: unhandled type=" << type << endl;
+ }
+
+// kdDebug() << k_funcinfo << "Data string:\n" << data << endl;
+
+ KCal::Incidence* incidence = static_cast<KCal::Incidence *>( incidencebase );
+
+ KCal::Attachment::List atts = incidence->attachments();
+ TQStringList attURLs, attMimeTypes, attNames;
+ TQValueList<KTempFile*> tmpFiles;
+ for ( KCal::Attachment::List::ConstIterator it = atts.constBegin(); it != atts.constEnd(); ++it ) {
+ if ( (*it)->isUri() ) {
+ continue;
+ }
+ KTempFile *tempFile = new KTempFile;
+ if ( tempFile->status() == 0 ) { // open ok
+ const TQByteArray decoded = (*it)->decodedData() ;
+
+ tempFile->file()->writeBlock( decoded.data(), decoded.count() );
+ KURL url;
+ url.setPath( tempFile->name() );
+ attURLs.append( url.url() );
+ attMimeTypes.append( (*it)->mimeType() );
+ attNames.append( (*it)->label() );
+ tempFile->close();
+ tmpFiles.append( tempFile );
+ } else {
+ kdWarning(5006) << "Cannot open temporary file for attachment";
+ }
+ }
+ TQStringList deletedAtts;
+ if ( kmailListAttachments( deletedAtts, subresource, sernum ) ) {
+ for ( TQStringList::ConstIterator it = attNames.constBegin(); it != attNames.constEnd(); ++it ) {
+ deletedAtts.remove( *it );
+ }
+ }
+ CustomHeaderMap customHeaders;
+ if ( incidence->schedulingID() != incidence->uid() ) {
+ customHeaders.insert( "X-Kolab-SchedulingID", incidence->schedulingID() );
+ }
+
+ TQString subject = incidencebase->uid();
+ if ( !isXMLStorageFormat ) subject.prepend( "iCal " ); // conform to the old style
+
+ // behold, sernum is an in-parameter
+ const bool rc = kmailUpdate( subresource, sernum, data, mimetype, subject, customHeaders, attURLs, attMimeTypes, attNames, deletedAtts );
+ // update the serial number
+ if ( mUidMap.contains( incidencebase->uid() ) ) {
+ mUidMap[ incidencebase->uid() ].setSerialNumber( sernum );
+ }
+
+ for( TQValueList<KTempFile *>::Iterator it = tmpFiles.begin(); it != tmpFiles.end(); ++it ) {
+ (*it)->setAutoDelete( true );
+ delete (*it);
+ }
+
+ return rc;
+}
+
+bool ResourceKolab::addIncidence( KCal::Incidence* incidence, const TQString& _subresource,
+ TQ_UINT32 sernum )
+{
+ Q_ASSERT( incidence );
+ if ( !incidence ) {
+ return false;
+ }
+
+ kdDebug() << "Resourcekolab, adding incidence "
+ << incidence->summary()
+ << "; subresource = " << _subresource
+ << "; sernum = " << sernum
+ << "; mBatchAddingInProgress = " << mBatchAddingInProgress
+ << endl;
+
+ TQString uid = incidence->uid();
+ TQString subResource = _subresource;
+
+ Kolab::ResourceMap *map = &mEventSubResources; // don't use a ref here!
+
+ const TQString& type = incidence->type();
+ if ( type == "Event" ) {
+ map = &mEventSubResources;
+ } else if ( type == "Todo" ) {
+ map = &mTodoSubResources;
+ } else if ( type == "Journal" ) {
+ map = &mJournalSubResources;
+ } else {
+ kdWarning() << "unknown type " << type << endl;
+ }
+
+ if ( !mSilent ) { /* We got this one from the user, tell KMail. */
+ // Find out if this event was previously stored in KMail
+ bool newIncidence = _subresource.isEmpty();
+ if ( newIncidence ) {
+ ResourceType type = Incidences;
+ // Add a description of the incidence
+ TQString text = "<b><font size=\"+1\">";
+ if ( incidence->type() == "Event" ) {
+ type = Events;
+ text += i18n( "Choose the folder where you want to store this event" );
+ } else if ( incidence->type() == "Todo" ) {
+ type = Tasks;
+ text += i18n( "Choose the folder where you want to store this task" );
+ } else {
+ text += i18n( "Choose the folder where you want to store this incidence" );
+ }
+ text += "<font></b><br>";
+ if ( !incidence->summary().isEmpty() )
+ text += i18n( "<b>Summary:</b> %1" ).arg( incidence->summary() ) + "<br>";
+ if ( !incidence->location().isEmpty() )
+ text += i18n( "<b>Location:</b> %1" ).arg( incidence->location() );
+ text += "<br>";
+ if ( !incidence->doesFloat() )
+ text += i18n( "<b>Start:</b> %1, %2" )
+ .arg( incidence->dtStartDateStr(), incidence->dtStartTimeStr() );
+ else
+ text += i18n( "<b>Start:</b> %1" ).arg( incidence->dtStartDateStr() );
+ text += "<br>";
+ if ( incidence->type() == "Event" ) {
+ Event* event = static_cast<Event*>( incidence );
+ if ( event->hasEndDate() ) {
+ if ( !event->doesFloat() ) {
+ text += i18n( "<b>End:</b> %1, %2" )
+ .arg( event->dtEndDateStr(), event->dtEndTimeStr() );
+ } else {
+ text += i18n( "<b>End:</b> %1" ).arg( event->dtEndDateStr() );
+ }
+ }
+ text += "<br>";
+ }
+
+ // Lets not warn the user 100 times that there's no writable resource
+ // and not ask 100 times which resource to use
+ if ( !mBatchAddingInProgress || !mLastUsedResources.contains( type ) ) {
+ subResource = findWritableResource( type, *map, text );
+ mLastUsedResources[type] = subResource;
+ } else {
+ subResource = mLastUsedResources[type];
+ }
+
+ if ( subResource.isEmpty() ) {
+ switch( mErrorCode ) {
+ case NoWritableFound:
+ setException( new ErrorFormat( ErrorFormat::NoWritableFound ) );
+ break;
+ case UserCancel:
+ setException( new ErrorFormat( ErrorFormat::UserCancel ) );
+ break;
+ case NoError:
+ break;
+ }
+ }
+ }
+
+ if ( subResource.isEmpty() ) {
+ endAddingIncidences(); // cleanup
+ kdDebug(5650) << "ResourceKolab: subResource is empty" << endl;
+ return false;
+ }
+
+ mNewIncidencesMap.insert( uid, subResource );
+
+ if ( !sendKMailUpdate( incidence, subResource, sernum ) ) {
+ kdError(5650) << "Communication problem in ResourceKolab::addIncidence()\n";
+ endAddingIncidences(); // cleanup
+ return false;
+ } else {
+ // KMail is doing it's best to add the event now, put a sticker on it,
+ // so we know it's one of our transient ones
+ mUidsPendingAdding.append( uid );
+
+ /* Add to the cache immediately if this is a new event coming from
+ * KOrganizer. It relies on the incidence being in the calendar when
+ * addIncidence returns. */
+ if ( newIncidence || sernum == 0 ) {
+ mCalendar.addIncidence( incidence );
+ incidence->registerObserver( this );
+ }
+ }
+ } else { /* KMail told us */
+ const bool ourOwnUpdate = mUidsPendingUpdate.contains( uid );
+ kdDebug( 5650 ) << "addIncidence: ourOwnUpdate " << ourOwnUpdate << endl;
+ /* Check if we updated this one, which means kmail deleted and added it.
+ * We know the new state, so lets just not do much at all. The old incidence
+ * in the calendar remains valid, but the serial number changed, so we need to
+ * update that */
+ if ( ourOwnUpdate ) {
+ mUidsPendingUpdate.remove( uid );
+ mUidMap.remove( uid );
+ mUidMap[ uid ] = StorageReference( subResource, sernum );
+ } else {
+ /* This is a real add, from KMail, we didn't trigger this ourselves.
+ * If this uid already exists in this folder, do conflict resolution,
+ * unless the folder is read-only, in which case the user should not be
+ * offered a means of putting mails in a folder she'll later be unable to
+ * upload. Skip the incidence, in this case. */
+ if ( mUidMap.contains( uid ) ) {
+ if ( mUidMap[ uid ].resource() == subResource ) {
+ if ( (*map)[ subResource ].writable() ) {
+ kdDebug( 5650 ) << "lets resolve the conflict " << endl;
+ resolveConflict( incidence, subResource, sernum );
+ } else {
+ kdWarning( 5650 ) << "Duplicate event in a read-only folder detected! "
+ "Please inform the owner of the folder. " << endl;
+ }
+ return true;
+ } else {
+ // duplicate uid in a different folder, do the internal-uid tango
+ incidence->setSchedulingID( uid );
+
+ incidence->setUid( CalFormat::createUniqueId( ) );
+ uid = incidence->uid();
+
+ /* Will be needed when kmail triggers a delete, so we don't delete the inocent
+ * incidence that's sharing the uid with this one */
+ mOriginalUID2fakeUID[tqMakePair( incidence->schedulingID(), subResource )] = uid;
+ }
+ }
+ /* Add to the cache if the add didn't come from KOrganizer, in which case
+ * we've already added it, and listen to updates from KOrganizer for it. */
+ if ( !mUidsPendingAdding.contains( uid ) ) {
+ mCalendar.addIncidence( incidence );
+ incidence->registerObserver( this );
+ }
+ if ( !subResource.isEmpty() && sernum != 0 ) {
+ mUidMap[ uid ] = StorageReference( subResource, sernum );
+ incidence->setReadOnly( !(*map)[ subResource ].writable() );
+ }
+ }
+ /* Check if there are updates for this uid pending and if so process them. */
+ if ( KCal::IncidenceBase *update = mPendingUpdates.find( uid ) ) {
+ mSilent = false; // we do want to tell KMail
+ mPendingUpdates.remove( uid );
+ incidenceUpdated( update );
+ } else {
+ /* If the uid was added by KMail, KOrganizer needs to be told, so
+ * schedule emitting of the resourceChanged signal. */
+ if ( !mUidsPendingAdding.contains( uid ) ) {
+ if ( !ourOwnUpdate ) mResourceChangedTimer.changeInterval( 100 );
+ } else {
+ mUidsPendingAdding.remove( uid );
+ }
+ }
+
+ mNewIncidencesMap.remove( uid );
+ }
+ return true;
+}
+
+bool ResourceKolab::addEvent( KCal::Event *event )
+{
+ return addEvent( event, TQString() );
+}
+
+bool ResourceKolab::addEvent( KCal::Event *event, const TQString &subResource )
+{
+ if ( mUidMap.contains( event->uid() ) ) {
+ return true; //noop
+ } else {
+ return addIncidence( event, subResource, 0 );
+ }
+}
+
+void ResourceKolab::addEvent( const TQString& xml, const TQString& subresource,
+ TQ_UINT32 sernum )
+{
+ KCal::Event* event = Kolab::Event::xmlToEvent( xml, mCalendar.timeZoneId(), this, subresource, sernum );
+ Q_ASSERT( event );
+ if ( event ) {
+ addIncidence( event, subresource, sernum );
+ }
+}
+
+bool ResourceKolab::deleteIncidence( KCal::Incidence* incidence )
+{
+ if ( incidence->isReadOnly() ) {
+ return false;
+ }
+
+ const TQString uid = incidence->uid();
+ if( !mUidMap.contains( uid ) ) return false; // Odd
+ /* The user told us to delete, tell KMail */
+ if ( !mSilent ) {
+ kmailDeleteIncidence( mUidMap[ uid ].resource(),
+ mUidMap[ uid ].serialNumber() );
+ mUidsPendingDeletion.append( uid );
+ incidence->unRegisterObserver( this );
+ mCalendar.deleteIncidence( incidence );
+ mUidMap.remove( uid );
+ } else {
+ assert( false ); // If this still happens, something is very wrong
+ }
+ return true;
+}
+
+bool ResourceKolab::deleteEvent( KCal::Event* event )
+{
+ return deleteIncidence( event );
+}
+
+KCal::Event* ResourceKolab::event( const TQString& uid )
+{
+ return mCalendar.event(uid);
+}
+
+KCal::Event::List ResourceKolab::rawEvents( EventSortField sortField, SortDirection sortDirection )
+{
+ return mCalendar.rawEvents( sortField, sortDirection );
+}
+
+KCal::Event::List ResourceKolab::rawEventsForDate( const TQDate& date,
+ EventSortField sortField,
+ SortDirection sortDirection )
+{
+ return mCalendar.rawEventsForDate( date, sortField, sortDirection );
+}
+
+KCal::Event::List ResourceKolab::rawEventsForDate( const TQDateTime& qdt )
+{
+ return mCalendar.rawEventsForDate( qdt );
+}
+
+KCal::Event::List ResourceKolab::rawEvents( const TQDate& start,
+ const TQDate& end,
+ bool inclusive )
+{
+ return mCalendar.rawEvents( start, end, inclusive );
+}
+
+bool ResourceKolab::addTodo( KCal::Todo *todo )
+{
+ return addTodo( todo, TQString() );
+}
+
+bool ResourceKolab::addTodo( KCal::Todo *todo, const TQString &subResource )
+{
+ if ( mUidMap.contains( todo->uid() ) ) {
+ return true; //noop
+ } else {
+ return addIncidence( todo, subResource, 0 );
+ }
+}
+
+void ResourceKolab::addTodo( const TQString& xml, const TQString& subresource,
+ TQ_UINT32 sernum )
+{
+ KCal::Todo* todo = Kolab::Task::xmlToTask( xml, mCalendar.timeZoneId(), this, subresource, sernum );
+ Q_ASSERT( todo );
+ if ( todo ) {
+ addIncidence( todo, subresource, sernum );
+ }
+}
+
+bool ResourceKolab::deleteTodo( KCal::Todo* todo )
+{
+ return deleteIncidence( todo );
+}
+
+KCal::Todo* ResourceKolab::todo( const TQString& uid )
+{
+ return mCalendar.todo( uid );
+}
+
+KCal::Todo::List ResourceKolab::rawTodos( TodoSortField sortField, SortDirection sortDirection )
+{
+ return mCalendar.rawTodos( sortField, sortDirection );
+}
+
+KCal::Todo::List ResourceKolab::rawTodosForDate( const TQDate& date )
+{
+ return mCalendar.rawTodosForDate( date );
+}
+
+bool ResourceKolab::addJournal( KCal::Journal *journal )
+{
+ return addJournal( journal, TQString() );
+}
+
+bool ResourceKolab::addJournal( KCal::Journal *journal, const TQString &subResource )
+{
+ if ( mUidMap.contains( journal->uid() ) )
+ return true; //noop
+ else
+ return addIncidence( journal, subResource, 0 );
+}
+
+void ResourceKolab::addJournal( const TQString& xml, const TQString& subresource,
+ TQ_UINT32 sernum )
+{
+ KCal::Journal* journal =
+ Kolab::Journal::xmlToJournal( xml, mCalendar.timeZoneId() );
+ Q_ASSERT( journal );
+ if( journal ) {
+ addIncidence( journal, subresource, sernum );
+ }
+}
+
+bool ResourceKolab::deleteJournal( KCal::Journal* journal )
+{
+ return deleteIncidence( journal );
+}
+
+KCal::Journal* ResourceKolab::journal( const TQString& uid )
+{
+ return mCalendar.journal(uid);
+}
+
+KCal::Journal::List ResourceKolab::rawJournals( JournalSortField sortField, SortDirection sortDirection )
+{
+ return mCalendar.rawJournals( sortField, sortDirection );
+}
+
+KCal::Journal::List ResourceKolab::rawJournalsForDate( const TQDate &date )
+{
+ return mCalendar.rawJournalsForDate( date );
+}
+
+KCal::Alarm::List ResourceKolab::relevantAlarms( const KCal::Alarm::List &alarms )
+{
+ KCal::Alarm::List relevantAlarms;
+ KCal::Alarm::List::ConstIterator it( alarms.begin() );
+ while ( it != alarms.end() ) {
+ KCal::Alarm *a = (*it);
+ ++it;
+ const TQString &uid = a->parent()->uid();
+ if ( mUidMap.contains( uid ) ) {
+ const TQString &sr = mUidMap[ uid ].resource();
+ Kolab::SubResource *subResource = 0;
+ if ( mEventSubResources.contains( sr ) )
+ subResource = &( mEventSubResources[ sr ] );
+ else if ( mTodoSubResources.contains( sr ) )
+ subResource = &( mTodoSubResources[ sr ] );
+ assert( subResource );
+ if ( subResource->alarmRelevant() )
+ relevantAlarms.append ( a );
+ else {
+ kdDebug(5650) << "Alarm skipped, not relevant." << endl;
+ }
+ }
+ }
+ return relevantAlarms;
+}
+
+
+
+KCal::Alarm::List ResourceKolab::alarms( const TQDateTime& from,
+ const TQDateTime& to )
+{
+ return relevantAlarms( mCalendar.alarms( from, to ) );
+}
+
+KCal::Alarm::List ResourceKolab::alarmsTo( const TQDateTime& to )
+{
+ return relevantAlarms( mCalendar.alarmsTo(to) );
+}
+
+void ResourceKolab::setTimeZoneId( const TQString& tzid )
+{
+ mCalendar.setTimeZoneId( tzid );
+ mFormat.setTimeZone( mCalendar.timeZoneId(), !mCalendar.isLocalTime() );
+}
+
+bool ResourceKolab::fromKMailAddIncidence( const TQString& type,
+ const TQString& subResource,
+ TQ_UINT32 sernum,
+ int format,
+ const TQString& data )
+{
+ bool rc = true;
+ TemporarySilencer t( this ); // RAII
+ if ( type != kmailCalendarContentsType && type != kmailTodoContentsType
+ && type != kmailJournalContentsType ) {
+ // Not ours
+ return false;
+ }
+
+ if ( !subresourceActive( subResource ) ) {
+ return true;
+ }
+
+ if ( format == KMailICalIface::StorageXML ) {
+ // If this data file is one of ours, load it here
+ if ( type == kmailCalendarContentsType ) {
+ addEvent( data, subResource, sernum );
+ } else if ( type == kmailTodoContentsType ) {
+ addTodo( data, subResource, sernum );
+ } else if ( type == kmailJournalContentsType ) {
+ addJournal( data, subResource, sernum );
+ } else {
+ rc = false;
+ }
+ } else {
+ Incidence *inc = mFormat.fromString( data );
+ if ( inc ) {
+ addIncidence( inc, subResource, sernum );
+ } else {
+ rc = false;
+ }
+ }
+ return rc;
+}
+
+void ResourceKolab::fromKMailDelIncidence( const TQString& type,
+ const TQString& subResource,
+ const TQString& uid )
+{
+ if ( type != kmailCalendarContentsType && type != kmailTodoContentsType
+ && type != kmailJournalContentsType )
+ // Not ours
+ return;
+ if ( !subresourceActive( subResource ) ) return;
+
+ // Can't be in both, by contract
+ if ( mUidsPendingDeletion.find( uid ) != mUidsPendingDeletion.end() ) {
+ mUidsPendingDeletion.remove( mUidsPendingDeletion.find( uid ) );
+ } else if ( mUidsPendingUpdate.contains( uid ) ) {
+ // It's good to know if was deleted, but we are waiting on a new one to
+ // replace it, so let's just sit tight.
+ } else {
+ TQString uidToUse;
+
+ TQPair<TQString, TQString> p( uid, subResource );
+ if ( mOriginalUID2fakeUID.contains( p ) ) {
+ // Incidence with the same uid in a different folder...
+ // use the UID that addIncidence(...) generated
+ uidToUse = mOriginalUID2fakeUID[p];
+ } else {
+ uidToUse = uid;
+ }
+
+ // We didn't trigger this, so KMail did, remove the reference to the uid
+ KCal::Incidence* incidence = mCalendar.incidence( uidToUse );
+ if( incidence ) {
+ incidence->unRegisterObserver( this );
+ mCalendar.deleteIncidence( incidence );
+ }
+ mUidMap.remove( uidToUse );
+ mOriginalUID2fakeUID.remove( p );
+ mResourceChangedTimer.changeInterval( 100 );
+ }
+}
+
+void ResourceKolab::fromKMailRefresh( const TQString& type,
+ const TQString& /*subResource*/ )
+{
+ // TODO: Only load the specified subResource
+ if ( type == "Calendar" )
+ loadAllEvents();
+ else if ( type == "Task" )
+ loadAllTodos();
+ else if ( type == "Journal" )
+ loadAllJournals();
+ else
+ kdWarning(5006) << "KCal Kolab resource: fromKMailRefresh: unknown type " << type << endl;
+ mResourceChangedTimer.changeInterval( 100 );
+}
+
+void ResourceKolab::fromKMailAddSubresource( const TQString& type,
+ const TQString& subResource,
+ const TQString& label,
+ bool writable, bool alarmRelevant )
+{
+ ResourceMap* map = 0;
+ const char* mimetype = 0;
+ if ( type == kmailCalendarContentsType ) {
+ map = &mEventSubResources;
+ mimetype = eventAttachmentMimeType;
+ } else if ( type == kmailTodoContentsType ) {
+ map = &mTodoSubResources;
+ mimetype = todoAttachmentMimeType;
+ } else if ( type == kmailJournalContentsType ) {
+ map = &mJournalSubResources;
+ mimetype = journalAttachmentMimeType;
+ } else
+ // Not ours
+ return;
+
+ if ( map->contains( subResource ) )
+ // Already registered
+ return;
+
+ TDEConfig config( configFile() );
+ config.setGroup( subResource );
+
+ bool active = config.readBoolEntry( subResource, true );
+ (*map)[ subResource ] = Kolab::SubResource( active, writable,
+ alarmRelevant, label );
+ loadSubResource( subResource, mimetype );
+ emit signalSubresourceAdded( this, type, subResource, label );
+}
+
+void ResourceKolab::fromKMailDelSubresource( const TQString& type,
+ const TQString& subResource )
+{
+ ResourceMap* map = subResourceMap( type );
+ if ( !map ) // not ours
+ return;
+ if ( map->contains( subResource ) )
+ map->erase( subResource );
+ else
+ // Not registered
+ return;
+
+ // Delete from the config file
+ TDEConfig config( configFile() );
+ config.deleteGroup( subResource );
+ config.sync();
+
+ unloadSubResource( subResource );
+
+ emit signalSubresourceRemoved( this, type, subResource );
+}
+
+TQStringList ResourceKolab::subresources() const
+{
+ // Workaround: The ResourceView in KOrganizer wants to know this
+ // before it opens the resource :-( Make sure we are open
+ const_cast<ResourceKolab*>( this )->doOpen();
+ return ( mEventSubResources.keys()
+ + mTodoSubResources.keys()
+ + mJournalSubResources.keys() );
+}
+
+const TQString
+ResourceKolab::labelForSubresource( const TQString& subresource ) const
+{
+ if ( mEventSubResources.contains( subresource ) )
+ return mEventSubResources[ subresource ].label();
+ if ( mTodoSubResources.contains( subresource ) )
+ return mTodoSubResources[ subresource ].label();
+ if ( mJournalSubResources.contains( subresource ) )
+ return mJournalSubResources[ subresource ].label();
+ return subresource;
+}
+
+void ResourceKolab::fromKMailAsyncLoadResult( const TQMap<TQ_UINT32, TQString>& map,
+ const TQString& type,
+ const TQString& folder )
+{
+ TemporarySilencer t( this );
+ for( TQMap<TQ_UINT32, TQString>::ConstIterator it = map.begin(); it != map.end(); ++it )
+ addIncidence( type.latin1(), it.data(), folder, it.key() );
+}
+
+bool ResourceKolab::subresourceActive( const TQString& subresource ) const
+{
+ // Workaround: The ResourceView in KOrganizer wants to know this
+ // before it opens the resource :-( Make sure we are open
+ const_cast<ResourceKolab*>( this )->doOpen();
+
+ if ( mEventSubResources.contains( subresource ) )
+ return mEventSubResources[ subresource ].active();
+ if ( mTodoSubResources.contains( subresource ) )
+ return mTodoSubResources[ subresource ].active();
+ if ( mJournalSubResources.contains( subresource ) )
+ return mJournalSubResources[ subresource ].active();
+
+ // Safe default bet:
+ kdDebug(5650) << "subresourceActive( " << subresource << " ): Safe bet\n";
+
+ return true;
+}
+
+void ResourceKolab::setSubresourceActive( const TQString &subresource, bool v )
+{
+ ResourceMap *map = 0;
+ const char* mimeType = 0;
+ if ( mEventSubResources.contains( subresource ) ) {
+ map = &mEventSubResources;
+ mimeType = eventAttachmentMimeType;
+ }
+ if ( mTodoSubResources.contains( subresource ) ) {
+ map = &mTodoSubResources;
+ mimeType = todoAttachmentMimeType;
+ }
+ if ( mJournalSubResources.contains( subresource ) ) {
+ map = &mJournalSubResources;
+ mimeType = journalAttachmentMimeType;
+ }
+
+ if ( map && ( ( *map )[ subresource ].active() != v ) ) {
+ ( *map )[ subresource ].setActive( v );
+ if ( v ) {
+ loadSubResource( subresource, mimeType );
+ } else {
+ unloadSubResource( subresource );
+ }
+ mResourceChangedTimer.changeInterval( 100 );
+ }
+ TQTimer::singleShot( 0, this, TQT_SLOT(writeConfig()) );
+}
+
+bool ResourceKolab::subresourceWritable( const TQString& subresource ) const
+{
+ // Workaround: The ResourceView in KOrganizer wants to know this
+ // before it opens the resource :-( Make sure we are open
+ const_cast<ResourceKolab*>( this )->doOpen();
+
+ if ( mEventSubResources.contains( subresource ) )
+ return mEventSubResources[ subresource ].writable();
+ if ( mTodoSubResources.contains( subresource ) )
+ return mTodoSubResources[ subresource ].writable();
+ if ( mJournalSubResources.contains( subresource ) )
+ return mJournalSubResources[ subresource ].writable();
+
+ return false; //better a safe default
+}
+
+void ResourceKolab::slotEmitResourceChanged()
+{
+ kdDebug(5650) << "KCal Kolab resource: emitting resource changed " << endl;
+ mResourceChangedTimer.stop();
+ emit resourceChanged( this );
+}
+
+KABC::Lock* ResourceKolab::lock()
+{
+ return new KABC::LockNull( true );
+}
+
+Kolab::ResourceMap* ResourceKolab::subResourceMap( const TQString& contentsType )
+{
+ if ( contentsType == kmailCalendarContentsType ) {
+ return &mEventSubResources;
+ } else if ( contentsType == kmailTodoContentsType ) {
+ return &mTodoSubResources;
+ } else if ( contentsType == kmailJournalContentsType ) {
+ return &mJournalSubResources;
+ }
+ // Not ours
+ return 0;
+}
+
+
+/*virtual*/
+bool ResourceKolab::addSubresource( const TQString& resource, const TQString& parent )
+{
+ kdDebug(5650) << "KCal Kolab resource - adding subresource: " << resource << endl;
+ TQString contentsType = kmailCalendarContentsType;
+ if ( !parent.isEmpty() ) {
+ if ( mEventSubResources.contains( parent ) )
+ contentsType = kmailCalendarContentsType;
+ else if ( mTodoSubResources.contains( parent ) )
+ contentsType = kmailTodoContentsType;
+ else if ( mJournalSubResources.contains( parent ) )
+ contentsType = kmailJournalContentsType;
+ } else {
+ TQStringList contentTypeChoices;
+ contentTypeChoices << i18n("Calendar") << i18n("Tasks") << i18n("Journals");
+ const TQString caption = i18n("Which kind of subresource should this be?");
+ const TQString choice = KInputDialog::getItem( caption, TQString(), contentTypeChoices );
+ if ( choice == contentTypeChoices[0] )
+ contentsType = kmailCalendarContentsType;
+ else if ( choice == contentTypeChoices[1] )
+ contentsType = kmailTodoContentsType;
+ else if ( choice == contentTypeChoices[2] )
+ contentsType = kmailJournalContentsType;
+ }
+
+ return kmailAddSubresource( resource, parent, contentsType );
+}
+
+/*virtual*/
+bool ResourceKolab::removeSubresource( const TQString& resource )
+{
+ kdDebug(5650) << "KCal Kolab resource - removing subresource: " << resource << endl;
+ return kmailRemoveSubresource( resource );
+}
+
+/*virtual*/
+TQString ResourceKolab::subresourceIdentifier( Incidence *incidence )
+{
+ TQString uid = incidence->uid();
+ if ( mUidMap.contains( uid ) )
+ return mUidMap[ uid ].resource();
+ else
+ if ( mNewIncidencesMap.contains( uid ) )
+ return mNewIncidencesMap[ uid ];
+ else
+ return TQString();
+}
+
+
+bool ResourceKolab::unloadSubResource( const TQString& subResource )
+{
+ const bool silent = mSilent;
+ mSilent = true;
+ Kolab::UidMap::Iterator mapIt = mUidMap.begin();
+ TQPtrList<KCal::Incidence> incidences;
+ while ( mapIt != mUidMap.end() )
+ {
+ Kolab::UidMap::Iterator it = mapIt++;
+ const StorageReference ref = it.data();
+ if ( ref.resource() != subResource ) continue;
+ // FIXME incidence() is expensive
+ KCal::Incidence* incidence = mCalendar.incidence( it.key() );
+ if( incidence ) {
+ // register all observers first before actually deleting them
+ // in case of inter-incidence relations the other part will get
+ // the change notification otherwise
+ incidence->unRegisterObserver( this );
+ incidences.append( incidence );
+ }
+ mUidMap.remove( it );
+ }
+ TQPtrListIterator<KCal::Incidence> it( incidences );
+ for ( ; it.current(); ++it ) {
+ mCalendar.deleteIncidence( it.current() );
+ }
+ mSilent = silent;
+ return true;
+}
+
+TQString ResourceKolab::subresourceType( const TQString &resource )
+{
+ if ( mEventSubResources.contains( resource ) )
+ return "event";
+ if ( mTodoSubResources.contains( resource ) )
+ return "todo";
+ if ( mJournalSubResources.contains( resource ) )
+ return "journal";
+ return TQString();
+}
+
+void ResourceKolab::writeConfig()
+{
+ TDEConfig config( configFile() );
+ writeResourceConfig( config, mEventSubResources );
+ writeResourceConfig( config, mTodoSubResources );
+ writeResourceConfig( config, mJournalSubResources );
+}
+
+void ResourceKolab::beginAddingIncidences()
+{
+ mBatchAddingInProgress = true;
+}
+
+void ResourceKolab::endAddingIncidences()
+{
+ mBatchAddingInProgress = false;
+ mLastUsedResources.clear();
+}
+
+#include "resourcekolab.moc"
diff --git a/tderesources/kolab/kcal/resourcekolab.h b/tderesources/kolab/kcal/resourcekolab.h
new file mode 100644
index 000000000..fdf952cc3
--- /dev/null
+++ b/tderesources/kolab/kcal/resourcekolab.h
@@ -0,0 +1,284 @@
+/*
+ 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.
+*/
+
+#ifndef KCAL_RESOURCEKOLAB_H
+#define KCAL_RESOURCEKOLAB_H
+
+#include <tqtimer.h>
+
+#include <tdepimmacros.h>
+#include <libkcal/calendarlocal.h>
+#include <libkcal/icalformat.h>
+#include <libkcal/resourcecalendar.h>
+#include "../shared/resourcekolabbase.h"
+
+namespace KCal {
+
+struct TemporarySilencer;
+
+class KDE_EXPORT ResourceKolab : public KCal::ResourceCalendar,
+ public KCal::IncidenceBase::Observer,
+ public Kolab::ResourceKolabBase
+{
+ Q_OBJECT
+
+ friend struct TemporarySilencer;
+
+public:
+ ResourceKolab( const TDEConfig* );
+ virtual ~ResourceKolab();
+
+ /// Load resource data.
+ bool doLoad();
+
+ /// Save resource data.
+ bool doSave();
+
+ /// Open the notes resource.
+ bool doOpen();
+ /// Close the notes resource.
+ void doClose();
+
+ // The libkcal functions. See the resource for descriptions
+ 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(
+ const TQDate& date,
+ EventSortField sortField=EventSortUnsorted,
+ SortDirection sortDirection=SortDirectionAscending );
+ KCal::Event::List rawEventsForDate( const TQDateTime& qdt );
+ KCal::Event::List rawEvents( const TQDate& start, const TQDate& end,
+ bool inclusive = false );
+
+ 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 );
+
+ 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 );
+
+ KCal::Alarm::List alarms( const TQDateTime& from, const TQDateTime& to );
+ KCal::Alarm::List alarmsTo( const TQDateTime& to );
+
+ void setTimeZoneId( const TQString& tzid );
+
+ bool deleteIncidence( KCal::Incidence* i );
+
+ /// The ResourceKolabBase methods called by KMail
+ bool fromKMailAddIncidence( const TQString& type, const TQString& subResource,
+ TQ_UINT32 sernum, int format, const TQString& data );
+ void fromKMailDelIncidence( const TQString& type, const TQString& subResource,
+ const TQString& uid );
+ void fromKMailRefresh( const TQString& type, const TQString& subResource );
+
+ /// Listen to KMail changes in the amount of sub resources
+ void fromKMailAddSubresource( const TQString& type, const TQString& subResource,
+ const TQString& label, bool writable,
+ bool alarmRelevant );
+ void fromKMailDelSubresource( const TQString& type, const TQString& subResource );
+
+ void fromKMailAsyncLoadResult( const TQMap<TQ_UINT32, TQString>& map,
+ const TQString& type,
+ const TQString& folder );
+
+ /** Return the list of subresources. */
+ TQStringList subresources() const;
+
+ bool canHaveSubresources() const { return true; }
+
+ /** Is this subresource active? */
+ bool subresourceActive( const TQString& ) const;
+ /** (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;
+
+ virtual TQString subresourceIdentifier( Incidence *incidence );
+
+ virtual bool addSubresource( const TQString& resource, const TQString& parent );
+ virtual bool removeSubresource( const TQString& resource );
+
+ virtual TQString subresourceType( const TQString &resource );
+
+ 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
+ * are the ones coming from folders which the user has "Administer" rights
+ * for, as per ACL */
+ KCal::Alarm::List relevantAlarms( const KCal::Alarm::List &alarms );
+
+private:
+ void removeIncidences( const TQCString& incidenceType );
+ void resolveConflict( KCal::Incidence*, const TQString& subresource, TQ_UINT32 sernum );
+ void addIncidence( const char* mimetype, const TQString& xml,
+ const TQString& subResource, TQ_UINT32 sernum );
+
+
+ /**
+ Caller guarantees i is not null.
+ */
+ bool addIncidence( KCal::Incidence *i, const TQString& subresource,
+ TQ_UINT32 sernum );
+
+ void addEvent( const TQString& xml, const TQString& subresource,
+ TQ_UINT32 sernum );
+ void addTodo( const TQString& xml, const TQString& subresource,
+ TQ_UINT32 sernum );
+ void addJournal( const TQString& xml, const TQString& subresource,
+ TQ_UINT32 sernum );
+
+
+ bool loadAllEvents();
+ bool loadAllTodos();
+ bool loadAllJournals();
+
+ bool doLoadAll( Kolab::ResourceMap& map, const char* mimetype );
+
+ /// Reimplemented from IncidenceBase::Observer to know when an incidence was changed
+ void incidenceUpdated( KCal::IncidenceBase* );
+ void incidenceUpdatedSilent( KCal::IncidenceBase* incidencebase);
+
+ bool openResource( TDEConfig& config, const char* contentType,
+ Kolab::ResourceMap& map );
+ void loadSubResourceConfig( TDEConfig& config, const TQString& name,
+ const TQString& label, bool writable,
+ bool alarmRelevant, Kolab::ResourceMap& subResource );
+ bool loadSubResource( const TQString& subResource, const char* mimetype );
+ bool unloadSubResource( const TQString& subResource );
+
+ TQString configFile() const {
+ return ResourceKolabBase::configFile( "kcal" );
+ }
+
+ Kolab::ResourceMap* subResourceMap( const TQString& contentsType );
+
+ bool sendKMailUpdate( KCal::IncidenceBase* incidence, const TQString& _subresource,
+ TQ_UINT32 sernum );
+
+
+ KCal::CalendarLocal mCalendar;
+
+ // The list of subresources
+ Kolab::ResourceMap mEventSubResources, mTodoSubResources, mJournalSubResources;
+
+ bool mOpen; // If the resource is open, this is true
+ TQDict<KCal::IncidenceBase> mPendingUpdates;
+ TQTimer mResourceChangedTimer;
+ ICalFormat mFormat;
+
+ /**
+ This map contains the association between a new added incidence
+ and the subresource it belongs to.
+ That's needed to return the correct mapping in subresourceIdentifier().
+
+ We can't trust on mUidMap here, because it contains only non-pending uids.
+ */
+ 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< TQPair<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 {
+ TemporarySilencer( ResourceKolab *_resource )
+ {
+ resource = _resource;
+ oldValue = resource->mSilent;
+ resource->mSilent = true;
+ }
+ ~TemporarySilencer()
+ {
+ resource->mSilent = oldValue;
+ }
+ ResourceKolab *resource;
+ bool oldValue;
+};
+
+}
+
+#endif // KCAL_RESOURCEKOLAB_H
diff --git a/tderesources/kolab/kcal/resourcekolab_plugin.cpp b/tderesources/kolab/kcal/resourcekolab_plugin.cpp
new file mode 100644
index 000000000..eb56897ae
--- /dev/null
+++ b/tderesources/kolab/kcal/resourcekolab_plugin.cpp
@@ -0,0 +1,49 @@
+/*
+ This file is part of libkabc and/or kaddressbook.
+ Copyright (c) 2002 - 2004 Klarälvdalens Datakonsult AB
+ <info@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"
+
+class KolabFactory : public KRES::PluginFactoryBase
+{
+public:
+ KRES::Resource *resource( const TDEConfig *config )
+ {
+ return new KCal::ResourceKolab( config );
+ }
+
+ KRES::ConfigWidget *configWidget( TQWidget* )
+ {
+ return 0;
+ }
+};
+
+K_EXPORT_COMPONENT_FACTORY(kcal_kolab,KolabFactory)
diff --git a/tderesources/kolab/kcal/task.cpp b/tderesources/kolab/kcal/task.cpp
new file mode 100644
index 000000000..fe75845db
--- /dev/null
+++ b/tderesources/kolab/kcal/task.cpp
@@ -0,0 +1,461 @@
+/*
+ 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>
+
+ 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 "task.h"
+
+#include <libkcal/todo.h>
+#include <kdebug.h>
+
+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, TQ_UINT32 sernum )
+{
+ Task task( res, subResource, sernum, tz );
+ task.load( xml );
+ KCal::Todo* todo = new KCal::Todo();
+ task.saveTo( todo );
+ return todo;
+}
+
+TQString Task::taskToXML( KCal::Todo* todo, const TQString& tz )
+{
+ Task task( 0, TQString(), 0, tz, todo );
+ return task.saveXML();
+}
+
+Task::Task( KCal::ResourceKolab *res, const TQString &subResource, TQ_UINT32 sernum,
+ const TQString& tz, KCal::Todo* task )
+ : Incidence( res, subResource, sernum, tz ),
+ mPriority( 5 ), mPercentCompleted( 0 ),
+ mStatus( KCal::Incidence::StatusNone ),
+ mHasStartDate( false ), mHasDueDate( false ),
+ mHasCompletedDate( false )
+{
+ if ( task )
+ setFields( task );
+}
+
+Task::~Task()
+{
+}
+
+void Task::setPriority( int priority )
+{
+ mPriority = priority;
+}
+
+int Task::priority() const
+{
+ return mPriority;
+}
+
+void Task::setPercentCompleted( int percent )
+{
+ mPercentCompleted = percent;
+}
+
+int Task::percentCompleted() const
+{
+ return mPercentCompleted;
+}
+
+void Task::setStatus( KCal::Incidence::Status status )
+{
+ mStatus = status;
+}
+
+KCal::Incidence::Status Task::status() const
+{
+ return mStatus;
+}
+
+void Task::setParent( const TQString& parentUid )
+{
+ mParent = parentUid;
+}
+
+TQString Task::parent() const
+{
+ return mParent;
+}
+
+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
+{
+ return mDueDate;
+}
+
+void Task::setHasStartDate( bool v )
+{
+ mHasStartDate = v;
+}
+
+bool Task::hasStartDate() const
+{
+ return mHasStartDate;
+}
+
+bool Task::hasDueDate() const
+{
+ return mHasDueDate;
+}
+
+void Task::setCompletedDate( const TQDateTime& date )
+{
+ mCompletedDate = date;
+ mHasCompletedDate = true;
+}
+
+TQDateTime Task::completedDate() const
+{
+ return mCompletedDate;
+}
+
+bool Task::hasCompletedDate() const
+{
+ return mHasCompletedDate;
+}
+
+bool Task::loadAttribute( TQDomElement& element )
+{
+ TQString tagName = element.tagName();
+
+ if ( tagName == "priority" ) {
+ bool ok;
+ 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 );
+ if ( !ok || percent < 0 || percent > 100 )
+ percent = 0;
+ setPercentCompleted( percent );
+ } else if ( tagName == "status" ) {
+ if ( element.text() == "in-progress" )
+ setStatus( KCal::Incidence::StatusInProcess );
+ else if ( element.text() == "completed" )
+ setStatus( KCal::Incidence::StatusCompleted );
+ else if ( element.text() == "waiting-on-someone-else" )
+ setStatus( KCal::Incidence::StatusNeedsAction );
+ else if ( element.text() == "deferred" )
+ // Guessing a status here
+ setStatus( KCal::Incidence::StatusCanceled );
+ else
+ // Default
+ setStatus( KCal::Incidence::StatusNone );
+ } else if ( tagName == "due-date" ) {
+ setDueDate( element.text() );
+ } else if ( tagName == "parent" ) {
+ setParent( element.text() );
+ } else if ( tagName == "x-completed-date" ) {
+ setCompletedDate( stringToDateTime( element.text() ) );
+ } else if ( tagName == "start-date" ) {
+ setHasStartDate( true );
+ setStartDate( element.text() );
+ } else
+ return Incidence::loadAttribute( element );
+
+ // We handled this
+ return true;
+}
+
+bool Task::saveAttributes( TQDomElement& element ) const
+{
+ // Save the base class elements
+ Incidence::saveAttributes( element );
+
+ // 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() ) {
+ case KCal::Incidence::StatusInProcess:
+ writeString( element, "status", "in-progress" );
+ break;
+ case KCal::Incidence::StatusCompleted:
+ writeString( element, "status", "completed" );
+ break;
+ case KCal::Incidence::StatusNeedsAction:
+ writeString( element, "status", "waiting-on-someone-else" );
+ break;
+ case KCal::Incidence::StatusCanceled:
+ writeString( element, "status", "deferred" );
+ break;
+ case KCal::Incidence::StatusNone:
+ writeString( element, "status", "not-started" );
+ break;
+ case KCal::Incidence::StatusTentative:
+ case KCal::Incidence::StatusConfirmed:
+ case KCal::Incidence::StatusDraft:
+ case KCal::Incidence::StatusFinal:
+ case KCal::Incidence::StatusX:
+ // All of these are saved as StatusNone.
+ writeString( element, "status", "not-started" );
+ break;
+ }
+
+ if ( hasDueDate() ) {
+ if ( mFloatingStatus == HasTime ) {
+ writeString( element, "due-date", dateTimeToString( dueDate() ) );
+ } else {
+ writeString( element, "due-date", dateToString( dueDate().date() ) );
+ }
+ }
+
+ if ( !parent().isNull() ) {
+ writeString( element, "parent", parent() );
+ }
+
+ if ( hasCompletedDate() && percentCompleted() == 100 ) {
+ writeString( element, "x-completed-date", dateTimeToString( completedDate() ) );
+ }
+
+ return true;
+}
+
+
+bool Task::loadXML( const TQDomDocument& document )
+{
+ mKolabPriorityFromDom = -1;
+ mKCalPriorityFromDom = -1;
+
+ TQDomElement top = document.documentElement();
+
+ if ( top.tagName() != "task" ) {
+ tqWarning( "XML error: Top tag was %s instead of the expected task",
+ top.tagName().ascii() );
+ return false;
+ }
+ setHasStartDate( false ); // todo's don't necessarily have one
+
+ for ( TQDomNode n = top.firstChild(); !n.isNull(); n = n.nextSibling() ) {
+ if ( n.isComment() )
+ continue;
+ if ( n.isElement() ) {
+ TQDomElement e = n.toElement();
+ if ( !loadAttribute( e ) )
+ // TODO: Unhandled tag - save for later storage
+ kdDebug() << "Warning: Unhandled tag " << e.tagName() << endl;
+ } else
+ kdDebug() << "Node is not a comment or an element???" << endl;
+ }
+
+ loadAttachments();
+ decideAndSetPriority();
+ return true;
+}
+
+TQString Task::saveXML() const
+{
+ TQDomDocument document = domTree();
+ TQDomElement element = document.createElement( "task" );
+ element.setAttribute( "version", "1.0" );
+ saveAttributes( element );
+ 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.
+ TQDomNodeList l = element.elementsByTagName( "start-date" );
+ Q_ASSERT( l.count() == 1 );
+ element.removeChild( l.item( 0 ) );
+ }
+ document.appendChild( element );
+ return document.toString();
+}
+
+void Task::setFields( const KCal::Todo* task )
+{
+ Incidence::setFields( task );
+
+ setPriority( task->priority() );
+ setPercentCompleted( task->percentComplete() );
+ setStatus( task->status() );
+ setHasStartDate( task->hasStartDate() );
+
+ if ( task->hasDueDate() ) {
+ setDueDate( localToUTC( task->dtDue() ) );
+ 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() ) {
+ setParent( task->relatedTo()->uid() );
+ } else if ( !task->relatedToUid().isEmpty() ) {
+ setParent( task->relatedToUid( ) );
+ } else {
+ setParent( TQString() );
+ }
+
+ if ( task->hasCompletedDate() && task->percentComplete() == 100 ) {
+ setCompletedDate( localToUTC( task->completed() ) );
+ } 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 )
+{
+ Incidence::saveTo( task );
+
+ task->setPriority( priority() );
+ task->setPercentComplete( percentCompleted() );
+ task->setStatus( status() );
+ task->setHasStartDate( hasStartDate() );
+ task->setHasDueDate( hasDueDate() );
+ if ( hasDueDate() )
+ task->setDtDue( utcToLocal( dueDate() ) );
+
+ if ( !parent().isNull() )
+ task->setRelatedToUid( parent() );
+
+ if ( hasCompletedDate() && task->percentComplete() == 100 )
+ task->setCompleted( utcToLocal( mCompletedDate ) );
+}
diff --git a/tderesources/kolab/kcal/task.h b/tderesources/kolab/kcal/task.h
new file mode 100644
index 000000000..38a12a70e
--- /dev/null
+++ b/tderesources/kolab/kcal/task.h
@@ -0,0 +1,143 @@
+/*
+ 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>
+
+ 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.
+*/
+
+#ifndef KOLAB_TASK_H
+#define KOLAB_TASK_H
+
+#include <incidence.h>
+
+#include <libkcal/incidence.h>
+
+class TQDomElement;
+
+namespace KCal {
+ class Todo;
+ class ResourceKolab;
+}
+
+namespace Kolab {
+
+/**
+ * This class represents a task, and knows how to load/save it
+ * from/to XML, and from/to a KCal::Todo.
+ * The instances of this class are temporary, only used to convert
+ * one to the other.
+ */
+class Task : public Incidence {
+public:
+ /// Use this to parse an xml string to a task entry
+ /// The caller is responsible for deleting the returned task
+ static KCal::Todo* xmlToTask( const TQString& xml, const TQString& tz, KCal::ResourceKolab *res = 0,
+ const TQString& subResource = TQString(), TQ_UINT32 sernum = 0 );
+
+ /// Use this to get an xml string describing this task entry
+ static TQString taskToXML( KCal::Todo*, const TQString& tz );
+
+ explicit Task( KCal::ResourceKolab *res, const TQString& subResource, TQ_UINT32 sernum,
+ const TQString& tz, KCal::Todo* todo = 0 );
+ virtual ~Task();
+
+ virtual TQString type() const { return "Task"; }
+
+ void saveTo( KCal::Todo* todo );
+
+ virtual void setPriority( int priority );
+ virtual int priority() const;
+
+ virtual void setPercentCompleted( int percent );
+ virtual int percentCompleted() const;
+
+ virtual void setStatus( KCal::Incidence::Status status );
+ virtual KCal::Incidence::Status status() const;
+
+ virtual void setParent( const TQString& parentUid );
+ virtual TQString parent() const;
+
+ virtual void setHasStartDate( bool );
+ virtual bool hasStartDate() const;
+
+ 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;
+
+ virtual void setCompletedDate( const TQDateTime& date );
+ virtual TQDateTime completedDate() const;
+ virtual bool hasCompletedDate() const;
+
+ // Load the attributes of this class
+ virtual bool loadAttribute( TQDomElement& );
+
+ // Save the attributes of this class
+ virtual bool saveAttributes( TQDomElement& ) const;
+
+ // Load this task by reading the XML file
+ virtual bool loadXML( const TQDomDocument& xml );
+
+ // Serialize this task to an XML string
+ virtual TQString saveXML() const;
+
+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;
+
+ bool mHasStartDate;
+
+ bool mHasDueDate;
+ TQDateTime mDueDate;
+
+ bool mHasCompletedDate;
+ TQDateTime mCompletedDate;
+};
+
+}
+
+#endif // KOLAB_TASK_H