mirror of
				https://github.com/zadam/trilium.git
				synced 2025-10-31 10:26:08 +01:00 
			
		
		
		
	blob WIP
This commit is contained in:
		| @@ -1,6 +1,6 @@ | ||||
| CREATE TABLE IF NOT EXISTS "note_attachments" | ||||
| CREATE TABLE IF NOT EXISTS "attachments" | ||||
| ( | ||||
|     noteAttachmentId      TEXT not null primary key, | ||||
|     attachmentId      TEXT not null primary key, | ||||
|     parentId       TEXT not null, | ||||
|     role         TEXT not null, | ||||
|     mime         TEXT not null, | ||||
| @@ -11,5 +11,5 @@ CREATE TABLE IF NOT EXISTS "note_attachments" | ||||
|     isDeleted    INT  not null, | ||||
|     deleteId    TEXT DEFAULT NULL); | ||||
| 
 | ||||
| CREATE UNIQUE INDEX IDX_note_attachments_parentId_role | ||||
|     on note_attachments (parentId, role); | ||||
| CREATE UNIQUE INDEX IDX_attachments_parentId_role | ||||
|     on attachments (parentId, role); | ||||
| @@ -35,34 +35,26 @@ CREATE TABLE IF NOT EXISTS "notes" ( | ||||
|                                        `isProtected`	INT NOT NULL DEFAULT 0, | ||||
|                                        `type` TEXT NOT NULL DEFAULT 'text', | ||||
|                                        `mime` TEXT NOT NULL DEFAULT 'text/html', | ||||
|                                        `blobId` TEXT DEFAULT NULL, | ||||
|                                        `isDeleted`	INT NOT NULL DEFAULT 0, | ||||
|                                        `deleteId`   TEXT DEFAULT NULL, | ||||
|                                        `dateCreated`	TEXT NOT NULL, | ||||
|                                        `dateModified`	TEXT NOT NULL, | ||||
|                                        `utcDateCreated`	TEXT NOT NULL, | ||||
|                                        `utcDateModified`	TEXT NOT NULL, | ||||
|                                        `utcDateModified`	TEXT NOT NULL | ||||
|                                        PRIMARY KEY(`noteId`)); | ||||
| CREATE TABLE IF NOT EXISTS "note_contents" ( | ||||
|                                                `noteId`	TEXT NOT NULL, | ||||
|                                                `content`	TEXT NULL DEFAULT NULL, | ||||
|                                                `dateModified` TEXT NOT NULL, | ||||
|                                                `utcDateModified` TEXT NOT NULL, | ||||
|                                                PRIMARY KEY(`noteId`) | ||||
| ); | ||||
| CREATE TABLE IF NOT EXISTS "note_revisions" (`noteRevisionId`	TEXT NOT NULL PRIMARY KEY, | ||||
|                                              `noteId`	TEXT NOT NULL, | ||||
|                                              type TEXT DEFAULT '' NOT NULL, | ||||
|                                              mime TEXT DEFAULT '' NOT NULL, | ||||
|                                              `title`	TEXT NOT NULL, | ||||
|                                              `isProtected`	INT NOT NULL DEFAULT 0, | ||||
|                                              `blobId` TEXT DEFAULT NULL, | ||||
|                                              `utcDateLastEdited` TEXT NOT NULL, | ||||
|                                              `utcDateCreated` TEXT NOT NULL, | ||||
|                                              `utcDateModified` TEXT NOT NULL, | ||||
|                                              `dateLastEdited` TEXT NOT NULL, | ||||
|                                              `dateCreated` TEXT NOT NULL); | ||||
| CREATE TABLE IF NOT EXISTS "note_revision_contents" (`noteRevisionId`	TEXT NOT NULL PRIMARY KEY, | ||||
|                                                      `content`	TEXT, | ||||
|                                                      `utcDateModified` TEXT NOT NULL); | ||||
| CREATE TABLE IF NOT EXISTS "options" | ||||
| ( | ||||
|     name TEXT not null PRIMARY KEY, | ||||
| @@ -112,18 +104,17 @@ CREATE TABLE IF NOT EXISTS "recent_notes" | ||||
|     notePath TEXT not null, | ||||
|     utcDateCreated TEXT not null | ||||
| ); | ||||
| CREATE TABLE IF NOT EXISTS "note_attachments" | ||||
| CREATE TABLE IF NOT EXISTS "attachments" | ||||
| ( | ||||
|     noteAttachmentId      TEXT not null primary key, | ||||
|     noteId       TEXT not null, | ||||
|     name         TEXT not null, | ||||
|     attachmentId      TEXT not null primary key, | ||||
|     parentId       TEXT not null, | ||||
|     role         TEXT not null, | ||||
|     mime         TEXT not null, | ||||
|     title         TEXT not null, | ||||
|     isProtected    INT  not null DEFAULT 0, | ||||
|     blobId    TEXT not null, | ||||
|     utcDateModified TEXT not null, | ||||
|     isDeleted    INT  not null, | ||||
|     `deleteId`    TEXT DEFAULT NULL); | ||||
|  | ||||
| CREATE INDEX IDX_note_attachments_name | ||||
|     on note_attachments (name); | ||||
| CREATE UNIQUE INDEX IDX_note_attachments_noteId_name | ||||
|     on note_attachments (noteId, name); | ||||
|     deleteId    TEXT DEFAULT NULL); | ||||
| CREATE UNIQUE INDEX IDX_attachments_parentId_role | ||||
|     on attachments (parentId, role); | ||||
|   | ||||
| @@ -121,12 +121,12 @@ class Becca { | ||||
|         return row ? new BNoteRevision(row) : null; | ||||
|     } | ||||
|  | ||||
|     /** @returns {BNoteAttachment|null} */ | ||||
|     getNoteAttachment(noteAttachmentId) { | ||||
|         const row = sql.getRow("SELECT * FROM note_attachments WHERE noteAttachmentId = ?", [noteAttachmentId]); | ||||
|     /** @returns {BAttachment|null} */ | ||||
|     getAttachment(attachmentId) { | ||||
|         const row = sql.getRow("SELECT * FROM attachments WHERE attachmentId = ?", [attachmentId]); | ||||
|  | ||||
|         const BNoteAttachment = require("./entities/bnote_attachment"); // avoiding circular dependency problems | ||||
|         return row ? new BNoteAttachment(row) : null; | ||||
|         const BAttachment = require("./entities/battachment"); // avoiding circular dependency problems | ||||
|         return row ? new BAttachment(row) : null; | ||||
|     } | ||||
|  | ||||
|     /** @returns {BOption|null} */ | ||||
| @@ -151,8 +151,8 @@ class Becca { | ||||
|  | ||||
|         if (entityName === 'note_revisions') { | ||||
|             return this.getNoteRevision(entityId); | ||||
|         } else if (entityName === 'note_attachments') { | ||||
|             return this.getNoteAttachment(entityId); | ||||
|         } else if (entityName === 'attachments') { | ||||
|             return this.getAttachment(entityId); | ||||
|         } | ||||
|  | ||||
|         const camelCaseEntityName = entityName.toLowerCase().replace(/(_[a-z])/g, | ||||
|   | ||||
| @@ -9,31 +9,31 @@ const entityChangesService = require('../../services/entity_changes'); | ||||
| const AbstractBeccaEntity = require("./abstract_becca_entity"); | ||||
| 
 | ||||
| /** | ||||
|  * NoteAttachment represent data related/attached to the note. Conceptually similar to attributes, but intended for | ||||
|  * Attachment represent data related/attached to the note. Conceptually similar to attributes, but intended for | ||||
|  * larger amounts of data and generally not accessible to the user. | ||||
|  * | ||||
|  * @extends AbstractBeccaEntity | ||||
|  */ | ||||
| class BNoteAttachment extends AbstractBeccaEntity { | ||||
|     static get entityName() { return "note_attachments"; } | ||||
|     static get primaryKeyName() { return "noteAttachmentId"; } | ||||
|     static get hashedProperties() { return ["noteAttachmentId", "parentId", "role", "mime", "title", "utcDateModified"]; } | ||||
| class BAttachment extends AbstractBeccaEntity { | ||||
|     static get entityName() { return "attachments"; } | ||||
|     static get primaryKeyName() { return "attachmentId"; } | ||||
|     static get hashedProperties() { return ["attachmentId", "parentId", "role", "mime", "title", "utcDateModified"]; } | ||||
| 
 | ||||
|     constructor(row) { | ||||
|         super(); | ||||
| 
 | ||||
|         if (!row.parentId?.trim()) { | ||||
|             throw new Error("'noteId' must be given to initialize a NoteAttachment entity"); | ||||
|             throw new Error("'noteId' must be given to initialize a Attachment entity"); | ||||
|         } else if (!row.role?.trim()) { | ||||
|             throw new Error("'role' must be given to initialize a NoteAttachment entity"); | ||||
|             throw new Error("'role' must be given to initialize a Attachment entity"); | ||||
|         } else if (!row.mime?.trim()) { | ||||
|             throw new Error("'mime' must be given to initialize a NoteAttachment entity"); | ||||
|             throw new Error("'mime' must be given to initialize a Attachment entity"); | ||||
|         } else if (!row.title?.trim()) { | ||||
|             throw new Error("'title' must be given to initialize a NoteAttachment entity"); | ||||
|             throw new Error("'title' must be given to initialize a Attachment entity"); | ||||
|         } | ||||
| 
 | ||||
|         /** @type {string} needs to be set at the initialization time since it's used in the .setContent() */ | ||||
|         this.noteAttachmentId = row.noteAttachmentId || `${this.noteId}_${this.name}`; // FIXME
 | ||||
|         this.attachmentId = row.attachmentId || `${this.noteId}_${this.name}`; // FIXME
 | ||||
|         /** @type {string} either noteId or noteRevisionId to which this attachment belongs */ | ||||
|         this.parentId = row.parentId; | ||||
|         /** @type {string} */ | ||||
| @@ -59,14 +59,14 @@ class BNoteAttachment extends AbstractBeccaEntity { | ||||
| 
 | ||||
|     /** @returns {*} */ | ||||
|     getContent(silentNotFoundError = false) { | ||||
|         const res = sql.getRow(`SELECT content FROM note_attachment_contents WHERE noteAttachmentId = ?`, [this.noteAttachmentId]); | ||||
|         const res = sql.getRow(`SELECT content FROM attachment_contents WHERE attachmentId = ?`, [this.attachmentId]); | ||||
| 
 | ||||
|         if (!res) { | ||||
|             if (silentNotFoundError) { | ||||
|                 return undefined; | ||||
|             } | ||||
|             else { | ||||
|                 throw new Error(`Cannot find note attachment content for noteAttachmentId=${this.noteAttachmentId}`); | ||||
|                 throw new Error(`Cannot find note attachment content for attachmentId=${this.attachmentId}`); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
| @@ -93,10 +93,10 @@ class BNoteAttachment extends AbstractBeccaEntity { | ||||
| 
 | ||||
|     setContent(content) { | ||||
|         sql.transactional(() => { | ||||
|             this.save(); // also explicitly save note_attachment to update contentCheckSum
 | ||||
|             this.save(); // also explicitly save attachment to update contentCheckSum
 | ||||
| 
 | ||||
|             const pojo = { | ||||
|                 noteAttachmentId: this.noteAttachmentId, | ||||
|                 attachmentId: this.attachmentId, | ||||
|                 content: content, | ||||
|                 utcDateModified: dateUtils.utcNowDateTime() | ||||
|             }; | ||||
| @@ -105,15 +105,15 @@ class BNoteAttachment extends AbstractBeccaEntity { | ||||
|                 if (protectedSessionService.isProtectedSessionAvailable()) { | ||||
|                     pojo.content = protectedSessionService.encrypt(pojo.content); | ||||
|                 } else { | ||||
|                     throw new Error(`Cannot update content of noteAttachmentId=${this.noteAttachmentId} since we're out of protected session.`); | ||||
|                     throw new Error(`Cannot update content of attachmentId=${this.attachmentId} since we're out of protected session.`); | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             sql.upsert("note_attachment_contents", "noteAttachmentId", pojo); | ||||
|             sql.upsert("attachment_contents", "attachmentId", pojo); | ||||
| 
 | ||||
|             entityChangesService.addEntityChange({ | ||||
|                 entityName: 'note_attachment_contents', | ||||
|                 entityId: this.noteAttachmentId, | ||||
|                 entityName: 'attachment_contents', | ||||
|                 entityId: this.attachmentId, | ||||
|                 hash: this.contentCheckSum, // FIXME
 | ||||
|                 isErased: false, | ||||
|                 utcDateChanged: pojo.utcDateModified, | ||||
| @@ -123,7 +123,7 @@ class BNoteAttachment extends AbstractBeccaEntity { | ||||
|     } | ||||
| 
 | ||||
|     calculateCheckSum(content) { | ||||
|         return utils.hash(`${this.noteAttachmentId}|${content.toString()}`); | ||||
|         return utils.hash(`${this.attachmentId}|${content.toString()}`); | ||||
|     } | ||||
| 
 | ||||
|     beforeSaving() { | ||||
| @@ -131,7 +131,7 @@ class BNoteAttachment extends AbstractBeccaEntity { | ||||
|             throw new Error(`Name must be alphanumerical, "${this.name}" given.`); | ||||
|         } | ||||
| 
 | ||||
|         this.noteAttachmentId = `${this.noteId}_${this.name}`; // FIXME
 | ||||
|         this.attachmentId = `${this.noteId}_${this.name}`; // FIXME
 | ||||
| 
 | ||||
|         super.beforeSaving(); | ||||
| 
 | ||||
| @@ -140,7 +140,7 @@ class BNoteAttachment extends AbstractBeccaEntity { | ||||
| 
 | ||||
|     getPojo() { | ||||
|         return { | ||||
|             noteAttachmentId: this.noteAttachmentId, | ||||
|             attachmentId: this.attachmentId, | ||||
|             parentId: this.parentId, | ||||
|             name: this.name, | ||||
|             mime: this.mime, | ||||
| @@ -159,4 +159,4 @@ class BNoteAttachment extends AbstractBeccaEntity { | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| module.exports = BNoteAttachment; | ||||
| module.exports = BAttachment; | ||||
| @@ -198,8 +198,8 @@ class BBranch extends AbstractBeccaEntity { | ||||
|                 relation.markAsDeleted(deleteId); | ||||
|             } | ||||
|  | ||||
|             for (const noteAttachment of note.getNoteAttachments()) { | ||||
|                 noteAttachment.markAsDeleted(deleteId); | ||||
|             for (const attachment of note.getAttachments()) { | ||||
|                 attachment.markAsDeleted(deleteId); | ||||
|             } | ||||
|  | ||||
|             note.markAsDeleted(deleteId); | ||||
|   | ||||
| @@ -8,7 +8,7 @@ const dateUtils = require('../../services/date_utils'); | ||||
| const entityChangesService = require('../../services/entity_changes'); | ||||
| const AbstractBeccaEntity = require("./abstract_becca_entity"); | ||||
| const BNoteRevision = require("./bnote_revision"); | ||||
| const BNoteAttachment = require("./bnote_attachment"); | ||||
| const BAttachment = require("./battachment"); | ||||
| const TaskContext = require("../../services/task_context"); | ||||
| const dayjs = require("dayjs"); | ||||
| const utc = require('dayjs/plugin/utc'); | ||||
| @@ -1161,16 +1161,16 @@ class BNote extends AbstractBeccaEntity { | ||||
|             .map(row => new BNoteRevision(row)); | ||||
|     } | ||||
|  | ||||
|     /** @returns {BNoteAttachment[]} */ | ||||
|     getNoteAttachments() { | ||||
|         return sql.getRows("SELECT * FROM note_attachments WHERE noteId = ? AND isDeleted = 0", [this.noteId]) | ||||
|             .map(row => new BNoteAttachment(row)); | ||||
|     /** @returns {BAttachment[]} */ | ||||
|     getAttachments() { | ||||
|         return sql.getRows("SELECT * FROM attachments WHERE noteId = ? AND isDeleted = 0", [this.noteId]) | ||||
|             .map(row => new BAttachment(row)); | ||||
|     } | ||||
|  | ||||
|     /** @returns {BNoteAttachment|undefined} */ | ||||
|     getNoteAttachmentByName(name) { | ||||
|         return sql.getRows("SELECT * FROM note_attachments WHERE noteId = ? AND name = ? AND isDeleted = 0", [this.noteId, name]) | ||||
|             .map(row => new BNoteAttachment(row)) | ||||
|     /** @returns {BAttachment|undefined} */ | ||||
|     getAttachmentByName(name) { | ||||
|         return sql.getRows("SELECT * FROM attachments WHERE noteId = ? AND name = ? AND isDeleted = 0", [this.noteId, name]) | ||||
|             .map(row => new BAttachment(row)) | ||||
|             [0]; | ||||
|     } | ||||
|  | ||||
| @@ -1502,21 +1502,21 @@ class BNote extends AbstractBeccaEntity { | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @returns {BNoteAttachment} | ||||
|      * @returns {BAttachment} | ||||
|      */ | ||||
|     saveNoteAttachment(name, mime, content) { | ||||
|         let noteAttachment = this.getNoteAttachmentByName(name); | ||||
|     saveAttachment(name, mime, content) { | ||||
|         let attachment = this.getAttachmentByName(name); | ||||
|  | ||||
|         noteAttachment = new BNoteAttachment({ | ||||
|         attachment = new BAttachment({ | ||||
|             noteId: this.noteId, | ||||
|             name, | ||||
|             mime, | ||||
|             isProtected: this.isProtected | ||||
|         }); | ||||
|  | ||||
|         noteAttachment.setContent(content); | ||||
|         attachment.setContent(content); | ||||
|  | ||||
|         return noteAttachment; | ||||
|         return attachment; | ||||
|     } | ||||
|  | ||||
|     beforeSaving() { | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| const BNote = require('./entities/bnote'); | ||||
| const BNoteRevision = require('./entities/bnote_revision'); | ||||
| const BNoteAttachment = require("./entities/bnote_attachment"); | ||||
| const BAttachment = require("./entities/battachment"); | ||||
| const BBranch = require('./entities/bbranch'); | ||||
| const BAttribute = require('./entities/battribute'); | ||||
| const BRecentNote = require('./entities/brecent_note'); | ||||
| @@ -12,7 +12,7 @@ const ENTITY_NAME_TO_ENTITY = { | ||||
|     "branches": BBranch, | ||||
|     "notes": BNote, | ||||
|     "note_revisions": BNoteRevision, | ||||
|     "note_attachments": BNoteAttachment, | ||||
|     "attachments": BAttachment, | ||||
|     "recent_notes": BRecentNote, | ||||
|     "etapi_tokens": BEtapiToken, | ||||
|     "options": BOption | ||||
|   | ||||
| @@ -34,7 +34,7 @@ async function processEntityChanges(entityChanges) { | ||||
|  | ||||
|                 loadResults.addOption(ec.entity.name); | ||||
|             } | ||||
|             else if (['etapi_tokens', 'note_attachments'].includes(ec.entityName)) { | ||||
|             else if (['etapi_tokens', 'attachments'].includes(ec.entityName)) { | ||||
|                 // NOOP | ||||
|             } | ||||
|             else { | ||||
|   | ||||
| @@ -28,7 +28,7 @@ const TPL = ` | ||||
|         <a data-trigger-command="renderActiveNote" class="dropdown-item render-note-button"><kbd data-command="renderActiveNote"></kbd> Re-render note</a> | ||||
|         <a data-trigger-command="findInText" class="dropdown-item find-in-text-button">Search in note <kbd data-command="findInText"></a> | ||||
|         <a data-trigger-command="showNoteSource" class="dropdown-item show-source-button"><kbd data-command="showNoteSource"></kbd> Note source</a> | ||||
|         <a data-trigger-command="showNoteAttachments" class="dropdown-item"><kbd data-command="showNoteAttachments"></kbd> Note attachments</a> | ||||
|         <a data-trigger-command="showAttachments" class="dropdown-item"><kbd data-command="showAttachments"></kbd> Note attachments</a> | ||||
|         <a data-trigger-command="openNoteExternally" class="dropdown-item open-note-externally-button"><kbd data-command="openNoteExternally"></kbd> Open note externally</a> | ||||
|         <a class="dropdown-item import-files-button">Import files</a> | ||||
|         <a class="dropdown-item export-note-button">Export note</a> | ||||
|   | ||||
| @@ -127,7 +127,7 @@ function setNoteTypeMime(req) { | ||||
|     note.save(); | ||||
| } | ||||
|  | ||||
| function getNoteAttachments(req) { | ||||
| function getAttachments(req) { | ||||
|     const includeContent = req.query.includeContent === 'true'; | ||||
|     const {noteId} = req.params; | ||||
|  | ||||
| @@ -137,9 +137,9 @@ function getNoteAttachments(req) { | ||||
|         throw new NotFoundError(`Note '${noteId}' doesn't exist.`); | ||||
|     } | ||||
|  | ||||
|     const noteAttachments = note.getNoteAttachments(); | ||||
|     const attachments = note.getAttachments(); | ||||
|  | ||||
|     return noteAttachments.map(attachment => { | ||||
|     return attachments.map(attachment => { | ||||
|        const pojo = attachment.getPojo(); | ||||
|  | ||||
|        if (includeContent && utils.isStringNote(null, attachment.mime)) { | ||||
| @@ -157,7 +157,7 @@ function getNoteAttachments(req) { | ||||
|     }); | ||||
| } | ||||
|  | ||||
| function saveNoteAttachment(req) { | ||||
| function saveAttachment(req) { | ||||
|     const {noteId, name} = req.params; | ||||
|     const {mime, content} = req.body; | ||||
|  | ||||
| @@ -167,7 +167,7 @@ function saveNoteAttachment(req) { | ||||
|         throw new NotFoundError(`Note '${noteId}' doesn't exist.`); | ||||
|     } | ||||
|  | ||||
|     note.saveNoteAttachment(name, mime, content); | ||||
|     note.saveAttachment(name, mime, content); | ||||
| } | ||||
|  | ||||
| function getRelationMap(req) { | ||||
| @@ -384,6 +384,6 @@ module.exports = { | ||||
|     getDeleteNotesPreview, | ||||
|     uploadModifiedFile, | ||||
|     forceSaveNoteRevision, | ||||
|     getNoteAttachments, | ||||
|     saveNoteAttachment | ||||
|     getAttachments, | ||||
|     saveAttachment | ||||
| }; | ||||
|   | ||||
| @@ -113,9 +113,9 @@ function forceNoteSync(req) { | ||||
|         entityChangesService.moveEntityChangeToTop('note_revisions', noteRevisionId); | ||||
|     } | ||||
|  | ||||
|     for (const noteAttachmentId of sql.getColumn("SELECT noteAttachmentId FROM note_attachments WHERE noteId = ?", [noteId])) { | ||||
|         sql.execute(`UPDATE note_attachments SET utcDateModified = ? WHERE noteAttachmentId = ?`, [now, noteAttachmentId]); | ||||
|         entityChangesService.moveEntityChangeToTop('note_attachments', noteAttachmentId); | ||||
|     for (const attachmentId of sql.getColumn("SELECT attachmentId FROM attachments WHERE noteId = ?", [noteId])) { | ||||
|         sql.execute(`UPDATE attachments SET utcDateModified = ? WHERE attachmentId = ?`, [now, attachmentId]); | ||||
|         entityChangesService.moveEntityChangeToTop('attachments', attachmentId); | ||||
|     } | ||||
|  | ||||
|     log.info(`Forcing note sync for ${noteId}`); | ||||
|   | ||||
| @@ -126,8 +126,8 @@ function register(app) { | ||||
|     apiRoute(PUT, '/api/notes/:noteId/sort-children', notesApiRoute.sortChildNotes); | ||||
|     apiRoute(PUT, '/api/notes/:noteId/protect/:isProtected', notesApiRoute.protectNote); | ||||
|     apiRoute(PUT, '/api/notes/:noteId/type', notesApiRoute.setNoteTypeMime); | ||||
|     apiRoute(GET, '/api/notes/:noteId/attachments', notesApiRoute.getNoteAttachments); | ||||
|     apiRoute(PUT, '/api/notes/:noteId/attachments/:name', notesApiRoute.saveNoteAttachment); | ||||
|     apiRoute(GET, '/api/notes/:noteId/attachments', notesApiRoute.getAttachments); | ||||
|     apiRoute(PUT, '/api/notes/:noteId/attachments/:name', notesApiRoute.saveAttachment); | ||||
|     apiRoute(GET, '/api/notes/:noteId/revisions', noteRevisionsApiRoute.getNoteRevisions); | ||||
|     apiRoute(DELETE, '/api/notes/:noteId/revisions', noteRevisionsApiRoute.eraseAllNoteRevisions); | ||||
|     apiRoute(GET, '/api/notes/:noteId/revisions/:noteRevisionId', noteRevisionsApiRoute.getNoteRevision); | ||||
|   | ||||
| @@ -4,9 +4,9 @@ const log = require("./log"); | ||||
| /** | ||||
|  * @param {BNote} note | ||||
|  */ | ||||
| function protectNoteAttachments(note) { | ||||
|     for (const noteAttachment of note.getNoteAttachments()) { | ||||
|         if (note.isProtected !== noteAttachment.isProtected) { | ||||
| function protectAttachments(note) { | ||||
|     for (const attachment of note.getAttachments()) { | ||||
|         if (note.isProtected !== attachment.isProtected) { | ||||
|             if (!protectedSession.isProtectedSessionAvailable()) { | ||||
|                 log.error("Protected session is not available to fix note attachments."); | ||||
| 
 | ||||
| @@ -14,17 +14,17 @@ function protectNoteAttachments(note) { | ||||
|             } | ||||
| 
 | ||||
|             try { | ||||
|                 const content = noteAttachment.getContent(); | ||||
|                 const content = attachment.getContent(); | ||||
| 
 | ||||
|                 noteAttachment.isProtected = note.isProtected; | ||||
|                 attachment.isProtected = note.isProtected; | ||||
| 
 | ||||
|                 // this will force de/encryption
 | ||||
|                 noteAttachment.setContent(content); | ||||
|                 attachment.setContent(content); | ||||
| 
 | ||||
|                 noteAttachment.save(); | ||||
|                 attachment.save(); | ||||
|             } | ||||
|             catch (e) { | ||||
|                 log.error(`Could not un/protect note attachment ID = ${noteAttachment.noteAttachmentId}`); | ||||
|                 log.error(`Could not un/protect note attachment ID = ${attachment.attachmentId}`); | ||||
| 
 | ||||
|                 throw e; | ||||
|             } | ||||
| @@ -33,5 +33,5 @@ function protectNoteAttachments(note) { | ||||
| } | ||||
| 
 | ||||
| module.exports = { | ||||
|     protectNoteAttachments | ||||
|     protectAttachments | ||||
| } | ||||
| @@ -214,24 +214,25 @@ class ConsistencyChecks { | ||||
|                 } | ||||
|             }); | ||||
|  | ||||
|         this.findAndFixIssues(` | ||||
|                     SELECT noteAttachmentId, note_attachments.noteId AS noteId | ||||
|                     FROM note_attachments | ||||
|                       LEFT JOIN notes ON notes.noteId = note_attachments.parentId | ||||
|                     WHERE notes.noteId IS NULL | ||||
|                       AND note_attachments.isDeleted = 0`, | ||||
|             ({noteAttachmentId, noteId}) => { | ||||
|                 if (this.autoFix) { | ||||
|                     const noteAttachment = becca.getNoteAttachment(noteAttachmentId); | ||||
|                     noteAttachment.markAsDeleted(); | ||||
|  | ||||
|                     this.reloadNeeded = false; | ||||
|  | ||||
|                     logFix(`Note attachment '${noteAttachmentId}' has been deleted since it references missing note '${noteId}'`); | ||||
|                 } else { | ||||
|                     logError(`Note attachment '${noteAttachmentId}' references missing note '${noteId}'`); | ||||
|                 } | ||||
|             }); | ||||
|         // FIXME | ||||
|         // this.findAndFixIssues(` | ||||
|         //             SELECT attachmentId, attachments.parentId AS noteId | ||||
|         //             FROM attachments | ||||
|         //               LEFT JOIN notes ON notes.noteId = attachments.parentId | ||||
|         //             WHERE notes.noteId IS NULL | ||||
|         //               AND attachments.isDeleted = 0`, | ||||
|         //     ({attachmentId, noteId}) => { | ||||
|         //         if (this.autoFix) { | ||||
|         //             const attachment = becca.getAttachment(attachmentId); | ||||
|         //             attachment.markAsDeleted(); | ||||
|         // | ||||
|         //             this.reloadNeeded = false; | ||||
|         // | ||||
|         //             logFix(`Note attachment '${attachmentId}' has been deleted since it references missing note '${noteId}'`); | ||||
|         //         } else { | ||||
|         //             logError(`Note attachment '${attachmentId}' references missing note '${noteId}'`); | ||||
|         //         } | ||||
|         //     }); | ||||
|     } | ||||
|  | ||||
|     findExistencyIssues() { | ||||
| @@ -341,22 +342,22 @@ class ConsistencyChecks { | ||||
|             }); | ||||
|  | ||||
|         this.findAndFixIssues(` | ||||
|                     SELECT noteAttachmentId, | ||||
|                            note_attachments.noteId AS noteId | ||||
|                     FROM note_attachments | ||||
|                       JOIN notes USING (noteId) | ||||
|                     SELECT attachmentId, | ||||
|                            attachments.parentId AS noteId | ||||
|                     FROM attachments | ||||
|                       JOIN notes ON notes.noteId = attachments.parentId | ||||
|                     WHERE notes.isDeleted = 1 | ||||
|                       AND note_attachments.isDeleted = 0`, | ||||
|             ({noteAttachmentId, noteId}) => { | ||||
|                       AND attachments.isDeleted = 0`, | ||||
|             ({attachmentId, noteId}) => { | ||||
|                 if (this.autoFix) { | ||||
|                     const noteAttachment = becca.getNoteAttachment(noteAttachmentId); | ||||
|                     noteAttachment.markAsDeleted(); | ||||
|                     const attachment = becca.getAttachment(attachmentId); | ||||
|                     attachment.markAsDeleted(); | ||||
|  | ||||
|                     this.reloadNeeded = false; | ||||
|  | ||||
|                     logFix(`Note attachment '${noteAttachmentId}' has been deleted since associated note '${noteId}' is deleted.`); | ||||
|                     logFix(`Note attachment '${attachmentId}' has been deleted since associated note '${noteId}' is deleted.`); | ||||
|                 } else { | ||||
|                     logError(`Note attachment '${noteAttachmentId}' is not deleted even though associated note '${noteId}' is deleted.`) | ||||
|                     logError(`Note attachment '${attachmentId}' is not deleted even though associated note '${noteId}' is deleted.`) | ||||
|                 } | ||||
|             }); | ||||
|     } | ||||
| @@ -657,7 +658,7 @@ class ConsistencyChecks { | ||||
|     findEntityChangeIssues() { | ||||
|         this.runEntityChangeChecks("notes", "noteId"); | ||||
|         this.runEntityChangeChecks("note_revisions", "noteRevisionId"); | ||||
|         this.runEntityChangeChecks("note_attachments", "noteAttachmentId"); | ||||
|         this.runEntityChangeChecks("attachments", "attachmentId"); | ||||
|         this.runEntityChangeChecks("blobs", "blobId"); | ||||
|         this.runEntityChangeChecks("branches", "branchId"); | ||||
|         this.runEntityChangeChecks("attributes", "attributeId"); | ||||
| @@ -754,7 +755,7 @@ class ConsistencyChecks { | ||||
|             return `${tableName}: ${count}`; | ||||
|         } | ||||
|  | ||||
|         const tables = [ "notes", "note_revisions", "note_attachments", "branches", "attributes", "etapi_tokens" ]; | ||||
|         const tables = [ "notes", "note_revisions", "attachments", "branches", "attributes", "etapi_tokens" ]; | ||||
|  | ||||
|         log.info(`Table counts: ${tables.map(tableName => getTableRowCount(tableName)).join(", ")}`); | ||||
|     } | ||||
|   | ||||
| @@ -149,7 +149,7 @@ function fillAllEntityChanges() { | ||||
|         fillEntityChanges("notes", "noteId"); | ||||
|         fillEntityChanges("branches", "branchId"); | ||||
|         fillEntityChanges("note_revisions", "noteRevisionId"); | ||||
|         fillEntityChanges("note_attachments", "noteAttachmentId"); | ||||
|         fillEntityChanges("attachments", "attachmentId"); | ||||
|         fillEntityChanges("blobs", "blobId"); | ||||
|         fillEntityChanges("attributes", "attributeId"); | ||||
|         fillEntityChanges("etapi_tokens", "etapiTokenId"); | ||||
|   | ||||
| @@ -170,7 +170,7 @@ async function exportToZip(taskContext, branch, format, res, setHeaders = true) | ||||
|             meta.dataFileName = getDataFileName(note.type, note.mime, baseFileName, existingFileNames); | ||||
|         } | ||||
|  | ||||
|         const attachments = note.getNoteAttachments(); | ||||
|         const attachments = note.getAttachments(); | ||||
|  | ||||
|         if (attachments.length > 0) { | ||||
|             meta.attachments = attachments | ||||
| @@ -339,8 +339,8 @@ ${markdownContent}`; | ||||
|  | ||||
|         for (const attachmentMeta of noteMeta.attachments || []) { | ||||
|             // FIXME | ||||
|             const noteAttachment = note.getNoteAttachmentByName(attachmentMeta.name); | ||||
|             const content = noteAttachment.getContent(); | ||||
|             const attachment = note.getAttachmentByName(attachmentMeta.name); | ||||
|             const content = attachment.getContent(); | ||||
|  | ||||
|             archive.append(content, { | ||||
|                 name: filePathPrefix + attachmentMeta.dataFileName, | ||||
|   | ||||
| @@ -14,7 +14,7 @@ const treeService = require("../tree"); | ||||
| const yauzl = require("yauzl"); | ||||
| const htmlSanitizer = require('../html_sanitizer'); | ||||
| const becca = require("../../becca/becca"); | ||||
| const BNoteAttachment = require("../../becca/entities/bnote_attachment"); | ||||
| const BAttachment = require("../../becca/entities/battachment"); | ||||
|  | ||||
| /** | ||||
|  * @param {TaskContext} taskContext | ||||
| @@ -379,14 +379,14 @@ async function importZip(taskContext, fileBuffer, importRootNote) { | ||||
|         const noteId = getNoteId(noteMeta, filePath); | ||||
|  | ||||
|         if (attachmentMeta) { | ||||
|             const noteAttachment = new BNoteAttachment({ | ||||
|             const attachment = new BAttachment({ | ||||
|                 parentId: noteId, | ||||
|                 title: attachmentMeta.title, | ||||
|                 role: attachmentMeta.role, | ||||
|                 mime: attachmentMeta.mime | ||||
|             }); | ||||
|  | ||||
|             noteAttachment.setContent(content); | ||||
|             attachment.setContent(content); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|   | ||||
| @@ -114,7 +114,7 @@ function eraseEntity(entityChange, instanceId) { | ||||
|         "branches", | ||||
|         "attributes", | ||||
|         "note_revisions", | ||||
|         "note_attachments", | ||||
|         "attachments", | ||||
|         "blobs", | ||||
|     ]; | ||||
|  | ||||
|   | ||||
| @@ -152,7 +152,7 @@ const ORDERING = { | ||||
|     "blobs": 0, | ||||
|     "note_reordering": 2, | ||||
|     "note_revisions": 2, | ||||
|     "note_attachments": 3, | ||||
|     "attachments": 3, | ||||
|     "notes": 1, | ||||
|     "options": 0 | ||||
| }; | ||||
|   | ||||
		Reference in New Issue
	
	Block a user