rename "note revision" to just "revision"

This commit is contained in:
zadam 2023-06-04 23:01:40 +02:00
parent cb9feab7b2
commit 779751a234
46 changed files with 275 additions and 248 deletions

View File

@ -2,7 +2,7 @@
UPDATE etapi_tokens SET tokenHash = 'API token hash value';
UPDATE notes SET title = 'title' WHERE noteId != 'root' AND noteId NOT LIKE '\_%' ESCAPE '\';
UPDATE blobs SET content = 'text' WHERE content IS NOT NULL;
UPDATE note_revisions SET title = 'title';
UPDATE revisions SET title = 'title';
UPDATE attributes SET name = 'name', value = 'value'
WHERE type = 'label'
@ -28,7 +28,7 @@ UPDATE attributes SET name = 'name', value = 'value'
'widget',
'noteInfoWidgetDisabled',
'linkMapWidgetDisabled',
'noteRevisionsWidgetDisabled',
'revisionsWidgetDisabled',
'whatLinksHereWidgetDisabled',
'similarNotesWidgetDisabled',
'workspace',
@ -103,7 +103,7 @@ UPDATE attributes SET name = 'name'
'widget',
'noteInfoWidgetDisabled',
'linkMapWidgetDisabled',
'noteRevisionsWidgetDisabled',
'revisionsWidgetDisabled',
'whatLinksHereWidgetDisabled',
'similarNotesWidgetDisabled',
'workspace',

View File

@ -0,0 +1,25 @@
CREATE TABLE IF NOT EXISTS "revisions" (`revisionId` 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);
INSERT INTO revisions (revisionId, noteId, type, mime, title, isProtected, utcDateLastEdited, utcDateCreated, utcDateModified, dateLastEdited, dateCreated, blobId)
SELECT noteRevisionId, noteId, type, mime, title, isProtected, utcDateLastEdited, utcDateCreated, utcDateModified, dateLastEdited, dateCreated, blobId FROM note_revisions;
DROP TABLE note_revisions;
CREATE INDEX `IDX_revisions_noteId` ON `revisions` (`noteId`);
CREATE INDEX `IDX_revisions_utcDateCreated` ON `revisions` (`utcDateCreated`);
CREATE INDEX `IDX_revisions_utcDateLastEdited` ON `revisions` (`utcDateLastEdited`);
CREATE INDEX `IDX_revisions_dateCreated` ON `revisions` (`dateCreated`);
CREATE INDEX `IDX_revisions_dateLastEdited` ON `revisions` (`dateLastEdited`);
UPDATE entity_changes SET entity_name = 'revisions' WHERE entity_name = 'note_revisions';

View File

