mirror of
				https://github.com/zadam/trilium.git
				synced 2025-10-31 02:16:05 +01:00 
			
		
		
		
	Compare commits
	
		
			27 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | 2b32addade | ||
|  | 0b251530fa | ||
|  | f5b933149a | ||
|  | 48bbfb8bdb | ||
|  | 889971c4d6 | ||
|  | 0722494d41 | ||
|  | 4b977a3306 | ||
|  | 3ff3021acd | ||
|  | 99e56a9c42 | ||
|  | 77279dfe16 | ||
|  | 93f8050454 | ||
|  | 31cfede7a7 | ||
|  | c8ec86e537 | ||
|  | 05aee884b6 | ||
|  | 012ba9e060 | ||
|  | 8e8fd88857 | ||
|  | 523ccdad6b | ||
|  | ded3f605be | ||
|  | 030d12a465 | ||
|  | 4d15628840 | ||
|  | 81b849898c | ||
|  | 3824486b85 | ||
|  | 081ab00a0a | ||
|  | 04f6af5c9a | ||
|  | 4dc1f1f6eb | ||
|  | 3930a02123 | ||
|  | 3112de105e | 
							
								
								
									
										7
									
								
								.gitpod.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								.gitpod.yml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,7 @@ | ||||
| tasks: | ||||
|     - before: nvm install 10 && nvm use 10 | ||||
|       init: npm install | ||||
|       command: npm run start | ||||
| ports: | ||||
|     - port: 8080 | ||||
|       onOpen: open-preview | ||||
| @@ -17,6 +17,7 @@ RUN set -x \ | ||||
|         libtool \ | ||||
|         make \ | ||||
|         nasm \ | ||||
|         libpng-dev \ | ||||
|     && npm install --production \ | ||||
|     && apk del .build-dependencies | ||||
|  | ||||
|   | ||||
							
								
								
									
										12
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										12
									
								
								README.md
									
									
									
									
									
								
							| @@ -35,3 +35,15 @@ Trilium is provided as either desktop application (Linux, Windows, Mac) or web a | ||||
