diff --git a/src/rocketchataccount.cpp b/src/rocketchataccount.cpp index 245ffbcb..68fa736e 100644 --- a/src/rocketchataccount.cpp +++ b/src/rocketchataccount.cpp @@ -1,618 +1,618 @@ /* Copyright (c) 2017-2018 Montel Laurent This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License or ( at your option ) version 3 or, at the discretion of KDE e.V. ( which shall act as a proxy as in section 14 of the GPLv3 ), any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "messagemodel.h" #include "rocketchataccount.h" #include "roommodel.h" #include "roomwrapper.h" #include "typingnotification.h" #include "usersmodel.h" #include "ruqola_debug.h" #include "ruqola.h" #include "messagequeue.h" #include "rocketchatbackend.h" #include "usersforroommodel.h" #include "roomfilterproxymodel.h" #include "ruqolalogger.h" #include "ruqolaserverconfig.h" #include "usercompletermodel.h" #include "statusmodel.h" #include "utils.h" #include "rocketchatcache.h" #include "ddpapi/ddpclient.h" #include "restapi/restapirequest.h" #include #include #include #include RocketChatAccount::RocketChatAccount(const QString &accountFileName, QObject *parent) : QObject(parent) { //create an uniq file for each account if (!qEnvironmentVariableIsEmpty("RUQOLA_LOGFILE")) { mRuqolaLogger = new RuqolaLogger; } mRuqolaServerConfig = new RuqolaServerConfig; //TODO add account name. mSettings = new RocketChatAccountSettings(accountFileName, this); connect(mSettings, &RocketChatAccountSettings::loginStatusChanged, this, &RocketChatAccount::loginStatusChanged); connect(mSettings, &RocketChatAccountSettings::serverURLChanged, this, &RocketChatAccount::serverUrlChanged); connect(mSettings, &RocketChatAccountSettings::userIDChanged, this, &RocketChatAccount::userIDChanged); connect(mSettings, &RocketChatAccountSettings::userNameChanged, this, &RocketChatAccount::userNameChanged); mRocketChatBackend = new RocketChatBackend(this, this); loadSettings(); mRoomFilterProxyModel = new RoomFilterProxyModel(this); mUserCompleterModel = new UserCompleterModel(this); mStatusModel = new StatusModel(this); mRoomModel = new RoomModel(this); connect(mRoomModel, &RoomModel::needToUpdateNotification, this, &RocketChatAccount::slotNeedToUpdateNotification); mRoomFilterProxyModel->setSourceModel(mRoomModel); mUserModel = new UsersModel(this); mMessageQueue = new MessageQueue(this); mTypingNotification = new TypingNotification(this); mCache = new RocketChatCache(this, this); connect(mCache, &RocketChatCache::fileDownloaded, this, &RocketChatAccount::fileDownloaded); connect(mTypingNotification, &TypingNotification::informTypingStatus, this, &RocketChatAccount::slotInformTypingStatus); QTimer::singleShot(0, this, &RocketChatAccount::clearModels); } RocketChatAccount::~RocketChatAccount() { delete mCache; mCache = nullptr; qDeleteAll(mUsersForRoomModels); qDeleteAll(mMessageModels); delete mRuqolaServerConfig; delete mRuqolaLogger; } void RocketChatAccount::slotNeedToUpdateNotification() { bool hasAlert = false; int nbUnread = 0; mRoomModel->getUnreadAlertFromAccount(hasAlert, nbUnread); Q_EMIT updateNotification(hasAlert, nbUnread, accountName()); } void RocketChatAccount::clearModels() { // Clear rooms data and refill it with data in the cache, if there is mRoomModel->reset(); mMessageQueue->loadCache(); //Try to send queue message mMessageQueue->processQueue(); } StatusModel *RocketChatAccount::statusModel() const { return mStatusModel; } UserCompleterModel *RocketChatAccount::userCompleterModel() const { return mUserCompleterModel; } RuqolaServerConfig *RocketChatAccount::ruqolaServerConfig() const { return mRuqolaServerConfig; } RuqolaLogger *RocketChatAccount::ruqolaLogger() const { return mRuqolaLogger; } RoomFilterProxyModel *RocketChatAccount::roomFilterProxyModel() const { return mRoomFilterProxyModel; } RocketChatBackend *RocketChatAccount::rocketChatBackend() const { return mRocketChatBackend; } void RocketChatAccount::loadSettings() { mSettings->loadSettings(); } MessageQueue *RocketChatAccount::messageQueue() const { return mMessageQueue; } RocketChatAccountSettings *RocketChatAccount::settings() const { return mSettings; } void RocketChatAccount::slotInformTypingStatus(const QString &room, bool typing) { ddp()->informTypingStatus(room, typing, mSettings->userName()); } RoomModel *RocketChatAccount::roomModel() const { return mRoomModel; } UsersModel *RocketChatAccount::usersModel() const { return mUserModel; } RoomWrapper *RocketChatAccount::getRoom(const QString &roomId) { return mRoomModel->findRoom(roomId); } MessageModel *RocketChatAccount::getMessageModelForRoom(const QString &roomID) { if (MessageModel *model = mMessageModels.value(roomID)) { return model; } else { mMessageModels[roomID] = new MessageModel(roomID, this, this); return mMessageModels[roomID]; } } UsersForRoomModel *RocketChatAccount::getUsersForRoomModel(const QString &roomId) { UsersForRoomModel *model = nullptr; if ((model = mUsersForRoomModels.value(roomId))) { return model; } else { model = new UsersForRoomModel(this); model->setCurrentRoomId(roomId); mUsersForRoomModels[roomId] = model; return model; } } QString RocketChatAccount::getUserCurrentMessage(const QString &roomId) { return mUserCurrentMessage.value(roomId); } void RocketChatAccount::setUserCurrentMessage(const QString &message, const QString &roomId) { if (mUserCurrentMessage.contains(roomId)) { if (!message.trimmed().isEmpty()) { mUserCurrentMessage[roomId] = message; } else { mUserCurrentMessage.remove(roomId); } } else { if (!message.trimmed().isEmpty()) { mUserCurrentMessage.insert(roomId, message); } } } void RocketChatAccount::textEditing(const QString &roomId, const QString &str) { mTypingNotification->setText(roomId, str); } void RocketChatAccount::sendMessage(const QString &roomID, const QString &message) { QJsonObject json; json[QStringLiteral("rid")] = roomID; json[QStringLiteral("msg")] = message; ddp()->method(QStringLiteral("sendMessage"), QJsonDocument(json), DDPClient::Persistent); } void RocketChatAccount::updateMessage(const QString &roomID, const QString &messageId, const QString &message) { QJsonObject json; json[QStringLiteral("rid")] = roomID; json[QStringLiteral("msg")] = message; json[QStringLiteral("_id")] = messageId; ddp()->method(QStringLiteral("updateMessage"), QJsonDocument(json), DDPClient::Persistent); } QString RocketChatAccount::avatarUrl(const QString &userId) { return mCache->avatarUrl(userId); } void RocketChatAccount::insertAvatarUrl(const QString &userId, const QString &url) { mCache->insertAvatarUrl(userId, url); } RestApiRequest *RocketChatAccount::restApi() { if (!mRestApi) { mRestApi = new RestApiRequest(this); mRestApi->setServerUrl(mSettings->serverUrl()); } return mRestApi; } void RocketChatAccount::leaveRoom(const QString &roomId) { ddp()->leaveRoom(roomId); } void RocketChatAccount::hideRoom(const QString &roomId) { ddp()->hideRoom(roomId); } DDPClient *RocketChatAccount::ddp() { if (!mDdp) { mDdp = new DDPClient(this, this); connect(mDdp, &DDPClient::loginStatusChanged, this, &RocketChatAccount::loginStatusChanged); connect(mDdp, &DDPClient::changed, this, &RocketChatAccount::changed); connect(mDdp, &DDPClient::added, this, &RocketChatAccount::added); mDdp->setServerUrl(mSettings->serverUrl()); mDdp->start(); } return mDdp; } DDPClient::LoginStatus RocketChatAccount::loginStatus() { if (mDdp) { return ddp()->loginStatus(); } else { return DDPClient::LoggedOut; } } void RocketChatAccount::tryLogin() { qCDebug(RUQOLA_LOG) << "Attempting login" << mSettings->userName() << "on" << mSettings->serverUrl(); // Reset model views foreach (const QString &key, mMessageModels.keys()) { MessageModel *m = mMessageModels.take(key); delete m; } delete mDdp; mDdp = nullptr; // This creates a new ddp() object. // DDP will automatically try to connect and login. ddp(); // In the meantime, load cache... mRoomModel->reset(); } void RocketChatAccount::logOut() { mSettings->logout(); foreach (const QString &key, mMessageModels.keys()) { MessageModel *m = mMessageModels.take(key); delete m; } mRoomModel->clear(); QJsonObject user; user[QStringLiteral("username")] = mSettings->userName(); QJsonObject json; json[QStringLiteral("user")] = user; ddp()->method(QStringLiteral("logout"), QJsonDocument(json)); delete mDdp; mDdp = nullptr; Q_EMIT loginStatusChanged(); qCDebug(RUQOLA_LOG) << "Successfully logged out!"; } void RocketChatAccount::clearUnreadMessages(const QString &roomId) { ddp()->clearUnreadMessages(roomId); } void RocketChatAccount::changeFavorite(const QString &roomId, bool checked) { ddp()->toggleFavorite(roomId, checked); } void RocketChatAccount::openChannel(const QString &url) { qCDebug(RUQOLA_LOG) << " void RocketChatAccount::openChannel(const QString &url)"<uniqueId() + roomId).toUtf8(), QCryptographicHash::Md5).toHex()); #if defined(Q_OS_IOS) || defined(Q_OS_ANDROID) const QString scheme = "org.jitsi.meet://"; #else const QString scheme = QStringLiteral("https://"); #endif const QString url = scheme + mRuqolaServerConfig->jitsiMeetUrl() + QLatin1Char('/') + mRuqolaServerConfig->jitsiMeetPrefix() + hash; const QUrl clickedUrl = QUrl::fromUserInput(url); QDesktopServices::openUrl(clickedUrl); } void RocketChatAccount::eraseRoom(const QString &roomId) { ddp()->eraseRoom(roomId); } void RocketChatAccount::openDirectChannel(const QString &username) { ddp()->openDirectChannel(username); } void RocketChatAccount::createNewChannel(const QString &name, bool readOnly, bool privateRoom, const QString &userNames) { const QStringList lstUsers = userNames.split(QLatin1Char(','), QString::SkipEmptyParts); if (privateRoom) { ddp()->createPrivateGroup(name, lstUsers); } else { ddp()->createChannel(name, lstUsers, readOnly); } } void RocketChatAccount::joinRoom(const QString &roomId, const QString &joinCode) { ddp()->joinRoom(roomId, joinCode); ddp()->subscribeRoomMessage(roomId); } void RocketChatAccount::listEmojiCustom() { ddp()->listEmojiCustom(); } void RocketChatAccount::setDefaultStatus(User::PresenceStatus status) { ddp()->setDefaultStatus(status); } void RocketChatAccount::changeDefaultStatus(int index) { setDefaultStatus(mStatusModel->status(index)); } void RocketChatAccount::loadEmoji() { mEmojiList.clear(); //TODO } void RocketChatAccount::deleteMessage(const QString &messageId) { ddp()->deleteMessage(messageId); } void RocketChatAccount::userAutocomplete(const QString &searchText, const QString &exception) { ddp()->userAutocomplete(searchText, exception); } void RocketChatAccount::createJitsiConfCall(const QString &roomId) { ddp()->createJitsiConfCall(roomId); joinJitsiConfCall(roomId); } void RocketChatAccount::starMessage(const QString &messageId, const QString &rid, bool starred) { ddp()->starMessage(messageId, rid, starred); } void RocketChatAccount::uploadFile(const QString &description, const QUrl &fileUrl) { qDebug() << " void RocketChatAccount::uploadFile(const QString &description, const QUrl &fileUrl)"<setRoomAnnouncement(roomId, newValue.toString()); break; case Description: ddp()->setRoomDescription(roomId, newValue.toString()); break; case Name: ddp()->setRoomName(roomId, newValue.toString()); break; case Topic: ddp()->setRoomTopic(roomId, newValue.toString()); break; case ReadOnly: ddp()->setRoomIsReadOnly(roomId, newValue.toBool()); break; case Archive: //No argument here. ddp()->archiveRoom(roomId); break; } } void RocketChatAccount::parsePublicSettings(const QJsonObject &obj) { QJsonArray configs = obj.value(QLatin1String("result")).toArray(); for (const QJsonValueRef ¤tConfig : configs) { QJsonObject currentConfObject = currentConfig.toObject(); const QString id = currentConfObject[QStringLiteral("_id")].toString(); const QVariant value = currentConfObject[QStringLiteral("value")].toVariant(); if (id == QLatin1String("uniqueID")) { mRuqolaServerConfig->setUniqueId(value.toString()); } else if (id == QLatin1String("Jitsi_Domain")) { mRuqolaServerConfig->setJitsiMeetUrl(value.toString()); } else if (id == QLatin1String("Jitsi_URL_Room_Prefix")) { mRuqolaServerConfig->setJitsiMeetPrefix(value.toString()); } else if (id == QLatin1String("FileUpload_Storage_Type")) { mRuqolaServerConfig->setFileUploadStorageType(value.toString()); } else if (id == QLatin1String("Message_AllowEditing")) { mRuqolaServerConfig->setAllowMessageEditing(value.toBool()); } else if (id == QLatin1String("Message_AllowEditing_BlockEditInMinutes")) { mRuqolaServerConfig->setBlockEditingMessageInMinutes(value.toInt()); } else { qCDebug(RUQOLA_LOG) << "Other public settings id " << id << value; } } } QString RocketChatAccount::authToken() const { return settings()->authToken(); } QString RocketChatAccount::userName() const { return settings()->userName(); } void RocketChatAccount::setAccountName(const QString &servername) { settings()->setAccountName(servername); } QString RocketChatAccount::accountName() const { return settings()->accountName(); } QString RocketChatAccount::userID() const { return settings()->userId(); } QString RocketChatAccount::password() const { return settings()->password(); } void RocketChatAccount::setAuthToken(const QString &token) { settings()->setAuthToken(token); } void RocketChatAccount::setPassword(const QString &password) { settings()->setPassword(password); } void RocketChatAccount::setUserName(const QString &username) { settings()->setUserName(username); } void RocketChatAccount::setUserID(const QString &userID) { settings()->setUserId(userID); } QString RocketChatAccount::serverUrl() const { return settings()->serverUrl(); } void RocketChatAccount::setServerUrl(const QString &serverURL) { settings()->setServerUrl(serverURL); restApi()->setServerUrl(serverURL); } QString RocketChatAccount::recordingVideoPath() const { return mCache->recordingVideoPath(accountName()); } QString RocketChatAccount::recordingImagePath() const { return mCache->recordingImagePath(accountName()); } void RocketChatAccount::downloadFile(const QString &downloadFileUrl, const QUrl &localFile) { mCache->downloadFile(downloadFileUrl, localFile); } QUrl RocketChatAccount::attachmentUrl(const QString &url) { return mCache->attachmentUrl(url); } void RocketChatAccount::loadHistory(const QString &roomID, bool initial) { MessageModel *roomModel = getMessageModelForRoom(roomID); if (roomModel) { QJsonArray params; params.append(QJsonValue(roomID)); // Load history const qint64 endDateTime = roomModel->lastTimestamp(); if (initial) { params.append(QJsonValue(QJsonValue::Null)); } else { const qint64 startDateTime = roomModel->generateNewStartTimeStamp(endDateTime); QJsonObject dateObject; dateObject[QStringLiteral("$date")] = QJsonValue(startDateTime); //qDebug() << " QDATE TIME END" << QDateTime::fromMSecsSinceEpoch(endDateTime) << " START " << QDateTime::fromMSecsSinceEpoch(startDateTime); params.append(dateObject); } params.append(QJsonValue(50)); // Max number of messages to load; QJsonObject dateObject; //qDebug() << "roomModel->lastTimestamp()" << roomModel->lastTimestamp() << " ROOMID " << roomID; dateObject[QStringLiteral("$date")] = QJsonValue(endDateTime); params.append(dateObject); ddp()->loadHistory(params); } else { qCWarning(RUQOLA_LOG) << "Room is not found " << roomID; } } bool RocketChatAccount::allowEditingMessages() const { return mRuqolaServerConfig->allowMessageEditing(); } void RocketChatAccount::sendNotification(const QJsonArray &contents) { qDebug() << "void RocketChatAccount::sendNotification(const QJsonArray &contents) " << contents; - const QJsonObject obj = contents.at(0).toObject(); - const QString message = obj[QStringLiteral("text")].toString(); - const QString title = obj[QStringLiteral("title")].toString(); + QString message; + QString title; + Utils::parseNotification(contents, message, title); Q_EMIT notification(title, message); } diff --git a/src/utils.cpp b/src/utils.cpp index 301740b6..3022a80e 100644 --- a/src/utils.cpp +++ b/src/utils.cpp @@ -1,114 +1,124 @@ /* Copyright (c) 2017-2018 Montel Laurent This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License or ( at your option ) version 3 or, at the discretion of KDE e.V. ( which shall act as a proxy as in section 14 of the GPLv3 ), any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "utils.h" #include "ruqola_debug.h" +#include +#include #include #include QUrl Utils::generateServerUrl(const QString &url) { if (url.isEmpty()) { return {}; } QString serverUrl = url; if (serverUrl.startsWith(QLatin1String("https://"))) { serverUrl.replace(QStringLiteral("https://"), QStringLiteral("wss://")); } else if (serverUrl.startsWith(QLatin1String("http://"))) { serverUrl.replace(QStringLiteral("http://"), QStringLiteral("ws://")); } else { serverUrl = QStringLiteral("wss://") + serverUrl; } return QUrl(serverUrl + QStringLiteral("/websocket")); } QString Utils::extractRoomUserFromUrl(QString url) { url.remove(QStringLiteral("ruqola:/user/")); url.remove(QStringLiteral("ruqola:/room/")); return url; } QString Utils::markdownToRichText(const QString &markDown) { //qCDebug(RUQOLA_LOG) << "BEFORE markdownToRichText "<")); //qCDebug(RUQOLA_LOG) << "markdownToRichText "< &mentions) { QString newStr = Utils::markdownToRichText(str); static const QRegularExpression regularExpressionUser(QStringLiteral("@([\\w.]+)")); QRegularExpressionMatchIterator userIterator = regularExpressionUser.globalMatch(newStr); while (userIterator.hasNext()) { const QRegularExpressionMatch match = userIterator.next(); const QString word = match.captured(1); newStr.replace(QLatin1Char('@') + word, QStringLiteral("@%1").arg(word)); } static const QRegularExpression regularExpressionRoom(QStringLiteral("#([\\w.]+)")); QRegularExpressionMatchIterator roomIterator = regularExpressionRoom.globalMatch(newStr); while (roomIterator.hasNext()) { const QRegularExpressionMatch match = roomIterator.next(); const QString word = match.captured(1); newStr.replace(QLatin1Char('#') + word, QStringLiteral("#%1").arg(word)); } return newStr; } QString Utils::presenceStatusToString(User::PresenceStatus status) { switch (status) { case User::PresenceStatus::PresenceOnline: return QStringLiteral("online"); case User::PresenceStatus::PresenceBusy: return QStringLiteral("busy"); case User::PresenceStatus::PresenceAway: return QStringLiteral("away"); case User::PresenceStatus::PresenceOffline: return QStringLiteral("offline"); case User::PresenceStatus::Unknown: return {}; } Q_UNREACHABLE(); return {}; } User::PresenceStatus Utils::presenceStatusFromString(const QString &status) { if (status == QStringLiteral("online")) { return User::PresenceStatus::PresenceOnline; } else if (status == QStringLiteral("busy")) { return User::PresenceStatus::PresenceBusy; } else if (status == QStringLiteral("away")) { return User::PresenceStatus::PresenceAway; } else if (status == QStringLiteral("offline")) { return User::PresenceStatus::PresenceOffline; } else { qCDebug(RUQOLA_LOG) << "Problem with status " << status; return {}; } } + + +void Utils::parseNotification(const QJsonArray &contents, QString &message, QString &title) +{ + const QJsonObject obj = contents.at(0).toObject(); + message = obj[QStringLiteral("text")].toString(); + title = obj[QStringLiteral("title")].toString(); +} diff --git a/src/utils.h b/src/utils.h index 4a88a2f5..441f359f 100644 --- a/src/utils.h +++ b/src/utils.h @@ -1,36 +1,37 @@ /* Copyright (c) 2017-2018 Montel Laurent This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License or ( at your option ) version 3 or, at the discretion of KDE e.V. ( which shall act as a proxy as in section 14 of the GPLv3 ), any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef UTILS_H #define UTILS_H #include "libruqola_private_export.h" #include "user.h" #include namespace Utils { LIBRUQOLACORE_TESTS_EXPORT QUrl generateServerUrl(const QString &url); LIBRUQOLACORE_TESTS_EXPORT QString markdownToRichText(const QString &markDown); LIBRUQOLACORE_TESTS_EXPORT QString presenceStatusToString(User::PresenceStatus status); LIBRUQOLACORE_TESTS_EXPORT User::PresenceStatus presenceStatusFromString(const QString &status); LIBRUQOLACORE_TESTS_EXPORT QString generateRichText(const QString &markDown, const QMap &mentions); LIBRUQOLACORE_TESTS_EXPORT QString extractRoomUserFromUrl(QString url); +LIBRUQOLACORE_TESTS_EXPORT void parseNotification(const QJsonArray &contents, QString &message, QString &title); } #endif // UTILS_H