@ -35,24 +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, blobId TEXT DEFAULT 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 "revisions" (`revisionId` 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, blobId TEXT DEFAULT NULL);
`dateCreated` TEXT NOT NULL);
CREATE TABLE IF NOT EXISTS "options"
(
name TEXT not null PRIMARY KEY,
@ -84,11 +86,11 @@ 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 INDEX `IDX_note_revisions_noteId` ON `note_revisions` (`noteId`);
CREATE INDEX `IDX_note_revisions_utcDateCreated` ON `note_revisions` (`utcDateCreated`);
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 INDEX `IDX_revisions_noteId` ON `revisions` (`noteId`);
CREATE INDEX `IDX_revisions_utcDateCreated` ON `revisions` (`utcDateCreated`);
CREATE INDEX `IDX_revisions_utcDateLastEdited` ON `revisions` (`utcDateLastEdited`);
CREATE INDEX `IDX_revisions_dateCreated` ON `revisions` (`dateCreated`);
CREATE INDEX `IDX_revisions_dateLastEdited` ON `revisions` (`dateLastEdited`);
CREATE INDEX `IDX_entity_changes_changeId` ON `entity_changes` (`changeId`);
CREATE INDEX IDX_attributes_name_value
on attributes (name, value);

View File

@ -144,12 +144,12 @@ class Becca {
return this.childParentToBranch[`${childNoteId}-${parentNoteId}`];
}
/** @returns {BNoteRevision|null} */
getNoteRevision(noteRevisionId) {
const row = sql.getRow("SELECT * FROM note_revisions WHERE noteRevisionId = ?", [noteRevisionId]);
/** @returns {BRevision|null} */
getRevision(revisionId) {
const row = sql.getRow("SELECT * FROM revisions WHERE revisionId = ?", [revisionId]);
const BNoteRevision = require("./entities/bnote_revision"); // avoiding circular dependency problems
return row ? new BNoteRevision(row) : null;
const BRevision = require("./entities/brevision.js"); // avoiding circular dependency problems
return row ? new BRevision(row) : null;
}
/** @returns {BAttachment|null} */
@ -213,8 +213,8 @@ class Becca {
return null;
}
if (entityName === 'note_revisions') {
return this.getNoteRevision(entityId);
if (entityName === 'revisions') {
return this.getRevision(entityId);
} else if (entityName === 'attachments') {
return this.getAttachment(entityId);
} else if (entityName === 'blobs') {
@ -243,12 +243,12 @@ class Becca {
return rows.map(row => new BRecentNote(row));
}
/** @returns {BNoteRevision[]} */
getNoteRevisionsFromQuery(query, params = []) {
/** @returns {BRevision[]} */
getRevisionsFromQuery(query, params = []) {
const rows = sql.getRows(query, params);
const BNoteRevision = require("./entities/bnote_revision"); // avoiding circular dependency problems
return rows.map(row => new BNoteRevision(row));
const BRevision = require("./entities/brevision.js"); // avoiding circular dependency problems
return rows.map(row => new BRevision(row));
}
/** Should be called when the set of all non-skeleton notes changes (added/removed) */

View File

@ -170,7 +170,7 @@ class AbstractBeccaEntity {
return;
}
if (sql.getValue("SELECT 1 FROM note_revisions WHERE blobId = ? LIMIT 1", [blobId])) {
if (sql.getValue("SELECT 1 FROM revisions WHERE blobId = ? LIMIT 1", [blobId])) {
return;
}

View File

@ -38,7 +38,7 @@ class BAttachment extends AbstractBeccaEntity {
/** @type {string} */
this.attachmentId = row.attachmentId;
/** @type {string} either noteId or noteRevisionId to which this attachment belongs */
/** @type {string} either noteId or revisionId to which this attachment belongs */
this.parentId = row.parentId;
/** @type {string} */
this.role = row.role;

View File

@ -6,7 +6,7 @@ const sql = require('../../services/sql');
const utils = require('../../services/utils');
const dateUtils = require('../../services/date_utils');
const AbstractBeccaEntity = require("./abstract_becca_entity");
const BNoteRevision = require("./bnote_revision");
const BRevision = require("./brevision.js");
const BAttachment = require("./battachment");
const TaskContext = require("../../services/task_context");
const dayjs = require("dayjs");
@ -1102,10 +1102,10 @@ class BNote extends AbstractBeccaEntity {
return minDistance;
}
/** @returns {BNoteRevision[]} */
getNoteRevisions() {
return sql.getRows("SELECT * FROM note_revisions WHERE noteId = ?", [this.noteId])
.map(row => new BNoteRevision(row));
/** @returns {BRevision[]} */
getRevisions() {
return sql.getRows("SELECT * FROM revisions WHERE noteId = ?", [this.noteId])
.map(row => new BRevision(row));
}
/** @returns {BAttachment[]} */
@ -1571,14 +1571,14 @@ class BNote extends AbstractBeccaEntity {
}
/**
* @returns {BNoteRevision|null}
* @returns {BRevision|null}
*/
saveNoteRevision() {
saveRevision() {
return sql.transactional(() => {
let noteContent = this.getContent();
const contentMetadata = this.getContentMetadata();
const noteRevision = new BNoteRevision({
const revision = new BRevision({
noteId: this.noteId,
// title and text should be decrypted now
title: this.title,
@ -1596,7 +1596,7 @@ class BNote extends AbstractBeccaEntity {
dateCreated: dateUtils.localNowDateTime()
}, true);
noteRevision.save(); // to generate noteRevisionId which is then used to save attachments
revision.save(); // to generate revisionId which is then used to save attachments
for (const noteAttachment of this.getAttachments()) {
if (noteAttachment.utcDateScheduledForErasureSince) {
@ -1604,16 +1604,16 @@ class BNote extends AbstractBeccaEntity {
}
const revisionAttachment = noteAttachment.copy();
revisionAttachment.parentId = noteRevision.noteRevisionId;
revisionAttachment.parentId = revision.revisionId;
revisionAttachment.setContent(noteAttachment.getContent(), { forceSave: true });
// content is rewritten to point to the revision attachments
noteContent = noteContent.replaceAll(`attachments/${noteAttachment.attachmentId}`, `attachments/${revisionAttachment.attachmentId}`);
}
noteRevision.setContent(noteContent, { forceSave: true });
revision.setContent(noteContent, { forceSave: true });
return noteRevision;
return revision;
});
}

View File

@ -9,21 +9,21 @@ const sql = require("../../services/sql");
const BAttachment = require("./battachment");
/**
* NoteRevision represents a snapshot of note's title and content at some point in the past.
* Revision represents a snapshot of note's title and content at some point in the past.
* It's used for seamless note versioning.
*
* @extends AbstractBeccaEntity
*/
class BNoteRevision extends AbstractBeccaEntity {
static get entityName() { return "note_revisions"; }
static get primaryKeyName() { return "noteRevisionId"; }
static get hashedProperties() { return ["noteRevisionId", "noteId", "title", "isProtected", "dateLastEdited", "dateCreated", "utcDateLastEdited", "utcDateCreated", "utcDateModified"]; }
class BRevision extends AbstractBeccaEntity {
static get entityName() { return "revisions"; }
static get primaryKeyName() { return "revisionId"; }
static get hashedProperties() { return ["revisionId", "noteId", "title", "isProtected", "dateLastEdited", "dateCreated", "utcDateLastEdited", "utcDateCreated", "utcDateModified"]; }
constructor(row, titleDecrypted = false) {
super();
/** @type {string} */
this.noteRevisionId = row.noteRevisionId;
this.revisionId = row.revisionId;
/** @type {string} */
this.noteId = row.noteId;
/** @type {string} */
@ -66,14 +66,14 @@ class BNoteRevision extends AbstractBeccaEntity {
}
isContentAvailable() {
return !this.noteRevisionId // new note which was not encrypted yet
return !this.revisionId // new note which was not encrypted yet
|| !this.isProtected
|| protectedSessionService.isProtectedSessionAvailable()
}
/*
* Note revision content has quite special handling - it's not a separate entity, but a lazily loaded
* part of NoteRevision entity with its own sync. The reason behind this hybrid design is that
* part of Revision entity with its own sync. The reason behind this hybrid design is that
* content can be quite large, and it's not necessary to load it / fill memory for any note access even
* if we don't need a content, especially for bulk operations like search.
*
@ -88,7 +88,7 @@ class BNoteRevision extends AbstractBeccaEntity {
/**
* @param content
* @param {object} [opts]
* @param {object} [opts.forceSave=false] - will also save this BNoteRevision entity
* @param {object} [opts.forceSave=false] - will also save this BRevision entity
*/
setContent(content, opts) {
this._setContent(content, opts);
@ -100,7 +100,7 @@ class BNoteRevision extends AbstractBeccaEntity {
SELECT attachments.*
FROM attachments
WHERE parentId = ?
AND isDeleted = 0`, [this.noteRevisionId])
AND isDeleted = 0`, [this.revisionId])
.map(row => new BAttachment(row));
}
@ -112,7 +112,7 @@ class BNoteRevision extends AbstractBeccaEntity {
getPojo() {
return {
noteRevisionId: this.noteRevisionId,
revisionId: this.revisionId,
noteId: this.noteId,
type: this.type,
mime: this.mime,
@ -148,4 +148,4 @@ class BNoteRevision extends AbstractBeccaEntity {
}
}
module.exports = BNoteRevision;
module.exports = BRevision;

View File

@ -1,5 +1,5 @@
const BNote = require('./entities/bnote');
const BNoteRevision = require('./entities/bnote_revision');
const BRevision = require('./entities/brevision.js');
const BAttachment = require("./entities/battachment");
const BBranch = require('./entities/bbranch');
const BAttribute = require('./entities/battribute');
@ -11,7 +11,7 @@ const ENTITY_NAME_TO_ENTITY = {
"attributes": BAttribute,
"branches": BBranch,
"notes": BNote,
"note_revisions": BNoteRevision,
"revisions": BRevision,
"attachments": BAttachment,
"recent_notes": BRecentNote,
"etapi_tokens": BEtapiToken,

View File

@ -24,7 +24,7 @@ const IGNORED_ATTR_NAMES = [
"keyboardshortcut",
"noteinfowidgetdisabled",
"linkmapwidgetdisabled",
"noterevisionswidgetdisabled",
"revisionswidgetdisabled",
"whatlinksherewidgetdisabled",
"similarnoteswidgetdisabled",
"disableinclusion",

View File

@ -308,7 +308,7 @@ paths:
default: html
post:
description: Create a note revision for the given note
operationId: createNoteRevision
operationId: createRevision
responses:
'204':
description: revision has been created

View File

@ -149,7 +149,7 @@ function register(router) {
eu.route(router, 'post' ,'/etapi/notes/:noteId/note-revision', (req, res, next) => {
const note = eu.getAndCheckNote(req.params.noteId);
note.saveNoteRevision();
note.saveRevision();
return res.sendStatus(204);
});

View File

@ -197,7 +197,7 @@ export default class Entrypoints extends Component {
this.hideAllPopups();
}
async forceSaveNoteRevisionCommand() {
async forceSaveRevisionCommand() {
const noteId = appContext.tabManager.getActiveContextNoteId();
await server.post(`notes/${noteId}/revision`);

View File

@ -61,7 +61,7 @@ import ImportDialog from "../widgets/dialogs/import.js";
import ExportDialog from "../widgets/dialogs/export.js";
import MarkdownImportDialog from "../widgets/dialogs/markdown_import.js";
import ProtectedSessionPasswordDialog from "../widgets/dialogs/protected_session_password.js";
import NoteRevisionsDialog from "../widgets/dialogs/note_revisions.js";
import RevisionsDialog from "../widgets/dialogs/revisions.js";
import DeleteNotesDialog from "../widgets/dialogs/delete_notes.js";
import InfoDialog from "../widgets/dialogs/info.js";
import ConfirmDialog from "../widgets/dialogs/confirm.js";
@ -70,7 +70,7 @@ import FloatingButtons from "../widgets/floating_buttons/floating_buttons.js";
import RelationMapButtons from "../widgets/floating_buttons/relation_map_buttons.js";
import MermaidExportButton from "../widgets/floating_buttons/mermaid_export_button.js";
import LauncherContainer from "../widgets/containers/launcher_container.js";
import NoteRevisionsButton from "../widgets/buttons/note_revisions_button.js";
import RevisionsButton from "../widgets/buttons/revisions_button.js";
import CodeButtonsWidget from "../widgets/floating_buttons/code_buttons.js";
import ApiLogWidget from "../widgets/api_log.js";
import HideFloatingButtonsButton from "../widgets/floating_buttons/hide_floating_buttons_button.js";
@ -147,7 +147,7 @@ export default class DesktopLayout {
.ribbon(new NoteMapRibbonWidget())
.ribbon(new SimilarNotesWidget())
.ribbon(new NoteInfoWidget())
.button(new NoteRevisionsButton())
.button(new RevisionsButton())
.button(new NoteActionsWidget())
)
.child(new SharedInfoWidget())
@ -204,7 +204,7 @@ export default class DesktopLayout {
.child(new UploadAttachmentsDialog())
.child(new MarkdownImportDialog())
.child(new ProtectedSessionPasswordDialog())
.child(new NoteRevisionsDialog())
.child(new RevisionsDialog())
.child(new DeleteNotesDialog())
.child(new InfoDialog())
.child(new ConfirmDialog())

View File

@ -2,7 +2,7 @@ import server from "./server.js";
import ws from "./ws.js";
import MoveNoteBulkAction from "../widgets/bulk_actions/note/move_note.js";
import DeleteNoteBulkAction from "../widgets/bulk_actions/note/delete_note.js";
import DeleteNoteRevisionsBulkAction from "../widgets/bulk_actions/note/delete_note_revisions.js";
import DeleteRevisionsBulkAction from "../widgets/bulk_actions/note/delete_revisions.js";
import DeleteLabelBulkAction from "../widgets/bulk_actions/label/delete_label.js";
import DeleteRelationBulkAction from "../widgets/bulk_actions/relation/delete_relation.js";
import RenameLabelBulkAction from "../widgets/bulk_actions/label/rename_label.js";
@ -25,7 +25,7 @@ const ACTION_GROUPS = [
},
{
title: 'Notes',
actions: [RenameNoteBulkAction, MoveNoteBulkAction, DeleteNoteBulkAction, DeleteNoteRevisionsBulkAction],
actions: [RenameNoteBulkAction, MoveNoteBulkAction, DeleteNoteBulkAction, DeleteRevisionsBulkAction],
},
{
title: 'Other',
@ -37,7 +37,7 @@ const ACTION_CLASSES = [
RenameNoteBulkAction,
MoveNoteBulkAction,
DeleteNoteBulkAction,
DeleteNoteRevisionsBulkAction,
DeleteRevisionsBulkAction,
DeleteLabelBulkAction,
DeleteRelationBulkAction,
RenameLabelBulkAction,

View File

@ -30,8 +30,8 @@ async function processEntityChanges(entityChanges) {
}
loadResults.addNoteContent(ec.noteIds, ec.componentId);
} else if (ec.entityName === 'note_revisions') {
loadResults.addNoteRevision(ec.entityId, ec.noteId, ec.componentId);
} else if (ec.entityName === 'revisions') {
loadResults.addRevision(ec.entityId, ec.noteId, ec.componentId);
} else if (ec.entityName === 'options') {
if (ec.entity.name === 'openNoteContexts') {
continue; // only noise

View File

@ -18,7 +18,7 @@ export default class LoadResults {
this.noteReorderings = [];
this.noteRevisions = [];
this.revisions = [];
this.contentNoteIdToComponentId = [];
@ -75,12 +75,12 @@ export default class LoadResults {
.filter(attr => !!attr);
}
addNoteRevision(noteRevisionId, noteId, componentId) {
this.noteRevisions.push({noteRevisionId, noteId, componentId});
addRevision(revisionId, noteId, componentId) {
this.revisions.push({revisionId, noteId, componentId});
}
hasNoteRevisionForNote(noteId) {
return !!this.noteRevisions.find(nr => nr.noteId === noteId);
hasRevisionForNote(noteId) {
return !!this.revisions.find(nr => nr.noteId === noteId);
}
getNoteIds() {
@ -140,7 +140,7 @@ export default class LoadResults {
&& this.branches.length === 0
&& this.attributes.length === 0
&& this.noteReorderings.length === 0
&& this.noteRevisions.length === 0
&& this.revisions.length === 0
&& this.contentNoteIdToComponentId.length === 0
&& this.options.length === 0
&& this.attachments.length === 0;

View File

@ -102,8 +102,8 @@ async function openNoteCustom(noteId) {
}
}
function downloadNoteRevision(noteId, noteRevisionId) {
const url = getUrlForDownload(`api/revisions/${noteRevisionId}/download`);
function downloadRevision(noteId, revisionId) {
const url = getUrlForDownload(`api/revisions/${revisionId}/download`);
download(url);
}
@ -164,7 +164,7 @@ function getHost() {
export default {
download,
downloadFileNote,
downloadNoteRevision,
downloadRevision,
downloadAttachment,
getUrlForDownload,
openNoteExternally,

View File

@ -19,8 +19,8 @@ const TPL = `
</td>
</tr>`;
export default class DeleteNoteRevisionsBulkAction extends AbstractBulkAction {
static get actionName() { return "deleteNoteRevisions"; }
export default class DeleteRevisionsBulkAction extends AbstractBulkAction {
static get actionName() { return "deleteRevisions"; }
static get actionTitle() { return "Delete note revisions"; }
doRender() {

View File

@ -1,12 +1,12 @@
import CommandButtonWidget from "./command_button.js";
export default class NoteRevisionsButton extends CommandButtonWidget {
export default class RevisionsButton extends CommandButtonWidget {
constructor() {
super();
this.icon('bx-history')
.title("Note Revisions")
.command("showNoteRevisions")
.command("showRevisions")
.titlePlacement("bottom")
.class("icon-action");
}

View File

@ -72,13 +72,13 @@ const TPL = `
</div>
</div>`;
export default class NoteRevisionsDialog extends BasicWidget {
export default class RevisionsDialog extends BasicWidget {
constructor() {
super();
this.revisionItems = [];
this.note = null;
this.noteRevisionId = null;
this.revisionId = null;
}
doRender() {
@ -100,7 +100,7 @@ export default class NoteRevisionsDialog extends BasicWidget {
});
this.$widget.on('shown.bs.modal', () => {
this.$list.find(`[data-note-revision-id="${this.noteRevisionId}"]`)
this.$list.find(`[data-note-revision-id="${this.revisionId}"]`)
.trigger('focus');
});
@ -130,13 +130,13 @@ export default class NoteRevisionsDialog extends BasicWidget {
});
}
async showNoteRevisionsEvent({noteId = appContext.tabManager.getActiveContextNoteId()}) {
async showRevisionsEvent({noteId = appContext.tabManager.getActiveContextNoteId()}) {
utils.openDialog(this.$widget);
await this.loadNoteRevisions(noteId);
await this.loadRevisions(noteId);
}
async loadNoteRevisions(noteId) {
async loadRevisions(noteId) {
this.$list.empty();
this.$content.empty();
this.$titleButtons.empty();
@ -148,7 +148,7 @@ export default class NoteRevisionsDialog extends BasicWidget {
this.$list.append(
$('<a class="dropdown-item" tabindex="0">')
.text(`${item.dateLastEdited.substr(0, 16)} (${item.contentLength} bytes)`)
.attr('data-note-revision-id', item.noteRevisionId)
.attr('data-note-revision-id', item.revisionId)
.attr('title', `This revision was last edited on ${item.dateLastEdited}`)
);
}
@ -156,21 +156,21 @@ export default class NoteRevisionsDialog extends BasicWidget {
this.$listDropdown.dropdown('show');
if (this.revisionItems.length > 0) {
if (!this.noteRevisionId) {
this.noteRevisionId = this.revisionItems[0].noteRevisionId;
if (!this.revisionId) {
this.revisionId = this.revisionItems[0].revisionId;
}
} else {
this.$title.text("No revisions for this note yet...");
this.noteRevisionId = null;
this.revisionId = null;
}
this.$eraseAllRevisionsButton.toggle(this.revisionItems.length > 0);
}
async setContentPane() {
const noteRevisionId = this.$list.find(".active").attr('data-note-revision-id');
const revisionId = this.$list.find(".active").attr('data-note-revision-id');
const revisionItem = this.revisionItems.find(r => r.noteRevisionId === noteRevisionId);
const revisionItem = this.revisionItems.find(r => r.revisionId === revisionId);
this.$title.html(revisionItem.title);
@ -188,7 +188,7 @@ export default class NoteRevisionsDialog extends BasicWidget {
const text = 'Do you want to restore this revision? This will overwrite current title/content of the note with this revision.';
if (await dialogService.confirm(text)) {
await server.post(`revisions/${revisionItem.noteRevisionId}/restore`);
await server.post(`revisions/${revisionItem.revisionId}/restore`);
this.$widget.modal('hide');
@ -202,9 +202,9 @@ export default class NoteRevisionsDialog extends BasicWidget {
const text = 'Do you want to delete this revision? This action will delete revision title and content, but still preserve revision metadata.';
if (await dialogService.confirm(text)) {
await server.remove(`revisions/${revisionItem.noteRevisionId}`);
await server.remove(`revisions/${revisionItem.revisionId}`);
this.loadNoteRevisions(revisionItem.noteId);
this.loadRevisions(revisionItem.noteId);
toastService.showMessage('Note revision has been deleted.');
}
@ -222,7 +222,7 @@ export default class NoteRevisionsDialog extends BasicWidget {
const $downloadButton = $('<button class="btn btn-sm btn-primary" type="button">Download</button>');
$downloadButton.on('click', () => openService.downloadNoteRevision(revisionItem.noteId, revisionItem.noteRevisionId));
$downloadButton.on('click', () => openService.downloadRevision(revisionItem.noteId, revisionItem.revisionId));
if (!revisionItem.isProtected || protectedSessionHolder.isProtectedSessionAvailable()) {
this.$titleButtons.append($downloadButton);
@ -232,10 +232,10 @@ export default class NoteRevisionsDialog extends BasicWidget {
async renderContent(revisionItem) {
this.$content.empty();
const fullNoteRevision = await server.get(`revisions/${revisionItem.noteRevisionId}`);
const fullRevision = await server.get(`revisions/${revisionItem.revisionId}`);
if (revisionItem.type === 'text') {
this.$content.html(fullNoteRevision.content);
this.$content.html(fullRevision.content);
if (this.$content.find('span.math-tex').length > 0) {
await libraryLoader.requireLibrary(libraryLoader.KATEX);
@ -243,12 +243,12 @@ export default class NoteRevisionsDialog extends BasicWidget {
renderMathInElement(this.$content[0], {trust: true});
}
} else if (revisionItem.type === 'code' || revisionItem.type === 'mermaid') {
this.$content.html($("<pre>").text(fullNoteRevision.content));
this.$content.html($("<pre>").text(fullRevision.content));
} else if (revisionItem.type === 'image') {
this.$content.html($("<img>")
// reason why we put this inline as base64 is that we do not want to let user copy this
// as a URL to be used in a note. Instead, if they copy and paste it into a note, it will be an uploaded as a new note
.attr("src", `data:${fullNoteRevision.mime};base64,${fullNoteRevision.content}`)
.attr("src", `data:${fullRevision.mime};base64,${fullRevision.content}`)
.css("max-width", "100%")
.css("max-height", "100%"));
} else if (revisionItem.type === 'file') {
@ -262,12 +262,12 @@ export default class NoteRevisionsDialog extends BasicWidget {
$("<td>").text(`${revisionItem.contentLength} bytes`)
));
if (fullNoteRevision.content) {
if (fullRevision.content) {
$table.append($("<tr>").append(
$('<td colspan="2">').append(
$('<div style="font-weight: bold;">').text("Preview:"),
$('<pre class="file-preview-content"></pre>')
.text(fullNoteRevision.content)
.text(fullRevision.content)
)
));
}
@ -278,7 +278,7 @@ export default class NoteRevisionsDialog extends BasicWidget {
* FIXME: We load a font called Virgil.wof2, which originates from excalidraw.com
* REMOVE external dependency!!!! This is defined in the svg in defs.style
*/
const content = fullNoteRevision.content;
const content = fullRevision.content;
try {
const data = JSON.parse(content)
@ -291,7 +291,7 @@ export default class NoteRevisionsDialog extends BasicWidget {
const $svgHtml = $(svg).css({maxWidth: "100%", height: "auto"});
this.$content.html($('<div>').append($svgHtml));
} catch (err) {
console.error("error parsing fullNoteRevision.content as JSON", fullNoteRevision.content, err);
console.error("error parsing fullRevision.content as JSON", fullRevision.content, err);
this.$content.html($("<div>").text("Error parsing content. Please check console.error() for more details."));
}
} else {

View File

@ -21,7 +21,7 @@ import SyncOptions from "./options/sync.js";
import SearchEngineOptions from "./options/other/search_engine.js";
import TrayOptions from "./options/other/tray.js";
import NoteErasureTimeoutOptions from "./options/other/note_erasure_timeout.js";
import NoteRevisionsSnapshotIntervalOptions from "./options/other/note_revisions_snapshot_interval.js";
import RevisionsSnapshotIntervalOptions from "./options/other/revisions_snapshot_interval.js";
import NetworkConnectionsOptions from "./options/other/network_connections.js";
import AdvancedSyncOptions from "./options/advanced/sync.js";
import DatabaseIntegrityCheckOptions from "./options/advanced/database_integrity_check.js";
@ -81,7 +81,7 @@ const CONTENT_WIDGETS = {
TrayOptions,
NoteErasureTimeoutOptions,
AttachmentErasureTimeoutOptions,
NoteRevisionsSnapshotIntervalOptions,
RevisionsSnapshotIntervalOptions,
NetworkConnectionsOptions
],
_optionsAdvanced: [

View File

@ -12,15 +12,15 @@ const TPL = `
</div>
</div>`;
export default class NoteRevisionsSnapshotIntervalOptions extends OptionsWidget {
export default class RevisionsSnapshotIntervalOptions extends OptionsWidget {
doRender() {
this.$widget = $(TPL);
this.$noteRevisionsTimeInterval = this.$widget.find(".note-revision-snapshot-time-interval-in-seconds");
this.$noteRevisionsTimeInterval.on('change', () =>
this.updateOption('noteRevisionSnapshotTimeInterval', this.$noteRevisionsTimeInterval.val()));
this.$revisionsTimeInterval = this.$widget.find(".note-revision-snapshot-time-interval-in-seconds");
this.$revisionsTimeInterval.on('change', () =>
this.updateOption('revisionSnapshotTimeInterval', this.$revisionsTimeInterval.val()));
}
async optionsLoaded(options) {
this.$noteRevisionsTimeInterval.val(options.noteRevisionSnapshotTimeInterval);
this.$revisionsTimeInterval.val(options.revisionSnapshotTimeInterval);
}
}

View File

@ -16,7 +16,7 @@ function updateFile(req) {
const note = becca.getNoteOrThrow(req.params.noteId);
const file = req.file;
note.saveNoteRevision();
note.saveRevision();
note.mime = file.mimetype.toLowerCase();
note.save();
@ -35,7 +35,7 @@ function updateFile(req) {
function updateAttachment(req) {
const attachment = becca.getAttachmentOrThrow(req.params.attachmentId);
const file = req.file;
attachment.getNote().saveNoteRevision();
attachment.getNote().saveRevision();
attachment.mime = file.mimetype.toLowerCase();
attachment.setContent(file.buffer, {forceSave: true});
@ -186,7 +186,7 @@ function uploadModifiedFileToNote(req) {
log.info(`Updating note '${noteId}' with content from '${filePath}'`);
note.saveNoteRevision();
note.saveRevision();
const fileContent = fs.readFileSync(filePath);
@ -205,7 +205,7 @@ function uploadModifiedFileToAttachment(req) {
log.info(`Updating attachment '${attachmentId}' with content from '${filePath}'`);
attachment.getNote().saveNoteRevision();
attachment.getNote().saveRevision();
const fileContent = fs.readFileSync(filePath);

View File

@ -131,7 +131,7 @@ function changeTitle(req) {
const noteTitleChanged = note.title !== title;
if (noteTitleChanged) {
noteService.saveNoteRevisionIfNeeded(note);
noteService.saveRevisionIfNeeded(note);
}
note.title = title;
@ -216,7 +216,7 @@ function getDeleteNotesPreview(req) {
};
}
function forceSaveNoteRevision(req) {
function forceSaveRevision(req) {
const {noteId} = req.params;
const note = becca.getNoteOrThrow(noteId);
@ -224,7 +224,7 @@ function forceSaveNoteRevision(req) {
throw new ValidationError(`Note revision of a protected note cannot be created outside of a protected session.`);
}
note.saveNoteRevision();
note.saveRevision();
}
function convertNoteToAttachment(req) {
@ -252,6 +252,6 @@ module.exports = {
eraseDeletedNotesNow,
eraseUnusedAttachmentsNow,
getDeleteNotesPreview,
forceSaveNoteRevision,
forceSaveRevision,
convertNoteToAttachment
};

View File

@ -9,7 +9,7 @@ const ValidationError = require("../../errors/validation_error");
const ALLOWED_OPTIONS = new Set([
'eraseEntitiesAfterTimeInSeconds',
'protectedSessionTimeout',
'noteRevisionSnapshotTimeInterval',
'revisionSnapshotTimeInterval',
'zoomFactor',
'theme',
'syncServerHost',
@ -28,7 +28,7 @@ const ALLOWED_OPTIONS = new Set([
'noteInfoWidget',
'attributesWidget',
'linkMapWidget',
'noteRevisionsWidget',
'revisionsWidget',
'whatLinksHereWidget',
'similarNotesWidget',
'editedNotesWidget',

View File

@ -10,25 +10,25 @@ function getRecentChanges(req) {
let recentChanges = [];
const noteRevisionRows = sql.getRows(`
const revisionRows = sql.getRows(`
SELECT
notes.noteId,
notes.isDeleted AS current_isDeleted,
notes.deleteId AS current_deleteId,
notes.title AS current_title,
notes.isProtected AS current_isProtected,
note_revisions.title,
note_revisions.utcDateCreated AS utcDate,
note_revisions.dateCreated AS date
revisions.title,
revisions.utcDateCreated AS utcDate,
revisions.dateCreated AS date
FROM
note_revisions
revisions
JOIN notes USING(noteId)`);
for (const noteRevisionRow of noteRevisionRows) {
const note = becca.getNote(noteRevisionRow.noteId);
for (const revisionRow of revisionRows) {
const note = becca.getNote(revisionRow.noteId);
if (note?.hasAncestor(ancestorNoteId)) {
recentChanges.push(noteRevisionRow);
recentChanges.push(revisionRow);
}
}

View File

@ -1,7 +1,7 @@
"use strict";
const beccaService = require('../../becca/becca_service');
const noteRevisionService = require('../../services/note_revisions');
const revisionService = require('../../services/revisions.js');
const utils = require('../../services/utils');
const sql = require('../../services/sql');
const cls = require('../../services/cls');
@ -9,50 +9,50 @@ const path = require('path');
const becca = require("../../becca/becca");
const blobService = require("../../services/blob.js");
function getNoteRevisionBlob(req) {
function getRevisionBlob(req) {
const preview = req.query.preview === 'true';
return blobService.getBlobPojo('note_revisions', req.params.noteRevisionId, { preview });
return blobService.getBlobPojo('revisions', req.params.revisionId, { preview });
}
function getNoteRevisions(req) {
return becca.getNoteRevisionsFromQuery(`
SELECT note_revisions.*,
function getRevisions(req) {
return becca.getRevisionsFromQuery(`
SELECT revisions.*,
LENGTH(blobs.content) AS contentLength
FROM note_revisions
JOIN blobs ON note_revisions.blobId = blobs.blobId
FROM revisions
JOIN blobs ON revisions.blobId = blobs.blobId
WHERE noteId = ?
ORDER BY utcDateCreated DESC`, [req.params.noteId]);
}
function getNoteRevision(req) {
const noteRevision = becca.getNoteRevision(req.params.noteRevisionId);
function getRevision(req) {
const revision = becca.getRevision(req.params.revisionId);
if (noteRevision.type === 'file') {
if (noteRevision.hasStringContent()) {
noteRevision.content = noteRevision.getContent().substr(0, 10000);
if (revision.type === 'file') {
if (revision.hasStringContent()) {
revision.content = revision.getContent().substr(0, 10000);
}
}
else {
noteRevision.content = noteRevision.getContent();
revision.content = revision.getContent();
if (noteRevision.content && noteRevision.type === 'image') {
noteRevision.content = noteRevision.content.toString('base64');
if (revision.content && revision.type === 'image') {
revision.content = revision.content.toString('base64');
}
}
return noteRevision;
return revision;
}
/**
* @param {BNoteRevision} noteRevision
* @param {BRevision} revision
* @returns {string}
*/
function getRevisionFilename(noteRevision) {
let filename = utils.formatDownloadTitle(noteRevision.title, noteRevision.type, noteRevision.mime);
function getRevisionFilename(revision) {
let filename = utils.formatDownloadTitle(revision.title, revision.type, revision.mime);
const extension = path.extname(filename);
const date = noteRevision.dateCreated
const date = revision.dateCreated
.substr(0, 19)
.replace(' ', '_')
.replace(/[^0-9_]/g, '');
@ -67,50 +67,50 @@ function getRevisionFilename(noteRevision) {
return filename;
}
function downloadNoteRevision(req, res) {
const noteRevision = becca.getNoteRevision(req.params.noteRevisionId);
function downloadRevision(req, res) {
const revision = becca.getRevision(req.params.revisionId);
if (!noteRevision.isContentAvailable()) {
if (!revision.isContentAvailable()) {
return res.setHeader("Content-Type", "text/plain")
.status(401)
.send("Protected session not available");
}
const filename = getRevisionFilename(noteRevision);
const filename = getRevisionFilename(revision);
res.setHeader('Content-Disposition', utils.getContentDisposition(filename));
res.setHeader('Content-Type', noteRevision.mime);
res.setHeader('Content-Type', revision.mime);
res.send(noteRevision.getContent());
res.send(revision.getContent());
}
function eraseAllNoteRevisions(req) {
const noteRevisionIdsToErase = sql.getColumn('SELECT noteRevisionId FROM note_revisions WHERE noteId = ?',
function eraseAllRevisions(req) {
const revisionIdsToErase = sql.getColumn('SELECT revisionId FROM revisions WHERE noteId = ?',
[req.params.noteId]);
noteRevisionService.eraseNoteRevisions(noteRevisionIdsToErase);
revisionService.eraseRevisions(revisionIdsToErase);
}
function eraseNoteRevision(req) {
noteRevisionService.eraseNoteRevisions([req.params.noteRevisionId]);
function eraseRevision(req) {
revisionService.eraseRevisions([req.params.revisionId]);
}
function restoreNoteRevision(req) {
const noteRevision = becca.getNoteRevision(req.params.noteRevisionId);
function restoreRevision(req) {
const revision = becca.getRevision(req.params.revisionId);
if (noteRevision) {
const note = noteRevision.getNote();
if (revision) {
const note = revision.getNote();
sql.transactional(() => {
note.saveNoteRevision();
note.saveRevision();
for (const oldNoteAttachment of note.getAttachments()) {
oldNoteAttachment.markAsDeleted();
}
let revisionContent = noteRevision.getContent();
let revisionContent = revision.getContent();
for (const revisionAttachment of noteRevision.getAttachments()) {
for (const revisionAttachment of revision.getAttachments()) {
const noteAttachment = revisionAttachment.copy();
noteAttachment.parentId = note.noteId;
noteAttachment.setContent(revisionAttachment.getContent(), { forceSave: true });
@ -119,7 +119,7 @@ function restoreNoteRevision(req) {
revisionContent = revisionContent.replaceAll(`attachments/${revisionAttachment.attachmentId}`, `attachments/${noteAttachment.attachmentId}`);
}
note.title = noteRevision.title;
note.title = revision.title;
note.setContent(revisionContent, { forceSave: true });
});
}
@ -134,8 +134,8 @@ function getEditedNotesOnDate(req) {
WHERE notes.dateCreated LIKE :date
OR notes.dateModified LIKE :date
UNION ALL
SELECT noteId FROM note_revisions
WHERE note_revisions.dateLastEdited LIKE :date
SELECT noteId FROM revisions
WHERE revisions.dateLastEdited LIKE :date
)
ORDER BY isDeleted
LIMIT 50`, {date: `${req.params.date}%`});
@ -186,12 +186,12 @@ function getNotePathData(note) {
}
module.exports = {
getNoteRevisionBlob,
getNoteRevisions,
getNoteRevision,
downloadNoteRevision,
getRevisionBlob,
getRevisions,
getRevision,
downloadRevision,
getEditedNotesOnDate,
eraseAllNoteRevisions,
eraseNoteRevision,
restoreNoteRevision
eraseAllRevisions,
eraseRevision,
restoreRevision
};

View File

@ -9,10 +9,10 @@ function getNoteSize(req) {
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 note_revisions ON note_revisions.blobId = blobs.blobId AND note_revisions.noteId = ?
LEFT JOIN revisions ON revisions.blobId = blobs.blobId AND revisions.noteId = ?
WHERE notes.noteId IS NOT NULL
OR attachments.attachmentId IS NOT NULL
OR note_revisions.noteRevisionId IS NOT NULL`, [noteId, noteId, noteId]);
OR revisions.revisionId IS NOT NULL`, [noteId, noteId, noteId]);
const noteSize = Object.values(blobSizes).reduce((acc, blobSize) => acc + blobSize, 0);
@ -33,8 +33,8 @@ function getSubtreeSize(req) {
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 note_revisions ON note_revisions.noteId = param_list.paramId
JOIN blobs ON blobs.blobId = notes.blobId OR blobs.blobId = attachments.blobId OR blobs.blobId = note_revisions.blobId`);
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`);
const subTreeSize = Object.values(blobSizes).reduce((acc, blobSize) => acc + blobSize, 0);

View File

@ -108,9 +108,9 @@ function forceNoteSync(req) {
entityChangesService.moveEntityChangeToTop('attributes', attributeId);
}
for (const noteRevisionId of sql.getColumn("SELECT noteRevisionId FROM note_revisions WHERE noteId = ?", [noteId])) {
sql.execute(`UPDATE note_revisions SET utcDateModified = ? WHERE noteRevisionId = ?`, [now, noteRevisionId]);
entityChangesService.moveEntityChangeToTop('note_revisions', noteRevisionId);
for (const revisionId of sql.getColumn("SELECT revisionId FROM revisions WHERE noteId = ?", [noteId])) {
sql.execute(`UPDATE revisions SET utcDateModified = ? WHERE revisionId = ?`, [now, revisionId]);
entityChangesService.moveEntityChangeToTop('revisions', revisionId);
}
for (const attachmentId of sql.getColumn("SELECT attachmentId FROM attachments WHERE noteId = ?", [noteId])) {

View File

@ -28,7 +28,7 @@ const branchesApiRoute = require('./api/branches');
const attachmentsApiRoute = require('./api/attachments');
const autocompleteApiRoute = require('./api/autocomplete');
const cloningApiRoute = require('./api/cloning');
const noteRevisionsApiRoute = require('./api/note_revisions');
const revisionsApiRoute = require('./api/revisions.js');
const recentChangesApiRoute = require('./api/recent_changes');
const optionsApiRoute = require('./api/options');
const passwordApiRoute = require('./api/password');
@ -117,7 +117,7 @@ function register(app) {
apiRoute(PUT, '/api/notes/:noteId/data', notesApiRoute.updateNoteData);
apiRoute(DEL, '/api/notes/:noteId', notesApiRoute.deleteNote);
apiRoute(PUT, '/api/notes/:noteId/undelete', notesApiRoute.undeleteNote);
apiRoute(PST, '/api/notes/:noteId/revision', notesApiRoute.forceSaveNoteRevision);
apiRoute(PST, '/api/notes/:noteId/revision', notesApiRoute.forceSaveRevision);
apiRoute(PST, '/api/notes/:parentNoteId/children', notesApiRoute.createNote);
apiRoute(PUT, '/api/notes/:noteId/sort-children', notesApiRoute.sortChildNotes);
apiRoute(PUT, '/api/notes/:noteId/protect/:isProtected', notesApiRoute.protectNote);
@ -171,13 +171,13 @@ function register(app) {
route(PUT, '/api/attachments/:attachmentId/file', [auth.checkApiAuthOrElectron, uploadMiddlewareWithErrorHandling, csrfMiddleware],
filesRoute.updateAttachment, apiResultHandler);
apiRoute(GET, '/api/notes/:noteId/revisions', noteRevisionsApiRoute.getNoteRevisions);
apiRoute(DEL, '/api/notes/:noteId/revisions', noteRevisionsApiRoute.eraseAllNoteRevisions);
apiRoute(GET, '/api/revisions/:noteRevisionId', noteRevisionsApiRoute.getNoteRevision);
apiRoute(GET, '/api/revisions/:noteRevisionId/blob', noteRevisionsApiRoute.getNoteRevisionBlob);
apiRoute(DEL, '/api/revisions/:noteRevisionId', noteRevisionsApiRoute.eraseNoteRevision);
apiRoute(PST, '/api/revisions/:noteRevisionId/restore', noteRevisionsApiRoute.restoreNoteRevision);
route(GET, '/api/revisions/:noteRevisionId/download', [auth.checkApiAuthOrElectron], noteRevisionsApiRoute.downloadNoteRevision);
apiRoute(GET, '/api/notes/:noteId/revisions', revisionsApiRoute.getRevisions);
apiRoute(DEL, '/api/notes/:noteId/revisions', revisionsApiRoute.eraseAllRevisions);
apiRoute(GET, '/api/revisions/:revisionId', revisionsApiRoute.getRevision);
apiRoute(GET, '/api/revisions/:revisionId/blob', revisionsApiRoute.getRevisionBlob);
apiRoute(DEL, '/api/revisions/:revisionId', revisionsApiRoute.eraseRevision);
apiRoute(PST, '/api/revisions/:revisionId/restore', revisionsApiRoute.restoreRevision);
route(GET, '/api/revisions/:revisionId/download', [auth.checkApiAuthOrElectron], revisionsApiRoute.downloadRevision);
route(GET, '/api/branches/:branchId/export/:type/:format/:version/:taskId', [auth.checkApiAuthOrElectron], exportRoute.exportBranch);
@ -321,7 +321,7 @@ function register(app) {
route(GET, '/api/fonts', [auth.checkApiAuthOrElectron], fontsRoute.getFontCss);
apiRoute(GET, '/api/other/icon-usage', otherRoute.getIconUsage);
apiRoute(GET, '/api/recent-changes/:ancestorNoteId', recentChangesApiRoute.getRecentChanges);
apiRoute(GET, '/api/edited-notes/:date', noteRevisionsApiRoute.getEditedNotesOnDate);
apiRoute(GET, '/api/edited-notes/:date', revisionsApiRoute.getEditedNotesOnDate);
apiRoute(PST, '/api/note-map/:noteId/tree', noteMapRoute.getTreeMap);
apiRoute(PST, '/api/note-map/:noteId/link', noteMapRoute.getLinkMap);

View File

@ -15,7 +15,7 @@ function getFullAnonymizationScript() {
UPDATE etapi_tokens SET tokenHash = 'API token hash value';
UPDATE notes SET title = 'title' WHERE title NOT IN ('root', '_hidden', '_share');
UPDATE blobs SET content = 'text' WHERE content IS NOT NULL;
UPDATE note_revisions SET title = 'title';
UPDATE revisions SET title = 'title';
UPDATE attributes SET name = 'name', value = 'value' WHERE type = 'label' AND name NOT IN(${builtinAttrNames});
UPDATE attributes SET name = 'name' WHERE type = 'relation' AND name NOT IN (${builtinAttrNames});
@ -36,7 +36,7 @@ function getLightAnonymizationScript() {
return `UPDATE blobs SET content = 'text' WHERE content IS NOT NULL AND blobId NOT IN (
SELECT blobId FROM notes WHERE mime IN ('application/javascript;env=backend', 'application/javascript;env=frontend')
UNION ALL
SELECT blobId FROM note_revisions WHERE mime IN ('application/javascript;env=backend', 'application/javascript;env=frontend')
SELECT blobId FROM revisions WHERE mime IN ('application/javascript;env=backend', 'application/javascript;env=frontend')
);`;
}

View File

@ -4,7 +4,7 @@ const build = require('./build');
const packageJson = require('../../package');
const {TRILIUM_DATA_DIR} = require('./data_dir');
const APP_DB_VERSION = 220;
const APP_DB_VERSION = 221;
const SYNC_VERSION = 30;
const CLIPPER_PROTOCOL_VERSION = "1.0";

View File

@ -22,7 +22,7 @@ module.exports = [
{ type: 'label', name: 'widget', isDangerous: true },
{ type: 'label', name: 'noteInfoWidgetDisabled' },
{ type: 'label', name: 'linkMapWidgetDisabled' },
{ type: 'label', name: 'noteRevisionsWidgetDisabled' },
{ type: 'label', name: 'revisionsWidgetDisabled' },
{ type: 'label', name: 'whatLinksHereWidgetDisabled' },
{ type: 'label', name: 'similarNotesWidgetDisabled' },
{ type: 'label', name: 'workspace' },

View File

@ -1,5 +1,5 @@
const log = require("./log");
const noteRevisionService = require("./note_revisions");
const revisionService = require("./revisions.js");
const becca = require("../becca/becca");
const cloningService = require("./cloning");
const branchService = require("./branches");
@ -17,8 +17,8 @@ const ACTION_HANDLERS = {
note.deleteNote(deleteId);
},
deleteNoteRevisions: (action, note) => {
noteRevisionService.eraseNoteRevisions(note.getNoteRevisions().map(rev => rev.noteRevisionId));
deleteRevisions: (action, note) => {
revisionService.eraseRevisions(note.getRevisions().map(rev => rev.revisionId));
},
deleteLabel: (action, note) => {
for (const label of note.getOwnedLabels(action.labelName)) {

View File

@ -9,7 +9,7 @@ const cls = require('./cls');
const entityChangesService = require('./entity_changes');
const optionsService = require('./options');
const BBranch = require('../becca/entities/bbranch');
const noteRevisionService = require('./note_revisions');
const revisionService = require('./revisions.js');
const becca = require("../becca/becca");
const utils = require("../services/utils");
const {sanitizeAttributeName} = require("./sanitize_attribute_name");
@ -221,7 +221,7 @@ class ConsistencyChecks {
WHERE attachments.parentId NOT IN (
SELECT noteId FROM notes
UNION ALL
SELECT noteRevisionId FROM note_revisions
SELECT revisionId FROM revisions
)
AND attachments.isDeleted = 0`,
({attachmentId, parentId}) => {
@ -461,19 +461,19 @@ class ConsistencyChecks {
}
this.findAndFixIssues(`
SELECT note_revisions.noteRevisionId
FROM note_revisions
SELECT revisions.revisionId
FROM revisions
LEFT JOIN blobs USING (blobId)
WHERE blobs.blobId IS NULL`,
({noteRevisionId}) => {
({revisionId}) => {
if (this.autoFix) {
noteRevisionService.eraseNoteRevisions([noteRevisionId]);
revisionService.eraseRevisions([revisionId]);
this.reloadNeeded = true;
logFix(`Note revision content '${noteRevisionId}' was set to erased since its content did not exist.`);
logFix(`Note revision content '${revisionId}' was set to erased since its content did not exist.`);
} else {
logError(`Note revision content '${noteRevisionId}' does not exist`);
logError(`Note revision content '${revisionId}' does not exist`);
}
});
@ -669,7 +669,7 @@ class ConsistencyChecks {
findEntityChangeIssues() {
this.runEntityChangeChecks("notes", "noteId");
this.runEntityChangeChecks("note_revisions", "noteRevisionId");
this.runEntityChangeChecks("revisions", "revisionId");
this.runEntityChangeChecks("attachments", "attachmentId");
this.runEntityChangeChecks("blobs", "blobId");
this.runEntityChangeChecks("branches", "branchId");
@ -767,7 +767,7 @@ class ConsistencyChecks {
return `${tableName}: ${count}`;
}
const tables = [ "notes", "note_revisions", "attachments", "branches", "attributes", "etapi_tokens" ];
const tables = [ "notes", "revisions", "attachments", "branches", "attributes", "etapi_tokens" ];
log.info(`Table counts: ${tables.map(tableName => getTableRowCount(tableName)).join(", ")}`);
}

View File

@ -148,7 +148,7 @@ function fillAllEntityChanges() {
fillEntityChanges("notes", "noteId");
fillEntityChanges("branches", "branchId");
fillEntityChanges("note_revisions", "noteRevisionId");
fillEntityChanges("revisions", "revisionId");
fillEntityChanges("attachments", "attachmentId");
fillEntityChanges("blobs", "blobId");
fillEntityChanges("attributes", "attributeId");

View File

@ -70,7 +70,7 @@ function updateImage(noteId, uploadBuffer, originalName) {
const note = becca.getNote(noteId);
note.saveNoteRevision();
note.saveRevision();
note.setLabel('originalFileName', originalName);

View File

@ -249,7 +249,7 @@ const DEFAULT_KEYBOARD_ACTIONS = [
scope: "window"
},
{
actionName: "showNoteRevisions",
actionName: "showRevisions",
defaultShortcuts: [],
description: "Shows Note Revisions dialog",
scope: "window"
@ -502,7 +502,7 @@ const DEFAULT_KEYBOARD_ACTIONS = [
scope: "text-detail"
},
{
actionName: "forceSaveNoteRevision",
actionName: "forceSaveRevision",
defaultShortcuts: [],
description: "Force creating / saving new note revision of the active note",
scope: "window"

View File

@ -8,7 +8,7 @@ const cls = require('../services/cls');
const protectedSessionService = require('../services/protected_session');
const log = require('../services/log');
const utils = require('../services/utils');
const noteRevisionService = require('../services/note_revisions');
const revisionService = require('./revisions.js');
const request = require('./request');
const path = require('path');
const url = require('url');
@ -315,7 +315,7 @@ function protectNote(note, protect) {
note.setContent(content, { forceSave: true });
}
noteRevisionService.protectNoteRevisions(note);
revisionService.protectRevisions(note);
for (const attachment of note.getAttachments()) {
if (protect !== attachment.isProtected) {
@ -666,24 +666,24 @@ function saveLinks(note, content) {
}
/** @param {BNote} note */
function saveNoteRevisionIfNeeded(note) {
function saveRevisionIfNeeded(note) {
// files and images are versioned separately
if (note.type === 'file' || note.type === 'image' || note.hasLabel('disableVersioning')) {
return;
}
const now = new Date();
const noteRevisionSnapshotTimeInterval = parseInt(optionService.getOption('noteRevisionSnapshotTimeInterval'));
const revisionSnapshotTimeInterval = parseInt(optionService.getOption('revisionSnapshotTimeInterval'));
const revisionCutoff = dateUtils.utcDateTimeStr(new Date(now.getTime() - noteRevisionSnapshotTimeInterval * 1000));
const revisionCutoff = dateUtils.utcDateTimeStr(new Date(now.getTime() - revisionSnapshotTimeInterval * 1000));
const existingNoteRevisionId = sql.getValue(
"SELECT noteRevisionId FROM note_revisions WHERE noteId = ? AND utcDateCreated >= ?", [note.noteId, revisionCutoff]);
const existingRevisionId = sql.getValue(
"SELECT revisionId FROM revisions WHERE noteId = ? AND utcDateCreated >= ?", [note.noteId, revisionCutoff]);
const msSinceDateCreated = now.getTime() - dateUtils.parseDateTime(note.utcDateCreated).getTime();
if (!existingNoteRevisionId && msSinceDateCreated >= noteRevisionSnapshotTimeInterval * 1000) {
note.saveNoteRevision();
if (!existingRevisionId && msSinceDateCreated >= revisionSnapshotTimeInterval * 1000) {
note.saveRevision();
}
}
@ -694,7 +694,7 @@ function updateNoteData(noteId, content) {
throw new Error(`Note '${noteId}' is not available for change!`);
}
saveNoteRevisionIfNeeded(note);
saveRevisionIfNeeded(note);
const { forceFrontendReload, content: newContent } = saveLinks(note, content);
@ -839,10 +839,10 @@ function eraseNotes(noteIdsToErase) {
eraseAttributes(attributeIdsToErase);
const noteRevisionIdsToErase = sql.getManyRows(`SELECT noteRevisionId FROM note_revisions WHERE noteId IN (???)`, noteIdsToErase)
.map(row => row.noteRevisionId);
const revisionIdsToErase = sql.getManyRows(`SELECT revisionId FROM revisions WHERE noteId IN (???)`, noteIdsToErase)
.map(row => row.revisionId);
noteRevisionService.eraseNoteRevisions(noteRevisionIdsToErase);
revisionService.eraseRevisions(revisionIdsToErase);
log.info(`Erased notes: ${JSON.stringify(noteIdsToErase)}`);
}
@ -899,10 +899,10 @@ function eraseUnusedBlobs() {
FROM blobs
LEFT JOIN notes ON notes.blobId = blobs.blobId
LEFT JOIN attachments ON attachments.blobId = blobs.blobId
LEFT JOIN note_revisions ON note_revisions.blobId = blobs.blobId
LEFT JOIN revisions ON revisions.blobId = blobs.blobId
WHERE notes.noteId IS NULL
AND attachments.attachmentId IS NULL
AND note_revisions.noteRevisionId IS NULL`);
AND revisions.revisionId IS NULL`);
if (unusedBlobIds.length === 0) {
return;
@ -1136,7 +1136,7 @@ module.exports = {
eraseDeletedNotesNow,
eraseUnusedAttachmentsNow,
eraseNotesWithDeleteId,
saveNoteRevisionIfNeeded,
saveRevisionIfNeeded,
downloadImages,
asyncPostProcessContent
};

View File

@ -44,7 +44,7 @@ function initNotSyncedOptions(initialized, opts = {}) {
}
const defaultOptions = [
{ name: 'noteRevisionSnapshotTimeInterval', value: '600', isSynced: true },
{ name: 'revisionSnapshotTimeInterval', value: '600', isSynced: true },
{ name: 'protectedSessionTimeout', value: '600', isSynced: true },
{ name: 'zoomFactor', value: process.platform === "win32" ? '0.9' : '1.0', isSynced: false },
{ name: 'overrideThemeFonts', value: 'false', isSynced: false },

View File

@ -7,12 +7,12 @@ const protectedSessionService = require("./protected_session");
/**
* @param {BNote} note
*/
function protectNoteRevisions(note) {
function protectRevisions(note) {
if (!protectedSessionService.isProtectedSessionAvailable()) {
throw new Error(`Cannot (un)protect revisions of note '${note.noteId}' without active protected session`);
}
for (const revision of note.getNoteRevisions()) {
for (const revision of note.getRevisions()) {
if (note.isProtected === revision.isProtected) {
continue;
}
@ -25,25 +25,25 @@ function protectNoteRevisions(note) {
// this will force de/encryption
revision.setContent(content, {forceSave: true});
} catch (e) {
log.error(`Could not un/protect note revision '${revision.noteRevisionId}'`);
log.error(`Could not un/protect note revision '${revision.revisionId}'`);
throw e;
}
}
}
function eraseNoteRevisions(noteRevisionIdsToErase) {
if (noteRevisionIdsToErase.length === 0) {
function eraseRevisions(revisionIdsToErase) {
if (revisionIdsToErase.length === 0) {
return;
}
log.info(`Removing note revisions: ${JSON.stringify(noteRevisionIdsToErase)}`);
log.info(`Removing note revisions: ${JSON.stringify(revisionIdsToErase)}`);
sql.executeMany(`DELETE FROM note_revisions WHERE noteRevisionId IN (???)`, noteRevisionIdsToErase);
sql.executeMany(`UPDATE entity_changes SET isErased = 1 WHERE entityName = 'note_revisions' AND entityId IN (???)`, noteRevisionIdsToErase);
sql.executeMany(`DELETE FROM revisions WHERE revisionId IN (???)`, revisionIdsToErase);
sql.executeMany(`UPDATE entity_changes SET isErased = 1 WHERE entityName = 'revisions' AND entityId IN (???)`, revisionIdsToErase);
}
module.exports = {
protectNoteRevisions,
eraseNoteRevisions
protectRevisions,
eraseRevisions
};

View File

@ -116,16 +116,16 @@ function loadNeededInfoFromDatabase() {
becca.notes[noteId].noteSize = length;
}
const noteRevisionContentLengths = sql.getRows(`
const revisionContentLengths = sql.getRows(`
SELECT
noteId,
LENGTH(content) AS length
FROM notes
JOIN note_revisions USING(noteId)
JOIN revisions USING(noteId)
JOIN blobs USING(blobId)
WHERE notes.isDeleted = 0`);
for (const {noteId, length} of noteRevisionContentLengths) {
for (const {noteId, length} of revisionContentLengths) {
if (!(noteId in becca.notes)) {
log.error(`Note ${noteId} not found in becca.`);
continue;

View File

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

View File

@ -127,10 +127,10 @@ function fillInAdditionalProperties(entityChange) {
entityChange.entity.title = protectedSessionService.decryptString(entityChange.entity.title);
}
}
} else if (entityChange.entityName === 'note_revisions') {
} else if (entityChange.entityName === 'revisions') {
entityChange.noteId = sql.getValue(`SELECT noteId
FROM note_revisions
WHERE noteRevisionId = ?`, [entityChange.entityId]);
FROM revisions
WHERE revisionId = ?`, [entityChange.entityId]);
} else if (entityChange.entityName === 'note_reordering') {
entityChange.positions = {};
@ -165,7 +165,7 @@ const ORDERING = {
"branches": 2,
"blobs": 0,
"note_reordering": 2,
"note_revisions": 2,
"revisions": 2,
"attachments": 3,
"notes": 1,
"options": 0

View File

@ -82,7 +82,7 @@ async function start() {
isInheritable: Math.random() > 0.1 // 10% are inheritable
});
note.saveNoteRevision();
note.saveRevision();
notes.push(note.noteId);
}