mirror of
				https://github.com/zadam/trilium.git
				synced 2025-10-31 10:26:08 +01:00 
			
		
		
		
	duplicate (single) note
This commit is contained in:
		
							
								
								
									
										22
									
								
								db/migrations/0148__make_isExpanded_not_null.sql
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								db/migrations/0148__make_isExpanded_not_null.sql
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,22 @@ | |||||||
|  | CREATE TABLE IF NOT EXISTS "mig_branches" ( | ||||||
|  |                                           `branchId`	TEXT NOT NULL, | ||||||
|  |                                           `noteId`	TEXT NOT NULL, | ||||||
|  |                                           `parentNoteId`	TEXT NOT NULL, | ||||||
|  |                                           `notePosition`	INTEGER NOT NULL, | ||||||
|  |                                           `prefix`	TEXT, | ||||||
|  |                                           `isExpanded`	INTEGER NOT NULL DEFAULT 0, | ||||||
|  |                                           `isDeleted`	INTEGER NOT NULL DEFAULT 0, | ||||||
|  |                                           `utcDateModified`	TEXT NOT NULL, | ||||||
|  |                                           utcDateCreated TEXT NOT NULL, | ||||||
|  |                                           hash TEXT DEFAULT "" NOT NULL, | ||||||
|  |                                           PRIMARY KEY(`branchId`)); | ||||||
|  |  | ||||||
|  | INSERT INTO mig_branches (branchId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, utcDateModified, utcDateCreated, hash) | ||||||
|  | SELECT branchId, noteId, parentNoteId, notePosition, prefix, COALESCE(isExpanded, 0), isDeleted, utcDateModified, utcDateCreated, hash FROM branches; | ||||||
|  |  | ||||||
|  | DROP TABLE branches; | ||||||
|  | ALTER TABLE mig_branches RENAME TO branches; | ||||||
|  |  | ||||||
|  | CREATE INDEX `IDX_branches_noteId` ON `branches` (`noteId`); | ||||||
|  | CREATE INDEX `IDX_branches_noteId_parentNoteId` ON `branches` (`noteId`,`parentNoteId`); | ||||||
|  | CREATE INDEX IDX_branches_parentNoteId ON branches (parentNoteId); | ||||||
							
								
								
									
										2
									
								
								db/migrations/0149__space_out_positions.sql
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								db/migrations/0149__space_out_positions.sql
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,2 @@ | |||||||
|  | UPDATE branches SET notePosition = notePosition * 10; | ||||||
|  | UPDATE attributes SET position = position * 10; | ||||||
| @@ -29,25 +29,6 @@ CREATE TABLE IF NOT EXISTS "api_tokens" | |||||||
|   utcDateCreated TEXT NOT NULL, |   utcDateCreated TEXT NOT NULL, | ||||||
|   isDeleted INT NOT NULL DEFAULT 0, |   isDeleted INT NOT NULL DEFAULT 0, | ||||||
|   hash TEXT DEFAULT "" NOT NULL); |   hash TEXT DEFAULT "" NOT NULL); | ||||||
| CREATE TABLE IF NOT EXISTS "branches" ( |  | ||||||
|                                         `branchId`	TEXT NOT NULL, |  | ||||||
|                                         `noteId`	TEXT NOT NULL, |  | ||||||
|                                         `parentNoteId`	TEXT NOT NULL, |  | ||||||
|                                         `notePosition`	INTEGER NOT NULL, |  | ||||||
|                                         `prefix`	TEXT, |  | ||||||
|                                         `isExpanded`	BOOLEAN, |  | ||||||
|                                         `isDeleted`	INTEGER NOT NULL DEFAULT 0, |  | ||||||
|                                         `utcDateModified`	TEXT NOT NULL, |  | ||||||
|                                         utcDateCreated TEXT NOT NULL, |  | ||||||
|                                         hash TEXT DEFAULT "" NOT NULL, |  | ||||||
|                                         PRIMARY KEY(`branchId`) |  | ||||||
| ); |  | ||||||
| CREATE TABLE IF NOT EXISTS "event_log" ( |  | ||||||
|                                          `eventId`	TEXT NOT NULL PRIMARY KEY, |  | ||||||
|                                          `noteId`	TEXT, |  | ||||||
|                                          `comment`	TEXT, |  | ||||||
|                                          `utcDateCreated`	TEXT NOT NULL |  | ||||||
| ); |  | ||||||
| CREATE TABLE IF NOT EXISTS "options" | CREATE TABLE IF NOT EXISTS "options" | ||||||
| ( | ( | ||||||
|   name TEXT not null PRIMARY KEY, |   name TEXT not null PRIMARY KEY, | ||||||
| @@ -84,21 +65,6 @@ CREATE TABLE IF NOT EXISTS "notes" ( | |||||||
|                                      `utcDateModified`	TEXT NOT NULL, |                                      `utcDateModified`	TEXT NOT NULL, | ||||||
|                                      PRIMARY KEY(`noteId`) |                                      PRIMARY KEY(`noteId`) | ||||||
| ); | ); | ||||||
| CREATE TABLE IF NOT EXISTS "note_contents" ( |  | ||||||
|                                                    `noteId`	TEXT NOT NULL, |  | ||||||
|                                                    `content`	TEXT NULL DEFAULT NULL, |  | ||||||
|                                                    `hash` TEXT DEFAULT "" NOT NULL, |  | ||||||
|                                                    `utcDateModified` TEXT NOT NULL, |  | ||||||
|                                                    PRIMARY KEY(`noteId`) |  | ||||||
| ); |  | ||||||
| CREATE TABLE recent_notes |  | ||||||
| ( |  | ||||||
|     noteId TEXT not null primary key, |  | ||||||
|     notePath TEXT not null, |  | ||||||
|     hash TEXT default "" not null, |  | ||||||
|     utcDateCreated TEXT not null, |  | ||||||
|     isDeleted INT |  | ||||||
| ); |  | ||||||
| CREATE UNIQUE INDEX `IDX_sync_entityName_entityId` ON `sync` ( | CREATE UNIQUE INDEX `IDX_sync_entityName_entityId` ON `sync` ( | ||||||
|                                                               `entityName`, |                                                               `entityName`, | ||||||
|                                                               `entityId` |                                                               `entityId` | ||||||
| @@ -115,14 +81,6 @@ CREATE INDEX `IDX_note_revisions_dateModifiedFrom` ON `note_revisions` ( | |||||||
| CREATE INDEX `IDX_note_revisions_dateModifiedTo` ON `note_revisions` ( | CREATE INDEX `IDX_note_revisions_dateModifiedTo` ON `note_revisions` ( | ||||||
|                                                                       `utcDateModifiedTo` |                                                                       `utcDateModifiedTo` | ||||||
|   ); |   ); | ||||||
| CREATE INDEX `IDX_branches_noteId` ON `branches` ( |  | ||||||
|                                                   `noteId` |  | ||||||
|   ); |  | ||||||
| CREATE INDEX `IDX_branches_noteId_parentNoteId` ON `branches` ( |  | ||||||
|                                                                `noteId`, |  | ||||||
|                                                                `parentNoteId` |  | ||||||
|   ); |  | ||||||
| CREATE INDEX IDX_branches_parentNoteId ON branches (parentNoteId); |  | ||||||
| CREATE INDEX IDX_attributes_name_value | CREATE INDEX IDX_attributes_name_value | ||||||
|   on attributes (name, value); |   on attributes (name, value); | ||||||
| CREATE INDEX IDX_attributes_name_index | CREATE INDEX IDX_attributes_name_index | ||||||
| @@ -131,3 +89,33 @@ CREATE INDEX IDX_attributes_noteId_index | |||||||
|   on attributes (noteId); |   on attributes (noteId); | ||||||
| CREATE INDEX IDX_attributes_value_index | CREATE INDEX IDX_attributes_value_index | ||||||
|   on attributes (value); |   on attributes (value); | ||||||
|  | CREATE TABLE IF NOT EXISTS "note_contents" ( | ||||||
|  |                                                    `noteId`	TEXT NOT NULL, | ||||||
|  |                                                    `content`	TEXT NULL DEFAULT NULL, | ||||||
|  |                                                    `hash` TEXT DEFAULT "" NOT NULL, | ||||||
|  |                                                    `utcDateModified` TEXT NOT NULL, | ||||||
|  |                                                    PRIMARY KEY(`noteId`) | ||||||
|  | ); | ||||||
|  | CREATE TABLE recent_notes | ||||||
|  | ( | ||||||
|  |     noteId TEXT not null primary key, | ||||||
|  |     notePath TEXT not null, | ||||||
|  |     hash TEXT default "" not null, | ||||||
|  |     utcDateCreated TEXT not null, | ||||||
|  |     isDeleted INT | ||||||
|  | ); | ||||||
|  | CREATE TABLE IF NOT EXISTS "branches" ( | ||||||
|  |                                           `branchId`	TEXT NOT NULL, | ||||||
|  |                                           `noteId`	TEXT NOT NULL, | ||||||
|  |                                           `parentNoteId`	TEXT NOT NULL, | ||||||
|  |                                           `notePosition`	INTEGER NOT NULL, | ||||||
|  |                                           `prefix`	TEXT, | ||||||
|  |                                           `isExpanded`	INTEGER NOT NULL DEFAULT 0, | ||||||
|  |                                           `isDeleted`	INTEGER NOT NULL DEFAULT 0, | ||||||
|  |                                           `utcDateModified`	TEXT NOT NULL, | ||||||
|  |                                           utcDateCreated TEXT NOT NULL, | ||||||
|  |                                           hash TEXT DEFAULT "" NOT NULL, | ||||||
|  |                                           PRIMARY KEY(`branchId`)); | ||||||
|  | CREATE INDEX `IDX_branches_noteId` ON `branches` (`noteId`); | ||||||
|  | CREATE INDEX `IDX_branches_noteId_parentNoteId` ON `branches` (`noteId`,`parentNoteId`); | ||||||
|  | CREATE INDEX IDX_branches_parentNoteId ON branches (parentNoteId); | ||||||
|   | |||||||
| @@ -42,7 +42,7 @@ class Branch extends Entity { | |||||||
|     async beforeSaving() { |     async beforeSaving() { | ||||||
|         if (this.notePosition === undefined) { |         if (this.notePosition === undefined) { | ||||||
|             const maxNotePos = await sql.getValue('SELECT MAX(notePosition) FROM branches WHERE parentNoteId = ? AND isDeleted = 0', [this.parentNoteId]); |             const maxNotePos = await sql.getValue('SELECT MAX(notePosition) FROM branches WHERE parentNoteId = ? AND isDeleted = 0', [this.parentNoteId]); | ||||||
|             this.notePosition = maxNotePos === null ? 0 : maxNotePos + 1; |             this.notePosition = maxNotePos === null ? 0 : maxNotePos + 10; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         if (!this.isDeleted) { |         if (!this.isDeleted) { | ||||||
|   | |||||||
| @@ -47,14 +47,15 @@ function AttributesModel() { | |||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     this.updateAttributePositions = function() { |     this.updateAttributePositions = function() { | ||||||
|         let position = 0; |         let position = 10; | ||||||
|  |  | ||||||
|         // we need to update positions by searching in the DOM, because order of the |         // we need to update positions by searching in the DOM, because order of the | ||||||
|         // attributes in the viewmodel (self.ownedAttributes()) stays the same |         // attributes in the viewmodel (self.ownedAttributes()) stays the same | ||||||
|         $ownedAttributesBody.find('input[name="position"]').each(function() { |         $ownedAttributesBody.find('input[name="position"]').each(function() { | ||||||
|             const attribute = self.getTargetAttribute(this); |             const attribute = self.getTargetAttribute(this); | ||||||
|  |  | ||||||
|             attribute().position = position++; |             attribute().position = position; | ||||||
|  |             position += 10; | ||||||
|         }); |         }); | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -845,6 +845,18 @@ $tree.on('mousedown', '.fancytree-title', e => { | |||||||
|     } |     } | ||||||
| }); | }); | ||||||
|  |  | ||||||
|  | async function duplicateNote(noteId, parentNoteId) { | ||||||
|  |     const {note} = await server.post(`notes/${noteId}/duplicate/${parentNoteId}`); | ||||||
|  |  | ||||||
|  |     await reload(); | ||||||
|  |  | ||||||
|  |     await activateNote(note.noteId); | ||||||
|  |  | ||||||
|  |     const origNote = await treeCache.getNote(noteId); | ||||||
|  |     infoService.showMessage(`Note "${origNote.title}" has been duplicated`); | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
| utils.bindGlobalShortcut('alt+c', () => collapseTree()); // don't use shortened form since collapseTree() accepts argument | utils.bindGlobalShortcut('alt+c', () => collapseTree()); // don't use shortened form since collapseTree() accepts argument | ||||||
| $collapseTreeButton.click(() => collapseTree()); | $collapseTreeButton.click(() => collapseTree()); | ||||||
|  |  | ||||||
| @@ -882,5 +894,6 @@ export default { | |||||||
|     resolveNotePath, |     resolveNotePath, | ||||||
|     getSomeNotePath, |     getSomeNotePath, | ||||||
|     focusTree, |     focusTree, | ||||||
|     scrollToActiveNote |     scrollToActiveNote, | ||||||
|  |     duplicateNote | ||||||
| }; | }; | ||||||
| @@ -8,6 +8,7 @@ import syncService from "./sync.js"; | |||||||
| import hoistedNoteService from './hoisted_note.js'; | import hoistedNoteService from './hoisted_note.js'; | ||||||
| import noteDetailService from './note_detail.js'; | import noteDetailService from './note_detail.js'; | ||||||
| import clipboard from './clipboard.js'; | import clipboard from './clipboard.js'; | ||||||
|  | import protectedSessionHolder from "./protected_session_holder.js"; | ||||||
|  |  | ||||||
| class TreeContextMenu { | class TreeContextMenu { | ||||||
|     constructor(node) { |     constructor(node) { | ||||||
| @@ -68,6 +69,8 @@ class TreeContextMenu { | |||||||
|                 enabled: !clipboard.isEmpty() && note.type !== 'search' && noSelectedNotes }, |                 enabled: !clipboard.isEmpty() && note.type !== 'search' && noSelectedNotes }, | ||||||
|             { title: "Paste after", cmd: "pasteAfter", uiIcon: "clipboard", |             { title: "Paste after", cmd: "pasteAfter", uiIcon: "clipboard", | ||||||
|                 enabled: !clipboard.isEmpty() && isNotRoot && parentNote.type !== 'search' && noSelectedNotes }, |                 enabled: !clipboard.isEmpty() && isNotRoot && parentNote.type !== 'search' && noSelectedNotes }, | ||||||
|  |             { title: "Duplicate note here", cmd: "duplicateNote", uiIcon: "empty", | ||||||
|  |                 enabled: noSelectedNotes && (!note.isProtected || protectedSessionHolder.isProtectedSessionAvailable()) }, | ||||||
|             { title: "----" }, |             { title: "----" }, | ||||||
|             { title: "Export", cmd: "export", uiIcon: "empty", |             { title: "Export", cmd: "export", uiIcon: "empty", | ||||||
|                 enabled: note.type !== 'search' && noSelectedNotes }, |                 enabled: note.type !== 'search' && noSelectedNotes }, | ||||||
| @@ -152,6 +155,11 @@ class TreeContextMenu { | |||||||
|         else if (cmd === "unhoist") { |         else if (cmd === "unhoist") { | ||||||
|             hoistedNoteService.unhoist(); |             hoistedNoteService.unhoist(); | ||||||
|         } |         } | ||||||
|  |         else if (cmd === "duplicateNote") { | ||||||
|  |             const branch = await treeCache.getBranch(this.node.data.branchId); | ||||||
|  |  | ||||||
|  |             treeService.duplicateNote(this.node.data.noteId, branch.parentNoteId); | ||||||
|  |         } | ||||||
|         else { |         else { | ||||||
|             ws.logError("Unknown command: " + cmd); |             ws.logError("Unknown command: " + cmd); | ||||||
|         } |         } | ||||||
|   | |||||||
| @@ -14,8 +14,7 @@ const TaskContext = require('../../services/task_context'); | |||||||
|  */ |  */ | ||||||
|  |  | ||||||
| async function moveBranchToParent(req) { | async function moveBranchToParent(req) { | ||||||
|     const branchId = req.params.branchId; |     const {branchId, parentNoteId} = req.params; | ||||||
|     const parentNoteId = req.params.parentNoteId; |  | ||||||
|  |  | ||||||
|     const noteToMove = await tree.getBranch(branchId); |     const noteToMove = await tree.getBranch(branchId); | ||||||
|  |  | ||||||
| @@ -26,7 +25,7 @@ async function moveBranchToParent(req) { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     const maxNotePos = await sql.getValue('SELECT MAX(notePosition) FROM branches WHERE parentNoteId = ? AND isDeleted = 0', [parentNoteId]); |     const maxNotePos = await sql.getValue('SELECT MAX(notePosition) FROM branches WHERE parentNoteId = ? AND isDeleted = 0', [parentNoteId]); | ||||||
|     const newNotePos = maxNotePos === null ? 0 : maxNotePos + 1; |     const newNotePos = maxNotePos === null ? 0 : maxNotePos + 10; | ||||||
|  |  | ||||||
|     const branch = await repository.getBranch(branchId); |     const branch = await repository.getBranch(branchId); | ||||||
|     branch.parentNoteId = parentNoteId; |     branch.parentNoteId = parentNoteId; | ||||||
| @@ -37,8 +36,7 @@ async function moveBranchToParent(req) { | |||||||
| } | } | ||||||
|  |  | ||||||
| async function moveBranchBeforeNote(req) { | async function moveBranchBeforeNote(req) { | ||||||
|     const branchId = req.params.branchId; |     const {branchId, beforeBranchId} = req.params; | ||||||
|     const beforeBranchId = req.params.beforeBranchId; |  | ||||||
|  |  | ||||||
|     const noteToMove = await tree.getBranch(branchId); |     const noteToMove = await tree.getBranch(branchId); | ||||||
|     const beforeNote = await tree.getBranch(beforeBranchId); |     const beforeNote = await tree.getBranch(beforeBranchId); | ||||||
| @@ -51,7 +49,7 @@ async function moveBranchBeforeNote(req) { | |||||||
|  |  | ||||||
|     // we don't change utcDateModified so other changes are prioritized in case of conflict |     // we don't change utcDateModified so other changes are prioritized in case of conflict | ||||||
|     // also we would have to sync all those modified branches otherwise hash checks would fail |     // also we would have to sync all those modified branches otherwise hash checks would fail | ||||||
|     await sql.execute("UPDATE branches SET notePosition = notePosition + 1 WHERE parentNoteId = ? AND notePosition >= ? AND isDeleted = 0", |     await sql.execute("UPDATE branches SET notePosition = notePosition + 10 WHERE parentNoteId = ? AND notePosition >= ? AND isDeleted = 0", | ||||||
|         [beforeNote.parentNoteId, beforeNote.notePosition]); |         [beforeNote.parentNoteId, beforeNote.notePosition]); | ||||||
|  |  | ||||||
|     await sync_table.addNoteReorderingSync(beforeNote.parentNoteId); |     await sync_table.addNoteReorderingSync(beforeNote.parentNoteId); | ||||||
| @@ -65,8 +63,7 @@ async function moveBranchBeforeNote(req) { | |||||||
| } | } | ||||||
|  |  | ||||||
| async function moveBranchAfterNote(req) { | async function moveBranchAfterNote(req) { | ||||||
|     const branchId = req.params.branchId; |     const {branchId, afterBranchId} = req.params; | ||||||
|     const afterBranchId = req.params.afterBranchId; |  | ||||||
|  |  | ||||||
|     const noteToMove = await tree.getBranch(branchId); |     const noteToMove = await tree.getBranch(branchId); | ||||||
|     const afterNote = await tree.getBranch(afterBranchId); |     const afterNote = await tree.getBranch(afterBranchId); | ||||||
| @@ -79,22 +76,21 @@ async function moveBranchAfterNote(req) { | |||||||
|  |  | ||||||
|     // we don't change utcDateModified so other changes are prioritized in case of conflict |     // we don't change utcDateModified so other changes are prioritized in case of conflict | ||||||
|     // also we would have to sync all those modified branches otherwise hash checks would fail |     // also we would have to sync all those modified branches otherwise hash checks would fail | ||||||
|     await sql.execute("UPDATE branches SET notePosition = notePosition + 1 WHERE parentNoteId = ? AND notePosition > ? AND isDeleted = 0", |     await sql.execute("UPDATE branches SET notePosition = notePosition + 10 WHERE parentNoteId = ? AND notePosition > ? AND isDeleted = 0", | ||||||
|         [afterNote.parentNoteId, afterNote.notePosition]); |         [afterNote.parentNoteId, afterNote.notePosition]); | ||||||
|  |  | ||||||
|     await sync_table.addNoteReorderingSync(afterNote.parentNoteId); |     await sync_table.addNoteReorderingSync(afterNote.parentNoteId); | ||||||
|  |  | ||||||
|     const branch = await repository.getBranch(branchId); |     const branch = await repository.getBranch(branchId); | ||||||
|     branch.parentNoteId = afterNote.parentNoteId; |     branch.parentNoteId = afterNote.parentNoteId; | ||||||
|     branch.notePosition = afterNote.notePosition + 1; |     branch.notePosition = afterNote.notePosition + 10; | ||||||
|     await branch.save(); |     await branch.save(); | ||||||
|  |  | ||||||
|     return { success: true }; |     return { success: true }; | ||||||
| } | } | ||||||
|  |  | ||||||
| async function setExpanded(req) { | async function setExpanded(req) { | ||||||
|     const branchId = req.params.branchId; |     const {branchId, expanded} = req.params; | ||||||
|     const expanded = req.params.expanded; |  | ||||||
|  |  | ||||||
|     await sql.execute("UPDATE branches SET isExpanded = ? WHERE branchId = ?", [expanded, branchId]); |     await sql.execute("UPDATE branches SET isExpanded = ? WHERE branchId = ?", [expanded, branchId]); | ||||||
|     // we don't sync expanded label |     // we don't sync expanded label | ||||||
|   | |||||||
| @@ -180,6 +180,12 @@ async function changeTitle(req) { | |||||||
|     await note.save(); |     await note.save(); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | async function duplicateNote(req) { | ||||||
|  |     const {noteId, parentNoteId} = req.params; | ||||||
|  |  | ||||||
|  |     return await noteService.duplicateNote(noteId, parentNoteId); | ||||||
|  | } | ||||||
|  |  | ||||||
| module.exports = { | module.exports = { | ||||||
|     getNote, |     getNote, | ||||||
|     updateNote, |     updateNote, | ||||||
| @@ -190,5 +196,6 @@ module.exports = { | |||||||
|     setNoteTypeMime, |     setNoteTypeMime, | ||||||
|     getChildren, |     getChildren, | ||||||
|     getRelationMap, |     getRelationMap, | ||||||
|     changeTitle |     changeTitle, | ||||||
|  |     duplicateNote | ||||||
| }; | }; | ||||||
| @@ -135,6 +135,7 @@ function register(app) { | |||||||
|     apiRoute(GET, '/api/notes/:noteId/revision-list', noteRevisionsApiRoute.getNoteRevisionList); |     apiRoute(GET, '/api/notes/:noteId/revision-list', noteRevisionsApiRoute.getNoteRevisionList); | ||||||
|     apiRoute(POST, '/api/notes/relation-map', notesApiRoute.getRelationMap); |     apiRoute(POST, '/api/notes/relation-map', notesApiRoute.getRelationMap); | ||||||
|     apiRoute(PUT, '/api/notes/:noteId/change-title', notesApiRoute.changeTitle); |     apiRoute(PUT, '/api/notes/:noteId/change-title', notesApiRoute.changeTitle); | ||||||
|  |     apiRoute(POST, '/api/notes/:noteId/duplicate/:parentNoteId', notesApiRoute.duplicateNote); | ||||||
|  |  | ||||||
|     apiRoute(GET, '/api/edited-notes/:date', noteRevisionsApiRoute.getEditedNotesOnDate); |     apiRoute(GET, '/api/edited-notes/:date', noteRevisionsApiRoute.getEditedNotesOnDate); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -4,7 +4,7 @@ const build = require('./build'); | |||||||
| const packageJson = require('../../package'); | const packageJson = require('../../package'); | ||||||
| const {TRILIUM_DATA_DIR} = require('./data_dir'); | const {TRILIUM_DATA_DIR} = require('./data_dir'); | ||||||
|  |  | ||||||
| const APP_DB_VERSION = 147; | const APP_DB_VERSION = 149; | ||||||
| const SYNC_VERSION = 10; | const SYNC_VERSION = 10; | ||||||
| const CLIPPER_PROTOCOL_VERSION = "1.0"; | const CLIPPER_PROTOCOL_VERSION = "1.0"; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -81,7 +81,7 @@ async function cloneNoteAfter(noteId, afterBranchId) { | |||||||
|  |  | ||||||
|     // we don't change utcDateModified so other changes are prioritized in case of conflict |     // we don't change utcDateModified so other changes are prioritized in case of conflict | ||||||
|     // also we would have to sync all those modified branches otherwise hash checks would fail |     // also we would have to sync all those modified branches otherwise hash checks would fail | ||||||
|     await sql.execute("UPDATE branches SET notePosition = notePosition + 1 WHERE parentNoteId = ? AND notePosition > ? AND isDeleted = 0", |     await sql.execute("UPDATE branches SET notePosition = notePosition + 10 WHERE parentNoteId = ? AND notePosition > ? AND isDeleted = 0", | ||||||
|         [afterNote.parentNoteId, afterNote.notePosition]); |         [afterNote.parentNoteId, afterNote.notePosition]); | ||||||
|  |  | ||||||
|     await syncTable.addNoteReorderingSync(afterNote.parentNoteId); |     await syncTable.addNoteReorderingSync(afterNote.parentNoteId); | ||||||
| @@ -89,7 +89,7 @@ async function cloneNoteAfter(noteId, afterBranchId) { | |||||||
|     const branch = await new Branch({ |     const branch = await new Branch({ | ||||||
|         noteId: noteId, |         noteId: noteId, | ||||||
|         parentNoteId: afterNote.parentNoteId, |         parentNoteId: afterNote.parentNoteId, | ||||||
|         notePosition: afterNote.notePosition + 1, |         notePosition: afterNote.notePosition + 10, | ||||||
|         isExpanded: 0 |         isExpanded: 0 | ||||||
|     }).save(); |     }).save(); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -198,7 +198,7 @@ async function findExistencyIssues() { | |||||||
|         const branches = await repository.getEntities(`SELECT * FROM branches WHERE noteId = ? and parentNoteId = ? and isDeleted = 1`, [noteId, parentNoteId]); |         const branches = await repository.getEntities(`SELECT * FROM branches WHERE noteId = ? and parentNoteId = ? and isDeleted = 1`, [noteId, parentNoteId]); | ||||||
|  |  | ||||||
|         // it's not necessarily "original" branch, it's just the only one which will survive |         // it's not necessarily "original" branch, it's just the only one which will survive | ||||||
|         const origBranch = branches.get(0); |         const origBranch = branches[0]; | ||||||
|  |  | ||||||
|         // delete all but the first branch |         // delete all but the first branch | ||||||
|         for (const branch of branches.slice(1)) { |         for (const branch of branches.slice(1)) { | ||||||
|   | |||||||
| @@ -21,15 +21,15 @@ async function getNewNotePosition(parentNoteId, noteData) { | |||||||
|     if (noteData.target === 'into') { |     if (noteData.target === 'into') { | ||||||
|         const maxNotePos = await sql.getValue('SELECT MAX(notePosition) FROM branches WHERE parentNoteId = ? AND isDeleted = 0', [parentNoteId]); |         const maxNotePos = await sql.getValue('SELECT MAX(notePosition) FROM branches WHERE parentNoteId = ? AND isDeleted = 0', [parentNoteId]); | ||||||
|  |  | ||||||
|         newNotePos = maxNotePos === null ? 0 : maxNotePos + 1; |         newNotePos = maxNotePos === null ? 0 : maxNotePos + 10; | ||||||
|     } |     } | ||||||
|     else if (noteData.target === 'after') { |     else if (noteData.target === 'after') { | ||||||
|         const afterNote = await sql.getRow('SELECT notePosition FROM branches WHERE branchId = ?', [noteData.target_branchId]); |         const afterNote = await sql.getRow('SELECT notePosition FROM branches WHERE branchId = ?', [noteData.target_branchId]); | ||||||
|  |  | ||||||
|         newNotePos = afterNote.notePosition + 1; |         newNotePos = afterNote.notePosition + 10; | ||||||
|  |  | ||||||
|         // not updating utcDateModified to avoig having to sync whole rows |         // not updating utcDateModified to avoig having to sync whole rows | ||||||
|         await sql.execute('UPDATE branches SET notePosition = notePosition + 1 WHERE parentNoteId = ? AND notePosition > ? AND isDeleted = 0', |         await sql.execute('UPDATE branches SET notePosition = notePosition + 10 WHERE parentNoteId = ? AND notePosition > ? AND isDeleted = 0', | ||||||
|             [parentNoteId, afterNote.notePosition]); |             [parentNoteId, afterNote.notePosition]); | ||||||
|  |  | ||||||
|         await syncTableService.addNoteReorderingSync(parentNoteId); |         await syncTableService.addNoteReorderingSync(parentNoteId); | ||||||
| @@ -465,6 +465,44 @@ async function cleanupDeletedNotes() { | |||||||
|     await sql.execute("UPDATE note_revisions SET content = NULL WHERE note_revisions.content IS NOT NULL AND noteId IN (SELECT noteId FROM notes WHERE isDeleted = 1 AND notes.utcDateModified <= ?)", [dateUtils.utcDateStr(cutoffDate)]); |     await sql.execute("UPDATE note_revisions SET content = NULL WHERE note_revisions.content IS NOT NULL AND noteId IN (SELECT noteId FROM notes WHERE isDeleted = 1 AND notes.utcDateModified <= ?)", [dateUtils.utcDateStr(cutoffDate)]); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | async function duplicateNote(noteId, parentNoteId) { | ||||||
|  |     const origNote = await repository.getNote(noteId); | ||||||
|  |  | ||||||
|  |     if (origNote.isProtected && !protectedSessionService.isProtectedSessionAvailable()) { | ||||||
|  |         throw new Error(`Cannot duplicate note=${origNote.noteId} because it is protected and protected session is not available`); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // might be null if orig note is not in the target parentNoteId | ||||||
|  |     const origBranch = (await origNote.getBranches()).find(branch => branch.parentNoteId === parentNoteId); | ||||||
|  |  | ||||||
|  |     const newNote = new Note(origNote); | ||||||
|  |     newNote.noteId = undefined; // force creation of new note | ||||||
|  |     newNote.title += " (dup)"; | ||||||
|  |  | ||||||
|  |     await newNote.save(); | ||||||
|  |     await newNote.setContent(await origNote.getContent()); | ||||||
|  |  | ||||||
|  |     const newBranch = await new Branch({ | ||||||
|  |         noteId: newNote.noteId, | ||||||
|  |         parentNoteId: parentNoteId, | ||||||
|  |         // here increasing just by 1 to make sure it's directly after original | ||||||
|  |         notePosition: origBranch ? origBranch.notePosition + 1 : null | ||||||
|  |     }).save(); | ||||||
|  |  | ||||||
|  |     for (const attribute of await origNote.getAttributes()) { | ||||||
|  |         const attr = new Attribute(attribute); | ||||||
|  |         attr.attributeId = undefined; // force creation of new attribute | ||||||
|  |         attr.noteId = newNote.noteId; | ||||||
|  |  | ||||||
|  |         await attr.save(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return { | ||||||
|  |         note: newNote, | ||||||
|  |         branch: newBranch | ||||||
|  |     }; | ||||||
|  | } | ||||||
|  |  | ||||||
| sqlInit.dbReady.then(() => { | sqlInit.dbReady.then(() => { | ||||||
|     // first cleanup kickoff 5 minutes after startup |     // first cleanup kickoff 5 minutes after startup | ||||||
|     setTimeout(cls.wrap(cleanupDeletedNotes), 5 * 60 * 1000); |     setTimeout(cls.wrap(cleanupDeletedNotes), 5 * 60 * 1000); | ||||||
| @@ -478,5 +516,6 @@ module.exports = { | |||||||
|     updateNote, |     updateNote, | ||||||
|     deleteBranch, |     deleteBranch, | ||||||
|     protectNoteRecursively, |     protectNoteRecursively, | ||||||
|     scanForLinks |     scanForLinks, | ||||||
|  |     duplicateNote | ||||||
| }; | }; | ||||||
| @@ -109,7 +109,7 @@ async function createInitialDatabase(username, password, theme) { | |||||||
|             noteId: 'root', |             noteId: 'root', | ||||||
|             parentNoteId: 'none', |             parentNoteId: 'none', | ||||||
|             isExpanded: true, |             isExpanded: true, | ||||||
|             notePosition: 0 |             notePosition: 10 | ||||||
|         }).save(); |         }).save(); | ||||||
|  |  | ||||||
|         const dummyTaskContext = new TaskContext("1", 'import', false); |         const dummyTaskContext = new TaskContext("1", 'import', false); | ||||||
|   | |||||||
| @@ -110,13 +110,13 @@ async function sortNotesAlphabetically(parentNoteId, directoriesFirst = false) { | |||||||
|             } |             } | ||||||
|         }); |         }); | ||||||
|  |  | ||||||
|         let position = 1; |         let position = 10; | ||||||
|  |  | ||||||
|         for (const note of notes) { |         for (const note of notes) { | ||||||
|             await sql.execute("UPDATE branches SET notePosition = ? WHERE branchId = ?", |             await sql.execute("UPDATE branches SET notePosition = ? WHERE branchId = ?", | ||||||
|                 [position, note.branchId]); |                 [position, note.branchId]); | ||||||
|  |  | ||||||
|             position++; |             position += 10; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         await syncTableService.addNoteReorderingSync(parentNoteId); |         await syncTableService.addNoteReorderingSync(parentNoteId); | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user