add: export linked notes to markdown
This commit is contained in:
parent
4bb748cc85
commit
2db441128a
|
|
@ -31,9 +31,13 @@
|
|||
<caption label="&zotero.__addonRef__.export.markdown.label;"></caption>
|
||||
<rows flex="1">
|
||||
<row>
|
||||
<checkbox id="__addonRef__-export-enablefile" checked="true" />
|
||||
<checkbox id="__addonRef__-export-enablefile" checked="true" oncommand="Zotero.Knowledge4Zotero.export.doUpdate(event)" />
|
||||
<label value="&zotero.__addonRef__.export.file.enable.label;" />
|
||||
</row>
|
||||
<row>
|
||||
<checkbox id="__addonRef__-export-enablesingle" checked="false" oncommand="Zotero.Knowledge4Zotero.export.doUpdate(event)" />
|
||||
<label value="&zotero.__addonRef__.export.singlefile.enable.label;" />
|
||||
</row>
|
||||
</rows>
|
||||
</groupbox>
|
||||
<groupbox flex="1">
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@
|
|||
<!ENTITY zotero.__addonRef__.export.note.enable.label "Export to new note">
|
||||
<!ENTITY zotero.__addonRef__.export.markdown.label "MarkDown Settings">
|
||||
<!ENTITY zotero.__addonRef__.export.file.enable.label "Export to MarkDown File">
|
||||
<!ENTITY zotero.__addonRef__.export.singlefile.enable.label "Export Linked Notes to MarkDown File">
|
||||
<!ENTITY zotero.__addonRef__.export.richtext.label "RichText(MS Word) Settings">
|
||||
<!ENTITY zotero.__addonRef__.export.copy.enable.label "Export to clipboard">
|
||||
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@
|
|||
<!ENTITY zotero.__addonRef__.export.note.enable.label "导出到新笔记">
|
||||
<!ENTITY zotero.__addonRef__.export.markdown.label "MarkDown设置">
|
||||
<!ENTITY zotero.__addonRef__.export.file.enable.label "导出为MarkDown文件">
|
||||
<!ENTITY zotero.__addonRef__.export.singlefile.enable.label "导出链接的子笔记为MarkDown文件">
|
||||
<!ENTITY zotero.__addonRef__.export.richtext.label "富文本(MS Word)设置">
|
||||
<!ENTITY zotero.__addonRef__.export.copy.enable.label "导出到剪贴板">
|
||||
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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") {
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
172
src/knowledge.ts
172
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("<img");
|
||||
if (hasImage && !attachmentCreated) {
|
||||
await Zotero.File.createDirectoryIfMissingAsync(
|
||||
OS.Path.join(...this._exportPath.split(/\//))
|
||||
);
|
||||
attachmentCreated = true;
|
||||
}
|
||||
|
||||
let filename = `${Zotero.File.pathToFile(filepath).path}/${
|
||||
newNote.getNoteTitle
|
||||
? newNote.getNoteTitle().replace(/[/\\?%*:|"<>]/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("<img");
|
||||
if (hasImage) {
|
||||
await Zotero.File.createDirectoryIfMissingAsync(
|
||||
OS.Path.join(...this._exportPath.split(/\//))
|
||||
);
|
||||
}
|
||||
|
||||
filename = filename.replace(/\\/g, "/");
|
||||
const translator = new Zotero.Translate.Export();
|
||||
translator.setItems([note]);
|
||||
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 (deleteAfterExport) {
|
||||
const _w: Window = ZoteroPane.findNoteWindow(note.id);
|
||||
if (_w) {
|
||||
_w.close();
|
||||
}
|
||||
await Zotero.Items.erase(note.id);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue