[prev in list] [next in list] [prev in thread] [next in thread] 

List:       kde-commits
Subject:    [kdepim-runtime/akregator_port] krsslocal: writeFeedsToOpml now preserves folder hierarchy. NEED POL
From:       Alessandro Cosentino <cosenal () gmail ! com>
Date:       2012-03-31 15:16:46
Message-ID: 20120331151646.A07E1A6124 () git ! kde ! org
[Download RAW message or body]

Git commit 623dd9a50c381c750450cc5916fbfee99dc209f9 by Alessandro Cosentino.
Committed on 09/09/2011 at 20:07.
Pushed by osterfeld 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-runtime/623dd9a50c381c750450cc5916fbfee99dc209f9

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