[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