-
-
Notifications
You must be signed in to change notification settings - Fork 3k
Fix: Make FileUtil.relativize symlink-aware #13553
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
Changes from 14 commits
133a324
91da0cf
bcf3b76
a8a1235
6ccf927
4dd6694
90233e4
dc0d67f
dd9ca53
87b7ca9
6c654b6
77fd7a1
e717e07
22522dc
9f38281
8c9257d
bb3b3b2
b394a3c
1d236aa
5124b5f
ad92c0d
cb314a2
c60919c
b7f7587
4270261
18fa797
e90cafb
9e1952d
618e12a
3f88a1d
19d7cb3
bf94ce8
77985e1
9814427
da97e5f
5f12089
824734f
cf2bc0f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
package org.jabref.gui.maintable; | ||
|
||
import java.util.List; | ||
|
||
import org.jabref.gui.DialogService; | ||
import org.jabref.gui.StateManager; | ||
import org.jabref.gui.actions.ActionHelper; | ||
import org.jabref.gui.actions.SimpleCommand; | ||
import org.jabref.gui.fieldeditors.LinkedFileViewModel; | ||
import org.jabref.gui.preferences.GuiPreferences; | ||
import org.jabref.logic.l10n.Localization; | ||
import org.jabref.logic.util.TaskExecutor; | ||
|
||
public class OpenSelectedEntriesFilesAction extends SimpleCommand { | ||
|
||
private static final int FILES_LIMIT = 10; | ||
|
||
private final DialogService dialogService; | ||
private final StateManager stateManager; | ||
private final GuiPreferences preferences; | ||
private final TaskExecutor taskExecutor; | ||
|
||
public OpenSelectedEntriesFilesAction(DialogService dialogService, | ||
StateManager stateManager, | ||
GuiPreferences preferences, | ||
TaskExecutor taskExecutor) { | ||
this.dialogService = dialogService; | ||
this.stateManager = stateManager; | ||
this.preferences = preferences; | ||
this.taskExecutor = taskExecutor; | ||
|
||
this.executable.bind(ActionHelper.hasLinkedFileForSelectedEntries(stateManager) | ||
.and(ActionHelper.needsEntriesSelected(stateManager))); | ||
} | ||
|
||
@Override | ||
public void execute() { | ||
stateManager.getActiveDatabase().ifPresent(databaseContext -> { | ||
List<LinkedFileViewModel> linkedFileViewModelList = stateManager | ||
.getSelectedEntries().stream() | ||
.flatMap(entry -> entry.getFiles().stream() | ||
.map(linkedFile -> new LinkedFileViewModel( | ||
linkedFile, | ||
entry, | ||
databaseContext, | ||
taskExecutor, | ||
dialogService, | ||
preferences))) | ||
.toList(); | ||
if (linkedFileViewModelList.size() > FILES_LIMIT) { | ||
boolean continueOpening = dialogService.showConfirmationDialogAndWait( | ||
Localization.lang("Opening large number of files"), | ||
Localization.lang("You are about to open %0 files. Continue?", linkedFileViewModelList.size()), | ||
Localization.lang("Open all linked files"), | ||
Localization.lang("Cancel file opening") | ||
); | ||
if (!continueOpening) { | ||
return; | ||
} | ||
} | ||
|
||
linkedFileViewModelList.forEach(LinkedFileViewModel::open); | ||
}); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
package org.jabref.gui.maintable; | ||
|
||
import java.util.Optional; | ||
|
||
import org.jabref.gui.DialogService; | ||
import org.jabref.gui.StateManager; | ||
import org.jabref.gui.actions.SimpleCommand; | ||
import org.jabref.gui.fieldeditors.LinkedFileViewModel; | ||
import org.jabref.gui.preferences.GuiPreferences; | ||
import org.jabref.logic.util.TaskExecutor; | ||
import org.jabref.model.database.BibDatabaseContext; | ||
import org.jabref.model.entry.BibEntry; | ||
import org.jabref.model.entry.LinkedFile; | ||
|
||
import org.jspecify.annotations.NonNull; | ||
|
||
public class OpenSingleExternalFileAction extends SimpleCommand { | ||
|
||
private final DialogService dialogService; | ||
private final GuiPreferences preferences; | ||
private final BibEntry entry; | ||
private final LinkedFile linkedFile; | ||
private final TaskExecutor taskExecutor; | ||
private final StateManager stateManager; | ||
|
||
public OpenSingleExternalFileAction(@NonNull DialogService dialogService, | ||
@NonNull GuiPreferences preferences, | ||
@NonNull BibEntry entry, | ||
@NonNull LinkedFile linkedFile, | ||
@NonNull TaskExecutor taskExecutor, | ||
@NonNull StateManager stateManager) { | ||
this.dialogService = dialogService; | ||
this.preferences = preferences; | ||
this.entry = entry; | ||
this.linkedFile = linkedFile; | ||
this.taskExecutor = taskExecutor; | ||
this.stateManager = stateManager; | ||
|
||
this.setExecutable(true); | ||
} | ||
|
||
@Override | ||
public void execute() { | ||
Optional<BibDatabaseContext> databaseContextOptional = stateManager.getActiveDatabase(); | ||
if (databaseContextOptional.isEmpty()) { | ||
return; | ||
} | ||
BibDatabaseContext databaseContext = databaseContextOptional.get(); | ||
LinkedFileViewModel linkedFileViewModel = new LinkedFileViewModel( | ||
linkedFile, | ||
entry, | ||
databaseContext, | ||
taskExecutor, | ||
dialogService, | ||
preferences); | ||
linkedFileViewModel.open(); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -256,15 +256,40 @@ public static Path relativize(Path file, List<Path> directories) { | |
if (!file.isAbsolute()) { | ||
return file; | ||
} | ||
Optional<Path> realFileOpt = tryRealPath(file); | ||
|
||
for (Path directory : directories) { | ||
Optional<Path> realDirOpt = tryRealPath(directory); | ||
|
||
if (realFileOpt.isPresent() && realDirOpt.isPresent()) { | ||
Path realFile = realFileOpt.get(); | ||
Path realDir = realDirOpt.get(); | ||
if (realFile.startsWith(realDir)) { | ||
// Figure out offset in original path structure, so the returned path is user-friendly and symlinks are preserved if possible | ||
// Compute number of elements to drop from file, starting from directory length | ||
Muskan244 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
int nameCountToDrop = realDir.getNameCount(); | ||
Path relativePart = realFile.subpath(nameCountToDrop, realFile.getNameCount()); | ||
return relativePart; | ||
|
||
} | ||
} | ||
|
||
// Fallback: original logic as before, for non-symlinked directories | ||
Muskan244 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
if (file.startsWith(directory)) { | ||
return directory.relativize(file); | ||
} | ||
} | ||
return file; | ||
} | ||
|
||
private static Optional<Path> tryRealPath(Path path) { | ||
try { | ||
return Optional.of(path.toRealPath()); | ||
} catch (IOException e) { | ||
Muskan244 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
// maybe the file does not exist yet? fallback to absolute path. | ||
return Optional.of(path.toAbsolutePath()); | ||
} | ||
} | ||
|
||
/** | ||
* Converts an absolute file to a relative one, if possible. Returns the parameter file itself if no shortening is | ||
* possible. | ||
|
Uh oh!
There was an error while loading. Please reload this page.