[prev in list] [next in list] [prev in thread] [next in thread]
List: kde-commits
Subject: [kdepim-runtime/akregator-migrator] migration: Add migrator for
From: Frank Osterfeld <frank.osterfeld () kdab ! com>
Date: 2011-09-26 20:51:31
Message-ID: 20110926205131.4AA7BA607A () git ! kde ! org
[Download RAW message or body]
Git commit 35a55815f13888be57d0587818ea04c81d64bba5 by Frank Osterfeld.
Committed on 21/09/2011 at 23:03.
Pushed by osterfeld into branch 'akregator-migrator'.
Add migrator for Akregator's feed storage
M +1 -0 migration/CMakeLists.txt
A +43 -0 migration/akregator/CMakeLists.txt
A +274 -0 migration/akregator/akregatormigrator.cpp [License: LGPL (v2+)]
A +66 -0 migration/akregator/akregatormigrator.h [License: LGPL (v2+)]
A +284 -0 migration/akregator/importitemsjob.cpp [License: GPL (v2+)]
A +74 -0 migration/akregator/importitemsjob.h [License: GPL (v2+)]
A +303 -0 migration/akregator/itemimportreader.cpp [License: GPL (v2+)]
A +48 -0 migration/akregator/itemimportreader.h [License: GPL (v2+)]
A +86 -0 migration/akregator/itemsync.cpp [License: GPL (v2+)]
A +38 -0 migration/akregator/itemsync.h [License: GPL (v2+)]
A +84 -0 migration/akregator/main.cpp [License: LGPL (v2+)]
A +2 -0 migration/akregator/org.kde.Akonadi.RssLocal.Settings.xml
http://commits.kde.org/kdepim-runtime/35a55815f13888be57d0587818ea04c81d64bba5
diff --git a/migration/CMakeLists.txt b/migration/CMakeLists.txt
index b10d56b..85548cf 100644
--- a/migration/CMakeLists.txt
+++ b/migration/CMakeLists.txt
@@ -16,6 +16,7 @@ set( MIGRATION_AKONADI_SHARED_SOURCES
)
+add_subdirectory( akregator )
add_subdirectory( kmail )
add_subdirectory( kaddressbook )
add_subdirectory( kjots )
diff --git a/migration/akregator/CMakeLists.txt b/migration/akregator/CMakeLists.txt
new file mode 100644
index 0000000..3bd4b74
--- /dev/null
+++ b/migration/akregator/CMakeLists.txt
@@ -0,0 +1,43 @@
+
+set( akregatormigrator_SRCS
+ main.cpp
+ akregatormigrator.cpp
+ itemimportreader.cpp
+ importitemsjob.cpp
+ itemsync.cpp
+ ${MIGRATION_AKONADI_SHARED_SOURCES}
+)
+
+include_directories(
+ ${CMAKE_CURRENT_BINARY_DIR}
+)
+
+#macro(add_resource_iface _kcfgFile _ifaceName _className)
+# kcfg_generate_dbus_interface(${kdepim-runtime_SOURCE_DIR}/resources/${_kcfgFile} \
${_ifaceName}) +# string(TOLOWER ${_className} _codeFile)
+# set_source_files_properties( ${CMAKE_CURRENT_BINARY_DIR}/${_ifaceName}.xml \
PROPERTIES INCLUDE "metatype.h") +# qt4_add_dbus_interface(akregatormigrator_SRCS
+# ${CMAKE_CURRENT_BINARY_DIR}/${_ifaceName}.xml ${_codeFile} ${_className}
+# )
+#endmacro(add_resource_iface)
+
+#qt4_add_dbus_adaptor(krsslocalresource_SRCS \
${CMAKE_CURRENT_BINARY_DIR}/org.kde.Akonadi.RssLocal.Settings.xml settings.h \
Settings) +
+#add_resource_iface( maildir/maildirresource.kcfg
+# org.kde.Akonadi.Maildir.Settings MaildirSettings )
+
+qt4_add_dbus_interfaces(akregatormigrator_SRCS \
org.kde.Akonadi.RssLocal.Settings.xml) +
+
+kde4_add_executable( akregatormigrator ${akregatormigrator_SRCS} )
+
+target_link_libraries(akregatormigrator
+ ${QT_QTXML_LIBRARY}
+ ${KDE4_KDECORE_LIBS}
+ ${KDE4_KDEPIMLIBS_LIBS}
+ ${KDEPIMLIBS_AKONADI_LIBS}
+ ${KDEPIMLIBS_SYNDICATION_LIBS}
+ krss
+)
+
+install(TARGETS akregatormigrator ${INSTALL_TARGETS_DEFAULT_ARGS})
diff --git a/migration/akregator/akregatormigrator.cpp \
b/migration/akregator/akregatormigrator.cpp new file mode 100644
index 0000000..400661e
--- /dev/null
+++ b/migration/akregator/akregatormigrator.cpp
@@ -0,0 +1,274 @@
+/*
+ Copyright (c) 2011 Frank Osterfeld <osterfeld@kde.org> (*)
+
+ This library is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or (at your
+ option) any later version.
+
+ This library is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
+ License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to the
+ Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ 02110-1301, USA.
+*/
+
+//Heavily copy/pasted from kjotsmigrator.cpp, (C) 2010 Stephen Kelly \
<steveire@gmail.com> +#include "akregatormigrator.h"
+#include "importitemsjob.h"
+#include "settingsinterface.h"
+
+#include <KConfigGroup>
+#include <KSaveFile>
+#include <KGlobal>
+#include <KLocale>
+#include <KRandom>
+#include <KStandardDirs>
+
+#include <Akonadi/AgentInstance>
+#include <Akonadi/AgentInstanceCreateJob>
+#include <Akonadi/Collection>
+#include <Akonadi/CollectionFetchJob>
+#include <Akonadi/CollectionFetchScope>
+#include <Akonadi/EntityDisplayAttribute>
+#include <akonadi/resourcesynchronizationjob.h>
+
+#include <krss/feedcollection.h>
+#include <krss/item.h>
+
+#include <cerrno>
+#include <functional>
+
+using namespace Akonadi;
+using namespace KRss;
+
+static const char* defaultOpml =
+"<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
+"<opml version=\"1.0\">"
+"<head>"
+"<text>Local RSS Feeds</text>"
+"</head>"
+"<body>"
+"<outline text=\"KDE\">"
+" <outline title=\"KDE Dot News\" version=\"RSS\" description=\"\" \
htmlUrl=\"http://dot.kde.org\" xmlUrl=\"http://www.kde.org/dotkdeorg.rdf\" \
type=\"rss\" text=\"KDE Dot News\"/>" +" <outline title=\"Planet KDE\" \
version=\"RSS\" description=\"Planet KDE - http://planetKDE.org/\" \
htmlUrl=\"http://planetKDE.org/\" xmlUrl=\"http://planetkde.org/rss20.xml\" \
type=\"rss\" text=\"Planet KDE\"/>" +" <outline title=\"Planet KDE PIM\" \
version=\"RSS\" description=\"Planet KDE - http://planetKDE.org/\" \
htmlUrl=\"http://planetKDE.org/\" xmlUrl=\"http://pim.planetkde.org/rss20.xml\" \
type=\"rss\" text=\"Planet KDE PIM\"/>" +" <outline title=\"KDE Apps\" \
version=\"RSS\" description=\"KDE-Look.org: Applications for your KDE-Desktop\" \
htmlUrl=\"http://kde-apps.org\" \
xmlUrl=\"http://www.kde.org/dot/kde-apps-content.rdf\" type=\"rss\" text=\"KDE \
Apps\"/>" +" <outline title=\"KDE Look\"version=\"RSS\" description=\"KDE-Look.org: \
Eyecandy for your KDE-Desktop\" htmlUrl=\"http://kde-look.org\" \
xmlUrl=\"http://www.kde.org/kde-look-content.rdf\" text=\"KDE Look\"/>" +" \
</outline>" +" </body>"
+"</opml>";
+
+
+AkregatorMigrator::AkregatorMigrator()
+ : KMigratorBase()
+{
+}
+
+AkregatorMigrator::~AkregatorMigrator()
+{
+}
+
+void AkregatorMigrator::migrate()
+{
+ emit message( Info, i18n( "Beginning Akregator migration..." ) );
+
+ createAgentInstance( "akonadi_krsslocal_resource", this, \
SLOT(resourceCreated(KJob*)) ); +}
+
+static bool ensureOpmlCreated( const QString& target, QString* err ) {
+ const QString source = KGlobal::dirs()->saveLocation("data", "akregator/data") + \
"feeds.opml"; +
+ //if there is a feeds.opml from Akregator, copy it over
+ if ( QFileInfo( source ).exists() ) {
+ QFile sf( source );
+ if ( !sf.copy( target ) ) {
+ *err = sf.errorString();
+ return false;
+ } else
+ return true;
+ }
+
+ //otherwise, create a new one from the default feed list
+ KSaveFile out( target );
+ if ( !out.open( QIODevice::WriteOnly ) ) {
+ *err = out.errorString();
+ return false;
+ }
+ const qint64 len = strlen( defaultOpml );
+ qint64 written = 0;
+ while ( written < len ) {
+ const qint64 n = out.write( defaultOpml + written, len - written );
+ if ( n < 0 ) {
+ *err = out.errorString();
+ return false;
+ }
+ written += n;
+ }
+ if ( !out.finalize() ) {
+ *err = out.errorString();
+ return false;
+ }
+
+ return true;
+}
+
+void AkregatorMigrator::resourceCreated( KJob *job )
+{
+ if ( job->error() ) {
+ emit message( Error, i18n( "Failed to create resource for RSS feeds: %1", \
job->errorText() ) ); + deleteLater();
+ return;
+ }
+ emit message( Info, i18n( "Created local RSS resource." ) );
+
+ AgentInstance instance = static_cast< AgentInstanceCreateJob* >( job \
)->instance(); +
+ instance.setName( i18nc( "Default name for resource holding RSS feeds", "Local RSS \
Feeds" ) ); +
+ OrgKdeAkonadiRssLocalSettingsInterface *iface = new \
OrgKdeAkonadiRssLocalSettingsInterface( + "org.freedesktop.Akonadi.Resource." + \
instance.identifier(), + "/Settings", QDBusConnection::sessionBus(), this );
+
+ if (!iface->isValid() ) {
+ migrationFailed( i18n("Failed to obtain D-Bus interface for remote \
configuration."), instance ); + delete iface;
+ return;
+ }
+
+ const QString targetPath = KGlobal::dirs()->localxdgdatadir() + "/feeds/" + \
KRandom::randomString( 10 ); +
+ errno = 0;
+ if ( !QDir().mkpath( targetPath ) ) {
+ migrationFailed( i18n("Failed to create folder for resource: %1", \
QString::fromLocal8Bit( strerror( errno ) ) ), instance ); + return;
+ }
+
+ const QString targetFile = targetPath + QLatin1String("/feeds.opml");
+ QString err;
+ if ( !ensureOpmlCreated( targetFile, &err ) ) {
+ migrationFailed( i18n("Failed to create OPML file in %1: %2", targetPath, err ), \
instance ); + return;
+ }
+
+ QDBusPendingReply<void> response = iface->setPath( targetFile );
+
+ // make sure the config is saved
+ iface->writeConfig();
+
+ instance.reconfigure();
+ m_resourceIdentifier = instance.identifier();
+
+ ResourceSynchronizationJob *syncJob = new ResourceSynchronizationJob(instance, \
this); + syncJob->setCollectionTreeOnly( true );
+ connect( syncJob, SIGNAL(result(KJob*)), SLOT(syncDone(KJob*)));
+ syncJob->start();
+}
+
+void AkregatorMigrator::syncDone(KJob *job)
+{
+ if ( job->error() ) {
+ emit message( Error, i18n( "Synchronizing the collection tree failed: %1" , \
job->errorString() ) ); + return;
+ }
+ emit message( Info, i18n( "Instance \"%1\" synchronized" , m_resourceIdentifier ) \
); +
+ CollectionFetchJob *collectionFetchJob = new CollectionFetchJob( \
Collection::root(), CollectionFetchJob::FirstLevel, this ); + connect( \
collectionFetchJob, SIGNAL(result(KJob*)), SLOT(rootCollectionsReceived(KJob*)) ); + \
collectionFetchJob->start(); +}
+
+void AkregatorMigrator::rootCollectionsReceived( KJob* j )
+{
+ CollectionFetchJob * job = qobject_cast<CollectionFetchJob*>( j );
+ if ( job->error() ) {
+ emit message( Error, i18nc( "A job to fetch akonadi collection failed. %1 is the \
error string.", "Fetching root collections failed: %1" , job->errorString() ) ); + \
return; + }
+
+ const Collection::List list = job->collections();
+ foreach( const Collection &collection, list ) {
+ if ( collection.resource() == m_resourceIdentifier ) {
+ m_resourceCollection = collection;
+ emit message( Info, i18n( "New resource is rooted at Collection(%1)", \
collection.id() ) ); + startMigration();
+ return;
+ }
+ }
+ emit message( Error, i18n( "Could not find root collection for resource \"%1\"" \
,m_resourceIdentifier ) ); +}
+
+void AkregatorMigrator::startMigration()
+{
+ CollectionFetchJob* job = new CollectionFetchJob( m_resourceCollection, \
CollectionFetchJob::Recursive, this ); + job->fetchScope().setContentMimeTypes( \
QStringList( KRss::Item::mimeType() ) ); + connect( job, SIGNAL(result(KJob*)), \
this, SLOT(feedCollectionsReceived(KJob*)) ); + connect( job, \
SIGNAL(collectionsReceived(Akonadi::Collection::List)), this, \
SLOT(feedCollectionsReceived(Akonadi::Collection::List)) ); + job->start();
+}
+
+namespace {
+ struct HasNoXmlUrl : public std::unary_function<Collection,bool> {
+ bool operator()( const Collection& c ) const {
+ return FeedCollection( c ).xmlUrl().isEmpty();
+ }
+ };
+}
+void AkregatorMigrator::feedCollectionsReceived( const Collection::List& l_ ) {
+ Collection::List l( l_ );
+ l.erase( std::remove_if( l.begin(), l.end(), HasNoXmlUrl() ), l.end() );
+ m_unmigrated << l;
+}
+
+void AkregatorMigrator::feedCollectionsReceived( KJob* j ) {
+ if ( j->error() ) {
+ emit message( Error, i18n("Failed to retrieve feed collections: %1", \
j->errorText() ) ); + return;
+ }
+ migrateNext();
+}
+
+void AkregatorMigrator::migrateNext()
+{
+ if ( m_unmigrated.isEmpty() ) {
+ migrationFinished();
+ return;
+ }
+
+ const KRss::FeedCollection fc( m_unmigrated.front() );
+ m_unmigrated.pop_front();
+
+ emit message( Info, i18nc( "A migration tool is migrating the RSS feed with title \
%1", "Migrating \"%1\"..." , fc.title() ) ); +
+ ImportItemsJob* job = new ImportItemsJob( fc, this );
+ connect( job, SIGNAL(result(KJob*)), this, SLOT(itemImportDone(KJob*)) );
+ job->start();
+}
+
+void AkregatorMigrator::itemImportDone( KJob* j ) {
+ ImportItemsJob* job = qobject_cast<ImportItemsJob*>( j );
+ Q_ASSERT( job );
+ if ( job->error() )
+ emit message( Error, i18n( "Could not import items for feed \"%1\": %2", \
FeedCollection( job->collection() ).title(), job->errorText() ) ); + else
+ emit message( Success, i18n( "Import done: \"%1\"", FeedCollection( \
job->collection() ).title() ) ); + migrateNext();
+}
+
+void AkregatorMigrator::migrationFinished()
+{
+ emit message( Info, i18n( "Akregator feeds migration finished" ) );
+ deleteLater();
+}
+
+void AkregatorMigrator::migrationFailed( const QString& errorMsg, const \
Akonadi::AgentInstance& instance ) +{
+ Q_UNUSED( instance )
+ emit message( Error, i18n( "Migration failed: %1" ,errorMsg ) );
+}
+
+#include "akregatormigrator.moc"
diff --git a/migration/akregator/akregatormigrator.h \
b/migration/akregator/akregatormigrator.h new file mode 100644
index 0000000..86f8fb6
--- /dev/null
+++ b/migration/akregator/akregatormigrator.h
@@ -0,0 +1,66 @@
+/*
+ Copyright (c) 2011 Frank Osterfeld <osterfeld@kde.org>
+
+ This library is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Library General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or (at your
+ option) any later version.
+
+ This library is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
+ License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to the
+ Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ 02110-1301, USA.
+*/
+
+#ifndef AKREGATORMIGRATOR_H
+#define AKREGATORMIGRATOR_H
+
+#include "kmigratorbase.h"
+
+#include <QDir>
+
+#include <KJob>
+
+#include <akonadi/collection.h>
+#include <akonadi/item.h>
+
+class QDomElement;
+
+class AkregatorMigrator : public KMigratorBase
+{
+ Q_OBJECT
+public:
+ AkregatorMigrator();
+ ~AkregatorMigrator();
+
+ /* reimp */ void migrate();
+ /* reimp */ void migrateNext();
+
+protected:
+ /* reimp */ void migrationFailed( const QString& errorMsg, const \
Akonadi::AgentInstance& instance = Akonadi::AgentInstance() ); +
+private slots:
+ void resourceCreated( KJob *job );
+ void rootCollectionsReceived( KJob* );
+ void feedCollectionsReceived( KJob* job );
+ void feedCollectionsReceived( const Akonadi::Collection::List& );
+ void syncDone(KJob *job);
+ void itemImportDone( KJob* );
+
+private:
+ void migrationFinished();
+ void startMigration();
+
+private:
+ QString m_resourceIdentifier;
+ Akonadi::Collection::List m_unmigrated;
+ Akonadi::Collection m_resourceCollection;
+
+};
+
+#endif
diff --git a/migration/akregator/importitemsjob.cpp \
b/migration/akregator/importitemsjob.cpp new file mode 100644
index 0000000..0246c10
--- /dev/null
+++ b/migration/akregator/importitemsjob.cpp
@@ -0,0 +1,284 @@
+/*
+ Copyright (C) 2009 Frank Osterfeld <osterfeld@kde.org>
+
+ 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "importitemsjob.h"
+#include <krss/rssitem.h>
+#include <krss/feedcollection.h>
+#include "itemsync.h"
+
+#include <Akonadi/Item>
+#include <Akonadi/ItemCreateJob>
+#include <Akonadi/ItemFetchJob>
+
+#include <KLocalizedString>
+#include <KRandom>
+#include <KTemporaryFile>
+
+#include <QFile>
+#include <QString>
+#include <QUrl>
+
+#include <algorithm>
+
+static QString exporterBinaryName() {
+ return QString::fromLatin1( "akregatorstorageexporter" );
+}
+
+static void deleteTempFile( const QString& path ) {
+ if ( !path.isEmpty() && QFile::exists( path ) ) {
+ QFile f( path );
+ if ( !f.remove( path ) )
+ kWarning() << "Could not delete temporary file" << path << \
f.errorString(); + }
+}
+
+static QString generateTmpFileName() {
+ KTemporaryFile file;
+ file.setSuffix( KRandom::randomString( 10 ) );
+ if ( !file.open() )
+ return QString();
+ return file.fileName();
+}
+
+using KRss::RssItem;
+
+ImportItemsJob::ImportItemsJob( const Akonadi::Collection& collection, QObject \
*parent ) + : KJob( parent )
+ , m_exporter( 0 )
+ , m_collection( collection )
+ , m_reader( 0 )
+ , m_pendingCreateJobs( 0 ) {
+}
+
+ImportItemsJob::~ImportItemsJob() {
+ deleteTempFile( m_exportFileName );
+ delete m_reader;
+}
+
+Akonadi::Collection ImportItemsJob::collection() const {
+ return m_collection;
+}
+
+void ImportItemsJob::cleanupAndEmitResult() {
+ delete m_reader;
+ m_reader = 0;
+ m_file.close();
+ emitResult();
+}
+
+void ImportItemsJob::start() {
+ QMetaObject::invokeMethod( this, "doStart", Qt::QueuedConnection );
+}
+
+void ImportItemsJob::doStart() {
+ assert( !m_exporter );
+ assert( m_exportFileName.isEmpty() );
+ m_exporter = new QProcess( this );
+ m_exportFileName = generateTmpFileName();
+ if ( m_exportFileName.isEmpty() || QFile::exists( m_exportFileName ) ) {
+ setError( ImportItemsJob::ExporterError );
+ setErrorText( i18n("Could not generate a temporary file for export." ) );
+ cleanupAndEmitResult();
+ return;
+ }
+
+ m_exporter->setStandardOutputFile( m_exportFileName );
+ connect( m_exporter, SIGNAL(finished(int,QProcess::ExitStatus)),
+ this, SLOT(exporterFinished(int,QProcess::ExitStatus)) );
+ connect( m_exporter, SIGNAL(error(QProcess::ProcessError)),
+ this, SLOT(exporterError(QProcess::ProcessError)) );
+
+ m_exporter->start( exporterBinaryName(), QStringList() << QString::fromLatin1( \
"--base64" ) << QString::fromAscii( QUrl( KRss::FeedCollection( m_collection \
).xmlUrl() ).toEncoded().toBase64() ) ); + if ( !m_exporter->waitForStarted() ) {
+ setError( ImportItemsJob::ExporterError );
+ setErrorText( i18n("Could not start storage exporter %1. Make sure it is \
properly installed.", exporterBinaryName() ) ); + cleanupAndEmitResult();
+ return;
+ }
+}
+
+void ImportItemsJob::exporterError( QProcess::ProcessError error ) {
+ setError( ImportItemsJob::ExporterError );
+ switch ( error ) {
+ case QProcess::FailedToStart:
+ setErrorText( i18n( "Storage exporter failed to start." ) );
+ break;
+ case QProcess::Crashed:
+ setErrorText( i18n( "Storage exporter crashed." ) );
+ break;
+ case QProcess::Timedout:
+ setErrorText( i18n( "Storage exporter communication timed out." ) );
+ break;
+ case QProcess::WriteError:
+ setErrorText( i18n( "Storage exporter write error." ) );
+ break;
+ case QProcess::ReadError:
+ setErrorText( i18n( "Storage exporter read error." ) );
+ break;
+ case QProcess::UnknownError:
+ setErrorText( i18n( "Storage exporter: unknown error" ) );
+ break;
+ }
+ cleanupAndEmitResult();
+}
+
+void ImportItemsJob::exporterFinished( int exitCode, QProcess::ExitStatus status ) {
+ if ( status == QProcess::CrashExit ) {
+ setError( ImportItemsJob::ExporterError );
+ setErrorText( i18n("Item export failed. Storage exporter %1 crashed.", \
exporterBinaryName() ) ); + cleanupAndEmitResult();
+ return;
+ }
+
+ if ( exitCode != 0 ) {
+ setError( ImportItemsJob::ExporterError );
+ setErrorText( i18n("Item export failed. Storage exporter %1 returned exit \
code %2.", exporterBinaryName(), QString::number( exitCode ) ) ); + \
cleanupAndEmitResult(); + return;
+ }
+
+ Akonadi::ItemFetchJob* job = new Akonadi::ItemFetchJob( m_collection, this );
+ //TODO set fetch scope
+ connect( job, SIGNAL(result(KJob*)), this, SLOT(slotItemsFetched(KJob*)) );
+ job->start();
+}
+
+static bool lessByRemoteId( const Akonadi::Item& lhs, const Akonadi::Item& rhs ) {
+ return lhs.remoteId() < rhs.remoteId();
+}
+
+static bool equalByRemoteId( const Akonadi::Item& lhs, const Akonadi::Item& rhs ) {
+ return lhs.remoteId() == rhs.remoteId();
+}
+
+void ImportItemsJob::slotItemsFetched( KJob* j ) {
+ const Akonadi::ItemFetchJob* const job = qobject_cast<const \
Akonadi::ItemFetchJob*>( j ); + assert( job );
+ if ( job->error() ) {
+ setError( ItemSyncFailed );
+ setErrorText( i18n( "Could not retrieve existing items for syncing: %1", \
job->errorString() ) ); + cleanupAndEmitResult();
+ return;
+ }
+
+ m_existingItems = job->items();
+ std::sort( m_existingItems.begin(), m_existingItems.end(), lessByRemoteId );
+ startImport();
+}
+
+void ImportItemsJob::startImport() {
+ m_file.setFileName( m_exportFileName );
+ if ( !m_file.open( QIODevice::ReadOnly ) ) {
+ setError( CouldNotOpenFile );
+ setErrorText( i18n("Could not open file %1 for reading: %2", \
m_exportFileName, m_file.errorString() ) ); + cleanupAndEmitResult();
+ return;
+ }
+ m_reader = new ItemImportReader( &m_file );
+ readBatch();
+}
+
+void ImportItemsJob::readBatch() {
+ int num = 500;
+
+ Akonadi::Item::List items;
+
+ while ( num > 0 && m_reader->hasNext() ) {
+ const Akonadi::Item item = m_reader->nextItem();
+ if ( !item.remoteId().isEmpty() )
+ items.append( item );
+ --num;
+ }
+
+ if ( items.isEmpty() ) {
+ cleanupAndEmitResult();
+ return;
+ }
+
+ syncItems( items );
+}
+
+void ImportItemsJob::syncItems( const Akonadi::Item::List& items_ ) {
+ Q_ASSERT( !items_.isEmpty() );
+#if 0
+ std::sort( items.begin(), items.end(), lessByRemoteId );
+
+ //split into existing items (must be merged) and new items (will be just added)
+ //Akonadi::Item::List toMerge;
+ Akonadi::Item::List toAdd;
+ Akonadi::Item::List::const_iterator itRead = items.constBegin();
+
+ if ( m_existingItems.isEmpty() )
+ toAdd = items;
+#endif
+
+#if 0
+ else {
+ Akonadi::Item::List::const_iterator itExisting = \
m_existingItems.constBegin(); + bool foundLast = false;
+ for ( ; itRead != items.constEnd(); ++itRead ) {
+ if ( foundLast ) {
+ toAdd.push_back( *itRead );
+ continue;
+ }
+ itExisting = std::lower_bound( itExisting, m_existingItems.constEnd(), \
*itRead, lessByRemoteId ); + foundLast = itExisting == items.constEnd();
+ if ( foundLast ) {
+ toAdd.push_back( *itRead );
+ continue;
+ }
+
+ if ( equalByRemoteId( *itExisting, *itRead ) )
+ toMerge.push_back( *itRead );
+ else
+ toAdd.push_back( *itRead );
+ }
+ }
+ //TODO: merge flags etc. for existing items
+
+ if ( toAdd.isEmpty() ) {
+ QMetaObject::invokeMethod( this, "readBatch", Qt::QueuedConnection );
+ return;
+ }
+
+ Q_FOREACH( const Akonadi::Item& item, items_/*toAdd*/ ) {
+ Akonadi::ItemCreateJob* const job = new Akonadi::ItemCreateJob( item, \
m_collection ); + ++m_pendingCreateJobs;
+ QObject::connect(job, SIGNAL(result(KJob*)), this, SLOT(syncDone(KJob*)) );
+ job->start();
+ }
+#endif
+ RssItemSync * const syncer = new RssItemSync( m_collection );
+ syncer->setIncrementalSyncItems( items_, Akonadi::Item::List() );
+ syncer->setSynchronizeFlags( false );
+ connect( syncer, SIGNAL( result( KJob* ) ), this, SLOT( syncDone( KJob* ) ) );
+ ++m_pendingCreateJobs;
+ syncer->start();
+}
+
+void ImportItemsJob::syncDone( KJob* job ) {
+ if ( job->error() ) {
+ setError( ItemSyncFailed );
+ setErrorText( job->errorText() );
+ cleanupAndEmitResult();
+ return;
+ }
+ assert( m_pendingCreateJobs > 0 );
+ --m_pendingCreateJobs;
+ if ( m_pendingCreateJobs == 0 )
+ readBatch();
+}
diff --git a/migration/akregator/importitemsjob.h \
b/migration/akregator/importitemsjob.h new file mode 100644
index 0000000..c55886d
--- /dev/null
+++ b/migration/akregator/importitemsjob.h
@@ -0,0 +1,74 @@
+/*
+ Copyright (C) 2009 Frank Osterfeld <osterfeld@kde.org>
+
+ 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef IMPORTITEMSJOB_H
+#define IMPORTITEMSJOB_H
+
+#include "itemimportreader.h"
+
+#include <akonadi/item.h>
+#include <akonadi/collection.h>
+
+#include <KJob>
+
+#include <QFile>
+#include <QProcess>
+#include <QString>
+
+class ImportItemsJob : public KJob {
+ Q_OBJECT
+public:
+
+ enum Error {
+ ExporterError = KJob::UserDefinedError,
+ CouldNotOpenFile,
+ ItemSyncFailed
+ };
+
+ explicit ImportItemsJob( const Akonadi::Collection&, QObject *parent = 0 );
+ ~ImportItemsJob();
+
+ Akonadi::Collection collection() const;
+
+ void setFlagsSynchronizable( bool flagsSynchronizable );
+
+ /* reimp */ void start();
+
+private:
+ void syncItems( const Akonadi::Item::List& items );
+ void cleanupAndEmitResult();
+ void startImport();
+
+private Q_SLOTS:
+ void doStart();
+ void exporterError( QProcess::ProcessError error );
+ void exporterFinished( int exitCode, QProcess::ExitStatus status );
+ void slotItemsFetched( KJob* job );
+ void readBatch();
+ void syncDone( KJob* );
+
+private:
+ QProcess* m_exporter;
+ QString m_exportFileName;
+ Akonadi::Collection m_collection;
+ QFile m_file;
+ QList<Akonadi::Item> m_existingItems;
+ ItemImportReader* m_reader;
+ int m_pendingCreateJobs;
+};
+
+#endif // IMPORTITEMSJOB_H
diff --git a/migration/akregator/itemimportreader.cpp \
b/migration/akregator/itemimportreader.cpp new file mode 100644
index 0000000..79c01b7
--- /dev/null
+++ b/migration/akregator/itemimportreader.cpp
@@ -0,0 +1,303 @@
+/*
+ Copyright (C) 2009 Frank Osterfeld <osterfeld@kde.org>
+
+ 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "itemimportreader.h"
+
+#include <krss/category.h>
+#include <krss/enclosure.h>
+#include <krss/rssitem.h>
+#include <krss/item.h>
+#include <krss/person.h>
+
+#include <syndication/atom/constants.h>
+#include <syndication/constants.h>
+
+#include <Akonadi/Item>
+#include <KDateTime>
+
+#include <QXmlStreamReader>
+#include <QVariant>
+
+namespace {
+
+enum TextMode {
+ PlainText,
+ Html
+};
+
+static QString akregatorNamespace() {
+ return QLatin1String("http://akregator.kde.org/StorageExporter#");
+}
+
+class Element
+{
+public:
+ Element( const QString& ns_, const QString& name_, const QVariant& defaultValue_ \
= QVariant() ) : ns( ns_ ), name( name_ ), qualifiedName( ns + QLatin1Char(':') + \
name ), defaultValue( defaultValue_ ) + {
+ }
+
+ const QString ns;
+ const QString name;
+ const QString qualifiedName;
+ const QVariant defaultValue;
+
+ bool isNextIn( const QXmlStreamReader& reader ) const
+ {
+ return reader.isStartElement() && reader.name() == name && \
reader.namespaceUri() == ns; + }
+};
+
+struct Elements
+{
+ Elements() : atomNS( Syndication::Atom::atom1Namespace() ),
+ akregatorNS( akregatorNamespace() ),
+ commentNS( Syndication::commentApiNamespace() ),
+ title( atomNS, QLatin1String("title"), QString() ),
+ summary( atomNS, QLatin1String("summary"), QString() ),
+ content( atomNS, QLatin1String("content"), QString() ),
+ link( atomNS, QLatin1String("link"), QString() ),
+ language( atomNS, QLatin1String("language"), QString() ),
+ guid( atomNS, QLatin1String("id"), QString() ),
+ published( atomNS, QLatin1String("published"), \
KDateTime().toString( KDateTime::ISODate ) ), + updated( atomNS, \
QLatin1String("updated"), KDateTime().toString( KDateTime::ISODate ) ), + \
commentsCount( Syndication::slashNamespace(), QLatin1String("comments"), -1 ), + \
commentsFeed( commentNS, QLatin1String("commentRss"), QString() ), + \
commentPostUri( commentNS, QLatin1String("comment"), QString() ), + \
commentsLink( akregatorNS, QLatin1String("commentsLink"), QString() ), + \
hash( akregatorNS, QLatin1String("hash"), 0 ), + guidIsHash( \
akregatorNS, QLatin1String("idIsHash"), false ), + sourceFeedId( \
akregatorNS, QLatin1String("sourceFeedId"), -1 ), + name( atomNS, \
QLatin1String("name"), QString() ), + uri( atomNS, \
QLatin1String("uri"), QString() ), + email( atomNS, \
QLatin1String("email"), QString() ), + author( atomNS, \
QLatin1String("author"), QString() ), + category( atomNS, \
QLatin1String("category"), QString() ), + entry( atomNS, \
QLatin1String("entry"), QString() ), + readStatus( akregatorNS, \
QLatin1String("readStatus") ), + deleted( akregatorNS, \
QLatin1String("deleted") ), + important( akregatorNS, \
QLatin1String("important") ) +{}
+ const QString atomNS;
+ const QString akregatorNS;
+ const QString commentNS;
+ const Element title;
+ const Element summary;
+ const Element content;
+ const Element link;
+ const Element language;
+ const Element guid;
+ const Element published;
+ const Element updated;
+ const Element commentsCount;
+ const Element commentsFeed;
+ const Element commentPostUri;
+ const Element commentsLink;
+ const Element hash;
+ const Element guidIsHash;
+ const Element sourceFeedId;
+ const Element name;
+ const Element uri;
+ const Element email;
+ const Element author;
+ const Element category;
+ const Element entry;
+ const Element readStatus;
+ const Element deleted;
+ const Element important;
+ static const Elements instance;
+};
+
+const Elements Elements::instance;
+
+static void readLink( KRss::RssItem& item, QXmlStreamReader& reader )
+{
+ const QXmlStreamAttributes attrs = reader.attributes();
+ const QString rel = attrs.value( QString(), QLatin1String("rel") ).toString();
+ if ( rel == QLatin1String("alternate") )
+ {
+ item.setLink( attrs.value( QString(), QLatin1String("href") ).toString() );
+ }
+ else if ( rel == QLatin1String("enclosure") )
+ {
+ KRss::Enclosure enc;
+ enc.setUrl( attrs.value( QString(), QLatin1String("href") ).toString() );
+ enc.setType( attrs.value( QString(), QLatin1String("type") ).toString() );
+ enc.setTitle( attrs.value( QString(), QLatin1String("title") ).toString() );
+ bool ok;
+ const uint length = attrs.value( QString(), QLatin1String("length") \
).toString().toUInt( &ok ); + if ( ok )
+ enc.setLength( length );
+ const uint duration = attrs.value( Syndication::itunesNamespace(), \
QLatin1String("duration") ).toString().toUInt( &ok ); + if ( ok )
+ enc.setDuration( duration );
+ QList<KRss::Enclosure> encs = item.enclosures();
+ encs.append( enc );
+ item.setEnclosures( encs );
+ }
+}
+
+static void readAuthor( KRss::RssItem& item, QXmlStreamReader& reader )
+{
+ KRss::Person author;
+ int depth = 1;
+ while ( !reader.atEnd() && depth > 0 )
+ {
+ reader.readNext();
+ if ( reader.isEndElement() )
+ --depth;
+ else if ( reader.isStartElement() )
+ {
+ if ( Elements::instance.name.isNextIn( reader ) )
+ author.setName( reader.readElementText() );
+ else if ( Elements::instance.uri.isNextIn( reader ) )
+ author.setUri( reader.readElementText() );
+ else if ( Elements::instance.email.isNextIn( reader ) )
+ author.setEmail( reader.readElementText() );
+ }
+
+ }
+ QList<KRss::Person> authors = item.authors();
+ authors.append( author );
+ item.setAuthors( authors );
+}
+
+static void readCategory( KRss::RssItem& item, QXmlStreamReader& reader )
+{
+ const QXmlStreamAttributes attrs = reader.attributes();
+ KRss::Category cat;
+ cat.setTerm( attrs.value( QString(), QLatin1String("term") ).toString() );
+ cat.setScheme( attrs.value( QString(), QLatin1String("scheme") ).toString() );
+ cat.setLabel( attrs.value( QString(), QLatin1String("label") ).toString() );
+ QList<KRss::Category> cats = item.categories();
+ cats.append( cat );
+ item.setCategories( cats );
+}
+
+static bool readItem( Akonadi::Item& akonadiItem, QXmlStreamReader& reader ) {
+ const Elements& el = Elements::instance;
+ if ( !reader.atEnd() )
+ reader.readNext();
+
+ Akonadi::Item::Flags flags;
+ KRss::RssItem item;
+ item.setHeadersLoaded( true );
+ item.setContentLoaded( true );
+
+ while ( !reader.atEnd() && !el.entry.isNextIn( reader ) )
+ {
+ if ( reader.isStartElement() )
+ {
+ if ( el.title.isNextIn( reader ) )
+ item.setTitle( reader.readElementText() );
+ else if ( el.summary.isNextIn( reader ) )
+ item.setDescription( reader.readElementText() );
+ else if ( el.content.isNextIn( reader ) )
+ item.setContent( reader.readElementText() );
+ else if ( el.language.isNextIn( reader ) )
+ item.setLanguage( reader.readElementText() );
+ else if ( el.guid.isNextIn( reader ) )
+ item.setGuid( reader.readElementText() );
+ else if ( el.hash.isNextIn( reader ) )
+ item.setHash( reader.readElementText().toInt() );
+ else if ( el.guidIsHash.isNextIn( reader ) )
+ item.setGuidIsHash( QVariant( reader.readElementText() ).toBool() );
+ else if ( el.sourceFeedId.isNextIn( reader ) )
+ item.setSourceFeedId( reader.readElementText().toInt() );
+ else if ( el.commentsLink.isNextIn( reader ) )
+ item.setCommentsLink( reader.readElementText() );
+ else if ( el.commentPostUri.isNextIn( reader ) )
+ item.setCommentPostUri( reader.readElementText() );
+ else if ( el.commentsCount.isNextIn( reader ) )
+ item.setCommentsCount( reader.readElementText().toInt() );
+ else if ( el.commentsFeed.isNextIn( reader ) )
+ item.setCommentsFeed( reader.readElementText() );
+ else if ( el.link.isNextIn( reader ) )
+ readLink( item, reader );
+ else if ( el.author.isNextIn( reader ) )
+ readAuthor( item, reader );
+ else if ( el.category.isNextIn( reader ) )
+ readCategory( item, reader );
+ else if ( el.published.isNextIn( reader ) )
+ item.setDatePublished( KDateTime::fromString( \
reader.readElementText(), KDateTime::ISODate ) ); + else if ( \
el.updated.isNextIn( reader ) ) + item.setDateUpdated( \
KDateTime::fromString( reader.readElementText(), KDateTime::ISODate ) ); + \
else if ( el.readStatus.isNextIn( reader ) ) { + const QString \
statusStr = reader.readElementText(); + if ( statusStr != \
QLatin1String("new") && statusStr != QLatin1String("unread") ) + \
flags.insert( KRss::RssItem::flagRead() ); + } else if ( \
el.important.isNextIn( reader ) ) { + if ( reader.readElementText() == \
QLatin1String("true") ) + flags.insert( \
KRss::RssItem::flagImportant() ); + } else if ( el.deleted.isNextIn( \
reader ) ) { + if ( reader.readElementText() == QLatin1String("true") \
) + flags.insert( KRss::RssItem::flagDeleted() );
+ }
+
+ }
+
+ reader.readNext();
+ }
+
+ akonadiItem.setPayload<KRss::RssItem>( item );
+ akonadiItem.setMimeType( KRss::Item::mimeType() );
+ akonadiItem.setRemoteId( item.guid() );
+ akonadiItem.setFlags( flags );
+ return !reader.hasError();
+}
+
+} // namespace
+
+ItemImportReader::ItemImportReader( QIODevice* dev ) : m_reader( new \
QXmlStreamReader( dev ) ) { + m_reader->setNamespaceProcessing( true );
+ m_firstItemFound = findFirstItem();
+}
+
+ItemImportReader::~ItemImportReader() {
+ delete m_reader;
+}
+
+bool ItemImportReader::hasNext() const {
+ return m_firstItemFound && !m_reader->atEnd();
+}
+
+Akonadi::Item ItemImportReader::nextItem() {
+ const Elements el;
+ if ( !m_firstItemFound )
+ return Akonadi::Item();
+
+ while ( !m_reader->atEnd() )
+ if ( m_reader->isStartElement() &&
+ el.entry.isNextIn( *m_reader ) ) {
+ Akonadi::Item item;
+ if ( readItem( item, *m_reader ) )
+ return item;
+ else
+ return Akonadi::Item();
+ }
+ return Akonadi::Item();
+}
+
+bool ItemImportReader::findFirstItem() {
+ const Elements el;
+ bool itemFound = false;
+ while ( !itemFound && !m_reader->atEnd() ) {
+ m_reader->readNext();
+ if ( m_reader->isStartElement() && el.entry.isNextIn( *m_reader ) )
+ itemFound = true;
+ }
+ return itemFound;
+}
diff --git a/migration/akregator/itemimportreader.h \
b/migration/akregator/itemimportreader.h new file mode 100644
index 0000000..2e31433
--- /dev/null
+++ b/migration/akregator/itemimportreader.h
@@ -0,0 +1,48 @@
+/*
+ Copyright (C) 2009 Frank Osterfeld <osterfeld@kde.org>
+
+ 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef ITEMIMPORTREADER_H
+#define ITEMIMPORTREADER_H
+
+#include <QString>
+#include <QXmlStreamReader>
+
+class QIODevice;
+
+namespace Akonadi {
+ class Item;
+}
+
+class ItemImportReader {
+public:
+ explicit ItemImportReader( QIODevice* dev );
+ ~ItemImportReader();
+
+ bool hasNext() const;
+
+ Akonadi::Item nextItem();
+
+private:
+ bool findFirstItem();
+
+private:
+ QXmlStreamReader* m_reader;
+ QString m_sourceFeed;
+ bool m_firstItemFound;
+};
+
+#endif // ITEMIMPORTREADER_H
diff --git a/migration/akregator/itemsync.cpp b/migration/akregator/itemsync.cpp
new file mode 100644
index 0000000..beeb3ee
--- /dev/null
+++ b/migration/akregator/itemsync.cpp
@@ -0,0 +1,86 @@
+/*
+ Copyright (C) 2008 Dmitry Ivanov <vonami@gmail.com>
+
+ 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "itemsync.h"
+
+#include <krss/rssitem.h>
+#include <krss/item.h>
+
+#include <KDebug>
+
+RssItemSync::RssItemSync( const Akonadi::Collection &collection, QObject *parent )
+ : ItemSync( collection, parent ), m_synchronizeFlags( false )
+{
+}
+
+void RssItemSync::setSynchronizeFlags( bool synchronizeFlags )
+{
+ m_synchronizeFlags = synchronizeFlags;
+}
+
+bool RssItemSync::synchronizeFlags() const
+{
+ return m_synchronizeFlags;
+}
+
+bool RssItemSync::updateItem( const Akonadi::Item &storedItem, Akonadi::Item \
&newItem ) +{
+ if ( storedItem.mimeType() != KRss::Item::mimeType() ||
+ newItem.mimeType() != KRss::Item::mimeType() ) {
+ kWarning() << "Either storedItem or newItem doesn't have mimetype \
\'application/rss+xml\'"; + kWarning() << "Remote id:" << \
storedItem.remoteId(); + return false;
+ }
+
+ if ( !storedItem.hasPayload() || !newItem.hasPayload() ) {
+ kWarning() << "Either storedItem or newItem doesn't have payload";
+ kWarning() << "Remote id:" << storedItem.remoteId();
+ return false;
+ }
+
+ const KRss::RssItem newRssItem = newItem.payload<KRss::RssItem>();
+ const int newHash = newRssItem.hash();
+ const int storedHash = storedItem.payload<KRss::RssItem>().hash();
+
+ if ( !newRssItem.guidIsHash() && storedHash != newHash ) {
+ kDebug() << "The article's content is updated:" << newItem.remoteId();
+ // dont overwrite the existing flags
+ // and set 'New'
+ newItem.setFlags( storedItem.flags() );
+ return true;
+ }
+
+ if ( m_synchronizeFlags ) {
+ const bool readFlagsDiffer = storedItem.hasFlag( KRss::RssItem::flagRead() ) \
!= + newItem.hasFlag( KRss::RssItem::flagRead() \
); + const bool importantFlagsDiffer = storedItem.hasFlag( \
KRss::RssItem::flagImportant() ) != + \
newItem.hasFlag( KRss::RssItem::flagImportant() ); +
+ // either 'Seen' or 'Important' was changed in the backend
+ // push the item to Akonadi clearing 'New'
+ if ( readFlagsDiffer || importantFlagsDiffer ) {
+ kDebug() << "The article's flags are updated:" << newItem.remoteId();
+ // We need to explicitely overwrite the item's flags
+ // otherwise Akonadi::ItemModifyJob just ignores them,
+ // see Akonadi::ItemModifyJob::doStart()
+ newItem.setFlags( newItem.flags() );
+ return true;
+ }
+ }
+
+ return false;
+}
diff --git a/migration/akregator/itemsync.h b/migration/akregator/itemsync.h
new file mode 100644
index 0000000..3dbb558
--- /dev/null
+++ b/migration/akregator/itemsync.h
@@ -0,0 +1,38 @@
+/*
+ Copyright (C) 2008 Dmitry Ivanov <vonami@gmail.com>
+
+ 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef ITEMSYNC_H
+#define ITEMSYNC_H
+
+#include <akonadi/itemsync.h>
+
+class RssItemSync : public Akonadi::ItemSync
+{
+public:
+ explicit RssItemSync( const Akonadi::Collection& collection, QObject *parent = 0 \
); +
+ void setSynchronizeFlags( bool synchronizeFlags );
+ bool synchronizeFlags() const;
+
+protected:
+ bool updateItem( const Akonadi::Item& storedItem, Akonadi::Item& newItem );
+
+private:
+ bool m_synchronizeFlags;
+};
+
+#endif // ITEMSYNC_h
diff --git a/migration/akregator/main.cpp b/migration/akregator/main.cpp
new file mode 100644
index 0000000..eceb915
--- /dev/null
+++ b/migration/akregator/main.cpp
@@ -0,0 +1,84 @@
+/*
+ Copyright (c) 2011 Frank Osterfeld <osterfeld@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include "akregatormigrator.h"
+
+#include "infodialog.h"
+
+#include <akonadi/control.h>
+
+#include <krss/feedcollection.h>
+
+#include <KAboutData>
+#include <KApplication>
+#include <KCmdLineArgs>
+#include <KGlobal>
+#include <KDebug>
+
+
+int main( int argc, char **argv )
+{
+ KAboutData aboutData( "akregatormigrator", 0,
+ ki18n( "Akregator Migration Tool" ),
+ "0.1",
+ ki18n( "Migration of Akregator feed to Akonadi" ),
+ KAboutData::License_LGPL,
+ ki18n( "(c) 2011 the Akonadi developers" ),
+ KLocalizedString(),
+ "http://pim.kde.org/akonadi/" );
+ aboutData.setProgramIconName( "akonadi" );
+ aboutData.addAuthor( ki18n( "Frank Osterfeld" ), ki18n( "Author" ), \
"osterfeld@kde.org" ); +
+ KCmdLineArgs::init( argc, argv, &aboutData );
+ KCmdLineOptions options;
+ options.add( "interactive", ki18n( "Show reporting dialog" ) );
+ options.add( "interactive-on-change", ki18n( "Show report only if changes were \
made" ) ); + KCmdLineArgs::addCmdLineOptions( options );
+ KCmdLineArgs *args = KCmdLineArgs::parsedArgs();
+
+ KApplication *app = new KApplication();
+ app->setQuitOnLastWindowClosed( false );
+
+ KGlobal::setAllowQuit( true );
+ KGlobal::locale()->insertCatalog( "libakonadi" );
+
+ if ( !Akonadi::Control::start( 0 ) )
+ return 2;
+
+ InfoDialog *infoDialog = 0;
+ if ( args->isSet( "interactive" ) || args->isSet( "interactive-on-change" ) ) {
+ infoDialog = new InfoDialog( args->isSet( "interactive-on-change" ) );
+ infoDialog->show();
+ }
+ args->clear();
+
+ KRss::FeedCollection::registerAttributes();
+ AkregatorMigrator *migrator = new AkregatorMigrator;
+ if ( infoDialog && migrator ) {
+ infoDialog->migratorAdded();
+ QObject::connect( migrator, SIGNAL(message(KMigratorBase::MessageType,QString)),
+ infoDialog, SLOT(message(KMigratorBase::MessageType,QString)) \
); + QObject::connect( migrator, SIGNAL(destroyed()), infoDialog, \
SLOT(migratorDone()) ); + }
+
+ const int result = app->exec();
+ if ( InfoDialog::hasError() )
+ return 3;
+ return result;
+}
diff --git a/migration/akregator/org.kde.Akonadi.RssLocal.Settings.xml \
b/migration/akregator/org.kde.Akonadi.RssLocal.Settings.xml new file mode 100644
index 0000000..f11fc17
--- /dev/null
+++ b/migration/akregator/org.kde.Akonadi.RssLocal.Settings.xml
@@ -0,0 +1,2 @@
+<?xml version="1.0"?>
+<node xmlns="http://www.kde.org/standards/kcfg/1.0" \
xmlns:kcfg="http://www.kde.org/standards/kcfg/1.0"><interface \
name="org.kde.Akonadi.RssLocal.Settings"><method name="writeConfig"/><method \
name="path"><arg direction="out" type="s"/></method><method name="setPath"><arg \
direction="in" identifier="value" type="s"/></method><method name="readOnly"><arg \
direction="out" type="b"/></method><method name="setReadOnly"><arg direction="in" \
identifier="value" type="b"/></method></interface></node>
[prev in list] [next in list] [prev in thread] [next in thread]
Configure |
About |
News |
Add a list |
Sponsored by KoreLogic