diff --git a/libnymea-app-core/engine.cpp b/libnymea-app-core/engine.cpp index f3d07012..68dcdd24 100644 --- a/libnymea-app-core/engine.cpp +++ b/libnymea-app-core/engine.cpp @@ -38,6 +38,7 @@ #include "connection/awsclient.h" #include "system/systemcontroller.h" #include "configuration/networkmanager.h" +#include "usersmanager.h" #include "connection/tcpsockettransport.h" #include "connection/websockettransport.h" @@ -54,7 +55,8 @@ Engine::Engine(QObject *parent) : m_logManager(new LogManager(m_jsonRpcClient, this)), m_tagsManager(new TagsManager(m_jsonRpcClient, this)), m_nymeaConfiguration(new NymeaConfiguration(m_jsonRpcClient, this)), - m_systemController(new SystemController(m_jsonRpcClient, this)) + m_systemController(new SystemController(m_jsonRpcClient, this)), + m_usersManager(new UsersManager(m_jsonRpcClient, this)) { m_connection->registerTransport(new TcpSocketTransportFactory()); m_connection->registerTransport(new WebsocketTransportFactory()); @@ -122,6 +124,11 @@ SystemController *Engine::systemController() const return m_systemController; } +UsersManager *Engine::usersManager() const +{ + return m_usersManager; +} + void Engine::deployCertificate() { if (!m_jsonRpcClient->connected()) { diff --git a/libnymea-app-core/engine.h b/libnymea-app-core/engine.h index 70e87dd0..b077678e 100644 --- a/libnymea-app-core/engine.h +++ b/libnymea-app-core/engine.h @@ -45,6 +45,7 @@ class TagsManager; class NymeaConfiguration; class SystemController; class NetworkManager; +class UsersManager; class Engine : public QObject { @@ -57,6 +58,7 @@ class Engine : public QObject Q_PROPERTY(JsonRpcClient* jsonRpcClient READ jsonRpcClient CONSTANT) Q_PROPERTY(NymeaConfiguration* nymeaConfiguration READ nymeaConfiguration CONSTANT) Q_PROPERTY(SystemController* systemController READ systemController CONSTANT) + Q_PROPERTY(UsersManager* usersManager READ usersManager CONSTANT) public: explicit Engine(QObject *parent = nullptr); @@ -73,6 +75,7 @@ public: LogManager *logManager() const; NymeaConfiguration *nymeaConfiguration() const; SystemController *systemController() const; + UsersManager *usersManager() const; Q_INVOKABLE void deployCertificate(); @@ -86,6 +89,7 @@ private: TagsManager *m_tagsManager; NymeaConfiguration *m_nymeaConfiguration; SystemController *m_systemController; + UsersManager *m_usersManager; private slots: void onConnectedChanged(); diff --git a/libnymea-app-core/libnymea-app-core.pro b/libnymea-app-core/libnymea-app-core.pro index c1fc4788..ebf067f5 100644 --- a/libnymea-app-core/libnymea-app-core.pro +++ b/libnymea-app-core/libnymea-app-core.pro @@ -90,6 +90,8 @@ SOURCES += \ configuration/mqttpolicies.cpp \ models/devicemodel.cpp \ system/systemcontroller.cpp \ + usersmanager.cpp \ + HEADERS += \ configuration/networkmanager.h \ @@ -157,6 +159,7 @@ HEADERS += \ configuration/mqttpolicies.h \ models/devicemodel.h \ system/systemcontroller.h \ + usersmanager.h \ ubports: { DEFINES += UBPORTS diff --git a/libnymea-app-core/usersmanager.cpp b/libnymea-app-core/usersmanager.cpp new file mode 100644 index 00000000..da931238 --- /dev/null +++ b/libnymea-app-core/usersmanager.cpp @@ -0,0 +1,20 @@ +#include "usersmanager.h" + +#include + +UsersManager::UsersManager(JsonRpcClient *client, QObject *parent): + JsonHandler(parent), + m_jsonRpcClient(client) +{ + m_jsonRpcClient->registerNotificationHandler(this, "notificationReceived"); +} + +QString UsersManager::nameSpace() const +{ + return "Users"; +} + +void UsersManager::notificationReceived(const QVariantMap &data) +{ + qDebug() << "Users notification" << data; +} diff --git a/libnymea-app-core/usersmanager.h b/libnymea-app-core/usersmanager.h new file mode 100644 index 00000000..5b3684cc --- /dev/null +++ b/libnymea-app-core/usersmanager.h @@ -0,0 +1,23 @@ +#ifndef USERSMANAGER_H +#define USERSMANAGER_H + +#include + +#include "jsonrpc/jsonrpcclient.h" + +class UsersManager: public JsonHandler +{ + Q_OBJECT +public: + explicit UsersManager(JsonRpcClient *client, QObject *parent = nullptr); + + QString nameSpace() const override; + +private slots: + void notificationReceived(const QVariantMap &data); + +private: + JsonRpcClient *m_jsonRpcClient = nullptr; +}; + +#endif // USERSMANAGER_H diff --git a/nymea-app/resources.qrc b/nymea-app/resources.qrc index 966f82c6..32b0a312 100644 --- a/nymea-app/resources.qrc +++ b/nymea-app/resources.qrc @@ -208,5 +208,6 @@ ui/magic/scripting/LineNumbers.qml ui/magic/scripting/CompletionBox.qml ui/magic/scripting/EditorPane.qml + ui/system/UsersSettingsPage.qml diff --git a/nymea-app/ui/SettingsPage.qml b/nymea-app/ui/SettingsPage.qml index 6a68d943..59a31f73 100644 --- a/nymea-app/ui/SettingsPage.qml +++ b/nymea-app/ui/SettingsPage.qml @@ -70,6 +70,21 @@ Page { } } + Pane { + Layout.fillWidth: true + Material.elevation: layout.isGrid ? 1 : 0 + padding: 0 + NymeaListItemDelegate { + width: parent.width + iconName: "../images/lock-closed.svg" + text: qsTr("Authentication") + subText: qsTr("Configure who can log in") + prominentSubText: false + wrapTexts: false + onClicked: pageStack.push(Qt.resolvedUrl("system/UsersSettingsPage.qml")) + } + } + Pane { Layout.fillWidth: true Material.elevation: layout.isGrid ? 1 : 0 diff --git a/nymea-app/ui/system/UsersSettingsPage.qml b/nymea-app/ui/system/UsersSettingsPage.qml new file mode 100644 index 00000000..97aecc29 --- /dev/null +++ b/nymea-app/ui/system/UsersSettingsPage.qml @@ -0,0 +1,237 @@ +import QtQuick 2.5 +import QtQuick.Controls 2.1 +import QtQuick.Controls.Material 2.1 +import QtQuick.Layouts 1.1 +import Nymea 1.0 +import "../components" + +Page { + id: root + header: NymeaHeader { + text: qsTr("Authentication") + backButtonVisible: true + onBackPressed: pageStack.pop() + } + + ColumnLayout { + id: settingsGrid + anchors { horizontalCenter: parent.horizontalCenter; top: parent.top; margins: app.margins } + width: Math.min(500, parent.width - app.margins * 2) + + RowLayout { + Layout.fillWidth: true + spacing: app.margins + Label { + text: qsTr("Name") + } + TextField { + id: nameTextField + Layout.fillWidth: true + text: engine.nymeaConfiguration.serverName + } + Button { + text: qsTr("OK") + visible: nameTextField.displayText !== engine.nymeaConfiguration.serverName + onClicked: engine.nymeaConfiguration.serverName = nameTextField.displayText + } + } + + RowLayout { + Layout.fillWidth: true + visible: engine.jsonRpcClient.ensureServerVersion("4.1") && engine.systemController.automaticTimeAvailable + Label { + text: qsTr("Set date and time automatically") + Layout.fillWidth: true + } + CheckBox { + checked: engine.systemController.automaticTime + onClicked: { + engine.systemController.automaticTime = checked + } + } + } + + RowLayout { + Layout.fillWidth: true + spacing: app.margins + Layout.preferredHeight: dateButton.implicitHeight + visible: engine.jsonRpcClient.ensureServerVersion("4.1") + Label { + text: qsTr("Date") + Layout.fillWidth: true + } + Label { + text: engine.systemController.serverTime.toLocaleDateString() + Layout.fillWidth: true + horizontalAlignment: Text.AlignRight + } + Button { + id: dateButton + visible: !engine.systemController.automaticTime && engine.systemController.timeManagementAvailable + contentItem: Item { + ColorIcon { + name: "../images/edit.svg" + color: app.foregroundColor + anchors.centerIn: parent + height: parent.height + width: height + } + } + + onClicked: { + var popup = datePickerComponent.createObject(root, {dateTime: engine.systemController.serverTime}) + popup.accepted.connect(function() { + print("setting new date", popup.dateTime) + engine.systemController.serverTime = popup.dateTime + }) + popup.open(); + + } + } + } + RowLayout { + Layout.fillWidth: true + spacing: app.margins + Layout.preferredHeight: timeButton.implicitHeight + visible: engine.jsonRpcClient.ensureServerVersion("4.1") + Label { + text: qsTr("Time") + Layout.fillWidth: true + } + Label { + text: engine.systemController.serverTime.toLocaleTimeString(/*Locale.ShortTimeString*/) + Layout.fillWidth: true + horizontalAlignment: Text.AlignRight + } + Button { + id: timeButton + visible: !engine.systemController.automaticTime && engine.systemController.timeManagementAvailable + contentItem: Item { + ColorIcon { + name: "../images/edit.svg" + color: app.foregroundColor + anchors.centerIn: parent + height: parent.height + width: height + } + } + + onClicked: { + var popup = timePickerComponent.createObject(root, {hour: engine.systemController.serverTime.getHours(), minute: engine.systemController.serverTime.getMinutes()}) + popup.accepted.connect(function() { + var date = new Date(engine.systemController.serverTime) + date.setHours(popup.hour); + date.setMinutes(popup.minute) + engine.systemController.serverTime = date; + }) + popup.open(); + + } + } + } + + + RowLayout { + Layout.fillWidth: true + spacing: app.margins + visible: engine.jsonRpcClient.ensureServerVersion("4.1") + Label { + Layout.fillWidth: true + text: qsTr("Time zone") + } + ComboBox { + Layout.minimumWidth: 200 + model: engine.systemController.timeZones + currentIndex: model.indexOf(engine.systemController.serverTimeZone) + onActivated: { + engine.systemController.serverTimeZone = currentText; + } + } + } + + Button { + Layout.fillWidth: true + text: qsTr("Reboot %1:core").arg(app.systemName) + visible: engine.systemController.powerManagementAvailable + onClicked: { + var dialog = Qt.createComponent(Qt.resolvedUrl("../components/MeaDialog.qml")); + var text = qsTr("Are you sure you want to reboot your %1:core sytem now?").arg(app.systemName) + var popup = dialog.createObject(app, + { + headerIcon: "../images/dialog-warning-symbolic.svg", + title: qsTr("Reboot %1:core").arg(app.systemName), + text: text, + standardButtons: Dialog.Ok | Dialog.Cancel + }); + popup.open(); + popup.accepted.connect(function() { + engine.systemController.reboot() + }) + } + } + Button { + Layout.fillWidth: true + text: qsTr("Shutdown %1:core").arg(app.systemName) + visible: engine.systemController.powerManagementAvailable + onClicked: { + var dialog = Qt.createComponent(Qt.resolvedUrl("../components/MeaDialog.qml")); + var text = qsTr("Are you sure you want to shut down your %1:core sytem now?").arg(app.systemName) + var popup = dialog.createObject(app, + { + headerIcon: "../images/dialog-warning-symbolic.svg", + title: qsTr("Shut down %1:core").arg(app.systemName), + text: text, + standardButtons: Dialog.Ok | Dialog.Cancel + }); + popup.open(); + popup.accepted.connect(function() { + engine.systemController.shutdown() + }) + } + } + } + + Component { + id: timePickerComponent + Dialog { + id: timePicker + property int maxSize: Math.min(parent.width, parent.height) + property int size: Math.min(maxSize, 500) + property alias hour: p.hour + property alias minute: p.minute + width: size - 80 + height: size + x: (parent.width - width) / 2 + y: (parent.height - height) / 2 + + TimePicker { + id: p + width: parent.width + height: parent.height + } + standardButtons: Dialog.Ok | Dialog.Cancel + } + } + + Component { + id: datePickerComponent + Dialog { + id: datePicker + property int maxSize: Math.min(parent.width, parent.height) + property int size: Math.min(maxSize, 500) + property alias dateTime: p.date + width: size - 80 + height: size + x: (parent.width - width) / 2 + y: (parent.height - height) / 2 + + DatePicker { + id: p + width: parent.width + height: parent.height + date: datePicker.dateTime + } + standardButtons: Dialog.Ok | Dialog.Cancel + } + } +}