mirror of
				https://github.com/zadam/trilium.git
				synced 2025-10-31 10:26:08 +01:00 
			
		
		
		
	note revisions changes WIP
This commit is contained in:
		| @@ -1,7 +1,7 @@ | ||||
| CREATE TABLE IF NOT EXISTS "note_revisions_mig" (`noteRevisionId`	TEXT NOT NULL PRIMARY KEY, | ||||
|                                                 `noteId`	TEXT NOT NULL, | ||||
|                                                 `title`	TEXT, | ||||
|                                                 `content`	TEXT, | ||||
|                                                 `contentLength`	INT NOT NULL, | ||||
|                                                 `isProtected`	INT NOT NULL DEFAULT 0, | ||||
|                                                 `utcDateLastEdited` TEXT NOT NULL, | ||||
|                                                 `utcDateCreated` TEXT NOT NULL, | ||||
| @@ -18,10 +18,10 @@ CREATE TABLE IF NOT EXISTS "note_revision_contents" (`noteRevisionId`	TEXT NOT N | ||||
|                                                  `utcDateModified` TEXT NOT NULL); | ||||
|  | ||||
| INSERT INTO note_revision_contents (noteRevisionId, content, hash, utcDateModified) | ||||
| SELECT noteRevisionId, content, hash, utcDateModified FROM note_revisions; | ||||
| SELECT noteRevisionId, content, hash, utcDateModifiedTo FROM note_revisions; | ||||
|  | ||||
| INSERT INTO note_revisions_mig (noteRevisionId, noteId, title, isProtected, utcDateLastEdited, utcDateCreated, utcDateModified, dateLastEdited, dateCreated, type, mime, hash) | ||||
| SELECT noteRevisionId, noteId, title, isProtected, utcDateModifiedFrom, utcDateModifiedTo, utcDateModifiedTo, dateModifiedFrom, dateModifiedTo, type, mime, hash FROM note_revisions; | ||||
| INSERT INTO note_revisions_mig (noteRevisionId, noteId, title, contentLength, isProtected, utcDateLastEdited, utcDateCreated, utcDateModified, dateLastEdited, dateCreated, type, mime, hash) | ||||
| SELECT noteRevisionId, noteId, title, LENGTH(content), isProtected, utcDateModifiedFrom, utcDateModifiedTo, utcDateModifiedTo, dateModifiedFrom, dateModifiedTo, type, mime, hash FROM note_revisions; | ||||
|  | ||||
| DROP TABLE note_revisions; | ||||
| ALTER TABLE note_revisions_mig RENAME TO note_revisions; | ||||
|   | ||||
| @@ -112,7 +112,7 @@ class Note extends Entity { | ||||
|  | ||||
|     /** @returns {Promise} */ | ||||
|     async setContent(content) { | ||||
|         // force updating note itself so that dateChanged is represented correctly even for the content | ||||
|         // force updating note itself so that dateModified is represented correctly even for the content | ||||
|         this.forcedChange = true; | ||||
|         await this.save(); | ||||
|  | ||||
|   | ||||
| @@ -4,6 +4,7 @@ const Entity = require('./entity'); | ||||
| const protectedSessionService = require('../services/protected_session'); | ||||
| const repository = require('../services/repository'); | ||||
| const utils = require('../services/utils'); | ||||
| const sql = require('../services/sql'); | ||||
| const dateUtils = require('../services/date_utils'); | ||||
| const syncTableService = require('../services/sync_table'); | ||||
|  | ||||
| @@ -16,17 +17,18 @@ const syncTableService = require('../services/sync_table'); | ||||
|  * @param {string} mime | ||||
|  * @param {string} title | ||||
|  * @param {string} isProtected | ||||
|  * @param {string} dateModifiedFrom | ||||
|  * @param {string} dateModifiedTo | ||||
|  * @param {string} utcDateModifiedFrom | ||||
|  * @param {string} utcDateModifiedTo | ||||
|  * @param {string} dateLastEdited | ||||
|  * @param {string} dateCreated | ||||
|  * @param {string} utcDateLastEdited | ||||
|  * @param {string} utcDateCreated | ||||
|  * @param {string} utcDateModified | ||||
|  * | ||||
|  * @extends Entity | ||||
|  */ | ||||
| class NoteRevision extends Entity { | ||||
|     static get entityName() { return "note_revisions"; } | ||||
|     static get primaryKeyName() { return "noteRevisionId"; } | ||||
|     static get hashedProperties() { return ["noteRevisionId", "noteId", "title", "isProtected", "dateModifiedFrom", "dateModifiedTo", "utcDateModifiedFrom", "utcDateModifiedTo"]; } | ||||
|     static get hashedProperties() { return ["noteRevisionId", "noteId", "title", "contentLength", "isProtected", "dateLastEdited", "dateCreated", "utcDateLastEdited", "utcDateCreated", "utcDateModified"]; } | ||||
|  | ||||
|     constructor(row) { | ||||
|         super(row); | ||||
| @@ -98,8 +100,9 @@ class NoteRevision extends Entity { | ||||
|  | ||||
|     /** @returns {Promise} */ | ||||
|     async setContent(content) { | ||||
|         // force updating note itself so that dateChanged is represented correctly even for the content | ||||
|         // force updating note itself so that utcDateModified is represented correctly even for the content | ||||
|         this.forcedChange = true; | ||||
|         this.contentLength = content.length; | ||||
|         await this.save(); | ||||
|  | ||||
|         this.content = content; | ||||
|   | ||||
| @@ -25,12 +25,12 @@ export async function showNoteRevisionsDialog(noteId, noteRevisionId) { | ||||
|     $content.empty(); | ||||
|  | ||||
|     note = noteDetailService.getActiveTabNote(); | ||||
|     revisionItems = await server.get('notes/' + noteId + '/revisions'); | ||||
|     revisionItems = await server.get(`notes/${noteId}/revisions`); | ||||
|  | ||||
|     for (const item of revisionItems) { | ||||
|         $list.append($('<option>', { | ||||
|             value: item.noteRevisionId, | ||||
|             text: item.dateModifiedFrom | ||||
|             text: item.dateLastEdited | ||||
|         })); | ||||
|     } | ||||
|  | ||||
| @@ -46,18 +46,20 @@ export async function showNoteRevisionsDialog(noteId, noteRevisionId) { | ||||
|     } | ||||
| } | ||||
|  | ||||
| $list.on('change', () => { | ||||
| $list.on('change', async () => { | ||||
|     const optVal = $list.find(":selected").val(); | ||||
|  | ||||
|     const revisionItem = revisionItems.find(r => r.noteRevisionId === optVal); | ||||
|  | ||||
|     $title.html(revisionItem.title); | ||||
|  | ||||
|     const fullNoteRevision = await server.get(`notes/${revisionItem.noteId}/revisions/${revisionItem.noteRevisionId}`); | ||||
|  | ||||
|     if (note.type === 'text') { | ||||
|         $content.html(revisionItem.content); | ||||
|         $content.html(fullNoteRevision.content); | ||||
|     } | ||||
|     else if (note.type === 'code') { | ||||
|         $content.html($("<pre>").text(revisionItem.content)); | ||||
|         $content.html($("<pre>").text(fullNoteRevision.content)); | ||||
|     } | ||||
|     else { | ||||
|         $content.text("Preview isn't available for this note type."); | ||||
|   | ||||
| @@ -27,7 +27,7 @@ class NoteRevisionsWidget extends StandardWidget { | ||||
|     } | ||||
|  | ||||
|     async doRenderBody() { | ||||
|         const revisionItems = await server.get(`notes/${this.ctx.note.noteId}/revision-list`); | ||||
|         const revisionItems = await server.get(`notes/${this.ctx.note.noteId}/revisions`); | ||||
|  | ||||
|         if (revisionItems.length === 0) { | ||||
|             this.$body.text("No revisions yet..."); | ||||
| @@ -44,7 +44,7 @@ class NoteRevisionsWidget extends StandardWidget { | ||||
|                 'data-note-path': this.ctx.note.noteId, | ||||
|                 'data-note-revision-id': item.noteRevisionId, | ||||
|                 href: 'javascript:' | ||||
|             }).text(item.dateModifiedFrom.substr(0, 16))); | ||||
|             }).text(item.dateLastEdited.substr(0, 16))); | ||||
|  | ||||
|             if (item.contentLength !== null) { | ||||
|                 $listItem.append($("<span>").text(` (${item.contentLength} characters)`)) | ||||
|   | ||||
| @@ -4,33 +4,21 @@ const repository = require('../../services/repository'); | ||||
| const noteCacheService = require('../../services/note_cache'); | ||||
|  | ||||
| async function getNoteRevisions(req) { | ||||
|     const noteId = req.params.noteId; | ||||
|     const {noteId} = req.params; | ||||
|  | ||||
|     return await repository.getEntities(` | ||||
|         SELECT note_revisions.* | ||||
|         FROM note_revisions  | ||||
|         WHERE noteId = ?  | ||||
|         ORDER BY utcDateModifiedTo DESC`, [noteId]); | ||||
|         ORDER BY utcDateCreated DESC`, [noteId]); | ||||
| } | ||||
|  | ||||
| async function getNoteRevisionList(req) { | ||||
|     const noteId = req.params.noteId; | ||||
| async function getNoteRevision(req) { | ||||
|     const {noteRevisionId} = req.params; | ||||
|  | ||||
|     return await repository.getEntities(` | ||||
|         SELECT noteRevisionId, | ||||
|                noteId, | ||||
|                title, | ||||
|                isProtected, | ||||
|                utcDateModifiedFrom, | ||||
|                utcDateModifiedTo, | ||||
|                dateModifiedFrom, | ||||
|                dateModifiedTo, | ||||
|                type, | ||||
|                mime, | ||||
|                CASE isProtected WHEN 1 THEN null ELSE LENGTH(content) END AS contentLength | ||||
|     return await repository.getNote(`SELECT * | ||||
|         FROM note_revisions  | ||||
|         WHERE noteId = ?  | ||||
|         ORDER BY utcDateModifiedTo DESC`, [noteId]); | ||||
|         WHERE noteRevisionId = ?`, [noteId]); | ||||
| } | ||||
|  | ||||
| async function getEditedNotesOnDate(req) { | ||||
| @@ -42,7 +30,7 @@ async function getEditedNotesOnDate(req) { | ||||
|         left join note_revisions using (noteId) | ||||
|         where substr(notes.dateCreated, 0, 11) = ? | ||||
|            or substr(notes.dateModified, 0, 11) = ? | ||||
|            or substr(note_revisions.dateModifiedFrom, 0, 11) = ?`, [date, date, date]); | ||||
|            or substr(note_revisions.dateLastEdited, 0, 11) = ?`, [date, date, date]); | ||||
|  | ||||
|     for (const note of notes) { | ||||
|         const notePath = noteCacheService.getNotePath(note.noteId); | ||||
| @@ -55,6 +43,6 @@ async function getEditedNotesOnDate(req) { | ||||
|  | ||||
| module.exports = { | ||||
|     getNoteRevisions, | ||||
|     getNoteRevisionList, | ||||
|     getNoteRevision, | ||||
|     getEditedNotesOnDate | ||||
| }; | ||||
| @@ -13,12 +13,12 @@ async function getRecentChanges() { | ||||
|                 notes.title AS current_title, | ||||
|                 notes.isProtected AS current_isProtected, | ||||
|                 note_revisions.title, | ||||
|                 note_revisions.utcDateModifiedTo AS date | ||||
|                 note_revisions.utcDateCreated AS date | ||||
|             FROM  | ||||
|                 note_revisions | ||||
|                 JOIN notes USING(noteId) | ||||
|             ORDER BY  | ||||
|                 utcDateModifiedTo DESC | ||||
|                 utcDateCreated DESC | ||||
|             LIMIT 1000 | ||||
|         ) | ||||
|         UNION ALL SELECT * FROM ( | ||||
|   | ||||
| @@ -132,7 +132,7 @@ function register(app) { | ||||
|     apiRoute(PUT, '/api/notes/:noteId/protect/:isProtected', notesApiRoute.protectSubtree); | ||||
|     apiRoute(PUT, /\/api\/notes\/(.*)\/type\/(.*)\/mime\/(.*)/, notesApiRoute.setNoteTypeMime); | ||||
|     apiRoute(GET, '/api/notes/:noteId/revisions', noteRevisionsApiRoute.getNoteRevisions); | ||||
|     apiRoute(GET, '/api/notes/:noteId/revision-list', noteRevisionsApiRoute.getNoteRevisionList); | ||||
|     apiRoute(GET, '/api/notes/:noteId/revisions/:noteRevisionId', noteRevisionsApiRoute.getNoteRevision); | ||||
|     apiRoute(POST, '/api/notes/relation-map', notesApiRoute.getRelationMap); | ||||
|     apiRoute(PUT, '/api/notes/:noteId/change-title', notesApiRoute.changeTitle); | ||||
|     apiRoute(POST, '/api/notes/:noteId/duplicate/:parentNoteId', notesApiRoute.duplicateNote); | ||||
|   | ||||
| @@ -329,24 +329,27 @@ async function saveNoteRevision(note) { | ||||
|     const revisionCutoff = dateUtils.utcDateStr(new Date(now.getTime() - noteRevisionSnapshotTimeInterval * 1000)); | ||||
|  | ||||
|     const existingNoteRevisionId = await sql.getValue( | ||||
|         "SELECT noteRevisionId FROM note_revisions WHERE noteId = ? AND utcDateModifiedTo >= ?", [note.noteId, revisionCutoff]); | ||||
|         "SELECT noteRevisionId FROM note_revisions WHERE noteId = ? AND utcDateCreated >= ?", [note.noteId, revisionCutoff]); | ||||
|  | ||||
|     const msSinceDateCreated = now.getTime() - dateUtils.parseDateTime(note.utcDateCreated).getTime(); | ||||
|  | ||||
|     if (!existingNoteRevisionId && msSinceDateCreated >= noteRevisionSnapshotTimeInterval * 1000) { | ||||
|         await new NoteRevision({ | ||||
|         const noteRevision = await new NoteRevision({ | ||||
|             noteId: note.noteId, | ||||
|             // title and text should be decrypted now | ||||
|             title: note.title, | ||||
|             content: await note.getContent(), | ||||
|             contentLength: -1, // will be updated in .setContent() | ||||
|             type: note.type, | ||||
|             mime: note.mime, | ||||
|             isProtected: false, // will be fixed in the protectNoteRevisions() call | ||||
|             utcDateModifiedFrom: note.utcDateModified, | ||||
|             utcDateModifiedTo: dateUtils.utcNowDateTime(), | ||||
|             dateModifiedFrom: note.dateModified, | ||||
|             dateModifiedTo: dateUtils.localNowDateTime() | ||||
|             utcDateLastEdited: note.utcDateModified, | ||||
|             utcDateCreated: dateUtils.utcNowDateTime(), | ||||
|             utcDateModified: dateUtils.utcNowDateTime(), | ||||
|             dateLastEdited: note.dateModified, | ||||
|             dateCreated: dateUtils.localNowDateTime() | ||||
|         }).save(); | ||||
|  | ||||
|         await noteRevision.setContent(await note.getContent()); | ||||
|     } | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -57,6 +57,11 @@ async function getNotes(noteIds) { | ||||
|     return notes; | ||||
| } | ||||
|  | ||||
| /** @returns {Promise<NoteRevision|null>} */ | ||||
| async function getNoteRevision(noteRevisionId) { | ||||
|     return await getEntity("SELECT * FROM note_revisions WHERE noteRevisionId = ?", [noteRevisionId]); | ||||
| } | ||||
|  | ||||
| /** @returns {Promise<Branch|null>} */ | ||||
| async function getBranch(branchId) { | ||||
|     return await getEntity("SELECT * FROM branches WHERE branchId = ?", [branchId]); | ||||
|   | ||||
| @@ -102,8 +102,8 @@ async function updateNoteRevision(entity, sourceId) { | ||||
|  | ||||
|     await sql.transactional(async () => { | ||||
|         // we update note revision even if date modified to is the same because the only thing which might have changed | ||||
|         // is the protected status (and correnspondingly title and content) which doesn't affect the utcDateModifiedTo | ||||
|         if (orig === null || orig.utcDateModifiedTo <= entity.utcDateModifiedTo) { | ||||
|         // is the protected status (and correnspondingly title and content) which doesn't affect the utcDateCreated | ||||
|         if (orig === null || orig.utcDateCreated <= entity.utcDateCreated) { | ||||
|             entity.content = entity.content === null ? null : Buffer.from(entity.content, 'base64'); | ||||
|  | ||||
|             await sql.replace('note_revisions', entity); | ||||
|   | ||||
		Reference in New Issue
	
	Block a user