summaryrefslogtreecommitdiff
path: root/board.cpp
diff options
context:
space:
mode:
authorJavier S. Pedro <maemo@javispedro.com>2013-04-01 15:04:58 +0200
committerJavier S. Pedro <maemo@javispedro.com>2013-04-01 15:04:58 +0200
commit5ef8b38e55c1883224fe1f01f47aba45b7b42666 (patch)
tree67a873c6a7c5263d202793314c3b3a61543fbb40 /board.cpp
downloadtapasboard-5ef8b38e55c1883224fe1f01f47aba45b7b42666.tar.gz
tapasboard-5ef8b38e55c1883224fe1f01f47aba45b7b42666.zip
initial import
Diffstat (limited to 'board.cpp')
-rw-r--r--board.cpp220
1 files changed, 220 insertions, 0 deletions
diff --git a/board.cpp b/board.cpp
new file mode 100644
index 0000000..887b3c6
--- /dev/null
+++ b/board.cpp
@@ -0,0 +1,220 @@
+#include <QtCore/QRegExp>
+#include <QtCore/QDateTime>
+#include <QtCore/QDir>
+#include <QtCore/QDebug>
+#include <QtSql/QSqlQuery>
+#include <QtSql/QSqlError>
+
+#include "global.h"
+#include "action.h"
+#include "fetchboardconfigaction.h"
+#include "fetchforumsaction.h"
+#include "xmlrpcinterface.h"
+#include "board.h"
+
+Board::Board(const QString& forumUrl, QObject *parent) :
+ QObject(parent), _url(forumUrl), _slug(createSlug(forumUrl)),
+ _db(QSqlDatabase::addDatabase("QSQLITE", _slug)),
+ _iface(new XmlRpcInterface(QUrl(_url), this))
+{
+ _db.setDatabaseName(QDir::toNativeSeparators(getDbPathFor(_slug)));
+ qDebug() << "Opening database file" << _db.databaseName() << "for" << _url;
+ if (!_db.open()) {
+ qWarning() << "Could not open database file" << _db.databaseName() << ":"
+ << _db.lastError().text();
+ }
+ initializeDb();
+ fetchConfigIfOutdated();
+ fetchForumsIfOutdated();
+}
+
+void Board::enqueueAction(Action *action)
+{
+ connect(action, SIGNAL(finished(Action*)), SLOT(handleActionFinished(Action*)));
+ connect(action, SIGNAL(error(Action*,QString)), SLOT(handleActionError(Action*,QString)));
+
+ _queue.enqueue(action);
+
+ if (_queue.size() == 1) {
+ // There were no actions queued, so start by executing this one.
+ executeActionFromQueue();
+ }
+}
+
+QString Board::getConfig(const QString &key) const
+{
+ QSqlQuery query(_db);
+ query.prepare("SELECT key, value FROM config WHERE key = :key");
+ query.bindValue(":key", key);
+ if (!query.exec()) {
+ qWarning() << "Could not get configuration key:" << key;
+ return QString();
+ }
+ if (query.next()) {
+ return query.value(1).toString();
+ }
+ return QString();
+}
+
+void Board::setConfig(const QString &key, const QString &value)
+{
+ QSqlQuery query(_db);
+ query.prepare("INSERT OR REPLACE INTO config (key, value) VALUES (:key, :value)");
+ query.bindValue(":key", key);
+ query.bindValue(":value", value);
+ if (!query.exec()) {
+ qWarning() << "Could not set configuration key" << key << ":" << query.lastError().text();
+ }
+ notifyConfigChanged();
+}
+
+int Board::rootForumId() const
+{
+ QSqlQuery query(_db);
+ query.exec("SELECT forum_id FROM forums WHERE parent_id = -1");
+ if (query.next()) {
+ return query.value(0).toInt();
+ } else {
+ return -1;
+ }
+}
+
+void Board::notifyConfigChanged()
+{
+ emit configChanged();
+}
+
+void Board::notifyForumsChanged()
+{
+ emit forumsChanged();
+}
+
+void Board::notifyForumTopicsChanged(int forumId, int start, int end)
+{
+ qDebug() << "ForumTopics Changed" << forumId << start << end;
+ emit forumTopicsChanged(forumId, start, end);
+}
+
+QString Board::createSlug(const QString &forumUrl)
+{
+ static const QRegExp regexp("[^a-z0-9]+");
+ QString url = forumUrl.toLower();
+ url.replace(regexp, "_");
+ return url;
+}
+
+QString Board::getDbDir()
+{
+ QString path;
+
+#ifdef Q_OS_LINUX
+ char * xdg_cache_dir = getenv("XDG_CACHE_HOME");
+ if (xdg_cache_dir) {
+ path = QString::fromLocal8Bit(xdg_cache_dir) + "/tapasboard";
+ } else {
+ path = QDir::homePath() + "/.cache/tapasboard";
+ }
+ if (!QDir().mkpath(path)) {
+ qWarning() << "Failed to create directory for databases:" << path;
+ }
+#endif
+
+ return path;
+}
+
+QString Board::getDbPathFor(const QString &slug)
+{
+ return getDbDir() + "/" + slug + ".sqlite";
+}
+
+bool Board::initializeDb()
+{
+ QSqlQuery q(_db);
+ if (!q.exec("CREATE TABLE IF NOT EXISTS config (key TEXT PRIMARY KEY, value TEXT)")) {
+ qWarning() << "Could not create config table:" << q.lastError().text();
+ return false;
+ }
+
+ if (!q.exec("CREATE TABLE IF NOT EXISTS forums (forum_id INTEGER PRIMARY KEY, forum_name TEXT, description TEXT, parent_id INT, logo_url TEXT, new_post BOOL, is_protected BOOL, is_subscribed BOOL, can_subscribe BOOL, url TEXT, sub_only BOOL, sort_index INT UNIQUE)")) {
+ qWarning() << "Could not create forums table:" << q.lastError().text();
+ return false;
+ }
+ if (!q.exec("CREATE INDEX IF NOT EXISTS forums_parent ON forums (parent_id)")) {
+ qWarning() << "Could not create forums table:" << q.lastError().text();
+ return false;
+ }
+
+ if (!q.exec("CREATE TABLE IF NOT EXISTS topics (forum_id INTEGER, topic_id INTEGER PRIMARY KEY, topic_title TEXT, topic_author_id INTEGER, topic_author_name TEXT, is_subscribed BOOL, is_closed BOOL, icon_url TEXT, last_reply_time TEXT, new_post BOOL, last_update_time TEXT)")) {
+ qWarning() << "Could not create topics table:" << q.lastError().text();
+ return false;
+ }
+ if (!q.exec("CREATE INDEX IF NOT EXISTS topics_forum ON topics (forum_id)")) {
+ qWarning() << "Could not create topics_forum index:" << q.lastError().text();
+ return false;
+ }
+ if (!q.exec("CREATE INDEX IF NOT EXISTS topics_time ON topics (last_reply_time)")) {
+ qWarning() << "Could not create topics_time index:" << q.lastError().text();
+ return false;
+ }
+
+ return true;
+}
+
+bool Board::removeFromActionQueue(Action *action)
+{
+ if (_queue.isEmpty()) return false;
+ Action *head = _queue.head();
+ if (_queue.removeOne(action)) {
+ if (!_queue.isEmpty() && head != _queue.head()) {
+ // The head action was removed; advance the queue.
+ executeActionFromQueue();
+ }
+ action->deleteLater();
+ return true;
+ }
+ return false;
+}
+
+void Board::executeActionFromQueue()
+{
+ if (!_queue.empty()) {
+ Action *head = _queue.head();
+ head->execute();
+ }
+}
+
+void Board::fetchConfigIfOutdated()
+{
+ if (_iface->isAccessible()) {
+ // Only fetch if network is accessible and data is >48h old.
+ QDateTime last_fetch = QDateTime::fromString(
+ getConfig("last_config_fetch"), Qt::ISODate);
+ if (!last_fetch.isValid() || last_fetch.daysTo(QDateTime::currentDateTimeUtc()) >= BOARD_CONFIG_TTL) {
+ enqueueAction(new FetchBoardConfigAction(this));
+ }
+ }
+
+}
+
+void Board::fetchForumsIfOutdated()
+{
+ if (_iface->isAccessible()) {
+ // Only fetch if network is accessible and data is >48h old.
+ QDateTime last_fetch = QDateTime::fromString(
+ getConfig("last_forums_fetch"), Qt::ISODate);
+ if (!last_fetch.isValid() || last_fetch.daysTo(QDateTime::currentDateTimeUtc()) >= BOARD_LIST_TTL) {
+ enqueueAction(new FetchForumsAction(this));
+ }
+ }
+}
+
+void Board::handleActionFinished(Action *action)
+{
+ removeFromActionQueue(action);
+}
+
+void Board::handleActionError(Action *action, const QString& message)
+{
+ qWarning() << "Action failed:" << message;
+ removeFromActionQueue(action);
+}