27 #include <tqdatetime.h> 28 #include <tqlistview.h> 32 #include <tqvaluelist.h> 36 #include <tdeglobal.h> 37 #include <tdestandarddirs.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");
135 if (xmlUrl.isEmpty())
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);
165 feed->setMarkImmediatelyAsRead(markImmediatelyAsRead);
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)
253 if (unread != oldUnread)
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;
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" );
391 parent.appendChild( el );
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()];
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())
virtual void slotAddToFetchQueue(FetchQueue *queue, bool intervalFetchOnly=false)
add this feed to the fetch queue queue
void fetched(Feed *)
emitted when feed finished fetching
static ArchiveMode stringToArchiveMode(const TQString &str)
converts strings to ArchiveMode value if parsing fails, it returns ArchiveMode::globalDefault ...
int fetchInterval() const
Returns custom auto fetch interval of this feed.
bool guidIsHash() const
returns if the guid is a hash or an ID taken from the source
static Feed * fromOPML(TQDomElement e)
creates a Feed object from a description in OPML format
virtual int totalCount() const
returns the number of total articles in this feed
virtual void slotDeleteExpiredArticles()
deletes expired articles
void setFavicon(const TQPixmap &p)
sets the favicon (used in the tree view)
void loadArticles()
loads articles from archive
void setFetchInterval(int interval)
Sets custom auto fetch interval.
ArchiveMode archiveMode() const
returns the archiving mode which is used for this feed
void signalArticlesRemoved(TreeNode *, const TQValueList< Article > &guids)
emitted when articles were removed from this subtree.
void loadFavicon()
downloads the favicon
uint hash() const
returns a hash value used to detect changes in articles with non-hash GUIDs.
virtual void setId(uint id)
sets the ID
virtual TreeNode * nextSibling() const
Get the next sibling.
void setMaxArticleNumber(int maxArticleNumber)
sets the article count limit used in limitArticleNumber archive mode
virtual void setTitle(const TQString &title)
Sets the title of the node.
virtual int unread() const
returns the unread count for this feed
virtual void setNotificationMode(bool doNotify, bool notifyOccurredChanges=true)
virtual const TQString & title() const
Get title of node.
int maxArticleNumber() const
returns the article count limit used in limitArticleNumber archive mode
int maxArticleAge() const
returns the maximum age of articles used for expiration by age (used in limitArticleAge archive mode)...
virtual void nodeModified()
call this if you modified the actual node (title, unread count).
void setHtmlUrl(const TQString &s)
sets the URL of the HTML page of this feed
bool markImmediatelyAsRead() const
if true, new articles are marked immediately as read instead of new/unread.
const TQString & htmlUrl() const
returns the URL of the HTML page of this feed
virtual void slotMarkAllArticlesAsRead()
mark all articles in this feed as read
void setDescription(const TQString &s)
sets the description of this feed
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)...
void fetch(bool followDiscovery=false)
starts fetching
virtual TQDomElement toOPML(TQDomElement parent, TQDomDocument document) const
exports the feed settings to OPML
void fetchError(Feed *)
emitted when a fetch error occurred
virtual void doArticleNotification()
reimplement this in subclasses to do the actual notification called by articlesModified ...
virtual TreeNode * next()
returns the next node in the tree.
void loadImage()
load the image from the cache if it is in there
void fetchDiscovery(Feed *)
emitted when a feed URL was found by auto discovery
virtual Folder * parent() const
Returns the parent node.
virtual TQStringList tags() const
returns a list of all tags occurring in this node (sub tree for folders)
ArchiveMode
the archiving modes:
void fetchAborted(Feed *)
emitted when a fetch is aborted
bool useCustomFetchInterval() const
returns whether this feed uses its own fetch interval or the global setting
void setLoadLinkedWebsite(bool enabled)
if true, the linked URL is loaded directly in the article viewer instead of showing the description ...
static TQString archiveModeToString(ArchiveMode mode)
converts ArchiveMode values to corresponding strings
virtual Article findArticle(const TQString &guid) const
returns the article with the given guid, or a null article if it not exists
virtual void doArticleNotification()
reimplement this in subclasses to do the actual notification called by articlesModified ...
bool isArticlesLoaded() const
returns if the article archive of this feed is loaded
const TQPixmap & favicon() const
returns the favicon
virtual TQValueList< Article > articles(const TQString &tag=TQString())
Returns a sequence of the articles this node contains.
Represents a folder (containing feeds and/or other folders)
bool keep() const
if true, the article should be kept even when expired
void fetchStarted(Feed *)
emitted when fetching started
void setArchiveMode(ArchiveMode archiveMode)
sets the archiving mode for this feed
virtual void articlesModified()
call this if the articles in the node were changed.
A proxy class for RSS::Article with some additional methods to assist sorting.
Feed()
default constructor
const TQString & description() const
returns the description of this feed
void setUnread(int unread)
sets the unread count for this feed
void signalArticlesUpdated(TreeNode *, const TQValueList< Article > &guids)
emitted when articles were updated
void setMaxArticleAge(int maxArticleAge)
sets the maximum age of articles used for expiration by age (used in limitArticleAge archive mode) ...
const TQPixmap & image() const
returns the feed image
const TQString & xmlUrl() const
returns the url of the actual feed source (rss/rdf/atom file)
Abstract base class for all kind of elements in the feed tree, like feeds and feed groups (and search...
void setXmlUrl(const TQString &s)
sets the url of the actual feed source (rss/rdf/atom file)
bool fetchErrorOccurred()
returns whether a fetch error has occurred
void setCustomFetchIntervalEnabled(bool enabled)
set if the feed has its custom fetch interval or uses the global setting