From ec9e6422ff8a3faca539379c670e33645f0e571d Mon Sep 17 00:00:00 2001 From: Martin Rotter Date: Tue, 11 Apr 2017 09:49:44 +0200 Subject: [PATCH] Fixed #109 and made better RSS parser - fixed #104. --- resources/text/CHANGELOG | 3 +- resources/text/UPDATES | 6 - src/definitions/definitions.h | 3 +- src/gui/dialogs/formupdate.cpp | 90 ++++++++----- src/gui/dialogs/formupdate.h | 9 +- src/gui/dialogs/formupdate.ui | 117 +++++++++++------ src/miscellaneous/systemfactory.cpp | 67 +++++----- src/miscellaneous/systemfactory.h | 13 +- src/services/standard/feedparser.cpp | 4 + src/services/standard/feedparser.h | 2 +- src/services/standard/rssparser.cpp | 171 ++++++++++++------------- src/services/standard/rssparser.h | 10 +- src/services/standard/standardfeed.cpp | 2 +- 13 files changed, 280 insertions(+), 217 deletions(-) delete mode 100644 resources/text/UPDATES diff --git a/resources/text/CHANGELOG b/resources/text/CHANGELOG index fcb65f7d4..314908986 100755 --- a/resources/text/CHANGELOG +++ b/resources/text/CHANGELOG @@ -5,7 +5,8 @@ Added: ▪ Auto-update status of feeds is now more general and complete. (issue #91) Changed: -▪ Better ATOM parsing, respects now XML namespaces, bit better link parsing and other stuff. (issue #104) +▪ Updating of RSS Guard now uses GitHub API. (issue #109) +▪ Better ATOM/RSS parsing, respects now XML namespaces, bit better link parsing and other stuff. (issue #104) ▪ Folder which holds SQL scripts got renamed to "sql". ▪ Tweaked some conditions for determining newly "updated" messages in ATOM format. (issue #103) diff --git a/resources/text/UPDATES b/resources/text/UPDATES deleted file mode 100644 index f9c7c4339..000000000 --- a/resources/text/UPDATES +++ /dev/null @@ -1,6 +0,0 @@ - - - - https://github.com/martinrotter/rssguard/releases/download/3.4.0/rssguard-3.4.0-cb15ba92-win32.exe - - diff --git a/src/definitions/definitions.h b/src/definitions/definitions.h index 05ba72ba8..01c9e1fb5 100755 --- a/src/definitions/definitions.h +++ b/src/definitions/definitions.h @@ -31,8 +31,7 @@ #define URI_SCHEME_FEED_SHORT "feed:" #define URI_SCHEME_FEED "feed://" #define URI_SCHEME_HTTP "http://" -#define RELEASES_LIST "https://raw.githubusercontent.com/martinrotter/rssguard/master/resources/text/UPDATES" -#define CHANGELOG "https://raw.githubusercontent.com/martinrotter/rssguard/master/resources/text/CHANGELOG" +#define RELEASES_LIST "https://api.github.com/repos/martinrotter/rssguard/releases" #define DEFAULT_LOCALE "en" #define DEFAULT_FEED_ENCODING "UTF-8" #define DEFAULT_FEED_TYPE "RSS" diff --git a/src/gui/dialogs/formupdate.cpp b/src/gui/dialogs/formupdate.cpp index 02df02182..5d1dc8456 100755 --- a/src/gui/dialogs/formupdate.cpp +++ b/src/gui/dialogs/formupdate.cpp @@ -34,16 +34,25 @@ FormUpdate::FormUpdate(QWidget *parent) - : QDialog(parent), m_downloader(nullptr), m_readyToInstall(false), m_ui(new Ui::FormUpdate) { + : QDialog(parent), m_downloader(nullptr), m_readyToInstall(false), m_ui(new Ui::FormUpdate), m_lastDownloadedBytes(0) { m_ui->setupUi(this); - m_btnUpdate = m_ui->m_buttonBox->addButton(tr("Download update"), QDialogButtonBox::ActionRole); - m_btnUpdate->setToolTip(tr("Download new installation files.")); m_ui->m_lblCurrentRelease->setText(APP_VERSION); + m_ui->m_tabInfo->removeTab(1); // Set flags and attributes. setWindowFlags(Qt::MSWindowsFixedSizeDialogHint | Qt::Dialog | Qt::WindowSystemMenuHint | Qt::WindowTitleHint); setWindowIcon(qApp->icons()->fromTheme(QSL("help-about"))); + if (isSelfUpdateSupported()) { + m_btnUpdate = m_ui->m_buttonBox->addButton(tr("Download selected update"), QDialogButtonBox::ActionRole); + m_btnUpdate->setToolTip(tr("Download new installation files.")); + } + else { + m_btnUpdate = m_ui->m_buttonBox->addButton(tr("Go to application website"), QDialogButtonBox::ActionRole); + m_btnUpdate->setToolTip(tr("Go to application website to get update packages manually.")); + } + + m_btnUpdate->setVisible(false); connect(m_btnUpdate, &QPushButton::clicked, this, &FormUpdate::startUpdate); checkForUpdates(); } @@ -51,12 +60,8 @@ FormUpdate::FormUpdate(QWidget *parent) FormUpdate::~FormUpdate() { } -bool FormUpdate::isUpdateForThisSystem() const { - return m_updateInfo.m_urls.keys().contains(OS_ID); -} - bool FormUpdate::isSelfUpdateSupported() const { -#if defined(Q_OS_WIN) +#if defined(Q_OS_WIN) || defined(Q_OS_MAC) return true; #else return false; @@ -64,51 +69,47 @@ bool FormUpdate::isSelfUpdateSupported() const { } void FormUpdate::checkForUpdates() { - const QPair update = qApp->system()->checkForUpdates(); - - m_updateInfo = update.first; + const QPair, QNetworkReply::NetworkError> update = qApp->system()->checkForUpdates(); if (update.second != QNetworkReply::NoError) { + m_updateInfo = UpdateInfo(); + m_ui->m_tabInfo->setEnabled(false); + //: Unknown release. m_ui->m_lblAvailableRelease->setText(tr("unknown")); m_ui->m_txtChanges->clear(); m_ui->m_lblStatus->setStatus(WidgetWithStatus::Error, tr("Error: '%1'.").arg(NetworkFactory::networkErrorText(update.second)), tr("List with updates was not\ndownloaded successfully.")); - m_btnUpdate->setEnabled(false); - m_btnUpdate->setToolTip(tr("Checking for updates failed.")); } else { - m_ui->m_lblAvailableRelease->setText(update.first.m_availableVersion); - m_ui->m_txtChanges->setText(update.first.m_changes); + const bool self_update_supported = isSelfUpdateSupported(); - const bool is_self_update_for_this_system = isUpdateForThisSystem() && isSelfUpdateSupported(); + m_updateInfo = update.first.at(0); + m_ui->m_tabInfo->setEnabled(true); + m_ui->m_lblAvailableRelease->setText(m_updateInfo.m_availableVersion); + m_ui->m_txtChanges->setText(m_updateInfo.m_changes); - if (SystemFactory::isVersionNewer(update.first.m_availableVersion, APP_VERSION)) { + if (SystemFactory::isVersionNewer(m_updateInfo.m_availableVersion, APP_VERSION)) { + m_btnUpdate->setVisible(true); m_ui->m_lblStatus->setStatus(WidgetWithStatus::Ok, tr("New release available."), tr("This is new version which can be\ndownloaded.")); - m_btnUpdate->setEnabled(true); - m_btnUpdate->setToolTip(is_self_update_for_this_system ? - tr("Download installation file for your OS.") : - tr("Installation file is not available directly.\n" - "Go to application website to obtain it manually.")); - m_btnUpdate->setText(is_self_update_for_this_system ? tr("Download update") : tr("Go to application website")); + if (self_update_supported) { + loadAvailableFiles(); + } } else { m_ui->m_lblStatus->setStatus(WidgetWithStatus::Warning, tr("No new release available."), tr("This release is not newer than\ncurrently installed one.")); - m_btnUpdate->setEnabled(false); - m_btnUpdate->setToolTip(tr("No new update available.")); } } } void FormUpdate::updateProgress(qint64 bytes_received, qint64 bytes_total) { - if (bytes_received % 10 == 0) { - qApp->processEvents(); + if (bytes_received - m_lastDownloadedBytes > 500000 || m_lastDownloadedBytes == 0) { m_ui->m_lblStatus->setStatus(WidgetWithStatus::Information, tr("Downloaded %1% (update size is %2 kB).").arg(QString::number(bytes_total == 0 ? 0 : (bytes_received * 100.0) / bytes_total, 'f', @@ -117,11 +118,14 @@ void FormUpdate::updateProgress(qint64 bytes_received, qint64 bytes_total) { 'f', 2)), tr("Downloading update...")); + m_ui->m_lblStatus->repaint(); + + m_lastDownloadedBytes = bytes_received; } } void FormUpdate::saveUpdateFile(const QByteArray &file_contents) { - const QString url_file = m_updateInfo.m_urls.value(OS_ID).m_fileUrl;; + const QString url_file = m_ui->m_listFiles->currentItem()->data(Qt::UserRole).toString(); const QString temp_directory = qApp->getTempFolderPath(); if (!temp_directory.isEmpty()) { @@ -151,13 +155,34 @@ void FormUpdate::saveUpdateFile(const QByteArray &file_contents) { } } +void FormUpdate::loadAvailableFiles() { + m_ui->m_listFiles->clear(); + + foreach (const UpdateUrl &url, m_updateInfo.m_urls) { + QListWidgetItem *item = new QListWidgetItem(url.m_name + tr(" (size ") + url.m_size + QSL(")")); + item->setData(Qt::UserRole, url.m_fileUrl); + item->setToolTip(url.m_fileUrl); + m_ui->m_listFiles->addItem(item); + } + + if (m_ui->m_listFiles->count() > 0) { + m_ui->m_listFiles->setCurrentRow(0); + } + else { + m_btnUpdate->setEnabled(false); + } + + m_ui->m_tabInfo->addTab(m_ui->tabFiles, tr("Available update files")); + m_ui->m_tabInfo->setCurrentIndex(1); +} + void FormUpdate::updateCompleted(QNetworkReply::NetworkError status, QByteArray contents) { qDebug("Download of application update file was completed with code '%d'.", status); switch (status) { case QNetworkReply::NoError: saveUpdateFile(contents); - m_ui->m_lblStatus->setStatus(WidgetWithStatus::Ok, tr("Downloaded successfully"), tr("Package was downloaded successfully.\nYou must install it manually.")); + m_ui->m_lblStatus->setStatus(WidgetWithStatus::Ok, tr("Downloaded successfully"), tr("Package was downloaded successfully.\nYou can install it now.")); m_btnUpdate->setText(tr("Install")); m_btnUpdate->setEnabled(true); break; @@ -171,10 +196,11 @@ void FormUpdate::updateCompleted(QNetworkReply::NetworkError status, QByteArray void FormUpdate::startUpdate() { QString url_file; - const bool update_for_this_system = isUpdateForThisSystem() && isSelfUpdateSupported(); + const bool update_for_this_system = isSelfUpdateSupported(); - if (update_for_this_system) { - url_file = m_updateInfo.m_urls.value(OS_ID).m_fileUrl; + if (update_for_this_system && m_ui->m_listFiles->currentItem() != nullptr) { + url_file = m_ui->m_listFiles->currentItem()->data(Qt::UserRole).toString(); + m_ui->m_listFiles->setEnabled(false); } else { url_file = APP_URL; diff --git a/src/gui/dialogs/formupdate.h b/src/gui/dialogs/formupdate.h index 84de91fe6..58dc21451 100755 --- a/src/gui/dialogs/formupdate.h +++ b/src/gui/dialogs/formupdate.h @@ -38,15 +38,11 @@ class FormUpdate : public QDialog { explicit FormUpdate(QWidget *parent = 0); virtual ~FormUpdate(); - // Returns true if current update provides - // installation file for current platform. - bool isUpdateForThisSystem() const; - // Returns true if application can self-update // on current platform. bool isSelfUpdateSupported() const; - protected slots: + private slots: // Check for updates and interprets the results. void checkForUpdates(); void startUpdate(); @@ -56,12 +52,15 @@ class FormUpdate : public QDialog { void saveUpdateFile(const QByteArray &file_contents); private: + void loadAvailableFiles(); + Downloader *m_downloader; bool m_readyToInstall; QString m_updateFilePath; QScopedPointer m_ui; UpdateInfo m_updateInfo; QPushButton *m_btnUpdate; + qint64 m_lastDownloadedBytes; }; #endif // FORMUPDATE_H diff --git a/src/gui/dialogs/formupdate.ui b/src/gui/dialogs/formupdate.ui index ba145bbf7..ddc829fd6 100755 --- a/src/gui/dialogs/formupdate.ui +++ b/src/gui/dialogs/formupdate.ui @@ -7,7 +7,7 @@ 0 0 607 - 366 + 326 @@ -15,10 +15,7 @@ - - - QFormLayout::AllNonFixedFieldsGrow - + @@ -40,16 +37,6 @@ - - - - Changes - - - m_txtChanges - - - @@ -75,37 +62,85 @@ - - - - 0 - 1 - + + + 0 - - - DejaVu Sans Mono - - - - false - - - QTextEdit::WidgetWidth - - - true - - - <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> + + + Changelog + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 1 + + + + + DejaVu Sans Mono + + + + false + + + QTextEdit::WidgetWidth + + + true + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'DejaVu Sans Mono'; font-size:8.25pt; font-weight:400; font-style:normal;"> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'MS Shell Dlg 2'; font-size:8pt;"><br /></p></body></html> - - - Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse - + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + + + + + + + + Available files + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + diff --git a/src/miscellaneous/systemfactory.cpp b/src/miscellaneous/systemfactory.cpp index 9c3b142fd..96f3f2cd4 100755 --- a/src/miscellaneous/systemfactory.cpp +++ b/src/miscellaneous/systemfactory.cpp @@ -28,9 +28,9 @@ #include #include #include -#include -#include -#include +#include +#include +#include #include #include #include @@ -205,18 +205,15 @@ QString SystemFactory::getUsername() const { return name; } -QPair SystemFactory::checkForUpdates() const { - QPair result; - QByteArray releases_xml; - QByteArray changelog; +QPair, QNetworkReply::NetworkError> SystemFactory::checkForUpdates() const { + QPair, QNetworkReply::NetworkError> result; + QByteArray releases_json; result.second = NetworkFactory::performNetworkOperation(RELEASES_LIST, DOWNLOAD_TIMEOUT, QByteArray(), QString(), - releases_xml, QNetworkAccessManager::GetOperation).first; - NetworkFactory::performNetworkOperation(CHANGELOG, DOWNLOAD_TIMEOUT, QByteArray(), QString(), changelog, - QNetworkAccessManager::GetOperation); + releases_json, QNetworkAccessManager::GetOperation).first; if (result.second == QNetworkReply::NoError) { - result.first = parseUpdatesFile(releases_xml, changelog); + result.first = parseUpdatesFile(releases_json); } return result; @@ -267,43 +264,47 @@ bool SystemFactory::openFolderFile(const QString &file_path) { #endif } -UpdateInfo SystemFactory::parseUpdatesFile(const QByteArray &updates_file, const QByteArray &changelog) const { - UpdateInfo update; - QDomDocument document; document.setContent(updates_file, false); - const QDomNodeList releases = document.elementsByTagName(QSL("release")); +QList SystemFactory::parseUpdatesFile(const QByteArray &updates_file) const { + QList updates; - if (releases.size() == 1) { - QDomElement rel_elem = releases.at(0).toElement(); + QJsonArray document = QJsonDocument::fromJson(updates_file).array(); - update.m_availableVersion = rel_elem.attributes().namedItem(QSL("version")).toAttr().value(); - update.m_changes = QString::fromUtf8(changelog); + for (int i = 0; i < document.size(); i++) { + QJsonObject release = document.at(i).toObject(); + UpdateInfo update; - QDomNodeList urls = rel_elem.elementsByTagName(QSL("url")); + update.m_date = QDateTime::fromString(release["published_at"].toString(), QSL("yyyy-MM-ddTHH:mm:ssZ")); + update.m_availableVersion = release["tag_name"].toString(); + update.m_changes = release["body"].toString(); - for (int j = 0; j < urls.size(); j++) { + QJsonArray assets = release["assets"].toArray(); + + for (int j = 0; j < assets.size(); j++) { + QJsonObject asset = assets.at(j).toObject(); UpdateUrl url; - QDomElement url_elem = urls.at(j).toElement(); - url.m_fileUrl = url_elem.text(); - url.m_os = url_elem.attributes().namedItem(QSL("os")).toAttr().value(); - url.m_platform = url_elem.attributes().namedItem(QSL("platform")).toAttr().value(); + url.m_fileUrl = asset["browser_download_url"].toString(); + url.m_name = asset["name"].toString(); + url.m_size = asset["size"].toVariant().toString() + tr(" bytes"); - update.m_urls.insert(url.m_os, url); + update.m_urls.append(url); } - } - else { - update.m_availableVersion = QString(); + + updates.append(update); } + qSort(updates.begin(), updates.end(), [](const UpdateInfo &a, const UpdateInfo &b) -> bool { + return a.m_date > b.m_date; + }); - return update; + return updates; } void SystemFactory::checkForUpdatesOnStartup() { - const UpdateCheck updates = checkForUpdates(); + const QPair, QNetworkReply::NetworkError> updates = checkForUpdates(); - if (updates.second == QNetworkReply::NoError && isVersionNewer(updates.first.m_availableVersion, - APP_VERSION)) { + if (!updates.first.isEmpty() && updates.second == QNetworkReply::NoError && isVersionNewer(updates.first.at(0).m_availableVersion, + APP_VERSION)) { qApp->showGuiMessage(tr("New version available"), tr("Click the bubble for more information."), QSystemTrayIcon::Information, diff --git a/src/miscellaneous/systemfactory.h b/src/miscellaneous/systemfactory.h index 3e83dc3d7..f34fb738e 100755 --- a/src/miscellaneous/systemfactory.h +++ b/src/miscellaneous/systemfactory.h @@ -29,18 +29,19 @@ class UpdateUrl { public: QString m_fileUrl; - QString m_platform; - QString m_os; + QString m_name; + QString m_size; }; class UpdateInfo { public: - explicit UpdateInfo() : m_availableVersion(QString()), m_changes(QString()) { + explicit UpdateInfo() : m_availableVersion(QString()), m_changes(QString()), m_urls(QList()) { } QString m_availableVersion; QString m_changes; - QHash m_urls; + QList m_urls; + QDateTime m_date; }; Q_DECLARE_METATYPE(UpdateInfo) @@ -84,7 +85,7 @@ class SystemFactory : public QObject { QString getUsername() const; // Tries to download list with new updates. - QPair checkForUpdates() const; + QPair, QNetworkReply::NetworkError> checkForUpdates() const; // Checks if update is newer than current application version. static bool isVersionNewer(const QString &new_version, const QString &base_version); @@ -97,7 +98,7 @@ class SystemFactory : public QObject { private: // Performs parsing of downloaded file with list of updates. - UpdateInfo parseUpdatesFile(const QByteArray &updates_file, const QByteArray &changelog) const; + QList parseUpdatesFile(const QByteArray &updates_file) const; }; #endif // SYSTEMFACTORY_H diff --git a/src/services/standard/feedparser.cpp b/src/services/standard/feedparser.cpp index cf0d0ea8d..a2a016b33 100644 --- a/src/services/standard/feedparser.cpp +++ b/src/services/standard/feedparser.cpp @@ -94,3 +94,7 @@ QStringList FeedParser::textsFromPath(const QDomElement &element, const QString return result; } + +QString FeedParser::feedAuthor() const { + return ""; +} diff --git a/src/services/standard/feedparser.h b/src/services/standard/feedparser.h index 0829a3106..d18ca4469 100644 --- a/src/services/standard/feedparser.h +++ b/src/services/standard/feedparser.h @@ -34,7 +34,7 @@ class FeedParser { protected: QStringList textsFromPath(const QDomElement &element, const QString &namespace_uri, const QString &xml_path, bool only_first) const; virtual QDomNodeList messageElements() = 0; - virtual QString feedAuthor() const = 0; + virtual QString feedAuthor() const; virtual Message extractMessage(const QDomElement &msg_element, QDateTime current_time) const = 0; protected: diff --git a/src/services/standard/rssparser.cpp b/src/services/standard/rssparser.cpp index bdfcd328e..ff43127f1 100644 --- a/src/services/standard/rssparser.cpp +++ b/src/services/standard/rssparser.cpp @@ -19,105 +19,104 @@ #include "miscellaneous/textfactory.h" #include "network-web/webfactory.h" +#include "miscellaneous/iofactory.h" +#include "exceptions/applicationexception.h" #include -RssParser::RssParser() { +RssParser::RssParser(const QString &data) : FeedParser(data) { } RssParser::~RssParser() { } -QList RssParser::parseXmlData(const QString &data) { - QList messages; - QDomDocument xml_file; - QDateTime current_time = QDateTime::currentDateTime(); +QDomNodeList RssParser::messageElements() { + QDomNode channel_elem = m_xml.namedItem(QSL("rss")).namedItem(QSL("channel")); - xml_file.setContent(data, true); + if (channel_elem.isNull()) { + return QDomNodeList(); + } + else { + return channel_elem.toElement().elementsByTagName(QSL("item")); + } +} - // Pull out all messages. - QDomNodeList messages_in_xml = xml_file.elementsByTagName(QSL("item")); +Message RssParser::extractMessage(const QDomElement &msg_element, QDateTime current_time) const { + Message new_message; - for (int i = 0; i < messages_in_xml.size(); i++) { - QDomNode message_item = messages_in_xml.item(i); - Message new_message; + // Deal with titles & descriptions. + QString elem_title = msg_element.namedItem(QSL("title")).toElement().text().simplified(); + QString elem_description = msg_element.namedItem(QSL("encoded")).toElement().text(); + QString elem_enclosure = msg_element.namedItem(QSL("enclosure")).toElement().attribute(QSL("url")); + QString elem_enclosure_type = msg_element.namedItem(QSL("enclosure")).toElement().attribute(QSL("type")); - // Deal with titles & descriptions. - QString elem_title = message_item.namedItem(QSL("title")).toElement().text().simplified(); - QString elem_description = message_item.namedItem(QSL("encoded")).toElement().text(); - QString elem_enclosure = message_item.namedItem(QSL("enclosure")).toElement().attribute(QSL("url")); - QString elem_enclosure_type = message_item.namedItem(QSL("enclosure")).toElement().attribute(QSL("type")); - - if (elem_description.isEmpty()) { - elem_description = message_item.namedItem(QSL("description")).toElement().text(); - } - - // Now we obtained maximum of information for title & description. - if (elem_title.isEmpty()) { - if (elem_description.isEmpty()) { - // BOTH title and description are empty, skip this message. - continue; - } - else { - // Title is empty but description is not. - new_message.m_title = WebFactory::instance()->stripTags(elem_description.simplified()); - new_message.m_contents = elem_description; - } - } - else { - // Title is really not empty, description does not matter. - new_message.m_title = WebFactory::instance()->stripTags(elem_title); - new_message.m_contents = elem_description; - } - - if (!elem_enclosure.isEmpty()) { - new_message.m_enclosures.append(Enclosure(elem_enclosure, elem_enclosure_type)); - - qDebug("Adding enclosure '%s' for the message.", qPrintable(elem_enclosure)); - } - - // Deal with link and author. - new_message.m_url = message_item.namedItem(QSL("link")).toElement().text(); - - if (new_message.m_url.isEmpty() && !new_message.m_enclosures.isEmpty()) { - new_message.m_url = new_message.m_enclosures.first().m_url; - } - - if (new_message.m_url.isEmpty()) { - // Try to get "href" attribute. - new_message.m_url = message_item.namedItem(QSL("link")).toElement().attribute(QSL("href")); - } - - new_message.m_author = message_item.namedItem(QSL("author")).toElement().text(); - - if (new_message.m_author.isEmpty()) { - new_message.m_author = message_item.namedItem(QSL("creator")).toElement().text(); - } - - // Deal with creation date. - new_message.m_created = TextFactory::parseDateTime(message_item.namedItem(QSL("pubDate")).toElement().text()); - - if (new_message.m_created.isNull()) { - new_message.m_created = TextFactory::parseDateTime(message_item.namedItem(QSL("date")).toElement().text()); - } - - if (!(new_message.m_createdFromFeed = !new_message.m_created.isNull())) { - // Date was NOT obtained from the feed, - // set current date as creation date for the message. - new_message.m_created = current_time; - } - - if (new_message.m_author.isNull()) { - new_message.m_author = ""; - } - - if (new_message.m_url.isNull()) { - new_message.m_url = ""; - } - - messages.append(new_message); + if (elem_description.isEmpty()) { + elem_description = msg_element.namedItem(QSL("description")).toElement().text(); } - return messages; + // Now we obtained maximum of information for title & description. + if (elem_title.isEmpty()) { + if (elem_description.isEmpty()) { + // BOTH title and description are empty, skip this message. + throw new ApplicationException(QSL("Not enough data for the message.")); + } + else { + // Title is empty but description is not. + new_message.m_title = WebFactory::instance()->stripTags(elem_description.simplified()); + new_message.m_contents = elem_description; + } + } + else { + // Title is really not empty, description does not matter. + new_message.m_title = WebFactory::instance()->stripTags(elem_title); + new_message.m_contents = elem_description; + } + + if (!elem_enclosure.isEmpty()) { + new_message.m_enclosures.append(Enclosure(elem_enclosure, elem_enclosure_type)); + + qDebug("Adding enclosure '%s' for the message.", qPrintable(elem_enclosure)); + } + + // Deal with link and author. + new_message.m_url = msg_element.namedItem(QSL("link")).toElement().text(); + + if (new_message.m_url.isEmpty() && !new_message.m_enclosures.isEmpty()) { + new_message.m_url = new_message.m_enclosures.first().m_url; + } + + if (new_message.m_url.isEmpty()) { + // Try to get "href" attribute. + new_message.m_url = msg_element.namedItem(QSL("link")).toElement().attribute(QSL("href")); + } + + new_message.m_author = msg_element.namedItem(QSL("author")).toElement().text(); + + if (new_message.m_author.isEmpty()) { + new_message.m_author = msg_element.namedItem(QSL("creator")).toElement().text(); + } + + // Deal with creation date. + new_message.m_created = TextFactory::parseDateTime(msg_element.namedItem(QSL("pubDate")).toElement().text()); + + if (new_message.m_created.isNull()) { + new_message.m_created = TextFactory::parseDateTime(msg_element.namedItem(QSL("date")).toElement().text()); + } + + if (!(new_message.m_createdFromFeed = !new_message.m_created.isNull())) { + // Date was NOT obtained from the feed, + // set current date as creation date for the message. + new_message.m_created = current_time; + } + + if (new_message.m_author.isNull()) { + new_message.m_author = ""; + } + + if (new_message.m_url.isNull()) { + new_message.m_url = ""; + } + + return new_message; } diff --git a/src/services/standard/rssparser.h b/src/services/standard/rssparser.h index 66f0dd3b3..db8e9822e 100644 --- a/src/services/standard/rssparser.h +++ b/src/services/standard/rssparser.h @@ -18,17 +18,21 @@ #ifndef RSSPARSER_H #define RSSPARSER_H +#include "services/standard/feedparser.h" + #include "core/message.h" #include -class RssParser { +class RssParser : public FeedParser { public: - explicit RssParser(); + explicit RssParser(const QString &data); virtual ~RssParser(); - QList parseXmlData(const QString &data); + private: + QDomNodeList messageElements(); + Message extractMessage(const QDomElement &msg_element, QDateTime current_time) const; }; #endif // RSSPARSER_H diff --git a/src/services/standard/standardfeed.cpp b/src/services/standard/standardfeed.cpp index 4e1de2bcb..718c0bead 100755 --- a/src/services/standard/standardfeed.cpp +++ b/src/services/standard/standardfeed.cpp @@ -446,7 +446,7 @@ QList StandardFeed::obtainNewMessages(bool *error_during_obtaining) { switch (type()) { case StandardFeed::Rss0X: case StandardFeed::Rss2X: - messages = RssParser().parseXmlData(formatted_feed_contents); + messages = RssParser(formatted_feed_contents).messages(); break; case StandardFeed::Rdf: