diff --git a/db/migrations/0219__attachments.sql b/db/migrations/0219__attachments.sql index ac1e70a6c..5e7c93e96 100644 --- a/db/migrations/0219__attachments.sql +++ b/db/migrations/0219__attachments.sql @@ -1,7 +1,7 @@ CREATE TABLE IF NOT EXISTS "attachments" ( attachmentId TEXT not null primary key, - parentId TEXT not null, + ownerId TEXT not null, role TEXT not null, mime TEXT not null, title TEXT not null, @@ -14,8 +14,8 @@ CREATE TABLE IF NOT EXISTS "attachments" isDeleted INT not null, deleteId TEXT DEFAULT NULL); -CREATE INDEX IDX_attachments_parentId_role - on attachments (parentId, role); +CREATE INDEX IDX_attachments_ownerId_role + on attachments (ownerId, role); CREATE INDEX IDX_attachments_utcDateScheduledForErasureSince on attachments (utcDateScheduledForErasureSince); diff --git a/db/schema.sql b/db/schema.sql index a11696f80..11123c9c6 100644 --- a/db/schema.sql +++ b/db/schema.sql @@ -114,7 +114,7 @@ CREATE TABLE IF NOT EXISTS "blobs" ( CREATE TABLE IF NOT EXISTS "attachments" ( attachmentId TEXT not null primary key, - parentId TEXT not null, + ownerId TEXT not null, role TEXT not null, mime TEXT not null, title TEXT not null, @@ -126,5 +126,5 @@ CREATE TABLE IF NOT EXISTS "attachments" utcDateScheduledForErasureSince TEXT DEFAULT NULL, isDeleted INT not null, deleteId TEXT DEFAULT NULL); -CREATE INDEX IDX_attachments_parentId_role - on attachments (parentId, role); +CREATE INDEX IDX_attachments_ownerId_role + on attachments (ownerId, role); diff --git a/src/becca/entities/battachment.js b/src/becca/entities/battachment.js index fcf3169d8..6772710c2 100644 --- a/src/becca/entities/battachment.js +++ b/src/becca/entities/battachment.js @@ -20,14 +20,14 @@ const attachmentRoleToNoteTypeMapping = { class BAttachment extends AbstractBeccaEntity { static get entityName() { return "attachments"; } static get primaryKeyName() { return "attachmentId"; } - static get hashedProperties() { return ["attachmentId", "parentId", "role", "mime", "title", "blobId", + static get hashedProperties() { return ["attachmentId", "ownerId", "role", "mime", "title", "blobId", "utcDateScheduledForErasureSince", "utcDateModified"]; } constructor(row) { super(); - if (!row.parentId?.trim()) { - throw new Error("'parentId' must be given to initialize a Attachment entity"); + if (!row.ownerId?.trim()) { + throw new Error("'ownerId' must be given to initialize a Attachment entity"); } else if (!row.role?.trim()) { throw new Error("'role' must be given to initialize a Attachment entity"); } else if (!row.mime?.trim()) { @@ -39,7 +39,7 @@ class BAttachment extends AbstractBeccaEntity { /** @type {string} */ this.attachmentId = row.attachmentId; /** @type {string} either noteId or revisionId to which this attachment belongs */ - this.parentId = row.parentId; + this.ownerId = row.ownerId; /** @type {string} */ this.role = row.role; /** @type {string} */ @@ -68,7 +68,7 @@ class BAttachment extends AbstractBeccaEntity { /** @returns {BAttachment} */ copy() { return new BAttachment({ - parentId: this.parentId, + ownerId: this.ownerId, role: this.role, mime: this.mime, title: this.title, @@ -79,7 +79,7 @@ class BAttachment extends AbstractBeccaEntity { /** @returns {BNote} */ getNote() { - return this.becca.notes[this.parentId]; + return this.becca.notes[this.ownerId]; } /** @returns {boolean} true if the note has string content (not binary) */ @@ -148,7 +148,7 @@ class BAttachment extends AbstractBeccaEntity { const noteService = require('../../services/notes'); const { note, branch } = noteService.createNewNote({ - parentNoteId: this.parentId, + parentNoteId: this.ownerId, title: this.title, type: attachmentRoleToNoteTypeMapping[this.role], mime: this.mime, @@ -189,7 +189,7 @@ class BAttachment extends AbstractBeccaEntity { if (this.position === undefined || this.position === null) { this.position = 10 + sql.getValue(`SELECT COALESCE(MAX(position), 0) FROM attachments - WHERE parentId = ?`, [this.noteId]); + WHERE ownerId = ?`, [this.noteId]); } this.dateModified = dateUtils.localNowDateTime(); @@ -199,7 +199,7 @@ class BAttachment extends AbstractBeccaEntity { getPojo() { return { attachmentId: this.attachmentId, - parentId: this.parentId, + ownerId: this.ownerId, role: this.role, mime: this.mime, title: this.title, diff --git a/src/becca/entities/bnote.js b/src/becca/entities/bnote.js index fdea88e26..4a36d2e4f 100644 --- a/src/becca/entities/bnote.js +++ b/src/becca/entities/bnote.js @@ -1101,9 +1101,9 @@ class BNote extends AbstractBeccaEntity { ? `SELECT attachments.*, LENGTH(blobs.content) AS contentLength FROM attachments JOIN blobs USING (blobId) - WHERE parentId = ? AND isDeleted = 0 + WHERE ownerId = ? AND isDeleted = 0 ORDER BY position` - : `SELECT * FROM attachments WHERE parentId = ? AND isDeleted = 0 ORDER BY position`; + : `SELECT * FROM attachments WHERE ownerId = ? AND isDeleted = 0 ORDER BY position`; return sql.getRows(query, [this.noteId]) .map(row => new BAttachment(row)); @@ -1117,8 +1117,8 @@ class BNote extends AbstractBeccaEntity { ? `SELECT attachments.*, LENGTH(blobs.content) AS contentLength FROM attachments JOIN blobs USING (blobId) - WHERE parentId = ? AND attachmentId = ? AND isDeleted = 0` - : `SELECT * FROM attachments WHERE parentId = ? AND attachmentId = ? AND isDeleted = 0`; + WHERE ownerId = ? AND attachmentId = ? AND isDeleted = 0` + : `SELECT * FROM attachments WHERE ownerId = ? AND attachmentId = ? AND isDeleted = 0`; return sql.getRows(query, [this.noteId, attachmentId]) .map(row => new BAttachment(row))[0]; @@ -1129,7 +1129,7 @@ class BNote extends AbstractBeccaEntity { return sql.getRows(` SELECT attachments.* FROM attachments - WHERE parentId = ? + WHERE ownerId = ? AND role = ? AND isDeleted = 0 ORDER BY position`, [this.noteId, role]) @@ -1590,7 +1590,7 @@ class BNote extends AbstractBeccaEntity { } const revisionAttachment = noteAttachment.copy(); - revisionAttachment.parentId = revision.revisionId; + revisionAttachment.ownerId = revision.revisionId; revisionAttachment.setContent(noteAttachment.getContent(), {forceSave: true}); // content is rewritten to point to the revision attachments @@ -1618,7 +1618,7 @@ class BNote extends AbstractBeccaEntity { attachment = this.becca.getAttachmentOrThrow(attachmentId); } else { attachment = new BAttachment({ - parentId: this.noteId, + ownerId: this.noteId, title, role, mime, diff --git a/src/becca/entities/brevision.js b/src/becca/entities/brevision.js index 45cece586..2e0f1f33e 100644 --- a/src/becca/entities/brevision.js +++ b/src/becca/entities/brevision.js @@ -100,7 +100,7 @@ class BRevision extends AbstractBeccaEntity { return sql.getRows(` SELECT attachments.* FROM attachments - WHERE parentId = ? + WHERE ownerId = ? AND isDeleted = 0`, [this.revisionId]) .map(row => new BAttachment(row)); } diff --git a/src/etapi/attachments.js b/src/etapi/attachments.js index 722463897..64fbb9964 100644 --- a/src/etapi/attachments.js +++ b/src/etapi/attachments.js @@ -6,7 +6,7 @@ const utils = require("../services/utils"); function register(router) { const ALLOWED_PROPERTIES_FOR_CREATE_ATTACHMENT = { - 'parentId': [v.notNull, v.isNoteId], + 'ownerId': [v.notNull, v.isNoteId], 'role': [v.notNull, v.isString], 'mime': [v.notNull, v.isString], 'title': [v.notNull, v.isString], @@ -20,7 +20,7 @@ function register(router) { eu.validateAndPatch(params, req.body, ALLOWED_PROPERTIES_FOR_CREATE_ATTACHMENT); try { - const note = becca.getNoteOrThrow(params.parentId); + const note = becca.getNoteOrThrow(params.ownerId); const attachment = note.saveAttachment(params); res.status(201).json(mappers.mapAttachmentToPojo(attachment)); diff --git a/src/etapi/mappers.js b/src/etapi/mappers.js index 8ec52aa26..d94cf2e69 100644 --- a/src/etapi/mappers.js +++ b/src/etapi/mappers.js @@ -50,7 +50,7 @@ function mapAttributeToPojo(attr) { function mapAttachmentToPojo(attachment) { return { attachmentId: attachment.attachmentId, - parentId: attachment.parentId, + ownerId: attachment.ownerId, role: attachment.role, mime: attachment.mime, title: attachment.title, diff --git a/src/public/app/entities/fattachment.js b/src/public/app/entities/fattachment.js index 3ef6433d4..ce8945076 100644 --- a/src/public/app/entities/fattachment.js +++ b/src/public/app/entities/fattachment.js @@ -9,7 +9,7 @@ class FAttachment { /** @type {string} */ this.attachmentId = row.attachmentId; /** @type {string} */ - this.parentId = row.parentId; + this.ownerId = row.ownerId; /** @type {string} */ this.role = row.role; /** @type {string} */ @@ -31,7 +31,7 @@ class FAttachment { /** @returns {FNote} */ getNote() { - return this.froca.notes[this.parentId]; + return this.froca.notes[this.ownerId]; } /** diff --git a/src/public/app/services/froca_updater.js b/src/public/app/services/froca_updater.js index 1feba5206..14f1b86ad 100644 --- a/src/public/app/services/froca_updater.js +++ b/src/public/app/services/froca_updater.js @@ -280,7 +280,7 @@ function processAttachment(loadResults, ec) { if (attachment) { attachment.update(ec.entity); } else { - const note = froca.notes[ec.entity.parentId]; + const note = froca.notes[ec.entity.ownerId]; if (note?.attachments) { note.attachments.push(new FAttachment(froca, ec.entity)); diff --git a/src/public/app/widgets/attachment_detail.js b/src/public/app/widgets/attachment_detail.js index 460653289..d9de70301 100644 --- a/src/public/app/widgets/attachment_detail.js +++ b/src/public/app/widgets/attachment_detail.js @@ -126,7 +126,7 @@ export default class AttachmentDetailWidget extends BasicWidget { if (!this.isFullDetail) { this.$wrapper.find('.attachment-title').append( - await linkService.createLink(this.attachment.parentId, { + await linkService.createLink(this.attachment.ownerId, { title: this.attachment.title, viewScope: { viewMode: 'attachments', @@ -174,7 +174,7 @@ export default class AttachmentDetailWidget extends BasicWidget { if (this.attachment.role === 'image') { imageService.copyImageReferenceToClipboard(this.$wrapper.find('.attachment-content-wrapper')); } else if (this.attachment.role === 'file') { - const $link = await linkService.createLink(this.attachment.parentId, { + const $link = await linkService.createLink(this.attachment.ownerId, { referenceLink: true, viewScope: { viewMode: 'attachments', diff --git a/src/public/app/widgets/buttons/note_actions.js b/src/public/app/widgets/buttons/note_actions.js index 3f6bbf843..a007f6b79 100644 --- a/src/public/app/widgets/buttons/note_actions.js +++ b/src/public/app/widgets/buttons/note_actions.js @@ -121,7 +121,7 @@ export default class NoteActionsWidget extends NoteContextAwareWidget { toastService.showMessage(`Note '${newAttachment.title}' has been converted to attachment.`); await ws.waitForMaxKnownEntityChangeId(); - await appContext.tabManager.getActiveContext().setNote(newAttachment.parentId, { + await appContext.tabManager.getActiveContext().setNote(newAttachment.ownerId, { viewScope: { viewMode: 'attachments', attachmentId: newAttachment.attachmentId diff --git a/src/public/app/widgets/type_widgets/abstract_text_type_widget.js b/src/public/app/widgets/type_widgets/abstract_text_type_widget.js index 9d4154b2c..93cb1b27d 100644 --- a/src/public/app/widgets/type_widgets/abstract_text_type_widget.js +++ b/src/public/app/widgets/type_widgets/abstract_text_type_widget.js @@ -60,7 +60,7 @@ export default class AbstractTextTypeWidget extends TypeWidget { const attachment = await froca.getAttachment(attachmentId); return { - noteId: attachment.parentId, + noteId: attachment.ownerId, viewScope: { viewMode: 'attachments', attachmentId: attachmentId diff --git a/src/routes/api/revisions.js b/src/routes/api/revisions.js index 0dab10e03..015d4ae93 100644 --- a/src/routes/api/revisions.js +++ b/src/routes/api/revisions.js @@ -112,7 +112,7 @@ function restoreRevision(req) { for (const revisionAttachment of revision.getAttachments()) { const noteAttachment = revisionAttachment.copy(); - noteAttachment.parentId = note.noteId; + noteAttachment.ownerId = note.noteId; noteAttachment.setContent(revisionAttachment.getContent(), { forceSave: true }); // content is rewritten to point to the restored revision attachments diff --git a/src/routes/api/stats.js b/src/routes/api/stats.js index 23c0c31af..05d05d25c 100644 --- a/src/routes/api/stats.js +++ b/src/routes/api/stats.js @@ -8,7 +8,7 @@ function getNoteSize(req) { SELECT blobs.blobId, LENGTH(content) FROM blobs LEFT JOIN notes ON notes.blobId = blobs.blobId AND notes.noteId = ? AND notes.isDeleted = 0 - LEFT JOIN attachments ON attachments.blobId = blobs.blobId AND attachments.parentId = ? AND attachments.isDeleted = 0 + LEFT JOIN attachments ON attachments.blobId = blobs.blobId AND attachments.ownerId = ? AND attachments.isDeleted = 0 LEFT JOIN revisions ON revisions.blobId = blobs.blobId AND revisions.noteId = ? WHERE notes.noteId IS NOT NULL OR attachments.attachmentId IS NOT NULL @@ -32,7 +32,7 @@ function getSubtreeSize(req) { SELECT blobs.blobId, LENGTH(content) FROM param_list JOIN notes ON notes.noteId = param_list.paramId AND notes.isDeleted = 0 - LEFT JOIN attachments ON attachments.parentId = param_list.paramId AND attachments.isDeleted = 0 + LEFT JOIN attachments ON attachments.ownerId = param_list.paramId AND attachments.isDeleted = 0 LEFT JOIN revisions ON revisions.noteId = param_list.paramId JOIN blobs ON blobs.blobId = notes.blobId OR blobs.blobId = attachments.blobId OR blobs.blobId = revisions.blobId`); diff --git a/src/services/consistency_checks.js b/src/services/consistency_checks.js index 49cec4052..19903c951 100644 --- a/src/services/consistency_checks.js +++ b/src/services/consistency_checks.js @@ -215,24 +215,24 @@ class ConsistencyChecks { }); this.findAndFixIssues(` - SELECT attachmentId, attachments.parentId AS noteId + SELECT attachmentId, attachments.ownerId AS noteId FROM attachments - WHERE attachments.parentId NOT IN ( + WHERE attachments.ownerId NOT IN ( SELECT noteId FROM notes UNION ALL SELECT revisionId FROM revisions ) AND attachments.isDeleted = 0`, - ({attachmentId, parentId}) => { + ({attachmentId, ownerId}) => { if (this.autoFix) { const attachment = becca.getAttachment(attachmentId); attachment.markAsDeleted(); this.reloadNeeded = false; - logFix(`Attachment '${attachmentId}' has been deleted since it references missing note/revision '${parentId}'`); + logFix(`Attachment '${attachmentId}' has been deleted since it references missing note/revision '${ownerId}'`); } else { - logError(`Attachment '${attachmentId}' references missing note/revision '${parentId}'`); + logError(`Attachment '${attachmentId}' references missing note/revision '${ownerId}'`); } }); } @@ -345,9 +345,9 @@ class ConsistencyChecks { this.findAndFixIssues(` SELECT attachmentId, - attachments.parentId AS noteId + attachments.ownerId AS noteId FROM attachments - JOIN notes ON notes.noteId = attachments.parentId + JOIN notes ON notes.noteId = attachments.ownerId WHERE notes.isDeleted = 1 AND attachments.isDeleted = 0`, ({attachmentId, noteId}) => { diff --git a/src/services/import/zip.js b/src/services/import/zip.js index 3fad97c4c..d18c9bc24 100644 --- a/src/services/import/zip.js +++ b/src/services/import/zip.js @@ -452,7 +452,7 @@ async function importZip(taskContext, fileBuffer, importRootNote) { if (attachmentMeta) { const attachment = new BAttachment({ attachmentId: getNewAttachmentId(attachmentMeta.attachmentId), - parentId: noteId, + ownerId: noteId, title: attachmentMeta.title, role: attachmentMeta.role, mime: attachmentMeta.mime, diff --git a/src/services/notes.js b/src/services/notes.js index 85f6c00bf..46a633df7 100644 --- a/src/services/notes.js +++ b/src/services/notes.js @@ -378,21 +378,21 @@ function checkImageAttachments(note, content) { localAttachment.save(); } - log.info(`Found equivalent attachment '${localAttachment.attachmentId}' of note '${note.noteId}' for the linked foreign attachment '${unknownAttachment.attachmentId}' of note '${unknownAttachment.parentId}'`); + log.info(`Found equivalent attachment '${localAttachment.attachmentId}' of note '${note.noteId}' for the linked foreign attachment '${unknownAttachment.attachmentId}' of note '${unknownAttachment.ownerId}'`); } else { localAttachment = unknownAttachment.copy(); - localAttachment.parentId = note.noteId; + localAttachment.ownerId = note.noteId; localAttachment.setContent(unknownAttachment.getContent(), {forceSave: true}); ws.sendMessageToAllClients({ type: 'toast', message: `Attachment '${localAttachment.title}' has been copied to note '${note.title}'.`}); - log.info(`Copied attachment '${unknownAttachment.attachmentId}' of note '${unknownAttachment.parentId}' to new '${localAttachment.attachmentId}' of note '${note.noteId}'`); + log.info(`Copied attachment '${unknownAttachment.attachmentId}' of note '${unknownAttachment.ownerId}' to new '${localAttachment.attachmentId}' of note '${note.noteId}'`); } // replace image links content = content.replace(`api/attachments/${unknownAttachment.attachmentId}/image`, `api/attachments/${localAttachment.attachmentId}/image`); // replace reference links content = content.replace(new RegExp(`href="[^"]+attachmentId=${unknownAttachment.attachmentId}[^"]*"`, "g"), - `href="#root/${localAttachment.parentId}?viewMode=attachments&attachmentId=${localAttachment.attachmentId}"`); + `href="#root/${localAttachment.ownerId}?viewMode=attachments&attachmentId=${localAttachment.attachmentId}"`); } return { diff --git a/src/share/routes.js b/src/share/routes.js index d807ce35e..c5836f177 100644 --- a/src/share/routes.js +++ b/src/share/routes.js @@ -48,7 +48,7 @@ function checkAttachmentAccess(attachmentId, req, res) { return false; } - const note = checkNoteAccess(attachment.parentId, req, res); + const note = checkNoteAccess(attachment.ownerId, req, res); // truthy note means the user has access, and we can return the attachment return note ? attachment : false; diff --git a/src/share/shaca/entities/sattachment.js b/src/share/shaca/entities/sattachment.js index d228bb79c..998c5ac95 100644 --- a/src/share/shaca/entities/sattachment.js +++ b/src/share/shaca/entities/sattachment.js @@ -5,13 +5,13 @@ const utils = require('../../../services/utils'); const AbstractShacaEntity = require('./abstract_shaca_entity'); class SAttachment extends AbstractShacaEntity { - constructor([attachmentId, parentId, role, mime, title, blobId, utcDateModified]) { + constructor([attachmentId, ownerId, role, mime, title, blobId, utcDateModified]) { super(); /** @param {string} */ this.attachmentId = attachmentId; /** @param {string} */ - this.parentId = parentId; + this.ownerId = ownerId; /** @param {string} */ this.title = title; /** @param {string} */ @@ -24,12 +24,12 @@ class SAttachment extends AbstractShacaEntity { this.utcDateModified = utcDateModified; // used for caching of images this.shaca.attachments[this.attachmentId] = this; - this.shaca.notes[this.parentId].attachments.push(this); + this.shaca.notes[this.ownerId].attachments.push(this); } /** @returns {SNote} */ get note() { - return this.shaca.notes[this.parentId]; + return this.shaca.notes[this.ownerId]; } getContent(silentNotFoundError = false) { diff --git a/src/share/shaca/shaca_loader.js b/src/share/shaca/shaca_loader.js index 09bc7a92b..6f3545bda 100644 --- a/src/share/shaca/shaca_loader.js +++ b/src/share/shaca/shaca_loader.js @@ -67,10 +67,10 @@ function load() { } const rawAttachmentRows = sql.getRawRows(` - SELECT attachmentId, parentId, role, mime, title, blobId, utcDateModified + SELECT attachmentId, ownerId, role, mime, title, blobId, utcDateModified FROM attachments WHERE isDeleted = 0 - AND parentId IN (${noteIdStr})`); + AND ownerId IN (${noteIdStr})`); rawAttachmentRows.sort((a, b) => a.position < b.position ? -1 : 1); diff --git a/test-etapi/create-entities.http b/test-etapi/create-entities.http index e3d9537fe..01f626872 100644 --- a/test-etapi/create-entities.http +++ b/test-etapi/create-entities.http @@ -127,7 +127,7 @@ Content-Type: application/json Authorization: {{authToken}} { - "parentId": "{{createdNoteId}}", + "ownerId": "{{createdNoteId}}", "role": "file", "mime": "plain/text", "title": "my attachment", diff --git a/test-etapi/delete-attachment.http b/test-etapi/delete-attachment.http index 3ed0dc4ec..d12e8de43 100644 --- a/test-etapi/delete-attachment.http +++ b/test-etapi/delete-attachment.http @@ -18,7 +18,7 @@ Authorization: {{authToken}} Content-Type: application/json { - "parentId": "{{createdNoteId}}", + "ownerId": "{{createdNoteId}}", "role": "file", "mime": "text/plain", "title": "my attachment", diff --git a/test-etapi/patch-attachment.http b/test-etapi/patch-attachment.http index 8fd0fea1b..44ffe696f 100644 --- a/test-etapi/patch-attachment.http +++ b/test-etapi/patch-attachment.http @@ -18,7 +18,7 @@ Authorization: {{authToken}} Content-Type: application/json { - "parentId": "{{createdNoteId}}", + "ownerId": "{{createdNoteId}}", "role": "file", "mime": "text/plain", "title": "my attachment", @@ -55,7 +55,7 @@ Authorization: {{authToken}} Content-Type: application/json { - "parentId": "root" + "ownerId": "root" } > {% @@ -76,4 +76,4 @@ Content-Type: application/json > {% client.assert(response.status === 400); client.assert(response.body.code == "PROPERTY_VALIDATION_ERROR"); -%} \ No newline at end of file +%} diff --git a/test-etapi/put-attachment-content-binary.http b/test-etapi/put-attachment-content-binary.http index 232667e56..6e6d6dad3 100644 --- a/test-etapi/put-attachment-content-binary.http +++ b/test-etapi/put-attachment-content-binary.http @@ -18,7 +18,7 @@ Authorization: {{authToken}} Content-Type: application/json { - "parentId": "{{createdNoteId}}", + "ownerId": "{{createdNoteId}}", "role": "file", "mime": "text/plain", "title": "my attachment", @@ -36,4 +36,4 @@ Content-Transfer-Encoding: binary < ../images/icon-color.png -> {% client.assert(response.status === 204); %} \ No newline at end of file +> {% client.assert(response.status === 204); %} diff --git a/test-etapi/put-attachment-content.http b/test-etapi/put-attachment-content.http index 2d0d80949..57e96a4b9 100644 --- a/test-etapi/put-attachment-content.http +++ b/test-etapi/put-attachment-content.http @@ -18,7 +18,7 @@ Authorization: {{authToken}} Content-Type: application/json { - "parentId": "{{createdNoteId}}", + "ownerId": "{{createdNoteId}}", "role": "file", "mime": "text/plain", "title": "my attachment",