Skip to content
Merged
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
9 changes: 6 additions & 3 deletions backend/src/main/java/cloudpage/service/FolderService.java
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ public List<SearchResult> searchInFolder(
SearchFilter filter)
throws IOException {

Path rootAbsolute = Paths.get(rootPath).toAbsolutePath().normalize();
Path folder = Paths.get(rootPath, folderPath).normalize();
validatePath(rootPath, folder);

Expand All @@ -78,7 +79,7 @@ public List<SearchResult> searchInFolder(
return stream
.filter(p -> !p.equals(folder))
.filter(p -> !p.startsWith(trashDir))
.map(p -> createSearchResult(p, lowerQuery))
.map(p -> createSearchResult(p, lowerQuery, rootAbsolute))
.filter(r -> r.getScore() >= minScore)
.filter(r -> matchesFilter(r, effectiveFilter))
.sorted(searchComparator(effectiveFilter))
Expand Down Expand Up @@ -153,7 +154,7 @@ private Comparator<SearchResult> searchComparator(SearchFilter filter) {
* @return a {@link SearchResult} with the calculated similarity score
* @throws FileNotFoundException if the file or folder metadata cannot be read
*/
private SearchResult createSearchResult(Path path, String query) {
private SearchResult createSearchResult(Path path, String query, Path rootAbsolute) {
String name = path.getFileName().toString();
// Locale.ROOT prevents using the system's local language for case conversion
String lowerName = name.toLowerCase(Locale.ROOT);
Expand All @@ -167,10 +168,12 @@ private SearchResult createSearchResult(Path path, String query) {
}

try {
Path lexicalPath = path.toAbsolutePath().normalize();
String relativePath = toRelativePath(rootAbsolute, lexicalPath);
boolean isDir = Files.isDirectory(path);
return new SearchResult(
name,
path.toString(),
relativePath,
isDir ? "folder" : "file",
isDir ? null : Files.size(path),
isDir ? null : Files.probeContentType(path),
Expand Down
61 changes: 61 additions & 0 deletions backend/src/test/java/cloudpage/service/FolderServiceTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -715,6 +715,67 @@ void getFolderContentPage_folderWithContents_sizeIsRecursiveSum() throws IOExcep

// ── searchInFolder: metadata filters & sorting ───────────────────────────

@Test
void searchInFolder_returnsUserRelativePath() throws IOException {
Path documents = Files.createDirectory(tempDir.resolve("Documents"));
Files.writeString(documents.resolve("report.txt"), "w");

List<SearchResult> results =
folderService.searchInFolder(tempDir.toString(), "", "report", 20, 0);

SearchResult result =
results.stream().filter(r -> r.getName().equals("report.txt")).findFirst().orElseThrow();

assertEquals("report.txt", result.getName());
assertEquals("Documents/report.txt", result.getPath());
}

@Test
void searchInFolder_symlinkOutsideRoot_doesNotFailSearch() throws IOException {
Path target = Files.createTempFile("outside-target", ".txt");
Path link = tempDir.resolve("report-link");

try {
Files.createSymbolicLink(link, target);
} catch (UnsupportedOperationException | IOException | SecurityException exception) {
assumeTrue(false, "Symbolic links are not available: " + exception.getMessage());
}

List<SearchResult> results =
folderService.searchInFolder(tempDir.toString(), "", "report", 20, 0);

SearchResult result =
results.stream().filter(r -> r.getName().equals("report-link")).findFirst().orElseThrow();

assertEquals("report-link", result.getPath());
}

@Test
void searchInFolder_symlinkedRootAncestor_returnsRelativePath() throws IOException {
Path realBase = Files.createDirectory(tempDir.resolve("real-base"));
Path userRoot = Files.createDirectory(realBase.resolve("user-root"));
Path documents = Files.createDirectory(userRoot.resolve("Documents"));
Files.writeString(documents.resolve("report.txt"), "w");

Path linkedBase = tempDir.resolve("linked-base");

try {
Files.createSymbolicLink(linkedBase, realBase);
} catch (UnsupportedOperationException | IOException | SecurityException exception) {
assumeTrue(false, "Symbolic links are not available: " + exception.getMessage());
}

Path lexicalRoot = linkedBase.resolve("user-root");

List<SearchResult> results =
folderService.searchInFolder(lexicalRoot.toString(), "", "report", 20, 0);

SearchResult result =
results.stream().filter(r -> r.getName().equals("report.txt")).findFirst().orElseThrow();

assertEquals("Documents/report.txt", result.getPath());
}

@Test
void searchInFolder_filterByTypeFile_excludesFolders() throws IOException {
Files.writeString(tempDir.resolve("report-file.txt"), "x");
Expand Down
Loading