diff --git a/src/core/Entry.cpp b/src/core/Entry.cpp index 844b3c463..cd7d2b261 100644 --- a/src/core/Entry.cpp +++ b/src/core/Entry.cpp @@ -1167,6 +1167,8 @@ QString Entry::resolvePlaceholderRecursive(const QString& placeholder, int maxDe return resolveMultiplePlaceholdersRecursive(notes(), maxDepth); case PlaceholderType::Url: return resolveMultiplePlaceholdersRecursive(url(), maxDepth); + case PlaceholderType::Uuid: + return uuidToHex(); case PlaceholderType::DbDir: { QFileInfo fileInfo(database()->filePath()); return fileInfo.absoluteDir().absolutePath(); @@ -1362,7 +1364,10 @@ QString Entry::resolveReferencePlaceholderRecursive(const QString& placeholder, QString result; const QString searchIn = match.captured(EntryAttributes::SearchInGroupName); - const QString searchText = match.captured(EntryAttributes::SearchTextGroupName); + QString searchText = match.captured(EntryAttributes::SearchTextGroupName); + + // Resolve placeholders in the search text (e.g., {UUID} -> actual UUID) + searchText = resolvePlaceholder(searchText); const EntryReferenceType searchInType = Entry::referenceType(searchIn); @@ -1573,6 +1578,7 @@ Entry::PlaceholderType Entry::placeholderType(const QString& placeholder) const {QStringLiteral("{NOTES}"), PlaceholderType::Notes}, {QStringLiteral("{TOTP}"), PlaceholderType::Totp}, {QStringLiteral("{URL}"), PlaceholderType::Url}, + {QStringLiteral("{UUID}"), PlaceholderType::Uuid}, {QStringLiteral("{URL:RMVSCM}"), PlaceholderType::UrlWithoutScheme}, {QStringLiteral("{URL:WITHOUTSCHEME}"), PlaceholderType::UrlWithoutScheme}, {QStringLiteral("{URL:SCM}"), PlaceholderType::UrlScheme}, diff --git a/src/core/Entry.h b/src/core/Entry.h index 69c38ac46..4874f5937 100644 --- a/src/core/Entry.h +++ b/src/core/Entry.h @@ -210,6 +210,7 @@ public: Notes, Totp, Url, + Uuid, UrlWithoutScheme, UrlScheme, UrlHost, diff --git a/src/core/EntryAttributes.cpp b/src/core/EntryAttributes.cpp index 5c1e1c3a0..dba4d8962 100644 --- a/src/core/EntryAttributes.cpp +++ b/src/core/EntryAttributes.cpp @@ -323,8 +323,9 @@ bool EntryAttributes::operator!=(const EntryAttributes& other) const QRegularExpressionMatch EntryAttributes::matchReference(const QString& text) { + // Updated regex to handle nested braces in SearchText (e.g., {UUID}) static const QRegularExpression referenceRegExp( - R"(\{REF:(?[TUPANI])@(?[TUPANIO]):(?[^}]+)\})", + R"(\{REF:(?[TUPANI])@(?[TUPANIO]):(?(?:[^{}]|\{[^}]*\})+)\})", QRegularExpression::CaseInsensitiveOption); return referenceRegExp.match(text); diff --git a/tests/TestEntry.cpp b/tests/TestEntry.cpp index 51cb4799c..9035ac81a 100644 --- a/tests/TestEntry.cpp +++ b/tests/TestEntry.cpp @@ -445,6 +445,49 @@ void TestEntry::testResolveReferencePlaceholders() entry3->attributes()->value("AttributeNotes")); } +void TestEntry::testResolveUuidPlaceholder() +{ + Database db; + auto* root = db.rootGroup(); + + auto* entry = new Entry(); + entry->setGroup(root); + entry->setUuid(QUuid::createUuid()); + entry->setTitle("Test Entry"); + entry->setUsername("TestUser"); + entry->setPassword("TestPass"); + entry->setNotes("Test with UUID: {UUID}"); + + // Test that {UUID} placeholder resolves to the entry's UUID + QString expectedUuid = entry->uuidToHex(); + QString resolvedNotes = entry->resolveMultiplePlaceholders(entry->notes()); + QCOMPARE(resolvedNotes, QString("Test with UUID: %1").arg(expectedUuid)); + + // Test {UUID} placeholder directly + QCOMPARE(entry->resolveMultiplePlaceholders("{UUID}"), expectedUuid); + + // Test case insensitivity + QCOMPARE(entry->resolveMultiplePlaceholders("{uuid}"), expectedUuid); + QCOMPARE(entry->resolveMultiplePlaceholders("{Uuid}"), expectedUuid); + + // Test mixed case in text + QCOMPARE(entry->resolveMultiplePlaceholders("UUID is {UUID} here"), QString("UUID is %1 here").arg(expectedUuid)); + + // Test advanced attribute with {REF:U@I:{UUID}} - should resolve to the entry's own username + entry->attributes()->set("SelfReference", "{REF:U@I:{UUID}}"); + QString attributeValue = entry->attributes()->value("SelfReference"); + QString resolvedSelfRef = entry->resolveMultiplePlaceholders(attributeValue); + + // Test the manual reference to confirm it works as before + QString manualReference = QString("{REF:U@I:%1}").arg(entry->uuidToHex()); + entry->attributes()->set("ManualReference", manualReference); + QString resolvedManualRef = entry->resolveMultiplePlaceholders(entry->attributes()->value("ManualReference")); + + // Test that both approaches work + QCOMPARE(resolvedManualRef, entry->username()); + QCOMPARE(resolvedSelfRef, entry->username()); +} + void TestEntry::testResolveNonIdPlaceholdersToUuid() { Database db; diff --git a/tests/TestEntry.h b/tests/TestEntry.h index 69f5b0d46..953a7ce7b 100644 --- a/tests/TestEntry.h +++ b/tests/TestEntry.h @@ -35,6 +35,7 @@ private slots: void testResolveUrlPlaceholders(); void testResolveRecursivePlaceholders(); void testResolveReferencePlaceholders(); + void testResolveUuidPlaceholder(); void testResolveNonIdPlaceholdersToUuid(); void testResolveConversionPlaceholders(); void testResolveReplacePlaceholders();