Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add drag & drop untracked cross-db move #10564

Open
wants to merge 16 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 14 additions & 8 deletions src/core/Entry.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -946,10 +946,15 @@ Entry* Entry::clone(CloneFlags flags) const

if (flags & CloneResetTimeInfo) {
QDateTime now = Clock::currentDateTimeUtc();
entry->m_data.timeInfo.setCreationTime(now);
entry->m_data.timeInfo.setLastModificationTime(now);
entry->m_data.timeInfo.setLastAccessTime(now);
entry->m_data.timeInfo.setLocationChanged(now);
if (flags & CloneResetCreationTime) {
entry->m_data.timeInfo.setCreationTime(now);
}
if (flags & CloneResetLastAccessTime) {
entry->m_data.timeInfo.setLastAccessTime(now);
}
if (flags & CloneResetLocationChangedTime) {
entry->m_data.timeInfo.setLocationChanged(now);
}
}

if (flags & CloneRenameTitle) {
Expand Down Expand Up @@ -1267,10 +1272,8 @@ void Entry::setGroup(Group* group, bool trackPrevious)
m_group->database()->addDeletedObject(m_uuid);

// copy custom icon to the new database
if (!iconUuid().isNull() && group->database() && m_group->database()->metadata()->hasCustomIcon(iconUuid())
&& !group->database()->metadata()->hasCustomIcon(iconUuid())) {
group->database()->metadata()->addCustomIcon(iconUuid(),
m_group->database()->metadata()->customIcon(iconUuid()));
if (group->database()) {
group->database()->metadata()->copyCustomIcon(iconUuid(), m_group->database()->metadata());
}
} else if (trackPrevious && m_group->database() && group != m_group) {
setPreviousParentGroup(m_group);
Expand Down Expand Up @@ -1487,7 +1490,10 @@ QUuid Entry::previousParentGroupUuid() const

void Entry::setPreviousParentGroupUuid(const QUuid& uuid)
{
bool prevUpdateTimeinfo = m_updateTimeinfo;
m_updateTimeinfo = false; // prevent update of LastModificationTime
set(m_data.previousParentGroupUuid, uuid);
m_updateTimeinfo = prevUpdateTimeinfo;
}

void Entry::setPreviousParentGroup(const Group* group)
Expand Down
17 changes: 11 additions & 6 deletions src/core/Entry.h
Original file line number Diff line number Diff line change
Expand Up @@ -175,13 +175,18 @@ class Entry : public ModifiableObject
{
CloneNoFlags = 0,
CloneNewUuid = 1, // generate a random uuid for the clone
CloneResetTimeInfo = 2, // set all TimeInfo attributes to the current time
CloneIncludeHistory = 4, // clone the history items
CloneResetCreationTime = 2, // set timeInfo.CreationTime to the current time
CloneResetLastAccessTime = 4, // set timeInfo.LastAccessTime to the current time
CloneResetLocationChangedTime = 8, // set timeInfo.LocationChangedTime to the current time
CloneIncludeHistory = 16, // clone the history items
CloneRenameTitle = 32, // add "-Clone" after the original title
CloneUserAsRef = 64, // Add the user as a reference to the original entry
ClonePassAsRef = 128, // Add the password as a reference to the original entry

CloneResetTimeInfo = CloneResetCreationTime | CloneResetLastAccessTime | CloneResetLocationChangedTime,
CloneExactCopy = CloneIncludeHistory,
CloneCopy = CloneExactCopy | CloneNewUuid | CloneResetTimeInfo,
CloneDefault = CloneNewUuid | CloneResetTimeInfo,
CloneCopy = CloneNewUuid | CloneResetTimeInfo | CloneIncludeHistory,
CloneRenameTitle = 8, // add "-Clone" after the original title
CloneUserAsRef = 16, // Add the user as a reference to the original entry
ClonePassAsRef = 32, // Add the password as a reference to the original entry
};
Q_DECLARE_FLAGS(CloneFlags, CloneFlag)

Expand Down
54 changes: 38 additions & 16 deletions src/core/Group.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -77,11 +77,11 @@ Group::~Group()
cleanupParent();
}

template <class P, class V> inline bool Group::set(P& property, const V& value)
template <class P, class V> inline bool Group::set(P& property, const V& value, bool preserveTimeinfo)
{
if (property != value) {
property = value;
emitModified();
emitModifiedEx(preserveTimeinfo);
return true;
} else {
return false;
Expand Down Expand Up @@ -440,6 +440,15 @@ const Group* Group::parentGroup() const
return m_parent;
}

void Group::emitModifiedEx(bool preserveTimeinfo) {
bool prevUpdateTimeinfo = m_updateTimeinfo;
if (preserveTimeinfo) {
m_updateTimeinfo = false; // prevent update of LastModificationTime
}
emitModified();
m_updateTimeinfo = prevUpdateTimeinfo;
}

void Group::setParent(Group* parent, int index, bool trackPrevious)
{
Q_ASSERT(parent);
Expand Down Expand Up @@ -469,9 +478,8 @@ void Group::setParent(Group* parent, int index, bool trackPrevious)
recCreateDelObjects();

// copy custom icon to the new database
if (!iconUuid().isNull() && parent->m_db && m_db->metadata()->hasCustomIcon(iconUuid())
&& !parent->m_db->metadata()->hasCustomIcon(iconUuid())) {
parent->m_db->metadata()->addCustomIcon(iconUuid(), m_db->metadata()->customIcon(iconUuid()));
if (parent->m_db) {
parent->m_db->metadata()->copyCustomIcon(iconUuid(), m_db->metadata());
}
}
if (m_db != parent->m_db) {
Expand All @@ -497,7 +505,7 @@ void Group::setParent(Group* parent, int index, bool trackPrevious)
m_data.timeInfo.setLocationChanged(Clock::currentDateTimeUtc());
}

emitModified();
emitModifiedEx(true);

if (!moveWithinDatabase) {
emit groupAdded();
Expand Down Expand Up @@ -550,6 +558,16 @@ bool Group::hasChildren() const
return !children().isEmpty();
}

bool Group::isDescendantOf(const Group* group) const
{
for(const Group* parent = m_parent; parent; parent = parent->m_parent) {
if (parent == group) {
return true;
}
}
return false;
}

Database* Group::database()
{
return m_db;
Expand Down Expand Up @@ -946,12 +964,16 @@ Group* Group::clone(Entry::CloneFlags entryFlags, Group::CloneFlags groupFlags)

clonedGroup->setUpdateTimeinfo(true);
if (groupFlags & Group::CloneResetTimeInfo) {

QDateTime now = Clock::currentDateTimeUtc();
clonedGroup->m_data.timeInfo.setCreationTime(now);
clonedGroup->m_data.timeInfo.setLastModificationTime(now);
clonedGroup->m_data.timeInfo.setLastAccessTime(now);
clonedGroup->m_data.timeInfo.setLocationChanged(now);
if (groupFlags & Group::CloneResetCreationTime) {
clonedGroup->m_data.timeInfo.setCreationTime(now);
}
if (groupFlags & Group::CloneResetLastAccessTime) {
clonedGroup->m_data.timeInfo.setLastAccessTime(now);
}
if (groupFlags & Group::CloneResetLocationChangedTime) {
clonedGroup->m_data.timeInfo.setLocationChanged(now);
}
}

if (groupFlags & Group::CloneRenameTitle) {
Expand Down Expand Up @@ -983,7 +1005,7 @@ void Group::addEntry(Entry* entry)
connect(entry, &Entry::modified, m_db, &Database::markAsModified);
}

emitModified();
emitModifiedEx(true);
emit entryAdded(entry);
}

Expand All @@ -1000,7 +1022,7 @@ void Group::removeEntry(Entry* entry)
entry->disconnect(m_db);
}
m_entries.removeAll(entry);
emitModified();
emitModifiedEx(true);
emit entryRemoved(entry);
}

Expand Down Expand Up @@ -1071,7 +1093,7 @@ void Group::cleanupParent()
if (m_parent) {
emit groupAboutToRemove(this);
m_parent->m_children.removeAll(this);
emitModified();
emitModifiedEx(true);
emit groupRemoved();
}
}
Expand Down Expand Up @@ -1222,7 +1244,7 @@ void Group::sortChildrenRecursively(bool reverse)
child->sortChildrenRecursively(reverse);
}

