diff --git a/kded/CMakeLists.txt b/kded/CMakeLists.txt index 10f8101..98c4528 100644 --- a/kded/CMakeLists.txt +++ b/kded/CMakeLists.txt @@ -1,103 +1,103 @@ configure_file (config-plasma-vault.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-plasma-vault.h) include_directories ( ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/engine ) set ( kded_plasmavault_SRCS service.cpp engine/vault.cpp engine/backend_p.cpp engine/fusebackend_p.cpp engine/types.cpp engine/commandresult.cpp engine/backends/encfs/encfsbackend.cpp engine/backends/cryfs/cryfsbackend.cpp ui/dialogdsl.cpp ui/activitieslinkingwidget.cpp ui/backendchooserwidget.cpp ui/cryfscypherchooserwidget.cpp ui/directorypairchooserwidget.cpp ui/directorychooserwidget.cpp ui/namechooserwidget.cpp ui/noticewidget.cpp ui/passwordchooserwidget.cpp ui/offlineonlywidget.cpp ui/vaultdeletionwidget.cpp ui/vaultcreationwizard.cpp ui/vaultimportingwizard.cpp - ui/vaultconfigurationwizard.cpp + ui/vaultconfigurationdialog.cpp ui/mountdialog.cpp ../common/vaultinfo.cpp ) ki18n_wrap_ui ( kded_plasmavault_SRCS ui/activitieslinkingwidget.ui ui/backendchooserwidget.ui ui/cryfscypherchooserwidget.ui ui/directorypairchooserwidget.ui ui/directorychooserwidget.ui ui/namechooserwidget.ui ui/noticewidget.ui ui/passwordchooserwidget.ui ui/offlineonlywidget.ui ui/vaultdeletionwidget.ui ui/vaultcreationwizard.ui ui/vaultimportingwizard.ui - ui/vaultconfigurationwizard.ui + ui/vaultconfigurationdialog.ui ui/mountdialog.ui ) add_library ( kded_plasmavault MODULE ${kded_plasmavault_SRCS} ) set_target_properties ( kded_plasmavault PROPERTIES OUTPUT_NAME plasmavault ) kcoreaddons_desktop_to_json ( kded_plasmavault plasmavault.desktop ) target_link_libraries ( kded_plasmavault Qt5::Core Qt5::DBus Qt5::Widgets KF5::Activities KF5::ConfigCore KF5::ConfigWidgets KF5::CoreAddons KF5::DBusAddons KF5::I18n KF5::KIOCore KF5::KIOWidgets KF5::SysGuard KF5::WidgetsAddons KF5::ProcessCore ) if (HAVE_NETWORKMANAGER) target_link_libraries ( kded_plasmavault KF5::NetworkManagerQt ) endif () install ( TARGETS kded_plasmavault DESTINATION ${PLUGIN_INSTALL_DIR}/kf5/kded) diff --git a/kded/service.cpp b/kded/service.cpp index e757817..d263a4a 100644 --- a/kded/service.cpp +++ b/kded/service.cpp @@ -1,445 +1,445 @@ /* * Copyright 2017 by Ivan Cukic * * 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) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * 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, see . */ #include "service.h" #include #include #include #include #include #include #include "engine/vault.h" #include "engine/commandresult.h" #include "ui/vaultcreationwizard.h" #include "ui/vaultimportingwizard.h" -#include "ui/vaultconfigurationwizard.h" +#include "ui/vaultconfigurationdialog.h" #include "ui/mountdialog.h" #include #include #ifdef HAVE_NETWORKMANAGER #include #else namespace NetworkManager { bool isNetworkingEnabled() { return true; } void setNetworkingEnabled(bool enabled) { Q_UNUSED(enabled); } } #endif K_PLUGIN_FACTORY_WITH_JSON(PlasmaVaultServiceFactory, "plasmavault.json", registerPlugin();) using namespace PlasmaVault; using AsynQt::Expected; class PlasmaVaultService::Private { public: QHash knownVaults; QSet openVaults; KActivities::Consumer kamd; struct NetworkingState { bool wasNetworkingEnabled; QVector devicesInhibittingNetworking; }; // Ideally, this would be std::optional... lovely C++17 Expected savedNetworkingState = Expected::error(0); void saveNetworkingState() { // Ignore the request if we already have a saved state if (savedNetworkingState) { return; } savedNetworkingState = Expected::success( NetworkingState { NetworkManager::isNetworkingEnabled() || true, {} }); } void restoreNetworkingState() { // Ignore the request if we do not have a state saved // or if there are more devices inhibitting networking if (!savedNetworkingState || !savedNetworkingState->devicesInhibittingNetworking.isEmpty()) { return; } NetworkManager::setNetworkingEnabled(savedNetworkingState->wasNetworkingEnabled); } Vault* vaultFor(const QString &device_) const { const Device device(device_); if (!knownVaults.contains(device)) { return nullptr; } return knownVaults[device]; } }; PlasmaVaultService::PlasmaVaultService(QObject * parent, const QVariantList&) : KDEDModule(parent) , d(new Private()) { connect(this, &KDEDModule::moduleRegistered, this, &PlasmaVaultService::slotRegistered); connect(&d->kamd, &KActivities::Consumer::currentActivityChanged, this, &PlasmaVaultService::onCurrentActivityChanged); for (const Device &device: Vault::availableDevices()) { registerVault(new Vault(device, this)); } } PlasmaVaultService::~PlasmaVaultService() { } PlasmaVault::VaultInfoList PlasmaVaultService::availableDevices() const { PlasmaVault::VaultInfoList result; for (const auto &vault: d->knownVaults.values()) { result << vault->info(); } return result; } void PlasmaVaultService::requestNewVault() { const auto dialog = new VaultCreationWizard(); connect(dialog, &VaultCreationWizard::createdVault, this, &PlasmaVaultService::registerVault); dialog->show(); } void PlasmaVaultService::requestImportVault() { const auto dialog = new VaultImportingWizard(); connect(dialog, &VaultImportingWizard::importedVault, this, &PlasmaVaultService::registerVault); dialog->show(); } void PlasmaVaultService::slotRegistered(const QDBusObjectPath &path) { if (path.path() == QLatin1String("/modules/plasmavault")) { emit registered(); } } void PlasmaVaultService::registerVault(Vault *vault) { if (!vault->isValid()) { qWarning() << "Warning: Trying to register an invalid vault: " << vault->device(); return; } if (d->knownVaults.contains(vault->device())) { qWarning() << "Warning: This one is already registered: " << vault->device(); return; } vault->setParent(this); d->knownVaults[vault->device()] = vault; connect(vault, &Vault::statusChanged, this, &PlasmaVaultService::onVaultStatusChanged); connect(vault, &Vault::messageChanged, this, &PlasmaVaultService::onVaultMessageChanged); connect(vault, &Vault::infoChanged, this, &PlasmaVaultService::onVaultInfoChanged); emit vaultAdded(vault->info()); if (vault->status() == VaultInfo::Opened) { d->openVaults << vault->device(); } } void PlasmaVaultService::forgetVault(Vault* vault) { // Can not be open // d->openVaults.remove(vault.device()); // and therefore can not inhibit networking // ... d->savedNetworkingState ... emit vaultRemoved(vault->device().data()); d->knownVaults.remove(vault->device()); vault->deleteLater(); } void PlasmaVaultService::onVaultStatusChanged(VaultInfo::Status status) { const auto vault = static_cast(sender()); if (status == VaultInfo::Dismantled) { forgetVault(vault); } else if (status == VaultInfo::Opened) { d->openVaults << vault->device(); if (d->openVaults.size() == 1) { emit hasOpenVaultsChanged(true); } } else { d->openVaults.remove(vault->device()); if (d->openVaults.isEmpty()) { emit hasOpenVaultsChanged(false); } } if (vault->isOfflineOnly()) { d->saveNetworkingState(); auto& devicesInhibittingNetworking = d->savedNetworkingState->devicesInhibittingNetworking; // We need to check whether this vault // should be added or removed from the // inhibitors list const bool alreadyInhibiting = devicesInhibittingNetworking.contains(vault->device()); if (status == VaultInfo::Opened && !alreadyInhibiting) { devicesInhibittingNetworking << vault->device(); } if (status != VaultInfo::Opened && alreadyInhibiting) { devicesInhibittingNetworking.removeAll(vault->device()); } // qDebug() << "Vaults inhibitting networking: " << devicesInhibittingNetworking; // Now, let's handle the networking part if (!devicesInhibittingNetworking.isEmpty()) { NetworkManager::setNetworkingEnabled(false); } d->restoreNetworkingState(); } emit vaultChanged(vault->info()); } void PlasmaVaultService::onVaultInfoChanged() { const auto vault = static_cast(sender()); emit vaultChanged(vault->info()); } void PlasmaVaultService::onVaultMessageChanged(const QString &message) { Q_UNUSED(message); const auto vault = static_cast(sender()); emit vaultChanged(vault->info()); } void showPasswordMountDialog(Vault *vault, const std::function &function) { auto dialog = new MountDialog(vault, function); dialog->open(); } //^ void PlasmaVaultService::openVault(const QString &device) { if (auto vault = d->vaultFor(device)) { if (vault->isOpened()) return; showPasswordMountDialog(vault, [this, vault] { emit vaultChanged(vault->info()); }); } } void PlasmaVaultService::closeVault(const QString &device) { if (auto vault = d->vaultFor(device)) { if (!vault->isOpened()) return; vault->close(); } } void PlasmaVaultService::configureVault(const QString &device) { if (auto vault = d->vaultFor(device)) { - const auto dialog = new VaultConfigurationWizard(vault); + const auto dialog = new VaultConfigurationDialog(vault); dialog->show(); } } void PlasmaVaultService::forceCloseVault(const QString &device) { if (auto vault = d->vaultFor(device)) { if (!vault->isOpened()) return; vault->forceClose(); } } void PlasmaVaultService::openVaultInFileManager(const QString &device) { if (auto vault = d->vaultFor(device)) { if (vault->isOpened()) { new KRun(QUrl::fromLocalFile((QString)vault->mountPoint()), nullptr); } else { showPasswordMountDialog(vault, [this, vault] { emit vaultChanged(vault->info()); new KRun(QUrl::fromLocalFile((QString)vault->mountPoint()), nullptr); }); } } } void PlasmaVaultService::onCurrentActivityChanged( const QString ¤tActivity) { for (auto* vault: d->knownVaults.values()) { const auto vaultActivities = vault->activities(); if (!vaultActivities.isEmpty() && !vaultActivities.contains(currentActivity)) { vault->close(); } } } bool PlasmaVaultService::hasOpenVaults() const { return !d->openVaults.isEmpty(); } void PlasmaVaultService::closeAllVaults() { for (const auto& device: d->openVaults) { closeVault(device.data()); } } void PlasmaVaultService::forceCloseAllVaults() { for (const auto& device: d->openVaults) { forceCloseVault(device.data()); } } void PlasmaVaultService::deleteVault(const QString &device, const QString &name) { if (!d->knownVaults.contains(Device(device))) { qWarning() << "The specified vault does not exist: " << device; return; } auto vault = d->knownVaults[Device(device)]; if (vault->status() == VaultInfo::Opened) { qWarning() << "Can not delete an open vault: " << device; return; } if (vault->name() != name) { qWarning() << "Name is not correct: " << device; return; } vault->dismantle({}); } #include "service.moc" diff --git a/kded/ui/vaultconfigurationwizard.cpp b/kded/ui/vaultconfigurationdialog.cpp similarity index 93% rename from kded/ui/vaultconfigurationwizard.cpp rename to kded/ui/vaultconfigurationdialog.cpp index 1849b57..2cb328e 100644 --- a/kded/ui/vaultconfigurationwizard.cpp +++ b/kded/ui/vaultconfigurationdialog.cpp @@ -1,203 +1,203 @@ /* * Copyright 2017 by Ivan Cukic * * 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) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * 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, see . */ -#include "vaultconfigurationwizard.h" -#include "ui_vaultconfigurationwizard.h" +#include "vaultconfigurationdialog.h" +#include "ui_vaultconfigurationdialog.h" #include #include #include #include #include #include "dialogdsl.h" #include "vault.h" using namespace DialogDsl; using namespace DialogDsl::operators; #include "backendchooserwidget.h" #include "activitieslinkingwidget.h" #include "cryfscypherchooserwidget.h" #include "directorychooserwidget.h" #include "noticewidget.h" #include "namechooserwidget.h" #include "passwordchooserwidget.h" #include "offlineonlywidget.h" #include "vaultdeletionwidget.h" using PlasmaVault::Vault; -class VaultConfigurationWizard::Private { +class VaultConfigurationDialog::Private { public: - VaultConfigurationWizard *const q; + VaultConfigurationDialog *const q; Vault *vault; - Ui::VaultConfigurationWizard ui; + Ui::VaultConfigurationDialog ui; QStackedLayout *layout; steps currentSteps; QVector currentModuleDialogs; QSet invalidModules; steps defaultSteps { i18n("General") / step { nameChooser(), directoryChooser(DirectoryChooserWidget::RequireEmptyMountPoint) }, i18n("Advanced") / step { activitiesChooser(), offlineOnlyChooser() }, i18n("Delete") / step { vaultDeletion() } }; Logic logic { { "encfs" / i18n("EncFS"), defaultSteps }, { "cryfs" / i18n("CryFS"), defaultSteps } }; - Private(Vault *vault, VaultConfigurationWizard *parent) + Private(Vault *vault, VaultConfigurationDialog *parent) : q(parent) , vault(vault) { ui.setupUi(parent); ui.message->hide(); layout = new QStackedLayout(); layout->setContentsMargins(0, 0, 0, 0); ui.container->setLayout(layout); auto tabs = new QTabWidget(); layout->addWidget(tabs); // Loading the backends auto modules = logic[Key(vault->backend().toLatin1())]; Vault::Payload payload { { KEY_DEVICE, QVariant(vault->device()) }, { KEY_NAME, QVariant(vault->name()) }, { KEY_MOUNT_POINT, QVariant(vault->mountPoint()) }, { KEY_ACTIVITIES, QVariant(vault->activities()) }, { KEY_OFFLINEONLY, QVariant(vault->isOfflineOnly()) } }; for (const auto& module: modules) { DialogModule *stepWidget = new CompoundDialogModule(module); stepWidget->init(payload); tabs->addTab(stepWidget, module.title()); currentModuleDialogs << stepWidget; QObject::connect( stepWidget, &DialogModule::isValidChanged, q, [this,stepWidget] (bool valid) { if (valid) { invalidModules.remove(stepWidget); } else { invalidModules << stepWidget; } ui.buttons->button(QDialogButtonBox::Ok)->setEnabled(invalidModules.isEmpty()); }); QObject::connect( stepWidget, &DialogModule::requestCancellation, q, [this] { q->reject(); }); } } void setVaultOpened(bool vaultOpened) { bool configurationEnabled = !vaultOpened; ui.buttons->button(QDialogButtonBox::Ok)->setEnabled(configurationEnabled); ui.frameUnlockVault->setVisible(!configurationEnabled); ui.container->setEnabled(configurationEnabled); } void saveConfiguration() { Vault::Payload collectedPayload; qDebug() << "Getting the data"; for (const auto* module: currentModuleDialogs) { qDebug() << "Data: " << module->fields(); collectedPayload.unite(module->fields()); } const auto name = collectedPayload[KEY_NAME].toString(); const PlasmaVault::MountPoint mountPoint(collectedPayload[KEY_MOUNT_POINT].toString()); const auto activities = collectedPayload[KEY_ACTIVITIES].toStringList(); const auto isOfflineOnly = collectedPayload[KEY_OFFLINEONLY].toBool(); if (name.isEmpty() || mountPoint.isEmpty()) return; vault->setName(name); vault->setMountPoint(mountPoint); vault->setActivities(activities); vault->setIsOfflineOnly(isOfflineOnly); } }; -VaultConfigurationWizard::VaultConfigurationWizard(Vault *vault, QWidget *parent) +VaultConfigurationDialog::VaultConfigurationDialog(Vault *vault, QWidget *parent) : QDialog(parent) , d(new Private(vault, this)) { setWindowTitle(i18n("Configure")); d->setVaultOpened(vault->isOpened()); connect(d->ui.buttonCloseVault, &QPushButton::clicked, this, [=] () { vault->close(); }); connect(vault, &Vault::isOpenedChanged, this, [=] (bool isOpened) { d->setVaultOpened(isOpened); }); connect(d->ui.buttons, &QDialogButtonBox::accepted, this, [=] { d->saveConfiguration(); }); } -VaultConfigurationWizard::~VaultConfigurationWizard() +VaultConfigurationDialog::~VaultConfigurationDialog() { } diff --git a/kded/ui/vaultconfigurationwizard.h b/kded/ui/vaultconfigurationdialog.h similarity index 82% rename from kded/ui/vaultconfigurationwizard.h rename to kded/ui/vaultconfigurationdialog.h index 9765248..8ad065c 100644 --- a/kded/ui/vaultconfigurationwizard.h +++ b/kded/ui/vaultconfigurationdialog.h @@ -1,47 +1,47 @@ /* * Copyright 2017 by Ivan Cukic * * 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) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * 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, see . */ -#ifndef PLASMAVAULT_KDED_UI_VAULT_CONFIGURATION_WIZARD_H -#define PLASMAVAULT_KDED_UI_VAULT_CONFIGURATION_WIZARD_H +#ifndef PLASMAVAULT_KDED_UI_VAULT_CONFIGURATION_DIALOG_H +#define PLASMAVAULT_KDED_UI_VAULT_CONFIGURATION_DIALOG_H #include namespace PlasmaVault { class Vault; } // namespace PlasmaVault -class VaultConfigurationWizard: public QDialog { +class VaultConfigurationDialog: public QDialog { Q_OBJECT public: - VaultConfigurationWizard(PlasmaVault::Vault *vault, QWidget *parent = nullptr); - ~VaultConfigurationWizard(); + VaultConfigurationDialog(PlasmaVault::Vault *vault, QWidget *parent = nullptr); + ~VaultConfigurationDialog(); Q_SIGNALS: void configurationChanged(PlasmaVault::Vault *vault); private: class Private; QScopedPointer d; }; #endif // include guard diff --git a/kded/ui/vaultconfigurationwizard.ui b/kded/ui/vaultconfigurationdialog.ui similarity index 91% rename from kded/ui/vaultconfigurationwizard.ui rename to kded/ui/vaultconfigurationdialog.ui index 56148b2..ebbc1ca 100644 --- a/kded/ui/vaultconfigurationwizard.ui +++ b/kded/ui/vaultconfigurationdialog.ui @@ -1,123 +1,124 @@ - VaultConfigurationWizard - + VaultConfigurationDialog + 0 0 646 529 Dialog - + + .. QFrame::StyledPanel QFrame::Raised The vault configuration can only be changed while it is closed. Qt::Horizontal 40 20 Close the vault false Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok KMessageWidget QFrame
kmessagewidget.h
buttons accepted() - VaultConfigurationWizard + VaultConfigurationDialog accept() 254 522 157 274 buttons rejected() - VaultConfigurationWizard + VaultConfigurationDialog reject() 322 522 286 274
diff --git a/kded/ui/vaultcreationwizard.cpp b/kded/ui/vaultcreationwizard.cpp index 1e26c78..16fa5f1 100644 --- a/kded/ui/vaultcreationwizard.cpp +++ b/kded/ui/vaultcreationwizard.cpp @@ -1,323 +1,168 @@ /* * Copyright 2017 by Ivan Cukic * * 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) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * 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, see . */ #include "vaultcreationwizard.h" #include "ui_vaultcreationwizard.h" #include #include #include #include #include "dialogdsl.h" #include "vault.h" using namespace DialogDsl; using namespace DialogDsl::operators; #include "backendchooserwidget.h" #include "activitieslinkingwidget.h" #include "cryfscypherchooserwidget.h" #include "directorypairchooserwidget.h" #include "noticewidget.h" #include "passwordchooserwidget.h" #include "offlineonlywidget.h" -class VaultCreationWizard::Private { -public: - VaultCreationWizard *const q; - Ui::VaultCreationWizard ui; - QPushButton *buttonPrevious; - QPushButton *buttonNext; - QPushButton *buttonCreate; - QStackedLayout *layout; - - inline void buttonNextSetEnabled(bool enabled) { - buttonNext->setEnabled(enabled); - buttonCreate->setEnabled(enabled); - } +#include "vaultwizardbase.h" - QVector currentStepModules; - steps currentSteps; - BackendChooserWidget *firstStepModule = nullptr; - DialogDsl::DialogModule *currentModule = nullptr; +class VaultCreationWizard::Private: public WBASE(VaultCreationWizard) { +public: Logic logic { { "encfs" / i18n("EncFS"), { step { notice("encfs-message", i18n("Security notice:\n\ According to a security audit by Taylor Hornby (Defuse Security),\n\ the current implementation of Encfs is vulnerable or potentially vulnerable\n\ to multiple types of attacks.\n\ For example, an attacker with read/write access\n\ to encrypted data might lower the decryption complexity\n\ for subsequently encrypted data without this being noticed by a legitimate user,\n\ or might use timing analysis to deduce information.\n\

