From 05c80ded1413743fb229aa0057610bdd594b17ae Mon Sep 17 00:00:00 2001 From: Matthieu Gallien Date: Tue, 2 Jun 2026 17:05:25 +0200 Subject: [PATCH 01/11] feat(governance): add base class for governance related network jobs will enable to share type definitions in all real jobs Signed-off-by: Matthieu Gallien --- src/gui/CMakeLists.txt | 2 + src/gui/governance/governancenetworkjob.cpp | 16 +++++++ src/gui/governance/governancenetworkjob.h | 47 +++++++++++++++++++++ 3 files changed, 65 insertions(+) create mode 100644 src/gui/governance/governancenetworkjob.cpp create mode 100644 src/gui/governance/governancenetworkjob.h diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index b68e5fd9287e4..30e2380c4c36d 100644 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -207,6 +207,8 @@ set(client_SRCS filedetails/shareemodel.cpp filedetails/sortedsharemodel.h filedetails/sortedsharemodel.cpp + governance/governancenetworkjob.h + governance/governancenetworkjob.cpp tray/svgimageprovider.h tray/svgimageprovider.cpp tray/syncstatussummary.h diff --git a/src/gui/governance/governancenetworkjob.cpp b/src/gui/governance/governancenetworkjob.cpp new file mode 100644 index 0000000000000..76fdfe24b4281 --- /dev/null +++ b/src/gui/governance/governancenetworkjob.cpp @@ -0,0 +1,16 @@ +/* + * SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "governancenetworkjob.h" + +namespace OCC +{ + +GovernanceNetworkJob::GovernanceNetworkJob(QObject *parent) + : QObject{parent} +{ +} + +} // namespace OCC diff --git a/src/gui/governance/governancenetworkjob.h b/src/gui/governance/governancenetworkjob.h new file mode 100644 index 0000000000000..50b1704d69e7c --- /dev/null +++ b/src/gui/governance/governancenetworkjob.h @@ -0,0 +1,47 @@ +/* + * SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef GOVERNANCENETWORKJOB_H +#define GOVERNANCENETWORKJOB_H + +#include +#include + +namespace OCC +{ + +class GovernanceNetworkJob : public QObject +{ + Q_OBJECT + QML_ELEMENT +public: + enum class EntityType { + Files, + Mails, + Custom, + }; + + Q_ENUM(EntityType) + + enum class LabelType { + Sensitivity, + REtention, + Hold, + }; + + Q_ENUM(LabelType) + + enum class ApiVersion { + Version_1, + }; + + Q_ENUM(ApiVersion) + + explicit GovernanceNetworkJob(QObject *parent = nullptr); +}; + +} // namespace OCC + +#endif // GOVERNANCENETWORKJOB_H From 1a4b585aaee91ef9d8b093874f0654ddae7fbc52 Mon Sep 17 00:00:00 2001 From: Matthieu Gallien Date: Tue, 2 Jun 2026 17:06:09 +0200 Subject: [PATCH 02/11] feat(governance): very basic getter to test governance capability Signed-off-by: Matthieu Gallien --- src/libsync/capabilities.cpp | 7 +++++++ src/libsync/capabilities.h | 2 ++ 2 files changed, 9 insertions(+) diff --git a/src/libsync/capabilities.cpp b/src/libsync/capabilities.cpp index 73b796fa2b212..e64d3aed9b888 100644 --- a/src/libsync/capabilities.cpp +++ b/src/libsync/capabilities.cpp @@ -13,6 +13,8 @@ #include #include +using namespace Qt::StringLiterals; + namespace OCC { Q_LOGGING_CATEGORY(lcServerCapabilities, "nextcloud.sync.server.capabilities", QtInfoMsg) @@ -534,6 +536,11 @@ DirectEditor* Capabilities::getDirectEditorForOptionalMimetype(const QMimeType & return nullptr; } +bool Capabilities::governanceAvailable() const +{ + return _capabilities.contains(u"governance"_s); +} + /*-------------------------------------------------------------------------------------*/ diff --git a/src/libsync/capabilities.h b/src/libsync/capabilities.h index a06d4a1036761..87861baee7087 100644 --- a/src/libsync/capabilities.h +++ b/src/libsync/capabilities.h @@ -183,6 +183,8 @@ class OWNCLOUDSYNC_EXPORT Capabilities DirectEditor* getDirectEditorForMimetype(const QMimeType &mimeType); DirectEditor* getDirectEditorForOptionalMimetype(const QMimeType &mimeType); + [[nodiscard]] bool governanceAvailable() const; + private: [[nodiscard]] QMap serverThemingMap() const; From c42229a10f1e22d019183dbc555f0d951537a308 Mon Sep 17 00:00:00 2001 From: Matthieu Gallien Date: Tue, 2 Jun 2026 22:50:11 +0200 Subject: [PATCH 03/11] feat(governance): add more job types for each requests also improve the available properties for the job parameters Signed-off-by: Matthieu Gallien --- src/gui/CMakeLists.txt | 10 ++++ src/gui/governance/applygovernancelabel.cpp | 16 +++++ src/gui/governance/applygovernancelabel.h | 26 ++++++++ src/gui/governance/deletegovernancelabel.cpp | 16 +++++ src/gui/governance/deletegovernancelabel.h | 26 ++++++++ .../getavailablegovernancelabels.cpp | 16 +++++ .../governance/getavailablegovernancelabels.h | 31 ++++++++++ src/gui/governance/getgovernancelabels.cpp | 16 +++++ src/gui/governance/getgovernancelabels.h | 26 ++++++++ src/gui/governance/governancenetworkjob.cpp | 60 +++++++++++++++++++ src/gui/governance/governancenetworkjob.h | 44 +++++++++++++- .../governance/typedgovernancenetworkjob.cpp | 31 ++++++++++ .../governance/typedgovernancenetworkjob.h | 39 ++++++++++++ 13 files changed, 356 insertions(+), 1 deletion(-) create mode 100644 src/gui/governance/applygovernancelabel.cpp create mode 100644 src/gui/governance/applygovernancelabel.h create mode 100644 src/gui/governance/deletegovernancelabel.cpp create mode 100644 src/gui/governance/deletegovernancelabel.h create mode 100644 src/gui/governance/getavailablegovernancelabels.cpp create mode 100644 src/gui/governance/getavailablegovernancelabels.h create mode 100644 src/gui/governance/getgovernancelabels.cpp create mode 100644 src/gui/governance/getgovernancelabels.h create mode 100644 src/gui/governance/typedgovernancenetworkjob.cpp create mode 100644 src/gui/governance/typedgovernancenetworkjob.h diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index 30e2380c4c36d..4e0586cb1a379 100644 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -209,6 +209,16 @@ set(client_SRCS filedetails/sortedsharemodel.cpp governance/governancenetworkjob.h governance/governancenetworkjob.cpp + governance/getgovernancelabels.h + governance/getgovernancelabels.cpp + governance/getavailablegovernancelabels.h + governance/getavailablegovernancelabels.cpp + governance/applygovernancelabel.h + governance/applygovernancelabel.cpp + governance/deletegovernancelabel.h + governance/deletegovernancelabel.cpp + governance/typedgovernancenetworkjob.h + governance/typedgovernancenetworkjob.cpp tray/svgimageprovider.h tray/svgimageprovider.cpp tray/syncstatussummary.h diff --git a/src/gui/governance/applygovernancelabel.cpp b/src/gui/governance/applygovernancelabel.cpp new file mode 100644 index 0000000000000..edfaa3aadf08a --- /dev/null +++ b/src/gui/governance/applygovernancelabel.cpp @@ -0,0 +1,16 @@ +/* + * SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "applygovernancelabel.h" + +namespace OCC +{ + +ApplyGovernanceLabel::ApplyGovernanceLabel(QObject *parent) + : OCC::TypedGovernanceNetworkJob{parent} +{ +} + +} // namespace OCC diff --git a/src/gui/governance/applygovernancelabel.h b/src/gui/governance/applygovernancelabel.h new file mode 100644 index 0000000000000..695706a718e9e --- /dev/null +++ b/src/gui/governance/applygovernancelabel.h @@ -0,0 +1,26 @@ +/* + * SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef APPLYGOVERNANCELABEL_H +#define APPLYGOVERNANCELABEL_H + +#include "typedgovernancenetworkjob.h" +#include +#include + +namespace OCC +{ + +class ApplyGovernanceLabel : public OCC::TypedGovernanceNetworkJob +{ + Q_OBJECT + QML_ELEMENT +public: + explicit ApplyGovernanceLabel(QObject *parent = nullptr); +}; + +} // namespace OCC + +#endif // APPLYGOVERNANCELABEL_H diff --git a/src/gui/governance/deletegovernancelabel.cpp b/src/gui/governance/deletegovernancelabel.cpp new file mode 100644 index 0000000000000..550af2df618f7 --- /dev/null +++ b/src/gui/governance/deletegovernancelabel.cpp @@ -0,0 +1,16 @@ +/* + * SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "deletegovernancelabel.h" + +namespace OCC +{ + +DeleteGovernanceLabel::DeleteGovernanceLabel(QObject *parent) + : OCC::TypedGovernanceNetworkJob{parent} +{ +} + +} // namespace OCC diff --git a/src/gui/governance/deletegovernancelabel.h b/src/gui/governance/deletegovernancelabel.h new file mode 100644 index 0000000000000..4bdc658650820 --- /dev/null +++ b/src/gui/governance/deletegovernancelabel.h @@ -0,0 +1,26 @@ +/* + * SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef DELETEGOVERNANCELABEL_H +#define DELETEGOVERNANCELABEL_H + +#include "typedgovernancenetworkjob.h" +#include +#include + +namespace OCC +{ + +class DeleteGovernanceLabel : public OCC::TypedGovernanceNetworkJob +{ + Q_OBJECT + QML_ELEMENT +public: + explicit DeleteGovernanceLabel(QObject *parent = nullptr); +}; + +} // namespace OCC + +#endif // DELETEGOVERNANCELABEL_H diff --git a/src/gui/governance/getavailablegovernancelabels.cpp b/src/gui/governance/getavailablegovernancelabels.cpp new file mode 100644 index 0000000000000..c9659cf5593b8 --- /dev/null +++ b/src/gui/governance/getavailablegovernancelabels.cpp @@ -0,0 +1,16 @@ +/* + * SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "getavailablegovernancelabels.h" + +namespace OCC +{ + +GetAvailableGovernanceLabels::GetAvailableGovernanceLabels(QObject *parent) + : OCC::TypedGovernanceNetworkJob{parent} +{ +} + +} // namespace OCC diff --git a/src/gui/governance/getavailablegovernancelabels.h b/src/gui/governance/getavailablegovernancelabels.h new file mode 100644 index 0000000000000..9849225d47a1d --- /dev/null +++ b/src/gui/governance/getavailablegovernancelabels.h @@ -0,0 +1,31 @@ +/* + * SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef GETAVAILABLEGOVERNANCELABELS_H +#define GETAVAILABLEGOVERNANCELABELS_H + +#include "typedgovernancenetworkjob.h" +#include +#include + +namespace OCC +{ + +class GetAvailableGovernanceLabels : public OCC::TypedGovernanceNetworkJob +{ + Q_OBJECT + QML_ELEMENT + +public: + explicit GetAvailableGovernanceLabels(QObject *parent = nullptr); + +Q_SIGNALS: + +private: +}; + +} // namespace OCC + +#endif // GETAVAILABLEGOVERNANCELABELS_H diff --git a/src/gui/governance/getgovernancelabels.cpp b/src/gui/governance/getgovernancelabels.cpp new file mode 100644 index 0000000000000..65a57b20589b4 --- /dev/null +++ b/src/gui/governance/getgovernancelabels.cpp @@ -0,0 +1,16 @@ +/* + * SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "getgovernancelabels.h" + +namespace OCC +{ + +GetGovernanceLabels::GetGovernanceLabels(QObject *parent) + : OCC::GovernanceNetworkJob{parent} +{ +} + +} // namespace OCC diff --git a/src/gui/governance/getgovernancelabels.h b/src/gui/governance/getgovernancelabels.h new file mode 100644 index 0000000000000..a488e1b7e8b1d --- /dev/null +++ b/src/gui/governance/getgovernancelabels.h @@ -0,0 +1,26 @@ +/* + * SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef GETGOVERNANCELABELS_H +#define GETGOVERNANCELABELS_H + +#include "governancenetworkjob.h" +#include +#include + +namespace OCC +{ + +class GetGovernanceLabels : public OCC::GovernanceNetworkJob +{ + Q_OBJECT + QML_ELEMENT +public: + explicit GetGovernanceLabels(QObject *parent = nullptr); +}; + +} // namespace OCC + +#endif // GETGOVERNANCELABELS_H diff --git a/src/gui/governance/governancenetworkjob.cpp b/src/gui/governance/governancenetworkjob.cpp index 76fdfe24b4281..593b7b6e8a58f 100644 --- a/src/gui/governance/governancenetworkjob.cpp +++ b/src/gui/governance/governancenetworkjob.cpp @@ -13,4 +13,64 @@ GovernanceNetworkJob::GovernanceNetworkJob(QObject *parent) { } +GovernanceNetworkJob::ApiVersion GovernanceNetworkJob::apiVersion() const +{ + return _apiVersion; +} + +void GovernanceNetworkJob::setApiVersion(ApiVersion newApiVersion) +{ + if (_apiVersion == newApiVersion) { + return; + } + + _apiVersion = newApiVersion; + Q_EMIT apiVersionChanged(); +} + +GovernanceNetworkJob::EntityType GovernanceNetworkJob::entityType() const +{ + return _entityType; +} + +void GovernanceNetworkJob::setEntityType(EntityType newEntityType) +{ + if (_entityType == newEntityType) { + return; + } + + _entityType = newEntityType; + Q_EMIT entityTypeChanged(); +} + +QString GovernanceNetworkJob::customEntityType() const +{ + return _customEntityType; +} + +void GovernanceNetworkJob::setCustomEntityType(const QString &newCustomEntityType) +{ + if (_customEntityType == newCustomEntityType) { + return; + } + + _customEntityType = newCustomEntityType; + Q_EMIT customEntityTypeChanged(); +} + +QString GovernanceNetworkJob::entityId() const +{ + return _entityId; +} + +void GovernanceNetworkJob::setEntityId(const QString &newEntityId) +{ + if (_entityId == newEntityId) { + return; + } + + _entityId = newEntityId; + Q_EMIT entityIdChanged(); +} + } // namespace OCC diff --git a/src/gui/governance/governancenetworkjob.h b/src/gui/governance/governancenetworkjob.h index 50b1704d69e7c..a28a716bad2a8 100644 --- a/src/gui/governance/governancenetworkjob.h +++ b/src/gui/governance/governancenetworkjob.h @@ -16,6 +16,15 @@ class GovernanceNetworkJob : public QObject { Q_OBJECT QML_ELEMENT + + Q_PROPERTY(ApiVersion apiVersion READ apiVersion WRITE setApiVersion NOTIFY apiVersionChanged FINAL) + + Q_PROPERTY(EntityType entityType READ entityType WRITE setEntityType NOTIFY entityTypeChanged FINAL) + + Q_PROPERTY(QString customEntityType READ customEntityType WRITE setCustomEntityType NOTIFY customEntityTypeChanged FINAL) + + Q_PROPERTY(QString entityId READ entityId WRITE setEntityId NOTIFY entityIdChanged FINAL) + public: enum class EntityType { Files, @@ -27,7 +36,7 @@ class GovernanceNetworkJob : public QObject enum class LabelType { Sensitivity, - REtention, + Retention, Hold, }; @@ -40,6 +49,39 @@ class GovernanceNetworkJob : public QObject Q_ENUM(ApiVersion) explicit GovernanceNetworkJob(QObject *parent = nullptr); + + [[nodiscard]] ApiVersion apiVersion() const; + + void setApiVersion(ApiVersion newApiVersion); + + [[nodiscard]] EntityType entityType() const; + + void setEntityType(EntityType newEntityType); + + [[nodiscard]] QString customEntityType() const; + + void setCustomEntityType(const QString &newCustomEntityType); + + QString entityId() const; + void setEntityId(const QString &newEntityId); + +Q_SIGNALS: + void apiVersionChanged(); + + void entityTypeChanged(); + + void customEntityTypeChanged(); + + void entityIdChanged(); + +private: + ApiVersion _apiVersion = ApiVersion::Version_1; + + EntityType _entityType = EntityType::Files; + + QString _customEntityType; + + QString _entityId; }; } // namespace OCC diff --git a/src/gui/governance/typedgovernancenetworkjob.cpp b/src/gui/governance/typedgovernancenetworkjob.cpp new file mode 100644 index 0000000000000..e17f9bf7f7c64 --- /dev/null +++ b/src/gui/governance/typedgovernancenetworkjob.cpp @@ -0,0 +1,31 @@ +/* + * SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "typedgovernancenetworkjob.h" + +namespace OCC +{ + +TypedGovernanceNetworkJob::TypedGovernanceNetworkJob(QObject *parent) + : OCC::GovernanceNetworkJob{parent} +{ +} + +GovernanceNetworkJob::LabelType TypedGovernanceNetworkJob::labelType() const +{ + return _labelType; +} + +void TypedGovernanceNetworkJob::setLabelType(LabelType newLabelType) +{ + if (_labelType == newLabelType) { + return; + } + + _labelType = newLabelType; + Q_EMIT labelTypeChanged(); +} + +} // namespace OCC diff --git a/src/gui/governance/typedgovernancenetworkjob.h b/src/gui/governance/typedgovernancenetworkjob.h new file mode 100644 index 0000000000000..d114624b02403 --- /dev/null +++ b/src/gui/governance/typedgovernancenetworkjob.h @@ -0,0 +1,39 @@ +/* + * SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef TYPEDGOVERNANCENETWORKJOB_H +#define TYPEDGOVERNANCENETWORKJOB_H + +#include "governancenetworkjob.h" +#include +#include + +namespace OCC +{ + +class TypedGovernanceNetworkJob : public GovernanceNetworkJob +{ + Q_OBJECT + QML_ELEMENT + + Q_PROPERTY(LabelType labelType READ labelType WRITE setLabelType NOTIFY labelTypeChanged FINAL) + +public: + TypedGovernanceNetworkJob(QObject *parent = nullptr); + + [[nodiscard]] LabelType labelType() const; + + void setLabelType(LabelType newLabelType); + +Q_SIGNALS: + void labelTypeChanged(); + +private: + LabelType _labelType; +}; + +} // namespace OCC + +#endif // TYPEDGOVERNANCENETWORKJOB_H From 6a5e67aa30e81389eb30818466f82979916ef77e Mon Sep 17 00:00:00 2001 From: Matthieu Gallien Date: Wed, 3 Jun 2026 18:32:31 +0200 Subject: [PATCH 04/11] feat(governance): getting ready to be able to use governance API finish implementing most of the code needed to send requests to the governance API will need further work to see how to read the replies and make something usefull Signed-off-by: Matthieu Gallien --- src/gui/CMakeLists.txt | 4 ++ src/gui/governance/applygovernancelabel.cpp | 27 +++++++++++- src/gui/governance/applygovernancelabel.h | 15 +++++-- src/gui/governance/deletegovernancelabel.cpp | 27 +++++++++++- src/gui/governance/deletegovernancelabel.h | 15 +++++-- .../getavailablegovernancelabels.cpp | 34 +++++++++++++- .../governance/getavailablegovernancelabels.h | 14 +++++- src/gui/governance/getgovernancelabels.cpp | 27 +++++++++++- src/gui/governance/getgovernancelabels.h | 11 ++++- src/gui/governance/governancenetworkjob.cpp | 44 ++++++++++++++++++- src/gui/governance/governancenetworkjob.h | 38 +++++++++++++++- src/gui/governance/ocsgovernancejob.cpp | 26 +++++++++++ src/gui/governance/ocsgovernancejob.h | 28 ++++++++++++ .../governance/typedgovernancenetworkjob.cpp | 26 ++++++++++- .../governance/typedgovernancenetworkjob.h | 6 ++- .../typedwithlabelidgovernancenetworkjob.cpp | 39 ++++++++++++++++ .../typedwithlabelidgovernancenetworkjob.h | 43 ++++++++++++++++++ 17 files changed, 401 insertions(+), 23 deletions(-) create mode 100644 src/gui/governance/ocsgovernancejob.cpp create mode 100644 src/gui/governance/ocsgovernancejob.h create mode 100644 src/gui/governance/typedwithlabelidgovernancenetworkjob.cpp create mode 100644 src/gui/governance/typedwithlabelidgovernancenetworkjob.h diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index 4e0586cb1a379..787ad0f0c162f 100644 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -219,6 +219,10 @@ set(client_SRCS governance/deletegovernancelabel.cpp governance/typedgovernancenetworkjob.h governance/typedgovernancenetworkjob.cpp + governance/ocsgovernancejob.h + governance/ocsgovernancejob.cpp + governance/typedwithlabelidgovernancenetworkjob.h + governance/typedwithlabelidgovernancenetworkjob.cpp tray/svgimageprovider.h tray/svgimageprovider.cpp tray/syncstatussummary.h diff --git a/src/gui/governance/applygovernancelabel.cpp b/src/gui/governance/applygovernancelabel.cpp index edfaa3aadf08a..0b5f3f6d63fdb 100644 --- a/src/gui/governance/applygovernancelabel.cpp +++ b/src/gui/governance/applygovernancelabel.cpp @@ -5,12 +5,35 @@ #include "applygovernancelabel.h" +#include "ocsgovernancejob.h" + namespace OCC { -ApplyGovernanceLabel::ApplyGovernanceLabel(QObject *parent) - : OCC::TypedGovernanceNetworkJob{parent} +ApplyGovernanceLabel::ApplyGovernanceLabel(AccountPtr account, QObject *parent) + : OCC::TypedWithLabelIdGovernanceNetworkJob{account, parent} +{ +} + +void ApplyGovernanceLabel::start() { + setOcsGovernanceJob(QPointer{new OcsGovernanceJob{account()}}); + + connect(ocsGovernanceJob().data(), &OcsJob::jobFinished, + this, &ApplyGovernanceLabel::jobDone); + + ocsGovernanceJob()->setPath(buildPath()); + ocsGovernanceJob()->setMethod("POST"); + + ocsGovernanceJob()->start(); +} + +void ApplyGovernanceLabel::jobDone(QJsonDocument reply, int statusCode) +{ + Q_UNUSED(reply) + Q_UNUSED(statusCode) + + Q_EMIT finished(); } } // namespace OCC diff --git a/src/gui/governance/applygovernancelabel.h b/src/gui/governance/applygovernancelabel.h index 695706a718e9e..c118e9756ab19 100644 --- a/src/gui/governance/applygovernancelabel.h +++ b/src/gui/governance/applygovernancelabel.h @@ -6,19 +6,28 @@ #ifndef APPLYGOVERNANCELABEL_H #define APPLYGOVERNANCELABEL_H -#include "typedgovernancenetworkjob.h" +#include "typedwithlabelidgovernancenetworkjob.h" + #include #include +#include namespace OCC { -class ApplyGovernanceLabel : public OCC::TypedGovernanceNetworkJob +class ApplyGovernanceLabel : public OCC::TypedWithLabelIdGovernanceNetworkJob { Q_OBJECT QML_ELEMENT public: - explicit ApplyGovernanceLabel(QObject *parent = nullptr); + explicit ApplyGovernanceLabel(AccountPtr account, + QObject *parent = nullptr); + +public Q_SLOTS: + void start(); + +private Q_SLOTS: + void jobDone(QJsonDocument reply, int statusCode); }; } // namespace OCC diff --git a/src/gui/governance/deletegovernancelabel.cpp b/src/gui/governance/deletegovernancelabel.cpp index 550af2df618f7..558ed5e294442 100644 --- a/src/gui/governance/deletegovernancelabel.cpp +++ b/src/gui/governance/deletegovernancelabel.cpp @@ -5,12 +5,35 @@ #include "deletegovernancelabel.h" +#include "ocsgovernancejob.h" + namespace OCC { -DeleteGovernanceLabel::DeleteGovernanceLabel(QObject *parent) - : OCC::TypedGovernanceNetworkJob{parent} +DeleteGovernanceLabel::DeleteGovernanceLabel(AccountPtr account, QObject *parent) + : OCC::TypedWithLabelIdGovernanceNetworkJob{account, parent} +{ +} + +void DeleteGovernanceLabel::start() { + setOcsGovernanceJob(QPointer{new OcsGovernanceJob{account()}}); + + connect(ocsGovernanceJob().data(), &OcsJob::jobFinished, + this, &DeleteGovernanceLabel::jobDone); + + ocsGovernanceJob()->setPath(buildPath()); + ocsGovernanceJob()->setMethod("DELETE"); + + ocsGovernanceJob()->start(); +} + +void DeleteGovernanceLabel::jobDone(QJsonDocument reply, int statusCode) +{ + Q_UNUSED(reply) + Q_UNUSED(statusCode) + + Q_EMIT finished(); } } // namespace OCC diff --git a/src/gui/governance/deletegovernancelabel.h b/src/gui/governance/deletegovernancelabel.h index 4bdc658650820..d9591d9ccfb5b 100644 --- a/src/gui/governance/deletegovernancelabel.h +++ b/src/gui/governance/deletegovernancelabel.h @@ -6,19 +6,28 @@ #ifndef DELETEGOVERNANCELABEL_H #define DELETEGOVERNANCELABEL_H -#include "typedgovernancenetworkjob.h" +#include "typedwithlabelidgovernancenetworkjob.h" + #include #include +#include namespace OCC { -class DeleteGovernanceLabel : public OCC::TypedGovernanceNetworkJob +class DeleteGovernanceLabel : public OCC::TypedWithLabelIdGovernanceNetworkJob { Q_OBJECT QML_ELEMENT public: - explicit DeleteGovernanceLabel(QObject *parent = nullptr); + explicit DeleteGovernanceLabel(AccountPtr account, + QObject *parent = nullptr); + +public Q_SLOTS: + void start(); + +private Q_SLOTS: + void jobDone(QJsonDocument reply, int statusCode); }; } // namespace OCC diff --git a/src/gui/governance/getavailablegovernancelabels.cpp b/src/gui/governance/getavailablegovernancelabels.cpp index c9659cf5593b8..0b713a45fc12a 100644 --- a/src/gui/governance/getavailablegovernancelabels.cpp +++ b/src/gui/governance/getavailablegovernancelabels.cpp @@ -5,12 +5,42 @@ #include "getavailablegovernancelabels.h" +#include "ocsgovernancejob.h" + +using namespace Qt::StringLiterals; + namespace OCC { -GetAvailableGovernanceLabels::GetAvailableGovernanceLabels(QObject *parent) - : OCC::TypedGovernanceNetworkJob{parent} +GetAvailableGovernanceLabels::GetAvailableGovernanceLabels(AccountPtr account, QObject *parent) + : OCC::TypedGovernanceNetworkJob{account, parent} +{ +} + +void GetAvailableGovernanceLabels::start() +{ + setOcsGovernanceJob(QPointer{new OcsGovernanceJob{account()}}); + + connect(ocsGovernanceJob().data(), &OcsJob::jobFinished, + this, &GetAvailableGovernanceLabels::jobDone); + + ocsGovernanceJob()->setPath(buildPath()); + ocsGovernanceJob()->setMethod("GET"); + + ocsGovernanceJob()->start(); +} + +void GetAvailableGovernanceLabels::jobDone(QJsonDocument reply, int statusCode) +{ + Q_UNUSED(reply) + Q_UNUSED(statusCode) + + Q_EMIT finished(); +} + +QString GetAvailableGovernanceLabels::buildPath() const { + return u"/ocs/v2.php/apps/governance/%1/labels/%2/%3/%4/available"_s.arg(apiVersionAsString(), entityTypeAsString(), entityId(), labelTypeAsString()); } } // namespace OCC diff --git a/src/gui/governance/getavailablegovernancelabels.h b/src/gui/governance/getavailablegovernancelabels.h index 9849225d47a1d..0d3032d86238f 100644 --- a/src/gui/governance/getavailablegovernancelabels.h +++ b/src/gui/governance/getavailablegovernancelabels.h @@ -7,8 +7,10 @@ #define GETAVAILABLEGOVERNANCELABELS_H #include "typedgovernancenetworkjob.h" + #include #include +#include namespace OCC { @@ -19,11 +21,19 @@ class GetAvailableGovernanceLabels : public OCC::TypedGovernanceNetworkJob QML_ELEMENT public: - explicit GetAvailableGovernanceLabels(QObject *parent = nullptr); + explicit GetAvailableGovernanceLabels(AccountPtr account, + QObject *parent = nullptr); Q_SIGNALS: -private: +public Q_SLOTS: + void start(); + +protected: + [[nodiscard]] QString buildPath() const override; + +private Q_SLOTS: + void jobDone(QJsonDocument reply, int statusCode); }; } // namespace OCC diff --git a/src/gui/governance/getgovernancelabels.cpp b/src/gui/governance/getgovernancelabels.cpp index 65a57b20589b4..26ac486d1463c 100644 --- a/src/gui/governance/getgovernancelabels.cpp +++ b/src/gui/governance/getgovernancelabels.cpp @@ -5,12 +5,35 @@ #include "getgovernancelabels.h" +#include "ocsgovernancejob.h" + namespace OCC { -GetGovernanceLabels::GetGovernanceLabels(QObject *parent) - : OCC::GovernanceNetworkJob{parent} +GetGovernanceLabels::GetGovernanceLabels(AccountPtr account, QObject *parent) + : OCC::GovernanceNetworkJob{account, parent} +{ +} + +void GetGovernanceLabels::start() { + setOcsGovernanceJob(QPointer{new OcsGovernanceJob{account()}}); + + connect(ocsGovernanceJob().data(), &OcsJob::jobFinished, + this, &GetGovernanceLabels::jobDone); + + ocsGovernanceJob()->setPath(buildPath()); + ocsGovernanceJob()->setMethod("GET"); + + ocsGovernanceJob()->start(); +} + +void GetGovernanceLabels::jobDone(QJsonDocument reply, int statusCode) +{ + Q_UNUSED(reply) + Q_UNUSED(statusCode) + + Q_EMIT finished(); } } // namespace OCC diff --git a/src/gui/governance/getgovernancelabels.h b/src/gui/governance/getgovernancelabels.h index a488e1b7e8b1d..a389aba6671a9 100644 --- a/src/gui/governance/getgovernancelabels.h +++ b/src/gui/governance/getgovernancelabels.h @@ -7,8 +7,10 @@ #define GETGOVERNANCELABELS_H #include "governancenetworkjob.h" + #include #include +#include namespace OCC { @@ -18,7 +20,14 @@ class GetGovernanceLabels : public OCC::GovernanceNetworkJob Q_OBJECT QML_ELEMENT public: - explicit GetGovernanceLabels(QObject *parent = nullptr); + explicit GetGovernanceLabels(AccountPtr account, + QObject *parent = nullptr); + +public Q_SLOTS: + void start(); + +private Q_SLOTS: + void jobDone(QJsonDocument reply, int statusCode); }; } // namespace OCC diff --git a/src/gui/governance/governancenetworkjob.cpp b/src/gui/governance/governancenetworkjob.cpp index 593b7b6e8a58f..9515d93091d37 100644 --- a/src/gui/governance/governancenetworkjob.cpp +++ b/src/gui/governance/governancenetworkjob.cpp @@ -5,11 +5,14 @@ #include "governancenetworkjob.h" +using namespace Qt::StringLiterals; + namespace OCC { -GovernanceNetworkJob::GovernanceNetworkJob(QObject *parent) +GovernanceNetworkJob::GovernanceNetworkJob(AccountPtr account, QObject *parent) : QObject{parent} + , _account{account} { } @@ -73,4 +76,43 @@ void GovernanceNetworkJob::setEntityId(const QString &newEntityId) Q_EMIT entityIdChanged(); } +QString GovernanceNetworkJob::buildPath() const +{ + return u"/ocs/v2.php/apps/governance/%1/labels/%2/%3"_s.arg(apiVersionAsString(), entityTypeAsString(), entityId()); +} + +QString GovernanceNetworkJob::apiVersionAsString() const +{ + auto result = QString{}; + + switch (_apiVersion) + { + case ApiVersion::Version_1: + result = u"v1"_s; + break; + } + + return result; +} + +QString GovernanceNetworkJob::entityTypeAsString() const +{ + auto result = QString{}; + + switch (_entityType) + { + case EntityType::Files: + result = u"FILES"_s; + break; + case EntityType::Mails: + result = u"MAILS"_s; + break; + case EntityType::Custom: + result = _customEntityType; + break; + } + + return result; +} + } // namespace OCC diff --git a/src/gui/governance/governancenetworkjob.h b/src/gui/governance/governancenetworkjob.h index a28a716bad2a8..a4368cdba49e5 100644 --- a/src/gui/governance/governancenetworkjob.h +++ b/src/gui/governance/governancenetworkjob.h @@ -6,12 +6,16 @@ #ifndef GOVERNANCENETWORKJOB_H #define GOVERNANCENETWORKJOB_H +#include "accountfwd.h" + #include #include namespace OCC { +class OcsGovernanceJob; + class GovernanceNetworkJob : public QObject { Q_OBJECT @@ -48,7 +52,8 @@ class GovernanceNetworkJob : public QObject Q_ENUM(ApiVersion) - explicit GovernanceNetworkJob(QObject *parent = nullptr); + explicit GovernanceNetworkJob(AccountPtr account, + QObject *parent = nullptr); [[nodiscard]] ApiVersion apiVersion() const; @@ -62,7 +67,8 @@ class GovernanceNetworkJob : public QObject void setCustomEntityType(const QString &newCustomEntityType); - QString entityId() const; + [[nodiscard]] QString entityId() const; + void setEntityId(const QString &newEntityId); Q_SIGNALS: @@ -74,7 +80,33 @@ class GovernanceNetworkJob : public QObject void entityIdChanged(); + void finished(); + +protected: + void setOcsGovernanceJob(QPointer newJob) + { + _ocsGovernanceJob = newJob; + } + + [[nodiscard]] QPointer ocsGovernanceJob() const + { + return _ocsGovernanceJob; + } + + [[nodiscard]] AccountPtr account() const + { + return _account; + } + + [[nodiscard]] virtual QString buildPath() const; + + [[nodiscard]] QString apiVersionAsString() const; + + [[nodiscard]] QString entityTypeAsString() const; + private: + AccountPtr _account; + ApiVersion _apiVersion = ApiVersion::Version_1; EntityType _entityType = EntityType::Files; @@ -82,6 +114,8 @@ class GovernanceNetworkJob : public QObject QString _customEntityType; QString _entityId; + + QPointer _ocsGovernanceJob; }; } // namespace OCC diff --git a/src/gui/governance/ocsgovernancejob.cpp b/src/gui/governance/ocsgovernancejob.cpp new file mode 100644 index 0000000000000..bfde0274f9cb6 --- /dev/null +++ b/src/gui/governance/ocsgovernancejob.cpp @@ -0,0 +1,26 @@ +/* + * SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "ocsgovernancejob.h" + +namespace OCC +{ + +OcsGovernanceJob::OcsGovernanceJob(AccountPtr account) + : OCC::OcsJob{account} +{ +} + +void OcsGovernanceJob::setMethod(const QByteArray &method) +{ + setVerb(method); +} + +void OcsGovernanceJob::start() +{ + OcsJob::start(); +} + +} // namespace OCC diff --git a/src/gui/governance/ocsgovernancejob.h b/src/gui/governance/ocsgovernancejob.h new file mode 100644 index 0000000000000..95a9fe83427b3 --- /dev/null +++ b/src/gui/governance/ocsgovernancejob.h @@ -0,0 +1,28 @@ +/* + * SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef OCSGOVERNANCEJOB_H +#define OCSGOVERNANCEJOB_H + +#include +#include + +namespace OCC +{ + +class OcsGovernanceJob : public OCC::OcsJob +{ + Q_OBJECT +public: + explicit OcsGovernanceJob(AccountPtr account); + + void setMethod(const QByteArray &method); + + void start() override; +}; + +} // namespace OCC + +#endif // OCSGOVERNANCEJOB_H diff --git a/src/gui/governance/typedgovernancenetworkjob.cpp b/src/gui/governance/typedgovernancenetworkjob.cpp index e17f9bf7f7c64..71a76d5f1c01c 100644 --- a/src/gui/governance/typedgovernancenetworkjob.cpp +++ b/src/gui/governance/typedgovernancenetworkjob.cpp @@ -5,11 +5,13 @@ #include "typedgovernancenetworkjob.h" +using namespace Qt::StringLiterals; + namespace OCC { -TypedGovernanceNetworkJob::TypedGovernanceNetworkJob(QObject *parent) - : OCC::GovernanceNetworkJob{parent} +TypedGovernanceNetworkJob::TypedGovernanceNetworkJob(AccountPtr account, QObject *parent) + : OCC::GovernanceNetworkJob{account, parent} { } @@ -28,4 +30,24 @@ void TypedGovernanceNetworkJob::setLabelType(LabelType newLabelType) Q_EMIT labelTypeChanged(); } +QString TypedGovernanceNetworkJob::labelTypeAsString() const +{ + auto result = QString{}; + + switch (_labelType) + { + case LabelType::Sensitivity: + result = u"sensitivity"_s; + break; + case LabelType::Retention: + result = u"retention"_s; + break; + case LabelType::Hold: + result = u"hold"_s; + break; + } + + return result; +} + } // namespace OCC diff --git a/src/gui/governance/typedgovernancenetworkjob.h b/src/gui/governance/typedgovernancenetworkjob.h index d114624b02403..f0b3253b075e2 100644 --- a/src/gui/governance/typedgovernancenetworkjob.h +++ b/src/gui/governance/typedgovernancenetworkjob.h @@ -21,7 +21,8 @@ class TypedGovernanceNetworkJob : public GovernanceNetworkJob Q_PROPERTY(LabelType labelType READ labelType WRITE setLabelType NOTIFY labelTypeChanged FINAL) public: - TypedGovernanceNetworkJob(QObject *parent = nullptr); + TypedGovernanceNetworkJob(AccountPtr account, + QObject *parent = nullptr); [[nodiscard]] LabelType labelType() const; @@ -30,6 +31,9 @@ class TypedGovernanceNetworkJob : public GovernanceNetworkJob Q_SIGNALS: void labelTypeChanged(); +protected: + [[nodiscard]] QString labelTypeAsString() const; + private: LabelType _labelType; }; diff --git a/src/gui/governance/typedwithlabelidgovernancenetworkjob.cpp b/src/gui/governance/typedwithlabelidgovernancenetworkjob.cpp new file mode 100644 index 0000000000000..6b777d27f664c --- /dev/null +++ b/src/gui/governance/typedwithlabelidgovernancenetworkjob.cpp @@ -0,0 +1,39 @@ +/* + * SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "typedwithlabelidgovernancenetworkjob.h" + +using namespace Qt::StringLiterals; + +namespace OCC +{ + +TypedWithLabelIdGovernanceNetworkJob::TypedWithLabelIdGovernanceNetworkJob(AccountPtr account, + QObject *parent) + : OCC::TypedGovernanceNetworkJob{account, parent} +{ +} + +QString TypedWithLabelIdGovernanceNetworkJob::labelId() const +{ + return _labelId; +} + +void TypedWithLabelIdGovernanceNetworkJob::setLabelId(const QString &newLabelId) +{ + if (_labelId == newLabelId) { + return; + } + + _labelId = newLabelId; + Q_EMIT labelIdChanged(); +} + +QString TypedWithLabelIdGovernanceNetworkJob::buildPath() const +{ + return u"/ocs/v2.php/apps/governance/%1/labels/%2/%3/%4/%5"_s.arg(apiVersionAsString(), entityTypeAsString(), entityId(), labelId()); +} + +} // namespace OCC diff --git a/src/gui/governance/typedwithlabelidgovernancenetworkjob.h b/src/gui/governance/typedwithlabelidgovernancenetworkjob.h new file mode 100644 index 0000000000000..fc4c997eda756 --- /dev/null +++ b/src/gui/governance/typedwithlabelidgovernancenetworkjob.h @@ -0,0 +1,43 @@ +/* + * SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef TYPEDWITHLABELIDGOVERNANCENETWORKJOB_H +#define TYPEDWITHLABELIDGOVERNANCENETWORKJOB_H + +#include "typedgovernancenetworkjob.h" +#include +#include + +namespace OCC +{ + +class TypedWithLabelIdGovernanceNetworkJob : public OCC::TypedGovernanceNetworkJob +{ + Q_OBJECT + QML_ELEMENT + + Q_PROPERTY(QString labelId READ labelId WRITE setLabelId NOTIFY labelIdChanged FINAL) + +public: + explicit TypedWithLabelIdGovernanceNetworkJob(AccountPtr account, + QObject *parent = nullptr); + + [[nodiscard]] QString labelId() const; + + void setLabelId(const QString &newLabelId); + +signals: + void labelIdChanged(); + +protected: + [[nodiscard]] QString buildPath() const override; + +private: + QString _labelId; +}; + +} // namespace OCC + +#endif // TYPEDWITHLABELIDGOVERNANCENETWORKJOB_H From 3c29e3d83090060991abc72a47a56d52e3d63adc Mon Sep 17 00:00:00 2001 From: Matthieu Gallien Date: Thu, 4 Jun 2026 16:38:42 +0200 Subject: [PATCH 05/11] feat(governance): open governance labels dialog from files manager Signed-off-by: Matthieu Gallien --- resources.qrc | 1 + src/gui/GovernanceLabelsDialog.qml | 157 ++++++++++++++++++ src/gui/application.cpp | 3 + src/gui/governance/applygovernancelabel.cpp | 7 +- src/gui/governance/applygovernancelabel.h | 3 +- src/gui/governance/deletegovernancelabel.cpp | 7 +- src/gui/governance/deletegovernancelabel.h | 3 +- .../getavailablegovernancelabels.cpp | 6 +- .../governance/getavailablegovernancelabels.h | 3 +- src/gui/governance/getgovernancelabels.cpp | 7 +- src/gui/governance/getgovernancelabels.h | 3 +- src/gui/governance/governancenetworkjob.cpp | 15 +- src/gui/governance/governancenetworkjob.h | 13 +- .../governance/typedgovernancenetworkjob.cpp | 4 +- .../governance/typedgovernancenetworkjob.h | 3 +- .../typedwithlabelidgovernancenetworkjob.cpp | 7 +- .../typedwithlabelidgovernancenetworkjob.h | 3 +- src/gui/owncloudgui.cpp | 18 ++ src/gui/owncloudgui.h | 3 + src/gui/socketapi/socketapi.cpp | 27 +++ src/gui/socketapi/socketapi.h | 5 + src/gui/systray.cpp | 31 ++++ src/gui/systray.h | 1 + src/gui/wizard/qml/AccountWizardWindow.qml | 1 + 24 files changed, 301 insertions(+), 30 deletions(-) create mode 100644 src/gui/GovernanceLabelsDialog.qml diff --git a/resources.qrc b/resources.qrc index ba9cdae0e0706..a372fa3eb691a 100644 --- a/resources.qrc +++ b/resources.qrc @@ -75,5 +75,6 @@ src/gui/macOS/ui/FileProviderSettings.qml src/gui/macOS/ui/FileProviderFileDelegate.qml src/gui/integration/FileActionsWindow.qml + src/gui/GovernanceLabelsDialog.qml diff --git a/src/gui/GovernanceLabelsDialog.qml b/src/gui/GovernanceLabelsDialog.qml new file mode 100644 index 0000000000000..9c1c8571a85d9 --- /dev/null +++ b/src/gui/GovernanceLabelsDialog.qml @@ -0,0 +1,157 @@ +/* + * SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +import QtQml +import QtQuick +import QtQuick.Window as QtWindow +import QtQuick.Layouts +import QtQuick.Controls +import QtQml.Models +import Style +import com.nextcloud.desktopclient +import "./tray" + +ApplicationWindow { + id: governanceLabelsDialog + + required property var fileName + required property var fileId + required property var account + + flags: Qt.Window | Qt.Dialog + visible: true + + LayoutMirroring.enabled: Application.layoutDirection === Qt.RightToLeft + LayoutMirroring.childrenInherit: true + + width: Style.minimumWidthResolveConflictsDialog + height: Style.minimumHeightResolveConflictsDialog + minimumWidth: Style.minimumWidthResolveConflictsDialog + minimumHeight: Style.minimumHeightResolveConflictsDialog + title: qsTr('Applys labels') + + onClosing: function(close) { + Systray.destroyDialog(self); + close.accepted = true + } + + ApplyGovernanceLabel { + id: applyGovernanceLabel + + account: governanceLabelsDialog.account + + labelId: 'labelId' + labelType: GovernanceNetworkJob.Sensitivity + entityId: governanceLabelsDialog.fileId + } + + DeleteGovernanceLabel { + id: deleteGovernanceLabel + + account: governanceLabelsDialog.account + + labelId: 'labelId' + labelType: GovernanceNetworkJob.Sensitivity + entityId: governanceLabelsDialog.fileId + } + + GetAvailableGovernanceLabels { + id: getAvailableGovernanceLabelsForSensitivity + + account: governanceLabelsDialog.account + + labelType: GovernanceNetworkJob.Sensitivity + entityId: governanceLabelsDialog.fileId + } + + GetAvailableGovernanceLabels { + id: getAvailableGovernanceLabelsForHold + + account: governanceLabelsDialog.account + + labelType: GovernanceNetworkJob.Hold + entityId: governanceLabelsDialog.fileId + } + + GetAvailableGovernanceLabels { + id: getAvailableGovernanceLabelsForRetention + + account: governanceLabelsDialog.account + + labelType: GovernanceNetworkJob.Retention + entityId: governanceLabelsDialog.fileId + } + + GetGovernanceLabels { + id: getGovernanceLabels + + account: governanceLabelsDialog.account + + entityId: governanceLabelsDialog.fileId + } + + ColumnLayout { + anchors.fill: parent + anchors.leftMargin: 10 + anchors.rightMargin: 10 + anchors.bottomMargin: 5 + anchors.topMargin: 10 + spacing: 15 + z: 2 + + Button { + text: 'Apply governance label' + onClicked: applyGovernanceLabel.start() + } + + Button { + text: 'Delete governance label' + onClicked: deleteGovernanceLabel.start() + } + + Button { + text: 'Get available governance labels for sensitivity' + onClicked: getAvailableGovernanceLabelsForSensitivity.start() + } + + Button { + text: 'Get available governance labels for retention' + onClicked: getAvailableGovernanceLabelsForRetention.start() + } + + Button { + text: 'Get available governance labels for legal hold' + onClicked: getAvailableGovernanceLabelsForHold.start() + } + + Button { + text: 'Get governance labels' + onClicked: getGovernanceLabels.start() + } + + DialogButtonBox { + Layout.fillWidth: true + + Button { + text: qsTr("Close") + DialogButtonBox.buttonRole: DialogButtonBox.AcceptRole + } + + onAccepted: function() { + Systray.destroyDialog(governanceLabelsDialog) + } + + onRejected: function() { + Systray.destroyDialog(governanceLabelsDialog) + } + } + } + + Rectangle { + color: palette.base + anchors.fill: parent + z: 1 + } +} diff --git a/src/gui/application.cpp b/src/gui/application.cpp index 3f508659ba576..e8b7fe216c7f0 100644 --- a/src/gui/application.cpp +++ b/src/gui/application.cpp @@ -454,6 +454,9 @@ Application::Application(int &argc, char **argv) connect(FolderMan::instance()->socketApi(), &SocketApi::shareCommandReceived, _gui.data(), &ownCloudGui::slotShowShareDialog); + connect(FolderMan::instance()->socketApi(), &SocketApi::governanceLabelsCommandReceived, + _gui.data(), &ownCloudGui::slotShowGovernanceLabelsDialog); + connect(FolderMan::instance()->socketApi(), &SocketApi::fileActivityCommandReceived, _gui.data(), &ownCloudGui::slotShowFileActivityDialog); diff --git a/src/gui/governance/applygovernancelabel.cpp b/src/gui/governance/applygovernancelabel.cpp index 0b5f3f6d63fdb..09cea422a615f 100644 --- a/src/gui/governance/applygovernancelabel.cpp +++ b/src/gui/governance/applygovernancelabel.cpp @@ -10,8 +10,8 @@ namespace OCC { -ApplyGovernanceLabel::ApplyGovernanceLabel(AccountPtr account, QObject *parent) - : OCC::TypedWithLabelIdGovernanceNetworkJob{account, parent} +ApplyGovernanceLabel::ApplyGovernanceLabel(QObject *parent) + : OCC::TypedWithLabelIdGovernanceNetworkJob{parent} { } @@ -33,6 +33,9 @@ void ApplyGovernanceLabel::jobDone(QJsonDocument reply, int statusCode) Q_UNUSED(reply) Q_UNUSED(statusCode) + qCInfo(lcGovernance) << reply; + + Q_EMIT finished(); } diff --git a/src/gui/governance/applygovernancelabel.h b/src/gui/governance/applygovernancelabel.h index c118e9756ab19..a82423a432d6c 100644 --- a/src/gui/governance/applygovernancelabel.h +++ b/src/gui/governance/applygovernancelabel.h @@ -20,8 +20,7 @@ class ApplyGovernanceLabel : public OCC::TypedWithLabelIdGovernanceNetworkJob Q_OBJECT QML_ELEMENT public: - explicit ApplyGovernanceLabel(AccountPtr account, - QObject *parent = nullptr); + explicit ApplyGovernanceLabel(QObject *parent = nullptr); public Q_SLOTS: void start(); diff --git a/src/gui/governance/deletegovernancelabel.cpp b/src/gui/governance/deletegovernancelabel.cpp index 558ed5e294442..31b92dc25413a 100644 --- a/src/gui/governance/deletegovernancelabel.cpp +++ b/src/gui/governance/deletegovernancelabel.cpp @@ -10,8 +10,8 @@ namespace OCC { -DeleteGovernanceLabel::DeleteGovernanceLabel(AccountPtr account, QObject *parent) - : OCC::TypedWithLabelIdGovernanceNetworkJob{account, parent} +DeleteGovernanceLabel::DeleteGovernanceLabel(QObject *parent) + : OCC::TypedWithLabelIdGovernanceNetworkJob{parent} { } @@ -33,6 +33,9 @@ void DeleteGovernanceLabel::jobDone(QJsonDocument reply, int statusCode) Q_UNUSED(reply) Q_UNUSED(statusCode) + qCInfo(lcGovernance) << reply; + + Q_EMIT finished(); } diff --git a/src/gui/governance/deletegovernancelabel.h b/src/gui/governance/deletegovernancelabel.h index d9591d9ccfb5b..938c70d2d3ac9 100644 --- a/src/gui/governance/deletegovernancelabel.h +++ b/src/gui/governance/deletegovernancelabel.h @@ -20,8 +20,7 @@ class DeleteGovernanceLabel : public OCC::TypedWithLabelIdGovernanceNetworkJob Q_OBJECT QML_ELEMENT public: - explicit DeleteGovernanceLabel(AccountPtr account, - QObject *parent = nullptr); + explicit DeleteGovernanceLabel(QObject *parent = nullptr); public Q_SLOTS: void start(); diff --git a/src/gui/governance/getavailablegovernancelabels.cpp b/src/gui/governance/getavailablegovernancelabels.cpp index 0b713a45fc12a..401f5bbe7fb34 100644 --- a/src/gui/governance/getavailablegovernancelabels.cpp +++ b/src/gui/governance/getavailablegovernancelabels.cpp @@ -12,8 +12,8 @@ using namespace Qt::StringLiterals; namespace OCC { -GetAvailableGovernanceLabels::GetAvailableGovernanceLabels(AccountPtr account, QObject *parent) - : OCC::TypedGovernanceNetworkJob{account, parent} +GetAvailableGovernanceLabels::GetAvailableGovernanceLabels(QObject *parent) + : OCC::TypedGovernanceNetworkJob{parent} { } @@ -35,6 +35,8 @@ void GetAvailableGovernanceLabels::jobDone(QJsonDocument reply, int statusCode) Q_UNUSED(reply) Q_UNUSED(statusCode) + qCInfo(lcGovernance) << reply; + Q_EMIT finished(); } diff --git a/src/gui/governance/getavailablegovernancelabels.h b/src/gui/governance/getavailablegovernancelabels.h index 0d3032d86238f..29a2cab6a2a47 100644 --- a/src/gui/governance/getavailablegovernancelabels.h +++ b/src/gui/governance/getavailablegovernancelabels.h @@ -21,8 +21,7 @@ class GetAvailableGovernanceLabels : public OCC::TypedGovernanceNetworkJob QML_ELEMENT public: - explicit GetAvailableGovernanceLabels(AccountPtr account, - QObject *parent = nullptr); + explicit GetAvailableGovernanceLabels(QObject *parent = nullptr); Q_SIGNALS: diff --git a/src/gui/governance/getgovernancelabels.cpp b/src/gui/governance/getgovernancelabels.cpp index 26ac486d1463c..20b75ff9101ae 100644 --- a/src/gui/governance/getgovernancelabels.cpp +++ b/src/gui/governance/getgovernancelabels.cpp @@ -10,8 +10,8 @@ namespace OCC { -GetGovernanceLabels::GetGovernanceLabels(AccountPtr account, QObject *parent) - : OCC::GovernanceNetworkJob{account, parent} +GetGovernanceLabels::GetGovernanceLabels(QObject *parent) + : OCC::GovernanceNetworkJob{parent} { } @@ -33,6 +33,9 @@ void GetGovernanceLabels::jobDone(QJsonDocument reply, int statusCode) Q_UNUSED(reply) Q_UNUSED(statusCode) + qCInfo(lcGovernance) << reply; + + Q_EMIT finished(); } diff --git a/src/gui/governance/getgovernancelabels.h b/src/gui/governance/getgovernancelabels.h index a389aba6671a9..be08db2d4be51 100644 --- a/src/gui/governance/getgovernancelabels.h +++ b/src/gui/governance/getgovernancelabels.h @@ -20,8 +20,7 @@ class GetGovernanceLabels : public OCC::GovernanceNetworkJob Q_OBJECT QML_ELEMENT public: - explicit GetGovernanceLabels(AccountPtr account, - QObject *parent = nullptr); + explicit GetGovernanceLabels(QObject *parent = nullptr); public Q_SLOTS: void start(); diff --git a/src/gui/governance/governancenetworkjob.cpp b/src/gui/governance/governancenetworkjob.cpp index 9515d93091d37..f39c23e9309fe 100644 --- a/src/gui/governance/governancenetworkjob.cpp +++ b/src/gui/governance/governancenetworkjob.cpp @@ -5,14 +5,15 @@ #include "governancenetworkjob.h" +Q_LOGGING_CATEGORY(lcGovernance, "nextcloud.gui.governance", QtInfoMsg) + using namespace Qt::StringLiterals; namespace OCC { -GovernanceNetworkJob::GovernanceNetworkJob(AccountPtr account, QObject *parent) +GovernanceNetworkJob::GovernanceNetworkJob(QObject *parent) : QObject{parent} - , _account{account} { } @@ -115,4 +116,14 @@ QString GovernanceNetworkJob::entityTypeAsString() const return result; } +void GovernanceNetworkJob::setAccount(AccountPtr newAccount) +{ + if (_account == newAccount) { + return; + } + + _account = newAccount; + Q_EMIT accountChanged(); +} + } // namespace OCC diff --git a/src/gui/governance/governancenetworkjob.h b/src/gui/governance/governancenetworkjob.h index a4368cdba49e5..a5bce972cd79c 100644 --- a/src/gui/governance/governancenetworkjob.h +++ b/src/gui/governance/governancenetworkjob.h @@ -10,6 +10,10 @@ #include #include +#include + +Q_DECLARE_LOGGING_CATEGORY(lcGovernance) + namespace OCC { @@ -21,6 +25,8 @@ class GovernanceNetworkJob : public QObject Q_OBJECT QML_ELEMENT + Q_PROPERTY(AccountPtr account READ account WRITE setAccount NOTIFY accountChanged FINAL) + Q_PROPERTY(ApiVersion apiVersion READ apiVersion WRITE setApiVersion NOTIFY apiVersionChanged FINAL) Q_PROPERTY(EntityType entityType READ entityType WRITE setEntityType NOTIFY entityTypeChanged FINAL) @@ -52,8 +58,7 @@ class GovernanceNetworkJob : public QObject Q_ENUM(ApiVersion) - explicit GovernanceNetworkJob(AccountPtr account, - QObject *parent = nullptr); + explicit GovernanceNetworkJob(QObject *parent = nullptr); [[nodiscard]] ApiVersion apiVersion() const; @@ -71,6 +76,8 @@ class GovernanceNetworkJob : public QObject void setEntityId(const QString &newEntityId); + void setAccount(AccountPtr newAccount); + Q_SIGNALS: void apiVersionChanged(); @@ -82,6 +89,8 @@ class GovernanceNetworkJob : public QObject void finished(); + void accountChanged(); + protected: void setOcsGovernanceJob(QPointer newJob) { diff --git a/src/gui/governance/typedgovernancenetworkjob.cpp b/src/gui/governance/typedgovernancenetworkjob.cpp index 71a76d5f1c01c..5cb3304ef04dd 100644 --- a/src/gui/governance/typedgovernancenetworkjob.cpp +++ b/src/gui/governance/typedgovernancenetworkjob.cpp @@ -10,8 +10,8 @@ using namespace Qt::StringLiterals; namespace OCC { -TypedGovernanceNetworkJob::TypedGovernanceNetworkJob(AccountPtr account, QObject *parent) - : OCC::GovernanceNetworkJob{account, parent} +TypedGovernanceNetworkJob::TypedGovernanceNetworkJob(QObject *parent) + : OCC::GovernanceNetworkJob{parent} { } diff --git a/src/gui/governance/typedgovernancenetworkjob.h b/src/gui/governance/typedgovernancenetworkjob.h index f0b3253b075e2..5c7464ef776a1 100644 --- a/src/gui/governance/typedgovernancenetworkjob.h +++ b/src/gui/governance/typedgovernancenetworkjob.h @@ -21,8 +21,7 @@ class TypedGovernanceNetworkJob : public GovernanceNetworkJob Q_PROPERTY(LabelType labelType READ labelType WRITE setLabelType NOTIFY labelTypeChanged FINAL) public: - TypedGovernanceNetworkJob(AccountPtr account, - QObject *parent = nullptr); + TypedGovernanceNetworkJob(QObject *parent = nullptr); [[nodiscard]] LabelType labelType() const; diff --git a/src/gui/governance/typedwithlabelidgovernancenetworkjob.cpp b/src/gui/governance/typedwithlabelidgovernancenetworkjob.cpp index 6b777d27f664c..7f83d373e4cce 100644 --- a/src/gui/governance/typedwithlabelidgovernancenetworkjob.cpp +++ b/src/gui/governance/typedwithlabelidgovernancenetworkjob.cpp @@ -10,9 +10,8 @@ using namespace Qt::StringLiterals; namespace OCC { -TypedWithLabelIdGovernanceNetworkJob::TypedWithLabelIdGovernanceNetworkJob(AccountPtr account, - QObject *parent) - : OCC::TypedGovernanceNetworkJob{account, parent} +TypedWithLabelIdGovernanceNetworkJob::TypedWithLabelIdGovernanceNetworkJob(QObject *parent) + : OCC::TypedGovernanceNetworkJob{parent} { } @@ -33,7 +32,7 @@ void TypedWithLabelIdGovernanceNetworkJob::setLabelId(const QString &newLabelId) QString TypedWithLabelIdGovernanceNetworkJob::buildPath() const { - return u"/ocs/v2.php/apps/governance/%1/labels/%2/%3/%4/%5"_s.arg(apiVersionAsString(), entityTypeAsString(), entityId(), labelId()); + return u"/ocs/v2.php/apps/governance/%1/labels/%2/%3/%4/%5"_s.arg(apiVersionAsString(), entityTypeAsString(), entityId(), labelId(), labelTypeAsString()); } } // namespace OCC diff --git a/src/gui/governance/typedwithlabelidgovernancenetworkjob.h b/src/gui/governance/typedwithlabelidgovernancenetworkjob.h index fc4c997eda756..785ddd7297230 100644 --- a/src/gui/governance/typedwithlabelidgovernancenetworkjob.h +++ b/src/gui/governance/typedwithlabelidgovernancenetworkjob.h @@ -21,8 +21,7 @@ class TypedWithLabelIdGovernanceNetworkJob : public OCC::TypedGovernanceNetworkJ Q_PROPERTY(QString labelId READ labelId WRITE setLabelId NOTIFY labelIdChanged FINAL) public: - explicit TypedWithLabelIdGovernanceNetworkJob(AccountPtr account, - QObject *parent = nullptr); + explicit TypedWithLabelIdGovernanceNetworkJob(QObject *parent = nullptr); [[nodiscard]] QString labelId() const; diff --git a/src/gui/owncloudgui.cpp b/src/gui/owncloudgui.cpp index 1fcda11fb55b9..a1131b3105f71 100644 --- a/src/gui/owncloudgui.cpp +++ b/src/gui/owncloudgui.cpp @@ -36,6 +36,10 @@ #include "tray/syncstatussummary.h" #include "tray/unifiedsearchresultslistmodel.h" #include "integration/fileactionsmodel.h" +#include "governance/applygovernancelabel.h" +#include "governance/deletegovernancelabel.h" +#include "governance/getavailablegovernancelabels.h" +#include "governance/getgovernancelabels.h" #include "filesystem.h" #ifdef WITH_LIBCLOUDPROVIDERS @@ -152,6 +156,10 @@ ownCloudGui::ownCloudGui(Application *parent) qmlRegisterType("com.nextcloud.desktopclient", 1, 0, "SyncConflictsModel"); qmlRegisterType("com.nextcloud.desktopclient", 1, 0, "FileActionsModel"); qmlRegisterType("com.nextcloud.desktopclient", 1, 0, "AccountWizardController"); + qmlRegisterType("com.nextcloud.desktopclient", 1, 0, "ApplyGovernanceLabel"); + qmlRegisterType("com.nextcloud.desktopclient", 1, 0, "DeleteGovernanceLabel"); + qmlRegisterType("com.nextcloud.desktopclient", 1, 0, "GetAvailableGovernanceLabels"); + qmlRegisterType("com.nextcloud.desktopclient", 1, 0, "GetGovernanceLabels"); qmlRegisterUncreatableType("com.nextcloud.desktopclient", 1, 0, "QAbstractItemModel", "QAbstractItemModel"); qmlRegisterUncreatableType("com.nextcloud.desktopclient", 1, 0, "activity", "Activity"); @@ -160,6 +168,9 @@ ownCloudGui::ownCloudGui(Application *parent) qmlRegisterUncreatableType("com.nextcloud.desktopclient", 1, 0, "userStatus", "Access to Status enum"); qmlRegisterUncreatableType("com.nextcloud.desktopclient", 1, 0, "sharee", "Access to Type enum"); qmlRegisterUncreatableType("com.nextcloud.desktopclient", 1, 0, "ClientSideEncryptionTokenSelector", "Access to the certificate selector"); + qmlRegisterUncreatableType("com.nextcloud.desktopclient", 1, 0, "GovernanceNetworkJob", "base abstract type for governance labels"); + qmlRegisterUncreatableType("com.nextcloud.desktopclient", 1, 0, "TypedGovernanceNetworkJob", "base abstract type for governance labels"); + qmlRegisterUncreatableType("com.nextcloud.desktopclient", 1, 0, "TypedWithLabelIdGovernanceNetworkJob", "base abstract type for governance labels"); qRegisterMetaType("ActivityListModel*"); qRegisterMetaType("UnifiedSearchResultsListModel*"); @@ -776,6 +787,13 @@ void ownCloudGui::slotShowShareDialog(const QString &localPath) const _tray->createShareDialog(localPath); } +void ownCloudGui::slotShowGovernanceLabelsDialog(AccountPtr account, + const QString &localPath, + const QString &fileId) const +{ + _tray->createGovernanceLabelsDialog(account, localPath, fileId); +} + void ownCloudGui::slotShowFileActivityDialog(const QString &localPath) const { _tray->createFileActivityDialog(localPath); diff --git a/src/gui/owncloudgui.h b/src/gui/owncloudgui.h index 9121f205bdd21..46429bdf41588 100644 --- a/src/gui/owncloudgui.h +++ b/src/gui/owncloudgui.h @@ -97,6 +97,9 @@ public slots: * to the folder). */ void slotShowShareDialog(const QString &localPath) const; + void slotShowGovernanceLabelsDialog(AccountPtr account, + const QString &localPath, + const QString &fileId) const; void slotShowFileActivityDialog(const QString &localPath) const; void slotShowFileActionsDialog(const QString &localPath) const; #ifdef BUILD_FILE_PROVIDER_MODULE diff --git a/src/gui/socketapi/socketapi.cpp b/src/gui/socketapi/socketapi.cpp index 496a2a4d10131..84419a78ffb54 100644 --- a/src/gui/socketapi/socketapi.cpp +++ b/src/gui/socketapi/socketapi.cpp @@ -774,6 +774,23 @@ void SocketApi::command_FILE_ACTIONS(const QString &localFile, SocketListener *l processFileActionsRequest(localFile); } +void SocketApi::command_FILES_GOVERNANCE_LABELS(const QString &localFile, SocketListener *listener) +{ + Q_UNUSED(listener); + + auto fileData = FileData::get(localFile); + if (!fileData.folder) { + qCWarning(lcSocketApi) << "Unknown path" << localFile; + return; + } + + auto record = fileData.journalRecord(); + if (!record.isValid()) + return; + + emit governanceLabelsCommandReceived(fileData.folder->accountState()->account(), fileData.localPath, QString::fromLatin1(record._fileId)); +} + // don't pull the share manager into socketapi unittests #ifndef OWNCLOUD_TEST @@ -1275,6 +1292,15 @@ void SocketApi::sendLockFileInfoMenuEntries(const QFileInfo &fileInfo, } } +void SocketApi::sendFilesGovernanceLabelsMenuOptions(const QFileInfo &fileInfo, + const FileData &fileData, + SocketListener *listener) +{ + if (!FileSystem::isDir(fileInfo.absoluteFilePath()) && fileData.folder->accountState()->account()->capabilities().governanceAvailable()) { + listener->sendMessage(QLatin1String("MENU_ITEM:FILES_GOVERNANCE_LABELS::") + tr("Apply labels")); + } +} + SocketApi::FileData SocketApi::FileData::get(const QString &localFile) { FileData data; @@ -1393,6 +1419,7 @@ void SocketApi::command_GET_MENU_ITEMS(const QString &argument, OCC::SocketListe const auto rootE2eeFolderFlag = isE2eEncryptedRootFolder ? SharingContextItemRootEncryptedFolderFlag::RootEncryptedFolder : SharingContextItemRootEncryptedFolderFlag::NonRootEncryptedFolder; sendSharingContextMenuOptions(fileData, listener, itemEncryptionFlag, rootE2eeFolderFlag); sendFileActionsContextMenuOptions(fileData, listener); + sendFilesGovernanceLabelsMenuOptions(fileInfo, fileData, listener); // Conflict files get conflict resolution actions bool isConflict = Utility::isConflictFile(fileData.folderRelativePath); diff --git a/src/gui/socketapi/socketapi.h b/src/gui/socketapi/socketapi.h index 41758c28c5827..b0a08ecaa328b 100644 --- a/src/gui/socketapi/socketapi.h +++ b/src/gui/socketapi/socketapi.h @@ -10,6 +10,7 @@ #include "common/syncfilestatus.h" #include "common/syncjournalfilerecord.h" #include "syncfileitem.h" +#include "accountfwd.h" #include "config.h" @@ -72,6 +73,7 @@ public slots: void shareCommandReceived(const QString &localPath); void fileActivityCommandReceived(const QString &localPath); void fileActionsCommandReceived(const QString &localPath); + void governanceLabelsCommandReceived(OCC::AccountPtr account, const QString &filePath, const QString &fileId); private slots: void slotNewConnection(); @@ -140,6 +142,7 @@ private slots: Q_INVOKABLE void command_LOCK_FILE(const QString &localFile, OCC::SocketListener *listener); Q_INVOKABLE void command_UNLOCK_FILE(const QString &localFile, OCC::SocketListener *listener); Q_INVOKABLE void command_FILE_ACTIONS(const QString &localFile, OCC::SocketListener *listener); + Q_INVOKABLE void command_FILES_GOVERNANCE_LABELS(const QString &localFile, OCC::SocketListener *listener); void setFileLock(const QString &localFile, const SyncFileItem::LockStatus lockState) const; @@ -168,6 +171,8 @@ private slots: const SocketListener* const listener, const SyncJournalFileRecord &record) const; + void sendFilesGovernanceLabelsMenuOptions(const QFileInfo &fileInfo, const FileData &fileData, SocketListener *listener); + /** Send the list of menu item. (added in version 1.1) * argument is a list of files for which the menu should be shown, separated by '\x1e' * Reply with GET_MENU_ITEMS:BEGIN diff --git a/src/gui/systray.cpp b/src/gui/systray.cpp index d9641b09be0fb..6e05d17c37f65 100644 --- a/src/gui/systray.cpp +++ b/src/gui/systray.cpp @@ -393,6 +393,37 @@ void Systray::createResolveConflictsDialog(const OCC::ActivityList &allConflicts dialogWindow->requestActivate(); } +void Systray::createGovernanceLabelsDialog(AccountPtr account, const QString &fileName, const QString &fileId) +{ + const auto conflictsDialog = std::make_unique(trayEngine(), QStringLiteral("qrc:/qml/src/gui/GovernanceLabelsDialog.qml")); + const QVariantMap initialProperties{ + {"fileName", QVariant::fromValue(fileName)}, + {"account", QVariant::fromValue(account)}, + {"fileId", QVariant::fromValue(fileId)}, + }; + + if(conflictsDialog->isError()) { + qCWarning(lcSystray) << conflictsDialog->errorString(); + return; + } + + // This call dialog gets deallocated on close conditions + // by a call from the QML side to the destroyDialog slot + auto dialog = std::unique_ptr(conflictsDialog->createWithInitialProperties(initialProperties)); + if (!dialog) { + return; + } + dialog->setParent(QGuiApplication::instance()); + + auto dialogWindow = qobject_cast(dialog.release()); + if (!dialogWindow) { + return; + } + dialogWindow->show(); + dialogWindow->raise(); + dialogWindow->requestActivate(); +} + void Systray::createEncryptionTokenDiscoveryDialog() { if (_encryptionTokenDiscoveryDialog) { diff --git a/src/gui/systray.h b/src/gui/systray.h index 15ecfb1998b4c..38237fc0a4426 100644 --- a/src/gui/systray.h +++ b/src/gui/systray.h @@ -130,6 +130,7 @@ public slots: void createEditFileLocallyLoadingDialog(const QString &fileName); void destroyEditFileLocallyLoadingDialog(); void createResolveConflictsDialog(const OCC::ActivityList &allConflicts); + void createGovernanceLabelsDialog(AccountPtr account, const QString &fileName, const QString &fileId); void createEncryptionTokenDiscoveryDialog(); void destroyEncryptionTokenDiscoveryDialog(); diff --git a/src/gui/wizard/qml/AccountWizardWindow.qml b/src/gui/wizard/qml/AccountWizardWindow.qml index b76f192c948cb..aa22ed9216d9c 100644 --- a/src/gui/wizard/qml/AccountWizardWindow.qml +++ b/src/gui/wizard/qml/AccountWizardWindow.qml @@ -8,6 +8,7 @@ import QtQuick.Controls import QtQuick.Layouts import QtQuick.Window import com.nextcloud.desktopclient +import "../../tray" import Style import "../../tray" From 95c96fb6eeacdfabc0362b34429238c00e96a882 Mon Sep 17 00:00:00 2001 From: Matthieu Gallien Date: Thu, 11 Jun 2026 17:05:56 +0200 Subject: [PATCH 06/11] feat(governance): add automated tesst for network requests should enable us to test the requests one by one and ensure we decode properly the replies Signed-off-by: Matthieu Gallien --- src/gui/governance/applygovernancelabel.cpp | 12 +- src/gui/governance/deletegovernancelabel.cpp | 12 +- .../getavailablegovernancelabels.cpp | 11 +- src/gui/governance/getgovernancelabels.cpp | 12 +- src/gui/governance/governancenetworkjob.h | 5 +- test/CMakeLists.txt | 1 + test/testgovernance.cpp | 529 ++++++++++++++++++ 7 files changed, 550 insertions(+), 32 deletions(-) create mode 100644 test/testgovernance.cpp diff --git a/src/gui/governance/applygovernancelabel.cpp b/src/gui/governance/applygovernancelabel.cpp index 09cea422a615f..9cc5ff341effc 100644 --- a/src/gui/governance/applygovernancelabel.cpp +++ b/src/gui/governance/applygovernancelabel.cpp @@ -21,6 +21,8 @@ void ApplyGovernanceLabel::start() connect(ocsGovernanceJob().data(), &OcsJob::jobFinished, this, &ApplyGovernanceLabel::jobDone); + connect(ocsGovernanceJob().data(), &OcsJob::ocsError, + this, &ApplyGovernanceLabel::finishedWitherror); ocsGovernanceJob()->setPath(buildPath()); ocsGovernanceJob()->setMethod("POST"); @@ -28,15 +30,9 @@ void ApplyGovernanceLabel::start() ocsGovernanceJob()->start(); } -void ApplyGovernanceLabel::jobDone(QJsonDocument reply, int statusCode) +void ApplyGovernanceLabel::jobDone(QJsonDocument reply, [[maybe_unused]] int statusCode) { - Q_UNUSED(reply) - Q_UNUSED(statusCode) - - qCInfo(lcGovernance) << reply; - - - Q_EMIT finished(); + Q_EMIT finished(reply); } } // namespace OCC diff --git a/src/gui/governance/deletegovernancelabel.cpp b/src/gui/governance/deletegovernancelabel.cpp index 31b92dc25413a..72804644dbb08 100644 --- a/src/gui/governance/deletegovernancelabel.cpp +++ b/src/gui/governance/deletegovernancelabel.cpp @@ -21,6 +21,8 @@ void DeleteGovernanceLabel::start() connect(ocsGovernanceJob().data(), &OcsJob::jobFinished, this, &DeleteGovernanceLabel::jobDone); + connect(ocsGovernanceJob().data(), &OcsJob::ocsError, + this, &DeleteGovernanceLabel::finishedWitherror); ocsGovernanceJob()->setPath(buildPath()); ocsGovernanceJob()->setMethod("DELETE"); @@ -28,15 +30,9 @@ void DeleteGovernanceLabel::start() ocsGovernanceJob()->start(); } -void DeleteGovernanceLabel::jobDone(QJsonDocument reply, int statusCode) +void DeleteGovernanceLabel::jobDone(QJsonDocument reply, [[maybe_unused]] int statusCode) { - Q_UNUSED(reply) - Q_UNUSED(statusCode) - - qCInfo(lcGovernance) << reply; - - - Q_EMIT finished(); + Q_EMIT finished(reply); } } // namespace OCC diff --git a/src/gui/governance/getavailablegovernancelabels.cpp b/src/gui/governance/getavailablegovernancelabels.cpp index 401f5bbe7fb34..1e2c7bbd2fc56 100644 --- a/src/gui/governance/getavailablegovernancelabels.cpp +++ b/src/gui/governance/getavailablegovernancelabels.cpp @@ -23,6 +23,8 @@ void GetAvailableGovernanceLabels::start() connect(ocsGovernanceJob().data(), &OcsJob::jobFinished, this, &GetAvailableGovernanceLabels::jobDone); + connect(ocsGovernanceJob().data(), &OcsJob::ocsError, + this, &GetAvailableGovernanceLabels::finishedWitherror); ocsGovernanceJob()->setPath(buildPath()); ocsGovernanceJob()->setMethod("GET"); @@ -30,14 +32,9 @@ void GetAvailableGovernanceLabels::start() ocsGovernanceJob()->start(); } -void GetAvailableGovernanceLabels::jobDone(QJsonDocument reply, int statusCode) +void GetAvailableGovernanceLabels::jobDone(QJsonDocument reply, [[maybe_unused]] int statusCode) { - Q_UNUSED(reply) - Q_UNUSED(statusCode) - - qCInfo(lcGovernance) << reply; - - Q_EMIT finished(); + Q_EMIT finished(reply); } QString GetAvailableGovernanceLabels::buildPath() const diff --git a/src/gui/governance/getgovernancelabels.cpp b/src/gui/governance/getgovernancelabels.cpp index 20b75ff9101ae..c442fd7698259 100644 --- a/src/gui/governance/getgovernancelabels.cpp +++ b/src/gui/governance/getgovernancelabels.cpp @@ -21,6 +21,8 @@ void GetGovernanceLabels::start() connect(ocsGovernanceJob().data(), &OcsJob::jobFinished, this, &GetGovernanceLabels::jobDone); + connect(ocsGovernanceJob().data(), &OcsJob::ocsError, + this, &GetGovernanceLabels::finishedWitherror); ocsGovernanceJob()->setPath(buildPath()); ocsGovernanceJob()->setMethod("GET"); @@ -28,15 +30,9 @@ void GetGovernanceLabels::start() ocsGovernanceJob()->start(); } -void GetGovernanceLabels::jobDone(QJsonDocument reply, int statusCode) +void GetGovernanceLabels::jobDone(QJsonDocument reply, [[maybe_unused]] int statusCode) { - Q_UNUSED(reply) - Q_UNUSED(statusCode) - - qCInfo(lcGovernance) << reply; - - - Q_EMIT finished(); + Q_EMIT finished(reply); } } // namespace OCC diff --git a/src/gui/governance/governancenetworkjob.h b/src/gui/governance/governancenetworkjob.h index a5bce972cd79c..f20ffd5e08ddb 100644 --- a/src/gui/governance/governancenetworkjob.h +++ b/src/gui/governance/governancenetworkjob.h @@ -11,6 +11,7 @@ #include #include #include +#include Q_DECLARE_LOGGING_CATEGORY(lcGovernance) @@ -87,7 +88,9 @@ class GovernanceNetworkJob : public QObject void entityIdChanged(); - void finished(); + void finished(QJsonDocument reply); + + void finishedWitherror(int errorCode, const QString &errorMessage); void accountChanged(); diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index cc5f9a3b72a7a..73c31a861a4d2 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -77,6 +77,7 @@ nextcloud_add_test(AllFilesDeleted) nextcloud_add_test(Blacklist) nextcloud_add_test(LocalDiscovery) nextcloud_add_test(RemoteDiscovery) +nextcloud_add_test(Governance) if (NOT APPLE) nextcloud_add_test(Permissions) diff --git a/test/testgovernance.cpp b/test/testgovernance.cpp new file mode 100644 index 0000000000000..42a50badd6154 --- /dev/null +++ b/test/testgovernance.cpp @@ -0,0 +1,529 @@ +/* + * SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: GPL-2.0-or-later + * + * This software is in the public domain, furnished "as is", without technical + * support, and with no warranty, express or implied, as to its usefulness for + * any purpose. + */ + +#include "syncenginetestutils.h" + +#include "governance/applygovernancelabel.h" +#include "governance/deletegovernancelabel.h" +#include "governance/getavailablegovernancelabels.h" +#include "governance/getgovernancelabels.h" + +using namespace OCC; +using namespace Qt::StringLiterals; + +class GovernanceTestHelper : public QObject +{ + Q_OBJECT + +public: + GovernanceTestHelper(QObject *parent = nullptr) + : QObject{parent} + { + } + +Q_SIGNALS: + void setupSucceeded(); + +public slots: + void setup(FakeFolder &fakeFolder) + { + fakeFolder.setServerOverride([this] (FakeQNAM::Operation operation, const QNetworkRequest &request, [[maybe_unused]] QIODevice *device) -> QNetworkReply* + { + const auto requestPathString = request.url().path(); + const auto requestPath = QStringView{requestPathString}; + const auto routeIndex = requestPath.indexOf(u"/ocs/v2.php/apps/governance/v1/labels/"_s); + if (routeIndex == -1) { + return nullptr; + } + + const auto governanceRequestParameters = requestPath.mid(routeIndex + u"/ocs/v2.php/apps/governance/v1/labels/"_s.size()).split(u"/"_s); + qDebug() << requestPath << routeIndex << governanceRequestParameters << operation; + + switch (operation) + { + case QNetworkAccessManager::CustomOperation: + if (request.attribute(QNetworkRequest::CustomVerbAttribute).toString() == u"DELETE"_s) { + return new FakePayloadReply{operation, request, fakeDeleteGovernanceLabelReply(governanceRequestParameters).toUtf8(), this}; + } else if (request.attribute(QNetworkRequest::CustomVerbAttribute).toString() == u"GET"_s) { + if (governanceRequestParameters.count() == 4 && governanceRequestParameters.at(3) == u"available"_s) { + return new FakePayloadReply{operation, request, fakeGetAvailableGovernanceLabelsReply(governanceRequestParameters).toUtf8(), this}; + } else if (governanceRequestParameters.count() == 2) { + return new FakePayloadReply{operation, request, fakeGetGovernanceLabelsReply(governanceRequestParameters).toUtf8(), this}; + } + } else if (request.attribute(QNetworkRequest::CustomVerbAttribute).toString() == u"POST"_s) { + return new FakePayloadReply{operation, request, fakeApplyGovernanceLabelReply(governanceRequestParameters).toUtf8(), this}; + } + break; + case QNetworkAccessManager::HeadOperation: + break; + case QNetworkAccessManager::GetOperation: + if (governanceRequestParameters.count() == 4 && governanceRequestParameters.at(3) == u"available"_s) { + return new FakePayloadReply{operation, request, fakeGetAvailableGovernanceLabelsReply(governanceRequestParameters).toUtf8(), this}; + } else if (governanceRequestParameters.count() == 2) { + return new FakePayloadReply{operation, request, fakeGetGovernanceLabelsReply(governanceRequestParameters).toUtf8(), this}; + } + break; + case QNetworkAccessManager::PutOperation: + break; + case QNetworkAccessManager::PostOperation: + return new FakePayloadReply{operation, request, fakeApplyGovernanceLabelReply(governanceRequestParameters).toUtf8(), this}; + break; + case QNetworkAccessManager::DeleteOperation: + return new FakePayloadReply{operation, request, fakeDeleteGovernanceLabelReply(governanceRequestParameters).toUtf8(), this}; + break; + case QNetworkAccessManager::UnknownOperation: + break; + } + + return nullptr; + }); + + Q_EMIT setupSucceeded(); + } + +private: + [[nodiscard]] QString fakeGetGovernanceLabelsReply(const QList ¶meters) const + { + qDebug() << parameters; + if (parameters.at(0) == u"FILES"_s && parameters.at(1) == u"117"_s) { + const auto replyJson = uR"json( +{ + "ocs": { + "meta": { + "status": "ok", + "statuscode": 200, + "message": "OK" + }, + "data": { + "hold": [], + "retention": [], + "sensitivity": null + } + } +} + )json"_s; + + return replyJson; + } else if (parameters.at(0) == u"FILES"_s && parameters.at(1) == u"117117"_s) { + const auto replyJson = uR"json( +{ + "ocs": { + "meta": { + "status": "failure", + "statuscode": 404, + "message": "Entity with id 117117 not found" + }, + "data": [] + } +} + )json"_s; + + return replyJson; + } else { + const auto replyJson = uR"json( +{ + "ocs": { + "meta": { + "status": "failure", + "statuscode": 400, + "message": "Invalid entity type FILEStdytf" + }, + "data": [] + } +} + )json"_s; + + return replyJson; + } + } + + [[nodiscard]] QString fakeGetAvailableGovernanceLabelsReply(const QList ¶meters) const + { + if (parameters.at(0) == u"FILES"_s && parameters.at(1) == u"117"_s) { + const auto replyJson = uR"json( +{ + "ocs": { + "meta": { + "status": "ok", + "statuscode": 200, + "message": "OK" + }, + "data": [ + { + "id": "91785883351310337", + "name": "Test Sensitivity", + "priority": 0, + "description": "", + "color": "bf4040", + "scopes": [ + "FILES" + ] + } + ] + } +} + )json"_s; + + return replyJson; + } else if (parameters.at(0) == u"FILES"_s && parameters.at(1) == u"117117"_s) { + const auto replyJson = uR"json( +{ + "ocs": { + "meta": { + "status": "ok", + "statuscode": 200, + "message": "OK" + }, + "data": [ + { + "id": "91785883351310337", + "name": "Test Sensitivity", + "priority": 0, + "description": "", + "color": "bf4040", + "scopes": [ + "FILES" + ] + } + ] + } +} + )json"_s; + + return replyJson; + } else { + const auto replyJson = uR"json( +{ + "ocs": { + "meta": { + "status": "failure", + "statuscode": 400, + "message": "Invalid entity type FILEStdytf" + }, + "data": [] + } +} + )json"_s; + + return replyJson; + } + } + + [[nodiscard]] QString fakeApplyGovernanceLabelReply(const QList ¶meters) const + { + if (parameters.at(1) == u"117"_s) { + const auto replyJson = uR"json( +{ + "ocs": { + "meta": { + "status": "ok", + "statuscode": 200, + "message": "OK" + }, + "data": { + "hold": [], + "retention": [], + "sensitivity": null + } + } +} + })json"_s; + + return replyJson; + } else { + const auto replyJson = uR"json( +{ + "ocs": { + "meta": { + "status": "ok", + "statuscode": 200, + "message": "OK" + }, + "data": { + "hold": [], + "retention": [], + "sensitivity": null + } + } +} + })json"_s; + + return replyJson; + } + } + + [[nodiscard]] QString fakeDeleteGovernanceLabelReply(const QList ¶meters) const + { + if (parameters.at(1) == u"117"_s) { + const auto replyJson = uR"json( +{ + "ocs": { + "meta": { + "status": "ok", + "statuscode": 200, + "message": "OK" + }, + "data": { + "hold": [], + "retention": [], + "sensitivity": null + } + } +} + })json"_s; + + return replyJson; + } else { + const auto replyJson = uR"json( +{ + "ocs": { + "meta": { + "status": "ok", + "statuscode": 200, + "message": "OK" + }, + "data": { + "hold": [], + "retention": [], + "sensitivity": null + } + } +} + })json"_s; + + return replyJson; + } + } +}; + +class TestGovernance : public QObject +{ + Q_OBJECT + +private slots: + void initTestCase() + { + OCC::Logger::instance()->setLogFlush(true); + OCC::Logger::instance()->setLogDebug(true); + + QStandardPaths::setTestModeEnabled(true); + } + + void testApplyGovernanceLabel() + { + FakeFolder fakeFolder{{}}; + GovernanceTestHelper testHelper; + testHelper.setup(fakeFolder); + + ApplyGovernanceLabel myJob; + QSignalSpy finishedSpy(&myJob, &ApplyGovernanceLabel::finished); + QSignalSpy finishedWithErrorSpy(&myJob, &ApplyGovernanceLabel::finishedWitherror); + + fakeFolder.remoteModifier().insert("test.txt"); + + QVERIFY(fakeFolder.syncOnce()); + + myJob.setAccount(fakeFolder.account()); + myJob.setEntityId(u"1"_s); + myJob.setEntityType(ApplyGovernanceLabel::EntityType::Files); + myJob.setLabelType(ApplyGovernanceLabel::LabelType::Sensitivity); + myJob.setLabelId(u"1"_s); + + myJob.start(); + + finishedSpy.wait(); + QCOMPARE(finishedSpy.count(), 1); + QCOMPARE(finishedWithErrorSpy.count(), 0); + } + + void testDeleteGovernanceLabel() + { + FakeFolder fakeFolder{{}}; + GovernanceTestHelper testHelper; + testHelper.setup(fakeFolder); + + DeleteGovernanceLabel myJob; + QSignalSpy finishedSpy(&myJob, &DeleteGovernanceLabel::finished); + QSignalSpy finishedWithErrorSpy(&myJob, &DeleteGovernanceLabel::finishedWitherror); + + fakeFolder.remoteModifier().insert("test.txt"); + + QVERIFY(fakeFolder.syncOnce()); + + myJob.setAccount(fakeFolder.account()); + myJob.setEntityId(u"1"_s); + myJob.setEntityType(DeleteGovernanceLabel::EntityType::Files); + myJob.setLabelType(DeleteGovernanceLabel::LabelType::Sensitivity); + myJob.setLabelId(u"1"_s); + + myJob.start(); + + finishedSpy.wait(); + QCOMPARE(finishedSpy.count(), 1); + QCOMPARE(finishedWithErrorSpy.count(), 0); + } + + void testGetAvailableGovernanceLabels_ValidIdValidType() + { + FakeFolder fakeFolder{{}}; + GovernanceTestHelper testHelper; + testHelper.setup(fakeFolder); + + GetAvailableGovernanceLabels myJob; + QSignalSpy finishedSpy(&myJob, &GetAvailableGovernanceLabels::finished); + QSignalSpy finishedWithErrorSpy(&myJob, &GetAvailableGovernanceLabels::finishedWitherror); + + fakeFolder.remoteModifier().insert("test.txt"); + + QVERIFY(fakeFolder.syncOnce()); + + myJob.setAccount(fakeFolder.account()); + myJob.setEntityId(u"117"_s); + myJob.setEntityType(GetAvailableGovernanceLabels::EntityType::Files); + myJob.setLabelType(GetAvailableGovernanceLabels::LabelType::Sensitivity); + + myJob.start(); + + finishedSpy.wait(); + QCOMPARE(finishedSpy.count(), 1); + QCOMPARE(finishedWithErrorSpy.count(), 0); + } + + void testGetAvailableGovernanceLabels_InvalidIdValidType() + { + FakeFolder fakeFolder{{}}; + GovernanceTestHelper testHelper; + testHelper.setup(fakeFolder); + + GetAvailableGovernanceLabels myJob; + QSignalSpy finishedSpy(&myJob, &GetAvailableGovernanceLabels::finished); + QSignalSpy finishedWithErrorSpy(&myJob, &GetAvailableGovernanceLabels::finishedWitherror); + + fakeFolder.remoteModifier().insert("test.txt"); + + QVERIFY(fakeFolder.syncOnce()); + + myJob.setAccount(fakeFolder.account()); + myJob.setEntityId(u"117117"_s); + myJob.setEntityType(GetAvailableGovernanceLabels::EntityType::Files); + myJob.setLabelType(GetAvailableGovernanceLabels::LabelType::Sensitivity); + + myJob.start(); + + finishedSpy.wait(); + QCOMPARE(finishedSpy.count(), 1); + QCOMPARE(finishedWithErrorSpy.count(), 0); + } + + void testGetAvailableGovernanceLabels_ValidIdInvalidType() + { + FakeFolder fakeFolder{{}}; + GovernanceTestHelper testHelper; + testHelper.setup(fakeFolder); + + GetAvailableGovernanceLabels myJob; + QSignalSpy finishedSpy(&myJob, &GetAvailableGovernanceLabels::finished); + QSignalSpy finishedWithErrorSpy(&myJob, &GetAvailableGovernanceLabels::finishedWitherror); + + fakeFolder.remoteModifier().insert("test.txt"); + + QVERIFY(fakeFolder.syncOnce()); + + myJob.setAccount(fakeFolder.account()); + myJob.setEntityId(u"117"_s); + myJob.setEntityType(GetAvailableGovernanceLabels::EntityType::Custom); + myJob.setCustomEntityType(u"FILEStdytf"_s); + myJob.setLabelType(GetAvailableGovernanceLabels::LabelType::Sensitivity); + + myJob.start(); + + finishedSpy.wait(); + QCOMPARE(finishedSpy.count(), 0); + QCOMPARE(finishedWithErrorSpy.count(), 1); + } + + void testGetGovernanceLabels_ValidIdValidEntityType() + { + FakeFolder fakeFolder{{}}; + GovernanceTestHelper testHelper; + testHelper.setup(fakeFolder); + + GetGovernanceLabels myJob; + QSignalSpy finishedSpy(&myJob, &GetGovernanceLabels::finished); + QSignalSpy finishedWithErrorSpy(&myJob, &GetGovernanceLabels::finishedWitherror); + + fakeFolder.remoteModifier().insert("test.txt"); + + QVERIFY(fakeFolder.syncOnce()); + + myJob.setAccount(fakeFolder.account()); + myJob.setEntityId(u"117"_s); + myJob.setEntityType(GetGovernanceLabels::EntityType::Files); + + myJob.start(); + + finishedSpy.wait(); + QCOMPARE(finishedSpy.count(), 1); + QCOMPARE(finishedWithErrorSpy.count(), 0); + } + + void testGetGovernanceLabels_InvalidId() + { + FakeFolder fakeFolder{{}}; + GovernanceTestHelper testHelper; + testHelper.setup(fakeFolder); + + GetGovernanceLabels myJob; + QSignalSpy finishedSpy(&myJob, &GetGovernanceLabels::finished); + QSignalSpy finishedWithErrorSpy(&myJob, &GetGovernanceLabels::finishedWitherror); + + fakeFolder.remoteModifier().insert("test.txt"); + + QVERIFY(fakeFolder.syncOnce()); + + myJob.setAccount(fakeFolder.account()); + myJob.setEntityId(u"117117"_s); + myJob.setEntityType(GetGovernanceLabels::EntityType::Files); + + myJob.start(); + + finishedWithErrorSpy.wait(); + QCOMPARE(finishedSpy.count(), 0); + QCOMPARE(finishedWithErrorSpy.count(), 1); + } + + void testGetGovernanceLabels_ValidIdInvalidEntityType() + { + FakeFolder fakeFolder{{}}; + GovernanceTestHelper testHelper; + testHelper.setup(fakeFolder); + + GetGovernanceLabels myJob; + QSignalSpy finishedSpy(&myJob, &GetGovernanceLabels::finished); + QSignalSpy finishedWithErrorSpy(&myJob, &GetGovernanceLabels::finishedWitherror); + + fakeFolder.remoteModifier().insert("test.txt"); + + QVERIFY(fakeFolder.syncOnce()); + + myJob.setAccount(fakeFolder.account()); + myJob.setEntityId(u"117"_s); + myJob.setEntityType(GetGovernanceLabels::EntityType::Custom); + myJob.setCustomEntityType(u"FILEStdytf"_s); + + myJob.start(); + + finishedWithErrorSpy.wait(); + QCOMPARE(finishedSpy.count(), 0); + QCOMPARE(finishedWithErrorSpy.count(), 1); + } +}; + +QTEST_GUILESS_MAIN(TestGovernance) +#include "testgovernance.moc" From 83e8123fed801f3a204612f4d7fc200b2d21a916 Mon Sep 17 00:00:00 2001 From: Matthieu Gallien Date: Mon, 15 Jun 2026 09:28:47 +0200 Subject: [PATCH 07/11] feat(governance): parse governance API replies to populate the UI Signed-off-by: Matthieu Gallien --- src/gui/CMakeLists.txt | 2 ++ src/gui/governance/governancelabelinfo.cpp | 15 ++++++++++ src/gui/governance/governancelabelinfo.h | 35 ++++++++++++++++++++++ 3 files changed, 52 insertions(+) create mode 100644 src/gui/governance/governancelabelinfo.cpp create mode 100644 src/gui/governance/governancelabelinfo.h diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index 787ad0f0c162f..cacfb1b12b845 100644 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -223,6 +223,8 @@ set(client_SRCS governance/ocsgovernancejob.cpp governance/typedwithlabelidgovernancenetworkjob.h governance/typedwithlabelidgovernancenetworkjob.cpp + governance/governancelabelinfo.h + governance/governancelabelinfo.cpp tray/svgimageprovider.h tray/svgimageprovider.cpp tray/syncstatussummary.h diff --git a/src/gui/governance/governancelabelinfo.cpp b/src/gui/governance/governancelabelinfo.cpp new file mode 100644 index 0000000000000..1f6da914a0b38 --- /dev/null +++ b/src/gui/governance/governancelabelinfo.cpp @@ -0,0 +1,15 @@ +/* + * SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "governancelabelinfo.h" + +namespace OCC +{ + +GOvernanceLabelInfo::GOvernanceLabelInfo() +{ +} + +} // namespace OCC diff --git a/src/gui/governance/governancelabelinfo.h b/src/gui/governance/governancelabelinfo.h new file mode 100644 index 0000000000000..8ddf85345254f --- /dev/null +++ b/src/gui/governance/governancelabelinfo.h @@ -0,0 +1,35 @@ +/* + * SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef GOVERNANCELABELINFO_H +#define GOVERNANCELABELINFO_H + +#include +#include + +namespace OCC +{ + +struct GOvernanceLabelInfo +{ +public: + GOvernanceLabelInfo(); + + QString _id; + + QString _name; + + int _priority = -1; + + QString _description; + + QString _color; + + QStringList _scopes; +}; + +} // namespace OCC + +#endif // GOVERNANCELABELINFO_H From 736828438602441a22d75ff9827ae6e9fea695b2 Mon Sep 17 00:00:00 2001 From: Matthieu Gallien Date: Thu, 18 Jun 2026 09:01:00 +0200 Subject: [PATCH 08/11] feat(governance): makes our apply labels dialog usefull Signed-off-by: Matthieu Gallien --- src/gui/CMakeLists.txt | 3 + src/gui/GovernanceLabelsDialog.qml | 56 ++--- src/gui/governance/applygovernancelabel.cpp | 19 ++ src/gui/governance/applygovernancelabel.h | 2 + src/gui/governance/deletegovernancelabel.cpp | 19 ++ src/gui/governance/deletegovernancelabel.h | 2 + .../getavailablegovernancelabels.cpp | 26 +++ .../governance/getavailablegovernancelabels.h | 4 + src/gui/governance/getgovernancelabels.cpp | 17 ++ src/gui/governance/getgovernancelabels.h | 2 + src/gui/governance/governancelabelinfo.cpp | 4 - src/gui/governance/governancelabelinfo.h | 4 +- .../governance/governancelabelslistmodel.cpp | 192 ++++++++++++++++++ .../governance/governancelabelslistmodel.h | 82 ++++++++ src/gui/governance/governancenetworkjob.cpp | 55 ++++- src/gui/governance/governancenetworkjob.h | 46 ++--- src/gui/governance/governancetypes.h | 45 ++++ .../governance/typedgovernancenetworkjob.cpp | 29 ++- .../governance/typedgovernancenetworkjob.h | 10 +- .../typedwithlabelidgovernancenetworkjob.cpp | 16 ++ .../typedwithlabelidgovernancenetworkjob.h | 2 + src/gui/owncloudgui.cpp | 2 + test/testgovernance.cpp | 91 +++++++-- 23 files changed, 631 insertions(+), 97 deletions(-) create mode 100644 src/gui/governance/governancelabelslistmodel.cpp create mode 100644 src/gui/governance/governancelabelslistmodel.h create mode 100644 src/gui/governance/governancetypes.h diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index cacfb1b12b845..a334a2a712a4d 100644 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -225,6 +225,9 @@ set(client_SRCS governance/typedwithlabelidgovernancenetworkjob.cpp governance/governancelabelinfo.h governance/governancelabelinfo.cpp + governance/governancelabelslistmodel.h + governance/governancelabelslistmodel.cpp + governance/governancetypes.h tray/svgimageprovider.h tray/svgimageprovider.cpp tray/syncstatussummary.h diff --git a/src/gui/GovernanceLabelsDialog.qml b/src/gui/GovernanceLabelsDialog.qml index 9c1c8571a85d9..53d35e4af86ba 100644 --- a/src/gui/GovernanceLabelsDialog.qml +++ b/src/gui/GovernanceLabelsDialog.qml @@ -64,6 +64,10 @@ ApplicationWindow { labelType: GovernanceNetworkJob.Sensitivity entityId: governanceLabelsDialog.fileId + + onFinished: function(reply) { + labelsModel.setAvailableLabelsJsonData(reply) + } } GetAvailableGovernanceLabels { @@ -92,6 +96,17 @@ ApplicationWindow { entityId: governanceLabelsDialog.fileId } + GovernanceLabelsListModel { + id: labelsModel + + entityId: governanceLabelsDialog.fileId + labelType: GovernanceNetworkJob.Sensitivity + + onRefreshData: function(labelType, entityId) { + getAvailableGovernanceLabelsForSensitivity.start(labelType, entityId) + } + } + ColumnLayout { anchors.fill: parent anchors.leftMargin: 10 @@ -101,42 +116,33 @@ ApplicationWindow { spacing: 15 z: 2 - Button { - text: 'Apply governance label' - onClicked: applyGovernanceLabel.start() - } - - Button { - text: 'Delete governance label' - onClicked: deleteGovernanceLabel.start() - } + EnforcedPlainTextLabel { + text: 'Sensitivity label:' - Button { - text: 'Get available governance labels for sensitivity' - onClicked: getAvailableGovernanceLabelsForSensitivity.start() + font.pixelSize: Style.pixelSize + 2 } - Button { - text: 'Get available governance labels for retention' - onClicked: getAvailableGovernanceLabelsForRetention.start() - } + ComboBox { + id: selectedNewSensitivityLabel - Button { - text: 'Get available governance labels for legal hold' - onClicked: getAvailableGovernanceLabelsForHold.start() - } + font.pixelSize: Style.pixelSize + 2 + Accessible.role: Accessible.ComboBox + Accessible.name: qsTr("Select sensitivity label") - Button { - text: 'Get governance labels' - onClicked: getGovernanceLabels.start() + model: labelsModel } DialogButtonBox { Layout.fillWidth: true Button { - text: qsTr("Close") - DialogButtonBox.buttonRole: DialogButtonBox.AcceptRole + text: qsTr("Apply") + DialogButtonBox.buttonRole: DialogButtonBox.ApplyRole + } + + Button { + text: qsTr("Cancel") + DialogButtonBox.buttonRole: DialogButtonBox.RejectRole } onAccepted: function() { diff --git a/src/gui/governance/applygovernancelabel.cpp b/src/gui/governance/applygovernancelabel.cpp index 9cc5ff341effc..e32f306a597ad 100644 --- a/src/gui/governance/applygovernancelabel.cpp +++ b/src/gui/governance/applygovernancelabel.cpp @@ -13,10 +13,22 @@ namespace OCC ApplyGovernanceLabel::ApplyGovernanceLabel(QObject *parent) : OCC::TypedWithLabelIdGovernanceNetworkJob{parent} { + connect(this, &ApplyGovernanceLabel::apiVersionChanged, this, &ApplyGovernanceLabel::initialize); + connect(this, &ApplyGovernanceLabel::entityTypeChanged, this, &ApplyGovernanceLabel::initialize); + connect(this, &ApplyGovernanceLabel::customEntityTypeChanged, this, &ApplyGovernanceLabel::initialize); + connect(this, &ApplyGovernanceLabel::entityIdChanged, this, &ApplyGovernanceLabel::initialize); + connect(this, &ApplyGovernanceLabel::accountChanged, this, &ApplyGovernanceLabel::initialize); + connect(this, &ApplyGovernanceLabel::labelTypeChanged, this, &ApplyGovernanceLabel::initialize); + connect(this, &ApplyGovernanceLabel::labelIdChanged, this, &ApplyGovernanceLabel::initialize); } void ApplyGovernanceLabel::start() { + if (!checkParameters()) { + Q_EMIT finishedWitherror(500, {}); + return; + } + setOcsGovernanceJob(QPointer{new OcsGovernanceJob{account()}}); connect(ocsGovernanceJob().data(), &OcsJob::jobFinished, @@ -35,4 +47,11 @@ void ApplyGovernanceLabel::jobDone(QJsonDocument reply, [[maybe_unused]] int sta Q_EMIT finished(reply); } +void ApplyGovernanceLabel::initialize() +{ + if (checkParameters()) { + start(); + } +} + } // namespace OCC diff --git a/src/gui/governance/applygovernancelabel.h b/src/gui/governance/applygovernancelabel.h index a82423a432d6c..5a7afab1b8e4f 100644 --- a/src/gui/governance/applygovernancelabel.h +++ b/src/gui/governance/applygovernancelabel.h @@ -27,6 +27,8 @@ public Q_SLOTS: private Q_SLOTS: void jobDone(QJsonDocument reply, int statusCode); + + void initialize(); }; } // namespace OCC diff --git a/src/gui/governance/deletegovernancelabel.cpp b/src/gui/governance/deletegovernancelabel.cpp index 72804644dbb08..f8d7a652a3721 100644 --- a/src/gui/governance/deletegovernancelabel.cpp +++ b/src/gui/governance/deletegovernancelabel.cpp @@ -13,10 +13,22 @@ namespace OCC DeleteGovernanceLabel::DeleteGovernanceLabel(QObject *parent) : OCC::TypedWithLabelIdGovernanceNetworkJob{parent} { + connect(this, &DeleteGovernanceLabel::apiVersionChanged, this, &DeleteGovernanceLabel::initialize); + connect(this, &DeleteGovernanceLabel::entityTypeChanged, this, &DeleteGovernanceLabel::initialize); + connect(this, &DeleteGovernanceLabel::customEntityTypeChanged, this, &DeleteGovernanceLabel::initialize); + connect(this, &DeleteGovernanceLabel::entityIdChanged, this, &DeleteGovernanceLabel::initialize); + connect(this, &DeleteGovernanceLabel::accountChanged, this, &DeleteGovernanceLabel::initialize); + connect(this, &DeleteGovernanceLabel::labelTypeChanged, this, &DeleteGovernanceLabel::initialize); + connect(this, &DeleteGovernanceLabel::labelIdChanged, this, &DeleteGovernanceLabel::initialize); } void DeleteGovernanceLabel::start() { + if (!checkParameters()) { + Q_EMIT finishedWitherror(500, {}); + return; + } + setOcsGovernanceJob(QPointer{new OcsGovernanceJob{account()}}); connect(ocsGovernanceJob().data(), &OcsJob::jobFinished, @@ -35,4 +47,11 @@ void DeleteGovernanceLabel::jobDone(QJsonDocument reply, [[maybe_unused]] int st Q_EMIT finished(reply); } +void DeleteGovernanceLabel::initialize() +{ + if (checkParameters()) { + start(); + } +} + } // namespace OCC diff --git a/src/gui/governance/deletegovernancelabel.h b/src/gui/governance/deletegovernancelabel.h index 938c70d2d3ac9..8be8c1a22bc5f 100644 --- a/src/gui/governance/deletegovernancelabel.h +++ b/src/gui/governance/deletegovernancelabel.h @@ -27,6 +27,8 @@ public Q_SLOTS: private Q_SLOTS: void jobDone(QJsonDocument reply, int statusCode); + + void initialize(); }; } // namespace OCC diff --git a/src/gui/governance/getavailablegovernancelabels.cpp b/src/gui/governance/getavailablegovernancelabels.cpp index 1e2c7bbd2fc56..0c681082ac38f 100644 --- a/src/gui/governance/getavailablegovernancelabels.cpp +++ b/src/gui/governance/getavailablegovernancelabels.cpp @@ -15,10 +15,29 @@ namespace OCC GetAvailableGovernanceLabels::GetAvailableGovernanceLabels(QObject *parent) : OCC::TypedGovernanceNetworkJob{parent} { + connect(this, &GetAvailableGovernanceLabels::apiVersionChanged, this, &GetAvailableGovernanceLabels::initialize); + connect(this, &GetAvailableGovernanceLabels::entityTypeChanged, this, &GetAvailableGovernanceLabels::initialize); + connect(this, &GetAvailableGovernanceLabels::customEntityTypeChanged, this, &GetAvailableGovernanceLabels::initialize); + connect(this, &GetAvailableGovernanceLabels::entityIdChanged, this, &GetAvailableGovernanceLabels::initialize); + connect(this, &GetAvailableGovernanceLabels::accountChanged, this, &GetAvailableGovernanceLabels::initialize); + connect(this, &GetAvailableGovernanceLabels::labelTypeChanged, this, &GetAvailableGovernanceLabels::initialize); +} + +void GetAvailableGovernanceLabels::start(Governance::LabelType labelType, const QString &entityId) +{ + setLabelType(labelType); + setEntityId(entityId); + + start(); } void GetAvailableGovernanceLabels::start() { + if (!checkParameters()) { + Q_EMIT finishedWitherror(500, {}); + return; + } + setOcsGovernanceJob(QPointer{new OcsGovernanceJob{account()}}); connect(ocsGovernanceJob().data(), &OcsJob::jobFinished, @@ -37,6 +56,13 @@ void GetAvailableGovernanceLabels::jobDone(QJsonDocument reply, [[maybe_unused]] Q_EMIT finished(reply); } +void GetAvailableGovernanceLabels::initialize() +{ + if (checkParameters()) { + start(); + } +} + QString GetAvailableGovernanceLabels::buildPath() const { return u"/ocs/v2.php/apps/governance/%1/labels/%2/%3/%4/available"_s.arg(apiVersionAsString(), entityTypeAsString(), entityId(), labelTypeAsString()); diff --git a/src/gui/governance/getavailablegovernancelabels.h b/src/gui/governance/getavailablegovernancelabels.h index 29a2cab6a2a47..9bbf0242bffce 100644 --- a/src/gui/governance/getavailablegovernancelabels.h +++ b/src/gui/governance/getavailablegovernancelabels.h @@ -28,11 +28,15 @@ class GetAvailableGovernanceLabels : public OCC::TypedGovernanceNetworkJob public Q_SLOTS: void start(); + void start(OCC::Governance::LabelType labelType, const QString &entityId); + protected: [[nodiscard]] QString buildPath() const override; private Q_SLOTS: void jobDone(QJsonDocument reply, int statusCode); + + void initialize(); }; } // namespace OCC diff --git a/src/gui/governance/getgovernancelabels.cpp b/src/gui/governance/getgovernancelabels.cpp index c442fd7698259..afe43a2b888a9 100644 --- a/src/gui/governance/getgovernancelabels.cpp +++ b/src/gui/governance/getgovernancelabels.cpp @@ -13,10 +13,20 @@ namespace OCC GetGovernanceLabels::GetGovernanceLabels(QObject *parent) : OCC::GovernanceNetworkJob{parent} { + connect(this, &GetGovernanceLabels::apiVersionChanged, this, &GetGovernanceLabels::initialize); + connect(this, &GetGovernanceLabels::entityTypeChanged, this, &GetGovernanceLabels::initialize); + connect(this, &GetGovernanceLabels::customEntityTypeChanged, this, &GetGovernanceLabels::initialize); + connect(this, &GetGovernanceLabels::entityIdChanged, this, &GetGovernanceLabels::initialize); + connect(this, &GetGovernanceLabels::accountChanged, this, &GetGovernanceLabels::initialize); } void GetGovernanceLabels::start() { + if (!checkParameters()) { + Q_EMIT finishedWitherror(500, {}); + return; + } + setOcsGovernanceJob(QPointer{new OcsGovernanceJob{account()}}); connect(ocsGovernanceJob().data(), &OcsJob::jobFinished, @@ -35,4 +45,11 @@ void GetGovernanceLabels::jobDone(QJsonDocument reply, [[maybe_unused]] int stat Q_EMIT finished(reply); } +void GetGovernanceLabels::initialize() +{ + if (checkParameters()) { + start(); + } +} + } // namespace OCC diff --git a/src/gui/governance/getgovernancelabels.h b/src/gui/governance/getgovernancelabels.h index be08db2d4be51..176944efbd30b 100644 --- a/src/gui/governance/getgovernancelabels.h +++ b/src/gui/governance/getgovernancelabels.h @@ -27,6 +27,8 @@ public Q_SLOTS: private Q_SLOTS: void jobDone(QJsonDocument reply, int statusCode); + + void initialize(); }; } // namespace OCC diff --git a/src/gui/governance/governancelabelinfo.cpp b/src/gui/governance/governancelabelinfo.cpp index 1f6da914a0b38..32aa1b905eb3a 100644 --- a/src/gui/governance/governancelabelinfo.cpp +++ b/src/gui/governance/governancelabelinfo.cpp @@ -8,8 +8,4 @@ namespace OCC { -GOvernanceLabelInfo::GOvernanceLabelInfo() -{ -} - } // namespace OCC diff --git a/src/gui/governance/governancelabelinfo.h b/src/gui/governance/governancelabelinfo.h index 8ddf85345254f..9ee7e311792c2 100644 --- a/src/gui/governance/governancelabelinfo.h +++ b/src/gui/governance/governancelabelinfo.h @@ -12,11 +12,9 @@ namespace OCC { -struct GOvernanceLabelInfo +struct GovernanceLabelInfo { public: - GOvernanceLabelInfo(); - QString _id; QString _name; diff --git a/src/gui/governance/governancelabelslistmodel.cpp b/src/gui/governance/governancelabelslistmodel.cpp new file mode 100644 index 0000000000000..4f580987ae225 --- /dev/null +++ b/src/gui/governance/governancelabelslistmodel.cpp @@ -0,0 +1,192 @@ +/* + * SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "governancelabelslistmodel.h" + +#include +#include +#include +#include + +using namespace Qt::StringLiterals; + +namespace OCC +{ + +Q_LOGGING_CATEGORY(lcGovernanceLabelsListModel, "nextcloud.gui.governance.labelslistmodel", QtInfoMsg) + +GovernanceLabelsListModel::GovernanceLabelsListModel(QObject *parent) + : QAbstractListModel(parent) +{ +} + +int GovernanceLabelsListModel::rowCount(const QModelIndex &parent) const +{ + auto result = 0; + if (parent.isValid()) { + return result; + } + + result = _data.count(); + return result; +} + +QVariant GovernanceLabelsListModel::data(const QModelIndex &index, int role) const +{ + auto result = QVariant{}; + + if (!index.isValid()) { + return result; + } + + if (index.column() != 0) { + return result; + } + + if (index.row() < 0 || index.row() >= _data.count()) { + return result; + } + + if (role >= Qt::UserRole + 1) { + auto convertedRole = static_cast(role); + + switch (convertedRole) + { + case LabelsListModelRoles::IdRole: + result = _data[index.row()]._id; + break; + case LabelsListModelRoles::NameRole: + result = _data[index.row()]._name; + break; + case LabelsListModelRoles::PriorityRole: + result = _data[index.row()]._priority; + break; + case LabelsListModelRoles::DescriptionRole: + result = _data[index.row()]._description; + break; + case LabelsListModelRoles::ColorRole: + result = _data[index.row()]._color; + break; + case LabelsListModelRoles::ScopesRole: + result = _data[index.row()]._scopes; + break; + } + } + + return result; +} + +QHash GovernanceLabelsListModel::roleNames() const +{ + auto result = QHash{ + {static_cast(LabelsListModelRoles::IdRole), "id"_ba}, + {static_cast(LabelsListModelRoles::NameRole), "name"_ba}, + {static_cast(LabelsListModelRoles::PriorityRole), "priority"_ba}, + {static_cast(LabelsListModelRoles::DescriptionRole), "description"_ba}, + {static_cast(LabelsListModelRoles::ColorRole), "color"_ba}, + {static_cast(LabelsListModelRoles::ScopesRole), "scopes"_ba}, + }; + + return result; +} + +Governance::LabelType GovernanceLabelsListModel::labelType() const +{ + return _labelType; +} + +void GovernanceLabelsListModel::setLabelType(Governance::LabelType newLabelType) +{ + if (_labelType == newLabelType) { + return; + } + + _labelType = newLabelType; + Q_EMIT labelTypeChanged(); + + emitRefreshData(); +} + +QString GovernanceLabelsListModel::entityId() const +{ + return _entityId; +} + +void GovernanceLabelsListModel::setEntityId(const QString &newEntityId) +{ + if (_entityId == newEntityId) { + return; + } + + _entityId = newEntityId; + Q_EMIT entityIdChanged(); + + emitRefreshData(); +} + +void GovernanceLabelsListModel::setAvailableLabelsJsonData(const QJsonDocument &reply) +{ + const auto replyObject = reply.object(); + + if (!replyObject.contains(u"ocs"_s)) { + qCWarning(lcGovernanceLabelsListModel()) << "wrong format for reply" << reply.toJson(QJsonDocument::JsonFormat::Compact); + return; + } + + const auto ocsObject = replyObject.value(u"ocs"_s).toObject(); + + if (!ocsObject.contains(u"data"_s)) { + qCWarning(lcGovernanceLabelsListModel()) << "wrong format for reply" << ocsObject; + return; + } + + const auto dataArray = ocsObject.value(u"data"_s).toArray(); + + const auto convertToStringList = [] (const QJsonArray &scopesList) -> QStringList + { + auto result = QStringList{}; + + for (const auto &oneScope : scopesList) { + result << oneScope.toString(); + } + + return result; + }; + + beginResetModel(); + _data.clear(); + for (const auto oneLabel : dataArray) { + const auto oneLabelObject = oneLabel.toObject(); + _data.emplaceBack(oneLabelObject.value(u"id"_s).toString(), + oneLabelObject.value(u"name"_s).toString(), + oneLabelObject.value(u"priority"_s).toInt(), + oneLabelObject.value(u"description"_s).toString(), + oneLabelObject.value(u"color"_s).toString(), + convertToStringList(oneLabelObject.value(u"scopes"_s).toArray()) + ); + } + endResetModel(); +} + +void GovernanceLabelsListModel::setExistingLabelsJsonData(const QJsonDocument &data) +{ + qCInfo(lcGovernanceLabelsListModel()) << data.toJson(QJsonDocument::JsonFormat::Compact); +} + +void OCC::GovernanceLabelsListModel::etagChanged() +{ + Q_EMIT refreshData(_labelType, _entityId); +} + +void GovernanceLabelsListModel::emitRefreshData() +{ + if (_entityId.isEmpty() || _labelType == Governance::LabelType::Invalid) { + return; + } + + Q_EMIT refreshData(_labelType, _entityId); +} + +} // namespace OCC diff --git a/src/gui/governance/governancelabelslistmodel.h b/src/gui/governance/governancelabelslistmodel.h new file mode 100644 index 0000000000000..5dd2c0d3ce8a8 --- /dev/null +++ b/src/gui/governance/governancelabelslistmodel.h @@ -0,0 +1,82 @@ +/* + * SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef GOVERNANCELABELLISTMODEL_H +#define GOVERNANCELABELLISTMODEL_H + +#include "governancetypes.h" +#include "governancelabelinfo.h" + +#include +#include +#include + +namespace OCC +{ + +class GovernanceLabelsListModel : public QAbstractListModel +{ + Q_OBJECT + QML_ELEMENT + + Q_PROPERTY(Governance::LabelType labelType READ labelType WRITE setLabelType NOTIFY labelTypeChanged FINAL) + + Q_PROPERTY(QString entityId READ entityId WRITE setEntityId NOTIFY entityIdChanged FINAL) + +public: + enum class LabelsListModelRoles { + IdRole = Qt::UserRole + 1, + NameRole, + PriorityRole, + DescriptionRole, + ColorRole, + ScopesRole, + }; + + Q_ENUM(LabelsListModelRoles) + + explicit GovernanceLabelsListModel(QObject *parent = nullptr); + + int rowCount(const QModelIndex &parent = QModelIndex()) const override; + + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; + + QHash roleNames() const override; + + [[nodiscard]] Governance::LabelType labelType() const; + + void setLabelType(Governance::LabelType newLabelType); + + [[nodiscard]] QString entityId() const; + + void setEntityId(const QString &newEntityId); + +public Q_SLOTS: + void setAvailableLabelsJsonData(const QJsonDocument &reply); + + void setExistingLabelsJsonData(const QJsonDocument &data); + + void etagChanged(); + +Q_SIGNALS: + void labelTypeChanged(); + + void entityIdChanged(); + + void refreshData(OCC::Governance::LabelType labelType, const QString &entityId); + +private: + void emitRefreshData(); + + Governance::LabelType _labelType = Governance::LabelType::Invalid; + + QString _entityId; + + QList _data; +}; + +} // namespace OCC + +#endif // GOVERNANCELABELLISTMODEL_H diff --git a/src/gui/governance/governancenetworkjob.cpp b/src/gui/governance/governancenetworkjob.cpp index f39c23e9309fe..fa14c110cc6b4 100644 --- a/src/gui/governance/governancenetworkjob.cpp +++ b/src/gui/governance/governancenetworkjob.cpp @@ -5,24 +5,24 @@ #include "governancenetworkjob.h" -Q_LOGGING_CATEGORY(lcGovernance, "nextcloud.gui.governance", QtInfoMsg) - using namespace Qt::StringLiterals; namespace OCC { +Q_LOGGING_CATEGORY(lcGovernanceNetwork, "nextcloud.gui.governance.network", QtInfoMsg) + GovernanceNetworkJob::GovernanceNetworkJob(QObject *parent) : QObject{parent} { } -GovernanceNetworkJob::ApiVersion GovernanceNetworkJob::apiVersion() const +Governance::ApiVersion GovernanceNetworkJob::apiVersion() const { return _apiVersion; } -void GovernanceNetworkJob::setApiVersion(ApiVersion newApiVersion) +void GovernanceNetworkJob::setApiVersion(Governance::ApiVersion newApiVersion) { if (_apiVersion == newApiVersion) { return; @@ -32,12 +32,12 @@ void GovernanceNetworkJob::setApiVersion(ApiVersion newApiVersion) Q_EMIT apiVersionChanged(); } -GovernanceNetworkJob::EntityType GovernanceNetworkJob::entityType() const +Governance::EntityType GovernanceNetworkJob::entityType() const { return _entityType; } -void GovernanceNetworkJob::setEntityType(EntityType newEntityType) +void GovernanceNetworkJob::setEntityType(Governance::EntityType newEntityType) { if (_entityType == newEntityType) { return; @@ -88,9 +88,12 @@ QString GovernanceNetworkJob::apiVersionAsString() const switch (_apiVersion) { - case ApiVersion::Version_1: + case Governance::ApiVersion::Version_1: result = u"v1"_s; break; + case Governance::ApiVersion::Invalid: + result = u"invalid"_s; + break; } return result; @@ -102,13 +105,13 @@ QString GovernanceNetworkJob::entityTypeAsString() const switch (_entityType) { - case EntityType::Files: + case Governance::EntityType::Files: result = u"FILES"_s; break; - case EntityType::Mails: + case Governance::EntityType::Mails: result = u"MAILS"_s; break; - case EntityType::Custom: + case Governance::EntityType::Custom: result = _customEntityType; break; } @@ -116,6 +119,38 @@ QString GovernanceNetworkJob::entityTypeAsString() const return result; } +bool GovernanceNetworkJob::checkParameters() const +{ + auto result = true; + + if (!_account) { + result = false; + return result; + } + + if (_apiVersion == Governance::ApiVersion::Invalid) { + result = false; + return result; + } + + if (_entityType != Governance::EntityType::Files) { + result = false; + return result; + } + + if (_entityType == Governance::EntityType::Custom && _customEntityType.isEmpty()) { + result = false; + return result; + } + + if (_entityId.isEmpty()) { + result = false; + return result; + } + + return result; +} + void GovernanceNetworkJob::setAccount(AccountPtr newAccount) { if (_account == newAccount) { diff --git a/src/gui/governance/governancenetworkjob.h b/src/gui/governance/governancenetworkjob.h index f20ffd5e08ddb..7afe83e66e1ef 100644 --- a/src/gui/governance/governancenetworkjob.h +++ b/src/gui/governance/governancenetworkjob.h @@ -6,6 +6,7 @@ #ifndef GOVERNANCENETWORKJOB_H #define GOVERNANCENETWORKJOB_H +#include "governancetypes.h" #include "accountfwd.h" #include @@ -13,12 +14,11 @@ #include #include -Q_DECLARE_LOGGING_CATEGORY(lcGovernance) - - namespace OCC { +Q_DECLARE_LOGGING_CATEGORY(lcGovernanceNetwork) + class OcsGovernanceJob; class GovernanceNetworkJob : public QObject @@ -28,46 +28,24 @@ class GovernanceNetworkJob : public QObject Q_PROPERTY(AccountPtr account READ account WRITE setAccount NOTIFY accountChanged FINAL) - Q_PROPERTY(ApiVersion apiVersion READ apiVersion WRITE setApiVersion NOTIFY apiVersionChanged FINAL) + Q_PROPERTY(Governance::ApiVersion apiVersion READ apiVersion WRITE setApiVersion NOTIFY apiVersionChanged FINAL) - Q_PROPERTY(EntityType entityType READ entityType WRITE setEntityType NOTIFY entityTypeChanged FINAL) + Q_PROPERTY(Governance::EntityType entityType READ entityType WRITE setEntityType NOTIFY entityTypeChanged FINAL) Q_PROPERTY(QString customEntityType READ customEntityType WRITE setCustomEntityType NOTIFY customEntityTypeChanged FINAL) Q_PROPERTY(QString entityId READ entityId WRITE setEntityId NOTIFY entityIdChanged FINAL) public: - enum class EntityType { - Files, - Mails, - Custom, - }; - - Q_ENUM(EntityType) - - enum class LabelType { - Sensitivity, - Retention, - Hold, - }; - - Q_ENUM(LabelType) - - enum class ApiVersion { - Version_1, - }; - - Q_ENUM(ApiVersion) - explicit GovernanceNetworkJob(QObject *parent = nullptr); - [[nodiscard]] ApiVersion apiVersion() const; + [[nodiscard]] Governance::ApiVersion apiVersion() const; - void setApiVersion(ApiVersion newApiVersion); + void setApiVersion(Governance::ApiVersion newApiVersion); - [[nodiscard]] EntityType entityType() const; + [[nodiscard]] Governance::EntityType entityType() const; - void setEntityType(EntityType newEntityType); + void setEntityType(Governance::EntityType newEntityType); [[nodiscard]] QString customEntityType() const; @@ -116,12 +94,14 @@ class GovernanceNetworkJob : public QObject [[nodiscard]] QString entityTypeAsString() const; + [[nodiscard]] virtual bool checkParameters() const; + private: AccountPtr _account; - ApiVersion _apiVersion = ApiVersion::Version_1; + Governance::ApiVersion _apiVersion = Governance::ApiVersion::Version_1; - EntityType _entityType = EntityType::Files; + Governance::EntityType _entityType = Governance::EntityType::Files; QString _customEntityType; diff --git a/src/gui/governance/governancetypes.h b/src/gui/governance/governancetypes.h new file mode 100644 index 0000000000000..6fea596059203 --- /dev/null +++ b/src/gui/governance/governancetypes.h @@ -0,0 +1,45 @@ +/* + * SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef GOVERNANCETYPES_H +#define GOVERNANCETYPES_H + +#include + +namespace OCC { + +namespace Governance { + +Q_NAMESPACE + +enum class EntityType { + Files, + Mails, + Custom, +}; + +Q_ENUM_NS(EntityType) + +enum class LabelType { + Sensitivity, + Retention, + Hold, + Invalid, +}; + +Q_ENUM_NS(LabelType) + +enum class ApiVersion { + Invalid, + Version_1, +}; + +Q_ENUM_NS(ApiVersion) + +} + +} + +#endif // GOVERNANCETYPES_H diff --git a/src/gui/governance/typedgovernancenetworkjob.cpp b/src/gui/governance/typedgovernancenetworkjob.cpp index 5cb3304ef04dd..6d28cdf80e355 100644 --- a/src/gui/governance/typedgovernancenetworkjob.cpp +++ b/src/gui/governance/typedgovernancenetworkjob.cpp @@ -15,12 +15,12 @@ TypedGovernanceNetworkJob::TypedGovernanceNetworkJob(QObject *parent) { } -GovernanceNetworkJob::LabelType TypedGovernanceNetworkJob::labelType() const +Governance::LabelType TypedGovernanceNetworkJob::labelType() const { return _labelType; } -void TypedGovernanceNetworkJob::setLabelType(LabelType newLabelType) +void TypedGovernanceNetworkJob::setLabelType(Governance::LabelType newLabelType) { if (_labelType == newLabelType) { return; @@ -36,15 +36,34 @@ QString TypedGovernanceNetworkJob::labelTypeAsString() const switch (_labelType) { - case LabelType::Sensitivity: + case Governance::LabelType::Sensitivity: result = u"sensitivity"_s; break; - case LabelType::Retention: + case Governance::LabelType::Retention: result = u"retention"_s; break; - case LabelType::Hold: + case Governance::LabelType::Hold: result = u"hold"_s; break; + case Governance::LabelType::Invalid: + result = u"invalid"_s; + break; + } + + return result; +} + +bool TypedGovernanceNetworkJob::checkParameters() const +{ + auto result = GovernanceNetworkJob::checkParameters(); + + if (!result) { + return result; + } + + if (_labelType == Governance::LabelType::Invalid) { + result = false; + return result; } return result; diff --git a/src/gui/governance/typedgovernancenetworkjob.h b/src/gui/governance/typedgovernancenetworkjob.h index 5c7464ef776a1..22df022642772 100644 --- a/src/gui/governance/typedgovernancenetworkjob.h +++ b/src/gui/governance/typedgovernancenetworkjob.h @@ -18,14 +18,14 @@ class TypedGovernanceNetworkJob : public GovernanceNetworkJob Q_OBJECT QML_ELEMENT - Q_PROPERTY(LabelType labelType READ labelType WRITE setLabelType NOTIFY labelTypeChanged FINAL) + Q_PROPERTY(Governance::LabelType labelType READ labelType WRITE setLabelType NOTIFY labelTypeChanged FINAL) public: TypedGovernanceNetworkJob(QObject *parent = nullptr); - [[nodiscard]] LabelType labelType() const; + [[nodiscard]] Governance::LabelType labelType() const; - void setLabelType(LabelType newLabelType); + void setLabelType(Governance::LabelType newLabelType); Q_SIGNALS: void labelTypeChanged(); @@ -33,8 +33,10 @@ class TypedGovernanceNetworkJob : public GovernanceNetworkJob protected: [[nodiscard]] QString labelTypeAsString() const; + [[nodiscard]] bool checkParameters() const override; + private: - LabelType _labelType; + Governance::LabelType _labelType = Governance::LabelType::Invalid; }; } // namespace OCC diff --git a/src/gui/governance/typedwithlabelidgovernancenetworkjob.cpp b/src/gui/governance/typedwithlabelidgovernancenetworkjob.cpp index 7f83d373e4cce..8d35ca055dcd1 100644 --- a/src/gui/governance/typedwithlabelidgovernancenetworkjob.cpp +++ b/src/gui/governance/typedwithlabelidgovernancenetworkjob.cpp @@ -35,4 +35,20 @@ QString TypedWithLabelIdGovernanceNetworkJob::buildPath() const return u"/ocs/v2.php/apps/governance/%1/labels/%2/%3/%4/%5"_s.arg(apiVersionAsString(), entityTypeAsString(), entityId(), labelId(), labelTypeAsString()); } +bool TypedWithLabelIdGovernanceNetworkJob::checkParameters() const +{ + auto result = TypedGovernanceNetworkJob::checkParameters(); + + if (!result) { + return result; + } + + if (_labelId.isEmpty()) { + result = false; + return result; + } + + return result; +} + } // namespace OCC diff --git a/src/gui/governance/typedwithlabelidgovernancenetworkjob.h b/src/gui/governance/typedwithlabelidgovernancenetworkjob.h index 785ddd7297230..428578e88e96a 100644 --- a/src/gui/governance/typedwithlabelidgovernancenetworkjob.h +++ b/src/gui/governance/typedwithlabelidgovernancenetworkjob.h @@ -33,6 +33,8 @@ class TypedWithLabelIdGovernanceNetworkJob : public OCC::TypedGovernanceNetworkJ protected: [[nodiscard]] QString buildPath() const override; + [[nodiscard]] bool checkParameters() const override; + private: QString _labelId; }; diff --git a/src/gui/owncloudgui.cpp b/src/gui/owncloudgui.cpp index a1131b3105f71..a66977b54f941 100644 --- a/src/gui/owncloudgui.cpp +++ b/src/gui/owncloudgui.cpp @@ -40,6 +40,7 @@ #include "governance/deletegovernancelabel.h" #include "governance/getavailablegovernancelabels.h" #include "governance/getgovernancelabels.h" +#include "governance/governancelabelslistmodel.h" #include "filesystem.h" #ifdef WITH_LIBCLOUDPROVIDERS @@ -160,6 +161,7 @@ ownCloudGui::ownCloudGui(Application *parent) qmlRegisterType("com.nextcloud.desktopclient", 1, 0, "DeleteGovernanceLabel"); qmlRegisterType("com.nextcloud.desktopclient", 1, 0, "GetAvailableGovernanceLabels"); qmlRegisterType("com.nextcloud.desktopclient", 1, 0, "GetGovernanceLabels"); + qmlRegisterType("com.nextcloud.desktopclient", 1, 0, "GovernanceLabelsListModel"); qmlRegisterUncreatableType("com.nextcloud.desktopclient", 1, 0, "QAbstractItemModel", "QAbstractItemModel"); qmlRegisterUncreatableType("com.nextcloud.desktopclient", 1, 0, "activity", "Activity"); diff --git a/test/testgovernance.cpp b/test/testgovernance.cpp index 42a50badd6154..a9e1cf50b3705 100644 --- a/test/testgovernance.cpp +++ b/test/testgovernance.cpp @@ -13,6 +13,11 @@ #include "governance/deletegovernancelabel.h" #include "governance/getavailablegovernancelabels.h" #include "governance/getgovernancelabels.h" +#include "governance/governancelabelslistmodel.h" +#include "governance/governancetypes.h" + +#include +#include using namespace OCC; using namespace Qt::StringLiterals; @@ -331,8 +336,8 @@ private slots: myJob.setAccount(fakeFolder.account()); myJob.setEntityId(u"1"_s); - myJob.setEntityType(ApplyGovernanceLabel::EntityType::Files); - myJob.setLabelType(ApplyGovernanceLabel::LabelType::Sensitivity); + myJob.setEntityType(Governance::EntityType::Files); + myJob.setLabelType(Governance::LabelType::Sensitivity); myJob.setLabelId(u"1"_s); myJob.start(); @@ -358,8 +363,8 @@ private slots: myJob.setAccount(fakeFolder.account()); myJob.setEntityId(u"1"_s); - myJob.setEntityType(DeleteGovernanceLabel::EntityType::Files); - myJob.setLabelType(DeleteGovernanceLabel::LabelType::Sensitivity); + myJob.setEntityType(Governance::EntityType::Files); + myJob.setLabelType(Governance::LabelType::Sensitivity); myJob.setLabelId(u"1"_s); myJob.start(); @@ -385,8 +390,8 @@ private slots: myJob.setAccount(fakeFolder.account()); myJob.setEntityId(u"117"_s); - myJob.setEntityType(GetAvailableGovernanceLabels::EntityType::Files); - myJob.setLabelType(GetAvailableGovernanceLabels::LabelType::Sensitivity); + myJob.setEntityType(Governance::EntityType::Files); + myJob.setLabelType(Governance::LabelType::Sensitivity); myJob.start(); @@ -411,8 +416,8 @@ private slots: myJob.setAccount(fakeFolder.account()); myJob.setEntityId(u"117117"_s); - myJob.setEntityType(GetAvailableGovernanceLabels::EntityType::Files); - myJob.setLabelType(GetAvailableGovernanceLabels::LabelType::Sensitivity); + myJob.setEntityType(Governance::EntityType::Files); + myJob.setLabelType(Governance::LabelType::Sensitivity); myJob.start(); @@ -437,9 +442,9 @@ private slots: myJob.setAccount(fakeFolder.account()); myJob.setEntityId(u"117"_s); - myJob.setEntityType(GetAvailableGovernanceLabels::EntityType::Custom); + myJob.setEntityType(Governance::EntityType::Custom); myJob.setCustomEntityType(u"FILEStdytf"_s); - myJob.setLabelType(GetAvailableGovernanceLabels::LabelType::Sensitivity); + myJob.setLabelType(Governance::LabelType::Sensitivity); myJob.start(); @@ -464,7 +469,7 @@ private slots: myJob.setAccount(fakeFolder.account()); myJob.setEntityId(u"117"_s); - myJob.setEntityType(GetGovernanceLabels::EntityType::Files); + myJob.setEntityType(Governance::EntityType::Files); myJob.start(); @@ -489,7 +494,7 @@ private slots: myJob.setAccount(fakeFolder.account()); myJob.setEntityId(u"117117"_s); - myJob.setEntityType(GetGovernanceLabels::EntityType::Files); + myJob.setEntityType(Governance::EntityType::Files); myJob.start(); @@ -514,7 +519,7 @@ private slots: myJob.setAccount(fakeFolder.account()); myJob.setEntityId(u"117"_s); - myJob.setEntityType(GetGovernanceLabels::EntityType::Custom); + myJob.setEntityType(Governance::EntityType::Custom); myJob.setCustomEntityType(u"FILEStdytf"_s); myJob.start(); @@ -523,6 +528,66 @@ private slots: QCOMPARE(finishedSpy.count(), 0); QCOMPARE(finishedWithErrorSpy.count(), 1); } + + void testGovernanceLabelListModel_setData() + { + GovernanceLabelsListModel myModel; + QAbstractItemModelTester myModelTester(&myModel); + + const auto replyJson = R"json( +{ + "ocs": { + "meta": { + "status": "ok", + "statuscode": 200, + "message": "OK" + }, + "data": [ + { + "id": "91785883351310337", + "name": "Test Sensitivity", + "priority": 0, + "description": "This is a long description", + "color": "bf4040", + "scopes": [ + "FILES" + ] + } + ] + } +} + )json"_ba; + + const auto replyData = QJsonDocument::fromJson(replyJson); + + myModel.setAvailableLabelsJsonData(replyData); + + QCOMPARE(myModel.rowCount(), 1); + const auto labelIndex = myModel.index(0); + QVERIFY(labelIndex.isValid()); + QCOMPARE(myModel.data(labelIndex, static_cast(GovernanceLabelsListModel::LabelsListModelRoles::IdRole)), u"91785883351310337"_s); + QCOMPARE(myModel.data(labelIndex, static_cast(GovernanceLabelsListModel::LabelsListModelRoles::NameRole)), u"Test Sensitivity"_s); + QCOMPARE(myModel.data(labelIndex, static_cast(GovernanceLabelsListModel::LabelsListModelRoles::PriorityRole)), 0); + QCOMPARE(myModel.data(labelIndex, static_cast(GovernanceLabelsListModel::LabelsListModelRoles::DescriptionRole)), u"This is a long description"_s); + QCOMPARE(myModel.data(labelIndex, static_cast(GovernanceLabelsListModel::LabelsListModelRoles::ColorRole)), u"bf4040"_s); + QCOMPARE(myModel.data(labelIndex, static_cast(GovernanceLabelsListModel::LabelsListModelRoles::ScopesRole)).toStringList(), QStringList{u"FILES"_s}); + } + + void testGovernanceLabelListModel_refreshData() + { + GovernanceLabelsListModel myModel; + QAbstractItemModelTester myModelTester(&myModel); + QSignalSpy modelRefreshDataSignalSpy(&myModel, &GovernanceLabelsListModel::refreshData); + + myModel.setEntityId(u"117"_s); + myModel.setLabelType(Governance::LabelType::Sensitivity); + + QVERIFY(modelRefreshDataSignalSpy.wait()); + QCOMPARE(modelRefreshDataSignalSpy.count(), 1); + QCOMPARE(modelRefreshDataSignalSpy.at(0).count(), 2); + QCOMPARE(modelRefreshDataSignalSpy.at(0).at(0).value(), OCC::Governance::LabelType::Sensitivity); + QCOMPARE(modelRefreshDataSignalSpy.at(0).at(1), u"117"_s); + } }; QTEST_GUILESS_MAIN(TestGovernance) From 9ca716ce81cfd02a930bd53fa329bd91df40357d Mon Sep 17 00:00:00 2001 From: Matthieu Gallien Date: Wed, 24 Jun 2026 16:40:19 +0200 Subject: [PATCH 09/11] feat(governance): fix some issues with QML bindings of enum types Signed-off-by: Matthieu Gallien --- src/gui/governance/governancelabelslistmodel.cpp | 2 +- src/gui/governance/governancelabelslistmodel.h | 2 +- src/gui/governance/governancenetworkjob.cpp | 4 ++-- src/gui/governance/governancetypes.h | 4 ++-- src/gui/governance/typedgovernancenetworkjob.cpp | 4 ++-- src/gui/governance/typedgovernancenetworkjob.h | 2 +- 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/gui/governance/governancelabelslistmodel.cpp b/src/gui/governance/governancelabelslistmodel.cpp index 4f580987ae225..02684e37b334c 100644 --- a/src/gui/governance/governancelabelslistmodel.cpp +++ b/src/gui/governance/governancelabelslistmodel.cpp @@ -182,7 +182,7 @@ void OCC::GovernanceLabelsListModel::etagChanged() void GovernanceLabelsListModel::emitRefreshData() { - if (_entityId.isEmpty() || _labelType == Governance::LabelType::Invalid) { + if (_entityId.isEmpty() || _labelType == Governance::LabelType::InvalidLabelType) { return; } diff --git a/src/gui/governance/governancelabelslistmodel.h b/src/gui/governance/governancelabelslistmodel.h index 5dd2c0d3ce8a8..d4b20b7028b31 100644 --- a/src/gui/governance/governancelabelslistmodel.h +++ b/src/gui/governance/governancelabelslistmodel.h @@ -70,7 +70,7 @@ public Q_SLOTS: private: void emitRefreshData(); - Governance::LabelType _labelType = Governance::LabelType::Invalid; + Governance::LabelType _labelType = Governance::LabelType::InvalidLabelType; QString _entityId; diff --git a/src/gui/governance/governancenetworkjob.cpp b/src/gui/governance/governancenetworkjob.cpp index fa14c110cc6b4..b2ce60d48b397 100644 --- a/src/gui/governance/governancenetworkjob.cpp +++ b/src/gui/governance/governancenetworkjob.cpp @@ -91,7 +91,7 @@ QString GovernanceNetworkJob::apiVersionAsString() const case Governance::ApiVersion::Version_1: result = u"v1"_s; break; - case Governance::ApiVersion::Invalid: + case Governance::ApiVersion::InvalidApiVersion: result = u"invalid"_s; break; } @@ -128,7 +128,7 @@ bool GovernanceNetworkJob::checkParameters() const return result; } - if (_apiVersion == Governance::ApiVersion::Invalid) { + if (_apiVersion == Governance::ApiVersion::InvalidApiVersion) { result = false; return result; } diff --git a/src/gui/governance/governancetypes.h b/src/gui/governance/governancetypes.h index 6fea596059203..b95d5eae8b530 100644 --- a/src/gui/governance/governancetypes.h +++ b/src/gui/governance/governancetypes.h @@ -26,13 +26,13 @@ enum class LabelType { Sensitivity, Retention, Hold, - Invalid, + InvalidLabelType, }; Q_ENUM_NS(LabelType) enum class ApiVersion { - Invalid, + InvalidApiVersion, Version_1, }; diff --git a/src/gui/governance/typedgovernancenetworkjob.cpp b/src/gui/governance/typedgovernancenetworkjob.cpp index 6d28cdf80e355..d9f2b091fcd56 100644 --- a/src/gui/governance/typedgovernancenetworkjob.cpp +++ b/src/gui/governance/typedgovernancenetworkjob.cpp @@ -45,7 +45,7 @@ QString TypedGovernanceNetworkJob::labelTypeAsString() const case Governance::LabelType::Hold: result = u"hold"_s; break; - case Governance::LabelType::Invalid: + case Governance::LabelType::InvalidLabelType: result = u"invalid"_s; break; } @@ -61,7 +61,7 @@ bool TypedGovernanceNetworkJob::checkParameters() const return result; } - if (_labelType == Governance::LabelType::Invalid) { + if (_labelType == Governance::LabelType::InvalidLabelType) { result = false; return result; } diff --git a/src/gui/governance/typedgovernancenetworkjob.h b/src/gui/governance/typedgovernancenetworkjob.h index 22df022642772..1b20e2eef80b0 100644 --- a/src/gui/governance/typedgovernancenetworkjob.h +++ b/src/gui/governance/typedgovernancenetworkjob.h @@ -36,7 +36,7 @@ class TypedGovernanceNetworkJob : public GovernanceNetworkJob [[nodiscard]] bool checkParameters() const override; private: - Governance::LabelType _labelType = Governance::LabelType::Invalid; + Governance::LabelType _labelType = Governance::LabelType::InvalidLabelType; }; } // namespace OCC From 69c7236bee9c8d86feb74812fa413b35c329c1c7 Mon Sep 17 00:00:00 2001 From: Matthieu Gallien Date: Wed, 24 Jun 2026 18:19:02 +0200 Subject: [PATCH 10/11] feat(governance): remove unused components from governance dialog Signed-off-by: Matthieu Gallien --- src/gui/GovernanceLabelsDialog.qml | 46 ------------------------------ 1 file changed, 46 deletions(-) diff --git a/src/gui/GovernanceLabelsDialog.qml b/src/gui/GovernanceLabelsDialog.qml index 53d35e4af86ba..1dc165b8906fe 100644 --- a/src/gui/GovernanceLabelsDialog.qml +++ b/src/gui/GovernanceLabelsDialog.qml @@ -37,26 +37,6 @@ ApplicationWindow { close.accepted = true } - ApplyGovernanceLabel { - id: applyGovernanceLabel - - account: governanceLabelsDialog.account - - labelId: 'labelId' - labelType: GovernanceNetworkJob.Sensitivity - entityId: governanceLabelsDialog.fileId - } - - DeleteGovernanceLabel { - id: deleteGovernanceLabel - - account: governanceLabelsDialog.account - - labelId: 'labelId' - labelType: GovernanceNetworkJob.Sensitivity - entityId: governanceLabelsDialog.fileId - } - GetAvailableGovernanceLabels { id: getAvailableGovernanceLabelsForSensitivity @@ -70,32 +50,6 @@ ApplicationWindow { } } - GetAvailableGovernanceLabels { - id: getAvailableGovernanceLabelsForHold - - account: governanceLabelsDialog.account - - labelType: GovernanceNetworkJob.Hold - entityId: governanceLabelsDialog.fileId - } - - GetAvailableGovernanceLabels { - id: getAvailableGovernanceLabelsForRetention - - account: governanceLabelsDialog.account - - labelType: GovernanceNetworkJob.Retention - entityId: governanceLabelsDialog.fileId - } - - GetGovernanceLabels { - id: getGovernanceLabels - - account: governanceLabelsDialog.account - - entityId: governanceLabelsDialog.fileId - } - GovernanceLabelsListModel { id: labelsModel From 518f087f28f7afe5d476581b44b3236825320d0a Mon Sep 17 00:00:00 2001 From: Matthieu Gallien Date: Wed, 24 Jun 2026 18:20:03 +0200 Subject: [PATCH 11/11] feat(governance): now the ComboBox displays the labels we explicitely define which roles need to be used Signed-off-by: Matthieu Gallien --- src/gui/GovernanceLabelsDialog.qml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/gui/GovernanceLabelsDialog.qml b/src/gui/GovernanceLabelsDialog.qml index 1dc165b8906fe..90230e23bf64c 100644 --- a/src/gui/GovernanceLabelsDialog.qml +++ b/src/gui/GovernanceLabelsDialog.qml @@ -84,6 +84,8 @@ ApplicationWindow { Accessible.name: qsTr("Select sensitivity label") model: labelsModel + textRole: 'name' + valueRole: 'id' } DialogButtonBox {