diff --git a/resources/graphics/misc/google.png b/resources/graphics/misc/google.png
new file mode 100644
index 000000000..83638cbb2
Binary files /dev/null and b/resources/graphics/misc/google.png differ
diff --git a/resources/rssguard.qrc b/resources/rssguard.qrc
index 35a39dceb..3c58b674a 100755
--- a/resources/rssguard.qrc
+++ b/resources/rssguard.qrc
@@ -17,6 +17,7 @@
graphics/misc/adblock.png
graphics/misc/adblock-disabled.png
graphics/misc/gmail.png
+ graphics/misc/google.png
graphics/misc/image-placeholder.png
graphics/misc/inoreader.png
graphics/misc/nextcloud.png
diff --git a/resources/sql/db_update_mysql_18_19.sql b/resources/sql/db_update_mysql_18_19.sql
new file mode 100755
index 000000000..c2a2ec363
--- /dev/null
+++ b/resources/sql/db_update_mysql_18_19.sql
@@ -0,0 +1,12 @@
+CREATE TABLE IF NOT EXISTS GoogleReaderApiAccounts (
+ id INTEGER,
+ type INTEGER NOT NULL CHECK (type >= 1),
+ username TEXT NOT NULL,
+ password TEXT,
+ url TEXT NOT NULL,
+ msg_limit INTEGER NOT NULL DEFAULT -1 CHECK (msg_limit >= -1),
+
+ FOREIGN KEY (id) REFERENCES Accounts (id)
+);
+-- !
+UPDATE Information SET inf_value = '19' WHERE inf_key = 'schema_version';
\ No newline at end of file
diff --git a/resources/sql/db_update_sqlite_18_19.sql b/resources/sql/db_update_sqlite_18_19.sql
new file mode 100755
index 000000000..c2a2ec363
--- /dev/null
+++ b/resources/sql/db_update_sqlite_18_19.sql
@@ -0,0 +1,12 @@
+CREATE TABLE IF NOT EXISTS GoogleReaderApiAccounts (
+ id INTEGER,
+ type INTEGER NOT NULL CHECK (type >= 1),
+ username TEXT NOT NULL,
+ password TEXT,
+ url TEXT NOT NULL,
+ msg_limit INTEGER NOT NULL DEFAULT -1 CHECK (msg_limit >= -1),
+
+ FOREIGN KEY (id) REFERENCES Accounts (id)
+);
+-- !
+UPDATE Information SET inf_value = '19' WHERE inf_key = 'schema_version';
\ No newline at end of file
diff --git a/src/librssguard/definitions/definitions.h b/src/librssguard/definitions/definitions.h
index d00b6c590..f8c7ef97b 100755
--- a/src/librssguard/definitions/definitions.h
+++ b/src/librssguard/definitions/definitions.h
@@ -12,6 +12,7 @@
#define SERVICE_CODE_STD_RSS "std-rss"
#define SERVICE_CODE_TT_RSS "tt-rss"
#define SERVICE_CODE_OWNCLOUD "owncloud"
+#define SERVICE_CODE_GREADER "greader"
#define SERVICE_CODE_INOREADER "inoreader"
#define SERVICE_CODE_GMAIL "gmail"
@@ -103,6 +104,7 @@
#define LOGSEC_CORE "core: "
#define LOGSEC_DB "database: "
#define LOGSEC_NEXTCLOUD "nextcloud: "
+#define LOGSEC_GREADER "greader: "
#define LOGSEC_INOREADER "inoreader: "
#define LOGSEC_TTRSS "tt-rss: "
#define LOGSEC_GMAIL "gmail: "
diff --git a/src/librssguard/librssguard.pro b/src/librssguard/librssguard.pro
index dfb602b1e..0c760247c 100644
--- a/src/librssguard/librssguard.pro
+++ b/src/librssguard/librssguard.pro
@@ -150,6 +150,13 @@ HEADERS += core/feeddownloader.h \
services/gmail/gui/formeditgmailaccount.h \
services/gmail/gui/gmailaccountdetails.h \
services/gmail/network/gmailnetworkfactory.h \
+ services/greader/definitions.h \
+ services/greader/greaderentrypoint.h \
+ services/greader/greaderfeed.h \
+ services/greader/greadernetwork.h \
+ services/greader/greaderserviceroot.h \
+ services/greader/gui/formeditgreaderaccount.h \
+ services/greader/gui/greaderaccountdetails.h \
services/inoreader/definitions.h \
services/inoreader/gui/formeditinoreaderaccount.h \
services/inoreader/gui/inoreaderaccountdetails.h \
@@ -310,6 +317,12 @@ SOURCES += core/feeddownloader.cpp \
services/gmail/gui/formeditgmailaccount.cpp \
services/gmail/gui/gmailaccountdetails.cpp \
services/gmail/network/gmailnetworkfactory.cpp \
+ services/greader/greaderentrypoint.cpp \
+ services/greader/greaderfeed.cpp \
+ services/greader/greadernetwork.cpp \
+ services/greader/greaderserviceroot.cpp \
+ services/greader/gui/formeditgreaderaccount.cpp \
+ services/greader/gui/greaderaccountdetails.cpp \
services/inoreader/gui/formeditinoreaderaccount.cpp \
services/inoreader/gui/inoreaderaccountdetails.cpp \
services/inoreader/inoreaderentrypoint.cpp \
@@ -384,6 +397,7 @@ FORMS += gui/dialogs/formabout.ui \
services/abstract/gui/formfeeddetails.ui \
services/abstract/gui/authenticationdetails.ui \
services/gmail/gui/gmailaccountdetails.ui \
+ services/greader/gui/greaderaccountdetails.ui \
services/inoreader/gui/inoreaderaccountdetails.ui \
services/owncloud/gui/owncloudaccountdetails.ui \
services/standard/gui/formstandardcategorydetails.ui \
diff --git a/src/librssguard/miscellaneous/databasequeries.cpp b/src/librssguard/miscellaneous/databasequeries.cpp
index 9ba775d4e..43c4cfd05 100755
--- a/src/librssguard/miscellaneous/databasequeries.cpp
+++ b/src/librssguard/miscellaneous/databasequeries.cpp
@@ -13,6 +13,9 @@
#include "services/gmail/gmailfeed.h"
#include "services/gmail/gmailserviceroot.h"
#include "services/gmail/network/gmailnetworkfactory.h"
+#include "services/greader/definitions.h"
+#include "services/greader/greadernetwork.h"
+#include "services/greader/greaderserviceroot.h"
#include "services/inoreader/definitions.h"
#include "services/inoreader/inoreaderfeed.h"
#include "services/inoreader/inoreaderserviceroot.h"
@@ -1675,6 +1678,46 @@ void DatabaseQueries::fillBaseAccountData(const QSqlDatabase& db, ServiceRoot* a
}
}
+QList DatabaseQueries::getGreaderAccounts(const QSqlDatabase& db, bool* ok) {
+ QSqlQuery query(db);
+ QList roots;
+
+ if (query.exec("SELECT * FROM GoogleReaderApiAccounts;")) {
+ while (query.next()) {
+ auto* root = new GreaderServiceRoot();
+
+ root->setId(query.value(0).toInt());
+ root->setAccountId(query.value(0).toInt());
+ root->network()->setService(GreaderServiceRoot::Service(query.value(1).toInt()));
+ root->network()->setUsername(query.value(2).toString());
+ root->network()->setPassword(TextFactory::decrypt(query.value(3).toString()));
+ root->network()->setBaseUrl(query.value(4).toString());
+ root->network()->setBatchSize(query.value(5).toInt());
+ root->updateTitle();
+
+ fillBaseAccountData(db, root);
+
+ roots.append(root);
+ }
+
+ if (ok != nullptr) {
+ *ok = true;
+ }
+ }
+ else {
+ qWarningNN << LOGSEC_GREADER
+ << "Getting list of activated accounts failed: '"
+ << query.lastError().text()
+ << "'.";
+
+ if (ok != nullptr) {
+ *ok = false;
+ }
+ }
+
+ return roots;
+}
+
QList DatabaseQueries::getOwnCloudAccounts(const QSqlDatabase& db, bool* ok) {
QSqlQuery query(db);
QList roots;
@@ -1768,6 +1811,32 @@ bool DatabaseQueries::deleteOwnCloudAccount(const QSqlDatabase& db, int account_
return q.exec();
}
+bool DatabaseQueries::overwriteGreaderAccount(const QSqlDatabase& db, const QString& username, const QString& password,
+ const QString& url, int batch_size, int account_id) {
+ QSqlQuery query(db);
+
+ query.prepare("UPDATE GoogleReaderApiAccounts "
+ "SET username = :username, password = :password, url = :url, "
+ "msg_limit = :msg_limit "
+ "WHERE id = :id;");
+ query.bindValue(QSL(":username"), username);
+ query.bindValue(QSL(":password"), TextFactory::encrypt(password));
+ query.bindValue(QSL(":url"), url);
+ query.bindValue(QSL(":id"), account_id);
+ query.bindValue(QSL(":msg_limit"), batch_size <= 0 ? GREADER_UNLIMITED_BATCH_SIZE : batch_size);
+
+ if (query.exec()) {
+ return true;
+ }
+ else {
+ qWarningNN << LOGSEC_GREADER
+ << "Updating account failed: '"
+ << query.lastError().text()
+ << "'.";
+ return false;
+ }
+}
+
bool DatabaseQueries::overwriteOwnCloudAccount(const QSqlDatabase& db, const QString& username, const QString& password,
const QString& url, bool force_server_side_feed_update, int batch_size,
bool download_only_unread_messages, int account_id) {
@@ -1797,6 +1866,32 @@ bool DatabaseQueries::overwriteOwnCloudAccount(const QSqlDatabase& db, const QSt
}
}
+bool DatabaseQueries::createGreaderAccount(const QSqlDatabase& db, int id_to_assign, const QString& username,
+ const QString& password, GreaderServiceRoot::Service service,
+ const QString& url, int batch_size) {
+ QSqlQuery q(db);
+
+ q.prepare("INSERT INTO GoogleReaderApiAccounts (id, type, username, password, url, msg_limit) "
+ "VALUES (:id, :service, :username, :password, :url, :msg_limit);");
+ q.bindValue(QSL(":id"), id_to_assign);
+ q.bindValue(QSL(":username"), username);
+ q.bindValue(QSL(":service"), int(service));
+ q.bindValue(QSL(":password"), TextFactory::encrypt(password));
+ q.bindValue(QSL(":url"), url);
+ q.bindValue(QSL(":msg_limit"), batch_size <= 0 ? GREADER_UNLIMITED_BATCH_SIZE : batch_size);
+
+ if (q.exec()) {
+ return true;
+ }
+ else {
+ qWarningNN << LOGSEC_GREADER
+ << "Inserting of new account failed: '"
+ << q.lastError().text()
+ << "'.";
+ return false;
+ }
+}
+
bool DatabaseQueries::createOwnCloudAccount(const QSqlDatabase& db, int id_to_assign, const QString& username,
const QString& password, const QString& url,
bool force_server_side_feed_update,
diff --git a/src/librssguard/miscellaneous/databasequeries.h b/src/librssguard/miscellaneous/databasequeries.h
index 9453c1a3c..9241c86a4 100644
--- a/src/librssguard/miscellaneous/databasequeries.h
+++ b/src/librssguard/miscellaneous/databasequeries.h
@@ -9,6 +9,7 @@
#include "services/abstract/category.h"
#include "services/abstract/label.h"
#include "services/abstract/serviceroot.h"
+#include "services/greader/greaderserviceroot.h"
#include "services/standard/standardfeed.h"
#include
@@ -147,14 +148,22 @@ class DatabaseQueries {
template
static void fillFeedData(T* feed, const QSqlRecord& sql_record);
+ // Greader account.
+ static QList getGreaderAccounts(const QSqlDatabase& db, bool* ok = nullptr);
+ static bool createGreaderAccount(const QSqlDatabase& db, int id_to_assign, const QString& username,
+ const QString& password, GreaderServiceRoot::Service service,
+ const QString& url, int batch_size);
+ static bool overwriteGreaderAccount(const QSqlDatabase& db, const QString& username, const QString& password,
+ const QString& url, int batch_size, int account_id);
+
// Nextcloud account.
static QList getOwnCloudAccounts(const QSqlDatabase& db, bool* ok = nullptr);
static bool deleteOwnCloudAccount(const QSqlDatabase& db, int account_id);
static bool overwriteOwnCloudAccount(const QSqlDatabase& db, const QString& username, const QString& password,
const QString& url, bool force_server_side_feed_update, int batch_size,
bool download_only_unread_messages, int account_id);
- static bool createOwnCloudAccount(const QSqlDatabase& db, int id_to_assign, const QString& username, const QString& password,
- const QString& url, bool force_server_side_feed_update,
+ static bool createOwnCloudAccount(const QSqlDatabase& db, int id_to_assign, const QString& username,
+ const QString& password, const QString& url, bool force_server_side_feed_update,
bool download_only_unread_messages, int batch_size);
// TT-RSS acccount.
diff --git a/src/librssguard/miscellaneous/feedreader.cpp b/src/librssguard/miscellaneous/feedreader.cpp
index 87caab1a3..4e184eb58 100644
--- a/src/librssguard/miscellaneous/feedreader.cpp
+++ b/src/librssguard/miscellaneous/feedreader.cpp
@@ -15,6 +15,7 @@
#include "services/abstract/cacheforserviceroot.h"
#include "services/abstract/serviceroot.h"
#include "services/gmail/gmailentrypoint.h"
+#include "services/greader/greaderentrypoint.h"
#include "services/inoreader/inoreaderentrypoint.h"
#include "services/owncloud/owncloudserviceentrypoint.h"
#include "services/standard/standardserviceentrypoint.h"
@@ -55,6 +56,7 @@ QList FeedReader::feedServices() {
if (m_feedServices.isEmpty()) {
// NOTE: All installed services create their entry points here.
m_feedServices.append(new GmailEntryPoint());
+ m_feedServices.append(new GreaderEntryPoint());
m_feedServices.append(new InoreaderEntryPoint());
m_feedServices.append(new OwnCloudServiceEntryPoint());
m_feedServices.append(new StandardServiceEntryPoint());
diff --git a/src/librssguard/services/abstract/gui/formaccountdetails.h b/src/librssguard/services/abstract/gui/formaccountdetails.h
index a80f6e141..8a9ee25fa 100644
--- a/src/librssguard/services/abstract/gui/formaccountdetails.h
+++ b/src/librssguard/services/abstract/gui/formaccountdetails.h
@@ -73,11 +73,9 @@ inline bool FormAccountDetails::applyInternal() {
QSqlDatabase database = qApp->database()->connection(QSL("FormAccountDetails"));
bool creating = m_account == nullptr;
- if (m_account == nullptr) {
+ if (creating) {
m_account = new T();
m_account->setAccountId(DatabaseQueries::createBaseAccount(database, m_account->code()));
-
- //m_account->setId(m_account->accountId());
}
m_account->setNetworkProxy(m_proxyDetails->proxy());
diff --git a/src/librssguard/services/greader/definitions.h b/src/librssguard/services/greader/definitions.h
new file mode 100755
index 000000000..f47edd8f3
--- /dev/null
+++ b/src/librssguard/services/greader/definitions.h
@@ -0,0 +1,6 @@
+#ifndef GREADER_DEFINITIONS_H
+#define GREADER_DEFINITIONS_H
+
+#define GREADER_UNLIMITED_BATCH_SIZE -1
+
+#endif // GREADER_DEFINITIONS_H
diff --git a/src/librssguard/services/greader/greaderentrypoint.cpp b/src/librssguard/services/greader/greaderentrypoint.cpp
new file mode 100755
index 000000000..a9871ad7a
--- /dev/null
+++ b/src/librssguard/services/greader/greaderentrypoint.cpp
@@ -0,0 +1,44 @@
+// For license of this file, see /LICENSE.md.
+
+#include "services/greader/greaderentrypoint.h"
+
+#include "definitions/definitions.h"
+#include "miscellaneous/application.h"
+#include "miscellaneous/databasequeries.h"
+#include "miscellaneous/iconfactory.h"
+#include "services/greader/definitions.h"
+#include "services/greader/greaderserviceroot.h"
+#include "services/greader/gui/formeditgreaderaccount.h"
+
+ServiceRoot* GreaderEntryPoint::createNewRoot() const {
+ FormEditGreaderAccount form_acc(qApp->mainFormWidget());
+
+ return form_acc.addEditAccount();
+}
+
+QList GreaderEntryPoint::initializeSubtree() const {
+ QSqlDatabase database = qApp->database()->connection(QSL("GreaderEntryPoint"));
+
+ return DatabaseQueries::getGreaderAccounts(database);
+}
+
+QString GreaderEntryPoint::name() const {
+ return QSL("Google Reader API");
+}
+
+QString GreaderEntryPoint::code() const {
+ return SERVICE_CODE_GREADER;
+}
+
+QString GreaderEntryPoint::description() const {
+ return QObject::tr("Google Reader API is used by many online RSS readers. This is here to support") +
+ QSL(" FreshRSS, Bazqux, TheOldReader.");
+}
+
+QString GreaderEntryPoint::author() const {
+ return APP_AUTHOR;
+}
+
+QIcon GreaderEntryPoint::icon() const {
+ return qApp->icons()->miscIcon(QSL("google"));
+}
diff --git a/src/librssguard/services/greader/greaderentrypoint.h b/src/librssguard/services/greader/greaderentrypoint.h
new file mode 100755
index 000000000..3cb92c374
--- /dev/null
+++ b/src/librssguard/services/greader/greaderentrypoint.h
@@ -0,0 +1,19 @@
+// For license of this file, see /LICENSE.md.
+
+#ifndef GREADERENTRYPOINT_H
+#define GREADERENTRYPOINT_H
+
+#include "services/abstract/serviceentrypoint.h"
+
+class GreaderEntryPoint : 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 // GREADERENTRYPOINT_H
diff --git a/src/librssguard/services/greader/greaderfeed.cpp b/src/librssguard/services/greader/greaderfeed.cpp
new file mode 100755
index 000000000..f8770d3d8
--- /dev/null
+++ b/src/librssguard/services/greader/greaderfeed.cpp
@@ -0,0 +1,29 @@
+// For license of this file, see /LICENSE.md.
+
+#include "services/greader/greaderfeed.h"
+
+#include "miscellaneous/application.h"
+#include "miscellaneous/iconfactory.h"
+#include "services/greader/greadernetwork.h"
+#include "services/greader/greaderserviceroot.h"
+
+GreaderFeed::GreaderFeed(RootItem* parent) : Feed(parent) {}
+
+GreaderFeed::GreaderFeed(const QSqlRecord& record) : Feed(record) {}
+
+GreaderServiceRoot* GreaderFeed::serviceRoot() const {
+ return qobject_cast(getParentServiceRoot());
+}
+
+QList GreaderFeed::obtainNewMessages(bool* error_during_obtaining) {
+ Feed::Status error = Feed::Status::Normal;
+ QList messages = serviceRoot()->network()->messages(getParentServiceRoot(), customId(), error);
+
+ setStatus(error);
+
+ if (error == Feed::Status::NetworkError || error == Feed::Status::AuthError) {
+ *error_during_obtaining = true;
+ }
+
+ return messages;
+}
diff --git a/src/librssguard/services/greader/greaderfeed.h b/src/librssguard/services/greader/greaderfeed.h
new file mode 100755
index 000000000..e8cbfa40e
--- /dev/null
+++ b/src/librssguard/services/greader/greaderfeed.h
@@ -0,0 +1,19 @@
+// For license of this file, see /LICENSE.md.
+
+#ifndef GREADERFEED_H
+#define GREADERFEED_H
+
+#include "services/abstract/feed.h"
+
+class GreaderServiceRoot;
+
+class GreaderFeed : public Feed {
+ public:
+ explicit GreaderFeed(RootItem* parent = nullptr);
+ explicit GreaderFeed(const QSqlRecord& record);
+
+ GreaderServiceRoot* serviceRoot() const;
+ QList obtainNewMessages(bool* error_during_obtaining);
+};
+
+#endif // GREADERFEED_H
diff --git a/src/librssguard/services/greader/greadernetwork.cpp b/src/librssguard/services/greader/greadernetwork.cpp
new file mode 100755
index 000000000..2cada5296
--- /dev/null
+++ b/src/librssguard/services/greader/greadernetwork.cpp
@@ -0,0 +1,70 @@
+// For license of this file, see /LICENSE.md.
+
+#include "services/greader/greadernetwork.h"
+
+GreaderNetwork::GreaderNetwork(QObject* parent)
+ : QObject(parent), m_service(GreaderServiceRoot::Service::FreshRss) {}
+
+QList GreaderNetwork::messages(ServiceRoot* root, const QString& stream_id, Feed::Status& error) {
+ return {};
+}
+
+NetworkResult GreaderNetwork::status(const QNetworkProxy& custom_proxy) const {
+ return NetworkResult(QNetworkReply::NetworkError::NoError, {});
+}
+
+GreaderServiceRoot::Service GreaderNetwork::service() const {
+ return m_service;
+}
+
+void GreaderNetwork::setService(const GreaderServiceRoot::Service& service) {
+ m_service = service;
+}
+
+QString GreaderNetwork::username() const {
+ return m_username;
+}
+
+void GreaderNetwork::setUsername(const QString& username) {
+ m_username = username;
+}
+
+QString GreaderNetwork::password() const {
+ return m_password;
+}
+
+void GreaderNetwork::setPassword(const QString& password) {
+ m_password = password;
+}
+
+QString GreaderNetwork::baseUrl() const {
+ return m_baseUrl;
+}
+
+void GreaderNetwork::setBaseUrl(const QString& base_url) {
+ m_baseUrl = base_url;
+}
+
+QString GreaderNetwork::serviceToString(GreaderServiceRoot::Service service) {
+ switch (service) {
+ case GreaderServiceRoot::Service::FreshRss:
+ return QSL("FreshRSS");
+
+ case GreaderServiceRoot::Service::Bazqux:
+ return QSL("Bazqux");
+
+ case GreaderServiceRoot::Service::TheOldReader:
+ return QSL("TheOldReader");
+
+ default:
+ return tr("Unknown service");
+ }
+}
+
+int GreaderNetwork::batchSize() const {
+ return m_batchSize;
+}
+
+void GreaderNetwork::setBatchSize(int batch_size) {
+ m_batchSize = batch_size;
+}
diff --git a/src/librssguard/services/greader/greadernetwork.h b/src/librssguard/services/greader/greadernetwork.h
new file mode 100755
index 000000000..3a7442fbe
--- /dev/null
+++ b/src/librssguard/services/greader/greadernetwork.h
@@ -0,0 +1,49 @@
+// For license of this file, see /LICENSE.md.
+
+#ifndef GREADERNETWORK_H
+#define GREADERNETWORK_H
+
+#include
+
+#include "network-web/networkfactory.h"
+#include "services/abstract/feed.h"
+#include "services/greader/greaderserviceroot.h"
+
+class GreaderNetwork : public QObject {
+ Q_OBJECT
+
+ public:
+ explicit GreaderNetwork(QObject* parent = nullptr);
+
+ // Network operations.
+ QList messages(ServiceRoot* root, const QString& stream_id, Feed::Status& error);
+
+ NetworkResult status(const QNetworkProxy& custom_proxy) const;
+
+ // Metadata.
+ GreaderServiceRoot::Service service() const;
+ void setService(const GreaderServiceRoot::Service& service);
+
+ 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);
+
+ static QString serviceToString(GreaderServiceRoot::Service service);
+
+ int batchSize() const;
+ void setBatchSize(int batch_size);
+
+ private:
+ GreaderServiceRoot::Service m_service;
+ QString m_username;
+ QString m_password;
+ QString m_baseUrl;
+ int m_batchSize;
+};
+
+#endif // GREADERNETWORK_H
diff --git a/src/librssguard/services/greader/greaderserviceroot.cpp b/src/librssguard/services/greader/greaderserviceroot.cpp
new file mode 100755
index 000000000..825a9ac75
--- /dev/null
+++ b/src/librssguard/services/greader/greaderserviceroot.cpp
@@ -0,0 +1,153 @@
+// For license of this file, see /LICENSE.md.
+
+#include "services/greader/greaderserviceroot.h"
+
+#include "definitions/definitions.h"
+#include "miscellaneous/application.h"
+#include "miscellaneous/databasequeries.h"
+#include "miscellaneous/iconfactory.h"
+#include "miscellaneous/mutex.h"
+#include "miscellaneous/textfactory.h"
+#include "services/abstract/importantnode.h"
+#include "services/abstract/recyclebin.h"
+#include "services/greader/greaderentrypoint.h"
+#include "services/greader/greaderfeed.h"
+#include "services/greader/greadernetwork.h"
+#include "services/greader/gui/formeditgreaderaccount.h"
+
+GreaderServiceRoot::GreaderServiceRoot(RootItem* parent)
+ : ServiceRoot(parent), m_network(new GreaderNetwork(this)) {
+ setIcon(GreaderEntryPoint().icon());
+}
+
+GreaderServiceRoot::~GreaderServiceRoot() {}
+
+bool GreaderServiceRoot::isSyncable() const {
+ return true;
+}
+
+bool GreaderServiceRoot::canBeEdited() const {
+ return true;
+}
+
+bool GreaderServiceRoot::canBeDeleted() const {
+ return true;
+}
+
+bool GreaderServiceRoot::editViaGui() {
+ FormEditGreaderAccount form_pointer(qApp->mainFormWidget());
+
+ form_pointer.addEditAccount(this);
+ return true;
+}
+
+bool GreaderServiceRoot::deleteViaGui() {
+ return false;
+}
+
+void GreaderServiceRoot::start(bool freshly_activated) {
+ Q_UNUSED(freshly_activated)
+ loadFromDatabase();
+ loadCacheFromFile();
+
+ if (childCount() <= 3) {
+ syncIn();
+ }
+}
+
+QString GreaderServiceRoot::code() const {
+ return GreaderEntryPoint().code();
+}
+
+void GreaderServiceRoot::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()) {
+ auto res = network()->markMessagesRead(key, ids, networkProxy());
+
+ if (!ignore_errors && res.first != QNetworkReply::NetworkError::NoError) {
+ 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 feed_ids, guid_hashes;
+
+ for (const Message& msg : messages) {
+ feed_ids.append(msg.m_feedId);
+ guid_hashes.append(msg.m_customHash);
+ }
+
+ auto res = network()->markMessagesStarred(key, feed_ids, guid_hashes, networkProxy());
+
+ if (!ignore_errors && res.first != QNetworkReply::NetworkError::NoError) {
+ addMessageStatesToCache(messages, key);
+ }
+ }
+ }*/
+}
+
+void GreaderServiceRoot::updateTitle() {
+ setTitle(QString("%1 (%2)").arg(m_network->username(),
+ m_network->serviceToString(m_network->service())));
+}
+
+void GreaderServiceRoot::saveAccountDataToDatabase(bool creating_new) {
+ QSqlDatabase database = qApp->database()->connection(metaObject()->className());
+
+ if (!creating_new) {
+ if (DatabaseQueries::overwriteGreaderAccount(database, m_network->username(),
+ m_network->password(), m_network->baseUrl(),
+ m_network->batchSize(), accountId())) {
+ updateTitle();
+ itemChanged(QList() << this);
+ }
+ }
+ else {
+ if (DatabaseQueries::createGreaderAccount(database, accountId(), m_network->username(),
+ m_network->password(), m_network->service(),
+ m_network->baseUrl(), m_network->batchSize())) {
+ updateTitle();
+ }
+ }
+}
+
+RootItem* GreaderServiceRoot::obtainNewTreeForSyncIn() const {
+ return nullptr;
+
+ /*OwnCloudGetFeedsCategoriesResponse feed_cats_response = m_network->feedsCategories(networkProxy());
+
+ if (feed_cats_response.networkError() == QNetworkReply::NetworkError::NoError) {
+ return feed_cats_response.feedsCategories(true);
+ }
+ else {
+ return nullptr;
+ }*/
+}
+
+void GreaderServiceRoot::loadFromDatabase() {
+ QSqlDatabase database = qApp->database()->connection(metaObject()->className());
+ Assignment categories = DatabaseQueries::getCategories(database, accountId());
+ Assignment feeds = DatabaseQueries::getFeeds(database, qApp->feedReader()->messageFilters(), accountId());
+ auto labels = DatabaseQueries::getLabels(database, accountId());
+
+ performInitialAssembly(categories, feeds, labels);
+}
diff --git a/src/librssguard/services/greader/greaderserviceroot.h b/src/librssguard/services/greader/greaderserviceroot.h
new file mode 100755
index 000000000..8dff72123
--- /dev/null
+++ b/src/librssguard/services/greader/greaderserviceroot.h
@@ -0,0 +1,61 @@
+// For license of this file, see /LICENSE.md.
+
+#ifndef GREADERSERVICEROOT_H
+#define GREADERSERVICEROOT_H
+
+#include "services/abstract/cacheforserviceroot.h"
+#include "services/abstract/serviceroot.h"
+
+#include
+
+class GreaderNetwork;
+
+class GreaderServiceRoot : public ServiceRoot, public CacheForServiceRoot {
+ Q_OBJECT
+
+ public:
+ enum class Service {
+ FreshRss = 1,
+ TheOldReader = 2,
+ Bazqux = 4
+ };
+
+ explicit GreaderServiceRoot(RootItem* parent = nullptr);
+ virtual ~GreaderServiceRoot();
+
+ virtual bool isSyncable() const;
+ virtual bool canBeEdited() const;
+ virtual bool canBeDeleted() const;
+ virtual bool editViaGui();
+ virtual bool deleteViaGui();
+ virtual void start(bool freshly_activated);
+ virtual QString code() const;
+ virtual void saveAllCachedData(bool ignore_errors);
+
+ void setNetwork(GreaderNetwork* network);
+ GreaderNetwork* network() const;
+
+ void updateTitle();
+ void saveAccountDataToDatabase(bool creating_new);
+
+ protected:
+ virtual RootItem* obtainNewTreeForSyncIn() const;
+
+ private:
+ void loadFromDatabase();
+
+ private:
+ GreaderNetwork* m_network;
+};
+
+Q_DECLARE_METATYPE(GreaderServiceRoot::Service)
+
+inline void GreaderServiceRoot::setNetwork(GreaderNetwork* network) {
+ m_network = network;
+}
+
+inline GreaderNetwork* GreaderServiceRoot::network() const {
+ return m_network;
+}
+
+#endif // GREADERSERVICEROOT_H
diff --git a/src/librssguard/services/greader/gui/formeditgreaderaccount.cpp b/src/librssguard/services/greader/gui/formeditgreaderaccount.cpp
new file mode 100755
index 000000000..ac83ff9bd
--- /dev/null
+++ b/src/librssguard/services/greader/gui/formeditgreaderaccount.cpp
@@ -0,0 +1,55 @@
+// For license of this file, see /LICENSE.md.
+
+#include "services/greader/gui/formeditgreaderaccount.h"
+
+#include "gui/guiutilities.h"
+#include "miscellaneous/iconfactory.h"
+#include "network-web/networkfactory.h"
+#include "services/greader/definitions.h"
+#include "services/greader/greadernetwork.h"
+#include "services/greader/greaderserviceroot.h"
+#include "services/greader/gui/greaderaccountdetails.h"
+
+FormEditGreaderAccount::FormEditGreaderAccount(QWidget* parent)
+ : FormAccountDetails(qApp->icons()->miscIcon(QSL("google")), parent), m_details(new GreaderAccountDetails(this)) {
+ insertCustomTab(m_details, tr("Server setup"), 0);
+ activateTab(0);
+
+ connect(m_details->m_ui.m_btnTestSetup, &QPushButton::clicked, this, &FormEditGreaderAccount::performTest);
+
+ m_details->m_ui.m_txtUrl->setFocus();
+}
+
+void FormEditGreaderAccount::apply() {
+ bool editing_account = !applyInternal();
+
+ account()->network()->setBaseUrl(m_details->m_ui.m_txtUrl->lineEdit()->text());
+ account()->network()->setUsername(m_details->m_ui.m_txtUsername->lineEdit()->text());
+ account()->network()->setPassword(m_details->m_ui.m_txtPassword->lineEdit()->text());
+ account()->network()->setBatchSize(m_details->m_ui.m_spinLimitMessages->value());
+ account()->network()->setService(m_details->service());
+
+ account()->saveAccountDataToDatabase(!editing_account);
+ accept();
+
+ if (editing_account) {
+ account()->completelyRemoveAllData();
+ account()->syncIn();
+ }
+}
+
+void FormEditGreaderAccount::setEditableAccount(ServiceRoot* editable_account) {
+ FormAccountDetails::setEditableAccount(editable_account);
+
+ GreaderServiceRoot* existing_root = account();
+
+ m_details->setService(existing_root->network()->service());
+ 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());
+}
+
+void FormEditGreaderAccount::performTest() {
+ m_details->performTest(m_proxyDetails->proxy());
+}
diff --git a/src/librssguard/services/greader/gui/formeditgreaderaccount.h b/src/librssguard/services/greader/gui/formeditgreaderaccount.h
new file mode 100755
index 000000000..2a267d21f
--- /dev/null
+++ b/src/librssguard/services/greader/gui/formeditgreaderaccount.h
@@ -0,0 +1,30 @@
+// For license of this file, see /LICENSE.md.
+
+#ifndef FORMEDITGREADERACCOUNT_H
+#define FORMEDITGREADERACCOUNT_H
+
+#include "services/abstract/gui/formaccountdetails.h"
+
+class GreaderAccountDetails;
+class GreaderServiceRoot;
+
+class FormEditGreaderAccount : public FormAccountDetails {
+ Q_OBJECT
+
+ public:
+ explicit FormEditGreaderAccount(QWidget* parent = nullptr);
+
+ protected slots:
+ virtual void apply();
+
+ protected:
+ virtual void setEditableAccount(ServiceRoot* editable_account);
+
+ private slots:
+ void performTest();
+
+ private:
+ GreaderAccountDetails* m_details;
+};
+
+#endif // FORMEDITGREADERACCOUNT_H
diff --git a/src/librssguard/services/greader/gui/greaderaccountdetails.cpp b/src/librssguard/services/greader/gui/greaderaccountdetails.cpp
new file mode 100755
index 000000000..ba91a861e
--- /dev/null
+++ b/src/librssguard/services/greader/gui/greaderaccountdetails.cpp
@@ -0,0 +1,125 @@
+// For license of this file, see /LICENSE.md.
+
+#include "services/greader/gui/greaderaccountdetails.h"
+
+#include "definitions/definitions.h"
+#include "gui/guiutilities.h"
+#include "miscellaneous/systemfactory.h"
+#include "services/greader/definitions.h"
+#include "services/greader/greadernetwork.h"
+
+GreaderAccountDetails::GreaderAccountDetails(QWidget* parent) : QWidget(parent) {
+ m_ui.setupUi(this);
+
+ for (auto serv : { GreaderServiceRoot::Service::FreshRss,
+ GreaderServiceRoot::Service::Bazqux,
+ GreaderServiceRoot::Service::TheOldReader }) {
+ m_ui.m_cmbService->addItem(GreaderNetwork::serviceToString(serv),
+ QVariant::fromValue(serv));
+ }
+
+ m_ui.m_lblTestResult->label()->setWordWrap(true);
+ m_ui.m_txtPassword->lineEdit()->setPlaceholderText(tr("Password for your Nextcloud account"));
+ m_ui.m_txtUsername->lineEdit()->setPlaceholderText(tr("Username for your Nextcloud account"));
+ m_ui.m_txtUrl->lineEdit()->setPlaceholderText(tr("URL of your Nextcloud server, without any API 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->setText(
+ tr("Limiting number of downloaded messages per feed makes updating of feeds faster but if your feed contains "
+ "bigger number of messages than specified limit, then some messages might not be downloaded during feed update."));
+
+ connect(m_ui.m_spinLimitMessages, static_cast(&QSpinBox::valueChanged), this, [=](int value) {
+ if (value <= 0) {
+ m_ui.m_spinLimitMessages->setSuffix(QSL(" ") + tr("= unlimited"));
+ }
+ else {
+ m_ui.m_spinLimitMessages->setSuffix(QSL(" ") + tr("messages"));
+ }
+ });
+
+ GuiUtilities::setLabelAsNotice(*m_ui.m_lblLimitMessages, true);
+
+ connect(m_ui.m_checkShowPassword, &QCheckBox::toggled, this, &GreaderAccountDetails::displayPassword);
+ connect(m_ui.m_txtPassword->lineEdit(), &BaseLineEdit::textChanged, this, &GreaderAccountDetails::onPasswordChanged);
+ connect(m_ui.m_txtUsername->lineEdit(), &BaseLineEdit::textChanged, this, &GreaderAccountDetails::onUsernameChanged);
+ connect(m_ui.m_txtUrl->lineEdit(), &BaseLineEdit::textChanged, this, &GreaderAccountDetails::onUrlChanged);
+
+ setTabOrder(m_ui.m_cmbService, m_ui.m_txtUrl->lineEdit());
+ setTabOrder(m_ui.m_txtUrl->lineEdit(), 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_checkShowPassword);
+ setTabOrder(m_ui.m_checkShowPassword, m_ui.m_btnTestSetup);
+
+ onPasswordChanged();
+ onUsernameChanged();
+ onUrlChanged();
+ displayPassword(false);
+}
+
+GreaderServiceRoot::Service GreaderAccountDetails::service() const {
+ return m_ui.m_cmbService->currentData().value();
+}
+
+void GreaderAccountDetails::setService(GreaderServiceRoot::Service service) {
+ m_ui.m_cmbService->setCurrentIndex(m_ui.m_cmbService->findData(QVariant::fromValue(service)));
+}
+
+void GreaderAccountDetails::displayPassword(bool display) {
+ m_ui.m_txtPassword->lineEdit()->setEchoMode(display ? QLineEdit::Normal : QLineEdit::Password);
+}
+
+void GreaderAccountDetails::performTest(const QNetworkProxy& custom_proxy) {
+ GreaderNetwork 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());
+
+ NetworkResult result = factory.status(custom_proxy);
+
+ if (result.first != QNetworkReply::NetworkError::NoError) {
+ m_ui.m_lblTestResult->setStatus(WidgetWithStatus::StatusType::Error,
+ tr("Network error: '%1'.").arg(NetworkFactory::networkErrorText(result.first)),
+ tr("Network error, have you entered correct Nextcloud endpoint and password?"));
+ }
+ else {
+ m_ui.m_lblTestResult->setStatus(WidgetWithStatus::StatusType::Error,
+ tr("Unspecified error, did you enter correct URL?"),
+ tr("Unspecified error, did you enter correct URL?"));
+ }
+}
+
+void GreaderAccountDetails::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 GreaderAccountDetails::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 GreaderAccountDetails::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/greader/gui/greaderaccountdetails.h b/src/librssguard/services/greader/gui/greaderaccountdetails.h
new file mode 100755
index 000000000..bc2450769
--- /dev/null
+++ b/src/librssguard/services/greader/gui/greaderaccountdetails.h
@@ -0,0 +1,36 @@
+// For license of this file, see /LICENSE.md.
+
+#ifndef GREADERACCOUNTDETAILS_H
+#define GREADERACCOUNTDETAILS_H
+
+#include
+
+#include "ui_greaderaccountdetails.h"
+
+#include "services/greader/greaderserviceroot.h"
+
+#include
+
+class GreaderAccountDetails : public QWidget {
+ Q_OBJECT
+
+ friend class FormEditGreaderAccount;
+
+ public:
+ explicit GreaderAccountDetails(QWidget* parent = nullptr);
+
+ GreaderServiceRoot::Service service() const;
+ void setService(GreaderServiceRoot::Service service);
+
+ private slots:
+ void displayPassword(bool display);
+ void performTest(const QNetworkProxy& custom_proxy);
+ void onUsernameChanged();
+ void onPasswordChanged();
+ void onUrlChanged();
+
+ private:
+ Ui::GreaderAccountDetails m_ui;
+};
+
+#endif // GREADERACCOUNTDETAILS_H
diff --git a/src/librssguard/services/greader/gui/greaderaccountdetails.ui b/src/librssguard/services/greader/gui/greaderaccountdetails.ui
new file mode 100755
index 000000000..f575497fd
--- /dev/null
+++ b/src/librssguard/services/greader/gui/greaderaccountdetails.ui
@@ -0,0 +1,188 @@
+
+
+ GreaderAccountDetails
+
+
+
+ 0
+ 0
+ 430
+ 390
+
+
+
+ -
+
+
+ Service
+
+
+
+ -
+
+
+ -
+
+
+ URL
+
+
+ m_txtUrl
+
+
+
+ -
+
+
+ -
+
+
-
+
+
+ Only download newest X messages per feed
+
+
+ m_spinLimitMessages
+
+
+
+ -
+
+
+ = unlimited
+
+
+ -1
+
+
+ 1000
+
+
+ -1
+
+
+
+
+
+ -
+
+
+
+
+
+ true
+
+
+
+ -
+
+
+ Some feeds require authentication, including GMail feeds. BASIC, NTLM-2 and DIGEST-MD5 authentication schemes are supported.
+
+
+ Authentication
+
+
+ false
+
+
+ false
+
+
+
-
+
+
+ Username
+
+
+ m_txtUsername
+
+
+
+ -
+
+
+ Password
+
+
+ m_txtPassword
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ Show password
+
+
+
+
+
+
+ -
+
+
-
+
+
+ &Test setup
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ Qt::RightToLeft
+
+
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 409
+ 60
+
+
+
+
+
+
+
+
+ LineEditWithStatus
+ QWidget
+
+ 1
+
+
+ LabelWithStatus
+ QWidget
+
+ 1
+
+
+
+ m_spinLimitMessages
+ m_checkShowPassword
+ m_btnTestSetup
+
+
+
+