-
-
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
Export pdf/linked files #3147
Export pdf/linked files #3147
Conversation
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.
I've tested it locally and it works in principle.
There's one thing I would like you to add, though: logging: When I do the export, I would like to have a log statement that says where I exported to, or alternatively if I tried to export and it failed. This will make error diagnosis later much easier. And you can expect to get issue reports, because this is something that works with the file system, so there are users who will use it in weird circumstances.
|
||
public class ExportLinkedFilesAction extends AbstractAction { | ||
|
||
private final BiFunction<Path, Path, Path> resolvePathFilename = (p, f) -> { |
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 use meaningful names (not p, f) even in lambdas
private final BiFunction<Path, Path, Path> resolvePathFilename = (p, f) -> { | ||
return p.resolve(f.getFileName()); | ||
}; | ||
private final DialogService ds = new FXDialogService(); |
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.
Same here, better name please.
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.
Not fixed.
|
||
long totalFilesCount = entries.stream().flatMap(entry -> entry.getFiles().stream()).count(); | ||
|
||
Service<Void> service = new Service<Void>() { |
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.
The enclosing actionPerformed()
is a really big method. Please do some refactoring and make it smaller. The first thing would be to extract this class, at least to an inner class instead of an anonymous one.
service.start(); | ||
|
||
DefaultTaskExecutor.runInJavaFXThread(() -> { | ||
ProgressDialog dlg = new ProgressDialog(service); |
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.
And again, more meaningful names please :-)
@Siedlerchr Your PR did not update as you named the branches differently: |
Current accessibility: Reading #2706 (comment) and the following comments, I do not like the word "Export" in the Tools menu. It took me 5 minutes to find the functionality.
Suggestion: Rename to "Copy attached files to folder..." Further minor issues |
Added devcall label to discuss milestone |
Stays at v4.1 milestone as v4.1 will be our PDF feature release. |
Remove code that produces NPE
Remove code that produces NPE Export linked files Don't replace existings Create new Action for exporting files TODO: Add to menu Move to Tools entry try around with some background task stuf remove compile errro Add Progessbar dialog Add localization Fix checkstyle extract to inner class Add some more output messages renamed variables Fix gui display
Fix localization to copy files
I think I fixed all issues raised by @koppor and I also create a log file in the export dir. |
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.
Overall, the code looks fine. I just have a few suggestions for quality improvements. It would be cool if you could have a go at these, but it wouldn't block this PR if you don't.
The only real blocker is the missing changelog entry :)
@@ -51,6 +51,7 @@ public static void main(String[] args) { | |||
public void start(Stage mainStage) throws Exception { | |||
Platform.setImplicitExit(false); | |||
SwingUtilities.invokeLater(() -> start(arguments)); | |||
|
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.
Why is there a blank line here?
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.
Oops.
}); | ||
} | ||
|
||
private class ExportService extends Service<Void> { |
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.
This is rather nitpicky, but what about extracting this into a separate class file in the same package with default visibility? You could also extract parts of call()
into separate private methods. All this wouldn't change anything about the functionality, but it would make the code a little less chunky and more readable.
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.
I see no reason why I should extract that in a new class. It's just a single Service for one purpose.
But I think I can surely extract some private methods
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.
One reason is that you could (and should) write tests for the ExportService
.
Avoid code duplicaiton
DefaultTaskExecutor.runInJavaFXThread(() -> { | ||
service.start(); | ||
|
||
ProgressDialog progressDialog = new ProgressDialog(service); |
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.
Can you please extract the "show progress dialog" part of this method to the DialogService
class.
List<LinkedFile> files = entries.get(i).getFiles(); | ||
|
||
for (int j = 0; j < files.size(); j++) { | ||
updateMessage(Localization.lang("Copying file %0 of BibEntry %1", Integer.toString(j + 1), Integer.toString(i + 1))); |
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.
I think we never used BibEntry
before in the user interface (it is the class name...). What about only "entry" or "item"?
|
||
for (int j = 0; j < files.size(); j++) { | ||
updateMessage(Localization.lang("Copying file %0 of BibEntry %1", Integer.toString(j + 1), Integer.toString(i + 1))); | ||
Thread.sleep(500); //TODO: Adjust/leave/any other idea? |
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.
This sleep makes the whole progress of copying files really slow. I would remove it even through then the user has no chance to actually read the message for small files (but that is ok in my opinion).
Thread.sleep(500); //TODO: Adjust/leave/any other idea? | ||
|
||
String fileName = files.get(j).getLink(); | ||
Optional<Path> fileToExport = FileHelper.expandFilenameAsPath(fileName, |
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.
Use
public Optional<Path> findIn(BibDatabaseContext databaseContext, FileDirectoryPreferences fileDirectoryPreferences) { |
updateMessage(Localization.lang("Copying files...")); | ||
updateProgress(0, totalFilesCount); | ||
|
||
try (BufferedWriter bw = Files.newBufferedWriter(exportPath.get().resolve(LOGFILE), StandardCharsets.UTF_8)) { |
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.
In my opinion, a log file is an inconvenient solution for the user. Why don't log all the errors to an internal list and display this list at the end in a new dialog?
@@ -313,12 +313,6 @@ public void performExport(final BibDatabaseContext databaseContext, final String | |||
|
|||
} | |||
|
|||
@Override | |||
public void performExport(final BibDatabaseContext databaseContext, Path file, final Charset encoding, |
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 don't delete this path
based methods. I know they are not yet used but they are actually preferable to the string or file-based methods (these should actually be deprecated).
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.
I deleted it on purpose because it is not working at all as I explained in my other PR or earlier.
It always lead to an exception.
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.
ok...
@@ -102,6 +103,7 @@ public RightClickMenu(JabRefFrame frame, BasePanel panel) { | |||
|
|||
add(new GeneralAction(Actions.SEND_AS_EMAIL, Localization.lang("Send as email"), IconTheme.JabRefIcon.EMAIL.getSmallIcon())); | |||
addSeparator(); | |||
add(new ExportLinkedFilesAction()); |
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.
Do you really think that this export functionality is used that often that we should add it to the right click menu?
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.
It was requested by @koppor And I think it makes sense to have it in the context menu
* upstream/master: Update gradle to 4.2.1 (#3322) Avoid recreation of the EntryEditor (#3187) Fix #3133 telemetry by locking azure to 1.0.9 German translation for missing properties (#3312) Improvement for Java FX font rendering on Linux (#3305) Add also conversion for em dash Add \textendash to the html conversion table Update latex2tunicode from 0.2.1 -> 0.2.2 Added note about updating controlsfx Fix exception/freezing on EntryChangedEvent in Entry Editor (#3299) Update libs (#3300) update guava from 23.0 -> 23.2 update mvvmfx-validation from 1.6.0 -> 1.7.0 Resolves #3255 file open dialog should have "supported formates" filetype Fix #3235: remote metadata is updated instead of delete + insert (#3282) Change OO paths to Libre Office in preferences (#3287) Fix #2471: remove line breaks from abstracts in ADS fetcher (#3285) Resolves #3280 Empty String instead of N/A (#3288)
</TableView> | ||
<ButtonBar prefHeight="40.0" prefWidth="200.0"> | ||
<buttons> | ||
<Button defaultButton="true" mnemonicParsing="false" onAction="#close" text="OK" /> |
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.
you should always annotate buttons in a button bar with a proper "button data", which lets JavaFX position these buttons appropriately according to the style guidelines of the current os.
import java.util.ArrayList; | ||
import java.util.List; | ||
|
||
public class CopyFilesResultListDependency { |
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.
@tobiasdiez I have created this wrapper class to inject it
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.
If I dont' intilaize the ArrayList with Empty, it will give an NPE in my View Model which leads to the conclusion that this are somehow different objects. I remember having similair problems wit the Sharelatex Integration and ended up making it static. But maybe there is another solution?
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.
Oh damn...thats ugly. It is not possible to directly inject the list of results?
You also hit this bug here: AdamBien/afterburner.fx#71 this is why you need the no-arg constructor.
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.
I'll have a look at it later.
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.
The list can not be directly injected, because List is an interface which of course can not be instantiated...that's where our default injector throws an error
I tried to add handling for list but then generics are a problem...
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.
@tobiasdiez I found the issue. The "key" of the Hashmap and the name of the with inject annotated variable differed! That's why the data was not passed correct
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.
With #3334 you should be able to use @Inject List<whatever> toBeInjected
or other complex types (they do not need to have a no-args constructor anymore). Of course the names of the "key" and the "field" have to coincide to make this work.
@@ -76,7 +71,11 @@ private void startServiceAndshowProgessDialog(Task<List<CopyFilesResultItemViewM | |||
} | |||
|
|||
private void showDialog(List<CopyFilesResultItemViewModel> data) { | |||
Dialog<ButtonType> dlg = new Dialog<>(); | |||
|
|||
CopyFilesDialogView dlg = new CopyFilesDialogView(databaseContext, new CopyFilesResultListDependency(data)); |
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.
@tobiasdiez Here I am passing the instance of the object, but it doesn't display any data.
Switch gui to border pane add tooltip to factory
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.
Just some minor remarks which are not really blockers.
public class CopyFilesTask extends Task<List<CopyFilesResultItemViewModel>> { | ||
|
||
private static final Log LOGGER = LogFactory.getLog(ExportLinkedFilesAction.class); | ||
private static final String LOGFILE = "exportLog.log"; |
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.
I am unsure if this logfile is needed.
If it is, I would encode a timestamp into the filename to be able to distinguish several exports into the same folder.
private List<BibEntry> entries; | ||
|
||
public ExportLinkedFilesAction() { | ||
super(Localization.lang("Copy attached files to folder...")); |
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.
The terminology should be consistent. I suggest "Copy linked files to folder.."
colMessage.setCellValueFactory(cellData -> cellData.getValue().getMessage()); | ||
colStatus.setCellValueFactory(cellData -> cellData.getValue().getIcon()); | ||
|
||
colFile.setCellFactory(new ValueTableCellFactory<CopyFilesResultItemViewModel, String>().withText(item -> item).withTooltip(item -> item)); |
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.
is there a way to prevent that fourth column appearing #3147 (comment) ?
Add timestamp Improve GUI
@lynyus I changed the column size and there is also a tooltip for the file column |
* upstream/master: Rework AutoSetFileLinks (#3368) revert wrongly commited changes Refactorings (#3370) Fix freezing when running cleanup file operations like rename (#3315) This additional style setting for IDEA (#3355) Fix checkstyle Add proper equals to content selector Source tab entry duplication (#3360) Use CITE_COMMENT not only for external latex editors but also for cop… (#3351) Updating with new translations (#3348) Upgrade error-prone (#3350) Jabref_pt_BR partially updated (#3349) Delete unused code Extract difference finder from gui to logic Used late initialization for context menus (#3340) Remove unnecessary EntrySorter Move existing code to gui and rename change classes to view model Small code improvements
…ortPdf * 'exportPdf' of https://github.com/JabRef/jabref: Rename l10n Add timestamp Improve GUI
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.
I had another look at the code, which seems fine. I think you have addressed all the other reviewers comments, so I"ll merge this now.
* upstream/master: (22 commits) Improve SyncLang.py (#3184) Config intellj code style to adhere to empty lines checkstyle rule (#3365) Don't list same look and feels more than once (#3393) progessdialog and result table are now shown correclty on copy files Export pdf/linked files (#3147) Added checking integrity dialog (#3388) Update richtext and flowless (#3386) Source tab: hide notification pane when code is correct again (#3387) Titles are optional at crossref (#3378) Update entry for every change in the source panel (#3366) Rework AutoSetFileLinks (#3368) revert wrongly commited changes Refactorings (#3370) Fix freezing when running cleanup file operations like rename (#3315) This additional style setting for IDEA (#3355) Fix checkstyle Add proper equals to content selector Delete unused code Extract difference finder from gui to logic Remove unnecessary EntrySorter ... # Conflicts: # src/main/java/org/jabref/gui/entryeditor/SourceTab.java
* upstream/master: (26 commits) Fix static localized text (#3400) Fix problems in source editor (#3398) Fix MathSciNet fetcher (#3402) Fix NPE when saving new file Improve SyncLang.py (#3184) Config intellj code style to adhere to empty lines checkstyle rule (#3365) Don't list same look and feels more than once (#3393) progessdialog and result table are now shown correclty on copy files Export pdf/linked files (#3147) Added checking integrity dialog (#3388) Update richtext and flowless (#3386) Source tab: hide notification pane when code is correct again (#3387) Titles are optional at crossref (#3378) Update entry for every change in the source panel (#3366) Rework AutoSetFileLinks (#3368) revert wrongly commited changes Refactorings (#3370) Fix freezing when running cleanup file operations like rename (#3315) This additional style setting for IDEA (#3355) Fix checkstyle ... # Conflicts: # CHANGELOG.md
* upstream/master: Improve SyncLang.py (#3184) Config intellj code style to adhere to empty lines checkstyle rule (#3365) Don't list same look and feels more than once (#3393) progessdialog and result table are now shown correclty on copy files Export pdf/linked files (#3147) Added checking integrity dialog (#3388)
Fixes #2539
Followup from #2706 because somehow my new changes did not update the PR correctly.
gradle localizationUpdate
?