Copy history when drag/drop entries and groups

* Fix #5809
This commit is contained in:
Jonathan White 2020-12-12 09:46:23 -05:00
parent 4b5248ee98
commit a74e2391e8
6 changed files with 36 additions and 19 deletions

View File

@ -36,8 +36,6 @@ const int Entry::ResolveMaximumDepth = 10;
const QString Entry::AutoTypeSequenceUsername = "{USERNAME}{ENTER}"; const QString Entry::AutoTypeSequenceUsername = "{USERNAME}{ENTER}";
const QString Entry::AutoTypeSequencePassword = "{PASSWORD}{ENTER}"; const QString Entry::AutoTypeSequencePassword = "{PASSWORD}{ENTER}";
Entry::CloneFlags Entry::DefaultCloneFlags = Entry::CloneNewUuid | Entry::CloneResetTimeInfo;
Entry::Entry() Entry::Entry()
: m_attributes(new EntryAttributes(this)) : m_attributes(new EntryAttributes(this))
, m_attachments(new EntryAttachments(this)) , m_attachments(new EntryAttachments(this))

View File

@ -160,6 +160,8 @@ public:
CloneNewUuid = 1, // generate a random uuid for the clone CloneNewUuid = 1, // generate a random uuid for the clone
CloneResetTimeInfo = 2, // set all TimeInfo attributes to the current time CloneResetTimeInfo = 2, // set all TimeInfo attributes to the current time
CloneIncludeHistory = 4, // clone the history items CloneIncludeHistory = 4, // clone the history items
CloneDefault = CloneNewUuid | CloneResetTimeInfo,
CloneCopy = CloneNewUuid | CloneResetTimeInfo | CloneIncludeHistory,
CloneRenameTitle = 8, // add "-Clone" after the original title CloneRenameTitle = 8, // add "-Clone" after the original title
CloneUserAsRef = 16, // Add the user as a reference to the original entry CloneUserAsRef = 16, // Add the user as a reference to the original entry
ClonePassAsRef = 32, // Add the password as a reference to the original entry ClonePassAsRef = 32, // Add the password as a reference to the original entry
@ -209,7 +211,6 @@ public:
static const int ResolveMaximumDepth; static const int ResolveMaximumDepth;
static const QString AutoTypeSequenceUsername; static const QString AutoTypeSequenceUsername;
static const QString AutoTypeSequencePassword; static const QString AutoTypeSequencePassword;
static CloneFlags DefaultCloneFlags;
/** /**
* Creates a duplicate of this entry except that the returned entry isn't * Creates a duplicate of this entry except that the returned entry isn't
@ -217,7 +218,7 @@ public:
* Note that you need to copy the custom icons manually when inserting the * Note that you need to copy the custom icons manually when inserting the
* new entry into another database. * new entry into another database.
*/ */
Entry* clone(CloneFlags flags = DefaultCloneFlags) const; Entry* clone(CloneFlags flags = CloneDefault) const;
void copyDataFrom(const Entry* other); void copyDataFrom(const Entry* other);
QString maskPasswordPlaceholders(const QString& str) const; QString maskPasswordPlaceholders(const QString& str) const;
Entry* resolveReference(const QString& str) const; Entry* resolveReference(const QString& str) const;

View File

@ -36,9 +36,6 @@ const int Group::DefaultIconNumber = 48;
const int Group::RecycleBinIconNumber = 43; const int Group::RecycleBinIconNumber = 43;
const QString Group::RootAutoTypeSequence = "{USERNAME}{TAB}{PASSWORD}{ENTER}"; const QString Group::RootAutoTypeSequence = "{USERNAME}{TAB}{PASSWORD}{ENTER}";
Group::CloneFlags Group::DefaultCloneFlags =
Group::CloneNewUuid | Group::CloneResetTimeInfo | Group::CloneIncludeEntries;
Group::Group() Group::Group()
: m_customData(new CustomData(this)) : m_customData(new CustomData(this))
, m_updateTimeinfo(true) , m_updateTimeinfo(true)

View File

@ -56,6 +56,7 @@ public:
CloneNewUuid = 1, // generate a random uuid for the clone CloneNewUuid = 1, // generate a random uuid for the clone
CloneResetTimeInfo = 2, // set all TimeInfo attributes to the current time CloneResetTimeInfo = 2, // set all TimeInfo attributes to the current time
CloneIncludeEntries = 4, // clone the group entries CloneIncludeEntries = 4, // clone the group entries
CloneDefault = CloneNewUuid | CloneResetTimeInfo | CloneIncludeEntries,
}; };
Q_DECLARE_FLAGS(CloneFlags, CloneFlag) Q_DECLARE_FLAGS(CloneFlags, CloneFlag)
@ -108,7 +109,6 @@ public:
static const int DefaultIconNumber; static const int DefaultIconNumber;
static const int RecycleBinIconNumber; static const int RecycleBinIconNumber;
static CloneFlags DefaultCloneFlags;
static const QString RootAutoTypeSequence; static const QString RootAutoTypeSequence;
Group* findChildByName(const QString& name); Group* findChildByName(const QString& name);
@ -157,8 +157,8 @@ public:
QSet<QUuid> customIconsRecursive() const; QSet<QUuid> customIconsRecursive() const;
QList<QString> usernamesRecursive(int topN = -1) const; QList<QString> usernamesRecursive(int topN = -1) const;
Group* clone(Entry::CloneFlags entryFlags = Entry::DefaultCloneFlags, Group* clone(Entry::CloneFlags entryFlags = Entry::CloneDefault,
CloneFlags groupFlags = DefaultCloneFlags) const; Group::CloneFlags groupFlags = Group::CloneDefault) const;
void copyDataFrom(const Group* other); void copyDataFrom(const Group* other);
QString print(bool recursive = false, bool flatten = false, int depth = 0); QString print(bool recursive = false, bool flatten = false, int depth = 0);