emitModified();
emitModifiedEx(true);
}

const Group* Group::previousParentGroup() const
Expand All @@ -1240,7 +1262,7 @@ QUuid Group::previousParentGroupUuid() const

void Group::setPreviousParentGroupUuid(const QUuid& uuid)
{
set(m_data.previousParentGroupUuid, uuid);
set(m_data.previousParentGroupUuid, uuid, true);
}

void Group::setPreviousParentGroup(const Group* group)
Expand Down
131 changes: 126 additions & 5 deletions src/core/Group.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,24 @@
#define KEEPASSX_GROUP_H

#include <QPointer>
#include <QList>
#include <utility>

#include "core/CustomData.h"
#include "core/Database.h"
#include "core/Entry.h"


class Entry;
class Group;


template <typename TCallable> concept CGroupVisitor = std::is_invocable_v<TCallable, Group*>;
template <typename TCallable> concept CGroupConstVisitor = std::is_invocable_v<TCallable, const Group*>;
template <typename TCallable> concept CEntryVisitor = std::is_invocable_v<TCallable, Entry*>;
template <typename TCallable> concept CEntryConstVisitor = std::is_invocable_v<TCallable, const Entry*>;


class Group : public ModifiableObject
{
Q_OBJECT
Expand All @@ -47,10 +60,16 @@ class Group : public ModifiableObject
{
CloneNoFlags = 0,
CloneNewUuid = 1, // generate a random uuid for the clone
CloneResetTimeInfo = 2, // set all TimeInfo attributes to the current time
CloneIncludeEntries = 4, // clone the group entries
CloneDefault = CloneNewUuid | CloneResetTimeInfo | CloneIncludeEntries,
CloneRenameTitle = 8, // add "- Clone" after the original title
CloneResetCreationTime = 2, // set timeInfo.CreationTime to the current time
CloneResetLastAccessTime = 4, // set timeInfo.LastAccessTime to the current time
CloneResetLocationChangedTime = 8, // set timeInfo.LocationChangedTime to the current time
CloneIncludeEntries = 16, // clone the group entries
CloneRenameTitle = 32, // add "- Clone" after the original title

CloneResetTimeInfo = CloneResetCreationTime | CloneResetLastAccessTime | CloneResetLocationChangedTime,
CloneExactCopy = CloneIncludeEntries,
CloneCopy = CloneExactCopy | CloneNewUuid | CloneResetTimeInfo,
CloneDefault = CloneCopy,
};
Q_DECLARE_FLAGS(CloneFlags, CloneFlag)

Expand Down Expand Up @@ -147,6 +166,7 @@ class Group : public ModifiableObject
void setParent(Group* parent, int index = -1, bool trackPrevious = true);
QStringList hierarchy(int height = -1) const;
bool hasChildren() const;
bool isDescendantOf(const Group* group) const;

Database* database();
const Database* database() const;
Expand All @@ -159,6 +179,53 @@ class Group : public ModifiableObject
QList<Entry*> entriesRecursive(bool includeHistoryItems = false) const;
QList<const Group*> groupsRecursive(bool includeSelf) const;
QList<Group*> groupsRecursive(bool includeSelf);

/**
* Walk methods for traversing the tree (depth-first search)
*
* @param[in] includeSelf is the current group to be included or excluded
* if `false` the current group's entries will not be included either
* @param[in] groupVisitor functor that takes a single argument: ([const] Group*)
* the functor may return a bool to indicate whether to stop=`true` or continue=`false` traversing
* for a non-`bool` return-type the value is ignored and the traversing will continue as if `false` had been returned
* @param[in] entryVisitor functor that takes a single argument: ([const] Entry*)
* the functor may return a bool to indicate whether to stop=`true` or continue=`false` traversing
* for a non-`bool` return-type the value is ignored and the traversing will continue as if `false` had been returned
* @return `false` if the traversing completed without stop, or `true` otherwise
*/
template <CGroupVisitor TGroupCallable, CEntryVisitor TEntryCallable>
bool walk(bool includeSelf, TGroupCallable&& groupVisitor, TEntryCallable&& entryVisitor)
{
return walk<TGroupCallable, TEntryCallable, false, true, true>(
includeSelf, std::forward<TGroupCallable>(groupVisitor), std::forward<TEntryCallable>(entryVisitor));
}
template <CGroupConstVisitor TGroupCallable, CEntryConstVisitor TEntryCallable>
bool walk(bool includeSelf, TGroupCallable&& groupVisitor, TEntryCallable&& entryVisitor) const
{
return walk<TGroupCallable, TEntryCallable, true, true, true>(
includeSelf, std::forward<TGroupCallable>(groupVisitor), std::forward<TEntryCallable>(entryVisitor));
}
template <CGroupConstVisitor TGroupCallable> bool walkGroups(bool includeSelf, TGroupCallable&& groupVisitor) const
{
return walk<TGroupCallable, void*, true, true, false>(
includeSelf, std::forward<TGroupCallable>(groupVisitor), nullptr);
}
template <CGroupVisitor TGroupCallable> bool walkGroups(bool includeSelf, TGroupCallable&& groupVisitor)
{
return walk<TGroupCallable, void*, false, true, false>(
includeSelf, std::forward<TGroupCallable>(groupVisitor), nullptr);
}
template <CEntryConstVisitor TEntryCallable> bool walkEntries(TEntryCallable&& entryVisitor) const
{
return walk<void*, TEntryCallable, true, false, true>(
true, nullptr, std::forward<TEntryCallable>(entryVisitor));
}
template <CEntryVisitor TEntryCallable> bool walkEntries(TEntryCallable&& entryVisitor)
{
return walk<void*, TEntryCallable, false, false, true>(
true, nullptr, std::forward<TEntryCallable>(entryVisitor));
}

QSet<QUuid> customIconsRecursive() const;
QList<QString> usernamesRecursive(int topN = -1) const;

Expand Down Expand Up @@ -204,8 +271,11 @@ private slots:
void updateTimeinfo();

private:
template <class P, class V> bool set(P& property, const V& value);
template <typename TGroupCallable, typename TEntryCallable, bool kIsConst, bool kVisitGroups, bool kVisitEntries>
bool walk(bool includeSelf, TGroupCallable&& groupVisitor, TEntryCallable&& entryVisitor) const;
template <class P, class V> bool set(P& property, const V& value, bool preserveTimeinfo = false);

void emitModifiedEx(bool preserveTimeinfo);
void setParent(Database* db);

void connectDatabaseSignalsRecursive(Database* db);
Expand Down Expand Up @@ -233,4 +303,55 @@ private slots:

Q_DECLARE_OPERATORS_FOR_FLAGS(Group::CloneFlags)

// helpers to support non-bool returning callables
template <bool kDefaultRetVal, typename TCallable, typename... Args>
bool visitorPredicateImpl(std::true_type, TCallable&& callable, Args&&... args)
{
return callable(std::forward<Args>(args)...);
}

template <bool kDefaultRetVal, typename TCallable, typename... Args>
bool visitorPredicateImpl(std::false_type, TCallable&& callable, Args&&... args)
{
callable(std::forward<Args>(args)...);
return kDefaultRetVal;
}

template <bool kDefaultRetVal, typename TCallable, typename... Args>
bool visitorPredicate(TCallable&& callable, Args&&... args)
{
using RetType = decltype(callable(args...));
return visitorPredicateImpl<kDefaultRetVal>(
std::is_same<RetType, bool>{}, std::forward<TCallable>(callable), std::forward<Args>(args)...);
}

template<typename TGroupCallable, typename TEntryCallable, bool kIsConst, bool kVisitGroups, bool kVisitEntries>
bool Group::walk(bool includeSelf, TGroupCallable&& groupVisitor, TEntryCallable&& entryVisitor) const
{
using GroupType = typename std::conditional<kIsConst,const Group, Group>::type;
QList<Group*> groupsToVisit;
if (includeSelf) {
groupsToVisit.append(const_cast<Group*>(this));
} else {
groupsToVisit.append(m_children);
}
while (!groupsToVisit.isEmpty()) {
GroupType* group = groupsToVisit.takeLast(); // right-to-left
if constexpr (kVisitGroups) {
if (visitorPredicate<false>(groupVisitor, group)) {
return true;
}
}
if constexpr (kVisitEntries) {
for (auto* entry : group->m_entries) {
if (visitorPredicate<false>(entryVisitor, entry)) {
return true;
}
}
}
groupsToVisit.append(group->m_children);
}
return false;
}

#endif // KEEPASSX_GROUP_H
Loading