mirror of
				https://github.com/TriliumNext/Notes.git
				synced 2025-11-04 07:01:31 +08:00 
			
		
		
		
	#125, basic infrastructure for scripts attached to notes via relations
This commit is contained in:
		
							parent
							
								
									ededc063df
								
							
						
					
					
						commit
						170d317589
					
				@ -1,8 +1,14 @@
 | 
			
		||||
import ScriptContext from "./script_context.js";
 | 
			
		||||
import server from "./server.js";
 | 
			
		||||
 | 
			
		||||
async function executeBundle(bundle) {
 | 
			
		||||
    const apiContext = ScriptContext(bundle.note, bundle.allNotes);
 | 
			
		||||
async function getAndExecuteBundle(noteId, targetNote = null) {
 | 
			
		||||
    const bundle = await server.get('script/bundle/' + noteId);
 | 
			
		||||
 | 
			
		||||
    await executeBundle(bundle, targetNote);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function executeBundle(bundle, targetNote) {
 | 
			
		||||
    const apiContext = ScriptContext(bundle.note, bundle.allNotes, targetNote);
 | 
			
		||||
 | 
			
		||||
    return await (function () {
 | 
			
		||||
        return eval(`const apiContext = this; (async function() { ${bundle.script}\r\n})()`);
 | 
			
		||||
@ -19,5 +25,6 @@ async function executeStartupBundles() {
 | 
			
		||||
 | 
			
		||||
export default {
 | 
			
		||||
    executeBundle,
 | 
			
		||||
    getAndExecuteBundle,
 | 
			
		||||
    executeStartupBundles
 | 
			
		||||
}
 | 
			
		||||
@ -15,6 +15,7 @@ import noteDetailText from './note_detail_text.js';
 | 
			
		||||
import noteDetailFile from './note_detail_file.js';
 | 
			
		||||
import noteDetailSearch from './note_detail_search.js';
 | 
			
		||||
import noteDetailRender from './note_detail_render.js';
 | 
			
		||||
import bundleService from "./bundle.js";
 | 
			
		||||
 | 
			
		||||
const $noteTitle = $("#note-title");
 | 
			
		||||
 | 
			
		||||
@ -30,6 +31,7 @@ const $labelListInner = $("#label-list-inner");
 | 
			
		||||
const $relationList = $("#relation-list");
 | 
			
		||||
const $relationListInner = $("#relation-list-inner");
 | 
			
		||||
const $childrenOverview = $("#children-overview");
 | 
			
		||||
const $scriptArea = $("#note-detail-script-area");
 | 
			
		||||
 | 
			
		||||
let currentNote = null;
 | 
			
		||||
 | 
			
		||||
@ -144,6 +146,8 @@ async function handleProtectedSession() {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function loadNoteDetail(noteId) {
 | 
			
		||||
    $scriptArea.html('');
 | 
			
		||||
 | 
			
		||||
    currentNote = await loadNote(noteId);
 | 
			
		||||
 | 
			
		||||
    if (isNewNoteCreated) {
 | 
			
		||||
@ -183,10 +187,15 @@ async function loadNoteDetail(noteId) {
 | 
			
		||||
 | 
			
		||||
    const labels = await loadLabelList();
 | 
			
		||||
 | 
			
		||||
    loadRelationList(); // no need to wait
 | 
			
		||||
 | 
			
		||||
    const hideChildrenOverview = labels.some(label => label.name === 'hideChildrenOverview');
 | 
			
		||||
    await showChildrenOverview(hideChildrenOverview);
 | 
			
		||||
 | 
			
		||||
    const relations = await loadRelationList();
 | 
			
		||||
    const relationsToRun = relations.filter(relation => relation.name === 'runOnNoteView');
 | 
			
		||||
 | 
			
		||||
    for (const relationToRun of relationsToRun) {
 | 
			
		||||
        await bundleService.getAndExecuteBundle(relationToRun.targetNoteId, getCurrentNote());
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function showChildrenOverview(hideChildrenOverview) {
 | 
			
		||||
 | 
			
		||||
@ -2,8 +2,9 @@ import treeService from './tree.js';
 | 
			
		||||
import server from './server.js';
 | 
			
		||||
import utils from './utils.js';
 | 
			
		||||
import infoService from './info.js';
 | 
			
		||||
import linkService from './link.js';
 | 
			
		||||
 | 
			
		||||
function ScriptApi(startNote, currentNote) {
 | 
			
		||||
function ScriptApi(startNote, currentNote, targetNote = null) {
 | 
			
		||||
    const $pluginButtons = $("#plugin-buttons");
 | 
			
		||||
 | 
			
		||||
    async function activateNote(notePath) {
 | 
			
		||||
@ -42,7 +43,8 @@ function ScriptApi(startNote, currentNote) {
 | 
			
		||||
            script: script,
 | 
			
		||||
            params: prepareParams(params),
 | 
			
		||||
            startNoteId: startNote.noteId,
 | 
			
		||||
            currentNoteId: currentNote.noteId
 | 
			
		||||
            currentNoteId: currentNote.noteId,
 | 
			
		||||
            targetNoteId: targetNote ? targetNote.noteId : null
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        return ret.executionResult;
 | 
			
		||||
@ -51,6 +53,7 @@ function ScriptApi(startNote, currentNote) {
 | 
			
		||||
    return {
 | 
			
		||||
        startNote: startNote,
 | 
			
		||||
        currentNote: currentNote,
 | 
			
		||||
        targetNote: targetNote,
 | 
			
		||||
        addButtonToToolbar,
 | 
			
		||||
        activateNote,
 | 
			
		||||
        getInstanceName: () => window.glob.instanceName,
 | 
			
		||||
@ -59,7 +62,8 @@ function ScriptApi(startNote, currentNote) {
 | 
			
		||||
        parseDate: utils.parseDate,
 | 
			
		||||
        showMessage: infoService.showMessage,
 | 
			
		||||
        showError: infoService.showError,
 | 
			
		||||
        reloadTree: treeService.reload
 | 
			
		||||
        reloadTree: treeService.reload,
 | 
			
		||||
        createNoteLink: linkService.createNoteLink
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -1,13 +1,13 @@
 | 
			
		||||
import ScriptApi from './script_api.js';
 | 
			
		||||
import utils from './utils.js';
 | 
			
		||||
 | 
			
		||||
function ScriptContext(startNote, allNotes) {
 | 
			
		||||
function ScriptContext(startNote, allNotes, targetNote = null) {
 | 
			
		||||
    const modules = {};
 | 
			
		||||
 | 
			
		||||
    return {
 | 
			
		||||
        modules: modules,
 | 
			
		||||
        notes: utils.toObject(allNotes, note => [note.noteId, note]),
 | 
			
		||||
        apis: utils.toObject(allNotes, note => [note.noteId, ScriptApi(startNote, note)]),
 | 
			
		||||
        apis: utils.toObject(allNotes, note => [note.noteId, ScriptApi(startNote, note, targetNote)]),
 | 
			
		||||
        require: moduleNoteIds => {
 | 
			
		||||
            return moduleName => {
 | 
			
		||||
                const candidates = allNotes.filter(note => moduleNoteIds.includes(note.noteId));
 | 
			
		||||
 | 
			
		||||
@ -5,7 +5,8 @@ const scriptService = require('../../services/script');
 | 
			
		||||
const repository = require('../../services/repository');
 | 
			
		||||
 | 
			
		||||
async function exec(req) {
 | 
			
		||||
    const result = await scriptService.executeScript(req.body.script, req.body.params, req.body.startNoteId, req.body.currentNoteId);
 | 
			
		||||
    const result = await scriptService.executeScript(req.body.script, req.body.params, req.body.startNoteId,
 | 
			
		||||
        req.body.currentNoteId, req.body.targetNoteId);
 | 
			
		||||
 | 
			
		||||
    return { executionResult: result };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -14,7 +14,7 @@ async function executeNote(note) {
 | 
			
		||||
    await executeBundle(bundle);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function executeBundle(bundle, startNote) {
 | 
			
		||||
async function executeBundle(bundle, startNote, targetNote = null) {
 | 
			
		||||
    if (!startNote) {
 | 
			
		||||
        // this is the default case, the only exception is when we want to preserve frontend startNote
 | 
			
		||||
        startNote = bundle.note;
 | 
			
		||||
@ -23,7 +23,7 @@ async function executeBundle(bundle, startNote) {
 | 
			
		||||
    // last \r\n is necessary if script contains line comment on its last line
 | 
			
		||||
    const script = "async function() {\r\n" + bundle.script + "\r\n}";
 | 
			
		||||
 | 
			
		||||
    const ctx = new ScriptContext(startNote, bundle.allNotes);
 | 
			
		||||
    const ctx = new ScriptContext(startNote, bundle.allNotes, targetNote);
 | 
			
		||||
 | 
			
		||||
    if (await bundle.note.hasLabel('manualTransactionHandling')) {
 | 
			
		||||
        return await execute(ctx, script, '');
 | 
			
		||||
@ -37,9 +37,10 @@ async function executeBundle(bundle, startNote) {
 | 
			
		||||
 * This method preserves frontend startNode - that's why we start execution from currentNote and override
 | 
			
		||||
 * bundle's startNote.
 | 
			
		||||
 */
 | 
			
		||||
async function executeScript(script, params, startNoteId, currentNoteId) {
 | 
			
		||||
async function executeScript(script, params, startNoteId, currentNoteId, targetNoteId) {
 | 
			
		||||
    const startNote = await repository.getNote(startNoteId);
 | 
			
		||||
    const currentNote = await repository.getNote(currentNoteId);
 | 
			
		||||
    const targetNote = await repository.getNote(targetNoteId);
 | 
			
		||||
 | 
			
		||||
    currentNote.content = `return await (${script}\r\n)(${getParams(params)})`;
 | 
			
		||||
    currentNote.type = 'code';
 | 
			
		||||
@ -47,7 +48,7 @@ async function executeScript(script, params, startNoteId, currentNoteId) {
 | 
			
		||||
 | 
			
		||||
    const bundle = await getScriptBundle(currentNote);
 | 
			
		||||
 | 
			
		||||
    return await executeBundle(bundle, startNote);
 | 
			
		||||
    return await executeBundle(bundle, startNote, targetNote);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function execute(ctx, script, paramsStr) {
 | 
			
		||||
 | 
			
		||||
@ -9,10 +9,10 @@ const config = require('./config');
 | 
			
		||||
const repository = require('./repository');
 | 
			
		||||
const axios = require('axios');
 | 
			
		||||
 | 
			
		||||
function ScriptContext(startNote, allNotes) {
 | 
			
		||||
function ScriptContext(startNote, allNotes, targetNote = null) {
 | 
			
		||||
    this.modules = {};
 | 
			
		||||
    this.notes = utils.toObject(allNotes, note => [note.noteId, note]);
 | 
			
		||||
    this.apis = utils.toObject(allNotes, note => [note.noteId, new ScriptApi(startNote, note)]);
 | 
			
		||||
    this.apis = utils.toObject(allNotes, note => [note.noteId, new ScriptApi(startNote, note, targetNote)]);
 | 
			
		||||
    this.require = moduleNoteIds => {
 | 
			
		||||
        return moduleName => {
 | 
			
		||||
            const candidates = allNotes.filter(note => moduleNoteIds.includes(note.noteId));
 | 
			
		||||
@ -27,9 +27,10 @@ function ScriptContext(startNote, allNotes) {
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function ScriptApi(startNote, currentNote) {
 | 
			
		||||
function ScriptApi(startNote, currentNote, targetNote) {
 | 
			
		||||
    this.startNote = startNote;
 | 
			
		||||
    this.currentNote = currentNote;
 | 
			
		||||
    this.targetNote = targetNote;
 | 
			
		||||
 | 
			
		||||
    this.axios = axios;
 | 
			
		||||
 | 
			
		||||
@ -44,6 +45,7 @@ function ScriptApi(startNote, currentNote) {
 | 
			
		||||
    this.getNote = repository.getNote;
 | 
			
		||||
    this.getBranch = repository.getBranch;
 | 
			
		||||
    this.getLabel = repository.getLabel;
 | 
			
		||||
    this.getRelation = repository.getRelation;
 | 
			
		||||
    this.getImage = repository.getImage;
 | 
			
		||||
    this.getEntity = repository.getEntity;
 | 
			
		||||
    this.getEntities = repository.getEntities;
 | 
			
		||||
 | 
			
		||||
@ -180,6 +180,8 @@
 | 
			
		||||
      </div>
 | 
			
		||||
 | 
			
		||||
      <div id="note-detail-wrapper">
 | 
			
		||||
        <div id="note-detail-script-area"></div>
 | 
			
		||||
 | 
			
		||||
        <div id="note-detail-component-wrapper">
 | 
			
		||||
          <div id="note-detail-text" class="note-detail-component" tabindex="2"></div>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user