Commit 291bf82b authored by Ghislain MARY's avatar Ghislain MARY

Store the date of message receiving and display for each participant of a chat room in DB.

parent bace2338
...@@ -58,7 +58,7 @@ public: ...@@ -58,7 +58,7 @@ public:
void setDirection (ChatMessage::Direction dir); void setDirection (ChatMessage::Direction dir);
void setParticipantState (const IdentityAddress &participantAddress, ChatMessage::State newState); void setParticipantState (const IdentityAddress &participantAddress, ChatMessage::State newState, time_t stateChangeTime);
std::list<std::shared_ptr<Participant>> getParticipantsInState (const ChatMessage::State state) const; std::list<std::shared_ptr<Participant>> getParticipantsInState (const ChatMessage::State state) const;
void setState (ChatMessage::State newState, bool force = false); void setState (ChatMessage::State newState, bool force = false);
......
...@@ -70,7 +70,7 @@ void ChatMessagePrivate::setIsReadOnly (bool readOnly) { ...@@ -70,7 +70,7 @@ void ChatMessagePrivate::setIsReadOnly (bool readOnly) {
isReadOnly = readOnly; isReadOnly = readOnly;
} }
void ChatMessagePrivate::setParticipantState (const IdentityAddress &participantAddress, ChatMessage::State newState) { void ChatMessagePrivate::setParticipantState (const IdentityAddress &participantAddress, ChatMessage::State newState, time_t stateChangeTime) {
L_Q(); L_Q();
if (!(q->getChatRoom()->getCapabilities() & AbstractChatRoom::Capabilities::Conference) if (!(q->getChatRoom()->getCapabilities() & AbstractChatRoom::Capabilities::Conference)
...@@ -93,7 +93,7 @@ void ChatMessagePrivate::setParticipantState (const IdentityAddress &participant ...@@ -93,7 +93,7 @@ void ChatMessagePrivate::setParticipantState (const IdentityAddress &participant
lInfo() << "Chat message " << this << ": moving participant '" << participantAddress.asString() << "' state to " lInfo() << "Chat message " << this << ": moving participant '" << participantAddress.asString() << "' state to "
<< Utils::toString(newState); << Utils::toString(newState);
mainDb->setChatMessageParticipantState(eventLog, participantAddress, newState); mainDb->setChatMessageParticipantState(eventLog, participantAddress, newState, stateChangeTime);
list<ChatMessage::State> states = mainDb->getChatMessageParticipantStates(eventLog); list<ChatMessage::State> states = mainDb->getChatMessageParticipantStates(eventLog);
size_t nbDisplayedStates = 0; size_t nbDisplayedStates = 0;
......
...@@ -170,6 +170,7 @@ void Imdn::parse (const shared_ptr<ChatMessage> &imdnMessage, xmlparsing_context ...@@ -170,6 +170,7 @@ void Imdn::parse (const shared_ptr<ChatMessage> &imdnMessage, xmlparsing_context
if (!cm) { if (!cm) {
lWarning() << "Received IMDN for unknown message " << messageIdStr; lWarning() << "Received IMDN for unknown message " << messageIdStr;
} else { } else {
time_t imdnTime = imdnMessage->getTime();
LinphoneImNotifPolicy *policy = linphone_core_get_im_notif_policy(cr->getCore()->getCCore()); LinphoneImNotifPolicy *policy = linphone_core_get_im_notif_policy(cr->getCore()->getCCore());
snprintf(xpathStr, sizeof(xpathStr), "%s[1]/imdn:delivery-notification/imdn:status", imdnPrefix.c_str()); snprintf(xpathStr, sizeof(xpathStr), "%s[1]/imdn:delivery-notification/imdn:status", imdnPrefix.c_str());
xmlXPathObjectPtr deliveryStatusObject = linphone_get_xml_xpath_object_for_node_list(xmlCtx, xpathStr); xmlXPathObjectPtr deliveryStatusObject = linphone_get_xml_xpath_object_for_node_list(xmlCtx, xpathStr);
...@@ -180,9 +181,9 @@ void Imdn::parse (const shared_ptr<ChatMessage> &imdnMessage, xmlparsing_context ...@@ -180,9 +181,9 @@ void Imdn::parse (const shared_ptr<ChatMessage> &imdnMessage, xmlparsing_context
xmlNodePtr node = deliveryStatusObject->nodesetval->nodeTab[0]; xmlNodePtr node = deliveryStatusObject->nodesetval->nodeTab[0];
if (node->children && node->children->name) { if (node->children && node->children->name) {
if (strcmp((const char *)node->children->name, "delivered") == 0) { if (strcmp((const char *)node->children->name, "delivered") == 0) {
cm->getPrivate()->setParticipantState(participantAddress, ChatMessage::State::DeliveredToUser); cm->getPrivate()->setParticipantState(participantAddress, ChatMessage::State::DeliveredToUser, imdnTime);
} else if (strcmp((const char *)node->children->name, "error") == 0) { } else if (strcmp((const char *)node->children->name, "error") == 0) {
cm->getPrivate()->setParticipantState(participantAddress, ChatMessage::State::NotDelivered); cm->getPrivate()->setParticipantState(participantAddress, ChatMessage::State::NotDelivered, imdnTime);
} }
} }
} }
...@@ -193,7 +194,7 @@ void Imdn::parse (const shared_ptr<ChatMessage> &imdnMessage, xmlparsing_context ...@@ -193,7 +194,7 @@ void Imdn::parse (const shared_ptr<ChatMessage> &imdnMessage, xmlparsing_context
xmlNodePtr node = displayStatusObject->nodesetval->nodeTab[0]; xmlNodePtr node = displayStatusObject->nodesetval->nodeTab[0];
if (node->children && node->children->name) { if (node->children && node->children->name) {
if (strcmp((const char *)node->children->name, "displayed") == 0) { if (strcmp((const char *)node->children->name, "displayed") == 0) {
cm->getPrivate()->setParticipantState(participantAddress, ChatMessage::State::Displayed); cm->getPrivate()->setParticipantState(participantAddress, ChatMessage::State::Displayed, imdnTime);
} }
} }
} }
......
...@@ -61,7 +61,7 @@ private: ...@@ -61,7 +61,7 @@ private:
long long insertChatRoom (const std::shared_ptr<AbstractChatRoom> &chatRoom); long long insertChatRoom (const std::shared_ptr<AbstractChatRoom> &chatRoom);
long long insertChatRoomParticipant (long long chatRoomId, long long participantSipAddressId, bool isAdmin); long long insertChatRoomParticipant (long long chatRoomId, long long participantSipAddressId, bool isAdmin);
void insertChatRoomParticipantDevice (long long participantId, long long participantDeviceSipAddressId); void insertChatRoomParticipantDevice (long long participantId, long long participantDeviceSipAddressId);
void insertChatMessageParticipant (long long chatMessageId, long long sipAddressId, int state); void insertChatMessageParticipant (long long chatMessageId, long long sipAddressId, int state, time_t stateChangeTime);
long long selectSipAddressId (const std::string &sipAddress) const; long long selectSipAddressId (const std::string &sipAddress) const;
long long selectChatRoomId (long long peerSipAddressId, long long localSipAddressId) const; long long selectChatRoomId (long long peerSipAddressId, long long localSipAddressId) const;
......
...@@ -47,7 +47,7 @@ using namespace std; ...@@ -47,7 +47,7 @@ using namespace std;
LINPHONE_BEGIN_NAMESPACE LINPHONE_BEGIN_NAMESPACE
namespace { namespace {
constexpr unsigned int ModuleVersionEvents = makeVersion(1, 0, 1); constexpr unsigned int ModuleVersionEvents = makeVersion(1, 0, 2);
constexpr unsigned int ModuleVersionFriends = makeVersion(1, 0, 0); constexpr unsigned int ModuleVersionFriends = makeVersion(1, 0, 0);
constexpr unsigned int ModuleVersionLegacyFriendsImport = makeVersion(1, 0, 0); constexpr unsigned int ModuleVersionLegacyFriendsImport = makeVersion(1, 0, 0);
constexpr unsigned int ModuleVersionLegacyHistoryImport = makeVersion(1, 0, 0); constexpr unsigned int ModuleVersionLegacyHistoryImport = makeVersion(1, 0, 0);
...@@ -408,12 +408,12 @@ void MainDbPrivate::insertChatRoomParticipantDevice ( ...@@ -408,12 +408,12 @@ void MainDbPrivate::insertChatRoomParticipantDevice (
soci::use(participantId), soci::use(participantDeviceSipAddressId); soci::use(participantId), soci::use(participantDeviceSipAddressId);
} }
void MainDbPrivate::insertChatMessageParticipant (long long chatMessageId, long long sipAddressId, int state) { void MainDbPrivate::insertChatMessageParticipant (long long chatMessageId, long long sipAddressId, int state, time_t stateChangeTime) {
if (state != static_cast<int>(ChatMessage::State::Displayed)) const tm &stateChangeTm = Utils::getTimeTAsTm(stateChangeTime);
*dbSession.getBackendSession() << *dbSession.getBackendSession() <<
"INSERT INTO chat_message_participant (event_id, participant_sip_address_id, state)" "INSERT INTO chat_message_participant (event_id, participant_sip_address_id, state, state_change_time)"
" VALUES (:chatMessageId, :sipAddressId, :state)", " VALUES (:chatMessageId, :sipAddressId, :state, :stateChangeTm)",
soci::use(chatMessageId), soci::use(sipAddressId), soci::use(state); soci::use(chatMessageId), soci::use(sipAddressId), soci::use(state), soci::use(stateChangeTm);
} }
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
...@@ -744,7 +744,7 @@ long long MainDbPrivate::insertConferenceChatMessageEvent (const shared_ptr<Even ...@@ -744,7 +744,7 @@ long long MainDbPrivate::insertConferenceChatMessageEvent (const shared_ptr<Even
for (const auto &participant : chatMessage->getChatRoom()->getParticipants()) { for (const auto &participant : chatMessage->getChatRoom()->getParticipants()) {
const long long &participantSipAddressId = selectSipAddressId(participant->getAddress().asString()); const long long &participantSipAddressId = selectSipAddressId(participant->getAddress().asString());
insertChatMessageParticipant(eventId, participantSipAddressId, state); insertChatMessageParticipant(eventId, participantSipAddressId, state, chatMessage->getTime());
} }
return eventId; return eventId;
...@@ -982,6 +982,11 @@ void MainDbPrivate::updateSchema () { ...@@ -982,6 +982,11 @@ void MainDbPrivate::updateSchema () {
if (version < makeVersion(1, 0, 1)) if (version < makeVersion(1, 0, 1))
*session << "ALTER TABLE chat_room_participant_device ADD COLUMN state TINYINT UNSIGNED DEFAULT 0"; *session << "ALTER TABLE chat_room_participant_device ADD COLUMN state TINYINT UNSIGNED DEFAULT 0";
if (version < makeVersion(1, 0, 2)) {
*session << "DROP TRIGGER IF EXISTS chat_message_participant_deleter";
*session << "ALTER TABLE chat_message_participant ADD COLUMN state_change_time"
+ dbSession.timestampType() + " NOT NULL DEFAULT " + dbSession.currentTimestamp();
}
} }
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
...@@ -1227,9 +1232,7 @@ void MainDbPrivate::importLegacyHistory (DbSession &inDbSession) { ...@@ -1227,9 +1232,7 @@ void MainDbPrivate::importLegacyHistory (DbSession &inDbSession) {
if (content) if (content)
insertContent(eventId, *content); insertContent(eventId, *content);
insertChatRoomParticipant(chatRoomId, remoteSipAddressId, false); insertChatRoomParticipant(chatRoomId, remoteSipAddressId, false);
insertChatMessageParticipant(eventId, remoteSipAddressId, state, std::time(nullptr));
if (state != int(ChatMessage::State::Displayed))
insertChatMessageParticipant(eventId, remoteSipAddressId, state);
} }
tr.commit(); tr.commit();
lInfo() << "Successful import of legacy messages."; lInfo() << "Successful import of legacy messages.";
...@@ -1594,26 +1597,6 @@ void MainDb::init () { ...@@ -1594,26 +1597,6 @@ void MainDb::init () {
" version INT UNSIGNED NOT NULL" " version INT UNSIGNED NOT NULL"
") " + charset; ") " + charset;
if (getBackend() == Backend::Mysql) {
*session <<
"DROP TRIGGER IF EXISTS chat_message_participant_deleter";
*session <<
"CREATE TRIGGER chat_message_participant_deleter"
" AFTER UPDATE ON conference_chat_message_event FOR EACH ROW"
" BEGIN"
" IF NEW.state = " + Utils::toString(int(ChatMessage::State::Displayed)) + " THEN"
" DELETE FROM chat_message_participant WHERE event_id = NEW.event_id;"
" END IF;"
" END ";
} else
*session <<
"CREATE TRIGGER IF NOT EXISTS chat_message_participant_deleter"
" AFTER UPDATE OF state ON conference_chat_message_event FOR EACH ROW"
" WHEN NEW.state = " + Utils::toString(int(ChatMessage::State::Displayed)) +
" BEGIN"
" DELETE FROM chat_message_participant WHERE event_id = NEW.event_id;"
" END ";
d->updateSchema(); d->updateSchema();
d->updateModuleVersion("events", ModuleVersionEvents); d->updateModuleVersion("events", ModuleVersionEvents);
...@@ -2018,7 +2001,8 @@ ChatMessage::State MainDb::getChatMessageParticipantState ( ...@@ -2018,7 +2001,8 @@ ChatMessage::State MainDb::getChatMessageParticipantState (
void MainDb::setChatMessageParticipantState ( void MainDb::setChatMessageParticipantState (
const shared_ptr<EventLog> &eventLog, const shared_ptr<EventLog> &eventLog,
const IdentityAddress &participantAddress, const IdentityAddress &participantAddress,
ChatMessage::State state ChatMessage::State state,
time_t stateChangeTime
) { ) {
L_DB_TRANSACTION { L_DB_TRANSACTION {
L_D(); L_D();
...@@ -2028,10 +2012,12 @@ void MainDb::setChatMessageParticipantState ( ...@@ -2028,10 +2012,12 @@ void MainDb::setChatMessageParticipantState (
const long long &eventId = dEventKey->storageId; const long long &eventId = dEventKey->storageId;
const long long &participantSipAddressId = d->selectSipAddressId(participantAddress.asString()); const long long &participantSipAddressId = d->selectSipAddressId(participantAddress.asString());
int stateInt = static_cast<int>(state); int stateInt = static_cast<int>(state);
const tm &stateChangeTm = Utils::getTimeTAsTm(stateChangeTime);
*d->dbSession.getBackendSession() << "UPDATE chat_message_participant SET state = :state" *d->dbSession.getBackendSession() << "UPDATE chat_message_participant SET state = :state,"
" state_change_time = :stateChangeTm"
" WHERE event_id = :eventId AND participant_sip_address_id = :participantSipAddressId", " WHERE event_id = :eventId AND participant_sip_address_id = :participantSipAddressId",
soci::use(stateInt), soci::use(eventId), soci::use(participantSipAddressId); soci::use(stateInt), soci::use(stateChangeTm), soci::use(eventId), soci::use(participantSipAddressId);
tr.commit(); tr.commit();
}; };
......
...@@ -99,7 +99,8 @@ public: ...@@ -99,7 +99,8 @@ public:
void setChatMessageParticipantState ( void setChatMessageParticipantState (
const std::shared_ptr<EventLog> &eventLog, const std::shared_ptr<EventLog> &eventLog,
const IdentityAddress &participantAddress, const IdentityAddress &participantAddress,
ChatMessage::State state ChatMessage::State state,
time_t stateChangeTime
); );
std::shared_ptr<ChatMessage> getLastChatMessage (const ChatRoomId &chatRoomId) const; std::shared_ptr<ChatMessage> getLastChatMessage (const ChatRoomId &chatRoomId) const;
......
...@@ -124,6 +124,31 @@ string DbSession::varcharPrimaryKeyStr (int length) const { ...@@ -124,6 +124,31 @@ string DbSession::varcharPrimaryKeyStr (int length) const {
return ""; return "";
} }
string DbSession::currentTimestamp () const {
L_D();
switch (d->backend) {
case DbSessionPrivate::Backend::Mysql:
return " CURRENT_TIMESTAMP";
case DbSessionPrivate::Backend::Sqlite3:
// Ugly hack but Sqlite3 does not allow table alteration where we add a date column using a default value
// of CURRENT_TIMESTAMP
{
const tm &now = Utils::getTimeTAsTm(std::time(nullptr));
const size_t bufSize = 22;
char buffer[bufSize];
snprintf(buffer, bufSize, "'%d-%02d-%02d %02d:%02d:%02d'",
now.tm_year + 1900, now.tm_mon + 1, now.tm_mday, now.tm_hour, now.tm_min, now.tm_sec);
return buffer;
}
case DbSessionPrivate::Backend::None:
return "";
}
L_ASSERT(false);
return "";
}
string DbSession::timestampType () const { string DbSession::timestampType () const {
L_D(); L_D();
......
...@@ -47,6 +47,7 @@ public: ...@@ -47,6 +47,7 @@ public:
std::string primaryKeyRefStr (const std::string &type = "INT") const; std::string primaryKeyRefStr (const std::string &type = "INT") const;
std::string varcharPrimaryKeyStr (int length) const; std::string varcharPrimaryKeyStr (int length) const;
std::string currentTimestamp () const;
std::string timestampType () const; std::string timestampType () const;
std::string noLimitValue () const; std::string noLimitValue () const;
......
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