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 +
lineeditwithstatus.h
+ 1 +
+ + LabelWithStatus + QWidget +
labelwithstatus.h
+ 1 +
+
+ + m_spinLimitMessages + m_checkShowPassword + m_btnTestSetup + + + +