From 7fda4cc791eb43d5f5f15371829724bae9d813c9 Mon Sep 17 00:00:00 2001 From: David Stevens Date: Fri, 6 Mar 2020 10:49:15 +0100 Subject: [PATCH] Add ShortScience integration (#6018) --- CHANGELOG.md | 1 + .../jabref/gui/actions/StandardActions.java | 1 + .../jabref/gui/maintable/RightClickMenu.java | 1 + .../maintable/SearchShortScienceAction.java | 50 +++++++++++++++++ .../logic/util/ExternalLinkCreator.java | 34 +++++++++++ src/main/resources/l10n/JabRef_en.properties | 4 ++ .../logic/util/ExternalLinkCreatorTest.java | 56 +++++++++++++++++++ 7 files changed, 147 insertions(+) create mode 100644 src/main/java/org/jabref/gui/maintable/SearchShortScienceAction.java create mode 100644 src/main/java/org/jabref/logic/util/ExternalLinkCreator.java create mode 100644 src/test/java/org/jabref/logic/util/ExternalLinkCreatorTest.java diff --git a/CHANGELOG.md b/CHANGELOG.md index 24af6f3b56c..622d0f9797e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ We refer to [GitHub issues](https://github.com/JabRef/jabref/issues) by using `# - Filenames of external files can no longer contain curly braces. [#5926](https://github.com/JabRef/jabref/pull/5926) - We made the filters more easily accessible in the integrity check dialog. [#5955](https://github.com/JabRef/jabref/pull/5955) - We reimplemented and improved the dialog "Customize entry types" [#4719](https://github.com/JabRef/jabref/issues/4719) +- We added support for searching ShortScience for an entry through the user's browser. [#6018](https://github.com/JabRef/jabref/pull/6018) ### Fixed diff --git a/src/main/java/org/jabref/gui/actions/StandardActions.java b/src/main/java/org/jabref/gui/actions/StandardActions.java index 930d710a495..10d70f444fb 100644 --- a/src/main/java/org/jabref/gui/actions/StandardActions.java +++ b/src/main/java/org/jabref/gui/actions/StandardActions.java @@ -33,6 +33,7 @@ public enum StandardActions implements Action { SEND_AS_EMAIL(Localization.lang("Send as email"), IconTheme.JabRefIcons.EMAIL), OPEN_EXTERNAL_FILE(Localization.lang("Open file"), IconTheme.JabRefIcons.FILE, KeyBinding.OPEN_FILE), OPEN_URL(Localization.lang("Open URL or DOI"), IconTheme.JabRefIcons.WWW, KeyBinding.OPEN_URL_OR_DOI), + SEARCH_SHORTSCIENCE(Localization.lang("Search ShortScience")), MERGE_WITH_FETCHED_ENTRY(Localization.lang("Get BibTeX data from %0", "DOI/ISBN/...")), ATTACH_FILE(Localization.lang("Attach file"), IconTheme.JabRefIcons.ATTACH_FILE), PRIORITY(Localization.lang("Priority"), IconTheme.JabRefIcons.PRIORITY), diff --git a/src/main/java/org/jabref/gui/maintable/RightClickMenu.java b/src/main/java/org/jabref/gui/maintable/RightClickMenu.java index 279211c7457..359569fbfd4 100644 --- a/src/main/java/org/jabref/gui/maintable/RightClickMenu.java +++ b/src/main/java/org/jabref/gui/maintable/RightClickMenu.java @@ -63,6 +63,7 @@ public static ContextMenu create(BibEntryTableViewModel entry, KeyBindingReposit contextMenu.getItems().add(factory.createMenuItem(StandardActions.OPEN_FOLDER, new OpenFolderAction(dialogService, stateManager, preferencesService))); contextMenu.getItems().add(factory.createMenuItem(StandardActions.OPEN_EXTERNAL_FILE, new OpenExternalFileAction(dialogService, stateManager, preferencesService))); contextMenu.getItems().add(factory.createMenuItem(StandardActions.OPEN_URL, new OpenUrlAction(dialogService, stateManager))); + contextMenu.getItems().add(factory.createMenuItem(StandardActions.SEARCH_SHORTSCIENCE, new SearchShortScienceAction(dialogService, stateManager))); contextMenu.getItems().add(new SeparatorMenuItem()); diff --git a/src/main/java/org/jabref/gui/maintable/SearchShortScienceAction.java b/src/main/java/org/jabref/gui/maintable/SearchShortScienceAction.java new file mode 100644 index 00000000000..663fd5be697 --- /dev/null +++ b/src/main/java/org/jabref/gui/maintable/SearchShortScienceAction.java @@ -0,0 +1,50 @@ +package org.jabref.gui.maintable; + +import java.io.IOException; +import java.util.List; + +import javafx.beans.binding.BooleanExpression; + +import org.jabref.gui.DialogService; +import org.jabref.gui.StateManager; +import org.jabref.gui.actions.SimpleCommand; +import org.jabref.gui.desktop.JabRefDesktop; +import org.jabref.logic.l10n.Localization; +import org.jabref.logic.util.ExternalLinkCreator; +import org.jabref.model.entry.BibEntry; +import org.jabref.model.entry.field.StandardField; + +import static org.jabref.gui.actions.ActionHelper.isFieldSetForSelectedEntry; +import static org.jabref.gui.actions.ActionHelper.needsEntriesSelected; + +public class SearchShortScienceAction extends SimpleCommand { + private final DialogService dialogService; + private final StateManager stateManager; + + public SearchShortScienceAction(DialogService dialogService, StateManager stateManager) { + this.dialogService = dialogService; + this.stateManager = stateManager; + + BooleanExpression fieldIsSet = isFieldSetForSelectedEntry(StandardField.TITLE, stateManager); + this.executable.bind(needsEntriesSelected(1, stateManager).and(fieldIsSet)); + } + + @Override + public void execute() { + stateManager.getActiveDatabase().ifPresent(databaseContext -> { + final List bibEntries = stateManager.getSelectedEntries(); + + if (bibEntries.size() != 1) { + dialogService.notify(Localization.lang("This operation requires exactly one item to be selected.")); + return; + } + ExternalLinkCreator.getShortScienceSearchURL(bibEntries.get(0)).ifPresent(url -> { + try { + JabRefDesktop.openExternalViewer(databaseContext, url, StandardField.URL); + } catch (IOException ex) { + dialogService.showErrorDialogAndWait(Localization.lang("Unable to open ShortScience."), ex); + } + }); + }); + } +} diff --git a/src/main/java/org/jabref/logic/util/ExternalLinkCreator.java b/src/main/java/org/jabref/logic/util/ExternalLinkCreator.java new file mode 100644 index 00000000000..8e98318b544 --- /dev/null +++ b/src/main/java/org/jabref/logic/util/ExternalLinkCreator.java @@ -0,0 +1,34 @@ +package org.jabref.logic.util; + +import java.net.URISyntaxException; +import java.util.Optional; + +import org.jabref.model.entry.BibEntry; +import org.jabref.model.entry.field.StandardField; + +import org.apache.http.client.utils.URIBuilder; + +public class ExternalLinkCreator { + private static final String SHORTSCIENCE_SEARCH_URL = "https://www.shortscience.org/internalsearch"; + + /** + * Get a URL to the search results of ShortScience for the BibEntry's title + * + * @param entry The entry to search for. Expects the BibEntry's title to be set for successful return. + * @return The URL if it was successfully created + */ + public static Optional getShortScienceSearchURL(BibEntry entry) { + return entry.getField(StandardField.TITLE).map(title -> { + URIBuilder uriBuilder; + try { + uriBuilder = new URIBuilder(SHORTSCIENCE_SEARCH_URL); + } catch (URISyntaxException e) { + // This should never be able to happen as it would require the field to be misconfigured. + throw new AssertionError("ShortScience URL is invalid.", e); + } + // Direct the user to the search results for the title. + uriBuilder.addParameter("q", title); + return uriBuilder.toString(); + }); + } +} diff --git a/src/main/resources/l10n/JabRef_en.properties b/src/main/resources/l10n/JabRef_en.properties index 5c3d694d816..a36946affb4 100644 --- a/src/main/resources/l10n/JabRef_en.properties +++ b/src/main/resources/l10n/JabRef_en.properties @@ -2114,5 +2114,9 @@ Optional=Optional Required=Required Entry\ type\ cannot\ be\ empty.\ Please\ enter\ a\ name.=Entry type cannot be empty. Please enter a name. Field\ cannot\ be\ empty.\ Please\ enter\ a\ name.=Field cannot be empty. Please enter a name. + +Search\ ShortScience=Search ShortScience +Unable\ to\ open\ ShortScience.=Unable to open ShortScience. + Shared\ database=Shared database Lookup=Lookup diff --git a/src/test/java/org/jabref/logic/util/ExternalLinkCreatorTest.java b/src/test/java/org/jabref/logic/util/ExternalLinkCreatorTest.java new file mode 100644 index 00000000000..736518e843d --- /dev/null +++ b/src/test/java/org/jabref/logic/util/ExternalLinkCreatorTest.java @@ -0,0 +1,56 @@ +package org.jabref.logic.util; + +import java.net.MalformedURLException; +import java.net.URISyntaxException; +import java.net.URL; +import java.util.Optional; + +import org.jabref.model.entry.BibEntry; +import org.jabref.model.entry.field.StandardField; + +import org.junit.jupiter.api.Test; + +import static org.jabref.logic.util.ExternalLinkCreator.getShortScienceSearchURL; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +class ExternalLinkCreatorTest { + + /** + * Validates URL conformance to RFC2396. Does not perform complex checks such as opening connections. + */ + private boolean urlIsValid(String url) { + try { + // This will throw on non-compliance to RFC2396. + new URL(url).toURI(); + return true; + } catch (MalformedURLException | URISyntaxException e) { + return false; + } + } + + @Test + void getShortScienceSearchURLEncodesSpecialCharacters() { + BibEntry entry = new BibEntry(); + String rfc3986ReservedCharacters = "!*'();:@&=+$,/?#[]"; + entry.setField(StandardField.TITLE, rfc3986ReservedCharacters); + Optional url = getShortScienceSearchURL(entry); + assertTrue(url.isPresent()); + assertTrue(urlIsValid(url.get())); + } + + @Test + void getShortScienceSearchURLReturnsEmptyOnMissingTitle() { + BibEntry entry = new BibEntry(); + assertEquals(Optional.empty(), getShortScienceSearchURL(entry)); + } + + @Test + void getShortScienceSearchURLLinksToSearchResults() { + // Take an arbitrary article name + BibEntry entry = new BibEntry().withField(StandardField.TITLE, "JabRef bibliography management"); + Optional url = getShortScienceSearchURL(entry); + // Expected behaviour is to link to the search results page, /internalsearch + assertEquals(Optional.of("https://www.shortscience.org/internalsearch?q=JabRef+bibliography+management"), url); + } +}