mirror of
				https://github.com/zadam/trilium.git
				synced 2025-10-31 18:36:30 +01:00 
			
		
		
		
	Compare commits
	
		
			20 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | fb3d5f25ac | ||
|  | 9d7d79ef94 | ||
|  | ba33a0d330 | ||
|  | aea81c9872 | ||
|  | 806ab22fa8 | ||
|  | 9c2b98915e | ||
|  | f2ca9276d6 | ||
|  | 48b697f408 | ||
|  | e1e185e5db | ||
|  | e06c5703ee | ||
|  | fe3bb2c5f6 | ||
|  | 6afc299efb | ||
|  | 369274ead7 | ||
|  | 04e6431c09 | ||
|  | e89057a771 | ||
|  | 4f27254e64 | ||
|  | 577dc95ab8 | ||
|  | a266d6a3d5 | ||
|  | 749b6cb57e | ||
|  | b0b2951ff6 | 
| @@ -5,6 +5,9 @@ instanceName= | ||||
| # set to true to allow using Trilium without authentication (makes sense for server build only, desktop build doesn't need password) | ||||
| noAuthentication=false | ||||
|  | ||||
| # set to true to disable backups (e.g. because of limited space on server) | ||||
| noBackup=false | ||||
|  | ||||
| # Disable automatically generating desktop icon | ||||
| # noDesktopIcon=true | ||||
|  | ||||
|   | ||||
| @@ -2,7 +2,7 @@ | ||||
|   "name": "trilium", | ||||
|   "productName": "Trilium Notes", | ||||
|   "description": "Trilium Notes", | ||||
|   "version": "0.45.7", | ||||
|   "version": "0.45.9", | ||||
|   "license": "AGPL-3.0-only", | ||||
|   "main": "electron.js", | ||||
|   "bin": { | ||||
|   | ||||
| @@ -234,6 +234,8 @@ export default class AttributeDetailWidget extends TabAwareWidget { | ||||
|  | ||||
|         this.$inputName = this.$widget.find('.attr-input-name'); | ||||
|         this.$inputName.on('keyup', () => this.userEditedAttribute()); | ||||
|         this.$inputName.on('change', () => this.userEditedAttribute()); | ||||
|         this.$inputName.on('autocomplete:closed', () => this.userEditedAttribute()); | ||||
|  | ||||
|         this.$inputName.on('focus', () => { | ||||
|             attributeAutocompleteService.initAttributeNameAutocomplete({ | ||||
|   | ||||
| @@ -59,6 +59,7 @@ export default class NotePathsWidget extends TabAwareWidget { | ||||
|  | ||||
|         this.$currentPath = this.$widget.find('.current-path'); | ||||
|         this.$dropdown = this.$widget.find(".dropdown"); | ||||
|         this.$dropdownToggle = this.$widget.find('.dropdown-toggle'); | ||||
|  | ||||
|         this.$notePathList = this.$dropdown.find(".note-path-list"); | ||||
|  | ||||
| @@ -100,6 +101,8 @@ export default class NotePathsWidget extends TabAwareWidget { | ||||
|  | ||||
|             parentNoteId = noteId; | ||||
|         } | ||||
|  | ||||
|         this.$dropdownToggle.dropdown('hide'); | ||||
|     } | ||||
|  | ||||
|     async renderDropdown() { | ||||
| @@ -141,20 +144,20 @@ export default class NotePathsWidget extends TabAwareWidget { | ||||
|     async addPath(notePath, isCurrent) { | ||||
|         const title = await treeService.getNotePathTitle(notePath); | ||||
|  | ||||
|         const noteLink = await linkService.createNoteLink(notePath, {title}); | ||||
|         const $noteLink = await linkService.createNoteLink(notePath, {title}); | ||||
|  | ||||
|         noteLink | ||||
|         $noteLink | ||||
|             .addClass("dropdown-item"); | ||||
|  | ||||
|         noteLink | ||||
|         $noteLink | ||||
|             .find('a') | ||||
|             .addClass("no-tooltip-preview"); | ||||
|  | ||||
|         if (isCurrent) { | ||||
|             noteLink.addClass("current"); | ||||
|             $noteLink.addClass("current"); | ||||
|         } | ||||
|  | ||||
|         this.$notePathList.append(noteLink); | ||||
|         this.$notePathList.append($noteLink); | ||||
|     } | ||||
|  | ||||
|     entitiesReloadedEvent({loadResults}) { | ||||
|   | ||||
| @@ -1198,7 +1198,25 @@ export default class NoteTreeWidget extends TabAwareWidget { | ||||
|         this.clearSelectedNodes(); | ||||
|     } | ||||
|  | ||||
|     canBeMovedUpOrDown(node) { | ||||
|         if (node.data.noteId === 'root') { | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         const parentNote = treeCache.getNoteFromCache(node.getParent().data.noteId); | ||||
|  | ||||
|         if (parentNote && parentNote.hasLabel('sorted')) { | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     moveNoteUpCommand({node}) { | ||||
|         if (!this.canBeMovedUpOrDown(node)) { | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         const beforeNode = node.getPrevSibling(); | ||||
|  | ||||
|         if (beforeNode !== null) { | ||||
| @@ -1207,7 +1225,12 @@ export default class NoteTreeWidget extends TabAwareWidget { | ||||
|     } | ||||
|  | ||||
|     moveNoteDownCommand({node}) { | ||||
|         if (!this.canBeMovedUpOrDown(node)) { | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         const afterNode = node.getNextSibling(); | ||||
|  | ||||
|         if (afterNode !== null) { | ||||
|             branchService.moveAfterBranch([node.data.branchId], afterNode.data.branchId); | ||||
|         } | ||||
|   | ||||
| @@ -41,6 +41,9 @@ export default class NoteTypeWidget extends TabAwareWidget { | ||||
|         this.$noteTypeDropdown = this.$widget.find(".note-type-dropdown"); | ||||
|         this.$noteTypeButton = this.$widget.find(".note-type-button"); | ||||
|         this.$noteTypeDesc = this.$widget.find(".note-type-desc"); | ||||
|  | ||||
|         this.$widget.on('click', '.dropdown-item', | ||||
|             () => this.$widget.find('.dropdown-toggle').dropdown('toggle')); | ||||
|     } | ||||
|  | ||||
|     async refreshWithNote(note) { | ||||
| @@ -64,8 +67,6 @@ export default class NoteTypeWidget extends TabAwareWidget { | ||||
|                     const noteType = NOTE_TYPES.find(nt => nt.type === type); | ||||
|  | ||||
|                     this.save(noteType.type, noteType.mime); | ||||
|  | ||||
|                     this.$widget.find('.dropdown-toggle').dropdown('toggle'); | ||||
|                 }); | ||||
|  | ||||
|             if (this.note.type === noteType.type) { | ||||
|   | ||||
| @@ -38,7 +38,7 @@ const TPL = ` | ||||
|         cursor: text !important; | ||||
|     } | ||||
|      | ||||
|     .note-detail-editable-text *:first-child { | ||||
|     .note-detail-editable-text *:not(figure):first-child { | ||||
|         margin-top: 0 !important; | ||||
|     } | ||||
|      | ||||
|   | ||||
| @@ -67,6 +67,11 @@ export default class ReadOnlyTextTypeWidget extends AbstractTextTypeWidget { | ||||
|     } | ||||
|  | ||||
|     async doRefresh(note) { | ||||
|         // we load CKEditor also for read only notes because they contain content styles required for correct rendering of even read only notes | ||||
|         // we could load just ckeditor-content.css but that causes CSS conflicts when both build CSS and this content CSS is loaded at the same time | ||||
|         // (see https://github.com/zadam/trilium/issues/1590 for example of such conflict) | ||||
|         await libraryLoader.requireLibrary(libraryLoader.CKEDITOR); | ||||
|  | ||||
|         const noteComplement = await treeCache.getNoteComplement(note.noteId); | ||||
|  | ||||
|         this.$content.html(noteComplement.content); | ||||
|   | ||||
| @@ -703,6 +703,7 @@ a.external:not(.no-arrow):after, a[href^="http://"]:not(.no-arrow):after, a[href | ||||
|     padding: 10px; | ||||
|     border-radius: 10px; | ||||
|     background-color: var(--accented-background-color); | ||||
|     clear: both; | ||||
| } | ||||
|  | ||||
| .include-note.ck-placeholder::before { /* remove placeholder in otherwise empty note */ | ||||
|   | ||||
| @@ -23,11 +23,7 @@ function exportBranch(req, res) { | ||||
|  | ||||
|     try { | ||||
|         if (type === 'subtree' && (format === 'html' || format === 'markdown')) { | ||||
|             const start = Date.now(); | ||||
|  | ||||
|             zipExportService.exportToZip(taskContext, branch, format, res); | ||||
|  | ||||
|             console.log("Export took", Date.now() - start, "ms"); | ||||
|         } | ||||
|         else if (type === 'single') { | ||||
|             singleExportService.exportSingleNote(taskContext, branch, format, res); | ||||
|   | ||||
| @@ -54,11 +54,21 @@ function getBundlesWithLabel(label, value) { | ||||
| } | ||||
|  | ||||
| function getStartupBundles() { | ||||
|     if (!process.env.TRILIUM_SAFE_MODE) { | ||||
|         return getBundlesWithLabel("run", "frontendStartup"); | ||||
|     } | ||||
|     else { | ||||
|         return []; | ||||
|     } | ||||
| } | ||||
|  | ||||
| function getWidgetBundles() { | ||||
|     if (!process.env.TRILIUM_SAFE_MODE) { | ||||
|         return getBundlesWithLabel("widget"); | ||||
|     } | ||||
|     else { | ||||
|         return []; | ||||
|     } | ||||
| } | ||||
|  | ||||
| function getRelationBundles(req) { | ||||
|   | ||||
| @@ -1 +1 @@ | ||||
| module.exports = { buildDate:"2020-12-22T20:21:15+01:00", buildRevision: "bdfd760b9d428dc29c45a0e016d12a25af220043" }; | ||||
| module.exports = { buildDate:"2021-02-05T21:38:32+01:00", buildRevision: "9d7d79ef94bc7734393ae7f89993e76bbc7d97e3" }; | ||||
|   | ||||
| @@ -143,7 +143,7 @@ function exportToZip(taskContext, branch, format, res) { | ||||
|         const available = !note.isProtected || protectedSessionService.isProtectedSessionAvailable(); | ||||
|  | ||||
|         // if it's a leaf then we'll export it even if it's empty | ||||
|         if (available && ((note.getContent()).length > 0 || childBranches.length === 0)) { | ||||
|         if (available && (note.getContent().length > 0 || childBranches.length === 0)) { | ||||
|             meta.dataFileName = getDataFileName(note, baseFileName, existingFileNames); | ||||
|         } | ||||
|  | ||||
| @@ -234,7 +234,7 @@ function exportToZip(taskContext, branch, format, res) { | ||||
|     <link rel="stylesheet" href="${cssUrl}"> | ||||
|     <base target="_parent"> | ||||
| </head> | ||||
| <body> | ||||
| <body class="ck-content"> | ||||
|   <h1>${utils.escapeHtml(title)}</h1> | ||||
| ${content} | ||||
| </body> | ||||
| @@ -433,14 +433,13 @@ ${content} | ||||
|     } | ||||
|  | ||||
|     const note = branch.getNote(); | ||||
|     const zipFileName = (branch.prefix ? (branch.prefix + " - ") : "") + note.title + ".zip"; | ||||
|     const zipFileName = (branch.prefix ? `${branch.prefix} - ` : "") + note.title + ".zip"; | ||||
|  | ||||
|     res.setHeader('Content-Disposition', utils.getContentDisposition(zipFileName)); | ||||
|     res.setHeader('Content-Type', 'application/zip'); | ||||
|  | ||||
|     zipFile.end(); | ||||
|  | ||||
|     zipFile.outputStream.pipe(res); | ||||
|     zipFile.end(); | ||||
|  | ||||
|     taskContext.taskSucceeded(); | ||||
| } | ||||
|   | ||||
| @@ -3,6 +3,7 @@ const scriptService = require('./script'); | ||||
| const treeService = require('./tree'); | ||||
| const noteService = require('./notes'); | ||||
| const repository = require('./repository'); | ||||
| const noteCache = require('./note_cache/note_cache'); | ||||
| const Attribute = require('../entities/attribute'); | ||||
|  | ||||
| function runAttachedRelations(note, relationName, originEntity) { | ||||
| @@ -22,11 +23,15 @@ eventService.subscribe(eventService.NOTE_TITLE_CHANGED, note => { | ||||
|     runAttachedRelations(note, 'runOnNoteTitleChange', note); | ||||
|  | ||||
|     if (!note.isRoot()) { | ||||
|         const parents = note.getParentNotes(); | ||||
|         const noteFromCache = noteCache.notes[note.noteId]; | ||||
|  | ||||
|         for (const parent of parents) { | ||||
|             if (parent.hasOwnedLabel("sorted")) { | ||||
|                 treeService.sortNotesAlphabetically(parent.noteId); | ||||
|         if (!noteFromCache) { | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         for (const parentNote of noteFromCache.parents) { | ||||
|             if (parentNote.hasLabel("sorted")) { | ||||
|                 treeService.sortNotesAlphabetically(parentNote.noteId); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| @@ -80,6 +85,16 @@ eventService.subscribe(eventService.ENTITY_CREATED, ({ entityName, entity }) => | ||||
|         } | ||||
|         else if (entity.type === 'label' && entity.name === 'sorted') { | ||||
|             treeService.sortNotesAlphabetically(entity.noteId); | ||||
|  | ||||
|             if (entity.isInheritable) { | ||||
|                 const note = noteCache.notes[entity.noteId]; | ||||
|  | ||||
|                 if (note) { | ||||
|                     for (const noteId of note.subtreeNoteIds) { | ||||
|                         treeService.sortNotesAlphabetically(noteId); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     else if (entityName === 'notes') { | ||||
|   | ||||
| @@ -133,6 +133,14 @@ class Note { | ||||
|         return !!this.attributes.find(attr => attr.type === type && attr.name === name); | ||||
|     } | ||||
|  | ||||
|     hasLabel(name) { | ||||
|         return this.hasAttribute('label', name); | ||||
|     } | ||||
|  | ||||
|     hasRelation(name) { | ||||
|         return this.hasAttribute('relation', name); | ||||
|     } | ||||
|  | ||||
|     getLabelValue(name) { | ||||
|         const label = this.attributes.find(attr => attr.type === 'label' && attr.name === name); | ||||
|  | ||||
| @@ -275,6 +283,11 @@ class Note { | ||||
|         return arr.flat(); | ||||
|     } | ||||
|  | ||||
|     /** @return {String[]} */ | ||||
|     get subtreeNoteIds() { | ||||
|         return this.subtreeNotes.map(note => note.noteId); | ||||
|     } | ||||
|  | ||||
|     get parentCount() { | ||||
|         return this.parents.length; | ||||
|     } | ||||
|   | ||||
| @@ -1,9 +1,5 @@ | ||||
| "use strict"; | ||||
|  | ||||
| const Note = require('./entities/note'); | ||||
| const Branch = require('./entities/branch'); | ||||
| const Attribute = require('./entities/attribute'); | ||||
|  | ||||
| class NoteCache { | ||||
|     constructor() { | ||||
|         this.reset(); | ||||
|   | ||||
| @@ -31,10 +31,7 @@ function initNotSyncedOptions(initialized, startNotePath = 'root', opts = {}) { | ||||
|     optionService.createOption('openTabs', JSON.stringify([ | ||||
|         { | ||||
|             notePath: startNotePath, | ||||
|             active: true, | ||||
|             sidebar: { | ||||
|                 widgets: [] | ||||
|             } | ||||
|             active: true | ||||
|         } | ||||
|     ]), false); | ||||
|  | ||||
| @@ -103,6 +100,15 @@ function initStartupOptions() { | ||||
|             log.info(`Created missing option "${name}" with default value "${value}"`); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     if (process.env.TRILIUM_START_NOTE_ID || process.env.TRILIUM_SAFE_MODE) { | ||||
|         optionService.setOption('openTabs', JSON.stringify([ | ||||
|             { | ||||
|                 notePath: process.env.TRILIUM_START_NOTE_ID || 'root', | ||||
|                 active: true | ||||
|             } | ||||
|         ])); | ||||
|     } | ||||
| } | ||||
|  | ||||
| function getKeyboardDefaultOptions() { | ||||
|   | ||||
| @@ -22,9 +22,11 @@ function runNotesWithLabel(runAttrValue) { | ||||
| } | ||||
|  | ||||
| sqlInit.dbReady.then(() => { | ||||
|     if (!process.env.TRILIUM_SAFE_MODE) { | ||||
|         setTimeout(cls.wrap(() => runNotesWithLabel('backendStartup')), 10 * 1000); | ||||
|  | ||||
|         setInterval(cls.wrap(() => runNotesWithLabel('hourly')), 3600 * 1000); | ||||
|  | ||||
|         setInterval(cls.wrap(() => runNotesWithLabel('daily')), 24 * 3600 * 1000); | ||||
|     } | ||||
| }); | ||||
|   | ||||
| @@ -1,4 +1,3 @@ | ||||
| const sql = require('./sql'); | ||||
| const ScriptContext = require('./script_context'); | ||||
| const repository = require('./repository'); | ||||
| const cls = require('./cls'); | ||||
|   | ||||
| @@ -32,26 +32,29 @@ class NoteContentProtectedFulltextExp extends Expression { | ||||
|                 FROM notes JOIN note_contents USING (noteId)  | ||||
|                 WHERE type IN ('text', 'code') AND isDeleted = 0 AND isProtected = 1`)) { | ||||
|  | ||||
|             if (!inputNoteSet.hasNoteId(noteId) || !(noteId in noteCache.notes)) { | ||||
|                 continue; | ||||
|             } | ||||
|  | ||||
|             try { | ||||
|                 content = protectedSessionService.decryptString(content); | ||||
|             } | ||||
|             catch (e) { | ||||
|                 log.info('Cannot decrypt content of note', noteId); | ||||
|                 log.info(`Cannot decrypt content of note ${noteId}`); | ||||
|                 continue; | ||||
|             } | ||||
|  | ||||
|             content = content.toLowerCase(); | ||||
|  | ||||
|             if (type === 'text' && mime === 'text/html') { | ||||
|                 if (content.length < 20000) { // striptags is slow for very large notes | ||||
|                     content = striptags(content); | ||||
|                 } | ||||
|  | ||||
|                 content = content.replace(/ /g, ' '); | ||||
|             } | ||||
|  | ||||
|             if (this.tokens.find(token => !content.includes(token))) { | ||||
|                 continue; | ||||
|             } | ||||
|  | ||||
|             if (inputNoteSet.hasNoteId(noteId) && noteId in noteCache.notes) { | ||||
|             if (!this.tokens.find(token => !content.includes(token))) { | ||||
|                 resultNoteSet.add(noteCache.notes[noteId]); | ||||
|             } | ||||
|         } | ||||
|   | ||||
| @@ -26,18 +26,21 @@ class NoteContentUnprotectedFulltextExp extends Expression { | ||||
|                 FROM notes JOIN note_contents USING (noteId)  | ||||
|                 WHERE type IN ('text', 'code') AND isDeleted = 0 AND isProtected = 0`)) { | ||||
|  | ||||
|             content = content.toString().toLowerCase(); | ||||
|  | ||||
|             if (type === 'text' && mime === 'text/html') { | ||||
|                 content = striptags(content); | ||||
|                 content = content.replace(/ /g, ' '); | ||||
|             } | ||||
|  | ||||
|             if (this.tokens.find(token => !content.includes(token))) { | ||||
|             if (!inputNoteSet.hasNoteId(noteId) || !(noteId in noteCache.notes)) { | ||||
|                 continue; | ||||
|             } | ||||
|  | ||||
|             if (inputNoteSet.hasNoteId(noteId) && noteId in noteCache.notes) { | ||||
|             content = content.toString().toLowerCase(); | ||||
|  | ||||
|             if (type === 'text' && mime === 'text/html') { | ||||
|                 if (content.length < 20000) { // striptags is slow for very large notes | ||||
|                     content = striptags(content); | ||||
|                 } | ||||
|  | ||||
|                 content = content.replace(/ /g, ' '); | ||||
|             } | ||||
|  | ||||
|             if (!this.tokens.find(token => !content.includes(token))) { | ||||
|                 resultNoteSet.add(noteCache.notes[noteId]); | ||||
|             } | ||||
|         } | ||||
|   | ||||
| @@ -9,6 +9,7 @@ const Option = require('../entities/option'); | ||||
| const TaskContext = require('./task_context.js'); | ||||
| const migrationService = require('./migration'); | ||||
| const cls = require('./cls'); | ||||
| const config = require('./config'); | ||||
|  | ||||
| const dbReady = utils.deferred(); | ||||
|  | ||||
| @@ -131,6 +132,12 @@ function setDbAsInitialized() { | ||||
| } | ||||
|  | ||||
| dbReady.then(() => { | ||||
|     if (config.General && config.General.noBackup === true) { | ||||
|         log.info("Disabling scheduled backups."); | ||||
|  | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     setInterval(() => require('./backup').regularBackup(), 4 * 60 * 60 * 1000); | ||||
|  | ||||
|     // kickoff first backup soon after start up | ||||
|   | ||||
| @@ -63,8 +63,6 @@ | ||||
| <link href="libraries/bootstrap/css/bootstrap.min.css" rel="stylesheet"> | ||||
| <script src="libraries/bootstrap/js/bootstrap.bundle.min.js"></script> | ||||
|  | ||||
| <link href="libraries/ckeditor/ckeditor-content.css" rel="stylesheet"> | ||||
|  | ||||
| <!-- Include Fancytree skin and library --> | ||||
| <link href="libraries/fancytree/skin-win8/ui.fancytree.css" rel="stylesheet"> | ||||
| <script src="libraries/fancytree/jquery.fancytree-all-deps.min.js"></script> | ||||
|   | ||||
| @@ -127,8 +127,6 @@ | ||||
| <link href="libraries/bootstrap/css/bootstrap.min.css" rel="stylesheet"> | ||||
| <script src="libraries/bootstrap/js/bootstrap.bundle.min.js"></script> | ||||
|  | ||||
| <link href="libraries/ckeditor/ckeditor-content.css" rel="stylesheet"> | ||||
|  | ||||
| <script src="app/mobile.js" crossorigin type="module"></script> | ||||
|  | ||||
| <link href="stylesheets/themes.css" rel="stylesheet"> | ||||
|   | ||||
		Reference in New Issue
	
	Block a user