diff options
author | Andrew Meyer <apmeyer@bgsu.edu> | 2020-05-09 06:48:03 +0300 |
---|---|---|
committer | Jonathan White <support@dmapps.us> | 2020-05-16 18:00:27 +0300 |
commit | 45848c3f611b88015c8227be197baac07a284b3d (patch) | |
tree | 04f2706254702c443a01cd09308acfbd1473338f /src | |
parent | 2237cf0188af38898e1492f5d7ce2c0f087c8f35 (diff) |
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
Diffstat (limited to 'src')
-rw-r--r-- | src/gui/DatabaseTabWidget.cpp | 14 | ||||
-rw-r--r-- | src/gui/DatabaseTabWidget.h | 1 | ||||
-rw-r--r-- | src/gui/DatabaseWidget.cpp | 47 | ||||
-rw-r--r-- | src/gui/DatabaseWidget.h | 1 | ||||
-rw-r--r-- | src/gui/MainWindow.cpp | 5 | ||||
-rw-r--r-- | src/gui/MainWindow.ui | 12 |
6 files changed, 77 insertions, 3 deletions
diff --git a/src/gui/DatabaseTabWidget.cpp b/src/gui/DatabaseTabWidget.cpp index 8088ae829..f484815af 100644 --- a/src/gui/DatabaseTabWidget.cpp +++ b/src/gui/DatabaseTabWidget.cpp @@ -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()); diff --git a/src/gui/DatabaseTabWidget.h b/src/gui/DatabaseTabWidget.h index b8cbdbb6f..d7c656908 100644 --- a/src/gui/DatabaseTabWidget.h +++ b/src/gui/DatabaseTabWidget.h @@ -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(); diff --git a/src/gui/DatabaseWidget.cpp b/src/gui/DatabaseWidget.cpp index a7531537e..310f141d7 100644 --- a/src/gui/DatabaseWidget.cpp +++ b/src/gui/DatabaseWidget.cpp @@ -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, diff --git a/src/gui/DatabaseWidget.h b/src/gui/DatabaseWidget.h index 0d0afe8f1..3f25a4361 100644 --- a/src/gui/DatabaseWidget.h +++ b/src/gui/DatabaseWidget.h @@ -158,6 +158,7 @@ public slots: bool lock(); bool save(); bool saveAs(); + bool saveBackup(); void replaceDatabase(QSharedPointer<Database> db); void createEntry(); diff --git a/src/gui/MainWindow.cpp b/src/gui/MainWindow.cpp index 0a0118eda..476a675fa 100644 --- a/src/gui/MainWindow.cpp +++ b/src/gui/MainWindow.cpp @@ -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); diff --git a/src/gui/MainWindow.ui b/src/gui/MainWindow.ui index c40191d6b..bc8aa48de 100644 --- a/src/gui/MainWindow.ui +++ b/src/gui/MainWindow.ui @@ -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> |