mirror of
https://github.com/zadam/trilium.git
synced 2025-03-01 14:22:32 +01:00
getting rid of note complement WIP
This commit is contained in:
parent
9cdcbb3125
commit
5e1f81e53e
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user