| [See wiki for complete list of documentation pages.](https://github.com/zadam/trilium/wiki/) | ||||
|  | ||||
| You can also read [Patterns of personal knowledge base](https://github.com/zadam/trilium/wiki/Patterns-of-personal-knowledge-base) to get some inspiration on how you might use Trilium. | ||||
|  | ||||
| ## Contribute | ||||
|  | ||||
| Use a browser based dev environment | ||||
|  | ||||
| [](https://gitpod.io/#https://github.com/zadam/trilium) | ||||
|  | ||||
| Or clone locally and run | ||||
| ``` | ||||
| npm install | ||||
| npm run start | ||||
| ``` | ||||
| @@ -2,7 +2,7 @@ | ||||
|   "name": "trilium", | ||||
|   "productName": "Trilium Notes", | ||||
|   "description": "Trilium Notes", | ||||
|   "version": "0.27.3", | ||||
|   "version": "0.27.4", | ||||
|   "license": "AGPL-3.0-only", | ||||
|   "main": "electron.js", | ||||
|   "bin": { | ||||
| @@ -33,6 +33,7 @@ | ||||
|     "electron-in-page-search": "1.3.2", | ||||
|     "express": "4.16.4", | ||||
|     "express-session": "1.15.6", | ||||
|     "file-type": "10.7.0", | ||||
|     "fs-extra": "7.0.1", | ||||
|     "get-port": "4.1.0", | ||||
|     "helmet": "3.15.0", | ||||
|   | ||||
| @@ -56,6 +56,9 @@ class Note extends Entity { | ||||
|     setContent(content) { | ||||
|         this.content = content; | ||||
|  | ||||
|         // if parsing below is not successful then there's no jsonContent as opposed to still having the old unupdated ones | ||||
|         delete this.jsonContent; | ||||
|  | ||||
|         try { | ||||
|             this.jsonContent = JSON.parse(this.content); | ||||
|         } | ||||
|   | ||||
| @@ -36,6 +36,7 @@ import hoistedNoteService from './services/hoisted_note.js'; | ||||
| import noteTypeService from './services/note_type.js'; | ||||
| import linkService from './services/link.js'; | ||||
| import noteAutocompleteService from './services/note_autocomplete.js'; | ||||
| import macInit from './services/mac_init.js'; | ||||
|  | ||||
| // required for CKEditor image upload plugin | ||||
| window.glob.getCurrentNode = treeService.getCurrentNode; | ||||
| @@ -110,28 +111,6 @@ if (utils.isElectron()) { | ||||
|     }); | ||||
| } | ||||
|  | ||||
| function exec(cmd) { | ||||
|     document.execCommand(cmd); | ||||
|  | ||||
|     return false; | ||||
| } | ||||
|  | ||||
| if (utils.isElectron() && utils.isMac()) { | ||||
|     utils.bindShortcut('ctrl+c', () => exec("copy")); | ||||
|     utils.bindShortcut('ctrl+v', () => exec('paste')); | ||||
|     utils.bindShortcut('ctrl+x', () => exec('cut')); | ||||
|     utils.bindShortcut('ctrl+a', () => exec('selectAll')); | ||||
|     utils.bindShortcut('ctrl+z', () => exec('undo')); | ||||
|     utils.bindShortcut('ctrl+y', () => exec('redo')); | ||||
|  | ||||
|     utils.bindShortcut('meta+c', () => exec("copy")); | ||||
|     utils.bindShortcut('meta+v', () => exec('paste')); | ||||
|     utils.bindShortcut('meta+x', () => exec('cut')); | ||||
|     utils.bindShortcut('meta+a', () => exec('selectAll')); | ||||
|     utils.bindShortcut('meta+z', () => exec('undo')); | ||||
|     utils.bindShortcut('meta+y', () => exec('redo')); | ||||
| } | ||||
|  | ||||
| $("#export-note-button").click(function () { | ||||
|     if ($(this).hasClass("disabled")) { | ||||
|         return; | ||||
| @@ -140,6 +119,8 @@ $("#export-note-button").click(function () { | ||||
|     exportDialog.showDialog('single'); | ||||
| }); | ||||
|  | ||||
| macInit.init(); | ||||
|  | ||||
| treeService.showTree(); | ||||
|  | ||||
| entrypoints.registerEntrypoints(); | ||||
|   | ||||
| @@ -6,8 +6,6 @@ const $dialog = $("#jump-to-note-dialog"); | ||||
| const $autoComplete = $("#jump-to-note-autocomplete"); | ||||
| const $showInFullTextButton = $("#show-in-full-text-button"); | ||||
|  | ||||
| $dialog.on("shown.bs.modal", e => $autoComplete.focus()); | ||||
|  | ||||
| async function showDialog() { | ||||
|     glob.activeDialog = $dialog; | ||||
|  | ||||
|   | ||||
| @@ -117,10 +117,6 @@ function registerEntrypoints() { | ||||
|  | ||||
|     utils.bindShortcut('ctrl+f', openInPageSearch); | ||||
|  | ||||
|     if (utils.isMac()) { | ||||
|         utils.bindShortcut('meta+f', openInPageSearch); | ||||
|     } | ||||
|  | ||||
|     // FIXME: do we really need these at this point? | ||||
|     utils.bindShortcut("ctrl+shift+up", () => { | ||||
|         const node = treeService.getCurrentNode(); | ||||
|   | ||||
							
								
								
									
										25
									
								
								src/public/javascripts/services/mac_init.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								src/public/javascripts/services/mac_init.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,25 @@ | ||||
| /** | ||||
|  * Mac specific initialization | ||||
|  */ | ||||
| import utils from "./utils.js"; | ||||
|  | ||||
| function init() { | ||||
|     if (utils.isElectron() && utils.isMac()) { | ||||
|         utils.bindShortcut('meta+c', () => exec("copy")); | ||||
|         utils.bindShortcut('meta+v', () => exec('paste')); | ||||
|         utils.bindShortcut('meta+x', () => exec('cut')); | ||||
|         utils.bindShortcut('meta+a', () => exec('selectAll')); | ||||
|         utils.bindShortcut('meta+z', () => exec('undo')); | ||||
|         utils.bindShortcut('meta+y', () => exec('redo')); | ||||
|     } | ||||
| } | ||||
|  | ||||
| function exec(cmd) { | ||||
|     document.execCommand(cmd); | ||||
|  | ||||
|     return false; | ||||
| } | ||||
|  | ||||
| export default { | ||||
|     init | ||||
| } | ||||
| @@ -28,7 +28,7 @@ function clearText($el) { | ||||
| function showRecentNotes($el) { | ||||
|     $el.setSelectedPath(""); | ||||
|     $el.autocomplete("val", ""); | ||||
|     $el.autocomplete("open"); | ||||
|     $el.focus(); | ||||
| } | ||||
|  | ||||
| function initNoteAutocomplete($el, options) { | ||||
| @@ -61,7 +61,13 @@ function initNoteAutocomplete($el, options) { | ||||
|  | ||||
|         $clearTextButton.click(() => clearText($el)); | ||||
|  | ||||
|         $showRecentNotesButton.click(() => showRecentNotes($el)); | ||||
|         $showRecentNotesButton.click(e => { | ||||
|             showRecentNotes($el); | ||||
|  | ||||
|             // this will cause the click not give focus to the "show recent notes" button | ||||
|             // this is important because otherwise input will lose focus immediatelly and not show the results | ||||
|             return false; | ||||
|         }); | ||||
|  | ||||
|         $goToSelectedNoteButton.click(() => { | ||||
|             if ($el.hasClass("disabled")) { | ||||
|   | ||||
| @@ -1,10 +1,13 @@ | ||||
| import noteDetailService from "./note_detail.js"; | ||||
| import treeService from "./tree.js"; | ||||
| import infoService from './info.js'; | ||||
|  | ||||
| const $searchString = $("#search-string"); | ||||
| const $component = $('#note-detail-search'); | ||||
| const $refreshButton = $('#note-detail-search-refresh-results-button'); | ||||
|  | ||||
| function getContent() { | ||||
|     JSON.stringify({ | ||||
|     return JSON.stringify({ | ||||
|         searchString: $searchString.val() | ||||
|     }); | ||||
| } | ||||
| @@ -25,6 +28,14 @@ function show() { | ||||
|     $searchString.on('input', noteDetailService.noteChanged); | ||||
| } | ||||
|  | ||||
| $refreshButton.click(async () => { | ||||
|     await noteDetailService.saveNoteIfChanged(); | ||||
|  | ||||
|     treeService.reload(); | ||||
|  | ||||
|     infoService.showMessage('Tree has been refreshed.'); | ||||
| }); | ||||
|  | ||||
| export default { | ||||
|     getContent, | ||||
|     show, | ||||
|   | ||||
| @@ -87,7 +87,7 @@ $searchInput.keyup(e => { | ||||
|     if (e && e.which === $.ui.keyCode.ENTER) { | ||||
|         doSearch(); | ||||
|     } | ||||
| }).focus(); | ||||
| }); | ||||
|  | ||||
| $doSearchButton.click(() => doSearch()); // keep long form because of argument | ||||
| $resetSearchButton.click(resetSearch); | ||||
|   | ||||
| @@ -94,9 +94,18 @@ async function expandToNote(notePath, expandOpts) { | ||||
|  | ||||
|     const noteId = treeUtils.getNoteIdFromNotePath(notePath); | ||||
|  | ||||
|     const hoistedNoteId = await hoistedNoteService.getHoistedNoteId(); | ||||
|     let hoistedNoteFound = false; | ||||
|  | ||||
|     let parentNoteId = null; | ||||
|  | ||||
|     for (const childNoteId of runPath) { | ||||
|         if (childNoteId === hoistedNoteId) { | ||||
|             hoistedNoteFound = true; | ||||
|         } | ||||
|  | ||||
|         // we expand only after hoisted note since before then nodes are not actually present in the tree | ||||
|         if (hoistedNoteFound) { | ||||
|             // for first node (!parentNoteId) it doesn't matter which node is found | ||||
|             let node = getNode(childNoteId, parentNoteId); | ||||
|  | ||||
| @@ -117,10 +126,10 @@ async function expandToNote(notePath, expandOpts) { | ||||
|  | ||||
|             if (childNoteId === noteId) { | ||||
|                 return node; | ||||
|         } | ||||
|         else { | ||||
|             } else { | ||||
|                 await node.setExpanded(true, expandOpts); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         parentNoteId = childNoteId; | ||||
|     } | ||||
| @@ -129,9 +138,12 @@ async function expandToNote(notePath, expandOpts) { | ||||
| async function activateNote(notePath, noteLoadedListener) { | ||||
|     utils.assertArguments(notePath); | ||||
|  | ||||
|     // notePath argument can contain only noteId which is not good when hoisted since | ||||
|     // then we need to check the whole note path | ||||
|     const runNotePath = await getRunPath(notePath); | ||||
|     const hoistedNoteId = await hoistedNoteService.getHoistedNoteId(); | ||||
|  | ||||
|     if (hoistedNoteId !== 'root' && !notePath.includes(hoistedNoteId)) { | ||||
|     if (hoistedNoteId !== 'root' && !runNotePath.includes(hoistedNoteId)) { | ||||
|         if (!await confirmDialog.confirm("Requested note is outside of hoisted note subtree. Do you want to unhoist?")) { | ||||
|             return; | ||||
|         } | ||||
| @@ -352,6 +364,7 @@ function clearSelectedNodes() { | ||||
| } | ||||
|  | ||||
| async function treeInitialized() { | ||||
|     // - is used in mobile to indicate that we don't want to activate any note after load | ||||
|     if (startNotePath === '-') { | ||||
|         return; | ||||
|     } | ||||
| @@ -363,7 +376,6 @@ async function treeInitialized() { | ||||
|         startNotePath = null; | ||||
|     } | ||||
|  | ||||
|     // - is used in mobile to indicate that we don't want to activate any note after load | ||||
|     if (startNotePath) { | ||||
|         const node = await activateNote(startNotePath); | ||||
|  | ||||
| @@ -438,6 +450,16 @@ function initFancyTree(tree) { | ||||
|  | ||||
|                 $span.append(unhoistButton); | ||||
|             } | ||||
|         }, | ||||
|         // this is done to automatically lazy load all expanded search notes after tree load | ||||
|         loadChildren: function(event, data) { | ||||
|             data.node.visit(function(subNode){ | ||||
|                 // Load all lazy/unloaded child nodes | ||||
|                 // (which will trigger `loadChildren` recursively) | ||||
|                 if( subNode.isUndefined() && subNode.isExpanded() ) { | ||||
|                     subNode.load(); | ||||
|                 } | ||||
|             }); | ||||
|         } | ||||
|     }); | ||||
|  | ||||
|   | ||||
| @@ -86,7 +86,7 @@ async function prepareNode(branch) { | ||||
|         extraClasses: await getExtraClasses(note), | ||||
|         icon: await getIcon(note), | ||||
|         refKey: note.noteId, | ||||
|         expanded: (note.type !== 'search' && branch.isExpanded) || hoistedNoteId === note.noteId | ||||
|         expanded: branch.isExpanded || hoistedNoteId === note.noteId | ||||
|     }; | ||||
|  | ||||
|     if (note.hasChildren() || note.type === 'search') { | ||||
|   | ||||
| @@ -137,6 +137,11 @@ function randomString(len) { | ||||
|  | ||||
| function bindShortcut(keyboardShortcut, handler) { | ||||
|     if (isDesktop()) { | ||||
|         if (isMac()) { | ||||
|             // use CMD (meta) instead of CTRL for all shortcuts | ||||
|             keyboardShortcut = keyboardShortcut.replace("ctrl", "meta"); | ||||
|         } | ||||
|  | ||||
|         $(document).bind('keydown', keyboardShortcut, e => { | ||||
|             handler(); | ||||
|  | ||||
|   | ||||
| @@ -1,4 +1,7 @@ | ||||
| import utils from "./services/utils.js"; | ||||
| import macInit from './services/mac_init.js'; | ||||
|  | ||||
| macInit.init(); | ||||
|  | ||||
| function SetupModel() { | ||||
|     if (syncInProgress) { | ||||
|   | ||||
| @@ -62,7 +62,7 @@ | ||||
|     padding: 10px 0 10px 0; | ||||
|     margin: 0 20px 0 10px; | ||||
|     border: 1px solid #ddd; | ||||
|     border-radius: 5px; | ||||
|     border-radius: 7px; | ||||
| } | ||||
|  | ||||
| #context-menu-container { | ||||
|   | ||||
| @@ -2,6 +2,7 @@ body { | ||||
|     /* Fix for CKEditor block gutter icon "stretching" body and causing scrollbar to appear after pressing enter | ||||
|        on the last line of the editor. */ | ||||
|     position: fixed; | ||||
|     width: 100%; | ||||
| } | ||||
|  | ||||
| #title-container { | ||||
| @@ -16,6 +17,16 @@ body { | ||||
|     flex-grow: 100; | ||||
| } | ||||
|  | ||||
| ul.fancytree-container { | ||||
|     /* override specific size from fancytree.css */ | ||||
|     font-family: inherit !important; | ||||
|     font-size: inherit !important; | ||||
| } | ||||
|  | ||||
| .fancytree-title { | ||||
|     margin-left: 7px !important; | ||||
| } | ||||
|  | ||||
| .fancytree-node:not(.fancytree-loading) .fancytree-expander { | ||||
|     background: none; | ||||
|     width: auto; | ||||
| @@ -131,8 +142,21 @@ span.fancytree-node.fancytree-active-clone:not(.fancytree-active) .fancytree-tit | ||||
|  | ||||
| /* By default not focused active tree item is not easily visible, this makes it more visible */ | ||||
| span.fancytree-active:not(.fancytree-focused) .fancytree-title { | ||||
|     background-color: #eee !important; | ||||
|     border-color: #ddd !important; | ||||
|     border-radius: 3px; | ||||
| } | ||||
|  | ||||
| span.fancytree-active.fancytree-focused .fancytree-title { | ||||
|     background-color: #ddd !important; | ||||
|     border-color: #555 !important; | ||||
|     border-color: #bbb !important; | ||||
|     border-radius: 3px; | ||||
| } | ||||
|  | ||||
| .fancytree-plain span.fancytree-node:hover span.fancytree-title { | ||||
|     background-color: #eee !important; | ||||
|     border-color: #bbb !important; | ||||
|     border-radius: 3px; | ||||
| } | ||||
|  | ||||
| .ui-autocomplete { | ||||
| @@ -366,7 +390,7 @@ div.ui-tooltip { | ||||
|     height: 150px; | ||||
| } | ||||
|  | ||||
| .btn:not(.btn-primary):not(.btn-danger) { | ||||
| .btn:not(.btn-primary):not(.btn-secondary):not(.btn-danger) { | ||||
|     border-color: #ddd; | ||||
|     background-color: #eee; | ||||
| } | ||||
|   | ||||
| @@ -7,6 +7,7 @@ const tarImportService = require('../../services/import/tar'); | ||||
| const singleImportService = require('../../services/import/single'); | ||||
| const cls = require('../../services/cls'); | ||||
| const path = require('path'); | ||||
| const noteCacheService = require('../../services/note_cache'); | ||||
|  | ||||
| async function importToBranch(req) { | ||||
|     const parentNoteId = req.params.parentNoteId; | ||||
| @@ -28,24 +29,32 @@ async function importToBranch(req) { | ||||
|     // and may produce unintended consequences | ||||
|     cls.disableEntityEvents(); | ||||
|  | ||||
|     let note; // typically root of the import - client can show it after finishing the import | ||||
|  | ||||
|     if (extension === '.tar') { | ||||
|         return await tarImportService.importTar(file.buffer, parentNote); | ||||
|         note = await tarImportService.importTar(file.buffer, parentNote); | ||||
|     } | ||||
|     else if (extension === '.opml') { | ||||
|         return await opmlImportService.importOpml(file.buffer, parentNote); | ||||
|         note = await opmlImportService.importOpml(file.buffer, parentNote); | ||||
|     } | ||||
|     else if (extension === '.md') { | ||||
|         return await singleImportService.importMarkdown(file, parentNote); | ||||
|         note = await singleImportService.importMarkdown(file, parentNote); | ||||
|     } | ||||
|     else if (extension === '.html' || extension === '.htm') { | ||||
|         return await singleImportService.importHtml(file, parentNote); | ||||
|         note = await singleImportService.importHtml(file, parentNote); | ||||
|     } | ||||
|     else if (extension === '.enex') { | ||||
|         return await enexImportService.importEnex(file, parentNote); | ||||
|         note = await enexImportService.importEnex(file, parentNote); | ||||
|     } | ||||
|     else { | ||||
|         return [400, `Unrecognized extension ${extension}, must be .tar or .opml`]; | ||||
|     } | ||||
|  | ||||
|     // import has deactivated note events so note cache is not updated | ||||
|     // instead we force it to reload (can be async) | ||||
|     noteCacheService.load(); | ||||
|  | ||||
|     return note; | ||||
| } | ||||
|  | ||||
| module.exports = { | ||||
|   | ||||
| @@ -23,7 +23,8 @@ async function loginSync(req) { | ||||
|  | ||||
|     const now = new Date(); | ||||
|  | ||||
|     if (Math.abs(timestamp.getTime() - now.getTime()) > 5000) { | ||||
|     // login token is valid for 5 minutes | ||||
|     if (Math.abs(timestamp.getTime() - now.getTime()) > 5 * 60 * 1000) { | ||||
|         return [400, { message: 'Auth request time is out of sync' }]; | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -19,7 +19,6 @@ async function anonymize() { | ||||
|     await db.run("UPDATE notes SET title = 'title', 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 images SET data = NULL"); | ||||
|     await db.run(`UPDATE options SET value = 'anonymized' WHERE name IN  | ||||
|                     ('documentSecret', 'encryptedDataKey', 'passwordVerificationHash',  | ||||
|                      'passwordVerificationSalt', 'passwordDerivedKeySalt')`); | ||||
|   | ||||
| @@ -1 +1 @@ | ||||
| module.exports = { buildDate:"2019-01-05T22:48:11+01:00", buildRevision: "9fca7f09a564c719c834d38d76c3b595c8579b2a" }; | ||||
| module.exports = { buildDate:"2019-01-10T21:31:30+01:00", buildRevision: "0b251530fa0ee61edc8dcc9235033abb73afc614" }; | ||||
|   | ||||
| @@ -1,6 +1,7 @@ | ||||
| "use strict"; | ||||
|  | ||||
| const repository = require('./repository'); | ||||
| const log = require('./log'); | ||||
| const protectedSessionService = require('./protected_session'); | ||||
| const noteService = require('./notes'); | ||||
| const imagemin = require('imagemin'); | ||||
| @@ -13,7 +14,13 @@ const sanitizeFilename = require('sanitize-filename'); | ||||
|  | ||||
| async function saveImage(buffer, originalName, parentNoteId) { | ||||
|     const resizedImage = await resize(buffer); | ||||
|     const optimizedImage = await optimize(resizedImage); | ||||
|     let optimizedImage; | ||||
|     try { | ||||
|       optimizedImage = await optimize(resizedImage); | ||||
|     } catch (e) { | ||||
|       log.error(e); | ||||
|       optimizedImage = resizedImage; | ||||
|     } | ||||
|  | ||||
|     const imageFormat = imageType(optimizedImage); | ||||
|  | ||||
|   | ||||
| @@ -1,4 +1,5 @@ | ||||
| const sax = require("sax"); | ||||
| const fileType = require('file-type'); | ||||
| const stream = require('stream'); | ||||
| const xml2js = require('xml2js'); | ||||
| const log = require("../log"); | ||||
| @@ -144,7 +145,7 @@ async function importEnex(file, parentNote) { | ||||
|                 }); | ||||
|             } | ||||
|             else if (currentTag === 'mime') { | ||||
|                 resource.mime = text; | ||||
|                 resource.mime = text.toLowerCase(); | ||||
|  | ||||
|                 if (text.startsWith("image/")) { | ||||
|                     resource.title = "image"; | ||||
| @@ -222,7 +223,26 @@ async function importEnex(file, parentNote) { | ||||
|  | ||||
|             const mediaRegex = new RegExp(`<en-media hash="${hash}"[^>]*>`, 'g'); | ||||
|  | ||||
|             if (resource.mime.startsWith("image/")) { | ||||
|             const fileTypeFromBuffer = fileType(resource.content); | ||||
|             if (fileTypeFromBuffer) { | ||||
|               // If fileType returns something for buffer, then set the mime given | ||||
|               resource.mime = fileTypeFromBuffer.mime; | ||||
|             } | ||||
|  | ||||
|             const createResourceNote = async () => { | ||||
|               const resourceNote = (await noteService.createNote(noteEntity.noteId, resource.title, resource.content, { | ||||
|                 attributes: resource.attributes, | ||||
|                 type: 'file', | ||||
|                 mime: resource.mime | ||||
|               })).note; | ||||
|  | ||||
|               const resourceLink = `<a href="#root/${resourceNote.noteId}">${utils.escapeHtml(resource.title)}</a>`; | ||||
|  | ||||
|               noteEntity.content = noteEntity.content.replace(mediaRegex, resourceLink); | ||||
|             } | ||||
|  | ||||
|             if (["image/jpeg", "image/png", "image/gif"].includes(resource.mime)) { | ||||
|               try { | ||||
|                 const originalName = "image." + resource.mime.substr(6); | ||||
|  | ||||
|                 const { url } = await imageService.saveImage(resource.content, originalName, noteEntity.noteId); | ||||
| @@ -236,17 +256,13 @@ async function importEnex(file, parentNote) { | ||||
|                     // otherwise image would be removed since no note would include it | ||||
|                     note.content += imageLink; | ||||
|                 } | ||||
|               } catch (e) { | ||||
|                 log.error("error when saving image from ENEX file: " + e); | ||||
|                 await createResourceNote(); | ||||
|               } | ||||
|             } | ||||
|             else { | ||||
|                 const resourceNote = (await noteService.createNote(noteEntity.noteId, resource.title, resource.content, { | ||||
|                     attributes: resource.attributes, | ||||
|                     type: 'file', | ||||
|                     mime: resource.mime | ||||
|                 })).note; | ||||
|  | ||||
|                 const resourceLink = `<a href="#root/${resourceNote.noteId}">${utils.escapeHtml(resource.title)}</a>`; | ||||
|  | ||||
|                 noteEntity.content = noteEntity.content.replace(mediaRegex, resourceLink); | ||||
|               await createResourceNote(); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|   | ||||
| @@ -33,9 +33,21 @@ async function load() { | ||||
|  | ||||
|     archived = await sql.getMap(`SELECT noteId, isInheritable FROM attributes WHERE isDeleted = 0 AND type = 'label' AND name = 'archived'`); | ||||
|  | ||||
|     if (protectedSessionService.isProtectedSessionAvailable()) { | ||||
|         await loadProtectedNotes(); | ||||
|     } | ||||
|  | ||||
|     loaded = true; | ||||
| } | ||||
|  | ||||
| async function loadProtectedNotes() { | ||||
|     protectedNoteTitles = await sql.getMap(`SELECT noteId, title FROM notes WHERE isDeleted = 0 AND isProtected = 1`); | ||||
|  | ||||
|     for (const noteId in protectedNoteTitles) { | ||||
|         protectedNoteTitles[noteId] = protectedSessionService.decryptNoteTitle(noteId, protectedNoteTitles[noteId]); | ||||
|     } | ||||
| } | ||||
|  | ||||
| function highlightResults(results, allTokens) { | ||||
|     // we remove < signs because they can cause trouble in matching and overwriting existing highlighted chunks | ||||
|     // which would make the resulting HTML string invalid. | ||||
| @@ -313,8 +325,17 @@ eventService.subscribe([eventService.ENTITY_CHANGED, eventService.ENTITY_DELETED | ||||
|             delete noteTitles[note.noteId]; | ||||
|             delete childToParent[note.noteId]; | ||||
|         } | ||||
|         else { | ||||
|             if (note.isProtected) { | ||||
|                 // we can assume we have protected session since we managed to update | ||||
|                 // removing from the maps is important when switching between protected & unprotected | ||||
|                 protectedNoteTitles[note.noteId] = note.title; | ||||
|                 delete noteTitles[note.noteId]; | ||||
|             } | ||||
|             else { | ||||
|                 noteTitles[note.noteId] = note.title; | ||||
|                 delete protectedNoteTitles[note.noteId]; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     else if (entityName === 'branches') { | ||||
| @@ -355,15 +376,9 @@ eventService.subscribe([eventService.ENTITY_CHANGED, eventService.ENTITY_DELETED | ||||
|     } | ||||
| }); | ||||
|  | ||||
| eventService.subscribe(eventService.ENTER_PROTECTED_SESSION, async () => { | ||||
|     if (!loaded) { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     protectedNoteTitles = await sql.getMap(`SELECT noteId, title FROM notes WHERE isDeleted = 0 AND isProtected = 1`); | ||||
|  | ||||
|     for (const noteId in protectedNoteTitles) { | ||||
|         protectedNoteTitles[noteId] = protectedSessionService.decryptNoteTitle(noteId, protectedNoteTitles[noteId]); | ||||
| eventService.subscribe(eventService.ENTER_PROTECTED_SESSION, () => { | ||||
|     if (loaded) { | ||||
|         loadProtectedNotes(); | ||||
|     } | ||||
| }); | ||||
|  | ||||
| @@ -372,5 +387,6 @@ sqlInit.dbReady.then(() => utils.stopWatch("Autocomplete load", load)); | ||||
| module.exports = { | ||||
|     findNotes, | ||||
|     getNotePath, | ||||
|     getNoteTitleForPath | ||||
|     getNoteTitleForPath, | ||||
|     load | ||||
| }; | ||||
| @@ -6,7 +6,7 @@ const sourceIdService = require('./source_id'); | ||||
| const log = require('./log'); | ||||
|  | ||||
| async function executeNote(note, originEntity) { | ||||
|     if (!note.isJavaScript()) { | ||||
|     if (!note.isJavaScript() || !note.isContentAvailable) { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
| @@ -80,6 +80,10 @@ function getParams(params) { | ||||
| } | ||||
|  | ||||
| async function getScriptBundle(note, root = true, scriptEnv = null, includedNoteIds = []) { | ||||
|     if (!note.isContentAvailable) { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     if (!note.isJavaScript() && !note.isHtml()) { | ||||
|         return; | ||||
|     } | ||||
|   | ||||
| @@ -1,7 +1,12 @@ | ||||
| <div id="note-detail-search" class="note-detail-component"> | ||||
|     <div style="display: flex; align-items: center;"> | ||||
|         <strong>Search string:    </strong> | ||||
|         <textarea rows="4" cols="50" id="search-string"></textarea> | ||||
|         <textarea rows="4" cols="40" id="search-string"></textarea> | ||||
|  | ||||
|         <span> | ||||
|                 | ||||
|             <button type="button" class="btn btn-primary" id="note-detail-search-refresh-results-button">Refresh tree</button> | ||||
|         </span> | ||||
|     </div> | ||||
|  | ||||
|     <br /> | ||||
|   | ||||
		Reference in New Issue
	
	Block a user