From 1b1e83aeb820df85ce7f10e81fe1f44deab2174e Mon Sep 17 00:00:00 2001 From: Christian Mollekopf Date: Thu, 21 Sep 2017 17:30:25 +0200 Subject: A login view --- .../contents/ui/KolabnowAccountSettings.qml | 21 +--- accounts/kolabnow/package/contents/ui/Login.qml | 62 +++++++++++ components/kube/contents/ui/Kube.qml | 27 ++++- components/kube/contents/ui/LoginView.qml | 39 +++++++ framework/qml/LoginAccount.qml | 117 +++++++++++++++++++++ framework/qmldir | 1 + framework/src/accounts/accountfactory.cpp | 2 + framework/src/accounts/accountfactory.h | 2 + framework/src/domain/settings/accountsettings.cpp | 6 +- framework/src/frameworkplugin.cpp | 9 ++ framework/src/keyring.cpp | 17 ++- framework/src/keyring.h | 14 ++- framework/src/sinkfabric.cpp | 2 +- 13 files changed, 290 insertions(+), 29 deletions(-) create mode 100644 accounts/kolabnow/package/contents/ui/Login.qml create mode 100644 components/kube/contents/ui/LoginView.qml create mode 100644 framework/qml/LoginAccount.qml diff --git a/accounts/kolabnow/package/contents/ui/KolabnowAccountSettings.qml b/accounts/kolabnow/package/contents/ui/KolabnowAccountSettings.qml index 8d8b6665..482ddc99 100644 --- a/accounts/kolabnow/package/contents/ui/KolabnowAccountSettings.qml +++ b/accounts/kolabnow/package/contents/ui/KolabnowAccountSettings.qml @@ -26,8 +26,8 @@ Item { property string accountId property string heading: qsTr("Connect your Kolab Now account") - property string subheadline: qsTr("To let Kube access your account, fill in email address, username, password and give the account a title that will be displayed inside Kube.") - property bool valid: accountField.acceptableInput && nameField.acceptableInput && emailField.acceptableInput && pwField.acceptableInput + property string subheadline: qsTr("To let Kube access your account, fill in your name, username, password.") + property bool valid: nameField.acceptableInput && emailField.acceptableInput KolabnowAccount.KolabnowSettings { id: kolabnowSettings @@ -85,23 +85,6 @@ Item { } placeholderText: qsTr("Your email address") } - - Kube.Label { - text: qsTr("Password") - Layout.alignment: Qt.AlignRight - } - - Kube.PasswordField { - id: pwField - Layout.fillWidth: true - - placeholderText: qsTr("Password of your email account") - text: kolabnowSettings.imapPassword - onTextChanged: { - kolabnowSettings.imapPassword = text - kolabnowSettings.smtpPassword = text - } - } } } } diff --git a/accounts/kolabnow/package/contents/ui/Login.qml b/accounts/kolabnow/package/contents/ui/Login.qml new file mode 100644 index 00000000..ae0213d5 --- /dev/null +++ b/accounts/kolabnow/package/contents/ui/Login.qml @@ -0,0 +1,62 @@ +/* + Copyright (C) 2016 Michael Bohlender, + Copyright (C) 2017 Christian Mollekopf, + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +import QtQuick 2.4 +import QtQuick.Layouts 1.1 +import org.kube.framework 1.0 as Kube +import org.kube.accounts.kolabnow 1.0 as KolabnowAccount + +Item { + property string accountId + property string heading: qsTr("Login") + property string subheadline: settings.accountName + + KolabnowAccount.KolabnowSettings { + id: settings + accountIdentifier: accountId + accountType: "kolabnow" + } + + function login(){ + settings.save() + } + + GridLayout { + anchors { + fill: parent + } + columns: 2 + columnSpacing: Kube.Units.largeSpacing + rowSpacing: Kube.Units.largeSpacing + + Kube.Label { + text: qsTr("Password") + Layout.alignment: Qt.AlignRight + } + + Kube.PasswordField { + id: pwField + Layout.fillWidth: true + + placeholderText: qsTr("Password of your email account") + text: settings.imapPassword + onTextChanged: settings.imapPassword = text + } + } +} diff --git a/components/kube/contents/ui/Kube.qml b/components/kube/contents/ui/Kube.qml index a9e8af09..603a499f 100644 --- a/components/kube/contents/ui/Kube.qml +++ b/components/kube/contents/ui/Kube.qml @@ -246,7 +246,6 @@ Controls2.ApplicationWindow { bottom: mainContent.bottom } Layout.fillWidth: true - initialItem: mailView Kube.Listener { filter: Kube.Messages.componentDone @@ -282,6 +281,10 @@ Controls2.ApplicationWindow { replaceView(logView) } + function setLoginView() { + pushView(loginView, {accountId: currentAccount}) + } + function openComposer(newMessage, recipients) { pushView(composerView, {newMessage: newMessage, recipients: recipients}) } @@ -296,6 +299,17 @@ Controls2.ApplicationWindow { } } + Component.onCompleted: { + if (!currentItem) { + if (!Kube.Keyring.isUnlocked(app.currentAccount)) { + setMailView(); + setLoginView() + } else { + setMailView(); + } + } + } + //These items are not visible until pushed onto the stack, so we keep them in resources instead of items resources: [ //Not components so we maintain state @@ -303,17 +317,20 @@ Controls2.ApplicationWindow { id: mailView anchors.fill: parent Controls2.StackView.onActivated: mailButton.checked = true + Controls2.StackView.onDeactivated: mailButton.checked = false }, PeopleView { id: peopleView anchors.fill: parent Controls2.StackView.onActivated: peopleButton.checked = true + Controls2.StackView.onDeactivated: peopleButton.checked = false }, //Not a component because otherwise we can't log stuff LogView { id: logView anchors.fill: parent Controls2.StackView.onActivated: logButton.checked = true + Controls2.StackView.onDeactivated: logButton.checked = false } ] //A component so it's always destroyed when we're done @@ -322,6 +339,7 @@ Controls2.ApplicationWindow { ComposerView { anchors.fill: parent Controls2.StackView.onActivated: composerButton.checked = true + Controls2.StackView.onDeactivated: composerButton.checked = false } } Component { @@ -329,6 +347,13 @@ Controls2.ApplicationWindow { AccountsView { anchors.fill: parent Controls2.StackView.onActivated: accountsButton.checked = true + Controls2.StackView.onDeactivated: accountsButton.checked = false + } + } + Component { + id: loginView + LoginView { + anchors.fill: parent } } } diff --git a/components/kube/contents/ui/LoginView.qml b/components/kube/contents/ui/LoginView.qml new file mode 100644 index 00000000..c5fdc396 --- /dev/null +++ b/components/kube/contents/ui/LoginView.qml @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2017 Michael Bohlender, + * Copyright (C) 2017 Christian Mollekopf, + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +import QtQuick 2.4 +import QtQuick.Layouts 1.1 +import QtQuick.Controls 1.3 as Controls +import QtQuick.Controls 2.0 +import org.kube.framework 1.0 as Kube + +Rectangle { + id: root + property alias accountId: login.accountId + + color: Kube.Colors.backgroundColor + + Kube.LoginAccount { + id: login + anchors { + fill: parent + bottomMargin: Kube.Units.largeSpacing + } + } +} diff --git a/framework/qml/LoginAccount.qml b/framework/qml/LoginAccount.qml new file mode 100644 index 00000000..7eaa47f4 --- /dev/null +++ b/framework/qml/LoginAccount.qml @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2016 Michael Bohlender, + * Copyright (C) 2017 Christian Mollekopf, + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +import QtQuick 2.7 +import QtQuick.Layouts 1.1 +import org.kube.framework 1.0 as Kube + +Item { + id: root + property string accountId + property bool canRemove: true + + function login() { + loader.item.login() + Kube.Fabric.postMessage(Kube.Messages.synchronize, {"accountId": loader.item.accountIdentifier}); + Kube.Fabric.postMessage(Kube.Messages.componentDone, {}) + } + + Kube.AccountFactory { + id: accountFactory + accountId: root.accountId + } + + Item { + + anchors { + fill: parent + margins: Kube.Units.largeSpacing * 2 + } + + Kube.Heading { + id: heading + text: loader.item ? loader.item.heading : "" + color: Kube.Colors.highlightColor + } + + Kube.Label { + id: subHeadline + + anchors { + left: heading.left + top: heading.bottom + } + + width: parent.width + text: loader.item ? loader.item.subheadline : "" + color: Kube.Colors.disabledTextColor + wrapMode: Text.Wrap + } + + Item { + id: accountEdit + anchors { + top: subHeadline.bottom + left: parent.left + right: parent.right + bottom: footer.top + topMargin: Kube.Units.largeSpacing * 2 + } + + Loader { + id: loader + anchors { + top: parent.top + left: parent.left + right: parent.right + } + //The initial size is somehow necessary so the loader is properly anchored + height: 10 + source: accountFactory.loginUi + onLoaded: item.accountId = root.accountId + } + Item { + id: spacer + anchors { + top: loader.bottom + left: parent.left + right: parent.right + bottom: parent.bottom + } + } + } + Keys.onReturnPressed: login() + + Item { + id: footer + anchors { + bottom: parent.bottom + left: parent.left + right: parent.right + topMargin: Kube.Units.largeSpacing * 2 + } + + Kube.Button { + anchors.right: parent.right + text: qsTr("Login") + onClicked: login() + } + } + } +} diff --git a/framework/qmldir b/framework/qmldir index 5eeaaeff..aef845b1 100644 --- a/framework/qmldir +++ b/framework/qmldir @@ -8,6 +8,7 @@ InlineAccountSwitcher 1.0 InlineAccountSwitcher.qml NewAccountDialog 1.0 NewAccountDialog.qml EditAccountDialog 1.0 EditAccountDialog.qml EditAccount 1.0 EditAccount.qml +LoginAccount 1.0 LoginAccount.qml OverlayDialog 1.0 OverlayDialog.qml Outbox 1.0 Outbox.qml People 1.0 People.qml diff --git a/framework/src/accounts/accountfactory.cpp b/framework/src/accounts/accountfactory.cpp index f1c4e29c..9726a2e0 100644 --- a/framework/src/accounts/accountfactory.cpp +++ b/framework/src/accounts/accountfactory.cpp @@ -64,6 +64,7 @@ void AccountFactory::loadPackage() if (!package.isValid()) { qWarning() << "Failed to load account package: " << "org.kube.accounts." + mAccountType; mUiPath.clear(); + mLoginUi.clear(); mName.clear(); mIcon.clear(); emit accountLoaded(); @@ -71,6 +72,7 @@ void AccountFactory::loadPackage() } Q_ASSERT(package.isValid()); mUiPath = package.filePath("mainscript"); + mLoginUi = package.filePath("ui", "Login.qml"); mName = package.metadata().name(); mIcon = package.metadata().iconName(); emit accountLoaded(); diff --git a/framework/src/accounts/accountfactory.h b/framework/src/accounts/accountfactory.h index b57854e5..21747df5 100644 --- a/framework/src/accounts/accountfactory.h +++ b/framework/src/accounts/accountfactory.h @@ -33,6 +33,7 @@ class AccountFactory : public QObject Q_PROPERTY(QString name MEMBER mName READ name NOTIFY accountLoaded); Q_PROPERTY(QString icon MEMBER mIcon NOTIFY accountLoaded); Q_PROPERTY(QString uiPath MEMBER mUiPath NOTIFY accountLoaded); + Q_PROPERTY(QString loginUi MEMBER mLoginUi NOTIFY accountLoaded); public: explicit AccountFactory(QObject *parent = Q_NULLPTR); @@ -49,5 +50,6 @@ private: QString mName; QString mIcon; QString mUiPath; + QString mLoginUi; QByteArray mAccountType; }; diff --git a/framework/src/domain/settings/accountsettings.cpp b/framework/src/domain/settings/accountsettings.cpp index 09cdf279..c174adfe 100644 --- a/framework/src/domain/settings/accountsettings.cpp +++ b/framework/src/domain/settings/accountsettings.cpp @@ -292,7 +292,7 @@ void AccountSettings::saveImapResource() {"server", mImapServer}, {"username", mImapUsername} }); - Kube::Keyring{mAccountIdentifier}.storePassword(mImapIdentifier, mImapPassword); + Kube::AccountKeyring{mAccountIdentifier}.storePassword(mImapIdentifier, mImapPassword); } void AccountSettings::saveCardDavResource() @@ -301,7 +301,7 @@ void AccountSettings::saveCardDavResource() {"server", mCardDavServer}, {"username", mCardDavUsername} }); - Kube::Keyring{mAccountIdentifier}.storePassword(mCardDavIdentifier, mCardDavPassword); + Kube::AccountKeyring{mAccountIdentifier}.storePassword(mCardDavIdentifier, mCardDavPassword); } void AccountSettings::saveMaildirResource() @@ -317,7 +317,7 @@ void AccountSettings::saveMailtransportResource() {"server", mSmtpServer}, {"username", mSmtpUsername} }); - Kube::Keyring{mAccountIdentifier}.storePassword(mMailtransportIdentifier, mSmtpPassword); + Kube::AccountKeyring{mAccountIdentifier}.storePassword(mMailtransportIdentifier, mSmtpPassword); } void AccountSettings::saveIdentity() diff --git a/framework/src/frameworkplugin.cpp b/framework/src/frameworkplugin.cpp index b8cad45d..1e1a169e 100644 --- a/framework/src/frameworkplugin.cpp +++ b/framework/src/frameworkplugin.cpp @@ -38,6 +38,7 @@ #include "clipboardproxy.h" #include "webengineprofile.h" #include "startupcheck.h" +#include "keyring.h" #include @@ -55,6 +56,13 @@ static QObject *webengineprofile_singletontype_provider(QQmlEngine *engine, QJSE return new WebEngineProfile; } +static QObject *keyring_singletontype_provider(QQmlEngine *engine, QJSEngine *scriptEngine) +{ + Q_UNUSED(engine) + Q_UNUSED(scriptEngine) + return new Kube::Keyring; +} + void FrameworkPlugin::registerTypes (const char *uri) { qmlRegisterType(uri, 1, 0, "FolderListModel"); @@ -80,4 +88,5 @@ void FrameworkPlugin::registerTypes (const char *uri) qmlRegisterType(uri, 1, 0, "Clipboard"); qmlRegisterType(uri, 1, 0, "StartupCheck"); qmlRegisterSingletonType(uri, 1, 0, "WebEngineProfile", webengineprofile_singletontype_provider); + qmlRegisterSingletonType(uri, 1, 0, "Keyring", keyring_singletontype_provider); } diff --git a/framework/src/keyring.cpp b/framework/src/keyring.cpp index 759d0c4c..e3fdb1cb 100644 --- a/framework/src/keyring.cpp +++ b/framework/src/keyring.cpp @@ -23,20 +23,31 @@ using namespace Kube; -Keyring::Keyring(const QByteArray &accountId, QObject *parent) +Keyring::Keyring() + : QObject() +{ + +} + +bool Keyring::isUnlocked(const QByteArray &accountId) +{ + return false; +} + +AccountKeyring::AccountKeyring(const QByteArray &accountId, QObject *parent) : QObject(parent), mAccountIdentifier(accountId) { } -void Keyring::storePassword(const QByteArray &resourceId, const QString &password) +void AccountKeyring::storePassword(const QByteArray &resourceId, const QString &password) { QSettings settings{mAccountIdentifier + ".keyring", QSettings::IniFormat}; settings.setValue(resourceId, password); Sink::SecretStore::instance().insert(resourceId, password); } -void Keyring::unlock() +void AccountKeyring::unlock() { QSettings settings{mAccountIdentifier + ".keyring", QSettings::IniFormat}; for (const auto &resourceId : settings.allKeys()) { diff --git a/framework/src/keyring.h b/framework/src/keyring.h index ee9c3577..ce4e137d 100644 --- a/framework/src/keyring.h +++ b/framework/src/keyring.h @@ -24,12 +24,22 @@ namespace Kube { class Keyring : public QObject { Q_OBJECT public: - Keyring(const QByteArray &accountId, QObject *parent = nullptr); + Keyring(); + Q_INVOKABLE bool isUnlocked(const QByteArray &accountId); + +private: + Q_DISABLE_COPY(Keyring); +}; + +class AccountKeyring : public QObject { + Q_OBJECT +public: + AccountKeyring(const QByteArray &accountId, QObject *parent = nullptr); void storePassword(const QByteArray &resourceId, const QString &password); void unlock(); private: - Q_DISABLE_COPY(Keyring); + Q_DISABLE_COPY(AccountKeyring); QByteArray mAccountIdentifier; }; diff --git a/framework/src/sinkfabric.cpp b/framework/src/sinkfabric.cpp index 954186bb..87b243e0 100644 --- a/framework/src/sinkfabric.cpp +++ b/framework/src/sinkfabric.cpp @@ -135,7 +135,7 @@ public: } if (id == "unlockKeyring") { auto accountId = message["accountId"].value(); - Kube::Keyring{accountId}.unlock(); + Kube::AccountKeyring{accountId}.unlock(); } } -- cgit v1.2.3