Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
20aa15e
gui changes: add a delete metadata button under Tools. functionality:…
vithlaithla Apr 12, 2022
cd595cf
add checkbox to select all fields in preference for delete purpose
JeffersonJefferson-pixel Apr 17, 2022
4a8b503
fix conflict
vithlaithla Apr 17, 2022
0f1157f
add text to pass LocalizationConsistencyTest
vithlaithla Apr 17, 2022
ced851f
fixes previously added files' code style to jabref's
vithlaithla Apr 18, 2022
e676757
Revert "fixes previously added files' code style to jabref's"
vithlaithla Apr 18, 2022
841d0e1
fixes previous commit's (0f1157f71c65153b8c8b0514cc116aef3da1435a) co…
vithlaithla Apr 18, 2022
35e1b52
use StringJoiner and fix usage of Localization
vithlaithla Apr 18, 2022
81ab83a
create temp file to remove errors of write and read at the same time …
vithlaithla Apr 19, 2022
498675f
write some initial tests
JeffersonJefferson-pixel Apr 19, 2022
d16b4d9
added javadoc
vithlaithla Apr 19, 2022
4564ad5
rewrite to dublin core after deleting
JeffersonJefferson-pixel Apr 19, 2022
c85bcef
add mock return value of xmpPreferences.getSelectAllfields() for expo…
JeffersonJefferson-pixel Apr 19, 2022
76f45ba
fix localization
vithlaithla Apr 19, 2022
713b9ef
remove cache from option dialog and unncessary bib export
vithlaithla Apr 19, 2022
c7c7697
added changes to CHANGELOG.md
vithlaithla Apr 19, 2022
17fa29a
Merge branch 'main' of https://github.com/vithlaithla/jabref into fix…
vithlaithla Apr 19, 2022
c6168dc
CHANGELOG.md had checkstyle errors around headings, so added a blank …
vithlaithla Apr 19, 2022
72ee75a
Merge branch 'main' of https://github.com/vithlaithla/jabref into rev…
vithlaithla Apr 21, 2022
11f7770
fix checkstyle
vithlaithla Apr 21, 2022
c21285f
no longer consider whether fields to be deleted are also in libray da…
JeffersonJefferson-pixel Apr 21, 2022
9cffa99
fix localization
vithlaithla Apr 22, 2022
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
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ Note that this project **does not** adhere to [Semantic Versioning](http://semve

### Added

- We added a checkbox to select all fields inside XmpPreference Tab [#8277](https://github.com/JabRef/jabref/issues/8277)
- We added a button to delete metadata of selected fields inside XmpPreference Tab without overwriting other fields [#8277](https://github.com/JabRef/jabref/issues/8277)
- We added an extra option when right-clicking an entry in the Entry List to copy either the DOI or the DOI url.
- We added a fetcher for [Directory of Open Access Books (DOAB)](https://doabooks.org/) [8576](https://github.com/JabRef/jabref/issues/8576)
- We added an extra option to ask the user whether they want to open to reveal the folder holding the saved file with the file selected. [#8195](https://github.com/JabRef/jabref/issues/8195)
Expand Down
2 changes: 2 additions & 0 deletions src/main/java/org/jabref/gui/JabRefFrame.java
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@
import org.jabref.gui.edit.ReplaceStringAction;
import org.jabref.gui.entryeditor.OpenEntryEditorAction;
import org.jabref.gui.entryeditor.PreviewSwitchAction;
import org.jabref.gui.exporter.DeleteMetadataAction;
import org.jabref.gui.exporter.ExportCommand;
import org.jabref.gui.exporter.ExportToClipboardAction;
import org.jabref.gui.exporter.SaveAction;
Expand Down Expand Up @@ -825,6 +826,7 @@ private MenuBar createMenu() {
new SeparatorMenuItem(),

factory.createMenuItem(StandardActions.WRITE_METADATA_TO_PDF, new WriteMetadataToPdfAction(stateManager, Globals.entryTypesManager, prefs.getFieldWriterPreferences(), dialogService, taskExecutor, prefs.getFilePreferences(), prefs.getXmpPreferences())),
factory.createMenuItem(StandardActions.DELETE_METADATA, new DeleteMetadataAction(stateManager, prefs.getGeneralPreferences().getDefaultBibDatabaseMode(), Globals.entryTypesManager, prefs.getFieldWriterPreferences(), dialogService, taskExecutor, prefs.getFilePreferences(), prefs.getXmpPreferences())),
factory.createMenuItem(StandardActions.COPY_LINKED_FILES, new CopyFilesAction(dialogService, prefs, stateManager)),

new SeparatorMenuItem(),
Expand Down
2 changes: 2 additions & 0 deletions src/main/java/org/jabref/gui/actions/StandardActions.java
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,8 @@ public enum StandardActions implements Action {
PARSE_LATEX(Localization.lang("Search for citations in LaTeX files..."), IconTheme.JabRefIcons.LATEX_CITATIONS),
NEW_SUB_LIBRARY_FROM_AUX(Localization.lang("New sublibrary based on AUX file") + "...", Localization.lang("New BibTeX sublibrary") + Localization.lang("This feature generates a new library based on which entries are needed in an existing LaTeX document."), IconTheme.JabRefIcons.NEW),
WRITE_METADATA_TO_PDF(Localization.lang("Write metadata to PDF files"), Localization.lang("Will write metadata to the PDFs linked from selected entries."), KeyBinding.WRITE_METADATA_TO_PDF),
DELETE_METADATA(Localization.lang("Delete metadata from PDFs"), Localization.lang("Will delete metadata fields from PDFs listed in preferences linked from selected entries.")),

START_NEW_STUDY(Localization.lang("Start new systematic literature review")),
SEARCH_FOR_EXISTING_STUDY(Localization.lang("Perform search for existing systematic literature review")),
OPEN_DATABASE_FOLDER(Localization.lang("Reveal in file explorer")),
Expand Down
251 changes: 251 additions & 0 deletions src/main/java/org/jabref/gui/exporter/DeleteMetadataAction.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,251 @@
package org.jabref.gui.exporter;

import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
import java.util.StringJoiner;
import java.util.stream.Collectors;

import javafx.application.Platform;
import javafx.geometry.Insets;
import javafx.scene.control.Button;
import javafx.scene.control.ScrollPane;
import javafx.scene.control.TextArea;
import javafx.scene.input.KeyCode;
import javafx.scene.layout.Background;
import javafx.scene.layout.BackgroundFill;
import javafx.scene.layout.CornerRadii;
import javafx.scene.layout.GridPane;
import javafx.scene.paint.Color;
import javafx.stage.Stage;

import org.jabref.gui.DialogService;
import org.jabref.gui.FXDialog;
import org.jabref.gui.StateManager;
import org.jabref.gui.actions.SimpleCommand;
import org.jabref.gui.util.BackgroundTask;
import org.jabref.gui.util.TaskExecutor;
import org.jabref.logic.bibtex.FieldWriterPreferences;
import org.jabref.logic.exporter.EmbeddedBibFilePdfExporter;
import org.jabref.logic.l10n.Localization;
import org.jabref.logic.util.io.FileUtil;
import org.jabref.logic.xmp.XmpPreferences;
import org.jabref.logic.xmp.XmpUtilRemover;
import org.jabref.model.database.BibDatabase;
import org.jabref.model.database.BibDatabaseMode;
import org.jabref.model.entry.BibEntry;
import org.jabref.model.entry.BibEntryTypesManager;
import org.jabref.preferences.FilePreferences;

import static org.jabref.gui.actions.ActionHelper.needsDatabase;

public class DeleteMetadataAction extends SimpleCommand {

private final StateManager stateManager;
private final DialogService dialogService;
private final TaskExecutor taskExecutor;
private final FilePreferences filePreferences;
private final XmpPreferences xmpPreferences;
private final EmbeddedBibFilePdfExporter embeddedBibExporter;

private OptionsDialog optionsDialog;

private BibDatabase database;
private Collection<BibEntry> entries;

private boolean shouldContinue = true;
private int skipped;
private int entriesChanged;
private int errors;

public DeleteMetadataAction(StateManager stateManager, BibDatabaseMode databaseMode, BibEntryTypesManager entryTypesManager, FieldWriterPreferences fieldWriterPreferences, DialogService dialogService, TaskExecutor taskExecutor, FilePreferences filePreferences, XmpPreferences xmpPreferences) {
this.stateManager = stateManager;
this.dialogService = dialogService;
this.taskExecutor = taskExecutor;
this.filePreferences = filePreferences;
this.xmpPreferences = xmpPreferences;
this.embeddedBibExporter = new EmbeddedBibFilePdfExporter(databaseMode, entryTypesManager, fieldWriterPreferences);

this.executable.bind(needsDatabase(stateManager));
}

@Override
public void execute() {
init();
BackgroundTask.wrap(this::deleteMetadata)
.executeWith(taskExecutor);
}

public void init() {
if (stateManager.getActiveDatabase().isEmpty()) {
return;
}

database = stateManager.getActiveDatabase().get().getDatabase();
// Get entries and check if it makes sense to perform this operation
entries = stateManager.getSelectedEntries();

if (entries.isEmpty()) {
entries = database.getEntries();
if (entries.isEmpty()) {
dialogService.showErrorDialogAndWait(
Localization.lang("Delete metadata from PDFs"),
Localization.lang("This operation requires one or more entries to be selected."));
shouldContinue = false;
return;
} else {
boolean confirm = dialogService.showConfirmationDialogAndWait(
Localization.lang("Delete metadata from PDFs"),
Localization.lang("Delete metadata for all PDFs in current library?"));
if (confirm) {
shouldContinue = false;
return;
}
}
}

errors = 0;
entriesChanged = 0;
skipped = 0;

optionsDialog = new OptionsDialog();
optionsDialog.open();
dialogService.notify(Localization.lang("Deleting metadata..."));
}

private void deleteMetadata() {
// use StringJoiner for messages
StringJoiner stringJoiner = new StringJoiner("\n");
if (!shouldContinue || stateManager.getActiveDatabase().isEmpty()) {
return;
}
for (BibEntry entry : entries) {
// Make a list of all PDFs linked from this entry:
List<Path> files = entry.getFiles().stream()
.map(file -> file.findIn(stateManager.getActiveDatabase().get(), filePreferences))
.filter(Optional::isPresent)
.map(Optional::get)
.filter(path -> FileUtil.isPDFFile(path))
.collect(Collectors.toList());
if (files.isEmpty()) {
skipped++;
stringJoiner.add(" " + Localization.lang("Skipped - No PDF linked") + ".");
} else {
for (Path file : files) {
if (Files.exists(file)) {
try {
DeleteMetadataFromFile(file);
stringJoiner.add(" " + Localization.lang("OK") + ".");
entriesChanged++;
} catch (Exception e) {
stringJoiner.add(" " + Localization.lang("Error while deleting '%0'", file.toString()) + ":");
stringJoiner.add(" " + e.getLocalizedMessage());
errors++;
}
} else {
skipped++;
stringJoiner.add(" " + Localization.lang("Skipped - PDF does not exist") + ":");
stringJoiner.add(" " + file.toString());
}
}
}
if (optionsDialog.isCanceled()) {
stringJoiner.add("\n" + Localization.lang("Operation canceled."));
break;
}
}
stringJoiner.add("\n"
+ Localization.lang("Finished deleting metadata for %0 file (%1 skipped, %2 errors).", String
.valueOf(entriesChanged), String.valueOf(skipped), String.valueOf(errors)));
Platform.runLater(() -> {
optionsDialog.getProgressArea()
.appendText(stringJoiner.toString());
optionsDialog.done();
});

if (!shouldContinue) {
return;
}
dialogService.notify(Localization.lang("Finished deleting metadata for %0 file (%1 skipped, %2 errors).",
String.valueOf(entriesChanged), String.valueOf(skipped), String.valueOf(errors)));
}

/**
* This deletes XMP data
*
* @param file The file to delete metadata from
*/
synchronized private void DeleteMetadataFromFile(Path file) throws Exception {
XmpUtilRemover.deleteXmp(file, xmpPreferences);
}

class OptionsDialog extends FXDialog {

private final Button okButton = new Button(Localization.lang("OK"));
private final Button cancelButton = new Button(Localization.lang("Cancel"));

private boolean isCancelled;

private final TextArea progressArea;

public OptionsDialog() {
super(AlertType.NONE, Localization.lang("Deleting metadata for selected entries..."), false);
okButton.setDisable(true);
okButton.setOnAction(e -> dispose());
okButton.setPrefSize(100, 30);
cancelButton.setOnAction(e -> isCancelled = true);
cancelButton.setOnKeyPressed(e -> {
if (e.getCode() == KeyCode.ESCAPE) {
isCancelled = true;
}
});
cancelButton.setPrefSize(100, 30);
progressArea = new TextArea();
ScrollPane scrollPane = new ScrollPane(progressArea);
progressArea.setBackground(new Background(new BackgroundFill(Color.WHITE, CornerRadii.EMPTY, Insets.EMPTY)));
progressArea.setEditable(false);
progressArea.setText("");

GridPane tmpPanel = new GridPane();
getDialogPane().setContent(tmpPanel);
tmpPanel.setHgap(450);
tmpPanel.setVgap(10);
tmpPanel.add(scrollPane, 0, 0, 2, 1);
tmpPanel.add(okButton, 0, 1);
tmpPanel.add(cancelButton, 1, 1);
tmpPanel.setGridLinesVisible(false);
this.setResizable(false);
}

private void dispose() {
((Stage) (getDialogPane().getScene().getWindow())).close();
}

public void done() {
okButton.setDisable(false);
cancelButton.setDisable(true);
}

public void open() {
progressArea.setText("");
isCancelled = false;

okButton.setDisable(true);
cancelButton.setDisable(false);

okButton.requestFocus();

optionsDialog.show();
}

public boolean isCanceled() {
return isCancelled;
}

public TextArea getProgressArea() {
return progressArea;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,14 @@
xmlns="http://javafx.com/javafx" xmlns:fx="http://javafx.com/fxml"
fx:controller="org.jabref.gui.preferences.xmp.XmpPrivacyTab">
<Label styleClass="titleHeader" text="%XMP export privacy settings"/>
<CheckBox fx:id="enableXmpFilter" text="%Do not write the following fields to XMP Metadata"/>
<CheckBox fx:id="enableXmpFilter" text="%Prevent writing following metadata fields from PDFs / Removing following metadata fields from PDFs"/>
<Label fx:id="descriptionLabel1" styleClass="info-section"
text="%-Do not write the following XMP metadata fields to PDFs, when using &quot;write metadata to PDF files &quot;. Additionally, choosing this option and using &quot;write metadata to PDF files &quot; will also remove existing metadata from PDFs, if one or multiple of the following fields are ALSO present in the entry, whose metadata is supposed to be written to PDF."
wrapText="true"/>
<Label fx:id="descriptionLabel2" styleClass="info-section"
text="%-Selected XMP metadata fields will be completely removed from PDFs if choosing &quot;Delete metadata from PDFs &quot;."
wrapText="true"/>
<CheckBox fx:id="selectAllFields" text="%Select all fields"/>
<HBox alignment="BOTTOM_LEFT" spacing="4.0">
<VBox spacing="4.0">
<TableView fx:id="filterList" prefHeight="300.0" prefWidth="200.0">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
public class XmpPrivacyTab extends AbstractPreferenceTabView<XmpPrivacyTabViewModel> implements PreferencesTab {

@FXML private CheckBox enableXmpFilter;
@FXML private CheckBox selectAllFields;
@FXML private TableView<Field> filterList;
@FXML private TableColumn<Field, Field> fieldColumn;
@FXML private TableColumn<Field, Field> actionsColumn;
Expand All @@ -50,9 +51,16 @@ public void initialize() {
this.viewModel = new XmpPrivacyTabViewModel(dialogService, preferencesService.getXmpPreferences());

enableXmpFilter.selectedProperty().bindBidirectional(viewModel.xmpFilterEnabledProperty());
selectAllFields.selectedProperty().bindBidirectional(viewModel.allFieldsSelectedProperty());
selectAllFields.disableProperty().bind(viewModel.xmpFilterEnabledProperty().not());

filterList.disableProperty().bind(viewModel.xmpFilterEnabledProperty().not());
addFieldName.disableProperty().bind(viewModel.xmpFilterEnabledProperty().not());
addField.disableProperty().bind(viewModel.xmpFilterEnabledProperty().not());
// these are only enabled if and only if xmp-filter is checked and select-all-field is not checked
filterList.disableProperty().bind(viewModel.allFieldsSelectedProperty());
addFieldName.disableProperty().bind(viewModel.allFieldsSelectedProperty());
addField.disableProperty().bind(viewModel.allFieldsSelectedProperty());

fieldColumn.setSortable(true);
fieldColumn.setReorderable(false);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
public class XmpPrivacyTabViewModel implements PreferenceTabViewModel {

private final BooleanProperty xmpFilterEnabledProperty = new SimpleBooleanProperty();
private final BooleanProperty allFieldsSelectedProperty = new SimpleBooleanProperty();
private final ListProperty<Field> xmpFilterListProperty = new SimpleListProperty<>(FXCollections.observableArrayList());
private final ListProperty<Field> availableFieldsProperty = new SimpleListProperty<>(FXCollections.observableArrayList());
private final ObjectProperty<Field> addFieldProperty = new SimpleObjectProperty<>();
Expand All @@ -50,6 +51,7 @@ public class XmpPrivacyTabViewModel implements PreferenceTabViewModel {
@Override
public void setValues() {
xmpFilterEnabledProperty.setValue(xmpPreferences.shouldUseXmpPrivacyFilter());
allFieldsSelectedProperty.setValue(xmpPreferences.getSelectAllFields().getValue());

xmpFilterListProperty.clear();
xmpFilterListProperty.addAll(xmpPreferences.getXmpPrivacyFilter());
Expand All @@ -62,6 +64,7 @@ public void setValues() {
@Override
public void storeSettings() {
xmpPreferences.setUseXmpPrivacyFilter(xmpFilterEnabledProperty.getValue());
xmpPreferences.setSelectAllFields(allFieldsSelectedProperty.getValue());
xmpPreferences.getXmpPrivacyFilter().clear();
xmpPreferences.getXmpPrivacyFilter().addAll(xmpFilterListProperty.getValue());
}
Expand Down Expand Up @@ -100,6 +103,10 @@ public BooleanProperty xmpFilterEnabledProperty() {
return xmpFilterEnabledProperty;
}

public BooleanProperty allFieldsSelectedProperty() {
return allFieldsSelectedProperty;
}

public ListProperty<Field> filterListProperty() {
return xmpFilterListProperty;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -413,7 +413,7 @@ public void fillDublinCoreSchema() {
fieldValues.addAll(bibEntry.getFieldMap().entrySet());
boolean hasStandardYearField = fieldValues.stream().anyMatch(field -> StandardField.YEAR.equals(field.getKey()));
for (Entry<Field, String> field : fieldValues) {
if (useXmpPrivacyFilter && xmpPreferences.getXmpPrivacyFilter().contains(field.getKey())) {
if (useXmpPrivacyFilter && (xmpPreferences.getSelectAllFields().getValue() || xmpPreferences.getXmpPrivacyFilter().contains(field.getKey()))) {
continue;
}

Expand Down
Loading