Add "Include Annotations" checkbox to export options dialog
This changes the attachment saveFile() function in translators to be async. In order for errors to be properly caught, translators will need to be changed to make doExport() async and await on saveFile() calls. (The translation architecture theoretically already allows doExport() to be async.)
This commit is contained in:
parent
688298af7f
commit
55c6de23ba
8 changed files with 102 additions and 29 deletions
|
@ -34,8 +34,6 @@ const OPTION_PREFIX = "export-option-";
|
|||
// Class to provide options for export
|
||||
|
||||
var Zotero_File_Interface_Export = new function() {
|
||||
this.init = init;
|
||||
this.updateOptions = updateOptions;
|
||||
this.accept = accept;
|
||||
this.cancel = cancel;
|
||||
|
||||
|
@ -44,7 +42,7 @@ var Zotero_File_Interface_Export = new function() {
|
|||
/*
|
||||
* add options to export
|
||||
*/
|
||||
function init() {
|
||||
this.init = function () {
|
||||
// Set font size from pref
|
||||
var sbc = document.getElementById('zotero-export-options-container');
|
||||
Zotero.setFontSize(sbc);
|
||||
|
@ -82,10 +80,25 @@ var Zotero_File_Interface_Export = new function() {
|
|||
// right now, option interface supports only boolean values, which
|
||||
// it interprets as checkboxes
|
||||
if(typeof(translators[i].displayOptions[option]) == "boolean") {
|
||||
var checkbox = document.createElement("checkbox");
|
||||
let checkbox = document.createElement("checkbox");
|
||||
checkbox.setAttribute("id", OPTION_PREFIX+option);
|
||||
checkbox.setAttribute("label", optionLabel);
|
||||
optionsBox.insertBefore(checkbox, charsetBox);
|
||||
|
||||
// Add "Include Annotations" after "Export Files"
|
||||
if (option == 'exportFileData') {
|
||||
checkbox.onclick = () => {
|
||||
setTimeout(() => this.updateAnnotationsCheckbox());
|
||||
};
|
||||
|
||||
checkbox = document.createElement("checkbox");
|
||||
checkbox.setAttribute("id", OPTION_PREFIX + 'includeAnnotations');
|
||||
checkbox.setAttribute(
|
||||
"label",
|
||||
Zotero.getString('exportOptions.includeAnnotations')
|
||||
);
|
||||
optionsBox.insertBefore(checkbox, charsetBox);
|
||||
}
|
||||
}
|
||||
|
||||
addedOptions[option] = true;
|
||||
|
@ -108,13 +121,13 @@ var Zotero_File_Interface_Export = new function() {
|
|||
_charsets = Zotero_Charset_Menu.populate(document.getElementById(OPTION_PREFIX+"exportCharset"), true);
|
||||
}
|
||||
|
||||
updateOptions(Zotero.Prefs.get("export.translatorSettings"));
|
||||
this.updateOptions(Zotero.Prefs.get("export.translatorSettings"));
|
||||
}
|
||||
|
||||
/*
|
||||
* update translator-specific options
|
||||
*/
|
||||
function updateOptions(optionString) {
|
||||
this.updateOptions = function (optionString) {
|
||||
// get selected translator
|
||||
var index = document.getElementById("format-menu").selectedIndex;
|
||||
var translatorOptions = window.arguments[0].translators[index].displayOptions;
|
||||
|
@ -133,7 +146,9 @@ var Zotero_File_Interface_Export = new function() {
|
|||
var node = optionsBox.childNodes[i];
|
||||
// skip non-options
|
||||
if(node.id.length <= OPTION_PREFIX.length
|
||||
|| node.id.substr(0, OPTION_PREFIX.length) != OPTION_PREFIX) {
|
||||
|| node.id.substr(0, OPTION_PREFIX.length) != OPTION_PREFIX
|
||||
// Handled separately by updateAnnotationsCheckbox()
|
||||
|| node.id == 'export-option-includeAnnotations') {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -161,6 +176,10 @@ var Zotero_File_Interface_Export = new function() {
|
|||
}
|
||||
}
|
||||
|
||||
this.updateAnnotationsCheckbox(
|
||||
(options && options.includeAnnotations) ? options.includeAnnotations : false
|
||||
);
|
||||
|
||||
// handle charset popup
|
||||
if(_charsets && translatorOptions && translatorOptions.exportCharset) {
|
||||
optionsBox.hidden = undefined;
|
||||
|
@ -181,6 +200,21 @@ var Zotero_File_Interface_Export = new function() {
|
|||
window.sizeToContent();
|
||||
}
|
||||
|
||||
this.updateAnnotationsCheckbox = function (defaultValue) {
|
||||
var filesCheckbox = document.getElementById(OPTION_PREFIX + 'exportFileData');
|
||||
var annotationsCheckbox = document.getElementById(OPTION_PREFIX + 'includeAnnotations');
|
||||
if (filesCheckbox.hidden) {
|
||||
annotationsCheckbox.hidden = true;
|
||||
annotationsCheckbox.checked = false;
|
||||
return;
|
||||
}
|
||||
annotationsCheckbox.hidden = false;
|
||||
annotationsCheckbox.disabled = !filesCheckbox.checked;
|
||||
if (defaultValue !== undefined) {
|
||||
annotationsCheckbox.checked = defaultValue;
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* make option array reflect status
|
||||
*/
|
||||
|
@ -210,6 +244,13 @@ var Zotero_File_Interface_Export = new function() {
|
|||
}
|
||||
}
|
||||
|
||||
// If "Export Files" is shown, add "Include Annotations" checkbox value
|
||||
if (optionsAvailable && optionsAvailable.exportFileData !== undefined) {
|
||||
let elem1 = document.getElementById(OPTION_PREFIX + 'exportFileData');
|
||||
let elem2 = document.getElementById(OPTION_PREFIX + 'includeAnnotations');
|
||||
displayOptions.includeAnnotations = elem1.checked && elem2.checked;
|
||||
}
|
||||
|
||||
// save options
|
||||
var optionString = JSON.stringify(displayOptions);
|
||||
Zotero.Prefs.set("export.translatorSettings", optionString);
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
<?xml version="1.0"?>
|
||||
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
|
||||
<?xml-stylesheet href="chrome://zotero-platform/content/zotero-react-client.css"?>
|
||||
|
||||
<!DOCTYPE window [
|
||||
<!ENTITY % zoteroDTD SYSTEM "chrome://zotero/locale/zotero.dtd" >
|
||||
%zoteroDTD;
|
||||
|
|
|
@ -2541,8 +2541,14 @@ Zotero.Translate.Export.prototype._prepareTranslation = Zotero.Promise.method(fu
|
|||
|
||||
function rest() {
|
||||
// export file data, if requested
|
||||
if(this._displayOptions["exportFileData"]) {
|
||||
this.location = this._itemGetter.exportFiles(this.location, this.translator[0].target);
|
||||
if (this._displayOptions.exportFileData) {
|
||||
this.location = this._itemGetter.exportFiles(
|
||||
this.location,
|
||||
this.translator[0].target,
|
||||
{
|
||||
includeAnnotations: this._displayOptions.includeAnnotations
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// initialize IO
|
||||
|
|
|
@ -1048,9 +1048,10 @@ Zotero.Translate.ItemGetter.prototype = {
|
|||
this.numItems = this._itemsLeft.length;
|
||||
}),
|
||||
|
||||
"exportFiles":function(dir, extension) {
|
||||
exportFiles: function (dir, extension, { includeAnnotations }) {
|
||||
// generate directory
|
||||
this._exportFileDirectory = dir.parent.clone();
|
||||
this._includeAnnotations = includeAnnotations;
|
||||
|
||||
// delete this file if it exists
|
||||
if(dir.exists()) {
|
||||
|
@ -1079,6 +1080,7 @@ Zotero.Translate.ItemGetter.prototype = {
|
|||
var attachmentArray = Zotero.Utilities.Internal.itemToExportFormat(attachment, this.legacy);
|
||||
var linkMode = attachment.attachmentLinkMode;
|
||||
if(linkMode != Zotero.Attachments.LINK_MODE_LINKED_URL) {
|
||||
let includeAnnotations = attachment.isPDFAttachment() && this._includeAnnotations;
|
||||
attachmentArray.localPath = attachment.getFilePath();
|
||||
|
||||
if(this._exportFileDirectory) {
|
||||
|
@ -1114,7 +1116,7 @@ Zotero.Translate.ItemGetter.prototype = {
|
|||
* file to be overwritten. If true, the file will be silently overwritten.
|
||||
* defaults to false if not provided.
|
||||
*/
|
||||
attachmentArray.saveFile = function(attachPath, overwriteExisting) {
|
||||
attachmentArray.saveFile = async function (attachPath, overwriteExisting) {
|
||||
// Ensure a valid path is specified
|
||||
if(attachPath === undefined || attachPath == "") {
|
||||
throw new Error("ERROR_EMPTY_PATH");
|
||||
|
@ -1162,18 +1164,13 @@ Zotero.Translate.ItemGetter.prototype = {
|
|||
|
||||
var directory = targetFile.parent;
|
||||
|
||||
// The only attachments that can have multiple supporting files are imported
|
||||
// attachments of mime type text/html
|
||||
//
|
||||
// TEMP: This used to check getNumFiles() here, but that's now async.
|
||||
// It could be restored (using hasMultipleFiles()) when this is made
|
||||
// async, but it's probably not necessary. (The below can also be changed
|
||||
// to use OS.File.DirectoryIterator.)
|
||||
if(attachment.attachmentContentType == "text/html"
|
||||
&& linkMode != Zotero.Attachments.LINK_MODE_LINKED_FILE) {
|
||||
// Attachment is a snapshot with supporting files. Check if any of the
|
||||
// supporting files would cause a name conflict, and build a list of transfers
|
||||
// For snapshots with supporting files, check if any of the supporting
|
||||
// files would cause a name conflict, and build a list of transfers
|
||||
// that should be performed
|
||||
//
|
||||
// TODO: Change the below to use OS.File.DirectoryIterator?
|
||||
if (linkMode != Zotero.Attachments.LINK_MODE_LINKED_FILE
|
||||
&& await Zotero.Attachments.hasMultipleFiles(attachment)) {
|
||||
var copySrcs = [];
|
||||
var files = attachment.getFile().parent.directoryEntries;
|
||||
while (files.hasMoreElements()) {
|
||||
|
@ -1204,11 +1201,23 @@ Zotero.Translate.ItemGetter.prototype = {
|
|||
for(var i = 0; i < copySrcs.length; i++) {
|
||||
copySrcs[i].copyTo(directory, copySrcs[i].leafName);
|
||||
}
|
||||
} else {
|
||||
// Attachment is a single file
|
||||
// Copy the file to the specified location
|
||||
}
|
||||
// For single files, just copy to the specified location
|
||||
else {
|
||||
if (includeAnnotations) {
|
||||
// TODO: Make export async
|
||||
try {
|
||||
await Zotero.PDFWorker.export(attachment.id, targetFile.path);
|
||||
}
|
||||
catch (e) {
|
||||
Zotero.logError(e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
else {
|
||||
attachFile.copyTo(directory, targetFile.leafName);
|
||||
}
|
||||
}
|
||||
|
||||
attachmentArray.path = targetFile.path;
|
||||
};
|
||||
|
|
|
@ -803,6 +803,7 @@ fulltext.indexState.queued = Queued
|
|||
|
||||
exportOptions.exportNotes = Export Notes
|
||||
exportOptions.exportFileData = Export Files
|
||||
exportOptions.includeAnnotations = Include Annotations
|
||||
exportOptions.useJournalAbbreviation = Use Journal Abbreviation
|
||||
charset.UTF8withoutBOM = Unicode (UTF-8 without BOM)
|
||||
charset.autoDetect = (auto detect)
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
@import "components/button";
|
||||
@import "components/createParent";
|
||||
@import "components/editable";
|
||||
@import "components/exportOptions";
|
||||
@import "components/icons";
|
||||
@import "components/mainWindow";
|
||||
@import "components/notesList";
|
||||
|
|
10
scss/components/_exportOptions.scss
Normal file
10
scss/components/_exportOptions.scss
Normal file
|
@ -0,0 +1,10 @@
|
|||
#zotero-export-options {
|
||||
#export-option-includeAnnotations {
|
||||
margin-top: 2px;
|
||||
margin-left: 17px;
|
||||
}
|
||||
|
||||
checkbox[disabled=true] label {
|
||||
opacity: .5;
|
||||
}
|
||||
}
|
|
@ -2059,7 +2059,7 @@ describe("Zotero.Translate.ItemGetter", function() {
|
|||
|
||||
// saveFile function
|
||||
assert.isFunction(attachment.saveFile, prefix + 'has saveFile function' + suffix);
|
||||
attachment.saveFile(attachment.defaultPath);
|
||||
yield attachment.saveFile(attachment.defaultPath);
|
||||
assert.equal(attachment.path, OS.Path.join(exportDir, OS.Path.normalize(attachment.defaultPath)), prefix + 'path is set correctly after saveFile call' + suffix);
|
||||
|
||||
let fileExists = yield OS.File.exists(attachment.path);
|
||||
|
@ -2067,8 +2067,11 @@ describe("Zotero.Translate.ItemGetter", function() {
|
|||
fileExists = yield OS.File.exists(attachment.localPath);
|
||||
assert.isTrue(fileExists, prefix + 'file was not removed from original location' + suffix);
|
||||
|
||||
assert.throws(attachment.saveFile.bind(attachment, attachment.defaultPath), /^ERROR_FILE_EXISTS /, prefix + 'saveFile does not overwrite existing file by default' + suffix);
|
||||
assert.throws(attachment.saveFile.bind(attachment, 'file/../../'), /./, prefix + 'saveFile does not allow exporting outside export directory' + suffix);
|
||||
let e;
|
||||
e = yield getPromiseError(attachment.saveFile(attachment.defaultPath));
|
||||
assert.match(e.message, /^ERROR_FILE_EXISTS /, prefix + 'saveFile does not overwrite existing file by default' + suffix);
|
||||
e = yield getPromiseError(attachment.saveFile('file/../../'));
|
||||
assert.match(e.message, /./, prefix + 'saveFile does not allow exporting outside export directory' + suffix);
|
||||
/** TODO: check if overwriting existing file works **/
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue