mirror of
https://github.com/zadam/trilium.git
synced 2025-06-04 17:08:41 +02:00
added possibility to search by attached script returning note ids + some refactorings
This commit is contained in:
parent
9ca680f842
commit
fc13e1fa6a
@ -41,7 +41,7 @@ import macInit from './services/mac_init.js';
|
||||
import cssLoader from './services/css_loader.js';
|
||||
|
||||
// required for CKEditor image upload plugin
|
||||
window.glob.getCurrentNode = treeService.getCurrentNode;
|
||||
window.glob.getActiveNode = treeService.getActiveNode;
|
||||
window.glob.getHeaders = server.getHeaders;
|
||||
window.glob.showAddLinkDialog = addLinkDialog.showDialog;
|
||||
// this is required by CKEditor when uploading images
|
||||
@ -120,7 +120,7 @@ if (utils.isElectron()) {
|
||||
await treeService.activateNote(parentNoteId);
|
||||
|
||||
setTimeout(async () => {
|
||||
const parentNode = treeService.getCurrentNode();
|
||||
const parentNode = treeService.getActiveNode();
|
||||
|
||||
const {note} = await treeService.createNote(parentNode, parentNode.data.noteId, 'into', "text", parentNode.data.isProtected);
|
||||
|
||||
|
@ -16,7 +16,7 @@ async function showDialog() {
|
||||
|
||||
$dialog.modal();
|
||||
|
||||
const currentNode = treeService.getCurrentNode();
|
||||
const currentNode = treeService.getActiveNode();
|
||||
|
||||
branchId = currentNode.data.branchId;
|
||||
const branch = await treeCache.getBranch(branchId);
|
||||
|
@ -45,7 +45,7 @@ async function showDialog(defaultType) {
|
||||
|
||||
$dialog.modal();
|
||||
|
||||
const currentNode = treeService.getCurrentNode();
|
||||
const currentNode = treeService.getActiveNode();
|
||||
const noteTitle = await treeUtils.getNoteTitle(currentNode.data.noteId);
|
||||
|
||||
$noteTitle.html(noteTitle);
|
||||
@ -69,7 +69,7 @@ $form.submit(() => {
|
||||
|
||||
const exportVersion = exportFormat === 'opml' ? $dialog.find("input[name='opml-version']:checked").val() : "1.0";
|
||||
|
||||
const currentNode = treeService.getCurrentNode();
|
||||
const currentNode = treeService.getActiveNode();
|
||||
|
||||
exportBranch(currentNode.data.branchId, exportType, exportFormat, exportVersion);
|
||||
|
||||
|
@ -35,14 +35,14 @@ async function showDialog() {
|
||||
|
||||
glob.activeDialog = $dialog;
|
||||
|
||||
const currentNode = treeService.getCurrentNode();
|
||||
const currentNode = treeService.getActiveNode();
|
||||
$noteTitle.text(await treeUtils.getNoteTitle(currentNode.data.noteId));
|
||||
|
||||
$dialog.modal();
|
||||
}
|
||||
|
||||
$form.submit(() => {
|
||||
const currentNode = treeService.getCurrentNode();
|
||||
const currentNode = treeService.getActiveNode();
|
||||
|
||||
// disabling so that import is not triggered again.
|
||||
$importButton.attr("disabled", "disabled");
|
||||
|
@ -71,7 +71,7 @@ async function showTree() {
|
||||
}
|
||||
|
||||
$("#note-menu-button").click(async e => {
|
||||
const node = treeService.getCurrentNode();
|
||||
const node = treeService.getActiveNode();
|
||||
const branch = await treeCache.getBranch(node.data.branchId);
|
||||
const note = await treeCache.getNote(node.data.noteId);
|
||||
const parentNote = await treeCache.getNote(branch.parentNoteId);
|
||||
|
@ -138,7 +138,7 @@ function registerEntrypoints() {
|
||||
|
||||
// FIXME: do we really need these at this point?
|
||||
utils.bindShortcut("ctrl+shift+up", () => {
|
||||
const node = treeService.getCurrentNode();
|
||||
const node = treeService.getActiveNode();
|
||||
node.navigate($.ui.keyCode.UP, true);
|
||||
|
||||
$("#note-detail-text").focus();
|
||||
@ -147,7 +147,7 @@ function registerEntrypoints() {
|
||||
|
||||
// FIXME: do we really need these at this point?
|
||||
utils.bindShortcut("ctrl+shift+down", () => {
|
||||
const node = treeService.getCurrentNode();
|
||||
const node = treeService.getActiveNode();
|
||||
node.navigate($.ui.keyCode.DOWN, true);
|
||||
|
||||
$("#note-detail-text").focus();
|
||||
|
@ -187,7 +187,7 @@ async function loadNoteDetail(noteId) {
|
||||
// this is useful when user quickly switches notes (by e.g. holding down arrow) so that we don't
|
||||
// try to render all those loaded notes one after each other. This only guarantees that correct note
|
||||
// will be displayed independent of timing
|
||||
const currentTreeNode = treeService.getCurrentNode();
|
||||
const currentTreeNode = treeService.getActiveNode();
|
||||
if (currentTreeNode && currentTreeNode.data.noteId !== loadedNote.noteId) {
|
||||
return;
|
||||
}
|
||||
@ -196,7 +196,7 @@ async function loadNoteDetail(noteId) {
|
||||
activeNote = loadedNote;
|
||||
|
||||
if (utils.isDesktop()) {
|
||||
// needs to happen after loading the note itself because it references current noteId
|
||||
// needs to happen after loading the note itself because it references active noteId
|
||||
attributeService.refreshAttributes();
|
||||
}
|
||||
else {
|
||||
|
@ -43,7 +43,7 @@ function ensureProtectedSession(requireProtectedSession, modal) {
|
||||
$noteTitle.prop("readonly", true);
|
||||
|
||||
if (modal) {
|
||||
if (treeService.getCurrentNode().data.isProtected) {
|
||||
if (treeService.getActiveNode().data.isProtected) {
|
||||
$noteDetailWrapper.hide();
|
||||
}
|
||||
|
||||
|
@ -20,7 +20,7 @@ import confirmDialog from "../dialogs/confirm.js";
|
||||
const $tree = $("#tree");
|
||||
const $createTopLevelNoteButton = $("#create-top-level-note-button");
|
||||
const $collapseTreeButton = $("#collapse-tree-button");
|
||||
const $scrollToCurrentNoteButton = $("#scroll-to-current-note-button");
|
||||
const $scrollToActiveNoteButton = $("#scroll-to-active-note-button");
|
||||
const $notePathList = $("#note-path-list");
|
||||
const $notePathCount = $("#note-path-count");
|
||||
|
||||
@ -35,12 +35,12 @@ function getFocusedNode() {
|
||||
}
|
||||
|
||||
// note that if you want to access data like noteId or isProtected, you need to go into "data" property
|
||||
function getCurrentNode() {
|
||||
function getActiveNode() {
|
||||
return $tree.fancytree("getActiveNode");
|
||||
}
|
||||
|
||||
function getActiveNotePath() {
|
||||
const node = getCurrentNode();
|
||||
const node = getActiveNode();
|
||||
|
||||
return treeUtils.getNotePath(node);
|
||||
}
|
||||
@ -356,7 +356,7 @@ function clearSelectedNodes() {
|
||||
selectedNode.setSelected(false);
|
||||
}
|
||||
|
||||
const currentNode = getCurrentNode();
|
||||
const currentNode = getActiveNode();
|
||||
|
||||
if (currentNode) {
|
||||
currentNode.setSelected(true);
|
||||
@ -520,8 +520,8 @@ async function collapseTree(node = null) {
|
||||
node.visit(node => node.setExpanded(false));
|
||||
}
|
||||
|
||||
function scrollToCurrentNote() {
|
||||
const node = getCurrentNode();
|
||||
function scrollToActiveNote() {
|
||||
const node = getActiveNode();
|
||||
|
||||
if (node) {
|
||||
node.makeVisible({scrollIntoView: true});
|
||||
@ -697,7 +697,7 @@ messagingService.subscribeToSyncMessages(syncData => {
|
||||
});
|
||||
|
||||
utils.bindShortcut('ctrl+o', async () => {
|
||||
const node = getCurrentNode();
|
||||
const node = getActiveNode();
|
||||
const parentNoteId = node.data.parentNoteId;
|
||||
const isProtected = treeUtils.getParentProtectedStatus(node);
|
||||
|
||||
@ -709,7 +709,7 @@ utils.bindShortcut('ctrl+o', async () => {
|
||||
});
|
||||
|
||||
function createNoteInto() {
|
||||
const node = getCurrentNode();
|
||||
const node = getActiveNode();
|
||||
|
||||
createNote(node, node.data.noteId, 'into', null, node.data.isProtected, true);
|
||||
}
|
||||
@ -742,7 +742,7 @@ window.glob.createNoteInto = createNoteInto;
|
||||
|
||||
utils.bindShortcut('ctrl+p', createNoteInto);
|
||||
|
||||
utils.bindShortcut('ctrl+.', scrollToCurrentNote);
|
||||
utils.bindShortcut('ctrl+.', scrollToActiveNote);
|
||||
|
||||
$(window).bind('hashchange', function() {
|
||||
if (isNotePathInAddress()) {
|
||||
@ -760,18 +760,18 @@ utils.bindShortcut('alt+c', () => collapseTree()); // don't use shortened form s
|
||||
$collapseTreeButton.click(() => collapseTree());
|
||||
|
||||
$createTopLevelNoteButton.click(createNewTopLevelNote);
|
||||
$scrollToCurrentNoteButton.click(scrollToCurrentNote);
|
||||
$scrollToActiveNoteButton.click(scrollToActiveNote);
|
||||
|
||||
export default {
|
||||
reload,
|
||||
collapseTree,
|
||||
scrollToCurrentNote,
|
||||
scrollToActiveNote,
|
||||
setBranchBackgroundBasedOnProtectedStatus,
|
||||
setProtected,
|
||||
expandToNote,
|
||||
activateNote,
|
||||
getFocusedNode,
|
||||
getCurrentNode,
|
||||
getActiveNode,
|
||||
getActiveNotePath,
|
||||
setCurrentNotePathToHash,
|
||||
setNoteTitle,
|
||||
|
@ -115,22 +115,7 @@ async function prepareRealBranch(parentNote) {
|
||||
}
|
||||
|
||||
async function prepareSearchBranch(note) {
|
||||
const fullNote = await noteDetailService.loadNote(note.noteId);
|
||||
|
||||
console.log("ZZZ", fullNote.noteContent.content);
|
||||
|
||||
if (!fullNote.noteContent.content || !fullNote.noteContent.content.trim()) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const json = JSON.parse(fullNote.noteContent.content);
|
||||
|
||||
if (!json.searchString || !json.searchString.trim()) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const results = (await server.get('search/' + encodeURIComponent(json.searchString)))
|
||||
.filter(res => res.noteId !== note.noteId); // this is necessary because title of the search note is often the same as the search text which would match and create circle
|
||||
const results = await server.get('search-note/' + note.noteId);
|
||||
|
||||
// force to load all the notes at once instead of one by one
|
||||
await treeCache.getNotes(results.map(res => res.noteId));
|
||||
@ -149,7 +134,7 @@ async function prepareSearchBranch(note) {
|
||||
treeCache.addBranch(branch);
|
||||
}
|
||||
|
||||
return await prepareRealBranch(fullNote);
|
||||
return await prepareRealBranch(note);
|
||||
}
|
||||
|
||||
async function getExtraClasses(note) {
|
||||
|
@ -158,7 +158,7 @@ async function getContextMenuItems(event) {
|
||||
|
||||
function selectContextMenuItem(event, cmd) {
|
||||
// context menu is always triggered on current node
|
||||
const node = treeService.getCurrentNode();
|
||||
const node = treeService.getActiveNode();
|
||||
|
||||
if (cmd.startsWith("insertNoteAfter")) {
|
||||
const parentNoteId = node.data.parentNoteId;
|
||||
|
2
src/public/libraries/ckeditor/ckeditor.js
vendored
2
src/public/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,7 +28,7 @@
|
||||
}
|
||||
|
||||
async function validatorJavaScript(text, options) {
|
||||
if (glob.getCurrentNote().mime === 'application/json') {
|
||||
if (glob.getActiveNote().mime === 'application/json') {
|
||||
// eslint doesn't seem to validate pure JSON well
|
||||
return [];
|
||||
}
|
||||
|
@ -1,7 +1,10 @@
|
||||
"use strict";
|
||||
|
||||
const noteService = require('../../services/notes');
|
||||
const repository = require('../../services/repository');
|
||||
const noteCacheService = require('../../services/note_cache');
|
||||
const log = require('../../services/log');
|
||||
const scriptService = require('../../services/script');
|
||||
const searchService = require('../../services/search');
|
||||
|
||||
async function searchNotes(req) {
|
||||
@ -24,7 +27,79 @@ async function saveSearchToNote(req) {
|
||||
return { noteId: note.noteId };
|
||||
}
|
||||
|
||||
async function searchFromNote(req) {
|
||||
const note = await repository.getNote(req.params.noteId);
|
||||
|
||||
if (!note) {
|
||||
return [404, `Note ${req.params.noteId} has not been found.`];
|
||||
}
|
||||
|
||||
if (note.type !== 'search') {
|
||||
return [400, '`Note ${req.params.noteId} is not search note.`']
|
||||
}
|
||||
|
||||
const json = await note.getJsonContent();
|
||||
|
||||
if (!json || !json.searchString) {
|
||||
return [];
|
||||
}
|
||||
|
||||
let noteIds;
|
||||
|
||||
if (json.searchString.startsWith('=')) {
|
||||
const relationName = json.searchString.substr(1).trim();
|
||||
|
||||
noteIds = await searchFromRelation(note, relationName);
|
||||
}
|
||||
else {
|
||||
noteIds = searchService.searchForNoteIds(json.searchString);
|
||||
}
|
||||
|
||||
// we won't return search note's own noteId
|
||||
noteIds = noteIds.filter(noteId => noteId !== note.noteId);
|
||||
|
||||
return noteIds.map(noteCacheService.getNotePath).filter(res => !!res);
|
||||
}
|
||||
|
||||
async function searchFromRelation(note, relationName) {
|
||||
const scriptNote = await note.getRelationTarget(relationName);
|
||||
|
||||
if (!scriptNote) {
|
||||
log.info(`Search note's relation ${relationName} has not been found.`);
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
if (!scriptNote.isJavaScript() || scriptNote.getScriptEnv() !== 'backend') {
|
||||
log.info(`Note ${scriptNote.noteId} is not executable.`);
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
if (!note.isContentAvailable) {
|
||||
log.info(`Note ${scriptNote.noteId} is not available outside of protected session.`);
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
const result = await scriptService.executeNote(scriptNote, { originEntity: note });
|
||||
|
||||
if (!Array.isArray(result)) {
|
||||
log.info(`Result from ${scriptNote.noteId} is not an array.`);
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
if (result.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// we expect either array of noteIds (strings) or notes, in that case we extract noteIds ourselves
|
||||
return typeof result[0] === 'string' ? result : result.map(item => item.noteId);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
searchNotes,
|
||||
saveSearchToNote
|
||||
saveSearchToNote,
|
||||
searchFromNote
|
||||
};
|
@ -202,6 +202,7 @@ function register(app) {
|
||||
|
||||
apiRoute(GET, '/api/search/:searchString', searchRoute.searchNotes);
|
||||
apiRoute(POST, '/api/search/:searchString', searchRoute.saveSearchToNote);
|
||||
apiRoute(GET, '/api/search-note/:noteId', searchRoute.searchFromNote);
|
||||
|
||||
route(POST, '/api/login/sync', [], loginApiRoute.loginSync, apiResultHandler);
|
||||
// this is for entering protected mode so user has to be already logged-in (that's the reason we don't require username)
|
||||
|
@ -7,12 +7,14 @@ const log = require('./log');
|
||||
|
||||
async function executeNote(note, apiParams) {
|
||||
if (!note.isJavaScript() || note.getScriptEnv() !== 'backend' || !note.isContentAvailable) {
|
||||
log.info(`Cannot execute note ${note.noteId}`);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const bundle = await getScriptBundle(note);
|
||||
|
||||
await executeBundle(bundle, apiParams);
|
||||
return await executeBundle(bundle, apiParams);
|
||||
}
|
||||
|
||||
async function executeNoteNoException(note, apiParams) {
|
||||
|
@ -94,7 +94,7 @@
|
||||
|
||||
<a id="collapse-tree-button" title="Collapse note tree. Shortcut ALT+C" class="icon-action jam jam-layers"></a>
|
||||
|
||||
<a id="scroll-to-current-note-button" title="Scroll to current note. Shortcut CTRL+." class="icon-action jam jam-download"></a>
|
||||
<a id="scroll-to-active-note-button" title="Scroll to active note. Shortcut CTRL+." class="icon-action jam jam-download"></a>
|
||||
|
||||
<a id="toggle-search-button" title="Search in notes. Shortcut CTRL+S" class="icon-action jam jam-search"></a>
|
||||
</div>
|
||||
|
@ -13,17 +13,17 @@
|
||||
<form id="add-link-form">
|
||||
<div class="modal-body">
|
||||
<div id="add-link-type-div" class="radio">
|
||||
<label title="Add HTML link to the selected note at cursor in current note">
|
||||
<label title="Add HTML link to the selected note at cursor in active note">
|
||||
<input type="radio" name="add-link-type" value="html"/>
|
||||
add normal HTML link</label>
|
||||
|
||||
<label title="Add selected note as a child of current note">
|
||||
<label title="Add selected note as a child of active note">
|
||||
<input type="radio" name="add-link-type" value="selected-to-current"/>
|
||||
add selected note to current note</label>
|
||||
add selected note to active note</label>
|
||||
|
||||
<label title="Add current note as a child of the selected note">
|
||||
<label title="Add active note as a child of the selected note">
|
||||
<input type="radio" name="add-link-type" value="current-to-selected"/>
|
||||
add current note to selected note</label>
|
||||
add active note to selected note</label>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
|
@ -20,7 +20,7 @@
|
||||
<li><kbd>LEFT/RIGHT</kbd> - collapse/expand node</li>
|
||||
<li><kbd>ALT+LEFT/RIGHT</kbd> - go back / forwards in the history</li>
|
||||
<li><kbd>CTRL+J</kbd> - show <a class="external" href="https://github.com/zadam/trilium/wiki/Note-navigation#jump-to-note">"Jump to" dialog</a></li>
|
||||
<li><kbd>CTRL+.</kbd> - scroll to current note</li>
|
||||
<li><kbd>CTRL+.</kbd> - scroll to active note</li>
|
||||
<li><kbd>BACKSPACE</kbd> - jumps to parent note</li>
|
||||
<li><kbd>ALT+C</kbd> - collapse whole note tree</li>
|
||||
<li><kbd>ALT+-</kbd> (alt with minus sign) - collapse sub-tree</li>
|
||||
@ -35,9 +35,9 @@
|
||||
|
||||
<p class="card-text">
|
||||
<ul>
|
||||
<li><kbd>CTRL+O</kbd> - creates new note after the current note</li>
|
||||
<li><kbd>CTRL+P</kbd> - creates new sub-note into current note</li>
|
||||
<li><kbd>F2</kbd> - edit <a class="external" href="https://github.com/zadam/trilium/wiki/Tree concepts#prefix">prefix</a> of current note clone</li>
|
||||
<li><kbd>CTRL+O</kbd> - creates new note after the active note</li>
|
||||
<li><kbd>CTRL+P</kbd> - creates new sub-note into active note</li>
|
||||
<li><kbd>F2</kbd> - edit <a class="external" href="https://github.com/zadam/trilium/wiki/Tree concepts#prefix">prefix</a> of active note clone</li>
|
||||
</ul>
|
||||
</p>
|
||||
</div>
|
||||
@ -54,9 +54,9 @@
|
||||
<li><kbd>SHIFT+UP/DOWN</kbd> - multi-select note above/below</li>
|
||||
<li><kbd>CTRL+A</kbd> - select all notes in the current level</li>
|
||||
<li><kbd>CTRL+click</kbd> - select note</li>
|
||||
<li><kbd>CTRL+C</kbd> - copies current note (or current selection) into clipboard (used for <a class="external" href="https://github.com/zadam/trilium/wiki/Cloning notes">cloning</a>)</li>
|
||||
<li><kbd>CTRL+C</kbd> - copies active note (or current selection) into clipboard (used for <a class="external" href="https://github.com/zadam/trilium/wiki/Cloning notes">cloning</a>)</li>
|
||||
<li><kbd>CTRL+X</kbd> - cuts current (or current selection) note into clipboard (used for moving notes)</li>
|
||||
<li><kbd>CTRL+V</kbd> - pastes note(s) as sub-note into current note (which is either move or clone depending on whether it was copied or cut into clipboard)</li>
|
||||
<li><kbd>CTRL+V</kbd> - pastes note(s) as sub-note into active note (which is either move or clone depending on whether it was copied or cut into clipboard)</li>
|
||||
<li><kbd>DEL</kbd> - delete note / sub-tree</li>
|
||||
</ul>
|
||||
</p>
|
||||
@ -73,7 +73,7 @@
|
||||
<li><kbd>CTRL+K</kbd> - create / edit external link</li>
|
||||
<li><kbd>CTRL+L</kbd> - create internal link</li>
|
||||
<li><kbd>ALT+T</kbd> - inserts current date and time at caret position</li>
|
||||
<li><kbd>CTRL+.</kbd> - jump away to the tree pane and scroll to current note</li>
|
||||
<li><kbd>CTRL+.</kbd> - jump away to the tree pane and scroll to active note</li>
|
||||
</ul>
|
||||
</p>
|
||||
</div>
|
||||
|
@ -15,7 +15,7 @@
|
||||
|
||||
<a id="collapse-tree-button" title="Collapse note tree. Shortcut ALT+C" class="icon-action jam jam-layers"></a>
|
||||
|
||||
<a id="scroll-to-current-note-button" title="Scroll to current note. Shortcut CTRL+." class="icon-action jam jam-download"></a>
|
||||
<a id="scroll-to-active-note-button" title="Scroll to active note. Shortcut CTRL+." class="icon-action jam jam-download"></a>
|
||||
|
||||
<div class="dropdown">
|
||||
<a id="global-actions-button" title="Global actions" class="icon-action jam jam-cogs dropdown-toggle" data-toggle="dropdown"></a>
|
||||
|
Loading…
x
Reference in New Issue
Block a user