mirror of
https://github.com/zadam/trilium.git
synced 2025-06-06 18:08:33 +02:00
Merge remote-tracking branch 'origin/stable'
This commit is contained in:
commit
5b3a6b5e9d
@ -1,54 +0,0 @@
|
|||||||
const $dialog = $("#delete-notes-dialog");
|
|
||||||
const $confirmContent = $("#delete-notes-dialog-content");
|
|
||||||
const $okButton = $("#delete-notes-dialog-ok-button");
|
|
||||||
const $cancelButton = $("#delete-notes-dialog-cancel-button");
|
|
||||||
const $custom = $("#delete-notes-dialog-custom");
|
|
||||||
|
|
||||||
const DELETE_NOTE_BUTTON_ID = "delete-notes-dialog-delete-note";
|
|
||||||
|
|
||||||
let $originallyFocused; // element focused before the dialog was opened so we can return to it afterwards
|
|
||||||
|
|
||||||
export function showDialog(message) {
|
|
||||||
$originallyFocused = $(':focus');
|
|
||||||
|
|
||||||
$custom.hide();
|
|
||||||
|
|
||||||
glob.activeDialog = $dialog;
|
|
||||||
|
|
||||||
if (typeof message === 'string') {
|
|
||||||
message = $("<div>").text(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
$confirmContent.empty().append(message);
|
|
||||||
|
|
||||||
$dialog.modal();
|
|
||||||
|
|
||||||
return new Promise((res, rej) => { resolve = res; });
|
|
||||||
}
|
|
||||||
|
|
||||||
export function isDeleteNoteChecked() {
|
|
||||||
return $("#" + DELETE_NOTE_BUTTON_ID + ":checked").length > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
$dialog.on('shown.bs.modal', () => $okButton.trigger("focus"));
|
|
||||||
|
|
||||||
$dialog.on("hidden.bs.modal", () => {
|
|
||||||
if (resolve) {
|
|
||||||
resolve(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($originallyFocused) {
|
|
||||||
$originallyFocused.trigger('focus');
|
|
||||||
$originallyFocused = null;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
function doResolve(ret) {
|
|
||||||
resolve(ret);
|
|
||||||
resolve = null;
|
|
||||||
|
|
||||||
$dialog.modal("hide");
|
|
||||||
}
|
|
||||||
|
|
||||||
$cancelButton.on('click', () => doResolve(false));
|
|
||||||
$okButton.on('click', () => doResolve(true));
|
|
@ -79,6 +79,15 @@ async function renderAttributes(attributes, renderIsInheritable) {
|
|||||||
return $container;
|
return $container;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const HIDDEN_ATTRIBUTES = [
|
||||||
|
'originalFileName',
|
||||||
|
'template',
|
||||||
|
'cssClass',
|
||||||
|
'iconClass',
|
||||||
|
'pageSize',
|
||||||
|
'viewType'
|
||||||
|
];
|
||||||
|
|
||||||
async function renderNormalAttributes(note) {
|
async function renderNormalAttributes(note) {
|
||||||
const promotedDefinitionAttributes = note.getPromotedDefinitionAttributes();
|
const promotedDefinitionAttributes = note.getPromotedDefinitionAttributes();
|
||||||
let attrs = note.getAttributes();
|
let attrs = note.getAttributes();
|
||||||
@ -90,6 +99,7 @@ async function renderNormalAttributes(note) {
|
|||||||
attrs = attrs.filter(
|
attrs = attrs.filter(
|
||||||
attr => !attr.isDefinition()
|
attr => !attr.isDefinition()
|
||||||
&& !attr.isAutoLink
|
&& !attr.isAutoLink
|
||||||
|
&& !HIDDEN_ATTRIBUTES.includes(attr.name)
|
||||||
&& attr.noteId === note.noteId
|
&& attr.noteId === note.noteId
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -74,9 +74,6 @@ async function deleteNotes(branchIdsToDelete) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const deleteNotesDialog = await import("../dialogs/delete_notes.js");
|
|
||||||
deleteNotesDialog.showDialog();
|
|
||||||
|
|
||||||
const $deleteClonesCheckbox = $('<div class="form-check">')
|
const $deleteClonesCheckbox = $('<div class="form-check">')
|
||||||
.append($('<input type="checkbox" class="form-check-input" id="delete-clones-checkbox">'))
|
.append($('<input type="checkbox" class="form-check-input" id="delete-clones-checkbox">'))
|
||||||
.append($('<label for="delete-clones-checkbox">')
|
.append($('<label for="delete-clones-checkbox">')
|
||||||
@ -96,7 +93,11 @@ async function deleteNotes(branchIdsToDelete) {
|
|||||||
.append($nodeTitles)
|
.append($nodeTitles)
|
||||||
.append($deleteClonesCheckbox);
|
.append($deleteClonesCheckbox);
|
||||||
|
|
||||||
return false;
|
const confirmDialog = await import('../dialogs/confirm.js');
|
||||||
|
|
||||||
|
if (!await confirmDialog.confirm($confirmText)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
const deleteClones = $deleteClonesCheckbox.find("input").is(":checked");
|
const deleteClones = $deleteClonesCheckbox.find("input").is(":checked");
|
||||||
|
|
||||||
|
@ -74,7 +74,11 @@ export default class Entrypoints extends Component {
|
|||||||
|
|
||||||
await ws.waitForMaxKnownEntityChangeId();
|
await ws.waitForMaxKnownEntityChangeId();
|
||||||
|
|
||||||
await appContext.tabManager.openTabWithNote(note.noteId, true);
|
const hoistedNoteId = appContext.tabManager.getActiveTabContext()
|
||||||
|
? appContext.tabManager.getActiveTabContext().hoistedNoteId
|
||||||
|
: 'root';
|
||||||
|
|
||||||
|
await appContext.tabManager.openTabWithNote(note.noteId, true, null, hoistedNoteId);
|
||||||
|
|
||||||
appContext.triggerEvent('focusAndSelectTitle');
|
appContext.triggerEvent('focusAndSelectTitle');
|
||||||
}
|
}
|
||||||
|
@ -194,9 +194,18 @@ const ATTR_HELP = {
|
|||||||
"appTheme": "marks CSS notes which are full Trilium themes and are thus available in Trilium options.",
|
"appTheme": "marks CSS notes which are full Trilium themes and are thus available in Trilium options.",
|
||||||
"cssClass": "value of this label is then added as CSS class to the node representing given note in the tree. This can be useful for advanced theming. Can be used in template notes.",
|
"cssClass": "value of this label is then added as CSS class to the node representing given note in the tree. This can be useful for advanced theming. Can be used in template notes.",
|
||||||
"iconClass": "value of this label is added as a CSS class to the icon on the tree which can help visually distinguish the notes in the tree. Example might be bx bx-home - icons are taken from boxicons. Can be used in template notes.",
|
"iconClass": "value of this label is added as a CSS class to the icon on the tree which can help visually distinguish the notes in the tree. Example might be bx bx-home - icons are taken from boxicons. Can be used in template notes.",
|
||||||
"bookZoomLevel": 'applies only to book note and sets the "zoom level" (how many notes fit on 1 row)',
|
"pageSize": "number of items per page in note listing",
|
||||||
"customRequestHandler": 'see <a href="javascript:" data-help-page="Custom request handler">Custom request handler</a>',
|
"customRequestHandler": 'see <a href="javascript:" data-help-page="Custom request handler">Custom request handler</a>',
|
||||||
"customResourceProvider": 'see <a href="javascript:" data-help-page="Custom request handler">Custom request handler</a>'
|
"customResourceProvider": 'see <a href="javascript:" data-help-page="Custom request handler">Custom request handler</a>',
|
||||||
|
"widget": "marks this note as a custom widget which will be added to the Trilium component tree",
|
||||||
|
"workspace": "marks this note as a workspace which allows easy hoisting",
|
||||||
|
"workspaceIconClass": "defines box icon CSS class which will be used in tab when hoisted to this note",
|
||||||
|
"workspaceTabBackgroundColor": "CSS color used in the note tab when hoisted to this note",
|
||||||
|
"searchHome": "new search notes will be created as children of this note",
|
||||||
|
"hoistedSearchHome": "new search notes will be created as children of this note when hoisted to some ancestor of this note",
|
||||||
|
"inbox": "default inbox location for new notes",
|
||||||
|
"hoistedInbox": "default inbox location for new notes when hoisted to some ancestor of this note",
|
||||||
|
"sqlConsoleHome": "default location of SQL console notes",
|
||||||
},
|
},
|
||||||
"relation": {
|
"relation": {
|
||||||
"runOnNoteCreation": "executes when note is created on backend",
|
"runOnNoteCreation": "executes when note is created on backend",
|
||||||
|
@ -278,7 +278,7 @@ export default class NoteDetailWidget extends TabAwareWidget {
|
|||||||
|
|
||||||
const label = attrs.find(attr =>
|
const label = attrs.find(attr =>
|
||||||
attr.type === 'label'
|
attr.type === 'label'
|
||||||
&& ['readOnly', 'autoReadOnlyDisabled', 'cssClass', 'bookZoomLevel', 'displayRelations'].includes(attr.name)
|
&& ['readOnly', 'autoReadOnlyDisabled', 'cssClass', 'displayRelations'].includes(attr.name)
|
||||||
&& attr.isAffecting(this.note));
|
&& attr.isAffecting(this.note));
|
||||||
|
|
||||||
const relation = attrs.find(attr =>
|
const relation = attrs.find(attr =>
|
||||||
|
@ -9,8 +9,27 @@ const cls = require('../../services/cls');
|
|||||||
const repository = require('../../services/repository');
|
const repository = require('../../services/repository');
|
||||||
|
|
||||||
function getInboxNote(req) {
|
function getInboxNote(req) {
|
||||||
return attributeService.getNoteWithLabel('inbox')
|
const hoistedNote = getHoistedNote();
|
||||||
|| dateNoteService.getDateNote(req.params.date);
|
|
||||||
|
let inbox;
|
||||||
|
|
||||||
|
if (hoistedNote) {
|
||||||
|
([inbox] = hoistedNote.getDescendantNotesWithLabel('hoistedInbox'));
|
||||||
|
|
||||||
|
if (!inbox) {
|
||||||
|
([inbox] = hoistedNote.getDescendantNotesWithLabel('inbox'));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!inbox) {
|
||||||
|
inbox = hoistedNote;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
inbox = attributeService.getNoteWithLabel('inbox')
|
||||||
|
|| dateNoteService.getDateNote(req.params.date);
|
||||||
|
}
|
||||||
|
|
||||||
|
return inbox;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getDateNote(req) {
|
function getDateNote(req) {
|
||||||
@ -66,27 +85,18 @@ function createSearchNote(req) {
|
|||||||
const searchString = params.searchString || "";
|
const searchString = params.searchString || "";
|
||||||
let ancestorNoteId = params.ancestorNoteId;
|
let ancestorNoteId = params.ancestorNoteId;
|
||||||
|
|
||||||
const hoistedNote = cls.getHoistedNoteId() && cls.getHoistedNoteId() !== 'root'
|
const hoistedNote = getHoistedNote();
|
||||||
? repository.getNote(cls.getHoistedNoteId())
|
|
||||||
: null;
|
|
||||||
|
|
||||||
let searchHome;
|
let searchHome;
|
||||||
|
|
||||||
if (hoistedNote) {
|
if (hoistedNote) {
|
||||||
([searchHome] = hoistedNote.getDescendantNotesWithLabel('hoistedSearchHome'));
|
([searchHome] = hoistedNote.getDescendantNotesWithLabel('hoistedSearchHome'));
|
||||||
}
|
|
||||||
|
|
||||||
if (!searchHome) {
|
if (!searchHome) {
|
||||||
const today = dateUtils.localNowDate();
|
([searchHome] = hoistedNote.getDescendantNotesWithLabel('searchHome'));
|
||||||
|
}
|
||||||
|
|
||||||
searchHome = attributeService.getNoteWithLabel('searchHome')
|
if (!searchHome) {
|
||||||
|| dateNoteService.getDateNote(today);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hoistedNote) {
|
|
||||||
|
|
||||||
if (!hoistedNote.getDescendantNoteIds().includes(searchHome.noteId)) {
|
|
||||||
// otherwise the note would be saved outside of the hoisted context which is weird
|
|
||||||
searchHome = hoistedNote;
|
searchHome = hoistedNote;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -94,6 +104,12 @@ function createSearchNote(req) {
|
|||||||
ancestorNoteId = hoistedNote.noteId;
|
ancestorNoteId = hoistedNote.noteId;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
const today = dateUtils.localNowDate();
|
||||||
|
|
||||||
|
searchHome = attributeService.getNoteWithLabel('searchHome')
|
||||||
|
|| dateNoteService.getDateNote(today);
|
||||||
|
}
|
||||||
|
|
||||||
const {note} = noteService.createNewNote({
|
const {note} = noteService.createNewNote({
|
||||||
parentNoteId: searchHome.noteId,
|
parentNoteId: searchHome.noteId,
|
||||||
@ -112,6 +128,12 @@ function createSearchNote(req) {
|
|||||||
return note;
|
return note;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getHoistedNote() {
|
||||||
|
return cls.getHoistedNoteId() && cls.getHoistedNoteId() !== 'root'
|
||||||
|
? repository.getNote(cls.getHoistedNoteId())
|
||||||
|
: null;
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
getInboxNote,
|
getInboxNote,
|
||||||
getDateNote,
|
getDateNote,
|
||||||
|
@ -27,7 +27,6 @@ const BUILTIN_ATTRIBUTES = [
|
|||||||
{ type: 'label', name: 'run', isDangerous: true },
|
{ type: 'label', name: 'run', isDangerous: true },
|
||||||
{ type: 'label', name: 'customRequestHandler', isDangerous: true },
|
{ type: 'label', name: 'customRequestHandler', isDangerous: true },
|
||||||
{ type: 'label', name: 'customResourceProvider', isDangerous: true },
|
{ type: 'label', name: 'customResourceProvider', isDangerous: true },
|
||||||
{ type: 'label', name: 'bookZoomLevel', isDangerous: false },
|
|
||||||
{ type: 'label', name: 'widget', isDangerous: true },
|
{ type: 'label', name: 'widget', isDangerous: true },
|
||||||
{ type: 'label', name: 'noteInfoWidgetDisabled' },
|
{ type: 'label', name: 'noteInfoWidgetDisabled' },
|
||||||
{ type: 'label', name: 'linkMapWidgetDisabled' },
|
{ type: 'label', name: 'linkMapWidgetDisabled' },
|
||||||
@ -38,9 +37,12 @@ const BUILTIN_ATTRIBUTES = [
|
|||||||
{ type: 'label', name: 'workspaceIconClass' },
|
{ type: 'label', name: 'workspaceIconClass' },
|
||||||
{ type: 'label', name: 'workspaceTabBackgroundColor' },
|
{ type: 'label', name: 'workspaceTabBackgroundColor' },
|
||||||
{ type: 'label', name: 'searchHome' },
|
{ type: 'label', name: 'searchHome' },
|
||||||
|
{ type: 'label', name: 'hoistedInbox' },
|
||||||
{ type: 'label', name: 'hoistedSearchHome' },
|
{ type: 'label', name: 'hoistedSearchHome' },
|
||||||
{ type: 'label', name: 'sqlConsoleHome' },
|
{ type: 'label', name: 'sqlConsoleHome' },
|
||||||
{ type: 'label', name: 'datePattern' },
|
{ type: 'label', name: 'datePattern' },
|
||||||
|
{ type: 'label', name: 'pageSize' },
|
||||||
|
{ type: 'label', name: 'viewType' },
|
||||||
|
|
||||||
// relation names
|
// relation names
|
||||||
{ type: 'relation', name: 'runOnNoteCreation', isDangerous: true },
|
{ type: 'relation', name: 'runOnNoteCreation', isDangerous: true },
|
||||||
|
@ -44,10 +44,14 @@ function isEntityEventsDisabled() {
|
|||||||
return !!namespace.get('disableEntityEvents');
|
return !!namespace.get('disableEntityEvents');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function clearEntityChanges() {
|
||||||
|
namespace.set('entityChanges', []);
|
||||||
|
}
|
||||||
|
|
||||||
function getAndClearEntityChanges() {
|
function getAndClearEntityChanges() {
|
||||||
const entityChanges = namespace.get('entityChanges') || [];
|
const entityChanges = namespace.get('entityChanges') || [];
|
||||||
|
|
||||||
namespace.set('entityChanges', []);
|
clearEntityChanges();
|
||||||
|
|
||||||
return entityChanges;
|
return entityChanges;
|
||||||
}
|
}
|
||||||
@ -92,6 +96,7 @@ module.exports = {
|
|||||||
disableEntityEvents,
|
disableEntityEvents,
|
||||||
isEntityEventsDisabled,
|
isEntityEventsDisabled,
|
||||||
reset,
|
reset,
|
||||||
|
clearEntityChanges,
|
||||||
getAndClearEntityChanges,
|
getAndClearEntityChanges,
|
||||||
addEntityChange,
|
addEntityChange,
|
||||||
getEntityFromCache,
|
getEntityFromCache,
|
||||||
|
@ -23,7 +23,6 @@ const IGNORED_ATTR_NAMES = [
|
|||||||
"archived",
|
"archived",
|
||||||
"hidepromotedattributes",
|
"hidepromotedattributes",
|
||||||
"keyboardshortcut",
|
"keyboardshortcut",
|
||||||
"bookzoomlevel",
|
|
||||||
"noteinfowidgetdisabled",
|
"noteinfowidgetdisabled",
|
||||||
"linkmapwidgetdisabled",
|
"linkmapwidgetdisabled",
|
||||||
"noterevisionswidgetdisabled",
|
"noterevisionswidgetdisabled",
|
||||||
|
@ -84,7 +84,15 @@ function exec(opts) {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
request.end(opts.body);
|
let payload;
|
||||||
|
|
||||||
|
if (opts.body) {
|
||||||
|
payload = typeof opts.body === 'object'
|
||||||
|
? JSON.stringify(opts.body)
|
||||||
|
: opts.body;
|
||||||
|
}
|
||||||
|
|
||||||
|
request.end(payload);
|
||||||
}
|
}
|
||||||
catch (e) {
|
catch (e) {
|
||||||
reject(generateError(opts, e.message));
|
reject(generateError(opts, e.message));
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
const log = require('./log');
|
const log = require('./log');
|
||||||
const Database = require('better-sqlite3');
|
const Database = require('better-sqlite3');
|
||||||
const dataDir = require('./data_dir');
|
const dataDir = require('./data_dir');
|
||||||
|
const cls = require('./cls');
|
||||||
|
|
||||||
const dbConnection = new Database(dataDir.DOCUMENT_PATH);
|
const dbConnection = new Database(dataDir.DOCUMENT_PATH);
|
||||||
dbConnection.pragma('journal_mode = WAL');
|
dbConnection.pragma('journal_mode = WAL');
|
||||||
@ -229,13 +230,20 @@ function wrap(query, func) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function transactional(func) {
|
function transactional(func) {
|
||||||
const ret = dbConnection.transaction(func).deferred();
|
try {
|
||||||
|
const ret = dbConnection.transaction(func).deferred();
|
||||||
|
|
||||||
if (!dbConnection.inTransaction) { // i.e. transaction was really committed (and not just savepoint released)
|
if (!dbConnection.inTransaction) { // i.e. transaction was really committed (and not just savepoint released)
|
||||||
require('./ws.js').sendTransactionSyncsToAllClients();
|
require('./ws.js').sendTransactionSyncsToAllClients();
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
catch (e) {
|
||||||
|
cls.clearEntityChanges();
|
||||||
|
|
||||||
return ret;
|
throw e;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function fillNoteIdList(noteIds, truncate = true) {
|
function fillNoteIdList(noteIds, truncate = true) {
|
||||||
|
@ -106,8 +106,6 @@ function sendPing(client, entityChanges = []) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const stats = require('./sync').stats;
|
|
||||||
|
|
||||||
sendMessage(client, {
|
sendMessage(client, {
|
||||||
type: 'sync',
|
type: 'sync',
|
||||||
data: entityChanges
|
data: entityChanges
|
||||||
@ -118,9 +116,7 @@ function sendTransactionSyncsToAllClients() {
|
|||||||
if (webSocketServer) {
|
if (webSocketServer) {
|
||||||
const entityChanges = cls.getAndClearEntityChanges();
|
const entityChanges = cls.getAndClearEntityChanges();
|
||||||
|
|
||||||
webSocketServer.clients.forEach(function each(client) {
|
webSocketServer.clients.forEach(client => sendPing(client, entityChanges));
|
||||||
sendPing(client, entityChanges);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -40,7 +40,6 @@
|
|||||||
<%- include('dialogs/backend_log.ejs') %>
|
<%- include('dialogs/backend_log.ejs') %>
|
||||||
<%- include('dialogs/include_note.ejs') %>
|
<%- include('dialogs/include_note.ejs') %>
|
||||||
<%- include('dialogs/sort_child_notes.ejs') %>
|
<%- include('dialogs/sort_child_notes.ejs') %>
|
||||||
<%- include('dialogs/delete_notes.ejs') %>
|
|
||||||
|
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
window.baseApiUrl = 'api/';
|
window.baseApiUrl = 'api/';
|
||||||
|
@ -1,23 +0,0 @@
|
|||||||
<div id="delete-notes-dialog" class="modal mx-auto" tabindex="-1" role="dialog">
|
|
||||||
<div class="modal-dialog modal-dialog-scrollable" role="document">
|
|
||||||
<div class="modal-content">
|
|
||||||
<div class="modal-header">
|
|
||||||
<h5 class="modal-title mr-auto">Delete notes</h5>
|
|
||||||
|
|
||||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
|
||||||
<span aria-hidden="true">×</span>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div class="modal-body">
|
|
||||||
... delete
|
|
||||||
</div>
|
|
||||||
<div class="modal-footer">
|
|
||||||
<button class="btn btn-sm" id="delete-notes-dialog-cancel-button">Cancel</button>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<button class="btn btn-primary btn-sm" id="delete-notes-dialog-ok-button">OK</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
Loading…
x
Reference in New Issue
Block a user