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:
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;
void setState (ChatMessage::State newState, bool force = false);
......
......@@ -70,7 +70,7 @@ void ChatMessagePrivate::setIsReadOnly (bool 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();
if (!(q->getChatRoom()->getCapabilities() & AbstractChatRoom::Capabilities::Conference)
......@@ -93,7 +93,7 @@ void ChatMessagePrivate::setParticipantState (const IdentityAddress &participant
lInfo() << "Chat message " << this << ": moving participant '" << participantAddress.asString() << "' state to "
<< Utils::toString(newState);
mainDb->setChatMessageParticipantState(eventLog, participantAddress, newState);
mainDb->setChatMessageParticipantState(eventLog, participantAddress, newState, stateChangeTime);
list<ChatMessage::State> states = mainDb->getChatMessageParticipantStates(eventLog);
size_t nbDisplayedStates = 0;
......
......@@ -170,6 +170,7 @@ void Imdn::parse (const shared_ptr<ChatMessage> &imdnMessage, xmlparsing_context
if (!cm) {
lWarning() << "Received IMDN for unknown message " << messageIdStr;
} else {
time_t imdnTime = imdnMessage->getTime();
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());
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
xmlNodePtr node = deliveryStatusObject->nodesetval->nodeTab[0];
if (node->children && node->children->name) {
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) {
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
xmlNodePtr node = displayStatusObject->nodesetval->nodeTab[0];
if (node->children && node->children->name) {
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:
long long insertChatRoom (const std::shared_ptr<AbstractChatRoom> &chatRoom);
long long insertChatRoomParticipant (long long chatRoomId, long long participantSipAddressId, bool isAdmin);
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 selectChatRoomId (long long peerSipAddressId, long long localSipAddressId) const;
......
......@@ -47,7 +47,7 @@ using namespace std;
LINPHONE_BEGIN_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 ModuleVersionLegacyFriendsImport = makeVersion(1, 0, 0);
constexpr unsigned int ModuleVersionLegacyHistoryImport = makeVersion(1, 0, 0);
......@@ -408,12 +408,12 @@ void MainDbPrivate::insertChatRoomParticipantDevice (
soci::use(participantId), soci::use(participantDeviceSipAddressId);
}
void MainDbPrivate::insertChatMessageParticipant (long long chatMessageId, long long sipAddressId, int state) {
if (state != static_cast<int>(ChatMessage::State::Displayed))
*dbSession.getBackendSession() <<
"INSERT INTO chat_message_participant (event_id, participant_sip_address_id, state)"
" VALUES (:chatMessageId, :sipAddressId, :state)",
soci::use(chatMessageId), soci::use(sipAddressId), soci::use(state);
void MainDbPrivate::insertChatMessageParticipant (long long chatMessageId, long long sipAddressId, int state, time_t stateChangeTime) {
const tm &stateChangeTm = Utils::getTimeTAsTm(stateChangeTime);
*dbSession.getBackendSession() <<
"INSERT INTO chat_message_participant (event_id, participant_sip_address_id, state, state_change_time)"
" VALUES (:chatMessageId, :sipAddressId, :state, :stateChangeTm)",
soci::use(chatMessageId), soci::use(sipAddressId), soci::use(state), soci::use(stateChangeTm);
}
// -----------------------------------------------------------------------------
......@@ -744,7 +744,7 @@ long long MainDbPrivate::insertConferenceChatMessageEvent (const shared_ptr<Even
for (const auto &participant : chatMessage->getChatRoom()->getParticipants()) {
const long long &participantSipAddressId = selectSipAddressId(participant->getAddress().asString());
insertChatMessageParticipant(eventId, participantSipAddressId, state);
insertChatMessageParticipant(eventId, participantSipAddressId, state, chatMessage->getTime());
}
return eventId;
......@@ -982,6 +982,11 @@ void MainDbPrivate::updateSchema () {
if (version < makeVersion(1, 0, 1))
*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) {
if (content)
insertContent(eventId, *content);
insertChatRoomParticipant(chatRoomId, remoteSipAddressId, false);
if (state != int(ChatMessage::State::Displayed))
insertChatMessageParticipant(eventId, remoteSipAddressId, state);
insertChatMessageParticipant(eventId, remoteSipAddressId, state, std::time(nullptr));
}
tr.commit();
lInfo() << "Successful import of legacy messages.";
......@@ -1594,26 +1597,6 @@ void MainDb::init () {
" version INT UNSIGNED NOT NULL"
") " + 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->updateModuleVersion("events", ModuleVersionEvents);
......@@ -2018,7 +2001,8 @@ ChatMessage::State MainDb::getChatMessageParticipantState (
void MainDb::setChatMessageParticipantState (
const shared_ptr<EventLog> &eventLog,
const IdentityAddress &participantAddress,
ChatMessage::State state
ChatMessage::State state,
time_t stateChangeTime
) {
L_DB_TRANSACTION {
L_D();
......@@ -2028,10 +2012,12 @@ void MainDb::setChatMessageParticipantState (
const long long &eventId = dEventKey->storageId;
const long long &participantSipAddressId = d->selectSipAddressId(participantAddress.asString());
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",
soci::use(stateInt), soci::use(eventId), soci::use(participantSipAddressId);
soci::use(stateInt), soci::use(stateChangeTm), soci::use(eventId), soci::use(participantSipAddressId);
tr.commit();
};
......
......@@ -99,7 +99,8 @@ public:
void setChatMessageParticipantState (
const std::shared_ptr<EventLog> &eventLog,
const IdentityAddress &participantAddress,
ChatMessage::State state
ChatMessage::State state,
time_t stateChangeTime
);
std::shared_ptr<ChatMessage> getLastChatMessage (const ChatRoomId &chatRoomId) const;
......
......@@ -124,6 +124,31 @@ string DbSession::varcharPrimaryKeyStr (int length) const {
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 {
L_D();
......
......@@ -47,6 +47,7 @@ public:
std::string primaryKeyRefStr (const std::string &type = "INT") const;
std::string varcharPrimaryKeyStr (int length) const;
std::string currentTimestamp () const;
std::string timestampType () 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