simple custom data SQL layer for accounts, something similar will come for feeds too maybe

This commit is contained in:
Martin Rotter 2021-02-28 19:44:02 +01:00 committed by Martin Rotter
parent 9d6c42002d
commit 3469e457ca
8 changed files with 54 additions and 171 deletions

@ -1 +1 @@
Subproject commit 47f4125753452eff8800dbd6600c5a05540b15d9
Subproject commit 9c10723bfbaf6cb85107d6ee16e0324e9e487749

View File

@ -13,27 +13,8 @@ CREATE TABLE Accounts (
proxy_port INTEGER,
proxy_username TEXT,
proxy_password TEXT,
/* Custom attributes dynamically mapped to actual account data fields. */
custom_data_1 TEXT,
custom_data_2 TEXT,
custom_data_3 TEXT,
custom_data_4 TEXT,
custom_data_5 TEXT,
custom_data_6 TEXT,
custom_data_7 TEXT,
custom_data_8 TEXT,
custom_data_9 TEXT,
custom_data_10 TEXT,
custom_data_11 TEXT,
custom_data_12 TEXT,
custom_data_13 TEXT,
custom_data_14 TEXT,
custom_data_15 TEXT,
custom_data_16 TEXT,
custom_data_17 TEXT,
custom_data_18 TEXT,
custom_data_19 TEXT,
custom_data_20 TEXT
/* Custom column for (serialized) custom account-specific data. */
custom_data TEXT
);
-- !
CREATE TABLE Categories (

View File

@ -1677,32 +1677,12 @@ void DatabaseQueries::createOverwriteAccount(const QSqlDatabase& db, ServiceRoot
// Now we construct the SQL update query.
auto proxy = account->networkProxy();
QString sql_statement = QSL("UPDATE Accounts "
"SET proxy_type = :proxy_type, proxy_host = :proxy_host, proxy_port = :proxy_port, "
" proxy_username = :proxy_username, proxy_password = :proxy_password%1 "
"WHERE id = :id");
auto custom_attributes = account->customDatabaseAttributes();
QStringList custom_sql_clauses;
for (int i = 0; i < custom_attributes.size(); i++) {
QString target_data = account->property(custom_attributes.at(i).m_name.toLocal8Bit()).toString();
if (custom_attributes.at(i).m_encrypted) {
target_data = TextFactory::encrypt(target_data);
}
custom_sql_clauses.append(QSL("custom_data_%1 = '%2'").arg(QString::number(i + 1),
target_data));
}
if (!custom_sql_clauses.isEmpty()) {
sql_statement = sql_statement.arg(QSL(", ") + custom_sql_clauses.join(QSL(", ")));
}
else {
sql_statement = sql_statement.arg(QString());
}
q.prepare(sql_statement);
q.prepare(QSL("UPDATE Accounts "
"SET proxy_type = :proxy_type, proxy_host = :proxy_host, proxy_port = :proxy_port, "
" proxy_username = :proxy_username, proxy_password = :proxy_password, "
" custom_data = :custom_data "
"WHERE id = :id"));
q.bindValue(QSL(":proxy_type"), proxy.type());
q.bindValue(QSL(":proxy_host"), proxy.hostName());
q.bindValue(QSL(":proxy_port"), proxy.port());
@ -1710,6 +1690,9 @@ void DatabaseQueries::createOverwriteAccount(const QSqlDatabase& db, ServiceRoot
q.bindValue(QSL(":proxy_password"), TextFactory::encrypt(proxy.password()));
q.bindValue(QSL(":id"), account->accountId());
q.bindValue(QSL(":custom_data"),
QString::fromUtf8(QJsonDocument::fromVariant(account->customDatabaseData()).toJson(QJsonDocument::JsonFormat::Indented)));
if (!q.exec()) {
throw ApplicationException(q.lastError().text());
}

View File

@ -14,6 +14,8 @@
#include "services/abstract/serviceroot.h"
#include "services/standard/standardfeed.h"
#include <QJsonDocument>
#include <QJsonObject>
#include <QMultiMap>
#include <QSqlError>
#include <QSqlQuery>
@ -197,20 +199,7 @@ QList<ServiceRoot*> DatabaseQueries::getAccounts(const QSqlDatabase& db, const Q
TextFactory::decrypt(query.value(QSL("proxy_password")).toString()));
root->setNetworkProxy(proxy);
// Load account-specific custom data.
auto custom_attributes = root->customDatabaseAttributes();
for (int i = 0; i < custom_attributes.size(); i++) {
const QString target_db_attribute = QSL("custom_data_%1").arg(QString::number(i + 1));
QString target_data = query.value(target_db_attribute).toString();
if (custom_attributes.at(i).m_encrypted) {
target_data = TextFactory::decrypt(target_data);
}
root->setProperty(custom_attributes.at(i).m_name.toLocal8Bit(), target_data);
}
root->setCustomDatabaseData(QJsonDocument::fromJson(query.value(QSL("custom_data")).toString().toUtf8()).object().toVariantHash());
roots.append(root);
}

View File

@ -260,16 +260,20 @@ ServiceRoot::LabelOperation ServiceRoot::supportedLabelOperations() const {
return LabelOperation::Adding | LabelOperation::Editing | LabelOperation::Deleting;
}
QList<CustomDatabaseEntry> ServiceRoot::customDatabaseAttributes() const {
return {};
}
void ServiceRoot::saveAccountDataToDatabase() {
QSqlDatabase database = qApp->database()->connection(metaObject()->className());
DatabaseQueries::createOverwriteAccount(database, this);
}
QVariantHash ServiceRoot::customDatabaseData() const {
return {};
}
void ServiceRoot::setCustomDatabaseData(const QVariantHash& data) const {
Q_UNUSED(data)
}
void ServiceRoot::itemChanged(const QList<RootItem*>& items) {
emit dataChanged(items);
}

View File

@ -9,6 +9,7 @@
#include "core/messagefilter.h"
#include "definitions/typedefs.h"
#include <QJsonDocument>
#include <QNetworkProxy>
#include <QPair>
@ -59,8 +60,9 @@ class ServiceRoot : public RootItem {
virtual bool supportsFeedAdding() const;
virtual bool supportsCategoryAdding() const;
virtual LabelOperation supportedLabelOperations() const;
virtual QList<CustomDatabaseEntry> customDatabaseAttributes() const;
virtual void saveAccountDataToDatabase();
virtual QVariantHash customDatabaseData() const;
virtual void setCustomDatabaseData(const QVariantHash& json) const;
// Returns list of specific actions for "Add new item" main window menu.
// So typical list of returned actions could look like:

View File

@ -185,11 +185,30 @@ void TtRssServiceRoot::saveAllCachedData(bool ignore_errors) {
}
}
QList<CustomDatabaseEntry> TtRssServiceRoot::customDatabaseAttributes() const {
return {
{ QSL("username") }, { QSL("password"), true }, { QSL("auth_protected") }, { QSL("auth_username") },
{ QSL("auth_password"), true }, { QSL("url") }, { QSL("force_update") }, { QSL("download_only_unread") }
};
QVariantHash TtRssServiceRoot::customDatabaseData() const {
QVariantHash data;
data["username"] = m_network->username();
data["password"] = TextFactory::encrypt(m_network->password());
data["auth_protected"] = m_network->authIsUsed();
data["auth_username"] = m_network->authUsername();
data["auth_password"] = TextFactory::encrypt(m_network->authPassword());
data["url"] = m_network->url();
data["force_update"] = m_network->forceServerSideUpdate();
data["download_only_unread"] = m_network->downloadOnlyUnreadMessages();
return data;
}
void TtRssServiceRoot::setCustomDatabaseData(const QVariantHash& data) const {
m_network->setUsername( data["username"].toString());
m_network->setPassword(TextFactory::decrypt(data["password"].toString()));
m_network->setAuthIsUsed(data["auth_protected"].toBool());
m_network->setAuthUsername(data["auth_username"].toString());
m_network->setAuthPassword(TextFactory::decrypt(data["auth_password"].toString()));
m_network->setUrl(data["url"].toString());
m_network->setForceServerSideUpdate(data["force_update"].toBool());
m_network->setDownloadOnlyUnreadMessages(data["download_only_unread"].toBool());
}
QString TtRssServiceRoot::additionalTooltip() const {
@ -216,70 +235,6 @@ void TtRssServiceRoot::updateTitle() {
setTitle(TextFactory::extractUsernameFromEmail(m_network->username()) + QSL(" (Tiny Tiny RSS)"));
}
QString TtRssServiceRoot::username() const {
return m_network->username();
}
void TtRssServiceRoot::setUsername(const QString& username) {
m_network->setUsername(username);
}
QString TtRssServiceRoot::password() const {
return m_network->password();
}
void TtRssServiceRoot::setPassword(const QString& password) {
m_network->setPassword(password);
}
bool TtRssServiceRoot::authProtected() const {
return m_network->authIsUsed();
}
void TtRssServiceRoot::setAuthProtected(bool auth_protected) {
m_network->setAuthIsUsed(auth_protected);
}
QString TtRssServiceRoot::authUsername() const {
return m_network->authUsername();
}
void TtRssServiceRoot::setAuthUsername(const QString& auth_username) {
m_network->setAuthUsername(auth_username);
}
QString TtRssServiceRoot::authPassword() const {
return m_network->authPassword();
}
void TtRssServiceRoot::setAuthPassword(const QString& auth_password) {
m_network->setAuthPassword(auth_password);
}
QString TtRssServiceRoot::url() const {
return m_network->url();
}
void TtRssServiceRoot::setUrl(const QString& url) {
m_network->setUrl(url);
}
bool TtRssServiceRoot::forceUpdate() const {
return m_network->forceServerSideUpdate();
}
void TtRssServiceRoot::setForceUpdate(bool force_update) {
m_network->setForceServerSideUpdate(force_update);
}
bool TtRssServiceRoot::downloadOnlyUnread() const {
return m_network->downloadOnlyUnreadMessages();
}
void TtRssServiceRoot::setDownloadOnlyUnread(bool download_only_unread) {
m_network->setDownloadOnlyUnreadMessages(download_only_unread);
}
RootItem* TtRssServiceRoot::obtainNewTreeForSyncIn() const {
TtRssGetFeedsCategoriesResponse feed_cats = m_network->getFeedsCategories(networkProxy());
TtRssGetLabelsResponse labels = m_network->getLabels(networkProxy());

View File

@ -14,14 +14,6 @@ class TtRssNetworkFactory;
class TtRssServiceRoot : public ServiceRoot, public CacheForServiceRoot {
Q_OBJECT
Q_PROPERTY(QString username READ username WRITE setUsername)
Q_PROPERTY(QString password READ password WRITE setPassword)
Q_PROPERTY(bool auth_protected READ authProtected WRITE setAuthProtected)
Q_PROPERTY(QString auth_username READ authUsername WRITE setAuthUsername)
Q_PROPERTY(QString auth_password READ authPassword WRITE setAuthPassword)
Q_PROPERTY(QString url READ url WRITE setUrl)
Q_PROPERTY(bool force_update READ forceUpdate WRITE setForceUpdate)
Q_PROPERTY(bool download_only_unread READ downloadOnlyUnread WRITE setDownloadOnlyUnread)
public:
explicit TtRssServiceRoot(RootItem* parent = nullptr);
@ -39,41 +31,18 @@ class TtRssServiceRoot : public ServiceRoot, public CacheForServiceRoot {
virtual void addNewFeed(RootItem* selected_item, const QString& url = QString());
virtual QString additionalTooltip() const;
virtual void saveAllCachedData(bool ignore_errors);
virtual QList<CustomDatabaseEntry> customDatabaseAttributes() const;
virtual QVariantHash customDatabaseData() const;
virtual void setCustomDatabaseData(const QVariantHash& data) const;
// Access to network.
TtRssNetworkFactory* network() const;
void updateTitle();
// Support for dynamic DB attributes.
QString username() const;
void setUsername(const QString& username);
QString password() const;
void setPassword(const QString& password);
bool authProtected() const;
void setAuthProtected(bool auth_protected);
QString authUsername() const;
void setAuthUsername(const QString& auth_username);
QString authPassword() const;
void setAuthPassword(const QString& auth_password);
QString url() const;
void setUrl(const QString& url);
bool forceUpdate() const;
void setForceUpdate(bool force_update);
bool downloadOnlyUnread() const;
void setDownloadOnlyUnread(bool download_only_unread);
protected:
virtual RootItem* obtainNewTreeForSyncIn() const;
private:
void updateTitle();
private:
TtRssNetworkFactory* m_network;
};