diff --git a/plugins/history/CMakeLists.txt b/plugins/history/CMakeLists.txt index 218981540..cf7ee0049 100644 --- a/plugins/history/CMakeLists.txt +++ b/plugins/history/CMakeLists.txt @@ -3,7 +3,7 @@ ) ########### next target ############### -set(kopete_history_PART_SRCS chathistoryhandler.cpp) +set(kopete_history_PART_SRCS chathistoryplugin.cpp databaseconstants.cpp databasemanager.cpp) kde4_add_plugin(kopete_history ${kopete_history_PART_SRCS} ) target_link_libraries(kopete_history ${KDE4_KHTML_LIBS} kopete ${QT_QTSQL_LIBRARY}) diff --git a/plugins/history/chathistoryhandler.cpp b/plugins/history/chathistoryhandler.cpp deleted file mode 100644 index 60844a90c..000000000 --- a/plugins/history/chathistoryhandler.cpp +++ /dev/null @@ -1,29 +0,0 @@ -#include "chathistoryhandler.h" -#include -#include -#include "kapplication.h" - -ChatHistoryHandler *ChatHistoryHandler::mInstance = 0; - -K_PLUGIN_FACTORY(HistoryPluginFactory, registerPlugin();) -K_EXPORT_PLUGIN(HistoryPluginFactory( "kopete_history" )) - -ChatHistoryHandler::ChatHistoryHandler(QObject *parent, const QVariantList &) - : Kopete::Plugin(HistoryPluginFactory::componentData(), parent) -{ - -} - -ChatHistoryHandler::~ChatHistoryHandler() -{ - -} - -ChatHistoryHandler *ChatHistoryHandler::instance() -{ - if (!mInstance) { - mInstance = new ChatHistoryHandler(0, QVariantList()); - } - - return mInstance; -} diff --git a/plugins/history/chathistoryhandler.h b/plugins/history/chathistoryhandler.h deleted file mode 100644 index 7ad3c3071..000000000 --- a/plugins/history/chathistoryhandler.h +++ /dev/null @@ -1,28 +0,0 @@ -#ifndef CHATHISTORYHANDLER_H -#define CHATHISTORYHANDLER_H - -#include -#include "kopeteplugin.h" -#include - -/** - * The ChatHistoryHandler class is used to handle all history connected activities, and - * then call the respective class (logger, searcher etc). - */ -class ChatHistoryHandler : public Kopete::Plugin -{ - Q_OBJECT -public: - /** - * Constructs a new ChatHistoryHandler class instance. There should only be one - * instance for every instance of Kopete running. - */ - explicit ChatHistoryHandler(QObject *parent, const QVariantList &); - - ~ChatHistoryHandler(); - static ChatHistoryHandler *instance(); -private: - static ChatHistoryHandler *mInstance; -}; - -#endif // CHATHISTORYHANDLER_H diff --git a/plugins/history/chathistoryplugin.cpp b/plugins/history/chathistoryplugin.cpp new file mode 100644 index 000000000..cf11151f3 --- /dev/null +++ b/plugins/history/chathistoryplugin.cpp @@ -0,0 +1,41 @@ +#include "chathistoryplugin.h" +#include "databasemanager.h" +#include "kopetemessage.h" +#include "kopetechatsessionmanager.h" +#include +#include +#include "kapplication.h" + +ChatHistoryPlugin *ChatHistoryPlugin::mInstance = 0; + +K_PLUGIN_FACTORY(ChatHistoryPluginFactory, registerPlugin();) +K_EXPORT_PLUGIN(ChatHistoryPluginFactory( "kopete_history" )) + +ChatHistoryPlugin::ChatHistoryPlugin(QObject *parent, const QVariantList &) + : Kopete::Plugin(ChatHistoryPluginFactory::componentData(), parent) +{ + //Initialize the database. + //TODO: Implement other DB Systems (MySQL, PostgreSQL etc) + DatabaseManager::instance()->initDatabase(DatabaseManager::SQLITE); + connect (Kopete::ChatSessionManager::self(), SIGNAL(aboutToDisplay(Kopete::Message&)), + this, SLOT(logMessage(Kopete::Message&))); +} + +ChatHistoryPlugin::~ChatHistoryPlugin() +{ + +} + +ChatHistoryPlugin *ChatHistoryPlugin::instance() +{ + if (!mInstance) { + mInstance = new ChatHistoryPlugin(0, QVariantList()); + } + + return mInstance; +} + +void ChatHistoryPlugin::logMessage(Kopete::Message &message) +{ + DatabaseManager::instance()->insertMessage(message); +} diff --git a/plugins/history/chathistoryplugin.h b/plugins/history/chathistoryplugin.h new file mode 100644 index 000000000..cdb831b4f --- /dev/null +++ b/plugins/history/chathistoryplugin.h @@ -0,0 +1,40 @@ +#ifndef CHATHISTORYPLUGIN_H +#define CHATHISTORYPLUGIN_H + +#include +#include "kopeteplugin.h" +#include + +namespace Kopete { +class Message; +} + +/** + * The ChatHistoryPlugin class is used to handle all history connected activities, and + * then call the respective class (logger, searcher etc). + */ +class ChatHistoryPlugin : public Kopete::Plugin +{ + Q_OBJECT +public: + /** + * Constructs a new ChatHistoryPlugin class instance. There should only be one + * instance for every instance of Kopete running. + */ + explicit ChatHistoryPlugin(QObject *parent, const QVariantList &); + + ~ChatHistoryPlugin(); + static ChatHistoryPlugin *instance(); + +public slots: + /** + * Insert a new chat message to the database. + * @param message The message to be logged. The message details to be stored + * in the database will be extracted from here. + */ + void logMessage(Kopete::Message &message); +private: + static ChatHistoryPlugin *mInstance; +}; + +#endif // CHATHISTORYPLUGIN_H diff --git a/plugins/history/databaseconstants.cpp b/plugins/history/databaseconstants.cpp new file mode 100644 index 000000000..a87f013e2 --- /dev/null +++ b/plugins/history/databaseconstants.cpp @@ -0,0 +1,140 @@ +#include "databaseconstants.h" + +QString DatabaseConstants::columnId() +{ + return "id"; +} + +QString DatabaseConstants::columnTimeStamp() +{ + return "timestamp"; +} + +QString DatabaseConstants::columnMessage() +{ + return "message"; +} + +QString DatabaseConstants::columnProtocol() +{ + return "protocol"; +} + +QString DatabaseConstants::columnAccount() +{ + return "account"; +} + +QString DatabaseConstants::columnDirection() +{ + return "direction"; +} + +QString DatabaseConstants::columnImportance() +{ + return "importance"; +} + +QString DatabaseConstants::columnContact() +{ + return "contact"; +} + +QString DatabaseConstants::columnSubject() +{ + return "subject"; +} + +QString DatabaseConstants::columnSession() +{ + return "session"; +} + +QString DatabaseConstants::columnSessionName() +{ + return "session_name"; +} + +QString DatabaseConstants::columnFrom() +{ + return "from"; +} + +QString DatabaseConstants::columnFromName() +{ + return "from_name"; +} + +QString DatabaseConstants::columnTo() +{ + return "to"; +} + +QString DatabaseConstants::columnToName() +{ + return "to_name"; +} + +QString DatabaseConstants::columnState() +{ + return "state"; +} + +QString DatabaseConstants::columnType() +{ + return "type"; +} + +QString DatabaseConstants::columnIsGroup() +{ + return "is_group"; +} + +QString DatabaseConstants::sql_prepareForMessageInsert() +{ + return "INSERT INTO `messages` (`" + columnTimeStamp() + "`, `" + columnMessage() + + "`, `" + columnAccount() + "`, `" + columnProtocol() + "`, `" + + columnDirection() + "`, `" + columnImportance() + "`, `" + + columnContact() + "`, `" + columnSubject() + "`, " + " `" + columnSession() + "`, `" + columnSessionName() + "`, `" + + columnFrom() + "`, `" + columnFromName() + "`, `" + columnTo() + + "`, `" + columnToName() + "`, `" + columnState() + "`, `" + + columnType() + "`, `" + columnIsGroup() + "`) " + " VALUES (:" + columnTimeStamp() + ", :" + columnMessage() + ", :" + + columnAccount() + ", :" + columnProtocol() + ", :" + + columnDirection() + ", :" + columnImportance() + ", :" + + columnContact() + ", :" + columnSubject() + ", :" + + columnSession() + ", :" + columnSessionName() + ", " + " :" + columnFrom() + ", :" + columnFromName() + ", :" + + columnTo() + ", :" + columnToName() + ", :" + + columnState() + ", :" + columnType() + ", :" + + columnIsGroup() + ")"; +} + +QString DatabaseConstants::sql_createMessagesTable() +{ + return "CREATE TABLE IF NOT EXISTS \"messages\" (" + "\"" + columnId() + "\" Integer Primary Key Autoincrement Not Null, " + "\"" + columnTimeStamp() + "\" Integer, " + "\"" + columnMessage() + "\" Text, " + "\"" + columnProtocol() + "\" Text Not Null, " + "\"" + columnAccount() + "\" Text Not Null, " + "\"" + columnDirection() + "\" Integer Not Null, " + "\"" + columnImportance() + "\" Integer, " + "\"" + columnContact() + "\" Text, " + "\"" + columnSubject() + "\" Text, " + "\"" + columnSession() + "\" Text, " + "\"" + columnSessionName() + "\" Text, " + "\"" + columnFrom() + "\" Text, " + "\"" + columnFromName() + "\" Text, " + "\"" + columnTo() + "\" Text, " + "\"" + columnToName() + "\" Text, " + "\"" + columnState() + "\" Integer, " + "\"" + columnType() + "\" Integer, " + "\"" + columnIsGroup() + "\" Integer Default'0') "; +} + +QString DatabaseConstants::sql_getContactList(QString accountId) +{ + return "SELECT DISTINCT contact, protocol FROM messages WHERE account = '" + accountId + "'"; +} diff --git a/plugins/history/databaseconstants.h b/plugins/history/databaseconstants.h new file mode 100644 index 000000000..898a39e07 --- /dev/null +++ b/plugins/history/databaseconstants.h @@ -0,0 +1,55 @@ +#ifndef DATABASECONSTANTS_H +#define DATABASECONSTANTS_H + +#include + +/** + * The DatabaseConstants class is used to provide static representations + * of the various strings used by the database in the history plugin. + */ +class DatabaseConstants : public QObject +{ + Q_OBJECT +public: + explicit DatabaseConstants(QObject *parent = 0); + /** + * These are the names of the various database columns + */ + static QString columnId(); + static QString columnTimeStamp(); + static QString columnMessage(); + static QString columnProtocol(); + static QString columnAccount(); + static QString columnDirection(); + static QString columnImportance(); + static QString columnContact(); + static QString columnSubject(); + static QString columnSession(); + static QString columnSessionName(); + static QString columnFrom(); + static QString columnFromName(); + static QString columnTo(); + static QString columnToName(); + static QString columnState(); + static QString columnType(); + static QString columnIsGroup(); + + /** + * Query string for inserting a new message + */ + static QString sql_prepareForMessageInsert(); + + /** + * Query string for creating the messsages table + */ + static QString sql_createMessagesTable(); + + /** + * Query string to retrieve the contacts in an account who have chat history. + * @param accountId The account to search for. + * @return + */ + static QString sql_getContactList(QString accountId = QString()); +}; + +#endif // DATABASECONSTANTS_H diff --git a/plugins/history/databasemanager.cpp b/plugins/history/databasemanager.cpp new file mode 100644 index 000000000..e9822065e --- /dev/null +++ b/plugins/history/databasemanager.cpp @@ -0,0 +1,147 @@ +#include "kapplication.h" +#include "databasemanager.h" +#include "kopetecontact.h" +#include "kopetemessage.h" +#include "kopeteaccount.h" +#include "kopetechatsession.h" +#include "kopeteprotocol.h" +#include "kopetechatsessionmanager.h" + +#include "databaseconstants.h" + +#include +#include + + +DatabaseManager* DatabaseManager::mInstance = 0; + +DatabaseManager::DatabaseManager(QObject *parent) + : QObject(parent) +{ + mInstance = this; +} + +void DatabaseManager::bindQueryValue(QSqlQuery query, QString columnName, QString value) +{ + query.bindValue(columnName, value); +} + +DatabaseManager::~DatabaseManager() +{ + +} + +void DatabaseManager::initDatabase(DatabaseManager::DatabaseType dbType) +{ + //Close the database, in case it is open. + if (db.isOpen()) { + db.close(); + } + + //Create the database tables based on the selected database system + switch (dbType) { + case SQLITE: + //For SQLite, we store the database in the user's application + //data folder. + QString dbPath = KStandardDirs::locateLocal("appdata", + "kopete_history.db"); + db = QSqlDatabase::addDatabase("QSQLITE", "kopete-history"); + db.setDatabaseName(dbPath); + + //Abort in case the database creation/opening fails. + if (!db.open()) { + //Database not open + emit error("Database not open"); + return; + } + + //If the messages table does not exist, lets create it. + db.exec(DatabaseConstants::sql_createMessagesTable()); + } +} + +void DatabaseManager::insertMessage(Kopete::Message &message) +{ + if (!db.isOpen()) { + emit error("Database not open."); + } + + QSqlQuery query(db); + + //Prepare an SQL query to insert the message to the db + query.prepare(DatabaseConstants::sql_prepareForMessageInsert()); + + //Add the values to the database fields. + bindQueryValue(query, DatabaseConstants::columnTimeStamp(), + QString::number(message.timestamp().toTime_t())); + bindQueryValue(query, DatabaseConstants::columnMessage(), + message.parsedBody()); + bindQueryValue(query, DatabaseConstants::columnAccount(), + message.manager()->account()->accountId()); + bindQueryValue(query, DatabaseConstants::columnProtocol(), + message.manager()->account()->protocol()->pluginId()); + bindQueryValue(query, DatabaseConstants::columnDirection(), + QString::number(message.direction())); + bindQueryValue(query, DatabaseConstants::columnImportance(), + QString::number(message.importance())); + + QString contact; + if (message.direction() == Kopete::Message::Outbound) { + contact = message.to().at(0)->contactId(); + } else { + contact = message.from()->contactId(); + } + bindQueryValue(query, DatabaseConstants::columnContact(), contact); + + bindQueryValue(query, DatabaseConstants::columnSubject(), + message.subject()); + bindQueryValue(query, DatabaseConstants::columnSession(), ""); + bindQueryValue(query, DatabaseConstants::columnSessionName(), ""); + bindQueryValue(query, DatabaseConstants::columnFrom(), + message.from()->contactId()); + bindQueryValue(query, DatabaseConstants::columnFromName(), + message.from()->displayName()); + + //If we are dealing with only one recepient, we save this as a single + //user chat + if (message.to().count() == 1) { + bindQueryValue(query, DatabaseConstants::columnTo(), + message.to().at(0)->contactId()); + bindQueryValue(query, DatabaseConstants::columnToName(), + message.to().at(0)->displayName()); + bindQueryValue(query, DatabaseConstants::columnIsGroup(), "0"); + } else { + //Otherwise we will create a string with the contact ids of the + //recepients, and another string to + //hold the contact names of the recepients. Both these strings + //are comma delimited. + QString to, to_name; + foreach (Kopete::Contact *c, message.to()) { + to.append(c->contactId() + ","); + to_name.append(c->displayName() + ","); + } + to.chop(1); + to_name.chop(1); + bindQueryValue(query, DatabaseConstants::columnTo(), to); + bindQueryValue(query, DatabaseConstants::columnToName(), to_name); + bindQueryValue(query, DatabaseConstants::columnIsGroup(), "1"); + } + bindQueryValue(query, DatabaseConstants::columnState(), + QString::number(message.state())); + bindQueryValue(query, DatabaseConstants::columnType(), + QString::number(message.type())); + + //Save the query to the database. + query.exec(); +} + +DatabaseManager *DatabaseManager::instance() +{ + //Check if there is an instance of this class. If there is none, a new + //one will be created. + if (!mInstance) { + mInstance = new DatabaseManager(kapp); + } + + return mInstance; +} diff --git a/plugins/history/databasemanager.h b/plugins/history/databasemanager.h new file mode 100644 index 000000000..689b1a361 --- /dev/null +++ b/plugins/history/databasemanager.h @@ -0,0 +1,83 @@ +#ifndef DATABASEMANAGER_H +#define DATABASEMANAGER_H + +#include +#include +#include + +namespace Kopete { +class Account; +class Contact; +class Message; +class Protocol; +} + +/** + * @brief The DatabaseManager class is used to manage all database connected activities + * of the history plugin. + */ +class DatabaseManager : public QObject +{ + Q_OBJECT +public: + /** + * List of the different database systems we use. SQLite is the default, + * and the user is able to select other database systems from the plugin + * preferences. + */ + enum DatabaseType { + SQLITE = 0 + }; + + /** + * Class destructor. + * */ + ~DatabaseManager(); + + /** + * Initializes the database based on the selected database format. + * This creates the database if it does not exist. For SQLite, this entails + * creating the kopete_history3.db file in the application data folder. For other + * database systems, the database is created on the server, and then all of the + * needed tables are be initialized. + * @param dbType + */ + void initDatabase(DatabaseType dbType); + + /** + * Inserts a chat message into the database. All chat related details such as + * protocol, subject, recepients etc are also logged into the database. + * @param message a Kopete::Message object containing the message to be logged into the + * database. + */ + void insertMessage(Kopete::Message &message); + + /** + * The current instance of the DatabaseManager class. + * @return the value of mInstance + */ + static DatabaseManager *instance(); +private: + /** + * existance, a new one will be created. + * @param parent The QObject parent of this class + */ + explicit DatabaseManager(QObject *parent = 0); + + /** + * QSqlDatabase containing the database used to log the chats. + */ + QSqlDatabase db; + + /** + * The current instance of the DatabaseManager class. + */ + static DatabaseManager *mInstance; + + void bindQueryValue(QSqlQuery query, QString columnName, QString value); + +signals: + void error(QString errorMessage); +}; + +#endif // DATABASEMANAGER_H