diff --git a/db/schema.sql b/db/schema.sql index fd41ad1c3..2917ad59d 100644 --- a/db/schema.sql +++ b/db/schema.sql @@ -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 +); diff --git a/docs/frontend_api/services_frontend_script_api.js.html b/docs/frontend_api/services_frontend_script_api.js.html index 62ff12f08..d04bc8332 100644 --- a/docs/frontend_api/services_frontend_script_api.js.html +++ b/docs/frontend_api/services_frontend_script_api.js.html @@ -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; }; /** diff --git a/src/app.js b/src/app.js index 660de2320..e2625db00 100644 --- a/src/app.js +++ b/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); diff --git a/src/public/javascripts/desktop.js b/src/public/javascripts/desktop.js index 43d41ea72..f0f32956c 100644 --- a/src/public/javascripts/desktop.js +++ b/src/public/javascripts/desktop.js @@ -160,8 +160,6 @@ entrypoints.registerEntrypoints(); noteTooltipService.setupGlobalTooltip(); -bundle.executeStartupBundles(); - linkService.init(); noteAutocompleteService.init(); diff --git a/src/public/javascripts/services/bundle.js b/src/public/javascripts/services/bundle.js index bcfa0b807..13b01910d 100644 --- a/src/public/javascripts/services/bundle.js +++ b/src/public/javascripts/services/bundle.js @@ -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); } } diff --git a/src/public/javascripts/services/frontend_script_api.js b/src/public/javascripts/services/frontend_script_api.js index 281b9e8a4..aa72ad14c 100644 --- a/src/public/javascripts/services/frontend_script_api.js +++ b/src/public/javascripts/services/frontend_script_api.js @@ -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; }; /** diff --git a/src/public/javascripts/services/note_detail.js b/src/public/javascripts/services/note_detail.js index b7ee2ea0a..a20275a87 100644 --- a/src/public/javascripts/services/note_detail.js +++ b/src/public/javascripts/services/note_detail.js @@ -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(); diff --git a/src/public/javascripts/services/note_detail_relation_map.js b/src/public/javascripts/services/note_detail_relation_map.js index 9a36a2210..3220466be 100644 --- a/src/public/javascripts/services/note_detail_relation_map.js +++ b/src/public/javascripts/services/note_detail_relation_map.js @@ -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]; } diff --git a/src/public/javascripts/services/search_notes.js b/src/public/javascripts/services/search_notes.js index a9817a9c4..e37df3818 100644 --- a/src/public/javascripts/services/search_notes.js +++ b/src/public/javascripts/services/search_notes.js @@ -80,6 +80,8 @@ async function doSearch(searchText) { return; } + $searchBox.tooltip("hide"); + const response = await server.get('search/' + encodeURIComponent(searchText)); if (!response.success) { diff --git a/src/public/javascripts/services/tab_context.js b/src/public/javascripts/services/tab_context.js index 51ee4cf1f..9165ff012 100644 --- a/src/public/javascripts/services/tab_context.js +++ b/src/public/javascripts/services/tab_context.js @@ -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() { diff --git a/src/public/javascripts/services/tree.js b/src/public/javascripts/services/tree.js index 58efee20b..8f99862c2 100644 --- a/src/public/javascripts/services/tree.js +++ b/src/public/javascripts/services/tree.js @@ -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, diff --git a/src/routes/index.js b/src/routes/index.js index 30cb87895..d41a3dc74 100644 --- a/src/routes/index.js +++ b/src/routes/index.js @@ -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), diff --git a/src/services/anonymization.js b/src/services/anonymization.js index 5df28b6b5..e5c356eb5 100644 --- a/src/services/anonymization.js +++ b/src/services/anonymization.js @@ -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 diff --git a/src/services/build.js b/src/services/build.js index de7fc5619..ef61e12f2 100644 --- a/src/services/build.js +++ b/src/services/build.js @@ -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" }; diff --git a/src/views/desktop.ejs b/src/views/desktop.ejs index c31c07272..ba9407270 100644 --- a/src/views/desktop.ejs +++ b/src/views/desktop.ejs @@ -6,7 +6,7 @@
-