mirror of
				https://github.com/zadam/trilium.git
				synced 2025-11-03 20:06:08 +01:00 
			
		
		
		
	import WIP
This commit is contained in:
		@@ -75,6 +75,7 @@ import CodeButtonsWidget from "../widgets/floating_buttons/code_buttons.js";
 | 
				
			|||||||
import ApiLogWidget from "../widgets/api_log.js";
 | 
					import ApiLogWidget from "../widgets/api_log.js";
 | 
				
			||||||
import HideFloatingButtonsButton from "../widgets/floating_buttons/hide_floating_buttons_button.js";
 | 
					import HideFloatingButtonsButton from "../widgets/floating_buttons/hide_floating_buttons_button.js";
 | 
				
			||||||
import ScriptExecutorWidget from "../widgets/ribbon_widgets/script_executor.js";
 | 
					import ScriptExecutorWidget from "../widgets/ribbon_widgets/script_executor.js";
 | 
				
			||||||
 | 
					import UploadAttachmentsDialog from "../widgets/dialogs/upload_attachments.js";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default class DesktopLayout {
 | 
					export default class DesktopLayout {
 | 
				
			||||||
    constructor(customWidgets) {
 | 
					    constructor(customWidgets) {
 | 
				
			||||||
@@ -200,6 +201,7 @@ export default class DesktopLayout {
 | 
				
			|||||||
            .child(new MoveToDialog())
 | 
					            .child(new MoveToDialog())
 | 
				
			||||||
            .child(new ImportDialog())
 | 
					            .child(new ImportDialog())
 | 
				
			||||||
            .child(new ExportDialog())
 | 
					            .child(new ExportDialog())
 | 
				
			||||||
 | 
					            .child(new UploadAttachmentsDialog())
 | 
				
			||||||
            .child(new MarkdownImportDialog())
 | 
					            .child(new MarkdownImportDialog())
 | 
				
			||||||
            .child(new ProtectedSessionPasswordDialog())
 | 
					            .child(new ProtectedSessionPasswordDialog())
 | 
				
			||||||
            .child(new NoteRevisionsDialog())
 | 
					            .child(new NoteRevisionsDialog())
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -182,7 +182,7 @@ function makeToast(id, message) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
ws.subscribeToMessages(async message => {
 | 
					ws.subscribeToMessages(async message => {
 | 
				
			||||||
    if (message.taskType !== 'delete-notes') {
 | 
					    if (message.taskType !== 'deleteNotes') {
 | 
				
			||||||
        return;
 | 
					        return;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -54,7 +54,7 @@ function makeToast(id, message) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
ws.subscribeToMessages(async message => {
 | 
					ws.subscribeToMessages(async message => {
 | 
				
			||||||
    if (message.taskType !== 'import') {
 | 
					    if (message.taskType !== 'importNotes') {
 | 
				
			||||||
        return;
 | 
					        return;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -75,6 +75,32 @@ ws.subscribeToMessages(async message => {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ws.subscribeToMessages(async message => {
 | 
				
			||||||
 | 
					    if (message.taskType !== 'importAttachments') {
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (message.type === 'taskError') {
 | 
				
			||||||
 | 
					        toastService.closePersistent(message.taskId);
 | 
				
			||||||
 | 
					        toastService.showError(message.message);
 | 
				
			||||||
 | 
					    } else if (message.type === 'taskProgressCount') {
 | 
				
			||||||
 | 
					        toastService.showPersistent(makeToast(message.taskId, `Import in progress: ${message.progressCount}`));
 | 
				
			||||||
 | 
					    } else if (message.type === 'taskSucceeded') {
 | 
				
			||||||
 | 
					        const toast = makeToast(message.taskId, "Import finished successfully.");
 | 
				
			||||||
 | 
					        toast.closeAfter = 5000;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        toastService.showPersistent(toast);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (message.result.parentNoteId) {
 | 
				
			||||||
 | 
					            await appContext.tabManager.getActiveContext().setNote(message.result.importedNoteId, {
 | 
				
			||||||
 | 
					                viewScope: {
 | 
				
			||||||
 | 
					                    viewMode: 'attachments'
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default {
 | 
					export default {
 | 
				
			||||||
    uploadFiles
 | 
					    uploadFiles
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -21,7 +21,7 @@ const TPL = `
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                        <input type="file" class="import-file-upload-input form-control-file" multiple />
 | 
					                        <input type="file" class="import-file-upload-input form-control-file" multiple />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                        <p>Content of the file will be imported as child note(s) into <strong class="import-note-title"></strong>.
 | 
					                        <p>Content of the selected file(s) will be imported as child note(s) into <strong class="import-note-title"></strong>.
 | 
				
			||||||
                    </div>
 | 
					                    </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    <div class="form-group">
 | 
					                    <div class="form-group">
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -9,7 +9,7 @@ const TPL = `
 | 
				
			|||||||
    <div class="modal-dialog modal-lg" role="document">
 | 
					    <div class="modal-dialog modal-lg" role="document">
 | 
				
			||||||
        <div class="modal-content">
 | 
					        <div class="modal-content">
 | 
				
			||||||
            <div class="modal-header">
 | 
					            <div class="modal-header">
 | 
				
			||||||
                <h5 class="modal-title">Import into note</h5>
 | 
					                <h5 class="modal-title">Upload attachments to note</h5>
 | 
				
			||||||
                <button type="button" class="close" data-dismiss="modal" aria-label="Close">
 | 
					                <button type="button" class="close" data-dismiss="modal" aria-label="Close">
 | 
				
			||||||
                    <span aria-hidden="true">×</span>
 | 
					                    <span aria-hidden="true">×</span>
 | 
				
			||||||
                </button>
 | 
					                </button>
 | 
				
			||||||
@@ -21,7 +21,7 @@ const TPL = `
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                        <input type="file" class="upload-attachment-file-upload-input form-control-file" multiple />
 | 
					                        <input type="file" class="upload-attachment-file-upload-input form-control-file" multiple />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                        <p>Content of the file will be imported as child note(s) into <strong class="upload-attachment-note-title"></strong>.
 | 
					                        <p>Files will be uploaded as attachments into <strong class="upload-attachment-note-title"></strong>.
 | 
				
			||||||
                    </div>
 | 
					                    </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    <div class="form-group">
 | 
					                    <div class="form-group">
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -67,6 +67,8 @@ export default class AttachmentDetailTypeWidget extends TypeWidget {
 | 
				
			|||||||
    async entitiesReloadedEvent({loadResults}) {
 | 
					    async entitiesReloadedEvent({loadResults}) {
 | 
				
			||||||
        const attachmentChange = loadResults.getAttachments().find(att => att.attachmentId === this.attachmentId);
 | 
					        const attachmentChange = loadResults.getAttachments().find(att => att.attachmentId === this.attachmentId);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        console.log(attachmentChange);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (attachmentChange?.isDeleted) {
 | 
					        if (attachmentChange?.isDeleted) {
 | 
				
			||||||
            this.refresh(); // all other updates are handled within AttachmentDetailWidget
 | 
					            this.refresh(); // all other updates are handled within AttachmentDetailWidget
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -13,6 +13,8 @@ const TPL = `
 | 
				
			|||||||
        .attachment-list .links-wrapper {
 | 
					        .attachment-list .links-wrapper {
 | 
				
			||||||
            font-size: larger;
 | 
					            font-size: larger;
 | 
				
			||||||
            margin-bottom: 15px;
 | 
					            margin-bottom: 15px;
 | 
				
			||||||
 | 
					            display: flex;
 | 
				
			||||||
 | 
					            justify-content: space-between;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    </style>
 | 
					    </style>
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
@@ -36,9 +38,11 @@ export default class AttachmentListTypeWidget extends TypeWidget {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    async doRefresh(note) {
 | 
					    async doRefresh(note) {
 | 
				
			||||||
        this.$linksWrapper.append(
 | 
					        this.$linksWrapper.append(
 | 
				
			||||||
 | 
					            $('<div>').append(
 | 
				
			||||||
                "Owning note: ",
 | 
					                "Owning note: ",
 | 
				
			||||||
                await linkService.createNoteLink(this.noteId),
 | 
					                await linkService.createNoteLink(this.noteId),
 | 
				
			||||||
            $('<button class="btn btn-sm">')
 | 
					            ),
 | 
				
			||||||
 | 
					            $('<button class="btn btn-xs">')
 | 
				
			||||||
                .text("Upload attachments")
 | 
					                .text("Upload attachments")
 | 
				
			||||||
                .on('click', () => this.triggerCommand("showUploadAttachmentsDialog", {noteId: this.noteId}))
 | 
					                .on('click', () => this.triggerCommand("showUploadAttachmentsDialog", {noteId: this.noteId}))
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -185,7 +185,7 @@ function deleteBranch(req) {
 | 
				
			|||||||
    const eraseNotes = req.query.eraseNotes === 'true';
 | 
					    const eraseNotes = req.query.eraseNotes === 'true';
 | 
				
			||||||
    const branch = becca.getBranchOrThrow(req.params.branchId);
 | 
					    const branch = becca.getBranchOrThrow(req.params.branchId);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const taskContext = TaskContext.getInstance(req.query.taskId, 'delete-notes');
 | 
					    const taskContext = TaskContext.getInstance(req.query.taskId, 'deleteNotes');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const deleteId = utils.randomString(10);
 | 
					    const deleteId = utils.randomString(10);
 | 
				
			||||||
    let noteDeleted;
 | 
					    let noteDeleted;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -44,7 +44,7 @@ async function importNotesToBranch(req) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    let note; // typically root of the import - client can show it after finishing the import
 | 
					    let note; // typically root of the import - client can show it after finishing the import
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const taskContext = TaskContext.getInstance(taskId, 'import', options);
 | 
					    const taskContext = TaskContext.getInstance(taskId, 'importNotes', options);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    try {
 | 
					    try {
 | 
				
			||||||
        if (extension === '.zip' && options.explodeArchives) {
 | 
					        if (extension === '.zip' && options.explodeArchives) {
 | 
				
			||||||
@@ -95,22 +95,12 @@ async function importAttachmentsToNote(req) {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const parentNote = becca.getNoteOrThrow(parentNoteId);
 | 
					    const parentNote = becca.getNoteOrThrow(parentNoteId);
 | 
				
			||||||
 | 
					    const taskContext = TaskContext.getInstance(taskId, 'importAttachment', options);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // running all the event handlers on imported notes (and attributes) is slow
 | 
					    // unlike in note import we let the events run, because a huge number of attachments is not likely
 | 
				
			||||||
    // and may produce unintended consequences
 | 
					 | 
				
			||||||
    cls.disableEntityEvents();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // eliminate flickering during import
 | 
					 | 
				
			||||||
    cls.ignoreEntityChangeIds();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    let note; // typically root of the import - client can show it after finishing the import
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    const taskContext = TaskContext.getInstance(taskId, 'import', options);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    try {
 | 
					    try {
 | 
				
			||||||
        // FIXME
 | 
					        await singleImportService.importAttachment(taskContext, file, parentNote);
 | 
				
			||||||
 | 
					 | 
				
			||||||
        note = await singleImportService.importSingleFile(taskContext, file, parentNote);
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    catch (e) {
 | 
					    catch (e) {
 | 
				
			||||||
        const message = `Import failed with following error: '${e.message}'. More details might be in the logs.`;
 | 
					        const message = `Import failed with following error: '${e.message}'. More details might be in the logs.`;
 | 
				
			||||||
@@ -124,15 +114,9 @@ async function importAttachmentsToNote(req) {
 | 
				
			|||||||
    if (last === "true") {
 | 
					    if (last === "true") {
 | 
				
			||||||
        // small timeout to avoid race condition (the message is received before the transaction is committed)
 | 
					        // small timeout to avoid race condition (the message is received before the transaction is committed)
 | 
				
			||||||
        setTimeout(() => taskContext.taskSucceeded({
 | 
					        setTimeout(() => taskContext.taskSucceeded({
 | 
				
			||||||
            parentNoteId: parentNoteId,
 | 
					            parentNoteId: parentNoteId
 | 
				
			||||||
            importedNoteId: note.noteId
 | 
					 | 
				
			||||||
        }), 1000);
 | 
					        }), 1000);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					 | 
				
			||||||
    // import has deactivated note events so becca is not updated, instead we force it to reload
 | 
					 | 
				
			||||||
    beccaLoader.load();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    return note.getPojo();
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
module.exports = {
 | 
					module.exports = {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -62,7 +62,7 @@ function deleteNote(req) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    const note = becca.getNote(noteId);
 | 
					    const note = becca.getNote(noteId);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const taskContext = TaskContext.getInstance(taskId, 'delete-notes');
 | 
					    const taskContext = TaskContext.getInstance(taskId, 'deleteNotes');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    note.deleteNote(deleteId, taskContext);
 | 
					    note.deleteNote(deleteId, taskContext);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -10,7 +10,7 @@ const cls = require('../services/cls');
 | 
				
			|||||||
const sql = require('../services/sql');
 | 
					const sql = require('../services/sql');
 | 
				
			||||||
const entityChangesService = require('../services/entity_changes');
 | 
					const entityChangesService = require('../services/entity_changes');
 | 
				
			||||||
const csurf = require('csurf');
 | 
					const csurf = require('csurf');
 | 
				
			||||||
const {createPartialContentHandler} = require("express-partial-content");
 | 
					const { createPartialContentHandler } = require("express-partial-content");
 | 
				
			||||||
const rateLimit = require("express-rate-limit");
 | 
					const rateLimit = require("express-rate-limit");
 | 
				
			||||||
const AbstractBeccaEntity = require("../becca/entities/abstract_becca_entity");
 | 
					const AbstractBeccaEntity = require("../becca/entities/abstract_becca_entity");
 | 
				
			||||||
const NotFoundError = require("../errors/not_found_error");
 | 
					const NotFoundError = require("../errors/not_found_error");
 | 
				
			||||||
@@ -71,7 +71,7 @@ const etapiSpecRoute = require('../etapi/spec');
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
const csrfMiddleware = csurf({
 | 
					const csrfMiddleware = csurf({
 | 
				
			||||||
    cookie: true,
 | 
					    cookie: true,
 | 
				
			||||||
    path: '' // nothing so cookie is valid only for current path
 | 
					    path: '' // empty, so cookie is valid only for the current path
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const MAX_ALLOWED_FILE_SIZE_MB = 250;
 | 
					const MAX_ALLOWED_FILE_SIZE_MB = 250;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -178,6 +178,34 @@ function importHtml(taskContext, file, parentNote) {
 | 
				
			|||||||
    return note;
 | 
					    return note;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * @param {TaskContext} taskContext
 | 
				
			||||||
 | 
					 * @param file
 | 
				
			||||||
 | 
					 * @param {BNote} parentNote
 | 
				
			||||||
 | 
					 * @returns {BNote}
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					function importAttachment(taskContext, file, parentNote) {
 | 
				
			||||||
 | 
					    const mime = mimeService.getMime(file.originalname) || file.mimetype;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    console.log("mime", mime);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (mime.startsWith("image/")) {
 | 
				
			||||||
 | 
					        imageService.saveImageToAttachment(parentNote.noteId, file.buffer, file.originalname, taskContext.data.shrinkImages);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        taskContext.increaseProgressCount();
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        parentNote.saveAttachment({
 | 
				
			||||||
 | 
					            title: file.originalname,
 | 
				
			||||||
 | 
					            content: file.buffer,
 | 
				
			||||||
 | 
					            role: 'file',
 | 
				
			||||||
 | 
					            mime: mime
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        taskContext.increaseProgressCount();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
module.exports = {
 | 
					module.exports = {
 | 
				
			||||||
    importSingleFile
 | 
					    importSingleFile,
 | 
				
			||||||
 | 
					    importAttachment
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user