From 6b1a93b66cf3413deeb8f9a0af838f3d6085257d Mon Sep 17 00:00:00 2001 From: Martin Rotter Date: Wed, 10 Nov 2021 13:00:41 +0100 Subject: [PATCH] bare skeleton for reddit plugin --- resources/graphics/misc/reddit.png | Bin 0 -> 10003 bytes resources/rssguard.qrc | 1 + src/librssguard/definitions/definitions.h | 2 + src/librssguard/librssguard.pro | 12 ++ src/librssguard/miscellaneous/feedreader.cpp | 6 + src/librssguard/services/reddit/definitions.h | 21 ++ .../reddit/gui/formeditredditaccount.cpp | 68 ++++++ .../reddit/gui/formeditredditaccount.h | 29 +++ .../reddit/gui/redditaccountdetails.cpp | 124 +++++++++++ .../reddit/gui/redditaccountdetails.h | 44 ++++ .../reddit/gui/redditaccountdetails.ui | 202 ++++++++++++++++++ .../services/reddit/redditentrypoint.cpp | 45 ++++ .../services/reddit/redditentrypoint.h | 19 ++ .../services/reddit/redditnetworkfactory.cpp | 155 ++++++++++++++ .../services/reddit/redditnetworkfactory.h | 59 +++++ .../services/reddit/redditserviceroot.cpp | 127 +++++++++++ .../services/reddit/redditserviceroot.h | 53 +++++ 17 files changed, 967 insertions(+) create mode 100755 resources/graphics/misc/reddit.png create mode 100755 src/librssguard/services/reddit/definitions.h create mode 100755 src/librssguard/services/reddit/gui/formeditredditaccount.cpp create mode 100755 src/librssguard/services/reddit/gui/formeditredditaccount.h create mode 100755 src/librssguard/services/reddit/gui/redditaccountdetails.cpp create mode 100755 src/librssguard/services/reddit/gui/redditaccountdetails.h create mode 100755 src/librssguard/services/reddit/gui/redditaccountdetails.ui create mode 100755 src/librssguard/services/reddit/redditentrypoint.cpp create mode 100755 src/librssguard/services/reddit/redditentrypoint.h create mode 100755 src/librssguard/services/reddit/redditnetworkfactory.cpp create mode 100755 src/librssguard/services/reddit/redditnetworkfactory.h create mode 100755 src/librssguard/services/reddit/redditserviceroot.cpp create mode 100755 src/librssguard/services/reddit/redditserviceroot.h diff --git a/resources/graphics/misc/reddit.png b/resources/graphics/misc/reddit.png new file mode 100755 index 0000000000000000000000000000000000000000..aa75b0cd2edfd50c218c3ff55641bda3efb932ad GIT binary patch literal 10003 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�xJ0>LZl>J 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 T$uH?0b^|ENs>xJJfkXZegv3k| literal 0 HcmV?d00001 diff --git a/resources/rssguard.qrc b/resources/rssguard.qrc index 29b6e4ff5..183960aa3 100644 --- a/resources/rssguard.qrc +++ b/resources/rssguard.qrc @@ -34,6 +34,7 @@ graphics/misc/image-placeholder.png graphics/misc/inoreader.png graphics/misc/nextcloud.png + graphics/misc/reddit.png graphics/misc/reedah.png graphics/misc/theoldreader.png graphics/misc/tt-rss.png diff --git a/src/librssguard/definitions/definitions.h b/src/librssguard/definitions/definitions.h index b11222b44..c50f2250b 100644 --- a/src/librssguard/definitions/definitions.h +++ b/src/librssguard/definitions/definitions.h @@ -13,6 +13,7 @@ #define SERVICE_CODE_FEEDLY "feedly" #define SERVICE_CODE_INOREADER "inoreader" #define SERVICE_CODE_GMAIL "gmail" +#define SERVICE_CODE_REDDIT "reddit" #define ADBLOCK_SERVER_PORT 48484 #define ADBLOCK_HOWTO "https://github.com/martinrotter/rssguard/blob/master/resources/docs/Documentation.md#adblock" @@ -132,6 +133,7 @@ #define LOGSEC_TTRSS "tt-rss: " #define LOGSEC_GMAIL "gmail: " #define LOGSEC_OAUTH "oauth: " +#define LOGSEC_REDDIT "reddit: " #define MAX_ZOOM_FACTOR 5.0f #define MIN_ZOOM_FACTOR 0.25f diff --git a/src/librssguard/librssguard.pro b/src/librssguard/librssguard.pro index c7be264fb..0048fd149 100644 --- a/src/librssguard/librssguard.pro +++ b/src/librssguard/librssguard.pro @@ -192,6 +192,12 @@ HEADERS += core/feeddownloader.h \ services/owncloud/owncloudnetworkfactory.h \ services/owncloud/owncloudserviceentrypoint.h \ services/owncloud/owncloudserviceroot.h \ + services/reddit/definitions.h \ + services/reddit/gui/formeditredditaccount.h \ + services/reddit/gui/redditaccountdetails.h \ + services/reddit/redditentrypoint.h \ + services/reddit/redditnetworkfactory.h \ + services/reddit/redditserviceroot.h \ services/standard/atomparser.h \ services/standard/definitions.h \ services/standard/feedparser.h \ @@ -368,6 +374,11 @@ SOURCES += core/feeddownloader.cpp \ services/owncloud/owncloudnetworkfactory.cpp \ services/owncloud/owncloudserviceentrypoint.cpp \ services/owncloud/owncloudserviceroot.cpp \ + services/reddit/gui/formeditredditaccount.cpp \ + services/reddit/gui/redditaccountdetails.cpp \ + services/reddit/redditentrypoint.cpp \ + services/reddit/redditnetworkfactory.cpp \ + services/reddit/redditserviceroot.cpp \ services/standard/atomparser.cpp \ services/standard/feedparser.cpp \ services/standard/gui/formeditstandardaccount.cpp \ @@ -432,6 +443,7 @@ FORMS += gui/dialogs/formabout.ui \ services/gmail/gui/gmailaccountdetails.ui \ services/greader/gui/greaderaccountdetails.ui \ services/owncloud/gui/owncloudaccountdetails.ui \ + services/reddit/gui/redditaccountdetails.ui \ services/standard/gui/formstandardimportexport.ui \ services/standard/gui/standardfeeddetails.ui \ services/tt-rss/gui/ttrssaccountdetails.ui \ diff --git a/src/librssguard/miscellaneous/feedreader.cpp b/src/librssguard/miscellaneous/feedreader.cpp index cad1a441a..728374251 100644 --- a/src/librssguard/miscellaneous/feedreader.cpp +++ b/src/librssguard/miscellaneous/feedreader.cpp @@ -18,6 +18,7 @@ #include "services/gmail/gmailentrypoint.h" #include "services/greader/greaderentrypoint.h" #include "services/owncloud/owncloudserviceentrypoint.h" +#include "services/reddit/redditentrypoint.h" #include "services/standard/standardserviceentrypoint.h" #include "services/tt-rss/ttrssserviceentrypoint.h" @@ -60,6 +61,11 @@ QList FeedReader::feedServices() { m_feedServices.append(new GmailEntryPoint()); m_feedServices.append(new GreaderEntryPoint()); m_feedServices.append(new OwnCloudServiceEntryPoint()); + +#if defined(DEBUG) + m_feedServices.append(new RedditEntryPoint()); +#endif + m_feedServices.append(new StandardServiceEntryPoint()); m_feedServices.append(new TtRssServiceEntryPoint()); } diff --git a/src/librssguard/services/reddit/definitions.h b/src/librssguard/services/reddit/definitions.h new file mode 100755 index 000000000..a8cc677b0 --- /dev/null +++ b/src/librssguard/services/reddit/definitions.h @@ -0,0 +1,21 @@ +// For license of this file, see /LICENSE.md. + +#ifndef REDDIT_DEFINITIONS_H +#define REDDIT_DEFINITIONS_H + +#define REDDIT_OAUTH_REDIRECT_URI_PORT 14499 +#define REDDIT_OAUTH_AUTH_URL "https://www.reddit.com/api/v1/authorize" +#define REDDIT_OAUTH_TOKEN_URL "https://www.reddit.com/api/v1/access_token" +#define REDDIT_OAUTH_SCOPE "identity" + +#define REDDIT_REG_API_URL "https://www.reddit.com/prefs/apps" + +#define REDDIT_API_GET_PROFILE "https://oauth.reddit.com/api/v1/me" + +#define REDDIT_DEFAULT_BATCH_SIZE 100 +#define REDDIT_MAX_BATCH_SIZE 999 + +#define REDDIT_CONTENT_TYPE_HTTP "application/http" +#define REDDIT_CONTENT_TYPE_JSON "application/json" + +#endif // REDDIT_DEFINITIONS_H diff --git a/src/librssguard/services/reddit/gui/formeditredditaccount.cpp b/src/librssguard/services/reddit/gui/formeditredditaccount.cpp new file mode 100755 index 000000000..f6930ba41 --- /dev/null +++ b/src/librssguard/services/reddit/gui/formeditredditaccount.cpp @@ -0,0 +1,68 @@ +// For license of this file, see /LICENSE.md. + +#include "services/reddit/gui/formeditredditaccount.h" + +#include "gui/guiutilities.h" +#include "miscellaneous/application.h" +#include "miscellaneous/iconfactory.h" +#include "network-web/oauth2service.h" +#include "network-web/webfactory.h" +#include "services/reddit/definitions.h" +#include "services/reddit/redditserviceroot.h" +#include "services/reddit/gui/redditaccountdetails.h" + +FormEditRedditAccount::FormEditRedditAccount(QWidget* parent) + : FormAccountDetails(qApp->icons()->miscIcon(QSL("reddit")), parent), m_details(new RedditAccountDetails(this)) { + insertCustomTab(m_details, tr("Server setup"), 0); + activateTab(0); + + m_details->m_ui.m_txtUsername->setFocus(); + connect(m_details->m_ui.m_btnTestSetup, &QPushButton::clicked, this, [this]() { + m_details->testSetup(m_proxyDetails->proxy()); + }); +} + +void FormEditRedditAccount::apply() { + FormAccountDetails::apply(); + + bool using_another_acc = + m_details->m_ui.m_txtUsername->lineEdit()->text() !=account()->network()->username(); + + // Make sure that the data copied from GUI are used for brand new login. + account()->network()->oauth()->logout(false); + account()->network()->oauth()->setClientId(m_details->m_ui.m_txtAppId->lineEdit()->text()); + account()->network()->oauth()->setClientSecret(m_details->m_ui.m_txtAppKey->lineEdit()->text()); + account()->network()->oauth()->setRedirectUrl(m_details->m_ui.m_txtRedirectUrl->lineEdit()->text(), + true); + + account()->network()->setUsername(m_details->m_ui.m_txtUsername->lineEdit()->text()); + account()->network()->setBatchSize(m_details->m_ui.m_spinLimitMessages->value()); + account()->network()->setDownloadOnlyUnreadMessages(m_details->m_ui.m_cbDownloadOnlyUnreadMessages->isChecked()); + + account()->saveAccountDataToDatabase(); + accept(); + + if (!m_creatingNew) { + if (using_another_acc) { + account()->completelyRemoveAllData(); + } + + account()->start(true); + } +} + +void FormEditRedditAccount::loadAccountData() { + FormAccountDetails::loadAccountData(); + + m_details->m_oauth = account()->network()->oauth(); + m_details->hookNetwork(); + + // Setup the GUI. + m_details->m_ui.m_txtAppId->lineEdit()->setText(m_details->m_oauth->clientId()); + m_details->m_ui.m_txtAppKey->lineEdit()->setText(m_details->m_oauth->clientSecret()); + m_details->m_ui.m_txtRedirectUrl->lineEdit()->setText(m_details->m_oauth->redirectUrl()); + + m_details->m_ui.m_txtUsername->lineEdit()->setText(account()->network()->username()); + m_details->m_ui.m_spinLimitMessages->setValue(account()->network()->batchSize()); + m_details->m_ui.m_cbDownloadOnlyUnreadMessages->setChecked(account()->network()->downloadOnlyUnreadMessages()); +} diff --git a/src/librssguard/services/reddit/gui/formeditredditaccount.h b/src/librssguard/services/reddit/gui/formeditredditaccount.h new file mode 100755 index 000000000..51618a790 --- /dev/null +++ b/src/librssguard/services/reddit/gui/formeditredditaccount.h @@ -0,0 +1,29 @@ +// For license of this file, see /LICENSE.md. + +#ifndef FORMEDITINOREADERACCOUNT_H +#define FORMEDITINOREADERACCOUNT_H + +#include "services/abstract/gui/formaccountdetails.h" + +#include "services/reddit/redditnetworkfactory.h" + +class RedditServiceRoot; +class RedditAccountDetails; + +class FormEditRedditAccount : public FormAccountDetails { + Q_OBJECT + + public: + explicit FormEditRedditAccount(QWidget* parent = nullptr); + + protected slots: + virtual void apply(); + + protected: + virtual void loadAccountData(); + + private: + RedditAccountDetails* m_details; +}; + +#endif // FORMEDITINOREADERACCOUNT_H diff --git a/src/librssguard/services/reddit/gui/redditaccountdetails.cpp b/src/librssguard/services/reddit/gui/redditaccountdetails.cpp new file mode 100755 index 000000000..0458d3fea --- /dev/null +++ b/src/librssguard/services/reddit/gui/redditaccountdetails.cpp @@ -0,0 +1,124 @@ +// For license of this file, see /LICENSE.md. + +#include "services/reddit/gui/redditaccountdetails.h" + +#include "exceptions/applicationexception.h" +#include "gui/guiutilities.h" +#include "miscellaneous/application.h" +#include "network-web/oauth2service.h" +#include "network-web/webfactory.h" +#include "services/reddit/definitions.h" +#include "services/reddit/redditnetworkfactory.h" + +RedditAccountDetails::RedditAccountDetails(QWidget* parent) + : QWidget(parent), m_oauth(nullptr), m_lastProxy({}) { + m_ui.setupUi(this); + + m_ui.m_lblInfo->setHelpText(tr("You have to fill in your client ID/secret and also fill in correct redirect URL."), + true); + m_ui.m_lblTestResult->setStatus(WidgetWithStatus::StatusType::Information, + tr("Not tested yet."), + tr("Not tested yet.")); + m_ui.m_lblTestResult->label()->setWordWrap(true); + m_ui.m_txtUsername->lineEdit()->setPlaceholderText(tr("User-visible username")); + + setTabOrder(m_ui.m_txtUsername->lineEdit(), m_ui.m_txtAppId); + setTabOrder(m_ui.m_txtAppId, m_ui.m_txtAppKey); + setTabOrder(m_ui.m_txtAppKey, m_ui.m_txtRedirectUrl); + setTabOrder(m_ui.m_txtRedirectUrl, m_ui.m_spinLimitMessages); + setTabOrder(m_ui.m_spinLimitMessages, m_ui.m_btnTestSetup); + + connect(m_ui.m_txtAppId->lineEdit(), &BaseLineEdit::textChanged, this, &RedditAccountDetails::checkOAuthValue); + connect(m_ui.m_txtAppKey->lineEdit(), &BaseLineEdit::textChanged, this, &RedditAccountDetails::checkOAuthValue); + connect(m_ui.m_txtRedirectUrl->lineEdit(), &BaseLineEdit::textChanged, this, &RedditAccountDetails::checkOAuthValue); + connect(m_ui.m_txtUsername->lineEdit(), &BaseLineEdit::textChanged, this, &RedditAccountDetails::checkUsername); + connect(m_ui.m_btnRegisterApi, &QPushButton::clicked, this, &RedditAccountDetails::registerApi); + + emit m_ui.m_txtUsername->lineEdit()->textChanged(m_ui.m_txtUsername->lineEdit()->text()); + emit m_ui.m_txtAppId->lineEdit()->textChanged(m_ui.m_txtAppId->lineEdit()->text()); + emit m_ui.m_txtAppKey->lineEdit()->textChanged(m_ui.m_txtAppKey->lineEdit()->text()); + emit m_ui.m_txtRedirectUrl->lineEdit()->textChanged(m_ui.m_txtAppKey->lineEdit()->text()); + + hookNetwork(); +} + +void RedditAccountDetails::testSetup(const QNetworkProxy& custom_proxy) { + m_oauth->logout(true); + m_oauth->setClientId(m_ui.m_txtAppId->lineEdit()->text()); + m_oauth->setClientSecret(m_ui.m_txtAppKey->lineEdit()->text()); + m_oauth->setRedirectUrl(m_ui.m_txtRedirectUrl->lineEdit()->text(), true); + + m_lastProxy = custom_proxy; + m_oauth->login(); +} + +void RedditAccountDetails::checkUsername(const QString& username) { + if (username.isEmpty()) { + m_ui.m_txtUsername->setStatus(WidgetWithStatus::StatusType::Error, tr("No username entered.")); + } + else { + m_ui.m_txtUsername->setStatus(WidgetWithStatus::StatusType::Ok, tr("Some username entered.")); + } +} + +void RedditAccountDetails::onAuthFailed() { + m_ui.m_lblTestResult->setStatus(WidgetWithStatus::StatusType::Error, + tr("You did not grant access."), + tr("There was error during testing.")); +} + +void RedditAccountDetails::onAuthError(const QString& error, const QString& detailed_description) { + Q_UNUSED(error) + + m_ui.m_lblTestResult->setStatus(WidgetWithStatus::StatusType::Error, + tr("There is error: %1").arg(detailed_description), + tr("There was error during testing.")); +} + +void RedditAccountDetails::onAuthGranted() { + m_ui.m_lblTestResult->setStatus(WidgetWithStatus::StatusType::Ok, + tr("Tested successfully. You may be prompted to login once more."), + tr("Your access was approved.")); + + try { + RedditNetworkFactory fac; + + fac.setOauth(m_oauth); + + auto resp = fac.me(m_lastProxy); + + m_ui.m_txtUsername->lineEdit()->setText(resp[QSL("name")].toString()); + } + catch (const ApplicationException& ex) { + qCriticalNN << LOGSEC_REDDIT + << "Failed to obtain profile with error:" + << QUOTE_W_SPACE_DOT(ex.message()); + } +} + +void RedditAccountDetails::hookNetwork() { + connect(m_oauth, &OAuth2Service::tokensRetrieved, this, &RedditAccountDetails::onAuthGranted); + connect(m_oauth, &OAuth2Service::tokensRetrieveError, this, &RedditAccountDetails::onAuthError); + connect(m_oauth, &OAuth2Service::authFailed, this, &RedditAccountDetails::onAuthFailed); +} + +void RedditAccountDetails::registerApi() { + qApp->web()->openUrlInExternalBrowser(QSL(REDDIT_REG_API_URL)); +} + +void RedditAccountDetails::checkOAuthValue(const QString& value) { + auto* line_edit = qobject_cast(sender()->parent()); + + if (line_edit != nullptr) { + if (value.isEmpty()) { +#if defined(REDDIT_OFFICIAL_SUPPORT) + line_edit->setStatus(WidgetWithStatus::StatusType::Ok, tr("Preconfigured client ID/secret will be used.")); +#else + line_edit->setStatus(WidgetWithStatus::StatusType::Error, tr("Empty value is entered.")); +#endif + } + else { + line_edit->setStatus(WidgetWithStatus::StatusType::Ok, tr("Some value is entered.")); + } + } +} diff --git a/src/librssguard/services/reddit/gui/redditaccountdetails.h b/src/librssguard/services/reddit/gui/redditaccountdetails.h new file mode 100755 index 000000000..b383a7eb7 --- /dev/null +++ b/src/librssguard/services/reddit/gui/redditaccountdetails.h @@ -0,0 +1,44 @@ +// For license of this file, see /LICENSE.md. + +#ifndef REDDITACCOUNTDETAILS_H +#define REDDITACCOUNTDETAILS_H + +#include + +#include "ui_redditaccountdetails.h" + +#include + +class OAuth2Service; + +class RedditAccountDetails : public QWidget { + Q_OBJECT + + friend class FormEditRedditAccount; + + public: + explicit RedditAccountDetails(QWidget* parent = nullptr); + + public slots: + void testSetup(const QNetworkProxy& custom_proxy); + + private slots: + void registerApi(); + void checkOAuthValue(const QString& value); + void checkUsername(const QString& username); + void onAuthFailed(); + void onAuthError(const QString& error, const QString& detailed_description); + void onAuthGranted(); + + private: + void hookNetwork(); + + private: + Ui::RedditAccountDetails m_ui; + + // Pointer to live OAuth. + OAuth2Service* m_oauth; + QNetworkProxy m_lastProxy; +}; + +#endif // REDDITACCOUNTDETAILS_H diff --git a/src/librssguard/services/reddit/gui/redditaccountdetails.ui b/src/librssguard/services/reddit/gui/redditaccountdetails.ui new file mode 100755 index 000000000..da0a54a31 --- /dev/null +++ b/src/librssguard/services/reddit/gui/redditaccountdetails.ui @@ -0,0 +1,202 @@ + + + RedditAccountDetails + + + + 0 + 0 + 431 + 259 + + + + + + + Username + + + + + + + + + + + 0 + 1 + + + + OAuth 2.0 settings + + + + + + Client ID + + + m_txtAppId + + + + + + + + + + Client secret + + + m_txtAppKey + + + + + + + + + + Redirect URL + + + m_txtRedirectUrl + + + + + + + + + + + + Get my credentials + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + + + + + + Only download newest X articles per feed + + + m_spinLimitMessages + + + + + + + + 140 + 16777215 + + + + + + + + + + + + &Login + + + + + + + Qt::RightToLeft + + + + + + + + + Qt::Vertical + + + + 410 + 0 + + + + + + + + Download unread articles only + + + + + + + + LineEditWithStatus + QWidget +
lineeditwithstatus.h
+ 1 +
+ + LabelWithStatus + QWidget +
labelwithstatus.h
+ 1 +
+ + MessageCountSpinBox + QSpinBox +
messagecountspinbox.h
+
+ + HelpSpoiler + QWidget +
helpspoiler.h
+ 1 +
+
+ + m_btnRegisterApi + m_cbDownloadOnlyUnreadMessages + m_spinLimitMessages + m_btnTestSetup + + + +
diff --git a/src/librssguard/services/reddit/redditentrypoint.cpp b/src/librssguard/services/reddit/redditentrypoint.cpp new file mode 100755 index 000000000..abff8f4e7 --- /dev/null +++ b/src/librssguard/services/reddit/redditentrypoint.cpp @@ -0,0 +1,45 @@ +// For license of this file, see /LICENSE.md. + +#include "services/reddit/redditentrypoint.h" + +#include "database/databasequeries.h" +#include "definitions/definitions.h" +#include "miscellaneous/application.h" +#include "miscellaneous/iconfactory.h" +#include "services/reddit/definitions.h" +#include "services/reddit/gui/formeditredditaccount.h" +#include "services/reddit/redditserviceroot.h" + +#include + +ServiceRoot* RedditEntryPoint::createNewRoot() const { + FormEditRedditAccount form_acc(qApp->mainFormWidget()); + + return form_acc.addEditAccount(); +} + +QList RedditEntryPoint::initializeSubtree() const { + QSqlDatabase database = qApp->database()->driver()->connection(QSL("RedditEntryPoint")); + + return DatabaseQueries::getAccounts(database, code()); +} + +QString RedditEntryPoint::name() const { + return QSL("Reddit"); +} + +QString RedditEntryPoint::code() const { + return QSL(SERVICE_CODE_REDDIT); +} + +QString RedditEntryPoint::description() const { + return QObject::tr("Simplistic Reddit client."); +} + +QString RedditEntryPoint::author() const { + return QSL(APP_AUTHOR); +} + +QIcon RedditEntryPoint::icon() const { + return qApp->icons()->miscIcon(QSL("reddit")); +} diff --git a/src/librssguard/services/reddit/redditentrypoint.h b/src/librssguard/services/reddit/redditentrypoint.h new file mode 100755 index 000000000..74a47caf1 --- /dev/null +++ b/src/librssguard/services/reddit/redditentrypoint.h @@ -0,0 +1,19 @@ +// For license of this file, see /LICENSE.md. + +#ifndef REDDITENTRYPOINT_H +#define REDDITENTRYPOINT_H + +#include "services/abstract/serviceentrypoint.h" + +class RedditEntryPoint : 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 // REDDITENTRYPOINT_H diff --git a/src/librssguard/services/reddit/redditnetworkfactory.cpp b/src/librssguard/services/reddit/redditnetworkfactory.cpp new file mode 100755 index 000000000..7dfffc256 --- /dev/null +++ b/src/librssguard/services/reddit/redditnetworkfactory.cpp @@ -0,0 +1,155 @@ +// For license of this file, see /LICENSE.md. + +#include "services/reddit/redditnetworkfactory.h" + +#include "database/databasequeries.h" +#include "definitions/definitions.h" +#include "exceptions/applicationexception.h" +#include "exceptions/networkexception.h" +#include "gui/dialogs/formmain.h" +#include "gui/tabwidget.h" +#include "miscellaneous/application.h" +#include "miscellaneous/textfactory.h" +#include "network-web/networkfactory.h" +#include "network-web/oauth2service.h" +#include "network-web/silentnetworkaccessmanager.h" +#include "network-web/webfactory.h" +#include "services/abstract/category.h" +#include "services/reddit/definitions.h" +#include "services/reddit/redditserviceroot.h" + +#include +#include +#include +#include +#include +#include +#include + +RedditNetworkFactory::RedditNetworkFactory(QObject* parent) : QObject(parent), + m_service(nullptr), m_username(QString()), m_batchSize(REDDIT_DEFAULT_BATCH_SIZE), + m_downloadOnlyUnreadMessages(false), + m_oauth2(new OAuth2Service(QSL(REDDIT_OAUTH_AUTH_URL), QSL(REDDIT_OAUTH_TOKEN_URL), + {}, {}, QSL(REDDIT_OAUTH_SCOPE), this)) { + initializeOauth(); +} + +void RedditNetworkFactory::setService(RedditServiceRoot* service) { + m_service = service; +} + +OAuth2Service* RedditNetworkFactory::oauth() const { + return m_oauth2; +} + +QString RedditNetworkFactory::username() const { + return m_username; +} + +int RedditNetworkFactory::batchSize() const { + return m_batchSize; +} + +void RedditNetworkFactory::setBatchSize(int batch_size) { + m_batchSize = batch_size; +} + +void RedditNetworkFactory::initializeOauth() { + m_oauth2->setUseHttpBasicAuthWithClientData(true); + m_oauth2->setRedirectUrl(QSL(OAUTH_REDIRECT_URI) + + QL1C(':') + + QString::number(REDDIT_OAUTH_REDIRECT_URI_PORT), + true); + + connect(m_oauth2, &OAuth2Service::tokensRetrieveError, this, &RedditNetworkFactory::onTokensError); + connect(m_oauth2, &OAuth2Service::authFailed, this, &RedditNetworkFactory::onAuthFailed); + connect(m_oauth2, &OAuth2Service::tokensRetrieved, this, [this](QString access_token, QString refresh_token, int expires_in) { + Q_UNUSED(expires_in) + Q_UNUSED(access_token) + + if (m_service != nullptr && !refresh_token.isEmpty()) { + QSqlDatabase database = qApp->database()->driver()->connection(metaObject()->className()); + + DatabaseQueries::storeNewOauthTokens(database, refresh_token, m_service->accountId()); + } + }); +} + +bool RedditNetworkFactory::downloadOnlyUnreadMessages() const { + return m_downloadOnlyUnreadMessages; +} + +void RedditNetworkFactory::setDownloadOnlyUnreadMessages(bool download_only_unread_messages) { + m_downloadOnlyUnreadMessages = download_only_unread_messages; +} + +void RedditNetworkFactory::setOauth(OAuth2Service* oauth) { + m_oauth2 = oauth; +} + +void RedditNetworkFactory::setUsername(const QString& username) { + m_username = username; +} + +QVariantHash RedditNetworkFactory::me(const QNetworkProxy& custom_proxy) { + QString bearer = m_oauth2->bearer().toLocal8Bit(); + + if (bearer.isEmpty()) { + throw ApplicationException(tr("you are not logged in")); + } + + QList> headers; + + headers.append(QPair(QSL(HTTP_HEADERS_AUTHORIZATION).toLocal8Bit(), + m_oauth2->bearer().toLocal8Bit())); + + int timeout = qApp->settings()->value(GROUP(Feeds), SETTING(Feeds::UpdateTimeout)).toInt(); + QByteArray output; + auto result = NetworkFactory::performNetworkOperation(QSL(REDDIT_API_GET_PROFILE), + timeout, + {}, + output, + QNetworkAccessManager::Operation::GetOperation, + headers, + false, + {}, + {}, + custom_proxy).first; + + if (result != QNetworkReply::NetworkError::NoError) { + throw NetworkException(result, output); + } + else { + QJsonDocument doc = QJsonDocument::fromJson(output); + + return doc.object().toVariantHash(); + } +} + +void RedditNetworkFactory::onTokensError(const QString& error, const QString& error_description) { + Q_UNUSED(error) + + qApp->showGuiMessage(Notification::Event::LoginFailure, { + tr("Reddit: authentication error"), + tr("Click this to login again. Error is: '%1'").arg(error_description), + QSystemTrayIcon::MessageIcon::Critical }, + {}, { + tr("Login"), + [this]() { + m_oauth2->setAccessToken(QString()); + m_oauth2->setRefreshToken(QString()); + m_oauth2->login(); + } }); +} + +void RedditNetworkFactory::onAuthFailed() { + qApp->showGuiMessage(Notification::Event::LoginFailure, { + tr("Reddit: authorization denied"), + tr("Click this to login again."), + QSystemTrayIcon::MessageIcon::Critical }, + {}, { + tr("Login"), + [this]() { + m_oauth2->login(); + } }); +} diff --git a/src/librssguard/services/reddit/redditnetworkfactory.h b/src/librssguard/services/reddit/redditnetworkfactory.h new file mode 100755 index 000000000..befef2cd6 --- /dev/null +++ b/src/librssguard/services/reddit/redditnetworkfactory.h @@ -0,0 +1,59 @@ +// For license of this file, see /LICENSE.md. + +#ifndef REDDITNETWORKFACTORY_H +#define REDDITNETWORKFACTORY_H + +#include + +#include "core/message.h" + +#include "3rd-party/mimesis/mimesis.hpp" +#include "services/abstract/feed.h" +#include "services/abstract/rootitem.h" + +#include + +class RootItem; +class RedditServiceRoot; +class OAuth2Service; +class Downloader; + +class RedditNetworkFactory : public QObject { + Q_OBJECT + + public: + explicit RedditNetworkFactory(QObject* parent = nullptr); + + void setService(RedditServiceRoot* service); + + OAuth2Service* oauth() const; + void setOauth(OAuth2Service* oauth); + + QString username() const; + void setUsername(const QString& username); + + int batchSize() const; + void setBatchSize(int batch_size); + + bool downloadOnlyUnreadMessages() const; + void setDownloadOnlyUnreadMessages(bool download_only_unread_messages); + + // API methods. + QVariantHash me(const QNetworkProxy& custom_proxy); + + private slots: + void onTokensError(const QString& error, const QString& error_description); + void onAuthFailed(); + + private: + void initializeOauth(); + + private: + RedditServiceRoot* m_service; + QString m_username; + int m_batchSize; + bool m_downloadOnlyUnreadMessages; + OAuth2Service* m_oauth2; +}; + +#endif // REDDITNETWORKFACTORY_H diff --git a/src/librssguard/services/reddit/redditserviceroot.cpp b/src/librssguard/services/reddit/redditserviceroot.cpp new file mode 100755 index 000000000..1953b8a63 --- /dev/null +++ b/src/librssguard/services/reddit/redditserviceroot.cpp @@ -0,0 +1,127 @@ +// For license of this file, see /LICENSE.md. + +#include "services/reddit/redditserviceroot.h" + +#include "database/databasequeries.h" +#include "exceptions/feedfetchexception.h" +#include "miscellaneous/application.h" +#include "miscellaneous/iconfactory.h" +#include "network-web/oauth2service.h" +#include "services/abstract/importantnode.h" +#include "services/abstract/recyclebin.h" +#include "services/reddit/definitions.h" +#include "services/reddit/gui/formeditredditaccount.h" +#include "services/reddit/redditentrypoint.h" +#include "services/reddit/redditnetworkfactory.h" + +#include + +RedditServiceRoot::RedditServiceRoot(RootItem* parent) + : ServiceRoot(parent), m_network(new RedditNetworkFactory(this)) { + m_network->setService(this); + setIcon(RedditEntryPoint().icon()); +} + +void RedditServiceRoot::updateTitle() { + setTitle(TextFactory::extractUsernameFromEmail(m_network->username()) + QSL(" (Reddit)")); +} + +RootItem* RedditServiceRoot::obtainNewTreeForSyncIn() const { + auto* root = new RootItem(); + + return root; +} + +QVariantHash RedditServiceRoot::customDatabaseData() const { + QVariantHash data; + + data[QSL("username")] = m_network->username(); + data[QSL("batch_size")] = m_network->batchSize(); + data[QSL("download_only_unread")] = m_network->downloadOnlyUnreadMessages(); + data[QSL("client_id")] = m_network->oauth()->clientId(); + data[QSL("client_secret")] = m_network->oauth()->clientSecret(); + data[QSL("refresh_token")] = m_network->oauth()->refreshToken(); + data[QSL("redirect_uri")] = m_network->oauth()->redirectUrl(); + + return data; +} + +void RedditServiceRoot::setCustomDatabaseData(const QVariantHash& data) { + m_network->setUsername(data[QSL("username")].toString()); + m_network->setBatchSize(data[QSL("batch_size")].toInt()); + m_network->setDownloadOnlyUnreadMessages(data[QSL("download_only_unread")].toBool()); + m_network->oauth()->setClientId(data[QSL("client_id")].toString()); + m_network->oauth()->setClientSecret(data[QSL("client_secret")].toString()); + m_network->oauth()->setRefreshToken(data[QSL("refresh_token")].toString()); + m_network->oauth()->setRedirectUrl(data[QSL("redirect_uri")].toString(), true); +} + +QList RedditServiceRoot::obtainNewMessages(Feed* feed, + const QHash& stated_messages, + const QHash& tagged_messages) { + Q_UNUSED(stated_messages) + Q_UNUSED(tagged_messages) + Q_UNUSED(feed) + + QList messages; + + return messages; +} + +bool RedditServiceRoot::isSyncable() const { + return true; +} + +bool RedditServiceRoot::canBeEdited() const { + return true; +} + +bool RedditServiceRoot::editViaGui() { + FormEditRedditAccount form_pointer(qApp->mainFormWidget()); + + form_pointer.addEditAccount(this); + return true; +} + +bool RedditServiceRoot::supportsFeedAdding() const { + return false; +} + +bool RedditServiceRoot::supportsCategoryAdding() const { + return false; +} + +void RedditServiceRoot::start(bool freshly_activated) { + if (!freshly_activated) { + DatabaseQueries::loadFromDatabase(this); + loadCacheFromFile(); + } + + updateTitle(); + + /* + if (getSubTreeFeeds().isEmpty()) { + syncIn(); + } + */ + + m_network->oauth()->login(); +} + +QString RedditServiceRoot::code() const { + return RedditEntryPoint().code(); +} + +QString RedditServiceRoot::additionalTooltip() const { + return tr("Authentication status: %1\n" + "Login tokens expiration: %2").arg(network()->oauth()->isFullyLoggedIn() + ? tr("logged-in") + : tr("NOT logged-in"), + network()->oauth()->tokensExpireIn().isValid() ? + network()->oauth()->tokensExpireIn().toString() : QSL("-")); +} + +void RedditServiceRoot::saveAllCachedData(bool ignore_errors) { + Q_UNUSED(ignore_errors) + auto msg_cache = takeMessageCache(); +} diff --git a/src/librssguard/services/reddit/redditserviceroot.h b/src/librssguard/services/reddit/redditserviceroot.h new file mode 100755 index 000000000..5600e0a95 --- /dev/null +++ b/src/librssguard/services/reddit/redditserviceroot.h @@ -0,0 +1,53 @@ +// For license of this file, see /LICENSE.md. + +#ifndef REDDITSERVICEROOT_H +#define REDDITSERVICEROOT_H + +#include "services/abstract/cacheforserviceroot.h" +#include "services/abstract/serviceroot.h" + +class RedditNetworkFactory; + +class RedditServiceRoot : public ServiceRoot, public CacheForServiceRoot { + Q_OBJECT + + public: + explicit RedditServiceRoot(RootItem* parent = nullptr); + + void setNetwork(RedditNetworkFactory* network); + RedditNetworkFactory* network() const; + + virtual bool isSyncable() const; + virtual bool canBeEdited() const; + virtual bool editViaGui(); + virtual bool supportsFeedAdding() const; + virtual bool supportsCategoryAdding() const; + virtual void start(bool freshly_activated); + virtual QString code() const; + virtual QString additionalTooltip() const; + virtual void saveAllCachedData(bool ignore_errors); + virtual QVariantHash customDatabaseData() const; + virtual void setCustomDatabaseData(const QVariantHash& data); + virtual QList obtainNewMessages(Feed* feed, + const QHash& stated_messages, + const QHash& tagged_messages); + + protected: + virtual RootItem* obtainNewTreeForSyncIn() const; + + private: + void updateTitle(); + + private: + RedditNetworkFactory* m_network; +}; + +inline void RedditServiceRoot::setNetwork(RedditNetworkFactory* network) { + m_network = network; +} + +inline RedditNetworkFactory* RedditServiceRoot::network() const { + return m_network; +} + +#endif // REDDITSERVICEROOT_H