mirror of
https://github.com/zadam/trilium.git
synced 2025-10-29 01:06:36 +01:00
Compare commits
22 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1f3d73b9fd | ||
|
|
bdfd760b9d | ||
|
|
7133e60267 | ||
|
|
fc4edf4aa7 | ||
|
|
eaf93a70cd | ||
|
|
b093569ec5 | ||
|
|
4633c68a0c | ||
|
|
33571e0ef3 | ||
|
|
31876d2cf9 | ||
|
|
81c6043cb6 | ||
|
|
1982d054ef | ||
|
|
e56979c482 | ||
|
|
58555b3660 | ||
|
|
b7b1324dd0 | ||
|
|
e318acc977 | ||
|
|
8ae82f5b69 | ||
|
|
26442f418a | ||
|
|
23a432e7d8 | ||
|
|
984ecaf99c | ||
|
|
21b73a86b2 | ||
|
|
7d8277699c | ||
|
|
928ed7a034 |
@@ -1,5 +1,5 @@
|
||||
[General]
|
||||
# Instance name can be used to distinguish between different instances
|
||||
# Instance name can be used to distinguish between different instances using backend api.getInstanceName()
|
||||
instanceName=
|
||||
|
||||
# set to true to allow using Trilium without authentication (makes sense for server build only, desktop build doesn't need password)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* !!!!!! TRILIUM CUSTOM CHANGES !!!!!! */
|
||||
|
||||
.ck-widget__selection-handle, .ck-widget__type-around { /* gets rid of triangles: https://github.com/zadam/trilium/issues/1129 */
|
||||
.printed-content .ck-widget__selection-handle, .printed-content .ck-widget__type-around { /* gets rid of triangles: https://github.com/zadam/trilium/issues/1129 */
|
||||
display: none;
|
||||
}
|
||||
|
||||
|
||||
8
package-lock.json
generated
8
package-lock.json
generated
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "trilium",
|
||||
"version": "0.45.4",
|
||||
"version": "0.45.6",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
@@ -2654,9 +2654,9 @@
|
||||
}
|
||||
},
|
||||
"electron": {
|
||||
"version": "9.3.4",
|
||||
"resolved": "https://registry.npmjs.org/electron/-/electron-9.3.4.tgz",
|
||||
"integrity": "sha512-OHP8qMKgW8D8GtH+altB22WJw/lBOyyVdoz5e8D0/iPBmJU3Jm93vO4z4Eh/9DvdSXlH8bMHUCMLL9PVW6f+tw==",
|
||||
"version": "9.3.5",
|
||||
"resolved": "https://registry.npmjs.org/electron/-/electron-9.3.5.tgz",
|
||||
"integrity": "sha512-EPmDsp7sO0UPtw7nLD1ufse/nBskP+ifXzBgUg9psCUlapkzuwYi6pmLAzKLW/bVjwgyUKwh1OKWILWfOeLGcQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@electron/get": "^1.0.1",
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"name": "trilium",
|
||||
"productName": "Trilium Notes",
|
||||
"description": "Trilium Notes",
|
||||
"version": "0.45.5",
|
||||
"version": "0.45.7",
|
||||
"license": "AGPL-3.0-only",
|
||||
"main": "electron.js",
|
||||
"bin": {
|
||||
@@ -77,7 +77,7 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"cross-env": "7.0.2",
|
||||
"electron": "9.3.4",
|
||||
"electron": "9.3.5",
|
||||
"electron-builder": "22.9.1",
|
||||
"electron-packager": "15.1.0",
|
||||
"electron-rebuild": "2.3.2",
|
||||
|
||||
@@ -51,6 +51,12 @@ const TPL = `
|
||||
<label for="erase-notes-after-time-in-seconds">Erase notes after X seconds</label>
|
||||
<input class="form-control" id="erase-notes-after-time-in-seconds" type="number" min="0">
|
||||
</div>
|
||||
|
||||
<p>You can also trigger erasing manually:</p>
|
||||
|
||||
<button id="erase-deleted-notes-now-button" class="btn">Erase deleted notes now</button>
|
||||
|
||||
<br/><br/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
@@ -117,6 +123,13 @@ export default class ProtectedSessionOptions {
|
||||
return false;
|
||||
});
|
||||
|
||||
this.$eraseDeletedNotesButton = $("#erase-deleted-notes-now-button");
|
||||
this.$eraseDeletedNotesButton.on('click', () => {
|
||||
server.post('notes/erase-deleted-notes-now').then(() => {
|
||||
toastService.showMessage("Deleted notes have been erased.");
|
||||
});
|
||||
});
|
||||
|
||||
this.$protectedSessionTimeout = $("#protected-session-timeout-in-seconds");
|
||||
|
||||
this.$protectedSessionTimeout.on('change', () => {
|
||||
|
||||
@@ -75,14 +75,16 @@ class NoteShort {
|
||||
this.parentToBranch[parentNoteId] = branchId;
|
||||
}
|
||||
|
||||
addChild(childNoteId, branchId) {
|
||||
addChild(childNoteId, branchId, sort = true) {
|
||||
if (!this.children.includes(childNoteId)) {
|
||||
this.children.push(childNoteId);
|
||||
}
|
||||
|
||||
this.childToBranch[childNoteId] = branchId;
|
||||
|
||||
this.sortChildren();
|
||||
if (sort) {
|
||||
this.sortChildren();
|
||||
}
|
||||
}
|
||||
|
||||
sortChildren() {
|
||||
|
||||
@@ -130,7 +130,7 @@ function linkContextMenu(e) {
|
||||
appContext.tabManager.openTabWithNote(notePath);
|
||||
}
|
||||
else if (command === 'openNoteInNewWindow') {
|
||||
appContext.openInNewWindow(notePath);
|
||||
appContext.triggerCommand('openInWindow', {notePath});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -8,8 +8,8 @@ async function syncNow() {
|
||||
toastService.showMessage("Sync finished successfully.");
|
||||
}
|
||||
else {
|
||||
if (result.message.length > 100) {
|
||||
result.message = result.message.substr(0, 100);
|
||||
if (result.message.length > 200) {
|
||||
result.message = result.message.substr(0, 200) + "...";
|
||||
}
|
||||
|
||||
toastService.showError("Sync failed: " + result.message);
|
||||
|
||||
@@ -87,6 +87,8 @@ class TreeCache {
|
||||
const branchRows = resp.branches;
|
||||
const attributeRows = resp.attributes;
|
||||
|
||||
const noteIdsToSort = new Set();
|
||||
|
||||
for (const noteRow of noteRows) {
|
||||
const {noteId} = noteRow;
|
||||
|
||||
@@ -153,7 +155,9 @@ class TreeCache {
|
||||
const parentNote = this.notes[branch.parentNoteId];
|
||||
|
||||
if (parentNote) {
|
||||
parentNote.addChild(branch.noteId, branch.branchId);
|
||||
parentNote.addChild(branch.noteId, branch.branchId, false);
|
||||
|
||||
noteIdsToSort.add(parentNote.noteId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -178,6 +182,11 @@ class TreeCache {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// sort all of them at once, this avoids repeated sorts (#1480)
|
||||
for (const noteId of noteIdsToSort) {
|
||||
this.notes[noteId].sortChildren();
|
||||
}
|
||||
}
|
||||
|
||||
async reloadNotes(noteIds) {
|
||||
|
||||
@@ -2,9 +2,9 @@ import treeService from './tree.js';
|
||||
import treeCache from "./tree_cache.js";
|
||||
import hoistedNoteService from './hoisted_note.js';
|
||||
import clipboard from './clipboard.js';
|
||||
import protectedSessionHolder from "./protected_session_holder.js";
|
||||
import noteCreateService from "./note_create.js";
|
||||
import contextMenu from "./context_menu.js";
|
||||
import appContext from "./app_context.js";
|
||||
|
||||
class TreeContextMenu {
|
||||
/**
|
||||
@@ -95,7 +95,7 @@ class TreeContextMenu {
|
||||
enabled: !clipboard.isClipboardEmpty() && notSearch && noSelectedNotes },
|
||||
{ title: 'Paste after', command: "pasteNotesAfterFromClipboard", uiIcon: "paste",
|
||||
enabled: !clipboard.isClipboardEmpty() && isNotRoot && !isHoisted && parentNotSearch && noSelectedNotes },
|
||||
{ title: "Duplicate subtree(s) here", command: "duplicateSubtree", uiIcon: "empty",
|
||||
{ title: `Duplicate subtree <kbd data-command="duplicateSubtree">`, command: "duplicateSubtree", uiIcon: "empty",
|
||||
enabled: parentNotSearch && isNotRoot && !isHoisted },
|
||||
{ title: "----" },
|
||||
{ title: "Export", command: "exportNote", uiIcon: "empty",
|
||||
@@ -110,14 +110,7 @@ class TreeContextMenu {
|
||||
const notePath = treeService.getNotePath(this.node);
|
||||
|
||||
if (command === 'openInTab') {
|
||||
|
||||
const start = Date.now();
|
||||
|
||||
await this.node.load(true);
|
||||
|
||||
console.log("Reload took", Date.now() - start, "ms");
|
||||
|
||||
// appContext.tabManager.openTabWithNote(notePath);
|
||||
appContext.tabManager.openTabWithNote(notePath);
|
||||
}
|
||||
else if (command === "insertNoteAfter") {
|
||||
const parentNoteId = this.node.data.parentNoteId;
|
||||
|
||||
@@ -214,7 +214,8 @@ export default class AttributeListWidget extends TabAwareWidget {
|
||||
noteId: attribute.noteId,
|
||||
type: attribute.type,
|
||||
name: attribute.name,
|
||||
value: attribute.value
|
||||
value: attribute.value,
|
||||
isInheritable: attribute.isInheritable
|
||||
},
|
||||
isOwned: false,
|
||||
x: e.pageX,
|
||||
|
||||
@@ -248,13 +248,22 @@ export default class NoteDetailWidget extends TabAwareWidget {
|
||||
|
||||
this.$widget.find('.note-detail-printable:visible').printThis({
|
||||
header: $("<h2>").text(this.note && this.note.title).prop('outerHTML'),
|
||||
footer: "<script>document.body.className += ' ck-content';</script>",
|
||||
footer: `
|
||||
<script src="libraries/katex/katex.min.js"></script>
|
||||
<script src="libraries/katex/auto-render.min.js"></script>
|
||||
<script>
|
||||
document.body.className += ' ck-content printed-content';
|
||||
|
||||
renderMathInElement(document.body, {});
|
||||
</script>
|
||||
`,
|
||||
importCSS: false,
|
||||
loadCSS: [
|
||||
"libraries/codemirror/codemirror.css",
|
||||
"libraries/ckeditor/ckeditor-content.css",
|
||||
"libraries/ckeditor/ckeditor-content.css",
|
||||
"libraries/bootstrap/css/bootstrap.min.css",
|
||||
"libraries/katex/katex.min.css",
|
||||
"stylesheets/print.css",
|
||||
"stylesheets/relation_map.css",
|
||||
"stylesheets/themes.css"
|
||||
|
||||
@@ -143,6 +143,11 @@ body {
|
||||
--ck-color-dropdown-panel-background: var(--accented-background-color);
|
||||
--ck-color-dropdown-panel-border: var(--main-border-color);
|
||||
|
||||
/* -- Overrides the default .ck-splitbutton class colors. ----------------------------------- */
|
||||
|
||||
--ck-color-split-button-hover-background: var(--ck-color-button-default-hover-background);
|
||||
--ck-color-split-button-hover-border: var(--main-border-color);
|
||||
|
||||
/* -- Overrides the default .ck-input class colors. ----------------------------------------- */
|
||||
|
||||
--ck-color-input-background: var(--accented-background-color);
|
||||
@@ -199,6 +204,9 @@ body {
|
||||
--ck-color-engine-placeholder-text: var(--muted-text-color);
|
||||
|
||||
--ck-z-modal: 10000;
|
||||
|
||||
--ck-color-widget-type-around-button: var(--main-border-color);
|
||||
--ck-color-widget-type-around-button-hover: var(--main-border-color);
|
||||
}
|
||||
|
||||
body {
|
||||
|
||||
@@ -3,15 +3,33 @@
|
||||
const sql = require('../../services/sql');
|
||||
|
||||
function getRelations(noteIds) {
|
||||
return (sql.getManyRows(`
|
||||
SELECT noteId, name, value AS targetNoteId
|
||||
FROM attributes
|
||||
WHERE (noteId IN (???) OR value IN (???))
|
||||
AND type = 'relation'
|
||||
AND isDeleted = 0
|
||||
AND noteId != ''
|
||||
AND value != ''
|
||||
`, Array.from(noteIds)));
|
||||
noteIds = Array.from(noteIds);
|
||||
|
||||
return [
|
||||
// first read all non-image relations
|
||||
...sql.getManyRows(`
|
||||
SELECT noteId, name, value AS targetNoteId
|
||||
FROM attributes
|
||||
WHERE (noteId IN (???) OR value IN (???))
|
||||
AND type = 'relation'
|
||||
AND name != 'imageLink'
|
||||
AND isDeleted = 0
|
||||
AND noteId != ''
|
||||
AND value != ''`, noteIds),
|
||||
// ... then read only imageLink relations which are not connecting parent and child
|
||||
// this is done to not show image links in the trivial case where they are direct children of the note to which they are included. Same heuristic as in note tree
|
||||
...sql.getManyRows(`
|
||||
SELECT rel.noteId, rel.name, rel.value AS targetNoteId
|
||||
FROM attributes AS rel
|
||||
LEFT JOIN branches ON branches.parentNoteId = rel.noteId AND branches.noteId = rel.value AND branches.isDeleted = 0
|
||||
WHERE (rel.noteId IN (???) OR rel.value IN (???))
|
||||
AND rel.type = 'relation'
|
||||
AND rel.name = 'imageLink'
|
||||
AND rel.isDeleted = 0
|
||||
AND rel.noteId != ''
|
||||
AND rel.value != ''
|
||||
AND branches.branchId IS NULL`, noteIds)
|
||||
];
|
||||
}
|
||||
|
||||
function getLinkMap(req) {
|
||||
|
||||
@@ -193,6 +193,10 @@ function duplicateSubtree(req) {
|
||||
return noteService.duplicateSubtree(noteId, parentNoteId);
|
||||
}
|
||||
|
||||
function eraseDeletedNotesNow() {
|
||||
noteService.eraseDeletedNotesNow();
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
getNote,
|
||||
updateNote,
|
||||
@@ -204,5 +208,6 @@ module.exports = {
|
||||
setNoteTypeMime,
|
||||
getRelationMap,
|
||||
changeTitle,
|
||||
duplicateSubtree
|
||||
duplicateSubtree,
|
||||
eraseDeletedNotesNow
|
||||
};
|
||||
|
||||
@@ -38,6 +38,8 @@ function saveSyncSeed(req) {
|
||||
}]
|
||||
}
|
||||
|
||||
log.info("Saved sync seed.");
|
||||
|
||||
sqlInit.createDatabaseForSync(options);
|
||||
}
|
||||
|
||||
|
||||
@@ -57,7 +57,7 @@ function getTree(req) {
|
||||
const noteIds = sql.getColumn(`
|
||||
WITH RECURSIVE
|
||||
treeWithDescendants(noteId, isExpanded) AS (
|
||||
SELECT noteId, 1 FROM branches WHERE parentNoteId = ? AND isDeleted = 0
|
||||
SELECT noteId, isExpanded FROM branches WHERE parentNoteId = ? AND isDeleted = 0
|
||||
UNION
|
||||
SELECT branches.noteId, branches.isExpanded FROM branches
|
||||
JOIN treeWithDescendants ON branches.parentNoteId = treeWithDescendants.noteId
|
||||
|
||||
@@ -153,6 +153,7 @@ function register(app) {
|
||||
route(GET, '/api/notes/:noteId/revisions/:noteRevisionId/download', [auth.checkApiAuthOrElectron], noteRevisionsApiRoute.downloadNoteRevision);
|
||||
apiRoute(PUT, '/api/notes/:noteId/restore-revision/:noteRevisionId', noteRevisionsApiRoute.restoreNoteRevision);
|
||||
apiRoute(POST, '/api/notes/relation-map', notesApiRoute.getRelationMap);
|
||||
apiRoute(POST, '/api/notes/erase-deleted-notes-now', notesApiRoute.eraseDeletedNotesNow);
|
||||
apiRoute(PUT, '/api/notes/:noteId/change-title', notesApiRoute.changeTitle);
|
||||
apiRoute(POST, '/api/notes/:noteId/duplicate/:parentNoteId', notesApiRoute.duplicateSubtree);
|
||||
|
||||
|
||||
@@ -1 +1 @@
|
||||
module.exports = { buildDate:"2020-11-20T22:50:10+01:00", buildRevision: "e5fa1e0ed555c1c2cb4a14c426d7091d62b5beea" };
|
||||
module.exports = { buildDate:"2020-12-22T20:21:15+01:00", buildRevision: "bdfd760b9d428dc29c45a0e016d12a25af220043" };
|
||||
|
||||
@@ -650,7 +650,7 @@ class ConsistencyChecks {
|
||||
// root branch should always be expanded
|
||||
sql.execute("UPDATE branches SET isExpanded = 1 WHERE branchId = 'root'");
|
||||
|
||||
if (this.unrecoveredConsistencyErrors) {
|
||||
if (!this.unrecoveredConsistencyErrors) {
|
||||
// we run this only if basic checks passed since this assumes basic data consistency
|
||||
|
||||
this.checkTreeCycles();
|
||||
|
||||
@@ -52,6 +52,10 @@ function encrypt(key, plainText, ivLength = 13) {
|
||||
}
|
||||
|
||||
function decrypt(key, cipherText, ivLength = 13) {
|
||||
if (cipherText === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!key) {
|
||||
return "[protected]";
|
||||
}
|
||||
@@ -93,6 +97,10 @@ function decrypt(key, cipherText, ivLength = 13) {
|
||||
function decryptString(dataKey, cipherText) {
|
||||
const buffer = decrypt(dataKey, cipherText);
|
||||
|
||||
if (buffer === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const str = buffer.toString('utf-8');
|
||||
|
||||
if (str === 'false') {
|
||||
@@ -108,4 +116,4 @@ module.exports = {
|
||||
encrypt,
|
||||
decrypt,
|
||||
decryptString
|
||||
};
|
||||
};
|
||||
|
||||
@@ -70,6 +70,10 @@ eventService.subscribe(eventService.ENTITY_CREATED, ({ entityName, entity }) =>
|
||||
if (templateNoteContent) {
|
||||
note.setContent(templateNoteContent);
|
||||
}
|
||||
|
||||
note.type = templateNote.type;
|
||||
note.mime = templateNote.mime;
|
||||
note.save();
|
||||
}
|
||||
|
||||
noteService.duplicateSubtreeWithoutRoot(templateNote.noteId, note.noteId);
|
||||
@@ -90,10 +94,10 @@ eventService.subscribe(eventService.CHILD_NOTE_CREATED, ({ parentNote, childNote
|
||||
function processInverseRelations(entityName, entity, handler) {
|
||||
if (entityName === 'attributes' && entity.type === 'relation') {
|
||||
const note = entity.getNote();
|
||||
const attributes = (note.getOwnedAttributes(entity.name)).filter(relation => relation.type === 'relation-definition');
|
||||
const relDefinitions = note.getLabels('relation:' + entity.name);
|
||||
|
||||
for (const attribute of attributes) {
|
||||
const definition = attribute.value;
|
||||
for (const relDefinition of relDefinitions) {
|
||||
const definition = relDefinition.getDefinition();
|
||||
|
||||
if (definition.inverseRelation && definition.inverseRelation.trim()) {
|
||||
const targetNote = entity.getTargetNote();
|
||||
|
||||
@@ -158,7 +158,7 @@ async function importZip(taskContext, fileBuffer, importRootNote) {
|
||||
}
|
||||
|
||||
if (taskContext.data.safeImport && attributeService.isAttributeDangerous(attr.type, attr.name)) {
|
||||
attr.name = 'disabled-' + attr.name;
|
||||
attr.name = 'disabled:' + attr.name;
|
||||
}
|
||||
|
||||
attributes.push(attr);
|
||||
|
||||
@@ -184,6 +184,12 @@ const DEFAULT_KEYBOARD_ACTIONS = [
|
||||
description: "Add note above to the selection",
|
||||
scope: "note-tree"
|
||||
},
|
||||
{
|
||||
actionName: "duplicateSubtree",
|
||||
defaultShortcuts: [],
|
||||
description: "Duplicate subtree",
|
||||
scope: "note-tree"
|
||||
},
|
||||
|
||||
|
||||
{
|
||||
@@ -307,6 +313,12 @@ const DEFAULT_KEYBOARD_ACTIONS = [
|
||||
description: "Cuts the selection from the current note and creates subnote with the selected text",
|
||||
scope: "text-detail"
|
||||
},
|
||||
{
|
||||
actionName: "addIncludeNoteToText",
|
||||
defaultShortcuts: [],
|
||||
description: "Opens the dialog to include a note",
|
||||
scope: "text-detail"
|
||||
},
|
||||
|
||||
{
|
||||
separator: "Attributes (labels & relations)"
|
||||
|
||||
@@ -338,7 +338,7 @@ class Note {
|
||||
|
||||
decrypt() {
|
||||
if (this.isProtected && !this.isDecrypted && protectedSessionService.isProtectedSessionAvailable()) {
|
||||
this.title = protectedSessionService.decryptString(note.title);
|
||||
this.title = protectedSessionService.decryptString(this.title);
|
||||
|
||||
this.isDecrypted = true;
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
const NoteRevision = require('../entities/note_revision');
|
||||
const dateUtils = require('../services/date_utils');
|
||||
const log = require('../services/log');
|
||||
|
||||
/**
|
||||
* @param {Note} note
|
||||
@@ -9,14 +10,21 @@ const dateUtils = require('../services/date_utils');
|
||||
function protectNoteRevisions(note) {
|
||||
for (const revision of note.getRevisions()) {
|
||||
if (note.isProtected !== revision.isProtected) {
|
||||
const content = revision.getContent();
|
||||
try {
|
||||
const content = revision.getContent();
|
||||
|
||||
revision.isProtected = note.isProtected;
|
||||
revision.isProtected = note.isProtected;
|
||||
|
||||
// this will force de/encryption
|
||||
revision.setContent(content);
|
||||
// this will force de/encryption
|
||||
revision.setContent(content);
|
||||
|
||||
revision.save();
|
||||
revision.save();
|
||||
}
|
||||
catch (e) {
|
||||
log.error("Could not un/protect note revision ID = " + revision.noteRevisionId);
|
||||
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -185,18 +185,25 @@ function protectNoteRecursively(note, protect, includingSubTree, taskContext) {
|
||||
}
|
||||
|
||||
function protectNote(note, protect) {
|
||||
if (protect !== note.isProtected) {
|
||||
const content = note.getContent();
|
||||
try {
|
||||
if (protect !== note.isProtected) {
|
||||
const content = note.getContent();
|
||||
|
||||
note.isProtected = protect;
|
||||
note.isProtected = protect;
|
||||
|
||||
// this will force de/encryption
|
||||
note.setContent(content);
|
||||
// this will force de/encryption
|
||||
note.setContent(content);
|
||||
|
||||
note.save();
|
||||
note.save();
|
||||
}
|
||||
|
||||
noteRevisionService.protectNoteRevisions(note);
|
||||
}
|
||||
catch (e) {
|
||||
log.error("Could not un/protect note ID = " + note.noteId);
|
||||
|
||||
noteRevisionService.protectNoteRevisions(note);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
function findImageLinks(content, foundLinks) {
|
||||
@@ -668,8 +675,10 @@ function scanForLinks(note) {
|
||||
}
|
||||
}
|
||||
|
||||
function eraseDeletedNotes() {
|
||||
const eraseNotesAfterTimeInSeconds = optionService.getOptionInt('eraseNotesAfterTimeInSeconds');
|
||||
function eraseDeletedNotes(eraseNotesAfterTimeInSeconds = null) {
|
||||
if (eraseNotesAfterTimeInSeconds === null) {
|
||||
eraseNotesAfterTimeInSeconds = optionService.getOptionInt('eraseNotesAfterTimeInSeconds');
|
||||
}
|
||||
|
||||
const cutoffDate = new Date(Date.now() - eraseNotesAfterTimeInSeconds * 1000);
|
||||
|
||||
@@ -719,6 +728,10 @@ function eraseDeletedNotes() {
|
||||
log.info(`Erased notes: ${JSON.stringify(noteIdsToErase)}`);
|
||||
}
|
||||
|
||||
function eraseDeletedNotesNow() {
|
||||
eraseDeletedNotes(0);
|
||||
}
|
||||
|
||||
// do a replace in str - all keys should be replaced by the corresponding values
|
||||
function replaceByMap(str, mapObj) {
|
||||
const re = new RegExp(Object.keys(mapObj).join("|"),"g");
|
||||
@@ -739,7 +752,10 @@ function duplicateSubtree(origNoteId, newParentNoteId) {
|
||||
|
||||
const res = duplicateSubtreeInner(origNote, origBranch, newParentNoteId, noteIdMapping);
|
||||
|
||||
res.note.title += " (dup)";
|
||||
if (!res.note.title.endsWith('(dup)')) {
|
||||
res.note.title += " (dup)";
|
||||
}
|
||||
|
||||
res.note.save();
|
||||
|
||||
return res;
|
||||
@@ -822,9 +838,9 @@ function getNoteIdMapping(origNote) {
|
||||
|
||||
sqlInit.dbReady.then(() => {
|
||||
// first cleanup kickoff 5 minutes after startup
|
||||
setTimeout(cls.wrap(eraseDeletedNotes), 5 * 60 * 1000);
|
||||
setTimeout(cls.wrap(() => eraseDeletedNotes()), 5 * 60 * 1000);
|
||||
|
||||
setInterval(cls.wrap(eraseDeletedNotes), 4 * 3600 * 1000);
|
||||
setInterval(cls.wrap(() => eraseDeletedNotes()), 4 * 3600 * 1000);
|
||||
});
|
||||
|
||||
module.exports = {
|
||||
@@ -838,5 +854,6 @@ module.exports = {
|
||||
duplicateSubtree,
|
||||
duplicateSubtreeWithoutRoot,
|
||||
getUndeletedParentBranches,
|
||||
triggerNoteTitleChanged
|
||||
triggerNoteTitleChanged,
|
||||
eraseDeletedNotesNow
|
||||
};
|
||||
|
||||
@@ -43,10 +43,18 @@ function decryptNotes(notes) {
|
||||
}
|
||||
|
||||
function encrypt(plainText) {
|
||||
if (plainText === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return dataEncryptionService.encrypt(getDataKey(), plainText);
|
||||
}
|
||||
|
||||
function decrypt(cipherText) {
|
||||
if (cipherText === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return dataEncryptionService.decrypt(getDataKey(), cipherText);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user