diff --git a/addon/chrome/content/export.xul b/addon/chrome/content/export.xul index 8c2c529..98f4e59 100644 --- a/addon/chrome/content/export.xul +++ b/addon/chrome/content/export.xul @@ -31,9 +31,13 @@ - + + + + diff --git a/addon/chrome/locale/en-US/overlay.dtd b/addon/chrome/locale/en-US/overlay.dtd index 31128f1..2b1c8ad 100644 --- a/addon/chrome/locale/en-US/overlay.dtd +++ b/addon/chrome/locale/en-US/overlay.dtd @@ -28,6 +28,7 @@ + diff --git a/addon/chrome/locale/zh-CN/overlay.dtd b/addon/chrome/locale/zh-CN/overlay.dtd index e28f90f..959f9ad 100644 --- a/addon/chrome/locale/zh-CN/overlay.dtd +++ b/addon/chrome/locale/zh-CN/overlay.dtd @@ -28,6 +28,7 @@ + diff --git a/src/Better Note Markdown.js b/src/Better Note Markdown.js index f327b22..c594665 100644 --- a/src/Better Note Markdown.js +++ b/src/Better Note Markdown.js @@ -774,6 +774,22 @@ let bundle; var href = node.getAttribute("href"); var title = cleanAttribute(node.getAttribute("title")); if (title) title = ' "' + title + '"'; + if (href.search(/zotero:\/\/note\/\w+\/\w+\//g) !== -1) { + // A note link should be converted if it is in the _exportFileDict + var _Zotero = Components.classes[ + "@zotero.org/Zotero;1" + ].getService( + Components.interfaces.nsISupports + ).wrappedJSObject; + const noteInfo = + _Zotero.Knowledge4Zotero.knowledge._exportFileDict && + _Zotero.Knowledge4Zotero.knowledge._exportFileDict.find( + (i) => href.includes(i.link) + ); + if (noteInfo) { + href = `./${noteInfo.filename}`; + } + } return "[" + content + "](" + href + title + ")"; }, }; @@ -1800,7 +1816,7 @@ let bundle; ); async function doExport() { - const _Zotero = Components.classes["@zotero.org/Zotero;1"].getService( + var _Zotero = Components.classes["@zotero.org/Zotero;1"].getService( Components.interfaces.nsISupports ).wrappedJSObject; Zotero.setCharacterSet("utf-8"); diff --git a/src/events.ts b/src/events.ts index 6b51729..16bd1bf 100644 --- a/src/events.ts +++ b/src/events.ts @@ -126,7 +126,6 @@ class AddonEvents extends AddonBase { if (state) { const noteTab = state.tabs.find((t) => t.type === "betternotes"); Zotero.debug(noteTab); - this._Addon.views.showProgressWindow("1", noteTab.select); if (noteTab) { let t = 0; while (t < 5) { @@ -1177,18 +1176,20 @@ class AddonEvents extends AddonBase { await io.deferred.promise; const options = io.dataOut; - await this._Addon.knowledge.exportNoteToFile( - item, - options.embedLink, - options.exportFile, - options.exportNote, - options.exportCopy - ); + if (options.exportFile && options.exportSingleFile) { + await this._Addon.knowledge.exportNotesToFile([item], false); + } else { + await this._Addon.knowledge.exportNoteToFile( + item, + options.embedLink, + options.exportFile, + options.exportNote, + options.exportCopy + ); + } } else if (message.type === "exportNotes") { /* - message.content = { - editorInstance - } + message.content = {} */ const items = ZoteroPane.getSelectedItems(); const noteItems = []; @@ -1210,7 +1211,11 @@ class AddonEvents extends AddonBase { new EditorMessage("export", { params: { item: noteItems[0] } }) ); } else { - await this._Addon.knowledge.exportNotesToFile(noteItems); + const useSingleFile = confirm("Export linked notes to markdown files?"); + await this._Addon.knowledge.exportNotesToFile( + noteItems, + !useSingleFile + ); } } else if (message.type === "openAttachment") { /* diff --git a/src/export.ts b/src/export.ts index 6cc3ea6..a752328 100644 --- a/src/export.ts +++ b/src/export.ts @@ -23,6 +23,16 @@ class AddonExport extends AddonBase { ) as XUL.Checkbox ).checked = exportFile; } + let exportSingleFile = Zotero.Prefs.get( + "Knowledge4Zotero.exportSingleFile" + ); + if (typeof exportSingleFile !== "undefined") { + ( + this._window.document.getElementById( + "Knowledge4Zotero-export-enablesingle" + ) as XUL.Checkbox + ).checked = exportSingleFile; + } let embedLink = Zotero.Prefs.get("Knowledge4Zotero.embedLink"); if (typeof embedLink !== "undefined") { ( @@ -48,6 +58,25 @@ class AddonExport extends AddonBase { ).checked = exportCopy; } } + doUpdate(event: XULEvent) { + if ( + event.target.getAttribute("id") === "Knowledge4Zotero-export-enablesingle" + ) { + ( + this._window.document.getElementById( + "Knowledge4Zotero-export-embedLink" + ) as XUL.Checkbox + ).disabled = (event.target as XUL.Checkbox).checked; + } else if ( + event.target.getAttribute("id") === "Knowledge4Zotero-export-enablefile" + ) { + ( + this._window.document.getElementById( + "Knowledge4Zotero-export-enablesingle" + ) as XUL.Checkbox + ).disabled = !(event.target as XUL.Checkbox).checked; + } + } doUnload() { this.io.deferred && this.io.deferred.resolve(); } @@ -58,6 +87,11 @@ class AddonExport extends AddonBase { "Knowledge4Zotero-export-enablefile" ) as XUL.Checkbox ).checked; + let exportSingleFile = ( + this._window.document.getElementById( + "Knowledge4Zotero-export-enablesingle" + ) as XUL.Checkbox + ).checked; let embedLink = ( this._window.document.getElementById( "Knowledge4Zotero-export-embedLink" @@ -74,6 +108,7 @@ class AddonExport extends AddonBase { ) as XUL.Checkbox ).checked; Zotero.Prefs.set("Knowledge4Zotero.exportFile", exportFile); + Zotero.Prefs.set("Knowledge4Zotero.exportSingleFile", exportSingleFile); Zotero.Prefs.set("Knowledge4Zotero.embedLink", embedLink); Zotero.Prefs.set("Knowledge4Zotero.exportNote", exportNote); Zotero.Prefs.set("Knowledge4Zotero.exportCopy", exportCopy); @@ -81,6 +116,7 @@ class AddonExport extends AddonBase { Zotero.debug(this.io.dataOut); this.io.dataOut = { exportFile: exportFile, + exportSingleFile: exportSingleFile, embedLink: embedLink, exportNote: exportNote, exportCopy: exportCopy, diff --git a/src/knowledge.ts b/src/knowledge.ts index d3eda8e..4549886 100644 --- a/src/knowledge.ts +++ b/src/knowledge.ts @@ -11,6 +11,7 @@ class Knowledge extends AddonBase { workspaceTabId: string; _exportNote: ZoteroItem; _exportPath: string; + _exportFileDict: object; constructor(parent: Knowledge4Zotero) { super(parent); this.currentLine = -1; @@ -712,6 +713,8 @@ class Knowledge extends AddonBase { if (!saveFile && !saveNote && !saveCopy) { return; } + this._exportFileDict = []; + note = note || this.getWorkspaceNote(); let newNote: ZoteroItem; if (convertNoteLinks || saveNote) { @@ -804,8 +807,9 @@ class Knowledge extends AddonBase { } } - async exportNotesToFile(notes: ZoteroItem[]) { + async exportNotesToFile(notes: ZoteroItem[], useEmbed: boolean) { Components.utils.import("resource://gre/modules/osfile.jsm"); + this._exportFileDict = []; const filepath = await pick( Zotero.getString("fileInterface.export"), "folder" @@ -821,68 +825,120 @@ class Knowledge extends AddonBase { // Convert to unix format this._exportPath = this._exportPath.replace(/\\/g, "/"); - let attachmentCreated = false; + if (useEmbed) { + for (const note of notes) { + let newNote: ZoteroItem; + if (this.getLinkFromText(note.getNote())) { + const noteID = await ZoteroPane_Local.newNote(); + newNote = Zotero.Items.get(noteID); + const rootNoteIds = [note.id]; - for (const note of notes) { - let newNote: ZoteroItem; - if (this.getLinkFromText(note.getNote())) { - const noteID = await ZoteroPane_Local.newNote(); - newNote = Zotero.Items.get(noteID); - const rootNoteIds = [note.id]; + const convertResult = await this.convertNoteLines( + note, + rootNoteIds, + true + ); - const convertResult = await this.convertNoteLines( - note, - rootNoteIds, - true - ); + this.setLinesToNote(newNote, convertResult.lines); + Zotero.debug(convertResult.subNotes); - this.setLinesToNote(newNote, convertResult.lines); - Zotero.debug(convertResult.subNotes); - - await Zotero.DB.executeTransaction(async () => { - await Zotero.Notes.copyEmbeddedImages(note, newNote); - for (const subNote of convertResult.subNotes) { - await Zotero.Notes.copyEmbeddedImages(subNote, newNote); - } - }); - } else { - newNote = note; - } - - this._exportNote = newNote; - - const hasImage = newNote.getNote().includes("]/g, "-") + "-" - : "" - }${note.key}.md`; - filename = filename.replace(/\\/g, "/"); - const translator = new Zotero.Translate.Export(); - translator.setItems([newNote]); - translator.setLocation( - Zotero.File.pathToFile(OS.Path.join(...filename.split(/\//))) - ); - translator.setTranslator(TRANSLATOR_ID_BETTER_MARKDOWN); - translator.translate(); - this._Addon.views.showProgressWindow( - "Better Notes", - `Note Saved to ${filename}` - ); - if (newNote.id !== note.id) { - const _w: Window = ZoteroPane.findNoteWindow(newNote.id); - if (_w) { - _w.close(); + await Zotero.DB.executeTransaction(async () => { + await Zotero.Notes.copyEmbeddedImages(note, newNote); + for (const subNote of convertResult.subNotes) { + await Zotero.Notes.copyEmbeddedImages(subNote, newNote); + } + }); + } else { + newNote = note; } - await Zotero.Items.erase(newNote.id); + + this._exportNote = newNote; + + let filename = `${Zotero.File.pathToFile(filepath).path}/${ + newNote.getNoteTitle + ? newNote.getNoteTitle().replace(/[/\\?%*:|"<>]/g, "-") + "-" + : "" + }${note.key}.md`; + filename = filename.replace(/\\/g, "/"); + + this._export(newNote, filename, newNote.id !== note.id); } + } else { + // Export every linked note as a markdown file + // Find all linked notes that need to be exported + let allNoteIds: number[] = [].concat(notes.map((n) => n.id)); + for (const note of notes) { + const subNoteIds = ( + await Promise.all( + note + .getNote() + .match(/zotero:\/\/note\/\w+\/\w+\//g) + .map(async (link) => this.getNoteFromLink(link)) + ) + ) + .filter((res) => res.item) + .map((res) => res.item.id); + allNoteIds = allNoteIds.concat(subNoteIds); + } + allNoteIds = new Array(...new Set(allNoteIds)); + // console.log(allNoteIds); + const allNoteItems: ZoteroItem[] = Zotero.Items.get(allNoteIds); + const noteLinkDict = allNoteItems.map((_note) => { + return { + link: this.getNoteLink(_note), + id: _note.id, + note: _note, + filename: + (_note.getNoteTitle + ? _note.getNoteTitle().replace(/[/\\?%*:|"<> ]/g, "-") + "-" + : "") + + _note.key + + ".md", + }; + }); + this._exportFileDict = noteLinkDict; + + for (const noteInfo of noteLinkDict) { + this._exportNote = noteInfo.note; + this._export( + noteInfo.note, + `${Zotero.File.pathToFile(filepath).path}/${noteInfo.filename}`, + false + ); + } + } + } + + async _export( + note: ZoteroItem, + filename: string, + deleteAfterExport: boolean + ) { + const hasImage = note.getNote().includes("