Create a history item when changing entries.

Closes #15
This commit is contained in:
Felix Geyer 2012-04-23 21:06:04 +02:00
parent 3df2ad35cb
commit e026f3d1eb
9 changed files with 224 additions and 74 deletions

View File

@ -25,10 +25,11 @@
Entry::Entry() Entry::Entry()
{ {
m_updateTimeinfo = true; m_updateTimeinfo = true;
m_tmpHistoryItem = 0;
m_iconNumber = 0; m_data.iconNumber = 0;
m_autoTypeEnabled = true; m_data.autoTypeEnabled = true;
m_autoTypeObfuscation = 0; m_data.autoTypeObfuscation = 0;
m_attributes = new EntryAttributes(this); m_attributes = new EntryAttributes(this);
connect(m_attributes, SIGNAL(modified()), this, SIGNAL(modified())); connect(m_attributes, SIGNAL(modified()), this, SIGNAL(modified()));
@ -37,6 +38,8 @@ Entry::Entry()
m_attachments = new EntryAttachments(this); m_attachments = new EntryAttachments(this);
connect(m_attachments, SIGNAL(modified()), this, SIGNAL(modified())); connect(m_attachments, SIGNAL(modified()), this, SIGNAL(modified()));
connect(this, SIGNAL(modified()), this, SLOT(updateTimeinfo())); connect(this, SIGNAL(modified()), this, SLOT(updateTimeinfo()));
connect(this, SIGNAL(modified()), SLOT(updateModifiedSinceBegin()));
} }
Entry::~Entry() Entry::~Entry()
@ -67,12 +70,11 @@ template <class T> bool Entry::set(T& property, const T& value)
void Entry::updateTimeinfo() void Entry::updateTimeinfo()
{ {
if (m_updateTimeinfo) { if (m_updateTimeinfo) {
m_timeInfo.setLastModificationTime(QDateTime::currentDateTimeUtc()); m_data.timeInfo.setLastModificationTime(QDateTime::currentDateTimeUtc());
m_timeInfo.setLastAccessTime(QDateTime::currentDateTimeUtc()); m_data.timeInfo.setLastAccessTime(QDateTime::currentDateTimeUtc());
} }
} }
void Entry::setUpdateTimeinfo(bool value) void Entry::setUpdateTimeinfo(bool value)
{ {
m_updateTimeinfo = value; m_updateTimeinfo = value;
@ -85,25 +87,25 @@ Uuid Entry::uuid() const
QImage Entry::icon() const QImage Entry::icon() const
{ {
if (m_customIcon.isNull()) { if (m_data.customIcon.isNull()) {
return databaseIcons()->icon(m_iconNumber); return databaseIcons()->icon(m_data.iconNumber);
} }
else { else {
// TODO check if database() is 0 // TODO check if database() is 0
return database()->metadata()->customIcon(m_customIcon); return database()->metadata()->customIcon(m_data.customIcon);
} }
} }
QPixmap Entry::iconPixmap() const QPixmap Entry::iconPixmap() const
{ {
if (m_customIcon.isNull()) { if (m_data.customIcon.isNull()) {
return databaseIcons()->iconPixmap(m_iconNumber); return databaseIcons()->iconPixmap(m_data.iconNumber);
} }
else { else {
QPixmap pixmap; QPixmap pixmap;
if (!QPixmapCache::find(m_pixmapCacheKey, &pixmap)) { if (!QPixmapCache::find(m_pixmapCacheKey, &pixmap)) {
// TODO check if database() is 0 // TODO check if database() is 0
pixmap = QPixmap::fromImage(database()->metadata()->customIcon(m_customIcon)); pixmap = QPixmap::fromImage(database()->metadata()->customIcon(m_data.customIcon));
*const_cast<QPixmapCache::Key*>(&m_pixmapCacheKey) = QPixmapCache::insert(pixmap); *const_cast<QPixmapCache::Key*>(&m_pixmapCacheKey) = QPixmapCache::insert(pixmap);
} }
@ -113,57 +115,57 @@ QPixmap Entry::iconPixmap() const
int Entry::iconNumber() const int Entry::iconNumber() const
{ {
return m_iconNumber; return m_data.iconNumber;
} }
Uuid Entry::iconUuid() const Uuid Entry::iconUuid() const
{ {
return m_customIcon; return m_data.customIcon;
} }
QColor Entry::foregroundColor() const QColor Entry::foregroundColor() const
{ {
return m_foregroundColor; return m_data.foregroundColor;
} }
QColor Entry::backgroundColor() const QColor Entry::backgroundColor() const
{ {
return m_backgroundColor; return m_data.backgroundColor;
} }
QString Entry::overrideUrl() const QString Entry::overrideUrl() const
{ {
return m_overrideUrl; return m_data.overrideUrl;
} }
QString Entry::tags() const QString Entry::tags() const
{ {
return m_tags; return m_data.tags;
} }
TimeInfo Entry::timeInfo() const TimeInfo Entry::timeInfo() const
{ {
return m_timeInfo; return m_data.timeInfo;
} }
bool Entry::autoTypeEnabled() const bool Entry::autoTypeEnabled() const
{ {
return m_autoTypeEnabled; return m_data.autoTypeEnabled;
} }
int Entry::autoTypeObfuscation() const int Entry::autoTypeObfuscation() const
{ {
return m_autoTypeObfuscation; return m_data.autoTypeObfuscation;
} }
QString Entry::defaultAutoTypeSequence() const QString Entry::defaultAutoTypeSequence() const
{ {
return m_defaultAutoTypeSequence; return m_data.defaultAutoTypeSequence;
} }
const QList<AutoTypeAssociation>& Entry::autoTypeAssociations() const const QList<AutoTypeAssociation>& Entry::autoTypeAssociations() const
{ {
return m_autoTypeAssociations; return m_data.autoTypeAssociations;
} }
QString Entry::title() const QString Entry::title() const
@ -221,9 +223,9 @@ void Entry::setIcon(int iconNumber)
{ {
Q_ASSERT(iconNumber >= 0); Q_ASSERT(iconNumber >= 0);
if (m_iconNumber != iconNumber || !m_customIcon.isNull()) { if (m_data.iconNumber != iconNumber || !m_data.customIcon.isNull()) {
m_iconNumber = iconNumber; m_data.iconNumber = iconNumber;
m_customIcon = Uuid(); m_data.customIcon = Uuid();
m_pixmapCacheKey = QPixmapCache::Key(); m_pixmapCacheKey = QPixmapCache::Key();
@ -235,9 +237,9 @@ void Entry::setIcon(const Uuid& uuid)
{ {
Q_ASSERT(!uuid.isNull()); Q_ASSERT(!uuid.isNull());
if (m_customIcon != uuid) { if (m_data.customIcon != uuid) {
m_customIcon = uuid; m_data.customIcon = uuid;
m_iconNumber = 0; m_data.iconNumber = 0;
m_pixmapCacheKey = QPixmapCache::Key(); m_pixmapCacheKey = QPixmapCache::Key();
@ -247,47 +249,47 @@ void Entry::setIcon(const Uuid& uuid)
void Entry::setForegroundColor(const QColor& color) void Entry::setForegroundColor(const QColor& color)
{ {
set(m_foregroundColor, color); set(m_data.foregroundColor, color);
} }
void Entry::setBackgroundColor(const QColor& color) void Entry::setBackgroundColor(const QColor& color)
{ {
set(m_backgroundColor, color); set(m_data.backgroundColor, color);
} }
void Entry::setOverrideUrl(const QString& url) void Entry::setOverrideUrl(const QString& url)
{ {
set(m_overrideUrl, url); set(m_data.overrideUrl, url);
} }
void Entry::setTags(const QString& tags) void Entry::setTags(const QString& tags)
{ {
set(m_tags, tags); set(m_data.tags, tags);
} }
void Entry::setTimeInfo(const TimeInfo& timeInfo) void Entry::setTimeInfo(const TimeInfo& timeInfo)
{ {
m_timeInfo = timeInfo; m_data.timeInfo = timeInfo;
} }
void Entry::setAutoTypeEnabled(bool enable) void Entry::setAutoTypeEnabled(bool enable)
{ {
set(m_autoTypeEnabled, enable); set(m_data.autoTypeEnabled, enable);
} }
void Entry::setAutoTypeObfuscation(int obfuscation) void Entry::setAutoTypeObfuscation(int obfuscation)
{ {
set(m_autoTypeObfuscation, obfuscation); set(m_data.autoTypeObfuscation, obfuscation);
} }
void Entry::setDefaultAutoTypeSequence(const QString& sequence) void Entry::setDefaultAutoTypeSequence(const QString& sequence)
{ {
set(m_defaultAutoTypeSequence, sequence); set(m_data.defaultAutoTypeSequence, sequence);
} }
void Entry::addAutoTypeAssociation(const AutoTypeAssociation& assoc) void Entry::addAutoTypeAssociation(const AutoTypeAssociation& assoc)
{ {
m_autoTypeAssociations << assoc; m_data.autoTypeAssociations << assoc;
Q_EMIT modified(); Q_EMIT modified();
} }
@ -318,16 +320,16 @@ void Entry::setNotes(const QString& notes)
void Entry::setExpires(const bool& value) void Entry::setExpires(const bool& value)
{ {
if (m_timeInfo.expires() != value) { if (m_data.timeInfo.expires() != value) {
m_timeInfo.setExpires(value); m_data.timeInfo.setExpires(value);
Q_EMIT modified(); Q_EMIT modified();
} }
} }
void Entry::setExpiryTime(const QDateTime& dateTime) void Entry::setExpiryTime(const QDateTime& dateTime)
{ {
if (m_timeInfo.expiryTime() != dateTime) { if (m_data.timeInfo.expiryTime() != dateTime) {
m_timeInfo.setExpiryTime(dateTime); m_data.timeInfo.setExpiryTime(dateTime);
Q_EMIT modified(); Q_EMIT modified();
} }
} }
@ -345,11 +347,46 @@ const QList<Entry*>& Entry::historyItems() const
void Entry::addHistoryItem(Entry* entry) void Entry::addHistoryItem(Entry* entry)
{ {
Q_ASSERT(!entry->parent()); Q_ASSERT(!entry->parent());
Q_ASSERT(entry->uuid() == uuid());
m_history.append(entry); m_history.append(entry);
Q_EMIT modified(); Q_EMIT modified();
} }
void Entry::beginUpdate()
{
Q_ASSERT(!m_tmpHistoryItem);
m_tmpHistoryItem = new Entry();
m_tmpHistoryItem->setUpdateTimeinfo(false);
m_tmpHistoryItem->m_uuid = m_uuid;
m_tmpHistoryItem->m_data = m_data;
*m_tmpHistoryItem->m_attributes = *m_attributes;
*m_tmpHistoryItem->m_attachments = *m_attachments;
m_modifiedSinceBegin = false;
}
void Entry::endUpdate()
{
Q_ASSERT(m_tmpHistoryItem);
if (m_modifiedSinceBegin) {
m_tmpHistoryItem->setUpdateTimeinfo(true);
addHistoryItem(m_tmpHistoryItem);
}
else {
delete m_tmpHistoryItem;
}
m_tmpHistoryItem = 0;
}
void Entry::updateModifiedSinceBegin()
{
m_modifiedSinceBegin = true;
}
Group* Entry::group() Group* Entry::group()
{ {
return m_group; return m_group;
@ -373,7 +410,7 @@ void Entry::setGroup(Group* group)
QObject::setParent(group); QObject::setParent(group);
if (m_updateTimeinfo) { if (m_updateTimeinfo) {
m_timeInfo.setLocationChanged(QDateTime::currentDateTimeUtc()); m_data.timeInfo.setLocationChanged(QDateTime::currentDateTimeUtc());
} }
} }

View File

@ -43,6 +43,21 @@ struct AutoTypeAssociation
Q_DECLARE_TYPEINFO(AutoTypeAssociation, Q_MOVABLE_TYPE); Q_DECLARE_TYPEINFO(AutoTypeAssociation, Q_MOVABLE_TYPE);
struct EntryData
{
int iconNumber;
Uuid customIcon;
QColor foregroundColor;
QColor backgroundColor;
QString overrideUrl;
QString tags;
bool autoTypeEnabled;
int autoTypeObfuscation;
QString defaultAutoTypeSequence;
QList<AutoTypeAssociation> autoTypeAssociations;
TimeInfo timeInfo;
};
class Entry : public QObject class Entry : public QObject
{ {
Q_OBJECT Q_OBJECT
@ -97,6 +112,12 @@ public:
QList<Entry*> historyItems(); QList<Entry*> historyItems();
const QList<Entry*>& historyItems() const; const QList<Entry*>& historyItems() const;
void addHistoryItem(Entry* entry); void addHistoryItem(Entry* entry);
/**
* Call before and after set*() methods to create a history item
* if the entry has been changed.
*/
void beginUpdate();
void endUpdate();
Group* group(); Group* group();
void setGroup(Group* group); void setGroup(Group* group);
@ -114,27 +135,20 @@ Q_SIGNALS:
private Q_SLOTS: private Q_SLOTS:
void emitDataChanged(); void emitDataChanged();
void updateTimeinfo(); void updateTimeinfo();
void updateModifiedSinceBegin();
private: private:
const Database* database() const; const Database* database() const;
template <class T> inline bool set(T& property, const T& value); template <class T> inline bool set(T& property, const T& value);
Uuid m_uuid; Uuid m_uuid;
int m_iconNumber; EntryData m_data;
Uuid m_customIcon;
QColor m_foregroundColor;
QColor m_backgroundColor;
QString m_overrideUrl;
QString m_tags;
TimeInfo m_timeInfo;
bool m_autoTypeEnabled;
int m_autoTypeObfuscation;
QString m_defaultAutoTypeSequence;
QList<AutoTypeAssociation> m_autoTypeAssociations;
EntryAttributes* m_attributes; EntryAttributes* m_attributes;
EntryAttachments* m_attachments; EntryAttachments* m_attachments;
QList<Entry*> m_history; QList<Entry*> m_history;
Entry* m_tmpHistoryItem;
bool m_modifiedSinceBegin;
QPointer<Group> m_group; QPointer<Group> m_group;
QPixmapCache::Key m_pixmapCacheKey; QPixmapCache::Key m_pixmapCacheKey;
bool m_updateTimeinfo; bool m_updateTimeinfo;

View File

@ -73,22 +73,6 @@ void EntryAttachments::remove(const QString& key)
Q_EMIT modified(); Q_EMIT modified();
} }
void EntryAttachments::copyFrom(const EntryAttachments* other)
{
if (*this != *other) {
Q_EMIT aboutToBeReset();
m_attachments.clear();
Q_FOREACH (const QString& key, other->keys()) {
m_attachments.insert(key, other->value(key));
}
Q_EMIT reset();
Q_EMIT modified();
}
}
void EntryAttachments::clear() void EntryAttachments::clear()
{ {
if (m_attachments.isEmpty()) { if (m_attachments.isEmpty()) {
@ -103,7 +87,26 @@ void EntryAttachments::clear()
Q_EMIT modified(); Q_EMIT modified();
} }
bool EntryAttachments::operator==(const EntryAttachments& other) const
{
return m_attachments == other.m_attachments;
}
bool EntryAttachments::operator!=(const EntryAttachments& other) const bool EntryAttachments::operator!=(const EntryAttachments& other) const
{ {
return m_attachments != other.m_attachments; return m_attachments != other.m_attachments;
} }
EntryAttachments& EntryAttachments::operator=(EntryAttachments& other)
{
if (*this != other) {
Q_EMIT aboutToBeReset();
m_attachments = other.m_attachments;
Q_EMIT reset();
Q_EMIT modified();
}
return *this;
}

View File

@ -31,9 +31,10 @@ public:
QByteArray value(const QString& key) const; QByteArray value(const QString& key) const;
void set(const QString& key, const QByteArray& value); void set(const QString& key, const QByteArray& value);
void remove(const QString& key); void remove(const QString& key);
void copyFrom(const EntryAttachments* other);
void clear(); void clear();
bool operator==(const EntryAttachments& other) const;
bool operator!=(const EntryAttachments& other) const; bool operator!=(const EntryAttachments& other) const;
EntryAttachments& operator=(EntryAttachments& other);
Q_SIGNALS: Q_SIGNALS:
void modified(); void modified();

View File

@ -149,6 +149,33 @@ bool EntryAttributes::areCustomKeysDifferent(const EntryAttributes* other)
return false; return false;
} }
EntryAttributes& EntryAttributes::operator=(const EntryAttributes& other)
{
if (*this != other) {
Q_EMIT aboutToBeReset();
m_attributes = other.m_attributes;
m_protectedAttributes = other.m_protectedAttributes;
Q_EMIT reset();
Q_EMIT modified();
}
return *this;
}
bool EntryAttributes::operator==(const EntryAttributes& other) const
{
return (m_attributes == other.m_attributes
&& m_protectedAttributes == other.m_protectedAttributes);
}
bool EntryAttributes::operator!=(const EntryAttributes& other) const
{
return (m_attributes != other.m_attributes
|| m_protectedAttributes != other.m_protectedAttributes);
}
void EntryAttributes::clear() void EntryAttributes::clear()
{ {
Q_EMIT aboutToBeReset(); Q_EMIT aboutToBeReset();

View File

@ -35,8 +35,11 @@ public:
void set(const QString& key, const QString& value, bool protect = false); void set(const QString& key, const QString& value, bool protect = false);
void remove(const QString& key); void remove(const QString& key);
void copyCustomKeysFrom(const EntryAttributes* other); void copyCustomKeysFrom(const EntryAttributes* other);
void clear();
bool areCustomKeysDifferent(const EntryAttributes* other); bool areCustomKeysDifferent(const EntryAttributes* other);
void clear();
EntryAttributes& operator=(const EntryAttributes& other);
bool operator==(const EntryAttributes& other) const;
bool operator!=(const EntryAttributes& other) const;
static const QStringList DEFAULT_ATTRIBUTES; static const QStringList DEFAULT_ATTRIBUTES;
static bool isDefaultAttribute(const QString& key); static bool isDefaultAttribute(const QString& key);

View File

@ -115,7 +115,7 @@ void EditEntryWidget::loadEntry(Entry* entry, bool create, const QString& groupN
m_entryAttributes->copyCustomKeysFrom(entry->attributes()); m_entryAttributes->copyCustomKeysFrom(entry->attributes());
m_attributesModel->setEntryAttributes(m_entryAttributes); m_attributesModel->setEntryAttributes(m_entryAttributes);
m_entryAttachments->copyFrom(entry->attachments()); *m_entryAttachments = *entry->attachments();
m_attachmentsModel->setEntryAttachments(m_entryAttachments); m_attachmentsModel->setEntryAttachments(m_entryAttachments);
m_ui->categoryList->setCurrentRow(0); m_ui->categoryList->setCurrentRow(0);
@ -127,6 +127,9 @@ void EditEntryWidget::saveEntry()
QMessageBox::warning(this, tr("Error"), tr("Different passwords supplied.")); QMessageBox::warning(this, tr("Error"), tr("Different passwords supplied."));
return; return;
} }
m_entry->beginUpdate();
m_entry->setTitle(m_mainUi->titleEdit->text()); m_entry->setTitle(m_mainUi->titleEdit->text());
m_entry->setUsername(m_mainUi->usernameEdit->text()); m_entry->setUsername(m_mainUi->usernameEdit->text());
m_entry->setUrl(m_mainUi->urlEdit->text()); m_entry->setUrl(m_mainUi->urlEdit->text());
@ -138,9 +141,11 @@ void EditEntryWidget::saveEntry()
m_entry->attributes()->copyCustomKeysFrom(m_entryAttributes); m_entry->attributes()->copyCustomKeysFrom(m_entryAttributes);
m_entryAttributes->clear(); m_entryAttributes->clear();
m_entry->attachments()->copyFrom(m_entryAttachments); *m_entry->attachments() = *m_entryAttachments;
m_entryAttachments->clear(); m_entryAttachments->clear();
m_entry->endUpdate();
m_entry = 0; m_entry = 0;
Q_EMIT editFinished(true); Q_EMIT editFinished(true);
} }

View File

@ -285,4 +285,63 @@ void TestModified::testEntrySets()
delete db; delete db;
} }
void TestModified::testHistoryItem()
{
Entry* entry = new Entry();
QDateTime created = entry->timeInfo().creationTime();
entry->setUuid(Uuid::random());
entry->setTitle("a");
entry->setTags("a");
EntryAttributes* attributes = new EntryAttributes();
attributes->copyCustomKeysFrom(entry->attributes());
Entry* historyEntry;
int historyItemsSize = 0;
entry->beginUpdate();
entry->setTitle("a");
entry->setTags("a");
entry->setOverrideUrl("");
entry->endUpdate();
QCOMPARE(entry->historyItems().size(), historyItemsSize);
QDateTime modified = entry->timeInfo().lastModificationTime();
QTest::qSleep(10);
entry->beginUpdate();
entry->setTitle("b");
entry->endUpdate();
QCOMPARE(entry->historyItems().size(), ++historyItemsSize);
historyEntry = entry->historyItems().at(historyItemsSize - 1);
QCOMPARE(historyEntry->title(), QString("a"));
QCOMPARE(historyEntry->uuid(), entry->uuid());
QCOMPARE(historyEntry->tags(), entry->tags());
QCOMPARE(historyEntry->overrideUrl(), entry->overrideUrl());
QCOMPARE(historyEntry->timeInfo().creationTime(), created);
QCOMPARE(historyEntry->timeInfo().lastModificationTime(), modified);
entry->beginUpdate();
entry->setTags("b");
entry->endUpdate();
QCOMPARE(entry->historyItems().size(), ++historyItemsSize);
QCOMPARE(entry->historyItems().at(historyItemsSize - 1)->tags(), QString("a"));
entry->beginUpdate();
entry->attachments()->set("test", QByteArray("value"));
entry->endUpdate();
QCOMPARE(entry->historyItems().size(), ++historyItemsSize);
QCOMPARE(entry->historyItems().at(historyItemsSize - 1)->attachments()->keys().size(), 0);
attributes->set("k", "myvalue");
entry->beginUpdate();
entry->attributes()->copyCustomKeysFrom(attributes);
entry->endUpdate();
QCOMPARE(entry->historyItems().size(), ++historyItemsSize);
QVERIFY(!entry->historyItems().at(historyItemsSize - 1)->attributes()->keys().contains("k"));
delete attributes;
delete entry;
}
KEEPASSX_QTEST_CORE_MAIN(TestModified) KEEPASSX_QTEST_CORE_MAIN(TestModified)

View File

@ -29,6 +29,7 @@ private Q_SLOTS:
void testSignals(); void testSignals();
void testGroupSets(); void testGroupSets();
void testEntrySets(); void testEntrySets();
void testHistoryItem();
}; };
#endif // KEEPASSX_TESTMODIFIED_H #endif // KEEPASSX_TESTMODIFIED_H