diff --git a/src/public/app/services/froca.js b/src/public/app/services/froca.js index de2ec7192..f978a2fa7 100644 --- a/src/public/app/services/froca.js +++ b/src/public/app/services/froca.js @@ -360,8 +360,6 @@ class Froca { opts.preview = !!opts.preview; const key = `${entityType}-${entityId}-${opts.preview}`; - console.log(key); - if (!this.blobPromises[key]) { this.blobPromises[key] = server.get(`${entityType}/${entityId}/blob?preview=${opts.preview}`) .then(row => new FBlob(row)) diff --git a/src/services/export/single.js b/src/services/export/single.js index 24b18bf75..7d2852b26 100644 --- a/src/services/export/single.js +++ b/src/services/export/single.js @@ -23,13 +23,16 @@ function exportSingleNote(taskContext, branch, format, res) { if (note.type === 'text') { if (format === 'html') { - content = inlineAttachmentImages(content); + content = inlineAttachments(content); if (!content.toLowerCase().includes("${content}`; } - payload = html.prettyPrint(content, {indent_size: 2}); + payload = content.length < 100_000 + ? html.prettyPrint(content, {indent_size: 2}) + : content; + extension = 'html'; mime = 'text/html'; } @@ -61,30 +64,40 @@ function exportSingleNote(taskContext, branch, format, res) { taskContext.taskSucceeded(); } -function inlineAttachmentImages(content) { - const re = /src="[^"]*api\/attachments\/([a-zA-Z0-9_]+)\/image\/?[^"]+"/g; - let match; - - while (match = re.exec(content)) { - const attachment = becca.getAttachment(match[1]); - if (!attachment) { - continue; - } - - if (!attachment.mime.startsWith('image/')) { - continue; +function inlineAttachments(content) { + content = content.replace(/src="[^"]*api\/attachments\/([a-zA-Z0-9_]+)\/image\/?[^"]+"/g, (match, attachmentId) => { + const attachment = becca.getAttachment(attachmentId); + if (!attachment || !attachment.mime.startsWith('image/')) { + return match; } const attachmentContent = attachment.getContent(); if (!Buffer.isBuffer(attachmentContent)) { - continue; + return match; } const base64Content = attachmentContent.toString('base64'); const srcValue = `data:${attachment.mime};base64,${base64Content}`; - content = content.replaceAll(match[0], `src="${srcValue}"`); - } + return `src="${srcValue}"`; + }); + + content = content.replace(/href="[^"]*#root[^"]*attachmentId=([a-zA-Z0-9_]+)\/?"/g, (match, attachmentId) => { + const attachment = becca.getAttachment(attachmentId); + if (!attachment) { + return match; + } + + const attachmentContent = attachment.getContent(); + if (!Buffer.isBuffer(attachmentContent)) { + return match; + } + + const base64Content = attachmentContent.toString('base64'); + const hrefValue = `data:${attachment.mime};base64,${base64Content}`; + + return `href="${hrefValue}" download="${utils.escapeHtml(attachment.title)}"`; + }); return content; } diff --git a/src/services/export/zip.js b/src/services/export/zip.js index a64bbe2c3..91ec8528a 100644 --- a/src/services/export/zip.js +++ b/src/services/export/zip.js @@ -280,6 +280,26 @@ async function exportToZip(taskContext, branch, format, res, setHeaders = true) }); content = content.replace(/src="[^"]*api\/attachments\/([a-zA-Z0-9_]+)\/image\/[^"]*"/g, (match, targetAttachmentId) => { + const url = findAttachment(targetAttachmentId); + + return url ? `src="${url}"` : match; + }); + + content = content.replace(/href="[^"]*#root[^"]*attachmentId=([a-zA-Z0-9_]+)\/?"/g, (match, targetAttachmentId) => { + const url = findAttachment(targetAttachmentId); + + return url ? `href="${url}"` : match; + }); + + content = content.replace(/href="[^"]*#root[a-zA-Z0-9_\/]*\/([a-zA-Z0-9_]+)[^"]*"/g, (match, targetNoteId) => { + const url = getNoteTargetUrl(targetNoteId, noteMeta); + + return url ? `href="${url}"` : match; + }); + + return content; + + function findAttachment(targetAttachmentId) { let url; const attachmentMeta = noteMeta.attachments.find(attMeta => attMeta.attachmentId === targetAttachmentId); @@ -289,17 +309,8 @@ async function exportToZip(taskContext, branch, format, res, setHeaders = true) } else { log.info(`Could not find attachment meta object for attachmentId '${targetAttachmentId}'`); } - - return url ? `src="${url}"` : match; - }); - - content = content.replace(/href="[^"]*#root[a-zA-Z0-9_\/]*\/([a-zA-Z0-9_]+)\/?"/g, (match, targetNoteId) => { - const url = getNoteTargetUrl(targetNoteId, noteMeta); - - return url ? `href="${url}"` : match; - }); - - return content; + return url; + } } /** @@ -339,7 +350,7 @@ async function exportToZip(taskContext, branch, format, res, setHeaders = true) `; } - return content.length < 100000 + return content.length < 100_000 ? html.prettyPrint(content, {indent_size: 2}) : content; } else if (noteMeta.format === 'markdown') { @@ -451,7 +462,9 @@ ${markdownContent}`; `; - const prettyHtml = html.prettyPrint(fullHtml, {indent_size: 2}); + const prettyHtml = fullHtml.length < 100_000 + ? html.prettyPrint(fullHtml, {indent_size: 2}) + : fullHtml; archive.append(prettyHtml, { name: navigationMeta.dataFileName }); } diff --git a/src/services/ws.js b/src/services/ws.js index cb8b132ef..d0ca24edc 100644 --- a/src/services/ws.js +++ b/src/services/ws.js @@ -103,8 +103,8 @@ function fillInAdditionalProperties(entityChange) { } // fill in some extra data needed by the frontend - // first try to use becca which works for non-deleted entities - // only when that fails try to load from database + // first try to use becca, which works for non-deleted entities + // only when that fails, try to load from the database if (entityChange.entityName === 'attributes') { entityChange.entity = becca.getAttribute(entityChange.entityId); @@ -150,11 +150,7 @@ function fillInAdditionalProperties(entityChange) { } else if (entityChange.entityName === 'blobs') { entityChange.noteIds = sql.getColumn("SELECT noteId FROM notes WHERE blobId = ? AND isDeleted = 0", [entityChange.entityId]); } else if (entityChange.entityName === 'attachments') { - entityChange.entity = sql.getRow(` - SELECT attachments.*, LENGTH(blobs.content) - FROM attachments - JOIN blobs ON blobs.blobId = attachments.blobId - WHERE attachmentId = ?`, [entityChange.entityId]); + entityChange.entity = becca.getAttachment(entityChange.entityId, {includeContentLength: true}); } if (entityChange.entity instanceof AbstractBeccaEntity) {