27 #include "feedstorage.h"
29 #include "librss/librss.h"
33 #include <tqdatetime.h>
36 #include <tqstringlist.h>
37 #include <tqvaluelist.h>
46 struct Article::Private :
public Shared
58 enum Status {Deleted=0x01, Trash=0x02, New=0x04, Read=0x08, Keep=0x10};
61 Backend::FeedStorage* archive;
74 Article::Article() : d(new Private)
82 Article::Article(
const TQString& guid,
Feed* feed) : d(new Private)
92 d->archive = Backend::Storage::getInstance()->archiveFor(feed->
xmlUrl());
97 void Article::initialize(RSS::Article article, Backend::FeedStorage* archive)
100 d->status = Private::New;
101 d->hash = Utils::calcHash(article.title() + article.description() + article.author() + article.link().url()
102 + article.commentsLink().url() );
104 d->guid = article.guid();
106 if (!d->archive->contains(d->guid))
108 d->archive->addEntry(d->guid);
110 if (article.meta(
"deleted") ==
"true")
112 d->status = Private::Read | Private::Deleted;
113 d->archive->setStatus(d->guid, d->status);
118 d->archive->setHash(d->guid,
hash() );
119 TQString title = article.title().isEmpty() ? buildTitle(article.description()) : article.title();
120 d->archive->setTitle(d->guid, title);
121 d->archive->setDescription(d->guid, article.description());
122 d->archive->setLink(d->guid, article.link().url());
123 d->archive->setComments(d->guid, article.comments());
124 d->archive->setCommentsLink(d->guid, article.commentsLink().url());
125 d->archive->setGuidIsPermaLink(d->guid, article.guidIsPermaLink());
126 d->archive->setGuidIsHash(d->guid, article.meta(
"guidIsHash") ==
"true");
127 d->pubDate = article.pubDate().isValid() ? article.pubDate() : TQDateTime::currentDateTime();
128 d->archive->setPubDate(d->guid, d->pubDate.toTime_t());
129 d->archive->setAuthor(d->guid, article.author());
131 TQValueList<RSS::Category> cats = article.categories();
132 TQValueList<RSS::Category>::ConstIterator end = cats.end();
134 for (TQValueList<RSS::Category>::ConstIterator it = cats.begin(); it != end; ++it)
136 Backend::Category cat;
138 cat.term = (*it).category();
139 cat.scheme = (*it).domain();
140 cat.name = (*it).category();
142 d->archive->addCategory(d->guid, cat);
145 if (!article.enclosure().isNull())
147 d->archive->setEnclosure(d->guid, article.enclosure().url(), article.enclosure().type(), article.enclosure().length());
151 d->archive->removeEnclosure(d->guid);
154 TQString status = article.meta(
"status");
156 if (!status.isEmpty())
158 int statusInt = status.toInt();
159 if (statusInt == New)
161 setStatus(statusInt);
163 setKeep(article.meta(
"keep") ==
"true");
169 d->archive->setComments(d->guid, article.comments());
170 if (
hash() != d->archive->hash(d->guid))
172 d->pubDate.setTime_t(d->archive->pubDate(d->guid));
173 d->archive->setHash(d->guid,
hash() );
174 TQString title = article.title().isEmpty() ? buildTitle(article.description()) : article.title();
175 d->archive->setTitle(d->guid, title);
176 d->archive->setDescription(d->guid, article.description());
177 d->archive->setLink(d->guid, article.link().url());
178 d->archive->setCommentsLink(d->guid, article.commentsLink().url());
179 d->archive->setAuthor(d->guid, article.author());
184 Article::Article(RSS::Article article,
Feed* feed) : d(new Private)
188 initialize(article, Backend::Storage::getInstance()->archiveFor(feed->
xmlUrl()));
191 Article::Article(RSS::Article article, Backend::FeedStorage* archive) : d(new Private)
194 initialize(article, archive);
197 bool Article::isNull()
const
199 return d->archive == 0;
202 void Article::offsetPubDate(
int secs)
204 d->pubDate = pubDate().addSecs(secs);
205 d->archive->setPubDate(d->guid, d->pubDate.toTime_t());
209 void Article::setDeleted()
215 d->status = Private::Deleted | Private::Read;
216 d->archive->setStatus(d->guid, d->status);
217 d->archive->setDeleted(d->guid);
220 d->feed->setArticleDeleted(*
this);
223 bool Article::isDeleted()
const
225 return (statusBits() & Private::Deleted) != 0;
228 Article::Article(
const Article &other) : d(new Private)
242 Article &Article::operator=(
const Article &other)
244 if (
this != &other) {
254 bool Article::operator<(
const Article &other)
const
256 return pubDate() > other.pubDate() ||
257 (pubDate() == other.pubDate() && guid() < other.guid() );
260 bool Article::operator<=(
const Article &other)
const
262 return (pubDate() > other.pubDate() || *
this == other);
265 bool Article::operator>(
const Article &other)
const
267 return pubDate() < other.pubDate() ||
268 (pubDate() == other.pubDate() && guid() > other.guid() );
271 bool Article::operator>=(
const Article &other)
const
273 return (pubDate() > other.pubDate() || *
this == other);
276 bool Article::operator==(
const Article &other)
const
278 return d->guid == other.guid();
281 int Article::statusBits()
const
284 if ( d->status == 0 )
286 d->status = d->archive->status(d->guid);
292 int Article::status()
const
294 if ((statusBits() & Private::Read) != 0)
297 if ((statusBits() & Private::New) != 0)
303 void Article::setStatus(
int stat)
307 int oldStatus = status();
309 if (oldStatus != stat)
314 d->status = ( d->status | Private::Read) & ~Private::New;
317 d->status = ( d->status & ~Private::Read) & ~Private::New;
320 d->status = ( d->status | Private::New) & ~Private::Read;
323 d->archive->setStatus(d->guid, d->status);
325 d->feed->setArticleChanged(*
this, oldStatus);
329 TQString Article::title()
const
331 return d->archive->title(d->guid);
334 TQString Article::author()
const
336 return d->archive->author(d->guid);
339 KURL Article::link()
const
341 return d->archive->link(d->guid);
344 TQString Article::description()
const
346 return d->archive->description(d->guid);
349 TQString Article::guid()
const
354 KURL Article::commentsLink()
const
356 return d->archive->commentsLink(d->guid);
360 int Article::comments()
const
362 return d->archive->comments(d->guid);
366 bool Article::guidIsPermaLink()
const
368 return d->archive->guidIsPermaLink(d->guid);
373 return d->archive->guidIsHash(d->guid);
381 d->hash = d->archive->hash(d->guid);
389 return ( statusBits() & Private::Keep) != 0;
392 RSS::Enclosure Article::enclosure()
const
397 d->archive->enclosure(d->guid, hasEnc, url, type, length);
398 return hasEnc ? RSS::Enclosure(url, length, type) : RSS::Enclosure();
404 void Article::setKeep(
bool keep)
406 d->status =
keep ? ( statusBits() | Private::Keep) : ( statusBits() & ~Private::Keep);
407 d->archive->setStatus(d->guid, d->status);
409 d->feed->setArticleChanged(*
this);
412 void Article::addTag(
const TQString& tag)
414 d->archive->addTag(d->guid, tag);
416 d->feed->setArticleChanged(*
this);
419 void Article::removeTag(
const TQString& tag)
421 d->archive->removeTag(d->guid, tag);
423 d->feed->setArticleChanged(*
this);
426 bool Article::hasTag(
const TQString& tag)
const
428 return d->archive->tags(d->guid).contains(tag);
431 TQStringList Article::tags()
const
433 return d->archive->tags(d->guid);
436 Feed* Article::feed()
const
439 const TQDateTime& Article::pubDate()
const
442 if ( d->pubDate.isNull() )
444 d->pubDate.setTime_t(d->archive->pubDate(d->guid));
450 TQString Article::buildTitle(
const TQString& description)
452 TQString s = description;
453 if (description.stripWhiteSpace().isEmpty())
456 int i = s.find(
'>',500);
459 TQRegExp rx(
"(<([^\\s>]*)(?:[^>]*)>)[^<]*",
false);
460 TQString tagName, toReplace, replaceWith;
461 while (rx.search(s) != -1 )
464 if (tagName==
"SCRIPT"||tagName==
"script")
466 else if (tagName.startsWith(
"br") || tagName.startsWith(
"BR"))
473 s=s.replace(s.find(toReplace),toReplace.length(),replaceWith);
477 return s.simplifyWhiteSpace();
bool guidIsHash() const
returns if the guid is a hash or an ID taken from the source
uint hash() const
returns a hash value used to detect changes in articles with non-hash GUIDs.
bool keep() const
if true, the article should be kept even when expired
const TQString & xmlUrl() const
returns the url of the actual feed source (rss/rdf/atom file)