• Skip to content
  • Skip to link menu
KDE 4.2 API Reference
  • KDE API Reference
  • API Reference
  • Sitemap
  • Contact Us
 

Engines

rss.cpp

Go to the documentation of this file.
00001 /*
00002  *   Copyright (C) 2007 Aaron Seigo <aseigo@kde.org>
00003  *   Copyright (C) 2007 Petri Damsten <damu@iki.fi>
00004  *   Copyright (C) 2008 Rob Scheepmaker <r.scheepmaker@student.utwente.nl>
00005  *
00006  *   This program is free software; you can redistribute it and/or modify
00007  *   it under the terms of the GNU Library General Public License version 2 as
00008  *   published by the Free Software Foundation
00009  *
00010  *   This program is distributed in the hope that it will be useful,
00011  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
00012  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00013  *   GNU General Public License for more details
00014  *
00015  *   You should have received a copy of the GNU Library General Public
00016  *   License along with this program; if not, write to the
00017  *   Free Software Foundation, Inc.,
00018  *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
00019  */
00020 
00021 //Own
00022 #include "rss.h"
00023 
00024 //KDE
00025 #include <KDebug>
00026 #include <KUrl>
00027 #include <kstandarddirs.h>
00028 #include <syndication/item.h>
00029 #include <syndication/loader.h>
00030 #include <syndication/image.h>
00031 
00032 //Plasma
00033 #include <Plasma/DataEngine>
00034 
00035 //Qt
00036 #include <QDateTime>
00037 #include <QDBusReply>
00038 #include <QDBusInterface>
00039 #include <QTimer>
00040 #include <QSignalMapper>
00041 
00042 #define TIMEOUT 15000    //timeout before updating the source if not all feeds
00043                          //are fetched.
00044 #define CACHE_TIMEOUT 60 //time in seconds before the cached feeds are marked
00045                          //as out of date.
00046 #define MINIMUM_INTERVAL 60000
00047 #define FAVICONINTERFACE "org.kde.FavIcon"
00048 
00049 RssEngine::RssEngine(QObject* parent, const QVariantList& args)
00050     : Plasma::DataEngine(parent, args)
00051 {
00052     Q_UNUSED(args)
00053     setMinimumPollingInterval(MINIMUM_INTERVAL);
00054     m_favIconsModule = new QDBusInterface("org.kde.kded", "/modules/favicons",
00055                                           FAVICONINTERFACE);
00056     m_signalMapper = new QSignalMapper(this);
00057     connect(m_favIconsModule, SIGNAL(iconChanged(bool,QString,QString)),
00058             this, SLOT(slotIconChanged(bool,QString,QString)));
00059     connect(m_signalMapper, SIGNAL(mapped(const QString &)),
00060             this, SLOT(timeout(const QString &)));
00061 }
00062 
00063 RssEngine::~RssEngine()
00064 {
00065     delete m_favIconsModule;
00066 }
00067 
00068 bool RssEngine::updateSourceEvent(const QString &name)
00069 {
00070     /* Plasmoids using this engine should be able to retrieve
00071      * multiple feeds at the same time, so we allow a comma
00072      * separated list of url's
00073      */
00074     // NOTE: A comma separated list of feeds is not url compliant. Urls 
00075     // may and do contain commas see http://www.spiegel.de/schlagzeilen/rss/0,5291,,00.xml
00076     // I have changed it to something more not url compliant " " three dots
00077     // Otherwise take a list instead
00078     const QStringList sources = name.split(" ", QString::SkipEmptyParts);
00079 
00080     foreach (const QString& source, sources) {
00081         // Let's first see if we've got a recent cached version of
00082         // the feed. This avoids 'large' amounts of unnecesarry network
00083         // traffic.
00084         if (QDateTime::currentDateTime() >
00085             m_feedTimes[source.toLower()].addSecs(CACHE_TIMEOUT)){
00086             kDebug() << "Cache from " << source <<
00087                         " older than 60 seconds, refreshing...";
00088 
00089             Syndication::Loader * loader = Syndication::Loader::create();
00090             connect(loader, SIGNAL(loadingComplete(Syndication::Loader*,
00091                                                    Syndication::FeedPtr,
00092                                                    Syndication::ErrorCode)),
00093                       this, SLOT(processRss(Syndication::Loader*,
00094                                             Syndication::FeedPtr,
00095                                             Syndication::ErrorCode)));
00096 
00097             m_feedMap.insert(loader, source);
00098             m_sourceMap.insert(loader, name);
00099             loader->loadFrom(source);
00100         } else {
00101             kDebug() << "Recent cached version of " << source <<
00102                         " found. Skipping...";
00103 
00104             // We might want to update the source:
00105             if (cachesUpToDate(name)) {
00106                 updateFeeds(name, m_feedTitles[ source ] );
00107             }
00108         }
00109     }
00110 
00111     QTimer *timer = new QTimer(this);
00112     m_timerMap[name] = timer;
00113     timer->setSingleShot(true);
00114     m_signalMapper->setMapping(timer, name);
00115 
00116     connect(timer, SIGNAL(timeout()), m_signalMapper, SLOT(map()));
00117 
00118     timer->start(TIMEOUT);
00119     return true;
00120 }
00121 
00122 void RssEngine::slotIconChanged(bool isHost, const QString& hostOrURL,
00123                                              const QString& iconName)
00124 {
00125     Q_UNUSED(isHost);
00126     QString iconFile = KGlobal::dirs()->findResource("cache",
00127                                                      iconName+".png");
00128     QString url = hostOrURL.toLower();
00129 
00130     m_feedIcons[url] = iconFile;
00131     QMap<QString, QVariant> map;
00132 
00133     for (int i = 0; i < m_feedItems[url].size(); i++) {
00134         map = m_feedItems[url].at(i).toMap();
00135         map["icon"] = iconFile;
00136         m_feedItems[url].replace(i, map);
00137     }
00138 
00139     //Are there sources ready to get updated now?
00140     foreach (const QString& source, m_sourceMap) {
00141         if (source.contains(url, Qt::CaseInsensitive) &&
00142             cachesUpToDate(source)) {
00143             kDebug() << "all caches from source " << source <<
00144                         " up to date, updating...";
00145             updateFeeds(source, m_feedTitles[ source ] );
00146         }
00147     }
00148 }
00149 
00150 void RssEngine::timeout(const QString & source)
00151 {
00152     kDebug() << "timout fired, updating source";
00153     updateFeeds(source, m_feedTitles[ source ] );
00154     m_signalMapper->removeMappings(m_timerMap[source]);
00155 }
00156 
00157 bool RssEngine::sourceRequestEvent(const QString &name)
00158 {
00159     setData(name, DataEngine::Data());
00160     updateSourceEvent(name);
00161     return true;
00162 }
00163 
00164 void RssEngine::processRss(Syndication::Loader* loader,
00165                            Syndication::FeedPtr feed,
00166                            Syndication::ErrorCode error)
00167 {
00168     QString url = m_feedMap.take(loader);
00169     QString source = m_sourceMap.take(loader);
00170     QString title;
00171     bool iconRequested = false;
00172     KUrl u(url);
00173 
00174     if (error != Syndication::Success) {
00175         kDebug() << "Syndication did not work out... url = " << url;
00176         title = i18n("Syndication did not work out");
00177         setData(source, "title", i18n("Fetching feed failed."));
00178         setData(source, "link", url);
00179     } else {
00180         title = feed->title();
00181         QVariantList items;
00182         QString location;
00183 
00184         foreach (const Syndication::ItemPtr& item, feed->items()) {
00185             QMap<QString, QVariant> dataItem;
00186 
00187             dataItem["title"]       = item->title();
00188             dataItem["feed_title"]  = feed->title();
00189             dataItem["link"]        = item->link();
00190             dataItem["feed_url"]    = url;
00191             dataItem["description"] = item->description();
00192             dataItem["content"]     = item->content();
00193             dataItem["time"]        = (uint)item->datePublished();
00194             if (!m_feedIcons.contains(url.toLower()) && !iconRequested) {
00195                 //lets request an icon, and only do this once per feed.
00196                 location = iconLocation(u);
00197                 if (location.isEmpty()) {
00198                     m_favIconsModule->call( "downloadHostIcon", u.url() );
00199                 } else {
00200                     //the icon is already in cache, so call this slot manually.
00201                     slotIconChanged(false, u.url(), iconLocation(u));
00202                 }
00203                 iconRequested = true;
00204             }
00205             dataItem["icon"] = m_feedIcons[url.toLower()];
00206 
00207             items.append(dataItem);
00208         }
00209         m_feedItems[url.toLower()] = items;
00210         m_feedTimes[url.toLower()] = QDateTime::currentDateTime();
00211         m_feedTitles[url.toLower()] = title;
00212 
00213         // If we update the feeds every time a feed is fetched,
00214         // only the first update will actually update a connected
00215         // applet, which is actually sane, since plasma updates
00216         // only one time each interval. This means, however, that
00217         // we maybe want to delay updating the feeds untill either
00218         // timeout, or all feeds are up to date.
00219         if (cachesUpToDate(source)) {
00220             kDebug() << "all caches from source " << source
00221                      << " up to date, updating...";
00222             updateFeeds(source, title);
00223         } else {
00224             kDebug() << "not all caches from source " << source
00225                      << ", delaying update.";
00226         }
00227     }
00228 }
00229 
00230 void RssEngine::updateFeeds(const QString & source, const QString & title)
00231 {
00236     const QVariantList list = mergeFeeds(source);
00237     setData(source, "items", list);
00238     QStringList sources = source.split(" ", QString::SkipEmptyParts);
00239     if (sources.size() >  1) {
00240         setData(source, "title", i18np("1 RSS feed fetched",
00241                                        "%1 RSS feeds fetched", sources.size()));
00242     } else {
00243         setData(source, "title", title);
00244     }
00245 }
00246 
00247 bool RssEngine::cachesUpToDate(const QString & source) const
00248 {
00249     QStringList sources = source.split(" ", QString::SkipEmptyParts);
00250     bool outOfDate = false;
00251     foreach (const QString &url, sources) {
00252         if (QDateTime::currentDateTime() >
00253             m_feedTimes[url.toLower()].addSecs(CACHE_TIMEOUT)){
00254             outOfDate = true;
00255         }
00256         if (!m_feedIcons.contains(url.toLower())) {
00257             outOfDate = true;
00258         }
00259     }
00260     return (!outOfDate);
00261 }
00262 
00263 bool compare(const QVariant &v1, const QVariant &v2)
00264 {
00265      return v1.toMap()["time"].toUInt() > v2.toMap()["time"].toUInt();
00266 }
00267 
00268 QVariantList RssEngine::mergeFeeds(QString source) const
00269 {
00270     QVariantList result;
00271     QStringList sources = source.split(" ", QString::SkipEmptyParts);
00272 
00273     foreach (const QString& feed, sources) {
00274         result += m_feedItems[feed.toLower()];
00275     }
00276 
00277     qSort(result.begin(), result.end(), compare);
00278     return result;
00279 }
00280 
00281 QString RssEngine::iconLocation(const KUrl & url) const
00282 {
00283     QDBusReply<QString> reply = m_favIconsModule->call( "iconForUrl", url.url() );
00284     if (reply.isValid()) {
00285         QString result = reply;
00286         return result;
00287     }
00288     return QString();
00289 }
00290 
00291 #include "rss.moc"
00292 

Engines

Skip menu "Engines"
  • Main Page
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members

API Reference

Skip menu "API Reference"
  • KWin
  •   KWin Libraries
  • Libraries
  •   libkworkspace
  •   libsolidcontrol
  •   libtaskmanager
  • Plasma
  •   Animators
  •   Applets
  •   Engines
  • Solid Modules
Generated for API Reference by doxygen 1.5.7
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal