diff options
Diffstat (limited to 'tderesources/kolab')
45 files changed, 10018 insertions, 0 deletions
diff --git a/tderesources/kolab/CMakeLists.txt b/tderesources/kolab/CMakeLists.txt new file mode 100644 index 000000000..01acd3972 --- /dev/null +++ b/tderesources/kolab/CMakeLists.txt @@ -0,0 +1,18 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +add_subdirectory( shared ) +add_subdirectory( kabc ) +add_subdirectory( knotes ) +add_subdirectory( kcal ) + +install( FILES kolab-resource.upd DESTINATION ${DATA_INSTALL_DIR}/kconf_update ) +install( PROGRAMS upgrade-resourcetype.pl DESTINATION ${DATA_INSTALL_DIR}/kconf_update ) diff --git a/tderesources/kolab/Makefile.am b/tderesources/kolab/Makefile.am new file mode 100644 index 000000000..0f144e05c --- /dev/null +++ b/tderesources/kolab/Makefile.am @@ -0,0 +1,8 @@ +SUBDIRS = shared kabc knotes kcal + +updatedir = $(kde_datadir)/kconf_update +update_DATA = kolab-resource.upd +update_SCRIPTS = upgrade-resourcetype.pl + +messages: rc.cpp + $(XGETTEXT) */*.cpp -o $(podir)/kres_kolab.pot diff --git a/tderesources/kolab/kabc/CMakeLists.txt b/tderesources/kolab/kabc/CMakeLists.txt new file mode 100644 index 000000000..e4e3e00c8 --- /dev/null +++ b/tderesources/kolab/kabc/CMakeLists.txt @@ -0,0 +1,54 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +include_directories( + ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_CURRENT_SOURCE_DIR}/../shared + ${CMAKE_SOURCE_DIR} + ${CMAKE_SOURCE_DIR}/libtdepim + ${TDE_INCLUDE_DIR} + ${TQT_INCLUDE_DIRS} +) + +link_directories( + ${TQT_LIBRARY_DIRS} +) + + +##### other data ################################ + +install( + FILES kolab.desktop + DESTINATION ${SERVICES_INSTALL_DIR}/tderesources/kabc ) + +install( + FILES ${CMAKE_CURRENT_SOURCE_DIR}/../uninstall.desktop + DESTINATION ${SERVICES_INSTALL_DIR}/tderesources/kabc + RENAME imap.desktop ) + + +##### kabc_kolab (module) ####################### + +tde_add_kpart( kabc_kolab AUTOMOC + SOURCES resourcekolab_plugin.cpp + LINK kabckolab-shared + DESTINATION ${PLUGIN_INSTALL_DIR} +) + + +##### kabckolab (shared) ######################## + +tde_add_library( kabckolab SHARED AUTOMOC + SOURCES resourcekolab.cpp contact.cpp + VERSION 0.0.0 + LINK resourcekolabshared-static kgroupwarebase-shared + DESTINATION ${LIB_INSTALL_DIR} +) diff --git a/tderesources/kolab/kabc/Makefile.am b/tderesources/kolab/kabc/Makefile.am new file mode 100644 index 000000000..80643fba4 --- /dev/null +++ b/tderesources/kolab/kabc/Makefile.am @@ -0,0 +1,27 @@ +METASOURCES = AUTO + +INCLUDES = -I$(top_srcdir)/tderesources/kolab/shared -I$(top_srcdir) $(all_includes) + +# The kolab wizard links to this library too +lib_LTLIBRARIES = libkabckolab.la + +libkabckolab_la_SOURCES = resourcekolab.cpp contact.cpp +libkabckolab_la_LDFLAGS = $(all_libraries) -no-undefined +libkabckolab_la_LIBADD = \ + $(top_builddir)/tderesources/kolab/shared/libresourcekolabshared.la \ + -ltderesources -lkabc + +kde_module_LTLIBRARIES = kabc_kolab.la + +noinst_HEADERS = resourcekolab.h contact.h + +kabc_kolab_la_SOURCES = resourcekolab_plugin.cpp +kabc_kolab_la_LDFLAGS = $(all_libraries) -module $(KDE_PLUGIN) -no-undefined +kabc_kolab_la_LIBADD = libkabckolab.la + +servicedir = $(kde_servicesdir)/tderesources/kabc +service_DATA = kolab.desktop + +install-data-local: $(srcdir)/../uninstall.desktop + $(mkinstalldirs) $(DESTDIR)$(servicedir) + $(INSTALL_DATA) $(srcdir)/../uninstall.desktop $(DESTDIR)$(servicedir)/imap.desktop diff --git a/tderesources/kolab/kabc/contact.cpp b/tderesources/kolab/kabc/contact.cpp new file mode 100644 index 000000000..ff8f869b7 --- /dev/null +++ b/tderesources/kolab/kabc/contact.cpp @@ -0,0 +1,1326 @@ +/* + This file is part of libkabc and/or kaddressbook. + Copyright (c) 2004 Klarälvdalens Datakonsult AB + <info@klaralvdalens-datakonsult.se> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. + + In addition, as a special exception, the copyright holders give + permission to link the code of this program with any edition of + the TQt library by Trolltech AS, Norway (or with modified versions + of TQt that use the same license as TQt), and distribute linked + combinations including the two. You must obey the GNU General + Public License in all respects for all of the code used other than + TQt. If you modify this file, you may extend this exception to + your version of the file, but you are not obligated to do so. If + you do not wish to do so, delete this exception statement from + your version. +*/ + +#include "contact.h" +#include "resourcekolab.h" + +#include <kabc/addressee.h> +#include <kabc/stdaddressbook.h> +#include <libkcal/freebusyurlstore.h> +#include <libtdepim/distributionlist.h> +#include <kio/netaccess.h> +#include <kdebug.h> +#include <tqfile.h> +#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 TQString& xml, KABC::ResourceKolab* resource, const TQString& subResource, TQ_UINT32 sernum ) + : mHasGeo( false ) +{ + load( xml ); + if ( !mPictureAttachmentName.isEmpty() ) + mPicture = loadPictureFromKMail( mPictureAttachmentName, resource, subResource, sernum ); + if ( !mLogoAttachmentName.isEmpty() ) + mLogo = loadPictureFromKMail( mLogoAttachmentName, resource, subResource, sernum ); + if ( !mSoundAttachmentName.isEmpty() ) + mSound = loadDataFromKMail( mSound, resource, subResource, sernum ); +} + +Contact::~Contact() +{ +} + +void Contact::setGivenName( const TQString& name ) +{ + mGivenName = name; +} + +TQString Contact::givenName() const +{ + return mGivenName; +} + +void Contact::setMiddleNames( const TQString& names ) +{ + mMiddleNames = names; +} + +TQString Contact::middleNames() const +{ + return mMiddleNames; +} + +void Contact::setLastName( const TQString& name ) +{ + mLastName = name; +} + +TQString Contact::lastName() const +{ + return mLastName; +} + +void Contact::setFullName( const TQString& name ) +{ + mFullName = name; +} + +TQString Contact::fullName() const +{ + return mFullName; +} + +void Contact::setInitials( const TQString& initials ) +{ + mInitials = initials; +} + +TQString Contact::initials() const +{ + return mInitials; +} + +void Contact::setPrefix( const TQString& prefix ) +{ + mPrefix = prefix; +} + +TQString Contact::prefix() const +{ + return mPrefix; +} + +void Contact::setSuffix( const TQString& suffix ) +{ + mSuffix = suffix; +} + +TQString Contact::suffix() const +{ + return mSuffix; +} + +void Contact::setRole( const TQString& role ) +{ + mRole = role; +} + +TQString Contact::role() const +{ + return mRole; +} + +void Contact::setFreeBusyUrl( const TQString& fbUrl ) +{ + mFreeBusyUrl = fbUrl; +} + +TQString Contact::freeBusyUrl() const +{ + return mFreeBusyUrl; +} + +void Contact::setOrganization( const TQString& organization ) +{ + mOrganization = organization; +} + +TQString Contact::organization() const +{ + return mOrganization; +} + +void Contact::setWebPage( const TQString& url ) +{ + mWebPage = url; +} + +TQString Contact::webPage() const +{ + return mWebPage; +} + +void Contact::setIMAddress( const TQString& imAddress ) +{ + mIMAddress = imAddress; +} + +TQString Contact::imAddress() const +{ + return mIMAddress; +} + +void Contact::setDepartment( const TQString& department ) +{ + mDepartment = department; +} + +TQString Contact::department() const +{ + return mDepartment; +} + +void Contact::setOfficeLocation( const TQString& location ) +{ + mOfficeLocation = location; +} + +TQString Contact::officeLocation() const +{ + return mOfficeLocation; +} + +void Contact::setProfession( const TQString& profession ) +{ + mProfession = profession; +} + +TQString Contact::profession() const +{ + return mProfession; +} + +void Contact::setJobTitle( const TQString& title ) +{ + mJobTitle = title; +} + +TQString Contact::jobTitle() const +{ + return mJobTitle; +} + +void Contact::setManagerName( const TQString& name ) +{ + mManagerName = name; +} + +TQString Contact::managerName() const +{ + return mManagerName; +} + +void Contact::setAssistant( const TQString& name ) +{ + mAssistant = name; +} + +TQString Contact::assistant() const +{ + return mAssistant; +} + +void Contact::setNickName( const TQString& name ) +{ + mNickName = name; +} + +TQString Contact::nickName() const +{ + return mNickName; +} + +void Contact::setSpouseName( const TQString& name ) +{ + mSpouseName = name; +} + +TQString Contact::spouseName() const +{ + return mSpouseName; +} + +void Contact::setBirthday( const TQDate& date ) +{ + mBirthday = date; +} + +TQDate Contact::birthday() const +{ + return mBirthday; +} + +void Contact::setAnniversary( const TQDate& date ) +{ + mAnniversary = date; +} + +TQDate Contact::anniversary() const +{ + return mAnniversary; +} + +void Contact::setChildren( const TQString& children ) +{ + mChildren = children; +} + +TQString Contact::children() const +{ + return mChildren; +} + +void Contact::setGender( const TQString& gender ) +{ + mGender = gender; +} + +TQString Contact::gender() const +{ + return mGender; +} + +void Contact::setLanguage( const TQString& language ) +{ + mLanguage = language; +} + +TQString Contact::language() const +{ + return mLanguage; +} + +void Contact::addPhoneNumber( const PhoneNumber& number ) +{ + mPhoneNumbers.append( number ); +} + +TQValueList<Contact::PhoneNumber>& Contact::phoneNumbers() +{ + return mPhoneNumbers; +} + +const TQValueList<Contact::PhoneNumber>& Contact::phoneNumbers() const +{ + return mPhoneNumbers; +} + +void Contact::addEmail( const Email& email ) +{ + mEmails.append( email ); +} + +TQValueList<Contact::Email>& Contact::emails() +{ + return mEmails; +} + +const TQValueList<Contact::Email>& Contact::emails() const +{ + return mEmails; +} + +void Contact::addAddress( const Contact::Address& address ) +{ + mAddresses.append( address ); +} + +TQValueList<Contact::Address>& Contact::addresses() +{ + return mAddresses; +} + +const TQValueList<Contact::Address>& Contact::addresses() const +{ + return mAddresses; +} + +void Contact::setPreferredAddress( const TQString& address ) +{ + mPreferredAddress = address; +} + +TQString Contact::preferredAddress() const +{ + return mPreferredAddress; +} + +bool Contact::loadNameAttribute( TQDomElement& element ) +{ + for ( TQDomNode n = element.firstChild(); !n.isNull(); n = n.nextSibling() ) { + if ( n.isComment() ) + continue; + if ( n.isElement() ) { + TQDomElement e = n.toElement(); + TQString tagName = e.tagName(); + + if ( tagName == "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 + kdDebug() << "Warning: Unhandled tag " << e.tagName() << endl; + } else + kdDebug() << "Node is not a comment or an element???" << endl; + } + + return true; +} + +void Contact::saveNameAttribute( TQDomElement& element ) const +{ + TQDomElement 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( TQDomElement& element ) +{ + PhoneNumber number; + for ( TQDomNode n = element.firstChild(); !n.isNull(); n = n.nextSibling() ) { + if ( n.isComment() ) + continue; + if ( n.isElement() ) { + TQDomElement e = n.toElement(); + TQString tagName = e.tagName(); + + if ( tagName == "type" ) + number.type = e.text(); + else if ( tagName == "number" ) + number.number = e.text(); + else + // TODO: Unhandled tag - save for later storage + kdDebug() << "Warning: Unhandled tag " << e.tagName() << endl; + } else + kdDebug() << "Node is not a comment or an element???" << endl; + } + + addPhoneNumber( number ); + return true; +} + +void Contact::savePhoneAttributes( TQDomElement& element ) const +{ + TQValueList<PhoneNumber>::ConstIterator it = mPhoneNumbers.begin(); + for ( ; it != mPhoneNumbers.end(); ++it ) { + TQDomElement 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( TQDomElement& element ) const +{ + TQValueList<Email>::ConstIterator it = mEmails.begin(); + for ( ; it != mEmails.end(); ++it ) + saveEmailAttribute( element, *it ); +} + +void Contact::loadCustomAttributes( TQDomElement& element ) +{ + Custom custom; + custom.app = element.attribute( "app" ); + custom.name = element.attribute( "name" ); + custom.value = element.attribute( "value" ); + mCustomList.append( custom ); +} + +void Contact::saveCustomAttributes( TQDomElement& element ) const +{ + TQValueList<Custom>::ConstIterator it = mCustomList.begin(); + for ( ; it != mCustomList.end(); ++it ) { + Q_ASSERT( !(*it).name.isEmpty() ); + if ( (*it).app == s_unhandledTagAppName ) { + writeString( element, (*it).name, (*it).value ); + } else { + // skip writing the freebusyurl as it is a hack we need to remove eventually + if ( (*it).name == TQString::fromLatin1( "FreeBusyURL" ) ) { + continue; + } + + // Let's use attributes so that other tag-preserving-code doesn't need sub-elements + TQDomElement e = element.ownerDocument().createElement( "x-custom" ); + element.appendChild( e ); + e.setAttribute( "app", (*it).app ); + e.setAttribute( "name", (*it).name ); + e.setAttribute( "value", (*it).value ); + } + } +} + +bool Contact::loadAddressAttribute( TQDomElement& element ) +{ + Address address; + + for ( TQDomNode n = element.firstChild(); !n.isNull(); n = n.nextSibling() ) { + if ( n.isComment() ) + continue; + if ( n.isElement() ) { + TQDomElement e = n.toElement(); + TQString tagName = e.tagName(); + + if ( tagName == "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 + kdDebug() << "Warning: Unhandled tag " << e.tagName() << endl; + } else + kdDebug() << "Node is not a comment or an element???" << endl; + } + + addAddress( address ); + return true; +} + +void Contact::saveAddressAttributes( TQDomElement& element ) const +{ + TQValueList<Address>::ConstIterator it = mAddresses.begin(); + for ( ; it != mAddresses.end(); ++it ) { + TQDomElement e = element.ownerDocument().createElement( "address" ); + element.appendChild( e ); + const Address& a = *it; + writeString( e, "type", a.type ); + writeString( e, "x-kde-type", TQString::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 ); + } +} + + +void Kolab::Contact::loadDistrListMember( const TQDomElement& element ) +{ + Member member; + for ( TQDomNode n = element.firstChild(); !n.isNull(); n = n.nextSibling() ) { + if ( n.isComment() ) + continue; + if ( n.isElement() ) { + TQDomElement e = n.toElement(); + TQString tagName = e.tagName(); + if ( tagName == "display-name" ) + member.displayName = e.text(); + else if ( tagName == "smtp-address" ) + member.email = e.text(); + } + } + mDistrListMembers.append( member ); +} + +void Contact::saveDistrListMembers( TQDomElement& element ) const +{ + TQValueList<Member>::ConstIterator it = mDistrListMembers.begin(); + for( ; it != mDistrListMembers.end(); ++it ) { + TQDomElement 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 Contact::loadAttribute( TQDomElement& element ) +{ + const TQString tagName = element.tagName(); + switch ( tagName[0].latin1() ) { + 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; + } + if ( mIsDistributionList && tagName == "display-name" ) { + setFullName( 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 + setJobTitle( 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; + } + if ( mIsDistributionList && tagName == "member" ) { + loadDistrListMember( element ); + return true; + } + break; + 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; + } + break; + case 'w': + if ( tagName == "web-page" ) { + setWebPage( element.text() ); + return true; + } + break; + default: + break; + } + return KolabBase::loadAttribute( element ); +} + +bool Contact::saveAttributes( TQDomElement& element ) const +{ + // Save the base class elements + KolabBase::saveAttributes( element ); + if ( mIsDistributionList ) { + writeString( element, "display-name", fullName() ); + saveDistrListMembers( element ); + } else { + 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", jobTitle() ); + 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", TQString::number( latitude(), 'g', DBL_DIG ) ); + writeString( element, "longitude", TQString::number( longitude(), 'g', DBL_DIG ) ); + } + } + saveCustomAttributes( element ); + + return true; +} + +bool Contact::loadXML( const TQDomDocument& document ) +{ + TQDomElement top = document.documentElement(); + + mIsDistributionList = top.tagName() == "distribution-list"; + if ( top.tagName() != "contact" && !mIsDistributionList ) { + tqWarning( "XML error: Top tag was %s instead of the expected contact or distribution-list", + top.tagName().ascii() ); + return false; + } + + + for ( TQDomNode n = top.firstChild(); !n.isNull(); n = n.nextSibling() ) { + if ( n.isComment() ) + continue; + if ( n.isElement() ) { + TQDomElement e = n.toElement(); + if ( !loadAttribute( e ) ) { + // Unhandled tag - save for later storage + //kdDebug() << "Saving unhandled tag " << e.tagName() << endl; + Custom c; + c.app = s_unhandledTagAppName; + c.name = e.tagName(); + c.value = e.text(); + mCustomList.append( c ); + } + } else + kdDebug() << "Node is not a comment or an element???" << endl; + } + + return true; +} + +TQString Contact::saveXML() const +{ + TQDomDocument document = domTree(); + TQDomElement element = document.createElement( + mIsDistributionList ? "distribution-list" : "contact" ); + element.setAttribute( "version", "1.0" ); + saveAttributes( element ); + document.appendChild( element ); + return document.toString(); +} + +static TQString 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 TQString& 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 TQStringList phoneTypeToString( int /*KABC::PhoneNumber::Types*/ 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. + TQStringList 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 home2 + if ( ( type & KABC::PhoneNumber::Home ) && ( type & KABC::PhoneNumber::Pref ) ) + { + types << "home2"; + type = type & ~KABC::PhoneNumber::Home; + type = type & ~KABC::PhoneNumber::Pref; + } + // To support both "business1" and "business2", map Work+Pref to business2 + if ( ( type & KABC::PhoneNumber::Work ) && ( type & KABC::PhoneNumber::Pref ) ) + { + types << "business2"; + type = type & ~KABC::PhoneNumber::Work; + type = type & ~KABC::PhoneNumber::Pref; + } + + + if ( type & KABC::PhoneNumber::Home ) + types << "home1"; + if ( type & KABC::PhoneNumber::Msg ) // Msg==messaging + types << "company"; + if ( type & KABC::PhoneNumber::Work ) + types << "business1"; + 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 int /*KABC::PhoneNumber::Types*/ phoneTypeFromString( const TQString& type ) +{ + if ( type == "homefax" ) + return KABC::PhoneNumber::Home | KABC::PhoneNumber::Fax; + if ( type == "businessfax" ) + return KABC::PhoneNumber::Work | KABC::PhoneNumber::Fax; + if ( type == "business2" ) + return KABC::PhoneNumber::Work | KABC::PhoneNumber::Pref; + if ( type == "business1" ) + return KABC::PhoneNumber::Work; + if ( type == "home2" ) + return KABC::PhoneNumber::Home | KABC::PhoneNumber::Pref; + if ( type == "home1" ) + 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-Department", + "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 ); + + mIsDistributionList = KPIM::DistributionList::isDistributionList( *addressee ); + if ( mIsDistributionList ) { + // Hopefully all resources are available during saving, so we can look up + // in the addressbook to get name+email from the UID. + KPIM::DistributionList distrList( *addressee ); + const KPIM::DistributionList::Entry::List entries = distrList.entries( KABC::StdAddressBook::self() ); + KPIM::DistributionList::Entry::List::ConstIterator it = entries.begin(); + for ( ; it != entries.end() ; ++it ) { + Member m; + m.displayName = (*it).addressee.formattedName(); + m.email = (*it).email; + if ( m.email.isEmpty() ) + m.email = (*it).addressee.preferredEmail(); + mDistrListMembers.append( m ); + } + } + + 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" ) ); +#if KDE_IS_VERSION(3,5,8) + setDepartment( addressee->department()); +#else + setDepartment( addressee->custom( "KADDRESSBOOK", "X-Department" ) ); +#endif + setOfficeLocation( addressee->custom( "KADDRESSBOOK", "X-Office" ) ); + setProfession( addressee->custom( "KADDRESSBOOK", "X-Profession" ) ); + setRole( addressee->role() ); + setJobTitle( 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 TQString& anniversary = addressee->custom( "KADDRESSBOOK", "X-Anniversary" ); + if ( !anniversary.isEmpty() ) + setAnniversary( stringToDate( anniversary ) ); + + const TQStringList 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 ( TQStringList::ConstIterator it = emails.begin(); it != emails.end(); ++it ) { + Email email; + email.displayName = fullName(); + email.smtpAddress = *it; + addEmail( email ); + } + + // Now the real-world addresses + TQString preferredAddress = "home"; + const KABC::Address::List addresses = addressee->addresses(); + for ( KABC::Address::List::ConstIterator it = addresses.begin() ; it != addresses.end(); ++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.begin(); it != phones.end(); ++it ) { + // Create a tag per phone type set in the bitfield + TQStringList types = phoneTypeToString( (*it).type() ); + for( TQStringList::Iterator typit = types.begin(); typit != types.end(); ++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) + TQStringList knownCustoms; + for ( const char** p = s_knownCustomFields; *p; ++p ) + knownCustoms << TQString::fromLatin1( *p ); + TQStringList customs = addressee->customs(); + for( TQStringList::Iterator it = customs.begin(); it != customs.end(); ++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).find( '-' ); + if ( pos == -1 ) continue; + TQString app = (*it).left( pos ); + if ( app == "KOLAB" ) continue; + TQString name = (*it).mid( pos + 1 ); + pos = name.find( ':' ); + if ( pos == -1 ) continue; + TQString value = name.mid( pos + 1 ); + name = name.left( pos ); + if ( !knownCustoms.contains( name ) ) { + //kdDebug() << k_funcinfo << "app=" << app << " name=" << name << " value=" << value << endl; + Custom c; + if ( app != "KADDRESSBOOK" ) // that's the default + c.app = app; + c.name = name; + c.value = value; + mCustomList.append( c ); + } + } + + TQString url = KCal::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 ); + + if ( mIsDistributionList ) { + KPIM::DistributionList distrList( *addressee ); + distrList.setName( fullName() ); + TQValueList<Member>::ConstIterator mit = mDistrListMembers.begin(); + for ( ; mit != mDistrListMembers.end(); ++mit ) { + TQString displayName = (*mit).displayName; + // fixup the display name DistributionList::assumes neither ',' nor ';' is present + displayName.replace( ',', ' ' ); + displayName.replace( ';', ' ' ); + distrList.insertEntry( displayName, (*mit).email ); + } + addressee->insertCustom( "KADDRESSBOOK", "DistributionList", distrList.custom( "KADDRESSBOOK", "DistributionList" ) ); + Q_ASSERT( KPIM::DistributionList::isDistributionList( *addressee ) ); + } + + addressee->setGivenName( givenName() ); + addressee->setAdditionalName( middleNames() ); + addressee->setFamilyName( lastName() ); + addressee->setFormattedName( fullName() ); + if ( mIsDistributionList ) + addressee->setName( fullName() ); + addressee->setPrefix( prefix() ); + addressee->setSuffix( suffix() ); + addressee->setOrganization( organization() ); + addressee->setUrl( webPage() ); + addressee->insertCustom( "KADDRESSBOOK", "X-IMAddress", imAddress() ); +#if KDE_IS_VERSION(3,5,8) + addressee->setDepartment( department() ); +#else + addressee->insertCustom( "KADDRESSBOOK", "X-Department", department() ); +#endif + addressee->insertCustom( "KADDRESSBOOK", "X-Office", officeLocation() ); + addressee->insertCustom( "KADDRESSBOOK", "X-Profession", profession() ); + addressee->setRole( role() ); + addressee->setTitle( jobTitle() ); + 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( TQDateTime( 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 ) ); + + TQStringList emailAddresses; + for ( TQValueList<Email>::ConstIterator it = mEmails.begin(); it != mEmails.end(); ++it ) { + // we can't do anything with (*it).displayName + emailAddresses.append( (*it).smtpAddress ); + } + addressee->setEmails( emailAddresses ); + + for ( TQValueList<Address>::ConstIterator it = mAddresses.begin(); it != mAddresses.end(); ++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( 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 ( TQValueList<PhoneNumber>::ConstIterator it = mPhoneNumbers.begin(); it != mPhoneNumbers.end(); ++it ) { + KABC::PhoneNumber number; + number.setType( phoneTypeFromString( (*it).type ) ); + number.setNumber( (*it).number ); + addressee->insertPhoneNumber( number ); + } + + for( TQValueList<Custom>::ConstIterator it = mCustomList.begin(); it != mCustomList.end(); ++it ) { + TQString app = (*it).app.isEmpty() ? TQString::fromLatin1( "KADDRESSBOOK" ) : (*it).app; + addressee->insertCustom( app, (*it).name, (*it).value ); + } + //kdDebug(5006) << addressee->customs() << endl; +} + +TQImage Contact::loadPictureFromKMail( const TQString& attachmentName, KABC::ResourceKolab* resource, const TQString& subResource, TQ_UINT32 sernum ) +{ + TQImage img; + KURL url; + if ( resource->kmailGetAttachment( url, subResource, sernum, attachmentName ) && !url.isEmpty() ) { + const TQString path = url.path(); + img.load( path ); + TQFile::remove(path); + } + return img; +} + +TQImage Contact::loadPictureFromAddressee( const KABC::Picture& picture ) +{ + TQImage img; + if ( !picture.isIntern() && !picture.url().isEmpty() ) { + TQString tmpFile; + if ( TDEIO::NetAccess::download( picture.url(), tmpFile, 0 /*no widget known*/ ) ) { + img.load( tmpFile ); + TDEIO::NetAccess::removeTempFile( tmpFile ); + } + } else + img = picture.data(); + return img; +} + +TQByteArray Kolab::Contact::loadDataFromKMail( const TQString& attachmentName, KABC::ResourceKolab* resource, const TQString& subResource, TQ_UINT32 sernum ) +{ + TQByteArray data; + KURL url; + if ( resource->kmailGetAttachment( url, subResource, sernum, attachmentName ) && !url.isEmpty() ) { + TQFile f( url.path() ); + if ( f.open( IO_ReadOnly ) ) { + data = f.readAll(); + f.close(); + } + f.remove(); + } + return data; +} + +TQByteArray Kolab::Contact::loadSoundFromAddressee( const KABC::Sound& sound ) +{ + TQByteArray data; + if ( !sound.isIntern() && !sound.url().isEmpty() ) { + TQString tmpFile; + if ( TDEIO::NetAccess::download( sound.url(), tmpFile, 0 /*no widget known*/ ) ) { + TQFile f( tmpFile ); + if ( f.open( IO_ReadOnly ) ) { + data = f.readAll(); + f.close(); + } + TDEIO::NetAccess::removeTempFile( tmpFile ); + } + } else + data = sound.data(); + return data; +} + +TQString 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/tderesources/kolab/kabc/contact.h b/tderesources/kolab/kabc/contact.h new file mode 100644 index 000000000..6e0de80bc --- /dev/null +++ b/tderesources/kolab/kabc/contact.h @@ -0,0 +1,286 @@ +/* + This file is part of libkabc and/or kaddressbook. + Copyright (c) 2002 - 2004 Klarälvdalens Datakonsult AB + <info@klaralvdalens-datakonsult.se> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. + + In addition, as a special exception, the copyright holders give + permission to link the code of this program with any edition of + the TQt library by Trolltech AS, Norway (or with modified versions + of TQt that use the same license as TQt), and distribute linked + combinations including the two. You must obey the GNU General + Public License in all respects for all of the code used other than + TQt. If you modify this file, you may extend this exception to + your version of the file, but you are not obligated to do so. If + you do not wish to do so, delete this exception statement from + your version. +*/ + +#ifndef KOLABCONTACT_H +#define KOLABCONTACT_H + +#include <kolabbase.h> +#include <tqimage.h> + +namespace KABC { + class Addressee; + class ResourceKolab; + class Picture; + class Sound; +} + +namespace Kolab { + +class Contact : public KolabBase { +public: + struct PhoneNumber { + public: + TQString type; + TQString number; + }; + + struct Address { + public: + Address() : kdeAddressType( -1 ) + { + } + int kdeAddressType; // KABC::Address::Type + TQString type; // kolab-compliant address type: home, work or other + TQString street; + TQString pobox; + TQString locality; + TQString region; + TQString postalCode; + TQString country; + }; + + explicit Contact( const KABC::Addressee* address ); + Contact( const TQString& xml, KABC::ResourceKolab* resource, const TQString& subResource, TQ_UINT32 sernum ); + ~Contact(); + + void saveTo( KABC::Addressee* address ); + + TQString type() const { return "Contact"; } + + void setGivenName( const TQString& name ); + TQString givenName() const; + + void setMiddleNames( const TQString& names ); + TQString middleNames() const; + + void setLastName( const TQString& name ); + TQString lastName() const; + + void setFullName( const TQString& name ); + TQString fullName() const; + + void setInitials( const TQString& initials ); + TQString initials() const; + + void setPrefix( const TQString& prefix ); + TQString prefix() const; + + void setSuffix( const TQString& suffix ); + TQString suffix() const; + + void setRole( const TQString& role ); + TQString role() const; + + void setFreeBusyUrl( const TQString& fbUrl ); + TQString freeBusyUrl() const; + + void setOrganization( const TQString& organization ); + TQString organization() const; + + void setWebPage( const TQString& url ); + TQString webPage() const; + + void setIMAddress( const TQString& imAddress ); + TQString imAddress() const; + + void setDepartment( const TQString& department ); + TQString department() const; + + void setOfficeLocation( const TQString& location ); + TQString officeLocation() const; + + void setProfession( const TQString& profession ); + TQString profession() const; + + void setJobTitle( const TQString& title ); + TQString jobTitle() const; + + void setManagerName( const TQString& name ); + TQString managerName() const; + + void setAssistant( const TQString& name ); + TQString assistant() const; + + void setNickName( const TQString& name ); + TQString nickName() const; + + void setSpouseName( const TQString& name ); + TQString spouseName() const; + + void setBirthday( const TQDate& date ); + TQDate birthday() const; + + void setAnniversary( const TQDate& date ); + TQDate anniversary() const; + + void setPicture( const TQImage& image) { mPicture = image; } + TQString pictureAttachmentName() const { return mPictureAttachmentName; } + TQImage picture() const { return mPicture; } + + void setLogo( const TQImage& image ) { mLogo = image; } + TQString logoAttachmentName() const { return mLogoAttachmentName; } + TQImage logo() const { return mLogo; } + + void setSound( const TQByteArray& sound ) { mSound = sound; } + TQString soundAttachmentName() const { return mSoundAttachmentName; } + TQByteArray sound() const { return mSound; } + + void setChildren( const TQString& children ); + TQString children() const; + + void setGender( const TQString& gender ); + TQString gender() const; + + void setLanguage( const TQString& language ); + TQString language() const; + + void addPhoneNumber( const PhoneNumber& number ); + TQValueList<PhoneNumber>& phoneNumbers(); + const TQValueList<PhoneNumber>& phoneNumbers() const; + + void addEmail( const Email& email ); + TQValueList<Email>& emails(); + const TQValueList<Email>& emails() const; + + void addAddress( const Address& address ); + TQValueList<Address>& addresses(); + const TQValueList<Address>& addresses() const; + + // which address is preferred: home or business or other + void setPreferredAddress( const TQString& address ); + TQString 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( TQDomElement& ); + + // Save the attributes of this class + bool saveAttributes( TQDomElement& ) const; + + // Load this note by reading the XML file + bool loadXML( const TQDomDocument& xml ); + + // Serialize this note to an XML string + TQString saveXML() const; + + // Return true if this contact is a distr list + bool isDistributionList() const { return mIsDistributionList; } + +protected: + void setFields( const KABC::Addressee* ); + +private: + bool loadNameAttribute( TQDomElement& element ); + void saveNameAttribute( TQDomElement& element ) const; + + bool loadPhoneAttribute( TQDomElement& element ); + void savePhoneAttributes( TQDomElement& element ) const; + + void saveEmailAttributes( TQDomElement& element ) const; + + bool loadAddressAttribute( TQDomElement& element ); + void saveAddressAttributes( TQDomElement& element ) const; + + void loadCustomAttributes( TQDomElement& element ); + void saveCustomAttributes( TQDomElement& element ) const; + + void loadDistrListMember( const TQDomElement& element ); + void saveDistrListMembers( TQDomElement& element ) const; + + TQImage loadPictureFromKMail( const TQString& attachmentName, KABC::ResourceKolab* resource, const TQString& subResource, TQ_UINT32 sernum ); + TQImage loadPictureFromAddressee( const KABC::Picture& picture ); + + TQByteArray loadDataFromKMail( const TQString& attachmentName, KABC::ResourceKolab* resource, const TQString& subResource, TQ_UINT32 sernum ); + TQByteArray loadSoundFromAddressee( const KABC::Sound& sound ); + + TQString productID() const; + + TQString mGivenName; + TQString mMiddleNames; + TQString mLastName; + TQString mFullName; + TQString mInitials; + TQString mPrefix; + TQString mSuffix; + TQString mRole; + TQString mFreeBusyUrl; + TQString mOrganization; + TQString mWebPage; + TQString mIMAddress; + TQString mDepartment; + TQString mOfficeLocation; + TQString mProfession; + TQString mJobTitle; + TQString mManagerName; + TQString mAssistant; + TQString mNickName; + TQString mSpouseName; + TQDate mBirthday; + TQDate mAnniversary; + TQImage mPicture; + TQImage mLogo; + TQByteArray mSound; + TQString mPictureAttachmentName; + TQString mLogoAttachmentName; + TQString mSoundAttachmentName; + TQString mChildren; + TQString mGender; + TQString mLanguage; + TQValueList<PhoneNumber> mPhoneNumbers; + TQValueList<Email> mEmails; + TQValueList<Address> mAddresses; + TQString mPreferredAddress; + float mLatitude; + float mLongitude; + bool mHasGeo; + bool mIsDistributionList; + struct Custom { + TQString app; + TQString name; + TQString value; + }; + TQValueList<Custom> mCustomList; + struct Member { + TQString displayName; + TQString email; + }; + TQValueList<Member> mDistrListMembers; +}; + +} + +#endif // KOLABCONTACT_H diff --git a/tderesources/kolab/kabc/kolab.desktop b/tderesources/kolab/kabc/kolab.desktop new file mode 100644 index 000000000..e0a478c0a --- /dev/null +++ b/tderesources/kolab/kabc/kolab.desktop @@ -0,0 +1,52 @@ +[Desktop Entry] +Name=Addressbook on IMAP Server via KMail +Name[af]=Adresboek op IMAP bediener via KMail +Name[bg]=Адресник на сървър IMAP през KMail +Name[br]=Karned chomlec'hioù war ur servijer IMAP gant KMail +Name[ca]=Llibreta d'adreces sobre servidor IMAP mitjançant KMail +Name[cs]=Kniha adres na IMAP serveru přes KMail +Name[da]=Adressebog på IMAP-server via KMail +Name[de]=Adressbuch auf einem IMAP-Server via KMail +Name[el]=Ημερολόγιο σε εξυπηρετητή IMAP μέσω του KMail +Name[es]=Libreta de direcciones en servidor IMAP por medio de KMail +Name[et]=Aadressiraamat IMAP-serveris (KMaili vahendusel) +Name[eu]=Helbide-liburua IMAP zerbitzarian KMail-en bidez +Name[fa]=کتاب نشانی روی کارساز IMAP از طریق KMail +Name[fi]=Osoitekirja IMAP-palvelimella KMailin avulla +Name[fr]=Carnet d'adresse sur serveur IMAP (via KMail) +Name[fy]=Adresboek op IMAP-tsjinner fia KMail +Name[ga]=Leabhar Seoltaí ar Fhreastalaí IMAP via KMail +Name[gl]=Caderno de enderezos nun servidor IMAP mediante KMail +Name[hu]=IMAP-kiszolgálón tárolt címjegyzék a KMailen keresztül +Name[is]=Vistfangaskrá á IMAP þjóni gegnum KMail +Name[it]=Rubrica indirizzi su server IMAP via KMail +Name[ja]=KMail 経由 IMAP サーバのアドレス帳 +Name[kk]=KMail арқылы IMAP серверіндегі адрестік кітапша +Name[km]=សៀវភៅអាសយដ្ឋានលើម៉ាស៊ីនបម្រើ IMAP តាមរយៈ KMail +Name[lt]=Adresų knygelė IMAP serveryje per KMail +Name[mk]=Адресар на IMAP-сервер преку КПошта +Name[ms]=Buku alamat pada pelayan IMAP melalui KMail +Name[nb]=Adressebok på IMAP-tjener via KMail +Name[nds]=Adressbook op IMAP-Server över KMail +Name[ne]=केडीई मेल मार्फत IMAP सर्भरमा ठेगाना पुस्तिका +Name[nl]=Adresboek op IMAP-server via KMail +Name[nn]=Adressebok på IMAP-tenar via KMail +Name[pl]=Książka adresowa na serwerze IMAP za pośrednictwem KMail +Name[pt]=Livro de Endereços em Servidor IMAP via KMail +Name[pt_BR]=Livro de Endereços em servidor IMAP via KMail +Name[ru]=Адресная книга на сервере IMAP через KMail +Name[sk]=Adresár na IMAP-serveri pomocou KMail +Name[sl]=Adresar na strežniku IMAP preko KMaila +Name[sr]=Адресар на IMAP серверу преко KMail-а +Name[sr@Latn]=Adresar na IMAP serveru preko KMail-a +Name[sv]=Adressbok på IMAP-server via Kmail +Name[ta]=IMAP சேவகன் மூலம் கேஅஞ்சல் முகவரிப்புத்தகம் +Name[tr]=KMail Aracılığı ile IMAP Sunucusunda Adres Defteri +Name[uk]=Адресна книга на сервері IMAP через KMail +Name[zh_CN]=通过 KMail 访问 IMAP 服务器上的地址簿 +Name[zh_TW]=透過 KMail 取得 IMAP 伺服器上的通訊錄 +X-TDE-Library=kabc_kolab +Type=Service +ServiceTypes=KResources/Plugin +X-TDE-ResourceFamily=contact +X-TDE-ResourceType=imap diff --git a/tderesources/kolab/kabc/resourcekolab.cpp b/tderesources/kolab/kabc/resourcekolab.cpp new file mode 100644 index 000000000..ce7c18c42 --- /dev/null +++ b/tderesources/kolab/kabc/resourcekolab.cpp @@ -0,0 +1,694 @@ +/* + This file is part of libkabc and/or kaddressbook. + Copyright (c) 2002 - 2004 Klarälvdalens Datakonsult AB + <info@klaralvdalens-datakonsult.se> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. + + In addition, as a special exception, the copyright holders give + permission to link the code of this program with any edition of + the TQt library by Trolltech AS, Norway (or with modified versions + of TQt that use the same license as TQt), and distribute linked + combinations including the two. You must obey the GNU General + Public License in all respects for all of the code used other than + TQt. If you modify this file, you may extend this exception to + your version of the file, but you are not obligated to do so. If + you do not wish to do so, delete this exception statement from + your version. +*/ + +#include "resourcekolab.h" +#include "contact.h" + +#include <kdebug.h> +#include <kglobal.h> +#include <klocale.h> +#include <kmessagebox.h> +#include <ktempfile.h> +#include <kio/observer.h> +#include <kio/uiserver_stub.h> +#include <kabc/vcardconverter.h> +#include <kmainwindow.h> +#include <kapplication.h> +#include <dcopclient.h> + +#include <tqobject.h> +#include <tqtimer.h> +#include <tqstring.h> +#include <tqfile.h> +#include <tqapplication.h> + +#include <assert.h> + +using namespace Kolab; + +class KolabFactory : public KRES::PluginFactoryBase +{ + public: + KRES::Resource *resource( const TDEConfig *config ) + { + return new KABC::ResourceKolab( config ); + } + + KRES::ConfigWidget *configWidget( TQWidget* ) + { + return 0; + } +}; + +K_EXPORT_COMPONENT_FACTORY(kabc_kolab,KolabFactory) + +static const char* s_kmailContentsType = "Contact"; +static const char* s_attachmentMimeTypeContact = "application/x-vnd.kolab.contact"; +static const char* s_attachmentMimeTypeDistList = "application/x-vnd.kolab.contact.distlist"; +static const char* s_inlineMimeType = "text/x-vcard"; + +KABC::ResourceKolab::ResourceKolab( const TDEConfig *config ) + : KPIM::ResourceABC( config ), + Kolab::ResourceKolabBase( "ResourceKolab-KABC" ), + mCachedSubresource( TQString() ), mCachedSubresourceNotFound( false ), mLocked( false ) +{ + setType( "imap" ); + if ( !config ) { + setResourceName( i18n( "Kolab Server" ) ); + } +} + +KABC::ResourceKolab::~ResourceKolab() +{ + // The resource is deleted on exit (StdAddressBook's KStaticDeleter), + // and it wasn't closed before that, so close here to save the config. + if ( isOpen() ) { + close(); + } +} + +void KABC::ResourceKolab::loadSubResourceConfig( TDEConfig& config, + const TQString& name, + const TQString& label, + bool writable ) +{ + TDEConfigGroup group( &config, name ); + bool active = group.readBoolEntry( "Active", true ); + int completionWeight = group.readNumEntry( "CompletionWeight", 80 ); + mSubResources.insert( name, Kolab::SubResource( active, writable, label, + completionWeight ) ); +} + +bool KABC::ResourceKolab::doOpen() +{ + TDEConfig config( configFile() ); + + // Read the calendar entries + TQValueList<KMailICalIface::SubResource> subResources; + if ( !kmailSubresources( subResources, s_kmailContentsType ) ) + return false; + mSubResources.clear(); + TQValueList<KMailICalIface::SubResource>::ConstIterator it; + for ( it = subResources.begin(); it != subResources.end(); ++it ) { + loadSubResourceConfig( config, (*it).location, (*it).label, (*it).writable ); + } + + return true; +} + +void KABC::ResourceKolab::doClose() +{ + writeConfig(); +} + +KABC::Ticket * KABC::ResourceKolab::requestSaveTicket() +{ + if ( !addressBook() ) { + kdError() << "no addressbook" << endl; + return 0; + } + mLocked = true; + + return createTicket( this ); +} + +void KABC::ResourceKolab::releaseSaveTicket( Ticket* ticket ) +{ + mLocked = false; + mCachedSubresource = TQString(); + mCachedSubresourceNotFound = false; + delete ticket; +} + +TQString KABC::ResourceKolab::loadContact( const TQString& contactData, + const TQString& subResource, + TQ_UINT32 sernum, + KMailICalIface::StorageFormat format ) +{ + KABC::Addressee addr; + if ( format == KMailICalIface::StorageXML ) { + Contact contact( contactData, this, subResource, sernum ); // load + contact.saveTo( &addr ); + } else { + KABC::VCardConverter converter; +#if defined(KABC_VCARD_ENCODING_FIX) + addr = converter.parseVCardRaw( contactData.utf8() ); +#else + addr = converter.parseVCard( contactData ); +#endif + } + + addr.setResource( this ); + addr.setChanged( false ); + KABC::Resource::insertAddressee( addr ); // same as mAddrMap.insert( addr.uid(), addr ); + mUidMap[ addr.uid() ] = StorageReference( subResource, sernum ); + kdDebug(5650) << "Loaded contact uid=" << addr.uid() << " sernum=" << sernum << " fullName=" << addr.name() << endl; + return addr.uid(); +} + +static const struct { const char* mimetype; KMailICalIface::StorageFormat format; } s_formats[] = +{ + { s_attachmentMimeTypeContact, KMailICalIface::StorageXML }, + { s_attachmentMimeTypeDistList, KMailICalIface::StorageXML }, + { s_inlineMimeType, KMailICalIface::StorageIcalVcard } +}; + +bool KABC::ResourceKolab::loadSubResource( const TQString& subResource ) +{ + int count = 0; + if ( !kmailIncidencesCount( count, TQString(), subResource ) ) { + kdError() << "Communication problem in KABC::ResourceKolab::loadSubResource()\n"; + return false; + } + if ( !count ) + return true; + + // Read that many contacts at a time. + // If this number is too small we lose time in kmail. + // If it's too big the progressbar is jumpy. + const int nbMessages = 200; + + (void)Observer::self(); // ensure kio_uiserver is running + UIServer_stub uiserver( "kio_uiserver", "UIServer" ); + int progressId = 0; + if ( count > 200 ) { + progressId = uiserver.newJob( kapp->dcopClient()->appId(), true ); + uiserver.totalFiles( progressId, count ); + uiserver.infoMessage( progressId, i18n( "Loading contacts..." ) ); + uiserver.transferring( progressId, "Contacts" ); + } + + for ( int startIndex = 0; startIndex < count; startIndex += nbMessages ) { + + // TODO it would be faster to pass the s_formats array to kmail and let it load + // all events - to avoid loading each mail 3 times. But then we need to extend the returned + // TQMap to also tell us the StorageFormat of each found contact... + for ( int indexFormat = 0; indexFormat < 3; ++indexFormat ) { + const char* mimetype = s_formats[indexFormat].mimetype; + KMailICalIface::StorageFormat format = s_formats[indexFormat].format; + TQMap<TQ_UINT32, TQString> lst; + if ( !kmailIncidences( lst, mimetype, subResource, startIndex, nbMessages ) ) { + kdError() << "Communication problem in KABC::ResourceKolab::loadSubResource()\n"; + if ( progressId ) + uiserver.jobFinished( progressId ); + return false; + } + + for( TQMap<TQ_UINT32, TQString>::ConstIterator it = lst.begin(); it != lst.end(); ++it ) { + loadContact( it.data(), subResource, it.key(), format ); + } + + } + if ( progressId ) { + uiserver.processedFiles( progressId, startIndex ); + uiserver.percent( progressId, 100 * startIndex / count ); + } + +// if ( progress.wasCanceled() ) { +// uiserver.jobFinished( progressId ); +// return false; +// } + + } + + kdDebug(5650) << "Contacts kolab resource: got " << count << " contacts in " << subResource << endl; + + if ( progressId ) + uiserver.jobFinished( progressId ); + return true; +} + +bool KABC::ResourceKolab::load() +{ + mUidMap.clear(); + mAddrMap.clear(); + + bool rc = true; + Kolab::ResourceMap::ConstIterator itR; + for ( itR = mSubResources.begin(); itR != mSubResources.end(); ++itR ) { + if ( !itR.data().active() ) + // This resource is disabled + continue; + + rc &= loadSubResource( itR.key() ); + } + + return rc; +} + +bool KABC::ResourceKolab::save( Ticket* ) +{ + bool rc = true; + + for( ConstIterator it = begin(); it != end(); ++it ) + if( (*it).changed() ) { + rc &= kmailUpdateAddressee( *it ); + } + + if ( !rc ) + kdDebug(5650) << k_funcinfo << " failed." << endl; + return rc; +} + +namespace Kolab { +struct AttachmentList { + TQStringList attachmentURLs; + TQStringList attachmentNames; + TQStringList attachmentMimeTypes; + TQStringList deletedAttachments; + TQValueList<KTempFile *> tempFiles; + + void addAttachment( const TQString& url, const TQString& name, const TQString& mimetype ) { + attachmentURLs.append( url ); + attachmentNames.append( name ); + attachmentMimeTypes.append( mimetype ); + } + + void updatePictureAttachment( const TQImage& image, const TQString& name ); + void updateAttachment( const TQByteArray& data, const TQString& name, const char* mimetype ); +}; +} // namespace + +void AttachmentList::updatePictureAttachment( const TQImage& image, const TQString& name ) +{ + assert( !name.isEmpty() ); + if ( !image.isNull() ) { + KTempFile tempFile; + image.save( tempFile.file(), "PNG" ); + tempFile.close(); + KURL url; + url.setPath( tempFile.name() ); + kdDebug(5650) << "picture saved to " << url.path() << endl; + addAttachment( url.url(), name, "image/png" ); + } else { + deletedAttachments.append( name ); + } +} + +void AttachmentList::updateAttachment( const TQByteArray& data, const TQString& name, const char* mimetype ) +{ + assert( !name.isEmpty() ); + if ( !data.isNull() ) { + KTempFile tempFile; + tempFile.file()->writeBlock( data ); + tempFile.close(); + KURL url; + url.setPath( tempFile.name() ); + kdDebug(5650) << "data saved to " << url.path() << endl; + addAttachment( url.url(), name, mimetype ); + } else { + deletedAttachments.append( name ); + } +} + +bool KABC::ResourceKolab::kmailUpdateAddressee( const Addressee& addr ) +{ + const TQString uid = addr.uid(); + TQString subResource; + TQ_UINT32 sernum; + if ( mUidMap.find( uid ) != mUidMap.end() ) { + subResource = mUidMap[ uid ].resource(); + if ( !subresourceWritable( subResource ) ) { + kdWarning() << "Wow! Something tried to update a non-writable addressee! Fix this caller: " << kdBacktrace() << endl; + return false; + } + sernum = mUidMap[ uid ].serialNumber(); + } else { + if ( !mCachedSubresource.isNull() || mCachedSubresourceNotFound ) { + subResource = mCachedSubresource; + } else { + subResource = findWritableResource( Kolab::Contacts, mSubResources ); + // We were locked, remember the subresource we are working with until + // we are unlocked + if ( mLocked ) { + mCachedSubresource = subResource; + + // If the subresource is empty here, it means findWritableResource() failed, for example + // because the user cancelled the resource selection dialog. Remember that, so we avoid + // asking multiple times when locked. + mCachedSubresourceNotFound = subResource.isEmpty(); + } + } + if ( subResource.isEmpty() ) + return false; + sernum = 0; + } + TQString data; + TQString mimetype; + AttachmentList att; + bool isXMLStorageFormat = kmailStorageFormat( subResource ) == KMailICalIface::StorageXML; + TQString subject = uid; // as per kolab2 spec + if ( isXMLStorageFormat ) { + Contact contact( &addr ); + // The addressee is converted to: 1) the xml 2) the optional picture 3) the optional logo 4) the optional sound + data = contact.saveXML(); + att.updatePictureAttachment( contact.picture(), contact.pictureAttachmentName() ); + att.updatePictureAttachment( contact.logo(), contact.logoAttachmentName() ); + // no way to know the mimetype. The addressee editor allows to attach _any_ kind of file, + // and the sound system sorts it out. + att.updateAttachment( contact.sound(), contact.soundAttachmentName(), "audio/unknown" ); + mimetype = contact.isDistributionList() ? + s_attachmentMimeTypeDistList : s_attachmentMimeTypeContact; + } else { + mimetype = s_inlineMimeType; + KABC::VCardConverter converter; +#if defined(KABC_VCARD_ENCODING_FIX) + data = TQString::fromUtf8( converter.createVCardRaw( addr ) ); +#else + data = converter.createVCard( addr ); +#endif + subject.prepend( "vCard " ); // as per kolab1 spec + } + bool rc = kmailUpdate( subResource, sernum, data, mimetype, subject, + CustomHeaderMap(), + att.attachmentURLs, att.attachmentMimeTypes, att.attachmentNames, + att.deletedAttachments ); + if ( !rc ) + kdDebug(5650) << "kmailUpdate returned false!" << endl; + if ( rc ) { + kdDebug(5650) << "kmailUpdate returned, now sernum=" << sernum << " for uid=" << uid << endl; + mUidMap[ uid ] = StorageReference( subResource, sernum ); + // This is ugly, but it's faster than doing + // mAddrMap.find(addr.uid()), which would give the same :-( + // Reason for this: The Changed attribute of Addressee should + // be mutable + const_cast<Addressee&>(addr).setChanged( false ); + } + + for( TQValueList<KTempFile *>::Iterator it = att.tempFiles.begin(); it != att.tempFiles.end(); ++it ) { + (*it)->setAutoDelete( true ); + delete (*it); + } + return rc; +} + +void KABC::ResourceKolab::insertAddressee( const Addressee& addr ) +{ + const TQString uid = addr.uid(); + //kdDebug(5650) << k_funcinfo << uid << endl; + bool ok = false; + if ( mUidMap.contains( uid ) ) { + mUidsPendingUpdate.append( uid ); + } else { + mUidsPendingAdding.append( uid ); + } + + ok = kmailUpdateAddressee( addr ); + + if ( ok ) + Resource::insertAddressee( addr ); +} + +void KABC::ResourceKolab::removeAddressee( const Addressee& addr ) +{ + const TQString uid = addr.uid(); + if ( mUidMap.find( uid ) == mUidMap.end() ) return; + //kdDebug(5650) << k_funcinfo << uid << endl; + const TQString resource = mUidMap[ uid ].resource(); + if ( !subresourceWritable( resource ) ) { + kdWarning() << "Wow! Something tried to delete a non-writable addressee! Fix this caller: " << kdBacktrace() << endl; + return; + } + /* The user told us to delete, tell KMail */ + kmailDeleteIncidence( resource, + mUidMap[ uid ].serialNumber() ); + mUidsPendingDeletion.append( uid ); + mUidMap.remove( uid ); + + Resource::removeAddressee( addr ); +} + +/* + * These are the DCOP slots that KMail call to notify when something + * changed. + */ +bool KABC::ResourceKolab::fromKMailAddIncidence( const TQString& type, + const TQString& subResource, + TQ_UINT32 sernum, + int format, + const TQString& contactXML ) +{ + // Check if this is a contact + if( type != s_kmailContentsType || !subresourceActive( subResource ) ) + return false; + + // Load contact to find the UID + const TQString uid = loadContact( contactXML, subResource, sernum, + ( KMailICalIface::StorageFormat )format ); + + //kdDebug(5650) << k_funcinfo << uid << endl; + + // Emit "addressbook changed" if this comes from kmail and not from the GUI + if ( !mUidsPendingAdding.contains( uid ) + && !mUidsPendingUpdate.contains( uid ) ) { + addressBook()->emitAddressBookChanged(); + } else { + mUidsPendingAdding.remove( uid ); + mUidsPendingUpdate.remove( uid ); + } + + return true; +} + +void KABC::ResourceKolab::fromKMailDelIncidence( const TQString& type, + const TQString& subResource, + const TQString& uid ) +{ + // Check if this is a contact + if( type != s_kmailContentsType || !subresourceActive( subResource ) ) + return; + + //kdDebug(5650) << k_funcinfo << uid << endl; + + // Can't be in both, by contract + if ( mUidsPendingDeletion.contains( uid ) ) { + mUidsPendingDeletion.remove( uid ); + } else if ( mUidsPendingUpdate.contains( uid ) ) { + // It's good to know if was deleted, but we are waiting on a new one to + // replace it, so let's just sit tight. + } else { + // We didn't trigger this, so KMail did, remove the reference to the uid + mAddrMap.remove( uid ); + mUidMap.remove( uid ); + addressBook()->emitAddressBookChanged(); + } +} + +void KABC::ResourceKolab::fromKMailRefresh( const TQString& type, + const TQString& /*subResource*/ ) +{ + // Check if this is a contact + if( type != s_kmailContentsType ) return; + + //kdDebug(5650) << k_funcinfo << endl; + + load(); // ### should call loadSubResource(subResource) probably + addressBook()->emitAddressBookChanged(); +} + +void KABC::ResourceKolab::fromKMailAddSubresource( const TQString& type, + const TQString& subResource, + const TQString& label, + bool writable, + bool ) +{ + if( type != s_kmailContentsType ) return; + + if ( mSubResources.contains( subResource ) ) + // Already registered + return; + + TDEConfig config( configFile() ); + config.setGroup( "Contact" ); + loadSubResourceConfig( config, subResource, label, writable ); + loadSubResource( subResource ); + addressBook()->emitAddressBookChanged(); + emit signalSubresourceAdded( this, type, subResource ); +} + +void KABC::ResourceKolab::fromKMailDelSubresource( const TQString& type, + const TQString& subResource ) +{ + if( type != s_kmailContentsType ) return; + + if ( !mSubResources.contains( subResource ) ) + // Not registered + return; + + // Ok, it's our job, and we have it here + mSubResources.erase( subResource ); + + TDEConfig config( configFile() ); + config.deleteGroup( subResource ); + config.sync(); + + // Make a list of all uids to remove + Kolab::UidMap::ConstIterator mapIt; + TQStringList uids; + for ( mapIt = mUidMap.begin(); mapIt != mUidMap.end(); ++mapIt ) + if ( mapIt.data().resource() == subResource ) + // We have a match + uids << mapIt.key(); + + // Finally delete all the incidences + if ( !uids.isEmpty() ) { + TQStringList::ConstIterator it; + for ( it = uids.begin(); it != uids.end(); ++it ) { + mAddrMap.remove( *it ); + mUidMap.remove( *it ); + } + + addressBook()->emitAddressBookChanged(); + } + + emit signalSubresourceRemoved( this, type, subResource ); +} + + + +void KABC::ResourceKolab::fromKMailAsyncLoadResult( const TQMap<TQ_UINT32, TQString>& map, + const TQString& /* type */, + const TQString& folder ) +{ + // FIXME + KMailICalIface::StorageFormat format = KMailICalIface::StorageXML; + for( TQMap<TQ_UINT32, TQString>::ConstIterator it = map.begin(); it != map.end(); ++it ) { + loadContact( it.data(), folder, it.key(), format ); + } + if ( !addressBook() ){ + kdDebug(5650) << "asyncLoadResult() : addressBook() returning NULL pointer.\n"; + }else + addressBook()->emitAddressBookChanged(); +} + +TQStringList KABC::ResourceKolab::subresources() const +{ + return mSubResources.keys(); +} + +bool KABC::ResourceKolab::subresourceActive( const TQString& subresource ) const +{ + if ( mSubResources.contains( subresource ) ) { + return mSubResources[ subresource ].active(); + } + + // Safe default bet: + kdDebug(5650) << "subresourceActive( " << subresource << " ): Safe bet\n"; + + return true; +} + +bool KABC::ResourceKolab::subresourceWritable( const TQString& subresource ) const +{ + if ( mSubResources.contains( subresource ) ) { + return mSubResources[ subresource ].writable(); + } + return false; //better a safe default +} + +int KABC::ResourceKolab::subresourceCompletionWeight( const TQString& subresource ) const +{ + if ( mSubResources.contains( subresource ) ) { + return mSubResources[ subresource ].completionWeight(); + } + + kdDebug(5650) << "subresourceCompletionWeight( " << subresource << " ): not found, using default\n"; + + return 80; +} + +TQString KABC::ResourceKolab::subresourceLabel( const TQString& subresource ) const +{ + if ( mSubResources.contains( subresource ) ) { + return mSubResources[ subresource ].label(); + } + + kdDebug(5650) << "subresourceLabel( " << subresource << " ): not found!\n"; + return TQString(); +} + +void KABC::ResourceKolab::setSubresourceCompletionWeight( const TQString& subresource, int completionWeight ) +{ + if ( mSubResources.contains( subresource ) ) { + mSubResources[ subresource ].setCompletionWeight( completionWeight ); + } else { + kdDebug(5650) << "setSubresourceCompletionWeight: subresource " << subresource << " not found" << endl; + } +} + +TQMap<TQString, TQString> KABC::ResourceKolab::uidToResourceMap() const +{ + // TODO: Couldn't this be made simpler? + TQMap<TQString, TQString> map; + Kolab::UidMap::ConstIterator mapIt; + for ( mapIt = mUidMap.begin(); mapIt != mUidMap.end(); ++mapIt ) + map[ mapIt.key() ] = mapIt.data().resource(); + return map; +} + +void KABC::ResourceKolab::setSubresourceActive( const TQString &subresource, bool active ) +{ + if ( mSubResources.contains( subresource ) ) { + mSubResources[ subresource ].setActive( active ); + load(); + } else { + kdDebug(5650) << "setSubresourceCompletionWeight: subresource " << subresource << " not found" << endl; + } + writeConfig(); +} + + +/*virtual*/ +bool KABC::ResourceKolab::addSubresource( const TQString& label, const TQString& parent ) +{ + return kmailAddSubresource( label, parent, s_kmailContentsType ); +} + +/*virtual*/ +bool KABC::ResourceKolab::removeSubresource( const TQString& id ) +{ + return kmailRemoveSubresource( id ); +} + +void KABC::ResourceKolab::writeConfig() +{ + TDEConfig config( configFile() ); + + Kolab::ResourceMap::ConstIterator it; + for ( it = mSubResources.constBegin(); it != mSubResources.constEnd(); ++it ) { + config.setGroup( it.key() ); + config.writeEntry( "Active", it.data().active() ); + config.writeEntry( "CompletionWeight", it.data().completionWeight() ); + } +} + +#include "resourcekolab.moc" diff --git a/tderesources/kolab/kabc/resourcekolab.h b/tderesources/kolab/kabc/resourcekolab.h new file mode 100644 index 000000000..3a142b209 --- /dev/null +++ b/tderesources/kolab/kabc/resourcekolab.h @@ -0,0 +1,179 @@ +/* + This file is part of libkabc and/or kaddressbook. + Copyright (c) 2002 - 2004 Klarälvdalens Datakonsult AB + <info@klaralvdalens-datakonsult.se> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. + + In addition, as a special exception, the copyright holders give + permission to link the code of this program with any edition of + the TQt library by Trolltech AS, Norway (or with modified versions + of TQt that use the same license as TQt), and distribute linked + combinations including the two. You must obey the GNU General + Public License in all respects for all of the code used other than + TQt. If you modify this file, you may extend this exception to + your version of the file, but you are not obligated to do so. If + you do not wish to do so, delete this exception statement from + your version. +*/ + +#ifndef KABC_RESOURCEKOLAB_H +#define KABC_RESOURCEKOLAB_H + +#include <libtdepim/resourceabc.h> +#include <dcopobject.h> +#include "../shared/resourcekolabbase.h" +#include "../shared/subresource.h" +#include <kmail/kmailicalIface.h> +#include <tdepimmacros.h> + +namespace KABC { + + class FormatPlugin; + +/** + * This class implements a KAddressBook resource that keeps its + * addresses in an Kolab folder in KMail (or other conforming email + * clients). + */ +class KDE_EXPORT ResourceKolab : public KPIM::ResourceABC, + public Kolab::ResourceKolabBase +{ + Q_OBJECT + + +public: + /** + * Constructor + */ + ResourceKolab( const TDEConfig* ); + + /** + * Destructor. + */ + virtual ~ResourceKolab(); + + /** + * Open the contacts list + */ + virtual bool doOpen(); + + /** + * Request a ticket, you have to pass through save() to + * allow locking. + */ + virtual Ticket *requestSaveTicket(); + + /** + Releases the ticket previousely requested with requestSaveTicket(). + The resource has to remove its locks in this function. + */ + virtual void releaseSaveTicket( Ticket* ); + + /** + * Load all addressees to the addressbook + */ + virtual bool load(); + + /** + * Save all addressees to the addressbook. + * + * @param ticket The ticket you get by requestSaveTicket() + */ + virtual bool save( Ticket *ticket ); + + /** + Insert an addressee into the resource. + */ + virtual void insertAddressee( const Addressee& ); + + /** + * Removes a addressee from resource. This method is mainly + * used by record-based resources like LDAP or SQL. + */ + virtual void removeAddressee( const Addressee& addr ); + + // Listen to KMail changes in the amount of sub resources + void fromKMailAddSubresource( const TQString& type, const TQString& id, + const TQString& label, bool writable, + bool alarmRelevant ); + void fromKMailDelSubresource( const TQString& type, const TQString& id ); + + bool fromKMailAddIncidence( const TQString& type, const TQString& resource, + TQ_UINT32 sernum, int format, const TQString& contact ); + void fromKMailDelIncidence( const TQString& type, const TQString& resource, + const TQString& contact ); + void fromKMailRefresh( const TQString& type, const TQString& resource ); + + void fromKMailAsyncLoadResult( const TQMap<TQ_UINT32, TQString>& map, + const TQString& type, + const TQString& folder ); + + /// Return the list of subresources. + TQStringList subresources() const; + + /// Is this subresource active? + bool subresourceActive( const TQString& ) const; + /// Is this subresource writable? + virtual bool subresourceWritable( const TQString& ) const; + + virtual void setSubresourceActive( const TQString &, bool ); + + virtual bool addSubresource( const TQString&, const TQString& ); + + virtual bool removeSubresource( const TQString& ); + + virtual bool canHaveSubresources() const { return true; } + + /// Completion weight for a given subresource + virtual int subresourceCompletionWeight( const TQString& ) const; + + /// Label for a given subresource + virtual TQString subresourceLabel( const TQString& ) const; + + /// Set completion weight for a given subresource + virtual void setSubresourceCompletionWeight( const TQString&, int ); + + /// Give the uidmap. Used for ordered searching + TQMap<TQString, TQString> uidToResourceMap() const; + +protected: + bool kmailUpdateAddressee( const Addressee& ); + + void doClose(); + + void loadSubResourceConfig( TDEConfig& config, const TQString& name, + const TQString& label, bool writable ); + bool loadSubResource( const TQString& subResource ); + TQString loadContact( const TQString& contactData, const TQString& subResource, + TQ_UINT32 sernum, const KMailICalIface::StorageFormat format ); + + TQString configFile() const { + return Kolab::ResourceKolabBase::configFile( "kabc" ); + } + + void writeConfig(); + + // The list of subresources + Kolab::ResourceMap mSubResources; + TQString mCachedSubresource; + bool mCachedSubresourceNotFound; + bool mLocked; +}; + +} + +#endif // KABC_RESOURCEKOLAB_H diff --git a/tderesources/kolab/kabc/resourcekolab_plugin.cpp b/tderesources/kolab/kabc/resourcekolab_plugin.cpp new file mode 100644 index 000000000..55ac64205 --- /dev/null +++ b/tderesources/kolab/kabc/resourcekolab_plugin.cpp @@ -0,0 +1,52 @@ +/* + This file is part of libkabc and/or kaddressbook. + Copyright (c) 2002 - 2004 Klarlvdalens Datakonsult AB + <info@klaralvdalens-datakonsult.se> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. + + In addition, as a special exception, the copyright holders give + permission to link the code of this program with any edition of + the TQt library by Trolltech AS, Norway (or with modified versions + of TQt that use the same license as TQt), and distribute linked + combinations including the two. You must obey the GNU General + Public License in all respects for all of the code used other than + TQt. If you modify this file, you may extend this exception to + your version of the file, but you are not obligated to do so. If + you do not wish to do so, delete this exception statement from + your version. +*/ + +#include "resourcekolab.h" + +using namespace Kolab; + +class KolabFactory : public KRES::PluginFactoryBase +{ + public: + KRES::Resource *resource( const TDEConfig *config ) + { + return new KABC::ResourceKolab( config ); + } + + KRES::ConfigWidget *configWidget( TQWidget* ) + { + return 0; + } +}; + +K_EXPORT_COMPONENT_FACTORY(kabc_kolab,KolabFactory) + diff --git a/tderesources/kolab/kcal/CMakeLists.txt b/tderesources/kolab/kcal/CMakeLists.txt new file mode 100644 index 000000000..9f932b5e1 --- /dev/null +++ b/tderesources/kolab/kcal/CMakeLists.txt @@ -0,0 +1,57 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +include_directories( + ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_CURRENT_SOURCE_DIR}/../shared + ${CMAKE_CURRENT_SOURCE_DIR}/../kcal + ${CMAKE_SOURCE_DIR} + ${CMAKE_SOURCE_DIR}/libtdepim + ${TDE_INCLUDE_DIR} + ${TQT_INCLUDE_DIRS} +) + +link_directories( + ${TQT_LIBRARY_DIRS} +) + + +##### other data ################################ + +install( + FILES kolab.desktop + DESTINATION ${SERVICES_INSTALL_DIR}/tderesources/kcal ) + +install( + FILES ${CMAKE_CURRENT_SOURCE_DIR}/../uninstall.desktop + DESTINATION ${SERVICES_INSTALL_DIR}/tderesources/kcal + RENAME imap.desktop) + + +##### kcal_kolab (module) ####################### + +tde_add_kpart( kcal_kolab AUTOMOC + SOURCES + resourcekolab_plugin.cpp + LINK kcalkolab-shared + DESTINATION ${PLUGIN_INSTALL_DIR} +) + + +##### kcalkolab (shared) ######################## + +tde_add_library( kcalkolab SHARED AUTOMOC + SOURCES + incidence.cpp event.cpp task.cpp journal.cpp resourcekolab.cpp + VERSION 0.0.0 + LINK resourcekolabshared-static kgroupwarebase-shared + DESTINATION ${LIB_INSTALL_DIR} +) diff --git a/tderesources/kolab/kcal/Makefile.am b/tderesources/kolab/kcal/Makefile.am new file mode 100644 index 000000000..a25027919 --- /dev/null +++ b/tderesources/kolab/kcal/Makefile.am @@ -0,0 +1,27 @@ +METASOURCES = AUTO + +INCLUDES = -I$(top_srcdir)/tderesources/kolab/shared -I$(top_srcdir) \ + -I$(top_builddir)/libtdepim $(all_includes) + +# The kolab wizard links to this library too +lib_LTLIBRARIES = libkcalkolab.la + +libkcalkolab_la_SOURCES = incidence.cpp event.cpp task.cpp journal.cpp resourcekolab.cpp +libkcalkolab_la_LDFLAGS = $(all_libraries) -no-undefined +libkcalkolab_la_LIBADD = $(top_builddir)/libkcal/libkcal.la \ + $(top_builddir)/tderesources/kolab/shared/libresourcekolabshared.la \ + -ltderesources + +kde_module_LTLIBRARIES = kcal_kolab.la + +kcal_kolab_la_SOURCES = resourcekolab_plugin.cpp +kcal_kolab_la_LDFLAGS = $(all_libraries) -module $(KDE_PLUGIN) -no-undefined +kcal_kolab_la_LIBADD = libkcalkolab.la + +servicedir = $(kde_servicesdir)/tderesources/kcal +service_DATA = kolab.desktop + +install-data-local: $(srcdir)/../uninstall.desktop + $(mkinstalldirs) $(DESTDIR)$(servicedir) + $(INSTALL_DATA) $(srcdir)/../uninstall.desktop $(DESTDIR)$(servicedir)/imap.desktop + diff --git a/tderesources/kolab/kcal/event.cpp b/tderesources/kolab/kcal/event.cpp new file mode 100644 index 000000000..c15c567d8 --- /dev/null +++ b/tderesources/kolab/kcal/event.cpp @@ -0,0 +1,223 @@ +/* + This file is part of the kolab resource - the implementation of the + Kolab storage format. See www.kolab.org for documentation on this. + + Copyright (c) 2004 Bo Thorsen <bo@sonofthor.dk> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. + + In addition, as a special exception, the copyright holders give + permission to link the code of this program with any edition of + the TQt library by Trolltech AS, Norway (or with modified versions + of TQt that use the same license as TQt), and distribute linked + combinations including the two. You must obey the GNU General + Public License in all respects for all of the code used other than + TQt. If you modify this file, you may extend this exception to + your version of the file, but you are not obligated to do so. If + you do not wish to do so, delete this exception statement from + your version. +*/ + +#include "event.h" + +#include <libkcal/event.h> +#include <kdebug.h> + +using namespace Kolab; + + +KCal::Event* Event::xmlToEvent( const TQString& xml, const TQString& tz, KCal::ResourceKolab* res, + const TQString& subResource, TQ_UINT32 sernum ) +{ + Event event( res, subResource, sernum, tz ); + event.load( xml ); + KCal::Event* kcalEvent = new KCal::Event(); + event.saveTo( kcalEvent ); + return kcalEvent; +} + +TQString Event::eventToXML( KCal::Event* kcalEvent, const TQString& tz ) +{ + Event event( 0, TQString(), 0, tz, kcalEvent ); + return event.saveXML(); +} + +Event::Event( KCal::ResourceKolab *res, const TQString &subResource, TQ_UINT32 sernum, + const TQString& tz, KCal::Event* event ) + : Incidence( res, subResource, sernum, tz ), + mShowTimeAs( KCal::Event::Opaque ), mHasEndDate( false ) +{ + if ( event ) + setFields( event ); +} + +Event::~Event() +{ +} + +void Event::setTransparency( KCal::Event::Transparency transparency ) +{ + mShowTimeAs = transparency; +} + +KCal::Event::Transparency Event::transparency() const +{ + return mShowTimeAs; +} + +void Event::setEndDate( const TQDateTime& date ) +{ + mEndDate = date; + mHasEndDate = true; + if ( mFloatingStatus == AllDay ) + kdDebug() << "ERROR: Time on end date but no time on the event\n"; + mFloatingStatus = HasTime; +} + +void Event::setEndDate( const TQDate& date ) +{ + mEndDate = date; + mHasEndDate = true; + if ( mFloatingStatus == HasTime ) + kdDebug() << "ERROR: No time on end date but time on the event\n"; + mFloatingStatus = AllDay; +} + +void Event::setEndDate( const TQString& endDate ) +{ + if ( endDate.length() > 10 ) + // This is a date + time + setEndDate( stringToDateTime( endDate ) ); + else + // This is only a date + setEndDate( stringToDate( endDate ) ); +} + +TQDateTime Event::endDate() const +{ + return mEndDate; +} + +bool Event::loadAttribute( TQDomElement& element ) +{ + // This method doesn't handle the color-label tag yet + TQString tagName = element.tagName(); + + if ( tagName == "show-time-as" ) { + // TODO: Support tentative and outofoffice + if ( element.text() == "free" ) + setTransparency( KCal::Event::Transparent ); + else + setTransparency( KCal::Event::Opaque ); + } else if ( tagName == "end-date" ) + setEndDate( element.text() ); + else + return Incidence::loadAttribute( element ); + + // We handled this + return true; +} + +bool Event::saveAttributes( TQDomElement& element ) const +{ + // Save the base class elements + Incidence::saveAttributes( element ); + + // TODO: Support tentative and outofoffice + if ( transparency() == KCal::Event::Transparent ) + writeString( element, "show-time-as", "free" ); + else + writeString( element, "show-time-as", "busy" ); + if ( mHasEndDate ) { + if ( mFloatingStatus == HasTime ) + writeString( element, "end-date", dateTimeToString( endDate() ) ); + else + writeString( element, "end-date", dateToString( endDate().date() ) ); + } + + return true; +} + + +bool Event::loadXML( const TQDomDocument& document ) +{ + TQDomElement top = document.documentElement(); + + if ( top.tagName() != "event" ) { + tqWarning( "XML error: Top tag was %s instead of the expected event", + top.tagName().ascii() ); + return false; + } + + for ( TQDomNode n = top.firstChild(); !n.isNull(); n = n.nextSibling() ) { + if ( n.isComment() ) + continue; + if ( n.isElement() ) { + TQDomElement e = n.toElement(); + loadAttribute( e ); + } else + kdDebug() << "Node is not a comment or an element???" << endl; + } + + loadAttachments(); + return true; +} + +TQString Event::saveXML() const +{ + TQDomDocument document = domTree(); + TQDomElement element = document.createElement( "event" ); + element.setAttribute( "version", "1.0" ); + saveAttributes( element ); + document.appendChild( element ); + return document.toString(); +} + +void Event::setFields( const KCal::Event* event ) +{ + Incidence::setFields( event ); + + // note: if hasEndDate() is false and hasDuration() is true + // dtEnd() returns start+duration + if ( event->hasEndDate() || event->hasDuration() ) { + if ( event->doesFloat() ) { + // This is a floating event. Don't timezone move this one + mFloatingStatus = AllDay; + setEndDate( event->dtEnd().date() ); + } else { + mFloatingStatus = HasTime; + setEndDate( localToUTC( event->dtEnd() ) ); + } + } else { + mHasEndDate = false; + } + setTransparency( event->transparency() ); +} + +void Event::saveTo( KCal::Event* event ) +{ + Incidence::saveTo( event ); + + event->setHasEndDate( mHasEndDate ); + if ( mHasEndDate ) { + if ( mFloatingStatus == AllDay ) + // This is a floating event. Don't timezone move this one + event->setDtEnd( endDate() ); + else + event->setDtEnd( utcToLocal( endDate() ) ); + } + event->setTransparency( transparency() ); +} diff --git a/tderesources/kolab/kcal/event.h b/tderesources/kolab/kcal/event.h new file mode 100644 index 000000000..57aa3358f --- /dev/null +++ b/tderesources/kolab/kcal/event.h @@ -0,0 +1,102 @@ +/* + This file is part of the kolab resource - the implementation of the + Kolab storage format. See www.kolab.org for documentation on this. + + Copyright (c) 2004 Bo Thorsen <bo@sonofthor.dk> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. + + In addition, as a special exception, the copyright holders give + permission to link the code of this program with any edition of + the TQt library by Trolltech AS, Norway (or with modified versions + of TQt that use the same license as TQt), and distribute linked + combinations including the two. You must obey the GNU General + Public License in all respects for all of the code used other than + TQt. If you modify this file, you may extend this exception to + your version of the file, but you are not obligated to do so. If + you do not wish to do so, delete this exception statement from + your version. +*/ + +#ifndef KOLAB_EVENT_H +#define KOLAB_EVENT_H + +#include "incidence.h" + +#include <libkcal/event.h> + +class TQDomElement; + + +namespace Kolab { + +/** + * This class represents an event, and knows how to load/save it + * from/to XML, and from/to a KCal::Event. + * The instances of this class are temporary, only used to convert + * one to the other. + */ +class Event : public Incidence { +public: + /// Use this to parse an xml string to a event entry + /// The caller is responsible for deleting the returned event + static KCal::Event* xmlToEvent( const TQString& xml, const TQString& tz, KCal::ResourceKolab* res = 0, + const TQString& subResource = TQString(), TQ_UINT32 sernum = 0 ); + + /// Use this to get an xml string describing this event entry + static TQString eventToXML( KCal::Event*, const TQString& tz ); + + /// Create a event object and + explicit Event( KCal::ResourceKolab *res, const TQString &subResource, TQ_UINT32 sernum, + const TQString& tz, KCal::Event* event = 0 ); + virtual ~Event(); + + void saveTo( KCal::Event* event ); + + virtual TQString type() const { return "Event"; } + + virtual void setTransparency( KCal::Event::Transparency transparency ); + virtual KCal::Event::Transparency transparency() const; + + virtual void setEndDate( const TQDateTime& date ); + virtual void setEndDate( const TQDate& date ); + virtual void setEndDate( const TQString& date ); + virtual TQDateTime endDate() const; + + // Load the attributes of this class + virtual bool loadAttribute( TQDomElement& ); + + // Save the attributes of this class + virtual bool saveAttributes( TQDomElement& ) const; + + // Load this event by reading the XML file + virtual bool loadXML( const TQDomDocument& xml ); + + // Serialize this event to an XML string + virtual TQString saveXML() const; + +protected: + // Read all known fields from this ical incidence + void setFields( const KCal::Event* ); + + KCal::Event::Transparency mShowTimeAs; + TQDateTime mEndDate; + bool mHasEndDate; +}; + +} + +#endif // KOLAB_EVENT_H diff --git a/tderesources/kolab/kcal/incidence.cpp b/tderesources/kolab/kcal/incidence.cpp new file mode 100644 index 000000000..ddc31491b --- /dev/null +++ b/tderesources/kolab/kcal/incidence.cpp @@ -0,0 +1,1039 @@ +/* + This file is part of the kolab resource - the implementation of the + Kolab storage format. See www.kolab.org for documentation on this. + + Copyright (c) 2004 Bo Thorsen <bo@sonofthor.dk> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. + + In addition, as a special exception, the copyright holders give + permission to link the code of this program with any edition of + the TQt library by Trolltech AS, Norway (or with modified versions + of TQt that use the same license as TQt), and distribute linked + combinations including the two. You must obey the GNU General + Public License in all respects for all of the code used other than + TQt. If you modify this file, you may extend this exception to + your version of the file, but you are not obligated to do so. If + you do not wish to do so, delete this exception statement from + your version. +*/ + +#include "incidence.h" +#include "resourcekolab.h" + +#include <tqfile.h> +#include <tqvaluelist.h> + +#include <libkcal/journal.h> +#include <korganizer/version.h> +#include <libemailfunctions/email.h> + +#include <kdebug.h> +#include <kmdcodec.h> +#include <kurl.h> +#include <kio/netaccess.h> + +using namespace Kolab; + + +Incidence::Incidence( KCal::ResourceKolab *res, const TQString &subResource, TQ_UINT32 sernum, + const TQString& tz ) + : KolabBase( tz ), mFloatingStatus( Unset ), mHasAlarm( false ), + mResource( res ), + mSubResource( subResource ), + mSernum( sernum ) +{ +} + +Incidence::~Incidence() +{ +} + +void Incidence::setSummary( const TQString& summary ) +{ + mSummary = summary; +} + +TQString Incidence::summary() const +{ + return mSummary; +} + +void Incidence::setLocation( const TQString& location ) +{ + mLocation = location; +} + +TQString Incidence::location() const +{ + return mLocation; +} + +void Incidence::setOrganizer( const Email& organizer ) +{ + mOrganizer = organizer; +} + +KolabBase::Email Incidence::organizer() const +{ + return mOrganizer; +} + +void Incidence::setStartDate( const TQDateTime& startDate ) +{ + mStartDate = startDate; + if ( mFloatingStatus == AllDay ) + kdDebug() << "ERROR: Time on start date but no time on the event\n"; + mFloatingStatus = HasTime; +} + +void Incidence::setStartDate( const TQDate& startDate ) +{ + mStartDate = startDate; + if ( mFloatingStatus == HasTime ) + kdDebug() << "ERROR: No time on start date but time on the event\n"; + mFloatingStatus = AllDay; +} + +void Incidence::setStartDate( const TQString& startDate ) +{ + if ( startDate.length() > 10 ) + // This is a date + time + setStartDate( stringToDateTime( startDate ) ); + else + // This is only a date + setStartDate( stringToDate( startDate ) ); +} + +TQDateTime Incidence::startDate() const +{ + return mStartDate; +} + +void Incidence::setAlarm( float alarm ) +{ + mAlarm = alarm; + mHasAlarm = true; +} + +float Incidence::alarm() const +{ + return mAlarm; +} + +Incidence::Recurrence Incidence::recurrence() const +{ + return mRecurrence; +} + +void Incidence::addAttendee( const Attendee& attendee ) +{ + mAttendees.append( attendee ); +} + +TQValueList<Incidence::Attendee>& Incidence::attendees() +{ + return mAttendees; +} + +const TQValueList<Incidence::Attendee>& Incidence::attendees() const +{ + return mAttendees; +} + +void Incidence::setInternalUID( const TQString& iuid ) +{ + mInternalUID = iuid; +} + +TQString Incidence::internalUID() const +{ + return mInternalUID; +} + +bool Incidence::loadAttendeeAttribute( TQDomElement& element, + Attendee& attendee ) +{ + for ( TQDomNode n = element.firstChild(); !n.isNull(); n = n.nextSibling() ) { + if ( n.isComment() ) + continue; + if ( n.isElement() ) { + TQDomElement e = n.toElement(); + TQString tagName = e.tagName(); + + if ( tagName == "display-name" ) { + // Quote the text in case it contains commas or other quotable chars. + TQString tusername = KPIM::quoteNameIfNecessary( e.text() ); + + TQString tname, temail; + // ignore the return value because it will always be false since + // tusername does not contain "@domain". + KPIM::getNameAndMail( tusername, tname, temail ); + attendee.displayName = tname; + } + else if ( tagName == "smtp-address" ) + attendee.smtpAddress = e.text(); + else if ( tagName == "status" ) + attendee.status = e.text(); + else if ( tagName == "request-response" ) + // This sets reqResp to false, if the text is "false". Otherwise it + // sets it to true. This means the default setting is true. + attendee.requestResponse = ( e.text().lower() != "false" ); + else if ( tagName == "invitation-sent" ) + // Like above, only this defaults to false + attendee.invitationSent = ( e.text().lower() != "true" ); + else if ( tagName == "role" ) + attendee.role = e.text(); + else if ( tagName == "delegated-to" ) + attendee.delegate = e.text(); + else if ( tagName == "delegated-from" ) + attendee.delegator = e.text(); + else + // TODO: Unhandled tag - save for later storage + kdDebug() << "Warning: Unhandled tag " << e.tagName() << endl; + } else + kdDebug() << "Node is not a comment or an element???" << endl; + } + + return true; +} + +void Incidence::saveAttendeeAttribute( TQDomElement& element, + const Attendee& attendee ) const +{ + TQDomElement e = element.ownerDocument().createElement( "attendee" ); + element.appendChild( e ); + writeString( e, "display-name", attendee.displayName ); + writeString( e, "smtp-address", attendee.smtpAddress ); + writeString( e, "status", attendee.status ); + writeString( e, "request-response", + ( attendee.requestResponse ? "true" : "false" ) ); + writeString( e, "invitation-sent", + ( attendee.invitationSent ? "true" : "false" ) ); + writeString( e, "role", attendee.role ); + writeString( e, "delegated-to", attendee.delegate ); + writeString( e, "delegated-from", attendee.delegator ); +} + +void Incidence::saveAttendees( TQDomElement& element ) const +{ + TQValueList<Attendee>::ConstIterator it = mAttendees.begin(); + for ( ; it != mAttendees.end(); ++it ) + saveAttendeeAttribute( element, *it ); +} + +void Incidence::saveAttachments( TQDomElement& element ) const +{ + KCal::Attachment::List::ConstIterator it = mAttachments.begin(); + for ( ; it != mAttachments.end(); ++it ) { + KCal::Attachment *a = (*it); + if ( a->isUri() ) { + writeString( element, "link-attachment", a->uri() ); + } else if ( a->isBinary() ) { + writeString( element, "inline-attachment", a->label() ); + } + } +} + +void Incidence::saveAlarms( TQDomElement& element ) const +{ + if ( mAlarms.isEmpty() ) return; + + TQDomElement list = element.ownerDocument().createElement( "advanced-alarms" ); + element.appendChild( list ); + for ( KCal::Alarm::List::ConstIterator it = mAlarms.constBegin(); it != mAlarms.constEnd(); ++it ) { + KCal::Alarm* a = *it; + TQDomElement e = list.ownerDocument().createElement( "alarm" ); + list.appendChild( e ); + + writeString( e, "enabled", a->enabled() ? "1" : "0" ); + if ( a->hasStartOffset() ) { + writeString( e, "start-offset", TQString::number( a->startOffset().asSeconds()/60 ) ); + } + if ( a->hasEndOffset() ) { + writeString( e, "end-offset", TQString::number( a->endOffset().asSeconds()/60 ) ); + } + if ( a->repeatCount() ) { + writeString( e, "repeat-count", TQString::number( a->repeatCount() ) ); + writeString( e, "repeat-interval", TQString::number( a->snoozeTime() ) ); + } + + switch ( a->type() ) { + case KCal::Alarm::Invalid: + break; + case KCal::Alarm::Display: + e.setAttribute( "type", "display" ); + writeString( e, "text", a->text() ); + break; + case KCal::Alarm::Procedure: + e.setAttribute( "type", "procedure" ); + writeString( e, "program", a->programFile() ); + writeString( e, "arguments", a->programArguments() ); + break; + case KCal::Alarm::Email: + { + e.setAttribute( "type", "email" ); + TQDomElement addresses = e.ownerDocument().createElement( "addresses" ); + e.appendChild( addresses ); + for ( TQValueList<KCal::Person>::ConstIterator it = a->mailAddresses().constBegin(); it != a->mailAddresses().constEnd(); ++it ) { + writeString( addresses, "address", (*it).fullName() ); + } + writeString( e, "subject", a->mailSubject() ); + writeString( e, "mail-text", a->mailText() ); + TQDomElement attachments = e.ownerDocument().createElement( "attachments" ); + e.appendChild( attachments ); + for ( TQStringList::ConstIterator it = a->mailAttachments().constBegin(); it != a->mailAttachments().constEnd(); ++it ) { + writeString( attachments, "attachment", *it ); + } + break; + } + case KCal::Alarm::Audio: + e.setAttribute( "type", "audio" ); + writeString( e, "file", a->audioFile() ); + break; + default: + kdWarning() << "Unhandled alarm type:" << a->type() << endl; + break; + } + } +} + +void Incidence::saveRecurrence( TQDomElement& element ) const +{ + TQDomElement e = element.ownerDocument().createElement( "recurrence" ); + element.appendChild( e ); + e.setAttribute( "cycle", mRecurrence.cycle ); + if ( !mRecurrence.type.isEmpty() ) + e.setAttribute( "type", mRecurrence.type ); + writeString( e, "interval", TQString::number( mRecurrence.interval ) ); + for( TQStringList::ConstIterator it = mRecurrence.days.begin(); it != mRecurrence.days.end(); ++it ) { + writeString( e, "day", *it ); + } + if ( !mRecurrence.dayNumber.isEmpty() ) + writeString( e, "daynumber", mRecurrence.dayNumber ); + if ( !mRecurrence.month.isEmpty() ) + writeString( e, "month", mRecurrence.month ); + if ( !mRecurrence.rangeType.isEmpty() ) { + TQDomElement range = element.ownerDocument().createElement( "range" ); + e.appendChild( range ); + range.setAttribute( "type", mRecurrence.rangeType ); + TQDomText t = element.ownerDocument().createTextNode( mRecurrence.range ); + range.appendChild( t ); + } + for( TQValueList<TQDate>::ConstIterator it = mRecurrence.exclusions.begin(); + it != mRecurrence.exclusions.end(); ++it ) { + writeString( e, "exclusion", dateToString( *it ) ); + } +} + +void Incidence::loadRecurrence( const TQDomElement& element ) +{ + mRecurrence.interval = 0; + mRecurrence.cycle = element.attribute( "cycle" ); + mRecurrence.type = element.attribute( "type" ); + for ( TQDomNode n = element.firstChild(); !n.isNull(); n = n.nextSibling() ) { + if ( n.isComment() ) + continue; + if ( n.isElement() ) { + TQDomElement e = n.toElement(); + TQString tagName = e.tagName(); + + if ( tagName == "interval" ) { + //kolab/issue4229, sometimes the interval value can be empty + if ( e.text().isEmpty() || e.text().toInt() <= 0 ) { + mRecurrence.interval = 1; + } else { + mRecurrence.interval = e.text().toInt(); + } + } + else if ( tagName == "day" ) // can be present multiple times + mRecurrence.days.append( e.text() ); + else if ( tagName == "daynumber" ) + mRecurrence.dayNumber = e.text(); + else if ( tagName == "month" ) + mRecurrence.month = e.text(); + else if ( tagName == "range" ) { + mRecurrence.rangeType = e.attribute( "type" ); + mRecurrence.range = e.text(); + } else if ( tagName == "exclusion" ) { + mRecurrence.exclusions.append( stringToDate( e.text() ) ); + } else + // TODO: Unhandled tag - save for later storage + kdDebug() << "Warning: Unhandled tag " << e.tagName() << endl; + } + } +} + +static void loadAddressesHelper( const TQDomElement& element, KCal::Alarm* a ) +{ + for ( TQDomNode n = element.firstChild(); !n.isNull(); n = n.nextSibling() ) { + if ( n.isComment() ) + continue; + if ( n.isElement() ) { + TQDomElement e = n.toElement(); + TQString tagName = e.tagName(); + + if ( tagName == "address" ) { + a->addMailAddress( KCal::Person( e.text() ) ); + } else { + kdWarning() << "Unhandled tag" << tagName << endl; + } + } + } +} + +static void loadAttachmentsHelper( const TQDomElement& element, KCal::Alarm* a ) +{ + for ( TQDomNode n = element.firstChild(); !n.isNull(); n = n.nextSibling() ) { + if ( n.isComment() ) + continue; + if ( n.isElement() ) { + TQDomElement e = n.toElement(); + TQString tagName = e.tagName(); + + if ( tagName == "attachment" ) { + a->addMailAttachment( e.text() ); + } else { + kdWarning() << "Unhandled tag" << tagName << endl; + } + } + } +} + +static void loadAlarmHelper( const TQDomElement& element, KCal::Alarm* a ) +{ + for ( TQDomNode n = element.firstChild(); !n.isNull(); n = n.nextSibling() ) { + if ( n.isComment() ) + continue; + if ( n.isElement() ) { + TQDomElement e = n.toElement(); + TQString tagName = e.tagName(); + + if ( tagName == "start-offset" ) { + a->setStartOffset( e.text().toInt()*60 ); + } else if ( tagName == "end-offset" ) { + a->setEndOffset( e.text().toInt()*60 ); + } else if ( tagName == "repeat-count" ) { + a->setRepeatCount( e.text().toInt() ); + } else if ( tagName == "repeat-interval" ) { + a->setSnoozeTime( e.text().toInt() ); + } else if ( tagName == "text" ) { + a->setText( e.text() ); + } else if ( tagName == "program" ) { + a->setProgramFile( e.text() ); + } else if ( tagName == "arguments" ) { + a->setProgramArguments( e.text() ); + } else if ( tagName == "addresses" ) { + loadAddressesHelper( e, a ); + } else if ( tagName == "subject" ) { + a->setMailSubject( e.text() ); + } else if ( tagName == "mail-text" ) { + a->setMailText( e.text() ); + } else if ( tagName == "attachments" ) { + loadAttachmentsHelper( e, a ); + } else if ( tagName == "file" ) { + a->setAudioFile( e.text() ); + } else if ( tagName == "enabled" ) { + a->setEnabled( e.text().toInt() != 0 ); + } else { + kdWarning() << "Unhandled tag" << tagName << endl; + } + } + } +} + +void Incidence::loadAlarms( const TQDomElement& element ) +{ + for ( TQDomNode n = element.firstChild(); !n.isNull(); n = n.nextSibling() ) { + if ( n.isComment() ) + continue; + if ( n.isElement() ) { + TQDomElement e = n.toElement(); + TQString tagName = e.tagName(); + + if ( tagName == "alarm" ) { + KCal::Alarm *a = new KCal::Alarm( 0 ); + a->setEnabled( true ); // default to enabled, unless some XML attribute says otherwise. + TQString type = e.attribute( "type" ); + if ( type == "display" ) { + a->setType( KCal::Alarm::Display ); + } else if ( type == "procedure" ) { + a->setType( KCal::Alarm::Procedure ); + } else if ( type == "email" ) { + a->setType( KCal::Alarm::Email ); + } else if ( type == "audio" ) { + a->setType( KCal::Alarm::Audio ); + } else { + kdWarning() << "Unhandled alarm type:" << type << endl; + } + + loadAlarmHelper( e, a ); + mAlarms << a; + } else { + kdWarning() << "Unhandled tag" << tagName << endl; + } + } + } +} + +bool Incidence::loadAttribute( TQDomElement& element ) +{ + TQString tagName = element.tagName(); + + if ( tagName == "summary" ) + setSummary( element.text() ); + else if ( tagName == "location" ) + setLocation( element.text() ); + else if ( tagName == "organizer" ) { + Email email; + if ( loadEmailAttribute( element, email ) ) { + setOrganizer( email ); + return true; + } else + return false; + } else if ( tagName == "start-date" ) + setStartDate( element.text() ); + else if ( tagName == "recurrence" ) + loadRecurrence( element ); + else if ( tagName == "attendee" ) { + Attendee attendee; + if ( loadAttendeeAttribute( element, attendee ) ) { + addAttendee( attendee ); + return true; + } else + return false; + } else if ( tagName == "link-attachment" ) { + mAttachments.push_back( new KCal::Attachment( element.text() ) ); + } else if ( tagName == "alarm" ) + // Alarms should be minutes before. Libkcal uses event time + alarm time + setAlarm( - element.text().toInt() ); + else if ( tagName == "advanced-alarms" ) + loadAlarms( element ); + else if ( tagName == "x-kde-internaluid" ) + setInternalUID( element.text() ); + else if ( tagName == "x-custom" ) + loadCustomAttributes( element ); + else { + bool ok = KolabBase::loadAttribute( element ); + if ( !ok ) { + // Unhandled tag - save for later storage + //kdDebug() << "Saving unhandled tag " << element.tagName() << endl; + Custom c; + c.key = TQCString( "X-TDE-KolabUnhandled-" ) + element.tagName().latin1(); + c.value = element.text(); + mCustomList.append( c ); + } + } + // We handled this + return true; +} + +bool Incidence::saveAttributes( TQDomElement& element ) const +{ + // Save the base class elements + KolabBase::saveAttributes( element ); + + if ( mFloatingStatus == HasTime ) + writeString( element, "start-date", dateTimeToString( startDate() ) ); + else + writeString( element, "start-date", dateToString( startDate().date() ) ); + writeString( element, "summary", summary() ); + writeString( element, "location", location() ); + saveEmailAttribute( element, organizer(), "organizer" ); + if ( !mRecurrence.cycle.isEmpty() ) + saveRecurrence( element ); + saveAttendees( element ); + saveAttachments( element ); + if ( mHasAlarm ) { + // Alarms should be minutes before. Libkcal uses event time + alarm time + int alarmTime = tqRound( -alarm() ); + writeString( element, "alarm", TQString::number( alarmTime ) ); + } + saveAlarms( element ); + writeString( element, "x-kde-internaluid", internalUID() ); + saveCustomAttributes( element ); + return true; +} + +void Incidence::saveCustomAttributes( TQDomElement& element ) const +{ + TQValueList<Custom>::ConstIterator it = mCustomList.begin(); + for ( ; it != mCustomList.end(); ++it ) { + TQString key = (*it).key; + Q_ASSERT( !key.isEmpty() ); + if ( key.startsWith( "X-TDE-KolabUnhandled-" ) ) { + key = key.mid( strlen( "X-TDE-KolabUnhandled-" ) ); + writeString( element, key, (*it).value ); + } else { + // Let's use attributes so that other tag-preserving-code doesn't need sub-elements + TQDomElement e = element.ownerDocument().createElement( "x-custom" ); + element.appendChild( e ); + e.setAttribute( "key", key ); + e.setAttribute( "value", (*it).value ); + } + } +} + +void Incidence::loadCustomAttributes( TQDomElement& element ) +{ + Custom custom; + custom.key = element.attribute( "key" ).latin1(); + custom.value = element.attribute( "value" ); + mCustomList.append( custom ); +} + +static KCal::Attendee::PartStat attendeeStringToStatus( const TQString& s ) +{ + if ( s == "none" ) + return KCal::Attendee::NeedsAction; + if ( s == "tentative" ) + return KCal::Attendee::Tentative; + if ( s == "accepted" ) + return KCal::Attendee::Accepted; + if ( s == "declined" ) + return KCal::Attendee::Declined; + if ( s == "delegated" ) + return KCal::Attendee::Delegated; + + // Default: + return KCal::Attendee::None; +} + +static TQString attendeeStatusToString( KCal::Attendee::PartStat status ) +{ + switch( status ) { + case KCal::Attendee::NeedsAction: + return "none"; + case KCal::Attendee::Accepted: + return "accepted"; + case KCal::Attendee::Declined: + return "declined"; + case KCal::Attendee::Tentative: + return "tentative"; + case KCal::Attendee::Delegated: + return "delegated"; + case KCal::Attendee::Completed: + case KCal::Attendee::InProcess: + // These don't have any meaning in the Kolab format, so just use: + return "accepted"; + } + + // Default for the case that there are more added later: + return "accepted"; +} + +static KCal::Attendee::Role attendeeStringToRole( const TQString& s ) +{ + if ( s == "optional" ) + return KCal::Attendee::OptParticipant; + if ( s == "resource" ) + return KCal::Attendee::NonParticipant; + return KCal::Attendee::ReqParticipant; +} + +static TQString attendeeRoleToString( KCal::Attendee::Role role ) +{ + switch( role ) { + case KCal::Attendee::ReqParticipant: + return "required"; + case KCal::Attendee::OptParticipant: + return "optional"; + case KCal::Attendee::Chair: + // We don't have the notion of chair, so use + return "required"; + case KCal::Attendee::NonParticipant: + // In Kolab, a non-participant is a resource + return "resource"; + } + + // Default for the case that there are more added later: + return "required"; +} + +static const char *s_weekDayName[] = +{ + "monday", "tuesday", "wednesday", "thursday", "friday", "saturday", "sunday" +}; + +static const char *s_monthName[] = +{ + "january", "february", "march", "april", "may", "june", "july", + "august", "september", "october", "november", "december" +}; + +void Incidence::setRecurrence( KCal::Recurrence* recur ) +{ + mRecurrence.interval = recur->frequency(); + switch ( recur->recurrenceType() ) { + case KCal::Recurrence::rMinutely: // Not handled by the kolab XML + mRecurrence.cycle = "minutely"; + break; + case KCal::Recurrence::rHourly: // Not handled by the kolab XML + mRecurrence.cycle = "hourly"; + break; + case KCal::Recurrence::rDaily: + mRecurrence.cycle = "daily"; + break; + case KCal::Recurrence::rWeekly: // every X weeks + mRecurrence.cycle = "weekly"; + { + TQBitArray arr = recur->days(); + for ( uint idx = 0 ; idx < 7 ; ++idx ) + if ( arr.testBit( idx ) ) + mRecurrence.days.append( s_weekDayName[idx] ); + } + break; + case KCal::Recurrence::rMonthlyPos: { + mRecurrence.cycle = "monthly"; + mRecurrence.type = "weekday"; + TQValueList<KCal::RecurrenceRule::WDayPos> monthPositions = recur->monthPositions(); + if ( !monthPositions.isEmpty() ) { + KCal::RecurrenceRule::WDayPos monthPos = monthPositions.first(); + // TODO: Handle multiple days in the same week + mRecurrence.dayNumber = TQString::number( monthPos.pos() ); + mRecurrence.days.append( s_weekDayName[ monthPos.day()-1 ] ); + // Not (properly) handled(?): monthPos.negative (nth days before end of month) + } + break; + } + case KCal::Recurrence::rMonthlyDay: { + mRecurrence.cycle = "monthly"; + mRecurrence.type = "daynumber"; + TQValueList<int> monthDays = recur->monthDays(); + // ####### Kolab XML limitation: only the first month day is used + if ( !monthDays.isEmpty() ) + mRecurrence.dayNumber = TQString::number( monthDays.first() ); + break; + } + case KCal::Recurrence::rYearlyMonth: // (day n of Month Y) + { + mRecurrence.cycle = "yearly"; + mRecurrence.type = "monthday"; + TQValueList<int> rmd = recur->yearDates(); + int day = !rmd.isEmpty() ? rmd.first() : recur->startDate().day(); + mRecurrence.dayNumber = TQString::number( day ); + TQValueList<int> months = recur->yearMonths(); + if ( !months.isEmpty() ) + mRecurrence.month = s_monthName[ months.first() - 1 ]; // #### Kolab XML limitation: only one month specified + break; + } + case KCal::Recurrence::rYearlyDay: // YearlyDay (day N of the year). Not supported by Outlook + mRecurrence.cycle = "yearly"; + mRecurrence.type = "yearday"; + mRecurrence.dayNumber = TQString::number( recur->yearDays().first() ); + break; + case KCal::Recurrence::rYearlyPos: // (weekday X of week N of month Y) + mRecurrence.cycle = "yearly"; + mRecurrence.type = "weekday"; + TQValueList<int> months = recur->yearMonths(); + if ( !months.isEmpty() ) + mRecurrence.month = s_monthName[ months.first() - 1 ]; // #### Kolab XML limitation: only one month specified + TQValueList<KCal::RecurrenceRule::WDayPos> monthPositions = recur->yearPositions(); + if ( !monthPositions.isEmpty() ) { + KCal::RecurrenceRule::WDayPos monthPos = monthPositions.first(); + // TODO: Handle multiple days in the same week + mRecurrence.dayNumber = TQString::number( monthPos.pos() ); + mRecurrence.days.append( s_weekDayName[ monthPos.day()-1 ] ); + + //mRecurrence.dayNumber = TQString::number( *recur->yearNums().getFirst() ); + // Not handled: monthPos.negative (nth days before end of month) + } + break; + } + int howMany = recur->duration(); + if ( howMany > 0 ) { + mRecurrence.rangeType = "number"; + mRecurrence.range = TQString::number( howMany ); + } else if ( howMany == 0 ) { + mRecurrence.rangeType = "date"; + mRecurrence.range = dateToString( recur->endDate() ); + } else { + mRecurrence.rangeType = "none"; + } +} + +void Incidence::setFields( const KCal::Incidence* incidence ) +{ + KolabBase::setFields( incidence ); + + if ( incidence->doesFloat() ) { + // This is a floating event. Don't timezone move this one + mFloatingStatus = AllDay; + setStartDate( incidence->dtStart().date() ); + } else { + mFloatingStatus = HasTime; + setStartDate( localToUTC( incidence->dtStart() ) ); + } + + setSummary( incidence->summary() ); + setLocation( incidence->location() ); + + // Alarm + mHasAlarm = false; // Will be set to true, if we actually have one + if ( incidence->isAlarmEnabled() ) { + const KCal::Alarm::List& alarms = incidence->alarms(); + if ( !alarms.isEmpty() ) { + const KCal::Alarm* alarm = alarms.first(); + if ( alarm->hasStartOffset() ) { + int dur = alarm->startOffset().asSeconds(); + setAlarm( (float)dur / 60.0 ); + } + } + } + + Email org( incidence->organizer().name(), incidence->organizer().email() ); + setOrganizer( org ); + + // Attendees: + KCal::Attendee::List attendees = incidence->attendees(); + KCal::Attendee::List::ConstIterator it; + for ( it = attendees.begin(); it != attendees.end(); ++it ) { + KCal::Attendee* kcalAttendee = *it; + Attendee attendee; + + attendee.displayName = kcalAttendee->name(); + attendee.smtpAddress = kcalAttendee->email(); + attendee.status = attendeeStatusToString( kcalAttendee->status() ); + attendee.requestResponse = kcalAttendee->RSVP(); + // TODO: KCal::Attendee::mFlag is not accessible + // attendee.invitationSent = kcalAttendee->mFlag; + // DF: Hmm? mFlag is set to true and never used at all.... Did you mean another field? + attendee.role = attendeeRoleToString( kcalAttendee->role() ); + attendee.delegate = kcalAttendee->delegate(); + attendee.delegator = kcalAttendee->delegator(); + + addAttendee( attendee ); + } + + mAttachments.clear(); + + // Attachments + KCal::Attachment::List attachments = incidence->attachments(); + KCal::Attachment::List::ConstIterator it2; + for ( it2 = attachments.begin(); it2 != attachments.end(); ++it2 ) { + KCal::Attachment *a = *it2; + mAttachments.push_back( a ); + } + + mAlarms.clear(); + + // Alarms + const KCal::Alarm::List alarms = incidence->alarms(); + for ( KCal::Alarm::List::ConstIterator it = alarms.begin(); it != alarms.end(); ++it ) { + mAlarms.push_back( *it ); + } + + if ( incidence->doesRecur() ) { + setRecurrence( incidence->recurrence() ); + mRecurrence.exclusions = incidence->recurrence()->exDates(); + } + + // Handle the scheduling ID + if ( incidence->schedulingID() == incidence->uid() ) { + // There is no scheduling ID + setInternalUID( TQString() ); + } else { + // We've internally been using a different uid, so save that as the + // temporary (internal) uid and restore the original uid, the one that + // is used in the folder and the outside world + setUid( incidence->schedulingID() ); + setInternalUID( incidence->uid() ); + } + + if ( incidence->pilotId() != 0 ) + setPilotSyncId( incidence->pilotId() ); + + setPilotSyncStatus( incidence->syncStatus() ); + + // Unhandled tags and other custom properties (see libkcal/customproperties.h) + const TQMap<TQCString, TQString> map = incidence->customProperties(); + TQMap<TQCString, TQString>::ConstIterator cit = map.begin(); + for ( ; cit != map.end() ; ++cit ) { + Custom c; + c.key = cit.key(); + c.value = cit.data(); + mCustomList.append( c ); + } +} + +static TQBitArray daysListToBitArray( const TQStringList& days ) +{ + TQBitArray arr( 7 ); + arr.fill( false ); + for( TQStringList::ConstIterator it = days.begin(); it != days.end(); ++it ) { + for ( uint i = 0; i < 7 ; ++i ) + if ( *it == s_weekDayName[i] ) + arr.setBit( i, true ); + } + return arr; +} + + +void Incidence::saveTo( KCal::Incidence* incidence ) +{ + KolabBase::saveTo( incidence ); + + if ( mFloatingStatus == AllDay ) { + // This is a floating event. Don't timezone move this one + incidence->setDtStart( startDate() ); + incidence->setFloats( true ); + } else { + incidence->setDtStart( utcToLocal( startDate() ) ); + incidence->setFloats( false ); + } + + incidence->setSummary( summary() ); + incidence->setLocation( location() ); + + if ( mHasAlarm && mAlarms.isEmpty() ) { + KCal::Alarm* alarm = incidence->newAlarm(); + alarm->setStartOffset( tqRound( mAlarm * 60.0 ) ); + alarm->setEnabled( true ); + alarm->setType( KCal::Alarm::Display ); + } else if ( !mAlarms.isEmpty() ) { + for ( KCal::Alarm::List::ConstIterator it = mAlarms.constBegin(); it != mAlarms.constEnd(); ++it ) { + KCal::Alarm *alarm = *it; + alarm->setParent( incidence ); + incidence->addAlarm( alarm ); + } + } + + if ( organizer().displayName.isEmpty() ) + incidence->setOrganizer( organizer().smtpAddress ); + else + incidence->setOrganizer( organizer().displayName + "<" + + organizer().smtpAddress + ">" ); + + incidence->clearAttendees(); + TQValueList<Attendee>::ConstIterator it; + for ( it = mAttendees.begin(); it != mAttendees.end(); ++it ) { + KCal::Attendee::PartStat status = attendeeStringToStatus( (*it).status ); + KCal::Attendee::Role role = attendeeStringToRole( (*it).role ); + KCal::Attendee* attendee = new KCal::Attendee( (*it).displayName, + (*it).smtpAddress, + (*it).requestResponse, + status, role ); + attendee->setDelegate( (*it).delegate ); + attendee->setDelegator( (*it).delegator ); + incidence->addAttendee( attendee ); + } + + incidence->clearAttachments(); + KCal::Attachment::List::ConstIterator it2; + for ( it2 = mAttachments.begin(); it2 != mAttachments.end(); ++it2 ) { + KCal::Attachment *a = (*it2); + // TODO should we copy? + incidence->addAttachment( a ); + } + + if ( !mRecurrence.cycle.isEmpty() ) { + KCal::Recurrence* recur = incidence->recurrence(); // yeah, this creates it + // done below recur->setFrequency( mRecurrence.interval ); + if ( mRecurrence.cycle == "minutely" ) { + recur->setMinutely( mRecurrence.interval ); + } else if ( mRecurrence.cycle == "hourly" ) { + recur->setHourly( mRecurrence.interval ); + } else if ( mRecurrence.cycle == "daily" ) { + recur->setDaily( mRecurrence.interval ); + } else if ( mRecurrence.cycle == "weekly" ) { + TQBitArray rDays = daysListToBitArray( mRecurrence.days ); + recur->setWeekly( mRecurrence.interval, rDays ); + } else if ( mRecurrence.cycle == "monthly" ) { + recur->setMonthly( mRecurrence.interval ); + if ( mRecurrence.type == "weekday" ) { + recur->addMonthlyPos( mRecurrence.dayNumber.toInt(), daysListToBitArray( mRecurrence.days ) ); + } else if ( mRecurrence.type == "daynumber" ) { + recur->addMonthlyDate( mRecurrence.dayNumber.toInt() ); + } else kdWarning() << "Unhandled monthly recurrence type " << mRecurrence.type << endl; + } else if ( mRecurrence.cycle == "yearly" ) { + recur->setYearly( mRecurrence.interval ); + if ( mRecurrence.type == "monthday" ) { + recur->addYearlyDate( mRecurrence.dayNumber.toInt() ); + for ( int i = 0; i < 12; ++i ) + if ( s_monthName[ i ] == mRecurrence.month ) + recur->addYearlyMonth( i+1 ); + } else if ( mRecurrence.type == "yearday" ) { + recur->addYearlyDay( mRecurrence.dayNumber.toInt() ); + } else if ( mRecurrence.type == "weekday" ) { + for ( int i = 0; i < 12; ++i ) + if ( s_monthName[ i ] == mRecurrence.month ) + recur->addYearlyMonth( i+1 ); + recur->addYearlyPos( mRecurrence.dayNumber.toInt(), daysListToBitArray( mRecurrence.days ) ); + } else kdWarning() << "Unhandled yearly recurrence type " << mRecurrence.type << endl; + } else kdWarning() << "Unhandled recurrence cycle " << mRecurrence.cycle << endl; + + if ( mRecurrence.rangeType == "number" ) { + recur->setDuration( mRecurrence.range.toInt() ); + } else if ( mRecurrence.rangeType == "date" ) { + recur->setEndDate( stringToDate( mRecurrence.range ) ); + } // "none" is default since tje set*ly methods set infinite recurrence + + incidence->recurrence()->setExDates( mRecurrence.exclusions ); + + } + /* If we've stored a uid to be used internally instead of the real one + * (to deal with duplicates of events in different folders) before, then + * restore it, so it does not change. Keep the original uid around for + * scheduling purposes. */ + if ( !internalUID().isEmpty() ) { + incidence->setUid( internalUID() ); + incidence->setSchedulingID( uid() ); + } + if ( hasPilotSyncId() ) + incidence->setPilotId( pilotSyncId() ); + if ( hasPilotSyncStatus() ) + incidence->setSyncStatus( pilotSyncStatus() ); + + for( TQValueList<Custom>::ConstIterator it = mCustomList.constBegin(); it != mCustomList.constEnd(); ++it ) { + incidence->setNonKDECustomProperty( (*it).key, (*it).value ); + } + +} + +void Incidence::loadAttachments() +{ + TQStringList attachments; + if ( mResource->kmailListAttachments( attachments, mSubResource, mSernum ) ) { + for ( TQStringList::ConstIterator it = attachments.constBegin(); it != attachments.constEnd(); ++it ) { + TQByteArray data; + KURL url; + if ( mResource->kmailGetAttachment( url, mSubResource, mSernum, *it ) && !url.isEmpty() ) { + TQFile f( url.path() ); + if ( f.open( IO_ReadOnly ) ) { + data = f.readAll(); + TQString mimeType; + if ( !mResource->kmailAttachmentMimetype( mimeType, mSubResource, mSernum, *it ) ) + mimeType = "application/octet-stream"; + KCal::Attachment *a = new KCal::Attachment( KCodecs::base64Encode( data ).data(), mimeType ); + a->setLabel( *it ); + mAttachments.append( a ); + f.close(); + } + f.remove(); + } + } + } +} + +TQString Incidence::productID() const +{ + return TQString( "KOrganizer %1, Kolab resource" ).arg( korgVersion ); +} + +// Unhandled KCal::Incidence fields: +// revision, status (unused), priority (done in tasks), attendee.uid, +// mComments, mReadOnly + diff --git a/tderesources/kolab/kcal/incidence.h b/tderesources/kolab/kcal/incidence.h new file mode 100644 index 000000000..926ec1e4f --- /dev/null +++ b/tderesources/kolab/kcal/incidence.h @@ -0,0 +1,175 @@ +/* + This file is part of the kolab resource - the implementation of the + Kolab storage format. See www.kolab.org for documentation on this. + + Copyright (c) 2004 Bo Thorsen <bo@sonofthor.dk> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. + + In addition, as a special exception, the copyright holders give + permission to link the code of this program with any edition of + the TQt library by Trolltech AS, Norway (or with modified versions + of TQt that use the same license as TQt), and distribute linked + combinations including the two. You must obey the GNU General + Public License in all respects for all of the code used other than + TQt. If you modify this file, you may extend this exception to + your version of the file, but you are not obligated to do so. If + you do not wish to do so, delete this exception statement from + your version. +*/ + +#ifndef KOLAB_INCIDENCE_H +#define KOLAB_INCIDENCE_H + +#include <kolabbase.h> + +class TQDomElement; + +namespace KCal { + class Incidence; + class Recurrence; + class Alarm; + class Attachment; + class ResourceKolab; +} + +namespace Kolab { + +/** + * This abstract class represents an incidence which has the shared + * fields, of events and tasks and knows how to load/save these + * from/to XML, and from/to a KCal::Incidence. + */ +class Incidence : public KolabBase { +public: + struct Recurrence { + TQString cycle; + TQString type; + int interval; + TQStringList days; // list of days-of-the-week + TQString dayNumber; + TQString month; + TQString rangeType; + TQString range; // date or number or nothing + TQValueList<TQDate> exclusions; + }; + + struct Attendee : Email { + Attendee() : requestResponse( true ), invitationSent( false ) {} + TQString status; + bool requestResponse; + bool invitationSent; + TQString role; + TQString delegate; + TQString delegator; + }; + + explicit Incidence( KCal::ResourceKolab *res, const TQString &subResource, TQ_UINT32 sernum, + const TQString& tz ); + virtual ~Incidence(); + + void saveTo( KCal::Incidence* incidence ); + + virtual void setSummary( const TQString& summary ); + virtual TQString summary() const; + + virtual void setLocation( const TQString& location ); + virtual TQString location() const; + + virtual void setOrganizer( const Email& organizer ); + virtual Email organizer() const; + + virtual void setStartDate( const TQDateTime& startDate ); + virtual void setStartDate( const TQDate& startDate ); + virtual void setStartDate( const TQString& startDate ); + virtual TQDateTime startDate() const; + + virtual void setAlarm( float alarm ); + virtual float alarm() const; + + virtual void setRecurrence( KCal::Recurrence* recur ); + virtual Recurrence recurrence() const; + + virtual void addAttendee( const Attendee& attendee ); + TQValueList<Attendee>& attendees(); + const TQValueList<Attendee>& attendees() const; + + /** + * The internal uid is used as the uid inside KOrganizer whenever + * two or more events with the same uid appear, which KOrganizer + * can't handle. To avoid keep that interal uid from changing all the + * time, it is persisted in the XML between a save and the next load. + */ + void setInternalUID( const TQString& iuid ); + TQString internalUID() const; + + // Load the attributes of this class + virtual bool loadAttribute( TQDomElement& ); + + // Save the attributes of this class + virtual bool saveAttributes( TQDomElement& ) const; + +protected: + enum FloatingStatus { Unset, AllDay, HasTime }; + + // Read all known fields from this ical incidence + void setFields( const KCal::Incidence* ); + + bool loadAttendeeAttribute( TQDomElement&, Attendee& ); + void saveAttendeeAttribute( TQDomElement& element, + const Attendee& attendee ) const; + void saveAttendees( TQDomElement& element ) const; + void saveAttachments( TQDomElement& element ) const; + + void loadAlarms( const TQDomElement& element ); + void saveAlarms( TQDomElement& element ) const; + + void loadRecurrence( const TQDomElement& element ); + void saveRecurrence( TQDomElement& element ) const; + void saveCustomAttributes( TQDomElement& element ) const; + void loadCustomAttributes( TQDomElement& element ); + + void loadAttachments(); + + TQString productID() const; + + TQString mSummary; + TQString mLocation; + Email mOrganizer; + TQDateTime mStartDate; + FloatingStatus mFloatingStatus; + float mAlarm; + bool mHasAlarm; + Recurrence mRecurrence; + TQValueList<Attendee> mAttendees; + TQValueList<KCal::Alarm*> mAlarms; + TQValueList<KCal::Attachment*> mAttachments; + TQString mInternalUID; + + struct Custom { + TQCString key; + TQString value; + }; + TQValueList<Custom> mCustomList; + + KCal::ResourceKolab *mResource; + TQString mSubResource; + TQ_UINT32 mSernum; +}; + +} + +#endif // KOLAB_INCIDENCE_H diff --git a/tderesources/kolab/kcal/journal.cpp b/tderesources/kolab/kcal/journal.cpp new file mode 100644 index 000000000..acfc4e71d --- /dev/null +++ b/tderesources/kolab/kcal/journal.cpp @@ -0,0 +1,184 @@ +/* + This file is part of the kolab resource - the implementation of the + Kolab storage format. See www.kolab.org for documentation on this. + + Copyright (c) 2004 Bo Thorsen <bo@sonofthor.dk> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. + + In addition, as a special exception, the copyright holders give + permission to link the code of this program with any edition of + the TQt library by Trolltech AS, Norway (or with modified versions + of TQt that use the same license as TQt), and distribute linked + combinations including the two. You must obey the GNU General + Public License in all respects for all of the code used other than + TQt. If you modify this file, you may extend this exception to + your version of the file, but you are not obligated to do so. If + you do not wish to do so, delete this exception statement from + your version. +*/ + +#include "journal.h" + +#include <libkcal/journal.h> +#include <korganizer/version.h> +#include <kdebug.h> + +using namespace Kolab; + + +KCal::Journal* Journal::xmlToJournal( const TQString& xml, const TQString& tz ) +{ + Journal journal( tz ); + journal.load( xml ); + KCal::Journal* kcalJournal = new KCal::Journal(); + journal.saveTo( kcalJournal ); + return kcalJournal; +} + +TQString Journal::journalToXML( KCal::Journal* kcalJournal, const TQString& tz ) +{ + Journal journal( tz, kcalJournal ); + return journal.saveXML(); +} + +Journal::Journal( const TQString& tz, KCal::Journal* journal ) + : KolabBase( tz ) +{ + if ( journal ) + setFields( journal ); +} + +Journal::~Journal() +{ +} + +void Journal::setSummary( const TQString& summary ) +{ + mSummary = summary; +} + +TQString Journal::summary() const +{ + return mSummary; +} + +void Journal::setStartDate( const TQDateTime& startDate ) +{ + mStartDate = startDate; +} + +TQDateTime Journal::startDate() const +{ + return mStartDate; +} + +void Journal::setEndDate( const TQDateTime& endDate ) +{ + mEndDate = endDate; +} + +TQDateTime Journal::endDate() const +{ + return mEndDate; +} + +bool Journal::loadAttribute( TQDomElement& element ) +{ + TQString tagName = element.tagName(); + + if ( tagName == "summary" ) + setSummary( element.text() ); + else if ( tagName == "start-date" ) + setStartDate( stringToDateTime( element.text() ) ); + else + // Not handled here + return KolabBase::loadAttribute( element ); + + // We handled this + return true; +} + +bool Journal::saveAttributes( TQDomElement& element ) const +{ + // Save the base class elements + KolabBase::saveAttributes( element ); + + writeString( element, "summary", summary() ); + writeString( element, "start-date", dateTimeToString( startDate() ) ); + + return true; +} + + +bool Journal::loadXML( const TQDomDocument& document ) +{ + TQDomElement top = document.documentElement(); + + if ( top.tagName() != "journal" ) { + tqWarning( "XML error: Top tag was %s instead of the expected Journal", + top.tagName().ascii() ); + return false; + } + + for ( TQDomNode n = top.firstChild(); !n.isNull(); n = n.nextSibling() ) { + if ( n.isComment() ) + continue; + if ( n.isElement() ) { + TQDomElement e = n.toElement(); + if ( !loadAttribute( e ) ) { + // Unhandled tag - save for later storage + //tqDebug( "Unhandled tag: %s", e.toCString().data() ); + } + } else + tqDebug( "Node is not a comment or an element???" ); + } + + return true; +} + +TQString Journal::saveXML() const +{ + TQDomDocument document = domTree(); + TQDomElement element = document.createElement( "journal" ); + element.setAttribute( "version", "1.0" ); + saveAttributes( element ); + document.appendChild( element ); + return document.toString(); +} + +void Journal::saveTo( KCal::Journal* journal ) +{ + KolabBase::saveTo( journal ); + + journal->setSummary( summary() ); + journal->setDtStart( utcToLocal( startDate() ) ); +} + +void Journal::setFields( const KCal::Journal* journal ) +{ + // Set baseclass fields + KolabBase::setFields( journal ); + + // Set our own fields + setSummary( journal->summary() ); + setStartDate( localToUTC( journal->dtStart() ) ); +} + +TQString Journal::productID() const +{ + return TQString( "KOrganizer " ) + korgVersion + ", Kolab resource"; +} diff --git a/tderesources/kolab/kcal/journal.h b/tderesources/kolab/kcal/journal.h new file mode 100644 index 000000000..cd583c6ca --- /dev/null +++ b/tderesources/kolab/kcal/journal.h @@ -0,0 +1,103 @@ +/* + This file is part of the kolab resource - the implementation of the + Kolab storage format. See www.kolab.org for documentation on this. + + Copyright (c) 2004 Bo Thorsen <bo@sonofthor.dk> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. + + In addition, as a special exception, the copyright holders give + permission to link the code of this program with any edition of + the TQt library by Trolltech AS, Norway (or with modified versions + of TQt that use the same license as TQt), and distribute linked + combinations including the two. You must obey the GNU General + Public License in all respects for all of the code used other than + TQt. If you modify this file, you may extend this exception to + your version of the file, but you are not obligated to do so. If + you do not wish to do so, delete this exception statement from + your version. +*/ + +#ifndef KOLAB_JOURNAL_H +#define KOLAB_JOURNAL_H + +#include <kolabbase.h> + +class TQDomElement; + +namespace KCal { + class Journal; +} + +namespace Kolab { + +/** + * This class represents a journal entry, and knows how to load/save it + * from/to XML, and from/to a KCal::Journal. + * The instances of this class are temporary, only used to convert + * one to the other. + */ +class Journal : public KolabBase { +public: + /// Use this to parse an xml string to a journal entry + /// The caller is responsible for deleting the returned journal + static KCal::Journal* xmlToJournal( const TQString& xml, const TQString& tz ); + + /// Use this to get an xml string describing this journal entry + static TQString journalToXML( KCal::Journal*, const TQString& tz ); + + explicit Journal( const TQString& tz, KCal::Journal* journal = 0 ); + virtual ~Journal(); + + virtual TQString type() const { return "Journal"; } + + void saveTo( KCal::Journal* journal ); + + virtual void setSummary( const TQString& summary ); + virtual TQString summary() const; + + virtual void setStartDate( const TQDateTime& startDate ); + virtual TQDateTime startDate() const; + + virtual void setEndDate( const TQDateTime& endDate ); + virtual TQDateTime endDate() const; + + // Load the attributes of this class + virtual bool loadAttribute( TQDomElement& ); + + // Save the attributes of this class + virtual bool saveAttributes( TQDomElement& ) const; + + // Load this journal by reading the XML file + virtual bool loadXML( const TQDomDocument& xml ); + + // Serialize this journal to an XML string + virtual TQString saveXML() const; + +protected: + // Read all known fields from this ical journal + void setFields( const KCal::Journal* ); + + TQString productID() const; + + TQString mSummary; + TQDateTime mStartDate; + TQDateTime mEndDate; +}; + +} + +#endif // KOLAB_JOURNAL_H diff --git a/tderesources/kolab/kcal/kolab.desktop b/tderesources/kolab/kcal/kolab.desktop new file mode 100644 index 000000000..40bcacb00 --- /dev/null +++ b/tderesources/kolab/kcal/kolab.desktop @@ -0,0 +1,52 @@ +[Desktop Entry] +Name=Calendar on IMAP Server via KMail +Name[af]=Kalender op IMAP bediener via KMail +Name[bg]=Календар на сървър IMAP през KMail +Name[br]=Deiziadur en ur servijer IMAP gant KMail +Name[ca]=Calendari sobre servidor IMAP mitjançant KMail +Name[cs]=Kalendář na IMAP serveru přes KMail +Name[da]=Kalender på IMAP-server via KMail +Name[de]=Kalender auf einem IMAP-Server via KMail +Name[el]=Ημερολόγιο σε εξυπηρετητή IMAP μέσω του KMail +Name[es]=Calendario en servidor IMAP por medio de KMail +Name[et]=Kalender IMAP-serveris (KMaili vahendusel) +Name[eu]=Egutegia IMAP zerbitzarian KMail-en bidez +Name[fa]=تقویم روی کارساز IMAP از طریق KMail +Name[fi]=Kalenteri IMAP-palvelimella KMailin avulla +Name[fr]=Agenda sur serveur IMAP (via KMail) +Name[fy]=Aginda op IMAP-tsjinner fia KMail +Name[ga]=Féilire ar Fhreastalaí IMAP via KMail +Name[gl]=Calendario no servidor IMAP mediante KMail +Name[hu]=IMAP-kiszolgálón tárolt naptár a KMailen keresztül +Name[is]=Dagatal á IMAP þjóni gegnum KMail +Name[it]=Calendario su server IMAP via KMail +Name[ja]=KMail 経由 IMAP サーバのカレンダー +Name[kk]=KMail арқылы IMAP серверіндегі күнтізбе +Name[km]=ប្រតិទិនលើម៉ាស៊ីនបម្រើ IMAP តាមរយៈ KMail +Name[lt]=Kalendorius IMAP serveryje per KMail +Name[mk]=Календар на IMAP-сервер преку КПошта +Name[ms]=Kalendar pada pelayan IMAP melalui KMail +Name[nb]=Kalender på IMAP-tjener via KMail +Name[nds]=Kalenner op IMAP-Server över KMail +Name[ne]=केडीई मेल मार्फत IMAP सर्भरमा क्यालेन्डर +Name[nl]=Agenda op IMAP-server via KMail +Name[nn]=Kalender på IMAP-tenar via KMail +Name[pl]=Kalendarz na serwerze IMAP za pośrednictwem KMail +Name[pt]=Calendário em Servidor IMAP via KMail +Name[pt_BR]=Calendário em Servidor IMAP via KMail +Name[ru]=Календарь на сервере IMAP через KMail +Name[sk]=Kalendár na IMAP serveri pomocou KMail +Name[sl]=Koledar na strežniku IMAP preko KMaila +Name[sr]=Календар на IMAP серверу преко KMail-а +Name[sr@Latn]=Kalendar na IMAP serveru preko KMail-a +Name[sv]=Kalender på IMAP-server via Kmail +Name[ta]=IMAP சேவையக வழியாக கேஅஞ்சலில் நாட்காட்டி +Name[tr]=KMail Aracılığı ile IMAP Sunucusunda Takvim +Name[uk]=Календар на сервері IMAP через KMail +Name[zh_CN]=通过 KMail 访问 IMAP 服务器上的日历 +Name[zh_TW]=透過 KMail 取得 IMAP 伺服器上的行事曆 +X-TDE-Library=kcal_kolab +Type=Service +ServiceTypes=KResources/Plugin +X-TDE-ResourceFamily=calendar +X-TDE-ResourceType=imap diff --git a/tderesources/kolab/kcal/resourcekolab.cpp b/tderesources/kolab/kcal/resourcekolab.cpp new file mode 100644 index 000000000..44c13fd2b --- /dev/null +++ b/tderesources/kolab/kcal/resourcekolab.cpp @@ -0,0 +1,1342 @@ +/* + This file is part of the kolab resource - the implementation of the + Kolab storage format. See www.kolab.org for documentation on this. + + Copyright (c) 2004 Bo Thorsen <bo@sonofthor.dk> + 2004 Till Adam <till@klaralvdalens-datakonsult.se> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. + + In addition, as a special exception, the copyright holders give + permission to link the code of this program with any edition of + the TQt library by Trolltech AS, Norway (or with modified versions + of TQt that use the same license as TQt), and distribute linked + combinations including the two. You must obey the GNU General + Public License in all respects for all of the code used other than + TQt. If you modify this file, you may extend this exception to + your version of the file, but you are not obligated to do so. If + you do not wish to do so, delete this exception statement from + your version. +*/ + +#include "resourcekolab.h" +#include "event.h" +#include "task.h" +#include "journal.h" + +#include <kio/observer.h> +#include <kio/uiserver_stub.h> +#include <kapplication.h> +#include <dcopclient.h> +#include <libtdepim/kincidencechooser.h> +#include <kabc/locknull.h> +#include <kmainwindow.h> +#include <klocale.h> +#include <kinputdialog.h> +#include <ktempfile.h> +#include <kmdcodec.h> + +#include <tqfile.h> +#include <tqobject.h> +#include <tqtimer.h> +#include <tqapplication.h> + +#include <assert.h> + +using namespace KCal; +using namespace Kolab; + +static const char* kmailCalendarContentsType = "Calendar"; +static const char* kmailTodoContentsType = "Task"; +static const char* kmailJournalContentsType = "Journal"; +static const char* eventAttachmentMimeType = "application/x-vnd.kolab.event"; +static const char* todoAttachmentMimeType = "application/x-vnd.kolab.task"; +static const char* journalAttachmentMimeType = "application/x-vnd.kolab.journal"; +static const char* incidenceInlineMimeType = "text/calendar"; + + +ResourceKolab::ResourceKolab( const TDEConfig *config ) + : ResourceCalendar( config ), ResourceKolabBase( "ResourceKolab-libkcal" ), + mCalendar( TQString::fromLatin1("UTC") ), mOpen( false ),mResourceChangedTimer( 0, + "mResourceChangedTimer" ), mBatchAddingInProgress( false ) +{ + if ( !config ) { + setResourceName( i18n( "Kolab Server" ) ); + } + setType( "imap" ); + connect( &mResourceChangedTimer, TQT_SIGNAL( timeout() ), + this, TQT_SLOT( slotEmitResourceChanged() ) ); +} + +ResourceKolab::~ResourceKolab() +{ + // The resource is deleted on exit (StdAddressBook's KStaticDeleter), + // and it wasn't closed before that, so close here to save the config. + if ( mOpen ) { + close(); + } +} + +void ResourceKolab::loadSubResourceConfig( TDEConfig& config, + const TQString& name, + const TQString& label, + bool writable, + bool alarmRelevant, + ResourceMap& subResource ) +{ + TDEConfigGroup group( &config, name ); + bool active = group.readBoolEntry( "Active", true ); + subResource.insert( name, Kolab::SubResource( active, writable, + alarmRelevant, label ) ); +} + +bool ResourceKolab::openResource( TDEConfig& config, const char* contentType, + ResourceMap& map ) +{ + // Read the subresource entries from KMail + TQValueList<KMailICalIface::SubResource> subResources; + if ( !kmailSubresources( subResources, contentType ) ) + return false; + map.clear(); + TQValueList<KMailICalIface::SubResource>::ConstIterator it; + for ( it = subResources.begin(); it != subResources.end(); ++it ) + loadSubResourceConfig( config, (*it).location, (*it).label, (*it).writable, + (*it).alarmRelevant, map ); + return true; +} + +bool ResourceKolab::doOpen() +{ + if ( mOpen ) + // Already open + return true; + mOpen = true; + + TDEConfig config( configFile() ); + config.setGroup( "General" ); + mProgressDialogIncidenceLimit = config.readNumEntry("ProgressDialogIncidenceLimit", 200); + + return openResource( config, kmailCalendarContentsType, mEventSubResources ) + && openResource( config, kmailTodoContentsType, mTodoSubResources ) + && openResource( config, kmailJournalContentsType, mJournalSubResources ); +} + +static void writeResourceConfig( TDEConfig& config, ResourceMap& map ) +{ + ResourceMap::ConstIterator it; + for ( it = map.begin(); it != map.end(); ++it ) { + config.setGroup( it.key() ); + config.writeEntry( "Active", it.data().active() ); + } +} + +void ResourceKolab::doClose() +{ + if ( !mOpen ) + // Not open + return; + mOpen = false; + + writeConfig(); +} + +bool ResourceKolab::loadSubResource( const TQString& subResource, + const char* mimetype ) +{ + int count = 0; + if ( !kmailIncidencesCount( count, mimetype, subResource ) ) { + kdError(5650) << "Communication problem in ResourceKolab::load()\n"; + return false; + } + + if ( !count ) + return true; + + const int nbMessages = 200; // read 200 mails at a time (see kabc resource) + + const TQString labelTxt = !strcmp(mimetype, "application/x-vnd.kolab.task") ? i18n( "Loading tasks..." ) + : !strcmp(mimetype, "application/x-vnd.kolab.journal") ? i18n( "Loading journals..." ) + : i18n( "Loading events..." ); + const bool useProgress = tqApp && tqApp->type() != TQApplication::Tty && count > mProgressDialogIncidenceLimit; + if ( useProgress ) + (void)::Observer::self(); // ensure kio_uiserver is running + UIServer_stub uiserver( "kio_uiserver", "UIServer" ); + int progressId = 0; + if ( useProgress ) { + progressId = uiserver.newJob( kapp->dcopClient()->appId(), true ); + uiserver.totalFiles( progressId, count ); + uiserver.infoMessage( progressId, labelTxt ); + uiserver.transferring( progressId, labelTxt ); + } + + for ( int startIndex = 0; startIndex < count; startIndex += nbMessages ) { + TQMap<TQ_UINT32, TQString> lst; + if ( !kmailIncidences( lst, mimetype, subResource, startIndex, nbMessages ) ) { + kdError(5650) << "Communication problem in ResourceKolab::load()\n"; + if ( progressId ) + uiserver.jobFinished( progressId ); + return false; + } + + { // for RAII scoping below + TemporarySilencer t( this ); + for( TQMap<TQ_UINT32, TQString>::ConstIterator it = lst.begin(); it != lst.end(); ++it ) { + addIncidence( mimetype, it.data(), subResource, it.key() ); + } + } + if ( progressId ) { + uiserver.processedFiles( progressId, startIndex ); + uiserver.percent( progressId, 100 * startIndex / count ); + } + +// if ( progress.wasCanceled() ) { +// uiserver.jobFinished( progressId ); +// return false; +// } + } + + if ( progressId ) + uiserver.jobFinished( progressId ); + return true; +} + +bool ResourceKolab::doLoad() +{ + if (!mUidMap.isEmpty() ) { + emit resourceLoaded( this ); + return true; + } + mUidMap.clear(); + + bool result = loadAllEvents() & loadAllTodos() & loadAllJournals(); + if ( result ) { + emit resourceLoaded( this ); + } else { + // FIXME: anyone know if the resource correctly calls loadError() + // if it has one? + } + + return result; +} + +bool ResourceKolab::doLoadAll( ResourceMap& map, const char* mimetype ) +{ + bool rc = true; + for ( ResourceMap::ConstIterator it = map.begin(); it != map.end(); ++it ) { + if ( !it.data().active() ) + // This resource is disabled + continue; + + rc &= loadSubResource( it.key(), mimetype ); + } + return rc; +} + +bool ResourceKolab::loadAllEvents() +{ + removeIncidences( "Event" ); + mCalendar.deleteAllEvents(); + bool kolabStyle = doLoadAll( mEventSubResources, eventAttachmentMimeType ); + bool icalStyle = doLoadAll( mEventSubResources, incidenceInlineMimeType ); + return kolabStyle && icalStyle; +} + +bool ResourceKolab::loadAllTodos() +{ + removeIncidences( "Todo" ); + mCalendar.deleteAllTodos(); + bool kolabStyle = doLoadAll( mTodoSubResources, todoAttachmentMimeType ); + bool icalStyle = doLoadAll( mTodoSubResources, incidenceInlineMimeType ); + + return kolabStyle && icalStyle; +} + +bool ResourceKolab::loadAllJournals() +{ + removeIncidences( "Journal" ); + mCalendar.deleteAllJournals(); + bool kolabStyle = doLoadAll( mJournalSubResources, journalAttachmentMimeType ); + bool icalStyle = doLoadAll( mJournalSubResources, incidenceInlineMimeType ); + + return kolabStyle && icalStyle; +} + +void ResourceKolab::removeIncidences( const TQCString& incidenceType ) +{ + Kolab::UidMap::Iterator mapIt = mUidMap.begin(); + while ( mapIt != mUidMap.end() ) + { + Kolab::UidMap::Iterator it = mapIt++; + // Check the type of this uid: event, todo or journal. + // Need to look up in mCalendar for that. Given the implementation of incidence(uid), + // better call event(uid), todo(uid) etc. directly. + + // A faster but hackish way would probably be to check the type of the resource, + // like mEventSubResources.find( it.data().resource() ) != mEventSubResources.end() ? + const TQString& uid = it.key(); + if ( incidenceType == "Event" && mCalendar.event( uid ) ) + mUidMap.remove( it ); + else if ( incidenceType == "Todo" && mCalendar.todo( uid ) ) + mUidMap.remove( it ); + else if ( incidenceType == "Journal" && mCalendar.journal( uid ) ) + mUidMap.remove( it ); + } +} + +bool ResourceKolab::doSave() +{ + return true; + /* + return kmailTriggerSync( kmailCalendarContentsType ) + && kmailTriggerSync( kmailTodoContentsType ) + && kmailTriggerSync( kmailJournalContentsType ); + */ +} +void ResourceKolab::incidenceUpdatedSilent( KCal::IncidenceBase* incidencebase ) +{ + const TQString uid = incidencebase->uid(); + //kdDebug() << k_funcinfo << uid << endl; + + if ( mUidsPendingUpdate.contains( uid ) || mUidsPendingAdding.contains( uid ) ) { + /* We are currently processing this event ( removing and readding or + * adding it ). If so, ignore this update. Keep the last of these around + * and process once we hear back from KMail on this event. */ + mPendingUpdates.remove( uid ); + mPendingUpdates.insert( uid, incidencebase ); + return; + } + + { // start optimization + /** + KOrganizer and libkcal like calling two Incidence::updated() + for only one user change. That's because after a change, + IncidenceChanger calls incidence->setRevision( rev++ ); + which also calls Incidence::updated(). + + Lets ignore the first updated() and only send to kmail + the second. This makes things faster. + */ + + //IncidenceBase doesn't have revision(), downcast needed. + Incidence *i = dynamic_cast<Incidence*>( incidencebase ); + + if ( i ) { + bool ignoreThisUpdate = false; + + if ( !mLastKnownRevisions.contains( uid ) ) { + mLastKnownRevisions[uid] = i->revision(); + } + + // update the last known revision + if ( mLastKnownRevisions[uid] < i->revision() ) { + mLastKnownRevisions[uid] = i->revision(); + } else { + ignoreThisUpdate = true; + } + + if ( ignoreThisUpdate ) { + return; + } + } + } // end optimization + + TQString subResource; + TQ_UINT32 sernum = 0; + if ( mUidMap.contains( uid ) ) { + subResource = mUidMap[ uid ].resource(); + sernum = mUidMap[ uid ].serialNumber(); + mUidsPendingUpdate.append( uid ); + } + + sendKMailUpdate( incidencebase, subResource, sernum ); +} +void ResourceKolab::incidenceUpdated( KCal::IncidenceBase* incidencebase ) +{ + if ( incidencebase->isReadOnly() ) { + return; + } + + incidencebase->setSyncStatusSilent( KCal::Event::SYNCMOD ); + incidencebase->setLastModified( TQDateTime::currentDateTime() ); + + // we should probably update the revision number here, + // or internally in the Event itself when certain things change. + // need to verify with ical documentation. + incidenceUpdatedSilent( incidencebase ); +} + +void ResourceKolab::resolveConflict( KCal::Incidence* inc, const TQString& subresource, TQ_UINT32 sernum ) +{ + if ( !inc ) { + return; + } + + if ( !mResolveConflict ) { + // we should do no conflict resolution + delete inc; + return; + } + const TQString origUid = inc->uid(); + Incidence* local = mCalendar.incidence( origUid ); + Incidence* localIncidence = 0; + Incidence* addedIncidence = 0; + Incidence* result = 0; + if ( local ) { + if ( *local == *inc ) { + // real duplicate, remove the second one + result = local; + } else { + KIncidenceChooser* ch = new KIncidenceChooser(); + ch->setIncidence( local ,inc ); + if ( KIncidenceChooser::chooseMode == KIncidenceChooser::ask ) { + connect ( this, TQT_SIGNAL( useGlobalMode() ), ch, TQT_SLOT ( useGlobalMode() ) ); + if ( ch->exec() ) { + if ( KIncidenceChooser::chooseMode != KIncidenceChooser::ask ) { + emit useGlobalMode() ; + } + } + } + result = ch->getIncidence(); + delete ch; + } + } else { + // nothing there locally, just take the new one. Can't Happen (TM) + result = inc; + } + if ( result == local ) { + delete inc; + localIncidence = local; + } else if ( result == inc ) { + addedIncidence = inc; + } else if ( result == 0 ) { // take both + addedIncidence = inc; + addedIncidence->setSummary( i18n("Copy of: %1").arg( addedIncidence->summary() ) ); + addedIncidence->setUid( CalFormat::createUniqueId() ); + localIncidence = local; + } + const bool silent = mSilent; + mSilent = false; + if ( !localIncidence ) { + deleteIncidence( local ); // remove local from kmail + } + mUidsPendingDeletion.append( origUid ); + if ( addedIncidence ) { + sendKMailUpdate( addedIncidence, subresource, sernum ); + } else { + kmailDeleteIncidence( subresource, sernum );// remove new from kmail + } + mSilent = silent; +} +void ResourceKolab::addIncidence( const char* mimetype, const TQString& data, + const TQString& subResource, TQ_UINT32 sernum ) +{ + // This uses pointer comparison, so it only works if we use the static + // objects defined in the top of the file + if ( mimetype == eventAttachmentMimeType ) + addEvent( data, subResource, sernum ); + else if ( mimetype == todoAttachmentMimeType ) + addTodo( data, subResource, sernum ); + else if ( mimetype == journalAttachmentMimeType ) + addJournal( data, subResource, sernum ); + else if ( mimetype == incidenceInlineMimeType ) { + Incidence *inc = mFormat.fromString( data ); + addIncidence( inc, subResource, sernum ); + } +} + + +bool ResourceKolab::sendKMailUpdate( KCal::IncidenceBase* incidencebase, const TQString& subresource, + TQ_UINT32 sernum ) +{ + const TQString& type = incidencebase->type(); + const char* mimetype = 0; + TQString data; + bool isXMLStorageFormat = kmailStorageFormat( subresource ) == KMailICalIface::StorageXML; + if ( type == "Event" ) { + if( isXMLStorageFormat ) { + mimetype = eventAttachmentMimeType; + data = Kolab::Event::eventToXML( static_cast<KCal::Event *>(incidencebase), + mCalendar.timeZoneId() ); + } else { + mimetype = incidenceInlineMimeType; + data = mFormat.createScheduleMessage( static_cast<KCal::Event *>(incidencebase), + Scheduler::Request ); + } + } else if ( type == "Todo" ) { + if( isXMLStorageFormat ) { + mimetype = todoAttachmentMimeType; + data = Kolab::Task::taskToXML( static_cast<KCal::Todo *>(incidencebase), + mCalendar.timeZoneId() ); + } else { + mimetype = incidenceInlineMimeType; + data = mFormat.createScheduleMessage( static_cast<KCal::Todo *>(incidencebase), + Scheduler::Request ); + } + } else if ( type == "Journal" ) { + if( isXMLStorageFormat ) { + mimetype = journalAttachmentMimeType; + data = Kolab::Journal::journalToXML( static_cast<KCal::Journal *>(incidencebase ), + mCalendar.timeZoneId() ); + } else { + mimetype = incidenceInlineMimeType; + data = mFormat.createScheduleMessage( static_cast<KCal::Journal *>(incidencebase), + Scheduler::Request ); + } + } else { + kdWarning(5006) << "Can't happen: unhandled type=" << type << endl; + } + +// kdDebug() << k_funcinfo << "Data string:\n" << data << endl; + + KCal::Incidence* incidence = static_cast<KCal::Incidence *>( incidencebase ); + + KCal::Attachment::List atts = incidence->attachments(); + TQStringList attURLs, attMimeTypes, attNames; + TQValueList<KTempFile*> tmpFiles; + for ( KCal::Attachment::List::ConstIterator it = atts.constBegin(); it != atts.constEnd(); ++it ) { + if ( (*it)->isUri() ) { + continue; + } + KTempFile *tempFile = new KTempFile; + if ( tempFile->status() == 0 ) { // open ok + const TQByteArray decoded = (*it)->decodedData() ; + + tempFile->file()->writeBlock( decoded.data(), decoded.count() ); + KURL url; + url.setPath( tempFile->name() ); + attURLs.append( url.url() ); + attMimeTypes.append( (*it)->mimeType() ); + attNames.append( (*it)->label() ); + tempFile->close(); + tmpFiles.append( tempFile ); + } else { + kdWarning(5006) << "Cannot open temporary file for attachment"; + } + } + TQStringList deletedAtts; + if ( kmailListAttachments( deletedAtts, subresource, sernum ) ) { + for ( TQStringList::ConstIterator it = attNames.constBegin(); it != attNames.constEnd(); ++it ) { + deletedAtts.remove( *it ); + } + } + CustomHeaderMap customHeaders; + if ( incidence->schedulingID() != incidence->uid() ) { + customHeaders.insert( "X-Kolab-SchedulingID", incidence->schedulingID() ); + } + + TQString subject = incidencebase->uid(); + if ( !isXMLStorageFormat ) subject.prepend( "iCal " ); // conform to the old style + + // behold, sernum is an in-parameter + const bool rc = kmailUpdate( subresource, sernum, data, mimetype, subject, customHeaders, attURLs, attMimeTypes, attNames, deletedAtts ); + // update the serial number + if ( mUidMap.contains( incidencebase->uid() ) ) { + mUidMap[ incidencebase->uid() ].setSerialNumber( sernum ); + } + + for( TQValueList<KTempFile *>::Iterator it = tmpFiles.begin(); it != tmpFiles.end(); ++it ) { + (*it)->setAutoDelete( true ); + delete (*it); + } + + return rc; +} + +bool ResourceKolab::addIncidence( KCal::Incidence* incidence, const TQString& _subresource, + TQ_UINT32 sernum ) +{ + Q_ASSERT( incidence ); + if ( !incidence ) { + return false; + } + + kdDebug() << "Resourcekolab, adding incidence " + << incidence->summary() + << "; subresource = " << _subresource + << "; sernum = " << sernum + << "; mBatchAddingInProgress = " << mBatchAddingInProgress + << endl; + + TQString uid = incidence->uid(); + TQString subResource = _subresource; + + Kolab::ResourceMap *map = &mEventSubResources; // don't use a ref here! + + const TQString& type = incidence->type(); + if ( type == "Event" ) { + map = &mEventSubResources; + } else if ( type == "Todo" ) { + map = &mTodoSubResources; + } else if ( type == "Journal" ) { + map = &mJournalSubResources; + } else { + kdWarning() << "unknown type " << type << endl; + } + + if ( !mSilent ) { /* We got this one from the user, tell KMail. */ + // Find out if this event was previously stored in KMail + bool newIncidence = _subresource.isEmpty(); + if ( newIncidence ) { + ResourceType type = Incidences; + // Add a description of the incidence + TQString text = "<b><font size=\"+1\">"; + if ( incidence->type() == "Event" ) { + type = Events; + text += i18n( "Choose the folder where you want to store this event" ); + } else if ( incidence->type() == "Todo" ) { + type = Tasks; + text += i18n( "Choose the folder where you want to store this task" ); + } else { + text += i18n( "Choose the folder where you want to store this incidence" ); + } + text += "<font></b><br>"; + if ( !incidence->summary().isEmpty() ) + text += i18n( "<b>Summary:</b> %1" ).arg( incidence->summary() ) + "<br>"; + if ( !incidence->location().isEmpty() ) + text += i18n( "<b>Location:</b> %1" ).arg( incidence->location() ); + text += "<br>"; + if ( !incidence->doesFloat() ) + text += i18n( "<b>Start:</b> %1, %2" ) + .arg( incidence->dtStartDateStr(), incidence->dtStartTimeStr() ); + else + text += i18n( "<b>Start:</b> %1" ).arg( incidence->dtStartDateStr() ); + text += "<br>"; + if ( incidence->type() == "Event" ) { + Event* event = static_cast<Event*>( incidence ); + if ( event->hasEndDate() ) { + if ( !event->doesFloat() ) { + text += i18n( "<b>End:</b> %1, %2" ) + .arg( event->dtEndDateStr(), event->dtEndTimeStr() ); + } else { + text += i18n( "<b>End:</b> %1" ).arg( event->dtEndDateStr() ); + } + } + text += "<br>"; + } + + // Lets not warn the user 100 times that there's no writable resource + // and not ask 100 times which resource to use + if ( !mBatchAddingInProgress || !mLastUsedResources.contains( type ) ) { + subResource = findWritableResource( type, *map, text ); + mLastUsedResources[type] = subResource; + } else { + subResource = mLastUsedResources[type]; + } + + if ( subResource.isEmpty() ) { + switch( mErrorCode ) { + case NoWritableFound: + setException( new ErrorFormat( ErrorFormat::NoWritableFound ) ); + break; + case UserCancel: + setException( new ErrorFormat( ErrorFormat::UserCancel ) ); + break; + case NoError: + break; + } + } + } + + if ( subResource.isEmpty() ) { + endAddingIncidences(); // cleanup + kdDebug(5650) << "ResourceKolab: subResource is empty" << endl; + return false; + } + + mNewIncidencesMap.insert( uid, subResource ); + + if ( !sendKMailUpdate( incidence, subResource, sernum ) ) { + kdError(5650) << "Communication problem in ResourceKolab::addIncidence()\n"; + endAddingIncidences(); // cleanup + return false; + } else { + // KMail is doing it's best to add the event now, put a sticker on it, + // so we know it's one of our transient ones + mUidsPendingAdding.append( uid ); + + /* Add to the cache immediately if this is a new event coming from + * KOrganizer. It relies on the incidence being in the calendar when + * addIncidence returns. */ + if ( newIncidence || sernum == 0 ) { + mCalendar.addIncidence( incidence ); + incidence->registerObserver( this ); + } + } + } else { /* KMail told us */ + const bool ourOwnUpdate = mUidsPendingUpdate.contains( uid ); + kdDebug( 5650 ) << "addIncidence: ourOwnUpdate " << ourOwnUpdate << endl; + /* Check if we updated this one, which means kmail deleted and added it. + * We know the new state, so lets just not do much at all. The old incidence + * in the calendar remains valid, but the serial number changed, so we need to + * update that */ + if ( ourOwnUpdate ) { + mUidsPendingUpdate.remove( uid ); + mUidMap.remove( uid ); + mUidMap[ uid ] = StorageReference( subResource, sernum ); + } else { + /* This is a real add, from KMail, we didn't trigger this ourselves. + * If this uid already exists in this folder, do conflict resolution, + * unless the folder is read-only, in which case the user should not be + * offered a means of putting mails in a folder she'll later be unable to + * upload. Skip the incidence, in this case. */ + if ( mUidMap.contains( uid ) ) { + if ( mUidMap[ uid ].resource() == subResource ) { + if ( (*map)[ subResource ].writable() ) { + kdDebug( 5650 ) << "lets resolve the conflict " << endl; + resolveConflict( incidence, subResource, sernum ); + } else { + kdWarning( 5650 ) << "Duplicate event in a read-only folder detected! " + "Please inform the owner of the folder. " << endl; + } + return true; + } else { + // duplicate uid in a different folder, do the internal-uid tango + incidence->setSchedulingID( uid ); + + incidence->setUid( CalFormat::createUniqueId( ) ); + uid = incidence->uid(); + + /* Will be needed when kmail triggers a delete, so we don't delete the inocent + * incidence that's sharing the uid with this one */ + mOriginalUID2fakeUID[tqMakePair( incidence->schedulingID(), subResource )] = uid; + } + } + /* Add to the cache if the add didn't come from KOrganizer, in which case + * we've already added it, and listen to updates from KOrganizer for it. */ + if ( !mUidsPendingAdding.contains( uid ) ) { + mCalendar.addIncidence( incidence ); + incidence->registerObserver( this ); + } + if ( !subResource.isEmpty() && sernum != 0 ) { + mUidMap[ uid ] = StorageReference( subResource, sernum ); + incidence->setReadOnly( !(*map)[ subResource ].writable() ); + } + } + /* Check if there are updates for this uid pending and if so process them. */ + if ( KCal::IncidenceBase *update = mPendingUpdates.find( uid ) ) { + mSilent = false; // we do want to tell KMail + mPendingUpdates.remove( uid ); + incidenceUpdated( update ); + } else { + /* If the uid was added by KMail, KOrganizer needs to be told, so + * schedule emitting of the resourceChanged signal. */ + if ( !mUidsPendingAdding.contains( uid ) ) { + if ( !ourOwnUpdate ) mResourceChangedTimer.changeInterval( 100 ); + } else { + mUidsPendingAdding.remove( uid ); + } + } + + mNewIncidencesMap.remove( uid ); + } + return true; +} + +bool ResourceKolab::addEvent( KCal::Event *event ) +{ + return addEvent( event, TQString() ); +} + +bool ResourceKolab::addEvent( KCal::Event *event, const TQString &subResource ) +{ + if ( mUidMap.contains( event->uid() ) ) { + return true; //noop + } else { + return addIncidence( event, subResource, 0 ); + } +} + +void ResourceKolab::addEvent( const TQString& xml, const TQString& subresource, + TQ_UINT32 sernum ) +{ + KCal::Event* event = Kolab::Event::xmlToEvent( xml, mCalendar.timeZoneId(), this, subresource, sernum ); + Q_ASSERT( event ); + if ( event ) { + addIncidence( event, subresource, sernum ); + } +} + +bool ResourceKolab::deleteIncidence( KCal::Incidence* incidence ) +{ + if ( incidence->isReadOnly() ) { + return false; + } + + const TQString uid = incidence->uid(); + if( !mUidMap.contains( uid ) ) return false; // Odd + /* The user told us to delete, tell KMail */ + if ( !mSilent ) { + kmailDeleteIncidence( mUidMap[ uid ].resource(), + mUidMap[ uid ].serialNumber() ); + mUidsPendingDeletion.append( uid ); + incidence->unRegisterObserver( this ); + mCalendar.deleteIncidence( incidence ); + mUidMap.remove( uid ); + } else { + assert( false ); // If this still happens, something is very wrong + } + return true; +} + +bool ResourceKolab::deleteEvent( KCal::Event* event ) +{ + return deleteIncidence( event ); +} + +KCal::Event* ResourceKolab::event( const TQString& uid ) +{ + return mCalendar.event(uid); +} + +KCal::Event::List ResourceKolab::rawEvents( EventSortField sortField, SortDirection sortDirection ) +{ + return mCalendar.rawEvents( sortField, sortDirection ); +} + +KCal::Event::List ResourceKolab::rawEventsForDate( const TQDate& date, + EventSortField sortField, + SortDirection sortDirection ) +{ + return mCalendar.rawEventsForDate( date, sortField, sortDirection ); +} + +KCal::Event::List ResourceKolab::rawEventsForDate( const TQDateTime& qdt ) +{ + return mCalendar.rawEventsForDate( qdt ); +} + +KCal::Event::List ResourceKolab::rawEvents( const TQDate& start, + const TQDate& end, + bool inclusive ) +{ + return mCalendar.rawEvents( start, end, inclusive ); +} + +bool ResourceKolab::addTodo( KCal::Todo *todo ) +{ + return addTodo( todo, TQString() ); +} + +bool ResourceKolab::addTodo( KCal::Todo *todo, const TQString &subResource ) +{ + if ( mUidMap.contains( todo->uid() ) ) { + return true; //noop + } else { + return addIncidence( todo, subResource, 0 ); + } +} + +void ResourceKolab::addTodo( const TQString& xml, const TQString& subresource, + TQ_UINT32 sernum ) +{ + KCal::Todo* todo = Kolab::Task::xmlToTask( xml, mCalendar.timeZoneId(), this, subresource, sernum ); + Q_ASSERT( todo ); + if ( todo ) { + addIncidence( todo, subresource, sernum ); + } +} + +bool ResourceKolab::deleteTodo( KCal::Todo* todo ) +{ + return deleteIncidence( todo ); +} + +KCal::Todo* ResourceKolab::todo( const TQString& uid ) +{ + return mCalendar.todo( uid ); +} + +KCal::Todo::List ResourceKolab::rawTodos( TodoSortField sortField, SortDirection sortDirection ) +{ + return mCalendar.rawTodos( sortField, sortDirection ); +} + +KCal::Todo::List ResourceKolab::rawTodosForDate( const TQDate& date ) +{ + return mCalendar.rawTodosForDate( date ); +} + +bool ResourceKolab::addJournal( KCal::Journal *journal ) +{ + return addJournal( journal, TQString() ); +} + +bool ResourceKolab::addJournal( KCal::Journal *journal, const TQString &subResource ) +{ + if ( mUidMap.contains( journal->uid() ) ) + return true; //noop + else + return addIncidence( journal, subResource, 0 ); +} + +void ResourceKolab::addJournal( const TQString& xml, const TQString& subresource, + TQ_UINT32 sernum ) +{ + KCal::Journal* journal = + Kolab::Journal::xmlToJournal( xml, mCalendar.timeZoneId() ); + Q_ASSERT( journal ); + if( journal ) { + addIncidence( journal, subresource, sernum ); + } +} + +bool ResourceKolab::deleteJournal( KCal::Journal* journal ) +{ + return deleteIncidence( journal ); +} + +KCal::Journal* ResourceKolab::journal( const TQString& uid ) +{ + return mCalendar.journal(uid); +} + +KCal::Journal::List ResourceKolab::rawJournals( JournalSortField sortField, SortDirection sortDirection ) +{ + return mCalendar.rawJournals( sortField, sortDirection ); +} + +KCal::Journal::List ResourceKolab::rawJournalsForDate( const TQDate &date ) +{ + return mCalendar.rawJournalsForDate( date ); +} + +KCal::Alarm::List ResourceKolab::relevantAlarms( const KCal::Alarm::List &alarms ) +{ + KCal::Alarm::List relevantAlarms; + KCal::Alarm::List::ConstIterator it( alarms.begin() ); + while ( it != alarms.end() ) { + KCal::Alarm *a = (*it); + ++it; + const TQString &uid = a->parent()->uid(); + if ( mUidMap.contains( uid ) ) { + const TQString &sr = mUidMap[ uid ].resource(); + Kolab::SubResource *subResource = 0; + if ( mEventSubResources.contains( sr ) ) + subResource = &( mEventSubResources[ sr ] ); + else if ( mTodoSubResources.contains( sr ) ) + subResource = &( mTodoSubResources[ sr ] ); + assert( subResource ); + if ( subResource->alarmRelevant() ) + relevantAlarms.append ( a ); + else { + kdDebug(5650) << "Alarm skipped, not relevant." << endl; + } + } + } + return relevantAlarms; +} + + + +KCal::Alarm::List ResourceKolab::alarms( const TQDateTime& from, + const TQDateTime& to ) +{ + return relevantAlarms( mCalendar.alarms( from, to ) ); +} + +KCal::Alarm::List ResourceKolab::alarmsTo( const TQDateTime& to ) +{ + return relevantAlarms( mCalendar.alarmsTo(to) ); +} + +void ResourceKolab::setTimeZoneId( const TQString& tzid ) +{ + mCalendar.setTimeZoneId( tzid ); + mFormat.setTimeZone( mCalendar.timeZoneId(), !mCalendar.isLocalTime() ); +} + +bool ResourceKolab::fromKMailAddIncidence( const TQString& type, + const TQString& subResource, + TQ_UINT32 sernum, + int format, + const TQString& data ) +{ + bool rc = true; + TemporarySilencer t( this ); // RAII + if ( type != kmailCalendarContentsType && type != kmailTodoContentsType + && type != kmailJournalContentsType ) { + // Not ours + return false; + } + + if ( !subresourceActive( subResource ) ) { + return true; + } + + if ( format == KMailICalIface::StorageXML ) { + // If this data file is one of ours, load it here + if ( type == kmailCalendarContentsType ) { + addEvent( data, subResource, sernum ); + } else if ( type == kmailTodoContentsType ) { + addTodo( data, subResource, sernum ); + } else if ( type == kmailJournalContentsType ) { + addJournal( data, subResource, sernum ); + } else { + rc = false; + } + } else { + Incidence *inc = mFormat.fromString( data ); + if ( inc ) { + addIncidence( inc, subResource, sernum ); + } else { + rc = false; + } + } + return rc; +} + +void ResourceKolab::fromKMailDelIncidence( const TQString& type, + const TQString& subResource, + const TQString& uid ) +{ + if ( type != kmailCalendarContentsType && type != kmailTodoContentsType + && type != kmailJournalContentsType ) + // Not ours + return; + if ( !subresourceActive( subResource ) ) return; + + // Can't be in both, by contract + if ( mUidsPendingDeletion.find( uid ) != mUidsPendingDeletion.end() ) { + mUidsPendingDeletion.remove( mUidsPendingDeletion.find( uid ) ); + } else if ( mUidsPendingUpdate.contains( uid ) ) { + // It's good to know if was deleted, but we are waiting on a new one to + // replace it, so let's just sit tight. + } else { + TQString uidToUse; + + TQPair<TQString, TQString> p( uid, subResource ); + if ( mOriginalUID2fakeUID.contains( p ) ) { + // Incidence with the same uid in a different folder... + // use the UID that addIncidence(...) generated + uidToUse = mOriginalUID2fakeUID[p]; + } else { + uidToUse = uid; + } + + // We didn't trigger this, so KMail did, remove the reference to the uid + KCal::Incidence* incidence = mCalendar.incidence( uidToUse ); + if( incidence ) { + incidence->unRegisterObserver( this ); + mCalendar.deleteIncidence( incidence ); + } + mUidMap.remove( uidToUse ); + mOriginalUID2fakeUID.remove( p ); + mResourceChangedTimer.changeInterval( 100 ); + } +} + +void ResourceKolab::fromKMailRefresh( const TQString& type, + const TQString& /*subResource*/ ) +{ + // TODO: Only load the specified subResource + if ( type == "Calendar" ) + loadAllEvents(); + else if ( type == "Task" ) + loadAllTodos(); + else if ( type == "Journal" ) + loadAllJournals(); + else + kdWarning(5006) << "KCal Kolab resource: fromKMailRefresh: unknown type " << type << endl; + mResourceChangedTimer.changeInterval( 100 ); +} + +void ResourceKolab::fromKMailAddSubresource( const TQString& type, + const TQString& subResource, + const TQString& label, + bool writable, bool alarmRelevant ) +{ + ResourceMap* map = 0; + const char* mimetype = 0; + if ( type == kmailCalendarContentsType ) { + map = &mEventSubResources; + mimetype = eventAttachmentMimeType; + } else if ( type == kmailTodoContentsType ) { + map = &mTodoSubResources; + mimetype = todoAttachmentMimeType; + } else if ( type == kmailJournalContentsType ) { + map = &mJournalSubResources; + mimetype = journalAttachmentMimeType; + } else + // Not ours + return; + + if ( map->contains( subResource ) ) + // Already registered + return; + + TDEConfig config( configFile() ); + config.setGroup( subResource ); + + bool active = config.readBoolEntry( subResource, true ); + (*map)[ subResource ] = Kolab::SubResource( active, writable, + alarmRelevant, label ); + loadSubResource( subResource, mimetype ); + emit signalSubresourceAdded( this, type, subResource, label ); +} + +void ResourceKolab::fromKMailDelSubresource( const TQString& type, + const TQString& subResource ) +{ + ResourceMap* map = subResourceMap( type ); + if ( !map ) // not ours + return; + if ( map->contains( subResource ) ) + map->erase( subResource ); + else + // Not registered + return; + + // Delete from the config file + TDEConfig config( configFile() ); + config.deleteGroup( subResource ); + config.sync(); + + unloadSubResource( subResource ); + + emit signalSubresourceRemoved( this, type, subResource ); +} + +TQStringList ResourceKolab::subresources() const +{ + // Workaround: The ResourceView in KOrganizer wants to know this + // before it opens the resource :-( Make sure we are open + const_cast<ResourceKolab*>( this )->doOpen(); + return ( mEventSubResources.keys() + + mTodoSubResources.keys() + + mJournalSubResources.keys() ); +} + +const TQString +ResourceKolab::labelForSubresource( const TQString& subresource ) const +{ + if ( mEventSubResources.contains( subresource ) ) + return mEventSubResources[ subresource ].label(); + if ( mTodoSubResources.contains( subresource ) ) + return mTodoSubResources[ subresource ].label(); + if ( mJournalSubResources.contains( subresource ) ) + return mJournalSubResources[ subresource ].label(); + return subresource; +} + +void ResourceKolab::fromKMailAsyncLoadResult( const TQMap<TQ_UINT32, TQString>& map, + const TQString& type, + const TQString& folder ) +{ + TemporarySilencer t( this ); + for( TQMap<TQ_UINT32, TQString>::ConstIterator it = map.begin(); it != map.end(); ++it ) + addIncidence( type.latin1(), it.data(), folder, it.key() ); +} + +bool ResourceKolab::subresourceActive( const TQString& subresource ) const +{ + // Workaround: The ResourceView in KOrganizer wants to know this + // before it opens the resource :-( Make sure we are open + const_cast<ResourceKolab*>( this )->doOpen(); + + if ( mEventSubResources.contains( subresource ) ) + return mEventSubResources[ subresource ].active(); + if ( mTodoSubResources.contains( subresource ) ) + return mTodoSubResources[ subresource ].active(); + if ( mJournalSubResources.contains( subresource ) ) + return mJournalSubResources[ subresource ].active(); + + // Safe default bet: + kdDebug(5650) << "subresourceActive( " << subresource << " ): Safe bet\n"; + + return true; +} + +void ResourceKolab::setSubresourceActive( const TQString &subresource, bool v ) +{ + ResourceMap *map = 0; + const char* mimeType = 0; + if ( mEventSubResources.contains( subresource ) ) { + map = &mEventSubResources; + mimeType = eventAttachmentMimeType; + } + if ( mTodoSubResources.contains( subresource ) ) { + map = &mTodoSubResources; + mimeType = todoAttachmentMimeType; + } + if ( mJournalSubResources.contains( subresource ) ) { + map = &mJournalSubResources; + mimeType = journalAttachmentMimeType; + } + + if ( map && ( ( *map )[ subresource ].active() != v ) ) { + ( *map )[ subresource ].setActive( v ); + if ( v ) { + loadSubResource( subresource, mimeType ); + } else { + unloadSubResource( subresource ); + } + mResourceChangedTimer.changeInterval( 100 ); + } + TQTimer::singleShot( 0, this, TQT_SLOT(writeConfig()) ); +} + +bool ResourceKolab::subresourceWritable( const TQString& subresource ) const +{ + // Workaround: The ResourceView in KOrganizer wants to know this + // before it opens the resource :-( Make sure we are open + const_cast<ResourceKolab*>( this )->doOpen(); + + if ( mEventSubResources.contains( subresource ) ) + return mEventSubResources[ subresource ].writable(); + if ( mTodoSubResources.contains( subresource ) ) + return mTodoSubResources[ subresource ].writable(); + if ( mJournalSubResources.contains( subresource ) ) + return mJournalSubResources[ subresource ].writable(); + + return false; //better a safe default +} + +void ResourceKolab::slotEmitResourceChanged() +{ + kdDebug(5650) << "KCal Kolab resource: emitting resource changed " << endl; + mResourceChangedTimer.stop(); + emit resourceChanged( this ); +} + +KABC::Lock* ResourceKolab::lock() +{ + return new KABC::LockNull( true ); +} + +Kolab::ResourceMap* ResourceKolab::subResourceMap( const TQString& contentsType ) +{ + if ( contentsType == kmailCalendarContentsType ) { + return &mEventSubResources; + } else if ( contentsType == kmailTodoContentsType ) { + return &mTodoSubResources; + } else if ( contentsType == kmailJournalContentsType ) { + return &mJournalSubResources; + } + // Not ours + return 0; +} + + +/*virtual*/ +bool ResourceKolab::addSubresource( const TQString& resource, const TQString& parent ) +{ + kdDebug(5650) << "KCal Kolab resource - adding subresource: " << resource << endl; + TQString contentsType = kmailCalendarContentsType; + if ( !parent.isEmpty() ) { + if ( mEventSubResources.contains( parent ) ) + contentsType = kmailCalendarContentsType; + else if ( mTodoSubResources.contains( parent ) ) + contentsType = kmailTodoContentsType; + else if ( mJournalSubResources.contains( parent ) ) + contentsType = kmailJournalContentsType; + } else { + TQStringList contentTypeChoices; + contentTypeChoices << i18n("Calendar") << i18n("Tasks") << i18n("Journals"); + const TQString caption = i18n("Which kind of subresource should this be?"); + const TQString choice = KInputDialog::getItem( caption, TQString(), contentTypeChoices ); + if ( choice == contentTypeChoices[0] ) + contentsType = kmailCalendarContentsType; + else if ( choice == contentTypeChoices[1] ) + contentsType = kmailTodoContentsType; + else if ( choice == contentTypeChoices[2] ) + contentsType = kmailJournalContentsType; + } + + return kmailAddSubresource( resource, parent, contentsType ); +} + +/*virtual*/ +bool ResourceKolab::removeSubresource( const TQString& resource ) +{ + kdDebug(5650) << "KCal Kolab resource - removing subresource: " << resource << endl; + return kmailRemoveSubresource( resource ); +} + +/*virtual*/ +TQString ResourceKolab::subresourceIdentifier( Incidence *incidence ) +{ + TQString uid = incidence->uid(); + if ( mUidMap.contains( uid ) ) + return mUidMap[ uid ].resource(); + else + if ( mNewIncidencesMap.contains( uid ) ) + return mNewIncidencesMap[ uid ]; + else + return TQString(); +} + + +bool ResourceKolab::unloadSubResource( const TQString& subResource ) +{ + const bool silent = mSilent; + mSilent = true; + Kolab::UidMap::Iterator mapIt = mUidMap.begin(); + TQPtrList<KCal::Incidence> incidences; + while ( mapIt != mUidMap.end() ) + { + Kolab::UidMap::Iterator it = mapIt++; + const StorageReference ref = it.data(); + if ( ref.resource() != subResource ) continue; + // FIXME incidence() is expensive + KCal::Incidence* incidence = mCalendar.incidence( it.key() ); + if( incidence ) { + // register all observers first before actually deleting them + // in case of inter-incidence relations the other part will get + // the change notification otherwise + incidence->unRegisterObserver( this ); + incidences.append( incidence ); + } + mUidMap.remove( it ); + } + TQPtrListIterator<KCal::Incidence> it( incidences ); + for ( ; it.current(); ++it ) { + mCalendar.deleteIncidence( it.current() ); + } + mSilent = silent; + return true; +} + +TQString ResourceKolab::subresourceType( const TQString &resource ) +{ + if ( mEventSubResources.contains( resource ) ) + return "event"; + if ( mTodoSubResources.contains( resource ) ) + return "todo"; + if ( mJournalSubResources.contains( resource ) ) + return "journal"; + return TQString(); +} + +void ResourceKolab::writeConfig() +{ + TDEConfig config( configFile() ); + writeResourceConfig( config, mEventSubResources ); + writeResourceConfig( config, mTodoSubResources ); + writeResourceConfig( config, mJournalSubResources ); +} + +void ResourceKolab::beginAddingIncidences() +{ + mBatchAddingInProgress = true; +} + +void ResourceKolab::endAddingIncidences() +{ + mBatchAddingInProgress = false; + mLastUsedResources.clear(); +} + +#include "resourcekolab.moc" diff --git a/tderesources/kolab/kcal/resourcekolab.h b/tderesources/kolab/kcal/resourcekolab.h new file mode 100644 index 000000000..fdf952cc3 --- /dev/null +++ b/tderesources/kolab/kcal/resourcekolab.h @@ -0,0 +1,284 @@ +/* + This file is part of the kolab resource - the implementation of the + Kolab storage format. See www.kolab.org for documentation on this. + + Copyright (c) 2004 Bo Thorsen <bo@sonofthor.dk> + 2004 Till Adam <till@klaralvdalens-datakonsult.se> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. + + In addition, as a special exception, the copyright holders give + permission to link the code of this program with any edition of + the TQt library by Trolltech AS, Norway (or with modified versions + of TQt that use the same license as TQt), and distribute linked + combinations including the two. You must obey the GNU General + Public License in all respects for all of the code used other than + TQt. If you modify this file, you may extend this exception to + your version of the file, but you are not obligated to do so. If + you do not wish to do so, delete this exception statement from + your version. +*/ + +#ifndef KCAL_RESOURCEKOLAB_H +#define KCAL_RESOURCEKOLAB_H + +#include <tqtimer.h> + +#include <tdepimmacros.h> +#include <libkcal/calendarlocal.h> +#include <libkcal/icalformat.h> +#include <libkcal/resourcecalendar.h> +#include "../shared/resourcekolabbase.h" + +namespace KCal { + +struct TemporarySilencer; + +class KDE_EXPORT ResourceKolab : public KCal::ResourceCalendar, + public KCal::IncidenceBase::Observer, + public Kolab::ResourceKolabBase +{ + Q_OBJECT + + friend struct TemporarySilencer; + +public: + ResourceKolab( const TDEConfig* ); + virtual ~ResourceKolab(); + + /// Load resource data. + bool doLoad(); + + /// Save resource data. + bool doSave(); + + /// Open the notes resource. + bool doOpen(); + /// Close the notes resource. + void doClose(); + + // The libkcal functions. See the resource for descriptions + KDE_DEPRECATED bool addEvent( KCal::Event *event ); + bool addEvent( KCal::Event *event, const TQString &subResource ); + bool deleteEvent( KCal::Event * ); + KCal::Event* event( const TQString &UniqueStr ); + KCal::Event::List rawEvents( EventSortField sortField = EventSortUnsorted, SortDirection sortDirection = SortDirectionAscending ); + KCal::Event::List rawEventsForDate( + const TQDate& date, + EventSortField sortField=EventSortUnsorted, + SortDirection sortDirection=SortDirectionAscending ); + KCal::Event::List rawEventsForDate( const TQDateTime& qdt ); + KCal::Event::List rawEvents( const TQDate& start, const TQDate& end, + bool inclusive = false ); + + KDE_DEPRECATED bool addTodo( KCal::Todo * todo ); + bool addTodo( KCal::Todo *todo, const TQString &subResource ); + bool deleteTodo( KCal::Todo * ); + KCal::Todo* todo( const TQString &uid ); + KCal::Todo::List rawTodos( TodoSortField sortField = TodoSortUnsorted, SortDirection sortDirection = SortDirectionAscending ); + KCal::Todo::List rawTodosForDate( const TQDate& date ); + + KDE_DEPRECATED bool addJournal( KCal::Journal * ); + bool addJournal( KCal::Journal *, const TQString &subResource ); + bool deleteJournal( KCal::Journal * ); + KCal::Journal* journal( const TQString &uid ); + KCal::Journal::List rawJournals( JournalSortField sortField = JournalSortUnsorted, SortDirection sortDirection = SortDirectionAscending ); + KCal::Journal::List rawJournalsForDate( const TQDate &date ); + + KCal::Alarm::List alarms( const TQDateTime& from, const TQDateTime& to ); + KCal::Alarm::List alarmsTo( const TQDateTime& to ); + + void setTimeZoneId( const TQString& tzid ); + + bool deleteIncidence( KCal::Incidence* i ); + + /// The ResourceKolabBase methods called by KMail + bool fromKMailAddIncidence( const TQString& type, const TQString& subResource, + TQ_UINT32 sernum, int format, const TQString& data ); + void fromKMailDelIncidence( const TQString& type, const TQString& subResource, + const TQString& uid ); + void fromKMailRefresh( const TQString& type, const TQString& subResource ); + + /// Listen to KMail changes in the amount of sub resources + void fromKMailAddSubresource( const TQString& type, const TQString& subResource, + const TQString& label, bool writable, + bool alarmRelevant ); + void fromKMailDelSubresource( const TQString& type, const TQString& subResource ); + + void fromKMailAsyncLoadResult( const TQMap<TQ_UINT32, TQString>& map, + const TQString& type, + const TQString& folder ); + + /** Return the list of subresources. */ + TQStringList subresources() const; + + bool canHaveSubresources() const { return true; } + + /** Is this subresource active? */ + bool subresourceActive( const TQString& ) const; + /** (De)activate the subresource */ + virtual void setSubresourceActive( const TQString &, bool ); + + /** Is this subresource writable? */ + bool subresourceWritable( const TQString& ) const; + + /** What is the label for this subresource? */ + virtual const TQString labelForSubresource( const TQString& resource ) const; + + virtual TQString subresourceIdentifier( Incidence *incidence ); + + virtual bool addSubresource( const TQString& resource, const TQString& parent ); + virtual bool removeSubresource( const TQString& resource ); + + virtual TQString subresourceType( const TQString &resource ); + + KABC::Lock* lock(); + + void beginAddingIncidences(); + + void endAddingIncidences(); + +signals: + void useGlobalMode(); +protected slots: + void slotEmitResourceChanged(); + void writeConfig(); + +protected: + /** + * Return list of alarms which are relevant for the current user. These + * are the ones coming from folders which the user has "Administer" rights + * for, as per ACL */ + KCal::Alarm::List relevantAlarms( const KCal::Alarm::List &alarms ); + +private: + void removeIncidences( const TQCString& incidenceType ); + void resolveConflict( KCal::Incidence*, const TQString& subresource, TQ_UINT32 sernum ); + void addIncidence( const char* mimetype, const TQString& xml, + const TQString& subResource, TQ_UINT32 sernum ); + + + /** + Caller guarantees i is not null. + */ + bool addIncidence( KCal::Incidence *i, const TQString& subresource, + TQ_UINT32 sernum ); + + void addEvent( const TQString& xml, const TQString& subresource, + TQ_UINT32 sernum ); + void addTodo( const TQString& xml, const TQString& subresource, + TQ_UINT32 sernum ); + void addJournal( const TQString& xml, const TQString& subresource, + TQ_UINT32 sernum ); + + + bool loadAllEvents(); + bool loadAllTodos(); + bool loadAllJournals(); + + bool doLoadAll( Kolab::ResourceMap& map, const char* mimetype ); + + /// Reimplemented from IncidenceBase::Observer to know when an incidence was changed + void incidenceUpdated( KCal::IncidenceBase* ); + void incidenceUpdatedSilent( KCal::IncidenceBase* incidencebase); + + bool openResource( TDEConfig& config, const char* contentType, + Kolab::ResourceMap& map ); + void loadSubResourceConfig( TDEConfig& config, const TQString& name, + const TQString& label, bool writable, + bool alarmRelevant, Kolab::ResourceMap& subResource ); + bool loadSubResource( const TQString& subResource, const char* mimetype ); + bool unloadSubResource( const TQString& subResource ); + + TQString configFile() const { + return ResourceKolabBase::configFile( "kcal" ); + } + + Kolab::ResourceMap* subResourceMap( const TQString& contentsType ); + + bool sendKMailUpdate( KCal::IncidenceBase* incidence, const TQString& _subresource, + TQ_UINT32 sernum ); + + + KCal::CalendarLocal mCalendar; + + // The list of subresources + Kolab::ResourceMap mEventSubResources, mTodoSubResources, mJournalSubResources; + + bool mOpen; // If the resource is open, this is true + TQDict<KCal::IncidenceBase> mPendingUpdates; + TQTimer mResourceChangedTimer; + ICalFormat mFormat; + + /** + This map contains the association between a new added incidence + and the subresource it belongs to. + That's needed to return the correct mapping in subresourceIdentifier(). + + We can't trust on mUidMap here, because it contains only non-pending uids. + */ + TQMap<TQString, TQString> mNewIncidencesMap; + int mProgressDialogIncidenceLimit; + + /** + * If a user has a subresource for viewing another user's folder then it can happen + * that addIncidence(...) adds an incidence with an already existing UID. + * + * When this happens, addIncidence(...) sets a new random UID and stores the + * original UID using incidence->setSchedulingID(uid) because KCal doesn't + * allow two incidences to have the same UID. + * + * This map keeps track of the generated UIDs (which are local) so we can delete the + * right incidence inside fromKMailDelIncidence(...) whenever we sync. + * + * The key is originalUID,subResource and the value is the fake UID. + */ + TQMap< TQPair<TQString, TQString>, TQString > mOriginalUID2fakeUID; + + bool mBatchAddingInProgress; + TQMap<Kolab::ResourceType,TQString> mLastUsedResources; + + /** + Indexed by uid, it holds the last known revision of an incidence. + If we receive an update where the incidence still has the same + revision as the last known, we ignore it and don't send it to kmail, + because shortly after, IncidenceChanger will increment the revision + and that will trigger another update. + + If we didn't discard the first update, kmail would have been updated twice. + */ + TQMap<TQString,int> mLastKnownRevisions; + +}; + +struct TemporarySilencer { + TemporarySilencer( ResourceKolab *_resource ) + { + resource = _resource; + oldValue = resource->mSilent; + resource->mSilent = true; + } + ~TemporarySilencer() + { + resource->mSilent = oldValue; + } + ResourceKolab *resource; + bool oldValue; +}; + +} + +#endif // KCAL_RESOURCEKOLAB_H diff --git a/tderesources/kolab/kcal/resourcekolab_plugin.cpp b/tderesources/kolab/kcal/resourcekolab_plugin.cpp new file mode 100644 index 000000000..eb56897ae --- /dev/null +++ b/tderesources/kolab/kcal/resourcekolab_plugin.cpp @@ -0,0 +1,49 @@ +/* + This file is part of libkabc and/or kaddressbook. + Copyright (c) 2002 - 2004 Klarlvdalens Datakonsult AB + <info@klaralvdalens-datakonsult.se> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. + + In addition, as a special exception, the copyright holders give + permission to link the code of this program with any edition of + the TQt library by Trolltech AS, Norway (or with modified versions + of TQt that use the same license as TQt), and distribute linked + combinations including the two. You must obey the GNU General + Public License in all respects for all of the code used other than + TQt. If you modify this file, you may extend this exception to + your version of the file, but you are not obligated to do so. If + you do not wish to do so, delete this exception statement from + your version. +*/ + +#include "resourcekolab.h" + +class KolabFactory : public KRES::PluginFactoryBase +{ +public: + KRES::Resource *resource( const TDEConfig *config ) + { + return new KCal::ResourceKolab( config ); + } + + KRES::ConfigWidget *configWidget( TQWidget* ) + { + return 0; + } +}; + +K_EXPORT_COMPONENT_FACTORY(kcal_kolab,KolabFactory) diff --git a/tderesources/kolab/kcal/task.cpp b/tderesources/kolab/kcal/task.cpp new file mode 100644 index 000000000..fe75845db --- /dev/null +++ b/tderesources/kolab/kcal/task.cpp @@ -0,0 +1,461 @@ +/* + This file is part of the kolab resource - the implementation of the + Kolab storage format. See www.kolab.org for documentation on this. + + Copyright (c) 2004 Bo Thorsen <bo@sonofthor.dk> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. + + In addition, as a special exception, the copyright holders give + permission to link the code of this program with any edition of + the TQt library by Trolltech AS, Norway (or with modified versions + of TQt that use the same license as TQt), and distribute linked + combinations including the two. You must obey the GNU General + Public License in all respects for all of the code used other than + TQt. If you modify this file, you may extend this exception to + your version of the file, but you are not obligated to do so. If + you do not wish to do so, delete this exception statement from + your version. +*/ + +#include "task.h" + +#include <libkcal/todo.h> +#include <kdebug.h> + +using namespace Kolab; + +// Kolab Storage Specification: +// "The priority can be a number between 1 and 5, with 1 being the highest priority." +// iCalendar (RFC 2445): +// "The priority is specified as an integer in the range +// zero to nine. A value of zero specifies an +// undefined priority. A value of one is the +// highest priority. A value of nine is the lowest +// priority." + +static int kcalPriorityToKolab( const int kcalPriority ) +{ + if ( kcalPriority >= 0 && kcalPriority <= 9 ) { + // We'll map undefined (0) to 3 (default) + // 0 1 2 3 4 5 6 7 8 9 + static const int priorityMap[10] = { 3, 1, 1, 2, 2, 3, 3, 4, 4, 5 }; + return priorityMap[kcalPriority]; + } + else { + kdWarning() << "kcalPriorityToKolab(): Got invalid priority " << kcalPriority << endl; + return 3; + } +} + +static int kolabPrioritytoKCal( const int kolabPriority ) +{ + if ( kolabPriority >= 1 && kolabPriority <= 5 ) { + // 1 2 3 4 5 + static const int priorityMap[5] = { 1, 3, 5, 7, 9 }; + return priorityMap[kolabPriority - 1]; + } + else { + kdWarning() << "kolabPrioritytoKCal(): Got invalid priority " << kolabPriority << endl; + return 5; + } +} + +KCal::Todo* Task::xmlToTask( const TQString& xml, const TQString& tz, KCal::ResourceKolab *res, + const TQString& subResource, TQ_UINT32 sernum ) +{ + Task task( res, subResource, sernum, tz ); + task.load( xml ); + KCal::Todo* todo = new KCal::Todo(); + task.saveTo( todo ); + return todo; +} + +TQString Task::taskToXML( KCal::Todo* todo, const TQString& tz ) +{ + Task task( 0, TQString(), 0, tz, todo ); + return task.saveXML(); +} + +Task::Task( KCal::ResourceKolab *res, const TQString &subResource, TQ_UINT32 sernum, + const TQString& tz, KCal::Todo* task ) + : Incidence( res, subResource, sernum, tz ), + mPriority( 5 ), mPercentCompleted( 0 ), + mStatus( KCal::Incidence::StatusNone ), + mHasStartDate( false ), mHasDueDate( false ), + mHasCompletedDate( false ) +{ + if ( task ) + setFields( task ); +} + +Task::~Task() +{ +} + +void Task::setPriority( int priority ) +{ + mPriority = priority; +} + +int Task::priority() const +{ + return mPriority; +} + +void Task::setPercentCompleted( int percent ) +{ + mPercentCompleted = percent; +} + +int Task::percentCompleted() const +{ + return mPercentCompleted; +} + +void Task::setStatus( KCal::Incidence::Status status ) +{ + mStatus = status; +} + +KCal::Incidence::Status Task::status() const +{ + return mStatus; +} + +void Task::setParent( const TQString& parentUid ) +{ + mParent = parentUid; +} + +TQString Task::parent() const +{ + return mParent; +} + +void Task::setDueDate( const TQDateTime& date ) +{ + mDueDate = date; + mHasDueDate = true; + mFloatingStatus = HasTime; +} + +void Task::setDueDate( const TQDate &date ) +{ + mDueDate = date; + mHasDueDate = true; + mFloatingStatus = AllDay; +} + + +void Task::setDueDate( const TQString &date ) +{ + if ( date.length() > 10 ) { + // This is a date + time + setDueDate( stringToDateTime( date ) ); + } else { + // This is only a date + setDueDate( stringToDate( date ) ); + } +} + +TQDateTime Task::dueDate() const +{ + return mDueDate; +} + +void Task::setHasStartDate( bool v ) +{ + mHasStartDate = v; +} + +bool Task::hasStartDate() const +{ + return mHasStartDate; +} + +bool Task::hasDueDate() const +{ + return mHasDueDate; +} + +void Task::setCompletedDate( const TQDateTime& date ) +{ + mCompletedDate = date; + mHasCompletedDate = true; +} + +TQDateTime Task::completedDate() const +{ + return mCompletedDate; +} + +bool Task::hasCompletedDate() const +{ + return mHasCompletedDate; +} + +bool Task::loadAttribute( TQDomElement& element ) +{ + TQString tagName = element.tagName(); + + if ( tagName == "priority" ) { + bool ok; + mKolabPriorityFromDom = element.text().toInt( &ok ); + if ( !ok || mKolabPriorityFromDom < 1 || mKolabPriorityFromDom > 5 ) { + kdWarning() << "loadAttribute(): Invalid \"priority\" value: " << element.text() << endl; + mKolabPriorityFromDom = -1; + } + } else if ( tagName == "x-kcal-priority" ) { + bool ok; + mKCalPriorityFromDom = element.text().toInt( &ok ); + if ( !ok || mKCalPriorityFromDom < 0 || mKCalPriorityFromDom > 9 ) { + kdWarning() << "loadAttribute(): Invalid \"x-kcal-priority\" value: " << element.text() << endl; + mKCalPriorityFromDom = -1; + } + } else if ( tagName == "completed" ) { + bool ok; + int percent = element.text().toInt( &ok ); + if ( !ok || percent < 0 || percent > 100 ) + percent = 0; + setPercentCompleted( percent ); + } else if ( tagName == "status" ) { + if ( element.text() == "in-progress" ) + setStatus( KCal::Incidence::StatusInProcess ); + else if ( element.text() == "completed" ) + setStatus( KCal::Incidence::StatusCompleted ); + else if ( element.text() == "waiting-on-someone-else" ) + setStatus( KCal::Incidence::StatusNeedsAction ); + else if ( element.text() == "deferred" ) + // Guessing a status here + setStatus( KCal::Incidence::StatusCanceled ); + else + // Default + setStatus( KCal::Incidence::StatusNone ); + } else if ( tagName == "due-date" ) { + setDueDate( element.text() ); + } else if ( tagName == "parent" ) { + setParent( element.text() ); + } else if ( tagName == "x-completed-date" ) { + setCompletedDate( stringToDateTime( element.text() ) ); + } else if ( tagName == "start-date" ) { + setHasStartDate( true ); + setStartDate( element.text() ); + } else + return Incidence::loadAttribute( element ); + + // We handled this + return true; +} + +bool Task::saveAttributes( TQDomElement& element ) const +{ + // Save the base class elements + Incidence::saveAttributes( element ); + + // We need to save x-kcal-priority as well, since the Kolab priority can only save values from + // 1 to 5, but we have values from 0 to 9, and do not want to loose them + writeString( element, "priority", TQString::number( kcalPriorityToKolab( priority() ) ) ); + writeString( element, "x-kcal-priority", TQString::number( priority() ) ); + + writeString( element, "completed", TQString::number( percentCompleted() ) ); + + switch( status() ) { + case KCal::Incidence::StatusInProcess: + writeString( element, "status", "in-progress" ); + break; + case KCal::Incidence::StatusCompleted: + writeString( element, "status", "completed" ); + break; + case KCal::Incidence::StatusNeedsAction: + writeString( element, "status", "waiting-on-someone-else" ); + break; + case KCal::Incidence::StatusCanceled: + writeString( element, "status", "deferred" ); + break; + case KCal::Incidence::StatusNone: + writeString( element, "status", "not-started" ); + break; + case KCal::Incidence::StatusTentative: + case KCal::Incidence::StatusConfirmed: + case KCal::Incidence::StatusDraft: + case KCal::Incidence::StatusFinal: + case KCal::Incidence::StatusX: + // All of these are saved as StatusNone. + writeString( element, "status", "not-started" ); + break; + } + + if ( hasDueDate() ) { + if ( mFloatingStatus == HasTime ) { + writeString( element, "due-date", dateTimeToString( dueDate() ) ); + } else { + writeString( element, "due-date", dateToString( dueDate().date() ) ); + } + } + + if ( !parent().isNull() ) { + writeString( element, "parent", parent() ); + } + + if ( hasCompletedDate() && percentCompleted() == 100 ) { + writeString( element, "x-completed-date", dateTimeToString( completedDate() ) ); + } + + return true; +} + + +bool Task::loadXML( const TQDomDocument& document ) +{ + mKolabPriorityFromDom = -1; + mKCalPriorityFromDom = -1; + + TQDomElement top = document.documentElement(); + + if ( top.tagName() != "task" ) { + tqWarning( "XML error: Top tag was %s instead of the expected task", + top.tagName().ascii() ); + return false; + } + setHasStartDate( false ); // todo's don't necessarily have one + + for ( TQDomNode n = top.firstChild(); !n.isNull(); n = n.nextSibling() ) { + if ( n.isComment() ) + continue; + if ( n.isElement() ) { + TQDomElement e = n.toElement(); + if ( !loadAttribute( e ) ) + // TODO: Unhandled tag - save for later storage + kdDebug() << "Warning: Unhandled tag " << e.tagName() << endl; + } else + kdDebug() << "Node is not a comment or an element???" << endl; + } + + loadAttachments(); + decideAndSetPriority(); + return true; +} + +TQString Task::saveXML() const +{ + TQDomDocument document = domTree(); + TQDomElement element = document.createElement( "task" ); + element.setAttribute( "version", "1.0" ); + saveAttributes( element ); + if ( !hasStartDate() && startDate().isValid() ) { + // events and journals always have a start date, but tasks don't. + // Remove the entry done by the inherited save above, because we + // don't have one. + TQDomNodeList l = element.elementsByTagName( "start-date" ); + Q_ASSERT( l.count() == 1 ); + element.removeChild( l.item( 0 ) ); + } + document.appendChild( element ); + return document.toString(); +} + +void Task::setFields( const KCal::Todo* task ) +{ + Incidence::setFields( task ); + + setPriority( task->priority() ); + setPercentCompleted( task->percentComplete() ); + setStatus( task->status() ); + setHasStartDate( task->hasStartDate() ); + + if ( task->hasDueDate() ) { + setDueDate( localToUTC( task->dtDue() ) ); + if ( task->doesFloat() ) { + // This is a floating task. Don't timezone move this one + mFloatingStatus = AllDay; + setDueDate( task->dtDue().date() ); + } else { + mFloatingStatus = HasTime; + setDueDate( localToUTC( task->dtDue() ) ); + } + } else { + mHasDueDate = false; + } + + if ( task->relatedTo() ) { + setParent( task->relatedTo()->uid() ); + } else if ( !task->relatedToUid().isEmpty() ) { + setParent( task->relatedToUid( ) ); + } else { + setParent( TQString() ); + } + + if ( task->hasCompletedDate() && task->percentComplete() == 100 ) { + setCompletedDate( localToUTC( task->completed() ) ); + } else { + mHasCompletedDate = false; + } +} + +void Task::decideAndSetPriority() +{ + // If we have both Kolab and KCal values in the XML, we prefer the KCal value, but only if the + // values are still in sync + if ( mKolabPriorityFromDom != -1 && mKCalPriorityFromDom != -1 ) { + const bool inSync = ( kcalPriorityToKolab( mKCalPriorityFromDom ) == mKolabPriorityFromDom ); + if ( inSync ) { + setPriority( mKCalPriorityFromDom ); + } + else { + // Out of sync, some other client changed the Kolab priority, so we have to ignore our + // KCal priority + setPriority( kolabPrioritytoKCal( mKolabPriorityFromDom ) ); + } + } + + // Only KCal priority set, use that. + else if ( mKolabPriorityFromDom == -1 && mKCalPriorityFromDom != -1 ) { + kdWarning() << "decideAndSetPriority(): No Kolab priority found, only the KCal priority!" << endl; + setPriority( mKCalPriorityFromDom ); + } + + // Only Kolab priority set, use that + else if ( mKolabPriorityFromDom != -1 && mKCalPriorityFromDom == -1 ) { + setPriority( kolabPrioritytoKCal( mKolabPriorityFromDom ) ); + } + + // No priority set, use the default + else { + // According the RFC 2445, we should use 0 here, for undefined priority, but AFAIK KOrganizer + // doesn't support that, so we'll use 5. + setPriority( 5 ); + } +} + +void Task::saveTo( KCal::Todo* task ) +{ + Incidence::saveTo( task ); + + task->setPriority( priority() ); + task->setPercentComplete( percentCompleted() ); + task->setStatus( status() ); + task->setHasStartDate( hasStartDate() ); + task->setHasDueDate( hasDueDate() ); + if ( hasDueDate() ) + task->setDtDue( utcToLocal( dueDate() ) ); + + if ( !parent().isNull() ) + task->setRelatedToUid( parent() ); + + if ( hasCompletedDate() && task->percentComplete() == 100 ) + task->setCompleted( utcToLocal( mCompletedDate ) ); +} diff --git a/tderesources/kolab/kcal/task.h b/tderesources/kolab/kcal/task.h new file mode 100644 index 000000000..38a12a70e --- /dev/null +++ b/tderesources/kolab/kcal/task.h @@ -0,0 +1,143 @@ +/* + This file is part of the kolab resource - the implementation of the + Kolab storage format. See www.kolab.org for documentation on this. + + Copyright (c) 2004 Bo Thorsen <bo@sonofthor.dk> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. + + In addition, as a special exception, the copyright holders give + permission to link the code of this program with any edition of + the TQt library by Trolltech AS, Norway (or with modified versions + of TQt that use the same license as TQt), and distribute linked + combinations including the two. You must obey the GNU General + Public License in all respects for all of the code used other than + TQt. If you modify this file, you may extend this exception to + your version of the file, but you are not obligated to do so. If + you do not wish to do so, delete this exception statement from + your version. +*/ + +#ifndef KOLAB_TASK_H +#define KOLAB_TASK_H + +#include <incidence.h> + +#include <libkcal/incidence.h> + +class TQDomElement; + +namespace KCal { + class Todo; + class ResourceKolab; +} + +namespace Kolab { + +/** + * This class represents a task, and knows how to load/save it + * from/to XML, and from/to a KCal::Todo. + * The instances of this class are temporary, only used to convert + * one to the other. + */ +class Task : public Incidence { +public: + /// Use this to parse an xml string to a task entry + /// The caller is responsible for deleting the returned task + static KCal::Todo* xmlToTask( const TQString& xml, const TQString& tz, KCal::ResourceKolab *res = 0, + const TQString& subResource = TQString(), TQ_UINT32 sernum = 0 ); + + /// Use this to get an xml string describing this task entry + static TQString taskToXML( KCal::Todo*, const TQString& tz ); + + explicit Task( KCal::ResourceKolab *res, const TQString& subResource, TQ_UINT32 sernum, + const TQString& tz, KCal::Todo* todo = 0 ); + virtual ~Task(); + + virtual TQString type() const { return "Task"; } + + void saveTo( KCal::Todo* todo ); + + virtual void setPriority( int priority ); + virtual int priority() const; + + virtual void setPercentCompleted( int percent ); + virtual int percentCompleted() const; + + virtual void setStatus( KCal::Incidence::Status status ); + virtual KCal::Incidence::Status status() const; + + virtual void setParent( const TQString& parentUid ); + virtual TQString parent() const; + + virtual void setHasStartDate( bool ); + virtual bool hasStartDate() const; + + virtual void setDueDate( const TQDateTime &date ); + virtual void setDueDate( const TQString &date ); + virtual void setDueDate( const TQDate &date ); + virtual TQDateTime dueDate() const; + virtual bool hasDueDate() const; + + virtual void setCompletedDate( const TQDateTime& date ); + virtual TQDateTime completedDate() const; + virtual bool hasCompletedDate() const; + + // Load the attributes of this class + virtual bool loadAttribute( TQDomElement& ); + + // Save the attributes of this class + virtual bool saveAttributes( TQDomElement& ) const; + + // Load this task by reading the XML file + virtual bool loadXML( const TQDomDocument& xml ); + + // Serialize this task to an XML string + virtual TQString saveXML() const; + +protected: + // Read all known fields from this ical todo + void setFields( const KCal::Todo* ); + + // This sets the priority of this task by looking at mKolabPriorityFromDom and + // mKCalPriorityFromDom. + void decideAndSetPriority(); + + // This is the KCal priority, not the Kolab priority. + // See kcalPriorityToKolab() and kolabPrioritytoKCal(). + int mPriority; + + // Those priority values are the raw values read by loadAttribute(). + // They will be converted later in decideAndSetPriority(). + int mKolabPriorityFromDom; + int mKCalPriorityFromDom; + + int mPercentCompleted; + KCal::Incidence::Status mStatus; + TQString mParent; + + bool mHasStartDate; + + bool mHasDueDate; + TQDateTime mDueDate; + + bool mHasCompletedDate; + TQDateTime mCompletedDate; +}; + +} + +#endif // KOLAB_TASK_H diff --git a/tderesources/kolab/knotes/CMakeLists.txt b/tderesources/kolab/knotes/CMakeLists.txt new file mode 100644 index 000000000..8e10f3e52 --- /dev/null +++ b/tderesources/kolab/knotes/CMakeLists.txt @@ -0,0 +1,55 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +include_directories( + ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_CURRENT_SOURCE_DIR}/../shared + ${CMAKE_SOURCE_DIR} + ${CMAKE_SOURCE_DIR}/knotes + ${CMAKE_SOURCE_DIR}/libtdepim + ${TDE_INCLUDE_DIR} + ${TQT_INCLUDE_DIRS} +) + +link_directories( + ${TQT_LIBRARY_DIRS} +) + + +##### other data ################################ + +install( + FILES kolabresource.desktop + DESTINATION ${SERVICES_INSTALL_DIR}/tderesources/knotes ) + +install( + FILES ${CMAKE_CURRENT_SOURCE_DIR}/../uninstall.desktop + DESTINATION ${SERVICES_INSTALL_DIR}/tderesources/knotes + RENAME imap.desktop ) + + +##### knotes_kolab (module) ##################### + +tde_add_kpart( knotes_kolab AUTOMOC + SOURCES resourcekolab_plugin.cpp + LINK knoteskolab-shared + DESTINATION ${PLUGIN_INSTALL_DIR} +) + + +##### knoteskolab (shared) ###################### + +tde_add_library( knoteskolab SHARED AUTOMOC + SOURCES resourcekolab.cpp note.cpp + VERSION 0.0.0 + LINK resourcekolabshared-static kgroupwarebase-shared knotes-shared + DESTINATION ${LIB_INSTALL_DIR} +) diff --git a/tderesources/kolab/knotes/Makefile.am b/tderesources/kolab/knotes/Makefile.am new file mode 100644 index 000000000..54337ce89 --- /dev/null +++ b/tderesources/kolab/knotes/Makefile.am @@ -0,0 +1,29 @@ +METASOURCES = AUTO + +INCLUDES = -I$(top_srcdir)/tderesources/kolab/shared \ + -I$(top_srcdir) -I$(top_srcdir)/knotes -I$(top_builddir)/libtdepim $(all_includes) + +# The kolab wizard links to this library too +lib_LTLIBRARIES = libknoteskolab.la + +libknoteskolab_la_SOURCES = resourcekolab.cpp note.cpp +libknoteskolab_la_LDFLAGS = $(all_libraries) -no-undefined +libknoteskolab_la_LIBADD = \ + $(top_builddir)/tderesources/kolab/shared/libresourcekolabshared.la \ + $(top_builddir)/knotes/libknotesresources.la \ + $(top_builddir)/libkcal/libkcal.la \ + -ltderesources -ltdeprint + +kde_module_LTLIBRARIES = knotes_kolab.la + +knotes_kolab_la_SOURCES = resourcekolab_plugin.cpp +knotes_kolab_la_LDFLAGS = $(all_libraries) -module $(KDE_PLUGIN) -no-undefined +knotes_kolab_la_LIBADD = libknoteskolab.la + +servicedir = $(kde_servicesdir)/tderesources/knotes +service_DATA = kolabresource.desktop + +install-data-local: $(srcdir)/../uninstall.desktop + $(mkinstalldirs) $(DESTDIR)$(servicedir) + $(INSTALL_DATA) $(srcdir)/../uninstall.desktop $(DESTDIR)$(servicedir)/imap.desktop + diff --git a/tderesources/kolab/knotes/kolabresource.desktop b/tderesources/kolab/knotes/kolabresource.desktop new file mode 100644 index 000000000..16f48e45a --- /dev/null +++ b/tderesources/kolab/knotes/kolabresource.desktop @@ -0,0 +1,52 @@ +[Desktop Entry] +Name=IMAP Server via KMail +Name[af]=IMAP bediener via KMail +Name[bg]=Сървър IMAP през KMail +Name[br]=Servijer IMAP gant KMail +Name[ca]=Servidor IMAP mitjançant KMail +Name[cs]=IMAP server přes KMail +Name[da]=IMAP-server via KMail +Name[de]=IMAP-Server via KMail +Name[el]=Εξυπηρετητής IMAP μέσω του KMail +Name[es]=Servidor IMAP por medio de KMail +Name[et]=IMAP-server (KMaili vahendusel) +Name[eu]=IMAP zerbitzaria KMail-en bidez +Name[fa]=کارساز IMAP از طریق KMail +Name[fi]=IMAP-palvelin KMailin avulla +Name[fr]=Serveur IMAP (via KMail) +Name[fy]=IMAP-tsjinner fia KMail +Name[ga]=Freastalaí IMAP via KMail +Name[gl]=Servidor MAP mediante KMail +Name[hu]=IMAP-kiszolgáló a KMailen keresztül +Name[is]=IMAP þjónn gegnum KMail +Name[it]=Server IMAP via KMail +Name[ja]=KMail 経由 IMAP サーバ +Name[kk]=KMail арқылы IMAP сервері +Name[km]=ម៉ាស៊ីនបម្រើ IMAP តាមរយៈ KMail +Name[lt]=IMAP serveris per KMail +Name[mk]=IMAP-сервер преку КПошта +Name[ms]=Pelayan IMAP melalui KMail +Name[nb]=IMAP-tjener via KMail +Name[nds]=IMAP-Server över KMail +Name[ne]=केडीई मेल मार्फत IMAP सर्भर +Name[nl]=IMAP-server via KMail +Name[nn]=IMAP-tenar via KMail +Name[pl]=Serwer IMAP za pośrednictwem KMail +Name[pt]=Servidor IMAP via KMail +Name[pt_BR]=Servidor IMAP via KMail +Name[ru]=Доступ к серверу IMAP через KMail +Name[sk]=IMAP-server pomocou KMail +Name[sl]=Strežnik IMAP preko KMaila +Name[sr]=IMAP сервер преко KMail-а +Name[sr@Latn]=IMAP server preko KMail-a +Name[sv]=IMAP-server via Kmail +Name[ta]=IMAP சேவகன் மூலாம் கேஅஞ்சல் +Name[tr]=KMail aracılığı ile IMAP Sunucu +Name[uk]=Сервер IMAP через KMail +Name[zh_CN]=通过 KMail 访问 IMAP 服务器 +Name[zh_TW]=透過 KMail 取得 IMAP 伺服器 +X-TDE-Library=knotes_kolab +Type=Service +ServiceTypes=KResources/Plugin +X-TDE-ResourceFamily=notes +X-TDE-ResourceType=imap diff --git a/tderesources/kolab/knotes/note.cpp b/tderesources/kolab/knotes/note.cpp new file mode 100644 index 000000000..7df5b26b3 --- /dev/null +++ b/tderesources/kolab/knotes/note.cpp @@ -0,0 +1,229 @@ +/* + This file is part of the kolab resource - the implementation of the + Kolab storage format. See www.kolab.org for documentation on this. + + Copyright (c) 2004 Bo Thorsen <bo@sonofthor.dk> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. + + In addition, as a special exception, the copyright holders give + permission to link the code of this program with any edition of + the TQt library by Trolltech AS, Norway (or with modified versions + of TQt that use the same license as TQt), and distribute linked + combinations including the two. You must obey the GNU General + Public License in all respects for all of the code used other than + TQt. If you modify this file, you may extend this exception to + your version of the file, but you are not obligated to do so. If + you do not wish to do so, delete this exception statement from + your version. +*/ + +#include "note.h" + +#include <libkcal/journal.h> +#include <knotes/version.h> +#include <kdebug.h> + +using namespace Kolab; + + +KCal::Journal* Note::xmlToJournal( const TQString& xml ) +{ + Note note; + note.load( xml ); + KCal::Journal* journal = new KCal::Journal(); + note.saveTo( journal ); + return journal; +} + +TQString Note::journalToXML( KCal::Journal* journal ) +{ + Note note( journal ); + return note.saveXML(); +} + +Note::Note( KCal::Journal* journal ) : mRichText( false ) +{ + if ( journal ) + setFields( journal ); +} + +Note::~Note() +{ +} + +void Note::setSummary( const TQString& summary ) +{ + mSummary = summary; +} + +TQString Note::summary() const +{ + return mSummary; +} + +void Note::setBackgroundColor( const TQColor& bgColor ) +{ + mBackgroundColor = bgColor; +} + +TQColor Note::backgroundColor() const +{ + return mBackgroundColor; +} + +void Note::setForegroundColor( const TQColor& fgColor ) +{ + mForegroundColor = fgColor; +} + +TQColor Note::foregroundColor() const +{ + return mForegroundColor; +} + +void Note::setRichText( bool richText ) +{ + mRichText = richText; +} + +bool Note::richText() const +{ + return mRichText; +} + +bool Note::loadAttribute( TQDomElement& element ) +{ + TQString 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( TQDomElement& element ) const +{ + // Save the base class elements + KolabBase::saveAttributes( element ); + + // Save the elements +#if 0 + TQDomComment 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 TQDomDocument& document ) +{ + TQDomElement top = document.documentElement(); + + if ( top.tagName() != "note" ) { + tqWarning( "XML error: Top tag was %s instead of the expected note", + top.tagName().ascii() ); + return false; + } + + for ( TQDomNode n = top.firstChild(); !n.isNull(); n = n.nextSibling() ) { + if ( n.isComment() ) + continue; + if ( n.isElement() ) { + TQDomElement e = n.toElement(); + if ( !loadAttribute( e ) ) + // TODO: Unhandled tag - save for later storage + kdDebug() << "Warning: Unhandled tag " << e.tagName() << endl; + } else + kdDebug() << "Node is not a comment or an element???" << endl; + } + + return true; +} + +TQString Note::saveXML() const +{ + TQDomDocument document = domTree(); + TQDomElement element = document.createElement( "note" ); + element.setAttribute( "version", "1.0" ); + saveAttributes( element ); + document.appendChild( element ); + return document.toString(); +} + +void Note::setFields( const KCal::Journal* journal ) +{ + KolabBase::setFields( journal ); + + setSummary( journal->summary() ); + + TQString property = journal->customProperty( "KNotes", "BgColor" ); + if ( !property.isNull() ) { + setBackgroundColor( property ); + } else { + setBackgroundColor( "yellow" ); + } + property = journal->customProperty( "KNotes", "FgColor" ); + if ( !property.isNull() ) { + setForegroundColor( property ); + } else { + setForegroundColor( "black" ); + } + + property = journal->customProperty( "KNotes", "RichText" ); + if ( !property.isNull() ) { + setRichText( property == "true" ? true : false ); + } else { + setRichText( "false" ); + } +} + +void Note::saveTo( KCal::Journal* 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" ); +} + +TQString Note::productID() const +{ + return TQString( "KNotes %1, Kolab resource" ).arg( KNOTES_VERSION ); +} diff --git a/tderesources/kolab/knotes/note.h b/tderesources/kolab/knotes/note.h new file mode 100644 index 000000000..d61a2009e --- /dev/null +++ b/tderesources/kolab/knotes/note.h @@ -0,0 +1,111 @@ +/* + This file is part of the kolab resource - the implementation of the + Kolab storage format. See www.kolab.org for documentation on this. + + Copyright (c) 2004 Bo Thorsen <bo@sonofthor.dk> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. + + In addition, as a special exception, the copyright holders give + permission to link the code of this program with any edition of + the TQt library by Trolltech AS, Norway (or with modified versions + of TQt that use the same license as TQt), and distribute linked + combinations including the two. You must obey the GNU General + Public License in all respects for all of the code used other than + TQt. If you modify this file, you may extend this exception to + your version of the file, but you are not obligated to do so. If + you do not wish to do so, delete this exception statement from + your version. +*/ + +#ifndef KOLAB_NOTE_H +#define KOLAB_NOTE_H + +#include <kolabbase.h> + +class TQDomElement; + +namespace KCal { + class Journal; +} + +namespace Kolab { + +/** + * This class represents a note, and knows how to load/save it + * from/to XML, and from/to a KCal::Journal. + * The instances of this class are temporary, only used to convert + * one to the other. + */ +class 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 KCal::Journal* xmlToJournal( const TQString& xml ); + + /// Use this to get an xml string describing this journal entry + static TQString journalToXML( KCal::Journal* ); + + /// Create a note object and + explicit Note( KCal::Journal* journal = 0 ); + virtual ~Note(); + + void saveTo( KCal::Journal* journal ); + + virtual TQString type() const { return "Note"; } + + virtual void setSummary( const TQString& summary ); + virtual TQString summary() const; + + virtual void setBackgroundColor( const TQColor& bgColor ); + virtual TQColor backgroundColor() const; + + virtual void setForegroundColor( const TQColor& fgColor ); + virtual TQColor foregroundColor() const; + + virtual void setRichText( bool richText ); + virtual bool richText() const; + + // Load the attributes of this class + virtual bool loadAttribute( TQDomElement& ); + + // Save the attributes of this class + virtual bool saveAttributes( TQDomElement& ) const; + + // Load this note by reading the XML file + virtual bool loadXML( const TQDomDocument& xml ); + + // Serialize this note to an XML string + virtual TQString saveXML() const; + +protected: + // Read all known fields from this ical incidence + void setFields( const KCal::Journal* ); + + // Save all known fields into this ical incidence + void saveTo( KCal::Incidence* ) const; + + TQString productID() const; + + TQString mSummary; + TQColor mBackgroundColor; + TQColor mForegroundColor; + bool mRichText; +}; + +} + +#endif // KOLAB_NOTE_H diff --git a/tderesources/kolab/knotes/resourcekolab.cpp b/tderesources/kolab/knotes/resourcekolab.cpp new file mode 100644 index 000000000..a28a51b27 --- /dev/null +++ b/tderesources/kolab/knotes/resourcekolab.cpp @@ -0,0 +1,463 @@ +/* + This file is part of the kolab resource - the implementation of the + Kolab storage format. See www.kolab.org for documentation on this. + + Copyright (c) 2004 Bo Thorsen <bo@sonofthor.dk> + Copyright (c) 2004 Till Adam <adam@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. + + In addition, as a special exception, the copyright holders give + permission to link the code of this program with any edition of + the TQt library by Trolltech AS, Norway (or with modified versions + of TQt that use the same license as TQt), and distribute linked + combinations including the two. You must obey the GNU General + Public License in all respects for all of the code used other than + TQt. If you modify this file, you may extend this exception to + your version of the file, but you are not obligated to do so. If + you do not wish to do so, delete this exception statement from + your version. +*/ + +#include "resourcekolab.h" +#include "note.h" + +#include <knotes/resourcemanager.h> + +#include <libkcal/icalformat.h> + +#include <kdebug.h> +#include <kglobal.h> +#include <klocale.h> + +using namespace Kolab; + +static const char* configGroupName = "Note"; +static const char* kmailContentsType = "Note"; +static const char* attachmentMimeType = "application/x-vnd.kolab.note"; +static const char* inlineMimeType = "text/calendar"; + +ResourceKolab::ResourceKolab( const TDEConfig *config ) + : ResourceNotes( config ), ResourceKolabBase( "ResourceKolab-KNotes" ), + mCalendar( TQString::fromLatin1("UTC") ) +{ + if ( !config ) { + setResourceName( i18n( "Kolab Server" ) ); + } + setType( "imap" ); +} + +ResourceKolab::~ResourceKolab() +{ +} + +bool ResourceKolab::doOpen() +{ + TDEConfig config( configFile() ); + config.setGroup( configGroupName ); + + // Get the list of Notes folders from KMail + TQValueList<KMailICalIface::SubResource> subResources; + if ( !kmailSubresources( subResources, kmailContentsType ) ) + return false; + + // Make the resource map from the folder list + TQValueList<KMailICalIface::SubResource>::ConstIterator it; + mSubResources.clear(); + for ( it = subResources.constBegin(); it != subResources.constEnd(); ++it ) { + const TQString subResource = (*it).location; + const bool active = config.readBoolEntry( subResource, true ); + mSubResources[ subResource ] = Kolab::SubResource( active, (*it).writable, (*it).label ); + } + + return true; +} + +void ResourceKolab::doClose() +{ + TDEConfig config( configFile() ); + config.setGroup( configGroupName ); + Kolab::ResourceMap::ConstIterator it; + for ( it = mSubResources.constBegin(); it != mSubResources.constEnd(); ++it ) + config.writeEntry( it.key(), it.data().active() ); +} + +bool ResourceKolab::loadSubResource( const TQString& subResource, + const TQString &mimetype ) +{ + // Get the list of journals + int count = 0; + if ( !kmailIncidencesCount( count, mimetype, subResource ) ) { + kdError() << "Communication problem in ResourceKolab::load()\n"; + return false; + } + + TQMap<TQ_UINT32, TQString> lst; + if( !kmailIncidences( lst, mimetype, subResource, 0, count ) ) { + kdError(5500) << "Communication problem in " + << "ResourceKolab::getIncidenceList()\n"; + return false; + } + + kdDebug(5500) << "Notes kolab resource: got " << lst.count() << " notes in " << subResource << endl; + + // Populate with the new entries + const bool silent = mSilent; + mSilent = true; + TQMap<TQ_UINT32, TQString>::ConstIterator it; + for ( it = lst.constBegin(); it != lst.constEnd(); ++it ) { + KCal::Journal* journal = addNote( it.data(), subResource, it.key(), mimetype ); + if ( !journal ) + kdDebug(5500) << "loading note " << it.key() << " failed" << endl; + else + manager()->registerNote( this, journal ); + } + mSilent = silent; + + return true; +} + +bool ResourceKolab::load() +{ + // We get a fresh list of events, so clean out the old ones + mCalendar.deleteAllEvents(); + mUidMap.clear(); + + bool rc = true; + Kolab::ResourceMap::ConstIterator itR; + for ( itR = mSubResources.constBegin(); itR != mSubResources.constEnd(); ++itR ) { + if ( !itR.data().active() ) + // This subResource is disabled + continue; + + TQString mimetype = inlineMimeType; + rc &= loadSubResource( itR.key(), mimetype ); + mimetype = attachmentMimeType; + rc &= loadSubResource( itR.key(), mimetype ); + } + + return rc; +} + +bool ResourceKolab::save() +{ + // Nothing to do here, we save everything in incidenceUpdated() + return true; +} + +bool ResourceKolab::addNote( KCal::Journal* journal ) +{ + return addNote( journal, TQString(), 0 ); +} + +KCal::Journal* ResourceKolab::addNote( const TQString& data, const TQString& subresource, + TQ_UINT32 sernum, const TQString &mimetype ) +{ + KCal::Journal *journal = 0; + + // FIXME: This does not take into account the time zone! + KCal::ICalFormat formatter; + if ( mimetype == attachmentMimeType ) { + journal = Note::xmlToJournal( data ); + } else { + journal = static_cast<KCal::Journal*>( formatter.fromString( data ) ); + } + Q_ASSERT( journal ); + + bool addedOk = journal && + !mUidMap.contains( journal->uid() ) && + addNote( journal, subresource, sernum ); + + // for debugging + if ( journal && mUidMap.contains( journal->uid() ) ) { + kdDebug(5500) << "mUidMap already contains " << journal->uid() << endl; + } + + if ( !addedOk ) { + delete journal; + journal = 0; + } + + return journal; +} + +bool ResourceKolab::addNote( KCal::Journal *journal, const TQString &subresource, TQ_UINT32 sernum ) +{ + kdDebug(5500) << "ResourceKolab::addNote( KCal::Journal*, '" << subresource << "', " << sernum << " )\n"; + + journal->registerObserver( this ); + + // Find out if this note was previously stored in KMail + bool newNote = subresource.isEmpty(); + if ( !mCalendar.addJournal( journal ) ) { + return false; + } + + TQString resource = newNote ? findWritableResource( Kolab::Notes, mSubResources ) : subresource; + if ( resource.isEmpty() ) { + // canceled + return false; + } + + if ( !mSilent ) { + TQString xml = Note::journalToXML( journal ); + kdDebug(5500) << k_funcinfo << "XML string:\n" << xml << endl; + + if( !kmailUpdate( resource, sernum, xml, attachmentMimeType, journal->uid() ) ) { + kdError(5500) << "Communication problem in ResourceKolab::addNote()\n"; + return false; + } + } + + if ( !resource.isEmpty() && sernum != 0 ) { + mUidMap[ journal->uid() ] = StorageReference( resource, sernum ); + return true; + } + return false; +} + +bool ResourceKolab::deleteNote( KCal::Journal* journal ) +{ + const TQString uid = journal->uid(); + if ( !mUidMap.contains( uid ) ) + // Odd + return false; + + if ( !mSilent ) { + kmailDeleteIncidence( mUidMap[ uid ].resource(), + mUidMap[ uid ].serialNumber() ); + } + mUidMap.remove( uid ); + return mCalendar.deleteJournal( journal ); +} + +KCal::Alarm::List ResourceKolab::alarms( const TQDateTime& from, const TQDateTime& to ) +{ + KCal::Alarm::List alarms; + KCal::Journal::List notes = mCalendar.journals(); + KCal::Journal::List::ConstIterator note; + for ( note = notes.begin(); note != notes.end(); ++note ) + { + TQDateTime preTime = from.addSecs( -1 ); + KCal::Alarm::List::ConstIterator it; + for( it = (*note)->alarms().constBegin(); it != (*note)->alarms().constEnd(); ++it ) + { + if ( (*it)->enabled() ) + { + TQDateTime dt = (*it)->nextRepetition( preTime ); + if ( dt.isValid() && dt <= to ) + alarms.append( *it ); + } + } + } + + return alarms; +} + +void ResourceKolab::incidenceUpdated( KCal::IncidenceBase* i ) +{ + TQString subResource; + TQ_UINT32 sernum; + if ( mUidMap.contains( i->uid() ) ) { + subResource = mUidMap[ i->uid() ].resource(); + sernum = mUidMap[ i->uid() ].serialNumber(); + } else { // can this happen? + subResource = findWritableResource( Kolab::Notes, mSubResources ); + if ( subResource.isEmpty() ) // canceled + return; + sernum = 0; + } + + KCal::Journal* journal = dynamic_cast<KCal::Journal*>( i ); + TQString xml = Note::journalToXML( journal ); + if( !xml.isEmpty() && kmailUpdate( subResource, sernum, xml, attachmentMimeType, journal->uid() ) ) + mUidMap[ i->uid() ] = StorageReference( subResource, sernum ); +} + +/* + * These are the DCOP slots that KMail call to notify when something + * changed. + */ +bool ResourceKolab::fromKMailAddIncidence( const TQString& type, + const TQString& subResource, + TQ_UINT32 sernum, + int format, + const TQString& note ) +{ + // Check if this is a note + if( type != kmailContentsType ) return false; + + const bool silent = mSilent; + mSilent = true; + TQString mimetype; + if ( format == KMailICalIface::StorageXML ) + mimetype = attachmentMimeType; + else + mimetype = inlineMimeType; + KCal::Journal* journal = addNote( note, subResource, sernum, mimetype ); + if ( journal ) + manager()->registerNote( this, journal ); + mSilent = silent; + return true; +} + +void ResourceKolab::fromKMailDelIncidence( const TQString& type, + const TQString& /*subResource*/, + const TQString& uid ) +{ + // Check if this is a note + if( type != kmailContentsType ) return; + + kdDebug(5500) << "ResourceKolab::fromKMailDelIncidence( " << type << ", " << uid + << " )" << endl; + + const bool silent = mSilent; + mSilent = true; + KCal::Journal* j = mCalendar.journal( uid ); + if ( j ) { + if ( deleteNote( j ) ) { + manager()->deleteNote( j ); + } + } + mSilent = silent; +} + +void ResourceKolab::fromKMailRefresh( const TQString& type, + const TQString& /*subResource*/ ) +{ + if ( type == kmailContentsType ) + load(); // ### should call loadSubResource(subResource) probably +} + +void ResourceKolab::fromKMailAddSubresource( const TQString& type, + const TQString& subResource, + const TQString& /*label*/, + bool writable, + bool /*alarmRelevant*/ ) +{ + if ( type != kmailContentsType ) + // Not ours + return; + + if ( mSubResources.contains( subResource ) ) + // Already registered + return; + + TDEConfig config( configFile() ); + config.setGroup( configGroupName ); + + bool active = config.readBoolEntry( subResource, true ); + mSubResources[ subResource ] = Kolab::SubResource( active, writable, subResource ); + loadSubResource( subResource, attachmentMimeType ); + emit signalSubresourceAdded( this, type, subResource ); +} + +void ResourceKolab::fromKMailDelSubresource( const TQString& type, + const TQString& subResource ) +{ + if ( type != configGroupName ) + // Not ours + return; + + if ( !mSubResources.contains( subResource ) ) + // Not registered + return; + + // Ok, it's our job, and we have it here + mSubResources.erase( subResource ); + + TDEConfig config( configFile() ); + config.setGroup( configGroupName ); + config.deleteEntry( subResource ); + config.sync(); + + // Make a list of all uids to remove + Kolab::UidMap::ConstIterator mapIt; + TQStringList uids; + for ( mapIt = mUidMap.constBegin(); mapIt != mUidMap.constEnd(); ++mapIt ) + if ( mapIt.data().resource() == subResource ) + // We have a match + uids << mapIt.key(); + + // Finally delete all the incidences + if ( !uids.isEmpty() ) { + const bool silent = mSilent; + mSilent = true; + TQStringList::ConstIterator it; + for ( it = uids.constBegin(); it != uids.constEnd(); ++it ) { + KCal::Journal* j = mCalendar.journal( *it ); + if( j ) + deleteNote( j ); + } + mSilent = silent; + } + + emit signalSubresourceRemoved( this, type, subResource ); +} + +void ResourceKolab::fromKMailAsyncLoadResult( const TQMap<TQ_UINT32, TQString>& map, + const TQString& type, + const TQString& folder ) +{ + // We are only interested in notes + if ( ( type != attachmentMimeType ) && ( type != inlineMimeType ) ) return; + // Populate with the new entries + const bool silent = mSilent; + mSilent = true; + TQString mimetype; + if ( kmailStorageFormat( folder ) == KMailICalIface::StorageXML ) + mimetype = attachmentMimeType; + else + mimetype = inlineMimeType; + for( TQMap<TQ_UINT32, TQString>::ConstIterator it = map.constBegin(); it != map.constEnd(); ++it ) { + KCal::Journal* journal = addNote( it.data(), folder, it.key(), mimetype ); + if ( !journal ) + kdDebug(5500) << "loading note " << it.key() << " failed" << endl; + else + manager()->registerNote( this, journal ); + } + mSilent = silent; +} + + +TQStringList ResourceKolab::subresources() const +{ + return mSubResources.keys(); +} + +bool ResourceKolab::subresourceActive( const TQString& res ) const +{ + if ( mSubResources.contains( res ) ) { + return mSubResources[ res ].active(); + } + + // Safe default bet: + kdDebug(5650) << "subresourceActive( " << res << " ): Safe bet\n"; + + return true; +} + +bool ResourceKolab::subresourceWritable( const TQString& res ) const +{ + if ( mSubResources.contains( res ) ) { + return mSubResources[ res ].writable(); + } + + // Safe default bet: + return false; +} + +#include "resourcekolab.moc" diff --git a/tderesources/kolab/knotes/resourcekolab.h b/tderesources/kolab/knotes/resourcekolab.h new file mode 100644 index 000000000..fb0d191b9 --- /dev/null +++ b/tderesources/kolab/knotes/resourcekolab.h @@ -0,0 +1,133 @@ +/* + This file is part of the kolab resource - the implementation of the + Kolab storage format. See www.kolab.org for documentation on this. + + Copyright (c) 2004 Bo Thorsen <bo@sonofthor.dk> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. + + In addition, as a special exception, the copyright holders give + permission to link the code of this program with any edition of + the TQt library by Trolltech AS, Norway (or with modified versions + of TQt that use the same license as TQt), and distribute linked + combinations including the two. You must obey the GNU General + Public License in all respects for all of the code used other than + TQt. If you modify this file, you may extend this exception to + your version of the file, but you are not obligated to do so. If + you do not wish to do so, delete this exception statement from + your version. +*/ + +#ifndef KNOTES_RESOURCEKOLAB_H +#define KNOTES_RESOURCEKOLAB_H + +#include <resourcenotes.h> +#include <libkcal/incidencebase.h> +#include <libkcal/calendarlocal.h> +#include "../shared/resourcekolabbase.h" +#include "../shared/subresource.h" +#include <tdepimmacros.h> + + +namespace Kolab { + +/** + * This class implements a KNotes resource that keeps its + * addresses in an IMAP folder in KMail (or other conforming email + * clients). + */ +class KDE_EXPORT ResourceKolab : public ResourceNotes, + public KCal::IncidenceBase::Observer, + public ResourceKolabBase +{ + Q_OBJECT + + +public: + ResourceKolab( const TDEConfig* ); + virtual ~ResourceKolab(); + + /// Load resource data. + bool load(); + + /// Save resource data. + bool save(); + + /// Open the notes resource. + bool doOpen(); + /// Close the notes resource. + void doClose(); + + bool addNote( KCal::Journal* ); + + bool deleteNote( KCal::Journal* ); + + KCal::Alarm::List alarms( const TQDateTime& from, const TQDateTime& to ); + + /// Reimplemented from IncidenceBase::Observer to know when a note was changed + void incidenceUpdated( KCal::IncidenceBase* ); + + /// The ResourceKolabBase methods called by KMail + bool fromKMailAddIncidence( const TQString& type, const TQString& resource, + TQ_UINT32 sernum, int format, const TQString& note ); + void fromKMailDelIncidence( const TQString& type, const TQString& resource, + const TQString& uid ); + void fromKMailRefresh( const TQString& type, const TQString& resource ); + + /// Listen to KMail changes in the amount of sub resources + void fromKMailAddSubresource( const TQString& type, const TQString& resource, + const TQString& label, bool writable, + bool alarmRelevant ); + void fromKMailDelSubresource( const TQString& type, const TQString& resource ); + + void fromKMailAsyncLoadResult( const TQMap<TQ_UINT32, TQString>& map, + const TQString& type, + const TQString& folder ); + + /** Return the list of subresources. */ + TQStringList subresources() const; + + /** Is this subresource active? */ + bool subresourceActive( const TQString& ) const; + + /** Is this subresource writable? */ + bool subresourceWritable( const TQString& ) const; + +signals: + void signalSubresourceAdded( Resource*, const TQString&, const TQString& ); + void signalSubresourceRemoved( Resource*, const TQString&, const TQString& ); + +private: + bool addNote( KCal::Journal* journal, const TQString& resource, + TQ_UINT32 sernum ); + KCal::Journal* addNote( const TQString& data, const TQString& subresource, + TQ_UINT32 sernum, const TQString &mimetype ); + + bool loadSubResource( const TQString& resource, const TQString& mimetype ); + + TQString configFile() const { + return ResourceKolabBase::configFile( "knotes" ); + } + + KCal::CalendarLocal mCalendar; + + // The list of subresources + Kolab::ResourceMap mSubResources; +}; + +} + +#endif // KNOTES_RESOURCEKOLAB_H diff --git a/tderesources/kolab/knotes/resourcekolab_plugin.cpp b/tderesources/kolab/knotes/resourcekolab_plugin.cpp new file mode 100644 index 000000000..f589986cb --- /dev/null +++ b/tderesources/kolab/knotes/resourcekolab_plugin.cpp @@ -0,0 +1,50 @@ +/* + This file is part of libkabc and/or kaddressbook. + Copyright (c) 2002 - 2004 Klarlvdalens Datakonsult AB + <info@klaralvdalens-datakonsult.se> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. + + In addition, as a special exception, the copyright holders give + permission to link the code of this program with any edition of + the TQt library by Trolltech AS, Norway (or with modified versions + of TQt that use the same license as TQt), and distribute linked + combinations including the two. You must obey the GNU General + Public License in all respects for all of the code used other than + TQt. If you modify this file, you may extend this exception to + your version of the file, but you are not obligated to do so. If + you do not wish to do so, delete this exception statement from + your version. +*/ + +#include "resourcekolab.h" + +class KolabFactory : public KRES::PluginFactoryBase +{ +public: + KRES::Resource *resource( const TDEConfig *config ) + { + return new Kolab::ResourceKolab( config ); + } + + KRES::ConfigWidget *configWidget( TQWidget* ) + { + return 0; + } +}; + +K_EXPORT_COMPONENT_FACTORY(knotes_kolab,KolabFactory()) + diff --git a/tderesources/kolab/kolab-resource.upd b/tderesources/kolab/kolab-resource.upd new file mode 100644 index 000000000..8f456f4ab --- /dev/null +++ b/tderesources/kolab/kolab-resource.upd @@ -0,0 +1,12 @@ +# Update the name of the resource +Id=kolab-calendar-resource-rename +File=tderesources/calendar/stdrc +Script=upgrade-resourcetype.pl,perl + +Id=kolab-contact-resource-rename +File=tderesources/contact/stdrc +Script=upgrade-resourcetype.pl,perl + +Id=kolab-notes-resource-rename +File=tderesources/notes/stdrc +Script=upgrade-resourcetype.pl,perl diff --git a/tderesources/kolab/shared/CMakeLists.txt b/tderesources/kolab/shared/CMakeLists.txt new file mode 100644 index 000000000..1509deb1a --- /dev/null +++ b/tderesources/kolab/shared/CMakeLists.txt @@ -0,0 +1,30 @@ +################################################# +# +# (C) 2010-2011 Serghei Amelian +# serghei (DOT) amelian (AT) gmail.com +# +# Improvements and feedback are welcome +# +# This file is released under GPL >= 2 +# +################################################# + +include_directories( + ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_SOURCE_DIR} + ${CMAKE_SOURCE_DIR}/tderesources/lib + ${CMAKE_SOURCE_DIR}/libtdepim + ${TDE_INCLUDE_DIR} + ${TQT_INCLUDE_DIRS} +) + + +##### resourcekolabshared (static) ############## + +set( KDE3_DCOPIDL_EXECUTABLE ${KDE3_DCOPIDLNG_EXECUTABLE} ) + +tde_add_library( resourcekolabshared STATIC_PIC AUTOMOC + SOURCES + resourcekolabbase.cpp kmailconnection.cpp kolabbase.cpp subresource.cpp + kmailconnection.skel ${CMAKE_SOURCE_DIR}/kmail/kmailicalIface.stub +) diff --git a/tderesources/kolab/shared/Makefile.am b/tderesources/kolab/shared/Makefile.am new file mode 100644 index 000000000..287620330 --- /dev/null +++ b/tderesources/kolab/shared/Makefile.am @@ -0,0 +1,17 @@ +INCLUDES = -I$(top_srcdir) -I$(top_srcdir)/tderesources/lib $(all_includes) + +noinst_HEADERS = resourcekolabbase.h kolabbase.h subresource.h + +noinst_LTLIBRARIES = libresourcekolabshared.la + +libresourcekolabshared_la_SOURCES = \ + resourcekolabbase.cpp kmailconnection.cpp kolabbase.cpp \ + subresource.cpp \ + kmailconnection.skel kmailicalIface.stub +libresourcekolabshared_la_METASOURCES = AUTO +libresourcekolabshared_la_LIBADD = $(top_builddir)/libkcal/libkcal.la $(top_builddir)/libtdepim/libtdepim.la ../../lib/libkgroupwarebase.la +libresourcekolabshared_la_LDFLAGS = -no-undefined + +kmailicalIface_DCOPIDLNG = true + +kmailicalIface_DIR = $(top_srcdir)/kmail diff --git a/tderesources/kolab/shared/kmailconnection.cpp b/tderesources/kolab/shared/kmailconnection.cpp new file mode 100644 index 000000000..e82102a34 --- /dev/null +++ b/tderesources/kolab/shared/kmailconnection.cpp @@ -0,0 +1,339 @@ +/* + This file is part of the kolab resource - the implementation of the + Kolab storage format. See www.kolab.org for documentation on this. + + Copyright (c) 2004 Bo Thorsen <bo@sonofthor.dk> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. + + In addition, as a special exception, the copyright holders give + permission to link the code of this program with any edition of + the TQt library by Trolltech AS, Norway (or with modified versions + of TQt that use the same license as TQt), and distribute linked + combinations including the two. You must obey the GNU General + Public License in all respects for all of the code used other than + TQt. If you modify this file, you may extend this exception to + your version of the file, but you are not obligated to do so. If + you do not wish to do so, delete this exception statement from + your version. +*/ + +#include "kmailconnection.h" +#include "resourcekolabbase.h" + +#include <kdebug.h> +#include <dcopclient.h> +#include <kapplication.h> +#include <kdcopservicestarter.h> +#include <klocale.h> + +#include "kmailicalIface_stub.h" + + +using namespace Kolab; + + +KMailConnection::KMailConnection( ResourceKolabBase* resource, + const TQCString& objId ) + : DCOPObject( objId ), mResource( resource ), mKMailIcalIfaceStub( 0 ) +{ + // Make the connection to KMail ready + mDCOPClient = new DCOPClient(); + mDCOPClient->attach(); + mDCOPClient->registerAs( objId, true ); + + kapp->dcopClient()->setNotifications( true ); + connect( kapp->dcopClient(), TQT_SIGNAL( applicationRemoved( const TQCString& ) ), + this, TQT_SLOT( unregisteredFromDCOP( const TQCString& ) ) ); +} + +KMailConnection::~KMailConnection() +{ + kapp->dcopClient()->setNotifications( false ); + delete mKMailIcalIfaceStub; + mKMailIcalIfaceStub = 0; + delete mDCOPClient; + mDCOPClient = 0; +} + +static const TQCString dcopObjectId = "KMailICalIface"; +bool KMailConnection::connectToKMail() +{ + if ( !mKMailIcalIfaceStub ) { + TQCString dcopService; + + // if we are kmail (and probably kontact as well) ourselves, don't try to start us again + // this prevents a DCOP deadlock when launching the kmail while kontact is the IMAP backend + // provider (and probably vice versa) + if ( kapp->instanceName() == "kmail" ) { + // someone, probably ourselves, already offers the interface, if not stop here + const QCStringList services = kapp->dcopClient()->registeredApplications(); + for ( uint i = 0; i < services.count(); ++i ) { + if ( services[i].find( "anonymous" ) == 0 ) // querying anonymous-XXXXX deadlocks as well, what are those anyway? + continue; + const QCStringList objs = kapp->dcopClient()->remoteObjects( services[i] ); + if ( objs.contains( dcopObjectId ) ) { + dcopService = services[i]; + break; + } + } + if ( dcopService.isEmpty() ) { + kdError(5650) << k_funcinfo << "Not connecting to KMail to prevent DCOP deadlock" << endl; + return false; + } + } else { + TQString error; + int result = KDCOPServiceStarter::self()-> + findServiceFor( "DCOP/ResourceBackend/IMAP", TQString(), + TQString(), &error, &dcopService ); + if ( result != 0 ) { + kdError(5650) << "Couldn't connect to the IMAP resource backend\n"; + // TODO: You might want to show "error" (if not empty) here, + // using e.g. KMessageBox + return false; + } + } + + mKMailIcalIfaceStub = new KMailICalIface_stub( kapp->dcopClient(), + dcopService, dcopObjectId ); + + // Attach to the KMail signals + if ( !connectKMailSignal( "incidenceAdded(TQString,TQString,TQ_UINT32,int,TQString)", + "fromKMailAddIncidence(TQString,TQString,TQ_UINT32,int,TQString)" ) ) + kdError(5650) << "DCOP connection to incidenceAdded failed" << endl; + if ( !connectKMailSignal( "incidenceDeleted(TQString,TQString,TQString)", + "fromKMailDelIncidence(TQString,TQString,TQString)" ) ) + kdError(5650) << "DCOP connection to incidenceDeleted failed" << endl; + if ( !connectKMailSignal( "signalRefresh(TQString,TQString)", + "fromKMailRefresh(TQString,TQString)" ) ) + kdError(5650) << "DCOP connection to signalRefresh failed" << endl; + if ( !connectKMailSignal( "subresourceAdded( TQString, TQString, TQString, bool, bool )", + "fromKMailAddSubresource( TQString, TQString, TQString, bool, bool )" ) ) + kdError(5650) << "DCOP connection to subresourceAdded failed" << endl; + if ( !connectKMailSignal( "subresourceDeleted(TQString,TQString)", + "fromKMailDelSubresource(TQString,TQString)" ) ) + kdError(5650) << "DCOP connection to subresourceDeleted failed" << endl; + if ( !connectKMailSignal( "asyncLoadResult(TQMap<TQ_UINT32, TQString>, TQString, TQString)", + "fromKMailAsyncLoadResult(TQMap<TQ_UINT32, TQString>, TQString, TQString)" ) ) + kdError(5650) << "DCOP connection to asyncLoadResult failed" << endl; + } + + return ( mKMailIcalIfaceStub != 0 ); +} + +bool KMailConnection::fromKMailAddIncidence( const TQString& type, + const TQString& folder, + TQ_UINT32 sernum, + int format, + const TQString& data ) +{ + if ( format != KMailICalIface::StorageXML + && format != KMailICalIface::StorageIcalVcard ) + return false; +// kdDebug(5650) << "KMailConnection::fromKMailAddIncidence( " << type << ", " +// << folder << " ). iCal:\n" << ical << endl; + return mResource->fromKMailAddIncidence( type, folder, sernum, format, data ); +} + +void KMailConnection::fromKMailDelIncidence( const TQString& type, + const TQString& folder, + const TQString& xml ) +{ +// kdDebug(5650) << "KMailConnection::fromKMailDelIncidence( " << type << ", " +// << folder << ", " << uid << " )\n"; + mResource->fromKMailDelIncidence( type, folder, xml ); +} + +void KMailConnection::fromKMailRefresh( const TQString& type, const TQString& folder ) +{ +// kdDebug(5650) << "KMailConnection::fromKMailRefresh( " << type << ", " +// << folder << " )\n"; + mResource->fromKMailRefresh( type, folder ); +} + +void KMailConnection::fromKMailAddSubresource( const TQString& type, + const TQString& resource, + const TQString& label, + bool writable, + bool alarmRelevant ) +{ +// kdDebug(5650) << "KMailConnection::fromKMailAddSubresource( " << type << ", " +// << resource << " )\n"; + mResource->fromKMailAddSubresource( type, resource, label, + writable, alarmRelevant ); +} + +void KMailConnection::fromKMailDelSubresource( const TQString& type, + const TQString& resource ) +{ +// kdDebug(5650) << "KMailConnection::fromKMailDelSubresource( " << type << ", " +// << resource << " )\n"; + mResource->fromKMailDelSubresource( type, resource ); +} + +void KMailConnection::fromKMailAsyncLoadResult( const TQMap<TQ_UINT32, TQString>& map, + const TQString& type, + const TQString& folder ) +{ + mResource->fromKMailAsyncLoadResult( map, type, folder ); +} + +bool KMailConnection::connectKMailSignal( const TQCString& signal, + const TQCString& method ) +{ + return connectDCOPSignal( "kmail", dcopObjectId, signal, method, false ) + && connectDCOPSignal( "kontact", dcopObjectId, signal, method, false ); +} + +bool KMailConnection::kmailSubresources( TQValueList<KMailICalIface::SubResource>& lst, + const TQString& contentsType ) +{ + if ( !connectToKMail() ) + return false; + + lst = mKMailIcalIfaceStub->subresourcesKolab( contentsType ); + return mKMailIcalIfaceStub->ok(); +} + +bool KMailConnection::kmailIncidencesCount( int& count, + const TQString& mimetype, + const TQString& resource ) +{ + if ( !connectToKMail() ) + return false; + + count = mKMailIcalIfaceStub->incidencesKolabCount( mimetype, resource ); + return mKMailIcalIfaceStub->ok(); +} + +bool KMailConnection::kmailIncidences( TQMap<TQ_UINT32, TQString>& lst, + const TQString& mimetype, + const TQString& resource, + int startIndex, + int nbMessages ) +{ + if ( !connectToKMail() ) + return false; + + lst = mKMailIcalIfaceStub->incidencesKolab( mimetype, resource, startIndex, nbMessages ); + return mKMailIcalIfaceStub->ok(); +} + + +bool KMailConnection::kmailGetAttachment( KURL& url, + const TQString& resource, + TQ_UINT32 sernum, + const TQString& filename ) +{ + if ( !connectToKMail() ) + return false; + + url = mKMailIcalIfaceStub->getAttachment( resource, sernum, filename ); + return mKMailIcalIfaceStub->ok(); +} + +bool KMailConnection::kmailAttachmentMimetype( TQString & mimeType, + const TQString & resource, + TQ_UINT32 sernum, + const TQString & filename ) +{ + if ( !connectToKMail() ) + return false; + mimeType = mKMailIcalIfaceStub->attachmentMimetype( resource, sernum, filename ); + return mKMailIcalIfaceStub->ok(); +} + +bool KMailConnection::kmailListAttachments(TQStringList &list, + const TQString & resource, TQ_UINT32 sernum) +{ + if ( !connectToKMail() ) + return false; + + list = mKMailIcalIfaceStub->listAttachments( resource, sernum ); + return mKMailIcalIfaceStub->ok(); +} + +bool KMailConnection::kmailDeleteIncidence( const TQString& resource, + TQ_UINT32 sernum ) +{ + return connectToKMail() + && mKMailIcalIfaceStub->deleteIncidenceKolab( resource, sernum ) + && mKMailIcalIfaceStub->ok(); +} + +bool KMailConnection::kmailUpdate( const TQString& resource, + TQ_UINT32& sernum, + const TQString& subject, + const TQString& plainTextBody, + const TQMap<TQCString, TQString>& customHeaders, + const TQStringList& attachmentURLs, + const TQStringList& attachmentMimetypes, + const TQStringList& attachmentNames, + const TQStringList& deletedAttachments ) +{ + //kdDebug(5006) << kdBacktrace() << endl; + if ( connectToKMail() ) { + sernum = mKMailIcalIfaceStub->update( resource, sernum, subject, plainTextBody, customHeaders, + attachmentURLs, attachmentMimetypes, attachmentNames, + deletedAttachments ); + return sernum && mKMailIcalIfaceStub->ok(); + } else + return false; +} + +bool KMailConnection::kmailAddSubresource( const TQString& resource, + const TQString& parent, + const TQString& contentsType ) +{ + return connectToKMail() + && mKMailIcalIfaceStub->addSubresource( resource, parent, contentsType ) + && mKMailIcalIfaceStub->ok(); +} + +bool KMailConnection::kmailRemoveSubresource( const TQString& resource ) +{ + return connectToKMail() + && mKMailIcalIfaceStub->removeSubresource( resource ) + && mKMailIcalIfaceStub->ok(); +} + + +bool KMailConnection::kmailStorageFormat( KMailICalIface::StorageFormat& type, + const TQString& folder ) +{ + bool ok = connectToKMail(); + type = mKMailIcalIfaceStub->storageFormat( folder ); + return ok && mKMailIcalIfaceStub->ok(); +} + + +bool KMailConnection::kmailTriggerSync( const TQString &contentsType ) +{ + bool ok = connectToKMail(); + return ok && mKMailIcalIfaceStub->triggerSync( contentsType ); +} + +void KMailConnection::unregisteredFromDCOP( const TQCString& appId ) +{ + if ( mKMailIcalIfaceStub && mKMailIcalIfaceStub->app() == appId ) { + // Delete the stub so that the next time we need to talk to kmail, + // we'll know that we need to start a new one. + delete mKMailIcalIfaceStub; + mKMailIcalIfaceStub = 0; + } +} + +#include "kmailconnection.moc" diff --git a/tderesources/kolab/shared/kmailconnection.h b/tderesources/kolab/shared/kmailconnection.h new file mode 100644 index 000000000..62003b5f9 --- /dev/null +++ b/tderesources/kolab/shared/kmailconnection.h @@ -0,0 +1,129 @@ +/* + This file is part of the kolab resource - the implementation of the + Kolab storage format. See www.kolab.org for documentation on this. + + Copyright (c) 2004 Bo Thorsen <bo@sonofthor.dk> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. + + In addition, as a special exception, the copyright holders give + permission to link the code of this program with any edition of + the TQt library by Trolltech AS, Norway (or with modified versions + of TQt that use the same license as TQt), and distribute linked + combinations including the two. You must obey the GNU General + Public License in all respects for all of the code used other than + TQt. If you modify this file, you may extend this exception to + your version of the file, but you are not obligated to do so. If + you do not wish to do so, delete this exception statement from + your version. +*/ + +#ifndef KMAILCONNECTION_H +#define KMAILCONNECTION_H + +#include <dcopobject.h> +#include <kmail/kmailicalIface.h> + +class KURL; +class DCOPClient; +class KMailICalIface_stub; + +namespace Kolab { + +class ResourceKolabBase; + +/** + This class provides the kmail connectivity for IMAP resources. +*/ +class KMailConnection : public TQObject, public DCOPObject { + Q_OBJECT +// + K_DCOP + + // These are the methods called by KMail when the resource changes +k_dcop: + bool fromKMailAddIncidence( const TQString& type, const TQString& resource, + TQ_UINT32 sernum, int format, const TQString& xml ); + void fromKMailDelIncidence( const TQString& type, const TQString& resource, + const TQString& xml ); + void fromKMailRefresh( const TQString& type, const TQString& resource ); + void fromKMailAddSubresource( const TQString& type, const TQString& resource, + const TQString& label, bool writable, + bool alarmRelevant ); + void fromKMailDelSubresource( const TQString& type, const TQString& resource ); + void fromKMailAsyncLoadResult( const TQMap<TQ_UINT32, TQString>& map, const TQString& type, + const TQString& folder ); + +public: + KMailConnection( ResourceKolabBase* resource, const TQCString& objId ); + virtual ~KMailConnection(); + + /** + * Do the connection to KMail. + */ + bool connectToKMail(); + + // Call the DCOP methods + bool kmailSubresources( TQValueList<KMailICalIface::SubResource>& lst, + const TQString& contentsType ); + bool kmailIncidencesCount( int& count, + const TQString& mimetype, + const TQString& resource ); + bool kmailIncidences( TQMap<TQ_UINT32, TQString>& lst, const TQString& mimetype, + const TQString& resource, + int startIndex, + int nbMessages ); + + bool kmailGetAttachment( KURL& url, const TQString& resource, TQ_UINT32 sernum, + const TQString& filename ); + bool kmailAttachmentMimetype( TQString &mimeType, const TQString &resource, + TQ_UINT32 sernum, const TQString &filename ); + bool kmailListAttachments( TQStringList &list, const TQString &resource, + TQ_UINT32 sernum ); + bool kmailDeleteIncidence( const TQString& resource, TQ_UINT32 sernum ); + bool kmailUpdate( const TQString& resource, + TQ_UINT32& sernum, + const TQString& subject, + const TQString& plainTextBody, + const TQMap<TQCString, TQString>& customHeaders, + const TQStringList& attachmentURLs, + const TQStringList& attachmentMimetypes, + const TQStringList& attachmentNames, + const TQStringList& deletedAttachments ); + + bool kmailStorageFormat( KMailICalIface::StorageFormat& type, const TQString& folder); + + bool kmailTriggerSync( const TQString& contentsType ); + bool kmailAddSubresource( const TQString& resource, + const TQString& parent, + const TQString& contentsType ); + bool kmailRemoveSubresource( const TQString& resource ); + +private slots: + virtual void unregisteredFromDCOP( const TQCString& ); + +private: + /** Connect a signal from KMail to a local slot. */ + bool connectKMailSignal( const TQCString&, const TQCString& ); + + ResourceKolabBase* mResource; + DCOPClient* mDCOPClient; + KMailICalIface_stub* mKMailIcalIfaceStub; +}; + +} + +#endif // KMAILCONNECTION_H diff --git a/tderesources/kolab/shared/kolabbase.cpp b/tderesources/kolab/shared/kolabbase.cpp new file mode 100644 index 000000000..167f2566a --- /dev/null +++ b/tderesources/kolab/shared/kolabbase.cpp @@ -0,0 +1,487 @@ +/* + This file is part of the kolab resource - the implementation of the + Kolab storage format. See www.kolab.org for documentation on this. + + Copyright (c) 2004 Bo Thorsen <bo@sonofthor.dk> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. + + In addition, as a special exception, the copyright holders give + permission to link the code of this program with any edition of + the TQt library by Trolltech AS, Norway (or with modified versions + of TQt that use the same license as TQt), and distribute linked + combinations including the two. You must obey the GNU General + Public License in all respects for all of the code used other than + TQt. If you modify this file, you may extend this exception to + your version of the file, but you are not obligated to do so. If + you do not wish to do so, delete this exception statement from + your version. +*/ + +#include "kolabbase.h" + +#include <kabc/addressee.h> +#include <libkcal/journal.h> +#include <libtdepim/kpimprefs.h> +#include <libemailfunctions/email.h> +#include <kdebug.h> +#include <tqfile.h> + +using namespace Kolab; + + +KolabBase::KolabBase( const TQString& tz ) + : mCreationDate( TQDateTime::currentDateTime() ), + mLastModified( TQDateTime::currentDateTime() ), + mSensitivity( Public ), mTimeZoneId( tz ), + mHasPilotSyncId( false ), mHasPilotSyncStatus( false ) +{ +} + +KolabBase::~KolabBase() +{ +} + +void KolabBase::setFields( const KCal::Incidence* incidence ) +{ + // So far unhandled KCal::IncidenceBase fields: + // mPilotID, mSyncStatus, mFloats + + setUid( incidence->uid() ); + setBody( incidence->description() ); + setCategories( incidence->categoriesStr() ); + setCreationDate( localToUTC( incidence->created() ) ); + setLastModified( localToUTC( incidence->lastModified() ) ); + setSensitivity( static_cast<Sensitivity>( incidence->secrecy() ) ); + // TODO: Attachments +} + +void KolabBase::saveTo( KCal::Incidence* incidence ) const +{ + incidence->setUid( uid() ); + incidence->setDescription( body() ); + incidence->setCategories( categories() ); + incidence->setCreated( utcToLocal( creationDate() ) ); + incidence->setLastModified( utcToLocal( lastModified() ) ); + incidence->setSecrecy( sensitivity() ); + // 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 TQString creationString = addressee->custom( "KOLAB", "CreationDate" ); + kdDebug(5006) << "Creation time string: " << creationString << endl; + TQDateTime creationDate; + if ( creationString.isEmpty() ) { + creationDate = TQDateTime::currentDateTime(); + kdDebug(5006) << "Creation date set to current time\n"; + } + else { + creationDate = stringToDateTime( creationString ); + kdDebug(5006) << "Creation date loaded\n"; + } + TQDateTime modified = addressee->revision(); + if ( !modified.isValid() ) + modified = TQDateTime::currentDateTime(); + setLastModified( modified ); + if ( modified < creationDate ) { + // It's not possible that the modification date is earlier than creation + creationDate = modified; + kdDebug(5006) << "Creation date set to modification date\n"; + } + setCreationDate( creationDate ); + const TQString 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 ); + kdDebug(5006) << "Creation date modified. New one: " << newCreationDate << endl; + } + + 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( TQStringList::split( ',', categories() ) ); + addressee->setRevision( lastModified() ); + 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::setUid( const TQString& uid ) +{ + mUid = uid; +} + +TQString KolabBase::uid() const +{ + return mUid; +} + +void KolabBase::setBody( const TQString& body ) +{ + mBody = body; +} + +TQString KolabBase::body() const +{ + return mBody; +} + +void KolabBase::setCategories( const TQString& categories ) +{ + mCategories = categories; +} + +TQString KolabBase::categories() const +{ + return mCategories; +} + +void KolabBase::setCreationDate( const TQDateTime& date ) +{ + mCreationDate = date; +} + +TQDateTime KolabBase::creationDate() const +{ + return mCreationDate; +} + +void KolabBase::setLastModified( const TQDateTime& date ) +{ + mLastModified = date; +} + +TQDateTime 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( TQDomElement& element, Email& email ) +{ + for ( TQDomNode n = element.firstChild(); !n.isNull(); n = n.nextSibling() ) { + if ( n.isComment() ) + continue; + if ( n.isElement() ) { + TQDomElement e = n.toElement(); + const TQString tagName = e.tagName(); + + if ( tagName == "display-name" ) { + // Quote the text in case it contains commas or other quotable chars. + TQString tusername = KPIM::quoteNameIfNecessary( e.text() ); + + TQString tname, temail; + // ignore the return value because it will always be false since + // tusername does not contain "@domain". + KPIM::getNameAndMail( tusername, tname, temail ); + email.displayName = tname; + } + else if ( tagName == "smtp-address" ) + email.smtpAddress = e.text(); + else + // TODO: Unhandled tag - save for later storage + kdDebug() << "Warning: Unhandled tag " << e.tagName() << endl; + } else + kdDebug() << "Node is not a comment or an element???" << endl; + } + + return true; +} + +void KolabBase::saveEmailAttribute( TQDomElement& element, const Email& email, + const TQString& tagName ) const +{ + TQDomElement e = element.ownerDocument().createElement( tagName ); + element.appendChild( e ); + writeString( e, "display-name", email.displayName ); + writeString( e, "smtp-address", email.smtpAddress ); +} + +bool KolabBase::loadAttribute( TQDomElement& element ) +{ + const TQString tagName = element.tagName(); + switch ( tagName[0].latin1() ) { + 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( TQDomElement& 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() ) ); + writeString( element, "last-modification-date", + dateTimeToString( lastModified() ) ); + writeString( element, "sensitivity", sensitivityToString( sensitivity() ) ); + if ( hasPilotSyncId() ) + writeString( element, "pilot-sync-id", TQString::number( pilotSyncId() ) ); + if ( hasPilotSyncStatus() ) + writeString( element, "pilot-sync-status", TQString::number( pilotSyncStatus() ) ); + return true; +} + +bool KolabBase::load( const TQString& xml ) +{ + TQString errorMsg; + int errorLine, errorColumn; + TQDomDocument document; + bool ok = document.setContent( xml, &errorMsg, &errorLine, &errorColumn ); + + if ( !ok ) { + tqWarning( "Error loading document: %s, line %d, column %d", + errorMsg.latin1(), errorLine, errorColumn ); + return false; + } + + // XML file loaded into tree. Now parse it + return loadXML( document ); +} + +bool KolabBase::load( TQFile& xml ) +{ + TQString errorMsg; + int errorLine, errorColumn; + TQDomDocument document; + bool ok = document.setContent( &xml, &errorMsg, &errorLine, &errorColumn ); + + if ( !ok ) { + tqWarning( "Error loading document: %s, line %d, column %d", + errorMsg.latin1(), errorLine, errorColumn ); + return false; + } + + // XML file loaded into tree. Now parse it + return loadXML( document ); +} + +TQDomDocument KolabBase::domTree() +{ + TQDomDocument document; + + TQString p = "version=\"1.0\" encoding=\"UTF-8\""; + document.appendChild(document.createProcessingInstruction( "xml", p ) ); + + return document; +} + + +TQString KolabBase::dateTimeToString( const TQDateTime& time ) +{ + return time.toString( Qt::ISODate ) + 'Z'; +} + +TQString KolabBase::dateToString( const TQDate& date ) +{ + return date.toString( Qt::ISODate ); +} + +TQDateTime KolabBase::stringToDateTime( const TQString& _date ) +{ + TQString date( _date ); + //Deal with data from some clients that always append a Z to dates. + if ( date.endsWith( "ZZ" ) ) + date.truncate( date.length() - 2 ); + //In TQt3, Qt::ISODate cannot handle a trailing Z for UTC, so remove if found. + else if ( date.endsWith( "Z" ) ) + date.truncate( date.length() - 1 ); + return TQDateTime::fromString( date, Qt::ISODate ); +} + +TQDate KolabBase::stringToDate( const TQString& date ) +{ + return TQDate::fromString( date, Qt::ISODate ); +} + +TQString 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 TQString& s ) +{ + if ( s == "private" ) + return Private; + if ( s == "confidential" ) + return Confidential; + return Public; +} + +TQString KolabBase::colorToString( const TQColor& color ) +{ + // Color is in the format "#RRGGBB" + return color.name(); +} + +TQColor KolabBase::stringToColor( const TQString& s ) +{ + return TQColor( s ); +} + +void KolabBase::writeString( TQDomElement& element, const TQString& tag, + const TQString& tagString ) +{ + if ( !tagString.isEmpty() ) { + TQDomElement e = element.ownerDocument().createElement( tag ); + TQDomText t = element.ownerDocument().createTextNode( tagString ); + e.appendChild( t ); + element.appendChild( e ); + } +} + +TQDateTime KolabBase::localToUTC( const TQDateTime& time ) const +{ + return KPimPrefs::localTimeToUtc( time, mTimeZoneId ); +} + +TQDateTime KolabBase::utcToLocal( const TQDateTime& time ) const +{ + return KPimPrefs::utcToLocalTime( time, mTimeZoneId ); +} diff --git a/tderesources/kolab/shared/kolabbase.h b/tderesources/kolab/shared/kolabbase.h new file mode 100644 index 000000000..294a7d81b --- /dev/null +++ b/tderesources/kolab/shared/kolabbase.h @@ -0,0 +1,177 @@ +/* + This file is part of the kolab resource - the implementation of the + Kolab storage format. See www.kolab.org for documentation on this. + + Copyright (c) 2004 Bo Thorsen <bo@sonofthor.dk> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. + + In addition, as a special exception, the copyright holders give + permission to link the code of this program with any edition of + the TQt library by Trolltech AS, Norway (or with modified versions + of TQt that use the same license as TQt), and distribute linked + combinations including the two. You must obey the GNU General + Public License in all respects for all of the code used other than + TQt. If you modify this file, you may extend this exception to + your version of the file, but you are not obligated to do so. If + you do not wish to do so, delete this exception statement from + your version. +*/ + +#ifndef KOLABBASE_H +#define KOLABBASE_H + +#include <tqdom.h> +#include <tqdatetime.h> +#include <tqcolor.h> + +class TQFile; + +namespace KCal { + class Incidence; +} + +namespace KABC { + class Addressee; +} + +namespace Kolab { + +class KolabBase { +public: + struct Email { + public: + Email( const TQString& name = TQString(), + const TQString& email = TQString() ) + : displayName( name ), smtpAddress( email ) + { + } + + TQString displayName; + TQString smtpAddress; + }; + + enum Sensitivity { Public = 0, Private = 1, Confidential = 2 }; + + explicit KolabBase( const TQString& timezone = TQString() ); + virtual ~KolabBase(); + + // Return a string identifying this type + virtual TQString type() const = 0; + + virtual void setUid( const TQString& uid ); + virtual TQString uid() const; + + virtual void setBody( const TQString& body ); + virtual TQString body() const; + + virtual void setCategories( const TQString& categories ); + virtual TQString categories() const; + + virtual void setCreationDate( const TQDateTime& date ); + virtual TQDateTime creationDate() const; + + virtual void setLastModified( const TQDateTime& date ); + virtual TQDateTime 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 TQString dateTimeToString( const TQDateTime& time ); + static TQString dateToString( const TQDate& date ); + static TQDateTime stringToDateTime( const TQString& time ); + static TQDate stringToDate( const TQString& date ); + + // String - Sensitivity conversion methods + static TQString sensitivityToString( Sensitivity ); + static Sensitivity stringToSensitivity( const TQString& ); + + // String - Color conversion methods + static TQString colorToString( const TQColor& ); + static TQColor stringToColor( const TQString& ); + + // Load this object by reading the XML file + bool load( const TQString& xml ); + bool load( TQFile& xml ); + + // Load this TQDomDocument + virtual bool loadXML( const TQDomDocument& xml ) = 0; + + // Serialize this object to an XML string + virtual TQString saveXML() const = 0; + +protected: + /// Read all known fields from this ical incidence + void setFields( const KCal::Incidence* ); + + /// Save all known fields into this ical incidence + void saveTo( KCal::Incidence* ) 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; + + // This just makes the initial dom tree with version and doctype + static TQDomDocument domTree(); + + bool loadEmailAttribute( TQDomElement& element, Email& email ); + + void saveEmailAttribute( TQDomElement& element, const Email& email, + const TQString& tagName = "email" ) const; + + // Load the attributes of this class + virtual bool loadAttribute( TQDomElement& ); + + // Save the attributes of this class + virtual bool saveAttributes( TQDomElement& ) const; + + // Return the product ID + virtual TQString productID() const = 0; + + // Write a string tag + static void writeString( TQDomElement&, const TQString&, const TQString& ); + + TQDateTime localToUTC( const TQDateTime& time ) const; + TQDateTime utcToLocal( const TQDateTime& time ) const; + + TQString mUid; + TQString mBody; + TQString mCategories; + TQDateTime mCreationDate; + TQDateTime mLastModified; + Sensitivity mSensitivity; + TQString mTimeZoneId; + + // KPilot synchronization stuff + bool mHasPilotSyncId, mHasPilotSyncStatus; + unsigned long mPilotSyncId; + int mPilotSyncStatus; +}; + +} + +#endif // KOLABBASE_H diff --git a/tderesources/kolab/shared/resourcekolabbase.cpp b/tderesources/kolab/shared/resourcekolabbase.cpp new file mode 100644 index 000000000..a56538a67 --- /dev/null +++ b/tderesources/kolab/shared/resourcekolabbase.cpp @@ -0,0 +1,287 @@ +/* + This file is part of the kolab resource - the implementation of the + Kolab storage format. See www.kolab.org for documentation on this. + + Copyright (c) 2004 Bo Thorsen <bo@sonofthor.dk> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. + + In addition, as a special exception, the copyright holders give + permission to link the code of this program with any edition of + the TQt library by Trolltech AS, Norway (or with modified versions + of TQt that use the same license as TQt), and distribute linked + combinations including the two. You must obey the GNU General + Public License in all respects for all of the code used other than + TQt. If you modify this file, you may extend this exception to + your version of the file, but you are not obligated to do so. If + you do not wish to do so, delete this exception statement from + your version. +*/ + +#include "resourcekolabbase.h" +#include "kmailconnection.h" + +#include <folderselectdialog.h> + +#include <klocale.h> +#include <kstandarddirs.h> +#include <kinputdialog.h> +#include <kurl.h> +#include <ktempfile.h> +#include <kmessagebox.h> +#include <tqtextstream.h> +#include <kdebug.h> + +using namespace Kolab; + +static unsigned int uniquifier = 0; + +ResourceKolabBase::ResourceKolabBase( const TQCString& objId ) + : mSilent( false ) +{ + TDEGlobal::locale()->insertCatalogue( "kres_kolab" ); + TDEGlobal::locale()->insertCatalogue( "libkcal" ); + TQString uniqueObjId = TQString( objId ) + TQString::number( uniquifier++ ); + mConnection = new KMailConnection( this, uniqueObjId.utf8() ); +} + +ResourceKolabBase::~ResourceKolabBase() +{ + delete mConnection; +} + + +bool ResourceKolabBase::kmailSubresources( TQValueList<KMailICalIface::SubResource>& lst, + const TQString& contentsType ) const +{ + return mConnection->kmailSubresources( lst, contentsType ); +} + +bool ResourceKolabBase::kmailTriggerSync( const TQString& contentsType ) const +{ + return mConnection->kmailTriggerSync( contentsType ); +} + + +bool ResourceKolabBase::kmailIncidencesCount( int &count, + const TQString& mimetype, + const TQString& resource ) const +{ + return mConnection->kmailIncidencesCount( count, mimetype, resource ); +} + +bool ResourceKolabBase::kmailIncidences( TQMap<TQ_UINT32, TQString>& lst, + const TQString& mimetype, + const TQString& resource, + int startIndex, + int nbMessages ) const +{ + return mConnection->kmailIncidences( lst, mimetype, resource, startIndex, nbMessages ); +} + +bool ResourceKolabBase::kmailGetAttachment( KURL& url, const TQString& resource, + TQ_UINT32 sernum, + const TQString& filename ) const +{ + return mConnection->kmailGetAttachment( url, resource, sernum, filename ); +} + +bool ResourceKolabBase::kmailAttachmentMimetype( TQString & mimeType, TQString & resource, + TQ_UINT32 sernum, const TQString & filename ) const +{ + return mConnection->kmailAttachmentMimetype( mimeType, resource, sernum, filename ); +} + +bool ResourceKolabBase::kmailListAttachments( TQStringList &list, + const TQString & resource, + TQ_UINT32 sernum ) const +{ + return mConnection->kmailListAttachments( list, resource, sernum ); +} + +bool ResourceKolabBase::kmailDeleteIncidence( const TQString& resource, + TQ_UINT32 sernum ) +{ + return mSilent || mConnection->kmailDeleteIncidence( resource, sernum ); +} + +static TQString plainTextBody() +{ + const char * firstPartTextToTranslate = I18N_NOOP( + "This is a Kolab Groupware object.\nTo view this object you" + " will need an email client that can understand the Kolab" + " Groupware format.\nFor a list of such email clients please" + " visit\n%1" ); + const char * url = "http://www.kolab.org/kolab2-clients.html"; + TQString firstPartTextUntranslated = TQString::fromLatin1( firstPartTextToTranslate ).arg( url ); + TQString firstPartText = i18n( firstPartTextToTranslate ).arg( url ); + if ( firstPartText != firstPartTextUntranslated ) { + firstPartText.append("\n\n-----------------------------------------------------\n\n"); + firstPartText.append( firstPartTextUntranslated ); + } + return firstPartText; +} + +bool ResourceKolabBase::kmailUpdate( const TQString& resource, + TQ_UINT32& sernum, + const TQString& xml, + const TQString& mimetype, + const TQString& subject, + const CustomHeaderMap& _customHeaders, + const TQStringList& _attachmentURLs, + const TQStringList& _attachmentMimetypes, + const TQStringList& _attachmentNames, + const TQStringList& deletedAttachments ) +{ + if ( mSilent ) + return true; + + TQString subj = subject; + if ( subj.isEmpty() ) + subj = i18n("Internal kolab data: Do not delete this mail."); + + if ( mimetype.startsWith( "application/x-vnd.kolab" ) ) { + + // Save the xml file. Will be deleted at the end of this method + KTempFile file; + file.setAutoDelete( true ); + TQTextStream* stream = file.textStream(); + stream->setEncoding( TQTextStream::UnicodeUTF8 ); + *stream << xml; + file.close(); + + // Add the xml file as an attachment + TQStringList attachmentURLs = _attachmentURLs; + TQStringList attachmentMimeTypes = _attachmentMimetypes; + TQStringList attachmentNames = _attachmentNames; + KURL url; + url.setPath( file.name() ); + url.setFileEncoding( "UTF-8" ); + attachmentURLs.prepend( url.url() ); + attachmentMimeTypes.prepend( mimetype ); + attachmentNames.prepend( "kolab.xml" ); + + CustomHeaderMap customHeaders( _customHeaders ); + customHeaders.insert( "X-Kolab-Type", mimetype ); + + return mConnection->kmailUpdate( resource, sernum, subj, plainTextBody(), customHeaders, + attachmentURLs, attachmentMimeTypes, attachmentNames, + deletedAttachments ); + } else { + // ical style, simply put the data inline + return mConnection->kmailUpdate( resource, sernum, subj, xml, _customHeaders, + _attachmentURLs, _attachmentMimetypes, _attachmentNames, deletedAttachments ); + } +} + +TQString ResourceKolabBase::configFile( const TQString& type ) const +{ + return locateLocal( "config", + TQString( "tderesources/kolab/%1rc" ).arg( type ) ); +} + +bool ResourceKolabBase::connectToKMail() const +{ + return mConnection->connectToKMail(); +} + +bool ResourceKolabBase::kmailAddSubresource( const TQString& resource, + const TQString& parent, + const TQString& contentsType ) +{ + return mConnection->kmailAddSubresource( resource, parent, contentsType ); +} + +bool ResourceKolabBase::kmailRemoveSubresource( const TQString& resource ) +{ + return mConnection->kmailRemoveSubresource( resource ); +} + +TQString ResourceKolabBase::findWritableResource( const ResourceType &type, + const ResourceMap& resources, + const TQString& text ) +{ + mErrorCode = NoError; + + // I have to use the label (shown in the dialog) as key here. But given how the + // label is made up, it should be unique. If it's not, well the dialog would suck anyway... + TQMap<TQString, TQString> possible; + TQStringList labels; + ResourceMap::ConstIterator it; + for ( it = resources.begin(); it != resources.end(); ++it ) { + if ( it.data().writable() && it.data().active() ) { + // Writable and active possibility + possible[ it.data().label() ] = it.key(); + } + } + + if ( possible.isEmpty() ) { // None found!! + kdWarning(5650) << "No writable resource found!" << endl; + + TQString errorText; + switch( type ) { + case Events: + errorText = i18n( "You have no writable event folders so saving will not be possible.\n" + "Please create or activate at least one writable event folder and try again." ); + break; + case Tasks: + errorText = i18n( "You have no writable task folders so saving will not be possible.\n" + "Please create or activate at least one writable task folder and try again." ); + break; + case Incidences: + errorText = i18n( "You have no writable calendar folder so saving will not be possible.\n" + "Please create or activate at least one writable calendar folder and try again." ); + break; + case Notes: + errorText = i18n( "You have no writable notes folders so saving will not be possible.\n" + "Please create or activate at least one writable notes folder and try again." ); + break; + case Contacts: + errorText = i18n( "You have no writable addressbook folder so saving will not be possible.\n" + "Please create or activate at least one writable addressbook folder and try again." ); + break; + } + + KMessageBox::error( 0, errorText ); + mErrorCode = NoWritableFound; + return TQString(); + } + if ( possible.count() == 1 ) + // Just one found + return possible.begin().data(); // yes this is the subresource key, i.e. location + + TQString t = text; + if ( t.isEmpty() ) + i18n( "You have more than one writable resource folder. " + "Please select the one you want to write to." ); + + // Several found, ask the user + TQString chosenLabel = KPIM::FolderSelectDialog::getItem( i18n( "Select Resource Folder" ), + t, possible.keys() ); + if ( chosenLabel.isEmpty() ) { + // cancelled + mErrorCode = UserCancel; + return TQString(); + } + return possible[chosenLabel]; +} + +KMailICalIface::StorageFormat ResourceKolabBase::kmailStorageFormat( const TQString &folder ) const +{ + KMailICalIface::StorageFormat format = (KMailICalIface::StorageFormat) 3; + mConnection->kmailStorageFormat( format, folder ); + return format; +} diff --git a/tderesources/kolab/shared/resourcekolabbase.h b/tderesources/kolab/shared/resourcekolabbase.h new file mode 100644 index 000000000..f32c151fd --- /dev/null +++ b/tderesources/kolab/shared/resourcekolabbase.h @@ -0,0 +1,205 @@ +/* + This file is part of the kolab resource - the implementation of the + Kolab storage format. See www.kolab.org for documentation on this. + + Copyright (c) 2004 Bo Thorsen <bo@sonofthor.dk> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. + + In addition, as a special exception, the copyright holders give + permission to link the code of this program with any edition of + the TQt library by Trolltech AS, Norway (or with modified versions + of TQt that use the same license as TQt), and distribute linked + combinations including the two. You must obey the GNU General + Public License in all respects for all of the code used other than + TQt. If you modify this file, you may extend this exception to + your version of the file, but you are not obligated to do so. If + you do not wish to do so, delete this exception statement from + your version. +*/ + +#ifndef RESOURCEKOLABBASE_H +#define RESOURCEKOLABBASE_H + +#include <tqstring.h> +#include <tqmap.h> +#include <tqstringlist.h> + +#include "subresource.h" +#include <kmail/kmailicalIface.h> + +class TQCString; +class KURL; + +namespace Kolab { + +enum ResourceType { Tasks, Events, Incidences, Contacts, Notes }; + +class KMailConnection; + +/** + This class provides the kmail connectivity for IMAP resources. + + The main methods are: + + fromKMail...() : calls made _by_ KMail to add/delete data representation in the resource. + + kmail...() : calls _into_ KMail made by the resource. + + e.g. fromKMailAddIncidence() is called by KMail + when a new iCard is there after an IMAP sync. + + By calling fromKMailAddIncidence() KMail notifies + the resource about the new incidence, so in the + addressbook a new address will appear like magic. + + e.g. kmailAddIncidence() is called by the resource when + iCard must be stored by KMail because the user has added + an address in the addressbook. + + By calling kmailAddIncidence() the resource causes + KMail to store the new address in the (IMAP) folder. +*/ +class ResourceKolabBase { +public: + ResourceKolabBase( const TQCString& objId ); + virtual ~ResourceKolabBase(); + + // These are the methods called by KMail when the resource changes + virtual bool fromKMailAddIncidence( const TQString& type, + const TQString& resource, + TQ_UINT32 sernum, + int format, + const TQString& data ) = 0; + virtual void fromKMailDelIncidence( const TQString& type, + const TQString& resource, + const TQString& xml ) = 0; + virtual void fromKMailRefresh( const TQString& type, + const TQString& resource ) = 0; + virtual void fromKMailAddSubresource( const TQString& type, + const TQString& resource, + const TQString& label, + bool writable, + bool alarmRelevant ) = 0; + virtual void fromKMailDelSubresource( const TQString& type, + const TQString& resource ) = 0; + + virtual void fromKMailAsyncLoadResult( const TQMap<TQ_UINT32, TQString>& map, + const TQString& type, + const TQString& folder ) = 0; +protected: + /// Do the connection to KMail. + bool connectToKMail() const; + + // These are the KMail dcop function connections. The docs here say + // "Get", which here means that the first argument is the return arg + + /// List all folders with a certain contentsType. Returns a TQMap with + /// resourcename/writable pairs + bool kmailSubresources( TQValueList<KMailICalIface::SubResource>& lst, + const TQString& contentsType ) const; + + /// Get the number of messages in this folder. + /// Used to iterate over kmailIncidences by chunks + bool kmailIncidencesCount( int& count, const TQString& mimetype, + const TQString& resource ) const; + + /// Get the mimetype attachments from a chunk of messages from this folder. + /// Returns a TQMap with serialNumber/attachment pairs. + bool kmailIncidences( TQMap<TQ_UINT32, TQString>& lst, const TQString& mimetype, + const TQString& resource, + int startIndex, + int nbMessages ) const; + + bool kmailTriggerSync( const TQString& contentType ) const; + +public: // for Contact + /// Get an attachment from a mail. Returns a URL to it. This can + /// be called by the resource after obtaining the incidence. + /// The resource must delete the temp file. + bool kmailGetAttachment( KURL& url, const TQString& resource, + TQ_UINT32 sernum, + const TQString& filename ) const; + + /** Get the mimetype of the specified attachment. */ + bool kmailAttachmentMimetype( TQString &mimeType, TQString &resource, + TQ_UINT32 sernum, const TQString &filename ) const; + + /// List all attachments of a mail. + bool kmailListAttachments( TQStringList &list, const TQString &resource, + TQ_UINT32 sernum ) const; + +protected: + /// Delete an incidence. + bool kmailDeleteIncidence( const TQString& resource, TQ_UINT32 sernum ); + + KMailICalIface::StorageFormat kmailStorageFormat( const TQString& folder ) const; + + typedef TQMap<TQCString, TQString> CustomHeaderMap; + + /// Update an incidence. The list of attachments are URLs. + /// The parameter sernum is updated with the right KMail serial number + bool kmailUpdate( const TQString& resource, TQ_UINT32& sernum, + const TQString& xml, + const TQString& mimetype, + const TQString& subject, + const CustomHeaderMap& customHeaders = CustomHeaderMap(), + const TQStringList& attachmentURLs = TQStringList(), + const TQStringList& attachmentMimetypes = TQStringList(), + const TQStringList& attachmentNames = TQStringList(), + const TQStringList& deletedAttachments = TQStringList() ); + + bool kmailAddSubresource( const TQString& resource, const TQString& parent, + const TQString& contentsType ); + bool kmailRemoveSubresource( const TQString& resource ); + + /// Get the full path of the config file. + TQString configFile( const TQString& type ) const; + + /// If only one of these is writable, return that. Otherwise return null. + TQString findWritableResource( const ResourceType &type, + const ResourceMap& resources, + const TQString& text = TQString() ); + + enum ErrorCode { + NoError, + NoWritableFound, /**< No writable resource is available */ + UserCancel /**< User canceled the operation */ + }; + ErrorCode mErrorCode; + + bool mSilent; + + /** + * This is used to store a mapping from the XML UID to the KMail + * serial number of the mail it's stored in. That provides a quick way + * to access the storage in KMail. + */ + UidMap mUidMap; + + /// This is used to distinguish operations triggered by the user, + /// from operations triggered by KMail + TQStringList mUidsPendingAdding; + TQStringList mUidsPendingDeletion; + TQStringList mUidsPendingUpdate; + +private: + mutable KMailConnection* mConnection; +}; + +} + +#endif // RESOURCEKOLABBASE_H diff --git a/tderesources/kolab/shared/subresource.cpp b/tderesources/kolab/shared/subresource.cpp new file mode 100644 index 000000000..7520d275a --- /dev/null +++ b/tderesources/kolab/shared/subresource.cpp @@ -0,0 +1,133 @@ +/* + This file is part of libkabc and/or kaddressbook. + Copyright (c) 2004 Klarälvdalens Datakonsult AB + <info@klaralvdalens-datakonsult.se> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. + + In addition, as a special exception, the copyright holders give + permission to link the code of this program with any edition of + the TQt library by Trolltech AS, Norway (or with modified versions + of TQt that use the same license as TQt), and distribute linked + combinations including the two. You must obey the GNU General + Public License in all respects for all of the code used other than + TQt. If you modify this file, you may extend this exception to + your version of the file, but you are not obligated to do so. If + you do not wish to do so, delete this exception statement from + your version. +*/ + +#include "subresource.h" + +using namespace Kolab; + +SubResource::SubResource( bool active, bool writable, + bool alarmRelevant, const TQString& label, + int completionWeight ) + : mActive( active ), mWritable( writable ), mAlarmRelevant( alarmRelevant ), + mLabel( label ), mCompletionWeight( completionWeight ) +{ +} + +SubResource::SubResource( bool active, bool writable, + const TQString& label, int completionWeight ) + : mActive( active ), mWritable( writable ), mAlarmRelevant( false ), + mLabel( label ), mCompletionWeight( completionWeight ) +{ +} + +SubResource::~SubResource() +{ +} + +void SubResource::setActive( bool active ) +{ + mActive = active; +} + +bool SubResource::active() const +{ + return mActive; +} + +void SubResource::setAlarmRelevant( bool active ) +{ + mAlarmRelevant = active; +} + +bool SubResource::alarmRelevant() const +{ + return mAlarmRelevant; +} + +void SubResource::setWritable( bool writable ) +{ + mWritable = writable; +} + +bool SubResource::writable() const +{ + return mWritable; +} + +void SubResource::setLabel( const TQString& label ) +{ + mLabel = label; +} + +TQString SubResource::label() const +{ + return mLabel; +} + +void SubResource::setCompletionWeight( int completionWeight ) +{ + mCompletionWeight = completionWeight; +} + +int SubResource::completionWeight() const +{ + return mCompletionWeight; +} + +StorageReference::StorageReference( const TQString& resource, TQ_UINT32 sernum ) + : mResource( resource ), mSerialNumber( sernum ) +{ +} + +StorageReference::~StorageReference() +{ +} + +void StorageReference::setResource( const TQString& resource ) +{ + mResource = resource; +} + +TQString StorageReference::resource() const +{ + return mResource; +} + +void StorageReference::setSerialNumber( TQ_UINT32 serialNumber ) +{ + mSerialNumber = serialNumber; +} + +TQ_UINT32 StorageReference::serialNumber() const +{ + return mSerialNumber; +} diff --git a/tderesources/kolab/shared/subresource.h b/tderesources/kolab/shared/subresource.h new file mode 100644 index 000000000..79104a747 --- /dev/null +++ b/tderesources/kolab/shared/subresource.h @@ -0,0 +1,117 @@ +/* + This file is part of libkabc and/or kaddressbook. + Copyright (c) 2004 Klarälvdalens Datakonsult AB + <info@klaralvdalens-datakonsult.se> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. + + In addition, as a special exception, the copyright holders give + permission to link the code of this program with any edition of + the TQt library by Trolltech AS, Norway (or with modified versions + of TQt that use the same license as TQt), and distribute linked + combinations including the two. You must obey the GNU General + Public License in all respects for all of the code used other than + TQt. If you modify this file, you may extend this exception to + your version of the file, but you are not obligated to do so. If + you do not wish to do so, delete this exception statement from + your version. +*/ + +#ifndef SUBRESOURCE_H +#define SUBRESOURCE_H + +#include <tqstring.h> +#include <tqmap.h> + + +namespace Kolab { + +/** + * This class is used to store in a map from resource id to this, providing + * a lookup of the subresource settings. + */ +class SubResource { +public: + // This is just for TQMap + SubResource() {} + + SubResource( bool active, bool writable, const TQString& label, + int completionWeight = 100 ); + + SubResource( bool active, bool writable, bool alarmRelevant, + const TQString& label, int completionWeight = 100 ); + virtual ~SubResource(); + + virtual void setActive( bool active ); + virtual bool active() const; + + virtual void setWritable( bool writable ); + virtual bool writable() const; + + virtual void setAlarmRelevant( bool active ); + virtual bool alarmRelevant() const; + + virtual void setLabel( const TQString& label ); + virtual TQString label() const; + + virtual void setCompletionWeight( int completionWeight ); + virtual int completionWeight() const; + +private: + bool mActive; // Controlled by the applications + bool mWritable; // Set if the KMail folder is writable + bool mAlarmRelevant; // Set if the alarms from this resource are of + // interest to the user, as per folder acls + TQString mLabel; // The GUI name of this resource + + // This is just for the abc plugin. But as long as only this is here, + // it's just as cheap to have it in here as making a d-pointer that + // subclasses could add to. If more are added, then we should refactor + // to a d-pointer instead. + int mCompletionWeight; +}; + +typedef TQMap<TQString, SubResource> ResourceMap; + +/** + * This class is used to store a mapping from the XML UID to the KMail + * serial number of the mail it's stored in and the resource. That provides + * a quick way to access the storage in KMail. + */ +class StorageReference { +public: + // Just for TQMap + StorageReference() {} + + StorageReference( const TQString& resource, TQ_UINT32 sernum ); + virtual ~StorageReference(); + + virtual void setResource( const TQString& resource ); + virtual TQString resource() const; + + virtual void setSerialNumber( TQ_UINT32 serialNumber ); + virtual TQ_UINT32 serialNumber() const; + +private: + TQString mResource; + TQ_UINT32 mSerialNumber; +}; + +typedef TQMap<TQString, StorageReference> UidMap; + +} + +#endif // SUBRESOURCE_H diff --git a/tderesources/kolab/uninstall.desktop b/tderesources/kolab/uninstall.desktop new file mode 100644 index 000000000..e1e3e1732 --- /dev/null +++ b/tderesources/kolab/uninstall.desktop @@ -0,0 +1,2 @@ +[Desktop Entry] +Hidden=true diff --git a/tderesources/kolab/upgrade-resourcetype.pl b/tderesources/kolab/upgrade-resourcetype.pl new file mode 100644 index 000000000..98337a88d --- /dev/null +++ b/tderesources/kolab/upgrade-resourcetype.pl @@ -0,0 +1,24 @@ +#!/usr/bin/perl -w + +use strict; + +# This script updates some configuration keys + +# read the whole config file +my $currentGroup = ""; +my %configFile; +while ( <> ) { + chomp; # eat the trailing '\n' + next if ( /^$/ ); # skip empty lines + next if ( /^\#/ ); # skip comments + if ( /^\[(.+)\]$/ ) { # group begin + $currentGroup = $1; + next; + } elsif ( $currentGroup =~ /^Resource/ ) { + my ($key,$value) = split /=/; + if ( $key eq "ResourceType" and $value eq "kolab" ) { + print "# DELETE [$currentGroup]$key\n"; + print "[$currentGroup]\nResourceType=imap\n"; + } + } +} |