Skip to content

Commit

Permalink
Add merge confirmation dialog
Browse files Browse the repository at this point in the history
  • Loading branch information
taminob committed Jan 29, 2024
1 parent aef2624 commit 4b1aac0
Show file tree
Hide file tree
Showing 7 changed files with 234 additions and 8 deletions.
35 changes: 35 additions & 0 deletions share/translations/keepassxc_en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2530,6 +2530,10 @@ Disable safe saves and try again?</source>
<source>Could not find database file: %1</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Merge aborted - database was not modified.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>EditEntryWidget</name>
Expand Down Expand Up @@ -5625,6 +5629,37 @@ We recommend you use the AppImage available on our downloads page.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>MergeDialog</name>
<message>
<source>Database Merge Confirmation</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Merge</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Group</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Title</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>UUID</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Type of change</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Details</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>Merger</name>
<message>
Expand Down
1 change: 1 addition & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ set(keepassx_SOURCES
gui/KeePass1OpenWidget.cpp
gui/KMessageWidget.cpp
gui/MainWindow.cpp
gui/MergeDialog.cpp
gui/MessageBox.cpp
gui/MessageWidget.cpp
gui/OpVaultOpenWidget.cpp
Expand Down
21 changes: 13 additions & 8 deletions src/gui/DatabaseWidget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
#include "gui/GuiTools.h"
#include "gui/KeePass1OpenWidget.h"
#include "gui/MainWindow.h"
#include "gui/MergeDialog.h"
#include "gui/MessageBox.h"
#include "gui/OpVaultOpenWidget.h"
#include "gui/TotpDialog.h"
Expand Down Expand Up @@ -1190,14 +1191,18 @@ void DatabaseWidget::mergeDatabase(bool accepted)
return;
}

Merger merger(srcDb.data(), m_db.data());
auto changeList = merger.merge();

if (!changeList.isEmpty()) {
showMessage(tr("Successfully merged the database files."), MessageWidget::Information);
} else {
showMessage(tr("Database was not modified by merge operation."), MessageWidget::Information);
}
auto* mergeDialog = new MergeDialog(srcDb, m_db, this);
connect(mergeDialog, &MergeDialog::databaseMerged, [this](bool changed) {
if (changed) {
showMessage(tr("Successfully merged the database files."), MessageWidget::Information);
} else {
showMessage(tr("Database was not modified by merge operation."), MessageWidget::Information);
}
});
connect(mergeDialog, &MergeDialog::rejected, [this]() {
showMessage(tr("Merge aborted - database was not modified."), MessageWidget::Information);
});
mergeDialog->open();
}