View File

@ -262,13 +262,13 @@ bool GroupModel::dropMimeData(const QMimeData* data,
targetDb->metadata()->copyCustomIcons(customIcons, sourceDb->metadata()); targetDb->metadata()->copyCustomIcons(customIcons, sourceDb->metadata());
// Always clone the group across db's to reset UUIDs // Always clone the group across db's to reset UUIDs
group = dragGroup->clone(); group = dragGroup->clone(Entry::CloneDefault | Entry::CloneIncludeHistory);
if (action == Qt::MoveAction) { if (action == Qt::MoveAction) {
// Remove the original group from the sourceDb // Remove the original group from the sourceDb
delete dragGroup; delete dragGroup;
} }
} else if (action == Qt::CopyAction) { } else if (action == Qt::CopyAction) {
group = dragGroup->clone(); group = dragGroup->clone(Entry::CloneCopy);
} }
group->setParent(parentGroup, row); group->setParent(parentGroup, row);
@ -303,13 +303,13 @@ bool GroupModel::dropMimeData(const QMimeData* data,
targetDb->metadata()->addCustomIcon(customIcon, sourceDb->metadata()->customIcon(customIcon)); targetDb->metadata()->addCustomIcon(customIcon, sourceDb->metadata()->customIcon(customIcon));
} }
// Always clone the entry across db's to reset the UUID // Reset the UUID when moving across db boundary
entry = dragEntry->clone(); entry = dragEntry->clone(Entry::CloneDefault | Entry::CloneIncludeHistory);
if (action == Qt::MoveAction) { if (action == Qt::MoveAction) {
delete dragEntry; delete dragEntry;
} }
} else if (action == Qt::CopyAction) { } else if (action == Qt::CopyAction) {
entry = dragEntry->clone(); entry = dragEntry->clone(Entry::CloneCopy);
} }
entry->setGroup(parentGroup); entry->setGroup(parentGroup);

View File

@ -1155,24 +1155,45 @@ void TestGui::testEntryPlaceholders()
void TestGui::testDragAndDropEntry() void TestGui::testDragAndDropEntry()
{ {
auto* entryView = m_dbWidget->findChild<EntryView*>("entryView"); auto entryView = m_dbWidget->findChild<EntryView*>("entryView");
auto* groupView = m_dbWidget->findChild<GroupView*>("groupView"); auto groupView = m_dbWidget->findChild<GroupView*>("groupView");
QAbstractItemModel* groupModel = groupView->model(); auto groupModel = qobject_cast<GroupModel*>(groupView->model());
QModelIndex sourceIndex = entryView->model()->index(0, 1); QModelIndex sourceIndex = entryView->model()->index(0, 1);
QModelIndex targetIndex = groupModel->index(0, 0, groupModel->index(0, 0)); QModelIndex targetIndex = groupModel->index(0, 0, groupModel->index(0, 0));
QVERIFY(sourceIndex.isValid()); QVERIFY(sourceIndex.isValid());
QVERIFY(targetIndex.isValid()); QVERIFY(targetIndex.isValid());
auto targetGroup = groupModel->groupFromIndex(targetIndex);
QMimeData mimeData; QMimeData mimeData;
QByteArray encoded; QByteArray encoded;
QDataStream stream(&encoded, QIODevice::WriteOnly); QDataStream stream(&encoded, QIODevice::WriteOnly);
Entry* entry = entryView->entryFromIndex(sourceIndex);
auto entry = entryView->entryFromIndex(sourceIndex);
stream << entry->group()->database()->uuid() << entry->uuid(); stream << entry->group()->database()->uuid() << entry->uuid();
mimeData.setData("application/x-keepassx-entry", encoded); mimeData.setData("application/x-keepassx-entry", encoded);
// Test Copy, UUID should change, history remain
QVERIFY(groupModel->dropMimeData(&mimeData, Qt::CopyAction, -1, 0, targetIndex));
// Find the copied entry
auto newEntry = targetGroup->findEntryByPath(entry->title());
QVERIFY(newEntry);
QVERIFY(entry->uuid() != newEntry->uuid());
QCOMPARE(entry->historyItems().count(), newEntry->historyItems().count());
encoded.clear();
entry = entryView->entryFromIndex(sourceIndex);
auto history = entry->historyItems().count();
auto uuid = entry->uuid();
stream << entry->group()->database()->uuid() << entry->uuid();
mimeData.setData("application/x-keepassx-entry", encoded);
// Test Move, entry pointer should remain the same
QCOMPARE(entry->group()->name(), QString("NewDatabase"));
QVERIFY(groupModel->dropMimeData(&mimeData, Qt::MoveAction, -1, 0, targetIndex)); QVERIFY(groupModel->dropMimeData(&mimeData, Qt::MoveAction, -1, 0, targetIndex));
QCOMPARE(entry->group()->name(), QString("General")); QCOMPARE(entry->group()->name(), QString("General"));
QCOMPARE(entry->uuid(), uuid);
QCOMPARE(entry->historyItems().count(), history);
} }
void TestGui::testDragAndDropGroup() void TestGui::testDragAndDropGroup()