diff options
author | toma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2009-11-25 17:56:58 +0000 |
---|---|---|
committer | toma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2009-11-25 17:56:58 +0000 |
commit | 460c52653ab0dcca6f19a4f492ed2c5e4e963ab0 (patch) | |
tree | 67208f7c145782a7e90b123b982ca78d88cc2c87 /kresources/scalix/kabc | |
download | tdepim-460c52653ab0dcca6f19a4f492ed2c5e4e963ab0.tar.gz tdepim-460c52653ab0dcca6f19a4f492ed2c5e4e963ab0.zip |
Copy the KDE 3.5 branch to branches/trinity for new KDE 3.5 features.
BUG:215923
git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/kdepim@1054174 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'kresources/scalix/kabc')
-rw-r--r-- | kresources/scalix/kabc/Makefile.am | 27 | ||||
-rw-r--r-- | kresources/scalix/kabc/contact.cpp | 377 | ||||
-rw-r--r-- | kresources/scalix/kabc/contact.h | 37 | ||||
-rw-r--r-- | kresources/scalix/kabc/resourcescalix.cpp | 628 | ||||
-rw-r--r-- | kresources/scalix/kabc/resourcescalix.h | 170 | ||||
-rw-r--r-- | kresources/scalix/kabc/resourcescalix_plugin.cpp | 53 | ||||
-rw-r--r-- | kresources/scalix/kabc/scalix.desktop | 30 |
7 files changed, 1322 insertions, 0 deletions
diff --git a/kresources/scalix/kabc/Makefile.am b/kresources/scalix/kabc/Makefile.am new file mode 100644 index 000000000..a9fbf2021 --- /dev/null +++ b/kresources/scalix/kabc/Makefile.am @@ -0,0 +1,27 @@ +METASOURCES = AUTO + +INCLUDES = -I$(top_srcdir)/kresources/scalix/shared -I$(top_srcdir) $(all_includes) + +# The scalix wizard links to this library too +lib_LTLIBRARIES = libkabcscalix.la + +libkabcscalix_la_SOURCES = resourcescalix.cpp contact.cpp +libkabcscalix_la_LDFLAGS = $(all_libraries) -no-undefined +libkabcscalix_la_LIBADD = \ + $(top_builddir)/kresources/scalix/shared/libresourcescalixshared.la \ + -lkresources -lkabc + +kde_module_LTLIBRARIES = kabc_scalix.la + +noinst_HEADERS = resourcescalix.h + +kabc_scalix_la_SOURCES = resourcescalix_plugin.cpp +kabc_scalix_la_LDFLAGS = $(all_libraries) -module $(KDE_PLUGIN) -no-undefined +kabc_scalix_la_LIBADD = libkabcscalix.la + +servicedir = $(kde_servicesdir)/kresources/kabc +service_DATA = scalix.desktop + +install-data-local: $(srcdir)/../uninstall.desktop + $(mkinstalldirs) $(DESTDIR)$(servicedir) + $(INSTALL_DATA) $(srcdir)/../uninstall.desktop $(DESTDIR)$(servicedir)/imap.desktop diff --git a/kresources/scalix/kabc/contact.cpp b/kresources/scalix/kabc/contact.cpp new file mode 100644 index 000000000..156ae01af --- /dev/null +++ b/kresources/scalix/kabc/contact.cpp @@ -0,0 +1,377 @@ +/* + * This file is part of the scalix resource. + * + * Copyright (C) 2007 Trolltech ASA. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include <qdom.h> + +#include <libkdepim/distributionlist.h> +#include <kstaticdeleter.h> + +#include "contact.h" + +using namespace Scalix; + +static QMap<QString, QString> *s_distListMap = 0; +static KStaticDeleter< QMap<QString, QString> > sd; + +static QString custom( const QString &name, const KABC::Addressee &addr, const QString &defaultValue = QString() ) +{ + const QString value = addr.custom( "Scalix", name ); + if ( value.isEmpty() ) + return defaultValue; + else + return value; +} + +static void setCustom( const QString &name, const QString &value, KABC::Addressee &addr ) +{ + addr.insertCustom( "Scalix", name, value ); +} + +QString Contact::toXml( const KABC::Addressee &addr ) +{ + /** + * Handle distribution lists. + */ + if ( KPIM::DistributionList::isDistributionList( addr ) ) { + if ( s_distListMap ) + return (*s_distListMap)[ addr.uid() ]; + else + return QString(); + } + + /** + * Handle normal contacts. + */ + QString xml; + xml += "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"; + xml += "<contact>\n"; + + xml += "<direct_ref>" + addr.uid() + "</direct_ref>\n"; + xml += "<sensitivity>" + custom( "sensitivity", addr, "0" ) + "</sensitivity>\n"; + + xml += "<message_class>IPM.Contact</message_class>\n"; + xml += "<is_recurring>" + custom( "is_recurring", addr, "false" ) + "</is_recurring>\n"; + xml += "<reminder_set>" + custom( "reminder_set", addr, "false" ) + "</reminder_set>\n"; + xml += "<send_rich_info>" + custom( "send_rich_info", addr, "false" ) + "</send_rich_info>\n"; + xml += "<subject>" + addr.formattedName() + "</subject>\n"; + xml += "<last_modification_time>" + addr.revision().toString( Qt::ISODate ) + "</last_modification_time>\n"; + + xml += "<display_name_prefix>" + addr.prefix() + "</display_name_prefix>\n"; + xml += "<first_name>" + addr.givenName() + "</first_name>\n"; + xml += "<middle_name>" + addr.additionalName() + "</middle_name>\n"; + xml += "<last_name>" + addr.familyName() + "</last_name>\n"; + xml += "<suffix>" + addr.suffix() + "</suffix>\n"; + xml += "<display_name>" + addr.assembledName() + "</display_name>\n"; + xml += "<file_as>" + addr.formattedName() + "</file_as>\n"; + xml += "<nickname>" + addr.nickName() + "</nickname>\n"; + + xml += "<web_page_address>" + addr.url().url() + "</web_page_address>\n"; + xml += "<company_name>" + addr.organization() + "</company_name>\n"; + xml += "<job_title>" + addr.title() + "</job_title>\n"; + + QStringList emails = addr.emails(); + for ( uint i = 0; i < 3; ++i ) { + QString type, address, comment, display; + + if ( i < emails.count() ) { + type = "SMTP"; + address = emails[ i ]; + + /** + * If the contact was created by kontact use the email address as + * display name and the formatted name as comment, otherwise we use + * the values from the server. + */ + if ( custom( "comes_from_scalix", addr ) != "true" ) { + comment = addr.formattedName(); + display = emails[ i ]; + } else { + comment = custom( QString( "email%1_address_with_comment" ).arg( i + 1 ), addr ); + display = custom( QString( "email%1_display_name" ).arg( i + 1 ), addr ); + } + } + + xml += QString( "<email%1_address_type>" ).arg( i + 1 ) + type + + QString( "</email%1_address_type>" ).arg( i + 1 ) +"\n"; + xml += QString( "<email%1_address>" ).arg( i + 1 ) + address + + QString( "</email%1_address>" ).arg( i + 1 ) +"\n"; + xml += QString( "<email%1_address_with_comment>" ).arg( i + 1 ) + comment + + QString( "</email%1_address_with_comment>" ).arg( i + 1 ) + "\n"; + xml += QString( "<email%1_display_name>" ).arg( i + 1 ) + display + + QString( "</email%1_display_name>" ).arg( i + 1 ) + "\n"; + } + + KABC::PhoneNumber phone = addr.phoneNumber( KABC::PhoneNumber::Home ); + xml += "<home_phone_number>" + phone.number() + "</home_phone_number>\n"; + + phone = addr.phoneNumber( KABC::PhoneNumber::Work ); + xml += "<work_phone_number>" + phone.number() + "</work_phone_number>\n"; + + phone = addr.phoneNumber( KABC::PhoneNumber::Work | KABC::PhoneNumber::Fax ); + xml += "<work_fax_number>" + phone.number() + "</work_fax_number>\n"; + + phone = addr.phoneNumber( KABC::PhoneNumber::Cell ); + xml += "<mobile_phone_number>" + phone.number() + "</mobile_phone_number>\n"; + + const KABC::Address workAddress = addr.address( KABC::Address::Work ); + xml += "<work_address_street>" + workAddress.street() + "</work_address_street>\n"; + xml += "<work_address_zip>" + workAddress.postalCode() + "</work_address_zip>\n"; + xml += "<work_address_city>" + workAddress.locality() + "</work_address_city>\n"; + xml += "<work_address_state>" + workAddress.region() + "</work_address_state>\n"; + xml += "<work_address_country>" + workAddress.country() + "</work_address_country>\n"; + + const KABC::Address homeAddress = addr.address( KABC::Address::Home ); + xml += "<home_address_street>" + homeAddress.street() + "</home_address_street>\n"; + xml += "<home_address_zip>" + homeAddress.postalCode() + "</home_address_zip>\n"; + xml += "<home_address_city>" + homeAddress.locality() + "</home_address_city>\n"; + xml += "<home_address_state>" + homeAddress.region() + "</home_address_state>\n"; + xml += "<home_address_country>" + homeAddress.country() + "</home_address_country>\n"; + + const KABC::Address otherAddress = addr.address( KABC::Address::Dom ); + xml += "<other_address_street>" + otherAddress.street() + "</other_address_street>\n"; + xml += "<other_address_zip>" + otherAddress.postalCode() + "</other_address_zip>\n"; + xml += "<other_address_city>" + otherAddress.locality() + "</other_address_city>\n"; + xml += "<other_address_state>" + otherAddress.region() + "</other_address_state>\n"; + xml += "<other_address_country>" + otherAddress.country() + "</other_address_country>\n"; + + if ( homeAddress.type() & KABC::Address::Pref ) + xml += "<selected_mailing_address>1</selected_mailing_address>\n"; + else if ( workAddress.type() & KABC::Address::Pref ) + xml += "<selected_mailing_address>2</selected_mailing_address>\n"; + else if ( otherAddress.type() & KABC::Address::Pref ) + xml += "<selected_mailing_address>3</selected_mailing_address>\n"; + + xml += "<im_address>" + addr.custom( "KADDRESSBOOK", "X-IMAddress" ) + "</im_address>\n"; + xml += "<manager>" + addr.custom( "KADDRESSBOOK", "X-ManagersName" ) + "</manager>\n"; + xml += "<department>" + addr.custom( "KADDRESSBOOK", "X-Department" ) + "</department>\n"; + xml += "<assistant>" + addr.custom( "KADDRESSBOOK", "X-AssistantsName" ) + "</assistant>\n"; + xml += "<profession>" + addr.custom( "KADDRESSBOOK", "X-Profession" ) + "</profession>\n"; + xml += "<office_location>" + addr.custom( "KADDRESSBOOK", "X-Office" ) + "</office_location>\n"; + xml += "<spouse>" + addr.custom( "KADDRESSBOOK", "X-SpousesName" ) + "</spouse>\n"; + + xml += "<bday>" + addr.birthday().toString( Qt::ISODate ) + "</bday>\n"; + xml += "<anniversary>" + addr.custom( "KADDRESSBOOK", "X-Anniversary" ) + "</anniversary>\n"; + + xml += "<mapi_charset>" + custom( "mapi_charset", addr, "UTF8" ) + "</mapi_charset>"; + + xml += "</contact>\n"; + + return xml; +} + +KABC::Addressee Contact::fromXml( const QString &xml ) +{ + QDomDocument document; + + QString errorMsg; + int errorLine, errorColumn; + if ( !document.setContent( xml, true, &errorMsg, &errorLine, &errorColumn ) ) { + qDebug( "Error parsing XML in Scalix::Contact::fromXml: %s (%d,%d)", errorMsg.latin1(), errorLine, errorColumn ); + return KABC::Addressee(); + } + + QDomElement contactElement = document.documentElement(); + if ( contactElement.tagName() != "contact" ) { + if ( contactElement.tagName() == "distlist" ) { + const QDomNodeList names = contactElement.elementsByTagName( "display_name" ); + const QString listName = ( names.count() == 1 ? names.item( 0 ).toElement().text() : "Scalix Dummy List" ); + + /** + * As we can't provide distribution list functionality we store the entry + * here and return it on save. + */ + KPIM::DistributionList list; + list.setName( listName ); + + if ( !s_distListMap ) + sd.setObject( s_distListMap, new QMap<QString, QString>() ); + + s_distListMap->insert( list.uid(), xml ); + + return list; + } else { + qDebug( "Error interpreting XML in Scalix::Contact::fromXml: no 'contact' or 'distlist' tag found" ); + return KABC::Addressee(); + } + } + + QString emails[ 3 ]; + KABC::Address homeAddress( KABC::Address::Home ); + KABC::Address workAddress( KABC::Address::Work ); + KABC::Address otherAddress( KABC::Address::Dom ); + + KABC::Addressee addr; + setCustom( "comes_from_scalix", "true", addr ); + + QDomNode node = contactElement.firstChild(); + while ( !node.isNull() ) { + QDomElement element = node.toElement(); + if ( !element.isNull() ) { + if ( element.tagName() == "direct_ref" ) + addr.setUid( element.text() ); + else if ( element.tagName() == "sensitivity" ) + setCustom( "sensitivity", element.text(), addr ); + else if ( element.tagName() == "is_recurring" ) + setCustom( "is_recurring", element.text(), addr ); + else if ( element.tagName() == "reminder_set" ) + setCustom( "reminder_set", element.text(), addr ); + else if ( element.tagName() == "send_rich_info" ) + setCustom( "send_rich_info", element.text(), addr ); + else if ( element.tagName() == "last_modification_time" ) + addr.setRevision( QDateTime::fromString( element.text(), Qt::ISODate ) ); + + // name + else if ( element.tagName() == "display_name_prefix" ) + addr.setPrefix( element.text() ); + else if ( element.tagName() == "first_name" ) + addr.setGivenName( element.text() ); + else if ( element.tagName() == "middle_name" ) + addr.setAdditionalName( element.text() ); + else if ( element.tagName() == "last_name" ) + addr.setFamilyName( element.text() ); + else if ( element.tagName() == "suffix" ) + addr.setSuffix( element.text() ); + else if ( element.tagName() == "file_as" ) + addr.setFormattedName( element.text() ); + else if ( element.tagName() == "nickname" ) + addr.setNickName( element.text() ); + + // job + else if ( element.tagName() == "web_page_address" ) + addr.setUrl( element.text() ); + else if ( element.tagName() == "company_name" ) + addr.setOrganization( element.text() ); + else if ( element.tagName() == "job_title" ) + addr.setTitle( element.text() ); + + // emails + else if ( element.tagName().startsWith( "email" ) ) { + if ( element.tagName() == "email1_address" ) + emails[ 0 ] = element.text(); + else if ( element.tagName() == "email2_address" ) + emails[ 1 ] = element.text(); + else if ( element.tagName() == "email3_address" ) + emails[ 2 ] = element.text(); + else + setCustom( element.tagName(), element.text(), addr ); + } + + // phone numbers + else if ( element.tagName() == "home_phone_number" ) + addr.insertPhoneNumber( KABC::PhoneNumber( element.text(), KABC::PhoneNumber::Home ) ); + else if ( element.tagName() == "work_phone_number" ) + addr.insertPhoneNumber( KABC::PhoneNumber( element.text(), KABC::PhoneNumber::Work ) ); + else if ( element.tagName() == "work_fax_number" ) + addr.insertPhoneNumber( KABC::PhoneNumber( element.text(), KABC::PhoneNumber::Work | KABC::PhoneNumber::Fax ) ); + else if ( element.tagName() == "mobile_phone_number" ) + addr.insertPhoneNumber( KABC::PhoneNumber( element.text(), KABC::PhoneNumber::Cell ) ); + + // address (work) + else if ( element.tagName() == "work_address_street" ) + workAddress.setStreet( element.text() ); + else if ( element.tagName() == "work_address_zip" ) + workAddress.setPostalCode( element.text() ); + else if ( element.tagName() == "work_address_city" ) + workAddress.setLocality( element.text() ); + else if ( element.tagName() == "work_address_state" ) + workAddress.setRegion( element.text() ); + else if ( element.tagName() == "work_address_country" ) + workAddress.setCountry( element.text() ); + + // address (home) + else if ( element.tagName() == "home_address_street" ) + homeAddress.setStreet( element.text() ); + else if ( element.tagName() == "home_address_zip" ) + homeAddress.setPostalCode( element.text() ); + else if ( element.tagName() == "home_address_city" ) + homeAddress.setLocality( element.text() ); + else if ( element.tagName() == "home_address_state" ) + homeAddress.setRegion( element.text() ); + else if ( element.tagName() == "home_address_country" ) + homeAddress.setCountry( element.text() ); + + // address (other) + else if ( element.tagName() == "other_address_street" ) + otherAddress.setStreet( element.text() ); + else if ( element.tagName() == "other_address_zip" ) + otherAddress.setPostalCode( element.text() ); + else if ( element.tagName() == "other_address_city" ) + otherAddress.setLocality( element.text() ); + else if ( element.tagName() == "other_address_state" ) + otherAddress.setRegion( element.text() ); + else if ( element.tagName() == "other_address_country" ) + otherAddress.setCountry( element.text() ); + + else if ( element.tagName() == "selected_mailing_address" ) + switch ( element.text().toInt() ) { + case 1: + homeAddress.setType( homeAddress.type() | KABC::Address::Pref ); + break; + case 2: + workAddress.setType( workAddress.type() | KABC::Address::Pref ); + break; + case 3: + otherAddress.setType( otherAddress.type() | KABC::Address::Pref ); + break; + default: + Q_ASSERT( !"Unknown selected_mailing_address enum" ); + break; + } + + // misc + else if ( element.tagName() == "im_address" ) + addr.insertCustom( "KADDRESSBOOK", "X-IMAddress", element.text() ); + else if ( element.tagName() == "manager" ) + addr.insertCustom( "KADDRESSBOOK", "X-ManagersName", element.text() ); + else if ( element.tagName() == "department" ) + addr.insertCustom( "KADDRESSBOOK", "X-Department", element.text() ); + else if ( element.tagName() == "assistant" ) + addr.insertCustom( "KADDRESSBOOK", "X-AssistantsName", element.text() ); + else if ( element.tagName() == "profession" ) + addr.insertCustom( "KADDRESSBOOK", "X-Profession", element.text() ); + else if ( element.tagName() == "office_location" ) + addr.insertCustom( "KADDRESSBOOK", "X-Office", element.text() ); + else if ( element.tagName() == "spouse" ) + addr.insertCustom( "KADDRESSBOOK", "X-SpousesName", element.text() ); + + else if ( element.tagName() == "bday" ) + addr.setBirthday( QDateTime::fromString( element.text(), Qt::ISODate ) ); + else if ( element.tagName() == "anniversary" ) + addr.insertCustom( "KADDRESSBOOK", "X-Anniversary", element.text() ); + else + setCustom( element.tagName(), element.text(), addr ); + } + + node = node.nextSibling(); + } + + for ( int i = 0; i < 3; ++i ) + if ( !emails[ i ].isEmpty() ) + addr.insertEmail( emails[ i ] ); + + if ( !homeAddress.isEmpty() ) + addr.insertAddress( homeAddress ); + if ( !workAddress.isEmpty() ) + addr.insertAddress( workAddress ); + if ( !otherAddress.isEmpty() ) + addr.insertAddress( otherAddress ); + + return addr; +} diff --git a/kresources/scalix/kabc/contact.h b/kresources/scalix/kabc/contact.h new file mode 100644 index 000000000..d73765ede --- /dev/null +++ b/kresources/scalix/kabc/contact.h @@ -0,0 +1,37 @@ +/* + * This file is part of the scalix resource. + * + * Copyright (C) 2007 Trolltech ASA. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef SCALIX_CONTACT_H +#define SCALIX_CONTACT_H + +#include <kabc/addressee.h> + +namespace Scalix { + +class Contact +{ + public: + static QString toXml( const KABC::Addressee &addr ); + static KABC::Addressee fromXml( const QString &xml ); +}; + +} + +#endif diff --git a/kresources/scalix/kabc/resourcescalix.cpp b/kresources/scalix/kabc/resourcescalix.cpp new file mode 100644 index 000000000..6cbf10832 --- /dev/null +++ b/kresources/scalix/kabc/resourcescalix.cpp @@ -0,0 +1,628 @@ +/* + This file is part of the scalix resource - based on the kolab resource. + + 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 Qt library by Trolltech AS, Norway (or with modified versions + of Qt that use the same license as Qt), and distribute linked + combinations including the two. You must obey the GNU General + Public License in all respects for all of the code used other than + Qt. If you modify this file, you may extend this exception to + your version of the file, but you are not obligated to do so. If + you do not wish to do so, delete this exception statement from + your version. +*/ + +#include "resourcescalix.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 <kmainwindow.h> +#include <kapplication.h> +#include <dcopclient.h> + +#include <qobject.h> +#include <qtimer.h> +#include <qstring.h> +#include <qfile.h> +#include <qapplication.h> + +#include <assert.h> + +#include "contact.h" + +using namespace Scalix; + +class ScalixFactory : public KRES::PluginFactoryBase +{ + public: + KRES::Resource *resource( const KConfig *config ) + { + return new KABC::ResourceScalix( config ); + } + + KRES::ConfigWidget *configWidget( QWidget* ) + { + return 0; + } +}; + +K_EXPORT_COMPONENT_FACTORY(kabc_scalix,ScalixFactory) + +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 = "application/scalix-properties"; + +KABC::ResourceScalix::ResourceScalix( const KConfig *config ) + : KPIM::ResourceABC( config ), + Scalix::ResourceScalixBase( "ResourceScalix-KABC" ), + mCachedSubresource( QString::null ), mLocked( false ) +{ + setType( "scalix" ); +} + +KABC::ResourceScalix::~ResourceScalix() +{ + // 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::ResourceScalix::loadSubResourceConfig( KConfig& config, + const QString& name, + const QString& label, + bool writable ) +{ + KConfigGroup group( &config, name ); + bool active = group.readBoolEntry( "Active", true ); + int completionWeight = group.readNumEntry( "CompletionWeight", 80 ); + mSubResources.insert( name, Scalix::SubResource( active, writable, label, + completionWeight ) ); +} + +bool KABC::ResourceScalix::doOpen() +{ + KConfig config( configFile() ); + + // Read the calendar entries + QValueList<KMailICalIface::SubResource> subResources; + if ( !kmailSubresources( subResources, s_kmailContentsType ) ) + return false; + mSubResources.clear(); + QValueList<KMailICalIface::SubResource>::ConstIterator it; + for ( it = subResources.begin(); it != subResources.end(); ++it ) { + loadSubResourceConfig( config, (*it).location, (*it).label, (*it).writable ); + } + + return true; +} + +void KABC::ResourceScalix::doClose() +{ + KConfig config( configFile() ); + + Scalix::ResourceMap::ConstIterator it; + for ( it = mSubResources.begin(); it != mSubResources.end(); ++it ) { + config.setGroup( it.key() ); + config.writeEntry( "Active", it.data().active() ); + config.writeEntry( "CompletionWeight", it.data().completionWeight() ); + } +} + +KABC::Ticket * KABC::ResourceScalix::requestSaveTicket() +{ + if ( !addressBook() ) { + kdError() << "no addressbook" << endl; + return 0; + } + mLocked = true; + + return createTicket( this ); +} + +void KABC::ResourceScalix::releaseSaveTicket( Ticket* ticket ) +{ + mLocked = false; + mCachedSubresource = QString::null; + delete ticket; +} + +QString KABC::ResourceScalix::loadContact( const QString& contactData, + const QString& subResource, + Q_UINT32 sernum, + KMailICalIface::StorageFormat ) +{ + KABC::Addressee addr = Contact::fromXml( contactData ); + + 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(); +} + +bool KABC::ResourceScalix::loadSubResource( const QString& subResource ) +{ + bool scalixcontacts = loadSubResourceHelper( subResource, s_attachmentMimeTypeContact, KMailICalIface::StorageXML ); + bool scalixdistlists = loadSubResourceHelper( subResource, s_attachmentMimeTypeDistList, KMailICalIface::StorageXML ); + bool vcardstyle = loadSubResourceHelper( subResource, s_inlineMimeType, KMailICalIface::StorageIcalVcard ); + return scalixcontacts && scalixdistlists && vcardstyle; +} + +bool KABC::ResourceScalix::loadSubResourceHelper( const QString& subResource, + const char* mimetype, + KMailICalIface::StorageFormat format ) +{ + int count = 0; + if ( !kmailIncidencesCount( count, mimetype, subResource ) ) { + kdError() << "Communication problem in KABC::ResourceScalix::loadSubResourceHelper()\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 ) { + QMap<Q_UINT32, QString> lst; + + if ( !kmailIncidences( lst, mimetype, subResource, startIndex, nbMessages ) ) { + kdError() << "Communication problem in ResourceScalix::load()\n"; + if ( progressId ) + uiserver.jobFinished( progressId ); + return false; + } + + for( QMap<Q_UINT32, QString>::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 ); + } + } + + kdDebug(5650) << "Contacts scalix resource: got " << count << " contacts in " << subResource << endl; + + if ( progressId ) + uiserver.jobFinished( progressId ); + return true; +} + +bool KABC::ResourceScalix::load() +{ + mUidMap.clear(); + mAddrMap.clear(); + + bool rc = true; + Scalix::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::ResourceScalix::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 Scalix { +struct AttachmentList { + QStringList attachmentURLs; + QStringList attachmentNames; + QStringList attachmentMimeTypes; + QStringList deletedAttachments; + QValueList<KTempFile *> tempFiles; + + void addAttachment( const QString& url, const QString& name, const QString& mimetype ) { + attachmentURLs.append( url ); + attachmentNames.append( name ); + attachmentMimeTypes.append( mimetype ); + } + + void updatePictureAttachment( const QImage& image, const QString& name ); + void updateAttachment( const QByteArray& data, const QString& name, const char* mimetype ); +}; +} // namespace + +void AttachmentList::updatePictureAttachment( const QImage& image, const QString& name ) +{ + assert( !name.isEmpty() ); + if ( !image.isNull() ) { + KTempFile* tempFile = new KTempFile; + 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 QByteArray& data, const QString& name, const char* mimetype ) +{ + assert( !name.isEmpty() ); + if ( !data.isNull() ) { + KTempFile* tempFile = new KTempFile; + 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::ResourceScalix::kmailUpdateAddressee( const Addressee& addr ) +{ + const QString uid = addr.uid(); + QString subResource; + Q_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() ) { + subResource = mCachedSubresource; + } else { + subResource = findWritableResource( mSubResources ); + // We were locked, remember the subresource we are working with until + // we are unlocked + if ( mLocked ) + mCachedSubresource = subResource; + } + if ( subResource.isEmpty() ) + return false; + sernum = 0; + } + + AttachmentList att; + QString subject = addr.formattedName(); + + QString mimetype = s_inlineMimeType; + + QString data = Contact::toXml( addr ); + + CustomHeaderMap customHeaders; + customHeaders.insert( "X-Scalix-Class", "IPM.Contact" ); + + bool rc = kmailUpdate( subResource, sernum, data, mimetype, subject, + customHeaders, + 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( QValueList<KTempFile *>::Iterator it = att.tempFiles.begin(); it != att.tempFiles.end(); ++it ) { + (*it)->setAutoDelete( true ); + delete (*it); + } + return rc; +} + +void KABC::ResourceScalix::insertAddressee( const Addressee& addr ) +{ + const QString 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::ResourceScalix::removeAddressee( const Addressee& addr ) +{ + const QString uid = addr.uid(); + if ( mUidMap.find( uid ) == mUidMap.end() ) return; + //kdDebug(5650) << k_funcinfo << uid << endl; + const QString 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::ResourceScalix::fromKMailAddIncidence( const QString& type, + const QString& subResource, + Q_UINT32 sernum, + int format, + const QString& contactXML ) +{ + // Check if this is a contact + if( type != s_kmailContentsType || !subresourceActive( subResource ) ) + return false; + + // Load contact to find the UID + const QString 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::ResourceScalix::fromKMailDelIncidence( const QString& type, + const QString& subResource, + const QString& 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::ResourceScalix::fromKMailRefresh( const QString& type, + const QString& /*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::ResourceScalix::fromKMailAddSubresource( const QString& type, + const QString& subResource, + const QString& label, + bool writable ) +{ + if( type != s_kmailContentsType ) return; + + if ( mSubResources.contains( subResource ) ) + // Already registered + return; + + KConfig config( configFile() ); + config.setGroup( "Contact" ); + loadSubResourceConfig( config, subResource, label, writable ); + loadSubResource( subResource ); + addressBook()->emitAddressBookChanged(); + emit signalSubresourceAdded( this, type, subResource ); +} + +void KABC::ResourceScalix::fromKMailDelSubresource( const QString& type, + const QString& 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 ); + + KConfig config( configFile() ); + config.deleteGroup( subResource ); + config.sync(); + + // Make a list of all uids to remove + Scalix::UidMap::ConstIterator mapIt; + QStringList 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() ) { + QStringList::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::ResourceScalix::fromKMailAsyncLoadResult( const QMap<Q_UINT32, QString>& map, + const QString& /* type */, + const QString& folder ) +{ + // FIXME + KMailICalIface::StorageFormat format = KMailICalIface::StorageXML; + for( QMap<Q_UINT32, QString>::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(); +} + +QStringList KABC::ResourceScalix::subresources() const +{ + return mSubResources.keys(); +} + +bool KABC::ResourceScalix::subresourceActive( const QString& subresource ) const +{ + if ( mSubResources.contains( subresource ) ) { + return mSubResources[ subresource ].active(); + } + + // Safe default bet: + kdDebug(5650) << "subresourceActive( " << subresource << " ): Safe bet\n"; + + return true; +} + +bool KABC::ResourceScalix::subresourceWritable( const QString& subresource ) const +{ + if ( mSubResources.contains( subresource ) ) { + return mSubResources[ subresource ].writable(); + } + return false; //better a safe default +} + +int KABC::ResourceScalix::subresourceCompletionWeight( const QString& subresource ) const +{ + if ( mSubResources.contains( subresource ) ) { + return mSubResources[ subresource ].completionWeight(); + } + + kdDebug(5650) << "subresourceCompletionWeight( " << subresource << " ): not found, using default\n"; + + return 80; +} + +QString KABC::ResourceScalix::subresourceLabel( const QString& subresource ) const +{ + if ( mSubResources.contains( subresource ) ) { + return mSubResources[ subresource ].label(); + } + + kdDebug(5650) << "subresourceLabel( " << subresource << " ): not found!\n"; + return QString::null; +} + +void KABC::ResourceScalix::setSubresourceCompletionWeight( const QString& subresource, int completionWeight ) +{ + if ( mSubResources.contains( subresource ) ) { + mSubResources[ subresource ].setCompletionWeight( completionWeight ); + } else { + kdDebug(5650) << "setSubresourceCompletionWeight: subresource " << subresource << " not found" << endl; + } +} + +QMap<QString, QString> KABC::ResourceScalix::uidToResourceMap() const +{ + // TODO: Couldn't this be made simpler? + QMap<QString, QString> map; + Scalix::UidMap::ConstIterator mapIt; + for ( mapIt = mUidMap.begin(); mapIt != mUidMap.end(); ++mapIt ) + map[ mapIt.key() ] = mapIt.data().resource(); + return map; +} + +void KABC::ResourceScalix::setSubresourceActive( const QString &subresource, bool active ) +{ + if ( mSubResources.contains( subresource ) ) { + mSubResources[ subresource ].setActive( active ); + load(); + } else { + kdDebug(5650) << "setSubresourceCompletionWeight: subresource " << subresource << " not found" << endl; + } +} + +#include "resourcescalix.moc" diff --git a/kresources/scalix/kabc/resourcescalix.h b/kresources/scalix/kabc/resourcescalix.h new file mode 100644 index 000000000..10d3d8aa5 --- /dev/null +++ b/kresources/scalix/kabc/resourcescalix.h @@ -0,0 +1,170 @@ +/* + This file is part of the scalix resource - based on the kolab resource. + + 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 Qt library by Trolltech AS, Norway (or with modified versions + of Qt that use the same license as Qt), and distribute linked + combinations including the two. You must obey the GNU General + Public License in all respects for all of the code used other than + Qt. If you modify this file, you may extend this exception to + your version of the file, but you are not obligated to do so. If + you do not wish to do so, delete this exception statement from + your version. +*/ + +#ifndef KABC_RESOURCESCALIX_H +#define KABC_RESOURCESCALIX_H + +#include <libkdepim/resourceabc.h> +#include <dcopobject.h> +#include "../shared/resourcescalixbase.h" +#include "../shared/subresource.h" +#include <kmail/kmailicalIface.h> +#include <kdepimmacros.h> + +namespace KABC { + + class FormatPlugin; + +/** + * This class implements a KAddressBook resource that keeps its + * addresses in an Scalix folder in KMail (or other conforming email + * clients). + */ +class KDE_EXPORT ResourceScalix : public KPIM::ResourceABC, + public Scalix::ResourceScalixBase +{ + Q_OBJECT + +public: + /** + * Constructor + */ + ResourceScalix( const KConfig* ); + + /** + * Destructor. + */ + virtual ~ResourceScalix(); + + /** + * 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 QString& type, const QString& id, + const QString& label, bool writable ); + void fromKMailDelSubresource( const QString& type, const QString& id ); + + bool fromKMailAddIncidence( const QString& type, const QString& resource, + Q_UINT32 sernum, int format, const QString& contact ); + void fromKMailDelIncidence( const QString& type, const QString& resource, + const QString& contact ); + void fromKMailRefresh( const QString& type, const QString& resource ); + + void fromKMailAsyncLoadResult( const QMap<Q_UINT32, QString>& map, + const QString& type, + const QString& folder ); + + /// Return the list of subresources. + QStringList subresources() const; + + /// Is this subresource active? + bool subresourceActive( const QString& ) const; + /// Is this subresource writabel? + bool subresourceWritable( const QString& ) const; + + virtual void setSubresourceActive( const QString &, bool ); + + /// Completion weight for a given subresource + virtual int subresourceCompletionWeight( const QString& ) const; + + /// Label for a given subresource + virtual QString subresourceLabel( const QString& ) const; + + /// Set completion weight for a given subresource + virtual void setSubresourceCompletionWeight( const QString&, int ); + + /// Give the uidmap. Used for ordered searching + QMap<QString, QString> uidToResourceMap() const; + +protected: + bool kmailUpdateAddressee( const Addressee& ); + + void doClose(); + + void loadSubResourceConfig( KConfig& config, const QString& name, + const QString& label, bool writable ); + bool loadSubResource( const QString& subResource ); + bool loadSubResourceHelper( const QString& subResource, const char* mimetype, KMailICalIface::StorageFormat format ); + QString loadContact( const QString& contactData, const QString& subResource, + Q_UINT32 sernum, const KMailICalIface::StorageFormat format ); + + QString configFile() const { + return Scalix::ResourceScalixBase::configFile( "kabc" ); + } + + // The list of subresources + Scalix::ResourceMap mSubResources; + QString mCachedSubresource; + bool mLocked; +}; + +} + +#endif // KABC_RESOURCESCALIX_H diff --git a/kresources/scalix/kabc/resourcescalix_plugin.cpp b/kresources/scalix/kabc/resourcescalix_plugin.cpp new file mode 100644 index 000000000..95bc28beb --- /dev/null +++ b/kresources/scalix/kabc/resourcescalix_plugin.cpp @@ -0,0 +1,53 @@ +/* + This file is part of the scalix resource - based on the kolab resource. + + 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 Qt library by Trolltech AS, Norway (or with modified versions + of Qt that use the same license as Qt), and distribute linked + combinations including the two. You must obey the GNU General + Public License in all respects for all of the code used other than + Qt. If you modify this file, you may extend this exception to + your version of the file, but you are not obligated to do so. If + you do not wish to do so, delete this exception statement from + your version. +*/ + +#include "resourcescalix.h" + +using namespace Scalix; + +class ScalixFactory : public KRES::PluginFactoryBase +{ + public: + KRES::Resource *resource( const KConfig *config ) + { + return new KABC::ResourceScalix( config ); + } + + KRES::ConfigWidget *configWidget( QWidget* ) + { + return 0; + } +}; + +K_EXPORT_COMPONENT_FACTORY(kabc_scalix,ScalixFactory) + diff --git a/kresources/scalix/kabc/scalix.desktop b/kresources/scalix/kabc/scalix.desktop new file mode 100644 index 000000000..b91af2470 --- /dev/null +++ b/kresources/scalix/kabc/scalix.desktop @@ -0,0 +1,30 @@ +[Desktop Entry] +Name=Addressbook on Scalix Server via KMail +Name[bg]=Адресник на сървъра Scalix през KMail +Name[ca]=Llibreta d'adreces en un servidor Scalix mitjançant el KMail +Name[da]=Adressebog på Scalix-server via KMail +Name[de]=Adressbuch auf einem Scalix-Server via KMail +Name[el]=Βιβλίο διευθύνσεων σε εξυπηρετητή Scalix μέσω του KMail +Name[es]=Libreta de direcciones en servidor Scalix por medio de KMail +Name[et]=Aadressiraamat Scalix-serveris (KMaili vahendusel) +Name[fr]=Carnet d'adresses sur serveur Scalix via KMail +Name[is]=Vistfangaskrá á Scalix-þjóni gegnum KMail +Name[it]=Rubrica indirizzi su server Scalix via KMail +Name[ja]=KMail 経由 Scalix サーバのアドレス帳 +Name[km]=សៀវភៅអាសយដ្ឋាននៅលើម៉ាស៊ីនបម្រើ Scalix តាមរយៈ KMail +Name[nds]=Adressbook op Scalix-Server över KMail +Name[nl]=Adresboek op Scalix-server via KMail +Name[pl]=Książka adresowa na serwerze Scalix za pośrednictwem KMaila +Name[ru]=Адресная книга на сервере Scalix через KMail +Name[sk]=Adresár na Scalix serveri pomocou KMail +Name[sr]=Адресар на Scalix серверу преко KMail-а +Name[sr@Latn]=Adresar na Scalix serveru preko KMail-a +Name[sv]=Adressbok på Scalix-server via Kmail +Name[tr]=KMail Aracılığı ile Scalix Sunucusunda Adres Defteri +Name[zh_CN]=通过 KMail 访问 Scalix 服务器上的地址簿 +Name[zh_TW]=透過 KMail 取得 Scalix 伺服器上的通訊錄 +X-KDE-Library=kabc_scalix +Type=Service +ServiceTypes=KResources/Plugin +X-KDE-ResourceFamily=contact +X-KDE-ResourceType=scalix |