From d6ba170efdd72c3321e407cdede463900aef5913 Mon Sep 17 00:00:00 2001 From: Martin Rotter Date: Tue, 16 Feb 2016 13:32:23 +0100 Subject: [PATCH] Added some initial "edit account" dialog etc. --- CMakeLists.txt | 5 + src/gui/dialogs/formmain.cpp | 17 ++ src/gui/dialogs/formupdate.cpp | 2 +- src/miscellaneous/systemfactory.cpp | 19 +- src/miscellaneous/systemfactory.h | 2 +- src/network-web/networkfactory.cpp | 11 +- src/network-web/networkfactory.h | 2 +- src/services/abstract/serviceroot.cpp | 34 +++ src/services/abstract/serviceroot.h | 19 +- src/services/owncloud/definitions.h | 21 +- .../owncloud/gui/formeditowncloudaccount.cpp | 230 ++++++++++++++++++ .../owncloud/gui/formeditowncloudaccount.h | 60 +++++ .../owncloud/gui/formeditowncloudaccount.ui | 164 +++++++++++++ .../network/owncloudnetworkfactory.cpp | 211 ++++++++++++++++ .../owncloud/network/owncloudnetworkfactory.h | 104 ++++++++ src/services/tt-rss/definitions.h | 17 ++ src/services/tt-rss/gui/formeditaccount.ui | 2 +- src/services/tt-rss/ttrssserviceroot.cpp | 34 --- src/services/tt-rss/ttrssserviceroot.h | 10 - 19 files changed, 896 insertions(+), 68 deletions(-) create mode 100755 src/services/owncloud/gui/formeditowncloudaccount.cpp create mode 100755 src/services/owncloud/gui/formeditowncloudaccount.h create mode 100755 src/services/owncloud/gui/formeditowncloudaccount.ui create mode 100755 src/services/owncloud/network/owncloudnetworkfactory.cpp create mode 100755 src/services/owncloud/network/owncloudnetworkfactory.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 05c92d944..270428eec 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -361,6 +361,8 @@ file( GLOB APP_SOURCES_GL "src/services/tt-rss/gui/*.cpp" "src/services/tt-rss/network/*.cpp" "src/services/owncloud/*.cpp" + "src/services/owncloud/network/*.cpp" + "src/services/owncloud/gui/*.cpp" "src/network-web/*.cpp" "src/network-web/adblock/*.cpp" "src/*.cpp") @@ -391,6 +393,8 @@ file( GLOB APP_HEADERS "src/services/tt-rss/gui/*.h" "src/services/tt-rss/network/*.h" "src/services/owncloud/*.h" + "src/services/owncloud/network/*.h" + "src/services/owncloud/gui/*.h" "src/network-web/*.h" "src/network-web/adblock/*.h") @@ -400,6 +404,7 @@ file( GLOB APP_FORMS "src/gui/*.ui" "src/services/standard/gui/*.ui" "src/services/tt-rss/gui/*.ui" + "src/services/owncloud/gui/*.ui" "src/network-web/*.ui" "src/network-web/adblock/*.ui") diff --git a/src/gui/dialogs/formmain.cpp b/src/gui/dialogs/formmain.cpp index a44e3f98f..919400f6c 100755 --- a/src/gui/dialogs/formmain.cpp +++ b/src/gui/dialogs/formmain.cpp @@ -54,6 +54,8 @@ #include #include +#include "services/owncloud/network/owncloudnetworkfactory.h" + FormMain::FormMain(QWidget *parent, Qt::WindowFlags f) : QMainWindow(parent, f), m_ui(new Ui::FormMain) { @@ -83,6 +85,21 @@ FormMain::FormMain(QWidget *parent, Qt::WindowFlags f) // Initialize the web factory. WebFactory::instance()->loadState(); + + + + OwnCloudNetworkFactory fac; + + fac.setAuthIsUsed(true); + fac.setUrl("https://cloud.yarpen.cz"); + fac.setAuthPassword("rssguard135"); + fac.setAuthUsername("rssguard"); + + OwnCloudStatusResponse user = fac.status(); + + QString ver = user.version(); + + int aaaa = 0; } FormMain::~FormMain() { diff --git a/src/gui/dialogs/formupdate.cpp b/src/gui/dialogs/formupdate.cpp index ea2046ece..22c2099d3 100755 --- a/src/gui/dialogs/formupdate.cpp +++ b/src/gui/dialogs/formupdate.cpp @@ -86,7 +86,7 @@ void FormUpdate::checkForUpdates() { const bool is_self_update_for_this_system = isUpdateForThisSystem() && isSelfUpdateSupported(); - if (SystemFactory::isUpdateNewer(update.first.m_availableVersion)) { + if (SystemFactory::isUpdateNewer(update.first.m_availableVersion, APP_VERSION)) { m_ui->m_lblStatus->setStatus(WidgetWithStatus::Ok, tr("New release available."), tr("This is new version which can be\ndownloaded and installed.")); diff --git a/src/miscellaneous/systemfactory.cpp b/src/miscellaneous/systemfactory.cpp index 5af31e2fe..383b78991 100755 --- a/src/miscellaneous/systemfactory.cpp +++ b/src/miscellaneous/systemfactory.cpp @@ -206,25 +206,25 @@ QPair SystemFactory::checkForUpdates() return result; } -bool SystemFactory::isUpdateNewer(const QString &update_version) { - QStringList current_version_tkn = QString(APP_VERSION).split(QL1C('.')); - QStringList new_version_tkn = update_version.split(QL1C('.')); +bool SystemFactory::isUpdateNewer(const QString &new_version, const QString &base_version) { + QStringList base_version_tkn = base_version.split(QL1C('.')); + QStringList new_version_tkn = new_version.split(QL1C('.')); - while (!current_version_tkn.isEmpty() && !new_version_tkn.isEmpty()) { - const int current_number = current_version_tkn.takeFirst().toInt(); + while (!base_version_tkn.isEmpty() && !new_version_tkn.isEmpty()) { + const int base_number = base_version_tkn.takeFirst().toInt(); const int new_number = new_version_tkn.takeFirst().toInt(); - if (new_number > current_number) { + if (new_number > base_number) { // New version is indeed higher thatn current version. return true; } - else if (new_number < current_number) { + else if (new_number < base_number) { return false; } } // Versions are either the same or they have unequal sizes. - if (current_version_tkn.isEmpty() && new_version_tkn.isEmpty()) { + if (base_version_tkn.isEmpty() && new_version_tkn.isEmpty()) { // Versions are the same. return false; } @@ -273,7 +273,8 @@ UpdateInfo SystemFactory::parseUpdatesFile(const QByteArray &updates_file, const void SystemFactory::checkForUpdatesOnStartup() { const UpdateCheck updates = checkForUpdates(); - if (updates.second == QNetworkReply::NoError && isUpdateNewer(updates.first.m_availableVersion)) { + if (updates.second == QNetworkReply::NoError && isUpdateNewer(updates.first.m_availableVersion, + APP_VERSION)) { qApp->showGuiMessage(tr("New version available"), tr("Click the bubble for more information."), QSystemTrayIcon::Information, diff --git a/src/miscellaneous/systemfactory.h b/src/miscellaneous/systemfactory.h index 71abe9850..6cc096a4e 100755 --- a/src/miscellaneous/systemfactory.h +++ b/src/miscellaneous/systemfactory.h @@ -87,7 +87,7 @@ class SystemFactory : public QObject { QPair checkForUpdates() const; // Checks if update is newer than current application version. - static bool isUpdateNewer(const QString &update_version); + static bool isUpdateNewer(const QString &new_version, const QString &base_version); public slots: void checkForUpdatesOnStartup(); diff --git a/src/network-web/networkfactory.cpp b/src/network-web/networkfactory.cpp index 0566852d9..c14293fe8 100755 --- a/src/network-web/networkfactory.cpp +++ b/src/network-web/networkfactory.cpp @@ -194,13 +194,20 @@ NetworkResult NetworkFactory::downloadFeedFile(const QString &url, int timeout, NetworkResult NetworkFactory::downloadFile(const QString &url, int timeout, QByteArray &output, bool protected_contents, - const QString &username, const QString &password) { - // Here, we want to achieve "synchronous" approach because we want synchronout download API for + const QString &username, const QString &password, bool set_basic_header) { + // Here, we want to achieve "synchronous" approach because we want synchronous download API for // some use-cases too. Downloader downloader; QEventLoop loop; NetworkResult result; + if (set_basic_header) { + QString basic_value = username + ":" + password; + QString header_value = QString("Basic ") + QString(basic_value.toUtf8().toBase64()); + + downloader.appendRawHeader("Authorization", header_value.toLocal8Bit()); + } + // We need to quit event loop when the download finishes. QObject::connect(&downloader, SIGNAL(completed(QNetworkReply::NetworkError)), &loop, SLOT(quit())); diff --git a/src/network-web/networkfactory.h b/src/network-web/networkfactory.h index 2b47f2dfd..224a4c5eb 100755 --- a/src/network-web/networkfactory.h +++ b/src/network-web/networkfactory.h @@ -56,7 +56,7 @@ class NetworkFactory { // and given timeout. static NetworkResult downloadFile(const QString &url, int timeout, QByteArray &output, bool protected_contents = false, const QString &username = QString(), - const QString &password = QString()); + const QString &password = QString(), bool set_basic_header = false); }; #endif // NETWORKFACTORY_H diff --git a/src/services/abstract/serviceroot.cpp b/src/services/abstract/serviceroot.cpp index 955034ddb..823888a6a 100755 --- a/src/services/abstract/serviceroot.cpp +++ b/src/services/abstract/serviceroot.cpp @@ -97,6 +97,40 @@ QList ServiceRoot::serviceMenu() { return QList(); } +void ServiceRoot::removeOldFeedTree(bool including_messages) { + QSqlDatabase database = qApp->database()->connection(metaObject()->className(), DatabaseFactory::FromSettings); + QSqlQuery query(database); + query.setForwardOnly(true); + + query.prepare(QSL("DELETE FROM Feeds WHERE account_id = :account_id;")); + query.bindValue(QSL(":account_id"), accountId()); + query.exec(); + + query.prepare(QSL("DELETE FROM Categories WHERE account_id = :account_id;")); + query.bindValue(QSL(":account_id"), accountId()); + query.exec(); + + if (including_messages) { + query.prepare(QSL("DELETE FROM Messages WHERE account_id = :account_id;")); + query.bindValue(QSL(":account_id"), accountId()); + query.exec(); + } +} + +void ServiceRoot::removeLeftOverMessages() { + QSqlDatabase database = qApp->database()->connection(metaObject()->className(), DatabaseFactory::FromSettings); + QSqlQuery query(database); + int account_id = accountId(); + + query.setForwardOnly(true); + query.prepare(QSL("DELETE FROM Messages WHERE account_id = :account_id AND feed NOT IN (SELECT custom_id FROM Feeds WHERE account_id = :account_id);")); + query.bindValue(QSL(":account_id"), account_id); + + if (!query.exec()) { + qWarning("Removing of left over messages failed: '%s'.", qPrintable(query.lastError().text())); + } +} + QList ServiceRoot::undeletedMessages() const { QList messages; const int account_id = accountId(); diff --git a/src/services/abstract/serviceroot.h b/src/services/abstract/serviceroot.h index 03cef3e2f..ca5051b22 100755 --- a/src/services/abstract/serviceroot.h +++ b/src/services/abstract/serviceroot.h @@ -44,10 +44,6 @@ class ServiceRoot : public RootItem { explicit ServiceRoot(RootItem *parent = NULL); virtual ~ServiceRoot(); - ///////////////////////////////////////// - // /* Members to override. - ///////////////////////////////////////// - bool deleteViaGui(); bool markAsReadUnread(ReadStatus status); @@ -149,10 +145,6 @@ class ServiceRoot : public RootItem { // Selected item is naturally recycle bin. virtual bool onAfterMessagesRestoredFromBin(RootItem *selected_item, const QList &messages); - ///////////////////////////////////////// - // Members to override. */ - ///////////////////////////////////////// - // Obvious methods to wrap signals. void itemChanged(const QList &items); void requestReloadMessageList(bool mark_selected_messages_read); @@ -166,6 +158,17 @@ class ServiceRoot : public RootItem { virtual void addNewCategory() = 0; protected: + // Removes all messages/categories/feeds which are + // associated with this account. + void removeOldFeedTree(bool including_messages); + + // Removes messages which do not belong to any + // existing feed. + // + // NOTE: This situation may happen if user deletes some feed + // from another machine and then performs sync-in on this machine. + void removeLeftOverMessages(); + QStringList textualFeedIds(const QList &feeds) const; // Takes lists of feeds/categories and assembles them into the tree structure. diff --git a/src/services/owncloud/definitions.h b/src/services/owncloud/definitions.h index afe3f58e1..0651b315f 100755 --- a/src/services/owncloud/definitions.h +++ b/src/services/owncloud/definitions.h @@ -1,7 +1,26 @@ +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2016 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + #ifndef OWNCLOUD_DEFINITIONS_H #define OWNCLOUD_DEFINITIONS_H -#define API_VERSION "1.2" +#define CONTENT_TYPE "application/json; charset=utf-8" +#define API_VERSION "1.2" +#define MINIMAL_OC_VERSION "6.0.5" #endif // OWNCLOUD_DEFINITIONS_H diff --git a/src/services/owncloud/gui/formeditowncloudaccount.cpp b/src/services/owncloud/gui/formeditowncloudaccount.cpp new file mode 100755 index 000000000..3aca37b22 --- /dev/null +++ b/src/services/owncloud/gui/formeditowncloudaccount.cpp @@ -0,0 +1,230 @@ +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2016 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + + +#include "services/owncloud/gui/formeditowncloudaccount.h" + +#include "services/owncloud/definitions.h" +#include "services/owncloud/owncloudserviceroot.h" +#include "services/owncloud/network/owncloudnetworkfactory.h" +#include "miscellaneous/iconfactory.h" +#include "network-web/networkfactory.h" + + +FormEditOwnCloudAccount::FormEditOwnCloudAccount(QWidget *parent) + : QDialog(parent), m_ui(new Ui::FormEditOwnCloudAccount), m_editableRoot(NULL) { + m_ui->setupUi(this); + m_btnOk = m_ui->m_buttonBox->button(QDialogButtonBox::Ok); + + setWindowFlags(Qt::MSWindowsFixedSizeDialogHint | Qt::Dialog | Qt::WindowSystemMenuHint); + setWindowIcon(qApp->icons()->fromTheme(QSL("application-ttrss"))); + + m_ui->m_lblServerSideUpdateInformation->setText(tr("Leaving this option on causes that updates " + "of feeds will be probably much slower and may time-out often.")); + m_ui->m_lblDescription->setText(tr("Note that at least API level %1 is required.").arg(MINIMAL_OC_VERSION)); + m_ui->m_txtPassword->lineEdit()->setPlaceholderText(tr("Password for your TT-RSS account")); + m_ui->m_txtUsername->lineEdit()->setPlaceholderText(tr("Username for your TT-RSS account")); + m_ui->m_txtUrl->lineEdit()->setPlaceholderText(tr("FULL URL of your TT-RSS instance WITH trailing \"/api/\" string")); + m_ui->m_lblTestResult->setStatus(WidgetWithStatus::Information, + tr("No test done yet."), + tr("Here, results of connection test are shown.")); + + setTabOrder(m_ui->m_txtUrl->lineEdit(), m_ui->m_checkServerSideUpdate); + setTabOrder(m_ui->m_checkServerSideUpdate, 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); + setTabOrder(m_ui->m_btnTestSetup, m_ui->m_buttonBox); + + connect(m_ui->m_checkShowPassword, SIGNAL(toggled(bool)), this, SLOT(displayPassword(bool))); + connect(m_ui->m_buttonBox, SIGNAL(accepted()), this, SLOT(onClickedOk())); + connect(m_ui->m_buttonBox, SIGNAL(rejected()), this, SLOT(onClickedCancel())); + connect(m_ui->m_txtPassword->lineEdit(), SIGNAL(textChanged(QString)), this, SLOT(onPasswordChanged())); + connect(m_ui->m_txtUsername->lineEdit(), SIGNAL(textChanged(QString)), this, SLOT(onUsernameChanged())); + connect(m_ui->m_txtUrl->lineEdit(), SIGNAL(textChanged(QString)), this, SLOT(onUrlChanged())); + connect(m_ui->m_txtPassword->lineEdit(), SIGNAL(textChanged(QString)), this, SLOT(checkOkButton())); + connect(m_ui->m_txtUsername->lineEdit(), SIGNAL(textChanged(QString)), this, SLOT(checkOkButton())); + connect(m_ui->m_txtUrl->lineEdit(), SIGNAL(textChanged(QString)), this, SLOT(checkOkButton())); + connect(m_ui->m_btnTestSetup, SIGNAL(clicked()), this, SLOT(performTest())); + + onPasswordChanged(); + onUsernameChanged(); + onUrlChanged(); + checkOkButton(); + displayPassword(false); +} + +FormEditOwnCloudAccount::~FormEditOwnCloudAccount() { +} + +OwnCloudServiceRoot *FormEditOwnCloudAccount::execForCreate() { + setWindowTitle(tr("Add new Tiny Tiny RSS account")); + exec(); + return m_editableRoot; +} + +void FormEditOwnCloudAccount::execForEdit(OwnCloudServiceRoot *existing_root) { + setWindowTitle(tr("Edit existing Tiny Tiny RSS account")); + m_editableRoot = existing_root; + + // TODO: todo +/* m_ui->m_txtUsername->lineEdit()->setText(existing_root->network()->username()); + m_ui->m_txtPassword->lineEdit()->setText(existing_root->network()->password()); + m_ui->m_txtUrl->lineEdit()->setText(existing_root->network()->url()); + m_ui->m_checkServerSideUpdate->setChecked(existing_root->network()->forceServerSideUpdate()); +*/ + exec(); +} + +void FormEditOwnCloudAccount::displayPassword(bool display) { + m_ui->m_txtPassword->lineEdit()->setEchoMode(display ? QLineEdit::Normal : QLineEdit::Password); +} + +void FormEditOwnCloudAccount::performTest() { + // TODO: todo + /*TtRssNetworkFactory factory; + + factory.setUsername(m_ui->m_txtUsername->lineEdit()->text()); + factory.setPassword(m_ui->m_txtPassword->lineEdit()->text()); + factory.setUrl(m_ui->m_txtUrl->lineEdit()->text()); + factory.setAuthIsUsed(m_ui->m_gbHttpAuthentication->isChecked()); + factory.setAuthUsername(m_ui->m_txtHttpUsername->lineEdit()->text()); + factory.setAuthPassword(m_ui->m_txtHttpPassword->lineEdit()->text()); + factory.setForceServerSideUpdate(m_ui->m_checkServerSideUpdate->isChecked()); + + TtRssLoginResponse result = factory.login(); + + if (result.isLoaded()) { + if (result.hasError()) { + QString error = result.error(); + + if (error == API_DISABLED) { + m_ui->m_lblTestResult->setStatus(WidgetWithStatus::Error, + tr("API access on selected server is not enabled."), + tr("API access on selected server is not enabled.")); + } + else if (error == LOGIN_ERROR) { + m_ui->m_lblTestResult->setStatus(WidgetWithStatus::Error, + tr("Entered credentials are incorrect."), + tr("Entered credentials are incorrect.")); + } + else { + m_ui->m_lblTestResult->setStatus(WidgetWithStatus::Error, + tr("Other error occurred, contact developers."), + tr("Other error occurred, contact developers.")); + } + } + else if (result.apiLevel() < MINIMAL_API_LEVEL) { + m_ui->m_lblTestResult->setStatus(WidgetWithStatus::Error, + tr("Selected Tiny Tiny RSS server is running unsupported version of API (%1). At least API level %2 is required.").arg(QString::number(result.apiLevel()), + QString::number(MINIMAL_API_LEVEL)), + tr("Selected Tiny Tiny RSS server is running unsupported version of API.")); + } + else { + m_ui->m_lblTestResult->setStatus(WidgetWithStatus::Ok, + tr("Tiny Tiny RSS server is okay, running with API level %1, while at least API level %2 is required.").arg(QString::number(result.apiLevel()), + QString::number(MINIMAL_API_LEVEL)), + tr("Tiny Tiny RSS server is okay.")); + } + } + else if (factory.lastError() != QNetworkReply::NoError ) { + m_ui->m_lblTestResult->setStatus(WidgetWithStatus::Error, + tr("Network error: '%1'.").arg(NetworkFactory::networkErrorText(factory.lastError())), + tr("Network error, have you entered correct Tiny Tiny RSS API endpoint and password?")); + } + else { + m_ui->m_lblTestResult->setStatus(WidgetWithStatus::Error, + tr("Unspecified error, did you enter correct URL?"), + tr("Unspecified error, did you enter correct URL?")); + }*/ +} + +void FormEditOwnCloudAccount::onClickedOk() { + bool editing_account = true; + + if (m_editableRoot == NULL) { + // We want to confirm newly created account. + // So save new account into DB, setup its properties. + m_editableRoot = new OwnCloudServiceRoot(); + editing_account = false; + } + + // TODO: TODO + /* + m_editableRoot->network()->setUrl(m_ui->m_txtUrl->lineEdit()->text()); + m_editableRoot->network()->setUsername(m_ui->m_txtUsername->lineEdit()->text()); + m_editableRoot->network()->setPassword(m_ui->m_txtPassword->lineEdit()->text()); + m_editableRoot->network()->setAuthIsUsed(m_ui->m_gbHttpAuthentication->isChecked()); + m_editableRoot->network()->setAuthUsername(m_ui->m_txtHttpUsername->lineEdit()->text()); + m_editableRoot->network()->setAuthPassword(m_ui->m_txtHttpPassword->lineEdit()->text()); + m_editableRoot->network()->setForceServerSideUpdate(m_ui->m_checkServerSideUpdate->isChecked()); + m_editableRoot->saveAccountDataToDatabase(); + + accept(); + + if (editing_account) { + m_editableRoot->network()->logout(); + m_editableRoot->completelyRemoveAllData(); + m_editableRoot->syncIn(); + }*/ +} + +void FormEditOwnCloudAccount::onClickedCancel() { + reject(); +} + +void FormEditOwnCloudAccount::onUsernameChanged() { + const QString username = m_ui->m_txtUsername->lineEdit()->text(); + + if (username.isEmpty()) { + m_ui->m_txtUsername->setStatus(WidgetWithStatus::Error, tr("Username cannot be empty.")); + } + else { + m_ui->m_txtUsername->setStatus(WidgetWithStatus::Ok, tr("Username is okay.")); + } +} + +void FormEditOwnCloudAccount::onPasswordChanged() { + const QString password = m_ui->m_txtPassword->lineEdit()->text(); + + if (password.isEmpty()) { + m_ui->m_txtPassword->setStatus(WidgetWithStatus::Error, tr("Password cannot be empty.")); + } + else { + m_ui->m_txtPassword->setStatus(WidgetWithStatus::Ok, tr("Password is okay.")); + } +} + +void FormEditOwnCloudAccount::onUrlChanged() { + const QString url = m_ui->m_txtUrl->lineEdit()->text(); + + if (url.isEmpty()) { + m_ui->m_txtUrl->setStatus(WidgetWithStatus::Error, tr("URL cannot be empty.")); + } + else if (!url.endsWith(QL1S("/api/"))) { + m_ui->m_txtUrl->setStatus(WidgetWithStatus::Warning, tr("URL should end with \"/api/\".")); + } + else { + m_ui->m_txtUrl->setStatus(WidgetWithStatus::Ok, tr("URL is okay.")); + } +} + +void FormEditOwnCloudAccount::checkOkButton() { + m_btnOk->setEnabled(!m_ui->m_txtUsername->lineEdit()->text().isEmpty() && + !m_ui->m_txtPassword->lineEdit()->text().isEmpty() && + !m_ui->m_txtUrl->lineEdit()->text().isEmpty()); +} diff --git a/src/services/owncloud/gui/formeditowncloudaccount.h b/src/services/owncloud/gui/formeditowncloudaccount.h new file mode 100755 index 000000000..003fad760 --- /dev/null +++ b/src/services/owncloud/gui/formeditowncloudaccount.h @@ -0,0 +1,60 @@ +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2016 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + + +#ifndef FORMEDITOWNCLOUDACCOUNT_H +#define FORMEDITOWNCLOUDACCOUNT_H + +#include + +#include "ui_formeditowncloudaccount.h" + + +namespace Ui { + class FormEditAccount; +} + +class OwnCloudServiceRoot; + +class FormEditOwnCloudAccount : public QDialog { + Q_OBJECT + + public: + explicit FormEditOwnCloudAccount(QWidget *parent = 0); + virtual ~FormEditOwnCloudAccount(); + + OwnCloudServiceRoot *execForCreate(); + void execForEdit(OwnCloudServiceRoot *existing_root); + + private slots: + void displayPassword(bool display); + void performTest(); + void onClickedOk(); + void onClickedCancel(); + + void onUsernameChanged(); + void onPasswordChanged(); + void onUrlChanged(); + void checkOkButton(); + + private: + QScopedPointer m_ui; + OwnCloudServiceRoot *m_editableRoot; + QPushButton *m_btnOk; +}; + +#endif // FORMEDITOWNCLOUDACCOUNT_H diff --git a/src/services/owncloud/gui/formeditowncloudaccount.ui b/src/services/owncloud/gui/formeditowncloudaccount.ui new file mode 100755 index 000000000..28d20d467 --- /dev/null +++ b/src/services/owncloud/gui/formeditowncloudaccount.ui @@ -0,0 +1,164 @@ + + + FormEditOwnCloudAccount + + + + 0 + 0 + 541 + 273 + + + + Dialog + + + + + + + + + + URL + + + m_txtUrl + + + + + + + + + + + + + + + true + + + + + + + Force execution of server-side update when updating feeds from RSS Guard + + + + + + + + + + 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::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + LineEditWithStatus + QWidget +
lineeditwithstatus.h
+ 1 +
+ + LabelWithStatus + QWidget +
labelwithstatus.h
+ 1 +
+
+ + +
diff --git a/src/services/owncloud/network/owncloudnetworkfactory.cpp b/src/services/owncloud/network/owncloudnetworkfactory.cpp new file mode 100755 index 000000000..60f70788b --- /dev/null +++ b/src/services/owncloud/network/owncloudnetworkfactory.cpp @@ -0,0 +1,211 @@ +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2016 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#include "services/owncloud/network/owncloudnetworkfactory.h" + +#include "services/owncloud/definitions.h" +#include "network-web/networkfactory.h" +#include "miscellaneous/application.h" +#include "miscellaneous/settings.h" + +#include + + +OwnCloudNetworkFactory::OwnCloudNetworkFactory() + : m_url(QString()), m_forceServerSideUpdate(false), m_authIsUsed(false), + m_authUsername(QString()), m_authPassword(QString()), m_urlUser(QString()), m_urlStatus(QString()) { +} + +OwnCloudNetworkFactory::~OwnCloudNetworkFactory() { +} + +QString OwnCloudNetworkFactory::url() const { + return m_url; +} + +void OwnCloudNetworkFactory::setUrl(const QString &url) { + if (url.endsWith('/')) { + m_url = url; + } + else { + m_url = url + '/'; + } + + // Store endpoints. + m_urlUser = m_url + "index.php/apps/news/api/v1-2/user"; + m_urlStatus = m_url + "index.php/apps/news/api/v1-2/status"; +} + +bool OwnCloudNetworkFactory::forceServerSideUpdate() const { + return m_forceServerSideUpdate; +} + +void OwnCloudNetworkFactory::setForceServerSideUpdate(bool force_update) { + m_forceServerSideUpdate = force_update; +} + +bool OwnCloudNetworkFactory::authIsUsed() const { + return m_authIsUsed; +} + +void OwnCloudNetworkFactory::setAuthIsUsed(bool authIsUsed) { + m_authIsUsed = authIsUsed; +} + +QString OwnCloudNetworkFactory::authUsername() const { + return m_authUsername; +} + +void OwnCloudNetworkFactory::setAuthUsername(const QString &auth_username) { + m_authUsername = auth_username; +} + +QString OwnCloudNetworkFactory::authPassword() const { + return m_authPassword; +} + +void OwnCloudNetworkFactory::setAuthPassword(const QString &auth_password) { + m_authPassword = auth_password; +} + +QNetworkReply::NetworkError OwnCloudNetworkFactory::lastError() const { + return m_lastError; +} + +OwnCloudUserResponse OwnCloudNetworkFactory::userInfo() { + QByteArray result_raw; + NetworkResult network_reply = NetworkFactory::downloadFile(m_urlUser, + qApp->settings()->value(GROUP(Feeds), + SETTING(Feeds::UpdateTimeout)).toInt(), + result_raw, + m_authIsUsed, m_authUsername, m_authPassword, + true); + OwnCloudUserResponse user_response(QString::fromUtf8(result_raw)); + + if (network_reply.first != QNetworkReply::NoError) { + qWarning("ownCloud: Obtaining user info failed with error %d.", network_reply.first); + } + + m_lastError = network_reply.first; + return user_response; +} + +OwnCloudStatusResponse OwnCloudNetworkFactory::status() { + QByteArray result_raw; + NetworkResult network_reply = NetworkFactory::downloadFile(m_urlStatus, + qApp->settings()->value(GROUP(Feeds), + SETTING(Feeds::UpdateTimeout)).toInt(), + result_raw, + m_authIsUsed, m_authUsername, m_authPassword, + true); + OwnCloudStatusResponse status_response(QString::fromUtf8(result_raw)); + + if (network_reply.first != QNetworkReply::NoError) { + qWarning("ownCloud: Obtaining status info failed with error %d.", network_reply.first); + } + + m_lastError = network_reply.first; + return status_response; +} + +OwnCloudResponse::OwnCloudResponse(const QString &raw_content) { + m_rawContent = QtJson::parse(raw_content).toMap(); +} + +OwnCloudResponse::~OwnCloudResponse() { +} + +bool OwnCloudResponse::isLoaded() const { + return !m_rawContent.empty(); +} + +QString OwnCloudResponse::toString() const { + return QtJson::serializeStr(m_rawContent); +} + + +OwnCloudUserResponse::OwnCloudUserResponse(const QString &raw_content) : OwnCloudResponse(raw_content) { +} + +OwnCloudUserResponse::~OwnCloudUserResponse() { +} + +QString OwnCloudUserResponse::displayName() const { + if (isLoaded()) { + return m_rawContent["displayName"].toString(); + } + else { + return QString(); + } +} + +QString OwnCloudUserResponse::userId() const { + if (isLoaded()) { + return m_rawContent["userId"].toString(); + } + else { + return QString(); + } +} + +QDateTime OwnCloudUserResponse::lastLoginTime() const { + if (isLoaded()) { + return QDateTime::fromMSecsSinceEpoch(m_rawContent["lastLoginTimestamp"].value()); + } + else { + return QDateTime(); + } +} + +QIcon OwnCloudUserResponse::avatar() const { + if (isLoaded()) { + QString image_data = m_rawContent["avatar"].toMap()["data"].toString(); + QByteArray decoded_data = QByteArray::fromBase64(image_data.toLocal8Bit()); + QPixmap image; + + if (image.loadFromData(decoded_data)) { + return QIcon(image); + } + } + + return QIcon(); +} + + +OwnCloudStatusResponse::OwnCloudStatusResponse(const QString &raw_content) : OwnCloudResponse(raw_content) { +} + +OwnCloudStatusResponse::~OwnCloudStatusResponse() { +} + +QString OwnCloudStatusResponse::version() const { + if (isLoaded()) { + return m_rawContent["version"].toString(); + } + else { + return QString(); + } +} + +bool OwnCloudStatusResponse::misconfiguredCron() const { + if (isLoaded()) { + return m_rawContent["warnings"].toMap()["improperlyConfiguredCron"].toBool(); + } + else { + return false; + } +} diff --git a/src/services/owncloud/network/owncloudnetworkfactory.h b/src/services/owncloud/network/owncloudnetworkfactory.h new file mode 100755 index 000000000..9c483b833 --- /dev/null +++ b/src/services/owncloud/network/owncloudnetworkfactory.h @@ -0,0 +1,104 @@ +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2016 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + +#ifndef OWNCLOUDNETWORKFACTORY_H +#define OWNCLOUDNETWORKFACTORY_H + +#include +#include +#include +#include + +#include "qt-json/json.h" + + +class OwnCloudResponse { + public: + explicit OwnCloudResponse(const QString &raw_content = QString()); + virtual ~OwnCloudResponse(); + + bool isLoaded() const; + QString toString() const; + + protected: + QtJson::JsonObject m_rawContent; +}; + +class OwnCloudUserResponse : public OwnCloudResponse { + public: + explicit OwnCloudUserResponse(const QString &raw_content = QString()); + virtual ~OwnCloudUserResponse(); + + QString userId() const; + QString displayName() const; + QDateTime lastLoginTime() const; + QIcon avatar() const; +}; + +class OwnCloudStatusResponse : public OwnCloudResponse { + public: + explicit OwnCloudStatusResponse(const QString &raw_content = QString()); + virtual ~OwnCloudStatusResponse(); + + QString version() const; + bool misconfiguredCron() const; +}; + +class OwnCloudNetworkFactory { + public: + explicit OwnCloudNetworkFactory(); + virtual ~OwnCloudNetworkFactory(); + + QString url() const; + void setUrl(const QString &url); + + bool forceServerSideUpdate() const; + void setForceServerSideUpdate(bool force_update); + + bool authIsUsed() const; + void setAuthIsUsed(bool authIsUsed); + + QString authUsername() const; + void setAuthUsername(const QString &auth_username); + + QString authPassword() const; + void setAuthPassword(const QString &auth_password); + + QNetworkReply::NetworkError lastError() const; + + // Operations. + + // Get user info. + OwnCloudUserResponse userInfo(); + + // Get version info. + OwnCloudStatusResponse status(); + + private: + QString m_url; + bool m_forceServerSideUpdate; + bool m_authIsUsed; + QString m_authUsername; + QString m_authPassword; + QNetworkReply::NetworkError m_lastError; + + // Endpoints. + QString m_urlUser; + QString m_urlStatus; +}; + +#endif // OWNCLOUDNETWORKFACTORY_H diff --git a/src/services/tt-rss/definitions.h b/src/services/tt-rss/definitions.h index a3e50f045..fae9637d6 100755 --- a/src/services/tt-rss/definitions.h +++ b/src/services/tt-rss/definitions.h @@ -1,3 +1,20 @@ +// This file is part of RSS Guard. +// +// Copyright (C) 2011-2016 by Martin Rotter +// +// RSS Guard is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// RSS Guard is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with RSS Guard. If not, see . + #ifndef TTRSS_DEFINITIONS_H #define TTRSS_DEFINITIONS_H diff --git a/src/services/tt-rss/gui/formeditaccount.ui b/src/services/tt-rss/gui/formeditaccount.ui index 51324f9a0..89b940531 100755 --- a/src/services/tt-rss/gui/formeditaccount.ui +++ b/src/services/tt-rss/gui/formeditaccount.ui @@ -161,7 +161,7 @@ - Force execution of server-side update when updating feeds from RSS Guard + Force execution of server-side update when updating feeds from RSS Guard diff --git a/src/services/tt-rss/ttrssserviceroot.cpp b/src/services/tt-rss/ttrssserviceroot.cpp index 645cc9f26..6143ea757 100755 --- a/src/services/tt-rss/ttrssserviceroot.cpp +++ b/src/services/tt-rss/ttrssserviceroot.cpp @@ -579,40 +579,6 @@ QStringList TtRssServiceRoot::customIDsOfMessages(const QList &messages return list; } -void TtRssServiceRoot::removeOldFeedTree(bool including_messages) { - QSqlDatabase database = qApp->database()->connection(metaObject()->className(), DatabaseFactory::FromSettings); - QSqlQuery query(database); - query.setForwardOnly(true); - - query.prepare(QSL("DELETE FROM Feeds WHERE account_id = :account_id;")); - query.bindValue(QSL(":account_id"), accountId()); - query.exec(); - - query.prepare(QSL("DELETE FROM Categories WHERE account_id = :account_id;")); - query.bindValue(QSL(":account_id"), accountId()); - query.exec(); - - if (including_messages) { - query.prepare(QSL("DELETE FROM Messages WHERE account_id = :account_id;")); - query.bindValue(QSL(":account_id"), accountId()); - query.exec(); - } -} - -void TtRssServiceRoot::removeLeftOverMessages() { - QSqlDatabase database = qApp->database()->connection(metaObject()->className(), DatabaseFactory::FromSettings); - QSqlQuery query(database); - int account_id = accountId(); - - query.setForwardOnly(true); - query.prepare(QSL("DELETE FROM Messages WHERE account_id = :account_id AND feed NOT IN (SELECT custom_id FROM Feeds WHERE account_id = :account_id);")); - query.bindValue(QSL(":account_id"), account_id); - - if (!query.exec()) { - qWarning("Removing of left over messages failed: '%s'.", qPrintable(query.lastError().text())); - } -} - void TtRssServiceRoot::cleanAllItems() { foreach (RootItem *top_level_item, childItems()) { if (top_level_item->kind() != RootItemKind::Bin) { diff --git a/src/services/tt-rss/ttrssserviceroot.h b/src/services/tt-rss/ttrssserviceroot.h index ca0df7837..0c0eab6d7 100755 --- a/src/services/tt-rss/ttrssserviceroot.h +++ b/src/services/tt-rss/ttrssserviceroot.h @@ -74,16 +74,6 @@ class TtRssServiceRoot : public ServiceRoot { QStringList customIDsOfMessages(const QList > &changes); QStringList customIDsOfMessages(const QList &messages); - // Removes all messages/categories/feeds which are - // associated with this account. - void removeOldFeedTree(bool including_messages); - - // Removes messages which do not belong to any - // existing feed. - // - // NOTE: This situation may happen if user deletes some feed - // from another machine and then performs sync-in on this machine. - void removeLeftOverMessages(); void cleanAllItems(); // Takes new tree and adds its feeds/categories/whatever.