diff --git a/resources/graphics/icons/mini-kfaenza/item-sync.png b/resources/graphics/icons/mini-kfaenza/item-sync.png new file mode 100755 index 000000000..4d025c236 Binary files /dev/null and b/resources/graphics/icons/mini-kfaenza/item-sync.png differ diff --git a/resources/misc/db_init_mysql.sql b/resources/misc/db_init_mysql.sql index 1931751fa..b3eb5e2cd 100644 --- a/resources/misc/db_init_mysql.sql +++ b/resources/misc/db_init_mysql.sql @@ -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, diff --git a/resources/misc/db_init_sqlite.sql b/resources/misc/db_init_sqlite.sql index ce8eced60..80a091205 100644 --- a/resources/misc/db_init_sqlite.sql +++ b/resources/misc/db_init_sqlite.sql @@ -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, diff --git a/resources/misc/db_update_mysql_3_4.sql b/resources/misc/db_update_mysql_3_4.sql index d1526931a..892d54579 100644 --- a/resources/misc/db_update_mysql_3_4.sql +++ b/resources/misc/db_update_mysql_3_4.sql @@ -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, diff --git a/resources/misc/db_update_sqlite_3_4.sql b/resources/misc/db_update_sqlite_3_4.sql index f70192ac4..6a6433312 100644 --- a/resources/misc/db_update_sqlite_3_4.sql +++ b/resources/misc/db_update_sqlite_3_4.sql @@ -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, diff --git a/src/core/rootitem.cpp b/src/core/rootitem.cpp index ff9b3ed8a..879ad8839 100755 --- a/src/core/rootitem.cpp +++ b/src/core/rootitem.cpp @@ -42,7 +42,7 @@ RootItem::~RootItem() { qDeleteAll(m_childItems); } -QList RootItem::contextMenuActions() { +QList RootItem::contextMenu() { return QList(); } diff --git a/src/core/rootitem.h b/src/core/rootitem.h index ed6dbc370..800bbc3cf 100755 --- a/src/core/rootitem.h +++ b/src/core/rootitem.h @@ -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 contextMenuActions(); + virtual QList contextMenu(); // Can properties of this item be edited? virtual bool canBeEdited(); diff --git a/src/gui/feedsview.cpp b/src/gui/feedsview.cpp index 8c67483d8..d976b6e84 100755 --- a/src/gui/feedsview.cpp +++ b/src/gui/feedsview.cpp @@ -303,7 +303,7 @@ QMenu *FeedsView::initializeContextMenuCategories(RootItem *clicked_item) { m_contextMenuCategories->clear(); } - QList specific_actions = clicked_item->contextMenuActions(); + QList specific_actions = clicked_item->contextMenu(); m_contextMenuCategories->addActions(QList() << qApp->mainForm()->m_ui->m_actionUpdateSelectedItems << @@ -329,7 +329,7 @@ QMenu *FeedsView::initializeContextMenuFeeds(RootItem *clicked_item) { m_contextMenuFeeds->clear(); } - QList specific_actions = clicked_item->contextMenuActions(); + QList specific_actions = clicked_item->contextMenu(); m_contextMenuFeeds->addActions(QList() << qApp->mainForm()->m_ui->m_actionUpdateSelectedItems << @@ -365,7 +365,7 @@ QMenu *FeedsView::initializeContextMenuOtherItem(RootItem *clicked_item) { m_contextMenuOtherItems->clear(); } - QList specific_actions = clicked_item->contextMenuActions(); + QList specific_actions = clicked_item->contextMenu(); if (!specific_actions.isEmpty()) { m_contextMenuOtherItems->addSeparator(); diff --git a/src/services/abstract/serviceroot.cpp b/src/services/abstract/serviceroot.cpp index 773475c68..2a804ce18 100755 --- a/src/services/abstract/serviceroot.cpp +++ b/src/services/abstract/serviceroot.cpp @@ -18,6 +18,9 @@ #include "services/abstract/serviceroot.h" #include "core/feedsmodel.h" +#include "miscellaneous/application.h" + +#include 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 items) { emit dataChanged(items); } diff --git a/src/services/abstract/serviceroot.h b/src/services/abstract/serviceroot.h index 98e30f2da..3dbd13475 100755 --- a/src/services/abstract/serviceroot.h +++ b/src/services/abstract/serviceroot.h @@ -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 diff --git a/src/services/standard/standardfeed.cpp b/src/services/standard/standardfeed.cpp index 6b56fd004..c9a355880 100755 --- a/src/services/standard/standardfeed.cpp +++ b/src/services/standard/standardfeed.cpp @@ -98,7 +98,7 @@ int StandardFeed::countOfUnreadMessages() const { return m_unreadCount; } -QList StandardFeed::contextMenuActions() { +QList StandardFeed::contextMenu() { return serviceRoot()->getContextMenuForFeed(this); } diff --git a/src/services/standard/standardfeed.h b/src/services/standard/standardfeed.h index 4e5f234fc..de1b8db56 100755 --- a/src/services/standard/standardfeed.h +++ b/src/services/standard/standardfeed.h @@ -61,7 +61,7 @@ class StandardFeed : public Feed { int countOfAllMessages() const; int countOfUnreadMessages() const; - QList contextMenuActions(); + QList contextMenu(); bool canBeEdited() { return true; diff --git a/src/services/standard/standardserviceroot.cpp b/src/services/standard/standardserviceroot.cpp index 89c8af3ef..ecebe24bf 100755 --- a/src/services/standard/standardserviceroot.cpp +++ b/src/services/standard/standardserviceroot.cpp @@ -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 StandardServiceRoot::serviceMenu() { return m_serviceMenu; } -QList StandardServiceRoot::contextMenuActions() { +QList StandardServiceRoot::contextMenu() { return serviceMenu(); } diff --git a/src/services/standard/standardserviceroot.h b/src/services/standard/standardserviceroot.h index c03461dfc..1a54f7b02 100755 --- a/src/services/standard/standardserviceroot.h +++ b/src/services/standard/standardserviceroot.h @@ -64,7 +64,7 @@ class StandardServiceRoot : public ServiceRoot { // Return menu to be shown in "Services -> service" menu. QList serviceMenu(); - QList contextMenuActions(); + QList contextMenu(); // Message stuff. bool loadMessagesForItem(RootItem *item, QSqlTableModel *model); diff --git a/src/services/tt-rss/definitions.h b/src/services/tt-rss/definitions.h index 9a96dfed7..6aae31bea 100755 --- a/src/services/tt-rss/definitions.h +++ b/src/services/tt-rss/definitions.h @@ -23,4 +23,7 @@ // Logout. #define LOGOUT_OK "OK" +// Get feed tree. +#define GFT_TYPE_CATEGORY "category" + #endif // DEFINITIONS_H diff --git a/src/services/tt-rss/network/ttrssnetworkfactory.cpp b/src/services/tt-rss/network/ttrssnetworkfactory.cpp index 86d93e765..18ab3dfe9 100755 --- a/src/services/tt-rss/network/ttrssnetworkfactory.cpp +++ b/src/services/tt-rss/network/ttrssnetworkfactory.cpp @@ -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 TtRssGetFeedTreeResponse::getTree() { + QList items; + + if (status() == API_STATUS_OK) { + // We have data, construct object tree according to data. + QList 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 &top_level_items, + RootItem *parent, const QList &items) { + foreach (QVariant item, items) { + QMap 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); + // } + } + } +} diff --git a/src/services/tt-rss/network/ttrssnetworkfactory.h b/src/services/tt-rss/network/ttrssnetworkfactory.h index c6ce09028..292666b55 100755 --- a/src/services/tt-rss/network/ttrssnetworkfactory.h +++ b/src/services/tt-rss/network/ttrssnetworkfactory.h @@ -25,6 +25,8 @@ #include +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 LoginResult; +typedef QPair LogoutResult; + +class TtRssGetFeedTreeResponse : public TtRssResponse { + public: + explicit TtRssGetFeedTreeResponse(const QString &raw_content = QString()); + virtual ~TtRssGetFeedTreeResponse(); + + QList getTree(); + + private: + void processSubtree(bool is_top_level, QList &top_level_items, + RootItem *parent, const QList &items); +}; + +typedef QPair 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 diff --git a/src/services/tt-rss/ttrssserviceroot.cpp b/src/services/tt-rss/ttrssserviceroot.cpp index 8997850e4..8360c4655 100755 --- a/src/services/tt-rss/ttrssserviceroot.cpp +++ b/src/services/tt-rss/ttrssserviceroot.cpp @@ -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()) { 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 TtRssServiceRoot::serviceMenu() { - return QList(); + 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 TtRssServiceRoot::contextMenu() { + return serviceMenu(); } bool TtRssServiceRoot::onBeforeSetMessagesRead(RootItem *selected_item, QList 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(); } diff --git a/src/services/tt-rss/ttrssserviceroot.h b/src/services/tt-rss/ttrssserviceroot.h index ee6b0d339..00ba6446e 100755 --- a/src/services/tt-rss/ttrssserviceroot.h +++ b/src/services/tt-rss/ttrssserviceroot.h @@ -44,8 +44,9 @@ class TtRssServiceRoot : public ServiceRoot { QVariant data(int column, int role) const; - QList addItemMenu(); - QList serviceMenu(); + QList addItemMenu(); + QList serviceMenu(); + QList 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 m_serviceMenu; + TtRssNetworkFactory *m_network; };