mirror of
https://github.com/keepassxreboot/keepassxc.git
synced 2024-10-01 01:26:01 -04:00
Merge pull request #520 from vsvyatski/develop
Add context menu entry to clean the Recycle Bin in databases
This commit is contained in:
commit
ad79162061
BIN
share/icons/application/16x16/actions/group-empty-trash.png
Normal file
BIN
share/icons/application/16x16/actions/group-empty-trash.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.1 KiB |
@ -308,6 +308,22 @@ void Database::recycleGroup(Group* group)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Database::emptyRecycleBin()
|
||||||
|
{
|
||||||
|
if (m_metadata->recycleBinEnabled() && m_metadata->recycleBin()) {
|
||||||
|
// destroying direct entries of the recycle bin
|
||||||
|
QList<Entry*> subEntries = m_metadata->recycleBin()->entries();
|
||||||
|
for (Entry* entry : subEntries) {
|
||||||
|
delete entry;
|
||||||
|
}
|
||||||
|
// destroying direct subgroups of the recycle bin
|
||||||
|
QList<Group*> subGroups = m_metadata->recycleBin()->children();
|
||||||
|
for (Group* group : subGroups) {
|
||||||
|
delete group;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Database::merge(const Database* other)
|
void Database::merge(const Database* other)
|
||||||
{
|
{
|
||||||
m_rootGroup->merge(other->rootGroup());
|
m_rootGroup->merge(other->rootGroup());
|
||||||
|
@ -108,6 +108,7 @@ public:
|
|||||||
bool verifyKey(const CompositeKey& key) const;
|
bool verifyKey(const CompositeKey& key) const;
|
||||||
void recycleEntry(Entry* entry);
|
void recycleEntry(Entry* entry);
|
||||||
void recycleGroup(Group* group);
|
void recycleGroup(Group* group);
|
||||||
|
void emptyRecycleBin();
|
||||||
void setEmitModified(bool value);
|
void setEmitModified(bool value);
|
||||||
void copyAttributesFrom(const Database* other);
|
void copyAttributesFrom(const Database* other);
|
||||||
void merge(const Database* other);
|
void merge(const Database* other);
|
||||||
|
@ -1267,3 +1267,25 @@ void DatabaseWidget::hideMessage()
|
|||||||
m_messageWidget->animatedHide();
|
m_messageWidget->animatedHide();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool DatabaseWidget::isRecycleBinSelected() const
|
||||||
|
{
|
||||||
|
return m_groupView->currentGroup() && m_groupView->currentGroup() == m_db->metadata()->recycleBin();
|
||||||
|
}
|
||||||
|
|
||||||
|
void DatabaseWidget::emptyRecycleBin()
|
||||||
|
{
|
||||||
|
if(!isRecycleBinSelected()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QMessageBox::StandardButton result = MessageBox::question(
|
||||||
|
this, tr("Empty recycle bin?"),
|
||||||
|
tr("Are you sure you want to permanently delete everything from your recycle bin?"),
|
||||||
|
QMessageBox::Yes | QMessageBox::No);
|
||||||
|
|
||||||
|
if (result == QMessageBox::Yes) {
|
||||||
|
m_db->emptyRecycleBin();
|
||||||
|
refreshSearch();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -102,6 +102,7 @@ public:
|
|||||||
void closeUnlockDialog();
|
void closeUnlockDialog();
|
||||||
void blockAutoReload(bool block = true);
|
void blockAutoReload(bool block = true);
|
||||||
void refreshSearch();
|
void refreshSearch();
|
||||||
|
bool isRecycleBinSelected() const;
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void closeRequest();
|
void closeRequest();
|
||||||
@ -152,6 +153,7 @@ public slots:
|
|||||||
void switchToImportKeepass1(const QString& fileName);
|
void switchToImportKeepass1(const QString& fileName);
|
||||||
void databaseModified();
|
void databaseModified();
|
||||||
void databaseSaved();
|
void databaseSaved();
|
||||||
|
void emptyRecycleBin();
|
||||||
|
|
||||||
// Search related slots
|
// Search related slots
|
||||||
void search(const QString& searchtext);
|
void search(const QString& searchtext);
|
||||||
|
@ -197,6 +197,7 @@ MainWindow::MainWindow()
|
|||||||
m_ui->actionGroupNew->setIcon(filePath()->icon("actions", "group-new", false));
|
m_ui->actionGroupNew->setIcon(filePath()->icon("actions", "group-new", false));
|
||||||
m_ui->actionGroupEdit->setIcon(filePath()->icon("actions", "group-edit", false));
|
m_ui->actionGroupEdit->setIcon(filePath()->icon("actions", "group-edit", false));
|
||||||
m_ui->actionGroupDelete->setIcon(filePath()->icon("actions", "group-delete", false));
|
m_ui->actionGroupDelete->setIcon(filePath()->icon("actions", "group-delete", false));
|
||||||
|
m_ui->actionGroupEmptyRecycleBin->setIcon(filePath()->icon("actions", "group-empty-trash", false));
|
||||||
|
|
||||||
m_ui->actionSettings->setIcon(filePath()->icon("actions", "configure"));
|
m_ui->actionSettings->setIcon(filePath()->icon("actions", "configure"));
|
||||||
m_ui->actionSettings->setMenuRole(QAction::PreferencesRole);
|
m_ui->actionSettings->setMenuRole(QAction::PreferencesRole);
|
||||||
@ -295,6 +296,8 @@ MainWindow::MainWindow()
|
|||||||
SLOT(switchToGroupEdit()));
|
SLOT(switchToGroupEdit()));
|
||||||
m_actionMultiplexer.connect(m_ui->actionGroupDelete, SIGNAL(triggered()),
|
m_actionMultiplexer.connect(m_ui->actionGroupDelete, SIGNAL(triggered()),
|
||||||
SLOT(deleteGroup()));
|
SLOT(deleteGroup()));
|
||||||
|
m_actionMultiplexer.connect(m_ui->actionGroupEmptyRecycleBin, SIGNAL(triggered()),
|
||||||
|
SLOT(emptyRecycleBin()));
|
||||||
|
|
||||||
connect(m_ui->actionSettings, SIGNAL(triggered()), SLOT(switchToSettings()));
|
connect(m_ui->actionSettings, SIGNAL(triggered()), SLOT(switchToSettings()));
|
||||||
connect(m_ui->actionPasswordGenerator, SIGNAL(toggled(bool)), SLOT(switchToPasswordGen(bool)));
|
connect(m_ui->actionPasswordGenerator, SIGNAL(toggled(bool)), SLOT(switchToPasswordGen(bool)));
|
||||||
@ -413,6 +416,7 @@ void MainWindow::setMenuActionState(DatabaseWidget::Mode mode)
|
|||||||
bool singleEntrySelected = dbWidget->numberOfSelectedEntries() == 1;
|
bool singleEntrySelected = dbWidget->numberOfSelectedEntries() == 1;
|
||||||
bool entriesSelected = dbWidget->numberOfSelectedEntries() > 0;
|
bool entriesSelected = dbWidget->numberOfSelectedEntries() > 0;
|
||||||
bool groupSelected = dbWidget->isGroupSelected();
|
bool groupSelected = dbWidget->isGroupSelected();
|
||||||
|
bool recycleBinSelected = dbWidget->isRecycleBinSelected();
|
||||||
|
|
||||||
m_ui->actionEntryNew->setEnabled(!inSearch);
|
m_ui->actionEntryNew->setEnabled(!inSearch);
|
||||||
m_ui->actionEntryClone->setEnabled(singleEntrySelected);
|
m_ui->actionEntryClone->setEnabled(singleEntrySelected);
|
||||||
@ -429,6 +433,8 @@ void MainWindow::setMenuActionState(DatabaseWidget::Mode mode)
|
|||||||
m_ui->actionGroupNew->setEnabled(groupSelected);
|
m_ui->actionGroupNew->setEnabled(groupSelected);
|
||||||
m_ui->actionGroupEdit->setEnabled(groupSelected);
|
m_ui->actionGroupEdit->setEnabled(groupSelected);
|
||||||
m_ui->actionGroupDelete->setEnabled(groupSelected && dbWidget->canDeleteCurrentGroup());
|
m_ui->actionGroupDelete->setEnabled(groupSelected && dbWidget->canDeleteCurrentGroup());
|
||||||
|
m_ui->actionGroupEmptyRecycleBin->setVisible(recycleBinSelected);
|
||||||
|
m_ui->actionGroupEmptyRecycleBin->setEnabled(recycleBinSelected);
|
||||||
m_ui->actionChangeMasterKey->setEnabled(true);
|
m_ui->actionChangeMasterKey->setEnabled(true);
|
||||||
m_ui->actionChangeDatabaseSettings->setEnabled(true);
|
m_ui->actionChangeDatabaseSettings->setEnabled(true);
|
||||||
m_ui->actionDatabaseSave->setEnabled(true);
|
m_ui->actionDatabaseSave->setEnabled(true);
|
||||||
|
@ -162,7 +162,7 @@
|
|||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>800</width>
|
<width>800</width>
|
||||||
<height>29</height>
|
<height>21</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<widget class="QMenu" name="menuFile">
|
<widget class="QMenu" name="menuFile">
|
||||||
@ -235,8 +235,10 @@
|
|||||||
<string>&Groups</string>
|
<string>&Groups</string>
|
||||||
</property>
|
</property>
|
||||||
<addaction name="actionGroupNew"/>
|
<addaction name="actionGroupNew"/>
|
||||||
|
<addaction name="separator"/>
|
||||||
<addaction name="actionGroupEdit"/>
|
<addaction name="actionGroupEdit"/>
|
||||||
<addaction name="actionGroupDelete"/>
|
<addaction name="actionGroupDelete"/>
|
||||||
|
<addaction name="actionGroupEmptyRecycleBin"/>
|
||||||
</widget>
|
</widget>
|
||||||
<widget class="QMenu" name="menuTools">
|
<widget class="QMenu" name="menuTools">
|
||||||
<property name="title">
|
<property name="title">
|
||||||
@ -521,6 +523,14 @@
|
|||||||
<string>Re&pair database</string>
|
<string>Re&pair database</string>
|
||||||
</property>
|
</property>
|
||||||
</action>
|
</action>
|
||||||
|
<action name="actionGroupEmptyRecycleBin">
|
||||||
|
<property name="text">
|
||||||
|
<string>Empty recycle bin</string>
|
||||||
|
</property>
|
||||||
|
<property name="visible">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
</widget>
|
</widget>
|
||||||
<customwidgets>
|
<customwidgets>
|
||||||
<customwidget>
|
<customwidget>
|
||||||
|
@ -177,6 +177,9 @@ add_unit_test(NAME testykchallengeresponsekey
|
|||||||
SOURCES TestYkChallengeResponseKey.cpp TestYkChallengeResponseKey.h
|
SOURCES TestYkChallengeResponseKey.cpp TestYkChallengeResponseKey.h
|
||||||
LIBS ${TEST_LIBRARIES})
|
LIBS ${TEST_LIBRARIES})
|
||||||
|
|
||||||
|
add_unit_test(NAME testdatabase SOURCES TestDatabase.cpp
|
||||||
|
LIBS ${TEST_LIBRARIES})
|
||||||
|
|
||||||
if(WITH_GUI_TESTS)
|
if(WITH_GUI_TESTS)
|
||||||
add_subdirectory(gui)
|
add_subdirectory(gui)
|
||||||
endif(WITH_GUI_TESTS)
|
endif(WITH_GUI_TESTS)
|
||||||
|
112
tests/TestDatabase.cpp
Normal file
112
tests/TestDatabase.cpp
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2017 Vladimir Svyatski <v.unreal@gmail.com>
|
||||||
|
*
|
||||||
|
* 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 or (at your option)
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "TestDatabase.h"
|
||||||
|
|
||||||
|
#include <QTest>
|
||||||
|
#include <QSignalSpy>
|
||||||
|
#include <QTemporaryFile>
|
||||||
|
|
||||||
|
#include "config-keepassx-tests.h"
|
||||||
|
#include "core/Database.h"
|
||||||
|
#include "crypto/Crypto.h"
|
||||||
|
#include "keys/PasswordKey.h"
|
||||||
|
#include "core/Metadata.h"
|
||||||
|
#include "core/Group.h"
|
||||||
|
#include "format/KeePass2Writer.h"
|
||||||
|
|
||||||
|
QTEST_GUILESS_MAIN(TestDatabase)
|
||||||
|
|
||||||
|
void TestDatabase::initTestCase()
|
||||||
|
{
|
||||||
|
QVERIFY(Crypto::init());
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestDatabase::testEmptyRecycleBinOnDisabled()
|
||||||
|
{
|
||||||
|
QString filename = QString(KEEPASSX_TEST_DATA_DIR).append("/RecycleBinDisabled.kdbx");
|
||||||
|
CompositeKey key;
|
||||||
|
key.addKey(PasswordKey("123"));
|
||||||
|
Database* db = Database::openDatabaseFile(filename, key);
|
||||||
|
QVERIFY(db);
|
||||||
|
|
||||||
|
QSignalSpy spyModified(db, SIGNAL(modifiedImmediate()));
|
||||||
|
|
||||||
|
db->emptyRecycleBin();
|
||||||
|
//The database must be unmodified in this test after emptying the recycle bin.
|
||||||
|
QCOMPARE(spyModified.count(), 0);
|
||||||
|
|
||||||
|
delete db;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestDatabase::testEmptyRecycleBinOnNotCreated()
|
||||||
|
{
|
||||||
|
QString filename = QString(KEEPASSX_TEST_DATA_DIR).append("/RecycleBinNotYetCreated.kdbx");
|
||||||
|
CompositeKey key;
|
||||||
|
key.addKey(PasswordKey("123"));
|
||||||
|
Database* db = Database::openDatabaseFile(filename, key);
|
||||||
|
QVERIFY(db);
|
||||||
|
|
||||||
|
QSignalSpy spyModified(db, SIGNAL(modifiedImmediate()));
|
||||||
|
|
||||||
|
db->emptyRecycleBin();
|
||||||
|
//The database must be unmodified in this test after emptying the recycle bin.
|
||||||
|
QCOMPARE(spyModified.count(), 0);
|
||||||
|
|
||||||
|
delete db;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestDatabase::testEmptyRecycleBinOnEmpty()
|
||||||
|
{
|
||||||
|
QString filename = QString(KEEPASSX_TEST_DATA_DIR).append("/RecycleBinEmpty.kdbx");
|
||||||
|
CompositeKey key;
|
||||||
|
key.addKey(PasswordKey("123"));
|
||||||
|
Database* db = Database::openDatabaseFile(filename, key);
|
||||||
|
QVERIFY(db);
|
||||||
|
|
||||||
|
QSignalSpy spyModified(db, SIGNAL(modifiedImmediate()));
|
||||||
|
|
||||||
|
db->emptyRecycleBin();
|
||||||
|
//The database must be unmodified in this test after emptying the recycle bin.
|
||||||
|
QCOMPARE(spyModified.count(), 0);
|
||||||
|
|
||||||
|
delete db;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestDatabase::testEmptyRecycleBinWithHierarchicalData()
|
||||||
|
{
|
||||||
|
QString filename = QString(KEEPASSX_TEST_DATA_DIR).append("/RecycleBinWithData.kdbx");
|
||||||
|
CompositeKey key;
|
||||||
|
key.addKey(PasswordKey("123"));
|
||||||
|
Database* db = Database::openDatabaseFile(filename, key);
|
||||||
|
QVERIFY(db);
|
||||||
|
|
||||||
|
QFile originalFile(filename);
|
||||||
|
qint64 initialSize = originalFile.size();
|
||||||
|
|
||||||
|
db->emptyRecycleBin();
|
||||||
|
QVERIFY(db->metadata()->recycleBin());
|
||||||
|
QVERIFY(db->metadata()->recycleBin()->entries().empty());
|
||||||
|
QVERIFY(db->metadata()->recycleBin()->children().empty());
|
||||||
|
|
||||||
|
QTemporaryFile afterCleanup;
|
||||||
|
KeePass2Writer writer;
|
||||||
|
writer.writeDatabase(&afterCleanup, db);
|
||||||
|
QVERIFY(afterCleanup.size() < initialSize);
|
||||||
|
|
||||||
|
delete db;
|
||||||
|
}
|
35
tests/TestDatabase.h
Normal file
35
tests/TestDatabase.h
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2017 Vladimir Svyatski <v.unreal@gmail.com>
|
||||||
|
*
|
||||||
|
* 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 or (at your option)
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef KEEPASSX_TESTDATABASE_H
|
||||||
|
#define KEEPASSX_TESTDATABASE_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
|
||||||
|
class TestDatabase : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void initTestCase();
|
||||||
|
void testEmptyRecycleBinOnDisabled();
|
||||||
|
void testEmptyRecycleBinOnNotCreated();
|
||||||
|
void testEmptyRecycleBinOnEmpty();
|
||||||
|
void testEmptyRecycleBinWithHierarchicalData();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // KEEPASSX_TESTDATABASE_H
|
BIN
tests/data/RecycleBinDisabled.kdbx
Normal file
BIN
tests/data/RecycleBinDisabled.kdbx
Normal file
Binary file not shown.
BIN
tests/data/RecycleBinEmpty.kdbx
Normal file
BIN
tests/data/RecycleBinEmpty.kdbx
Normal file
Binary file not shown.
BIN
tests/data/RecycleBinNotYetCreated.kdbx
Normal file
BIN
tests/data/RecycleBinNotYetCreated.kdbx
Normal file
Binary file not shown.
BIN
tests/data/RecycleBinWithData.kdbx
Normal file
BIN
tests/data/RecycleBinWithData.kdbx
Normal file
Binary file not shown.
Loading…
Reference in New Issue
Block a user