This commit is contained in:
zadam 2023-03-16 12:17:55 +01:00
parent eee05a4d01
commit b6efc954bd
19 changed files with 127 additions and 135 deletions

View File

@ -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, parentId TEXT not null,
role TEXT not null, role TEXT not null,
mime TEXT not null, mime TEXT not null,
@ -11,5 +11,5 @@ CREATE TABLE IF NOT EXISTS "note_attachments"
isDeleted INT not null, isDeleted INT not null,
deleteId TEXT DEFAULT NULL); deleteId TEXT DEFAULT NULL);
CREATE UNIQUE INDEX IDX_note_attachments_parentId_role CREATE UNIQUE INDEX IDX_attachments_parentId_role
on note_attachments (parentId, role); on attachments (parentId, role);

View File

@ -35,34 +35,26 @@ CREATE TABLE IF NOT EXISTS "notes" (
`isProtected` INT NOT NULL DEFAULT 0, `isProtected` INT NOT NULL DEFAULT 0,
`type` TEXT NOT NULL DEFAULT 'text', `type` TEXT NOT NULL DEFAULT 'text',
`mime` TEXT NOT NULL DEFAULT 'text/html', `mime` TEXT NOT NULL DEFAULT 'text/html',
`blobId` TEXT DEFAULT NULL,
`isDeleted` INT NOT NULL DEFAULT 0, `isDeleted` INT NOT NULL DEFAULT 0,
`deleteId` TEXT DEFAULT NULL, `deleteId` TEXT DEFAULT NULL,
`dateCreated` TEXT NOT NULL, `dateCreated` TEXT NOT NULL,
`dateModified` TEXT NOT NULL, `dateModified` TEXT NOT NULL,
`utcDateCreated` TEXT NOT NULL, `utcDateCreated` TEXT NOT NULL,
`utcDateModified` TEXT NOT NULL, `utcDateModified` TEXT NOT NULL
PRIMARY KEY(`noteId`)); 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, CREATE TABLE IF NOT EXISTS "note_revisions" (`noteRevisionId` TEXT NOT NULL PRIMARY KEY,
`noteId` TEXT NOT NULL, `noteId` TEXT NOT NULL,
type TEXT DEFAULT '' NOT NULL, type TEXT DEFAULT '' NOT NULL,
mime TEXT DEFAULT '' NOT NULL, mime TEXT DEFAULT '' NOT NULL,
`title` TEXT NOT NULL, `title` TEXT NOT NULL,
`isProtected` INT NOT NULL DEFAULT 0, `isProtected` INT NOT NULL DEFAULT 0,
`blobId` TEXT DEFAULT NULL,
`utcDateLastEdited` TEXT NOT NULL, `utcDateLastEdited` TEXT NOT NULL,
`utcDateCreated` TEXT NOT NULL, `utcDateCreated` TEXT NOT NULL,
`utcDateModified` TEXT NOT NULL, `utcDateModified` TEXT NOT NULL,
`dateLastEdited` TEXT NOT NULL, `dateLastEdited` TEXT NOT NULL,
`dateCreated` 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" CREATE TABLE IF NOT EXISTS "options"
( (
name TEXT not null PRIMARY KEY, name TEXT not null PRIMARY KEY,
@ -112,18 +104,17 @@ CREATE TABLE IF NOT EXISTS "recent_notes"
notePath TEXT not null, notePath TEXT not null,
utcDateCreated 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, attachmentId TEXT not null primary key,
noteId TEXT not null, parentId TEXT not null,
name TEXT not null, role TEXT not null,
mime TEXT not null, mime TEXT not null,
title TEXT not null,
isProtected INT not null DEFAULT 0, isProtected INT not null DEFAULT 0,
blobId TEXT not null,
utcDateModified TEXT not null, utcDateModified TEXT not null,
isDeleted INT not null, isDeleted INT not null,
`deleteId` TEXT DEFAULT NULL); deleteId TEXT DEFAULT NULL);
CREATE UNIQUE INDEX IDX_attachments_parentId_role
CREATE INDEX IDX_note_attachments_name on attachments (parentId, role);
on note_attachments (name);
CREATE UNIQUE INDEX IDX_note_attachments_noteId_name
on note_attachments (noteId, name);

View File

@ -121,12 +121,12 @@ class Becca {
return row ? new BNoteRevision(row) : null; return row ? new BNoteRevision(row) : null;
} }
/** @returns {BNoteAttachment|null} */ /** @returns {BAttachment|null} */
getNoteAttachment(noteAttachmentId) { getAttachment(attachmentId) {
const row = sql.getRow("SELECT * FROM note_attachments WHERE noteAttachmentId = ?", [noteAttachmentId]); const row = sql.getRow("SELECT * FROM attachments WHERE attachmentId = ?", [attachmentId]);
const BNoteAttachment = require("./entities/bnote_attachment"); // avoiding circular dependency problems const BAttachment = require("./entities/battachment"); // avoiding circular dependency problems
return row ? new BNoteAttachment(row) : null; return row ? new BAttachment(row) : null;
} }
/** @returns {BOption|null} */ /** @returns {BOption|null} */
@ -151,8 +151,8 @@ class Becca {
if (entityName === 'note_revisions') { if (entityName === 'note_revisions') {
return this.getNoteRevision(entityId); return this.getNoteRevision(entityId);
} else if (entityName === 'note_attachments') { } else if (entityName === 'attachments') {
return this.getNoteAttachment(entityId); return this.getAttachment(entityId);
} }
const camelCaseEntityName = entityName.toLowerCase().replace(/(_[a-z])/g, const camelCaseEntityName = entityName.toLowerCase().replace(/(_[a-z])/g,

View File

@ -9,31 +9,31 @@ const entityChangesService = require('../../services/entity_changes');
const AbstractBeccaEntity = require("./abstract_becca_entity"); 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. * larger amounts of data and generally not accessible to the user.
* *
* @extends AbstractBeccaEntity * @extends AbstractBeccaEntity
*/ */
class BNoteAttachment extends AbstractBeccaEntity { class BAttachment extends AbstractBeccaEntity {
static get entityName() { return "note_attachments"; } static get entityName() { return "attachments"; }
static get primaryKeyName() { return "noteAttachmentId"; } static get primaryKeyName() { return "attachmentId"; }
static get hashedProperties() { return ["noteAttachmentId", "parentId", "role", "mime", "title", "utcDateModified"]; } static get hashedProperties() { return ["attachmentId", "parentId", "role", "mime", "title", "utcDateModified"]; }
constructor(row) { constructor(row) {
super(); super();
if (!row.parentId?.trim()) { 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()) { } 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()) { } 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()) { } 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() */ /** @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 */ /** @type {string} either noteId or noteRevisionId to which this attachment belongs */
this.parentId = row.parentId; this.parentId = row.parentId;
/** @type {string} */ /** @type {string} */
@ -59,14 +59,14 @@ class BNoteAttachment extends AbstractBeccaEntity {
/** @returns {*} */ /** @returns {*} */
getContent(silentNotFoundError = false) { 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 (!res) {
if (silentNotFoundError) { if (silentNotFoundError) {
return undefined; return undefined;
} }
else { 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) { setContent(content) {
sql.transactional(() => { sql.transactional(() => {
this.save(); // also explicitly save note_attachment to update contentCheckSum this.save(); // also explicitly save attachment to update contentCheckSum
const pojo = { const pojo = {
noteAttachmentId: this.noteAttachmentId, attachmentId: this.attachmentId,
content: content, content: content,
utcDateModified: dateUtils.utcNowDateTime() utcDateModified: dateUtils.utcNowDateTime()
}; };
@ -105,15 +105,15 @@ class BNoteAttachment extends AbstractBeccaEntity {
if (protectedSessionService.isProtectedSessionAvailable()) { if (protectedSessionService.isProtectedSessionAvailable()) {
pojo.content = protectedSessionService.encrypt(pojo.content); pojo.content = protectedSessionService.encrypt(pojo.content);
} else { } 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({ entityChangesService.addEntityChange({
entityName: 'note_attachment_contents', entityName: 'attachment_contents',
entityId: this.noteAttachmentId, entityId: this.attachmentId,
hash: this.contentCheckSum, // FIXME hash: this.contentCheckSum, // FIXME
isErased: false, isErased: false,
utcDateChanged: pojo.utcDateModified, utcDateChanged: pojo.utcDateModified,
@ -123,7 +123,7 @@ class BNoteAttachment extends AbstractBeccaEntity {
} }
calculateCheckSum(content) { calculateCheckSum(content) {
return utils.hash(`${this.noteAttachmentId}|${content.toString()}`); return utils.hash(`${this.attachmentId}|${content.toString()}`);
} }
beforeSaving() { beforeSaving() {
@ -131,7 +131,7 @@ class BNoteAttachment extends AbstractBeccaEntity {
throw new Error(`Name must be alphanumerical, "${this.name}" given.`); 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(); super.beforeSaving();
@ -140,7 +140,7 @@ class BNoteAttachment extends AbstractBeccaEntity {
getPojo() { getPojo() {
return { return {
noteAttachmentId: this.noteAttachmentId, attachmentId: this.attachmentId,
parentId: this.parentId, parentId: this.parentId,
name: this.name, name: this.name,
mime: this.mime, mime: this.mime,
@ -159,4 +159,4 @@ class BNoteAttachment extends AbstractBeccaEntity {
} }
} }
module.exports = BNoteAttachment; module.exports = BAttachment;

View File

@ -198,8 +198,8 @@ class BBranch extends AbstractBeccaEntity {
relation.markAsDeleted(deleteId); relation.markAsDeleted(deleteId);
} }
for (const noteAttachment of note.getNoteAttachments()) { for (const attachment of note.getAttachments()) {
noteAttachment.markAsDeleted(deleteId); attachment.markAsDeleted(deleteId);
} }
note.markAsDeleted(deleteId); note.markAsDeleted(deleteId);

View File

@ -8,7 +8,7 @@ const dateUtils = require('../../services/date_utils');
const entityChangesService = require('../../services/entity_changes'); const entityChangesService = require('../../services/entity_changes');
const AbstractBeccaEntity = require("./abstract_becca_entity"); const AbstractBeccaEntity = require("./abstract_becca_entity");
const BNoteRevision = require("./bnote_revision"); const BNoteRevision = require("./bnote_revision");
const BNoteAttachment = require("./bnote_attachment"); const BAttachment = require("./battachment");
const TaskContext = require("../../services/task_context"); const TaskContext = require("../../services/task_context");
const dayjs = require("dayjs"); const dayjs = require("dayjs");
const utc = require('dayjs/plugin/utc'); const utc = require('dayjs/plugin/utc');
@ -1161,16 +1161,16 @@ class BNote extends AbstractBeccaEntity {
.map(row => new BNoteRevision(row)); .map(row => new BNoteRevision(row));
} }
/** @returns {BNoteAttachment[]} */ /** @returns {BAttachment[]} */
getNoteAttachments() { getAttachments() {
return sql.getRows("SELECT * FROM note_attachments WHERE noteId = ? AND isDeleted = 0", [this.noteId]) return sql.getRows("SELECT * FROM attachments WHERE noteId = ? AND isDeleted = 0", [this.noteId])
.map(row => new BNoteAttachment(row)); .map(row => new BAttachment(row));
} }
/** @returns {BNoteAttachment|undefined} */ /** @returns {BAttachment|undefined} */
getNoteAttachmentByName(name) { getAttachmentByName(name) {
return sql.getRows("SELECT * FROM note_attachments WHERE noteId = ? AND name = ? AND isDeleted = 0", [this.noteId, name]) return sql.getRows("SELECT * FROM attachments WHERE noteId = ? AND name = ? AND isDeleted = 0", [this.noteId, name])
.map(row => new BNoteAttachment(row)) .map(row => new BAttachment(row))
[0]; [0];
} }
@ -1502,21 +1502,21 @@ class BNote extends AbstractBeccaEntity {
} }
/** /**
* @returns {BNoteAttachment} * @returns {BAttachment}
*/ */
saveNoteAttachment(name, mime, content) { saveAttachment(name, mime, content) {
let noteAttachment = this.getNoteAttachmentByName(name); let attachment = this.getAttachmentByName(name);
noteAttachment = new BNoteAttachment({ attachment = new BAttachment({
noteId: this.noteId, noteId: this.noteId,
name, name,
mime, mime,
isProtected: this.isProtected isProtected: this.isProtected
}); });
noteAttachment.setContent(content); attachment.setContent(content);
return noteAttachment; return attachment;
} }
beforeSaving() { beforeSaving() {

View File

@ -1,6 +1,6 @@
const BNote = require('./entities/bnote'); const BNote = require('./entities/bnote');
const BNoteRevision = require('./entities/bnote_revision'); const BNoteRevision = require('./entities/bnote_revision');
const BNoteAttachment = require("./entities/bnote_attachment"); const BAttachment = require("./entities/battachment");
const BBranch = require('./entities/bbranch'); const BBranch = require('./entities/bbranch');
const BAttribute = require('./entities/battribute'); const BAttribute = require('./entities/battribute');
const BRecentNote = require('./entities/brecent_note'); const BRecentNote = require('./entities/brecent_note');
@ -12,7 +12,7 @@ const ENTITY_NAME_TO_ENTITY = {
"branches": BBranch, "branches": BBranch,
"notes": BNote, "notes": BNote,
"note_revisions": BNoteRevision, "note_revisions": BNoteRevision,
"note_attachments": BNoteAttachment, "attachments": BAttachment,
"recent_notes": BRecentNote, "recent_notes": BRecentNote,
"etapi_tokens": BEtapiToken, "etapi_tokens": BEtapiToken,
"options": BOption "options": BOption

View File

@ -34,7 +34,7 @@ async function processEntityChanges(entityChanges) {
loadResults.addOption(ec.entity.name); loadResults.addOption(ec.entity.name);
} }
else if (['etapi_tokens', 'note_attachments'].includes(ec.entityName)) { else if (['etapi_tokens', 'attachments'].includes(ec.entityName)) {
// NOOP // NOOP
} }
else { else {

View File

@ -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="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="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="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 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 import-files-button">Import files</a>
<a class="dropdown-item export-note-button">Export note</a> <a class="dropdown-item export-note-button">Export note</a>

View File

@ -127,7 +127,7 @@ function setNoteTypeMime(req) {
note.save(); note.save();
} }
function getNoteAttachments(req) { function getAttachments(req) {
const includeContent = req.query.includeContent === 'true'; const includeContent = req.query.includeContent === 'true';
const {noteId} = req.params; const {noteId} = req.params;
@ -137,9 +137,9 @@ function getNoteAttachments(req) {
throw new NotFoundError(`Note '${noteId}' doesn't exist.`); 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(); const pojo = attachment.getPojo();
if (includeContent && utils.isStringNote(null, attachment.mime)) { 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 {noteId, name} = req.params;
const {mime, content} = req.body; const {mime, content} = req.body;
@ -167,7 +167,7 @@ function saveNoteAttachment(req) {
throw new NotFoundError(`Note '${noteId}' doesn't exist.`); throw new NotFoundError(`Note '${noteId}' doesn't exist.`);
} }
note.saveNoteAttachment(name, mime, content); note.saveAttachment(name, mime, content);
} }
function getRelationMap(req) { function getRelationMap(req) {
@ -384,6 +384,6 @@ module.exports = {
getDeleteNotesPreview, getDeleteNotesPreview,
uploadModifiedFile, uploadModifiedFile,
forceSaveNoteRevision, forceSaveNoteRevision,
getNoteAttachments, getAttachments,
saveNoteAttachment saveAttachment
}; };

View File

@ -113,9 +113,9 @@ function forceNoteSync(req) {
entityChangesService.moveEntityChangeToTop('note_revisions', noteRevisionId); entityChangesService.moveEntityChangeToTop('note_revisions', noteRevisionId);
} }
for (const noteAttachmentId of sql.getColumn("SELECT noteAttachmentId FROM note_attachments WHERE noteId = ?", [noteId])) { for (const attachmentId of sql.getColumn("SELECT attachmentId FROM attachments WHERE noteId = ?", [noteId])) {
sql.execute(`UPDATE note_attachments SET utcDateModified = ? WHERE noteAttachmentId = ?`, [now, noteAttachmentId]); sql.execute(`UPDATE attachments SET utcDateModified = ? WHERE attachmentId = ?`, [now, attachmentId]);
entityChangesService.moveEntityChangeToTop('note_attachments', noteAttachmentId); entityChangesService.moveEntityChangeToTop('attachments', attachmentId);
} }
log.info(`Forcing note sync for ${noteId}`); log.info(`Forcing note sync for ${noteId}`);

View File

@ -126,8 +126,8 @@ function register(app) {
apiRoute(PUT, '/api/notes/:noteId/sort-children', notesApiRoute.sortChildNotes); apiRoute(PUT, '/api/notes/:noteId/sort-children', notesApiRoute.sortChildNotes);
apiRoute(PUT, '/api/notes/:noteId/protect/:isProtected', notesApiRoute.protectNote); apiRoute(PUT, '/api/notes/:noteId/protect/:isProtected', notesApiRoute.protectNote);
apiRoute(PUT, '/api/notes/:noteId/type', notesApiRoute.setNoteTypeMime); apiRoute(PUT, '/api/notes/:noteId/type', notesApiRoute.setNoteTypeMime);
apiRoute(GET, '/api/notes/:noteId/attachments', notesApiRoute.getNoteAttachments); apiRoute(GET, '/api/notes/:noteId/attachments', notesApiRoute.getAttachments);
apiRoute(PUT, '/api/notes/:noteId/attachments/:name', notesApiRoute.saveNoteAttachment); apiRoute(PUT, '/api/notes/:noteId/attachments/:name', notesApiRoute.saveAttachment);
apiRoute(GET, '/api/notes/:noteId/revisions', noteRevisionsApiRoute.getNoteRevisions); apiRoute(GET, '/api/notes/:noteId/revisions', noteRevisionsApiRoute.getNoteRevisions);
apiRoute(DELETE, '/api/notes/:noteId/revisions', noteRevisionsApiRoute.eraseAllNoteRevisions); apiRoute(DELETE, '/api/notes/:noteId/revisions', noteRevisionsApiRoute.eraseAllNoteRevisions);
apiRoute(GET, '/api/notes/:noteId/revisions/:noteRevisionId', noteRevisionsApiRoute.getNoteRevision); apiRoute(GET, '/api/notes/:noteId/revisions/:noteRevisionId', noteRevisionsApiRoute.getNoteRevision);

View File

@ -4,9 +4,9 @@ const log = require("./log");
/** /**
* @param {BNote} note * @param {BNote} note
*/ */
function protectNoteAttachments(note) { function protectAttachments(note) {
for (const noteAttachment of note.getNoteAttachments()) { for (const attachment of note.getAttachments()) {
if (note.isProtected !== noteAttachment.isProtected) { if (note.isProtected !== attachment.isProtected) {
if (!protectedSession.isProtectedSessionAvailable()) { if (!protectedSession.isProtectedSessionAvailable()) {
log.error("Protected session is not available to fix note attachments."); log.error("Protected session is not available to fix note attachments.");
@ -14,17 +14,17 @@ function protectNoteAttachments(note) {
} }
try { try {
const content = noteAttachment.getContent(); const content = attachment.getContent();
noteAttachment.isProtected = note.isProtected; attachment.isProtected = note.isProtected;
// this will force de/encryption // this will force de/encryption
noteAttachment.setContent(content); attachment.setContent(content);
noteAttachment.save(); attachment.save();
} }
catch (e) { 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; throw e;
} }
@ -33,5 +33,5 @@ function protectNoteAttachments(note) {
} }
module.exports = { module.exports = {
protectNoteAttachments protectAttachments
} }

View File

@ -214,24 +214,25 @@ class ConsistencyChecks {
} }
}); });
this.findAndFixIssues(` // FIXME
SELECT noteAttachmentId, note_attachments.noteId AS noteId // this.findAndFixIssues(`
FROM note_attachments // SELECT attachmentId, attachments.parentId AS noteId
LEFT JOIN notes ON notes.noteId = note_attachments.parentId // FROM attachments
WHERE notes.noteId IS NULL // LEFT JOIN notes ON notes.noteId = attachments.parentId
AND note_attachments.isDeleted = 0`, // WHERE notes.noteId IS NULL
({noteAttachmentId, noteId}) => { // AND attachments.isDeleted = 0`,
if (this.autoFix) { // ({attachmentId, noteId}) => {
const noteAttachment = becca.getNoteAttachment(noteAttachmentId); // if (this.autoFix) {
noteAttachment.markAsDeleted(); // const attachment = becca.getAttachment(attachmentId);
// attachment.markAsDeleted();
this.reloadNeeded = false; //
// this.reloadNeeded = false;
logFix(`Note attachment '${noteAttachmentId}' has been deleted since it references missing note '${noteId}'`); //
} else { // logFix(`Note attachment '${attachmentId}' has been deleted since it references missing note '${noteId}'`);
logError(`Note attachment '${noteAttachmentId}' references missing note '${noteId}'`); // } else {
} // logError(`Note attachment '${attachmentId}' references missing note '${noteId}'`);
}); // }
// });
} }
findExistencyIssues() { findExistencyIssues() {
@ -341,22 +342,22 @@ class ConsistencyChecks {
}); });
this.findAndFixIssues(` this.findAndFixIssues(`
SELECT noteAttachmentId, SELECT attachmentId,
note_attachments.noteId AS noteId attachments.parentId AS noteId
FROM note_attachments FROM attachments
JOIN notes USING (noteId) JOIN notes ON notes.noteId = attachments.parentId
WHERE notes.isDeleted = 1 WHERE notes.isDeleted = 1
AND note_attachments.isDeleted = 0`, AND attachments.isDeleted = 0`,
({noteAttachmentId, noteId}) => { ({attachmentId, noteId}) => {
if (this.autoFix) { if (this.autoFix) {
const noteAttachment = becca.getNoteAttachment(noteAttachmentId); const attachment = becca.getAttachment(attachmentId);
noteAttachment.markAsDeleted(); attachment.markAsDeleted();
this.reloadNeeded = false; 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 { } 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() { findEntityChangeIssues() {
this.runEntityChangeChecks("notes", "noteId"); this.runEntityChangeChecks("notes", "noteId");
this.runEntityChangeChecks("note_revisions", "noteRevisionId"); this.runEntityChangeChecks("note_revisions", "noteRevisionId");
this.runEntityChangeChecks("note_attachments", "noteAttachmentId"); this.runEntityChangeChecks("attachments", "attachmentId");
this.runEntityChangeChecks("blobs", "blobId"); this.runEntityChangeChecks("blobs", "blobId");
this.runEntityChangeChecks("branches", "branchId"); this.runEntityChangeChecks("branches", "branchId");
this.runEntityChangeChecks("attributes", "attributeId"); this.runEntityChangeChecks("attributes", "attributeId");
@ -754,7 +755,7 @@ class ConsistencyChecks {
return `${tableName}: ${count}`; 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(", ")}`); log.info(`Table counts: ${tables.map(tableName => getTableRowCount(tableName)).join(", ")}`);
} }

View File

@ -149,7 +149,7 @@ function fillAllEntityChanges() {
fillEntityChanges("notes", "noteId"); fillEntityChanges("notes", "noteId");
fillEntityChanges("branches", "branchId"); fillEntityChanges("branches", "branchId");
fillEntityChanges("note_revisions", "noteRevisionId"); fillEntityChanges("note_revisions", "noteRevisionId");
fillEntityChanges("note_attachments", "noteAttachmentId"); fillEntityChanges("attachments", "attachmentId");
fillEntityChanges("blobs", "blobId"); fillEntityChanges("blobs", "blobId");
fillEntityChanges("attributes", "attributeId"); fillEntityChanges("attributes", "attributeId");
fillEntityChanges("etapi_tokens", "etapiTokenId"); fillEntityChanges("etapi_tokens", "etapiTokenId");

View File

@ -170,7 +170,7 @@ async function exportToZip(taskContext, branch, format, res, setHeaders = true)
meta.dataFileName = getDataFileName(note.type, note.mime, baseFileName, existingFileNames); meta.dataFileName = getDataFileName(note.type, note.mime, baseFileName, existingFileNames);
} }
const attachments = note.getNoteAttachments(); const attachments = note.getAttachments();
if (attachments.length > 0) { if (attachments.length > 0) {
meta.attachments = attachments meta.attachments = attachments
@ -339,8 +339,8 @@ ${markdownContent}`;
for (const attachmentMeta of noteMeta.attachments || []) { for (const attachmentMeta of noteMeta.attachments || []) {
// FIXME // FIXME
const noteAttachment = note.getNoteAttachmentByName(attachmentMeta.name); const attachment = note.getAttachmentByName(attachmentMeta.name);
const content = noteAttachment.getContent(); const content = attachment.getContent();
archive.append(content, { archive.append(content, {
name: filePathPrefix + attachmentMeta.dataFileName, name: filePathPrefix + attachmentMeta.dataFileName,

View File

@ -14,7 +14,7 @@ const treeService = require("../tree");
const yauzl = require("yauzl"); const yauzl = require("yauzl");
const htmlSanitizer = require('../html_sanitizer'); const htmlSanitizer = require('../html_sanitizer');
const becca = require("../../becca/becca"); const becca = require("../../becca/becca");
const BNoteAttachment = require("../../becca/entities/bnote_attachment"); const BAttachment = require("../../becca/entities/battachment");
/** /**
* @param {TaskContext} taskContext * @param {TaskContext} taskContext
@ -379,14 +379,14 @@ async function importZip(taskContext, fileBuffer, importRootNote) {
const noteId = getNoteId(noteMeta, filePath); const noteId = getNoteId(noteMeta, filePath);
if (attachmentMeta) { if (attachmentMeta) {
const noteAttachment = new BNoteAttachment({ const attachment = new BAttachment({
parentId: noteId, parentId: noteId,
title: attachmentMeta.title, title: attachmentMeta.title,
role: attachmentMeta.role, role: attachmentMeta.role,
mime: attachmentMeta.mime mime: attachmentMeta.mime
}); });
noteAttachment.setContent(content); attachment.setContent(content);
return; return;
} }

View File

@ -114,7 +114,7 @@ function eraseEntity(entityChange, instanceId) {
"branches", "branches",
"attributes", "attributes",
"note_revisions", "note_revisions",
"note_attachments", "attachments",
"blobs", "blobs",
]; ];

View File

@ -152,7 +152,7 @@ const ORDERING = {
"blobs": 0, "blobs": 0,
"note_reordering": 2, "note_reordering": 2,
"note_revisions": 2, "note_revisions": 2,
"note_attachments": 3, "attachments": 3,
"notes": 1, "notes": 1,
"options": 0 "options": 0
}; };