27 #include <tqdatetime.h>
28 #include <tqlistview.h>
32 #include <tqvaluelist.h>
36 #include <tdeglobal.h>
37 #include <kstandarddirs.h>
39 #include "akregatorconfig.h"
41 #include "articleinterceptor.h"
44 #include "fetchqueue.h"
45 #include "feediconmanager.h"
46 #include "feedstorage.h"
48 #include "treenodevisitor.h"
51 #include "librss/librss.h"
55 class Feed::FeedPrivate
65 bool loadLinkedWebsite;
78 Backend::FeedStorage* archive;
88 TQMap<TQString, TQStringList> taggedArticles;
91 TQValueList<Article> deletedArticles;
95 TQValueList<Article> addedArticlesNotify;
96 TQValueList<Article> removedArticlesNotify;
97 TQValueList<Article> updatedArticlesNotify;
108 case keepAllArticles:
109 return "keepAllArticles";
110 case disableArchiving:
111 return "disableArchiving";
112 case limitArticleNumber:
113 return "limitArticleNumber";
114 case limitArticleAge:
115 return "limitArticleAge";
117 return "globalDefault";
122 return "globalDefault";
130 if( e.hasAttribute(
"xmlUrl") || e.hasAttribute(
"xmlurl") || e.hasAttribute(
"xmlURL") )
132 TQString
title = e.hasAttribute(
"text") ? e.attribute(
"text") : e.attribute(
"title");
134 TQString
xmlUrl = e.hasAttribute(
"xmlUrl") ? e.attribute(
"xmlUrl") : e.attribute(
"xmlurl");
136 xmlUrl = e.attribute(
"xmlURL");
138 bool useCustomFetchInterval = e.attribute(
"useCustomFetchInterval") ==
"true" || e.attribute(
"autoFetch") ==
"true";
142 TQString
htmlUrl = e.attribute(
"htmlUrl");
149 bool useNotification = e.attribute(
"useNotification") ==
"true";
150 bool loadLinkedWebsite = e.attribute(
"loadLinkedWebsite") ==
"true";
151 uint
id = e.attribute(
"id").toUInt();
161 feed->setUseNotification(useNotification);
174 bool Feed::accept(TreeNodeVisitor* visitor)
176 if (visitor->visitFeed(
this))
179 return visitor->visitTreeNode(
this);
184 return d->archive->tags();
189 return d->articles[guid];
194 if (!d->articlesLoaded)
197 return d->articles.values();
200 TQValueList<Article> tagged;
201 TQStringList guids = d->archive->articles(tag);
202 for (TQStringList::ConstIterator it = guids.begin(); it != guids.end(); ++it)
203 tagged += d->articles[*it];
211 TQString imageFileName = TDEGlobal::dirs()->saveLocation(
"cache",
"akregator/Media/")
212 + Utils::fileNameForUrl(d->xmlUrl) +
214 d->imagePixmap.load(imageFileName,
"PNG");
219 if (d->articlesLoaded)
223 d->archive = Backend::Storage::getInstance()->archiveFor(
xmlUrl());
225 TQStringList list = d->archive->articles();
226 for ( TQStringList::ConstIterator it = list.begin(); it != list.end(); ++it)
229 d->articles[mya.guid()] = mya;
231 d->deletedArticles.append(mya);
234 d->articlesLoaded =
true;
235 enforceLimitArticleNumber();
239 void Feed::recalcUnreadCount()
241 TQValueList<Article> tarticles =
articles();
242 TQValueList<Article>::Iterator it;
243 TQValueList<Article>::Iterator en = tarticles.end();
245 int oldUnread = d->archive->unread();
249 for (it = tarticles.begin(); it != en; ++it)
250 if (!(*it).isDeleted() && (*it).status() != Article::Read)
255 d->archive->setUnread(
unread);
262 if (str ==
"globalDefault")
263 return globalDefault;
264 if (str ==
"keepAllArticles")
265 return keepAllArticles;
266 if (str ==
"disableArchiving")
267 return disableArchiving;
268 if (str ==
"limitArticleNumber")
269 return limitArticleNumber;
270 if (str ==
"limitArticleAge")
271 return limitArticleAge;
273 return globalDefault;
278 d->autoFetch =
false;
279 d->fetchInterval = 30;
280 d->archiveMode = globalDefault;
281 d->maxArticleAge = 60;
282 d->maxArticleNumber = 1000;
283 d->markImmediatelyAsRead =
false;
284 d->useNotification =
false;
285 d->fetchError =
false;
286 d->lastErrorFetch = 0;
289 d->articlesLoaded =
false;
291 d->loadLinkedWebsite =
false;
297 emitSignalDestroyed();
320 void Feed::setMarkImmediatelyAsRead(
bool enabled)
322 d->markImmediatelyAsRead = enabled;
327 void Feed::setUseNotification(
bool enabled)
329 d->useNotification = enabled;
332 bool Feed::useNotification()
const
334 return d->useNotification;
339 d->loadLinkedWebsite = enabled;
342 bool Feed::loadLinkedWebsite()
const
344 return d->loadLinkedWebsite;
368 TQDomElement
Feed::toOPML( TQDomElement parent, TQDomDocument document )
const
370 TQDomElement el = document.createElement(
"outline" );
371 el.setAttribute(
"text",
title() );
372 el.setAttribute(
"title",
title() );
373 el.setAttribute(
"xmlUrl", d->xmlUrl );
374 el.setAttribute(
"htmlUrl", d->htmlUrl );
375 el.setAttribute(
"id", TQString::number(
id()) );
376 el.setAttribute(
"description", d->description );
378 el.setAttribute(
"fetchInterval", TQString::number(
fetchInterval()) );
380 el.setAttribute(
"maxArticleAge", d->maxArticleAge );
381 el.setAttribute(
"maxArticleNumber", d->maxArticleNumber );
382 if (d->markImmediatelyAsRead)
383 el.setAttribute(
"markImmediatelyAsRead",
"true" );
384 if (d->useNotification)
385 el.setAttribute(
"useNotification",
"true" );
386 if (d->loadLinkedWebsite)
387 el.setAttribute(
"loadLinkedWebsite",
"true" );
388 el.setAttribute(
"maxArticleNumber", d->maxArticleNumber );
389 el.setAttribute(
"type",
"rss" );
390 el.setAttribute(
"version",
"RSS" );
400 TQValueList<Article> tarticles =
articles();
401 TQValueList<Article>::Iterator it;
402 TQValueList<Article>::Iterator en = tarticles.end();
404 for (it = tarticles.begin(); it != en; ++it)
405 (*it).setStatus(Article::Read);
411 if (!intervalFetchOnly)
412 queue->addFeed(
this);
415 uint now = TQDateTime::currentDateTime().toTime_t();
428 if ( Settings::useIntervalFetch() )
429 interval = Settings::autoFetchInterval() * 60;
431 uint lastFetch = d->archive->lastFetch();
433 if ( interval > 0 && now - lastFetch >= (uint)interval )
434 queue->addFeed(
this);
439 void Feed::appendArticles(
const RSS::Document &doc)
441 bool changed =
false;
443 RSS::Article::List d_articles = doc.articles();
444 RSS::Article::List::ConstIterator it;
445 RSS::Article::List::ConstIterator en = d_articles.end();
449 TQValueList<Article> deletedArticles = d->deletedArticles;
451 for (it = d_articles.begin(); it != en; ++it)
453 if ( !d->articles.contains((*it).guid()) )
456 mya.offsetPubDate(nudge);
460 TQValueList<ArticleInterceptor*> interceptors = ArticleInterceptorManager::self()->interceptors();
461 for (TQValueList<ArticleInterceptor*>::ConstIterator it = interceptors.begin(); it != interceptors.end(); ++it)
462 (*it)->processArticle(mya);
464 d->addedArticlesNotify.append(mya);
467 mya.setStatus(Article::New);
469 mya.setStatus(Article::Read);
476 Article old = d->articles[(*it).guid()];
477 Article mya(*it,
this);
478 if (!mya.guidIsHash() && mya.hash() != old.hash() && !old.isDeleted())
480 mya.setKeep(old.keep());
481 int oldstatus = old.status();
482 old.setStatus(Article::Read);
484 d->articles.remove(old.guid());
487 mya.setStatus(oldstatus);
489 d->updatedArticlesNotify.append(mya);
492 else if (old.isDeleted())
493 deletedArticles.remove(mya);
497 TQValueList<Article>::ConstIterator dit = deletedArticles.begin();
498 TQValueList<Article>::ConstIterator dtmp;
499 TQValueList<Article>::ConstIterator den = deletedArticles.end();
506 d->articles.remove((*dtmp).guid());
507 d->archive->deleteArticle((*dtmp).guid());
508 d->deletedArticles.remove(*dtmp);
515 bool Feed::usesExpiryByAge()
const
517 return ( d->archiveMode == globalDefault && Settings::archiveMode() == Settings::EnumArchiveMode::limitArticleAge) || d->archiveMode == limitArticleAge;
520 bool Feed::isExpired(
const Article& a)
const
522 TQDateTime now = TQDateTime::currentDateTime();
525 if ( d->archiveMode == globalDefault && Settings::archiveMode() == Settings::EnumArchiveMode::limitArticleAge)
526 expiryAge = Settings::maxArticleAge() *24*3600;
528 if ( d->archiveMode == limitArticleAge)
529 expiryAge = d->maxArticleAge *24*3600;
531 return ( expiryAge != -1 && a.pubDate().secsTo(now) > expiryAge);
534 void Feed::appendArticle(
const Article& a)
536 if ( (a.keep() && Settings::doNotExpireImportantArticles()) || ( !usesExpiryByAge() || !isExpired(a) ) )
538 if (!d->articles.contains(a.guid()))
540 d->articles[a.guid()] = a;
541 if (!a.isDeleted() && a.status() != Article::Read)
550 d->followDiscovery = followDiscovery;
554 TQValueList<Article>
articles = d->articles.values();
555 TQValueList<Article>::Iterator it;
556 TQValueList<Article>::Iterator en =
articles.end();
557 for (it =
articles.begin(); it != en; ++it)
559 if ((*it).status() == Article::New)
561 (*it).setStatus(Article::Unread);
570 void Feed::slotAbortFetch()
578 void Feed::tryFetch()
580 d->fetchError =
false;
582 d->loader = RSS::Loader::create(
this, TQ_SLOT(fetchCompleted(Loader *, Document, Status)) );
584 d->loader->loadFrom( d->xmlUrl,
new RSS::FileRetriever );
587 void Feed::slotImageFetched(
const TQPixmap& image)
591 d->imagePixmap=
image;
592 d->imagePixmap.save(TDEGlobal::dirs()->saveLocation(
"cache",
"akregator/Media/")
593 + Utils::fileNameForUrl(d->xmlUrl) +
598 void Feed::fetchCompleted(RSS::Loader *l, RSS::Document doc, RSS::Status status)
604 if (status != RSS::Success)
606 if (status == RSS::Aborted)
608 d->fetchError =
false;
611 else if (d->followDiscovery && (status == RSS::ParseError) && (d->fetchTries < 3) && (l->discoveredFeedURL().isValid()))
614 d->xmlUrl = l->discoveredFeedURL().url();
620 d->fetchError =
true;
621 d->lastErrorFetch = TQDateTime::currentDateTime().toTime_t();
630 if (d->favicon.isNull())
633 d->fetchError =
false;
635 if (doc.image() && d->imagePixmap.isNull())
637 d->image = *doc.image();
638 connect(&d->image, TQ_SIGNAL(gotPixmap(
const TQPixmap&)),
this, TQ_SLOT(slotImageFetched(
const TQPixmap&)));
639 d->image.getPixmap();
642 if (
title().isEmpty())
645 d->description = doc.description();
646 d->htmlUrl = doc.link().url();
650 d->archive->setLastFetch( TQDateTime::currentDateTime().toTime_t());
656 FeedIconManager::self()->fetchIcon(
this);
661 if ( !usesExpiryByAge() )
664 TQValueList<Article>
articles = d->articles.values();
666 TQValueList<Article>::Iterator en =
articles.end();
673 if (Settings::doNotExpireImportantArticles())
675 for (TQValueList<Article>::Iterator it =
articles.begin(); it != en; ++it)
677 if (!(*it).keep() && isExpired(*it))
685 for (TQValueList<Article>::Iterator it =
articles.begin(); it != en; ++it)
704 return d->archiveMode;
714 return d->archive ? d->archive->unread() : 0;
719 if (d->archive &&
unread != d->archive->unread())
721 d->archive->setUnread(
unread);
727 void Feed::setArticleDeleted(
Article& a)
729 if (!d->deletedArticles.contains(a))
730 d->deletedArticles.append(a);
732 if (!d->removedArticlesNotify.contains(a))
733 d->removedArticlesNotify.append(a);
738 void Feed::setArticleChanged(Article& a,
int oldStatus)
742 int newStatus = a.status();
743 if (oldStatus == Article::Read && newStatus != Article::Read)
745 else if (oldStatus != Article::Read && newStatus == Article::Read)
748 d->updatedArticlesNotify.append(a);
754 return d->articles.count();
775 if (!d->addedArticlesNotify.isEmpty())
779 TQValueList<Article> l = d->addedArticlesNotify;
781 d->addedArticlesNotify.clear();
783 if (!d->updatedArticlesNotify.isEmpty())
787 TQValueList<Article> l = d->updatedArticlesNotify;
789 d->updatedArticlesNotify.clear();
791 if (!d->removedArticlesNotify.isEmpty())
795 TQValueList<Article> l = d->removedArticlesNotify;
797 d->removedArticlesNotify.clear();
802 void Feed::enforceLimitArticleNumber()
805 if (d->archiveMode == globalDefault && Settings::archiveMode() == Settings::EnumArchiveMode::limitArticleNumber)
806 limit = Settings::maxArticleNumber();
807 else if (d->archiveMode == limitArticleNumber)
810 if (limit == -1 || limit >= d->articles.count() - d->deletedArticles.count())
814 TQValueList<Article>
articles = d->articles.values();
816 TQValueList<Article>::Iterator it =
articles.begin();
817 TQValueList<Article>::Iterator tmp;
818 TQValueList<Article>::Iterator en =
articles.end();
822 if (Settings::doNotExpireImportantArticles())
830 if (!(*tmp).isDeleted() && !(*tmp).keep())
833 else if (!(*tmp).keep())
843 if (c < limit && !(*tmp).isDeleted())
A proxy class for RSS::Article with some additional methods to assist sorting.
virtual int unread() const
returns the unread count for this feed
void loadImage()
load the image from the cache if it is in there
void setArchiveMode(ArchiveMode archiveMode)
sets the archiving mode for this feed
virtual TQValueList< Article > articles(const TQString &tag=TQString())
Returns a sequence of the articles this node contains.
void fetchError(Feed *)
emitted when a fetch error occurred
ArchiveMode
the archiving modes:
bool markImmediatelyAsRead() const
if true, new articles are marked immediately as read instead of new/unread.
void setDescription(const TQString &s)
sets the description of this feed
bool fetchErrorOccurred()
returns whether a fetch error has occurred
virtual TreeNode * next()
returns the next node in the tree.
void loadFavicon()
downloads the favicon
const TQPixmap & favicon() const
returns the favicon
void loadArticles()
loads articles from archive
int fetchInterval() const
Returns custom auto fetch interval of this feed.
virtual int totalCount() const
returns the number of total articles in this feed
void setLoadLinkedWebsite(bool enabled)
if true, the linked URL is loaded directly in the article viewer instead of showing the description
void fetchStarted(Feed *)
emitted when fetching started
void fetchDiscovery(Feed *)
emitted when a feed URL was found by auto discovery
void setMaxArticleAge(int maxArticleAge)
sets the maximum age of articles used for expiration by age (used in limitArticleAge archive mode)
const TQString & description() const
returns the description of this feed
virtual void doArticleNotification()
reimplement this in subclasses to do the actual notification called by articlesModified
virtual void slotAddToFetchQueue(FetchQueue *queue, bool intervalFetchOnly=false)
add this feed to the fetch queue queue
void setMaxArticleNumber(int maxArticleNumber)
sets the article count limit used in limitArticleNumber archive mode
static TQString archiveModeToString(ArchiveMode mode)
converts ArchiveMode values to corresponding strings
int maxArticleAge() const
returns the maximum age of articles used for expiration by age (used in limitArticleAge archive mode)
const TQPixmap & image() const
returns the feed image
bool isArticlesLoaded() const
returns if the article archive of this feed is loaded
Feed()
default constructor
static Feed * fromOPML(TQDomElement e)
creates a Feed object from a description in OPML format
const TQString & xmlUrl() const
returns the url of the actual feed source (rss/rdf/atom file)
void setXmlUrl(const TQString &s)
sets the url of the actual feed source (rss/rdf/atom file)
void setUnread(int unread)
sets the unread count for this feed
bool useCustomFetchInterval() const
returns whether this feed uses its own fetch interval or the global setting
virtual TQDomElement toOPML(TQDomElement parent, TQDomDocument document) const
exports the feed settings to OPML
int maxArticleNumber() const
returns the article count limit used in limitArticleNumber archive mode
virtual TQStringList tags() const
returns a list of all tags occurring in this node (sub tree for folders)
void setFetchInterval(int interval)
Sets custom auto fetch interval.
void fetchAborted(Feed *)
emitted when a fetch is aborted
void fetched(Feed *)
emitted when feed finished fetching
ArchiveMode archiveMode() const
returns the archiving mode which is used for this feed
virtual void slotDeleteExpiredArticles()
deletes expired articles
void fetch(bool followDiscovery=false)
starts fetching
virtual void slotMarkAllArticlesAsRead()
mark all articles in this feed as read
static ArchiveMode stringToArchiveMode(const TQString &str)
converts strings to ArchiveMode value if parsing fails, it returns ArchiveMode::globalDefault
const TQString & htmlUrl() const
returns the URL of the HTML page of this feed
void setFavicon(const TQPixmap &p)
sets the favicon (used in the tree view)
void setHtmlUrl(const TQString &s)
sets the URL of the HTML page of this feed
virtual Article findArticle(const TQString &guid) const
returns the article with the given guid, or a null article if it not exists
void setCustomFetchIntervalEnabled(bool enabled)
set if the feed has its custom fetch interval or uses the global setting
Represents a folder (containing feeds and/or other folders)
virtual void appendChild(TreeNode *node)
inserts node as last child
Abstract base class for all kind of elements in the feed tree, like feeds and feed groups (and search...
virtual TreeNode * nextSibling() const
Get the next sibling.
virtual void setTitle(const TQString &title)
Sets the title of the node.
void signalArticlesAdded(TreeNode *node, const TQValueList< Article > &guids)
emitted when new articles were added to this node or any node in the subtree (for folders).
virtual void setNotificationMode(bool doNotify, bool notifyOccurredChanges=true)
void signalArticlesUpdated(TreeNode *, const TQValueList< Article > &guids)
emitted when articles were updated
virtual void doArticleNotification()
reimplement this in subclasses to do the actual notification called by articlesModified
virtual void articlesModified()
call this if the articles in the node were changed.
void signalArticlesRemoved(TreeNode *, const TQValueList< Article > &guids)
emitted when articles were removed from this subtree.
virtual void setId(uint id)
sets the ID
virtual const TQString & title() const
Get title of node.
virtual void nodeModified()
call this if you modified the actual node (title, unread count).
virtual Folder * parent() const
Returns the parent node.