Verified Commit 72457cd5 authored by Linus Jahn's avatar Linus Jahn Committed by Linus Jahn
Browse files

database: Add message types and mediaUrl; Clean up

This introduces different message types (text, image, video, audio, document,
file). They should be used to determine the correct message element in the UI.

Also, there is a new mediaUrl in the Messages table which will be used to
save the HTTP urls to download the media shares.

The database version is increased to 5.
parent 07b87757
...@@ -41,7 +41,7 @@ ...@@ -41,7 +41,7 @@
#include <QSqlQuery> #include <QSqlQuery>
#include <QSqlRecord> #include <QSqlRecord>
static const int DATABASE_LATEST_VERSION = 4; static const int DATABASE_LATEST_VERSION = 5;
static const char *DATABASE_TABLE_INFO = "dbinfo"; static const char *DATABASE_TABLE_INFO = "dbinfo";
static const char *DATABASE_TABLE_MESSAGES = "Messages"; static const char *DATABASE_TABLE_MESSAGES = "Messages";
static const char *DATABASE_TABLE_ROSTER = "Roster"; static const char *DATABASE_TABLE_ROSTER = "Roster";
...@@ -124,21 +124,22 @@ bool Database::needToConvert() ...@@ -124,21 +124,22 @@ bool Database::needToConvert()
void Database::convertDatabase() void Database::convertDatabase()
{ {
qDebug() << "[Database] Converting database to latest version from verion" << version; qDebug() << "[database] Converting database to latest version from verion" << version;
switch (version) { while (version < DATABASE_LATEST_VERSION) {
case 0: switch (version) {
createNewDatabase(); case 0:
break; createNewDatabase(); version = DATABASE_LATEST_VERSION; break;
case 1: case 1:
convertDatabaseToV2(); version = 2; convertDatabaseToV2(); version = 2; break;
case 2: case 2:
convertDatabaseToV3(); version = 3; convertDatabaseToV3(); version = 3; break;
case 3: case 3:
convertDatabaseToV4(); version = 4; convertDatabaseToV4(); version = 4; break;
// only break on last convertion step, to not enter default (!) case 4:
break; convertDatabaseToV5(); version = 5; break;
default: default:
createNewDatabase(); break;
}
} }
QSqlQuery query(database); QSqlQuery query(database);
...@@ -195,6 +196,8 @@ void Database::createNewDatabase() ...@@ -195,6 +196,8 @@ void Database::createNewDatabase()
"'id' TEXT NOT NULL," "'id' TEXT NOT NULL,"
"'isSent' BOOL," // is sent to server "'isSent' BOOL," // is sent to server
"'isDelivered' BOOL," // message has arrived at other client "'isDelivered' BOOL," // message has arrived at other client
"'type' INTEGER," // type of message (text/image/video/...)
"'mediaUrl' TEXT,"
"FOREIGN KEY('author') REFERENCES Roster ('jid')," "FOREIGN KEY('author') REFERENCES Roster ('jid'),"
"FOREIGN KEY('recipient') REFERENCES Roster ('jid')" "FOREIGN KEY('recipient') REFERENCES Roster ('jid')"
")")) ")"))
...@@ -207,16 +210,11 @@ void Database::createDbInfoTable() ...@@ -207,16 +210,11 @@ void Database::createDbInfoTable()
{ {
QSqlQuery query(database); QSqlQuery query(database);
query.prepare("CREATE TABLE IF NOT EXISTS 'dbinfo' (version INTEGER NOT NULL)"); query.prepare("CREATE TABLE IF NOT EXISTS 'dbinfo' (version INTEGER NOT NULL)");
execQuery(query);
if (!query.exec()) {
qFatal("Failed to query database: %s", qPrintable(query.lastError().text()));
}
query.prepare(QString("INSERT INTO 'dbinfo' (version) VALUES (%1)") query.prepare(QString("INSERT INTO 'dbinfo' (version) VALUES (%1)")
.arg(DATABASE_LATEST_VERSION)); .arg(DATABASE_LATEST_VERSION));
if (!query.exec()) { execQuery(query);
qFatal("Failed to query database: %s", qPrintable(query.lastError().text()));
}
} }
void Database::convertDatabaseToV2() void Database::convertDatabaseToV2()
...@@ -229,8 +227,7 @@ void Database::convertDatabaseToV3() ...@@ -229,8 +227,7 @@ void Database::convertDatabaseToV3()
{ {
QSqlQuery query(database); QSqlQuery query(database);
query.prepare("ALTER TABLE Roster ADD avatarHash TEXT"); query.prepare("ALTER TABLE Roster ADD avatarHash TEXT");
if (!query.exec()) execQuery(query);
qFatal("Failed to query database: %s", qPrintable(query.lastError().text()));
} }
void Database::convertDatabaseToV4() void Database::convertDatabaseToV4()
...@@ -241,35 +238,43 @@ void Database::convertDatabaseToV4() ...@@ -241,35 +238,43 @@ void Database::convertDatabaseToV4()
// and copy everything to the normal table again // and copy everything to the normal table again
query.prepare("CREATE TEMPORARY TABLE roster_backup(jid,name,lastExchanged," query.prepare("CREATE TEMPORARY TABLE roster_backup(jid,name,lastExchanged,"
"unreadMessages,lastMessage,lastOnline,activity,status,mood);"); "unreadMessages,lastMessage,lastOnline,activity,status,mood);");
if (!query.exec()) { execQuery(query);
qDebug() << query.executedQuery();
qFatal("Failed to query database: %s", qPrintable(query.lastError().text()));
}
query.prepare("INSERT INTO roster_backup SELECT jid,name,lastExchanged,unreadMessages," query.prepare("INSERT INTO roster_backup SELECT jid,name,lastExchanged,unreadMessages,"
"lastMessage,lastOnline,activity,status,mood FROM Roster;"); "lastMessage,lastOnline,activity,status,mood FROM Roster;");
if (!query.exec()) { execQuery(query);
qDebug() << query.executedQuery();
qFatal("Failed to query database: %s", qPrintable(query.lastError().text()));
}
query.prepare("DROP TABLE Roster;"); query.prepare("DROP TABLE Roster;");
if (!query.exec()) { execQuery(query);
qDebug() << query.executedQuery();
qFatal("Failed to query database: %s", qPrintable(query.lastError().text()));
}
query.prepare("CREATE TABLE Roster('jid' TEXT NOT NULL,'name' TEXT NOT NULL," query.prepare("CREATE TABLE Roster('jid' TEXT NOT NULL,'name' TEXT NOT NULL,"
"'lastExchanged' TEXT NOT NULL,'unreadMessages' INTEGER,'lastMessage' TEXT," "'lastExchanged' TEXT NOT NULL,'unreadMessages' INTEGER,'lastMessage' TEXT,"
"'lastOnline' TEXT,'activity' TEXT,'status' TEXT,'mood' TEXT);"); "'lastOnline' TEXT,'activity' TEXT,'status' TEXT,'mood' TEXT);");
if (!query.exec()) { execQuery(query);
qDebug() << query.executedQuery();
qFatal("Failed to query database: %s", qPrintable(query.lastError().text()));
}
query.prepare("INSERT INTO Roster SELECT jid,name,lastExchanged,unreadMessages," query.prepare("INSERT INTO Roster SELECT jid,name,lastExchanged,unreadMessages,"
"lastMessage,lastOnline,activity,status,mood FROM Roster_backup;"); "lastMessage,lastOnline,activity,status,mood FROM Roster_backup;");
if (!query.exec()) { execQuery(query);
qDebug() << query.executedQuery();
qFatal("Failed to query database: %s", qPrintable(query.lastError().text()));
}
query.prepare("DROP TABLE Roster_backup;"); query.prepare("DROP TABLE Roster_backup;");
execQuery(query);
}
void Database::convertDatabaseToV5()
{
QSqlQuery query(database);
query.prepare("ALTER TABLE 'Messages' ADD 'type' INTEGER");
execQuery(query);
query.prepare("UPDATE Messages SET type = 0 WHERE type IS NULL");
execQuery(query);
query.prepare("ALTER TABLE 'Messages' ADD 'mediaUrl' TEXT");
execQuery(query);
}
void Database::execQuery(QSqlQuery &query)
{
if (!query.exec()) { if (!query.exec()) {
qDebug() << query.executedQuery(); qDebug() << query.executedQuery();
qFatal("Failed to query database: %s", qPrintable(query.lastError().text())); qFatal("Failed to query database: %s", qPrintable(query.lastError().text()));
......
...@@ -34,6 +34,8 @@ ...@@ -34,6 +34,8 @@
#include <QObject> #include <QObject>
#include <QSqlDatabase> #include <QSqlDatabase>
class QSqlQuery;
class Database : public QObject class Database : public QObject
{ {
Q_OBJECT Q_OBJECT
...@@ -54,6 +56,8 @@ private: ...@@ -54,6 +56,8 @@ private:
void convertDatabaseToV2(); void convertDatabaseToV2();
void convertDatabaseToV3(); void convertDatabaseToV3();
void convertDatabaseToV4(); void convertDatabaseToV4();
void convertDatabaseToV5();
void execQuery(QSqlQuery &query);
QSqlDatabase database; QSqlDatabase database;
int version; int version;
......
/* /*
* Kaidan - A user-friendly XMPP client for every device! * Kaidan - A user-friendly XMPP client for every device!
* *
* Copyright (C) 2018 Kaidan developers and contributors (see the LICENSE * Copyright (C) 2018 Kaidan developers and contributors
* file for a full list of copyright authors) * (see the LICENSE file for a full list of copyright authors)
* *
* Kaidan is free software; you can redistribute it and/or modify * Kaidan is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* In addition, as a special exception, the author of Kaidan gives * In addition, as a special exception, the author of Kaidan gives
* permission to link the code of its release with the OpenSSL project's * permission to link the code of its release with the OpenSSL
* "OpenSSL" library (or with modified versions of it that use the same * project's "OpenSSL" library (or with modified versions of it that
* license as the "OpenSSL" library), and distribute the linked * use the same license as the "OpenSSL" library), and distribute the
* executables. You must obey the GNU General Public License in all * linked executables. You must obey the GNU General Public License in
* respects for all of the code used other than "OpenSSL". If you modify * all respects for all of the code used other than "OpenSSL". If you
* this file, you may extend this exception to your version of the file, * modify this file, you may extend this exception to your version of
* but you are not obligated to do so. If you do not wish to do so, * the file, but you are not obligated to do so. If you do not wish to
* delete this exception statement from your version. * do so, delete this exception statement from your version.
* *
* Kaidan is distributed in the hope that it will be useful, but WITHOUT ANY * Kaidan is distributed in the hope that it will be useful,
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * but WITHOUT ANY WARRANTY; without even the implied warranty of
* PARTICULAR PURPOSE. See the GNU General Public License for more details. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* * GNU General Public License for more details.
* You should have received a copy of the GNU General Public License along with *
* this package. If not, see <http://www.gnu.org/licenses/>. * You should have received a copy of the GNU General Public License
* along with Kaidan. If not, see <http://www.gnu.org/licenses/>.
*/ */
#ifndef ENUMS_H #ifndef ENUMS_H
...@@ -38,7 +39,7 @@ namespace Enums { ...@@ -38,7 +39,7 @@ namespace Enums {
Q_NAMESPACE Q_NAMESPACE
/** /**
* Enumration of possible connection states. * Enumeration of possible connection states.
*/ */
enum class ConnectionState : quint8 { enum class ConnectionState : quint8 {
StateNone, StateNone,
...@@ -50,7 +51,7 @@ namespace Enums { ...@@ -50,7 +51,7 @@ namespace Enums {
Q_ENUM_NS(ConnectionState) Q_ENUM_NS(ConnectionState)
/** /**
* Enumration of possible disconnection reasons (compatible to gloox:: * Enumeration of possible disconnection reasons (compatible to gloox::
* ConnectionError) * ConnectionError)
*/ */
enum class DisconnectionReason : quint8 { enum class DisconnectionReason : quint8 {
...@@ -79,6 +80,19 @@ namespace Enums { ...@@ -79,6 +80,19 @@ namespace Enums {
// Alias, so that qDebug outputs the full name, but it can be // Alias, so that qDebug outputs the full name, but it can be
// abrieviated in the code // abrieviated in the code
using DisconnReason = DisconnectionReason; using DisconnReason = DisconnectionReason;
/**
* Enumeration of different media/message types
*/
enum class MessageType : quint8 {
MessageText,
MessageFile,
MessageImage,
MessageVideo,
MessageAudio,
MessageDocument
};
Q_ENUM_NS(MessageType)
} }
// Needed workaround to trigger older CMake auto moc versions to generate moc // Needed workaround to trigger older CMake auto moc versions to generate moc
......
...@@ -44,6 +44,7 @@ ...@@ -44,6 +44,7 @@
// Kaidan // Kaidan
#include "MessageModel.h" #include "MessageModel.h"
#include "Notifications.h" #include "Notifications.h"
#include "Enums.h"
QDateTime stringToQDateTime(std::string stamp) QDateTime stringToQDateTime(std::string stamp)
{ {
...@@ -131,9 +132,10 @@ void MessageHandler::handleMessage(const gloox::Message &stanza, gloox::MessageS ...@@ -131,9 +132,10 @@ void MessageHandler::handleMessage(const gloox::Message &stanza, gloox::MessageS
.toString(Qt::ISODate); .toString(Qt::ISODate);
// add the message to the database // add the message to the database
emit messageModel->addMessageRequested(fromJid, toJid, timestamp, emit messageModel->addMessageRequested(
body, msgId, isSentByMe, fromJid, toJid, timestamp, body, msgId, isSentByMe,
fromJidResource, toJidResource); MessageType::MessageText, fromJidResource, toJidResource
);
// //
// Send a new notification | TODO: Resolve nickname from JID // Send a new notification | TODO: Resolve nickname from JID
...@@ -211,7 +213,9 @@ void MessageHandler::sendMessage(QString toJid, QString body) ...@@ -211,7 +213,9 @@ void MessageHandler::sendMessage(QString toJid, QString body)
const QString id = QString::fromStdString(message.id()); const QString id = QString::fromStdString(message.id());
const QString fromJid = QString::fromStdString(client->jid().bare()); const QString fromJid = QString::fromStdString(client->jid().bare());
emit messageModel->addMessageRequested(fromJid, toJid, timestamp, body, id, true); emit messageModel->addMessageRequested(
fromJid, toJid, timestamp, body, id, true, MessageType::MessageText
);
// XEP-0184: Message Delivery Receipts // XEP-0184: Message Delivery Receipts
// request a delivery receipt from the other client // request a delivery receipt from the other client
......
...@@ -52,7 +52,7 @@ class MessageHandler : public QObject, public gloox::MessageHandler ...@@ -52,7 +52,7 @@ class MessageHandler : public QObject, public gloox::MessageHandler
public: public:
MessageHandler(gloox::Client *client, MessageModel *messageModel, MessageHandler(gloox::Client *client, MessageModel *messageModel,
RosterModel *rosterModel, QObject *parent = nullptr); RosterModel *rosterModel, QObject *parent = nullptr);
~MessageHandler(); ~MessageHandler();
virtual void handleMessage(const gloox::Message &message, gloox::MessageSession *session = 0); virtual void handleMessage(const gloox::Message &message, gloox::MessageSession *session = 0);
......
...@@ -105,8 +105,9 @@ void MessageModel::setMessageAsDelivered(const QString msgId) ...@@ -105,8 +105,9 @@ void MessageModel::setMessageAsDelivered(const QString msgId)
void MessageModel::addMessage(const QString author, const QString recipient, void MessageModel::addMessage(const QString author, const QString recipient,
const QString timestamp, const QString message, const QString timestamp, const QString message,
const QString msgId, bool sentByMe, const QString msgId, bool sentByMe, MessageType type,
const QString author_resource, const QString recipient_resource) const QString mediaUrl, const QString author_resource,
const QString recipient_resource)
{ {
// //
// add the new message // add the new message
...@@ -122,6 +123,8 @@ void MessageModel::addMessage(const QString author, const QString recipient, ...@@ -122,6 +123,8 @@ void MessageModel::addMessage(const QString author, const QString recipient,
record.setValue("id", msgId); record.setValue("id", msgId);
record.setValue("isSent", !sentByMe); record.setValue("isSent", !sentByMe);
record.setValue("isDelivered", !sentByMe); record.setValue("isDelivered", !sentByMe);
record.setValue("type", (quint8) type);
record.setValue("mediaUrl", mediaUrl);
if (!insertRecord(rowCount(), record)) { if (!insertRecord(rowCount(), record)) {
qWarning() << "Failed to add message to DB:" << lastError().text(); qWarning() << "Failed to add message to DB:" << lastError().text();
......
...@@ -32,6 +32,9 @@ ...@@ -32,6 +32,9 @@
#define MESSAGEMODEL_H #define MESSAGEMODEL_H
#include <QSqlTableModel> #include <QSqlTableModel>
#include "Enums.h"
using namespace Enums;
class MessageModel : public QSqlTableModel class MessageModel : public QSqlTableModel
{ {
...@@ -61,7 +64,8 @@ signals: ...@@ -61,7 +64,8 @@ signals:
void addMessageRequested(const QString author, const QString recipient, void addMessageRequested(const QString author, const QString recipient,
const QString timestamp, const QString message, const QString timestamp, const QString message,
const QString msgId, bool sentByMe, const QString msgId, bool sentByMe, MessageType type,
const QString mediaUrl = QString(),
const QString author_resource = QString(), const QString author_resource = QString(),
const QString recipient_resource = QString()); const QString recipient_resource = QString());
void setMessageAsSentRequested(const QString msgId); void setMessageAsSentRequested(const QString msgId);
...@@ -69,8 +73,10 @@ signals: ...@@ -69,8 +73,10 @@ signals:
private slots: private slots:
void addMessage(const QString author, const QString recipient, void addMessage(const QString author, const QString recipient,
const QString timestamp, const QString message, const QString msgId, const QString timestamp, const QString message,
bool sentByMe, const QString author_resource = QString(), const QString msgId, bool sentByMe, MessageType type,
const QString mediaUrl = QString(),
const QString author_resource = QString(),
const QString recipient_resource = QString()); const QString recipient_resource = QString());
void setMessageAsSent(const QString msgId); void setMessageAsSent(const QString msgId);
void setMessageAsDelivered(const QString msgId); void setMessageAsDelivered(const QString msgId);
......
...@@ -137,6 +137,7 @@ int main(int argc, char *argv[]) ...@@ -137,6 +137,7 @@ int main(int argc, char *argv[])
qRegisterMetaType<EntityPresence*>("EntityPresence*"); qRegisterMetaType<EntityPresence*>("EntityPresence*");
qRegisterMetaType<Qt::ApplicationState>("Qt::ApplicationState"); qRegisterMetaType<Qt::ApplicationState>("Qt::ApplicationState");
qRegisterMetaType<gloox::Presence::PresenceType>("gloox::Presence::PresenceType"); qRegisterMetaType<gloox::Presence::PresenceType>("gloox::Presence::PresenceType");
qRegisterMetaType<MessageType>("MessageType");
qmlRegisterUncreatableMetaObject(Enums::staticMetaObject, APPLICATION_ID, qmlRegisterUncreatableMetaObject(Enums::staticMetaObject, APPLICATION_ID,
1, 0, "Kaidan", "Access to enums & flags only"); 1, 0, "Kaidan", "Access to enums & flags only");
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment