mirror of
https://github.com/zadam/trilium.git
synced 2025-10-28 16:56:34 +01:00
Compare commits
22 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d6046efa1b | ||
|
|
ee608fcf46 | ||
|
|
894b08a1b8 | ||
|
|
4e549baedc | ||
|
|
6b6e42e9ba | ||
|
|
0404b78fb8 | ||
|
|
439743d2b0 | ||
|
|
0ac397e7ff | ||
|
|
d243880099 | ||
|
|
2e23c521c3 | ||
|
|
eb761b286f | ||
|
|
d0f6ff5f98 | ||
|
|
84feaabc52 | ||
|
|
a6036859b8 | ||
|
|
93dcce2217 | ||
|
|
686af0c6a1 | ||
|
|
d07f02b95f | ||
|
|
ad74952194 | ||
|
|
10f3df3ed4 | ||
|
|
18e2e6779b | ||
|
|
ed129c307b | ||
|
|
8742e4bfe9 |
18
package-lock.json
generated
18
package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "trilium",
|
||||
"version": "0.62.1-beta",
|
||||
"version": "0.62.4",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "trilium",
|
||||
"version": "0.62.1-beta",
|
||||
"version": "0.62.4",
|
||||
"hasInstallScript": true,
|
||||
"license": "AGPL-3.0-only",
|
||||
"dependencies": {
|
||||
@@ -81,7 +81,7 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"cross-env": "7.0.3",
|
||||
"electron": "25.9.5",
|
||||
"electron": "25.9.8",
|
||||
"electron-builder": "24.6.4",
|
||||
"electron-packager": "17.1.2",
|
||||
"electron-rebuild": "3.2.9",
|
||||
@@ -4366,9 +4366,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/electron": {
|
||||
"version": "25.9.5",
|
||||
"resolved": "https://registry.npmjs.org/electron/-/electron-25.9.5.tgz",
|
||||
"integrity": "sha512-gM7GXUSd3JVRcYbBnNOtZeNnE5MCJjtZTT8QyIxJvpQ0Dh9dz3hTuEL62dOwnMFW/l47ACQ6es/8qi01P4QGZA==",
|
||||
"version": "25.9.8",
|
||||
"resolved": "https://registry.npmjs.org/electron/-/electron-25.9.8.tgz",
|
||||
"integrity": "sha512-PGgp6PH46QVENHuAHc2NT1Su8Q1qov7qIl2jI5tsDpTibwV2zD8539AeWBQySeBU4dhbj9onIl7+1bXQ0wefBg==",
|
||||
"hasInstallScript": true,
|
||||
"dependencies": {
|
||||
"@electron/get": "^2.0.0",
|
||||
@@ -16964,9 +16964,9 @@
|
||||
}
|
||||
},
|
||||
"electron": {
|
||||
"version": "25.9.5",
|
||||
"resolved": "https://registry.npmjs.org/electron/-/electron-25.9.5.tgz",
|
||||
"integrity": "sha512-gM7GXUSd3JVRcYbBnNOtZeNnE5MCJjtZTT8QyIxJvpQ0Dh9dz3hTuEL62dOwnMFW/l47ACQ6es/8qi01P4QGZA==",
|
||||
"version": "25.9.8",
|
||||
"resolved": "https://registry.npmjs.org/electron/-/electron-25.9.8.tgz",
|
||||
"integrity": "sha512-PGgp6PH46QVENHuAHc2NT1Su8Q1qov7qIl2jI5tsDpTibwV2zD8539AeWBQySeBU4dhbj9onIl7+1bXQ0wefBg==",
|
||||
"requires": {
|
||||
"@electron/get": "^2.0.0",
|
||||
"@types/node": "^18.11.18",
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"name": "trilium",
|
||||
"productName": "Trilium Notes",
|
||||
"description": "Trilium Notes",
|
||||
"version": "0.62.2",
|
||||
"version": "0.62.5",
|
||||
"license": "AGPL-3.0-only",
|
||||
"main": "electron.js",
|
||||
"bin": {
|
||||
@@ -104,7 +104,7 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"cross-env": "7.0.3",
|
||||
"electron": "25.9.5",
|
||||
"electron": "25.9.8",
|
||||
"electron-builder": "24.6.4",
|
||||
"electron-packager": "17.1.2",
|
||||
"electron-rebuild": "3.2.9",
|
||||
|
||||
@@ -427,6 +427,116 @@ paths:
|
||||
application/json; charset=utf-8:
|
||||
schema:
|
||||
$ref: '#/components/schemas/Error'
|
||||
/attachments:
|
||||
post:
|
||||
description: create an attachment
|
||||
operationId: postAttachment
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/CreateAttachment'
|
||||
responses:
|
||||
'201':
|
||||
description: attachment created
|
||||
content:
|
||||
application/json; charset=utf-8:
|
||||
schema:
|
||||
$ref: '#/components/schemas/Attachment'
|
||||
default:
|
||||
description: unexpected error
|
||||
content:
|
||||
application/json; charset=utf-8:
|
||||
schema:
|
||||
$ref: '#/components/schemas/Error'
|
||||
/attachments/{attachmentId}:
|
||||
parameters:
|
||||
- name: attachmentId
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/components/schemas/EntityId'
|
||||
get:
|
||||
description: Returns an attachment identified by its ID
|
||||
operationId: getAttachmentById
|
||||
responses:
|
||||
'200':
|
||||
description: attachment response
|
||||
content:
|
||||
application/json; charset=utf-8:
|
||||
schema:
|
||||
$ref: '#/components/schemas/Attachment'
|
||||
default:
|
||||
description: unexpected error
|
||||
content:
|
||||
application/json; charset=utf-8:
|
||||
schema:
|
||||
$ref: '#/components/schemas/Error'
|
||||
patch:
|
||||
description: patch an attachment identified by the attachmentId with changes in the body. Only role, mime, title, and position are patchable.
|
||||
operationId: patchAttachmentById
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/Attachment'
|
||||
responses:
|
||||
'200':
|
||||
description: attribute updated
|
||||
content:
|
||||
application/json; charset=utf-8:
|
||||
schema:
|
||||
$ref: '#/components/schemas/Attachment'
|
||||
default:
|
||||
description: unexpected error
|
||||
content:
|
||||
application/json; charset=utf-8:
|
||||
schema:
|
||||
$ref: '#/components/schemas/Error'
|
||||
delete:
|
||||
description: deletes an attachment based on the attachmentId supplied.
|
||||
operationId: deleteAttachmentById
|
||||
responses:
|
||||
'204':
|
||||
description: attachment deleted
|
||||
default:
|
||||
description: unexpected error
|
||||
content:
|
||||
application/json; charset=utf-8:
|
||||
schema:
|
||||
$ref: '#/components/schemas/Error'
|
||||
/attachments/{attachmentId}/content:
|
||||
parameters:
|
||||
- name: attachmentId
|
||||
in: path
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/components/schemas/EntityId'
|
||||
get:
|
||||
description: Returns attachment content identified by its ID
|
||||
operationId: getAttachmentContent
|
||||
responses:
|
||||
'200':
|
||||
description: attachment content response
|
||||
content:
|
||||
text/html:
|
||||
schema:
|
||||
type: string
|
||||
put:
|
||||
description: Updates attachment content identified by its ID
|
||||
operationId: putAttachmentContentById
|
||||
requestBody:
|
||||
description: html content of attachment
|
||||
required: true
|
||||
content:
|
||||
text/plain:
|
||||
schema:
|
||||
type: string
|
||||
responses:
|
||||
'204':
|
||||
description: attachment content updated
|
||||
/attributes:
|
||||
post:
|
||||
description: create an attribute for a given note
|
||||
@@ -474,7 +584,7 @@ paths:
|
||||
schema:
|
||||
$ref: '#/components/schemas/Error'
|
||||
patch:
|
||||
description: patch a attribute identified by the attributeId with changes in the body. For labels, only value and position can be updated. For relations, only position can be updated. If you want to modify other properties, you need to delete the old attribute and create a new one.
|
||||
description: patch an attribute identified by the attributeId with changes in the body. For labels, only value and position can be updated. For relations, only position can be updated. If you want to modify other properties, you need to delete the old attribute and create a new one.
|
||||
operationId: patchAttributeById
|
||||
requestBody:
|
||||
required: true
|
||||
@@ -496,7 +606,7 @@ paths:
|
||||
schema:
|
||||
$ref: '#/components/schemas/Error'
|
||||
delete:
|
||||
description: deletes a attribute based on the attributeId supplied.
|
||||
description: deletes an attribute based on the attributeId supplied.
|
||||
operationId: deleteAttributeById
|
||||
responses:
|
||||
'204':
|
||||
@@ -884,6 +994,57 @@ components:
|
||||
$ref: '#/components/schemas/Note'
|
||||
branch:
|
||||
$ref: '#/components/schemas/Branch'
|
||||
Attachment:
|
||||
type: object
|
||||
description: Attachment is owned by a note, has title and content
|
||||
properties:
|
||||
attachmentId:
|
||||
$ref: '#/components/schemas/EntityId'
|
||||
readOnly: true
|
||||
ownerId:
|
||||
$ref: '#/components/schemas/EntityId'
|
||||
description: identifies the owner of the attachment, is either noteId or revisionId
|
||||
role:
|
||||
type: string
|
||||
mime:
|
||||
type: string
|
||||
title:
|
||||
type: string
|
||||
position:
|
||||
type: integer
|
||||
format: int32
|
||||
blobId:
|
||||
type: string
|
||||
description: ID of the blob object which effectively serves as a content hash
|
||||
dateModified:
|
||||
$ref: '#/components/schemas/LocalDateTime'
|
||||
readOnly: true
|
||||
utcDateModified:
|
||||
$ref: '#/components/schemas/UtcDateTime'
|
||||
readOnly: true
|
||||
utcDateScheduledForErasureSince:
|
||||
$ref: '#/components/schemas/UtcDateTime'
|
||||
readOnly: true
|
||||
contentLength:
|
||||
type: integer
|
||||
format: int32
|
||||
CreateAttachment:
|
||||
type: object
|
||||
properties:
|
||||
ownerId:
|
||||
$ref: '#/components/schemas/EntityId'
|
||||
description: identifies the owner of the attachment, is either noteId or revisionId
|
||||
role:
|
||||
type: string
|
||||
mime:
|
||||
type: string
|
||||
title:
|
||||
type: string
|
||||
content:
|
||||
type: string
|
||||
position:
|
||||
type: integer
|
||||
format: int32
|
||||
Attribute:
|
||||
type: object
|
||||
description: Attribute (Label, Relation) is a key-value record attached to a note.
|
||||
|
||||
@@ -117,14 +117,14 @@ async function renderCode(note, $renderedContent) {
|
||||
}
|
||||
|
||||
function renderImage(entity, $renderedContent, options = {}) {
|
||||
const sanitizedTitle = entity.title.replace(/[^a-z0-9-.]/gi, "");
|
||||
const encodedTitle = encodeURIComponent(entity.title);
|
||||
|
||||
let url;
|
||||
|
||||
if (entity instanceof FNote) {
|
||||
url = `api/images/${entity.noteId}/${sanitizedTitle}?${Math.random()}`;
|
||||
url = `api/images/${entity.noteId}/${encodedTitle}?${Math.random()}`;
|
||||
} else if (entity instanceof FAttachment) {
|
||||
url = `api/attachments/${entity.attachmentId}/image/${sanitizedTitle}?${entity.utcDateModified}">`;
|
||||
url = `api/attachments/${entity.attachmentId}/image/${encodedTitle}?${entity.utcDateModified}">`;
|
||||
}
|
||||
|
||||
$renderedContent // styles needed for the zoom to work well
|
||||
|
||||
@@ -42,6 +42,7 @@ async function createLink(notePath, options = {}) {
|
||||
const showNotePath = options.showNotePath === undefined ? false : options.showNotePath;
|
||||
const showNoteIcon = options.showNoteIcon === undefined ? false : options.showNoteIcon;
|
||||
const referenceLink = options.referenceLink === undefined ? false : options.referenceLink;
|
||||
const autoConvertToImage = options.autoConvertToImage === undefined ? false : options.autoConvertToImage;
|
||||
|
||||
const { noteId, parentNoteId } = treeService.getNoteIdAndParentIdFromUrl(notePath);
|
||||
const viewScope = options.viewScope || {};
|
||||
@@ -58,6 +59,16 @@ async function createLink(notePath, options = {}) {
|
||||
}
|
||||
}
|
||||
|
||||
const note = await froca.getNote(noteId);
|
||||
|
||||
if (autoConvertToImage && ['image', 'canvas', 'mermaid'].includes(note.type) && viewMode === 'default') {
|
||||
const encodedTitle = encodeURIComponent(linkTitle);
|
||||
|
||||
return $("<img>")
|
||||
.attr("src", `api/images/${noteId}/${encodedTitle}?${Math.random()}`)
|
||||
.attr("alt", linkTitle);
|
||||
}
|
||||
|
||||
const $container = $("<span>");
|
||||
|
||||
if (showNoteIcon) {
|
||||
|
||||
@@ -97,7 +97,7 @@ export default class IncludeNoteDialog extends BasicWidget {
|
||||
|
||||
const boxSize = $("input[name='include-note-box-size']:checked").val();
|
||||
|
||||
if (note.type === 'image') {
|
||||
if (['image', 'canvas', 'mermaid'].includes(note.type)) {
|
||||
// there's no benefit to use insert note functionlity for images,
|
||||
// so we'll just add an IMG tag
|
||||
this.textTypeWidget.addImage(noteId);
|
||||
|
||||
@@ -274,16 +274,16 @@ export default class RevisionsDialog extends BasicWidget {
|
||||
|
||||
this.$content.html($table);
|
||||
} else if (revisionItem.type === 'canvas') {
|
||||
const sanitizedTitle = revisionItem.title.replace(/[^a-z0-9-.]/gi, "");
|
||||
const encodedTitle = encodeURIComponent(revisionItem.title);
|
||||
|
||||
this.$content.html($("<img>")
|
||||
.attr("src", `api/revisions/${revisionItem.revisionId}/image/${sanitizedTitle}?${Math.random()}`)
|
||||
.attr("src", `api/revisions/${revisionItem.revisionId}/image/${encodedTitle}?${Math.random()}`)
|
||||
.css("max-width", "100%"));
|
||||
} else if (revisionItem.type === 'mermaid') {
|
||||
const sanitizedTitle = revisionItem.title.replace(/[^a-z0-9-.]/gi, "");
|
||||
const encodedTitle = encodeURIComponent(revisionItem.title);
|
||||
|
||||
this.$content.html($("<img>")
|
||||
.attr("src", `api/revisions/${revisionItem.revisionId}/image/${sanitizedTitle}?${Math.random()}`)
|
||||
.attr("src", `api/revisions/${revisionItem.revisionId}/image/${encodedTitle}?${Math.random()}`)
|
||||
.css("max-width", "100%"));
|
||||
|
||||
this.$content.append($("<pre>").text(fullRevision.content));
|
||||
|
||||
@@ -56,7 +56,6 @@ export default class HighlightsListWidget extends RightPanelWidget {
|
||||
.class("icon-action"),
|
||||
new OnClickButtonWidget()
|
||||
.icon("bx-x")
|
||||
.title("Close Highlights List")
|
||||
.titlePlacement("left")
|
||||
.onClick(widget => widget.triggerCommand("closeHlt"))
|
||||
.class("icon-action")
|
||||
|
||||
@@ -258,10 +258,11 @@ export default class NoteDetailWidget extends NoteContextAwareWidget {
|
||||
.append($("<h2>").text(this.note.title))
|
||||
.append($promotedAttributes)
|
||||
.prop('outerHTML'),
|
||||
|
||||
footer: `
|
||||
<script src="${assetPath}/libraries/katex/katex.min.js"></script>
|
||||
<script src="${assetPath}/libraries/katex/mhchem.min.js"></script>
|
||||
<script src="${assetPath}/libraries/katex/auto-render.min.js"></script>
|
||||
<script src="${assetPath}/node_modules/katex/dist/katex.min.js"></script>
|
||||
<script src="${assetPath}/node_modules/katex/dist/contrib/mhchem.min.js"></script>
|
||||
<script src="${assetPath}/node_modules/katex/dist/contrib/auto-render.min.js"></script>
|
||||
<script>
|
||||
document.body.className += ' ck-content printed-content';
|
||||
|
||||
@@ -273,7 +274,7 @@ export default class NoteDetailWidget extends NoteContextAwareWidget {
|
||||
`${assetPath}/libraries/codemirror/codemirror.css`,
|
||||
`${assetPath}/libraries/ckeditor/ckeditor-content.css`,
|
||||
`${assetPath}/libraries/bootstrap/css/bootstrap.min.css`,
|
||||
`${assetPath}/libraries/katex/katex.min.css`,
|
||||
`${assetPath}/node_modules/katex/dist/katex.min.css`,
|
||||
`${assetPath}/stylesheets/print.css`,
|
||||
`${assetPath}/stylesheets/relation_map.css`,
|
||||
`${assetPath}/stylesheets/ckeditor-theme.css`
|
||||
|
||||
@@ -402,11 +402,11 @@ export default class NoteTreeWidget extends NoteContextAwareWidget {
|
||||
}));
|
||||
|
||||
if (notes.length === 1) {
|
||||
linkService.createLink(notes[0].noteId, {referenceLink: true})
|
||||
linkService.createLink(notes[0].noteId, {referenceLink: true, autoConvertToImage: true})
|
||||
.then($link => data.dataTransfer.setData("text/html", $link[0].outerHTML));
|
||||
}
|
||||
else {
|
||||
Promise.all(notes.map(note => linkService.createLink(note.noteId, {referenceLink: true}))).then(links => {
|
||||
Promise.all(notes.map(note => linkService.createLink(note.noteId, {referenceLink: true, autoConvertToImage: true}))).then(links => {
|
||||
const $list = $("<ul>").append(...links.map($link => $("<li>").append($link)));
|
||||
|
||||
data.dataTransfer.setData("text/html", $list[0].outerHTML);
|
||||
|
||||
@@ -8,8 +8,14 @@ import options from "../../services/options.js";
|
||||
import utils from "../../services/utils.js";
|
||||
|
||||
const TPL = `
|
||||
<div>
|
||||
<div class="promoted-attributes-widget">
|
||||
<style>
|
||||
body.mobile .promoted-attributes-widget {
|
||||
/* https://github.com/zadam/trilium/issues/4468 */
|
||||
flex-shrink: 0.4;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.promoted-attributes-container {
|
||||
margin: auto;
|
||||
display: flex;
|
||||
|
||||
@@ -68,7 +68,6 @@ export default class TocWidget extends RightPanelWidget {
|
||||
.class("icon-action"),
|
||||
new OnClickButtonWidget()
|
||||
.icon("bx-x")
|
||||
.title("Close Table of Contents")
|
||||
.titlePlacement("left")
|
||||
.onClick(widget => widget.triggerCommand("closeToc"))
|
||||
.class("icon-action")
|
||||
|
||||
@@ -365,12 +365,10 @@ export default class EditableTextTypeWidget extends AbstractTextTypeWidget {
|
||||
const note = await froca.getNote(noteId);
|
||||
|
||||
this.watchdog.editor.model.change( writer => {
|
||||
const sanitizedTitle = note.title.replace(/[^a-z0-9-.]/gi, "");
|
||||
const src = `api/images/${note.noteId}/${sanitizedTitle}`;
|
||||
const encodedTitle = encodeURIComponent(note.title);
|
||||
const src = `api/images/${note.noteId}/${encodedTitle}`;
|
||||
|
||||
const imageElement = writer.createElement( 'image', { 'src': src } );
|
||||
|
||||
this.watchdog.editor.model.insertContent(imageElement, this.watchdog.editor.model.document.selection);
|
||||
this.watchdog.editor.execute( 'insertImage', { source: src } );
|
||||
} );
|
||||
}
|
||||
|
||||
|
||||
@@ -153,8 +153,9 @@ function processContent(images, note, content) {
|
||||
const buffer = Buffer.from(dataUrl.split(",")[1], 'base64');
|
||||
|
||||
const attachment = imageService.saveImageToAttachment(note.noteId, buffer, filename, true);
|
||||
const sanitizedTitle = attachment.title.replace(/[^a-z0-9-.]/gi, "");
|
||||
const url = `api/attachments/${attachment.attachmentId}/image/${sanitizedTitle}`;
|
||||
|
||||
const encodedTitle = encodeURIComponent(attachment.title);
|
||||
const url = `api/attachments/${attachment.attachmentId}/image/${encodedTitle}`;
|
||||
|
||||
log.info(`Replacing '${imageId}' with '${url}' in note '${note.noteId}'`);
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ const cls = require('../../services/cls');
|
||||
const path = require('path');
|
||||
const becca = require("../../becca/becca");
|
||||
const blobService = require("../../services/blob");
|
||||
const eraseService = require("../../services/erase.js");
|
||||
|
||||
function getRevisionBlob(req) {
|
||||
const preview = req.query.preview === 'true';
|
||||
@@ -88,11 +89,11 @@ function eraseAllRevisions(req) {
|
||||
const revisionIdsToErase = sql.getColumn('SELECT revisionId FROM revisions WHERE noteId = ?',
|
||||
[req.params.noteId]);
|
||||
|
||||
revisionService.eraseRevisions(revisionIdsToErase);
|
||||
eraseService.eraseRevisions(revisionIdsToErase);
|
||||
}
|
||||
|
||||
function eraseRevision(req) {
|
||||
revisionService.eraseRevisions([req.params.revisionId]);
|
||||
eraseService.eraseRevisions([req.params.revisionId]);
|
||||
}
|
||||
|
||||
function restoreRevision(req) {
|
||||
|
||||
@@ -1 +1 @@
|
||||
module.exports = { buildDate:"2023-11-21T20:49:24+01:00", buildRevision: "e2b1421bf3d764ffe444a103c118e67d8c563673" };
|
||||
module.exports = { buildDate:"2024-01-08T00:05:13+01:00", buildRevision: "ee608fcf46a2052f21d94a8a72fc023dcaf955f7" };
|
||||
|
||||
@@ -4,6 +4,7 @@ const becca = require("../becca/becca");
|
||||
const cloningService = require("./cloning");
|
||||
const branchService = require("./branches");
|
||||
const utils = require("./utils");
|
||||
const eraseService = require("./erase.js");
|
||||
|
||||
const ACTION_HANDLERS = {
|
||||
addLabel: (action, note) => {
|
||||
@@ -18,7 +19,7 @@ const ACTION_HANDLERS = {
|
||||
note.deleteNote(deleteId);
|
||||
},
|
||||
deleteRevisions: (action, note) => {
|
||||
revisionService.eraseRevisions(note.getRevisions().map(rev => rev.revisionId));
|
||||
eraseService.eraseRevisions(note.getRevisions().map(rev => rev.revisionId));
|
||||
},
|
||||
deleteLabel: (action, note) => {
|
||||
for (const label of note.getOwnedLabels(action.labelName)) {
|
||||
|
||||
@@ -467,7 +467,7 @@ class ConsistencyChecks {
|
||||
WHERE blobs.blobId IS NULL`,
|
||||
({revisionId, blobId}) => {
|
||||
if (this.autoFix) {
|
||||
revisionService.eraseRevisions([revisionId]);
|
||||
eraseService.eraseRevisions([revisionId]);
|
||||
|
||||
this.reloadNeeded = true;
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ function eraseNotes(noteIdsToErase) {
|
||||
const revisionIdsToErase = sql.getManyRows(`SELECT revisionId FROM revisions WHERE noteId IN (???)`, noteIdsToErase)
|
||||
.map(row => row.revisionId);
|
||||
|
||||
revisionService.eraseRevisions(revisionIdsToErase);
|
||||
eraseRevisions(revisionIdsToErase);
|
||||
|
||||
log.info(`Erased notes: ${JSON.stringify(noteIdsToErase)}`);
|
||||
}
|
||||
@@ -79,6 +79,18 @@ function eraseAttachments(attachmentIdsToErase) {
|
||||
log.info(`Erased attachments: ${JSON.stringify(attachmentIdsToErase)}`);
|
||||
}
|
||||
|
||||
function eraseRevisions(revisionIdsToErase) {
|
||||
if (revisionIdsToErase.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
sql.executeMany(`DELETE FROM revisions WHERE revisionId IN (???)`, revisionIdsToErase);
|
||||
|
||||
setEntityChangesAsErased(sql.getManyRows(`SELECT * FROM entity_changes WHERE entityName = 'revisions' AND entityId IN (???)`, revisionIdsToErase));
|
||||
|
||||
log.info(`Removed revisions: ${JSON.stringify(revisionIdsToErase)}`);
|
||||
}
|
||||
|
||||
function eraseUnusedBlobs() {
|
||||
const unusedBlobIds = sql.getColumn(`
|
||||
SELECT blobs.blobId
|
||||
@@ -184,5 +196,6 @@ module.exports = {
|
||||
eraseUnusedAttachmentsNow,
|
||||
eraseNotesWithDeleteId,
|
||||
eraseUnusedBlobs,
|
||||
eraseAttachments
|
||||
eraseAttachments,
|
||||
eraseRevisions
|
||||
};
|
||||
|
||||
@@ -303,8 +303,8 @@ function importEnex(taskContext, file, parentNote) {
|
||||
|
||||
const attachment = imageService.saveImageToAttachment(noteEntity.noteId, resource.content, originalName, taskContext.data.shrinkImages);
|
||||
|
||||
const sanitizedTitle = attachment.title.replace(/[^a-z0-9-.]/gi, "");
|
||||
const url = `api/attachments/${attachment.attachmentId}/image/${sanitizedTitle}`;
|
||||
const encodedTitle = encodeURIComponent(attachment.title);
|
||||
const url = `api/attachments/${attachment.attachmentId}/image/${encodedTitle}`;
|
||||
const imageLink = `<img src="${url}">`;
|
||||
|
||||
content = content.replace(mediaRegex, imageLink);
|
||||
|
||||
@@ -494,7 +494,7 @@ async function downloadImage(noteId, imageUrl) {
|
||||
const title = path.basename(parsedUrl.pathname);
|
||||
|
||||
const imageService = require('../services/image');
|
||||
const {attachment} = imageService.saveImageToAttachment(noteId, imageBuffer, title, true, true);
|
||||
const attachment = imageService.saveImageToAttachment(noteId, imageBuffer, title, true, true);
|
||||
|
||||
imageUrlToAttachmentIdMapping[imageUrl] = attachment.attachmentId;
|
||||
|
||||
@@ -511,7 +511,7 @@ const downloadImagePromises = {};
|
||||
function replaceUrl(content, url, attachment) {
|
||||
const quotedUrl = utils.quoteRegex(url);
|
||||
|
||||
return content.replace(new RegExp(`\\s+src=[\"']${quotedUrl}[\"']`, "ig"), ` src="api/attachments/${encodeURIComponent(attachment.title)}/image"`);
|
||||
return content.replace(new RegExp(`\\s+src=[\"']${quotedUrl}[\"']`, "ig"), ` src="api/attachments/${attachment.attachmentId}/image/${encodeURIComponent(attachment.title)}"`);
|
||||
}
|
||||
|
||||
function downloadImages(noteId, content) {
|
||||
@@ -519,7 +519,7 @@ function downloadImages(noteId, content) {
|
||||
let imageMatch;
|
||||
|
||||
while (imageMatch = imageRe.exec(content)) {
|
||||
const url = imageMatch[1];
|
||||
let url = imageMatch[1];
|
||||
const inlineImageMatch = /^data:image\/[a-z]+;base64,/.exec(url);
|
||||
|
||||
if (inlineImageMatch) {
|
||||
@@ -529,9 +529,9 @@ function downloadImages(noteId, content) {
|
||||
const imageService = require('../services/image');
|
||||
const attachment = imageService.saveImageToAttachment(noteId, imageBuffer, "inline image", true, true);
|
||||
|
||||
const sanitizedTitle = attachment.title.replace(/[^a-z0-9-.]/gi, "");
|
||||
const encodedTitle = encodeURIComponent(attachment.title);
|
||||
|
||||
content = `${content.substr(0, imageMatch.index)}<img src="api/attachments/${attachment.attachmentId}/image/${sanitizedTitle}"${content.substr(imageMatch.index + imageMatch[0].length)}`;
|
||||
content = `${content.substr(0, imageMatch.index)}<img src="api/attachments/${attachment.attachmentId}/image/${encodedTitle}"${content.substr(imageMatch.index + imageMatch[0].length)}`;
|
||||
}
|
||||
else if (!url.includes('api/images/') && !/api\/attachments\/.+\/image\/?.*/.test(url)
|
||||
// this is an exception for the web clipper's "imageId"
|
||||
@@ -541,6 +541,8 @@ function downloadImages(noteId, content) {
|
||||
continue;
|
||||
}
|
||||
|
||||
url = utils.unescapeHtml(url);
|
||||
|
||||
if (url in imageUrlToAttachmentIdMapping) {
|
||||
const attachment = becca.getAttachment(imageUrlToAttachmentIdMapping[url]);
|
||||
|
||||
@@ -636,6 +638,10 @@ function saveAttachments(note, content) {
|
||||
content = `${content.substr(0, attachmentMatch.index)}<a class="reference-link" href="#root/${note.noteId}?viewMode=attachments&attachmentId=${attachment.attachmentId}">${title}</a>${content.substr(attachmentMatch.index + attachmentMatch[0].length)}`;
|
||||
}
|
||||
|
||||
// removing absolute references to server to keep it working between instances,
|
||||
// we also omit / at the beginning to keep the paths relative
|
||||
content = content.replace(/src="[^"]*\/api\/attachments\//g, 'src="api/attachments/');
|
||||
|
||||
return content;
|
||||
}
|
||||
|
||||
@@ -889,6 +895,10 @@ function scanForLinks(note, content) {
|
||||
* Things which have to be executed after updating content, but asynchronously (separate transaction)
|
||||
*/
|
||||
async function asyncPostProcessContent(note, content) {
|
||||
if (note.hasStringContent() && !utils.isString(content)) {
|
||||
content = content.toString();
|
||||
}
|
||||
|
||||
scanForLinks(note, content);
|
||||
}
|
||||
|
||||
|
||||
@@ -46,18 +46,6 @@ function protectRevisions(note) {
|
||||
}
|
||||
}
|
||||
|
||||
function eraseRevisions(revisionIdsToErase) {
|
||||
if (revisionIdsToErase.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
log.info(`Removing revisions: ${JSON.stringify(revisionIdsToErase)}`);
|
||||
|
||||
sql.executeMany(`DELETE FROM revisions WHERE revisionId IN (???)`, revisionIdsToErase);
|
||||
sql.executeMany(`UPDATE entity_changes SET isErased = 1, utcDateChanged = '${dateUtils.utcNowDateTime()}' WHERE entityName = 'revisions' AND entityId IN (???)`, revisionIdsToErase);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
protectRevisions,
|
||||
eraseRevisions
|
||||
protectRevisions
|
||||
};
|
||||
|
||||
@@ -209,8 +209,9 @@ function sortNotesIfNeeded(parentNoteId) {
|
||||
function setNoteToParent(noteId, prefix, parentNoteId) {
|
||||
const parentNote = becca.getNote(parentNoteId);
|
||||
|
||||
if (!parentNote) {
|
||||
throw new Error(`Cannot move note to deleted parent note '${parentNoteId}'`);
|
||||
if (parentNoteId && !parentNote) {
|
||||
// null parentNoteId is a valid value
|
||||
throw new Error(`Cannot move note to deleted / missing parent note '${parentNoteId}'`);
|
||||
}
|
||||
|
||||
// case where there might be more such branches is ignored. It's expected there should be just one
|
||||
|
||||
@@ -63,6 +63,8 @@ function isElectron() {
|
||||
}
|
||||
|
||||
function hash(text) {
|
||||
text = text.normalize();
|
||||
|
||||
return crypto.createHash('sha1').update(text).digest('base64');
|
||||
}
|
||||
|
||||
@@ -303,6 +305,10 @@ function toMap(list, key) {
|
||||
return map;
|
||||
}
|
||||
|
||||
function isString(x) {
|
||||
return Object.prototype.toString.call(x) === "[object String]";
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
randomSecureToken,
|
||||
randomString,
|
||||
@@ -335,4 +341,5 @@ module.exports = {
|
||||
normalize,
|
||||
hashedBlobId,
|
||||
toMap,
|
||||
isString
|
||||
};
|
||||
|
||||
@@ -105,10 +105,10 @@ function renderText(result, note) {
|
||||
|
||||
if (result.content.includes(`<span class="math-tex">`)) {
|
||||
result.header += `
|
||||
<script src="../../${assetPath}/libraries/katex/katex.min.js"></script>
|
||||
<link rel="stylesheet" href="../../${assetPath}/libraries/katex/katex.min.css">
|
||||
<script src="../../${assetPath}/libraries/katex/auto-render.min.js"></script>
|
||||
<script src="../../${assetPath}/libraries/katex/mhchem.min.js"></script>
|
||||
<script src="../../${assetPath}/node_modules/katex/dist/katex.min.js"></script>
|
||||
<link rel="stylesheet" href="../../${assetPath}/node_modules/katex/dist/katex.min.css">
|
||||
<script src="../../${assetPath}/node_modules/katex/dist/contrib/auto-render.min.js"></script>
|
||||
<script src="../../${assetPath}/node_modules/katex/dist/contrib/mhchem.min.js"></script>
|
||||
<script>
|
||||
document.addEventListener("DOMContentLoaded", function() {
|
||||
renderMathInElement(document.getElementById('content'));
|
||||
|
||||
Reference in New Issue
Block a user