diff --git a/CHANGELOG.md b/CHANGELOG.md index 3886e8993e5..1deb3884306 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -41,6 +41,7 @@ Note that this project **does not** adhere to [Semantic Versioning](http://semve - We fixed an issue where opening the changelog from withing JabRef led to a 404 error [#8563](https://github.com/JabRef/jabref/issues/8563) - We fixed an issue where not all found unlinked local files were imported correctly due to some race condition. [#8444](https://github.com/JabRef/jabref/issues/8444) - We fixed an issue where Merge entries dialog exceeds screen boundaries. +- We fixed an issue where no citationkey was generated on import, pasting a doi or an entry on the main table [8406](https://github.com/JabRef/jabref/issues/8406), [koppor#553](https://github.com/koppor/jabref/issues/553) ### Removed diff --git a/src/main/java/org/jabref/gui/EntryTypeViewModel.java b/src/main/java/org/jabref/gui/EntryTypeViewModel.java index d8255ee0fa7..c48a061158f 100644 --- a/src/main/java/org/jabref/gui/EntryTypeViewModel.java +++ b/src/main/java/org/jabref/gui/EntryTypeViewModel.java @@ -14,13 +14,11 @@ import javafx.concurrent.Task; import javafx.concurrent.Worker; -import org.jabref.gui.duplicationFinder.DuplicateResolverDialog; +import org.jabref.gui.externalfiles.ImportHandler; +import org.jabref.gui.externalfiletype.ExternalFileTypes; import org.jabref.gui.importer.NewEntryAction; -import org.jabref.logic.citationkeypattern.CitationKeyGenerator; -import org.jabref.logic.database.DuplicateCheck; import org.jabref.logic.importer.FetcherException; import org.jabref.logic.importer.IdBasedFetcher; -import org.jabref.logic.importer.ImportCleanup; import org.jabref.logic.importer.WebFetchers; import org.jabref.logic.importer.fetcher.DoiFetcher; import org.jabref.logic.l10n.Localization; @@ -153,28 +151,10 @@ public void runFetcherWorker() { Optional result = fetcherWorker.getValue(); if (result.isPresent()) { final BibEntry entry = result.get(); - ImportCleanup cleanup = new ImportCleanup(libraryTab.getBibDatabaseContext().getMode()); - cleanup.doPostCleanup(entry); - Optional duplicate = new DuplicateCheck(Globals.entryTypesManager).containsDuplicate(libraryTab.getDatabase(), entry, libraryTab.getBibDatabaseContext().getMode()); - if (duplicate.isPresent()) { - DuplicateResolverDialog dialog = new DuplicateResolverDialog(entry, duplicate.get(), DuplicateResolverDialog.DuplicateResolverType.IMPORT_CHECK, libraryTab.getBibDatabaseContext(), stateManager); - switch (dialogService.showCustomDialogAndWait(dialog) - .orElse(DuplicateResolverDialog.DuplicateResolverResult.BREAK)) { - case KEEP_LEFT -> { - libraryTab.getDatabase().removeEntry(duplicate.get()); - libraryTab.getDatabase().insertEntry(entry); - } - case KEEP_BOTH -> libraryTab.getDatabase().insertEntry(entry); - case KEEP_MERGE -> { - libraryTab.getDatabase().removeEntry(duplicate.get()); - libraryTab.getDatabase().insertEntry(dialog.getMergedEntry()); - } - } - } else { - // Regenerate CiteKey of imported BibEntry - new CitationKeyGenerator(libraryTab.getBibDatabaseContext(), preferencesService.getCitationKeyPatternPreferences()).generateAndSetKey(entry); - libraryTab.insertEntry(entry); - } + + ImportHandler handler = new ImportHandler(libraryTab.getBibDatabaseContext(), ExternalFileTypes.getInstance(), preferencesService, Globals.getFileUpdateMonitor(), libraryTab.getUndoManager(), stateManager, dialogService); + handler.importEntryWithDuplicateCheck(libraryTab.getBibDatabaseContext(), entry); + searchSuccesfulProperty.set(true); } else if (StringUtil.isBlank(idText.getValue())) { dialogService.showWarningDialogAndWait(Localization.lang("Empty search ID"), Localization.lang("The given search ID was empty.")); diff --git a/src/main/java/org/jabref/gui/LibraryTab.java b/src/main/java/org/jabref/gui/LibraryTab.java index 1d487cbea9d..3c38c3f7801 100644 --- a/src/main/java/org/jabref/gui/LibraryTab.java +++ b/src/main/java/org/jabref/gui/LibraryTab.java @@ -757,7 +757,7 @@ public void copy() { } public void paste() { - mainTable.paste(this.bibDatabaseContext.getMode()); + mainTable.paste(); } public void cut() { diff --git a/src/main/java/org/jabref/gui/bibtexextractor/BibtexExtractorViewModel.java b/src/main/java/org/jabref/gui/bibtexextractor/BibtexExtractorViewModel.java index aee3adde536..3472e69d466 100644 --- a/src/main/java/org/jabref/gui/bibtexextractor/BibtexExtractorViewModel.java +++ b/src/main/java/org/jabref/gui/bibtexextractor/BibtexExtractorViewModel.java @@ -54,7 +54,8 @@ public BibtexExtractorViewModel(BibDatabaseContext bibdatabaseContext, preferencesService, fileUpdateMonitor, undoManager, - stateManager); + stateManager, + dialogService); } public StringProperty inputTextProperty() { diff --git a/src/main/java/org/jabref/gui/externalfiles/ImportHandler.java b/src/main/java/org/jabref/gui/externalfiles/ImportHandler.java index 0cb650d805f..c639fdab635 100644 --- a/src/main/java/org/jabref/gui/externalfiles/ImportHandler.java +++ b/src/main/java/org/jabref/gui/externalfiles/ImportHandler.java @@ -6,16 +6,21 @@ import java.util.Collection; import java.util.Collections; import java.util.List; +import java.util.Optional; import javax.swing.undo.CompoundEdit; import javax.swing.undo.UndoManager; +import org.jabref.gui.DialogService; +import org.jabref.gui.Globals; import org.jabref.gui.StateManager; +import org.jabref.gui.duplicationFinder.DuplicateResolverDialog; import org.jabref.gui.externalfiletype.ExternalFileTypes; import org.jabref.gui.undo.UndoableInsertEntries; import org.jabref.gui.util.BackgroundTask; import org.jabref.gui.util.DefaultTaskExecutor; import org.jabref.logic.citationkeypattern.CitationKeyGenerator; +import org.jabref.logic.database.DuplicateCheck; import org.jabref.logic.externalfiles.ExternalFilesContentImporter; import org.jabref.logic.importer.ImportCleanup; import org.jabref.logic.l10n.Localization; @@ -36,25 +41,28 @@ public class ImportHandler { private static final Logger LOGGER = LoggerFactory.getLogger(ImportHandler.class); - private final BibDatabaseContext bibdatabase; + private final BibDatabaseContext bibDatabaseContext; private final PreferencesService preferencesService; private final FileUpdateMonitor fileUpdateMonitor; private final ExternalFilesEntryLinker linker; private final ExternalFilesContentImporter contentImporter; private final UndoManager undoManager; private final StateManager stateManager; + private final DialogService dialogService; public ImportHandler(BibDatabaseContext database, ExternalFileTypes externalFileTypes, PreferencesService preferencesService, FileUpdateMonitor fileupdateMonitor, UndoManager undoManager, - StateManager stateManager) { + StateManager stateManager, + DialogService dialogService) { - this.bibdatabase = database; + this.bibDatabaseContext = database; this.preferencesService = preferencesService; this.fileUpdateMonitor = fileupdateMonitor; this.stateManager = stateManager; + this.dialogService = dialogService; this.linker = new ExternalFilesEntryLinker(externalFileTypes, preferencesService.getFilePreferences(), database); this.contentImporter = new ExternalFilesContentImporter( @@ -127,7 +135,7 @@ protected List call() { // We need to run the actual import on the FX Thread, otherwise we will get some deadlocks with the UIThreadList DefaultTaskExecutor.runInJavaFXThread(() -> importEntries(entriesToAdd)); - ce.addEdit(new UndoableInsertEntries(bibdatabase.getDatabase(), entriesToAdd)); + ce.addEdit(new UndoableInsertEntries(bibDatabaseContext.getDatabase(), entriesToAdd)); ce.end(); undoManager.addEdit(ce); @@ -151,9 +159,9 @@ private BibEntry createEmptyEntryWithLink(Path file) { } public void importEntries(List entries) { - ImportCleanup cleanup = new ImportCleanup(bibdatabase.getMode()); + ImportCleanup cleanup = new ImportCleanup(bibDatabaseContext.getMode()); cleanup.doPostCleanup(entries); - bibdatabase.getDatabase().insertEntries(entries); + bibDatabaseContext.getDatabase().insertEntries(entries); // Set owner/timestamp UpdateField.setAutomaticFields(entries, @@ -166,7 +174,43 @@ public void importEntries(List entries) { } // Add to group - addToGroups(entries, stateManager.getSelectedGroup(bibdatabase)); + addToGroups(entries, stateManager.getSelectedGroup(bibDatabaseContext)); + } + + public void importEntryWithDuplicateCheck(BibDatabaseContext bibDatabaseContext, BibEntry entry) { + ImportCleanup cleanup = new ImportCleanup(bibDatabaseContext.getMode()); + BibEntry cleanedEntry = cleanup.doPostCleanup(entry); + BibEntry entryToInsert = cleanedEntry; + + Optional existingDuplicateInLibrary = new DuplicateCheck(Globals.entryTypesManager).containsDuplicate(bibDatabaseContext.getDatabase(), entryToInsert, bibDatabaseContext.getMode()); + if (existingDuplicateInLibrary.isPresent()) { + DuplicateResolverDialog dialog = new DuplicateResolverDialog(existingDuplicateInLibrary.get(), entryToInsert, DuplicateResolverDialog.DuplicateResolverType.IMPORT_CHECK, bibDatabaseContext, stateManager); + switch (dialogService.showCustomDialogAndWait(dialog).orElse(DuplicateResolverDialog.DuplicateResolverResult.BREAK)) { + case KEEP_LEFT: + bibDatabaseContext.getDatabase().removeEntry(existingDuplicateInLibrary.get()); + break; + case KEEP_BOTH: + break; + case KEEP_MERGE: + bibDatabaseContext.getDatabase().removeEntry(existingDuplicateInLibrary.get()); + entryToInsert = dialog.getMergedEntry(); + break; + default: + return; + } + } + // Regenerate CiteKey of imported BibEntry + if (preferencesService.getImporterPreferences().isGenerateNewKeyOnImport()) { + generateKeys(List.of(entryToInsert)); + } + bibDatabaseContext.getDatabase().insertEntry(entryToInsert); + + // Set owner/timestamp + UpdateField.setAutomaticFields(List.of(entryToInsert), + preferencesService.getOwnerPreferences(), + preferencesService.getTimestampPreferences()); + + addToGroups(List.of(entry), stateManager.getSelectedGroup(this.bibDatabaseContext)); } private void addToGroups(List entries, Collection groups) { @@ -189,9 +233,9 @@ private void addToGroups(List entries, Collection group */ private void generateKeys(List entries) { CitationKeyGenerator keyGenerator = new CitationKeyGenerator( - bibdatabase.getMetaData().getCiteKeyPattern(preferencesService.getCitationKeyPatternPreferences() - .getKeyPattern()), - bibdatabase.getDatabase(), + bibDatabaseContext.getMetaData().getCiteKeyPattern(preferencesService.getCitationKeyPatternPreferences() + .getKeyPattern()), + bibDatabaseContext.getDatabase(), preferencesService.getCitationKeyPatternPreferences()); for (BibEntry entry : entries) { diff --git a/src/main/java/org/jabref/gui/externalfiles/UnlinkedFilesDialogViewModel.java b/src/main/java/org/jabref/gui/externalfiles/UnlinkedFilesDialogViewModel.java index c516be37f86..7d19b2bdc98 100644 --- a/src/main/java/org/jabref/gui/externalfiles/UnlinkedFilesDialogViewModel.java +++ b/src/main/java/org/jabref/gui/externalfiles/UnlinkedFilesDialogViewModel.java @@ -97,7 +97,8 @@ public UnlinkedFilesDialogViewModel(DialogService dialogService, preferences, fileUpdateMonitor, undoManager, - stateManager); + stateManager, + dialogService); this.fileFilterList = FXCollections.observableArrayList( new FileExtensionViewModel(StandardFileType.ANY_FILE, externalFileTypes), diff --git a/src/main/java/org/jabref/gui/importer/GenerateEntryFromIdAction.java b/src/main/java/org/jabref/gui/importer/GenerateEntryFromIdAction.java index d0f86008ab1..ad1b5b78780 100644 --- a/src/main/java/org/jabref/gui/importer/GenerateEntryFromIdAction.java +++ b/src/main/java/org/jabref/gui/importer/GenerateEntryFromIdAction.java @@ -7,13 +7,13 @@ import org.jabref.gui.LibraryTab; import org.jabref.gui.StateManager; import org.jabref.gui.actions.SimpleCommand; +import org.jabref.gui.externalfiles.ImportHandler; +import org.jabref.gui.externalfiletype.ExternalFileTypes; import org.jabref.gui.util.BackgroundTask; import org.jabref.gui.util.TaskExecutor; import org.jabref.logic.JabRefException; -import org.jabref.logic.database.DuplicateCheck; import org.jabref.logic.importer.CompositeIdFetcher; import org.jabref.logic.importer.FetcherException; -import org.jabref.logic.importer.ImportCleanup; import org.jabref.logic.l10n.Localization; import org.jabref.model.entry.BibEntry; import org.jabref.model.entry.types.StandardEntryType; @@ -50,26 +50,32 @@ public void execute() { backgroundTask.onFailure((e) -> { // When unable to import by ID, present the user options to cancel or add entry manually boolean addEntryFlag = dialogService.showConfirmationDialogAndWait(Localization.lang("Failed to import by ID"), - e.getMessage(), - Localization.lang("Add entry manually")); + e.getMessage(), + Localization.lang("Add entry manually")); if (addEntryFlag) { // add entry manually new NewEntryAction(libraryTab.frame(), StandardEntryType.Article, dialogService, - preferencesService, stateManager).execute(); + preferencesService, stateManager).execute(); } }); - backgroundTask.onSuccess((bibEntry) -> bibEntry.ifPresentOrElse((entry) -> { - libraryTab.insertEntry(entry); - entryFromIdPopOver.hide(); - dialogService.notify(Localization.lang("Imported one entry")); - }, - () -> dialogService.notify(Localization.lang("Import canceled")) - )); + backgroundTask.onSuccess((bibEntry) -> { + Optional result = bibEntry; + if (result.isPresent()) { + final BibEntry entry = result.get(); + ImportHandler handler = new ImportHandler(libraryTab.getBibDatabaseContext(), ExternalFileTypes.getInstance(), preferencesService, Globals.getFileUpdateMonitor(), libraryTab.getUndoManager(), stateManager, dialogService); + handler.importEntryWithDuplicateCheck(libraryTab.getBibDatabaseContext(), entry); + } else { + dialogService.notify("No entry found or import canceled"); + } + + entryFromIdPopOver.hide(); + }); backgroundTask.executeWith(taskExecutor); } private BackgroundTask> searchAndImportEntryInBackground() { return new BackgroundTask<>() { + @Override protected Optional call() throws JabRefException { if (isCanceled()) { @@ -78,21 +84,8 @@ protected Optional call() throws JabRefException { updateMessage(Localization.lang("Searching...")); try { - Optional result = new CompositeIdFetcher(preferencesService.getImportFormatPreferences()).performSearchById(identifier); - if (result.isPresent()) { - final BibEntry entry = result.get(); - ImportCleanup cleanup = new ImportCleanup(libraryTab.getBibDatabaseContext().getMode()); - cleanup.doPostCleanup(entry); - // DuplicateCheck only covers DOI and ISBN at the moment. - Optional duplicate = new DuplicateCheck(Globals.entryTypesManager).containsDuplicate(libraryTab.getDatabase(), entry, libraryTab.getBibDatabaseContext().getMode()); - if (duplicate.isPresent()) { - throw new JabRefException(Localization.lang("Entry already exists")); - } - } else { - throw new JabRefException(Localization.lang("Could not find any bibliographic information.")); - } - updateMessage(Localization.lang("Imported one entry")); - return result; + return new CompositeIdFetcher(preferencesService.getImportFormatPreferences()).performSearchById(identifier); + } catch (FetcherException fetcherException) { throw new JabRefException("Fetcher error: %s".formatted(fetcherException.getMessage())); } diff --git a/src/main/java/org/jabref/gui/importer/ImportEntriesViewModel.java b/src/main/java/org/jabref/gui/importer/ImportEntriesViewModel.java index 52ccab28413..1eed96197a6 100644 --- a/src/main/java/org/jabref/gui/importer/ImportEntriesViewModel.java +++ b/src/main/java/org/jabref/gui/importer/ImportEntriesViewModel.java @@ -169,7 +169,8 @@ private void buildImportHandlerThenImportEntries(List entriesToImport) preferences, fileUpdateMonitor, undoManager, - stateManager); + stateManager, + dialogService); importHandler.importEntries(entriesToImport); dialogService.notify(Localization.lang("Number of entries successfully imported") + ": " + entriesToImport.size()); } diff --git a/src/main/java/org/jabref/gui/maintable/MainTable.java b/src/main/java/org/jabref/gui/maintable/MainTable.java index fc783262bc0..4fdc4add495 100644 --- a/src/main/java/org/jabref/gui/maintable/MainTable.java +++ b/src/main/java/org/jabref/gui/maintable/MainTable.java @@ -41,10 +41,8 @@ import org.jabref.gui.util.CustomLocalDragboard; import org.jabref.gui.util.DefaultTaskExecutor; import org.jabref.gui.util.ViewModelTableRowFactory; -import org.jabref.logic.importer.ImportCleanup; import org.jabref.logic.l10n.Localization; import org.jabref.model.database.BibDatabaseContext; -import org.jabref.model.database.BibDatabaseMode; import org.jabref.model.database.event.EntriesAddedEvent; import org.jabref.model.entry.BibEntry; import org.jabref.preferences.PreferencesService; @@ -92,7 +90,8 @@ public MainTable(MainTableDataModel model, preferencesService, Globals.getFileUpdateMonitor(), undoManager, - stateManager); + stateManager, + dialogService); localDragboard = stateManager.getLocalDragboard(); @@ -303,12 +302,13 @@ private void clearAndSelectLast() { scrollTo(getItems().size() - 1); } - public void paste(BibDatabaseMode bibDatabaseMode) { + public void paste() { // Find entries in clipboard List entriesToAdd = Globals.getClipboardManager().extractData(); - ImportCleanup cleanup = new ImportCleanup(bibDatabaseMode); - cleanup.doPostCleanup(entriesToAdd); - libraryTab.insertEntries(entriesToAdd); + + for (BibEntry entry : entriesToAdd) { + importHandler.importEntryWithDuplicateCheck(database, entry); + } if (!entriesToAdd.isEmpty()) { this.requestFocus(); } diff --git a/src/main/resources/l10n/JabRef_en.properties b/src/main/resources/l10n/JabRef_en.properties index 0d85c62a5c1..0fefcd90936 100644 --- a/src/main/resources/l10n/JabRef_en.properties +++ b/src/main/resources/l10n/JabRef_en.properties @@ -1902,8 +1902,6 @@ Manage\ protected\ terms=Manage protected terms New\ entry\ from\ plain\ text=New entry from plain text Import\ by\ ID=Import by ID Enter\ a\ valid\ ID=Enter a valid ID -Imported\ one\ entry=Imported one entry -Entry\ already\ exists=Entry already exists New\ sublibrary\ based\ on\ AUX\ file=New sublibrary based on AUX file Push\ entries\ to\ external\ application\ (%0)=Push entries to external application (%0) Quit=Quit