summaryrefslogtreecommitdiffstats
path: root/kresources/kolab/kcal
diff options
context:
space:
mode:
Diffstat (limited to 'kresources/kolab/kcal')
-rw-r--r--kresources/kolab/kcal/Makefile.am27
-rw-r--r--kresources/kolab/kcal/event.cpp220
-rw-r--r--kresources/kolab/kcal/event.h102
-rw-r--r--kresources/kolab/kcal/incidence.cpp845
-rw-r--r--kresources/kolab/kcal/incidence.h174
-rw-r--r--kresources/kolab/kcal/journal.cpp184
-rw-r--r--kresources/kolab/kcal/journal.h103
-rw-r--r--kresources/kolab/kcal/kolab.desktop53
-rw-r--r--kresources/kolab/kcal/resourcekolab.cpp1153
-rw-r--r--kresources/kolab/kcal/resourcekolab.h237
-rw-r--r--kresources/kolab/kcal/resourcekolab_plugin.cpp49
-rw-r--r--kresources/kolab/kcal/task.cpp336
-rw-r--r--kresources/kolab/kcal/task.h129
13 files changed, 3612 insertions, 0 deletions
diff --git a/kresources/kolab/kcal/Makefile.am b/kresources/kolab/kcal/Makefile.am
new file mode 100644
index 000000000..bc0af8ddf
--- /dev/null
+++ b/kresources/kolab/kcal/Makefile.am
@@ -0,0 +1,27 @@
+METASOURCES = AUTO
+
+INCLUDES = -I$(top_srcdir)/kresources/kolab/shared -I$(top_srcdir) \
+ -I$(top_builddir)/libkdepim $(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)/kresources/kolab/shared/libresourcekolabshared.la \
+ -lkresources
+
+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)/kresources/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/kresources/kolab/kcal/event.cpp b/kresources/kolab/kcal/event.cpp
new file mode 100644
index 000000000..a53ca4162
--- /dev/null
+++ b/kresources/kolab/kcal/event.cpp
@@ -0,0 +1,220 @@
+/*
+ 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 Qt library by Trolltech AS, Norway (or with modified versions
+ of Qt that use the same license as Qt), 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
+ Qt. 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 QString& xml, const QString& tz, KCal::ResourceKolab* res,
+ const QString& subResource, Q_UINT32 sernum )
+{
+ Event event( res, subResource, sernum, tz );
+ event.load( xml );
+ KCal::Event* kcalEvent = new KCal::Event();
+ event.saveTo( kcalEvent );
+ return kcalEvent;
+}
+
+QString Event::eventToXML( KCal::Event* kcalEvent, const QString& tz )
+{
+ Event event( 0, QString::null, 0, tz, kcalEvent );
+ return event.saveXML();
+}
+
+Event::Event( KCal::ResourceKolab *res, const QString &subResource, Q_UINT32 sernum,
+ const QString& 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 QDateTime& 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 QDate& 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 QString& endDate )
+{
+ if ( endDate.length() > 10 )
+ // This is a date + time
+ setEndDate( stringToDateTime( endDate ) );
+ else
+ // This is only a date
+ setEndDate( stringToDate( endDate ) );
+}
+
+QDateTime Event::endDate() const
+{
+ return mEndDate;
+}
+
+bool Event::loadAttribute( QDomElement& element )
+{
+ // This method doesn't handle the color-label tag yet
+ QString 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( QDomElement& 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 QDomDocument& document )
+{
+ QDomElement top = document.documentElement();
+
+ if ( top.tagName() != "event" ) {
+ qWarning( "XML error: Top tag was %s instead of the expected event",
+ top.tagName().ascii() );
+ return false;
+ }
+
+ for ( QDomNode n = top.firstChild(); !n.isNull(); n = n.nextSibling() ) {
+ if ( n.isComment() )
+ continue;
+ if ( n.isElement() ) {
+ QDomElement e = n.toElement();
+ loadAttribute( e );
+ } else
+ kdDebug() << "Node is not a comment or an element???" << endl;
+ }
+
+ loadAttachments();
+ return true;
+}
+
+QString Event::saveXML() const
+{
+ QDomDocument document = domTree();
+ QDomElement 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 );
+
+ if ( event->hasEndDate() ) {
+ 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/kresources/kolab/kcal/event.h b/kresources/kolab/kcal/event.h
new file mode 100644
index 000000000..ad33db520
--- /dev/null
+++ b/kresources/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 Qt library by Trolltech AS, Norway (or with modified versions
+ of Qt that use the same license as Qt), 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
+ Qt. 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 QDomElement;
+
+
+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 QString& xml, const QString& tz, KCal::ResourceKolab* res = 0,
+ const QString& subResource = QString::null, Q_UINT32 sernum = 0 );
+
+ /// Use this to get an xml string describing this event entry
+ static QString eventToXML( KCal::Event*, const QString& tz );
+
+ /// Create a event object and
+ explicit Event( KCal::ResourceKolab *res, const QString &subResource, Q_UINT32 sernum,
+ const QString& tz, KCal::Event* event = 0 );
+ virtual ~Event();
+
+ void saveTo( KCal::Event* event );
+
+ virtual QString type() const { return "Event"; }
+
+ virtual void setTransparency( KCal::Event::Transparency transparency );
+ virtual KCal::Event::Transparency transparency() const;
+
+ virtual void setEndDate( const QDateTime& date );
+ virtual void setEndDate( const QDate& date );
+ virtual void setEndDate( const QString& date );
+ virtual QDateTime endDate() const;
+
+ // Load the attributes of this class
+ virtual bool loadAttribute( QDomElement& );
+
+ // Save the attributes of this class
+ virtual bool saveAttributes( QDomElement& ) const;
+
+ // Load this event by reading the XML file
+ virtual bool loadXML( const QDomDocument& xml );
+
+ // Serialize this event to an XML string
+ virtual QString saveXML() const;
+
+protected:
+ // Read all known fields from this ical incidence
+ void setFields( const KCal::Event* );
+
+ KCal::Event::Transparency mShowTimeAs;
+ QDateTime mEndDate;
+ bool mHasEndDate;
+};
+
+}
+
+#endif // KOLAB_EVENT_H
diff --git a/kresources/kolab/kcal/incidence.cpp b/kresources/kolab/kcal/incidence.cpp
new file mode 100644
index 000000000..8c74e3bdf
--- /dev/null
+++ b/kresources/kolab/kcal/incidence.cpp
@@ -0,0 +1,845 @@
+/*
+ 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 Qt library by Trolltech AS, Norway (or with modified versions
+ of Qt that use the same license as Qt), 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
+ Qt. 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 <qfile.h>
+#include <qvaluelist.h>
+
+#include <libkcal/journal.h>
+#include <korganizer/version.h>
+#include <kdebug.h>
+#include <kmdcodec.h>
+#include <kurl.h>
+#include <kio/netaccess.h>
+
+using namespace Kolab;
+
+
+Incidence::Incidence( KCal::ResourceKolab *res, const QString &subResource, Q_UINT32 sernum,
+ const QString& tz )
+ : KolabBase( tz ), mFloatingStatus( Unset ), mHasAlarm( false ),
+ mRevision( 0 ),
+ mResource( res ),
+ mSubResource( subResource ),
+ mSernum( sernum )
+{
+}
+
+Incidence::~Incidence()
+{
+}
+
+void Incidence::setSummary( const QString& summary )
+{
+ mSummary = summary;
+}
+
+QString Incidence::summary() const
+{
+ return mSummary;
+}
+
+void Incidence::setLocation( const QString& location )
+{
+ mLocation = location;
+}
+
+QString Incidence::location() const
+{
+ return mLocation;
+}
+
+void Incidence::setOrganizer( const Email& organizer )
+{
+ mOrganizer = organizer;
+}
+
+KolabBase::Email Incidence::organizer() const
+{
+ return mOrganizer;
+}
+
+void Incidence::setStartDate( const QDateTime& 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 QDate& 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 QString& startDate )
+{
+ if ( startDate.length() > 10 )
+ // This is a date + time
+ setStartDate( stringToDateTime( startDate ) );
+ else
+ // This is only a date
+ setStartDate( stringToDate( startDate ) );
+}
+
+QDateTime 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 );
+}
+
+QValueList<Incidence::Attendee>& Incidence::attendees()
+{
+ return mAttendees;
+}
+
+const QValueList<Incidence::Attendee>& Incidence::attendees() const
+{
+ return mAttendees;
+}
+
+void Incidence::setInternalUID( const QString& iuid )
+{
+ mInternalUID = iuid;
+}
+
+QString Incidence::internalUID() const
+{
+ return mInternalUID;
+}
+
+void Incidence::setRevision( int revision )
+{
+ mRevision = revision;
+}
+
+int Incidence::revision() const
+{
+ return mRevision;
+}
+
+bool Incidence::loadAttendeeAttribute( QDomElement& element,
+ Attendee& attendee )
+{
+ for ( QDomNode n = element.firstChild(); !n.isNull(); n = n.nextSibling() ) {
+ if ( n.isComment() )
+ continue;
+ if ( n.isElement() ) {
+ QDomElement e = n.toElement();
+ QString tagName = e.tagName();
+
+ if ( tagName == "display-name" )
+ attendee.displayName = e.text();
+ 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( QDomElement& element,
+ const Attendee& attendee ) const
+{
+ QDomElement 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( QDomElement& element ) const
+{
+ QValueList<Attendee>::ConstIterator it = mAttendees.begin();
+ for ( ; it != mAttendees.end(); ++it )
+ saveAttendeeAttribute( element, *it );
+}
+
+void Incidence::saveAttachments( QDomElement& 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::saveRecurrence( QDomElement& element ) const
+{
+ QDomElement 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", QString::number( mRecurrence.interval ) );
+ for( QStringList::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() ) {
+ QDomElement range = element.ownerDocument().createElement( "range" );
+ e.appendChild( range );
+ range.setAttribute( "type", mRecurrence.rangeType );
+ QDomText t = element.ownerDocument().createTextNode( mRecurrence.range );
+ range.appendChild( t );
+ }
+ for( QValueList<QDate>::ConstIterator it = mRecurrence.exclusions.begin();
+ it != mRecurrence.exclusions.end(); ++it ) {
+ writeString( e, "exclusion", dateToString( *it ) );
+ }
+}
+
+void Incidence::loadRecurrence( const QDomElement& element )
+{
+ mRecurrence.interval = 0;
+ mRecurrence.cycle = element.attribute( "cycle" );
+ mRecurrence.type = element.attribute( "type" );
+ for ( QDomNode n = element.firstChild(); !n.isNull(); n = n.nextSibling() ) {
+ if ( n.isComment() )
+ continue;
+ if ( n.isElement() ) {
+ QDomElement e = n.toElement();
+ QString tagName = e.tagName();
+
+ if ( tagName == "interval" )
+ 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;
+ }
+ }
+}
+
+bool Incidence::loadAttribute( QDomElement& element )
+{
+ QString 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 == "x-kde-internaluid" )
+ setInternalUID( element.text() );
+ else if ( tagName == "revision" ) {
+ bool ok;
+ int revision = element.text().toInt( &ok );
+ if ( ok )
+ setRevision( revision );
+ } else if ( tagName == "x-custom" )
+ 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 = QCString( "X-KDE-KolabUnhandled-" ) + element.tagName().latin1();
+ c.value = element.text();
+ mCustomList.append( c );
+ }
+ }
+ // We handled this
+ return true;
+}
+
+bool Incidence::saveAttributes( QDomElement& 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 = qRound( -alarm() );
+ writeString( element, "alarm", QString::number( alarmTime ) );
+ }
+ writeString( element, "x-kde-internaluid", internalUID() );
+ writeString( element, "revision", QString::number( revision() ) );
+ saveCustomAttributes( element );
+ return true;
+}
+
+void Incidence::saveCustomAttributes( QDomElement& element ) const
+{
+ QValueList<Custom>::ConstIterator it = mCustomList.begin();
+ for ( ; it != mCustomList.end(); ++it ) {
+ QString key = (*it).key;
+ Q_ASSERT( !key.isEmpty() );
+ if ( key.startsWith( "X-KDE-KolabUnhandled-" ) ) {
+ key = key.mid( strlen( "X-KDE-KolabUnhandled-" ) );
+ writeString( element, key, (*it).value );
+ } else {
+ // Let's use attributes so that other tag-preserving-code doesn't need sub-elements
+ QDomElement e = element.ownerDocument().createElement( "x-custom" );
+ element.appendChild( e );
+ e.setAttribute( "key", key );
+ e.setAttribute( "value", (*it).value );
+ }
+ }
+}
+
+void Incidence::loadCustomAttributes( QDomElement& element )
+{
+ Custom custom;
+ custom.key = element.attribute( "key" ).latin1();
+ custom.value = element.attribute( "value" );
+ mCustomList.append( custom );
+}
+
+static KCal::Attendee::PartStat attendeeStringToStatus( const QString& s )
+{
+ if ( s == "none" )
+ return KCal::Attendee::NeedsAction;
+ if ( s == "tentative" )
+ return KCal::Attendee::Tentative;
+ if ( s == "declined" )
+ return KCal::Attendee::Declined;
+ if ( s == "delegated" )
+ return KCal::Attendee::Delegated;
+
+ // Default:
+ return KCal::Attendee::Accepted;
+}
+
+static QString 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 QString& s )
+{
+ if ( s == "optional" )
+ return KCal::Attendee::OptParticipant;
+ if ( s == "resource" )
+ return KCal::Attendee::NonParticipant;
+ return KCal::Attendee::ReqParticipant;
+}
+
+static QString 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";
+ {
+ QBitArray 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";
+ QValueList<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 = QString::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";
+ QValueList<int> monthDays = recur->monthDays();
+ // ####### Kolab XML limitation: only the first month day is used
+ if ( !monthDays.isEmpty() )
+ mRecurrence.dayNumber = QString::number( monthDays.first() );
+ break;
+ }
+ case KCal::Recurrence::rYearlyMonth: // (day n of Month Y)
+ {
+ mRecurrence.cycle = "yearly";
+ mRecurrence.type = "monthday";
+ QValueList<int> rmd = recur->yearDates();
+ int day = !rmd.isEmpty() ? rmd.first() : recur->startDate().day();
+ mRecurrence.dayNumber = QString::number( day );
+ QValueList<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 = QString::number( recur->yearDays().first() );
+ break;
+ case KCal::Recurrence::rYearlyPos: // (weekday X of week N of month Y)
+ mRecurrence.cycle = "yearly";
+ mRecurrence.type = "weekday";
+ QValueList<int> months = recur->yearMonths();
+ if ( !months.isEmpty() )
+ mRecurrence.month = s_monthName[ months.first() - 1 ]; // #### Kolab XML limitation: only one month specified
+ QValueList<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 = QString::number( monthPos.pos() );
+ mRecurrence.days.append( s_weekDayName[ monthPos.day()-1 ] );
+
+ //mRecurrence.dayNumber = QString::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 = QString::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 );
+ }
+
+ 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( QString::null );
+ } 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 QMap<QCString, QString> map = incidence->customProperties();
+ QMap<QCString, QString>::ConstIterator cit = map.begin();
+ for ( ; cit != map.end() ; ++cit ) {
+ Custom c;
+ c.key = cit.key();
+ c.value = cit.data();
+ mCustomList.append( c );
+ }
+}
+
+static QBitArray daysListToBitArray( const QStringList& days )
+{
+ QBitArray arr( 7 );
+ arr.fill( false );
+ for( QStringList::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 ) {
+ KCal::Alarm* alarm = incidence->newAlarm();
+ alarm->setStartOffset( qRound( mAlarm * 60.0 ) );
+ alarm->setEnabled( true );
+ }
+
+ if ( organizer().displayName.isEmpty() )
+ incidence->setOrganizer( organizer().smtpAddress );
+ else
+ incidence->setOrganizer( organizer().displayName + "<"
+ + organizer().smtpAddress + ">" );
+
+ incidence->clearAttendees();
+ QValueList<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" ) {
+ QBitArray 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( QValueList<Custom>::ConstIterator it = mCustomList.begin(); it != mCustomList.end(); ++it ) {
+ incidence->setNonKDECustomProperty( (*it).key, (*it).value );
+ }
+
+}
+
+void Incidence::loadAttachments()
+{
+ QStringList attachments;
+ if ( mResource->kmailListAttachments( attachments, mSubResource, mSernum ) ) {
+ for ( QStringList::ConstIterator it = attachments.constBegin(); it != attachments.constEnd(); ++it ) {
+ QByteArray data;
+ KURL url;
+ if ( mResource->kmailGetAttachment( url, mSubResource, mSernum, *it ) && !url.isEmpty() ) {
+ QFile f( url.path() );
+ if ( f.open( IO_ReadOnly ) ) {
+ data = f.readAll();
+ QString 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();
+ }
+ }
+ }
+}
+
+QString Incidence::productID() const
+{
+ return QString( "KOrganizer " ) + korgVersion + ", Kolab resource";
+}
+
+// Unhandled KCal::Incidence fields:
+// revision, status (unused), priority (done in tasks), attendee.uid,
+// mComments, mReadOnly
+
diff --git a/kresources/kolab/kcal/incidence.h b/kresources/kolab/kcal/incidence.h
new file mode 100644
index 000000000..de549328e
--- /dev/null
+++ b/kresources/kolab/kcal/incidence.h
@@ -0,0 +1,174 @@
+/*
+ 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 Qt library by Trolltech AS, Norway (or with modified versions
+ of Qt that use the same license as Qt), 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
+ Qt. 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 QDomElement;
+
+namespace KCal {
+ class Incidence;
+ class Recurrence;
+ 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 {
+ QString cycle;
+ QString type;
+ int interval;
+ QStringList days; // list of days-of-the-week
+ QString dayNumber;
+ QString month;
+ QString rangeType;
+ QString range; // date or number or nothing
+ QValueList<QDate> exclusions;
+ };
+
+ struct Attendee : Email {
+ Attendee() : requestResponse( true ), invitationSent( false ) {}
+ QString status;
+ bool requestResponse;
+ bool invitationSent;
+ QString role;
+ QString delegate;
+ QString delegator;
+ };
+
+ explicit Incidence( KCal::ResourceKolab *res, const QString &subResource, Q_UINT32 sernum,
+ const QString& tz );
+ virtual ~Incidence();
+
+ void saveTo( KCal::Incidence* incidence );
+
+ virtual void setSummary( const QString& summary );
+ virtual QString summary() const;
+
+ virtual void setLocation( const QString& location );
+ virtual QString location() const;
+
+ virtual void setOrganizer( const Email& organizer );
+ virtual Email organizer() const;
+
+ virtual void setStartDate( const QDateTime& startDate );
+ virtual void setStartDate( const QDate& startDate );
+ virtual void setStartDate( const QString& startDate );
+ virtual QDateTime 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 );
+ QValueList<Attendee>& attendees();
+ const QValueList<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 QString& iuid );
+ QString internalUID() const;
+
+ virtual void setRevision( int );
+ virtual int revision() const;
+
+ // Load the attributes of this class
+ virtual bool loadAttribute( QDomElement& );
+
+ // Save the attributes of this class
+ virtual bool saveAttributes( QDomElement& ) const;
+
+protected:
+ enum FloatingStatus { Unset, AllDay, HasTime };
+
+ // Read all known fields from this ical incidence
+ void setFields( const KCal::Incidence* );
+
+ bool loadAttendeeAttribute( QDomElement&, Attendee& );
+ void saveAttendeeAttribute( QDomElement& element,
+ const Attendee& attendee ) const;
+ void saveAttendees( QDomElement& element ) const;
+ void saveAttachments( QDomElement& element ) const;
+
+ void loadRecurrence( const QDomElement& element );
+ void saveRecurrence( QDomElement& element ) const;
+ void saveCustomAttributes( QDomElement& element ) const;
+ void loadCustomAttributes( QDomElement& element );
+
+ void loadAttachments();
+
+ QString productID() const;
+
+ QString mSummary;
+ QString mLocation;
+ Email mOrganizer;
+ QDateTime mStartDate;
+ FloatingStatus mFloatingStatus;
+ float mAlarm;
+ bool mHasAlarm;
+ Recurrence mRecurrence;
+ QValueList<Attendee> mAttendees;
+ QValueList<KCal::Attachment*> mAttachments;
+ QString mInternalUID;
+ int mRevision;
+
+ struct Custom {
+ QCString key;
+ QString value;
+ };
+ QValueList<Custom> mCustomList;
+
+ KCal::ResourceKolab *mResource;
+ QString mSubResource;
+ Q_UINT32 mSernum;
+};
+
+}
+
+#endif // KOLAB_INCIDENCE_H
diff --git a/kresources/kolab/kcal/journal.cpp b/kresources/kolab/kcal/journal.cpp
new file mode 100644
index 000000000..45b91587d
--- /dev/null
+++ b/kresources/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 Qt library by Trolltech AS, Norway (or with modified versions
+ of Qt that use the same license as Qt), 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
+ Qt. 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 QString& xml, const QString& tz )
+{
+ Journal journal( tz );
+ journal.load( xml );
+ KCal::Journal* kcalJournal = new KCal::Journal();
+ journal.saveTo( kcalJournal );
+ return kcalJournal;
+}
+
+QString Journal::journalToXML( KCal::Journal* kcalJournal, const QString& tz )
+{
+ Journal journal( tz, kcalJournal );
+ return journal.saveXML();
+}
+
+Journal::Journal( const QString& tz, KCal::Journal* journal )
+ : KolabBase( tz )
+{
+ if ( journal )
+ setFields( journal );
+}
+
+Journal::~Journal()
+{
+}
+
+void Journal::setSummary( const QString& summary )
+{
+ mSummary = summary;
+}
+
+QString Journal::summary() const
+{
+ return mSummary;
+}
+
+void Journal::setStartDate( const QDateTime& startDate )
+{
+ mStartDate = startDate;
+}
+
+QDateTime Journal::startDate() const
+{
+ return mStartDate;
+}
+
+void Journal::setEndDate( const QDateTime& endDate )
+{
+ mEndDate = endDate;
+}
+
+QDateTime Journal::endDate() const
+{
+ return mEndDate;
+}
+
+bool Journal::loadAttribute( QDomElement& element )
+{
+ QString 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( QDomElement& 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 QDomDocument& document )
+{
+ QDomElement top = document.documentElement();
+
+ if ( top.tagName() != "journal" ) {
+ qWarning( "XML error: Top tag was %s instead of the expected Journal",
+ top.tagName().ascii() );
+ return false;
+ }
+
+ for ( QDomNode n = top.firstChild(); !n.isNull(); n = n.nextSibling() ) {
+ if ( n.isComment() )
+ continue;
+ if ( n.isElement() ) {
+ QDomElement e = n.toElement();
+ if ( !loadAttribute( e ) ) {
+ // Unhandled tag - save for later storage
+ //qDebug( "Unhandled tag: %s", e.toCString().data() );
+ }
+ } else
+ qDebug( "Node is not a comment or an element???" );
+ }
+
+ return true;
+}
+
+QString Journal::saveXML() const
+{
+ QDomDocument document = domTree();
+ QDomElement 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() ) );
+}
+
+QString Journal::productID() const
+{
+ return QString( "KOrganizer " ) + korgVersion + ", Kolab resource";
+}
diff --git a/kresources/kolab/kcal/journal.h b/kresources/kolab/kcal/journal.h
new file mode 100644
index 000000000..d8b09bb96
--- /dev/null
+++ b/kresources/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 Qt library by Trolltech AS, Norway (or with modified versions
+ of Qt that use the same license as Qt), 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
+ Qt. 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 QDomElement;
+
+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 QString& xml, const QString& tz );
+
+ /// Use this to get an xml string describing this journal entry
+ static QString journalToXML( KCal::Journal*, const QString& tz );
+
+ explicit Journal( const QString& tz, KCal::Journal* journal = 0 );
+ virtual ~Journal();
+
+ virtual QString type() const { return "Journal"; }
+
+ void saveTo( KCal::Journal* journal );
+
+ virtual void setSummary( const QString& summary );
+ virtual QString summary() const;
+
+ virtual void setStartDate( const QDateTime& startDate );
+ virtual QDateTime startDate() const;
+
+ virtual void setEndDate( const QDateTime& endDate );
+ virtual QDateTime endDate() const;
+
+ // Load the attributes of this class
+ virtual bool loadAttribute( QDomElement& );
+
+ // Save the attributes of this class
+ virtual bool saveAttributes( QDomElement& ) const;
+
+ // Load this journal by reading the XML file
+ virtual bool loadXML( const QDomDocument& xml );
+
+ // Serialize this journal to an XML string
+ virtual QString saveXML() const;
+
+protected:
+ // Read all known fields from this ical journal
+ void setFields( const KCal::Journal* );
+
+ QString productID() const;
+
+ QString mSummary;
+ QDateTime mStartDate;
+ QDateTime mEndDate;
+};
+
+}
+
+#endif // KOLAB_JOURNAL_H
diff --git a/kresources/kolab/kcal/kolab.desktop b/kresources/kolab/kcal/kolab.desktop
new file mode 100644
index 000000000..579e6406d
--- /dev/null
+++ b/kresources/kolab/kcal/kolab.desktop
@@ -0,0 +1,53 @@
+[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[ka]=კáƒáƒšáƒ”ნდáƒáƒ áƒ˜ IMAP სერვერზე KMail-ის სáƒáƒ¨áƒ£áƒáƒšáƒ”ბით
+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-KDE-Library=kcal_kolab
+Type=Service
+ServiceTypes=KResources/Plugin
+X-KDE-ResourceFamily=calendar
+X-KDE-ResourceType=imap
diff --git a/kresources/kolab/kcal/resourcekolab.cpp b/kresources/kolab/kcal/resourcekolab.cpp
new file mode 100644
index 000000000..3c1c80e10
--- /dev/null
+++ b/kresources/kolab/kcal/resourcekolab.cpp
@@ -0,0 +1,1153 @@
+/*
+ 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 Qt library by Trolltech AS, Norway (or with modified versions
+ of Qt that use the same license as Qt), 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
+ Qt. 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 <libkcal/icalformat.h>
+#include <libkdepim/kincidencechooser.h>
+#include <kabc/locknull.h>
+#include <kmainwindow.h>
+#include <klocale.h>
+#include <kinputdialog.h>
+#include <ktempfile.h>
+#include <kmdcodec.h>
+
+#include <qfile.h>
+#include <qobject.h>
+#include <qtimer.h>
+#include <qapplication.h>
+
+#include <assert.h>
+
+using namespace KCal;
+using namespace Kolab;
+
+static const char* kmailCalendarContentsType = "Calendar";
+static const char* kmailTodoContentsType = "Task";
+static const char* kmailJournalContentsType = "Journal";
+static const char* eventAttachmentMimeType = "application/x-vnd.kolab.event";
+static const char* todoAttachmentMimeType = "application/x-vnd.kolab.task";
+static const char* journalAttachmentMimeType = "application/x-vnd.kolab.journal";
+static const char* incidenceInlineMimeType = "text/calendar";
+
+
+ResourceKolab::ResourceKolab( const KConfig *config )
+ : ResourceCalendar( config ), ResourceKolabBase( "ResourceKolab-libkcal" ),
+ mCalendar( QString::fromLatin1("UTC") ), mOpen( false ),mResourceChangedTimer( 0,
+ "mResourceChangedTimer" )
+{
+ setType( "imap" );
+ connect( &mResourceChangedTimer, SIGNAL( timeout() ),
+ this, SLOT( slotEmitResourceChanged() ) );
+}
+
+ResourceKolab::~ResourceKolab()
+{
+ // The resource is deleted on exit (StdAddressBook's KStaticDeleter),
+ // and it wasn't closed before that, so close here to save the config.
+ if ( mOpen ) {
+ close();
+ }
+}
+
+void ResourceKolab::loadSubResourceConfig( KConfig& config,
+ const QString& name,
+ const QString& label,
+ bool writable,
+ bool alarmRelevant,
+ ResourceMap& subResource )
+{
+ KConfigGroup group( &config, name );
+ bool active = group.readBoolEntry( "Active", true );
+ subResource.insert( name, Kolab::SubResource( active, writable,
+ alarmRelevant, label ) );
+}
+
+bool ResourceKolab::openResource( KConfig& config, const char* contentType,
+ ResourceMap& map )
+{
+ // Read the subresource entries from KMail
+ QValueList<KMailICalIface::SubResource> subResources;
+ if ( !kmailSubresources( subResources, contentType ) )
+ return false;
+ map.clear();
+ QValueList<KMailICalIface::SubResource>::ConstIterator it;
+ for ( it = subResources.begin(); it != subResources.end(); ++it )
+ loadSubResourceConfig( config, (*it).location, (*it).label, (*it).writable,
+ (*it).alarmRelevant, map );
+ return true;
+}
+
+bool ResourceKolab::doOpen()
+{
+ if ( mOpen )
+ // Already open
+ return true;
+ mOpen = true;
+
+ KConfig config( configFile() );
+ config.setGroup( "General" );
+ mProgressDialogIncidenceLimit = config.readNumEntry("ProgressDialogIncidenceLimit", 200);
+
+ return openResource( config, kmailCalendarContentsType, mEventSubResources )
+ && openResource( config, kmailTodoContentsType, mTodoSubResources )
+ && openResource( config, kmailJournalContentsType, mJournalSubResources );
+}
+
+static void closeResource( KConfig& config, ResourceMap& map )
+{
+ ResourceMap::ConstIterator it;
+ for ( it = map.begin(); it != map.end(); ++it ) {
+ config.setGroup( it.key() );
+ config.writeEntry( "Active", it.data().active() );
+ }
+}
+
+void ResourceKolab::doClose()
+{
+ if ( !mOpen )
+ // Not open
+ return;
+ mOpen = false;
+
+ KConfig config( configFile() );
+ closeResource( config, mEventSubResources );
+ closeResource( config, mTodoSubResources );
+ closeResource( config, mJournalSubResources );
+}
+
+bool ResourceKolab::loadSubResource( const QString& 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 QString 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 = qApp && qApp->type() != QApplication::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 ) {
+ QMap<Q_UINT32, QString> 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( QMap<Q_UINT32, QString>::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() ) {
+ return true;
+ }
+ mUidMap.clear();
+
+ return loadAllEvents() & loadAllTodos() & loadAllJournals();
+}
+
+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 QCString& 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 QString& 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 QString uid = incidencebase->uid();
+ //kdDebug() << k_funcinfo << uid << endl;
+
+ if ( mUidsPendingUpdate.contains( uid ) || mUidsPendingAdding.contains( uid ) ) {
+ /* We are currently processing this event ( removing and readding or
+ * adding it ). If so, ignore this update. Keep the last of these around
+ * and process once we hear back from KMail on this event. */
+ mPendingUpdates.replace( uid, incidencebase );
+ return;
+ }
+
+ QString subResource;
+ Q_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( QDateTime::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 QString& subresource, Q_UINT32 sernum )
+{
+ if ( ! inc )
+ return;
+ if ( ! mResolveConflict ) {
+ // we should do no conflict resolution
+ delete inc;
+ return;
+ }
+ const QString 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, SIGNAL( useGlobalMode() ), ch, SLOT ( useGlobalMode() ) );
+ if ( ch->exec() )
+ if ( KIncidenceChooser::chooseMode != KIncidenceChooser::ask )
+ emit useGlobalMode() ;
+ }
+ result = ch->getIncidence();
+ delete ch;
+ }
+ } else {
+ // nothing there locally, just take the new one. Can't Happen (TM)
+ result = inc;
+ }
+ if ( result == local ) {
+ delete inc;
+ localIncidence = local;
+ } else if ( result == inc ) {
+ addedIncidence = inc;
+ } else if ( result == 0 ) { // take both
+ addedIncidence = inc;
+ addedIncidence->setSummary( i18n("Copy of: %1").arg( addedIncidence->summary() ) );
+ addedIncidence->setUid( CalFormat::createUniqueId() );
+ localIncidence = local;
+ }
+ bool silent = mSilent;
+ mSilent = false;
+ if ( !localIncidence ) {
+ deleteIncidence( local ); // remove local from kmail
+ }
+ mUidsPendingDeletion.append( origUid );
+ if ( addedIncidence ) {
+ sendKMailUpdate( addedIncidence, subresource, sernum );
+ } else {
+ kmailDeleteIncidence( subresource, sernum );// remove new from kmail
+ }
+ mSilent = silent;
+}
+void ResourceKolab::addIncidence( const char* mimetype, const QString& data,
+ const QString& subResource, Q_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 QString& subresource,
+ Q_UINT32 sernum )
+{
+ const QString& type = incidencebase->type();
+ const char* mimetype = 0;
+ QString 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();
+ QStringList attURLs, attMimeTypes, attNames;
+ QValueList<KTempFile*> tmpFiles;
+ for ( KCal::Attachment::List::ConstIterator it = atts.constBegin(); it != atts.constEnd(); ++it ) {
+ KTempFile* tempFile = new KTempFile;
+ QCString decoded = KCodecs::base64Decode( QCString( (*it)->data() ) );
+ tempFile->file()->writeBlock( decoded.data(), decoded.length() );
+ tempFile->close();
+ KURL url;
+ url.setPath( tempFile->name() );
+ attURLs.append( url.url() );
+ attMimeTypes.append( (*it)->mimeType() );
+ attNames.append( (*it)->label() );
+ }
+ QStringList deletedAtts;
+ if ( kmailListAttachments( deletedAtts, subresource, sernum ) ) {
+ for ( QStringList::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() );
+
+ QString 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( QValueList<KTempFile *>::Iterator it = tmpFiles.begin(); it != tmpFiles.end(); ++it ) {
+ (*it)->setAutoDelete( true );
+ delete (*it);
+ }
+
+ return rc;
+}
+
+bool ResourceKolab::addIncidence( KCal::Incidence* incidence, const QString& _subresource,
+ Q_UINT32 sernum )
+{
+ Q_ASSERT( incidence );
+ if ( !incidence ) return false;
+ QString uid = incidence->uid();
+ QString subResource = _subresource;
+
+ Kolab::ResourceMap *map = &mEventSubResources; // don't use a ref here!
+
+ const QString& 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 ) {
+ // Add a description of the incidence
+ QString text = "<b><font size=\"+1\">";
+ if ( incidence->type() == "Event" )
+ text += i18n( "Choose the folder where you want to store this event" );
+ else if ( incidence->type() == "Todo" )
+ 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>";
+ }
+ subResource = findWritableResource( *map, text );
+ }
+
+ if ( subResource.isEmpty() )
+ return false;
+
+ mNewIncidencesMap.insert( uid, subResource );
+
+ if ( !sendKMailUpdate( incidence, subResource, sernum ) ) {
+ kdError(5650) << "Communication problem in ResourceKolab::addIncidence()\n";
+ 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 ) {
+ mCalendar.addIncidence( incidence );
+ incidence->registerObserver( this );
+ }
+ }
+ } else { /* KMail told us */
+ bool ourOwnUpdate = mUidsPendingUpdate.contains( uid );
+ /* 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() ) {
+ 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();
+ }
+ }
+ /* 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 )
+{
+ if ( mUidMap.contains( event->uid() ) )
+ return true; //noop
+ else
+ return addIncidence( event, QString::null, 0 );
+}
+
+void ResourceKolab::addEvent( const QString& xml, const QString& subresource,
+ Q_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 QString 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 QString& 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 QDate& date,
+ EventSortField sortField,
+ SortDirection sortDirection )
+{
+ return mCalendar.rawEventsForDate( date, sortField, sortDirection );
+}
+
+KCal::Event::List ResourceKolab::rawEventsForDate( const QDateTime& qdt )
+{
+ return mCalendar.rawEventsForDate( qdt );
+}
+
+KCal::Event::List ResourceKolab::rawEvents( const QDate& start,
+ const QDate& end,
+ bool inclusive )
+{
+ return mCalendar.rawEvents( start, end, inclusive );
+}
+
+bool ResourceKolab::addTodo( KCal::Todo* todo )
+{
+ if ( mUidMap.contains( todo->uid() ) )
+ return true; //noop
+ else
+ return addIncidence( todo, QString::null, 0 );
+}
+
+void ResourceKolab::addTodo( const QString& xml, const QString& subresource,
+ Q_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 QString& 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 QDate& date )
+{
+ return mCalendar.rawTodosForDate( date );
+}
+
+bool ResourceKolab::addJournal( KCal::Journal* journal )
+{
+ if ( mUidMap.contains( journal->uid() ) )
+ return true; //noop
+ else
+ return addIncidence( journal, QString::null, 0 );
+}
+
+void ResourceKolab::addJournal( const QString& xml, const QString& subresource,
+ Q_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 QString& 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 QDate &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 QString &uid = a->parent()->uid();
+ if ( mUidMap.contains( uid ) ) {
+ const QString &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 QDateTime& from,
+ const QDateTime& to )
+{
+ return relevantAlarms( mCalendar.alarms( from, to ) );
+}
+
+KCal::Alarm::List ResourceKolab::alarmsTo( const QDateTime& to )
+{
+ return relevantAlarms( mCalendar.alarmsTo(to) );
+}
+
+void ResourceKolab::setTimeZoneId( const QString& tzid )
+{
+ mCalendar.setTimeZoneId( tzid );
+ mFormat.setTimeZone( mCalendar.timeZoneId(), !mCalendar.isLocalTime() );
+}
+
+bool ResourceKolab::fromKMailAddIncidence( const QString& type,
+ const QString& subResource,
+ Q_UINT32 sernum,
+ int format,
+ const QString& 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 )
+ rc = false;
+ else
+ addIncidence( inc, subResource, sernum );
+ }
+ return rc;
+}
+
+void ResourceKolab::fromKMailDelIncidence( const QString& type,
+ const QString& subResource,
+ const QString& 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 {
+ // We didn't trigger this, so KMail did, remove the reference to the uid
+ KCal::Incidence* incidence = mCalendar.incidence( uid );
+ if( incidence ) {
+ incidence->unRegisterObserver( this );
+ mCalendar.deleteIncidence( incidence );
+ }
+ mUidMap.remove( uid );
+ mResourceChangedTimer.changeInterval( 100 );
+ }
+}
+
+void ResourceKolab::fromKMailRefresh( const QString& type,
+ const QString& /*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 QString& type,
+ const QString& subResource,
+ const QString& label,
+ bool writable, bool alarmRelevant )
+{
+ ResourceMap* map = 0;
+ const char* mimetype = 0;
+ if ( type == kmailCalendarContentsType ) {
+ map = &mEventSubResources;
+ mimetype = eventAttachmentMimeType;
+ } else if ( type == kmailTodoContentsType ) {
+ map = &mTodoSubResources;
+ mimetype = todoAttachmentMimeType;
+ } else if ( type == kmailJournalContentsType ) {
+ map = &mJournalSubResources;
+ mimetype = journalAttachmentMimeType;
+ } else
+ // Not ours
+ return;
+
+ if ( map->contains( subResource ) )
+ // Already registered
+ return;
+
+ KConfig config( configFile() );
+ config.setGroup( subResource );
+
+ bool active = config.readBoolEntry( subResource, true );
+ (*map)[ subResource ] = Kolab::SubResource( active, writable,
+ alarmRelevant, label );
+ loadSubResource( subResource, mimetype );
+ emit signalSubresourceAdded( this, type, subResource, label );
+}
+
+void ResourceKolab::fromKMailDelSubresource( const QString& type,
+ const QString& subResource )
+{
+ ResourceMap* map = subResourceMap( type );
+ if ( !map ) // not ours
+ return;
+ if ( map->contains( subResource ) )
+ map->erase( subResource );
+ else
+ // Not registered
+ return;
+
+ // Delete from the config file
+ KConfig config( configFile() );
+ config.deleteGroup( subResource );
+ config.sync();
+
+ unloadSubResource( subResource );
+
+ emit signalSubresourceRemoved( this, type, subResource );
+}
+
+QStringList 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 QString
+ResourceKolab::labelForSubresource( const QString& 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 QMap<Q_UINT32, QString>& map,
+ const QString& type,
+ const QString& folder )
+{
+ TemporarySilencer t( this );
+ for( QMap<Q_UINT32, QString>::ConstIterator it = map.begin(); it != map.end(); ++it )
+ addIncidence( type.latin1(), it.data(), folder, it.key() );
+}
+
+bool ResourceKolab::subresourceActive( const QString& 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 QString &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 );
+ }
+}
+
+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 QString& 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 QString& resource, const QString& parent )
+{
+ kdDebug(5650) << "KCal Kolab resource - adding subresource: " << resource << endl;
+ QString 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 {
+ QStringList contentTypeChoices;
+ contentTypeChoices << i18n("Calendar") << i18n("Tasks") << i18n("Journals");
+ const QString caption = i18n("Which kind of subresource should this be?");
+ const QString choice = KInputDialog::getItem( caption, QString::null, 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 QString& resource )
+{
+ kdDebug(5650) << "KCal Kolab resource - removing subresource: " << resource << endl;
+ return kmailRemoveSubresource( resource );
+}
+
+/*virtual*/
+QString ResourceKolab::subresourceIdentifier( Incidence *incidence )
+{
+ QString uid = incidence->uid();
+ if ( mUidMap.contains( uid ) )
+ return mUidMap[ uid ].resource();
+ else
+ if ( mNewIncidencesMap.contains( uid ) )
+ return mNewIncidencesMap[ uid ];
+ else
+ return QString();
+}
+
+
+bool ResourceKolab::unloadSubResource( const QString& subResource )
+{
+ const bool silent = mSilent;
+ mSilent = true;
+ Kolab::UidMap::Iterator mapIt = mUidMap.begin();
+ 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 ) {
+ incidence->unRegisterObserver( this );
+ mCalendar.deleteIncidence( incidence );
+ }
+ mUidMap.remove( it );
+ }
+ mSilent = silent;
+ return true;
+}
+
+QString ResourceKolab::subresourceType( const QString &resource )
+{
+ if ( mEventSubResources.contains( resource ) )
+ return "event";
+ if ( mTodoSubResources.contains( resource ) )
+ return "todo";
+ if ( mJournalSubResources.contains( resource ) )
+ return "journal";
+ return QString();
+}
+
+#include "resourcekolab.moc"
diff --git a/kresources/kolab/kcal/resourcekolab.h b/kresources/kolab/kcal/resourcekolab.h
new file mode 100644
index 000000000..0974e4ada
--- /dev/null
+++ b/kresources/kolab/kcal/resourcekolab.h
@@ -0,0 +1,237 @@
+/*
+ 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 Qt library by Trolltech AS, Norway (or with modified versions
+ of Qt that use the same license as Qt), 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
+ Qt. 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 <qtimer.h>
+
+#include <kdepimmacros.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 KConfig* );
+ 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
+ bool addEvent( KCal::Event* anEvent );
+ bool deleteEvent( KCal::Event* );
+ KCal::Event* event( const QString &UniqueStr );
+ KCal::Event::List rawEvents( EventSortField sortField = EventSortUnsorted, SortDirection sortDirection = SortDirectionAscending );
+ KCal::Event::List rawEventsForDate(
+ const QDate& date,
+ EventSortField sortField=EventSortUnsorted,
+ SortDirection sortDirection=SortDirectionAscending );
+ KCal::Event::List rawEventsForDate( const QDateTime& qdt );
+ KCal::Event::List rawEvents( const QDate& start, const QDate& end,
+ bool inclusive = false );
+
+ bool addTodo( KCal::Todo* todo );
+ bool deleteTodo( KCal::Todo* );
+ KCal::Todo* todo( const QString& uid );
+ KCal::Todo::List rawTodos( TodoSortField sortField = TodoSortUnsorted, SortDirection sortDirection = SortDirectionAscending );
+ KCal::Todo::List rawTodosForDate( const QDate& date );
+
+ bool addJournal( KCal::Journal* );
+ bool deleteJournal( KCal::Journal* );
+ KCal::Journal* journal( const QString& uid );
+ KCal::Journal::List rawJournals( JournalSortField sortField = JournalSortUnsorted, SortDirection sortDirection = SortDirectionAscending );
+ KCal::Journal::List rawJournalsForDate( const QDate &date );
+
+ KCal::Alarm::List alarms( const QDateTime& from, const QDateTime& to );
+ KCal::Alarm::List alarmsTo( const QDateTime& to );
+
+ void setTimeZoneId( const QString& tzid );
+
+ bool deleteIncidence( KCal::Incidence* i );
+
+ /// The ResourceKolabBase methods called by KMail
+ bool fromKMailAddIncidence( const QString& type, const QString& subResource,
+ Q_UINT32 sernum, int format, const QString& data );
+ void fromKMailDelIncidence( const QString& type, const QString& subResource,
+ const QString& uid );
+ void fromKMailRefresh( const QString& type, const QString& subResource );
+
+ /// Listen to KMail changes in the amount of sub resources
+ void fromKMailAddSubresource( const QString& type, const QString& subResource,
+ const QString& label, bool writable,
+ bool alarmRelevant );
+ void fromKMailDelSubresource( const QString& type, const QString& subResource );
+
+ void fromKMailAsyncLoadResult( const QMap<Q_UINT32, QString>& map,
+ const QString& type,
+ const QString& folder );
+
+ /** Return the list of subresources. */
+ QStringList subresources() const;
+
+ bool canHaveSubresources() const { return true; }
+
+ /** Is this subresource active? */
+ bool subresourceActive( const QString& ) const;
+ /** (De)activate the subresource */
+ virtual void setSubresourceActive( const QString &, bool );
+
+ /** What is the label for this subresource? */
+ virtual const QString labelForSubresource( const QString& resource ) const;
+
+ virtual QString subresourceIdentifier( Incidence *incidence );
+
+ virtual bool addSubresource( const QString& resource, const QString& parent );
+ virtual bool removeSubresource( const QString& resource );
+
+ virtual QString subresourceType( const QString &resource );
+
+ KABC::Lock* lock();
+
+signals:
+ void useGlobalMode();
+protected slots:
+ void slotEmitResourceChanged();
+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 QCString& incidenceType );
+ void resolveConflict( KCal::Incidence*, const QString& subresource, Q_UINT32 sernum );
+ void addIncidence( const char* mimetype, const QString& xml,
+ const QString& subResource, Q_UINT32 sernum );
+
+ bool addIncidence( KCal::Incidence* i, const QString& subresource,
+ Q_UINT32 sernum );
+
+ void addEvent( const QString& xml, const QString& subresource,
+ Q_UINT32 sernum );
+ void addTodo( const QString& xml, const QString& subresource,
+ Q_UINT32 sernum );
+ void addJournal( const QString& xml, const QString& subresource,
+ Q_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( KConfig& config, const char* contentType,
+ Kolab::ResourceMap& map );
+ void loadSubResourceConfig( KConfig& config, const QString& name,
+ const QString& label, bool writable,
+ bool alarmRelevant, Kolab::ResourceMap& subResource );
+ bool loadSubResource( const QString& subResource, const char* mimetype );
+ bool unloadSubResource( const QString& subResource );
+
+ QString configFile() const {
+ return ResourceKolabBase::configFile( "kcal" );
+ }
+
+ Kolab::ResourceMap* subResourceMap( const QString& contentsType );
+
+ bool sendKMailUpdate( KCal::IncidenceBase* incidence, const QString& _subresource,
+ Q_UINT32 sernum );
+
+
+ KCal::CalendarLocal mCalendar;
+
+ // The list of subresources
+ Kolab::ResourceMap mEventSubResources, mTodoSubResources, mJournalSubResources;
+
+ bool mOpen; // If the resource is open, this is true
+ QDict<KCal::IncidenceBase> mPendingUpdates;
+ QTimer 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.
+ */
+ QMap<QString, QString> mNewIncidencesMap;
+ int mProgressDialogIncidenceLimit;
+};
+
+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/kresources/kolab/kcal/resourcekolab_plugin.cpp b/kresources/kolab/kcal/resourcekolab_plugin.cpp
new file mode 100644
index 000000000..afc83e1d6
--- /dev/null
+++ b/kresources/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 Qt library by Trolltech AS, Norway (or with modified versions
+ of Qt that use the same license as Qt), 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
+ Qt. 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 KConfig *config )
+ {
+ return new KCal::ResourceKolab( config );
+ }
+
+ KRES::ConfigWidget *configWidget( QWidget* )
+ {
+ return 0;
+ }
+};
+
+K_EXPORT_COMPONENT_FACTORY(kcal_kolab,KolabFactory)
diff --git a/kresources/kolab/kcal/task.cpp b/kresources/kolab/kcal/task.cpp
new file mode 100644
index 000000000..b66c9827b
--- /dev/null
+++ b/kresources/kolab/kcal/task.cpp
@@ -0,0 +1,336 @@
+/*
+ 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 Qt library by Trolltech AS, Norway (or with modified versions
+ of Qt that use the same license as Qt), 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
+ Qt. 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;
+
+
+KCal::Todo* Task::xmlToTask( const QString& xml, const QString& tz, KCal::ResourceKolab *res,
+ const QString& subResource, Q_UINT32 sernum )
+{
+ Task task( res, subResource, sernum, tz );
+ task.load( xml );
+ KCal::Todo* todo = new KCal::Todo();
+ task.saveTo( todo );
+ return todo;
+}
+
+QString Task::taskToXML( KCal::Todo* todo, const QString& tz )
+{
+ Task task( 0, QString::null, 0, tz, todo );
+ return task.saveXML();
+}
+
+Task::Task( KCal::ResourceKolab *res, const QString &subResource, Q_UINT32 sernum,
+ const QString& 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 QString& parentUid )
+{
+ mParent = parentUid;
+}
+
+QString Task::parent() const
+{
+ return mParent;
+}
+
+void Task::setDueDate( const QDateTime& date )
+{
+ mDueDate = date;
+ mHasDueDate = true;
+}
+
+QDateTime 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 QDateTime& date )
+{
+ mCompletedDate = date;
+ mHasCompletedDate = true;
+}
+
+QDateTime Task::completedDate() const
+{
+ return mCompletedDate;
+}
+
+bool Task::hasCompletedDate() const
+{
+ return mHasCompletedDate;
+}
+
+bool Task::loadAttribute( QDomElement& element )
+{
+ QString tagName = element.tagName();
+
+ if ( tagName == "priority" ) {
+ bool ok;
+ int priority = element.text().toInt( &ok );
+ if ( !ok || priority < 0 || priority > 9 )
+ priority = 5;
+ setPriority( priority );
+ } 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( stringToDateTime( 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( QDomElement& element ) const
+{
+ // Save the base class elements
+ Incidence::saveAttributes( element );
+
+ writeString( element, "priority", QString::number( priority() ) );
+ writeString( element, "completed", QString::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() )
+ writeString( element, "due-date", dateTimeToString( dueDate() ) );
+
+ if ( !parent().isNull() )
+ writeString( element, "parent", parent() );
+
+ if ( hasCompletedDate() && percentCompleted() == 100)
+ writeString( element, "x-completed-date", dateTimeToString( completedDate() ) );
+
+ return true;
+}
+
+
+bool Task::loadXML( const QDomDocument& document )
+{
+ QDomElement top = document.documentElement();
+
+ if ( top.tagName() != "task" ) {
+ qWarning( "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 ( QDomNode n = top.firstChild(); !n.isNull(); n = n.nextSibling() ) {
+ if ( n.isComment() )
+ continue;
+ if ( n.isElement() ) {
+ QDomElement 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();
+ return true;
+}
+
+QString Task::saveXML() const
+{
+ QDomDocument document = domTree();
+ QDomElement element = document.createElement( "task" );
+ element.setAttribute( "version", "1.0" );
+ saveAttributes( element );
+ if ( !hasStartDate() ) {
+ // 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.
+ QDomNodeList 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() ) );
+ else
+ mHasDueDate = false;
+ if ( task->relatedTo() )
+ setParent( task->relatedTo()->uid() );
+ else if ( !task->relatedToUid().isEmpty() )
+ setParent( task->relatedToUid() );
+ else
+ setParent( QString::null );
+
+ if ( task->hasCompletedDate() && task->percentComplete() == 100 )
+ setCompletedDate( localToUTC( task->completed() ) );
+ else
+ mHasCompletedDate = false;
+}
+
+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/kresources/kolab/kcal/task.h b/kresources/kolab/kcal/task.h
new file mode 100644
index 000000000..96bcafd96
--- /dev/null
+++ b/kresources/kolab/kcal/task.h
@@ -0,0 +1,129 @@
+/*
+ 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 Qt library by Trolltech AS, Norway (or with modified versions
+ of Qt that use the same license as Qt), 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
+ Qt. 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 QDomElement;
+
+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 QString& xml, const QString& tz, KCal::ResourceKolab *res = 0,
+ const QString& subResource = QString::null, Q_UINT32 sernum = 0 );
+
+ /// Use this to get an xml string describing this task entry
+ static QString taskToXML( KCal::Todo*, const QString& tz );
+
+ explicit Task( KCal::ResourceKolab *res, const QString& subResource, Q_UINT32 sernum,
+ const QString& tz, KCal::Todo* todo = 0 );
+ virtual ~Task();
+
+ virtual QString 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 QString& parentUid );
+ virtual QString parent() const;
+
+ virtual void setHasStartDate( bool );
+ virtual bool hasStartDate() const;
+
+ virtual void setDueDate( const QDateTime& date );
+ virtual QDateTime dueDate() const;
+ virtual bool hasDueDate() const;
+
+ virtual void setCompletedDate( const QDateTime& date );
+ virtual QDateTime completedDate() const;
+ virtual bool hasCompletedDate() const;
+
+ // Load the attributes of this class
+ virtual bool loadAttribute( QDomElement& );
+
+ // Save the attributes of this class
+ virtual bool saveAttributes( QDomElement& ) const;
+
+ // Load this task by reading the XML file
+ virtual bool loadXML( const QDomDocument& xml );
+
+ // Serialize this task to an XML string
+ virtual QString saveXML() const;
+
+protected:
+ // Read all known fields from this ical todo
+ void setFields( const KCal::Todo* );
+
+ int mPriority;
+ int mPercentCompleted;
+ KCal::Incidence::Status mStatus;
+ QString mParent;
+
+ bool mHasStartDate;
+
+ bool mHasDueDate;
+ QDateTime mDueDate;
+
+ bool mHasCompletedDate;
+ QDateTime mCompletedDate;
+};
+
+}
+
+#endif // KOLAB_TASK_H