From aff8fe1a460d3fd923e150d69513b4e840d8bf1f Mon Sep 17 00:00:00 2001 From: David Llewellyn-Jones Date: Tue, 14 May 2019 12:18:38 +0000 Subject: [PATCH] [nemo-qml-plugin-systemsettings] Support VPN ordering by connected status. Contributes to JB#45380 This change adds the `orderByConnected` flag to VpnModel which, when set, will order VPN connections in the model first by the connection status (connected first, followed by not connected), then by the name alphabetially ascending (case-sensitive). When not set, the previous case-sensitive alphabetic name ordering is used. The new ordering is used when the MDM policy is set to prevent the user from manually connecting or disconnecting the VPNs. In this case, the list of VPNs is presented differently with headers indicating connection status, rather than indicating it using a TextSwitch glassitem. --- src/vpnmodel.cpp | 76 ++++++++++++++++++++++++++++++++++++++---------- src/vpnmodel.h | 11 +++++++ 2 files changed, 71 insertions(+), 16 deletions(-) diff --git a/src/vpnmodel.cpp b/src/vpnmodel.cpp index 0c0232b..06b6070 100644 --- a/src/vpnmodel.cpp +++ b/src/vpnmodel.cpp @@ -320,6 +320,7 @@ VpnModel::VpnModel(QObject *parent) , provisioningOutputPath_(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QStringLiteral("/system/privileged/vpn-provisioning")) , bestState_(VpnModel::Idle) , autoConnect_(false) + , orderByConnected_(false) { qDBusRegisterMetaType(); qDBusRegisterMetaType(); @@ -400,6 +401,30 @@ bool VpnModel::autoConnect() const return autoConnect_; } +bool VpnModel::orderByConnected() const +{ + return orderByConnected_; +} + +void VpnModel::setOrderByConnected(bool orderByConnected) +{ + if (orderByConnected != orderByConnected_) { + orderByConnected_ = orderByConnected; + + // Update the ordering; only the connected connections need to move + // In practice only one VPN can be connected, so full sort is overkill + const int itemCount(count()); + for (int index = 0; index < itemCount; ++index) { + VpnConnection *conn = get(index); + if (conn->connected()) { + reorderConnection(conn); + } + } + + emit orderByConnectedChanged(); + } +} + void VpnModel::createConnection(const QVariantMap &createProperties) { const QString path(createProperties.value(QString("path")).toString()); @@ -851,6 +876,7 @@ void VpnModel::updateConnection(VpnConnection *conn, const QVariantMap &updatePr } int oldState(conn->state()); + bool connectionChanged = false; if (updateItem(conn, properties)) { itemChanged(conn); @@ -859,6 +885,10 @@ void VpnModel::updateConnection(VpnConnection *conn, const QVariantMap &updatePr if (conn->state() != oldState) { emit connectionStateChanged(conn->path(), static_cast(conn->state())); + if ((conn->state() == VpnModel::Ready) != (oldState == VpnModel::Ready)) { + emit conn->connectedChanged(); + connectionChanged = true; + } // Check to see if the best state has changed ConnectionState maxState = Idle; @@ -874,23 +904,14 @@ void VpnModel::updateConnection(VpnConnection *conn, const QVariantMap &updatePr } } - - // Keep the items sorted by name. So sort only when updateProperties map contains - // a name e.i. not when "autoConnect" changes. In practice this means that sorting - // is only allowed when a VPN is created. When modifying name of a VPN, the VPN + // Keep the items sorted by name and possibly connected status. So sort + // only when the connection status has changed, or the updateProperties + // map contains a name i.e. not when "autoConnect" changes. In practice + // this means that if orderByConnected_ is false then sorting is only + // allowed when a VPN is created. When modifying name of a VPN, the VPN // will be first removed and then recreated. - if (itemCount > 1 && updateProperties.contains(QStringLiteral("name"))) { - int index = 0; - for ( ; index < itemCount; ++index) { - const VpnConnection *existing = get(index); - if (existing->name() > conn->name()) { - break; - } - } - const int currentIndex = indexOf(conn); - if (index != currentIndex && (index - 1) != currentIndex) { - moveItem(currentIndex, (currentIndex < index ? (index - 1) : index)); - } + if (updateProperties.contains(QStringLiteral("name")) || (orderByConnected_ && connectionChanged)) { + reorderConnection(conn); } } @@ -905,6 +926,29 @@ void VpnModel::updateConnection(VpnConnection *conn, const QVariantMap &updatePr } } +void VpnModel::reorderConnection(VpnConnection * conn) +{ + const int itemCount(count()); + + if (itemCount > 1) { + int index = 0; + for ( ; index < itemCount; ++index) { + const VpnConnection *existing = get(index); + // Scenario 1 orderByConnected == true: order first by connected, second by name + // Scenario 2 orderByConnected == false: order only by name + if ((orderByConnected_ && (existing->connected() < conn->connected())) + || ((!orderByConnected_ || (existing->connected() == conn->connected())) + && (existing->name() > conn->name()))) { + break; + } + } + const int currentIndex = indexOf(conn); + if (index != currentIndex && (index - 1) != currentIndex) { + moveItem(currentIndex, (currentIndex < index ? (index - 1) : index)); + } + } +} + QVariantMap VpnModel::processOpenVpnProvisioningFile(QFile &provisioningFile) { QVariantMap rv; diff --git a/src/vpnmodel.h b/src/vpnmodel.h index 2fa08a9..7c24d89 100644 --- a/src/vpnmodel.h +++ b/src/vpnmodel.h @@ -52,6 +52,7 @@ class SYSTEMSETTINGS_EXPORT VpnModel : public ObjectListModel Q_PROPERTY(int bestState READ bestState NOTIFY bestStateChanged) Q_PROPERTY(bool autoConnect READ autoConnect NOTIFY autoConnectChanged) + Q_PROPERTY(bool orderByConnected READ orderByConnected WRITE setOrderByConnected NOTIFY orderByConnectedChanged) public: enum ConnectionState { @@ -69,6 +70,9 @@ class SYSTEMSETTINGS_EXPORT VpnModel : public ObjectListModel int bestState() const; bool autoConnect() const; + bool orderByConnected() const; + void setOrderByConnected(bool orderByConnected); + Q_INVOKABLE void createConnection(const QVariantMap &properties); Q_INVOKABLE void modifyConnection(const QString &path, const QVariantMap &properties); Q_INVOKABLE void deleteConnection(const QString &path); @@ -92,6 +96,7 @@ class SYSTEMSETTINGS_EXPORT VpnModel : public ObjectListModel void bestStateChanged(); void autoConnectChanged(); void connectionStateChanged(const QString &path, int state); + void orderByConnectedChanged(); private: void fetchVpnList(); @@ -104,6 +109,7 @@ class SYSTEMSETTINGS_EXPORT VpnModel : public ObjectListModel bool domainInUse(const QString &domain) const; QString createDefaultDomain() const; bool isDefaultDomain(const QString &domain) const; + void reorderConnection(VpnConnection * conn); class CredentialsRepository { @@ -135,6 +141,7 @@ class SYSTEMSETTINGS_EXPORT VpnModel : public ObjectListModel ConnectionState bestState_; // True if there's one VPN that has autoConnect true bool autoConnect_; + bool orderByConnected_; }; class SYSTEMSETTINGS_EXPORT VpnConnection : public QObject @@ -157,6 +164,7 @@ class SYSTEMSETTINGS_EXPORT VpnConnection : public QObject Q_PROPERTY(QVariantList userRoutes READ userRoutes WRITE setUserRoutes NOTIFY userRoutesChanged) Q_PROPERTY(QVariantList serverRoutes READ serverRoutes WRITE setServerRoutes NOTIFY serverRoutesChanged) Q_PROPERTY(QVariantMap providerProperties READ providerProperties WRITE setProviderProperties NOTIFY providerPropertiesChanged) + Q_PROPERTY(bool connected READ connected NOTIFY connectedChanged) public: VpnConnection(const QString &path); @@ -211,6 +219,8 @@ class SYSTEMSETTINGS_EXPORT VpnConnection : public QObject QVariantMap providerProperties() const { return providerProperties_; } void setProviderProperties(const QVariantMap providerProperties) { updateMember(&VpnConnection::providerProperties_, providerProperties, &VpnConnection::providerPropertiesChanged); } + int connected() const { return state_ == VpnModel::Ready; } + signals: void nameChanged(); void stateChanged(); @@ -228,6 +238,7 @@ class SYSTEMSETTINGS_EXPORT VpnConnection : public QObject void userRoutesChanged(); void serverRoutesChanged(); void providerPropertiesChanged(); + void connectedChanged(); private: template