single file export working, tar WIP

This commit is contained in:
azivner 2018-11-24 20:58:38 +01:00
parent ee23bcc783
commit e09b61d1ac
11 changed files with 103 additions and 159 deletions

17
package-lock.json generated
View file

@ -1,6 +1,6 @@
{
"name": "trilium",
"version": "0.24.3-beta",
"version": "0.24.4-beta",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
@ -6417,11 +6417,18 @@
"integrity": "sha512-L+xvyD9MkoYMXb1jAmzI/lWYAxAMCPvIBSWur0PZ5nOf5euahRLVqH//FKW9mWp2lkqUgYiXPgkzfMUFi4zVDw=="
},
"mime-types": {
"version": "2.1.20",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.20.tgz",
"integrity": "sha512-HrkrPaP9vGuWbLK1B1FfgAkbqNjIuy4eHlIYnFi7kamZyLLrGlo2mpcx0bBmNpKqBtYtAfGbodDddIgddSJC2A==",
"version": "2.1.21",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.21.tgz",
"integrity": "sha512-3iL6DbwpyLzjR3xHSFNFeb9Nz/M8WDkX33t1GFQnFOllWk8pOrh/LSrB5OXlnlW5P9LH73X6loW/eogc+F5lJg==",
"requires": {
"mime-db": "~1.36.0"
"mime-db": "~1.37.0"
},
"dependencies": {
"mime-db": {
"version": "1.37.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.37.0.tgz",
"integrity": "sha512-R3C4db6bgQhlIhPU48fUtdVmKnflq+hRdad7IyKhtFj06VPNVdk2RhiYL3UjQIlso8L+YxAtFkobT0VK+S/ybg=="
}
}
},
"mimic-fn": {

View file

@ -45,6 +45,7 @@
"imagemin-pngquant": "6.0.0",
"ini": "1.3.5",
"jimp": "0.5.6",
"mime-types": "^2.1.21",
"moment": "2.22.2",
"multer": "1.4.1",
"open": "0.0.5",

View file

@ -34,9 +34,19 @@ async function showDialog(defaultType) {
$form.submit(() => {
const exportType = $dialog.find("input[name='export-type']:checked").val();
if (!exportType) {
// this shouldn't happen as we always choose default export type
alert("Choose export type first please");
return;
}
const exportFormat = exportType === 'subtree'
? $("input[name=export-subtree-format]:checked").val()
: $("input[name=export-single-format]:checked").val();
const currentNode = treeService.getCurrentNode();
exportService.exportNote(currentNode.data.branchId, exportType);
exportService.exportBranch(currentNode.data.branchId, exportType, exportFormat);
$dialog.modal('hide');

View file

@ -1,16 +1,14 @@
import treeService from './tree.js';
import infoService from './info.js';
import protectedSessionHolder from './protected_session_holder.js';
import utils from './utils.js';
import server from './server.js';
function exportNote(noteId, format) {
const url = utils.getHost() + "/api/notes/" + noteId + "/export/" + format +
"?protectedSessionId=" + encodeURIComponent(protectedSessionHolder.getProtectedSessionId());
function exportBranch(branchId, type, format) {
const url = utils.getHost() + `/api/notes/${branchId}/export/${type}/${format}?protectedSessionId=` + encodeURIComponent(protectedSessionHolder.getProtectedSessionId());
console.log(url);
utils.download(url);
infoService.showMessage("Export to file has been finished.");
}
let importNoteId;
@ -47,6 +45,6 @@ $("#import-upload").change(async function() {
});
export default {
exportNote,
exportBranch,
importIntoNote
};

View file

@ -1,29 +1,22 @@
"use strict";
const nativeTarExportService = require('../../services/export/native_tar');
const markdownTarExportService = require('../../services/export/markdown_tar');
const markdownSingleExportService = require('../../services/export/markdown_single');
const tarExportService = require('../../services/export/tar');
const singleExportService = require('../../services/export/single');
const opmlExportService = require('../../services/export/opml');
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;
async function exportBranch(req, res) {
const {branchId, type, format} = req.params;
const branch = await repository.getBranch(branchId);
if (type === 'tar') {
await nativeTarExportService.exportToTar(await repository.getBranch(entityId), format, res);
if (type === 'subtree' && (format === 'html' || format === 'markdown')) {
await tarExportService.exportToTar(branch, 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);
else if (type === 'single') {
await singleExportService.exportSingleNote(branch, format, res);
}
else if (format === 'opml') {
await opmlExportService.exportToOpml(await repository.getBranch(entityId), res);
await opmlExportService.exportToOpml(branch, res);
}
else {
return [404, "Unrecognized export format " + format];
@ -31,5 +24,5 @@ async function exportNote(req, res) {
}
module.exports = {
exportNote
exportBranch
};

View file

@ -128,7 +128,7 @@ function register(app) {
apiRoute(PUT, '/api/notes/:noteId/clone-to/:parentNoteId', cloningApiRoute.cloneNoteToParent);
apiRoute(PUT, '/api/notes/:noteId/clone-after/:afterBranchId', cloningApiRoute.cloneNoteAfter);
route(GET, '/api/notes/:entityId/export/:format', [auth.checkApiAuthOrElectron], exportRoute.exportNote);
route(GET, '/api/notes/:branchId/export/:type/:format', [auth.checkApiAuthOrElectron], exportRoute.exportBranch);
route(POST, '/api/notes/:parentNoteId/import', [auth.checkApiAuthOrElectron, uploadMiddleware], importRoute.importToBranch, apiResultHandler);
route(POST, '/api/notes/:parentNoteId/upload', [auth.checkApiAuthOrElectron, uploadMiddleware],

View file

@ -1,31 +0,0 @@
"use strict";
const sanitize = require("sanitize-filename");
const TurndownService = require('turndown');
async function exportSingleMarkdown(note, res) {
if (note.type !== 'text' && note.type !== 'code') {
return [400, `Note type ${note.type} cannot be exported as single markdown file.`];
}
let markdown;
if (note.type === 'code') {
markdown = '```\n' + note.content + "\n```";
}
else if (note.type === 'text') {
const turndownService = new TurndownService();
markdown = turndownService.turndown(note.content);
}
const name = sanitize(note.title);
res.setHeader('Content-Disposition', 'file; filename="' + name + '.md"');
res.setHeader('Content-Type', 'text/markdown; charset=UTF-8');
res.send(markdown);
}
module.exports = {
exportSingleMarkdown
};

View file

@ -1,91 +0,0 @@
"use strict";
const tar = require('tar-stream');
const TurndownService = require('turndown');
const sanitize = require("sanitize-filename");
const markdownSingleExportService = require('../../services/export/markdown_single');
async function exportToMarkdown(branch, res) {
const note = await branch.getNote();
if (!await note.hasChildren()) {
await markdownSingleExportService.exportSingleMarkdown(note, res);
return;
}
const turndownService = new TurndownService();
const pack = tar.pack();
const name = await exportNoteInner(note, '');
async function exportNoteInner(note, directory) {
const childFileName = directory + sanitize(note.title);
if (await note.hasLabel('excludeFromExport')) {
return;
}
saveNote(childFileName, note);
const childNotes = await note.getChildNotes();
if (childNotes.length > 0) {
saveDirectory(childFileName);
}
for (const childNote of childNotes) {
await exportNoteInner(childNote, childFileName + "/");
}
return childFileName;
}
function saveTextNote(childFileName, note) {
if (note.content.trim().length === 0) {
return;
}
let markdown;
if (note.type === 'code') {
markdown = '```\n' + note.content + "\n```";
}
else if (note.type === 'text') {
markdown = turndownService.turndown(note.content);
}
else {
// other note types are not supported
return;
}
pack.entry({name: childFileName + ".md", size: markdown.length}, markdown);
}
function saveFileNote(childFileName, note) {
pack.entry({name: childFileName, size: note.content.length}, note.content);
}
function saveNote(childFileName, note) {
if (note.type === 'text' || note.type === 'code') {
saveTextNote(childFileName, note);
}
else if (note.type === 'image' || note.type === 'file') {
saveFileNote(childFileName, note);
}
}
function saveDirectory(childFileName) {
pack.entry({name: childFileName, type: 'directory'});
}
pack.finalize();
res.setHeader('Content-Disposition', 'file; filename="' + name + '.tar"');
res.setHeader('Content-Type', 'application/tar');
pack.pipe(res);
}
module.exports = {
exportToMarkdown
};

View file

@ -0,0 +1,57 @@
"use strict";
const sanitize = require("sanitize-filename");
const TurndownService = require('turndown');
const mimeTypes = require('mime-types');
const html = require('html');
async function exportSingleNote(branch, format, res) {
const note = await branch.getNote();
if (note.type === 'image' || note.type === 'file') {
return [400, `Note type ${note.type} cannot be exported as single file.`];
}
if (format !== 'html' && format !== 'markdown') {
return [400, 'Unrecognized format ' + format];
}
let payload, extension, mime;
if (note.type === 'text') {
if (format === 'html') {
payload = html.prettyPrint(note.content, {indent_size: 2});
extension = 'html';
mime = 'text/html';
}
else if (format === 'markdown') {
const turndownService = new TurndownService();
payload = turndownService.turndown(note.content);
extension = 'md';
mime = 'text/markdown'
}
}
else if (note.type === 'code') {
payload = note.content;
extension = mimeTypes.extension(note.mime) || 'code';
mime = note.mime;
}
else if (note.type === 'relation-map' || note.type === 'search') {
payload = note.content;
extension = 'json';
mime = 'application/json';
}
const name = sanitize(note.title);
console.log(name, extension, mime);
res.setHeader('Content-Disposition', `file; filename="${name}.${extension}"`);
res.setHeader('Content-Type', mime + '; charset=UTF-8');
res.send(payload);
}
module.exports = {
exportSingleNote
};

View file

@ -1,7 +1,7 @@
"use strict";
const html = require('html');
const native_tar = require('tar-stream');
const tar = require('tar-stream');
const sanitize = require("sanitize-filename");
const mimeTypes = require('mime-types');
const TurndownService = require('turndown');
@ -12,7 +12,7 @@ const TurndownService = require('turndown');
async function exportToTar(branch, format, res) {
const turndownService = new TurndownService();
const pack = native_tar.pack();
const pack = tar.pack();
const exportedNoteIds = [];
const name = await exportNoteInner(branch, '');
@ -79,7 +79,7 @@ async function exportToTar(branch, format, res) {
}
for (const childBranch of childBranches) {
await exportNoteInner(childBranch, childFileName + "/");
await exportNoteInner(await childBranch.getNote(), childBranch, childFileName + "/");
}
return childFileName;

View file

@ -23,7 +23,7 @@
<div class="form-check">
<input class="form-check-input" type="radio" name="export-subtree-format" id="export-subtree-format-markdown"
value="markdown-tar">
value="markdown">
<label class="form-check-label" for="export-subtree-format-markdown">
Markdown - this preserves most of the formatting.
</label>
@ -51,7 +51,7 @@
<div class="form-check">
<input class="form-check-input" type="radio" name="export-single-format" id="export-single-format-markdown"
value="markdown-tar">
value="markdown">
<label class="form-check-label" for="export-single-format-markdown">
Markdown - this preserves most of the formatting.
</label>