\n\ This means that you should not synchronize\n\ the encrypted data to a cloud storage service,\n\ or use it in other circumstances where the attacker\n\ can frequently access the encrypted data.\n\

\n\ See defuse.ca/audits/encfs.htm for more information.")) }, step { passwordChooser() }, step { directoryPairChooser( DirectoryPairChooserWidget::AutoFillPaths | DirectoryPairChooserWidget::ShowMountPointPicker | DirectoryPairChooserWidget::RequireEmptyMountPoint ) }, step { activitiesChooser(), offlineOnlyChooser() } } }, { "cryfs" / i18n("CryFS"), { step { notice("cryfs-message", i18n("Security notice:\n\ CryFS encrypts your files, so you can safely store them anywhere.\n\ It works well together with cloud services like Dropbox, iCloud, OneDrive and others.\n\

\n\ Unlike some other file-system overlay solutions,\n\ it does not expose the directory structure,\n\ the number of files nor the file sizes\n\ through the encrypted data format.\n\

\n\ One important thing to note is that,\n\ while CryFS is considered safe,\n\ there is no independent security audit\n\ which confirms this.")) }, step { passwordChooser() }, step { directoryPairChooser( DirectoryPairChooserWidget::AutoFillPaths | DirectoryPairChooserWidget::ShowDevicePicker | DirectoryPairChooserWidget::ShowMountPointPicker | DirectoryPairChooserWidget::RequireEmptyDevice | DirectoryPairChooserWidget::RequireEmptyMountPoint ) }, step { cryfsCypherChooser(), activitiesChooser(), offlineOnlyChooser() } } } }; - // to suggest the highest priority to the user as a starting value - QMap priorities = { - { "encfs", 1 }, - { "cryfs", 2 } - }; - - template - QPushButton *addDialogButton(const QString &icon, const QString &title, ClickHandler clickHandler) - { - auto button = new QPushButton(QIcon::fromTheme(icon), title); - ui.buttons->addButton(button, QDialogButtonBox::ActionRole); - QObject::connect(button, &QPushButton::clicked, - q, clickHandler); - return button; - } - Private(VaultCreationWizard *parent) - : q(parent) - { - ui.setupUi(parent); - ui.message->hide(); - - layout = new QStackedLayout(); - layout->setContentsMargins(0, 0, 0, 0); - ui.container->setLayout(layout); - - // The dialog buttons do not have previous/next by default - // so we need to create them - buttonPrevious = addDialogButton("go-previous", i18n("Previous"), [this] { previousStep(); }); - buttonNext = addDialogButton("go-next", i18n("Next"), [this] { nextStep(); }); - buttonCreate = addDialogButton("dialog-ok-apply", i18n("Create"), [this] { createVault(); }); - - // The 'Create' button should be hidden by default - buttonCreate->hide(); - buttonPrevious->setEnabled(false); - buttonNextSetEnabled(false); - - // Loading the fist page of the wizard - firstStepModule = new BackendChooserWidget(); - setCurrentModule(firstStepModule); - layout->addWidget(firstStepModule); - - // Loading the backends to the combo box - for (const auto& key: logic.keys()) { - firstStepModule->addItem(key, key.translation(), priorities.value(key)); - } - firstStepModule->checkBackendAvailable(); - } - - void setCurrentModule(DialogDsl::DialogModule *module) + : WBASE(VaultCreationWizard)(parent) { - // If there is a current module already, disconnect it - if (currentModule) { - currentModule->aboutToBeHidden(); - currentModule->disconnect(); - } - - // The current module needs to be changed - currentModule = module; - currentModule->aboutToBeShown(); - - QObject::connect( - currentModule, &DialogModule::isValidChanged, - q, [&] (bool valid) { - buttonNextSetEnabled(valid); - }); - - // Lets update the button states - - // 1. next/create button is enabled only if the current - // module is in the valid state - buttonNextSetEnabled(currentModule->isValid()); - - // 2. previous button is enabled only if we are not on - // the first page - buttonPrevious->setEnabled(currentStepModules.size() > 0); - - // 3. If we have loaded the last page, we want to show the - // 'Create' button instead of 'Next' - if (!currentSteps.isEmpty() && currentStepModules.size() == currentSteps.size()) { - buttonNext->hide(); - buttonCreate->show(); - } else { - buttonNext->show(); - buttonCreate->hide(); - } - - // Calling to initialize the module -- we are passing all the - // previously collected data to it - auto collectedPayload = firstStepModule->fields(); - for (const auto* module: currentStepModules) { - collectedPayload.unite(module->fields()); - } - currentModule->init(collectedPayload); - } - - void previousStep() - { - if (currentStepModules.isEmpty()) return; - - // We want to kill the current module, and move to the previous one - currentStepModules.takeLast(); - currentModule->deleteLater();; - - if (currentStepModules.size()) { - setCurrentModule(currentStepModules.last()); - } else { - setCurrentModule(firstStepModule); - } - - if (!currentModule->shouldBeShown()) { - previousStep(); - } - } - - void nextStep() - { - // If the step modules are empty, this means that we - // have just started - the user chose the backend - // and we need to load the vault creation steps - if (currentStepModules.isEmpty()) { - const auto &fields = firstStepModule->fields(); - currentSteps = logic[fields[KEY_BACKEND].toByteArray()]; - } - - // Loading the modulws that we need to show now - auto subModules = currentSteps[currentStepModules.size()]; - - // If there is only one module on the current page, - // lets not complicate things by creating the compound module - DialogModule *stepWidget = - (subModules.size() == 1) ? subModules.first()() - : new CompoundDialogModule(subModules); - - // Adding the widget to the list and the layout - currentStepModules << stepWidget; - layout->addWidget(stepWidget); - layout->setCurrentWidget(stepWidget); - - // Set the newly added module to be the current - setCurrentModule(stepWidget); - - if (!currentModule->shouldBeShown()) { - nextStep(); - } + initBase(); } - void createVault() + void finish() { auto collectedPayload = firstStepModule->fields(); for (const auto* module: currentStepModules) { collectedPayload.unite(module->fields()); } const auto name = collectedPayload[KEY_NAME].toString(); const PlasmaVault::Device device(collectedPayload[KEY_DEVICE].toString()); const PlasmaVault::MountPoint mountPoint(collectedPayload[KEY_MOUNT_POINT].toString()); auto vault = new PlasmaVault::Vault(device, q); auto future = vault->create(name, mountPoint, collectedPayload); auto result = AsynQt::await(future); if (result) { emit q->createdVault(vault); q->QDialog::accept(); } else { ui.message->setText(result.error().message()); ui.message->setMessageType(KMessageWidget::Error); ui.message->show(); vault->scheduleDeletion(); } } }; VaultCreationWizard::VaultCreationWizard(QWidget *parent) : QDialog(parent) , d(new Private(this)) { setWindowTitle(i18nc("@title:window", "Create a New Vault")); } VaultCreationWizard::~VaultCreationWizard() { } diff --git a/kded/ui/vaultimportingwizard.cpp b/kded/ui/vaultimportingwizard.cpp index 58f215d..3a95a15 100644 --- a/kded/ui/vaultimportingwizard.cpp +++ b/kded/ui/vaultimportingwizard.cpp @@ -1,290 +1,136 @@ /* * Copyright 2017 by Ivan Cukic * * 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) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * 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, see . */ #include "vaultimportingwizard.h" #include "ui_vaultimportingwizard.h" #include #include #include #include #include "dialogdsl.h" #include "vault.h" using namespace DialogDsl; using namespace DialogDsl::operators; #include "backendchooserwidget.h" #include "activitieslinkingwidget.h" #include "cryfscypherchooserwidget.h" #include "directorypairchooserwidget.h" #include "noticewidget.h" #include "passwordchooserwidget.h" #include "offlineonlywidget.h" -class VaultImportingWizard::Private { -public: - VaultImportingWizard *const q; - Ui::VaultImportingWizard ui; - QPushButton *buttonPrevious; - QPushButton *buttonNext; - QPushButton *buttonImport; - QStackedLayout *layout; - - inline void buttonNextSetEnabled(bool enabled) { - buttonNext->setEnabled(enabled); - buttonImport->setEnabled(enabled); - } +#include "vaultwizardbase.h" - QVector currentStepModules; - steps currentSteps; - BackendChooserWidget *firstStepModule = nullptr; - DialogDsl::DialogModule *currentModule = nullptr; +class VaultImportingWizard::Private: public WBASE(VaultImportingWizard) { +public: Logic logic { { "encfs" / i18n("EncFS"), { step { directoryPairChooser( DirectoryPairChooserWidget::ShowDevicePicker | DirectoryPairChooserWidget::ShowMountPointPicker | DirectoryPairChooserWidget::RequireExistingDevice | DirectoryPairChooserWidget::RequireEmptyMountPoint ) }, step { passwordChooser() }, step { activitiesChooser(), offlineOnlyChooser() } } }, { "cryfs" / i18n("CryFS"), { step { directoryPairChooser( DirectoryPairChooserWidget::ShowDevicePicker | DirectoryPairChooserWidget::ShowMountPointPicker | DirectoryPairChooserWidget::RequireExistingDevice | DirectoryPairChooserWidget::RequireEmptyMountPoint ) }, step { passwordChooser() }, step { activitiesChooser(), offlineOnlyChooser() } } } }; - // to suggest the highest priority to the user as a starting value - QMap priorities = { - { "encfs", 1 }, - { "cryfs", 2 } - }; - - template - QPushButton *addDialogButton(const QString &icon, const QString &title, ClickHandler clickHandler) - { - auto button = new QPushButton(QIcon::fromTheme(icon), title); - ui.buttons->addButton(button, QDialogButtonBox::ActionRole); - QObject::connect(button, &QPushButton::clicked, - q, clickHandler); - return button; - } - Private(VaultImportingWizard *parent) - : q(parent) - { - ui.setupUi(parent); - ui.message->hide(); - - layout = new QStackedLayout(); - layout->setContentsMargins(0, 0, 0, 0); - ui.container->setLayout(layout); - - // The dialog buttons do not have previous/next by default - // so we need to create them - buttonPrevious = addDialogButton("go-previous", i18n("Previous"), [this] { previousStep(); }); - buttonNext = addDialogButton("go-next", i18n("Next"), [this] { nextStep(); }); - buttonImport = addDialogButton("dialog-ok-apply", i18n("Import"), [this] { importVault(); }); - - // The 'Import' button should be hidden by default - buttonImport->hide(); - buttonPrevious->setEnabled(false); - buttonNextSetEnabled(false); - - // Loading the fist page of the wizard - firstStepModule = new BackendChooserWidget(); - setCurrentModule(firstStepModule); - layout->addWidget(firstStepModule); - - // Loading the backends to the combo box - for (const auto& key: logic.keys()) { - firstStepModule->addItem(key, key.translation(), priorities.value(key)); - } - firstStepModule->checkBackendAvailable(); - } - - void setCurrentModule(DialogDsl::DialogModule *module) + : WBASE(VaultImportingWizard)(parent) { - // If there is a current module already, disconnect it - if (currentModule) { - currentModule->aboutToBeHidden(); - currentModule->disconnect(); - } - - // The current module needs to be changed - currentModule = module; - currentModule->aboutToBeShown(); - - QObject::connect( - currentModule, &DialogModule::isValidChanged, - q, [&] (bool valid) { - buttonNextSetEnabled(valid); - }); - - // Lets update the button states - - // 1. next/Import button is enabled only if the current - // module is in the valid state - buttonNextSetEnabled(currentModule->isValid()); - - // 2. previous button is enabled only if we are not on - // the first page - buttonPrevious->setEnabled(currentStepModules.size() > 0); - - // 3. If we have loaded the last page, we want to show the - // 'Import' button instead of 'Next' - if (!currentSteps.isEmpty() && currentStepModules.size() == currentSteps.size()) { - buttonNext->hide(); - buttonImport->show(); - } else { - buttonNext->show(); - buttonImport->hide(); - } - - // Calling to initialize the module -- we are passing all the - // previously collected data to it - auto collectedPayload = firstStepModule->fields(); - for (const auto* module: currentStepModules) { - collectedPayload.unite(module->fields()); - } - currentModule->init(collectedPayload); - } - - void previousStep() - { - if (currentStepModules.isEmpty()) return; - - // We want to kill the current module, and move to the previous one - currentStepModules.takeLast(); - currentModule->deleteLater();; - - if (currentStepModules.size()) { - setCurrentModule(currentStepModules.last()); - } else { - setCurrentModule(firstStepModule); - } - - if (!currentModule->shouldBeShown()) { - previousStep(); - } - } - - void nextStep() - { - // If the step modules are empty, this means that we - // have just started - the user chose the backend - // and we need to load the vault creation steps - if (currentStepModules.isEmpty()) { - const auto &fields = firstStepModule->fields(); - currentSteps = logic[fields[KEY_BACKEND].toByteArray()]; - } - - // Loading the modulws that we need to show now - auto subModules = currentSteps[currentStepModules.size()]; - - // If there is only one module on the current page, - // lets not complicate things by creating the compound module - DialogModule *stepWidget = - (subModules.size() == 1) ? subModules.first()() - : new CompoundDialogModule(subModules); - - // Adding the widget to the list and the layout - currentStepModules << stepWidget; - layout->addWidget(stepWidget); - layout->setCurrentWidget(stepWidget); - - // Set the newly added module to be the current - setCurrentModule(stepWidget); - - if (!currentModule->shouldBeShown()) { - nextStep(); - } + lastButtonText = i18n("Import"); + initBase(); } - void importVault() + void finish() { auto collectedPayload = firstStepModule->fields(); for (const auto* module: currentStepModules) { collectedPayload.unite(module->fields()); } const auto name = collectedPayload[KEY_NAME].toString(); const PlasmaVault::Device device(collectedPayload[KEY_DEVICE].toString()); const PlasmaVault::MountPoint mountPoint(collectedPayload[KEY_MOUNT_POINT].toString()); auto vault = new PlasmaVault::Vault(device, q); auto future = vault->import(name, mountPoint, collectedPayload); auto result = AsynQt::await(future); if (result) { emit q->importedVault(vault); q->QDialog::accept(); } else { ui.message->setText(result.error().message()); ui.message->setMessageType(KMessageWidget::Error); ui.message->show(); vault->scheduleDeletion(); } } }; VaultImportingWizard::VaultImportingWizard(QWidget *parent) : QDialog(parent) , d(new Private(this)) { setWindowTitle(i18nc("@title:window", "Import an Existing Vault")); } VaultImportingWizard::~VaultImportingWizard() { } diff --git a/kded/ui/vaultimportingwizard.cpp b/kded/ui/vaultwizardbase.h similarity index 55% copy from kded/ui/vaultimportingwizard.cpp copy to kded/ui/vaultwizardbase.h index 58f215d..9f6372d 100644 --- a/kded/ui/vaultimportingwizard.cpp +++ b/kded/ui/vaultwizardbase.h @@ -1,290 +1,218 @@ /* * Copyright 2017 by Ivan Cukic * * 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) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * 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, see . */ -#include "vaultimportingwizard.h" -#include "ui_vaultimportingwizard.h" +#ifndef PLASMAVAULT_KDED_UI_VAULT_WIZARD_BASE_P_H +#define PLASMAVAULT_KDED_UI_VAULT_WIZARD_BASE_P_H #include -#include -#include -#include -#include "dialogdsl.h" -#include "vault.h" +namespace PlasmaVault { + class Vault; +} // namespace PlasmaVault -using namespace DialogDsl; -using namespace DialogDsl::operators; +#define WBASE(Class) VaultWizardBase -#include "backendchooserwidget.h" -#include "activitieslinkingwidget.h" -#include "cryfscypherchooserwidget.h" -#include "directorypairchooserwidget.h" -#include "noticewidget.h" -#include "passwordchooserwidget.h" -#include "offlineonlywidget.h" - -class VaultImportingWizard::Private { +template +class VaultWizardBase { public: - VaultImportingWizard *const q; - Ui::VaultImportingWizard ui; + Class *const q; + Ui ui; + QPushButton *buttonPrevious; QPushButton *buttonNext; - QPushButton *buttonImport; QStackedLayout *layout; - inline void buttonNextSetEnabled(bool enabled) { - buttonNext->setEnabled(enabled); - buttonImport->setEnabled(enabled); + bool lastModule = false; + QString lastButtonText; + inline void setLastModule(bool last) { + lastModule = last; + if (last) { + buttonNext->setText(lastButtonText); + buttonNext->setIcon(QIcon::fromTheme("dialog-ok-apply")); + } else { + buttonNext->setText(i18n("Next")); + buttonNext->setIcon(QIcon::fromTheme("go-next")); + } } QVector currentStepModules; steps currentSteps; BackendChooserWidget *firstStepModule = nullptr; DialogDsl::DialogModule *currentModule = nullptr; - Logic logic - { - { "encfs" / i18n("EncFS"), - { - step { directoryPairChooser( - DirectoryPairChooserWidget::ShowDevicePicker | - DirectoryPairChooserWidget::ShowMountPointPicker | - DirectoryPairChooserWidget::RequireExistingDevice | - DirectoryPairChooserWidget::RequireEmptyMountPoint - ) }, - step { passwordChooser() }, - step { - activitiesChooser(), - offlineOnlyChooser() - } - } - }, - - { "cryfs" / i18n("CryFS"), - { - step { directoryPairChooser( - DirectoryPairChooserWidget::ShowDevicePicker | - DirectoryPairChooserWidget::ShowMountPointPicker | - DirectoryPairChooserWidget::RequireExistingDevice | - DirectoryPairChooserWidget::RequireEmptyMountPoint - ) }, - step { passwordChooser() }, - step { - activitiesChooser(), - offlineOnlyChooser() - } - } - } - }; // to suggest the highest priority to the user as a starting value QMap priorities = { { "encfs", 1 }, { "cryfs", 2 } }; template - QPushButton *addDialogButton(const QString &icon, const QString &title, ClickHandler clickHandler) + QPushButton *addDialogButton(const QString &icon, const QString &title, + ClickHandler clickHandler) { auto button = new QPushButton(QIcon::fromTheme(icon), title); ui.buttons->addButton(button, QDialogButtonBox::ActionRole); QObject::connect(button, &QPushButton::clicked, q, clickHandler); return button; } - Private(VaultImportingWizard *parent) + Impl* self() + { + return static_cast(this); + } + + VaultWizardBase(Class *parent) : q(parent) { ui.setupUi(parent); ui.message->hide(); layout = new QStackedLayout(); layout->setContentsMargins(0, 0, 0, 0); ui.container->setLayout(layout); + lastButtonText = i18n("Create"); + } + + void initBase() + { // The dialog buttons do not have previous/next by default // so we need to create them - buttonPrevious = addDialogButton("go-previous", i18n("Previous"), [this] { previousStep(); }); - buttonNext = addDialogButton("go-next", i18n("Next"), [this] { nextStep(); }); - buttonImport = addDialogButton("dialog-ok-apply", i18n("Import"), [this] { importVault(); }); + buttonPrevious = addDialogButton("go-previous", i18n("Previous"), + [this] { previousStep(); }); + buttonNext = addDialogButton("go-next", i18n("Next"), + [this] { if (lastModule) self()->finish(); else nextStep(); }); - // The 'Import' button should be hidden by default - buttonImport->hide(); + // The 'Create' button should be hidden by default buttonPrevious->setEnabled(false); - buttonNextSetEnabled(false); + buttonNext->setEnabled(false); + buttonNext->setDefault(true); // Loading the fist page of the wizard firstStepModule = new BackendChooserWidget(); setCurrentModule(firstStepModule); layout->addWidget(firstStepModule); // Loading the backends to the combo box - for (const auto& key: logic.keys()) { + for (const auto& key: self()->logic.keys()) { firstStepModule->addItem(key, key.translation(), priorities.value(key)); } firstStepModule->checkBackendAvailable(); } void setCurrentModule(DialogDsl::DialogModule *module) { // If there is a current module already, disconnect it if (currentModule) { currentModule->aboutToBeHidden(); currentModule->disconnect(); } // The current module needs to be changed currentModule = module; currentModule->aboutToBeShown(); QObject::connect( currentModule, &DialogModule::isValidChanged, q, [&] (bool valid) { - buttonNextSetEnabled(valid); + buttonNext->setEnabled(valid); }); // Lets update the button states - // 1. next/Import button is enabled only if the current + // 1. next/create button is enabled only if the current // module is in the valid state - buttonNextSetEnabled(currentModule->isValid()); + buttonNext->setEnabled(currentModule->isValid()); // 2. previous button is enabled only if we are not on // the first page buttonPrevious->setEnabled(currentStepModules.size() > 0); // 3. If we have loaded the last page, we want to show the - // 'Import' button instead of 'Next' - if (!currentSteps.isEmpty() && currentStepModules.size() == currentSteps.size()) { - buttonNext->hide(); - buttonImport->show(); - } else { - buttonNext->show(); - buttonImport->hide(); - } + // 'Create' button instead of 'Next' + setLastModule(!currentSteps.isEmpty() && currentStepModules.size() == currentSteps.size()); // Calling to initialize the module -- we are passing all the // previously collected data to it auto collectedPayload = firstStepModule->fields(); for (const auto* module: currentStepModules) { collectedPayload.unite(module->fields()); } currentModule->init(collectedPayload); } void previousStep() { if (currentStepModules.isEmpty()) return; // We want to kill the current module, and move to the previous one currentStepModules.takeLast(); currentModule->deleteLater();; if (currentStepModules.size()) { setCurrentModule(currentStepModules.last()); } else { setCurrentModule(firstStepModule); } if (!currentModule->shouldBeShown()) { previousStep(); } } void nextStep() { // If the step modules are empty, this means that we // have just started - the user chose the backend // and we need to load the vault creation steps if (currentStepModules.isEmpty()) { const auto &fields = firstStepModule->fields(); - currentSteps = logic[fields[KEY_BACKEND].toByteArray()]; + currentSteps = self()->logic[fields[KEY_BACKEND].toByteArray()]; } // Loading the modulws that we need to show now auto subModules = currentSteps[currentStepModules.size()]; // If there is only one module on the current page, // lets not complicate things by creating the compound module DialogModule *stepWidget = (subModules.size() == 1) ? subModules.first()() : new CompoundDialogModule(subModules); // Adding the widget to the list and the layout currentStepModules << stepWidget; layout->addWidget(stepWidget); layout->setCurrentWidget(stepWidget); // Set the newly added module to be the current setCurrentModule(stepWidget); if (!currentModule->shouldBeShown()) { nextStep(); } } - void importVault() - { - auto collectedPayload = firstStepModule->fields(); - for (const auto* module: currentStepModules) { - collectedPayload.unite(module->fields()); - } - - const auto name = collectedPayload[KEY_NAME].toString(); - const PlasmaVault::Device device(collectedPayload[KEY_DEVICE].toString()); - const PlasmaVault::MountPoint mountPoint(collectedPayload[KEY_MOUNT_POINT].toString()); - - auto vault = new PlasmaVault::Vault(device, q); - - auto future = vault->import(name, mountPoint, collectedPayload); - - auto result = AsynQt::await(future); - - if (result) { - emit q->importedVault(vault); - q->QDialog::accept(); - - } else { - ui.message->setText(result.error().message()); - ui.message->setMessageType(KMessageWidget::Error); - ui.message->show(); - vault->scheduleDeletion(); - } - } }; - -VaultImportingWizard::VaultImportingWizard(QWidget *parent) - : QDialog(parent) - , d(new Private(this)) -{ - setWindowTitle(i18nc("@title:window", "Import an Existing Vault")); -} - - - -VaultImportingWizard::~VaultImportingWizard() -{ -} - - +#endif // include guard