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 000000000..658ffe1f1
Binary files /dev/null and b/resources/graphics/misc/newsblur.png differ
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
+
+ 1
+
+
+ LineEditWithStatus
+ QWidget
+
+ 1
+
+
+ MessageCountSpinBox
+ QSpinBox
+
+
+
+ HelpSpoiler
+ QWidget
+
+ 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