From c8b1b900df441ce2fc90b79b33672ba4fbe54b4b Mon Sep 17 00:00:00 2001 From: Martin Rotter Date: Thu, 10 Dec 2020 20:53:31 +0100 Subject: [PATCH] Work on #261. --- src/librssguard/core/message.cpp | 40 ++++++++------ src/librssguard/core/message.h | 5 +- src/librssguard/core/messageobject.cpp | 8 +++ src/librssguard/core/messageobject.h | 4 ++ .../gui/dialogs/formmessagefiltersmanager.cpp | 55 ++++++++++++++++++- .../gui/dialogs/formmessagefiltersmanager.h | 3 + .../services/abstract/accountcheckmodel.cpp | 11 +++- .../services/abstract/accountcheckmodel.h | 4 +- 8 files changed, 105 insertions(+), 25 deletions(-) diff --git a/src/librssguard/core/message.cpp b/src/librssguard/core/message.cpp index 9058af5c7..ca17cf4b6 100644 --- a/src/librssguard/core/message.cpp +++ b/src/librssguard/core/message.cpp @@ -66,7 +66,7 @@ Message::Message() { m_title = m_url = m_author = m_contents = m_feedId = m_customId = m_customHash = ""; m_enclosures = QList(); m_accountId = m_id = 0; - m_isRead = m_isImportant = false; + m_isRead = m_isImportant = m_isDeleted = false; m_assignedLabels = QList(); } @@ -98,6 +98,7 @@ Message Message::fromSqlRecord(const QSqlRecord& record, bool* result) { message.m_id = record.value(MSG_DB_ID_INDEX).toInt(); message.m_isRead = record.value(MSG_DB_READ_INDEX).toBool(); message.m_isImportant = record.value(MSG_DB_IMPORTANT_INDEX).toBool(); + message.m_isDeleted = record.value(MSG_DB_DELETED_INDEX).toBool(); message.m_feedId = record.value(MSG_DB_FEED_CUSTOM_ID_INDEX).toString(); message.m_title = record.value(MSG_DB_TITLE_INDEX).toString(); message.m_url = record.value(MSG_DB_URL_INDEX).toString(); @@ -116,19 +117,20 @@ Message Message::fromSqlRecord(const QSqlRecord& record, bool* result) { return message; } -QDataStream& operator<<(QDataStream& out, const Message& myObj) { - out << myObj.m_accountId - << myObj.m_customHash - << myObj.m_customId - << myObj.m_feedId - << myObj.m_id - << myObj.m_isImportant - << myObj.m_isRead; +QDataStream& operator<<(QDataStream& out, const Message& my_obj) { + out << my_obj.m_accountId + << my_obj.m_customHash + << my_obj.m_customId + << my_obj.m_feedId + << my_obj.m_id + << my_obj.m_isImportant + << my_obj.m_isRead + << my_obj.m_isDeleted; return out; } -QDataStream& operator>>(QDataStream& in, Message& myObj) { +QDataStream& operator>>(QDataStream& in, Message& my_obj) { int accountId; QString customHash; QString customId; @@ -136,16 +138,18 @@ QDataStream& operator>>(QDataStream& in, Message& myObj) { int id; bool isImportant; bool isRead; + bool isDeleted; - in >> accountId >> customHash >> customId >> feedId >> id >> isImportant >> isRead; + in >> accountId >> customHash >> customId >> feedId >> id >> isImportant >> isRead >> isDeleted; - myObj.m_accountId = accountId; - myObj.m_customHash = customHash; - myObj.m_customId = customId; - myObj.m_feedId = feedId; - myObj.m_id = id; - myObj.m_isImportant = isImportant; - myObj.m_isRead = isRead; + my_obj.m_accountId = accountId; + my_obj.m_customHash = customHash; + my_obj.m_customId = customId; + my_obj.m_feedId = feedId; + my_obj.m_id = id; + my_obj.m_isImportant = isImportant; + my_obj.m_isRead = isRead; + my_obj.m_isDeleted = isDeleted; return in; } diff --git a/src/librssguard/core/message.h b/src/librssguard/core/message.h index c25fd44bb..340a50ced 100644 --- a/src/librssguard/core/message.h +++ b/src/librssguard/core/message.h @@ -53,6 +53,7 @@ class RSSGUARD_DLLSPEC Message { QString m_customHash; bool m_isRead; bool m_isImportant; + bool m_isDeleted; QList m_enclosures; // List of custom IDs of labels assigned to this message. @@ -75,8 +76,8 @@ inline bool operator!=(const Message& lhs, const Message& rhs) { // Serialize message state. // NOTE: This is used for persistent caching of message state changes. -RSSGUARD_DLLSPEC QDataStream& operator<<(QDataStream& out, const Message& myObj); -RSSGUARD_DLLSPEC QDataStream& operator>>(QDataStream& in, Message& myObj); +RSSGUARD_DLLSPEC QDataStream& operator<<(QDataStream& out, const Message& my_obj); +RSSGUARD_DLLSPEC QDataStream& operator>>(QDataStream& in, Message& my_obj); uint qHash(const Message& key, uint seed); uint qHash(const Message& key); diff --git a/src/librssguard/core/messageobject.cpp b/src/librssguard/core/messageobject.cpp index 490c0f942..091e6d0bd 100755 --- a/src/librssguard/core/messageobject.cpp +++ b/src/librssguard/core/messageobject.cpp @@ -175,6 +175,14 @@ void MessageObject::setIsImportant(bool is_important) { m_message->m_isImportant = is_important; } +bool MessageObject::isDeleted() const { + return m_message->m_isDeleted; +} + +void MessageObject::setIsDeleted(bool is_deleted) { + m_message->m_isDeleted = is_deleted; +} + QString MessageObject::feedCustomId() const { return m_feedCustomId; } diff --git a/src/librssguard/core/messageobject.h b/src/librssguard/core/messageobject.h index 13020a223..ac3e041d8 100755 --- a/src/librssguard/core/messageobject.h +++ b/src/librssguard/core/messageobject.h @@ -21,6 +21,7 @@ class MessageObject : public QObject { Q_PROPERTY(QDateTime created READ created WRITE setCreated) Q_PROPERTY(bool isRead READ isRead WRITE setIsRead) Q_PROPERTY(bool isImportant READ isImportant WRITE setIsImportant) + Q_PROPERTY(bool isDeleted READ isDeleted WRITE setIsDeleted) Q_PROPERTY(bool alreadyStored READ alreadyStored) public: @@ -108,6 +109,9 @@ class MessageObject : public QObject { bool isImportant() const; void setIsImportant(bool is_important); + bool isDeleted() const; + void setIsDeleted(bool is_deleted); + private: QSqlDatabase* m_db; QString m_feedCustomId; diff --git a/src/librssguard/gui/dialogs/formmessagefiltersmanager.cpp b/src/librssguard/gui/dialogs/formmessagefiltersmanager.cpp index 85c869e2e..d26fb0dd8 100644 --- a/src/librssguard/gui/dialogs/formmessagefiltersmanager.cpp +++ b/src/librssguard/gui/dialogs/formmessagefiltersmanager.cpp @@ -17,6 +17,7 @@ #include "network-web/webfactory.h" #include "services/abstract/accountcheckmodel.h" #include "services/abstract/feed.h" +#include "services/abstract/labelsnode.h" FormMessageFiltersManager::FormMessageFiltersManager(FeedReader* reader, const QList& accounts, QWidget* parent) : QDialog(parent), m_feedsModel(new AccountCheckSortedModel(this)), m_rootItem(new RootItem()), @@ -72,6 +73,8 @@ FormMessageFiltersManager::FormMessageFiltersManager(FeedReader* reader, const Q this, &FormMessageFiltersManager::onFeedChecked); connect(m_ui.m_treeFeeds->selectionModel(), &QItemSelectionModel::selectionChanged, this, &FormMessageFiltersManager::displayMessagesOfFeed); + connect(m_ui.m_btnRunOnMessages, &QPushButton::clicked, + this, &FormMessageFiltersManager::processCheckedFeeds); initializeTestingMessage(); loadFilters(); @@ -165,14 +168,17 @@ void FormMessageFiltersManager::testFilter() { m_ui.m_txtErrors->clear(); // Perform per-message filtering. + auto* selected_fd_cat = selectedCategoryFeed(); QJSEngine filter_engine; QSqlDatabase database = qApp->database()->connection(metaObject()->className()); MessageObject msg_obj(&database, - QString::number(NO_PARENT_CATEGORY), + selected_fd_cat->kind() == RootItem::Kind::Feed + ? selected_fd_cat->customId() + : QString::number(NO_PARENT_CATEGORY), selectedAccount() != nullptr ? selectedAccount()->accountId() : NO_PARENT_CATEGORY, - {}); + selected_fd_cat->getParentServiceRoot()->labelsNode()->labels()); auto* fltr = selectedFilter(); MessageFilter::initializeFilteringEngine(filter_engine, &msg_obj); @@ -227,7 +233,7 @@ void FormMessageFiltersManager::testFilter() { } void FormMessageFiltersManager::displayMessagesOfFeed() { - auto* item = m_feedsModel->sourceModel()->itemForIndex(m_feedsModel->mapToSource(m_ui.m_treeFeeds->currentIndex())); + auto* item = selectedCategoryFeed(); if (item != nullptr) { m_msgModel->setMessages(item->undeletedMessages()); @@ -237,6 +243,45 @@ void FormMessageFiltersManager::displayMessagesOfFeed() { } } +void FormMessageFiltersManager::processCheckedFeeds() { + QList checked = m_feedsModel->sourceModel()->checkedItems(); + auto* fltr = selectedFilter(); + QSqlDatabase database = qApp->database()->connection(metaObject()->className()); + + for (const RootItem* it : checked) { + if (it->kind() == RootItem::Kind::Feed) { + QJSEngine filter_engine; + MessageObject msg_obj(&database, + QString(), + selectedAccount() != nullptr ? selectedAccount()->accountId() : NO_PARENT_CATEGORY, + it->getParentServiceRoot()->labelsNode()->labels()); + + MessageFilter::initializeFilteringEngine(filter_engine, &msg_obj); + + // We process messages of the feed. + QList msgs = it->undeletedMessages(); + + for (int i = 0; i < msgs.size(); i++) { + Message& msg = msgs[i]; + + // TODO: create backup of msg and forward changes to + // caching mechanisms if needed. + + MessageObject::FilteringAction result = fltr->filterMessage(&filter_engine); + + if (result == MessageObject::FilteringAction::Purge) { + // TODO: Purge the message completely. + msgs.removeAt(i--); + } + + // Message will be processed/update via SQL layer. + } + + int updated_in_db = it->toFeed()->updateMessages(msgs, false); + } + } +} + void FormMessageFiltersManager::loadAccount(ServiceRoot* account) { m_feedsModel->setRootItem(account, false, true); @@ -395,6 +440,10 @@ void FormMessageFiltersManager::initializeTestingMessage() { m_ui.m_txtSampleCreatedOn->setText(QString::number(QDateTime::currentDateTimeUtc().toMSecsSinceEpoch())); } +RootItem* FormMessageFiltersManager::selectedCategoryFeed() const { + return m_feedsModel->sourceModel()->itemForIndex(m_feedsModel->mapToSource(m_ui.m_treeFeeds->currentIndex())); +} + Message FormMessageFiltersManager::testingMessage() const { Message msg; diff --git a/src/librssguard/gui/dialogs/formmessagefiltersmanager.h b/src/librssguard/gui/dialogs/formmessagefiltersmanager.h index e97e6bce3..367da5f93 100644 --- a/src/librssguard/gui/dialogs/formmessagefiltersmanager.h +++ b/src/librssguard/gui/dialogs/formmessagefiltersmanager.h @@ -32,6 +32,7 @@ class FormMessageFiltersManager : public QDialog { void loadFilters(); void testFilter(); void displayMessagesOfFeed(); + void processCheckedFeeds(); // Load feeds/categories tree. void loadAccount(ServiceRoot* account); @@ -49,6 +50,8 @@ class FormMessageFiltersManager : public QDialog { void loadAccounts(); void beautifyScript(); void initializeTestingMessage(); + + RootItem* selectedCategoryFeed() const; Message testingMessage() const; private: diff --git a/src/librssguard/services/abstract/accountcheckmodel.cpp b/src/librssguard/services/abstract/accountcheckmodel.cpp index d5fac5b04..c403870aa 100644 --- a/src/librssguard/services/abstract/accountcheckmodel.cpp +++ b/src/librssguard/services/abstract/accountcheckmodel.cpp @@ -2,6 +2,7 @@ #include "services/abstract/accountcheckmodel.h" +#include "3rd-party/boolinq/boolinq.h" #include "definitions/definitions.h" #include "miscellaneous/application.h" #include "miscellaneous/iconfactory.h" @@ -273,7 +274,15 @@ Qt::ItemFlags AccountCheckModel::flags(const QModelIndex& index) const { return flags; } -bool AccountCheckModel::isItemChecked(RootItem* item) { +QList AccountCheckModel::checkedItems() const { + auto keys = m_checkStates.keys(); + + return FROM_STD_LIST(QList, boolinq::from(keys).where([&](const auto& key) { + return m_checkStates.value(key) == Qt::CheckState::Checked; + }).toStdList()); +} + +bool AccountCheckModel::isItemChecked(RootItem* item) const { return m_checkStates.value(item, Qt::CheckState::Unchecked) == Qt::CheckState::Checked; } diff --git a/src/librssguard/services/abstract/accountcheckmodel.h b/src/librssguard/services/abstract/accountcheckmodel.h index 4c099334c..8c84d1ac1 100644 --- a/src/librssguard/services/abstract/accountcheckmodel.h +++ b/src/librssguard/services/abstract/accountcheckmodel.h @@ -25,7 +25,9 @@ class AccountCheckModel : public QAbstractItemModel { bool setData(const QModelIndex& index, const QVariant& value, int role); Qt::ItemFlags flags(const QModelIndex& index) const; - bool isItemChecked(RootItem* item); + QList checkedItems() const; + + bool isItemChecked(RootItem* item) const; bool setItemChecked(RootItem* item, Qt::CheckState check); // Returns feed/category which lies at the specified index or