mirror of
https://github.com/keepassxreboot/keepassxc.git
synced 2026-03-11 08:54:48 +00:00
Use ReportsWidgetBase for HIBP report widget
This commit is contained in:
parent
bcac630a9f
commit
be137a2cac
2 changed files with 29 additions and 198 deletions
|
|
@ -24,42 +24,17 @@
|
||||||
#include "gui/GuiTools.h"
|
#include "gui/GuiTools.h"
|
||||||
#include "gui/Icons.h"
|
#include "gui/Icons.h"
|
||||||
#include "gui/MessageBox.h"
|
#include "gui/MessageBox.h"
|
||||||
|
#include "gui/reports/ProxyModels.h"
|
||||||
|
|
||||||
#include <QMenu>
|
#include <QMenu>
|
||||||
#include <QShortcut>
|
#include <QShortcut>
|
||||||
#include <QSortFilterProxyModel>
|
|
||||||
#include <QStandardItemModel>
|
#include <QStandardItemModel>
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
namespace
|
|
||||||
{
|
|
||||||
class ReportSortProxyModel : public QSortFilterProxyModel
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
ReportSortProxyModel(QObject* parent)
|
|
||||||
: QSortFilterProxyModel(parent){};
|
|
||||||
~ReportSortProxyModel() override = default;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
bool lessThan(const QModelIndex& left, const QModelIndex& right) const override
|
|
||||||
{
|
|
||||||
// Sort count column by user data
|
|
||||||
if (left.column() == 2) {
|
|
||||||
return sourceModel()->data(left, Qt::UserRole).toInt()
|
|
||||||
< sourceModel()->data(right, Qt::UserRole).toInt();
|
|
||||||
}
|
|
||||||
// Otherwise use default sorting
|
|
||||||
return QSortFilterProxyModel::lessThan(left, right);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
ReportsWidgetHibp::ReportsWidgetHibp(QWidget* parent)
|
ReportsWidgetHibp::ReportsWidgetHibp(QWidget* parent)
|
||||||
: QWidget(parent)
|
: ReportsWidgetBase(parent, SortProxyModelKind::Hibp)
|
||||||
, m_ui(new Ui::ReportsWidgetHibp())
|
, m_ui(new Ui::ReportsWidgetHibp())
|
||||||
, m_referencesModel(new QStandardItemModel(this))
|
|
||||||
, m_modelProxy(new ReportSortProxyModel(this))
|
|
||||||
{
|
{
|
||||||
m_ui->setupUi(this);
|
m_ui->setupUi(this);
|
||||||
|
|
||||||
|
|
@ -183,7 +158,7 @@ void ReportsWidgetHibp::makeHibpTable()
|
||||||
m_referencesModel->appendRow(row);
|
m_referencesModel->appendRow(row);
|
||||||
|
|
||||||
// Store entry pointer per table row (used in double click handler)
|
// Store entry pointer per table row (used in double click handler)
|
||||||
m_rowToEntry.append(entry);
|
m_rowToEntry.append({group, entry});
|
||||||
}
|
}
|
||||||
|
|
||||||
// If there was an error, append the error message to the table
|
// If there was an error, append the error message to the table
|
||||||
|
|
@ -318,12 +293,12 @@ void ReportsWidgetHibp::emitEntryActivated(const QModelIndex& index)
|
||||||
// Find which database entry was double-clicked
|
// Find which database entry was double-clicked
|
||||||
auto mappedIndex = m_modelProxy->mapToSource(index);
|
auto mappedIndex = m_modelProxy->mapToSource(index);
|
||||||
const auto entry = m_rowToEntry[mappedIndex.row()];
|
const auto entry = m_rowToEntry[mappedIndex.row()];
|
||||||
if (entry) {
|
if (entry.second) {
|
||||||
// Found it, invoke entry editor
|
// Found it, invoke entry editor
|
||||||
m_editedEntry = entry;
|
m_editedEntry = entry.second;
|
||||||
m_editedPassword = entry->password();
|
m_editedPassword = entry.second->password();
|
||||||
m_editedExcluded = entry->excludeFromReports();
|
m_editedExcluded = entry.second->excludeFromReports();
|
||||||
emit entryActivated(const_cast<Entry*>(entry));
|
emit entryActivated(const_cast<Entry*>(entry.second));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -362,171 +337,28 @@ void ReportsWidgetHibp::refreshAfterEdit()
|
||||||
|
|
||||||
void ReportsWidgetHibp::customMenuRequested(QPoint pos)
|
void ReportsWidgetHibp::customMenuRequested(QPoint pos)
|
||||||
{
|
{
|
||||||
auto selected = m_ui->hibpTableView->selectionModel()->selectedRows();
|
|
||||||
if (selected.isEmpty()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create the context menu
|
// Create the context menu
|
||||||
const auto menu = new QMenu(this);
|
const auto menu = customMenuRequestedBase();
|
||||||
|
|
||||||
// Create the "edit entry" menu item if 1 row is selected
|
if(!menu) {
|
||||||
if (selected.size() == 1) {
|
|
||||||
const auto edit = new QAction(icons()->icon("entry-edit"), tr("Edit Entry…"), this);
|
|
||||||
menu->addAction(edit);
|
|
||||||
connect(edit, &QAction::triggered, edit, [this, selected] {
|
|
||||||
auto row = m_modelProxy->mapToSource(selected[0]).row();
|
|
||||||
auto entry = m_rowToEntry[row];
|
|
||||||
emit entryActivated(entry);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create the "Expire entry" menu item
|
|
||||||
const auto expEntry = new QAction(icons()->icon("entry-expire"), tr("Expire Entry(s)…", "", selected.size()), this);
|
|
||||||
menu->addAction(expEntry);
|
|
||||||
connect(expEntry, &QAction::triggered, this, &ReportsWidgetHibp::expireSelectedEntries);
|
|
||||||
|
|
||||||
// Create the "delete entry" menu item
|
|
||||||
const auto delEntry = new QAction(icons()->icon("entry-delete"), tr("Delete Entry(s)…", "", selected.size()), this);
|
|
||||||
menu->addAction(delEntry);
|
|
||||||
connect(delEntry, &QAction::triggered, this, &ReportsWidgetHibp::deleteSelectedEntries);
|
|
||||||
|
|
||||||
// Create the "exclude from reports" menu item
|
|
||||||
const auto excludeAction = new QAction(icons()->icon("reports-exclude"), tr("Exclude Entry(s) from reports"), this);
|
|
||||||
const auto excludeGroupsAction =
|
|
||||||
new QAction(icons()->icon("reports-exclude"), tr("Exclude Group(s) from reports"), this);
|
|
||||||
|
|
||||||
bool isExcluded = false;
|
|
||||||
bool isGroupExcluded = false;
|
|
||||||
|
|
||||||
for (auto index : selected) {
|
|
||||||
auto row = m_modelProxy->mapToSource(index).row();
|
|
||||||
auto entry = m_rowToEntry[row];
|
|
||||||
if (entry) {
|
|
||||||
// If at least one entry is excluded switch to inclusion
|
|
||||||
if (entry->excludeFromReports() || entry->group()->excludeFromReports()) {
|
|
||||||
isExcluded = true;
|
|
||||||
}
|
|
||||||
if (entry->group()->excludeFromReports()) {
|
|
||||||
isGroupExcluded = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
excludeAction->setCheckable(true);
|
|
||||||
excludeAction->setChecked(isExcluded);
|
|
||||||
|
|
||||||
excludeGroupsAction->setCheckable(true);
|
|
||||||
excludeGroupsAction->setChecked(isGroupExcluded);
|
|
||||||
|
|
||||||
menu->addAction(excludeAction);
|
|
||||||
connect(excludeAction, &QAction::toggled, excludeAction, [this, selected](bool checked) {
|
|
||||||
QSet<Group*> groups;
|
|
||||||
|
|
||||||
// If we are including entries (checked is false) but a group is excluded, ask the user if they
|
|
||||||
// would like to include the rest of the group as well (or keep it excluded).
|
|
||||||
// If they exclude it, we need to include the whole group, and then exclude
|
|
||||||
// the entries that aren't selected here.
|
|
||||||
if (!checked) {
|
|
||||||
for (const auto index : selected) {
|
|
||||||
auto row = m_modelProxy->mapToSource(index).row();
|
|
||||||
auto entry = m_rowToEntry[row];
|
|
||||||
|
|
||||||
if (entry) {
|
|
||||||
auto* group = entry->group();
|
|
||||||
if (group->excludeFromReports() && !groups.contains(group)) {
|
|
||||||
QString msg = tr("The Group for \"%1\" is excluded. Would you like to include all Entries from "
|
|
||||||
"there as well?")
|
|
||||||
.arg(entry->title());
|
|
||||||
auto response = MessageBox::question(this,
|
|
||||||
tr("Include Group?"),
|
|
||||||
msg,
|
|
||||||
MessageBox::Yes | MessageBox::No | MessageBox::Cancel,
|
|
||||||
MessageBox::No);
|
|
||||||
|
|
||||||
if (response == MessageBox::Cancel) {
|
|
||||||
return;
|
return;
|
||||||
} else if (response == MessageBox::Yes) {
|
|
||||||
group->setExcludeFromReports(false);
|
|
||||||
} else if (response == MessageBox::No) {
|
|
||||||
// We'll exclude all entries from the group here and then
|
|
||||||
// include the selected ones below
|
|
||||||
group->setExcludeFromReports(false);
|
|
||||||
group->markAllEntriesExcludedFromReports();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
groups.insert(group);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto index : selected) {
|
|
||||||
auto row = m_modelProxy->mapToSource(index).row();
|
|
||||||
auto entry = m_rowToEntry[row];
|
|
||||||
|
|
||||||
// If the containing group is excluded but the user wants to include
|
|
||||||
// this entry, ask if they want to keep the remaining items in the group
|
|
||||||
// excluded or included
|
|
||||||
if (entry) {
|
|
||||||
entry->setExcludeFromReports(checked);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
makeHibpTable();
|
|
||||||
});
|
|
||||||
|
|
||||||
menu->addAction(excludeGroupsAction);
|
|
||||||
connect(excludeGroupsAction, &QAction::toggled, excludeGroupsAction, [this, selected](bool checked) {
|
|
||||||
for (const auto index : selected) {
|
|
||||||
auto row = m_modelProxy->mapToSource(index).row();
|
|
||||||
auto entry = m_rowToEntry[row];
|
|
||||||
if (entry) {
|
|
||||||
entry->group()->setExcludeFromReports(checked);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
makeHibpTable();
|
|
||||||
});
|
|
||||||
|
|
||||||
// Show the context menu
|
// Show the context menu
|
||||||
menu->popup(m_ui->hibpTableView->viewport()->mapToGlobal(pos));
|
menu->popup(m_ui->hibpTableView->viewport()->mapToGlobal(pos));
|
||||||
}
|
}
|
||||||
|
|
||||||
QList<Entry*> ReportsWidgetHibp::getSelectedEntries()
|
|
||||||
{
|
|
||||||
QList<Entry*> selectedEntries;
|
|
||||||
for (auto index : m_ui->hibpTableView->selectionModel()->selectedRows()) {
|
|
||||||
auto row = m_modelProxy->mapToSource(index).row();
|
|
||||||
auto entry = m_rowToEntry[row];
|
|
||||||
if (entry) {
|
|
||||||
selectedEntries << entry;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return selectedEntries;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ReportsWidgetHibp::expireSelectedEntries()
|
|
||||||
{
|
|
||||||
for (auto entry : getSelectedEntries()) {
|
|
||||||
entry->expireNow();
|
|
||||||
}
|
|
||||||
|
|
||||||
makeHibpTable();
|
|
||||||
}
|
|
||||||
|
|
||||||
void ReportsWidgetHibp::deleteSelectedEntries()
|
|
||||||
{
|
|
||||||
QList<Entry*> selectedEntries = getSelectedEntries();
|
|
||||||
bool permanent = !m_db->metadata()->recycleBinEnabled();
|
|
||||||
if (GuiTools::confirmDeleteEntries(this, selectedEntries, permanent)) {
|
|
||||||
GuiTools::deleteEntriesResolveReferences(this, selectedEntries, permanent);
|
|
||||||
}
|
|
||||||
|
|
||||||
makeHibpTable();
|
|
||||||
}
|
|
||||||
|
|
||||||
void ReportsWidgetHibp::saveSettings()
|
void ReportsWidgetHibp::saveSettings()
|
||||||
{
|
{
|
||||||
// nothing to do - the tab is passive
|
// nothing to do - the tab is passive
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ReportsWidgetHibp::updateWidget()
|
||||||
|
{
|
||||||
|
makeHibpTable();
|
||||||
|
}
|
||||||
|
|
||||||
|
QTableView *ReportsWidgetHibp::getTableView() const
|
||||||
|
{
|
||||||
|
return m_ui->hibpTableView;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@
|
||||||
|
|
||||||
#include "config-keepassx.h"
|
#include "config-keepassx.h"
|
||||||
#include "gui/entry/EntryModel.h"
|
#include "gui/entry/EntryModel.h"
|
||||||
|
#include "gui/reports/ReportsWidgetBase.h"
|
||||||
|
|
||||||
#include <QWidget>
|
#include <QWidget>
|
||||||
|
|
||||||
|
|
@ -32,13 +33,14 @@ class Entry;
|
||||||
class Group;
|
class Group;
|
||||||
class QSortFilterProxyModel;
|
class QSortFilterProxyModel;
|
||||||
class QStandardItemModel;
|
class QStandardItemModel;
|
||||||
|
class QTableView;
|
||||||
|
|
||||||
namespace Ui
|
namespace Ui
|
||||||
{
|
{
|
||||||
class ReportsWidgetHibp;
|
class ReportsWidgetHibp;
|
||||||
}
|
}
|
||||||
|
|
||||||
class ReportsWidgetHibp : public QWidget
|
class ReportsWidgetHibp : public ReportsWidgetBase
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
|
|
@ -49,6 +51,10 @@ public:
|
||||||
void saveSettings();
|
void saveSettings();
|
||||||
void refreshAfterEdit();
|
void refreshAfterEdit();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void updateWidget() override;
|
||||||
|
QTableView *getTableView() const override;
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void entryActivated(Entry*);
|
void entryActivated(Entry*);
|
||||||
|
|
||||||
|
|
@ -58,22 +64,15 @@ public slots:
|
||||||
void fetchFailed(const QString& error);
|
void fetchFailed(const QString& error);
|
||||||
void makeHibpTable();
|
void makeHibpTable();
|
||||||
void customMenuRequested(QPoint);
|
void customMenuRequested(QPoint);
|
||||||
QList<Entry*> getSelectedEntries();
|
|
||||||
void expireSelectedEntries();
|
|
||||||
void deleteSelectedEntries();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void startValidation();
|
void startValidation();
|
||||||
static QString countToText(int count);
|
static QString countToText(int count);
|
||||||
|
|
||||||
QScopedPointer<Ui::ReportsWidgetHibp> m_ui;
|
QScopedPointer<Ui::ReportsWidgetHibp> m_ui;
|
||||||
QScopedPointer<QStandardItemModel> m_referencesModel;
|
|
||||||
QScopedPointer<QSortFilterProxyModel> m_modelProxy;
|
|
||||||
QSharedPointer<Database> m_db;
|
|
||||||
|
|
||||||
QMap<QString, int> m_pwndPasswords; // Passwords we found to have been pwned (value is pwn count)
|
QMap<QString, int> m_pwndPasswords; // Passwords we found to have been pwned (value is pwn count)
|
||||||
QString m_error; // Error message if download failed, else empty
|
QString m_error; // Error message if download failed, else empty
|
||||||
QList<Entry*> m_rowToEntry; // List index is table row
|
|
||||||
QPointer<Entry> m_editedEntry; // The entry we're currently editing
|
QPointer<Entry> m_editedEntry; // The entry we're currently editing
|
||||||
QString m_editedPassword; // The old password of the entry we're editing
|
QString m_editedPassword; // The old password of the entry we're editing
|
||||||
bool m_editedExcluded; // The old "known bad" flag of the entry we're editing
|
bool m_editedExcluded; // The old "known bad" flag of the entry we're editing
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue