diff --git a/src/main/java/org/jabref/gui/JabRefFrame.java b/src/main/java/org/jabref/gui/JabRefFrame.java
index 601c3fba93f..8a58d2a6257 100644
--- a/src/main/java/org/jabref/gui/JabRefFrame.java
+++ b/src/main/java/org/jabref/gui/JabRefFrame.java
@@ -490,7 +490,7 @@ public boolean quit() {
context.clearDBMSSynchronizer();
}
AutosaveManager.shutdown(context);
- BackupManager.shutdown(context);
+ BackupManager.shutdown(context, prefs.getFilePreferences().getBackupDirectory(), prefs.getFilePreferences().shouldCreateBackup());
context.getDatabasePath().map(Path::toAbsolutePath).map(Path::toString).ifPresent(filenames::add);
}
@@ -1235,7 +1235,7 @@ private boolean confirmClose(LibraryTab libraryTab) {
}
if (buttonType.equals(discardChanges)) {
- BackupManager.discardBackup(libraryTab.getBibDatabaseContext());
+ BackupManager.discardBackup(libraryTab.getBibDatabaseContext(), prefs.getFilePreferences().getBackupDirectory());
return true;
}
@@ -1312,7 +1312,7 @@ private void closeTab(LibraryTab libraryTab) {
removeTab(libraryTab);
}
AutosaveManager.shutdown(context);
- BackupManager.shutdown(context);
+ BackupManager.shutdown(context, prefs.getFilePreferences().getBackupDirectory(), prefs.getFilePreferences().shouldCreateBackup());
}
private void removeTab(LibraryTab libraryTab) {
diff --git a/src/main/java/org/jabref/gui/LibraryTab.java b/src/main/java/org/jabref/gui/LibraryTab.java
index bc0ac7fb296..c3a06b8c56b 100644
--- a/src/main/java/org/jabref/gui/LibraryTab.java
+++ b/src/main/java/org/jabref/gui/LibraryTab.java
@@ -289,7 +289,7 @@ public void installAutosaveManagerAndBackupManager() {
AutosaveManager autosaveManager = AutosaveManager.start(bibDatabaseContext);
autosaveManager.registerListener(new AutosaveUiManager(this));
}
- if (isDatabaseReadyForBackup(bibDatabaseContext)) {
+ if (isDatabaseReadyForBackup(bibDatabaseContext) && preferencesService.getFilePreferences().shouldCreateBackup()) {
BackupManager.start(bibDatabaseContext, Globals.entryTypesManager, preferencesService);
}
}
@@ -706,7 +706,7 @@ private void saveDividerLocation(Number position) {
public void cleanUp() {
changeMonitor.ifPresent(DatabaseChangeMonitor::unregister);
AutosaveManager.shutdown(bibDatabaseContext);
- BackupManager.shutdown(bibDatabaseContext);
+ BackupManager.shutdown(bibDatabaseContext, preferencesService.getFilePreferences().getBackupDirectory(), preferencesService.getFilePreferences().shouldCreateBackup());
}
/**
diff --git a/src/main/java/org/jabref/gui/StateManager.java b/src/main/java/org/jabref/gui/StateManager.java
index fffab647dd0..76d696aad4b 100644
--- a/src/main/java/org/jabref/gui/StateManager.java
+++ b/src/main/java/org/jabref/gui/StateManager.java
@@ -62,7 +62,7 @@ public class StateManager {
private final OptionalObjectProperty activeSearchQuery = OptionalObjectProperty.empty();
private final ObservableMap searchResultMap = FXCollections.observableHashMap();
private final OptionalObjectProperty focusOwner = OptionalObjectProperty.empty();
- private final ObservableList>> backgroundTasks = FXCollections.observableArrayList(task -> new Observable[] {task.getValue().progressProperty(), task.getValue().runningProperty()});
+ private final ObservableList, Task>>> backgroundTasks = FXCollections.observableArrayList(task -> new Observable[] {task.getValue().progressProperty(), task.getValue().runningProperty()});
private final EasyBinding anyTaskRunning = EasyBind.reduce(backgroundTasks, tasks -> tasks.map(Pair::getValue).anyMatch(Task::isRunning));
private final EasyBinding anyTasksThatWillNotBeRecoveredRunning = EasyBind.reduce(backgroundTasks, tasks -> tasks.anyMatch(task -> !task.getKey().willBeRecoveredAutomatically() && task.getValue().isRunning()));
private final EasyBinding tasksProgress = EasyBind.reduce(backgroundTasks, tasks -> tasks.map(Pair::getValue).filter(Task::isRunning).mapToDouble(Task::getProgress).average().orElse(1));
@@ -169,7 +169,7 @@ public ObservableList> getBackgroundTasks() {
return EasyBind.map(backgroundTasks, Pair::getValue);
}
- public void addBackgroundTask(BackgroundTask backgroundTask, Task> task) {
+ public void addBackgroundTask(BackgroundTask> backgroundTask, Task> task) {
this.backgroundTasks.add(0, new Pair<>(backgroundTask, task));
}
diff --git a/src/main/java/org/jabref/gui/backup/BackupResolverDialog.java b/src/main/java/org/jabref/gui/backup/BackupResolverDialog.java
index bdd2fbd2d16..de43a067eb8 100644
--- a/src/main/java/org/jabref/gui/backup/BackupResolverDialog.java
+++ b/src/main/java/org/jabref/gui/backup/BackupResolverDialog.java
@@ -26,13 +26,13 @@ public class BackupResolverDialog extends FXDialog {
private static final Logger LOGGER = LoggerFactory.getLogger(BackupResolverDialog.class);
- public BackupResolverDialog(Path originalPath) {
+ public BackupResolverDialog(Path originalPath, Path backupDir) {
super(AlertType.CONFIRMATION, Localization.lang("Backup found"), true);
setHeaderText(null);
getDialogPane().setMinHeight(180);
getDialogPane().getButtonTypes().setAll(RESTORE_FROM_BACKUP, REVIEW_BACKUP, IGNORE_BACKUP);
- Optional backupPathOpt = BackupFileUtil.getPathOfLatestExistingBackupFile(originalPath, BackupFileType.BACKUP);
+ Optional backupPathOpt = BackupFileUtil.getPathOfLatestExistingBackupFile(originalPath, BackupFileType.BACKUP, backupDir);
String backupFilename = backupPathOpt.map(Path::getFileName).map(Path::toString).orElse(Localization.lang("File not found"));
String content = new StringBuilder()
.append(Localization.lang("A backup file for '%0' was found at [%1]",
diff --git a/src/main/java/org/jabref/gui/citationkeypattern/GenerateCitationKeyAction.java b/src/main/java/org/jabref/gui/citationkeypattern/GenerateCitationKeyAction.java
index 1cc32a198c1..5cc07a31c71 100644
--- a/src/main/java/org/jabref/gui/citationkeypattern/GenerateCitationKeyAction.java
+++ b/src/main/java/org/jabref/gui/citationkeypattern/GenerateCitationKeyAction.java
@@ -54,7 +54,7 @@ public void execute() {
checkOverwriteKeysChosen();
if (!this.isCanceled) {
- BackgroundTask backgroundTask = this.generateKeysInBackground();
+ BackgroundTask backgroundTask = this.generateKeysInBackground();
backgroundTask.showToUser(true);
backgroundTask.titleProperty().set(Localization.lang("Autogenerate citation keys"));
backgroundTask.messageProperty().set(Localization.lang("%0/%1 entries", 0, entries.size()));
@@ -93,8 +93,8 @@ private void checkOverwriteKeysChosen() {
}
}
- private BackgroundTask generateKeysInBackground() {
- return new BackgroundTask() {
+ private BackgroundTask generateKeysInBackground() {
+ return new BackgroundTask<>() {
private NamedCompound compound;
@Override
diff --git a/src/main/java/org/jabref/gui/dialogs/BackupUIManager.java b/src/main/java/org/jabref/gui/dialogs/BackupUIManager.java
index ceba43dd088..b1c4981e744 100644
--- a/src/main/java/org/jabref/gui/dialogs/BackupUIManager.java
+++ b/src/main/java/org/jabref/gui/dialogs/BackupUIManager.java
@@ -38,10 +38,10 @@ private BackupUIManager() {
}
public static Optional showRestoreBackupDialog(DialogService dialogService, Path originalPath, PreferencesService preferencesService) {
- var actionOpt = showBackupResolverDialog(dialogService, originalPath);
+ var actionOpt = showBackupResolverDialog(dialogService, originalPath, preferencesService.getFilePreferences().getBackupDirectory());
return actionOpt.flatMap(action -> {
if (action == BackupResolverDialog.RESTORE_FROM_BACKUP) {
- BackupManager.restoreBackup(originalPath);
+ BackupManager.restoreBackup(originalPath, preferencesService.getFilePreferences().getBackupDirectory());
return Optional.empty();
} else if (action == BackupResolverDialog.REVIEW_BACKUP) {
return showReviewBackupDialog(dialogService, originalPath, preferencesService);
@@ -50,20 +50,20 @@ public static Optional showRestoreBackupDialog(DialogService dialo
});
}
- private static Optional showBackupResolverDialog(DialogService dialogService, Path originalPath) {
- return DefaultTaskExecutor.runInJavaFXThread(() -> dialogService.showCustomDialogAndWait(new BackupResolverDialog(originalPath)));
+ private static Optional showBackupResolverDialog(DialogService dialogService, Path originalPath, Path backupDir) {
+ return DefaultTaskExecutor.runInJavaFXThread(() -> dialogService.showCustomDialogAndWait(new BackupResolverDialog(originalPath, backupDir)));
}
private static Optional showReviewBackupDialog(DialogService dialogService, Path originalPath, PreferencesService preferencesService) {
try {
- ImportFormatPreferences importFormatPreferences = Globals.prefs.getImportFormatPreferences();
+ ImportFormatPreferences importFormatPreferences = preferencesService.getImportFormatPreferences();
// The database of the originalParserResult will be modified
ParserResult originalParserResult = OpenDatabase.loadDatabase(originalPath, importFormatPreferences, Globals.getFileUpdateMonitor());
// This will be modified by using the `DatabaseChangesResolverDialog`.
BibDatabaseContext originalDatabase = originalParserResult.getDatabaseContext();
- Path backupPath = BackupFileUtil.getPathOfLatestExistingBackupFile(originalPath, BackupFileType.BACKUP).orElseThrow();
+ Path backupPath = BackupFileUtil.getPathOfLatestExistingBackupFile(originalPath, BackupFileType.BACKUP, preferencesService.getFilePreferences().getBackupDirectory()).orElseThrow();
BibDatabaseContext backupDatabase = OpenDatabase.loadDatabase(backupPath, importFormatPreferences, new DummyFileUpdateMonitor()).getDatabaseContext();
DatabaseChangeResolverFactory changeResolverFactory = new DatabaseChangeResolverFactory(dialogService, originalDatabase, preferencesService.getBibEntryPreferences());
diff --git a/src/main/java/org/jabref/gui/exporter/SaveDatabaseAction.java b/src/main/java/org/jabref/gui/exporter/SaveDatabaseAction.java
index 95fa69f1777..ca32a6a98df 100644
--- a/src/main/java/org/jabref/gui/exporter/SaveDatabaseAction.java
+++ b/src/main/java/org/jabref/gui/exporter/SaveDatabaseAction.java
@@ -116,7 +116,7 @@ boolean saveAs(Path file, SaveDatabaseMode mode) {
final Path oldFile = databasePath.get();
context.setDatabasePath(oldFile);
AutosaveManager.shutdown(context);
- BackupManager.shutdown(context);
+ BackupManager.shutdown(context, this.preferences.getFilePreferences().getBackupDirectory(), preferences.getFilePreferences().shouldCreateBackup());
}
// Set new location
diff --git a/src/main/java/org/jabref/gui/importer/actions/OpenDatabaseAction.java b/src/main/java/org/jabref/gui/importer/actions/OpenDatabaseAction.java
index 403359dd390..75ee983ec31 100644
--- a/src/main/java/org/jabref/gui/importer/actions/OpenDatabaseAction.java
+++ b/src/main/java/org/jabref/gui/importer/actions/OpenDatabaseAction.java
@@ -192,9 +192,10 @@ private ParserResult loadDatabase(Path file) throws Exception {
dialogService.notify(Localization.lang("Opening") + ": '" + file + "'");
preferencesService.getFilePreferences().setWorkingDirectory(fileToLoad.getParent());
+ Path backupDir = preferencesService.getFilePreferences().getBackupDirectory();
ParserResult parserResult = null;
- if (BackupManager.backupFileDiffers(fileToLoad)) {
+ if (BackupManager.backupFileDiffers(fileToLoad, backupDir)) {
// In case the backup differs, ask the user what to do.
// In case the user opted for restoring a backup, the content of the backup is contained in parserResult.
parserResult = BackupUIManager.showRestoreBackupDialog(dialogService, fileToLoad, preferencesService).orElse(null);
diff --git a/src/main/java/org/jabref/gui/preferences/general/GeneralTab.fxml b/src/main/java/org/jabref/gui/preferences/general/GeneralTab.fxml
index a108e2fec39..8044c84f7e7 100644
--- a/src/main/java/org/jabref/gui/preferences/general/GeneralTab.fxml
+++ b/src/main/java/org/jabref/gui/preferences/general/GeneralTab.fxml
@@ -1,12 +1,18 @@
+
+
+
+
+
+
@@ -35,4 +41,22 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/main/java/org/jabref/gui/preferences/general/GeneralTab.java b/src/main/java/org/jabref/gui/preferences/general/GeneralTab.java
index 6a28b84db3a..0f3811c47cb 100644
--- a/src/main/java/org/jabref/gui/preferences/general/GeneralTab.java
+++ b/src/main/java/org/jabref/gui/preferences/general/GeneralTab.java
@@ -3,6 +3,7 @@
import javafx.fxml.FXML;
import javafx.scene.control.CheckBox;
import javafx.scene.control.ComboBox;
+import javafx.scene.control.TextField;
import org.jabref.gui.preferences.AbstractPreferenceTabView;
import org.jabref.gui.preferences.PreferencesTab;
@@ -24,6 +25,9 @@ public class GeneralTab extends AbstractPreferenceTabView i
@FXML private CheckBox collectTelemetry;
@FXML private CheckBox showAdvancedHints;
+ @FXML private CheckBox createBackup;
+ @FXML private TextField backupDirectory;
+
public GeneralTab() {
ViewLoader.view(this)
.root(this)
@@ -36,7 +40,7 @@ public String getTabName() {
}
public void initialize() {
- this.viewModel = new GeneralTabViewModel(dialogService, preferencesService.getGeneralPreferences(), preferencesService.getTelemetryPreferences());
+ this.viewModel = new GeneralTabViewModel(preferencesService, dialogService);
new ViewModelListCellFactory()
.withText(Language::getDisplayName)
@@ -56,5 +60,13 @@ public void initialize() {
openLastStartup.selectedProperty().bindBidirectional(viewModel.openLastStartupProperty());
collectTelemetry.selectedProperty().bindBidirectional(viewModel.collectTelemetryProperty());
showAdvancedHints.selectedProperty().bindBidirectional(viewModel.showAdvancedHintsProperty());
+
+ createBackup.selectedProperty().bindBidirectional(viewModel.createBackupProperty());
+ backupDirectory.textProperty().bindBidirectional(viewModel.backupDirectoryProperty());
+ backupDirectory.disableProperty().bind(viewModel.createBackupProperty().not());
+ }
+
+ public void backupFileDirBrowse() {
+ viewModel.backupFileDirBrowse();
}
}
diff --git a/src/main/java/org/jabref/gui/preferences/general/GeneralTabViewModel.java b/src/main/java/org/jabref/gui/preferences/general/GeneralTabViewModel.java
index 68130266435..b36ef455302 100644
--- a/src/main/java/org/jabref/gui/preferences/general/GeneralTabViewModel.java
+++ b/src/main/java/org/jabref/gui/preferences/general/GeneralTabViewModel.java
@@ -1,5 +1,6 @@
package org.jabref.gui.preferences.general;
+import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
@@ -10,15 +11,20 @@
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleListProperty;
import javafx.beans.property.SimpleObjectProperty;
+import javafx.beans.property.SimpleStringProperty;
+import javafx.beans.property.StringProperty;
import javafx.collections.FXCollections;
import javafx.collections.transformation.SortedList;
import org.jabref.gui.DialogService;
import org.jabref.gui.preferences.PreferenceTabViewModel;
+import org.jabref.gui.util.DirectoryDialogConfiguration;
import org.jabref.logic.l10n.Language;
import org.jabref.logic.l10n.Localization;
import org.jabref.model.database.BibDatabaseMode;
+import org.jabref.preferences.FilePreferences;
import org.jabref.preferences.GeneralPreferences;
+import org.jabref.preferences.PreferencesService;
import org.jabref.preferences.TelemetryPreferences;
public class GeneralTabViewModel implements PreferenceTabViewModel {
@@ -34,19 +40,25 @@ public class GeneralTabViewModel implements PreferenceTabViewModel {
private final BooleanProperty openLastStartupProperty = new SimpleBooleanProperty();
private final BooleanProperty showAdvancedHintsProperty = new SimpleBooleanProperty();
+ private final BooleanProperty createBackupProperty = new SimpleBooleanProperty();
+ private final StringProperty backupDirectoryProperty = new SimpleStringProperty("");
+
private final DialogService dialogService;
private final GeneralPreferences generalPreferences;
private final TelemetryPreferences telemetryPreferences;
+ private final FilePreferences filePreferences;
private final List restartWarning = new ArrayList<>();
@SuppressWarnings("ReturnValueIgnored")
- public GeneralTabViewModel(DialogService dialogService, GeneralPreferences generalPreferences, TelemetryPreferences telemetryPreferences) {
+ public GeneralTabViewModel(PreferencesService preferencesService, DialogService dialogService) {
this.dialogService = dialogService;
- this.generalPreferences = generalPreferences;
- this.telemetryPreferences = telemetryPreferences;
+ this.generalPreferences = preferencesService.getGeneralPreferences();
+ this.telemetryPreferences = preferencesService.getTelemetryPreferences();
+ this.filePreferences = preferencesService.getFilePreferences();
}
+ @Override
public void setValues() {
languagesListProperty.setValue(new SortedList<>(FXCollections.observableArrayList(Language.values()), Comparator.comparing(Language::getDisplayName)));
selectedLanguageProperty.setValue(generalPreferences.getLanguage());
@@ -60,8 +72,12 @@ public void setValues() {
collectTelemetryProperty.setValue(telemetryPreferences.shouldCollectTelemetry());
openLastStartupProperty.setValue(generalPreferences.shouldOpenLastEdited());
showAdvancedHintsProperty.setValue(generalPreferences.shouldShowAdvancedHints());
+
+ createBackupProperty.setValue(filePreferences.shouldCreateBackup());
+ backupDirectoryProperty.setValue(filePreferences.getBackupDirectory().toString());
}
+ @Override
public void storeSettings() {
Language newLanguage = selectedLanguageProperty.getValue();
if (newLanguage != generalPreferences.getLanguage()) {
@@ -84,6 +100,9 @@ public void storeSettings() {
generalPreferences.setShowAdvancedHints(showAdvancedHintsProperty.getValue());
telemetryPreferences.setCollectTelemetry(collectTelemetryProperty.getValue());
+
+ filePreferences.createBackupProperty().setValue(createBackupProperty.getValue());
+ filePreferences.backupDirectoryProperty().setValue(Path.of(backupDirectoryProperty.getValue()));
}
@Override
@@ -132,4 +151,19 @@ public BooleanProperty openLastStartupProperty() {
public BooleanProperty showAdvancedHintsProperty() {
return this.showAdvancedHintsProperty;
}
+
+ public BooleanProperty createBackupProperty() {
+ return this.createBackupProperty;
+ }
+
+ public StringProperty backupDirectoryProperty() {
+ return this.backupDirectoryProperty;
+ }
+
+ public void backupFileDirBrowse() {
+ DirectoryDialogConfiguration dirDialogConfiguration =
+ new DirectoryDialogConfiguration.Builder().withInitialDirectory(Path.of(backupDirectoryProperty().getValue())).build();
+ dialogService.showDirectorySelectionDialog(dirDialogConfiguration)
+ .ifPresent(dir -> backupDirectoryProperty.setValue(dir.toString()));
+ }
}
diff --git a/src/main/java/org/jabref/logic/autosaveandbackup/BackupManager.java b/src/main/java/org/jabref/logic/autosaveandbackup/BackupManager.java
index 9a8aafacc38..599ac265fc9 100644
--- a/src/main/java/org/jabref/logic/autosaveandbackup/BackupManager.java
+++ b/src/main/java/org/jabref/logic/autosaveandbackup/BackupManager.java
@@ -58,11 +58,9 @@ public class BackupManager {
private final CoarseChangeFilter changeFilter;
private final BibEntryTypesManager entryTypesManager;
-
// Contains a list of all backup paths
// During a write, the less recent backup file is deleted
private final Queue backupFilesQueue = new LinkedBlockingQueue<>();
-
private boolean needsBackup = false;
BackupManager(BibDatabaseContext bibDatabaseContext, BibEntryTypesManager entryTypesManager, PreferencesService preferences) {
@@ -78,15 +76,15 @@ public class BackupManager {
/**
* Determines the most recent backup file name
*/
- static Path getBackupPathForNewBackup(Path originalPath) {
- return BackupFileUtil.getPathForNewBackupFileAndCreateDirectory(originalPath, BackupFileType.BACKUP);
+ static Path getBackupPathForNewBackup(Path originalPath, Path backupDir) {
+ return BackupFileUtil.getPathForNewBackupFileAndCreateDirectory(originalPath, BackupFileType.BACKUP, backupDir);
}
/**
* Determines the most recent existing backup file name
*/
- static Optional getLatestBackupPath(Path originalPath) {
- return BackupFileUtil.getPathOfLatestExistingBackupFile(originalPath, BackupFileType.BACKUP);
+ static Optional getLatestBackupPath(Path originalPath, Path backupDir) {
+ return BackupFileUtil.getPathOfLatestExistingBackupFile(originalPath, BackupFileType.BACKUP, backupDir);
}
/**
@@ -99,7 +97,7 @@ static Optional getLatestBackupPath(Path originalPath) {
*/
public static BackupManager start(BibDatabaseContext bibDatabaseContext, BibEntryTypesManager entryTypesManager, PreferencesService preferences) {
BackupManager backupManager = new BackupManager(bibDatabaseContext, entryTypesManager, preferences);
- backupManager.startBackupTask();
+ backupManager.startBackupTask(preferences.getFilePreferences().getBackupDirectory());
runningInstances.add(backupManager);
return backupManager;
}
@@ -109,19 +107,19 @@ public static BackupManager start(BibDatabaseContext bibDatabaseContext, BibEntr
*
* @param bibDatabaseContext Associated {@link BibDatabaseContext}
*/
- public static void discardBackup(BibDatabaseContext bibDatabaseContext) {
- runningInstances.stream().filter(instance -> instance.bibDatabaseContext == bibDatabaseContext).forEach(
- BackupManager::discardBackup);
+ public static void discardBackup(BibDatabaseContext bibDatabaseContext, Path backupDir) {
+ runningInstances.stream().filter(instance -> instance.bibDatabaseContext == bibDatabaseContext).forEach(backupManager -> backupManager.discardBackup(backupDir));
}
/**
* Shuts down the BackupManager which is associated with the given {@link BibDatabaseContext}.
*
* @param bibDatabaseContext Associated {@link BibDatabaseContext}
+ * @param createBackup True, if a backup should be created
+ * @param backupDir The path to the backup directory
*/
- public static void shutdown(BibDatabaseContext bibDatabaseContext) {
- runningInstances.stream().filter(instance -> instance.bibDatabaseContext == bibDatabaseContext).forEach(
- BackupManager::shutdown);
+ public static void shutdown(BibDatabaseContext bibDatabaseContext, Path backupDir, boolean createBackup) {
+ runningInstances.stream().filter(instance -> instance.bibDatabaseContext == bibDatabaseContext).forEach(backupManager -> backupManager.shutdown(backupDir, createBackup));
runningInstances.removeIf(instance -> instance.bibDatabaseContext == bibDatabaseContext);
}
@@ -137,8 +135,8 @@ public static void shutdown(BibDatabaseContext bibDatabaseContext) {
* "default" return value in the good case. In case a discarded file exists, false
is returned, too.
* In the case of an exception true
is returned to ensure that the user checks the output.
*/
- public static boolean backupFileDiffers(Path originalPath) {
- Path discardedFile = determineDiscardedFile(originalPath);
+ public static boolean backupFileDiffers(Path originalPath, Path backupDir) {
+ Path discardedFile = determineDiscardedFile(originalPath, backupDir);
if (Files.exists(discardedFile)) {
try {
Files.delete(discardedFile);
@@ -148,10 +146,10 @@ public static boolean backupFileDiffers(Path originalPath) {
}
return false;
}
- return getLatestBackupPath(originalPath).map(latestBackupPath -> {
+ return getLatestBackupPath(originalPath, backupDir).map(latestBackupPath -> {
FileTime latestBackupFileLastModifiedTime;
try {
- latestBackupFileLastModifiedTime = Files.getLastModifiedTime(latestBackupPath);
+ latestBackupFileLastModifiedTime = Files.getLastModifiedTime(latestBackupPath);
} catch (IOException e) {
LOGGER.debug("Could not get timestamp of backup file {}", latestBackupPath, e);
// If we cannot get the timestamp, we do show any warning
@@ -185,8 +183,8 @@ public static boolean backupFileDiffers(Path originalPath) {
*
* @param originalPath Path to the file which should be equalized to the backup file.
*/
- public static void restoreBackup(Path originalPath) {
- Optional backupPath = getLatestBackupPath(originalPath);
+ public static void restoreBackup(Path originalPath, Path backupDir) {
+ Optional backupPath = getLatestBackupPath(originalPath, backupDir);
if (backupPath.isEmpty()) {
LOGGER.error("There is no backup file");
return;
@@ -198,8 +196,8 @@ public static void restoreBackup(Path originalPath) {
}
}
- Optional determineBackupPathForNewBackup() {
- return bibDatabaseContext.getDatabasePath().map(BackupManager::getBackupPathForNewBackup);
+ Optional determineBackupPathForNewBackup(Path backupDir) {
+ return bibDatabaseContext.getDatabasePath().map(path -> BackupManager.getBackupPathForNewBackup(path, backupDir));
}
/**
@@ -207,7 +205,8 @@ Optional determineBackupPathForNewBackup() {
*
* SIDE EFFECT: Deletes oldest backup file
*
- * @param backupPath the path where the library should be backed up to
+ *
+ * @param backupPath the full path to the file where the library should be backed up to
*/
void performBackup(Path backupPath) {
if (!needsBackup) {
@@ -253,10 +252,8 @@ void performBackup(Path backupPath) {
}
}
- private static Path determineDiscardedFile(Path file) {
- return BackupFileUtil.getAppDataBackupDir().resolve(
- BackupFileUtil.getUniqueFilePrefix(file) + "--" + file.getFileName() + "--discarded"
- );
+ private static Path determineDiscardedFile(Path file, Path backupDir) {
+ return backupDir.resolve(BackupFileUtil.getUniqueFilePrefix(file) + "--" + file.getFileName() + "--discarded");
}
/**
@@ -265,8 +262,8 @@ private static Path determineDiscardedFile(Path file) {
* We do not delete any files, because the user might want to recover old backup files.
* Therefore, we mark discarded backups by a --discarded file.
*/
- public void discardBackup() {
- Path path = determineDiscardedFile(bibDatabaseContext.getDatabasePath().get());
+ public void discardBackup(Path backupDir) {
+ Path path = determineDiscardedFile(bibDatabaseContext.getDatabasePath().get(), backupDir);
try {
Files.createFile(path);
} catch (IOException e) {
@@ -294,19 +291,18 @@ public synchronized void listen(@SuppressWarnings("unused") BibDatabaseContextCh
}
}
- private void startBackupTask() {
- fillQueue();
+ private void startBackupTask(Path backupDir) {
+ fillQueue(backupDir);
executor.scheduleAtFixedRate(
- // We need to determine the backup path on each action, because we use the timestamp in the filename
- () -> determineBackupPathForNewBackup().ifPresent(this::performBackup),
- DELAY_BETWEEN_BACKUP_ATTEMPTS_IN_SECONDS,
- DELAY_BETWEEN_BACKUP_ATTEMPTS_IN_SECONDS,
- TimeUnit.SECONDS);
+ // We need to determine the backup path on each action, because we use the timestamp in the filename
+ () -> determineBackupPathForNewBackup(backupDir).ifPresent(path -> this.performBackup(path)),
+ DELAY_BETWEEN_BACKUP_ATTEMPTS_IN_SECONDS,
+ DELAY_BETWEEN_BACKUP_ATTEMPTS_IN_SECONDS,
+ TimeUnit.SECONDS);
}
- private void fillQueue() {
- Path backupDir = BackupFileUtil.getAppDataBackupDir();
+ private void fillQueue(Path backupDir) {
if (!Files.exists(backupDir)) {
return;
}
@@ -328,13 +324,18 @@ private void fillQueue() {
/**
* Unregisters the BackupManager from the eventBus of {@link BibDatabaseContext}.
* This method should only be used when closing a database/JabRef in a normal way.
+ *
+ * @param backupDir The backup directory
+ * @param createBackup If the backup manager should still perform a backup
*/
- private void shutdown() {
+ private void shutdown(Path backupDir, boolean createBackup) {
changeFilter.unregisterListener(this);
changeFilter.shutdown();
executor.shutdown();
- // Ensure that backup is a recent one
- determineBackupPathForNewBackup().ifPresent(this::performBackup);
+ if (createBackup) {
+ // Ensure that backup is a recent one
+ determineBackupPathForNewBackup(backupDir).ifPresent(this::performBackup);
+ }
}
}
diff --git a/src/main/java/org/jabref/logic/util/io/BackupFileUtil.java b/src/main/java/org/jabref/logic/util/io/BackupFileUtil.java
index c378386c1ed..d2b42eef98f 100644
--- a/src/main/java/org/jabref/logic/util/io/BackupFileUtil.java
+++ b/src/main/java/org/jabref/logic/util/io/BackupFileUtil.java
@@ -48,12 +48,13 @@ public static Path getAppDataBackupDir() {
* (and configured in the preferences as "make backups")
*
*/
- public static Path getPathForNewBackupFileAndCreateDirectory(Path targetFile, BackupFileType fileType) {
+
+ public static Path getPathForNewBackupFileAndCreateDirectory(Path targetFile, BackupFileType fileType, Path backupDir) {
String extension = "." + fileType.getExtensions().get(0);
String timeSuffix = ZonedDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd--HH.mm.ss"));
// We choose the data directory, because a ".bak" file should survive cache cleanups
- Path directory = getAppDataBackupDir();
+ Path directory = backupDir;
try {
Files.createDirectories(directory);
} catch (IOException e) {
@@ -66,18 +67,12 @@ public static Path getPathForNewBackupFileAndCreateDirectory(Path targetFile, Ba
return directory.resolve(fileName);
}
- /**
- * Finds the latest backup (.sav). If it does not exist, an empty optional is returned
- *
- * @param targetFile the full path of the file to backup
- */
- public static Optional getPathOfLatestExistingBackupFile(Path targetFile, BackupFileType fileType) {
+ public static Optional getPathOfLatestExistingBackupFile(Path targetFile, BackupFileType fileType, Path backupDir) {
// The code is similar to "getPathForNewBackupFileAndCreateDirectory"
String extension = "." + fileType.getExtensions().get(0);
- Path directory = getAppDataBackupDir();
- if (Files.notExists(directory)) {
+ if (Files.notExists(backupDir)) {
// In case there is no app directory, we search in the directory of the bib file
Path result = FileUtil.addExtension(targetFile, extension);
if (Files.exists(result)) {
@@ -91,11 +86,11 @@ public static Optional getPathOfLatestExistingBackupFile(Path targetFile,
final String prefix = getUniqueFilePrefix(targetFile) + "--" + targetFile.getFileName();
Optional mostRecentFile;
try {
- mostRecentFile = Files.list(directory)
- // just list the .sav belonging to the given targetFile
- .filter(p -> p.getFileName().toString().startsWith(prefix))
- .sorted()
- .reduce((first, second) -> second);
+ mostRecentFile = Files.list(backupDir)
+ // just list the .sav belonging to the given targetFile
+ .filter(p -> p.getFileName().toString().startsWith(prefix))
+ .sorted()
+ .reduce((first, second) -> second);
} catch (IOException e) {
LOGGER.error("Could not determine most recent file", e);
return Optional.empty();
diff --git a/src/main/java/org/jabref/preferences/FilePreferences.java b/src/main/java/org/jabref/preferences/FilePreferences.java
index 264d778aeda..f204005afed 100644
--- a/src/main/java/org/jabref/preferences/FilePreferences.java
+++ b/src/main/java/org/jabref/preferences/FilePreferences.java
@@ -31,6 +31,8 @@ public class FilePreferences {
private final BooleanProperty fulltextIndexLinkedFiles = new SimpleBooleanProperty();
private final ObjectProperty workingDirectory = new SimpleObjectProperty<>();
private final ObservableSet externalFileTypes = FXCollections.observableSet(new TreeSet<>(Comparator.comparing(ExternalFileType::getName)));
+ private final BooleanProperty createBackup = new SimpleBooleanProperty();
+ private final ObjectProperty backupDiretory = new SimpleObjectProperty<>();
public FilePreferences(String user,
String mainFileDirectory,
@@ -40,7 +42,9 @@ public FilePreferences(String user,
boolean downloadLinkedFiles,
boolean fulltextIndexLinkedFiles,
Path workingDirectory,
- Set externalFileTypes) {
+ Set externalFileTypes,
+ boolean createBackup,
+ Path backupDirectory) {
this.user.setValue(user);
this.mainFileDirectory.setValue(mainFileDirectory);
this.storeFilesRelativeToBibFile.setValue(storeFilesRelativeToBibFile);
@@ -50,6 +54,8 @@ public FilePreferences(String user,
this.fulltextIndexLinkedFiles.setValue(fulltextIndexLinkedFiles);
this.workingDirectory.setValue(workingDirectory);
this.externalFileTypes.addAll(externalFileTypes);
+ this.createBackup.setValue(createBackup);
+ this.backupDiretory.setValue(backupDirectory);
}
public String getUser() {
@@ -147,4 +153,28 @@ public void setWorkingDirectory(Path workingDirectory) {
public ObservableSet getExternalFileTypes() {
return this.externalFileTypes;
}
+
+ public void setCreateBackup(boolean createBackup) {
+ this.createBackup.set(createBackup);
+ }
+
+ public boolean shouldCreateBackup() {
+ return this.createBackup.getValue();
+ }
+
+ public BooleanProperty createBackupProperty() {
+ return this.createBackup;
+ }
+
+ public ObjectProperty backupDirectoryProperty() {
+ return this.backupDiretory;
+ }
+
+ public void setBackupDirectory(Path backupPath) {
+ this.backupDiretory.set(backupPath);
+ }
+
+ public Path getBackupDirectory() {
+ return this.backupDiretory.getValue();
+ }
}
diff --git a/src/main/java/org/jabref/preferences/JabRefPreferences.java b/src/main/java/org/jabref/preferences/JabRefPreferences.java
index 2ed223792a2..fb9a98a535c 100644
--- a/src/main/java/org/jabref/preferences/JabRefPreferences.java
+++ b/src/main/java/org/jabref/preferences/JabRefPreferences.java
@@ -100,6 +100,7 @@
import org.jabref.logic.util.OS;
import org.jabref.logic.util.Version;
import org.jabref.logic.util.io.AutoLinkPreferences;
+import org.jabref.logic.util.io.BackupFileUtil;
import org.jabref.logic.util.io.FileHistory;
import org.jabref.logic.xmp.XmpPreferences;
import org.jabref.model.database.BibDatabaseMode;
@@ -192,6 +193,7 @@ public class JabRefPreferences implements PreferencesService {
public static final String SIZE_X = "mainWindowSizeX";
public static final String POS_Y = "mainWindowPosY";
public static final String POS_X = "mainWindowPosX";
+
public static final String LAST_EDITED = "lastEdited";
public static final String OPEN_LAST_EDITED = "openLastEdited";
public static final String LAST_FOCUSED = "lastFocused";
@@ -200,6 +202,8 @@ public class JabRefPreferences implements PreferencesService {
public static final String LAST_USED_EXPORT = "lastUsedExport";
public static final String EXPORT_WORKING_DIRECTORY = "exportWorkingDirectory";
public static final String WORKING_DIRECTORY = "workingDirectory";
+ public static final String BACKUP_DIRECTORY = "backupDirectory";
+ public static final String CREATE_BACKUP = "createBackup";
public static final String KEYWORD_SEPARATOR = "groupKeywordSeparator";
public static final String AUTO_ASSIGN_GROUP = "autoAssignGroup";
@@ -587,6 +591,9 @@ private JabRefPreferences() {
defaults.put(USE_XMP_PRIVACY_FILTER, Boolean.FALSE);
defaults.put(WORKING_DIRECTORY, USER_HOME);
defaults.put(EXPORT_WORKING_DIRECTORY, USER_HOME);
+
+ defaults.put(CREATE_BACKUP, Boolean.TRUE);
+
// Remembers working directory of last import
defaults.put(IMPORT_WORKING_DIRECTORY, USER_HOME);
defaults.put(PREFS_EXPORT_PATH, USER_HOME);
@@ -2113,8 +2120,10 @@ public FilePreferences getFilePreferences() {
getBoolean(DOWNLOAD_LINKED_FILES),
getBoolean(FULLTEXT_INDEX_LINKED_FILES),
Path.of(get(WORKING_DIRECTORY)),
- ExternalFileTypes.fromString(get(EXTERNAL_FILE_TYPES))
- );
+ ExternalFileTypes.fromString(get(EXTERNAL_FILE_TYPES)),
+ getBoolean(CREATE_BACKUP),
+ // We choose the data directory, because a ".bak" file should survive cache cleanups
+ getPath(BACKUP_DIRECTORY, BackupFileUtil.getAppDataBackupDir()));
EasyBind.listen(filePreferences.mainFileDirectoryProperty(), (obs, oldValue, newValue) -> put(MAIN_FILE_DIRECTORY, newValue));
EasyBind.listen(filePreferences.storeFilesRelativeToBibFileProperty(), (obs, oldValue, newValue) -> putBoolean(STORE_RELATIVE_TO_BIB, newValue));
@@ -2125,6 +2134,8 @@ public FilePreferences getFilePreferences() {
EasyBind.listen(filePreferences.workingDirectoryProperty(), (obs, oldValue, newValue) -> put(WORKING_DIRECTORY, newValue.toString()));
filePreferences.getExternalFileTypes().addListener((SetChangeListener) c ->
put(EXTERNAL_FILE_TYPES, ExternalFileTypes.toStringList(filePreferences.getExternalFileTypes())));
+ EasyBind.listen(filePreferences.createBackupProperty(), (obs, oldValue, newValue) -> putBoolean(CREATE_BACKUP, newValue));
+ EasyBind.listen(filePreferences.backupDirectoryProperty(), (obs, oldValue, newValue) -> put(BACKUP_DIRECTORY, newValue.toString()));
return filePreferences;
}
diff --git a/src/main/resources/l10n/JabRef_en.properties b/src/main/resources/l10n/JabRef_en.properties
index 75b6a7a35d9..985647652c5 100644
--- a/src/main/resources/l10n/JabRef_en.properties
+++ b/src/main/resources/l10n/JabRef_en.properties
@@ -2541,3 +2541,5 @@ Delete\ %0\ files=Delete %0 files
Delete\ %0\ files\ permanently\ from\ disk,\ or\ just\ remove\ the\ files\ from\ the\ entry?\ Pressing\ Delete\ will\ delete\ the\ files\ permanently\ from\ disk.=Delete %0 files permanently from disk, or just remove the files from the entry? Pressing Delete will delete the files permanently from disk.
Error\ accessing\ file\ '%0'.=Error accessing file '%0'.
This\ operation\ requires\ selected\ linked\ files.=This operation requires selected linked files.
+
+Create\ backup=Create backup
\ No newline at end of file
diff --git a/src/test/java/org/jabref/logic/autosaveandbackup/BackupManagerDiscardedTest.java b/src/test/java/org/jabref/logic/autosaveandbackup/BackupManagerDiscardedTest.java
index 7d90122bd86..ee60abcc067 100644
--- a/src/test/java/org/jabref/logic/autosaveandbackup/BackupManagerDiscardedTest.java
+++ b/src/test/java/org/jabref/logic/autosaveandbackup/BackupManagerDiscardedTest.java
@@ -40,10 +40,11 @@ class BackupManagerDiscardedTest {
private SaveConfiguration saveConfiguration;
private PreferencesService preferencesService;
private BibEntryTypesManager bibEntryTypesManager;
+ private Path backupDir;
@BeforeEach
public void setup(@TempDir Path tempDir) throws Exception {
- Path backupDir = tempDir.resolve("backups");
+ this.backupDir = tempDir.resolve("backups");
Files.createDirectories(backupDir);
testBib = tempDir.resolve("test.bib");
@@ -84,14 +85,14 @@ private void databaseModification() {
}
private void makeBackup() {
- backupManager.determineBackupPathForNewBackup().ifPresent(backupManager::performBackup);
+ backupManager.determineBackupPathForNewBackup(backupDir).ifPresent(path -> backupManager.performBackup(path));
}
@Test
public void noDiscardingAChangeLeadsToNewerBackupBeReported() throws Exception {
databaseModification();
makeBackup();
- assertTrue(BackupManager.backupFileDiffers(testBib));
+ assertTrue(BackupManager.backupFileDiffers(testBib, backupDir));
}
@Test
@@ -99,14 +100,14 @@ public void noDiscardingASavedChange() throws Exception {
databaseModification();
makeBackup();
saveDatabase();
- assertFalse(BackupManager.backupFileDiffers(testBib));
+ assertFalse(BackupManager.backupFileDiffers(testBib, backupDir));
}
@Test
public void discardingAChangeLeadsToNewerBackupToBeIgnored() throws Exception {
databaseModification();
makeBackup();
- backupManager.discardBackup();
- assertFalse(BackupManager.backupFileDiffers(testBib));
+ backupManager.discardBackup(backupDir);
+ assertFalse(BackupManager.backupFileDiffers(testBib, backupDir));
}
}
diff --git a/src/test/java/org/jabref/logic/autosaveandbackup/BackupManagerTest.java b/src/test/java/org/jabref/logic/autosaveandbackup/BackupManagerTest.java
index e99e3a3d8e8..7bf69d79f91 100644
--- a/src/test/java/org/jabref/logic/autosaveandbackup/BackupManagerTest.java
+++ b/src/test/java/org/jabref/logic/autosaveandbackup/BackupManagerTest.java
@@ -4,22 +4,47 @@
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.nio.file.attribute.FileTime;
+import java.util.Collections;
+import java.util.List;
+import java.util.Optional;
import org.jabref.logic.util.BackupFileType;
import org.jabref.logic.util.io.BackupFileUtil;
-
+import org.jabref.model.database.BibDatabase;
+import org.jabref.model.database.BibDatabaseContext;
+import org.jabref.model.entry.BibEntryTypesManager;
+import org.jabref.model.groups.event.GroupUpdatedEvent;
+import org.jabref.model.metadata.MetaData;
+import org.jabref.model.metadata.event.MetaDataChangedEvent;
+import org.jabref.preferences.FilePreferences;
+import org.jabref.preferences.PreferencesService;
+
+import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.io.TempDir;
+import org.mockito.Answers;
+import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
public class BackupManagerTest {
+ Path backupDir;
+
+ @BeforeEach
+ void setup(@TempDir Path tempDir) {
+ backupDir = tempDir.resolve("backup");
+ }
+
@Test
public void backupFileNameIsCorrectlyGeneratedInAppDataDirectory() {
Path bibPath = Path.of("tmp", "test.bib");
- Path bakPath = BackupManager.getBackupPathForNewBackup(bibPath);
+ backupDir = BackupFileUtil.getAppDataBackupDir();
+ Path bakPath = BackupManager.getBackupPathForNewBackup(bibPath, backupDir);
// Pattern is "27182d3c--test.bib--", but the hashing is implemented differently on Linux than on Windows
assertNotEquals("", bakPath);
@@ -28,29 +53,29 @@ public void backupFileNameIsCorrectlyGeneratedInAppDataDirectory() {
@Test
public void backupFileIsEqualForNonExistingBackup() throws Exception {
Path originalFile = Path.of(BackupManagerTest.class.getResource("no-autosave.bib").toURI());
- assertFalse(BackupManager.backupFileDiffers(originalFile));
+ assertFalse(BackupManager.backupFileDiffers(originalFile, backupDir));
}
@Test
public void backupFileIsEqual() throws Exception {
// Prepare test: Create backup file on "right" path
Path source = Path.of(BackupManagerTest.class.getResource("no-changes.bib.bak").toURI());
- Path target = BackupFileUtil.getPathForNewBackupFileAndCreateDirectory(Path.of(BackupManagerTest.class.getResource("no-changes.bib").toURI()), BackupFileType.BACKUP);
+ Path target = BackupFileUtil.getPathForNewBackupFileAndCreateDirectory(Path.of(BackupManagerTest.class.getResource("no-changes.bib").toURI()), BackupFileType.BACKUP, backupDir);
Files.copy(source, target, StandardCopyOption.REPLACE_EXISTING);
Path originalFile = Path.of(BackupManagerTest.class.getResource("no-changes.bib").toURI());
- assertFalse(BackupManager.backupFileDiffers(originalFile));
+ assertFalse(BackupManager.backupFileDiffers(originalFile, backupDir));
}
@Test
public void backupFileDiffers() throws Exception {
// Prepare test: Create backup file on "right" path
Path source = Path.of(BackupManagerTest.class.getResource("changes.bib.bak").toURI());
- Path target = BackupFileUtil.getPathForNewBackupFileAndCreateDirectory(Path.of(BackupManagerTest.class.getResource("changes.bib").toURI()), BackupFileType.BACKUP);
+ Path target = BackupFileUtil.getPathForNewBackupFileAndCreateDirectory(Path.of(BackupManagerTest.class.getResource("changes.bib").toURI()), BackupFileType.BACKUP, backupDir);
Files.copy(source, target, StandardCopyOption.REPLACE_EXISTING);
Path originalFile = Path.of(BackupManagerTest.class.getResource("changes.bib").toURI());
- assertTrue(BackupManager.backupFileDiffers(originalFile));
+ assertTrue(BackupManager.backupFileDiffers(originalFile, backupDir));
}
@Test
@@ -60,13 +85,13 @@ public void correctBackupFileDeterminedForMultipleBakFiles() throws Exception {
// Prepare test: Create backup files on "right" path
// most recent file does not have any changes
- Path target = BackupFileUtil.getPathForNewBackupFileAndCreateDirectory(noChangesBib, BackupFileType.BACKUP);
+ Path target = BackupFileUtil.getPathForNewBackupFileAndCreateDirectory(noChangesBib, BackupFileType.BACKUP, backupDir);
Files.copy(noChangesBibBak, target, StandardCopyOption.REPLACE_EXISTING);
// create "older" .bak files containing changes
for (int i = 0; i < 10; i++) {
Path changesBibBak = Path.of(BackupManagerTest.class.getResource("changes.bib").toURI());
- Path directory = BackupFileUtil.getAppDataBackupDir();
+ Path directory = backupDir;
String timeSuffix = "2020-02-03--00.00.0" + Integer.toString(i);
String fileName = BackupFileUtil.getUniqueFilePrefix(noChangesBib) + "--no-changes.bib--" + timeSuffix + ".bak";
target = directory.resolve(fileName);
@@ -74,7 +99,7 @@ public void correctBackupFileDeterminedForMultipleBakFiles() throws Exception {
}
Path originalFile = noChangesBib;
- assertFalse(BackupManager.backupFileDiffers(originalFile));
+ assertFalse(BackupManager.backupFileDiffers(originalFile, backupDir));
}
@Test
@@ -82,10 +107,10 @@ public void bakFileWithNewerTimeStampLeadsToDiff() throws Exception {
Path changesBib = Path.of(BackupManagerTest.class.getResource("changes.bib").toURI());
Path changesBibBak = Path.of(BackupManagerTest.class.getResource("changes.bib.bak").toURI());
- Path target = BackupFileUtil.getPathForNewBackupFileAndCreateDirectory(changesBib, BackupFileType.BACKUP);
+ Path target = BackupFileUtil.getPathForNewBackupFileAndCreateDirectory(changesBib, BackupFileType.BACKUP, backupDir);
Files.copy(changesBibBak, target, StandardCopyOption.REPLACE_EXISTING);
- assertTrue(BackupManager.backupFileDiffers(changesBib));
+ assertTrue(BackupManager.backupFileDiffers(changesBib, backupDir));
}
@Test
@@ -93,12 +118,63 @@ public void bakFileWithOlderTimeStampDoesNotLeadToDiff() throws Exception {
Path changesBib = Path.of(BackupManagerTest.class.getResource("changes.bib").toURI());
Path changesBibBak = Path.of(BackupManagerTest.class.getResource("changes.bib.bak").toURI());
- Path target = BackupFileUtil.getPathForNewBackupFileAndCreateDirectory(changesBib, BackupFileType.BACKUP);
+ Path target = BackupFileUtil.getPathForNewBackupFileAndCreateDirectory(changesBib, BackupFileType.BACKUP, backupDir);
Files.copy(changesBibBak, target, StandardCopyOption.REPLACE_EXISTING);
// Make .bak file very old
Files.setLastModifiedTime(target, FileTime.fromMillis(0));
- assertFalse(BackupManager.backupFileDiffers(changesBib));
+ assertFalse(BackupManager.backupFileDiffers(changesBib, backupDir));
+ }
+
+ @Test
+ public void shouldNotCreateABackup(@TempDir Path customDir) throws Exception {
+ Path backupDir = customDir.resolve("subBackupDir");
+ Files.createDirectories(backupDir);
+
+ var database = new BibDatabaseContext(new BibDatabase());
+ database.setDatabasePath(customDir.resolve("Bibfile.bib"));
+
+ var preferences = mock(PreferencesService.class, Answers.RETURNS_DEEP_STUBS);
+ var filePreferences = mock(FilePreferences.class);
+ when(preferences.getFilePreferences()).thenReturn(filePreferences);
+ when(filePreferences.getBackupDirectory()).thenReturn(backupDir);
+
+ BackupManager manager = BackupManager.start(database, mock(BibEntryTypesManager.class, Answers.RETURNS_DEEP_STUBS), preferences);
+ manager.listen(new MetaDataChangedEvent(new MetaData()));
+
+ BackupManager.shutdown(database, backupDir, false);
+
+ List files = Files.list(backupDir).toList();
+ assertEquals(Collections.emptyList(), files);
+ }
+
+ @Test
+ public void shouldCreateABackup(@TempDir Path customDir) throws Exception {
+ Path backupDir = customDir.resolve("subBackupDir");
+ Files.createDirectories(backupDir);
+
+ var database = new BibDatabaseContext(new BibDatabase());
+ database.setDatabasePath(customDir.resolve("Bibfile.bib"));
+
+ var preferences = mock(PreferencesService.class, Answers.RETURNS_DEEP_STUBS);
+ var filePreferences = mock(FilePreferences.class);
+ when(preferences.getFilePreferences()).thenReturn(filePreferences);
+ when(filePreferences.getBackupDirectory()).thenReturn(backupDir);
+ when(filePreferences.shouldCreateBackup()).thenReturn(true);
+
+ BackupManager manager = BackupManager.start(database, mock(BibEntryTypesManager.class, Answers.RETURNS_DEEP_STUBS), preferences);
+ manager.listen(new MetaDataChangedEvent(new MetaData()));
+
+ Optional fullBackupPath = manager.determineBackupPathForNewBackup(backupDir);
+ fullBackupPath.ifPresent(manager::performBackup);
+ manager.listen(new GroupUpdatedEvent(new MetaData()));
+
+ BackupManager.shutdown(database, backupDir, true);
+
+ List files = Files.list(backupDir).sorted().toList();
+ // we only know the first backup path because the second one is created on shutdown
+ // due to timing issues we cannot test that reliable
+ assertEquals(fullBackupPath.get(), files.get(0));
}
}
diff --git a/src/test/java/org/jabref/logic/util/io/BackupFileUtilTest.java b/src/test/java/org/jabref/logic/util/io/BackupFileUtilTest.java
index f44e6a45e1e..bedea46b580 100644
--- a/src/test/java/org/jabref/logic/util/io/BackupFileUtilTest.java
+++ b/src/test/java/org/jabref/logic/util/io/BackupFileUtilTest.java
@@ -6,7 +6,9 @@
import org.jabref.logic.util.BackupFileType;
+import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.io.TempDir;
import org.mockito.Answers;
import org.mockito.MockedStatic;
import org.mockito.Mockito;
@@ -16,6 +18,13 @@
public class BackupFileUtilTest {
+ Path backupDir;
+
+ @BeforeEach
+ void setup(@TempDir Path tempDir) {
+ backupDir = tempDir.resolve("backup");
+ }
+
@Test
void uniqueFilePrefix() {
// We cannot test for a concrete hash code, because hashing implementation differs from environment to environment
@@ -25,18 +34,20 @@ void uniqueFilePrefix() {
@Test
void getPathOfBackupFileAndCreateDirectoryReturnsAppDirectoryInCaseOfNoError() {
String start = BackupFileUtil.getAppDataBackupDir().toString();
- String result = BackupFileUtil.getPathForNewBackupFileAndCreateDirectory(Path.of("test.bib"), BackupFileType.BACKUP).toString();
+ backupDir = BackupFileUtil.getAppDataBackupDir();
+ String result = BackupFileUtil.getPathForNewBackupFileAndCreateDirectory(Path.of("test.bib"), BackupFileType.BACKUP, backupDir).toString();
// We just check the prefix
assertEquals(start, result.substring(0, start.length()));
}
@Test
void getPathOfBackupFileAndCreateDirectoryReturnsSameDirectoryInCaseOfException() {
+ backupDir = BackupFileUtil.getAppDataBackupDir();
try (MockedStatic files = Mockito.mockStatic(Files.class, Answers.RETURNS_DEEP_STUBS)) {
files.when(() -> Files.createDirectories(BackupFileUtil.getAppDataBackupDir()))
.thenThrow(new IOException());
Path testPath = Path.of("tmp", "test.bib");
- Path result = BackupFileUtil.getPathForNewBackupFileAndCreateDirectory(testPath, BackupFileType.BACKUP);
+ Path result = BackupFileUtil.getPathForNewBackupFileAndCreateDirectory(testPath, BackupFileType.BACKUP, backupDir);
// The intended fallback behavior is to put the .bak file in the same directory as the .bib file
assertEquals(Path.of("tmp", "test.bib.bak"), result);
}