Skip to content

Commit

Permalink
Add ability to expire entries from context menus (#8731)
Browse files Browse the repository at this point in the history
Closes #1972

Add ability to immediately expire an entry from the context menu

---------
Co-authored-by: Jonathan White <[email protected]>
  • Loading branch information
mattesony authored Jan 12, 2025
1 parent 832340e commit 29ac4da
Show file tree
Hide file tree
Showing 16 changed files with 150 additions and 18 deletions.
1 change: 1 addition & 0 deletions COPYING
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,7 @@ Files: share/icons/application/scalable/actions/application-exit.svg
share/icons/application/scalable/actions/entry-delete.svg
share/icons/application/scalable/actions/entry-restore.svg
share/icons/application/scalable/actions/entry-edit.svg
share/icons/application/scalable/actions/entry-expire.svg
share/icons/application/scalable/actions/entry-new.svg
share/icons/application/scalable/actions/favicon-download.svg
share/icons/application/scalable/actions/fingerprint.svg
Expand Down
1 change: 1 addition & 0 deletions share/icons/application/scalable/actions/entry-expire.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions share/icons/icons.qrc
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
<file>application/scalable/actions/edit-clear-locationbar-rtl.svg</file>
<file>application/scalable/actions/entry-clone.svg</file>
<file>application/scalable/actions/entry-delete.svg</file>
<file>application/scalable/actions/entry-expire.svg</file>
<file>application/scalable/actions/entry-restore.svg</file>
<file>application/scalable/actions/entry-edit.svg</file>
<file>application/scalable/actions/entry-new.svg</file>
Expand Down
55 changes: 40 additions & 15 deletions share/translations/keepassxc_en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5950,6 +5950,10 @@ Expect some bugs and minor issues, this version is meant for testing purposes.</
<source>Import Passkey</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Remote S&amp;ync…</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Quit Application</source>
<translation type="unfinished"></translation>
Expand Down Expand Up @@ -6054,6 +6058,10 @@ Expect some bugs and minor issues, this version is meant for testing purposes.</
<source>Show Password Generator</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Remove Passkey From Entry</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Perform Auto-Type: {USERNAME}</source>
<translation type="unfinished"></translation>
Expand Down Expand Up @@ -6199,27 +6207,23 @@ Expect some bugs and minor issues, this version is meant for testing purposes.</
<translation type="unfinished"></translation>
</message>
<message>
<source>Remote S&amp;ync…</source>
<source>Show Group Panel</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Remove Passkey From Entry</source>
<source>Toggle Show Group Panel</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Setup Remote Sync…</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Show Group Panel</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Toggle Show Group Panel</source>
<source>Password Generator</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Password Generator</source>
<source>E&amp;xpire Entry…</source>
<translation type="unfinished"></translation>
</message>
</context>
Expand Down Expand Up @@ -9281,6 +9285,13 @@ This option is deprecated, use --set-key-file instead.</source>
<source>Exclude from reports</source>
<translation type="unfinished"></translation>
</message>
<message numerus="yes">
<source>Expire Entry(s)…</source>
<translation type="unfinished">
<numerusform></numerusform>
<numerusform></numerusform>
</translation>
</message>
<message>
<source>Only show entries that have a URL</source>
<translation type="unfinished"></translation>
Expand All @@ -9307,6 +9318,14 @@ This option is deprecated, use --set-key-file instead.</source>
</context>
<context>
<name>ReportsWidgetHealthcheck</name>
<message>
<source>Show expired entries</source>
<translation type="unfinished"></translation>
</message>
<message>
<source> (Expired)</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Hover over reason to show additional details. Double-click entries to edit.</source>
<translation type="unfinished"></translation>
Expand Down Expand Up @@ -9370,18 +9389,17 @@ This option is deprecated, use --set-key-file instead.</source>
<source>Exclude from reports</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Show expired entries</source>
<translation type="unfinished"></translation>
<message numerus="yes">
<source>Expire Entry(s)…</source>
<translation type="unfinished">
<numerusform></numerusform>
<numerusform></numerusform>
</translation>
</message>
<message>
<source>Show entries that have been excluded from reports</source>
<translation type="unfinished"></translation>
</message>
<message>
<source> (Expired)</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>ReportsWidgetHibp</name>
Expand Down Expand Up @@ -9480,6 +9498,13 @@ This option is deprecated, use --set-key-file instead.</source>
<source>Exclude from reports</source>
<translation type="unfinished"></translation>
</message>
<message numerus="yes">
<source>Expire Entry(s)…</source>
<translation type="unfinished">
<numerusform></numerusform>
<numerusform></numerusform>
</translation>
</message>
</context>
<context>
<name>ReportsWidgetPasskeys</name>
Expand Down
6 changes: 6 additions & 0 deletions src/core/Entry.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -460,6 +460,12 @@ bool Entry::willExpireInDays(int days) const
return m_data.timeInfo.expires() && m_data.timeInfo.expiryTime() < Clock::currentDateTime().addDays(days);
}

void Entry::expireNow()
{
setExpiryTime(Clock::currentDateTimeUtc());
setExpires(true);
}

