mirror of
https://github.com/keepassxreboot/keepassxc.git
synced 2025-01-13 16:30:29 -05:00
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:
parent
698b44f71c
commit
0b54710734
@ -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
156
src/core/CustomData.cpp
Normal 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
64
src/core/CustomData.h
Normal 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
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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 ) ));
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
};
|
||||
|
||||
|
@ -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>
|
||||
|
@ -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());
|
||||
|
@ -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());
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user