mirror of
				https://github.com/zadam/trilium.git
				synced 2025-10-31 18:36:30 +01:00 
			
		
		
		
	getting rid of note complement WIP
This commit is contained in:
		| @@ -137,6 +137,15 @@ class Becca { | |||||||
|             .map(row => new BAttachment(row)); |             .map(row => new BAttachment(row)); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     /** @returns {BBlob|null} */ | ||||||
|  |     getBlob(blobId) { | ||||||
|  |         const row = sql.getRow("SELECT *, LENGTH(content) AS contentLength " + | ||||||
|  |                                      "FROM blob WHERE blobId = ?", [blobId]); | ||||||
|  |  | ||||||
|  |         const BBlob = require("./entities/bblob"); // avoiding circular dependency problems | ||||||
|  |         return row ? new BBlob(row) : null; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     /** @returns {BOption|null} */ |     /** @returns {BOption|null} */ | ||||||
|     getOption(name) { |     getOption(name) { | ||||||
|         return this.options[name]; |         return this.options[name]; | ||||||
| @@ -161,6 +170,8 @@ class Becca { | |||||||
|             return this.getNoteRevision(entityId); |             return this.getNoteRevision(entityId); | ||||||
|         } else if (entityName === 'attachments') { |         } else if (entityName === 'attachments') { | ||||||
|             return this.getAttachment(entityId); |             return this.getAttachment(entityId); | ||||||
|  |         } else if (entityName === 'blobs') { | ||||||
|  |             return this.getBlob(entityId); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         const camelCaseEntityName = entityName.toLowerCase().replace(/(_[a-z])/g, |         const camelCaseEntityName = entityName.toLowerCase().replace(/(_[a-z])/g, | ||||||
|   | |||||||
| @@ -142,7 +142,7 @@ class AbstractBeccaEntity { | |||||||
|             throw new Error(`Cannot set null content to ${this.constructor.primaryKeyName} '${this[this.constructor.primaryKeyName]}'`); |             throw new Error(`Cannot set null content to ${this.constructor.primaryKeyName} '${this[this.constructor.primaryKeyName]}'`); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         if (this.isStringNote()) { |         if (this.hasStringContent()) { | ||||||
|             content = content.toString(); |             content = content.toString(); | ||||||
|         } |         } | ||||||
|         else { |         else { | ||||||
| @@ -246,7 +246,7 @@ class AbstractBeccaEntity { | |||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         if (this.isStringNote()) { |         if (this.hasStringContent()) { | ||||||
|             return content === null |             return content === null | ||||||
|                 ? "" |                 ? "" | ||||||
|                 : content.toString("UTF-8"); |                 : content.toString("UTF-8"); | ||||||
|   | |||||||
| @@ -76,7 +76,7 @@ class BAttachment extends AbstractBeccaEntity { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     /** @returns {boolean} true if the note has string content (not binary) */ |     /** @returns {boolean} true if the note has string content (not binary) */ | ||||||
|     isStringNote() { |     hasStringContent() { | ||||||
|         return utils.isStringNote(this.type, this.mime); |         return utils.isStringNote(this.type, this.mime); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										26
									
								
								src/becca/entities/bblob.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								src/becca/entities/bblob.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,26 @@ | |||||||
|  | class BBlob { | ||||||
|  |     constructor(row) { | ||||||
|  |         /** @type {string} */ | ||||||
|  |         this.blobId = row.blobId; | ||||||
|  |         /** @type {string|Buffer} */ | ||||||
|  |         this.content = row.content; | ||||||
|  |         /** @type {number} */ | ||||||
|  |         this.contentLength = row.contentLength; | ||||||
|  |         /** @type {string} */ | ||||||
|  |         this.dateModified = row.dateModified; | ||||||
|  |         /** @type {string} */ | ||||||
|  |         this.utcDateModified = row.utcDateModified; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     getPojo() { | ||||||
|  |         return { | ||||||
|  |             blobId: this.blobId, | ||||||
|  |             content: this.content, | ||||||
|  |             contentLength: this.contentLength, | ||||||
|  |             dateModified: this.dateModified, | ||||||
|  |             utcDateModified: this.utcDateModified | ||||||
|  |         }; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | module.exports = BBlob; | ||||||
| @@ -103,6 +103,7 @@ class BBranch extends AbstractBeccaEntity { | |||||||
|         return this.becca.notes[this.noteId]; |         return this.becca.notes[this.noteId]; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     /** @returns {BNote} */ | ||||||
|     getNote() { |     getNote() { | ||||||
|         return this.childNote; |         return this.childNote; | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -301,8 +301,13 @@ class BNote extends AbstractBeccaEntity { | |||||||
|             || (this.type === 'file' && this.mime?.startsWith('image/')); |             || (this.type === 'file' && this.mime?.startsWith('image/')); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /** @returns {boolean} true if the note has string content (not binary) */ |     /** @deprecated use hasStringContent() instead */ | ||||||
|     isStringNote() { |     isStringNote() { | ||||||
|  |         return this.hasStringContent(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** @returns {boolean} true if the note has string content (not binary) */ | ||||||
|  |     hasStringContent() { | ||||||
|         return utils.isStringNote(this.type, this.mime); |         return utils.isStringNote(this.type, this.mime); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -61,7 +61,7 @@ class BNoteRevision extends AbstractBeccaEntity { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     /** @returns {boolean} true if the note has string content (not binary) */ |     /** @returns {boolean} true if the note has string content (not binary) */ | ||||||
|     isStringNote() { |     hasStringContent() { | ||||||
|         return utils.isStringNote(this.type, this.mime); |         return utils.isStringNote(this.type, this.mime); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -166,15 +166,6 @@ class NoteContext extends Component { | |||||||
|         return this.notePath ? this.notePath.split('/') : []; |         return this.notePath ? this.notePath.split('/') : []; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /** @returns {FNoteComplement} */ |  | ||||||
|     async getNoteComplement() { |  | ||||||
|         if (!this.noteId) { |  | ||||||
|             return null; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         return await froca.getNoteComplement(this.noteId); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     isActive() { |     isActive() { | ||||||
|         return appContext.tabManager.activeNtxId === this.ntxId; |         return appContext.tabManager.activeNtxId === this.ntxId; | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -30,6 +30,14 @@ class FAttachment { | |||||||
|     getNote() { |     getNote() { | ||||||
|         return this.froca.notes[this.parentId]; |         return this.froca.notes[this.parentId]; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * @param [opts.full=false] - force retrieval of the full note | ||||||
|  |      * @return {FBlob} | ||||||
|  |      */ | ||||||
|  |     async getBlob(opts = {}) { | ||||||
|  |         return await this.froca.getBlob('attachments', this.attachmentId, opts); | ||||||
|  |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| export default FAttachment; | export default FAttachment; | ||||||
|   | |||||||
							
								
								
									
										17
									
								
								src/public/app/entities/fblob.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								src/public/app/entities/fblob.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,17 @@ | |||||||
|  | class FBlob { | ||||||
|  |     constructor(row) { | ||||||
|  |         /** @type {string} */ | ||||||
|  |         this.blobId = row.blobId; | ||||||
|  |  | ||||||
|  |         /** | ||||||
|  |          * can either contain the whole content (in e.g. string notes), only part (large text notes) or nothing at all (binary notes, images) | ||||||
|  |          * @type {string} | ||||||
|  |          */ | ||||||
|  |         this.content = row.content; | ||||||
|  |  | ||||||
|  |         /** @type {string} */ | ||||||
|  |         this.dateModified = row.dateModified; | ||||||
|  |         /** @type {string} */ | ||||||
|  |         this.utcDateModified = row.utcDateModified; | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -851,13 +851,17 @@ class FNote { | |||||||
|         return await this.froca.getNotes(targetRelations.map(tr => tr.noteId)); |         return await this.froca.getNotes(targetRelations.map(tr => tr.noteId)); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /** |     /** @deprecated use getBlob() instead */ | ||||||
|      * Return note complement which is most importantly note's content |  | ||||||
|      * |  | ||||||
|      * @returns {Promise<FNoteComplement>} |  | ||||||
|      */ |  | ||||||
|     async getNoteComplement() { |     async getNoteComplement() { | ||||||
|         return await this.froca.getNoteComplement(this.noteId); |         return this.getBlob({ full: true }); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * @param [opts.full=false] - force retrieval of the full note | ||||||
|  |      * @return {FBlob} | ||||||
|  |      */ | ||||||
|  |     async getBlob(opts = {}) { | ||||||
|  |         return await this.froca.getBlob('notes', this.noteId, opts); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     toString() { |     toString() { | ||||||
|   | |||||||
| @@ -1,41 +0,0 @@ | |||||||
| /** |  | ||||||
|  * FIXME: probably make it a FBlob |  | ||||||
|  * Complements the FNote with the main note content and other extra attributes |  | ||||||
|  */ |  | ||||||
| class FNoteComplement { |  | ||||||
|     constructor(row) { |  | ||||||
|         /** @type {string} */ |  | ||||||
|         this.noteId = row.noteId; |  | ||||||
|  |  | ||||||
|         /** |  | ||||||
|          * can either contain the whole content (in e.g. string notes), only part (large text notes) or nothing at all (binary notes, images) |  | ||||||
|          * @type {string} |  | ||||||
|          */ |  | ||||||
|         this.content = row.content; |  | ||||||
|  |  | ||||||
|         /** @type {int} */ |  | ||||||
|         this.contentLength = row.contentLength; |  | ||||||
|  |  | ||||||
|         /** @type {string} */ |  | ||||||
|         this.dateCreated = row.dateCreated; |  | ||||||
|  |  | ||||||
|         /** @type {string} */ |  | ||||||
|         this.dateModified = row.dateModified; |  | ||||||
|  |  | ||||||
|         /** @type {string} */ |  | ||||||
|         this.utcDateCreated = row.utcDateCreated; |  | ||||||
|  |  | ||||||
|         /** @type {string} */ |  | ||||||
|         this.utcDateModified = row.utcDateModified; |  | ||||||
|  |  | ||||||
|         // "combined" date modified give larger out of note's and blob's dateModified |  | ||||||
|  |  | ||||||
|         /** @type {string} */ |  | ||||||
|         this.combinedDateModified = row.combinedDateModified; |  | ||||||
|  |  | ||||||
|         /** @type {string} */ |  | ||||||
|         this.combinedUtcDateModified = row.combinedUtcDateModified; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| export default FNoteComplement; |  | ||||||
| @@ -3,7 +3,7 @@ import FNote from "../entities/fnote.js"; | |||||||
| import FAttribute from "../entities/fattribute.js"; | import FAttribute from "../entities/fattribute.js"; | ||||||
| import server from "./server.js"; | import server from "./server.js"; | ||||||
| import appContext from "../components/app_context.js"; | import appContext from "../components/app_context.js"; | ||||||
| import FNoteComplement from "../entities/fnote_complement.js"; | import FBlob from "../entities/fblob.js"; | ||||||
| import FAttachment from "../entities/fattachment.js"; | import FAttachment from "../entities/fattachment.js"; | ||||||
|  |  | ||||||
| /** | /** | ||||||
| @@ -38,8 +38,7 @@ class Froca { | |||||||
|         /** @type {Object.<string, FAttachment>} */ |         /** @type {Object.<string, FAttachment>} */ | ||||||
|         this.attachments = {}; |         this.attachments = {}; | ||||||
|  |  | ||||||
|         // FIXME |         /** @type {Object.<string, Promise<FBlob>>} */ | ||||||
|         /** @type {Object.<string, Promise<FNoteComplement>>} */ |  | ||||||
|         this.blobPromises = {}; |         this.blobPromises = {}; | ||||||
|  |  | ||||||
|         this.addResp(resp); |         this.addResp(resp); | ||||||
| @@ -321,25 +320,24 @@ class Froca { | |||||||
|         return attachmentRow ? new FAttachment(this, attachmentRow) : null; |         return attachmentRow ? new FAttachment(this, attachmentRow) : null; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /** |     async getBlob(entityType, entityId, opts = {}) { | ||||||
|      * // FIXME |         opts.full = !!opts.full; | ||||||
|      * @returns {Promise<FNoteComplement>} |         const key = `${entityType}-${entityId}`; | ||||||
|      */ |  | ||||||
|     async getNoteComplement(noteId) { |         if (!this.blobPromises[key]) { | ||||||
|         if (!this.blobPromises[noteId]) { |             this.blobPromises[key] = server.get(`${entityType}/${entityId}/blob?full=${opts.full}`) | ||||||
|             this.blobPromises[noteId] = server.get(`notes/${noteId}`) |                 .then(row => new FBlob(row)) | ||||||
|                 .then(row => new FNoteComplement(row)) |                 .catch(e => console.error(`Cannot get blob for ${entityType} '${entityId}'`)); | ||||||
|                 .catch(e => console.error(`Cannot get note complement for note '${noteId}'`)); |  | ||||||
|  |  | ||||||
|             // we don't want to keep large payloads forever in memory, so we clean that up quite quickly |             // we don't want to keep large payloads forever in memory, so we clean that up quite quickly | ||||||
|             // this cache is more meant to share the data between different components within one business transaction (e.g. loading of the note into the tab context and all the components) |             // this cache is more meant to share the data between different components within one business transaction (e.g. loading of the note into the tab context and all the components) | ||||||
|             // this is also a workaround for missing invalidation after change |             // this is also a workaround for missing invalidation after change | ||||||
|             this.blobPromises[noteId].then( |             this.blobPromises[key].then( | ||||||
|                 () => setTimeout(() => this.blobPromises[noteId] = null, 1000) |                 () => setTimeout(() => this.blobPromises[key] = null, 1000) | ||||||
|             ); |             ); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         return await this.blobPromises[noteId]; |         return await this.blobPromises[key]; | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -90,7 +90,7 @@ export default class TocWidget extends RightPanelWidget { | |||||||
|         let $toc = "", headingCount = 0; |         let $toc = "", headingCount = 0; | ||||||
|         // Check for type text unconditionally in case alwaysShowWidget is set |         // Check for type text unconditionally in case alwaysShowWidget is set | ||||||
|         if (this.note.type === 'text') { |         if (this.note.type === 'text') { | ||||||
|             const { content } = await note.getNoteComplement(); |             const { content } = await note.getBlob(); | ||||||
|             ({$toc, headingCount} = await this.getToc(content)); |             ({$toc, headingCount} = await this.getToc(content)); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,6 +1,13 @@ | |||||||
| const becca = require("../../becca/becca"); | const becca = require("../../becca/becca"); | ||||||
| const NotFoundError = require("../../errors/not_found_error"); | const NotFoundError = require("../../errors/not_found_error"); | ||||||
| const utils = require("../../services/utils"); | const utils = require("../../services/utils"); | ||||||
|  | const blobService = require("../../services/blob.js"); | ||||||
|  |  | ||||||
|  | function getAttachmentBlob(req) { | ||||||
|  |     const full = req.query.full === 'true'; | ||||||
|  |  | ||||||
|  |     return blobService.getBlobPojo('attachments', req.params.attachmentId, { full }); | ||||||
|  | } | ||||||
|  |  | ||||||
| function getAttachments(req) { | function getAttachments(req) { | ||||||
|     const includeContent = req.query.includeContent === 'true'; |     const includeContent = req.query.includeContent === 'true'; | ||||||
| @@ -87,6 +94,7 @@ function convertAttachmentToNote(req) { | |||||||
| } | } | ||||||
|  |  | ||||||
| module.exports = { | module.exports = { | ||||||
|  |     getAttachmentBlob, | ||||||
|     getAttachments, |     getAttachments, | ||||||
|     getAttachment, |     getAttachment, | ||||||
|     saveAttachment, |     saveAttachment, | ||||||
|   | |||||||
| @@ -1,13 +1,19 @@ | |||||||
| "use strict"; | "use strict"; | ||||||
|  |  | ||||||
| const beccaService = require('../../becca/becca_service'); | const beccaService = require('../../becca/becca_service'); | ||||||
| const protectedSessionService = require('../../services/protected_session'); |  | ||||||
| const noteRevisionService = require('../../services/note_revisions'); | const noteRevisionService = require('../../services/note_revisions'); | ||||||
| const utils = require('../../services/utils'); | const utils = require('../../services/utils'); | ||||||
| const sql = require('../../services/sql'); | const sql = require('../../services/sql'); | ||||||
| const cls = require('../../services/cls'); | const cls = require('../../services/cls'); | ||||||
| const path = require('path'); | const path = require('path'); | ||||||
| const becca = require("../../becca/becca"); | const becca = require("../../becca/becca"); | ||||||
|  | const blobService = require("../../services/blob.js"); | ||||||
|  |  | ||||||
|  | function getNoteRevisionBlob(req) { | ||||||
|  |     const full = req.query.full === 'true'; | ||||||
|  |  | ||||||
|  |     return blobService.getBlobPojo('note_revisions', req.params.noteRevisionId, { full }); | ||||||
|  | } | ||||||
|  |  | ||||||
| function getNoteRevisions(req) { | function getNoteRevisions(req) { | ||||||
|     return becca.getNoteRevisionsFromQuery(` |     return becca.getNoteRevisionsFromQuery(` | ||||||
| @@ -20,10 +26,11 @@ function getNoteRevisions(req) { | |||||||
| } | } | ||||||
|  |  | ||||||
| function getNoteRevision(req) { | function getNoteRevision(req) { | ||||||
|  |     // FIXME | ||||||
|     const noteRevision = becca.getNoteRevision(req.params.noteRevisionId); |     const noteRevision = becca.getNoteRevision(req.params.noteRevisionId); | ||||||
|  |  | ||||||
|     if (noteRevision.type === 'file') { |     if (noteRevision.type === 'file') { | ||||||
|         if (noteRevision.isStringNote()) { |         if (noteRevision.hasStringContent()) { | ||||||
|             noteRevision.content = noteRevision.getContent().substr(0, 10000); |             noteRevision.content = noteRevision.getContent().substr(0, 10000); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| @@ -180,6 +187,7 @@ function getNotePathData(note) { | |||||||
| } | } | ||||||
|  |  | ||||||
| module.exports = { | module.exports = { | ||||||
|  |     getNoteRevisionBlob, | ||||||
|     getNoteRevisions, |     getNoteRevisions, | ||||||
|     getNoteRevision, |     getNoteRevision, | ||||||
|     downloadNoteRevision, |     downloadNoteRevision, | ||||||
|   | |||||||
| @@ -10,20 +10,20 @@ const fs = require('fs'); | |||||||
| const becca = require("../../becca/becca"); | const becca = require("../../becca/becca"); | ||||||
| const ValidationError = require("../../errors/validation_error"); | const ValidationError = require("../../errors/validation_error"); | ||||||
| const NotFoundError = require("../../errors/not_found_error"); | const NotFoundError = require("../../errors/not_found_error"); | ||||||
|  | const blobService = require("../../services/blob"); | ||||||
|  |  | ||||||
| function getNote(req) { | function getNote(req) { | ||||||
|     const noteId = req.params.noteId; |     const note = becca.getNote(req.params.noteId); | ||||||
|     const note = becca.getNote(noteId); |  | ||||||
|  |  | ||||||
|     if (!note) { |     if (!note) { | ||||||
|         throw new NotFoundError(`Note '${noteId}' has not been found.`); |         throw new NotFoundError(`Note '${req.params.noteId}' has not been found.`); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     const pojo = note.getPojo(); |     const pojo = note.getPojo(); | ||||||
|  |  | ||||||
|     if (note.isStringNote()) { |     if (note.hasStringContent()) { | ||||||
|         pojo.content = note.getContent(); |         pojo.content = note.getContent(); | ||||||
|  |  | ||||||
|  |         // FIXME: use blobs instead | ||||||
|         if (note.type === 'file' && pojo.content.length > 10000) { |         if (note.type === 'file' && pojo.content.length > 10000) { | ||||||
|             pojo.content = `${pojo.content.substr(0, 10000)}\r\n\r\n... and ${pojo.content.length - 10000} more characters.`; |             pojo.content = `${pojo.content.substr(0, 10000)}\r\n\r\n... and ${pojo.content.length - 10000} more characters.`; | ||||||
|         } |         } | ||||||
| @@ -39,6 +39,12 @@ function getNote(req) { | |||||||
|     return pojo; |     return pojo; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | function getNoteBlob(req) { | ||||||
|  |     const full = req.query.full === 'true'; | ||||||
|  |  | ||||||
|  |     return blobService.getBlobPojo('notes', req.params.noteId, { full }); | ||||||
|  | } | ||||||
|  |  | ||||||
| function createNote(req) { | function createNote(req) { | ||||||
|     const params = Object.assign({}, req.body); // clone |     const params = Object.assign({}, req.body); // clone | ||||||
|     params.parentNoteId = req.params.parentNoteId; |     params.parentNoteId = req.params.parentNoteId; | ||||||
| @@ -259,6 +265,7 @@ function convertNoteToAttachment(req) { | |||||||
|  |  | ||||||
| module.exports = { | module.exports = { | ||||||
|     getNote, |     getNote, | ||||||
|  |     getNoteBlob, | ||||||
|     updateNoteData, |     updateNoteData, | ||||||
|     deleteNote, |     deleteNote, | ||||||
|     undeleteNote, |     undeleteNote, | ||||||
|   | |||||||
| @@ -112,6 +112,7 @@ function register(app) { | |||||||
|     apiRoute(PST, '/api/tree/load', treeApiRoute.load); |     apiRoute(PST, '/api/tree/load', treeApiRoute.load); | ||||||
|  |  | ||||||
|     apiRoute(GET, '/api/notes/:noteId', notesApiRoute.getNote); |     apiRoute(GET, '/api/notes/:noteId', notesApiRoute.getNote); | ||||||
|  |     apiRoute(GET, '/api/notes/:noteId/blob', notesApiRoute.getNoteBlob); | ||||||
|     apiRoute(PUT, '/api/notes/:noteId/data', notesApiRoute.updateNoteData); |     apiRoute(PUT, '/api/notes/:noteId/data', notesApiRoute.updateNoteData); | ||||||
|     apiRoute(DEL, '/api/notes/:noteId', notesApiRoute.deleteNote); |     apiRoute(DEL, '/api/notes/:noteId', notesApiRoute.deleteNote); | ||||||
|     apiRoute(PUT, '/api/notes/:noteId/undelete', notesApiRoute.undeleteNote); |     apiRoute(PUT, '/api/notes/:noteId/undelete', notesApiRoute.undeleteNote); | ||||||
| @@ -153,6 +154,7 @@ function register(app) { | |||||||
|     apiRoute(GET, '/api/attachments/:attachmentId', attachmentsApiRoute.getAttachment); |     apiRoute(GET, '/api/attachments/:attachmentId', attachmentsApiRoute.getAttachment); | ||||||
|     apiRoute(PST, '/api/attachments/:attachmentId/convert-to-note', attachmentsApiRoute.convertAttachmentToNote); |     apiRoute(PST, '/api/attachments/:attachmentId/convert-to-note', attachmentsApiRoute.convertAttachmentToNote); | ||||||
|     apiRoute(DEL, '/api/attachments/:attachmentId', attachmentsApiRoute.deleteAttachment); |     apiRoute(DEL, '/api/attachments/:attachmentId', attachmentsApiRoute.deleteAttachment); | ||||||
|  |     apiRoute(GET, '/api/attachments/:attachmentId/blob', attachmentsApiRoute.getAttachmentBlob); | ||||||
|     route(GET, '/api/attachments/:attachmentId/image/:filename', [auth.checkApiAuthOrElectron], imageRoute.returnAttachedImage); |     route(GET, '/api/attachments/:attachmentId/image/:filename', [auth.checkApiAuthOrElectron], imageRoute.returnAttachedImage); | ||||||
|     route(GET, '/api/attachments/:attachmentId/open', [auth.checkApiAuthOrElectron], filesRoute.openAttachment); |     route(GET, '/api/attachments/:attachmentId/open', [auth.checkApiAuthOrElectron], filesRoute.openAttachment); | ||||||
|     route(GET, '/api/attachments/:attachmentId/open-partial', [auth.checkApiAuthOrElectron], |     route(GET, '/api/attachments/:attachmentId/open-partial', [auth.checkApiAuthOrElectron], | ||||||
| @@ -170,6 +172,7 @@ function register(app) { | |||||||
|     apiRoute(GET, '/api/notes/:noteId/revisions', noteRevisionsApiRoute.getNoteRevisions); |     apiRoute(GET, '/api/notes/:noteId/revisions', noteRevisionsApiRoute.getNoteRevisions); | ||||||
|     apiRoute(DEL, '/api/notes/:noteId/revisions', noteRevisionsApiRoute.eraseAllNoteRevisions); |     apiRoute(DEL, '/api/notes/:noteId/revisions', noteRevisionsApiRoute.eraseAllNoteRevisions); | ||||||
|     apiRoute(GET, '/api/revisions/:noteRevisionId', noteRevisionsApiRoute.getNoteRevision); |     apiRoute(GET, '/api/revisions/:noteRevisionId', noteRevisionsApiRoute.getNoteRevision); | ||||||
|  |     apiRoute(GET, '/api/revisions/:noteRevisionId/blob', noteRevisionsApiRoute.getNoteRevisionBlob); | ||||||
|     apiRoute(DEL, '/api/revisions/:noteRevisionId', noteRevisionsApiRoute.eraseNoteRevision); |     apiRoute(DEL, '/api/revisions/:noteRevisionId', noteRevisionsApiRoute.eraseNoteRevision); | ||||||
|     apiRoute(PST, '/api/revisions/:noteRevisionId/restore', noteRevisionsApiRoute.restoreNoteRevision); |     apiRoute(PST, '/api/revisions/:noteRevisionId/restore', noteRevisionsApiRoute.restoreNoteRevision); | ||||||
|     route(GET, '/api/revisions/:noteRevisionId/download', [auth.checkApiAuthOrElectron], noteRevisionsApiRoute.downloadNoteRevision); |     route(GET, '/api/revisions/:noteRevisionId/download', [auth.checkApiAuthOrElectron], noteRevisionsApiRoute.downloadNoteRevision); | ||||||
|   | |||||||
							
								
								
									
										28
									
								
								src/services/blob.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								src/services/blob.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,28 @@ | |||||||
|  | const becca = require('../becca/becca'); | ||||||
|  | const NotFoundError = require("../errors/not_found_error"); | ||||||
|  |  | ||||||
|  | function getBlobPojo(entityName, entityId, opts = {}) { | ||||||
|  |     opts.full = !!opts.full; | ||||||
|  |  | ||||||
|  |     const entity = becca.getEntity(entityName, entityId); | ||||||
|  |  | ||||||
|  |     if (!entity) { | ||||||
|  |         throw new NotFoundError(`Entity ${entityName} '${entityId}' was not found.`); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     const blob = becca.getBlob(entity.blobId); | ||||||
|  |  | ||||||
|  |     const pojo = blob.getPojo(); | ||||||
|  |  | ||||||
|  |     if (!entity.hasStringContent()) { | ||||||
|  |         pojo.content = null; | ||||||
|  |     } else if (!opts.full && pojo.content.length > 10000) { | ||||||
|  |         pojo.content = `${pojo.content.substr(0, 10000)}\r\n\r\n... and ${pojo.content.length - 10000} more characters.`; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return pojo; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | module.exports = { | ||||||
|  |     getBlobPojo | ||||||
|  | }; | ||||||
| @@ -24,13 +24,13 @@ function exportToOpml(taskContext, branch, version, res) { | |||||||
|  |  | ||||||
|         if (opmlVersion === 1) { |         if (opmlVersion === 1) { | ||||||
|             const preparedTitle = escapeXmlAttribute(title); |             const preparedTitle = escapeXmlAttribute(title); | ||||||
|             const preparedContent = note.isStringNote() ? prepareText(note.getContent()) : ''; |             const preparedContent = note.hasStringContent() ? prepareText(note.getContent()) : ''; | ||||||
|  |  | ||||||
|             res.write(`<outline title="${preparedTitle}" text="${preparedContent}">\n`); |             res.write(`<outline title="${preparedTitle}" text="${preparedContent}">\n`); | ||||||
|         } |         } | ||||||
|         else if (opmlVersion === 2) { |         else if (opmlVersion === 2) { | ||||||
|             const preparedTitle = escapeXmlAttribute(title); |             const preparedTitle = escapeXmlAttribute(title); | ||||||
|             const preparedContent = note.isStringNote() ? escapeXmlAttribute(note.getContent()) : ''; |             const preparedContent = note.hasStringContent() ? escapeXmlAttribute(note.getContent()) : ''; | ||||||
|  |  | ||||||
|             res.write(`<outline text="${preparedTitle}" _note="${preparedContent}">\n`); |             res.write(`<outline text="${preparedTitle}" _note="${preparedContent}">\n`); | ||||||
|         } |         } | ||||||
|   | |||||||
| @@ -90,7 +90,7 @@ eventService.subscribe(eventService.ENTITY_CREATED, ({ entityName, entity }) => | |||||||
|             if (["text", "code"].includes(note.type) |             if (["text", "code"].includes(note.type) | ||||||
|                 // if the note has already content we're not going to overwrite it with template's one |                 // if the note has already content we're not going to overwrite it with template's one | ||||||
|                 && (!content || content.trim().length === 0) |                 && (!content || content.trim().length === 0) | ||||||
|                 && templateNote.isStringNote()) { |                 && templateNote.hasStringContent()) { | ||||||
|  |  | ||||||
|                 const templateNoteContent = templateNote.getContent(); |                 const templateNoteContent = templateNote.getContent(); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -21,7 +21,6 @@ const htmlSanitizer = require("./html_sanitizer"); | |||||||
| const ValidationError = require("../errors/validation_error"); | const ValidationError = require("../errors/validation_error"); | ||||||
| const noteTypesService = require("./note_types"); | const noteTypesService = require("./note_types"); | ||||||
| const fs = require("fs"); | const fs = require("fs"); | ||||||
| const BAttachment = require("../becca/entities/battachment"); |  | ||||||
|  |  | ||||||
| /** @param {BNote} parentNote */ | /** @param {BNote} parentNote */ | ||||||
| function getNewNotePosition(parentNote) { | function getNewNotePosition(parentNote) { | ||||||
|   | |||||||
| @@ -107,7 +107,7 @@ class SNote extends AbstractShacaEntity { | |||||||
|  |  | ||||||
|         let content = row.content; |         let content = row.content; | ||||||
|  |  | ||||||
|         if (this.isStringNote()) { |         if (this.hasStringContent()) { | ||||||
|             return content === null |             return content === null | ||||||
|                 ? "" |                 ? "" | ||||||
|                 : content.toString("UTF-8"); |                 : content.toString("UTF-8"); | ||||||
| @@ -118,7 +118,7 @@ class SNote extends AbstractShacaEntity { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     /** @returns {boolean} true if the note has string content (not binary) */ |     /** @returns {boolean} true if the note has string content (not binary) */ | ||||||
|     isStringNote() { |     hasStringContent() { | ||||||
|         return utils.isStringNote(this.type, this.mime); |         return utils.isStringNote(this.type, this.mime); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user