Merge lp:history-service/staging into lp:history-service

Proposed by Gustavo Pichorim Boiko
Status: Merged
Approved by: Tiago Salem Herrmann
Approved revision: 252
Merged at revision: 233
Proposed branch: lp:history-service/staging
Merge into: lp:history-service
Diff against target: 4547 lines (+2269/-272)
62 files modified
CMakeLists.txt (+1/-0)
Ubuntu/History/historyeventmodel.cpp (+17/-0)
Ubuntu/History/historyeventmodel.h (+2/-0)
Ubuntu/History/historymodel.cpp (+144/-24)
Ubuntu/History/historymodel.h (+48/-2)
Ubuntu/History/historythreadmodel.cpp (+9/-1)
Ubuntu/History/historythreadmodel.h (+3/-1)
daemon/CMakeLists.txt (+1/-0)
daemon/HistoryService.xml (+14/-0)
daemon/historydaemon.cpp (+613/-55)
daemon/historydaemon.h (+28/-9)
daemon/historyservicedbus.cpp (+25/-17)
daemon/historyservicedbus.h (+6/-1)
daemon/rolesinterface.cpp (+71/-0)
daemon/rolesinterface.h (+210/-0)
daemon/textchannelobserver.cpp (+3/-1)
daemon/textchannelobserver.h (+2/-1)
plugins/sqlite/schema/v13.sql (+44/-0)
plugins/sqlite/schema/v14.sql (+82/-0)
plugins/sqlite/schema/v15.sql (+1/-0)
plugins/sqlite/schema/v16.sql (+2/-0)
plugins/sqlite/schema/v17.sql (+2/-0)
plugins/sqlite/sqlitedatabase.cpp (+55/-1)
plugins/sqlite/sqlitedatabase.h (+1/-0)
plugins/sqlite/sqlitehistoryplugin.cpp (+478/-44)
plugins/sqlite/sqlitehistoryplugin.h (+10/-1)
plugins/sqlite/sqlitehistorythreadview.cpp (+0/-5)
src/channelobserver.cpp (+2/-1)
src/contactmatcher.cpp (+13/-3)
src/contactmatcher_p.h (+2/-2)
src/manager.cpp (+18/-2)
src/manager.h (+7/-1)
src/managerdbus.cpp (+14/-2)
src/managerdbus_p.h (+7/-1)
src/participant.cpp (+48/-6)
src/participant.h (+7/-2)
src/participant_p.h (+4/-0)
src/plugin.h (+9/-1)
src/plugineventview.cpp (+0/-1)
src/pluginthreadview.cpp (+0/-1)
src/textevent.cpp (+13/-3)
src/textevent.h (+2/-0)
src/textevent_p.h (+2/-0)
src/thread.cpp (+48/-14)
src/thread.h (+8/-2)
src/thread_p.h (+8/-2)
src/types.h (+67/-1)
src/utils.cpp (+28/-3)
src/utils_p.h (+4/-2)
tests/Ubuntu.History/HistoryEventModelTest.cpp (+1/-0)
tests/Ubuntu.History/HistoryGroupedThreadsModelTest.cpp (+1/-1)
tests/common/mock/CMakeLists.txt (+1/-1)
tests/common/mock/callchannel.cpp (+2/-1)
tests/common/mock/mockconnectiondbus.cpp (+5/-6)
tests/common/mock/textchannel.cpp (+6/-4)
tests/common/mock/textchannel.h (+1/-1)
tests/daemon/CMakeLists.txt (+4/-4)
tests/daemon/DaemonTest.cpp (+2/-2)
tests/libhistoryservice/ParticipantTest.cpp (+18/-4)
tests/libhistoryservice/TextEventTest.cpp (+26/-17)
tests/libhistoryservice/ThreadTest.cpp (+4/-2)
tests/plugins/sqlite/SqlitePluginTest.cpp (+15/-16)
To merge this branch: bzr merge lp:history-service/staging
Reviewer Review Type Date Requested Status
Roberto Mier Escandon (community) Approve
Tiago Salem Herrmann (community) Approve
Review via email: mp+308933@code.launchpad.net

Commit message

Improve group chat support.

Description of the change

Improve group chat support.

To post a comment you must log in.
Revision history for this message
Roberto Mier Escandon (rmescandon) wrote :

Left some comments

review: Needs Fixing
lp:history-service/staging updated
241. By Gustavo Pichorim Boiko

Re-enable DaemonTests.

242. By Gustavo Pichorim Boiko

Adapt the registration of objects and services to the way QtDBus works from 5.6.x on.

243. By Gustavo Pichorim Boiko

Skip self join notification in conversation when account is a ofono one

244. By Gustavo Pichorim Boiko

Fix tests on latest tp-qt.

245. By Gustavo Pichorim Boiko

Leave the mock call channel opened for a bit longer to make sure the last state change propagates correctly.

246. By Gustavo Pichorim Boiko

Allow applications to insert different kind of information events.

247. By Gustavo Pichorim Boiko

Update existing chats to Room or None based on the MMS option in Accounts Service.

248. By Gustavo Pichorim Boiko

- Create hash for threadId of broadcast messages
- Avoid grouping chats with different chatType's

249. By Gustavo Pichorim Boiko

- Fix broken tests with latest changes.
- Set ChatType correctly when using createThreadForParticipants()

Revision history for this message
Tiago Salem Herrmann (tiagosh) wrote :

just two more fixes.

review: Needs Fixing
Revision history for this message
Gustavo Pichorim Boiko (boiko) wrote :

Debug prints removed, question answered and loops changed on a separate MR.

lp:history-service/staging updated
250. By Gustavo Pichorim Boiko

Remove debug prints.

251. By Gustavo Pichorim Boiko

Use compareIds()

252. By Gustavo Pichorim Boiko

Use a more complete time format to generate the hash.

Revision history for this message
Tiago Salem Herrmann (tiagosh) wrote :

Looks good to me. thank you!

review: Approve
lp:history-service/staging updated
253. By Gustavo Pichorim Boiko

Simplify the filtering of participants.

Revision history for this message
Roberto Mier Escandon (rmescandon) wrote :