bool Entry::isRecycled() const
{
const Database* db = database();
Expand Down
1 change: 1 addition & 0 deletions src/core/Entry.h
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ class Entry : public ModifiableObject
bool hasTotp() const;
bool isExpired() const;
bool willExpireInDays(int days) const;
void expireNow();
bool isRecycled() const;
bool isAttributeReference(const QString& key) const;
bool isAttributeReferenceOf(const QString& key, const QUuid& uuid) const;
Expand Down
11 changes: 11 additions & 0 deletions src/gui/DatabaseWidget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -569,6 +569,17 @@ void DatabaseWidget::setupTotp()
setupTotpDialog->open();
}

void DatabaseWidget::expireSelectedEntries()
{
const QModelIndexList selected = m_entryView->selectionModel()->selectedRows();
for (const auto& index : selected) {
auto entry = m_entryView->entryFromIndex(index);
if (entry) {
entry->expireNow();
}
}
}

void DatabaseWidget::deleteSelectedEntries()
{
const QModelIndexList selected = m_entryView->selectionModel()->selectedRows();
Expand Down
1 change: 1 addition & 0 deletions src/gui/DatabaseWidget.h
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,7 @@ public slots:
void replaceDatabase(QSharedPointer<Database> db);
void createEntry();
void cloneEntry();
void expireSelectedEntries();
void deleteSelectedEntries();
void restoreSelectedEntries();
void deleteEntries(QList<Entry*> entries, bool confirm = true);
Expand Down
7 changes: 6 additions & 1 deletion src/gui/MainWindow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@ MainWindow::MainWindow()
m_entryContextMenu->addSeparator();
#endif
m_entryContextMenu->addAction(m_ui->actionEntryEdit);
m_entryContextMenu->addAction(m_ui->actionEntryExpire);
m_entryContextMenu->addAction(m_ui->actionEntryClone);
m_entryContextMenu->addAction(m_ui->actionEntryDelete);
m_entryContextMenu->addAction(m_ui->actionEntryNew);
Expand Down Expand Up @@ -276,6 +277,7 @@ MainWindow::MainWindow()
// Unfortunately, Qt::AA_DontShowShortcutsInContextMenus is broken, have to manually enable them
m_ui->actionEntryNew->setShortcutVisibleInContextMenu(true);
m_ui->actionEntryEdit->setShortcutVisibleInContextMenu(true);
m_ui->actionEntryExpire->setShortcutVisibleInContextMenu(true);
m_ui->actionEntryDelete->setShortcutVisibleInContextMenu(true);
m_ui->actionEntryRestore->setShortcutVisibleInContextMenu(true);
m_ui->actionEntryClone->setShortcutVisibleInContextMenu(true);
Expand Down Expand Up @@ -372,6 +374,7 @@ MainWindow::MainWindow()
m_ui->actionEntryNew->setIcon(icons()->icon("entry-new"));
m_ui->actionEntryClone->setIcon(icons()->icon("entry-clone"));
m_ui->actionEntryEdit->setIcon(icons()->icon("entry-edit"));
m_ui->actionEntryExpire->setIcon(icons()->icon("entry-expire"));
m_ui->actionEntryDelete->setIcon(icons()->icon("entry-delete"));
m_ui->actionEntryRestore->setIcon(icons()->icon("entry-restore"));
m_ui->actionEntryAutoType->setIcon(icons()->icon("auto-type"));
Expand Down Expand Up @@ -489,8 +492,9 @@ MainWindow::MainWindow()
connect(m_ui->actionQuit, SIGNAL(triggered()), SLOT(appExit()));

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->actionEntryEdit, SIGNAL(triggered()), SLOT(switchToEntryEdit()));
m_actionMultiplexer.connect(m_ui->actionEntryExpire, SIGNAL(triggered()), SLOT(expireSelectedEntries()));
m_actionMultiplexer.connect(m_ui->actionEntryClone, SIGNAL(triggered()), SLOT(cloneEntry()));
m_actionMultiplexer.connect(m_ui->actionEntryDelete, SIGNAL(triggered()), SLOT(deleteSelectedEntries()));
m_actionMultiplexer.connect(m_ui->actionEntryRestore, SIGNAL(triggered()), SLOT(restoreSelectedEntries()));

Expand Down Expand Up @@ -912,6 +916,7 @@ void MainWindow::updateMenuActionState()
m_ui->actionEntryNew->setEnabled(inDatabase && !inRecycleBin);
m_ui->actionEntryClone->setEnabled(singleEntrySelected && !inRecycleBin);
m_ui->actionEntryEdit->setEnabled(singleEntrySelected);
m_ui->actionEntryExpire->setEnabled(multiEntrySelected);
m_ui->actionEntryDelete->setEnabled(multiEntrySelected);
m_ui->actionEntryRestore->setVisible(multiEntrySelected && inRecycleBin);
m_ui->actionEntryRestore->setEnabled(multiEntrySelected && inRecycleBin);
Expand Down
9 changes: 9 additions & 0 deletions src/gui/MainWindow.ui
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,7 @@
<addaction name="separator"/>
<addaction name="actionEntryNew"/>
<addaction name="actionEntryEdit"/>
<addaction name="actionEntryExpire"/>
<addaction name="actionEntryClone"/>
<addaction name="actionEntryDelete"/>
<addaction name="separator"/>
Expand Down Expand Up @@ -544,6 +545,14 @@
<string notr="true">Ctrl+E</string>
</property>
</action>
<action name="actionEntryExpire">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>E&amp;xpire Entry…</string>
</property>
</action>
<action name="actionEntryDelete">
<property name="text">
<string>&amp;Delete Entry…</string>
Expand Down
27 changes: 27 additions & 0 deletions src/gui/reports/ReportsWidgetBrowserStatistics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,11 @@ void ReportsWidgetBrowserStatistics::customMenuRequested(QPoint pos)
});
}

