-
-
Notifications
You must be signed in to change notification settings - Fork 2.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Integrate mrdlib redesign #4361
Changes from 9 commits
cfa4356
1320270
0416402
86350c9
25e5cb8
045c15d
69c6383
3518596
4215e13
55ca696
4ccbea6
440db51
40af08c
4d5df3d
761ed98
b4dabe0
bfe5abf
fdd1e03
fb2fc79
fd82562
9c769b1
4357ada
493d5e6
571618c
ecf9eb5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,82 +1,161 @@ | ||
package org.jabref.gui.entryeditor; | ||
|
||
import java.net.URL; | ||
import java.io.IOException; | ||
import java.util.List; | ||
import java.util.Optional; | ||
|
||
import javafx.event.ActionEvent; | ||
import javafx.event.EventHandler; | ||
import javafx.scene.control.Button; | ||
import javafx.scene.control.Hyperlink; | ||
import javafx.scene.control.ProgressIndicator; | ||
import javafx.scene.control.ScrollPane; | ||
import javafx.scene.control.Tooltip; | ||
import javafx.scene.layout.HBox; | ||
import javafx.scene.layout.StackPane; | ||
import javafx.scene.web.WebView; | ||
import javafx.scene.layout.VBox; | ||
import javafx.scene.text.Font; | ||
import javafx.scene.text.FontPosture; | ||
import javafx.scene.text.Text; | ||
|
||
import org.jabref.Globals; | ||
import org.jabref.gui.icon.IconTheme; | ||
import org.jabref.gui.desktop.JabRefDesktop; | ||
import org.jabref.gui.util.BackgroundTask; | ||
import org.jabref.gui.util.OpenHyperlinksInExternalBrowser; | ||
import org.jabref.logic.importer.fetcher.MrDLibFetcher; | ||
import org.jabref.logic.l10n.Localization; | ||
import org.jabref.model.entry.BibEntry; | ||
import org.jabref.model.entry.FieldName; | ||
import org.jabref.preferences.JabRefPreferences; | ||
|
||
import org.slf4j.Logger; | ||
import org.slf4j.LoggerFactory; | ||
|
||
/** | ||
* GUI for tab displaying article recommendations based on the currently selected BibEntry | ||
*/ | ||
public class RelatedArticlesTab extends EntryEditorTab { | ||
|
||
private final EntryEditorPreferences preferences; | ||
private static final Logger LOGGER = LoggerFactory.getLogger(RelatedArticlesTab.class); | ||
|
||
public RelatedArticlesTab(EntryEditorPreferences preferences) { | ||
setText(Localization.lang("Related articles")); | ||
setTooltip(new Tooltip(Localization.lang("Related articles"))); | ||
this.preferences = preferences; | ||
} | ||
|
||
private StackPane getPane(BibEntry entry) { | ||
/** | ||
* Gets a StackPane of related article information to be displayed in the Related Articles tab | ||
* @param entry The currently selected BibEntry on the JabRef UI. | ||
* @return A StackPane with related article information to be displayed in the Related Articles tab. | ||
*/ | ||
private StackPane getRelatedArticlesPane(BibEntry entry) { | ||
StackPane root = new StackPane(); | ||
root.setStyle("-fx-padding: 20 20 20 20"); | ||
ProgressIndicator progress = new ProgressIndicator(); | ||
progress.setMaxSize(100, 100); | ||
WebView browser = new WebView(); | ||
root.getChildren().addAll(browser, progress); | ||
|
||
MrDLibFetcher fetcher = new MrDLibFetcher(Globals.prefs.get(JabRefPreferences.LANGUAGE), | ||
Globals.BUILD_INFO.getVersion().getFullVersion()); | ||
Globals.BUILD_INFO.getVersion().getFullVersion()); | ||
BackgroundTask | ||
.wrap(() -> fetcher.performSearch(entry)) | ||
.onRunning(() -> progress.setVisible(true)) | ||
.onSuccess(relatedArticles -> { | ||
progress.setVisible(false); | ||
browser.getEngine().loadContent(convertToHtml(relatedArticles)); | ||
}) | ||
.executeWith(Globals.TASK_EXECUTOR); | ||
.wrap(() -> fetcher.performSearch(entry)) | ||
.onRunning(() -> progress.setVisible(true)) | ||
.onSuccess(relatedArticles -> { | ||
progress.setVisible(false); | ||
root.getChildren().add(getRelatedArticleInfo(relatedArticles)); | ||
}) | ||
.executeWith(Globals.TASK_EXECUTOR); | ||
|
||
browser.getEngine().getLoadWorker().stateProperty().addListener(new OpenHyperlinksInExternalBrowser(browser)); | ||
root.getChildren().add(progress); | ||
|
||
return root; | ||
} | ||
|
||
/** | ||
* Takes a List of HTML snippets stored in the field "html_representation" of a list of bibentries | ||
* | ||
* @param list of bib entries having a field html_representation | ||
* Creates a VBox of the related article information to be used in the StackPane displayed in the Related Articles tab | ||
* @param list List of BibEntries of related articles | ||
* @return VBox of related article descriptions to be displayed in the Related Articles tab | ||
*/ | ||
private String convertToHtml(List<BibEntry> list) { | ||
StringBuilder htmlContent = new StringBuilder(); | ||
URL url = IconTheme.getIconUrl("mdlListIcon"); | ||
htmlContent | ||
.append("<html><head><title></title></head><body bgcolor='#ffffff'>"); | ||
htmlContent.append("<ul style='list-style-image:("); | ||
htmlContent.append(url); | ||
htmlContent.append(")'>"); | ||
list.stream() | ||
.map(bibEntry -> bibEntry.getField("html_representation")) | ||
.filter(Optional::isPresent) | ||
.map(o -> "<li style='margin: 5px'>" + o.get() + "</li>") | ||
.forEach(html -> htmlContent.append(html)); | ||
htmlContent.append("</ul>"); | ||
htmlContent.append("<br><div style='margin-left: 5px'>"); | ||
htmlContent.append( | ||
"<a href='http://mr-dlib.org/information-for-users/information-about-mr-dlib-for-jabref-users/#' target=\"_blank\">"); | ||
htmlContent.append(Localization.lang("What is Mr. DLib?")); | ||
htmlContent.append("</a></div>"); | ||
htmlContent.append("</body></html>"); | ||
return htmlContent.toString(); | ||
private VBox getRelatedArticleInfo(List<BibEntry> list) { | ||
VBox vBox = new VBox(); | ||
vBox.setSpacing(20.0); | ||
|
||
for (BibEntry entry : list) { | ||
HBox hBox = new HBox(); | ||
hBox.setSpacing(5.0); | ||
|
||
String title = entry.getTitle().orElse(""); | ||
String journal = entry.getField(FieldName.JOURNAL).orElse(""); | ||
String authors = entry.getField(FieldName.AUTHOR).orElse(""); | ||
String year = entry.getField(FieldName.YEAR).orElse(""); | ||
|
||
Hyperlink titleLink = new Hyperlink(title); | ||
Text journalText = new Text(journal); | ||
journalText.setFont(Font.font(Font.getDefault().getFamily(), FontPosture.ITALIC, Font.getDefault().getSize())); | ||
Text authorsText = new Text(authors); | ||
Text yearText = new Text("(" + year + ")"); | ||
titleLink.setOnAction(new EventHandler<ActionEvent>() { | ||
@Override | ||
public void handle(ActionEvent event) { | ||
if (entry.getField(FieldName.URL).isPresent()) { | ||
try { | ||
JabRefDesktop.openBrowser(entry.getField(FieldName.URL).get()); | ||
} catch (IOException e) { | ||
LOGGER.error("Error opening the browser to: " + entry.getField(FieldName.URL).get(), e); | ||
e.printStackTrace(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please use one of the |
||
} | ||
} | ||
} | ||
}); | ||
|
||
hBox.getChildren().addAll(titleLink, journalText, authorsText, yearText); | ||
vBox.getChildren().add(hBox); | ||
} | ||
return vBox; | ||
} | ||
|
||
/** | ||
* Returns a consent dialog used to ask permission to send data to Mr. DLib. | ||
* @param entry Currently selected BibEntry. (required to allow reloading of pane if accepted) | ||
* @return StackPane returned to be placed into Related Articles tab. | ||
*/ | ||
private ScrollPane getPrivacyDialog(BibEntry entry) { | ||
ScrollPane root = new ScrollPane(); | ||
VBox vbox = new VBox(); | ||
vbox.getStyleClass().add("gdpr-dialog"); | ||
vbox.setSpacing(20.0); | ||
|
||
Button button = new Button("I Agree"); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Use |
||
button.getStyleClass().add("accept-button"); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
Text line1 = new Text("Mr. DLib is an external service which provides article recommendations based on the currently selected entry. Data about the selected entry must be sent to Mr. DLib in order to provide these recommendations. Do you agree that this data may be sent?"); | ||
|
||
line1.setWrappingWidth(1300.0); | ||
Text line2 = new Text("This setting may be changed in preferences at any time."); | ||
Hyperlink mdlLink = new Hyperlink("Further information about Mr DLib. for JabRef users."); | ||
mdlLink.setOnAction(new EventHandler<ActionEvent>() { | ||
@Override | ||
public void handle(ActionEvent event) { | ||
try { | ||
JabRefDesktop.openBrowser("http://mr-dlib.org/information-for-users/information-about-mr-dlib-for-jabref-users/"); | ||
} catch (IOException e) { | ||
LOGGER.error("Error opening the browser to: " + entry.getField(FieldName.URL).get(), e); | ||
} | ||
} | ||
}); | ||
|
||
button.setOnAction(new EventHandler<ActionEvent>() { | ||
@Override | ||
public void handle(ActionEvent event) { | ||
JabRefPreferences prefs = JabRefPreferences.getInstance(); | ||
prefs.putBoolean(JabRefPreferences.ACCEPT_RECOMMENDATIONS, true); | ||
setContent(getRelatedArticlesPane(entry)); | ||
} | ||
}); | ||
|
||
root.setStyle("-fx-padding: 20 20 20 20"); | ||
vbox.getChildren().addAll(line1, mdlLink, line2, button); | ||
root.setContent(vbox); | ||
|
||
return root; | ||
} | ||
|
||
@Override | ||
|
@@ -86,6 +165,11 @@ public boolean shouldShow(BibEntry entry) { | |
|
||
@Override | ||
protected void bindToEntry(BibEntry entry) { | ||
setContent(getPane(entry)); | ||
// Ask for consent to send data to Mr. DLib on first time to tab | ||
if (JabRefPreferences.getInstance().getBoolean(JabRefPreferences.ACCEPT_RECOMMENDATIONS)) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please use the EntryEditorPreferences preferences object for the check There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hi Christoph, thanks for your help. I had been using EntryEditorPreferences, but switched because the object doesn't get updated until the application is restarted. This resulted in the dialog still appearing after accepting, until the application was restarted. Is there a way of doing this such that the preferences are refreshed on each load of the tab? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. At the moment, this is not possible. However, please use the EntryEditorPreferences, we are trying to get rid of the global JabRef preference class. It is a bit of a nuance that you need to restart JabRef, but in this case I prefer the better code quality. |
||
setContent(getRelatedArticlesPane(entry)); | ||
} else { | ||
setContent(getPrivacyDialog(entry)); | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please set this in the css file.