mirror of
				https://github.com/zadam/trilium.git
				synced 2025-10-31 10:26:08 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			151 lines
		
	
	
		
			4.9 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			151 lines
		
	
	
		
			4.9 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| "use strict";
 | |
| 
 | |
| const Entity = require('./entity');
 | |
| const protectedSessionService = require('../services/protected_session');
 | |
| const utils = require('../services/utils');
 | |
| const sql = require('../services/sql');
 | |
| const dateUtils = require('../services/date_utils');
 | |
| const entityChangesService = require('../services/entity_changes.js');
 | |
| 
 | |
| /**
 | |
|  * NoteRevision represents snapshot of note's title and content at some point in the past. It's used for seamless note versioning.
 | |
|  *
 | |
|  * @property {string} noteRevisionId
 | |
|  * @property {string} noteId
 | |
|  * @property {string} type
 | |
|  * @property {string} mime
 | |
|  * @property {string} title
 | |
|  * @property {boolean} isErased
 | |
|  * @property {boolean} isProtected
 | |
|  * @property {string} dateLastEdited
 | |
|  * @property {string} dateCreated
 | |
|  * @property {string} utcDateLastEdited
 | |
|  * @property {string} utcDateCreated
 | |
|  * @property {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", "isErased", "isProtected", "dateLastEdited", "dateCreated", "utcDateLastEdited", "utcDateCreated", "utcDateModified"]; }
 | |
| 
 | |
|     constructor(row) {
 | |
|         super(row);
 | |
| 
 | |
|         this.isErased = !!this.isErased;
 | |
|         this.isProtected = !!this.isProtected;
 | |
| 
 | |
|         if (this.isProtected) {
 | |
|             if (protectedSessionService.isProtectedSessionAvailable()) {
 | |
|                 this.title = protectedSessionService.decryptString(this.title);
 | |
|             }
 | |
|             else {
 | |
|                 this.title = "[Protected]";
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     getNote() {
 | |
|         return this.repository.getNote(this.noteId);
 | |
|     }
 | |
| 
 | |
|     /** @returns {boolean} true if the note has string content (not binary) */
 | |
|     isStringNote() {
 | |
|         return utils.isStringNote(this.type, this.mime);
 | |
|     }
 | |
| 
 | |
|     /*
 | |
|      * Note revision content has quite special handling - it's not a separate entity, but a lazily loaded
 | |
|      * part of NoteRevision entity with it's own sync. Reason behind this hybrid design is that
 | |
|      * content can be quite large and it's not necessary to load it / fill memory for any note access even
 | |
|      * if we don't need a content, especially for bulk operations like search.
 | |
|      *
 | |
|      * This is the same approach as is used for Note's content.
 | |
|      */
 | |
| 
 | |
|     /** @returns {*} */
 | |
|     getContent(silentNotFoundError = false) {
 | |
|         if (this.content === undefined) {
 | |
|             const res = sql.getRow(`SELECT content, hash FROM note_revision_contents WHERE noteRevisionId = ?`, [this.noteRevisionId]);
 | |
| 
 | |
|             if (!res) {
 | |
|                 if (silentNotFoundError) {
 | |
|                     return undefined;
 | |
|                 }
 | |
|                 else {
 | |
|                     throw new Error("Cannot find note revision content for noteRevisionId=" + this.noteRevisionId);
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             this.content = res.content;
 | |
| 
 | |
|             if (this.isProtected) {
 | |
|                 if (protectedSessionService.isProtectedSessionAvailable()) {
 | |
|                     this.content = protectedSessionService.decrypt(this.content);
 | |
|                 }
 | |
|                 else {
 | |
|                     this.content = "";
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         if (this.isStringNote()) {
 | |
|             return this.content === null
 | |
|                 ? ""
 | |
|                 : this.content.toString("UTF-8");
 | |
|         }
 | |
|         else {
 | |
|             return this.content;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     setContent(content) {
 | |
|         this.content = content;
 | |
| 
 | |
|         const pojo = {
 | |
|             noteRevisionId: this.noteRevisionId,
 | |
|             content: content,
 | |
|             utcDateModified: dateUtils.utcNowDateTime()
 | |
|         };
 | |
| 
 | |
|         if (this.isProtected) {
 | |
|             if (protectedSessionService.isProtectedSessionAvailable()) {
 | |
|                 pojo.content = protectedSessionService.encrypt(pojo.content);
 | |
|             }
 | |
|             else {
 | |
|                 throw new Error(`Cannot update content of noteRevisionId=${this.noteRevisionId} since we're out of protected session.`);
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         sql.upsert("note_revision_contents", "noteRevisionId", pojo);
 | |
| 
 | |
|         const hash = utils.hash(this.noteRevisionId + "|" + content);
 | |
| 
 | |
|         entityChangesService.addEntityChange('note_revision_contents', this.noteRevisionId, hash);
 | |
|     }
 | |
| 
 | |
|     beforeSaving() {
 | |
|         super.beforeSaving();
 | |
| 
 | |
|         this.utcDateModified = dateUtils.utcNowDateTime();
 | |
|     }
 | |
| 
 | |
|     // cannot be static!
 | |
|     updatePojo(pojo) {
 | |
|         if (pojo.isProtected) {
 | |
|             if (protectedSessionService.isProtectedSessionAvailable()) {
 | |
|                 pojo.title = protectedSessionService.encrypt(pojo.title);
 | |
|             }
 | |
|             else {
 | |
|                 // updating protected note outside of protected session means we will keep original ciphertexts
 | |
|                 delete pojo.title;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         delete pojo.content;
 | |
|     }
 | |
| }
 | |
| 
 | |
| module.exports = NoteRevision;
 |