From ee23bcc783f12f32373eb575329c5ea773aeefc7 Mon Sep 17 00:00:00 2001 From: azivner Date: Sat, 24 Nov 2018 14:44:56 +0100 Subject: [PATCH] unified export dialog, WIP --- src/public/javascripts/dialogs/export.js | 67 +++++++++++++++++++ .../javascripts/dialogs/export_subtree.js | 35 ---------- src/public/javascripts/services/bootstrap.js | 5 +- src/public/javascripts/services/export.js | 4 +- src/public/javascripts/services/tree.js | 2 - .../javascripts/services/tree_context_menu.js | 10 +-- src/public/stylesheets/style.css | 10 +++ src/routes/api/export.js | 11 +-- src/services/export/native_tar.js | 43 +++++++++++- src/views/dialogs/export.ejs | 67 +++++++++++++++++++ src/views/dialogs/export_subtree.ejs | 46 ------------- src/views/index.ejs | 6 +- 12 files changed, 203 insertions(+), 103 deletions(-) create mode 100644 src/public/javascripts/dialogs/export.js delete mode 100644 src/public/javascripts/dialogs/export_subtree.js create mode 100644 src/views/dialogs/export.ejs delete mode 100644 src/views/dialogs/export_subtree.ejs diff --git a/src/public/javascripts/dialogs/export.js b/src/public/javascripts/dialogs/export.js new file mode 100644 index 000000000..820e9963c --- /dev/null +++ b/src/public/javascripts/dialogs/export.js @@ -0,0 +1,67 @@ +import treeService from '../services/tree.js'; +import treeUtils from "../services/tree_utils.js"; +import exportService from "../services/export.js"; + +const $dialog = $("#export-dialog"); +const $form = $("#export-form"); +const $noteTitle = $dialog.find(".note-title"); +const $subtreeFormats = $("#export-subtree-formats"); +const $singleFormats = $("#export-single-formats"); +const $subtreeType = $("#export-type-subtree"); +const $singleType = $("#export-type-single"); + +async function showDialog(defaultType) { + if (defaultType === 'subtree') { + $subtreeType.prop("checked", true).change(); + } + else if (defaultType === 'single') { + $singleType.prop("checked", true).change(); + } + else { + throw new Error("Unrecognized type " + defaultType); + } + + glob.activeDialog = $dialog; + + $dialog.modal(); + + const currentNode = treeService.getCurrentNode(); + const noteTitle = await treeUtils.getNoteTitle(currentNode.data.noteId); + + $noteTitle.html(noteTitle); +} + +$form.submit(() => { + const exportType = $dialog.find("input[name='export-type']:checked").val(); + + const currentNode = treeService.getCurrentNode(); + + exportService.exportNote(currentNode.data.branchId, exportType); + + $dialog.modal('hide'); + + return false; +}); + +$('input[name=export-type]').change(function () { + if (this.value === 'subtree') { + if ($("input[name=export-subtree-format]:checked").length === 0) { + $("input[name=export-subtree-format]:first").prop("checked", true); + } + + $subtreeFormats.slideDown(); + $singleFormats.slideUp(); + } + else { + if ($("input[name=export-single-format]:checked").length === 0) { + $("input[name=export-single-format]:first").prop("checked", true); + } + + $subtreeFormats.slideUp(); + $singleFormats.slideDown(); + } +}); + +export default { + showDialog +}; \ No newline at end of file diff --git a/src/public/javascripts/dialogs/export_subtree.js b/src/public/javascripts/dialogs/export_subtree.js deleted file mode 100644 index 3d6058fa8..000000000 --- a/src/public/javascripts/dialogs/export_subtree.js +++ /dev/null @@ -1,35 +0,0 @@ -import treeService from '../services/tree.js'; -import server from '../services/server.js'; -import treeUtils from "../services/tree_utils.js"; -import exportService from "../services/export.js"; - -const $dialog = $("#export-subtree-dialog"); -const $form = $("#export-subtree-form"); -const $noteTitle = $dialog.find(".note-title"); - -async function showDialog() { - glob.activeDialog = $dialog; - - $dialog.modal(); - - const currentNode = treeService.getCurrentNode(); - const noteTitle = await treeUtils.getNoteTitle(currentNode.data.noteId); - - $noteTitle.html(noteTitle); -} - -$form.submit(() => { - const exportFormat = $dialog.find("input[name='export-format']:checked").val(); - - const currentNode = treeService.getCurrentNode(); - - exportService.exportSubtree(currentNode.data.branchId, exportFormat); - - $dialog.modal('hide'); - - return false; -}); - -export default { - showDialog -}; \ No newline at end of file diff --git a/src/public/javascripts/services/bootstrap.js b/src/public/javascripts/services/bootstrap.js index e1a779d3e..8e3d4f93e 100644 --- a/src/public/javascripts/services/bootstrap.js +++ b/src/public/javascripts/services/bootstrap.js @@ -7,6 +7,7 @@ import recentChangesDialog from '../dialogs/recent_changes.js'; import optionsDialog from '../dialogs/options.js'; import sqlConsoleDialog from '../dialogs/sql_console.js'; import markdownImportDialog from '../dialogs/markdown_import.js'; +import exportDialog from '../dialogs/export.js'; import cloning from './cloning.js'; import contextMenu from './tree_context_menu.js'; @@ -103,12 +104,12 @@ if (utils.isElectron()) { }); } -$("#export-note-to-markdown-button").click(function () { +$("#export-note-button").click(function () { if ($(this).hasClass("disabled")) { return; } - exportService.exportSubtree(noteDetailService.getCurrentNoteId(), 'markdown-single') + exportDialog.showDialog('single'); }); treeService.showTree(); diff --git a/src/public/javascripts/services/export.js b/src/public/javascripts/services/export.js index a459e2d6c..c82532909 100644 --- a/src/public/javascripts/services/export.js +++ b/src/public/javascripts/services/export.js @@ -4,7 +4,7 @@ import protectedSessionHolder from './protected_session_holder.js'; import utils from './utils.js'; import server from './server.js'; -function exportSubtree(noteId, format) { +function exportNote(noteId, format) { const url = utils.getHost() + "/api/notes/" + noteId + "/export/" + format + "?protectedSessionId=" + encodeURIComponent(protectedSessionHolder.getProtectedSessionId()); @@ -47,6 +47,6 @@ $("#import-upload").change(async function() { }); export default { - exportSubtree, + exportNote, importIntoNote }; \ No newline at end of file diff --git a/src/public/javascripts/services/tree.js b/src/public/javascripts/services/tree.js index 778c2f01a..b8a76ae0e 100644 --- a/src/public/javascripts/services/tree.js +++ b/src/public/javascripts/services/tree.js @@ -564,8 +564,6 @@ async function createNote(node, parentNoteId, target, isProtected, saveSelection clearSelectedNodes(); // to unmark previously active node - infoService.showMessage("Created!"); - return {note, branch}; } diff --git a/src/public/javascripts/services/tree_context_menu.js b/src/public/javascripts/services/tree_context_menu.js index a79a2222d..ac21f13b2 100644 --- a/src/public/javascripts/services/tree_context_menu.js +++ b/src/public/javascripts/services/tree_context_menu.js @@ -6,7 +6,7 @@ import protectedSessionService from './protected_session.js'; import treeChangesService from './branches.js'; import treeUtils from './tree_utils.js'; import branchPrefixDialog from '../dialogs/branch_prefix.js'; -import exportSubtreeDialog from '../dialogs/export_subtree.js'; +import exportDialog from '../dialogs/export.js'; import infoService from "./info.js"; import treeCache from "./tree_cache.js"; import syncService from "./sync.js"; @@ -93,7 +93,7 @@ const contextMenuItems = [ {title: "Paste into Ctrl+V", cmd: "pasteInto", uiIcon: "clipboard"}, {title: "Paste after", cmd: "pasteAfter", uiIcon: "clipboard"}, {title: "----"}, - {title: "Export subtree", cmd: "exportSubtree", uiIcon: "arrow-up-right"}, + {title: "Export", cmd: "export", uiIcon: "arrow-up-right"}, {title: "Import into note (tar, opml, md, enex)", cmd: "importIntoNote", uiIcon: "arrow-down-left"}, {title: "----"}, {title: "Collapse subtree Alt+-", cmd: "collapseSubtree", uiIcon: "align-justify"}, @@ -127,7 +127,7 @@ async function getContextMenuItems(event) { enableItem("pasteAfter", clipboardIds.length > 0 && isNotRoot && parentNote.type !== 'search'); enableItem("pasteInto", clipboardIds.length > 0 && note.type !== 'search'); enableItem("importIntoNote", note.type !== 'search'); - enableItem("exportSubtree", note.type !== 'search'); + enableItem("export", note.type !== 'search'); enableItem("editBranchPrefix", isNotRoot && parentNote.type !== 'search'); // Activate node on right-click @@ -179,8 +179,8 @@ function selectContextMenuItem(event, cmd) { else if (cmd === "delete") { treeChangesService.deleteNodes(treeService.getSelectedNodes(true)); } - else if (cmd === "exportSubtree") { - exportSubtreeDialog.showDialog(); + else if (cmd === "export") { + exportDialog.showDialog("subtree"); } else if (cmd === "importIntoNote") { exportService.importIntoNote(node.data.noteId); diff --git a/src/public/stylesheets/style.css b/src/public/stylesheets/style.css index fcc7c1c46..9f4586cd5 100644 --- a/src/public/stylesheets/style.css +++ b/src/public/stylesheets/style.css @@ -684,4 +684,14 @@ div[data-notify="container"] { font-size: x-large; color: #777; z-index: 100; +} + +#export-form .form-check { + padding-top: 10px; + padding-bottom: 10px; +} + +#export-form .format-choice { + padding-left: 40px; + display: none; } \ No newline at end of file diff --git a/src/routes/api/export.js b/src/routes/api/export.js index 2336584a4..b64834e62 100644 --- a/src/routes/api/export.js +++ b/src/routes/api/export.js @@ -9,14 +9,15 @@ const repository = require("../../services/repository"); async function exportNote(req, res) { // entityId maybe either noteId or branchId depending on format const entityId = req.params.entityId; + const type = req.params.type; const format = req.params.format; - if (format === 'native-tar') { - await nativeTarExportService.exportToTar(await repository.getBranch(entityId), res); - } - else if (format === 'markdown-tar') { - await markdownTarExportService.exportToMarkdown(await repository.getBranch(entityId), res); + if (type === 'tar') { + await nativeTarExportService.exportToTar(await repository.getBranch(entityId), format, res); } + // else if (format === 'tar') { + // await markdownTarExportService.exportToMarkdown(await repository.getBranch(entityId), res); + // } // export single note without subtree else if (format === 'markdown-single') { await markdownSingleExportService.exportSingleMarkdown(await repository.getNote(entityId), res); diff --git a/src/services/export/native_tar.js b/src/services/export/native_tar.js index 323998837..3ea4bd544 100644 --- a/src/services/export/native_tar.js +++ b/src/services/export/native_tar.js @@ -3,8 +3,15 @@ const html = require('html'); const native_tar = require('tar-stream'); const sanitize = require("sanitize-filename"); +const mimeTypes = require('mime-types'); +const TurndownService = require('turndown'); + +/** + * @param format - 'html' or 'markdown' + */ +async function exportToTar(branch, format, res) { + const turndownService = new TurndownService(); -async function exportToTar(branch, res) { const pack = native_tar.pack(); const exportedNoteIds = []; @@ -52,6 +59,10 @@ async function exportToTar(branch, res) { }) }; + if (note.type === 'text') { + metadata.format = format; + } + if (await note.hasLabel('excludeFromExport')) { return; } @@ -75,9 +86,35 @@ async function exportToTar(branch, res) { } function saveDataFile(childFileName, note) { - const content = note.type === 'text' ? html.prettyPrint(note.content, {indent_size: 2}) : note.content; + let content = note.content; - pack.entry({name: childFileName + ".dat", size: content.length}, content); + if (note.type === 'text') { + if (format === 'html') { + content = html.prettyPrint(note.content, {indent_size: 2}); + } + else if (format === 'markdown') { + content = turndownService.turndown(note.content); + } + else { + throw new Error("Unknown format: " + format); + } + } + + const extension = mimeTypes.extension(note.mime) + || getExceptionalExtension(note.mime) + || "dat"; + + if (!childFileName.toLowerCase().endsWith(extension)) { + childFileName += "." + extension; + } + + pack.entry({name: childFileName, size: content.length}, content); + } + + function getExceptionalExtension(mime) { + if (mime === 'application/x-javascript') { + return 'js'; + } } function saveMetadataFile(childFileName, metadata) { diff --git a/src/views/dialogs/export.ejs b/src/views/dialogs/export.ejs new file mode 100644 index 000000000..0ae2f2f8c --- /dev/null +++ b/src/views/dialogs/export.ejs @@ -0,0 +1,67 @@ + diff --git a/src/views/dialogs/export_subtree.ejs b/src/views/dialogs/export_subtree.ejs deleted file mode 100644 index 339b6b55e..000000000 --- a/src/views/dialogs/export_subtree.ejs +++ /dev/null @@ -1,46 +0,0 @@ - diff --git a/src/views/index.ejs b/src/views/index.ejs index b6c11d37b..c817f5c1d 100644 --- a/src/views/index.ejs +++ b/src/views/index.ejs @@ -157,9 +157,9 @@ @@ -173,7 +173,7 @@ <% include dialogs/attributes.ejs %> <% include dialogs/branch_prefix.ejs %> <% include dialogs/event_log.ejs %> - <% include dialogs/export_subtree.ejs %> + <% include dialogs/export.ejs %> <% include dialogs/jump_to_note.ejs %> <% include dialogs/markdown_import.ejs %> <% include dialogs/note_revisions.ejs %>