switchToMainView();
Expand Down
97 changes: 97 additions & 0 deletions src/gui/MergeDialog.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
/*
* Copyright (C) 2017 Weslly Honorato <weslly@protonmail.com>
* Copyright (C) 2017 KeePassXC Team <team@keepassxc.org>
*
* 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 "MergeDialog.h"
#include "ui_MergeDialog.h"

#include "core/Database.h"

#include <QPushButton>
#include <QShortcut>
#include <qdebug.h>

MergeDialog::MergeDialog(QSharedPointer<Database> source, QSharedPointer<Database> target, QWidget* parent)
: QDialog(parent)
, m_merger(source.data(), target.data())
, m_sourceDatabase{std::move(source)}
, m_targetDatabase{std::move(target)}
{
setAttribute(Qt::WA_DeleteOnClose);

m_ui.setupUi(this);

m_ui.buttonBox->button(QDialogButtonBox::Ok)->setText(tr("Merge"));
m_ui.buttonBox->button(QDialogButtonBox::Ok)->setFocus();

connect(m_ui.buttonBox, SIGNAL(rejected()), SLOT(close()));
connect(m_ui.buttonBox, SIGNAL(accepted()), SLOT(performMerge()));

setupChangeTable();

// block input to other windows since other interactions can lead to unexpected merge results
setWindowModality(Qt::WindowModality::ApplicationModal);
}

void MergeDialog::setupChangeTable()
{
auto changeList = m_merger.changes();

auto* table = m_ui.changeTable;
auto columns = QVector<QPair<QString, std::function<QString(const Merger::Change&)>>>{
qMakePair(tr("Group"), [](const auto& change) { return change.group(); }),
qMakePair(tr("Title"), [](const auto& change) { return change.title(); }),
qMakePair(tr("UUID"),
[](const auto& change) {
if (!change.uuid().isNull()) {
return change.uuid().toString();
}
return QString();
}),
qMakePair(tr("Type of change"), [](const auto& change) { return change.typeString(); }),
qMakePair(tr("Details"), [](const auto& change) { return change.details(); })};
table->setColumnCount(columns.size());
table->setRowCount(changeList.size());
for (int column = 0; column < columns.size(); ++column) {
const auto& columnName = columns[column].first;
table->setHorizontalHeaderItem(column, new QTableWidgetItem(columnName));
}
for (int row = 0; row < changeList.size(); ++row) {
const auto& change = changeList[row];
for (int column = 0; column < columns.size(); ++column) {
auto changeMember = columns[column].second;
table->setItem(row, column, new QTableWidgetItem(changeMember(change)));
}
}

table->verticalHeader()->setVisible(false);
table->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeMode::Interactive);
table->horizontalHeader()->resizeSections(QHeaderView::ResizeMode::ResizeToContents);
table->horizontalHeader()->setStretchLastSection(true);

table->setShowGrid(false);
table->setEditTriggers(QAbstractItemView::NoEditTriggers);
table->setSelectionBehavior(QAbstractItemView::SelectRows);
table->setSelectionMode(QAbstractItemView::SingleSelection);
}

void MergeDialog::performMerge()
{
auto changelist = m_merger.merge(false);
emit databaseMerged(!changelist.isEmpty());
done(QDialog::Accepted);
}
53 changes: 53 additions & 0 deletions src/gui/MergeDialog.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/*
* Copyright (C) 2017 Weslly Honorato <weslly@protonmail.com>
* Copyright (C) 2017 KeePassXC Team <team@keepassxc.org>
*
* 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_MERGEDIALOG_H
#define KEEPASSX_MERGEDIALOG_H

#include "core/Merger.h"
#include "ui_MergeDialog.h"

#include <QDialog>

class Database;

class MergeDialog : public QDialog
{
Q_OBJECT

public:
explicit MergeDialog(QSharedPointer<Database> source, QSharedPointer<Database> target, QWidget* parent = nullptr);

Q_SIGNALS:
void databaseMerged(bool databaseChanged);

private Q_SLOTS:
void performMerge();

private:
void setupChangeTable();

private:
Ui::MergeDialog m_ui;

Merger m_merger;
QSharedPointer<Database> m_sourceDatabase;
QSharedPointer<Database> m_targetDatabase;
};

#endif // KEEPASSX_MERGEDIALOG_H
31 changes: 31 additions & 0 deletions src/gui/MergeDialog.ui
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MergeDialog</class>
<widget class="QWidget" name="MergeDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>800</width>
<height>450</height>
</rect>
</property>
<property name="windowTitle">
<string>Database Merge Confirmation</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QTableWidget" name="changeTable"/>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="standardButtons">
<set>QDialogButtonBox::Abort|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>
4 changes: 4 additions & 0 deletions tests/gui/TestGui.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,10 @@ void TestGui::testMergeDatabase()
QTest::keyClicks(editPasswordMerge, "a");
QTest::keyClick(editPasswordMerge, Qt::Key_Enter);

// confirm merge in confirmation dialog
QTRY_VERIFY(QApplication::focusWindow()->title().contains("Merge"));
QTest::keyClick(QApplication::focusWidget(), Qt::Key_Enter);

QTRY_COMPARE(dbMergeSpy.count(), 1);
QTRY_VERIFY(m_tabWidget->tabText(m_tabWidget->currentIndex()).contains("*"));

Expand Down

0 comments on commit 4b1aac0

Please sign in to comment.