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 <tqdatetime.h> 00023 #include <tqdom.h> 00024 00025 using namespace RSS; 00026 namespace RSS 00027 { 00028 KMD5 md5Machine; 00029 } 00030 00031 struct Article::Private : public Shared 00032 { 00033 TQString title; 00034 KURL link; 00035 TQString description; 00036 TQDateTime pubDate; 00037 TQString guid; 00038 TQString author; 00039 bool guidIsPermaLink; 00040 MetaInfoMap meta; 00041 KURL commentsLink; 00042 int numComments; 00043 Enclosure enclosure; 00044 TQValueList<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 TQValueList<Category> Article::categories() const 00062 { 00063 return d->categories; 00064 } 00065 00066 00067 Article::Article(const TQDomNode &node, Format format, Version version) : d(new Private) 00068 { 00069 TQString elemText; 00070 00071 d->numComments=0; 00072 00073 if (!(elemText = extractTitle(node)).isNull()) 00074 d->title = elemText; 00075 00076 if (format==AtomFeed) 00077 { 00078 TQDomNode n; 00079 for (n = node.firstChild(); !n.isNull(); n = n.nextSibling()) { 00080 const TQDomElement e = n.toElement(); 00081 if ( (e.tagName()==TQString::fromLatin1("link")) && 00082 (e.attribute(TQString::fromLatin1("rel"), TQString::fromLatin1("alternate")) == TQString::fromLatin1("alternate"))) 00083 { 00084 d->link=n.toElement().attribute(TQString::fromLatin1("href")); 00085 break; 00086 } 00087 } 00088 } 00089 else 00090 { 00091 if (!(elemText = extractNode(node, TQString::fromLatin1("link"))).isNull()) 00092 d->link = elemText; 00093 } 00094 00095 00096 // prefer content/content:encoded over summary/description for feeds that provide it 00097 TQString tagName=(format==AtomFeed)? TQString::fromLatin1("content"): TQString::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, TQString::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, TQString::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, TQString::fromLatin1("updated")); 00120 else 00121 elemText = extractNode(node, TQString::fromLatin1("issued")); 00122 00123 if (!elemText.isNull()) 00124 time = parseISO8601Date(elemText); 00125 } 00126 else 00127 { 00128 elemText = extractNode(node, TQString::fromLatin1("pubDate")); 00129 if (!elemText.isNull()) 00130 time = KRFCDate::parseDate(elemText); 00131 } 00132 00133 if (!(elemText = extractNode(node, TQString::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, TQString::fromLatin1("wfw:comment"))).isNull()) { 00143 d->commentsLink = elemText; 00144 } 00145 00146 if (!(elemText = extractNode(node, TQString::fromLatin1("slash:comments"))).isNull()) { 00147 d->numComments = elemText.toInt(); 00148 } 00149 00150 TQDomElement element = TQDomNode(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(TQString::fromLatin1("rdf:about"))) 00156 { 00157 d->guid = element.attribute(TQString::fromLatin1("rdf:about")); // HACK: using ns properly did not work 00158 d->guidIsPermaLink = false; 00159 } 00160 else 00161 { 00162 tagName=(format==AtomFeed)? TQString::fromLatin1("id"): TQString::fromLatin1("guid"); 00163 TQDomNode n = node.namedItem(tagName); 00164 if (!n.isNull()) 00165 { 00166 d->guidIsPermaLink = (format==AtomFeed)? false : true; 00167 if (n.toElement().attribute(TQString::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 TQDomNode n(node); 00178 md5Machine.update(d->title.utf8()); 00179 md5Machine.update(d->description.utf8()); 00180 d->guid = TQString(md5Machine.hexDigest().data()); 00181 d->meta[TQString::fromLatin1("guidIsHash")] = TQString::fromLatin1("true"); 00182 } 00183 00184 TQDomNode enclosure = element.namedItem(TQString::fromLatin1("enclosure")); 00185 if (enclosure.isElement()) 00186 d->enclosure = Enclosure::fromXML(enclosure.toElement()); 00187 00188 d->author = parseItemAuthor(element, format, version); 00189 00190 for (TQDomNode i = node.firstChild(); !i.isNull(); i = i.nextSibling()) 00191 { 00192 if (i.isElement()) 00193 { 00194 if (i.toElement().tagName() == TQString::fromLatin1("metaInfo:meta")) 00195 { 00196 TQString type = i.toElement().attribute(TQString::fromLatin1("type")); 00197 d->meta[type] = i.toElement().text(); 00198 } 00199 else if (i.toElement().tagName() == TQString::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 TQString Article::title() const 00214 { 00215 return d->title; 00216 } 00217 00218 TQString Article::author() const 00219 { 00220 return d->author; 00221 } 00222 00223 const KURL &Article::link() const 00224 { 00225 return d->link; 00226 } 00227 00228 TQString Article::description() const 00229 { 00230 return d->description; 00231 } 00232 00233 TQString Article::guid() const 00234 { 00235 return d->guid; 00236 } 00237 00238 bool Article::guidIsPermaLink() const 00239 { 00240 return d->guidIsPermaLink; 00241 } 00242 00243 const TQDateTime &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 TQString Article::meta(const TQString &key) const 00260 { 00261 return d->meta[key]; 00262 } 00263 00264 KURLLabel *Article::widget(TQWidget *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