Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/nextcloud/desktop.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFelix Weilbach <felix.weilbach@nextcloud.com>2021-09-09 12:18:22 +0300
committerFelix Weilbach <felix.weilbach@nextcloud.com>2021-09-09 12:18:22 +0300
commit8a8d488454405356b5d11f63bebff2d69be43b02 (patch)
treeafa6c5a496561e6778227ece22a6c871622af926 /src/libsync
parentf34d66302942ede371bf7bc5d5e213b6c41ea5d8 (diff)
Add dialog to set user status
Signed-off-by: Felix Weilbach <felix.weilbach@nextcloud.com>
Diffstat (limited to 'src/libsync')
-rw-r--r--src/libsync/CMakeLists.txt3
-rw-r--r--src/libsync/abstractnetworkjob.cpp9
-rw-r--r--src/libsync/abstractnetworkjob.h3
-rw-r--r--src/libsync/account.cpp37
-rw-r--r--src/libsync/account.h11
-rw-r--r--src/libsync/capabilities.cpp20
-rw-r--r--src/libsync/capabilities.h2
-rw-r--r--src/libsync/clientsideencryption.cpp13
-rw-r--r--src/libsync/clientsideencryption.h2
-rw-r--r--src/libsync/clientsideencryptionjobs.cpp17
-rw-r--r--src/libsync/datetimeprovider.cpp17
-rw-r--r--src/libsync/datetimeprovider.h18
-rw-r--r--src/libsync/networkjobs.cpp38
-rw-r--r--src/libsync/networkjobs.h25
-rw-r--r--src/libsync/ocsuserstatusconnector.cpp455
-rw-r--r--src/libsync/ocsuserstatusconnector.h70
-rw-r--r--src/libsync/userstatusconnector.cpp121
-rw-r--r--src/libsync/userstatusconnector.h138
18 files changed, 971 insertions, 28 deletions
diff --git a/src/libsync/CMakeLists.txt b/src/libsync/CMakeLists.txt
index 3c38c6374..750d45ec8 100644
--- a/src/libsync/CMakeLists.txt
+++ b/src/libsync/CMakeLists.txt
@@ -55,6 +55,9 @@ set(libsync_SRCS
theme.cpp
clientsideencryption.cpp
clientsideencryptionjobs.cpp
+ datetimeprovider.cpp
+ ocsuserstatusconnector.cpp
+ userstatusconnector.cpp
creds/dummycredentials.cpp
creds/abstractcredentials.cpp
creds/credentialscommon.cpp
diff --git a/src/libsync/abstractnetworkjob.cpp b/src/libsync/abstractnetworkjob.cpp
index be9c01c93..6bb00a6b2 100644
--- a/src/libsync/abstractnetworkjob.cpp
+++ b/src/libsync/abstractnetworkjob.cpp
@@ -139,6 +139,15 @@ QNetworkReply *AbstractNetworkJob::sendRequest(const QByteArray &verb, const QUr
return reply;
}
+QNetworkReply *AbstractNetworkJob::sendRequest(const QByteArray &verb, const QUrl &url,
+ QNetworkRequest req, const QByteArray &requestBody)
+{
+ auto reply = _account->sendRawRequest(verb, url, req, requestBody);
+ _requestBody = nullptr;
+ adoptRequest(reply);
+ return reply;
+}
+
void AbstractNetworkJob::adoptRequest(QNetworkReply *reply)
{
addTimer(reply);
diff --git a/src/libsync/abstractnetworkjob.h b/src/libsync/abstractnetworkjob.h
index d1f3f6bc5..63391e672 100644
--- a/src/libsync/abstractnetworkjob.h
+++ b/src/libsync/abstractnetworkjob.h
@@ -128,6 +128,9 @@ protected:
QNetworkRequest req = QNetworkRequest(),
QIODevice *requestBody = nullptr);
+ QNetworkReply *sendRequest(const QByteArray &verb, const QUrl &url,
+ QNetworkRequest req, const QByteArray &requestBody);
+
// sendRequest does not take a relative path instead of an url,
// but the old API allowed that. We have this undefined overload
// to help catch usage errors
diff --git a/src/libsync/account.cpp b/src/libsync/account.cpp
index 8beb34412..d0cfe36e4 100644
--- a/src/libsync/account.cpp
+++ b/src/libsync/account.cpp
@@ -13,6 +13,8 @@
*/
#include "account.h"
+#include "accountfwd.h"
+#include "clientsideencryptionjobs.h"
#include "cookiejar.h"
#include "networkjobs.h"
#include "configfile.h"
@@ -27,6 +29,7 @@
#include "common/asserts.h"
#include "clientsideencryption.h"
+#include "ocsuserstatusconnector.h"
#include <QLoggingCategory>
#include <QNetworkReply>
@@ -43,6 +46,7 @@
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonArray>
+#include <QLoggingCategory>
#include <qsslconfiguration.h>
#include <qt5keychain/keychain.h>
@@ -93,6 +97,7 @@ QString Account::davPath() const
void Account::setSharedThis(AccountPtr sharedThis)
{
_sharedThis = sharedThis.toWeakRef();
+ setupUserStatusConnector();
}
QString Account::davPathBase()
@@ -337,6 +342,24 @@ QNetworkReply *Account::sendRawRequest(const QByteArray &verb, const QUrl &url,
return _am->sendCustomRequest(req, verb, data);
}
+QNetworkReply *Account::sendRawRequest(const QByteArray &verb, const QUrl &url, QNetworkRequest req, const QByteArray &data)
+{
+ req.setUrl(url);
+ req.setSslConfiguration(this->getOrCreateSslConfig());
+ if (verb == "HEAD" && data.isEmpty()) {
+ return _am->head(req);
+ } else if (verb == "GET" && data.isEmpty()) {
+ return _am->get(req);
+ } else if (verb == "POST") {
+ return _am->post(req, data);
+ } else if (verb == "PUT") {
+ return _am->put(req, data);
+ } else if (verb == "DELETE" && data.isEmpty()) {
+ return _am->deleteResource(req);
+ }
+ return _am->sendCustomRequest(req, verb, data);
+}
+
SimpleNetworkJob *Account::sendRequest(const QByteArray &verb, const QUrl &url, QNetworkRequest req, QIODevice *data)
{
auto job = new SimpleNetworkJob(sharedFromThis());
@@ -544,9 +567,18 @@ void Account::setCapabilities(const QVariantMap &caps)
{
_capabilities = Capabilities(caps);
+ setupUserStatusConnector();
trySetupPushNotifications();
}
+void Account::setupUserStatusConnector()
+{
+ _userStatusConnector = std::make_shared<OcsUserStatusConnector>(sharedFromThis());
+ connect(_userStatusConnector.get(), &UserStatusConnector::userStatusFetched, this, [this](const UserStatus &) {
+ emit userStatusChanged();
+ });
+}
+
QString Account::serverVersion() const
{
return _serverVersion;
@@ -744,4 +776,9 @@ PushNotifications *Account::pushNotifications() const
return _pushNotifications;
}
+std::shared_ptr<UserStatusConnector> Account::userStatusConnector() const
+{
+ return _userStatusConnector;
+}
+
} // namespace OCC
diff --git a/src/libsync/account.h b/src/libsync/account.h
index 00930b2b3..334425853 100644
--- a/src/libsync/account.h
+++ b/src/libsync/account.h
@@ -55,6 +55,7 @@ using AccountPtr = QSharedPointer<Account>;
class AccessManager;
class SimpleNetworkJob;
class PushNotifications;
+class UserStatusConnector;
/**
* @brief Reimplement this to handle SSL errors from libsync
@@ -150,6 +151,9 @@ public:
QNetworkRequest req = QNetworkRequest(),
QIODevice *data = nullptr);
+ QNetworkReply *sendRawRequest(const QByteArray &verb,
+ const QUrl &url, QNetworkRequest req, const QByteArray &data);
+
/** Create and start network job for a simple one-off request.
*
* More complicated requests typically create their own job types.
@@ -251,10 +255,13 @@ public:
// Check for the directEditing capability
void fetchDirectEditors(const QUrl &directEditingURL, const QString &directEditingETag);
+ void setupUserStatusConnector();
void trySetupPushNotifications();
PushNotifications *pushNotifications() const;
void setPushNotificationsReconnectInterval(int interval);
+ std::shared_ptr<UserStatusConnector> userStatusConnector() const;
+
public slots:
/// Used when forgetting credentials
void clearQNAMCache();
@@ -287,6 +294,8 @@ signals:
void pushNotificationsReady(Account *account);
void pushNotificationsDisabled(Account *account);
+ void userStatusChanged();
+
protected Q_SLOTS:
void slotCredentialsFetched();
void slotCredentialsAsked();
@@ -343,6 +352,8 @@ private:
PushNotifications *_pushNotifications = nullptr;
+ std::shared_ptr<UserStatusConnector> _userStatusConnector;
+
/* IMPORTANT - remove later - FIXME MS@2019-12-07 -->
* TODO: For "Log out" & "Remove account": Remove client CA certs and KEY!
*
diff --git a/src/libsync/capabilities.cpp b/src/libsync/capabilities.cpp
index 04db298b1..9983821ae 100644
--- a/src/libsync/capabilities.cpp
+++ b/src/libsync/capabilities.cpp
@@ -187,13 +187,31 @@ bool Capabilities::chunkingNg() const
return _capabilities["dav"].toMap()["chunking"].toByteArray() >= "1.0";
}
-bool Capabilities::userStatus() const
+bool Capabilities::userStatusNotification() const
{
return _capabilities.contains("notifications") &&
_capabilities["notifications"].toMap().contains("ocs-endpoints") &&
_capabilities["notifications"].toMap()["ocs-endpoints"].toStringList().contains("user-status");
}
+bool Capabilities::userStatus() const
+{
+ if (!_capabilities.contains("user_status")) {
+ return false;
+ }
+ const auto userStatusMap = _capabilities["user_status"].toMap();
+ return userStatusMap.value("enabled", false).toBool();
+}
+
+bool Capabilities::userStatusSupportsEmoji() const
+{
+ if (!userStatus()) {
+ return false;
+ }
+ const auto userStatusMap = _capabilities["user_status"].toMap();
+ return userStatusMap.value("supports_emoji", false).toBool();
+}
+
PushNotificationTypes Capabilities::availablePushNotifications() const
{
if (!_capabilities.contains("notify_push")) {
diff --git a/src/libsync/capabilities.h b/src/libsync/capabilities.h
index 078a0cd35..3040db890 100644
--- a/src/libsync/capabilities.h
+++ b/src/libsync/capabilities.h
@@ -58,7 +58,9 @@ public:
bool sharePublicLinkMultiple() const;
bool shareResharing() const;
bool chunkingNg() const;
+ bool userStatusNotification() const;
bool userStatus() const;
+ bool userStatusSupportsEmoji() const;
/// Returns which kind of push notfications are available
PushNotificationTypes availablePushNotifications() const;
diff --git a/src/libsync/clientsideencryption.cpp b/src/libsync/clientsideencryption.cpp
index 4ea561da3..2a8b1a189 100644
--- a/src/libsync/clientsideencryption.cpp
+++ b/src/libsync/clientsideencryption.cpp
@@ -56,7 +56,8 @@ Q_LOGGING_CATEGORY(lcCse, "nextcloud.sync.clientsideencryption", QtInfoMsg)
Q_LOGGING_CATEGORY(lcCseDecryption, "nextcloud.e2e", QtInfoMsg)
Q_LOGGING_CATEGORY(lcCseMetadata, "nextcloud.metadata", QtInfoMsg)
-QString baseUrl(){
+QString e2eeBaseUrl()
+{
return QStringLiteral("ocs/v2.php/apps/end_to_end_encryption/api/v1/");
}
@@ -1180,7 +1181,7 @@ void ClientSideEncryption::generateCSR(const AccountPtr &account, EVP_PKEY *keyP
qCInfo(lcCse()) << "Returning the certificate";
qCInfo(lcCse()) << output;
- auto job = new SignPublicKeyApiJob(account, baseUrl() + "public-key", this);
+ auto job = new SignPublicKeyApiJob(account, e2eeBaseUrl() + "public-key", this);
job->setCsr(output);
connect(job, &SignPublicKeyApiJob::jsonReceived, [this, account](const QJsonDocument& json, int retCode) {
@@ -1212,7 +1213,7 @@ void ClientSideEncryption::encryptPrivateKey(const AccountPtr &account)
auto cryptedText = EncryptionHelper::encryptPrivateKey(secretKey, EncryptionHelper::privateKeyToPem(_privateKey), salt);
// Send private key to the server
- auto job = new StorePrivateKeyApiJob(account, baseUrl() + "private-key", this);
+ auto job = new StorePrivateKeyApiJob(account, e2eeBaseUrl() + "private-key", this);
job->setPrivateKey(cryptedText);
connect(job, &StorePrivateKeyApiJob::jsonReceived, [this, account](const QJsonDocument& doc, int retCode) {
Q_UNUSED(doc);
@@ -1296,7 +1297,7 @@ void ClientSideEncryption::decryptPrivateKey(const AccountPtr &account, const QB
void ClientSideEncryption::getPrivateKeyFromServer(const AccountPtr &account)
{
qCInfo(lcCse()) << "Retrieving private key from server";
- auto job = new JsonApiJob(account, baseUrl() + "private-key", this);
+ auto job = new JsonApiJob(account, e2eeBaseUrl() + "private-key", this);
connect(job, &JsonApiJob::jsonReceived, [this, account](const QJsonDocument& doc, int retCode) {
if (retCode == 200) {
QString key = doc.object()["ocs"].toObject()["data"].toObject()["private-key"].toString();
@@ -1315,7 +1316,7 @@ void ClientSideEncryption::getPrivateKeyFromServer(const AccountPtr &account)
void ClientSideEncryption::getPublicKeyFromServer(const AccountPtr &account)
{
qCInfo(lcCse()) << "Retrieving public key from server";
- auto job = new JsonApiJob(account, baseUrl() + "public-key", this);
+ auto job = new JsonApiJob(account, e2eeBaseUrl() + "public-key", this);
connect(job, &JsonApiJob::jsonReceived, [this, account](const QJsonDocument& doc, int retCode) {
if (retCode == 200) {
QString publicKey = doc.object()["ocs"].toObject()["data"].toObject()["public-keys"].toObject()[account->davUser()].toString();
@@ -1336,7 +1337,7 @@ void ClientSideEncryption::getPublicKeyFromServer(const AccountPtr &account)
void ClientSideEncryption::fetchAndValidatePublicKeyFromServer(const AccountPtr &account)
{
qCInfo(lcCse()) << "Retrieving public key from server";
- auto job = new JsonApiJob(account, baseUrl() + "server-key", this);
+ auto job = new JsonApiJob(account, e2eeBaseUrl() + "server-key", this);
connect(job, &JsonApiJob::jsonReceived, [this, account](const QJsonDocument& doc, int retCode) {
if (retCode == 200) {
const auto serverPublicKey = doc.object()["ocs"].toObject()["data"].toObject()["public-key"].toString().toLatin1();
diff --git a/src/libsync/clientsideencryption.h b/src/libsync/clientsideencryption.h
index e89586451..751c7d4c8 100644
--- a/src/libsync/clientsideencryption.h
+++ b/src/libsync/clientsideencryption.h
@@ -23,7 +23,7 @@ class ReadPasswordJob;
namespace OCC {
-QString baseUrl();
+QString e2eeBaseUrl();
namespace EncryptionHelper {
QByteArray generateRandomFilename();
diff --git a/src/libsync/clientsideencryptionjobs.cpp b/src/libsync/clientsideencryptionjobs.cpp
index ad8e30c66..71bb8a510 100644
--- a/src/libsync/clientsideencryptionjobs.cpp
+++ b/src/libsync/clientsideencryptionjobs.cpp
@@ -27,7 +27,7 @@ namespace OCC {
GetMetadataApiJob::GetMetadataApiJob(const AccountPtr& account,
const QByteArray& fileId,
QObject* parent)
-: AbstractNetworkJob(account, baseUrl() + QStringLiteral("meta-data/") + fileId, parent), _fileId(fileId)
+: AbstractNetworkJob(account, e2eeBaseUrl() + QStringLiteral("meta-data/") + fileId, parent), _fileId(fileId)
{
}
@@ -63,7 +63,7 @@ StoreMetaDataApiJob::StoreMetaDataApiJob(const AccountPtr& account,
const QByteArray& fileId,
const QByteArray& b64Metadata,
QObject* parent)
-: AbstractNetworkJob(account, baseUrl() + QStringLiteral("meta-data/") + fileId, parent), _fileId(fileId), _b64Metadata(b64Metadata)
+: AbstractNetworkJob(account, e2eeBaseUrl() + QStringLiteral("meta-data/") + fileId, parent), _fileId(fileId), _b64Metadata(b64Metadata)
{
}
@@ -104,8 +104,8 @@ UpdateMetadataApiJob::UpdateMetadataApiJob(const AccountPtr& account,
const QByteArray& b64Metadata,
const QByteArray& token,
QObject* parent)
-: AbstractNetworkJob(account, baseUrl() + QStringLiteral("meta-data/") + fileId, parent),
-_fileId(fileId),
+: AbstractNetworkJob(account, e2eeBaseUrl() + QStringLiteral("meta-data/") + fileId, parent)
+, _fileId(fileId),
_b64Metadata(b64Metadata),
_token(token)
{
@@ -154,7 +154,7 @@ UnlockEncryptFolderApiJob::UnlockEncryptFolderApiJob(const AccountPtr& account,
const QByteArray& fileId,
const QByteArray& token,
QObject* parent)
-: AbstractNetworkJob(account, baseUrl() + QStringLiteral("lock/") + fileId, parent), _fileId(fileId), _token(token)
+: AbstractNetworkJob(account, e2eeBaseUrl() + QStringLiteral("lock/") + fileId, parent), _fileId(fileId), _token(token)
{
}
@@ -185,11 +185,10 @@ bool UnlockEncryptFolderApiJob::finished()
}
-
DeleteMetadataApiJob::DeleteMetadataApiJob(const AccountPtr& account,
const QByteArray& fileId,
QObject* parent)
-: AbstractNetworkJob(account, baseUrl() + QStringLiteral("meta-data/") + fileId, parent), _fileId(fileId)
+: AbstractNetworkJob(account, e2eeBaseUrl() + QStringLiteral("meta-data/") + fileId, parent), _fileId(fileId)
{
}
@@ -219,7 +218,7 @@ bool DeleteMetadataApiJob::finished()
}
LockEncryptFolderApiJob::LockEncryptFolderApiJob(const AccountPtr& account, const QByteArray& fileId, QObject* parent)
-: AbstractNetworkJob(account, baseUrl() + QStringLiteral("lock/") + fileId, parent), _fileId(fileId)
+: AbstractNetworkJob(account, e2eeBaseUrl() + QStringLiteral("lock/") + fileId, parent), _fileId(fileId)
{
}
@@ -258,7 +257,7 @@ bool LockEncryptFolderApiJob::finished()
}
SetEncryptionFlagApiJob::SetEncryptionFlagApiJob(const AccountPtr& account, const QByteArray& fileId, FlagAction flagAction, QObject* parent)
-: AbstractNetworkJob(account, baseUrl() + QStringLiteral("encrypted/") + fileId, parent), _fileId(fileId), _flagAction(flagAction)
+: AbstractNetworkJob(account, e2eeBaseUrl() + QStringLiteral("encrypted/") + fileId, parent), _fileId(fileId), _flagAction(flagAction)
{
}
diff --git a/src/libsync/datetimeprovider.cpp b/src/libsync/datetimeprovider.cpp
new file mode 100644
index 000000000..084072312
--- /dev/null
+++ b/src/libsync/datetimeprovider.cpp
@@ -0,0 +1,17 @@
+#include "datetimeprovider.h"
+
+namespace OCC {
+
+DateTimeProvider::~DateTimeProvider() = default;
+
+QDateTime DateTimeProvider::currentDateTime() const
+{
+ return QDateTime::currentDateTime();
+}
+
+QDate DateTimeProvider::currentDate() const
+{
+ return QDate::currentDate();
+}
+
+}
diff --git a/src/libsync/datetimeprovider.h b/src/libsync/datetimeprovider.h
new file mode 100644
index 000000000..1525a2e3d
--- /dev/null
+++ b/src/libsync/datetimeprovider.h
@@ -0,0 +1,18 @@
+#pragma once
+
+#include "owncloudlib.h"
+
+#include <QDateTime>
+
+namespace OCC {
+
+class OWNCLOUDSYNC_EXPORT DateTimeProvider
+{
+public:
+ virtual ~DateTimeProvider();
+
+ virtual QDateTime currentDateTime() const;
+
+ virtual QDate currentDate() const;
+};
+}
diff --git a/src/libsync/networkjobs.cpp b/src/libsync/networkjobs.cpp
index 9224afc76..2800af0d6 100644
--- a/src/libsync/networkjobs.cpp
+++ b/src/libsync/networkjobs.cpp
@@ -830,13 +830,49 @@ void JsonApiJob::addRawHeader(const QByteArray &headerName, const QByteArray &va
_request.setRawHeader(headerName, value);
}
+void JsonApiJob::setBody(const QJsonDocument &body)
+{
+ _body = body.toJson();
+ qCDebug(lcJsonApiJob) << "Set body for request:" << _body;
+ if (!_body.isEmpty()) {
+ _request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
+ }
+}
+
+
+void JsonApiJob::setVerb(Verb value)
+{
+ _verb = value;
+}
+
+
+QByteArray JsonApiJob::verbToString() const
+{
+ switch (_verb) {
+ case Verb::Get:
+ return "GET";
+ case Verb::Post:
+ return "POST";
+ case Verb::Put:
+ return "PUT";
+ case Verb::Delete:
+ return "DELETE";
+ }
+ return "GET";
+}
+
void JsonApiJob::start()
{
addRawHeader("OCS-APIREQUEST", "true");
auto query = _additionalParams;
query.addQueryItem(QLatin1String("format"), QLatin1String("json"));
QUrl url = Utility::concatUrlPath(account()->url(), path(), query);
- sendRequest(_usePOST ? "POST" : "GET", url, _request);
+ const auto httpVerb = verbToString();
+ if (!_body.isEmpty()) {
+ sendRequest(httpVerb, url, _request, _body);
+ } else {
+ sendRequest(httpVerb, url, _request);
+ }
AbstractNetworkJob::start();
}
diff --git a/src/libsync/networkjobs.h b/src/libsync/networkjobs.h
index 45fb12a22..01cfcdedd 100644
--- a/src/libsync/networkjobs.h
+++ b/src/libsync/networkjobs.h
@@ -22,6 +22,7 @@
#include <QBuffer>
#include <QUrlQuery>
+#include <QJsonDocument>
#include <functional>
class QUrl;
@@ -375,6 +376,13 @@ class OWNCLOUDSYNC_EXPORT JsonApiJob : public AbstractNetworkJob
{
Q_OBJECT
public:
+ enum class Verb {
+ Get,
+ Post,
+ Put,
+ Delete,
+ };
+
explicit JsonApiJob(const AccountPtr &account, const QString &path, QObject *parent = nullptr);
/**
@@ -390,15 +398,9 @@ public:
void addQueryParams(const QUrlQuery &params);
void addRawHeader(const QByteArray &headerName, const QByteArray &value);
- /**
- * @brief usePOST - allow job to do an anonymous POST request instead of GET
- * @param params: (optional) true for POST, false for GET (default).
- *
- * This function needs to be called before start() obviously.
- */
- void usePOST(bool usePOST = true) {
- _usePOST = usePOST;
- }
+ void setBody(const QJsonDocument &body);
+
+ void setVerb(Verb value);
public slots:
void start() override;
@@ -429,10 +431,13 @@ signals:
void allowDesktopNotificationsChanged(bool isAllowed);
private:
+ QByteArray _body;
QUrlQuery _additionalParams;
QNetworkRequest _request;
- bool _usePOST = false;
+ Verb _verb = Verb::Get;
+
+ QByteArray verbToString() const;
};
/**
diff --git a/src/libsync/ocsuserstatusconnector.cpp b/src/libsync/ocsuserstatusconnector.cpp
new file mode 100644
index 000000000..95f3810e2
--- /dev/null
+++ b/src/libsync/ocsuserstatusconnector.cpp
@@ -0,0 +1,455 @@
+/*
+ * Copyright (C) by Felix Weilbach <felix.weilbach@nextcloud.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License
+ * for more details.
+ */
+
+#include "ocsuserstatusconnector.h"
+#include "account.h"
+#include "userstatusconnector.h"
+
+#include <networkjobs.h>
+
+#include <QDateTime>
+#include <QtGlobal>
+#include <QJsonDocument>
+#include <QJsonValue>
+#include <QLoggingCategory>
+#include <QString>
+#include <QJsonObject>
+#include <QJsonArray>
+#include <qdatetime.h>
+#include <qjsonarray.h>
+#include <qjsonobject.h>
+#include <qloggingcategory.h>
+
+namespace {
+
+Q_LOGGING_CATEGORY(lcOcsUserStatusConnector, "nextcloud.gui.ocsuserstatusconnector", QtInfoMsg)
+
+OCC::UserStatus::OnlineStatus stringToUserOnlineStatus(const QString &status)
+{
+ // it needs to match the Status enum
+ const QHash<QString, OCC::UserStatus::OnlineStatus> preDefinedStatus {
+ { "online", OCC::UserStatus::OnlineStatus::Online },
+ { "dnd", OCC::UserStatus::OnlineStatus::DoNotDisturb },
+ { "away", OCC::UserStatus::OnlineStatus::Away },
+ { "offline", OCC::UserStatus::OnlineStatus::Offline },
+ { "invisible", OCC::UserStatus::OnlineStatus::Invisible }
+ };
+
+ // api should return invisible, dnd,... toLower() it is to make sure
+ // it matches _preDefinedStatus, otherwise the default is online (0)
+ return preDefinedStatus.value(status.toLower(), OCC::UserStatus::OnlineStatus::Online);
+}
+
+QString onlineStatusToString(OCC::UserStatus::OnlineStatus status)
+{
+ switch (status) {
+ case OCC::UserStatus::OnlineStatus::Online:
+ return QStringLiteral("online");
+ case OCC::UserStatus::OnlineStatus::DoNotDisturb:
+ return QStringLiteral("dnd");
+ case OCC::UserStatus::OnlineStatus::Away:
+ return QStringLiteral("offline");
+ case OCC::UserStatus::OnlineStatus::Offline:
+ return QStringLiteral("offline");
+ case OCC::UserStatus::OnlineStatus::Invisible:
+ return QStringLiteral("invisible");
+ }
+ return QStringLiteral("online");
+}
+
+OCC::Optional<OCC::ClearAt> jsonExtractClearAt(QJsonObject jsonObject)
+{
+ OCC::Optional<OCC::ClearAt> clearAt {};
+ if (jsonObject.contains("clearAt") && !jsonObject.value("clearAt").isNull()) {
+ OCC::ClearAt clearAtValue;
+ clearAtValue._type = OCC::ClearAtType::Timestamp;
+ clearAtValue._timestamp = jsonObject.value("clearAt").toInt();
+ clearAt = clearAtValue;
+ }
+ return clearAt;
+}
+
+OCC::UserStatus jsonExtractUserStatus(QJsonObject json)
+{
+ const auto clearAt = jsonExtractClearAt(json);
+
+ const OCC::UserStatus userStatus(json.value("messageId").toString(),
+ json.value("message").toString().trimmed(),
+ json.value("icon").toString().trimmed(), stringToUserOnlineStatus(json.value("status").toString()),
+ json.value("messageIsPredefined").toBool(false), clearAt);
+
+ return userStatus;
+}
+
+OCC::UserStatus jsonToUserStatus(const QJsonDocument &json)
+{
+ const QJsonObject defaultValues {
+ { "icon", "" },
+ { "message", "" },
+ { "status", "online" },
+ { "messageIsPredefined", "false" },
+ { "statusIsUserDefined", "false" }
+ };
+ const auto retrievedData = json.object().value("ocs").toObject().value("data").toObject(defaultValues);
+ return jsonExtractUserStatus(retrievedData);
+}
+
+quint64 clearAtEndOfToTimestamp(const OCC::ClearAt &clearAt)
+{
+ Q_ASSERT(clearAt._type == OCC::ClearAtType::EndOf);
+
+ if (clearAt._endof == "day") {
+ return QDate::currentDate().addDays(1).startOfDay().toTime_t();
+ } else if (clearAt._endof == "week") {
+ const auto days = Qt::Sunday - QDate::currentDate().dayOfWeek();
+ return QDate::currentDate().addDays(days + 1).startOfDay().toTime_t();
+ }
+ qCWarning(lcOcsUserStatusConnector) << "Can not handle clear at endof day type" << clearAt._endof;
+ return QDateTime::currentDateTime().toTime_t();
+}
+
+quint64 clearAtPeriodToTimestamp(const OCC::ClearAt &clearAt)
+{
+ return QDateTime::currentDateTime().addSecs(clearAt._period).toTime_t();
+}
+
+quint64 clearAtToTimestamp(const OCC::ClearAt &clearAt)
+{
+ switch (clearAt._type) {
+ case OCC::ClearAtType::Period: {
+ return clearAtPeriodToTimestamp(clearAt);
+ }
+
+ case OCC::ClearAtType::EndOf: {
+ return clearAtEndOfToTimestamp(clearAt);
+ }
+
+ case OCC::ClearAtType::Timestamp: {
+ return clearAt._timestamp;
+ }
+ }
+
+ return 0;
+}
+
+quint64 clearAtToTimestamp(const OCC::Optional<OCC::ClearAt> &clearAt)
+{
+ if (clearAt) {
+ return clearAtToTimestamp(*clearAt);
+ }
+ return 0;
+}
+
+OCC::Optional<OCC::ClearAt> jsonToClearAt(QJsonObject jsonObject)
+{
+ OCC::Optional<OCC::ClearAt> clearAt;
+
+ if (jsonObject.value("clearAt").isObject() && !jsonObject.value("clearAt").isNull()) {
+ OCC::ClearAt clearAtValue;
+ const auto clearAtObject = jsonObject.value("clearAt").toObject();
+ const auto typeValue = clearAtObject.value("type").toString("period");
+ if (typeValue == "period") {
+ const auto timeValue = clearAtObject.value("time").toInt(0);
+ clearAtValue._type = OCC::ClearAtType::Period;
+ clearAtValue._period = timeValue;
+ } else if (typeValue == "end-of") {
+ const auto timeValue = clearAtObject.value("time").toString("day");
+ clearAtValue._type = OCC::ClearAtType::EndOf;
+ clearAtValue._endof = timeValue;
+ } else {
+ qCWarning(lcOcsUserStatusConnector) << "Can not handle clear type value" << typeValue;
+ }
+ clearAt = clearAtValue;
+ }
+
+ return clearAt;
+}
+
+OCC::UserStatus jsonToUserStatus(QJsonObject jsonObject)
+{
+ const auto clearAt = jsonToClearAt(jsonObject);
+
+ OCC::UserStatus userStatus(
+ jsonObject.value("id").toString("no-id"),
+ jsonObject.value("message").toString("No message"),
+ jsonObject.value("icon").toString("no-icon"),
+ OCC::UserStatus::OnlineStatus::Online,
+ true,
+ clearAt);
+
+ return userStatus;
+}
+
+std::vector<OCC::UserStatus> jsonToPredefinedStatuses(QJsonArray jsonDataArray)
+{
+ std::vector<OCC::UserStatus> statuses;
+ for (const auto &jsonEntry : jsonDataArray) {
+ Q_ASSERT(jsonEntry.isObject());
+ if (!jsonEntry.isObject()) {
+ continue;
+ }
+ statuses.push_back(jsonToUserStatus(jsonEntry.toObject()));
+ }
+
+ return statuses;
+}
+
+
+const QString baseUrl("/ocs/v2.php/apps/user_status/api/v1");
+const QString userStatusBaseUrl = baseUrl + QStringLiteral("/user_status");
+}
+
+namespace OCC {
+
+OcsUserStatusConnector::OcsUserStatusConnector(AccountPtr account, QObject *parent)
+ : UserStatusConnector(parent)
+ , _account(account)
+{
+ Q_ASSERT(_account);
+ _userStatusSupported = _account->capabilities().userStatus();
+ _userStatusEmojisSupported = _account->capabilities().userStatusSupportsEmoji();
+}
+
+void OcsUserStatusConnector::fetchUserStatus()
+{
+ qCDebug(lcOcsUserStatusConnector) << "Try to fetch user status";
+
+ if (!_userStatusSupported) {
+ qCDebug(lcOcsUserStatusConnector) << "User status not supported";
+ emit error(Error::UserStatusNotSupported);
+ return;
+ }
+
+ startFetchUserStatusJob();
+}
+
+void OcsUserStatusConnector::startFetchUserStatusJob()
+{
+ if (_getUserStatusJob) {
+ qCDebug(lcOcsUserStatusConnector) << "Get user status job is already running.";
+ return;
+ }
+
+ _getUserStatusJob = new JsonApiJob(_account, userStatusBaseUrl, this);
+ connect(_getUserStatusJob, &JsonApiJob::jsonReceived, this, &OcsUserStatusConnector::onUserStatusFetched);
+ _getUserStatusJob->start();
+}
+
+void OcsUserStatusConnector::onUserStatusFetched(const QJsonDocument &json, int statusCode)
+{
+ logResponse("user status fetched", json, statusCode);
+
+ if (statusCode != 200) {
+ qCInfo(lcOcsUserStatusConnector) << "Slot fetch UserStatus finished with status code" << statusCode;
+ emit error(Error::CouldNotFetchUserStatus);
+ return;
+ }
+
+ _userStatus = jsonToUserStatus(json);
+ emit userStatusFetched(_userStatus);
+}
+
+void OcsUserStatusConnector::startFetchPredefinedStatuses()
+{
+ if (_getPredefinedStausesJob) {
+ qCDebug(lcOcsUserStatusConnector) << "Get predefined statuses job is already running";
+ return;
+ }
+
+ _getPredefinedStausesJob = new JsonApiJob(_account,
+ baseUrl + QStringLiteral("/predefined_statuses"), this);
+ connect(_getPredefinedStausesJob, &JsonApiJob::jsonReceived, this,
+ &OcsUserStatusConnector::onPredefinedStatusesFetched);
+ _getPredefinedStausesJob->start();
+}
+
+void OcsUserStatusConnector::fetchPredefinedStatuses()
+{
+ if (!_userStatusSupported) {
+ emit error(Error::UserStatusNotSupported);
+ return;
+ }
+ startFetchPredefinedStatuses();
+}
+
+void OcsUserStatusConnector::onPredefinedStatusesFetched(const QJsonDocument &json, int statusCode)
+{
+ logResponse("predefined statuses", json, statusCode);
+
+ if (statusCode != 200) {
+ qCInfo(lcOcsUserStatusConnector) << "Slot predefined user statuses finished with status code" << statusCode;
+ emit error(Error::CouldNotFetchPredefinedUserStatuses);
+ return;
+ }
+ const auto jsonData = json.object().value("ocs").toObject().value("data");
+ Q_ASSERT(jsonData.isArray());
+ if (!jsonData.isArray()) {
+ return;
+ }
+ const auto statuses = jsonToPredefinedStatuses(jsonData.toArray());
+ emit predefinedStatusesFetched(statuses);
+}
+
+void OcsUserStatusConnector::logResponse(const QString &message, const QJsonDocument &json, int statusCode)
+{
+ qCDebug(lcOcsUserStatusConnector) << "Response from:" << message << "Status:" << statusCode << "Json:" << json;
+}
+
+void OcsUserStatusConnector::setUserStatusOnlineStatus(UserStatus::OnlineStatus onlineStatus)
+{
+ _setOnlineStatusJob = new JsonApiJob(_account,
+ userStatusBaseUrl + QStringLiteral("/status"), this);
+ _setOnlineStatusJob->setVerb(JsonApiJob::Verb::Put);
+ // Set body
+ QJsonObject dataObject;
+ dataObject.insert("statusType", onlineStatusToString(onlineStatus));
+ QJsonDocument body;
+ body.setObject(dataObject);
+ _setOnlineStatusJob->setBody(body);
+ connect(_setOnlineStatusJob, &JsonApiJob::jsonReceived, this, &OcsUserStatusConnector::onUserStatusOnlineStatusSet);
+ _setOnlineStatusJob->start();
+}
+
+void OcsUserStatusConnector::setUserStatusMessagePredefined(const UserStatus &userStatus)
+{
+ Q_ASSERT(userStatus.messagePredefined());
+ if (!userStatus.messagePredefined()) {
+ return;
+ }
+
+ _setMessageJob = new JsonApiJob(_account, userStatusBaseUrl + QStringLiteral("/message/predefined"), this);
+ _setMessageJob->setVerb(JsonApiJob::Verb::Put);
+ // Set body
+ QJsonObject dataObject;
+ dataObject.insert("messageId", userStatus.id());
+ if (userStatus.clearAt()) {
+ dataObject.insert("clearAt", static_cast<int>(clearAtToTimestamp(userStatus.clearAt())));
+ } else {
+ dataObject.insert("clearAt", QJsonValue());
+ }
+ QJsonDocument body;
+ body.setObject(dataObject);
+ _setMessageJob->setBody(body);
+ connect(_setMessageJob, &JsonApiJob::jsonReceived, this, &OcsUserStatusConnector::onUserStatusMessageSet);
+ _setMessageJob->start();
+}
+
+void OcsUserStatusConnector::setUserStatusMessageCustom(const UserStatus &userStatus)
+{
+ Q_ASSERT(!userStatus.messagePredefined());
+ if (userStatus.messagePredefined()) {
+ return;
+ }
+
+ if (!_userStatusEmojisSupported) {
+ emit error(Error::EmojisNotSupported);
+ return;
+ }
+ _setMessageJob = new JsonApiJob(_account, userStatusBaseUrl + QStringLiteral("/message/custom"), this);
+ _setMessageJob->setVerb(JsonApiJob::Verb::Put);
+ // Set body
+ QJsonObject dataObject;
+ dataObject.insert("statusIcon", userStatus.icon());
+ dataObject.insert("message", userStatus.message());
+ const auto clearAt = userStatus.clearAt();
+ if (clearAt) {
+ dataObject.insert("clearAt", static_cast<int>(clearAtToTimestamp(*clearAt)));
+ } else {
+ dataObject.insert("clearAt", QJsonValue());
+ }
+ QJsonDocument body;
+ body.setObject(dataObject);
+ _setMessageJob->setBody(body);
+ connect(_setMessageJob, &JsonApiJob::jsonReceived, this, &OcsUserStatusConnector::onUserStatusMessageSet);
+ _setMessageJob->start();
+}
+
+void OcsUserStatusConnector::setUserStatusMessage(const UserStatus &userStatus)
+{
+ if (userStatus.messagePredefined()) {
+ setUserStatusMessagePredefined(userStatus);
+ return;
+ }
+ setUserStatusMessageCustom(userStatus);
+}
+
+void OcsUserStatusConnector::setUserStatus(const UserStatus &userStatus)
+{
+ if (!_userStatusSupported) {
+ emit error(Error::UserStatusNotSupported);
+ return;
+ }
+
+ if (_setOnlineStatusJob || _setMessageJob) {
+ qCDebug(lcOcsUserStatusConnector) << "Set online status job or set message job are already running.";
+ return;
+ }
+
+ setUserStatusOnlineStatus(userStatus.state());
+ setUserStatusMessage(userStatus);
+}
+
+void OcsUserStatusConnector::onUserStatusOnlineStatusSet(const QJsonDocument &json, int statusCode)
+{
+ logResponse("Online status set", json, statusCode);
+
+ if (statusCode != 200) {
+ emit error(Error::CouldNotSetUserStatus);
+ return;
+ }
+}
+
+void OcsUserStatusConnector::onUserStatusMessageSet(const QJsonDocument &json, int statusCode)
+{
+ logResponse("Message set", json, statusCode);
+
+ if (statusCode != 200) {
+ emit error(Error::CouldNotSetUserStatus);
+ return;
+ }
+
+ // We fetch the user status again because json does not contain
+ // the new message when user status was set from a predefined
+ // message
+ fetchUserStatus();
+
+ emit userStatusSet();
+}
+
+void OcsUserStatusConnector::clearMessage()
+{
+ _clearMessageJob = new JsonApiJob(_account, userStatusBaseUrl + QStringLiteral("/message"));
+ _clearMessageJob->setVerb(JsonApiJob::Verb::Delete);
+ connect(_clearMessageJob, &JsonApiJob::jsonReceived, this, &OcsUserStatusConnector::onMessageCleared);
+ _clearMessageJob->start();
+}
+
+UserStatus OcsUserStatusConnector::userStatus() const
+{
+ return _userStatus;
+}
+
+void OcsUserStatusConnector::onMessageCleared(const QJsonDocument &json, int statusCode)
+{
+ logResponse("Message cleared", json, statusCode);
+
+ if (statusCode != 200) {
+ emit error(Error::CouldNotClearMessage);
+ return;
+ }
+
+ _userStatus = {};
+ emit messageCleared();
+}
+}
diff --git a/src/libsync/ocsuserstatusconnector.h b/src/libsync/ocsuserstatusconnector.h
new file mode 100644
index 000000000..0d366419f
--- /dev/null
+++ b/src/libsync/ocsuserstatusconnector.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) by Felix Weilbach <felix.weilbach@nextcloud.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License
+ * for more details.
+ */
+
+#pragma once
+
+#include "accountfwd.h"
+#include "userstatusconnector.h"
+
+#include <QPointer>
+
+namespace OCC {
+
+class JsonApiJob;
+class SimpleNetworkJob;
+
+class OWNCLOUDSYNC_EXPORT OcsUserStatusConnector : public UserStatusConnector
+{
+public:
+ explicit OcsUserStatusConnector(AccountPtr account, QObject *parent = nullptr);
+
+ void fetchUserStatus() override;
+
+ void fetchPredefinedStatuses() override;
+
+ void setUserStatus(const UserStatus &userStatus) override;
+
+ void clearMessage() override;
+
+ UserStatus userStatus() const override;
+
+private:
+ void onUserStatusFetched(const QJsonDocument &json, int statusCode);
+ void onPredefinedStatusesFetched(const QJsonDocument &json, int statusCode);
+ void onUserStatusOnlineStatusSet(const QJsonDocument &json, int statusCode);
+ void onUserStatusMessageSet(const QJsonDocument &json, int statusCode);
+ void onMessageCleared(const QJsonDocument &json, int statusCode);
+
+ void logResponse(const QString &message, const QJsonDocument &json, int statusCode);
+ void startFetchUserStatusJob();
+ void startFetchPredefinedStatuses();
+ void setUserStatusOnlineStatus(UserStatus::OnlineStatus onlineStatus);
+ void setUserStatusMessage(const UserStatus &userStatus);
+ void setUserStatusMessagePredefined(const UserStatus &userStatus);
+ void setUserStatusMessageCustom(const UserStatus &userStatus);
+
+ AccountPtr _account;
+
+ bool _userStatusSupported = false;
+ bool _userStatusEmojisSupported = false;
+
+ QPointer<JsonApiJob> _clearMessageJob {};
+ QPointer<JsonApiJob> _setMessageJob {};
+ QPointer<JsonApiJob> _setOnlineStatusJob {};
+ QPointer<JsonApiJob> _getPredefinedStausesJob {};
+ QPointer<JsonApiJob> _getUserStatusJob {};
+
+ UserStatus _userStatus;
+};
+}
diff --git a/src/libsync/userstatusconnector.cpp b/src/libsync/userstatusconnector.cpp
new file mode 100644
index 000000000..178e8d794
--- /dev/null
+++ b/src/libsync/userstatusconnector.cpp
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) by Felix Weilbach <felix.weilbach@nextcloud.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License
+ * for more details.
+ */
+
+#include "userstatusconnector.h"
+#include "theme.h"
+
+namespace OCC {
+
+UserStatus::UserStatus() = default;
+
+UserStatus::UserStatus(
+ const QString &id, const QString &message, const QString &icon,
+ OnlineStatus state, bool messagePredefined, const Optional<ClearAt> &clearAt)
+ : _id(id)
+ , _message(message)
+ , _icon(icon)
+ , _state(state)
+ , _messagePredefined(messagePredefined)
+ , _clearAt(clearAt)
+{
+}
+
+QString UserStatus::id() const
+{
+ return _id;
+}
+
+QString UserStatus::message() const
+{
+ return _message;
+}
+
+QString UserStatus::icon() const
+{
+ return _icon;
+}
+
+auto UserStatus::state() const -> OnlineStatus
+{
+ return _state;
+}
+
+bool UserStatus::messagePredefined() const
+{
+ return _messagePredefined;
+}
+
+QUrl UserStatus::stateIcon() const
+{
+ switch (_state) {
+ case UserStatus::OnlineStatus::Away:
+ return Theme::instance()->statusAwayImageSource();
+
+ case UserStatus::OnlineStatus::DoNotDisturb:
+ return Theme::instance()->statusDoNotDisturbImageSource();
+
+ case UserStatus::OnlineStatus::Invisible:
+ case UserStatus::OnlineStatus::Offline:
+ return Theme::instance()->statusInvisibleImageSource();
+
+ case UserStatus::OnlineStatus::Online:
+ return Theme::instance()->statusOnlineImageSource();
+ }
+
+ Q_UNREACHABLE();
+}
+
+Optional<ClearAt> UserStatus::clearAt() const
+{
+ return _clearAt;
+}
+
+void UserStatus::setId(const QString &id)
+{
+ _id = id;
+}
+
+void UserStatus::setMessage(const QString &message)
+{
+ _message = message;
+}
+
+void UserStatus::setState(OnlineStatus state)
+{
+ _state = state;
+}
+
+void UserStatus::setIcon(const QString &icon)
+{
+ _icon = icon;
+}
+
+void UserStatus::setMessagePredefined(bool value)
+{
+ _messagePredefined = value;
+}
+
+void UserStatus::setClearAt(const Optional<ClearAt> &dateTime)
+{
+ _clearAt = dateTime;
+}
+
+
+UserStatusConnector::UserStatusConnector(QObject *parent)
+ : QObject(parent)
+{
+}
+
+UserStatusConnector::~UserStatusConnector() = default;
+}
diff --git a/src/libsync/userstatusconnector.h b/src/libsync/userstatusconnector.h
new file mode 100644
index 000000000..d5593fe9e
--- /dev/null
+++ b/src/libsync/userstatusconnector.h
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) by Felix Weilbach <felix.weilbach@nextcloud.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License
+ * for more details.
+ */
+
+#pragma once
+
+#include "common/result.h"
+#include "owncloudlib.h"
+
+#include <QObject>
+#include <QString>
+#include <QMetaType>
+#include <QUrl>
+#include <QDateTime>
+#include <QtGlobal>
+#include <QVariant>
+
+#include <vector>
+
+
+namespace OCC {
+
+enum class OWNCLOUDSYNC_EXPORT ClearAtType {
+ Period,
+ EndOf,
+ Timestamp
+};
+
+// TODO: If we can use C++17 make it a std::variant
+struct OWNCLOUDSYNC_EXPORT ClearAt
+{
+ ClearAtType _type = ClearAtType::Period;
+
+ quint64 _timestamp;
+ int _period;
+ QString _endof;
+};
+
+class OWNCLOUDSYNC_EXPORT UserStatus
+{
+ Q_GADGET
+
+ Q_PROPERTY(QString id MEMBER _id)
+ Q_PROPERTY(QString message MEMBER _message)
+ Q_PROPERTY(QString icon MEMBER _icon)
+ Q_PROPERTY(OnlineStatus state MEMBER _state)
+
+public:
+ enum class OnlineStatus : quint8 {
+ Online,
+ DoNotDisturb,
+ Away,
+ Offline,
+ Invisible
+ };
+ Q_ENUM(OnlineStatus);
+
+ UserStatus();
+
+ UserStatus(const QString &id, const QString &message, const QString &icon,
+ OnlineStatus state, bool messagePredefined, const Optional<ClearAt> &clearAt = {});
+
+ Q_REQUIRED_RESULT QString id() const;
+ Q_REQUIRED_RESULT QString message() const;
+ Q_REQUIRED_RESULT QString icon() const;
+ Q_REQUIRED_RESULT OnlineStatus state() const;
+ Q_REQUIRED_RESULT Optional<ClearAt> clearAt() const;
+
+ void setId(const QString &id);
+ void setMessage(const QString &message);
+ void setState(OnlineStatus state);
+ void setIcon(const QString &icon);
+ void setMessagePredefined(bool value);
+ void setClearAt(const Optional<ClearAt> &dateTime);
+
+ Q_REQUIRED_RESULT bool messagePredefined() const;
+
+ Q_REQUIRED_RESULT QUrl stateIcon() const;
+
+private:
+ QString _id;
+ QString _message;
+ QString _icon;
+ OnlineStatus _state = OnlineStatus::Online;
+ bool _messagePredefined;
+ Optional<ClearAt> _clearAt;
+};
+
+class OWNCLOUDSYNC_EXPORT UserStatusConnector : public QObject
+{
+ Q_OBJECT
+
+public:
+ enum class Error {
+ CouldNotFetchUserStatus,
+ CouldNotFetchPredefinedUserStatuses,
+ UserStatusNotSupported,
+ EmojisNotSupported,
+ CouldNotSetUserStatus,
+ CouldNotClearMessage
+ };
+ Q_ENUM(Error)
+
+ explicit UserStatusConnector(QObject *parent = nullptr);
+
+ ~UserStatusConnector() override;
+
+ virtual void fetchUserStatus() = 0;
+
+ virtual void fetchPredefinedStatuses() = 0;
+
+ virtual void setUserStatus(const UserStatus &userStatus) = 0;
+
+ virtual void clearMessage() = 0;
+
+ virtual UserStatus userStatus() const = 0;
+
+signals:
+ void userStatusFetched(const UserStatus &userStatus);
+ void predefinedStatusesFetched(const std::vector<UserStatus> &statuses);
+ void userStatusSet();
+ void messageCleared();
+ void error(Error error);
+};
+}
+
+Q_DECLARE_METATYPE(OCC::UserStatusConnector *)
+Q_DECLARE_METATYPE(OCC::UserStatus)