mirror of
				https://github.com/zadam/trilium.git
				synced 2025-10-31 10:26:08 +01:00 
			
		
		
		
	notes_tree now has note_tree_id so we stricly distinguish between working on notes or note trees
This commit is contained in:
		
							
								
								
									
										21
									
								
								migrations/0037__add_note_tree_id.sql
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								migrations/0037__add_note_tree_id.sql
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,21 @@ | ||||
| CREATE TABLE [notes_tree_mig] ( | ||||
|     [note_tree_id] VARCHAR(30) PRIMARY KEY NOT NULL, | ||||
|     [note_id] VARCHAR(30) UNIQUE NOT NULL, | ||||
|     [note_pid] VARCHAR(30) NOT NULL, | ||||
|     [note_pos] INTEGER NOT NULL, | ||||
|     [is_expanded] BOOLEAN NULL , | ||||
|     date_modified INTEGER NOT NULL DEFAULT 0, | ||||
|     is_deleted INTEGER NOT NULL DEFAULT 0 | ||||
| ); | ||||
|  | ||||
| INSERT INTO notes_tree_mig (note_tree_id, note_id, note_pid, note_pos, is_expanded, date_modified, is_deleted) | ||||
|     SELECT 'TT' || SUBSTR(note_id, 3), note_id, note_pid, note_pos, is_expanded, date_modified, is_deleted FROM notes_tree; | ||||
|  | ||||
| UPDATE notes_tree_mig SET note_pid = 'TT' || SUBSTR(note_pid, 3) WHERE note_pid != 'root'; | ||||
|  | ||||
| DROP TABLE notes_tree; | ||||
| ALTER TABLE notes_tree_mig RENAME TO notes_tree; | ||||
|  | ||||
| CREATE INDEX `IDX_notes_tree_note_id` ON `notes_tree` ( | ||||
| 	`note_tree_id` | ||||
| ); | ||||
							
								
								
									
										1
									
								
								migrations/0038__rename_start_node.sql
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								migrations/0038__rename_start_node.sql
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| UPDATE options SET opt_name = 'start_note_tree_id' WHERE opt_name = 'start_node'; | ||||
| @@ -0,0 +1,7 @@ | ||||
| DROP TABLE recent_notes; | ||||
|  | ||||
| CREATE TABLE `recent_notes` ( | ||||
|     `note_tree_id` TEXT NOT NULL PRIMARY KEY, | ||||
|     `date_accessed` INTEGER NOT NULL , | ||||
|     is_deleted INT | ||||
| ); | ||||
| @@ -4,23 +4,23 @@ const contextMenu = (function() { | ||||
|     const treeEl = $("#tree"); | ||||
|  | ||||
|     function pasteAfter(node) { | ||||
|         const subjectNode = treeUtils.getNodeByKey(noteTree.getClipboardNoteId()); | ||||
|         const subjectNode = treeUtils.getNodeByNoteTreeId(noteTree.getClipboardNoteTreeId()); | ||||
|  | ||||
|         treeChanges.moveAfterNode(subjectNode, node); | ||||
|  | ||||
|         noteTree.setClipboardNoteId(null); | ||||
|         noteTree.setClipboardNoteTreeId(null); | ||||
|     } | ||||
|  | ||||
|     function pasteInto(node) { | ||||
|         const subjectNode = treeUtils.getNodeByKey(noteTree.getClipboardNoteId()); | ||||
|         const subjectNode = treeUtils.getNodeByNoteTreeId(noteTree.getClipboardNoteTreeId()); | ||||
|  | ||||
|         treeChanges.moveToNode(subjectNode, node); | ||||
|  | ||||
|         noteTree.setClipboardNoteId(null); | ||||
|         noteTree.setClipboardNoteTreeId(null); | ||||
|     } | ||||
|  | ||||
|     function cut(node) { | ||||
|         noteTree.setClipboardNoteId(node.key); | ||||
|         noteTree.setClipboardNoteTreeId(node.note_tree_id); | ||||
|     } | ||||
|  | ||||
|     const contextMenuSettings = { | ||||
| @@ -42,8 +42,8 @@ const contextMenu = (function() { | ||||
|         beforeOpen: (event, ui) => { | ||||
|             const node = $.ui.fancytree.getNode(ui.target); | ||||
|             // Modify menu entries depending on node status | ||||
|             treeEl.contextmenu("enableEntry", "pasteAfter", noteTree.getClipboardNoteId() !== null); | ||||
|             treeEl.contextmenu("enableEntry", "pasteInto", noteTree.getClipboardNoteId() !== null); | ||||
|             treeEl.contextmenu("enableEntry", "pasteAfter", noteTree.getClipboardNoteTreeId() !== null); | ||||
|             treeEl.contextmenu("enableEntry", "pasteInto", noteTree.getClipboardNoteTreeId() !== null); | ||||
|  | ||||
|             treeEl.contextmenu("enableEntry", "protectSubTree", protected_session.isProtectedSessionAvailable()); | ||||
|             treeEl.contextmenu("enableEntry", "unprotectSubTree", protected_session.isProtectedSessionAvailable()); | ||||
| @@ -59,10 +59,10 @@ const contextMenu = (function() { | ||||
|             const node = $.ui.fancytree.getNode(ui.target); | ||||
|  | ||||
|             if (ui.cmd === "insertNoteHere") { | ||||
|                 const parentKey = treeUtils.getParentKey(node); | ||||
|                 const parentNoteTreeId = treeUtils.getParentNoteTreeId(node); | ||||
|                 const isProtected = treeUtils.getParentProtectedStatus(node); | ||||
|  | ||||
|                 noteEditor.createNote(node, parentKey, 'after', isProtected); | ||||
|                 noteEditor.createNote(node, parentNoteTreeId, 'after', isProtected); | ||||
|             } | ||||
|             else if (ui.cmd === "insertChildNote") { | ||||
|                 noteEditor.createNote(node, node.key, 'into'); | ||||
|   | ||||
| @@ -13,31 +13,31 @@ const recentNotes = (function() { | ||||
|         type: 'GET', | ||||
|         error: () => showError("Error getting recent notes.") | ||||
|     }).then(result => { | ||||
|         list = result.map(r => r.note_id); | ||||
|         list = result.map(r => r.note_tree_id); | ||||
|     }); | ||||
|  | ||||
|     function addRecentNote(noteTreeId, noteContentId) { | ||||
|     function addRecentNote(noteTreeId) { | ||||
|         setTimeout(() => { | ||||
|             // we include the note into recent list only if the user stayed on the note at least 5 seconds | ||||
|             if (noteTreeId === noteEditor.getCurrentNoteId() || noteContentId === noteEditor.getCurrentNoteId()) { | ||||
|             if (noteTreeId === noteEditor.getCurrentNoteId()) { | ||||
|                 $.ajax({ | ||||
|                     url: baseApiUrl + 'recent-notes/' + noteTreeId, | ||||
|                     type: 'PUT', | ||||
|                     error: () => showError("Error setting recent notes.") | ||||
|                 }).then(result => { | ||||
|                     list = result.map(r => r.note_id); | ||||
|                     list = result.map(r => r.note_tree_id); | ||||
|                 }); | ||||
|             } | ||||
|         }, 1500); | ||||
|     } | ||||
|  | ||||
|     function removeRecentNote(noteIdToRemove) { | ||||
|     function removeRecentNote(noteTreeIdToRemove) { | ||||
|         $.ajax({ | ||||
|             url: baseApiUrl + 'recent-notes/' + noteIdToRemove, | ||||
|             url: baseApiUrl + 'recent-notes/' + noteTreeIdToRemove, | ||||
|             type: 'DELETE', | ||||
|             error: () => showError("Error removing note from recent notes.") | ||||
|         }).then(result => { | ||||
|             list = result.map(r => r.note_id); | ||||
|             list = result.map(r => r.note_tree_id); | ||||
|         }); | ||||
|     } | ||||
|  | ||||
| @@ -56,15 +56,15 @@ const recentNotes = (function() { | ||||
|         // remove the current note | ||||
|         const recNotes = list.filter(note => note !== noteEditor.getCurrentNoteId()); | ||||
|  | ||||
|         $.each(recNotes, (key, valueNoteId) => { | ||||
|             const noteTitle = treeUtils.getFullName(valueNoteId); | ||||
|         $.each(recNotes, (key, valueNoteTreeId) => { | ||||
|             const noteTitle = treeUtils.getFullName(valueNoteTreeId); | ||||
|  | ||||
|             if (!noteTitle) { | ||||
|                 return; | ||||
|             } | ||||
|  | ||||
|             const option = $("<option></option>") | ||||
|                 .attr("value", valueNoteId) | ||||
|                 .attr("value", valueNoteTreeId) | ||||
|                 .text(noteTitle); | ||||
|  | ||||
|             // select the first one (most recent one) by default | ||||
|   | ||||
| @@ -92,7 +92,7 @@ const noteEditor = (function() { | ||||
|  | ||||
|         const title = noteTitleEl.val(); | ||||
|  | ||||
|         treeUtils.getNodeByKey(note.detail.note_id).setTitle(title); | ||||
|         noteTree.getCurrentNode().setTitle(title); | ||||
|  | ||||
|         note.detail.note_title = title; | ||||
|     } | ||||
| @@ -121,7 +121,7 @@ const noteEditor = (function() { | ||||
|  | ||||
|     let newNoteCreated = false; | ||||
|  | ||||
|     async function createNote(node, parentKey, target, isProtected) { | ||||
|     async function createNote(node, parentTreeId, target, isProtected) { | ||||
|         // if isProtected isn't available (user didn't enter password yet), then note is created as unencrypted | ||||
|         // but this is quite weird since user doesn't see where the note is being created so it shouldn't occur often | ||||
|         if (!isProtected || !protected_session.isProtectedSessionAvailable()) { | ||||
| @@ -131,12 +131,12 @@ const noteEditor = (function() { | ||||
|         const newNoteName = "new note"; | ||||
|  | ||||
|         const result = await $.ajax({ | ||||
|             url: baseApiUrl + 'notes/' + parentKey + '/children' , | ||||
|             url: baseApiUrl + 'notes/' + parentTreeId + '/children' , | ||||
|             type: 'POST', | ||||
|             data: JSON.stringify({ | ||||
|                 note_title: newNoteName, | ||||
|                 target: target, | ||||
|                 target_note_id: node.key, | ||||
|                 target_note_id: node.note_tree_id, | ||||
|                 is_protected: isProtected | ||||
|             }), | ||||
|             contentType: "application/json" | ||||
| @@ -144,13 +144,14 @@ const noteEditor = (function() { | ||||
|  | ||||
|         const newNode = { | ||||
|             title: newNoteName, | ||||
|             key: result.note_id, | ||||
|             key: counter++, | ||||
|             note_id: result.note_id, | ||||
|             note_tree_id: result.note_tree_id, | ||||
|             is_protected: isProtected, | ||||
|             extraClasses: isProtected ? "protected" : "" | ||||
|         }; | ||||
|  | ||||
|         glob.allNoteIds.push(result.note_id); | ||||
|         glob.allNoteIds.push(result.note_tree_id); | ||||
|  | ||||
|         newNoteCreated = true; | ||||
|  | ||||
| @@ -167,11 +168,6 @@ const noteEditor = (function() { | ||||
|         showMessage("Created!"); | ||||
|     } | ||||
|  | ||||
|     function setTreeBasedOnProtectedStatus(note) { | ||||
|         const node = treeUtils.getNodeByKey(note.detail.note_id); | ||||
|         node.toggleClass("protected", !!note.detail.is_protected); | ||||
|     } | ||||
|  | ||||
|     function setNoteBackgroundIfProtected(note) { | ||||
|         if (note.detail.is_protected) { | ||||
|             $(".note-editable").addClass("protected"); | ||||
| @@ -184,7 +180,7 @@ const noteEditor = (function() { | ||||
|             unprotectButton.hide(); | ||||
|         } | ||||
|  | ||||
|         setTreeBasedOnProtectedStatus(note); | ||||
|         noteTree.setCurrentNoteTreeBasedOnProtectedStatus(); | ||||
|     } | ||||
|  | ||||
|     async function loadNoteToEditor(noteId) { | ||||
| @@ -217,10 +213,6 @@ const noteEditor = (function() { | ||||
|  | ||||
|         noteDetailEl.summernote('code', currentNote.detail.note_text); | ||||
|  | ||||
|         document.location.hash = noteId; | ||||
|  | ||||
|         recentNotes.addRecentNote(noteId, currentNote.detail.note_id); | ||||
|  | ||||
|         noteChangeDisabled = false; | ||||
|  | ||||
|         setNoteBackgroundIfProtected(currentNote); | ||||
|   | ||||
| @@ -3,35 +3,59 @@ | ||||
| const noteTree = (function() { | ||||
|     const noteDetailEl = $('#note-detail'); | ||||
|     const treeEl = $("#tree"); | ||||
|     let startNoteId = null; | ||||
|     let startNoteTreeId = null; | ||||
|     let treeLoadTime = null; | ||||
|     let clipboardNoteId = null; | ||||
|     let clipboardNoteTreeId = null; | ||||
|     let notesMap = {}; | ||||
|     let parentToNotes = {}; | ||||
|     let counter = 1; | ||||
|     let noteTreeIdToKey = {}; | ||||
|  | ||||
|     function getNoteTreeIdFromKey(key) { | ||||
|         const node = treeUtils.getNodeByKey(key); | ||||
|  | ||||
|         return node.note_tree_id; | ||||
|     } | ||||
|  | ||||
|     function getKeyFromNoteTreeId(noteTreeId) { | ||||
|         return noteTreeIdToKey[noteTreeId]; | ||||
|     } | ||||
|  | ||||
|     function getTreeLoadTime() { | ||||
|         return treeLoadTime; | ||||
|     } | ||||
|  | ||||
|     function getClipboardNoteId() { | ||||
|         return clipboardNoteId; | ||||
|     function getClipboardNoteTreeId() { | ||||
|         return clipboardNoteTreeId; | ||||
|     } | ||||
|  | ||||
|     function setClipboardNoteId(cbNoteId) { | ||||
|         clipboardNoteId = cbNoteId; | ||||
|     function setClipboardNoteTreeId(cbNoteId) { | ||||
|         clipboardNoteTreeId = cbNoteId; | ||||
|     } | ||||
|  | ||||
|     function prepareNoteTree(notes) { showAppIfHidden(); | ||||
|         parentToNotes = {}; | ||||
|         notesMap = {}; | ||||
|  | ||||
|         for (const note of notes) { | ||||
|             if (!parentToNotes[note.note_pid]) { | ||||
|                 parentToNotes[note.note_pid] = []; | ||||
|             } | ||||
|  | ||||
|             notesMap[note.note_tree_id] = note; | ||||
|             parentToNotes[note.note_pid].push(note.note_tree_id); | ||||
|         } | ||||
|  | ||||
|     function prepareNoteTree() { | ||||
|         glob.allNoteIds = Object.keys(notesMap); | ||||
|  | ||||
|         return prepareNoteTreeInner(parentToNotes['root']); | ||||
|     } | ||||
|  | ||||
|     function prepareNoteTreeInner(noteIds) { | ||||
|     function prepareNoteTreeInner(noteTreeIds) { | ||||
|         const noteList = []; | ||||
|  | ||||
|         for (const noteId of noteIds) { | ||||
|             const note = notesMap[noteId]; | ||||
|         for (const noteTreeId of noteTreeIds) { | ||||
|             const note = notesMap[noteTreeId]; | ||||
|  | ||||
|             note.title = note.note_title; | ||||
|  | ||||
| @@ -39,14 +63,16 @@ const noteTree = (function() { | ||||
|                 note.extraClasses = "protected"; | ||||
|             } | ||||
|  | ||||
|             note.key = note.note_id; | ||||
|             note.key = counter++ + ""; // key needs to be string | ||||
|             note.expanded = note.is_expanded; | ||||
|  | ||||
|             if (parentToNotes[noteId] && parentToNotes[noteId].length > 0) { | ||||
|             noteTreeIdToKey[noteTreeId] = note.key; | ||||
|  | ||||
|             if (parentToNotes[noteTreeId] && parentToNotes[noteTreeId].length > 0) { | ||||
|                 note.folder = true; | ||||
|  | ||||
|                 if (note.expanded) { | ||||
|                     note.children = prepareNoteTreeInner(parentToNotes[noteId], notesMap, parentToNotes); | ||||
|                     note.children = prepareNoteTreeInner(parentToNotes[noteTreeId], notesMap, parentToNotes); | ||||
|                 } | ||||
|                 else { | ||||
|                     note.lazy = true; | ||||
| @@ -59,27 +85,27 @@ const noteTree = (function() { | ||||
|         return noteList; | ||||
|     } | ||||
|  | ||||
|     function setExpandedToServer(note_id, is_expanded) { | ||||
|         const expandedNum = is_expanded ? 1 : 0; | ||||
|     function setExpandedToServer(noteTreeId, isExpanded) { | ||||
|         const expandedNum = isExpanded ? 1 : 0; | ||||
|  | ||||
|         $.ajax({ | ||||
|             url: baseApiUrl + 'notes/' + note_id + '/expanded/' + expandedNum, | ||||
|             url: baseApiUrl + 'notes/' + noteTreeId + '/expanded/' + expandedNum, | ||||
|             type: 'PUT', | ||||
|             contentType: "application/json", | ||||
|             success: result => {} | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     function initFancyTree(notes) { | ||||
|     function initFancyTree(noteTree) { | ||||
|         const keybindings = { | ||||
|             "insert": node => { | ||||
|                 const parentKey = treeUtils.getParentKey(node); | ||||
|                 const parentNoteTreeId = treeUtils.getParentNoteTreeId(node); | ||||
|                 const isProtected = treeUtils.getParentProtectedStatus(node); | ||||
|  | ||||
|                 noteEditor.createNote(node, parentKey, 'after', isProtected); | ||||
|                 noteEditor.createNote(node, parentNoteTreeId, 'after', isProtected); | ||||
|             }, | ||||
|             "ctrl+insert": node => { | ||||
|                 noteEditor.createNote(node, node.key, 'into', node.data.is_protected); | ||||
|                 noteEditor.createNote(node, node.note_id, 'into', node.data.is_protected); | ||||
|             }, | ||||
|             "del": node => { | ||||
|                 treeChanges.deleteNode(node); | ||||
| @@ -116,22 +142,26 @@ const noteTree = (function() { | ||||
|         treeEl.fancytree({ | ||||
|             autoScroll: true, | ||||
|             extensions: ["hotkeys", "filter", "dnd"], | ||||
|             source: notes, | ||||
|             source: noteTree, | ||||
|             scrollParent: $("#tree"), | ||||
|             activate: (event, data) => { | ||||
|                 const node = data.node.data; | ||||
|  | ||||
|                 document.location.hash = node.note_tree_id; | ||||
|  | ||||
|                 recentNotes.addRecentNote(node.note_tree_id); | ||||
|  | ||||
|                 noteEditor.switchToNote(node.note_id); | ||||
|             }, | ||||
|             expand: (event, data) => { | ||||
|                 setExpandedToServer(data.node.key, true); | ||||
|                 setExpandedToServer(getNoteTreeIdFromKey(data.node.key), true); | ||||
|             }, | ||||
|             collapse: (event, data) => { | ||||
|                 setExpandedToServer(data.node.key, false); | ||||
|                 setExpandedToServer(getNoteTreeIdFromKey(data.node.key), false); | ||||
|             }, | ||||
|             init: (event, data) => { | ||||
|                 if (startNoteId) { | ||||
|                     data.tree.activateKey(startNoteId); | ||||
|                 if (startNoteTreeId) { | ||||
|                     treeUtils.activateNode(startNoteTreeId); | ||||
|                 } | ||||
|             }, | ||||
|             hotkeys: { | ||||
| @@ -197,13 +227,14 @@ const noteTree = (function() { | ||||
|                 } | ||||
|             }, | ||||
|             lazyLoad: function(event, data){ | ||||
|                 const node = data.node; | ||||
|                 const node = data.node.data; | ||||
|                 const noteTreeId = node.note_tree_id; | ||||
|  | ||||
|                 if (parentToNotes[node.key]) { | ||||
|                     data.result = prepareNoteTreeInner(parentToNotes[node.key]); | ||||
|                 if (parentToNotes[noteTreeId]) { | ||||
|                     data.result = prepareNoteTreeInner(parentToNotes[noteTreeId]); | ||||
|                 } | ||||
|                 else { | ||||
|                     console.log("No children. This shouldn't happen."); | ||||
|                     console.log("No children for " + noteTreeId + ". This shouldn't happen."); | ||||
|                 } | ||||
|             } | ||||
|         }); | ||||
| @@ -212,28 +243,26 @@ const noteTree = (function() { | ||||
|     } | ||||
|  | ||||
|     async function reload() { | ||||
|         const notesMap = await loadTree(); | ||||
|         const notes = await loadTree(); | ||||
|  | ||||
|         // this will also reload the note content | ||||
|         await treeEl.fancytree('getTree').reload(notesMap); | ||||
|         await treeEl.fancytree('getTree').reload(notes); | ||||
|     } | ||||
|  | ||||
|     function loadTree() { | ||||
|         return $.get(baseApiUrl + 'tree').then(resp => { | ||||
|             notesMap = resp.notes_map; | ||||
|             parentToNotes = resp.parent_to_notes; | ||||
|             startNoteId = resp.start_note_id; | ||||
|             startNoteTreeId = resp.start_note_tree_id; | ||||
|             treeLoadTime = resp.tree_load_time; | ||||
|  | ||||
|             if (document.location.hash) { | ||||
|                 startNoteId = document.location.hash.substr(1); // strip initial # | ||||
|                 startNoteTreeId = document.location.hash.substr(1); // strip initial # | ||||
|             } | ||||
|  | ||||
|             return prepareNoteTree(); | ||||
|             return prepareNoteTree(resp.notes); | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     $(() => loadTree().then(notesMap => initFancyTree(notesMap))); | ||||
|     $(() => loadTree().then(noteTree => initFancyTree(noteTree))); | ||||
|  | ||||
|     function collapseTree() { | ||||
|         treeEl.fancytree("getRootNode").visit(node => { | ||||
| @@ -244,7 +273,7 @@ const noteTree = (function() { | ||||
|     $(document).bind('keydown', 'alt+c', collapseTree); | ||||
|  | ||||
|     function scrollToCurrentNote() { | ||||
|         const node = treeUtils.getNodeByKey(noteEditor.getCurrentNoteId()); | ||||
|         const node = treeUtils.getNodeByNoteTreeId(noteEditor.getCurrentNoteId()); | ||||
|  | ||||
|         if (node) { | ||||
|             node.makeVisible({scrollIntoView: true}); | ||||
| @@ -281,6 +310,22 @@ const noteTree = (function() { | ||||
|         return notesMap[noteId]; | ||||
|     } | ||||
|  | ||||
|     // note that if you want to access data like note_id or is_protected, you need to go into "data" property | ||||
|     function getCurrentNode() { | ||||
|         return treeEl.fancytree("getActiveNode"); | ||||
|     } | ||||
|  | ||||
|     function getCurrentNoteTreeId() { | ||||
|         const node = getCurrentNode(); | ||||
|         return node.note_tree_id; | ||||
|     } | ||||
|  | ||||
|     function setCurrentNoteTreeBasedOnProtectedStatus() { | ||||
|         const node = getCurrentNode(); | ||||
|  | ||||
|         node.toggleClass("protected", !!node.data.is_protected); | ||||
|     } | ||||
|  | ||||
|     $("button#reset-search-button").click(resetSearch); | ||||
|  | ||||
|     $("input[name=search]").keyup(e => { | ||||
| @@ -308,12 +353,17 @@ const noteTree = (function() { | ||||
|  | ||||
|     return { | ||||
|         getTreeLoadTime, | ||||
|         getClipboardNoteId, | ||||
|         setClipboardNoteId, | ||||
|         getClipboardNoteTreeId, | ||||
|         setClipboardNoteTreeId, | ||||
|         reload, | ||||
|         collapseTree, | ||||
|         scrollToCurrentNote, | ||||
|         toggleSearch, | ||||
|         getByNoteId | ||||
|         getByNoteId, | ||||
|         getKeyFromNoteTreeId, | ||||
|         getNoteTreeIdFromKey, | ||||
|         setCurrentNoteTreeBasedOnProtectedStatus, | ||||
|         getCurrentNode, | ||||
|         getCurrentNoteTreeId, | ||||
|     }; | ||||
| })(); | ||||
| @@ -37,7 +37,7 @@ const protected_session = (function() { | ||||
|                 open: () => { | ||||
|                     if (!modal) { | ||||
|                         // dialog steals focus for itself, which is not what we want for non-modal (viewing) | ||||
|                         treeUtils.getNodeByKey(noteEditor.getCurrentNoteId()).setFocus(); | ||||
|                         noteTree.getCurrentNode().setFocus(); | ||||
|                     } | ||||
|                 } | ||||
|             }); | ||||
|   | ||||
| @@ -52,7 +52,7 @@ const treeChanges = (function() { | ||||
|  | ||||
|                     glob.allNoteIds = glob.allNoteIds.filter(e => e !== node.key); | ||||
|  | ||||
|                     recentNotes.removeRecentNote(node.key); | ||||
|                     recentNotes.removeRecentNote(node.note_tree_id); | ||||
|  | ||||
|                     let next = node.getNextSibling(); | ||||
|                     if (!next) { | ||||
|   | ||||
| @@ -3,37 +3,41 @@ | ||||
| const treeUtils = (function() { | ||||
|     const treeEl = $("#tree"); | ||||
|  | ||||
|     function getParentKey(node) { | ||||
|         return (node.getParent() === null || node.getParent().key === "root_1") ? "root" : node.getParent().key; | ||||
|     function getParentNoteTreeId(node) { | ||||
|         return node.note_pid; | ||||
|     } | ||||
|  | ||||
|     function getParentProtectedStatus(node) { | ||||
|         return node.getParent() === null ? 0 : node.getParent().data.is_protected; | ||||
|     } | ||||
|  | ||||
|     function getNodeByKey(noteId) { | ||||
|         return treeEl.fancytree('getNodeByKey', noteId); | ||||
|     function getNodeByKey(key) { | ||||
|         return treeEl.fancytree('getNodeByKey', key); | ||||
|     } | ||||
|  | ||||
|     async function activateNode(noteIdToActivate) { | ||||
|         const noteIdPath = [ noteIdToActivate ]; | ||||
|     function getNodeByNoteTreeId(noteTreeId) { | ||||
|         const key = noteTree.getKeyFromNoteTreeId(noteTreeId); | ||||
|  | ||||
|         let note = noteTree.getByNoteId(noteIdToActivate); | ||||
|         return getNodeByKey(key); | ||||
|     } | ||||
|  | ||||
|     async function activateNode(noteTreeIdToActivate) { | ||||
|         const noteTreeIdPath = [ noteTreeIdToActivate ]; | ||||
|  | ||||
|         let note = noteTree.getByNoteId(noteTreeIdToActivate); | ||||
|  | ||||
|         while (note) { | ||||
|             if (note.note_pid !== 'root') { | ||||
|                 noteIdPath.push(note.note_pid); | ||||
|                 noteTreeIdPath.push(note.note_pid); | ||||
|             } | ||||
|  | ||||
|             note = noteTree.getByNoteId(note.note_pid); | ||||
|         } | ||||
|  | ||||
|         for (const noteId of noteIdPath.reverse()) { | ||||
|             console.log("Activating/expanding " + noteId); | ||||
|         for (const noteTreeId of noteTreeIdPath.reverse()) { | ||||
|             const node = treeUtils.getNodeByNoteTreeId(noteTreeId); | ||||
|  | ||||
|             const node = treeUtils.getNodeByKey(noteId); | ||||
|  | ||||
|             if (noteId !== noteIdToActivate) { | ||||
|             if (noteTreeId !== noteTreeIdToActivate) { | ||||
|                 await node.setExpanded(); | ||||
|             } | ||||
|             else { | ||||
| @@ -57,8 +61,8 @@ const treeUtils = (function() { | ||||
|         return noteTitle; | ||||
|     } | ||||
|  | ||||
|     function getFullName(noteId) { | ||||
|         let note = noteTree.getByNoteId(noteId); | ||||
|     function getFullName(noteTreeId) { | ||||
|         let note = noteTree.getByNoteId(noteTreeId); | ||||
|  | ||||
|         if (note === null) { | ||||
|             return "[unknown]"; | ||||
| @@ -76,9 +80,10 @@ const treeUtils = (function() { | ||||
|     } | ||||
|  | ||||
|     return { | ||||
|         getParentKey, | ||||
|         getParentNoteTreeId, | ||||
|         getParentProtectedStatus, | ||||
|         getNodeByKey, | ||||
|         getNodeByNoteTreeId, | ||||
|         activateNode, | ||||
|         getNoteTitle, | ||||
|         getFullName | ||||
|   | ||||
| @@ -4,7 +4,6 @@ const express = require('express'); | ||||
| const router = express.Router(); | ||||
| const auth = require('../../services/auth'); | ||||
| const sql = require('../../services/sql'); | ||||
| const options = require('../../services/options'); | ||||
| const utils = require('../../services/utils'); | ||||
| const notes = require('../../services/notes'); | ||||
| const protected_session = require('../../services/protected_session'); | ||||
| @@ -14,33 +13,32 @@ const RequestContext = require('../../services/request_context'); | ||||
| router.get('/:noteId', auth.checkApiAuth, async (req, res, next) => { | ||||
|     const noteId = req.params.noteId; | ||||
|  | ||||
|     await options.setOption('start_node', noteId); | ||||
|  | ||||
|     const detail = await sql.getSingleResult("select * from notes where note_id = ?", [noteId]); | ||||
|  | ||||
|     if (detail.is_protected) { | ||||
|         const dataKey = protected_session.getDataKey(req); | ||||
|  | ||||
|         detail.note_title = data_encryption.decryptString(dataKey, data_encryption.noteTitleIv(noteId), detail.note_title); | ||||
|         detail.note_text = data_encryption.decryptString(dataKey, data_encryption.noteTextIv(noteId), detail.note_text); | ||||
|         detail.note_title = data_encryption.decryptString(dataKey, data_encryption.noteTitleIv(detail.note_id), detail.note_title); | ||||
|         detail.note_text = data_encryption.decryptString(dataKey, data_encryption.noteTextIv(detail.note_id), detail.note_text); | ||||
|     } | ||||
|  | ||||
|     res.send({ | ||||
|         detail: detail, | ||||
|         images: await sql.getResults("select * from images where note_id = ? order by note_offset", [noteId]), | ||||
|         images: await sql.getResults("select * from images where note_id = ? order by note_offset", [detail.note_id]), | ||||
|         loadTime: utils.nowTimestamp() | ||||
|     }); | ||||
| }); | ||||
|  | ||||
| router.post('/:parentNoteId/children', async (req, res, next) => { | ||||
|     const parentNoteId = req.params.parentNoteId; | ||||
| router.post('/:parentNoteTreeId/children', async (req, res, next) => { | ||||
|     const parentNoteTreeId = req.params.parentNoteTreeId; | ||||
|     const browserId = utils.browserId(req); | ||||
|     const note = req.body; | ||||
|  | ||||
|     const noteId = await notes.createNewNote(parentNoteId, note, browserId); | ||||
|     const { noteId, noteTreeId } = await notes.createNewNote(parentNoteTreeId, note, browserId); | ||||
|  | ||||
|     res.send({ | ||||
|         'note_id': noteId | ||||
|         'note_id': noteId, | ||||
|         'note_tree_id': noteTreeId | ||||
|     }); | ||||
| }); | ||||
|  | ||||
|   | ||||
| @@ -6,27 +6,32 @@ const sql = require('../../services/sql'); | ||||
| const auth = require('../../services/auth'); | ||||
| const utils = require('../../services/utils'); | ||||
| const sync_table = require('../../services/sync_table'); | ||||
| const options = require('../../services/options'); | ||||
|  | ||||
| router.get('', auth.checkApiAuth, async (req, res, next) => { | ||||
|     res.send(await getRecentNotes()); | ||||
| }); | ||||
|  | ||||
| router.put('/:noteId', auth.checkApiAuth, async (req, res, next) => { | ||||
| router.put('/:noteTreeId', auth.checkApiAuth, async (req, res, next) => { | ||||
|     const noteTreeId = req.params.noteTreeId; | ||||
|  | ||||
|     await sql.replace('recent_notes', { | ||||
|         note_id: req.params.noteId, | ||||
|         note_tree_id: noteTreeId, | ||||
|         date_accessed: utils.nowTimestamp(), | ||||
|         is_deleted: 0 | ||||
|     }); | ||||
|  | ||||
|     await sync_table.addRecentNoteSync(req.params.noteId); | ||||
|     await sync_table.addRecentNoteSync(noteTreeId); | ||||
|  | ||||
|     await options.setOption('start_note_tree_id', noteTreeId); | ||||
|  | ||||
|     res.send(await getRecentNotes()); | ||||
| }); | ||||
|  | ||||
| router.delete('/:noteId', auth.checkApiAuth, async (req, res, next) => { | ||||
|     await sql.execute('UPDATE recent_notes SET is_deleted = 1 WHERE note_id = ?', [req.params.noteId]); | ||||
| router.delete('/:noteTreeId', auth.checkApiAuth, async (req, res, next) => { | ||||
|     await sql.execute('UPDATE recent_notes SET is_deleted = 1 WHERE note_tree_id = ?', [req.params.noteTreeId]); | ||||
|  | ||||
|     await sync_table.addRecentNoteSync(req.params.noteId); | ||||
|     await sync_table.addRecentNoteSync(req.params.noteTreeId); | ||||
|  | ||||
|     res.send(await getRecentNotes()); | ||||
| }); | ||||
|   | ||||
| @@ -21,28 +21,17 @@ router.get('/', auth.checkApiAuth, async (req, res, next) => { | ||||
|         + "where notes.is_deleted = 0 and notes_tree.is_deleted = 0 " | ||||
|         + "order by note_pid, note_pos"); | ||||
|  | ||||
|     const parentToNotes = {}; | ||||
|     const notesMap = {}; | ||||
|  | ||||
|     const dataKey = protected_session.getDataKey(req); | ||||
|  | ||||
|     for (const note of notes) { | ||||
|         if (note.is_protected) { | ||||
|             note.note_title = data_encryption.decryptString(dataKey, data_encryption.noteTitleIv(note.note_id), note.note_title); | ||||
|         } | ||||
|  | ||||
|         if (!parentToNotes[note.note_pid]) { | ||||
|             parentToNotes[note.note_pid] = []; | ||||
|         } | ||||
|  | ||||
|         notesMap[note.note_id] = note; | ||||
|         parentToNotes[note.note_pid].push(note.note_id); | ||||
|     } | ||||
|  | ||||
|     res.send({ | ||||
|         notes_map: notesMap, | ||||
|         parent_to_notes: parentToNotes, | ||||
|         start_note_id: await options.getOption('start_node'), | ||||
|         notes: notes, | ||||
|         start_note_tree_id: await options.getOption('start_note_tree_id'), | ||||
|         tree_load_time: utils.nowTimestamp() | ||||
|     }); | ||||
| }); | ||||
|   | ||||
| @@ -4,7 +4,7 @@ const options = require('./options'); | ||||
| const fs = require('fs-extra'); | ||||
| const log = require('./log'); | ||||
|  | ||||
| const APP_DB_VERSION = 36; | ||||
| const APP_DB_VERSION = 39; | ||||
| const MIGRATIONS_DIR = "migrations"; | ||||
|  | ||||
| async function migrate() { | ||||
|   | ||||
| @@ -8,6 +8,7 @@ const sync_table = require('./sync_table'); | ||||
|  | ||||
| async function createNewNote(parentNoteId, note, browserId) { | ||||
|     const noteId = utils.newNoteId(); | ||||
|     const noteTreeId = utils.newNoteId(); | ||||
|  | ||||
|     let newNotePos = 0; | ||||
|  | ||||
| @@ -50,6 +51,7 @@ async function createNewNote(parentNoteId, note, browserId) { | ||||
|         }); | ||||
|  | ||||
|         await sql.insert("notes_tree", { | ||||
|             'note_tree_id': noteTreeId, | ||||
|             'note_id': noteId, | ||||
|             'note_pid': parentNoteId, | ||||
|             'note_pos': newNotePos, | ||||
| @@ -58,7 +60,11 @@ async function createNewNote(parentNoteId, note, browserId) { | ||||
|             'is_deleted': 0 | ||||
|         }); | ||||
|     }); | ||||
|     return noteId; | ||||
|  | ||||
|     return { | ||||
|         noteId, | ||||
|         noteTreeId | ||||
|     }; | ||||
| } | ||||
|  | ||||
| async function encryptNote(note, ctx) { | ||||
|   | ||||
| @@ -22,8 +22,8 @@ async function addOptionsSync(optName, sourceId) { | ||||
|     await addEntitySync("options", optName, sourceId); | ||||
| } | ||||
|  | ||||
| async function addRecentNoteSync(noteId, sourceId) { | ||||
|     await addEntitySync("recent_notes", noteId, sourceId); | ||||
| async function addRecentNoteSync(noteTreeId, sourceId) { | ||||
|     await addEntitySync("recent_notes", noteTreeId, sourceId); | ||||
| } | ||||
|  | ||||
| async function addEntitySync(entityName, entityId, sourceId) { | ||||
|   | ||||
| @@ -137,10 +137,10 @@ | ||||
|       </form> | ||||
|     </div> | ||||
|  | ||||
|     <div id="protected-session-password-dialog" title="Encrypted note" style="display: none;"> | ||||
|     <div id="protected-session-password-dialog" title="Protected session" style="display: none;"> | ||||
|       <form id="protected-session-password-form"> | ||||
|         <div class="form-group"> | ||||
|           <label for="protected-session-password">This note is encrypted. Enter password to show it:</label> | ||||
|           <label for="protected-session-password">To proceed with requested action you need to enter protected session by entering password:</label> | ||||
|           <input id="protected-session-password" style="width: 250px;" type="password"> | ||||
|           <button class="btn btn-sm">Show</button> | ||||
|         </div> | ||||
|   | ||||
		Reference in New Issue
	
	Block a user