[Merge] lp:history-service/staging into lp:history-service
Roberto Mier Escandón
roberto.escandon at canonical.com
Wed Oct 26 11:28:27 UTC 2016
Review: Needs Fixing
Left some comments
Diff comments:
>
> === modified file 'Ubuntu/History/historymodel.cpp'
> --- Ubuntu/History/historymodel.cpp 2015-10-08 21:52:59 +0000
> +++ Ubuntu/History/historymodel.cpp 2016-10-20 14:04:14 +0000
> @@ -89,16 +92,98 @@
> case TypeRole:
> result = properties[History::FieldType];
> break;
> - case ParticipantsRole:
> + case ParticipantsRole: {
> + // FIXME: reimplement in a cleaner way
> + History::Participants participants = History::Participants::fromVariantList(properties[History::FieldParticipants].toList());
> if (mMatchContacts) {
> - result = History::ContactMatcher::instance()->contactInfo(properties[History::FieldAccountId].toString(),
> - History::Participants::fromVariantList(properties[History::FieldParticipants].toList()).identifiers());
> + QVariantList finalParticipantsList;
> + QVariantList participantsInfo = History::ContactMatcher::instance()->contactInfo(properties[History::FieldAccountId].toString(),
> + participants.identifiers());
> + int count = 0;
> + Q_FOREACH(const QVariant &participantInfo, participantsInfo) {
> + QVariantMap newMap = participantInfo.toMap();
> + if (participants.at(count).state() != History::ParticipantStateRegular) {
> + count++;
> + continue;
> + }
> + newMap[History::FieldParticipantState] = participants.at(count).state();
> + newMap[History::FieldParticipantRoles] = participants.at(count++).roles();
> + finalParticipantsList << newMap;
> + }
This Q_FOREACH sounds weird to me. Wouldn't be better doing like this?:
for (int i = 0; i < participantsInfo.size(); ++i) {
if (participants.at(i).state() == History::ParticipantStateRegular) {
QVariantMap newMap = participantsInfo.value(i).toMap();
newMap[History::FieldParticipantState] = participants.at(i).state();
newMap[History::FieldParticipantRoles] = participants.at(i).roles();
finalParticipantsList << newMap;
}
}
> + result = finalParticipantsList;
> } else {
> //FIXME: handle contact changes
> result = properties[History::FieldParticipants];
> }
> break;
> }
> + case ParticipantsRemotePendingRole: {
> + // FIXME: reimplement in a cleaner way
> + QStringList identifiers;
> + History::Participants participants;
> + // filter remote pending participants
> + Q_FOREACH(const History::Participant &participant, History::Participants::fromVariantList(properties[History::FieldParticipants].toList())) {
> + if (participant.state() == History::ParticipantStateRemotePending) {
> + participants << participant;
> + }
> + }
> +
> + if (mMatchContacts) {
> + QVariantList finalParticipantsList;
> + QVariantList participantsInfo = History::ContactMatcher::instance()->contactInfo(properties[History::FieldAccountId].toString(),
> + participants.identifiers());
> + int count = 0;
> + Q_FOREACH(const QVariant &participantInfo, participantsInfo) {
> + QVariantMap newMap = participantInfo.toMap();
> + newMap[History::FieldParticipantState] = participants.at(count).state();
> + newMap[History::FieldParticipantRoles] = participants.at(count++).roles();
> + finalParticipantsList << newMap;
> + }
Same as before,
for (int i = 0; i < participantsInfo.size(); ++i) {
QVariantMap newMap = participantsInfo.value(i).toMap();
newMap[History::FieldParticipantState] = participants.at(i).state();
newMap[History::FieldParticipantRoles] = participants.at(i).roles();
finalParticipantsList << newMap;
}
Actually, as both are quite similar, maybe it would be a good idea doing this in a separated method
> + result = finalParticipantsList;
> + } else {
> + //FIXME: handle contact changes
> + result = participants.identifiers();
> + }
> +
> + break;
> + }
> + case ParticipantsLocalPendingRole: {
> + // FIXME: reimplement in a cleaner way
> + QStringList identifiers;
> + History::Participants participants;
> + Q_FOREACH(const History::Participant &participant, History::Participants::fromVariantList(properties[History::FieldParticipants].toList())) {
> + if (participant.state() == History::ParticipantStateLocalPending) {
> + participants << participant;
> + }
> + }
> +
> + if (mMatchContacts) {
> + QVariantList finalParticipantsList;
> + QVariantList participantsInfo = History::ContactMatcher::instance()->contactInfo(properties[History::FieldAccountId].toString(),
> + identifiers);
> + int count = 0;
> + Q_FOREACH(const QVariant &participantInfo, participantsInfo) {
> + QVariantMap newMap = participantInfo.toMap();
> + newMap[History::FieldParticipantState] = participants.at(count).state();
> + newMap[History::FieldParticipantRoles] = participants.at(count++).roles();
> + finalParticipantsList << newMap;
> + }
> + result = finalParticipantsList;
Same as before. Rewrite in a separated method
> + } else {
> + //FIXME: handle contact changes
> + result = identifiers;
> + }
> +
> + break;
> + }
> + case ParticipantIdsRole:
> + result = History::Participants::fromVariantList(properties[History::FieldParticipants].toList()).identifiers();
> + break;
> + case TimestampRole:
> + result = QDateTime::fromString(properties[History::FieldTimestamp].toString(), Qt::ISODate);
> + break;
> + }
> +
> return result;
> }
>
>
> === modified file 'daemon/historydaemon.cpp'
> --- daemon/historydaemon.cpp 2015-11-20 12:53:49 +0000
> +++ daemon/historydaemon.cpp 2016-10-20 14:04:14 +0000
> @@ -414,8 +613,270 @@
> event[History::FieldMissed] = missed;
> event[History::FieldDuration] = duration;
> // FIXME: check what to do when there are more than just one remote participant
> - event[History::FieldRemoteParticipant] = participants[0];
> - writeEvents(QList<QVariantMap>() << event);
> + event[History::FieldRemoteParticipant] = participants[0].toMap()[History::FieldIdentifier];
> + writeEvents(QList<QVariantMap>() << event, properties);
> +}
> +
> +void HistoryDaemon::onTextChannelAvailable(const Tp::TextChannelPtr channel)
> +{
> + // for Rooms we need to explicitly create the thread to allow users to send messages to groups even
> + // before they receive any message.
> + // for other types, we can wait until messages are received
> + if (channel->targetHandleType() == Tp::HandleTypeRoom) {
> + QString accountId = channel->property(History::FieldAccountId).toString();
> + QVariantMap properties = propertiesFromChannel(channel);
> +
> + // first try to fetch the existing thread to see if there is any.
> + QVariantMap thread = threadForProperties(accountId,
> + History::EventTypeText,
> + properties,
> + matchFlagsForChannel(channel),
> + false);
> + if (thread.isEmpty()) {
> + // if there no existing thread, create one
> + properties["Requested"] = channel->isRequested();
> + thread = threadForProperties(accountId,
> + History::EventTypeText,
> + properties,
> + matchFlagsForChannel(channel),
> + true);
> +
> + // write information event including all initial invitees
> + Q_FOREACH(const Tp::ContactPtr contact, channel->groupRemotePendingContacts(false)) {
> + writeInformationEvent(thread, History::InformationTypeInvitationSent, contact->alias());
> + }
> +
> + // update participants only if the thread is not available previously. Otherwise we'll wait for membersChanged event
> + // for reflect in conversation information events for modified participants.
> + updateRoomParticipants(channel);
> + }
> +
> + // write an entry saying you joined the group if 'joined' flag in thread is false and modify that flag.
> + if (!thread[History::FieldChatRoomInfo].toMap()["Joined"].toBool()) {
Maybe hold "Joined" string as a const value somewhere?
> + writeInformationEvent(thread, History::InformationTypeSelfJoined);
> + // update backend
> + updateRoomProperties(channel, QVariantMap{{"Joined", true}});
> + }
> +
> + Tp::AbstractInterface *room_interface = channel->optionalInterface<Tp::Client::ChannelInterfaceRoomInterface>();
> + Tp::AbstractInterface *room_config_interface = channel->optionalInterface<Tp::Client::ChannelInterfaceRoomConfigInterface>();
> + Tp::AbstractInterface *subject_interface = channel->optionalInterface<Tp::Client::ChannelInterfaceSubjectInterface>();
> + ChannelInterfaceRolesInterface *roles_interface = channel->optionalInterface<ChannelInterfaceRolesInterface>();
> +
> + QList<Tp::AbstractInterface*> interfaces;
> + interfaces << room_interface << room_config_interface << subject_interface << roles_interface;
> + for (auto interface : interfaces) {
> + if (interface) {
> + interface->setMonitorProperties(true);
> + interface->setProperty(History::FieldAccountId, accountId);
> + interface->setProperty(History::FieldThreadId, thread[History::FieldThreadId].toString());
> + interface->setProperty(History::FieldType, thread[History::FieldType].toInt());
> + connect(interface, SIGNAL(propertiesChanged(const QVariantMap &,const QStringList &)),
> + SLOT(onRoomPropertiesChanged(const QVariantMap &,const QStringList &)));
> + // update the stored info
> + Q_EMIT interface->propertiesChanged(getInterfaceProperties(interface), QStringList());
> + }
> + }
> +
> + connect(channel.data(), SIGNAL(groupMembersChanged(const Tp::Contacts &, const Tp::Contacts &, const Tp::Contacts &, const Tp::Contacts &, const Tp::Channel::GroupMemberChangeDetails &)),
> + SLOT(onGroupMembersChanged(const Tp::Contacts &, const Tp::Contacts &, const Tp::Contacts &, const Tp::Contacts &, const Tp::Channel::GroupMemberChangeDetails &)));
> +
> + connect(roles_interface, SIGNAL(RolesChanged(const HandleRolesMap&, const HandleRolesMap&)), SLOT(onRolesChanged(const HandleRolesMap&, const HandleRolesMap&)));
> + }
> +}
> +
> +void HistoryDaemon::onGroupMembersChanged(const Tp::Contacts &groupMembersAdded,
> + const Tp::Contacts &groupLocalPendingMembersAdded,
> + const Tp::Contacts &groupRemotePendingMembersAdded,
> + const Tp::Contacts &groupMembersRemoved,
> + const Tp::Channel::GroupMemberChangeDetails &details)
> +{
> + Tp::TextChannelPtr channel(qobject_cast<Tp::TextChannel*>(sender()));
> +
> + QVariantMap properties;
> + QVariantMap thread;
> +
> + // information events for members updates.
> + bool hasRemotePendingMembersAdded = groupRemotePendingMembersAdded.size() > 0;
> + bool hasMembersAdded = groupMembersAdded.size() > 0;
> + bool hasMembersRemoved = groupMembersRemoved.size() > 0;
> +
> + if (hasRemotePendingMembersAdded || hasMembersAdded || hasMembersRemoved) {
> + properties = propertiesFromChannel(channel);
> + thread = threadForProperties(channel->property(History::FieldAccountId).toString(),
> + History::EventTypeText,
> + properties,
> + matchFlagsForChannel(channel),
> + false);
> + if (!thread.isEmpty()) {
> + if (hasRemotePendingMembersAdded) {
> + Q_FOREACH (const Tp::ContactPtr& contact, groupRemotePendingMembersAdded) {
> + if (!foundInThread(contact, thread)) {
> + writeInformationEvent(thread, History::InformationTypeInvitationSent, contact->alias());
> + }
> + }
> + }
> + if (hasMembersAdded) {
> + Q_FOREACH (const Tp::ContactPtr& contact, groupMembersAdded) {
> + // if this member was not previously regular member in thread, notify about his join
> + if (!foundAsMemberInThread(contact, thread)) {
> + writeInformationEvent(thread, History::InformationTypeJoined, contact->alias());
> + }
> + }
> + }
> +
> + if (hasMembersRemoved) {
> + if (channel->groupSelfContactRemoveInfo().isValid()) {
> + // evaluate if we are leaving by our own or we are kicked
> + History::InformationType type = History::InformationTypeSelfLeaving;
> + if (channel->groupSelfContactRemoveInfo().hasReason()) {
> + switch (channel->groupSelfContactRemoveInfo().reason()) {
> + case ChannelGroupChangeReasonKicked:
> + type = History::InformationTypeSelfKicked;
> + break;
> + case ChannelGroupChangeReasonGone:
> + type = History::InformationTypeGroupGone;
> + break;
> + }
> + }
> + writeInformationEvent(thread, type);
> + // update backend
> + updateRoomProperties(channel, QVariantMap{{"Joined", false}});
> + }
> + else // don't notify any other group member removal if we are leaving the group
> + {
> + Q_FOREACH (const Tp::ContactPtr& contact, groupMembersRemoved) {
> + // inform about removed members other than us
> + if (contact->id() != channel->groupSelfContact()->id()) {
> + writeInformationEvent(thread, History::InformationTypeLeaving, contact->alias());
> + }
> + }
> + }
> + }
> + }
> + }
> +
> + updateRoomParticipants(channel);
> +}
> +
> +void HistoryDaemon::updateRoomParticipants(const Tp::TextChannelPtr channel)
> +{
> + if (!channel) {
> + return;
> + }
> +
> + QVariantList participants;
> + QStringList contactsAdded;
> +
> + ChannelInterfaceRolesInterface *roles_interface = channel->optionalInterface<ChannelInterfaceRolesInterface>();
> + RolesMap roles;
> + if (roles_interface) {
> + roles = roles_interface->getRoles();
> + }
> +
> + Q_FOREACH(const Tp::ContactPtr contact, channel->groupRemotePendingContacts(false)) {
> + QVariantMap participant;
> + contactsAdded << contact->id();
> + participant[History::FieldIdentifier] = contact->id();
> + participant[History::FieldAlias] = contact->alias();
> + participant[History::FieldParticipantState] = History::ParticipantStateRemotePending;
> + participant[History::FieldParticipantRoles] = roles[contact->handle().at(0)];
> + participants << QVariant::fromValue(participant);
> + }
> + Q_FOREACH(const Tp::ContactPtr contact, channel->groupLocalPendingContacts(false)) {
> + QVariantMap participant;
> + contactsAdded << contact->id();
> + participant[History::FieldIdentifier] = contact->id();
> + participant[History::FieldAlias] = contact->alias();
> + participant[History::FieldParticipantState] = History::ParticipantStateLocalPending;
> + participant[History::FieldParticipantRoles] = roles[contact->handle().at(0)];
> + participants << QVariant::fromValue(participant);
> + }
> +
> + Q_FOREACH(const Tp::ContactPtr contact, channel->groupContacts(false)) {
> + // do not include remote and local pending members
> + if (contactsAdded.contains(contact->id())) {
> + continue;
> + }
> + QVariantMap participant;
> + participant[History::FieldIdentifier] = contact->id();
> + participant[History::FieldAlias] = contact->alias();
> + participant[History::FieldParticipantState] = History::ParticipantStateRegular;
> + participant[History::FieldParticipantRoles] = roles[contact->handle().at(0)];
> + participants << QVariant::fromValue(participant);
> + }
> +
> + QString accountId = channel->property(History::FieldAccountId).toString();
> + QString threadId = channel->targetId();
> + if (mBackend->updateRoomParticipants(accountId, threadId, History::EventTypeText, participants)) {
> + QVariantMap updatedThread = getSingleThread(History::EventTypeText, accountId, threadId, QVariantMap());
> + mDBus.notifyThreadsModified(QList<QVariantMap>() << updatedThread);
> + }
> +}
> +
> +void HistoryDaemon::updateRoomRoles(const Tp::TextChannelPtr &channel, const RolesMap &rolesMap)
> +{
> + if (!channel) {
> + return;
> + }
> +
> + QVariantMap participantsRoles;
> +
> + Q_FOREACH(const Tp::ContactPtr contact, channel->groupRemotePendingContacts(false)) {
> + participantsRoles[contact->id()] = rolesMap[contact->handle().at(0)];
> + }
> + Q_FOREACH(const Tp::ContactPtr contact, channel->groupLocalPendingContacts(false)) {
> + participantsRoles[contact->id()] = rolesMap[contact->handle().at(0)];
> + }
> +
> + Q_FOREACH(const Tp::ContactPtr contact, channel->groupContacts(false)) {
> + if (!participantsRoles.contains(contact->id())) {
> + participantsRoles[contact->id()] = rolesMap[contact->handle().at(0)];
> + }
> + }
> +
> + // update participants roles
> + QString accountId = channel->property(History::FieldAccountId).toString();
> + QString threadId = channel->targetId();
> + if (mBackend->updateRoomParticipantsRoles(accountId, threadId, History::EventTypeText, participantsRoles)) {
> + QVariantMap updatedThread = getSingleThread(History::EventTypeText, accountId, threadId, QVariantMap());
> + mDBus.notifyThreadsModified(QList<QVariantMap>() << updatedThread);
> + }
> +
> + // update self roles in room properties
> + uint selfRoles = rolesMap[channel->groupSelfContact()->handle().at(0)];
> + updateRoomProperties(channel, QVariantMap{{"SelfRoles", selfRoles}});
> +}
> +
> +void HistoryDaemon::onRoomPropertiesChanged(const QVariantMap &properties,const QStringList &invalidated)
> +{
> + QString accountId = sender()->property(History::FieldAccountId).toString();
> + QString threadId = sender()->property(History::FieldThreadId).toString();
> + History::EventType type = (History::EventType)sender()->property(History::FieldType).toInt();
> +
> + // get thread before updating to see if there are changes to insert as information events
> + QVariantMap thread = getSingleThread(type, accountId, threadId, QVariantMap());
> + if (!thread.empty()) {
> + writeRoomChangesInformationEvents(thread, properties);
> + }
> +
> + updateRoomProperties(accountId, threadId, type, properties, invalidated);
> +}
> +
> +void HistoryDaemon::updateRoomProperties(const Tp::TextChannelPtr &channel, const QVariantMap &properties)
> +{
> + QString accountId = channel->property(History::FieldAccountId).toString();
> + QString threadId = channel->targetId();
> + History::EventType type = History::EventTypeText;
> + updateRoomProperties(accountId, threadId, type, properties, QStringList());
> +}
> +
> +void HistoryDaemon::updateRoomProperties(const QString &accountId, const QString &threadId, History::EventType type, const QVariantMap &properties, const QStringList &invalidated)
> +{
> + if (mBackend->updateRoomInfo(accountId, threadId, type, properties, invalidated)) {
> + QVariantMap thread = getSingleThread(type, accountId, threadId, QVariantMap());
> + mDBus.notifyThreadsModified(QList<QVariantMap>() << thread);
> + }
> }
>
> void HistoryDaemon::onMessageReceived(const Tp::TextChannelPtr textChannel, const Tp::ReceivedMessage &message)
> @@ -424,8 +885,13 @@
> QString eventId;
> Tp::MessagePart header = message.header();
> QString senderId;
> + QVariantMap properties = propertiesFromChannel(textChannel);
> History::MessageStatus status = History::MessageStatusUnknown;
> - if (message.sender()->handle().at(0) == textChannel->connection()->selfHandle()) {
> + if (!message.sender() || message.sender()->handle().at(0) == textChannel->connection()->selfHandle()) {
> + qDebug() << __PRETTY_FUNCTION__ << message.sender();
Do we need these traces?
> + if (message.sender()) {
> + qDebug() << __PRETTY_FUNCTION__ << "size: " << message.sender()->handle().size() << "first handle" << message.sender()->handle().at(0);
> + }
> senderId = "self";
> status = History::MessageStatusDelivered;
> } else {
> @@ -707,3 +1184,88 @@
> hash += "#-#" + thread[History::FieldThreadId].toString();
> return hash;
> }
> +
> +QVariantMap HistoryDaemon::getInterfaceProperties(const Tp::AbstractInterface *interface)
> +{
> + QDBusInterface propsInterface(interface->service(), interface->path(), "org.freedesktop.DBus.Properties");
> + QDBusReply<QVariantMap> reply = propsInterface.call("GetAll", interface->interface());
> + if (!reply.isValid()) {
> + qWarning() << "Failed to fetch channel properties for interface" << interface->interface() << reply.error().message();
> + }
> + return reply.value();
> +}
> +
> +void HistoryDaemon::writeInformationEvent(const QVariantMap &thread, History::InformationType type, const QString &subject, const QString &sender, const QString &text)
> +{
> + History::TextEvent historyEvent = History::TextEvent(thread[History::FieldAccountId].toString(),
> + thread[History::FieldThreadId].toString(),
> + QString(QCryptographicHash::hash(QByteArray(
> + (QDateTime::currentDateTime().toString() + subject + text).toLatin1()),
> + QCryptographicHash::Md5).toHex()),
> + sender,
> + QDateTime::currentDateTime(),
> + false,
> + text,
> + History::MessageTypeInformation,
> + History::MessageStatusUnknown,
> + QDateTime::currentDateTime(),
> + subject,
> + type);
> + writeEvents(QList<QVariantMap>() << historyEvent.properties(), thread);
> +}
> +
> +void HistoryDaemon::writeRoomChangesInformationEvents(const QVariantMap &thread, const QVariantMap &interfaceProperties)
> +{
> + if (!thread.isEmpty()) {
> + // group subject
> + QString storedSubject = thread[History::FieldChatRoomInfo].toMap()["Subject"].toString();
> + QString newSubject = interfaceProperties["Subject"].toString();
Should be also have "Subject" somewhere as const value?
> + if (!newSubject.isEmpty() && storedSubject != newSubject) {
> + //see if we have an actor. If actor is 'me', we have changed that subject
> + QString actor = thread[History::FieldChatRoomInfo].toMap()["Actor"].toString();
> + if (actor == "me") {
> + actor = "self";
> + }
> + writeInformationEvent(thread, History::InformationTypeTitleChanged, newSubject, actor);
> + }
> + }
> +}
> +
> +void HistoryDaemon::writeRolesInformationEvents(const QVariantMap &thread, const Tp::ChannelPtr &channel, const RolesMap &rolesMap)
> +{
> + if (thread.isEmpty()) {
> + return;
> + }
> +
> + if (!thread[History::FieldChatRoomInfo].toMap()["Joined"].toBool()) {
> + return;
> + }
> +
> + // list of identifiers for current channel admins
> + QStringList adminIds;
> +
> + Q_FOREACH(const Tp::ContactPtr contact, channel->groupContacts(false)) {
> + // see if admin role (ChannelAdminRole == 2)
> + if (rolesMap[contact->handle().at(0)] & AdminRole) {
> + adminIds << contact->id();
> + }
> + }
> +
> + Q_FOREACH (QVariant participant, thread[History::FieldParticipants].toList()) {
> + QString participantId = participant.toMap()[History::FieldIdentifier].toString();
> + if (adminIds.contains(participantId)) {
> + // see if already was admin or not (ChannelAdminRole == 2)
> + if (! (participant.toMap()[History::FieldParticipantRoles].toUInt() & AdminRole)) {
> + writeInformationEvent(thread, History::InformationTypeAdminGranted, participantId);
> + }
> + }
> + }
> +
> + //evaluate now self roles
> + if (rolesMap[channel->groupSelfContact()->handle().at(0)] & AdminRole) {
> + uint selfRoles = thread[History::FieldChatRoomInfo].toMap()["SelfRoles"].toUInt();
> + if (! (selfRoles & AdminRole)) {
> + writeInformationEvent(thread, History::InformationTypeSelfAdminGranted);
> + }
> + }
> +}
>
> === modified file 'plugins/sqlite/sqlitehistoryplugin.cpp'
> --- plugins/sqlite/sqlitehistoryplugin.cpp 2015-11-20 11:40:36 +0000
> +++ plugins/sqlite/sqlitehistoryplugin.cpp 2016-10-20 14:04:14 +0000
> @@ -469,58 +510,341 @@
> return result;
> }
>
> -// Writer
> -QVariantMap SQLiteHistoryPlugin::createThreadForParticipants(const QString &accountId, History::EventType type, const QStringList &participants)
> +bool SQLiteHistoryPlugin::updateRoomParticipants(const QString &accountId, const QString &threadId, History::EventType type, const QVariantList &participants)
> +{
> + QSqlQuery query(SQLiteDatabase::instance()->database());
> + if (accountId.isEmpty() || threadId.isEmpty()) {
> + return false;
> + }
> +
> + SQLiteDatabase::instance()->beginTransation();
> + QString deleteString("DELETE FROM thread_participants WHERE threadId=:threadId AND type=:type AND accountId=:accountId");
> + query.prepare(deleteString);
> + query.bindValue(":accountId", accountId);
> + query.bindValue(":threadId", threadId);
> + query.bindValue(":type", type);
> + if (!query.exec()) {
> + qCritical() << "Error removing old participants:" << query.lastError() << query.lastQuery();
> + SQLiteDatabase::instance()->rollbackTransaction();
> + return false;
> + }
> +
> + // and insert the participants
> + Q_FOREACH(const QVariant &participantVariant, participants) {
> + QVariantMap participant = participantVariant.toMap();
> + query.prepare("INSERT INTO thread_participants (accountId, threadId, type, participantId, normalizedId, alias, state, roles)"
> + "VALUES (:accountId, :threadId, :type, :participantId, :normalizedId, :alias, :state, :roles)");
> + query.bindValue(":accountId", accountId);
> + query.bindValue(":threadId", threadId);
> + query.bindValue(":type", type);
> + query.bindValue(":participantId", participant["identifier"].toString());
> + query.bindValue(":normalizedId", participant["identifier"].toString());
> + query.bindValue(":alias", participant["alias"].toString());
> + query.bindValue(":state", participant["state"].toUInt());
> + query.bindValue(":roles", participant["roles"].toUInt());
> + if (!query.exec()) {
> + qCritical() << "Error:" << query.lastError() << query.lastQuery();
> + SQLiteDatabase::instance()->rollbackTransaction();
> + return false;
> + }
> + }
> +
> + if (!SQLiteDatabase::instance()->finishTransaction()) {
> + qCritical() << "Failed to commit the transaction.";
> + return false;
> + }
> +
> + QVariantMap existingThread = getSingleThread(type,
> + accountId,
> + threadId,
> + QVariantMap());
> +
> + if (!existingThread.isEmpty()) {
> + addThreadsToCache(QList<QVariantMap>() << existingThread);
> + }
> +
> + return true;
> +}
> +
> +bool SQLiteHistoryPlugin::updateRoomParticipantsRoles(const QString &accountId, const QString &threadId, History::EventType type, const QVariantMap &participantsRoles)
> +{
> + QSqlQuery query(SQLiteDatabase::instance()->database());
> + if (accountId.isEmpty() || threadId.isEmpty()) {
> + return false;
> + }
> +
> + SQLiteDatabase::instance()->beginTransation();
> + Q_FOREACH(const QString &participantId, participantsRoles.keys()) {
> + query.prepare("UPDATE thread_participants SET roles=:roles WHERE accountId=:accountId AND threadId=:threadId AND type=:type AND participantId=:participantId");
> + query.bindValue(":roles", participantsRoles.value(participantId).toUInt());
> + query.bindValue(":accountId", accountId);
> + query.bindValue(":threadId", threadId);
> + query.bindValue(":type", type);
> + query.bindValue(":participantId", participantId);
> + if (!query.exec()) {
> + qCritical() << "Error:" << query.lastError() << query.lastQuery();
> + SQLiteDatabase::instance()->rollbackTransaction();
> + return false;
> + }
> + }
> +
> + if (!SQLiteDatabase::instance()->finishTransaction()) {
> + qCritical() << "Failed to commit the transaction.";
> + return false;
> + }
> +
> + QVariantMap existingThread = getSingleThread(type,
> + accountId,
> + threadId,
> + QVariantMap());
> +
> + if (!existingThread.isEmpty()) {
> + addThreadsToCache(QList<QVariantMap>() << existingThread);
> + }
> +
> + return true;
> +}
> +
> +bool SQLiteHistoryPlugin::updateRoomInfo(const QString &accountId, const QString &threadId, History::EventType type, const QVariantMap &properties, const QStringList &invalidated)
> +{
> + QSqlQuery query(SQLiteDatabase::instance()->database());
> +
> + if (threadId.isEmpty() || accountId.isEmpty()) {
> + return false;
> + }
> +
> + SQLiteDatabase::instance()->beginTransation();
> +
> + QDateTime creationTimestamp = QDateTime::fromTime_t(properties["CreationTimestamp"].toUInt());
> + QDateTime timestamp = QDateTime::fromTime_t(properties["Timestamp"].toUInt());
> +
> + QVariantMap propertyMapping;
> + propertyMapping["RoomName"] = "roomName";
> + propertyMapping["Server"] = "server";
> + propertyMapping["Creator"] = "creator";
> + propertyMapping["CreationTimestamp"] = "creationTimestamp";
> + propertyMapping["Anonymous"] = "anonymous";
> + propertyMapping["InviteOnly"] = "inviteOnly";
> + propertyMapping["Limit"] = "participantLimit";
> + propertyMapping["Moderated"] = "moderated";
> + propertyMapping["Title"] = "title";
> + propertyMapping["Description"] = "description";
> + propertyMapping["Persistent"] = "persistent";
> + propertyMapping["Private"] = "private";
> + propertyMapping["PasswordProtected"] = "passwordProtected";
> + propertyMapping["Password"] = "password";
> + propertyMapping["PasswordHint"] = "passwordHint";
> + propertyMapping["CanUpdateConfiguration"] = "canUpdateConfiguration";
> + propertyMapping["Subject"] = "subject";
> + propertyMapping["Actor"] = "actor";
> + propertyMapping["Timestamp"] = "timestamp";
> + propertyMapping["Joined"] = "joined";
> + propertyMapping["SelfRoles"] = "selfRoles";
> +
> + QStringList changedPropListValues;
> + // populate sql query
> + Q_FOREACH (const QString &key, properties.keys()) {
> + if (propertyMapping.contains(key)) {
> + QString prop = propertyMapping[key].toString();
> + changedPropListValues << QString(prop+"=:"+ prop);
> + }
> + }
> + if (changedPropListValues.isEmpty()) {
> + return false;
> + }
> +
> + query.prepare("UPDATE chat_room_info SET "+ changedPropListValues.join(", ")+" WHERE accountId=:accountId AND threadId=:threadId AND type=:type");
> + query.bindValue(":accountId", accountId);
> + query.bindValue(":threadId", threadId);
> + query.bindValue(":type", (int) type);
> + query.bindValue(":roomName", properties["RoomName"].toString());
> + query.bindValue(":server", properties["Server"].toString());
> + query.bindValue(":creator", properties["Creator"].toString());
> + query.bindValue(":creationTimestamp", creationTimestamp.toUTC().toString(timestampFormat));
> + query.bindValue(":anonymous", properties["Anonymous"].toBool());
> + query.bindValue(":inviteOnly", properties["InviteOnly"].toBool());
> + query.bindValue(":participantLimit", properties["Limit"].toInt());
> + query.bindValue(":moderated", properties["Moderated"].toBool());
> + query.bindValue(":title", properties["Title"].toString());
> + query.bindValue(":description", properties["Description"].toString());
> + query.bindValue(":persistent", properties["Persistent"].toBool());
> + query.bindValue(":private", properties["Private"].toBool());
> + query.bindValue(":passwordProtected", properties["PasswordProtected"].toBool());
> + query.bindValue(":password", properties["Password"].toString());
> + query.bindValue(":passwordHint", properties["PasswordHint"].toString());
> + query.bindValue(":canUpdateConfiguration", properties["CanUpdateConfiguration"].toBool());
> + query.bindValue(":subject", properties["Subject"].toString());
> + query.bindValue(":actor", properties["Actor"].toString());
> + query.bindValue(":timestamp", timestamp.toUTC().toString(timestampFormat));
> + query.bindValue(":joined", properties["Joined"].toBool());
> + query.bindValue(":selfRoles", properties["SelfRoles"].toInt());
> +
> + if (!query.exec()) {
> + qCritical() << "Error:" << query.lastError() << query.lastQuery();
> + SQLiteDatabase::instance()->rollbackTransaction();
> + return false;
> + }
> +
> + if (!SQLiteDatabase::instance()->finishTransaction()) {
> + qCritical() << "Failed to commit the transaction.";
> + return false;
> + }
> +
> + QVariantMap existingThread = getSingleThread(type,
> + accountId,
> + threadId,
> + QVariantMap());
> +
> + if (!existingThread.isEmpty()) {
> + addThreadsToCache(QList<QVariantMap>() << existingThread);
> + }
> +
> + return true;
> +}
> +
> +QVariantMap SQLiteHistoryPlugin::createThreadForProperties(const QString &accountId, History::EventType type, const QVariantMap &properties)
> {
> // WARNING: this function does NOT test to check if the thread is already created, you should check using HistoryReader::threadForParticipants()
>
> QVariantMap thread;
> + History::Participants participants = History::Participants::fromVariant(properties[History::FieldParticipantIds]);
>
> // Create a new thread
> // FIXME: define what the threadId will be
> - QString threadId = participants.join("%");
> + QString threadId;
> + History::ChatType chatType = (History::ChatType)properties[History::FieldChatType].toInt();
> + QVariantMap chatRoomInfo;
> +
> + SQLiteDatabase::instance()->beginTransation();
> +
> + if (chatType == History::ChatTypeRoom) {
> + threadId = properties[History::FieldThreadId].toString();
> + // we cannot save chat room without threadId
> + if (accountId.isEmpty() || threadId.isEmpty()) {
> + SQLiteDatabase::instance()->rollbackTransaction();
> + return thread;
> + }
> + chatRoomInfo = properties[History::FieldChatRoomInfo].toMap();
> + QSqlQuery query(SQLiteDatabase::instance()->database());
> +
> + QDateTime creationTimestamp = QDateTime::fromTime_t(chatRoomInfo["CreationTimestamp"].toUInt());
> + QDateTime timestamp = QDateTime::fromTime_t(chatRoomInfo["Timestamp"].toUInt());
> +
> + 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) "
> + "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)");
> + query.bindValue(":accountId", accountId);
> + query.bindValue(":threadId", threadId);
> + query.bindValue(":type", (int) type);
> + query.bindValue(":roomName", chatRoomInfo["RoomName"].toString());
> + query.bindValue(":server", chatRoomInfo["Server"].toString());
> + query.bindValue(":creator", chatRoomInfo["Creator"].toString());
> + query.bindValue(":creationTimestamp", creationTimestamp.toUTC().toString(timestampFormat));
> + query.bindValue(":anonymous", chatRoomInfo["Anonymous"].toBool());
> + query.bindValue(":inviteOnly", chatRoomInfo["InviteOnly"].toBool());
> + query.bindValue(":participantLimit", chatRoomInfo["Limit"].toInt());
> + query.bindValue(":moderated", chatRoomInfo["Moderated"].toBool());
> + query.bindValue(":title", chatRoomInfo["Title"].toString());
> + query.bindValue(":description", chatRoomInfo["Description"].toString());
> + query.bindValue(":persistent", chatRoomInfo["Persistent"].toBool());
> + query.bindValue(":private", chatRoomInfo["Private"].toBool());
> + query.bindValue(":passwordProtected", chatRoomInfo["PasswordProtected"].toBool());
> + query.bindValue(":password", chatRoomInfo["Password"].toString());
> + query.bindValue(":passwordHint", chatRoomInfo["PasswordHint"].toString());
> + query.bindValue(":canUpdateConfiguration", chatRoomInfo["CanUpdateConfiguration"].toBool());
> + query.bindValue(":subject", chatRoomInfo["Subject"].toString());
> + query.bindValue(":actor", chatRoomInfo["Actor"].toString());
> + query.bindValue(":timestamp", timestamp.toUTC().toString(timestampFormat));
> + query.bindValue(":joined", chatRoomInfo["Joined"].toBool());
> + query.bindValue(":selfRoles", chatRoomInfo["SelfRoles"].toInt());
> +
> + if (!query.exec()) {
> + qCritical() << "Error:" << query.lastError() << query.lastQuery();
> + SQLiteDatabase::instance()->rollbackTransaction();
> + return QVariantMap();
> + }
> + for (QVariantMap::iterator iter = chatRoomInfo.begin(); iter != chatRoomInfo.end();) {
> + if (!iter.value().isValid()) {
> + iter = chatRoomInfo.erase(iter);
> + } else {
> + iter++;
> + }
> + }
> + thread[History::FieldChatRoomInfo] = chatRoomInfo;
> + } else {
> + threadId = participants.identifiers().join("%");
> + }
>
> QSqlQuery query(SQLiteDatabase::instance()->database());
> - query.prepare("INSERT INTO threads (accountId, threadId, type, count, unreadCount)"
> - "VALUES (:accountId, :threadId, :type, :count, :unreadCount)");
> + query.prepare("INSERT INTO threads (accountId, threadId, type, count, unreadCount, chatType, lastEventTimestamp)"
> + "VALUES (:accountId, :threadId, :type, :count, :unreadCount, :chatType, :lastEventTimestamp)");
> query.bindValue(":accountId", accountId);
> query.bindValue(":threadId", threadId);
> query.bindValue(":type", (int) type);
> query.bindValue(":count", 0);
> query.bindValue(":unreadCount", 0);
> + query.bindValue(":chatType", (int) chatType);
> + // make sure threads are created with an up-to-date timestamp
> + query.bindValue(":lastEventTimestamp", QDateTime::currentDateTimeUtc().toString(timestampFormat));
> if (!query.exec()) {
> qCritical() << "Error:" << query.lastError() << query.lastQuery();
> + SQLiteDatabase::instance()->rollbackTransaction();
> return QVariantMap();
> }
>
> // and insert the participants
> - Q_FOREACH(const QString &participant, participants) {
> - query.prepare("INSERT INTO thread_participants (accountId, threadId, type, participantId, normalizedId)"
> - "VALUES (:accountId, :threadId, :type, :participantId, :normalizedId)");
> + Q_FOREACH(const History::Participant &participant, participants) {
> + query.prepare("INSERT INTO thread_participants (accountId, threadId, type, participantId, normalizedId, alias, state, roles)"
> + "VALUES (:accountId, :threadId, :type, :participantId, :normalizedId, :alias, :state, :roles)");
> query.bindValue(":accountId", accountId);
> query.bindValue(":threadId", threadId);
> query.bindValue(":type", type);
> - query.bindValue(":participantId", participant);
> - query.bindValue(":normalizedId", History::Utils::normalizeId(accountId, participant));
> + query.bindValue(":participantId", participant.identifier());
> + query.bindValue(":normalizedId", History::Utils::normalizeId(accountId, participant.identifier()));
> + query.bindValue(":alias", participant.alias());
> + query.bindValue(":state", participant.state());
> + query.bindValue(":roles", participant.roles());
> if (!query.exec()) {
> qCritical() << "Error:" << query.lastError() << query.lastQuery();
> + SQLiteDatabase::instance()->rollbackTransaction();
> return QVariantMap();
> }
> }
>
> + if (!SQLiteDatabase::instance()->finishTransaction()) {
> + qCritical() << "Failed to commit the transaction.";
> + return QVariantMap();
> + }
> +
> // and finally create the thread
> thread[History::FieldAccountId] = accountId;
> thread[History::FieldThreadId] = threadId;
> thread[History::FieldType] = (int) type;
> - thread[History::FieldParticipants] = History::ContactMatcher::instance()->contactInfo(accountId, participants, true);
> + QVariantList contactList;
> + int count = 0;
> + Q_FOREACH (QVariant contact, History::ContactMatcher::instance()->contactInfo(accountId, participants.identifiers(), true)) {
> + QVariantMap map = contact.toMap();
> + map["state"] = participants.at(count).state();
> + map["roles"] = participants.at(count++).roles();
> + contactList << map;
> + }
same as before, i prefer using classic for (int i = 0... etc. But up to you to change it or not.
In any case, if you leave the code as is, I would set count++ in a single line:
map["state"] = participants.at(count).state();
map["roles"] = participants.at(count).roles();
++count;
> + thread[History::FieldParticipants] = contactList;
> thread[History::FieldCount] = 0;
> thread[History::FieldUnreadCount] = 0;
> + thread[History::FieldChatType] = (int)chatType;
>
> addThreadsToCache(QList<QVariantMap>() << thread);
>
> return thread;
> }
>
> +// Writer
> +QVariantMap SQLiteHistoryPlugin::createThreadForParticipants(const QString &accountId, History::EventType type, const QStringList &participants)
> +{
> + QVariantMap properties;
> + properties[History::FieldParticipantIds] = participants;
> + return createThreadForProperties(accountId, type, properties);
> +}
> +
> bool SQLiteHistoryPlugin::removeThread(const QVariantMap &thread)
> {
> QSqlQuery query(SQLiteDatabase::instance()->database());
> @@ -820,13 +1165,31 @@
> thread[History::FieldEventId] = query.value(2);
> thread[History::FieldCount] = query.value(3);
> thread[History::FieldUnreadCount] = query.value(4);
> - QStringList participants = query.value(5).toString().split("|,|");
> - thread[History::FieldParticipants] = History::ContactMatcher::instance()->contactInfo(accountId, participants, true);
> + QStringList participants = query.value(6).toString().split("|,|", QString::SkipEmptyParts);
> + QList<int> participantStatus;
> + QStringList participantStatusString = query.value(7).toString().split("|,|", QString::SkipEmptyParts);
> + Q_FOREACH(const QString &statusString, participantStatusString) {
> + participantStatus << statusString.toUInt();
> + }
> + QStringList participantRolesString = query.value(8).toString().split("|,|", QString::SkipEmptyParts);
> + QList<int> participantRoles;
> + Q_FOREACH(const QString &rolesString, participantRolesString) {
> + participantRoles << rolesString.toUInt();
> + }
> + QVariantList contactList;
> + int count = 0;
> + Q_FOREACH (QVariant contact, History::ContactMatcher::instance()->contactInfo(accountId, participants, true)) {
> + QVariantMap map = contact.toMap();
> + map["state"] = participantStatus.at(count);
> + map["roles"] = participantRoles.at(count++);
use classic for or count++ in a separated line
> + contactList << map;
> + }
> + thread[History::FieldParticipants] = contactList;
>
> // the generic event fields
> - thread[History::FieldSenderId] = query.value(6);
> - thread[History::FieldTimestamp] = toLocalTimeString(query.value(7).toDateTime());
> - thread[History::FieldNewEvent] = query.value(8).toBool();
> + thread[History::FieldSenderId] = query.value(9);
> + thread[History::FieldTimestamp] = toLocalTimeString(query.value(5).toDateTime());
> + thread[History::FieldNewEvent] = query.value(10).toBool();
>
> // the next step is to get the last event
> switch (type) {
--
https://code.launchpad.net/~phablet-team/history-service/staging/+merge/308933
Your team Ubuntu Phablet Team is subscribed to branch lp:history-service.
More information about the Ubuntu-reviews
mailing list