OwnCloud plugin can delete feeds.

This commit is contained in:
Martin Rotter 2016-04-11 08:53:21 +02:00
parent 75fb8fa9b1
commit 0b2cb88bf6
12 changed files with 133 additions and 48 deletions

View File

@ -8,6 +8,7 @@ Main:
Added:
▪ ownCloud plugin now can delete feeds.
▪ Global auto-update feed interval spinbox now has better format. (issue #176)
▪ Message preview's font is now fully adjustable in settings. (issue #177)
▪ RSS Guard now automatically switches to SQLite backend if MySQL is not available on program startup.

View File

@ -975,5 +975,26 @@ Assignment DatabaseQueries::getOwnCloudFeeds(QSqlDatabase db, int account_id, bo
return feeds;
}
bool DatabaseQueries::deleteFeed(QSqlDatabase db, int feed_custom_id, int account_id) {
QSqlQuery query_remove(db);
query_remove.setForwardOnly(true);
// Remove all messages from this feed.
query_remove.prepare(QSL("DELETE FROM Messages WHERE feed = :feed AND account_id = :account_id;"));
query_remove.bindValue(QSL(":feed"), feed_custom_id);
query_remove.bindValue(QSL(":account_id"), account_id);
if (!query_remove.exec()) {
return false;
}
// Remove feed itself.
query_remove.prepare(QSL("DELETE FROM Feeds WHERE custom_id = :feed AND account_id = :account_id;"));
query_remove.bindValue(QSL(":feed"), feed_custom_id);
query_remove.bindValue(QSL(":account_id"), account_id);
return query_remove.exec();
}
DatabaseQueries::DatabaseQueries() {
}

View File

@ -68,6 +68,7 @@ class DatabaseQueries {
static int createAccount(QSqlDatabase db, const QString &code, bool *ok = NULL);
static Assignment getOwnCloudCategories(QSqlDatabase db, int account_id, bool *ok = NULL);
static Assignment getOwnCloudFeeds(QSqlDatabase db, int account_id, bool *ok = NULL);
static bool deleteFeed(QSqlDatabase db, int feed_custom_id, int account_id);
private:
explicit DatabaseQueries();

View File

@ -39,33 +39,12 @@ Downloader::~Downloader() {
void Downloader::downloadFile(const QString &url, int timeout, bool protected_contents, const QString &username,
const QString &password) {
QNetworkRequest request;
QString non_const_url = url;
foreach (const QByteArray &header_name, m_customHeaders.keys()) {
request.setRawHeader(header_name, m_customHeaders.value(header_name));
}
// Set url for this request and fire it up.
m_timer->setInterval(timeout);
if (non_const_url.startsWith(URI_SCHEME_FEED)) {
qDebug("Replacing URI schemes for '%s'.", qPrintable(non_const_url));
request.setUrl(non_const_url.replace(QRegExp(QString('^') + URI_SCHEME_FEED), QString(URI_SCHEME_HTTP)));
}
else {
request.setUrl(non_const_url);
}
m_targetProtected = protected_contents;
m_targetUsername = username;
m_targetPassword = password;
runGetRequest(request);
manipulateData(url, QNetworkAccessManager::GetOperation, QByteArray(), timeout,
protected_contents, username, password);
}
void Downloader::uploadData(const QString &url, const QByteArray &data, QNetworkAccessManager::Operation operation,
int timeout, bool protected_contents, const QString &username, const QString &password) {
void Downloader::manipulateData(const QString &url, QNetworkAccessManager::Operation operation, const QByteArray &data,
int timeout, bool protected_contents, const QString &username, const QString &password) {
QNetworkRequest request;
QString non_const_url = url;
@ -93,9 +72,15 @@ void Downloader::uploadData(const QString &url, const QByteArray &data, QNetwork
if (operation == QNetworkAccessManager::PostOperation) {
runPostRequest(request, m_inputData);
}
else {
else if (operation == QNetworkAccessManager::GetOperation) {
runGetRequest(request);
}
else if (operation == QNetworkAccessManager::PutOperation) {
runPutRequest(request, m_inputData);
}
else if (operation == QNetworkAccessManager::DeleteOperation) {
runDeleteRequest(request);
}
}
void Downloader::finished() {
@ -131,6 +116,9 @@ void Downloader::finished() {
else if (reply_operation == QNetworkAccessManager::PutOperation) {
runPutRequest(request, m_inputData);
}
else if (reply_operation == QNetworkAccessManager::DeleteOperation) {
runDeleteRequest(request);
}
}
else {
// No redirection is indicated. Final file is obtained in our "reply" object.
@ -161,6 +149,18 @@ void Downloader::timeout() {
}
}
void Downloader::runDeleteRequest(const QNetworkRequest &request) {
m_timer->start();
m_activeReply = m_downloadManager->deleteResource(request);
m_activeReply->setProperty("protected", m_targetProtected);
m_activeReply->setProperty("username", m_targetUsername);
m_activeReply->setProperty("password", m_targetPassword);
connect(m_activeReply, SIGNAL(downloadProgress(qint64,qint64)), this, SLOT(progressInternal(qint64,qint64)));
connect(m_activeReply, SIGNAL(finished()), this, SLOT(finished()));
}
void Downloader::runPutRequest(const QNetworkRequest &request, const QByteArray &data) {
m_timer->start();
m_activeReply = m_downloadManager->put(request, data);

View File

@ -52,9 +52,10 @@ class Downloader : public QObject {
// Performs asynchronous upload of given data as HTTP POST.
// User needs to setup "Content-Encoding" header which
// matches encoding of the data.
void uploadData(const QString &url, const QByteArray &data, QNetworkAccessManager::Operation operation,
int timeout = DOWNLOAD_TIMEOUT, bool protected_contents = false,
const QString &username = QString(), const QString &password = QString());
void manipulateData(const QString &url, QNetworkAccessManager::Operation operation,
const QByteArray &data = QByteArray(),
int timeout = DOWNLOAD_TIMEOUT, bool protected_contents = false,
const QString &username = QString(), const QString &password = QString());
signals:
// Emitted when new progress is known.
@ -72,6 +73,7 @@ class Downloader : public QObject {
void timeout();
private:
void runDeleteRequest(const QNetworkRequest &request);
void runPutRequest(const QNetworkRequest &request, const QByteArray &data);
void runPostRequest(const QNetworkRequest &request, const QByteArray &data);
void runGetRequest(const QNetworkRequest &request);

View File

@ -167,8 +167,7 @@ NetworkResult NetworkFactory::uploadData(const QString &url, int timeout, const
// We need to quit event loop when the download finishes.
QObject::connect(&downloader, SIGNAL(completed(QNetworkReply::NetworkError)), &loop, SLOT(quit()));
downloader.uploadData(url, input_data, operation,
timeout, protected_contents, username, password);
downloader.manipulateData(url, operation, input_data, timeout, protected_contents, username, password);
loop.exec();
output = downloader.lastOutputData();
result.first = downloader.lastOutputError();
@ -227,3 +226,31 @@ NetworkResult NetworkFactory::downloadFile(const QString &url, int timeout,
return result;
}
NetworkResult NetworkFactory::deleteResource(const QString &url, int timeout, bool protected_contents,
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()));
downloader.manipulateData(url, QNetworkAccessManager::DeleteOperation, QByteArray(),
timeout, protected_contents, username, password);
loop.exec();
result.first = downloader.lastOutputError();
result.second = downloader.lastContentType();
return result;
}

View File

@ -58,6 +58,10 @@ class NetworkFactory {
static NetworkResult downloadFile(const QString &url, int timeout, QByteArray &output,
bool protected_contents = false, const QString &username = QString(),
const QString &password = QString(), bool set_basic_header = false);
static NetworkResult deleteResource(const QString &url, int timeout,
bool protected_contents = false, const QString &username = QString(),
const QString &password = QString(), bool set_basic_header = false);
};
#endif // NETWORKFACTORY_H

View File

@ -33,7 +33,7 @@ OwnCloudNetworkFactory::OwnCloudNetworkFactory()
: m_url(QString()), m_fixedUrl(QString()), m_forceServerSideUpdate(false),
m_authUsername(QString()), m_authPassword(QString()), m_urlUser(QString()), m_urlStatus(QString()),
m_urlFolders(QString()), m_urlFeeds(QString()), m_urlMessages(QString()), m_urlFeedsUpdate(QString()),
m_userId(QString()) {
m_urlDeleteFeed(QString()), m_userId(QString()) {
}
OwnCloudNetworkFactory::~OwnCloudNetworkFactory() {
@ -60,6 +60,7 @@ void OwnCloudNetworkFactory::setUrl(const QString &url) {
m_urlFeeds = m_fixedUrl + API_PATH + "feeds";
m_urlMessages = m_fixedUrl + API_PATH + "items?id=%1&batchSize=%2&type=%3";
m_urlFeedsUpdate = m_fixedUrl + API_PATH + "feeds/update?userId=%1&feedId=%2";
m_urlDeleteFeed = m_fixedUrl + API_PATH + "feeds/%1";
setUserId(QString());
}
@ -168,6 +169,24 @@ OwnCloudGetFeedsCategoriesResponse OwnCloudNetworkFactory::feedsCategories() {
return OwnCloudGetFeedsCategoriesResponse(content_categories, content_feeds);
}
bool OwnCloudNetworkFactory::deleteFeed(int feed_id) {
QString final_url = m_urlDeleteFeed.arg(QString::number(feed_id));
NetworkResult network_reply = NetworkFactory::deleteResource(final_url,
qApp->settings()->value(GROUP(Feeds),
SETTING(Feeds::UpdateTimeout)).toInt(),
true, m_authUsername, m_authPassword, true);
m_lastError = network_reply.first;
if (network_reply.first != QNetworkReply::NoError) {
qWarning("ownCloud: Obtaining of categories failed with error %d.", network_reply.first);
return false;
}
else {
return true;
}
}
OwnCloudGetMessagesResponse OwnCloudNetworkFactory::getMessages(int feed_id) {
if (forceServerSideUpdate()) {
triggerFeedUpdate(feed_id);

View File

@ -119,6 +119,9 @@ class OwnCloudNetworkFactory {
// Get feeds & categories (used for sync-in).
OwnCloudGetFeedsCategoriesResponse feedsCategories();
// Delete a feed.
bool deleteFeed(int feed_id);
// Get messages for given feed.
OwnCloudGetMessagesResponse getMessages(int feed_id);
@ -143,6 +146,7 @@ class OwnCloudNetworkFactory {
QString m_urlFeeds;
QString m_urlMessages;
QString m_urlFeedsUpdate;
QString m_urlDeleteFeed;
QString m_userId;
};

View File

@ -18,6 +18,7 @@
#include "services/owncloud/owncloudfeed.h"
#include "miscellaneous/iconfactory.h"
#include "miscellaneous/databasequeries.h"
#include "services/owncloud/owncloudserviceroot.h"
#include "services/owncloud/network/owncloudnetworkfactory.h"
#include "services/owncloud/gui/formeditowncloudfeed.h"
@ -53,6 +54,23 @@ bool OwnCloudFeed::editViaGui() {
return false;
}
bool OwnCloudFeed::canBeDeleted() const {
return true;
}
bool OwnCloudFeed::deleteViaGui() {
QSqlDatabase database = qApp->database()->connection(metaObject()->className(), DatabaseFactory::FromSettings);
if (serviceRoot()->network()->deleteFeed(customId()) &&
DatabaseQueries::deleteFeed(database, customId(), serviceRoot()->accountId())) {
serviceRoot()->requestItemRemoval(this);
return true;
}
else {
return false;
}
}
bool OwnCloudFeed::markAsReadUnread(RootItem::ReadStatus status) {
QStringList ids = getParentServiceRoot()->customIDSOfMessagesForItem(this);
QNetworkReply::NetworkError response = serviceRoot()->network()->markMessagesRead(status, ids);

View File

@ -33,6 +33,8 @@ class OwnCloudFeed : public Feed {
bool canBeEdited() const;
bool editViaGui();
bool canBeDeleted() const;
bool deleteViaGui();
bool markAsReadUnread(ReadStatus status);
bool cleanMessages(bool clear_only_read);

View File

@ -20,6 +20,7 @@
#include "definitions/definitions.h"
#include "miscellaneous/application.h"
#include "miscellaneous/databasefactory.h"
#include "miscellaneous/databasequeries.h"
#include "miscellaneous/iconfactory.h"
#include "miscellaneous/textfactory.h"
#include "gui/dialogs/formmain.h"
@ -200,23 +201,8 @@ bool TtRssFeed::removeItself() {
if (response.code() == UFF_OK) {
// Feed was removed online from server, remove local data.
QSqlDatabase database = qApp->database()->connection(metaObject()->className(), DatabaseFactory::FromSettings);
QSqlQuery query_remove(database);
query_remove.setForwardOnly(true);
// Remove all messages from this standard feed.
query_remove.prepare(QSL("DELETE FROM Messages WHERE feed = :feed;"));
query_remove.bindValue(QSL(":feed"), customId());
if (!query_remove.exec()) {
return false;
}
// Remove feed itself.
query_remove.prepare(QSL("DELETE FROM Feeds WHERE id = :feed;"));
query_remove.bindValue(QSL(":feed"), id());
return query_remove.exec();
return DatabaseQueries::deleteFeed(database, customId(), serviceRoot()->accountId());
}
else {
qWarning("TT-RSS: Unsubscribing from feed failed, received JSON: '%s'", qPrintable(response.toString()));