From f925bf3a7752e04701e77d9242f47257821855a3 Mon Sep 17 00:00:00 2001 From: Martin Rotter Date: Tue, 6 Jun 2023 07:35:07 +0200 Subject: [PATCH] initial work on supporting embedded categories/labels in feeds/entries --- src/librssguard/core/message.cpp | 11 +++++ src/librssguard/core/message.h | 20 +++++++++ src/librssguard/core/messageobject.cpp | 41 ++++++++++++++++--- src/librssguard/core/messageobject.h | 5 ++- .../services/abstract/serviceroot.cpp | 1 - .../services/standard/parsers/atomparser.cpp | 15 +++++++ .../services/standard/parsers/atomparser.h | 1 + .../services/standard/parsers/feedparser.cpp | 9 ++++ .../services/standard/parsers/feedparser.h | 7 +++- 9 files changed, 101 insertions(+), 9 deletions(-) diff --git a/src/librssguard/core/message.cpp b/src/librssguard/core/message.cpp index 175833b20..882f1a89d 100644 --- a/src/librssguard/core/message.cpp +++ b/src/librssguard/core/message.cpp @@ -65,6 +65,7 @@ QString Enclosures::encodeEnclosuresToString(const QList& enclosures) Message::Message() { m_title = m_url = m_author = m_contents = m_rawContents = m_feedId = m_customId = m_customHash = QL1S(""); m_enclosures = QList(); + m_categories = QList(); m_accountId = m_id = 0; m_score = 0.0; m_isRead = m_isImportant = m_isDeleted = false; @@ -209,3 +210,13 @@ uint qHash(const Message& key, uint seed) { uint qHash(const Message& key) { return (uint(key.m_accountId) * 10000) + uint(key.m_id); } + +MessageCategory::MessageCategory(const QString& title) : QObject(), m_title(title) {} + +MessageCategory::MessageCategory(const MessageCategory& other) { + m_title = other.m_title; +} + +QString MessageCategory::title() const { + return m_title; +} diff --git a/src/librssguard/core/message.h b/src/librssguard/core/message.h index 52a6d1a62..b29d2755e 100644 --- a/src/librssguard/core/message.h +++ b/src/librssguard/core/message.h @@ -31,6 +31,22 @@ class RSSGUARD_DLLSPEC Enclosures { class Feed; +// Represents RSS, JSON or ATOM category (or tag, label - depending on terminology of each entry. +class RSSGUARD_DLLSPEC MessageCategory : public QObject { + Q_OBJECT + + Q_PROPERTY(QString title READ title) + + public: + explicit MessageCategory(const QString& title); + MessageCategory(const MessageCategory& other); + + QString title() const; + + private: + QString m_title; +}; + // Represents single message. class RSSGUARD_DLLSPEC Message { public: @@ -64,6 +80,10 @@ class RSSGUARD_DLLSPEC Message { double m_score; QList m_enclosures; + // List of assigned labels. + // This field is only field when fetching entries of a feed. + QList m_categories; + // List of custom IDs of labels assigned to this message. QList m_assignedLabels; diff --git a/src/librssguard/core/messageobject.cpp b/src/librssguard/core/messageobject.cpp index 3f18926ef..16b575a86 100644 --- a/src/librssguard/core/messageobject.cpp +++ b/src/librssguard/core/messageobject.cpp @@ -8,6 +8,7 @@ #include "definitions/definitions.h" #include "services/abstract/labelsnode.h" +#include #include #include #include @@ -168,7 +169,7 @@ QString MessageObject::findLabelId(const QString& label_title) const { return found_lbl != nullptr ? found_lbl->customId() : QString(); } -QString MessageObject::createLabelId(const QString& title, const QString& hex_color) const { +QString MessageObject::createLabelId(const QString& title, const QString& hex_color) { QString lbl_id = findLabelId(title); if (!lbl_id.isEmpty()) { @@ -176,13 +177,37 @@ QString MessageObject::createLabelId(const QString& title, const QString& hex_co return lbl_id; } - if (hex_color.isEmpty()) { - // Generate color. - return nullptr; + if ((m_account->supportedLabelOperations() & ServiceRoot::LabelOperation::Adding) != + ServiceRoot::LabelOperation::Adding) { + qWarningNN << LOGSEC_CORE << "This account does not support creating labels."; + return nullptr; } - // TODO: CONTINUE - return nullptr; + Label* new_lbl = nullptr; + + try { + auto rnd_color = QRandomGenerator::global()->bounded(0xFFFFFF); + auto rnd_color_name = QSL("#%1").arg(QString::number(rnd_color, 16)); + + new_lbl = new Label(title, hex_color.isEmpty() ? rnd_color_name : hex_color); + QSqlDatabase db = qApp->database()->driver()->threadSafeConnection(metaObject()->className()); + + DatabaseQueries::createLabel(db, new_lbl, m_account->accountId()); + m_account->requestItemReassignment(new_lbl, m_account->labelsNode()); + + m_availableLabels.append(new_lbl); + + return new_lbl->customId(); + } + catch (const ApplicationException& ex) { + qCriticalNN << LOGSEC_CORE << "Cannot create label:" << QUOTE_W_SPACE_DOT(ex.message()); + + if (new_lbl != nullptr) { + new_lbl->deleteLater(); + } + } + + return {}; } void MessageObject::addEnclosure(const QString& url, const QString& mime_type) const { @@ -310,6 +335,10 @@ QList MessageObject::availableLabels() const { return m_availableLabels; } +QList MessageObject::categories() const { + return m_message->m_categories; +} + bool MessageObject::runningFilterWhenFetching() const { return m_runningAfterFetching; } diff --git a/src/librssguard/core/messageobject.h b/src/librssguard/core/messageobject.h index 4b9749834..25f85ae10 100644 --- a/src/librssguard/core/messageobject.h +++ b/src/librssguard/core/messageobject.h @@ -10,6 +10,7 @@ class MessageObject : public QObject { Q_OBJECT + Q_PROPERTY(QList categories READ categories) Q_PROPERTY(QList assignedLabels READ assignedLabels) Q_PROPERTY(QList availableLabels READ availableLabels) Q_PROPERTY(QString feedCustomId READ feedCustomId) @@ -93,7 +94,7 @@ class MessageObject : public QObject { // Returns label custom ID given label title or creates // the label if it does not exist. - Q_INVOKABLE QString createLabelId(const QString& title, const QString& hex_color = {}) const; + Q_INVOKABLE QString createLabelId(const QString& title, const QString& hex_color = {}); // Add multimedia attachment to the message. Q_INVOKABLE void addEnclosure(const QString& url, const QString& mime_type) const; @@ -102,6 +103,8 @@ class MessageObject : public QObject { QList assignedLabels() const; QList availableLabels() const; + QList categories() const; + bool runningFilterWhenFetching() const; // Generic Message's properties bindings. diff --git a/src/librssguard/services/abstract/serviceroot.cpp b/src/librssguard/services/abstract/serviceroot.cpp index f3eae8205..18a74ca69 100644 --- a/src/librssguard/services/abstract/serviceroot.cpp +++ b/src/librssguard/services/abstract/serviceroot.cpp @@ -1015,6 +1015,5 @@ QPair ServiceRoot::updateMessages(QList& messages, Feed* feed } // NOTE: Do not update model items here. We update only once when all feeds are fetched. - return updated_messages; } diff --git a/src/librssguard/services/standard/parsers/atomparser.cpp b/src/librssguard/services/standard/parsers/atomparser.cpp index cf33568c0..7286faca7 100644 --- a/src/librssguard/services/standard/parsers/atomparser.cpp +++ b/src/librssguard/services/standard/parsers/atomparser.cpp @@ -128,3 +128,18 @@ QList AtomParser::xmlMessageEnclosures(const QDomElement& msg_element return enclosures; } + +QList AtomParser::xmlMessageCategories(const QDomElement& msg_element) const { + QList cats; + QDomNodeList elem_cats = msg_element.toElement().elementsByTagNameNS(m_atomNamespace, QSL("category")); + + for (int i = 0; i < elem_cats.size(); i++) { + QDomElement cat = elem_cats.at(i).toElement(); + QString lbl = cat.attribute(QSL("label")); + QString term = cat.attribute(QSL("term")); + + cats.append(MessageCategory(lbl.isEmpty() ? term : lbl)); + } + + return cats; +} diff --git a/src/librssguard/services/standard/parsers/atomparser.h b/src/librssguard/services/standard/parsers/atomparser.h index 861218480..2e6d6f83e 100644 --- a/src/librssguard/services/standard/parsers/atomparser.h +++ b/src/librssguard/services/standard/parsers/atomparser.h @@ -23,6 +23,7 @@ class AtomParser : public FeedParser { virtual QString xmlMessageId(const QDomElement& msg_element) const; virtual QString xmlMessageUrl(const QDomElement& msg_element) const; virtual QList xmlMessageEnclosures(const QDomElement& msg_element) const; + virtual QList xmlMessageCategories(const QDomElement& msg_element) const; virtual QDomNodeList xmlMessageElements(); virtual QString xmlMessageAuthor(const QDomElement& msg_element) const; virtual QString feedAuthor() const; diff --git a/src/librssguard/services/standard/parsers/feedparser.cpp b/src/librssguard/services/standard/parsers/feedparser.cpp index 371451570..b13f7784c 100644 --- a/src/librssguard/services/standard/parsers/feedparser.cpp +++ b/src/librssguard/services/standard/parsers/feedparser.cpp @@ -76,6 +76,10 @@ QList FeedParser::jsonMessageEnclosures(const QJsonObject& msg_elemen return {}; } +QList FeedParser::jsonMessageCategories(const QJsonObject& msg_element) const { + return {}; +} + QString FeedParser::jsonMessageRawContents(const QJsonObject& msg_element) const { return {}; } @@ -105,6 +109,7 @@ QList FeedParser::messages() { new_message.m_rawContents = xmlMessageRawContents(message_item); new_message.m_enclosures = xmlMessageEnclosures(message_item); new_message.m_enclosures.append(xmlMrssGetEnclosures(message_item)); + new_message.m_categories.append(xmlMessageCategories(message_item)); messages.append(new_message); } @@ -322,3 +327,7 @@ QString FeedParser::xmlMessageId(const QDomElement& msg_element) const { QList FeedParser::xmlMessageEnclosures(const QDomElement& msg_element) const { return {}; } + +QList FeedParser::xmlMessageCategories(const QDomElement& msg_element) const { + return {}; +} diff --git a/src/librssguard/services/standard/parsers/feedparser.h b/src/librssguard/services/standard/parsers/feedparser.h index 5a4aaae66..51ea277c3 100644 --- a/src/librssguard/services/standard/parsers/feedparser.h +++ b/src/librssguard/services/standard/parsers/feedparser.h @@ -30,6 +30,7 @@ class FeedParser { virtual QDateTime xmlMessageDateCreated(const QDomElement& msg_element) const; virtual QString xmlMessageId(const QDomElement& msg_element) const; virtual QList xmlMessageEnclosures(const QDomElement& msg_element) const; + virtual QList xmlMessageCategories(const QDomElement& msg_element) const; virtual QString xmlMessageRawContents(const QDomElement& msg_element) const; // JSON. @@ -41,13 +42,17 @@ class FeedParser { virtual QDateTime jsonMessageDateCreated(const QJsonObject& msg_element) const; virtual QString jsonMessageId(const QJsonObject& msg_element) const; virtual QList jsonMessageEnclosures(const QJsonObject& msg_element) const; + virtual QList jsonMessageCategories(const QJsonObject& msg_element) const; virtual QString jsonMessageRawContents(const QJsonObject& msg_element) const; protected: QList xmlMrssGetEnclosures(const QDomElement& msg_element) const; QString xmlMrssTextFromPath(const QDomElement& msg_element, const QString& xml_path) const; QString xmlRawChild(const QDomElement& container) const; - QStringList xmlTextsFromPath(const QDomElement& element, const QString& namespace_uri, const QString& xml_path, bool only_first) const; + QStringList xmlTextsFromPath(const QDomElement& element, + const QString& namespace_uri, + const QString& xml_path, + bool only_first) const; protected: bool m_isXml;