starting to merge inoreader into greader

This commit is contained in:
Martin Rotter 2021-08-05 14:46:39 +02:00
parent 5971287174
commit 8118c3155b
25 changed files with 637 additions and 149 deletions

View file

@ -0,0 +1,13 @@
// For license of this file, see <project-root-folder>/LICENSE.md.
#include "gui/reusable/resizablestackedwidget.h"
ResizableStackedWidget::ResizableStackedWidget(QWidget* parent) : QStackedWidget(parent) {}
QSize ResizableStackedWidget::sizeHint() const {
return currentWidget()->sizeHint();
}
QSize ResizableStackedWidget::minimumSizeHint() const {
return currentWidget()->minimumSizeHint();
}

View file

@ -0,0 +1,16 @@
// For license of this file, see <project-root-folder>/LICENSE.md.
#ifndef RESIZABLESTACKEDWIDGET_H
#define RESIZABLESTACKEDWIDGET_H
#include <QStackedWidget>
class ResizableStackedWidget : public QStackedWidget {
public:
explicit ResizableStackedWidget(QWidget* parent = nullptr);
virtual QSize sizeHint() const;
virtual QSize minimumSizeHint() const;
};
#endif // RESIZABLESTACKEDWIDGET_H

View file

@ -65,6 +65,7 @@ HEADERS += core/feeddownloader.h \
gui/notifications/singlenotificationeditor.h \ gui/notifications/singlenotificationeditor.h \
gui/reusable/baselineedit.h \ gui/reusable/baselineedit.h \
gui/reusable/progressbarwithtext.h \ gui/reusable/progressbarwithtext.h \
gui/reusable/resizablestackedwidget.h \
gui/settings/settingsnotifications.h \ gui/settings/settingsnotifications.h \
gui/toolbars/basetoolbar.h \ gui/toolbars/basetoolbar.h \
gui/reusable/comboboxwithstatus.h \ gui/reusable/comboboxwithstatus.h \
@ -251,6 +252,7 @@ SOURCES += core/feeddownloader.cpp \
gui/notifications/singlenotificationeditor.cpp \ gui/notifications/singlenotificationeditor.cpp \
gui/reusable/baselineedit.cpp \ gui/reusable/baselineedit.cpp \
gui/reusable/progressbarwithtext.cpp \ gui/reusable/progressbarwithtext.cpp \
gui/reusable/resizablestackedwidget.cpp \
gui/settings/settingsnotifications.cpp \ gui/settings/settingsnotifications.cpp \
gui/toolbars/basetoolbar.cpp \ gui/toolbars/basetoolbar.cpp \
gui/reusable/comboboxwithstatus.cpp \ gui/reusable/comboboxwithstatus.cpp \

View file

