Introduced missing CustomData on Group and Entry

Introduce missing CustomData-attributes of KDBX4 format to allow
storing of plugin data for groups and entries - adopt Metadata to use
the same storage mechanism
Add simple view for CustomData as part of EditWidgetProperties
Tracking of CustomData-Modification using SIGNAL-SLOT update-mechanism
This commit is contained in:
Christian Kieschnick 2018-02-06 16:37:06 +01:00 committed by Janek Bevendorff
parent 698b44f71c
commit 0b54710734
18 changed files with 500 additions and 118 deletions

View File

@ -42,6 +42,7 @@ set(keepassx_SOURCES
core/AutoTypeMatch.cpp
core/Config.cpp
core/CsvParser.cpp
core/CustomData.cpp
core/Database.cpp
core/DatabaseIcons.cpp
core/Entry.cpp

156
src/core/CustomData.cpp Normal file
View File

@ -0,0 +1,156 @@
/*
* Copyright (C) 2012 Felix Geyer <debfx@fobos.de>
* 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 "CustomData.h"
CustomData::CustomData(QObject* parent)
: QObject(parent)
{
}
QList<QString> CustomData::keys() const
{
return m_data.keys();
}
bool CustomData::hasKey(const QString& key) const
{
return m_data.contains(key);
}
QString CustomData::value(const QString& key) const
{
return m_data.value(key);
}
bool CustomData::contains(const QString& key) const
{
return m_data.contains(key);
}
bool CustomData::containsValue(const QString& value) const
{
return m_data.values().contains(value);
}
void CustomData::set(const QString& key, const QString& value)
{
bool emitModified = false;
bool addAttribute = !m_data.contains(key);
bool changeValue = !addAttribute && (m_data.value(key) != value);
if (addAttribute ) {
emit aboutToBeAdded(key);
}
if (addAttribute || changeValue) {
m_data.insert(key, value);
emitModified = true;
}
if (emitModified) {
emit modified();
}
if (addAttribute) {
emit added(key);
}
}
void CustomData::remove(const QString& key)
{
emit aboutToBeRemoved(key);
m_data.remove(key);
emit removed(key);
emit modified();
}
void CustomData::rename(const QString& oldKey, const QString& newKey)
{
if (!m_data.contains(oldKey)) {
Q_ASSERT(false);
return;
}
if (m_data.contains(newKey)) {
Q_ASSERT(false);
return;
}
QString data = value(oldKey);
emit aboutToRename(oldKey, newKey);
m_data.remove(oldKey);
m_data.insert(newKey, data);
emit modified();
emit renamed(oldKey, newKey);
}
void CustomData::copyDataFrom(const CustomData* other)
{
if (*this != *other) {
emit aboutToBeReset();
m_data = other->m_data;
emit reset();
emit modified();
}
}
bool CustomData::operator==(const CustomData& other) const
{
return (m_data == other.m_data);
}
bool CustomData::operator!=(const CustomData& other) const
{
return (m_data != other.m_data);
}
void CustomData::clear()
{
emit aboutToBeReset();
m_data.clear();
emit reset();
emit modified();
}
bool CustomData::isEmpty() const
{
return m_data.isEmpty();
}
int CustomData::dataSize()
{
int size = 0;
QHashIterator<QString, QString> i(m_data);
while (i.hasNext()) {
i.next();
size += i.key().toUtf8().size() + i.value().toUtf8().size();
}
return size;
}

64
src/core/CustomData.h Normal file
View File

@ -0,0 +1,64 @@
/*
* Copyright (C) 2012 Felix Geyer <debfx@fobos.de>
* 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_CUSTOMDATA_H
#define KEEPASSX_CUSTOMDATA_H
#include <QMap>
#include <QObject>
#include <QSet>
#include <QStringList>
class CustomData : public QObject
{
Q_OBJECT
public:
explicit CustomData(QObject* parent = nullptr);
QList<QString> keys() const;
bool hasKey(const QString& key) const;
QString value(const QString& key) const;
bool contains(const QString& key) const;
bool containsValue(const QString& value) const;
void set(const QString& key, const QString& value);
void remove(const QString& key);
void rename(const QString& oldKey, const QString& newKey);
void clear();
bool isEmpty() const;
int dataSize();
void copyDataFrom(const CustomData* other);
bool operator==(const CustomData& other) const;
bool operator!=(const CustomData& other) const;
signals:
void modified();
void aboutToBeAdded(const QString& key);
void added(const QString& key);
void aboutToBeRemoved(const QString& key);
void removed(const QString& key);
void aboutToRename(const QString& oldKey, const QString& newKey);
void renamed(const QString& oldKey, const QString& newKey);
void aboutToBeReset();
void reset();
private:
QHash<QString, QString> m_data;
};
#endif // KEEPASSX_CUSTOMDATA_H

View File

@ -37,6 +37,7 @@ Entry::Entry()
: m_attributes(new EntryAttributes(this))
, m_attachments(new EntryAttachments(this))
, m_autoTypeAssociations(new AutoTypeAssociations(this))
, m_customData(new CustomData(this))
, m_tmpHistoryItem(nullptr)
, m_modifiedSinceBegin(false)
, m_updateTimeinfo(true)
@ -52,6 +53,7 @@ Entry::Entry()
connect(m_attributes, SIGNAL(defaultKeyModified()), SLOT(emitDataChanged()));
connect(m_attachments, SIGNAL(modified()), this, SIGNAL(modified()));
connect(m_autoTypeAssociations, SIGNAL(modified()), SIGNAL(modified()));
connect(m_customData, SIGNAL(modified()), this, SIGNAL(modified()));
connect(this, SIGNAL(modified()), SLOT(updateTimeinfo()));
connect(this, SIGNAL(modified()), SLOT(updateModifiedSinceBegin()));
@ -340,6 +342,16 @@ const EntryAttachments* Entry::attachments() const
return m_attachments;
}
CustomData *Entry::customData()
{
return m_customData;
}
const CustomData *Entry::customData() const
{
return m_customData;
}
bool Entry::hasTotp() const
{
return m_attributes->hasKey("TOTP Seed") || m_attributes->hasKey("otp");
@ -606,6 +618,7 @@ void Entry::truncateHistory()
size += historyItem->attributes()->attributesSize();
size += historyItem->autoTypeAssociations()->associationsSize();
size += historyItem->attachments()->attachmentsSize();
size += customData()->dataSize();
const QStringList tags = historyItem->tags().split(delimiter, QString::SkipEmptyParts);
for (const QString& tag : tags) {
size += tag.toUtf8().size();
@ -632,6 +645,7 @@ Entry* Entry::clone(CloneFlags flags) const
entry->m_uuid = m_uuid;
}
entry->m_data = m_data;
entry->m_customData->copyDataFrom(m_customData);
entry->m_attributes->copyDataFrom(m_attributes);
entry->m_attachments->copyDataFrom(m_attachments);
@ -676,6 +690,7 @@ void Entry::copyDataFrom(const Entry* other)
{
setUpdateTimeinfo(false);
m_data = other->m_data;
m_customData->copyDataFrom(other->m_customData);
m_attributes->copyDataFrom(other->m_attributes);
m_attachments->copyDataFrom(other->m_attachments);
m_autoTypeAssociations->copyDataFrom(other->m_autoTypeAssociations);

View File

@ -28,6 +28,7 @@
#include <QUrl>
#include "core/AutoTypeAssociations.h"
#include "core/CustomData.h"
#include "core/EntryAttachments.h"
#include "core/EntryAttributes.h"
#include "core/TimeInfo.h"
@ -107,6 +108,8 @@ public:
const EntryAttributes* attributes() const;
EntryAttachments* attachments();
const EntryAttachments* attachments() const;
CustomData *customData();
const CustomData *customData() const;
static const int DefaultIconNumber;
static const int ResolveMaximumDepth;
@ -232,6 +235,8 @@ private:
EntryAttachments* const m_attachments;
AutoTypeAssociations* const m_autoTypeAssociations;
CustomData* const m_customData;
QList<Entry*> m_history;
Entry* m_tmpHistoryItem;
bool m_modifiedSinceBegin;

View File

@ -33,13 +33,17 @@ Entry::CloneFlags Group::DefaultEntryCloneFlags = static_cast<Entry::CloneFlags>
Entry::CloneNewUuid | Entry::CloneResetTimeInfo);
Group::Group()
: m_updateTimeinfo(true)
: m_customData(new CustomData(this))
, m_updateTimeinfo(true)
{
m_data.iconNumber = DefaultIconNumber;
m_data.isExpanded = true;
m_data.autoTypeEnabled = Inherit;
m_data.searchingEnabled = Inherit;
m_data.mergeMode = ModeInherit;
connect(m_customData, SIGNAL(modified()), this, SIGNAL(modified()));
connect(this, SIGNAL(modified()), SLOT(updateTimeinfo()));
}
Group::~Group()
@ -80,7 +84,6 @@ Group* Group::createRecycleBin()
template <class P, class V> inline bool Group::set(P& property, const V& value) {
if (property != value) {
property = value;
updateTimeinfo();
emit modified();
return true;
} else {
@ -251,6 +254,16 @@ bool Group::isExpired() const
return m_data.timeInfo.expires() && m_data.timeInfo.expiryTime() < QDateTime::currentDateTimeUtc();
}
CustomData *Group::customData()
{
return m_customData;
}
const CustomData *Group::customData() const
{
return m_customData;
}
void Group::setUuid(const Uuid& uuid)
{
set(m_uuid, uuid);
@ -275,8 +288,6 @@ void Group::setIcon(int iconNumber)
if (m_data.iconNumber != iconNumber || !m_data.customIcon.isNull()) {
m_data.iconNumber = iconNumber;
m_data.customIcon = Uuid();
updateTimeinfo();
emit modified();
emit dataChanged(this);
}
@ -289,8 +300,6 @@ void Group::setIcon(const Uuid& uuid)
if (m_data.customIcon != uuid) {
m_data.customIcon = uuid;
m_data.iconNumber = 0;
updateTimeinfo();
emit modified();
emit dataChanged(this);
}
@ -305,8 +314,8 @@ void Group::setExpanded(bool expanded)
{
if (m_data.isExpanded != expanded) {
m_data.isExpanded = expanded;
updateTimeinfo();
if (config()->get("IgnoreGroupExpansion").toBool()) {
updateTimeinfo();
return;
}
emit modified();
@ -337,7 +346,6 @@ void Group::setExpires(bool value)
{
if (m_data.timeInfo.expires() != value) {
m_data.timeInfo.setExpires(value);
updateTimeinfo();
emit modified();
}
}
@ -346,7 +354,6 @@ void Group::setExpiryTime(const QDateTime& dateTime)
{
if (m_data.timeInfo.expiryTime() != dateTime) {
m_data.timeInfo.setExpiryTime(dateTime);
updateTimeinfo();
emit modified();
}
}
@ -768,6 +775,7 @@ Group* Group::clone(Entry::CloneFlags entryFlags, Group::CloneFlags groupFlags)
}
clonedGroup->m_data = m_data;
clonedGroup->m_customData->copyDataFrom(m_customData);
if (groupFlags & Group::CloneIncludeEntries) {
const QList<Entry*> entryList = entries();
@ -799,6 +807,7 @@ Group* Group::clone(Entry::CloneFlags entryFlags, Group::CloneFlags groupFlags)
void Group::copyDataFrom(const Group* other)
{
m_data = other->m_data;
m_customData->copyDataFrom(other->m_customData);
m_lastTopVisibleEntry = other->m_lastTopVisibleEntry;
}

View File

@ -26,6 +26,7 @@
#include "core/Database.h"
#include "core/Entry.h"
#include "core/CustomData.h"
#include "core/TimeInfo.h"
#include "core/Uuid.h"
@ -83,6 +84,8 @@ public:
bool resolveAutoTypeEnabled() const;
Entry* lastTopVisibleEntry() const;
bool isExpired() const;
CustomData *customData();
const CustomData *customData() const;
static const int DefaultIconNumber;
static const int RecycleBinIconNumber;
@ -164,6 +167,9 @@ signals:
void modified();
private slots:
void updateTimeinfo();
private:
template <class P, class V> bool set(P& property, const V& value);
@ -177,7 +183,6 @@ private:
void recSetDatabase(Database* db);
void cleanupParent();
void recCreateDelObjects();
void updateTimeinfo();
QPointer<Database> m_db;
Uuid m_uuid;
@ -186,6 +191,8 @@ private:
QList<Group*> m_children;
QList<Entry*> m_entries;
CustomData *const m_customData;
QPointer<Group> m_parent;
bool m_updateTimeinfo;

View File

@ -27,6 +27,7 @@ const int Metadata::DefaultHistoryMaxSize = 6 * 1024 * 1024;
Metadata::Metadata(QObject* parent)
: QObject(parent)
, m_customData(new CustomData(this))
, m_updateDatetime(true)
{
m_data.generator = "KeePassXC";
@ -50,6 +51,8 @@ Metadata::Metadata(QObject* parent)
m_entryTemplatesGroupChanged = now;
m_masterKeyChanged = now;
m_settingsChanged = now;
connect(m_customData, SIGNAL(modified()), this, SIGNAL(modified()));
}
template <class P, class V> bool Metadata::set(P& property, const V& value)
@ -291,9 +294,14 @@ int Metadata::historyMaxSize() const
return m_data.historyMaxSize;
}
QHash<QString, QString> Metadata::customFields() const
CustomData *Metadata::customData()
{
return m_customFields;
return m_customData;
}
const CustomData *Metadata::customData() const
{
return m_customData;
}
void Metadata::setGenerator(const QString& value)
@ -511,27 +519,13 @@ void Metadata::setHistoryMaxSize(int value)
set(m_data.historyMaxSize, value);
}
void Metadata::addCustomField(const QString& key, const QString& value)
QDateTime Metadata::settingsChanged() const
{
Q_ASSERT(!m_customFields.contains(key));
m_customFields.insert(key, value);
emit modified();
}
void Metadata::removeCustomField(const QString& key)
{
Q_ASSERT(m_customFields.contains(key));
m_customFields.remove(key);
emit modified();
}
QDateTime Metadata::settingsChanged() const {
return m_settingsChanged;
}
void Metadata::setSettingsChanged(const QDateTime& value) {
void Metadata::setSettingsChanged(const QDateTime& value)
{
Q_ASSERT(value.timeSpec() == Qt::UTC);
m_settingsChanged = value;
}

View File

@ -27,6 +27,7 @@
#include <QPointer>
#include "core/Uuid.h"
#include "core/CustomData.h"
class Database;
class Group;
@ -97,7 +98,8 @@ public:
int masterKeyChangeForce() const;
int historyMaxItems() const;
int historyMaxSize() const;
QHash<QString, QString> customFields() const;
CustomData *customData();
const CustomData *customData() const;
static const int DefaultHistoryMaxItems;
static const int DefaultHistoryMaxSize;
@ -134,8 +136,6 @@ public:
void setMasterKeyChangeForce(int value);
void setHistoryMaxItems(int value);
void setHistoryMaxSize(int value);
void addCustomField(const QString& key, const QString& value);
void removeCustomField(const QString& key);
void setUpdateDatetime(bool value);
/*
* Copy all attributes from other except:
@ -175,7 +175,7 @@ private:
QDateTime m_masterKeyChanged;
QDateTime m_settingsChanged;
QHash<QString, QString> m_customFields;
CustomData *const m_customData;
bool m_updateDatetime;
};

View File

@ -295,7 +295,7 @@ void KdbxXmlReader::parseMeta()
} else if (m_xml.name() == "Binaries") {
parseBinaries();
} else if (m_xml.name() == "CustomData") {
parseCustomData();
parseCustomData(m_meta->customData());
} else if (m_xml.name() == "SettingsChanged") {
m_meta->setSettingsChanged(readDateTime());
} else {
@ -397,20 +397,20 @@ void KdbxXmlReader::parseBinaries()
}
}
void KdbxXmlReader::parseCustomData()
void KdbxXmlReader::parseCustomData(CustomData *customData)
{
Q_ASSERT(m_xml.isStartElement() && m_xml.name() == "CustomData");
while (!m_xml.hasError() && m_xml.readNextStartElement()) {
if (m_xml.name() == "Item") {
parseCustomDataItem();
parseCustomDataItem(customData);
continue;
}
skipCurrentElement();
}
}
void KdbxXmlReader::parseCustomDataItem()
void KdbxXmlReader::parseCustomDataItem(CustomData *customData)
{
Q_ASSERT(m_xml.isStartElement() && m_xml.name() == "Item");
@ -432,7 +432,7 @@ void KdbxXmlReader::parseCustomDataItem()
}
if (keySet && valueSet) {
m_meta->addCustomField(key, value);
customData->set(key, value);
return;
}
@ -583,6 +583,10 @@ Group* KdbxXmlReader::parseGroup()
}
continue;
}
if (m_xml.name() == "CustomData") {
parseCustomData(group->customData());
continue;
}
skipCurrentElement();
}
@ -744,7 +748,10 @@ Entry* KdbxXmlReader::parseEntry(bool history)
}
continue;
}
if (m_xml.name() == "CustomData" ){
parseCustomData(entry->customData());
continue;
}
skipCurrentElement();
}

View File

@ -66,8 +66,8 @@ protected:
virtual void parseCustomIcons();
virtual void parseIcon();
virtual void parseBinaries();
virtual void parseCustomData();
virtual void parseCustomDataItem();
virtual void parseCustomData(CustomData *customData);
virtual void parseCustomDataItem(CustomData *customData);
virtual bool parseRoot();
virtual Group* parseGroup();
virtual void parseDeletedObjects();

View File

@ -129,7 +129,7 @@ void KdbxXmlWriter::writeMetadata()
if (m_kdbxVersion < KeePass2::FILE_VERSION_4) {
writeBinaries();
}
writeCustomData();
writeCustomData(m_meta->customData());
m_xml.writeEndElement();
}
@ -218,14 +218,13 @@ void KdbxXmlWriter::writeBinaries()
m_xml.writeEndElement();
}
void KdbxXmlWriter::writeCustomData()
void KdbxXmlWriter::writeCustomData(const CustomData *customData)
{
m_xml.writeStartElement("CustomData");
QHash<QString, QString> customFields = m_meta->customFields();
const QList<QString> keyList = customFields.keys();
const QList<QString> keyList = customData->keys();
for (const QString& key : keyList) {
writeCustomDataItem(key, customFields.value(key));
writeCustomDataItem(key, customData->value(key));
}
m_xml.writeEndElement();
@ -277,6 +276,10 @@ void KdbxXmlWriter::writeGroup(const Group* group)
writeUuid("LastTopVisibleEntry", group->lastTopVisibleEntry());
if (!group->customData()->isEmpty()){
writeCustomData(group->customData());
}
const QList<Entry*>& entryList = group->entries();
for (const Entry* entry : entryList) {
writeEntry(entry);
@ -401,6 +404,11 @@ void KdbxXmlWriter::writeEntry(const Entry* entry)
}
writeAutoType(entry);
if (!entry->customData()->isEmpty()){
writeCustomData(entry->customData());
}
// write history only for entries that are not history items
if (entry->parent()) {
writeEntryHistory(entry);

View File

@ -51,7 +51,7 @@ private:
void writeCustomIcons();
void writeIcon(const Uuid& uuid, const QImage& icon);
void writeBinaries();
void writeCustomData();
void writeCustomData(const CustomData *customData);
void writeCustomDataItem(const QString& key, const QString& value);
void writeRoot();
void writeGroup(const Group* group);

View File

@ -18,20 +18,27 @@
#include "EditWidgetProperties.h"
#include "ui_EditWidgetProperties.h"
#include <QStandardItemModel>
EditWidgetProperties::EditWidgetProperties(QWidget* parent)
: QWidget(parent)
, m_ui(new Ui::EditWidgetProperties())
, m_customData(new CustomData(this))
, m_customDataModel(new QStandardItemModel(this))
{
m_ui->setupUi(this);
m_ui->customDataTable->setModel(m_customDataModel);
this->connect( m_ui->removeCustomDataButton, SIGNAL(clicked()), SLOT(removeSelectedPluginData()));
}
EditWidgetProperties::~EditWidgetProperties()
{
}
void EditWidgetProperties::setFields(TimeInfo timeInfo, Uuid uuid)
void EditWidgetProperties::setFields(const TimeInfo &timeInfo, const Uuid &uuid)
{
QString timeFormat("d MMM yyyy HH:mm:ss");
static const QString timeFormat("d MMM yyyy HH:mm:ss");
m_ui->modifiedEdit->setText(
timeInfo.lastModificationTime().toLocalTime().toString(timeFormat));
m_ui->createdEdit->setText(
@ -40,3 +47,36 @@ void EditWidgetProperties::setFields(TimeInfo timeInfo, Uuid uuid)
timeInfo.lastAccessTime().toLocalTime().toString(timeFormat));
m_ui->uuidEdit->setText(uuid.toHex());
}
void EditWidgetProperties::setCustomData(const CustomData *customData)
{
Q_ASSERT(customData != nullptr);
m_customData->copyDataFrom(customData);
this->updateModel();
}
const CustomData *EditWidgetProperties::customData() const
{
return m_customData;
}
void EditWidgetProperties::removeSelectedPluginData()
{
const QItemSelectionModel *pSelectionModel = m_ui->customDataTable->selectionModel();
if (pSelectionModel){
for( const QModelIndex& index : pSelectionModel->selectedRows(0) ){
const QString key = index.data().toString();
m_customData->remove(key);
}
this->updateModel();
}
}
void EditWidgetProperties::updateModel()
{
m_customDataModel->clear();
for( const QString& key : m_customData->keys() ){
m_customDataModel->appendRow(QList<QStandardItem*>() << new QStandardItem( key ) << new QStandardItem( m_customData->value( key ) ));
}
}

View File

@ -18,8 +18,11 @@
#ifndef KEEPASSX_EDITWIDGETPROPERTIES_H
#define KEEPASSX_EDITWIDGETPROPERTIES_H
#include <QStandardItemModel>
#include <QPointer>
#include <QWidget>
#include "core/CustomData.h"
#include "core/TimeInfo.h"
#include "core/Uuid.h"
@ -35,11 +38,21 @@ public:
explicit EditWidgetProperties(QWidget* parent = nullptr);
~EditWidgetProperties();
void setFields(TimeInfo timeInfo, Uuid uuid);
void setFields(const TimeInfo &timeInfo, const Uuid &uuid);
void setCustomData(const CustomData *customData);
const CustomData *customData() const;
private slots:
void removeSelectedPluginData();
private:
void updateModel();
const QScopedPointer<Ui::EditWidgetProperties> m_ui;
QPointer<CustomData> m_customData;
QPointer<QStandardItemModel> m_customDataModel;
Q_DISABLE_COPY(EditWidgetProperties)
};

View File

@ -10,9 +10,12 @@
<height>328</height>
</rect>
</property>
<layout class="QFormLayout" name="formLayout">
<property name="fieldGrowthPolicy">
<enum>QFormLayout::ExpandingFieldsGrow</enum>
<layout class="QVBoxLayout" name="verticalLayout" stretch="0,0">
<property name="spacing">
<number>5</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
@ -23,72 +26,128 @@
<property name="bottomMargin">
<number>0</number>
</property>
<item row="1" column="1">
<widget class="QLineEdit" name="createdEdit">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
<item>
<layout class="QFormLayout" name="formLayout_2">
<item row="1" column="0">
<widget class="QLabel" name="labelCreated">
<property name="text">
<string>Created:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="createdEdit">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="labelModfied">
<property name="text">
<string>Modified:</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLineEdit" name="modifiedEdit">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="labelAccessed">
<property name="text">
<string>Accessed:</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QLineEdit" name="accessedEdit">
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="labelUuid">
<property name="text">
<string>Uuid:</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QLineEdit" name="uuidEdit">
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
<item row="2" column="1">
<widget class="QLineEdit" name="modifiedEdit">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Created:</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Modified:</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Accessed:</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QLineEdit" name="accessedEdit">
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Uuid:</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QLineEdit" name="uuidEdit">
<property name="readOnly">
<bool>true</bool>
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Plugin Data</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QTableView" name="customDataTable">
<property name="editTriggers">
<set>QAbstractItemView::NoEditTriggers</set>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum>
</property>
<attribute name="horizontalHeaderVisible">
<bool>false</bool>
</attribute>
<attribute name="horizontalHeaderStretchLastSection">
<bool>true</bool>
</attribute>
<attribute name="verticalHeaderVisible">
<bool>false</bool>
</attribute>
</widget>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QPushButton" name="removeCustomDataButton">
<property name="text">
<string>Remove</string>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<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>
</item>
</layout>

View File

@ -666,6 +666,7 @@ void EditEntryWidget::setForms(const Entry* entry, bool restore)
#endif
m_editWidgetProperties->setFields(entry->timeInfo(), entry->uuid());
m_editWidgetProperties->setCustomData(entry->customData());
if (!m_history && !restore) {
m_historyModel->setEntries(entry->historyItems());
@ -771,7 +772,7 @@ void EditEntryWidget::updateEntryData(Entry* entry) const
{
entry->attributes()->copyCustomKeysFrom(m_entryAttributes);
entry->attachments()->copyDataFrom(m_advancedUi->attachmentsWidget->entryAttachments());
entry->customData()->copyDataFrom(m_editWidgetProperties->customData());
entry->setTitle(m_mainUi->titleEdit->text());
entry->setUsername(m_mainUi->usernameEdit->text());
entry->setUrl(m_mainUi->urlEdit->text());

View File

@ -95,6 +95,7 @@ void EditGroupWidget::loadGroup(Group* group, bool create, Database* database)
m_editGroupWidgetIcons->load(group->uuid(), database, iconStruct);
m_editWidgetProperties->setFields(group->timeInfo(), group->uuid());
m_editWidgetProperties->setCustomData(group->customData());
setCurrentPage(0);
@ -118,6 +119,8 @@ void EditGroupWidget::apply()
m_group->setSearchingEnabled(triStateFromIndex(m_mainUi->searchComboBox->currentIndex()));
m_group->setAutoTypeEnabled(triStateFromIndex(m_mainUi->autotypeComboBox->currentIndex()));
m_group->customData()->copyDataFrom(m_editWidgetProperties->customData());
if (m_mainUi->autoTypeSequenceInherit->isChecked()) {
m_group->setDefaultAutoTypeSequence(QString());
}