mirror of
https://github.com/keepassxreboot/keepassxc.git
synced 2024-10-01 01:26:01 -04:00
Implement 'Save Database Backup' option
Add an option in the 'Database' menu to save a backup of the current database. Add unit test for saving database copy * Open a test database, mark it as modified, and save a copy * Fail if the copy is not a valid database * Fail if the original database is saved * Fail if the original database is no longer marked as modified
This commit is contained in:
parent
2237cf0188
commit
45848c3f61
113
COPYING
113
COPYING
@ -124,66 +124,67 @@ Copyright: 2003-2004, David Vignoni <david@icon-king.com>
|
||||
License: LGPL-2.1
|
||||
Comment: from Nuvola icon theme
|
||||
|
||||
Files: share/icons/application/scalable/categories/preferences-other.svg
|
||||
share/icons/application/scalable/apps/keepassxc-dark.svg
|
||||
share/icons/application/scalable/apps/preferences-system-network-sharing.svg
|
||||
share/icons/application/scalable/apps/utilities-terminal.svg
|
||||
share/icons/application/scalable/apps/keepassxc-locked.svg
|
||||
share/icons/application/scalable/apps/keepassxc-unlocked.svg
|
||||
share/icons/application/scalable/apps/keepassxc.svg
|
||||
share/icons/application/scalable/apps/freedesktop.svg
|
||||
share/icons/application/scalable/apps/internet-web-browser.svg
|
||||
share/icons/application/scalable/apps/preferences-desktop-icons.svg
|
||||
share/icons/application/scalable/status/dialog-information.svg
|
||||
share/icons/application/scalable/status/dialog-warning.svg
|
||||
share/icons/application/scalable/status/dialog-error.svg
|
||||
share/icons/application/scalable/status/security-high.svg
|
||||
share/icons/application/scalable/mimetypes/application-x-keepassxc.svg
|
||||
share/icons/application/scalable/actions/document-close.svg
|
||||
share/icons/application/scalable/actions/application-exit.svg
|
||||
share/icons/application/scalable/actions/database-change-key.svg
|
||||
share/icons/application/scalable/actions/group-new.svg
|
||||
share/icons/application/scalable/actions/document-properties.svg
|
||||
share/icons/application/scalable/actions/group-empty-trash.svg
|
||||
share/icons/application/scalable/actions/statistics.svg
|
||||
share/icons/application/scalable/actions/edit-clear-locationbar-ltr.svg
|
||||
share/icons/application/scalable/actions/entry-delete.svg
|
||||
share/icons/application/scalable/actions/entry-clone.svg
|
||||
share/icons/application/scalable/actions/entry-edit.svg
|
||||
share/icons/application/scalable/actions/password-generator.svg
|
||||
share/icons/application/scalable/actions/dialog-ok.svg
|
||||
share/icons/application/scalable/actions/chronometer.svg
|
||||
share/icons/application/scalable/actions/document-new.svg
|
||||
share/icons/application/scalable/actions/view-history.svg
|
||||
share/icons/application/scalable/actions/group-delete.svg
|
||||
share/icons/application/scalable/actions/dialog-close.svg
|
||||
share/icons/application/scalable/actions/group-edit.svg
|
||||
share/icons/application/scalable/actions/document-save.svg
|
||||
share/icons/application/scalable/actions/password-show-on.svg
|
||||
share/icons/application/scalable/actions/message-close.svg
|
||||
share/icons/application/scalable/actions/entry-new.svg
|
||||
share/icons/application/scalable/actions/url-copy.svg
|
||||
share/icons/application/scalable/actions/username-copy.svg
|
||||
Files: share/icons/application/scalable/actions/application-exit.svg
|
||||
share/icons/application/scalable/actions/auto-type.svg
|
||||
share/icons/application/scalable/actions/password-show-off.svg
|
||||
share/icons/application/scalable/actions/paperclip.svg
|
||||
share/icons/application/scalable/actions/configure.svg
|
||||
share/icons/application/scalable/actions/database-lock.svg
|
||||
share/icons/application/scalable/actions/password-copy.svg
|
||||
share/icons/application/scalable/actions/system-help.svg
|
||||
share/icons/application/scalable/actions/help-about.svg
|
||||
share/icons/application/scalable/actions/system-search.svg
|
||||
share/icons/application/scalable/actions/key-enter.svg
|
||||
share/icons/application/scalable/actions/document-edit.svg
|
||||
share/icons/application/scalable/actions/edit-clear-locationbar-rtl.svg
|
||||
share/icons/application/scalable/actions/password-generate.svg
|
||||
share/icons/application/scalable/actions/favicon-download.svg
|
||||
share/icons/application/scalable/actions/document-open.svg
|
||||
share/icons/application/scalable/actions/document-save-as.svg
|
||||
share/icons/application/scalable/actions/refresh.svg
|
||||
share/icons/application/scalable/actions/chronometer.svg
|
||||
share/icons/application/scalable/actions/clipboard-text.svg
|
||||
share/icons/application/scalable/actions/configure.svg
|
||||
share/icons/application/scalable/actions/database-change-key.svg
|
||||
share/icons/application/scalable/actions/database-lock.svg
|
||||
share/icons/application/scalable/actions/dialog-close.svg
|
||||
share/icons/application/scalable/actions/dialog-ok.svg
|
||||
share/icons/application/scalable/actions/document-close.svg
|
||||
share/icons/application/scalable/actions/document-edit.svg
|
||||
share/icons/application/scalable/actions/document-new.svg
|
||||
share/icons/application/scalable/actions/document-open.svg
|
||||
share/icons/application/scalable/actions/document-properties.svg
|
||||
share/icons/application/scalable/actions/document-save.svg
|
||||
share/icons/application/scalable/actions/document-save-as.svg
|
||||
share/icons/application/scalable/actions/document-save-copy.svg
|
||||
share/icons/application/scalable/actions/edit-clear-locationbar-ltr.svg
|
||||
share/icons/application/scalable/actions/edit-clear-locationbar-rtl.svg
|
||||
share/icons/application/scalable/actions/entry-clone.svg
|
||||
share/icons/application/scalable/actions/entry-delete.svg
|
||||
share/icons/application/scalable/actions/entry-edit.svg
|
||||
share/icons/application/scalable/actions/entry-new.svg
|
||||
share/icons/application/scalable/actions/favicon-download.svg
|
||||
share/icons/application/scalable/actions/group-delete.svg
|
||||
share/icons/application/scalable/actions/group-edit.svg
|
||||
share/icons/application/scalable/actions/group-empty-trash.svg
|
||||
share/icons/application/scalable/actions/group-new.svg
|
||||
share/icons/application/scalable/actions/help-about.svg
|
||||
share/icons/application/scalable/actions/key-enter.svg
|
||||
share/icons/application/scalable/actions/message-close.svg
|
||||
share/icons/application/scalable/actions/paperclip.svg
|
||||
share/icons/application/scalable/actions/password-copy.svg
|
||||
share/icons/application/scalable/actions/password-generate.svg
|
||||
share/icons/application/scalable/actions/password-generator.svg
|
||||
share/icons/application/scalable/actions/password-show-off.svg
|
||||
share/icons/application/scalable/actions/password-show-on.svg
|
||||
share/icons/application/scalable/actions/refresh.svg
|
||||
share/icons/application/scalable/actions/reports.svg
|
||||
share/icons/application/scalable/actions/reports-exclude.svg
|
||||
share/icons/application/scalable/actions/statistics.svg
|
||||
share/icons/application/scalable/actions/system-help.svg
|
||||
share/icons/application/scalable/actions/system-search.svg
|
||||
share/icons/application/scalable/actions/url-copy.svg
|
||||
share/icons/application/scalable/actions/username-copy.svg
|
||||
share/icons/application/scalable/actions/view-history.svg
|
||||
share/icons/application/scalable/apps/freedesktop.svg
|
||||
share/icons/application/scalable/apps/internet-web-browser.svg
|
||||
share/icons/application/scalable/apps/keepassxc.svg
|
||||
share/icons/application/scalable/apps/keepassxc-dark.svg
|
||||
share/icons/application/scalable/apps/keepassxc-locked.svg
|
||||
share/icons/application/scalable/apps/keepassxc-unlocked.svg
|
||||
share/icons/application/scalable/apps/preferences-desktop-icons.svg
|
||||
share/icons/application/scalable/apps/preferences-system-network-sharing.svg
|
||||
share/icons/application/scalable/apps/utilities-terminal.svg
|
||||
share/icons/application/scalable/categories/preferences-other.svg
|
||||
share/icons/application/scalable/mimetypes/application-x-keepassxc.svg
|
||||
share/icons/application/scalable/status/dialog-error.svg
|
||||
share/icons/application/scalable/status/dialog-information.svg
|
||||
share/icons/application/scalable/status/dialog-warning.svg
|
||||
share/icons/application/scalable/status/security-high.svg
|
||||
Copyright: 2019 Austin Andrews <http://templarian.com/>
|
||||
License: SIL OPEN FONT LICENSE Version 1.1
|
||||
Comment: Taken from Material Design icon set (https://github.com/templarian/MaterialDesign/)
|
||||
|
@ -0,0 +1 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" id="mdi-content-save-move-outline" width="24" height="24" viewBox="0 0 24 24"><path d="M13 17H17V14L22 18.5L17 23V20H13V17M14 12.8C13.5 12.31 12.78 12 12 12C10.34 12 9 13.34 9 15C9 16.31 9.84 17.41 11 17.82C11.07 15.67 12.27 13.8 14 12.8M11.09 19H5V5H16.17L19 7.83V12.35C19.75 12.61 20.42 13 21 13.54V7L17 3H5C3.89 3 3 3.9 3 5V19C3 20.1 3.89 21 5 21H11.81C11.46 20.39 11.21 19.72 11.09 19M6 10H15V6H6V10Z" /></svg>
|
After Width: | Height: | Size: 647 B |
@ -23,6 +23,7 @@
|
||||
<file>application/scalable/actions/document-properties.svg</file>
|
||||
<file>application/scalable/actions/document-save.svg</file>
|
||||
<file>application/scalable/actions/document-save-as.svg</file>
|
||||
<file>application/scalable/actions/document-save-copy.svg</file>
|
||||
<file>application/scalable/actions/donate.svg</file>
|
||||
<file>application/scalable/actions/edit-clear-locationbar-ltr.svg</file>
|
||||
<file>application/scalable/actions/edit-clear-locationbar-rtl.svg</file>
|
||||
|
@ -393,6 +393,20 @@ bool DatabaseTabWidget::saveDatabaseAs(int index)
|
||||
return ok;
|
||||
}
|
||||
|
||||
bool DatabaseTabWidget::saveDatabaseBackup(int index)
|
||||
{
|
||||
if (index == -1) {
|
||||
index = currentIndex();
|
||||
}
|
||||
|
||||
auto* dbWidget = databaseWidgetFromIndex(index);
|
||||
bool ok = dbWidget->saveBackup();
|
||||
if (ok) {
|
||||
updateLastDatabases(dbWidget->database()->filePath());
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
void DatabaseTabWidget::closeDatabaseFromSender()
|
||||
{
|
||||
auto* dbWidget = qobject_cast<DatabaseWidget*>(sender());
|
||||
|
@ -68,6 +68,7 @@ public slots:
|
||||
void importOpVaultDatabase();
|
||||
bool saveDatabase(int index = -1);
|
||||
bool saveDatabaseAs(int index = -1);
|
||||
bool saveDatabaseBackup(int index = -1);
|
||||
void exportToCsv();
|
||||
void exportToHtml();
|
||||
|
||||
|
@ -1866,6 +1866,53 @@ bool DatabaseWidget::saveAs()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Save copy of database under a new user-selected filename.
|
||||
*
|
||||
* @return true on success
|
||||
*/
|
||||
bool DatabaseWidget::saveBackup()
|
||||
{
|
||||
while (true) {
|
||||
QString oldFilePath = m_db->filePath();
|
||||
if (!QFileInfo::exists(oldFilePath)) {
|
||||
oldFilePath = QDir::toNativeSeparators(config()->get(Config::LastDir).toString() + "/"
|
||||
+ tr("Passwords").append(".kdbx"));
|
||||
}
|
||||
const QString newFilePath = fileDialog()->getSaveFileName(this,
|
||||
tr("Save database backup"),
|
||||
oldFilePath,
|
||||
tr("KeePass 2 Database").append(" (*.kdbx)"),
|
||||
nullptr,
|
||||
nullptr);
|
||||
|
||||
if (!newFilePath.isEmpty()) {
|
||||
// Ensure we don't recurse back into this function
|
||||
m_db->setReadOnly(false);
|
||||
m_db->setFilePath(newFilePath);
|
||||
m_saveAttempts = 0;
|
||||
|
||||
bool modified = m_db->isModified();
|
||||
|
||||
if (!save()) {
|
||||
// Failed to save, try again
|
||||
m_db->setFilePath(oldFilePath);
|
||||
continue;
|
||||
}
|
||||
|
||||
m_db->setFilePath(oldFilePath);
|
||||
if (modified) {
|
||||
// Source database is marked as clean when copy is saved, even if source has unsaved changes
|
||||
m_db->markAsModified();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Canceled file selection
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void DatabaseWidget::showMessage(const QString& text,
|
||||
MessageWidget::MessageType type,
|
||||
bool showClosebutton,
|
||||
|
@ -158,6 +158,7 @@ public slots:
|
||||
bool lock();
|
||||
bool save();
|
||||
bool saveAs();
|
||||
bool saveBackup();
|
||||
|
||||
void replaceDatabase(QSharedPointer<Database> db);
|
||||
void createEntry();
|
||||
|
@ -324,6 +324,7 @@ MainWindow::MainWindow()
|
||||
m_ui->actionDatabaseOpen->setIcon(resources()->icon("document-open"));
|
||||
m_ui->actionDatabaseSave->setIcon(resources()->icon("document-save"));
|
||||
m_ui->actionDatabaseSaveAs->setIcon(resources()->icon("document-save-as"));
|
||||
m_ui->actionDatabaseSaveBackup->setIcon(resources()->icon("document-save-copy"));
|
||||
m_ui->actionDatabaseClose->setIcon(resources()->icon("document-close"));
|
||||
m_ui->actionReports->setIcon(resources()->icon("reports"));
|
||||
m_ui->actionChangeDatabaseSettings->setIcon(resources()->icon("document-edit"));
|
||||
@ -396,6 +397,7 @@ MainWindow::MainWindow()
|
||||
connect(m_ui->actionDatabaseOpen, SIGNAL(triggered()), m_ui->tabWidget, SLOT(openDatabase()));
|
||||
connect(m_ui->actionDatabaseSave, SIGNAL(triggered()), m_ui->tabWidget, SLOT(saveDatabase()));
|
||||
connect(m_ui->actionDatabaseSaveAs, SIGNAL(triggered()), m_ui->tabWidget, SLOT(saveDatabaseAs()));
|
||||
connect(m_ui->actionDatabaseSaveBackup, SIGNAL(triggered()), m_ui->tabWidget, SLOT(saveDatabaseBackup()));
|
||||
connect(m_ui->actionDatabaseClose, SIGNAL(triggered()), m_ui->tabWidget, SLOT(closeCurrentDatabaseTab()));
|
||||
connect(m_ui->actionDatabaseMerge, SIGNAL(triggered()), m_ui->tabWidget, SLOT(mergeDatabase()));
|
||||
connect(m_ui->actionChangeMasterKey, SIGNAL(triggered()), m_ui->tabWidget, SLOT(changeMasterKey()));
|
||||
@ -681,6 +683,7 @@ void MainWindow::setMenuActionState(DatabaseWidget::Mode mode)
|
||||
m_ui->actionChangeDatabaseSettings->setEnabled(true);
|
||||
m_ui->actionDatabaseSave->setEnabled(m_ui->tabWidget->canSave());
|
||||
m_ui->actionDatabaseSaveAs->setEnabled(true);
|
||||
m_ui->actionDatabaseSaveBackup->setEnabled(true);
|
||||
m_ui->menuExport->setEnabled(true);
|
||||
m_ui->actionExportCsv->setEnabled(true);
|
||||
m_ui->actionExportHtml->setEnabled(true);
|
||||
@ -736,6 +739,7 @@ void MainWindow::setMenuActionState(DatabaseWidget::Mode mode)
|
||||
m_ui->actionChangeDatabaseSettings->setEnabled(false);
|
||||
m_ui->actionDatabaseSave->setEnabled(false);
|
||||
m_ui->actionDatabaseSaveAs->setEnabled(false);
|
||||
m_ui->actionDatabaseSaveBackup->setEnabled(false);
|
||||
m_ui->menuExport->setEnabled(false);
|
||||
m_ui->actionExportCsv->setEnabled(false);
|
||||
m_ui->actionExportHtml->setEnabled(false);
|
||||
@ -764,6 +768,7 @@ void MainWindow::setMenuActionState(DatabaseWidget::Mode mode)
|
||||
m_ui->actionChangeDatabaseSettings->setEnabled(false);
|
||||
m_ui->actionDatabaseSave->setEnabled(false);
|
||||
m_ui->actionDatabaseSaveAs->setEnabled(false);
|
||||
m_ui->actionDatabaseSaveBackup->setEnabled(false);
|
||||
m_ui->actionDatabaseClose->setEnabled(false);
|
||||
m_ui->menuExport->setEnabled(false);
|
||||
m_ui->actionExportCsv->setEnabled(false);
|
||||
|
@ -260,6 +260,7 @@
|
||||
<addaction name="menuRecentDatabases"/>
|
||||
<addaction name="actionDatabaseSave"/>
|
||||
<addaction name="actionDatabaseSaveAs"/>
|
||||
<addaction name="actionDatabaseSaveBackup"/>
|
||||
<addaction name="actionDatabaseClose"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionChangeMasterKey"/>
|
||||
@ -332,9 +333,6 @@
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionEntryOpenUrl"/>
|
||||
<addaction name="actionEntryDownloadIcon"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionEntryAddToAgent"/>
|
||||
<addaction name="actionEntryRemoveFromAgent"/>
|
||||
</widget>
|
||||
<widget class="QMenu" name="menuGroups">
|
||||
<property name="title">
|
||||
@ -808,6 +806,14 @@
|
||||
<string notr="true">Ctrl+/</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionDatabaseSaveBackup">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Save Database Backup...</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionEntryAddToAgent">
|
||||
<property name="text">
|
||||
<string>Add key to SSH Agent</string>
|
||||
|
@ -1212,6 +1212,35 @@ void TestGui::testSaveAs()
|
||||
tmpFile.remove();
|
||||
}
|
||||
|
||||
void TestGui::testSaveBackup()
|
||||
{
|
||||
m_db->metadata()->setName("testSaveBackup");
|
||||
|
||||
QFileInfo fileInfo(m_dbFilePath);
|
||||
QDateTime lastModified = fileInfo.lastModified();
|
||||
|
||||
// open temporary file so it creates a filename
|
||||
TemporaryFile tmpFile;
|
||||
QVERIFY(tmpFile.open());
|
||||
QString tmpFileName = tmpFile.fileName();
|
||||
tmpFile.remove();
|
||||
|
||||
// wait for modified timer
|
||||
QTRY_COMPARE(m_tabWidget->tabText(m_tabWidget->currentIndex()), QString("testSaveBackup*"));
|
||||
|
||||
fileDialog()->setNextFileName(tmpFileName);
|
||||
|
||||
triggerAction("actionDatabaseSaveBackup");
|
||||
|
||||
QCOMPARE(m_tabWidget->tabName(m_tabWidget->currentIndex()), QString("testSaveBackup*"));
|
||||
|
||||
checkDatabase(tmpFileName);
|
||||
|
||||
fileInfo.refresh();
|
||||
QCOMPARE(fileInfo.lastModified(), lastModified);
|
||||
tmpFile.remove();
|
||||
}
|
||||
|
||||
void TestGui::testSave()
|
||||
{
|
||||
m_db->metadata()->setName("testSave");
|
||||
|
@ -61,6 +61,7 @@ private slots:
|
||||
void testDragAndDropEntry();
|
||||
void testDragAndDropGroup();
|
||||
void testSaveAs();
|
||||
void testSaveBackup();
|
||||
void testSave();
|
||||
void testDatabaseSettings();
|
||||
void testKeePass1Import();
|
||||
|
@ -86,6 +86,7 @@ map() {
|
||||
document-properties) echo file-edit-outline ;;
|
||||
document-save) echo content-save-outline ;;
|
||||
document-save-as) echo content-save-all-outline ;;
|
||||
document-save-copy) echo content-save-move-outline ;;
|
||||
donate) echo gift-outline ;;
|
||||
edit-clear-locationbar-ltr) echo backspace-reverse-outline ;;
|
||||
edit-clear-locationbar-rtl) echo backspace-outline ;;
|
||||
|
Loading…
Reference in New Issue
Block a user