diff options
Diffstat (limited to 'tderesources/scalix/kcal')
-rw-r--r-- | tderesources/scalix/kcal/CMakeLists.txt | 53 | ||||
-rw-r--r-- | tderesources/scalix/kcal/Makefile.am | 27 | ||||
-rw-r--r-- | tderesources/scalix/kcal/resourcescalix.cpp | 903 | ||||
-rw-r--r-- | tderesources/scalix/kcal/resourcescalix.h | 225 | ||||
-rw-r--r-- | tderesources/scalix/kcal/resourcescalix_plugin.cpp | 50 | ||||
-rw-r--r-- | tderesources/scalix/kcal/scalix.desktop | 31 |
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 <[email protected]> + 2004 Till Adam <[email protected]> + + 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 <[email protected]> + 2004 Till Adam <[email protected]> + + 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 Klar�lvdalens Datakonsult AB + + 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 |