diff --git a/README.md b/README.md new file mode 100644 index 0000000..8703a87 --- /dev/null +++ b/README.md @@ -0,0 +1,52 @@ +# KAccounts Integration + +Integration library and QML module for Accounts-SSO and SignOn-SSO + +# Introduction + +KAccounts Integration provides a way to share accounts data such as login tokens and general +user information (like usernames and such) between various applications. + +The KAccounts library is a KDE Frameworks style abstraction layer on top of the Accounts-SSO +and SignOnD libraries, which uses a combination of models and jobs to expose the functionality +of those. + +The kaccounts QML plugin exposes that functionality directly to Qt Quick based applications, +and using the classes only requires importing the module like so: + +``` +import org.kde.kaccounts 1.2 as KAccounts +``` + +The main functionality in the library can be accessed through the various classes below, and +the accounts manager can be accessed directly through ```KAccounts::accountsManager()```. The +other central classes are: + +## Models + +* AccountsModel +* ServicesModel +* ProvidersModel + +## Jobs + +* AccountServiceToggleJob +* ChangeAccountDisplayNameJob +* CreateAccountJob +* RemoveAccountJob + +# KDE Control Module + +The Online Accounts KCM is the main user-visible point for KAccounts, and can be accessed +either through System Settings, or directly from any system menu which allows launching of +KCMs directly (including KRunner). It is built using the Qt Quick module mentioned above, +and uses Kirigami as its base. + +# Provider and Service files + +If you plan on creating new providers and services, you will need to register those with +the accounts manager. Two cmake macros are provided to assist you in the creation and +installation of these files, and further assists in translation integration for them: + +* kaccounts_add_provider +* kaccounts_add_service diff --git a/src/declarative/kaccountsdeclarativeplugin.cpp b/src/declarative/kaccountsdeclarativeplugin.cpp index 702b8b8..89f0c18 100644 --- a/src/declarative/kaccountsdeclarativeplugin.cpp +++ b/src/declarative/kaccountsdeclarativeplugin.cpp @@ -1,28 +1,52 @@ /************************************************************************************* * Copyright (C) 2015 by Aleix Pol * + * Copyright (C) 2020 by Dan Leinir Turthra Jensen * * * * 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 * *************************************************************************************/ #include "kaccountsdeclarativeplugin.h" -#include "createaccountjob.h" + +#include "accountsmodel.h" +#include "servicesmodel.h" +#include "providersmodel.h" + #include "accountservicetogglejob.h" +#include "changeaccountdisplaynamejob.h" +#include "createaccountjob.h" +#include "removeaccountjob.h" + #include void KAccountsDeclarativePlugin::registerTypes(const char* uri) { - qmlRegisterType(uri, 1, 0, "CreateAccountJob"); - qmlRegisterType(uri, 1, 1, "AccountServiceToggleJob"); + // Version 1.0 + // Consider this registration deprecated - use the one named ...Job below instead + qmlRegisterType( uri, 1, 0, "CreateAccount"); + + // Version 1.1 + // Consider this registration deprecated - use the one named ...Job below instead + qmlRegisterType( uri, 1, 1, "AccountServiceToggle"); + + // Version 1.2 + qmlRegisterType( uri, 1, 2, "AccountsModel"); + qmlRegisterType( uri, 1, 2, "ProvidersModel"); + qmlRegisterType( uri, 1, 2, "ServicesModel"); + + qmlRegisterType( uri, 1, 2, "AccountServiceToggleJob"); + qmlRegisterType( uri, 1, 2, "ChangeAccountDisplayNameJob"); + qmlRegisterType( uri, 1, 2, "CreateAccountJob"); + qmlRegisterType( uri, 1, 2, "RemoveAccountJob"); } diff --git a/src/kcm/accounts.cpp b/src/kcm/accounts.cpp index f428474..dcc9cf0 100644 --- a/src/kcm/accounts.cpp +++ b/src/kcm/accounts.cpp @@ -1,38 +1,40 @@ /*************************************************************************** * * * Copyright 2019 Nicolas Fella * * * * 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 . * ***************************************************************************/ #include "accounts.h" #include #include #include K_PLUGIN_CLASS_WITH_JSON(AccountsSettings, "kcm_kaccounts.json") AccountsSettings::AccountsSettings(QObject* parent, const QVariantList& args) : KQuickAddons::ConfigModule(parent, args) { KAboutData* about = new KAboutData(QStringLiteral("kcm_kaccounts"), i18n("Accounts"), QStringLiteral("1.0"), QString(), KAboutLicense::LGPL); about->addAuthor(i18n("Sebastian Kügler"), QString(), QStringLiteral("sebas@kde.org")); + about->addAuthor(i18n("Dan Leinir Turthra Jensen"), QString(), QStringLiteral("admin@leinir.dk"), QString(), QStringLiteral("leinir")); setAboutData(about); + setButtons(KQuickAddons::ConfigModule::NoAdditionalButton); } #include "accounts.moc" diff --git a/src/kcm/package/contents/ui/AccountDetails.qml b/src/kcm/package/contents/ui/AccountDetails.qml new file mode 100644 index 0000000..280cd63 --- /dev/null +++ b/src/kcm/package/contents/ui/AccountDetails.qml @@ -0,0 +1,169 @@ +/* + * Copyright 2019 Nicolas Fella + * Copyright 2020 Dan Leinir Turthra Jensen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 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 Library General Public License for more details + * + * You should have received a copy of the GNU Library 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.12 +import QtQuick.Controls 2.12 as Controls +import QtQuick.Layouts 1.12 + +import org.kde.kirigami 2.4 as Kirigami +import org.kde.kcm 1.2 + +import org.kde.kaccounts 1.2 as KAccounts + +SimpleKCM { + id: component; + + title: i18n("Account Details") + + property alias model: servicesList.model + + RemoveAccountDialog { + id: accountRemover + parent: component + accountId: servicesList.model.accountId + displayName: servicesList.model.accountDisplayName + providerName: servicesList.model.accountProviderName + onAccountRemoved: kcm.pop() + } + + RenameAccountDialog { + id: accountRenamer + parent: component + accountId: servicesList.model.accountId + currentDisplayName: servicesList.model.accountDisplayName + } + + Component { + id: serviceToggleJob + KAccounts.AccountServiceToggleJob { } + } + + header: RowLayout { + Layout.fillWidth: true + Layout.margins: Kirigami.Units.smallSpacing + spacing: Kirigami.Units.smallSpacing + Kirigami.Icon { + source: model.accountIconName + Layout.preferredWidth: Kirigami.Units.iconSizes.large + Layout.preferredHeight: Kirigami.Units.iconSizes.large + } + Controls.Label { + Layout.fillWidth: true + text: { + if (model.accountDisplayName.length > 0 && model.accountProviderName.length > 0) { + return i18n("%1 (%2)", model.accountDisplayName, model.accountProviderName) + } else if (model.accountDisplayName.length > 0) { + return model.accountDisplayName + } else { + return i18n("%1 account", model.accountProviderName) + } + } + } + Controls.ToolButton { + icon.name: "edit-entry" + display: Controls.AbstractButton.IconOnly + Layout.preferredHeight: Kirigami.Units.iconSizes.large + Layout.preferredWidth: Kirigami.Units.iconSizes.large + onClicked: accountRenamer.open(); + Controls.ToolTip { + visible: parent.hovered && !parent.pressed + text: i18nc("Button which spawns a dialog allowing the user to change the displayed account's human-readable name", "Change Account Display Name") + delay: Kirigami.Units.toolTipDelay + timeout: 5000 + y: parent.height + } + } + } + + footer: RowLayout { + Controls.Button { + Layout.alignment: Qt.AlignRight + text: i18n("Remove This Account") + icon.name: "edit-delete-remove" + onClicked: accountRemover.open(); + } + } + + Controls.Frame { + Layout.fillWidth: true + background: Rectangle { + Kirigami.Theme.colorSet: Kirigami.Theme.Button + border { + width: 1 + color: Qt.tint(Kirigami.Theme.textColor, Qt.rgba(Kirigami.Theme.backgroundColor.r, Kirigami.Theme.backgroundColor.g, Kirigami.Theme.backgroundColor.b, 0.8)) + } + } + ColumnLayout { + anchors.fill: parent + spacing: Kirigami.Units.smallSpacing + Controls.Label { + visible: servicesList.count === 0 + Layout.fillWidth: true + Layout.minimumHeight: component.height / 3 + enabled: false + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignHCenter + wrapMode: Text.Wrap + text: i18nc("A text shown when an account has no configurable services", "There are no configurable services available for this account. You can still change its display name by clicking the edit icon above.") + } + Kirigami.Heading { + Layout.fillWidth: true + Layout.leftMargin: Kirigami.Units.smallSpacing + visible: servicesList.count > 0 + level: 3 + text: i18nc("Heading for a list of services available with this account", "Use This Account For") + } + Repeater { + id: servicesList + delegate: ColumnLayout { + Layout.fillWidth: true + Layout.leftMargin: Kirigami.Units.smallSpacing + spacing: 0 + Controls.CheckBox { + id: serviceCheck + Layout.fillWidth: true + checked: model.enabled + text: model.displayName + Binding { + target: serviceCheck + property: "checked" + value: model.enabled + } + onClicked: { + var job = serviceToggleJob.createObject(component, { "accountId": servicesList.model.accountId, "serviceId": model.name, "serviceEnabled": !model.enabled }) + job.start() + } + } + Controls.Label { + Layout.fillWidth: true + Layout.leftMargin: Kirigami.Units.iconSizes.small + Kirigami.Units.smallSpacing * 2 + visible: text.length > 0 + text: model.description + wrapMode: Text.Wrap + } + } + } + Item { + Layout.fillWidth: true + height: Kirigami.Units.smallSpacing + } + } + } +} diff --git a/src/kcm/package/contents/ui/Accounts.qml b/src/kcm/package/contents/ui/Accounts.qml index ae4064f..f97e5ff 100644 --- a/src/kcm/package/contents/ui/Accounts.qml +++ b/src/kcm/package/contents/ui/Accounts.qml @@ -1,124 +1,102 @@ /* * Copyright 2015 Martin Klapetek * Copyright 2020 Dan Leinir Turthra Jensen * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2 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 Library General Public License for more details * * You should have received a copy of the GNU Library 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.11 -import QtQuick.Controls 2.0 as Controls +import QtQuick 2.12 +import QtQuick.Controls 2.12 as Controls +import QtQuick.Layouts 1.12 import org.kde.kirigami 2.7 as Kirigami - -import org.kde.kaccounts 1.0 import org.kde.kcm 1.2 -import Ubuntu.OnlineAccounts 0.1 as OA +import org.kde.kaccounts 1.2 as KAccounts ScrollViewKCM { id: kaccountsRoot // Existing accounts view: ListView { - model: OA.AccountServiceModel { - id: accountsModel - service: "global" - includeDisabled: true - } + model: KAccounts.AccountsModel { } delegate: Kirigami.SwipeListItem { id: accountDelegate width: ListView.view.width - contentItem: Controls.Label { - text: { - if (model.displayName.length > 0 && model.providerName.length > 0) { - return i18n("%1 (%2)", model.displayName, model.providerName) - } else if (model.displayName.length > 0) { - return model.displayName - } else { - return i18n("%1 account", model.providerName) - } + contentItem: RowLayout { + implicitWidth: accountDelegate.ListView.view.width + implicitHeight: Kirigami.Units.iconSizes.large + Kirigami.Units.smallSpacing * 2 + spacing: Kirigami.Units.smallSpacing + Kirigami.Icon { + source: model.iconName + Layout.preferredWidth: Kirigami.Units.iconSizes.large + Layout.preferredHeight: Kirigami.Units.iconSizes.large } - - OA.Account { - id: account - objectHandle: model.accountHandle + Controls.Label { + Layout.fillWidth: true + text: { + if (model.displayName.length > 0 && model.providerName.length > 0) { + return i18n("%1 (%2)", model.displayName, model.providerName) + } else if (model.displayName.length > 0) { + return model.displayName + } else { + return i18n("%1 account", model.providerName) + } + } } } actions: [ Kirigami.Action { text: i18nc("Tooltip for an action which will offer the user to remove the mentioned account", "Remove %1", accountDelegate.contentItem.text) iconName: "edit-delete-remove" onTriggered: { - accountRemovalDlg.account = account; - accountRemovalDlg.displayName = model.displayName; - accountRemovalDlg.providerName = model.providerName; - accountRemovalDlg.open(); + accountRemover.accountId = model.id; + accountRemover.displayName = model.displayName; + accountRemover.providerName = model.providerName; + accountRemover.open(); } } ] - onClicked: kcm.push("AvailableServices.qml", {accountId: model.accountId}) + onClicked: kcm.push("AccountDetails.qml", {model: model.services}) } Controls.Label { - anchors { - fill: parent - margins: Kirigami.Units.largeSpacing - } - verticalAlignment: Text.AlignVCenter + visible: view.count === 0 + anchors.fill: parent + clip: true + enabled: false horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter wrapMode: Text.Wrap - visible: parent.count === 0 - opacity: 0.5 - text: i18nc("A text shown when a user has not yet added any accounts", "You have not added any accounts yet.\nClick the \"Add new Account\" button below to do so.") + text: i18nc("A text shown when a user has not yet added any accounts", "You have not added any accounts yet.\n\nClick on \"Add New Account...\" below to do so.") } } - MessageBoxSheet { - id: accountRemovalDlg + + RemoveAccountDialog { + id: accountRemover parent: kaccountsRoot - property QtObject account - property string displayName - property string providerName - title: i18nc("The title for a dialog which lets you remove an account", "Remove Account?") - text: { - if (accountRemovalDlg.displayName.length > 0 && accountRemovalDlg.providerName.length > 0) { - return i18nc("The text for a dialog which lets you remove an account when both provider name and account name are available", "Are you sure you wish to remove the \"%1\" account \"%2\"?", accountRemovalDlg.providerName, accountRemovalDlg.displayName) - } else if (accountRemovalDlg.displayName.length > 0) { - return i18nc("The text for a dialog which lets you remove an account when only the account name is available", "Are you sure you wish to remove the account \"%1\"?", accountRemovalDlg.displayName) - } else { - return i18nc("The text for a dialog which lets you remove an account when only the provider name is available", "Are you sure you wish to remove this \"%1\" account?", accountRemovalDlg.providerName) - } - } - actions: [ - Kirigami.Action { - text: i18nc("The label for a button which will cause the removal of a specified account", "Remove Account") - onTriggered: { accountRemovalDlg.account.remove(); } - } - ] } footer: RowLayout { Controls.Button { Layout.alignment: Qt.AlignRight - text: i18n("Add new Account") + text: i18n("Add New Account...") icon.name: "contact-new" onClicked: kcm.push("AvailableAccounts.qml") } } } diff --git a/src/kcm/package/contents/ui/AvailableAccounts.qml b/src/kcm/package/contents/ui/AvailableAccounts.qml index e0db0f1..1079c62 100644 --- a/src/kcm/package/contents/ui/AvailableAccounts.qml +++ b/src/kcm/package/contents/ui/AvailableAccounts.qml @@ -1,53 +1,93 @@ /* * Copyright 2019 Nicolas Fella + * Copyright 2020 Dan Leinir Turthra Jensen * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2 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 Library General Public License for more details * * You should have received a copy of the GNU Library 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.11 -import QtQuick.Controls 2.0 as Controls +import QtQuick 2.12 +import QtQuick.Controls 2.12 as Controls +import QtQuick.Layouts 1.12 + import org.kde.kirigami 2.4 as Kirigami -import org.kde.kaccounts 1.0 import org.kde.kcm 1.2 -import Ubuntu.OnlineAccounts 0.1 as OA + +import org.kde.kaccounts 1.2 as KAccounts ScrollViewKCM { id: root - title: i18n("Add new Account") + title: i18n("Add New Account") view: ListView { - model: OA.ProviderModel {} - - delegate: Kirigami.BasicListItem { - icon: model.iconName - label: model.displayName - width: parent.width + model: KAccounts.ProvidersModel {} + delegate: Kirigami.AbstractListItem { + id: accountDelegate + width: ListView.view.width + enabled: model.supportsMultipleAccounts === true || model.accountsCount === 0 + contentItem: RowLayout { + implicitWidth: accountDelegate.ListView.view.width + implicitHeight: Kirigami.Units.iconSizes.large + Kirigami.Units.smallSpacing * 2 + spacing: Kirigami.Units.smallSpacing + Kirigami.Icon { + source: model.iconName + Layout.preferredWidth: Kirigami.Units.iconSizes.large + Layout.preferredHeight: Kirigami.Units.iconSizes.large + Item { + visible: model.accountsCount > 0 + anchors { + bottom: parent.bottom + right: parent.right + } + height: parent.height / 3 + width: height + Rectangle { + anchors.fill: parent + radius: height / 2 + color: Kirigami.Theme.highlightColor + border { + width: 1 + color: Kirigami.Theme.highlightedTextColor + } + } + Controls.Label { + anchors.fill: parent + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + color: Kirigami.Theme.highlightedTextColor + text: model.accountsCount + } + } + } + Controls.Label { + Layout.fillWidth: true + text: model.displayName + "\n" + model.description; + } + } onClicked: { - var job = jobComponent.createObject(root, { "providerName": providerId }) + var job = jobComponent.createObject(root, { "providerName": model.name }) job.start() } } } Component { id: jobComponent - CreateAccountJob { + KAccounts.CreateAccountJob { onFinished: kcm.pop() } } } diff --git a/src/kcm/package/contents/ui/AvailableServices.qml b/src/kcm/package/contents/ui/AvailableServices.qml deleted file mode 100644 index 9bcbc8e..0000000 --- a/src/kcm/package/contents/ui/AvailableServices.qml +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright 2019 Nicolas Fella - * Copyright 2020 Dan Leinir Turthra Jensen - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Library General Public License as - * published by the Free Software Foundation; either version 2 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 Library General Public License for more details - * - * You should have received a copy of the GNU Library 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.11 -import QtQuick.Controls 2.0 as Controls -import org.kde.kirigami 2.4 as Kirigami -import org.kde.kaccounts 1.1 as KAccounts -import org.kde.kcm 1.2 -import Ubuntu.OnlineAccounts 0.1 as OA - -ScrollViewKCM { - id: component; - - title: i18n("Available Services") - - property alias accountId: servicesModel.accountId - - view: ListView { - model: OA.AccountServiceModel { - id: servicesModel - includeDisabled: true - function refreshData() { - // Because AccountServiceModel seems to not pick this up itself, we'll reset the model... like so, because there's no reset - var oldId = component.accountId; - component.accountId = ""; - component.accountId = oldId; - } - } - delegate: Kirigami.AbstractListItem { - width: parent.width - Controls.CheckBox { - id: serviceCheck - text: model.serviceName - checked: model.enabled - Binding { - target: serviceCheck - property: "checked" - value: model.enabled - } - onClicked: { - var job = jobComponent.createObject(component, { "accountId": component.accountId, "serviceId": model.serviceName, "serviceEnabled": !model.enabled }) - job.result.connect(servicesModel.refreshData); - job.start() - } - } - } - Controls.Label { - anchors.fill: parent - verticalAlignment: Text.AlignVCenter - horizontalAlignment: Text.AlignHCenter - wrapMode: Text.Wrap - visible: parent.count === 0 - opacity: 0.5 - text: i18nc("A text shown when an account has no configurable services", "(No services for this account)") - } - } - Component { - id: jobComponent - KAccounts.AccountServiceToggleJob { } - } -} diff --git a/src/kcm/package/contents/ui/MessageBoxSheet.qml b/src/kcm/package/contents/ui/MessageBoxSheet.qml index 2190e7a..e2d646c 100644 --- a/src/kcm/package/contents/ui/MessageBoxSheet.qml +++ b/src/kcm/package/contents/ui/MessageBoxSheet.qml @@ -1,58 +1,58 @@ /* * Copyright 2020 Dan Leinir Turthra Jensen * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2 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 Library General Public License for more details * * You should have received a copy of the GNU Library 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.Controls 2.11 as QtControls -import QtQuick.Layouts 1.11 as QtLayouts +import QtQuick 2.12 +import QtQuick.Controls 2.12 as QtControls +import QtQuick.Layouts 1.12 as QtLayouts import org.kde.kirigami 2.7 as Kirigami Kirigami.OverlaySheet { id: component property alias title: headerLabel.text property alias text: messageLabel.text property list actions showCloseButton: true header: Kirigami.Heading { id: headerLabel QtLayouts.Layout.fillWidth: true elide: Text.ElideRight } contentItem: QtControls.Label { id: messageLabel QtLayouts.Layout.preferredWidth: Kirigami.Units.gridUnit * 10 QtLayouts.Layout.margins: Kirigami.Units.largeSpacing wrapMode: Text.Wrap } footer: QtLayouts.RowLayout { Item { QtLayouts.Layout.fillWidth: true } Repeater { model: component.actions; QtControls.Button { action: modelData Connections { target: action onTriggered: component.close() } } } } } diff --git a/src/kcm/package/contents/ui/RemoveAccountDialog.qml b/src/kcm/package/contents/ui/RemoveAccountDialog.qml new file mode 100644 index 0000000..18ceb93 --- /dev/null +++ b/src/kcm/package/contents/ui/RemoveAccountDialog.qml @@ -0,0 +1,58 @@ +/* + * Copyright 2020 Dan Leinir Turthra Jensen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 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 Library General Public License for more details + * + * You should have received a copy of the GNU Library 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.12 +import QtQuick.Layouts 1.12 + +import org.kde.kirigami 2.7 as Kirigami + +import org.kde.kaccounts 1.2 as KAccounts + +MessageBoxSheet { + id: component + property int accountId + property string displayName + property string providerName + signal accountRemoved() + title: i18nc("The title for a dialog which lets you remove an account", "Remove Account?") + text: { + if (displayName.length > 0 && providerName.length > 0) { + return i18nc("The text for a dialog which lets you remove an account when both provider name and account name are available", "Are you sure you wish to remove the \"%1\" account \"%2\"?", providerName, displayName) + } else if (displayName.length > 0) { + return i18nc("The text for a dialog which lets you remove an account when only the account name is available", "Are you sure you wish to remove the account \"%1\"?", displayName) + } else { + return i18nc("The text for a dialog which lets you remove an account when only the provider name is available", "Are you sure you wish to remove this \"%1\" account?", providerName) + } + } + actions: [ + Kirigami.Action { + text: i18nc("The label for a button which will cause the removal of a specified account", "Remove Account") + onTriggered: { + var job = accountRemovalJob.createObject(component, { "accountId": component.accountId }); + job.start(); + } + Component { + id: accountRemovalJob + KAccounts.RemoveAccountJob { + onFinished: component.accountRemoved() + } + } + } + ] +} diff --git a/src/kcm/package/contents/ui/RenameAccountDialog.qml b/src/kcm/package/contents/ui/RenameAccountDialog.qml new file mode 100644 index 0000000..f09ba8d --- /dev/null +++ b/src/kcm/package/contents/ui/RenameAccountDialog.qml @@ -0,0 +1,63 @@ +/* + * Copyright 2020 Dan Leinir Turthra Jensen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 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 Library General Public License for more details + * + * You should have received a copy of the GNU Library 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.12 +import QtQuick.Controls 2.12 as Controls +import QtQuick.Layouts 1.12 + +import org.kde.kirigami 2.7 as Kirigami + +import org.kde.kaccounts 1.2 as KAccounts + +MessageBoxSheet { + id: component + title: i18nc("The title for a dialog which lets you set the human-readable name of an account", "Rename Account") + property int accountId + property string currentDisplayName + signal accountRenamed() + onSheetOpenChanged: { + if (sheetOpen === true) { + newAccountDisplayName.text = currentDisplayName; + } + } + contentItem: Kirigami.FormLayout { + Layout.preferredWidth: Kirigami.Units.gridUnit * 10 + Layout.margins: Kirigami.Units.largeSpacing + Controls.TextField { + id: newAccountDisplayName + Kirigami.FormData.label: i18nc("Label for the text field used to enter a new human-readable name for an account", "Enter the new name of the account") + } + } + actions: [ + Kirigami.Action { + enabled: newAccountDisplayName.text.length > 0 && newAccountDisplayName.text !== component.currentDisplayName + text: i18nc("Text of a button which will cause the human-readable name of an account to be set to a text specified by the user", "Set Account Name") + onTriggered: { + var job = accountDisplayNameJob.createObject(component, { "accountId": component.accountId, "displayName": newAccountDisplayName.text }) + job.start(); + } + Component { + id: accountDisplayNameJob + KAccounts.ChangeAccountDisplayNameJob { + onFinished: component.accountRenamed() + } + } + } + ] +} diff --git a/src/lib/CMakeLists.txt b/src/lib/CMakeLists.txt index 4782d67..9070643 100644 --- a/src/lib/CMakeLists.txt +++ b/src/lib/CMakeLists.txt @@ -1,75 +1,87 @@ set(CMAKECONFIG_INSTALL_DIR "${KDE_INSTALL_CMAKEPACKAGEDIR}/KAccounts") ecm_setup_version(${KACCOUNTS_VERSION} VARIABLE_PREFIX KACCOUNTS VERSION_HEADER "${CMAKE_CURRENT_BINARY_DIR}/kaccounts_version.h" PACKAGE_VERSION_FILE "${CMAKE_CURRENT_BINARY_DIR}/KAccountsConfigVersion.cmake" SOVERSION ${KACCOUNTS_SOVERSION}) -set(kaccountslib_SRCS +set (kaccountslib_SRCS + accountsmodel.cpp + core.cpp + kaccountsdplugin.cpp + kaccountsuiplugin.cpp + providersmodel.cpp + servicesmodel.cpp + uipluginsmanager.cpp + accountservicetogglejob.cpp + changeaccountdisplaynamejob.cpp createaccountjob.cpp - kaccountsuiplugin.cpp - kaccountsdplugin.cpp getcredentialsjob.cpp - core.cpp - uipluginsmanager.cpp + removeaccountjob.cpp ) ecm_generate_headers(kaccountslib_HEADERS HEADER_NAMES + AccountsModel Core - GetCredentialsJob KAccountsUiPlugin KAccountsDPlugin + ProvidersModel + ServicesModel + AccountServiceToggleJob + ChangeAccountDisplayNameJob CreateAccountJob + GetCredentialsJob + RemoveAccountJob REQUIRED_HEADERS kaccountslib_HEADERS ) add_library(kaccounts ${kaccountslib_SRCS}) generate_export_header(kaccounts BASE_NAME kaccounts) target_link_libraries (kaccounts PUBLIC KF5::CoreAddons KF5::I18n ${ACCOUNTSQT_LIBRARIES} Qt5::Xml Qt5::Gui PRIVATE ${SIGNONQT_LIBRARIES} ) target_include_directories(kaccounts INTERFACE "$" PUBLIC "${ACCOUNTSQT_INCLUDE_DIRS}" PRIVATE "${SIGNONQT_INCLUDE_DIRS}") set_target_properties(kaccounts PROPERTIES VERSION ${KACCOUNTS_VERSION} SOVERSION ${KACCOUNTS_SOVERSION} EXPORT_NAME KAccounts ) ecm_configure_package_config_file( "${CMAKE_CURRENT_SOURCE_DIR}/KAccountsConfig.cmake.in" "${CMAKE_CURRENT_BINARY_DIR}/KAccountsConfig.cmake" INSTALL_DESTINATION ${CMAKECONFIG_INSTALL_DIR} ) install(FILES "KAccountsMacros.cmake" "${CMAKE_CURRENT_BINARY_DIR}/KAccountsConfig.cmake" "${CMAKE_CURRENT_BINARY_DIR}/KAccountsConfigVersion.cmake" DESTINATION "${CMAKECONFIG_INSTALL_DIR}" COMPONENT Devel ) install(TARGETS kaccounts EXPORT KAccountsTargets ${INSTALL_TARGETS_DEFAULT_ARGS}) install(EXPORT KAccountsTargets DESTINATION "${CMAKECONFIG_INSTALL_DIR}" FILE KAccountsTargets.cmake ) #NAMESPACE KF5:: install (FILES ${CMAKE_CURRENT_BINARY_DIR}/kaccounts_export.h ${kaccountslib_HEADERS} ${CMAKE_CURRENT_BINARY_DIR}/kaccounts_version.h DESTINATION ${KDE_INSTALL_INCLUDEDIR}/KAccounts COMPONENT Devel ) add_subdirectory(cmake) diff --git a/src/lib/accountservicetogglejob.h b/src/lib/accountservicetogglejob.h index 618ef72..e4fa226 100644 --- a/src/lib/accountservicetogglejob.h +++ b/src/lib/accountservicetogglejob.h @@ -1,55 +1,58 @@ /************************************************************************************* * Copyright (C) 2020 by Dan Leinir Turthra Jensen * * * * 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 * *************************************************************************************/ #ifndef ACCOUNTSERVICETOGGLE_H #define ACCOUNTSERVICETOGGLE_H #include "kaccounts_export.h" #include #include +/** + * @brief A job used to change the enabled state of a specific service on a specific account + */ class KACCOUNTS_EXPORT AccountServiceToggleJob : public KJob { Q_OBJECT Q_PROPERTY(QString accountId READ accountId WRITE setAccountId NOTIFY accountIdChanged) Q_PROPERTY(QString serviceId READ serviceId WRITE setServiceId NOTIFY serviceIdChanged) Q_PROPERTY(bool serviceEnabled READ serviceEnabled WRITE setServiceEnabled NOTIFY serviceEnabledChanged) public: explicit AccountServiceToggleJob(QObject* parent = nullptr); virtual ~AccountServiceToggleJob(); void start() override; QString accountId() const; void setAccountId(const QString& accountId); Q_SIGNAL void accountIdChanged(); QString serviceId() const; void setServiceId(const QString& serviceId); Q_SIGNAL void serviceIdChanged(); bool serviceEnabled() const; void setServiceEnabled(bool serviceEnabled); Q_SIGNAL void serviceEnabledChanged(); private: class Private; Private* d; }; #endif//ACCOUNTSERVICETOGGLE_H diff --git a/src/lib/accountsmodel.cpp b/src/lib/accountsmodel.cpp new file mode 100644 index 0000000..929f4e6 --- /dev/null +++ b/src/lib/accountsmodel.cpp @@ -0,0 +1,193 @@ +/************************************************************************************* + * Copyright (C) 2012 by Alejandro Fiestas Olivares * + * Copyright (C) 2020 by Dan Leinir Turthra Jensen * + * * + * 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 * + *************************************************************************************/ + +#include "accountsmodel.h" + +#include "core.h" +#include "servicesmodel.h" + +#include + +#include +#include + +#include + +#include +#include + +class AccountsModel::Private : public QObject +{ +public: + Private(AccountsModel *model) + : accountsManager(KAccounts::accountsManager()) + , q(model) + { + accountIDs = accountsManager->accountList(); + + connect(accountsManager, &Accounts::Manager::accountCreated, + q, [this](Accounts::AccountId accountId){ + int row = accountIDs.count(); + q->beginInsertRows(QModelIndex(), row, row); + accountIDs.insert(row, accountId); + q->endInsertRows(); + }); + connect(accountsManager, &Accounts::Manager::accountRemoved, + q, [this](Accounts::AccountId accountId) { + q->beginRemoveRows(QModelIndex(), accountIDs.indexOf(accountId), accountIDs.indexOf(accountId)); + removeAccount(accountId); + q->endRemoveRows(); + }); + }; + virtual ~Private() + { + qDeleteAll(accounts); + }; + + Accounts::Manager *accountsManager; + Accounts::AccountIdList accountIDs; + QHash accounts; + QHash servicesModels; + + Accounts::Account* accountById(int id); + void removeAccount(Accounts::AccountId accountId); + +private: + AccountsModel* q; +}; + +Accounts::Account* AccountsModel::Private::accountById(int id) +{ + if (accounts.contains(id)) { + return accounts.value(id); + } + + // If we don't yet have this account cached, get it and connect it up to the model + Accounts::Account* account = accountsManager->account(id); + if (!account) { + qDebug() << "\t Failed to get the account from manager"; + return nullptr; + } + + connect(account, &Accounts::Account::displayNameChanged, q, [this,account](){ + QModelIndex accountIndex = q->index(accountIDs.indexOf(account->id())); + Q_EMIT q->dataChanged(accountIndex, accountIndex, QVector() << AccountsModel::DisplayNameRole); + }); + + accounts[id] = account; + return account; +} + +void AccountsModel::Private::removeAccount(Accounts::AccountId accountId) +{ + accountIDs.removeOne(accountId); + delete accounts.take(accountId); +} + +AccountsModel::AccountsModel(QObject* parent) + : QAbstractListModel(parent) + , d(new AccountsModel::Private(this)) +{ +} + +AccountsModel::~AccountsModel() +{ + delete d; +} + +QHash AccountsModel::roleNames() const +{ + static QHash roles{ + {IdRole, "id"}, + {ServicesRole, "services"}, + {EnabledRole, "enabled"}, + {CredentialsIdRole, "credentialsId"}, + {DisplayNameRole, "displayName"}, + {ProviderNameRole, "providerName"}, + {IconNameRole, "iconName"}, + {DataObjectRole, "dataObject"} + }; + return roles; +} + +int AccountsModel::rowCount(const QModelIndex& parent) const +{ + if (parent.isValid()) { + return 0; + } + + return d->accountIDs.count(); +} + +QVariant AccountsModel::data(const QModelIndex& index, int role) const +{ + QVariant data; + if(checkIndex(index)) { + Accounts::AccountId accountId = d->accountIDs.value(index.row()); + Accounts::Account *account = d->accountById(accountId); + if (account) { + switch (role) { + case IdRole: + data.setValue(account->id()); + break; + case ServicesRole: + { + ServicesModel* servicesModel{nullptr}; + if (d->servicesModels.contains(account)) { + servicesModel = d->servicesModels.value(account); + } else { + // Not parenting to the account itself, so we can avoid it suddenly + // disappearing. Just to be on the safe side + servicesModel = new ServicesModel(d->accountsManager); + servicesModel->setAccount(account); + d->servicesModels[account] = servicesModel; + } + data.setValue(servicesModel); + break; + } + case EnabledRole: + data.setValue(account->enabled()); + break; + case CredentialsIdRole: + data.setValue(account->credentialsId()); + break; + case DisplayNameRole: + data.setValue(account->displayName()); + break; + case ProviderNameRole: + data.setValue(account->providerName()); + break; + case IconNameRole: + { + QString iconName = QString::fromLatin1("unknown"); + if (account->provider().isValid() && !account->provider().iconName().isEmpty()) { + iconName = account->provider().iconName(); + } + data.setValue(iconName); + break; + } + case DataObjectRole: + data.setValue(account); + break; + } + } + } + + return data; +} diff --git a/src/lib/accountsmodel.h b/src/lib/accountsmodel.h new file mode 100644 index 0000000..6b86c5f --- /dev/null +++ b/src/lib/accountsmodel.h @@ -0,0 +1,72 @@ +/************************************************************************************* + * Copyright (C) 2012 by Alejandro Fiestas Olivares * + * Copyright (C) 2020 by Dan Leinir Turthra Jensen * + * * + * 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 * + *************************************************************************************/ + +#ifndef ACCOUNTS_MODEL_H +#define ACCOUNTS_MODEL_H + +#include "kaccounts_export.h" + +#include + +#include + +/** + * @brief A model representing all the accounts registered on a system + * + * # Roles + * + * The following role names are available in this model: + * + * * id: The internal ID of the account + * * services: A model which contains information about the services this account supports (see ServicesModel) + * * enabled: Whether or not this account is enabled + * * credentialsId: The internal ID for any stored credentials for this account + * * displayName: A human-readable name for this account (change this using ChangeAccountDisplayNameJob) + * * providerName: The internal name of the provider this account is registered through + * * iconName: An XDG Icon specification icon name + * * dataObject: The instance of Accounts::Account which the data for this account is fetched from + */ +class KACCOUNTS_EXPORT AccountsModel : public QAbstractListModel +{ + Q_OBJECT + + public: + enum Roles { + IdRole = Qt::UserRole + 1, + ServicesRole, + EnabledRole, + CredentialsIdRole, + DisplayNameRole, + ProviderNameRole, + IconNameRole, + DataObjectRole + }; + explicit AccountsModel(QObject* parent = nullptr); + virtual ~AccountsModel(); + + QHash< int, QByteArray > roleNames() const override; + virtual int rowCount(const QModelIndex& parent = QModelIndex()) const override; + virtual QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; + + private: + class Private; + Private *d; +}; + +#endif //ACCOUNTS_MODEL_H diff --git a/src/lib/changeaccountdisplaynamejob.cpp b/src/lib/changeaccountdisplaynamejob.cpp new file mode 100644 index 0000000..73ed0e0 --- /dev/null +++ b/src/lib/changeaccountdisplaynamejob.cpp @@ -0,0 +1,90 @@ +/************************************************************************************* + * Copyright (C) 2020 by Dan Leinir Turthra Jensen * + * * + * 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 * + *************************************************************************************/ + +#include "changeaccountdisplaynamejob.h" + +#include "core.h" +#include +#include +#include + +class ChangeAccountDisplayNameJob::Private { +public: + Private() {} + QString accountId; + QString displayName; +}; + +ChangeAccountDisplayNameJob::ChangeAccountDisplayNameJob(QObject* parent) + : KJob(parent) + , d(new Private) +{ } + +ChangeAccountDisplayNameJob::~ChangeAccountDisplayNameJob() +{ + delete d; +} + +QString ChangeAccountDisplayNameJob::accountId() const +{ + return d->accountId; +} + +void ChangeAccountDisplayNameJob::setAccountId(const QString& accountId) +{ + d->accountId = accountId; + Q_EMIT accountIdChanged(); +} + +QString ChangeAccountDisplayNameJob::displayName() const +{ + return d->displayName; +} + +void ChangeAccountDisplayNameJob::setDisplayName(const QString& displayName) +{ + d->displayName = displayName; + Q_EMIT displayNameChanged(); +} + +void ChangeAccountDisplayNameJob::start() +{ + if (!d->displayName.isEmpty()) { + Accounts::Manager* accountsManager = KAccounts::accountsManager(); + if (accountsManager) { + Accounts::Account *account = accountsManager->account(d->accountId.toInt()); + if (account) { + account->setDisplayName(d->displayName); + connect(account, &Accounts::Account::synced, this, [this](){ emitResult(); }); + account->sync(); + } else { + qWarning() << "No account found with the ID" << d->accountId; + setErrorText(i18n("No account found with the ID %1").arg(d->accountId)); + emitResult(); + } + } else { + qWarning() << "No accounts manager, this is not awesome."; + setErrorText(i18n("No accounts manager, this is not awesome.")); + emitResult(); + } + } else { + qWarning() << "Setting an account display name to empty is a terrible idea, and we refuse to do that"; + setErrorText(i18n("The display name cannot be empty")); + emitResult(); + } +} diff --git a/src/lib/accountservicetogglejob.h b/src/lib/changeaccountdisplaynamejob.h similarity index 71% copy from src/lib/accountservicetogglejob.h copy to src/lib/changeaccountdisplaynamejob.h index 618ef72..a1f2634 100644 --- a/src/lib/accountservicetogglejob.h +++ b/src/lib/changeaccountdisplaynamejob.h @@ -1,55 +1,56 @@ /************************************************************************************* * Copyright (C) 2020 by Dan Leinir Turthra Jensen * * * * 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 * *************************************************************************************/ -#ifndef ACCOUNTSERVICETOGGLE_H -#define ACCOUNTSERVICETOGGLE_H +#ifndef CHANGEACCOUNTDISPLAYNAMEJOB_H +#define CHANGEACCOUNTDISPLAYNAMEJOB_H #include "kaccounts_export.h" #include #include -class KACCOUNTS_EXPORT AccountServiceToggleJob : public KJob +/** + * @brief A job used to change the human-readable name of a specified account + * + * This job will refuse to change the name to something empty (while it is technically + * possible to do so for an account, it is highly undesirable) + */ +class KACCOUNTS_EXPORT ChangeAccountDisplayNameJob : public KJob { Q_OBJECT Q_PROPERTY(QString accountId READ accountId WRITE setAccountId NOTIFY accountIdChanged) - Q_PROPERTY(QString serviceId READ serviceId WRITE setServiceId NOTIFY serviceIdChanged) - Q_PROPERTY(bool serviceEnabled READ serviceEnabled WRITE setServiceEnabled NOTIFY serviceEnabledChanged) + Q_PROPERTY(QString displayName READ displayName WRITE setDisplayName NOTIFY displayNameChanged) public: - explicit AccountServiceToggleJob(QObject* parent = nullptr); - virtual ~AccountServiceToggleJob(); + explicit ChangeAccountDisplayNameJob(QObject* parent = nullptr); + virtual ~ChangeAccountDisplayNameJob(); void start() override; QString accountId() const; void setAccountId(const QString& accountId); Q_SIGNAL void accountIdChanged(); - QString serviceId() const; - void setServiceId(const QString& serviceId); - Q_SIGNAL void serviceIdChanged(); - - bool serviceEnabled() const; - void setServiceEnabled(bool serviceEnabled); - Q_SIGNAL void serviceEnabledChanged(); + QString displayName() const; + void setDisplayName(const QString& displayName); + Q_SIGNAL void displayNameChanged(); private: class Private; Private* d; }; -#endif//ACCOUNTSERVICETOGGLE_H +#endif//CHANGEACCOUNTDISPLAYNAMEJOB_H diff --git a/src/lib/createaccountjob.cpp b/src/lib/createaccountjob.cpp index 05d9b72..a4ecdf9 100644 --- a/src/lib/createaccountjob.cpp +++ b/src/lib/createaccountjob.cpp @@ -1,244 +1,245 @@ /************************************************************************************* * Copyright (C) 2013 by Alejandro Fiestas Olivares * * * * 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 * *************************************************************************************/ #include "createaccountjob.h" + #include "kaccountsuiplugin.h" #include "core.h" #include "uipluginsmanager.h" #include #include #include #include #include #include #include #include CreateAccountJob::CreateAccountJob(QObject* parent) : CreateAccountJob(QString(), parent) { } CreateAccountJob::CreateAccountJob(const QString &providerName, QObject* parent) : KJob(parent) , m_providerName(providerName) , m_manager(new Accounts::Manager(this)) , m_account(nullptr) , m_accInfo(nullptr) , m_identity(nullptr) , m_done(false) { } void CreateAccountJob::start() { qDebug() << m_providerName; QMetaObject::invokeMethod(this, "processSession"); } void CreateAccountJob::processSession() { m_account = m_manager->createAccount(m_providerName); Accounts::Service service; if (m_account->services().size() == 1) { service = m_account->services().at(0); } m_accInfo = new Accounts::AccountService(m_account, service, this); const QString pluginName = m_account->provider().pluginName(); qDebug() << "Looking for plugin" << pluginName; if (!pluginName.isEmpty()) { loadPluginAndShowDialog(pluginName); } else { SignOn::IdentityInfo info; info.setCaption(m_providerName); info.setAccessControlList({QStringLiteral("*")}); info.setType(SignOn::IdentityInfo::Application); info.setStoreSecret(true); m_identity = SignOn::Identity::newIdentity(info, this); m_identity->storeCredentials(); connect(m_identity, &SignOn::Identity::info, this, &CreateAccountJob::info); connect(m_identity, &SignOn::Identity::error, [=](const SignOn::Error &err) { qDebug() << "Error storing identity:" << err.message(); }); QVariantMap data = m_accInfo->authData().parameters(); data.insert(QStringLiteral("Embedded"), false); SignOn::SessionData sessionData(data); SignOn::AuthSessionP session = m_identity->createSession(m_accInfo->authData().method()); qDebug() << "Starting auth session with" << m_accInfo->authData().method(); connect(session, &SignOn::AuthSession::error, this, &CreateAccountJob::sessionError); connect(session, &SignOn::AuthSession::response, this, &CreateAccountJob::sessionResponse); session->process(sessionData, m_accInfo->authData().mechanism()); } } void CreateAccountJob::loadPluginAndShowDialog(const QString &pluginName) { KAccountsUiPlugin *ui = KAccounts::UiPluginsManager::pluginForName(pluginName); if (!ui) { qDebug() << "Plugin could not be loaded"; pluginError(i18nc("The %1 is for plugin name, eg. Could not load UI plugin", "Could not load %1 plugin, please check your installation", pluginName)); return; } connect(ui, &KAccountsUiPlugin::success, this, &CreateAccountJob::pluginFinished, Qt::UniqueConnection); connect(ui, &KAccountsUiPlugin::error, this, &CreateAccountJob::pluginError, Qt::UniqueConnection); ui->setProviderName(m_providerName); ui->init(KAccountsUiPlugin::NewAccountDialog); } void CreateAccountJob::pluginFinished(const QString &screenName, const QString &secret, const QVariantMap &data) { // Set up the new identity SignOn::IdentityInfo info; info.setStoreSecret(true); info.setUserName(screenName); info.setSecret(secret, true); info.setCaption(m_providerName); info.setAccessControlList(QStringList(QLatin1String("*"))); info.setType(SignOn::IdentityInfo::Application); const auto keys = data.keys(); for (const QString &key : keys) { // If a key with __service/ prefix exists and its value is false, // add it to m_disabledServices which will later be used for disabling // the services contained in that list if (key.startsWith(QLatin1String("__service/")) && !data.value(key).toBool()) { m_disabledServices << key.mid(10); } m_account->setValue(key, data.value(key).toString()); } m_identity = SignOn::Identity::newIdentity(info, this); connect(m_identity, &SignOn::Identity::info, this, &CreateAccountJob::info); m_done = true; connect(m_identity, &SignOn::Identity::credentialsStored, m_identity, &SignOn::Identity::queryInfo); m_identity->storeCredentials(); } void CreateAccountJob::pluginError(const QString &error) { if (error.isEmpty()) { setError(-1); } else { setError(KJob::UserDefinedError); } setErrorText(error); // Delete the dialog emitResult(); } void CreateAccountJob::sessionResponse(const SignOn::SessionData &/*data*/) { qDebug() << "Received session response"; m_done = true; m_identity->queryInfo(); } void CreateAccountJob::info(const SignOn::IdentityInfo &info) { qDebug() << "Info:"; qDebug() << "\tId:" << info.id(); qDebug() << "\tcaption:" << info.caption(); qDebug() << "\towner:" << info.owner(); qDebug() << "\tuserName:" << info.userName(); if (!m_done) { return; } m_account->selectService(); if (m_account->displayName().isEmpty()) { if(info.userName().isEmpty()) { // info.userName() can be empty, see bug#414219 m_account->setDisplayName(QStringLiteral("%1%2").arg(info.caption()).arg(info.id())); } else { m_account->setDisplayName(info.userName()); } } m_account->setValue(QStringLiteral("username"), info.userName()); m_account->setCredentialsId(info.id()); Accounts::AuthData authData = m_accInfo->authData(); m_account->setValue(QStringLiteral("auth/mechanism"), authData.mechanism()); m_account->setValue(QStringLiteral("auth/method"), authData.method()); QString base = QStringLiteral("auth/"); base.append(authData.method()); base.append(QLatin1Char('/')); base.append(authData.mechanism()); base.append(QLatin1Char('/')); QVariantMap data = authData.parameters(); QMapIterator i(data); while (i.hasNext()) { i.next(); m_account->setValue(base + i.key(), i.value()); } const Accounts::ServiceList services = m_account->services(); for (const Accounts::Service &service : services) { m_account->selectService(service); m_account->setEnabled(m_disabledServices.contains(service.name()) ? false : true); } m_account->selectService(); m_account->setEnabled(true); m_account->sync(); connect(m_account, &Accounts::Account::synced, this, &CreateAccountJob::emitResult); } void CreateAccountJob::sessionError(const SignOn::Error &signOnError) { if (error()) { // Guard against SignOn sending two error() signals return; } qWarning() << "Error:"; qWarning() << "\t" << signOnError.message(); setError(KJob::UserDefinedError); setErrorText(i18n("There was an error while trying to process the request: %1", signOnError.message())); emitResult(); } void CreateAccountJob::setProviderName(const QString &name) { if (m_providerName != name) { m_providerName = name; Q_EMIT providerNameChanged(); } } diff --git a/src/lib/createaccountjob.h b/src/lib/createaccountjob.h index a86635b..a827069 100644 --- a/src/lib/createaccountjob.h +++ b/src/lib/createaccountjob.h @@ -1,76 +1,79 @@ /************************************************************************************* * Copyright (C) 2013 by Alejandro Fiestas Olivares * * * * 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 * *************************************************************************************/ #ifndef CREATE_ACCOUNT_JOB_H #define CREATE_ACCOUNT_JOB_H #include "kaccounts_export.h" #include #include namespace Accounts { class Account; class Manager; class AccountService; } namespace SignOn { class Error; class Identity; class SessionData; class IdentityInfo; } +/** + * @brief Create a new account for the specified provider + */ class KACCOUNTS_EXPORT CreateAccountJob : public KJob { Q_OBJECT Q_PROPERTY(QString providerName READ providerName WRITE setProviderName NOTIFY providerNameChanged) public: explicit CreateAccountJob(QObject* parent = nullptr); explicit CreateAccountJob(const QString &providerName, QObject* parent = nullptr); QString providerName() const { return m_providerName; } void setProviderName(const QString &name); void start() override; private Q_SLOTS: void processSession(); void sessionError(const SignOn::Error &signOnError); void sessionResponse(const SignOn::SessionData &data); void info(const SignOn::IdentityInfo &info); void pluginFinished(const QString &screenName, const QString &secret, const QVariantMap &map); void pluginError(const QString &error); Q_SIGNALS: void providerNameChanged(); private: void loadPluginAndShowDialog(const QString &pluginName); QString m_providerName; QStringList m_disabledServices; Accounts::Manager *m_manager; Accounts::Account *m_account; Accounts::AccountService *m_accInfo; SignOn::Identity *m_identity; bool m_done; }; #endif //CREATE_ACCOUNT_JOB_H diff --git a/src/lib/getcredentialsjob.h b/src/lib/getcredentialsjob.h index 7de5a33..195641e 100644 --- a/src/lib/getcredentialsjob.h +++ b/src/lib/getcredentialsjob.h @@ -1,94 +1,94 @@ /************************************************************************************* * Copyright (C) 2013 by Alejandro Fiestas Olivares * * * * This library is free software; you can redistribute it and/or * * modify it under the terms of the GNU Lesser General Public * * License as published by the Free Software Foundation; either * * version 2 of the License, or (at your option) any later version. * * * * This library 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 * * Library General Public License for more details. * * * * You should have received a copy of the GNU Library General Public License * * along with this library; see the file COPYING.LIB. If not, write to * * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02110-1301, USA. * *************************************************************************************/ #ifndef GET_CREDENTIALS_JOB_H #define GET_CREDENTIALS_JOB_H #include "kaccounts_export.h" #include #include namespace Accounts { class Manager; } /** - * A KJob for obtaining user's credentials for the given Accounts::AccountId + * @brief A KJob for obtaining user's credentials for the given Accounts::AccountId */ class KACCOUNTS_EXPORT GetCredentialsJob : public KJob { Q_OBJECT public: /** * Constructs the job with auth method and mechanism coming from the service * type. If no service type is specified, the default will be used * * @param id AccountId for which the credentials will be obtained */ explicit GetCredentialsJob(const Accounts::AccountId &id, QObject *parent = nullptr); /** * This version of the constructor allow passing specific auth method and mechanism * for which we want the credentials * * For example some account has OAuth token and username-password credentials, * by setting both method and mechanism to "password", only the password will be * retrieved. Otherwise it depends on the passed serviceType - if there's no serviceType * set, it will use the default service for the given AccountId and will obtain * the credentials needed for that service * * @param id AccountId for which the credentials will be obtained * @param authMethod Auth method for which the credentials will be obtained * @param authMechanism Auth mechanism for which the credentials will be obtained */ GetCredentialsJob(const Accounts::AccountId &id, const QString &authMethod = QString(), const QString &authMechanism = QString(), QObject *parent = nullptr); /** * Starts the credentials job */ void start() override; /** * Set service for which the auth method and mechanism will be selected * * @param serviceType Account's service type */ void setServiceType(const QString &serviceType); /** * The obtained credentials data * * This will be valid only after the job has finished * * @returns Map with the credentials */ QVariantMap credentialsData() const; /** * @returns Account id for which the credentials are obtained */ Accounts::AccountId accountId() const; private: class Private; Private * const d; Q_PRIVATE_SLOT(d, void getCredentials()) }; #endif //GET_CREDENTIALS_JOB_H diff --git a/src/lib/providersmodel.cpp b/src/lib/providersmodel.cpp new file mode 100644 index 0000000..27bf35a --- /dev/null +++ b/src/lib/providersmodel.cpp @@ -0,0 +1,116 @@ +/************************************************************************************* + * Copyright (C) 2020 by Dan Leinir Turthra Jensen * + * * + * 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 * + *************************************************************************************/ + +#include "providersmodel.h" + +#include "core.h" + +#include +#include + +class ProvidersModel::Private { +public: + Private() {} + Accounts::Manager* accountsManager{nullptr}; + + const Accounts::ProviderList& providerList() { + if (!accountsManager) { + accountsManager = KAccounts::accountsManager(); + providers = accountsManager->providerList(); + } + return providers; + } +private: + Accounts::ProviderList providers; +}; + +ProvidersModel::ProvidersModel(QObject* parent) + : QAbstractListModel(parent) + , d(new Private) +{ +} + +ProvidersModel::~ProvidersModel() +{ + delete d; +} + +QHash ProvidersModel::roleNames() const +{ + static const QHash roleNames { + {NameRole, "name"}, + {DisplayNameRole, "displayName"}, + {DescriptionRole, "description"}, + {IconNameRole, "iconName"}, + {SupportsMultipleAccountsRole, "supportsMultipleAccounts"}, + {AccountsCountRole, "accountsCount"} + }; + return roleNames; +} + +int ProvidersModel::rowCount(const QModelIndex& parent) const +{ + if (parent.isValid()) { + return 0; + } + return d->providerList().count(); +} + +QVariant ProvidersModel::data(const QModelIndex& index, int role) const +{ + QVariant data; + if (checkIndex(index)) { + const Accounts::Provider& provider = d->providerList().value(index.row()); + if (provider.isValid()) { + switch(role) { + case NameRole: + data.setValue(provider.name()); + break; + case DisplayNameRole: + data.setValue(provider.displayName()); + break; + case DescriptionRole: + data.setValue(provider.description()); + break; + case IconNameRole: + data.setValue(provider.iconName()); + break; + case SupportsMultipleAccountsRole: + data.setValue(!provider.isSingleAccount()); + break; + case AccountsCountRole: + { + const Accounts::AccountIdList accounts = d->accountsManager->accountList(); + int i{0}; + for (const Accounts::AccountId& accountId : accounts) { + Accounts::Account* account = d->accountsManager->account(accountId); + if (account->providerName() == provider.name()) { + ++i; + } + } + data.setValue(i); + break; + } + default: + data.setValue(QString::fromLatin1("No such role: %1").arg(role)); + break; + } + } + } + return data; +} diff --git a/src/lib/providersmodel.h b/src/lib/providersmodel.h new file mode 100644 index 0000000..d806dc0 --- /dev/null +++ b/src/lib/providersmodel.h @@ -0,0 +1,64 @@ +/************************************************************************************* + * Copyright (C) 2020 by Dan Leinir Turthra Jensen * + * * + * 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 * + *************************************************************************************/ + +#ifndef PROVIDERS_MODEL_H +#define PROVIDERS_MODEL_H + +#include "kaccounts_export.h" + +#include + +/** + * @brief A model which represents the available providers + * + * # Roles + * + * The role names available in this model are: + * + * * name: The internal name identifying this provider + * * displayName: The human-readable name for this provider + * * description: A (usually single sentence) description of this provider + * * iconName: An XDG Icon specification icon name for this provider + * * supportsMultipleAccounts: Whether or not this provider supports multiple simultaneous accounts + * * accountsCount: The number of accounts which already exist on the system for this provider + */ +class KACCOUNTS_EXPORT ProvidersModel : public QAbstractListModel +{ + Q_OBJECT + public: + enum Roles { + NameRole = Qt::UserRole + 1, ///< The unique name identifier for this provider + DisplayNameRole, ///< The human-readable name for this provider + DescriptionRole, ///< A (usually single sentence) description of this provider + IconNameRole, ///< The name of the icon to be used for this provider (an XDG Icon Spec style name) + SupportsMultipleAccountsRole, ///< Whether or not this provider supports multiple simultaneous accounts + AccountsCountRole ///< The number of accounts which already exist for this provider + }; + explicit ProvidersModel(QObject* parent = nullptr); + virtual ~ProvidersModel(); + + QHash< int, QByteArray > roleNames() const override; + virtual int rowCount(const QModelIndex& parent = QModelIndex()) const override; + virtual QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; + + private: + class Private; + Private *d; +}; + +#endif//PROVIDERS_MODEL_H diff --git a/src/lib/removeaccountjob.cpp b/src/lib/removeaccountjob.cpp new file mode 100644 index 0000000..d9e5359 --- /dev/null +++ b/src/lib/removeaccountjob.cpp @@ -0,0 +1,81 @@ +/************************************************************************************* + * Copyright (C) 2020 by Dan Leinir Turthra Jensen * + * * + * 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 * + *************************************************************************************/ + +#include "removeaccountjob.h" + +#include +#include "core.h" +#include +#include + +class RemoveAccountJob::Private { +public: + Private() {} + QString accountId; +}; + +RemoveAccountJob::RemoveAccountJob(QObject* parent) + : KJob(parent) + , d(new Private) +{ } + +RemoveAccountJob::~RemoveAccountJob() +{ + delete d; +} + +QString RemoveAccountJob::accountId() const +{ + return d->accountId; +} + +void RemoveAccountJob::setAccountId(const QString& accountId) +{ + d->accountId = accountId; + Q_EMIT accountIdChanged(); +} + +void RemoveAccountJob::start() +{ + Accounts::Manager* accountsManager = KAccounts::accountsManager(); + if (accountsManager) { + Accounts::Account *account = accountsManager->account(d->accountId.toInt()); + if (account) { + // We can't depend on Accounts::Account::synced, as that doesn't necessarily get fired when + // asking for the account to be removed... + connect(accountsManager, &Accounts::Manager::accountRemoved, this, [this](Accounts::AccountId id) { + if (id == d->accountId.toUInt()) { + emitResult(); + } + }); + SignOn::Identity *identity = SignOn::Identity::existingIdentity(account->credentialsId(), this); + if (identity) { + identity->remove(); + identity->deleteLater(); + } + account->remove(); + account->sync(); + } else { + qWarning() << "No account found with the ID" << d->accountId; + emitResult(); + } + } else { + qWarning() << "No accounts manager, this is not awesome."; + emitResult(); + } +} diff --git a/src/lib/accountservicetogglejob.h b/src/lib/removeaccountjob.h similarity index 71% copy from src/lib/accountservicetogglejob.h copy to src/lib/removeaccountjob.h index 618ef72..2597848 100644 --- a/src/lib/accountservicetogglejob.h +++ b/src/lib/removeaccountjob.h @@ -1,55 +1,48 @@ /************************************************************************************* * Copyright (C) 2020 by Dan Leinir Turthra Jensen * * * * 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 * *************************************************************************************/ -#ifndef ACCOUNTSERVICETOGGLE_H -#define ACCOUNTSERVICETOGGLE_H +#ifndef REMOVEACCOUNT_H +#define REMOVEACCOUNT_H #include "kaccounts_export.h" #include -#include +#include -class KACCOUNTS_EXPORT AccountServiceToggleJob : public KJob +/** + * @brief A job which will attempt to remove the specified account + */ +class KACCOUNTS_EXPORT RemoveAccountJob : public KJob { Q_OBJECT Q_PROPERTY(QString accountId READ accountId WRITE setAccountId NOTIFY accountIdChanged) - Q_PROPERTY(QString serviceId READ serviceId WRITE setServiceId NOTIFY serviceIdChanged) - Q_PROPERTY(bool serviceEnabled READ serviceEnabled WRITE setServiceEnabled NOTIFY serviceEnabledChanged) public: - explicit AccountServiceToggleJob(QObject* parent = nullptr); - virtual ~AccountServiceToggleJob(); + explicit RemoveAccountJob(QObject* parent = nullptr); + virtual ~RemoveAccountJob(); void start() override; QString accountId() const; void setAccountId(const QString& accountId); Q_SIGNAL void accountIdChanged(); - - QString serviceId() const; - void setServiceId(const QString& serviceId); - Q_SIGNAL void serviceIdChanged(); - - bool serviceEnabled() const; - void setServiceEnabled(bool serviceEnabled); - Q_SIGNAL void serviceEnabledChanged(); private: class Private; Private* d; }; -#endif//ACCOUNTSERVICETOGGLE_H +#endif//REMOVEACCOUNT_H diff --git a/src/lib/servicesmodel.cpp b/src/lib/servicesmodel.cpp new file mode 100644 index 0000000..ef0bb63 --- /dev/null +++ b/src/lib/servicesmodel.cpp @@ -0,0 +1,191 @@ +/************************************************************************************* + * Copyright (C) 2012 by Alejandro Fiestas Olivares * + * Copyright (C) 2020 by Dan Leinir Turthra Jensen * + * * + * 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 * + *************************************************************************************/ + +#include "servicesmodel.h" + +#include "core.h" + +#include + +#include + +#include + +#include +#include + +class ServicesModel::Private : public QObject +{ +public: + Private(ServicesModel *model) + : q(model) + { }; + virtual ~Private() + { }; + + Accounts::ServiceList services; + Accounts::Account* account{nullptr}; + +private: + ServicesModel* q; +}; + +ServicesModel::ServicesModel(QObject* parent) + : QAbstractListModel(parent) + , d(new ServicesModel::Private(this)) +{ +} + +ServicesModel::~ServicesModel() +{ + delete d; +} + +QHash ServicesModel::roleNames() const +{ + static QHash roles{ + {NameRole, "name"}, + {DescriptionRole, "description"}, + {DisplayNameRole, "displayName"}, + {ServiceTypeRole, "servieType"}, + {ProviderNameRole, "providerName"}, + {IconNameRole, "iconName"}, + {TagsRole, "tags"}, + {EnabledRole, "enabled"} + }; + return roles; +} + +int ServicesModel::rowCount(const QModelIndex& parent) const +{ + if (parent.isValid()) { + return 0; + } + + return d->services.count(); +} + +QVariant ServicesModel::data(const QModelIndex& index, int role) const +{ + QVariant data; + if(checkIndex(index)) { + const Accounts::Service& service = d->services.value(index.row()); + if (service.isValid()) { + switch (role) { + case NameRole: + data.setValue(service.name()); + break; + case DescriptionRole: + data.setValue(service.description()); + break; + case DisplayNameRole: + data.setValue(service.displayName()); + break; + case ServiceTypeRole: + data.setValue(service.serviceType()); + break; + case ProviderNameRole: + data.setValue(service.provider()); + break; + case IconNameRole: + data.setValue(service.iconName()); + break; + case TagsRole: + data.setValue(service.tags().values()); + break; + case EnabledRole: + data.setValue(d->account->enabledServices().contains(service)); + break; + } + } + } + return data; +} + +void ServicesModel::setAccount(QObject* account) +{ + if (d->account != account) { + beginResetModel(); + d->services.clear(); + if (d->account) { + disconnect(d->account, nullptr, this, nullptr); + } + d->account = qobject_cast(account); + if (d->account) { + connect(d->account, &Accounts::Account::displayNameChanged, this, &ServicesModel::accountChanged); + connect(d->account, &Accounts::Account::enabledChanged, this, [this](const QString& serviceName, bool /*enabled*/){ + int i{0}; + for (const Accounts::Service& service : d->services) { + if (service.name() == serviceName) { + break; + } + ++i; + } + dataChanged(index(i), index(i)); + }); + connect(d->account, &QObject::destroyed, this, [this](){ + beginResetModel(); + d->account = nullptr; + Q_EMIT accountChanged(); + d->services.clear(); + endResetModel(); + }); + d->services = d->account->services(); + } + endResetModel(); + Q_EMIT accountChanged(); + } +} + +QObject * ServicesModel::account() const +{ + return d->account; +} + +quint32 ServicesModel::accountId() const +{ + if (d->account) { + return d->account->id(); + } + return -1; +} + +QString ServicesModel::accountDisplayName() const +{ + if (d->account) { + return d->account->displayName(); + } + return QString{}; +} + +QString ServicesModel::accountProviderName() const +{ + if (d->account) { + return d->account->providerName(); + } + return QString{}; +} + +QString ServicesModel::accountIconName() const +{ + if (d->account && d->account->provider().isValid() && !d->account->provider().iconName().isEmpty()) { + return d->account->provider().iconName(); + } + return QString::fromLatin1("unknown"); +} diff --git a/src/lib/servicesmodel.h b/src/lib/servicesmodel.h new file mode 100644 index 0000000..35d42f7 --- /dev/null +++ b/src/lib/servicesmodel.h @@ -0,0 +1,101 @@ +/************************************************************************************* + * Copyright (C) 2012 by Alejandro Fiestas Olivares * + * Copyright (C) 2020 by Dan Leinir Turthra Jensen * + * * + * 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 * + *************************************************************************************/ + +#ifndef SERVICES_MODEL_H +#define SERVICES_MODEL_H + +#include "kaccounts_export.h" + +#include + +/** + * @brief A model which represents the services in a single account + * + * You can create this manually, but usually you would get an instance of it from the + * AccountsModel::Roles::ServicesRole data role (model.services) of an AccountsModel + * instance. + * + * # Roles + * + * The following role names are available in this model: + * + * * name: The internal name for the service, as understood by the backend + * * description: A (usually single line) description of the service + * * displayName: A human-readable name for the service (use this in the UI instead of name) + * * serviceType: A machine-readable category for the service (e.g. microblogging, e-mail, IM...) + * * providerName: The machine-readable name of the provider which this service is related to + * * iconName: An XDG Icon specification icon name representing this service + * * tags: A list of strings representing tags set on this service + * * enabled: Whether or not the service is enabled for the given account + */ +class KACCOUNTS_EXPORT ServicesModel : public QAbstractListModel +{ + Q_OBJECT + /** + * The Accounts::Account instance which this model should use for fetching the list of services + */ + Q_PROPERTY(QObject* account READ account WRITE setAccount NOTIFY accountChanged) + /** + * The internal ID for the account this model represents + */ + Q_PROPERTY(quint32 accountId READ accountId NOTIFY accountChanged) + /** + * The human-readable name of the account this model represents + */ + Q_PROPERTY(QString accountDisplayName READ accountDisplayName NOTIFY accountChanged) + /** + * The name of the provider this model's account is signed in through + */ + Q_PROPERTY(QString accountProviderName READ accountProviderName NOTIFY accountChanged) + /** + * The XDG Icon specification icon name for this model's account + */ + Q_PROPERTY(QString accountIconName READ accountIconName NOTIFY accountChanged) + public: + enum Roles { + NameRole = Qt::UserRole + 1, + DescriptionRole, + DisplayNameRole, + ServiceTypeRole, + ProviderNameRole, + IconNameRole, + TagsRole, + EnabledRole + }; + explicit ServicesModel(QObject* parent = nullptr); + virtual ~ServicesModel(); + + QHash< int, QByteArray > roleNames() const override; + virtual int rowCount(const QModelIndex& parent = QModelIndex()) const override; + virtual QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; + + void setAccount(QObject* account); + QObject* account() const; + quint32 accountId() const; + QString accountDisplayName() const; + QString accountProviderName() const; + QString accountIconName() const; + Q_SIGNAL void accountChanged(); + + private: + class Private; + Private *d; +}; + +#endif//SERVICES_MODEL_H