mirror of
https://github.com/zadam/trilium.git
synced 2025-06-05 09:28:45 +02:00
Merge branch 'master' into next
# Conflicts: # package-lock.json # package.json
This commit is contained in:
commit
20ae67f510
@ -42,13 +42,6 @@ CREATE TABLE IF NOT EXISTS "branches" (
|
||||
hash TEXT DEFAULT "" NOT NULL,
|
||||
PRIMARY KEY(`branchId`)
|
||||
);
|
||||
CREATE TABLE IF NOT EXISTS "recent_notes" (
|
||||
`branchId` TEXT NOT NULL PRIMARY KEY,
|
||||
`notePath` TEXT NOT NULL,
|
||||
hash TEXT DEFAULT "" NOT NULL,
|
||||
`utcDateCreated` TEXT NOT NULL,
|
||||
isDeleted INT
|
||||
);
|
||||
CREATE TABLE IF NOT EXISTS "event_log" (
|
||||
`eventId` TEXT NOT NULL PRIMARY KEY,
|
||||
`noteId` TEXT,
|
||||
@ -145,3 +138,11 @@ CREATE TABLE IF NOT EXISTS "note_contents" (
|
||||
`utcDateModified` TEXT NOT NULL,
|
||||
PRIMARY KEY(`noteId`)
|
||||
);
|
||||
CREATE TABLE recent_notes
|
||||
(
|
||||
noteId TEXT not null primary key,
|
||||
notePath TEXT not null,
|
||||
hash TEXT default "" not null,
|
||||
utcDateCreated TEXT not null,
|
||||
isDeleted INT
|
||||
);
|
||||
|
@ -311,7 +311,7 @@ function FrontendScriptApi(startNote, currentNote, originEntity = null, tabConte
|
||||
* @return {boolean} returns true if the original note is still loaded, false if user switched to another
|
||||
*/
|
||||
this.isNoteStillActive = () => {
|
||||
return this.originEntity.noteId === noteDetailService.getActiveNoteId();
|
||||
return this.originEntity.noteId === tabContext.noteId;
|
||||
};
|
||||
|
||||
/**
|
||||
|
12
src/app.js
12
src/app.js
@ -67,6 +67,18 @@ require('./routes/routes').register(app);
|
||||
|
||||
require('./routes/custom').register(app);
|
||||
|
||||
app.use((err, req, res, next) => {
|
||||
if (err.code !== 'EBADCSRFTOKEN') {
|
||||
return next(err);
|
||||
}
|
||||
|
||||
log.error(`Invalid CSRF token: ${req.headers['x-csrf-token']}, secret: ${req.cookies['_csrf']}`);
|
||||
|
||||
err = new Error('Invalid CSRF token');
|
||||
err.status = 403;
|
||||
next(err);
|
||||
});
|
||||
|
||||
// catch 404 and forward to error handler
|
||||
app.use((req, res, next) => {
|
||||
const err = new Error('Router not found for request ' + req.url);
|
||||
|
@ -160,8 +160,6 @@ entrypoints.registerEntrypoints();
|
||||
|
||||
noteTooltipService.setupGlobalTooltip();
|
||||
|
||||
bundle.executeStartupBundles();
|
||||
|
||||
linkService.init();
|
||||
|
||||
noteAutocompleteService.init();
|
||||
|
@ -29,7 +29,7 @@ async function executeStartupBundles() {
|
||||
}
|
||||
}
|
||||
|
||||
async function executeRelationBundles(note, relationName) {
|
||||
async function executeRelationBundles(note, relationName, tabContext) {
|
||||
note.bundleCache = note.bundleCache || {};
|
||||
|
||||
if (!note.bundleCache[relationName]) {
|
||||
@ -37,7 +37,7 @@ async function executeRelationBundles(note, relationName) {
|
||||
}
|
||||
|
||||
for (const bundle of note.bundleCache[relationName]) {
|
||||
await executeBundle(bundle, note);
|
||||
await executeBundle(bundle, note, tabContext);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -283,7 +283,7 @@ function FrontendScriptApi(startNote, currentNote, originEntity = null, tabConte
|
||||
* @return {boolean} returns true if the original note is still loaded, false if user switched to another
|
||||
*/
|
||||
this.isNoteStillActive = () => {
|
||||
return this.originEntity.noteId === noteDetailService.getActiveNoteId();
|
||||
return this.originEntity.noteId === tabContext.noteId;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -215,7 +215,7 @@ async function loadNoteDetailToContext(ctx, note, notePath) {
|
||||
|
||||
ctx.$scriptArea.empty();
|
||||
|
||||
await bundleService.executeRelationBundles(ctx.note, 'runOnNoteView');
|
||||
await bundleService.executeRelationBundles(ctx.note, 'runOnNoteView', ctx);
|
||||
|
||||
if (utils.isDesktop()) {
|
||||
await ctx.attributes.showAttributes();
|
||||
|
@ -553,7 +553,13 @@ class NoteDetailRelationMap {
|
||||
getZoom() {
|
||||
const matrixRegex = /matrix\((-?\d*\.?\d+),\s*0,\s*0,\s*-?\d*\.?\d+,\s*-?\d*\.?\d+,\s*-?\d*\.?\d+\)/;
|
||||
|
||||
const matches = this.$relationMapContainer.css('transform').match(matrixRegex);
|
||||
const transform = this.$relationMapContainer.css('transform');
|
||||
|
||||
const matches = transform.match(matrixRegex);
|
||||
|
||||
if (!matches) {
|
||||
throw new Error("Cannot match transform: " + transform);
|
||||
}
|
||||
|
||||
return matches[1];
|
||||
}
|
||||
|
@ -80,6 +80,8 @@ async function doSearch(searchText) {
|
||||
return;
|
||||
}
|
||||
|
||||
$searchBox.tooltip("hide");
|
||||
|
||||
const response = await server.get('search/' + encodeURIComponent(searchText));
|
||||
|
||||
if (!response.success) {
|
||||
|
@ -101,6 +101,8 @@ class TabContext {
|
||||
|
||||
this.setCurrentNotePathToHash();
|
||||
|
||||
this.setTitleBar();
|
||||
|
||||
setTimeout(async () => {
|
||||
// we include the note into recent list only if the user stayed on the note at least 5 seconds
|
||||
if (notePath && notePath === this.notePath) {
|
||||
@ -123,6 +125,13 @@ class TabContext {
|
||||
show() {
|
||||
this.$tabContent.show();
|
||||
this.setCurrentNotePathToHash();
|
||||
this.setTitleBar();
|
||||
}
|
||||
|
||||
setTitleBar() {
|
||||
if (!this.$tabContent.is(":visible")) {
|
||||
return;
|
||||
}
|
||||
|
||||
document.title = "Trilium Notes";
|
||||
|
||||
@ -226,7 +235,7 @@ class TabContext {
|
||||
this.$savedIndicator.fadeIn();
|
||||
|
||||
// run async
|
||||
bundleService.executeRelationBundles(this.note, 'runOnNoteChange');
|
||||
bundleService.executeRelationBundles(this.note, 'runOnNoteChange', this);
|
||||
}
|
||||
|
||||
async saveNoteIfChanged() {
|
||||
|
@ -1,6 +1,5 @@
|
||||
import contextMenuWidget from './context_menu.js';
|
||||
import dragAndDropSetup from './drag_and_drop.js';
|
||||
import linkService from './link.js';
|
||||
import messagingService from './messaging.js';
|
||||
import noteDetailService from './note_detail.js';
|
||||
import protectedSessionHolder from './protected_session_holder.js';
|
||||
@ -17,12 +16,16 @@ import hoistedNoteService from '../services/hoisted_note.js';
|
||||
import confirmDialog from "../dialogs/confirm.js";
|
||||
import optionsInit from "../services/options_init.js";
|
||||
import TreeContextMenu from "./tree_context_menu.js";
|
||||
import bundle from "./bundle.js";
|
||||
|
||||
const $tree = $("#tree");
|
||||
const $createTopLevelNoteButton = $("#create-top-level-note-button");
|
||||
const $collapseTreeButton = $("#collapse-tree-button");
|
||||
const $scrollToActiveNoteButton = $("#scroll-to-active-note-button");
|
||||
|
||||
let setFrontendAsLoaded;
|
||||
const frontendLoaded = new Promise(resolve => { setFrontendAsLoaded = resolve; });
|
||||
|
||||
// focused & not active node can happen during multiselection where the node is selected but not activated
|
||||
// (its content is not displayed in the detail)
|
||||
function getFocusedNode() {
|
||||
@ -362,6 +365,8 @@ async function treeInitialized() {
|
||||
// previous opening triggered task to save tab changes but these are bogus changes (this is init)
|
||||
// so we'll cancel it
|
||||
noteDetailService.clearOpenTabsTask();
|
||||
|
||||
setFrontendAsLoaded();
|
||||
}
|
||||
|
||||
let ignoreNextActivationNoteId = null;
|
||||
@ -398,24 +403,20 @@ function initFancyTree(tree) {
|
||||
}
|
||||
},
|
||||
beforeActivate: (event, data) => {
|
||||
// this is for the case when tree reload has been called and we don't want to
|
||||
if (ignoreNextActivationNoteId && getActiveNode() !== null) {
|
||||
// make sure the reload won't trigger reactivation.
|
||||
// This is important especially in cases where we wait for the reload to finish to then activate some other note.
|
||||
// But since the activate() event is called asynchronously, it may be called (or finished calling)
|
||||
// after we switched to a different note so it's not possible to just check if current note matches requested note
|
||||
if (ignoreNextActivationNoteId && getActiveNode() !== null && getActiveNode().data.noteId === data.node.data.noteId) {
|
||||
ignoreNextActivationNoteId = null;
|
||||
return false;
|
||||
}
|
||||
},
|
||||
activate: async (event, data) => {
|
||||
const node = data.node;
|
||||
const noteId = node.data.noteId;
|
||||
|
||||
if (ignoreNextActivationNoteId === noteId) {
|
||||
ignoreNextActivationNoteId = null;
|
||||
return;
|
||||
}
|
||||
|
||||
// click event won't propagate so let's close context menu manually
|
||||
contextMenuWidget.hideContextMenu();
|
||||
|
||||
const notePath = await treeUtils.getNotePath(node);
|
||||
const notePath = await treeUtils.getNotePath(data.node);
|
||||
|
||||
noteDetailService.switchToNote(notePath);
|
||||
},
|
||||
@ -489,9 +490,6 @@ function getTree() {
|
||||
async function reload() {
|
||||
const notes = await loadTree();
|
||||
|
||||
// make sure the reload won't trigger reactivation. This is important especially in cases where we wait for the reload
|
||||
// to finish to then activate some other note. But since the activate() event is called asynchronously, it may be called
|
||||
// (or finished calling) after we switched to a different note.
|
||||
if (getActiveNode()) {
|
||||
ignoreNextActivationNoteId = getActiveNode().data.noteId;
|
||||
}
|
||||
@ -816,6 +814,8 @@ $collapseTreeButton.click(() => collapseTree());
|
||||
$createTopLevelNoteButton.click(createNewTopLevelNote);
|
||||
$scrollToActiveNoteButton.click(scrollToActiveNote);
|
||||
|
||||
frontendLoaded.then(bundle.executeStartupBundles);
|
||||
|
||||
export default {
|
||||
reload,
|
||||
collapseTree,
|
||||
|
@ -5,14 +5,18 @@ const sql = require('../services/sql');
|
||||
const attributeService = require('../services/attributes');
|
||||
const config = require('../services/config');
|
||||
const optionService = require('../services/options');
|
||||
const log = require('../services/log');
|
||||
|
||||
async function index(req, res) {
|
||||
const options = await optionService.getOptionsMap();
|
||||
|
||||
const view = req.cookies['trilium-device'] === 'mobile' ? 'mobile' : 'desktop';
|
||||
|
||||
const csrfToken = req.csrfToken();
|
||||
log.info(`Generated CSRF token ${csrfToken} with secret ${res.getHeader('set-cookie')}`);
|
||||
|
||||
res.render(view, {
|
||||
csrfToken: req.csrfToken(),
|
||||
csrfToken: csrfToken,
|
||||
theme: options.theme,
|
||||
leftPaneMinWidth: parseInt(options.leftPaneMinWidth),
|
||||
leftPaneWidthPercent: parseInt(options.leftPaneWidthPercent),
|
||||
|
@ -16,7 +16,8 @@ async function anonymize() {
|
||||
|
||||
const db = await sqlite.open(anonymizedFile, {Promise});
|
||||
|
||||
await db.run("UPDATE notes SET title = 'title', content = 'text'");
|
||||
await db.run("UPDATE notes SET title = 'title'");
|
||||
await db.run("UPDATE note_contents SET content = 'text'");
|
||||
await db.run("UPDATE note_revisions SET title = 'title', content = 'text'");
|
||||
await db.run("UPDATE branches SET prefix = 'prefix' WHERE prefix IS NOT NULL");
|
||||
await db.run(`UPDATE options SET value = 'anonymized' WHERE name IN
|
||||
|
@ -1 +1 @@
|
||||
module.exports = { buildDate:"2019-05-22T22:28:35+02:00", buildRevision: "35648b9f3756294f91809ed6e5dbc3a30c7c9f13" };
|
||||
module.exports = { buildDate:"2019-05-29T23:14:59+02:00", buildRevision: "0a0cac5f41df0b867d0644fa392abb7a0ad4507a" };
|
||||
|
@ -6,7 +6,7 @@
|
||||
</head>
|
||||
<body class="desktop theme-<%= theme %>" style="--main-font-size: <%= mainFontSize %>%; --tree-font-size: <%= treeFontSize %>%; --detail-font-size: <%= detailFontSize %>%;">
|
||||
<noscript>Trilium requires JavaScript to be enabled.</noscript>
|
||||
<div id="container" style="display: none; grid-template-columns: minmax(<%= leftPaneMinWidth %>px, <%= leftPaneWidthPercent %>fr) <%= rightPaneWidthPercent %>fr">
|
||||
<div id="container" style="display: none; grid-template-columns: minmax(<%= leftPaneMinWidth %>px, <%= leftPaneWidthPercent %>fr) minmax(0, <%= rightPaneWidthPercent %>fr)">
|
||||
<div id="header" class="hide-toggle">
|
||||
<div id="history-navigation" style="display: none;">
|
||||
<a id="history-back-button" title="Go to previous note." class="icon-action jam jam-arrow-square-left"></a>
|
||||
|
@ -1,5 +1,5 @@
|
||||
<div class="note-detail-search note-detail-component">
|
||||
<div style="display: flex; align-items: center; margin-right: 20px;">
|
||||
<div style="display: flex; align-items: center; margin-right: 20px; margin-top: 15px;">
|
||||
<strong>Search string: </strong>
|
||||
<textarea rows="4" style="width: auto !important; flex-grow: 4" class="search-string form-control"></textarea>
|
||||
|
||||
|
@ -1,6 +1,5 @@
|
||||
|
||||
<div id="recent-changes-dialog" class="modal fade mx-auto" tabindex="-1" role="dialog">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-dialog modal-dialog-scrollable" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">Recent changes</h5>
|
||||
|
5
src/www
5
src/www
@ -8,6 +8,11 @@ process.on('unhandledRejection', error => {
|
||||
require('./services/log').info(error);
|
||||
});
|
||||
|
||||
process.on('SIGINT', function() {
|
||||
console.log("Caught interrupt signal. Exiting.");
|
||||
process.exit();
|
||||
});
|
||||
|
||||
const { app, sessionParser } = require('./app');
|
||||
const debug = require('debug')('node:server');
|
||||
const fs = require('fs');
|
||||
|
Loading…
x
Reference in New Issue
Block a user