Merge branch 'master' into next

# Conflicts:
#	package-lock.json
#	package.json
This commit is contained in:
zadam 2019-05-31 18:30:16 +02:00
commit 20ae67f510
18 changed files with 75 additions and 38 deletions

View File

@ -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
);

View File

@ -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;
};
/**

View File

@ -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);

View File

@ -160,8 +160,6 @@ entrypoints.registerEntrypoints();
noteTooltipService.setupGlobalTooltip();
bundle.executeStartupBundles();
linkService.init();
noteAutocompleteService.init();

View File

@ -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);
}
}

View File

@ -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;
};
/**

View File

@ -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();

View File

@ -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];
}

View File

@ -80,6 +80,8 @@ async function doSearch(searchText) {
return;
}
$searchBox.tooltip("hide");
const response = await server.get('search/' + encodeURIComponent(searchText));
if (!response.success) {

View File

@ -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() {

View File

@ -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,

View File

@ -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),

View File

@ -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

View File

@ -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" };

View File

@ -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>

View File

@ -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: &nbsp; &nbsp;</strong>
<textarea rows="4" style="width: auto !important; flex-grow: 4" class="search-string form-control"></textarea>

View File

@ -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>

View File

@ -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');