Merge lp:history-service/staging into lp:history-service
- staging
- Merge into trunk
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 |
Related bugs: |
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.
- 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( )
Tiago Salem Herrmann (tiagosh) wrote : | # |
just two more fixes.
Gustavo Pichorim Boiko (boiko) wrote : | # |
Debug prints removed, question answered and loops changed on a separate MR.
- 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.
Tiago Salem Herrmann (tiagosh) wrote : | # |
Looks good to me. thank you!
- 253. By Gustavo Pichorim Boiko
-
Simplify the filtering of participants.
Preview Diff
1 | === modified file 'CMakeLists.txt' |
2 | --- CMakeLists.txt 2015-11-03 14:25:26 +0000 |
3 | +++ CMakeLists.txt 2016-11-24 12:52:24 +0000 |
4 | @@ -17,6 +17,7 @@ |
5 | find_package(Qt5Qml) |
6 | find_package(Qt5Quick) |
7 | find_package(Qt5Test) |
8 | +find_package(Qt5Network) |
9 | find_package(LibPhoneNumber REQUIRED) |
10 | |
11 | include(qt5) |
12 | |
13 | === modified file 'Ubuntu/History/historyeventmodel.cpp' |
14 | --- Ubuntu/History/historyeventmodel.cpp 2015-10-06 12:59:03 +0000 |
15 | +++ Ubuntu/History/historyeventmodel.cpp 2016-11-24 12:52:24 +0000 |
16 | @@ -45,9 +45,11 @@ |
17 | mRoles[TextMessageAttachmentsRole] = "textMessageAttachments"; |
18 | mRoles[TextReadTimestampRole] = "textReadTimestamp"; |
19 | mRoles[TextReadSubjectRole] = "textSubject"; |
20 | + mRoles[TextInformationTypeRole] = "textInformationType"; |
21 | mRoles[CallMissedRole] = "callMissed"; |
22 | mRoles[CallDurationRole] = "callDuration"; |
23 | mRoles[RemoteParticipantRole] = "remoteParticipant"; |
24 | + mRoles[SubjectAsAliasRole] = "subjectAsAlias"; |
25 | } |
26 | |
27 | int HistoryEventModel::rowCount(const QModelIndex &parent) const |
28 | @@ -135,6 +137,11 @@ |
29 | result = textEvent.subject(); |
30 | } |
31 | break; |
32 | + case TextInformationTypeRole: |
33 | + if (!textEvent.isNull()) { |
34 | + result = (int)textEvent.informationType(); |
35 | + } |
36 | + break; |
37 | case TextMessageAttachmentsRole: |
38 | if (!textEvent.isNull()) { |
39 | if (mAttachmentCache.contains(textEvent)) { |
40 | @@ -164,6 +171,16 @@ |
41 | result = voiceEvent.remoteParticipant(); |
42 | } |
43 | break; |
44 | + case SubjectAsAliasRole: |
45 | + if (!textEvent.isNull()) { |
46 | + QVariantMap contactInfo = History::ContactMatcher::instance()->contactInfo(event.accountId(), textEvent.subject()); |
47 | + QString returnValue = contactInfo[History::FieldAlias].toString(); |
48 | + if (returnValue.isEmpty()) { |
49 | + returnValue = contactInfo[History::FieldIdentifier].toString(); |
50 | + } |
51 | + return returnValue; |
52 | + } |
53 | + break; |
54 | } |
55 | |
56 | return result; |
57 | |
58 | === modified file 'Ubuntu/History/historyeventmodel.h' |
59 | --- Ubuntu/History/historyeventmodel.h 2015-10-01 18:25:36 +0000 |
60 | +++ Ubuntu/History/historyeventmodel.h 2016-11-24 12:52:24 +0000 |
61 | @@ -44,10 +44,12 @@ |
62 | TextMessageStatusRole, |
63 | TextReadTimestampRole, |
64 | TextReadSubjectRole, |
65 | + TextInformationTypeRole, |
66 | TextMessageAttachmentsRole, |
67 | CallMissedRole, |
68 | CallDurationRole, |
69 | RemoteParticipantRole, |
70 | + SubjectAsAliasRole, |
71 | LastEventRole |
72 | }; |
73 | |
74 | |
75 | === modified file 'Ubuntu/History/historymodel.cpp' |
76 | --- Ubuntu/History/historymodel.cpp 2015-10-08 21:52:59 +0000 |
77 | +++ Ubuntu/History/historymodel.cpp 2016-11-24 12:52:24 +0000 |
78 | @@ -1,5 +1,5 @@ |
79 | /* |
80 | - * Copyright (C) 2013-2014 Canonical, Ltd. |
81 | + * Copyright (C) 2013-2016 Canonical, Ltd. |
82 | * |
83 | * Authors: |
84 | * Gustavo Pichorim Boiko <gustavo.boiko@canonical.com> |
85 | @@ -40,7 +40,10 @@ |
86 | mRoles[AccountIdRole] = "accountId"; |
87 | mRoles[ThreadIdRole] = "threadId"; |
88 | mRoles[ParticipantsRole] = "participants"; |
89 | + mRoles[ParticipantsRemotePendingRole] = "remotePendingParticipants"; |
90 | + mRoles[ParticipantsLocalPendingRole] = "localPendingParticipants"; |
91 | mRoles[TypeRole] = "type"; |
92 | + mRoles[TimestampRole] = "timestamp"; |
93 | mRoles[PropertiesRole] = "properties"; |
94 | |
95 | connect(this, SIGNAL(rowsInserted(QModelIndex,int,int)), this, SIGNAL(countChanged())); |
96 | @@ -89,16 +92,79 @@ |
97 | case TypeRole: |
98 | result = properties[History::FieldType]; |
99 | break; |
100 | - case ParticipantsRole: |
101 | - if (mMatchContacts) { |
102 | - result = History::ContactMatcher::instance()->contactInfo(properties[History::FieldAccountId].toString(), |
103 | - History::Participants::fromVariantList(properties[History::FieldParticipants].toList()).identifiers()); |
104 | - } else { |
105 | - //FIXME: handle contact changes |
106 | - result = properties[History::FieldParticipants]; |
107 | - } |
108 | - break; |
109 | - } |
110 | + case ParticipantsRole: { |
111 | + History::Participants participants = History::Participants::fromVariantList(properties[History::FieldParticipants].toList()) |
112 | + .filterByState(History::ParticipantStateRegular); |
113 | + if (mMatchContacts) { |
114 | + QVariantList finalParticipantsList; |
115 | + QVariantList participantsInfo = History::ContactMatcher::instance()->contactInfo(properties[History::FieldAccountId].toString(), |
116 | + participants.identifiers()); |
117 | + for (int i = 0; i < participantsInfo.count(); ++i) { |
118 | + QVariantMap newMap = participantsInfo[i].toMap(); |
119 | + History::Participant participant = participants[i]; |
120 | + newMap[History::FieldParticipantState] = participant.state(); |
121 | + newMap[History::FieldParticipantRoles] = participant.roles(); |
122 | + finalParticipantsList << newMap; |
123 | + } |
124 | + result = finalParticipantsList; |
125 | + } else { |
126 | + //FIXME: handle contact changes |
127 | + result = participants.identifiers(); |
128 | + } |
129 | + break; |
130 | + } |
131 | + case ParticipantsRemotePendingRole: { |
132 | + History::Participants participants = History::Participants::fromVariantList(properties[History::FieldParticipants].toList()) |
133 | + .filterByState(History::ParticipantStateRemotePending); |
134 | + if (mMatchContacts) { |
135 | + QVariantList finalParticipantsList; |
136 | + QVariantList participantsInfo = History::ContactMatcher::instance()->contactInfo(properties[History::FieldAccountId].toString(), |
137 | + participants.identifiers()); |
138 | + int count = 0; |
139 | + Q_FOREACH(const QVariant &participantInfo, participantsInfo) { |
140 | + QVariantMap newMap = participantInfo.toMap(); |
141 | + newMap[History::FieldParticipantState] = participants.at(count).state(); |
142 | + newMap[History::FieldParticipantRoles] = participants.at(count++).roles(); |
143 | + finalParticipantsList << newMap; |
144 | + } |
145 | + result = finalParticipantsList; |
146 | + } else { |
147 | + //FIXME: handle contact changes |
148 | + result = participants.identifiers(); |
149 | + } |
150 | + |
151 | + break; |
152 | + } |
153 | + case ParticipantsLocalPendingRole: { |
154 | + History::Participants participants = History::Participants::fromVariantList(properties[History::FieldParticipants].toList()) |
155 | + .filterByState(History::ParticipantStateLocalPending); |
156 | + if (mMatchContacts) { |
157 | + QVariantList finalParticipantsList; |
158 | + QVariantList participantsInfo = History::ContactMatcher::instance()->contactInfo(properties[History::FieldAccountId].toString(), |
159 | + participants.identifiers()); |
160 | + int count = 0; |
161 | + Q_FOREACH(const QVariant &participantInfo, participantsInfo) { |
162 | + QVariantMap newMap = participantInfo.toMap(); |
163 | + newMap[History::FieldParticipantState] = participants.at(count).state(); |
164 | + newMap[History::FieldParticipantRoles] = participants.at(count++).roles(); |
165 | + finalParticipantsList << newMap; |
166 | + } |
167 | + result = finalParticipantsList; |
168 | + } else { |
169 | + //FIXME: handle contact changes |
170 | + result = participants.identifiers(); |
171 | + } |
172 | + |
173 | + break; |
174 | + } |
175 | + case ParticipantIdsRole: |
176 | + result = History::Participants::fromVariantList(properties[History::FieldParticipants].toList()).identifiers(); |
177 | + break; |
178 | + case TimestampRole: |
179 | + result = QDateTime::fromString(properties[History::FieldTimestamp].toString(), Qt::ISODate); |
180 | + break; |
181 | + } |
182 | + |
183 | return result; |
184 | } |
185 | |
186 | @@ -187,17 +253,66 @@ |
187 | } |
188 | } |
189 | |
190 | +QVariantMap HistoryModel::threadForProperties(const QString &accountId, int eventType, const QVariantMap &properties, int matchFlags, bool create) |
191 | +{ |
192 | + QVariantMap newProperties = properties; |
193 | + if (properties.isEmpty()) { |
194 | + return QVariantMap(); |
195 | + } |
196 | + |
197 | + if (newProperties.contains(History::FieldParticipantIds)) { |
198 | + newProperties[History::FieldParticipantIds] = newProperties[History::FieldParticipantIds].toStringList(); |
199 | + } |
200 | + |
201 | + History::Thread thread = History::Manager::instance()->threadForProperties(accountId, |
202 | + (History::EventType)eventType, |
203 | + newProperties, |
204 | + (History::MatchFlags)matchFlags, |
205 | + create); |
206 | + if (!thread.isNull()) { |
207 | + return thread.properties(); |
208 | + } |
209 | + |
210 | + return QVariantMap(); |
211 | +} |
212 | + |
213 | +QString HistoryModel::threadIdForProperties(const QString &accountId, int eventType, const QVariantMap &properties, int matchFlags, bool create) |
214 | +{ |
215 | + QVariantMap newProperties = properties; |
216 | + if (properties.isEmpty()) { |
217 | + return QString::null; |
218 | + } |
219 | + |
220 | + if (newProperties.contains(History::FieldParticipantIds)) { |
221 | + newProperties[History::FieldParticipantIds] = newProperties[History::FieldParticipantIds].toStringList(); |
222 | + } |
223 | + |
224 | + History::Thread thread = History::Manager::instance()->threadForProperties(accountId, |
225 | + (History::EventType)eventType, |
226 | + newProperties, |
227 | + (History::MatchFlags)matchFlags, |
228 | + create); |
229 | + if (!thread.isNull()) { |
230 | + return thread.threadId(); |
231 | + } |
232 | + |
233 | + return QString::null; |
234 | +} |
235 | + |
236 | QVariantMap HistoryModel::threadForParticipants(const QString &accountId, int eventType, const QStringList &participants, int matchFlags, bool create) |
237 | { |
238 | if (participants.isEmpty()) { |
239 | return QVariantMap(); |
240 | } |
241 | |
242 | - History::Thread thread = History::Manager::instance()->threadForParticipants(accountId, |
243 | - (History::EventType)eventType, |
244 | - participants, |
245 | - (History::MatchFlags)matchFlags, |
246 | - create); |
247 | + QVariantMap properties; |
248 | + properties[History::FieldParticipantIds] = participants; |
249 | + |
250 | + History::Thread thread = History::Manager::instance()->threadForProperties(accountId, |
251 | + (History::EventType)eventType, |
252 | + properties, |
253 | + (History::MatchFlags)matchFlags, |
254 | + create); |
255 | if (!thread.isNull()) { |
256 | return thread.properties(); |
257 | } |
258 | @@ -211,11 +326,14 @@ |
259 | return QString::null; |
260 | } |
261 | |
262 | - History::Thread thread = History::Manager::instance()->threadForParticipants(accountId, |
263 | - (History::EventType)eventType, |
264 | - participants, |
265 | - (History::MatchFlags)matchFlags, |
266 | - create); |
267 | + QVariantMap properties; |
268 | + properties[History::FieldParticipantIds] = participants; |
269 | + |
270 | + History::Thread thread = History::Manager::instance()->threadForProperties(accountId, |
271 | + (History::EventType)eventType, |
272 | + properties, |
273 | + (History::MatchFlags)matchFlags, |
274 | + create); |
275 | if (!thread.isNull()) { |
276 | return thread.threadId(); |
277 | } |
278 | @@ -223,7 +341,7 @@ |
279 | return QString::null; |
280 | } |
281 | |
282 | -bool HistoryModel::writeTextInformationEvent(const QString &accountId, const QString &threadId, const QStringList &participants, const QString &message) |
283 | +bool HistoryModel::writeTextInformationEvent(const QString &accountId, const QString &threadId, const QStringList &participants, const QString &message, int informationType, const QString &subject) |
284 | { |
285 | if (participants.isEmpty() || threadId.isEmpty() || accountId.isEmpty()) { |
286 | return false; |
287 | @@ -232,7 +350,7 @@ |
288 | History::TextEvent historyEvent = History::TextEvent(accountId, |
289 | threadId, |
290 | QString(QCryptographicHash::hash(QByteArray( |
291 | - QDateTime::currentDateTime().toString().toLatin1()), |
292 | + QDateTime::currentDateTimeUtc().toString("yyyyMMddhhmmsszzz").toLatin1()), |
293 | QCryptographicHash::Md5).toHex()), |
294 | "self", |
295 | QDateTime::currentDateTime(), |
296 | @@ -240,7 +358,9 @@ |
297 | message, |
298 | History::MessageTypeInformation, |
299 | History::MessageStatusUnknown, |
300 | - QDateTime::currentDateTime()); |
301 | + QDateTime::currentDateTime(), |
302 | + subject, |
303 | + (History::InformationType)informationType); |
304 | History::Events events; |
305 | events << historyEvent; |
306 | return History::Manager::instance()->writeEvents(events); |
307 | |
308 | === modified file 'Ubuntu/History/historymodel.h' |
309 | --- Ubuntu/History/historymodel.h 2015-10-08 21:47:42 +0000 |
310 | +++ Ubuntu/History/historymodel.h 2016-11-24 12:52:24 +0000 |
311 | @@ -1,5 +1,5 @@ |
312 | /* |
313 | - * Copyright (C) 2013-2014 Canonical, Ltd. |
314 | + * Copyright (C) 2013-2016 Canonical, Ltd. |
315 | * |
316 | * Authors: |
317 | * Gustavo Pichorim Boiko <gustavo.boiko@canonical.com> |
318 | @@ -39,14 +39,22 @@ |
319 | Q_PROPERTY(EventType type READ type WRITE setType NOTIFY typeChanged) |
320 | Q_PROPERTY(bool matchContacts READ matchContacts WRITE setMatchContacts NOTIFY matchContactsChanged) |
321 | Q_PROPERTY(bool canFetchMore READ canFetchMore NOTIFY canFetchMoreChanged) |
322 | + Q_ENUMS(ChatType) |
323 | Q_ENUMS(EventType) |
324 | Q_ENUMS(MessageType) |
325 | Q_ENUMS(MatchFlag) |
326 | Q_ENUMS(MessageStatus) |
327 | Q_ENUMS(AttachmentFlag) |
328 | Q_ENUMS(Role) |
329 | + Q_ENUMS(InformationType) |
330 | |
331 | public: |
332 | + enum ChatType { |
333 | + ChatTypeNone = History::ChatTypeNone, |
334 | + ChatTypeContact = History::ChatTypeContact, |
335 | + ChatTypeRoom = History::ChatTypeRoom |
336 | + }; |
337 | + |
338 | enum EventType { |
339 | EventTypeText = History::EventTypeText, |
340 | EventTypeVoice = History::EventTypeVoice |
341 | @@ -83,12 +91,36 @@ |
342 | AttachmentPending = History::AttachmentPending, |
343 | AttachmentError = History::AttachmentError |
344 | }; |
345 | + |
346 | + enum InformationType |
347 | + { |
348 | + InformationTypeNone = History::InformationTypeNone, |
349 | + InformationTypeSimChange = History::InformationTypeSimChange, |
350 | + InformationTypeText = History::InformationTypeText, |
351 | + InformationTypeSelfJoined = History::InformationTypeSelfJoined, |
352 | + InformationTypeJoined = History::InformationTypeJoined, |
353 | + InformationTypeTitleChanged = History::InformationTypeTitleChanged, |
354 | + InformationTypeInvitationSent = History::InformationTypeInvitationSent, |
355 | + InformationTypeLeaving = History::InformationTypeLeaving, |
356 | + InformationTypeSelfLeaving = History::InformationTypeSelfLeaving, |
357 | + InformationTypeAdminGranted = History::InformationTypeAdminGranted, |
358 | + InformationTypeAdminRemoved = History::InformationTypeAdminRemoved, |
359 | + InformationTypeSelfAdminGranted = History::InformationTypeSelfAdminGranted, |
360 | + InformationTypeSelfAdminRemoved = History::InformationTypeSelfAdminRemoved, |
361 | + InformationTypeSelfKicked = History::InformationTypeSelfKicked, |
362 | + InformationTypeGroupGone = History::InformationTypeGroupGone |
363 | + }; |
364 | + |
365 | |
366 | enum Role { |
367 | AccountIdRole = Qt::UserRole, |
368 | ThreadIdRole, |
369 | ParticipantsRole, |
370 | + ParticipantsLocalPendingRole, |
371 | + ParticipantsRemotePendingRole, |
372 | + ParticipantIdsRole, |
373 | TypeRole, |
374 | + TimestampRole, |
375 | PropertiesRole, |
376 | LastRole |
377 | }; |
378 | @@ -112,6 +144,18 @@ |
379 | bool matchContacts() const; |
380 | void setMatchContacts(bool value); |
381 | |
382 | + Q_INVOKABLE QVariantMap threadForProperties(const QString &accountId, |
383 | + int eventType, |
384 | + const QVariantMap &properties, |
385 | + int matchFlags = (int)History::MatchCaseSensitive, |
386 | + bool create = false); |
387 | + |
388 | + Q_INVOKABLE QString threadIdForProperties(const QString &accountId, |
389 | + int eventType, |
390 | + const QVariantMap &properties, |
391 | + int matchFlags = (int)History::MatchCaseSensitive, |
392 | + bool create = false); |
393 | + |
394 | Q_INVOKABLE QVariantMap threadForParticipants(const QString &accountId, |
395 | int eventType, |
396 | const QStringList &participants, |
397 | @@ -125,7 +169,9 @@ |
398 | Q_INVOKABLE bool writeTextInformationEvent(const QString &accountId, |
399 | const QString &threadId, |
400 | const QStringList &participants, |
401 | - const QString &message); |
402 | + const QString &message, |
403 | + int informationType = (int)History::InformationTypeNone, |
404 | + const QString &subject = QString()); |
405 | |
406 | Q_INVOKABLE virtual QVariant get(int row) const; |
407 | |
408 | |
409 | === modified file 'Ubuntu/History/historythreadmodel.cpp' |
410 | --- Ubuntu/History/historythreadmodel.cpp 2015-10-08 19:35:40 +0000 |
411 | +++ Ubuntu/History/historythreadmodel.cpp 2016-11-24 12:52:24 +0000 |
412 | @@ -1,5 +1,5 @@ |
413 | /* |
414 | - * Copyright (C) 2013-2015 Canonical, Ltd. |
415 | + * Copyright (C) 2013-2016 Canonical, Ltd. |
416 | * |
417 | * Authors: |
418 | * Gustavo Pichorim Boiko <gustavo.boiko@canonical.com> |
419 | @@ -38,6 +38,8 @@ |
420 | mRoles = HistoryModel::roleNames(); |
421 | mRoles[CountRole] = "count"; |
422 | mRoles[UnreadCountRole] = "unreadCount"; |
423 | + mRoles[ChatType] = "chatType"; |
424 | + mRoles[ChatRoomInfo] = "chatRoomInfo"; |
425 | |
426 | // roles related to the thread´s last event |
427 | mRoles[LastEventIdRole] = "eventId"; |
428 | @@ -104,6 +106,12 @@ |
429 | case UnreadCountRole: |
430 | result = thread.unreadCount(); |
431 | break; |
432 | + case ChatType: |
433 | + result = thread.chatType(); |
434 | + break; |
435 | + case ChatRoomInfo: |
436 | + result = thread.chatRoomInfo(); |
437 | + break; |
438 | case PropertiesRole: |
439 | result = thread.properties(); |
440 | break; |
441 | |
442 | === modified file 'Ubuntu/History/historythreadmodel.h' |
443 | --- Ubuntu/History/historythreadmodel.h 2015-09-29 14:28:17 +0000 |
444 | +++ Ubuntu/History/historythreadmodel.h 2016-11-24 12:52:24 +0000 |
445 | @@ -1,5 +1,5 @@ |
446 | /* |
447 | - * Copyright (C) 2013-2015 Canonical, Ltd. |
448 | + * Copyright (C) 2013-2016 Canonical, Ltd. |
449 | * |
450 | * Authors: |
451 | * Gustavo Pichorim Boiko <gustavo.boiko@canonical.com> |
452 | @@ -40,6 +40,8 @@ |
453 | enum ThreadRole { |
454 | CountRole = HistoryModel::LastRole, |
455 | UnreadCountRole, |
456 | + ChatType, |
457 | + ChatRoomInfo, |
458 | LastEventIdRole, |
459 | LastEventSenderIdRole, |
460 | LastEventTimestampRole, |
461 | |
462 | === modified file 'daemon/CMakeLists.txt' |
463 | --- daemon/CMakeLists.txt 2015-09-30 13:17:52 +0000 |
464 | +++ daemon/CMakeLists.txt 2016-11-24 12:52:24 +0000 |
465 | @@ -4,6 +4,7 @@ |
466 | historydaemon.cpp |
467 | historyservicedbus.cpp |
468 | pluginmanager.cpp |
469 | + rolesinterface.cpp |
470 | textchannelobserver.cpp |
471 | ) |
472 | |
473 | |
474 | === modified file 'daemon/HistoryService.xml' |
475 | --- daemon/HistoryService.xml 2015-09-23 15:08:07 +0000 |
476 | +++ daemon/HistoryService.xml 2016-11-24 12:52:24 +0000 |
477 | @@ -9,6 +9,20 @@ |
478 | <dox:d> |
479 | An interface to the history service |
480 | </dox:d> |
481 | + <method name="ThreadForProperties"> |
482 | + <dox:d><![CDATA[ |
483 | + Return an existing thread for the given parameters. |
484 | + ]]></dox:d> |
485 | + <arg name="accountId" type="s" direction="in"/> |
486 | + <arg name="type" type="i" direction="in"/> |
487 | + <arg name="properties" type="a{sv}" direction="in"/> |
488 | + <arg name="matchFlags" type="i" direction="in"/> |
489 | + <arg name="create" type="b" direction="in"/> |
490 | + <arg type="a{sv}" direction="out"/> |
491 | + <annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="QVariantMap"/> |
492 | + <annotation name="org.qtproject.QtDBus.QtTypeName.In2" value="QVariantMap"/> |
493 | + </method> |
494 | + |
495 | <method name="ThreadForParticipants"> |
496 | <dox:d><![CDATA[ |
497 | Return an existing thread for the given parameters. |
498 | |
499 | === modified file 'daemon/historydaemon.cpp' |
500 | --- daemon/historydaemon.cpp 2015-11-20 12:53:49 +0000 |
501 | +++ daemon/historydaemon.cpp 2016-11-24 12:52:24 +0000 |
502 | @@ -1,5 +1,5 @@ |
503 | /* |
504 | - * Copyright (C) 2013-2015 Canonical, Ltd. |
505 | + * Copyright (C) 2013-2016 Canonical, Ltd. |
506 | * |
507 | * Authors: |
508 | * Gustavo Pichorim Boiko <gustavo.boiko@canonical.com> |
509 | @@ -23,20 +23,97 @@ |
510 | #include "telepathyhelper_p.h" |
511 | #include "filter.h" |
512 | #include "sort.h" |
513 | +#include "utils_p.h" |
514 | |
515 | #include "pluginmanager.h" |
516 | #include "plugin.h" |
517 | #include "pluginthreadview.h" |
518 | #include "plugineventview.h" |
519 | +#include "textevent.h" |
520 | |
521 | #include <QStandardPaths> |
522 | #include <QCryptographicHash> |
523 | #include <TelepathyQt/CallChannel> |
524 | +#include <TelepathyQt/PendingVariantMap> |
525 | #include <TelepathyQt/ReferencedHandles> |
526 | |
527 | +#include <TelepathyQt/PendingVariant> |
528 | +#include <TelepathyQt/PendingOperation> |
529 | + |
530 | +Q_DECLARE_METATYPE(RolesMap) |
531 | + |
532 | +const constexpr static int AdminRole = 2; |
533 | + |
534 | +enum ChannelGroupChangeReason |
535 | +{ |
536 | + ChannelGroupChangeReasonNone = 0, |
537 | + ChannelGroupChangeReasonOffline = 1, |
538 | + ChannelGroupChangeReasonKicked = 2, |
539 | + ChannelGroupChangeReasonBusy = 3, |
540 | + ChannelGroupChangeReasonInvited = 4, |
541 | + ChannelGroupChangeReasonBanned = 5, |
542 | + ChannelGroupChangeReasonError = 6, |
543 | + ChannelGroupChangeReasonInvalidContact = 7, |
544 | + ChannelGroupChangeReasonNoAnswer = 8, |
545 | + ChannelGroupChangeReasonRenamed = 9, |
546 | + ChannelGroupChangeReasonPermissionDenied = 10, |
547 | + ChannelGroupChangeReasonSeparated = 11, |
548 | + |
549 | + // additional enum values not included in original ChannelGroupChangeReason |
550 | + // telepathy enumeration but needed here to provide extra info to client when group |
551 | + // is cancelled |
552 | + ChannelGroupChangeReasonGone = 12, |
553 | + ChannelGroupChangeReasonRejected = 13 |
554 | +}; |
555 | + |
556 | +const QDBusArgument &operator>>(const QDBusArgument &argument, RolesMap &roles) |
557 | +{ |
558 | + argument.beginMap(); |
559 | + while ( !argument.atEnd() ) { |
560 | + argument.beginMapEntry(); |
561 | + uint key,value; |
562 | + argument >> key >> value; |
563 | + argument.endMapEntry(); |
564 | + roles[key] = value; |
565 | + } |
566 | + |
567 | + argument.endMap(); |
568 | + return argument; |
569 | +} |
570 | + |
571 | +bool foundAsMemberInThread(const Tp::ContactPtr& contact, QVariantMap thread) |
572 | +{ |
573 | + Q_FOREACH (QVariant participant, thread[History::FieldParticipants].toList()) { |
574 | + // found if same identifier and as member into thread info |
575 | + if (History::Utils::compareIds(thread[History::FieldAccountId].toString(), |
576 | + contact->id(), |
577 | + participant.toMap()[History::FieldIdentifier].toString()) && |
578 | + participant.toMap()[History::FieldParticipantState].toUInt() == History::ParticipantStateRegular) |
579 | + { |
580 | + return true; |
581 | + } |
582 | + } |
583 | + return false; |
584 | +} |
585 | + |
586 | +bool foundInThread(const Tp::ContactPtr& contact, QVariantMap thread) |
587 | +{ |
588 | + Q_FOREACH (QVariant participant, thread[History::FieldParticipants].toList()) { |
589 | + if (History::Utils::compareIds(thread[History::FieldAccountId].toString(), |
590 | + contact->id(), |
591 | + participant.toMap()[History::FieldIdentifier].toString())) |
592 | + { |
593 | + return true; |
594 | + } |
595 | + } |
596 | + return false; |
597 | +} |
598 | + |
599 | HistoryDaemon::HistoryDaemon(QObject *parent) |
600 | : QObject(parent), mCallObserver(this), mTextObserver(this) |
601 | { |
602 | + qRegisterMetaType<HandleRolesMap>(); |
603 | + qDBusRegisterMetaType<HandleRolesMap>(); |
604 | // get the first plugin |
605 | if (!History::PluginManager::instance()->plugins().isEmpty()) { |
606 | mBackend = History::PluginManager::instance()->plugins().first(); |
607 | @@ -65,6 +142,9 @@ |
608 | connect(&mTextObserver, |
609 | SIGNAL(messageRead(Tp::TextChannelPtr,Tp::ReceivedMessage)), |
610 | SLOT(onMessageRead(Tp::TextChannelPtr,Tp::ReceivedMessage))); |
611 | + connect(&mTextObserver, |
612 | + SIGNAL(channelAvailable(Tp::TextChannelPtr)), |
613 | + SLOT(onTextChannelAvailable(Tp::TextChannelPtr))); |
614 | |
615 | // FIXME: we need to do this in a better way, but for now this should do |
616 | mProtocolFlags["ofono"] = History::MatchPhoneNumber; |
617 | @@ -81,37 +161,153 @@ |
618 | return self; |
619 | } |
620 | |
621 | -QStringList HistoryDaemon::participantsFromChannel(const Tp::TextChannelPtr &textChannel) |
622 | -{ |
623 | - QStringList participants; |
624 | +void HistoryDaemon::onRolesChanged(const HandleRolesMap &added, const HandleRolesMap &removed) |
625 | +{ |
626 | + Q_UNUSED(added); |
627 | + Q_UNUSED(removed); |
628 | + |
629 | + ChannelInterfaceRolesInterface *roles_interface = qobject_cast<ChannelInterfaceRolesInterface*>(sender()); |
630 | + RolesMap roles = roles_interface->getRoles(); |
631 | + |
632 | + Tp::TextChannelPtr channel(qobject_cast<Tp::TextChannel*>(sender()->parent())); |
633 | + QVariantMap properties = propertiesFromChannel(channel); |
634 | + QVariantMap thread = threadForProperties(channel->property(History::FieldAccountId).toString(), |
635 | + History::EventTypeText, |
636 | + properties, |
637 | + matchFlagsForChannel(channel), |
638 | + false); |
639 | + |
640 | + writeRolesInformationEvents(thread, channel, roles); |
641 | + |
642 | + updateRoomRoles(channel, roles); |
643 | +} |
644 | + |
645 | +QVariantMap HistoryDaemon::propertiesFromChannel(const Tp::ChannelPtr &textChannel) |
646 | +{ |
647 | + QVariantMap properties; |
648 | + QVariantList participants; |
649 | + QStringList participantIds; |
650 | + |
651 | + ChannelInterfaceRolesInterface *roles_interface = textChannel->optionalInterface<ChannelInterfaceRolesInterface>(); |
652 | + RolesMap roles; |
653 | + if (roles_interface) { |
654 | + roles = roles_interface->getRoles(); |
655 | + } |
656 | + |
657 | Q_FOREACH(const Tp::ContactPtr contact, textChannel->groupContacts(false)) { |
658 | - participants << contact->id(); |
659 | + QVariantMap contactProperties; |
660 | + contactProperties[History::FieldAlias] = contact->alias(); |
661 | + contactProperties[History::FieldAccountId] = textChannel->property(History::FieldAccountId).toString(); |
662 | + contactProperties[History::FieldIdentifier] = contact->id(); |
663 | + contactProperties[History::FieldParticipantState] = History::ParticipantStateRegular; |
664 | + contactProperties[History::FieldParticipantRoles] = roles[contact->handle().at(0)]; |
665 | + participantIds << contact->id(); |
666 | + participants << contactProperties; |
667 | + } |
668 | + |
669 | + Q_FOREACH(const Tp::ContactPtr contact, textChannel->groupRemotePendingContacts(false)) { |
670 | + QVariantMap contactProperties; |
671 | + contactProperties[History::FieldAlias] = contact->alias(); |
672 | + contactProperties[History::FieldAccountId] = textChannel->property(History::FieldAccountId).toString(); |
673 | + contactProperties[History::FieldIdentifier] = contact->id(); |
674 | + contactProperties[History::FieldParticipantState] = History::ParticipantStateRemotePending; |
675 | + contactProperties[History::FieldParticipantRoles] = roles[contact->handle().at(0)]; |
676 | + participantIds << contact->id(); |
677 | + participants << contactProperties; |
678 | + } |
679 | + |
680 | + Q_FOREACH(const Tp::ContactPtr contact, textChannel->groupLocalPendingContacts(false)) { |
681 | + QVariantMap contactProperties; |
682 | + contactProperties[History::FieldAlias] = contact->alias(); |
683 | + contactProperties[History::FieldAccountId] = textChannel->property(History::FieldAccountId).toString(); |
684 | + contactProperties[History::FieldIdentifier] = contact->id(); |
685 | + contactProperties[History::FieldParticipantState] = History::ParticipantStateLocalPending; |
686 | + contactProperties[History::FieldParticipantRoles] = roles[contact->handle().at(0)]; |
687 | + participantIds << contact->id(); |
688 | + participants << contactProperties; |
689 | } |
690 | |
691 | if (participants.isEmpty() && textChannel->targetHandleType() == Tp::HandleTypeContact && |
692 | - textChannel->targetContact() == textChannel->connection()->selfContact()) { |
693 | - participants << textChannel->targetContact()->id(); |
694 | - } |
695 | - return participants; |
696 | + textChannel->targetContact() == textChannel->connection()->selfContact()) { |
697 | + QVariantMap contactProperties; |
698 | + contactProperties[History::FieldAlias] = textChannel->targetContact()->alias(); |
699 | + contactProperties[History::FieldAccountId] = textChannel->property(History::FieldAccountId).toString(); |
700 | + contactProperties[History::FieldIdentifier] = textChannel->targetContact()->id(); |
701 | + contactProperties[History::FieldParticipantState] = History::ParticipantStateRegular; |
702 | + participantIds << textChannel->targetContact()->id(); |
703 | + participants << contactProperties; |
704 | + } |
705 | + |
706 | + // We map chatType directly from telepathy targetHandleType: None, Contact, Room |
707 | + properties[History::FieldChatType] = textChannel->targetHandleType(); |
708 | + properties[History::FieldParticipants] = participants; |
709 | + properties[History::FieldParticipantIds] = participantIds; |
710 | + |
711 | + QVariantMap roomProperties; |
712 | + switch(textChannel->targetHandleType()) { |
713 | + case Tp::HandleTypeRoom: |
714 | + if (textChannel->hasInterface(TP_QT_IFACE_CHANNEL_INTERFACE_ROOM)) { |
715 | + auto room_interface = textChannel->optionalInterface<Tp::Client::ChannelInterfaceRoomInterface>(); |
716 | + QVariantMap map = getInterfaceProperties(room_interface); |
717 | + for (QVariantMap::const_iterator iter = map.begin(); iter != map.end(); ++iter) { |
718 | + if (iter.value().isValid()) { |
719 | + roomProperties[iter.key()] = iter.value(); |
720 | + } |
721 | + } |
722 | + } |
723 | + if (textChannel->hasInterface(TP_QT_IFACE_CHANNEL_INTERFACE_ROOM_CONFIG)) { |
724 | + auto room_config_interface = textChannel->optionalInterface<Tp::Client::ChannelInterfaceRoomConfigInterface>(); |
725 | + QVariantMap map = getInterfaceProperties(room_config_interface); |
726 | + for (QVariantMap::const_iterator iter = map.begin(); iter != map.end(); ++iter) { |
727 | + if (iter.value().isValid()) { |
728 | + roomProperties[iter.key()] = iter.value(); |
729 | + } |
730 | + } |
731 | + } |
732 | + if (textChannel->hasInterface(TP_QT_IFACE_CHANNEL_INTERFACE_SUBJECT)) { |
733 | + auto subject_interface = textChannel->optionalInterface<Tp::Client::ChannelInterfaceSubjectInterface>(); |
734 | + QVariantMap map = getInterfaceProperties(subject_interface); |
735 | + for (QVariantMap::const_iterator iter = map.begin(); iter != map.end(); ++iter) { |
736 | + if (iter.value().isValid()) { |
737 | + roomProperties[iter.key()] = iter.value(); |
738 | + } |
739 | + } |
740 | + } |
741 | + |
742 | + properties[History::FieldChatRoomInfo] = roomProperties; |
743 | + properties[History::FieldThreadId] = textChannel->targetId(); |
744 | + break; |
745 | + case Tp::HandleTypeContact: |
746 | + case Tp::HandleTypeNone: |
747 | + default: |
748 | + break; |
749 | + } |
750 | + |
751 | + return properties; |
752 | } |
753 | |
754 | -QVariantMap HistoryDaemon::threadForParticipants(const QString &accountId, |
755 | - History::EventType type, |
756 | - const QStringList &participants, |
757 | - History::MatchFlags matchFlags, |
758 | - bool create) |
759 | +QVariantMap HistoryDaemon::threadForProperties(const QString &accountId, |
760 | + History::EventType type, |
761 | + const QVariantMap &properties, |
762 | + History::MatchFlags matchFlags, |
763 | + bool create) |
764 | { |
765 | if (!mBackend) { |
766 | return QVariantMap(); |
767 | } |
768 | |
769 | - QVariantMap thread = mBackend->threadForParticipants(accountId, |
770 | - type, |
771 | - participants, |
772 | - matchFlags); |
773 | + QVariantMap thread = mBackend->threadForProperties(accountId, |
774 | + type, |
775 | + properties, |
776 | + matchFlags); |
777 | if (thread.isEmpty() && create) { |
778 | - thread = mBackend->createThreadForParticipants(accountId, type, participants); |
779 | + thread = mBackend->createThreadForProperties(accountId, type, properties); |
780 | if (!thread.isEmpty()) { |
781 | + if (properties.contains("Requested") && properties[History::FieldChatType].toInt() == History::ChatTypeRoom) { |
782 | + QVariantMap map = thread[History::FieldChatRoomInfo].toMap(); |
783 | + map["Requested"] = properties["Requested"]; |
784 | + thread[History::FieldChatRoomInfo] = map; |
785 | + } |
786 | mDBus.notifyThreadsAdded(QList<QVariantMap>() << thread); |
787 | } |
788 | } |
789 | @@ -174,7 +370,7 @@ |
790 | return mBackend->getSingleEvent((History::EventType)type, accountId, threadId, eventId); |
791 | } |
792 | |
793 | -bool HistoryDaemon::writeEvents(const QList<QVariantMap> &events) |
794 | +bool HistoryDaemon::writeEvents(const QList<QVariantMap> &events, const QVariantMap &properties) |
795 | { |
796 | if (!mBackend) { |
797 | return false; |
798 | @@ -206,7 +402,7 @@ |
799 | } |
800 | |
801 | // only get the thread AFTER the event is written to make sure it is up-to-date |
802 | - QVariantMap thread = getSingleThread(type, accountId, threadId, QVariantMap()); |
803 | + QVariantMap thread = getSingleThread(type, accountId, threadId, properties); |
804 | QString hash = hashThread(thread); |
805 | threads[hash] = thread; |
806 | |
807 | @@ -245,7 +441,6 @@ |
808 | |
809 | bool HistoryDaemon::removeEvents(const QList<QVariantMap> &events) |
810 | { |
811 | - qDebug() << __PRETTY_FUNCTION__; |
812 | if (!mBackend) { |
813 | return false; |
814 | } |
815 | @@ -319,7 +514,6 @@ |
816 | |
817 | bool HistoryDaemon::removeThreads(const QList<QVariantMap> &threads) |
818 | { |
819 | - qDebug() << __PRETTY_FUNCTION__; |
820 | if (!mBackend) { |
821 | return false; |
822 | } |
823 | @@ -360,7 +554,6 @@ |
824 | |
825 | void HistoryDaemon::onObserverCreated() |
826 | { |
827 | - qDebug() << __PRETTY_FUNCTION__; |
828 | History::ChannelObserver *observer = History::TelepathyHelper::instance()->channelObserver(); |
829 | |
830 | connect(observer, SIGNAL(callChannelAvailable(Tp::CallChannelPtr)), |
831 | @@ -371,10 +564,14 @@ |
832 | |
833 | void HistoryDaemon::onCallEnded(const Tp::CallChannelPtr &channel) |
834 | { |
835 | - qDebug() << __PRETTY_FUNCTION__; |
836 | - QStringList participants; |
837 | + QVariantMap properties = propertiesFromChannel(channel); |
838 | + QVariantList participants; |
839 | Q_FOREACH(const Tp::ContactPtr contact, channel->remoteMembers()) { |
840 | - participants << contact->id(); |
841 | + QVariantMap contactProperties; |
842 | + contactProperties[History::FieldAlias] = contact->alias(); |
843 | + contactProperties[History::FieldIdentifier] = contact->id(); |
844 | + contactProperties[History::FieldAccountId] = channel->property(History::FieldAccountId).toString(); |
845 | + participants << contactProperties; |
846 | } |
847 | |
848 | // it shouldn't happen, but in case it does, we won't crash |
849 | @@ -384,11 +581,11 @@ |
850 | } |
851 | |
852 | QString accountId = channel->property(History::FieldAccountId).toString(); |
853 | - QVariantMap thread = threadForParticipants(accountId, |
854 | - History::EventTypeVoice, |
855 | - participants, |
856 | - matchFlagsForChannel(channel), |
857 | - true); |
858 | + QVariantMap thread = threadForProperties(accountId, |
859 | + History::EventTypeVoice, |
860 | + properties, |
861 | + matchFlagsForChannel(channel), |
862 | + true); |
863 | // fill the call info |
864 | QDateTime timestamp = channel->property(History::FieldTimestamp).toDateTime(); |
865 | |
866 | @@ -414,18 +611,284 @@ |
867 | event[History::FieldMissed] = missed; |
868 | event[History::FieldDuration] = duration; |
869 | // FIXME: check what to do when there are more than just one remote participant |
870 | - event[History::FieldRemoteParticipant] = participants[0]; |
871 | - writeEvents(QList<QVariantMap>() << event); |
872 | + event[History::FieldRemoteParticipant] = participants[0].toMap()[History::FieldIdentifier]; |
873 | + writeEvents(QList<QVariantMap>() << event, properties); |
874 | +} |
875 | + |
876 | +void HistoryDaemon::onTextChannelAvailable(const Tp::TextChannelPtr channel) |
877 | +{ |
878 | + // for Rooms we need to explicitly create the thread to allow users to send messages to groups even |
879 | + // before they receive any message. |
880 | + // for other types, we can wait until messages are received |
881 | + if (channel->targetHandleType() == Tp::HandleTypeRoom) { |
882 | + QString accountId = channel->property(History::FieldAccountId).toString(); |
883 | + QVariantMap properties = propertiesFromChannel(channel); |
884 | + |
885 | + // first try to fetch the existing thread to see if there is any. |
886 | + QVariantMap thread = threadForProperties(accountId, |
887 | + History::EventTypeText, |
888 | + properties, |
889 | + matchFlagsForChannel(channel), |
890 | + false); |
891 | + if (thread.isEmpty()) { |
892 | + // if there no existing thread, create one |
893 | + properties["Requested"] = channel->isRequested(); |
894 | + thread = threadForProperties(accountId, |
895 | + History::EventTypeText, |
896 | + properties, |
897 | + matchFlagsForChannel(channel), |
898 | + true); |
899 | + |
900 | + // write information event including all initial invitees |
901 | + Q_FOREACH(const Tp::ContactPtr contact, channel->groupRemotePendingContacts(false)) { |
902 | + writeInformationEvent(thread, History::InformationTypeInvitationSent, contact->alias()); |
903 | + } |
904 | + |
905 | + // update participants only if the thread is not available previously. Otherwise we'll wait for membersChanged event |
906 | + // for reflect in conversation information events for modified participants. |
907 | + updateRoomParticipants(channel); |
908 | + } |
909 | + |
910 | + // write an entry saying you joined the group if 'joined' flag in thread is false and modify that flag. |
911 | + if (!thread[History::FieldChatRoomInfo].toMap()["Joined"].toBool()) { |
912 | + // only write self joined notification if protocol is not a phone one. |
913 | + // FIXME (rmescandon): as a first solution, let's take only ofono as phone protocol |
914 | + if (History::TelepathyHelper::instance()->accountForId(accountId)->protocolName() != "ofono") { |
915 | + writeInformationEvent(thread, History::InformationTypeSelfJoined); |
916 | + } |
917 | + // update backend |
918 | + updateRoomProperties(channel, QVariantMap{{"Joined", true}}); |
919 | + } |
920 | + |
921 | + Tp::AbstractInterface *room_interface = channel->optionalInterface<Tp::Client::ChannelInterfaceRoomInterface>(); |
922 | + Tp::AbstractInterface *room_config_interface = channel->optionalInterface<Tp::Client::ChannelInterfaceRoomConfigInterface>(); |
923 | + Tp::AbstractInterface *subject_interface = channel->optionalInterface<Tp::Client::ChannelInterfaceSubjectInterface>(); |
924 | + ChannelInterfaceRolesInterface *roles_interface = channel->optionalInterface<ChannelInterfaceRolesInterface>(); |
925 | + |
926 | + QList<Tp::AbstractInterface*> interfaces; |
927 | + interfaces << room_interface << room_config_interface << subject_interface << roles_interface; |
928 | + for (auto interface : interfaces) { |
929 | + if (interface) { |
930 | + interface->setMonitorProperties(true); |
931 | + interface->setProperty(History::FieldAccountId, accountId); |
932 | + interface->setProperty(History::FieldThreadId, thread[History::FieldThreadId].toString()); |
933 | + interface->setProperty(History::FieldType, thread[History::FieldType].toInt()); |
934 | + connect(interface, SIGNAL(propertiesChanged(const QVariantMap &,const QStringList &)), |
935 | + SLOT(onRoomPropertiesChanged(const QVariantMap &,const QStringList &))); |
936 | + // update the stored info |
937 | + Q_EMIT interface->propertiesChanged(getInterfaceProperties(interface), QStringList()); |
938 | + } |
939 | + } |
940 | + |
941 | + connect(channel.data(), SIGNAL(groupMembersChanged(const Tp::Contacts &, const Tp::Contacts &, const Tp::Contacts &, const Tp::Contacts &, const Tp::Channel::GroupMemberChangeDetails &)), |
942 | + SLOT(onGroupMembersChanged(const Tp::Contacts &, const Tp::Contacts &, const Tp::Contacts &, const Tp::Contacts &, const Tp::Channel::GroupMemberChangeDetails &))); |
943 | + |
944 | + connect(roles_interface, SIGNAL(RolesChanged(const HandleRolesMap&, const HandleRolesMap&)), SLOT(onRolesChanged(const HandleRolesMap&, const HandleRolesMap&))); |
945 | + } |
946 | +} |
947 | + |
948 | +void HistoryDaemon::onGroupMembersChanged(const Tp::Contacts &groupMembersAdded, |
949 | + const Tp::Contacts &groupLocalPendingMembersAdded, |
950 | + const Tp::Contacts &groupRemotePendingMembersAdded, |
951 | + const Tp::Contacts &groupMembersRemoved, |
952 | + const Tp::Channel::GroupMemberChangeDetails &details) |
953 | +{ |
954 | + Tp::TextChannelPtr channel(qobject_cast<Tp::TextChannel*>(sender())); |
955 | + |
956 | + QVariantMap properties; |
957 | + QVariantMap thread; |
958 | + |
959 | + // information events for members updates. |
960 | + bool hasRemotePendingMembersAdded = groupRemotePendingMembersAdded.size() > 0; |
961 | + bool hasMembersAdded = groupMembersAdded.size() > 0; |
962 | + bool hasMembersRemoved = groupMembersRemoved.size() > 0; |
963 | + |
964 | + if (hasRemotePendingMembersAdded || hasMembersAdded || hasMembersRemoved) { |
965 | + properties = propertiesFromChannel(channel); |
966 | + thread = threadForProperties(channel->property(History::FieldAccountId).toString(), |
967 | + History::EventTypeText, |
968 | + properties, |
969 | + matchFlagsForChannel(channel), |
970 | + false); |
971 | + if (!thread.isEmpty()) { |
972 | + if (hasRemotePendingMembersAdded) { |
973 | + Q_FOREACH (const Tp::ContactPtr& contact, groupRemotePendingMembersAdded) { |
974 | + if (!foundInThread(contact, thread)) { |
975 | + writeInformationEvent(thread, History::InformationTypeInvitationSent, contact->alias()); |
976 | + } |
977 | + } |
978 | + } |
979 | + if (hasMembersAdded) { |
980 | + Q_FOREACH (const Tp::ContactPtr& contact, groupMembersAdded) { |
981 | + // if this member was not previously regular member in thread, notify about his join |
982 | + if (!foundAsMemberInThread(contact, thread)) { |
983 | + writeInformationEvent(thread, History::InformationTypeJoined, contact->alias()); |
984 | + } |
985 | + } |
986 | + } |
987 | + |
988 | + if (hasMembersRemoved) { |
989 | + if (channel->groupSelfContactRemoveInfo().isValid()) { |
990 | + // evaluate if we are leaving by our own or we are kicked |
991 | + History::InformationType type = History::InformationTypeSelfLeaving; |
992 | + if (channel->groupSelfContactRemoveInfo().hasReason()) { |
993 | + switch (channel->groupSelfContactRemoveInfo().reason()) { |
994 | + case ChannelGroupChangeReasonKicked: |
995 | + type = History::InformationTypeSelfKicked; |
996 | + break; |
997 | + case ChannelGroupChangeReasonGone: |
998 | + type = History::InformationTypeGroupGone; |
999 | + break; |
1000 | + } |
1001 | + } |
1002 | + writeInformationEvent(thread, type); |
1003 | + // update backend |
1004 | + updateRoomProperties(channel, QVariantMap{{"Joined", false}}); |
1005 | + } |
1006 | + else // don't notify any other group member removal if we are leaving the group |
1007 | + { |
1008 | + Q_FOREACH (const Tp::ContactPtr& contact, groupMembersRemoved) { |
1009 | + // inform about removed members other than us |
1010 | + if (contact->id() != channel->groupSelfContact()->id()) { |
1011 | + writeInformationEvent(thread, History::InformationTypeLeaving, contact->alias()); |
1012 | + } |
1013 | + } |
1014 | + } |
1015 | + } |
1016 | + } |
1017 | + } |
1018 | + |
1019 | + updateRoomParticipants(channel); |
1020 | +} |
1021 | + |
1022 | +void HistoryDaemon::updateRoomParticipants(const Tp::TextChannelPtr channel) |
1023 | +{ |
1024 | + if (!channel) { |
1025 | + return; |
1026 | + } |
1027 | + |
1028 | + QVariantList participants; |
1029 | + QStringList contactsAdded; |
1030 | + |
1031 | + ChannelInterfaceRolesInterface *roles_interface = channel->optionalInterface<ChannelInterfaceRolesInterface>(); |
1032 | + RolesMap roles; |
1033 | + if (roles_interface) { |
1034 | + roles = roles_interface->getRoles(); |
1035 | + } |
1036 | + |
1037 | + Q_FOREACH(const Tp::ContactPtr contact, channel->groupRemotePendingContacts(false)) { |
1038 | + QVariantMap participant; |
1039 | + contactsAdded << contact->id(); |
1040 | + participant[History::FieldIdentifier] = contact->id(); |
1041 | + participant[History::FieldAlias] = contact->alias(); |
1042 | + participant[History::FieldParticipantState] = History::ParticipantStateRemotePending; |
1043 | + participant[History::FieldParticipantRoles] = roles[contact->handle().at(0)]; |
1044 | + participants << QVariant::fromValue(participant); |
1045 | + } |
1046 | + Q_FOREACH(const Tp::ContactPtr contact, channel->groupLocalPendingContacts(false)) { |
1047 | + QVariantMap participant; |
1048 | + contactsAdded << contact->id(); |
1049 | + participant[History::FieldIdentifier] = contact->id(); |
1050 | + participant[History::FieldAlias] = contact->alias(); |
1051 | + participant[History::FieldParticipantState] = History::ParticipantStateLocalPending; |
1052 | + participant[History::FieldParticipantRoles] = roles[contact->handle().at(0)]; |
1053 | + participants << QVariant::fromValue(participant); |
1054 | + } |
1055 | + |
1056 | + Q_FOREACH(const Tp::ContactPtr contact, channel->groupContacts(false)) { |
1057 | + // do not include remote and local pending members |
1058 | + if (contactsAdded.contains(contact->id())) { |
1059 | + continue; |
1060 | + } |
1061 | + QVariantMap participant; |
1062 | + participant[History::FieldIdentifier] = contact->id(); |
1063 | + participant[History::FieldAlias] = contact->alias(); |
1064 | + participant[History::FieldParticipantState] = History::ParticipantStateRegular; |
1065 | + participant[History::FieldParticipantRoles] = roles[contact->handle().at(0)]; |
1066 | + participants << QVariant::fromValue(participant); |
1067 | + } |
1068 | + |
1069 | + QString accountId = channel->property(History::FieldAccountId).toString(); |
1070 | + QString threadId = channel->targetId(); |
1071 | + if (mBackend->updateRoomParticipants(accountId, threadId, History::EventTypeText, participants)) { |
1072 | + QVariantMap updatedThread = getSingleThread(History::EventTypeText, accountId, threadId, QVariantMap()); |
1073 | + mDBus.notifyThreadsModified(QList<QVariantMap>() << updatedThread); |
1074 | + } |
1075 | +} |
1076 | + |
1077 | +void HistoryDaemon::updateRoomRoles(const Tp::TextChannelPtr &channel, const RolesMap &rolesMap) |
1078 | +{ |
1079 | + if (!channel) { |
1080 | + return; |
1081 | + } |
1082 | + |
1083 | + QVariantMap participantsRoles; |
1084 | + |
1085 | + Q_FOREACH(const Tp::ContactPtr contact, channel->groupRemotePendingContacts(false)) { |
1086 | + participantsRoles[contact->id()] = rolesMap[contact->handle().at(0)]; |
1087 | + } |
1088 | + Q_FOREACH(const Tp::ContactPtr contact, channel->groupLocalPendingContacts(false)) { |
1089 | + participantsRoles[contact->id()] = rolesMap[contact->handle().at(0)]; |
1090 | + } |
1091 | + |
1092 | + Q_FOREACH(const Tp::ContactPtr contact, channel->groupContacts(false)) { |
1093 | + if (!participantsRoles.contains(contact->id())) { |
1094 | + participantsRoles[contact->id()] = rolesMap[contact->handle().at(0)]; |
1095 | + } |
1096 | + } |
1097 | + |
1098 | + // update participants roles |
1099 | + QString accountId = channel->property(History::FieldAccountId).toString(); |
1100 | + QString threadId = channel->targetId(); |
1101 | + if (mBackend->updateRoomParticipantsRoles(accountId, threadId, History::EventTypeText, participantsRoles)) { |
1102 | + QVariantMap updatedThread = getSingleThread(History::EventTypeText, accountId, threadId, QVariantMap()); |
1103 | + mDBus.notifyThreadsModified(QList<QVariantMap>() << updatedThread); |
1104 | + } |
1105 | + |
1106 | + // update self roles in room properties |
1107 | + uint selfRoles = rolesMap[channel->groupSelfContact()->handle().at(0)]; |
1108 | + updateRoomProperties(channel, QVariantMap{{"SelfRoles", selfRoles}}); |
1109 | +} |
1110 | + |
1111 | +void HistoryDaemon::onRoomPropertiesChanged(const QVariantMap &properties,const QStringList &invalidated) |
1112 | +{ |
1113 | + QString accountId = sender()->property(History::FieldAccountId).toString(); |
1114 | + QString threadId = sender()->property(History::FieldThreadId).toString(); |
1115 | + History::EventType type = (History::EventType)sender()->property(History::FieldType).toInt(); |
1116 | + |
1117 | + // get thread before updating to see if there are changes to insert as information events |
1118 | + QVariantMap thread = getSingleThread(type, accountId, threadId, QVariantMap()); |
1119 | + if (!thread.empty()) { |
1120 | + writeRoomChangesInformationEvents(thread, properties); |
1121 | + } |
1122 | + |
1123 | + updateRoomProperties(accountId, threadId, type, properties, invalidated); |
1124 | +} |
1125 | + |
1126 | +void HistoryDaemon::updateRoomProperties(const Tp::TextChannelPtr &channel, const QVariantMap &properties) |
1127 | +{ |
1128 | + QString accountId = channel->property(History::FieldAccountId).toString(); |
1129 | + QString threadId = channel->targetId(); |
1130 | + History::EventType type = History::EventTypeText; |
1131 | + updateRoomProperties(accountId, threadId, type, properties, QStringList()); |
1132 | +} |
1133 | + |
1134 | +void HistoryDaemon::updateRoomProperties(const QString &accountId, const QString &threadId, History::EventType type, const QVariantMap &properties, const QStringList &invalidated) |
1135 | +{ |
1136 | + if (mBackend->updateRoomInfo(accountId, threadId, type, properties, invalidated)) { |
1137 | + QVariantMap thread = getSingleThread(type, accountId, threadId, QVariantMap()); |
1138 | + mDBus.notifyThreadsModified(QList<QVariantMap>() << thread); |
1139 | + } |
1140 | } |
1141 | |
1142 | void HistoryDaemon::onMessageReceived(const Tp::TextChannelPtr textChannel, const Tp::ReceivedMessage &message) |
1143 | { |
1144 | - qDebug() << __PRETTY_FUNCTION__; |
1145 | QString eventId; |
1146 | Tp::MessagePart header = message.header(); |
1147 | QString senderId; |
1148 | + QVariantMap properties = propertiesFromChannel(textChannel); |
1149 | History::MessageStatus status = History::MessageStatusUnknown; |
1150 | - if (message.sender()->handle().at(0) == textChannel->connection()->selfHandle()) { |
1151 | + if (!message.sender() || message.sender()->handle().at(0) == textChannel->connection()->selfHandle()) { |
1152 | senderId = "self"; |
1153 | status = History::MessageStatusDelivered; |
1154 | } else { |
1155 | @@ -452,6 +915,13 @@ |
1156 | return; |
1157 | } |
1158 | |
1159 | + // FIXME: if this message is already read, don't allow reverting the status. |
1160 | + // we need to check if this is the right place to do it. |
1161 | + if (textEvent[History::FieldMessageStatus].toInt() == History::MessageStatusRead) { |
1162 | + qWarning() << "Skipping delivery report as it is trying to revert the Read status of an existing message to the following status:" << message.deliveryDetails().status(); |
1163 | + return; |
1164 | + } |
1165 | + |
1166 | History::MessageStatus status; |
1167 | switch (message.deliveryDetails().status()) { |
1168 | case Tp::DeliveryStatusAccepted: |
1169 | @@ -478,20 +948,18 @@ |
1170 | } |
1171 | |
1172 | textEvent[History::FieldMessageStatus] = (int) status; |
1173 | - if (!writeEvents(QList<QVariantMap>() << textEvent)) { |
1174 | + if (!writeEvents(QList<QVariantMap>() << textEvent, properties)) { |
1175 | qWarning() << "Failed to save the new message status!"; |
1176 | } |
1177 | |
1178 | return; |
1179 | } |
1180 | |
1181 | - QStringList participants = participantsFromChannel(textChannel); |
1182 | - |
1183 | - QVariantMap thread = threadForParticipants(textChannel->property(History::FieldAccountId).toString(), |
1184 | - History::EventTypeText, |
1185 | - participants, |
1186 | - matchFlagsForChannel(textChannel), |
1187 | - true); |
1188 | + QVariantMap thread = threadForProperties(textChannel->property(History::FieldAccountId).toString(), |
1189 | + History::EventTypeText, |
1190 | + properties, |
1191 | + matchFlagsForChannel(textChannel), |
1192 | + true); |
1193 | int count = 1; |
1194 | QList<QVariantMap> attachments; |
1195 | History::MessageType type = History::MessageTypeText; |
1196 | @@ -558,16 +1026,22 @@ |
1197 | event[History::FieldSubject] = subject; |
1198 | event[History::FieldAttachments] = QVariant::fromValue(attachments); |
1199 | |
1200 | - writeEvents(QList<QVariantMap>() << event); |
1201 | + writeEvents(QList<QVariantMap>() << event, properties); |
1202 | + |
1203 | + // if this messages supersedes another one, remove the original message |
1204 | + if (!message.supersededToken().isEmpty()) { |
1205 | + event[History::FieldEventId] = message.supersededToken(); |
1206 | + removeEvents(QList<QVariantMap>() << event); |
1207 | + } |
1208 | } |
1209 | |
1210 | QVariantMap HistoryDaemon::getSingleEventFromTextChannel(const Tp::TextChannelPtr textChannel, const QString &messageId) |
1211 | { |
1212 | - QStringList participants = participantsFromChannel(textChannel); |
1213 | + QVariantMap properties = propertiesFromChannel(textChannel); |
1214 | |
1215 | - QVariantMap thread = threadForParticipants(textChannel->property(History::FieldAccountId).toString(), |
1216 | + QVariantMap thread = threadForProperties(textChannel->property(History::FieldAccountId).toString(), |
1217 | History::EventTypeText, |
1218 | - participants, |
1219 | + properties, |
1220 | matchFlagsForChannel(textChannel), |
1221 | false); |
1222 | if (thread.isEmpty()) { |
1223 | @@ -587,6 +1061,7 @@ |
1224 | void HistoryDaemon::onMessageRead(const Tp::TextChannelPtr textChannel, const Tp::ReceivedMessage &message) |
1225 | { |
1226 | QVariantMap textEvent = getSingleEventFromTextChannel(textChannel, message.messageToken()); |
1227 | + QVariantMap properties = propertiesFromChannel(textChannel); |
1228 | |
1229 | if (textEvent.isEmpty()) { |
1230 | qWarning() << "Cound not find the original event to update with newEvent = false."; |
1231 | @@ -594,15 +1069,14 @@ |
1232 | } |
1233 | |
1234 | textEvent[History::FieldNewEvent] = false; |
1235 | - if (!writeEvents(QList<QVariantMap>() << textEvent)) { |
1236 | + if (!writeEvents(QList<QVariantMap>() << textEvent, properties)) { |
1237 | qWarning() << "Failed to save the new message status!"; |
1238 | } |
1239 | } |
1240 | |
1241 | void HistoryDaemon::onMessageSent(const Tp::TextChannelPtr textChannel, const Tp::Message &message, const QString &messageToken) |
1242 | { |
1243 | - qDebug() << __PRETTY_FUNCTION__; |
1244 | - QStringList participants = participantsFromChannel(textChannel); |
1245 | + QVariantMap properties = propertiesFromChannel(textChannel); |
1246 | QList<QVariantMap> attachments; |
1247 | History::MessageType type = History::MessageTypeText; |
1248 | int count = 1; |
1249 | @@ -615,9 +1089,9 @@ |
1250 | eventId = messageToken; |
1251 | } |
1252 | |
1253 | - QVariantMap thread = threadForParticipants(textChannel->property(History::FieldAccountId).toString(), |
1254 | + QVariantMap thread = threadForProperties(textChannel->property(History::FieldAccountId).toString(), |
1255 | History::EventTypeText, |
1256 | - participants, |
1257 | + properties, |
1258 | matchFlagsForChannel(textChannel), |
1259 | true); |
1260 | if (message.hasNonTextContent()) { |
1261 | @@ -666,7 +1140,6 @@ |
1262 | } |
1263 | } |
1264 | |
1265 | - |
1266 | QVariantMap event; |
1267 | event[History::FieldType] = History::EventTypeText; |
1268 | event[History::FieldAccountId] = thread[History::FieldAccountId]; |
1269 | @@ -686,7 +1159,7 @@ |
1270 | event[History::FieldSubject] = ""; |
1271 | event[History::FieldAttachments] = QVariant::fromValue(attachments); |
1272 | |
1273 | - writeEvents(QList<QVariantMap>() << event); |
1274 | + writeEvents(QList<QVariantMap>() << event, properties); |
1275 | } |
1276 | |
1277 | History::MatchFlags HistoryDaemon::matchFlagsForChannel(const Tp::ChannelPtr &channel) |
1278 | @@ -707,3 +1180,88 @@ |
1279 | hash += "#-#" + thread[History::FieldThreadId].toString(); |
1280 | return hash; |
1281 | } |
1282 | + |
1283 | +QVariantMap HistoryDaemon::getInterfaceProperties(const Tp::AbstractInterface *interface) |
1284 | +{ |
1285 | + QDBusInterface propsInterface(interface->service(), interface->path(), "org.freedesktop.DBus.Properties"); |
1286 | + QDBusReply<QVariantMap> reply = propsInterface.call("GetAll", interface->interface()); |
1287 | + if (!reply.isValid()) { |
1288 | + qWarning() << "Failed to fetch channel properties for interface" << interface->interface() << reply.error().message(); |
1289 | + } |
1290 | + return reply.value(); |
1291 | +} |
1292 | + |
1293 | +void HistoryDaemon::writeInformationEvent(const QVariantMap &thread, History::InformationType type, const QString &subject, const QString &sender, const QString &text) |
1294 | +{ |
1295 | + History::TextEvent historyEvent = History::TextEvent(thread[History::FieldAccountId].toString(), |
1296 | + thread[History::FieldThreadId].toString(), |
1297 | + QString(QCryptographicHash::hash(QByteArray( |
1298 | + (QDateTime::currentDateTime().toString("yyyy-MM-ddTHH:mm:ss.zzz") + subject + text).toLatin1()), |
1299 | + QCryptographicHash::Md5).toHex()), |
1300 | + sender, |
1301 | + QDateTime::currentDateTime(), |
1302 | + false, |
1303 | + text, |
1304 | + History::MessageTypeInformation, |
1305 | + History::MessageStatusUnknown, |
1306 | + QDateTime::currentDateTime(), |
1307 | + subject, |
1308 | + type); |
1309 | + writeEvents(QList<QVariantMap>() << historyEvent.properties(), thread); |
1310 | +} |
1311 | + |
1312 | +void HistoryDaemon::writeRoomChangesInformationEvents(const QVariantMap &thread, const QVariantMap &interfaceProperties) |
1313 | +{ |
1314 | + if (!thread.isEmpty()) { |
1315 | + // group subject |
1316 | + QString storedSubject = thread[History::FieldChatRoomInfo].toMap()["Subject"].toString(); |
1317 | + QString newSubject = interfaceProperties["Subject"].toString(); |
1318 | + if (!newSubject.isEmpty() && storedSubject != newSubject) { |
1319 | + //see if we have an actor. If actor is 'me', we have changed that subject |
1320 | + QString actor = thread[History::FieldChatRoomInfo].toMap()["Actor"].toString(); |
1321 | + if (actor == "me") { |
1322 | + actor = "self"; |
1323 | + } |
1324 | + writeInformationEvent(thread, History::InformationTypeTitleChanged, newSubject, actor); |
1325 | + } |
1326 | + } |
1327 | +} |
1328 | + |
1329 | +void HistoryDaemon::writeRolesInformationEvents(const QVariantMap &thread, const Tp::ChannelPtr &channel, const RolesMap &rolesMap) |
1330 | +{ |
1331 | + if (thread.isEmpty()) { |
1332 | + return; |
1333 | + } |
1334 | + |
1335 | + if (!thread[History::FieldChatRoomInfo].toMap()["Joined"].toBool()) { |
1336 | + return; |
1337 | + } |
1338 | + |
1339 | + // list of identifiers for current channel admins |
1340 | + QStringList adminIds; |
1341 | + |
1342 | + Q_FOREACH(const Tp::ContactPtr contact, channel->groupContacts(false)) { |
1343 | + // see if admin role (ChannelAdminRole == 2) |
1344 | + if (rolesMap[contact->handle().at(0)] & AdminRole) { |
1345 | + adminIds << contact->id(); |
1346 | + } |
1347 | + } |
1348 | + |
1349 | + Q_FOREACH (QVariant participant, thread[History::FieldParticipants].toList()) { |
1350 | + QString participantId = participant.toMap()[History::FieldIdentifier].toString(); |
1351 | + if (adminIds.contains(participantId)) { |
1352 | + // see if already was admin or not (ChannelAdminRole == 2) |
1353 | + if (! (participant.toMap()[History::FieldParticipantRoles].toUInt() & AdminRole)) { |
1354 | + writeInformationEvent(thread, History::InformationTypeAdminGranted, participantId); |
1355 | + } |
1356 | + } |
1357 | + } |
1358 | + |
1359 | + //evaluate now self roles |
1360 | + if (rolesMap[channel->groupSelfContact()->handle().at(0)] & AdminRole) { |
1361 | + uint selfRoles = thread[History::FieldChatRoomInfo].toMap()["SelfRoles"].toUInt(); |
1362 | + if (! (selfRoles & AdminRole)) { |
1363 | + writeInformationEvent(thread, History::InformationTypeSelfAdminGranted); |
1364 | + } |
1365 | + } |
1366 | +} |
1367 | |
1368 | === modified file 'daemon/historydaemon.h' |
1369 | --- daemon/historydaemon.h 2015-11-20 11:45:07 +0000 |
1370 | +++ daemon/historydaemon.h 2016-11-24 12:52:24 +0000 |
1371 | @@ -1,5 +1,5 @@ |
1372 | /* |
1373 | - * Copyright (C) 2013 Canonical, Ltd. |
1374 | + * Copyright (C) 2013-2016 Canonical, Ltd. |
1375 | * |
1376 | * Authors: |
1377 | * Gustavo Pichorim Boiko <gustavo.boiko@canonical.com> |
1378 | @@ -30,6 +30,9 @@ |
1379 | #include "callchannelobserver.h" |
1380 | #include "historyservicedbus.h" |
1381 | #include "plugin.h" |
1382 | +#include "rolesinterface.h" |
1383 | + |
1384 | +typedef QMap<uint,uint> RolesMap; |
1385 | |
1386 | class HistoryDaemon : public QObject |
1387 | { |
1388 | @@ -39,19 +42,19 @@ |
1389 | |
1390 | static HistoryDaemon *instance(); |
1391 | |
1392 | - static QStringList participantsFromChannel(const Tp::TextChannelPtr &textChannel); |
1393 | - QVariantMap threadForParticipants(const QString &accountId, |
1394 | - History::EventType type, |
1395 | - const QStringList &participants, |
1396 | - History::MatchFlags matchFlags = History::MatchCaseSensitive, |
1397 | - bool create = true); |
1398 | + static QVariantMap propertiesFromChannel(const Tp::ChannelPtr &textChannel); |
1399 | + QVariantMap threadForProperties(const QString &accountId, |
1400 | + History::EventType type, |
1401 | + const QVariantMap &properties, |
1402 | + History::MatchFlags matchFlags = History::MatchCaseSensitive, |
1403 | + bool create = true); |
1404 | QString queryThreads(int type, const QVariantMap &sort, const QVariantMap &filter, const QVariantMap &properties); |
1405 | QString queryEvents(int type, const QVariantMap &sort, const QVariantMap &filter); |
1406 | QVariantMap getSingleThread(int type, const QString &accountId, const QString &threadId, const QVariantMap &properties); |
1407 | QVariantMap getSingleEvent(int type, const QString &accountId, const QString &threadId, const QString &eventId); |
1408 | QVariantMap getSingleEventFromTextChannel(const Tp::TextChannelPtr textChannel, const QString &messageId); |
1409 | |
1410 | - bool writeEvents(const QList<QVariantMap> &events); |
1411 | + bool writeEvents(const QList<QVariantMap> &events, const QVariantMap &properties); |
1412 | bool removeEvents(const QList<QVariantMap> &events); |
1413 | bool removeThreads(const QList<QVariantMap> &threads); |
1414 | |
1415 | @@ -61,11 +64,27 @@ |
1416 | void onMessageReceived(const Tp::TextChannelPtr textChannel, const Tp::ReceivedMessage &message); |
1417 | void onMessageRead(const Tp::TextChannelPtr textChannel, const Tp::ReceivedMessage &message); |
1418 | void onMessageSent(const Tp::TextChannelPtr textChannel, const Tp::Message &message, const QString &messageToken); |
1419 | + void onTextChannelAvailable(const Tp::TextChannelPtr channel); |
1420 | + void onRoomPropertiesChanged(const QVariantMap &properties,const QStringList &invalidated); |
1421 | + void onGroupMembersChanged(const Tp::Contacts &groupMembersAdded, const Tp::Contacts &groupLocalPendingMembersAdded, |
1422 | + const Tp::Contacts &groupRemotePendingMembersAdded, const Tp::Contacts &groupMembersRemoved, |
1423 | + const Tp::Channel::GroupMemberChangeDetails &details); |
1424 | + void onRolesChanged(const HandleRolesMap &added, const HandleRolesMap &removed); |
1425 | |
1426 | protected: |
1427 | History::MatchFlags matchFlagsForChannel(const Tp::ChannelPtr &channel); |
1428 | + void updateRoomParticipants(const Tp::TextChannelPtr channel); |
1429 | + void updateRoomRoles(const Tp::TextChannelPtr &channel, const RolesMap &rolesMap); |
1430 | QString hashThread(const QVariantMap &thread); |
1431 | - |
1432 | + static QVariantMap getInterfaceProperties(const Tp::AbstractInterface *interface); |
1433 | + void updateRoomProperties(const Tp::TextChannelPtr &channel, const QVariantMap &properties); |
1434 | + void updateRoomProperties(const QString &accountId, const QString &threadId, History::EventType type, const QVariantMap &properties, const QStringList &invalidated); |
1435 | + |
1436 | + void writeInformationEvent(const QVariantMap &thread, History::InformationType type, const QString &subject = QString(), const QString &sender = QString("self"), const QString &text = QString()); |
1437 | + |
1438 | + void writeRoomChangesInformationEvents(const QVariantMap &thread, const QVariantMap &interfaceProperties); |
1439 | + void writeRolesInformationEvents(const QVariantMap &thread, const Tp::ChannelPtr &channel, const RolesMap &rolesMap); |
1440 | + void writeRolesChangesInformationEvents(const QVariantMap &thread, const Tp::ChannelPtr &channel, const RolesMap &rolesMap); |
1441 | private: |
1442 | HistoryDaemon(QObject *parent = 0); |
1443 | |
1444 | |
1445 | === modified file 'daemon/historyservicedbus.cpp' |
1446 | --- daemon/historyservicedbus.cpp 2015-10-01 19:44:45 +0000 |
1447 | +++ daemon/historyservicedbus.cpp 2016-11-24 12:52:24 +0000 |
1448 | @@ -1,5 +1,5 @@ |
1449 | /* |
1450 | - * Copyright (C) 2013 Canonical, Ltd. |
1451 | + * Copyright (C) 2013-2016 Canonical, Ltd. |
1452 | * |
1453 | * Authors: |
1454 | * Gustavo Pichorim Boiko <gustavo.boiko@canonical.com> |
1455 | @@ -34,16 +34,15 @@ |
1456 | |
1457 | bool HistoryServiceDBus::connectToBus() |
1458 | { |
1459 | - bool ok = QDBusConnection::sessionBus().registerService(History::DBusService); |
1460 | - if (!ok) { |
1461 | - return false; |
1462 | - } |
1463 | - |
1464 | if (!mAdaptor) { |
1465 | mAdaptor = new HistoryServiceAdaptor(this); |
1466 | } |
1467 | |
1468 | - return QDBusConnection::sessionBus().registerObject(History::DBusObjectPath, this); |
1469 | + if (!QDBusConnection::sessionBus().registerObject(History::DBusObjectPath, this)) { |
1470 | + return false; |
1471 | + } |
1472 | + |
1473 | + return QDBusConnection::sessionBus().registerService(History::DBusService); |
1474 | } |
1475 | |
1476 | void HistoryServiceDBus::notifyThreadsAdded(const QList<QVariantMap> &threads) |
1477 | @@ -76,58 +75,67 @@ |
1478 | Q_EMIT EventsRemoved(events); |
1479 | } |
1480 | |
1481 | +QVariantMap HistoryServiceDBus::ThreadForProperties(const QString &accountId, |
1482 | + int type, |
1483 | + const QVariantMap &properties, |
1484 | + int matchFlags, |
1485 | + bool create) |
1486 | +{ |
1487 | + return HistoryDaemon::instance()->threadForProperties(accountId, |
1488 | + (History::EventType) type, |
1489 | + properties, |
1490 | + (History::MatchFlags) matchFlags, |
1491 | + create); |
1492 | +} |
1493 | + |
1494 | QVariantMap HistoryServiceDBus::ThreadForParticipants(const QString &accountId, |
1495 | int type, |
1496 | const QStringList &participants, |
1497 | int matchFlags, |
1498 | bool create) |
1499 | { |
1500 | - return HistoryDaemon::instance()->threadForParticipants(accountId, |
1501 | + QVariantMap properties; |
1502 | + properties[History::FieldParticipants] = participants; |
1503 | + |
1504 | + return HistoryDaemon::instance()->threadForProperties(accountId, |
1505 | (History::EventType) type, |
1506 | - participants, |
1507 | + properties, |
1508 | (History::MatchFlags) matchFlags, |
1509 | create); |
1510 | } |
1511 | |
1512 | bool HistoryServiceDBus::WriteEvents(const QList<QVariantMap> &events) |
1513 | { |
1514 | - qDebug() << __PRETTY_FUNCTION__; |
1515 | - return HistoryDaemon::instance()->writeEvents(events); |
1516 | + return HistoryDaemon::instance()->writeEvents(events, QVariantMap()); |
1517 | } |
1518 | |
1519 | bool HistoryServiceDBus::RemoveThreads(const QList<QVariantMap> &threads) |
1520 | { |
1521 | - qDebug() << __PRETTY_FUNCTION__; |
1522 | return HistoryDaemon::instance()->removeThreads(threads); |
1523 | } |
1524 | |
1525 | bool HistoryServiceDBus::RemoveEvents(const QList<QVariantMap> &events) |
1526 | { |
1527 | - qDebug() << __PRETTY_FUNCTION__; |
1528 | return HistoryDaemon::instance()->removeEvents(events); |
1529 | } |
1530 | |
1531 | QString HistoryServiceDBus::QueryThreads(int type, const QVariantMap &sort, const QVariantMap &filter, const QVariantMap &properties) |
1532 | { |
1533 | - qDebug() << __PRETTY_FUNCTION__; |
1534 | return HistoryDaemon::instance()->queryThreads(type, sort, filter, properties); |
1535 | } |
1536 | |
1537 | QString HistoryServiceDBus::QueryEvents(int type, const QVariantMap &sort, const QVariantMap &filter) |
1538 | { |
1539 | - qDebug() << __PRETTY_FUNCTION__; |
1540 | return HistoryDaemon::instance()->queryEvents(type, sort, filter); |
1541 | } |
1542 | |
1543 | QVariantMap HistoryServiceDBus::GetSingleThread(int type, const QString &accountId, const QString &threadId, const QVariantMap &properties) |
1544 | { |
1545 | - qDebug() << __PRETTY_FUNCTION__; |
1546 | return HistoryDaemon::instance()->getSingleThread(type, accountId, threadId, properties); |
1547 | } |
1548 | |
1549 | QVariantMap HistoryServiceDBus::GetSingleEvent(int type, const QString &accountId, const QString &threadId, const QString &eventId) |
1550 | { |
1551 | - qDebug() << __PRETTY_FUNCTION__; |
1552 | return HistoryDaemon::instance()->getSingleEvent(type, accountId, threadId, eventId); |
1553 | } |
1554 | |
1555 | |
1556 | === modified file 'daemon/historyservicedbus.h' |
1557 | --- daemon/historyservicedbus.h 2015-09-23 15:08:07 +0000 |
1558 | +++ daemon/historyservicedbus.h 2016-11-24 12:52:24 +0000 |
1559 | @@ -1,5 +1,5 @@ |
1560 | /* |
1561 | - * Copyright (C) 2013 Canonical, Ltd. |
1562 | + * Copyright (C) 2013-2016 Canonical, Ltd. |
1563 | * |
1564 | * Authors: |
1565 | * Gustavo Pichorim Boiko <gustavo.boiko@canonical.com> |
1566 | @@ -50,6 +50,11 @@ |
1567 | const QStringList &participants, |
1568 | int matchFlags, |
1569 | bool create); |
1570 | + QVariantMap ThreadForProperties(const QString &accountId, |
1571 | + int type, |
1572 | + const QVariantMap &properties, |
1573 | + int matchFlags, |
1574 | + bool create); |
1575 | bool WriteEvents(const QList <QVariantMap> &events); |
1576 | bool RemoveThreads(const QList <QVariantMap> &threads); |
1577 | bool RemoveEvents(const QList <QVariantMap> &events); |
1578 | |
1579 | === added file 'daemon/rolesinterface.cpp' |
1580 | --- daemon/rolesinterface.cpp 1970-01-01 00:00:00 +0000 |
1581 | +++ daemon/rolesinterface.cpp 2016-11-24 12:52:24 +0000 |
1582 | @@ -0,0 +1,71 @@ |
1583 | +/* |
1584 | + * Copyright (C) 2016 Canonical, Ltd. |
1585 | + * |
1586 | + * Authors: |
1587 | + * Roberto Mier Escandon <roberto.escandon@canonical.com> |
1588 | + * |
1589 | + * This file is part of history-service. |
1590 | + * |
1591 | + * history-service is free software; you can redistribute it and/or modify |
1592 | + * it under the terms of the GNU General Public License as published by |
1593 | + * the Free Software Foundation; version 3. |
1594 | + * |
1595 | + * history-service is distributed in the hope that it will be useful, |
1596 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
1597 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1598 | + * GNU General Public License for more details. |
1599 | + * |
1600 | + * You should have received a copy of the GNU General Public License |
1601 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1602 | + */ |
1603 | + |
1604 | +#include <daemon/rolesinterface.h> |
1605 | + |
1606 | +ChannelInterfaceRolesInterface::ChannelInterfaceRolesInterface(const QString& busName, const QString& objectPath, QObject *parent) |
1607 | + : Tp::AbstractInterface(busName, objectPath, staticInterfaceName(), QDBusConnection::sessionBus(), parent) |
1608 | +{ |
1609 | +} |
1610 | + |
1611 | +ChannelInterfaceRolesInterface::ChannelInterfaceRolesInterface(const QDBusConnection& connection, const QString& busName, const QString& objectPath, QObject *parent) |
1612 | + : Tp::AbstractInterface(busName, objectPath, staticInterfaceName(), connection, parent) |
1613 | +{ |
1614 | +} |
1615 | + |
1616 | +ChannelInterfaceRolesInterface::ChannelInterfaceRolesInterface(Tp::DBusProxy *proxy) |
1617 | + : Tp::AbstractInterface(proxy, staticInterfaceName()) |
1618 | +{ |
1619 | +} |
1620 | + |
1621 | +ChannelInterfaceRolesInterface::ChannelInterfaceRolesInterface(const Tp::Client::ChannelInterface& mainInterface) |
1622 | + : Tp::AbstractInterface(mainInterface.service(), mainInterface.path(), staticInterfaceName(), mainInterface.connection(), mainInterface.parent()) |
1623 | +{ |
1624 | +} |
1625 | + |
1626 | +ChannelInterfaceRolesInterface::ChannelInterfaceRolesInterface(const Tp::Client::ChannelInterface& mainInterface, QObject *parent) |
1627 | + : Tp::AbstractInterface(mainInterface.service(), mainInterface.path(), staticInterfaceName(), mainInterface.connection(), parent) |
1628 | +{ |
1629 | +} |
1630 | + |
1631 | +void ChannelInterfaceRolesInterface::invalidate(Tp::DBusProxy *proxy, |
1632 | + const QString &error, const QString &message) |
1633 | +{ |
1634 | + Tp::AbstractInterface::invalidate(proxy, error, message); |
1635 | +} |
1636 | + |
1637 | +HandleRolesMap ChannelInterfaceRolesInterface::getRoles() const |
1638 | +{ |
1639 | + QDBusMessage msg = QDBusMessage::createMethodCall(service(), path(), |
1640 | + TP_QT_IFACE_PROPERTIES, QLatin1String("Get")); |
1641 | + msg << interface() << QLatin1String("Roles"); |
1642 | + QDBusMessage result = connection().call(msg); |
1643 | + return qdbus_cast<HandleRolesMap>(result.arguments().at(0).value<QDBusVariant>().variant()); |
1644 | +} |
1645 | + |
1646 | +bool ChannelInterfaceRolesInterface::getCanUpdateRoles() const |
1647 | +{ |
1648 | + QDBusMessage msg = QDBusMessage::createMethodCall(service(), path(), |
1649 | + TP_QT_IFACE_PROPERTIES, QLatin1String("Get")); |
1650 | + msg << interface() << QLatin1String("CanUpdateRoles"); |
1651 | + QDBusMessage result = connection().call(msg); |
1652 | + return qdbus_cast<bool>(result.arguments().at(0).value<QDBusVariant>().variant()); |
1653 | +} |
1654 | |
1655 | === added file 'daemon/rolesinterface.h' |
1656 | --- daemon/rolesinterface.h 1970-01-01 00:00:00 +0000 |
1657 | +++ daemon/rolesinterface.h 2016-11-24 12:52:24 +0000 |
1658 | @@ -0,0 +1,210 @@ |
1659 | +/* |
1660 | + * Copyright (C) 2016 Canonical, Ltd. |
1661 | + * |
1662 | + * Authors: |
1663 | + * Roberto Mier Escandon <roberto.escandon@canonical.com> |
1664 | + * |
1665 | + * This file is part of history-service. |
1666 | + * |
1667 | + * history-service is free software; you can redistribute it and/or modify |
1668 | + * it under the terms of the GNU General Public License as published by |
1669 | + * the Free Software Foundation; version 3. |
1670 | + * |
1671 | + * history-service is distributed in the hope that it will be useful, |
1672 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
1673 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1674 | + * GNU General Public License for more details. |
1675 | + * |
1676 | + * You should have received a copy of the GNU General Public License |
1677 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1678 | + */ |
1679 | + |
1680 | +#ifndef CHANNELINTERFACEROLESINTERFACE_H |
1681 | +#define CHANNELINTERFACEROLESINTERFACE_H |
1682 | + |
1683 | +#include <QMap> |
1684 | + |
1685 | +#include <TelepathyQt/AbstractInterface> |
1686 | +#include <TelepathyQt/ChannelInterface> |
1687 | + |
1688 | +/** |
1689 | + * \struct HandleRolesMap |
1690 | + * \ingroup mapping |
1691 | + * \headerfile TelepathyQt/types.h <TelepathyQt/Types> |
1692 | + * |
1693 | + * Convertible with |
1694 | + * QMap<uint, uint>, but needed to have a discrete type in the Qt type system. |
1695 | + * |
1696 | + * A map from channel-specific handles to their owners. |
1697 | + */ |
1698 | +struct HandleRolesMap : public QMap<uint, uint> |
1699 | +{ |
1700 | + inline HandleRolesMap() : QMap<uint, uint>() {} |
1701 | + inline HandleRolesMap(const QMap<uint, uint>& a) : QMap<uint, uint>(a) {} |
1702 | + |
1703 | + inline HandleRolesMap& operator=(const QMap<uint, uint>& a) |
1704 | + { |
1705 | + *(static_cast<QMap<uint, uint>*>(this)) = a; |
1706 | + return *this; |
1707 | + } |
1708 | +}; |
1709 | + |
1710 | +Q_DECLARE_METATYPE(HandleRolesMap) |
1711 | + |
1712 | +class ChannelInterfaceRolesInterface : public Tp::AbstractInterface |
1713 | +{ |
1714 | + Q_OBJECT |
1715 | +public: |
1716 | + |
1717 | + /** |
1718 | + * Returns the name of the interface "org.freedesktop.Telepathy.Channel.Interface.Roles", which this class |
1719 | + * represents. |
1720 | + * |
1721 | + * \return The D-Bus interface name. |
1722 | + */ |
1723 | + static inline QLatin1String staticInterfaceName() |
1724 | + { |
1725 | + return QLatin1String("org.freedesktop.Telepathy.Channel.Interface.Roles"); |
1726 | + } |
1727 | + |
1728 | + /** |
1729 | + * Creates a ChannelInterfaceRolesInterface associated with the given object on the session bus. |
1730 | + * |
1731 | + * \param busName Name of the service the object is on. |
1732 | + * \param objectPath Path to the object on the service. |
1733 | + * \param parent Passed to the parent class constructor. |
1734 | + */ |
1735 | + ChannelInterfaceRolesInterface( |
1736 | + const QString& busName, |
1737 | + const QString& objectPath, |
1738 | + QObject* parent = 0 |
1739 | + ); |
1740 | + |
1741 | + /** |
1742 | + * Creates a ChannelInterfaceRolesInterface associated with the given object on the given bus. |
1743 | + * |
1744 | + * \param connection The bus via which the object can be reached. |
1745 | + * \param busName Name of the service the object is on. |
1746 | + * \param objectPath Path to the object on the service. |
1747 | + * \param parent Passed to the parent class constructor. |
1748 | + */ |
1749 | + ChannelInterfaceRolesInterface( |
1750 | + const QDBusConnection& connection, |
1751 | + const QString& busName, |
1752 | + const QString& objectPath, |
1753 | + QObject* parent = 0 |
1754 | + ); |
1755 | + |
1756 | + /** |
1757 | + * Creates a ChannelInterfaceRolesInterface associated with the same object as the given proxy. |
1758 | + * |
1759 | + * \param proxy The proxy to use. It will also be the QObject::parent() |
1760 | + * for this object. |
1761 | + */ |
1762 | + ChannelInterfaceRolesInterface(Tp::DBusProxy *proxy); |
1763 | + |
1764 | + /** |
1765 | + * Creates a ChannelInterfaceRolesInterface associated with the same object as the given proxy. |
1766 | + * Additionally, the created proxy will have the same parent as the given |
1767 | + * proxy. |
1768 | + * |
1769 | + * \param mainInterface The proxy to use. |
1770 | + */ |
1771 | + explicit ChannelInterfaceRolesInterface(const Tp::Client::ChannelInterface& mainInterface); |
1772 | + |
1773 | + /** |
1774 | + * Creates a ChannelInterfaceRolesInterface associated with the same object as the given proxy. |
1775 | + * However, a different parent object can be specified. |
1776 | + * |
1777 | + * \param mainInterface The proxy to use. |
1778 | + * \param parent Passed to the parent class constructor. |
1779 | + */ |
1780 | + ChannelInterfaceRolesInterface(const Tp::Client::ChannelInterface& mainInterface, QObject* parent); |
1781 | + |
1782 | + /** |
1783 | + * Asynchronous getter for the remote object property \c Roles of type \c HandleRolesMap. |
1784 | + * |
1785 | + * \return A pending variant which will emit finished when the property has been |
1786 | + * retrieved. |
1787 | + */ |
1788 | + inline Tp::PendingVariant *requestPropertyRoles() const |
1789 | + { |
1790 | + return internalRequestProperty(QLatin1String("Roles")); |
1791 | + } |
1792 | + |
1793 | + /** |
1794 | + * Asynchronous getter for the remote object property \c CanUpdateRoles of type \c bool. |
1795 | + * |
1796 | + * \return A pending variant which will emit finished when the property has been |
1797 | + * retrieved. |
1798 | + */ |
1799 | + inline Tp::PendingVariant *requestPropertyCanUpdateRoles() const |
1800 | + { |
1801 | + return internalRequestProperty(QLatin1String("CanUpdateRoles")); |
1802 | + } |
1803 | + |
1804 | + /** |
1805 | + * Request all of the DBus properties on the interface. |
1806 | + * |
1807 | + * \return A pending variant map which will emit finished when the properties have |
1808 | + * been retrieved. |
1809 | + */ |
1810 | + Tp::PendingVariantMap *requestAllProperties() const |
1811 | + { |
1812 | + return internalRequestAllProperties(); |
1813 | + } |
1814 | + |
1815 | + /** |
1816 | + * Synchronous version to get Roles property |
1817 | + */ |
1818 | + HandleRolesMap getRoles() const; |
1819 | + |
1820 | + /** |
1821 | + * Synchronous version to get CanUpdateRoles property |
1822 | + */ |
1823 | + bool getCanUpdateRoles() const; |
1824 | + |
1825 | +public Q_SLOTS: |
1826 | + /** |
1827 | + * Begins a call to the D-Bus method \c UpdateRoles on the remote object. |
1828 | + * |
1829 | + * Update the roles in the server |
1830 | + * |
1831 | + */ |
1832 | + inline QDBusPendingReply<> UpdateRoles(const HandleRolesMap &contactRoles, int timeout = -1) |
1833 | + { |
1834 | + if (!invalidationReason().isEmpty()) { |
1835 | + return QDBusPendingReply<>(QDBusMessage::createError( |
1836 | + invalidationReason(), |
1837 | + invalidationMessage() |
1838 | + )); |
1839 | + } |
1840 | + |
1841 | + QDBusMessage callMessage = QDBusMessage::createMethodCall(this->service(), this->path(), |
1842 | + this->staticInterfaceName(), QLatin1String("UpdateRoles")); |
1843 | + callMessage << QVariant::fromValue(contactRoles); |
1844 | + return this->connection().asyncCall(callMessage, timeout); |
1845 | + } |
1846 | + |
1847 | +Q_SIGNALS: |
1848 | + /** |
1849 | + * Represents the signal \c RolesChanged on the remote object. |
1850 | + * |
1851 | + * Emitted when the state the roles of the channel has changed. |
1852 | + * |
1853 | + * \param added |
1854 | + * |
1855 | + * map of handles and related roles added |
1856 | + * |
1857 | + * \param removed |
1858 | + * |
1859 | + * map of handles and related roles removed |
1860 | + */ |
1861 | + void RolesChanged(const HandleRolesMap &added, const HandleRolesMap& removed); |
1862 | + |
1863 | +protected: |
1864 | + virtual void invalidate(Tp::DBusProxy *, const QString &, const QString &); |
1865 | + |
1866 | +}; |
1867 | + |
1868 | +#endif // CHANNELINTERFACEROLESINTERFACE_H |
1869 | |
1870 | === modified file 'daemon/textchannelobserver.cpp' |
1871 | --- daemon/textchannelobserver.cpp 2014-09-10 04:28:32 +0000 |
1872 | +++ daemon/textchannelobserver.cpp 2016-11-24 12:52:24 +0000 |
1873 | @@ -1,5 +1,5 @@ |
1874 | /* |
1875 | - * Copyright (C) 2012-2013 Canonical, Ltd. |
1876 | + * Copyright (C) 2012-2016 Canonical, Ltd. |
1877 | * |
1878 | * Authors: |
1879 | * Gustavo Pichorim Boiko <gustavo.boiko@canonical.com> |
1880 | @@ -43,6 +43,8 @@ |
1881 | SIGNAL(pendingMessageRemoved(const Tp::ReceivedMessage&)), |
1882 | SLOT(onPendingMessageRemoved(const Tp::ReceivedMessage&))); |
1883 | |
1884 | + Q_EMIT channelAvailable(textChannel); |
1885 | + |
1886 | // process the messages that are already pending in the channel |
1887 | Q_FOREACH(const Tp::ReceivedMessage &message, textChannel->messageQueue()) { |
1888 | Q_EMIT messageReceived(textChannel, message); |
1889 | |
1890 | === modified file 'daemon/textchannelobserver.h' |
1891 | --- daemon/textchannelobserver.h 2013-07-12 14:30:18 +0000 |
1892 | +++ daemon/textchannelobserver.h 2016-11-24 12:52:24 +0000 |
1893 | @@ -1,5 +1,5 @@ |
1894 | /* |
1895 | - * Copyright (C) 2012-2013 Canonical, Ltd. |
1896 | + * Copyright (C) 2012-2016 Canonical, Ltd. |
1897 | * |
1898 | * Authors: |
1899 | * Gustavo Pichorim Boiko <gustavo.boiko@canonical.com> |
1900 | @@ -36,6 +36,7 @@ |
1901 | void onTextChannelAvailable(Tp::TextChannelPtr textChannel); |
1902 | |
1903 | Q_SIGNALS: |
1904 | + void channelAvailable(const Tp::TextChannelPtr textChannel); |
1905 | void messageReceived(const Tp::TextChannelPtr textChannel, const Tp::ReceivedMessage &message); |
1906 | void messageRead(const Tp::TextChannelPtr textChannel, const Tp::ReceivedMessage &message); |
1907 | void messageSent(const Tp::TextChannelPtr textChannel, const Tp::Message &message, const QString &messageToken); |
1908 | |
1909 | === added file 'plugins/sqlite/schema/v13.sql' |
1910 | --- plugins/sqlite/schema/v13.sql 1970-01-01 00:00:00 +0000 |
1911 | +++ plugins/sqlite/schema/v13.sql 2016-11-24 12:52:24 +0000 |
1912 | @@ -0,0 +1,44 @@ |
1913 | +ALTER TABLE threads ADD COLUMN chatType tinyint; |
1914 | +ALTER TABLE thread_participants ADD COLUMN alias varchar(255); |
1915 | +ALTER TABLE thread_participants ADD COLUMN state tinyint; |
1916 | +CREATE TABLE chat_room_info ( |
1917 | + accountId varchar(255), |
1918 | + type tinyint, |
1919 | + threadId varchar(255), |
1920 | + roomName varchar(255), |
1921 | + server varchar(255), |
1922 | + creator varchar(255), |
1923 | + creationTimestamp datetime, |
1924 | + anonymous boolean, |
1925 | + inviteOnly boolean, |
1926 | + participantLimit integer, |
1927 | + moderated boolean, |
1928 | + title varchar(1024), |
1929 | + description varchar(1024), |
1930 | + persistent boolean, |
1931 | + private boolean, |
1932 | + passwordProtected boolean, |
1933 | + password varchar(512), |
1934 | + passwordHint varchar(512), |
1935 | + canUpdateConfiguration boolean, |
1936 | + subject varchar(1024), |
1937 | + actor varchar(512), |
1938 | + timestamp datetime |
1939 | +); |
1940 | +UPDATE threads SET chatType = 0; |
1941 | +UPDATE 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; |
1942 | +UPDATE thread_participants SET state = 0; |
1943 | + |
1944 | +DROP TRIGGER threads_delete_trigger; |
1945 | +CREATE TRIGGER threads_delete_trigger AFTER DELETE ON threads |
1946 | +FOR EACH ROW |
1947 | +BEGIN |
1948 | + DELETE FROM thread_participants WHERE |
1949 | + accountId=old.accountId AND |
1950 | + threadId=old.threadId AND |
1951 | + type=old.type; |
1952 | + DELETE FROM chat_room_info WHERE |
1953 | + accountId=old.accountId AND |
1954 | + threadId=old.threadId AND |
1955 | + type=old.type; |
1956 | +END; |
1957 | |
1958 | === added file 'plugins/sqlite/schema/v14.sql' |
1959 | --- plugins/sqlite/schema/v14.sql 1970-01-01 00:00:00 +0000 |
1960 | +++ plugins/sqlite/schema/v14.sql 2016-11-24 12:52:24 +0000 |
1961 | @@ -0,0 +1,82 @@ |
1962 | +DROP TRIGGER text_events_insert_trigger; |
1963 | +CREATE TRIGGER text_events_insert_trigger AFTER INSERT ON text_events |
1964 | +FOR EACH ROW WHEN new.messageType!=2 |
1965 | +BEGIN |
1966 | + UPDATE threads SET count=(SELECT count(eventId) FROM text_events WHERE |
1967 | + accountId=new.accountId AND |
1968 | + threadId=new.threadId AND |
1969 | + messageType!=2) |
1970 | + WHERE accountId=new.accountId AND threadId=new.threadId AND type=0; |
1971 | + UPDATE threads SET unreadCount=(SELECT count(eventId) FROM text_events WHERE |
1972 | + accountId=new.accountId AND threadId=new.threadId AND newEvent='1' AND messageType!=2) |
1973 | + WHERE accountId=new.accountId AND threadId=new.threadId AND type=0; |
1974 | + UPDATE threads SET lastEventId=(SELECT eventId FROM text_events WHERE |
1975 | + accountId=new.accountId AND |
1976 | + threadId=new.threadId AND |
1977 | + messageType!=2 |
1978 | + ORDER BY timestamp DESC LIMIT 1) |
1979 | + WHERE accountId=new.accountId AND threadId=new.threadId AND type=0; |
1980 | + UPDATE threads SET lastEventTimestamp=(SELECT timestamp FROM text_events WHERE |
1981 | + accountId=new.accountId AND |
1982 | + threadId=new.threadId AND |
1983 | + messageType!=2 |
1984 | + ORDER BY timestamp DESC LIMIT 1) |
1985 | + WHERE accountId=new.accountId AND threadId=new.threadId AND type=0; |
1986 | +END; |
1987 | + |
1988 | +DROP TRIGGER text_events_update_trigger; |
1989 | +CREATE TRIGGER text_events_update_trigger AFTER UPDATE ON text_events |
1990 | +FOR EACH ROW WHEN new.messageType!=2 |
1991 | +BEGIN |
1992 | + UPDATE threads SET count=(SELECT count(eventId) FROM text_events WHERE |
1993 | + accountId=new.accountId AND |
1994 | + threadId=new.threadId AND |
1995 | + messageType!=2) |
1996 | + WHERE accountId=new.accountId AND threadId=new.threadId AND type=0; |
1997 | + UPDATE threads SET unreadCount=(SELECT count(eventId) FROM text_events WHERE |
1998 | + accountId=new.accountId AND threadId=new.threadId AND newEvent='1' AND messageType!=2) |
1999 | + WHERE accountId=new.accountId AND threadId=new.threadId AND type=0; |
2000 | + UPDATE threads SET lastEventId=(SELECT eventId FROM text_events WHERE |
2001 | + accountId=new.accountId AND |
2002 | + threadId=new.threadId AND |
2003 | + messageType!=2 |
2004 | + ORDER BY timestamp DESC LIMIT 1) |
2005 | + WHERE accountId=new.accountId AND threadId=new.threadId AND type=0; |
2006 | + UPDATE threads SET lastEventTimestamp=(SELECT timestamp FROM text_events WHERE |
2007 | + accountId=new.accountId AND |
2008 | + threadId=new.threadId AND |
2009 | + messageType!=2 |
2010 | + ORDER BY timestamp DESC LIMIT 1) |
2011 | + WHERE accountId=new.accountId AND threadId=new.threadId AND type=0; |
2012 | +END; |
2013 | + |
2014 | +DROP TRIGGER text_events_delete_trigger; |
2015 | +CREATE TRIGGER text_events_delete_trigger AFTER DELETE ON text_events |
2016 | +FOR EACH ROW WHEN old.messageType!=2 |
2017 | +BEGIN |
2018 | + UPDATE threads SET count=(SELECT count(eventId) FROM text_events WHERE |
2019 | + accountId=old.accountId AND |
2020 | + threadId=old.threadId AND |
2021 | + messageType!=2) |
2022 | + WHERE accountId=old.accountId AND threadId=old.threadId AND type=0; |
2023 | + UPDATE threads SET unreadCount=(SELECT count(eventId) FROM text_events WHERE |
2024 | + accountId=old.accountId AND threadId=old.threadId AND newEvent='1' AND messageType!=2) |
2025 | + WHERE accountId=old.accountId AND threadId=old.threadId AND type=0; |
2026 | + UPDATE threads SET lastEventId=(SELECT eventId FROM text_events WHERE |
2027 | + accountId=old.accountId AND |
2028 | + threadId=old.threadId AND |
2029 | + messageType!=2 |
2030 | + ORDER BY timestamp DESC LIMIT 1) |
2031 | + WHERE accountId=old.accountId AND threadId=old.threadId AND type=0; |
2032 | + UPDATE threads SET lastEventTimestamp=(SELECT timestamp FROM text_events WHERE |
2033 | + accountId=old.accountId AND |
2034 | + threadId=old.threadId AND |
2035 | + messageType!=2 |
2036 | + ORDER BY timestamp DESC LIMIT 1) |
2037 | + WHERE accountId=old.accountId AND threadId=old.threadId AND type=0; |
2038 | + DELETE from text_event_attachments WHERE |
2039 | + accountId=old.accountId AND |
2040 | + threadId=old.threadId AND |
2041 | + eventId=old.eventId; |
2042 | +END; |
2043 | + |
2044 | |
2045 | === added file 'plugins/sqlite/schema/v15.sql' |
2046 | --- plugins/sqlite/schema/v15.sql 1970-01-01 00:00:00 +0000 |
2047 | +++ plugins/sqlite/schema/v15.sql 2016-11-24 12:52:24 +0000 |
2048 | @@ -0,0 +1,1 @@ |
2049 | +ALTER TABLE thread_participants ADD COLUMN roles tinyint; |
2050 | |
2051 | === added file 'plugins/sqlite/schema/v16.sql' |
2052 | --- plugins/sqlite/schema/v16.sql 1970-01-01 00:00:00 +0000 |
2053 | +++ plugins/sqlite/schema/v16.sql 2016-11-24 12:52:24 +0000 |
2054 | @@ -0,0 +1,2 @@ |
2055 | +ALTER TABLE chat_room_info ADD COLUMN joined boolean; |
2056 | +ALTER TABLE chat_room_info ADD COLUMN selfRoles integer; |
2057 | |
2058 | === added file 'plugins/sqlite/schema/v17.sql' |
2059 | --- plugins/sqlite/schema/v17.sql 1970-01-01 00:00:00 +0000 |
2060 | +++ plugins/sqlite/schema/v17.sql 2016-11-24 12:52:24 +0000 |
2061 | @@ -0,0 +1,2 @@ |
2062 | +ALTER TABLE text_events ADD COLUMN informationType integer; |
2063 | +UPDATE text_events SET informationType = 1; |
2064 | |
2065 | === modified file 'plugins/sqlite/sqlitedatabase.cpp' |
2066 | --- plugins/sqlite/sqlitedatabase.cpp 2015-10-06 12:54:04 +0000 |
2067 | +++ plugins/sqlite/sqlitedatabase.cpp 2016-11-24 12:52:24 +0000 |
2068 | @@ -207,6 +207,7 @@ |
2069 | |
2070 | QStringList statements; |
2071 | int existingVersion = 0; |
2072 | + int upgradeToVersion = 0; |
2073 | |
2074 | if (create) { |
2075 | statements = parseSchemaFile(":/database/schema/schema.sql"); |
2076 | @@ -219,7 +220,7 @@ |
2077 | } |
2078 | |
2079 | existingVersion = query.value(0).toInt(); |
2080 | - int upgradeToVersion = existingVersion + 1; |
2081 | + upgradeToVersion = existingVersion + 1; |
2082 | while (upgradeToVersion <= mSchemaVersion) { |
2083 | statements += parseSchemaFile(QString(":/database/schema/v%1.sql").arg(QString::number(upgradeToVersion))); |
2084 | ++upgradeToVersion; |
2085 | @@ -256,6 +257,22 @@ |
2086 | return false; |
2087 | } |
2088 | } |
2089 | + if (existingVersion < 13) { |
2090 | + // convert all ofono groups to Room type depending if the mms option is enabled |
2091 | + QVariant mmsGroupChatEnabled = History::Utils::getUserValue("com.ubuntu.touch.AccountsService.Phone", "MmsGroupChatEnabled"); |
2092 | + // we must not fail if we cannot reach accounts service. |
2093 | + if (mmsGroupChatEnabled.isValid()) { |
2094 | + // if mms is disabled all chats will be turned into broadcast, otherwise |
2095 | + // we turn them into Room |
2096 | + if (mmsGroupChatEnabled.toBool()) { |
2097 | + if (!convertOfonoGroupChatToRoom()) { |
2098 | + qCritical() << "Failed to update existing group chats to Room type."; |
2099 | + rollbackTransaction(); |
2100 | + return false; |
2101 | + } |
2102 | + } |
2103 | + } |
2104 | + } |
2105 | } |
2106 | |
2107 | finishTransaction(); |
2108 | @@ -392,3 +409,40 @@ |
2109 | |
2110 | return true; |
2111 | } |
2112 | + |
2113 | +bool SQLiteDatabase::convertOfonoGroupChatToRoom() |
2114 | +{ |
2115 | + QSqlQuery query(database()); |
2116 | + 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"; |
2117 | + |
2118 | + query.prepare(queryText); |
2119 | + if (!query.exec()) { |
2120 | + qWarning() << "Failed to update group chats to Room 1:" << query.executedQuery() << query.lastError(); |
2121 | + return false; |
2122 | + } |
2123 | + query.clear(); |
2124 | + |
2125 | + // now insert a row in chat_room_info for each room |
2126 | + if (!query.exec("SELECT accountId, threadId from threads WHERE accountId LIKE 'ofono/ofono%' AND chatType=2")) { |
2127 | + qWarning() << "Failed to update group chats to Room 2:" << query.executedQuery() << query.lastError(); |
2128 | + return false; |
2129 | + } |
2130 | + |
2131 | + while (query.next()) { |
2132 | + QSqlQuery queryInsertRoom(database()); |
2133 | + QString accountId = query.value(0).toString(); |
2134 | + QString threadId = query.value(1).toString(); |
2135 | + queryInsertRoom.prepare("INSERT INTO chat_room_info (accountId, threadId, type, joined) VALUES (:accountId,:threadId,:type,:joined)"); |
2136 | + queryInsertRoom.bindValue(":accountId", accountId); |
2137 | + queryInsertRoom.bindValue(":threadId", threadId); |
2138 | + queryInsertRoom.bindValue(":type", History::EventTypeText); |
2139 | + queryInsertRoom.bindValue(":joined", true); |
2140 | + if(!queryInsertRoom.exec()) { |
2141 | + qWarning() << "Failed to update group chats to Room 3:" << queryInsertRoom.executedQuery() << queryInsertRoom.lastError(); |
2142 | + return false; |
2143 | + } |
2144 | + queryInsertRoom.clear(); |
2145 | + } |
2146 | + query.clear(); |
2147 | +} |
2148 | + |
2149 | |
2150 | === modified file 'plugins/sqlite/sqlitedatabase.h' |
2151 | --- plugins/sqlite/sqlitedatabase.h 2015-09-02 18:14:15 +0000 |
2152 | +++ plugins/sqlite/sqlitedatabase.h 2016-11-24 12:52:24 +0000 |
2153 | @@ -50,6 +50,7 @@ |
2154 | |
2155 | // data upgrade functions |
2156 | bool changeTimestampsToUtc(); |
2157 | + bool convertOfonoGroupChatToRoom(); |
2158 | |
2159 | private: |
2160 | explicit SQLiteDatabase(QObject *parent = 0); |
2161 | |
2162 | === modified file 'plugins/sqlite/sqlitehistoryplugin.cpp' |
2163 | --- plugins/sqlite/sqlitehistoryplugin.cpp 2015-11-20 11:40:36 +0000 |
2164 | +++ plugins/sqlite/sqlitehistoryplugin.cpp 2016-11-24 12:52:24 +0000 |
2165 | @@ -1,5 +1,5 @@ |
2166 | /* |
2167 | - * Copyright (C) 2013-2015 Canonical, Ltd. |
2168 | + * Copyright (C) 2013-2016 Canonical, Ltd. |
2169 | * |
2170 | * Authors: |
2171 | * Gustavo Pichorim Boiko <gustavo.boiko@canonical.com> |
2172 | @@ -35,17 +35,20 @@ |
2173 | #include <QStringList> |
2174 | #include <QSqlError> |
2175 | #include <QDBusMetaType> |
2176 | +#include <QCryptographicHash> |
2177 | |
2178 | -QString generateThreadMapKey(const History::Thread &thread) |
2179 | -{ |
2180 | - return thread.accountId() + thread.threadId(); |
2181 | -} |
2182 | +static const QLatin1String timestampFormat("yyyy-MM-ddTHH:mm:ss.zzz"); |
2183 | |
2184 | QString generateThreadMapKey(const QString &accountId, const QString &threadId) |
2185 | { |
2186 | return accountId + threadId; |
2187 | } |
2188 | |
2189 | +QString generateThreadMapKey(const History::Thread &thread) |
2190 | +{ |
2191 | + return generateThreadMapKey(thread.accountId(), thread.threadId()); |
2192 | +} |
2193 | + |
2194 | SQLiteHistoryPlugin::SQLiteHistoryPlugin(QObject *parent) : |
2195 | QObject(parent), mInitialised(false) |
2196 | { |
2197 | @@ -81,18 +84,18 @@ |
2198 | // so instead we just convert to UTC here on the cache and convert back to local time |
2199 | // when returning |
2200 | QDateTime timestamp = QDateTime::fromString(properties[History::FieldTimestamp].toString(), Qt::ISODate); |
2201 | - properties[History::FieldTimestamp] = timestamp.toUTC().toString("yyyy-MM-ddTHH:mm:ss.zzz"); |
2202 | + properties[History::FieldTimestamp] = timestamp.toUTC().toString(timestampFormat); |
2203 | |
2204 | // the same for readTimestamp |
2205 | timestamp = QDateTime::fromString(properties[History::FieldReadTimestamp].toString(), Qt::ISODate); |
2206 | - properties[History::FieldReadTimestamp] = timestamp.toUTC().toString("yyyy-MM-ddTHH:mm:ss.zzz"); |
2207 | + properties[History::FieldReadTimestamp] = timestamp.toUTC().toString(timestampFormat); |
2208 | |
2209 | History::Thread thread = History::Thread::fromProperties(properties); |
2210 | const QString &threadKey = generateThreadMapKey(thread); |
2211 | |
2212 | if (thread.type() != History::EventTypeText) { |
2213 | continue; |
2214 | - } else if (!History::Utils::shouldGroupAccount(thread.accountId())) { |
2215 | + } else if (!History::Utils::shouldGroupThread(thread)) { |
2216 | // never group non phone accounts |
2217 | mConversationsCache[threadKey] = History::Threads() << thread; |
2218 | mConversationsCacheKeys[threadKey] = threadKey; |
2219 | @@ -119,6 +122,9 @@ |
2220 | const QString &conversationKey = it.key(); |
2221 | History::Threads groupedThreads = it.value(); |
2222 | Q_FOREACH(const History::Thread &groupedThread, groupedThreads) { |
2223 | + if (!History::Utils::shouldGroupThread(groupedThread) || thread.chatType() != groupedThread.chatType()) { |
2224 | + continue; |
2225 | + } |
2226 | found = History::Utils::compareNormalizedParticipants(thread.participants().identifiers(), groupedThread.participants().identifiers(), History::MatchPhoneNumber); |
2227 | if (found) { |
2228 | Q_FOREACH(const History::Thread &groupedThread, groupedThreads) { |
2229 | @@ -176,7 +182,7 @@ |
2230 | History::Thread thread = History::Thread::fromProperties(properties); |
2231 | QString threadKey = generateThreadMapKey(thread); |
2232 | |
2233 | - if (thread.type() != History::EventTypeText || !History::Utils::shouldGroupAccount(thread.accountId())) { |
2234 | + if (thread.type() != History::EventTypeText || !History::Utils::shouldGroupThread(thread)) { |
2235 | mConversationsCache.remove(threadKey); |
2236 | mConversationsCacheKeys.remove(threadKey); |
2237 | return; |
2238 | @@ -259,7 +265,7 @@ |
2239 | time.start(); |
2240 | qDebug() << "---- HistoryService: start generating cached content"; |
2241 | QSqlQuery query(SQLiteDatabase::instance()->database()); |
2242 | - if (!query.exec("SELECT DISTINCT accountId, normalizedId FROM thread_participants")) { |
2243 | + if (!query.exec("SELECT DISTINCT accountId, normalizedId, alias, state FROM thread_participants")) { |
2244 | qWarning() << "Failed to generate contact cache:" << query.lastError().text(); |
2245 | return; |
2246 | } |
2247 | @@ -267,9 +273,14 @@ |
2248 | while (query.next()) { |
2249 | QString accountId = query.value(0).toString(); |
2250 | QString participantId = query.value(1).toString(); |
2251 | + QString alias = query.value(2).toString(); |
2252 | + QVariantMap properties; |
2253 | + if (!alias.isEmpty()) { |
2254 | + properties[History::FieldAlias] = alias; |
2255 | + } |
2256 | // we don't care about the results, as long as the contact data is present in the cache for |
2257 | // future usage. |
2258 | - History::ContactMatcher::instance()->contactInfo(accountId, participantId, true); |
2259 | + History::ContactMatcher::instance()->contactInfo(accountId, participantId, true, properties); |
2260 | } |
2261 | |
2262 | updateGroupedThreadsCache(); |
2263 | @@ -295,6 +306,32 @@ |
2264 | return new SQLiteHistoryEventView(this, type, sort, filter); |
2265 | } |
2266 | |
2267 | +QVariantMap SQLiteHistoryPlugin::threadForProperties(const QString &accountId, |
2268 | + History::EventType type, |
2269 | + const QVariantMap &properties, |
2270 | + History::MatchFlags matchFlags) |
2271 | +{ |
2272 | + if (properties.isEmpty()) { |
2273 | + return QVariantMap(); |
2274 | + } |
2275 | + |
2276 | + QSqlQuery query(SQLiteDatabase::instance()->database()); |
2277 | + |
2278 | + History::ChatType chatType = (History::ChatType)properties[History::FieldChatType].toUInt(); |
2279 | + |
2280 | + if (chatType == History::ChatTypeRoom) { |
2281 | + QString threadId = properties[History::FieldThreadId].toString(); |
2282 | + if (threadId.isEmpty()) { |
2283 | + return QVariantMap(); |
2284 | + } |
2285 | + return getSingleThread(type, accountId, threadId); |
2286 | + } |
2287 | + |
2288 | + History::Participants participants = History::Participants::fromVariant(properties[History::FieldParticipantIds]); |
2289 | + // if chatType != Room, then we select the thread based on the participant list. |
2290 | + return threadForParticipants(accountId, type, participants.identifiers(), matchFlags); |
2291 | +} |
2292 | + |
2293 | QVariantMap SQLiteHistoryPlugin::threadForParticipants(const QString &accountId, |
2294 | History::EventType type, |
2295 | const QStringList &participants, |
2296 | @@ -310,7 +347,9 @@ |
2297 | // select all the threads the first participant is listed in, and from that list |
2298 | // check if any of the threads has all the other participants listed |
2299 | // FIXME: find a better way to do this |
2300 | - QString queryString("SELECT threadId FROM thread_participants WHERE %1 AND type=:type AND accountId=:accountId"); |
2301 | + QString queryString("SELECT threadId FROM thread_participants WHERE %1 AND type=:type AND accountId=:accountId " |
2302 | + "AND (SELECT chatType FROM threads WHERE threads.threadId=thread_participants.threadId AND " |
2303 | + " threads.type=thread_participants.type)!=:chatType"); |
2304 | |
2305 | // FIXME: for now we just compare differently when using MatchPhoneNumber |
2306 | QString firstParticipant = participants.first(); |
2307 | @@ -324,6 +363,9 @@ |
2308 | query.bindValue(":participantId", firstParticipant); |
2309 | query.bindValue(":type", type); |
2310 | query.bindValue(":accountId", accountId); |
2311 | + // we don't want to accidentally return a chat room for a multi-recipient conversation |
2312 | + query.bindValue(":chatType", (int)History::ChatTypeRoom); |
2313 | + |
2314 | if (!query.exec()) { |
2315 | qCritical() << "Error:" << query.lastError() << query.lastQuery(); |
2316 | return QVariantMap(); |
2317 | @@ -469,58 +511,345 @@ |
2318 | return result; |
2319 | } |
2320 | |
2321 | -// Writer |
2322 | -QVariantMap SQLiteHistoryPlugin::createThreadForParticipants(const QString &accountId, History::EventType type, const QStringList &participants) |
2323 | +bool SQLiteHistoryPlugin::updateRoomParticipants(const QString &accountId, const QString &threadId, History::EventType type, const QVariantList &participants) |
2324 | +{ |
2325 | + QSqlQuery query(SQLiteDatabase::instance()->database()); |
2326 | + if (accountId.isEmpty() || threadId.isEmpty()) { |
2327 | + return false; |
2328 | + } |
2329 | + |
2330 | + SQLiteDatabase::instance()->beginTransation(); |
2331 | + QString deleteString("DELETE FROM thread_participants WHERE threadId=:threadId AND type=:type AND accountId=:accountId"); |
2332 | + query.prepare(deleteString); |
2333 | + query.bindValue(":accountId", accountId); |
2334 | + query.bindValue(":threadId", threadId); |
2335 | + query.bindValue(":type", type); |
2336 | + if (!query.exec()) { |
2337 | + qCritical() << "Error removing old participants:" << query.lastError() << query.lastQuery(); |
2338 | + SQLiteDatabase::instance()->rollbackTransaction(); |
2339 | + return false; |
2340 | + } |
2341 | + |
2342 | + // and insert the participants |
2343 | + Q_FOREACH(const QVariant &participantVariant, participants) { |
2344 | + QVariantMap participant = participantVariant.toMap(); |
2345 | + query.prepare("INSERT INTO thread_participants (accountId, threadId, type, participantId, normalizedId, alias, state, roles)" |
2346 | + "VALUES (:accountId, :threadId, :type, :participantId, :normalizedId, :alias, :state, :roles)"); |
2347 | + query.bindValue(":accountId", accountId); |
2348 | + query.bindValue(":threadId", threadId); |
2349 | + query.bindValue(":type", type); |
2350 | + query.bindValue(":participantId", participant["identifier"].toString()); |
2351 | + query.bindValue(":normalizedId", participant["identifier"].toString()); |
2352 | + query.bindValue(":alias", participant["alias"].toString()); |
2353 | + query.bindValue(":state", participant["state"].toUInt()); |
2354 | + query.bindValue(":roles", participant["roles"].toUInt()); |
2355 | + if (!query.exec()) { |
2356 | + qCritical() << "Error:" << query.lastError() << query.lastQuery(); |
2357 | + SQLiteDatabase::instance()->rollbackTransaction(); |
2358 | + return false; |
2359 | + } |
2360 | + } |
2361 | + |
2362 | + if (!SQLiteDatabase::instance()->finishTransaction()) { |
2363 | + qCritical() << "Failed to commit the transaction."; |
2364 | + return false; |
2365 | + } |
2366 | + |
2367 | + QVariantMap existingThread = getSingleThread(type, |
2368 | + accountId, |
2369 | + threadId, |
2370 | + QVariantMap()); |
2371 | + |
2372 | + if (!existingThread.isEmpty()) { |
2373 | + addThreadsToCache(QList<QVariantMap>() << existingThread); |
2374 | + } |
2375 | + |
2376 | + return true; |
2377 | +} |
2378 | + |
2379 | +bool SQLiteHistoryPlugin::updateRoomParticipantsRoles(const QString &accountId, const QString &threadId, History::EventType type, const QVariantMap &participantsRoles) |
2380 | +{ |
2381 | + QSqlQuery query(SQLiteDatabase::instance()->database()); |
2382 | + if (accountId.isEmpty() || threadId.isEmpty()) { |
2383 | + return false; |
2384 | + } |
2385 | + |
2386 | + SQLiteDatabase::instance()->beginTransation(); |
2387 | + Q_FOREACH(const QString &participantId, participantsRoles.keys()) { |
2388 | + query.prepare("UPDATE thread_participants SET roles=:roles WHERE accountId=:accountId AND threadId=:threadId AND type=:type AND participantId=:participantId"); |
2389 | + query.bindValue(":roles", participantsRoles.value(participantId).toUInt()); |
2390 | + query.bindValue(":accountId", accountId); |
2391 | + query.bindValue(":threadId", threadId); |
2392 | + query.bindValue(":type", type); |
2393 | + query.bindValue(":participantId", participantId); |
2394 | + if (!query.exec()) { |
2395 | + qCritical() << "Error:" << query.lastError() << query.lastQuery(); |
2396 | + SQLiteDatabase::instance()->rollbackTransaction(); |
2397 | + return false; |
2398 | + } |
2399 | + } |
2400 | + |
2401 | + if (!SQLiteDatabase::instance()->finishTransaction()) { |
2402 | + qCritical() << "Failed to commit the transaction."; |
2403 | + return false; |
2404 | + } |
2405 | + |
2406 | + QVariantMap existingThread = getSingleThread(type, |
2407 | + accountId, |
2408 | + threadId, |
2409 | + QVariantMap()); |
2410 | + |
2411 | + if (!existingThread.isEmpty()) { |
2412 | + addThreadsToCache(QList<QVariantMap>() << existingThread); |
2413 | + } |
2414 | + |
2415 | + return true; |
2416 | +} |
2417 | + |
2418 | +bool SQLiteHistoryPlugin::updateRoomInfo(const QString &accountId, const QString &threadId, History::EventType type, const QVariantMap &properties, const QStringList &invalidated) |
2419 | +{ |
2420 | + QSqlQuery query(SQLiteDatabase::instance()->database()); |
2421 | + |
2422 | + if (threadId.isEmpty() || accountId.isEmpty()) { |
2423 | + return false; |
2424 | + } |
2425 | + |
2426 | + SQLiteDatabase::instance()->beginTransation(); |
2427 | + |
2428 | + QDateTime creationTimestamp = QDateTime::fromTime_t(properties["CreationTimestamp"].toUInt()); |
2429 | + QDateTime timestamp = QDateTime::fromTime_t(properties["Timestamp"].toUInt()); |
2430 | + |
2431 | + QVariantMap propertyMapping; |
2432 | + propertyMapping["RoomName"] = "roomName"; |
2433 | + propertyMapping["Server"] = "server"; |
2434 | + propertyMapping["Creator"] = "creator"; |
2435 | + propertyMapping["CreationTimestamp"] = "creationTimestamp"; |
2436 | + propertyMapping["Anonymous"] = "anonymous"; |
2437 | + propertyMapping["InviteOnly"] = "inviteOnly"; |
2438 | + propertyMapping["Limit"] = "participantLimit"; |
2439 | + propertyMapping["Moderated"] = "moderated"; |
2440 | + propertyMapping["Title"] = "title"; |
2441 | + propertyMapping["Description"] = "description"; |
2442 | + propertyMapping["Persistent"] = "persistent"; |
2443 | + propertyMapping["Private"] = "private"; |
2444 | + propertyMapping["PasswordProtected"] = "passwordProtected"; |
2445 | + propertyMapping["Password"] = "password"; |
2446 | + propertyMapping["PasswordHint"] = "passwordHint"; |
2447 | + propertyMapping["CanUpdateConfiguration"] = "canUpdateConfiguration"; |
2448 | + propertyMapping["Subject"] = "subject"; |
2449 | + propertyMapping["Actor"] = "actor"; |
2450 | + propertyMapping["Timestamp"] = "timestamp"; |
2451 | + propertyMapping["Joined"] = "joined"; |
2452 | + propertyMapping["SelfRoles"] = "selfRoles"; |
2453 | + |
2454 | + QStringList changedPropListValues; |
2455 | + // populate sql query |
2456 | + Q_FOREACH (const QString &key, properties.keys()) { |
2457 | + if (propertyMapping.contains(key)) { |
2458 | + QString prop = propertyMapping[key].toString(); |
2459 | + changedPropListValues << QString(prop+"=:"+ prop); |
2460 | + } |
2461 | + } |
2462 | + if (changedPropListValues.isEmpty()) { |
2463 | + return false; |
2464 | + } |
2465 | + |
2466 | + query.prepare("UPDATE chat_room_info SET "+ changedPropListValues.join(", ")+" WHERE accountId=:accountId AND threadId=:threadId AND type=:type"); |
2467 | + query.bindValue(":accountId", accountId); |
2468 | + query.bindValue(":threadId", threadId); |
2469 | + query.bindValue(":type", (int) type); |
2470 | + query.bindValue(":roomName", properties["RoomName"].toString()); |
2471 | + query.bindValue(":server", properties["Server"].toString()); |
2472 | + query.bindValue(":creator", properties["Creator"].toString()); |
2473 | + query.bindValue(":creationTimestamp", creationTimestamp.toUTC().toString(timestampFormat)); |
2474 | + query.bindValue(":anonymous", properties["Anonymous"].toBool()); |
2475 | + query.bindValue(":inviteOnly", properties["InviteOnly"].toBool()); |
2476 | + query.bindValue(":participantLimit", properties["Limit"].toInt()); |
2477 | + query.bindValue(":moderated", properties["Moderated"].toBool()); |
2478 | + query.bindValue(":title", properties["Title"].toString()); |
2479 | + query.bindValue(":description", properties["Description"].toString()); |
2480 | + query.bindValue(":persistent", properties["Persistent"].toBool()); |
2481 | + query.bindValue(":private", properties["Private"].toBool()); |
2482 | + query.bindValue(":passwordProtected", properties["PasswordProtected"].toBool()); |
2483 | + query.bindValue(":password", properties["Password"].toString()); |
2484 | + query.bindValue(":passwordHint", properties["PasswordHint"].toString()); |
2485 | + query.bindValue(":canUpdateConfiguration", properties["CanUpdateConfiguration"].toBool()); |
2486 | + query.bindValue(":subject", properties["Subject"].toString()); |
2487 | + query.bindValue(":actor", properties["Actor"].toString()); |
2488 | + query.bindValue(":timestamp", timestamp.toUTC().toString(timestampFormat)); |
2489 | + query.bindValue(":joined", properties["Joined"].toBool()); |
2490 | + query.bindValue(":selfRoles", properties["SelfRoles"].toInt()); |
2491 | + |
2492 | + if (!query.exec()) { |
2493 | + qCritical() << "Error:" << query.lastError() << query.lastQuery(); |
2494 | + SQLiteDatabase::instance()->rollbackTransaction(); |
2495 | + return false; |
2496 | + } |
2497 | + |
2498 | + if (!SQLiteDatabase::instance()->finishTransaction()) { |
2499 | + qCritical() << "Failed to commit the transaction."; |
2500 | + return false; |
2501 | + } |
2502 | + |
2503 | + QVariantMap existingThread = getSingleThread(type, |
2504 | + accountId, |
2505 | + threadId, |
2506 | + QVariantMap()); |
2507 | + |
2508 | + if (!existingThread.isEmpty()) { |
2509 | + addThreadsToCache(QList<QVariantMap>() << existingThread); |
2510 | + } |
2511 | + |
2512 | + return true; |
2513 | +} |
2514 | + |
2515 | +QVariantMap SQLiteHistoryPlugin::createThreadForProperties(const QString &accountId, History::EventType type, const QVariantMap &properties) |
2516 | { |
2517 | // WARNING: this function does NOT test to check if the thread is already created, you should check using HistoryReader::threadForParticipants() |
2518 | |
2519 | QVariantMap thread; |
2520 | + History::Participants participants = History::Participants::fromVariant(properties[History::FieldParticipantIds]); |
2521 | |
2522 | // Create a new thread |
2523 | // FIXME: define what the threadId will be |
2524 | - QString threadId = participants.join("%"); |
2525 | + QString threadId; |
2526 | + History::ChatType chatType = (History::ChatType)properties[History::FieldChatType].toInt(); |
2527 | + QVariantMap chatRoomInfo; |
2528 | + |
2529 | + SQLiteDatabase::instance()->beginTransation(); |
2530 | + |
2531 | + if (chatType == History::ChatTypeRoom) { |
2532 | + threadId = properties[History::FieldThreadId].toString(); |
2533 | + // we cannot save chat room without threadId |
2534 | + if (accountId.isEmpty() || threadId.isEmpty()) { |
2535 | + SQLiteDatabase::instance()->rollbackTransaction(); |
2536 | + return thread; |
2537 | + } |
2538 | + chatRoomInfo = properties[History::FieldChatRoomInfo].toMap(); |
2539 | + QSqlQuery query(SQLiteDatabase::instance()->database()); |
2540 | + |
2541 | + QDateTime creationTimestamp = QDateTime::fromTime_t(chatRoomInfo["CreationTimestamp"].toUInt()); |
2542 | + QDateTime timestamp = QDateTime::fromTime_t(chatRoomInfo["Timestamp"].toUInt()); |
2543 | + |
2544 | + 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) " |
2545 | + "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)"); |
2546 | + query.bindValue(":accountId", accountId); |
2547 | + query.bindValue(":threadId", threadId); |
2548 | + query.bindValue(":type", (int) type); |
2549 | + query.bindValue(":roomName", chatRoomInfo["RoomName"].toString()); |
2550 | + query.bindValue(":server", chatRoomInfo["Server"].toString()); |
2551 | + query.bindValue(":creator", chatRoomInfo["Creator"].toString()); |
2552 | + query.bindValue(":creationTimestamp", creationTimestamp.toUTC().toString(timestampFormat)); |
2553 | + query.bindValue(":anonymous", chatRoomInfo["Anonymous"].toBool()); |
2554 | + query.bindValue(":inviteOnly", chatRoomInfo["InviteOnly"].toBool()); |
2555 | + query.bindValue(":participantLimit", chatRoomInfo["Limit"].toInt()); |
2556 | + query.bindValue(":moderated", chatRoomInfo["Moderated"].toBool()); |
2557 | + query.bindValue(":title", chatRoomInfo["Title"].toString()); |
2558 | + query.bindValue(":description", chatRoomInfo["Description"].toString()); |
2559 | + query.bindValue(":persistent", chatRoomInfo["Persistent"].toBool()); |
2560 | + query.bindValue(":private", chatRoomInfo["Private"].toBool()); |
2561 | + query.bindValue(":passwordProtected", chatRoomInfo["PasswordProtected"].toBool()); |
2562 | + query.bindValue(":password", chatRoomInfo["Password"].toString()); |
2563 | + query.bindValue(":passwordHint", chatRoomInfo["PasswordHint"].toString()); |
2564 | + query.bindValue(":canUpdateConfiguration", chatRoomInfo["CanUpdateConfiguration"].toBool()); |
2565 | + query.bindValue(":subject", chatRoomInfo["Subject"].toString()); |
2566 | + query.bindValue(":actor", chatRoomInfo["Actor"].toString()); |
2567 | + query.bindValue(":timestamp", timestamp.toUTC().toString(timestampFormat)); |
2568 | + query.bindValue(":joined", chatRoomInfo["Joined"].toBool()); |
2569 | + query.bindValue(":selfRoles", chatRoomInfo["SelfRoles"].toInt()); |
2570 | + |
2571 | + if (!query.exec()) { |
2572 | + qCritical() << "Error:" << query.lastError() << query.lastQuery(); |
2573 | + SQLiteDatabase::instance()->rollbackTransaction(); |
2574 | + return QVariantMap(); |
2575 | + } |
2576 | + for (QVariantMap::iterator iter = chatRoomInfo.begin(); iter != chatRoomInfo.end();) { |
2577 | + if (!iter.value().isValid()) { |
2578 | + iter = chatRoomInfo.erase(iter); |
2579 | + } else { |
2580 | + iter++; |
2581 | + } |
2582 | + } |
2583 | + thread[History::FieldChatRoomInfo] = chatRoomInfo; |
2584 | + } else if (chatType == History::ChatTypeContact) { |
2585 | + threadId = participants.identifiers().join("%"); |
2586 | + } else { |
2587 | + threadId = QString("broadcast:%1").arg(QString(QCryptographicHash::hash(participants.identifiers().join(";").toLocal8Bit(),QCryptographicHash::Md5).toHex()));; |
2588 | + } |
2589 | |
2590 | QSqlQuery query(SQLiteDatabase::instance()->database()); |
2591 | - query.prepare("INSERT INTO threads (accountId, threadId, type, count, unreadCount)" |
2592 | - "VALUES (:accountId, :threadId, :type, :count, :unreadCount)"); |
2593 | + query.prepare("INSERT INTO threads (accountId, threadId, type, count, unreadCount, chatType, lastEventTimestamp)" |
2594 | + "VALUES (:accountId, :threadId, :type, :count, :unreadCount, :chatType, :lastEventTimestamp)"); |
2595 | query.bindValue(":accountId", accountId); |
2596 | query.bindValue(":threadId", threadId); |
2597 | query.bindValue(":type", (int) type); |
2598 | query.bindValue(":count", 0); |
2599 | query.bindValue(":unreadCount", 0); |
2600 | + query.bindValue(":chatType", (int) chatType); |
2601 | + // make sure threads are created with an up-to-date timestamp |
2602 | + query.bindValue(":lastEventTimestamp", QDateTime::currentDateTimeUtc().toString(timestampFormat)); |
2603 | if (!query.exec()) { |
2604 | qCritical() << "Error:" << query.lastError() << query.lastQuery(); |
2605 | + SQLiteDatabase::instance()->rollbackTransaction(); |
2606 | return QVariantMap(); |
2607 | } |
2608 | |
2609 | // and insert the participants |
2610 | - Q_FOREACH(const QString &participant, participants) { |
2611 | - query.prepare("INSERT INTO thread_participants (accountId, threadId, type, participantId, normalizedId)" |
2612 | - "VALUES (:accountId, :threadId, :type, :participantId, :normalizedId)"); |
2613 | + Q_FOREACH(const History::Participant &participant, participants) { |
2614 | + query.prepare("INSERT INTO thread_participants (accountId, threadId, type, participantId, normalizedId, alias, state, roles)" |
2615 | + "VALUES (:accountId, :threadId, :type, :participantId, :normalizedId, :alias, :state, :roles)"); |
2616 | query.bindValue(":accountId", accountId); |
2617 | query.bindValue(":threadId", threadId); |
2618 | query.bindValue(":type", type); |
2619 | - query.bindValue(":participantId", participant); |
2620 | - query.bindValue(":normalizedId", History::Utils::normalizeId(accountId, participant)); |
2621 | + query.bindValue(":participantId", participant.identifier()); |
2622 | + query.bindValue(":normalizedId", History::Utils::normalizeId(accountId, participant.identifier())); |
2623 | + query.bindValue(":alias", participant.alias()); |
2624 | + query.bindValue(":state", participant.state()); |
2625 | + query.bindValue(":roles", participant.roles()); |
2626 | if (!query.exec()) { |
2627 | qCritical() << "Error:" << query.lastError() << query.lastQuery(); |
2628 | + SQLiteDatabase::instance()->rollbackTransaction(); |
2629 | return QVariantMap(); |
2630 | } |
2631 | } |
2632 | |
2633 | + if (!SQLiteDatabase::instance()->finishTransaction()) { |
2634 | + qCritical() << "Failed to commit the transaction."; |
2635 | + return QVariantMap(); |
2636 | + } |
2637 | + |
2638 | // and finally create the thread |
2639 | thread[History::FieldAccountId] = accountId; |
2640 | thread[History::FieldThreadId] = threadId; |
2641 | thread[History::FieldType] = (int) type; |
2642 | - thread[History::FieldParticipants] = History::ContactMatcher::instance()->contactInfo(accountId, participants, true); |
2643 | + QVariantList contactList; |
2644 | + QVariantList contactInfo = History::ContactMatcher::instance()->contactInfo(accountId, participants.identifiers(), true); |
2645 | + for (int i = 0; i < participants.count(); ++i) { |
2646 | + QVariantMap map = contactInfo[i].toMap(); |
2647 | + History::Participant participant = participants[i]; |
2648 | + map["state"] = participant.state(); |
2649 | + map["roles"] = participant.roles(); |
2650 | + contactList << map; |
2651 | + } |
2652 | + thread[History::FieldParticipants] = contactList; |
2653 | thread[History::FieldCount] = 0; |
2654 | thread[History::FieldUnreadCount] = 0; |
2655 | + thread[History::FieldChatType] = (int)chatType; |
2656 | |
2657 | addThreadsToCache(QList<QVariantMap>() << thread); |
2658 | |
2659 | return thread; |
2660 | } |
2661 | |
2662 | +// Writer |
2663 | +QVariantMap SQLiteHistoryPlugin::createThreadForParticipants(const QString &accountId, History::EventType type, const QStringList &participants) |
2664 | +{ |
2665 | + QVariantMap properties; |
2666 | + properties[History::FieldParticipantIds] = participants; |
2667 | + properties[History::FieldChatType] = participants.size() != 1 ? History::ChatTypeNone : History::ChatTypeContact; |
2668 | + return createThreadForProperties(accountId, type, properties); |
2669 | +} |
2670 | + |
2671 | bool SQLiteHistoryPlugin::removeThread(const QVariantMap &thread) |
2672 | { |
2673 | QSqlQuery query(SQLiteDatabase::instance()->database()); |
2674 | @@ -550,16 +879,18 @@ |
2675 | event[History::FieldThreadId].toString(), |
2676 | event[History::FieldEventId].toString()); |
2677 | |
2678 | + SQLiteDatabase::instance()->beginTransation(); |
2679 | + |
2680 | History::EventWriteResult result; |
2681 | if (existingEvent.isEmpty()) { |
2682 | // create new |
2683 | - query.prepare("INSERT INTO text_events (accountId, threadId, eventId, senderId, timestamp, newEvent, message, messageType, messageStatus, readTimestamp, subject) " |
2684 | - "VALUES (:accountId, :threadId, :eventId, :senderId, :timestamp, :newEvent, :message, :messageType, :messageStatus, :readTimestamp, :subject)"); |
2685 | + query.prepare("INSERT INTO text_events (accountId, threadId, eventId, senderId, timestamp, newEvent, message, messageType, messageStatus, readTimestamp, subject, informationType)" |
2686 | + "VALUES (:accountId, :threadId, :eventId, :senderId, :timestamp, :newEvent, :message, :messageType, :messageStatus, :readTimestamp, :subject, :informationType)"); |
2687 | result = History::EventWriteCreated; |
2688 | } else { |
2689 | // update existing event |
2690 | - query.prepare("UPDATE text_events SET senderId=:senderId, timestamp=:timestamp, newEvent=:newEvent, message=:message, messageType=:messageType," |
2691 | - "messageStatus=:messageStatus, readTimestamp=:readTimestamp, subject=:subject WHERE accountId=:accountId AND threadId=:threadId AND eventId=:eventId"); |
2692 | + query.prepare("UPDATE text_events SET senderId=:senderId, timestamp=:timestamp, newEvent=:newEvent, message=:message, messageType=:messageType, informationType=:informationType, " |
2693 | + "messageStatus=:messageStatus, readTimestamp=:readTimestamp, subject=:subject, informationType=:informationType WHERE accountId=:accountId AND threadId=:threadId AND eventId=:eventId"); |
2694 | result = History::EventWriteModified; |
2695 | } |
2696 | |
2697 | @@ -574,9 +905,11 @@ |
2698 | query.bindValue(":messageStatus", event[History::FieldMessageStatus]); |
2699 | query.bindValue(":readTimestamp", event[History::FieldReadTimestamp].toDateTime().toUTC()); |
2700 | query.bindValue(":subject", event[History::FieldSubject].toString()); |
2701 | + query.bindValue(":informationType", event[History::FieldInformationType].toInt()); |
2702 | |
2703 | if (!query.exec()) { |
2704 | qCritical() << "Failed to save the text event: Error:" << query.lastError() << query.lastQuery(); |
2705 | + SQLiteDatabase::instance()->rollbackTransaction(); |
2706 | return History::EventWriteError; |
2707 | } |
2708 | |
2709 | @@ -592,6 +925,7 @@ |
2710 | query.bindValue(":eventId", event[History::FieldEventId]); |
2711 | if (!query.exec()) { |
2712 | qCritical() << "Could not erase previous attachments. Error:" << query.lastError() << query.lastQuery(); |
2713 | + SQLiteDatabase::instance()->rollbackTransaction(); |
2714 | return History::EventWriteError; |
2715 | } |
2716 | } |
2717 | @@ -608,11 +942,17 @@ |
2718 | query.bindValue(":status", attachment[History::FieldStatus]); |
2719 | if (!query.exec()) { |
2720 | qCritical() << "Failed to save attachment to database" << query.lastError() << attachment; |
2721 | + SQLiteDatabase::instance()->rollbackTransaction(); |
2722 | return History::EventWriteError; |
2723 | } |
2724 | } |
2725 | } |
2726 | |
2727 | + if (!SQLiteDatabase::instance()->finishTransaction()) { |
2728 | + qCritical() << "Failed to commit transaction."; |
2729 | + return History::EventWriteError; |
2730 | + } |
2731 | + |
2732 | if (result == History::EventWriteModified || result == History::EventWriteCreated) { |
2733 | QVariantMap existingThread = getSingleThread((History::EventType) event[History::FieldType].toInt(), |
2734 | event[History::FieldAccountId].toString(), |
2735 | @@ -750,7 +1090,8 @@ |
2736 | << "threads.threadId" |
2737 | << "threads.lastEventId" |
2738 | << "threads.count" |
2739 | - << "threads.unreadCount"; |
2740 | + << "threads.unreadCount" |
2741 | + << "threads.lastEventTimestamp"; |
2742 | |
2743 | // get the participants in the query already |
2744 | fields << "(SELECT group_concat(thread_participants.participantId, \"|,|\") " |
2745 | @@ -758,13 +1099,23 @@ |
2746 | "AND thread_participants.threadId=threads.threadId " |
2747 | "AND thread_participants.type=threads.type GROUP BY accountId,threadId,type) as participants"; |
2748 | |
2749 | + fields << "(SELECT group_concat(thread_participants.state, \"|,|\") " |
2750 | + "FROM thread_participants WHERE thread_participants.accountId=threads.accountId " |
2751 | + "AND thread_participants.threadId=threads.threadId " |
2752 | + "AND thread_participants.type=threads.type GROUP BY accountId,threadId,type) as state"; |
2753 | + |
2754 | + fields << "(SELECT group_concat(thread_participants.roles, \"|,|\") " |
2755 | + "FROM thread_participants WHERE thread_participants.accountId=threads.accountId " |
2756 | + "AND thread_participants.threadId=threads.threadId " |
2757 | + "AND thread_participants.type=threads.type GROUP BY accountId,threadId,type) as roles"; |
2758 | + |
2759 | QStringList extraFields; |
2760 | QString table; |
2761 | |
2762 | switch (type) { |
2763 | case History::EventTypeText: |
2764 | table = "text_events"; |
2765 | - extraFields << "text_events.message" << "text_events.messageType" << "text_events.messageStatus" << "text_events.readTimestamp"; |
2766 | + extraFields << "text_events.message" << "text_events.messageType" << "text_events.messageStatus" << "text_events.readTimestamp" << "chatType" << "text_events.subject" << "text_events.informationType"; |
2767 | break; |
2768 | case History::EventTypeVoice: |
2769 | table = "voice_events"; |
2770 | @@ -773,7 +1124,6 @@ |
2771 | } |
2772 | |
2773 | fields << QString("%1.senderId").arg(table) |
2774 | - << QString("%1.timestamp").arg(table) |
2775 | << QString("%1.newEvent").arg(table); |
2776 | fields << extraFields; |
2777 | |
2778 | @@ -820,13 +1170,31 @@ |
2779 | thread[History::FieldEventId] = query.value(2); |
2780 | thread[History::FieldCount] = query.value(3); |
2781 | thread[History::FieldUnreadCount] = query.value(4); |
2782 | - QStringList participants = query.value(5).toString().split("|,|"); |
2783 | - thread[History::FieldParticipants] = History::ContactMatcher::instance()->contactInfo(accountId, participants, true); |
2784 | + QStringList participants = query.value(6).toString().split("|,|", QString::SkipEmptyParts); |
2785 | + QList<int> participantStatus; |
2786 | + QStringList participantStatusString = query.value(7).toString().split("|,|", QString::SkipEmptyParts); |
2787 | + Q_FOREACH(const QString &statusString, participantStatusString) { |
2788 | + participantStatus << statusString.toUInt(); |
2789 | + } |
2790 | + QStringList participantRolesString = query.value(8).toString().split("|,|", QString::SkipEmptyParts); |
2791 | + QList<int> participantRoles; |
2792 | + Q_FOREACH(const QString &rolesString, participantRolesString) { |
2793 | + participantRoles << rolesString.toUInt(); |
2794 | + } |
2795 | + QVariantList contactList; |
2796 | + QVariantList contactInfo = History::ContactMatcher::instance()->contactInfo(accountId, participants, true); |
2797 | + for (int i = 0; i < contactInfo.count(); ++i) { |
2798 | + QVariantMap map = contactInfo[i].toMap(); |
2799 | + map["state"] = participantStatus[i]; |
2800 | + map["roles"] = participantRoles[i]; |
2801 | + contactList << map; |
2802 | + } |
2803 | + thread[History::FieldParticipants] = contactList; |
2804 | |
2805 | // the generic event fields |
2806 | - thread[History::FieldSenderId] = query.value(6); |
2807 | - thread[History::FieldTimestamp] = toLocalTimeString(query.value(7).toDateTime()); |
2808 | - thread[History::FieldNewEvent] = query.value(8).toBool(); |
2809 | + thread[History::FieldSenderId] = query.value(9); |
2810 | + thread[History::FieldTimestamp] = toLocalTimeString(query.value(5).toDateTime()); |
2811 | + thread[History::FieldNewEvent] = query.value(10).toBool(); |
2812 | |
2813 | // the next step is to get the last event |
2814 | switch (type) { |
2815 | @@ -857,15 +1225,77 @@ |
2816 | thread[History::FieldAttachments] = QVariant::fromValue(attachments); |
2817 | attachments.clear(); |
2818 | } |
2819 | - thread[History::FieldMessage] = query.value(9); |
2820 | - thread[History::FieldMessageType] = query.value(10); |
2821 | - thread[History::FieldMessageStatus] = query.value(11); |
2822 | - thread[History::FieldReadTimestamp] = toLocalTimeString(query.value(12).toDateTime()); |
2823 | + thread[History::FieldMessage] = query.value(11); |
2824 | + thread[History::FieldMessageType] = query.value(12); |
2825 | + thread[History::FieldMessageStatus] = query.value(13); |
2826 | + thread[History::FieldReadTimestamp] = toLocalTimeString(query.value(14).toDateTime()); |
2827 | + thread[History::FieldChatType] = query.value(15).toUInt(); |
2828 | + |
2829 | + if (thread[History::FieldChatType].toInt() == 2) { |
2830 | + QVariantMap chatRoomInfo; |
2831 | + QSqlQuery query1(SQLiteDatabase::instance()->database()); |
2832 | + |
2833 | + 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"); |
2834 | + query1.bindValue(":accountId", thread[History::FieldAccountId]); |
2835 | + query1.bindValue(":threadId", thread[History::FieldThreadId]); |
2836 | + query1.bindValue(":type", thread[History::FieldType].toInt()); |
2837 | + |
2838 | + if (!query1.exec()) { |
2839 | + qCritical() << "Failed to get chat room info for thread: Error:" << query1.lastError() << query1.lastQuery(); |
2840 | + break; |
2841 | + } |
2842 | + query1.next(); |
2843 | + |
2844 | + if (query1.value(0).isValid()) |
2845 | + chatRoomInfo["RoomName"] = query1.value(0); |
2846 | + if (query1.value(1).isValid()) |
2847 | + chatRoomInfo["Server"] = query1.value(1); |
2848 | + if (query1.value(2).isValid()) |
2849 | + chatRoomInfo["Creator"] = query1.value(2); |
2850 | + if (query1.value(3).isValid()) |
2851 | + chatRoomInfo["CreationTimestamp"] = toLocalTimeString(query1.value(3).toDateTime()); |
2852 | + if (query1.value(4).isValid()) |
2853 | + chatRoomInfo["Anonymous"] = query1.value(4).toBool(); |
2854 | + if (query1.value(5).isValid()) |
2855 | + chatRoomInfo["InviteOnly"] = query1.value(5).toBool(); |
2856 | + if (query1.value(6).isValid()) |
2857 | + chatRoomInfo["Limit"] = query1.value(6).toInt(); |
2858 | + if (query1.value(7).isValid()) |
2859 | + chatRoomInfo["Moderated"] = query1.value(7).toBool(); |
2860 | + if (query1.value(8).isValid()) |
2861 | + chatRoomInfo["Title"] = query1.value(8); |
2862 | + if (query1.value(9).isValid()) |
2863 | + chatRoomInfo["Description"] = query1.value(9); |
2864 | + if (query1.value(10).isValid()) |
2865 | + chatRoomInfo["Persistent"] = query1.value(10).toBool(); |
2866 | + if (query1.value(11).isValid()) |
2867 | + chatRoomInfo["Private"] = query1.value(11).toBool(); |
2868 | + if (query1.value(12).isValid()) |
2869 | + chatRoomInfo["PasswordProtected"] = query1.value(12).toBool(); |
2870 | + if (query1.value(13).isValid()) |
2871 | + chatRoomInfo["Password"] = query1.value(13); |
2872 | + if (query1.value(14).isValid()) |
2873 | + chatRoomInfo["PasswordHint"] = query1.value(14); |
2874 | + if (query1.value(15).isValid()) |
2875 | + chatRoomInfo["CanUpdateConfiguration"] = query1.value(15).toBool(); |
2876 | + if (query1.value(16).isValid()) |
2877 | + chatRoomInfo["Subject"] = query1.value(16); |
2878 | + if (query1.value(17).isValid()) |
2879 | + chatRoomInfo["Actor"] = query1.value(17); |
2880 | + if (query1.value(18).isValid()) |
2881 | + chatRoomInfo["Timestamp"] = toLocalTimeString(query1.value(18).toDateTime()); |
2882 | + if (query1.value(19).isValid()) |
2883 | + chatRoomInfo["Joined"] = query1.value(19).toBool(); |
2884 | + if (query1.value(20).isValid()) |
2885 | + chatRoomInfo["SelfRoles"] = query1.value(20).toInt(); |
2886 | + |
2887 | + thread[History::FieldChatRoomInfo] = chatRoomInfo; |
2888 | + } |
2889 | break; |
2890 | case History::EventTypeVoice: |
2891 | - thread[History::FieldMissed] = query.value(10); |
2892 | - thread[History::FieldDuration] = query.value(9); |
2893 | - thread[History::FieldRemoteParticipant] = History::ContactMatcher::instance()->contactInfo(accountId, query.value(11).toString(), true); |
2894 | + thread[History::FieldMissed] = query.value(12); |
2895 | + thread[History::FieldDuration] = query.value(11); |
2896 | + thread[History::FieldRemoteParticipant] = History::ContactMatcher::instance()->contactInfo(accountId, query.value(13).toString(), true); |
2897 | break; |
2898 | } |
2899 | threads << thread; |
2900 | @@ -889,7 +1319,7 @@ |
2901 | case History::EventTypeText: |
2902 | participantsField = participantsField.arg("text_events", QString::number(type)); |
2903 | queryText = QString("SELECT accountId, threadId, eventId, senderId, timestamp, newEvent, %1, " |
2904 | - "message, messageType, messageStatus, readTimestamp, subject FROM text_events %2 %3").arg(participantsField, modifiedCondition, order); |
2905 | + "message, messageType, messageStatus, readTimestamp, subject, informationType FROM text_events %2 %3").arg(participantsField, modifiedCondition, order); |
2906 | break; |
2907 | case History::EventTypeVoice: |
2908 | participantsField = participantsField.arg("voice_events", QString::number(type)); |
2909 | @@ -960,6 +1390,10 @@ |
2910 | event[History::FieldMessageType] = query.value(8); |
2911 | event[History::FieldMessageStatus] = query.value(9); |
2912 | event[History::FieldReadTimestamp] = toLocalTimeString(query.value(10).toDateTime()); |
2913 | + if (!query.value(11).toString().isEmpty()) { |
2914 | + event[History::FieldSubject] = query.value(11).toString(); |
2915 | + } |
2916 | + event[History::FieldInformationType] = query.value(12).toInt(); |
2917 | break; |
2918 | case History::EventTypeVoice: |
2919 | event[History::FieldDuration] = query.value(7).toInt(); |
2920 | @@ -975,7 +1409,7 @@ |
2921 | |
2922 | QString SQLiteHistoryPlugin::toLocalTimeString(const QDateTime ×tamp) |
2923 | { |
2924 | - return QDateTime(timestamp.date(), timestamp.time(), Qt::UTC).toLocalTime().toString("yyyy-MM-ddTHH:mm:ss.zzz"); |
2925 | + return QDateTime(timestamp.date(), timestamp.time(), Qt::UTC).toLocalTime().toString(timestampFormat); |
2926 | } |
2927 | |
2928 | QString SQLiteHistoryPlugin::filterToString(const History::Filter &filter, QVariantMap &bindValues, const QString &propertyPrefix) const |
2929 | |
2930 | === modified file 'plugins/sqlite/sqlitehistoryplugin.h' |
2931 | --- plugins/sqlite/sqlitehistoryplugin.h 2015-10-20 19:20:00 +0000 |
2932 | +++ plugins/sqlite/sqlitehistoryplugin.h 2016-11-24 12:52:24 +0000 |
2933 | @@ -1,5 +1,5 @@ |
2934 | /* |
2935 | - * Copyright (C) 2013-2015 Canonical, Ltd. |
2936 | + * Copyright (C) 2013-2016 Canonical, Ltd. |
2937 | * |
2938 | * Authors: |
2939 | * Gustavo Pichorim Boiko <gustavo.boiko@canonical.com> |
2940 | @@ -55,6 +55,10 @@ |
2941 | History::EventType type, |
2942 | const QStringList &participants, |
2943 | History::MatchFlags matchFlags = History::MatchCaseSensitive); |
2944 | + QVariantMap threadForProperties(const QString &accountId, |
2945 | + History::EventType type, |
2946 | + const QVariantMap &properties, |
2947 | + History::MatchFlags matchFlags = History::MatchCaseSensitive); |
2948 | |
2949 | QList<QVariantMap> eventsForThread(const QVariantMap &thread); |
2950 | |
2951 | @@ -62,7 +66,12 @@ |
2952 | QVariantMap getSingleEvent(History::EventType type, const QString &accountId, const QString &threadId, const QString &eventId); |
2953 | |
2954 | // Writer part of the plugin |
2955 | + QVariantMap createThreadForProperties(const QString &accountId, History::EventType type, const QVariantMap &properties); |
2956 | QVariantMap createThreadForParticipants(const QString &accountId, History::EventType type, const QStringList &participants); |
2957 | + |
2958 | + bool updateRoomParticipants(const QString &accountId, const QString &threadId, History::EventType type, const QVariantList &participants); |
2959 | + bool updateRoomParticipantsRoles(const QString &accountId, const QString &threadId, History::EventType type, const QVariantMap &participantsRoles); |
2960 | + bool updateRoomInfo(const QString &accountId, const QString &threadId, History::EventType type, const QVariantMap &properties, const QStringList &invalidated = QStringList()); |
2961 | bool removeThread(const QVariantMap &thread); |
2962 | |
2963 | History::EventWriteResult writeTextEvent(const QVariantMap &event); |
2964 | |
2965 | === modified file 'plugins/sqlite/sqlitehistorythreadview.cpp' |
2966 | --- plugins/sqlite/sqlitehistorythreadview.cpp 2015-09-21 20:05:06 +0000 |
2967 | +++ plugins/sqlite/sqlitehistorythreadview.cpp 2016-11-24 12:52:24 +0000 |
2968 | @@ -35,7 +35,6 @@ |
2969 | : History::PluginThreadView(), mPlugin(plugin), mType(type), mSort(sort), |
2970 | mFilter(filter), mPageSize(15), mQuery(SQLiteDatabase::instance()->database()), mOffset(0), mValid(true), mQueryProperties(properties) |
2971 | { |
2972 | - qDebug() << __PRETTY_FUNCTION__; |
2973 | mTemporaryTable = QString("threadview%1%2").arg(QString::number((qulonglong)this), QDateTime::currentDateTimeUtc().toString("yyyyMMddhhmmsszzz")); |
2974 | mQuery.setForwardOnly(true); |
2975 | |
2976 | @@ -69,9 +68,6 @@ |
2977 | Q_EMIT Invalidated(); |
2978 | return; |
2979 | } |
2980 | - |
2981 | - mQuery.exec(QString("SELECT count(*) FROM %1").arg(mTemporaryTable)); |
2982 | - mQuery.next(); |
2983 | } |
2984 | |
2985 | SQLiteHistoryThreadView::~SQLiteHistoryThreadView() |
2986 | @@ -84,7 +80,6 @@ |
2987 | |
2988 | QList<QVariantMap> SQLiteHistoryThreadView::NextPage() |
2989 | { |
2990 | - qDebug() << __PRETTY_FUNCTION__; |
2991 | QList<QVariantMap> threads; |
2992 | |
2993 | // now prepare for selecting from it |
2994 | |
2995 | === modified file 'src/channelobserver.cpp' |
2996 | --- src/channelobserver.cpp 2015-10-06 17:03:38 +0000 |
2997 | +++ src/channelobserver.cpp 2016-11-24 12:52:24 +0000 |
2998 | @@ -40,6 +40,8 @@ |
2999 | Tp::ChannelClassSpecList specList; |
3000 | specList << Tp::ChannelClassSpec::audioCall(); |
3001 | specList << Tp::ChannelClassSpec::textChat(); |
3002 | + specList << Tp::ChannelClassSpec::textChatroom(); |
3003 | + specList << Tp::ChannelClassSpec::unnamedTextChat(); |
3004 | |
3005 | return specList; |
3006 | } |
3007 | @@ -58,7 +60,6 @@ |
3008 | Q_UNUSED(requestsSatisfied) |
3009 | Q_UNUSED(observerInfo) |
3010 | |
3011 | - qDebug() << __PRETTY_FUNCTION__; |
3012 | Q_FOREACH (Tp::ChannelPtr channel, channels) { |
3013 | // tp-qt has not support for the SMS interface |
3014 | if (channel->immutableProperties().contains(TP_QT_IFACE_CHANNEL_INTERFACE_SMS + QLatin1String(".Flash"))) { |
3015 | |
3016 | === modified file 'src/contactmatcher.cpp' |
3017 | --- src/contactmatcher.cpp 2015-10-07 20:21:54 +0000 |
3018 | +++ src/contactmatcher.cpp 2016-11-24 12:52:24 +0000 |
3019 | @@ -1,5 +1,5 @@ |
3020 | /* |
3021 | - * Copyright (C) 2014-2015 Canonical, Ltd. |
3022 | + * Copyright (C) 2014-2016 Canonical, Ltd. |
3023 | * |
3024 | * Authors: |
3025 | * Gustavo Pichorim Boiko <gustavo.boiko@canonical.com> |
3026 | @@ -95,7 +95,7 @@ |
3027 | * |
3028 | * Note that synchronous requests should only be placed after \ref TelepathyHelper is ready. |
3029 | */ |
3030 | -QVariantMap ContactMatcher::contactInfo(const QString &accountId, const QString &identifier, bool synchronous) |
3031 | +QVariantMap ContactMatcher::contactInfo(const QString &accountId, const QString &identifier, bool synchronous, const QVariantMap &properties) |
3032 | { |
3033 | InternalContactMap &internalMap = mContactMap[accountId]; |
3034 | |
3035 | @@ -103,8 +103,9 @@ |
3036 | if (internalMap.contains(identifier)) { |
3037 | return internalMap[identifier]; |
3038 | } |
3039 | - |
3040 | + |
3041 | QVariantMap map; |
3042 | + |
3043 | // and if there was no match, asynchronously request the info, and return an empty map for now |
3044 | if (History::TelepathyHelper::instance()->ready()) { |
3045 | map = requestContactInfo(accountId, identifier, synchronous); |
3046 | @@ -114,6 +115,15 @@ |
3047 | } |
3048 | map[History::FieldIdentifier] = identifier; |
3049 | map[History::FieldAccountId] = accountId; |
3050 | + |
3051 | + QMapIterator<QString, QVariant> i(properties); |
3052 | + while (i.hasNext()) { |
3053 | + i.next(); |
3054 | + if (!map.contains(i.key())) { |
3055 | + map[i.key()] = i.value(); |
3056 | + } |
3057 | + } |
3058 | + |
3059 | mContactMap[accountId][identifier] = map; |
3060 | return map; |
3061 | } |
3062 | |
3063 | === modified file 'src/contactmatcher_p.h' |
3064 | --- src/contactmatcher_p.h 2015-10-06 12:59:03 +0000 |
3065 | +++ src/contactmatcher_p.h 2016-11-24 12:52:24 +0000 |
3066 | @@ -1,5 +1,5 @@ |
3067 | /* |
3068 | - * Copyright (C) 2014-2015 Canonical, Ltd. |
3069 | + * Copyright (C) 2014-2016 Canonical, Ltd. |
3070 | * |
3071 | * Authors: |
3072 | * Gustavo Pichorim Boiko <gustavo.boiko@canonical.com> |
3073 | @@ -45,7 +45,7 @@ |
3074 | Q_OBJECT |
3075 | public: |
3076 | static ContactMatcher *instance(QContactManager *manager = 0); |
3077 | - QVariantMap contactInfo(const QString &accountId, const QString &identifier, bool synchronous = false); |
3078 | + QVariantMap contactInfo(const QString &accountId, const QString &identifier, bool synchronous = false, const QVariantMap &properties = QVariantMap()); |
3079 | QVariantList contactInfo(const QString &accountId, const QStringList &identifiers, bool synchronous = false); |
3080 | |
3081 | // this will only watch for contact changes affecting the identifier, but won't fetch contact info |
3082 | |
3083 | === modified file 'src/manager.cpp' |
3084 | --- src/manager.cpp 2015-10-01 19:44:45 +0000 |
3085 | +++ src/manager.cpp 2016-11-24 12:52:24 +0000 |
3086 | @@ -1,5 +1,5 @@ |
3087 | /* |
3088 | - * Copyright (C) 2013 Canonical, Ltd. |
3089 | + * Copyright (C) 2013-2016 Canonical, Ltd. |
3090 | * |
3091 | * Authors: |
3092 | * Gustavo Pichorim Boiko <gustavo.boiko@canonical.com> |
3093 | @@ -135,7 +135,23 @@ |
3094 | { |
3095 | Q_D(Manager); |
3096 | |
3097 | - return d->dbus->threadForParticipants(accountId, type, participants, matchFlags, create); |
3098 | + QVariantMap properties; |
3099 | + properties[History::FieldParticipantIds] = participants; |
3100 | + if (participants.size() == 1) { |
3101 | + properties[History::FieldChatType] = History::ChatTypeContact; |
3102 | + } |
3103 | + return d->dbus->threadForProperties(accountId, type, properties, matchFlags, create); |
3104 | +} |
3105 | + |
3106 | +Thread Manager::threadForProperties(const QString &accountId, |
3107 | + EventType type, |
3108 | + const QVariantMap &properties, |
3109 | + MatchFlags matchFlags, |
3110 | + bool create) |
3111 | +{ |
3112 | + Q_D(Manager); |
3113 | + |
3114 | + return d->dbus->threadForProperties(accountId, type, properties, matchFlags, create); |
3115 | } |
3116 | |
3117 | Thread Manager::getSingleThread(EventType type, const QString &accountId, const QString &threadId, const QVariantMap &properties) |
3118 | |
3119 | === modified file 'src/manager.h' |
3120 | --- src/manager.h 2015-10-01 19:44:45 +0000 |
3121 | +++ src/manager.h 2016-11-24 12:52:24 +0000 |
3122 | @@ -1,5 +1,5 @@ |
3123 | /* |
3124 | - * Copyright (C) 2013 Canonical, Ltd. |
3125 | + * Copyright (C) 2013-2016 Canonical, Ltd. |
3126 | * |
3127 | * Authors: |
3128 | * Gustavo Pichorim Boiko <gustavo.boiko@canonical.com> |
3129 | @@ -61,6 +61,12 @@ |
3130 | const QStringList &participants, |
3131 | History::MatchFlags matchFlags = History::MatchCaseSensitive, |
3132 | bool create = false); |
3133 | + Thread threadForProperties(const QString &accountId, |
3134 | + EventType type, |
3135 | + const QVariantMap &properties, |
3136 | + History::MatchFlags matchFlags = History::MatchCaseSensitive, |
3137 | + bool create = false); |
3138 | + |
3139 | Thread getSingleThread(EventType type, const QString &accountId, const QString &threadId, const QVariantMap &properties = QVariantMap()); |
3140 | |
3141 | bool writeEvents(const History::Events &events); |
3142 | |
3143 | === modified file 'src/managerdbus.cpp' |
3144 | --- src/managerdbus.cpp 2015-09-23 15:08:07 +0000 |
3145 | +++ src/managerdbus.cpp 2016-11-24 12:52:24 +0000 |
3146 | @@ -1,5 +1,5 @@ |
3147 | /* |
3148 | - * Copyright (C) 2013 Canonical, Ltd. |
3149 | + * Copyright (C) 2013-2016 Canonical, Ltd. |
3150 | * |
3151 | * Authors: |
3152 | * Gustavo Pichorim Boiko <gustavo.boiko@canonical.com> |
3153 | @@ -64,9 +64,21 @@ |
3154 | MatchFlags matchFlags, |
3155 | bool create) |
3156 | { |
3157 | + QVariantMap properties; |
3158 | + properties[History::FieldParticipantIds] = participants; |
3159 | + |
3160 | + return threadForProperties(accountId, type, properties, matchFlags, create); |
3161 | +} |
3162 | + |
3163 | +Thread ManagerDBus::threadForProperties(const QString &accountId, |
3164 | + EventType type, |
3165 | + const QVariantMap &properties, |
3166 | + MatchFlags matchFlags, |
3167 | + bool create) |
3168 | +{ |
3169 | Thread thread; |
3170 | // FIXME: move to async call if possible |
3171 | - QDBusReply<QVariantMap> reply = mInterface.call("ThreadForParticipants", accountId, (int) type, participants, (int)matchFlags, create); |
3172 | + QDBusReply<QVariantMap> reply = mInterface.call("ThreadForProperties", accountId, (int) type, properties, (int)matchFlags, create); |
3173 | if (reply.isValid()) { |
3174 | QVariantMap properties = reply.value(); |
3175 | thread = Thread::fromProperties(properties); |
3176 | |
3177 | === modified file 'src/managerdbus_p.h' |
3178 | --- src/managerdbus_p.h 2015-09-23 15:08:07 +0000 |
3179 | +++ src/managerdbus_p.h 2016-11-24 12:52:24 +0000 |
3180 | @@ -1,5 +1,5 @@ |
3181 | /* |
3182 | - * Copyright (C) 2013 Canonical, Ltd. |
3183 | + * Copyright (C) 2013-2016 Canonical, Ltd. |
3184 | * |
3185 | * Authors: |
3186 | * Gustavo Pichorim Boiko <gustavo.boiko@canonical.com> |
3187 | @@ -45,6 +45,12 @@ |
3188 | History::MatchFlags matchFlags, |
3189 | bool create); |
3190 | |
3191 | + Thread threadForProperties(const QString &accountId, |
3192 | + EventType type, |
3193 | + const QVariantMap &properties, |
3194 | + History::MatchFlags matchFlags, |
3195 | + bool create); |
3196 | + |
3197 | bool writeEvents(const History::Events &events); |
3198 | bool removeThreads(const Threads &threads); |
3199 | bool removeEvents(const Events &events); |
3200 | |
3201 | === modified file 'src/participant.cpp' |
3202 | --- src/participant.cpp 2016-01-04 18:28:34 +0000 |
3203 | +++ src/participant.cpp 2016-11-24 12:52:24 +0000 |
3204 | @@ -1,5 +1,5 @@ |
3205 | /* |
3206 | - * Copyright (C) 2015 Canonical, Ltd. |
3207 | + * Copyright (C) 2015-2016 Canonical, Ltd. |
3208 | * |
3209 | * Authors: |
3210 | * Gustavo Pichorim Boiko <gustavo.boiko@canonical.com> |
3211 | @@ -36,9 +36,11 @@ |
3212 | const QString &theContactId, |
3213 | const QString &theAlias, |
3214 | const QString &theAvatar, |
3215 | + const uint theState, |
3216 | + const uint theRoles, |
3217 | const QVariantMap &theDetailProperties) : |
3218 | accountId(theAccountId), identifier(theIdentifier), contactId(theContactId), |
3219 | - alias(theAlias), avatar(theAvatar), detailProperties(theDetailProperties) |
3220 | + alias(theAlias), avatar(theAvatar), state(theState), roles(theRoles), detailProperties(theDetailProperties) |
3221 | { |
3222 | } |
3223 | |
3224 | @@ -51,8 +53,8 @@ |
3225 | { |
3226 | } |
3227 | |
3228 | -Participant::Participant(const QString &accountId, const QString &identifier, const QString &contactId, const QString &alias, const QString &avatar, const QVariantMap &detailProperties) |
3229 | - : d_ptr(new ParticipantPrivate(accountId, identifier, contactId, alias, avatar, detailProperties)) |
3230 | +Participant::Participant(const QString &accountId, const QString &identifier, const QString &contactId, const QString &alias, const QString &avatar, uint state, uint roles, const QVariantMap &detailProperties) |
3231 | + : d_ptr(new ParticipantPrivate(accountId, identifier, contactId, alias, avatar, state, roles, detailProperties)) |
3232 | { |
3233 | } |
3234 | |
3235 | @@ -104,6 +106,18 @@ |
3236 | return d->avatar; |
3237 | } |
3238 | |
3239 | +uint Participant::state() const |
3240 | +{ |
3241 | + Q_D(const Participant); |
3242 | + return d->state; |
3243 | +} |
3244 | + |
3245 | +uint Participant::roles() const |
3246 | +{ |
3247 | + Q_D(const Participant); |
3248 | + return d->roles; |
3249 | +} |
3250 | + |
3251 | QVariantMap Participant::detailProperties() const |
3252 | { |
3253 | Q_D(const Participant); |
3254 | @@ -140,6 +154,8 @@ |
3255 | map[FieldContactId] = d->contactId; |
3256 | map[FieldAlias] = d->alias; |
3257 | map[FieldAvatar] = d->avatar; |
3258 | + map[FieldParticipantState] = d->state; |
3259 | + map[FieldParticipantRoles] = d->roles; |
3260 | map[FieldDetailProperties] = d->detailProperties; |
3261 | |
3262 | return map; |
3263 | @@ -157,6 +173,8 @@ |
3264 | QString contactId = properties[FieldContactId].toString(); |
3265 | QString alias = properties[FieldAlias].toString(); |
3266 | QString avatar = properties[FieldAvatar].toString(); |
3267 | + uint state = properties[FieldParticipantState].toUInt(); |
3268 | + uint roles = properties[FieldParticipantRoles].toUInt(); |
3269 | QVariantMap detailProperties; |
3270 | QVariant detailPropertiesVariant = properties[FieldDetailProperties]; |
3271 | if (detailPropertiesVariant.canConvert<QVariantMap>()) { |
3272 | @@ -169,7 +187,7 @@ |
3273 | } |
3274 | } |
3275 | |
3276 | - return Participant(accountId, identifier, contactId, alias, avatar, detailProperties); |
3277 | + return Participant(accountId, identifier, contactId, alias, avatar, state, roles, detailProperties); |
3278 | } |
3279 | |
3280 | QStringList Participants::identifiers() const |
3281 | @@ -184,7 +202,9 @@ |
3282 | Participants Participants::fromVariant(const QVariant &variant) |
3283 | { |
3284 | Participants participants; |
3285 | - if (variant.canConvert<QVariantList>()) { |
3286 | + if (variant.type() == QVariant::StringList) { |
3287 | + participants = Participants::fromStringList(variant.toStringList()); |
3288 | + } else if (variant.canConvert<QVariantList>()) { |
3289 | participants = Participants::fromVariantList(variant.toList()); |
3290 | } else if (variant.canConvert<QDBusArgument>()) { |
3291 | QDBusArgument argument = variant.value<QDBusArgument>(); |
3292 | @@ -193,6 +213,17 @@ |
3293 | return participants; |
3294 | } |
3295 | |
3296 | +Participants Participants::fromStringList(const QStringList &list) |
3297 | +{ |
3298 | + Participants participants; |
3299 | + Q_FOREACH(const QString& participantId, list) { |
3300 | + QVariantMap properties; |
3301 | + properties[FieldIdentifier] = participantId; |
3302 | + participants << Participant::fromProperties(properties); |
3303 | + } |
3304 | + return participants; |
3305 | +} |
3306 | + |
3307 | Participants Participants::fromVariantList(const QVariantList &list) |
3308 | { |
3309 | Participants participants; |
3310 | @@ -211,6 +242,17 @@ |
3311 | return list; |
3312 | } |
3313 | |
3314 | +Participants Participants::filterByState(uint state) const |
3315 | +{ |
3316 | + Participants filtered; |
3317 | + Q_FOREACH(const Participant &participant, *this) { |
3318 | + if (participant.state() == state) { |
3319 | + filtered << participant; |
3320 | + } |
3321 | + } |
3322 | + return filtered; |
3323 | +} |
3324 | + |
3325 | const QDBusArgument &operator>>(const QDBusArgument &argument, Participants &participants) |
3326 | { |
3327 | argument.beginArray(); |
3328 | |
3329 | === modified file 'src/participant.h' |
3330 | --- src/participant.h 2015-09-28 23:13:21 +0000 |
3331 | +++ src/participant.h 2016-11-24 12:52:24 +0000 |
3332 | @@ -1,5 +1,5 @@ |
3333 | /* |
3334 | - * Copyright (C) 2015 Canonical, Ltd. |
3335 | + * Copyright (C) 2015-2016 Canonical, Ltd. |
3336 | * |
3337 | * Authors: |
3338 | * Gustavo Pichorim Boiko <gustavo.boiko@canonical.com> |
3339 | @@ -44,6 +44,8 @@ |
3340 | const QString &contactId = QString::null, |
3341 | const QString &alias = QString::null, |
3342 | const QString &avatar = QString::null, |
3343 | + uint state = 0, |
3344 | + uint roles = 0, |
3345 | const QVariantMap &detailProperties = QVariantMap()); |
3346 | Participant(const Participant &other); |
3347 | Participant& operator=(const Participant &other); |
3348 | @@ -54,6 +56,8 @@ |
3349 | QString contactId() const; |
3350 | QString alias() const; |
3351 | QString avatar() const; |
3352 | + uint state() const; |
3353 | + uint roles() const; |
3354 | QVariantMap detailProperties() const; |
3355 | |
3356 | bool isNull() const; |
3357 | @@ -75,8 +79,9 @@ |
3358 | QStringList identifiers() const; |
3359 | static Participants fromVariant(const QVariant &variant); |
3360 | static Participants fromVariantList(const QVariantList &list); |
3361 | + static Participants fromStringList(const QStringList &list); |
3362 | QVariantList toVariantList() const; |
3363 | - |
3364 | + History::Participants filterByState(uint state) const; |
3365 | }; |
3366 | |
3367 | const QDBusArgument &operator>>(const QDBusArgument &argument, Participants &participants); |
3368 | |
3369 | === modified file 'src/participant_p.h' |
3370 | --- src/participant_p.h 2015-09-28 18:53:49 +0000 |
3371 | +++ src/participant_p.h 2016-11-24 12:52:24 +0000 |
3372 | @@ -39,6 +39,8 @@ |
3373 | const QString &theContactId = QString::null, |
3374 | const QString &theAlias = QString::null, |
3375 | const QString &theAvatar = QString::null, |
3376 | + uint theState = 0, |
3377 | + uint theRoles = 0, |
3378 | const QVariantMap &theDetailProperties = QVariantMap()); |
3379 | virtual ~ParticipantPrivate(); |
3380 | |
3381 | @@ -47,6 +49,8 @@ |
3382 | QString contactId; |
3383 | QString alias; |
3384 | QString avatar; |
3385 | + uint state; |
3386 | + uint roles; |
3387 | QVariantMap detailProperties; |
3388 | }; |
3389 | |
3390 | |
3391 | === modified file 'src/plugin.h' |
3392 | --- src/plugin.h 2015-09-28 22:23:09 +0000 |
3393 | +++ src/plugin.h 2016-11-24 12:52:24 +0000 |
3394 | @@ -1,5 +1,5 @@ |
3395 | /* |
3396 | - * Copyright (C) 2013 Canonical, Ltd. |
3397 | + * Copyright (C) 2013-2016 Canonical, Ltd. |
3398 | * |
3399 | * Authors: |
3400 | * Gustavo Pichorim Boiko <gustavo.boiko@canonical.com> |
3401 | @@ -61,11 +61,19 @@ |
3402 | EventType type, |
3403 | const QStringList &participants, |
3404 | History::MatchFlags matchFlags = History::MatchCaseSensitive) = 0; |
3405 | + virtual QVariantMap threadForProperties(const QString &accountId, |
3406 | + EventType type, |
3407 | + const QVariantMap &properties, |
3408 | + History::MatchFlags matchFlags = History::MatchCaseSensitive) = 0; |
3409 | |
3410 | virtual QList<QVariantMap> eventsForThread(const QVariantMap &thread) = 0; |
3411 | |
3412 | // Writer part of the plugin |
3413 | virtual QVariantMap createThreadForParticipants(const QString &accountId, EventType type, const QStringList &participants) { return QVariantMap(); } |
3414 | + virtual QVariantMap createThreadForProperties(const QString &accountId, EventType type, const QVariantMap &properties) { return QVariantMap(); } |
3415 | + virtual bool updateRoomParticipants(const QString &accountId, const QString &threadId, History::EventType type, const QVariantList &participants) { return false; }; |
3416 | + virtual bool updateRoomParticipantsRoles(const QString &accountId, const QString &threadId, History::EventType type, const QVariantMap &participantsRoles) { return false; }; |
3417 | + virtual bool updateRoomInfo(const QString &accountId, const QString &threadId, EventType type, const QVariantMap &properties, const QStringList &invalidated = QStringList()) { return false; }; |
3418 | virtual bool removeThread(const QVariantMap &thread) { return false; } |
3419 | |
3420 | virtual EventWriteResult writeTextEvent(const QVariantMap &event) { return EventWriteError; } |
3421 | |
3422 | === modified file 'src/plugineventview.cpp' |
3423 | --- src/plugineventview.cpp 2013-11-19 17:52:53 +0000 |
3424 | +++ src/plugineventview.cpp 2016-11-24 12:52:24 +0000 |
3425 | @@ -56,7 +56,6 @@ |
3426 | |
3427 | void PluginEventView::Destroy() |
3428 | { |
3429 | - qDebug() << __PRETTY_FUNCTION__; |
3430 | Q_D(PluginEventView); |
3431 | deleteLater(); |
3432 | } |
3433 | |
3434 | === modified file 'src/pluginthreadview.cpp' |
3435 | --- src/pluginthreadview.cpp 2013-11-19 17:52:53 +0000 |
3436 | +++ src/pluginthreadview.cpp 2016-11-24 12:52:24 +0000 |
3437 | @@ -56,7 +56,6 @@ |
3438 | |
3439 | void PluginThreadView::Destroy() |
3440 | { |
3441 | - qDebug() << __PRETTY_FUNCTION__; |
3442 | Q_D(PluginThreadView); |
3443 | deleteLater(); |
3444 | } |
3445 | |
3446 | === modified file 'src/textevent.cpp' |
3447 | --- src/textevent.cpp 2015-09-28 23:13:21 +0000 |
3448 | +++ src/textevent.cpp 2016-11-24 12:52:24 +0000 |
3449 | @@ -47,10 +47,11 @@ |
3450 | MessageStatus theMessageStatus, |
3451 | const QDateTime &theReadTimestamp, |
3452 | const QString &theSubject, |
3453 | + InformationType theInformationType, |
3454 | const TextEventAttachments &theAttachments, const Participants &theParticipants) : |
3455 | EventPrivate(theAccountId, theThreadId, theEventId, theSender, theTimestamp, theNewEvent, theParticipants), |
3456 | message(theMessage), messageType(theMessageType), messageStatus(theMessageStatus), |
3457 | - readTimestamp(theReadTimestamp), subject(theSubject), attachments(theAttachments) |
3458 | + readTimestamp(theReadTimestamp), subject(theSubject), informationType(theInformationType), attachments(theAttachments) |
3459 | { |
3460 | } |
3461 | |
3462 | @@ -72,6 +73,7 @@ |
3463 | map[FieldMessageStatus] = (int)messageStatus; |
3464 | map[FieldReadTimestamp] = readTimestamp.toString("yyyy-MM-ddTHH:mm:ss.zzz"); |
3465 | map[FieldSubject] = subject; |
3466 | + map[FieldInformationType] = informationType; |
3467 | |
3468 | QList<QVariantMap> attachmentsMap; |
3469 | Q_FOREACH(const TextEventAttachment &attachment, attachments) { |
3470 | @@ -102,10 +104,11 @@ |
3471 | MessageStatus messageStatus, |
3472 | const QDateTime &readTimestamp, |
3473 | const QString &subject, |
3474 | + InformationType informationType, |
3475 | const TextEventAttachments &attachments, |
3476 | const Participants &participants) |
3477 | : Event(*new TextEventPrivate(accountId, threadId, eventId, sender, timestamp, newEvent, |
3478 | - message, messageType, messageStatus, readTimestamp, subject, |
3479 | + message, messageType, messageStatus, readTimestamp, subject, informationType, |
3480 | attachments, participants)) |
3481 | { |
3482 | qDBusRegisterMetaType<QList<QVariantMap> >(); |
3483 | @@ -158,6 +161,12 @@ |
3484 | return d->subject; |
3485 | } |
3486 | |
3487 | +InformationType TextEvent::informationType() const |
3488 | +{ |
3489 | + Q_D(const TextEvent); |
3490 | + return d->informationType; |
3491 | +} |
3492 | + |
3493 | TextEventAttachments TextEvent::attachments() const |
3494 | { |
3495 | Q_D(const TextEvent); |
3496 | @@ -180,6 +189,7 @@ |
3497 | Participants participants = Participants::fromVariant(properties[FieldParticipants]); |
3498 | QString message = properties[FieldMessage].toString(); |
3499 | QString subject = properties[FieldSubject].toString(); |
3500 | + InformationType informationType = (InformationType) properties[FieldInformationType].toInt(); |
3501 | MessageType messageType = (MessageType) properties[FieldMessageType].toInt(); |
3502 | MessageStatus messageStatus = (MessageStatus) properties[FieldMessageStatus].toInt(); |
3503 | QDateTime readTimestamp = QDateTime::fromString(properties[FieldReadTimestamp].toString(), Qt::ISODate); |
3504 | @@ -203,7 +213,7 @@ |
3505 | |
3506 | // and finally create the event |
3507 | event = TextEvent(accountId, threadId, eventId, senderId, timestamp, newEvent, |
3508 | - message, messageType, messageStatus, readTimestamp, subject, attachments, participants); |
3509 | + message, messageType, messageStatus, readTimestamp, subject, informationType, attachments, participants); |
3510 | return event; |
3511 | } |
3512 | |
3513 | |
3514 | === modified file 'src/textevent.h' |
3515 | --- src/textevent.h 2015-09-28 23:13:21 +0000 |
3516 | +++ src/textevent.h 2016-11-24 12:52:24 +0000 |
3517 | @@ -49,6 +49,7 @@ |
3518 | MessageStatus messageStatus = MessageStatusUnknown, |
3519 | const QDateTime &readTimestamp = QDateTime(), |
3520 | const QString &subject = QString(), |
3521 | + InformationType informationType = InformationTypeNone, |
3522 | const TextEventAttachments &attachments = TextEventAttachments(), |
3523 | const Participants &participants = Participants()); |
3524 | |
3525 | @@ -65,6 +66,7 @@ |
3526 | QDateTime readTimestamp() const; |
3527 | void setReadTimestamp(const QDateTime &value); |
3528 | QString subject() const; |
3529 | + InformationType informationType() const; |
3530 | TextEventAttachments attachments() const; |
3531 | |
3532 | static Event fromProperties(const QVariantMap &properties); |
3533 | |
3534 | === modified file 'src/textevent_p.h' |
3535 | --- src/textevent_p.h 2015-09-28 23:13:21 +0000 |
3536 | +++ src/textevent_p.h 2016-11-24 12:52:24 +0000 |
3537 | @@ -45,6 +45,7 @@ |
3538 | MessageStatus theMessageStatus, |
3539 | const QDateTime &theReadTimestamp, |
3540 | const QString &theSubject, |
3541 | + InformationType theInformationType, |
3542 | const TextEventAttachments &theAttachments, |
3543 | const Participants &theParticipants); |
3544 | ~TextEventPrivate(); |
3545 | @@ -53,6 +54,7 @@ |
3546 | MessageStatus messageStatus; |
3547 | QDateTime readTimestamp; |
3548 | QString subject; |
3549 | + InformationType informationType; |
3550 | TextEventAttachments attachments; |
3551 | |
3552 | EventType type() const; |
3553 | |
3554 | === modified file 'src/thread.cpp' |
3555 | --- src/thread.cpp 2015-10-08 19:35:40 +0000 |
3556 | +++ src/thread.cpp 2016-11-24 12:52:24 +0000 |
3557 | @@ -1,5 +1,5 @@ |
3558 | /* |
3559 | - * Copyright (C) 2013-2015 Canonical, Ltd. |
3560 | + * Copyright (C) 2013-2016 Canonical, Ltd. |
3561 | * |
3562 | * Authors: |
3563 | * Gustavo Pichorim Boiko <gustavo.boiko@canonical.com> |
3564 | @@ -38,14 +38,18 @@ |
3565 | } |
3566 | |
3567 | ThreadPrivate::ThreadPrivate(const QString &theAccountId, |
3568 | - const QString &theThreadId, EventType theType, |
3569 | - const Participants &theParticipants, |
3570 | - const Event &theLastEvent, |
3571 | - int theCount, |
3572 | - int theUnreadCount, |
3573 | - const Threads &theGroupedThreads) : |
3574 | - accountId(theAccountId), threadId(theThreadId), type(theType), participants(theParticipants), |
3575 | - lastEvent(theLastEvent), count(theCount), unreadCount(theUnreadCount), groupedThreads(theGroupedThreads) |
3576 | + const QString &theThreadId, EventType theType, |
3577 | + const Participants &theParticipants, |
3578 | + const QDateTime &theTimestamp, |
3579 | + const Event &theLastEvent, |
3580 | + int theCount, |
3581 | + int theUnreadCount, |
3582 | + const Threads &theGroupedThreads, |
3583 | + ChatType theChatType, |
3584 | + const QVariantMap &theChatRoomInfo) : |
3585 | + accountId(theAccountId), threadId(theThreadId), type(theType), participants(theParticipants), timestamp(theTimestamp), |
3586 | + lastEvent(theLastEvent), count(theCount), unreadCount(theUnreadCount), groupedThreads(theGroupedThreads), |
3587 | + chatType(theChatType), chatRoomInfo(theChatRoomInfo) |
3588 | { |
3589 | } |
3590 | |
3591 | @@ -63,11 +67,14 @@ |
3592 | Thread::Thread(const QString &accountId, |
3593 | const QString &threadId, EventType type, |
3594 | const Participants &participants, |
3595 | + const QDateTime ×tamp, |
3596 | const Event &lastEvent, |
3597 | int count, |
3598 | int unreadCount, |
3599 | - const Threads &groupedThreads) |
3600 | -: d_ptr(new ThreadPrivate(accountId, threadId, type, participants, lastEvent, count, unreadCount, groupedThreads)) |
3601 | + const Threads &groupedThreads, |
3602 | + ChatType chatType, |
3603 | + const QVariantMap &chatRoomInfo) |
3604 | +: d_ptr(new ThreadPrivate(accountId, threadId, type, participants, timestamp, lastEvent, count, unreadCount, groupedThreads, chatType, chatRoomInfo)) |
3605 | { |
3606 | qDBusRegisterMetaType<QList<QVariantMap> >(); |
3607 | qRegisterMetaType<QList<QVariantMap> >(); |
3608 | @@ -115,6 +122,12 @@ |
3609 | return d->participants; |
3610 | } |
3611 | |
3612 | +QDateTime Thread::timestamp() const |
3613 | +{ |
3614 | + Q_D(const Thread); |
3615 | + return d->timestamp; |
3616 | +} |
3617 | + |
3618 | Event Thread::lastEvent() const |
3619 | { |
3620 | Q_D(const Thread); |
3621 | @@ -139,6 +152,18 @@ |
3622 | return d->groupedThreads; |
3623 | } |
3624 | |
3625 | +ChatType Thread::chatType() const |
3626 | +{ |
3627 | + Q_D(const Thread); |
3628 | + return d->chatType; |
3629 | +} |
3630 | + |
3631 | +QVariantMap Thread::chatRoomInfo() const |
3632 | +{ |
3633 | + Q_D(const Thread); |
3634 | + return d->chatRoomInfo; |
3635 | +} |
3636 | + |
3637 | bool Thread::isNull() const |
3638 | { |
3639 | Q_D(const Thread); |
3640 | @@ -182,11 +207,14 @@ |
3641 | map[FieldAccountId] = d->accountId; |
3642 | map[FieldThreadId] = d->threadId; |
3643 | map[FieldType] = d->type; |
3644 | + map[FieldChatType] = d->chatType; |
3645 | map[FieldParticipants] = d->participants.toVariantList(); |
3646 | + map[FieldTimestamp] = d->timestamp; |
3647 | map[FieldCount] = d->count; |
3648 | map[FieldUnreadCount] = d->unreadCount; |
3649 | map[FieldLastEventId] = lastEvent().eventId(); |
3650 | - map[FieldLastEventTimestamp] = lastEvent().timestamp(); |
3651 | + map[FieldLastEventTimestamp] = d->timestamp; |
3652 | + map[FieldChatRoomInfo] = d->chatRoomInfo; |
3653 | |
3654 | QList<QVariantMap> groupedThreads; |
3655 | Q_FOREACH(const Thread &thread, d->groupedThreads) { |
3656 | @@ -210,8 +238,9 @@ |
3657 | QString accountId = properties[FieldAccountId].toString(); |
3658 | QString threadId = properties[FieldThreadId].toString(); |
3659 | EventType type = (EventType) properties[FieldType].toInt(); |
3660 | - |
3661 | + ChatType chatType = (ChatType) properties[FieldChatType].toInt(); |
3662 | Participants participants = Participants::fromVariant(properties[FieldParticipants]); |
3663 | + QDateTime timestamp = QDateTime::fromString(properties[FieldTimestamp].toString(), Qt::ISODate); |
3664 | int count = properties[FieldCount].toInt(); |
3665 | int unreadCount = properties[FieldUnreadCount].toInt(); |
3666 | |
3667 | @@ -227,6 +256,11 @@ |
3668 | argument >> groupedThreads; |
3669 | } |
3670 | } |
3671 | + QVariantMap chatRoomInfo = qdbus_cast<QVariantMap>(properties[FieldChatRoomInfo]); |
3672 | + // dbus_cast fails if the map was generated by a qml app, so we demarshal it by hand |
3673 | + if (chatRoomInfo.isEmpty()) { |
3674 | + chatRoomInfo = properties[FieldChatRoomInfo].toMap(); |
3675 | + } |
3676 | |
3677 | Event event; |
3678 | switch (type) { |
3679 | @@ -237,7 +271,7 @@ |
3680 | event = VoiceEvent::fromProperties(properties); |
3681 | break; |
3682 | } |
3683 | - return Thread(accountId, threadId, type, participants, event, count, unreadCount, groupedThreads); |
3684 | + return Thread(accountId, threadId, type, participants, timestamp, event, count, unreadCount, groupedThreads, chatType, chatRoomInfo); |
3685 | } |
3686 | |
3687 | const QDBusArgument &operator>>(const QDBusArgument &argument, Threads &threads) |
3688 | |
3689 | === modified file 'src/thread.h' |
3690 | --- src/thread.h 2015-10-08 19:35:40 +0000 |
3691 | +++ src/thread.h 2016-11-24 12:52:24 +0000 |
3692 | @@ -1,5 +1,5 @@ |
3693 | /* |
3694 | - * Copyright (C) 2013-2015 Canonical, Ltd. |
3695 | + * Copyright (C) 2013-2016 Canonical, Ltd. |
3696 | * |
3697 | * Authors: |
3698 | * Gustavo Pichorim Boiko <gustavo.boiko@canonical.com> |
3699 | @@ -51,10 +51,13 @@ |
3700 | const QString &threadId, |
3701 | EventType type, |
3702 | const Participants &participants, |
3703 | + const QDateTime ×tamp = QDateTime(), |
3704 | const Event &lastEvent = Event(), |
3705 | int count = 0, |
3706 | int unreadCount = 0, |
3707 | - const Threads &groupedThreads = Threads()); |
3708 | + const Threads &groupedThreads = Threads(), |
3709 | + ChatType chatType = ChatTypeNone, |
3710 | + const QVariantMap &chatRoomInfo = QVariantMap()); |
3711 | Thread(const Thread &other); |
3712 | virtual ~Thread(); |
3713 | Thread& operator=(const Thread &other); |
3714 | @@ -63,10 +66,13 @@ |
3715 | QString threadId() const; |
3716 | EventType type() const; |
3717 | Participants participants() const; |
3718 | + QDateTime timestamp() const; |
3719 | Event lastEvent() const; |
3720 | int count() const; |
3721 | int unreadCount() const; |
3722 | + ChatType chatType() const; |
3723 | Threads groupedThreads() const; |
3724 | + QVariantMap chatRoomInfo() const; |
3725 | |
3726 | bool isNull() const; |
3727 | bool operator==(const Thread &other) const; |
3728 | |
3729 | === modified file 'src/thread_p.h' |
3730 | --- src/thread_p.h 2015-10-08 19:35:40 +0000 |
3731 | +++ src/thread_p.h 2016-11-24 12:52:24 +0000 |
3732 | @@ -1,5 +1,5 @@ |
3733 | /* |
3734 | - * Copyright (C) 2013-2015 Canonical, Ltd. |
3735 | + * Copyright (C) 2013-2016 Canonical, Ltd. |
3736 | * |
3737 | * Authors: |
3738 | * Gustavo Pichorim Boiko <gustavo.boiko@canonical.com> |
3739 | @@ -38,20 +38,26 @@ |
3740 | const QString &theThreadId, |
3741 | EventType theType, |
3742 | const Participants &theParticipants, |
3743 | + const QDateTime &theTimestamp, |
3744 | const Event &theLastEvent, |
3745 | int theCount, |
3746 | int theUnreadCount, |
3747 | - const Threads &theGroupedThreads); |
3748 | + const Threads &theGroupedThreads, |
3749 | + ChatType chatType, |
3750 | + const QVariantMap &chatRoomInfo); |
3751 | virtual ~ThreadPrivate(); |
3752 | |
3753 | QString accountId; |
3754 | QString threadId; |
3755 | Participants participants; |
3756 | EventType type; |
3757 | + QDateTime timestamp; |
3758 | Event lastEvent; |
3759 | int count; |
3760 | int unreadCount; |
3761 | Threads groupedThreads; |
3762 | + ChatType chatType; |
3763 | + QVariantMap chatRoomInfo; |
3764 | }; |
3765 | |
3766 | } |
3767 | |
3768 | === modified file 'src/types.h' |
3769 | --- src/types.h 2015-10-08 12:28:23 +0000 |
3770 | +++ src/types.h 2016-11-24 12:52:24 +0000 |
3771 | @@ -1,5 +1,5 @@ |
3772 | /* |
3773 | - * Copyright (C) 2013 Canonical, Ltd. |
3774 | + * Copyright (C) 2013-2016 Canonical, Ltd. |
3775 | * |
3776 | * Authors: |
3777 | * Gustavo Pichorim Boiko <gustavo.boiko@canonical.com> |
3778 | @@ -75,6 +75,32 @@ |
3779 | MessageTypeInformation = 2 |
3780 | }; |
3781 | |
3782 | +enum InformationType |
3783 | +{ |
3784 | + InformationTypeNone = 0, |
3785 | + InformationTypeSimChange = 1, |
3786 | + InformationTypeText = 2, |
3787 | + InformationTypeSelfJoined = 3, |
3788 | + InformationTypeJoined = 4, |
3789 | + InformationTypeTitleChanged = 5, |
3790 | + InformationTypeInvitationSent = 6, |
3791 | + InformationTypeLeaving = 7, |
3792 | + InformationTypeSelfLeaving = 8, |
3793 | + InformationTypeAdminGranted = 9, |
3794 | + InformationTypeAdminRemoved = 10, |
3795 | + InformationTypeSelfAdminGranted = 11, |
3796 | + InformationTypeSelfAdminRemoved = 12, |
3797 | + InformationTypeSelfKicked = 13, |
3798 | + InformationTypeGroupGone = 14 |
3799 | +}; |
3800 | + |
3801 | +enum ChatType |
3802 | +{ |
3803 | + ChatTypeNone = 0, |
3804 | + ChatTypeContact = 1, |
3805 | + ChatTypeRoom = 2 |
3806 | +}; |
3807 | + |
3808 | // FIXME (boiko): I think this needs to be changed to a simple enum and not flags, |
3809 | // as the statuses are mutually exclusive |
3810 | enum AttachmentFlag |
3811 | @@ -86,6 +112,20 @@ |
3812 | |
3813 | Q_DECLARE_FLAGS(AttachmentFlags, AttachmentFlag) |
3814 | |
3815 | +enum ParticipantState |
3816 | +{ |
3817 | + ParticipantStateRegular = 0, |
3818 | + ParticipantStateLocalPending = 1, |
3819 | + ParticipantStateRemotePending = 2 |
3820 | +}; |
3821 | + |
3822 | +enum ParticipantRoles |
3823 | +{ |
3824 | + ParticipantRoleNone = 0, |
3825 | + ParticipantRoleMember = 1, |
3826 | + ParticipantRoleAdmin = 2 |
3827 | +}; |
3828 | + |
3829 | // Event writing results |
3830 | enum EventWriteResult { |
3831 | EventWriteCreated, |
3832 | @@ -106,12 +146,35 @@ |
3833 | static const char* FieldEventId = "eventId"; |
3834 | static const char* FieldType = "type"; |
3835 | static const char* FieldParticipants = "participants"; |
3836 | +static const char* FieldParticipantIds = "participantIds"; |
3837 | static const char* FieldCount = "count"; |
3838 | static const char* FieldUnreadCount = "unreadCount"; |
3839 | static const char* FieldSenderId = "senderId"; |
3840 | static const char* FieldTimestamp = "timestamp"; |
3841 | static const char* FieldDate = "date"; |
3842 | static const char* FieldNewEvent = "newEvent"; |
3843 | +static const char* FieldChatType = "chatType"; |
3844 | +static const char* FieldChatRoomInfo = "chatRoomInfo"; |
3845 | +static const char* FieldChatRoomJoined = "joined"; |
3846 | +static const char* FieldChatRoomSelfRoles = "selfRoles"; |
3847 | + |
3848 | +// Chat Room Info Fields |
3849 | +static const char* FieldChatRoomName = "roomName"; |
3850 | +static const char* FieldChatRoomServer = "server"; |
3851 | +static const char* FieldChatRoomCreator = "creator"; |
3852 | +static const char* FieldChatRoomCreationTimestamp = "creationTimestamp"; |
3853 | +static const char* FieldChatRoomAnonymous = "anonymous"; |
3854 | +static const char* FieldChatRoomInviteOnly = "inviteOnly"; |
3855 | +static const char* FieldChatRoomParticipantLimit = "participantLimit"; |
3856 | +static const char* FieldChatRoomModerated = "moderated"; |
3857 | +static const char* FieldChatRoomTitle = "title"; |
3858 | +static const char* FieldChatRoomDescription = "description"; |
3859 | +static const char* FieldChatRoomPersistent = "persistent"; |
3860 | +static const char* FieldChatRoomPrivate = "private"; |
3861 | +static const char* FieldChatRoomPasswordProtected = "passwordProtected"; |
3862 | +static const char* FieldChatRoomPassword = "password"; |
3863 | +static const char* FieldChatRoomPasswordHint = "passwordHint"; |
3864 | +static const char* FieldChatRoomCanUpdateConfiguration = "canUpdateConfiguration"; |
3865 | |
3866 | // thread fields |
3867 | static const char* FieldLastEventId = "lastEventId"; |
3868 | @@ -124,6 +187,7 @@ |
3869 | static const char* FieldMessageStatus = "messageStatus"; |
3870 | static const char* FieldReadTimestamp = "readTimestamp"; |
3871 | static const char* FieldSubject = "subject"; |
3872 | +static const char* FieldInformationType = "informationType"; |
3873 | static const char* FieldAttachments = "attachments"; |
3874 | |
3875 | // text attachment fields |
3876 | @@ -157,6 +221,8 @@ |
3877 | static const char* FieldAvatar = "avatar"; |
3878 | static const char* FieldIdentifier = "identifier"; |
3879 | static const char* FieldDetailProperties = "detailProperties"; |
3880 | +static const char* FieldParticipantState = "state"; |
3881 | +static const char* FieldParticipantRoles = "roles"; |
3882 | |
3883 | } |
3884 | |
3885 | |
3886 | === modified file 'src/utils.cpp' |
3887 | --- src/utils.cpp 2015-10-08 19:36:57 +0000 |
3888 | +++ src/utils.cpp 2016-11-24 12:52:24 +0000 |
3889 | @@ -1,5 +1,5 @@ |
3890 | /* |
3891 | - * Copyright (C) 2015 Canonical, Ltd. |
3892 | + * Copyright (C) 2015-2016 Canonical, Ltd. |
3893 | * |
3894 | * Authors: |
3895 | * Gustavo Pichorim Boiko <gustavo.boiko@canonical.com> |
3896 | @@ -19,10 +19,14 @@ |
3897 | * along with this program. If not, see <http://www.gnu.org/licenses/>. |
3898 | */ |
3899 | |
3900 | +#include <unistd.h> |
3901 | #include "utils_p.h" |
3902 | #include "phoneutils_p.h" |
3903 | #include <QDebug> |
3904 | #include <QStringList> |
3905 | +#include <QDBusInterface> |
3906 | +#include <QDBusConnection> |
3907 | +#include <QDBusReply> |
3908 | #include <QMap> |
3909 | |
3910 | namespace History { |
3911 | @@ -32,9 +36,12 @@ |
3912 | } |
3913 | |
3914 | // FIXME: find a better way to determine when accounts should be grouped |
3915 | -bool Utils::shouldGroupAccount(const QString &accountId) |
3916 | +bool Utils::shouldGroupThread(const Thread &thread) |
3917 | { |
3918 | - return (matchFlagsForAccount(accountId) & MatchPhoneNumber); |
3919 | + if (protocolFromAccountId(thread.accountId()) == "multimedia") { |
3920 | + return thread.chatType() != History::ChatTypeRoom; |
3921 | + } |
3922 | + return (matchFlagsForAccount(thread.accountId()) & MatchPhoneNumber); |
3923 | } |
3924 | |
3925 | MatchFlags Utils::matchFlagsForAccount(const QString &accountId) |
3926 | @@ -150,4 +157,22 @@ |
3927 | return normalizedId; |
3928 | } |
3929 | |
3930 | +QVariant Utils::getUserValue(const QString &interface, const QString &propName) |
3931 | +{ |
3932 | + QString uid = QString::number(getuid()); |
3933 | + QString activeUser = "/org/freedesktop/Accounts/User" + uid; |
3934 | + |
3935 | + QDBusInterface iface("org.freedesktop.Accounts", |
3936 | + activeUser, |
3937 | + "org.freedesktop.DBus.Properties", |
3938 | + QDBusConnection::systemBus()); |
3939 | + QDBusReply<QVariant> reply = iface.call("Get", interface, propName); |
3940 | + if (reply.isValid()) { |
3941 | + return reply.value(); |
3942 | + } else { |
3943 | + qWarning() << "Failed to get user property " << propName << " from AccountsService:" << reply.error().message(); |
3944 | + } |
3945 | + return QVariant(); |
3946 | +} |
3947 | + |
3948 | } |
3949 | |
3950 | === modified file 'src/utils_p.h' |
3951 | --- src/utils_p.h 2015-10-02 18:28:47 +0000 |
3952 | +++ src/utils_p.h 2016-11-24 12:52:24 +0000 |
3953 | @@ -1,5 +1,5 @@ |
3954 | /* |
3955 | - * Copyright (C) 2015 Canonical, Ltd. |
3956 | + * Copyright (C) 2015-2016 Canonical, Ltd. |
3957 | * |
3958 | * Authors: |
3959 | * Gustavo Pichorim Boiko <gustavo.boiko@canonical.com> |
3960 | @@ -23,6 +23,7 @@ |
3961 | #define UTILS_P_H |
3962 | |
3963 | #include "types.h" |
3964 | +#include "thread.h" |
3965 | |
3966 | namespace History { |
3967 | |
3968 | @@ -34,8 +35,9 @@ |
3969 | static bool compareIds(const QString &accountId, const QString &id1, const QString & id2); |
3970 | static bool compareParticipants(const QStringList &participants1, const QStringList &participants2, MatchFlags flags); |
3971 | static bool compareNormalizedParticipants(const QStringList &participants1, const QStringList &participants2, MatchFlags flags); |
3972 | - static bool shouldGroupAccount(const QString &accountId); |
3973 | + static bool shouldGroupThread(const Thread &thread); |
3974 | static QString normalizeId(const QString &accountId, const QString &id); |
3975 | + static QVariant getUserValue(const QString &interface, const QString &propName); |
3976 | |
3977 | private: |
3978 | Utils(); |
3979 | |
3980 | === modified file 'tests/Ubuntu.History/HistoryEventModelTest.cpp' |
3981 | --- tests/Ubuntu.History/HistoryEventModelTest.cpp 2016-03-30 16:53:04 +0000 |
3982 | +++ tests/Ubuntu.History/HistoryEventModelTest.cpp 2016-11-24 12:52:24 +0000 |
3983 | @@ -68,6 +68,7 @@ |
3984 | History::MessageStatusRead, |
3985 | QDateTime::currentDateTime(), |
3986 | "The subject", |
3987 | + History::InformationTypeNone, |
3988 | History::TextEventAttachments(), |
3989 | textThread.participants()); |
3990 | QVERIFY(mManager->writeEvents(History::Events() << event)); |
3991 | |
3992 | === modified file 'tests/Ubuntu.History/HistoryGroupedThreadsModelTest.cpp' |
3993 | --- tests/Ubuntu.History/HistoryGroupedThreadsModelTest.cpp 2015-10-05 22:54:47 +0000 |
3994 | +++ tests/Ubuntu.History/HistoryGroupedThreadsModelTest.cpp 2016-11-24 12:52:24 +0000 |
3995 | @@ -123,7 +123,7 @@ |
3996 | |
3997 | // insert another event in second thread |
3998 | History::TextEvent secondEvent = History::TextEvent(textThread.accountId(), textThread.threadId(), QString("eventId2%1").arg(QString::number(qrand() % 1024)), |
3999 | - QString("1234567"), QDateTime::currentDateTime(), false, "Random Message2", |
4000 | + QString("1234567"), QDateTime::currentDateTime().addSecs(1), false, "Random Message2", |
4001 | History::MessageTypeText); |
4002 | mManager->writeEvents(History::Events() << secondEvent); |
4003 | QTRY_COMPARE(dataChanged.count(), 1); |
4004 | |
4005 | === modified file 'tests/common/mock/CMakeLists.txt' |
4006 | --- tests/common/mock/CMakeLists.txt 2015-04-07 14:40:58 +0000 |
4007 | +++ tests/common/mock/CMakeLists.txt 2016-11-24 12:52:24 +0000 |
4008 | @@ -16,4 +16,4 @@ |
4009 | |
4010 | add_executable(telepathy-mock ${mock_SRCS}) |
4011 | qt5_use_modules(telepathy-mock Core DBus) |
4012 | -target_link_libraries(telepathy-mock ${TP_QT5_LIBRARIES} ${TELEPATHY_QT5_SERVICE_LIBRARIES} ${OFONO_QT_LIBRARIES} ${PULSEAUDIO_LIBRARIES}) |
4013 | +target_link_libraries(telepathy-mock ${TP_QT5_LIBRARIES} ${TELEPATHY_QT5_SERVICE_LIBRARIES} ${OFONO_QT_LIBRARIES} ${PULSEAUDIO_LIBRARIES} ${Qt5Network_LIBRARIES}) |
4014 | |
4015 | === modified file 'tests/common/mock/callchannel.cpp' |
4016 | --- tests/common/mock/callchannel.cpp 2015-06-16 16:16:54 +0000 |
4017 | +++ tests/common/mock/callchannel.cpp 2016-11-24 12:52:24 +0000 |
4018 | @@ -194,7 +194,8 @@ |
4019 | reason.reason = Tp::CallStateChangeReasonNoAnswer; |
4020 | } |
4021 | mCallChannel->setCallState(Tp::CallStateEnded, 0, reason, stateDetails); |
4022 | - mBaseChannel->close(); |
4023 | + // leave the channel opened for a bit longer so that the call state gets propagated correctly. |
4024 | + QTimer::singleShot(10, mBaseChannel.data(), &Tp::BaseChannel::close); |
4025 | } else if (state == "active") { |
4026 | qDebug() << "active"; |
4027 | mHoldIface->setHoldState(Tp::LocalHoldStateUnheld, Tp::LocalHoldStateReasonNone); |
4028 | |
4029 | === modified file 'tests/common/mock/mockconnectiondbus.cpp' |
4030 | --- tests/common/mock/mockconnectiondbus.cpp 2015-04-07 14:40:58 +0000 |
4031 | +++ tests/common/mock/mockconnectiondbus.cpp 2016-11-24 12:52:24 +0000 |
4032 | @@ -70,16 +70,15 @@ |
4033 | |
4034 | bool MockConnectionDBus::connectToBus() |
4035 | { |
4036 | - bool ok = QDBusConnection::sessionBus().registerService("com.canonical.MockConnection"); |
4037 | - if (!ok) { |
4038 | - return false; |
4039 | - } |
4040 | - |
4041 | if (!mAdaptor) { |
4042 | mAdaptor = new MockConnectionAdaptor(this); |
4043 | } |
4044 | |
4045 | - return QDBusConnection::sessionBus().registerObject(mObjectPath, this); |
4046 | + if (!QDBusConnection::sessionBus().registerObject(mObjectPath, this)) { |
4047 | + return false; |
4048 | + } |
4049 | + |
4050 | + return QDBusConnection::sessionBus().registerService("com.canonical.MockConnection"); |
4051 | } |
4052 | |
4053 | void MockConnectionDBus::PlaceIncomingMessage(const QString &message, const QVariantMap &properties) |
4054 | |
4055 | === modified file 'tests/common/mock/textchannel.cpp' |
4056 | --- tests/common/mock/textchannel.cpp 2015-08-28 10:15:47 +0000 |
4057 | +++ tests/common/mock/textchannel.cpp 2016-11-24 12:52:24 +0000 |
4058 | @@ -74,7 +74,9 @@ |
4059 | baseChannel->plugInterface(Tp::AbstractChannelInterfacePtr::dynamicCast(mMessagesIface)); |
4060 | |
4061 | // group stuff |
4062 | - mGroupIface = Tp::BaseChannelGroupInterface::create(Tp::ChannelGroupFlagCanAdd, conn->selfHandle()); |
4063 | + mGroupIface = Tp::BaseChannelGroupInterface::create(); |
4064 | + mGroupIface->setGroupFlags(Tp::ChannelGroupFlagCanAdd); |
4065 | + mGroupIface->setSelfHandle(conn->selfHandle()); |
4066 | mGroupIface->setAddMembersCallback(Tp::memFun(this,&MockTextChannel::onAddMembers)); |
4067 | mGroupIface->setRemoveMembersCallback(Tp::memFun(this,&MockTextChannel::onRemoveMembers)); |
4068 | baseChannel->plugInterface(Tp::AbstractChannelInterfacePtr::dynamicCast(mGroupIface)); |
4069 | @@ -232,7 +234,7 @@ |
4070 | mMembers << handle; |
4071 | } |
4072 | } |
4073 | - mGroupIface->addMembers(handles, recipients); |
4074 | + mGroupIface->setMembers(mMembers, QVariantMap()); |
4075 | } |
4076 | |
4077 | QStringList MockTextChannel::recipients() const |
4078 | @@ -250,7 +252,7 @@ |
4079 | addMembers(mConnection->inspectHandles(Tp::HandleTypeContact, handles, error)); |
4080 | } |
4081 | |
4082 | -void MockTextChannel::onRemoveMembers(const Tp::UIntList &handles, const QString &message, Tp::DBusError *error) |
4083 | +void MockTextChannel::onRemoveMembers(const Tp::UIntList &handles, const QString &message, uint reason, Tp::DBusError *error) |
4084 | { |
4085 | Q_FOREACH(uint handle, handles) { |
4086 | Q_FOREACH(const QString &recipient, mConnection->inspectHandles(Tp::HandleTypeContact, Tp::UIntList() << handle, error)) { |
4087 | @@ -258,5 +260,5 @@ |
4088 | } |
4089 | mMembers.removeAll(handle); |
4090 | } |
4091 | - mGroupIface->removeMembers(handles); |
4092 | + mGroupIface->setMembers(mMembers, QVariantMap()); |
4093 | } |
4094 | |
4095 | === modified file 'tests/common/mock/textchannel.h' |
4096 | --- tests/common/mock/textchannel.h 2015-04-07 14:40:58 +0000 |
4097 | +++ tests/common/mock/textchannel.h 2016-11-24 12:52:24 +0000 |
4098 | @@ -45,8 +45,8 @@ |
4099 | void addMembers(QStringList recipients); |
4100 | QStringList recipients() const; |
4101 | Tp::UIntList members(); |
4102 | + void onRemoveMembers(const Tp::UIntList& handles, const QString& message, uint reason, Tp::DBusError* error); |
4103 | void onAddMembers(const Tp::UIntList& handles, const QString& message, Tp::DBusError* error); |
4104 | - void onRemoveMembers(const Tp::UIntList& handles, const QString& message, Tp::DBusError* error); |
4105 | |
4106 | public Q_SLOTS: |
4107 | void placeDeliveryReport(const QString &messageId, const QString &status); |
4108 | |
4109 | === modified file 'tests/daemon/CMakeLists.txt' |
4110 | --- tests/daemon/CMakeLists.txt 2016-09-09 14:48:33 +0000 |
4111 | +++ tests/daemon/CMakeLists.txt 2016-11-24 12:52:24 +0000 |
4112 | @@ -5,7 +5,7 @@ |
4113 | ${TP_QT5_INCLUDE_DIRS} |
4114 | ) |
4115 | |
4116 | -#generate_telepathy_test(DaemonTest |
4117 | -# SOURCES DaemonTest.cpp handler.cpp approver.cpp |
4118 | -# TASKS --task ${CMAKE_BINARY_DIR}/daemon/history-daemon --ignore-return --task-name history-daemon |
4119 | -# WAIT_FOR com.canonical.HistoryService) |
4120 | +generate_telepathy_test(DaemonTest |
4121 | + SOURCES DaemonTest.cpp handler.cpp approver.cpp |
4122 | + TASKS --task ${CMAKE_BINARY_DIR}/daemon/history-daemon --ignore-return --task-name history-daemon |
4123 | + WAIT_FOR com.canonical.HistoryService) |
4124 | |
4125 | === modified file 'tests/daemon/DaemonTest.cpp' |
4126 | --- tests/daemon/DaemonTest.cpp 2015-10-06 12:50:14 +0000 |
4127 | +++ tests/daemon/DaemonTest.cpp 2016-11-24 12:52:24 +0000 |
4128 | @@ -324,7 +324,7 @@ |
4129 | connect(mAccount->connection()->contactManager()->contactsForIdentifiers(QStringList() << phoneNumber), |
4130 | SIGNAL(finished(Tp::PendingOperation*)), |
4131 | SLOT(onPendingContactsFinished(Tp::PendingOperation*))); |
4132 | - QTRY_COMPARE(spy.count(), 1); |
4133 | + QVERIFY(spy.wait()); |
4134 | |
4135 | QList<Tp::ContactPtr> contacts = spy.first().first().value<QList<Tp::ContactPtr> >(); |
4136 | QCOMPARE(contacts.count(), 1); |
4137 | @@ -335,7 +335,7 @@ |
4138 | Q_FOREACH(Tp::ContactPtr contact, contacts) { |
4139 | mAccount->ensureAudioCall(contact, "audio", QDateTime::currentDateTime(), TP_QT_IFACE_CLIENT + ".HistoryTestHandler"); |
4140 | } |
4141 | - QTRY_COMPARE(spyCallChannel.count(), 1); |
4142 | + QVERIFY(spyCallChannel.wait()); |
4143 | |
4144 | Tp::CallChannelPtr channel = spyCallChannel.first().first().value<Tp::CallChannelPtr>(); |
4145 | QVERIFY(channel); |
4146 | |
4147 | === modified file 'tests/libhistoryservice/ParticipantTest.cpp' |
4148 | --- tests/libhistoryservice/ParticipantTest.cpp 2015-10-05 22:20:00 +0000 |
4149 | +++ tests/libhistoryservice/ParticipantTest.cpp 2016-11-24 12:52:24 +0000 |
4150 | @@ -58,16 +58,20 @@ |
4151 | QString contactId("theContactId"); |
4152 | QString alias("theAlias"); |
4153 | QString avatar("theAvatar"); |
4154 | + uint state = History::ParticipantStateRegular; |
4155 | + uint roles = History::ParticipantRoleMember; |
4156 | QVariantMap detailProperties; |
4157 | detailProperties["someProperty"] = "someValue"; |
4158 | |
4159 | |
4160 | - History::Participant participant(accountId, identifier, contactId, alias, avatar, detailProperties); |
4161 | + History::Participant participant(accountId, identifier, contactId, alias, avatar, state, roles, detailProperties); |
4162 | QCOMPARE(participant.accountId(), accountId); |
4163 | QCOMPARE(participant.identifier(), identifier); |
4164 | QCOMPARE(participant.contactId(), contactId); |
4165 | QCOMPARE(participant.alias(), alias); |
4166 | QCOMPARE(participant.avatar(), avatar); |
4167 | + QCOMPARE(participant.state(), state); |
4168 | + QCOMPARE(participant.roles(), roles); |
4169 | QCOMPARE(participant.detailProperties(), detailProperties); |
4170 | } |
4171 | |
4172 | @@ -105,7 +109,7 @@ |
4173 | { |
4174 | QVariantMap detailProperties; |
4175 | detailProperties["theProperty"] = "theValue"; |
4176 | - History::Participant original("accountId", "identifier", "contactId", "alias", "avatar", detailProperties); |
4177 | + History::Participant original("accountId", "identifier", "contactId", "alias", "avatar", History::ParticipantStateRegular, History::ParticipantRoleAdmin, detailProperties); |
4178 | |
4179 | History::Participant copy(original); |
4180 | |
4181 | @@ -114,6 +118,8 @@ |
4182 | QCOMPARE(copy.contactId(), original.contactId()); |
4183 | QCOMPARE(copy.alias(), original.alias()); |
4184 | QCOMPARE(copy.avatar(), original.avatar()); |
4185 | + QCOMPARE(copy.state(), original.state()); |
4186 | + QCOMPARE(copy.roles(), original.roles()); |
4187 | QCOMPARE(copy.detailProperties(), original.detailProperties()); |
4188 | } |
4189 | |
4190 | @@ -121,7 +127,7 @@ |
4191 | { |
4192 | QVariantMap detailProperties; |
4193 | detailProperties["theProperty2"] = "theValue2"; |
4194 | - History::Participant original("accountId2", "identifier2", "contactId2", "alias2", "avatar2", detailProperties); |
4195 | + History::Participant original("accountId2", "identifier2", "contactId2", "alias2", "avatar2", History::ParticipantStateRegular, History::ParticipantRoleMember, detailProperties); |
4196 | |
4197 | History::Participant copy; |
4198 | copy = original; |
4199 | @@ -131,6 +137,8 @@ |
4200 | QCOMPARE(copy.contactId(), original.contactId()); |
4201 | QCOMPARE(copy.alias(), original.alias()); |
4202 | QCOMPARE(copy.avatar(), original.avatar()); |
4203 | + QCOMPARE(copy.state(), original.state()); |
4204 | + QCOMPARE(copy.roles(), original.roles()); |
4205 | QCOMPARE(copy.detailProperties(), original.detailProperties()); |
4206 | } |
4207 | |
4208 | @@ -166,13 +174,15 @@ |
4209 | QVariantMap detailProperties; |
4210 | detailProperties["someDetailProperty"] = "someValue"; |
4211 | |
4212 | - History::Participant participant("theAccountId", "theIdentifier", "theContactId", "theAlias", "theAvatar", detailProperties); |
4213 | + History::Participant participant("theAccountId", "theIdentifier", "theContactId", "theAlias", "theAvatar", History::ParticipantStateRegular, History::ParticipantRoleAdmin, detailProperties); |
4214 | QVariantMap properties = participant.properties(); |
4215 | QCOMPARE(properties[History::FieldAccountId].toString(), participant.accountId()); |
4216 | QCOMPARE(properties[History::FieldIdentifier].toString(), participant.identifier()); |
4217 | QCOMPARE(properties[History::FieldContactId].toString(), participant.contactId()); |
4218 | QCOMPARE(properties[History::FieldAlias].toString(), participant.alias()); |
4219 | QCOMPARE(properties[History::FieldAvatar].toString(), participant.avatar()); |
4220 | + QCOMPARE(properties[History::FieldParticipantState].toUInt(), participant.state()); |
4221 | + QCOMPARE(properties[History::FieldParticipantRoles].toUInt(), participant.roles()); |
4222 | QCOMPARE(properties[History::FieldDetailProperties].toMap(), participant.detailProperties()); |
4223 | } |
4224 | |
4225 | @@ -186,6 +196,8 @@ |
4226 | properties[History::FieldContactId] = "someContactId"; |
4227 | properties[History::FieldAlias] = "someAlias"; |
4228 | properties[History::FieldAvatar] = "someAvatar"; |
4229 | + properties[History::FieldParticipantState] = History::ParticipantStateRegular; |
4230 | + properties[History::FieldParticipantRoles] = History::ParticipantRoleAdmin; |
4231 | detailProperties["someDetailProperty"] = "someValue"; |
4232 | properties[History::FieldDetailProperties] = detailProperties; |
4233 | |
4234 | @@ -195,6 +207,8 @@ |
4235 | QCOMPARE(participant.contactId(), properties[History::FieldContactId].toString()); |
4236 | QCOMPARE(participant.alias(), properties[History::FieldAlias].toString()); |
4237 | QCOMPARE(participant.avatar(), properties[History::FieldAvatar].toString()); |
4238 | + QCOMPARE(participant.state(), properties[History::FieldParticipantState].toUInt()); |
4239 | + QCOMPARE(participant.roles(), properties[History::FieldParticipantRoles].toUInt()); |
4240 | QCOMPARE(participant.detailProperties(), properties[History::FieldDetailProperties].toMap()); |
4241 | } |
4242 | |
4243 | |
4244 | === modified file 'tests/libhistoryservice/TextEventTest.cpp' |
4245 | --- tests/libhistoryservice/TextEventTest.cpp 2015-09-28 23:13:21 +0000 |
4246 | +++ tests/libhistoryservice/TextEventTest.cpp 2016-11-24 12:52:24 +0000 |
4247 | @@ -53,33 +53,34 @@ |
4248 | QTest::addColumn<int>("messageStatus"); |
4249 | QTest::addColumn<QDateTime>("readTimestamp"); |
4250 | QTest::addColumn<QString>("subject"); |
4251 | + QTest::addColumn<int>("informationType"); |
4252 | QTest::addColumn<QStringList>("participants"); |
4253 | |
4254 | QTest::newRow("unread message") << "testAccountId" << "testThreadId" << "testEventId" |
4255 | << "testSenderId" << QDateTime::currentDateTime().addDays(-10) |
4256 | << true << "One Test Message" << (int)History::MessageTypeText |
4257 | - << 0 << QDateTime::currentDateTime().addDays(-5) << "Test Subject" |
4258 | + << 0 << QDateTime::currentDateTime().addDays(-5) << "Test Subject" << (int) History::InformationTypeJoined |
4259 | << (QStringList() << "testParticipant"); |
4260 | QTest::newRow("read message") << "testAccountId2" << "testThreadId2" << "testEventId2" |
4261 | << "testSenderId2" << QDateTime::currentDateTime().addDays(-10) |
4262 | << false << "One Test Message" << (int)History::MessageTypeText |
4263 | - << 0 << QDateTime::currentDateTime().addDays(-5) << "Test Subject 2" |
4264 | + << 0 << QDateTime::currentDateTime().addDays(-5) << "Test Subject 2" << (int) History::InformationTypeLeaving |
4265 | << (QStringList() << "testParticipant2"); |
4266 | QTest::newRow("message status") << "testAccountId" << "testThreadId" << "testEventId" |
4267 | << "testSenderId" << QDateTime::currentDateTime().addDays(-10) |
4268 | << true << "One Test Message" << (int)History::MessageTypeText |
4269 | << (int)History::MessageStatusAccepted |
4270 | - << QDateTime::currentDateTime().addDays(-5) << "Test Subject 3" |
4271 | + << QDateTime::currentDateTime().addDays(-5) << "Test Subject 3" << (int) History::InformationTypeSelfLeaving |
4272 | << (QStringList() << "testParticipant"); |
4273 | QTest::newRow("multi party message") << "testAccountId" << "testThreadId" << "testEventId" |
4274 | << "testSenderId" << QDateTime::currentDateTime().addDays(-10) |
4275 | << true << "One Test Message" << (int)History::MessageTypeMultiPart |
4276 | - << 0 << QDateTime::currentDateTime().addDays(-5) << QString() |
4277 | + << 0 << QDateTime::currentDateTime().addDays(-5) << QString() << (int) History::InformationTypeNone |
4278 | << (QStringList() << "testParticipant"); |
4279 | QTest::newRow("multiple participants") << "testAccountId2" << "testThreadId2" << "testEventId2" |
4280 | << "testSenderId2" << QDateTime::currentDateTime().addDays(-7) |
4281 | << true << "One Test Message 2" << (int)History::MessageTypeText |
4282 | - << 0 << QDateTime::currentDateTime().addDays(-4) << QString() |
4283 | + << 0 << QDateTime::currentDateTime().addDays(-4) << QString() << (int) History::InformationTypeNone |
4284 | << (QStringList() << "one" << "two" << "three" << "four"); |
4285 | } |
4286 | |
4287 | @@ -96,10 +97,11 @@ |
4288 | QFETCH(int, messageStatus); |
4289 | QFETCH(QDateTime, readTimestamp); |
4290 | QFETCH(QString, subject); |
4291 | + QFETCH(int, informationType); |
4292 | QFETCH(QStringList, participants); |
4293 | History::TextEvent event(accountId, threadId, eventId, senderId, timestamp, newEvent, |
4294 | message, (History::MessageType)messageType, (History::MessageStatus)messageStatus, |
4295 | - readTimestamp, subject, History::TextEventAttachments(), |
4296 | + readTimestamp, subject, (History::InformationType) informationType, History::TextEventAttachments(), |
4297 | participantsFromIdentifiers(accountId, participants)); |
4298 | |
4299 | // check that the values are properly set |
4300 | @@ -150,33 +152,34 @@ |
4301 | QTest::addColumn<int>("messageStatus"); |
4302 | QTest::addColumn<QDateTime>("readTimestamp"); |
4303 | QTest::addColumn<QString>("subject"); |
4304 | + QTest::addColumn<int>("informationType"); |
4305 | QTest::addColumn<QStringList>("participants"); |
4306 | |
4307 | QTest::newRow("unread message") << "testAccountId" << "testThreadId" << "testEventId" |
4308 | << "testSenderId" << QDateTime::currentDateTime().addDays(-10) |
4309 | << true << "One Test Message" << (int)History::MessageTypeText |
4310 | - << 0 << QDateTime::currentDateTime().addDays(-5) << "Test Subject" |
4311 | + << 0 << QDateTime::currentDateTime().addDays(-5) << "Test Subject" << (int) History::InformationTypeJoined |
4312 | << (QStringList() << "testParticipant"); |
4313 | QTest::newRow("read message") << "testAccountId2" << "testThreadId2" << "testEventId2" |
4314 | << "testSenderId2" << QDateTime::currentDateTime().addDays(-10) |
4315 | << false << "One Test Message" << (int)History::MessageTypeText |
4316 | - << 0 << QDateTime::currentDateTime().addDays(-5) << "Test Subject 2" |
4317 | + << 0 << QDateTime::currentDateTime().addDays(-5) << "Test Subject 2" << (int) History::InformationTypeNone |
4318 | << (QStringList() << "testParticipant2"); |
4319 | QTest::newRow("message status") << "testAccountId" << "testThreadId" << "testEventId" |
4320 | << "testSenderId" << QDateTime::currentDateTime().addDays(-10) |
4321 | << true << "One Test Message" << (int)History::MessageTypeText |
4322 | << (int)History::MessageStatusAccepted |
4323 | - << QDateTime::currentDateTime().addDays(-5) << "Test Subject 3" |
4324 | + << QDateTime::currentDateTime().addDays(-5) << "Test Subject 3" << (int) History::InformationTypeNone |
4325 | << (QStringList() << "testParticipant"); |
4326 | QTest::newRow("multi party message") << "testAccountId" << "testThreadId" << "testEventId" |
4327 | << "testSenderId" << QDateTime::currentDateTime().addDays(-10) |
4328 | << true << "One Test Message" << (int)History::MessageTypeMultiPart |
4329 | - << 0 << QDateTime::currentDateTime().addDays(-5) << QString() |
4330 | + << 0 << QDateTime::currentDateTime().addDays(-5) << QString() << (int) History::InformationTypeNone |
4331 | << (QStringList() << "testParticipant"); |
4332 | QTest::newRow("multiple participants") << "testAccountId2" << "testThreadId2" << "testEventId2" |
4333 | << "testSenderId2" << QDateTime::currentDateTime().addDays(-7) |
4334 | << true << "One Test Message 2" << (int)History::MessageTypeText |
4335 | - << 0 << QDateTime::currentDateTime().addDays(-4) << QString() |
4336 | + << 0 << QDateTime::currentDateTime().addDays(-4) << QString() << (int) History::InformationTypeNone |
4337 | << (QStringList() << "one" << "two" << "three" << "four"); |
4338 | } |
4339 | |
4340 | @@ -193,6 +196,7 @@ |
4341 | QFETCH(int, messageStatus); |
4342 | QFETCH(QDateTime, readTimestamp); |
4343 | QFETCH(QString, subject); |
4344 | + QFETCH(int, informationType); |
4345 | QFETCH(QStringList, participants); |
4346 | |
4347 | QVariantMap properties; |
4348 | @@ -207,6 +211,7 @@ |
4349 | properties[History::FieldMessageStatus] = messageStatus; |
4350 | properties[History::FieldReadTimestamp] = readTimestamp.toString("yyyy-MM-ddTHH:mm:ss.zzz"); |
4351 | properties[History::FieldSubject] = subject; |
4352 | + properties[History::FieldInformationType] = informationType; |
4353 | properties[History::FieldParticipants] = participantsFromIdentifiers(accountId, participants).toVariantList(); |
4354 | |
4355 | History::TextEvent textEvent = History::TextEvent::fromProperties(properties); |
4356 | @@ -221,6 +226,7 @@ |
4357 | QCOMPARE(textEvent.messageStatus(), (History::MessageStatus) messageStatus); |
4358 | QCOMPARE(textEvent.readTimestamp().toString(Qt::ISODate), readTimestamp.toString(Qt::ISODate)); |
4359 | QCOMPARE(textEvent.subject(), subject); |
4360 | + QCOMPARE(textEvent.informationType(), (History::InformationType) informationType); |
4361 | QCOMPARE(textEvent.participants().identifiers(), participants); |
4362 | } |
4363 | |
4364 | @@ -245,33 +251,34 @@ |
4365 | QTest::addColumn<int>("messageStatus"); |
4366 | QTest::addColumn<QDateTime>("readTimestamp"); |
4367 | QTest::addColumn<QString>("subject"); |
4368 | + QTest::addColumn<int>("informationType"); |
4369 | QTest::addColumn<QStringList>("participants"); |
4370 | |
4371 | QTest::newRow("unread message") << "testAccountId" << "testThreadId" << "testEventId" |
4372 | << "testSenderId" << QDateTime::currentDateTime().addDays(-10) |
4373 | << true << "One Test Message" << (int)History::MessageTypeText |
4374 | - << 0 << QDateTime::currentDateTime().addDays(-5) << "Test Subject" |
4375 | + << 0 << QDateTime::currentDateTime().addDays(-5) << "Test Subject" << (int) History::InformationTypeNone |
4376 | << (QStringList() << "testParticipant"); |
4377 | QTest::newRow("read message") << "testAccountId2" << "testThreadId2" << "testEventId2" |
4378 | << "testSenderId2" << QDateTime::currentDateTime().addDays(-10) |
4379 | << false << "One Test Message" << (int)History::MessageTypeText |
4380 | - << 0 << QDateTime::currentDateTime().addDays(-5) << "Test Subject 2" |
4381 | + << 0 << QDateTime::currentDateTime().addDays(-5) << "Test Subject 2" << (int) History::InformationTypeNone |
4382 | << (QStringList() << "testParticipant2"); |
4383 | QTest::newRow("message status") << "testAccountId" << "testThreadId" << "testEventId" |
4384 | << "testSenderId" << QDateTime::currentDateTime().addDays(-10) |
4385 | << true << "One Test Message" << (int)History::MessageTypeText |
4386 | << (int)History::MessageStatusAccepted |
4387 | - << QDateTime::currentDateTime().addDays(-5) << "Test Subject 3" |
4388 | + << QDateTime::currentDateTime().addDays(-5) << "Test Subject 3" << (int) History::InformationTypeNone |
4389 | << (QStringList() << "testParticipant"); |
4390 | QTest::newRow("multi party message") << "testAccountId" << "testThreadId" << "testEventId" |
4391 | << "testSenderId" << QDateTime::currentDateTime().addDays(-10) |
4392 | << true << "One Test Message" << (int)History::MessageTypeMultiPart |
4393 | - << 0 << QDateTime::currentDateTime().addDays(-5) << QString() |
4394 | + << 0 << QDateTime::currentDateTime().addDays(-5) << QString() << (int) History::InformationTypeNone |
4395 | << (QStringList() << "testParticipant"); |
4396 | QTest::newRow("multiple participants") << "testAccountId2" << "testThreadId2" << "testEventId2" |
4397 | << "testSenderId2" << QDateTime::currentDateTime().addDays(-7) |
4398 | << true << "One Test Message 2" << (int)History::MessageTypeText |
4399 | - << 0 << QDateTime::currentDateTime().addDays(-4) << QString() |
4400 | + << 0 << QDateTime::currentDateTime().addDays(-4) << QString() << (int) History::InformationTypeNone |
4401 | << (QStringList() << "one" << "two" << "three" << "four"); |
4402 | } |
4403 | |
4404 | @@ -288,10 +295,11 @@ |
4405 | QFETCH(int, messageStatus); |
4406 | QFETCH(QDateTime, readTimestamp); |
4407 | QFETCH(QString, subject); |
4408 | + QFETCH(int, informationType); |
4409 | QFETCH(QStringList, participants); |
4410 | History::TextEvent event(accountId, threadId, eventId, senderId, timestamp, newEvent, |
4411 | message, (History::MessageType)messageType, (History::MessageStatus)messageStatus, |
4412 | - readTimestamp, subject, History::TextEventAttachments(), |
4413 | + readTimestamp, subject, History::InformationTypeNone, History::TextEventAttachments(), |
4414 | participantsFromIdentifiers(accountId, participants)); |
4415 | |
4416 | QVariantMap properties = event.properties(); |
4417 | @@ -306,6 +314,7 @@ |
4418 | QCOMPARE(properties[History::FieldMessageStatus].toInt(), messageStatus); |
4419 | QCOMPARE(properties[History::FieldReadTimestamp].toString(), readTimestamp.toString("yyyy-MM-ddTHH:mm:ss.zzz")); |
4420 | QCOMPARE(properties[History::FieldSubject].toString(), subject); |
4421 | + QCOMPARE(properties[History::FieldInformationType].toInt(), informationType); |
4422 | QCOMPARE(History::Participants::fromVariantList(properties[History::FieldParticipants].toList()).identifiers(), participants); |
4423 | } |
4424 | |
4425 | |
4426 | === modified file 'tests/libhistoryservice/ThreadTest.cpp' |
4427 | --- tests/libhistoryservice/ThreadTest.cpp 2015-10-02 16:48:19 +0000 |
4428 | +++ tests/libhistoryservice/ThreadTest.cpp 2016-11-24 12:52:24 +0000 |
4429 | @@ -102,11 +102,12 @@ |
4430 | break; |
4431 | } |
4432 | |
4433 | - History::Thread threadItem(accountId, threadId, type, participantsFromIdentifiers(accountId, participants), event, count, unreadCount); |
4434 | + History::Thread threadItem(accountId, threadId, type, participantsFromIdentifiers(accountId, participants), event.timestamp(), event, count, unreadCount); |
4435 | QCOMPARE(threadItem.accountId(), accountId); |
4436 | QCOMPARE(threadItem.threadId(), threadId); |
4437 | QCOMPARE(threadItem.type(), type); |
4438 | QCOMPARE(threadItem.participants().identifiers(), participants); |
4439 | + QCOMPARE(threadItem.timestamp(), event.timestamp()); |
4440 | QCOMPARE(threadItem.lastEvent(), event); |
4441 | QCOMPARE(threadItem.count(), count); |
4442 | QCOMPARE(threadItem.unreadCount(), unreadCount); |
4443 | @@ -172,6 +173,7 @@ |
4444 | QCOMPARE(thread.threadId(), threadId); |
4445 | QCOMPARE(thread.type(), type); |
4446 | QCOMPARE(thread.participants().identifiers(), participants); |
4447 | + QCOMPARE(thread.timestamp(), event.timestamp()); |
4448 | QCOMPARE(thread.count(), count); |
4449 | QCOMPARE(thread.unreadCount(), unreadCount); |
4450 | QVERIFY(thread.lastEvent() == event); |
4451 | @@ -229,7 +231,7 @@ |
4452 | break; |
4453 | } |
4454 | |
4455 | - History::Thread threadItem(accountId, threadId, type, participantsFromIdentifiers(accountId, participants), event, count, unreadCount); |
4456 | + History::Thread threadItem(accountId, threadId, type, participantsFromIdentifiers(accountId, participants), event.timestamp(), event, count, unreadCount); |
4457 | QVariantMap properties = threadItem.properties(); |
4458 | QCOMPARE(properties[History::FieldAccountId].toString(), accountId); |
4459 | QCOMPARE(properties[History::FieldThreadId].toString(), threadId); |
4460 | |
4461 | === modified file 'tests/plugins/sqlite/SqlitePluginTest.cpp' |
4462 | --- tests/plugins/sqlite/SqlitePluginTest.cpp 2015-09-23 22:27:38 +0000 |
4463 | +++ tests/plugins/sqlite/SqlitePluginTest.cpp 2016-11-24 12:52:24 +0000 |
4464 | @@ -249,36 +249,35 @@ |
4465 | { |
4466 | // clear the database |
4467 | SQLiteDatabase::instance()->reopen(); |
4468 | + QSqlQuery query(SQLiteDatabase::instance()->database()); |
4469 | |
4470 | QVERIFY(mPlugin->beginBatchOperation()); |
4471 | - mPlugin->createThreadForParticipants("accountOne", History::EventTypeText, QStringList() << "participantOne"); |
4472 | - mPlugin->createThreadForParticipants("accountTwo", History::EventTypeText, QStringList() << "participantTwo"); |
4473 | - mPlugin->createThreadForParticipants("accountThree", History::EventTypeText, QStringList() << "participantThree"); |
4474 | + QVERIFY(query.exec("UPDATE schema_version SET version=123")); |
4475 | QVERIFY(mPlugin->endBatchOperation()); |
4476 | |
4477 | // check that the data was actually written |
4478 | - QSqlQuery query(SQLiteDatabase::instance()->database()); |
4479 | - QVERIFY(query.exec("SELECT count(*) FROM threads")); |
4480 | + QVERIFY(query.exec("SELECT version FROM schema_version")); |
4481 | QVERIFY(query.next()); |
4482 | - QCOMPARE(query.value(0).toInt(), 3); |
4483 | + QCOMPARE(query.value(0).toInt(), 123); |
4484 | } |
4485 | |
4486 | void SqlitePluginTest::testRollback() |
4487 | { |
4488 | // clear the database |
4489 | SQLiteDatabase::instance()->reopen(); |
4490 | + QSqlQuery query(SQLiteDatabase::instance()->database()); |
4491 | + QVERIFY(query.exec("SELECT version FROM schema_version")); |
4492 | + QVERIFY(query.next()); |
4493 | + int version = query.value(0).toInt(); |
4494 | |
4495 | QVERIFY(mPlugin->beginBatchOperation()); |
4496 | - mPlugin->createThreadForParticipants("accountOne", History::EventTypeText, QStringList() << "participantOne"); |
4497 | - mPlugin->createThreadForParticipants("accountTwo", History::EventTypeText, QStringList() << "participantTwo"); |
4498 | - mPlugin->createThreadForParticipants("accountThree", History::EventTypeText, QStringList() << "participantThree"); |
4499 | + QVERIFY(query.exec("UPDATE schema_version SET version=255")); |
4500 | QVERIFY(mPlugin->rollbackBatchOperation()); |
4501 | |
4502 | // check that the steps were reverted |
4503 | - QSqlQuery query(SQLiteDatabase::instance()->database()); |
4504 | - QVERIFY(query.exec("SELECT count(*) FROM threads")); |
4505 | + QVERIFY(query.exec("SELECT version FROM schema_version")); |
4506 | QVERIFY(query.next()); |
4507 | - QCOMPARE(query.value(0).toInt(), 0); |
4508 | + QCOMPARE(query.value(0).toInt(), version); |
4509 | } |
4510 | |
4511 | void SqlitePluginTest::testQueryThreads() |
4512 | @@ -319,7 +318,7 @@ |
4513 | QTest::newRow("text event with attachments") << History::TextEvent("mmsAccountId", "mmsSender", "mmsEventId", "mmsSender", |
4514 | QDateTime::currentDateTime(), false, "Hello with attachments", |
4515 | History::MessageTypeMultiPart, History::MessageStatusDelivered, |
4516 | - QDateTime::currentDateTime(), "The Subject", attachments).properties(); |
4517 | + QDateTime::currentDateTime(), "The Subject", History::InformationTypeNone, attachments).properties(); |
4518 | } |
4519 | |
4520 | void SqlitePluginTest::testWriteTextEvent() |
4521 | @@ -406,7 +405,7 @@ |
4522 | thread[History::FieldEventId].toString(), "theAttachmentId", "text/plain", "/file/path"); |
4523 | History::TextEvent textEvent(thread[History::FieldAccountId].toString(), thread[History::FieldThreadId].toString(), "theEventId", |
4524 | "theParticipant", QDateTime::currentDateTime(), true, "Hi there!", History::MessageTypeMultiPart, |
4525 | - History::MessageStatusPending, QDateTime::currentDateTime(), "theSubject", |
4526 | + History::MessageStatusPending, QDateTime::currentDateTime(), "theSubject", History::InformationTypeNone, |
4527 | History::TextEventAttachments() << attachment); |
4528 | QCOMPARE(mPlugin->writeTextEvent(textEvent.properties()), History::EventWriteCreated); |
4529 | |
4530 | @@ -606,7 +605,7 @@ |
4531 | QString("textEventId%1").arg(QString::number(i)), "textParticipant", |
4532 | QDateTime::currentDateTime(), true, "Hello World!", History::MessageTypeMultiPart, |
4533 | History::MessageStatusPending, QDateTime::currentDateTime(), |
4534 | - "theSubject", History::TextEventAttachments() << attachment); |
4535 | + "theSubject", History::InformationTypeNone, History::TextEventAttachments() << attachment); |
4536 | QCOMPARE(mPlugin->writeTextEvent(textEvent.properties()), History::EventWriteCreated); |
4537 | } |
4538 | |
4539 | @@ -659,7 +658,7 @@ |
4540 | QTest::newRow("text event with attachments") << History::TextEvent("mmsAccountId", "mmsSender", "mmsEventId", "mmsSender", |
4541 | QDateTime::currentDateTime(), false, "Hello with attachments", |
4542 | History::MessageTypeMultiPart, History::MessageStatusDelivered, |
4543 | - QDateTime::currentDateTime(), "The Subject", attachments).properties(); |
4544 | + QDateTime::currentDateTime(), "The Subject", History::InformationTypeNone, attachments).properties(); |
4545 | QTest::newRow("missed call") << History::VoiceEvent("theAccountId", "theSenderId", "theEventId", "theSenderId", |
4546 | QDateTime::currentDateTime(), true, true).properties(); |
4547 | QTest::newRow("incoming call") << History::VoiceEvent("otherAccountId", "otherSenderId", "otherEventId", "otherSenderId", |
Left some comments