akregator/src/librss

article.cpp

00001 /*
00002  * article.cpp
00003  *
00004  * Copyright (c) 2001, 2002, 2003, 2004 Frerich Raabe <raabe@kde.org>
00005  *
00006  * This program is distributed in the hope that it will be useful, but WITHOUT
00007  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
00008  * FOR A PARTICULAR PURPOSE. For licensing and distribution details, check the
00009  * accompanying file 'COPYING'.
00010  */
00011 #include "article.h"
00012 #include "tools_p.h"
00013 #include "enclosure.h"
00014 #include "category.h"
00015 
00016 #include <kdebug.h>
00017 #include <krfcdate.h>
00018 #include <kurl.h>
00019 #include <kurllabel.h>
00020 #include <kmdcodec.h> 
00021 
00022 #include <qdatetime.h>
00023 #include <qdom.h>
00024 
00025 using namespace RSS;
00026 namespace RSS
00027 {
00028     KMD5 md5Machine;
00029 }
00030 
00031 struct Article::Private : public Shared
00032 {
00033     QString title;
00034     KURL link;
00035     QString description;
00036     QDateTime pubDate;
00037     QString guid;
00038         QString author;
00039     bool guidIsPermaLink;
00040     MetaInfoMap meta;
00041     KURL commentsLink;
00042         int numComments;
00043         Enclosure enclosure;
00044     QValueList<Category> categories;
00045 };
00046 
00047 Article::Article() : d(new Private)
00048 {
00049 }
00050 
00051 Article::Article(const Article &other) : d(0)
00052 {
00053     *this = other;
00054 }
00055 
00056 Enclosure Article::enclosure() const
00057 {
00058     return d->enclosure;
00059 }
00060 
00061 QValueList<Category> Article::categories() const
00062 {
00063     return d->categories;
00064 }
00065 
00066 
00067 Article::Article(const QDomNode &node, Format format, Version version) : d(new Private)
00068 {
00069     QString elemText;
00070 
00071     d->numComments=0;
00072 
00073     if (!(elemText = extractTitle(node)).isNull())
00074         d->title = elemText;
00075    
00076     if (format==AtomFeed)
00077     {
00078         QDomNode n;
00079         for (n = node.firstChild(); !n.isNull(); n = n.nextSibling()) {
00080             const QDomElement e = n.toElement();
00081             if ( (e.tagName()==QString::fromLatin1("link")) &&
00082                 (e.attribute(QString::fromLatin1("rel"), QString::fromLatin1("alternate")) == QString::fromLatin1("alternate")))
00083                 {   
00084                     d->link=n.toElement().attribute(QString::fromLatin1("href"));
00085                     break;
00086                 }
00087         }
00088     }
00089     else
00090     {
00091         if (!(elemText = extractNode(node, QString::fromLatin1("link"))).isNull())
00092             d->link = elemText;
00093     }
00094 
00095 
00096     // prefer content/content:encoded over summary/description for feeds that provide it
00097     QString tagName=(format==AtomFeed)? QString::fromLatin1("content"): QString::fromLatin1("content:encoded");
00098     
00099     if (!(elemText = extractNode(node, tagName, false)).isNull())
00100         d->description = elemText;
00101     
00102     if (d->description.isEmpty())
00103     {
00104         if (!(elemText = extractNode(node, QString::fromLatin1("body"), false)).isNull())
00105             d->description = elemText;
00106     
00107         if (d->description.isEmpty())  // 3rd try: see http://www.intertwingly.net/blog/1299.html
00108         {
00109             if (!(elemText = extractNode(node, QString::fromLatin1((format==AtomFeed)? "summary" : "description"), false)).isNull())
00110                 d->description = elemText;
00111         }
00112     }
00113     
00114     time_t time = 0;
00115 
00116     if (format == AtomFeed)
00117     {
00118         if (version == vAtom_1_0)
00119             elemText = extractNode(node, QString::fromLatin1("updated"));
00120         else
00121            elemText = extractNode(node, QString::fromLatin1("issued"));
00122 
00123         if (!elemText.isNull())
00124             time = parseISO8601Date(elemText);  
00125     }
00126     else 
00127     {
00128         elemText = extractNode(node, QString::fromLatin1("pubDate"));
00129         if (!elemText.isNull())
00130             time = KRFCDate::parseDate(elemText);
00131     }
00132     
00133     if (!(elemText = extractNode(node, QString::fromLatin1("dc:date"))).isNull())
00134     {
00135         time = parseISO8601Date(elemText);
00136     }
00137 
00138     // 0 means invalid, not epoch (parsers return epoch+1 when parsing epoch, see the KRFCDate::parseDate() docs)
00139         if (time != 0)
00140         d->pubDate.setTime_t(time);
00141 
00142     if (!(elemText = extractNode(node, QString::fromLatin1("wfw:comment"))).isNull()) {
00143         d->commentsLink = elemText;
00144     }
00145 
00146     if (!(elemText = extractNode(node, QString::fromLatin1("slash:comments"))).isNull()) {
00147         d->numComments = elemText.toInt();
00148     }
00149 
00150     QDomElement element = QDomNode(node).toElement();
00151 
00152     // in RSS 1.0, we use <item about> attribute as ID
00153     // FIXME: pass format version instead of checking for attribute
00154 
00155     if (!element.isNull() && element.hasAttribute(QString::fromLatin1("rdf:about")))
00156     {
00157         d->guid = element.attribute(QString::fromLatin1("rdf:about")); // HACK: using ns properly did not work
00158         d->guidIsPermaLink = false;
00159     }
00160     else
00161     {
00162         tagName=(format==AtomFeed)? QString::fromLatin1("id"): QString::fromLatin1("guid");
00163         QDomNode n = node.namedItem(tagName);
00164         if (!n.isNull())
00165         {
00166             d->guidIsPermaLink = (format==AtomFeed)? false : true;
00167             if (n.toElement().attribute(QString::fromLatin1("isPermaLink"), "true") == "false") d->guidIsPermaLink = false;
00168             if (!(elemText = extractNode(node, tagName)).isNull())
00169                 d->guid = elemText;
00170         }
00171     }    
00172 
00173     if(d->guid.isEmpty()) {
00174         d->guidIsPermaLink = false;
00175         
00176         md5Machine.reset();
00177         QDomNode n(node);
00178         md5Machine.update(d->title.utf8());
00179         md5Machine.update(d->description.utf8());
00180         d->guid = QString(md5Machine.hexDigest().data());
00181         d->meta[QString::fromLatin1("guidIsHash")] = QString::fromLatin1("true");
00182     }
00183 
00184     QDomNode enclosure = element.namedItem(QString::fromLatin1("enclosure"));
00185     if (enclosure.isElement())
00186         d->enclosure = Enclosure::fromXML(enclosure.toElement());
00187 
00188     d->author = parseItemAuthor(element, format, version);
00189     
00190     for (QDomNode i = node.firstChild(); !i.isNull(); i = i.nextSibling())
00191     {
00192         if (i.isElement())
00193         {
00194             if (i.toElement().tagName() == QString::fromLatin1("metaInfo:meta"))
00195             {
00196                 QString type = i.toElement().attribute(QString::fromLatin1("type"));
00197                 d->meta[type] = i.toElement().text();
00198             }
00199             else if (i.toElement().tagName() == QString::fromLatin1("category"))
00200             {
00201                 d->categories.append(Category::fromXML(i.toElement()));
00202             }
00203         }
00204     }
00205 }
00206 
00207 Article::~Article()
00208 {
00209     if (d->deref())
00210         delete d;
00211 }
00212 
00213 QString Article::title() const
00214 {
00215     return d->title;
00216 }
00217 
00218 QString Article::author() const
00219 {
00220     return d->author;
00221 }
00222 
00223 const KURL &Article::link() const
00224 {
00225     return d->link;
00226 }
00227 
00228 QString Article::description() const
00229 {
00230     return d->description;
00231 }
00232 
00233 QString Article::guid() const
00234 {
00235     return d->guid;
00236 }
00237 
00238 bool Article::guidIsPermaLink() const
00239 {
00240     return d->guidIsPermaLink;
00241 }
00242 
00243 const QDateTime &Article::pubDate() const
00244 {
00245     return d->pubDate;
00246 }
00247 
00248 const KURL &Article::commentsLink() const
00249 {
00250     return d->commentsLink;
00251 }
00252 
00253 int Article::comments() const
00254 {
00255     return d->numComments;
00256 }
00257 
00258 
00259 QString Article::meta(const QString &key) const
00260 {
00261     return d->meta[key];
00262 }
00263 
00264 KURLLabel *Article::widget(QWidget *parent, const char *name) const
00265 {
00266     KURLLabel *label = new KURLLabel(d->link.url(), d->title, parent, name);
00267     label->setUseTips(true);
00268     if (!d->description.isNull())
00269         label->setTipText(d->description);
00270     
00271     return label;
00272 }
00273 
00274 Article &Article::operator=(const Article &other)
00275 {
00276     if (this != &other) {
00277         other.d->ref();
00278         if (d && d->deref())
00279             delete d;
00280         d = other.d;
00281     }
00282     return *this;
00283 }
00284 
00285 bool Article::operator==(const Article &other) const
00286 {
00287     return d->guid == other.guid();
00288 }
00289 
00290 // vim:noet:ts=4
KDE Home | KDE Accessibility Home | Description of Access Keys