mirror of
				https://github.com/zadam/trilium.git
				synced 2025-11-03 20:06:08 +01:00 
			
		
		
		
	fixes, allowing conversion of note into an attachment
This commit is contained in:
		
							
								
								
									
										2
									
								
								libraries/ckeditor/ckeditor.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								libraries/ckeditor/ckeditor.js
									
									
									
									
										vendored
									
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										28
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										28
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							@@ -15,7 +15,7 @@
 | 
			
		||||
        "@excalidraw/excalidraw": "0.15.2",
 | 
			
		||||
        "archiver": "5.3.1",
 | 
			
		||||
        "async-mutex": "0.4.0",
 | 
			
		||||
        "axios": "1.3.6",
 | 
			
		||||
        "axios": "1.4.0",
 | 
			
		||||
        "better-sqlite3": "7.4.5",
 | 
			
		||||
        "chokidar": "3.5.3",
 | 
			
		||||
        "cls-hooked": "4.2.2",
 | 
			
		||||
@@ -99,7 +99,7 @@
 | 
			
		||||
        "nodemon": "2.0.22",
 | 
			
		||||
        "prettier": "2.8.8",
 | 
			
		||||
        "rcedit": "3.0.1",
 | 
			
		||||
        "webpack": "5.80.0",
 | 
			
		||||
        "webpack": "5.81.0",
 | 
			
		||||
        "webpack-cli": "5.0.2"
 | 
			
		||||
      },
 | 
			
		||||
      "optionalDependencies": {
 | 
			
		||||
@@ -2299,9 +2299,9 @@
 | 
			
		||||
      "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ=="
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/axios": {
 | 
			
		||||
      "version": "1.3.6",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/axios/-/axios-1.3.6.tgz",
 | 
			
		||||
      "integrity": "sha512-PEcdkk7JcdPiMDkvM4K6ZBRYq9keuVJsToxm2zQIM70Qqo2WHTdJZMXcG9X+RmRp2VPNUQC8W1RAGbgt6b1yMg==",
 | 
			
		||||
      "version": "1.4.0",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/axios/-/axios-1.4.0.tgz",
 | 
			
		||||
      "integrity": "sha512-S4XCWMEmzvo64T9GfvQDOXgYRDJ/wsSZc7Jvdgx5u1sd0JwsuPLqb3SYmusag+edF6ziyMensPVqLTSc1PiSEA==",
 | 
			
		||||
      "dependencies": {
 | 
			
		||||
        "follow-redirects": "^1.15.0",
 | 
			
		||||
        "form-data": "^4.0.0",
 | 
			
		||||
@@ -12666,9 +12666,9 @@
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/webpack": {
 | 
			
		||||
      "version": "5.80.0",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.80.0.tgz",
 | 
			
		||||
      "integrity": "sha512-OIMiq37XK1rWO8mH9ssfFKZsXg4n6klTEDL7S8/HqbAOBBaiy8ABvXvz0dDCXeEF9gqwxSvVk611zFPjS8hJxA==",
 | 
			
		||||
      "version": "5.81.0",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.81.0.tgz",
 | 
			
		||||
      "integrity": "sha512-AAjaJ9S4hYCVODKLQTgG5p5e11hiMawBwV2v8MYLE0C/6UAGLuAF4n1qa9GOwdxnicaP+5k6M5HrLmD4+gIB8Q==",
 | 
			
		||||
      "dev": true,
 | 
			
		||||
      "dependencies": {
 | 
			
		||||
        "@types/eslint-scope": "^3.7.3",
 | 
			
		||||
@@ -14890,9 +14890,9 @@
 | 
			
		||||
      "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ=="
 | 
			
		||||
    },
 | 
			
		||||
    "axios": {
 | 
			
		||||
      "version": "1.3.6",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/axios/-/axios-1.3.6.tgz",
 | 
			
		||||
      "integrity": "sha512-PEcdkk7JcdPiMDkvM4K6ZBRYq9keuVJsToxm2zQIM70Qqo2WHTdJZMXcG9X+RmRp2VPNUQC8W1RAGbgt6b1yMg==",
 | 
			
		||||
      "version": "1.4.0",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/axios/-/axios-1.4.0.tgz",
 | 
			
		||||
      "integrity": "sha512-S4XCWMEmzvo64T9GfvQDOXgYRDJ/wsSZc7Jvdgx5u1sd0JwsuPLqb3SYmusag+edF6ziyMensPVqLTSc1PiSEA==",
 | 
			
		||||
      "requires": {
 | 
			
		||||
        "follow-redirects": "^1.15.0",
 | 
			
		||||
        "form-data": "^4.0.0",
 | 
			
		||||
@@ -22757,9 +22757,9 @@
 | 
			
		||||
      "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g=="
 | 
			
		||||
    },
 | 
			
		||||
    "webpack": {
 | 
			
		||||
      "version": "5.80.0",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.80.0.tgz",
 | 
			
		||||
      "integrity": "sha512-OIMiq37XK1rWO8mH9ssfFKZsXg4n6klTEDL7S8/HqbAOBBaiy8ABvXvz0dDCXeEF9gqwxSvVk611zFPjS8hJxA==",
 | 
			
		||||
      "version": "5.81.0",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.81.0.tgz",
 | 
			
		||||
      "integrity": "sha512-AAjaJ9S4hYCVODKLQTgG5p5e11hiMawBwV2v8MYLE0C/6UAGLuAF4n1qa9GOwdxnicaP+5k6M5HrLmD4+gIB8Q==",
 | 
			
		||||
      "dev": true,
 | 
			
		||||
      "requires": {
 | 
			
		||||
        "@types/eslint-scope": "^3.7.3",
 | 
			
		||||
 
 | 
			
		||||
@@ -36,7 +36,7 @@
 | 
			
		||||
    "@excalidraw/excalidraw": "0.15.2",
 | 
			
		||||
    "archiver": "5.3.1",
 | 
			
		||||
    "async-mutex": "0.4.0",
 | 
			
		||||
    "axios": "1.3.6",
 | 
			
		||||
    "axios": "1.4.0",
 | 
			
		||||
    "better-sqlite3": "7.4.5",
 | 
			
		||||
    "chokidar": "3.5.3",
 | 
			
		||||
    "cls-hooked": "4.2.2",
 | 
			
		||||
@@ -117,7 +117,7 @@
 | 
			
		||||
    "prettier": "2.8.8",
 | 
			
		||||
    "nodemon": "2.0.22",
 | 
			
		||||
    "rcedit": "3.0.1",
 | 
			
		||||
    "webpack": "5.80.0",
 | 
			
		||||
    "webpack": "5.81.0",
 | 
			
		||||
    "webpack-cli": "5.0.2"
 | 
			
		||||
  },
 | 
			
		||||
  "optionalDependencies": {
 | 
			
		||||
 
 | 
			
		||||
@@ -2,9 +2,9 @@
 | 
			
		||||
 | 
			
		||||
const utils = require('../../services/utils');
 | 
			
		||||
const dateUtils = require('../../services/date_utils');
 | 
			
		||||
const becca = require('../becca');
 | 
			
		||||
const AbstractBeccaEntity = require("./abstract_becca_entity");
 | 
			
		||||
const sql = require("../../services/sql");
 | 
			
		||||
const protectedSessionService = require("../../services/protected_session.js");
 | 
			
		||||
 | 
			
		||||
const attachmentRoleToNoteTypeMapping = {
 | 
			
		||||
    'image': 'image'
 | 
			
		||||
@@ -72,7 +72,7 @@ class BAttachment extends AbstractBeccaEntity {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    getNote() {
 | 
			
		||||
        return becca.notes[this.parentId];
 | 
			
		||||
        return this.becca.notes[this.parentId];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /** @returns {boolean} true if the note has string content (not binary) */
 | 
			
		||||
@@ -80,6 +80,12 @@ class BAttachment extends AbstractBeccaEntity {
 | 
			
		||||
        return utils.isStringNote(this.type, this.mime);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    isContentAvailable() {
 | 
			
		||||
        return !this.attachmentId // new attachment which was not encrypted yet
 | 
			
		||||
            || !this.isProtected
 | 
			
		||||
            || protectedSessionService.isProtectedSessionAvailable()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /** @returns {*} */
 | 
			
		||||
    getContent() {
 | 
			
		||||
        return this._getContent();
 | 
			
		||||
@@ -129,15 +135,17 @@ class BAttachment extends AbstractBeccaEntity {
 | 
			
		||||
 | 
			
		||||
        this.markAsDeleted();
 | 
			
		||||
 | 
			
		||||
        if (this.role === 'image' && this.type === 'text') {
 | 
			
		||||
            const origContent = this.getContent();
 | 
			
		||||
            const oldAttachmentUrl = `api/attachment/${this.attachmentId}/image/`;
 | 
			
		||||
        const parentNote = this.getNote();
 | 
			
		||||
 | 
			
		||||
        if (this.role === 'image' && parentNote.type === 'text') {
 | 
			
		||||
            const origContent = parentNote.getContent();
 | 
			
		||||
            const oldAttachmentUrl = `api/attachments/${this.attachmentId}/image/`;
 | 
			
		||||
            const newNoteUrl = `api/images/${note.noteId}/`;
 | 
			
		||||
 | 
			
		||||
            const fixedContent = utils.replaceAll(origContent, oldAttachmentUrl, newNoteUrl);
 | 
			
		||||
 | 
			
		||||
            if (origContent !== fixedContent) {
 | 
			
		||||
                this.setContent(fixedContent);
 | 
			
		||||
            if (fixedContent !== origContent) {
 | 
			
		||||
                parentNote.setContent(fixedContent);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1436,6 +1436,28 @@ class BNote extends AbstractBeccaEntity {
 | 
			
		||||
 | 
			
		||||
        return cloningService.cloneNoteToBranch(this.noteId, branch.branchId);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    isEligibleForConversionToAttachment() {
 | 
			
		||||
        if (this.type !== 'image' || !this.isContentAvailable() || this.hasChildren() || this.getParentBranches().length !== 1) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const targetRelations = this.getTargetRelations().filter(relation => relation.name === 'imageLink');
 | 
			
		||||
 | 
			
		||||
        if (targetRelations.length !== 1) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const parentNote = this.getParentNotes()[0]; // at this point note can have only one parent
 | 
			
		||||
        const referencingNote = targetRelations[0].getNote();
 | 
			
		||||
 | 
			
		||||
        if (parentNote !== referencingNote || parentNote.type !== 'text' || !parentNote.isContentAvailable()) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Some notes are eligible for conversion into an attachment of its parent, note must have these properties:
 | 
			
		||||
     * - it has exactly one target relation
 | 
			
		||||
@@ -1456,25 +1478,13 @@ class BNote extends AbstractBeccaEntity {
 | 
			
		||||
     * @returns {BAttachment|null} - null if note is not eligible for conversion
 | 
			
		||||
     */
 | 
			
		||||
    convertToParentAttachment(opts = {force: false}) {
 | 
			
		||||
        if (this.type !== 'image' || !this.isContentAvailable() || this.hasChildren() || this.getParentBranches().length !== 1) {
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const targetRelations = this.getTargetRelations().filter(relation => relation.name === 'imageLink');
 | 
			
		||||
 | 
			
		||||
        if (targetRelations.length !== 1) {
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const parentNote = this.getParentNotes()[0]; // at this point note can have only one parent
 | 
			
		||||
        const referencingNote = targetRelations[0].note;
 | 
			
		||||
 | 
			
		||||
        if (parentNote !== referencingNote || parentNote.type !== 'text' || !parentNote.isContentAvailable()) {
 | 
			
		||||
        if (!this.isEligibleForConversionToAttachment()) {
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const content = this.getContent();
 | 
			
		||||
 | 
			
		||||
        const parentNote = this.getParentNotes()[0];
 | 
			
		||||
        const attachment = parentNote.saveAttachment({
 | 
			
		||||
            role: 'image',
 | 
			
		||||
            mime: this.mime,
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,6 @@
 | 
			
		||||
import server from '../services/server.js';
 | 
			
		||||
import noteAttributeCache from "../services/note_attribute_cache.js";
 | 
			
		||||
import ws from "../services/ws.js";
 | 
			
		||||
import options from "../services/options.js";
 | 
			
		||||
import froca from "../services/froca.js";
 | 
			
		||||
import protectedSessionHolder from "../services/protected_session_holder.js";
 | 
			
		||||
import cssClassManager from "../services/css_class_manager.js";
 | 
			
		||||
@@ -246,6 +245,27 @@ class FNote {
 | 
			
		||||
        return attachments.find(att => att.attachmentId === attachmentId);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    isEligibleForConversionToAttachment() {
 | 
			
		||||
        if (this.type !== 'image' || !this.isContentAvailable() || this.hasChildren() || this.getParentBranches().length !== 1) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const targetRelations = this.getTargetRelations().filter(relation => relation.name === 'imageLink');
 | 
			
		||||
 | 
			
		||||
        if (targetRelations.length !== 1) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const parentNote = this.getParentNotes()[0]; // at this point note can have only one parent
 | 
			
		||||
        const referencingNote = targetRelations[0].getNote();
 | 
			
		||||
 | 
			
		||||
        if (parentNote !== referencingNote || parentNote.type !== 'text' || !parentNote.isContentAvailable()) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @param {string} [type] - (optional) attribute type to filter
 | 
			
		||||
     * @param {string} [name] - (optional) attribute name to filter
 | 
			
		||||
 
 | 
			
		||||
@@ -30,7 +30,6 @@ const TPL = `
 | 
			
		||||
    <div class="dropdown-menu dropdown-menu-right">
 | 
			
		||||
        <a data-trigger-command="deleteAttachment" class="dropdown-item">Delete attachment</a>
 | 
			
		||||
        <a data-trigger-command="convertAttachmentIntoNote" class="dropdown-item">Convert attachment into note</a>
 | 
			
		||||
        <a data-trigger-command="convertAttachmentIntoNote" class="dropdown-item pull-attachment-into-note-button">Copy into clipboard</a>
 | 
			
		||||
    </div>
 | 
			
		||||
</div>`;
 | 
			
		||||
 | 
			
		||||
@@ -47,22 +46,22 @@ export default class AttachmentActionsWidget extends BasicWidget {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async deleteAttachmentCommand() {
 | 
			
		||||
        if (await dialogService.confirm(`Are you sure you want to delete attachment '${this.attachment.title}'?`)) {
 | 
			
		||||
            await server.remove(`attachments/${this.attachment.attachmentId}`);
 | 
			
		||||
 | 
			
		||||
            toastService.showMessage(`Attachment '${this.attachment.title}' has been deleted.`);
 | 
			
		||||
        if (!await dialogService.confirm(`Are you sure you want to delete attachment '${this.attachment.title}'?`)) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        await server.remove(`attachments/${this.attachment.attachmentId}`);
 | 
			
		||||
        toastService.showMessage(`Attachment '${this.attachment.title}' has been deleted.`);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async convertAttachmentIntoNoteCommand() {
 | 
			
		||||
        if (await dialogService.confirm(`Are you sure you want to convert attachment '${this.attachment.title}' into a separate note?`)) {
 | 
			
		||||
            const {note: newNote} = await server.post(`attachments/${this.attachment.attachmentId}/convert-to-note`)
 | 
			
		||||
 | 
			
		||||
            toastService.showMessage(`Attachment '${this.attachment.title}' has been converted to note.`);
 | 
			
		||||
 | 
			
		||||
            await ws.waitForMaxKnownEntityChangeId();
 | 
			
		||||
 | 
			
		||||
            await appContext.tabManager.getActiveContext().setNote(newNote.noteId);
 | 
			
		||||
        if (!await dialogService.confirm(`Are you sure you want to convert attachment '${this.attachment.title}' into a separate note?`)) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const {note: newNote} = await server.post(`attachments/${this.attachment.attachmentId}/convert-to-note`)
 | 
			
		||||
        toastService.showMessage(`Attachment '${this.attachment.title}' has been converted to note.`);
 | 
			
		||||
        await ws.waitForMaxKnownEntityChangeId();
 | 
			
		||||
        await appContext.tabManager.getActiveContext().setNote(newNote.noteId);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,11 @@
 | 
			
		||||
import NoteContextAwareWidget from "../note_context_aware_widget.js";
 | 
			
		||||
import utils from "../../services/utils.js";
 | 
			
		||||
import branchService from "../../services/branches.js";
 | 
			
		||||
import dialogService from "../../services/dialog.js";
 | 
			
		||||
import server from "../../services/server.js";
 | 
			
		||||
import toastService from "../../services/toast.js";
 | 
			
		||||
import ws from "../../services/ws.js";
 | 
			
		||||
import appContext from "../../components/app_context.js";
 | 
			
		||||
 | 
			
		||||
const TPL = `
 | 
			
		||||
<div class="dropdown note-actions">
 | 
			
		||||
@@ -25,6 +30,7 @@ const TPL = `
 | 
			
		||||
        aria-expanded="false" class="icon-action bx bx-dots-vertical-rounded"></button>
 | 
			
		||||
 | 
			
		||||
    <div class="dropdown-menu dropdown-menu-right">
 | 
			
		||||
        <a data-trigger-command="convertNoteIntoAttachment" class="dropdown-item">Convert into attachment</a>
 | 
			
		||||
        <a data-trigger-command="renderActiveNote" class="dropdown-item render-note-button"><kbd data-command="renderActiveNote"></kbd> Re-render note</a>
 | 
			
		||||
        <a data-trigger-command="findInText" class="dropdown-item find-in-text-button">Search in note <kbd data-command="findInText"></a>
 | 
			
		||||
        <a data-trigger-command="showNoteSource" class="dropdown-item show-source-button"><kbd data-command="showNoteSource"></kbd> Note source</a>
 | 
			
		||||
@@ -45,6 +51,7 @@ export default class NoteActionsWidget extends NoteContextAwareWidget {
 | 
			
		||||
    doRender() {
 | 
			
		||||
        this.$widget = $(TPL);
 | 
			
		||||
 | 
			
		||||
        this.$convertNoteIntoAttachmentButton = this.$widget.find("[data-trigger-command='convertNoteIntoAttachment']");
 | 
			
		||||
        this.$findInTextButton = this.$widget.find('.find-in-text-button');
 | 
			
		||||
        this.$printActiveNoteButton = this.$widget.find('.print-active-note-button');
 | 
			
		||||
        this.$showSourceButton = this.$widget.find('.show-source-button');
 | 
			
		||||
@@ -80,6 +87,8 @@ export default class NoteActionsWidget extends NoteContextAwareWidget {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    refreshWithNote(note) {
 | 
			
		||||
        this.$convertNoteIntoAttachmentButton.toggle(note.isEligibleForConversionToAttachment());
 | 
			
		||||
 | 
			
		||||
        this.toggleDisabled(this.$findInTextButton, ['text', 'code', 'book', 'search'].includes(note.type));
 | 
			
		||||
 | 
			
		||||
        this.toggleDisabled(this.$showSourceButton, ['text', 'relationMap', 'mermaid'].includes(note.type));
 | 
			
		||||
@@ -91,6 +100,28 @@ export default class NoteActionsWidget extends NoteContextAwareWidget {
 | 
			
		||||
        this.$openNoteExternallyButton.toggle(utils.isElectron());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async convertNoteIntoAttachmentCommand() {
 | 
			
		||||
        if (!await dialogService.confirm(`Are you sure you want to convert note '${this.note.title}' into an attachment of the parent note?`)) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const {attachment: newAttachment} = await server.post(`notes/${this.noteId}/convert-to-attachment`);
 | 
			
		||||
 | 
			
		||||
        if (!newAttachment) {
 | 
			
		||||
            toastService.showMessage(`Converting note '${this.note.title}' failed.`);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        toastService.showMessage(`Note '${newAttachment.title}' has been converted to attachment.`);
 | 
			
		||||
        await ws.waitForMaxKnownEntityChangeId();
 | 
			
		||||
        await appContext.tabManager.getActiveContext().setNote(newAttachment.parentId, {
 | 
			
		||||
            viewScope: {
 | 
			
		||||
                viewMode: 'attachments',
 | 
			
		||||
                attachmentId: newAttachment.attachmentId
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    toggleDisabled($el, enable) {
 | 
			
		||||
        if (enable) {
 | 
			
		||||
            $el.removeAttr('disabled');
 | 
			
		||||
 
 | 
			
		||||
@@ -267,6 +267,19 @@ function forceSaveNoteRevision(req) {
 | 
			
		||||
    note.saveNoteRevision();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function convertNoteToAttachment(req) {
 | 
			
		||||
    const {noteId} = req.params;
 | 
			
		||||
    const note = becca.getNote(noteId);
 | 
			
		||||
 | 
			
		||||
    if (!note) {
 | 
			
		||||
        throw new NotFoundError(`Note '${noteId}' not found.`);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return {
 | 
			
		||||
        attachment: note.convertToParentAttachment({ force: true })
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
module.exports = {
 | 
			
		||||
    getNote,
 | 
			
		||||
    updateNoteData,
 | 
			
		||||
@@ -282,5 +295,6 @@ module.exports = {
 | 
			
		||||
    eraseUnusedAttachmentsNow,
 | 
			
		||||
    getDeleteNotesPreview,
 | 
			
		||||
    uploadModifiedFile,
 | 
			
		||||
    forceSaveNoteRevision
 | 
			
		||||
    forceSaveNoteRevision,
 | 
			
		||||
    convertNoteToAttachment
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -138,6 +138,7 @@ function register(app) {
 | 
			
		||||
    // this "hacky" path is used for easier referencing of CSS resources
 | 
			
		||||
    route(GET, '/api/notes/download/:noteId', [auth.checkApiAuthOrElectron], filesRoute.downloadFile);
 | 
			
		||||
    apiRoute(PST, '/api/notes/:noteId/save-to-tmp-dir', filesRoute.saveToTmpDir);
 | 
			
		||||
    apiRoute(PST, '/api/notes/:noteId/convert-to-attachment', notesApiRoute.convertNoteToAttachment);
 | 
			
		||||
 | 
			
		||||
    apiRoute(PUT, '/api/branches/:branchId/move-to/:parentBranchId', branchesApiRoute.moveBranchToParent);
 | 
			
		||||
    apiRoute(PUT, '/api/branches/:branchId/move-before/:beforeBranchId', branchesApiRoute.moveBranchBeforeNote);
 | 
			
		||||
 
 | 
			
		||||
@@ -370,6 +370,8 @@ function checkImageAttachments(note, content) {
 | 
			
		||||
        newAttachment.setContent(unknownAttachment.getContent(), { forceSave: true });
 | 
			
		||||
 | 
			
		||||
        content = content.replace(`api/attachments/${unknownAttachment.attachmentId}/image`, `api/attachments/${newAttachment.attachmentId}/image`);
 | 
			
		||||
 | 
			
		||||
        log.info(`Copied attachment '${unknownAttachment.attachmentId}' to new '${newAttachment.attachmentId}'`);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return content;
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user