File indexing completed on 2024-05-05 05:13:01

0001 /*
0002     This file is part of Akregator.
0003 
0004     SPDX-FileCopyrightText: 2004 Stanislav Karchebny <Stanislav.Karchebny@kdemail.net>
0005     SPDX-FileCopyrightText: 2005 Frank Osterfeld <osterfeld@kde.org>
0006 
0007     SPDX-License-Identifier: GPL-2.0-or-later WITH Qt-Commercial-exception-1.0
0008 */
0009 
0010 #pragma once
0011 
0012 #include "akregator_export.h"
0013 #include "treenode.h"
0014 
0015 #include <Syndication/Syndication>
0016 
0017 #include <QIcon>
0018 
0019 #include <memory>
0020 
0021 class QDomElement;
0022 class QString;
0023 
0024 namespace Akregator
0025 {
0026 class Article;
0027 class FetchQueue;
0028 class TreeNodeVisitor;
0029 class ArticleDeleteJob;
0030 
0031 namespace Backend
0032 {
0033 class Storage;
0034 }
0035 
0036 class FeedPrivate;
0037 
0038 /** represents a feed */
0039 class AKREGATOR_EXPORT Feed : public TreeNode
0040 {
0041     friend class ::Akregator::Article;
0042     friend class ::Akregator::Folder;
0043     Q_OBJECT
0044 public:
0045     /** the archiving modes */
0046 
0047     enum ArchiveMode {
0048         globalDefault, /**< use default from Settings (default) */
0049         keepAllArticles, /**< Don't delete any articles */
0050         disableArchiving, /**< Don't save any articles except articles with keep flag set (equal to maxArticleNumber() == 0) */
0051         limitArticleNumber, /**< Save maxArticleNumber() articles, plus the ones with keep flag set */
0052         limitArticleAge /**< Save articles not older than maxArticleAge() (or keep flag set) */
0053     };
0054 
0055     struct ImageInfo {
0056         QString imageUrl;
0057         int width = -1;
0058         int height = -1;
0059         bool operator==(const ImageInfo &other) const;
0060         bool operator!=(const ImageInfo &other) const;
0061     };
0062 
0063     // class methods
0064     /** converts strings to ArchiveMode value
0065         if parsing fails, it returns ArchiveMode::globalDefault
0066         */
0067     static ArchiveMode stringToArchiveMode(const QString &str);
0068 
0069     /** converts ArchiveMode values to corresponding strings */
0070     static QString archiveModeToString(ArchiveMode mode);
0071 
0072     /** creates a Feed object from a description in OPML format */
0073     static Feed *fromOPML(const QDomElement &e, Akregator::Backend::Storage *storage);
0074 
0075     /** default constructor */
0076     explicit Feed(Akregator::Backend::Storage *storage);
0077     ~Feed() override;
0078 
0079     [[nodiscard]] bool accept(TreeNodeVisitor *visitor) override;
0080 
0081     /** exports the feed settings to OPML */
0082     [[nodiscard]] QDomElement toOPML(QDomElement parent, QDomDocument document) const override;
0083 
0084     /**
0085         returns whether this feed uses its own fetch interval or the global setting
0086         @return @c true iff this feed has a custom fetch interval
0087         */
0088     [[nodiscard]] bool useCustomFetchInterval() const;
0089 
0090     /** set if the feed has its custom fetch interval or uses the
0091         global setting
0092         @param enabled @c true: use custom interval, @c false: use global default
0093         */
0094     void setCustomFetchIntervalEnabled(bool enabled);
0095 
0096     // FIXME is it -1 or 0 to disable interval fetching?
0097     /** Returns custom auto fetch interval of this feed.
0098     @return custom fetch interval in minutes, 0 if disabled */
0099     [[nodiscard]] int fetchInterval() const;
0100 
0101     /** Sets custom auto fetch interval.
0102     @param interval interval in minutes, -1 for disabling auto fetching */
0103     void setFetchInterval(int interval);
0104 
0105     /** returns the archiving mode which is used for this feed */
0106     [[nodiscard]] ArchiveMode archiveMode() const;
0107 
0108     /** sets the archiving mode for this feed */
0109     void setArchiveMode(ArchiveMode archiveMode);
0110 
0111     /** returns the maximum age of articles used for expiration by age (used in @c limitArticleAge archive mode)
0112     @return expiry age in days */
0113     [[nodiscard]] int maxArticleAge() const;
0114 
0115     /** sets the maximum age of articles used for expiration by age (used in @c limitArticleAge archive mode)
0116     @param maxArticleAge expiry age in days */
0117     void setMaxArticleAge(int maxArticleAge);
0118 
0119     /** returns the article count limit used in @c limitArticleNumber archive mode **/
0120     [[nodiscard]] int maxArticleNumber() const;
0121 
0122     /** sets the article count limit used in @c limitArticleNumber archive mode **/
0123     void setMaxArticleNumber(int maxArticleNumber);
0124 
0125     /** if @c true, new articles are marked immediately as read instead of new/unread. Useful for high-traffic feeds. */
0126     [[nodiscard]] bool markImmediatelyAsRead() const;
0127 
0128     void setMarkImmediatelyAsRead(bool enabled);
0129 
0130     void setUseNotification(bool enabled);
0131 
0132     [[nodiscard]] bool useNotification() const;
0133 
0134     /** if true, the linked URL is loaded directly in the article viewer instead of showing the description */
0135     void setLoadLinkedWebsite(bool enabled);
0136 
0137     [[nodiscard]] bool loadLinkedWebsite() const;
0138 
0139     /** returns the feed logo */
0140     [[nodiscard]] Feed::ImageInfo logoInfo() const;
0141 
0142     /** sets the feed image */
0143     void setLogoInfo(const ImageInfo &image);
0144 
0145     /** returns the url of the actual feed source (rss/rdf/atom file) */
0146     [[nodiscard]] QString xmlUrl() const;
0147     /** sets the url of the actual feed source (rss/rdf/atom file) */
0148     void setXmlUrl(const QString &s);
0149 
0150     /** returns the URL of the HTML page of this feed */
0151     [[nodiscard]] QString htmlUrl() const;
0152     /** sets the URL of the HTML page of this feed */
0153     void setHtmlUrl(const QString &s);
0154 
0155     [[nodiscard]] Feed::ImageInfo faviconInfo() const;
0156     void setFaviconInfo(const Feed::ImageInfo &url);
0157 
0158     /** returns the description of this feed */
0159     [[nodiscard]] QString description() const;
0160 
0161     /** sets the description of this feed */
0162     void setDescription(const QString &s);
0163 
0164     /** returns article by guid
0165      * @param guid the guid of the article to be returned
0166      * @return the article object with the given guid, or a
0167      * null article if non-existent
0168      */
0169     [[nodiscard]] Article findArticle(const QString &guid) const;
0170 
0171     /** returns whether a fetch error has occurred */
0172     [[nodiscard]] bool fetchErrorOccurred() const;
0173 
0174     Syndication::ErrorCode fetchErrorCode() const;
0175 
0176     /** returns the unread count for this feed */
0177     [[nodiscard]] int unread() const override;
0178 
0179     /** returns the number of total articles in this feed
0180     @return number of articles */
0181 
0182     [[nodiscard]] int totalCount() const override;
0183 
0184     /** returns if the article archive of this feed is loaded */
0185     [[nodiscard]] bool isArticlesLoaded() const;
0186 
0187     /** returns if this node is a feed group (@c false here) */
0188     [[nodiscard]] bool isGroup() const override
0189     {
0190         return false;
0191     }
0192 
0193     // impl
0194     [[nodiscard]] bool isAggregation() const override
0195     {
0196         return false;
0197     }
0198 
0199     /** returns the next node in the tree.
0200     Calling next() unless it returns 0 iterates through the tree in pre-order
0201     */
0202     const TreeNode *next() const override;
0203     TreeNode *next() override;
0204 
0205     // impl
0206     [[nodiscard]] QIcon icon() const override;
0207 
0208     /** deletes expired articles */
0209     void deleteExpiredArticles(Akregator::ArticleDeleteJob *job);
0210 
0211     [[nodiscard]] bool isFetching() const;
0212 
0213     QList<const Feed *> feeds() const override;
0214     QList<Feed *> feeds() override;
0215     QList<const Folder *> folders() const override;
0216     QList<Folder *> folders() override;
0217 
0218     KJob *createMarkAsReadJob() override;
0219 
0220     [[nodiscard]] QString comment() const;
0221     void setComment(const QString &comment);
0222     void setFaviconLocalPath(const QString &file);
0223     void setCopyright(const QString &copyright);
0224     [[nodiscard]] QString copyright() const;
0225 public Q_SLOTS:
0226     /** starts fetching */
0227     void fetch(bool followDiscovery = false);
0228 
0229     void slotAbortFetch();
0230 
0231     /** add this feed to the fetch queue @c queue */
0232     void slotAddToFetchQueue(Akregator::FetchQueue *queue, bool intervalFetchOnly = false) override;
0233 
0234     void slotAddFeedIconListener();
0235 
0236 Q_SIGNALS:
0237     /** emitted when fetching started */
0238     void fetchStarted(Akregator::Feed *);
0239     /** emitted when feed finished fetching */
0240     void fetched(Akregator::Feed *);
0241     /** emitted when a fetch error occurred */
0242     void fetchError(Akregator::Feed *);
0243     /** emitted when a feed URL was found by auto discovery */
0244     void fetchDiscovery(Akregator::Feed *);
0245     /** emitted when a fetch is aborted */
0246     void fetchAborted(Akregator::Feed *);
0247 
0248 private:
0249     Akregator::Backend::Storage *storage();
0250 
0251 private:
0252     void setFavicon(const QIcon &icon);
0253     void loadFavicon(const QString &url, bool downloadFavicon);
0254     QList<Article> articles() override;
0255 
0256     /** loads articles from archive **/
0257     void loadArticles();
0258     void enforceLimitArticleNumber();
0259 
0260     void recalcUnreadCount();
0261 
0262     void doArticleNotification() override;
0263 
0264     /** sets the unread count for this feed */
0265     void setUnread(int unread);
0266 
0267     /** notifies that article @c mya was set to "deleted".
0268         To be called by @ref Article
0269         */
0270     void setArticleDeleted(Article &a);
0271 
0272     /** Notifies that article @p a was changed.
0273         @param oldStatus The old status if the status was changed,
0274         or -1 if the status was not changed
0275         @param process Set to @c false to disable processing the change
0276         (updating article list and updating on-screen unread count)
0277         To be called by @ref Article
0278         */
0279     void setArticleChanged(Article &a, int oldStatus = -1, bool process = true);
0280 
0281     void appendArticles(const Syndication::FeedPtr &feed);
0282 
0283     /** appends article @c a to the article list */
0284     void appendArticle(const Article &a);
0285 
0286     /** checks whether article @c a is expired (considering custom and global archive mode settings) */
0287     bool isExpired(const Article &a) const;
0288 
0289     /** returns @c true if either this article uses @c limitArticleAge as custom setting or uses the global default, which is @c limitArticleAge */
0290     bool usesExpiryByAge() const;
0291 
0292     /** executes the actual fetch action */
0293     void tryFetch();
0294 
0295     void markAsFetchedNow();
0296 
0297 private Q_SLOTS:
0298 
0299     void fetchCompleted(Syndication::Loader *loader, Syndication::FeedPtr doc, Syndication::ErrorCode errorCode);
0300 
0301 private:
0302     std::unique_ptr<FeedPrivate> const d;
0303 };
0304 } // namespace Akregator