rssguard/src/librssguard/services/abstract/serviceroot.h
2023-08-09 12:41:48 +02:00

331 lines
14 KiB
C++

// For license of this file, see <project-root-folder>/LICENSE.md.
#ifndef SERVICEROOT_H
#define SERVICEROOT_H
#include "services/abstract/rootitem.h"
#include "core/message.h"
#include "core/messagefilter.h"
#include "definitions/typedefs.h"
#include <QJsonDocument>
#include <QNetworkProxy>
#include <QPair>
class QAction;
class QMutex;
class FeedsModel;
class RecycleBin;
class ImportantNode;
class UnreadNode;
class SearchsNode;
class LabelsNode;
class Label;
class MessagesModel;
class CustomMessagePreviewer;
class CacheForServiceRoot;
// THIS IS the root node of the service.
// NOTE: The root usually contains some core functionality of the
// service like service account username/password etc.
class ServiceRoot : public RootItem {
Q_OBJECT
public:
enum class LabelOperation {
Adding = 1,
Editing = 2,
Deleting = 4,
// NOTE: Service fetches list of labels from remote source
// and does not use local offline labels.
Synchronised = 8
};
enum class BagOfMessages {
Read,
Unread,
Starred
};
public:
explicit ServiceRoot(RootItem* parent = nullptr);
virtual ~ServiceRoot();
// These methods bellow are part of "interface".
RecycleBin* recycleBin() const;
ImportantNode* importantNode() const;
LabelsNode* labelsNode() const;
SearchsNode* probesNode() const;
UnreadNode* unreadNode() const;
virtual void updateCounts(bool including_total_count);
virtual bool canBeDeleted() const;
virtual bool deleteViaGui();
virtual bool markAsReadUnread(ReadStatus status);
virtual QList<Message> undeletedMessages() const;
virtual bool supportsFeedAdding() const;
virtual bool supportsCategoryAdding() const;
virtual LabelOperation supportedLabelOperations() const;
virtual QString additionalTooltip() const;
virtual void saveAccountDataToDatabase();
virtual QVariantHash customDatabaseData() const;
virtual void setCustomDatabaseData(const QVariantHash& data);
virtual bool wantsBaggedIdsOfExistingMessages() const;
virtual bool displaysEnclosures() const;
virtual void aboutToBeginFeedFetching(const QList<Feed*>& feeds,
const QHash<QString, QHash<ServiceRoot::BagOfMessages, QStringList>>&
stated_messages,
const QHash<QString, QStringList>& tagged_messages);
// Returns list of specific actions for "Add new item" main window menu.
// So typical list of returned actions could look like:
// a) Add new feed
// b) Add new category
// c) ...
// NOTE: Caller does NOT take ownership of created menu/actions!
virtual QList<QAction*> addItemMenu();
// NOTE: Caller does NOT take ownership of created menu/actions!
virtual QList<QAction*> contextMenuFeedsList();
// NOTE: Caller does NOT take ownership of created menu/actions!
virtual QList<QAction*> contextMenuMessagesList(const QList<Message>& messages);
// Returns list of specific actions to be shown in main window menu
// bar in sections "Services -> 'this service'".
// NOTE: Caller does NOT take ownership of created menu!
virtual QList<QAction*> serviceMenu();
// If plugin uses online synchronization of feeds/labels/etc, then returns true.
virtual bool isSyncable() const;
// Start/stop services.
// Start method is called when feed model gets initialized OR after user adds new service.
// Account should synchronously initialize its children (load them from DB is recommended
// here).
//
// Stop method is called just before application exits OR when
// user explicitly deletes existing service instance.
virtual void start(bool freshly_activated);
virtual void stop();
// Obtains list of messages.
// Throws exception subclassed from ApplicationException, preferably FeedFetchException
// if any problems arise.
virtual QList<Message> obtainNewMessages(Feed* feed,
const QHash<ServiceRoot::BagOfMessages, QStringList>& stated_messages,
const QHash<QString, QStringList>& tagged_messages) = 0;
// Returns special widget to display articles of this account type.
// Caller does NOT free returned previewer after usage from memory,
// it only may hide it.
// Thus, account is responsible to free any custom previewers.
virtual CustomMessagePreviewer* customMessagePreviewer();
// This method should load messages for given "item" into model.
virtual bool loadMessagesForItem(RootItem* item, MessagesModel* model);
// Called BEFORE this read status update (triggered by user in message list) is stored in DB,
// when false is returned, change is aborted.
// This is the place to make some other changes like updating
// some ONLINE service or something.
//
// "read" is status which is ABOUT TO BE SET.
virtual bool onBeforeSetMessagesRead(RootItem* selected_item, const QList<Message>& messages, ReadStatus read);
// Called AFTER this read status update (triggered by user in message list) is stored in DB,
// when false is returned, change is aborted.
// Here service root should inform (via signals)
// which items are actually changed.
//
// "read" is status which is ABOUT TO BE SET.
virtual bool onAfterSetMessagesRead(RootItem* selected_item, const QList<Message>& messages, ReadStatus read);
// Called BEFORE this importance switch update is stored in DB,
// when false is returned, change is aborted.
// This is the place to make some other changes like updating
// some ONLINE service or something.
//
// "changes" - list of pairs - <message (integer id), new status>
virtual bool onBeforeSwitchMessageImportance(RootItem* selected_item, const QList<ImportanceChange>& changes);
// Called AFTER this importance switch update is stored in DB,
// when false is returned, change is aborted.
// Here service root should inform (via signals)
// which items are actually changed.
//
// "changes" - list of pairs - <message (integer id), new status>
virtual bool onAfterSwitchMessageImportance(RootItem* selected_item, const QList<ImportanceChange>& changes);
// Called BEFORE the list of messages is about to be deleted
// by the user from message list.
virtual bool onBeforeMessagesDelete(RootItem* selected_item, const QList<Message>& messages);
// Called AFTER the list of messages was deleted
// by the user from message list.
virtual bool onAfterMessagesDelete(RootItem* selected_item, const QList<Message>& messages);
// Called BEFORE some labels are assigned/deassigned from/to messages.
virtual bool onBeforeLabelMessageAssignmentChanged(const QList<Label*>& labels,
const QList<Message>& messages,
bool assign);
// Called AFTER some labels are assigned/deassigned from/to messages.
virtual bool onAfterLabelMessageAssignmentChanged(const QList<Label*>& labels,
const QList<Message>& messages,
bool assign);
// Called BEFORE the list of messages is about to be restored from recycle bin
// by the user from message list.
// Selected item is naturally recycle bin.
virtual bool onBeforeMessagesRestoredFromBin(RootItem* selected_item, const QList<Message>& messages);
// Called AFTER the list of messages was restored from recycle bin
// by the user from message list.
// Selected item is naturally recycle bin.
virtual bool onAfterMessagesRestoredFromBin(RootItem* selected_item, const QList<Message>& messages);
// Returns the UNIQUE code of the given service.
// NOTE: Keep in sync with ServiceEntryRoot::code().
virtual QString code() const = 0;
public:
CacheForServiceRoot* toCache() const;
// Account ID corresponds with DB attribute Accounts (id).
int accountId() const;
void setAccountId(int account_id);
QNetworkProxy networkProxy() const;
void setNetworkProxy(const QNetworkProxy& network_proxy);
// Removes all data associated with this account from DB
// and from model.
void completelyRemoveAllData();
// Returns counts of updated messages <unread, all>.
QPair<int, int> updateMessages(QList<Message>& messages, Feed* feed, bool force_update, QMutex* db_mutex);
QIcon feedIconForMessage(const QString& feed_custom_id) const;
// Removes all/read only messages from given underlying feeds.
bool cleanFeeds(const QList<Feed*>& items, bool clean_read_only);
// Marks all messages from feeds read/unread.
bool markFeedsReadUnread(const QList<Feed*>& items, ReadStatus read);
// Obvious methods to wrap signals.
void itemChanged(const QList<RootItem*>& items);
void requestReloadMessageList(bool mark_selected_messages_read);
void requestItemExpand(const QList<RootItem*>& items, bool expand);
void requestItemExpandStateSave(RootItem* subtree_root);
void requestItemReassignment(RootItem* item, RootItem* new_parent);
void requestItemRemoval(RootItem* item);
// Some message/feed attribute selectors.
QStringList textualFeedUrls(const QList<Feed*>& feeds) const;
QStringList textualFeedIds(const QList<Feed*>& feeds) const;
QStringList customIDsOfMessages(const QList<ImportanceChange>& changes);
QStringList customIDsOfMessages(const QList<Message>& messages);
// Returns list of article IDs depending on what target operation is.
// NOTE: So if we want to mark some articles as read,
// then we only return UNREAD IDs here to really return
// only IDs when the change makes sense.
// NOTE: Importance is not dealt here because it was not needed
// yet.
QStringList customIDSOfMessagesForItem(RootItem* item,
RootItem::ReadStatus target_read = RootItem::ReadStatus::Unknown);
void performInitialAssembly(const Assignment& categories,
const Assignment& feeds,
const QList<Label*>& labels,
const QList<Search*>& probes);
public slots:
virtual void addNewFeed(RootItem* selected_item, const QString& url = QString());
virtual void addNewCategory(RootItem* selected_item);
virtual void syncIn();
protected:
// This method should obtain new tree of feed/categories/whatever to perform sync in.
virtual RootItem* obtainNewTreeForSyncIn() const;
// Removes all messages/categories/feeds which are
// associated with this account.
void removeOldAccountFromDatabase(bool delete_messages_too, bool delete_labels_too);
void cleanAllItemsFromModel(bool clean_labels_too);
void appendCommonNodes();
// Removes messages which do not belong to any
// existing feed.
//
// NOTE: This situation may happen if user deletes some feed
// from another machine and then performs sync-in on this machine.
void removeLeftOverMessages();
// Removes all msg. filter assignments which are (within this account)
// assigned to feed (via custom ID) which does not exist anymore.
//
// NOTE: This situation may happen if user deletes some feed
// from another machine and then performs sync-in on this machine.
void removeLeftOverMessageFilterAssignments();
// Takes lists of feeds/categories and assembles them into the tree structure.
void assembleCategories(const Assignment& categories);
void assembleFeeds(const Assignment& feeds);
signals:
void proxyChanged(QNetworkProxy proxy);
void dataChanged(QList<RootItem*> items);
void reloadMessageListRequested(bool mark_selected_messages_read);
void itemExpandRequested(QList<RootItem*> items, bool expand);
void itemExpandStateSaveRequested(RootItem* subtree_root);
void itemReassignmentRequested(RootItem* item, RootItem* new_parent);
void itemRemovalRequested(RootItem* item);
private:
void resortAccountTree(RootItem* tree,
const QMap<QString, QVariantMap>& custom_category_data,
const QMap<QString, QVariantMap>& custom_feed_data) const;
// Key is feed's custom ID.
virtual QMap<QString, QVariantMap> storeCustomFeedsData();
// Key is category's custom ID.
virtual QMap<QString, QVariantMap> storeCustomCategoriesData();
virtual void restoreCustomFeedsData(const QMap<QString, QVariantMap>& data, const QHash<QString, Feed*>& feeds);
virtual void restoreCustomCategoriesData(const QMap<QString, QVariantMap>& data,
const QHash<QString, Category*>& cats);
protected:
RecycleBin* m_recycleBin;
ImportantNode* m_importantNode;
LabelsNode* m_labelsNode;
SearchsNode* m_probesNode;
UnreadNode* m_unreadNode;
int m_accountId;
QList<QAction*> m_serviceMenu;
QNetworkProxy m_networkProxy;
};
#if QT_VERSION_MAJOR == 6
inline size_t qHash(ServiceRoot::BagOfMessages key, size_t seed) {
return ::qHash(static_cast<uint>(key), seed);
}
#else
inline uint qHash(ServiceRoot::BagOfMessages key, uint seed) {
return ::qHash(static_cast<uint>(key), seed);
}
#endif
ServiceRoot::LabelOperation operator|(ServiceRoot::LabelOperation lhs, ServiceRoot::LabelOperation rhs);
ServiceRoot::LabelOperation operator&(ServiceRoot::LabelOperation lhs, ServiceRoot::LabelOperation rhs);
#endif // SERVICEROOT_H