note revisions changes WIP

This commit is contained in:
zadam 2019-11-01 19:21:48 +01:00
parent cf53cbf1dd
commit 4e5e3e4675
11 changed files with 51 additions and 50 deletions

View File

@ -1,7 +1,7 @@
CREATE TABLE IF NOT EXISTS "note_revisions_mig" (`noteRevisionId` TEXT NOT NULL PRIMARY KEY, CREATE TABLE IF NOT EXISTS "note_revisions_mig" (`noteRevisionId` TEXT NOT NULL PRIMARY KEY,
`noteId` TEXT NOT NULL, `noteId` TEXT NOT NULL,
`title` TEXT, `title` TEXT,
`content` TEXT, `contentLength` INT NOT NULL,
`isProtected` INT NOT NULL DEFAULT 0, `isProtected` INT NOT NULL DEFAULT 0,
`utcDateLastEdited` TEXT NOT NULL, `utcDateLastEdited` TEXT NOT NULL,
`utcDateCreated` TEXT NOT NULL, `utcDateCreated` TEXT NOT NULL,
@ -18,10 +18,10 @@ CREATE TABLE IF NOT EXISTS "note_revision_contents" (`noteRevisionId` TEXT NOT N
`utcDateModified` TEXT NOT NULL); `utcDateModified` TEXT NOT NULL);
INSERT INTO note_revision_contents (noteRevisionId, content, hash, utcDateModified) INSERT INTO note_revision_contents (noteRevisionId, content, hash, utcDateModified)
SELECT noteRevisionId, content, hash, utcDateModified FROM note_revisions; SELECT noteRevisionId, content, hash, utcDateModifiedTo FROM note_revisions;
INSERT INTO note_revisions_mig (noteRevisionId, noteId, title, isProtected, utcDateLastEdited, utcDateCreated, utcDateModified, dateLastEdited, dateCreated, type, mime, hash) INSERT INTO note_revisions_mig (noteRevisionId, noteId, title, contentLength, isProtected, utcDateLastEdited, utcDateCreated, utcDateModified, dateLastEdited, dateCreated, type, mime, hash)
SELECT noteRevisionId, noteId, title, isProtected, utcDateModifiedFrom, utcDateModifiedTo, utcDateModifiedTo, dateModifiedFrom, dateModifiedTo, type, mime, hash FROM note_revisions; SELECT noteRevisionId, noteId, title, LENGTH(content), isProtected, utcDateModifiedFrom, utcDateModifiedTo, utcDateModifiedTo, dateModifiedFrom, dateModifiedTo, type, mime, hash FROM note_revisions;
DROP TABLE note_revisions; DROP TABLE note_revisions;
ALTER TABLE note_revisions_mig RENAME TO note_revisions; ALTER TABLE note_revisions_mig RENAME TO note_revisions;

View File

@ -112,7 +112,7 @@ class Note extends Entity {
/** @returns {Promise} */ /** @returns {Promise} */
async setContent(content) { async setContent(content) {
// force updating note itself so that dateChanged is represented correctly even for the content // force updating note itself so that dateModified is represented correctly even for the content
this.forcedChange = true; this.forcedChange = true;
await this.save(); await this.save();

View File

@ -4,6 +4,7 @@ const Entity = require('./entity');
const protectedSessionService = require('../services/protected_session'); const protectedSessionService = require('../services/protected_session');
const repository = require('../services/repository'); const repository = require('../services/repository');
const utils = require('../services/utils'); const utils = require('../services/utils');
const sql = require('../services/sql');
const dateUtils = require('../services/date_utils'); const dateUtils = require('../services/date_utils');
const syncTableService = require('../services/sync_table'); const syncTableService = require('../services/sync_table');
@ -16,17 +17,18 @@ const syncTableService = require('../services/sync_table');
* @param {string} mime * @param {string} mime
* @param {string} title * @param {string} title
* @param {string} isProtected * @param {string} isProtected
* @param {string} dateModifiedFrom * @param {string} dateLastEdited
* @param {string} dateModifiedTo * @param {string} dateCreated
* @param {string} utcDateModifiedFrom * @param {string} utcDateLastEdited
* @param {string} utcDateModifiedTo * @param {string} utcDateCreated
* @param {string} utcDateModified
* *
* @extends Entity * @extends Entity
*/ */
class NoteRevision extends Entity { class NoteRevision extends Entity {
static get entityName() { return "note_revisions"; } static get entityName() { return "note_revisions"; }
static get primaryKeyName() { return "noteRevisionId"; } static get primaryKeyName() { return "noteRevisionId"; }
static get hashedProperties() { return ["noteRevisionId", "noteId", "title", "isProtected", "dateModifiedFrom", "dateModifiedTo", "utcDateModifiedFrom", "utcDateModifiedTo"]; } static get hashedProperties() { return ["noteRevisionId", "noteId", "title", "contentLength", "isProtected", "dateLastEdited", "dateCreated", "utcDateLastEdited", "utcDateCreated", "utcDateModified"]; }
constructor(row) { constructor(row) {
super(row); super(row);
@ -98,8 +100,9 @@ class NoteRevision extends Entity {
/** @returns {Promise} */ /** @returns {Promise} */
async setContent(content) { async setContent(content) {
// force updating note itself so that dateChanged is represented correctly even for the content // force updating note itself so that utcDateModified is represented correctly even for the content
this.forcedChange = true; this.forcedChange = true;
this.contentLength = content.length;
await this.save(); await this.save();
this.content = content; this.content = content;

View File

@ -25,12 +25,12 @@ export async function showNoteRevisionsDialog(noteId, noteRevisionId) {
$content.empty(); $content.empty();
note = noteDetailService.getActiveTabNote(); note = noteDetailService.getActiveTabNote();
revisionItems = await server.get('notes/' + noteId + '/revisions'); revisionItems = await server.get(`notes/${noteId}/revisions`);
for (const item of revisionItems) { for (const item of revisionItems) {
$list.append($('<option>', { $list.append($('<option>', {
value: item.noteRevisionId, value: item.noteRevisionId,
text: item.dateModifiedFrom text: item.dateLastEdited
})); }));
} }
@ -46,18 +46,20 @@ export async function showNoteRevisionsDialog(noteId, noteRevisionId) {
} }
} }
$list.on('change', () => { $list.on('change', async () => {
const optVal = $list.find(":selected").val(); const optVal = $list.find(":selected").val();
const revisionItem = revisionItems.find(r => r.noteRevisionId === optVal); const revisionItem = revisionItems.find(r => r.noteRevisionId === optVal);
$title.html(revisionItem.title); $title.html(revisionItem.title);
const fullNoteRevision = await server.get(`notes/${revisionItem.noteId}/revisions/${revisionItem.noteRevisionId}`);
if (note.type === 'text') { if (note.type === 'text') {
$content.html(revisionItem.content); $content.html(fullNoteRevision.content);
} }
else if (note.type === 'code') { else if (note.type === 'code') {
$content.html($("<pre>").text(revisionItem.content)); $content.html($("<pre>").text(fullNoteRevision.content));
} }
else { else {
$content.text("Preview isn't available for this note type."); $content.text("Preview isn't available for this note type.");

View File

@ -27,7 +27,7 @@ class NoteRevisionsWidget extends StandardWidget {
} }
async doRenderBody() { async doRenderBody() {
const revisionItems = await server.get(`notes/${this.ctx.note.noteId}/revision-list`); const revisionItems = await server.get(`notes/${this.ctx.note.noteId}/revisions`);
if (revisionItems.length === 0) { if (revisionItems.length === 0) {
this.$body.text("No revisions yet..."); this.$body.text("No revisions yet...");
@ -44,7 +44,7 @@ class NoteRevisionsWidget extends StandardWidget {
'data-note-path': this.ctx.note.noteId, 'data-note-path': this.ctx.note.noteId,
'data-note-revision-id': item.noteRevisionId, 'data-note-revision-id': item.noteRevisionId,
href: 'javascript:' href: 'javascript:'
}).text(item.dateModifiedFrom.substr(0, 16))); }).text(item.dateLastEdited.substr(0, 16)));
if (item.contentLength !== null) { if (item.contentLength !== null) {
$listItem.append($("<span>").text(` (${item.contentLength} characters)`)) $listItem.append($("<span>").text(` (${item.contentLength} characters)`))

View File

@ -4,33 +4,21 @@ const repository = require('../../services/repository');
const noteCacheService = require('../../services/note_cache'); const noteCacheService = require('../../services/note_cache');
async function getNoteRevisions(req) { async function getNoteRevisions(req) {
const noteId = req.params.noteId; const {noteId} = req.params;
return await repository.getEntities(` return await repository.getEntities(`
SELECT note_revisions.* SELECT note_revisions.*
FROM note_revisions FROM note_revisions
WHERE noteId = ? WHERE noteId = ?
ORDER BY utcDateModifiedTo DESC`, [noteId]); ORDER BY utcDateCreated DESC`, [noteId]);
} }
async function getNoteRevisionList(req) { async function getNoteRevision(req) {
const noteId = req.params.noteId; const {noteRevisionId} = req.params;
return await repository.getEntities(` return await repository.getNote(`SELECT *
SELECT noteRevisionId,
noteId,
title,
isProtected,
utcDateModifiedFrom,
utcDateModifiedTo,
dateModifiedFrom,
dateModifiedTo,
type,
mime,
CASE isProtected WHEN 1 THEN null ELSE LENGTH(content) END AS contentLength
FROM note_revisions FROM note_revisions
WHERE noteId = ? WHERE noteRevisionId = ?`, [noteId]);
ORDER BY utcDateModifiedTo DESC`, [noteId]);
} }
async function getEditedNotesOnDate(req) { async function getEditedNotesOnDate(req) {
@ -42,7 +30,7 @@ async function getEditedNotesOnDate(req) {
left join note_revisions using (noteId) left join note_revisions using (noteId)
where substr(notes.dateCreated, 0, 11) = ? where substr(notes.dateCreated, 0, 11) = ?
or substr(notes.dateModified, 0, 11) = ? or substr(notes.dateModified, 0, 11) = ?
or substr(note_revisions.dateModifiedFrom, 0, 11) = ?`, [date, date, date]); or substr(note_revisions.dateLastEdited, 0, 11) = ?`, [date, date, date]);
for (const note of notes) { for (const note of notes) {
const notePath = noteCacheService.getNotePath(note.noteId); const notePath = noteCacheService.getNotePath(note.noteId);
@ -55,6 +43,6 @@ async function getEditedNotesOnDate(req) {
module.exports = { module.exports = {
getNoteRevisions, getNoteRevisions,
getNoteRevisionList, getNoteRevision,
getEditedNotesOnDate getEditedNotesOnDate
}; };

View File

@ -13,12 +13,12 @@ async function getRecentChanges() {
notes.title AS current_title, notes.title AS current_title,
notes.isProtected AS current_isProtected, notes.isProtected AS current_isProtected,
note_revisions.title, note_revisions.title,
note_revisions.utcDateModifiedTo AS date note_revisions.utcDateCreated AS date
FROM FROM
note_revisions note_revisions
JOIN notes USING(noteId) JOIN notes USING(noteId)
ORDER BY ORDER BY
utcDateModifiedTo DESC utcDateCreated DESC
LIMIT 1000 LIMIT 1000
) )
UNION ALL SELECT * FROM ( UNION ALL SELECT * FROM (

View File

@ -132,7 +132,7 @@ function register(app) {
apiRoute(PUT, '/api/notes/:noteId/protect/:isProtected', notesApiRoute.protectSubtree); apiRoute(PUT, '/api/notes/:noteId/protect/:isProtected', notesApiRoute.protectSubtree);
apiRoute(PUT, /\/api\/notes\/(.*)\/type\/(.*)\/mime\/(.*)/, notesApiRoute.setNoteTypeMime); apiRoute(PUT, /\/api\/notes\/(.*)\/type\/(.*)\/mime\/(.*)/, notesApiRoute.setNoteTypeMime);
apiRoute(GET, '/api/notes/:noteId/revisions', noteRevisionsApiRoute.getNoteRevisions); apiRoute(GET, '/api/notes/:noteId/revisions', noteRevisionsApiRoute.getNoteRevisions);
apiRoute(GET, '/api/notes/:noteId/revision-list', noteRevisionsApiRoute.getNoteRevisionList); apiRoute(GET, '/api/notes/:noteId/revisions/:noteRevisionId', noteRevisionsApiRoute.getNoteRevision);
apiRoute(POST, '/api/notes/relation-map', notesApiRoute.getRelationMap); apiRoute(POST, '/api/notes/relation-map', notesApiRoute.getRelationMap);
apiRoute(PUT, '/api/notes/:noteId/change-title', notesApiRoute.changeTitle); apiRoute(PUT, '/api/notes/:noteId/change-title', notesApiRoute.changeTitle);
apiRoute(POST, '/api/notes/:noteId/duplicate/:parentNoteId', notesApiRoute.duplicateNote); apiRoute(POST, '/api/notes/:noteId/duplicate/:parentNoteId', notesApiRoute.duplicateNote);

View File

@ -329,24 +329,27 @@ async function saveNoteRevision(note) {
const revisionCutoff = dateUtils.utcDateStr(new Date(now.getTime() - noteRevisionSnapshotTimeInterval * 1000)); const revisionCutoff = dateUtils.utcDateStr(new Date(now.getTime() - noteRevisionSnapshotTimeInterval * 1000));
const existingNoteRevisionId = await sql.getValue( const existingNoteRevisionId = await sql.getValue(
"SELECT noteRevisionId FROM note_revisions WHERE noteId = ? AND utcDateModifiedTo >= ?", [note.noteId, revisionCutoff]); "SELECT noteRevisionId FROM note_revisions WHERE noteId = ? AND utcDateCreated >= ?", [note.noteId, revisionCutoff]);
const msSinceDateCreated = now.getTime() - dateUtils.parseDateTime(note.utcDateCreated).getTime(); const msSinceDateCreated = now.getTime() - dateUtils.parseDateTime(note.utcDateCreated).getTime();
if (!existingNoteRevisionId && msSinceDateCreated >= noteRevisionSnapshotTimeInterval * 1000) { if (!existingNoteRevisionId && msSinceDateCreated >= noteRevisionSnapshotTimeInterval * 1000) {
await new NoteRevision({ const noteRevision = await new NoteRevision({
noteId: note.noteId, noteId: note.noteId,
// title and text should be decrypted now // title and text should be decrypted now
title: note.title, title: note.title,
content: await note.getContent(), contentLength: -1, // will be updated in .setContent()
type: note.type, type: note.type,
mime: note.mime, mime: note.mime,
isProtected: false, // will be fixed in the protectNoteRevisions() call isProtected: false, // will be fixed in the protectNoteRevisions() call
utcDateModifiedFrom: note.utcDateModified, utcDateLastEdited: note.utcDateModified,
utcDateModifiedTo: dateUtils.utcNowDateTime(), utcDateCreated: dateUtils.utcNowDateTime(),
dateModifiedFrom: note.dateModified, utcDateModified: dateUtils.utcNowDateTime(),
dateModifiedTo: dateUtils.localNowDateTime() dateLastEdited: note.dateModified,
dateCreated: dateUtils.localNowDateTime()
}).save(); }).save();
await noteRevision.setContent(await note.getContent());
} }
} }

View File

@ -57,6 +57,11 @@ async function getNotes(noteIds) {
return notes; return notes;
} }
/** @returns {Promise<NoteRevision|null>} */
async function getNoteRevision(noteRevisionId) {
return await getEntity("SELECT * FROM note_revisions WHERE noteRevisionId = ?", [noteRevisionId]);
}
/** @returns {Promise<Branch|null>} */ /** @returns {Promise<Branch|null>} */
async function getBranch(branchId) { async function getBranch(branchId) {
return await getEntity("SELECT * FROM branches WHERE branchId = ?", [branchId]); return await getEntity("SELECT * FROM branches WHERE branchId = ?", [branchId]);

View File

@ -102,8 +102,8 @@ async function updateNoteRevision(entity, sourceId) {
await sql.transactional(async () => { await sql.transactional(async () => {
// we update note revision even if date modified to is the same because the only thing which might have changed // we update note revision even if date modified to is the same because the only thing which might have changed
// is the protected status (and correnspondingly title and content) which doesn't affect the utcDateModifiedTo // is the protected status (and correnspondingly title and content) which doesn't affect the utcDateCreated
if (orig === null || orig.utcDateModifiedTo <= entity.utcDateModifiedTo) { if (orig === null || orig.utcDateCreated <= entity.utcDateCreated) {
entity.content = entity.content === null ? null : Buffer.from(entity.content, 'base64'); entity.content = entity.content === null ? null : Buffer.from(entity.content, 'base64');
await sql.replace('note_revisions', entity); await sql.replace('note_revisions', entity);