From 58197ab4b5be4789a52a3eb84cabc3b1138dc810 Mon Sep 17 00:00:00 2001 From: vuurvlieg Date: Tue, 2 Apr 2024 17:14:43 +0200 Subject: [PATCH] Prevent moving items that exist in the targetDb - The user is presented with an in error message if it is the case. - The recycler is prevented from being moved away. --- src/gui/MainWindow.cpp | 4 ++ src/gui/MainWindow.h | 1 + src/gui/group/GroupModel.cpp | 114 ++++++++++++++++++++++++++--------- 3 files changed, 91 insertions(+), 28 deletions(-) diff --git a/src/gui/MainWindow.cpp b/src/gui/MainWindow.cpp index c97e04936..72417b781 100644 --- a/src/gui/MainWindow.cpp +++ b/src/gui/MainWindow.cpp @@ -708,6 +708,10 @@ QList MainWindow::getOpenDatabases() return dbWidgets; } +DatabaseWidget* MainWindow::currentDatabaseWidget() { + return m_ui->tabWidget->currentDatabaseWidget(); +} + void MainWindow::showErrorMessage(const QString& message) { m_ui->globalMessageWidget->showMessage(message, MessageWidget::Error); diff --git a/src/gui/MainWindow.h b/src/gui/MainWindow.h index c47c0d205..5c727e5a6 100644 --- a/src/gui/MainWindow.h +++ b/src/gui/MainWindow.h @@ -52,6 +52,7 @@ public: ~MainWindow() override; QList getOpenDatabases(); + DatabaseWidget* currentDatabaseWidget(); void restoreConfigState(); void setAllowScreenCapture(bool state); diff --git a/src/gui/group/GroupModel.cpp b/src/gui/group/GroupModel.cpp index c4c703cbd..fac98b56e 100644 --- a/src/gui/group/GroupModel.cpp +++ b/src/gui/group/GroupModel.cpp @@ -25,6 +25,7 @@ #include "core/Tools.h" #include "gui/DatabaseIcons.h" #include "gui/Icons.h" +#include "gui/MainWindow.h" #include "keeshare/KeeShare.h" GroupModel::GroupModel(Database* db, QObject* parent) @@ -204,9 +205,11 @@ bool GroupModel::dropMimeData(const QMimeData* data, if (action == Qt::IgnoreAction) { return true; + } else if (action != Qt::MoveAction && action != Qt::CopyAction && action != ::Qt::LinkAction) { + return false; } - if (!data || !(action & (Qt::MoveAction | Qt::CopyAction | Qt::LinkAction)) || !parent.isValid()) { + if (!data || !parent.isValid()) { return false; } @@ -223,6 +226,12 @@ bool GroupModel::dropMimeData(const QMimeData* data, row = rowCount(parent); } + auto showErrorMessage = [](const QString& errorMessage){ + if(auto dbWidget = getMainWindow()->currentDatabaseWidget()) { + dbWidget->showErrorMessage(errorMessage); + } + }; + // decode and insert QByteArray encoded = data->data(isGroup ? types.at(0) : types.at(1)); QDataStream stream(&encoded, QIODevice::ReadOnly); @@ -234,17 +243,17 @@ bool GroupModel::dropMimeData(const QMimeData* data, QUuid groupUuid; stream >> dbUuid >> groupUuid; - Database* db = Database::databaseByUuid(dbUuid); - if (!db) { + Database* sourceDb = Database::databaseByUuid(dbUuid); + if (!sourceDb) { return false; } - Group* dragGroup = db->rootGroup()->findGroupByUuid(groupUuid); - if (!dragGroup || !db->rootGroup()->findGroupByUuid(dragGroup->uuid()) || dragGroup == db->rootGroup()) { + Group* dragGroup = sourceDb->rootGroup()->findGroupByUuid(groupUuid); + if (!dragGroup || dragGroup == sourceDb->rootGroup()) { return false; } - if (dragGroup == parentGroup || dragGroup->findGroupByUuid(parentGroup->uuid())) { + if (dragGroup == parentGroup || parentGroup->isDescendantOf(dragGroup)) { return false; } @@ -252,25 +261,55 @@ bool GroupModel::dropMimeData(const QMimeData* data, row--; } - Database* sourceDb = dragGroup->database(); Database* targetDb = parentGroup->database(); - Group* group = dragGroup; if (sourceDb != targetDb) { - targetDb->metadata()->copyCustomIcons(group->customIconsRecursive(), sourceDb->metadata()); + if (action == Qt::MoveAction || action == Qt::LinkAction) { // clang-format off + + Group* binGroup = sourceDb->metadata()->recycleBin(); + if(binGroup && binGroup->uuid() == dragGroup->uuid()) { + showErrorMessage(tr("Move error: \"%1\" group cannot be moved").arg(binGroup->name())); + return true; + } + + // Collect all UUID(s) or short-circuit when UUID is deleted in targetDb + QSet uuidSet; + bool complexMove = group->walk(true, + [&](const Group* group) { + uuidSet.insert(group->uuid()); + return targetDb->containsDeletedObject(group->uuid()); + }, + [&](const Entry* entry) { + uuidSet.insert(entry->uuid()); + return targetDb->containsDeletedObject(entry->uuid()); + } + ); + + // Unable to handle complex moves until the Merger interface supports single group/entry merging + if (complexMove || targetDb->rootGroup()->walk(true, + [&](const Group* group)-> bool { + return uuidSet.contains(group->uuid()); + }, + [&](const Entry* entry) -> bool { + return uuidSet.contains(entry->uuid()); + } + )) { + showErrorMessage(tr("Move error: the group or one of it's descendants is already present in this database")); + return true; + } + } // clang-format on + + if (action == Qt::MoveAction) { // -- Tracked move - if (action == Qt::MoveAction) { - // -- Tracked move // A clone with new UUID but original CreationTime group = dragGroup->clone(Entry::CloneFlags(Entry::CloneCopy & ~Entry::CloneResetCreationTime), Group::CloneFlags(Group::CloneCopy & ~Group::CloneResetCreationTime)); - // Original UUID is marked as deleted to remove it from dbs that merge with this one + // Original UUID is marked as deleted to propagate the move to dbs that merge with this one delete dragGroup; - } else if (action == Qt::LinkAction) { - // -- Untracked move + } else if (action == Qt::LinkAction) { // -- Untracked move + QList deletedObjects(sourceDb->deletedObjects()); - // Exact copy of original group = dragGroup->clone(Entry::CloneExactCopy, Group::CloneExactCopy); delete dragGroup; // Unmark UUID(s) as deleted by restoring the previous list @@ -278,6 +317,8 @@ bool GroupModel::dropMimeData(const QMimeData* data, } else { group = dragGroup->clone(Entry::CloneCopy); } + + targetDb->metadata()->copyCustomIcons(group->customIconsRecursive(), sourceDb->metadata()); } else if (action == Qt::CopyAction) { group = dragGroup->clone(Entry::CloneCopy); } @@ -288,39 +329,49 @@ bool GroupModel::dropMimeData(const QMimeData* data, return false; } + int entries{0}, entriesNotMoved{0}; while (!stream.atEnd()) { QUuid dbUuid; QUuid entryUuid; stream >> dbUuid >> entryUuid; + ++entries; - Database* db = Database::databaseByUuid(dbUuid); - if (!db) { + Database* sourceDb = Database::databaseByUuid(dbUuid); + if (!sourceDb) { continue; } - Entry* dragEntry = db->rootGroup()->findEntryByUuid(entryUuid); - if (!dragEntry || !db->rootGroup()->findEntryByUuid(dragEntry->uuid())) { + Entry* dragEntry = sourceDb->rootGroup()->findEntryByUuid(entryUuid); + if (!dragEntry) { continue; } - Database* sourceDb = dragEntry->group()->database(); Database* targetDb = parentGroup->database(); - Entry* entry = dragEntry; if (sourceDb != targetDb) { - targetDb->metadata()->copyCustomIcon(entry->iconUuid(), sourceDb->metadata()); + if (action == Qt::MoveAction || action == Qt::LinkAction) { // clang-format off + + // Unable to handle complex moves until the Merger interface supports single group/entry merging + if (targetDb->containsDeletedObject(dragEntry->uuid()) || + targetDb->rootGroup()->walkEntries([=](const Entry* entry) { + return dragEntry->uuid() == entry->uuid(); + } + )) { + ++entriesNotMoved; + continue; + } + } // clang-format on + + if (action == Qt::MoveAction) { // -- Tracked move - if (action == Qt::MoveAction) { - // -- Tracked move // A clone with new UUID but original CreationTime entry = dragEntry->clone(Entry::CloneFlags(Entry::CloneCopy & ~Entry::CloneResetCreationTime)); - // Original UUID is marked as deleted to remove it from dbs that merge with this one + // Original UUID is marked as deleted to propagate the move to dbs that merge with this one delete dragEntry; - } else if (action == Qt::LinkAction) { - // -- Untracked move + } else if (action == Qt::LinkAction) { // -- Untracked move + QList deletedObjects(sourceDb->deletedObjects()); - // Exact copy of original entry = dragEntry->clone(Entry::CloneExactCopy); delete dragEntry; // Unmark UUID as deleted by restoring the previous list @@ -328,12 +379,19 @@ bool GroupModel::dropMimeData(const QMimeData* data, } else { entry = dragEntry->clone(Entry::CloneCopy); } + + targetDb->metadata()->copyCustomIcon(entry->iconUuid(), sourceDb->metadata()); } else if (action == Qt::CopyAction) { entry = dragEntry->clone(Entry::CloneCopy); } entry->setGroup(parentGroup); } + + if (entriesNotMoved) { + showErrorMessage( + tr("Move error: %1 of %2 entry(s) are already present in this database").arg(entriesNotMoved).arg(entries)); + } } return true;