@ -299,8 +299,8 @@ QString OAuth2Service::redirectUrl() const {
return m_redirectionHandler->listenAddressPort(); return m_redirectionHandler->listenAddressPort();
} }
void OAuth2Service::setRedirectUrl(const QString& redirect_url) { void OAuth2Service::setRedirectUrl(const QString& redirect_url, bool start_handler) {
m_redirectionHandler->setListenAddressPort(redirect_url); m_redirectionHandler->setListenAddressPort(redirect_url, start_handler);
} }
QString OAuth2Service::refreshToken() const { QString OAuth2Service::refreshToken() const {

View file

@ -57,7 +57,7 @@ class OAuth2Service : public QObject {
void setRefreshToken(const QString& refresh_token); void setRefreshToken(const QString& refresh_token);
QString redirectUrl() const; QString redirectUrl() const;
void setRedirectUrl(const QString& redirect_url); void setRedirectUrl(const QString& redirect_url, bool start_handler);
QString clientId() const; QString clientId() const;
void setClientId(const QString& client_id); void setClientId(const QString& client_id);

View file

@ -29,7 +29,7 @@ bool OAuthHttpHandler::isListening() const {
return m_httpServer.isListening(); return m_httpServer.isListening();
} }
void OAuthHttpHandler::setListenAddressPort(const QString& full_uri) { void OAuthHttpHandler::setListenAddressPort(const QString& full_uri, bool start_handler) {
QUrl url = QUrl::fromUserInput(full_uri); QUrl url = QUrl::fromUserInput(full_uri);
QHostAddress listen_address; QHostAddress listen_address;
quint16 listen_port = quint16(url.port(80)); quint16 listen_port = quint16(url.port(80));
@ -41,7 +41,9 @@ void OAuthHttpHandler::setListenAddressPort(const QString& full_uri) {
listen_address = QHostAddress(url.host()); listen_address = QHostAddress(url.host());
} }
if (listen_address == m_listenAddress && listen_port == m_listenPort && m_httpServer.isListening()) { if (listen_address == m_listenAddress &&
listen_port == m_listenPort &&
start_handler == m_httpServer.isListening()) {
// NOTE: We do not need to change listener's settings or re-start it. // NOTE: We do not need to change listener's settings or re-start it.
return; return;
} }
@ -51,6 +53,16 @@ void OAuthHttpHandler::setListenAddressPort(const QString& full_uri) {
stop(); stop();
} }
m_listenAddress = listen_address;
m_listenPort = listen_port;
m_listenAddressPort = full_uri;
if (!start_handler) {
qDebugNN << LOGSEC_OAUTH
<< "User does not want handler to be running.";
return;
}
if (!m_httpServer.listen(listen_address, listen_port)) { if (!m_httpServer.listen(listen_address, listen_port)) {
qCriticalNN << LOGSEC_OAUTH qCriticalNN << LOGSEC_OAUTH
<< "OAuth redirect handler FAILED TO START TO LISTEN on address" << "OAuth redirect handler FAILED TO START TO LISTEN on address"
@ -61,10 +73,6 @@ void OAuthHttpHandler::setListenAddressPort(const QString& full_uri) {
<< QUOTE_W_SPACE_DOT(m_httpServer.errorString()); << QUOTE_W_SPACE_DOT(m_httpServer.errorString());
} }
else { else {
m_listenAddress = listen_address;
m_listenPort = listen_port;
m_listenAddressPort = full_uri;
qDebugNN << LOGSEC_OAUTH qDebugNN << LOGSEC_OAUTH
<< "OAuth redirect handler IS LISTENING on address" << "OAuth redirect handler IS LISTENING on address"
<< QUOTE_W_SPACE(m_listenAddress.toString()) << QUOTE_W_SPACE(m_listenAddress.toString())

View file

@ -30,7 +30,7 @@ class OAuthHttpHandler : public QObject {
QString listenAddressPort() const; QString listenAddressPort() const;
// Sets full URL string, for example "http://localhost:123456". // Sets full URL string, for example "http://localhost:123456".
void setListenAddressPort(const QString& full_uri); void setListenAddressPort(const QString& full_uri, bool start_handler);
signals: signals:
void authRejected(const QString& error_description, const QString& state); void authRejected(const QString& error_description, const QString& state);

View file

@ -127,7 +127,8 @@ void GmailNetworkFactory::initializeOauth() {
m_oauth2->setRedirectUrl(QString(OAUTH_REDIRECT_URI) + m_oauth2->setRedirectUrl(QString(OAUTH_REDIRECT_URI) +
QL1C(':') + QL1C(':') +
QString::number(GMAIL_OAUTH_REDIRECT_URI_PORT)); QString::number(GMAIL_OAUTH_REDIRECT_URI_PORT),
true);
connect(m_oauth2, &OAuth2Service::tokensRetrieveError, this, &GmailNetworkFactory::onTokensError); connect(m_oauth2, &OAuth2Service::tokensRetrieveError, this, &GmailNetworkFactory::onTokensError);
connect(m_oauth2, &OAuth2Service::authFailed, this, &GmailNetworkFactory::onAuthFailed); connect(m_oauth2, &OAuth2Service::authFailed, this, &GmailNetworkFactory::onAuthFailed);

View file

@ -71,7 +71,7 @@ void GmailServiceRoot::setCustomDatabaseData(const QVariantHash& data) {
m_network->oauth()->setClientId(data["client_id"].toString()); m_network->oauth()->setClientId(data["client_id"].toString());
m_network->oauth()->setClientSecret(data["client_secret"].toString()); m_network->oauth()->setClientSecret(data["client_secret"].toString());
m_network->oauth()->setRefreshToken(data["refresh_token"].toString()); m_network->oauth()->setRefreshToken(data["refresh_token"].toString());
m_network->oauth()->setRedirectUrl(data["redirect_uri"].toString()); m_network->oauth()->setRedirectUrl(data["redirect_uri"].toString(), true);
} }
QList<Message> GmailServiceRoot::obtainNewMessages(Feed* feed, QList<Message> GmailServiceRoot::obtainNewMessages(Feed* feed,

View file

@ -29,7 +29,8 @@ void FormEditGmailAccount::apply() {
account<GmailServiceRoot>()->network()->oauth()->logout(false); account<GmailServiceRoot>()->network()->oauth()->logout(false);
account<GmailServiceRoot>()->network()->oauth()->setClientId(m_details->m_ui.m_txtAppId->lineEdit()->text()); account<GmailServiceRoot>()->network()->oauth()->setClientId(m_details->m_ui.m_txtAppId->lineEdit()->text());
account<GmailServiceRoot>()->network()->oauth()->setClientSecret(m_details->m_ui.m_txtAppKey->lineEdit()->text()); account<GmailServiceRoot>()->network()->oauth()->setClientSecret(m_details->m_ui.m_txtAppKey->lineEdit()->text());
account<GmailServiceRoot>()->network()->oauth()->setRedirectUrl(m_details->m_ui.m_txtRedirectUrl->lineEdit()->text()); account<GmailServiceRoot>()->network()->oauth()->setRedirectUrl(m_details->m_ui.m_txtRedirectUrl->lineEdit()->text(),
true);
account<GmailServiceRoot>()->network()->setUsername(m_details->m_ui.m_txtUsername->lineEdit()->text()); account<GmailServiceRoot>()->network()->setUsername(m_details->m_ui.m_txtUsername->lineEdit()->text());
account<GmailServiceRoot>()->network()->setBatchSize(m_details->m_ui.m_spinLimitMessages->value()); account<GmailServiceRoot>()->network()->setBatchSize(m_details->m_ui.m_spinLimitMessages->value());

View file

@ -53,10 +53,10 @@ GmailAccountDetails::GmailAccountDetails(QWidget* parent)
} }
void GmailAccountDetails::testSetup(const QNetworkProxy& custom_proxy) { void GmailAccountDetails::testSetup(const QNetworkProxy& custom_proxy) {
m_oauth->logout(); m_oauth->logout(true);
m_oauth->setClientId(m_ui.m_txtAppId->lineEdit()->text()); m_oauth->setClientId(m_ui.m_txtAppId->lineEdit()->text());
m_oauth->setClientSecret(m_ui.m_txtAppKey->lineEdit()->text()); m_oauth->setClientSecret(m_ui.m_txtAppKey->lineEdit()->text());
m_oauth->setRedirectUrl(m_ui.m_txtRedirectUrl->lineEdit()->text()); m_oauth->setRedirectUrl(m_ui.m_txtRedirectUrl->lineEdit()->text(), true);
m_lastProxy = custom_proxy; m_lastProxy = custom_proxy;
m_oauth->login(); m_oauth->login();

View file

@ -7,6 +7,7 @@
#define GREADER_URL_REEDAH "https://www.reedah.com" #define GREADER_URL_REEDAH "https://www.reedah.com"
#define GREADER_URL_TOR "https://theoldreader.com" #define GREADER_URL_TOR "https://theoldreader.com"
#define GREADER_URL_BAZQUX "https://bazqux.com" #define GREADER_URL_BAZQUX "https://bazqux.com"
#define GREADER_URL_INOREADER "https://www.inoreader.com"
// States. // States.
#define GREADER_API_STATE_READING_LIST "state/com.google/reading-list" #define GREADER_API_STATE_READING_LIST "state/com.google/reading-list"
@ -41,6 +42,15 @@
#define TOR_SPONSORED_STREAM_ID "tor/sponsored" #define TOR_SPONSORED_STREAM_ID "tor/sponsored"
#define TOR_ITEM_CONTENTS_BATCH 9999 #define TOR_ITEM_CONTENTS_BATCH 9999
#define INO_HEADER_APPID "AppId"
#define INO_HEADER_APPKEY "AppKey"
#define INO_OAUTH_REDIRECT_URI_PORT 14488
#define INO_OAUTH_SCOPE "read write"
#define INO_OAUTH_TOKEN_URL "https://www.inoreader.com/oauth2/token"
#define INO_OAUTH_AUTH_URL "https://www.inoreader.com/oauth2/auth"
#define INO_REG_API_URL "https://www.inoreader.com/developers/register-app"
// FreshRSS. // FreshRSS.
#define FRESHRSS_BASE_URL_PATH "api/greader.php/" #define FRESHRSS_BASE_URL_PATH "api/greader.php/"

View file

@ -2,9 +2,9 @@
#include "services/greader/greaderentrypoint.h" #include "services/greader/greaderentrypoint.h"
#include "database/databasequeries.h"
#include "definitions/definitions.h" #include "definitions/definitions.h"
#include "miscellaneous/application.h" #include "miscellaneous/application.h"
#include "database/databasequeries.h"
#include "miscellaneous/iconfactory.h" #include "miscellaneous/iconfactory.h"
#include "services/greader/definitions.h" #include "services/greader/definitions.h"
#include "services/greader/greaderserviceroot.h" #include "services/greader/greaderserviceroot.h"
@ -32,7 +32,7 @@ QString GreaderEntryPoint::code() const {
QString GreaderEntryPoint::description() const { QString GreaderEntryPoint::description() const {
return QObject::tr("Google Reader API is used by many online RSS readers. This is here to support") + return QObject::tr("Google Reader API is used by many online RSS readers. This is here to support") +
QSL(" FreshRSS, Bazqux, TheOldReader, Reedah, ..."); QSL(" Inoreader, FreshRSS, Bazqux, TheOldReader, Reedah, ...");
} }
QString GreaderEntryPoint::author() const { QString GreaderEntryPoint::author() const {

View file

@ -3,10 +3,12 @@
#include "services/greader/greadernetwork.h" #include "services/greader/greadernetwork.h"
#include "3rd-party/boolinq/boolinq.h" #include "3rd-party/boolinq/boolinq.h"
#include "database/databasequeries.h"
#include "exceptions/applicationexception.h" #include "exceptions/applicationexception.h"
#include "exceptions/networkexception.h" #include "exceptions/networkexception.h"
#include "miscellaneous/application.h" #include "miscellaneous/application.h"
#include "network-web/networkfactory.h" #include "network-web/networkfactory.h"
#include "network-web/oauth2service.h"
#include "network-web/webfactory.h" #include "network-web/webfactory.h"
#include "services/abstract/category.h" #include "services/abstract/category.h"
#include "services/abstract/label.h" #include "services/abstract/label.h"
@ -18,9 +20,12 @@
#include <QJsonObject> #include <QJsonObject>
GreaderNetwork::GreaderNetwork(QObject* parent) GreaderNetwork::GreaderNetwork(QObject* parent)
: QObject(parent), m_service(GreaderServiceRoot::Service::FreshRss), m_username(QString()), m_password(QString()), : QObject(parent), m_root(nullptr), m_service(GreaderServiceRoot::Service::FreshRss), m_username(QString()),
m_baseUrl(QString()), m_batchSize(GREADER_DEFAULT_BATCH_SIZE), m_downloadOnlyUnreadMessages(false), m_password(QString()), m_baseUrl(QString()), m_batchSize(GREADER_DEFAULT_BATCH_SIZE), m_downloadOnlyUnreadMessages(false),
m_prefetchedMessages({}), m_performGlobalFetching(false), m_intelligentSynchronization(false) { m_prefetchedMessages({}), m_performGlobalFetching(false), m_intelligentSynchronization(false),
m_oauth2(new OAuth2Service(INO_OAUTH_AUTH_URL, INO_OAUTH_TOKEN_URL,
{}, {}, INO_OAUTH_SCOPE, this)) {
initializeOauth();
clearCredentials(); clearCredentials();
} }
@ -819,27 +824,11 @@ void GreaderNetwork::setBaseUrl(const QString& base_url) {
m_baseUrl = 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::Reedah:
return QSL("Reedah");
case GreaderServiceRoot::Service::TheOldReader:
return QSL("The Old Reader");
default:
return tr("Other services");
}
}
QPair<QByteArray, QByteArray> GreaderNetwork::authHeader() const { QPair<QByteArray, QByteArray> GreaderNetwork::authHeader() const {
return { QSL(HTTP_HEADERS_AUTHORIZATION).toLocal8Bit(), QSL("GoogleLogin auth=%1").arg(m_authAuth).toLocal8Bit() }; QPair<QByteArray, QByteArray> header = { QSL(HTTP_HEADERS_AUTHORIZATION).toLocal8Bit(),
QSL("GoogleLogin auth=%1").arg(m_authAuth).toLocal8Bit() };
return header;
} }
bool GreaderNetwork::ensureLogin(const QNetworkProxy& proxy, QNetworkReply::NetworkError* output) { bool GreaderNetwork::ensureLogin(const QNetworkProxy& proxy, QNetworkReply::NetworkError* output) {
@ -1053,6 +1042,69 @@ QString GreaderNetwork::generateFullUrl(GreaderNetwork::Operations operation) co
} }
} }
void GreaderNetwork::onTokensError(const QString& error, const QString& error_description) {
Q_UNUSED(error)
qApp->showGuiMessage(Notification::Event::GeneralEvent,
tr("Inoreader: authentication error"),
tr("Click this to login again. Error is: '%1'").arg(error_description),
QSystemTrayIcon::MessageIcon::Critical,
{}, {},
[this]() {
m_oauth2->setAccessToken(QString());
m_oauth2->setRefreshToken(QString());
m_oauth2->login();
});
}
void GreaderNetwork::onAuthFailed() {
qApp->showGuiMessage(Notification::Event::GeneralEvent,
tr("Inoreader: authorization denied"),
tr("Click this to login again."),
QSystemTrayIcon::MessageIcon::Critical,
{}, {},
[this]() {
m_oauth2->login();
});
}
void GreaderNetwork::initializeOauth() {
#if defined(INOREADER_OFFICIAL_SUPPORT)
m_oauth2->setClientSecretId(TextFactory::decrypt(INOREADER_CLIENT_ID, OAUTH_DECRYPTION_KEY));
m_oauth2->setClientSecretSecret(TextFactory::decrypt(INOREADER_CLIENT_SECRET, OAUTH_DECRYPTION_KEY));
#endif
m_oauth2->setRedirectUrl(QString(OAUTH_REDIRECT_URI) +
QL1C(':') +
QString::number(INO_OAUTH_REDIRECT_URI_PORT),
false);
connect(m_oauth2, &OAuth2Service::tokensRetrieveError, this, &GreaderNetwork::onTokensError);
connect(m_oauth2, &OAuth2Service::authFailed, this, &GreaderNetwork::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_root != nullptr && m_root->accountId() > 0 && !refresh_token.isEmpty()) {
QSqlDatabase database = qApp->database()->driver()->connection(metaObject()->className());
DatabaseQueries::storeNewOauthTokens(database, refresh_token, m_root->accountId());
}
});
}
OAuth2Service* GreaderNetwork::oauth() const {
return m_oauth2;
}
void GreaderNetwork::setOauth(OAuth2Service* oauth) {
m_oauth2 = oauth;
}
void GreaderNetwork::setRoot(GreaderServiceRoot* root) {
m_root = root;
}
bool GreaderNetwork::intelligentSynchronization() const { bool GreaderNetwork::intelligentSynchronization() const {
return m_intelligentSynchronization; return m_intelligentSynchronization;
} }

View file

@ -9,6 +9,8 @@
#include "services/abstract/feed.h" #include "services/abstract/feed.h"
#include "services/greader/greaderserviceroot.h" #include "services/greader/greaderserviceroot.h"
class OAuth2Service;
class GreaderNetwork : public QObject { class GreaderNetwork : public QObject {
Q_OBJECT Q_OBJECT
@ -87,14 +89,21 @@ class GreaderNetwork : public QObject {
void clearCredentials(); void clearCredentials();
static QString serviceToString(GreaderServiceRoot::Service service);
bool downloadOnlyUnreadMessages() const; bool downloadOnlyUnreadMessages() const;
void setDownloadOnlyUnreadMessages(bool download_only_unread); void setDownloadOnlyUnreadMessages(bool download_only_unread);
bool intelligentSynchronization() const; bool intelligentSynchronization() const;
void setIntelligentSynchronization(bool intelligent_synchronization); void setIntelligentSynchronization(bool intelligent_synchronization);
void setRoot(GreaderServiceRoot* root);
OAuth2Service* oauth() const;
void setOauth(OAuth2Service* oauth);
private slots:
void onTokensError(const QString& error, const QString& error_description);
void onAuthFailed();
private: private:
QPair<QByteArray, QByteArray> authHeader() const; QPair<QByteArray, QByteArray> authHeader() const;
@ -110,8 +119,10 @@ class GreaderNetwork : public QObject {
RootItem* decodeTagsSubscriptions(const QString& categories, const QString& feeds, bool obtain_icons, const QNetworkProxy& proxy); RootItem* decodeTagsSubscriptions(const QString& categories, const QString& feeds, bool obtain_icons, const QNetworkProxy& proxy);
QString sanitizedBaseUrl() const; QString sanitizedBaseUrl() const;
QString generateFullUrl(Operations operation) const; QString generateFullUrl(Operations operation) const;
void initializeOauth();
private: private:
GreaderServiceRoot* m_root;
GreaderServiceRoot::Service m_service; GreaderServiceRoot::Service m_service;
QString m_username; QString m_username;
QString m_password; QString m_password;
@ -124,6 +135,7 @@ class GreaderNetwork : public QObject {
QList<Message> m_prefetchedMessages; QList<Message> m_prefetchedMessages;
bool m_performGlobalFetching; bool m_performGlobalFetching;
bool m_intelligentSynchronization; bool m_intelligentSynchronization;
OAuth2Service* m_oauth2;
}; };
#endif // GREADERNETWORK_H #endif // GREADERNETWORK_H

View file

@ -9,6 +9,7 @@
#include "miscellaneous/iconfactory.h" #include "miscellaneous/iconfactory.h"
#include "miscellaneous/mutex.h" #include "miscellaneous/mutex.h"
#include "miscellaneous/textfactory.h" #include "miscellaneous/textfactory.h"
#include "network-web/oauth2service.h"
#include "services/abstract/importantnode.h" #include "services/abstract/importantnode.h"
#include "services/abstract/recyclebin.h" #include "services/abstract/recyclebin.h"
#include "services/greader/greaderentrypoint.h" #include "services/greader/greaderentrypoint.h"
@ -18,6 +19,7 @@
GreaderServiceRoot::GreaderServiceRoot(RootItem* parent) GreaderServiceRoot::GreaderServiceRoot(RootItem* parent)
: ServiceRoot(parent), m_network(new GreaderNetwork(this)) { : ServiceRoot(parent), m_network(new GreaderNetwork(this)) {
setIcon(GreaderEntryPoint().icon()); setIcon(GreaderEntryPoint().icon());
m_network->setRoot(this);
} }
bool GreaderServiceRoot::isSyncable() const { bool GreaderServiceRoot::isSyncable() const {
@ -46,6 +48,13 @@ QVariantHash GreaderServiceRoot::customDatabaseData() const {
data["download_only_unread"] = m_network->downloadOnlyUnreadMessages(); data["download_only_unread"] = m_network->downloadOnlyUnreadMessages();
data["intelligent_synchronization"] = m_network->intelligentSynchronization(); data["intelligent_synchronization"] = m_network->intelligentSynchronization();
if (m_network->service() == Service::Inoreader) {
data["client_id"] = m_network->oauth()->clientId();
data["client_secret"] = m_network->oauth()->clientSecret();
data["refresh_token"] = m_network->oauth()->refreshToken();
data["redirect_uri"] = m_network->oauth()->redirectUrl();
}
return data; return data;
} }
@ -57,6 +66,13 @@ void GreaderServiceRoot::setCustomDatabaseData(const QVariantHash& data) {
m_network->setBatchSize(data["batch_size"].toInt()); m_network->setBatchSize(data["batch_size"].toInt());
m_network->setDownloadOnlyUnreadMessages(data["download_only_unread"].toBool()); m_network->setDownloadOnlyUnreadMessages(data["download_only_unread"].toBool());
m_network->setIntelligentSynchronization(data["intelligent_synchronization"].toBool()); m_network->setIntelligentSynchronization(data["intelligent_synchronization"].toBool());
if (m_network->service() == Service::Inoreader) {
m_network->oauth()->setClientId(data["client_id"].toString());
m_network->oauth()->setClientSecret(data["client_secret"].toString());
m_network->oauth()->setRefreshToken(data["refresh_token"].toString());
m_network->oauth()->setRedirectUrl(data["redirect_uri"].toString(), true);
}
} }
void GreaderServiceRoot::aboutToBeginFeedFetching(const QList<Feed*>& feeds, void GreaderServiceRoot::aboutToBeginFeedFetching(const QList<Feed*>& feeds,
@ -70,6 +86,28 @@ void GreaderServiceRoot::aboutToBeginFeedFetching(const QList<Feed*>& feeds,
} }
} }
QString GreaderServiceRoot::serviceToString(Service service) {
switch (service) {
case Service::FreshRss:
return QSL("FreshRSS");
case Service::Bazqux:
return QSL("Bazqux");
case Service::Reedah:
return QSL("Reedah");
case Service::TheOldReader:
return QSL("The Old Reader");
case Service::Inoreader:
return QSL("Inoreader");
default:
return tr("Other services");
}
}
QList<Message> GreaderServiceRoot::obtainNewMessages(Feed* feed, QList<Message> GreaderServiceRoot::obtainNewMessages(Feed* feed,
const QHash<ServiceRoot::BagOfMessages, QStringList>& stated_messages, const QHash<ServiceRoot::BagOfMessages, QStringList>& stated_messages,
const QHash<QString, QStringList>& tagged_messages) { const QHash<QString, QStringList>& tagged_messages) {
@ -109,7 +147,17 @@ void GreaderServiceRoot::start(bool freshly_activated) {
updateTitleIcon(); updateTitleIcon();
if (getSubTreeFeeds().isEmpty()) { if (getSubTreeFeeds().isEmpty()) {
if (m_network->service() == Service::Inoreader) {
m_network->oauth()->login([this]() {
syncIn(); syncIn();
});
}
else {
syncIn();
}
}
else if (m_network->service() == Service::Inoreader) {
m_network->oauth()->login();
} }
} }
@ -199,7 +247,7 @@ ServiceRoot::LabelOperation GreaderServiceRoot::supportedLabelOperations() const
void GreaderServiceRoot::updateTitleIcon() { void GreaderServiceRoot::updateTitleIcon() {
setTitle(QString("%1 (%2)").arg(TextFactory::extractUsernameFromEmail(m_network->username()), setTitle(QString("%1 (%2)").arg(TextFactory::extractUsernameFromEmail(m_network->username()),
m_network->serviceToString(m_network->service()))); GreaderServiceRoot::serviceToString(m_network->service())));
switch (m_network->service()) { switch (m_network->service()) {
case Service::TheOldReader: case Service::TheOldReader:
@ -218,6 +266,10 @@ void GreaderServiceRoot::updateTitleIcon() {
setIcon(qApp->icons()->miscIcon(QSL("reedah"))); setIcon(qApp->icons()->miscIcon(QSL("reedah")));
break; break;
case Service::Inoreader:
setIcon(qApp->icons()->miscIcon(QSL("inoreader")));
break;
default: default:
setIcon(GreaderEntryPoint().icon()); setIcon(GreaderEntryPoint().icon());
break; break;

View file

@ -17,6 +17,7 @@ class GreaderServiceRoot : public ServiceRoot, public CacheForServiceRoot {
TheOldReader = 2, TheOldReader = 2,
Bazqux = 4, Bazqux = 4,
Reedah = 8, Reedah = 8,
Inoreader = 16,
Other = 1024 Other = 1024
}; };
@ -41,6 +42,8 @@ class GreaderServiceRoot : public ServiceRoot, public CacheForServiceRoot {
GreaderNetwork* network() const; GreaderNetwork* network() const;
static QString serviceToString(Service service);
protected: protected:
virtual RootItem* obtainNewTreeForSyncIn() const; virtual RootItem* obtainNewTreeForSyncIn() const;

View file

@ -5,6 +5,7 @@
#include "gui/guiutilities.h" #include "gui/guiutilities.h"
#include "miscellaneous/iconfactory.h" #include "miscellaneous/iconfactory.h"
#include "network-web/networkfactory.h" #include "network-web/networkfactory.h"
#include "network-web/oauth2service.h"
#include "services/greader/definitions.h" #include "services/greader/definitions.h"
#include "services/greader/greadernetwork.h" #include "services/greader/greadernetwork.h"
#include "services/greader/greaderserviceroot.h" #include "services/greader/greaderserviceroot.h"
@ -23,20 +24,31 @@ FormEditGreaderAccount::FormEditGreaderAccount(QWidget* parent)
void FormEditGreaderAccount::apply() { void FormEditGreaderAccount::apply() {
FormAccountDetails::apply(); FormAccountDetails::apply();
account<GreaderServiceRoot>()->network()->setBaseUrl(m_details->m_ui.m_txtUrl->lineEdit()->text()); GreaderServiceRoot* existing_root = account<GreaderServiceRoot>();
account<GreaderServiceRoot>()->network()->setUsername(m_details->m_ui.m_txtUsername->lineEdit()->text());
account<GreaderServiceRoot>()->network()->setPassword(m_details->m_ui.m_txtPassword->lineEdit()->text());
account<GreaderServiceRoot>()->network()->setBatchSize(m_details->m_ui.m_spinLimitMessages->value());
account<GreaderServiceRoot>()->network()->setDownloadOnlyUnreadMessages(m_details->m_ui.m_cbDownloadOnlyUnreadMessages->isChecked());
account<GreaderServiceRoot>()->network()->setService(m_details->service());
account<GreaderServiceRoot>()->network()->setIntelligentSynchronization(m_details->m_ui.m_cbNewAlgorithm->isChecked());
account<GreaderServiceRoot>()->saveAccountDataToDatabase(); existing_root->network()->setBaseUrl(m_details->m_ui.m_txtUrl->lineEdit()->text());
existing_root->network()->setUsername(m_details->m_ui.m_txtUsername->lineEdit()->text());
existing_root->network()->setPassword(m_details->m_ui.m_txtPassword->lineEdit()->text());
existing_root->network()->setBatchSize(m_details->m_ui.m_spinLimitMessages->value());
existing_root->network()->setDownloadOnlyUnreadMessages(m_details->m_ui.m_cbDownloadOnlyUnreadMessages->isChecked());
existing_root->network()->setService(m_details->service());
existing_root->network()->setIntelligentSynchronization(m_details->m_ui.m_cbNewAlgorithm->isChecked());
existing_root->network()->oauth()->logout(true);
if (existing_root->network()->service() == GreaderServiceRoot::Service::Inoreader) {
existing_root->network()->oauth()->setClientId(m_details->m_ui.m_txtAppId->lineEdit()->text());
existing_root->network()->oauth()->setClientSecret(m_details->m_ui.m_txtAppKey->lineEdit()->text());
existing_root->network()->oauth()->setRedirectUrl(m_details->m_ui.m_txtRedirectUrl->lineEdit()->text(),
true);
}
existing_root->saveAccountDataToDatabase();
accept(); accept();
if (!m_creatingNew) { if (!m_creatingNew) {
account<GreaderServiceRoot>()->completelyRemoveAllData(); existing_root->completelyRemoveAllData();
account<GreaderServiceRoot>()->start(true); existing_root->start(true);
} }
} }
@ -46,7 +58,15 @@ void FormEditGreaderAccount::loadAccountData() {
GreaderServiceRoot* existing_root = account<GreaderServiceRoot>(); GreaderServiceRoot* existing_root = account<GreaderServiceRoot>();
setWindowIcon(existing_root->icon()); setWindowIcon(existing_root->icon());
m_details->setService(existing_root->network()->service()); m_details->setService(existing_root->network()->service());
m_details->m_oauth = existing_root->network()->oauth();
m_details->hookNetwork();
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(existing_root->network()->username()); 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_txtPassword->lineEdit()->setText(existing_root->network()->password());
m_details->m_ui.m_txtUrl->lineEdit()->setText(existing_root->network()->baseUrl()); m_details->m_ui.m_txtUrl->lineEdit()->setText(existing_root->network()->baseUrl());

View file

@ -5,21 +5,26 @@
#include "definitions/definitions.h" #include "definitions/definitions.h"
#include "exceptions/applicationexception.h" #include "exceptions/applicationexception.h"
#include "gui/guiutilities.h" #include "gui/guiutilities.h"
#include "miscellaneous/application.h"
#include "miscellaneous/systemfactory.h" #include "miscellaneous/systemfactory.h"
#include "network-web/oauth2service.h"
#include "network-web/webfactory.h"
#include "services/greader/definitions.h" #include "services/greader/definitions.h"
#include "services/greader/greadernetwork.h" #include "services/greader/greadernetwork.h"
#include <QVariantHash> #include <QVariantHash>
GreaderAccountDetails::GreaderAccountDetails(QWidget* parent) : QWidget(parent) { GreaderAccountDetails::GreaderAccountDetails(QWidget* parent) : QWidget(parent),
m_oauth(nullptr), m_lastProxy({}) {
m_ui.setupUi(this); m_ui.setupUi(this);
for (auto serv : { GreaderServiceRoot::Service::Bazqux, for (auto serv : { GreaderServiceRoot::Service::Bazqux,
GreaderServiceRoot::Service::FreshRss, GreaderServiceRoot::Service::FreshRss,
GreaderServiceRoot::Service::Inoreader,
GreaderServiceRoot::Service::Reedah, GreaderServiceRoot::Service::Reedah,
GreaderServiceRoot::Service::TheOldReader, GreaderServiceRoot::Service::TheOldReader,
GreaderServiceRoot::Service::Other }) { GreaderServiceRoot::Service::Other }) {
m_ui.m_cmbService->addItem(GreaderNetwork::serviceToString(serv), QVariant::fromValue(serv)); m_ui.m_cmbService->addItem(GreaderServiceRoot::serviceToString(serv), QVariant::fromValue(serv));
} }
m_ui.m_lblTestResult->label()->setWordWrap(true); m_ui.m_lblTestResult->label()->setWordWrap(true);
@ -34,19 +39,37 @@ GreaderAccountDetails::GreaderAccountDetails(QWidget* parent) : QWidget(parent)
"articles faster, but if your feed contains more articles " "articles faster, but if your feed contains more articles "
"than specified limit, then some older articles might not be " "than specified limit, then some older articles might not be "
"fetched at all.")); "fetched at all."));
GuiUtilities::setLabelAsNotice(*m_ui.m_lblLimitMessages, true); GuiUtilities::setLabelAsNotice(*m_ui.m_lblLimitMessages, true);
m_ui.m_lblNewAlgorithm->setText(tr("If you select intelligent synchronization, then only not-yet-fetched " m_ui.m_lblNewAlgorithm->setText(tr("If you select intelligent synchronization, then only not-yet-fetched "
"or updated articles are downloaded. Network usage is greatly reduced and " "or updated articles are downloaded. Network usage is greatly reduced and "
"overall synchronization speed is greatly improved.")); "overall synchronization speed is greatly improved."));
GuiUtilities::setLabelAsNotice(*m_ui.m_lblNewAlgorithm, false); GuiUtilities::setLabelAsNotice(*m_ui.m_lblNewAlgorithm, false);
#if defined(INOREADER_OFFICIAL_SUPPORT)
m_ui.m_lblInfo->setText(tr("There are some preconfigured OAuth tokens so you do not have to fill in your "
"client ID/secret, but it is strongly recommended to obtain your "
"own as preconfigured tokens have limited global usage quota. If you wish "
"to use preconfigured tokens, simply leave all above fields to their default values even "
"if they are empty."));
#else
m_ui.m_lblInfo->setText(tr("You have to fill in your client ID/secret and also fill in correct redirect URL."));
#endif
GuiUtilities::setLabelAsNotice(*m_ui.m_lblInfo, true);
connect(m_ui.m_checkShowPassword, &QCheckBox::toggled, this, &GreaderAccountDetails::displayPassword); 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_txtPassword->lineEdit(), &BaseLineEdit::textChanged, this, &GreaderAccountDetails::onPasswordChanged);
connect(m_ui.m_txtUsername->lineEdit(), &BaseLineEdit::textChanged, this, &GreaderAccountDetails::onUsernameChanged); connect(m_ui.m_txtUsername->lineEdit(), &BaseLineEdit::textChanged, this, &GreaderAccountDetails::onUsernameChanged);
connect(m_ui.m_txtUrl->lineEdit(), &BaseLineEdit::textChanged, this, &GreaderAccountDetails::onUrlChanged); connect(m_ui.m_txtUrl->lineEdit(), &BaseLineEdit::textChanged, this, &GreaderAccountDetails::onUrlChanged);
connect(m_ui.m_cmbService, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &GreaderAccountDetails::fillPredefinedUrl); connect(m_ui.m_cmbService, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &GreaderAccountDetails::fillPredefinedUrl);
connect(m_ui.m_cbNewAlgorithm, &QCheckBox::toggled, m_ui.m_spinLimitMessages, &MessageCountSpinBox::setDisabled); connect(m_ui.m_cbNewAlgorithm, &QCheckBox::toggled, m_ui.m_spinLimitMessages, &MessageCountSpinBox::setDisabled);
connect(m_ui.m_txtAppId->lineEdit(), &BaseLineEdit::textChanged, this, &GreaderAccountDetails::checkOAuthValue);
connect(m_ui.m_txtAppKey->lineEdit(), &BaseLineEdit::textChanged, this, &GreaderAccountDetails::checkOAuthValue);
connect(m_ui.m_txtRedirectUrl->lineEdit(), &BaseLineEdit::textChanged, this, &GreaderAccountDetails::checkOAuthValue);
connect(m_ui.m_btnRegisterApi, &QPushButton::clicked, this, &GreaderAccountDetails::registerApi);
setTabOrder(m_ui.m_cmbService, m_ui.m_txtUrl->lineEdit()); setTabOrder(m_ui.m_cmbService, m_ui.m_txtUrl->lineEdit());
setTabOrder(m_ui.m_txtUrl->lineEdit(), m_ui.m_cbDownloadOnlyUnreadMessages); setTabOrder(m_ui.m_txtUrl->lineEdit(), m_ui.m_cbDownloadOnlyUnreadMessages);
@ -55,12 +78,83 @@ GreaderAccountDetails::GreaderAccountDetails(QWidget* parent) : QWidget(parent)
setTabOrder(m_ui.m_spinLimitMessages, m_ui.m_txtUsername->lineEdit()); 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_txtUsername->lineEdit(), m_ui.m_txtPassword->lineEdit());
setTabOrder(m_ui.m_txtPassword->lineEdit(), m_ui.m_checkShowPassword); setTabOrder(m_ui.m_txtPassword->lineEdit(), m_ui.m_checkShowPassword);
setTabOrder(m_ui.m_checkShowPassword, m_ui.m_btnTestSetup); setTabOrder(m_ui.m_checkShowPassword, 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_btnRegisterApi);
setTabOrder(m_ui.m_btnRegisterApi, m_ui.m_btnTestSetup);
onPasswordChanged(); onPasswordChanged();
onUsernameChanged(); onUsernameChanged();
onUrlChanged(); onUrlChanged();
displayPassword(false); displayPassword(false);
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());
}
void GreaderAccountDetails::onAuthFailed() {
m_ui.m_lblTestResult->setStatus(WidgetWithStatus::StatusType::Error,
tr("You did not grant access."),
tr("There was error during testing."));
}
void GreaderAccountDetails::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 GreaderAccountDetails::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 {
GreaderNetwork fac;
fac.setOauth(m_oauth);
auto resp = fac.userInfo(m_lastProxy);
m_ui.m_txtUsername->lineEdit()->setText(resp["userEmail"].toString());
}
catch (const ApplicationException& ex) {
qCriticalNN << LOGSEC_GREADER
<< "Failed to obtain profile with error:"
<< QUOTE_W_SPACE_DOT(ex.message());
}
}
void GreaderAccountDetails::hookNetwork() {
if (m_oauth != nullptr) {
connect(m_oauth, &OAuth2Service::tokensRetrieved, this, &GreaderAccountDetails::onAuthGranted);
connect(m_oauth, &OAuth2Service::tokensRetrieveError, this, &GreaderAccountDetails::onAuthError);
connect(m_oauth, &OAuth2Service::authFailed, this, &GreaderAccountDetails::onAuthFailed);
}
}
void GreaderAccountDetails::registerApi() {
qApp->web()->openUrlInExternalBrowser(INO_REG_API_URL);
}
void GreaderAccountDetails::checkOAuthValue(const QString& value) {
auto* line_edit = qobject_cast<LineEditWithStatus*>(sender()->parent());
if (line_edit != nullptr) {
if (value.isEmpty()) {
#if defined(INOREADER_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."));
}
}
} }
GreaderServiceRoot::Service GreaderAccountDetails::service() const { GreaderServiceRoot::Service GreaderAccountDetails::service() const {
@ -76,6 +170,18 @@ void GreaderAccountDetails::displayPassword(bool display) {
} }
void GreaderAccountDetails::performTest(const QNetworkProxy& custom_proxy) { void GreaderAccountDetails::performTest(const QNetworkProxy& custom_proxy) {
m_lastProxy = custom_proxy;
if (service() == GreaderServiceRoot::Service::Inoreader) {
if (m_oauth != nullptr) {
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_oauth->login();
}
}
else {
GreaderNetwork factory; GreaderNetwork factory;
factory.setUsername(m_ui.m_txtUsername->lineEdit()->text()); factory.setUsername(m_ui.m_txtUsername->lineEdit()->text());
@ -97,6 +203,7 @@ void GreaderAccountDetails::performTest(const QNetworkProxy& custom_proxy) {
tr("Yeah.")); tr("Yeah."));
} }
} }
}
void GreaderAccountDetails::onUsernameChanged() { void GreaderAccountDetails::onUsernameChanged() {
const QString username = m_ui.m_txtUsername->lineEdit()->text(); const QString username = m_ui.m_txtUsername->lineEdit()->text();
@ -145,9 +252,17 @@ void GreaderAccountDetails::fillPredefinedUrl() {
m_ui.m_txtUrl->lineEdit()->setText(QSL(GREADER_URL_TOR)); m_ui.m_txtUrl->lineEdit()->setText(QSL(GREADER_URL_TOR));
break; break;
case GreaderServiceRoot::Service::Inoreader:
m_ui.m_txtUrl->lineEdit()->setText(QSL(GREADER_URL_INOREADER));
break;
default: default:
m_ui.m_txtUrl->lineEdit()->clear(); m_ui.m_txtUrl->lineEdit()->clear();
m_ui.m_txtUrl->setFocus(); m_ui.m_txtUrl->setFocus();
break; break;
} }
// Show OAuth settings for Inoreader and classic for other services.
m_ui.m_stackedAuth->setCurrentIndex(service() == GreaderServiceRoot::Service::Inoreader ? 1 : 0);
m_ui.m_txtUrl->setDisabled(service() == GreaderServiceRoot::Service::Inoreader);
} }

View file

@ -11,6 +11,8 @@
#include <QNetworkProxy> #include <QNetworkProxy>
class OAuth2Service;
class GreaderAccountDetails : public QWidget { class GreaderAccountDetails : public QWidget {
Q_OBJECT Q_OBJECT
@ -29,9 +31,25 @@ class GreaderAccountDetails : public QWidget {
void onPasswordChanged(); void onPasswordChanged();
void onUrlChanged(); void onUrlChanged();
void fillPredefinedUrl(); void fillPredefinedUrl();
void checkOAuthValue(const QString& value);
void registerApi();
void onAuthFailed();
void onAuthError(const QString& error, const QString& detailed_description);
void onAuthGranted();
private:
void hookNetwork();
private: private:
Ui::GreaderAccountDetails m_ui; Ui::GreaderAccountDetails m_ui;
// Testing OAuth service. This object is not ever copied
// to new living account instance, instead only its properties
// like tokens are copied.
// If editing existing account, then the pointer points
// directly to existing OAuth from the account.
OAuth2Service* m_oauth;
QNetworkProxy m_lastProxy;
}; };
#endif // GREADERACCOUNTDETAILS_H #endif // GREADERACCOUNTDETAILS_H

View file

@ -7,7 +7,7 @@
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>430</width> <width>430</width>
<height>390</height> <height>437</height>
</rect> </rect>
</property> </property>
<layout class="QFormLayout" name="formLayout"> <layout class="QFormLayout" name="formLayout">
@ -34,6 +34,27 @@
<item row="2" column="1"> <item row="2" column="1">
<widget class="LineEditWithStatus" name="m_txtUrl" native="true"/> <widget class="LineEditWithStatus" name="m_txtUrl" native="true"/>
</item> </item>
<item row="4" column="0" colspan="2">
<widget class="QCheckBox" name="m_cbDownloadOnlyUnreadMessages">
<property name="text">
<string>Download unread articles only</string>
</property>
</widget>
</item>
<item row="5" column="0" colspan="2">
<widget class="QCheckBox" name="m_cbNewAlgorithm">
<property name="text">
<string>Intelligent synchronization algorithm</string>
</property>
</widget>
</item>
<item row="6" column="0" colspan="2">
<widget class="QLabel" name="m_lblNewAlgorithm">
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item row="7" column="0" colspan="2"> <item row="7" column="0" colspan="2">
<layout class="QHBoxLayout" name="horizontalLayout"> <layout class="QHBoxLayout" name="horizontalLayout">
<item> <item>
@ -59,7 +80,44 @@
</widget> </widget>
</item> </item>
<item row="10" column="0" colspan="2"> <item row="10" column="0" colspan="2">
<widget class="ResizableStackedWidget" name="m_stackedAuth">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="m_authClassic">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QGroupBox" name="m_gbAuthentication"> <widget class="QGroupBox" name="m_gbAuthentication">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip"> <property name="toolTip">
<string>Some feeds require authentication, including GMail feeds. BASIC, NTLM-2 and DIGEST-MD5 authentication schemes are supported.</string> <string>Some feeds require authentication, including GMail feeds. BASIC, NTLM-2 and DIGEST-MD5 authentication schemes are supported.</string>
</property> </property>
@ -83,6 +141,9 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="0" column="1">
<widget class="LineEditWithStatus" name="m_txtUsername" native="true"/>
</item>
<item row="1" column="0"> <item row="1" column="0">
<widget class="QLabel" name="label_5"> <widget class="QLabel" name="label_5">
<property name="text"> <property name="text">
@ -96,9 +157,6 @@
<item row="1" column="1"> <item row="1" column="1">
<widget class="LineEditWithStatus" name="m_txtPassword" native="true"/> <widget class="LineEditWithStatus" name="m_txtPassword" native="true"/>
</item> </item>
<item row="0" column="1">
<widget class="LineEditWithStatus" name="m_txtUsername" native="true"/>
</item>
<item row="2" column="1"> <item row="2" column="1">
<widget class="QCheckBox" name="m_checkShowPassword"> <widget class="QCheckBox" name="m_checkShowPassword">
<property name="text"> <property name="text">
@ -109,6 +167,126 @@
</layout> </layout>
</widget> </widget>
</item> </item>
</layout>
</widget>
<widget class="QWidget" name="m_authOauth">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QGroupBox" name="groupBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="title">
<string>OAuth 2.0 settings</string>
</property>
<layout class="QFormLayout" name="formLayout_3">
<item row="0" column="0">
<widget class="QLabel" name="m_lblUsername_2">
<property name="text">
<string>App ID</string>
</property>
<property name="buddy">
<cstring>m_txtAppId</cstring>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="LineEditWithStatus" name="m_txtAppId" native="true"/>
</item>
<item row="1" column="0">
<widget class="QLabel" name="m_lblUsername_3">
<property name="text">
<string>App key</string>
</property>
<property name="buddy">
<cstring>m_txtAppKey</cstring>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="LineEditWithStatus" name="m_txtAppKey" native="true"/>
</item>
<item row="2" column="0">
<widget class="QLabel" name="m_lblUsername_4">
<property name="text">
<string>Redirect URL</string>
</property>
<property name="buddy">
<cstring>m_txtRedirectUrl</cstring>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="LineEditWithStatus" name="m_txtRedirectUrl" native="true"/>
</item>
<item row="4" column="0" colspan="2">
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<widget class="QPushButton" name="m_btnRegisterApi">
<property name="text">
<string>Get my own App ID</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item row="3" column="0" colspan="2">
<widget class="QLabel" name="m_lblInfo">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>1</verstretch>
</sizepolicy>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
<item row="11" column="0" colspan="2"> <item row="11" column="0" colspan="2">
<layout class="QHBoxLayout" name="horizontalLayout_2"> <layout class="QHBoxLayout" name="horizontalLayout_2">
<item> <item>
@ -146,27 +324,6 @@
</property> </property>
</spacer> </spacer>
</item> </item>
<item row="4" column="0" colspan="2">
<widget class="QCheckBox" name="m_cbDownloadOnlyUnreadMessages">
<property name="text">
<string>Download unread articles only</string>
</property>
</widget>
</item>
<item row="5" column="0" colspan="2">
<widget class="QCheckBox" name="m_cbNewAlgorithm">
<property name="text">
<string>Intelligent synchronization algorithm</string>
</property>
</widget>
</item>
<item row="6" column="0" colspan="2">
<widget class="QLabel" name="m_lblNewAlgorithm">
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
</layout> </layout>
</widget> </widget>
<customwidgets> <customwidgets>
@ -187,6 +344,12 @@
<extends>QSpinBox</extends> <extends>QSpinBox</extends>
<header>messagecountspinbox.h</header> <header>messagecountspinbox.h</header>
</customwidget> </customwidget>
<customwidget>
<class>ResizableStackedWidget</class>
<extends>QStackedWidget</extends>
<header>resizablestackedwidget.h</header>
<container>1</container>
</customwidget>
</customwidgets> </customwidgets>
<tabstops> <tabstops>
<tabstop>m_cmbService</tabstop> <tabstop>m_cmbService</tabstop>

View file

@ -30,7 +30,8 @@ void FormEditInoreaderAccount::apply() {
account<InoreaderServiceRoot>()->network()->oauth()->logout(false); account<InoreaderServiceRoot>()->network()->oauth()->logout(false);
account<InoreaderServiceRoot>()->network()->oauth()->setClientId(m_details->m_ui.m_txtAppId->lineEdit()->text()); account<InoreaderServiceRoot>()->network()->oauth()->setClientId(m_details->m_ui.m_txtAppId->lineEdit()->text());
account<InoreaderServiceRoot>()->network()->oauth()->setClientSecret(m_details->m_ui.m_txtAppKey->lineEdit()->text()); account<InoreaderServiceRoot>()->network()->oauth()->setClientSecret(m_details->m_ui.m_txtAppKey->lineEdit()->text());
account<InoreaderServiceRoot>()->network()->oauth()->setRedirectUrl(m_details->m_ui.m_txtRedirectUrl->lineEdit()->text()); account<InoreaderServiceRoot>()->network()->oauth()->setRedirectUrl(m_details->m_ui.m_txtRedirectUrl->lineEdit()->text(),
true);
account<InoreaderServiceRoot>()->network()->setUsername(m_details->m_ui.m_txtUsername->lineEdit()->text()); account<InoreaderServiceRoot>()->network()->setUsername(m_details->m_ui.m_txtUsername->lineEdit()->text());
account<InoreaderServiceRoot>()->network()->setBatchSize(m_details->m_ui.m_spinLimitMessages->value()); account<InoreaderServiceRoot>()->network()->setBatchSize(m_details->m_ui.m_spinLimitMessages->value());

View file

@ -58,10 +58,10 @@ void InoreaderAccountDetails::testSetup(const QNetworkProxy& custom_proxy) {
m_lastProxy = custom_proxy; m_lastProxy = custom_proxy;
if (m_oauth != nullptr) { if (m_oauth != nullptr) {
m_oauth->logout(); m_oauth->logout(true);
m_oauth->setClientId(m_ui.m_txtAppId->lineEdit()->text()); m_oauth->setClientId(m_ui.m_txtAppId->lineEdit()->text());
m_oauth->setClientSecret(m_ui.m_txtAppKey->lineEdit()->text()); m_oauth->setClientSecret(m_ui.m_txtAppKey->lineEdit()->text());
m_oauth->setRedirectUrl(m_ui.m_txtRedirectUrl->lineEdit()->text()); m_oauth->setRedirectUrl(m_ui.m_txtRedirectUrl->lineEdit()->text(), true);
m_oauth->login(); m_oauth->login();
} }
} }

View file

@ -61,7 +61,8 @@ void InoreaderNetworkFactory::initializeOauth() {
m_oauth2->setRedirectUrl(QString(OAUTH_REDIRECT_URI) + m_oauth2->setRedirectUrl(QString(OAUTH_REDIRECT_URI) +
QL1C(':') + QL1C(':') +
QString::number(INOREADER_OAUTH_REDIRECT_URI_PORT)); QString::number(INOREADER_OAUTH_REDIRECT_URI_PORT),
true);
connect(m_oauth2, &OAuth2Service::tokensRetrieveError, this, &InoreaderNetworkFactory::onTokensError); connect(m_oauth2, &OAuth2Service::tokensRetrieveError, this, &InoreaderNetworkFactory::onTokensError);
connect(m_oauth2, &OAuth2Service::authFailed, this, &InoreaderNetworkFactory::onAuthFailed); connect(m_oauth2, &OAuth2Service::authFailed, this, &InoreaderNetworkFactory::onAuthFailed);

View file

@ -53,7 +53,7 @@ void InoreaderServiceRoot::setCustomDatabaseData(const QVariantHash& data) {
m_network->oauth()->setClientId(data["client_id"].toString()); m_network->oauth()->setClientId(data["client_id"].toString());
m_network->oauth()->setClientSecret(data["client_secret"].toString()); m_network->oauth()->setClientSecret(data["client_secret"].toString());
m_network->oauth()->setRefreshToken(data["refresh_token"].toString()); m_network->oauth()->setRefreshToken(data["refresh_token"].toString());
m_network->oauth()->setRedirectUrl(data["redirect_uri"].toString()); m_network->oauth()->setRedirectUrl(data["redirect_uri"].toString(), true);
} }
QList<Message> InoreaderServiceRoot::obtainNewMessages(Feed* feed, QList<Message> InoreaderServiceRoot::obtainNewMessages(Feed* feed,