4 commits - c++/lib c++/tests libkolab/kolabformatV2
Christian Mollekopf
mollekopf at kolabsys.com
Wed Mar 21 16:05:27 CET 2012
c++/lib/CMakeLists.txt | 2
c++/lib/kolabcontact.cpp | 6
c++/lib/kolabcontact.h | 14
c++/lib/kolabformat.cpp | 4
c++/lib/xcardconversions.h | 46 -
c++/tests/bindingstest.cpp | 6
c++/tests/conversiontest.cpp | 16
c++/tests/conversiontest.h | 2
libkolab/kolabformatV2/contact.cpp | 1208 ++++++++++++++++++++++++++++
libkolab/kolabformatV2/contact.h | 274 ++++++
libkolab/kolabformatV2/distributionlist.cpp | 242 +++++
libkolab/kolabformatV2/distributionlist.h | 96 ++
libkolab/kolabformatV2/event.cpp | 221 +++++
libkolab/kolabformatV2/event.h | 101 ++
libkolab/kolabformatV2/incidence.cpp | 976 ++++++++++++++++++++++
libkolab/kolabformatV2/incidence.h | 166 +++
libkolab/kolabformatV2/journal.cpp | 184 ++++
libkolab/kolabformatV2/journal.h | 101 ++
libkolab/kolabformatV2/kolabbase.cpp | 500 +++++++++++
libkolab/kolabformatV2/kolabbase.h | 183 ++++
libkolab/kolabformatV2/note.cpp | 230 +++++
libkolab/kolabformatV2/note.h | 109 ++
libkolab/kolabformatV2/task.cpp | 455 ++++++++++
libkolab/kolabformatV2/task.h | 143 +++
24 files changed, 5271 insertions(+), 14 deletions(-)
New commits:
commit 4d22a0e2c137b1d9ae02cd11f1291fd139c4c1f2
Merge: b94b1ac c2f4ccf
Author: Christian Mollekopf <mollekopf at kolabsys.com>
Date: Wed Mar 21 16:05:22 2012 +0100
Merge branch 'master' of ssh://git.kolabsys.com/git/libkolabxml
commit b94b1acd370b007cc49ce2ec7799743360074e9f
Author: Christian Mollekopf <mollekopf at kolabsys.com>
Date: Wed Mar 21 16:05:03 2012 +0100
Stricter compiling options, and complying to them.
diff --git a/c++/lib/CMakeLists.txt b/c++/lib/CMakeLists.txt
index ac961a8..44caba9 100644
--- a/c++/lib/CMakeLists.txt
+++ b/c++/lib/CMakeLists.txt
@@ -9,7 +9,7 @@ add_library(kolabxml SHARED kolabformat.cpp kolabcontainers.cpp kolabnote.cpp ko
target_link_libraries(kolabxml ${XERCES_C} ${Boost_LIBRARIES})
#For the core library we can be stricter when compiling. This doesn't work with the auto generated code though.
-set_target_properties(kolabxml PROPERTIES COMPILE_FLAGS "-Wl,--no-undefined -Werror ")
+set_target_properties(kolabxml PROPERTIES COMPILE_FLAGS "-Wall -Wextra -Werror -Wfatal-errors -Wl,--no-undefined")
set_target_properties(kolabxml PROPERTIES VERSION 3.0.0 SOVERSION 0)
install(TARGETS kolabxml LIBRARY DESTINATION lib)
diff --git a/c++/lib/kolabformat.cpp b/c++/lib/kolabformat.cpp
index d7ac396..98a42a2 100644
--- a/c++/lib/kolabformat.cpp
+++ b/c++/lib/kolabformat.cpp
@@ -142,7 +142,7 @@ std::string writeNote(const Note ¬e)
return Kolab::KolabObjects::serializeObject<Kolab::Note>(note);
}
-Configuration readConfiguration(const std::string& s, bool isUrl)
+Configuration readConfiguration(const std::string& /*s*/, bool /*isUrl*/)
{
// boost::shared_ptr <Kolab::Configuration> ptr = deserializeConfiguration(s, isUrl);
// if (!ptr.get()) {
@@ -152,7 +152,7 @@ Configuration readConfiguration(const std::string& s, bool isUrl)
return Configuration();
}
-std::string writeConfiguration(const Configuration &config)
+std::string writeConfiguration(const Configuration &/*config*/)
{
// return serializeConfiguration(config);
return std::string();
diff --git a/c++/lib/xcardconversions.h b/c++/lib/xcardconversions.h
index 226e07d..eeaa3b8 100644
--- a/c++/lib/xcardconversions.h
+++ b/c++/lib/xcardconversions.h
@@ -302,7 +302,7 @@ vcard_4_0::relatedPropType fromRelated(const Kolab::Related &r)
Kolab::Related toRelated(const vcard_4_0::relatedPropType &r)
{
- Kolab::Related::DescriptionType type;
+ Kolab::Related::DescriptionType type = Kolab::Related::Invalid;
std::string textOrUri;
if (r.uri()) {
type = Kolab::Related::Uid;
@@ -312,6 +312,7 @@ Kolab::Related toRelated(const vcard_4_0::relatedPropType &r)
textOrUri = *r.text();
} else {
ERROR("no text and no uri");
+ return Kolab::Related();
}
Kolab::Related related(type, textOrUri);
if (r.parameters()) {
commit 00f5299b4fde9db148998066af9c44af98982a41
Author: Christian Mollekopf <mollekopf at kolabsys.com>
Date: Tue Mar 20 00:31:22 2012 +0100
Copied Kolab Format implementation from kdepim-runtime/resources/kolabproxy/
Copied from commit 4db1c180a37713293ee126deed1f97473ab64618
It's being moved out of kdepim-runtime so we can start using it for the upgrade-tool.
diff --git a/libkolab/kolabformatV2/contact.cpp b/libkolab/kolabformatV2/contact.cpp
new file mode 100644
index 0000000..e421629
--- /dev/null
+++ b/libkolab/kolabformatV2/contact.cpp
@@ -0,0 +1,1208 @@
+/*
+ This file is part of libkabc and/or kaddressbook.
+ Copyright (c) 2004 Klarälvdalens Datakonsult AB
+ <info at 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 "contact.h"
+
+#include <kabc/addressee.h>
+#include <kcalcore/freebusyurlstore.h>
+#include <kio/netaccess.h>
+#include <kdebug.h>
+#include <QFile>
+#include <float.h>
+
+using namespace Kolab;
+
+static const char* s_pictureAttachmentName = "kolab-picture.png";
+static const char* s_logoAttachmentName = "kolab-logo.png";
+static const char* s_soundAttachmentName = "sound";
+static const char* s_unhandledTagAppName = "KOLABUNHANDLED"; // no hyphens in appnames!
+
+// saving (addressee->xml)
+Contact::Contact( const KABC::Addressee* addr )
+ : mHasGeo( false )
+{
+ setFields( addr );
+}
+
+// loading (xml->addressee)
+Contact::Contact( const QString& xml )
+ : mHasGeo( false )
+{
+ load( xml );
+}
+
+Contact::~Contact()
+{
+}
+
+void Contact::setGivenName( const QString& name )
+{
+ mGivenName = name;
+}
+
+QString Contact::givenName() const
+{
+ return mGivenName;
+}
+
+void Contact::setMiddleNames( const QString& names )
+{
+ mMiddleNames = names;
+}
+
+QString Contact::middleNames() const
+{
+ return mMiddleNames;
+}
+
+void Contact::setLastName( const QString& name )
+{
+ mLastName = name;
+}
+
+QString Contact::lastName() const
+{
+ return mLastName;
+}
+
+void Contact::setFullName( const QString& name )
+{
+ mFullName = name;
+}
+
+QString Contact::fullName() const
+{
+ return mFullName;
+}
+
+void Contact::setInitials( const QString& initials )
+{
+ mInitials = initials;
+}
+
+QString Contact::initials() const
+{
+ return mInitials;
+}
+
+void Contact::setPrefix( const QString& prefix )
+{
+ mPrefix = prefix;
+}
+
+QString Contact::prefix() const
+{
+ return mPrefix;
+}
+
+void Contact::setSuffix( const QString& suffix )
+{
+ mSuffix = suffix;
+}
+
+QString Contact::suffix() const
+{
+ return mSuffix;
+}
+
+void Contact::setRole( const QString& role )
+{
+ mRole = role;
+}
+
+QString Contact::role() const
+{
+ return mRole;
+}
+
+void Contact::setFreeBusyUrl( const QString& fbUrl )
+{
+ mFreeBusyUrl = fbUrl;
+}
+
+QString Contact::freeBusyUrl() const
+{
+ return mFreeBusyUrl;
+}
+
+void Contact::setOrganization( const QString& organization )
+{
+ mOrganization = organization;
+}
+
+QString Contact::organization() const
+{
+ return mOrganization;
+}
+
+void Contact::setWebPage( const QString& url )
+{
+ mWebPage = url;
+}
+
+QString Contact::webPage() const
+{
+ return mWebPage;
+}
+
+void Contact::setIMAddress( const QString& imAddress )
+{
+ mIMAddress = imAddress;
+}
+
+QString Contact::imAddress() const
+{
+ return mIMAddress;
+}
+
+void Contact::setDepartment( const QString& department )
+{
+ mDepartment = department;
+}
+
+QString Contact::department() const
+{
+ return mDepartment;
+}
+
+void Contact::setOfficeLocation( const QString& location )
+{
+ mOfficeLocation = location;
+}
+
+QString Contact::officeLocation() const
+{
+ return mOfficeLocation;
+}
+
+void Contact::setProfession( const QString& profession )
+{
+ mProfession = profession;
+}
+
+QString Contact::profession() const
+{
+ return mProfession;
+}
+
+void Contact::setTitle( const QString& title )
+{
+ mTitle = title;
+}
+
+QString Contact::title() const
+{
+ return mTitle;
+}
+
+void Contact::setManagerName( const QString& name )
+{
+ mManagerName = name;
+}
+
+QString Contact::managerName() const
+{
+ return mManagerName;
+}
+
+void Contact::setAssistant( const QString& name )
+{
+ mAssistant = name;
+}
+
+QString Contact::assistant() const
+{
+ return mAssistant;
+}
+
+void Contact::setNickName( const QString& name )
+{
+ mNickName = name;
+}
+
+QString Contact::nickName() const
+{
+ return mNickName;
+}
+
+void Contact::setSpouseName( const QString& name )
+{
+ mSpouseName = name;
+}
+
+QString Contact::spouseName() const
+{
+ return mSpouseName;
+}
+
+void Contact::setBirthday( const QDate& date )
+{
+ mBirthday = date;
+}
+
+QDate Contact::birthday() const
+{
+ return mBirthday;
+}
+
+void Contact::setAnniversary( const QDate& date )
+{
+ mAnniversary = date;
+}
+
+QDate Contact::anniversary() const
+{
+ return mAnniversary;
+}
+
+void Contact::setChildren( const QString& children )
+{
+ mChildren = children;
+}
+
+QString Contact::children() const
+{
+ return mChildren;
+}
+
+void Contact::setGender( const QString& gender )
+{
+ mGender = gender;
+}
+
+QString Contact::gender() const
+{
+ return mGender;
+}
+
+void Contact::setLanguage( const QString& language )
+{
+ mLanguage = language;
+}
+
+QString Contact::language() const
+{
+ return mLanguage;
+}
+
+void Contact::addPhoneNumber( const PhoneNumber& number )
+{
+ mPhoneNumbers.append( number );
+}
+
+QList<Contact::PhoneNumber>& Contact::phoneNumbers()
+{
+ return mPhoneNumbers;
+}
+
+const QList<Contact::PhoneNumber>& Contact::phoneNumbers() const
+{
+ return mPhoneNumbers;
+}
+
+void Contact::addEmail( const Email& email )
+{
+ mEmails.append( email );
+}
+
+QList<Contact::Email>& Contact::emails()
+{
+ return mEmails;
+}
+
+QString Contact::fullEmail() const
+{
+ return mFullEmail;
+}
+
+const QList<Contact::Email>& Contact::emails() const
+{
+ return mEmails;
+}
+
+void Contact::addAddress( const Contact::Address& address )
+{
+ mAddresses.append( address );
+}
+
+QList<Contact::Address>& Contact::addresses()
+{
+ return mAddresses;
+}
+
+const QList<Contact::Address>& Contact::addresses() const
+{
+ return mAddresses;
+}
+
+void Contact::setPreferredAddress( const QString& address )
+{
+ mPreferredAddress = address;
+}
+
+QString Contact::preferredAddress() const
+{
+ return mPreferredAddress;
+}
+
+bool Contact::loadNameAttribute( QDomElement& element )
+{
+ 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 == "given-name" )
+ setGivenName( e.text() );
+ else if ( tagName == "middle-names" )
+ setMiddleNames( e.text() );
+ else if ( tagName == "last-name" )
+ setLastName( e.text() );
+ else if ( tagName == "full-name" )
+ setFullName( e.text() );
+ else if ( tagName == "initials" )
+ setInitials( e.text() );
+ else if ( tagName == "prefix" )
+ setPrefix( e.text() );
+ else if ( tagName == "suffix" )
+ setSuffix( e.text() );
+ else
+ // TODO: Unhandled tag - save for later storage
+ kDebug() <<"Warning: Unhandled tag" << e.tagName();
+ } else
+ kDebug() <<"Node is not a comment or an element???";
+ }
+
+ return true;
+}
+
+void Contact::saveNameAttribute( QDomElement& element ) const
+{
+ QDomElement e = element.ownerDocument().createElement( "name" );
+ element.appendChild( e );
+
+ writeString( e, "given-name", givenName() );
+ writeString( e, "middle-names", middleNames() );
+ writeString( e, "last-name", lastName() );
+ writeString( e, "full-name", fullName() );
+ writeString( e, "initials", initials() );
+ writeString( e, "prefix", prefix() );
+ writeString( e, "suffix", suffix() );
+}
+
+bool Contact::loadPhoneAttribute( QDomElement& element )
+{
+ PhoneNumber number;
+ 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 == "type" )
+ number.type = e.text();
+ else if ( tagName == "number" )
+ number.number = e.text();
+ else
+ // TODO: Unhandled tag - save for later storage
+ kDebug() <<"Warning: Unhandled tag" << e.tagName();
+ } else
+ kDebug() <<"Node is not a comment or an element???";
+ }
+
+ addPhoneNumber( number );
+ return true;
+}
+
+void Contact::savePhoneAttributes( QDomElement& element ) const
+{
+ QList<PhoneNumber>::ConstIterator it = mPhoneNumbers.constBegin();
+ for ( ; it != mPhoneNumbers.constEnd(); ++it ) {
+ QDomElement e = element.ownerDocument().createElement( "phone" );
+ element.appendChild( e );
+ const PhoneNumber& p = *it;
+ writeString( e, "type", p.type );
+ writeString( e, "number", p.number );
+ }
+}
+
+void Contact::saveEmailAttributes( QDomElement& element ) const
+{
+ QList<Email>::ConstIterator it = mEmails.constBegin();
+ for ( ; it != mEmails.constEnd(); ++it )
+ saveEmailAttribute( element, *it );
+}
+
+void Contact::loadCustomAttributes( QDomElement& element )
+{
+ Custom custom;
+ custom.app = element.attribute( "app" );
+ custom.name = element.attribute( "name" );
+ custom.value = element.attribute( "value" );
+ mCustomList.append( custom );
+}
+
+void Contact::saveCustomAttributes( QDomElement& element ) const
+{
+ QList<Custom>::ConstIterator it = mCustomList.constBegin();
+ for ( ; it != mCustomList.constEnd(); ++it ) {
+ Q_ASSERT( !(*it).name.isEmpty() );
+ if ( (*it).app == s_unhandledTagAppName ) {
+ writeString( element, (*it).name, (*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( "app", (*it).app );
+ e.setAttribute( "name", (*it).name );
+ e.setAttribute( "value", (*it).value );
+ }
+ }
+}
+
+bool Contact::loadAddressAttribute( QDomElement& element )
+{
+ Address address;
+
+ 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 == "type" )
+ address.type = e.text();
+ else if ( tagName == "x-kde-type" )
+ address.kdeAddressType = e.text().toInt();
+ else if ( tagName == "street" )
+ address.street = e.text();
+ else if ( tagName == "pobox" )
+ address.pobox = e.text();
+ else if ( tagName == "locality" )
+ address.locality = e.text();
+ else if ( tagName == "region" )
+ address.region = e.text();
+ else if ( tagName == "postal-code" )
+ address.postalCode = e.text();
+ else if ( tagName == "country" )
+ address.country = e.text();
+ else
+ // TODO: Unhandled tag - save for later storage
+ kDebug() <<"Warning: Unhandled tag" << e.tagName();
+ } else
+ kDebug() <<"Node is not a comment or an element???";
+ }
+
+ addAddress( address );
+ return true;
+}
+
+void Contact::saveAddressAttributes( QDomElement& element ) const
+{
+ QList<Address>::ConstIterator it = mAddresses.constBegin();
+ for ( ; it != mAddresses.constEnd(); ++it ) {
+ QDomElement e = element.ownerDocument().createElement( "address" );
+ element.appendChild( e );
+ const Address& a = *it;
+ writeString( e, "type", a.type );
+ writeString( e, "x-kde-type", QString::number( a.kdeAddressType ) );
+ if ( !a.street.isEmpty() )
+ writeString( e, "street", a.street );
+ if ( !a.pobox.isEmpty() )
+ writeString( e, "pobox", a.pobox );
+ if ( !a.locality.isEmpty() )
+ writeString( e, "locality", a.locality );
+ if ( !a.region.isEmpty() )
+ writeString( e, "region", a.region );
+ if ( !a.postalCode.isEmpty() )
+ writeString( e, "postal-code", a.postalCode );
+ if ( !a.country.isEmpty() )
+ writeString( e, "country", a.country );
+ }
+}
+
+bool Contact::loadAttribute( QDomElement& element )
+{
+ const QString tagName = element.tagName();
+ switch ( tagName[0].toLatin1() ) {
+ case 'a':
+ if ( tagName == "address" )
+ return loadAddressAttribute( element );
+ if ( tagName == "assistant" ) {
+ setAssistant( element.text() );
+ return true;
+ }
+ if ( tagName == "anniversary" ) {
+ if ( !element.text().isEmpty() )
+ setAnniversary( stringToDate( element.text() ) );
+ return true;
+ }
+ break;
+ case 'b':
+ if ( tagName == "birthday" ) {
+ if ( !element.text().isEmpty() )
+ setBirthday( stringToDate( element.text() ) );
+ return true;
+ }
+ break;
+ case 'c':
+ if ( tagName == "children" ) {
+ setChildren( element.text() );
+ return true;
+ }
+ break;
+ case 'd':
+ if ( tagName == "department" ) {
+ setDepartment( element.text() );
+ return true;
+ }
+ break;
+ case 'e':
+ if ( tagName == "email" ) {
+ Email email;
+ if ( loadEmailAttribute( element, email ) ) {
+ addEmail( email );
+ return true;
+ } else
+ return false;
+ }
+ break;
+ case 'f':
+ if ( tagName == "free-busy-url" ) {
+ setFreeBusyUrl( element.text() );
+ return true;
+ }
+ break;
+ case 'g':
+ if ( tagName == "gender" ) {
+ setGender( element.text() );
+ return true;
+ }
+ break;
+ case 'i':
+ if ( tagName == "im-address" ) {
+ setIMAddress( element.text() );
+ return true;
+ }
+ break;
+ case 'j':
+ if ( tagName == "job-title" ) {
+ // see saveAttributes: <job-title> is mapped to the Role field
+ setTitle( element.text() );
+ return true;
+ }
+ break;
+ case 'l':
+ if ( tagName == "language" ) {
+ setLanguage( element.text() );
+ return true;
+ }
+ if ( tagName == "latitude" ) {
+ setLatitude( element.text().toFloat() );
+ mHasGeo = true;
+ return true;
+ }
+ if ( tagName == "longitude" ) {
+ setLongitude( element.text().toFloat() );
+ mHasGeo = true;
+ }
+ break;
+ case 'm':
+ if ( tagName == "manager-name" ) {
+ setManagerName( element.text() );
+ return true;
+ }
+ case 'n':
+ if ( tagName == "name" )
+ return loadNameAttribute( element );
+ if ( tagName == "nick-name" ) {
+ setNickName( element.text() );
+ return true;
+ }
+ break;
+ case 'o':
+ if ( tagName == "organization" ) {
+ setOrganization( element.text() );
+ return true;
+ }
+ if ( tagName == "office-location" ) {
+ setOfficeLocation( element.text() );
+ return true;
+ }
+ break;
+ case 'p':
+ if ( tagName == "profession" ) {
+ setProfession( element.text() );
+ return true;
+ }
+ if ( tagName == "picture" ) {
+ mPictureAttachmentName = element.text();
+ return true;
+ }
+ if ( tagName == "phone" ) {
+ return loadPhoneAttribute( element );
+ return true;
+ }
+ if ( tagName == "preferred-address" ) {
+ setPreferredAddress( element.text() );
+ return true;
+ }
+ break;
+ case 'r':
+ if ( tagName == "role" ) {
+ setRole( element.text() );
+ return true;
+ }
+ break;
+ case 's':
+ if ( tagName == "spouse-name" ) {
+ setSpouseName( element.text() );
+ return true;
+ }
+ break;
+ case 'x':
+ if ( tagName == "x-logo" ) {
+ mLogoAttachmentName = element.text();
+ return true;
+ }
+ if ( tagName == "x-sound" ) {
+ mSoundAttachmentName = element.text();
+ return true;
+ }
+ if ( tagName == "x-custom" ) {
+ loadCustomAttributes( element );
+ return true;
+ }
+ if ( tagName == "x-title" ) {
+ setTitle( element.text() );
+ return true;
+ }
+ break;
+ case 'w':
+ if ( tagName == "web-page" ) {
+ setWebPage( element.text() );
+ return true;
+ }
+ break;
+ default:
+ break;
+ }
+ return KolabBase::loadAttribute( element );
+}
+
+bool Contact::saveAttributes( QDomElement& element ) const
+{
+ // Save the base class elements
+ KolabBase::saveAttributes( element );
+ saveNameAttribute( element );
+ writeString( element, "free-busy-url", freeBusyUrl() );
+ writeString( element, "organization", organization() );
+ writeString( element, "web-page", webPage() );
+ writeString( element, "im-address", imAddress() );
+ writeString( element, "department", department() );
+ writeString( element, "office-location", officeLocation() );
+ writeString( element, "profession", profession() );
+ writeString( element, "role", role() );
+ writeString( element, "job-title", title() );
+ writeString( element, "manager-name", managerName() );
+ writeString( element, "assistant", assistant() );
+ writeString( element, "nick-name", nickName() );
+ writeString( element, "spouse-name", spouseName() );
+ writeString( element, "birthday", dateToString( birthday() ) );
+ writeString( element, "anniversary", dateToString( anniversary() ) );
+ if ( !picture().isNull() )
+ writeString( element, "picture", mPictureAttachmentName );
+ if ( !logo().isNull() )
+ writeString( element, "x-logo", mLogoAttachmentName );
+ if ( !sound().isNull() )
+ writeString( element, "x-sound", mSoundAttachmentName );
+ writeString( element, "children", children() );
+ writeString( element, "gender", gender() );
+ writeString( element, "language", language() );
+ savePhoneAttributes( element );
+ saveEmailAttributes( element );
+ saveAddressAttributes( element );
+ writeString( element, "preferred-address", preferredAddress() );
+ if ( mHasGeo ) {
+ writeString( element, "latitude", QString::number( latitude(), 'g', DBL_DIG ) );
+ writeString( element, "longitude", QString::number( longitude(), 'g', DBL_DIG ) );
+ }
+ saveCustomAttributes( element );
+
+ return true;
+}
+
+bool Contact::loadXML( const QDomDocument& document )
+{
+ QDomElement top = document.documentElement();
+
+ if ( top.tagName() != "contact" ) {
+ qWarning( "XML error: Top tag was %s instead of the expected contact",
+ top.tagName().toAscii().data() );
+ 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
+ //kDebug() <<"Saving unhandled tag" << e.tagName();
+ Custom c;
+ c.app = s_unhandledTagAppName;
+ c.name = e.tagName();
+ c.value = e.text();
+ mCustomList.append( c );
+ }
+ } else
+ kDebug() <<"Node is not a comment or an element???";
+ }
+
+ return true;
+}
+
+QString Contact::saveXML() const
+{
+ QDomDocument document = domTree();
+ QDomElement element = document.createElement("contact" );
+ element.setAttribute( "version", "1.0" );
+ saveAttributes( element );
+ document.appendChild( element );
+ return document.toString();
+}
+
+static QString addressTypeToString( int /*KABC::Address::Type*/ type )
+{
+ if ( type & KABC::Address::Home )
+ return "home";
+ if ( type & KABC::Address::Work )
+ return "business";
+ return "other";
+}
+
+static int addressTypeFromString( const QString& type )
+{
+ if ( type == "home" )
+ return KABC::Address::Home;
+ if ( type == "business" )
+ return KABC::Address::Work;
+ // well, this shows "other" in the editor, which is what we want...
+ return KABC::Address::Dom | KABC::Address::Intl | KABC::Address::Postal | KABC::Address::Parcel;
+}
+
+static QStringList phoneTypeToString( KABC::PhoneNumber::Type type )
+{
+ // KABC has a bitfield, i.e. the same phone number can be used for work and home
+ // and fax and cellphone etc. etc.
+ // So when saving we need to create as many tags as bits that were set.
+ QStringList types;
+ if ( type & KABC::PhoneNumber::Fax ) {
+ if ( type & KABC::PhoneNumber::Home )
+ types << "homefax";
+ else // assume work -- if ( type & KABC::PhoneNumber::Work )
+ types << "businessfax";
+ type = type & ~KABC::PhoneNumber::Home;
+ type = type & ~KABC::PhoneNumber::Work;
+ }
+
+ // To support both "home1" and "home2", map Home+Pref to home1
+ if ( ( type & KABC::PhoneNumber::Home ) && ( type & KABC::PhoneNumber::Pref ) )
+ {
+ types << "home1";
+ type = type & ~KABC::PhoneNumber::Home;
+ type = type & ~KABC::PhoneNumber::Pref;
+ }
+ // To support both "business1" and "business2", map Work+Pref to business1
+ if ( ( type & KABC::PhoneNumber::Work ) && ( type & KABC::PhoneNumber::Pref ) )
+ {
+ types << "business1";
+ type = type & ~KABC::PhoneNumber::Work;
+ type = type & ~KABC::PhoneNumber::Pref;
+ }
+
+
+ if ( type & KABC::PhoneNumber::Home )
+ types << "home2";
+ if ( type & KABC::PhoneNumber::Msg ) // Msg==messaging
+ types << "company";
+ if ( type & KABC::PhoneNumber::Work )
+ types << "business2";
+ if ( type & KABC::PhoneNumber::Pref )
+ types << "primary";
+ if ( type & KABC::PhoneNumber::Voice )
+ types << "callback"; // ##
+ if ( type & KABC::PhoneNumber::Cell )
+ types << "mobile";
+ if ( type & KABC::PhoneNumber::Video )
+ types << "radio"; // ##
+ if ( type & KABC::PhoneNumber::Bbs )
+ types << "ttytdd";
+ if ( type & KABC::PhoneNumber::Modem )
+ types << "telex"; // #
+ if ( type & KABC::PhoneNumber::Car )
+ types << "car";
+ if ( type & KABC::PhoneNumber::Isdn )
+ types << "isdn";
+ if ( type & KABC::PhoneNumber::Pcs )
+ types << "assistant"; // ## Assistant is e.g. secretary
+ if ( type & KABC::PhoneNumber::Pager )
+ types << "pager";
+ return types;
+}
+
+static KABC::PhoneNumber::Type phoneTypeFromString( const QString& type )
+{
+ if ( type == "homefax" )
+ return KABC::PhoneNumber::Home | KABC::PhoneNumber::Fax;
+ if ( type == "businessfax" )
+ return KABC::PhoneNumber::Work | KABC::PhoneNumber::Fax;
+ if ( type == "business1" )
+ return KABC::PhoneNumber::Work | KABC::PhoneNumber::Pref;
+ if ( type == "business2" )
+ return KABC::PhoneNumber::Work;
+ if ( type == "home1" )
+ return KABC::PhoneNumber::Home | KABC::PhoneNumber::Pref;
+ if ( type == "home2" )
+ return KABC::PhoneNumber::Home;
+ if ( type == "company" )
+ return KABC::PhoneNumber::Msg;
+ if ( type == "primary" )
+ return KABC::PhoneNumber::Pref;
+ if ( type == "callback" )
+ return KABC::PhoneNumber::Voice;
+ if ( type == "mobile" )
+ return KABC::PhoneNumber::Cell;
+ if ( type == "radio" )
+ return KABC::PhoneNumber::Video;
+ if ( type == "ttytdd" )
+ return KABC::PhoneNumber::Bbs;
+ if ( type == "telex" )
+ return KABC::PhoneNumber::Modem;
+ if ( type == "car" )
+ return KABC::PhoneNumber::Car;
+ if ( type == "isdn" )
+ return KABC::PhoneNumber::Isdn;
+ if ( type == "assistant" )
+ return KABC::PhoneNumber::Pcs;
+ if ( type == "pager" )
+ return KABC::PhoneNumber::Pager;
+ return KABC::PhoneNumber::Home; // whatever
+}
+
+static const char* s_knownCustomFields[] = {
+ "X-IMAddress",
+ "X-Office",
+ "X-Profession",
+ "X-ManagersName",
+ "X-AssistantsName",
+ "X-SpousesName",
+ "X-Anniversary",
+ "DistributionList",
+ 0
+};
+
+
+// The saving is addressee -> Contact -> xml, this is the first part
+void Contact::setFields( const KABC::Addressee* addressee )
+{
+ KolabBase::setFields( addressee );
+
+ setGivenName( addressee->givenName() );
+ setMiddleNames( addressee->additionalName() );
+ setLastName( addressee->familyName() );
+ setFullName( addressee->formattedName() );
+ setPrefix( addressee->prefix() );
+ setSuffix( addressee->suffix() );
+ setOrganization( addressee->organization() );
+ setWebPage( addressee->url().url() );
+ setIMAddress( addressee->custom( "KADDRESSBOOK", "X-IMAddress" ) );
+ setDepartment( addressee->department());
+ setOfficeLocation( addressee->custom( "KADDRESSBOOK", "X-Office" ) );
+ setProfession( addressee->custom( "KADDRESSBOOK", "X-Profession" ) );
+ setRole( addressee->role() );
+ setTitle( addressee->title() );
+ setManagerName( addressee->custom( "KADDRESSBOOK", "X-ManagersName" ) );
+ setAssistant( addressee->custom( "KADDRESSBOOK", "X-AssistantsName" ) );
+ setNickName( addressee->nickName() );
+ setSpouseName( addressee->custom( "KADDRESSBOOK", "X-SpousesName" ) );
+ if ( !addressee->birthday().isNull() )
+ setBirthday( addressee->birthday().date() );
+ const QString& anniversary = addressee->custom( "KADDRESSBOOK", "X-Anniversary" );
+ if ( !anniversary.isEmpty() )
+ setAnniversary( stringToDate( anniversary ) );
+
+ const QStringList emails = addressee->emails();
+ // Conversion problem here:
+ // KABC::Addressee has only one full name and N addresses, but the XML format
+ // has N times (fullname+address). So we just copy the fullname over and ignore it on loading.
+ for ( QStringList::ConstIterator it = emails.constBegin(); it != emails.constEnd(); ++it ) {
+ Email email;
+ email.displayName = fullName();
+ email.smtpAddress = *it;
+ addEmail( email );
+ }
+
+ // save formatted full email for later usage
+ mFullEmail = addressee->fullEmail();
+
+ // Now the real-world addresses
+ QString preferredAddress = "home";
+ const KABC::Address::List addresses = addressee->addresses();
+ for ( KABC::Address::List::ConstIterator it = addresses.constBegin() ; it != addresses.constEnd(); ++it ) {
+ Address address;
+ address.kdeAddressType = (*it).type();
+ address.type = addressTypeToString( address.kdeAddressType );
+ address.street = (*it).street();
+ address.pobox = (*it).postOfficeBox();
+ address.locality = (*it).locality();
+ address.region = (*it).region();
+ address.postalCode = (*it).postalCode();
+ address.country = (*it).country();
+ // ## TODO not in the XML format: extended address info.
+ // ## KDE-specific tags? Or hiding those fields? Or adding a warning?
+ addAddress( address );
+ if ( address.kdeAddressType & KABC::Address::Pref ) {
+ preferredAddress = address.type; // home, business or other
+ }
+ }
+ setPreferredAddress( preferredAddress );
+
+ const KABC::PhoneNumber::List phones = addressee->phoneNumbers();
+ for ( KABC::PhoneNumber::List::ConstIterator it = phones.constBegin(); it != phones.constEnd(); ++it ) {
+ // Create a tag per phone type set in the bitfield
+ QStringList types = phoneTypeToString( (*it).type() );
+ for( QStringList::ConstIterator typit = types.constBegin(); typit != types.constEnd(); ++typit ) {
+ PhoneNumber phoneNumber;
+ phoneNumber.type = *typit;
+ phoneNumber.number = (*it).number();
+ addPhoneNumber( phoneNumber );
+ }
+ }
+
+ setPicture( loadPictureFromAddressee( addressee->photo() ) );
+ mPictureAttachmentName = addressee->custom( "KOLAB", "PictureAttachmentName" );
+ if ( mPictureAttachmentName.isEmpty() )
+ mPictureAttachmentName = s_pictureAttachmentName;
+
+ setLogo( loadPictureFromAddressee( addressee->logo() ) );
+ mLogoAttachmentName = addressee->custom( "KOLAB", "LogoAttachmentName" );
+ if ( mLogoAttachmentName.isEmpty() )
+ mLogoAttachmentName = s_logoAttachmentName;
+
+ setSound( loadSoundFromAddressee( addressee->sound() ) );
+ mSoundAttachmentName = addressee->custom( "KOLAB", "SoundAttachmentName" );
+ if ( mSoundAttachmentName.isEmpty() )
+ mSoundAttachmentName = s_soundAttachmentName;
+
+ if ( addressee->geo().isValid() ) {
+ setLatitude( addressee->geo().latitude() );
+ setLongitude( addressee->geo().longitude() );
+ mHasGeo = true;
+ }
+
+ // Other KADDRESSBOOK custom fields than those already handled
+ // (includes e.g. crypto settings, and extra im addresses)
+ QStringList knownCustoms;
+ for ( const char** p = s_knownCustomFields; *p; ++p )
+ knownCustoms << QString::fromLatin1( *p );
+ QStringList customs = addressee->customs();
+ for( QStringList::ConstIterator it = customs.constBegin(); it != customs.constEnd(); ++it ) {
+ // KABC::Addressee doesn't offer a real way to iterate over customs, other than splitting strings ourselves
+ // The format is "app-name:value".
+ int pos = (*it).indexOf( '-' );
+ if ( pos == -1 ) continue;
+ QString app = (*it).left( pos );
+ if ( app == "KOLAB" ) continue;
+ QString name = (*it).mid( pos + 1 );
+ pos = name.indexOf( ':' );
+ if ( pos == -1 ) continue;
+ QString value = name.mid( pos + 1 );
+ name = name.left( pos );
+ if ( !knownCustoms.contains( name ) ) {
+ //kDebug() <<"app=" << app <<" name=" << name <<" value=" << value;
+ Custom c;
+ if ( app != "KADDRESSBOOK" ) // that's the default
+ c.app = app;
+ c.name = name;
+ c.value = value;
+ mCustomList.append( c );
+ }
+ }
+
+ QString url = KCalCore::FreeBusyUrlStore::self()->readUrl( addressee->preferredEmail() );
+ if ( !url.isEmpty() ) {
+ setFreeBusyUrl( url );
+ }
+
+ // Those fields, although defined in Addressee, are not used in KDE
+ // (e.g. not visible in kaddressbook/addresseeeditorwidget.cpp)
+ // So it doesn't matter much if we don't have them in the XML.
+ // mailer, timezone, productId, sortString, agent, rfc2426 name()
+
+ // Things KAddressBook can't handle, so they are saved as unhandled tags:
+ // initials, children, gender, language
+}
+
+
+// The loading is: xml -> Contact -> addressee, this is the second part
+void Contact::saveTo( KABC::Addressee* addressee )
+{
+ // TODO: This needs the same set of TODOs as the setFields method
+ KolabBase::saveTo( addressee );
+
+ addressee->setGivenName( givenName() );
+ addressee->setAdditionalName( middleNames() );
+ addressee->setFamilyName( lastName() );
+ addressee->setFormattedName( fullName() );
+ addressee->setPrefix( prefix() );
+ addressee->setSuffix( suffix() );
+ addressee->setOrganization( organization() );
+ addressee->setUrl( webPage() );
+ addressee->insertCustom( "KADDRESSBOOK", "X-IMAddress", imAddress() );
+ addressee->setDepartment( department() );
+ addressee->insertCustom( "KADDRESSBOOK", "X-Office", officeLocation() );
+ addressee->insertCustom( "KADDRESSBOOK", "X-Profession", profession() );
+ addressee->setRole( role() );
+ addressee->setTitle( title() );
+ addressee->insertCustom( "KADDRESSBOOK", "X-ManagersName", managerName() );
+ addressee->insertCustom( "KADDRESSBOOK", "X-AssistantsName", assistant() );
+ addressee->setNickName( nickName() );
+ addressee->insertCustom( "KADDRESSBOOK", "X-SpousesName", spouseName() );
+ if ( birthday().isValid() )
+ addressee->setBirthday( QDateTime( birthday() ) );
+
+ if ( anniversary().isValid() )
+ addressee->insertCustom( "KADDRESSBOOK", "X-Anniversary",
+ dateToString( anniversary() ) );
+ else
+ addressee->removeCustom( "KADDRESSBOOK", "X-Anniversary" );
+
+ // We need to store both the original attachment name and the picture data into the addressee.
+ // This is important, otherwise we would save the image under another attachment name w/o deleting the original one!
+ if ( !mPicture.isNull() )
+ addressee->setPhoto( KABC::Picture( mPicture ) );
+ // Note that we must save the filename in all cases, so that removing the picture
+ // actually deletes the attachment.
+ addressee->insertCustom( "KOLAB", "PictureAttachmentName", mPictureAttachmentName );
+ if ( !mLogo.isNull() )
+ addressee->setLogo( KABC::Picture( mLogo ) );
+ addressee->insertCustom( "KOLAB", "LogoAttachmentName", mLogoAttachmentName );
+ if ( !mSound.isNull() )
+ addressee->setSound( KABC::Sound( mSound ) );
+ addressee->insertCustom( "KOLAB", "SoundAttachmentName", mSoundAttachmentName );
+
+ if ( mHasGeo )
+ addressee->setGeo( KABC::Geo( mLatitude, mLongitude ) );
+
+ QStringList emailAddresses;
+ for ( QList<Email>::ConstIterator it = mEmails.constBegin(); it != mEmails.constEnd(); ++it ) {
+ // we can't do anything with (*it).displayName
+ emailAddresses.append( (*it).smtpAddress );
+ }
+ addressee->setEmails( emailAddresses );
+
+ for ( QList<Address>::ConstIterator it = mAddresses.constBegin(); it != mAddresses.constEnd(); ++it ) {
+ KABC::Address address;
+ int type = (*it).kdeAddressType;
+ if ( type == -1 ) { // no kde-specific type available
+ type = addressTypeFromString( (*it).type );
+ if ( (*it).type == mPreferredAddress )
+ type |= KABC::Address::Pref;
+ }
+ address.setType( static_cast<KABC::Address::Type>(type) );
+ address.setStreet( (*it).street );
+ address.setPostOfficeBox( (*it).pobox );
+ address.setLocality( (*it).locality );
+ address.setRegion( (*it).region );
+ address.setPostalCode( (*it).postalCode );
+ address.setCountry( (*it).country );
+ addressee->insertAddress( address );
+ }
+
+ for ( QList<PhoneNumber>::ConstIterator it = mPhoneNumbers.constBegin(); it != mPhoneNumbers.constEnd(); ++it ) {
+ KABC::PhoneNumber number;
+ number.setType( phoneTypeFromString( (*it).type ) );
+ number.setNumber( (*it).number );
+ addressee->insertPhoneNumber( number );
+ }
+
+ for( QList<Custom>::ConstIterator it = mCustomList.constBegin(); it != mCustomList.constEnd(); ++it ) {
+ QString app = (*it).app.isEmpty() ? QString::fromLatin1( "KADDRESSBOOK" ) : (*it).app;
+ addressee->insertCustom( app, (*it).name, (*it).value );
+ }
+ //kDebug() << addressee->customs();
+}
+
+QImage Contact::loadPictureFromAddressee( const KABC::Picture& picture )
+{
+ QImage img;
+ if ( !picture.isIntern() && !picture.url().isEmpty() ) {
+ QString tmpFile;
+ if ( KIO::NetAccess::download( picture.url(), tmpFile, 0 /*no widget known*/ ) ) {
+ img.load( tmpFile );
+ KIO::NetAccess::removeTempFile( tmpFile );
+ }
+ } else
+ img = picture.data();
+ return img;
+}
+
+QByteArray Kolab::Contact::loadSoundFromAddressee( const KABC::Sound& sound )
+{
+ QByteArray data;
+ if ( !sound.isIntern() && !sound.url().isEmpty() ) {
+ QString tmpFile;
+ if ( KIO::NetAccess::download( sound.url(), tmpFile, 0 /*no widget known*/ ) ) {
+ QFile f( tmpFile );
+ if ( f.open( QIODevice::ReadOnly ) ) {
+ data = f.readAll();
+ f.close();
+ }
+ KIO::NetAccess::removeTempFile( tmpFile );
+ }
+ } else
+ data = sound.data();
+ return data;
+}
+
+QString Kolab::Contact::productID() const
+{
+ // TODO: When KAB has the version number in a header file, don't hardcode (Bo)
+ // Or we could use Addressee::productID? (David)
+ return "KAddressBook 3.3, Kolab resource";
+}
diff --git a/libkolab/kolabformatV2/contact.h b/libkolab/kolabformatV2/contact.h
new file mode 100644
index 0000000..ba88a77
--- /dev/null
+++ b/libkolab/kolabformatV2/contact.h
@@ -0,0 +1,274 @@
+/*
+ This file is part of libkabc and/or kaddressbook.
+ Copyright (c) 2002 - 2004 Klarälvdalens Datakonsult AB
+ <info at 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 KOLABCONTACT_H
+#define KOLABCONTACT_H
+
+#include "kolabbase.h"
+#include <qimage.h>
+
+namespace KABC {
+ class Addressee;
+ class Picture;
+ class Sound;
+}
+
+namespace Kolab {
+
+class Contact : public KolabBase {
+public:
+ struct PhoneNumber {
+ public:
+ QString type;
+ QString number;
+ };
+
+ struct Address {
+ public:
+ Address() : kdeAddressType( -1 )
+ {
+ }
+ int kdeAddressType; // KABC::Address::Type
+ QString type; // kolab-compliant address type: home, work or other
+ QString street;
+ QString pobox;
+ QString locality;
+ QString region;
+ QString postalCode;
+ QString country;
+ };
+
+ explicit Contact( const KABC::Addressee* address );
+ Contact( const QString& xml );
+ ~Contact();
+
+ void saveTo( KABC::Addressee* address );
+
+ QString type() const { return "Contact"; }
+
+ void setGivenName( const QString& name );
+ QString givenName() const;
+
+ void setMiddleNames( const QString& names );
+ QString middleNames() const;
+
+ void setLastName( const QString& name );
+ QString lastName() const;
+
+ void setFullName( const QString& name );
+ QString fullName() const;
+
+ void setInitials( const QString& initials );
+ QString initials() const;
+
+ void setPrefix( const QString& prefix );
+ QString prefix() const;
+
+ void setSuffix( const QString& suffix );
+ QString suffix() const;
+
+ void setRole( const QString& role );
+ QString role() const;
+
+ void setFreeBusyUrl( const QString& fbUrl );
+ QString freeBusyUrl() const;
+
+ void setOrganization( const QString& organization );
+ QString organization() const;
+
+ void setWebPage( const QString& url );
+ QString webPage() const;
+
+ void setIMAddress( const QString& imAddress );
+ QString imAddress() const;
+
+ void setDepartment( const QString& department );
+ QString department() const;
+
+ void setOfficeLocation( const QString& location );
+ QString officeLocation() const;
+
+ void setProfession( const QString& profession );
+ QString profession() const;
+
+ void setTitle( const QString& title );
+ QString title() const;
+
+ void setManagerName( const QString& name );
+ QString managerName() const;
+
+ void setAssistant( const QString& name );
+ QString assistant() const;
+
+ void setNickName( const QString& name );
+ QString nickName() const;
+
+ void setSpouseName( const QString& name );
+ QString spouseName() const;
+
+ void setBirthday( const QDate& date );
+ QDate birthday() const;
+
+ void setAnniversary( const QDate& date );
+ QDate anniversary() const;
+
+ void setPicture( const QImage& image) { mPicture = image; }
+ QString pictureAttachmentName() const { return mPictureAttachmentName; }
+ QImage picture() const { return mPicture; }
+
+ void setLogo( const QImage& image ) { mLogo = image; }
+ QString logoAttachmentName() const { return mLogoAttachmentName; }
+ QImage logo() const { return mLogo; }
+
+ void setSound( const QByteArray& sound ) { mSound = sound; }
+ QString soundAttachmentName() const { return mSoundAttachmentName; }
+ QByteArray sound() const { return mSound; }
+
+ void setChildren( const QString& children );
+ QString children() const;
+
+ void setGender( const QString& gender );
+ QString gender() const;
+
+ void setLanguage( const QString& language );
+ QString language() const;
+
+ void addPhoneNumber( const PhoneNumber& number );
+ QList<PhoneNumber>& phoneNumbers();
+ const QList<PhoneNumber>& phoneNumbers() const;
+
+ void addEmail( const Email& email );
+ QList<Email>& emails();
+ const QList<Email>& emails() const;
+
+ QString fullEmail() const;
+
+ void addAddress( const Address& address );
+ QList<Address>& addresses();
+ const QList<Address>& addresses() const;
+
+ // which address is preferred: home or business or other
+ void setPreferredAddress( const QString& address );
+ QString preferredAddress() const;
+
+ float latitude() const { return mLatitude; }
+ void setLatitude( float latitude ) { mLatitude = latitude; }
+
+ float longitude() const { return mLongitude; }
+ void setLongitude( float longitude ) { mLongitude = longitude; }
+
+ // Load the attributes of this class
+ bool loadAttribute( QDomElement& );
+
+ // Save the attributes of this class
+ bool saveAttributes( QDomElement& ) const;
+
+ // Load this note by reading the XML file
+ bool loadXML( const QDomDocument& xml );
+
+ // Serialize this note to an XML string
+ QString saveXML() const;
+
+protected:
+ void setFields( const KABC::Addressee* );
+
+private:
+ bool loadNameAttribute( QDomElement& element );
+ void saveNameAttribute( QDomElement& element ) const;
+
+ bool loadPhoneAttribute( QDomElement& element );
+ void savePhoneAttributes( QDomElement& element ) const;
+
+ void saveEmailAttributes( QDomElement& element ) const;
+
+ bool loadAddressAttribute( QDomElement& element );
+ void saveAddressAttributes( QDomElement& element ) const;
+
+ void loadCustomAttributes( QDomElement& element );
+ void saveCustomAttributes( QDomElement& element ) const;
+
+ QImage loadPictureFromAddressee( const KABC::Picture& picture );
+
+ QByteArray loadSoundFromAddressee( const KABC::Sound& sound );
+
+ QString productID() const;
+
+ QString mGivenName;
+ QString mMiddleNames;
+ QString mLastName;
+ QString mFullName;
+ QString mInitials;
+ QString mPrefix;
+ QString mSuffix;
+ QString mRole;
+ QString mFreeBusyUrl;
+ QString mOrganization;
+ QString mWebPage;
+ QString mIMAddress;
+ QString mDepartment;
+ QString mOfficeLocation;
+ QString mProfession;
+ QString mTitle;
+ QString mManagerName;
+ QString mAssistant;
+ QString mNickName;
+ QString mSpouseName;
+ QDate mBirthday;
+ QDate mAnniversary;
+ QImage mPicture;
+ QImage mLogo;
+ QByteArray mSound;
+ QString mPictureAttachmentName;
+ QString mLogoAttachmentName;
+ QString mSoundAttachmentName;
+ QString mChildren;
+ QString mGender;
+ QString mLanguage;
+ QList<PhoneNumber> mPhoneNumbers;
+ QList<Email> mEmails;
+ QString mFullEmail;
+ QList<Address> mAddresses;
+ QString mPreferredAddress;
+ float mLatitude;
+ float mLongitude;
+ bool mHasGeo;
+ struct Custom {
+ QString app;
+ QString name;
+ QString value;
+ };
+ QList<Custom> mCustomList;
+};
+
+}
+
+#endif // KOLABCONTACT_H
diff --git a/libkolab/kolabformatV2/distributionlist.cpp b/libkolab/kolabformatV2/distributionlist.cpp
new file mode 100644
index 0000000..840c5a8
--- /dev/null
+++ b/libkolab/kolabformatV2/distributionlist.cpp
@@ -0,0 +1,242 @@
+/*
+ This file is part of Akonadi KolabProxy.
+ Copyright (c) 2009 Kevin Krammer <kevin.krammer at gmx.at>
+
+ 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 "distributionlist.h"
+
+#include <akonadi/itemfetchjob.h>
+#include <akonadi/itemfetchscope.h>
+#include <kabc/addressee.h>
+#include <kabc/contactgroup.h>
+#include <kdebug.h>
+
+using namespace Kolab;
+
+static const char* s_unhandledTagAppName = "KOLABUNHANDLED"; // no hyphens in appnames!
+
+// saving (contactgroup->xml)
+DistributionList::DistributionList( const KABC::ContactGroup* contactGroup )
+{
+ setFields( contactGroup );
+}
+
+// loading (xml->contactgroup)
+DistributionList::DistributionList( const QString& xml )
+{
+ load( xml );
+}
+
+DistributionList::~DistributionList()
+{
+}
+
+void DistributionList::setName( const QString& name )
+{
+ mName = name;
+}
+
+QString DistributionList::name() const
+{
+ return mName;
+}
+
+void Kolab::DistributionList::loadDistrListMember( const QDomElement& element )
+{
+ Member member;
+ 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" )
+ member.displayName = e.text();
+ else if ( tagName == "smtp-address" )
+ member.email = e.text();
+ }
+ }
+ mDistrListMembers.append( member );
+}
+
+void DistributionList::saveDistrListMembers( QDomElement& element ) const
+{
+ QList<Member>::ConstIterator it = mDistrListMembers.constBegin();
+ for( ; it != mDistrListMembers.constEnd(); ++it ) {
+ QDomElement e = element.ownerDocument().createElement( "member" );
+ element.appendChild( e );
+ const Member& m = *it;
+ writeString( e, "display-name", m.displayName );
+ writeString( e, "smtp-address", m.email );
+ }
+}
+
+bool DistributionList::loadAttribute( QDomElement& element )
+{
+ const QString tagName = element.tagName();
+ switch ( tagName[0].toLatin1() ) {
+ case 'd':
+ if ( tagName == "display-name" ) {
+ setName( element.text() );
+ return true;
+ }
+ break;
+ case 'm':
+ if ( tagName == "member" ) {
+ loadDistrListMember( element );
+ return true;
+ }
+ break;
+ default:
+ break;
+ }
+ return KolabBase::loadAttribute( element );
+}
+
+bool DistributionList::saveAttributes( QDomElement& element ) const
+{
+ // Save the base class elements
+ KolabBase::saveAttributes( element );
+ writeString( element, "display-name", name() );
+ saveDistrListMembers( element );
+
+ return true;
+}
+
+bool DistributionList::loadXML( const QDomDocument& document )
+{
+ QDomElement top = document.documentElement();
+
+ if ( top.tagName() != "distribution-list" ) {
+ qWarning( "XML error: Top tag was %s instead of the expected distribution-list",
+ top.tagName().toAscii().data() );
+ 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
+ //kDebug() <<"Saving unhandled tag" << e.tagName();
+ Custom c;
+ c.app = s_unhandledTagAppName;
+ c.name = e.tagName();
+ c.value = e.text();
+ mCustomList.append( c );
+ }
+ } else
+ kDebug() <<"Node is not a comment or an element???";
+ }
+
+ return true;
+}
+
+QString DistributionList::saveXML() const
+{
+ QDomDocument document = domTree();
+ QDomElement element = document.createElement( "distribution-list" );
+ element.setAttribute( "version", "1.0" );
+ saveAttributes( element );
+ document.appendChild( element );
+ return document.toString();
+}
+
+QString DistributionList::productID() const
+{
+ // TODO should we get name/version from desktop file?
+ return QLatin1String( "Akonadi Kolab Proxy" );
+}
+
+// The saving is contactgroup -> DistributionList -> xml, this is the first part
+void DistributionList::setFields( const KABC::ContactGroup* contactGroup )
+{
+ KolabBase::setFields( contactGroup );
+
+ setName( contactGroup->name() );
+
+ // explicit contact data
+ for ( uint index = 0; index < contactGroup->dataCount(); ++index ) {
+ const KABC::ContactGroup::Data& data = contactGroup->data( index );
+
+ Member m;
+ m.displayName = data.name();
+ m.email = data.email();
+
+ mDistrListMembers.append( m );
+ }
+
+ // Hopefully all resources are available during saving, so we can look up
+ // in the addressbook to get name+email from the UID.
+ // TODO proxy should at least know the addressees it created
+ for ( uint index = 0; index < contactGroup->contactReferenceCount(); ++index ) {
+ const KABC::ContactGroup::ContactReference& reference = contactGroup->contactReference( index );
+
+ const Akonadi::Item item( reference.uid().toLongLong() );
+ Akonadi::ItemFetchJob *job = new Akonadi::ItemFetchJob( item );
+ job->fetchScope().fetchFullPayload();
+ if ( !job->exec() )
+ continue;
+
+ const Akonadi::Item::List items = job->items();
+ if ( items.count() != 1 )
+ continue;
+
+ const KABC::Addressee addressee = job->items().first().payload<KABC::Addressee>();
+
+ if ( !addressee.isEmpty() ) {
+ Member m;
+ m.displayName = addressee.formattedName();
+ m.email = reference.preferredEmail();
+ if ( m.email.isEmpty() )
+ m.email = addressee.preferredEmail();
+
+ mDistrListMembers.append( m );
+ }
+ }
+}
+
+// The loading is: xml -> DistributionList -> contactgroup, this is the second part
+void DistributionList::saveTo( KABC::ContactGroup* contactGroup )
+{
+ KolabBase::saveTo( contactGroup );
+
+ contactGroup->setName( name() );
+
+ QList<Member>::ConstIterator mit = mDistrListMembers.constBegin();
+ for ( ; mit != mDistrListMembers.constEnd(); ++mit ) {
+ const KABC::ContactGroup::Data data( (*mit).displayName, (*mit).email );
+ contactGroup->append( data );
+ }
+}
+
+// kate: space-indent on; indent-width 2; replace-tabs on;
diff --git a/libkolab/kolabformatV2/distributionlist.h b/libkolab/kolabformatV2/distributionlist.h
new file mode 100644
index 0000000..33dc61d
--- /dev/null
+++ b/libkolab/kolabformatV2/distributionlist.h
@@ -0,0 +1,96 @@
+/*
+ This file is part of Akonadi KolabProxy
+ Copyright (c) 2009 <kevin.krammer at gmx.at>
+
+ 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 KOLABDISTRIBUTIONLIST_H
+#define KOLABDISTRIBUTIONLIST_H
+
+#include "kolabbase.h"
+
+namespace KABC {
+ class ContactGroup;
+}
+
+namespace Kolab {
+
+class DistributionList : public KolabBase {
+public:
+ explicit DistributionList( const KABC::ContactGroup* contactGroup );
+ DistributionList( const QString& xml );
+ ~DistributionList();
+
+ void saveTo( KABC::ContactGroup* contactGroup );
+
+ QString type() const { return "DistributionList"; }
+
+ void setName( const QString& name );
+ QString name() const;
+
+ // Load the attributes of this class
+ bool loadAttribute( QDomElement& );
+
+ // Save the attributes of this class
+ bool saveAttributes( QDomElement& ) const;
+
+ // Load this note by reading the XML file
+ bool loadXML( const QDomDocument& xml );
+
+ // Serialize this note to an XML string
+ QString saveXML() const;
+
+ QString productID() const;
+
+protected:
+ void setFields( const KABC::ContactGroup* );
+
+private:
+ void loadDistrListMember( const QDomElement& element );
+ void saveDistrListMembers( QDomElement& element ) const;
+
+ QString mName;
+
+ struct Custom {
+ QString app;
+ QString name;
+ QString value;
+ };
+ QList<Custom> mCustomList;
+
+ struct Member {
+ QString displayName;
+ QString email;
+ };
+ QList<Member> mDistrListMembers;
+};
+
+}
+
+#endif // KOLABDISTRIBUTIONLIST_H
+// kate: space-indent on; indent-width 2; replace-tabs on;
diff --git a/libkolab/kolabformatV2/event.cpp b/libkolab/kolabformatV2/event.cpp
new file mode 100644
index 0000000..103ced3
--- /dev/null
+++ b/libkolab/kolabformatV2/event.cpp
@@ -0,0 +1,221 @@
+/*
+ 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 at 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 <kcalcore/event.h>
+#include <kdebug.h>
+
+using namespace Kolab;
+
+
+KCalCore::Event::Ptr Event::fromXml( const QDomDocument& xmlDoc, const QString& tz)
+{
+ Event event( tz );
+ event.loadXML( xmlDoc );
+ KCalCore::Event::Ptr kcalEvent( new KCalCore::Event() );
+ event.saveTo( kcalEvent );
+ return kcalEvent;
+}
+
+QString Event::eventToXML( const KCalCore::Event::Ptr &kcalEvent, const QString& tz )
+{
+ Event event( tz, kcalEvent );
+ return event.saveXML();
+}
+
+Event::Event( const QString& tz, const KCalCore::Event::Ptr &event )
+ : Incidence( tz, event ),
+ mShowTimeAs( KCalCore::Event::Opaque ), mHasEndDate( false )
+{
+ if ( event ) {
+ setFields( event );
+ }
+}
+
+Event::~Event()
+{
+}
+
+void Event::setTransparency( KCalCore::Event::Transparency transparency )
+{
+ mShowTimeAs = transparency;
+}
+
+KCalCore::Event::Transparency Event::transparency() const
+{
+ return mShowTimeAs;
+}
+
+void Event::setEndDate( const KDateTime& date )
+{
+ mEndDate = date;
+ mHasEndDate = true;
+ if ( mFloatingStatus == AllDay )
+ kDebug() <<"ERROR: Time on end date but no time on the event";
+ mFloatingStatus = HasTime;
+}
+
+void Event::setEndDate( const QDate& date )
+{
+ mEndDate = KDateTime( date );
+ mHasEndDate = true;
+ if ( mFloatingStatus == HasTime )
+ kDebug() <<"ERROR: No time on end date but time on the event";
+ 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 ) );
+}
+
+KDateTime 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( KCalCore::Event::Transparent );
+ else
+ setTransparency( KCalCore::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() == KCalCore::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().toAscii().data() );
+ 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
+ kDebug() <<"Node is not a comment or an element???";
+ }
+
+ 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 KCalCore::Event::Ptr &event )
+{
+ Incidence::setFields( event );
+
+ // note: if hasEndDate() is false and hasDuration() is true
+ // dtEnd() returns start+duration
+ if ( event->hasEndDate() || event->hasDuration() ) {
+ if ( event->allDay() ) {
+ // This is an all-day 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( const KCalCore::Event::Ptr &event )
+{
+ Incidence::saveTo( event );
+
+ event->setHasEndDate( mHasEndDate );
+ if ( mHasEndDate ) {
+ if ( mFloatingStatus == AllDay )
+ // This is an all-day event. Don't timezone move this one
+ event->setDtEnd( endDate() );
+ else
+ event->setDtEnd( utcToLocal( endDate() ) );
+ }
+ event->setTransparency( transparency() );
+}
diff --git a/libkolab/kolabformatV2/event.h b/libkolab/kolabformatV2/event.h
new file mode 100644
index 0000000..acc327b
--- /dev/null
+++ b/libkolab/kolabformatV2/event.h
@@ -0,0 +1,101 @@
+/*
+ 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 at 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 <kcalcore/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 KCalCore::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 KCalCore::Event::Ptr fromXml( const QDomDocument& xmlDoc, const QString& tz);
+
+ /// Use this to get an xml string describing this event entry
+ static QString eventToXML( const KCalCore::Event::Ptr &, const QString& tz );
+
+ /// Create a event object and
+ explicit Event( const QString& tz,
+ const KCalCore::Event::Ptr &event = KCalCore::Event::Ptr() );
+ virtual ~Event();
+
+ void saveTo( const KCalCore::Event::Ptr &event );
+
+ virtual QString type() const { return "Event"; }
+
+ virtual void setTransparency( KCalCore::Event::Transparency transparency );
+ virtual KCalCore::Event::Transparency transparency() const;
+
+ virtual void setEndDate( const KDateTime& date );
+ virtual void setEndDate( const QDate& date );
+ virtual void setEndDate( const QString& date );
+ virtual KDateTime 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 KCalCore::Event::Ptr & );
+
+ KCalCore::Event::Transparency mShowTimeAs;
+ KDateTime mEndDate;
+ bool mHasEndDate;
+};
+
+}
+
+#endif // KOLAB_EVENT_H
diff --git a/libkolab/kolabformatV2/incidence.cpp b/libkolab/kolabformatV2/incidence.cpp
new file mode 100644
index 0000000..9f4323f
--- /dev/null
+++ b/libkolab/kolabformatV2/incidence.cpp
@@ -0,0 +1,976 @@
+/*
+ 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 at 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 "akonadi-version.h"
+
+#include <QList>
+
+#include <kcalcore/journal.h>
+#include <kdebug.h>
+#include <kurl.h>
+#include <kio/netaccess.h>
+
+#include <QBitArray>
+
+using namespace Kolab;
+
+
+Incidence::Incidence( const QString& tz, const KCalCore::Incidence::Ptr &incidence )
+ : KolabBase( tz ), mFloatingStatus( Unset ), mHasAlarm( false )
+{
+ Q_UNUSED( incidence );
+}
+
+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 KDateTime& startDate )
+{
+ mStartDate = startDate;
+ if ( mFloatingStatus == AllDay )
+ kDebug() <<"ERROR: Time on start date but no time on the event";
+ mFloatingStatus = HasTime;
+}
+
+void Incidence::setStartDate( const QDate& startDate )
+{
+ mStartDate = KDateTime( startDate );
+ if ( mFloatingStatus == HasTime )
+ kDebug() <<"ERROR: No time on start date but time on the event";
+ 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 ) );
+}
+
+KDateTime 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 );
+}
+
+QList<Incidence::Attendee>& Incidence::attendees()
+{
+ return mAttendees;
+}
+
+const QList<Incidence::Attendee>& Incidence::attendees() const
+{
+ return mAttendees;
+}
+
+void Incidence::setInternalUID( const QString& iuid )
+{
+ mInternalUID = iuid;
+}
+
+QString Incidence::internalUID() const
+{
+ return mInternalUID;
+}
+
+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().toLower() != "false" );
+ else if ( tagName == "invitation-sent" )
+ // Like above, only this defaults to false
+ attendee.invitationSent = ( e.text().toLower() != "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
+ kDebug() <<"Warning: Unhandled tag" << e.tagName();
+ } else
+ kDebug() <<"Node is not a comment or an element???";
+ }
+
+ 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
+{
+ foreach ( const Attendee& attendee, mAttendees )
+ saveAttendeeAttribute( element, attendee );
+}
+
+void Incidence::saveAttachments( QDomElement& element ) const
+{
+ foreach ( KCalCore::Attachment::Ptr a, mAttachments ) {
+ if ( a->isUri() ) {
+ writeString( element, "link-attachment", a->uri() );
+ } else if ( a->isBinary() ) {
+ writeString( element, "inline-attachment", a->label() );
+ }
+ }
+}
+
+void Incidence::saveAlarms( QDomElement& element ) const
+{
+ if ( mAlarms.isEmpty() ) return;
+
+ QDomElement list = element.ownerDocument().createElement( "advanced-alarms" );
+ element.appendChild( list );
+ foreach ( KCalCore::Alarm::Ptr a, mAlarms ) {
+ QDomElement e = list.ownerDocument().createElement( "alarm" );
+ list.appendChild( e );
+
+ writeString( e, "enabled", a->enabled() ? "1" : "0" );
+ if ( a->hasStartOffset() ) {
+ writeString( e, "start-offset", QString::number( a->startOffset().asSeconds()/60 ) );
+ }
+ if ( a->hasEndOffset() ) {
+ writeString( e, "end-offset", QString::number( a->endOffset().asSeconds()/60 ) );
+ }
+ if ( a->repeatCount() ) {
+ writeString( e, "repeat-count", QString::number( a->repeatCount() ) );
+ writeString( e, "repeat-interval", QString::number( a->snoozeTime().asSeconds() ) );
+ }
+
+ switch ( a->type() ) {
+ case KCalCore::Alarm::Invalid:
+ break;
+ case KCalCore::Alarm::Display:
+ e.setAttribute( "type", "display" );
+ writeString( e, "text", a->text() );
+ break;
+ case KCalCore::Alarm::Procedure:
+ e.setAttribute( "type", "procedure" );
+ writeString( e, "program", a->programFile() );
+ writeString( e, "arguments", a->programArguments() );
+ break;
+ case KCalCore::Alarm::Email:
+ {
+ e.setAttribute( "type", "email" );
+ QDomElement addresses = e.ownerDocument().createElement( "addresses" );
+ e.appendChild( addresses );
+ foreach ( const KCalCore::Person::Ptr &person, a->mailAddresses() ) {
+ writeString( addresses, "address", person->fullName() );
+ }
+ writeString( e, "subject", a->mailSubject() );
+ writeString( e, "mail-text", a->mailText() );
+ QDomElement attachments = e.ownerDocument().createElement( "attachments" );
+ e.appendChild( attachments );
+ foreach ( const QString &attachment, a->mailAttachments() ) {
+ writeString( attachments, "attachment", attachment );
+ }
+ break;
+ }
+ case KCalCore::Alarm::Audio:
+ e.setAttribute( "type", "audio" );
+ writeString( e, "file", a->audioFile() );
+ break;
+ default:
+ kWarning() << "Unhandled alarm type:" << a->type();
+ break;
+ }
+ }
+}
+
+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 ) );
+ foreach ( const QString& recurrence, mRecurrence.days ) {
+ writeString( e, "day", recurrence );
+ }
+ 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 );
+ }
+ foreach ( const QDate& date, mRecurrence.exclusions ) {
+ writeString( e, "exclusion", dateToString( date ) );
+ }
+}
+
+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" ) {
+ //kolab/issue4229, sometimes the interval value can be empty
+ if ( e.text().isEmpty() || e.text().toInt() <= 0 ) {
+ mRecurrence.interval = 1;
+ } else {
+ mRecurrence.interval = e.text().toInt();
+ }
+ }
+ else if ( tagName == "day" ) // can be present multiple times
+ mRecurrence.days.append( e.text() );
+ else if ( tagName == "daynumber" )
+ mRecurrence.dayNumber = e.text();
+ else if ( tagName == "month" )
+ mRecurrence.month = e.text();
+ else if ( tagName == "range" ) {
+ mRecurrence.rangeType = e.attribute( "type" );
+ mRecurrence.range = e.text();
+ } else if ( tagName == "exclusion" ) {
+ mRecurrence.exclusions.append( stringToDate( e.text() ) );
+ } else
+ // TODO: Unhandled tag - save for later storage
+ kDebug() <<"Warning: Unhandled tag" << e.tagName();
+ }
+ }
+}
+
+static void loadAddressesHelper( const QDomElement& element, const KCalCore::Alarm::Ptr &a )
+{
+ 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 == "address" ) {
+ a->addMailAddress( KCalCore::Person::fromFullName( e.text() ) );
+ } else {
+ kWarning() << "Unhandled tag" << tagName;
+ }
+ }
+ }
+}
+
+static void loadAttachmentsHelper( const QDomElement& element, const KCalCore::Alarm::Ptr &a )
+{
+ 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 == "attachment" ) {
+ a->addMailAttachment( e.text() );
+ } else {
+ kWarning() << "Unhandled tag" << tagName;
+ }
+ }
+ }
+}
+
+static void loadAlarmHelper( const QDomElement& element, const KCalCore::Alarm::Ptr &a )
+{
+ 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 == "start-offset" ) {
+ a->setStartOffset( e.text().toInt()*60 );
+ } else if ( tagName == "end-offset" ) {
+ a->setEndOffset( e.text().toInt()*60 );
+ } else if ( tagName == "repeat-count" ) {
+ a->setRepeatCount( e.text().toInt() );
+ } else if ( tagName == "repeat-interval" ) {
+ a->setSnoozeTime( e.text().toInt() );
+ } else if ( tagName == "text" ) {
+ a->setText( e.text() );
+ } else if ( tagName == "program" ) {
+ a->setProgramFile( e.text() );
+ } else if ( tagName == "arguments" ) {
+ a->setProgramArguments( e.text() );
+ } else if ( tagName == "addresses" ) {
+ loadAddressesHelper( e, a );
+ } else if ( tagName == "subject" ) {
+ a->setMailSubject( e.text() );
+ } else if ( tagName == "mail-text" ) {
+ a->setMailText( e.text() );
+ } else if ( tagName == "attachments" ) {
+ loadAttachmentsHelper( e, a );
+ } else if ( tagName == "file" ) {
+ a->setAudioFile( e.text() );
+ } else if ( tagName == "enabled" ) {
+ a->setEnabled( e.text().toInt() != 0 );
+ } else {
+ kWarning() << "Unhandled tag" << tagName;
+ }
+ }
+ }
+}
+
+void Incidence::loadAlarms( const QDomElement& element )
+{
+ 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 == "alarm" ) {
+ KCalCore::Alarm::Ptr a = KCalCore::Alarm::Ptr( new KCalCore::Alarm( 0 ) );
+ a->setEnabled( true ); // default to enabled, unless some XML attribute says otherwise.
+ QString type = e.attribute( "type" );
+ if ( type == "display" ) {
+ a->setType( KCalCore::Alarm::Display );
+ } else if ( type == "procedure" ) {
+ a->setType( KCalCore::Alarm::Procedure );
+ } else if ( type == "email" ) {
+ a->setType( KCalCore::Alarm::Email );
+ } else if ( type == "audio" ) {
+ a->setType( KCalCore::Alarm::Audio );
+ } else {
+ kWarning() << "Unhandled alarm type:" << type;
+ }
+
+ loadAlarmHelper( e, a );
+ mAlarms << a;
+ } else {
+ kWarning() << "Unhandled tag" << tagName;
+ }
+ }
+ }
+}
+
+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( KCalCore::Attachment::Ptr( new KCalCore::Attachment( element.text() ) ) );
+ } else if ( tagName == "alarm" )
+ // Alarms should be minutes before. Libkcal uses event time + alarm time
+ setAlarm( - element.text().toInt() );
+ else if ( tagName == "advanced-alarms" )
+ loadAlarms( element );
+ else if ( tagName == "x-kde-internaluid" )
+ setInternalUID( element.text() );
+ else if ( tagName == "x-custom" ) {
+ loadCustomAttributes( element );
+ } else if ( tagName == "inline-attachment" ) {
+ // we handle that separately later on, so no need to create a KolabUnhandled entry for it
+ } else {
+ bool ok = KolabBase::loadAttribute( element );
+ if ( !ok ) {
+ // Unhandled tag - save for later storage
+ kDebug() <<"Saving unhandled tag" << element.tagName();
+ Custom c;
+ c.key = QByteArray( "X-KDE-KolabUnhandled-" ) + element.tagName().toLatin1();
+ 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 ) );
+ }
+ saveAlarms( element );
+ writeString( element, "x-kde-internaluid", internalUID() );
+ saveCustomAttributes( element );
+ return true;
+}
+
+void Incidence::saveCustomAttributes( QDomElement& element ) const
+{
+ foreach ( const Custom& custom, mCustomList ) {
+ QString key( custom.key );
+ Q_ASSERT( !key.isEmpty() );
+ if ( key.startsWith( QLatin1String( "X-KDE-KolabUnhandled-" ) ) ) {
+ key = key.mid( strlen( "X-KDE-KolabUnhandled-" ) );
+ writeString( element, key, custom.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", custom.value );
+ }
+ }
+}
+
+void Incidence::loadCustomAttributes( QDomElement& element )
+{
+ Custom custom;
+ custom.key = element.attribute( "key" ).toLatin1();
+ custom.value = element.attribute( "value" );
+ mCustomList.append( custom );
+}
+
+static KCalCore::Attendee::PartStat attendeeStringToStatus( const QString& s )
+{
+ if ( s == "none" )
+ return KCalCore::Attendee::NeedsAction;
+ if ( s == "tentative" )
+ return KCalCore::Attendee::Tentative;
+ if ( s == "declined" )
+ return KCalCore::Attendee::Declined;
+ if ( s == "delegated" )
+ return KCalCore::Attendee::Delegated;
+
+ // Default:
+ return KCalCore::Attendee::Accepted;
+}
+
+static QString attendeeStatusToString( KCalCore::Attendee::PartStat status )
+{
+ switch( status ) {
+ case KCalCore::Attendee::NeedsAction:
+ return "none";
+ case KCalCore::Attendee::Accepted:
+ return "accepted";
+ case KCalCore::Attendee::Declined:
+ return "declined";
+ case KCalCore::Attendee::Tentative:
+ return "tentative";
+ case KCalCore::Attendee::Delegated:
+ return "delegated";
+ case KCalCore::Attendee::Completed:
+ case KCalCore::Attendee::InProcess:
+ // These don't have any meaning in the Kolab format, so just use:
+ return "accepted";
+ default:
+ // Default for the case that there are more added later:
+ return "accepted";
+ }
+}
+
+static KCalCore::Attendee::Role attendeeStringToRole( const QString& s )
+{
+ if ( s == "optional" )
+ return KCalCore::Attendee::OptParticipant;
+ if ( s == "resource" )
+ return KCalCore::Attendee::NonParticipant;
+ return KCalCore::Attendee::ReqParticipant;
+}
+
+static QString attendeeRoleToString( KCalCore::Attendee::Role role )
+{
+ switch( role ) {
+ case KCalCore::Attendee::ReqParticipant:
+ return "required";
+ case KCalCore::Attendee::OptParticipant:
+ return "optional";
+ case KCalCore::Attendee::Chair:
+ // We don't have the notion of chair, so use
+ return "required";
+ case KCalCore::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( KCalCore::Recurrence* recur )
+{
+ mRecurrence.interval = recur->frequency();
+ switch ( recur->recurrenceType() ) {
+ case KCalCore::Recurrence::rMinutely: // Not handled by the kolab XML
+ mRecurrence.cycle = "minutely";
+ break;
+ case KCalCore::Recurrence::rHourly: // Not handled by the kolab XML
+ mRecurrence.cycle = "hourly";
+ break;
+ case KCalCore::Recurrence::rDaily:
+ mRecurrence.cycle = "daily";
+ break;
+ case KCalCore::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 KCalCore::Recurrence::rMonthlyPos: {
+ mRecurrence.cycle = "monthly";
+ mRecurrence.type = "weekday";
+ QList<KCalCore::RecurrenceRule::WDayPos> monthPositions = recur->monthPositions();
+ if ( !monthPositions.isEmpty() ) {
+ KCalCore::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 KCalCore::Recurrence::rMonthlyDay: {
+ mRecurrence.cycle = "monthly";
+ mRecurrence.type = "daynumber";
+ QList<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 KCalCore::Recurrence::rYearlyMonth: // (day n of Month Y)
+ {
+ mRecurrence.cycle = "yearly";
+ mRecurrence.type = "monthday";
+ QList<int> rmd = recur->yearDates();
+ int day = !rmd.isEmpty() ? rmd.first() : recur->startDate().day();
+ mRecurrence.dayNumber = QString::number( day );
+ QList<int> months = recur->yearMonths();
+ if ( !months.isEmpty() )
+ mRecurrence.month = s_monthName[ months.first() - 1 ]; // #### Kolab XML limitation: only one month specified
+ break;
+ }
+ case KCalCore::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 KCalCore::Recurrence::rYearlyPos: // (weekday X of week N of month Y)
+ mRecurrence.cycle = "yearly";
+ mRecurrence.type = "weekday";
+ QList<int> months = recur->yearMonths();
+ if ( !months.isEmpty() )
+ mRecurrence.month = s_monthName[ months.first() - 1 ]; // #### Kolab XML limitation: only one month specified
+ QList<KCalCore::RecurrenceRule::WDayPos> monthPositions = recur->yearPositions();
+ if ( !monthPositions.isEmpty() ) {
+ KCalCore::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 KCalCore::Incidence::Ptr &incidence )
+{
+ KolabBase::setFields( incidence );
+
+ if ( incidence->allDay() ) {
+ // This is a all-day 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->hasEnabledAlarms() ) {
+ const KCalCore::Alarm::List& alarms = incidence->alarms();
+ if ( !alarms.isEmpty() ) {
+ const KCalCore::Alarm::Ptr 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:
+ KCalCore::Attendee::List attendees = incidence->attendees();
+ foreach ( KCalCore::Attendee::Ptr kcalAttendee, attendees ) {
+ Attendee attendee;
+
+ attendee.displayName = kcalAttendee->name();
+ attendee.smtpAddress = kcalAttendee->email();
+ attendee.status = attendeeStatusToString( kcalAttendee->status() );
+ attendee.requestResponse = kcalAttendee->RSVP();
+ // TODO: KCalCore::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
+ KCalCore::Attachment::List attachments = incidence->attachments();
+ foreach ( KCalCore::Attachment::Ptr a, attachments ) {
+ mAttachments.push_back( a );
+ }
+
+ mAlarms.clear();
+
+ // Alarms
+ KCalCore::Alarm::List alarms = incidence->alarms();
+ foreach ( KCalCore::Alarm::Ptr a, alarms ) {
+ mAlarms.push_back( a );
+ }
+
+ if ( incidence->recurs() ) {
+ 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 ); //krazy:exclude=nullstrassign for old broken gcc
+ } 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() );
+ }
+
+ // Unhandled tags and other custom properties (see libkcal/customproperties.h)
+ const QMap<QByteArray, QString> map = incidence->customProperties();
+ QMap<QByteArray, QString>::ConstIterator cit = map.begin();
+ for ( ; cit != map.end() ; ++cit ) {
+ Custom c;
+ c.key = cit.key();
+ c.value = cit.value();
+ mCustomList.append( c );
+ }
+}
+
+static QBitArray daysListToBitArray( const QStringList& days )
+{
+ QBitArray arr( 7 );
+ arr.fill( false );
+ foreach ( const QString& day, days ) {
+ for ( uint i = 0; i < 7 ; ++i )
+ if ( day == s_weekDayName[i] )
+ arr.setBit( i, true );
+ }
+ return arr;
+}
+
+
+void Incidence::saveTo( const KCalCore::Incidence::Ptr &incidence )
+{
+ KolabBase::saveTo( incidence );
+
+ if ( mFloatingStatus == AllDay ) {
+ // This is an all-day event. Don't timezone move this one
+ incidence->setDtStart( startDate() );
+ incidence->setAllDay( true );
+ } else {
+ incidence->setDtStart( utcToLocal( startDate() ) );
+ incidence->setAllDay( false );
+ }
+
+ incidence->setSummary( summary() );
+ incidence->setLocation( location() );
+
+ if ( mHasAlarm && mAlarms.isEmpty() ) {
+ KCalCore::Alarm::Ptr alarm = incidence->newAlarm();
+ alarm->setStartOffset( qRound( mAlarm * 60.0 ) );
+ alarm->setEnabled( true );
+ alarm->setType( KCalCore::Alarm::Display );
+ } else if ( !mAlarms.isEmpty() ) {
+ foreach ( KCalCore::Alarm::Ptr a, mAlarms ) {
+ a->setParent( incidence.data() );
+ incidence->addAlarm( a );
+ }
+ }
+
+ if ( organizer().displayName.isEmpty() )
+ incidence->setOrganizer( organizer().smtpAddress );
+ else
+ incidence->setOrganizer( organizer().displayName + '<'
+ + organizer().smtpAddress + '>' );
+
+ incidence->clearAttendees();
+ foreach ( const Attendee& attendee, mAttendees ) {
+ KCalCore::Attendee::PartStat status = attendeeStringToStatus( attendee.status );
+ KCalCore::Attendee::Role role = attendeeStringToRole( attendee.role );
+ KCalCore::Attendee::Ptr a( new KCalCore::Attendee( attendee.displayName,
+ attendee.smtpAddress,
+ attendee.requestResponse,
+ status, role ) );
+ a->setDelegate( attendee.delegate );
+ a->setDelegator( attendee.delegator );
+ incidence->addAttendee( a );
+ }
+
+ incidence->clearAttachments();
+ foreach ( KCalCore::Attachment::Ptr a, mAttachments ) {
+ // TODO should we copy?
+ incidence->addAttachment( a );
+ }
+
+ if ( !mRecurrence.cycle.isEmpty() ) {
+ KCalCore::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 kWarning() <<"Unhandled monthly recurrence type" << mRecurrence.type;
+ } 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 kWarning() <<"Unhandled yearly recurrence type" << mRecurrence.type;
+ } else kWarning() <<"Unhandled recurrence cycle" << mRecurrence.cycle;
+
+ 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() );
+ }
+
+ foreach ( const Custom& custom, mCustomList ) {
+ incidence->setNonKDECustomProperty( custom.key, custom.value );
+ }
+
+}
+
+QString Incidence::productID() const
+{
+ return QString( "Akonadi %1, Kolab resource" ).arg( AKONADI_VERSION );
+}
+
+// Unhandled KCalCore::Incidence fields:
+// revision, status (unused), priority (done in tasks), attendee.uid,
+// mComments, mReadOnly
+
diff --git a/libkolab/kolabformatV2/incidence.h b/libkolab/kolabformatV2/incidence.h
new file mode 100644
index 0000000..2b96776
--- /dev/null
+++ b/libkolab/kolabformatV2/incidence.h
@@ -0,0 +1,166 @@
+/*
+ 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 at 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 <kcalcore/incidence.h>
+
+#include "kolabbase.h"
+
+class QDomElement;
+
+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 KCalCore::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
+ QList<QDate> exclusions;
+ };
+
+ struct Attendee : Email {
+ Attendee() : requestResponse( true ), invitationSent( false ) {}
+ QString status;
+ bool requestResponse;
+ bool invitationSent;
+ QString role;
+ QString delegate;
+ QString delegator;
+ };
+
+ explicit Incidence( const QString& tz, const KCalCore::Incidence::Ptr &incidence = KCalCore::Incidence::Ptr() );
+
+public:
+ virtual ~Incidence();
+
+ void saveTo( const KCalCore::Incidence::Ptr &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 KDateTime& startDate );
+ virtual void setStartDate( const QDate& startDate );
+ virtual void setStartDate( const QString& startDate );
+ virtual KDateTime startDate() const;
+
+ virtual void setAlarm( float alarm );
+ virtual float alarm() const;
+
+ virtual void setRecurrence( KCalCore::Recurrence* recur );
+ virtual Recurrence recurrence() const;
+
+ virtual void addAttendee( const Attendee& attendee );
+ QList<Attendee>& attendees();
+ const QList<Attendee>& attendees() const;
+
+ virtual QString type() const { return "Incidence"; }
+ /**
+ * 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;
+
+ // 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 KCalCore::Incidence::Ptr & );
+
+ bool loadAttendeeAttribute( QDomElement&, Attendee& );
+ void saveAttendeeAttribute( QDomElement& element,
+ const Attendee& attendee ) const;
+ void saveAttendees( QDomElement& element ) const;
+ void saveAttachments( QDomElement& element ) const;
+
+ void loadAlarms( const QDomElement& element );
+ void saveAlarms( QDomElement& element ) const;
+
+ void loadRecurrence( const QDomElement& element );
+ void saveRecurrence( QDomElement& element ) const;
+ void saveCustomAttributes( QDomElement& element ) const;
+ void loadCustomAttributes( QDomElement& element );
+
+ QString productID() const;
+
+ QString mSummary;
+ QString mLocation;
+ Email mOrganizer;
+ KDateTime mStartDate;
+ FloatingStatus mFloatingStatus;
+ float mAlarm;
+ bool mHasAlarm;
+ Recurrence mRecurrence;
+ QList<Attendee> mAttendees;
+ QList<KCalCore::Alarm::Ptr> mAlarms;
+ QList<KCalCore::Attachment::Ptr> mAttachments;
+ QString mInternalUID;
+
+ struct Custom {
+ QByteArray key;
+ QString value;
+ };
+ QList<Custom> mCustomList;
+
+};
+
+}
+
+#endif // KOLAB_INCIDENCE_H
diff --git a/libkolab/kolabformatV2/journal.cpp b/libkolab/kolabformatV2/journal.cpp
new file mode 100644
index 0000000..c9c2c1b
--- /dev/null
+++ b/libkolab/kolabformatV2/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 at 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 "akonadi-version.h"
+
+#include <kdebug.h>
+
+using namespace Kolab;
+
+
+KCalCore::Journal::Ptr Journal::fromXml( const QDomDocument& xmlDoc, const QString& tz )
+{
+ Journal journal( tz );
+ journal.loadXML( xmlDoc );
+ KCalCore::Journal::Ptr kcalJournal( new KCalCore::Journal() );
+ journal.saveTo( kcalJournal );
+ return kcalJournal;
+}
+
+QString Journal::journalToXML( const KCalCore::Journal::Ptr &kcalJournal, const QString& tz )
+{
+ Journal journal( tz, kcalJournal );
+ return journal.saveXML();
+}
+
+Journal::Journal( const QString& tz, const KCalCore::Journal::Ptr &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 KDateTime& startDate )
+{
+ mStartDate = startDate;
+}
+
+KDateTime Journal::startDate() const
+{
+ return mStartDate;
+}
+
+void Journal::setEndDate( const KDateTime& endDate )
+{
+ mEndDate = endDate;
+}
+
+KDateTime 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().toAscii().data() );
+ 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( const KCalCore::Journal::Ptr &journal )
+{
+ KolabBase::saveTo( journal );
+
+ journal->setSummary( summary() );
+ journal->setDtStart( utcToLocal( startDate() ) );
+}
+
+void Journal::setFields( const KCalCore::Journal::Ptr &journal )
+{
+ // Set baseclass fields
+ KolabBase::setFields( journal );
+
+ // Set our own fields
+ setSummary( journal->summary() );
+ setStartDate( localToUTC( journal->dtStart() ) );
+}
+
+QString Journal::productID() const
+{
+ return QString( "Akonadi " ) + AKONADI_VERSION + ", Kolab resource";
+}
diff --git a/libkolab/kolabformatV2/journal.h b/libkolab/kolabformatV2/journal.h
new file mode 100644
index 0000000..a049c86
--- /dev/null
+++ b/libkolab/kolabformatV2/journal.h
@@ -0,0 +1,101 @@
+/*
+ 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 at 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 <kcalcore/journal.h>
+
+#include <kolabbase.h>
+
+class QDomElement;
+
+namespace Kolab {
+
+/**
+ * This class represents a journal entry, and knows how to load/save it
+ * from/to XML, and from/to a KCalCore::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 KCalCore::Journal::Ptr fromXml( const QDomDocument& xmlDoc, const QString& tz );
+
+ /// Use this to get an xml string describing this journal entry
+ static QString journalToXML( const KCalCore::Journal::Ptr &, const QString& tz );
+
+ explicit Journal( const QString& tz, const KCalCore::Journal::Ptr &journal = KCalCore::Journal::Ptr() );
+ virtual ~Journal();
+
+ virtual QString type() const { return "Journal"; }
+
+ void saveTo( const KCalCore::Journal::Ptr &journal );
+
+ virtual void setSummary( const QString& summary );
+ virtual QString summary() const;
+
+ virtual void setStartDate( const KDateTime& startDate );
+ virtual KDateTime startDate() const;
+
+ virtual void setEndDate( const KDateTime& endDate );
+ virtual KDateTime 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 KCalCore::Journal::Ptr & );
+
+ QString productID() const;
+
+ QString mSummary;
+ KDateTime mStartDate;
+ KDateTime mEndDate;
+};
+
+}
+
+#endif // KOLAB_JOURNAL_H
diff --git a/libkolab/kolabformatV2/kolabbase.cpp b/libkolab/kolabformatV2/kolabbase.cpp
new file mode 100644
index 0000000..4315f3e
--- /dev/null
+++ b/libkolab/kolabformatV2/kolabbase.cpp
@@ -0,0 +1,500 @@
+/*
+ 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 at 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 "kolabbase.h"
+
+#include <kabc/addressee.h>
+#include <kabc/contactgroup.h>
+#include <kcalcore/incidence.h>
+#include <kcalcore/journal.h>
+#include <ksystemtimezone.h>
+#include <kdebug.h>
+
+using namespace Kolab;
+
+KolabBase::KolabBase( const QString& tz )
+ : mCreationDate( QDateTime::currentDateTime() ),
+ mLastModified( KDateTime::currentUtcDateTime() ),
+ mSensitivity( Public ),
+ mTimeZone( KSystemTimeZones::zone( tz ) ),
+ mHasPilotSyncId( false ), mHasPilotSyncStatus( false )
+{
+}
+
+KolabBase::~KolabBase()
+{
+}
+
+void KolabBase::setFields( const KCalCore::Incidence::Ptr &incidence )
+{
+ // So far unhandled KCalCore::IncidenceBase fields:
+ // mPilotID, mSyncStatus, mFloats
+
+ setUid( incidence->uid() );
+ setBody( incidence->description() );
+ setCategories( incidence->categoriesStr() );
+ setCreationDate( localToUTC( incidence->created() ) );
+ setLastModified( incidence->lastModified() );
+ setSensitivity( static_cast<Sensitivity>( incidence->secrecy() ) );
+ // TODO: Attachments
+}
+
+void KolabBase::saveTo( const KCalCore::Incidence::Ptr &incidence ) const
+{
+ incidence->setUid( uid() );
+ incidence->setDescription( body() );
+ incidence->setCategories( categories() );
+ incidence->setCreated( utcToLocal( creationDate() ) );
+ incidence->setLastModified( lastModified() );
+ switch( sensitivity() ) {
+ case 1:
+ incidence->setSecrecy( KCalCore::Incidence::SecrecyPrivate );
+ break;
+ case 2:
+ incidence->setSecrecy( KCalCore::Incidence::SecrecyConfidential );
+ break;
+ default:
+ incidence->setSecrecy( KCalCore::Incidence::SecrecyPublic );
+ break;
+ }
+
+ // TODO: Attachments
+}
+
+void KolabBase::setFields( const KABC::Addressee* addressee )
+{
+ // An addressee does not have a creation date, so somehow we should
+ // make one, if this is a new entry
+
+ setUid( addressee->uid() );
+ setBody( addressee->note() );
+ setCategories( addressee->categories().join( "," ) );
+
+ // Set creation-time and last-modification-time
+ const QString creationString = addressee->custom( "KOLAB", "CreationDate" );
+ kDebug() <<"Creation time string:" << creationString;
+ KDateTime creationDate;
+ if ( creationString.isEmpty() ) {
+ creationDate = KDateTime::currentDateTime(KDateTime::Spec( mTimeZone ) );
+ kDebug() <<"Creation date set to current time";
+ }
+ else {
+ creationDate = stringToDateTime( creationString );
+ kDebug() <<"Creation date loaded";
+ }
+ KDateTime modified = KDateTime( addressee->revision(), mTimeZone );
+ if ( !modified.isValid() )
+ modified = KDateTime::currentUtcDateTime();
+ setLastModified( modified );
+ if ( modified < creationDate ) {
+ // It's not possible that the modification date is earlier than creation
+ creationDate = modified;
+ kDebug() <<"Creation date set to modification date";
+ }
+ setCreationDate( creationDate );
+ const QString newCreationDate = dateTimeToString( creationDate );
+ if ( creationString != newCreationDate ) {
+ // We modified the creation date, so store it for future reference
+ const_cast<KABC::Addressee*>( addressee )
+ ->insertCustom( "KOLAB", "CreationDate", newCreationDate );
+ kDebug() <<"Creation date modified. New one:" << newCreationDate;
+ }
+
+ switch( addressee->secrecy().type() ) {
+ case KABC::Secrecy::Private:
+ setSensitivity( Private );
+ break;
+ case KABC::Secrecy::Confidential:
+ setSensitivity( Confidential );
+ break;
+ default:
+ setSensitivity( Public );
+ }
+
+ // TODO: Attachments
+}
+
+void KolabBase::saveTo( KABC::Addressee* addressee ) const
+{
+ addressee->setUid( uid() );
+ addressee->setNote( body() );
+ addressee->setCategories( categories().split( ',', QString::SkipEmptyParts ) );
+ addressee->setRevision( lastModified().toZone( mTimeZone ).dateTime() );
+ addressee->insertCustom( "KOLAB", "CreationDate",
+ dateTimeToString( creationDate() ) );
+
+ switch( sensitivity() ) {
+ case Private:
+ addressee->setSecrecy( KABC::Secrecy( KABC::Secrecy::Private ) );
+ break;
+ case Confidential:
+ addressee->setSecrecy( KABC::Secrecy( KABC::Secrecy::Confidential ) );
+ break;
+ default:
+ addressee->setSecrecy( KABC::Secrecy( KABC::Secrecy::Public ) );
+ break;
+ }
+ // TODO: Attachments
+}
+
+void KolabBase::setFields( const KABC::ContactGroup* contactGroup )
+{
+ // A contactgroup does not have a creation date, so somehow we should
+ // make one, if this is a new entry
+
+ setUid( contactGroup->id() );
+
+ // Set creation-time and last-modification-time
+ KDateTime creationDate = KDateTime::currentDateTime( KDateTime::Spec( mTimeZone ) );
+ kDebug() <<"Creation date set to current time";
+
+ KDateTime modified = KDateTime::currentUtcDateTime();
+ setLastModified( modified );
+ if ( modified < creationDate ) {
+ // It's not possible that the modification date is earlier than creation
+ creationDate = modified;
+ kDebug() <<"Creation date set to modification date";
+ }
+ setCreationDate( creationDate );
+}
+
+void KolabBase::saveTo( KABC::ContactGroup* contactGroup ) const
+{
+ contactGroup->setId( uid() );
+}
+
+void KolabBase::setUid( const QString& uid )
+{
+ mUid = uid;
+}
+
+QString KolabBase::uid() const
+{
+ return mUid;
+}
+
+void KolabBase::setBody( const QString& body )
+{
+ mBody = body;
+}
+
+QString KolabBase::body() const
+{
+ return mBody;
+}
+
+void KolabBase::setCategories( const QString& categories )
+{
+ mCategories = categories;
+}
+
+QString KolabBase::categories() const
+{
+ return mCategories;
+}
+
+void KolabBase::setCreationDate( const KDateTime& date )
+{
+ mCreationDate = date;
+}
+
+KDateTime KolabBase::creationDate() const
+{
+ return mCreationDate;
+}
+
+void KolabBase::setLastModified( const KDateTime& date )
+{
+ mLastModified = date;
+}
+
+KDateTime KolabBase::lastModified() const
+{
+ return mLastModified;
+}
+
+void KolabBase::setSensitivity( Sensitivity sensitivity )
+{
+ mSensitivity = sensitivity;
+}
+
+KolabBase::Sensitivity KolabBase::sensitivity() const
+{
+ return mSensitivity;
+}
+
+void KolabBase::setPilotSyncId( unsigned long id )
+{
+ mHasPilotSyncId = true;
+ mPilotSyncId = id;
+}
+
+bool KolabBase::hasPilotSyncId() const
+{
+ return mHasPilotSyncId;
+}
+
+unsigned long KolabBase::pilotSyncId() const
+{
+ return mPilotSyncId;
+}
+
+void KolabBase::setPilotSyncStatus( int status )
+{
+ mHasPilotSyncStatus = true;
+ mPilotSyncStatus = status;
+}
+
+bool KolabBase::hasPilotSyncStatus() const
+{
+ return mHasPilotSyncStatus;
+}
+
+int KolabBase::pilotSyncStatus() const
+{
+ return mPilotSyncStatus;
+}
+
+bool KolabBase::loadEmailAttribute( QDomElement& element, Email& email )
+{
+ for ( QDomNode n = element.firstChild(); !n.isNull(); n = n.nextSibling() ) {
+ if ( n.isComment() )
+ continue;
+ if ( n.isElement() ) {
+ QDomElement e = n.toElement();
+ const QString tagName = e.tagName();
+
+ if ( tagName == "display-name" )
+ email.displayName = e.text();
+ else if ( tagName == "smtp-address" )
+ email.smtpAddress = e.text();
+ else
+ // TODO: Unhandled tag - save for later storage
+ kDebug() <<"Warning: Unhandled tag" << e.tagName();
+ } else
+ kDebug() <<"Node is not a comment or an element???";
+ }
+
+ return true;
+}
+
+void KolabBase::saveEmailAttribute( QDomElement& element, const Email& email,
+ const QString& tagName ) const
+{
+ QDomElement e = element.ownerDocument().createElement( tagName );
+ element.appendChild( e );
+ writeString( e, "display-name", email.displayName );
+ writeString( e, "smtp-address", email.smtpAddress );
+}
+
+bool KolabBase::loadAttribute( QDomElement& element )
+{
+ const QString tagName = element.tagName();
+ switch ( tagName[0].toLatin1() ) {
+ case 'u':
+ if ( tagName == "uid" ) {
+ setUid( element.text() );
+ return true;
+ }
+ break;
+ case 'b':
+ if ( tagName == "body" ) {
+ setBody( element.text() );
+ return true;
+ }
+ break;
+ case 'c':
+ if ( tagName == "categories" ) {
+ setCategories( element.text() );
+ return true;
+ }
+ if ( tagName == "creation-date" ) {
+ setCreationDate( stringToDateTime( element.text() ) );
+ return true;
+ }
+ break;
+ case 'l':
+ if ( tagName == "last-modification-date" ) {
+ setLastModified( stringToDateTime( element.text() ) );
+ return true;
+ }
+ break;
+ case 's':
+ if ( tagName == "sensitivity" ) {
+ setSensitivity( stringToSensitivity( element.text() ) );
+ return true;
+ }
+ break;
+ case 'p':
+ if ( tagName == "product-id" )
+ return true; // ignore this field
+ if ( tagName == "pilot-sync-id" ) {
+ setPilotSyncId( element.text().toULong() );
+ return true;
+ }
+ if ( tagName == "pilot-sync-status" ) {
+ setPilotSyncStatus( element.text().toInt() );
+ return true;
+ }
+ break;
+ default:
+ break;
+ }
+ return false;
+}
+
+bool KolabBase::saveAttributes( QDomElement& element ) const
+{
+ writeString( element, "product-id", productID() );
+ writeString( element, "uid", uid() );
+ writeString( element, "body", body() );
+ writeString( element, "categories", categories() );
+ writeString( element, "creation-date", dateTimeToString( creationDate().toUtc() ) );
+ writeString( element, "last-modification-date", dateTimeToString( lastModified().toUtc() ) );
+ writeString( element, "sensitivity", sensitivityToString( sensitivity() ) );
+ if ( hasPilotSyncId() )
+ writeString( element, "pilot-sync-id", QString::number( pilotSyncId() ) );
+ if ( hasPilotSyncStatus() )
+ writeString( element, "pilot-sync-status", QString::number( pilotSyncStatus() ) );
+ return true;
+}
+
+bool KolabBase::load( const QString& xml )
+{
+ const QDomDocument document = loadDocument( xml );
+ if ( document.isNull() )
+ return false;
+ // XML file loaded into tree. Now parse it
+ return loadXML( document );
+}
+
+QDomDocument KolabBase::loadDocument( const QString& xmlData )
+{
+ QString errorMsg;
+ int errorLine, errorColumn;
+ QDomDocument document;
+ bool ok = document.setContent( xmlData, &errorMsg, &errorLine, &errorColumn );
+
+ if ( !ok ) {
+ qWarning( "Error loading document: %s, line %d, column %d", qPrintable( errorMsg ), errorLine, errorColumn );
+ return QDomDocument();
+ }
+
+ return document;
+}
+
+QDomDocument KolabBase::domTree()
+{
+ QDomDocument document;
+
+ QString p = "version=\"1.0\" encoding=\"UTF-8\"";
+ document.appendChild(document.createProcessingInstruction( "xml", p ) );
+
+ return document;
+}
+
+
+QString KolabBase::dateTimeToString( const KDateTime& time )
+{
+ return time.toString( KDateTime::ISODate );
+}
+
+QString KolabBase::dateToString( const QDate& date )
+{
+ return date.toString( Qt::ISODate );
+}
+
+KDateTime KolabBase::stringToDateTime( const QString& _date )
+{
+ const QString date( _date );
+ return KDateTime::fromString( date, KDateTime::ISODate );
+}
+
+QDate KolabBase::stringToDate( const QString& date )
+{
+ return QDate::fromString( date, Qt::ISODate );
+}
+
+QString KolabBase::sensitivityToString( Sensitivity s )
+{
+ switch( s ) {
+ case Private: return "private";
+ case Confidential: return "confidential";
+ case Public: return "public";
+ }
+
+ return "What what what???";
+}
+
+KolabBase::Sensitivity KolabBase::stringToSensitivity( const QString& s )
+{
+ if ( s == "private" )
+ return Private;
+ if ( s == "confidential" )
+ return Confidential;
+ return Public;
+}
+
+QString KolabBase::colorToString( const QColor& color )
+{
+ // Color is in the format "#RRGGBB"
+ return color.name();
+}
+
+QColor KolabBase::stringToColor( const QString& s )
+{
+ return QColor( s );
+}
+
+void KolabBase::writeString( QDomElement& element, const QString& tag,
+ const QString& tagString )
+{
+ if ( !tagString.isEmpty() ) {
+ QDomElement e = element.ownerDocument().createElement( tag );
+ QDomText t = element.ownerDocument().createTextNode( tagString );
+ e.appendChild( t );
+ element.appendChild( e );
+ }
+}
+
+KDateTime KolabBase::localToUTC( const KDateTime& time ) const
+{
+ return time.toUtc();
+}
+
+KDateTime KolabBase::utcToLocal( const KDateTime& time ) const
+{
+ KDateTime dt = time;
+ dt.setTimeSpec( KDateTime::UTC );
+ return dt;
+}
diff --git a/libkolab/kolabformatV2/kolabbase.h b/libkolab/kolabformatV2/kolabbase.h
new file mode 100644
index 0000000..5044bc3
--- /dev/null
+++ b/libkolab/kolabformatV2/kolabbase.h
@@ -0,0 +1,183 @@
+/*
+ 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 at 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 KOLABBASE_H
+#define KOLABBASE_H
+
+
+#include <kcalcore/incidence.h>
+
+#include <KDateTime>
+#include <KTimeZone>
+
+#include <QColor>
+#include <qdom.h>
+
+namespace KABC {
+ class Addressee;
+ class ContactGroup;
+}
+
+namespace Kolab {
+
+class KolabBase {
+public:
+ struct Email {
+ public:
+ Email( const QString& name = QString(),
+ const QString& email = QString() )
+ : displayName( name ), smtpAddress( email )
+ {
+ }
+
+ QString displayName;
+ QString smtpAddress;
+ };
+
+ enum Sensitivity { Public = 0, Private = 1, Confidential = 2 };
+
+ explicit KolabBase( const QString& time_zone = QString() );
+ virtual ~KolabBase();
+
+ // Return a string identifying this type
+ virtual QString type() const = 0;
+
+ virtual void setUid( const QString& uid );
+ virtual QString uid() const;
+
+ virtual void setBody( const QString& body );
+ virtual QString body() const;
+
+ virtual void setCategories( const QString& categories );
+ virtual QString categories() const;
+
+ virtual void setCreationDate( const KDateTime& date );
+ virtual KDateTime creationDate() const;
+
+ virtual void setLastModified( const KDateTime& date );
+ virtual KDateTime lastModified() const;
+
+ virtual void setSensitivity( Sensitivity sensitivity );
+ virtual Sensitivity sensitivity() const;
+
+ virtual void setPilotSyncId( unsigned long id );
+ virtual bool hasPilotSyncId() const;
+ virtual unsigned long pilotSyncId() const;
+
+ virtual void setPilotSyncStatus( int status );
+ virtual bool hasPilotSyncStatus() const;
+ virtual int pilotSyncStatus() const;
+
+ // String - Date conversion methods
+ static QString dateTimeToString( const KDateTime& time );
+ static QString dateToString( const QDate& date );
+ static KDateTime stringToDateTime( const QString& time );
+ static QDate stringToDate( const QString& date );
+
+ // String - Sensitivity conversion methods
+ static QString sensitivityToString( Sensitivity );
+ static Sensitivity stringToSensitivity( const QString& );
+
+ // String - Color conversion methods
+ static QString colorToString( const QColor& );
+ static QColor stringToColor( const QString& );
+
+ // Load this object by reading the XML file
+ bool load( const QString& xml );
+ static QDomDocument loadDocument( const QString& xmlData );
+
+ // Load this QDomDocument
+ virtual bool loadXML( const QDomDocument& xml ) = 0;
+
+ // Serialize this object to an XML string
+ virtual QString saveXML() const = 0;
+
+protected:
+ /// Read all known fields from this ical incidence
+ void setFields( const KCalCore::Incidence::Ptr & );
+
+ /// Save all known fields into this ical incidence
+ void saveTo( const KCalCore::Incidence::Ptr & ) const;
+
+ /// Read all known fields from this contact
+ void setFields( const KABC::Addressee* );
+
+ /// Save all known fields into this contact
+ void saveTo( KABC::Addressee* ) const;
+
+ /// Read all known fields from this contact group
+ void setFields( const KABC::ContactGroup* );
+
+ /// Save all known fields into this contact groupd
+ void saveTo( KABC::ContactGroup* ) const;
+
+ // This just makes the initial dom tree with version and doctype
+ static QDomDocument domTree();
+
+ bool loadEmailAttribute( QDomElement& element, Email& email );
+
+ void saveEmailAttribute( QDomElement& element, const Email& email,
+ const QString& tagName = "email" ) const;
+
+ // Load the attributes of this class
+ virtual bool loadAttribute( QDomElement& );
+
+ // Save the attributes of this class
+ virtual bool saveAttributes( QDomElement& ) const;
+
+ // Return the product ID
+ virtual QString productID() const = 0;
+
+ // Write a string tag
+ static void writeString( QDomElement&, const QString&, const QString& );
+
+ KDateTime localToUTC( const KDateTime& time ) const;
+ KDateTime utcToLocal( const KDateTime& time ) const;
+
+ QString mUid;
+ QString mBody;
+ QString mCategories;
+ KDateTime mCreationDate;
+ KDateTime mLastModified;
+ Sensitivity mSensitivity;
+ KTimeZone mTimeZone;
+
+ // KPilot synchronization stuff
+ bool mHasPilotSyncId, mHasPilotSyncStatus;
+ unsigned long mPilotSyncId;
+ int mPilotSyncStatus;
+};
+
+}
+
+#endif // KOLABBASE_H
diff --git a/libkolab/kolabformatV2/note.cpp b/libkolab/kolabformatV2/note.cpp
new file mode 100644
index 0000000..65afebe
--- /dev/null
+++ b/libkolab/kolabformatV2/note.cpp
@@ -0,0 +1,230 @@
+/*
+ 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 at 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 "note.h"
+#include "akonadi-version.h"
+
+#include <kcalcore/journal.h>
+#include <kdebug.h>
+
+using namespace Kolab;
+
+
+KCalCore::Journal::Ptr Note::xmlToJournal( const QString& xml )
+{
+ Note note;
+ note.load( xml );
+ KCalCore::Journal::Ptr journal( new KCalCore::Journal() );
+ note.saveTo( journal );
+ return journal;
+}
+
+QString Note::journalToXML( const KCalCore::Journal::Ptr &journal )
+{
+ Note note( journal );
+ return note.saveXML();
+}
+
+Note::Note( const KCalCore::Journal::Ptr &journal ) : mRichText( false )
+{
+ if ( journal )
+ setFields( journal );
+}
+
+Note::~Note()
+{
+}
+
+void Note::setSummary( const QString& summary )
+{
+ mSummary = summary;
+}
+
+QString Note::summary() const
+{
+ return mSummary;
+}
+
+void Note::setBackgroundColor( const QColor& bgColor )
+{
+ mBackgroundColor = bgColor;
+}
+
+QColor Note::backgroundColor() const
+{
+ return mBackgroundColor;
+}
+
+void Note::setForegroundColor( const QColor& fgColor )
+{
+ mForegroundColor = fgColor;
+}
+
+QColor Note::foregroundColor() const
+{
+ return mForegroundColor;
+}
+
+void Note::setRichText( bool richText )
+{
+ mRichText = richText;
+}
+
+bool Note::richText() const
+{
+ return mRichText;
+}
+
+bool Note::loadAttribute( QDomElement& element )
+{
+ QString tagName = element.tagName();
+ if ( tagName == "summary" )
+ setSummary( element.text() );
+ else if ( tagName == "foreground-color" )
+ setForegroundColor( stringToColor( element.text() ) );
+ else if ( tagName == "background-color" )
+ setBackgroundColor( stringToColor( element.text() ) );
+ else if ( tagName == "knotes-richtext" )
+ mRichText = ( element.text() == "true" );
+ else
+ return KolabBase::loadAttribute( element );
+
+ // We handled this
+ return true;
+}
+
+bool Note::saveAttributes( QDomElement& element ) const
+{
+ // Save the base class elements
+ KolabBase::saveAttributes( element );
+
+ // Save the elements
+#if 0
+ QDomComment c = element.ownerDocument().createComment( "Note specific attributes" );
+ element.appendChild( c );
+#endif
+
+ writeString( element, "summary", summary() );
+ if ( foregroundColor().isValid() )
+ writeString( element, "foreground-color", colorToString( foregroundColor() ) );
+ if ( backgroundColor().isValid() )
+ writeString( element, "background-color", colorToString( backgroundColor() ) );
+ writeString( element, "knotes-richtext", mRichText ? "true" : "false" );
+
+ return true;
+}
+
+
+bool Note::loadXML( const QDomDocument& document )
+{
+ QDomElement top = document.documentElement();
+
+ if ( top.tagName() != "note" ) {
+ qWarning( "XML error: Top tag was %s instead of the expected note",
+ top.tagName().toAscii().data() );
+ 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 ) )
+ // TODO: Unhandled tag - save for later storage
+ kDebug() <<"Warning: Unhandled tag" << e.tagName();
+ } else
+ kDebug() <<"Node is not a comment or an element???";
+ }
+
+ return true;
+}
+
+QString Note::saveXML() const
+{
+ QDomDocument document = domTree();
+ QDomElement element = document.createElement( "note" );
+ element.setAttribute( "version", "1.0" );
+ saveAttributes( element );
+ document.appendChild( element );
+ return document.toString();
+}
+
+void Note::setFields( const KCalCore::Journal::Ptr &journal )
+{
+ KolabBase::setFields( journal );
+
+ setSummary( journal->summary() );
+
+ QString property = journal->customProperty( "KNotes", "BgColor" );
+ if ( !property.isEmpty() ) {
+ setBackgroundColor( property );
+ } else {
+ setBackgroundColor( "yellow" );
+ }
+
+ property = journal->customProperty( "KNotes", "FgColor" );
+ if ( !property.isEmpty() ) {
+ setForegroundColor( property );
+ } else {
+ setForegroundColor( "black" );
+ }
+
+ property = journal->customProperty( "KNotes", "RichText" );
+ if ( !property.isEmpty() ) {
+ setRichText( property == "true" ? true : false );
+ } else {
+ setRichText( "false" );
+ }
+}
+
+void Note::saveTo( const KCalCore::Journal::Ptr &journal )
+{
+ KolabBase::saveTo( journal );
+
+ // TODO: background and foreground
+ journal->setSummary( summary() );
+ if ( foregroundColor().isValid() )
+ journal->setCustomProperty( "KNotes", "FgColor",
+ colorToString( foregroundColor() ) );
+ if ( backgroundColor().isValid() )
+ journal->setCustomProperty( "KNotes", "BgColor",
+ colorToString( backgroundColor() ) );
+ journal->setCustomProperty( "KNotes", "RichText",
+ richText() ? "true" : "false" );
+}
+
+QString Note::productID() const
+{
+ return QString( "KNotes %1, Kolab resource" ).arg( AKONADI_VERSION );
+}
diff --git a/libkolab/kolabformatV2/note.h b/libkolab/kolabformatV2/note.h
new file mode 100644
index 0000000..b0291c4
--- /dev/null
+++ b/libkolab/kolabformatV2/note.h
@@ -0,0 +1,109 @@
+/*
+ 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 at 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_NOTE_H
+#define KOLAB_NOTE_H
+
+#include <kcalcore/journal.h>
+
+#include <kolabbase.h>
+
+class QDomElement;
+
+namespace Kolab {
+
+/**
+ * This class represents a note, and knows how to load/save it
+ * from/to XML, and from/to a KCalCore::Journal.
+ * The instances of this class are temporary, only used to convert
+ * one to the other.
+ */
+class Note : public KolabBase {
+public:
+ /// Use this to parse an xml string to a journal entry
+ /// The caller is responsible for deleting the returned journal
+ static KCalCore::Journal::Ptr xmlToJournal( const QString& xml );
+
+ /// Use this to get an xml string describing this journal entry
+ static QString journalToXML( const KCalCore::Journal::Ptr & );
+
+ /// Create a note object and
+ explicit Note( const KCalCore::Journal::Ptr &journal = KCalCore::Journal::Ptr() );
+ virtual ~Note();
+
+ void saveTo( const KCalCore::Journal::Ptr &journal );
+
+ virtual QString type() const { return "Note"; }
+
+ virtual void setSummary( const QString& summary );
+ virtual QString summary() const;
+
+ virtual void setBackgroundColor( const QColor& bgColor );
+ virtual QColor backgroundColor() const;
+
+ virtual void setForegroundColor( const QColor& fgColor );
+ virtual QColor foregroundColor() const;
+
+ virtual void setRichText( bool richText );
+ virtual bool richText() const;
+
+ // Load the attributes of this class
+ virtual bool loadAttribute( QDomElement& );
+
+ // Save the attributes of this class
+ virtual bool saveAttributes( QDomElement& ) const;
+
+ // Load this note by reading the XML file
+ virtual bool loadXML( const QDomDocument& xml );
+
+ // Serialize this note to an XML string
+ virtual QString saveXML() const;
+
+protected:
+ // Read all known fields from this ical incidence
+ void setFields( const KCalCore::Journal::Ptr & );
+
+ // Save all known fields into this ical incidence
+ void saveTo( const KCalCore::Incidence::Ptr & ) const;
+
+ QString productID() const;
+
+ QString mSummary;
+ QColor mBackgroundColor;
+ QColor mForegroundColor;
+ bool mRichText;
+};
+
+}
+
+#endif // KOLAB_NOTE_H
diff --git a/libkolab/kolabformatV2/task.cpp b/libkolab/kolabformatV2/task.cpp
new file mode 100644
index 0000000..974fdb2
--- /dev/null
+++ b/libkolab/kolabformatV2/task.cpp
@@ -0,0 +1,455 @@
+/*
+ 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 at 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 <kcalcore/todo.h>
+#include <kdebug.h>
+
+using namespace Kolab;
+
+// Kolab Storage Specification:
+// "The priority can be a number between 1 and 5, with 1 being the highest priority."
+// iCalendar (RFC 2445):
+// "The priority is specified as an integer in the range
+// zero to nine. A value of zero specifies an
+// undefined priority. A value of one is the
+// highest priority. A value of nine is the lowest
+// priority."
+
+static int kcalPriorityToKolab( const int kcalPriority )
+{
+ if ( kcalPriority >= 0 && kcalPriority <= 9 ) {
+ // We'll map undefined (0) to 3 (default)
+ // 0 1 2 3 4 5 6 7 8 9
+ static const int priorityMap[10] = { 3, 1, 1, 2, 2, 3, 3, 4, 4, 5 };
+ return priorityMap[kcalPriority];
+ }
+ else {
+ kWarning() << "Got invalid priority" << kcalPriority;
+ return 3;
+ }
+}
+
+static int kolabPrioritytoKCal( const int kolabPriority )
+{
+ if ( kolabPriority >= 1 && kolabPriority <= 5 ) {
+ // 1 2 3 4 5
+ static const int priorityMap[5] = { 1, 3, 5, 7, 9 };
+ return priorityMap[kolabPriority - 1];
+ }
+ else {
+ kWarning() << "Got invalid priority" << kolabPriority;
+ return 5;
+ }
+}
+
+KCalCore::Todo::Ptr Task::fromXml( const QDomDocument& xmlDoc, const QString& tz )
+{
+ Task task( tz );
+ task.loadXML( xmlDoc );
+ KCalCore::Todo::Ptr todo( new KCalCore::Todo() );
+ task.saveTo( todo );
+ return todo;
+}
+
+QString Task::taskToXML( const KCalCore::Todo::Ptr &todo, const QString& tz )
+{
+ Task task( tz, todo );
+ return task.saveXML();
+}
+
+Task::Task( const QString& tz, const KCalCore::Todo::Ptr &task )
+ : Incidence( tz, task ),
+ mPriority( 5 ), mPercentCompleted( 0 ),
+ mStatus( KCalCore::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( KCalCore::Incidence::Status status )
+{
+ mStatus = status;
+}
+
+KCalCore::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 KDateTime &date )
+{
+ mDueDate = date;
+ mHasDueDate = true;
+}
+
+void Task::setDueDate( const QDate &date )
+{
+ mDueDate = KDateTime( date );
+ mHasDueDate = true;
+ mFloatingStatus = AllDay;
+}
+
+void Task::setDueDate( const QString &date )
+{
+ if ( date.length() > 10 ) {
+ // This is a date + time
+ setDueDate( stringToDateTime( date ) );
+ } else {
+ // This is only a date
+ setDueDate( stringToDate( date ) );
+ }
+}
+
+KDateTime 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 KDateTime& date )
+{
+ mCompletedDate = date;
+ mHasCompletedDate = true;
+}
+
+KDateTime 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;
+ mKolabPriorityFromDom = element.text().toInt( &ok );
+ if ( !ok || mKolabPriorityFromDom < 1 || mKolabPriorityFromDom > 5 ) {
+ kWarning() << "Invalid \"priority\" value:" << element.text();
+ mKolabPriorityFromDom = -1;
+ }
+ } else if ( tagName == "x-kcal-priority" ) {
+ bool ok;
+ mKCalPriorityFromDom = element.text().toInt( &ok );
+ if ( !ok || mKCalPriorityFromDom < 0 || mKCalPriorityFromDom > 9 ) {
+ kWarning() << "Invalid \"x-kcal-priority\" value:" << element.text();
+ mKCalPriorityFromDom = -1;
+ }
+ } else if ( tagName == "completed" ) {
+ bool ok;
+ int percent = element.text().toInt( &ok );
+ if ( !ok || percent < 0 || percent > 100 )
+ percent = 0;
+ setPercentCompleted( percent );
+ } else if ( tagName == "status" ) {
+ if ( element.text() == "in-progress" )
+ setStatus( KCalCore::Incidence::StatusInProcess );
+ else if ( element.text() == "completed" )
+ setStatus( KCalCore::Incidence::StatusCompleted );
+ else if ( element.text() == "waiting-on-someone-else" )
+ setStatus( KCalCore::Incidence::StatusNeedsAction );
+ else if ( element.text() == "deferred" )
+ // Guessing a status here
+ setStatus( KCalCore::Incidence::StatusCanceled );
+ else
+ // Default
+ setStatus( KCalCore::Incidence::StatusNone );
+ } else if ( tagName == "due-date" ) {
+ setDueDate( element.text() );
+ } else if ( tagName == "parent" ) {
+ setParent( element.text() );
+ } else if ( tagName == "x-completed-date" ) {
+ setCompletedDate( stringToDateTime( element.text() ) );
+ } else if ( tagName == "start-date" ) {
+ setHasStartDate( true );
+ setStartDate( element.text() );
+ } else
+ return Incidence::loadAttribute( element );
+
+ // We handled this
+ return true;
+}
+
+bool Task::saveAttributes( QDomElement& element ) const
+{
+ // Save the base class elements
+ Incidence::saveAttributes( element );
+
+ // We need to save x-kcal-priority as well, since the Kolab priority can only save values from
+ // 1 to 5, but we have values from 0 to 9, and do not want to loose them
+ writeString( element, "priority", QString::number( kcalPriorityToKolab( priority() ) ) );
+ writeString( element, "x-kcal-priority", QString::number( priority() ) );
+
+ writeString( element, "completed", QString::number( percentCompleted() ) );
+
+ switch( status() ) {
+ case KCalCore::Incidence::StatusInProcess:
+ writeString( element, "status", "in-progress" );
+ break;
+ case KCalCore::Incidence::StatusCompleted:
+ writeString( element, "status", "completed" );
+ break;
+ case KCalCore::Incidence::StatusNeedsAction:
+ writeString( element, "status", "waiting-on-someone-else" );
+ break;
+ case KCalCore::Incidence::StatusCanceled:
+ writeString( element, "status", "deferred" );
+ break;
+ case KCalCore::Incidence::StatusNone:
+ writeString( element, "status", "not-started" );
+ break;
+ case KCalCore::Incidence::StatusTentative:
+ case KCalCore::Incidence::StatusConfirmed:
+ case KCalCore::Incidence::StatusDraft:
+ case KCalCore::Incidence::StatusFinal:
+ case KCalCore::Incidence::StatusX:
+ // All of these are saved as StatusNone.
+ writeString( element, "status", "not-started" );
+ break;
+ }
+
+ if ( hasDueDate() ) {
+ if ( mFloatingStatus == HasTime ) {
+ writeString( element, "due-date", dateTimeToString( dueDate() ) );
+ } else {
+ writeString( element, "due-date", dateToString( dueDate().date() ) );
+ }
+ }
+
+ if ( !parent().isNull() ) {
+ writeString( element, "parent", parent() );
+ }
+
+ if ( hasCompletedDate() && percentCompleted() == 100 ) {
+ writeString( element, "x-completed-date", dateTimeToString( completedDate() ) );
+ }
+
+ return true;
+}
+
+
+bool Task::loadXML( const QDomDocument& document )
+{
+ mKolabPriorityFromDom = -1;
+ mKCalPriorityFromDom = -1;
+
+ QDomElement top = document.documentElement();
+
+ if ( top.tagName() != "task" ) {
+ qWarning( "XML error: Top tag was %s instead of the expected task",
+ top.tagName().toAscii().data() );
+ 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
+ kDebug() <<"Warning: Unhandled tag" << e.tagName();
+ } else
+ kDebug() <<"Node is not a comment or an element???";
+ }
+
+ decideAndSetPriority();
+ return true;
+}
+
+QString Task::saveXML() const
+{
+ QDomDocument document = domTree();
+ QDomElement element = document.createElement( "task" );
+ element.setAttribute( "version", "1.0" );
+ saveAttributes( element );
+ if ( !hasStartDate() && startDate().isValid() ) {
+ // events and journals always have a start date, but tasks don't.
+ // Remove the entry done by the inherited save above, because we
+ // don't have one.
+ 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 KCalCore::Todo::Ptr &task )
+{
+ Incidence::setFields( task );
+
+ setPriority( task->priority() );
+ setPercentCompleted( task->percentComplete() );
+ setStatus( task->status() );
+ setHasStartDate( task->hasStartDate() );
+
+ if ( task->hasDueDate() ) {
+ if ( task->allDay() ) {
+ // This is a floating task. Don't timezone move this one
+ mFloatingStatus = AllDay;
+ setDueDate( KDateTime( task->dtDue().date() ) );
+ } else {
+ mFloatingStatus = HasTime;
+ setDueDate( localToUTC( task->dtDue() ) );
+ }
+ } else {
+ mHasDueDate = false;
+ }
+
+ if ( !task->relatedTo().isEmpty() ) {
+ setParent( task->relatedTo() );
+ } else{
+ setParent( QString() );
+ }
+
+ if ( task->hasCompletedDate() && task->percentComplete() == 100 ) {
+ setCompletedDate( localToUTC( task->completed() ) );
+ } else {
+ mHasCompletedDate = false;
+ }
+}
+
+void Task::decideAndSetPriority()
+{
+ // If we have both Kolab and KCal values in the XML, we prefer the KCal value, but only if the
+ // values are still in sync
+ if ( mKolabPriorityFromDom != -1 && mKCalPriorityFromDom != -1 ) {
+ const bool inSync = ( kcalPriorityToKolab( mKCalPriorityFromDom ) == mKolabPriorityFromDom );
+ if ( inSync ) {
+ setPriority( mKCalPriorityFromDom );
+ }
+ else {
+ // Out of sync, some other client changed the Kolab priority, so we have to ignore our
+ // KCal priority
+ setPriority( kolabPrioritytoKCal( mKolabPriorityFromDom ) );
+ }
+ }
+
+ // Only KCal priority set, use that.
+ else if ( mKolabPriorityFromDom == -1 && mKCalPriorityFromDom != -1 ) {
+ kWarning() << "No Kolab priority found, only the KCal priority!";
+ setPriority( mKCalPriorityFromDom );
+ }
+
+ // Only Kolab priority set, use that
+ else if ( mKolabPriorityFromDom != -1 && mKCalPriorityFromDom == -1 ) {
+ setPriority( kolabPrioritytoKCal( mKolabPriorityFromDom ) );
+ }
+
+ // No priority set, use the default
+ else {
+ // According the RFC 2445, we should use 0 here, for undefined priority, but AFAIK KOrganizer
+ // doesn't support that, so we'll use 5.
+ setPriority( 5 );
+ }
+}
+
+void Task::saveTo( const KCalCore::Todo::Ptr &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().isEmpty() ) {
+ task->setRelatedTo( parent() );
+ }
+
+ if ( hasCompletedDate() && task->percentComplete() == 100 )
+ task->setCompleted( utcToLocal( mCompletedDate ) );
+}
diff --git a/libkolab/kolabformatV2/task.h b/libkolab/kolabformatV2/task.h
new file mode 100644
index 0000000..bafc160
--- /dev/null
+++ b/libkolab/kolabformatV2/task.h
@@ -0,0 +1,143 @@
+/*
+ This file is part of the kolab resource - the implementation of the
+ Kolab storage format. See www.kolab.org for documentation on this.
+
+ Copyright (c) 2004 Bo Thorsen <bo at 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 <kcalcore/todo.h>
+#include <kcalcore/incidence.h>
+
+class QDomElement;
+
+namespace KCal {
+ class ResourceKolab;
+}
+
+namespace Kolab {
+
+/**
+ * This class represents a task, and knows how to load/save it
+ * from/to XML, and from/to a KCalCore::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 KCalCore::Todo::Ptr fromXml( const QDomDocument& xmlDoc, const QString& tz/*, KCalCore::ResourceKolab *res = 0,
+ const QString& subResource = QString(), quint32 sernum = 0 */);
+
+ /// Use this to get an xml string describing this task entry
+ static QString taskToXML( const KCalCore::Todo::Ptr &, const QString& tz );
+
+ explicit Task( /*KCalCore::ResourceKolab *res, const QString& subResource, quint32 sernum,*/
+ const QString& tz, const KCalCore::Todo::Ptr &todo = KCalCore::Todo::Ptr() );
+ virtual ~Task();
+
+ virtual QString type() const { return "Task"; }
+
+ void saveTo( const KCalCore::Todo::Ptr &todo );
+
+ virtual void setPriority( int priority );
+ virtual int priority() const;
+
+ virtual void setPercentCompleted( int percent );
+ virtual int percentCompleted() const;
+
+ virtual void setStatus( KCalCore::Incidence::Status status );
+ virtual KCalCore::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 KDateTime& date );
+ virtual void setDueDate( const QString &date );
+ virtual void setDueDate( const QDate &date );
+ virtual KDateTime dueDate() const;
+ virtual bool hasDueDate() const;
+
+ virtual void setCompletedDate( const KDateTime& date );
+ virtual KDateTime 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 KCalCore::Todo::Ptr & );
+
+ // This sets the priority of this task by looking at mKolabPriorityFromDom and
+ // mKCalPriorityFromDom.
+ void decideAndSetPriority();
+
+ // This is the KCal priority, not the Kolab priority.
+ // See kcalPriorityToKolab() and kolabPrioritytoKCal().
+ int mPriority;
+
+ // Those priority values are the raw values read by loadAttribute().
+ // They will be converted later in decideAndSetPriority().
+ int mKolabPriorityFromDom;
+ int mKCalPriorityFromDom;
+
+ int mPercentCompleted;
+ KCalCore::Incidence::Status mStatus;
+ QString mParent;
+
+ bool mHasStartDate;
+
+ bool mHasDueDate;
+ KDateTime mDueDate;
+
+ bool mHasCompletedDate;
+ KDateTime mCompletedDate;
+};
+
+}
+
+#endif // KOLAB_TASK_H
commit f2b9cd3925bc7ca9aa5680fbfe1e28fc407b6143
Author: Christian Mollekopf <mollekopf at kolabsys.com>
Date: Mon Mar 19 23:55:18 2012 +0100
Implemented geo uri.
diff --git a/c++/lib/kolabcontact.cpp b/c++/lib/kolabcontact.cpp
index eaa2ab8..a959a2c 100644
--- a/c++/lib/kolabcontact.cpp
+++ b/c++/lib/kolabcontact.cpp
@@ -152,7 +152,7 @@ struct Contact::Private
int imAddressPreferredIndex;
std::vector<std::string> emailAddresses;
int emailAddressPreferredIndex;
- std::vector<std::string> gpsPos;
+ std::vector<Geo> gpsPos;
Crypto crypto;
std::vector<CustomProperty> customProperties;
};
@@ -431,12 +431,12 @@ int Contact::emailAddressPreferredIndex() const
return d->emailAddressPreferredIndex;
}
-void Contact::setGPSpos(const std::vector< std::string >& pos)
+void Contact::setGPSpos(const std::vector< Geo >& pos)
{
d->gpsPos = pos;
}
-std::vector< std::string > Contact::gpsPos() const
+std::vector< Geo > Contact::gpsPos() const
{
return d->gpsPos;
}
diff --git a/c++/lib/kolabcontact.h b/c++/lib/kolabcontact.h
index bf365fb..e4164dd 100644
--- a/c++/lib/kolabcontact.h
+++ b/c++/lib/kolabcontact.h
@@ -236,6 +236,16 @@ private:
CryptoPref mEncryptPref;
};
+struct Geo {
+ Geo(): latitude(0.0), longitude(0.0) {};
+ Geo(double lat, double lon)
+ : latitude(lat), longitude(lon) {};
+
+ bool operator==(const Geo &other) const{ return (longitude == other.longitude && latitude == other.latitude);};
+ double latitude;
+ double longitude;
+};
+
class DistList {
public:
@@ -351,8 +361,8 @@ public:
std::vector<std::string> emailAddresses() const;
int emailAddressPreferredIndex() const;
- void setGPSpos(const std::vector<std::string> &);
- std::vector<std::string> gpsPos() const;
+ void setGPSpos(const std::vector<Geo> &);
+ std::vector<Geo> gpsPos() const;
void setCrypto(const Crypto &);
Crypto crypto() const;
diff --git a/c++/lib/xcardconversions.h b/c++/lib/xcardconversions.h
index 00291e7..226e07d 100644
--- a/c++/lib/xcardconversions.h
+++ b/c++/lib/xcardconversions.h
@@ -24,6 +24,7 @@
#include <boost/shared_ptr.hpp>
#include <boost/foreach.hpp>
#include <boost/lexical_cast.hpp>
+#include <iomanip>
#include "global_definitions.h"
#include "utils.h"
#include "kolabcontact.h"
@@ -404,6 +405,34 @@ Kolab::Address toAddress(const vcard_4_0::vcard::adr_type &adr, bool *isPreferre
return address;
}
+std::string toGeoUri(double lat, double lon)
+{
+ //lexical_cast doesn't work, so we're using stringstream
+ std::stringstream s;
+ s << "geo:";
+ s.precision(15); //can't use std::numeric_limits<double>::max_digits10 because that's c++ 0x, it should be 17, but that seems to cause rounding problems... no idea why.
+ s << lat << ",";
+ s.precision(15); //Needed to get the right precision somehow...
+ s << lon;
+ return s.str();
+}
+
+bool fromGeoUri(const std::string &uri, double &lat, double &lon)
+{
+ if (uri.substr(0,4) != std::string("geo:")) {
+ WARNING("not a geo uri");
+ return false;
+ }
+ std::size_t pos1 = uri.find(",");
+ if (pos1 == std::string::npos) {
+ WARNING("not a valid geo uri");
+ return false;
+ }
+ lat = boost::lexical_cast<double>(uri.substr(4, pos1-4));
+ lon = boost::lexical_cast<double>(uri.substr(pos1+1, uri.size()-pos1-1));
+ return true;
+}
+
template <typename T>
void writeCard(vcard_4_0::vcard &vcard, const T &);
@@ -612,7 +641,11 @@ void writeCard<Kolab::Contact>(vcard_4_0::vcard &vcard, const Kolab::Contact &co
}
if (!contact.gpsPos().empty()) {
- vcard.geo(fromList<vcard::geo_type>(contact.gpsPos()));
+ vcard_4_0::vcard::geo_sequence list;
+ BOOST_FOREACH(const Kolab::Geo &g ,contact.gpsPos()) {
+ list.push_back(vcard_4_0::vcard::geo_type(toGeoUri(g.latitude, g.longitude)));
+ }
+ vcard.geo(list);
}
if (contact.crypto().isValid()) {
@@ -924,9 +957,13 @@ boost::shared_ptr<Kolab::Contact> readCard <Kolab::Contact> (const vcard_4_0::Vc
contact->setEmailAddresses(list, preferredIndex);
}
if (!vcard.geo().empty()) {
- std::vector<std::string> list;
+ std::vector<Geo> list;
BOOST_FOREACH(const vcard_4_0::geoPropType &s, vcard.geo()) {
- list.push_back(s.uri());
+ double lon = 0.0;
+ double lat = 0.0;
+ if (fromGeoUri(s.uri(), lat, lon)) {
+ list.push_back(Kolab::Geo(lat, lon));
+ }
}
contact->setGPSpos(list);
}
diff --git a/c++/tests/bindingstest.cpp b/c++/tests/bindingstest.cpp
index 1c26297..5bd327a 100644
--- a/c++/tests/bindingstest.cpp
+++ b/c++/tests/bindingstest.cpp
@@ -422,7 +422,9 @@ void BindingsTest::contactCompletness()
telephones.push_back(phone);
telephones.push_back(phone);
c.setTelephones(telephones, 1);
- c.setGPSpos(stringlist);
+ std::vector<Kolab::Geo> geo;
+ geo << Kolab::Geo(1.3, -40.3);
+ c.setGPSpos(geo);
Kolab::Crypto crypto;
crypto.setAllowed(Kolab::Crypto::PGPinline | Kolab::Crypto::SMIMEopaque);
crypto.setPGPKey("pgp");
@@ -438,7 +440,7 @@ void BindingsTest::contactCompletness()
const std::string result = Kolab::writeContact(c);
QVERIFY(Kolab::error() == Kolab::NoError);
-// std::cout << result << endl;
+ std::cout << result << endl;
Kolab::Contact e = Kolab::readContact(result, false);
QVERIFY(Kolab::error() == Kolab::NoError);
QCOMPARE(e.uid(), c.uid());
diff --git a/c++/tests/conversiontest.cpp b/c++/tests/conversiontest.cpp
index 700c251..0ab7047 100644
--- a/c++/tests/conversiontest.cpp
+++ b/c++/tests/conversiontest.cpp
@@ -207,6 +207,22 @@ void ConversionTest::urnTest()
QCOMPARE(Kolab::Utils::getError(), Kolab::NoError);
}
+void ConversionTest::geoUriTest()
+{
+ QCOMPARE(Kolab::XCARD::toGeoUri(-34.056, 179.3453), std::string("geo:-34.056,179.3453"));
+ QCOMPARE(Kolab::XCARD::toGeoUri(-34.1, 179.5), std::string("geo:-34.1,179.5"));
+ QCOMPARE(Kolab::XCARD::toGeoUri(-34.0, 179.0), std::string("geo:-34,179"));
+ QCOMPARE(Kolab::XCARD::toGeoUri(-34, 179), std::string("geo:-34,179"));
+ QCOMPARE(Kolab::XCARD::toGeoUri(-34.012342356, 179.3451234553), std::string("geo:-34.012342356,179.3451234553"));
+
+ double lat, lon;
+ QVERIFY(Kolab::XCARD::fromGeoUri(std::string("geo:-34.056,179.3453"), lat, lon));
+ QCOMPARE(lat, -34.056);
+ QCOMPARE(lon, 179.3453);
+ QCOMPARE(Kolab::Utils::getError(), Kolab::NoError);
+}
+
+
void ConversionTest::contactReferenceTest()
{
Kolab::ContactReference email(Kolab::ContactReference::EmailReference, "mail", "name");
diff --git a/c++/tests/conversiontest.h b/c++/tests/conversiontest.h
index 9c2bc86..516470c 100644
--- a/c++/tests/conversiontest.h
+++ b/c++/tests/conversiontest.h
@@ -37,6 +37,8 @@ class ConversionTest : public QObject
void contactReferenceTest();
+ void geoUriTest();
+
void threadLocalTest();
};
More information about the commits
mailing list