Commit 5ff134b2 authored by Ingo Klöcker's avatar Ingo Klöcker
Browse files

Add a configuration module for configuring groups of keys

GnuPG-bug-id: 5175, 5239
parent 2e51be87
......@@ -37,6 +37,9 @@ set(kcm_kleopatra_PART_SRCS
appearanceconfigpage.cpp
appearanceconfigwidget.cpp
gnupgsystemconfigurationpage.cpp
groupsconfigpage.cpp
groupsconfigwidget.cpp
${kleopatra_SOURCE_DIR}/src/dialogs/editgroupdialog.cpp
${kleopatra_BINARY_DIR}/src/kleopatra_debug.cpp
${_kcm_kleopatra_libkleopatraclient_extra_SRCS}
)
......@@ -82,7 +85,7 @@ install(FILES
kleopatra_config_dirserv.desktop
kleopatra_config_appear.desktop
kleopatra_config_gnupgsystem.desktop
kleopatra_config_groups.desktop
${_kcm_kleopatra_libkleopatraclient_extra_install_FILES}
DESTINATION ${KDE_INSTALL_KSERVICES5DIR}
)
......@@ -35,6 +35,7 @@ ConfigureDialog::ConfigureDialog(QWidget *parent)
{
setFaceType(KPageDialog::List);
setWindowTitle(i18nc("@title:window", "Configure"));
addModule(QStringLiteral("kleopatra_config_groups"));
addModule(QStringLiteral("kleopatra_config_dirserv"));
addModule(QStringLiteral("kleopatra_config_appear"));
addModule(QStringLiteral("kleopatra_config_cryptooperations"));
......
/*
conf/groupsconfigpage.cpp
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2021 g10 Code GmbH
SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include <config-kleopatra.h>
#include "groupsconfigpage.h"
#include "groupsconfigwidget.h"
#include <Libkleo/Debug>
#include <Libkleo/KeyCache>
#include <Libkleo/KeyGroup>
#include <QVBoxLayout>
#include "kleopatra_debug.h"
using namespace Kleo;
class GroupsConfigPage::Private
{
friend class ::GroupsConfigPage;
GroupsConfigPage *const q;
Private(GroupsConfigPage *qq);
public:
~Private() = default;
private:
GroupsConfigWidget *widget = nullptr;
};
GroupsConfigPage::Private::Private(GroupsConfigPage *qq)
: q(qq)
{
}
GroupsConfigPage::GroupsConfigPage(QWidget *parent, const QVariantList &args)
: KCModule(parent, args)
, d(new Private(this))
{
// do not show the Defaults button; it has no effect
setButtons(buttons() & ~KCModule::Default);
QVBoxLayout *layout = new QVBoxLayout(this);
layout->setContentsMargins(0, 0, 0, 0);
d->widget = new Kleo::GroupsConfigWidget(this);
if (QLayout *l = d->widget->layout()) {
l->setContentsMargins(0, 0, 0, 0);
}
layout->addWidget(d->widget);
connect(d->widget, &GroupsConfigWidget::changed, this, &GroupsConfigPage::markAsChanged);
}
GroupsConfigPage::~GroupsConfigPage() = default;
void GroupsConfigPage::load()
{
qCDebug(KLEOPATRA_LOG) << "GroupsConfigPage::load()";
d->widget->setGroups(KeyCache::instance()->configurableGroups());
}
void GroupsConfigPage::save()
{
KeyCache::mutableInstance()->saveConfigurableGroups(d->widget->groups());
// reload after saving to ensure that the groups reflect the saved groups (e.g. in case of immutable entries)
load();
}
extern "C"
{
Q_DECL_EXPORT KCModule *create_kleopatra_config_groups(QWidget *parent = nullptr, const QVariantList &args = QVariantList())
{
GroupsConfigPage *page = new GroupsConfigPage(parent, args);
page->setObjectName(QStringLiteral("kleopatra_config_groups"));
return page;
}
}
/*
conf/groupsconfigpage.h
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2021 g10 Code GmbH
SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#ifndef __KLEOPATRA_CONF_GROUPSCONFIGPAGE_H__
#define __KLEOPATRA_CONF_GROUPSCONFIGPAGE_H__
#include <KCModule>
class GroupsConfigPage : public KCModule
{
Q_OBJECT
public:
explicit GroupsConfigPage(QWidget *parent = nullptr, const QVariantList &args = QVariantList());
~GroupsConfigPage() override;
void load() override;
void save() override;
private:
class Private;
const std::unique_ptr<Private> d;
};
#endif // __KLEOPATRA_CONF_GROUPSCONFIGPAGE_H__
/*
conf/groupsconfigwidget.cpp
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2021 g10 Code GmbH
SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "groupsconfigwidget.h"
#include "dialogs/editgroupdialog.h"
#include <Libkleo/KeyCache>
#include <Libkleo/KeyGroup>
#include <Libkleo/KeyListModel>
#include <Libkleo/KeyListSortFilterProxyModel>
#include <KLocalizedString>
#include <KRandom>
#include <QLabel>
#include <QLineEdit>
#include <QListView>
#include <QPointer>
#include <QPushButton>
#include <QVBoxLayout>
#include "kleopatra_debug.h"
using namespace Kleo;
using namespace Kleo::Dialogs;
Q_DECLARE_METATYPE(KeyGroup)
namespace
{
class ProxyModel : public AbstractKeyListSortFilterProxyModel
{
Q_OBJECT
public:
ProxyModel(QObject *parent = nullptr)
: AbstractKeyListSortFilterProxyModel(parent)
{
}
~ProxyModel() override = default;
ProxyModel *clone() const override
{
// compiler-generated copy ctor is fine!
return new ProxyModel(*this);
}
};
}
class GroupsConfigWidget::Private
{
friend class ::Kleo::GroupsConfigWidget;
GroupsConfigWidget *const q;
struct {
QLineEdit *groupsFilter = nullptr;
QListView *groupsList = nullptr;
QPushButton *newButton = nullptr;
QPushButton *editButton = nullptr;
QPushButton *deleteButton = nullptr;
} ui;
AbstractKeyListModel *groupsModel = nullptr;
ProxyModel *groupsFilterModel = nullptr;
public:
Private(GroupsConfigWidget *qq)
: q(qq)
{
auto mainLayout = new QVBoxLayout(q);
auto groupsLayout = new QGridLayout();
groupsLayout->setColumnStretch(0, 1);
groupsLayout->setRowStretch(1, 1);
ui.groupsFilter = new QLineEdit();
ui.groupsFilter->setClearButtonEnabled(true);
ui.groupsFilter->setPlaceholderText(i18nc("Placeholder text", "Search..."));
groupsLayout->addWidget(ui.groupsFilter, 0, 0);
groupsModel = AbstractKeyListModel::createFlatKeyListModel(q);
groupsFilterModel = new ProxyModel(q);
groupsFilterModel->setFilterCaseSensitivity(Qt::CaseInsensitive);
groupsFilterModel->setFilterKeyColumn(KeyList::Summary);
groupsFilterModel->setSortCaseSensitivity(Qt::CaseInsensitive);
groupsFilterModel->setSourceModel(groupsModel);
groupsFilterModel->sort(KeyList::Summary, Qt::AscendingOrder);
ui.groupsList = new QListView();
ui.groupsList->setModel(groupsFilterModel);
ui.groupsList->setModelColumn(KeyList::Summary);
ui.groupsList->setSelectionBehavior(QAbstractItemView::SelectRows);
ui.groupsList->setSelectionMode(QAbstractItemView::SingleSelection);
groupsLayout->addWidget(ui.groupsList, 1, 0);
auto groupsButtonLayout = new QVBoxLayout();
ui.newButton = new QPushButton(i18n("New"));
groupsButtonLayout->addWidget(ui.newButton);
ui.editButton = new QPushButton(i18n("Edit"));
ui.editButton->setEnabled(false);
groupsButtonLayout->addWidget(ui.editButton);
ui.deleteButton = new QPushButton(i18n("Delete"));
ui.deleteButton->setEnabled(false);
groupsButtonLayout->addWidget(ui.deleteButton);
groupsButtonLayout->addStretch(1);
groupsLayout->addLayout(groupsButtonLayout, 1, 1);
mainLayout->addLayout(groupsLayout, /*stretch=*/ 1);
connect(ui.groupsFilter, &QLineEdit::textChanged, groupsFilterModel, &QSortFilterProxyModel::setFilterFixedString);
connect(ui.groupsList->selectionModel(), &QItemSelectionModel::selectionChanged,
q, [this] () { selectionChanged(); });
connect(ui.groupsList, &QListView::doubleClicked,
q, [this] (const QModelIndex &index) { editGroup(index); });
connect(ui.newButton, &QPushButton::clicked, q, [this] () { addGroup(); });
connect(ui.editButton, &QPushButton::clicked, q, [this] () { editGroup(); });
connect(ui.deleteButton, &QPushButton::clicked, q, [this] () { deleteGroup(); });
}
~Private()
{
}
private:
QModelIndex selectedIndex()
{
const QModelIndexList selected = ui.groupsList->selectionModel()->selectedRows();
return selected.empty() ? QModelIndex() : selected[0];
}
KeyGroup getGroup(const QModelIndex &index)
{
return index.isValid() ? ui.groupsList->model()->data(index, KeyList::GroupRole).value<KeyGroup>() : KeyGroup();
}
void selectionChanged()
{
const KeyGroup selectedGroup = getGroup(selectedIndex());
const bool selectedGroupIsEditable = !selectedGroup.isNull() && !selectedGroup.isImmutable();
ui.editButton->setEnabled(selectedGroupIsEditable);
ui.deleteButton->setEnabled(selectedGroupIsEditable);
}
KeyGroup showEditGroupDialog(KeyGroup group, const QString &windowTitle, EditGroupDialog::FocusWidget focusWidget)
{
auto dialog = std::make_unique<EditGroupDialog>(q);
dialog->setWindowTitle(windowTitle);
dialog->setGroupName(group.name());
const KeyGroup::Keys &keys = group.keys();
dialog->setGroupKeys(std::vector<GpgME::Key>(keys.cbegin(), keys.cend()));
dialog->setInitialFocus(focusWidget);
const int result = dialog->exec();
if (result == QDialog::Rejected) {
return KeyGroup();
}
group.setName(dialog->groupName());
group.setKeys(dialog->groupKeys());
return group;
}
void addGroup()
{
const KeyGroup::Id newId = KRandom::randomString(8);
KeyGroup group = KeyGroup(newId, i18nc("default name for new group of keys", "New Group"), {}, KeyGroup::ApplicationConfig);
group.setIsImmutable(false);
const KeyGroup newGroup = showEditGroupDialog(
group, i18nc("@title:window a group of keys", "New Group"), EditGroupDialog::GroupName);
if (newGroup.isNull()) {
return;
}
const QModelIndex newIndex = groupsModel->addGroup(newGroup);
if (!newIndex.isValid()) {
qCDebug(KLEOPATRA_LOG) << "Adding group to model failed";
return;
}
Q_EMIT q->changed();
}
void editGroup(const QModelIndex &index = QModelIndex())
{
const QModelIndex groupIndex = index.isValid() ? index : selectedIndex();
if (!groupIndex.isValid()) {
qCDebug(KLEOPATRA_LOG) << "selection is empty";
return;
}
const KeyGroup group = getGroup(groupIndex);
if (group.isNull()) {
qCDebug(KLEOPATRA_LOG) << "selected group is null";
return;
}
if (group.isImmutable()) {
qCDebug(KLEOPATRA_LOG) << "selected group is immutable";
return;
}
const KeyGroup updatedGroup = showEditGroupDialog(
group, i18nc("@title:window a group of keys", "Edit Group"), EditGroupDialog::KeysFilter);
if (updatedGroup.isNull()) {
return;
}
const bool success = ui.groupsList->model()->setData(groupIndex, QVariant::fromValue(updatedGroup));
if (!success) {
qCDebug(KLEOPATRA_LOG) << "Updating group in model failed";
return;
}
Q_EMIT q->changed();
}
void deleteGroup()
{
const QModelIndex groupIndex = selectedIndex();
if (!groupIndex.isValid()) {
qCDebug(KLEOPATRA_LOG) << "selection is empty";
return;
}
const KeyGroup group = getGroup(groupIndex);
if (group.isNull()) {
qCDebug(KLEOPATRA_LOG) << "selected group is null";
return;
}
const bool success = groupsModel->removeGroup(group);
if (!success) {
qCDebug(KLEOPATRA_LOG) << "Removing group from model failed";
return;
}
Q_EMIT q->changed();
}
};
GroupsConfigWidget::GroupsConfigWidget(QWidget *parent)
: QWidget(parent)
, d(new Private(this))
{
}
GroupsConfigWidget::~GroupsConfigWidget() = default;
void GroupsConfigWidget::setGroups(const std::vector<KeyGroup> &groups)
{
d->groupsModel->setGroups(groups);
}
std::vector<KeyGroup> GroupsConfigWidget::groups() const
{
std::vector<KeyGroup> result;
result.reserve(d->groupsModel->rowCount());
for (int row = 0; row < d->groupsModel->rowCount(); ++row) {
const QModelIndex index = d->groupsModel->index(row, 0);
result.push_back(d->groupsModel->group(index));
}
return result;
}
#include "groupsconfigwidget.moc"
/*
conf/groupsconfigwidget.h
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2021 g10 Code GmbH
SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#ifndef __KLEOPATRA_CONF_GROUPSCONFIGWIDGET_H__
#define __KLEOPATRA_CONF_GROUPSCONFIGWIDGET_H__
#include <QWidget>
#include <vector>
namespace Kleo
{
class KeyGroup;
class GroupsConfigWidget : public QWidget
{
Q_OBJECT
public:
explicit GroupsConfigWidget(QWidget *parent = nullptr);
~GroupsConfigWidget() override;
void setGroups(const std::vector<KeyGroup> &groups);
std::vector<KeyGroup> groups() const;
Q_SIGNALS:
void changed();
private:
class Private;
const std::unique_ptr<Private> d;
};
}
#endif // __KLEOPATRA_CONF_GROUPSCONFIGWIDGET_H__
[Desktop Entry]
Icon=group
Type=Service
X-KDE-ServiceTypes=KCModule
X-DocPath=kleopatra/configuration.html
X-KDE-ModuleType=Library
X-KDE-Library=kcm_kleopatra
X-KDE-FactoryName=kleopatra_config_groups
X-KDE-HasReadOnlyMode=false
X-KDE-ParentApp=kleopatra
X-KDE-ParentComponents=kleopatra
X-KDE-CfgDlgHierarchy=Kleopatra
Name=Groups
Comment=Configuration of groups of keys
X-KDE-Keywords=groups,keys,certificates,configuration
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment