mirror of
https://github.com/zadam/trilium.git
synced 2025-11-12 16:25:51 +01:00
Merge branch 'master' into next58
# Conflicts: # libraries/ckeditor/ckeditor.js # libraries/ckeditor/ckeditor.js.map # package-lock.json # package.json # src/public/app/layouts/desktop_layout.js
This commit is contained in:
@@ -191,6 +191,11 @@ function deleteBranch(req) {
|
||||
const last = req.query.last === 'true';
|
||||
const eraseNotes = req.query.eraseNotes === 'true';
|
||||
const branch = becca.getBranch(req.params.branchId);
|
||||
|
||||
if (!branch) {
|
||||
return [404, `Branch ${req.params.branchId} not found`];
|
||||
}
|
||||
|
||||
const taskContext = TaskContext.getInstance(req.query.taskId, 'delete-notes');
|
||||
|
||||
const deleteId = utils.randomString(10);
|
||||
|
||||
@@ -37,7 +37,7 @@ function getNeighbors(note, depth) {
|
||||
const retNoteIds = [];
|
||||
|
||||
function isIgnoredRelation(relation) {
|
||||
return ['relationMapLink', 'template', 'image'].includes(relation.name);
|
||||
return ['relationMapLink', 'template', 'image', 'ancestor'].includes(relation.name);
|
||||
}
|
||||
|
||||
// forward links
|
||||
@@ -83,16 +83,28 @@ function getNeighbors(note, depth) {
|
||||
|
||||
function getLinkMap(req) {
|
||||
const mapRootNote = becca.getNote(req.params.noteId);
|
||||
// if the map root itself has ignore (journal typically) then there wouldn't be anything to display so
|
||||
// if the map root itself has exclude attribute (journal typically) then there wouldn't be anything to display, so
|
||||
// we'll just ignore it
|
||||
const ignoreExcludeFromNoteMap = mapRootNote.hasLabel('excludeFromNoteMap');
|
||||
let unfilteredNotes;
|
||||
|
||||
if (mapRootNote.type === 'search') {
|
||||
// for search notes we want to consider the direct search results only without the descendants
|
||||
unfilteredNotes = mapRootNote.getSearchResultNotes();
|
||||
} else {
|
||||
unfilteredNotes = mapRootNote.getSubtree({includeArchived: false, resolveSearch: true}).notes;
|
||||
}
|
||||
|
||||
const noteIds = new Set(
|
||||
mapRootNote.getSubtreeNotes(false)
|
||||
unfilteredNotes
|
||||
.filter(note => ignoreExcludeFromNoteMap || !note.hasLabel('excludeFromNoteMap'))
|
||||
.map(note => note.noteId)
|
||||
);
|
||||
|
||||
if (mapRootNote.type === 'search') {
|
||||
noteIds.delete(mapRootNote.noteId);
|
||||
}
|
||||
|
||||
for (const noteId of getNeighbors(mapRootNote, 3)) {
|
||||
noteIds.add(noteId);
|
||||
}
|
||||
@@ -123,7 +135,7 @@ function getLinkMap(req) {
|
||||
return true;
|
||||
}
|
||||
})
|
||||
.map(rel => ({
|
||||
.map(rel => ({
|
||||
id: rel.noteId + "-" + rel.name + "-" + rel.value,
|
||||
sourceNoteId: rel.noteId,
|
||||
targetNoteId: rel.value,
|
||||
@@ -142,8 +154,9 @@ function getTreeMap(req) {
|
||||
// if the map root itself has ignore (journal typically) then there wouldn't be anything to display so
|
||||
// we'll just ignore it
|
||||
const ignoreExcludeFromNoteMap = mapRootNote.hasLabel('excludeFromNoteMap');
|
||||
const subtree = mapRootNote.getSubtree({includeArchived: false, resolveSearch: true});
|
||||
|
||||
const notes = mapRootNote.getSubtreeNotes(false)
|
||||
const notes = subtree.notes
|
||||
.filter(note => ignoreExcludeFromNoteMap || !note.hasLabel('excludeFromNoteMap'))
|
||||
.filter(note => {
|
||||
if (note.type !== 'image' || note.getChildNotes().length > 0) {
|
||||
@@ -158,7 +171,6 @@ function getTreeMap(req) {
|
||||
|
||||
return !note.getParentNotes().find(parentNote => parentNote.noteId === imageLinkRelation.noteId);
|
||||
})
|
||||
.concat(...mapRootNote.getParentNotes().filter(note => note.noteId !== 'none'))
|
||||
.map(note => [
|
||||
note.noteId,
|
||||
note.getTitleOrProtected(),
|
||||
@@ -170,25 +182,40 @@ function getTreeMap(req) {
|
||||
|
||||
const links = [];
|
||||
|
||||
for (const branch of Object.values(becca.branches)) {
|
||||
if (!noteIds.has(branch.parentNoteId) || !noteIds.has(branch.noteId)) {
|
||||
for (const {parentNoteId, childNoteId} of subtree.relationships) {
|
||||
if (!noteIds.has(parentNoteId) || !noteIds.has(childNoteId)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
links.push({
|
||||
id: branch.branchId,
|
||||
sourceNoteId: branch.parentNoteId,
|
||||
targetNoteId: branch.noteId
|
||||
sourceNoteId: parentNoteId,
|
||||
targetNoteId: childNoteId
|
||||
});
|
||||
}
|
||||
|
||||
const noteIdToDescendantCountMap = buildDescendantCountMap();
|
||||
|
||||
updateDescendantCountMapForSearch(noteIdToDescendantCountMap, subtree.relationships);
|
||||
|
||||
return {
|
||||
notes: notes,
|
||||
noteIdToDescendantCountMap: buildDescendantCountMap(),
|
||||
noteIdToDescendantCountMap: noteIdToDescendantCountMap,
|
||||
links: links
|
||||
};
|
||||
}
|
||||
|
||||
function updateDescendantCountMapForSearch(noteIdToDescendantCountMap, relationships) {
|
||||
for (const {parentNoteId, childNoteId} of relationships) {
|
||||
const parentNote = becca.notes[parentNoteId];
|
||||
if (!parentNote || parentNote.type !== 'search') {
|
||||
continue;
|
||||
}
|
||||
|
||||
noteIdToDescendantCountMap[parentNote.noteId] = noteIdToDescendantCountMap[parentNoteId] || 0;
|
||||
noteIdToDescendantCountMap[parentNote.noteId] += noteIdToDescendantCountMap[childNoteId] || 1;
|
||||
}
|
||||
}
|
||||
|
||||
function removeImages(document) {
|
||||
const images = document.getElementsByTagName('img');
|
||||
while (images.length > 0) {
|
||||
@@ -290,7 +317,7 @@ function findExcerpts(sourceNote, referencedNoteId) {
|
||||
function getFilteredBacklinks(note) {
|
||||
return note.getTargetRelations()
|
||||
// search notes have "ancestor" relations which are not interesting
|
||||
.filter(note => note.getNote().type !== 'search');
|
||||
.filter(relation => !!relation.getNote() && relation.getNote().type !== 'search');
|
||||
}
|
||||
|
||||
function getBacklinkCount(req) {
|
||||
|
||||
@@ -5,6 +5,7 @@ const protectedSessionService = require('../../services/protected_session');
|
||||
const noteRevisionService = require('../../services/note_revisions');
|
||||
const utils = require('../../services/utils');
|
||||
const sql = require('../../services/sql');
|
||||
const cls = require('../../services/cls');
|
||||
const path = require('path');
|
||||
const becca = require("../../becca/becca");
|
||||
|
||||
@@ -124,8 +125,15 @@ function getEditedNotesOnDate(req) {
|
||||
ORDER BY isDeleted
|
||||
LIMIT 50`, {date: req.params.date + '%'});
|
||||
|
||||
const notes = becca.getNotes(noteIds, true)
|
||||
.map(note => note.getPojo());
|
||||
let notes = becca.getNotes(noteIds, true);
|
||||
|
||||
// Narrow down the results if a note is hoisted, similar to "Jump to note".
|
||||
const hoistedNoteId = cls.getHoistedNoteId();
|
||||
if (hoistedNoteId !== 'root') {
|
||||
notes = notes.filter(note => note.hasAncestor(hoistedNoteId));
|
||||
}
|
||||
|
||||
notes = notes.map(note => note.getPojo());
|
||||
|
||||
for (const note of notes) {
|
||||
const notePath = note.isDeleted ? null : beccaService.getNotePath(note.noteId);
|
||||
|
||||
@@ -6,6 +6,7 @@ const sql = require('../../services/sql');
|
||||
const utils = require('../../services/utils');
|
||||
const log = require('../../services/log');
|
||||
const TaskContext = require('../../services/task_context');
|
||||
const protectedSessionService = require('../../services/protected_session');
|
||||
const fs = require('fs');
|
||||
const becca = require("../../becca/becca");
|
||||
|
||||
@@ -305,6 +306,21 @@ function uploadModifiedFile(req) {
|
||||
note.setContent(fileContent);
|
||||
}
|
||||
|
||||
function forceSaveNoteRevision(req) {
|
||||
const {noteId} = req.params;
|
||||
const note = becca.getNote(noteId);
|
||||
|
||||
if (!note) {
|
||||
return [404, `Note ${noteId} not found.`];
|
||||
}
|
||||
|
||||
if (!note.isContentAvailable()) {
|
||||
return [400, `Note revision of a protected note cannot be created outside of a protected session.`];
|
||||
}
|
||||
|
||||
note.saveNoteRevision();
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
getNote,
|
||||
updateNoteContent,
|
||||
@@ -319,5 +335,6 @@ module.exports = {
|
||||
duplicateSubtree,
|
||||
eraseDeletedNotesNow,
|
||||
getDeleteNotesPreview,
|
||||
uploadModifiedFile
|
||||
uploadModifiedFile,
|
||||
forceSaveNoteRevision
|
||||
};
|
||||
|
||||
@@ -33,6 +33,7 @@ const ALLOWED_OPTIONS = new Set([
|
||||
'editedNotesWidget',
|
||||
'calendarWidget',
|
||||
'vimKeymapEnabled',
|
||||
'codeLineWrapEnabled',
|
||||
'codeNotesMimeTypes',
|
||||
'spellCheckEnabled',
|
||||
'spellCheckLanguageCode',
|
||||
@@ -58,7 +59,8 @@ const ALLOWED_OPTIONS = new Set([
|
||||
'compressImages',
|
||||
'downloadImagesAutomatically',
|
||||
'minTocHeadings',
|
||||
'checkForUpdates'
|
||||
'checkForUpdates',
|
||||
'disableTray'
|
||||
]);
|
||||
|
||||
function getOptions() {
|
||||
|
||||
@@ -2,49 +2,11 @@
|
||||
|
||||
const becca = require('../../becca/becca');
|
||||
const SearchContext = require('../../services/search/search_context');
|
||||
const log = require('../../services/log');
|
||||
const scriptService = require('../../services/script');
|
||||
const searchService = require('../../services/search/services/search');
|
||||
const bulkActionService = require("../../services/bulk_actions");
|
||||
const cls = require("../../services/cls");
|
||||
const {formatAttrForSearch} = require("../../services/attribute_formatter");
|
||||
|
||||
function searchFromNoteInt(note) {
|
||||
let searchResultNoteIds, highlightedTokens;
|
||||
|
||||
const searchScript = note.getRelationValue('searchScript');
|
||||
const searchString = note.getLabelValue('searchString');
|
||||
|
||||
if (searchScript) {
|
||||
searchResultNoteIds = searchFromRelation(note, 'searchScript');
|
||||
highlightedTokens = [];
|
||||
} else {
|
||||
const searchContext = new SearchContext({
|
||||
fastSearch: note.hasLabel('fastSearch'),
|
||||
ancestorNoteId: note.getRelationValue('ancestor'),
|
||||
ancestorDepth: note.getLabelValue('ancestorDepth'),
|
||||
includeArchivedNotes: note.hasLabel('includeArchivedNotes'),
|
||||
orderBy: note.getLabelValue('orderBy'),
|
||||
orderDirection: note.getLabelValue('orderDirection'),
|
||||
limit: note.getLabelValue('limit'),
|
||||
debug: note.hasLabel('debug'),
|
||||
fuzzyAttributeSearch: false
|
||||
});
|
||||
|
||||
searchResultNoteIds = searchService.findResultsWithQuery(searchString, searchContext)
|
||||
.map(sr => sr.noteId);
|
||||
|
||||
highlightedTokens = searchContext.highlightedTokens;
|
||||
}
|
||||
|
||||
// we won't return search note's own noteId
|
||||
// also don't allow root since that would force infinite cycle
|
||||
return {
|
||||
searchResultNoteIds: searchResultNoteIds.filter(resultNoteId => !['root', note.noteId].includes(resultNoteId)),
|
||||
highlightedTokens
|
||||
};
|
||||
}
|
||||
|
||||
function searchFromNote(req) {
|
||||
const note = becca.getNote(req.params.noteId);
|
||||
|
||||
@@ -61,7 +23,7 @@ function searchFromNote(req) {
|
||||
return [400, `Note ${req.params.noteId} is not a search note.`]
|
||||
}
|
||||
|
||||
return searchFromNoteInt(note);
|
||||
return searchService.searchFromNote(note);
|
||||
}
|
||||
|
||||
function searchAndExecute(req) {
|
||||
@@ -80,48 +42,11 @@ function searchAndExecute(req) {
|
||||
return [400, `Note ${req.params.noteId} is not a search note.`]
|
||||
}
|
||||
|
||||
const {searchResultNoteIds} = searchFromNoteInt(note);
|
||||
const {searchResultNoteIds} = searchService.searchFromNote(note);
|
||||
|
||||
bulkActionService.executeActions(note, searchResultNoteIds);
|
||||
}
|
||||
|
||||
function searchFromRelation(note, relationName) {
|
||||
const scriptNote = 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 = 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);
|
||||
}
|
||||
|
||||
function quickSearch(req) {
|
||||
const {searchString} = req.params;
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ function getSubtreeSize(req) {
|
||||
return [404, `Note ${noteId} was not found.`];
|
||||
}
|
||||
|
||||
const subTreeNoteIds = note.getSubtreeNotes().map(note => note.noteId);
|
||||
const subTreeNoteIds = note.getSubtreeNoteIds();
|
||||
|
||||
sql.fillParamList(subTreeNoteIds);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user