From 5ce90e5943731dc71114c385496cf0eabab34d4c Mon Sep 17 00:00:00 2001 From: Martin Rotter Date: Fri, 18 Feb 2022 11:57:35 +0100 Subject: [PATCH] files for newsbluer plugin --- localization/rssguard_cs.ts | 196 +++++++++--------- .../desktop/com.github.rssguard.appdata.xml | 2 +- resources/graphics/misc/newsblur.png | Bin 0 -> 17121 bytes resources/rssguard.qrc | 1 + src/librssguard/CMakeLists.txt | 12 ++ src/librssguard/definitions/definitions.h | 2 + src/librssguard/miscellaneous/application.cpp | 2 +- src/librssguard/miscellaneous/feedreader.cpp | 2 + src/librssguard/network-web/downloader.cpp | 15 ++ src/librssguard/network-web/downloader.h | 2 + .../network-web/networkfactory.cpp | 10 +- .../services/newsblur/definitions.h | 15 ++ .../newsblur/gui/formeditnewsbluraccount.cpp | 63 ++++++ .../newsblur/gui/formeditnewsbluraccount.h | 30 +++ .../newsblur/gui/newsbluraccountdetails.cpp | 114 ++++++++++ .../newsblur/gui/newsbluraccountdetails.h | 33 +++ .../newsblur/gui/newsbluraccountdetails.ui | 163 +++++++++++++++ .../services/newsblur/newsblurentrypoint.cpp | 42 ++++ .../services/newsblur/newsblurentrypoint.h | 19 ++ .../services/newsblur/newsblurnetwork.cpp | 177 ++++++++++++++++ .../services/newsblur/newsblurnetwork.h | 77 +++++++ .../services/newsblur/newsblurserviceroot.cpp | 145 +++++++++++++ .../services/newsblur/newsblurserviceroot.h | 46 ++++ 23 files changed, 1066 insertions(+), 102 deletions(-) create mode 100755 resources/graphics/misc/newsblur.png create mode 100755 src/librssguard/services/newsblur/definitions.h create mode 100755 src/librssguard/services/newsblur/gui/formeditnewsbluraccount.cpp create mode 100755 src/librssguard/services/newsblur/gui/formeditnewsbluraccount.h create mode 100755 src/librssguard/services/newsblur/gui/newsbluraccountdetails.cpp create mode 100755 src/librssguard/services/newsblur/gui/newsbluraccountdetails.h create mode 100755 src/librssguard/services/newsblur/gui/newsbluraccountdetails.ui create mode 100755 src/librssguard/services/newsblur/newsblurentrypoint.cpp create mode 100755 src/librssguard/services/newsblur/newsblurentrypoint.h create mode 100755 src/librssguard/services/newsblur/newsblurnetwork.cpp create mode 100755 src/librssguard/services/newsblur/newsblurnetwork.h create mode 100755 src/librssguard/services/newsblur/newsblurserviceroot.cpp create mode 100755 src/librssguard/services/newsblur/newsblurserviceroot.h diff --git a/localization/rssguard_cs.ts b/localization/rssguard_cs.ts index 8e9470ea0..ceeb36472 100644 --- a/localization/rssguard_cs.ts +++ b/localization/rssguard_cs.ts @@ -22,7 +22,7 @@ Cannot enable AdBlock - + AdBlock nelze povolit @@ -32,25 +32,27 @@ OK! - + OK! There is error, check application log for more details and head to online documentation. - + Došlo k chybě, zkontrolujte log programu a případně prověřte dokumentaci. There is error, check application log for more details and head to online documentation. Also make sure that Node.js is installed. Error: %1 - + Došlo k chybě, zkontrolujte log programu a případně prověřte dokumentaci. Ujistětě se, že máte nakonfigurovaný Node.js. + +Chyba: %1 ERROR! - + CHYBA! @@ -58,12 +60,12 @@ Error: %1 No additional info. - + Žádné další info. It seems your AdBlock runs fine, but wait few seconds to be sure. - + Vypadá to, že AdBlock běží dobře, ale počkejme pár vteřin. @@ -78,7 +80,7 @@ Error: %1 Filter lists - + Filtrovací seznamy @@ -114,7 +116,7 @@ Error: %1 failed to download filter list '%1' - + nepodařilo se stáhnout filter '%1' @@ -167,22 +169,22 @@ Error: %1 Packages %1 were updated. - + Balíčky %1 byly aktualizovány. Unread articles fetched - + Staženy nepřečtené zprávy Go to changelog - + Zobrazit seznam změn AdBlock needs to be configured - + Je třeba nastavit AdBlock @@ -202,7 +204,7 @@ Error: %1 Welcome - + Vítejte @@ -218,7 +220,7 @@ na tuto bublinu. Already running - + Již běží @@ -286,7 +288,7 @@ na tuto bublinu. Show/hide the password - + Zobrazit/skrýt heslo @@ -327,32 +329,32 @@ na tuto bublinu. Removing read articles... - + Mažu přečtené zprávy... Read articles purged... - + Přečtené zprávy smazány... Removing old articles... - + Mažu staré zprávy... Old articles purged... - + Staré zprávy smazány... Removing starred articles... - + Mažu důležité zprávy... Starred articles purged... - + Důležité zprávy smazány... @@ -393,7 +395,7 @@ na tuto bublinu. Not supported by account - + Není podporováno @@ -483,7 +485,7 @@ Klikněte sem pro otevření nadřazeného adresáře. Open folder - + Otevřít složku @@ -612,39 +614,39 @@ Stav: %3 does not use auto-fetching of articles Describes feed auto-update status. - + nestahuje zprávy automaticky uses global settings (%n minute(s) to next auto-fetch of articles) Describes feed auto-update status. - + používá globální nastavení (%n minuta do dalšího stahování zpráv)používá globální nastavení (%n minuty do dalšího stahování zpráv)používá globální nastavení (%n minut do dalšího stahování zpráv)používá globální nastavení (%n minut do dalšího stahování zpráv) uses global settings (global auto-fetching of articles is disabled) - + používá globální nastavení (globální stahování zpráv je zakázáno) uses specific settings (%n minute(s) to next auto-fetching of new articles) Describes feed auto-update status. - + používá specifické nastavení (%n minuta do dalšího automatického stažení zpráv)používá specifické nastavení (%n minuty do dalšího automatického stažení zpráv)používá specifické nastavení (%n minut do dalšího automatického stažení zpráv)používá specifické nastavení (%n minut do dalšího automatického stažení zpráv) has new articles - + má nové zprávy parsing error - + špatný formát kanálu error - + chyba @@ -657,7 +659,7 @@ Stav: %3 Toolbar for articles - + Nástrojová lišta pro zprávy @@ -665,22 +667,22 @@ Stav: %3 Starting auto-download of some feeds' articles - + Zahajuji automatické stažení zpráv pro některé kanály I will auto-download new articles for %n feed(s). - + Budou se stahovat zprávy pro %n kanálBudou se stahovat zprávy pro %n kanályBudou se stahovat zprávy pro %n kanálůBudou se stahovat zprávy pro %n kanálů Cannot fetch articles at this point - + V tuto chvíli nelze stáhnout zprávy You cannot fetch new articles now because another critical operation is ongoing. - + V tuto chvíli nelze zahájit stahování zpráv, protože běží jiná důležitá operace. @@ -704,7 +706,7 @@ Stav: %3 Only download newest X articles per feed - + Stáhnout pouze X nejnovějších zpráv pro každý kanál @@ -714,7 +716,7 @@ Stav: %3 Download unread articles only - + Stahovat pouze nepřečtené zprávy @@ -770,17 +772,17 @@ Stav: %3 Login was successful. - + Přihlášení bylo úspěšné. Your %1 build has official Feedly support. You do not have to use "developer access token". You can therefore leave corresponding field empty. - + Vaše verze programu %1 má oficiální podporu Feedly. Nemusíte tedy používat DAC, a tak můžete nechat odpovídající políčko prázdné. Some problems. - + Nějaké problémy. @@ -795,22 +797,22 @@ Stav: %3 Access token is empty. - + Přístupový token je prázdný. Access token is okay. - + Přístupový token je v pořádku. Error: '%1' - + Chyba: '%1' Beware of downloading too many articles, because Feedly permanently caches ALL articles of the feed, so you might end up with thousands of articles which you will never read anyway. - + Zvažte, zda je dobrá věc stahovat příliš mnoho zpráv. Feedly permantně cachuje všechny dřívější zprávy z kanálu, takže můžete skončit s tisícovkami zpráv, které stejně nebudete číst. @@ -818,7 +820,7 @@ Stav: %3 Feedly: authentication error - + Feedly: chyba autentizace @@ -828,7 +830,7 @@ Stav: %3 Feedly: authorization denied - + Feedly: přihlašovací údaje zamítnuty @@ -1006,13 +1008,13 @@ or this functionality is not implemented yet. Context menu for important articles - + Kontextové menu pro důležité zprávy Not supported by account - + Není podporováno @@ -1082,7 +1084,7 @@ or this functionality is not implemented yet. GNU LGPL License (applies to Breeze source code) - + GNU LGPL Licence (pro kód komponenty Breeze) @@ -1122,7 +1124,7 @@ or this functionality is not implemented yet. Database location - + Umístění databáze @@ -1135,7 +1137,7 @@ or this functionality is not implemented yet. Network proxy - + Síťová proxy @@ -1145,7 +1147,7 @@ or this functionality is not implemented yet. Edit account "%1" - + Upravit účet "%1" @@ -1431,12 +1433,12 @@ or this functionality is not implemented yet. Edit "%1" - + Upravit "%1" Parent folder - + Nadřazený uzel @@ -1506,32 +1508,32 @@ or this functionality is not implemented yet. Cleanup settings - + Nastavení čištění Optimize database file - + Optimalizovat databázi Remove all read articles - + Smazat přečtené zprávy Remove all articles from recycle bin - + Smazat zprávy z košů Remove all articles older than - + Smazat zprávy starší než Remove all starred articles - + Smazat důležité zprávy @@ -1541,7 +1543,7 @@ or this functionality is not implemented yet. Total data size - + Velikost dat @@ -1572,7 +1574,7 @@ or this functionality is not implemented yet. Service setup - + Nastavení služby @@ -1580,7 +1582,7 @@ or this functionality is not implemented yet. Server setup - + Nastavení serveru @@ -1588,7 +1590,7 @@ or this functionality is not implemented yet. Server setup - + Nastavení serveru @@ -1596,7 +1598,7 @@ or this functionality is not implemented yet. Server setup - + Nastavení serveru @@ -1604,7 +1606,7 @@ or this functionality is not implemented yet. Server setup - + Nastavení serveru @@ -1612,7 +1614,7 @@ or this functionality is not implemented yet. Server setup - + Nastavení serveru @@ -1625,62 +1627,62 @@ or this functionality is not implemented yet. Cannot save changes: %1 - + Změny nelze uložit: %1 Edit "%1" - + Upravit "%1" Fetch articles using global interval - + Stahovat zprávy dle hlavního nastavení Fetch articles every - + Stahovat zprávy každých Disable auto-fetching of articles - + Zakázat automatické stahování zpráv Cannot save feed properties - + Nelze uložit vlastnosti kanálu Articles - + Zprávy Auto-downloading of articles - + Automatické stahování zpráv Select the auto-download strategy for messages of this feed. Default auto-download strategy means that new messges of this feed will be downloaded in time intervals set in application settings. - + Zvolte strategii auto-aktualizací zpráv tohoto kanálu. Výchozí strategorie auto-aktualizace znamená, že kanál bude aktualizován v intervalech udaných v nastavení aplikace. Open articles via their URL automatically - + Otevírat zdrojové URL zpráv automaticky Miscellaneous - + Různé Disable this feed - + Vypnout kanál @@ -1703,7 +1705,7 @@ or this functionality is not implemented yet. Open main menu - + Otevřít hlavní menu @@ -1738,12 +1740,12 @@ or this functionality is not implemented yet. F&eeds - + &Kanály &Add item - + &Přidat položku @@ -3149,7 +3151,7 @@ Nyní ho můžete nainstalovat. Only download newest X articles per feed - + Stáhnout pouze X nejnovějších zpráv pro každý kanál @@ -3159,7 +3161,7 @@ Nyní ho můžete nainstalovat. Download unread articles only - + Stahovat pouze nepřečtené zprávy @@ -3413,7 +3415,7 @@ Tokeny vyprší: %2 Download unread articles only - + Stahovat pouze nepřečtené zprávy @@ -3428,7 +3430,7 @@ Tokeny vyprší: %2 Only download newest X articles per feed - + Stáhnout pouze X nejnovějších zpráv pro každý kanál @@ -4434,12 +4436,12 @@ Tokeny vyprší: %2 Download unread articles only - + Stahovat pouze nepřečtené zprávy Only download newest X articles per feed - + Stáhnout pouze X nejnovějších zpráv pro každý kanál @@ -4835,7 +4837,7 @@ List of supported readers: Only download newest X articles per feed - + Stáhnout pouze X nejnovějších zpráv pro každý kanál @@ -4845,7 +4847,7 @@ List of supported readers: Download unread articles only - + Stahovat pouze nepřečtené zprávy @@ -5160,7 +5162,7 @@ File filter for external e-mail selection dialog. Network proxy - + Síťová proxy @@ -5469,7 +5471,7 @@ Authors of this application are NOT responsible for lost data. Articles - + Zprávy @@ -6309,7 +6311,7 @@ Also, you can post-process generated feed data with yet another script if you wi Parent folder - + Nadřazený uzel @@ -6798,12 +6800,12 @@ Nepřečtené zprávy: %2 Download unread articles only - + Stahovat pouze nepřečtené zprávy Only download newest X articles per feed - + Stáhnout pouze X nejnovějších zpráv pro každý kanál @@ -6887,7 +6889,7 @@ Nepřečtené zprávy: %2 Parent folder - + Nadřazený uzel diff --git a/resources/desktop/com.github.rssguard.appdata.xml b/resources/desktop/com.github.rssguard.appdata.xml index 3c194cc98..cb71a2436 100644 --- a/resources/desktop/com.github.rssguard.appdata.xml +++ b/resources/desktop/com.github.rssguard.appdata.xml @@ -26,7 +26,7 @@ https://github.com/sponsors/martinrotter - + none diff --git a/resources/graphics/misc/newsblur.png b/resources/graphics/misc/newsblur.png new file mode 100755 index 0000000000000000000000000000000000000000..658ffe1f1df83fb20b843fd1871c9cf69c9ab951 GIT binary patch literal 17121 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 literal 0 HcmV?d00001 diff --git a/resources/rssguard.qrc b/resources/rssguard.qrc index e8b7d4baa..a269d096f 100644 --- a/resources/rssguard.qrc +++ b/resources/rssguard.qrc @@ -34,6 +34,7 @@ graphics/misc/google.png graphics/misc/image-placeholder.png graphics/misc/inoreader.png + graphics/misc/newsblur.png graphics/misc/nextcloud.png graphics/misc/reddit.png graphics/misc/reedah.png diff --git a/src/librssguard/CMakeLists.txt b/src/librssguard/CMakeLists.txt index eaadab8de..1be67a101 100644 --- a/src/librssguard/CMakeLists.txt +++ b/src/librssguard/CMakeLists.txt @@ -305,6 +305,17 @@ set(SOURCES services/greader/gui/formeditgreaderaccount.h services/greader/gui/greaderaccountdetails.cpp services/greader/gui/greaderaccountdetails.h + services/newsblur/definitions.h + services/newsblur/newsblurentrypoint.cpp + services/newsblur/newsblurentrypoint.h + services/newsblur/newsblurnetwork.cpp + services/newsblur/newsblurnetwork.h + services/newsblur/newsblurserviceroot.cpp + services/newsblur/newsblurserviceroot.h + services/newsblur/gui/formeditnewsbluraccount.cpp + services/newsblur/gui/formeditnewsbluraccount.h + services/newsblur/gui/newsbluraccountdetails.cpp + services/newsblur/gui/newsbluraccountdetails.h services/owncloud/definitions.h services/owncloud/gui/formeditowncloudaccount.cpp services/owncloud/gui/formeditowncloudaccount.h @@ -447,6 +458,7 @@ set(UI_FILES services/gmail/gui/formdownloadattachment.ui services/gmail/gui/gmailaccountdetails.ui services/greader/gui/greaderaccountdetails.ui + services/newsblur/gui/newsbluraccountdetails.ui services/owncloud/gui/owncloudaccountdetails.ui services/reddit/gui/redditaccountdetails.ui services/standard/gui/formstandardimportexport.ui diff --git a/src/librssguard/definitions/definitions.h b/src/librssguard/definitions/definitions.h index 1aa656654..fcc1c65fd 100644 --- a/src/librssguard/definitions/definitions.h +++ b/src/librssguard/definitions/definitions.h @@ -14,6 +14,7 @@ #define SERVICE_CODE_INOREADER "inoreader" #define SERVICE_CODE_GMAIL "gmail" #define SERVICE_CODE_REDDIT "reddit" +#define SERVICE_CODE_NEWSBLUR "newsblur" #define ADBLOCK_SERVER_PORT 48484 #define ADBLOCK_HOWTO "https://github.com/martinrotter/rssguard/blob/master/resources/docs/Documentation.md#adbl" @@ -139,6 +140,7 @@ #define LOGSEC_GMAIL "gmail: " #define LOGSEC_OAUTH "oauth: " #define LOGSEC_REDDIT "reddit: " +#define LOGSEC_NEWSBLUR "newsblur: " #define MAX_ZOOM_FACTOR 5.0f #define MIN_ZOOM_FACTOR 0.25f diff --git a/src/librssguard/miscellaneous/application.cpp b/src/librssguard/miscellaneous/application.cpp index b17cb52b0..d0c420a3b 100644 --- a/src/librssguard/miscellaneous/application.cpp +++ b/src/librssguard/miscellaneous/application.cpp @@ -850,7 +850,7 @@ void Application::parseCmdArgumentsFromMyInstance() { void Application::onNodeJsPackageUpdateError(const QList& pkgs, const QString& error) { qApp->showGuiMessage(Notification::Event::NodePackageFailedToUpdate, { {}, - tr("Packages %1 were NOT updated because of error: %3.").arg(NodeJs::packagesToString(pkgs), + tr("Packages %1 were NOT updated because of error: %2.").arg(NodeJs::packagesToString(pkgs), error), QSystemTrayIcon::MessageIcon::Critical }); } diff --git a/src/librssguard/miscellaneous/feedreader.cpp b/src/librssguard/miscellaneous/feedreader.cpp index 0e4a0d764..cf31d3333 100644 --- a/src/librssguard/miscellaneous/feedreader.cpp +++ b/src/librssguard/miscellaneous/feedreader.cpp @@ -17,6 +17,7 @@ #include "services/feedly/feedlyentrypoint.h" #include "services/gmail/gmailentrypoint.h" #include "services/greader/greaderentrypoint.h" +#include "services/newsblur/newsblurentrypoint.h" #include "services/owncloud/owncloudserviceentrypoint.h" #include "services/reddit/redditentrypoint.h" #include "services/standard/standardserviceentrypoint.h" @@ -60,6 +61,7 @@ QList FeedReader::feedServices() { m_feedServices.append(new FeedlyEntryPoint()); m_feedServices.append(new GmailEntryPoint()); m_feedServices.append(new GreaderEntryPoint()); + m_feedServices.append(new NewsBlurEntryPoint()); m_feedServices.append(new OwnCloudServiceEntryPoint()); #if defined(DEBUG) diff --git a/src/librssguard/network-web/downloader.cpp b/src/librssguard/network-web/downloader.cpp index c31da6327..c1a94280b 100644 --- a/src/librssguard/network-web/downloader.cpp +++ b/src/librssguard/network-web/downloader.cpp @@ -171,6 +171,17 @@ void Downloader::finished() { m_lastOutputMultipartData = decodeMultipartAnswer(reply); } + QVariant set_cookies_header = reply->header(QNetworkRequest::SetCookieHeader); + + if (set_cookies_header.isValid()) { + QList cookies = set_cookies_header.value>(); + + m_lastCookies = cookies; + } + else { + m_lastCookies = {}; + } + m_lastContentType = reply->header(QNetworkRequest::ContentTypeHeader); m_lastOutputError = reply->error(); m_activeReply->deleteLater(); @@ -292,6 +303,10 @@ void Downloader::runGetRequest(const QNetworkRequest& request) { connect(m_activeReply, &QNetworkReply::finished, this, &Downloader::finished); } +QList Downloader::lastCookies() const { + return m_lastCookies; +} + QVariant Downloader::lastContentType() const { return m_lastContentType; } diff --git a/src/librssguard/network-web/downloader.h b/src/librssguard/network-web/downloader.h index f0421bb4a..a275fe14d 100644 --- a/src/librssguard/network-web/downloader.h +++ b/src/librssguard/network-web/downloader.h @@ -28,6 +28,7 @@ class Downloader : public QObject { QNetworkReply::NetworkError lastOutputError() const; QList lastOutputMultipartData() const; QVariant lastContentType() const; + QList lastCookies() const; void setProxy(const QNetworkProxy& proxy); @@ -98,6 +99,7 @@ class Downloader : public QObject { QNetworkReply::NetworkError m_lastOutputError; QVariant m_lastContentType; + QList m_lastCookies; }; #endif // DOWNLOADER_H diff --git a/src/librssguard/network-web/networkfactory.cpp b/src/librssguard/network-web/networkfactory.cpp index 8c7d308d6..010d60536 100644 --- a/src/librssguard/network-web/networkfactory.cpp +++ b/src/librssguard/network-web/networkfactory.cpp @@ -229,11 +229,15 @@ QNetworkReply::NetworkError NetworkFactory::downloadIcon(const QList>& additional_headers, bool protected_contents, - const QString& username, const QString& password, + const QString& username, + const QString& password, const QNetworkProxy& custom_proxy) { Downloader downloader; QEventLoop loop; diff --git a/src/librssguard/services/newsblur/definitions.h b/src/librssguard/services/newsblur/definitions.h new file mode 100755 index 000000000..d68b61fbf --- /dev/null +++ b/src/librssguard/services/newsblur/definitions.h @@ -0,0 +1,15 @@ +#ifndef NEWSBLUR_DEFINITIONS_H +#define NEWSBLUR_DEFINITIONS_H + +// Misc. +#define NEWSBLUR_DEFAULT_BATCH_SIZE 500 + +// URLs. +#define NEWSBLUR_URL "https://newsblur.com" + +// API. +#define NEWSBLUR_API_LOGIN "api/login" +#define NEWSBLUR_API_LOGOUT "api/logout" +#define NEWSBLUR_API_SIGNUP "api/signup" + +#endif // NEWSBLUR_DEFINITIONS_H diff --git a/src/librssguard/services/newsblur/gui/formeditnewsbluraccount.cpp b/src/librssguard/services/newsblur/gui/formeditnewsbluraccount.cpp new file mode 100755 index 000000000..d1aca3b6b --- /dev/null +++ b/src/librssguard/services/newsblur/gui/formeditnewsbluraccount.cpp @@ -0,0 +1,63 @@ +// For license of this file, see /LICENSE.md. + +#include "services/newsblur/gui/formeditnewsbluraccount.h" + +#include "gui/guiutilities.h" +#include "miscellaneous/iconfactory.h" +#include "network-web/networkfactory.h" +#include "services/newsblur/definitions.h" +#include "services/newsblur/gui/newsbluraccountdetails.h" +#include "services/newsblur/newsblurnetwork.h" +#include "services/newsblur/newsblurserviceroot.h" + +FormEditNewsBlurAccount::FormEditNewsBlurAccount(QWidget* parent) + : FormAccountDetails(qApp->icons()->miscIcon(QSL("newsblur")), parent), m_details(new NewsBlurAccountDetails(this)) { + insertCustomTab(m_details, tr("Server setup"), 0); + activateTab(0); + + connect(m_details->m_ui.m_btnTestSetup, &QPushButton::clicked, this, &FormEditNewsBlurAccount::performTest); + + m_details->m_ui.m_txtUrl->setFocus(); +} + +void FormEditNewsBlurAccount::apply() { + FormAccountDetails::apply(); + + NewsBlurServiceRoot* existing_root = account(); + bool using_another_acc = + m_details->m_ui.m_txtUsername->lineEdit()->text() != existing_root->network()->username() || + m_details->m_ui.m_txtUrl->lineEdit()->text() != existing_root->network()->baseUrl(); + + existing_root->network()->setBaseUrl(m_details->m_ui.m_txtUrl->lineEdit()->text()); + existing_root->network()->setUsername(m_details->m_ui.m_txtUsername->lineEdit()->text()); + existing_root->network()->setPassword(m_details->m_ui.m_txtPassword->lineEdit()->text()); + existing_root->network()->setBatchSize(m_details->m_ui.m_spinLimitMessages->value()); + existing_root->network()->setDownloadOnlyUnreadMessages(m_details->m_ui.m_cbDownloadOnlyUnreadMessages->isChecked()); + + existing_root->saveAccountDataToDatabase(); + accept(); + + if (!m_creatingNew) { + if (using_another_acc) { + existing_root->completelyRemoveAllData(); + } + + existing_root->start(true); + } +} + +void FormEditNewsBlurAccount::loadAccountData() { + FormAccountDetails::loadAccountData(); + + NewsBlurServiceRoot* existing_root = account(); + + m_details->m_ui.m_txtUsername->lineEdit()->setText(existing_root->network()->username()); + m_details->m_ui.m_txtPassword->lineEdit()->setText(existing_root->network()->password()); + m_details->m_ui.m_txtUrl->lineEdit()->setText(existing_root->network()->baseUrl()); + m_details->m_ui.m_spinLimitMessages->setValue(existing_root->network()->batchSize()); + m_details->m_ui.m_cbDownloadOnlyUnreadMessages->setChecked(existing_root->network()->downloadOnlyUnreadMessages()); +} + +void FormEditNewsBlurAccount::performTest() { + m_details->performTest(m_proxyDetails->proxy()); +} diff --git a/src/librssguard/services/newsblur/gui/formeditnewsbluraccount.h b/src/librssguard/services/newsblur/gui/formeditnewsbluraccount.h new file mode 100755 index 000000000..1871f8295 --- /dev/null +++ b/src/librssguard/services/newsblur/gui/formeditnewsbluraccount.h @@ -0,0 +1,30 @@ +// For license of this file, see /LICENSE.md. + +#ifndef FORMEDITNEWSBLURACCOUNT_H +#define FORMEDITNEWSBLURACCOUNT_H + +#include "services/abstract/gui/formaccountdetails.h" + +class NewsBlurAccountDetails; +class NewsBlurServiceRoot; + +class FormEditNewsBlurAccount : public FormAccountDetails { + Q_OBJECT + + public: + explicit FormEditNewsBlurAccount(QWidget* parent = nullptr); + + protected slots: + virtual void apply(); + + protected: + virtual void loadAccountData(); + + private slots: + void performTest(); + + private: + NewsBlurAccountDetails* m_details; +}; + +#endif // FORMEDITNEWSBLURACCOUNT_H diff --git a/src/librssguard/services/newsblur/gui/newsbluraccountdetails.cpp b/src/librssguard/services/newsblur/gui/newsbluraccountdetails.cpp new file mode 100755 index 000000000..bbd894b1f --- /dev/null +++ b/src/librssguard/services/newsblur/gui/newsbluraccountdetails.cpp @@ -0,0 +1,114 @@ +// For license of this file, see /LICENSE.md. + +#include "services/newsblur/gui/newsbluraccountdetails.h" + +#include "definitions/definitions.h" +#include "exceptions/applicationexception.h" +#include "exceptions/networkexception.h" +#include "gui/guiutilities.h" +#include "miscellaneous/application.h" +#include "miscellaneous/systemfactory.h" +#include "network-web/webfactory.h" +#include "services/newsblur/definitions.h" +#include "services/newsblur/newsblurnetwork.h" + +#include + +NewsBlurAccountDetails::NewsBlurAccountDetails(QWidget* parent) : QWidget(parent), m_lastProxy({}) { + m_ui.setupUi(this); + + m_ui.m_lblTestResult->label()->setWordWrap(true); + m_ui.m_txtPassword->lineEdit()->setPasswordMode(true); + m_ui.m_txtPassword->lineEdit()->setPlaceholderText(tr("Password for your account")); + m_ui.m_txtUsername->lineEdit()->setPlaceholderText(tr("Username for your account")); + m_ui.m_txtUrl->lineEdit()->setPlaceholderText(tr("URL of your server, without any service-specific path")); + m_ui.m_lblTestResult->setStatus(WidgetWithStatus::StatusType::Information, + tr("No test done yet."), + tr("Here, results of connection test are shown.")); + + m_ui.m_lblLimitMessages->setHelpText(tr("Some feeds might contain tens of thousands of articles " + "and downloading all of them could take great amount of time, " + "so sometimes it is good to download " + "only certain amount of newest messages."), + true); + + connect(m_ui.m_txtPassword->lineEdit(), &BaseLineEdit::textChanged, this, &NewsBlurAccountDetails::onPasswordChanged); + connect(m_ui.m_txtUsername->lineEdit(), &BaseLineEdit::textChanged, this, &NewsBlurAccountDetails::onUsernameChanged); + connect(m_ui.m_txtUrl->lineEdit(), &BaseLineEdit::textChanged, this, &NewsBlurAccountDetails::onUrlChanged); + + setTabOrder(m_ui.m_txtUrl->lineEdit(), m_ui.m_cbDownloadOnlyUnreadMessages); + setTabOrder(m_ui.m_cbDownloadOnlyUnreadMessages, m_ui.m_spinLimitMessages); + setTabOrder(m_ui.m_spinLimitMessages, m_ui.m_txtUsername->lineEdit()); + setTabOrder(m_ui.m_txtUsername->lineEdit(), m_ui.m_txtPassword->lineEdit()); + setTabOrder(m_ui.m_txtPassword->lineEdit(), m_ui.m_btnTestSetup); + + onPasswordChanged(); + onUsernameChanged(); + onUrlChanged(); +} + +void NewsBlurAccountDetails::performTest(const QNetworkProxy& custom_proxy) { + m_lastProxy = custom_proxy; + + NewsBlurNetwork factory; + + factory.setUsername(m_ui.m_txtUsername->lineEdit()->text()); + factory.setPassword(m_ui.m_txtPassword->lineEdit()->text()); + factory.setBaseUrl(m_ui.m_txtUrl->lineEdit()->text()); + + try { + LoginResult result = factory.login(custom_proxy); + + if (result.m_authenticated && !result.m_sessiodId.isEmpty()) { + m_ui.m_lblTestResult->setStatus(WidgetWithStatus::StatusType::Ok, + tr("You are good to go!"), + tr("Yeah.")); + } + else { + throw ApplicationException(result.m_errors.join(QSL(", "))); + } + } + catch (const NetworkException& netEx) { + m_ui.m_lblTestResult->setStatus(WidgetWithStatus::StatusType::Error, + tr("Network error: '%1'.").arg(NetworkFactory::networkErrorText(netEx.networkError())), + tr("Network error, have you entered correct username and password?")); + } + catch (const ApplicationException& ex) { + m_ui.m_lblTestResult->setStatus(WidgetWithStatus::StatusType::Error, + tr("Error: '%1'.").arg(ex.message()), + tr("Error, have you entered correct Nextcloud endpoint and password?")); + } +} + +void NewsBlurAccountDetails::onUsernameChanged() { + const QString username = m_ui.m_txtUsername->lineEdit()->text(); + + if (username.isEmpty()) { + m_ui.m_txtUsername->setStatus(WidgetWithStatus::StatusType::Error, tr("Username cannot be empty.")); + } + else { + m_ui.m_txtUsername->setStatus(WidgetWithStatus::StatusType::Ok, tr("Username is okay.")); + } +} + +void NewsBlurAccountDetails::onPasswordChanged() { + const QString password = m_ui.m_txtPassword->lineEdit()->text(); + + if (password.isEmpty()) { + m_ui.m_txtPassword->setStatus(WidgetWithStatus::StatusType::Error, tr("Password cannot be empty.")); + } + else { + m_ui.m_txtPassword->setStatus(WidgetWithStatus::StatusType::Ok, tr("Password is okay.")); + } +} + +void NewsBlurAccountDetails::onUrlChanged() { + const QString url = m_ui.m_txtUrl->lineEdit()->text(); + + if (url.isEmpty()) { + m_ui.m_txtUrl->setStatus(WidgetWithStatus::StatusType::Error, tr("URL cannot be empty.")); + } + else { + m_ui.m_txtUrl->setStatus(WidgetWithStatus::StatusType::Ok, tr("URL is okay.")); + } +} diff --git a/src/librssguard/services/newsblur/gui/newsbluraccountdetails.h b/src/librssguard/services/newsblur/gui/newsbluraccountdetails.h new file mode 100755 index 000000000..18fdeb3fa --- /dev/null +++ b/src/librssguard/services/newsblur/gui/newsbluraccountdetails.h @@ -0,0 +1,33 @@ +// For license of this file, see /LICENSE.md. + +#ifndef NEWSBLURACCOUNTDETAILS_H +#define NEWSBLURACCOUNTDETAILS_H + +#include + +#include "ui_newsbluraccountdetails.h" + +#include "services/newsblur/newsblurserviceroot.h" + +#include + +class NewsBlurAccountDetails : public QWidget { + Q_OBJECT + + friend class FormEditNewsBlurAccount; + + public: + explicit NewsBlurAccountDetails(QWidget* parent = nullptr); + + private slots: + void performTest(const QNetworkProxy& custom_proxy); + void onUsernameChanged(); + void onPasswordChanged(); + void onUrlChanged(); + + private: + Ui::NewsBlurAccountDetails m_ui; + QNetworkProxy m_lastProxy; +}; + +#endif // NEWSBLURACCOUNTDETAILS_H diff --git a/src/librssguard/services/newsblur/gui/newsbluraccountdetails.ui b/src/librssguard/services/newsblur/gui/newsbluraccountdetails.ui new file mode 100755 index 000000000..51378af47 --- /dev/null +++ b/src/librssguard/services/newsblur/gui/newsbluraccountdetails.ui @@ -0,0 +1,163 @@ + + + NewsBlurAccountDetails + + + + 0 + 0 + 430 + 281 + + + + + + + URL + + + m_txtUrl + + + + + + + + + + Download unread articles only + + + + + + + + + Only download newest X articles per feed + + + m_spinLimitMessages + + + + + + + + + + + + + + + + 0 + 0 + + + + Authentication + + + + + + Username + + + m_txtUsername + + + + + + + + + + Password + + + m_txtPassword + + + + + + + + + + + + + + + &Test setup + + + + + + + + 0 + 0 + + + + + + + + + + Qt::Vertical + + + + 409 + 60 + + + + + + + + + LabelWithStatus + QWidget +
labelwithstatus.h
+ 1 +
+ + LineEditWithStatus + QWidget +
lineeditwithstatus.h
+ 1 +
+ + MessageCountSpinBox + QSpinBox +
messagecountspinbox.h
+
+ + HelpSpoiler + QWidget +
helpspoiler.h
+ 1 +
+
+ + m_cbDownloadOnlyUnreadMessages + m_spinLimitMessages + m_btnTestSetup + + + +
diff --git a/src/librssguard/services/newsblur/newsblurentrypoint.cpp b/src/librssguard/services/newsblur/newsblurentrypoint.cpp new file mode 100755 index 000000000..898deb464 --- /dev/null +++ b/src/librssguard/services/newsblur/newsblurentrypoint.cpp @@ -0,0 +1,42 @@ +// For license of this file, see /LICENSE.md. + +#include "services/newsblur/newsblurentrypoint.h" + +#include "database/databasequeries.h" +#include "definitions/definitions.h" +#include "miscellaneous/application.h" +#include "miscellaneous/iconfactory.h" +#include "services/newsblur/gui/formeditnewsbluraccount.h" +#include "services/newsblur/newsblurserviceroot.h" + +ServiceRoot* NewsBlurEntryPoint::createNewRoot() const { + FormEditNewsBlurAccount form_acc(qApp->mainFormWidget()); + + return form_acc.addEditAccount(); +} + +QList NewsBlurEntryPoint::initializeSubtree() const { + QSqlDatabase database = qApp->database()->driver()->connection(QSL("NewsBlurEntryPoint")); + + return DatabaseQueries::getAccounts(database, code()); +} + +QString NewsBlurEntryPoint::name() const { + return QSL("NewsBlur"); +} + +QString NewsBlurEntryPoint::code() const { + return QSL(SERVICE_CODE_NEWSBLUR); +} + +QString NewsBlurEntryPoint::description() const { + return QObject::tr("Personal news reader bringing people together to talk about the world."); +} + +QString NewsBlurEntryPoint::author() const { + return QSL(APP_AUTHOR); +} + +QIcon NewsBlurEntryPoint::icon() const { + return qApp->icons()->miscIcon(QSL("newsblur")); +} diff --git a/src/librssguard/services/newsblur/newsblurentrypoint.h b/src/librssguard/services/newsblur/newsblurentrypoint.h new file mode 100755 index 000000000..6e96a3422 --- /dev/null +++ b/src/librssguard/services/newsblur/newsblurentrypoint.h @@ -0,0 +1,19 @@ +// For license of this file, see /LICENSE.md. + +#ifndef NEWSBLURENTRYPOINT_H +#define NEWSBLURENTRYPOINT_H + +#include "services/abstract/serviceentrypoint.h" + +class NewsBlurEntryPoint : public ServiceEntryPoint { + public: + virtual ServiceRoot* createNewRoot() const; + virtual QList initializeSubtree() const; + virtual QString name() const; + virtual QString code() const; + virtual QString description() const; + virtual QString author() const; + virtual QIcon icon() const; +}; + +#endif // NEWSBLURENTRYPOINT_H diff --git a/src/librssguard/services/newsblur/newsblurnetwork.cpp b/src/librssguard/services/newsblur/newsblurnetwork.cpp new file mode 100755 index 000000000..44064edee --- /dev/null +++ b/src/librssguard/services/newsblur/newsblurnetwork.cpp @@ -0,0 +1,177 @@ +// For license of this file, see /LICENSE.md. + +#include "services/newsblur/newsblurnetwork.h" + +#include "3rd-party/boolinq/boolinq.h" +#include "database/databasequeries.h" +#include "exceptions/applicationexception.h" +#include "exceptions/feedfetchexception.h" +#include "exceptions/networkexception.h" +#include "miscellaneous/application.h" +#include "network-web/networkfactory.h" +#include "network-web/webfactory.h" +#include "services/abstract/category.h" +#include "services/abstract/label.h" +#include "services/abstract/labelsnode.h" +#include "services/newsblur/definitions.h" + +#include +#include +#include + +NewsBlurNetwork::NewsBlurNetwork(QObject* parent) + : QObject(parent), m_root(nullptr), m_username(QString()), m_password(QString()), m_baseUrl(QSL(NEWSBLUR_URL)), + m_batchSize(NEWSBLUR_DEFAULT_BATCH_SIZE), + m_downloadOnlyUnreadMessages(false) { + clearCredentials(); +} + +LoginResult NewsBlurNetwork::login(const QNetworkProxy& proxy) { + const QString full_url = generateFullUrl(Operations::Login); + const auto timeout = qApp->settings()->value(GROUP(Feeds), SETTING(Feeds::UpdateTimeout)).toInt(); + const QString data = QSL("username=%1&password=%2").arg(m_username, m_password); + QByteArray output; + auto network_result = NetworkFactory::performNetworkOperation(full_url, + timeout, + data.toUtf8(), + output, + QNetworkAccessManager::Operation::PostOperation, + { { + QSL(HTTP_HEADERS_CONTENT_TYPE).toLocal8Bit(), + QSL("application/x-www-form-urlencoded").toLocal8Bit() + } }, + false, + {}, + {}, + proxy); + + if (network_result.first == QNetworkReply::NetworkError::NoError) { + QJsonParseError err; + QJsonDocument doc = QJsonDocument::fromJson(output, &err); + + if (err.error != QJsonParseError::ParseError::NoError) { + throw ApplicationException(err.errorString()); + } + + LoginResult res; + + res.decodeBaseResponse(doc); + res.m_userId = doc.object()["user_id"].toInt(); + + return res; + } + else { + throw NetworkException(network_result.first, output); + } +} + +QString NewsBlurNetwork::username() const { + return m_username; +} + +void NewsBlurNetwork::setUsername(const QString& username) { + m_username = username; +} + +QString NewsBlurNetwork::password() const { + return m_password; +} + +void NewsBlurNetwork::setPassword(const QString& password) { + m_password = password; +} + +QString NewsBlurNetwork::baseUrl() const { + return m_baseUrl; +} + +void NewsBlurNetwork::setBaseUrl(const QString& base_url) { + m_baseUrl = base_url; +} + +QPair NewsBlurNetwork::authHeader() const { + return { QSL("Cookie").toLocal8Bit(), + QSL("newsblur_sessionid=%1").arg(m_authSid).toLocal8Bit() }; +} + +void NewsBlurNetwork::ensureLogin(const QNetworkProxy& proxy) { + if (m_authSid.isEmpty()) { + try { + auto log = login(proxy); + + if (log.m_authenticated && !log.m_sessiodId.isEmpty()) { + m_authSid = log.m_sessiodId; + } + else { + throw ApplicationException(log.m_errors.join(QSL(", "))); + } + } + catch (const NetworkException& ex) { + throw ex; + } + catch (const ApplicationException& ex) { + throw ex; + } + } +} + +int NewsBlurNetwork::batchSize() const { + return m_batchSize; +} + +void NewsBlurNetwork::setBatchSize(int batch_size) { + m_batchSize = batch_size; +} + +void NewsBlurNetwork::clearCredentials() { + m_authSid = {}; + m_userId = {}; +} + +QString NewsBlurNetwork::sanitizedBaseUrl() const { + QString base_url = m_baseUrl; + + if (!base_url.endsWith('/')) { + base_url = base_url + QL1C('/'); + } + + return base_url; +} + +QString NewsBlurNetwork::generateFullUrl(NewsBlurNetwork::Operations operation) const { + switch (operation) { + case Operations::Login: + return sanitizedBaseUrl() + QSL(NEWSBLUR_API_LOGIN); + + default: + return sanitizedBaseUrl(); + } +} + +void NewsBlurNetwork::setRoot(NewsBlurServiceRoot* root) { + m_root = root; +} + +bool NewsBlurNetwork::downloadOnlyUnreadMessages() const { + return m_downloadOnlyUnreadMessages; +} + +void NewsBlurNetwork::setDownloadOnlyUnreadMessages(bool download_only_unread) { + m_downloadOnlyUnreadMessages = download_only_unread; +} + +void ApiResult::decodeBaseResponse(const QJsonDocument& doc) { + m_authenticated = doc.object()["authenticated"].toBool(); + m_code = doc.object()["code"].toInt(); + + QStringList errs; + QJsonObject obj_errs = doc.object()["errors"].toObject(); + + for (const QString& key : obj_errs.keys()) { + for (const QJsonValue& val: obj_errs.value(key).toArray()) { + errs << val.toString(); + } + } + + m_errors = errs; +} diff --git a/src/librssguard/services/newsblur/newsblurnetwork.h b/src/librssguard/services/newsblur/newsblurnetwork.h new file mode 100755 index 000000000..c6a130fe0 --- /dev/null +++ b/src/librssguard/services/newsblur/newsblurnetwork.h @@ -0,0 +1,77 @@ +// For license of this file, see /LICENSE.md. + +#ifndef NEWSBLURNETWORK_H +#define NEWSBLURNETWORK_H + +#include + +#include "network-web/networkfactory.h" +#include "services/abstract/feed.h" +#include "services/newsblur/newsblurserviceroot.h" + +struct ApiResult { + bool m_authenticated; + int m_code; + QStringList m_errors; + + void decodeBaseResponse(const QJsonDocument& doc); +}; + +struct LoginResult : ApiResult { + QString m_sessiodId; + int m_userId; +}; + +class NewsBlurNetwork : public QObject { + Q_OBJECT + + public: + enum class Operations { + Login + }; + + explicit NewsBlurNetwork(QObject* parent = nullptr); + + void clearCredentials(); + + QString username() const; + void setUsername(const QString& username); + + QString password() const; + void setPassword(const QString& password); + + QString baseUrl() const; + void setBaseUrl(const QString& base_url); + + int batchSize() const; + void setBatchSize(int batch_size); + + bool downloadOnlyUnreadMessages() const; + void setDownloadOnlyUnreadMessages(bool download_only_unread); + + void setRoot(NewsBlurServiceRoot* root); + + // API methods. + LoginResult login(const QNetworkProxy& proxy); + + private: + QPair authHeader() const; + + // Make sure we are logged in and if we are not, throw exception. + void ensureLogin(const QNetworkProxy& proxy); + + QString sanitizedBaseUrl() const; + QString generateFullUrl(Operations operation) const; + + private: + NewsBlurServiceRoot* m_root; + QString m_username; + QString m_password; + QString m_baseUrl; + int m_batchSize; + bool m_downloadOnlyUnreadMessages; + QString m_authSid; + int m_userId; +}; + +#endif // NEWSBLURNETWORK_H diff --git a/src/librssguard/services/newsblur/newsblurserviceroot.cpp b/src/librssguard/services/newsblur/newsblurserviceroot.cpp new file mode 100755 index 000000000..0d4c1712f --- /dev/null +++ b/src/librssguard/services/newsblur/newsblurserviceroot.cpp @@ -0,0 +1,145 @@ +// For license of this file, see /LICENSE.md. + +#include "services/newsblur/newsblurserviceroot.h" + +#include "database/databasequeries.h" +#include "definitions/definitions.h" +#include "exceptions/feedfetchexception.h" +#include "miscellaneous/application.h" +#include "miscellaneous/iconfactory.h" +#include "miscellaneous/mutex.h" +#include "miscellaneous/textfactory.h" +#include "network-web/oauth2service.h" +#include "services/abstract/importantnode.h" +#include "services/abstract/recyclebin.h" +#include "services/newsblur/definitions.h" +#include "services/newsblur/gui/formeditnewsbluraccount.h" +#include "services/newsblur/newsblurentrypoint.h" +#include "services/newsblur/newsblurnetwork.h" + +NewsBlurServiceRoot::NewsBlurServiceRoot(RootItem* parent) + : ServiceRoot(parent), m_network(new NewsBlurNetwork(this)) { + m_network->setRoot(this); + setIcon(NewsBlurEntryPoint().icon()); +} + +bool NewsBlurServiceRoot::isSyncable() const { + return true; +} + +bool NewsBlurServiceRoot::canBeEdited() const { + return true; +} + +bool NewsBlurServiceRoot::editViaGui() { + FormEditNewsBlurAccount form_pointer(qApp->mainFormWidget()); + + form_pointer.addEditAccount(this); + return true; +} + +QVariantHash NewsBlurServiceRoot::customDatabaseData() const { + QVariantHash data; + + data[QSL("username")] = m_network->username(); + data[QSL("password")] = TextFactory::encrypt(m_network->password()); + data[QSL("url")] = m_network->baseUrl(); + + return data; +} + +void NewsBlurServiceRoot::setCustomDatabaseData(const QVariantHash& data) { + m_network->setUsername(data[QSL("username")].toString()); + m_network->setPassword(TextFactory::decrypt(data[QSL("password")].toString())); + m_network->setBaseUrl(data[QSL("url")].toString()); +} + +QList NewsBlurServiceRoot::obtainNewMessages(Feed* feed, + const QHash& stated_messages, + const QHash& tagged_messages) { + Feed::Status error = Feed::Status::Normal; + QList msgs; + + // TODO:: + + if (error != Feed::Status::NewMessages && error != Feed::Status::Normal) { + throw FeedFetchException(error); + } + else { + return msgs; + } +} + +void NewsBlurServiceRoot::start(bool freshly_activated) { + if (!freshly_activated) { + DatabaseQueries::loadFromDatabase(this); + loadCacheFromFile(); + } + + updateTitleIcon(); + + if (getSubTreeFeeds().isEmpty()) { + syncIn(); + } +} + +QString NewsBlurServiceRoot::code() const { + return NewsBlurEntryPoint().code(); +} + +void NewsBlurServiceRoot::saveAllCachedData(bool ignore_errors) { + auto msg_cache = takeMessageCache(); + QMapIterator i(msg_cache.m_cachedStatesRead); + + /* + // Save the actual data read/unread. + while (i.hasNext()) { + i.next(); + auto key = i.key(); + QStringList ids = i.value(); + + if (!ids.isEmpty()) { + if (network()->markMessagesRead(key, ids, networkProxy()) != QNetworkReply::NetworkError::NoError && + !ignore_errors) { + addMessageStatesToCache(ids, key); + } + } + } + + QMapIterator> j(msg_cache.m_cachedStatesImportant); + + // Save the actual data important/not important. + while (j.hasNext()) { + j.next(); + auto key = j.key(); + QList messages = j.value(); + + if (!messages.isEmpty()) { + QStringList custom_ids; custom_ids.reserve(messages.size()); + + for (const Message& msg : messages) { + custom_ids.append(msg.m_customId); + } + + if (network()->markMessagesStarred(key, custom_ids, networkProxy()) != QNetworkReply::NetworkError::NoError && + !ignore_errors) { + addMessageStatesToCache(messages, key); + } + } + } + */ +} + +ServiceRoot::LabelOperation NewsBlurServiceRoot::supportedLabelOperations() const { + return ServiceRoot::LabelOperation::Synchronised; +} + +void NewsBlurServiceRoot::updateTitleIcon() { + setTitle(QSL("%1 (%2)").arg(m_network->username(), NewsBlurEntryPoint().name())); +} + +RootItem* NewsBlurServiceRoot::obtainNewTreeForSyncIn() const { + return nullptr; + + //return m_network->categoriesFeedsLabelsTree(true, networkProxy()); +} diff --git a/src/librssguard/services/newsblur/newsblurserviceroot.h b/src/librssguard/services/newsblur/newsblurserviceroot.h new file mode 100755 index 000000000..60ec2fc46 --- /dev/null +++ b/src/librssguard/services/newsblur/newsblurserviceroot.h @@ -0,0 +1,46 @@ +// For license of this file, see /LICENSE.md. + +#ifndef NEWSBLURSERVICEROOT_H +#define NEWSBLURSERVICEROOT_H + +#include "services/abstract/cacheforserviceroot.h" +#include "services/abstract/serviceroot.h" + +class NewsBlurNetwork; + +class NewsBlurServiceRoot : public ServiceRoot, public CacheForServiceRoot { + Q_OBJECT + + public: + explicit NewsBlurServiceRoot(RootItem* parent = nullptr); + + virtual bool isSyncable() const; + virtual bool canBeEdited() const; + virtual bool editViaGui(); + virtual void start(bool freshly_activated); + virtual QString code() const; + virtual void saveAllCachedData(bool ignore_errors); + virtual LabelOperation supportedLabelOperations() const; + virtual QVariantHash customDatabaseData() const; + virtual void setCustomDatabaseData(const QVariantHash& data); + virtual QList obtainNewMessages(Feed* feed, + const QHash& stated_messages, + const QHash& tagged_messages); + + NewsBlurNetwork* network() const; + + protected: + virtual RootItem* obtainNewTreeForSyncIn() const; + + private: + void updateTitleIcon(); + + private: + NewsBlurNetwork* m_network; +}; + +inline NewsBlurNetwork* NewsBlurServiceRoot::network() const { + return m_network; +} + +#endif // NEWSBLURSERVICEROOT_H