From c1d0a1e07bf1e84e9352470fb4931cf93901779f Mon Sep 17 00:00:00 2001 From: zadam Date: Fri, 3 Jan 2020 10:48:36 +0100 Subject: [PATCH] undelete note WIP --- db/migrations/0156__add_deleteId.sql | 81 +++++++++++++ db/schema.sql | 113 +++++++++--------- src/entities/attribute.js | 1 + src/entities/branch.js | 3 +- src/entities/note.js | 3 +- .../javascripts/dialogs/recent_changes.js | 22 ++-- src/public/javascripts/services/link.js | 6 + src/public/javascripts/services/tree.js | 2 + src/routes/api/branches.js | 3 +- src/routes/api/notes.js | 5 +- src/routes/api/recent_changes.js | 33 ++++- src/services/app_info.js | 4 +- src/services/notes.js | 16 ++- 13 files changed, 217 insertions(+), 75 deletions(-) create mode 100644 db/migrations/0156__add_deleteId.sql diff --git a/db/migrations/0156__add_deleteId.sql b/db/migrations/0156__add_deleteId.sql new file mode 100644 index 000000000..dc7ea2182 --- /dev/null +++ b/db/migrations/0156__add_deleteId.sql @@ -0,0 +1,81 @@ +CREATE TABLE IF NOT EXISTS "notes_mig" ( + `noteId` TEXT NOT NULL, + `title` TEXT NOT NULL DEFAULT "note", + `contentLength` INT NOT NULL, + `isProtected` INT NOT NULL DEFAULT 0, + `type` TEXT NOT NULL DEFAULT 'text', + `mime` TEXT NOT NULL DEFAULT 'text/html', + `hash` TEXT DEFAULT "" NOT NULL, + `isDeleted` INT NOT NULL DEFAULT 0, + `deleteId` TEXT DEFAULT NULL, + `isErased` INT NOT NULL DEFAULT 0, + `dateCreated` TEXT NOT NULL, + `dateModified` TEXT NOT NULL, + `utcDateCreated` TEXT NOT NULL, + `utcDateModified` TEXT NOT NULL, + PRIMARY KEY(`noteId`)); + +INSERT INTO notes_mig (noteId, title, contentLength, isProtected, type, mime, hash, isDeleted, isErased, dateCreated, dateModified, utcDateCreated, utcDateModified) +SELECT noteId, title, -1, isProtected, type, mime, hash, isDeleted, isErased, dateCreated, dateModified, utcDateCreated, utcDateModified FROM notes; + +DROP TABLE notes; +ALTER TABLE notes_mig RENAME TO notes; + +CREATE INDEX `IDX_notes_isDeleted` ON `notes` (`isDeleted`); +CREATE INDEX `IDX_notes_title` ON `notes` (`title`); +CREATE INDEX `IDX_notes_type` ON `notes` (`type`); +CREATE INDEX `IDX_notes_dateCreated` ON `notes` (`dateCreated`); +CREATE INDEX `IDX_notes_dateModified` ON `notes` (`dateModified`); +CREATE INDEX `IDX_notes_utcDateModified` ON `notes` (`utcDateModified`); +CREATE INDEX `IDX_notes_utcDateCreated` ON `notes` (`utcDateCreated`); + +CREATE TABLE IF NOT EXISTS "branches_mig" ( + `branchId` TEXT NOT NULL, + `noteId` TEXT NOT NULL, + `parentNoteId` TEXT NOT NULL, + `notePosition` INTEGER NOT NULL, + `prefix` TEXT, + `isExpanded` INTEGER NOT NULL DEFAULT 0, + `isDeleted` INTEGER NOT NULL DEFAULT 0, + `deleteId` TEXT DEFAULT NULL, + `utcDateModified` TEXT NOT NULL, + utcDateCreated TEXT NOT NULL, + hash TEXT DEFAULT "" NOT NULL, + PRIMARY KEY(`branchId`)); + +INSERT INTO branches_mig (branchId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, utcDateModified, utcDateCreated, hash) + SELECT branchId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, utcDateModified, utcDateCreated, hash FROM branches; + +DROP TABLE branches; +ALTER TABLE branches_mig RENAME TO branches; + +CREATE INDEX `IDX_branches_noteId_parentNoteId` ON `branches` (`noteId`,`parentNoteId`); +CREATE INDEX IDX_branches_parentNoteId ON branches (parentNoteId); + +CREATE TABLE IF NOT EXISTS "attributes_mig" +( + attributeId TEXT not null primary key, + noteId TEXT not null, + type TEXT not null, + name TEXT not null, + value TEXT default '' not null, + position INT default 0 not null, + utcDateCreated TEXT not null, + utcDateModified TEXT not null, + isDeleted INT not null, + `deleteId` TEXT DEFAULT NULL, + hash TEXT default "" not null, + isInheritable int DEFAULT 0 NULL); + +INSERT INTO attributes_mig (attributeId, noteId, type, name, value, position, utcDateCreated, utcDateModified, isDeleted, hash, isInheritable) +SELECT attributeId, noteId, type, name, value, position, utcDateCreated, utcDateModified, isDeleted, hash, isInheritable FROM attributes; + +DROP TABLE attributes; +ALTER TABLE attributes_mig RENAME TO attributes; + +CREATE INDEX IDX_attributes_name_value + on attributes (name, value); +CREATE INDEX IDX_attributes_noteId_index + on attributes (noteId); +CREATE INDEX IDX_attributes_value_index + on attributes (value); diff --git a/db/schema.sql b/db/schema.sql index 66af0788c..8446f6c0d 100644 --- a/db/schema.sql +++ b/db/schema.sql @@ -27,19 +27,6 @@ CREATE TABLE IF NOT EXISTS "options" utcDateCreated TEXT not null, utcDateModified TEXT NOT NULL ); -CREATE TABLE IF NOT EXISTS "attributes" -( - attributeId TEXT not null primary key, - noteId TEXT not null, - type TEXT not null, - name TEXT not null, - value TEXT default '' not null, - position INT default 0 not null, - utcDateCreated TEXT not null, - utcDateModified TEXT not null, - isDeleted INT not null, - hash TEXT default "" not null, - isInheritable int DEFAULT 0 NULL); CREATE UNIQUE INDEX `IDX_sync_entityName_entityId` ON `sync` ( `entityName`, `entityId` @@ -47,12 +34,6 @@ CREATE UNIQUE INDEX `IDX_sync_entityName_entityId` ON `sync` ( CREATE INDEX `IDX_sync_utcSyncDate` ON `sync` ( `utcSyncDate` ); -CREATE INDEX IDX_attributes_name_value - on attributes (name, value); -CREATE INDEX IDX_attributes_noteId_index - on attributes (noteId); -CREATE INDEX IDX_attributes_value_index - on attributes (value); CREATE TABLE IF NOT EXISTS "note_contents" ( `noteId` TEXT NOT NULL, `content` TEXT NULL DEFAULT NULL, @@ -68,46 +49,10 @@ CREATE TABLE recent_notes utcDateCreated TEXT not null, isDeleted INT ); -CREATE TABLE IF NOT EXISTS "branches" ( - `branchId` TEXT NOT NULL, - `noteId` TEXT NOT NULL, - `parentNoteId` TEXT NOT NULL, - `notePosition` INTEGER NOT NULL, - `prefix` TEXT, - `isExpanded` INTEGER NOT NULL DEFAULT 0, - `isDeleted` INTEGER NOT NULL DEFAULT 0, - `utcDateModified` TEXT NOT NULL, - utcDateCreated TEXT NOT NULL, - hash TEXT DEFAULT "" NOT NULL, - PRIMARY KEY(`branchId`)); -CREATE INDEX `IDX_branches_noteId_parentNoteId` ON `branches` (`noteId`,`parentNoteId`); -CREATE INDEX IDX_branches_parentNoteId ON branches (parentNoteId); CREATE TABLE IF NOT EXISTS "note_revision_contents" (`noteRevisionId` TEXT NOT NULL PRIMARY KEY, `content` TEXT, hash TEXT DEFAULT '' NOT NULL, `utcDateModified` TEXT NOT NULL); -CREATE TABLE IF NOT EXISTS "notes" ( - `noteId` TEXT NOT NULL, - `title` TEXT NOT NULL DEFAULT "note", - `contentLength` INT NOT NULL, - `isProtected` INT NOT NULL DEFAULT 0, - `type` TEXT NOT NULL DEFAULT 'text', - `mime` TEXT NOT NULL DEFAULT 'text/html', - `hash` TEXT DEFAULT "" NOT NULL, - `isDeleted` INT NOT NULL DEFAULT 0, - `isErased` INT NOT NULL DEFAULT 0, - `dateCreated` TEXT NOT NULL, - `dateModified` TEXT NOT NULL, - `utcDateCreated` TEXT NOT NULL, - `utcDateModified` TEXT NOT NULL, - PRIMARY KEY(`noteId`)); -CREATE INDEX `IDX_notes_isDeleted` ON `notes` (`isDeleted`); -CREATE INDEX `IDX_notes_title` ON `notes` (`title`); -CREATE INDEX `IDX_notes_type` ON `notes` (`type`); -CREATE INDEX `IDX_notes_dateCreated` ON `notes` (`dateCreated`); -CREATE INDEX `IDX_notes_dateModified` ON `notes` (`dateModified`); -CREATE INDEX `IDX_notes_utcDateModified` ON `notes` (`utcDateModified`); -CREATE INDEX `IDX_notes_utcDateCreated` ON `notes` (`utcDateCreated`); CREATE TABLE IF NOT EXISTS "note_revisions" (`noteRevisionId` TEXT NOT NULL PRIMARY KEY, `noteId` TEXT NOT NULL, `title` TEXT, @@ -127,3 +72,61 @@ CREATE INDEX `IDX_note_revisions_utcDateCreated` ON `note_revisions` (`utcDateCr CREATE INDEX `IDX_note_revisions_utcDateLastEdited` ON `note_revisions` (`utcDateLastEdited`); CREATE INDEX `IDX_note_revisions_dateCreated` ON `note_revisions` (`dateCreated`); CREATE INDEX `IDX_note_revisions_dateLastEdited` ON `note_revisions` (`dateLastEdited`); +CREATE TABLE IF NOT EXISTS "notes" ( + `noteId` TEXT NOT NULL, + `title` TEXT NOT NULL DEFAULT "note", + `contentLength` INT NOT NULL, + `isProtected` INT NOT NULL DEFAULT 0, + `type` TEXT NOT NULL DEFAULT 'text', + `mime` TEXT NOT NULL DEFAULT 'text/html', + `hash` TEXT DEFAULT "" NOT NULL, + `isDeleted` INT NOT NULL DEFAULT 0, + `deleteId` TEXT DEFAULT NULL, + `isErased` INT NOT NULL DEFAULT 0, + `dateCreated` TEXT NOT NULL, + `dateModified` TEXT NOT NULL, + `utcDateCreated` TEXT NOT NULL, + `utcDateModified` TEXT NOT NULL, + PRIMARY KEY(`noteId`)); +CREATE INDEX `IDX_notes_isDeleted` ON `notes` (`isDeleted`); +CREATE INDEX `IDX_notes_title` ON `notes` (`title`); +CREATE INDEX `IDX_notes_type` ON `notes` (`type`); +CREATE INDEX `IDX_notes_dateCreated` ON `notes` (`dateCreated`); +CREATE INDEX `IDX_notes_dateModified` ON `notes` (`dateModified`); +CREATE INDEX `IDX_notes_utcDateModified` ON `notes` (`utcDateModified`); +CREATE INDEX `IDX_notes_utcDateCreated` ON `notes` (`utcDateCreated`); +CREATE TABLE IF NOT EXISTS "branches" ( + `branchId` TEXT NOT NULL, + `noteId` TEXT NOT NULL, + `parentNoteId` TEXT NOT NULL, + `notePosition` INTEGER NOT NULL, + `prefix` TEXT, + `isExpanded` INTEGER NOT NULL DEFAULT 0, + `isDeleted` INTEGER NOT NULL DEFAULT 0, + `deleteId` TEXT DEFAULT NULL, + `utcDateModified` TEXT NOT NULL, + utcDateCreated TEXT NOT NULL, + hash TEXT DEFAULT "" NOT NULL, + PRIMARY KEY(`branchId`)); +CREATE INDEX `IDX_branches_noteId_parentNoteId` ON `branches` (`noteId`,`parentNoteId`); +CREATE INDEX IDX_branches_parentNoteId ON branches (parentNoteId); +CREATE TABLE IF NOT EXISTS "attributes" +( + attributeId TEXT not null primary key, + noteId TEXT not null, + type TEXT not null, + name TEXT not null, + value TEXT default '' not null, + position INT default 0 not null, + utcDateCreated TEXT not null, + utcDateModified TEXT not null, + isDeleted INT not null, + `deleteId` TEXT DEFAULT NULL, + hash TEXT default "" not null, + isInheritable int DEFAULT 0 NULL); +CREATE INDEX IDX_attributes_name_value + on attributes (name, value); +CREATE INDEX IDX_attributes_noteId_index + on attributes (noteId); +CREATE INDEX IDX_attributes_value_index + on attributes (value); diff --git a/src/entities/attribute.js b/src/entities/attribute.js index 59ab5252b..e37c59fba 100644 --- a/src/entities/attribute.js +++ b/src/entities/attribute.js @@ -16,6 +16,7 @@ const sql = require('../services/sql'); * @property {int} position * @property {boolean} isInheritable * @property {boolean} isDeleted + * @property {string|null} deleteId - ID identifying delete transaction * @property {string} utcDateCreated * @property {string} utcDateModified * diff --git a/src/entities/branch.js b/src/entities/branch.js index 4567f0ca1..e860e3ebb 100644 --- a/src/entities/branch.js +++ b/src/entities/branch.js @@ -16,6 +16,7 @@ const sql = require('../services/sql'); * @property {string} prefix * @property {boolean} isExpanded * @property {boolean} isDeleted + * @property {string|null} deleteId - ID identifying delete transaction * @property {string} utcDateModified * @property {string} utcDateCreated * @@ -25,7 +26,7 @@ class Branch extends Entity { static get entityName() { return "branches"; } static get primaryKeyName() { return "branchId"; } // notePosition is not part of hash because it would produce a lot of updates in case of reordering - static get hashedProperties() { return ["branchId", "noteId", "parentNoteId", "isDeleted", "prefix"]; } + static get hashedProperties() { return ["branchId", "noteId", "parentNoteId", "isDeleted", "deleteId", "prefix"]; } constructor(row = {}) { super(row); diff --git a/src/entities/note.js b/src/entities/note.js index 6eb87c5b2..2662d4048 100644 --- a/src/entities/note.js +++ b/src/entities/note.js @@ -24,6 +24,7 @@ const RELATION_DEFINITION = 'relation-definition'; * @property {int} contentLength - length of content * @property {boolean} isProtected - true if note is protected * @property {boolean} isDeleted - true if note is deleted + * @property {string|null} deleteId - ID identifying delete transaction * @property {boolean} isErased - true if note's content is erased after it has been deleted * @property {string} dateCreated - local date time (with offset) * @property {string} dateModified - local date time (with offset) @@ -35,7 +36,7 @@ const RELATION_DEFINITION = 'relation-definition'; class Note extends Entity { static get entityName() { return "notes"; } static get primaryKeyName() { return "noteId"; } - static get hashedProperties() { return ["noteId", "title", "type", "isProtected", "isDeleted"]; } + static get hashedProperties() { return ["noteId", "title", "type", "isProtected", "isDeleted", "deleteId"]; } /** * @param row - object containing database row from "notes" table diff --git a/src/public/javascripts/dialogs/recent_changes.js b/src/public/javascripts/dialogs/recent_changes.js index 703cb145f..2038d243e 100644 --- a/src/public/javascripts/dialogs/recent_changes.js +++ b/src/public/javascripts/dialogs/recent_changes.js @@ -28,31 +28,38 @@ export async function showDialog() { const groupedByDate = groupByDate(result); for (const [dateDay, dayChanges] of groupedByDate) { - const changesListEl = $('