mirror of
				https://github.com/zadam/trilium.git
				synced 2025-10-31 18:36:30 +01:00 
			
		
		
		
	Merge branch 'master' into next50
This commit is contained in:
		
							
								
								
									
										5734
									
								
								libraries/codemirror/keymap/vim.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										5734
									
								
								libraries/codemirror/keymap/vim.js
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -2,7 +2,7 @@ | |||||||
|   "name": "trilium", |   "name": "trilium", | ||||||
|   "productName": "Trilium Notes", |   "productName": "Trilium Notes", | ||||||
|   "description": "Trilium Notes", |   "description": "Trilium Notes", | ||||||
|   "version": "0.49.1-beta", |   "version": "0.49.2-beta", | ||||||
|   "license": "AGPL-3.0-only", |   "license": "AGPL-3.0-only", | ||||||
|   "main": "electron.js", |   "main": "electron.js", | ||||||
|   "bin": { |   "bin": { | ||||||
|   | |||||||
| @@ -1,7 +1,15 @@ | |||||||
| import mimeTypesService from "../../services/mime_types.js"; | import mimeTypesService from "../../services/mime_types.js"; | ||||||
| import options from "../../services/options.js"; | import options from "../../services/options.js"; | ||||||
|  | import server from "../../services/server.js"; | ||||||
|  | import toastService from "../../services/toast.js"; | ||||||
|  | import utils from "../../services/utils.js"; | ||||||
|  |  | ||||||
| const TPL = ` | const TPL = ` | ||||||
|  | <h4>Use vim keybindings in CodeNotes (no ex mode)</h4> | ||||||
|  | <div class="custom-control custom-checkbox"> | ||||||
|  |     <input type="checkbox" class="custom-control-input" id="vim-keymap-enabled"> | ||||||
|  |     <label class="custom-control-label" for="vim-keymap-enabled">Enable Vim Keybindings</label> | ||||||
|  | </div> | ||||||
| <h4>Available MIME types in the dropdown</h4> | <h4>Available MIME types in the dropdown</h4> | ||||||
|  |  | ||||||
| <ul id="options-mime-types" style="max-height: 500px; overflow: auto; list-style-type: none;"></ul>`; | <ul id="options-mime-types" style="max-height: 500px; overflow: auto; list-style-type: none;"></ul>`; | ||||||
| @@ -10,12 +18,18 @@ export default class CodeNotesOptions { | |||||||
|     constructor() { |     constructor() { | ||||||
|         $("#options-code-notes").html(TPL); |         $("#options-code-notes").html(TPL); | ||||||
|  |  | ||||||
|  |         this.$vimKeymapEnabled = $("#vim-keymap-enabled"); | ||||||
|  |         this.$vimKeymapEnabled.on('change', () => { | ||||||
|  |             const opts = { 'vimKeymapEnabled': this.$vimKeymapEnabled.is(":checked") ? "true" : "false" }; | ||||||
|  |             server.put('options', opts).then(() => toastService.showMessage("Options change have been saved.")); | ||||||
|  |             return false; | ||||||
|  |         }); | ||||||
|         this.$mimeTypes = $("#options-mime-types"); |         this.$mimeTypes = $("#options-mime-types"); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     async optionsLoaded() { |     async optionsLoaded(options) { | ||||||
|         this.$mimeTypes.empty(); |         this.$mimeTypes.empty(); | ||||||
|  |         this.$vimKeymapEnabled.prop("checked", options['vimKeymapEnabled'] === 'true'); | ||||||
|         let idCtr = 1; |         let idCtr = 1; | ||||||
|  |  | ||||||
|         for (const mimeType of await mimeTypesService.getMimeTypes()) { |         for (const mimeType of await mimeTypesService.getMimeTypes()) { | ||||||
| @@ -45,4 +59,4 @@ export default class CodeNotesOptions { | |||||||
|  |  | ||||||
|         mimeTypesService.loadMimeTypes(); |         mimeTypesService.loadMimeTypes(); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -10,6 +10,7 @@ const CODE_MIRROR = { | |||||||
|         "libraries/codemirror/addon/edit/matchtags.js", |         "libraries/codemirror/addon/edit/matchtags.js", | ||||||
|         "libraries/codemirror/addon/search/match-highlighter.js", |         "libraries/codemirror/addon/search/match-highlighter.js", | ||||||
|         "libraries/codemirror/mode/meta.js", |         "libraries/codemirror/mode/meta.js", | ||||||
|  |         "libraries/codemirror/keymap/vim.js", | ||||||
|         "libraries/codemirror/addon/lint/lint.js", |         "libraries/codemirror/addon/lint/lint.js", | ||||||
|         "libraries/codemirror/addon/lint/eslint.js" |         "libraries/codemirror/addon/lint/eslint.js" | ||||||
|     ], |     ], | ||||||
|   | |||||||
| @@ -129,12 +129,7 @@ export default class TabManager extends Component { | |||||||
|             window.history.pushState(null, "", url); |             window.history.pushState(null, "", url); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         const titleFragments = [ |         this.updateDocumentTitle(activeNoteContext); | ||||||
|             // it helps navigating in history if note title is included in the title |  | ||||||
|             activeNoteContext.note?.title, |  | ||||||
|             "Trilium Notes" |  | ||||||
|         ].filter(Boolean); |  | ||||||
|         document.title = titleFragments.join(" - "); |  | ||||||
|  |  | ||||||
|         this.triggerEvent('activeNoteChanged'); // trigger this even in on popstate event |         this.triggerEvent('activeNoteChanged'); // trigger this even in on popstate event | ||||||
|     } |     } | ||||||
| @@ -453,4 +448,22 @@ export default class TabManager extends Component { | |||||||
|     hoistedNoteChangedEvent() { |     hoistedNoteChangedEvent() { | ||||||
|         this.tabsUpdate.scheduleUpdate(); |         this.tabsUpdate.scheduleUpdate(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     updateDocumentTitle(activeNoteContext) { | ||||||
|  |         const titleFragments = [ | ||||||
|  |             // it helps navigating in history if note title is included in the title | ||||||
|  |             activeNoteContext.note?.title, | ||||||
|  |             "Trilium Notes" | ||||||
|  |         ].filter(Boolean); | ||||||
|  |  | ||||||
|  |         document.title = titleFragments.join(" - "); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     entitiesReloadedEvent({loadResults}) { | ||||||
|  |         const activeContext = this.getActiveContext(); | ||||||
|  |  | ||||||
|  |         if (activeContext && loadResults.isNoteReloaded(activeContext.noteId)) { | ||||||
|  |             this.updateDocumentTitle(activeContext); | ||||||
|  |         } | ||||||
|  |     } | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										21
									
								
								src/public/app/share.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								src/public/app/share.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,21 @@ | |||||||
|  | /** | ||||||
|  |  * Fetch note with given ID from backend | ||||||
|  |  * | ||||||
|  |  * @param noteId of the given note to be fetched. If falsy, fetches current note. | ||||||
|  |  */ | ||||||
|  | async function fetchNote(noteId = null) { | ||||||
|  |     if (!noteId) { | ||||||
|  |         noteId = document.body.getAttribute("data-note-id"); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     const resp = await fetch(`api/notes/${noteId}`); | ||||||
|  |  | ||||||
|  |     return await resp.json(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | document.addEventListener('DOMContentLoaded', () => { | ||||||
|  |     const toggleMenuButton = document.getElementById('toggleMenuButton'); | ||||||
|  |     const layout = document.getElementById('layout'); | ||||||
|  |  | ||||||
|  |     toggleMenuButton.addEventListener('click', () => layout.classList.toggle('showMenu')); | ||||||
|  | }, false); | ||||||
| @@ -226,6 +226,8 @@ const ATTR_HELP = { | |||||||
|         "renderNote": 'notes of type "render HTML note" will be rendered using a code note (HTML or script) and it is necessary to point using this relation to which note should be rendered', |         "renderNote": 'notes of type "render HTML note" will be rendered using a code note (HTML or script) and it is necessary to point using this relation to which note should be rendered', | ||||||
|         "widget": "target of this relation will be executed and rendered as a widget in the sidebar", |         "widget": "target of this relation will be executed and rendered as a widget in the sidebar", | ||||||
|         "shareCss": "CSS note which will be injected into the share page. CSS note must be in the shared sub-tree as well. Consider using 'shareHiddenFromTree' and 'shareOmitDefaultCss' as well.", |         "shareCss": "CSS note which will be injected into the share page. CSS note must be in the shared sub-tree as well. Consider using 'shareHiddenFromTree' and 'shareOmitDefaultCss' as well.", | ||||||
|  |         "shareJs": "JavaScript note which will be injected into the share page. JS note must be in the shared sub-tree as well. Consider using 'shareHiddenFromTree'.", | ||||||
|  |         "shareFavicon": "Favicon note to be set in the shared page. Typically you want to set it to share root and make it inheritable. Favicon note must be in the shared sub-tree as well. Consider using 'shareHiddenFromTree'.", | ||||||
|     } |     } | ||||||
| }; | }; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -6,6 +6,7 @@ import ws from "../../services/ws.js"; | |||||||
| import appContext from "../../services/app_context.js"; | import appContext from "../../services/app_context.js"; | ||||||
| import toastService from "../../services/toast.js"; | import toastService from "../../services/toast.js"; | ||||||
| import treeService from "../../services/tree.js"; | import treeService from "../../services/tree.js"; | ||||||
|  | import options from "../../services/options.js"; | ||||||
|  |  | ||||||
| const TPL = ` | const TPL = ` | ||||||
| <div class="note-detail-code note-detail-printable"> | <div class="note-detail-code note-detail-printable"> | ||||||
| @@ -94,6 +95,7 @@ export default class EditableCodeTypeWidget extends TypeWidget { | |||||||
|             viewportMargin: Infinity, |             viewportMargin: Infinity, | ||||||
|             indentUnit: 4, |             indentUnit: 4, | ||||||
|             matchBrackets: true, |             matchBrackets: true, | ||||||
|  |             keyMap: options.is('vimKeymapEnabled') ? "vim": "default", | ||||||
|             matchTags: {bothTags: true}, |             matchTags: {bothTags: true}, | ||||||
|             highlightSelectionMatches: {showToken: /\w/, annotateScrollbar: false}, |             highlightSelectionMatches: {showToken: /\w/, annotateScrollbar: false}, | ||||||
|             lint: true, |             lint: true, | ||||||
|   | |||||||
| @@ -117,6 +117,15 @@ iframe.pdf-view { | |||||||
|     margin-right: 20px; |     margin-right: 20px; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | #noteClippedFrom { | ||||||
|  |     padding: 10px 0 10px 0; | ||||||
|  |     margin: 20px 0 20px 0; | ||||||
|  |     color: #666; | ||||||
|  |     border: 1px solid #ddd; | ||||||
|  |     border-left: 0; | ||||||
|  |     border-right: 0; | ||||||
|  | } | ||||||
|  |  | ||||||
| #toggleMenuButton::after { | #toggleMenuButton::after { | ||||||
|     position: relative; |     position: relative; | ||||||
|     top: -2px; |     top: -2px; | ||||||
|   | |||||||
| @@ -32,6 +32,7 @@ const ALLOWED_OPTIONS = new Set([ | |||||||
|     'similarNotesWidget', |     'similarNotesWidget', | ||||||
|     'editedNotesWidget', |     'editedNotesWidget', | ||||||
|     'calendarWidget', |     'calendarWidget', | ||||||
|  |     'vimKeymapEnabled', | ||||||
|     'codeNotesMimeTypes', |     'codeNotesMimeTypes', | ||||||
|     'spellCheckEnabled', |     'spellCheckEnabled', | ||||||
|     'spellCheckLanguageCode', |     'spellCheckLanguageCode', | ||||||
|   | |||||||
| @@ -67,6 +67,8 @@ const BUILTIN_ATTRIBUTES = [ | |||||||
|     { type: 'relation', name: 'widget', isDangerous: true }, |     { type: 'relation', name: 'widget', isDangerous: true }, | ||||||
|     { type: 'relation', name: 'renderNote', isDangerous: true }, |     { type: 'relation', name: 'renderNote', isDangerous: true }, | ||||||
|     { type: 'relation', name: 'shareCss', isDangerous: false }, |     { type: 'relation', name: 'shareCss', isDangerous: false }, | ||||||
|  |     { type: 'relation', name: 'shareJs', isDangerous: false }, | ||||||
|  |     { type: 'relation', name: 'shareFavicon', isDangerous: false }, | ||||||
| ]; | ]; | ||||||
|  |  | ||||||
| /** @returns {Note[]} */ | /** @returns {Note[]} */ | ||||||
|   | |||||||
| @@ -1 +1 @@ | |||||||
| module.exports = { buildDate:"2021-12-24T23:05:10+01:00", buildRevision: "0217b1c85de9a2824e7f07d07a357064c5803383" }; | module.exports = { buildDate:"2022-01-02T22:43:30+01:00", buildRevision: "feffd57f240438d107c1ed1c1772545611a97dee" }; | ||||||
|   | |||||||
| @@ -3,6 +3,10 @@ const sanitizeHtml = require('sanitize-html'); | |||||||
| // intended mainly as protection against XSS via import | // intended mainly as protection against XSS via import | ||||||
| // secondarily it (partly) protects against "CSS takeover" | // secondarily it (partly) protects against "CSS takeover" | ||||||
| function sanitize(dirtyHtml) { | function sanitize(dirtyHtml) { | ||||||
|  |     if (!dirtyHtml) { | ||||||
|  |         return dirtyHtml; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     // avoid H1 per https://github.com/zadam/trilium/issues/1552 |     // avoid H1 per https://github.com/zadam/trilium/issues/1552 | ||||||
|     // demote H1, and if that conflicts with existing H2, demote that, etc |     // demote H1, and if that conflicts with existing H2, demote that, etc | ||||||
|     const transformTags = {}; |     const transformTags = {}; | ||||||
|   | |||||||
| @@ -51,7 +51,7 @@ async function importOpml(taskContext, fileBuffer, parentNote) { | |||||||
|             throw new Error("Unrecognized OPML version " + opmlVersion); |             throw new Error("Unrecognized OPML version " + opmlVersion); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         content = htmlSanitizer.sanitize(content); |         content = htmlSanitizer.sanitize(content || ""); | ||||||
|  |  | ||||||
|         const {note} = noteService.createNewNote({ |         const {note} = noteService.createNewNote({ | ||||||
|             parentNoteId, |             parentNoteId, | ||||||
|   | |||||||
| @@ -1,7 +1,16 @@ | |||||||
| const becca = require('../becca/becca'); | const becca = require('../becca/becca'); | ||||||
|  | const sql = require("./sql.js"); | ||||||
|  |  | ||||||
| function getOption(name) { | function getOption(name) { | ||||||
|     const option = require('../becca/becca').getOption(name); |     let option; | ||||||
|  |  | ||||||
|  |     if (becca.loaded) { | ||||||
|  |         option = becca.getOption(name); | ||||||
|  |     } | ||||||
|  |     else { | ||||||
|  |         // e.g. in initial sync becca is not loaded because DB is not initialized | ||||||
|  |         option = sql.getRow("SELECT * FROM options WHERE name = ?", name); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     if (!option) { |     if (!option) { | ||||||
|         throw new Error(`Option "${name}" doesn't exist`); |         throw new Error(`Option "${name}" doesn't exist`); | ||||||
| @@ -39,12 +48,12 @@ function getOptionBool(name) { | |||||||
| } | } | ||||||
|  |  | ||||||
| function setOption(name, value) { | function setOption(name, value) { | ||||||
|     const option = becca.getOption(name); |  | ||||||
|  |  | ||||||
|     if (value === true || value === false) { |     if (value === true || value === false) { | ||||||
|         value = value.toString(); |         value = value.toString(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     const option = becca.getOption(name); | ||||||
|  |  | ||||||
|     if (option) { |     if (option) { | ||||||
|         option.value = value; |         option.value = value; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -53,6 +53,7 @@ const defaultOptions = [ | |||||||
|     { name: 'imageMaxWidthHeight', value: '2000', isSynced: true }, |     { name: 'imageMaxWidthHeight', value: '2000', isSynced: true }, | ||||||
|     { name: 'imageJpegQuality', value: '75', isSynced: true }, |     { name: 'imageJpegQuality', value: '75', isSynced: true }, | ||||||
|     { name: 'autoFixConsistencyIssues', value: 'true', isSynced: false }, |     { name: 'autoFixConsistencyIssues', value: 'true', isSynced: false }, | ||||||
|  |     { name: 'vimKeymapEnabled', value: 'false', isSynced: false }, | ||||||
|     { name: 'codeNotesMimeTypes', value: '["text/x-csrc","text/x-c++src","text/x-csharp","text/css","text/x-go","text/x-groovy","text/x-haskell","text/html","message/http","text/x-java","application/javascript;env=frontend","application/javascript;env=backend","application/json","text/x-kotlin","text/x-markdown","text/x-perl","text/x-php","text/x-python","text/x-ruby",null,"text/x-sql","text/x-sqlite;schema=trilium","text/x-swift","text/xml","text/x-yaml"]', isSynced: true }, |     { name: 'codeNotesMimeTypes', value: '["text/x-csrc","text/x-c++src","text/x-csharp","text/css","text/x-go","text/x-groovy","text/x-haskell","text/html","message/http","text/x-java","application/javascript;env=frontend","application/javascript;env=backend","application/json","text/x-kotlin","text/x-markdown","text/x-perl","text/x-php","text/x-python","text/x-ruby",null,"text/x-sql","text/x-sqlite;schema=trilium","text/x-swift","text/xml","text/x-yaml"]', isSynced: true }, | ||||||
|     { name: 'leftPaneWidth', value: '25', isSynced: false }, |     { name: 'leftPaneWidth', value: '25', isSynced: false }, | ||||||
|     { name: 'leftPaneVisible', value: 'true', isSynced: false }, |     { name: 'leftPaneVisible', value: 'true', isSynced: false }, | ||||||
|   | |||||||
| @@ -371,7 +371,10 @@ function getLastSyncedPull() { | |||||||
|  |  | ||||||
| function setLastSyncedPull(entityChangeId) { | function setLastSyncedPull(entityChangeId) { | ||||||
|     const lastSyncedPullOption = becca.getOption('lastSyncedPull'); |     const lastSyncedPullOption = becca.getOption('lastSyncedPull'); | ||||||
|     lastSyncedPullOption.value = entityChangeId + ''; |  | ||||||
|  |     if (lastSyncedPullOption) { // might be null in initial sync when becca is not loaded | ||||||
|  |         lastSyncedPullOption.value = entityChangeId + ''; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     // this way we avoid updating entity_changes which otherwise means that we've never pushed all entity_changes |     // this way we avoid updating entity_changes which otherwise means that we've never pushed all entity_changes | ||||||
|     sql.execute("UPDATE options SET value = ? WHERE name = ?", [entityChangeId, 'lastSyncedPull']); |     sql.execute("UPDATE options SET value = ? WHERE name = ?", [entityChangeId, 'lastSyncedPull']); | ||||||
| @@ -389,7 +392,10 @@ function setLastSyncedPush(entityChangeId) { | |||||||
|     ws.setLastSyncedPush(entityChangeId); |     ws.setLastSyncedPush(entityChangeId); | ||||||
|  |  | ||||||
|     const lastSyncedPushOption = becca.getOption('lastSyncedPush'); |     const lastSyncedPushOption = becca.getOption('lastSyncedPush'); | ||||||
|     lastSyncedPushOption.value = entityChangeId + ''; |  | ||||||
|  |     if (lastSyncedPushOption) { // might be null in initial sync when becca is not loaded | ||||||
|  |         lastSyncedPushOption.value = entityChangeId + ''; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     // this way we avoid updating entity_changes which otherwise means that we've never pushed all entity_changes |     // this way we avoid updating entity_changes which otherwise means that we've never pushed all entity_changes | ||||||
|     sql.execute("UPDATE options SET value = ? WHERE name = ?", [entityChangeId, 'lastSyncedPush']); |     sql.execute("UPDATE options SET value = ? WHERE name = ?", [entityChangeId, 'lastSyncedPush']); | ||||||
|   | |||||||
| @@ -16,7 +16,10 @@ let mainWindow; | |||||||
| let setupWindow; | let setupWindow; | ||||||
|  |  | ||||||
| async function createExtraWindow(notePath, hoistedNoteId = 'root') { | async function createExtraWindow(notePath, hoistedNoteId = 'root') { | ||||||
|  |     const spellcheckEnabled = optionService.getOptionBool('spellCheckEnabled'); | ||||||
|  |  | ||||||
|     const {BrowserWindow} = require('electron'); |     const {BrowserWindow} = require('electron'); | ||||||
|  |  | ||||||
|     const win = new BrowserWindow({ |     const win = new BrowserWindow({ | ||||||
|         width: 1000, |         width: 1000, | ||||||
|         height: 800, |         height: 800, | ||||||
| @@ -25,7 +28,7 @@ async function createExtraWindow(notePath, hoistedNoteId = 'root') { | |||||||
|             enableRemoteModule: true, |             enableRemoteModule: true, | ||||||
|             nodeIntegration: true, |             nodeIntegration: true, | ||||||
|             contextIsolation: false, |             contextIsolation: false, | ||||||
|             spellcheck: optionService.getOptionBool('spellCheckEnabled') |             spellcheck: spellcheckEnabled | ||||||
|         }, |         }, | ||||||
|         frame: optionService.getOptionBool('nativeTitleBarVisible'), |         frame: optionService.getOptionBool('nativeTitleBarVisible'), | ||||||
|         icon: getIcon() |         icon: getIcon() | ||||||
| @@ -33,6 +36,8 @@ async function createExtraWindow(notePath, hoistedNoteId = 'root') { | |||||||
|  |  | ||||||
|     win.setMenuBarVisibility(false); |     win.setMenuBarVisibility(false); | ||||||
|     win.loadURL('http://127.0.0.1:' + await port + '/?extra=1&extraHoistedNoteId=' + hoistedNoteId + '#' + notePath); |     win.loadURL('http://127.0.0.1:' + await port + '/?extra=1&extraHoistedNoteId=' + hoistedNoteId + '#' + notePath); | ||||||
|  |  | ||||||
|  |     configureWebContents(win.webContents, spellcheckEnabled); | ||||||
| } | } | ||||||
|  |  | ||||||
| ipcMain.on('create-extra-window', (event, arg) => { | ipcMain.on('create-extra-window', (event, arg) => { | ||||||
| @@ -74,8 +79,10 @@ async function createMainWindow() { | |||||||
|     mainWindow.loadURL('http://127.0.0.1:' + await port); |     mainWindow.loadURL('http://127.0.0.1:' + await port); | ||||||
|     mainWindow.on('closed', () => mainWindow = null); |     mainWindow.on('closed', () => mainWindow = null); | ||||||
|  |  | ||||||
|     const {webContents} = mainWindow; |     configureWebContents(mainWindow.webContents, spellcheckEnabled); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | function configureWebContents(webContents, spellcheckEnabled) { | ||||||
|     require("@electron/remote/main").enable(webContents); |     require("@electron/remote/main").enable(webContents); | ||||||
|  |  | ||||||
|     webContents.on('new-window', (e, url) => { |     webContents.on('new-window', (e, url) => { | ||||||
|   | |||||||
| @@ -46,19 +46,15 @@ function register(router) { | |||||||
|         } |         } | ||||||
|     }); |     }); | ||||||
|  |  | ||||||
|     router.get('/share/api/images/:noteId/:filename', (req, res, next) => { |     router.get('/share/api/notes/:noteId', (req, res, next) => { | ||||||
|         const image = shaca.getNote(req.params.noteId); |         const {noteId} = req.params; | ||||||
|  |         const note = shaca.getNote(noteId); | ||||||
|  |  | ||||||
|         if (!image) { |         if (!note) { | ||||||
|             return res.status(404).send("Not found"); |             return res.status(404).send(`Note ${noteId} not found`); | ||||||
|         } |  | ||||||
|         else if (image.type !== 'image') { |  | ||||||
|             return res.status(400).send("Requested note is not an image"); |  | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         res.set('Content-Type', image.mime); |         res.json(note.getPojoWithAttributes()); | ||||||
|  |  | ||||||
|         res.send(image.getContent()); |  | ||||||
|     }); |     }); | ||||||
|  |  | ||||||
|     router.get('/share/api/notes/:noteId/download', (req, res, next) => { |     router.get('/share/api/notes/:noteId/download', (req, res, next) => { | ||||||
| @@ -66,7 +62,7 @@ function register(router) { | |||||||
|         const note = shaca.getNote(noteId); |         const note = shaca.getNote(noteId); | ||||||
|  |  | ||||||
|         if (!note) { |         if (!note) { | ||||||
|             return res.status(404).send(`Not found`); |             return res.status(404).send(`Note ${noteId} not found`); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         const utils = require("../services/utils"); |         const utils = require("../services/utils"); | ||||||
| @@ -81,20 +77,30 @@ function register(router) { | |||||||
|         res.send(note.getContent()); |         res.send(note.getContent()); | ||||||
|     }); |     }); | ||||||
|  |  | ||||||
|  |     router.get('/share/api/images/:noteId/:filename', (req, res, next) => { | ||||||
|  |         const image = shaca.getNote(req.params.noteId); | ||||||
|  |  | ||||||
|  |         if (!image) { | ||||||
|  |             return res.status(404).send(`Note ${noteId} not found`); | ||||||
|  |         } | ||||||
|  |         else if (image.type !== 'image') { | ||||||
|  |             return res.status(400).send("Requested note is not an image"); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         res.set('Content-Type', image.mime); | ||||||
|  |  | ||||||
|  |         res.send(image.getContent()); | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     // used for PDF viewing | ||||||
|     router.get('/share/api/notes/:noteId/view', (req, res, next) => { |     router.get('/share/api/notes/:noteId/view', (req, res, next) => { | ||||||
|         const {noteId} = req.params; |         const {noteId} = req.params; | ||||||
|         const note = shaca.getNote(noteId); |         const note = shaca.getNote(noteId); | ||||||
|  |  | ||||||
|         if (!note) { |         if (!note) { | ||||||
|             return res.status(404).send(`Not found`); |             return res.status(404).send(`Note ${noteId} not found`); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         const utils = require("../services/utils"); |  | ||||||
|  |  | ||||||
|         const filename = utils.formatDownloadTitle(note.title, note.type, note.mime); |  | ||||||
|  |  | ||||||
|         // res.setHeader('Content-Disposition', utils.getContentDisposition(filename)); |  | ||||||
|  |  | ||||||
|         res.setHeader("Cache-Control", "no-cache, no-store, must-revalidate"); |         res.setHeader("Cache-Control", "no-cache, no-store, must-revalidate"); | ||||||
|         res.setHeader('Content-Type', note.mime); |         res.setHeader('Content-Type', note.mime); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -89,6 +89,18 @@ class Attribute extends AbstractEntity { | |||||||
|  |  | ||||||
|         return this.shaca.getNote(this.value); |         return this.shaca.getNote(this.value); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     getPojo() { | ||||||
|  |         return { | ||||||
|  |             attributeId: this.attributeId, | ||||||
|  |             noteId: this.noteId, | ||||||
|  |             type: this.type, | ||||||
|  |             name: this.name, | ||||||
|  |             position: this.position, | ||||||
|  |             value: this.value, | ||||||
|  |             isInheritable: this.isInheritable | ||||||
|  |         }; | ||||||
|  |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| module.exports = Attribute; | module.exports = Attribute; | ||||||
|   | |||||||
| @@ -410,6 +410,19 @@ class Note extends AbstractEntity { | |||||||
|  |  | ||||||
|         return sharedAlias || this.noteId; |         return sharedAlias || this.noteId; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     getPojoWithAttributes() { | ||||||
|  |         return { | ||||||
|  |             noteId: this.noteId, | ||||||
|  |             title: this.title, | ||||||
|  |             type: this.type, | ||||||
|  |             mime: this.mime, | ||||||
|  |             utcDateModified: this.utcDateModified, | ||||||
|  |             attributes: this.getAttributes().map(attr => attr.getPojo()), | ||||||
|  |             parentNoteIds: this.parents.map(parentNote => parentNote.noteId), | ||||||
|  |             childNoteIds: this.children.map(child => child.noteId) | ||||||
|  |         }; | ||||||
|  |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| module.exports = Note; | module.exports = Note; | ||||||
|   | |||||||
| @@ -59,11 +59,7 @@ function load() { | |||||||
|         SELECT attributeId, noteId, type, name, value, isInheritable, position, utcDateModified  |         SELECT attributeId, noteId, type, name, value, isInheritable, position, utcDateModified  | ||||||
|         FROM attributes  |         FROM attributes  | ||||||
|         WHERE isDeleted = 0  |         WHERE isDeleted = 0  | ||||||
|           AND noteId IN (${noteIdStr}) |           AND noteId IN (${noteIdStr})`); | ||||||
|           AND ( |  | ||||||
|               (type = 'label' AND name IN ('archived', 'shareHiddenFromTree', 'shareAlias', 'shareOmitDefaultCss'))  |  | ||||||
|               OR (type = 'relation' AND name IN ('imageLink', 'template', 'shareCss')) |  | ||||||
|           )`, []); |  | ||||||
|  |  | ||||||
|     for (const row of rawAttributeRows) { |     for (const row of rawAttributeRows) { | ||||||
|         new Attribute(row); |         new Attribute(row); | ||||||
|   | |||||||
| @@ -2,7 +2,12 @@ | |||||||
| <html lang="en"> | <html lang="en"> | ||||||
| <head> | <head> | ||||||
|     <meta charset="utf-8"> |     <meta charset="utf-8"> | ||||||
|  |     <% if (note.hasRelation("shareFavicon")) { %> | ||||||
|  |     <link rel="shortcut icon" href="api/notes/<%= note.getRelation("shareFavicon").value %>/download"> | ||||||
|  |     <% } else { %> | ||||||
|     <link rel="shortcut icon" href="../favicon.ico"> |     <link rel="shortcut icon" href="../favicon.ico"> | ||||||
|  |     <% } %> | ||||||
|  |     <script src="../app/share.js"></script> | ||||||
|     <% if (!note.hasLabel("shareOmitDefaultCss")) { %> |     <% if (!note.hasLabel("shareOmitDefaultCss")) { %> | ||||||
|         <link href="../libraries/normalize.min.css" rel="stylesheet"> |         <link href="../libraries/normalize.min.css" rel="stylesheet"> | ||||||
|         <link href="../stylesheets/share.css" rel="stylesheet"> |         <link href="../stylesheets/share.css" rel="stylesheet"> | ||||||
| @@ -13,20 +18,28 @@ | |||||||
|     <% for (const cssRelation of note.getRelations("shareCss")) { %> |     <% for (const cssRelation of note.getRelations("shareCss")) { %> | ||||||
|         <link href="api/notes/<%= cssRelation.value %>/download" rel="stylesheet"> |         <link href="api/notes/<%= cssRelation.value %>/download" rel="stylesheet"> | ||||||
|     <% } %> |     <% } %> | ||||||
|  |     <% for (const jsRelation of note.getRelations("shareJs")) { %> | ||||||
|  |         <script type="module" src="api/notes/<%= jsRelation.value %>/download"></script> | ||||||
|  |     <% } %> | ||||||
|     <%- header %> |     <%- header %> | ||||||
|     <title><%= note.title %></title> |     <title><%= note.title %></title> | ||||||
| </head> | </head> | ||||||
| <body> | <body data-note-id="<%= note.noteId %>"> | ||||||
| <div id="layout"> | <div id="layout"> | ||||||
|     <div id="main"> |     <div id="main"> | ||||||
|         <% if (note.parents[0].noteId !== 'share' && note.parents.length !== 0) { %> |         <% if (note.parents[0].noteId !== 'share' && note.parents.length !== 0) { %> | ||||||
|             <nav id="parentLink"> |             <nav id="parentLink"> | ||||||
|                 parent: <a href="<%= note.parents[0].noteId %>" class="type-<%= note.parents[0].type %>"><%= note.parents[0].title %></a> |                 parent: <a href="<%= note.parents[0].noteId %>" | ||||||
|  |                            class="type-<%= note.parents[0].type %>"><%= note.parents[0].title %></a> | ||||||
|             </nav> |             </nav> | ||||||
|         <% } %> |         <% } %> | ||||||
|  |  | ||||||
|         <h1 id="title"><%= note.title %></h1> |         <h1 id="title"><%= note.title %></h1> | ||||||
|  |  | ||||||
|  |         <% if (note.hasLabel("pageUrl")) { %> | ||||||
|  |             <div id="noteClippedFrom">This note was originally clipped from <a href="<%= note.getLabelValue("pageUrl") %>"><%= note.getLabelValue("pageUrl") %></a></div> | ||||||
|  |         <% } %> | ||||||
|  |  | ||||||
|         <% if (note.type === 'book') { %> |         <% if (note.type === 'book') { %> | ||||||
|         <% } else if (isEmpty) { %> |         <% } else if (isEmpty) { %> | ||||||
|             <p>This note has no content.</p> |             <p>This note has no content.</p> | ||||||
| @@ -39,18 +52,19 @@ | |||||||
|         <% if (note.hasChildren()) { %> |         <% if (note.hasChildren()) { %> | ||||||
|             <nav id="childLinks" class="<% if (isEmpty) { %>grid<% } else { %>list<% } %>"> |             <nav id="childLinks" class="<% if (isEmpty) { %>grid<% } else { %>list<% } %>"> | ||||||
|                 <% if (!isEmpty) { %> |                 <% if (!isEmpty) { %> | ||||||
|                     <hr> |                     <div id="noteClippedFrom"> | ||||||
|                     <span>Child notes: </span> |                     <span>Child notes: </span> | ||||||
|  |                     <ul> | ||||||
|  |                         <% for (const childNote of note.getChildNotes()) { %> | ||||||
|  |                             <li> | ||||||
|  |                                 <a href="<%= childNote.shareId %>" | ||||||
|  |                                    class="type-<%= childNote.type %>"><%= childNote.title %></a> | ||||||
|  |                             </li> | ||||||
|  |                         <% } %> | ||||||
|  |                     </ul> | ||||||
|  |                 </div> | ||||||
|                 <% } %> |                 <% } %> | ||||||
|  |  | ||||||
|                 <ul> |  | ||||||
|                     <% for (const childNote of note.getChildNotes()) { %> |  | ||||||
|                         <li> |  | ||||||
|                             <a href="<%= childNote.shareId %>" |  | ||||||
|                                class="type-<%= childNote.type %>"><%= childNote.title %></a> |  | ||||||
|                         </li> |  | ||||||
|                     <% } %> |  | ||||||
|                 </ul> |  | ||||||
|             </nav> |             </nav> | ||||||
|         <% } %> |         <% } %> | ||||||
|     </div> |     </div> | ||||||
| @@ -63,14 +77,5 @@ | |||||||
|         </nav> |         </nav> | ||||||
|     <% } %> |     <% } %> | ||||||
| </div> | </div> | ||||||
|  |  | ||||||
| <script> |  | ||||||
|     (function () { |  | ||||||
|         const toggleMenuButton = document.getElementById('toggleMenuButton'); |  | ||||||
|         const layout = document.getElementById('layout'); |  | ||||||
|  |  | ||||||
|         toggleMenuButton.addEventListener('click', () => layout.classList.toggle('showMenu')); |  | ||||||
|     }()); |  | ||||||
| </script> |  | ||||||
| </body> | </body> | ||||||
| </html> | </html> | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user