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());
96 void Article::initialize(RSS::Article article, Backend::FeedStorage* archive)
99 d->status = Private::New;
100 d->hash = Utils::calcHash(article.title() + article.description() + article.author() + article.link().url()
101 + article.commentsLink().url() );
103 d->guid = article.guid();
105 if (!d->archive->contains(d->guid))
107 d->archive->addEntry(d->guid);
109 if (article.meta(
"deleted") ==
"true")
111 d->status = Private::Read | Private::Deleted;
112 d->archive->setStatus(d->guid, d->status);
117 d->archive->setHash(d->guid,
hash() );
118 TQString title = article.title().isEmpty() ? buildTitle(article.description()) : article.title();
119 d->archive->setTitle(d->guid, title);
120 d->archive->setDescription(d->guid, article.description());
121 d->archive->setLink(d->guid, article.link().url());
122 d->archive->setComments(d->guid, article.comments());
123 d->archive->setCommentsLink(d->guid, article.commentsLink().url());
124 d->archive->setGuidIsPermaLink(d->guid, article.guidIsPermaLink());
125 d->archive->setGuidIsHash(d->guid, article.meta(
"guidIsHash") ==
"true");
126 d->pubDate = article.pubDate().isValid() ? article.pubDate() : TQDateTime::currentDateTime();
127 d->archive->setPubDate(d->guid, d->pubDate.toTime_t());
128 d->archive->setAuthor(d->guid, article.author());
130 TQValueList<RSS::Category> cats = article.categories();
131 TQValueList<RSS::Category>::ConstIterator end = cats.end();
133 for (TQValueList<RSS::Category>::ConstIterator it = cats.begin(); it != end; ++it)
135 Backend::Category cat;
137 cat.term = (*it).category();
138 cat.scheme = (*it).domain();
139 cat.name = (*it).category();
141 d->archive->addCategory(d->guid, cat);
144 if (!article.enclosure().isNull())
146 d->archive->setEnclosure(d->guid, article.enclosure().url(), article.enclosure().type(), article.enclosure().length());
150 d->archive->removeEnclosure(d->guid);
153 TQString status = article.meta(
"status");
155 if (!status.isEmpty())
157 int statusInt = status.toInt();
158 if (statusInt == New)
160 setStatus(statusInt);
162 setKeep(article.meta(
"keep") ==
"true");
168 d->archive->setComments(d->guid, article.comments());
169 if (
hash() != d->archive->hash(d->guid))
171 d->pubDate.setTime_t(d->archive->pubDate(d->guid));
172 d->archive->setHash(d->guid,
hash() );
173 TQString title = article.title().isEmpty() ? buildTitle(article.description()) : article.title();
174 d->archive->setTitle(d->guid, title);
175 d->archive->setDescription(d->guid, article.description());
176 d->archive->setLink(d->guid, article.link().url());
177 d->archive->setCommentsLink(d->guid, article.commentsLink().url());
178 d->archive->setAuthor(d->guid, article.author());
183 Article::Article(RSS::Article article,
Feed* feed) : d(new Private)
187 initialize(article, Backend::Storage::getInstance()->archiveFor(feed->
xmlUrl()));
190 Article::Article(RSS::Article article, Backend::FeedStorage* archive) : d(new Private)
193 initialize(article, archive);
196 bool Article::isNull()
const
198 return d->archive == 0;
201 void Article::offsetPubDate(
int secs)
203 d->pubDate = pubDate().addSecs(secs);
204 d->archive->setPubDate(d->guid, d->pubDate.toTime_t());
208 void Article::setDeleted()
214 d->status = Private::Deleted | Private::Read;
215 d->archive->setStatus(d->guid, d->status);
216 d->archive->setDeleted(d->guid);
219 d->feed->setArticleDeleted(*
this);
222 bool Article::isDeleted()
const
224 return (statusBits() & Private::Deleted) != 0;
227 Article::Article(
const Article &other) : d(new Private)
241 Article &Article::operator=(
const Article &other)
243 if (
this != &other) {
253 bool Article::operator<(
const Article &other)
const
255 return pubDate() > other.pubDate() ||
256 (pubDate() == other.pubDate() && guid() < other.guid() );
259 bool Article::operator<=(
const Article &other)
const
261 return (pubDate() > other.pubDate() || *
this == other);
264 bool Article::operator>(
const Article &other)
const
266 return pubDate() < other.pubDate() ||
267 (pubDate() == other.pubDate() && guid() > other.guid() );
270 bool Article::operator>=(
const Article &other)
const
272 return (pubDate() > other.pubDate() || *
this == other);
275 bool Article::operator==(
const Article &other)
const
277 return d->guid == other.guid();
280 int Article::statusBits()
const
283 if ( d->status == 0 )
285 d->status = d->archive->status(d->guid);
291 int Article::status()
const
293 if ((statusBits() & Private::Read) != 0)
296 if ((statusBits() & Private::New) != 0)
302 void Article::setStatus(
int stat)
306 int oldStatus = status();
308 if (oldStatus != stat)
313 d->status = ( d->status | Private::Read) & ~Private::New;
316 d->status = ( d->status & ~Private::Read) & ~Private::New;
319 d->status = ( d->status | Private::New) & ~Private::Read;
322 d->archive->setStatus(d->guid, d->status);
324 d->feed->setArticleChanged(*
this, oldStatus);
328 TQString Article::title()
const
330 return d->archive->title(d->guid);
333 TQString Article::author()
const
335 return d->archive->author(d->guid);
338 KURL Article::link()
const
340 return d->archive->link(d->guid);
343 TQString Article::description()
const
345 return d->archive->description(d->guid);
348 TQString Article::guid()
const
353 KURL Article::commentsLink()
const
355 return d->archive->commentsLink(d->guid);
359 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();