mirror of
https://github.com/zadam/trilium.git
synced 2025-11-02 03:16:11 +01:00
Add revision number limit
This commit is contained in:
@@ -3,6 +3,8 @@
|
||||
import protectedSessionService from "../../services/protected_session.js";
|
||||
import log from "../../services/log.js";
|
||||
import sql from "../../services/sql.js";
|
||||
import optionService from "../../services/options.js";
|
||||
import eraseService from "../../services/erase.js";
|
||||
import utils from "../../services/utils.js";
|
||||
import dateUtils from "../../services/date_utils.js";
|
||||
import AbstractBeccaEntity from "./abstract_becca_entity.js";
|
||||
@@ -68,7 +70,7 @@ class BNote extends AbstractBeccaEntity<BNote> {
|
||||
/** set during the deletion operation, before it is completed (removed from becca completely). */
|
||||
isBeingDeleted!: boolean;
|
||||
isDecrypted!: boolean;
|
||||
|
||||
|
||||
ownedAttributes!: BAttribute[];
|
||||
parentBranches!: BBranch[];
|
||||
parents!: BNote[];
|
||||
@@ -455,8 +457,8 @@ class BNote extends AbstractBeccaEntity<BNote> {
|
||||
|
||||
return this.getAttributes().find(
|
||||
attr => attr.name.toLowerCase() === name
|
||||
&& (!value || attr.value.toLowerCase() === value)
|
||||
&& attr.type === type);
|
||||
&& (!value || attr.value.toLowerCase() === value)
|
||||
&& attr.type === type);
|
||||
}
|
||||
|
||||
getRelationTarget(name: string) {
|
||||
@@ -1612,10 +1614,27 @@ class BNote extends AbstractBeccaEntity<BNote> {
|
||||
|
||||
revision.setContent(noteContent);
|
||||
|
||||
this.eraseExcessRevisions()
|
||||
return revision;
|
||||
});
|
||||
}
|
||||
|
||||
// Limit the number of Snapshots to revisionSnapshotNumberLimit
|
||||
// Delete older Snapshots that exceed the limit
|
||||
eraseExcessRevisions() {
|
||||
const revisionSnapshotNumberLimit = parseInt(optionService.getOption('revisionSnapshotNumberLimit'));
|
||||
if (revisionSnapshotNumberLimit >= 0) {
|
||||
const revisions = this.getRevisions();
|
||||
if (revisions.length - revisionSnapshotNumberLimit > 0) {
|
||||
const revisionIds = revisions
|
||||
.slice(0, revisions.length - revisionSnapshotNumberLimit)
|
||||
.map(revision => revision.revisionId)
|
||||
.filter((id): id is string => id !== undefined);
|
||||
eraseService.eraseRevisions(revisionIds);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param matchBy - choose by which property we detect if to update an existing attachment.
|
||||
* Supported values are either 'attachmentId' (default) or 'title'
|
||||
|
||||
@@ -23,6 +23,7 @@ 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 RevisionsSnapshotIntervalOptions from "./options/other/revisions_snapshot_interval.js";
|
||||
import RevisionsSnapshotLimitOptions from "./options/other/revisions_snapshot_limit.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";
|
||||
@@ -88,6 +89,7 @@ const CONTENT_WIDGETS = {
|
||||
NoteErasureTimeoutOptions,
|
||||
AttachmentErasureTimeoutOptions,
|
||||
RevisionsSnapshotIntervalOptions,
|
||||
RevisionsSnapshotLimitOptions,
|
||||
NetworkConnectionsOptions
|
||||
],
|
||||
_optionsAdvanced: [
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
import OptionsWidget from "../options_widget.js";
|
||||
import { t } from "../../../../services/i18n.js";
|
||||
import server from "../../../../services/server.js";
|
||||
import toastService from "../../../../services/toast.js";
|
||||
|
||||
const TPL = `
|
||||
<div class="options-section">
|
||||
<h4>${t("revisions_snapshot_limit.note_revisions_snapshot_limit_title")}</h4>
|
||||
|
||||
<p>${t("revisions_snapshot_limit.note_revisions_snapshot_limit_description")}</p>
|
||||
|
||||
<div class="form-group">
|
||||
<label>${t("revisions_snapshot_limit.snapshot_number_limit_label")}</label>
|
||||
<input class="revision-snapshot-number-limit form-control options-number-input" type="number" min="-1">
|
||||
</div>
|
||||
|
||||
<button class="erase-excess-revision-snapshots-now-button btn btn-sm" style="padding: 0 10px">
|
||||
${t('revisions_snapshot_limit.erase_excess_revision_snapshots')}</button>
|
||||
</div>`;
|
||||
|
||||
export default class RevisionsSnapshotLimitOptions extends OptionsWidget {
|
||||
doRender() {
|
||||
this.$widget = $(TPL);
|
||||
this.$revisionsNumberLimit = this.$widget.find(".revision-snapshot-number-limit");
|
||||
this.$revisionsNumberLimit.on('change', () => {
|
||||
let revisionSnapshotNumberLimit = this.$revisionsNumberLimit.val();
|
||||
if (!isNaN(revisionSnapshotNumberLimit) && revisionSnapshotNumberLimit >= -1) {
|
||||
this.updateOption('revisionSnapshotNumberLimit', revisionSnapshotNumberLimit)
|
||||
}
|
||||
});
|
||||
this.$eraseExcessRevisionSnapshotsButton = this.$widget.find(".erase-excess-revision-snapshots-now-button");
|
||||
this.$eraseExcessRevisionSnapshotsButton.on('click', () => {
|
||||
server.post('revisions/erase-all-excess-revisions').then(() => {
|
||||
toastService.showMessage(t("revisions_snapshot_limit.erase_excess_revision_snapshots_prompt"));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async optionsLoaded(options) {
|
||||
this.$revisionsNumberLimit.val(options.revisionSnapshotNumberLimit);
|
||||
}
|
||||
}
|
||||
@@ -1064,7 +1064,14 @@
|
||||
"revisions_snapshot_interval": {
|
||||
"note_revisions_snapshot_interval_title": "Note Revisions Snapshot Interval",
|
||||
"note_revisions_snapshot_description": "Note revision snapshot time interval is time in seconds after which a new note revision will be created for the note. See <a href=\"https://triliumnext.github.io/Docs/Wiki/note-revisions.html\" class=\"external\">wiki</a> for more info.",
|
||||
"snapshot_time_interval_label": "Note revision snapshot time interval (in seconds)"
|
||||
"snapshot_time_interval_label": "Note revision snapshot time interval (in seconds):"
|
||||
},
|
||||
"revisions_snapshot_limit": {
|
||||
"note_revisions_snapshot_limit_title": "Note Revisions Snapshot Limit",
|
||||
"note_revisions_snapshot_limit_description": "The note revision snapshot number limit refers to the maximum number of revisions that can be saved for each note. Where -1 means no limit, 0 means delete all revisions.",
|
||||
"snapshot_number_limit_label": "Note revision snapshot number limit:",
|
||||
"erase_excess_revision_snapshots": "Erase excess revision snapshots now",
|
||||
"erase_excess_revision_snapshots_prompt": "Excess revision snapshots have been erased."
|
||||
},
|
||||
"search_engine": {
|
||||
"title": "Search Engine",
|
||||
|
||||
@@ -11,6 +11,7 @@ const ALLOWED_OPTIONS = new Set([
|
||||
'eraseEntitiesAfterTimeInSeconds',
|
||||
'protectedSessionTimeout',
|
||||
'revisionSnapshotTimeInterval',
|
||||
'revisionSnapshotNumberLimit',
|
||||
'zoomFactor',
|
||||
'theme',
|
||||
'syncServerHost',
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
import beccaService from "../../becca/becca_service.js";
|
||||
import revisionService from "../../services/revisions.js";
|
||||
import optionService from "../../services/options.js";
|
||||
import utils from "../../services/utils.js";
|
||||
import sql from "../../services/sql.js";
|
||||
import cls from "../../services/cls.js";
|
||||
@@ -112,6 +113,13 @@ function eraseRevision(req: Request) {
|
||||
eraseService.eraseRevisions([req.params.revisionId]);
|
||||
}
|
||||
|
||||
function eraseAllExcessRevisions() {
|
||||
let allNoteIds = sql.getRows('SELECT noteId FROM notes') as { noteId: string }[];
|
||||
allNoteIds.forEach(row => {
|
||||
becca.getNote(row.noteId)?.eraseExcessRevisions()
|
||||
});
|
||||
}
|
||||
|
||||
function restoreRevision(req: Request) {
|
||||
const revision = becca.getRevision(req.params.revisionId);
|
||||
|
||||
@@ -211,6 +219,7 @@ export default {
|
||||
downloadRevision,
|
||||
getEditedNotesOnDate,
|
||||
eraseAllRevisions,
|
||||
eraseAllExcessRevisions,
|
||||
eraseRevision,
|
||||
restoreRevision
|
||||
};
|
||||
|
||||
@@ -184,6 +184,7 @@ function register(app: express.Application) {
|
||||
|
||||
apiRoute(GET, '/api/notes/:noteId/revisions', revisionsApiRoute.getRevisions);
|
||||
apiRoute(DEL, '/api/notes/:noteId/revisions', revisionsApiRoute.eraseAllRevisions);
|
||||
apiRoute(PST, '/api/revisions/erase-all-excess-revisions', revisionsApiRoute.eraseAllExcessRevisions);
|
||||
apiRoute(GET, '/api/revisions/:revisionId', revisionsApiRoute.getRevision);
|
||||
apiRoute(GET, '/api/revisions/:revisionId/blob', revisionsApiRoute.getRevisionBlob);
|
||||
apiRoute(DEL, '/api/revisions/:revisionId', revisionsApiRoute.eraseRevision);
|
||||
|
||||
@@ -49,6 +49,7 @@ async function initNotSyncedOptions(initialized: boolean, theme: string, opts: N
|
||||
|
||||
const defaultOptions: DefaultOption[] = [
|
||||
{ name: 'revisionSnapshotTimeInterval', value: '600', isSynced: true },
|
||||
{ name: 'revisionSnapshotNumberLimit', value: '-1', 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 },
|
||||
|
||||
Reference in New Issue
Block a user