mirror of
https://github.com/keepassxreboot/keepassxc.git
synced 2025-08-05 21:14:20 -04:00
Support opening remote databases (#10896)
* Use the import wizard to support opening a remote database --------- Co-authored-by: Jonathan White <support@dmapps.us>
This commit is contained in:
parent
abcb1414a3
commit
d2da13da20
14 changed files with 379 additions and 62 deletions
|
@ -35,13 +35,13 @@
|
|||
#include "gui/osutils/macutils/MacUtils.h"
|
||||
#endif
|
||||
#include "gui/wizard/NewDatabaseWizard.h"
|
||||
#include "wizard/ImportWizard.h"
|
||||
|
||||
DatabaseTabWidget::DatabaseTabWidget(QWidget* parent)
|
||||
: QTabWidget(parent)
|
||||
, m_dbWidgetStateSync(new DatabaseWidgetStateSync(this))
|
||||
, m_dbWidgetPendingLock(nullptr)
|
||||
, m_databaseOpenDialog(new DatabaseOpenDialog(this))
|
||||
, m_importWizard(nullptr)
|
||||
, m_databaseOpenInProgress(false)
|
||||
{
|
||||
auto* tabBar = new QTabBar(this);
|
||||
|
@ -255,53 +255,65 @@ void DatabaseTabWidget::addDatabaseTab(DatabaseWidget* dbWidget, bool inBackgrou
|
|||
&DatabaseTabWidget::unlockDatabaseInDialogForSync);
|
||||
}
|
||||
|
||||
DatabaseWidget* DatabaseTabWidget::importFile()
|
||||
void DatabaseTabWidget::importFile()
|
||||
{
|
||||
// Show the import wizard
|
||||
QScopedPointer wizard(new ImportWizard(this));
|
||||
if (!wizard->exec()) {
|
||||
return nullptr;
|
||||
}
|
||||
m_importWizard = new ImportWizard(this);
|
||||
|
||||
auto db = wizard->database();
|
||||
if (!db) {
|
||||
// Import wizard was cancelled
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto importInto = wizard->importInto();
|
||||
if (importInto.first.isNull()) {
|
||||
// Start the new database wizard with the imported database
|
||||
auto newDb = execNewDatabaseWizard();
|
||||
if (newDb) {
|
||||
// Merge the imported db into the new one
|
||||
Merger merger(db.data(), newDb.data());
|
||||
merger.setSkipDatabaseCustomData(true);
|
||||
merger.merge();
|
||||
// Show the new database
|
||||
auto dbWidget = new DatabaseWidget(newDb, this);
|
||||
addDatabaseTab(dbWidget);
|
||||
newDb->markAsModified();
|
||||
return dbWidget;
|
||||
connect(m_importWizard.data(), &QWizard::finished, [&](int result) {
|
||||
if (result != QDialog::Accepted) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
for (int i = 0, c = count(); i < c; ++i) {
|
||||
// Find the database and group to import into based on import wizard choice
|
||||
auto dbWidget = databaseWidgetFromIndex(i);
|
||||
if (!dbWidget->isLocked() && dbWidget->database()->uuid() == importInto.first) {
|
||||
auto group = dbWidget->database()->rootGroup()->findGroupByUuid(importInto.second);
|
||||
if (group) {
|
||||
// Extract the root group from the import database
|
||||
auto importGroup = db->setRootGroup(new Group());
|
||||
importGroup->setParent(group);
|
||||
setCurrentIndex(i);
|
||||
return dbWidget;
|
||||
|
||||
auto db = m_importWizard->database();
|
||||
if (!db) {
|
||||
// Import wizard was cancelled
|
||||
return;
|
||||
}
|
||||
|
||||
switch (m_importWizard->importIntoType()) {
|
||||
case ImportWizard::EXISTING_DATABASE:
|
||||
for (int i = 0, c = count(); i < c; ++i) {
|
||||
auto importInto = m_importWizard->importInto();
|
||||
// Find the database and group to import into based on import wizard choice
|
||||
auto dbWidget = databaseWidgetFromIndex(i);
|
||||
if (!dbWidget->isLocked() && dbWidget->database()->uuid() == importInto.first) {
|
||||
auto group = dbWidget->database()->rootGroup()->findGroupByUuid(importInto.second);
|
||||
if (group) {
|
||||
// Extract the root group from the import database
|
||||
auto importGroup = db->setRootGroup(new Group());
|
||||
importGroup->setParent(group);
|
||||
setCurrentIndex(i);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ImportWizard::TEMPORARY_DATABASE: {
|
||||
// Use the already created database as temporary database
|
||||
auto dbWidget = new DatabaseWidget(db, this);
|
||||
addDatabaseTab(dbWidget);
|
||||
return;
|
||||
}
|
||||
}
|
||||
default:
|
||||
// Start the new database wizard with the imported database
|
||||
auto newDb = execNewDatabaseWizard();
|
||||
if (newDb) {
|
||||
// Merge the imported db into the new one
|
||||
Merger merger(db.data(), newDb.data());
|
||||
merger.setSkipDatabaseCustomData(true);
|
||||
merger.merge();
|
||||
// Show the new database
|
||||
auto dbWidget = new DatabaseWidget(newDb, this);
|
||||
addDatabaseTab(dbWidget);
|
||||
newDb->markAsModified();
|
||||
return;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return nullptr;
|
||||
// use `open` instead of `exec`. `exec` should not be used, see https://doc.qt.io/qt-6/qdialog.html#exec
|
||||
m_importWizard->show();
|
||||
}
|
||||
|
||||
void DatabaseTabWidget::mergeDatabase()
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include "DatabaseOpenDialog.h"
|
||||
#include "config-keepassx.h"
|
||||
#include "gui/MessageWidget.h"
|
||||
#include "wizard/ImportWizard.h"
|
||||
|
||||
#include <QTabWidget>
|
||||
#include <QTimer>
|
||||
|
@ -64,7 +65,7 @@ public slots:
|
|||
DatabaseWidget* newDatabase();
|
||||
void openDatabase();
|
||||
void mergeDatabase();
|
||||
DatabaseWidget* importFile();
|
||||
void importFile();
|
||||
bool saveDatabase(int index = -1);
|
||||
bool saveDatabaseAs(int index = -1);
|
||||
bool saveDatabaseBackup(int index = -1);
|
||||
|
@ -123,6 +124,7 @@ private:
|
|||
QPointer<DatabaseWidgetStateSync> m_dbWidgetStateSync;
|
||||
QPointer<DatabaseWidget> m_dbWidgetPendingLock;
|
||||
QPointer<DatabaseOpenDialog> m_databaseOpenDialog;
|
||||
QPointer<ImportWizard> m_importWizard;
|
||||
QTimer m_lockDelayTimer;
|
||||
bool m_databaseOpenInProgress;
|
||||
};
|
||||
|
|
|
@ -69,6 +69,11 @@ bool ImportWizard::validateCurrentPage()
|
|||
return ret;
|
||||
}
|
||||
|
||||
ImportWizard::ImportIntoType ImportWizard::importIntoType()
|
||||
{
|
||||
return static_cast<ImportIntoType>(field("ImportIntoType").toInt());
|
||||
}
|
||||
|
||||
QPair<QUuid, QUuid> ImportWizard::importInto()
|
||||
{
|
||||
auto list = field("ImportInto").toList();
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#define KEEPASSXC_IMPORTWIZARD_H
|
||||
|
||||
#include <QPointer>
|
||||
#include <QUuid>
|
||||
#include <QWizard>
|
||||
|
||||
class Database;
|
||||
|
@ -39,7 +40,6 @@ public:
|
|||
bool validateCurrentPage() override;
|
||||
|
||||
QSharedPointer<Database> database();
|
||||
QPair<QUuid, QUuid> importInto();
|
||||
|
||||
enum ImportType
|
||||
{
|
||||
|
@ -48,9 +48,20 @@ public:
|
|||
IMPORT_OPVAULT,
|
||||
IMPORT_OPUX,
|
||||
IMPORT_BITWARDEN,
|
||||
IMPORT_KEEPASS1
|
||||
IMPORT_KEEPASS1,
|
||||
IMPORT_REMOTE,
|
||||
};
|
||||
|
||||
enum ImportIntoType
|
||||
{
|
||||
NEW_DATABASE = 1,
|
||||
EXISTING_DATABASE,
|
||||
TEMPORARY_DATABASE,
|
||||
};
|
||||
|
||||
ImportWizard::ImportIntoType importIntoType();
|
||||
QPair<QUuid, QUuid> importInto();
|
||||
|
||||
private:
|
||||
QSharedPointer<Database> m_db;
|
||||
QPointer<ImportWizardPageSelect> m_pageSelect;
|
||||
|
|
|
@ -27,14 +27,23 @@
|
|||
#include "gui/csvImport/CsvImportWidget.h"
|
||||
#include "gui/wizard/ImportWizard.h"
|
||||
|
||||
#include "cli/Utils.h"
|
||||
#include "keys/FileKey.h"
|
||||
#include "keys/PasswordKey.h"
|
||||
|
||||
#include <QBoxLayout>
|
||||
#include <QDir>
|
||||
#include <QHeaderView>
|
||||
#include <QTableWidget>
|
||||
|
||||
#include "gui/remote/RemoteSettings.h"
|
||||
|
||||
struct RemoteParams;
|
||||
|
||||
ImportWizardPageReview::ImportWizardPageReview(QWidget* parent)
|
||||
: QWizardPage(parent)
|
||||
, m_ui(new Ui::ImportWizardPageReview)
|
||||
, m_remoteHandler(new RemoteHandler(this))
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -80,6 +89,12 @@ void ImportWizardPageReview::initializePage()
|
|||
m_db = importBitwarden(filename, field("ImportPassword").toString());
|
||||
setupDatabasePreview();
|
||||
break;
|
||||
case ImportWizard::IMPORT_REMOTE:
|
||||
m_db = importRemote(field("DownloadCommand").toString(),
|
||||
field("DownloadInput").toString(),
|
||||
field("ImportPassword").toString(),
|
||||
field("ImportKeyFile").toString());
|
||||
setupDatabasePreview();
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -200,3 +215,43 @@ ImportWizardPageReview::importKeePass1(const QString& filename, const QString& p
|
|||
|
||||
return db;
|
||||
}
|
||||
|
||||
QSharedPointer<Database> ImportWizardPageReview::importRemote(const QString& downloadCommand,
|
||||
const QString& downloadInput,
|
||||
const QString& password,
|
||||
const QString& keyfile)
|
||||
{
|
||||
auto* params = new RemoteParams();
|
||||
params->downloadCommand = downloadCommand;
|
||||
params->downloadInput = downloadInput;
|
||||
|
||||
auto result = m_remoteHandler->download(params);
|
||||
|
||||
if (!result.success) {
|
||||
m_ui->messageWidget->showMessage(result.errorMessage, KMessageWidget::Error, -1);
|
||||
}
|
||||
|
||||
auto key = QSharedPointer<CompositeKey>::create();
|
||||
|
||||
if (!password.isEmpty()) {
|
||||
key->addKey(QSharedPointer<PasswordKey>::create(password));
|
||||
}
|
||||
if (!keyfile.isEmpty()) {
|
||||
QSharedPointer<FileKey> fileKey = QSharedPointer<FileKey>::create();
|
||||
if (Utils::loadFileKey(keyfile, fileKey)) {
|
||||
key->addKey(fileKey);
|
||||
} else {
|
||||
m_ui->messageWidget->showMessage(tr("Could not load key file."), KMessageWidget::Error, -1);
|
||||
}
|
||||
}
|
||||
|
||||
QString error;
|
||||
QSharedPointer<Database> remoteDb = QSharedPointer<Database>::create();
|
||||
remoteDb->markAsTemporaryDatabase();
|
||||
if (!remoteDb->open(result.filePath, key, &error)) {
|
||||
m_ui->messageWidget->showMessage(
|
||||
tr("Could not open remote database. Password or key file may be incorrect."), KMessageWidget::Error, -1);
|
||||
}
|
||||
|
||||
return remoteDb;
|
||||
}
|
||||
|
|
|
@ -21,6 +21,12 @@
|
|||
#include <QPointer>
|
||||
#include <QWizardPage>
|
||||
|
||||
#include <QLabel>
|
||||
#include <QProgressBar>
|
||||
#include <QStatusBar>
|
||||
|
||||
#include "../remote/RemoteHandler.h"
|
||||
|
||||
class CsvImportWidget;
|
||||
class Database;
|
||||
namespace Ui
|
||||
|
@ -48,6 +54,10 @@ private:
|
|||
QSharedPointer<Database> importBitwarden(const QString& filename, const QString& password);
|
||||
QSharedPointer<Database> importOPVault(const QString& filename, const QString& password);
|
||||
QSharedPointer<Database> importKeePass1(const QString& filename, const QString& password, const QString& keyfile);
|
||||
QSharedPointer<Database> importRemote(const QString& downloadCommand,
|
||||
const QString& downloadInput,
|
||||
const QString& password,
|
||||
const QString& keyfile);
|
||||
|
||||
void setupDatabasePreview();
|
||||
|
||||
|
@ -55,6 +65,7 @@ private:
|
|||
|
||||
QSharedPointer<Database> m_db;
|
||||
QPointer<CsvImportWidget> m_csvWidget;
|
||||
QPointer<RemoteHandler> m_remoteHandler;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -25,6 +25,8 @@
|
|||
#include "gui/Icons.h"
|
||||
#include "gui/MainWindow.h"
|
||||
|
||||
#include <QDesktopServices>
|
||||
|
||||
ImportWizardPageSelect::ImportWizardPageSelect(QWidget* parent)
|
||||
: QWizardPage(parent)
|
||||
, m_ui(new Ui::ImportWizardPageSelect())
|
||||
|
@ -36,12 +38,14 @@ ImportWizardPageSelect::ImportWizardPageSelect(QWidget* parent)
|
|||
new QListWidgetItem(icons()->icon("onepassword"), tr("1Password Vault (.opvault)"), m_ui->importTypeList);
|
||||
new QListWidgetItem(icons()->icon("bitwarden"), tr("Bitwarden (.json)"), m_ui->importTypeList);
|
||||
new QListWidgetItem(icons()->icon("object-locked"), tr("KeePass 1 Database (.kdb)"), m_ui->importTypeList);
|
||||
new QListWidgetItem(icons()->icon("web"), tr("Remote Database (.kdbx)"), m_ui->importTypeList);
|
||||
|
||||
m_ui->importTypeList->item(0)->setData(Qt::UserRole, ImportWizard::IMPORT_CSV);
|
||||
m_ui->importTypeList->item(1)->setData(Qt::UserRole, ImportWizard::IMPORT_OPUX);
|
||||
m_ui->importTypeList->item(2)->setData(Qt::UserRole, ImportWizard::IMPORT_OPVAULT);
|
||||
m_ui->importTypeList->item(3)->setData(Qt::UserRole, ImportWizard::IMPORT_BITWARDEN);
|
||||
m_ui->importTypeList->item(4)->setData(Qt::UserRole, ImportWizard::IMPORT_KEEPASS1);
|
||||
m_ui->importTypeList->item(5)->setData(Qt::UserRole, ImportWizard::IMPORT_REMOTE);
|
||||
|
||||
connect(m_ui->importTypeList, &QListWidget::currentItemChanged, this, &ImportWizardPageSelect::itemSelected);
|
||||
m_ui->importTypeList->setCurrentRow(0);
|
||||
|
@ -54,11 +58,22 @@ ImportWizardPageSelect::ImportWizardPageSelect(QWidget* parent)
|
|||
|
||||
updateDatabaseChoices();
|
||||
|
||||
m_ui->downloadCommandHelpButton->setIcon(icons()->icon("system-help"));
|
||||
connect(m_ui->downloadCommandHelpButton, &QToolButton::clicked, this, [] {
|
||||
QDesktopServices::openUrl(QUrl("https://keepassxc.org/docs/KeePassXC_UserGuide#_remote_database_support"));
|
||||
});
|
||||
|
||||
connect(m_ui->importFileEdit, &QLineEdit::textChanged, this, &QWizardPage::completeChanged);
|
||||
connect(m_ui->downloadCommand, &QLineEdit::textChanged, this, &QWizardPage::completeChanged);
|
||||
|
||||
registerField("ImportType", this);
|
||||
registerField("ImportFile*", m_ui->importFileEdit);
|
||||
registerField("ImportInto", m_ui->importIntoLabel);
|
||||
registerField("ImportFile", m_ui->importFileEdit);
|
||||
registerField("ImportIntoType", m_ui->importIntoGroupBox); // This is intentional
|
||||
registerField("ImportInto", m_ui->importIntoLabel); // This is intentional
|
||||
registerField("ImportPassword", m_ui->passwordEdit, "text", "textChanged");
|
||||
registerField("ImportKeyFile", m_ui->keyFileEdit);
|
||||
registerField("DownloadCommand", m_ui->downloadCommand);
|
||||
registerField("DownloadInput", m_ui->downloadCommandInput, "plainText", "textChanged");
|
||||
}
|
||||
|
||||
ImportWizardPageSelect::~ImportWizardPageSelect()
|
||||
|
@ -77,14 +92,27 @@ bool ImportWizardPageSelect::validatePage()
|
|||
if (m_ui->existingDatabaseChoice->currentIndex() == -1) {
|
||||
return false;
|
||||
}
|
||||
setField("ImportIntoType", ImportWizard::EXISTING_DATABASE);
|
||||
setField("ImportInto", m_ui->existingDatabaseChoice->currentData());
|
||||
} else if (m_ui->temporaryDatabaseRadio->isChecked()) {
|
||||
setField("ImportIntoType", ImportWizard::TEMPORARY_DATABASE);
|
||||
setField("ImportInto", {});
|
||||
} else {
|
||||
setField("ImportIntoType", ImportWizard::NEW_DATABASE);
|
||||
setField("ImportInto", {});
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ImportWizardPageSelect::isComplete() const
|
||||
{
|
||||
if (field("ImportType").toInt() == ImportWizard::IMPORT_REMOTE) {
|
||||
return !field("DownloadCommand").toString().isEmpty();
|
||||
}
|
||||
return !field("ImportFile").toString().isEmpty();
|
||||
}
|
||||
|
||||
void ImportWizardPageSelect::itemSelected(QListWidgetItem* current, QListWidgetItem* previous)
|
||||
{
|
||||
Q_UNUSED(previous)
|
||||
|
@ -105,15 +133,22 @@ void ImportWizardPageSelect::itemSelected(QListWidgetItem* current, QListWidgetI
|
|||
case ImportWizard::IMPORT_CSV:
|
||||
case ImportWizard::IMPORT_OPUX:
|
||||
setCredentialState(false);
|
||||
setDownloadCommand(false);
|
||||
break;
|
||||
// Password may be required
|
||||
case ImportWizard::IMPORT_BITWARDEN:
|
||||
case ImportWizard::IMPORT_OPVAULT:
|
||||
setCredentialState(true);
|
||||
setDownloadCommand(false);
|
||||
break;
|
||||
// Password and/or Key File may be required
|
||||
case ImportWizard::IMPORT_KEEPASS1:
|
||||
setCredentialState(true, true);
|
||||
setDownloadCommand(false);
|
||||
break;
|
||||
case ImportWizard::IMPORT_REMOTE:
|
||||
setCredentialState(true, true);
|
||||
setDownloadCommand(true);
|
||||
break;
|
||||
default:
|
||||
Q_ASSERT(false);
|
||||
|
@ -228,6 +263,33 @@ void ImportWizardPageSelect::setCredentialState(bool passwordEnabled, bool keyFi
|
|||
}
|
||||
}
|
||||
|
||||
void ImportWizardPageSelect::setDownloadCommand(bool downloadCommandEnabled)
|
||||
{
|
||||
bool downloadCommandStateChanged = m_ui->downloadCommandLabel->isVisible() != downloadCommandEnabled;
|
||||
m_ui->downloadCommandLabel->setVisible(downloadCommandEnabled);
|
||||
m_ui->downloadCommand->setVisible(downloadCommandEnabled);
|
||||
m_ui->downloadCommandInputLabel->setVisible(downloadCommandEnabled);
|
||||
m_ui->downloadCommandInput->setVisible(downloadCommandEnabled);
|
||||
m_ui->downloadCommandHelpButton->setVisible(downloadCommandEnabled);
|
||||
|
||||
m_ui->temporaryDatabaseRadio->setVisible(downloadCommandEnabled);
|
||||
|
||||
m_ui->importFileLabel->setVisible(!downloadCommandEnabled);
|
||||
m_ui->importFileEdit->setVisible(!downloadCommandEnabled);
|
||||
m_ui->importFileButton->setVisible(!downloadCommandEnabled);
|
||||
|
||||
// Workaround Qt bug where the wizard window is not updated when the internal layout changes
|
||||
if (window()) {
|
||||
int height = window()->height();
|
||||
if (downloadCommandStateChanged) {
|
||||
auto diff = m_ui->downloadCommand->height() + m_ui->downloadCommandInput->height()
|
||||
+ m_ui->temporaryDatabaseRadio->height() + m_ui->inputFields->layout()->spacing();
|
||||
height += downloadCommandEnabled ? diff : -diff;
|
||||
}
|
||||
window()->resize(window()->width(), height);
|
||||
}
|
||||
}
|
||||
|
||||
QString ImportWizardPageSelect::importFileFilter()
|
||||
{
|
||||
switch (field("ImportType").toInt()) {
|
||||
|
|
|
@ -39,6 +39,7 @@ public:
|
|||
|
||||
void initializePage() override;
|
||||
bool validatePage() override;
|
||||
bool isComplete() const override;
|
||||
|
||||
private slots:
|
||||
void itemSelected(QListWidgetItem* current, QListWidgetItem* previous);
|
||||
|
@ -49,6 +50,7 @@ private slots:
|
|||
private:
|
||||
QString importFileFilter();
|
||||
void setCredentialState(bool passwordEnabled, bool keyFileEnable = false);
|
||||
void setDownloadCommand(bool downloadCommandEnabled);
|
||||
|
||||
QScopedPointer<Ui::ImportWizardPageSelect> m_ui;
|
||||
};
|
||||
|
|
|
@ -94,14 +94,14 @@
|
|||
<property name="sizeConstraint">
|
||||
<enum>QLayout::SetMinimumSize</enum>
|
||||
</property>
|
||||
<item row="0" column="0">
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="importFileLabel">
|
||||
<property name="text">
|
||||
<string>Import File:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<item row="3" column="1">
|
||||
<layout class="QHBoxLayout" name="importFileLayout">
|
||||
<item>
|
||||
<widget class="QLineEdit" name="importFileEdit"/>
|
||||
|
@ -115,24 +115,24 @@
|
|||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<item row="4" column="0">
|
||||
<widget class="QLabel" name="passwordLabel">
|
||||
<property name="text">
|
||||
<string>Password:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<item row="4" column="1">
|
||||
<widget class="PasswordWidget" name="passwordEdit" native="true"/>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<item row="5" column="0">
|
||||
<widget class="QLabel" name="keyFileLabel">
|
||||
<property name="text">
|
||||
<string>Key File:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<item row="5" column="1">
|
||||
<layout class="QHBoxLayout" name="keyFileLayout">
|
||||
<item>
|
||||
<widget class="QLineEdit" name="keyFileEdit"/>
|
||||
|
@ -146,7 +146,7 @@
|
|||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<item row="6" column="1">
|
||||
<spacer name="verticalSpacer_3">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
|
@ -162,7 +162,7 @@
|
|||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<item row="7" column="0">
|
||||
<widget class="QLabel" name="importIntoLabel">
|
||||
<property name="text">
|
||||
<string>Import Into:</string>
|
||||
|
@ -172,7 +172,7 @@
|
|||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="1">
|
||||
<item row="7" column="1">
|
||||
<widget class="QGroupBox" name="importIntoGroupBox">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="MinimumExpanding">
|
||||
|
@ -239,9 +239,64 @@
|
|||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QRadioButton" name="temporaryDatabaseRadio">
|
||||
<property name="text">
|
||||
<string>Temporary Database</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="downloadCommandLabel">
|
||||
<property name="text">
|
||||
<string>Command:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<layout class="QHBoxLayout" name="downloadCommandLayout">
|
||||
<item>
|
||||
<widget class="QLineEdit" name="downloadCommand">
|
||||
<property name="placeholderText">
|
||||
<string>e.g.: "sftp user@hostname" or "scp user@hostname:DatabaseOnRemote.kdbx {TEMP_DATABASE}"</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="downloadCommandHelpButton">
|
||||
<property name="text">
|
||||
<string notr="true"/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="downloadCommandInputLabel">
|
||||
<property name="text">
|
||||
<string>Input:</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QPlainTextEdit" name="downloadCommandInput">
|
||||
<property name="placeholderText">
|
||||
<string>e.g.:
|
||||
get DatabaseOnRemote.kdbx {TEMP_DATABASE}
|
||||
exit
|
||||
---
|
||||
{TEMP_DATABASE} is used as placeholder to store the database in a temporary location
|
||||
The command has to exit. In case of `sftp` as last commend `exit` has to be sent
|
||||
</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue