working on unification of SQL logic

This commit is contained in:
Martin Rotter 2021-02-26 12:35:57 +01:00
parent 2c64bb1569
commit f2a8f63f91
16 changed files with 307 additions and 432 deletions

View File

@ -1,100 +1,44 @@
DROP TABLE IF EXISTS Information;
-- !
CREATE TABLE IF NOT EXISTS Information (
id INTEGER PRIMARY KEY,
inf_key TEXT NOT NULL,
inf_value TEXT NOT NULL
CREATE TABLE Information (
inf_key TEXT NOT NULL UNIQUE CHECK (inf_key != ''),
inf_value TEXT
);
-- !
INSERT INTO Information VALUES (1, 'schema_version', '1');
INSERT INTO Information VALUES ('schema_version', '1');
-- !
CREATE TABLE IF NOT EXISTS Accounts (
CREATE TABLE Accounts (
id INTEGER PRIMARY KEY,
type TEXT NOT NULL CHECK (type != ''),
proxy_type INTEGER NOT NULL CHECK (proxy_type >= 0) DEFAULT 0,
type TEXT NOT NULL CHECK (type != ''), /* ID of the account type. Each account defines its own, for example 'ttrss'. */
proxy_type INTEGER NOT NULL DEFAULT 0 CHECK (proxy_type >= 0),
proxy_host TEXT,
proxy_port INTEGER,
proxy_username TEXT,
proxy_password 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
);
-- !
CREATE TABLE IF NOT EXISTS TtRssAccounts (
id INTEGER,
username TEXT NOT NULL,
password TEXT,
auth_protected INTEGER(1) NOT NULL CHECK (auth_protected >= 0 AND auth_protected <= 1) DEFAULT 0,
auth_username TEXT,
auth_password TEXT,
url TEXT NOT NULL,
force_update INTEGER(1) NOT NULL CHECK (force_update >= 0 AND force_update <= 1) DEFAULT 0,
update_only_unread INTEGER(1) NOT NULL CHECK (update_only_unread >= 0 AND update_only_unread <= 1) DEFAULT 0,
FOREIGN KEY (id) REFERENCES Accounts (id)
);
-- !
CREATE TABLE IF NOT EXISTS OwnCloudAccounts (
id INTEGER,
username TEXT NOT NULL,
password TEXT,
url TEXT NOT NULL,
force_update INTEGER(1) NOT NULL CHECK (force_update >= 0 AND force_update <= 1) DEFAULT 0,
msg_limit INTEGER NOT NULL DEFAULT -1 CHECK (msg_limit >= -1),
update_only_unread INTEGER(1) NOT NULL CHECK (update_only_unread >= 0 AND update_only_unread <= 1) DEFAULT 0,
FOREIGN KEY (id) REFERENCES Accounts (id)
);
-- !
CREATE TABLE IF NOT EXISTS InoreaderAccounts (
id INTEGER,
username TEXT NOT NULL,
app_id TEXT,
app_key TEXT,
redirect_url TEXT,
refresh_token TEXT,
msg_limit INTEGER NOT NULL DEFAULT -1 CHECK (msg_limit >= -1),
FOREIGN KEY (id) REFERENCES Accounts (id)
);
-- !
CREATE TABLE IF NOT EXISTS GmailAccounts (
id INTEGER,
username TEXT NOT NULL,
app_id TEXT,
app_key TEXT,
redirect_url TEXT,
refresh_token TEXT,
msg_limit INTEGER NOT NULL DEFAULT -1 CHECK (msg_limit >= -1),
FOREIGN KEY (id) REFERENCES Accounts (id)
);
-- !
CREATE TABLE IF NOT EXISTS GoogleReaderApiAccounts (
id INTEGER,
type INTEGER NOT NULL CHECK (type >= 1),
username TEXT NOT NULL,
password TEXT,
url TEXT NOT NULL,
msg_limit INTEGER NOT NULL DEFAULT -1 CHECK (msg_limit >= -1),
FOREIGN KEY (id) REFERENCES Accounts (id)
);
-- !
CREATE TABLE IF NOT EXISTS FeedlyAccounts (
id INTEGER,
username TEXT NOT NULL,
developer_access_token TEXT,
refresh_token TEXT,
msg_limit INTEGER NOT NULL DEFAULT -1 CHECK (msg_limit >= -1),
update_only_unread INTEGER(1) NOT NULL DEFAULT 0 CHECK (update_only_unread >= 0 AND update_only_unread <= 1),
FOREIGN KEY (id) REFERENCES Accounts (id)
);
-- !
DROP TABLE IF EXISTS Categories;
-- !
CREATE TABLE IF NOT EXISTS Categories (
CREATE TABLE Categories (
id INTEGER PRIMARY KEY,
parent_id INTEGER NOT NULL,
parent_id INTEGER NOT NULL CHECK (parent_id >= -1), /* Root categories contain -1 here. */
title TEXT NOT NULL CHECK (title != ''),
description TEXT,
date_created INTEGER,
@ -105,15 +49,13 @@ CREATE TABLE IF NOT EXISTS Categories (
FOREIGN KEY (account_id) REFERENCES Accounts (id)
);
-- !
DROP TABLE IF EXISTS Feeds;
-- !
CREATE TABLE IF NOT EXISTS Feeds (
CREATE TABLE Feeds (
id INTEGER PRIMARY KEY,
title TEXT NOT NULL CHECK (title != ''),
description TEXT,
date_created INTEGER,
icon BLOB,
category INTEGER NOT NULL CHECK (category >= -1),
category INTEGER NOT NULL CHECK (category >= -1), /* Root feeds contain -1 here. */
encoding TEXT,
source_type INTEGER,
url TEXT,
@ -122,7 +64,7 @@ CREATE TABLE IF NOT EXISTS Feeds (
username TEXT,
password TEXT,
update_type INTEGER(1) NOT NULL CHECK (update_type >= 0),
update_interval INTEGER NOT NULL CHECK (update_interval >= 1) DEFAULT 15,
update_interval INTEGER NOT NULL DEFAULT 15 CHECK (update_interval >= 1),
type INTEGER,
account_id INTEGER NOT NULL,
custom_id TEXT,
@ -130,20 +72,18 @@ CREATE TABLE IF NOT EXISTS Feeds (
FOREIGN KEY (account_id) REFERENCES Accounts (id)
);
-- !
DROP TABLE IF EXISTS Messages;
-- !
CREATE TABLE IF NOT EXISTS Messages (
CREATE TABLE Messages (
id INTEGER PRIMARY KEY,
is_read INTEGER(1) NOT NULL CHECK (is_read >= 0 AND is_read <= 1) DEFAULT 0,
is_deleted INTEGER(1) NOT NULL CHECK (is_deleted >= 0 AND is_deleted <= 1) DEFAULT 0,
is_important INTEGER(1) NOT NULL CHECK (is_important >= 0 AND is_important <= 1) DEFAULT 0,
is_read INTEGER(1) NOT NULL DEFAULT 0 CHECK (is_read >= 0 AND is_read <= 1),
is_deleted INTEGER(1) NOT NULL DEFAULT 0 CHECK (is_deleted >= 0 AND is_deleted <= 1),
is_important INTEGER(1) NOT NULL DEFAULT 0 CHECK (is_important >= 0 AND is_important <= 1),
feed TEXT NOT NULL,
title TEXT NOT NULL CHECK (title != ''),
url TEXT,
author TEXT,
date_created INTEGER NOT NULL CHECK (date_created != 0),
contents TEXT,
is_pdeleted INTEGER(1) NOT NULL CHECK (is_pdeleted >= 0 AND is_pdeleted <= 1) DEFAULT 0,
is_pdeleted INTEGER(1) NOT NULL DEFAULT 0 CHECK (is_pdeleted >= 0 AND is_pdeleted <= 1),
enclosures TEXT,
account_id INTEGER NOT NULL,
custom_id TEXT,
@ -152,13 +92,13 @@ CREATE TABLE IF NOT EXISTS Messages (
FOREIGN KEY (account_id) REFERENCES Accounts (id)
);
-- !
CREATE TABLE IF NOT EXISTS MessageFilters (
CREATE TABLE MessageFilters (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL CHECK (name != ''),
script TEXT NOT NULL CHECK (script != '')
);
-- !
CREATE TABLE IF NOT EXISTS MessageFiltersInFeeds (
CREATE TABLE MessageFiltersInFeeds (
filter INTEGER NOT NULL,
feed_custom_id TEXT NOT NULL,
account_id INTEGER NOT NULL,
@ -167,7 +107,7 @@ CREATE TABLE IF NOT EXISTS MessageFiltersInFeeds (
FOREIGN KEY (account_id) REFERENCES Accounts (id) ON DELETE CASCADE
);
-- !
CREATE TABLE IF NOT EXISTS Labels (
CREATE TABLE Labels (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL CHECK (name != ''),
color VARCHAR(7),
@ -177,7 +117,7 @@ CREATE TABLE IF NOT EXISTS Labels (
FOREIGN KEY (account_id) REFERENCES Accounts (id)
);
-- !
CREATE TABLE IF NOT EXISTS LabelsInMessages (
CREATE TABLE LabelsInMessages (
label TEXT NOT NULL, /* Custom ID of label. */
message TEXT NOT NULL, /* Custom ID of message. */
account_id INTEGER NOT NULL,

View File

@ -6,7 +6,6 @@
#include "exceptions/applicationexception.h"
#include "miscellaneous/application.h"
#include "miscellaneous/iconfactory.h"
#include "miscellaneous/textfactory.h"
#include "network-web/oauth2service.h"
#include "services/abstract/category.h"
#include "services/feedly/definitions.h"
@ -1682,130 +1681,6 @@ void DatabaseQueries::fillBaseAccountData(const QSqlDatabase& db, ServiceRoot* a
}
}
QList<ServiceRoot*> DatabaseQueries::getGreaderAccounts(const QSqlDatabase& db, bool* ok) {
QSqlQuery query(db);
QList<ServiceRoot*> roots;
if (query.exec("SELECT * FROM GoogleReaderApiAccounts;")) {
while (query.next()) {
auto* root = new GreaderServiceRoot();
root->setId(query.value(0).toInt());
root->setAccountId(query.value(0).toInt());
root->network()->setService(GreaderServiceRoot::Service(query.value(1).toInt()));
root->network()->setUsername(query.value(2).toString());
root->network()->setPassword(TextFactory::decrypt(query.value(3).toString()));
root->network()->setBaseUrl(query.value(4).toString());
root->network()->setBatchSize(query.value(5).toInt());
root->updateTitleIcon();
fillBaseAccountData(db, root);
roots.append(root);
}
if (ok != nullptr) {
*ok = true;
}
}
else {
qWarningNN << LOGSEC_GREADER
<< "Getting list of activated accounts failed: '"
<< query.lastError().text()
<< "'.";
if (ok != nullptr) {
*ok = false;
}
}
return roots;
}
QList<ServiceRoot*> DatabaseQueries::getOwnCloudAccounts(const QSqlDatabase& db, bool* ok) {
QSqlQuery query(db);
QList<ServiceRoot*> roots;
if (query.exec("SELECT * FROM OwnCloudAccounts;")) {
while (query.next()) {
auto* root = new OwnCloudServiceRoot();
root->setId(query.value(0).toInt());
root->setAccountId(query.value(0).toInt());
root->network()->setAuthUsername(query.value(1).toString());
root->network()->setAuthPassword(TextFactory::decrypt(query.value(2).toString()));
root->network()->setUrl(query.value(3).toString());
root->network()->setForceServerSideUpdate(query.value(4).toBool());
root->network()->setBatchSize(query.value(5).toInt());
root->network()->setDownloadOnlyUnreadMessages(query.value(6).toBool());
root->updateTitle();
fillBaseAccountData(db, root);
roots.append(root);
}
if (ok != nullptr) {
*ok = true;
}
}
else {
qWarningNN << LOGSEC_NEXTCLOUD
<< "Getting list of activated accounts failed: '"
<< query.lastError().text()
<< "'.";
if (ok != nullptr) {
*ok = false;
}
}
return roots;
}
QList<ServiceRoot*> DatabaseQueries::getTtRssAccounts(const QSqlDatabase& db, bool* ok) {
QSqlQuery query(db);
QList<ServiceRoot*> roots;
if (query.exec("SELECT * FROM TtRssAccounts;")) {
while (query.next()) {
auto* root = new TtRssServiceRoot();
root->setId(query.value(0).toInt());
root->setAccountId(query.value(0).toInt());
root->network()->setUsername(query.value(1).toString());
root->network()->setPassword(TextFactory::decrypt(query.value(2).toString()));
root->network()->setAuthIsUsed(query.value(3).toBool());
root->network()->setAuthUsername(query.value(4).toString());
root->network()->setAuthPassword(TextFactory::decrypt(query.value(5).toString()));
root->network()->setUrl(query.value(6).toString());
root->network()->setForceServerSideUpdate(query.value(7).toBool());
root->network()->setDownloadOnlyUnreadMessages(query.value(8).toBool());
root->updateTitle();
fillBaseAccountData(db, root);
roots.append(root);
}
if (ok != nullptr) {
*ok = true;
}
}
else {
qWarningNN << LOGSEC_TTRSS
<< "Getting list of activated accounts failed: '"
<< query.lastError().text()
<< "'.";
if (ok != nullptr) {
*ok = false;
}
}
return roots;
}
bool DatabaseQueries::deleteOwnCloudAccount(const QSqlDatabase& db, int account_id) {
QSqlQuery q(db);
@ -1978,6 +1853,63 @@ bool DatabaseQueries::createOwnCloudAccount(const QSqlDatabase& db, int id_to_as
}
}
void DatabaseQueries::createOverwriteAccount(const QSqlDatabase& db, ServiceRoot* account) {
QSqlQuery q(db);
if (account->accountId() <= 0) {
// We need to insert account first.
q.prepare(QSL("INSERT INTO Accounts (type) VALUES (:type);"));
q.bindValue(QSL(":type"), account->code());
if (!q.exec()) {
throw ApplicationException(q.lastError().text());
}
else {
account->setId(q.lastInsertId().toInt());
account->setAccountId(account->id());
}
}
// 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.bindValue(QSL(":proxy_type"), proxy.type());
q.bindValue(QSL(":proxy_host"), proxy.hostName());
q.bindValue(QSL(":proxy_port"), proxy.port());
q.bindValue(QSL(":proxy_username"), proxy.user());
q.bindValue(QSL(":proxy_password"), TextFactory::encrypt(proxy.password()));
q.bindValue(QSL(":id"), account->accountId());
if (!q.exec()) {
throw ApplicationException(q.lastError().text());
}
}
int DatabaseQueries::createBaseAccount(const QSqlDatabase& db, const QString& code, bool* ok) {
QSqlQuery q(db);
@ -2478,38 +2410,6 @@ void DatabaseQueries::removeMessageFilterFromFeed(const QSqlDatabase& db, const
}
}
QList<ServiceRoot*> DatabaseQueries::getStandardAccounts(const QSqlDatabase& db, bool* ok) {
QSqlQuery q(db);
QList<ServiceRoot*> roots;
q.setForwardOnly(true);
q.prepare(QSL("SELECT id FROM Accounts WHERE type = :type;"));
q.bindValue(QSL(":type"), SERVICE_CODE_STD_RSS);
if (q.exec()) {
while (q.next()) {
auto* root = new StandardServiceRoot();
root->setAccountId(q.value(0).toInt());
fillBaseAccountData(db, root);
roots.append(root);
}
if (ok != nullptr) {
*ok = true;
}
}
else {
if (ok != nullptr) {
*ok = false;
}
}
return roots;
}
bool DatabaseQueries::deleteFeedlyAccount(const QSqlDatabase& db, int account_id) {
QSqlQuery q(db);
@ -2624,91 +2524,6 @@ QStringList DatabaseQueries::getAllRecipients(const QSqlDatabase& db, int accoun
return rec;
}
QList<ServiceRoot*> DatabaseQueries::getFeedlyAccounts(const QSqlDatabase& db, bool* ok) {
QSqlQuery query(db);
QList<ServiceRoot*> roots;
if (query.exec("SELECT * FROM FeedlyAccounts;")) {
while (query.next()) {
auto* root = new FeedlyServiceRoot();
root->setId(query.value(0).toInt());
root->setAccountId(query.value(0).toInt());
root->network()->setUsername(query.value(1).toString());
root->network()->setDeveloperAccessToken(query.value(2).toString());
#if defined(FEEDLY_OFFICIAL_SUPPORT)
root->network()->oauth()->setRefreshToken(query.value(3).toString());
#endif
root->network()->setBatchSize(query.value(4).toInt());
root->network()->setDownloadOnlyUnreadMessages(query.value(5).toBool());
root->updateTitle();
fillBaseAccountData(db, root);
roots.append(root);
}
if (ok != nullptr) {
*ok = true;
}
}
else {
qWarningNN << LOGSEC_GMAIL
<< "Getting list of activated accounts failed: '"
<< query.lastError().text()
<< "'.";
if (ok != nullptr) {
*ok = false;
}
}
return roots;
}
QList<ServiceRoot*> DatabaseQueries::getGmailAccounts(const QSqlDatabase& db, bool* ok) {
QSqlQuery query(db);
QList<ServiceRoot*> roots;
if (query.exec("SELECT * FROM GmailAccounts;")) {
while (query.next()) {
auto* root = new GmailServiceRoot();
root->setId(query.value(0).toInt());
root->setAccountId(query.value(0).toInt());
root->network()->setUsername(query.value(1).toString());
root->network()->oauth()->setClientId(query.value(2).toString());
root->network()->oauth()->setClientSecret(query.value(3).toString());
root->network()->oauth()->setRefreshToken(query.value(5).toString());
root->network()->oauth()->setRedirectUrl(query.value(4).toString());
root->network()->setBatchSize(query.value(6).toInt());
root->updateTitle();
fillBaseAccountData(db, root);
roots.append(root);
}
if (ok != nullptr) {
*ok = true;
}
}
else {
qWarningNN << LOGSEC_GMAIL
<< "Getting list of activated accounts failed: '"
<< query.lastError().text()
<< "'.";
if (ok != nullptr) {
*ok = false;
}
}
return roots;
}
bool DatabaseQueries::deleteGmailAccount(const QSqlDatabase& db, int account_id) {
QSqlQuery q(db);
@ -2747,47 +2562,6 @@ bool DatabaseQueries::storeNewOauthTokens(const QSqlDatabase& db, const QString&
}
}
QList<ServiceRoot*> DatabaseQueries::getInoreaderAccounts(const QSqlDatabase& db, bool* ok) {
QSqlQuery query(db);
QList<ServiceRoot*> roots;
if (query.exec("SELECT * FROM InoreaderAccounts;")) {
while (query.next()) {
auto* root = new InoreaderServiceRoot();
root->setId(query.value(0).toInt());
root->setAccountId(query.value(0).toInt());
root->network()->setUsername(query.value(1).toString());
root->network()->oauth()->setClientId(query.value(2).toString());
root->network()->oauth()->setClientSecret(query.value(3).toString());
root->network()->oauth()->setRefreshToken(query.value(5).toString());
root->network()->oauth()->setRedirectUrl(query.value(4).toString());
root->network()->setBatchSize(query.value(6).toInt());
root->updateTitle();
fillBaseAccountData(db, root);
roots.append(root);
}
if (ok != nullptr) {
*ok = true;
}
}
else {
qWarningNN << LOGSEC_INOREADER
<< "Getting list of activated accounts failed: '"
<< query.lastError().text()
<< "'.";
if (ok != nullptr) {
*ok = false;
}
}
return roots;
}
bool DatabaseQueries::overwriteGmailAccount(const QSqlDatabase& db, const QString& username, const QString& app_id,
const QString& app_key, const QString& redirect_url,
const QString& refresh_token, int batch_size, int account_id) {

View File

@ -6,6 +6,7 @@
#include "services/abstract/rootitem.h"
#include "core/messagefilter.h"
#include "miscellaneous/textfactory.h"
#include "services/abstract/category.h"
#include "services/abstract/label.h"
#include "services/abstract/serviceroot.h"
@ -89,9 +90,12 @@ class DatabaseQueries {
bool* ok = nullptr);
// Common account methods.
template<typename T>
static QList<ServiceRoot*> getAccounts(const QSqlDatabase& db, const QString& code, bool* ok = nullptr);
static bool storeNewOauthTokens(const QSqlDatabase& db, const QString& table_name,
const QString& refresh_token, int account_id);
static void fillBaseAccountData(const QSqlDatabase& db, ServiceRoot* account, bool* ok = nullptr);
static void createOverwriteAccount(const QSqlDatabase& db, ServiceRoot* account);
static int createBaseAccount(const QSqlDatabase& db, const QString& code, bool* ok = nullptr);
static void editBaseAccount(const QSqlDatabase& db, ServiceRoot* account, bool* ok = nullptr);
static int updateMessages(QSqlDatabase db, const QList<Message>& messages, const QString& feed_custom_id,
@ -147,14 +151,12 @@ class DatabaseQueries {
const QString& username, const QString& password, Feed::AutoUpdateType auto_update_type,
int auto_update_interval, StandardFeed::SourceType source_type,
const QString& post_process_script, StandardFeed::Type feed_format);
static QList<ServiceRoot*> getStandardAccounts(const QSqlDatabase& db, bool* ok = nullptr);
template<typename T>
static void fillFeedData(T* feed, const QSqlRecord& sql_record);
// Feedly account.
static bool deleteFeedlyAccount(const QSqlDatabase& db, int account_id);
static QList<ServiceRoot*> getFeedlyAccounts(const QSqlDatabase& db, bool* ok = nullptr);
static bool createFeedlyAccount(const QSqlDatabase& db,
const QString& username,
const QString& developer_access_token,
@ -172,7 +174,6 @@ class DatabaseQueries {
// Greader account.
static bool deleteGreaderAccount(const QSqlDatabase& db, int account_id);
static QList<ServiceRoot*> getGreaderAccounts(const QSqlDatabase& db, bool* ok = nullptr);
static bool createGreaderAccount(const QSqlDatabase& db, int id_to_assign, const QString& username,
const QString& password, GreaderServiceRoot::Service service,
const QString& url, int batch_size);
@ -181,7 +182,6 @@ class DatabaseQueries {
int account_id);
// Nextcloud account.
static QList<ServiceRoot*> getOwnCloudAccounts(const QSqlDatabase& db, bool* ok = nullptr);
static bool deleteOwnCloudAccount(const QSqlDatabase& db, int account_id);
static bool overwriteOwnCloudAccount(const QSqlDatabase& db, const QString& username, const QString& password,
const QString& url, bool force_server_side_feed_update, int batch_size,
@ -191,7 +191,6 @@ class DatabaseQueries {
bool download_only_unread_messages, int batch_size);
// TT-RSS acccount.
static QList<ServiceRoot*> getTtRssAccounts(const QSqlDatabase& db, bool* ok = nullptr);
static bool deleteTtRssAccount(const QSqlDatabase& db, int account_id);
static bool overwriteTtRssAccount(const QSqlDatabase& db, const QString& username, const QString& password,
bool auth_protected, const QString& auth_username, const QString& auth_password,
@ -205,7 +204,6 @@ class DatabaseQueries {
// Gmail account.
static QStringList getAllRecipients(const QSqlDatabase& db, int account_id);
static bool deleteGmailAccount(const QSqlDatabase& db, int account_id);
static QList<ServiceRoot*> getGmailAccounts(const QSqlDatabase& db, bool* ok = nullptr);
static bool overwriteGmailAccount(const QSqlDatabase& db, const QString& username, const QString& app_id,
const QString& app_key, const QString& redirect_url, const QString& refresh_token,
int batch_size, int account_id);
@ -215,7 +213,6 @@ class DatabaseQueries {
// Inoreader account.
static bool deleteInoreaderAccount(const QSqlDatabase& db, int account_id);
static QList<ServiceRoot*> getInoreaderAccounts(const QSqlDatabase& db, bool* ok = nullptr);
static bool overwriteInoreaderAccount(const QSqlDatabase& db, const QString& username, const QString& app_id,
const QString& app_key, const QString& redirect_url, const QString& refresh_token,
int batch_size, int account_id);
@ -241,6 +238,63 @@ inline void DatabaseQueries::fillFeedData(StandardFeed* feed, const QSqlRecord&
Q_UNUSED(sql_record)
}
template<typename T>
QList<ServiceRoot*> DatabaseQueries::getAccounts(const QSqlDatabase& db, const QString& code, bool* ok) {
QSqlQuery query(db);
QList<ServiceRoot*> roots;
if (query.exec(QSL("SELECT * FROM Accounts WHERE type = '%1';").arg(code))) {
while (query.next()) {
ServiceRoot* root = new T();
// Load common data.
root->setId(query.value(QSL("id")).toInt());
root->setAccountId(root->id());
QNetworkProxy proxy(QNetworkProxy::ProxyType(query.value(QSL("proxy_type")).toInt()),
query.value(QSL("proxy_host")).toString(),
query.value(QSL("proxy_port")).toInt(),
query.value(QSL("proxy_username")).toString(),
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);
}
roots.append(root);
}
if (ok != nullptr) {
*ok = true;
}
}
else {
qWarningNN << LOGSEC_DB
<< "Loading of accounts with code"
<< QUOTE_W_SPACE(code)
<< "failed with error:"
<< QUOTE_W_SPACE_DOT(query.lastError().text());
if (ok != nullptr) {
*ok = false;
}
}
return roots;
}
template<typename T>
Assignment DatabaseQueries::getCategories(const QSqlDatabase& db, int account_id, bool* ok) {
Assignment categories;

View File

@ -21,14 +21,16 @@ FormAccountDetails::FormAccountDetails(const QIcon& icon, QWidget* parent)
void FormAccountDetails::apply() {
QSqlDatabase database = qApp->database()->connection(QSL("FormAccountDetails"));
if (m_creatingNew) {
m_account->setAccountId(DatabaseQueries::createBaseAccount(database, m_account->code()));
}
/*
if (m_creatingNew) {
m_account->setAccountId(DatabaseQueries::createBaseAccount(database, m_account->code()));
}
*/
m_account->setNetworkProxy(m_proxyDetails->proxy());
// NOTE: We edit account common attributes here directly.
DatabaseQueries::editBaseAccount(database, m_account);
//DatabaseQueries::editBaseAccount(database, m_account);
}
void FormAccountDetails::insertCustomTab(QWidget* custom_tab, const QString& title, int index) {

View File

@ -256,6 +256,10 @@ ServiceRoot::LabelOperation ServiceRoot::supportedLabelOperations() const {
return LabelOperation::Adding | LabelOperation::Editing | LabelOperation::Deleting;
}
QList<CustomDatabaseEntry> ServiceRoot::customDatabaseAttributes() const {
return {};
}
void ServiceRoot::itemChanged(const QList<RootItem*>& items) {
emit dataChanged(items);
}
@ -393,7 +397,9 @@ void ServiceRoot::syncIn() {
requestItemExpand({ this }, true);
}
void ServiceRoot::performInitialAssembly(const Assignment& categories, const Assignment& feeds, const QList<Label*>& labels) {
void ServiceRoot::performInitialAssembly(const Assignment& categories,
const Assignment& feeds,
const QList<Label*>& labels) {
// All data are now obtained, lets create the hierarchy.
assembleCategories(categories);
assembleFeeds(feeds);
@ -401,10 +407,12 @@ void ServiceRoot::performInitialAssembly(const Assignment& categories, const Ass
// As the last item, add recycle bin, which is needed.
appendChild(recycleBin());
appendChild(importantNode());
appendChild(labelsNode());
labelsNode()->loadLabels(labels);
requestItemExpand({ labelsNode() }, true);
if (labelsNode() != nullptr) {
appendChild(labelsNode());
labelsNode()->loadLabels(labels);
requestItemExpand({ labelsNode() }, true);
}
updateCounts(true);
}

View File

@ -24,6 +24,14 @@ typedef QList<QPair<int, RootItem*>> Assignment;
typedef QPair<int, RootItem*> AssignmentItem;
typedef QPair<Message, RootItem::Importance> ImportanceChange;
struct CustomDatabaseEntry {
public:
CustomDatabaseEntry(const QString& name, bool encrypted = false) : m_name(name), m_encrypted(encrypted) {}
QString m_name;
bool m_encrypted;
};
// THIS IS the root node of the service.
// NOTE: The root usually contains some core functionality of the
// service like service account username/password etc.
@ -53,6 +61,7 @@ class ServiceRoot : public RootItem {
virtual bool supportsFeedAdding() const;
virtual bool supportsCategoryAdding() const;
virtual LabelOperation supportedLabelOperations() const;
virtual QList<CustomDatabaseEntry> customDatabaseAttributes() const;
// Returns list of specific actions for "Add new item" main window menu.
// So typical list of returned actions could look like:

View File

@ -19,7 +19,7 @@ ServiceRoot* FeedlyEntryPoint::createNewRoot() const {
QList<ServiceRoot*> FeedlyEntryPoint::initializeSubtree() const {
QSqlDatabase database = qApp->database()->connection(QSL("FeedlyEntryPoint"));
return DatabaseQueries::getFeedlyAccounts(database);
return DatabaseQueries::getAccounts<FeedlyServiceRoot>(database, code());
}
QString FeedlyEntryPoint::name() const {

View File

@ -21,7 +21,7 @@ ServiceRoot* GmailEntryPoint::createNewRoot() const {
QList<ServiceRoot*> GmailEntryPoint::initializeSubtree() const {
QSqlDatabase database = qApp->database()->connection(QSL("GmailEntryPoint"));
return DatabaseQueries::getGmailAccounts(database);
return DatabaseQueries::getAccounts<GmailServiceRoot>(database, code());
}
QString GmailEntryPoint::name() const {

View File

@ -19,7 +19,7 @@ ServiceRoot* GreaderEntryPoint::createNewRoot() const {
QList<ServiceRoot*> GreaderEntryPoint::initializeSubtree() const {
QSqlDatabase database = qApp->database()->connection(QSL("GreaderEntryPoint"));
return DatabaseQueries::getGreaderAccounts(database);
return DatabaseQueries::getAccounts<GreaderServiceRoot>(database, code());
}
QString GreaderEntryPoint::name() const {

View File

@ -22,7 +22,7 @@ ServiceRoot* InoreaderEntryPoint::createNewRoot() const {
QList<ServiceRoot*> InoreaderEntryPoint::initializeSubtree() const {
QSqlDatabase database = qApp->database()->connection(QSL("InoreaderEntryPoint"));
return DatabaseQueries::getInoreaderAccounts(database);
return DatabaseQueries::getAccounts<InoreaderServiceRoot>(database, code());
}
QString InoreaderEntryPoint::name() const {

View File

@ -19,7 +19,7 @@ ServiceRoot* OwnCloudServiceEntryPoint::createNewRoot() const {
QList<ServiceRoot*> OwnCloudServiceEntryPoint::initializeSubtree() const {
QSqlDatabase database = qApp->database()->connection(QSL("OwnCloudServiceEntryPoint"));
return DatabaseQueries::getOwnCloudAccounts(database);
return DatabaseQueries::getAccounts<OwnCloudServiceRoot>(database, code());
}
QString OwnCloudServiceEntryPoint::name() const {

View File

@ -38,5 +38,5 @@ QList<ServiceRoot*> StandardServiceEntryPoint::initializeSubtree() const {
// Check DB if standard account is enabled.
QSqlDatabase database = qApp->database()->connection(QSL("StandardServiceEntryPoint"));
return DatabaseQueries::getStandardAccounts(database);
return DatabaseQueries::getAccounts<StandardServiceRoot>(database, code());
}

View File

@ -30,7 +30,7 @@ void FormEditTtRssAccount::apply() {
account<TtRssServiceRoot>()->network()->setForceServerSideUpdate(m_details->m_ui.m_checkServerSideUpdate->isChecked());
account<TtRssServiceRoot>()->network()->setDownloadOnlyUnreadMessages(m_details->m_ui.m_checkDownloadOnlyUnreadMessages->isChecked());
account<TtRssServiceRoot>()->saveAccountDataToDatabase(m_creatingNew);
account<TtRssServiceRoot>()->saveAccountDataToDatabase();
accept();
if (!m_creatingNew) {

View File

@ -42,5 +42,5 @@ QList<ServiceRoot*> TtRssServiceEntryPoint::initializeSubtree() const {
// Check DB if standard account is enabled.
QSqlDatabase database = qApp->database()->connection(QSL("TtRssServiceEntryPoint"));
return DatabaseQueries::getTtRssAccounts(database);
return DatabaseQueries::getAccounts<TtRssServiceRoot>(database, code());
}

View File

@ -42,6 +42,8 @@ void TtRssServiceRoot::start(bool freshly_activated) {
loadCacheFromFile();
}
updateTitle();
if (getSubTreeFeeds().isEmpty()) {
syncIn();
}
@ -200,6 +202,13 @@ 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") }
};
}
QString TtRssServiceRoot::additionalTooltip() const {
return tr("Username: %1\nServer: %2\n"
"Last error: %3\nLast login on: %4").arg(m_network->username(),
@ -214,29 +223,10 @@ TtRssNetworkFactory* TtRssServiceRoot::network() const {
return m_network;
}
void TtRssServiceRoot::saveAccountDataToDatabase(bool creating_new) {
void TtRssServiceRoot::saveAccountDataToDatabase() {
QSqlDatabase database = qApp->database()->connection(metaObject()->className());
if (!creating_new) {
// We are overwritting previously saved data.
if (DatabaseQueries::overwriteTtRssAccount(database, m_network->username(), m_network->password(),
m_network->authIsUsed(), m_network->authUsername(),
m_network->authPassword(), m_network->url(),
m_network->forceServerSideUpdate(), m_network->downloadOnlyUnreadMessages(),
accountId())) {
updateTitle();
itemChanged(QList<RootItem*>() << this);
}
}
else {
if (DatabaseQueries::createTtRssAccount(database, accountId(), m_network->username(),
m_network->password(), m_network->authIsUsed(),
m_network->authUsername(), m_network->authPassword(),
m_network->url(), m_network->forceServerSideUpdate(),
m_network->downloadOnlyUnreadMessages())) {
updateTitle();
}
}
DatabaseQueries::createOverwriteAccount(database, this);
}
void TtRssServiceRoot::loadFromDatabase() {
@ -255,7 +245,71 @@ void TtRssServiceRoot::updateTitle() {
host = m_network->url();
}
setTitle(m_network->username() + QSL(" (Tiny Tiny RSS)"));
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 {

View File

@ -14,6 +14,14 @@ 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);
@ -33,13 +41,39 @@ 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;
// Access to network.
TtRssNetworkFactory* network() const;
void saveAccountDataToDatabase(bool creating_new);
void saveAccountDataToDatabase();
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;