lgtm

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'CMakeLists.txt'
--- CMakeLists.txt 2015-11-03 14:25:26 +0000
+++ CMakeLists.txt 2016-11-24 12:52:24 +0000
@@ -17,6 +17,7 @@
17find_package(Qt5Qml)17find_package(Qt5Qml)
18find_package(Qt5Quick)18find_package(Qt5Quick)
19find_package(Qt5Test)19find_package(Qt5Test)
20find_package(Qt5Network)
20find_package(LibPhoneNumber REQUIRED)21find_package(LibPhoneNumber REQUIRED)
2122
22include(qt5)23include(qt5)
2324
=== modified file 'Ubuntu/History/historyeventmodel.cpp'
--- Ubuntu/History/historyeventmodel.cpp 2015-10-06 12:59:03 +0000
+++ Ubuntu/History/historyeventmodel.cpp 2016-11-24 12:52:24 +0000
@@ -45,9 +45,11 @@
45 mRoles[TextMessageAttachmentsRole] = "textMessageAttachments";45 mRoles[TextMessageAttachmentsRole] = "textMessageAttachments";
46 mRoles[TextReadTimestampRole] = "textReadTimestamp";46 mRoles[TextReadTimestampRole] = "textReadTimestamp";
47 mRoles[TextReadSubjectRole] = "textSubject";47 mRoles[TextReadSubjectRole] = "textSubject";
48 mRoles[TextInformationTypeRole] = "textInformationType";
48 mRoles[CallMissedRole] = "callMissed";49 mRoles[CallMissedRole] = "callMissed";
49 mRoles[CallDurationRole] = "callDuration";50 mRoles[CallDurationRole] = "callDuration";
50 mRoles[RemoteParticipantRole] = "remoteParticipant";51 mRoles[RemoteParticipantRole] = "remoteParticipant";
52 mRoles[SubjectAsAliasRole] = "subjectAsAlias";
51}53}
5254
53int HistoryEventModel::rowCount(const QModelIndex &parent) const55int HistoryEventModel::rowCount(const QModelIndex &parent) const
@@ -135,6 +137,11 @@
135 result = textEvent.subject();137 result = textEvent.subject();
136 }138 }
137 break;139 break;
140 case TextInformationTypeRole:
141 if (!textEvent.isNull()) {
142 result = (int)textEvent.informationType();
143 }
144 break;
138 case TextMessageAttachmentsRole:145 case TextMessageAttachmentsRole:
139 if (!textEvent.isNull()) {146 if (!textEvent.isNull()) {
140 if (mAttachmentCache.contains(textEvent)) {147 if (mAttachmentCache.contains(textEvent)) {
@@ -164,6 +171,16 @@
164 result = voiceEvent.remoteParticipant();171 result = voiceEvent.remoteParticipant();
165 }172 }
166 break;173 break;
174 case SubjectAsAliasRole:
175 if (!textEvent.isNull()) {
176 QVariantMap contactInfo = History::ContactMatcher::instance()->contactInfo(event.accountId(), textEvent.subject());
177 QString returnValue = contactInfo[History::FieldAlias].toString();
178 if (returnValue.isEmpty()) {
179 returnValue = contactInfo[History::FieldIdentifier].toString();
180 }
181 return returnValue;
182 }
183 break;
167 }184 }
168185
169 return result;186 return result;
170187
=== modified file 'Ubuntu/History/historyeventmodel.h'
--- Ubuntu/History/historyeventmodel.h 2015-10-01 18:25:36 +0000
+++ Ubuntu/History/historyeventmodel.h 2016-11-24 12:52:24 +0000
@@ -44,10 +44,12 @@
44 TextMessageStatusRole,44 TextMessageStatusRole,
45 TextReadTimestampRole,45 TextReadTimestampRole,
46 TextReadSubjectRole,46 TextReadSubjectRole,
47 TextInformationTypeRole,
47 TextMessageAttachmentsRole,48 TextMessageAttachmentsRole,
48 CallMissedRole,49 CallMissedRole,
49 CallDurationRole,50 CallDurationRole,
50 RemoteParticipantRole,51 RemoteParticipantRole,
52 SubjectAsAliasRole,
51 LastEventRole53 LastEventRole
52 };54 };
5355
5456
=== modified file 'Ubuntu/History/historymodel.cpp'
--- Ubuntu/History/historymodel.cpp 2015-10-08 21:52:59 +0000
+++ Ubuntu/History/historymodel.cpp 2016-11-24 12:52:24 +0000
@@ -1,5 +1,5 @@
1/*1/*
2 * Copyright (C) 2013-2014 Canonical, Ltd.2 * Copyright (C) 2013-2016 Canonical, Ltd.
3 *3 *
4 * Authors:4 * Authors:
5 * Gustavo Pichorim Boiko <gustavo.boiko@canonical.com>5 * Gustavo Pichorim Boiko <gustavo.boiko@canonical.com>
@@ -40,7 +40,10 @@
40 mRoles[AccountIdRole] = "accountId";40 mRoles[AccountIdRole] = "accountId";
41 mRoles[ThreadIdRole] = "threadId";41 mRoles[ThreadIdRole] = "threadId";
42 mRoles[ParticipantsRole] = "participants";42 mRoles[ParticipantsRole] = "participants";
43 mRoles[ParticipantsRemotePendingRole] = "remotePendingParticipants";
44 mRoles[ParticipantsLocalPendingRole] = "localPendingParticipants";
43 mRoles[TypeRole] = "type";45 mRoles[TypeRole] = "type";
46 mRoles[TimestampRole] = "timestamp";
44 mRoles[PropertiesRole] = "properties";47 mRoles[PropertiesRole] = "properties";
4548
46 connect(this, SIGNAL(rowsInserted(QModelIndex,int,int)), this, SIGNAL(countChanged()));49 connect(this, SIGNAL(rowsInserted(QModelIndex,int,int)), this, SIGNAL(countChanged()));
@@ -89,16 +92,79 @@
89 case TypeRole:92 case TypeRole:
90 result = properties[History::FieldType];93 result = properties[History::FieldType];
91 break;94 break;
92 case ParticipantsRole:95 case ParticipantsRole: {
93 if (mMatchContacts) {96 History::Participants participants = History::Participants::fromVariantList(properties[History::FieldParticipants].toList())
94 result = History::ContactMatcher::instance()->contactInfo(properties[History::FieldAccountId].toString(),97 .filterByState(History::ParticipantStateRegular);
95 History::Participants::fromVariantList(properties[History::FieldParticipants].toList()).identifiers());98 if (mMatchContacts) {
96 } else {99 QVariantList finalParticipantsList;
97 //FIXME: handle contact changes100 QVariantList participantsInfo = History::ContactMatcher::instance()->contactInfo(properties[History::FieldAccountId].toString(),
98 result = properties[History::FieldParticipants];101 participants.identifiers());
99 }102 for (int i = 0; i < participantsInfo.count(); ++i) {
100 break;103 QVariantMap newMap = participantsInfo[i].toMap();
101 }104 History::Participant participant = participants[i];
105 newMap[History::FieldParticipantState] = participant.state();
106 newMap[History::FieldParticipantRoles] = participant.roles();
107 finalParticipantsList << newMap;
108 }
109 result = finalParticipantsList;
110 } else {
111 //FIXME: handle contact changes
112 result = participants.identifiers();
113 }
114 break;
115 }
116 case ParticipantsRemotePendingRole: {
117 History::Participants participants = History::Participants::fromVariantList(properties[History::FieldParticipants].toList())
118 .filterByState(History::ParticipantStateRemotePending);
119 if (mMatchContacts) {
120 QVariantList finalParticipantsList;
121 QVariantList participantsInfo = History::ContactMatcher::instance()->contactInfo(properties[History::FieldAccountId].toString(),
122 participants.identifiers());
123 int count = 0;
124 Q_FOREACH(const QVariant &participantInfo, participantsInfo) {
125 QVariantMap newMap = participantInfo.toMap();
126 newMap[History::FieldParticipantState] = participants.at(count).state();
127 newMap[History::FieldParticipantRoles] = participants.at(count++).roles();
128 finalParticipantsList << newMap;
129 }
130 result = finalParticipantsList;
131 } else {
132 //FIXME: handle contact changes
133 result = participants.identifiers();
134 }
135
136 break;
137 }
138 case ParticipantsLocalPendingRole: {
139 History::Participants participants = History::Participants::fromVariantList(properties[History::FieldParticipants].toList())
140 .filterByState(History::ParticipantStateLocalPending);
141 if (mMatchContacts) {
142 QVariantList finalParticipantsList;
143 QVariantList participantsInfo = History::ContactMatcher::instance()->contactInfo(properties[History::FieldAccountId].toString(),
144 participants.identifiers());
145 int count = 0;
146 Q_FOREACH(const QVariant &participantInfo, participantsInfo) {
147 QVariantMap newMap = participantInfo.toMap();
148 newMap[History::FieldParticipantState] = participants.at(count).state();
149 newMap[History::FieldParticipantRoles] = participants.at(count++).roles();
150 finalParticipantsList << newMap;
151 }
152 result = finalParticipantsList;
153 } else {
154 //FIXME: handle contact changes
155 result = participants.identifiers();
156 }
157
158 break;
159 }
160 case ParticipantIdsRole:
161 result = History::Participants::fromVariantList(properties[History::FieldParticipants].toList()).identifiers();
162 break;
163 case TimestampRole:
164 result = QDateTime::fromString(properties[History::FieldTimestamp].toString(), Qt::ISODate);
165 break;
166 }
167
102 return result;168 return result;
103}169}
104170
@@ -187,17 +253,66 @@
187 }253 }
188}254}
189255
256QVariantMap HistoryModel::threadForProperties(const QString &accountId, int eventType, const QVariantMap &properties, int matchFlags, bool create)
257{
258 QVariantMap newProperties = properties;
259 if (properties.isEmpty()) {
260 return QVariantMap();
261 }
262
263 if (newProperties.contains(History::FieldParticipantIds)) {
264 newProperties[History::FieldParticipantIds] = newProperties[History::FieldParticipantIds].toStringList();
265 }
266
267 History::Thread thread = History::Manager::instance()->threadForProperties(accountId,
268 (History::EventType)eventType,
269 newProperties,
270 (History::MatchFlags)matchFlags,
271 create);
272 if (!thread.isNull()) {
273 return thread.properties();
274 }
275
276 return QVariantMap();
277}
278
279QString HistoryModel::threadIdForProperties(const QString &accountId, int eventType, const QVariantMap &properties, int matchFlags, bool create)
280{
281 QVariantMap newProperties = properties;
282 if (properties.isEmpty()) {
283 return QString::null;
284 }
285
286 if (newProperties.contains(History::FieldParticipantIds)) {
287 newProperties[History::FieldParticipantIds] = newProperties[History::FieldParticipantIds].toStringList();
288 }
289
290 History::Thread thread = History::Manager::instance()->threadForProperties(accountId,
291 (History::EventType)eventType,
292 newProperties,
293 (History::MatchFlags)matchFlags,
294 create);
295 if (!thread.isNull()) {
296 return thread.threadId();
297 }
298
299 return QString::null;
300}
301
190QVariantMap HistoryModel::threadForParticipants(const QString &accountId, int eventType, const QStringList &participants, int matchFlags, bool create)302QVariantMap HistoryModel::threadForParticipants(const QString &accountId, int eventType, const QStringList &participants, int matchFlags, bool create)
191{303{
192 if (participants.isEmpty()) {304 if (participants.isEmpty()) {
193 return QVariantMap();305 return QVariantMap();
194 }306 }
195307
196 History::Thread thread = History::Manager::instance()->threadForParticipants(accountId,308 QVariantMap properties;
197 (History::EventType)eventType,309 properties[History::FieldParticipantIds] = participants;
198 participants,310
199 (History::MatchFlags)matchFlags,311 History::Thread thread = History::Manager::instance()->threadForProperties(accountId,
200 create);312 (History::EventType)eventType,
313 properties,
314 (History::MatchFlags)matchFlags,
315 create);
201 if (!thread.isNull()) {316 if (!thread.isNull()) {
202 return thread.properties();317 return thread.properties();
203 }318 }
@@ -211,11 +326,14 @@
211 return QString::null;326 return QString::null;
212 }327 }
213328
214 History::Thread thread = History::Manager::instance()->threadForParticipants(accountId,329 QVariantMap properties;
215 (History::EventType)eventType,330 properties[History::FieldParticipantIds] = participants;
216 participants,331
217 (History::MatchFlags)matchFlags,332 History::Thread thread = History::Manager::instance()->threadForProperties(accountId,
218 create);333 (History::EventType)eventType,
334 properties,
335 (History::MatchFlags)matchFlags,
336 create);
219 if (!thread.isNull()) {337 if (!thread.isNull()) {
220 return thread.threadId();338 return thread.threadId();
221 }339 }
@@ -223,7 +341,7 @@
223 return QString::null;341 return QString::null;
224}342}
225343
226bool HistoryModel::writeTextInformationEvent(const QString &accountId, const QString &threadId, const QStringList &participants, const QString &message)344bool HistoryModel::writeTextInformationEvent(const QString &accountId, const QString &threadId, const QStringList &participants, const QString &message, int informationType, const QString &subject)
227{345{
228 if (participants.isEmpty() || threadId.isEmpty() || accountId.isEmpty()) {346 if (participants.isEmpty() || threadId.isEmpty() || accountId.isEmpty()) {
229 return false;347 return false;
@@ -232,7 +350,7 @@
232 History::TextEvent historyEvent = History::TextEvent(accountId,350 History::TextEvent historyEvent = History::TextEvent(accountId,
233 threadId,351 threadId,
234 QString(QCryptographicHash::hash(QByteArray(352 QString(QCryptographicHash::hash(QByteArray(
235 QDateTime::currentDateTime().toString().toLatin1()), 353 QDateTime::currentDateTimeUtc().toString("yyyyMMddhhmmsszzz").toLatin1()),
236 QCryptographicHash::Md5).toHex()),354 QCryptographicHash::Md5).toHex()),
237 "self",355 "self",
238 QDateTime::currentDateTime(),356 QDateTime::currentDateTime(),
@@ -240,7 +358,9 @@
240 message,358 message,
241 History::MessageTypeInformation,359 History::MessageTypeInformation,
242 History::MessageStatusUnknown,360 History::MessageStatusUnknown,
243 QDateTime::currentDateTime());361 QDateTime::currentDateTime(),
362 subject,
363 (History::InformationType)informationType);
244 History::Events events;364 History::Events events;
245 events << historyEvent;365 events << historyEvent;
246 return History::Manager::instance()->writeEvents(events);366 return History::Manager::instance()->writeEvents(events);
247367
=== modified file 'Ubuntu/History/historymodel.h'
--- Ubuntu/History/historymodel.h 2015-10-08 21:47:42 +0000
+++ Ubuntu/History/historymodel.h 2016-11-24 12:52:24 +0000
@@ -1,5 +1,5 @@
1/*1/*
2 * Copyright (C) 2013-2014 Canonical, Ltd.2 * Copyright (C) 2013-2016 Canonical, Ltd.
3 *3 *
4 * Authors:4 * Authors:
5 * Gustavo Pichorim Boiko <gustavo.boiko@canonical.com>5 * Gustavo Pichorim Boiko <gustavo.boiko@canonical.com>
@@ -39,14 +39,22 @@
39 Q_PROPERTY(EventType type READ type WRITE setType NOTIFY typeChanged)39 Q_PROPERTY(EventType type READ type WRITE setType NOTIFY typeChanged)
40 Q_PROPERTY(bool matchContacts READ matchContacts WRITE setMatchContacts NOTIFY matchContactsChanged)40 Q_PROPERTY(bool matchContacts READ matchContacts WRITE setMatchContacts NOTIFY matchContactsChanged)
41 Q_PROPERTY(bool canFetchMore READ canFetchMore NOTIFY canFetchMoreChanged)41 Q_PROPERTY(bool canFetchMore READ canFetchMore NOTIFY canFetchMoreChanged)
42 Q_ENUMS(ChatType)
42 Q_ENUMS(EventType)43 Q_ENUMS(EventType)
43 Q_ENUMS(MessageType)44 Q_ENUMS(MessageType)
44 Q_ENUMS(MatchFlag)45 Q_ENUMS(MatchFlag)
45 Q_ENUMS(MessageStatus)46 Q_ENUMS(MessageStatus)
46 Q_ENUMS(AttachmentFlag)47 Q_ENUMS(AttachmentFlag)
47 Q_ENUMS(Role)48 Q_ENUMS(Role)
49 Q_ENUMS(InformationType)
4850
49public:51public:
52 enum ChatType {
53 ChatTypeNone = History::ChatTypeNone,
54 ChatTypeContact = History::ChatTypeContact,
55 ChatTypeRoom = History::ChatTypeRoom
56 };
57
50 enum EventType {58 enum EventType {
51 EventTypeText = History::EventTypeText,59 EventTypeText = History::EventTypeText,
52 EventTypeVoice = History::EventTypeVoice60 EventTypeVoice = History::EventTypeVoice
@@ -83,12 +91,36 @@
83 AttachmentPending = History::AttachmentPending,91 AttachmentPending = History::AttachmentPending,
84 AttachmentError = History::AttachmentError92 AttachmentError = History::AttachmentError
85 };93 };
94
95 enum InformationType
96 {
97 InformationTypeNone = History::InformationTypeNone,
98 InformationTypeSimChange = History::InformationTypeSimChange,
99 InformationTypeText = History::InformationTypeText,
100 InformationTypeSelfJoined = History::InformationTypeSelfJoined,
101 InformationTypeJoined = History::InformationTypeJoined,
102 InformationTypeTitleChanged = History::InformationTypeTitleChanged,
103 InformationTypeInvitationSent = History::InformationTypeInvitationSent,
104 InformationTypeLeaving = History::InformationTypeLeaving,
105 InformationTypeSelfLeaving = History::InformationTypeSelfLeaving,
106 InformationTypeAdminGranted = History::InformationTypeAdminGranted,
107 InformationTypeAdminRemoved = History::InformationTypeAdminRemoved,
108 InformationTypeSelfAdminGranted = History::InformationTypeSelfAdminGranted,
109 InformationTypeSelfAdminRemoved = History::InformationTypeSelfAdminRemoved,
110 InformationTypeSelfKicked = History::InformationTypeSelfKicked,
111 InformationTypeGroupGone = History::InformationTypeGroupGone
112 };
113
86 114
87 enum Role {115 enum Role {
88 AccountIdRole = Qt::UserRole,116 AccountIdRole = Qt::UserRole,
89 ThreadIdRole,117 ThreadIdRole,
90 ParticipantsRole,118 ParticipantsRole,
119 ParticipantsLocalPendingRole,
120 ParticipantsRemotePendingRole,
121 ParticipantIdsRole,
91 TypeRole,122 TypeRole,
123 TimestampRole,
92 PropertiesRole,124 PropertiesRole,
93 LastRole125 LastRole
94 };126 };
@@ -112,6 +144,18 @@
112 bool matchContacts() const;144 bool matchContacts() const;
113 void setMatchContacts(bool value);145 void setMatchContacts(bool value);
114146
147 Q_INVOKABLE QVariantMap threadForProperties(const QString &accountId,
148 int eventType,
149 const QVariantMap &properties,
150 int matchFlags = (int)History::MatchCaseSensitive,
151 bool create = false);
152
153 Q_INVOKABLE QString threadIdForProperties(const QString &accountId,
154 int eventType,
155 const QVariantMap &properties,
156 int matchFlags = (int)History::MatchCaseSensitive,
157 bool create = false);
158
115 Q_INVOKABLE QVariantMap threadForParticipants(const QString &accountId,159 Q_INVOKABLE QVariantMap threadForParticipants(const QString &accountId,
116 int eventType,160 int eventType,
117 const QStringList &participants,161 const QStringList &participants,
@@ -125,7 +169,9 @@
125 Q_INVOKABLE bool writeTextInformationEvent(const QString &accountId,169 Q_INVOKABLE bool writeTextInformationEvent(const QString &accountId,
126 const QString &threadId,170 const QString &threadId,
127 const QStringList &participants,171 const QStringList &participants,
128 const QString &message);172 const QString &message,
173 int informationType = (int)History::InformationTypeNone,
174 const QString &subject = QString());
129175
130 Q_INVOKABLE virtual QVariant get(int row) const;176 Q_INVOKABLE virtual QVariant get(int row) const;
131177
132178
=== modified file 'Ubuntu/History/historythreadmodel.cpp'
--- Ubuntu/History/historythreadmodel.cpp 2015-10-08 19:35:40 +0000
+++ Ubuntu/History/historythreadmodel.cpp 2016-11-24 12:52:24 +0000
@@ -1,5 +1,5 @@
1/*1/*
2 * Copyright (C) 2013-2015 Canonical, Ltd.2 * Copyright (C) 2013-2016 Canonical, Ltd.
3 *3 *
4 * Authors:4 * Authors:
5 * Gustavo Pichorim Boiko <gustavo.boiko@canonical.com>5 * Gustavo Pichorim Boiko <gustavo.boiko@canonical.com>
@@ -38,6 +38,8 @@
38 mRoles = HistoryModel::roleNames();38 mRoles = HistoryModel::roleNames();
39 mRoles[CountRole] = "count";39 mRoles[CountRole] = "count";
40 mRoles[UnreadCountRole] = "unreadCount";40 mRoles[UnreadCountRole] = "unreadCount";
41 mRoles[ChatType] = "chatType";
42 mRoles[ChatRoomInfo] = "chatRoomInfo";
4143
42 // roles related to the thread´s last event44 // roles related to the thread´s last event
43 mRoles[LastEventIdRole] = "eventId";45 mRoles[LastEventIdRole] = "eventId";
@@ -104,6 +106,12 @@
104 case UnreadCountRole:106 case UnreadCountRole:
105 result = thread.unreadCount();107 result = thread.unreadCount();
106 break;108 break;
109 case ChatType:
110 result = thread.chatType();
111 break;
112 case ChatRoomInfo:
113 result = thread.chatRoomInfo();
114 break;
107 case PropertiesRole:115 case PropertiesRole:
108 result = thread.properties();116 result = thread.properties();
109 break;117 break;
110118
=== modified file 'Ubuntu/History/historythreadmodel.h'
--- Ubuntu/History/historythreadmodel.h 2015-09-29 14:28:17 +0000
+++ Ubuntu/History/historythreadmodel.h 2016-11-24 12:52:24 +0000
@@ -1,5 +1,5 @@
1/*1/*
2 * Copyright (C) 2013-2015 Canonical, Ltd.2 * Copyright (C) 2013-2016 Canonical, Ltd.
3 *3 *
4 * Authors:4 * Authors:
5 * Gustavo Pichorim Boiko <gustavo.boiko@canonical.com>5 * Gustavo Pichorim Boiko <gustavo.boiko@canonical.com>
@@ -40,6 +40,8 @@
40 enum ThreadRole {40 enum ThreadRole {
41 CountRole = HistoryModel::LastRole,41 CountRole = HistoryModel::LastRole,
42 UnreadCountRole,42 UnreadCountRole,
43 ChatType,
44 ChatRoomInfo,
43 LastEventIdRole,45 LastEventIdRole,
44 LastEventSenderIdRole,46 LastEventSenderIdRole,
45 LastEventTimestampRole,47 LastEventTimestampRole,
4648
=== modified file 'daemon/CMakeLists.txt'
--- daemon/CMakeLists.txt 2015-09-30 13:17:52 +0000
+++ daemon/CMakeLists.txt 2016-11-24 12:52:24 +0000
@@ -4,6 +4,7 @@
4 historydaemon.cpp4 historydaemon.cpp
5 historyservicedbus.cpp5 historyservicedbus.cpp
6 pluginmanager.cpp6 pluginmanager.cpp
7 rolesinterface.cpp
7 textchannelobserver.cpp8 textchannelobserver.cpp
8 )9 )
910
1011
=== modified file 'daemon/HistoryService.xml'
--- daemon/HistoryService.xml 2015-09-23 15:08:07 +0000
+++ daemon/HistoryService.xml 2016-11-24 12:52:24 +0000
@@ -9,6 +9,20 @@
9 <dox:d>9 <dox:d>
10 An interface to the history service10 An interface to the history service
11 </dox:d>11 </dox:d>
12 <method name="ThreadForProperties">
13 <dox:d><![CDATA[
14 Return an existing thread for the given parameters.
15 ]]></dox:d>
16 <arg name="accountId" type="s" direction="in"/>
17 <arg name="type" type="i" direction="in"/>
18 <arg name="properties" type="a{sv}" direction="in"/>
19 <arg name="matchFlags" type="i" direction="in"/>
20 <arg name="create" type="b" direction="in"/>
21 <arg type="a{sv}" direction="out"/>
22 <annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="QVariantMap"/>
23 <annotation name="org.qtproject.QtDBus.QtTypeName.In2" value="QVariantMap"/>
24 </method>
25
12 <method name="ThreadForParticipants">26 <method name="ThreadForParticipants">
13 <dox:d><![CDATA[27 <dox:d><![CDATA[
14 Return an existing thread for the given parameters.28 Return an existing thread for the given parameters.
1529
=== modified file 'daemon/historydaemon.cpp'
--- daemon/historydaemon.cpp 2015-11-20 12:53:49 +0000
+++ daemon/historydaemon.cpp 2016-11-24 12:52:24 +0000
@@ -1,5 +1,5 @@
1/*1/*
2 * Copyright (C) 2013-2015 Canonical, Ltd.2 * Copyright (C) 2013-2016 Canonical, Ltd.
3 *3 *
4 * Authors:4 * Authors:
5 * Gustavo Pichorim Boiko <gustavo.boiko@canonical.com>5 * Gustavo Pichorim Boiko <gustavo.boiko@canonical.com>
@@ -23,20 +23,97 @@
23#include "telepathyhelper_p.h"23#include "telepathyhelper_p.h"
24#include "filter.h"24#include "filter.h"
25#include "sort.h"25#include "sort.h"
26#include "utils_p.h"
2627
27#include "pluginmanager.h"28#include "pluginmanager.h"
28#include "plugin.h"29#include "plugin.h"
29#include "pluginthreadview.h"30#include "pluginthreadview.h"
30#include "plugineventview.h"31#include "plugineventview.h"
32#include "textevent.h"
3133
32#include <QStandardPaths>34#include <QStandardPaths>
33#include <QCryptographicHash>35#include <QCryptographicHash>
34#include <TelepathyQt/CallChannel>36#include <TelepathyQt/CallChannel>
37#include <TelepathyQt/PendingVariantMap>
35#include <TelepathyQt/ReferencedHandles>38#include <TelepathyQt/ReferencedHandles>
3639
40#include <TelepathyQt/PendingVariant>
41#include <TelepathyQt/PendingOperation>
42
43Q_DECLARE_METATYPE(RolesMap)
44
45const constexpr static int AdminRole = 2;
46
47enum ChannelGroupChangeReason
48{
49 ChannelGroupChangeReasonNone = 0,
50 ChannelGroupChangeReasonOffline = 1,
51 ChannelGroupChangeReasonKicked = 2,
52 ChannelGroupChangeReasonBusy = 3,
53 ChannelGroupChangeReasonInvited = 4,
54 ChannelGroupChangeReasonBanned = 5,
55 ChannelGroupChangeReasonError = 6,
56 ChannelGroupChangeReasonInvalidContact = 7,
57 ChannelGroupChangeReasonNoAnswer = 8,
58 ChannelGroupChangeReasonRenamed = 9,
59 ChannelGroupChangeReasonPermissionDenied = 10,
60 ChannelGroupChangeReasonSeparated = 11,
61
62 // additional enum values not included in original ChannelGroupChangeReason
63 // telepathy enumeration but needed here to provide extra info to client when group
64 // is cancelled
65 ChannelGroupChangeReasonGone = 12,
66 ChannelGroupChangeReasonRejected = 13
67};
68
69const QDBusArgument &operator>>(const QDBusArgument &argument, RolesMap &roles)
70{
71 argument.beginMap();
72 while ( !argument.atEnd() ) {
73 argument.beginMapEntry();
74 uint key,value;
75 argument >> key >> value;
76 argument.endMapEntry();
77 roles[key] = value;
78 }
79
80 argument.endMap();
81 return argument;
82}
83
84bool foundAsMemberInThread(const Tp::ContactPtr& contact, QVariantMap thread)
85{
86 Q_FOREACH (QVariant participant, thread[History::FieldParticipants].toList()) {
87 // found if same identifier and as member into thread info
88 if (History::Utils::compareIds(thread[History::FieldAccountId].toString(),
89 contact->id(),
90 participant.toMap()[History::FieldIdentifier].toString()) &&
91 participant.toMap()[History::FieldParticipantState].toUInt() == History::ParticipantStateRegular)
92 {
93 return true;
94 }
95 }
96 return false;
97}
98
99bool foundInThread(const Tp::ContactPtr& contact, QVariantMap thread)
100{
101 Q_FOREACH (QVariant participant, thread[History::FieldParticipants].toList()) {
102 if (History::Utils::compareIds(thread[History::FieldAccountId].toString(),
103 contact->id(),
104 participant.toMap()[History::FieldIdentifier].toString()))
105 {
106 return true;
107 }
108 }
109 return false;
110}
111
37HistoryDaemon::HistoryDaemon(QObject *parent)112HistoryDaemon::HistoryDaemon(QObject *parent)
38 : QObject(parent), mCallObserver(this), mTextObserver(this)113 : QObject(parent), mCallObserver(this), mTextObserver(this)
39{114{
115 qRegisterMetaType<HandleRolesMap>();
116 qDBusRegisterMetaType<HandleRolesMap>();
40 // get the first plugin117 // get the first plugin
41 if (!History::PluginManager::instance()->plugins().isEmpty()) {118 if (!History::PluginManager::instance()->plugins().isEmpty()) {
42 mBackend = History::PluginManager::instance()->plugins().first();119 mBackend = History::PluginManager::instance()->plugins().first();
@@ -65,6 +142,9 @@
65 connect(&mTextObserver,142 connect(&mTextObserver,
66 SIGNAL(messageRead(Tp::TextChannelPtr,Tp::ReceivedMessage)),143 SIGNAL(messageRead(Tp::TextChannelPtr,Tp::ReceivedMessage)),
67 SLOT(onMessageRead(Tp::TextChannelPtr,Tp::ReceivedMessage)));144 SLOT(onMessageRead(Tp::TextChannelPtr,Tp::ReceivedMessage)));
145 connect(&mTextObserver,
146 SIGNAL(channelAvailable(Tp::TextChannelPtr)),
147 SLOT(onTextChannelAvailable(Tp::TextChannelPtr)));
68148
69 // FIXME: we need to do this in a better way, but for now this should do149 // FIXME: we need to do this in a better way, but for now this should do
70 mProtocolFlags["ofono"] = History::MatchPhoneNumber;150 mProtocolFlags["ofono"] = History::MatchPhoneNumber;
@@ -81,37 +161,153 @@
81 return self;161 return self;
82}162}
83163
84QStringList HistoryDaemon::participantsFromChannel(const Tp::TextChannelPtr &textChannel)164void HistoryDaemon::onRolesChanged(const HandleRolesMap &added, const HandleRolesMap &removed)
85{165{
86 QStringList participants;166 Q_UNUSED(added);
167 Q_UNUSED(removed);
168
169 ChannelInterfaceRolesInterface *roles_interface = qobject_cast<ChannelInterfaceRolesInterface*>(sender());
170 RolesMap roles = roles_interface->getRoles();
171
172 Tp::TextChannelPtr channel(qobject_cast<Tp::TextChannel*>(sender()->parent()));
173 QVariantMap properties = propertiesFromChannel(channel);
174 QVariantMap thread = threadForProperties(channel->property(History::FieldAccountId).toString(),
175 History::EventTypeText,
176 properties,
177 matchFlagsForChannel(channel),
178 false);
179
180 writeRolesInformationEvents(thread, channel, roles);
181
182 updateRoomRoles(channel, roles);
183}
184
185QVariantMap HistoryDaemon::propertiesFromChannel(const Tp::ChannelPtr &textChannel)
186{
187 QVariantMap properties;
188 QVariantList participants;
189 QStringList participantIds;
190
191 ChannelInterfaceRolesInterface *roles_interface = textChannel->optionalInterface<ChannelInterfaceRolesInterface>();
192 RolesMap roles;
193 if (roles_interface) {
194 roles = roles_interface->getRoles();
195 }
196
87 Q_FOREACH(const Tp::ContactPtr contact, textChannel->groupContacts(false)) {197 Q_FOREACH(const Tp::ContactPtr contact, textChannel->groupContacts(false)) {
88 participants << contact->id();198 QVariantMap contactProperties;
199 contactProperties[History::FieldAlias] = contact->alias();
200 contactProperties[History::FieldAccountId] = textChannel->property(History::FieldAccountId).toString();
201 contactProperties[History::FieldIdentifier] = contact->id();
202 contactProperties[History::FieldParticipantState] = History::ParticipantStateRegular;
203 contactProperties[History::FieldParticipantRoles] = roles[contact->handle().at(0)];
204 participantIds << contact->id();
205 participants << contactProperties;
206 }
207
208 Q_FOREACH(const Tp::ContactPtr contact, textChannel->groupRemotePendingContacts(false)) {
209 QVariantMap contactProperties;
210 contactProperties[History::FieldAlias] = contact->alias();
211 contactProperties[History::FieldAccountId] = textChannel->property(History::FieldAccountId).toString();
212 contactProperties[History::FieldIdentifier] = contact->id();
213 contactProperties[History::FieldParticipantState] = History::ParticipantStateRemotePending;
214 contactProperties[History::FieldParticipantRoles] = roles[contact->handle().at(0)];
215 participantIds << contact->id();
216 participants << contactProperties;
217 }
218
219 Q_FOREACH(const Tp::ContactPtr contact, textChannel->groupLocalPendingContacts(false)) {
220 QVariantMap contactProperties;
221 contactProperties[History::FieldAlias] = contact->alias();
222 contactProperties[History::FieldAccountId] = textChannel->property(History::FieldAccountId).toString();
223 contactProperties[History::FieldIdentifier] = contact->id();
224 contactProperties[History::FieldParticipantState] = History::ParticipantStateLocalPending;
225 contactProperties[History::FieldParticipantRoles] = roles[contact->handle().at(0)];
226 participantIds << contact->id();
227 participants << contactProperties;
89 }228 }
90229
91 if (participants.isEmpty() && textChannel->targetHandleType() == Tp::HandleTypeContact &&230 if (participants.isEmpty() && textChannel->targetHandleType() == Tp::HandleTypeContact &&
92 textChannel->targetContact() == textChannel->connection()->selfContact()) {231 textChannel->targetContact() == textChannel->connection()->selfContact()) {
93 participants << textChannel->targetContact()->id();232 QVariantMap contactProperties;
94 }233 contactProperties[History::FieldAlias] = textChannel->targetContact()->alias();
95 return participants;234 contactProperties[History::FieldAccountId] = textChannel->property(History::FieldAccountId).toString();
235 contactProperties[History::FieldIdentifier] = textChannel->targetContact()->id();
236 contactProperties[History::FieldParticipantState] = History::ParticipantStateRegular;
237 participantIds << textChannel->targetContact()->id();
238 participants << contactProperties;
239 }
240
241 // We map chatType directly from telepathy targetHandleType: None, Contact, Room
242 properties[History::FieldChatType] = textChannel->targetHandleType();
243 properties[History::FieldParticipants] = participants;
244 properties[History::FieldParticipantIds] = participantIds;
245
246 QVariantMap roomProperties;
247 switch(textChannel->targetHandleType()) {
248 case Tp::HandleTypeRoom:
249 if (textChannel->hasInterface(TP_QT_IFACE_CHANNEL_INTERFACE_ROOM)) {
250 auto room_interface = textChannel->optionalInterface<Tp::Client::ChannelInterfaceRoomInterface>();
251 QVariantMap map = getInterfaceProperties(room_interface);
252 for (QVariantMap::const_iterator iter = map.begin(); iter != map.end(); ++iter) {
253 if (iter.value().isValid()) {
254 roomProperties[iter.key()] = iter.value();
255 }
256 }
257 }
258 if (textChannel->hasInterface(TP_QT_IFACE_CHANNEL_INTERFACE_ROOM_CONFIG)) {
259 auto room_config_interface = textChannel->optionalInterface<Tp::Client::ChannelInterfaceRoomConfigInterface>();
260 QVariantMap map = getInterfaceProperties(room_config_interface);
261 for (QVariantMap::const_iterator iter = map.begin(); iter != map.end(); ++iter) {
262 if (iter.value().isValid()) {
263 roomProperties[iter.key()] = iter.value();
264 }
265 }
266 }
267 if (textChannel->hasInterface(TP_QT_IFACE_CHANNEL_INTERFACE_SUBJECT)) {
268 auto subject_interface = textChannel->optionalInterface<Tp::Client::ChannelInterfaceSubjectInterface>();
269 QVariantMap map = getInterfaceProperties(subject_interface);
270 for (QVariantMap::const_iterator iter = map.begin(); iter != map.end(); ++iter) {
271 if (iter.value().isValid()) {
272 roomProperties[iter.key()] = iter.value();
273 }
274 }
275 }
276
277 properties[History::FieldChatRoomInfo] = roomProperties;
278 properties[History::FieldThreadId] = textChannel->targetId();
279 break;
280 case Tp::HandleTypeContact:
281 case Tp::HandleTypeNone:
282 default:
283 break;
284 }
285
286 return properties;
96}287}
97288
98QVariantMap HistoryDaemon::threadForParticipants(const QString &accountId,289QVariantMap HistoryDaemon::threadForProperties(const QString &accountId,
99 History::EventType type,290 History::EventType type,
100 const QStringList &participants,291 const QVariantMap &properties,
101 History::MatchFlags matchFlags,292 History::MatchFlags matchFlags,
102 bool create)293 bool create)
103{294{
104 if (!mBackend) {295 if (!mBackend) {
105 return QVariantMap();296 return QVariantMap();
106 }297 }
107298
108 QVariantMap thread = mBackend->threadForParticipants(accountId,299 QVariantMap thread = mBackend->threadForProperties(accountId,
109 type,300 type,
110 participants,301 properties,
111 matchFlags);302 matchFlags);
112 if (thread.isEmpty() && create) {303 if (thread.isEmpty() && create) {
113 thread = mBackend->createThreadForParticipants(accountId, type, participants);304 thread = mBackend->createThreadForProperties(accountId, type, properties);
114 if (!thread.isEmpty()) {305 if (!thread.isEmpty()) {
306 if (properties.contains("Requested") && properties[History::FieldChatType].toInt() == History::ChatTypeRoom) {
307 QVariantMap map = thread[History::FieldChatRoomInfo].toMap();
308 map["Requested"] = properties["Requested"];
309 thread[History::FieldChatRoomInfo] = map;
310 }
115 mDBus.notifyThreadsAdded(QList<QVariantMap>() << thread);311 mDBus.notifyThreadsAdded(QList<QVariantMap>() << thread);
116 }312 }
117 }313 }
@@ -174,7 +370,7 @@
174 return mBackend->getSingleEvent((History::EventType)type, accountId, threadId, eventId);370 return mBackend->getSingleEvent((History::EventType)type, accountId, threadId, eventId);
175}371}
176372
177bool HistoryDaemon::writeEvents(const QList<QVariantMap> &events)373bool HistoryDaemon::writeEvents(const QList<QVariantMap> &events, const QVariantMap &properties)
178{374{
179 if (!mBackend) {375 if (!mBackend) {
180 return false;376 return false;
@@ -206,7 +402,7 @@
206 }402 }
207403
208 // only get the thread AFTER the event is written to make sure it is up-to-date404 // only get the thread AFTER the event is written to make sure it is up-to-date
209 QVariantMap thread = getSingleThread(type, accountId, threadId, QVariantMap());405 QVariantMap thread = getSingleThread(type, accountId, threadId, properties);
210 QString hash = hashThread(thread);406 QString hash = hashThread(thread);
211 threads[hash] = thread;407 threads[hash] = thread;
212408
@@ -245,7 +441,6 @@
245441
246bool HistoryDaemon::removeEvents(const QList<QVariantMap> &events)442bool HistoryDaemon::removeEvents(const QList<QVariantMap> &events)
247{443{
248 qDebug() << __PRETTY_FUNCTION__;
249 if (!mBackend) {444 if (!mBackend) {
250 return false;445 return false;
251 }446 }
@@ -319,7 +514,6 @@
319514
320bool HistoryDaemon::removeThreads(const QList<QVariantMap> &threads)515bool HistoryDaemon::removeThreads(const QList<QVariantMap> &threads)
321{516{
322 qDebug() << __PRETTY_FUNCTION__;
323 if (!mBackend) {517 if (!mBackend) {
324 return false;518 return false;
325 }519 }
@@ -360,7 +554,6 @@
360554
361void HistoryDaemon::onObserverCreated()555void HistoryDaemon::onObserverCreated()
362{556{
363 qDebug() << __PRETTY_FUNCTION__;
364 History::ChannelObserver *observer = History::TelepathyHelper::instance()->channelObserver();557 History::ChannelObserver *observer = History::TelepathyHelper::instance()->channelObserver();
365558
366 connect(observer, SIGNAL(callChannelAvailable(Tp::CallChannelPtr)),559 connect(observer, SIGNAL(callChannelAvailable(Tp::CallChannelPtr)),
@@ -371,10 +564,14 @@
371564
372void HistoryDaemon::onCallEnded(const Tp::CallChannelPtr &channel)565void HistoryDaemon::onCallEnded(const Tp::CallChannelPtr &channel)
373{566{
374 qDebug() << __PRETTY_FUNCTION__;567 QVariantMap properties = propertiesFromChannel(channel);
375 QStringList participants;568 QVariantList participants;
376 Q_FOREACH(const Tp::ContactPtr contact, channel->remoteMembers()) {569 Q_FOREACH(const Tp::ContactPtr contact, channel->remoteMembers()) {
377 participants << contact->id();570 QVariantMap contactProperties;
571 contactProperties[History::FieldAlias] = contact->alias();
572 contactProperties[History::FieldIdentifier] = contact->id();
573 contactProperties[History::FieldAccountId] = channel->property(History::FieldAccountId).toString();
574 participants << contactProperties;
378 }575 }
379576
380 // it shouldn't happen, but in case it does, we won't crash577 // it shouldn't happen, but in case it does, we won't crash
@@ -384,11 +581,11 @@
384 }581 }
385582
386 QString accountId = channel->property(History::FieldAccountId).toString();583 QString accountId = channel->property(History::FieldAccountId).toString();
387 QVariantMap thread = threadForParticipants(accountId,584 QVariantMap thread = threadForProperties(accountId,
388 History::EventTypeVoice,585 History::EventTypeVoice,
389 participants,586 properties,
390 matchFlagsForChannel(channel),587 matchFlagsForChannel(channel),
391 true);588 true);
392 // fill the call info589 // fill the call info
393 QDateTime timestamp = channel->property(History::FieldTimestamp).toDateTime();590 QDateTime timestamp = channel->property(History::FieldTimestamp).toDateTime();
394591
@@ -414,18 +611,284 @@
414 event[History::FieldMissed] = missed;611 event[History::FieldMissed] = missed;
415 event[History::FieldDuration] = duration;612 event[History::FieldDuration] = duration;
416 // FIXME: check what to do when there are more than just one remote participant613 // FIXME: check what to do when there are more than just one remote participant
417 event[History::FieldRemoteParticipant] = participants[0];614 event[History::FieldRemoteParticipant] = participants[0].toMap()[History::FieldIdentifier];
418 writeEvents(QList<QVariantMap>() << event);615 writeEvents(QList<QVariantMap>() << event, properties);
616}
617
618void HistoryDaemon::onTextChannelAvailable(const Tp::TextChannelPtr channel)
619{
620 // for Rooms we need to explicitly create the thread to allow users to send messages to groups even
621 // before they receive any message.
622 // for other types, we can wait until messages are received
623 if (channel->targetHandleType() == Tp::HandleTypeRoom) {
624 QString accountId = channel->property(History::FieldAccountId).toString();
625 QVariantMap properties = propertiesFromChannel(channel);
626
627 // first try to fetch the existing thread to see if there is any.
628 QVariantMap thread = threadForProperties(accountId,
629 History::EventTypeText,
630 properties,
631 matchFlagsForChannel(channel),
632 false);
633 if (thread.isEmpty()) {
634 // if there no existing thread, create one
635 properties["Requested"] = channel->isRequested();
636 thread = threadForProperties(accountId,
637 History::EventTypeText,
638 properties,
639 matchFlagsForChannel(channel),
640 true);
641
642 // write information event including all initial invitees
643 Q_FOREACH(const Tp::ContactPtr contact, channel->groupRemotePendingContacts(false)) {
644 writeInformationEvent(thread, History::InformationTypeInvitationSent, contact->alias());
645 }
646
647 // update participants only if the thread is not available previously. Otherwise we'll wait for membersChanged event
648 // for reflect in conversation information events for modified participants.
649 updateRoomParticipants(channel);
650 }
651
652 // write an entry saying you joined the group if 'joined' flag in thread is false and modify that flag.
653 if (!thread[History::FieldChatRoomInfo].toMap()["Joined"].toBool()) {
654 // only write self joined notification if protocol is not a phone one.
655 // FIXME (rmescandon): as a first solution, let's take only ofono as phone protocol
656 if (History::TelepathyHelper::instance()->accountForId(accountId)->protocolName() != "ofono") {
657 writeInformationEvent(thread, History::InformationTypeSelfJoined);
658 }
659 // update backend
660 updateRoomProperties(channel, QVariantMap{{"Joined", true}});
661 }
662
663 Tp::AbstractInterface *room_interface = channel->optionalInterface<Tp::Client::ChannelInterfaceRoomInterface>();
664 Tp::AbstractInterface *room_config_interface = channel->optionalInterface<Tp::Client::ChannelInterfaceRoomConfigInterface>();
665 Tp::AbstractInterface *subject_interface = channel->optionalInterface<Tp::Client::ChannelInterfaceSubjectInterface>();
666 ChannelInterfaceRolesInterface *roles_interface = channel->optionalInterface<ChannelInterfaceRolesInterface>();
667
668 QList<Tp::AbstractInterface*> interfaces;
669 interfaces << room_interface << room_config_interface << subject_interface << roles_interface;
670 for (auto interface : interfaces) {
671 if (interface) {
672 interface->setMonitorProperties(true);
673 interface->setProperty(History::FieldAccountId, accountId);
674 interface->setProperty(History::FieldThreadId, thread[History::FieldThreadId].toString());
675 interface->setProperty(History::FieldType, thread[History::FieldType].toInt());
676 connect(interface, SIGNAL(propertiesChanged(const QVariantMap &,const QStringList &)),
677 SLOT(onRoomPropertiesChanged(const QVariantMap &,const QStringList &)));
678 // update the stored info
679 Q_EMIT interface->propertiesChanged(getInterfaceProperties(interface), QStringList());
680 }
681 }
682
683 connect(channel.data(), SIGNAL(groupMembersChanged(const Tp::Contacts &, const Tp::Contacts &, const Tp::Contacts &, const Tp::Contacts &, const Tp::Channel::GroupMemberChangeDetails &)),
684 SLOT(onGroupMembersChanged(const Tp::Contacts &, const Tp::Contacts &, const Tp::Contacts &, const Tp::Contacts &, const Tp::Channel::GroupMemberChangeDetails &)));
685
686 connect(roles_interface, SIGNAL(RolesChanged(const HandleRolesMap&, const HandleRolesMap&)), SLOT(onRolesChanged(const HandleRolesMap&, const HandleRolesMap&)));
687 }
688}
689
690void HistoryDaemon::onGroupMembersChanged(const Tp::Contacts &groupMembersAdded,
691 const Tp::Contacts &groupLocalPendingMembersAdded,
692 const Tp::Contacts &groupRemotePendingMembersAdded,
693 const Tp::Contacts &groupMembersRemoved,
694 const Tp::Channel::GroupMemberChangeDetails &details)
695{
696 Tp::TextChannelPtr channel(qobject_cast<Tp::TextChannel*>(sender()));
697
698 QVariantMap properties;
699 QVariantMap thread;
700
701 // information events for members updates.
702 bool hasRemotePendingMembersAdded = groupRemotePendingMembersAdded.size() > 0;
703 bool hasMembersAdded = groupMembersAdded.size() > 0;
704 bool hasMembersRemoved = groupMembersRemoved.size() > 0;
705
706 if (hasRemotePendingMembersAdded || hasMembersAdded || hasMembersRemoved) {
707 properties = propertiesFromChannel(channel);
708 thread = threadForProperties(channel->property(History::FieldAccountId).toString(),
709 History::EventTypeText,
710 properties,
711 matchFlagsForChannel(channel),
712 false);
713 if (!thread.isEmpty()) {
714 if (hasRemotePendingMembersAdded) {
715 Q_FOREACH (const Tp::ContactPtr& contact, groupRemotePendingMembersAdded) {
716 if (!foundInThread(contact, thread)) {
717 writeInformationEvent(thread, History::InformationTypeInvitationSent, contact->alias());
718 }
719 }
720 }
721 if (hasMembersAdded) {
722 Q_FOREACH (const Tp::ContactPtr& contact, groupMembersAdded) {
723 // if this member was not previously regular member in thread, notify about his join
724 if (!foundAsMemberInThread(contact, thread)) {
725 writeInformationEvent(thread, History::InformationTypeJoined, contact->alias());
726 }
727 }
728 }
729
730 if (hasMembersRemoved) {
731 if (channel->groupSelfContactRemoveInfo().isValid()) {
732 // evaluate if we are leaving by our own or we are kicked
733 History::InformationType type = History::InformationTypeSelfLeaving;
734 if (channel->groupSelfContactRemoveInfo().hasReason()) {
735 switch (channel->groupSelfContactRemoveInfo().reason()) {
736 case ChannelGroupChangeReasonKicked:
737 type = History::InformationTypeSelfKicked;
738 break;
739 case ChannelGroupChangeReasonGone:
740 type = History::InformationTypeGroupGone;
741 break;
742 }
743 }
744 writeInformationEvent(thread, type);
745 // update backend
746 updateRoomProperties(channel, QVariantMap{{"Joined", false}});
747 }
748 else // don't notify any other group member removal if we are leaving the group
749 {
750 Q_FOREACH (const Tp::ContactPtr& contact, groupMembersRemoved) {
751 // inform about removed members other than us
752 if (contact->id() != channel->groupSelfContact()->id()) {
753 writeInformationEvent(thread, History::InformationTypeLeaving, contact->alias());
754 }
755 }
756 }
757 }
758 }
759 }
760
761 updateRoomParticipants(channel);
762}
763
764void HistoryDaemon::updateRoomParticipants(const Tp::TextChannelPtr channel)
765{
766 if (!channel) {
767 return;
768 }
769
770 QVariantList participants;
771 QStringList contactsAdded;
772
773 ChannelInterfaceRolesInterface *roles_interface = channel->optionalInterface<ChannelInterfaceRolesInterface>();
774 RolesMap roles;
775 if (roles_interface) {
776 roles = roles_interface->getRoles();
777 }
778
779 Q_FOREACH(const Tp::ContactPtr contact, channel->groupRemotePendingContacts(false)) {
780 QVariantMap participant;
781 contactsAdded << contact->id();
782 participant[History::FieldIdentifier] = contact->id();
783 participant[History::FieldAlias] = contact->alias();
784 participant[History::FieldParticipantState] = History::ParticipantStateRemotePending;
785 participant[History::FieldParticipantRoles] = roles[contact->handle().at(0)];
786 participants << QVariant::fromValue(participant);
787 }
788 Q_FOREACH(const Tp::ContactPtr contact, channel->groupLocalPendingContacts(false)) {
789 QVariantMap participant;
790 contactsAdded << contact->id();
791 participant[History::FieldIdentifier] = contact->id();
792 participant[History::FieldAlias] = contact->alias();
793 participant[History::FieldParticipantState] = History::ParticipantStateLocalPending;
794 participant[History::FieldParticipantRoles] = roles[contact->handle().at(0)];
795 participants << QVariant::fromValue(participant);
796 }
797
798 Q_FOREACH(const Tp::ContactPtr contact, channel->groupContacts(false)) {
799 // do not include remote and local pending members
800 if (contactsAdded.contains(contact->id())) {
801 continue;
802 }
803 QVariantMap participant;
804 participant[History::FieldIdentifier] = contact->id();
805 participant[History::FieldAlias] = contact->alias();
806 participant[History::FieldParticipantState] = History::ParticipantStateRegular;
807 participant[History::FieldParticipantRoles] = roles[contact->handle().at(0)];
808 participants << QVariant::fromValue(participant);
809 }
810
811 QString accountId = channel->property(History::FieldAccountId).toString();
812 QString threadId = channel->targetId();
813 if (mBackend->updateRoomParticipants(accountId, threadId, History::EventTypeText, participants)) {
814 QVariantMap updatedThread = getSingleThread(History::EventTypeText, accountId, threadId, QVariantMap());
815 mDBus.notifyThreadsModified(QList<QVariantMap>() << updatedThread);
816 }
817}
818
819void HistoryDaemon::updateRoomRoles(const Tp::TextChannelPtr &channel, const RolesMap &rolesMap)
820{
821 if (!channel) {
822 return;
823 }
824
825 QVariantMap participantsRoles;
826
827 Q_FOREACH(const Tp::ContactPtr contact, channel->groupRemotePendingContacts(false)) {
828 participantsRoles[contact->id()] = rolesMap[contact->handle().at(0)];
829 }
830 Q_FOREACH(const Tp::ContactPtr contact, channel->groupLocalPendingContacts(false)) {
831 participantsRoles[contact->id()] = rolesMap[contact->handle().at(0)];
832 }
833
834 Q_FOREACH(const Tp::ContactPtr contact, channel->groupContacts(false)) {
835 if (!participantsRoles.contains(contact->id())) {
836 participantsRoles[contact->id()] = rolesMap[contact->handle().at(0)];
837 }
838 }
839
840 // update participants roles
841 QString accountId = channel->property(History::FieldAccountId).toString();
842 QString threadId = channel->targetId();
843 if (mBackend->updateRoomParticipantsRoles(accountId, threadId, History::EventTypeText, participantsRoles)) {
844 QVariantMap updatedThread = getSingleThread(History::EventTypeText, accountId, threadId, QVariantMap());
845 mDBus.notifyThreadsModified(QList<QVariantMap>() << updatedThread);
846 }
847
848 // update self roles in room properties
849 uint selfRoles = rolesMap[channel->groupSelfContact()->handle().at(0)];
850 updateRoomProperties(channel, QVariantMap{{"SelfRoles", selfRoles}});
851}
852
853void HistoryDaemon::onRoomPropertiesChanged(const QVariantMap &properties,const QStringList &invalidated)
854{
855 QString accountId = sender()->property(History::FieldAccountId).toString();
856 QString threadId = sender()->property(History::FieldThreadId).toString();
857 History::EventType type = (History::EventType)sender()->property(History::FieldType).toInt();
858
859 // get thread before updating to see if there are changes to insert as information events
860 QVariantMap thread = getSingleThread(type, accountId, threadId, QVariantMap());
861 if (!thread.empty()) {
862 writeRoomChangesInformationEvents(thread, properties);
863 }
864
865 updateRoomProperties(accountId, threadId, type, properties, invalidated);
866}
867
868void HistoryDaemon::updateRoomProperties(const Tp::TextChannelPtr &channel, const QVariantMap &properties)
869{
870 QString accountId = channel->property(History::FieldAccountId).toString();
871 QString threadId = channel->targetId();
872 History::EventType type = History::EventTypeText;
873 updateRoomProperties(accountId, threadId, type, properties, QStringList());
874}
875
876void HistoryDaemon::updateRoomProperties(const QString &accountId, const QString &threadId, History::EventType type, const QVariantMap &properties, const QStringList &invalidated)
877{
878 if (mBackend->updateRoomInfo(accountId, threadId, type, properties, invalidated)) {
879 QVariantMap thread = getSingleThread(type, accountId, threadId, QVariantMap());
880 mDBus.notifyThreadsModified(QList<QVariantMap>() << thread);
881 }
419}882}
420883
421void HistoryDaemon::onMessageReceived(const Tp::TextChannelPtr textChannel, const Tp::ReceivedMessage &message)884void HistoryDaemon::onMessageReceived(const Tp::TextChannelPtr textChannel, const Tp::ReceivedMessage &message)
422{885{
423 qDebug() << __PRETTY_FUNCTION__;
424 QString eventId;886 QString eventId;
425 Tp::MessagePart header = message.header();887 Tp::MessagePart header = message.header();
426 QString senderId;888 QString senderId;
889 QVariantMap properties = propertiesFromChannel(textChannel);
427 History::MessageStatus status = History::MessageStatusUnknown;890 History::MessageStatus status = History::MessageStatusUnknown;
428 if (message.sender()->handle().at(0) == textChannel->connection()->selfHandle()) {891 if (!message.sender() || message.sender()->handle().at(0) == textChannel->connection()->selfHandle()) {
429 senderId = "self";892 senderId = "self";
430 status = History::MessageStatusDelivered;893 status = History::MessageStatusDelivered;
431 } else {894 } else {
@@ -452,6 +915,13 @@
452 return;915 return;
453 }916 }
454917
918 // FIXME: if this message is already read, don't allow reverting the status.
919 // we need to check if this is the right place to do it.
920 if (textEvent[History::FieldMessageStatus].toInt() == History::MessageStatusRead) {
921 qWarning() << "Skipping delivery report as it is trying to revert the Read status of an existing message to the following status:" << message.deliveryDetails().status();
922 return;
923 }
924
455 History::MessageStatus status;925 History::MessageStatus status;
456 switch (message.deliveryDetails().status()) {926 switch (message.deliveryDetails().status()) {
457 case Tp::DeliveryStatusAccepted:927 case Tp::DeliveryStatusAccepted:
@@ -478,20 +948,18 @@
478 }948 }
479949
480 textEvent[History::FieldMessageStatus] = (int) status;950 textEvent[History::FieldMessageStatus] = (int) status;
481 if (!writeEvents(QList<QVariantMap>() << textEvent)) {951 if (!writeEvents(QList<QVariantMap>() << textEvent, properties)) {
482 qWarning() << "Failed to save the new message status!";952 qWarning() << "Failed to save the new message status!";
483 }953 }
484954
485 return;955 return;
486 }956 }
487957
488 QStringList participants = participantsFromChannel(textChannel);958 QVariantMap thread = threadForProperties(textChannel->property(History::FieldAccountId).toString(),
489959 History::EventTypeText,
490 QVariantMap thread = threadForParticipants(textChannel->property(History::FieldAccountId).toString(),960 properties,
491 History::EventTypeText,961 matchFlagsForChannel(textChannel),
492 participants,962 true);
493 matchFlagsForChannel(textChannel),
494 true);
495 int count = 1;963 int count = 1;
496 QList<QVariantMap> attachments;964 QList<QVariantMap> attachments;
497 History::MessageType type = History::MessageTypeText;965 History::MessageType type = History::MessageTypeText;
@@ -558,16 +1026,22 @@
558 event[History::FieldSubject] = subject;1026 event[History::FieldSubject] = subject;
559 event[History::FieldAttachments] = QVariant::fromValue(attachments);1027 event[History::FieldAttachments] = QVariant::fromValue(attachments);
5601028
561 writeEvents(QList<QVariantMap>() << event);1029 writeEvents(QList<QVariantMap>() << event, properties);
1030
1031 // if this messages supersedes another one, remove the original message
1032 if (!message.supersededToken().isEmpty()) {
1033 event[History::FieldEventId] = message.supersededToken();
1034 removeEvents(QList<QVariantMap>() << event);
1035 }
562}1036}
5631037
564QVariantMap HistoryDaemon::getSingleEventFromTextChannel(const Tp::TextChannelPtr textChannel, const QString &messageId)1038QVariantMap HistoryDaemon::getSingleEventFromTextChannel(const Tp::TextChannelPtr textChannel, const QString &messageId)
565{1039{
566 QStringList participants = participantsFromChannel(textChannel);1040 QVariantMap properties = propertiesFromChannel(textChannel);
5671041
568 QVariantMap thread = threadForParticipants(textChannel->property(History::FieldAccountId).toString(),1042 QVariantMap thread = threadForProperties(textChannel->property(History::FieldAccountId).toString(),
569 History::EventTypeText,1043 History::EventTypeText,
570 participants,1044 properties,
571 matchFlagsForChannel(textChannel),1045 matchFlagsForChannel(textChannel),
572 false);1046 false);
573 if (thread.isEmpty()) {1047 if (thread.isEmpty()) {
@@ -587,6 +1061,7 @@
587void HistoryDaemon::onMessageRead(const Tp::TextChannelPtr textChannel, const Tp::ReceivedMessage &message)1061void HistoryDaemon::onMessageRead(const Tp::TextChannelPtr textChannel, const Tp::ReceivedMessage &message)
588{1062{
589 QVariantMap textEvent = getSingleEventFromTextChannel(textChannel, message.messageToken());1063 QVariantMap textEvent = getSingleEventFromTextChannel(textChannel, message.messageToken());
1064 QVariantMap properties = propertiesFromChannel(textChannel);
5901065
591 if (textEvent.isEmpty()) {1066 if (textEvent.isEmpty()) {
592 qWarning() << "Cound not find the original event to update with newEvent = false.";1067 qWarning() << "Cound not find the original event to update with newEvent = false.";
@@ -594,15 +1069,14 @@
594 }1069 }
5951070
596 textEvent[History::FieldNewEvent] = false;1071 textEvent[History::FieldNewEvent] = false;
597 if (!writeEvents(QList<QVariantMap>() << textEvent)) {1072 if (!writeEvents(QList<QVariantMap>() << textEvent, properties)) {
598 qWarning() << "Failed to save the new message status!";1073 qWarning() << "Failed to save the new message status!";
599 }1074 }
600}1075}
6011076
602void HistoryDaemon::onMessageSent(const Tp::TextChannelPtr textChannel, const Tp::Message &message, const QString &messageToken)1077void HistoryDaemon::onMessageSent(const Tp::TextChannelPtr textChannel, const Tp::Message &message, const QString &messageToken)
603{1078{
604 qDebug() << __PRETTY_FUNCTION__;1079 QVariantMap properties = propertiesFromChannel(textChannel);
605 QStringList participants = participantsFromChannel(textChannel);
606 QList<QVariantMap> attachments;1080 QList<QVariantMap> attachments;
607 History::MessageType type = History::MessageTypeText;1081 History::MessageType type = History::MessageTypeText;
608 int count = 1;1082 int count = 1;
@@ -615,9 +1089,9 @@
615 eventId = messageToken;1089 eventId = messageToken;
616 }1090 }
617 1091
618 QVariantMap thread = threadForParticipants(textChannel->property(History::FieldAccountId).toString(),1092 QVariantMap thread = threadForProperties(textChannel->property(History::FieldAccountId).toString(),
619 History::EventTypeText,1093 History::EventTypeText,
620 participants,1094 properties,
621 matchFlagsForChannel(textChannel),1095 matchFlagsForChannel(textChannel),
622 true);1096 true);
623 if (message.hasNonTextContent()) {1097 if (message.hasNonTextContent()) {
@@ -666,7 +1140,6 @@
666 }1140 }
667 }1141 }
6681142
669
670 QVariantMap event;1143 QVariantMap event;
671 event[History::FieldType] = History::EventTypeText;1144 event[History::FieldType] = History::EventTypeText;
672 event[History::FieldAccountId] = thread[History::FieldAccountId];1145 event[History::FieldAccountId] = thread[History::FieldAccountId];
@@ -686,7 +1159,7 @@
686 event[History::FieldSubject] = "";1159 event[History::FieldSubject] = "";
687 event[History::FieldAttachments] = QVariant::fromValue(attachments);1160 event[History::FieldAttachments] = QVariant::fromValue(attachments);
6881161
689 writeEvents(QList<QVariantMap>() << event);1162 writeEvents(QList<QVariantMap>() << event, properties);
690}1163}
6911164
692History::MatchFlags HistoryDaemon::matchFlagsForChannel(const Tp::ChannelPtr &channel)1165History::MatchFlags HistoryDaemon::matchFlagsForChannel(const Tp::ChannelPtr &channel)
@@ -707,3 +1180,88 @@
707 hash += "#-#" + thread[History::FieldThreadId].toString();1180 hash += "#-#" + thread[History::FieldThreadId].toString();
708 return hash;1181 return hash;
709}1182}
1183
1184QVariantMap HistoryDaemon::getInterfaceProperties(const Tp::AbstractInterface *interface)
1185{
1186 QDBusInterface propsInterface(interface->service(), interface->path(), "org.freedesktop.DBus.Properties");
1187 QDBusReply<QVariantMap> reply = propsInterface.call("GetAll", interface->interface());
1188 if (!reply.isValid()) {
1189 qWarning() << "Failed to fetch channel properties for interface" << interface->interface() << reply.error().message();
1190 }
1191 return reply.value();
1192}
1193
1194void HistoryDaemon::writeInformationEvent(const QVariantMap &thread, History::InformationType type, const QString &subject, const QString &sender, const QString &text)
1195{
1196 History::TextEvent historyEvent = History::TextEvent(thread[History::FieldAccountId].toString(),
1197 thread[History::FieldThreadId].toString(),
1198 QString(QCryptographicHash::hash(QByteArray(
1199 (QDateTime::currentDateTime().toString("yyyy-MM-ddTHH:mm:ss.zzz") + subject + text).toLatin1()),
1200 QCryptographicHash::Md5).toHex()),
1201 sender,
1202 QDateTime::currentDateTime(),
1203 false,
1204 text,
1205 History::MessageTypeInformation,
1206 History::MessageStatusUnknown,
1207 QDateTime::currentDateTime(),
1208 subject,
1209 type);
1210 writeEvents(QList<QVariantMap>() << historyEvent.properties(), thread);
1211}
1212
1213void HistoryDaemon::writeRoomChangesInformationEvents(const QVariantMap &thread, const QVariantMap &interfaceProperties)
1214{
1215 if (!thread.isEmpty()) {
1216 // group subject
1217 QString storedSubject = thread[History::FieldChatRoomInfo].toMap()["Subject"].toString();
1218 QString newSubject = interfaceProperties["Subject"].toString();
1219 if (!newSubject.isEmpty() && storedSubject != newSubject) {
1220 //see if we have an actor. If actor is 'me', we have changed that subject
1221 QString actor = thread[History::FieldChatRoomInfo].toMap()["Actor"].toString();
1222 if (actor == "me") {
1223 actor = "self";
1224 }
1225 writeInformationEvent(thread, History::InformationTypeTitleChanged, newSubject, actor);
1226 }
1227 }
1228}
1229
1230void HistoryDaemon::writeRolesInformationEvents(const QVariantMap &thread, const Tp::ChannelPtr &channel, const RolesMap &rolesMap)
1231{
1232 if (thread.isEmpty()) {
1233 return;
1234 }
1235
1236 if (!thread[History::FieldChatRoomInfo].toMap()["Joined"].toBool()) {
1237 return;
1238 }
1239
1240 // list of identifiers for current channel admins
1241 QStringList adminIds;
1242
1243 Q_FOREACH(const Tp::ContactPtr contact, channel->groupContacts(false)) {
1244 // see if admin role (ChannelAdminRole == 2)
1245 if (rolesMap[contact->handle().at(0)] & AdminRole) {
1246 adminIds << contact->id();
1247 }
1248 }
1249
1250 Q_FOREACH (QVariant participant, thread[History::FieldParticipants].toList()) {
1251 QString participantId = participant.toMap()[History::FieldIdentifier].toString();
1252 if (adminIds.contains(participantId)) {
1253 // see if already was admin or not (ChannelAdminRole == 2)
1254 if (! (participant.toMap()[History::FieldParticipantRoles].toUInt() & AdminRole)) {
1255 writeInformationEvent(thread, History::InformationTypeAdminGranted, participantId);
1256 }
1257 }
1258 }
1259
1260 //evaluate now self roles
1261 if (rolesMap[channel->groupSelfContact()->handle().at(0)] & AdminRole) {
1262 uint selfRoles = thread[History::FieldChatRoomInfo].toMap()["SelfRoles"].toUInt();
1263 if (! (selfRoles & AdminRole)) {
1264 writeInformationEvent(thread, History::InformationTypeSelfAdminGranted);
1265 }
1266 }
1267}
7101268
=== modified file 'daemon/historydaemon.h'
--- daemon/historydaemon.h 2015-11-20 11:45:07 +0000
+++ daemon/historydaemon.h 2016-11-24 12:52:24 +0000
@@ -1,5 +1,5 @@
1/*1/*
2 * Copyright (C) 2013 Canonical, Ltd.2 * Copyright (C) 2013-2016 Canonical, Ltd.
3 *3 *
4 * Authors:4 * Authors:
5 * Gustavo Pichorim Boiko <gustavo.boiko@canonical.com>5 * Gustavo Pichorim Boiko <gustavo.boiko@canonical.com>
@@ -30,6 +30,9 @@
30#include "callchannelobserver.h"30#include "callchannelobserver.h"
31#include "historyservicedbus.h"31#include "historyservicedbus.h"
32#include "plugin.h"32#include "plugin.h"
33#include "rolesinterface.h"
34
35typedef QMap<uint,uint> RolesMap;
3336
34class HistoryDaemon : public QObject37class HistoryDaemon : public QObject
35{38{
@@ -39,19 +42,19 @@
3942
40 static HistoryDaemon *instance();43 static HistoryDaemon *instance();
4144
42 static QStringList participantsFromChannel(const Tp::TextChannelPtr &textChannel);45 static QVariantMap propertiesFromChannel(const Tp::ChannelPtr &textChannel);
43 QVariantMap threadForParticipants(const QString &accountId,46 QVariantMap threadForProperties(const QString &accountId,
44 History::EventType type,47 History::EventType type,
45 const QStringList &participants,48 const QVariantMap &properties,
46 History::MatchFlags matchFlags = History::MatchCaseSensitive,49 History::MatchFlags matchFlags = History::MatchCaseSensitive,
47 bool create = true);50 bool create = true);
48 QString queryThreads(int type, const QVariantMap &sort, const QVariantMap &filter, const QVariantMap &properties);51 QString queryThreads(int type, const QVariantMap &sort, const QVariantMap &filter, const QVariantMap &properties);
49 QString queryEvents(int type, const QVariantMap &sort, const QVariantMap &filter);52 QString queryEvents(int type, const QVariantMap &sort, const QVariantMap &filter);
50 QVariantMap getSingleThread(int type, const QString &accountId, const QString &threadId, const QVariantMap &properties);53 QVariantMap getSingleThread(int type, const QString &accountId, const QString &threadId, const QVariantMap &properties);
51 QVariantMap getSingleEvent(int type, const QString &accountId, const QString &threadId, const QString &eventId);54 QVariantMap getSingleEvent(int type, const QString &accountId, const QString &threadId, const QString &eventId);
52 QVariantMap getSingleEventFromTextChannel(const Tp::TextChannelPtr textChannel, const QString &messageId);55 QVariantMap getSingleEventFromTextChannel(const Tp::TextChannelPtr textChannel, const QString &messageId);
5356
54 bool writeEvents(const QList<QVariantMap> &events);57 bool writeEvents(const QList<QVariantMap> &events, const QVariantMap &properties);
55 bool removeEvents(const QList<QVariantMap> &events);58 bool removeEvents(const QList<QVariantMap> &events);
56 bool removeThreads(const QList<QVariantMap> &threads);59 bool removeThreads(const QList<QVariantMap> &threads);
5760
@@ -61,11 +64,27 @@
61 void onMessageReceived(const Tp::TextChannelPtr textChannel, const Tp::ReceivedMessage &message);64 void onMessageReceived(const Tp::TextChannelPtr textChannel, const Tp::ReceivedMessage &message);
62 void onMessageRead(const Tp::TextChannelPtr textChannel, const Tp::ReceivedMessage &message);65 void onMessageRead(const Tp::TextChannelPtr textChannel, const Tp::ReceivedMessage &message);
63 void onMessageSent(const Tp::TextChannelPtr textChannel, const Tp::Message &message, const QString &messageToken);66 void onMessageSent(const Tp::TextChannelPtr textChannel, const Tp::Message &message, const QString &messageToken);
67 void onTextChannelAvailable(const Tp::TextChannelPtr channel);
68 void onRoomPropertiesChanged(const QVariantMap &properties,const QStringList &invalidated);
69 void onGroupMembersChanged(const Tp::Contacts &groupMembersAdded, const Tp::Contacts &groupLocalPendingMembersAdded,
70 const Tp::Contacts &groupRemotePendingMembersAdded, const Tp::Contacts &groupMembersRemoved,
71 const Tp::Channel::GroupMemberChangeDetails &details);
72 void onRolesChanged(const HandleRolesMap &added, const HandleRolesMap &removed);
6473
65protected:74protected:
66 History::MatchFlags matchFlagsForChannel(const Tp::ChannelPtr &channel);75 History::MatchFlags matchFlagsForChannel(const Tp::ChannelPtr &channel);
76 void updateRoomParticipants(const Tp::TextChannelPtr channel);
77 void updateRoomRoles(const Tp::TextChannelPtr &channel, const RolesMap &rolesMap);
67 QString hashThread(const QVariantMap &thread);78 QString hashThread(const QVariantMap &thread);
6879 static QVariantMap getInterfaceProperties(const Tp::AbstractInterface *interface);
80 void updateRoomProperties(const Tp::TextChannelPtr &channel, const QVariantMap &properties);
81 void updateRoomProperties(const QString &accountId, const QString &threadId, History::EventType type, const QVariantMap &properties, const QStringList &invalidated);
82
83 void writeInformationEvent(const QVariantMap &thread, History::InformationType type, const QString &subject = QString(), const QString &sender = QString("self"), const QString &text = QString());
84
85 void writeRoomChangesInformationEvents(const QVariantMap &thread, const QVariantMap &interfaceProperties);
86 void writeRolesInformationEvents(const QVariantMap &thread, const Tp::ChannelPtr &channel, const RolesMap &rolesMap);
87 void writeRolesChangesInformationEvents(const QVariantMap &thread, const Tp::ChannelPtr &channel, const RolesMap &rolesMap);
69private:88private:
70 HistoryDaemon(QObject *parent = 0);89 HistoryDaemon(QObject *parent = 0);
7190
7291
=== modified file 'daemon/historyservicedbus.cpp'
--- daemon/historyservicedbus.cpp 2015-10-01 19:44:45 +0000
+++ daemon/historyservicedbus.cpp 2016-11-24 12:52:24 +0000
@@ -1,5 +1,5 @@
1/*1/*
2 * Copyright (C) 2013 Canonical, Ltd.2 * Copyright (C) 2013-2016 Canonical, Ltd.
3 *3 *
4 * Authors:4 * Authors:
5 * Gustavo Pichorim Boiko <gustavo.boiko@canonical.com>5 * Gustavo Pichorim Boiko <gustavo.boiko@canonical.com>
@@ -34,16 +34,15 @@
3434
35bool HistoryServiceDBus::connectToBus()35bool HistoryServiceDBus::connectToBus()
36{36{
37 bool ok = QDBusConnection::sessionBus().registerService(History::DBusService);
38 if (!ok) {
39 return false;
40 }
41
42 if (!mAdaptor) {37 if (!mAdaptor) {
43 mAdaptor = new HistoryServiceAdaptor(this);38 mAdaptor = new HistoryServiceAdaptor(this);
44 }39 }
4540
46 return QDBusConnection::sessionBus().registerObject(History::DBusObjectPath, this);41 if (!QDBusConnection::sessionBus().registerObject(History::DBusObjectPath, this)) {
42 return false;
43 }
44
45 return QDBusConnection::sessionBus().registerService(History::DBusService);
47}46}
4847
49void HistoryServiceDBus::notifyThreadsAdded(const QList<QVariantMap> &threads)48void HistoryServiceDBus::notifyThreadsAdded(const QList<QVariantMap> &threads)
@@ -76,58 +75,67 @@
76 Q_EMIT EventsRemoved(events);75 Q_EMIT EventsRemoved(events);
77}76}
7877
78QVariantMap HistoryServiceDBus::ThreadForProperties(const QString &accountId,
79 int type,
80 const QVariantMap &properties,
81 int matchFlags,
82 bool create)
83{
84 return HistoryDaemon::instance()->threadForProperties(accountId,
85 (History::EventType) type,
86 properties,
87 (History::MatchFlags) matchFlags,
88 create);
89}
90
79QVariantMap HistoryServiceDBus::ThreadForParticipants(const QString &accountId,91QVariantMap HistoryServiceDBus::ThreadForParticipants(const QString &accountId,
80 int type,92 int type,
81 const QStringList &participants,93 const QStringList &participants,
82 int matchFlags,94 int matchFlags,
83 bool create)95 bool create)
84{96{
85 return HistoryDaemon::instance()->threadForParticipants(accountId,97 QVariantMap properties;
98 properties[History::FieldParticipants] = participants;
99
100 return HistoryDaemon::instance()->threadForProperties(accountId,
86 (History::EventType) type,101 (History::EventType) type,
87 participants,102 properties,
88 (History::MatchFlags) matchFlags,103 (History::MatchFlags) matchFlags,
89 create);104 create);
90}105}
91106
92bool HistoryServiceDBus::WriteEvents(const QList<QVariantMap> &events)107bool HistoryServiceDBus::WriteEvents(const QList<QVariantMap> &events)
93{108{
94 qDebug() << __PRETTY_FUNCTION__;109 return HistoryDaemon::instance()->writeEvents(events, QVariantMap());
95 return HistoryDaemon::instance()->writeEvents(events);
96}110}
97111
98bool HistoryServiceDBus::RemoveThreads(const QList<QVariantMap> &threads)112bool HistoryServiceDBus::RemoveThreads(const QList<QVariantMap> &threads)
99{113{
100 qDebug() << __PRETTY_FUNCTION__;
101 return HistoryDaemon::instance()->removeThreads(threads);114 return HistoryDaemon::instance()->removeThreads(threads);
102}115}
103116
104bool HistoryServiceDBus::RemoveEvents(const QList<QVariantMap> &events)117bool HistoryServiceDBus::RemoveEvents(const QList<QVariantMap> &events)
105{118{
106 qDebug() << __PRETTY_FUNCTION__;
107 return HistoryDaemon::instance()->removeEvents(events);119 return HistoryDaemon::instance()->removeEvents(events);
108}120}
109121
110QString HistoryServiceDBus::QueryThreads(int type, const QVariantMap &sort, const QVariantMap &filter, const QVariantMap &properties)122QString HistoryServiceDBus::QueryThreads(int type, const QVariantMap &sort, const QVariantMap &filter, const QVariantMap &properties)
111{123{
112 qDebug() << __PRETTY_FUNCTION__;
113 return HistoryDaemon::instance()->queryThreads(type, sort, filter, properties);124 return HistoryDaemon::instance()->queryThreads(type, sort, filter, properties);
114}125}
115126
116QString HistoryServiceDBus::QueryEvents(int type, const QVariantMap &sort, const QVariantMap &filter)127QString HistoryServiceDBus::QueryEvents(int type, const QVariantMap &sort, const QVariantMap &filter)
117{128{
118 qDebug() << __PRETTY_FUNCTION__;
119 return HistoryDaemon::instance()->queryEvents(type, sort, filter);129 return HistoryDaemon::instance()->queryEvents(type, sort, filter);
120}130}
121131
122QVariantMap HistoryServiceDBus::GetSingleThread(int type, const QString &accountId, const QString &threadId, const QVariantMap &properties)132QVariantMap HistoryServiceDBus::GetSingleThread(int type, const QString &accountId, const QString &threadId, const QVariantMap &properties)
123{133{
124 qDebug() << __PRETTY_FUNCTION__;
125 return HistoryDaemon::instance()->getSingleThread(type, accountId, threadId, properties);134 return HistoryDaemon::instance()->getSingleThread(type, accountId, threadId, properties);
126}135}
127136
128QVariantMap HistoryServiceDBus::GetSingleEvent(int type, const QString &accountId, const QString &threadId, const QString &eventId)137QVariantMap HistoryServiceDBus::GetSingleEvent(int type, const QString &accountId, const QString &threadId, const QString &eventId)
129{138{
130 qDebug() << __PRETTY_FUNCTION__;
131 return HistoryDaemon::instance()->getSingleEvent(type, accountId, threadId, eventId);139 return HistoryDaemon::instance()->getSingleEvent(type, accountId, threadId, eventId);
132}140}
133141
134142
=== modified file 'daemon/historyservicedbus.h'
--- daemon/historyservicedbus.h 2015-09-23 15:08:07 +0000
+++ daemon/historyservicedbus.h 2016-11-24 12:52:24 +0000
@@ -1,5 +1,5 @@
1/*1/*
2 * Copyright (C) 2013 Canonical, Ltd.2 * Copyright (C) 2013-2016 Canonical, Ltd.
3 *3 *
4 * Authors:4 * Authors:
5 * Gustavo Pichorim Boiko <gustavo.boiko@canonical.com>5 * Gustavo Pichorim Boiko <gustavo.boiko@canonical.com>
@@ -50,6 +50,11 @@
50 const QStringList &participants,50 const QStringList &participants,
51 int matchFlags,51 int matchFlags,
52 bool create);52 bool create);
53 QVariantMap ThreadForProperties(const QString &accountId,
54 int type,
55 const QVariantMap &properties,
56 int matchFlags,
57 bool create);
53 bool WriteEvents(const QList <QVariantMap> &events);58 bool WriteEvents(const QList <QVariantMap> &events);
54 bool RemoveThreads(const QList <QVariantMap> &threads);59 bool RemoveThreads(const QList <QVariantMap> &threads);
55 bool RemoveEvents(const QList <QVariantMap> &events);60 bool RemoveEvents(const QList <QVariantMap> &events);
5661
=== added file 'daemon/rolesinterface.cpp'
--- daemon/rolesinterface.cpp 1970-01-01 00:00:00 +0000
+++ daemon/rolesinterface.cpp 2016-11-24 12:52:24 +0000
@@ -0,0 +1,71 @@
1/*
2 * Copyright (C) 2016 Canonical, Ltd.
3 *
4 * Authors:
5 * Roberto Mier Escandon <roberto.escandon@canonical.com>
6 *
7 * This file is part of history-service.
8 *
9 * history-service is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; version 3.
12 *
13 * history-service is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 */
21
22#include <daemon/rolesinterface.h>
23
24ChannelInterfaceRolesInterface::ChannelInterfaceRolesInterface(const QString& busName, const QString& objectPath, QObject *parent)
25 : Tp::AbstractInterface(busName, objectPath, staticInterfaceName(), QDBusConnection::sessionBus(), parent)
26{
27}
28
29ChannelInterfaceRolesInterface::ChannelInterfaceRolesInterface(const QDBusConnection& connection, const QString& busName, const QString& objectPath, QObject *parent)
30 : Tp::AbstractInterface(busName, objectPath, staticInterfaceName(), connection, parent)
31{
32}
33
34ChannelInterfaceRolesInterface::ChannelInterfaceRolesInterface(Tp::DBusProxy *proxy)
35 : Tp::AbstractInterface(proxy, staticInterfaceName())
36{
37}
38
39ChannelInterfaceRolesInterface::ChannelInterfaceRolesInterface(const Tp::Client::ChannelInterface& mainInterface)
40 : Tp::AbstractInterface(mainInterface.service(), mainInterface.path(), staticInterfaceName(), mainInterface.connection(), mainInterface.parent())
41{
42}
43
44ChannelInterfaceRolesInterface::ChannelInterfaceRolesInterface(const Tp::Client::ChannelInterface& mainInterface, QObject *parent)
45 : Tp::AbstractInterface(mainInterface.service(), mainInterface.path(), staticInterfaceName(), mainInterface.connection(), parent)
46{
47}
48
49void ChannelInterfaceRolesInterface::invalidate(Tp::DBusProxy *proxy,
50 const QString &error, const QString &message)
51{
52 Tp::AbstractInterface::invalidate(proxy, error, message);
53}
54
55HandleRolesMap ChannelInterfaceRolesInterface::getRoles() const
56{
57 QDBusMessage msg = QDBusMessage::createMethodCall(service(), path(),
58 TP_QT_IFACE_PROPERTIES, QLatin1String("Get"));
59 msg << interface() << QLatin1String("Roles");
60 QDBusMessage result = connection().call(msg);
61 return qdbus_cast<HandleRolesMap>(result.arguments().at(0).value<QDBusVariant>().variant());
62}
63
64bool ChannelInterfaceRolesInterface::getCanUpdateRoles() const
65{
66 QDBusMessage msg = QDBusMessage::createMethodCall(service(), path(),
67 TP_QT_IFACE_PROPERTIES, QLatin1String("Get"));
68 msg << interface() << QLatin1String("CanUpdateRoles");
69 QDBusMessage result = connection().call(msg);
70 return qdbus_cast<bool>(result.arguments().at(0).value<QDBusVariant>().variant());
71}
072
=== added file 'daemon/rolesinterface.h'
--- daemon/rolesinterface.h 1970-01-01 00:00:00 +0000
+++ daemon/rolesinterface.h 2016-11-24 12:52:24 +0000
@@ -0,0 +1,210 @@
1/*
2 * Copyright (C) 2016 Canonical, Ltd.
3 *
4 * Authors:
5 * Roberto Mier Escandon <roberto.escandon@canonical.com>
6 *
7 * This file is part of history-service.
8 *
9 * history-service is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; version 3.
12 *
13 * history-service is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 */
21
22#ifndef CHANNELINTERFACEROLESINTERFACE_H
23#define CHANNELINTERFACEROLESINTERFACE_H
24
25#include <QMap>
26
27#include <TelepathyQt/AbstractInterface>
28#include <TelepathyQt/ChannelInterface>
29
30/**
31 * \struct HandleRolesMap
32 * \ingroup mapping
33 * \headerfile TelepathyQt/types.h <TelepathyQt/Types>
34 *
35 * Convertible with
36 * QMap<uint, uint>, but needed to have a discrete type in the Qt type system.
37 *
38 * A map from channel-specific handles to their owners.
39 */
40struct HandleRolesMap : public QMap<uint, uint>
41{
42 inline HandleRolesMap() : QMap<uint, uint>() {}
43 inline HandleRolesMap(const QMap<uint, uint>& a) : QMap<uint, uint>(a) {}
44
45 inline HandleRolesMap& operator=(const QMap<uint, uint>& a)
46 {
47 *(static_cast<QMap<uint, uint>*>(this)) = a;
48 return *this;
49 }
50};
51
52Q_DECLARE_METATYPE(HandleRolesMap)
53
54class ChannelInterfaceRolesInterface : public Tp::AbstractInterface
55{
56 Q_OBJECT
57public:
58
59 /**
60 * Returns the name of the interface "org.freedesktop.Telepathy.Channel.Interface.Roles", which this class
61 * represents.
62 *
63 * \return The D-Bus interface name.
64 */
65 static inline QLatin1String staticInterfaceName()
66 {
67 return QLatin1String("org.freedesktop.Telepathy.Channel.Interface.Roles");
68 }
69
70 /**
71 * Creates a ChannelInterfaceRolesInterface associated with the given object on the session bus.
72 *
73 * \param busName Name of the service the object is on.
74 * \param objectPath Path to the object on the service.
75 * \param parent Passed to the parent class constructor.
76 */
77 ChannelInterfaceRolesInterface(
78 const QString& busName,
79 const QString& objectPath,
80 QObject* parent = 0
81 );
82
83 /**
84 * Creates a ChannelInterfaceRolesInterface associated with the given object on the given bus.
85 *
86 * \param connection The bus via which the object can be reached.
87 * \param busName Name of the service the object is on.
88 * \param objectPath Path to the object on the service.
89 * \param parent Passed to the parent class constructor.
90 */
91 ChannelInterfaceRolesInterface(
92 const QDBusConnection& connection,
93 const QString& busName,
94 const QString& objectPath,
95 QObject* parent = 0
96 );
97
98 /**
99 * Creates a ChannelInterfaceRolesInterface associated with the same object as the given proxy.
100 *
101 * \param proxy The proxy to use. It will also be the QObject::parent()
102 * for this object.
103 */
104 ChannelInterfaceRolesInterface(Tp::DBusProxy *proxy);
105
106 /**
107 * Creates a ChannelInterfaceRolesInterface associated with the same object as the given proxy.
108 * Additionally, the created proxy will have the same parent as the given
109 * proxy.
110 *
111 * \param mainInterface The proxy to use.
112 */
113 explicit ChannelInterfaceRolesInterface(const Tp::Client::ChannelInterface& mainInterface);
114
115 /**
116 * Creates a ChannelInterfaceRolesInterface associated with the same object as the given proxy.
117 * However, a different parent object can be specified.
118 *
119 * \param mainInterface The proxy to use.
120 * \param parent Passed to the parent class constructor.
121 */
122 ChannelInterfaceRolesInterface(const Tp::Client::ChannelInterface& mainInterface, QObject* parent);
123
124 /**
125 * Asynchronous getter for the remote object property \c Roles of type \c HandleRolesMap.
126 *
127 * \return A pending variant which will emit finished when the property has been
128 * retrieved.
129 */
130 inline Tp::PendingVariant *requestPropertyRoles() const
131 {
132 return internalRequestProperty(QLatin1String("Roles"));
133 }
134
135 /**
136 * Asynchronous getter for the remote object property \c CanUpdateRoles of type \c bool.
137 *
138 * \return A pending variant which will emit finished when the property has been
139 * retrieved.
140 */
141 inline Tp::PendingVariant *requestPropertyCanUpdateRoles() const
142 {
143 return internalRequestProperty(QLatin1String("CanUpdateRoles"));
144 }
145
146 /**
147 * Request all of the DBus properties on the interface.
148 *
149 * \return A pending variant map which will emit finished when the properties have
150 * been retrieved.
151 */
152 Tp::PendingVariantMap *requestAllProperties() const
153 {
154 return internalRequestAllProperties();
155 }
156
157 /**
158 * Synchronous version to get Roles property
159 */
160 HandleRolesMap getRoles() const;
161
162 /**
163 * Synchronous version to get CanUpdateRoles property
164 */
165 bool getCanUpdateRoles() const;
166
167public Q_SLOTS:
168 /**
169 * Begins a call to the D-Bus method \c UpdateRoles on the remote object.
170 *
171 * Update the roles in the server
172 *
173 */
174 inline QDBusPendingReply<> UpdateRoles(const HandleRolesMap &contactRoles, int timeout = -1)
175 {
176 if (!invalidationReason().isEmpty()) {
177 return QDBusPendingReply<>(QDBusMessage::createError(
178 invalidationReason(),
179 invalidationMessage()
180 ));
181 }
182
183 QDBusMessage callMessage = QDBusMessage::createMethodCall(this->service(), this->path(),
184 this->staticInterfaceName(), QLatin1String("UpdateRoles"));
185 callMessage << QVariant::fromValue(contactRoles);
186 return this->connection().asyncCall(callMessage, timeout);
187 }
188
189Q_SIGNALS:
190 /**
191 * Represents the signal \c RolesChanged on the remote object.
192 *
193 * Emitted when the state the roles of the channel has changed.
194 *
195 * \param added
196 *
197 * map of handles and related roles added
198 *
199 * \param removed
200 *
201 * map of handles and related roles removed
202 */
203 void RolesChanged(const HandleRolesMap &added, const HandleRolesMap& removed);
204
205protected:
206 virtual void invalidate(Tp::DBusProxy *, const QString &, const QString &);
207
208};
209
210#endif // CHANNELINTERFACEROLESINTERFACE_H
0211
=== modified file 'daemon/textchannelobserver.cpp'
--- daemon/textchannelobserver.cpp 2014-09-10 04:28:32 +0000
+++ daemon/textchannelobserver.cpp 2016-11-24 12:52:24 +0000
@@ -1,5 +1,5 @@
1/*1/*
2 * Copyright (C) 2012-2013 Canonical, Ltd.2 * Copyright (C) 2012-2016 Canonical, Ltd.
3 *3 *
4 * Authors:4 * Authors:
5 * Gustavo Pichorim Boiko <gustavo.boiko@canonical.com>5 * Gustavo Pichorim Boiko <gustavo.boiko@canonical.com>
@@ -43,6 +43,8 @@
43 SIGNAL(pendingMessageRemoved(const Tp::ReceivedMessage&)),43 SIGNAL(pendingMessageRemoved(const Tp::ReceivedMessage&)),
44 SLOT(onPendingMessageRemoved(const Tp::ReceivedMessage&)));44 SLOT(onPendingMessageRemoved(const Tp::ReceivedMessage&)));
4545
46 Q_EMIT channelAvailable(textChannel);
47
46 // process the messages that are already pending in the channel48 // process the messages that are already pending in the channel
47 Q_FOREACH(const Tp::ReceivedMessage &message, textChannel->messageQueue()) {49 Q_FOREACH(const Tp::ReceivedMessage &message, textChannel->messageQueue()) {
48 Q_EMIT messageReceived(textChannel, message);50 Q_EMIT messageReceived(textChannel, message);
4951
=== modified file 'daemon/textchannelobserver.h'
--- daemon/textchannelobserver.h 2013-07-12 14:30:18 +0000
+++ daemon/textchannelobserver.h 2016-11-24 12:52:24 +0000
@@ -1,5 +1,5 @@
1/*1/*
2 * Copyright (C) 2012-2013 Canonical, Ltd.2 * Copyright (C) 2012-2016 Canonical, Ltd.
3 *3 *
4 * Authors:4 * Authors:
5 * Gustavo Pichorim Boiko <gustavo.boiko@canonical.com>5 * Gustavo Pichorim Boiko <gustavo.boiko@canonical.com>
@@ -36,6 +36,7 @@
36 void onTextChannelAvailable(Tp::TextChannelPtr textChannel);36 void onTextChannelAvailable(Tp::TextChannelPtr textChannel);
3737
38Q_SIGNALS:38Q_SIGNALS:
39 void channelAvailable(const Tp::TextChannelPtr textChannel);
39 void messageReceived(const Tp::TextChannelPtr textChannel, const Tp::ReceivedMessage &message);40 void messageReceived(const Tp::TextChannelPtr textChannel, const Tp::ReceivedMessage &message);
40 void messageRead(const Tp::TextChannelPtr textChannel, const Tp::ReceivedMessage &message);41 void messageRead(const Tp::TextChannelPtr textChannel, const Tp::ReceivedMessage &message);
41 void messageSent(const Tp::TextChannelPtr textChannel, const Tp::Message &message, const QString &messageToken);42 void messageSent(const Tp::TextChannelPtr textChannel, const Tp::Message &message, const QString &messageToken);
4243
=== added file 'plugins/sqlite/schema/v13.sql'
--- plugins/sqlite/schema/v13.sql 1970-01-01 00:00:00 +0000
+++ plugins/sqlite/schema/v13.sql 2016-11-24 12:52:24 +0000
@@ -0,0 +1,44 @@
1ALTER TABLE threads ADD COLUMN chatType tinyint;
2ALTER TABLE thread_participants ADD COLUMN alias varchar(255);
3ALTER TABLE thread_participants ADD COLUMN state tinyint;
4CREATE TABLE chat_room_info (
5 accountId varchar(255),
6 type tinyint,
7 threadId varchar(255),
8 roomName varchar(255),
9 server varchar(255),
10 creator varchar(255),
11 creationTimestamp datetime,
12 anonymous boolean,
13 inviteOnly boolean,
14 participantLimit integer,
15 moderated boolean,
16 title varchar(1024),
17 description varchar(1024),
18 persistent boolean,
19 private boolean,
20 passwordProtected boolean,
21 password varchar(512),
22 passwordHint varchar(512),
23 canUpdateConfiguration boolean,
24 subject varchar(1024),
25 actor varchar(512),
26 timestamp datetime
27);
28UPDATE threads SET chatType = 0;
29UPDATE threads SET chatType=1 WHERE (SELECT COUNT(participantId) from thread_participants WHERE thread_participants.threadId=threads.threadId and thread_participants.accountId=threads.accountId AND thread_participants.type=threads.type)=1;
30UPDATE thread_participants SET state = 0;
31
32DROP TRIGGER threads_delete_trigger;
33CREATE TRIGGER threads_delete_trigger AFTER DELETE ON threads
34FOR EACH ROW
35BEGIN
36 DELETE FROM thread_participants WHERE
37 accountId=old.accountId AND
38 threadId=old.threadId AND
39 type=old.type;
40 DELETE FROM chat_room_info WHERE
41 accountId=old.accountId AND
42 threadId=old.threadId AND
43 type=old.type;
44END;
045
=== added file 'plugins/sqlite/schema/v14.sql'
--- plugins/sqlite/schema/v14.sql 1970-01-01 00:00:00 +0000
+++ plugins/sqlite/schema/v14.sql 2016-11-24 12:52:24 +0000
@@ -0,0 +1,82 @@
1DROP TRIGGER text_events_insert_trigger;
2CREATE TRIGGER text_events_insert_trigger AFTER INSERT ON text_events
3FOR EACH ROW WHEN new.messageType!=2
4BEGIN
5 UPDATE threads SET count=(SELECT count(eventId) FROM text_events WHERE
6 accountId=new.accountId AND
7 threadId=new.threadId AND
8 messageType!=2)
9 WHERE accountId=new.accountId AND threadId=new.threadId AND type=0;
10 UPDATE threads SET unreadCount=(SELECT count(eventId) FROM text_events WHERE
11 accountId=new.accountId AND threadId=new.threadId AND newEvent='1' AND messageType!=2)
12 WHERE accountId=new.accountId AND threadId=new.threadId AND type=0;
13 UPDATE threads SET lastEventId=(SELECT eventId FROM text_events WHERE
14 accountId=new.accountId AND
15 threadId=new.threadId AND
16 messageType!=2
17 ORDER BY timestamp DESC LIMIT 1)
18 WHERE accountId=new.accountId AND threadId=new.threadId AND type=0;
19 UPDATE threads SET lastEventTimestamp=(SELECT timestamp FROM text_events WHERE
20 accountId=new.accountId AND
21 threadId=new.threadId AND
22 messageType!=2
23 ORDER BY timestamp DESC LIMIT 1)
24 WHERE accountId=new.accountId AND threadId=new.threadId AND type=0;
25END;
26
27DROP TRIGGER text_events_update_trigger;
28CREATE TRIGGER text_events_update_trigger AFTER UPDATE ON text_events
29FOR EACH ROW WHEN new.messageType!=2
30BEGIN
31 UPDATE threads SET count=(SELECT count(eventId) FROM text_events WHERE
32 accountId=new.accountId AND
33 threadId=new.threadId AND
34 messageType!=2)
35 WHERE accountId=new.accountId AND threadId=new.threadId AND type=0;
36 UPDATE threads SET unreadCount=(SELECT count(eventId) FROM text_events WHERE
37 accountId=new.accountId AND threadId=new.threadId AND newEvent='1' AND messageType!=2)
38 WHERE accountId=new.accountId AND threadId=new.threadId AND type=0;
39 UPDATE threads SET lastEventId=(SELECT eventId FROM text_events WHERE
40 accountId=new.accountId AND
41 threadId=new.threadId AND
42 messageType!=2
43 ORDER BY timestamp DESC LIMIT 1)
44 WHERE accountId=new.accountId AND threadId=new.threadId AND type=0;
45 UPDATE threads SET lastEventTimestamp=(SELECT timestamp FROM text_events WHERE
46 accountId=new.accountId AND
47 threadId=new.threadId AND
48 messageType!=2
49 ORDER BY timestamp DESC LIMIT 1)
50 WHERE accountId=new.accountId AND threadId=new.threadId AND type=0;
51END;
52
53DROP TRIGGER text_events_delete_trigger;
54CREATE TRIGGER text_events_delete_trigger AFTER DELETE ON text_events
55FOR EACH ROW WHEN old.messageType!=2
56BEGIN
57 UPDATE threads SET count=(SELECT count(eventId) FROM text_events WHERE
58 accountId=old.accountId AND
59 threadId=old.threadId AND
60 messageType!=2)
61 WHERE accountId=old.accountId AND threadId=old.threadId AND type=0;
62 UPDATE threads SET unreadCount=(SELECT count(eventId) FROM text_events WHERE
63 accountId=old.accountId AND threadId=old.threadId AND newEvent='1' AND messageType!=2)
64 WHERE accountId=old.accountId AND threadId=old.threadId AND type=0;
65 UPDATE threads SET lastEventId=(SELECT eventId FROM text_events WHERE
66 accountId=old.accountId AND
67 threadId=old.threadId AND
68 messageType!=2
69 ORDER BY timestamp DESC LIMIT 1)
70 WHERE accountId=old.accountId AND threadId=old.threadId AND type=0;
71 UPDATE threads SET lastEventTimestamp=(SELECT timestamp FROM text_events WHERE
72 accountId=old.accountId AND
73 threadId=old.threadId AND
74 messageType!=2
75 ORDER BY timestamp DESC LIMIT 1)
76 WHERE accountId=old.accountId AND threadId=old.threadId AND type=0;
77 DELETE from text_event_attachments WHERE
78 accountId=old.accountId AND
79 threadId=old.threadId AND
80 eventId=old.eventId;
81END;
82
083
=== added file 'plugins/sqlite/schema/v15.sql'
--- plugins/sqlite/schema/v15.sql 1970-01-01 00:00:00 +0000
+++ plugins/sqlite/schema/v15.sql 2016-11-24 12:52:24 +0000
@@ -0,0 +1,1 @@
1ALTER TABLE thread_participants ADD COLUMN roles tinyint;
02
=== added file 'plugins/sqlite/schema/v16.sql'
--- plugins/sqlite/schema/v16.sql 1970-01-01 00:00:00 +0000
+++ plugins/sqlite/schema/v16.sql 2016-11-24 12:52:24 +0000
@@ -0,0 +1,2 @@
1ALTER TABLE chat_room_info ADD COLUMN joined boolean;
2ALTER TABLE chat_room_info ADD COLUMN selfRoles integer;
03
=== added file 'plugins/sqlite/schema/v17.sql'
--- plugins/sqlite/schema/v17.sql 1970-01-01 00:00:00 +0000
+++ plugins/sqlite/schema/v17.sql 2016-11-24 12:52:24 +0000
@@ -0,0 +1,2 @@
1ALTER TABLE text_events ADD COLUMN informationType integer;
2UPDATE text_events SET informationType = 1;
03
=== modified file 'plugins/sqlite/sqlitedatabase.cpp'
--- plugins/sqlite/sqlitedatabase.cpp 2015-10-06 12:54:04 +0000
+++ plugins/sqlite/sqlitedatabase.cpp 2016-11-24 12:52:24 +0000
@@ -207,6 +207,7 @@
207207
208 QStringList statements;208 QStringList statements;
209 int existingVersion = 0;209 int existingVersion = 0;
210 int upgradeToVersion = 0;
210211
211 if (create) {212 if (create) {
212 statements = parseSchemaFile(":/database/schema/schema.sql");213 statements = parseSchemaFile(":/database/schema/schema.sql");
@@ -219,7 +220,7 @@
219 }220 }
220221
221 existingVersion = query.value(0).toInt();222 existingVersion = query.value(0).toInt();
222 int upgradeToVersion = existingVersion + 1;223 upgradeToVersion = existingVersion + 1;
223 while (upgradeToVersion <= mSchemaVersion) {224 while (upgradeToVersion <= mSchemaVersion) {
224 statements += parseSchemaFile(QString(":/database/schema/v%1.sql").arg(QString::number(upgradeToVersion)));225 statements += parseSchemaFile(QString(":/database/schema/v%1.sql").arg(QString::number(upgradeToVersion)));
225 ++upgradeToVersion;226 ++upgradeToVersion;
@@ -256,6 +257,22 @@
256 return false;257 return false;
257 }258 }
258 }259 }
260 if (existingVersion < 13) {
261 // convert all ofono groups to Room type depending if the mms option is enabled
262 QVariant mmsGroupChatEnabled = History::Utils::getUserValue("com.ubuntu.touch.AccountsService.Phone", "MmsGroupChatEnabled");
263 // we must not fail if we cannot reach accounts service.
264 if (mmsGroupChatEnabled.isValid()) {
265 // if mms is disabled all chats will be turned into broadcast, otherwise
266 // we turn them into Room
267 if (mmsGroupChatEnabled.toBool()) {
268 if (!convertOfonoGroupChatToRoom()) {
269 qCritical() << "Failed to update existing group chats to Room type.";
270 rollbackTransaction();
271 return false;
272 }
273 }
274 }
275 }
259 }276 }
260277
261 finishTransaction();278 finishTransaction();
@@ -392,3 +409,40 @@
392409
393 return true;410 return true;
394}411}
412
413bool SQLiteDatabase::convertOfonoGroupChatToRoom()
414{
415 QSqlQuery query(database());
416 QString queryText = "UPDATE threads SET chatType=2 WHERE accountId LIKE 'ofono/ofono%' AND (SELECT COUNT(participantId) from thread_participants WHERE thread_participants.threadId=threads.threadId and thread_participants.accountId=threads.accountId AND thread_participants.type=threads.type) > 1";
417
418 query.prepare(queryText);
419 if (!query.exec()) {
420 qWarning() << "Failed to update group chats to Room 1:" << query.executedQuery() << query.lastError();
421 return false;
422 }
423 query.clear();
424
425 // now insert a row in chat_room_info for each room
426 if (!query.exec("SELECT accountId, threadId from threads WHERE accountId LIKE 'ofono/ofono%' AND chatType=2")) {
427 qWarning() << "Failed to update group chats to Room 2:" << query.executedQuery() << query.lastError();
428 return false;
429 }
430
431 while (query.next()) {
432 QSqlQuery queryInsertRoom(database());
433 QString accountId = query.value(0).toString();
434 QString threadId = query.value(1).toString();
435 queryInsertRoom.prepare("INSERT INTO chat_room_info (accountId, threadId, type, joined) VALUES (:accountId,:threadId,:type,:joined)");
436 queryInsertRoom.bindValue(":accountId", accountId);
437 queryInsertRoom.bindValue(":threadId", threadId);
438 queryInsertRoom.bindValue(":type", History::EventTypeText);
439 queryInsertRoom.bindValue(":joined", true);
440 if(!queryInsertRoom.exec()) {
441 qWarning() << "Failed to update group chats to Room 3:" << queryInsertRoom.executedQuery() << queryInsertRoom.lastError();
442 return false;
443 }
444 queryInsertRoom.clear();
445 }
446 query.clear();
447}
448
395449
=== modified file 'plugins/sqlite/sqlitedatabase.h'
--- plugins/sqlite/sqlitedatabase.h 2015-09-02 18:14:15 +0000
+++ plugins/sqlite/sqlitedatabase.h 2016-11-24 12:52:24 +0000
@@ -50,6 +50,7 @@
5050
51 // data upgrade functions51 // data upgrade functions
52 bool changeTimestampsToUtc();52 bool changeTimestampsToUtc();
53 bool convertOfonoGroupChatToRoom();
5354
54private:55private:
55 explicit SQLiteDatabase(QObject *parent = 0);56 explicit SQLiteDatabase(QObject *parent = 0);
5657
=== modified file 'plugins/sqlite/sqlitehistoryplugin.cpp'
--- plugins/sqlite/sqlitehistoryplugin.cpp 2015-11-20 11:40:36 +0000
+++ plugins/sqlite/sqlitehistoryplugin.cpp 2016-11-24 12:52:24 +0000
@@ -1,5 +1,5 @@
1/*1/*
2 * Copyright (C) 2013-2015 Canonical, Ltd.2 * Copyright (C) 2013-2016 Canonical, Ltd.
3 *3 *
4 * Authors:4 * Authors:
5 * Gustavo Pichorim Boiko <gustavo.boiko@canonical.com>5 * Gustavo Pichorim Boiko <gustavo.boiko@canonical.com>
@@ -35,17 +35,20 @@
35#include <QStringList>35#include <QStringList>
36#include <QSqlError>36#include <QSqlError>
37#include <QDBusMetaType>37#include <QDBusMetaType>
38#include <QCryptographicHash>
3839
39QString generateThreadMapKey(const History::Thread &thread)40static const QLatin1String timestampFormat("yyyy-MM-ddTHH:mm:ss.zzz");
40{
41 return thread.accountId() + thread.threadId();
42}
4341
44QString generateThreadMapKey(const QString &accountId, const QString &threadId)42QString generateThreadMapKey(const QString &accountId, const QString &threadId)
45{43{
46 return accountId + threadId;44 return accountId + threadId;
47}45}
4846
47QString generateThreadMapKey(const History::Thread &thread)
48{
49 return generateThreadMapKey(thread.accountId(), thread.threadId());
50}
51
49SQLiteHistoryPlugin::SQLiteHistoryPlugin(QObject *parent) :52SQLiteHistoryPlugin::SQLiteHistoryPlugin(QObject *parent) :
50 QObject(parent), mInitialised(false)53 QObject(parent), mInitialised(false)
51{54{
@@ -81,18 +84,18 @@
81 // so instead we just convert to UTC here on the cache and convert back to local time84 // so instead we just convert to UTC here on the cache and convert back to local time
82 // when returning85 // when returning
83 QDateTime timestamp = QDateTime::fromString(properties[History::FieldTimestamp].toString(), Qt::ISODate);86 QDateTime timestamp = QDateTime::fromString(properties[History::FieldTimestamp].toString(), Qt::ISODate);
84 properties[History::FieldTimestamp] = timestamp.toUTC().toString("yyyy-MM-ddTHH:mm:ss.zzz");87 properties[History::FieldTimestamp] = timestamp.toUTC().toString(timestampFormat);
8588
86 // the same for readTimestamp89 // the same for readTimestamp
87 timestamp = QDateTime::fromString(properties[History::FieldReadTimestamp].toString(), Qt::ISODate);90 timestamp = QDateTime::fromString(properties[History::FieldReadTimestamp].toString(), Qt::ISODate);
88 properties[History::FieldReadTimestamp] = timestamp.toUTC().toString("yyyy-MM-ddTHH:mm:ss.zzz");91 properties[History::FieldReadTimestamp] = timestamp.toUTC().toString(timestampFormat);
8992
90 History::Thread thread = History::Thread::fromProperties(properties);93 History::Thread thread = History::Thread::fromProperties(properties);
91 const QString &threadKey = generateThreadMapKey(thread);94 const QString &threadKey = generateThreadMapKey(thread);
9295
93 if (thread.type() != History::EventTypeText) {96 if (thread.type() != History::EventTypeText) {
94 continue;97 continue;
95 } else if (!History::Utils::shouldGroupAccount(thread.accountId())) {98 } else if (!History::Utils::shouldGroupThread(thread)) {
96 // never group non phone accounts99 // never group non phone accounts
97 mConversationsCache[threadKey] = History::Threads() << thread;100 mConversationsCache[threadKey] = History::Threads() << thread;
98 mConversationsCacheKeys[threadKey] = threadKey;101 mConversationsCacheKeys[threadKey] = threadKey;
@@ -119,6 +122,9 @@
119 const QString &conversationKey = it.key();122 const QString &conversationKey = it.key();
120 History::Threads groupedThreads = it.value();123 History::Threads groupedThreads = it.value();
121 Q_FOREACH(const History::Thread &groupedThread, groupedThreads) {124 Q_FOREACH(const History::Thread &groupedThread, groupedThreads) {
125 if (!History::Utils::shouldGroupThread(groupedThread) || thread.chatType() != groupedThread.chatType()) {
126 continue;
127 }
122 found = History::Utils::compareNormalizedParticipants(thread.participants().identifiers(), groupedThread.participants().identifiers(), History::MatchPhoneNumber);128 found = History::Utils::compareNormalizedParticipants(thread.participants().identifiers(), groupedThread.participants().identifiers(), History::MatchPhoneNumber);
123 if (found) {129 if (found) {
124 Q_FOREACH(const History::Thread &groupedThread, groupedThreads) {130 Q_FOREACH(const History::Thread &groupedThread, groupedThreads) {
@@ -176,7 +182,7 @@
176 History::Thread thread = History::Thread::fromProperties(properties);182 History::Thread thread = History::Thread::fromProperties(properties);
177 QString threadKey = generateThreadMapKey(thread);183 QString threadKey = generateThreadMapKey(thread);
178 184
179 if (thread.type() != History::EventTypeText || !History::Utils::shouldGroupAccount(thread.accountId())) {185 if (thread.type() != History::EventTypeText || !History::Utils::shouldGroupThread(thread)) {
180 mConversationsCache.remove(threadKey);186 mConversationsCache.remove(threadKey);
181 mConversationsCacheKeys.remove(threadKey);187 mConversationsCacheKeys.remove(threadKey);
182 return;188 return;
@@ -259,7 +265,7 @@
259 time.start();265 time.start();
260 qDebug() << "---- HistoryService: start generating cached content";266 qDebug() << "---- HistoryService: start generating cached content";
261 QSqlQuery query(SQLiteDatabase::instance()->database());267 QSqlQuery query(SQLiteDatabase::instance()->database());
262 if (!query.exec("SELECT DISTINCT accountId, normalizedId FROM thread_participants")) {268 if (!query.exec("SELECT DISTINCT accountId, normalizedId, alias, state FROM thread_participants")) {
263 qWarning() << "Failed to generate contact cache:" << query.lastError().text();269 qWarning() << "Failed to generate contact cache:" << query.lastError().text();
264 return;270 return;
265 }271 }
@@ -267,9 +273,14 @@
267 while (query.next()) {273 while (query.next()) {
268 QString accountId = query.value(0).toString();274 QString accountId = query.value(0).toString();
269 QString participantId = query.value(1).toString();275 QString participantId = query.value(1).toString();
276 QString alias = query.value(2).toString();
277 QVariantMap properties;
278 if (!alias.isEmpty()) {
279 properties[History::FieldAlias] = alias;
280 }
270 // we don't care about the results, as long as the contact data is present in the cache for281 // we don't care about the results, as long as the contact data is present in the cache for
271 // future usage.282 // future usage.
272 History::ContactMatcher::instance()->contactInfo(accountId, participantId, true);283 History::ContactMatcher::instance()->contactInfo(accountId, participantId, true, properties);
273 }284 }
274285
275 updateGroupedThreadsCache();286 updateGroupedThreadsCache();
@@ -295,6 +306,32 @@
295 return new SQLiteHistoryEventView(this, type, sort, filter);306 return new SQLiteHistoryEventView(this, type, sort, filter);
296}307}
297308
309QVariantMap SQLiteHistoryPlugin::threadForProperties(const QString &accountId,
310 History::EventType type,
311 const QVariantMap &properties,
312 History::MatchFlags matchFlags)
313{
314 if (properties.isEmpty()) {
315 return QVariantMap();
316 }
317
318 QSqlQuery query(SQLiteDatabase::instance()->database());
319
320 History::ChatType chatType = (History::ChatType)properties[History::FieldChatType].toUInt();
321
322 if (chatType == History::ChatTypeRoom) {
323 QString threadId = properties[History::FieldThreadId].toString();
324 if (threadId.isEmpty()) {
325 return QVariantMap();
326 }
327 return getSingleThread(type, accountId, threadId);
328 }
329
330 History::Participants participants = History::Participants::fromVariant(properties[History::FieldParticipantIds]);
331 // if chatType != Room, then we select the thread based on the participant list.
332 return threadForParticipants(accountId, type, participants.identifiers(), matchFlags);
333}
334
298QVariantMap SQLiteHistoryPlugin::threadForParticipants(const QString &accountId,335QVariantMap SQLiteHistoryPlugin::threadForParticipants(const QString &accountId,
299 History::EventType type,336 History::EventType type,
300 const QStringList &participants,337 const QStringList &participants,
@@ -310,7 +347,9 @@
310 // select all the threads the first participant is listed in, and from that list347 // select all the threads the first participant is listed in, and from that list
311 // check if any of the threads has all the other participants listed348 // check if any of the threads has all the other participants listed
312 // FIXME: find a better way to do this349 // FIXME: find a better way to do this
313 QString queryString("SELECT threadId FROM thread_participants WHERE %1 AND type=:type AND accountId=:accountId");350 QString queryString("SELECT threadId FROM thread_participants WHERE %1 AND type=:type AND accountId=:accountId "
351 "AND (SELECT chatType FROM threads WHERE threads.threadId=thread_participants.threadId AND "
352 " threads.type=thread_participants.type)!=:chatType");
314353
315 // FIXME: for now we just compare differently when using MatchPhoneNumber354 // FIXME: for now we just compare differently when using MatchPhoneNumber
316 QString firstParticipant = participants.first();355 QString firstParticipant = participants.first();
@@ -324,6 +363,9 @@
324 query.bindValue(":participantId", firstParticipant);363 query.bindValue(":participantId", firstParticipant);
325 query.bindValue(":type", type);364 query.bindValue(":type", type);
326 query.bindValue(":accountId", accountId);365 query.bindValue(":accountId", accountId);
366 // we don't want to accidentally return a chat room for a multi-recipient conversation
367 query.bindValue(":chatType", (int)History::ChatTypeRoom);
368
327 if (!query.exec()) {369 if (!query.exec()) {
328 qCritical() << "Error:" << query.lastError() << query.lastQuery();370 qCritical() << "Error:" << query.lastError() << query.lastQuery();
329 return QVariantMap();371 return QVariantMap();
@@ -469,58 +511,345 @@
469 return result;511 return result;
470}512}
471513
472// Writer514bool SQLiteHistoryPlugin::updateRoomParticipants(const QString &accountId, const QString &threadId, History::EventType type, const QVariantList &participants)
473QVariantMap SQLiteHistoryPlugin::createThreadForParticipants(const QString &accountId, History::EventType type, const QStringList &participants)515{
516 QSqlQuery query(SQLiteDatabase::instance()->database());
517 if (accountId.isEmpty() || threadId.isEmpty()) {
518 return false;
519 }
520
521 SQLiteDatabase::instance()->beginTransation();
522 QString deleteString("DELETE FROM thread_participants WHERE threadId=:threadId AND type=:type AND accountId=:accountId");
523 query.prepare(deleteString);
524 query.bindValue(":accountId", accountId);
525 query.bindValue(":threadId", threadId);
526 query.bindValue(":type", type);
527 if (!query.exec()) {
528 qCritical() << "Error removing old participants:" << query.lastError() << query.lastQuery();
529 SQLiteDatabase::instance()->rollbackTransaction();
530 return false;
531 }
532
533 // and insert the participants
534 Q_FOREACH(const QVariant &participantVariant, participants) {
535 QVariantMap participant = participantVariant.toMap();
536 query.prepare("INSERT INTO thread_participants (accountId, threadId, type, participantId, normalizedId, alias, state, roles)"
537 "VALUES (:accountId, :threadId, :type, :participantId, :normalizedId, :alias, :state, :roles)");
538 query.bindValue(":accountId", accountId);
539 query.bindValue(":threadId", threadId);
540 query.bindValue(":type", type);
541 query.bindValue(":participantId", participant["identifier"].toString());
542 query.bindValue(":normalizedId", participant["identifier"].toString());
543 query.bindValue(":alias", participant["alias"].toString());
544 query.bindValue(":state", participant["state"].toUInt());
545 query.bindValue(":roles", participant["roles"].toUInt());
546 if (!query.exec()) {
547 qCritical() << "Error:" << query.lastError() << query.lastQuery();
548 SQLiteDatabase::instance()->rollbackTransaction();
549 return false;
550 }
551 }
552
553 if (!SQLiteDatabase::instance()->finishTransaction()) {
554 qCritical() << "Failed to commit the transaction.";
555 return false;
556 }
557
558 QVariantMap existingThread = getSingleThread(type,
559 accountId,
560 threadId,
561 QVariantMap());
562
563 if (!existingThread.isEmpty()) {
564 addThreadsToCache(QList<QVariantMap>() << existingThread);
565 }
566
567 return true;
568}
569
570bool SQLiteHistoryPlugin::updateRoomParticipantsRoles(const QString &accountId, const QString &threadId, History::EventType type, const QVariantMap &participantsRoles)
571{
572 QSqlQuery query(SQLiteDatabase::instance()->database());
573 if (accountId.isEmpty() || threadId.isEmpty()) {
574 return false;
575 }
576
577 SQLiteDatabase::instance()->beginTransation();
578 Q_FOREACH(const QString &participantId, participantsRoles.keys()) {
579 query.prepare("UPDATE thread_participants SET roles=:roles WHERE accountId=:accountId AND threadId=:threadId AND type=:type AND participantId=:participantId");
580 query.bindValue(":roles", participantsRoles.value(participantId).toUInt());
581 query.bindValue(":accountId", accountId);
582 query.bindValue(":threadId", threadId);
583 query.bindValue(":type", type);
584 query.bindValue(":participantId", participantId);
585 if (!query.exec()) {
586 qCritical() << "Error:" << query.lastError() << query.lastQuery();
587 SQLiteDatabase::instance()->rollbackTransaction();
588 return false;
589 }
590 }
591
592 if (!SQLiteDatabase::instance()->finishTransaction()) {
593 qCritical() << "Failed to commit the transaction.";
594 return false;
595 }
596
597 QVariantMap existingThread = getSingleThread(type,
598 accountId,
599 threadId,
600 QVariantMap());
601
602 if (!existingThread.isEmpty()) {
603 addThreadsToCache(QList<QVariantMap>() << existingThread);
604 }
605
606 return true;
607}
608
609bool SQLiteHistoryPlugin::updateRoomInfo(const QString &accountId, const QString &threadId, History::EventType type, const QVariantMap &properties, const QStringList &invalidated)
610{
611 QSqlQuery query(SQLiteDatabase::instance()->database());
612
613 if (threadId.isEmpty() || accountId.isEmpty()) {
614 return false;
615 }
616
617 SQLiteDatabase::instance()->beginTransation();
618
619 QDateTime creationTimestamp = QDateTime::fromTime_t(properties["CreationTimestamp"].toUInt());
620 QDateTime timestamp = QDateTime::fromTime_t(properties["Timestamp"].toUInt());
621
622 QVariantMap propertyMapping;
623 propertyMapping["RoomName"] = "roomName";
624 propertyMapping["Server"] = "server";
625 propertyMapping["Creator"] = "creator";
626 propertyMapping["CreationTimestamp"] = "creationTimestamp";
627 propertyMapping["Anonymous"] = "anonymous";
628 propertyMapping["InviteOnly"] = "inviteOnly";
629 propertyMapping["Limit"] = "participantLimit";
630 propertyMapping["Moderated"] = "moderated";
631 propertyMapping["Title"] = "title";
632 propertyMapping["Description"] = "description";
633 propertyMapping["Persistent"] = "persistent";
634 propertyMapping["Private"] = "private";
635 propertyMapping["PasswordProtected"] = "passwordProtected";
636 propertyMapping["Password"] = "password";
637 propertyMapping["PasswordHint"] = "passwordHint";
638 propertyMapping["CanUpdateConfiguration"] = "canUpdateConfiguration";
639 propertyMapping["Subject"] = "subject";
640 propertyMapping["Actor"] = "actor";
641 propertyMapping["Timestamp"] = "timestamp";
642 propertyMapping["Joined"] = "joined";
643 propertyMapping["SelfRoles"] = "selfRoles";
644
645 QStringList changedPropListValues;
646 // populate sql query
647 Q_FOREACH (const QString &key, properties.keys()) {
648 if (propertyMapping.contains(key)) {
649 QString prop = propertyMapping[key].toString();
650 changedPropListValues << QString(prop+"=:"+ prop);
651 }
652 }
653 if (changedPropListValues.isEmpty()) {
654 return false;
655 }
656
657 query.prepare("UPDATE chat_room_info SET "+ changedPropListValues.join(", ")+" WHERE accountId=:accountId AND threadId=:threadId AND type=:type");
658 query.bindValue(":accountId", accountId);
659 query.bindValue(":threadId", threadId);
660 query.bindValue(":type", (int) type);
661 query.bindValue(":roomName", properties["RoomName"].toString());
662 query.bindValue(":server", properties["Server"].toString());
663 query.bindValue(":creator", properties["Creator"].toString());
664 query.bindValue(":creationTimestamp", creationTimestamp.toUTC().toString(timestampFormat));
665 query.bindValue(":anonymous", properties["Anonymous"].toBool());
666 query.bindValue(":inviteOnly", properties["InviteOnly"].toBool());
667 query.bindValue(":participantLimit", properties["Limit"].toInt());
668 query.bindValue(":moderated", properties["Moderated"].toBool());
669 query.bindValue(":title", properties["Title"].toString());
670 query.bindValue(":description", properties["Description"].toString());
671 query.bindValue(":persistent", properties["Persistent"].toBool());
672 query.bindValue(":private", properties["Private"].toBool());
673 query.bindValue(":passwordProtected", properties["PasswordProtected"].toBool());
674 query.bindValue(":password", properties["Password"].toString());
675 query.bindValue(":passwordHint", properties["PasswordHint"].toString());
676 query.bindValue(":canUpdateConfiguration", properties["CanUpdateConfiguration"].toBool());
677 query.bindValue(":subject", properties["Subject"].toString());
678 query.bindValue(":actor", properties["Actor"].toString());
679 query.bindValue(":timestamp", timestamp.toUTC().toString(timestampFormat));
680 query.bindValue(":joined", properties["Joined"].toBool());
681 query.bindValue(":selfRoles", properties["SelfRoles"].toInt());
682
683 if (!query.exec()) {
684 qCritical() << "Error:" << query.lastError() << query.lastQuery();
685 SQLiteDatabase::instance()->rollbackTransaction();
686 return false;
687 }
688
689 if (!SQLiteDatabase::instance()->finishTransaction()) {
690 qCritical() << "Failed to commit the transaction.";
691 return false;
692 }
693
694 QVariantMap existingThread = getSingleThread(type,
695 accountId,
696 threadId,
697 QVariantMap());
698
699 if (!existingThread.isEmpty()) {
700 addThreadsToCache(QList<QVariantMap>() << existingThread);
701 }
702
703 return true;
704}
705
706QVariantMap SQLiteHistoryPlugin::createThreadForProperties(const QString &accountId, History::EventType type, const QVariantMap &properties)
474{707{
475 // WARNING: this function does NOT test to check if the thread is already created, you should check using HistoryReader::threadForParticipants()708 // WARNING: this function does NOT test to check if the thread is already created, you should check using HistoryReader::threadForParticipants()
476709
477 QVariantMap thread;710 QVariantMap thread;
711 History::Participants participants = History::Participants::fromVariant(properties[History::FieldParticipantIds]);
478712
479 // Create a new thread713 // Create a new thread
480 // FIXME: define what the threadId will be714 // FIXME: define what the threadId will be
481 QString threadId = participants.join("%");715 QString threadId;
716 History::ChatType chatType = (History::ChatType)properties[History::FieldChatType].toInt();
717 QVariantMap chatRoomInfo;
718
719 SQLiteDatabase::instance()->beginTransation();
720
721 if (chatType == History::ChatTypeRoom) {
722 threadId = properties[History::FieldThreadId].toString();
723 // we cannot save chat room without threadId
724 if (accountId.isEmpty() || threadId.isEmpty()) {
725 SQLiteDatabase::instance()->rollbackTransaction();
726 return thread;
727 }
728 chatRoomInfo = properties[History::FieldChatRoomInfo].toMap();
729 QSqlQuery query(SQLiteDatabase::instance()->database());
730
731 QDateTime creationTimestamp = QDateTime::fromTime_t(chatRoomInfo["CreationTimestamp"].toUInt());
732 QDateTime timestamp = QDateTime::fromTime_t(chatRoomInfo["Timestamp"].toUInt());
733
734 query.prepare("INSERT INTO chat_room_info (accountId, threadId, type, roomName, server, creator, creationTimestamp, anonymous, inviteOnly, participantLimit, moderated, title, description, persistent, private, passwordProtected, password, passwordHint, canUpdateConfiguration, subject, actor, timestamp, joined, selfRoles) "
735 "VALUES (:accountId, :threadId, :type, :roomName, :server, :creator, :creationTimestamp, :anonymous, :inviteOnly, :participantLimit, :moderated, :title, :description, :persistent, :private, :passwordProtected, :password, :passwordHint, :canUpdateConfiguration, :subject, :actor, :timestamp, :joined, :selfRoles)");
736 query.bindValue(":accountId", accountId);
737 query.bindValue(":threadId", threadId);
738 query.bindValue(":type", (int) type);
739 query.bindValue(":roomName", chatRoomInfo["RoomName"].toString());
740 query.bindValue(":server", chatRoomInfo["Server"].toString());
741 query.bindValue(":creator", chatRoomInfo["Creator"].toString());
742 query.bindValue(":creationTimestamp", creationTimestamp.toUTC().toString(timestampFormat));
743 query.bindValue(":anonymous", chatRoomInfo["Anonymous"].toBool());
744 query.bindValue(":inviteOnly", chatRoomInfo["InviteOnly"].toBool());
745 query.bindValue(":participantLimit", chatRoomInfo["Limit"].toInt());
746 query.bindValue(":moderated", chatRoomInfo["Moderated"].toBool());
747 query.bindValue(":title", chatRoomInfo["Title"].toString());
748 query.bindValue(":description", chatRoomInfo["Description"].toString());
749 query.bindValue(":persistent", chatRoomInfo["Persistent"].toBool());
750 query.bindValue(":private", chatRoomInfo["Private"].toBool());
751 query.bindValue(":passwordProtected", chatRoomInfo["PasswordProtected"].toBool());
752 query.bindValue(":password", chatRoomInfo["Password"].toString());
753 query.bindValue(":passwordHint", chatRoomInfo["PasswordHint"].toString());
754 query.bindValue(":canUpdateConfiguration", chatRoomInfo["CanUpdateConfiguration"].toBool());
755 query.bindValue(":subject", chatRoomInfo["Subject"].toString());
756 query.bindValue(":actor", chatRoomInfo["Actor"].toString());
757 query.bindValue(":timestamp", timestamp.toUTC().toString(timestampFormat));
758 query.bindValue(":joined", chatRoomInfo["Joined"].toBool());
759 query.bindValue(":selfRoles", chatRoomInfo["SelfRoles"].toInt());
760
761 if (!query.exec()) {
762 qCritical() << "Error:" << query.lastError() << query.lastQuery();
763 SQLiteDatabase::instance()->rollbackTransaction();
764 return QVariantMap();
765 }
766 for (QVariantMap::iterator iter = chatRoomInfo.begin(); iter != chatRoomInfo.end();) {
767 if (!iter.value().isValid()) {
768 iter = chatRoomInfo.erase(iter);
769 } else {
770 iter++;
771 }
772 }
773 thread[History::FieldChatRoomInfo] = chatRoomInfo;
774 } else if (chatType == History::ChatTypeContact) {
775 threadId = participants.identifiers().join("%");
776 } else {
777 threadId = QString("broadcast:%1").arg(QString(QCryptographicHash::hash(participants.identifiers().join(";").toLocal8Bit(),QCryptographicHash::Md5).toHex()));;
778 }
482779
483 QSqlQuery query(SQLiteDatabase::instance()->database());780 QSqlQuery query(SQLiteDatabase::instance()->database());
484 query.prepare("INSERT INTO threads (accountId, threadId, type, count, unreadCount)"781 query.prepare("INSERT INTO threads (accountId, threadId, type, count, unreadCount, chatType, lastEventTimestamp)"
485 "VALUES (:accountId, :threadId, :type, :count, :unreadCount)");782 "VALUES (:accountId, :threadId, :type, :count, :unreadCount, :chatType, :lastEventTimestamp)");
486 query.bindValue(":accountId", accountId);783 query.bindValue(":accountId", accountId);
487 query.bindValue(":threadId", threadId);784 query.bindValue(":threadId", threadId);
488 query.bindValue(":type", (int) type);785 query.bindValue(":type", (int) type);
489 query.bindValue(":count", 0);786 query.bindValue(":count", 0);
490 query.bindValue(":unreadCount", 0);787 query.bindValue(":unreadCount", 0);
788 query.bindValue(":chatType", (int) chatType);
789 // make sure threads are created with an up-to-date timestamp
790 query.bindValue(":lastEventTimestamp", QDateTime::currentDateTimeUtc().toString(timestampFormat));
491 if (!query.exec()) {791 if (!query.exec()) {
492 qCritical() << "Error:" << query.lastError() << query.lastQuery();792 qCritical() << "Error:" << query.lastError() << query.lastQuery();
793 SQLiteDatabase::instance()->rollbackTransaction();
493 return QVariantMap();794 return QVariantMap();
494 }795 }
495796
496 // and insert the participants797 // and insert the participants
497 Q_FOREACH(const QString &participant, participants) {798 Q_FOREACH(const History::Participant &participant, participants) {
498 query.prepare("INSERT INTO thread_participants (accountId, threadId, type, participantId, normalizedId)"799 query.prepare("INSERT INTO thread_participants (accountId, threadId, type, participantId, normalizedId, alias, state, roles)"
499 "VALUES (:accountId, :threadId, :type, :participantId, :normalizedId)");800 "VALUES (:accountId, :threadId, :type, :participantId, :normalizedId, :alias, :state, :roles)");
500 query.bindValue(":accountId", accountId);801 query.bindValue(":accountId", accountId);
501 query.bindValue(":threadId", threadId);802 query.bindValue(":threadId", threadId);
502 query.bindValue(":type", type);803 query.bindValue(":type", type);
503 query.bindValue(":participantId", participant);804 query.bindValue(":participantId", participant.identifier());
504 query.bindValue(":normalizedId", History::Utils::normalizeId(accountId, participant));805 query.bindValue(":normalizedId", History::Utils::normalizeId(accountId, participant.identifier()));
806 query.bindValue(":alias", participant.alias());
807 query.bindValue(":state", participant.state());
808 query.bindValue(":roles", participant.roles());
505 if (!query.exec()) {809 if (!query.exec()) {
506 qCritical() << "Error:" << query.lastError() << query.lastQuery();810 qCritical() << "Error:" << query.lastError() << query.lastQuery();
811 SQLiteDatabase::instance()->rollbackTransaction();
507 return QVariantMap();812 return QVariantMap();
508 }813 }
509 }814 }
510815
816 if (!SQLiteDatabase::instance()->finishTransaction()) {
817 qCritical() << "Failed to commit the transaction.";
818 return QVariantMap();
819 }
820
511 // and finally create the thread821 // and finally create the thread
512 thread[History::FieldAccountId] = accountId;822 thread[History::FieldAccountId] = accountId;
513 thread[History::FieldThreadId] = threadId;823 thread[History::FieldThreadId] = threadId;
514 thread[History::FieldType] = (int) type;824 thread[History::FieldType] = (int) type;
515 thread[History::FieldParticipants] = History::ContactMatcher::instance()->contactInfo(accountId, participants, true);825 QVariantList contactList;
826 QVariantList contactInfo = History::ContactMatcher::instance()->contactInfo(accountId, participants.identifiers(), true);
827 for (int i = 0; i < participants.count(); ++i) {
828 QVariantMap map = contactInfo[i].toMap();
829 History::Participant participant = participants[i];
830 map["state"] = participant.state();
831 map["roles"] = participant.roles();
832 contactList << map;
833 }
834 thread[History::FieldParticipants] = contactList;
516 thread[History::FieldCount] = 0;835 thread[History::FieldCount] = 0;
517 thread[History::FieldUnreadCount] = 0;836 thread[History::FieldUnreadCount] = 0;
837 thread[History::FieldChatType] = (int)chatType;
518838
519 addThreadsToCache(QList<QVariantMap>() << thread);839 addThreadsToCache(QList<QVariantMap>() << thread);
520840
521 return thread;841 return thread;
522}842}
523843
844// Writer
845QVariantMap SQLiteHistoryPlugin::createThreadForParticipants(const QString &accountId, History::EventType type, const QStringList &participants)
846{
847 QVariantMap properties;
848 properties[History::FieldParticipantIds] = participants;
849 properties[History::FieldChatType] = participants.size() != 1 ? History::ChatTypeNone : History::ChatTypeContact;
850 return createThreadForProperties(accountId, type, properties);
851}
852
524bool SQLiteHistoryPlugin::removeThread(const QVariantMap &thread)853bool SQLiteHistoryPlugin::removeThread(const QVariantMap &thread)
525{854{
526 QSqlQuery query(SQLiteDatabase::instance()->database());855 QSqlQuery query(SQLiteDatabase::instance()->database());
@@ -550,16 +879,18 @@
550 event[History::FieldThreadId].toString(),879 event[History::FieldThreadId].toString(),
551 event[History::FieldEventId].toString());880 event[History::FieldEventId].toString());
552881
882 SQLiteDatabase::instance()->beginTransation();
883
553 History::EventWriteResult result;884 History::EventWriteResult result;
554 if (existingEvent.isEmpty()) {885 if (existingEvent.isEmpty()) {
555 // create new886 // create new
556 query.prepare("INSERT INTO text_events (accountId, threadId, eventId, senderId, timestamp, newEvent, message, messageType, messageStatus, readTimestamp, subject) "887 query.prepare("INSERT INTO text_events (accountId, threadId, eventId, senderId, timestamp, newEvent, message, messageType, messageStatus, readTimestamp, subject, informationType)"
557 "VALUES (:accountId, :threadId, :eventId, :senderId, :timestamp, :newEvent, :message, :messageType, :messageStatus, :readTimestamp, :subject)");888 "VALUES (:accountId, :threadId, :eventId, :senderId, :timestamp, :newEvent, :message, :messageType, :messageStatus, :readTimestamp, :subject, :informationType)");
558 result = History::EventWriteCreated;889 result = History::EventWriteCreated;
559 } else {890 } else {
560 // update existing event891 // update existing event
561 query.prepare("UPDATE text_events SET senderId=:senderId, timestamp=:timestamp, newEvent=:newEvent, message=:message, messageType=:messageType,"892 query.prepare("UPDATE text_events SET senderId=:senderId, timestamp=:timestamp, newEvent=:newEvent, message=:message, messageType=:messageType, informationType=:informationType, "
562 "messageStatus=:messageStatus, readTimestamp=:readTimestamp, subject=:subject WHERE accountId=:accountId AND threadId=:threadId AND eventId=:eventId");893 "messageStatus=:messageStatus, readTimestamp=:readTimestamp, subject=:subject, informationType=:informationType WHERE accountId=:accountId AND threadId=:threadId AND eventId=:eventId");
563 result = History::EventWriteModified;894 result = History::EventWriteModified;
564 }895 }
565896
@@ -574,9 +905,11 @@
574 query.bindValue(":messageStatus", event[History::FieldMessageStatus]);905 query.bindValue(":messageStatus", event[History::FieldMessageStatus]);
575 query.bindValue(":readTimestamp", event[History::FieldReadTimestamp].toDateTime().toUTC());906 query.bindValue(":readTimestamp", event[History::FieldReadTimestamp].toDateTime().toUTC());
576 query.bindValue(":subject", event[History::FieldSubject].toString());907 query.bindValue(":subject", event[History::FieldSubject].toString());
908 query.bindValue(":informationType", event[History::FieldInformationType].toInt());
577909
578 if (!query.exec()) {910 if (!query.exec()) {
579 qCritical() << "Failed to save the text event: Error:" << query.lastError() << query.lastQuery();911 qCritical() << "Failed to save the text event: Error:" << query.lastError() << query.lastQuery();
912 SQLiteDatabase::instance()->rollbackTransaction();
580 return History::EventWriteError;913 return History::EventWriteError;
581 }914 }
582915
@@ -592,6 +925,7 @@
592 query.bindValue(":eventId", event[History::FieldEventId]);925 query.bindValue(":eventId", event[History::FieldEventId]);
593 if (!query.exec()) {926 if (!query.exec()) {
594 qCritical() << "Could not erase previous attachments. Error:" << query.lastError() << query.lastQuery();927 qCritical() << "Could not erase previous attachments. Error:" << query.lastError() << query.lastQuery();
928 SQLiteDatabase::instance()->rollbackTransaction();
595 return History::EventWriteError;929 return History::EventWriteError;
596 }930 }
597 }931 }
@@ -608,11 +942,17 @@
608 query.bindValue(":status", attachment[History::FieldStatus]);942 query.bindValue(":status", attachment[History::FieldStatus]);
609 if (!query.exec()) {943 if (!query.exec()) {
610 qCritical() << "Failed to save attachment to database" << query.lastError() << attachment;944 qCritical() << "Failed to save attachment to database" << query.lastError() << attachment;
945 SQLiteDatabase::instance()->rollbackTransaction();
611 return History::EventWriteError;946 return History::EventWriteError;
612 }947 }
613 }948 }
614 }949 }
615950
951 if (!SQLiteDatabase::instance()->finishTransaction()) {
952 qCritical() << "Failed to commit transaction.";
953 return History::EventWriteError;
954 }
955
616 if (result == History::EventWriteModified || result == History::EventWriteCreated) {956 if (result == History::EventWriteModified || result == History::EventWriteCreated) {
617 QVariantMap existingThread = getSingleThread((History::EventType) event[History::FieldType].toInt(),957 QVariantMap existingThread = getSingleThread((History::EventType) event[History::FieldType].toInt(),
618 event[History::FieldAccountId].toString(),958 event[History::FieldAccountId].toString(),
@@ -750,7 +1090,8 @@
750 << "threads.threadId"1090 << "threads.threadId"
751 << "threads.lastEventId"1091 << "threads.lastEventId"
752 << "threads.count"1092 << "threads.count"
753 << "threads.unreadCount";1093 << "threads.unreadCount"
1094 << "threads.lastEventTimestamp";
7541095
755 // get the participants in the query already1096 // get the participants in the query already
756 fields << "(SELECT group_concat(thread_participants.participantId, \"|,|\") "1097 fields << "(SELECT group_concat(thread_participants.participantId, \"|,|\") "
@@ -758,13 +1099,23 @@
758 "AND thread_participants.threadId=threads.threadId "1099 "AND thread_participants.threadId=threads.threadId "
759 "AND thread_participants.type=threads.type GROUP BY accountId,threadId,type) as participants";1100 "AND thread_participants.type=threads.type GROUP BY accountId,threadId,type) as participants";
7601101
1102 fields << "(SELECT group_concat(thread_participants.state, \"|,|\") "
1103 "FROM thread_participants WHERE thread_participants.accountId=threads.accountId "
1104 "AND thread_participants.threadId=threads.threadId "
1105 "AND thread_participants.type=threads.type GROUP BY accountId,threadId,type) as state";
1106
1107 fields << "(SELECT group_concat(thread_participants.roles, \"|,|\") "
1108 "FROM thread_participants WHERE thread_participants.accountId=threads.accountId "
1109 "AND thread_participants.threadId=threads.threadId "
1110 "AND thread_participants.type=threads.type GROUP BY accountId,threadId,type) as roles";
1111
761 QStringList extraFields;1112 QStringList extraFields;
762 QString table;1113 QString table;
7631114
764 switch (type) {1115 switch (type) {
765 case History::EventTypeText:1116 case History::EventTypeText:
766 table = "text_events";1117 table = "text_events";
767 extraFields << "text_events.message" << "text_events.messageType" << "text_events.messageStatus" << "text_events.readTimestamp";1118 extraFields << "text_events.message" << "text_events.messageType" << "text_events.messageStatus" << "text_events.readTimestamp" << "chatType" << "text_events.subject" << "text_events.informationType";
768 break;1119 break;
769 case History::EventTypeVoice:1120 case History::EventTypeVoice:
770 table = "voice_events";1121 table = "voice_events";
@@ -773,7 +1124,6 @@
773 }1124 }
7741125
775 fields << QString("%1.senderId").arg(table)1126 fields << QString("%1.senderId").arg(table)
776 << QString("%1.timestamp").arg(table)
777 << QString("%1.newEvent").arg(table);1127 << QString("%1.newEvent").arg(table);
778 fields << extraFields;1128 fields << extraFields;
7791129
@@ -820,13 +1170,31 @@
820 thread[History::FieldEventId] = query.value(2);1170 thread[History::FieldEventId] = query.value(2);
821 thread[History::FieldCount] = query.value(3);1171 thread[History::FieldCount] = query.value(3);
822 thread[History::FieldUnreadCount] = query.value(4);1172 thread[History::FieldUnreadCount] = query.value(4);
823 QStringList participants = query.value(5).toString().split("|,|");1173 QStringList participants = query.value(6).toString().split("|,|", QString::SkipEmptyParts);
824 thread[History::FieldParticipants] = History::ContactMatcher::instance()->contactInfo(accountId, participants, true);1174 QList<int> participantStatus;
1175 QStringList participantStatusString = query.value(7).toString().split("|,|", QString::SkipEmptyParts);
1176 Q_FOREACH(const QString &statusString, participantStatusString) {
1177 participantStatus << statusString.toUInt();
1178 }
1179 QStringList participantRolesString = query.value(8).toString().split("|,|", QString::SkipEmptyParts);
1180 QList<int> participantRoles;
1181 Q_FOREACH(const QString &rolesString, participantRolesString) {
1182 participantRoles << rolesString.toUInt();
1183 }
1184 QVariantList contactList;
1185 QVariantList contactInfo = History::ContactMatcher::instance()->contactInfo(accountId, participants, true);
1186 for (int i = 0; i < contactInfo.count(); ++i) {
1187 QVariantMap map = contactInfo[i].toMap();
1188 map["state"] = participantStatus[i];
1189 map["roles"] = participantRoles[i];
1190 contactList << map;
1191 }
1192 thread[History::FieldParticipants] = contactList;
8251193
826 // the generic event fields1194 // the generic event fields
827 thread[History::FieldSenderId] = query.value(6);1195 thread[History::FieldSenderId] = query.value(9);
828 thread[History::FieldTimestamp] = toLocalTimeString(query.value(7).toDateTime());1196 thread[History::FieldTimestamp] = toLocalTimeString(query.value(5).toDateTime());
829 thread[History::FieldNewEvent] = query.value(8).toBool();1197 thread[History::FieldNewEvent] = query.value(10).toBool();
8301198
831 // the next step is to get the last event1199 // the next step is to get the last event
832 switch (type) {1200 switch (type) {
@@ -857,15 +1225,77 @@
857 thread[History::FieldAttachments] = QVariant::fromValue(attachments);1225 thread[History::FieldAttachments] = QVariant::fromValue(attachments);
858 attachments.clear();1226 attachments.clear();
859 }1227 }
860 thread[History::FieldMessage] = query.value(9);1228 thread[History::FieldMessage] = query.value(11);
861 thread[History::FieldMessageType] = query.value(10);1229 thread[History::FieldMessageType] = query.value(12);
862 thread[History::FieldMessageStatus] = query.value(11);1230 thread[History::FieldMessageStatus] = query.value(13);
863 thread[History::FieldReadTimestamp] = toLocalTimeString(query.value(12).toDateTime());1231 thread[History::FieldReadTimestamp] = toLocalTimeString(query.value(14).toDateTime());
1232 thread[History::FieldChatType] = query.value(15).toUInt();
1233
1234 if (thread[History::FieldChatType].toInt() == 2) {
1235 QVariantMap chatRoomInfo;
1236 QSqlQuery query1(SQLiteDatabase::instance()->database());
1237
1238 query1.prepare("SELECT roomName, server, creator, creationTimestamp, anonymous, inviteOnly, participantLimit, moderated, title, description, persistent, private, passwordProtected, password, passwordHint, canUpdateConfiguration, subject, actor, timestamp, joined, selfRoles FROM chat_room_info WHERE accountId=:accountId AND threadId=:threadId AND type=:type LIMIT 1");
1239 query1.bindValue(":accountId", thread[History::FieldAccountId]);
1240 query1.bindValue(":threadId", thread[History::FieldThreadId]);
1241 query1.bindValue(":type", thread[History::FieldType].toInt());
1242
1243 if (!query1.exec()) {
1244 qCritical() << "Failed to get chat room info for thread: Error:" << query1.lastError() << query1.lastQuery();
1245 break;
1246 }
1247 query1.next();
1248
1249 if (query1.value(0).isValid())
1250 chatRoomInfo["RoomName"] = query1.value(0);
1251 if (query1.value(1).isValid())
1252 chatRoomInfo["Server"] = query1.value(1);
1253 if (query1.value(2).isValid())
1254 chatRoomInfo["Creator"] = query1.value(2);
1255 if (query1.value(3).isValid())
1256 chatRoomInfo["CreationTimestamp"] = toLocalTimeString(query1.value(3).toDateTime());
1257 if (query1.value(4).isValid())
1258 chatRoomInfo["Anonymous"] = query1.value(4).toBool();
1259 if (query1.value(5).isValid())
1260 chatRoomInfo["InviteOnly"] = query1.value(5).toBool();
1261 if (query1.value(6).isValid())
1262 chatRoomInfo["Limit"] = query1.value(6).toInt();
1263 if (query1.value(7).isValid())
1264 chatRoomInfo["Moderated"] = query1.value(7).toBool();
1265 if (query1.value(8).isValid())
1266 chatRoomInfo["Title"] = query1.value(8);
1267 if (query1.value(9).isValid())
1268 chatRoomInfo["Description"] = query1.value(9);
1269 if (query1.value(10).isValid())
1270 chatRoomInfo["Persistent"] = query1.value(10).toBool();
1271 if (query1.value(11).isValid())
1272 chatRoomInfo["Private"] = query1.value(11).toBool();
1273 if (query1.value(12).isValid())
1274 chatRoomInfo["PasswordProtected"] = query1.value(12).toBool();
1275 if (query1.value(13).isValid())
1276 chatRoomInfo["Password"] = query1.value(13);
1277 if (query1.value(14).isValid())
1278 chatRoomInfo["PasswordHint"] = query1.value(14);
1279 if (query1.value(15).isValid())
1280 chatRoomInfo["CanUpdateConfiguration"] = query1.value(15).toBool();
1281 if (query1.value(16).isValid())
1282 chatRoomInfo["Subject"] = query1.value(16);
1283 if (query1.value(17).isValid())
1284 chatRoomInfo["Actor"] = query1.value(17);
1285 if (query1.value(18).isValid())
1286 chatRoomInfo["Timestamp"] = toLocalTimeString(query1.value(18).toDateTime());
1287 if (query1.value(19).isValid())
1288 chatRoomInfo["Joined"] = query1.value(19).toBool();
1289 if (query1.value(20).isValid())
1290 chatRoomInfo["SelfRoles"] = query1.value(20).toInt();
1291
1292 thread[History::FieldChatRoomInfo] = chatRoomInfo;
1293 }
864 break;1294 break;
865 case History::EventTypeVoice:1295 case History::EventTypeVoice:
866 thread[History::FieldMissed] = query.value(10);1296 thread[History::FieldMissed] = query.value(12);
867 thread[History::FieldDuration] = query.value(9);1297 thread[History::FieldDuration] = query.value(11);
868 thread[History::FieldRemoteParticipant] = History::ContactMatcher::instance()->contactInfo(accountId, query.value(11).toString(), true);1298 thread[History::FieldRemoteParticipant] = History::ContactMatcher::instance()->contactInfo(accountId, query.value(13).toString(), true);
869 break;1299 break;
870 }1300 }
871 threads << thread;1301 threads << thread;
@@ -889,7 +1319,7 @@
889 case History::EventTypeText:1319 case History::EventTypeText:
890 participantsField = participantsField.arg("text_events", QString::number(type));1320 participantsField = participantsField.arg("text_events", QString::number(type));
891 queryText = QString("SELECT accountId, threadId, eventId, senderId, timestamp, newEvent, %1, "1321 queryText = QString("SELECT accountId, threadId, eventId, senderId, timestamp, newEvent, %1, "
892 "message, messageType, messageStatus, readTimestamp, subject FROM text_events %2 %3").arg(participantsField, modifiedCondition, order);1322 "message, messageType, messageStatus, readTimestamp, subject, informationType FROM text_events %2 %3").arg(participantsField, modifiedCondition, order);
893 break;1323 break;
894 case History::EventTypeVoice:1324 case History::EventTypeVoice:
895 participantsField = participantsField.arg("voice_events", QString::number(type));1325 participantsField = participantsField.arg("voice_events", QString::number(type));
@@ -960,6 +1390,10 @@
960 event[History::FieldMessageType] = query.value(8);1390 event[History::FieldMessageType] = query.value(8);
961 event[History::FieldMessageStatus] = query.value(9);1391 event[History::FieldMessageStatus] = query.value(9);
962 event[History::FieldReadTimestamp] = toLocalTimeString(query.value(10).toDateTime());1392 event[History::FieldReadTimestamp] = toLocalTimeString(query.value(10).toDateTime());
1393 if (!query.value(11).toString().isEmpty()) {
1394 event[History::FieldSubject] = query.value(11).toString();
1395 }
1396 event[History::FieldInformationType] = query.value(12).toInt();
963 break;1397 break;
964 case History::EventTypeVoice:1398 case History::EventTypeVoice:
965 event[History::FieldDuration] = query.value(7).toInt();1399 event[History::FieldDuration] = query.value(7).toInt();
@@ -975,7 +1409,7 @@
9751409
976QString SQLiteHistoryPlugin::toLocalTimeString(const QDateTime &timestamp)1410QString SQLiteHistoryPlugin::toLocalTimeString(const QDateTime &timestamp)
977{1411{
978 return QDateTime(timestamp.date(), timestamp.time(), Qt::UTC).toLocalTime().toString("yyyy-MM-ddTHH:mm:ss.zzz");1412 return QDateTime(timestamp.date(), timestamp.time(), Qt::UTC).toLocalTime().toString(timestampFormat);
979}1413}
9801414
981QString SQLiteHistoryPlugin::filterToString(const History::Filter &filter, QVariantMap &bindValues, const QString &propertyPrefix) const1415QString SQLiteHistoryPlugin::filterToString(const History::Filter &filter, QVariantMap &bindValues, const QString &propertyPrefix) const
9821416
=== modified file 'plugins/sqlite/sqlitehistoryplugin.h'
--- plugins/sqlite/sqlitehistoryplugin.h 2015-10-20 19:20:00 +0000
+++ plugins/sqlite/sqlitehistoryplugin.h 2016-11-24 12:52:24 +0000
@@ -1,5 +1,5 @@
1/*1/*
2 * Copyright (C) 2013-2015 Canonical, Ltd.2 * Copyright (C) 2013-2016 Canonical, Ltd.
3 *3 *
4 * Authors:4 * Authors:
5 * Gustavo Pichorim Boiko <gustavo.boiko@canonical.com>5 * Gustavo Pichorim Boiko <gustavo.boiko@canonical.com>
@@ -55,6 +55,10 @@
55 History::EventType type,55 History::EventType type,
56 const QStringList &participants,56 const QStringList &participants,
57 History::MatchFlags matchFlags = History::MatchCaseSensitive);57 History::MatchFlags matchFlags = History::MatchCaseSensitive);
58 QVariantMap threadForProperties(const QString &accountId,
59 History::EventType type,
60 const QVariantMap &properties,
61 History::MatchFlags matchFlags = History::MatchCaseSensitive);
5862
59 QList<QVariantMap> eventsForThread(const QVariantMap &thread);63 QList<QVariantMap> eventsForThread(const QVariantMap &thread);
6064
@@ -62,7 +66,12 @@
62 QVariantMap getSingleEvent(History::EventType type, const QString &accountId, const QString &threadId, const QString &eventId);66 QVariantMap getSingleEvent(History::EventType type, const QString &accountId, const QString &threadId, const QString &eventId);
6367
64 // Writer part of the plugin68 // Writer part of the plugin
69 QVariantMap createThreadForProperties(const QString &accountId, History::EventType type, const QVariantMap &properties);
65 QVariantMap createThreadForParticipants(const QString &accountId, History::EventType type, const QStringList &participants);70 QVariantMap createThreadForParticipants(const QString &accountId, History::EventType type, const QStringList &participants);
71
72 bool updateRoomParticipants(const QString &accountId, const QString &threadId, History::EventType type, const QVariantList &participants);
73 bool updateRoomParticipantsRoles(const QString &accountId, const QString &threadId, History::EventType type, const QVariantMap &participantsRoles);
74 bool updateRoomInfo(const QString &accountId, const QString &threadId, History::EventType type, const QVariantMap &properties, const QStringList &invalidated = QStringList());
66 bool removeThread(const QVariantMap &thread);75 bool removeThread(const QVariantMap &thread);
6776
68 History::EventWriteResult writeTextEvent(const QVariantMap &event);77 History::EventWriteResult writeTextEvent(const QVariantMap &event);
6978
=== modified file 'plugins/sqlite/sqlitehistorythreadview.cpp'
--- plugins/sqlite/sqlitehistorythreadview.cpp 2015-09-21 20:05:06 +0000
+++ plugins/sqlite/sqlitehistorythreadview.cpp 2016-11-24 12:52:24 +0000
@@ -35,7 +35,6 @@
35 : History::PluginThreadView(), mPlugin(plugin), mType(type), mSort(sort),35 : History::PluginThreadView(), mPlugin(plugin), mType(type), mSort(sort),
36 mFilter(filter), mPageSize(15), mQuery(SQLiteDatabase::instance()->database()), mOffset(0), mValid(true), mQueryProperties(properties)36 mFilter(filter), mPageSize(15), mQuery(SQLiteDatabase::instance()->database()), mOffset(0), mValid(true), mQueryProperties(properties)
37{37{
38 qDebug() << __PRETTY_FUNCTION__;
39 mTemporaryTable = QString("threadview%1%2").arg(QString::number((qulonglong)this), QDateTime::currentDateTimeUtc().toString("yyyyMMddhhmmsszzz"));38 mTemporaryTable = QString("threadview%1%2").arg(QString::number((qulonglong)this), QDateTime::currentDateTimeUtc().toString("yyyyMMddhhmmsszzz"));
40 mQuery.setForwardOnly(true);39 mQuery.setForwardOnly(true);
4140
@@ -69,9 +68,6 @@
69 Q_EMIT Invalidated();68 Q_EMIT Invalidated();
70 return;69 return;
71 }70 }
72
73 mQuery.exec(QString("SELECT count(*) FROM %1").arg(mTemporaryTable));
74 mQuery.next();
75}71}
7672
77SQLiteHistoryThreadView::~SQLiteHistoryThreadView()73SQLiteHistoryThreadView::~SQLiteHistoryThreadView()
@@ -84,7 +80,6 @@
8480
85QList<QVariantMap> SQLiteHistoryThreadView::NextPage()81QList<QVariantMap> SQLiteHistoryThreadView::NextPage()
86{82{
87 qDebug() << __PRETTY_FUNCTION__;
88 QList<QVariantMap> threads;83 QList<QVariantMap> threads;
8984
90 // now prepare for selecting from it85 // now prepare for selecting from it
9186
=== modified file 'src/channelobserver.cpp'
--- src/channelobserver.cpp 2015-10-06 17:03:38 +0000
+++ src/channelobserver.cpp 2016-11-24 12:52:24 +0000
@@ -40,6 +40,8 @@
40 Tp::ChannelClassSpecList specList;40 Tp::ChannelClassSpecList specList;
41 specList << Tp::ChannelClassSpec::audioCall();41 specList << Tp::ChannelClassSpec::audioCall();
42 specList << Tp::ChannelClassSpec::textChat();42 specList << Tp::ChannelClassSpec::textChat();
43 specList << Tp::ChannelClassSpec::textChatroom();
44 specList << Tp::ChannelClassSpec::unnamedTextChat();
4345
44 return specList;46 return specList;
45}47}
@@ -58,7 +60,6 @@
58 Q_UNUSED(requestsSatisfied)60 Q_UNUSED(requestsSatisfied)
59 Q_UNUSED(observerInfo)61 Q_UNUSED(observerInfo)
6062
61 qDebug() << __PRETTY_FUNCTION__;
62 Q_FOREACH (Tp::ChannelPtr channel, channels) {63 Q_FOREACH (Tp::ChannelPtr channel, channels) {
63 // tp-qt has not support for the SMS interface64 // tp-qt has not support for the SMS interface
64 if (channel->immutableProperties().contains(TP_QT_IFACE_CHANNEL_INTERFACE_SMS + QLatin1String(".Flash"))) {65 if (channel->immutableProperties().contains(TP_QT_IFACE_CHANNEL_INTERFACE_SMS + QLatin1String(".Flash"))) {
6566
=== modified file 'src/contactmatcher.cpp'
--- src/contactmatcher.cpp 2015-10-07 20:21:54 +0000
+++ src/contactmatcher.cpp 2016-11-24 12:52:24 +0000
@@ -1,5 +1,5 @@
1/*1/*
2 * Copyright (C) 2014-2015 Canonical, Ltd.2 * Copyright (C) 2014-2016 Canonical, Ltd.
3 *3 *
4 * Authors:4 * Authors:
5 * Gustavo Pichorim Boiko <gustavo.boiko@canonical.com>5 * Gustavo Pichorim Boiko <gustavo.boiko@canonical.com>
@@ -95,7 +95,7 @@
95 *95 *
96 * Note that synchronous requests should only be placed after \ref TelepathyHelper is ready.96 * Note that synchronous requests should only be placed after \ref TelepathyHelper is ready.
97 */97 */
98QVariantMap ContactMatcher::contactInfo(const QString &accountId, const QString &identifier, bool synchronous)98QVariantMap ContactMatcher::contactInfo(const QString &accountId, const QString &identifier, bool synchronous, const QVariantMap &properties)
99{99{
100 InternalContactMap &internalMap = mContactMap[accountId];100 InternalContactMap &internalMap = mContactMap[accountId];
101101
@@ -103,8 +103,9 @@
103 if (internalMap.contains(identifier)) {103 if (internalMap.contains(identifier)) {
104 return internalMap[identifier];104 return internalMap[identifier];
105 }105 }
106106
107 QVariantMap map;107 QVariantMap map;
108
108 // and if there was no match, asynchronously request the info, and return an empty map for now109 // and if there was no match, asynchronously request the info, and return an empty map for now
109 if (History::TelepathyHelper::instance()->ready()) {110 if (History::TelepathyHelper::instance()->ready()) {
110 map = requestContactInfo(accountId, identifier, synchronous);111 map = requestContactInfo(accountId, identifier, synchronous);
@@ -114,6 +115,15 @@
114 }115 }
115 map[History::FieldIdentifier] = identifier;116 map[History::FieldIdentifier] = identifier;
116 map[History::FieldAccountId] = accountId;117 map[History::FieldAccountId] = accountId;
118
119 QMapIterator<QString, QVariant> i(properties);
120 while (i.hasNext()) {
121 i.next();
122 if (!map.contains(i.key())) {
123 map[i.key()] = i.value();
124 }
125 }
126
117 mContactMap[accountId][identifier] = map;127 mContactMap[accountId][identifier] = map;
118 return map;128 return map;
119}129}
120130
=== modified file 'src/contactmatcher_p.h'
--- src/contactmatcher_p.h 2015-10-06 12:59:03 +0000
+++ src/contactmatcher_p.h 2016-11-24 12:52:24 +0000
@@ -1,5 +1,5 @@
1/*1/*
2 * Copyright (C) 2014-2015 Canonical, Ltd.2 * Copyright (C) 2014-2016 Canonical, Ltd.
3 *3 *
4 * Authors:4 * Authors:
5 * Gustavo Pichorim Boiko <gustavo.boiko@canonical.com>5 * Gustavo Pichorim Boiko <gustavo.boiko@canonical.com>
@@ -45,7 +45,7 @@
45 Q_OBJECT45 Q_OBJECT
46public:46public:
47 static ContactMatcher *instance(QContactManager *manager = 0);47 static ContactMatcher *instance(QContactManager *manager = 0);
48 QVariantMap contactInfo(const QString &accountId, const QString &identifier, bool synchronous = false);48 QVariantMap contactInfo(const QString &accountId, const QString &identifier, bool synchronous = false, const QVariantMap &properties = QVariantMap());
49 QVariantList contactInfo(const QString &accountId, const QStringList &identifiers, bool synchronous = false);49 QVariantList contactInfo(const QString &accountId, const QStringList &identifiers, bool synchronous = false);
5050
51 // this will only watch for contact changes affecting the identifier, but won't fetch contact info51 // this will only watch for contact changes affecting the identifier, but won't fetch contact info
5252
=== modified file 'src/manager.cpp'
--- src/manager.cpp 2015-10-01 19:44:45 +0000
+++ src/manager.cpp 2016-11-24 12:52:24 +0000
@@ -1,5 +1,5 @@
1/*1/*
2 * Copyright (C) 2013 Canonical, Ltd.2 * Copyright (C) 2013-2016 Canonical, Ltd.
3 *3 *
4 * Authors:4 * Authors:
5 * Gustavo Pichorim Boiko <gustavo.boiko@canonical.com>5 * Gustavo Pichorim Boiko <gustavo.boiko@canonical.com>
@@ -135,7 +135,23 @@
135{135{
136 Q_D(Manager);136 Q_D(Manager);
137137
138 return d->dbus->threadForParticipants(accountId, type, participants, matchFlags, create);138 QVariantMap properties;
139 properties[History::FieldParticipantIds] = participants;
140 if (participants.size() == 1) {
141 properties[History::FieldChatType] = History::ChatTypeContact;
142 }
143 return d->dbus->threadForProperties(accountId, type, properties, matchFlags, create);
144}
145
146Thread Manager::threadForProperties(const QString &accountId,
147 EventType type,
148 const QVariantMap &properties,
149 MatchFlags matchFlags,
150 bool create)
151{
152 Q_D(Manager);
153
154 return d->dbus->threadForProperties(accountId, type, properties, matchFlags, create);
139}155}
140156
141Thread Manager::getSingleThread(EventType type, const QString &accountId, const QString &threadId, const QVariantMap &properties)157Thread Manager::getSingleThread(EventType type, const QString &accountId, const QString &threadId, const QVariantMap &properties)
142158
=== modified file 'src/manager.h'
--- src/manager.h 2015-10-01 19:44:45 +0000
+++ src/manager.h 2016-11-24 12:52:24 +0000
@@ -1,5 +1,5 @@
1/*1/*
2 * Copyright (C) 2013 Canonical, Ltd.2 * Copyright (C) 2013-2016 Canonical, Ltd.
3 *3 *
4 * Authors:4 * Authors:
5 * Gustavo Pichorim Boiko <gustavo.boiko@canonical.com>5 * Gustavo Pichorim Boiko <gustavo.boiko@canonical.com>
@@ -61,6 +61,12 @@
61 const QStringList &participants,61 const QStringList &participants,
62 History::MatchFlags matchFlags = History::MatchCaseSensitive,62 History::MatchFlags matchFlags = History::MatchCaseSensitive,
63 bool create = false);63 bool create = false);
64 Thread threadForProperties(const QString &accountId,
65 EventType type,
66 const QVariantMap &properties,
67 History::MatchFlags matchFlags = History::MatchCaseSensitive,
68 bool create = false);
69
64 Thread getSingleThread(EventType type, const QString &accountId, const QString &threadId, const QVariantMap &properties = QVariantMap());70 Thread getSingleThread(EventType type, const QString &accountId, const QString &threadId, const QVariantMap &properties = QVariantMap());
6571
66 bool writeEvents(const History::Events &events);72 bool writeEvents(const History::Events &events);
6773
=== modified file 'src/managerdbus.cpp'
--- src/managerdbus.cpp 2015-09-23 15:08:07 +0000
+++ src/managerdbus.cpp 2016-11-24 12:52:24 +0000
@@ -1,5 +1,5 @@
1/*1/*
2 * Copyright (C) 2013 Canonical, Ltd.2 * Copyright (C) 2013-2016 Canonical, Ltd.
3 *3 *
4 * Authors:4 * Authors:
5 * Gustavo Pichorim Boiko <gustavo.boiko@canonical.com>5 * Gustavo Pichorim Boiko <gustavo.boiko@canonical.com>
@@ -64,9 +64,21 @@
64 MatchFlags matchFlags,64 MatchFlags matchFlags,
65 bool create)65 bool create)
66{66{
67 QVariantMap properties;
68 properties[History::FieldParticipantIds] = participants;
69
70 return threadForProperties(accountId, type, properties, matchFlags, create);
71}
72
73Thread ManagerDBus::threadForProperties(const QString &accountId,
74 EventType type,
75 const QVariantMap &properties,
76 MatchFlags matchFlags,
77 bool create)
78{
67 Thread thread;79 Thread thread;
68 // FIXME: move to async call if possible80 // FIXME: move to async call if possible
69 QDBusReply<QVariantMap> reply = mInterface.call("ThreadForParticipants", accountId, (int) type, participants, (int)matchFlags, create);81 QDBusReply<QVariantMap> reply = mInterface.call("ThreadForProperties", accountId, (int) type, properties, (int)matchFlags, create);
70 if (reply.isValid()) {82 if (reply.isValid()) {
71 QVariantMap properties = reply.value();83 QVariantMap properties = reply.value();
72 thread = Thread::fromProperties(properties);84 thread = Thread::fromProperties(properties);
7385
=== modified file 'src/managerdbus_p.h'
--- src/managerdbus_p.h 2015-09-23 15:08:07 +0000
+++ src/managerdbus_p.h 2016-11-24 12:52:24 +0000
@@ -1,5 +1,5 @@
1/*1/*
2 * Copyright (C) 2013 Canonical, Ltd.2 * Copyright (C) 2013-2016 Canonical, Ltd.
3 *3 *
4 * Authors:4 * Authors:
5 * Gustavo Pichorim Boiko <gustavo.boiko@canonical.com>5 * Gustavo Pichorim Boiko <gustavo.boiko@canonical.com>
@@ -45,6 +45,12 @@
45 History::MatchFlags matchFlags,45 History::MatchFlags matchFlags,
46 bool create);46 bool create);
4747
48 Thread threadForProperties(const QString &accountId,
49 EventType type,
50 const QVariantMap &properties,
51 History::MatchFlags matchFlags,
52 bool create);
53
48 bool writeEvents(const History::Events &events);54 bool writeEvents(const History::Events &events);
49 bool removeThreads(const Threads &threads);55 bool removeThreads(const Threads &threads);
50 bool removeEvents(const Events &events);56 bool removeEvents(const Events &events);
5157
=== modified file 'src/participant.cpp'
--- src/participant.cpp 2016-01-04 18:28:34 +0000
+++ src/participant.cpp 2016-11-24 12:52:24 +0000
@@ -1,5 +1,5 @@
1/*1/*
2 * Copyright (C) 2015 Canonical, Ltd.2 * Copyright (C) 2015-2016 Canonical, Ltd.
3 *3 *
4 * Authors:4 * Authors:
5 * Gustavo Pichorim Boiko <gustavo.boiko@canonical.com>5 * Gustavo Pichorim Boiko <gustavo.boiko@canonical.com>
@@ -36,9 +36,11 @@
36 const QString &theContactId,36 const QString &theContactId,
37 const QString &theAlias,37 const QString &theAlias,
38 const QString &theAvatar,38 const QString &theAvatar,
39 const uint theState,
40 const uint theRoles,
39 const QVariantMap &theDetailProperties) :41 const QVariantMap &theDetailProperties) :
40 accountId(theAccountId), identifier(theIdentifier), contactId(theContactId),42 accountId(theAccountId), identifier(theIdentifier), contactId(theContactId),
41 alias(theAlias), avatar(theAvatar), detailProperties(theDetailProperties)43 alias(theAlias), avatar(theAvatar), state(theState), roles(theRoles), detailProperties(theDetailProperties)
42{44{
43}45}
4446
@@ -51,8 +53,8 @@
51{53{
52}54}
5355
54Participant::Participant(const QString &accountId, const QString &identifier, const QString &contactId, const QString &alias, const QString &avatar, const QVariantMap &detailProperties)56Participant::Participant(const QString &accountId, const QString &identifier, const QString &contactId, const QString &alias, const QString &avatar, uint state, uint roles, const QVariantMap &detailProperties)
55 : d_ptr(new ParticipantPrivate(accountId, identifier, contactId, alias, avatar, detailProperties))57 : d_ptr(new ParticipantPrivate(accountId, identifier, contactId, alias, avatar, state, roles, detailProperties))
56{58{
57}59}
5860
@@ -104,6 +106,18 @@
104 return d->avatar;106 return d->avatar;
105}107}
106108
109uint Participant::state() const
110{
111 Q_D(const Participant);
112 return d->state;
113}
114
115uint Participant::roles() const
116{
117 Q_D(const Participant);
118 return d->roles;
119}
120
107QVariantMap Participant::detailProperties() const121QVariantMap Participant::detailProperties() const
108{122{
109 Q_D(const Participant);123 Q_D(const Participant);
@@ -140,6 +154,8 @@
140 map[FieldContactId] = d->contactId;154 map[FieldContactId] = d->contactId;
141 map[FieldAlias] = d->alias;155 map[FieldAlias] = d->alias;
142 map[FieldAvatar] = d->avatar;156 map[FieldAvatar] = d->avatar;
157 map[FieldParticipantState] = d->state;
158 map[FieldParticipantRoles] = d->roles;
143 map[FieldDetailProperties] = d->detailProperties;159 map[FieldDetailProperties] = d->detailProperties;
144160
145 return map;161 return map;
@@ -157,6 +173,8 @@
157 QString contactId = properties[FieldContactId].toString();173 QString contactId = properties[FieldContactId].toString();
158 QString alias = properties[FieldAlias].toString();174 QString alias = properties[FieldAlias].toString();
159 QString avatar = properties[FieldAvatar].toString();175 QString avatar = properties[FieldAvatar].toString();
176 uint state = properties[FieldParticipantState].toUInt();
177 uint roles = properties[FieldParticipantRoles].toUInt();
160 QVariantMap detailProperties;178 QVariantMap detailProperties;
161 QVariant detailPropertiesVariant = properties[FieldDetailProperties];179 QVariant detailPropertiesVariant = properties[FieldDetailProperties];
162 if (detailPropertiesVariant.canConvert<QVariantMap>()) {180 if (detailPropertiesVariant.canConvert<QVariantMap>()) {
@@ -169,7 +187,7 @@
169 }187 }
170 }188 }
171189
172 return Participant(accountId, identifier, contactId, alias, avatar, detailProperties);190 return Participant(accountId, identifier, contactId, alias, avatar, state, roles, detailProperties);
173}191}
174192
175QStringList Participants::identifiers() const193QStringList Participants::identifiers() const
@@ -184,7 +202,9 @@
184Participants Participants::fromVariant(const QVariant &variant)202Participants Participants::fromVariant(const QVariant &variant)
185{203{
186 Participants participants;204 Participants participants;
187 if (variant.canConvert<QVariantList>()) {205 if (variant.type() == QVariant::StringList) {
206 participants = Participants::fromStringList(variant.toStringList());
207 } else if (variant.canConvert<QVariantList>()) {
188 participants = Participants::fromVariantList(variant.toList());208 participants = Participants::fromVariantList(variant.toList());
189 } else if (variant.canConvert<QDBusArgument>()) {209 } else if (variant.canConvert<QDBusArgument>()) {
190 QDBusArgument argument = variant.value<QDBusArgument>();210 QDBusArgument argument = variant.value<QDBusArgument>();
@@ -193,6 +213,17 @@
193 return participants;213 return participants;
194}214}
195215
216Participants Participants::fromStringList(const QStringList &list)
217{
218 Participants participants;
219 Q_FOREACH(const QString& participantId, list) {
220 QVariantMap properties;
221 properties[FieldIdentifier] = participantId;
222 participants << Participant::fromProperties(properties);
223 }
224 return participants;
225}
226
196Participants Participants::fromVariantList(const QVariantList &list)227Participants Participants::fromVariantList(const QVariantList &list)
197{228{
198 Participants participants;229 Participants participants;
@@ -211,6 +242,17 @@
211 return list;242 return list;
212}243}
213244
245Participants Participants::filterByState(uint state) const
246{
247 Participants filtered;
248 Q_FOREACH(const Participant &participant, *this) {
249 if (participant.state() == state) {
250 filtered << participant;
251 }
252 }
253 return filtered;
254}
255
214const QDBusArgument &operator>>(const QDBusArgument &argument, Participants &participants)256const QDBusArgument &operator>>(const QDBusArgument &argument, Participants &participants)
215{257{
216 argument.beginArray();258 argument.beginArray();
217259
=== modified file 'src/participant.h'
--- src/participant.h 2015-09-28 23:13:21 +0000
+++ src/participant.h 2016-11-24 12:52:24 +0000
@@ -1,5 +1,5 @@
1/*1/*
2 * Copyright (C) 2015 Canonical, Ltd.2 * Copyright (C) 2015-2016 Canonical, Ltd.
3 *3 *
4 * Authors:4 * Authors:
5 * Gustavo Pichorim Boiko <gustavo.boiko@canonical.com>5 * Gustavo Pichorim Boiko <gustavo.boiko@canonical.com>
@@ -44,6 +44,8 @@
44 const QString &contactId = QString::null,44 const QString &contactId = QString::null,
45 const QString &alias = QString::null,45 const QString &alias = QString::null,
46 const QString &avatar = QString::null,46 const QString &avatar = QString::null,
47 uint state = 0,
48 uint roles = 0,
47 const QVariantMap &detailProperties = QVariantMap());49 const QVariantMap &detailProperties = QVariantMap());
48 Participant(const Participant &other);50 Participant(const Participant &other);
49 Participant& operator=(const Participant &other);51 Participant& operator=(const Participant &other);
@@ -54,6 +56,8 @@
54 QString contactId() const;56 QString contactId() const;
55 QString alias() const;57 QString alias() const;
56 QString avatar() const;58 QString avatar() const;
59 uint state() const;
60 uint roles() const;
57 QVariantMap detailProperties() const;61 QVariantMap detailProperties() const;
5862
59 bool isNull() const;63 bool isNull() const;
@@ -75,8 +79,9 @@
75 QStringList identifiers() const;79 QStringList identifiers() const;
76 static Participants fromVariant(const QVariant &variant);80 static Participants fromVariant(const QVariant &variant);
77 static Participants fromVariantList(const QVariantList &list);81 static Participants fromVariantList(const QVariantList &list);
82 static Participants fromStringList(const QStringList &list);
78 QVariantList toVariantList() const;83 QVariantList toVariantList() const;
7984 History::Participants filterByState(uint state) const;
80};85};
8186
82const QDBusArgument &operator>>(const QDBusArgument &argument, Participants &participants);87const QDBusArgument &operator>>(const QDBusArgument &argument, Participants &participants);
8388
=== modified file 'src/participant_p.h'
--- src/participant_p.h 2015-09-28 18:53:49 +0000
+++ src/participant_p.h 2016-11-24 12:52:24 +0000
@@ -39,6 +39,8 @@
39 const QString &theContactId = QString::null,39 const QString &theContactId = QString::null,
40 const QString &theAlias = QString::null,40 const QString &theAlias = QString::null,
41 const QString &theAvatar = QString::null,41 const QString &theAvatar = QString::null,
42 uint theState = 0,
43 uint theRoles = 0,
42 const QVariantMap &theDetailProperties = QVariantMap());44 const QVariantMap &theDetailProperties = QVariantMap());
43 virtual ~ParticipantPrivate();45 virtual ~ParticipantPrivate();
4446
@@ -47,6 +49,8 @@
47 QString contactId;49 QString contactId;
48 QString alias;50 QString alias;
49 QString avatar;51 QString avatar;
52 uint state;
53 uint roles;
50 QVariantMap detailProperties;54 QVariantMap detailProperties;
51};55};
5256
5357
=== modified file 'src/plugin.h'
--- src/plugin.h 2015-09-28 22:23:09 +0000
+++ src/plugin.h 2016-11-24 12:52:24 +0000
@@ -1,5 +1,5 @@
1/*1/*
2 * Copyright (C) 2013 Canonical, Ltd.2 * Copyright (C) 2013-2016 Canonical, Ltd.
3 *3 *
4 * Authors:4 * Authors:
5 * Gustavo Pichorim Boiko <gustavo.boiko@canonical.com>5 * Gustavo Pichorim Boiko <gustavo.boiko@canonical.com>
@@ -61,11 +61,19 @@
61 EventType type,61 EventType type,
62 const QStringList &participants,62 const QStringList &participants,
63 History::MatchFlags matchFlags = History::MatchCaseSensitive) = 0;63 History::MatchFlags matchFlags = History::MatchCaseSensitive) = 0;
64 virtual QVariantMap threadForProperties(const QString &accountId,
65 EventType type,
66 const QVariantMap &properties,
67 History::MatchFlags matchFlags = History::MatchCaseSensitive) = 0;
6468
65 virtual QList<QVariantMap> eventsForThread(const QVariantMap &thread) = 0;69 virtual QList<QVariantMap> eventsForThread(const QVariantMap &thread) = 0;
6670
67 // Writer part of the plugin71 // Writer part of the plugin
68 virtual QVariantMap createThreadForParticipants(const QString &accountId, EventType type, const QStringList &participants) { return QVariantMap(); }72 virtual QVariantMap createThreadForParticipants(const QString &accountId, EventType type, const QStringList &participants) { return QVariantMap(); }
73 virtual QVariantMap createThreadForProperties(const QString &accountId, EventType type, const QVariantMap &properties) { return QVariantMap(); }
74 virtual bool updateRoomParticipants(const QString &accountId, const QString &threadId, History::EventType type, const QVariantList &participants) { return false; };
75 virtual bool updateRoomParticipantsRoles(const QString &accountId, const QString &threadId, History::EventType type, const QVariantMap &participantsRoles) { return false; };
76 virtual bool updateRoomInfo(const QString &accountId, const QString &threadId, EventType type, const QVariantMap &properties, const QStringList &invalidated = QStringList()) { return false; };
69 virtual bool removeThread(const QVariantMap &thread) { return false; }77 virtual bool removeThread(const QVariantMap &thread) { return false; }
7078
71 virtual EventWriteResult writeTextEvent(const QVariantMap &event) { return EventWriteError; }79 virtual EventWriteResult writeTextEvent(const QVariantMap &event) { return EventWriteError; }
7280
=== modified file 'src/plugineventview.cpp'
--- src/plugineventview.cpp 2013-11-19 17:52:53 +0000
+++ src/plugineventview.cpp 2016-11-24 12:52:24 +0000
@@ -56,7 +56,6 @@
5656
57void PluginEventView::Destroy()57void PluginEventView::Destroy()
58{58{
59 qDebug() << __PRETTY_FUNCTION__;
60 Q_D(PluginEventView);59 Q_D(PluginEventView);
61 deleteLater();60 deleteLater();
62}61}
6362
=== modified file 'src/pluginthreadview.cpp'
--- src/pluginthreadview.cpp 2013-11-19 17:52:53 +0000
+++ src/pluginthreadview.cpp 2016-11-24 12:52:24 +0000
@@ -56,7 +56,6 @@
5656
57void PluginThreadView::Destroy()57void PluginThreadView::Destroy()
58{58{
59 qDebug() << __PRETTY_FUNCTION__;
60 Q_D(PluginThreadView);59 Q_D(PluginThreadView);
61 deleteLater();60 deleteLater();
62}61}
6362
=== modified file 'src/textevent.cpp'
--- src/textevent.cpp 2015-09-28 23:13:21 +0000
+++ src/textevent.cpp 2016-11-24 12:52:24 +0000
@@ -47,10 +47,11 @@
47 MessageStatus theMessageStatus,47 MessageStatus theMessageStatus,
48 const QDateTime &theReadTimestamp,48 const QDateTime &theReadTimestamp,
49 const QString &theSubject,49 const QString &theSubject,
50 InformationType theInformationType,
50 const TextEventAttachments &theAttachments, const Participants &theParticipants) :51 const TextEventAttachments &theAttachments, const Participants &theParticipants) :
51 EventPrivate(theAccountId, theThreadId, theEventId, theSender, theTimestamp, theNewEvent, theParticipants),52 EventPrivate(theAccountId, theThreadId, theEventId, theSender, theTimestamp, theNewEvent, theParticipants),
52 message(theMessage), messageType(theMessageType), messageStatus(theMessageStatus),53 message(theMessage), messageType(theMessageType), messageStatus(theMessageStatus),
53 readTimestamp(theReadTimestamp), subject(theSubject), attachments(theAttachments)54 readTimestamp(theReadTimestamp), subject(theSubject), informationType(theInformationType), attachments(theAttachments)
54{55{
55}56}
5657
@@ -72,6 +73,7 @@
72 map[FieldMessageStatus] = (int)messageStatus;73 map[FieldMessageStatus] = (int)messageStatus;
73 map[FieldReadTimestamp] = readTimestamp.toString("yyyy-MM-ddTHH:mm:ss.zzz");74 map[FieldReadTimestamp] = readTimestamp.toString("yyyy-MM-ddTHH:mm:ss.zzz");
74 map[FieldSubject] = subject;75 map[FieldSubject] = subject;
76 map[FieldInformationType] = informationType;
7577
76 QList<QVariantMap> attachmentsMap;78 QList<QVariantMap> attachmentsMap;
77 Q_FOREACH(const TextEventAttachment &attachment, attachments) {79 Q_FOREACH(const TextEventAttachment &attachment, attachments) {
@@ -102,10 +104,11 @@
102 MessageStatus messageStatus,104 MessageStatus messageStatus,
103 const QDateTime &readTimestamp,105 const QDateTime &readTimestamp,
104 const QString &subject,106 const QString &subject,
107 InformationType informationType,
105 const TextEventAttachments &attachments,108 const TextEventAttachments &attachments,
106 const Participants &participants)109 const Participants &participants)
107 : Event(*new TextEventPrivate(accountId, threadId, eventId, sender, timestamp, newEvent,110 : Event(*new TextEventPrivate(accountId, threadId, eventId, sender, timestamp, newEvent,
108 message, messageType, messageStatus, readTimestamp, subject,111 message, messageType, messageStatus, readTimestamp, subject, informationType,
109 attachments, participants))112 attachments, participants))
110{113{
111 qDBusRegisterMetaType<QList<QVariantMap> >();114 qDBusRegisterMetaType<QList<QVariantMap> >();
@@ -158,6 +161,12 @@
158 return d->subject;161 return d->subject;
159}162}
160163
164InformationType TextEvent::informationType() const
165{
166 Q_D(const TextEvent);
167 return d->informationType;
168}
169
161TextEventAttachments TextEvent::attachments() const170TextEventAttachments TextEvent::attachments() const
162{171{
163 Q_D(const TextEvent);172 Q_D(const TextEvent);
@@ -180,6 +189,7 @@
180 Participants participants = Participants::fromVariant(properties[FieldParticipants]);189 Participants participants = Participants::fromVariant(properties[FieldParticipants]);
181 QString message = properties[FieldMessage].toString();190 QString message = properties[FieldMessage].toString();
182 QString subject = properties[FieldSubject].toString();191 QString subject = properties[FieldSubject].toString();
192 InformationType informationType = (InformationType) properties[FieldInformationType].toInt();
183 MessageType messageType = (MessageType) properties[FieldMessageType].toInt();193 MessageType messageType = (MessageType) properties[FieldMessageType].toInt();
184 MessageStatus messageStatus = (MessageStatus) properties[FieldMessageStatus].toInt();194 MessageStatus messageStatus = (MessageStatus) properties[FieldMessageStatus].toInt();
185 QDateTime readTimestamp = QDateTime::fromString(properties[FieldReadTimestamp].toString(), Qt::ISODate);195 QDateTime readTimestamp = QDateTime::fromString(properties[FieldReadTimestamp].toString(), Qt::ISODate);
@@ -203,7 +213,7 @@
203213
204 // and finally create the event214 // and finally create the event
205 event = TextEvent(accountId, threadId, eventId, senderId, timestamp, newEvent,215 event = TextEvent(accountId, threadId, eventId, senderId, timestamp, newEvent,
206 message, messageType, messageStatus, readTimestamp, subject, attachments, participants);216 message, messageType, messageStatus, readTimestamp, subject, informationType, attachments, participants);
207 return event;217 return event;
208}218}
209219
210220
=== modified file 'src/textevent.h'
--- src/textevent.h 2015-09-28 23:13:21 +0000
+++ src/textevent.h 2016-11-24 12:52:24 +0000
@@ -49,6 +49,7 @@
49 MessageStatus messageStatus = MessageStatusUnknown,49 MessageStatus messageStatus = MessageStatusUnknown,
50 const QDateTime &readTimestamp = QDateTime(),50 const QDateTime &readTimestamp = QDateTime(),
51 const QString &subject = QString(),51 const QString &subject = QString(),
52 InformationType informationType = InformationTypeNone,
52 const TextEventAttachments &attachments = TextEventAttachments(),53 const TextEventAttachments &attachments = TextEventAttachments(),
53 const Participants &participants = Participants());54 const Participants &participants = Participants());
5455
@@ -65,6 +66,7 @@
65 QDateTime readTimestamp() const;66 QDateTime readTimestamp() const;
66 void setReadTimestamp(const QDateTime &value);67 void setReadTimestamp(const QDateTime &value);
67 QString subject() const;68 QString subject() const;
69 InformationType informationType() const;
68 TextEventAttachments attachments() const;70 TextEventAttachments attachments() const;
6971
70 static Event fromProperties(const QVariantMap &properties);72 static Event fromProperties(const QVariantMap &properties);
7173
=== modified file 'src/textevent_p.h'
--- src/textevent_p.h 2015-09-28 23:13:21 +0000
+++ src/textevent_p.h 2016-11-24 12:52:24 +0000
@@ -45,6 +45,7 @@
45 MessageStatus theMessageStatus,45 MessageStatus theMessageStatus,
46 const QDateTime &theReadTimestamp,46 const QDateTime &theReadTimestamp,
47 const QString &theSubject,47 const QString &theSubject,
48 InformationType theInformationType,
48 const TextEventAttachments &theAttachments,49 const TextEventAttachments &theAttachments,
49 const Participants &theParticipants);50 const Participants &theParticipants);
50 ~TextEventPrivate();51 ~TextEventPrivate();
@@ -53,6 +54,7 @@
53 MessageStatus messageStatus;54 MessageStatus messageStatus;
54 QDateTime readTimestamp;55 QDateTime readTimestamp;
55 QString subject;56 QString subject;
57 InformationType informationType;
56 TextEventAttachments attachments;58 TextEventAttachments attachments;
5759
58 EventType type() const;60 EventType type() const;
5961
=== modified file 'src/thread.cpp'
--- src/thread.cpp 2015-10-08 19:35:40 +0000
+++ src/thread.cpp 2016-11-24 12:52:24 +0000
@@ -1,5 +1,5 @@
1/*1/*
2 * Copyright (C) 2013-2015 Canonical, Ltd.2 * Copyright (C) 2013-2016 Canonical, Ltd.
3 *3 *
4 * Authors:4 * Authors:
5 * Gustavo Pichorim Boiko <gustavo.boiko@canonical.com>5 * Gustavo Pichorim Boiko <gustavo.boiko@canonical.com>
@@ -38,14 +38,18 @@
38}38}
3939
40ThreadPrivate::ThreadPrivate(const QString &theAccountId,40ThreadPrivate::ThreadPrivate(const QString &theAccountId,
41 const QString &theThreadId, EventType theType,41 const QString &theThreadId, EventType theType,
42 const Participants &theParticipants,42 const Participants &theParticipants,
43 const Event &theLastEvent,43 const QDateTime &theTimestamp,
44 int theCount,44 const Event &theLastEvent,
45 int theUnreadCount,45 int theCount,
46 const Threads &theGroupedThreads) :46 int theUnreadCount,
47 accountId(theAccountId), threadId(theThreadId), type(theType), participants(theParticipants),47 const Threads &theGroupedThreads,
48 lastEvent(theLastEvent), count(theCount), unreadCount(theUnreadCount), groupedThreads(theGroupedThreads)48 ChatType theChatType,
49 const QVariantMap &theChatRoomInfo) :
50 accountId(theAccountId), threadId(theThreadId), type(theType), participants(theParticipants), timestamp(theTimestamp),
51 lastEvent(theLastEvent), count(theCount), unreadCount(theUnreadCount), groupedThreads(theGroupedThreads),
52 chatType(theChatType), chatRoomInfo(theChatRoomInfo)
49{53{
50}54}
5155
@@ -63,11 +67,14 @@
63Thread::Thread(const QString &accountId,67Thread::Thread(const QString &accountId,
64 const QString &threadId, EventType type,68 const QString &threadId, EventType type,
65 const Participants &participants,69 const Participants &participants,
70 const QDateTime &timestamp,
66 const Event &lastEvent,71 const Event &lastEvent,
67 int count,72 int count,
68 int unreadCount,73 int unreadCount,
69 const Threads &groupedThreads)74 const Threads &groupedThreads,
70: d_ptr(new ThreadPrivate(accountId, threadId, type, participants, lastEvent, count, unreadCount, groupedThreads))75 ChatType chatType,
76 const QVariantMap &chatRoomInfo)
77: d_ptr(new ThreadPrivate(accountId, threadId, type, participants, timestamp, lastEvent, count, unreadCount, groupedThreads, chatType, chatRoomInfo))
71{78{
72 qDBusRegisterMetaType<QList<QVariantMap> >();79 qDBusRegisterMetaType<QList<QVariantMap> >();
73 qRegisterMetaType<QList<QVariantMap> >();80 qRegisterMetaType<QList<QVariantMap> >();
@@ -115,6 +122,12 @@
115 return d->participants;122 return d->participants;
116}123}
117124
125QDateTime Thread::timestamp() const
126{
127 Q_D(const Thread);
128 return d->timestamp;
129}
130
118Event Thread::lastEvent() const131Event Thread::lastEvent() const
119{132{
120 Q_D(const Thread);133 Q_D(const Thread);
@@ -139,6 +152,18 @@
139 return d->groupedThreads;152 return d->groupedThreads;
140}153}
141154
155ChatType Thread::chatType() const
156{
157 Q_D(const Thread);
158 return d->chatType;
159}
160
161QVariantMap Thread::chatRoomInfo() const
162{
163 Q_D(const Thread);
164 return d->chatRoomInfo;
165}
166
142bool Thread::isNull() const167bool Thread::isNull() const
143{168{
144 Q_D(const Thread);169 Q_D(const Thread);
@@ -182,11 +207,14 @@
182 map[FieldAccountId] = d->accountId;207 map[FieldAccountId] = d->accountId;
183 map[FieldThreadId] = d->threadId;208 map[FieldThreadId] = d->threadId;
184 map[FieldType] = d->type;209 map[FieldType] = d->type;
210 map[FieldChatType] = d->chatType;
185 map[FieldParticipants] = d->participants.toVariantList();211 map[FieldParticipants] = d->participants.toVariantList();
212 map[FieldTimestamp] = d->timestamp;
186 map[FieldCount] = d->count;213 map[FieldCount] = d->count;
187 map[FieldUnreadCount] = d->unreadCount;214 map[FieldUnreadCount] = d->unreadCount;
188 map[FieldLastEventId] = lastEvent().eventId();215 map[FieldLastEventId] = lastEvent().eventId();
189 map[FieldLastEventTimestamp] = lastEvent().timestamp();216 map[FieldLastEventTimestamp] = d->timestamp;
217 map[FieldChatRoomInfo] = d->chatRoomInfo;
190218
191 QList<QVariantMap> groupedThreads;219 QList<QVariantMap> groupedThreads;
192 Q_FOREACH(const Thread &thread, d->groupedThreads) {220 Q_FOREACH(const Thread &thread, d->groupedThreads) {
@@ -210,8 +238,9 @@
210 QString accountId = properties[FieldAccountId].toString();238 QString accountId = properties[FieldAccountId].toString();
211 QString threadId = properties[FieldThreadId].toString();239 QString threadId = properties[FieldThreadId].toString();
212 EventType type = (EventType) properties[FieldType].toInt();240 EventType type = (EventType) properties[FieldType].toInt();
213241 ChatType chatType = (ChatType) properties[FieldChatType].toInt();
214 Participants participants = Participants::fromVariant(properties[FieldParticipants]);242 Participants participants = Participants::fromVariant(properties[FieldParticipants]);
243 QDateTime timestamp = QDateTime::fromString(properties[FieldTimestamp].toString(), Qt::ISODate);
215 int count = properties[FieldCount].toInt();244 int count = properties[FieldCount].toInt();
216 int unreadCount = properties[FieldUnreadCount].toInt();245 int unreadCount = properties[FieldUnreadCount].toInt();
217246
@@ -227,6 +256,11 @@
227 argument >> groupedThreads;256 argument >> groupedThreads;
228 }257 }
229 }258 }
259 QVariantMap chatRoomInfo = qdbus_cast<QVariantMap>(properties[FieldChatRoomInfo]);
260 // dbus_cast fails if the map was generated by a qml app, so we demarshal it by hand
261 if (chatRoomInfo.isEmpty()) {
262 chatRoomInfo = properties[FieldChatRoomInfo].toMap();
263 }
230264
231 Event event;265 Event event;
232 switch (type) {266 switch (type) {
@@ -237,7 +271,7 @@
237 event = VoiceEvent::fromProperties(properties);271 event = VoiceEvent::fromProperties(properties);
238 break;272 break;
239 }273 }
240 return Thread(accountId, threadId, type, participants, event, count, unreadCount, groupedThreads);274 return Thread(accountId, threadId, type, participants, timestamp, event, count, unreadCount, groupedThreads, chatType, chatRoomInfo);
241}275}
242276
243const QDBusArgument &operator>>(const QDBusArgument &argument, Threads &threads)277const QDBusArgument &operator>>(const QDBusArgument &argument, Threads &threads)
244278
=== modified file 'src/thread.h'
--- src/thread.h 2015-10-08 19:35:40 +0000
+++ src/thread.h 2016-11-24 12:52:24 +0000
@@ -1,5 +1,5 @@
1/*1/*
2 * Copyright (C) 2013-2015 Canonical, Ltd.2 * Copyright (C) 2013-2016 Canonical, Ltd.
3 *3 *
4 * Authors:4 * Authors:
5 * Gustavo Pichorim Boiko <gustavo.boiko@canonical.com>5 * Gustavo Pichorim Boiko <gustavo.boiko@canonical.com>
@@ -51,10 +51,13 @@
51 const QString &threadId,51 const QString &threadId,
52 EventType type,52 EventType type,
53 const Participants &participants,53 const Participants &participants,
54 const QDateTime &timestamp = QDateTime(),
54 const Event &lastEvent = Event(),55 const Event &lastEvent = Event(),
55 int count = 0,56 int count = 0,
56 int unreadCount = 0,57 int unreadCount = 0,
57 const Threads &groupedThreads = Threads());58 const Threads &groupedThreads = Threads(),
59 ChatType chatType = ChatTypeNone,
60 const QVariantMap &chatRoomInfo = QVariantMap());
58 Thread(const Thread &other);61 Thread(const Thread &other);
59 virtual ~Thread();62 virtual ~Thread();
60 Thread& operator=(const Thread &other);63 Thread& operator=(const Thread &other);
@@ -63,10 +66,13 @@
63 QString threadId() const;66 QString threadId() const;
64 EventType type() const;67 EventType type() const;
65 Participants participants() const;68 Participants participants() const;
69 QDateTime timestamp() const;
66 Event lastEvent() const;70 Event lastEvent() const;
67 int count() const;71 int count() const;
68 int unreadCount() const;72 int unreadCount() const;
73 ChatType chatType() const;
69 Threads groupedThreads() const;74 Threads groupedThreads() const;
75 QVariantMap chatRoomInfo() const;
7076
71 bool isNull() const;77 bool isNull() const;
72 bool operator==(const Thread &other) const;78 bool operator==(const Thread &other) const;
7379
=== modified file 'src/thread_p.h'
--- src/thread_p.h 2015-10-08 19:35:40 +0000
+++ src/thread_p.h 2016-11-24 12:52:24 +0000
@@ -1,5 +1,5 @@
1/*1/*
2 * Copyright (C) 2013-2015 Canonical, Ltd.2 * Copyright (C) 2013-2016 Canonical, Ltd.
3 *3 *
4 * Authors:4 * Authors:
5 * Gustavo Pichorim Boiko <gustavo.boiko@canonical.com>5 * Gustavo Pichorim Boiko <gustavo.boiko@canonical.com>
@@ -38,20 +38,26 @@
38 const QString &theThreadId,38 const QString &theThreadId,
39 EventType theType,39 EventType theType,
40 const Participants &theParticipants,40 const Participants &theParticipants,
41 const QDateTime &theTimestamp,
41 const Event &theLastEvent,42 const Event &theLastEvent,
42 int theCount,43 int theCount,
43 int theUnreadCount,44 int theUnreadCount,
44 const Threads &theGroupedThreads);45 const Threads &theGroupedThreads,
46 ChatType chatType,
47 const QVariantMap &chatRoomInfo);
45 virtual ~ThreadPrivate();48 virtual ~ThreadPrivate();
4649
47 QString accountId;50 QString accountId;
48 QString threadId;51 QString threadId;
49 Participants participants;52 Participants participants;
50 EventType type;53 EventType type;
54 QDateTime timestamp;
51 Event lastEvent;55 Event lastEvent;
52 int count;56 int count;
53 int unreadCount;57 int unreadCount;
54 Threads groupedThreads;58 Threads groupedThreads;
59 ChatType chatType;
60 QVariantMap chatRoomInfo;
55};61};
5662
57}63}
5864
=== modified file 'src/types.h'
--- src/types.h 2015-10-08 12:28:23 +0000
+++ src/types.h 2016-11-24 12:52:24 +0000
@@ -1,5 +1,5 @@
1/*1/*
2 * Copyright (C) 2013 Canonical, Ltd.2 * Copyright (C) 2013-2016 Canonical, Ltd.
3 *3 *
4 * Authors:4 * Authors:
5 * Gustavo Pichorim Boiko <gustavo.boiko@canonical.com>5 * Gustavo Pichorim Boiko <gustavo.boiko@canonical.com>
@@ -75,6 +75,32 @@
75 MessageTypeInformation = 275 MessageTypeInformation = 2
76};76};
7777
78enum InformationType
79{
80 InformationTypeNone = 0,
81 InformationTypeSimChange = 1,
82 InformationTypeText = 2,
83 InformationTypeSelfJoined = 3,
84 InformationTypeJoined = 4,
85 InformationTypeTitleChanged = 5,
86 InformationTypeInvitationSent = 6,
87 InformationTypeLeaving = 7,
88 InformationTypeSelfLeaving = 8,
89 InformationTypeAdminGranted = 9,
90 InformationTypeAdminRemoved = 10,
91 InformationTypeSelfAdminGranted = 11,
92 InformationTypeSelfAdminRemoved = 12,
93 InformationTypeSelfKicked = 13,
94 InformationTypeGroupGone = 14
95};
96
97enum ChatType
98{
99 ChatTypeNone = 0,
100 ChatTypeContact = 1,
101 ChatTypeRoom = 2
102};
103
78// FIXME (boiko): I think this needs to be changed to a simple enum and not flags,104// FIXME (boiko): I think this needs to be changed to a simple enum and not flags,
79// as the statuses are mutually exclusive105// as the statuses are mutually exclusive
80enum AttachmentFlag106enum AttachmentFlag
@@ -86,6 +112,20 @@
86112
87Q_DECLARE_FLAGS(AttachmentFlags, AttachmentFlag)113Q_DECLARE_FLAGS(AttachmentFlags, AttachmentFlag)
88114
115enum ParticipantState
116{
117 ParticipantStateRegular = 0,
118 ParticipantStateLocalPending = 1,
119 ParticipantStateRemotePending = 2
120};
121
122enum ParticipantRoles
123{
124 ParticipantRoleNone = 0,
125 ParticipantRoleMember = 1,
126 ParticipantRoleAdmin = 2
127};
128
89// Event writing results129// Event writing results
90enum EventWriteResult {130enum EventWriteResult {
91 EventWriteCreated,131 EventWriteCreated,
@@ -106,12 +146,35 @@
106static const char* FieldEventId = "eventId";146static const char* FieldEventId = "eventId";
107static const char* FieldType = "type";147static const char* FieldType = "type";
108static const char* FieldParticipants = "participants";148static const char* FieldParticipants = "participants";
149static const char* FieldParticipantIds = "participantIds";
109static const char* FieldCount = "count";150static const char* FieldCount = "count";
110static const char* FieldUnreadCount = "unreadCount";151static const char* FieldUnreadCount = "unreadCount";
111static const char* FieldSenderId = "senderId";152static const char* FieldSenderId = "senderId";
112static const char* FieldTimestamp = "timestamp";153static const char* FieldTimestamp = "timestamp";
113static const char* FieldDate = "date";154static const char* FieldDate = "date";
114static const char* FieldNewEvent = "newEvent";155static const char* FieldNewEvent = "newEvent";
156static const char* FieldChatType = "chatType";
157static const char* FieldChatRoomInfo = "chatRoomInfo";
158static const char* FieldChatRoomJoined = "joined";
159static const char* FieldChatRoomSelfRoles = "selfRoles";
160
161// Chat Room Info Fields
162static const char* FieldChatRoomName = "roomName";
163static const char* FieldChatRoomServer = "server";
164static const char* FieldChatRoomCreator = "creator";
165static const char* FieldChatRoomCreationTimestamp = "creationTimestamp";
166static const char* FieldChatRoomAnonymous = "anonymous";
167static const char* FieldChatRoomInviteOnly = "inviteOnly";
168static const char* FieldChatRoomParticipantLimit = "participantLimit";
169static const char* FieldChatRoomModerated = "moderated";
170static const char* FieldChatRoomTitle = "title";
171static const char* FieldChatRoomDescription = "description";
172static const char* FieldChatRoomPersistent = "persistent";
173static const char* FieldChatRoomPrivate = "private";
174static const char* FieldChatRoomPasswordProtected = "passwordProtected";
175static const char* FieldChatRoomPassword = "password";
176static const char* FieldChatRoomPasswordHint = "passwordHint";
177static const char* FieldChatRoomCanUpdateConfiguration = "canUpdateConfiguration";
115178
116// thread fields179// thread fields
117static const char* FieldLastEventId = "lastEventId";180static const char* FieldLastEventId = "lastEventId";
@@ -124,6 +187,7 @@
124static const char* FieldMessageStatus = "messageStatus";187static const char* FieldMessageStatus = "messageStatus";
125static const char* FieldReadTimestamp = "readTimestamp";188static const char* FieldReadTimestamp = "readTimestamp";
126static const char* FieldSubject = "subject";189static const char* FieldSubject = "subject";
190static const char* FieldInformationType = "informationType";
127static const char* FieldAttachments = "attachments";191static const char* FieldAttachments = "attachments";
128192
129// text attachment fields193// text attachment fields
@@ -157,6 +221,8 @@
157static const char* FieldAvatar = "avatar";221static const char* FieldAvatar = "avatar";
158static const char* FieldIdentifier = "identifier";222static const char* FieldIdentifier = "identifier";
159static const char* FieldDetailProperties = "detailProperties";223static const char* FieldDetailProperties = "detailProperties";
224static const char* FieldParticipantState = "state";
225static const char* FieldParticipantRoles = "roles";
160226
161}227}
162228
163229
=== modified file 'src/utils.cpp'
--- src/utils.cpp 2015-10-08 19:36:57 +0000
+++ src/utils.cpp 2016-11-24 12:52:24 +0000
@@ -1,5 +1,5 @@
1/*1/*
2 * Copyright (C) 2015 Canonical, Ltd.2 * Copyright (C) 2015-2016 Canonical, Ltd.
3 *3 *
4 * Authors:4 * Authors:
5 * Gustavo Pichorim Boiko <gustavo.boiko@canonical.com>5 * Gustavo Pichorim Boiko <gustavo.boiko@canonical.com>
@@ -19,10 +19,14 @@
19 * along with this program. If not, see <http://www.gnu.org/licenses/>.19 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 */20 */
2121
22#include <unistd.h>
22#include "utils_p.h"23#include "utils_p.h"
23#include "phoneutils_p.h"24#include "phoneutils_p.h"
24#include <QDebug>25#include <QDebug>
25#include <QStringList>26#include <QStringList>
27#include <QDBusInterface>
28#include <QDBusConnection>
29#include <QDBusReply>
26#include <QMap>30#include <QMap>
2731
28namespace History {32namespace History {
@@ -32,9 +36,12 @@
32}36}
3337
34// FIXME: find a better way to determine when accounts should be grouped38// FIXME: find a better way to determine when accounts should be grouped
35bool Utils::shouldGroupAccount(const QString &accountId)39bool Utils::shouldGroupThread(const Thread &thread)
36{40{
37 return (matchFlagsForAccount(accountId) & MatchPhoneNumber);41 if (protocolFromAccountId(thread.accountId()) == "multimedia") {
42 return thread.chatType() != History::ChatTypeRoom;
43 }
44 return (matchFlagsForAccount(thread.accountId()) & MatchPhoneNumber);
38}45}
3946
40MatchFlags Utils::matchFlagsForAccount(const QString &accountId)47MatchFlags Utils::matchFlagsForAccount(const QString &accountId)
@@ -150,4 +157,22 @@
150 return normalizedId;157 return normalizedId;
151}158}
152159
160QVariant Utils::getUserValue(const QString &interface, const QString &propName)
161{
162 QString uid = QString::number(getuid());
163 QString activeUser = "/org/freedesktop/Accounts/User" + uid;
164
165 QDBusInterface iface("org.freedesktop.Accounts",
166 activeUser,
167 "org.freedesktop.DBus.Properties",
168 QDBusConnection::systemBus());
169 QDBusReply<QVariant> reply = iface.call("Get", interface, propName);
170 if (reply.isValid()) {
171 return reply.value();
172 } else {
173 qWarning() << "Failed to get user property " << propName << " from AccountsService:" << reply.error().message();
174 }
175 return QVariant();
176}
177
153}178}
154179
=== modified file 'src/utils_p.h'
--- src/utils_p.h 2015-10-02 18:28:47 +0000
+++ src/utils_p.h 2016-11-24 12:52:24 +0000
@@ -1,5 +1,5 @@
1/*1/*
2 * Copyright (C) 2015 Canonical, Ltd.2 * Copyright (C) 2015-2016 Canonical, Ltd.
3 *3 *
4 * Authors:4 * Authors:
5 * Gustavo Pichorim Boiko <gustavo.boiko@canonical.com>5 * Gustavo Pichorim Boiko <gustavo.boiko@canonical.com>
@@ -23,6 +23,7 @@
23#define UTILS_P_H23#define UTILS_P_H
2424
25#include "types.h"25#include "types.h"
26#include "thread.h"
2627
27namespace History {28namespace History {
2829
@@ -34,8 +35,9 @@
34 static bool compareIds(const QString &accountId, const QString &id1, const QString & id2);35 static bool compareIds(const QString &accountId, const QString &id1, const QString & id2);
35 static bool compareParticipants(const QStringList &participants1, const QStringList &participants2, MatchFlags flags);36 static bool compareParticipants(const QStringList &participants1, const QStringList &participants2, MatchFlags flags);
36 static bool compareNormalizedParticipants(const QStringList &participants1, const QStringList &participants2, MatchFlags flags);37 static bool compareNormalizedParticipants(const QStringList &participants1, const QStringList &participants2, MatchFlags flags);
37 static bool shouldGroupAccount(const QString &accountId);38 static bool shouldGroupThread(const Thread &thread);
38 static QString normalizeId(const QString &accountId, const QString &id);39 static QString normalizeId(const QString &accountId, const QString &id);
40 static QVariant getUserValue(const QString &interface, const QString &propName);
3941
40private:42private:
41 Utils();43 Utils();
4244
=== modified file 'tests/Ubuntu.History/HistoryEventModelTest.cpp'
--- tests/Ubuntu.History/HistoryEventModelTest.cpp 2016-03-30 16:53:04 +0000
+++ tests/Ubuntu.History/HistoryEventModelTest.cpp 2016-11-24 12:52:24 +0000
@@ -68,6 +68,7 @@
68 History::MessageStatusRead,68 History::MessageStatusRead,
69 QDateTime::currentDateTime(),69 QDateTime::currentDateTime(),
70 "The subject",70 "The subject",
71 History::InformationTypeNone,
71 History::TextEventAttachments(),72 History::TextEventAttachments(),
72 textThread.participants());73 textThread.participants());
73 QVERIFY(mManager->writeEvents(History::Events() << event));74 QVERIFY(mManager->writeEvents(History::Events() << event));
7475
=== modified file 'tests/Ubuntu.History/HistoryGroupedThreadsModelTest.cpp'
--- tests/Ubuntu.History/HistoryGroupedThreadsModelTest.cpp 2015-10-05 22:54:47 +0000
+++ tests/Ubuntu.History/HistoryGroupedThreadsModelTest.cpp 2016-11-24 12:52:24 +0000
@@ -123,7 +123,7 @@
123123
124 // insert another event in second thread124 // insert another event in second thread
125 History::TextEvent secondEvent = History::TextEvent(textThread.accountId(), textThread.threadId(), QString("eventId2%1").arg(QString::number(qrand() % 1024)),125 History::TextEvent secondEvent = History::TextEvent(textThread.accountId(), textThread.threadId(), QString("eventId2%1").arg(QString::number(qrand() % 1024)),
126 QString("1234567"), QDateTime::currentDateTime(), false, "Random Message2",126 QString("1234567"), QDateTime::currentDateTime().addSecs(1), false, "Random Message2",
127 History::MessageTypeText);127 History::MessageTypeText);
128 mManager->writeEvents(History::Events() << secondEvent);128 mManager->writeEvents(History::Events() << secondEvent);
129 QTRY_COMPARE(dataChanged.count(), 1);129 QTRY_COMPARE(dataChanged.count(), 1);
130130
=== modified file 'tests/common/mock/CMakeLists.txt'
--- tests/common/mock/CMakeLists.txt 2015-04-07 14:40:58 +0000
+++ tests/common/mock/CMakeLists.txt 2016-11-24 12:52:24 +0000
@@ -16,4 +16,4 @@
1616
17add_executable(telepathy-mock ${mock_SRCS})17add_executable(telepathy-mock ${mock_SRCS})
18qt5_use_modules(telepathy-mock Core DBus)18qt5_use_modules(telepathy-mock Core DBus)
19target_link_libraries(telepathy-mock ${TP_QT5_LIBRARIES} ${TELEPATHY_QT5_SERVICE_LIBRARIES} ${OFONO_QT_LIBRARIES} ${PULSEAUDIO_LIBRARIES})19target_link_libraries(telepathy-mock ${TP_QT5_LIBRARIES} ${TELEPATHY_QT5_SERVICE_LIBRARIES} ${OFONO_QT_LIBRARIES} ${PULSEAUDIO_LIBRARIES} ${Qt5Network_LIBRARIES})
2020
=== modified file 'tests/common/mock/callchannel.cpp'
--- tests/common/mock/callchannel.cpp 2015-06-16 16:16:54 +0000
+++ tests/common/mock/callchannel.cpp 2016-11-24 12:52:24 +0000
@@ -194,7 +194,8 @@
194 reason.reason = Tp::CallStateChangeReasonNoAnswer;194 reason.reason = Tp::CallStateChangeReasonNoAnswer;
195 }195 }
196 mCallChannel->setCallState(Tp::CallStateEnded, 0, reason, stateDetails);196 mCallChannel->setCallState(Tp::CallStateEnded, 0, reason, stateDetails);
197 mBaseChannel->close();197 // leave the channel opened for a bit longer so that the call state gets propagated correctly.
198 QTimer::singleShot(10, mBaseChannel.data(), &Tp::BaseChannel::close);
198 } else if (state == "active") {199 } else if (state == "active") {
199 qDebug() << "active";200 qDebug() << "active";
200 mHoldIface->setHoldState(Tp::LocalHoldStateUnheld, Tp::LocalHoldStateReasonNone);201 mHoldIface->setHoldState(Tp::LocalHoldStateUnheld, Tp::LocalHoldStateReasonNone);
201202
=== modified file 'tests/common/mock/mockconnectiondbus.cpp'
--- tests/common/mock/mockconnectiondbus.cpp 2015-04-07 14:40:58 +0000
+++ tests/common/mock/mockconnectiondbus.cpp 2016-11-24 12:52:24 +0000
@@ -70,16 +70,15 @@
7070
71bool MockConnectionDBus::connectToBus()71bool MockConnectionDBus::connectToBus()
72{72{
73 bool ok = QDBusConnection::sessionBus().registerService("com.canonical.MockConnection");
74 if (!ok) {
75 return false;
76 }
77
78 if (!mAdaptor) {73 if (!mAdaptor) {
79 mAdaptor = new MockConnectionAdaptor(this);74 mAdaptor = new MockConnectionAdaptor(this);
80 }75 }
8176
82 return QDBusConnection::sessionBus().registerObject(mObjectPath, this);77 if (!QDBusConnection::sessionBus().registerObject(mObjectPath, this)) {
78 return false;
79 }
80
81 return QDBusConnection::sessionBus().registerService("com.canonical.MockConnection");
83}82}
8483
85void MockConnectionDBus::PlaceIncomingMessage(const QString &message, const QVariantMap &properties)84void MockConnectionDBus::PlaceIncomingMessage(const QString &message, const QVariantMap &properties)
8685
=== modified file 'tests/common/mock/textchannel.cpp'
--- tests/common/mock/textchannel.cpp 2015-08-28 10:15:47 +0000
+++ tests/common/mock/textchannel.cpp 2016-11-24 12:52:24 +0000
@@ -74,7 +74,9 @@
74 baseChannel->plugInterface(Tp::AbstractChannelInterfacePtr::dynamicCast(mMessagesIface));74 baseChannel->plugInterface(Tp::AbstractChannelInterfacePtr::dynamicCast(mMessagesIface));
7575
76 // group stuff76 // group stuff
77 mGroupIface = Tp::BaseChannelGroupInterface::create(Tp::ChannelGroupFlagCanAdd, conn->selfHandle());77 mGroupIface = Tp::BaseChannelGroupInterface::create();
78 mGroupIface->setGroupFlags(Tp::ChannelGroupFlagCanAdd);
79 mGroupIface->setSelfHandle(conn->selfHandle());
78 mGroupIface->setAddMembersCallback(Tp::memFun(this,&MockTextChannel::onAddMembers));80 mGroupIface->setAddMembersCallback(Tp::memFun(this,&MockTextChannel::onAddMembers));
79 mGroupIface->setRemoveMembersCallback(Tp::memFun(this,&MockTextChannel::onRemoveMembers));81 mGroupIface->setRemoveMembersCallback(Tp::memFun(this,&MockTextChannel::onRemoveMembers));
80 baseChannel->plugInterface(Tp::AbstractChannelInterfacePtr::dynamicCast(mGroupIface));82 baseChannel->plugInterface(Tp::AbstractChannelInterfacePtr::dynamicCast(mGroupIface));
@@ -232,7 +234,7 @@
232 mMembers << handle;234 mMembers << handle;
233 }235 }
234 }236 }
235 mGroupIface->addMembers(handles, recipients);237 mGroupIface->setMembers(mMembers, QVariantMap());
236}238}
237239
238QStringList MockTextChannel::recipients() const240QStringList MockTextChannel::recipients() const
@@ -250,7 +252,7 @@
250 addMembers(mConnection->inspectHandles(Tp::HandleTypeContact, handles, error));252 addMembers(mConnection->inspectHandles(Tp::HandleTypeContact, handles, error));
251}253}
252254
253void MockTextChannel::onRemoveMembers(const Tp::UIntList &handles, const QString &message, Tp::DBusError *error)255void MockTextChannel::onRemoveMembers(const Tp::UIntList &handles, const QString &message, uint reason, Tp::DBusError *error)
254{256{
255 Q_FOREACH(uint handle, handles) {257 Q_FOREACH(uint handle, handles) {
256 Q_FOREACH(const QString &recipient, mConnection->inspectHandles(Tp::HandleTypeContact, Tp::UIntList() << handle, error)) {258 Q_FOREACH(const QString &recipient, mConnection->inspectHandles(Tp::HandleTypeContact, Tp::UIntList() << handle, error)) {
@@ -258,5 +260,5 @@
258 }260 }
259 mMembers.removeAll(handle);261 mMembers.removeAll(handle);
260 }262 }
261 mGroupIface->removeMembers(handles);263 mGroupIface->setMembers(mMembers, QVariantMap());
262}264}
263265
=== modified file 'tests/common/mock/textchannel.h'
--- tests/common/mock/textchannel.h 2015-04-07 14:40:58 +0000
+++ tests/common/mock/textchannel.h 2016-11-24 12:52:24 +0000
@@ -45,8 +45,8 @@
45 void addMembers(QStringList recipients);45 void addMembers(QStringList recipients);
46 QStringList recipients() const;46 QStringList recipients() const;
47 Tp::UIntList members();47 Tp::UIntList members();
48 void onRemoveMembers(const Tp::UIntList& handles, const QString& message, uint reason, Tp::DBusError* error);
48 void onAddMembers(const Tp::UIntList& handles, const QString& message, Tp::DBusError* error);49 void onAddMembers(const Tp::UIntList& handles, const QString& message, Tp::DBusError* error);
49 void onRemoveMembers(const Tp::UIntList& handles, const QString& message, Tp::DBusError* error);
5050
51public Q_SLOTS:51public Q_SLOTS:
52 void placeDeliveryReport(const QString &messageId, const QString &status);52 void placeDeliveryReport(const QString &messageId, const QString &status);
5353
=== modified file 'tests/daemon/CMakeLists.txt'
--- tests/daemon/CMakeLists.txt 2016-09-09 14:48:33 +0000
+++ tests/daemon/CMakeLists.txt 2016-11-24 12:52:24 +0000
@@ -5,7 +5,7 @@
5 ${TP_QT5_INCLUDE_DIRS}5 ${TP_QT5_INCLUDE_DIRS}
6 )6 )
77
8#generate_telepathy_test(DaemonTest8generate_telepathy_test(DaemonTest
9# SOURCES DaemonTest.cpp handler.cpp approver.cpp9 SOURCES DaemonTest.cpp handler.cpp approver.cpp
10# TASKS --task ${CMAKE_BINARY_DIR}/daemon/history-daemon --ignore-return --task-name history-daemon10 TASKS --task ${CMAKE_BINARY_DIR}/daemon/history-daemon --ignore-return --task-name history-daemon
11# WAIT_FOR com.canonical.HistoryService)11 WAIT_FOR com.canonical.HistoryService)
1212
=== modified file 'tests/daemon/DaemonTest.cpp'
--- tests/daemon/DaemonTest.cpp 2015-10-06 12:50:14 +0000
+++ tests/daemon/DaemonTest.cpp 2016-11-24 12:52:24 +0000
@@ -324,7 +324,7 @@
324 connect(mAccount->connection()->contactManager()->contactsForIdentifiers(QStringList() << phoneNumber),324 connect(mAccount->connection()->contactManager()->contactsForIdentifiers(QStringList() << phoneNumber),
325 SIGNAL(finished(Tp::PendingOperation*)),325 SIGNAL(finished(Tp::PendingOperation*)),
326 SLOT(onPendingContactsFinished(Tp::PendingOperation*)));326 SLOT(onPendingContactsFinished(Tp::PendingOperation*)));
327 QTRY_COMPARE(spy.count(), 1);327 QVERIFY(spy.wait());
328328
329 QList<Tp::ContactPtr> contacts = spy.first().first().value<QList<Tp::ContactPtr> >();329 QList<Tp::ContactPtr> contacts = spy.first().first().value<QList<Tp::ContactPtr> >();
330 QCOMPARE(contacts.count(), 1);330 QCOMPARE(contacts.count(), 1);
@@ -335,7 +335,7 @@
335 Q_FOREACH(Tp::ContactPtr contact, contacts) {335 Q_FOREACH(Tp::ContactPtr contact, contacts) {
336 mAccount->ensureAudioCall(contact, "audio", QDateTime::currentDateTime(), TP_QT_IFACE_CLIENT + ".HistoryTestHandler");336 mAccount->ensureAudioCall(contact, "audio", QDateTime::currentDateTime(), TP_QT_IFACE_CLIENT + ".HistoryTestHandler");
337 }337 }
338 QTRY_COMPARE(spyCallChannel.count(), 1);338 QVERIFY(spyCallChannel.wait());
339339
340 Tp::CallChannelPtr channel = spyCallChannel.first().first().value<Tp::CallChannelPtr>();340 Tp::CallChannelPtr channel = spyCallChannel.first().first().value<Tp::CallChannelPtr>();
341 QVERIFY(channel);341 QVERIFY(channel);
342342
=== modified file 'tests/libhistoryservice/ParticipantTest.cpp'
--- tests/libhistoryservice/ParticipantTest.cpp 2015-10-05 22:20:00 +0000
+++ tests/libhistoryservice/ParticipantTest.cpp 2016-11-24 12:52:24 +0000
@@ -58,16 +58,20 @@
58 QString contactId("theContactId");58 QString contactId("theContactId");
59 QString alias("theAlias");59 QString alias("theAlias");
60 QString avatar("theAvatar");60 QString avatar("theAvatar");
61 uint state = History::ParticipantStateRegular;
62 uint roles = History::ParticipantRoleMember;
61 QVariantMap detailProperties;63 QVariantMap detailProperties;
62 detailProperties["someProperty"] = "someValue";64 detailProperties["someProperty"] = "someValue";
6365
6466
65 History::Participant participant(accountId, identifier, contactId, alias, avatar, detailProperties);67 History::Participant participant(accountId, identifier, contactId, alias, avatar, state, roles, detailProperties);
66 QCOMPARE(participant.accountId(), accountId);68 QCOMPARE(participant.accountId(), accountId);
67 QCOMPARE(participant.identifier(), identifier);69 QCOMPARE(participant.identifier(), identifier);
68 QCOMPARE(participant.contactId(), contactId);70 QCOMPARE(participant.contactId(), contactId);
69 QCOMPARE(participant.alias(), alias);71 QCOMPARE(participant.alias(), alias);
70 QCOMPARE(participant.avatar(), avatar);72 QCOMPARE(participant.avatar(), avatar);
73 QCOMPARE(participant.state(), state);
74 QCOMPARE(participant.roles(), roles);
71 QCOMPARE(participant.detailProperties(), detailProperties);75 QCOMPARE(participant.detailProperties(), detailProperties);
72}76}
7377
@@ -105,7 +109,7 @@
105{109{
106 QVariantMap detailProperties;110 QVariantMap detailProperties;
107 detailProperties["theProperty"] = "theValue";111 detailProperties["theProperty"] = "theValue";
108 History::Participant original("accountId", "identifier", "contactId", "alias", "avatar", detailProperties);112 History::Participant original("accountId", "identifier", "contactId", "alias", "avatar", History::ParticipantStateRegular, History::ParticipantRoleAdmin, detailProperties);
109113
110 History::Participant copy(original);114 History::Participant copy(original);
111115
@@ -114,6 +118,8 @@
114 QCOMPARE(copy.contactId(), original.contactId());118 QCOMPARE(copy.contactId(), original.contactId());
115 QCOMPARE(copy.alias(), original.alias());119 QCOMPARE(copy.alias(), original.alias());
116 QCOMPARE(copy.avatar(), original.avatar());120 QCOMPARE(copy.avatar(), original.avatar());
121 QCOMPARE(copy.state(), original.state());
122 QCOMPARE(copy.roles(), original.roles());
117 QCOMPARE(copy.detailProperties(), original.detailProperties());123 QCOMPARE(copy.detailProperties(), original.detailProperties());
118}124}
119125
@@ -121,7 +127,7 @@
121{127{
122 QVariantMap detailProperties;128 QVariantMap detailProperties;
123 detailProperties["theProperty2"] = "theValue2";129 detailProperties["theProperty2"] = "theValue2";
124 History::Participant original("accountId2", "identifier2", "contactId2", "alias2", "avatar2", detailProperties);130 History::Participant original("accountId2", "identifier2", "contactId2", "alias2", "avatar2", History::ParticipantStateRegular, History::ParticipantRoleMember, detailProperties);
125131
126 History::Participant copy;132 History::Participant copy;
127 copy = original;133 copy = original;
@@ -131,6 +137,8 @@
131 QCOMPARE(copy.contactId(), original.contactId());137 QCOMPARE(copy.contactId(), original.contactId());
132 QCOMPARE(copy.alias(), original.alias());138 QCOMPARE(copy.alias(), original.alias());
133 QCOMPARE(copy.avatar(), original.avatar());139 QCOMPARE(copy.avatar(), original.avatar());
140 QCOMPARE(copy.state(), original.state());
141 QCOMPARE(copy.roles(), original.roles());
134 QCOMPARE(copy.detailProperties(), original.detailProperties());142 QCOMPARE(copy.detailProperties(), original.detailProperties());
135}143}
136144
@@ -166,13 +174,15 @@
166 QVariantMap detailProperties;174 QVariantMap detailProperties;
167 detailProperties["someDetailProperty"] = "someValue";175 detailProperties["someDetailProperty"] = "someValue";
168176
169 History::Participant participant("theAccountId", "theIdentifier", "theContactId", "theAlias", "theAvatar", detailProperties);177 History::Participant participant("theAccountId", "theIdentifier", "theContactId", "theAlias", "theAvatar", History::ParticipantStateRegular, History::ParticipantRoleAdmin, detailProperties);
170 QVariantMap properties = participant.properties();178 QVariantMap properties = participant.properties();
171 QCOMPARE(properties[History::FieldAccountId].toString(), participant.accountId());179 QCOMPARE(properties[History::FieldAccountId].toString(), participant.accountId());
172 QCOMPARE(properties[History::FieldIdentifier].toString(), participant.identifier());180 QCOMPARE(properties[History::FieldIdentifier].toString(), participant.identifier());
173 QCOMPARE(properties[History::FieldContactId].toString(), participant.contactId());181 QCOMPARE(properties[History::FieldContactId].toString(), participant.contactId());
174 QCOMPARE(properties[History::FieldAlias].toString(), participant.alias());182 QCOMPARE(properties[History::FieldAlias].toString(), participant.alias());
175 QCOMPARE(properties[History::FieldAvatar].toString(), participant.avatar());183 QCOMPARE(properties[History::FieldAvatar].toString(), participant.avatar());
184 QCOMPARE(properties[History::FieldParticipantState].toUInt(), participant.state());
185 QCOMPARE(properties[History::FieldParticipantRoles].toUInt(), participant.roles());
176 QCOMPARE(properties[History::FieldDetailProperties].toMap(), participant.detailProperties());186 QCOMPARE(properties[History::FieldDetailProperties].toMap(), participant.detailProperties());
177}187}
178188
@@ -186,6 +196,8 @@
186 properties[History::FieldContactId] = "someContactId";196 properties[History::FieldContactId] = "someContactId";
187 properties[History::FieldAlias] = "someAlias";197 properties[History::FieldAlias] = "someAlias";
188 properties[History::FieldAvatar] = "someAvatar";198 properties[History::FieldAvatar] = "someAvatar";
199 properties[History::FieldParticipantState] = History::ParticipantStateRegular;
200 properties[History::FieldParticipantRoles] = History::ParticipantRoleAdmin;
189 detailProperties["someDetailProperty"] = "someValue";201 detailProperties["someDetailProperty"] = "someValue";
190 properties[History::FieldDetailProperties] = detailProperties;202 properties[History::FieldDetailProperties] = detailProperties;
191203
@@ -195,6 +207,8 @@
195 QCOMPARE(participant.contactId(), properties[History::FieldContactId].toString());207 QCOMPARE(participant.contactId(), properties[History::FieldContactId].toString());
196 QCOMPARE(participant.alias(), properties[History::FieldAlias].toString());208 QCOMPARE(participant.alias(), properties[History::FieldAlias].toString());
197 QCOMPARE(participant.avatar(), properties[History::FieldAvatar].toString());209 QCOMPARE(participant.avatar(), properties[History::FieldAvatar].toString());
210 QCOMPARE(participant.state(), properties[History::FieldParticipantState].toUInt());
211 QCOMPARE(participant.roles(), properties[History::FieldParticipantRoles].toUInt());
198 QCOMPARE(participant.detailProperties(), properties[History::FieldDetailProperties].toMap());212 QCOMPARE(participant.detailProperties(), properties[History::FieldDetailProperties].toMap());
199}213}
200214
201215
=== modified file 'tests/libhistoryservice/TextEventTest.cpp'
--- tests/libhistoryservice/TextEventTest.cpp 2015-09-28 23:13:21 +0000
+++ tests/libhistoryservice/TextEventTest.cpp 2016-11-24 12:52:24 +0000
@@ -53,33 +53,34 @@
53 QTest::addColumn<int>("messageStatus");53 QTest::addColumn<int>("messageStatus");
54 QTest::addColumn<QDateTime>("readTimestamp");54 QTest::addColumn<QDateTime>("readTimestamp");
55 QTest::addColumn<QString>("subject");55 QTest::addColumn<QString>("subject");
56 QTest::addColumn<int>("informationType");
56 QTest::addColumn<QStringList>("participants");57 QTest::addColumn<QStringList>("participants");
5758
58 QTest::newRow("unread message") << "testAccountId" << "testThreadId" << "testEventId"59 QTest::newRow("unread message") << "testAccountId" << "testThreadId" << "testEventId"
59 << "testSenderId" << QDateTime::currentDateTime().addDays(-10)60 << "testSenderId" << QDateTime::currentDateTime().addDays(-10)
60 << true << "One Test Message" << (int)History::MessageTypeText61 << true << "One Test Message" << (int)History::MessageTypeText
61 << 0 << QDateTime::currentDateTime().addDays(-5) << "Test Subject"62 << 0 << QDateTime::currentDateTime().addDays(-5) << "Test Subject" << (int) History::InformationTypeJoined
62 << (QStringList() << "testParticipant");63 << (QStringList() << "testParticipant");
63 QTest::newRow("read message") << "testAccountId2" << "testThreadId2" << "testEventId2"64 QTest::newRow("read message") << "testAccountId2" << "testThreadId2" << "testEventId2"
64 << "testSenderId2" << QDateTime::currentDateTime().addDays(-10)65 << "testSenderId2" << QDateTime::currentDateTime().addDays(-10)
65 << false << "One Test Message" << (int)History::MessageTypeText66 << false << "One Test Message" << (int)History::MessageTypeText
66 << 0 << QDateTime::currentDateTime().addDays(-5) << "Test Subject 2"67 << 0 << QDateTime::currentDateTime().addDays(-5) << "Test Subject 2" << (int) History::InformationTypeLeaving
67 << (QStringList() << "testParticipant2");68 << (QStringList() << "testParticipant2");
68 QTest::newRow("message status") << "testAccountId" << "testThreadId" << "testEventId"69 QTest::newRow("message status") << "testAccountId" << "testThreadId" << "testEventId"
69 << "testSenderId" << QDateTime::currentDateTime().addDays(-10)70 << "testSenderId" << QDateTime::currentDateTime().addDays(-10)
70 << true << "One Test Message" << (int)History::MessageTypeText71 << true << "One Test Message" << (int)History::MessageTypeText
71 << (int)History::MessageStatusAccepted72 << (int)History::MessageStatusAccepted
72 << QDateTime::currentDateTime().addDays(-5) << "Test Subject 3"73 << QDateTime::currentDateTime().addDays(-5) << "Test Subject 3" << (int) History::InformationTypeSelfLeaving
73 << (QStringList() << "testParticipant");74 << (QStringList() << "testParticipant");
74 QTest::newRow("multi party message") << "testAccountId" << "testThreadId" << "testEventId"75 QTest::newRow("multi party message") << "testAccountId" << "testThreadId" << "testEventId"
75 << "testSenderId" << QDateTime::currentDateTime().addDays(-10)76 << "testSenderId" << QDateTime::currentDateTime().addDays(-10)
76 << true << "One Test Message" << (int)History::MessageTypeMultiPart77 << true << "One Test Message" << (int)History::MessageTypeMultiPart
77 << 0 << QDateTime::currentDateTime().addDays(-5) << QString()78 << 0 << QDateTime::currentDateTime().addDays(-5) << QString() << (int) History::InformationTypeNone
78 << (QStringList() << "testParticipant");79 << (QStringList() << "testParticipant");
79 QTest::newRow("multiple participants") << "testAccountId2" << "testThreadId2" << "testEventId2"80 QTest::newRow("multiple participants") << "testAccountId2" << "testThreadId2" << "testEventId2"
80 << "testSenderId2" << QDateTime::currentDateTime().addDays(-7)81 << "testSenderId2" << QDateTime::currentDateTime().addDays(-7)
81 << true << "One Test Message 2" << (int)History::MessageTypeText82 << true << "One Test Message 2" << (int)History::MessageTypeText
82 << 0 << QDateTime::currentDateTime().addDays(-4) << QString()83 << 0 << QDateTime::currentDateTime().addDays(-4) << QString() << (int) History::InformationTypeNone
83 << (QStringList() << "one" << "two" << "three" << "four");84 << (QStringList() << "one" << "two" << "three" << "four");
84}85}
8586
@@ -96,10 +97,11 @@
96 QFETCH(int, messageStatus);97 QFETCH(int, messageStatus);
97 QFETCH(QDateTime, readTimestamp);98 QFETCH(QDateTime, readTimestamp);
98 QFETCH(QString, subject);99 QFETCH(QString, subject);
100 QFETCH(int, informationType);
99 QFETCH(QStringList, participants);101 QFETCH(QStringList, participants);
100 History::TextEvent event(accountId, threadId, eventId, senderId, timestamp, newEvent,102 History::TextEvent event(accountId, threadId, eventId, senderId, timestamp, newEvent,
101 message, (History::MessageType)messageType, (History::MessageStatus)messageStatus,103 message, (History::MessageType)messageType, (History::MessageStatus)messageStatus,
102 readTimestamp, subject, History::TextEventAttachments(),104 readTimestamp, subject, (History::InformationType) informationType, History::TextEventAttachments(),
103 participantsFromIdentifiers(accountId, participants));105 participantsFromIdentifiers(accountId, participants));
104106
105 // check that the values are properly set107 // check that the values are properly set
@@ -150,33 +152,34 @@
150 QTest::addColumn<int>("messageStatus");152 QTest::addColumn<int>("messageStatus");
151 QTest::addColumn<QDateTime>("readTimestamp");153 QTest::addColumn<QDateTime>("readTimestamp");
152 QTest::addColumn<QString>("subject");154 QTest::addColumn<QString>("subject");
155 QTest::addColumn<int>("informationType");
153 QTest::addColumn<QStringList>("participants");156 QTest::addColumn<QStringList>("participants");
154157
155 QTest::newRow("unread message") << "testAccountId" << "testThreadId" << "testEventId"158 QTest::newRow("unread message") << "testAccountId" << "testThreadId" << "testEventId"
156 << "testSenderId" << QDateTime::currentDateTime().addDays(-10)159 << "testSenderId" << QDateTime::currentDateTime().addDays(-10)
157 << true << "One Test Message" << (int)History::MessageTypeText160 << true << "One Test Message" << (int)History::MessageTypeText
158 << 0 << QDateTime::currentDateTime().addDays(-5) << "Test Subject"161 << 0 << QDateTime::currentDateTime().addDays(-5) << "Test Subject" << (int) History::InformationTypeJoined
159 << (QStringList() << "testParticipant");162 << (QStringList() << "testParticipant");
160 QTest::newRow("read message") << "testAccountId2" << "testThreadId2" << "testEventId2"163 QTest::newRow("read message") << "testAccountId2" << "testThreadId2" << "testEventId2"
161 << "testSenderId2" << QDateTime::currentDateTime().addDays(-10)164 << "testSenderId2" << QDateTime::currentDateTime().addDays(-10)
162 << false << "One Test Message" << (int)History::MessageTypeText165 << false << "One Test Message" << (int)History::MessageTypeText
163 << 0 << QDateTime::currentDateTime().addDays(-5) << "Test Subject 2"166 << 0 << QDateTime::currentDateTime().addDays(-5) << "Test Subject 2" << (int) History::InformationTypeNone
164 << (QStringList() << "testParticipant2");167 << (QStringList() << "testParticipant2");
165 QTest::newRow("message status") << "testAccountId" << "testThreadId" << "testEventId"168 QTest::newRow("message status") << "testAccountId" << "testThreadId" << "testEventId"
166 << "testSenderId" << QDateTime::currentDateTime().addDays(-10)169 << "testSenderId" << QDateTime::currentDateTime().addDays(-10)
167 << true << "One Test Message" << (int)History::MessageTypeText170 << true << "One Test Message" << (int)History::MessageTypeText
168 << (int)History::MessageStatusAccepted171 << (int)History::MessageStatusAccepted
169 << QDateTime::currentDateTime().addDays(-5) << "Test Subject 3"172 << QDateTime::currentDateTime().addDays(-5) << "Test Subject 3" << (int) History::InformationTypeNone
170 << (QStringList() << "testParticipant");173 << (QStringList() << "testParticipant");
171 QTest::newRow("multi party message") << "testAccountId" << "testThreadId" << "testEventId"174 QTest::newRow("multi party message") << "testAccountId" << "testThreadId" << "testEventId"
172 << "testSenderId" << QDateTime::currentDateTime().addDays(-10)175 << "testSenderId" << QDateTime::currentDateTime().addDays(-10)
173 << true << "One Test Message" << (int)History::MessageTypeMultiPart176 << true << "One Test Message" << (int)History::MessageTypeMultiPart
174 << 0 << QDateTime::currentDateTime().addDays(-5) << QString()177 << 0 << QDateTime::currentDateTime().addDays(-5) << QString() << (int) History::InformationTypeNone
175 << (QStringList() << "testParticipant");178 << (QStringList() << "testParticipant");
176 QTest::newRow("multiple participants") << "testAccountId2" << "testThreadId2" << "testEventId2"179 QTest::newRow("multiple participants") << "testAccountId2" << "testThreadId2" << "testEventId2"
177 << "testSenderId2" << QDateTime::currentDateTime().addDays(-7)180 << "testSenderId2" << QDateTime::currentDateTime().addDays(-7)
178 << true << "One Test Message 2" << (int)History::MessageTypeText181 << true << "One Test Message 2" << (int)History::MessageTypeText
179 << 0 << QDateTime::currentDateTime().addDays(-4) << QString()182 << 0 << QDateTime::currentDateTime().addDays(-4) << QString() << (int) History::InformationTypeNone
180 << (QStringList() << "one" << "two" << "three" << "four");183 << (QStringList() << "one" << "two" << "three" << "four");
181}184}
182185
@@ -193,6 +196,7 @@
193 QFETCH(int, messageStatus);196 QFETCH(int, messageStatus);
194 QFETCH(QDateTime, readTimestamp);197 QFETCH(QDateTime, readTimestamp);
195 QFETCH(QString, subject);198 QFETCH(QString, subject);
199 QFETCH(int, informationType);
196 QFETCH(QStringList, participants);200 QFETCH(QStringList, participants);
197201
198 QVariantMap properties;202 QVariantMap properties;
@@ -207,6 +211,7 @@
207 properties[History::FieldMessageStatus] = messageStatus;211 properties[History::FieldMessageStatus] = messageStatus;
208 properties[History::FieldReadTimestamp] = readTimestamp.toString("yyyy-MM-ddTHH:mm:ss.zzz");212 properties[History::FieldReadTimestamp] = readTimestamp.toString("yyyy-MM-ddTHH:mm:ss.zzz");
209 properties[History::FieldSubject] = subject;213 properties[History::FieldSubject] = subject;
214 properties[History::FieldInformationType] = informationType;
210 properties[History::FieldParticipants] = participantsFromIdentifiers(accountId, participants).toVariantList();215 properties[History::FieldParticipants] = participantsFromIdentifiers(accountId, participants).toVariantList();
211216
212 History::TextEvent textEvent = History::TextEvent::fromProperties(properties);217 History::TextEvent textEvent = History::TextEvent::fromProperties(properties);
@@ -221,6 +226,7 @@
221 QCOMPARE(textEvent.messageStatus(), (History::MessageStatus) messageStatus);226 QCOMPARE(textEvent.messageStatus(), (History::MessageStatus) messageStatus);
222 QCOMPARE(textEvent.readTimestamp().toString(Qt::ISODate), readTimestamp.toString(Qt::ISODate));227 QCOMPARE(textEvent.readTimestamp().toString(Qt::ISODate), readTimestamp.toString(Qt::ISODate));
223 QCOMPARE(textEvent.subject(), subject);228 QCOMPARE(textEvent.subject(), subject);
229 QCOMPARE(textEvent.informationType(), (History::InformationType) informationType);
224 QCOMPARE(textEvent.participants().identifiers(), participants);230 QCOMPARE(textEvent.participants().identifiers(), participants);
225}231}
226232
@@ -245,33 +251,34 @@
245 QTest::addColumn<int>("messageStatus");251 QTest::addColumn<int>("messageStatus");
246 QTest::addColumn<QDateTime>("readTimestamp");252 QTest::addColumn<QDateTime>("readTimestamp");
247 QTest::addColumn<QString>("subject");253 QTest::addColumn<QString>("subject");
254 QTest::addColumn<int>("informationType");
248 QTest::addColumn<QStringList>("participants");255 QTest::addColumn<QStringList>("participants");
249256
250 QTest::newRow("unread message") << "testAccountId" << "testThreadId" << "testEventId"257 QTest::newRow("unread message") << "testAccountId" << "testThreadId" << "testEventId"
251 << "testSenderId" << QDateTime::currentDateTime().addDays(-10)258 << "testSenderId" << QDateTime::currentDateTime().addDays(-10)
252 << true << "One Test Message" << (int)History::MessageTypeText259 << true << "One Test Message" << (int)History::MessageTypeText
253 << 0 << QDateTime::currentDateTime().addDays(-5) << "Test Subject"260 << 0 << QDateTime::currentDateTime().addDays(-5) << "Test Subject" << (int) History::InformationTypeNone
254 << (QStringList() << "testParticipant");261 << (QStringList() << "testParticipant");
255 QTest::newRow("read message") << "testAccountId2" << "testThreadId2" << "testEventId2"262 QTest::newRow("read message") << "testAccountId2" << "testThreadId2" << "testEventId2"
256 << "testSenderId2" << QDateTime::currentDateTime().addDays(-10)263 << "testSenderId2" << QDateTime::currentDateTime().addDays(-10)
257 << false << "One Test Message" << (int)History::MessageTypeText264 << false << "One Test Message" << (int)History::MessageTypeText
258 << 0 << QDateTime::currentDateTime().addDays(-5) << "Test Subject 2"265 << 0 << QDateTime::currentDateTime().addDays(-5) << "Test Subject 2" << (int) History::InformationTypeNone
259 << (QStringList() << "testParticipant2");266 << (QStringList() << "testParticipant2");
260 QTest::newRow("message status") << "testAccountId" << "testThreadId" << "testEventId"267 QTest::newRow("message status") << "testAccountId" << "testThreadId" << "testEventId"
261 << "testSenderId" << QDateTime::currentDateTime().addDays(-10)268 << "testSenderId" << QDateTime::currentDateTime().addDays(-10)
262 << true << "One Test Message" << (int)History::MessageTypeText269 << true << "One Test Message" << (int)History::MessageTypeText
263 << (int)History::MessageStatusAccepted270 << (int)History::MessageStatusAccepted
264 << QDateTime::currentDateTime().addDays(-5) << "Test Subject 3"271 << QDateTime::currentDateTime().addDays(-5) << "Test Subject 3" << (int) History::InformationTypeNone
265 << (QStringList() << "testParticipant");272 << (QStringList() << "testParticipant");
266 QTest::newRow("multi party message") << "testAccountId" << "testThreadId" << "testEventId"273 QTest::newRow("multi party message") << "testAccountId" << "testThreadId" << "testEventId"
267 << "testSenderId" << QDateTime::currentDateTime().addDays(-10)274 << "testSenderId" << QDateTime::currentDateTime().addDays(-10)
268 << true << "One Test Message" << (int)History::MessageTypeMultiPart275 << true << "One Test Message" << (int)History::MessageTypeMultiPart
269 << 0 << QDateTime::currentDateTime().addDays(-5) << QString()276 << 0 << QDateTime::currentDateTime().addDays(-5) << QString() << (int) History::InformationTypeNone
270 << (QStringList() << "testParticipant");277 << (QStringList() << "testParticipant");
271 QTest::newRow("multiple participants") << "testAccountId2" << "testThreadId2" << "testEventId2"278 QTest::newRow("multiple participants") << "testAccountId2" << "testThreadId2" << "testEventId2"
272 << "testSenderId2" << QDateTime::currentDateTime().addDays(-7)279 << "testSenderId2" << QDateTime::currentDateTime().addDays(-7)
273 << true << "One Test Message 2" << (int)History::MessageTypeText280 << true << "One Test Message 2" << (int)History::MessageTypeText
274 << 0 << QDateTime::currentDateTime().addDays(-4) << QString()281 << 0 << QDateTime::currentDateTime().addDays(-4) << QString() << (int) History::InformationTypeNone
275 << (QStringList() << "one" << "two" << "three" << "four");282 << (QStringList() << "one" << "two" << "three" << "four");
276}283}
277284
@@ -288,10 +295,11 @@
288 QFETCH(int, messageStatus);295 QFETCH(int, messageStatus);
289 QFETCH(QDateTime, readTimestamp);296 QFETCH(QDateTime, readTimestamp);
290 QFETCH(QString, subject);297 QFETCH(QString, subject);
298 QFETCH(int, informationType);
291 QFETCH(QStringList, participants);299 QFETCH(QStringList, participants);
292 History::TextEvent event(accountId, threadId, eventId, senderId, timestamp, newEvent,300 History::TextEvent event(accountId, threadId, eventId, senderId, timestamp, newEvent,
293 message, (History::MessageType)messageType, (History::MessageStatus)messageStatus,301 message, (History::MessageType)messageType, (History::MessageStatus)messageStatus,
294 readTimestamp, subject, History::TextEventAttachments(),302 readTimestamp, subject, History::InformationTypeNone, History::TextEventAttachments(),
295 participantsFromIdentifiers(accountId, participants));303 participantsFromIdentifiers(accountId, participants));
296304
297 QVariantMap properties = event.properties();305 QVariantMap properties = event.properties();
@@ -306,6 +314,7 @@
306 QCOMPARE(properties[History::FieldMessageStatus].toInt(), messageStatus);314 QCOMPARE(properties[History::FieldMessageStatus].toInt(), messageStatus);
307 QCOMPARE(properties[History::FieldReadTimestamp].toString(), readTimestamp.toString("yyyy-MM-ddTHH:mm:ss.zzz"));315 QCOMPARE(properties[History::FieldReadTimestamp].toString(), readTimestamp.toString("yyyy-MM-ddTHH:mm:ss.zzz"));
308 QCOMPARE(properties[History::FieldSubject].toString(), subject);316 QCOMPARE(properties[History::FieldSubject].toString(), subject);
317 QCOMPARE(properties[History::FieldInformationType].toInt(), informationType);
309 QCOMPARE(History::Participants::fromVariantList(properties[History::FieldParticipants].toList()).identifiers(), participants);318 QCOMPARE(History::Participants::fromVariantList(properties[History::FieldParticipants].toList()).identifiers(), participants);
310}319}
311320
312321
=== modified file 'tests/libhistoryservice/ThreadTest.cpp'
--- tests/libhistoryservice/ThreadTest.cpp 2015-10-02 16:48:19 +0000
+++ tests/libhistoryservice/ThreadTest.cpp 2016-11-24 12:52:24 +0000
@@ -102,11 +102,12 @@
102 break;102 break;
103 }103 }
104104
105 History::Thread threadItem(accountId, threadId, type, participantsFromIdentifiers(accountId, participants), event, count, unreadCount);105 History::Thread threadItem(accountId, threadId, type, participantsFromIdentifiers(accountId, participants), event.timestamp(), event, count, unreadCount);
106 QCOMPARE(threadItem.accountId(), accountId);106 QCOMPARE(threadItem.accountId(), accountId);
107 QCOMPARE(threadItem.threadId(), threadId);107 QCOMPARE(threadItem.threadId(), threadId);
108 QCOMPARE(threadItem.type(), type);108 QCOMPARE(threadItem.type(), type);
109 QCOMPARE(threadItem.participants().identifiers(), participants);109 QCOMPARE(threadItem.participants().identifiers(), participants);
110 QCOMPARE(threadItem.timestamp(), event.timestamp());
110 QCOMPARE(threadItem.lastEvent(), event);111 QCOMPARE(threadItem.lastEvent(), event);
111 QCOMPARE(threadItem.count(), count);112 QCOMPARE(threadItem.count(), count);
112 QCOMPARE(threadItem.unreadCount(), unreadCount);113 QCOMPARE(threadItem.unreadCount(), unreadCount);
@@ -172,6 +173,7 @@
172 QCOMPARE(thread.threadId(), threadId);173 QCOMPARE(thread.threadId(), threadId);
173 QCOMPARE(thread.type(), type);174 QCOMPARE(thread.type(), type);
174 QCOMPARE(thread.participants().identifiers(), participants);175 QCOMPARE(thread.participants().identifiers(), participants);
176 QCOMPARE(thread.timestamp(), event.timestamp());
175 QCOMPARE(thread.count(), count);177 QCOMPARE(thread.count(), count);
176 QCOMPARE(thread.unreadCount(), unreadCount);178 QCOMPARE(thread.unreadCount(), unreadCount);
177 QVERIFY(thread.lastEvent() == event);179 QVERIFY(thread.lastEvent() == event);
@@ -229,7 +231,7 @@
229 break;231 break;
230 }232 }
231233
232 History::Thread threadItem(accountId, threadId, type, participantsFromIdentifiers(accountId, participants), event, count, unreadCount);234 History::Thread threadItem(accountId, threadId, type, participantsFromIdentifiers(accountId, participants), event.timestamp(), event, count, unreadCount);
233 QVariantMap properties = threadItem.properties();235 QVariantMap properties = threadItem.properties();
234 QCOMPARE(properties[History::FieldAccountId].toString(), accountId);236 QCOMPARE(properties[History::FieldAccountId].toString(), accountId);
235 QCOMPARE(properties[History::FieldThreadId].toString(), threadId);237 QCOMPARE(properties[History::FieldThreadId].toString(), threadId);
236238
=== modified file 'tests/plugins/sqlite/SqlitePluginTest.cpp'
--- tests/plugins/sqlite/SqlitePluginTest.cpp 2015-09-23 22:27:38 +0000
+++ tests/plugins/sqlite/SqlitePluginTest.cpp 2016-11-24 12:52:24 +0000
@@ -249,36 +249,35 @@
249{249{
250 // clear the database250 // clear the database
251 SQLiteDatabase::instance()->reopen();251 SQLiteDatabase::instance()->reopen();
252 QSqlQuery query(SQLiteDatabase::instance()->database());
252253
253 QVERIFY(mPlugin->beginBatchOperation());254 QVERIFY(mPlugin->beginBatchOperation());
254 mPlugin->createThreadForParticipants("accountOne", History::EventTypeText, QStringList() << "participantOne");255 QVERIFY(query.exec("UPDATE schema_version SET version=123"));
255 mPlugin->createThreadForParticipants("accountTwo", History::EventTypeText, QStringList() << "participantTwo");
256 mPlugin->createThreadForParticipants("accountThree", History::EventTypeText, QStringList() << "participantThree");
257 QVERIFY(mPlugin->endBatchOperation());256 QVERIFY(mPlugin->endBatchOperation());
258257
259 // check that the data was actually written258 // check that the data was actually written
260 QSqlQuery query(SQLiteDatabase::instance()->database());259 QVERIFY(query.exec("SELECT version FROM schema_version"));
261 QVERIFY(query.exec("SELECT count(*) FROM threads"));
262 QVERIFY(query.next());260 QVERIFY(query.next());
263 QCOMPARE(query.value(0).toInt(), 3);261 QCOMPARE(query.value(0).toInt(), 123);
264}262}
265263
266void SqlitePluginTest::testRollback()264void SqlitePluginTest::testRollback()
267{265{
268 // clear the database266 // clear the database
269 SQLiteDatabase::instance()->reopen();267 SQLiteDatabase::instance()->reopen();
268 QSqlQuery query(SQLiteDatabase::instance()->database());
269 QVERIFY(query.exec("SELECT version FROM schema_version"));
270 QVERIFY(query.next());
271 int version = query.value(0).toInt();
270272
271 QVERIFY(mPlugin->beginBatchOperation());273 QVERIFY(mPlugin->beginBatchOperation());
272 mPlugin->createThreadForParticipants("accountOne", History::EventTypeText, QStringList() << "participantOne");274 QVERIFY(query.exec("UPDATE schema_version SET version=255"));
273 mPlugin->createThreadForParticipants("accountTwo", History::EventTypeText, QStringList() << "participantTwo");
274 mPlugin->createThreadForParticipants("accountThree", History::EventTypeText, QStringList() << "participantThree");
275 QVERIFY(mPlugin->rollbackBatchOperation());275 QVERIFY(mPlugin->rollbackBatchOperation());
276276
277 // check that the steps were reverted277 // check that the steps were reverted
278 QSqlQuery query(SQLiteDatabase::instance()->database());278 QVERIFY(query.exec("SELECT version FROM schema_version"));
279 QVERIFY(query.exec("SELECT count(*) FROM threads"));
280 QVERIFY(query.next());279 QVERIFY(query.next());
281 QCOMPARE(query.value(0).toInt(), 0);280 QCOMPARE(query.value(0).toInt(), version);
282}281}
283282
284void SqlitePluginTest::testQueryThreads()283void SqlitePluginTest::testQueryThreads()
@@ -319,7 +318,7 @@
319 QTest::newRow("text event with attachments") << History::TextEvent("mmsAccountId", "mmsSender", "mmsEventId", "mmsSender",318 QTest::newRow("text event with attachments") << History::TextEvent("mmsAccountId", "mmsSender", "mmsEventId", "mmsSender",
320 QDateTime::currentDateTime(), false, "Hello with attachments",319 QDateTime::currentDateTime(), false, "Hello with attachments",
321 History::MessageTypeMultiPart, History::MessageStatusDelivered,320 History::MessageTypeMultiPart, History::MessageStatusDelivered,
322 QDateTime::currentDateTime(), "The Subject", attachments).properties();321 QDateTime::currentDateTime(), "The Subject", History::InformationTypeNone, attachments).properties();
323}322}
324323
325void SqlitePluginTest::testWriteTextEvent()324void SqlitePluginTest::testWriteTextEvent()
@@ -406,7 +405,7 @@
406 thread[History::FieldEventId].toString(), "theAttachmentId", "text/plain", "/file/path");405 thread[History::FieldEventId].toString(), "theAttachmentId", "text/plain", "/file/path");
407 History::TextEvent textEvent(thread[History::FieldAccountId].toString(), thread[History::FieldThreadId].toString(), "theEventId",406 History::TextEvent textEvent(thread[History::FieldAccountId].toString(), thread[History::FieldThreadId].toString(), "theEventId",
408 "theParticipant", QDateTime::currentDateTime(), true, "Hi there!", History::MessageTypeMultiPart,407 "theParticipant", QDateTime::currentDateTime(), true, "Hi there!", History::MessageTypeMultiPart,
409 History::MessageStatusPending, QDateTime::currentDateTime(), "theSubject",408 History::MessageStatusPending, QDateTime::currentDateTime(), "theSubject", History::InformationTypeNone,
410 History::TextEventAttachments() << attachment);409 History::TextEventAttachments() << attachment);
411 QCOMPARE(mPlugin->writeTextEvent(textEvent.properties()), History::EventWriteCreated);410 QCOMPARE(mPlugin->writeTextEvent(textEvent.properties()), History::EventWriteCreated);
412411
@@ -606,7 +605,7 @@
606 QString("textEventId%1").arg(QString::number(i)), "textParticipant",605 QString("textEventId%1").arg(QString::number(i)), "textParticipant",
607 QDateTime::currentDateTime(), true, "Hello World!", History::MessageTypeMultiPart,606 QDateTime::currentDateTime(), true, "Hello World!", History::MessageTypeMultiPart,
608 History::MessageStatusPending, QDateTime::currentDateTime(),607 History::MessageStatusPending, QDateTime::currentDateTime(),
609 "theSubject", History::TextEventAttachments() << attachment);608 "theSubject", History::InformationTypeNone, History::TextEventAttachments() << attachment);
610 QCOMPARE(mPlugin->writeTextEvent(textEvent.properties()), History::EventWriteCreated);609 QCOMPARE(mPlugin->writeTextEvent(textEvent.properties()), History::EventWriteCreated);
611 }610 }
612611
@@ -659,7 +658,7 @@
659 QTest::newRow("text event with attachments") << History::TextEvent("mmsAccountId", "mmsSender", "mmsEventId", "mmsSender",658 QTest::newRow("text event with attachments") << History::TextEvent("mmsAccountId", "mmsSender", "mmsEventId", "mmsSender",
660 QDateTime::currentDateTime(), false, "Hello with attachments",659 QDateTime::currentDateTime(), false, "Hello with attachments",
661 History::MessageTypeMultiPart, History::MessageStatusDelivered,660 History::MessageTypeMultiPart, History::MessageStatusDelivered,
662 QDateTime::currentDateTime(), "The Subject", attachments).properties();661 QDateTime::currentDateTime(), "The Subject", History::InformationTypeNone, attachments).properties();
663 QTest::newRow("missed call") << History::VoiceEvent("theAccountId", "theSenderId", "theEventId", "theSenderId",662 QTest::newRow("missed call") << History::VoiceEvent("theAccountId", "theSenderId", "theEventId", "theSenderId",
664 QDateTime::currentDateTime(), true, true).properties();663 QDateTime::currentDateTime(), true, true).properties();
665 QTest::newRow("incoming call") << History::VoiceEvent("otherAccountId", "otherSenderId", "otherEventId", "otherSenderId",664 QTest::newRow("incoming call") << History::VoiceEvent("otherAccountId", "otherSenderId", "otherEventId", "otherSenderId",

Subscribers

People subscribed via source and target branches

to all changes: