[prev in list] [next in list] [prev in thread] [next in thread]
List: kde-commits
Subject: [kdepim/akregator_port] krsslocal: writeFeedsToOpml now preserves folder hierarchy. NEED POLISHING
From: Alessandro Cosentino <cosenal () gmail ! com>
Date: 2012-02-20 21:25:55
Message-ID: 20120220212555.B6E37A60ED () git ! kde ! org
[Download RAW message or body]
Git commit 1251b3f9e7420942f78a66399f5bed48f5a21ef0 by Alessandro Cosentino.
Committed on 09/09/2011 at 20:07.
Pushed by cgiboudeaux into branch 'akregator_port'.
writeFeedsToOpml now preserves folder hierarchy. NEED POLISHING
M +1 -0 krsslocal/CMakeLists.txt
M +44 -18 krsslocal/krsslocalresource.cpp
M +19 -3 krsslocal/krsslocalresource.h
A +499 -0 krsslocal/opmlparser.cpp [License: GPL (v2+)]
A +149 -0 krsslocal/opmlparser.h [License: GPL (v2+)]
M +31 -8 krsslocal/util.cpp
M +5 -2 krsslocal/util.h
http://commits.kde.org/kdepim/1251b3f9e7420942f78a66399f5bed48f5a21ef0
diff --git a/krsslocal/CMakeLists.txt b/krsslocal/CMakeLists.txt
index f8a8ec3..03d3218 100644
--- a/krsslocal/CMakeLists.txt
+++ b/krsslocal/CMakeLists.txt
@@ -48,6 +48,7 @@ set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${KDE4_ENABLE_EXCEPTIONS}" \
) set( krsslocalresource_SRCS
krsslocalresource.cpp
util.cpp
+ opmlparser.cpp
)
install( FILES krsslocalresource.desktop DESTINATION \
"${CMAKE_INSTALL_PREFIX}/share/akonadi/agents" )
diff --git a/krsslocal/krsslocalresource.cpp b/krsslocal/krsslocalresource.cpp
index 9be5cfb..7e868c9 100644
--- a/krsslocal/krsslocalresource.cpp
+++ b/krsslocal/krsslocalresource.cpp
@@ -1,3 +1,20 @@
+/*
+ Copyright (C) 2011 Alessandro Cosentino <cosenal@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 "krsslocalresource.h"
#include "settings.h"
#include "settingsadaptor.h"
@@ -8,9 +25,9 @@
#include <KDebug>
#include <KStandardDirs>
#include <KLocale>
+#include <KSaveFile>
#include <QtXml/QXmlStreamReader>
#include <QtXml/QXmlStreamWriter>
-#include <QMessageBox>
#include <Akonadi/EntityDisplayAttribute>
#include <Akonadi/ItemFetchJob>
#include <Akonadi/ItemFetchScope>
@@ -20,6 +37,8 @@
#include <Akonadi/CollectionFetchScope>
#include <krss/rssitem.h>
#include <krssresource/krssresource_export.h>
+#include <krss/resourcemanager.h>
+#include <krss/feedcollection.h>
using namespace Akonadi;
using namespace KRssResource;
@@ -35,6 +54,8 @@ KRssLocalResource::KRssLocalResource( const QString &id )
//policy.setCacheTimeout( CACHE_TIMEOUT );
//policy.setIntervalCheckTime( INTERVAL_CHECK_TIME );
+ KRss::ResourceManager::registerAttributes();
+
policy.setInheritFromParent( false );
policy.setSyncOnDemand( false );
policy.setLocalParts( QStringList() << KRss::Item::HeadersPart << \
KRss::Item::ContentPart ); @@ -57,6 +78,7 @@ KRssLocalResource::KRssLocalResource( \
const QString &id )
KRssLocalResource::~KRssLocalResource()
{
+ delete writeBackTimer;
}
QString KRssLocalResource::mimeType()
@@ -79,7 +101,7 @@ void KRssLocalResource::retrieveCollections()
QFile file( path );
/* If we can't open it, let's show an error message. */
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
- error(i18n("Couldn't open ") + path);
+ error(i18n("Could not open %1: %2", path, file.errorString()) );
return;
}
@@ -120,6 +142,8 @@ void KRssLocalResource::retrieveCollections()
list = buildCollectionTree(parser.topLevelNodes(), list, top);
collectionsRetrieved( list );
+
+ file.close();
}
@@ -277,7 +301,6 @@ void KRssLocalResource::collectionChanged(const \
Akonadi::Collection& collection) }
void KRssLocalResource::fetchCollections() {
- // fetching all collections containing rss feeds recursively, starting at the root \
collection CollectionFetchJob *job = new CollectionFetchJob( Collection::root(), \
CollectionFetchJob::Recursive, this ); job->fetchScope().setContentMimeTypes( \
QStringList() << mimeType() ); connect( job, SIGNAL( result( KJob* ) ), SLOT( \
fetchCollectionsFinished( KJob* ) ) ); @@ -291,29 +314,32 @@ void \
KRssLocalResource::fetchCollectionsFinished(KJob *job) { }
CollectionFetchJob *fetchJob = qobject_cast<CollectionFetchJob*>( job );
-
- const Collection::List collections = fetchJob->collections();
+ Collection::List collections = fetchJob->collections();
+ writeFeedsToOpml( Settings::self()->path(), Util::parsedDescendants( collections, \
Akonadi::Collection::root() ) );
- const QString path = Settings::self()->path();
- writeFeedsToOpml( path, collections );
-
}
-void KRssLocalResource::writeFeedsToOpml(const QString &path, const \
QList<Akonadi::Collection>& feeds) +void KRssLocalResource::writeFeedsToOpml(const \
QString &path, const QList<boost::shared_ptr< const ParsedNode> >& nodes) {
- QFile file( path );
- /* If we can't open it, let's show an error message. */
- if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
- error(i18n("Couldn't open ") + path);
- return;
- }
-
+
+ KSaveFile file( path );
+ if ( !file.open( QIODevice::ReadWrite | QIODevice::Text ) ) {
+ error( i18n("Could not open %1: %2", path, file.errorString()) );
+ return;
+ }
+
QXmlStreamWriter writer( &file );
writer.setAutoFormatting( true );
writer.writeStartDocument();
- OpmlWriter::writeOpml( writer, Util::toParsedFeedList( feeds ));
+ OpmlWriter::writeOpml( writer, nodes, QLatin1String("test_title")); //TODO: \
replace with the actual title writer.writeEndDocument();
-
+
+ if ( !file.finalize() ) {
+ error( i18n("Could not save %1: %2", path, file.errorString()) );
+ }
+ file.close();
+ return;
+
}
diff --git a/krsslocal/krsslocalresource.h b/krsslocal/krsslocalresource.h
index b3f2b0b..26f24de 100644
--- a/krsslocal/krsslocalresource.h
+++ b/krsslocal/krsslocalresource.h
@@ -1,12 +1,29 @@
+/*
+ Copyright (C) 2011 Alessandro Cosentino <cosenal@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 KRSSLOCALRESOURCE_H
#define KRSSLOCALRESOURCE_H
#include <Akonadi/CachePolicy>
#include <Akonadi/ResourceBase>
#include <boost/shared_ptr.hpp>
-#include <krssresource/opmlparser.h>
#include <Syndication/Syndication>
#include <QTimer>
+#include "opmlparser.h"
class KRssLocalResource : public Akonadi::ResourceBase,
public Akonadi::AgentBase::Observer
@@ -19,7 +36,7 @@ class KRssLocalResource : public Akonadi::ResourceBase,
Akonadi::Collection::List buildCollectionTree( QList<boost::shared_ptr<const \
KRssResource::ParsedNode> > listOfNodes, Akonadi::Collection::List &list, \
Akonadi::Collection &parent); QString mimeType();
- void writeFeedsToOpml(const QString &path, const QList<Akonadi::Collection>& \
feeds); + void writeFeedsToOpml(const QString &path, const \
QList<boost::shared_ptr<const KRssResource::ParsedNode> >& nodes);
public Q_SLOTS:
virtual void configure( WId windowId );
@@ -46,7 +63,6 @@ class KRssLocalResource : public Akonadi::ResourceBase,
private:
Akonadi::CachePolicy policy;
- //QList<Syndication::ItemPtr> m_syndItems;
QTimer *writeBackTimer;
static const int CACHE_TIMEOUT = -1, INTERVAL_CHECK_TIME = 5;
static const int WRITE_BACK_TIMEOUT = 30000; // in milliseconds
diff --git a/krsslocal/opmlparser.cpp b/krsslocal/opmlparser.cpp
new file mode 100644
index 0000000..4cc1b6a
--- /dev/null
+++ b/krsslocal/opmlparser.cpp
@@ -0,0 +1,499 @@
+/*
+ Copyright (C) 2009 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 "opmlparser.h"
+
+#include <krss/feedcollection.h>
+
+#include <KLocale>
+#include <KDebug>
+#include <QtXml/QXmlStreamWriter>
+#include <QtXml/QXmlStreamReader>
+#include <QtXml/QXmlAttributes>
+#include <memory>
+
+using namespace boost;
+using namespace KRssResource;
+
+class ParsedNode::Private {
+public:
+ QString title;
+ QHash<QString, QString> attributes;
+ weak_ptr<ParsedFolder> parent;
+};
+
+ParsedNode::ParsedNode() : d( new Private ) {
+}
+
+weak_ptr<ParsedFolder> ParsedNode::parent() const {
+ return d->parent;
+}
+
+void ParsedNode::setParent( const weak_ptr<ParsedFolder>& parent ) {
+ d->parent = parent;
+}
+
+ParsedNode::~ParsedNode() {
+ delete d;
+}
+
+QString ParsedNode::title() const {
+ return d->title;
+}
+
+void ParsedNode::setTitle( const QString& title ) {
+ d->title = title;
+}
+
+QString ParsedNode::attribute( const QString& key ) const {
+ return d->attributes.value( key );
+}
+
+QHash<QString, QString> ParsedNode::attributes() const
+{
+ return d->attributes;
+}
+
+void ParsedNode::setAttribute( const QString& key, const QString& value )
+{
+ d->attributes.insert( key, value );
+}
+
+void ParsedNode::setAttributes( const QHash<QString, QString>& attributes )
+{
+ d->attributes = attributes;
+}
+
+QStringList ParsedNode::parentFolderTitles() const {
+ QStringList labels;
+ shared_ptr<ParsedNode> it = parent().lock();
+ while ( it ) {
+ labels.push_back( it->title() );
+ it = it->parent().lock();
+ }
+ return labels;
+}
+
+class ParsedFeed::Private
+{
+public:
+ Private() {}
+
+ QString xmlUrl;
+ QString htmlUrl;
+ QString description;
+ QString type;
+};
+
+ParsedFeed::ParsedFeed() : d( new Private )
+{
+}
+
+ParsedFeed::~ParsedFeed()
+{
+ delete d;
+}
+
+bool ParsedFeed::isFolder() const
+{
+ return false;
+}
+
+QList<shared_ptr<const ParsedFeed> > ParsedFeed::feeds() const
+{
+ return QList<shared_ptr<const ParsedFeed> >() << static_pointer_cast<const \
ParsedFeed>( shared_from_this() ); +}
+
+QList<shared_ptr<const ParsedFolder> > ParsedFeed::folders() const
+{
+ return QList<shared_ptr<const ParsedFolder> >();
+}
+
+
+QString ParsedFeed::xmlUrl() const
+{
+ return d->xmlUrl;
+}
+
+void ParsedFeed::setXmlUrl( const QString& xmlUrl )
+{
+ d->xmlUrl = xmlUrl;
+}
+
+QString ParsedFeed::htmlUrl() const
+{
+ return d->htmlUrl;
+}
+
+void ParsedFeed::setHtmlUrl( const QString& htmlUrl )
+{
+ d->htmlUrl = htmlUrl;
+}
+
+QString ParsedFeed::description() const
+{
+ return d->description;
+}
+
+void ParsedFeed::setDescription( const QString& description )
+{
+ d->description = description;
+}
+
+QString ParsedFeed::type() const
+{
+ return d->type;
+}
+
+void ParsedFeed::setType( const QString& type )
+{
+ d->type = type;
+}
+
+
+Akonadi::Collection ParsedFeed::toAkonadiCollection() const
+{
+ KRss::FeedCollection feed;
+ feed.setRemoteId( d->xmlUrl );
+ feed.setTitle( title() );
+ feed.setXmlUrl( d->xmlUrl );
+ feed.setHtmlUrl( d->htmlUrl );
+ feed.setDescription( d->description );
+ feed.setFeedType( d->type );
+ feed.setName( title() );
+ feed.setContentMimeTypes( QStringList( QLatin1String("application/rss+xml") ) );
+ return feed;
+}
+
+shared_ptr<ParsedFeed> ParsedFeed::fromAkonadiCollection( const Akonadi::Collection& \
collection ) +{
+ const KRss::FeedCollection feedCollection = collection;
+ shared_ptr<ParsedFeed> parsedFeed( new ParsedFeed );
+ parsedFeed->setTitle( feedCollection.title() );
+ parsedFeed->d->xmlUrl = feedCollection.xmlUrl();
+ parsedFeed->d->htmlUrl = feedCollection.htmlUrl();
+ parsedFeed->d->description = feedCollection.description();
+ parsedFeed->d->type = feedCollection.feedType();
+ parsedFeed->setAttribute( QLatin1String("remoteid"), feedCollection.remoteId() \
); + return parsedFeed;
+}
+
+class ParsedFolder::Private {
+public:
+ QList<shared_ptr<const ParsedNode> > children;
+};
+
+ParsedFolder::ParsedFolder() : d( new Private ) {
+
+}
+
+ParsedFolder::~ParsedFolder() {
+ delete d;
+}
+
+bool ParsedFolder::isFolder() const {
+ return true;
+}
+
+QList<shared_ptr<const ParsedFeed> > ParsedFolder::feeds() const {
+ QList<shared_ptr<const ParsedFeed> > f;
+ Q_FOREACH( const shared_ptr<const ParsedNode>& i, d->children )
+ f << i->feeds();
+ return f;
+}
+
+QList<shared_ptr<const ParsedFolder> > ParsedFolder::folders() const {
+ QList<shared_ptr<const ParsedFolder> > f;
+ f << static_pointer_cast<const ParsedFolder>( shared_from_this() );
+ Q_FOREACH( const shared_ptr<const ParsedNode>& i, d->children )
+ f << i->folders();
+ return f;
+}
+
+QList<shared_ptr<const ParsedNode> > ParsedFolder::children() const {
+ return d->children;
+}
+
+void ParsedFolder::setChildren( const QList<shared_ptr<const ParsedNode> >& children \
) { + d->children = children;
+}
+
+void ParsedFolder::addChild( const shared_ptr<const ParsedNode>& child ) {
+ d->children.push_back( child );
+}
+
+class OpmlReader::Private
+{
+public:
+ explicit Private() {}
+
+ QStringRef attributeValue( const QXmlStreamAttributes& attributes, const \
QString& name ); + void readBody( QXmlStreamReader& reader );
+ void readOutline( QXmlStreamReader& reader, const shared_ptr<ParsedFolder> \
&parent ); + void readUnknownElement( QXmlStreamReader& reader );
+
+ QList<shared_ptr<ParsedNode> > m_topNodes;
+};
+
+QStringRef OpmlReader::Private::attributeValue( const QXmlStreamAttributes& \
attributes, const QString& name ) +{
+ Q_FOREACH( const QXmlStreamAttribute& attr, attributes ) {
+ if ( attr.name().toString().toLower() == name ) {
+ return attr.value();
+ }
+ }
+
+ return QStringRef();
+}
+
+void OpmlReader::Private::readBody( QXmlStreamReader& reader )
+{
+ Q_ASSERT( reader.isStartElement() && reader.name().toString().toLower() == \
QLatin1String("body") ); +
+ while ( !reader.atEnd() ) {
+ reader.readNext();
+
+ if ( reader.isEndElement() )
+ break;
+
+ if ( reader.isStartElement() ) {
+ if ( reader.name().toString().toLower() == QLatin1String("outline") ) {
+ readOutline( reader, shared_ptr<ParsedFolder>() );
+ }
+ else {
+ readUnknownElement( reader );
+ }
+ }
+ }
+}
+
+void OpmlReader::Private::readOutline( QXmlStreamReader& reader, const \
shared_ptr<ParsedFolder>& parent ) +{
+ Q_ASSERT( reader.isStartElement() && reader.name().toString().toLower() == \
QLatin1String("outline") ); +
+ shared_ptr<ParsedNode> newNode;
+ const QString xmlUrl = attributeValue( reader.attributes(), \
QLatin1String("xmlurl") ).toString(); +
+ if ( xmlUrl.isEmpty() ) {
+ const QStringRef textAttribute = attributeValue( reader.attributes(), \
QLatin1String("text") ); + if ( !textAttribute.isEmpty() ) {
+ // this attribute seem to represent a folder
+ shared_ptr<ParsedFolder> newFolder( new ParsedFolder );
+ newNode = newFolder;
+ const QXmlStreamAttributes attrs = reader.attributes();
+
+ Q_FOREACH( const QXmlStreamAttribute& attr, attrs ) {
+ if ( attr.name().toString().toLower() == QLatin1String("title") )
+ newFolder->setTitle( attr.value().toString() );
+ else if ( newFolder->title().isEmpty() && \
attr.name().toString().toLower() == QLatin1String("text") ) + \
newFolder->setTitle( attr.value().toString() ); + else {
+ newFolder->setAttribute( attr.name().toString(), \
attr.value().toString() ); + }
+ }
+ }
+ else {
+ kDebug() << "Encountered an empty outline";
+ const QXmlStreamAttributes attrs;
+ Q_FOREACH( const QXmlStreamAttribute& attr, attrs ) {
+ kDebug() << "Attribute name:" << attr.name() << ", value:" << \
attr.value(); + }
+ }
+ }
+ else {
+ // this is a feed
+ kDebug() << "Feed:" << xmlUrl;
+ shared_ptr<ParsedFeed> newFeed( new ParsedFeed );
+ newNode = newFeed;
+ const QXmlStreamAttributes attrs = reader.attributes();
+ Q_FOREACH( const QXmlStreamAttribute& attr, attrs ) {
+ if ( attr.name().toString().toLower() == QLatin1String("title") )
+ newFeed->setTitle( attr.value().toString() );
+ else if ( newFeed->title().isEmpty() && attr.name().toString().toLower() \
== QLatin1String("text") ) + newFeed->setTitle( \
attr.value().toString() ); + else if ( attr.name().toString().toLower() == \
QLatin1String("htmlurl") ) + newFeed->setHtmlUrl( \
attr.value().toString() ); + else if ( attr.name().toString().toLower() == \
QLatin1String("xmlurl") ) + newFeed->setXmlUrl( \
attr.value().toString() ); + else if ( attr.name().toString().toLower() == \
QLatin1String("description") ) + newFeed->setDescription( \
attr.value().toString() ); + else if ( attr.name().toString().toLower() == \
QLatin1String("type") ) + newFeed->setType( attr.value().toString() );
+ else if ( attr.name().toString().toLower() == QLatin1String("category") \
) { + const QStringList categories = attr.value().toString().split( \
QRegExp( QLatin1String("[,/]") ), QString::SkipEmptyParts ); +#if 0
+ Q_FOREACH( const QString& category, categories ) {
+ if ( !currentTags.contains( category ) )
+ currentTags.append( category );
+ }
+#endif
+ }
+ else {
+ newFeed->setAttribute( attr.name().toString(), \
attr.value().toString() ); + }
+ }
+
+ if ( newFeed->title().isEmpty() )
+ newFeed->setTitle( xmlUrl );
+
+ if ( newFeed->type().isEmpty() )
+ newFeed->setType( QLatin1String("rss") );
+ }
+
+ if ( parent ) {
+ parent->addChild( newNode );
+ newNode->setParent( parent );
+ }
+ else
+ m_topNodes.append( newNode );
+
+ while ( !reader.atEnd() ) {
+ reader.readNext();
+
+ if ( reader.isEndElement() )
+ break;
+
+ if ( reader.isStartElement() ) {
+ if ( reader.name().toString().toLower() == QLatin1String("outline") && \
newNode->isFolder() ) + readOutline( reader, \
static_pointer_cast<ParsedFolder>( newNode ) ); + else
+ readUnknownElement( reader );
+ }
+ }
+}
+
+void OpmlReader::Private::readUnknownElement( QXmlStreamReader& reader )
+{
+ Q_ASSERT( reader.isStartElement() );
+
+ while ( !reader.atEnd() ) {
+ reader.readNext();
+
+ if ( reader.isEndElement() )
+ break;
+
+ if ( reader.isStartElement() )
+ readUnknownElement( reader );
+ }
+}
+
+OpmlReader::OpmlReader()
+ : d( new Private )
+{
+}
+
+OpmlReader::~OpmlReader()
+{
+ delete d;
+}
+
+QList<shared_ptr<const ParsedFeed> > OpmlReader::feeds() const
+{
+ QList<shared_ptr<const ParsedFeed> > f;
+ Q_FOREACH ( const shared_ptr<const ParsedNode>& i, d->m_topNodes )
+ f << i->feeds();
+ return f;
+}
+
+QList<shared_ptr<const ParsedNode> > OpmlReader::topLevelNodes() const
+{
+ QList<shared_ptr<const ParsedNode> > l;
+ Q_FOREACH( const shared_ptr<ParsedNode>& i, d->m_topNodes )
+ l << i;
+ return l;
+}
+
+QStringList OpmlReader::tags() const
+{
+ QStringList titles;
+ QList<shared_ptr<const ParsedFolder> > f;
+ Q_FOREACH ( const shared_ptr<const ParsedNode>& i, d->m_topNodes )
+ f << i->folders();
+ Q_FOREACH ( const shared_ptr<const ParsedFolder>& i, f )
+ titles << i->title();
+ return titles;
+}
+
+void OpmlReader::readOpml( QXmlStreamReader& reader )
+{
+ Q_ASSERT( reader.isStartElement() && reader.name().toString().toLower() == \
QLatin1String("opml") ); +
+ while ( !reader.atEnd() ) {
+ reader.readNext();
+
+ if ( reader.isEndElement() )
+ break;
+
+ if ( reader.isStartElement() ) {
+ if ( reader.name().toString().toLower() == QLatin1String("head") ) {
+ d->readUnknownElement( reader );
+ }
+ else if ( reader.name().toString().toLower() == QLatin1String("body") ) \
{ + d->readBody( reader );
+ }
+ else {
+ d->readUnknownElement( reader );
+ }
+ }
+ }
+}
+
+void OpmlWriter::writeOpml( QXmlStreamWriter& writer, const QList<shared_ptr<const \
ParsedNode> >& nodes, const QString& title) +{
+ writer.writeStartElement( QLatin1String("opml") );
+ writer.writeAttribute( QLatin1String("version"), QLatin1String("2.0") );
+ writer.writeStartElement( QLatin1String("head") );
+ writer.writeTextElement( QLatin1String("title"), title );
+ writer.writeEndElement(); // head
+ writer.writeStartElement( QLatin1String("body") );
+ writeOutlineNodes( writer, nodes );
+ writer.writeEndElement(); // body
+ writer.writeEndElement(); // opml
+}
+
+void OpmlWriter::writeOutlineNodes( QXmlStreamWriter& writer, const \
QList<shared_ptr<const ParsedNode> >& nodes ) +{
+ Q_FOREACH( const shared_ptr<const ParsedNode>& node, nodes ) {
+ if (node->isFolder()) {
+ const shared_ptr<const ParsedFolder> folder = (static_pointer_cast<const \
ParsedFolder>( node )); + writer.writeStartElement( QLatin1String("outline") );
+ writer.writeAttribute( QLatin1String("text"), folder->title() );
+ writer.writeAttribute( QLatin1String("title"), folder->title() );
+ writeOutlineNodes( writer, folder->children());
+ writer.writeEndElement();
+ }
+ else {
+ writeOutlineFeed( writer, static_pointer_cast<const ParsedFeed>( node ) );
+ }
+ }
+}
+
+void OpmlWriter::writeOutlineFeed( QXmlStreamWriter& writer, const shared_ptr<const \
ParsedFeed>& feed ) +{
+ writer.writeStartElement( QLatin1String("outline") );
+ writer.writeAttribute( QLatin1String("text"), feed->title() );
+ writer.writeAttribute( QLatin1String("title"), feed->title() );
+ writer.writeAttribute( QLatin1String("description"), feed->description() );
+ writer.writeAttribute( QLatin1String("htmlUrl"), feed->htmlUrl() );
+ writer.writeAttribute( QLatin1String("xmlUrl"), feed->xmlUrl() );
+ QHashIterator<QString, QString> it( feed->attributes() );
+ while ( it.hasNext() ) {
+ it.next();
+ writer.writeAttribute( it.key(), it.value() );
+ }
+ writer.writeEndElement(); // outline
+}
diff --git a/krsslocal/opmlparser.h b/krsslocal/opmlparser.h
new file mode 100644
index 0000000..9007517
--- /dev/null
+++ b/krsslocal/opmlparser.h
@@ -0,0 +1,149 @@
+/*
+ Copyright (C) 2009 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 KRSSRESOURCE_OPMLPARSER_H
+#define KRSSRESOURCE_OPMLPARSER_H
+
+#include <krssresource/krssresource_export.h>
+
+#include <Akonadi/Collection>
+#include <QtCore/QString>
+#include <QtCore/QList>
+#include <QtCore/QHash>
+#include <QtCore/QStringList>
+#include <QtCore/QXmlStreamReader>
+
+#include <boost/shared_ptr.hpp>
+#include <boost/enable_shared_from_this.hpp>
+
+#include <boost/weak_ptr.hpp>
+
+class QXmlStreamWriter;
+class QXmlStreamAttributes;
+
+namespace KRssResource {
+
+class ParsedFeed;
+class ParsedFolder;
+
+class KRSSRESOURCE_EXPORT ParsedNode : public \
boost::enable_shared_from_this<ParsedNode> +{
+public:
+ ParsedNode();
+ virtual ~ParsedNode();
+
+ boost::weak_ptr<ParsedFolder> parent() const;
+ void setParent( const boost::weak_ptr<ParsedFolder>& parent );
+
+ virtual bool isFolder() const = 0;
+ virtual QList<boost::shared_ptr<const ParsedFeed> > feeds() const = 0;
+ virtual QList<boost::shared_ptr<const ParsedFolder> > folders() const = 0;
+ QString title() const;
+ void setTitle( const QString& title );
+
+ QString attribute( const QString& key ) const;
+ QHash<QString, QString> attributes() const;
+ void setAttribute( const QString& key, const QString& value );
+ void setAttributes( const QHash<QString, QString>& attributes );
+
+ QStringList parentFolderTitles() const;
+
+private:
+ Q_DISABLE_COPY(ParsedNode)
+ class Private;
+ Private* const d;
+};
+
+class KRSSRESOURCE_EXPORT ParsedFeed : public ParsedNode
+{
+public:
+ ParsedFeed();
+ ~ParsedFeed();
+
+ /* reimp */ bool isFolder() const;
+ /* reimp */ QList<boost::shared_ptr<const ParsedFeed> > feeds() const;
+ /* reimp */ QList<boost::shared_ptr<const ParsedFolder> > folders() const;
+
+ QString xmlUrl() const;
+ void setXmlUrl( const QString& xmlUrl );
+ QString htmlUrl() const;
+ void setHtmlUrl( const QString& htmlUrl );
+ QString description() const;
+ void setDescription( const QString& description );
+ QString type() const;
+ void setType( const QString& type );
+
+ Akonadi::Collection toAkonadiCollection() const;
+ static boost::shared_ptr<ParsedFeed> fromAkonadiCollection( const \
Akonadi::Collection& collection ); +
+private:
+ Q_DISABLE_COPY(ParsedFeed)
+ class Private;
+ Private* const d;
+};
+
+class KRSSRESOURCE_EXPORT ParsedFolder : public ParsedNode
+{
+public:
+ ParsedFolder();
+ ~ParsedFolder();
+
+ /* reimp */ bool isFolder() const;
+ /* reimp */ QList<boost::shared_ptr<const ParsedFeed> > feeds() const;
+ /* reimp */ QList<boost::shared_ptr<const ParsedFolder> > folders() const;
+
+ QList<boost::shared_ptr<const ParsedNode> > children() const;
+ void setChildren( const QList<boost::shared_ptr<const ParsedNode> >& children );
+ void addChild( const boost::shared_ptr<const ParsedNode>& child );
+
+private:
+ class Private;
+ Private* const d;
+};
+
+class KRSSRESOURCE_EXPORT OpmlReader
+{
+public:
+ OpmlReader();
+ ~OpmlReader();
+
+ void readOpml( QXmlStreamReader& reader );
+
+ QList<boost::shared_ptr<const ParsedFeed> > feeds() const;
+ QList<boost::shared_ptr<const ParsedNode> > topLevelNodes() const;
+
+ QStringList tags() const;
+
+private:
+ class Private;
+ Private * const d;
+ Q_DISABLE_COPY( OpmlReader )
+};
+
+class KRSSRESOURCE_EXPORT OpmlWriter
+{
+public:
+ static void writeOpml( QXmlStreamWriter& writer, const QList<boost::shared_ptr< \
const ParsedNode> >& nodes, + const QString& title = \
QLatin1String("") ); + static void writeOutlineFeed( QXmlStreamWriter& writer, \
const boost::shared_ptr<const ParsedFeed>& feed ); + static void \
writeOutlineNodes( QXmlStreamWriter& writer, const QList<boost::shared_ptr< const \
ParsedNode> >& nodes ); +
+};
+
+} //namespace KRssResource
+
+#endif // KRSSRESOURCE_OPMLPARSER_H
diff --git a/krsslocal/util.cpp b/krsslocal/util.cpp
index 3c04b3c..f1b06fb 100644
--- a/krsslocal/util.cpp
+++ b/krsslocal/util.cpp
@@ -23,7 +23,7 @@
#include <krss/person.h>
#include <krss/category.h>
#include <krss/enclosure.h>
-//#include <krss/feedcollection.h>
+#include <krss/feedcollection.h>
#include <Akonadi/Collection>
#include <Syndication/Person>
@@ -34,6 +34,7 @@
#include <QtCore/QByteArray>
#include <KDebug>
+#include <KLocale>
#include <QtCore/QMultiMap>
#include <QtCore/QMapIterator>
#include <QtXml/QDomElement>
@@ -124,7 +125,7 @@ KRss::RssItem KRssResource::Util::fromSyndicationItem(const \
Syndication::ItemPtr QMapIterator<QString, QDomElement> it( syndProperties );
while ( it.hasNext() ) {
it.next();
- rssItem.setCustomProperty( it.key(), it.value().text() );
+ rssItem.setCustomProperty( it.key(), it.value().text() ); //FIXME \
(Alessandro: why is it yellow on kdevelop?) }
rssItem.setHash( calcHash( syndItem->title() + syndItem->description() +
@@ -132,16 +133,38 @@ KRss::RssItem KRssResource::Util::fromSyndicationItem(const \
Syndication::ItemPtr return rssItem;
}
-QList< boost::shared_ptr< KRssResource::ParsedFeed > > \
KRssResource::Util::toParsedFeedList(const QList< Akonadi::Collection >& feeds) \
+QList< boost::shared_ptr< const KRssResource::ParsedNode > > \
KRssResource::Util::parsedDescendants( QList< Akonadi::Collection >& collections, \
Akonadi::Collection parent ) {
- QList<boost::shared_ptr<ParsedFeed> > parsedFeeds;
- Q_FOREACH( const Akonadi::Collection& feed, feeds ) {
- if ( feed.parent() != Akonadi::Collection::root().id() )
- parsedFeeds.append( ParsedFeed::fromAkonadiCollection( feed ) );
+ QList<boost::shared_ptr< const KRssResource::ParsedNode > > nodesList;
+
+ Q_FOREACH( const Akonadi::Collection& collection , collections ) {
+ if (collection.parentCollection() == parent) {
+ boost::shared_ptr< KRssResource::ParsedNode > node;
+ const KRss::FeedCollection feedCollection = collection;
+ if (feedCollection.feedType() == QLatin1String( "rss" )) { //it's a feed. \
correct test??? + node = ParsedFeed::fromAkonadiCollection ( collection );
+ }
+ else if (feedCollection.feedType() == QLatin1String( "" )) { //it's a feed. \
again, correct test??? + boost::shared_ptr<ParsedFolder> parsedFolder( new \
ParsedFolder ); + parsedFolder->setTitle( feedCollection.name() );
+ QList< boost::shared_ptr< const KRssResource::ParsedNode > > children = \
parsedDescendants( collections, collection ); + parsedFolder->setChildren( children \
); +
+ node = parsedFolder;
+ }
+ else {
+ kWarning() << "Collection type not recognized";
+ }
+ collections.removeOne( collection );
+ nodesList.append( node );
+ }
}
- return parsedFeeds;
+
+ return nodesList;
+
}
+
/*
QString KRssResource::generateCollectionName( const Akonadi::Collection& collection \
) {
diff --git a/krsslocal/util.h b/krsslocal/util.h
index c1abec8..aaf36f0 100644
--- a/krsslocal/util.h
+++ b/krsslocal/util.h
@@ -1,5 +1,6 @@
/*
Copyright (C) 2009 Dmitry Ivanov <vonami@gmail.com>
+ Copyright (C) 2011 Alessandro Cosentino <cosenal@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
@@ -18,15 +19,17 @@
#ifndef KRSSRESOURCE_UTIL_H
#define KRSSRESOURCE_UTIL_H
+#include "opmlparser.h"
+
#include <krss/rssitem.h>
#include <Syndication/Item>
-#include <krssresource/opmlparser.h>
namespace KRssResource {
namespace Util {
KRss::RssItem fromSyndicationItem( const Syndication::ItemPtr& syndItem );
- QList<boost::shared_ptr<ParsedFeed> > toParsedFeedList( const \
QList<Akonadi::Collection>& feeds ); + QList<boost::shared_ptr<const ParsedNode> > \
toParsedNodesTree( QList<Akonadi::Collection>& collections ); + \
QList<boost::shared_ptr<const ParsedNode> > parsedDescendants( QList< \
Akonadi::Collection >& collections, Akonadi::Collection parent );
};
};
[prev in list] [next in list] [prev in thread] [next in thread]
Configure |
About |
News |
Add a list |
Sponsored by KoreLogic