27 #include <tqdatetime.h>
28 #include <tqlistview.h>
32 #include <tqvaluelist.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");
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 = KGlobal::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;
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" );
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()];
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, TQT_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(KGlobal::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, TQT_SIGNAL(gotPixmap(
const TQPixmap&)),
this, TQT_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())