mirror of
https://github.com/keepassxreboot/keepassxc.git
synced 2025-08-09 06:52:50 -04:00
Warn user if deleting entries that are referenced. (#1744)
On warning, references can be replaced with original values or ignored. Removal process can be also skipped for each conflicting entry. Resolves #852.
This commit is contained in:
parent
4d4c839afa
commit
c630214915
9 changed files with 205 additions and 61 deletions
|
@ -24,6 +24,7 @@
|
||||||
#include "core/DatabaseIcons.h"
|
#include "core/DatabaseIcons.h"
|
||||||
#include "core/Group.h"
|
#include "core/Group.h"
|
||||||
#include "core/Metadata.h"
|
#include "core/Metadata.h"
|
||||||
|
#include "core/Tools.h"
|
||||||
#include "totp/totp.h"
|
#include "totp/totp.h"
|
||||||
|
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
|
@ -100,6 +101,32 @@ void Entry::setUpdateTimeinfo(bool value)
|
||||||
m_updateTimeinfo = value;
|
m_updateTimeinfo = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString Entry::buildReference(const QUuid& uuid, const QString& field)
|
||||||
|
{
|
||||||
|
Q_ASSERT(EntryAttributes::DefaultAttributes.count(field) > 0);
|
||||||
|
|
||||||
|
QString uuidStr = Tools::uuidToHex(uuid).toUpper();
|
||||||
|
QString shortField;
|
||||||
|
|
||||||
|
if (field == EntryAttributes::TitleKey) {
|
||||||
|
shortField = "T";
|
||||||
|
} else if (field == EntryAttributes::UserNameKey) {
|
||||||
|
shortField = "U";
|
||||||
|
} else if (field == EntryAttributes::PasswordKey) {
|
||||||
|
shortField = "P";
|
||||||
|
} else if (field == EntryAttributes::URLKey) {
|
||||||
|
shortField = "A";
|
||||||
|
} else if (field == EntryAttributes::NotesKey) {
|
||||||
|
shortField = "N";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shortField.isEmpty()) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
return QString("{REF:%1@I:%2}").arg(shortField, uuidStr);
|
||||||
|
}
|
||||||
|
|
||||||
EntryReferenceType Entry::referenceType(const QString& referenceStr)
|
EntryReferenceType Entry::referenceType(const QString& referenceStr)
|
||||||
{
|
{
|
||||||
const QString referenceLowerStr = referenceStr.toLower();
|
const QString referenceLowerStr = referenceStr.toLower();
|
||||||
|
@ -130,7 +157,7 @@ const QUuid& Entry::uuid() const
|
||||||
|
|
||||||
const QString Entry::uuidToHex() const
|
const QString Entry::uuidToHex() const
|
||||||
{
|
{
|
||||||
return QString::fromLatin1(m_uuid.toRfc4122().toHex());
|
return Tools::uuidToHex(m_uuid);
|
||||||
}
|
}
|
||||||
|
|
||||||
QImage Entry::icon() const
|
QImage Entry::icon() const
|
||||||
|
@ -304,11 +331,25 @@ QString Entry::notes() const
|
||||||
return m_attributes->value(EntryAttributes::NotesKey);
|
return m_attributes->value(EntryAttributes::NotesKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString Entry::attribute(const QString& key) const
|
||||||
|
{
|
||||||
|
return m_attributes->value(key);
|
||||||
|
}
|
||||||
|
|
||||||
bool Entry::isExpired() const
|
bool Entry::isExpired() const
|
||||||
{
|
{
|
||||||
return m_data.timeInfo.expires() && m_data.timeInfo.expiryTime() < Clock::currentDateTimeUtc();
|
return m_data.timeInfo.expires() && m_data.timeInfo.expiryTime() < Clock::currentDateTimeUtc();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Entry::isAttributeReferenceOf(const QString& key, const QUuid& uuid) const
|
||||||
|
{
|
||||||
|
if (!m_attributes->isReference(key)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return m_attributes->value(key).contains(Tools::uuidToHex(uuid), Qt::CaseInsensitive);
|
||||||
|
}
|
||||||
|
|
||||||
bool Entry::hasReferences() const
|
bool Entry::hasReferences() const
|
||||||
{
|
{
|
||||||
const QList<QString> keyList = EntryAttributes::DefaultAttributes;
|
const QList<QString> keyList = EntryAttributes::DefaultAttributes;
|
||||||
|
@ -320,6 +361,26 @@ bool Entry::hasReferences() const
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Entry::hasReferencesTo(const QUuid& uuid) const
|
||||||
|
{
|
||||||
|
const QList<QString> keyList = EntryAttributes::DefaultAttributes;
|
||||||
|
for (const QString& key : keyList) {
|
||||||
|
if (isAttributeReferenceOf(key, uuid)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Entry::replaceReferencesWithValues(const Entry* other)
|
||||||
|
{
|
||||||
|
for (const QString& key : EntryAttributes::DefaultAttributes) {
|
||||||
|
if (isAttributeReferenceOf(key, other->uuid())) {
|
||||||
|
setDefaultAttribute(key, other->attribute(key));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
EntryAttributes* Entry::attributes()
|
EntryAttributes* Entry::attributes()
|
||||||
{
|
{
|
||||||
return m_attributes;
|
return m_attributes;
|
||||||
|
@ -496,6 +557,17 @@ void Entry::setNotes(const QString& notes)
|
||||||
m_attributes->set(EntryAttributes::NotesKey, notes, m_attributes->isProtected(EntryAttributes::NotesKey));
|
m_attributes->set(EntryAttributes::NotesKey, notes, m_attributes->isProtected(EntryAttributes::NotesKey));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Entry::setDefaultAttribute(const QString& attribute, const QString& value)
|
||||||
|
{
|
||||||
|
Q_ASSERT(EntryAttributes::isDefaultAttribute(attribute));
|
||||||
|
|
||||||
|
if (!EntryAttributes::isDefaultAttribute(attribute)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_attributes->set(attribute, value, m_attributes->isProtected(attribute));
|
||||||
|
}
|
||||||
|
|
||||||
void Entry::setExpires(const bool& value)
|
void Entry::setExpires(const bool& value)
|
||||||
{
|
{
|
||||||
if (m_data.timeInfo.expires() != value) {
|
if (m_data.timeInfo.expires() != value) {
|
||||||
|
@ -654,16 +726,17 @@ Entry* Entry::clone(CloneFlags flags) const
|
||||||
entry->m_attachments->copyDataFrom(m_attachments);
|
entry->m_attachments->copyDataFrom(m_attachments);
|
||||||
|
|
||||||
if (flags & CloneUserAsRef) {
|
if (flags & CloneUserAsRef) {
|
||||||
// Build the username reference
|
|
||||||
QString username = "{REF:U@I:" + uuidToHex() + "}";
|
|
||||||
entry->m_attributes->set(
|
entry->m_attributes->set(
|
||||||
EntryAttributes::UserNameKey, username.toUpper(), m_attributes->isProtected(EntryAttributes::UserNameKey));
|
EntryAttributes::UserNameKey,
|
||||||
|
buildReference(uuid(), EntryAttributes::UserNameKey),
|
||||||
|
m_attributes->isProtected(EntryAttributes::UserNameKey));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (flags & ClonePassAsRef) {
|
if (flags & ClonePassAsRef) {
|
||||||
QString password = "{REF:P@I:" + uuidToHex() + "}";
|
|
||||||
entry->m_attributes->set(
|
entry->m_attributes->set(
|
||||||
EntryAttributes::PasswordKey, password.toUpper(), m_attributes->isProtected(EntryAttributes::PasswordKey));
|
EntryAttributes::PasswordKey,
|
||||||
|
buildReference(uuid(), EntryAttributes::PasswordKey),
|
||||||
|
m_attributes->isProtected(EntryAttributes::PasswordKey));
|
||||||
}
|
}
|
||||||
|
|
||||||
entry->m_autoTypeAssociations->copyDataFrom(m_autoTypeAssociations);
|
entry->m_autoTypeAssociations->copyDataFrom(m_autoTypeAssociations);
|
||||||
|
|
|
@ -105,12 +105,16 @@ public:
|
||||||
QString username() const;
|
QString username() const;
|
||||||
QString password() const;
|
QString password() const;
|
||||||
QString notes() const;
|
QString notes() const;
|
||||||
|
QString attribute(const QString& key) const;
|
||||||
QString totp() const;
|
QString totp() const;
|
||||||
QSharedPointer<Totp::Settings> totpSettings() const;
|
QSharedPointer<Totp::Settings> totpSettings() const;
|
||||||
|
|
||||||
bool hasTotp() const;
|
bool hasTotp() const;
|
||||||
bool isExpired() const;
|
bool isExpired() const;
|
||||||
|
bool isAttributeReferenceOf(const QString& key, const QUuid& uuid) const;
|
||||||
|
void replaceReferencesWithValues(const Entry* other);
|
||||||
bool hasReferences() const;
|
bool hasReferences() const;
|
||||||
|
bool hasReferencesTo(const QUuid& uuid) const;
|
||||||
EntryAttributes* attributes();
|
EntryAttributes* attributes();
|
||||||
const EntryAttributes* attributes() const;
|
const EntryAttributes* attributes() const;
|
||||||
EntryAttachments* attachments();
|
EntryAttachments* attachments();
|
||||||
|
@ -139,6 +143,7 @@ public:
|
||||||
void setUsername(const QString& username);
|
void setUsername(const QString& username);
|
||||||
void setPassword(const QString& password);
|
void setPassword(const QString& password);
|
||||||
void setNotes(const QString& notes);
|
void setNotes(const QString& notes);
|
||||||
|
void setDefaultAttribute(const QString& attribute, const QString& value);
|
||||||
void setExpires(const bool& value);
|
void setExpires(const bool& value);
|
||||||
void setExpiryTime(const QDateTime& dateTime);
|
void setExpiryTime(const QDateTime& dateTime);
|
||||||
void setTotp(QSharedPointer<Totp::Settings> settings);
|
void setTotp(QSharedPointer<Totp::Settings> settings);
|
||||||
|
@ -237,6 +242,7 @@ private:
|
||||||
QString resolveReferencePlaceholderRecursive(const QString& placeholder, int maxDepth) const;
|
QString resolveReferencePlaceholderRecursive(const QString& placeholder, int maxDepth) const;
|
||||||
QString referenceFieldValue(EntryReferenceType referenceType) const;
|
QString referenceFieldValue(EntryReferenceType referenceType) const;
|
||||||
|
|
||||||
|
static QString buildReference(const QUuid& uuid, const QString& field);
|
||||||
static EntryReferenceType referenceType(const QString& referenceStr);
|
static EntryReferenceType referenceType(const QString& referenceStr);
|
||||||
|
|
||||||
template <class T> bool set(T& property, const T& value);
|
template <class T> bool set(T& property, const T& value);
|
||||||
|
|
|
@ -23,6 +23,9 @@
|
||||||
#include "core/DatabaseIcons.h"
|
#include "core/DatabaseIcons.h"
|
||||||
#include "core/Global.h"
|
#include "core/Global.h"
|
||||||
#include "core/Metadata.h"
|
#include "core/Metadata.h"
|
||||||
|
#include "core/Tools.h"
|
||||||
|
|
||||||
|
#include <QtConcurrent>
|
||||||
|
|
||||||
const int Group::DefaultIconNumber = 48;
|
const int Group::DefaultIconNumber = 48;
|
||||||
const int Group::RecycleBinIconNumber = 43;
|
const int Group::RecycleBinIconNumber = 43;
|
||||||
|
@ -119,7 +122,7 @@ const QUuid& Group::uuid() const
|
||||||
|
|
||||||
const QString Group::uuidToHex() const
|
const QString Group::uuidToHex() const
|
||||||
{
|
{
|
||||||
return QString::fromLatin1(m_uuid.toRfc4122().toHex());
|
return Tools::uuidToHex(m_uuid);
|
||||||
}
|
}
|
||||||
|
|
||||||
QString Group::name() const
|
QString Group::name() const
|
||||||
|
@ -548,6 +551,12 @@ QList<Entry*> Group::entriesRecursive(bool includeHistoryItems) const
|
||||||
return entryList;
|
return entryList;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QList<Entry*> Group::referencesRecursive(const Entry* entry) const
|
||||||
|
{
|
||||||
|
auto entries = entriesRecursive();
|
||||||
|
return QtConcurrent::blockingFiltered(entries, [entry](const Entry* e) { return e->hasReferencesTo(entry->uuid()); });
|
||||||
|
}
|
||||||
|
|
||||||
Entry* Group::findEntryByUuid(const QUuid& uuid) const
|
Entry* Group::findEntryByUuid(const QUuid& uuid) const
|
||||||
{
|
{
|
||||||
if (uuid.isNull()) {
|
if (uuid.isNull()) {
|
||||||
|
|
|
@ -151,6 +151,7 @@ public:
|
||||||
QList<Entry*> entries();
|
QList<Entry*> entries();
|
||||||
const QList<Entry*>& entries() const;
|
const QList<Entry*>& entries() const;
|
||||||
Entry* findEntryRecursive(const QString& text, EntryReferenceType referenceType, Group* group = nullptr);
|
Entry* findEntryRecursive(const QString& text, EntryReferenceType referenceType, Group* group = nullptr);
|
||||||
|
QList<Entry*> referencesRecursive(const Entry* entry) const;
|
||||||
QList<Entry*> entriesRecursive(bool includeHistoryItems = false) const;
|
QList<Entry*> entriesRecursive(bool includeHistoryItems = false) const;
|
||||||
QList<const Group*> groupsRecursive(bool includeSelf) const;
|
QList<const Group*> groupsRecursive(bool includeSelf) const;
|
||||||
QList<Group*> groupsRecursive(bool includeSelf);
|
QList<Group*> groupsRecursive(bool includeSelf);
|
||||||
|
|
|
@ -28,6 +28,7 @@
|
||||||
#include <QLocale>
|
#include <QLocale>
|
||||||
#include <QRegularExpression>
|
#include <QRegularExpression>
|
||||||
#include <QStringList>
|
#include <QStringList>
|
||||||
|
#include <QUuid>
|
||||||
#include <cctype>
|
#include <cctype>
|
||||||
|
|
||||||
#ifdef Q_OS_WIN
|
#ifdef Q_OS_WIN
|
||||||
|
@ -197,31 +198,34 @@ namespace Tools
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Escape common regex symbols except for *, ?, and |
|
// Escape common regex symbols except for *, ?, and |
|
||||||
auto regexEscape = QRegularExpression(R"re(([-[\]{}()+.,\\\/^$#]))re");
|
auto regexEscape = QRegularExpression(R"re(([-[\]{}()+.,\\\/^$#]))re");
|
||||||
|
|
||||||
QRegularExpression convertToRegex(const QString& string, bool useWildcards, bool exactMatch, bool caseSensitive)
|
QRegularExpression convertToRegex(const QString& string, bool useWildcards, bool exactMatch, bool caseSensitive)
|
||||||
{
|
{
|
||||||
QString pattern = string;
|
QString pattern = string;
|
||||||
|
|
||||||
// Wildcard support (*, ?, |)
|
// Wildcard support (*, ?, |)
|
||||||
if (useWildcards) {
|
if (useWildcards) {
|
||||||
pattern.replace(regexEscape, "\\\\1");
|
pattern.replace(regexEscape, "\\\\1");
|
||||||
pattern.replace("*", ".*");
|
pattern.replace("*", ".*");
|
||||||
pattern.replace("?", ".");
|
pattern.replace("?", ".");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exact modifier
|
||||||
|
if (exactMatch) {
|
||||||
|
pattern = "^" + pattern + "$";
|
||||||
|
}
|
||||||
|
|
||||||
|
auto regex = QRegularExpression(pattern);
|
||||||
|
if (!caseSensitive) {
|
||||||
|
regex.setPatternOptions(QRegularExpression::CaseInsensitiveOption);
|
||||||
|
}
|
||||||
|
|
||||||
|
return regex;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Exact modifier
|
QString uuidToHex(const QUuid& uuid) {
|
||||||
if (exactMatch) {
|
return QString::fromLatin1(uuid.toRfc4122().toHex());
|
||||||
pattern = "^" + pattern + "$";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto regex = QRegularExpression(pattern);
|
|
||||||
if (!caseSensitive) {
|
|
||||||
regex.setPatternOptions(QRegularExpression::CaseInsensitiveOption);
|
|
||||||
}
|
|
||||||
|
|
||||||
return regex;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace Tools
|
} // namespace Tools
|
||||||
|
|
|
@ -23,6 +23,7 @@
|
||||||
|
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
|
#include <QUuid>
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
|
@ -39,7 +40,8 @@ namespace Tools
|
||||||
bool isBase64(const QByteArray& ba);
|
bool isBase64(const QByteArray& ba);
|
||||||
void sleep(int ms);
|
void sleep(int ms);
|
||||||
void wait(int ms);
|
void wait(int ms);
|
||||||
QRegularExpression convertToRegex(const QString& string, bool useWildcards = false,
|
QString uuidToHex(const QUuid& uuid);
|
||||||
|
QRegularExpression convertToRegex(const QString& string, bool useWildcards = false,
|
||||||
bool exactMatch = false, bool caseSensitive = false);
|
bool exactMatch = false, bool caseSensitive = false);
|
||||||
|
|
||||||
template <typename RandomAccessIterator, typename T>
|
template <typename RandomAccessIterator, typename T>
|
||||||
|
|
|
@ -426,68 +426,116 @@ void DatabaseWidget::setupTotp()
|
||||||
setupTotpDialog->open();
|
setupTotpDialog->open();
|
||||||
}
|
}
|
||||||
|
|
||||||
void DatabaseWidget::deleteEntries()
|
void DatabaseWidget::deleteSelectedEntries()
|
||||||
{
|
{
|
||||||
const QModelIndexList selected = m_entryView->selectionModel()->selectedRows();
|
const QModelIndexList selected = m_entryView->selectionModel()->selectedRows();
|
||||||
|
|
||||||
Q_ASSERT(!selected.isEmpty());
|
|
||||||
if (selected.isEmpty()) {
|
if (selected.isEmpty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// get all entry pointers as the indexes change when removing multiple entries
|
// Resolve entries from the selection model
|
||||||
QList<Entry*> selectedEntries;
|
QList<Entry*> selectedEntries;
|
||||||
for (const QModelIndex& index : selected) {
|
for (const QModelIndex& index : selected) {
|
||||||
selectedEntries.append(m_entryView->entryFromIndex(index));
|
selectedEntries.append(m_entryView->entryFromIndex(index));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Confirm entry removal before moving forward
|
||||||
auto* recycleBin = m_db->metadata()->recycleBin();
|
auto* recycleBin = m_db->metadata()->recycleBin();
|
||||||
bool inRecycleBin = recycleBin && recycleBin->findEntryByUuid(selectedEntries.first()->uuid());
|
bool permanent = (recycleBin && recycleBin->findEntryByUuid(selectedEntries.first()->uuid()))
|
||||||
if (inRecycleBin || !m_db->metadata()->recycleBinEnabled()) {
|
|| !m_db->metadata()->recycleBinEnabled();
|
||||||
|
|
||||||
|
if (!confirmDeleteEntries(selectedEntries, permanent)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find references to selected entries and prompt for direction if necessary
|
||||||
|
auto it = selectedEntries.begin();
|
||||||
|
while (it != selectedEntries.end()) {
|
||||||
|
auto references = m_db->rootGroup()->referencesRecursive(*it);
|
||||||
|
if (!references.isEmpty()) {
|
||||||
|
// Ignore references that are selected for deletion
|
||||||
|
for (auto* entry : selectedEntries) {
|
||||||
|
references.removeAll(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!references.isEmpty()) {
|
||||||
|
// Prompt for reference handling
|
||||||
|
auto result = MessageBox::question(
|
||||||
|
this,
|
||||||
|
tr("Replace references to entry?"),
|
||||||
|
tr("Entry \"%1\" has %2 reference(s). "
|
||||||
|
"Do you want to overwrite references with values, skip this entry, or delete anyway?", "",
|
||||||
|
references.size())
|
||||||
|
.arg((*it)->title().toHtmlEscaped())
|
||||||
|
.arg(references.size()),
|
||||||
|
MessageBox::Overwrite | MessageBox::Skip | MessageBox::Delete,
|
||||||
|
MessageBox::Overwrite);
|
||||||
|
|
||||||
|
if (result == MessageBox::Overwrite) {
|
||||||
|
for (auto* entry : references) {
|
||||||
|
entry->replaceReferencesWithValues(*it);
|
||||||
|
}
|
||||||
|
} else if (result == MessageBox::Skip) {
|
||||||
|
it = selectedEntries.erase(it);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
it++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (permanent) {
|
||||||
|
for (auto* entry : asConst(selectedEntries)) {
|
||||||
|
delete entry;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (auto* entry : asConst(selectedEntries)) {
|
||||||
|
m_db->recycleEntry(entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
refreshSearch();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DatabaseWidget::confirmDeleteEntries(QList<Entry*> entries, bool permanent)
|
||||||
|
{
|
||||||
|
if (entries.isEmpty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (permanent) {
|
||||||
QString prompt;
|
QString prompt;
|
||||||
refreshSearch();
|
if (entries.size() == 1) {
|
||||||
if (selected.size() == 1) {
|
|
||||||
prompt = tr("Do you really want to delete the entry \"%1\" for good?")
|
prompt = tr("Do you really want to delete the entry \"%1\" for good?")
|
||||||
.arg(selectedEntries.first()->title().toHtmlEscaped());
|
.arg(entries.first()->title().toHtmlEscaped());
|
||||||
} else {
|
} else {
|
||||||
prompt = tr("Do you really want to delete %n entry(s) for good?", "", selected.size());
|
prompt = tr("Do you really want to delete %n entry(s) for good?", "", entries.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
auto answer = MessageBox::question(this,
|
auto answer = MessageBox::question(this,
|
||||||
tr("Delete entry(s)?", "", selected.size()),
|
tr("Delete entry(s)?", "", entries.size()),
|
||||||
prompt,
|
prompt,
|
||||||
MessageBox::Delete | MessageBox::Cancel,
|
MessageBox::Delete | MessageBox::Cancel,
|
||||||
MessageBox::Cancel);
|
MessageBox::Cancel);
|
||||||
|
|
||||||
if (answer == MessageBox::Delete) {
|
return answer == MessageBox::Delete;
|
||||||
for (Entry* entry : asConst(selectedEntries)) {
|
|
||||||
delete entry;
|
|
||||||
refreshSearch();
|
|
||||||
}
|
|
||||||
refreshSearch();
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
QString prompt;
|
QString prompt;
|
||||||
if (selected.size() == 1) {
|
if (entries.size() == 1) {
|
||||||
prompt = tr("Do you really want to move entry \"%1\" to the recycle bin?")
|
prompt = tr("Do you really want to move entry \"%1\" to the recycle bin?")
|
||||||
.arg(selectedEntries.first()->title().toHtmlEscaped());
|
.arg(entries.first()->title().toHtmlEscaped());
|
||||||
} else {
|
} else {
|
||||||
prompt = tr("Do you really want to move %n entry(s) to the recycle bin?", "", selected.size());
|
prompt = tr("Do you really want to move %n entry(s) to the recycle bin?", "", entries.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
auto answer = MessageBox::question(this,
|
auto answer = MessageBox::question(this,
|
||||||
tr("Move entry(s) to recycle bin?", "", selected.size()),
|
tr("Move entry(s) to recycle bin?", "", entries.size()),
|
||||||
prompt,
|
prompt,
|
||||||
MessageBox::Move | MessageBox::Cancel,
|
MessageBox::Move | MessageBox::Cancel,
|
||||||
MessageBox::Cancel);
|
MessageBox::Cancel);
|
||||||
|
|
||||||
if (answer == MessageBox::Cancel) {
|
return answer == MessageBox::Move;
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (Entry* entry : asConst(selectedEntries)) {
|
|
||||||
m_db->recycleEntry(entry);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -149,7 +149,7 @@ public slots:
|
||||||
void replaceDatabase(QSharedPointer<Database> db);
|
void replaceDatabase(QSharedPointer<Database> db);
|
||||||
void createEntry();
|
void createEntry();
|
||||||
void cloneEntry();
|
void cloneEntry();
|
||||||
void deleteEntries();
|
void deleteSelectedEntries();
|
||||||
void setFocus();
|
void setFocus();
|
||||||
void copyTitle();
|
void copyTitle();
|
||||||
void copyUsername();
|
void copyUsername();
|
||||||
|
@ -225,6 +225,7 @@ private:
|
||||||
void setClipboardTextAndMinimize(const QString& text);
|
void setClipboardTextAndMinimize(const QString& text);
|
||||||
void setIconFromParent();
|
void setIconFromParent();
|
||||||
void processAutoOpen();
|
void processAutoOpen();
|
||||||
|
bool confirmDeleteEntries(QList<Entry*> entries, bool permanent);
|
||||||
|
|
||||||
QSharedPointer<Database> m_db;
|
QSharedPointer<Database> m_db;
|
||||||
|
|
||||||
|
|
|
@ -310,7 +310,7 @@ MainWindow::MainWindow()
|
||||||
m_actionMultiplexer.connect(m_ui->actionEntryNew, SIGNAL(triggered()), SLOT(createEntry()));
|
m_actionMultiplexer.connect(m_ui->actionEntryNew, SIGNAL(triggered()), SLOT(createEntry()));
|
||||||
m_actionMultiplexer.connect(m_ui->actionEntryClone, SIGNAL(triggered()), SLOT(cloneEntry()));
|
m_actionMultiplexer.connect(m_ui->actionEntryClone, SIGNAL(triggered()), SLOT(cloneEntry()));
|
||||||
m_actionMultiplexer.connect(m_ui->actionEntryEdit, SIGNAL(triggered()), SLOT(switchToEntryEdit()));
|
m_actionMultiplexer.connect(m_ui->actionEntryEdit, SIGNAL(triggered()), SLOT(switchToEntryEdit()));
|
||||||
m_actionMultiplexer.connect(m_ui->actionEntryDelete, SIGNAL(triggered()), SLOT(deleteEntries()));
|
m_actionMultiplexer.connect(m_ui->actionEntryDelete, SIGNAL(triggered()), SLOT(deleteSelectedEntries()));
|
||||||
|
|
||||||
m_actionMultiplexer.connect(m_ui->actionEntryTotp, SIGNAL(triggered()), SLOT(showTotp()));
|
m_actionMultiplexer.connect(m_ui->actionEntryTotp, SIGNAL(triggered()), SLOT(showTotp()));
|
||||||
m_actionMultiplexer.connect(m_ui->actionEntrySetupTotp, SIGNAL(triggered()), SLOT(setupTotp()));
|
m_actionMultiplexer.connect(m_ui->actionEntrySetupTotp, SIGNAL(triggered()), SLOT(setupTotp()));
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue