Add Browser Integration to Group Edit page

Closes #1789 and closes #3998
This commit is contained in:
varjolintu 2021-10-09 14:46:39 -04:00 committed by Jonathan White
parent c7cdce6e33
commit b6716bdfe5
11 changed files with 524 additions and 66 deletions

View file

@ -2513,6 +2513,10 @@ Would you like to correct it?</source>
Would you like to correct it?</source> Would you like to correct it?</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message>
<source>Some Browser Integration settings are overridden by group settings.</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>EditEntryWidgetAdvanced</name> <name>EditEntryWidgetAdvanced</name>
@ -2968,8 +2972,55 @@ Would you like to correct it?</source>
<translation>Inherit from parent group (%1)</translation> <translation>Inherit from parent group (%1)</translation>
</message> </message>
<message> <message>
<source>Entry has unsaved changes</source> <source>Browser Integration</source>
<translation type="unfinished">Entry has unsaved changes</translation> <translation type="unfinished">Browser Integration</translation>
</message>
<message>
<source>Group has unsaved changes</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>EditGroupWidgetBrowser</name>
<message>
<source>Edit Group</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>These settings affect to the group&apos;s behaviour with the browser extension.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Hide entries from browser extension:</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Hide entries from browser extension toggle for this and sub groups</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Skip Auto-Submit for entries:</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Skip Auto-Submit toggle for this and sub groups</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Use entries only with HTTP Basic Auth:</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Only HTTP Auth toggle for this and sub groups</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Do not use entries with HTTP Basic Auth:</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Do not use HTTP Auth toggle for this and sub groups</source>
<translation type="unfinished"></translation>
</message> </message>
</context> </context>
<context> <context>

View file

@ -390,18 +390,19 @@ QJsonArray BrowserService::findMatchingEntries(const QString& dbid,
QList<Entry*> pwEntriesToConfirm; QList<Entry*> pwEntriesToConfirm;
QList<Entry*> pwEntries; QList<Entry*> pwEntries;
for (auto* entry : searchEntries(siteUrlStr, formUrlStr, keyList)) { for (auto* entry : searchEntries(siteUrlStr, formUrlStr, keyList)) {
if (entry->customData()->contains(BrowserService::OPTION_HIDE_ENTRY) auto entryCustomData = entry->customData();
&& entry->customData()->value(BrowserService::OPTION_HIDE_ENTRY) == TRUE_STR) {
if (!httpAuth
&& ((entryCustomData->contains(BrowserService::OPTION_ONLY_HTTP_AUTH)
&& entryCustomData->value(BrowserService::OPTION_ONLY_HTTP_AUTH) == TRUE_STR)
|| entry->group()->resolveCustomDataTriState(BrowserService::OPTION_ONLY_HTTP_AUTH) == Group::Enable)) {
continue; continue;
} }
if (!httpAuth && entry->customData()->contains(BrowserService::OPTION_ONLY_HTTP_AUTH) if (httpAuth
&& entry->customData()->value(BrowserService::OPTION_ONLY_HTTP_AUTH) == TRUE_STR) { && ((entryCustomData->contains(BrowserService::OPTION_NOT_HTTP_AUTH)
continue; && entryCustomData->value(BrowserService::OPTION_NOT_HTTP_AUTH) == TRUE_STR)
} || entry->group()->resolveCustomDataTriState(BrowserService::OPTION_NOT_HTTP_AUTH) == Group::Enable)) {
if (httpAuth && entry->customData()->contains(BrowserService::OPTION_NOT_HTTP_AUTH)
&& entry->customData()->value(BrowserService::OPTION_NOT_HTTP_AUTH) == TRUE_STR) {
continue; continue;
} }
@ -614,12 +615,15 @@ BrowserService::searchEntries(const QSharedPointer<Database>& db, const QString&
} }
for (const auto& group : rootGroup->groupsRecursive(true)) { for (const auto& group : rootGroup->groupsRecursive(true)) {
if (group->isRecycled() || !group->resolveSearchingEnabled()) { if (group->isRecycled()
|| group->resolveCustomDataTriState(BrowserService::OPTION_HIDE_ENTRY) == Group::Enable) {
continue; continue;
} }
for (auto* entry : group->entries()) { for (auto* entry : group->entries()) {
if (entry->isRecycled()) { if (entry->isRecycled()
|| (entry->customData()->contains(BrowserService::OPTION_HIDE_ENTRY)
&& entry->customData()->value(BrowserService::OPTION_HIDE_ENTRY) == TRUE_STR)) {
continue; continue;
} }
@ -870,9 +874,14 @@ QJsonObject BrowserService::prepareEntry(const Entry* entry)
res["expired"] = TRUE_STR; res["expired"] = TRUE_STR;
} }
auto skipAutoSubmitGroup = entry->group()->resolveCustomDataTriState(BrowserService::OPTION_SKIP_AUTO_SUBMIT);
if (skipAutoSubmitGroup == Group::Inherit) {
if (entry->customData()->contains(BrowserService::OPTION_SKIP_AUTO_SUBMIT)) { if (entry->customData()->contains(BrowserService::OPTION_SKIP_AUTO_SUBMIT)) {
res["skipAutoSubmit"] = entry->customData()->value(BrowserService::OPTION_SKIP_AUTO_SUBMIT); res["skipAutoSubmit"] = entry->customData()->value(BrowserService::OPTION_SKIP_AUTO_SUBMIT);
} }
} else {
res["skipAutoSubmit"] = skipAutoSubmitGroup == Group::Enable ? TRUE_STR : FALSE_STR;
}
if (browserSettings()->supportKphFields()) { if (browserSettings()->supportKphFields()) {
const EntryAttributes* attr = entry->attributes(); const EntryAttributes* attr = entry->attributes();

View file

@ -80,11 +80,13 @@ void CustomData::remove(const QString& key)
{ {
emit aboutToBeRemoved(key); emit aboutToBeRemoved(key);
if (m_data.contains(key)) {
m_data.remove(key); m_data.remove(key);
updateLastModified(); updateLastModified();
emit removed(key);
emitModified(); emitModified();
}
emit removed(key);
} }
void CustomData::rename(const QString& oldKey, const QString& newKey) void CustomData::rename(const QString& oldKey, const QString& newKey)
@ -180,7 +182,7 @@ int CustomData::dataSize() const
void CustomData::updateLastModified() void CustomData::updateLastModified()
{ {
if (m_data.size() == 1 && m_data.contains(LastModified)) { if (m_data.isEmpty() || (m_data.size() == 1 && m_data.contains(LastModified))) {
m_data.remove(LastModified); m_data.remove(LastModified);
return; return;
} }

View file

@ -1,6 +1,6 @@
/* /*
* Copyright (C) 2010 Felix Geyer <debfx@fobos.de> * Copyright (C) 2010 Felix Geyer <debfx@fobos.de>
* Copyright (C) 2017 KeePassXC Team <team@keepassxc.org> * Copyright (C) 2021 KeePassXC Team <team@keepassxc.org>
* *
* This program is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@ -276,6 +276,35 @@ const CustomData* Group::customData() const
return m_customData; return m_customData;
} }
Group::TriState Group::resolveCustomDataTriState(const QString& key, bool checkParent) const
{
// If not defined, check our parent up to the root group
if (!m_customData->contains(key)) {
if (!m_parent || !checkParent) {
return Inherit;
} else {
return m_parent->resolveCustomDataTriState(key);
}
}
return m_customData->value(key) == TRUE_STR ? Enable : Disable;
}
void Group::setCustomDataTriState(const QString& key, const Group::TriState& value)
{
switch (value) {
case Enable:
m_customData->set(key, TRUE_STR);
break;
case Disable:
m_customData->set(key, FALSE_STR);
break;
case Inherit:
m_customData->remove(key);
break;
}
}
bool Group::equals(const Group* other, CompareItemOptions options) const bool Group::equals(const Group* other, CompareItemOptions options) const
{ {
if (!other) { if (!other) {

View file

@ -1,6 +1,6 @@
/* /*
* Copyright (C) 2010 Felix Geyer <debfx@fobos.de> * Copyright (C) 2010 Felix Geyer <debfx@fobos.de>
* Copyright (C) 2017 KeePassXC Team <team@keepassxc.org> * Copyright (C) 2021 KeePassXC Team <team@keepassxc.org>
* *
* This program is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@ -101,6 +101,8 @@ public:
bool isEmpty() const; bool isEmpty() const;
CustomData* customData(); CustomData* customData();
const CustomData* customData() const; const CustomData* customData() const;
Group::TriState resolveCustomDataTriState(const QString& key, bool checkParent = true) const;
void setCustomDataTriState(const QString& key, const Group::TriState& value);
bool equals(const Group* other, CompareItemOptions options) const; bool equals(const Group* other, CompareItemOptions options) const;

View file

@ -1,6 +1,6 @@
/* /*
* Copyright (C) 2010 Felix Geyer <debfx@fobos.de> * Copyright (C) 2010 Felix Geyer <debfx@fobos.de>
* Copyright (C) 2020 KeePassXC Team <team@keepassxc.org> * Copyright (C) 2021 KeePassXC Team <team@keepassxc.org>
* *
* This program is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@ -36,6 +36,7 @@
#include "core/Database.h" #include "core/Database.h"
#include "core/Entry.h" #include "core/Entry.h"
#include "core/EntryAttributes.h" #include "core/EntryAttributes.h"
#include "core/Group.h"
#include "core/Metadata.h" #include "core/Metadata.h"
#include "core/TimeDelta.h" #include "core/TimeDelta.h"
#ifdef WITH_XC_SSHAGENT #ifdef WITH_XC_SSHAGENT
@ -263,6 +264,11 @@ void EditEntryWidget::setupBrowser()
m_additionalURLsDataModel->setEntryAttributes(m_entryAttributes); m_additionalURLsDataModel->setEntryAttributes(m_entryAttributes);
m_browserUi->additionalURLsView->setModel(m_additionalURLsDataModel); m_browserUi->additionalURLsView->setModel(m_additionalURLsDataModel);
m_browserUi->messageWidget->setCloseButtonVisible(false);
m_browserUi->messageWidget->setAutoHideTimeout(-1);
m_browserUi->messageWidget->setAnimate(false);
m_browserUi->messageWidget->setVisible(false);
// Use a custom item delegate to align the icon to the right side // Use a custom item delegate to align the icon to the right side
auto iconDelegate = new URLModelIconDelegate(m_browserUi->additionalURLsView); auto iconDelegate = new URLModelIconDelegate(m_browserUi->additionalURLsView);
m_browserUi->additionalURLsView->setItemDelegate(iconDelegate); m_browserUi->additionalURLsView->setItemDelegate(iconDelegate);
@ -296,14 +302,26 @@ void EditEntryWidget::updateBrowser()
return; return;
} }
auto skip = m_browserUi->skipAutoSubmitCheckbox->isChecked(); // Only update the custom data if no group level settings are used (checkbox is enabled)
if (m_browserUi->hideEntryCheckbox->isEnabled()) {
auto hide = m_browserUi->hideEntryCheckbox->isChecked(); auto hide = m_browserUi->hideEntryCheckbox->isChecked();
auto onlyHttpAuth = m_browserUi->onlyHttpAuthCheckbox->isChecked();
auto notHttpAuth = m_browserUi->notHttpAuthCheckbox->isChecked();
m_customData->set(BrowserService::OPTION_SKIP_AUTO_SUBMIT, (skip ? TRUE_STR : FALSE_STR));
m_customData->set(BrowserService::OPTION_HIDE_ENTRY, (hide ? TRUE_STR : FALSE_STR)); m_customData->set(BrowserService::OPTION_HIDE_ENTRY, (hide ? TRUE_STR : FALSE_STR));
}
if (m_browserUi->skipAutoSubmitCheckbox->isEnabled()) {
auto skip = m_browserUi->skipAutoSubmitCheckbox->isChecked();
m_customData->set(BrowserService::OPTION_SKIP_AUTO_SUBMIT, (skip ? TRUE_STR : FALSE_STR));
}
if (m_browserUi->onlyHttpAuthCheckbox->isEnabled()) {
auto onlyHttpAuth = m_browserUi->onlyHttpAuthCheckbox->isChecked();
m_customData->set(BrowserService::OPTION_ONLY_HTTP_AUTH, (onlyHttpAuth ? TRUE_STR : FALSE_STR)); m_customData->set(BrowserService::OPTION_ONLY_HTTP_AUTH, (onlyHttpAuth ? TRUE_STR : FALSE_STR));
}
if (m_browserUi->notHttpAuthCheckbox->isEnabled()) {
auto notHttpAuth = m_browserUi->notHttpAuthCheckbox->isChecked();
m_customData->set(BrowserService::OPTION_NOT_HTTP_AUTH, (notHttpAuth ? TRUE_STR : FALSE_STR)); m_customData->set(BrowserService::OPTION_NOT_HTTP_AUTH, (notHttpAuth ? TRUE_STR : FALSE_STR));
}
} }
void EditEntryWidget::insertURL() void EditEntryWidget::insertURL()
@ -941,34 +959,55 @@ void EditEntryWidget::setForms(Entry* entry, bool restore)
setupBrowser(); setupBrowser();
} }
if (m_customData->contains(BrowserService::OPTION_SKIP_AUTO_SUBMIT)) { auto hideEntriesCheckBoxEnabled = true;
// clang-format off auto skipAutoSubmitCheckBoxEnabled = true;
m_browserUi->skipAutoSubmitCheckbox->setChecked(m_customData->value(BrowserService::OPTION_SKIP_AUTO_SUBMIT) == TRUE_STR); auto onlyHttpAuthCheckBoxEnabled = true;
// clang-format on auto notHttpAuthCheckBoxEnabled = true;
} else { auto hideEntries = false;
m_browserUi->skipAutoSubmitCheckbox->setChecked(false); auto skipAutoSubmit = false;
auto onlyHttpAuth = false;
auto notHttpAuth = false;
const auto group = m_entry->group();
if (group) {
hideEntries = group->resolveCustomDataTriState(BrowserService::OPTION_HIDE_ENTRY) == Group::Enable;
skipAutoSubmit = group->resolveCustomDataTriState(BrowserService::OPTION_SKIP_AUTO_SUBMIT) == Group::Enable;
onlyHttpAuth = group->resolveCustomDataTriState(BrowserService::OPTION_ONLY_HTTP_AUTH) == Group::Enable;
notHttpAuth = group->resolveCustomDataTriState(BrowserService::OPTION_NOT_HTTP_AUTH) == Group::Enable;
hideEntriesCheckBoxEnabled =
group->resolveCustomDataTriState(BrowserService::OPTION_HIDE_ENTRY) == Group::Inherit;
skipAutoSubmitCheckBoxEnabled =
group->resolveCustomDataTriState(BrowserService::OPTION_SKIP_AUTO_SUBMIT) == Group::Inherit;
onlyHttpAuthCheckBoxEnabled =
group->resolveCustomDataTriState(BrowserService::OPTION_ONLY_HTTP_AUTH) == Group::Inherit;
notHttpAuthCheckBoxEnabled =
group->resolveCustomDataTriState(BrowserService::OPTION_NOT_HTTP_AUTH) == Group::Inherit;
} }
if (m_customData->contains(BrowserService::OPTION_HIDE_ENTRY)) { // Show information about group level settings
m_browserUi->hideEntryCheckbox->setChecked(m_customData->value(BrowserService::OPTION_HIDE_ENTRY) if (!hideEntriesCheckBoxEnabled || !skipAutoSubmitCheckBoxEnabled || !onlyHttpAuthCheckBoxEnabled
== TRUE_STR); || !notHttpAuthCheckBoxEnabled) {
} else { m_browserUi->messageWidget->showMessage(
m_browserUi->hideEntryCheckbox->setChecked(false); tr("Some Browser Integration settings are overridden by group settings."), MessageWidget::Information);
m_browserUi->messageWidget->setVisible(true);
} }
if (m_customData->contains(BrowserService::OPTION_ONLY_HTTP_AUTH)) { // Disable checkboxes based on group level settings
m_browserUi->onlyHttpAuthCheckbox->setChecked(m_customData->value(BrowserService::OPTION_ONLY_HTTP_AUTH) updateBrowserIntegrationCheckbox(
== TRUE_STR); m_browserUi->hideEntryCheckbox, hideEntriesCheckBoxEnabled, hideEntries, BrowserService::OPTION_HIDE_ENTRY);
} else { updateBrowserIntegrationCheckbox(m_browserUi->skipAutoSubmitCheckbox,
m_browserUi->onlyHttpAuthCheckbox->setChecked(false); skipAutoSubmitCheckBoxEnabled,
} skipAutoSubmit,
BrowserService::OPTION_SKIP_AUTO_SUBMIT);
if (m_customData->contains(BrowserService::OPTION_NOT_HTTP_AUTH)) { updateBrowserIntegrationCheckbox(m_browserUi->onlyHttpAuthCheckbox,
m_browserUi->notHttpAuthCheckbox->setChecked(m_customData->value(BrowserService::OPTION_NOT_HTTP_AUTH) onlyHttpAuthCheckBoxEnabled,
== TRUE_STR); onlyHttpAuth,
} else { BrowserService::OPTION_ONLY_HTTP_AUTH);
m_browserUi->notHttpAuthCheckbox->setChecked(false); updateBrowserIntegrationCheckbox(m_browserUi->notHttpAuthCheckbox,
} notHttpAuthCheckBoxEnabled,
notHttpAuth,
BrowserService::OPTION_NOT_HTTP_AUTH);
m_browserUi->addURLButton->setEnabled(!m_history); m_browserUi->addURLButton->setEnabled(!m_history);
m_browserUi->removeURLButton->setEnabled(false); m_browserUi->removeURLButton->setEnabled(false);
@ -1163,6 +1202,28 @@ void EditEntryWidget::updateEntryData(Entry* entry) const
#endif #endif
} }
void EditEntryWidget::updateBrowserIntegrationCheckbox(QCheckBox* checkBox,
bool enabled,
bool value,
const QString& option)
{
auto block = checkBox->signalsBlocked();
checkBox->blockSignals(true);
if (enabled) {
if (m_customData->contains(option)) {
checkBox->setChecked(m_customData->value(option) == TRUE_STR);
} else {
checkBox->setChecked(false);
}
} else {
checkBox->setChecked(value);
}
checkBox->setEnabled(enabled);
checkBox->blockSignals(block);
}
void EditEntryWidget::cancel() void EditEntryWidget::cancel()
{ {
if (m_history) { if (m_history) {

View file

@ -1,6 +1,6 @@
/* /*
* Copyright (C) 2010 Felix Geyer <debfx@fobos.de> * Copyright (C) 2010 Felix Geyer <debfx@fobos.de>
* Copyright (C) 2017 KeePassXC Team <team@keepassxc.org> * Copyright (C) 2021 KeePassXC Team <team@keepassxc.org>
* *
* This program is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@ -20,6 +20,7 @@
#define KEEPASSX_EDITENTRYWIDGET_H #define KEEPASSX_EDITENTRYWIDGET_H
#include <QButtonGroup> #include <QButtonGroup>
#include <QCheckBox>
#include <QCompleter> #include <QCompleter>
#include <QPointer> #include <QPointer>
#include <QTimer> #include <QTimer>
@ -152,6 +153,7 @@ private:
void setForms(Entry* entry, bool restore = false); void setForms(Entry* entry, bool restore = false);
QMenu* createPresetsMenu(); QMenu* createPresetsMenu();
void updateEntryData(Entry* entry) const; void updateEntryData(Entry* entry) const;
void updateBrowserIntegrationCheckbox(QCheckBox* checkBox, bool enabled, bool value, const QString& option);
#ifdef WITH_XC_SSHAGENT #ifdef WITH_XC_SSHAGENT
bool getOpenSSHKey(OpenSSHKey& key, bool decrypt = false); bool getOpenSSHKey(OpenSSHKey& key, bool decrypt = false);
#endif #endif

View file

@ -30,6 +30,16 @@
</property> </property>
</widget> </widget>
</item> </item>
<item>
<widget class="MessageWidget" name="messageWidget" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item> <item>
<widget class="QGroupBox" name="groupBox_2"> <widget class="QGroupBox" name="groupBox_2">
<property name="title"> <property name="title">
@ -37,16 +47,16 @@
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout_2"> <layout class="QVBoxLayout" name="verticalLayout_2">
<item> <item>
<widget class="QCheckBox" name="skipAutoSubmitCheckbox"> <widget class="QCheckBox" name="hideEntryCheckbox">
<property name="text"> <property name="text">
<string>Skip Auto-Submit for this entry</string> <string>Hide this entry from the browser extension</string>
</property> </property>
</widget> </widget>
</item> </item>
<item> <item>
<widget class="QCheckBox" name="hideEntryCheckbox"> <widget class="QCheckBox" name="skipAutoSubmitCheckbox">
<property name="text"> <property name="text">
<string>Hide this entry from the browser extension</string> <string>Skip Auto-Submit for this entry</string>
</property> </property>
</widget> </widget>
</item> </item>
@ -147,9 +157,15 @@
</item> </item>
</layout> </layout>
</widget> </widget>
<customwidgets>
<customwidget>
<class>MessageWidget</class>
<extends>QWidget</extends>
<header>gui/MessageWidget.h</header>
</customwidget>
</customwidgets>
<tabstops> <tabstops>
<tabstop>skipAutoSubmitCheckbox</tabstop> <tabstop>skipAutoSubmitCheckbox</tabstop>
<tabstop>hideEntryCheckbox</tabstop>
<tabstop>onlyHttpAuthCheckbox</tabstop> <tabstop>onlyHttpAuthCheckbox</tabstop>
<tabstop>additionalURLsView</tabstop> <tabstop>additionalURLsView</tabstop>
<tabstop>addURLButton</tabstop> <tabstop>addURLButton</tabstop>

View file

@ -1,5 +1,6 @@
/* /*
* Copyright (C) 2011 Felix Geyer <debfx@fobos.de> * Copyright (C) 2011 Felix Geyer <debfx@fobos.de>
* Copyright (C) 2021 KeePassXC Team <team@keepassxc.org>
* *
* This program is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@ -17,6 +18,10 @@
#include "EditGroupWidget.h" #include "EditGroupWidget.h"
#include "ui_EditGroupWidgetMain.h" #include "ui_EditGroupWidgetMain.h"
#if defined(WITH_XC_BROWSER)
#include "browser/BrowserService.h"
#include "ui_EditGroupWidgetBrowser.h"
#endif
#include "core/Config.h" #include "core/Config.h"
#include "core/Metadata.h" #include "core/Metadata.h"
@ -65,12 +70,23 @@ EditGroupWidget::EditGroupWidget(QWidget* parent)
, m_editGroupWidgetMain(new QScrollArea()) , m_editGroupWidgetMain(new QScrollArea())
, m_editGroupWidgetIcons(new EditWidgetIcons()) , m_editGroupWidgetIcons(new EditWidgetIcons())
, m_editWidgetProperties(new EditWidgetProperties()) , m_editWidgetProperties(new EditWidgetProperties())
#if defined(WITH_XC_BROWSER)
, m_browserSettingsChanged(false)
, m_browserUi(new Ui::EditGroupWidgetBrowser())
, m_browserWidget(new QScrollArea())
#endif
, m_group(nullptr) , m_group(nullptr)
{ {
m_mainUi->setupUi(m_editGroupWidgetMain); m_mainUi->setupUi(m_editGroupWidgetMain);
addPage(tr("Group"), icons()->icon("document-edit"), m_editGroupWidgetMain); addPage(tr("Group"), icons()->icon("document-edit"), m_editGroupWidgetMain);
addPage(tr("Icon"), icons()->icon("preferences-desktop-icons"), m_editGroupWidgetIcons); addPage(tr("Icon"), icons()->icon("preferences-desktop-icons"), m_editGroupWidgetIcons);
#if defined(WITH_XC_BROWSER)
if (config()->get(Config::Browser_Enabled).toBool()) {
addPage(tr("Browser Integration"), icons()->icon("internet-web-browser"), m_browserWidget);
m_browserUi->setupUi(m_browserWidget);
}
#endif
#if defined(WITH_XC_KEESHARE) #if defined(WITH_XC_KEESHARE)
addEditPage(new EditGroupPageKeeShare(this)); addEditPage(new EditGroupPageKeeShare(this));
#endif #endif
@ -116,6 +132,33 @@ void EditGroupWidget::setupModifiedTracking()
// Icon tab // Icon tab
connect(m_editGroupWidgetIcons, SIGNAL(widgetUpdated()), SLOT(setModified())); connect(m_editGroupWidgetIcons, SIGNAL(widgetUpdated()), SLOT(setModified()));
#if defined(WITH_XC_BROWSER)
if (config()->get(Config::Browser_Enabled).toBool()) {
// Browser integration tab
connect(
m_browserUi->browserIntegrationHideEntriesComboBox, SIGNAL(currentIndexChanged(int)), SLOT(setModified()));
connect(m_browserUi->browserIntegrationSkipAutoSubmitComboBox,
SIGNAL(currentIndexChanged(int)),
SLOT(setModified()));
connect(
m_browserUi->browserIntegrationOnlyHttpAuthComboBox, SIGNAL(currentIndexChanged(int)), SLOT(setModified()));
connect(
m_browserUi->browserIntegrationNotHttpAuthComboBox, SIGNAL(currentIndexChanged(int)), SLOT(setModified()));
connect(m_browserUi->browserIntegrationHideEntriesComboBox,
SIGNAL(currentIndexChanged(int)),
SLOT(updateBrowserModified()));
connect(m_browserUi->browserIntegrationSkipAutoSubmitComboBox,
SIGNAL(currentIndexChanged(int)),
SLOT(updateBrowserModified()));
connect(m_browserUi->browserIntegrationOnlyHttpAuthComboBox,
SIGNAL(currentIndexChanged(int)),
SLOT(updateBrowserModified()));
connect(m_browserUi->browserIntegrationNotHttpAuthComboBox,
SIGNAL(currentIndexChanged(int)),
SLOT(updateBrowserModified()));
}
#endif
} }
void EditGroupWidget::loadGroup(Group* group, bool create, const QSharedPointer<Database>& database) void EditGroupWidget::loadGroup(Group* group, bool create, const QSharedPointer<Database>& database)
@ -170,6 +213,37 @@ void EditGroupWidget::loadGroup(Group* group, bool create, const QSharedPointer<
page.set(m_temporaryGroup.data(), m_db); page.set(m_temporaryGroup.data(), m_db);
} }
#ifdef WITH_XC_BROWSER
if (config()->get(Config::Browser_Enabled).toBool()) {
auto inheritHideEntries = false;
auto inheritSkipSubmit = false;
auto inheritOnlyHttp = false;
auto inheritNoHttp = false;
auto parent = group->parentGroup();
if (parent) {
inheritHideEntries = parent->resolveCustomDataTriState(BrowserService::OPTION_HIDE_ENTRY);
inheritSkipSubmit = parent->resolveCustomDataTriState(BrowserService::OPTION_SKIP_AUTO_SUBMIT);
inheritOnlyHttp = parent->resolveCustomDataTriState(BrowserService::OPTION_ONLY_HTTP_AUTH);
inheritNoHttp = parent->resolveCustomDataTriState(BrowserService::OPTION_NOT_HTTP_AUTH);
}
addTriStateItems(m_browserUi->browserIntegrationHideEntriesComboBox, inheritHideEntries);
addTriStateItems(m_browserUi->browserIntegrationSkipAutoSubmitComboBox, inheritSkipSubmit);
addTriStateItems(m_browserUi->browserIntegrationOnlyHttpAuthComboBox, inheritOnlyHttp);
addTriStateItems(m_browserUi->browserIntegrationNotHttpAuthComboBox, inheritNoHttp);
m_browserUi->browserIntegrationHideEntriesComboBox->setCurrentIndex(
indexFromTriState(group->resolveCustomDataTriState(BrowserService::OPTION_HIDE_ENTRY, false)));
m_browserUi->browserIntegrationSkipAutoSubmitComboBox->setCurrentIndex(
indexFromTriState(group->resolveCustomDataTriState(BrowserService::OPTION_SKIP_AUTO_SUBMIT, false)));
m_browserUi->browserIntegrationOnlyHttpAuthComboBox->setCurrentIndex(
indexFromTriState(group->resolveCustomDataTriState(BrowserService::OPTION_ONLY_HTTP_AUTH, false)));
m_browserUi->browserIntegrationNotHttpAuthComboBox->setCurrentIndex(
indexFromTriState(group->resolveCustomDataTriState(BrowserService::OPTION_NOT_HTTP_AUTH, false)));
}
#endif
setCurrentPage(0); setCurrentPage(0);
m_mainUi->editName->setFocus(); m_mainUi->editName->setFocus();
@ -217,6 +291,27 @@ void EditGroupWidget::apply()
page.assign(); page.assign();
} }
#ifdef WITH_XC_BROWSER
if (config()->get(Config::Browser_Enabled).toBool()) {
if (!m_browserSettingsChanged) {
return;
}
m_temporaryGroup->setCustomDataTriState(
BrowserService::OPTION_HIDE_ENTRY,
triStateFromIndex(m_browserUi->browserIntegrationHideEntriesComboBox->currentIndex()));
m_temporaryGroup->setCustomDataTriState(
BrowserService::OPTION_SKIP_AUTO_SUBMIT,
triStateFromIndex(m_browserUi->browserIntegrationSkipAutoSubmitComboBox->currentIndex()));
m_temporaryGroup->setCustomDataTriState(
BrowserService::OPTION_ONLY_HTTP_AUTH,
triStateFromIndex(m_browserUi->browserIntegrationOnlyHttpAuthComboBox->currentIndex()));
m_temporaryGroup->setCustomDataTriState(
BrowserService::OPTION_NOT_HTTP_AUTH,
triStateFromIndex(m_browserUi->browserIntegrationNotHttpAuthComboBox->currentIndex()));
}
#endif
// Icons add/remove are applied globally outside the transaction! // Icons add/remove are applied globally outside the transaction!
m_group->copyDataFrom(m_temporaryGroup.data()); m_group->copyDataFrom(m_temporaryGroup.data());
@ -243,7 +338,7 @@ void EditGroupWidget::cancel()
if (isModified()) { if (isModified()) {
auto result = MessageBox::question(this, auto result = MessageBox::question(this,
QString(), QString(),
tr("Entry has unsaved changes"), tr("Group has unsaved changes"),
MessageBox::Cancel | MessageBox::Save | MessageBox::Discard, MessageBox::Cancel | MessageBox::Save | MessageBox::Discard,
MessageBox::Cancel); MessageBox::Cancel);
if (result == MessageBox::Cancel) { if (result == MessageBox::Cancel) {
@ -259,6 +354,13 @@ void EditGroupWidget::cancel()
emit editFinished(false); emit editFinished(false);
} }
#ifdef WITH_XC_BROWSER
void EditGroupWidget::updateBrowserModified()
{
m_browserSettingsChanged = true;
}
#endif
void EditGroupWidget::clear() void EditGroupWidget::clear()
{ {
m_group = nullptr; m_group = nullptr;

View file

@ -1,5 +1,6 @@
/* /*
* Copyright (C) 2011 Felix Geyer <debfx@fobos.de> * Copyright (C) 2011 Felix Geyer <debfx@fobos.de>
* Copyright (C) 2021 KeePassXC Team <team@keepassxc.org>
* *
* This program is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@ -31,6 +32,7 @@ class EditWidgetProperties;
namespace Ui namespace Ui
{ {
class EditGroupWidgetMain; class EditGroupWidgetMain;
class EditGroupWidgetBrowser;
class EditWidget; class EditWidget;
} // namespace Ui } // namespace Ui
@ -69,6 +71,9 @@ private slots:
void apply(); void apply();
void save(); void save();
void cancel(); void cancel();
#ifdef WITH_XC_BROWSER
void updateBrowserModified();
#endif
private: private:
void addTriStateItems(QComboBox* comboBox, bool inheritValue); void addTriStateItems(QComboBox* comboBox, bool inheritValue);
@ -81,6 +86,11 @@ private:
QPointer<QScrollArea> m_editGroupWidgetMain; QPointer<QScrollArea> m_editGroupWidgetMain;
QPointer<EditWidgetIcons> m_editGroupWidgetIcons; QPointer<EditWidgetIcons> m_editGroupWidgetIcons;
QPointer<EditWidgetProperties> m_editWidgetProperties; QPointer<EditWidgetProperties> m_editWidgetProperties;
#ifdef WITH_XC_BROWSER
bool m_browserSettingsChanged;
const QScopedPointer<Ui::EditGroupWidgetBrowser> m_browserUi;
QPointer<QScrollArea> m_browserWidget;
#endif
QScopedPointer<Group> m_temporaryGroup; QScopedPointer<Group> m_temporaryGroup;
QPointer<Group> m_group; QPointer<Group> m_group;

View file

@ -0,0 +1,174 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>EditGroupWidgetBrowser</class>
<widget class="QScrollArea" name="EditGroupWidgetBrowser">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>539</width>
<height>523</height>
</rect>
</property>
<property name="windowTitle">
<string>Edit Group</string>
</property>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Plain</enum>
</property>
<property name="horizontalScrollBarPolicy">
<enum>Qt::ScrollBarAlwaysOff</enum>
</property>
<property name="sizeAdjustPolicy">
<enum>QAbstractScrollArea::AdjustToContents</enum>
</property>
<property name="widgetResizable">
<bool>true</bool>
</property>
<widget class="QWidget" name="container">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>539</width>
<height>523</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_1">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>These settings affect to the group's behaviour with the browser extension.</string>
</property>
</widget>
</item>
<item>
<layout class="QGridLayout" name="gridLayout" rowstretch="0,0,0,1" columnstretch="0,1" rowminimumheight="0,0,0,1">
   <property name="leftMargin">
   <number>0</number>
   </property>
   <property name="topMargin">
   <number>10</number>
   </property>
   <property name="rightMargin">
   <number>0</number>
   </property>
   <property name="bottomMargin">
  <number>0</number>
  </property>
  <property name="horizontalSpacing">
  <number>10</number>
  </property>
  <property name="verticalSpacing">
  <number>8</number>
  </property>
  <item row="0" column="0">
  <widget class="QLabel" name="browserIntegrationHideEntriesLabel">
  <property name="text">
  <string>Hide entries from browser extension:</string>
  </property>
  <property name="alignment">
  <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
  </property>
  </widget>
  </item>
  <item row="0" column="1">
  <widget class="QComboBox" name="browserIntegrationHideEntriesComboBox">
  <property name="accessibleName">
  <string>Hide entries from browser extension toggle for this and sub groups</string>
  </property>
  </widget>
  </item>
  <item row="1" column="0">
  <widget class="QLabel" name="browserIntegrationSkipAutoSubmitLabel">
  <property name="text">
  <string>Skip Auto-Submit for entries:</string>
  </property>
  <property name="alignment">
  <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
  </property>
  </widget>
  </item>
  <item row="1" column="1">
  <widget class="QComboBox" name="browserIntegrationSkipAutoSubmitComboBox">
  <property name="accessibleName">
  <string>Skip Auto-Submit toggle for this and sub groups</string>
  </property>
  </widget>
  </item>
  <item row="2" column="0">
  <widget class="QLabel" name="browserIntegrationOnlyHttpAuthLabel">
  <property name="text">
  <string>Use entries only with HTTP Basic Auth:</string>
  </property>
  <property name="alignment">
  <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
  </property>
  </widget>
  </item>
  <item row="2" column="1">
  <widget class="QComboBox" name="browserIntegrationOnlyHttpAuthComboBox">
  <property name="accessibleName">
  <string>Only HTTP Auth toggle for this and sub groups</string>
  </property>
  </widget>
  </item>
  <item row="3" column="0">
   <widget class="QLabel" name="browserIntegrationNotHttpAuthLabel">
   <property name="text">
   <string>Do not use entries with HTTP Basic Auth:</string>
   </property>
   <property name="alignment">
   <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
   </property>
   </widget>
   </item>
   <item row="3" column="1">
   <widget class="QComboBox" name="browserIntegrationNotHttpAuthComboBox">
   <property name="accessibleName">
   <string>Do not use HTTP Auth toggle for this and sub groups</string>
   </property>
   </widget>
   </item>
   <item row="4" column="0">
   <spacer name="verticalSpacer_1">
   <property name="orientation">
   <enum>Qt::Vertical</enum>
   </property>
   <property name="sizeHint" stdset="0">
   <size>
   <width>20</width>
   <height>40</height>
  </size>
  </property>
  </spacer>
  </item>
  </layout>
 </item>
</layout>
</widget>
</widget>
<tabstops>
<tabstop>browserIntegrationHideEntriesComboBox</tabstop>
<tabstop>browserIntegrationSkipAutoSubmitComboBox</tabstop>
<tabstop>browserIntegrationOnlyHttpAuthComboBox</tabstop>
<tabstop>browserIntegrationNotHttpAuthComboBox</tabstop>
</tabstops>
<resources/>
<connections/>
</ui>