summaryrefslogtreecommitdiffstats
path: root/tderesources/scalix/kcal
diff options
context:
space:
mode:
Diffstat (limited to 'tderesources/scalix/kcal')
-rw-r--r--tderesources/scalix/kcal/CMakeLists.txt53
-rw-r--r--tderesources/scalix/kcal/Makefile.am27
-rw-r--r--tderesources/scalix/kcal/resourcescalix.cpp903
-rw-r--r--tderesources/scalix/kcal/resourcescalix.h225
-rw-r--r--tderesources/scalix/kcal/resourcescalix_plugin.cpp50
-rw-r--r--tderesources/scalix/kcal/scalix.desktop31
6 files changed, 1289 insertions, 0 deletions
diff --git a/tderesources/scalix/kcal/CMakeLists.txt b/tderesources/scalix/kcal/CMakeLists.txt
new file mode 100644
index 000000000..647ad26ed
--- /dev/null
+++ b/tderesources/scalix/kcal/CMakeLists.txt
@@ -0,0 +1,53 @@
+#################################################
+#
+# (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}/libtdepim
+ ${TDE_INCLUDE_DIR}
+ ${TQT_INCLUDE_DIRS}
+)
+
+link_directories(
+ ${TQT_LIBRARY_DIRS}
+)
+
+
+##### other data ################################
+
+install(
+ FILES scalix.desktop
+ DESTINATION ${SERVICES_INSTALL_DIR}/tderesources/kcal )
+
+install(
+ FILES ../uninstall.desktop
+ RENAME imap.desktop
+ DESTINATION ${SERVICES_INSTALL_DIR}/tderesources/kcal )
+
+
+##### kcal_scalix (module) ######################
+
+tde_add_kpart( kcal_scalix AUTOMOC
+ SOURCES resourcescalix_plugin.cpp
+ LINK kcalscalix-shared
+ DESTINATION ${PLUGIN_INSTALL_DIR}
+)
+
+
+##### kcalscalix (shared) #######################
+
+tde_add_library( kcalscalix SHARED AUTOMOC
+ SOURCES resourcescalix.cpp
+ VERSION 0.0.0
+ LINK resourcescalixshared-static kgroupwarebase-shared
+ DESTINATION ${LIB_INSTALL_DIR}
+)
diff --git a/tderesources/scalix/kcal/Makefile.am b/tderesources/scalix/kcal/Makefile.am
new file mode 100644
index 000000000..5ca09c64b
--- /dev/null
+++ b/tderesources/scalix/kcal/Makefile.am
@@ -0,0 +1,27 @@
+METASOURCES = AUTO
+
+INCLUDES = -I$(top_srcdir)/tderesources/scalix/shared -I$(top_srcdir) \
+ -I$(top_builddir)/libtdepim $(all_includes)
+
+# The scalix wizard links to this library too
+lib_LTLIBRARIES = libkcalscalix.la
+
+libkcalscalix_la_SOURCES = resourcescalix.cpp
+libkcalscalix_la_LDFLAGS = $(all_libraries) -no-undefined
+libkcalscalix_la_LIBADD = $(top_builddir)/libkcal/libkcal.la \
+ $(top_builddir)/tderesources/scalix/shared/libresourcescalixshared.la \
+ -ltderesources
+
+kde_module_LTLIBRARIES = kcal_scalix.la
+
+kcal_scalix_la_SOURCES = resourcescalix_plugin.cpp
+kcal_scalix_la_LDFLAGS = $(all_libraries) -module $(KDE_PLUGIN) -no-undefined
+kcal_scalix_la_LIBADD = libkcalscalix.la
+
+servicedir = $(kde_servicesdir)/tderesources/kcal
+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/tderesources/scalix/kcal/resourcescalix.cpp b/tderesources/scalix/kcal/resourcescalix.cpp
new file mode 100644
index 000000000..a757c81fc
--- /dev/null
+++ b/tderesources/scalix/kcal/resourcescalix.cpp
@@ -0,0 +1,903 @@
+/*
+ This file is part of the scalix resource - based on the kolab resource.
+
+ 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 "resourcescalix.h"
+
+#include <kio/observer.h>
+#include <kio/uiserver_stub.h>
+#include <kapplication.h>
+#include <dcopclient.h>
+#include <libkcal/icalformat.h>
+#include <libtdepim/kincidencechooser.h>
+#include <kabc/locknull.h>
+#include <kmainwindow.h>
+#include <klocale.h>
+
+#include <tqobject.h>
+#include <tqtimer.h>
+#include <tqapplication.h>
+
+#include <assert.h>
+
+using namespace KCal;
+using namespace Scalix;
+
+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";
+
+
+ResourceScalix::ResourceScalix( const TDEConfig *config )
+ : ResourceCalendar( config ), ResourceScalixBase( "ResourceScalix-libkcal" ),
+ mCalendar( TQString::fromLatin1("UTC") ), mOpen( false )
+{
+ setType( "scalix" );
+ connect( &mResourceChangedTimer, TQT_SIGNAL( timeout() ),
+ this, TQT_SLOT( slotEmitResourceChanged() ) );
+}
+
+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 ( mOpen ) {
+ close();
+ }
+}
+
+void ResourceScalix::loadSubResourceConfig( TDEConfig& config,
+ const TQString& name,
+ const TQString& label,
+ bool writable,
+ ResourceMap& subResource )
+{
+ TDEConfigGroup group( &config, name );
+ bool active = group.readBoolEntry( "Active", true );
+ subResource.insert( name, Scalix::SubResource( active, writable, label ) );
+}
+
+bool ResourceScalix::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, map );
+ return true;
+}
+
+bool ResourceScalix::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 closeResource( 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 ResourceScalix::doClose()
+{
+ if ( !mOpen )
+ // Not open
+ return;
+ mOpen = false;
+
+ TDEConfig config( configFile() );
+ closeResource( config, mEventSubResources );
+ closeResource( config, mTodoSubResources );
+ closeResource( config, mJournalSubResources );
+}
+
+bool ResourceScalix::loadSubResource( const TQString& subResource,
+ const char* mimetype )
+{
+ int count = 0;
+ if ( !kmailIncidencesCount( count, mimetype, subResource ) ) {
+ kdError(5650) << "Communication problem in ResourceScalix::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 ResourceScalix::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 ( progressId )
+ uiserver.jobFinished( progressId );
+ return true;
+}
+
+bool ResourceScalix::doLoad()
+{
+ mUidMap.clear();
+
+ return loadAllEvents() & loadAllTodos() & loadAllJournals();
+}
+
+bool ResourceScalix::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 ResourceScalix::loadAllEvents()
+{
+ removeIncidences( "Event" );
+ mCalendar.deleteAllEvents();
+ return doLoadAll( mEventSubResources, incidenceInlineMimeType );
+}
+
+bool ResourceScalix::loadAllTodos()
+{
+ removeIncidences( "Todo" );
+ mCalendar.deleteAllTodos();
+ return doLoadAll( mTodoSubResources, incidenceInlineMimeType );
+}
+
+bool ResourceScalix::loadAllJournals()
+{
+ removeIncidences( "Journal" );
+ mCalendar.deleteAllJournals();
+ return doLoadAll( mJournalSubResources, incidenceInlineMimeType );
+}
+
+void ResourceScalix::removeIncidences( const TQCString& incidenceType )
+{
+ Scalix::UidMap::Iterator mapIt = mUidMap.begin();
+ while ( mapIt != mUidMap.end() )
+ {
+ Scalix::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 ResourceScalix::doSave()
+{
+ return true;
+}
+
+void ResourceScalix::incidenceUpdated( KCal::IncidenceBase* incidencebase )
+{
+ if ( incidencebase->isReadOnly() ) return; // Should not happen (TM)
+ incidencebase->setSyncStatus( 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.
+
+ const TQString uid = incidencebase->uid();
+
+ if ( mUidsPendingUpdate.contains( uid ) || mUidsPendingAdding.contains( uid ) ) {
+ /* We are currently processing this event ( removing and readding or
+ * adding it ). If so, ignore this update. Keep the last of these around
+ * and process once we hear back from KMail on this event. */
+ mPendingUpdates.replace( uid, incidencebase );
+ return;
+ }
+
+ 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 ResourceScalix::resolveConflict( KCal::Incidence* inc, const TQString& subresource, TQ_UINT32 sernum )
+{
+ if ( ! inc )
+ return;
+ if ( ! mResolveConflict ) {
+ // we should do no conflict resolution
+ delete inc;
+ return;
+ }
+ Incidence* local = mCalendar.incidence( inc->uid() );
+ Incidence* localIncidence = 0;
+ Incidence* addedIncidence = 0;
+ if ( local ) {
+ 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() ;
+ }
+ Incidence* result = ch->getIncidence();
+ delete ch;
+ if ( result == local ) {
+ localIncidence = local->clone();
+ delete inc;
+ } else if ( result == inc ) {
+ addedIncidence = inc;
+ } else if ( result == 0 ) { // take both
+ localIncidence = local->clone();
+ localIncidence->recreate();
+ localIncidence->setSummary( i18n("Copy of: %1").arg(localIncidence->summary()) );
+ addedIncidence = inc;
+ }
+ bool silent = mSilent;
+ mSilent = false;
+ deleteIncidence( local ); // remove local from kmail
+ kmailDeleteIncidence( subresource, sernum );// remove new from kmail
+ if ( localIncidence ) {
+ addIncidence( localIncidence, subresource, 0 );
+ mUidsPendingAdding.remove( localIncidence->uid() ); // we do want to inform KOrg also
+ }
+ if ( addedIncidence ) {
+ addIncidence( addedIncidence, subresource, 0 );
+ mUidsPendingAdding.remove( addedIncidence->uid() ); // we do want to inform KOrg also
+ }
+ mSilent = silent;
+ }
+}
+void ResourceScalix::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
+ Incidence *inc = mFormat.fromString( data );
+ addIncidence( inc, subResource, sernum );
+}
+
+
+bool ResourceScalix::sendKMailUpdate( KCal::IncidenceBase* incidencebase, const TQString& subresource,
+ TQ_UINT32 sernum )
+{
+ const TQString& type = incidencebase->type();
+ const char* mimetype = 0;
+ TQString data;
+ if ( type == "Event" ) {
+ mimetype = incidenceInlineMimeType;
+ data = mFormat.createScheduleMessage( static_cast<KCal::Event *>(incidencebase),
+ Scheduler::Publish );
+ } else if ( type == "Todo" ) {
+ mimetype = incidenceInlineMimeType;
+ data = mFormat.createScheduleMessage( static_cast<KCal::Todo *>(incidencebase),
+ Scheduler::Publish );
+ } else if ( type == "Journal" ) {
+ mimetype = incidenceInlineMimeType;
+ data = mFormat.createScheduleMessage( static_cast<KCal::Journal *>(incidencebase),
+ Scheduler::Publish );
+ } 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 );
+ CustomHeaderMap customHeaders;
+
+ if ( type == "Event" )
+ customHeaders.insert( "X-Scalix-Class", "IPM.Appointment" );
+ else if ( type == "Todo" )
+ customHeaders.insert( "X-Scalix-Class", "IPM.Task" );
+
+ TQString subject = incidence->summary();
+
+ // behold, sernum is an in-parameter
+ const bool rc = kmailUpdate( subresource, sernum, data, mimetype, subject, customHeaders );
+ // update the serial number
+ if ( mUidMap.contains( incidencebase->uid() ) ) {
+ mUidMap[ incidencebase->uid() ].setSerialNumber( sernum );
+ }
+ return rc;
+}
+
+bool ResourceScalix::addIncidence( KCal::Incidence* incidence, const TQString& _subresource,
+ TQ_UINT32 sernum )
+{
+ Q_ASSERT( incidence );
+ if ( !incidence ) return false;
+ const TQString &uid = incidence->uid();
+ TQString subResource = _subresource;
+
+ Scalix::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 ) {
+ subResource = findWritableResource( *map );
+ }
+
+ if ( subResource.isEmpty() )
+ return false;
+
+ mNewIncidencesMap.insert( uid, subResource );
+
+ if ( !sendKMailUpdate( incidence, subResource, sernum ) ) {
+ kdError(5650) << "Communication problem in ResourceScalix::addIncidence()\n";
+ return false;
+ } else {
+ // KMail is doing it's best to add the event now, put a sticker on it,
+ // so we know it's one of our transient ones
+ mUidsPendingAdding.append( uid );
+
+ /* Add to the cache immediately if this is a new event coming from
+ * KOrganizer. It relies on the incidence being in the calendar when
+ * addIncidence returns. */
+ if ( newIncidence ) {
+ mCalendar.addIncidence( incidence );
+ incidence->registerObserver( this );
+ }
+ }
+ } else { /* KMail told us */
+ bool ourOwnUpdate = false;
+ /* 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.contains( uid ) ) {
+ 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 )
+ && ( mUidMap[ uid ].resource() == subResource ) ) {
+ if ( (*map)[ subResource ].writable() ) {
+ resolveConflict( incidence, subResource, sernum );
+ } else {
+ kdWarning( 5650 ) << "Duplicate event in a read-only folder detected! "
+ "Please inform the owner of the folder. " << endl;
+ }
+ return true;
+ }
+ /* 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 ResourceScalix::addEvent( KCal::Event* event )
+{
+ if ( mUidMap.contains( event->uid() ) )
+ return true; //noop
+ else
+ return addIncidence( event, TQString(), 0 );
+}
+
+bool ResourceScalix::addEvent( KCal::Event *event, const TQString &subresource )
+{
+ Q_UNUSED( subresource ); // ResourceScalix does not support subresources
+ return this->addEvent( event );
+}
+
+bool ResourceScalix::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 ResourceScalix::deleteEvent( KCal::Event* event )
+{
+ return deleteIncidence( event );
+}
+
+KCal::Event* ResourceScalix::event( const TQString& uid )
+{
+ return mCalendar.event(uid);
+}
+
+KCal::Event::List ResourceScalix::rawEvents( EventSortField sortField, SortDirection sortDirection )
+{
+ return mCalendar.rawEvents( sortField, sortDirection );
+}
+
+KCal::Event::List ResourceScalix::rawEventsForDate( const TQDate& date,
+ EventSortField sortField,
+ SortDirection sortDirection )
+{
+ return mCalendar.rawEventsForDate( date, sortField, sortDirection );
+}
+
+KCal::Event::List ResourceScalix::rawEventsForDate( const TQDateTime& qdt )
+{
+ return mCalendar.rawEventsForDate( qdt );
+}
+
+KCal::Event::List ResourceScalix::rawEvents( const TQDate& start,
+ const TQDate& end,
+ bool inclusive )
+{
+ return mCalendar.rawEvents( start, end, inclusive );
+}
+
+bool ResourceScalix::addTodo( KCal::Todo* todo )
+{
+ if ( mUidMap.contains( todo->uid() ) )
+ return true; //noop
+ else
+ return addIncidence( todo, TQString(), 0 );
+}
+
+bool ResourceScalix::addTodo( KCal::Todo *todo, const TQString &subresource )
+{
+ Q_UNUSED( subresource ); // ResourceScalix does not support subresources
+ return this->addTodo( todo );
+}
+
+bool ResourceScalix::deleteTodo( KCal::Todo* todo )
+{
+ return deleteIncidence( todo );
+}
+
+KCal::Todo* ResourceScalix::todo( const TQString& uid )
+{
+ return mCalendar.todo( uid );
+}
+
+KCal::Todo::List ResourceScalix::rawTodos( TodoSortField sortField, SortDirection sortDirection )
+{
+ return mCalendar.rawTodos( sortField, sortDirection );
+}
+
+KCal::Todo::List ResourceScalix::rawTodosForDate( const TQDate& date )
+{
+ return mCalendar.rawTodosForDate( date );
+}
+
+bool ResourceScalix::addJournal( KCal::Journal* journal )
+{
+ if ( mUidMap.contains( journal->uid() ) )
+ return true; //noop
+ else
+ return addIncidence( journal, TQString(), 0 );
+}
+
+bool ResourceScalix::addJournal( KCal::Journal *journal, const TQString &subresource )
+{
+ Q_UNUSED( subresource ); // ResourceScalix does not support subresources
+ return this->addJournal( journal );
+}
+
+bool ResourceScalix::deleteJournal( KCal::Journal* journal )
+{
+ return deleteIncidence( journal );
+}
+
+KCal::Journal* ResourceScalix::journal( const TQString& uid )
+{
+ return mCalendar.journal(uid);
+}
+
+KCal::Journal::List ResourceScalix::rawJournals( JournalSortField sortField, SortDirection sortDirection )
+{
+ return mCalendar.rawJournals( sortField, sortDirection );
+}
+
+KCal::Journal::List ResourceScalix::rawJournalsForDate( const TQDate &date )
+{
+ return mCalendar.rawJournalsForDate( date );
+}
+
+KCal::Alarm::List ResourceScalix::alarms( const TQDateTime& from,
+ const TQDateTime& to )
+{
+ return mCalendar.alarms( from, to );
+}
+
+KCal::Alarm::List ResourceScalix::alarmsTo( const TQDateTime& to )
+{
+ return mCalendar.alarmsTo(to);
+}
+
+void ResourceScalix::setTimeZoneId( const TQString& tzid )
+{
+ mCalendar.setTimeZoneId( tzid );
+ mFormat.setTimeZone( mCalendar.timeZoneId(), !mCalendar.isLocalTime() );
+}
+
+bool ResourceScalix::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;
+
+ Incidence *inc = mFormat.fromString( data );
+ if ( !inc )
+ rc = false;
+ else
+ addIncidence( inc, subResource, sernum );
+
+ return rc;
+}
+
+void ResourceScalix::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.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
+ KCal::Incidence* incidence = mCalendar.incidence( uid );
+ if( incidence ) {
+ incidence->unRegisterObserver( this );
+ mCalendar.deleteIncidence( incidence );
+ }
+ mUidMap.remove( uid );
+ mResourceChangedTimer.changeInterval( 100 );
+ }
+}
+
+void ResourceScalix::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 Scalix resource: fromKMailRefresh: unknown type " << type << endl;
+ mResourceChangedTimer.changeInterval( 100 );
+}
+
+void ResourceScalix::fromKMailAddSubresource( const TQString& type,
+ const TQString& subResource,
+ const TQString& label,
+ bool writable )
+{
+ 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 ] = Scalix::SubResource( active, writable, label );
+ loadSubResource( subResource, mimetype );
+ emit signalSubresourceAdded( this, type, subResource, label );
+}
+
+void ResourceScalix::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();
+
+ // Make a list of all uids to remove
+ Scalix::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() ) {
+ TemporarySilencer t( this );
+ TQStringList::ConstIterator it;
+ for ( it = uids.begin(); it != uids.end(); ++it ) {
+ KCal::Incidence* incidence = mCalendar.incidence( *it );
+ if( incidence )
+ mCalendar.deleteIncidence( incidence );
+ mUidMap.remove( *it );
+ }
+ }
+
+ emit signalSubresourceRemoved( this, type, subResource );
+}
+
+TQStringList ResourceScalix::subresources() const
+{
+ // Workaround: The ResourceView in KOrganizer wants to know this
+ // before it opens the resource :-( Make sure we are open
+ const_cast<ResourceScalix*>( this )->doOpen();
+ return ( mEventSubResources.keys()
+ + mTodoSubResources.keys()
+ + mJournalSubResources.keys() );
+}
+
+const TQString
+ResourceScalix::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;
+}
+
+TQString ResourceScalix::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();
+}
+
+void ResourceScalix::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 ResourceScalix::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<ResourceScalix*>( 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 ResourceScalix::setSubresourceActive( const TQString &subresource, bool v )
+{
+ ResourceMap *map = 0;
+
+ if ( mEventSubResources.contains( subresource ) )
+ map = &mEventSubResources;
+ if ( mTodoSubResources.contains( subresource ) )
+ map = &mTodoSubResources;
+ if ( mJournalSubResources.contains( subresource ) )
+ map = &mJournalSubResources;
+
+ if ( map && ( ( *map )[ subresource ].active() != v ) ) {
+ ( *map )[ subresource ].setActive( v );
+ doLoad(); // refresh the mCalendar cache
+ mResourceChangedTimer.changeInterval( 100 );
+ }
+}
+
+void ResourceScalix::slotEmitResourceChanged()
+{
+ kdDebug(5650) << "KCal Scalix resource: emitting resource changed " << endl;
+ mResourceChangedTimer.stop();
+ emit resourceChanged( this );
+}
+
+KABC::Lock* ResourceScalix::lock()
+{
+ return new KABC::LockNull( true );
+}
+
+
+Scalix::ResourceMap* ResourceScalix::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;
+}
+
+#include "resourcescalix.moc"
diff --git a/tderesources/scalix/kcal/resourcescalix.h b/tderesources/scalix/kcal/resourcescalix.h
new file mode 100644
index 000000000..dcd783979
--- /dev/null
+++ b/tderesources/scalix/kcal/resourcescalix.h
@@ -0,0 +1,225 @@
+/*
+ This file is part of the scalix resource - based on the kolab resource.
+
+ 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_RESOURCESCALIX_H
+#define KCAL_RESOURCESCALIX_H
+
+#include <tqtimer.h>
+
+#include <tdepimmacros.h>
+#include <libkcal/calendarlocal.h>
+#include <libkcal/icalformat.h>
+#include <libkcal/resourcecalendar.h>
+#include "../shared/resourcescalixbase.h"
+
+namespace KCal {
+
+struct TemporarySilencer;
+
+class KDE_EXPORT ResourceScalix : public KCal::ResourceCalendar,
+ public KCal::IncidenceBase::Observer,
+ public Scalix::ResourceScalixBase
+{
+ Q_OBJECT
+
+ friend struct TemporarySilencer;
+
+public:
+ ResourceScalix( const TDEConfig* );
+ virtual ~ResourceScalix();
+
+ /// Load resource data.
+ bool doLoad();
+
+ /// Save resource data.
+ bool doSave();
+
+ /// Open the notes resource.
+ bool doOpen();
+ /// Close the notes resource.
+ void doClose();
+
+ // The libkcal functions. See the resource for descriptions
+ bool addEvent( KCal::Event* anEvent );
+ bool addEvent( KCal::Event* anEvent, 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 );
+
+ 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 );
+
+ bool addJournal( KCal::Journal* );
+ bool addJournal( KCal::Journal* 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 ResourceScalixBase 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 );
+ 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;
+
+ /** Is this subresource active? */
+ bool subresourceActive( const TQString& ) const;
+ /** (De)activate the subresource */
+ virtual void setSubresourceActive( const TQString &, bool );
+
+ /** What is the label for this subresource? */
+ virtual const TQString labelForSubresource( const TQString& resource ) const;
+
+ virtual TQString subresourceIdentifier( Incidence *incidence );
+
+ KABC::Lock* lock();
+
+signals:
+ void useGlobalMode();
+protected slots:
+ void slotEmitResourceChanged();
+
+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 );
+
+ 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( Scalix::ResourceMap& map, const char* mimetype );
+
+ /// Reimplemented from IncidenceBase::Observer to know when an incidence was changed
+ void incidenceUpdated( KCal::IncidenceBase* );
+
+ bool openResource( TDEConfig& config, const char* contentType,
+ Scalix::ResourceMap& map );
+ void loadSubResourceConfig( TDEConfig& config, const TQString& name,
+ const TQString& label, bool writable,
+ Scalix::ResourceMap& subResource );
+ bool loadSubResource( const TQString& subResource, const char* mimetype );
+
+ TQString configFile() const {
+ return ResourceScalixBase::configFile( "kcal" );
+ }
+
+ Scalix::ResourceMap* subResourceMap( const TQString& contentsType );
+
+ bool sendKMailUpdate( KCal::IncidenceBase* incidence, const TQString& _subresource,
+ TQ_UINT32 sernum );
+
+
+ KCal::CalendarLocal mCalendar;
+
+ // The list of subresources
+ Scalix::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;
+};
+
+struct TemporarySilencer {
+ TemporarySilencer( ResourceScalix *_resource )
+ {
+ resource = _resource;
+ oldValue = resource->mSilent;
+ resource->mSilent = true;
+ }
+ ~TemporarySilencer()
+ {
+ resource->mSilent = oldValue;
+ }
+ ResourceScalix *resource;
+ bool oldValue;
+};
+
+}
+
+#endif // KCAL_RESOURCESCALIX_H
diff --git a/tderesources/scalix/kcal/resourcescalix_plugin.cpp b/tderesources/scalix/kcal/resourcescalix_plugin.cpp
new file mode 100644
index 000000000..036c2f45e
--- /dev/null
+++ b/tderesources/scalix/kcal/resourcescalix_plugin.cpp
@@ -0,0 +1,50 @@
+/*
+ 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 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 "resourcescalix.h"
+
+class ScalixFactory : public KRES::PluginFactoryBase
+{
+public:
+ KRES::Resource *resource( const TDEConfig *config )
+ {
+ return new KCal::ResourceScalix( config );
+ }
+
+ KRES::ConfigWidget *configWidget( TQWidget* )
+ {
+ return 0;
+ }
+};
+
+K_EXPORT_COMPONENT_FACTORY(kcal_scalix,ScalixFactory)
diff --git a/tderesources/scalix/kcal/scalix.desktop b/tderesources/scalix/kcal/scalix.desktop
new file mode 100644
index 000000000..06c5c8187
--- /dev/null
+++ b/tderesources/scalix/kcal/scalix.desktop
@@ -0,0 +1,31 @@
+[Desktop Entry]
+Name=Calendar on Scalix Server via KMail
+Name[bg]=Календар на сървъра Scalix през KMail
+Name[ca]=Calendari en un servidor Scalix mitjançant el KMail
+Name[da]=Kalender på Scalix-server via KMail
+Name[de]=Kalender auf einem Scalix-Server via KMail
+Name[el]=Ημερολόγιο σε εξυπηρετητή Scalix μέσω του KMail
+Name[es]=Calendario en servidor Scalix por medio de KMail
+Name[et]=Kalender Scalix-serveris (KMaili vahendusel)
+Name[fr]=Agenda sur serveur Scalix via KMail
+Name[is]=Dagatal á Scalix-þjóni gegnum KMail
+Name[it]=Calendario su server Scalix via KMail
+Name[ja]=KMail 経由 Scalix サーバのカレンダー
+Name[km]=ប្រតិទិន​នៅ​លើ​ម៉ាស៊ីន​បម្រើ Scalix តាម​រយៈ KMail
+Name[nds]=Kalenner op Scalix-Server över KMail
+Name[nl]=Agenda op Scalix-server via KMail
+Name[pl]=Kalendarz na serwerze Scalix za pośrednictwem KMaila
+Name[pt_BR]=Calendário em Servidor Scalix via KMail
+Name[ru]=Календарь на сервере Scalix через KMail
+Name[sk]=Kalendár na Scalix serveri pomocou KMail
+Name[sr]=Календар на Scalix серверу преко KMail-а
+Name[sr@Latn]=Kalendar na Scalix serveru preko KMail-a
+Name[sv]=Kalender på Scalix-server via Kmail
+Name[tr]=KMail Aracılığı ile Scalix Sunucusunda Takvim
+Name[zh_CN]=通过 KMail 访问 Scalix 服务器上的日历
+Name[zh_TW]=透過 KMail 取得 Scalix 伺服器上的行事曆
+X-TDE-Library=kcal_scalix
+Type=Service
+ServiceTypes=KResources/Plugin
+X-TDE-ResourceFamily=calendar
+X-TDE-ResourceType=scalix