// 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, &ReportsWidgetBrowserStatistics::expireSelectedEntries);

// Create the "delete entry" menu item
const auto deleteEntry =
new QAction(icons()->icon("entry-delete"), tr("Delete Entry(s)…", "", selected.size()), this);
Expand Down Expand Up @@ -327,6 +332,28 @@ void ReportsWidgetBrowserStatistics::saveSettings()
// Nothing to do - the tab is passive
}

QList<Entry*> ReportsWidgetBrowserStatistics::getSelectedEntries()
{
QList<Entry*> selectedEntries;
for (auto index : m_ui->browserStatisticsTableView->selectionModel()->selectedRows()) {
auto row = m_modelProxy->mapToSource(index).row();
auto entry = m_rowToEntry[row].second;
if (entry) {
selectedEntries << entry;
}
}
return selectedEntries;
}

void ReportsWidgetBrowserStatistics::expireSelectedEntries()
{
for (auto entry : getSelectedEntries()) {
entry->expireNow();
}

calculateBrowserStatistics();
}

void ReportsWidgetBrowserStatistics::deleteSelectedEntries()
{
const auto& selectedEntries = getSelectedEntries();
Expand Down
2 changes: 2 additions & 0 deletions src/gui/reports/ReportsWidgetBrowserStatistics.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ public slots:
void calculateBrowserStatistics();
void emitEntryActivated(const QModelIndex& index);
void customMenuRequested(QPoint);
QList<Entry*> getSelectedEntries();
void expireSelectedEntries();
void deleteSelectedEntries();
void deletePluginDataFromSelectedEntries();

Expand Down
21 changes: 20 additions & 1 deletion src/gui/reports/ReportsWidgetHealthcheck.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,11 @@ void ReportsWidgetHealthcheck::customMenuRequested(QPoint pos)
});
}

// 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, &ReportsWidgetHealthcheck::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);
Expand Down Expand Up @@ -365,7 +370,7 @@ void ReportsWidgetHealthcheck::saveSettings()
// nothing to do - the tab is passive
}

void ReportsWidgetHealthcheck::deleteSelectedEntries()
QList<Entry*> ReportsWidgetHealthcheck::getSelectedEntries()
{
QList<Entry*> selectedEntries;
for (auto index : m_ui->healthcheckTableView->selectionModel()->selectedRows()) {
Expand All @@ -375,7 +380,21 @@ void ReportsWidgetHealthcheck::deleteSelectedEntries()
selectedEntries << entry;
}
}
return selectedEntries;
}

void ReportsWidgetHealthcheck::expireSelectedEntries()
{
for (auto entry : getSelectedEntries()) {
entry->expireNow();
}

calculateHealth();
}

void ReportsWidgetHealthcheck::deleteSelectedEntries()
{
QList<Entry*> selectedEntries = getSelectedEntries();
bool permanent = !m_db->metadata()->recycleBinEnabled();
if (GuiTools::confirmDeleteEntries(this, selectedEntries, permanent)) {
GuiTools::deleteEntriesResolveReferences(this, selectedEntries, permanent);
Expand Down
2 changes: 2 additions & 0 deletions src/gui/reports/ReportsWidgetHealthcheck.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ public slots:
void calculateHealth();
void emitEntryActivated(const QModelIndex& index);
void customMenuRequested(QPoint);
QList<Entry*> getSelectedEntries();
void expireSelectedEntries();
void deleteSelectedEntries();

private:
Expand Down
21 changes: 20 additions & 1 deletion src/gui/reports/ReportsWidgetHibp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -374,6 +374,11 @@ void ReportsWidgetHibp::customMenuRequested(QPoint pos)
});
}

// 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);
Expand Down Expand Up @@ -411,7 +416,7 @@ void ReportsWidgetHibp::customMenuRequested(QPoint pos)
menu->popup(m_ui->hibpTableView->viewport()->mapToGlobal(pos));
}

void ReportsWidgetHibp::deleteSelectedEntries()
QList<Entry*> ReportsWidgetHibp::getSelectedEntries()
{
QList<Entry*> selectedEntries;
for (auto index : m_ui->hibpTableView->selectionModel()->selectedRows()) {
Expand All @@ -421,7 +426,21 @@ void ReportsWidgetHibp::deleteSelectedEntries()
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);
Expand Down
Loading

0 comments on commit 29ac4da

Please sign in to comment.