Work on TT-RSS network.

This commit is contained in:
Martin Rotter 2015-12-07 09:42:46 +01:00
parent 8fbb434a56
commit 32a8fc81cb
19 changed files with 217 additions and 58 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

View File

@ -22,7 +22,7 @@ CREATE TABLE IF NOT EXISTS Accounts (
INSERT INTO Accounts (type) VALUES ('std-rss');
-- !
CREATE TABLE IF NOT EXISTS TtRssAccounts (
id INTEGER PRIMARY KEY,
id INTEGER,
username TEXT NOT NULL,
password TEXT,
url TEXT NOT NULL,

View File

@ -16,7 +16,7 @@ CREATE TABLE IF NOT EXISTS Accounts (
INSERT INTO Accounts (type) VALUES ('std-rss');
-- !
CREATE TABLE IF NOT EXISTS TtRssAccounts (
id INTEGER PRIMARY KEY,
id INTEGER,
username TEXT NOT NULL,
password TEXT,
url TEXT NOT NULL,

View File

@ -8,7 +8,7 @@ INSERT INTO Accounts (type) VALUES ('std-rss');
DROP TABLE IF EXISTS FeedsData;
-- !
CREATE TABLE IF NOT EXISTS TtRssAccounts (
id INTEGER PRIMARY KEY,
id INTEGER,
username TEXT NOT NULL,
password TEXT,
url TEXT NOT NULL,

View File

@ -8,7 +8,7 @@ INSERT INTO Accounts (type) VALUES ('std-rss');
DROP TABLE IF EXISTS FeedsData;
-- !
CREATE TABLE IF NOT EXISTS TtRssAccounts (
id INTEGER PRIMARY KEY,
id INTEGER,
username TEXT NOT NULL,
password TEXT,
url TEXT NOT NULL,

View File

@ -42,7 +42,7 @@ RootItem::~RootItem() {
qDeleteAll(m_childItems);
}
QList<QAction*> RootItem::contextMenuActions() {
QList<QAction*> RootItem::contextMenu() {
return QList<QAction*>();
}

View File

@ -76,7 +76,7 @@ class RootItem : public QObject {
// Returns list of specific actions which can be done with the item.
// Do not include general actions here like actions: Mark as read, Update, ...
// NOTE: Ownership of returned actions is not switched to caller, free them when needed.
virtual QList<QAction*> contextMenuActions();
virtual QList<QAction*> contextMenu();
// Can properties of this item be edited?
virtual bool canBeEdited();

View File

@ -303,7 +303,7 @@ QMenu *FeedsView::initializeContextMenuCategories(RootItem *clicked_item) {
m_contextMenuCategories->clear();
}
QList<QAction*> specific_actions = clicked_item->contextMenuActions();
QList<QAction*> specific_actions = clicked_item->contextMenu();
m_contextMenuCategories->addActions(QList<QAction*>() <<
qApp->mainForm()->m_ui->m_actionUpdateSelectedItems <<
@ -329,7 +329,7 @@ QMenu *FeedsView::initializeContextMenuFeeds(RootItem *clicked_item) {
m_contextMenuFeeds->clear();
}
QList<QAction*> specific_actions = clicked_item->contextMenuActions();
QList<QAction*> specific_actions = clicked_item->contextMenu();
m_contextMenuFeeds->addActions(QList<QAction*>() <<
qApp->mainForm()->m_ui->m_actionUpdateSelectedItems <<
@ -365,7 +365,7 @@ QMenu *FeedsView::initializeContextMenuOtherItem(RootItem *clicked_item) {
m_contextMenuOtherItems->clear();
}
QList<QAction*> specific_actions = clicked_item->contextMenuActions();
QList<QAction*> specific_actions = clicked_item->contextMenu();
if (!specific_actions.isEmpty()) {
m_contextMenuOtherItems->addSeparator();

View File

@ -18,6 +18,9 @@
#include "services/abstract/serviceroot.h"
#include "core/feedsmodel.h"
#include "miscellaneous/application.h"
#include <QSqlQuery>
ServiceRoot::ServiceRoot(RootItem *parent) : RootItem(parent), m_accountId(NO_PARENT_CATEGORY) {
@ -27,6 +30,34 @@ ServiceRoot::ServiceRoot(RootItem *parent) : RootItem(parent), m_accountId(NO_PA
ServiceRoot::~ServiceRoot() {
}
bool ServiceRoot::deleteViaGui() {
QSqlDatabase connection = qApp->database()->connection(metaObject()->className(), DatabaseFactory::FromSettings);
// Remove all messages.
if (!QSqlQuery(connection).exec(QString("DELETE FROM Messages WHERE account_id = %1;").arg(accountId()))) {
return false;
}
// Remove all feeds.
if (!QSqlQuery(connection).exec(QString("DELETE FROM Feeds WHERE account_id = %1;").arg(accountId()))) {
return false;
}
// Remove all categories.
if (!QSqlQuery(connection).exec(QString("DELETE FROM Categories WHERE account_id = %1;").arg(accountId()))) {
return false;
}
// Switch "existence" flag.
bool data_removed = QSqlQuery(connection).exec(QString("DELETE FROM Accounts WHERE id = %1;").arg(accountId()));
if (data_removed) {
requestItemRemoval(this);
}
return data_removed;
}
void ServiceRoot::itemChanged(QList<RootItem*> items) {
emit dataChanged(items);
}

View File

@ -44,6 +44,8 @@ class ServiceRoot : public RootItem {
// /* Members to override.
/////////////////////////////////////////
bool deleteViaGui();
// Returns list of specific actions for "Add new item" main window menu.
// So typical list of returned actions could look like:
// a) Add new feed

View File

@ -98,7 +98,7 @@ int StandardFeed::countOfUnreadMessages() const {
return m_unreadCount;
}
QList<QAction*> StandardFeed::contextMenuActions() {
QList<QAction*> StandardFeed::contextMenu() {
return serviceRoot()->getContextMenuForFeed(this);
}

View File

@ -61,7 +61,7 @@ class StandardFeed : public Feed {
int countOfAllMessages() const;
int countOfUnreadMessages() const;
QList<QAction*> contextMenuActions();
QList<QAction*> contextMenu();
bool canBeEdited() {
return true;

View File

@ -109,31 +109,7 @@ bool StandardServiceRoot::canBeDeleted() {
}
bool StandardServiceRoot::deleteViaGui() {
QSqlDatabase connection = qApp->database()->connection(metaObject()->className(), DatabaseFactory::FromSettings);
// Remove all messages.
if (!QSqlQuery(connection).exec(QString("DELETE FROM Messages WHERE account_id = %1;").arg(accountId()))) {
return false;
}
// Remove all feeds.
if (!QSqlQuery(connection).exec(QString("DELETE FROM Feeds WHERE account_id = %1;").arg(accountId()))) {
return false;
}
// Remove all categories.
if (!QSqlQuery(connection).exec(QString("DELETE FROM Categories WHERE account_id = %1;").arg(accountId()))) {
return false;
}
// Switch "existence" flag.
bool data_removed = QSqlQuery(connection).exec(QString("DELETE FROM Accounts WHERE id = %1;").arg(accountId()));
if (data_removed) {
requestItemRemoval(this);
}
return data_removed;
return ServiceRoot::deleteViaGui();
}
QVariant StandardServiceRoot::data(int column, int role) const {
@ -622,7 +598,7 @@ QList<QAction*> StandardServiceRoot::serviceMenu() {
return m_serviceMenu;
}
QList<QAction*> StandardServiceRoot::contextMenuActions() {
QList<QAction*> StandardServiceRoot::contextMenu() {
return serviceMenu();
}

View File

@ -64,7 +64,7 @@ class StandardServiceRoot : public ServiceRoot {
// Return menu to be shown in "Services -> service" menu.
QList<QAction*> serviceMenu();
QList<QAction*> contextMenuActions();
QList<QAction*> contextMenu();
// Message stuff.
bool loadMessagesForItem(RootItem *item, QSqlTableModel *model);

View File

@ -23,4 +23,7 @@
// Logout.
#define LOGOUT_OK "OK"
// Get feed tree.
#define GFT_TYPE_CATEGORY "category"
#endif // DEFINITIONS_H

View File

@ -18,12 +18,13 @@
#include "services/tt-rss/network/ttrssnetworkfactory.h"
#include "definitions/definitions.h"
#include "core/rootitem.h"
#include "services/tt-rss/definitions.h"
#include "network-web/networkfactory.h"
TtRssNetworkFactory::TtRssNetworkFactory()
: m_url(QString()), m_username(QString()), m_password(QString()), m_session_Id(QString()) {
: m_url(QString()), m_username(QString()), m_password(QString()), m_sessionId(QString()) {
}
TtRssNetworkFactory::~TtRssNetworkFactory() {
@ -63,22 +64,56 @@ void TtRssNetworkFactory::setPassword(const QString &password) {
LoginResult TtRssNetworkFactory::login() {
if (!m_sessionId.isEmpty()) {
logout();
}
QtJson::JsonObject json;
json["op"] = "login";
json["user"] = m_username;
json["password"] = m_password;
QByteArray result_raw;
NetworkResult res = NetworkFactory::uploadData(m_url, DOWNLOAD_TIMEOUT, QtJson::serialize(json), CONTENT_TYPE, result_raw);
NetworkResult network_reply = NetworkFactory::uploadData(m_url, DOWNLOAD_TIMEOUT, QtJson::serialize(json), CONTENT_TYPE, result_raw);
LoginResult result(network_reply.first, TtRssLoginResponse(QString::fromUtf8(result_raw)));
if (res.first != QNetworkReply::NoError) {
return LoginResult(res.first, TtRssLoginResponse());
if (network_reply.first == QNetworkReply::NoError) {
m_sessionId = result.second.sessionId();
}
else {
LoginResult result(res.first, TtRssLoginResponse(QString::fromUtf8(result_raw)));
m_session_Id = result.second.sessionId();
return result;
return result;
}
LogoutResult TtRssNetworkFactory::logout() {
QtJson::JsonObject json;
json["op"] = "logout";
json["sid"] = m_sessionId;
QByteArray result_raw;
NetworkResult network_reply = NetworkFactory::uploadData(m_url, DOWNLOAD_TIMEOUT, QtJson::serialize(json), CONTENT_TYPE, result_raw);
return LogoutResult(network_reply.first, TtRssResponse(QString::fromUtf8(result_raw)));
}
GetFeedTreeResult TtRssNetworkFactory::getFeedTree() {
QtJson::JsonObject json;
json["op"] = "getFeedTree";
json["sid"] = m_sessionId;
json["include_empty"] = true;
QByteArray result_raw;
NetworkResult network_reply = NetworkFactory::uploadData(m_url, DOWNLOAD_TIMEOUT, QtJson::serialize(json), CONTENT_TYPE, result_raw);
GetFeedTreeResult result(network_reply.first, TtRssGetFeedTreeResponse(QString::fromUtf8(result_raw)));
if (result.second.isNotLoggedIn()) {
// We are not logged in.
login();
network_reply = NetworkFactory::uploadData(m_url, DOWNLOAD_TIMEOUT, QtJson::serialize(json), CONTENT_TYPE, result_raw);
result = GetFeedTreeResult(network_reply.first, TtRssGetFeedTreeResponse(QString::fromUtf8(result_raw)));
}
return result;
}
TtRssResponse::TtRssResponse(const QString &raw_content) {
@ -110,6 +145,10 @@ int TtRssResponse::status() const {
}
}
bool TtRssResponse::isNotLoggedIn() const {
return status() == API_STATUS_ERR && hasError() && error() == NOT_LOGGED_IN;
}
TtRssLoginResponse::TtRssLoginResponse(const QString &raw_content) : TtRssResponse(raw_content) {
}
@ -135,7 +174,7 @@ QString TtRssLoginResponse::sessionId() const {
}
}
QString TtRssLoginResponse::error() const {
QString TtRssResponse::error() const {
if (!isLoaded()) {
return QString();
}
@ -144,7 +183,7 @@ QString TtRssLoginResponse::error() const {
}
}
bool TtRssLoginResponse::hasError() const {
bool TtRssResponse::hasError() const {
if (!isLoaded()) {
return false;
}
@ -152,3 +191,58 @@ bool TtRssLoginResponse::hasError() const {
return m_rawContent["content"].toMap().contains("error");
}
}
TtRssGetFeedTreeResponse::TtRssGetFeedTreeResponse(const QString &raw_content) : TtRssResponse(raw_content) {
}
TtRssGetFeedTreeResponse::~TtRssGetFeedTreeResponse() {
}
QList<RootItem*> TtRssGetFeedTreeResponse::getTree() {
QList<RootItem*> items;
if (status() == API_STATUS_OK) {
// We have data, construct object tree according to data.
QList<QVariant> items_to_process = m_rawContent["content"].toMap()["categories"].toMap()["items"].toList();
processSubtree(true, items, NULL, items_to_process);
}
return items;
}
void TtRssGetFeedTreeResponse::processSubtree(bool is_top_level, QList<RootItem *> &top_level_items,
RootItem *parent, const QList<QVariant> &items) {
foreach (QVariant item, items) {
QMap<QString,QVariant> map_item = item.toMap();
if (map_item.contains("type") && map_item["type"].toString() == GFT_TYPE_CATEGORY) {
// TODO: pokračovat tady
// We have category, create it, add it to "parent".
// Then process all its children.
//
// TtRssCategory *new_category = new TtRssCategory();
// naplnit informace.....
// parent->appendChild(new_category);
// if (is_top_level) {
// top_level_items.append(new_category);
// }
// else {
// parent->appendChild(new_category);
// }
// processSubtree(false, top_level_items, new_category, map_item["items"].toList());
}
else {
// We have feed, add it.
// TtRssFeed *new_feed = new TtRssFeed();
// naplnit informace.....
// parent->appendChild(new_feed);
// if (is_top_level) {
// top_level_items.append(new_feed);
// }
}
}
}

View File

@ -25,6 +25,8 @@
#include <QNetworkReply>
class RootItem;
class TtRssResponse {
public:
explicit TtRssResponse(const QString &raw_content = QString());
@ -34,6 +36,9 @@ class TtRssResponse {
int seq() const;
int status() const;
QString error() const;
bool hasError() const;
bool isNotLoggedIn() const;
protected:
QtJson::JsonObject m_rawContent;
@ -46,11 +51,24 @@ class TtRssLoginResponse : public TtRssResponse {
int apiLevel() const;
QString sessionId() const;
QString error() const;
bool hasError() const;
};
typedef QPair<QNetworkReply::NetworkError,TtRssLoginResponse> LoginResult;
typedef QPair<QNetworkReply::NetworkError,TtRssResponse> LogoutResult;
class TtRssGetFeedTreeResponse : public TtRssResponse {
public:
explicit TtRssGetFeedTreeResponse(const QString &raw_content = QString());
virtual ~TtRssGetFeedTreeResponse();
QList<RootItem*> getTree();
private:
void processSubtree(bool is_top_level, QList<RootItem*> &top_level_items,
RootItem *parent, const QList<QVariant> &items);
};
typedef QPair<QNetworkReply::NetworkError,TtRssGetFeedTreeResponse> GetFeedTreeResult;
class TtRssNetworkFactory {
public:
@ -67,13 +85,21 @@ class TtRssNetworkFactory {
void setPassword(const QString &password);
// Operations.
// Logs user in.
LoginResult login();
private:
// Logs user out.
LogoutResult logout();
// Gets tree from feeds/categories obtained from the server.
GetFeedTreeResult getFeedTree();
private:
QString m_url;
QString m_username;
QString m_password;
QString m_session_Id;
QString m_sessionId;
};
#endif // TTRSSNETWORKFACTORY_H

View File

@ -30,7 +30,7 @@
TtRssServiceRoot::TtRssServiceRoot(RootItem *parent)
: ServiceRoot(parent), m_network(new TtRssNetworkFactory) {
: ServiceRoot(parent), m_network(new TtRssNetworkFactory), m_actionSyncIn(NULL), m_serviceMenu(QList<QAction*>()) {
setIcon(TtRssServiceEntryPoint().icon());
setCreationDate(QDateTime::currentDateTime());
}
@ -63,7 +63,16 @@ bool TtRssServiceRoot::editViaGui() {
}
bool TtRssServiceRoot::deleteViaGui() {
return false;
QSqlDatabase connection = qApp->database()->connection(metaObject()->className(), DatabaseFactory::FromSettings);
// Remove extra entry in "Tiny Tiny RSS accounts list" and then delete
// all the categories/feeds and messages.
if (!QSqlQuery(connection).exec(QString("DELETE FROM TtRssAccounts WHERE id = %1;").arg(accountId()))) {
return false;
}
else {
return ServiceRoot::deleteViaGui();
}
}
bool TtRssServiceRoot::canBeEdited() {
@ -105,7 +114,19 @@ bool TtRssServiceRoot::loadMessagesForItem(RootItem *item, QSqlTableModel *model
}
QList<QAction*> TtRssServiceRoot::serviceMenu() {
return QList<QAction*>();
if (m_serviceMenu.isEmpty()) {
m_actionSyncIn = new QAction(qApp->icons()->fromTheme(QSL("item-sync")), tr("Sync in"), this);
connect(m_actionSyncIn, SIGNAL(triggered()), this, SLOT(syncIn()));
m_serviceMenu.append(m_actionSyncIn);
}
return m_serviceMenu;
}
QList<QAction*> TtRssServiceRoot::contextMenu() {
return serviceMenu();
}
bool TtRssServiceRoot::onBeforeSetMessagesRead(RootItem *selected_item, QList<int> message_db_ids, RootItem::ReadStatus read) {
@ -207,4 +228,5 @@ void TtRssServiceRoot::syncIn() {
// TODO: provede stažení kanálů/kategorií
// ze serveru, a sloučení s aktuálními
// neprovádí aktualizace kanálů ani stažení počtu nepřečtených zpráv
m_network->getFeedTree();
}

View File

@ -44,8 +44,9 @@ class TtRssServiceRoot : public ServiceRoot {
QVariant data(int column, int role) const;
QList<QAction *> addItemMenu();
QList<QAction *> serviceMenu();
QList<QAction*> addItemMenu();
QList<QAction*> serviceMenu();
QList<QAction*> contextMenu();
RecycleBin *recycleBin();
@ -69,9 +70,13 @@ class TtRssServiceRoot : public ServiceRoot {
void loadFromDatabase();
void updateTitle();
private:
private slots:
void syncIn();
private:
QAction *m_actionSyncIn;
QList<QAction*> m_serviceMenu;
TtRssNetworkFactory *m_network;
};