mirror of
https://github.com/zadam/trilium.git
synced 2025-10-28 16:56:34 +01:00
Compare commits
32 Commits
v0.48.6-do
...
v0.48.9
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
265401775b | ||
|
|
10a5773c66 | ||
|
|
1e8472266f | ||
|
|
b30792a3da | ||
|
|
26602e8226 | ||
|
|
b8eeb0371c | ||
|
|
b1c4737e78 | ||
|
|
e29aee1aae | ||
|
|
1aff42f453 | ||
|
|
a098630e09 | ||
|
|
074eb1c02f | ||
|
|
a81ea3771f | ||
|
|
d9550dd59b | ||
|
|
a810c08c02 | ||
|
|
263b7a84bb | ||
|
|
9d18bebb13 | ||
|
|
26bcfe5160 | ||
|
|
89c04e6b6b | ||
|
|
40fb4ff56b | ||
|
|
d64c14482b | ||
|
|
564366861e | ||
|
|
8c11d022fb | ||
|
|
24210ef80c | ||
|
|
2135aa058e | ||
|
|
67542f448d | ||
|
|
08e9b59696 | ||
|
|
fe605c012a | ||
|
|
4c7c53d8c8 | ||
|
|
d345b7ed56 | ||
|
|
298af217e9 | ||
|
|
7d64f6a7dd | ||
|
|
bc8b6284a6 |
10
.idea/runConfigurations.xml
generated
10
.idea/runConfigurations.xml
generated
@@ -1,10 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="RunConfigurationProducerService">
|
|
||||||
<option name="ignoredProducers">
|
|
||||||
<set>
|
|
||||||
<option value="com.android.tools.idea.compose.preview.runconfiguration.ComposePreviewRunConfigurationProducer" />
|
|
||||||
</set>
|
|
||||||
</option>
|
|
||||||
</component>
|
|
||||||
</project>
|
|
||||||
11080
package-lock.json
generated
11080
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -2,7 +2,7 @@
|
|||||||
"name": "trilium",
|
"name": "trilium",
|
||||||
"productName": "Trilium Notes",
|
"productName": "Trilium Notes",
|
||||||
"description": "Trilium Notes",
|
"description": "Trilium Notes",
|
||||||
"version": "0.48.6",
|
"version": "0.48.9",
|
||||||
"license": "AGPL-3.0-only",
|
"license": "AGPL-3.0-only",
|
||||||
"main": "electron.js",
|
"main": "electron.js",
|
||||||
"bin": {
|
"bin": {
|
||||||
@@ -81,7 +81,7 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"cross-env": "7.0.3",
|
"cross-env": "7.0.3",
|
||||||
"electron": "13.6.1",
|
"electron": "13.6.3",
|
||||||
"electron-builder": "22.13.1",
|
"electron-builder": "22.13.1",
|
||||||
"electron-packager": "15.4.0",
|
"electron-packager": "15.4.0",
|
||||||
"electron-rebuild": "3.2.3",
|
"electron-rebuild": "3.2.3",
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ class NoteBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
child(childNoteBuilder, prefix = "") {
|
child(childNoteBuilder, prefix = "") {
|
||||||
new Branch(becca, {
|
new Branch({
|
||||||
branchId: id(),
|
branchId: id(),
|
||||||
noteId: childNoteBuilder.note.noteId,
|
noteId: childNoteBuilder.note.noteId,
|
||||||
parentNoteId: this.note.noteId,
|
parentNoteId: this.note.noteId,
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ describe("Parser", () => {
|
|||||||
expect(rootExp.constructor.name).toEqual("AndExp");
|
expect(rootExp.constructor.name).toEqual("AndExp");
|
||||||
expect(rootExp.subExpressions[0].constructor.name).toEqual("PropertyComparisonExp");
|
expect(rootExp.subExpressions[0].constructor.name).toEqual("PropertyComparisonExp");
|
||||||
expect(rootExp.subExpressions[1].constructor.name).toEqual("OrExp");
|
expect(rootExp.subExpressions[1].constructor.name).toEqual("OrExp");
|
||||||
expect(rootExp.subExpressions[1].subExpressions[0].constructor.name).toEqual("BeccaFlatTextExp");
|
expect(rootExp.subExpressions[1].subExpressions[0].constructor.name).toEqual("NoteFlatTextExp");
|
||||||
expect(rootExp.subExpressions[1].subExpressions[0].tokens).toEqual(["hello", "hi"]);
|
expect(rootExp.subExpressions[1].subExpressions[0].tokens).toEqual(["hello", "hi"]);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -55,7 +55,7 @@ describe("Parser", () => {
|
|||||||
|
|
||||||
const subs = rootExp.subExpressions[1].subExpressions;
|
const subs = rootExp.subExpressions[1].subExpressions;
|
||||||
|
|
||||||
expect(subs[0].constructor.name).toEqual("BeccaFlatTextExp");
|
expect(subs[0].constructor.name).toEqual("NoteFlatTextExp");
|
||||||
expect(subs[0].tokens).toEqual(["hello", "hi"]);
|
expect(subs[0].tokens).toEqual(["hello", "hi"]);
|
||||||
|
|
||||||
expect(subs[1].constructor.name).toEqual("NoteContentProtectedFulltextExp");
|
expect(subs[1].constructor.name).toEqual("NoteContentProtectedFulltextExp");
|
||||||
@@ -182,7 +182,7 @@ describe("Parser", () => {
|
|||||||
expect(firstSub.propertyName).toEqual('isArchived');
|
expect(firstSub.propertyName).toEqual('isArchived');
|
||||||
|
|
||||||
expect(secondSub.constructor.name).toEqual("OrExp");
|
expect(secondSub.constructor.name).toEqual("OrExp");
|
||||||
expect(secondSub.subExpressions[0].constructor.name).toEqual("BeccaFlatTextExp");
|
expect(secondSub.subExpressions[0].constructor.name).toEqual("NoteFlatTextExp");
|
||||||
expect(secondSub.subExpressions[0].tokens).toEqual(["hello"]);
|
expect(secondSub.subExpressions[0].tokens).toEqual(["hello"]);
|
||||||
|
|
||||||
expect(thirdSub.constructor.name).toEqual("LabelComparisonExp");
|
expect(thirdSub.constructor.name).toEqual("LabelComparisonExp");
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ describe("Search", () => {
|
|||||||
becca.reset();
|
becca.reset();
|
||||||
|
|
||||||
rootNote = new NoteBuilder(new Note({noteId: 'root', title: 'root', type: 'text'}));
|
rootNote = new NoteBuilder(new Note({noteId: 'root', title: 'root', type: 'text'}));
|
||||||
new Branch(becca, {branchId: 'root', noteId: 'root', parentNoteId: 'none', notePosition: 10});
|
new Branch({branchId: 'root', noteId: 'root', parentNoteId: 'none', notePosition: 10});
|
||||||
});
|
});
|
||||||
|
|
||||||
it("simple path match", () => {
|
it("simple path match", () => {
|
||||||
@@ -157,6 +157,21 @@ describe("Search", () => {
|
|||||||
expect(findNoteByTitle(searchResults, "Czech Republic")).toBeTruthy();
|
expect(findNoteByTitle(searchResults, "Czech Republic")).toBeTruthy();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("inherited label comparison", () => {
|
||||||
|
rootNote
|
||||||
|
.child(note("Europe")
|
||||||
|
.label('country', '', true)
|
||||||
|
.child(note("Austria"))
|
||||||
|
.child(note("Czech Republic"))
|
||||||
|
);
|
||||||
|
|
||||||
|
const searchContext = new SearchContext();
|
||||||
|
|
||||||
|
const searchResults = searchService.findResultsWithQuery('austria #country', searchContext);
|
||||||
|
expect(searchResults.length).toEqual(1);
|
||||||
|
expect(findNoteByTitle(searchResults, "Austria")).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
it("numeric label comparison fallback to string comparison", () => {
|
it("numeric label comparison fallback to string comparison", () => {
|
||||||
// dates should not be coerced into numbers which would then give wrong numbers
|
// dates should not be coerced into numbers which would then give wrong numbers
|
||||||
|
|
||||||
@@ -169,7 +184,7 @@ describe("Search", () => {
|
|||||||
.label('established', '1993-01-01'))
|
.label('established', '1993-01-01'))
|
||||||
.child(note("Hungary")
|
.child(note("Hungary")
|
||||||
.label('established', '1920-06-04'))
|
.label('established', '1920-06-04'))
|
||||||
);
|
);
|
||||||
|
|
||||||
const searchContext = new SearchContext();
|
const searchContext = new SearchContext();
|
||||||
|
|
||||||
@@ -218,7 +233,7 @@ describe("Search", () => {
|
|||||||
test("#month = month", 1);
|
test("#month = month", 1);
|
||||||
test("#month = 'MONTH'", 0);
|
test("#month = 'MONTH'", 0);
|
||||||
|
|
||||||
test("note.dateCreated =* month", 1);
|
test("note.dateCreated =* month", 2);
|
||||||
|
|
||||||
test("#date = TODAY", 1);
|
test("#date = TODAY", 1);
|
||||||
test("#date = today", 1);
|
test("#date = today", 1);
|
||||||
@@ -337,11 +352,11 @@ describe("Search", () => {
|
|||||||
|
|
||||||
const searchContext = new SearchContext();
|
const searchContext = new SearchContext();
|
||||||
|
|
||||||
let searchResults = searchService.findResultsWithQuery('#city AND note.getAncestors().title = Europe', searchContext);
|
let searchResults = searchService.findResultsWithQuery('#city AND note.ancestors.title = Europe', searchContext);
|
||||||
expect(searchResults.length).toEqual(1);
|
expect(searchResults.length).toEqual(1);
|
||||||
expect(findNoteByTitle(searchResults, "Prague")).toBeTruthy();
|
expect(findNoteByTitle(searchResults, "Prague")).toBeTruthy();
|
||||||
|
|
||||||
searchResults = searchService.findResultsWithQuery('#city AND note.getAncestors().title = Asia', searchContext);
|
searchResults = searchService.findResultsWithQuery('#city AND note.ancestors.title = Asia', searchContext);
|
||||||
expect(searchResults.length).toEqual(1);
|
expect(searchResults.length).toEqual(1);
|
||||||
expect(findNoteByTitle(searchResults, "Taipei")).toBeTruthy();
|
expect(findNoteByTitle(searchResults, "Taipei")).toBeTruthy();
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -58,6 +58,9 @@ class Branch extends AbstractEntity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
|
this.becca.branches[this.branchId] = this;
|
||||||
|
this.becca.childParentToBranch[`${this.noteId}-${this.parentNoteId}`] = this;
|
||||||
|
|
||||||
if (this.branchId === 'root') {
|
if (this.branchId === 'root') {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -76,15 +79,12 @@ class Branch extends AbstractEntity {
|
|||||||
if (!parentNote.children.includes(childNote)) {
|
if (!parentNote.children.includes(childNote)) {
|
||||||
parentNote.children.push(childNote);
|
parentNote.children.push(childNote);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.becca.branches[this.branchId] = this;
|
|
||||||
this.becca.childParentToBranch[`${this.noteId}-${this.parentNoteId}`] = this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @returns {Note} */
|
/** @returns {Note} */
|
||||||
get childNote() {
|
get childNote() {
|
||||||
if (!(this.noteId in this.becca.notes)) {
|
if (!(this.noteId in this.becca.notes)) {
|
||||||
// entities can come out of order in sync, create skeleton which will be filled later
|
// entities can come out of order in sync/import, create skeleton which will be filled later
|
||||||
this.becca.addNote(this.noteId, new Note({noteId: this.noteId}));
|
this.becca.addNote(this.noteId, new Note({noteId: this.noteId}));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -98,7 +98,7 @@ class Branch extends AbstractEntity {
|
|||||||
/** @returns {Note} */
|
/** @returns {Note} */
|
||||||
get parentNote() {
|
get parentNote() {
|
||||||
if (!(this.parentNoteId in this.becca.notes)) {
|
if (!(this.parentNoteId in this.becca.notes)) {
|
||||||
// entities can come out of order in sync, create skeleton which will be filled later
|
// entities can come out of order in sync/import, create skeleton which will be filled later
|
||||||
this.becca.addNote(this.parentNoteId, new Note({noteId: this.parentNoteId}));
|
this.becca.addNote(this.parentNoteId, new Note({noteId: this.parentNoteId}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -30,12 +30,12 @@ const TPL = `
|
|||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="image-max-width-height">Max width / height of an image in pixels (image will be resized if it exceeds this setting).</label>
|
<label for="image-max-width-height">Max width / height of an image in pixels (image will be resized if it exceeds this setting).</label>
|
||||||
<input class="form-control" id="image-max-width-height" type="number">
|
<input class="form-control" id="image-max-width-height" type="number" min="1">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="image-jpeg-quality">JPEG quality (0 - worst quality, 100 best quality, 50 - 85 is recommended)</label>
|
<label for="image-jpeg-quality">JPEG quality (10 - worst quality, 100 best quality, 50 - 85 is recommended)</label>
|
||||||
<input class="form-control" id="image-jpeg-quality" min="0" max="100" type="number">
|
<input class="form-control" id="image-jpeg-quality" min="10" max="100" type="number">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -67,7 +67,7 @@ const TPL = `
|
|||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="protected-session-timeout-in-seconds">Protected session timeout (in seconds)</label>
|
<label for="protected-session-timeout-in-seconds">Protected session timeout (in seconds)</label>
|
||||||
<input class="form-control" id="protected-session-timeout-in-seconds" type="number">
|
<input class="form-control" id="protected-session-timeout-in-seconds" type="number" min="60">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -78,7 +78,7 @@ const TPL = `
|
|||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="note-revision-snapshot-time-interval-in-seconds">Note revision snapshot time interval (in seconds)</label>
|
<label for="note-revision-snapshot-time-interval-in-seconds">Note revision snapshot time interval (in seconds)</label>
|
||||||
<input class="form-control" id="note-revision-snapshot-time-interval-in-seconds" type="number">
|
<input class="form-control" id="note-revision-snapshot-time-interval-in-seconds" type="number" min="10">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -89,12 +89,12 @@ const TPL = `
|
|||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="auto-readonly-size-text">Automatic readonly size (text notes)</label>
|
<label for="auto-readonly-size-text">Automatic readonly size (text notes)</label>
|
||||||
<input class="form-control" id="auto-readonly-size-text" type="number">
|
<input class="form-control" id="auto-readonly-size-text" type="number" min="0">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="auto-readonly-size-code">Automatic readonly size (code notes)</label>
|
<label for="auto-readonly-size-code">Automatic readonly size (code notes)</label>
|
||||||
<input class="form-control" id="auto-readonly-size-code" type="number">
|
<input class="form-control" id="auto-readonly-size-code" type="number" min="0">
|
||||||
</div>
|
</div>
|
||||||
</div>`;
|
</div>`;
|
||||||
|
|
||||||
|
|||||||
@@ -88,7 +88,7 @@ export default class MobileLayout {
|
|||||||
return new FlexContainer('row').cssBlock(MOBILE_CSS)
|
return new FlexContainer('row').cssBlock(MOBILE_CSS)
|
||||||
.setParent(appContext)
|
.setParent(appContext)
|
||||||
.id('root-widget')
|
.id('root-widget')
|
||||||
.css('height', '100vh')
|
.css('height', '100%')
|
||||||
.child(new ScreenContainer("tree", 'column')
|
.child(new ScreenContainer("tree", 'column')
|
||||||
.class("d-sm-flex d-md-flex d-lg-flex d-xl-flex col-12 col-sm-5 col-md-4 col-lg-4 col-xl-4")
|
.class("d-sm-flex d-md-flex d-lg-flex d-xl-flex col-12 col-sm-5 col-md-4 col-lg-4 col-xl-4")
|
||||||
.css("max-height", "100%")
|
.css("max-height", "100%")
|
||||||
|
|||||||
@@ -213,9 +213,13 @@ export default class Entrypoints extends Component {
|
|||||||
} else if (note.mime.endsWith("env=backend")) {
|
} else if (note.mime.endsWith("env=backend")) {
|
||||||
await server.post('script/run/' + note.noteId);
|
await server.post('script/run/' + note.noteId);
|
||||||
} else if (note.mime === 'text/x-sqlite;schema=trilium') {
|
} else if (note.mime === 'text/x-sqlite;schema=trilium') {
|
||||||
const {results} = await server.post("sql/execute/" + note.noteId);
|
const resp = await server.post("sql/execute/" + note.noteId);
|
||||||
|
|
||||||
await appContext.triggerEvent('sqlQueryResults', {ntxId: ntxId, results: results});
|
if (!resp.success) {
|
||||||
|
alert("Error occurred while executing SQL query: " + resp.message);
|
||||||
|
}
|
||||||
|
|
||||||
|
await appContext.triggerEvent('sqlQueryResults', {ntxId: ntxId, results: resp.results});
|
||||||
}
|
}
|
||||||
|
|
||||||
toastService.showMessage("Note executed");
|
toastService.showMessage("Note executed");
|
||||||
|
|||||||
@@ -83,6 +83,10 @@ async function resolveNotePathToSegments(notePath, hoistedNoteId = 'root', logEr
|
|||||||
if (someNotePath) { // in case it's root the path may be empty
|
if (someNotePath) { // in case it's root the path may be empty
|
||||||
const pathToRoot = someNotePath.split("/").reverse().slice(1);
|
const pathToRoot = someNotePath.split("/").reverse().slice(1);
|
||||||
|
|
||||||
|
if (!pathToRoot.includes("root")) {
|
||||||
|
pathToRoot.push('root');
|
||||||
|
}
|
||||||
|
|
||||||
for (const noteId of pathToRoot) {
|
for (const noteId of pathToRoot) {
|
||||||
effectivePathSegments.push(noteId);
|
effectivePathSegments.push(noteId);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -181,9 +181,9 @@ async function consumeFrontendUpdateData() {
|
|||||||
|
|
||||||
for (const entityChange of nonProcessedEntityChanges) {
|
for (const entityChange of nonProcessedEntityChanges) {
|
||||||
processedEntityChangeIds.add(entityChange.id);
|
processedEntityChangeIds.add(entityChange.id);
|
||||||
}
|
|
||||||
|
|
||||||
lastProcessedEntityChangeId = Math.max(lastProcessedEntityChangeId, allEntityChanges[allEntityChanges.length - 1].id);
|
lastProcessedEntityChangeId = Math.max(lastProcessedEntityChangeId, entityChange.id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
checkEntityChangeIdListeners();
|
checkEntityChangeIdListeners();
|
||||||
|
|||||||
@@ -44,7 +44,10 @@ export default class ButtonWidget extends NoteContextAwareWidget {
|
|||||||
this.$widget.tooltip({
|
this.$widget.tooltip({
|
||||||
html: true,
|
html: true,
|
||||||
title: () => {
|
title: () => {
|
||||||
const title = this.settings.title;
|
const title = typeof this.settings.title === "function"
|
||||||
|
? this.settings.title()
|
||||||
|
: this.settings.title;
|
||||||
|
|
||||||
const action = actions.find(act => act.actionName === this.settings.command);
|
const action = actions.find(act => act.actionName === this.settings.command);
|
||||||
|
|
||||||
if (action && action.effectiveShortcuts.length > 0) {
|
if (action && action.effectiveShortcuts.length > 0) {
|
||||||
|
|||||||
@@ -5,8 +5,25 @@ import froca from "../../services/froca.js";
|
|||||||
export default class OpenNoteButtonWidget extends ButtonWidget {
|
export default class OpenNoteButtonWidget extends ButtonWidget {
|
||||||
targetNote(noteId) {
|
targetNote(noteId) {
|
||||||
froca.getNote(noteId).then(note => {
|
froca.getNote(noteId).then(note => {
|
||||||
|
if (!note) {
|
||||||
|
console.log(`Note ${noteId} has not been found. This might happen on the first run before the target note is created.`);
|
||||||
|
|
||||||
|
if (!this.retried) {
|
||||||
|
this.retried = true;
|
||||||
|
|
||||||
|
setTimeout(() => this.targetNote(noteId), 15000); // should be higher than timeout for createMissingSpecialNotes
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
this.icon(note.getIcon());
|
this.icon(note.getIcon());
|
||||||
this.title(note.title);
|
this.title(() => {
|
||||||
|
const n = froca.getNoteFromCache(noteId);
|
||||||
|
|
||||||
|
// always fresh, always decoded (when protected session is available)
|
||||||
|
return n.title;
|
||||||
|
});
|
||||||
|
|
||||||
this.refreshIcon();
|
this.refreshIcon();
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ export default class RootContainer extends FlexContainer {
|
|||||||
super('row');
|
super('row');
|
||||||
|
|
||||||
this.id('root-widget');
|
this.id('root-widget');
|
||||||
this.css('height', '100vh');
|
this.css('height', '100%');
|
||||||
}
|
}
|
||||||
|
|
||||||
refresh() {
|
refresh() {
|
||||||
|
|||||||
@@ -150,6 +150,7 @@ export default class NoteTreeWidget extends NoteContextAwareWidget {
|
|||||||
doRender() {
|
doRender() {
|
||||||
this.$widget = $(TPL);
|
this.$widget = $(TPL);
|
||||||
this.$tree = this.$widget.find('.tree');
|
this.$tree = this.$widget.find('.tree');
|
||||||
|
this.$treeActions = this.$widget.find(".tree-actions");
|
||||||
|
|
||||||
this.$tree.on("mousedown", ".unhoist-button", () => hoistedNoteService.unhoist());
|
this.$tree.on("mousedown", ".unhoist-button", () => hoistedNoteService.unhoist());
|
||||||
this.$tree.on("mousedown", ".refresh-search-button", e => this.refreshSearch(e));
|
this.$tree.on("mousedown", ".refresh-search-button", e => this.refreshSearch(e));
|
||||||
@@ -200,20 +201,16 @@ export default class NoteTreeWidget extends NoteContextAwareWidget {
|
|||||||
this.$hideIncludedImages.prop("checked", this.hideIncludedImages);
|
this.$hideIncludedImages.prop("checked", this.hideIncludedImages);
|
||||||
this.$autoCollapseNoteTree.prop("checked", this.autoCollapseNoteTree);
|
this.$autoCollapseNoteTree.prop("checked", this.autoCollapseNoteTree);
|
||||||
|
|
||||||
let top = this.$treeSettingsButton[0].offsetTop;
|
const top = this.$treeActions[0].offsetTop - (this.$treeSettingsPopup.outerHeight());
|
||||||
let left = this.$treeSettingsButton[0].offsetLeft;
|
const left = Math.max(
|
||||||
top -= this.$treeSettingsPopup.outerHeight() + 10;
|
0,
|
||||||
left += this.$treeSettingsButton.outerWidth() - this.$treeSettingsPopup.outerWidth();
|
this.$treeActions[0].offsetLeft - this.$treeSettingsPopup.outerWidth() + this.$treeActions.outerWidth()
|
||||||
|
);
|
||||||
if (left < 0) {
|
|
||||||
left = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.$treeSettingsPopup.css({
|
this.$treeSettingsPopup.css({
|
||||||
display: "block",
|
top,
|
||||||
top: top,
|
left
|
||||||
left: left
|
}).show();
|
||||||
}).addClass("show");
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -134,6 +134,8 @@ export default class QuickSearchWidget extends BasicWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async showInFullSearch() {
|
async showInFullSearch() {
|
||||||
|
this.$dropdownToggle.dropdown("hide");
|
||||||
|
|
||||||
const searchNote = await dateNotesService.createSearchNote({searchString: this.$searchString.val()});
|
const searchNote = await dateNotesService.createSearchNote({searchString: this.$searchString.val()});
|
||||||
|
|
||||||
await froca.loadSearchNote(searchNote.noteId);
|
await froca.loadSearchNote(searchNote.noteId);
|
||||||
|
|||||||
@@ -69,7 +69,10 @@ export default class NotePathsWidget extends NoteContextAwareWidget {
|
|||||||
this.$notePathList.empty();
|
this.$notePathList.empty();
|
||||||
|
|
||||||
if (this.noteId === 'root') {
|
if (this.noteId === 'root') {
|
||||||
await this.getRenderedPath('root');
|
this.$notePathList.empty().append(
|
||||||
|
await this.getRenderedPath('root')
|
||||||
|
);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -94,7 +97,7 @@ export default class NotePathsWidget extends NoteContextAwareWidget {
|
|||||||
this.$notePathList.empty().append(...renderedPaths);
|
this.$notePathList.empty().append(...renderedPaths);
|
||||||
}
|
}
|
||||||
|
|
||||||
async getRenderedPath(notePath, notePathRecord) {
|
async getRenderedPath(notePath, notePathRecord = null) {
|
||||||
const title = await treeService.getNotePathTitle(notePath);
|
const title = await treeService.getNotePathTitle(notePath);
|
||||||
|
|
||||||
const $noteLink = await linkService.createNoteLink(notePath, {title});
|
const $noteLink = await linkService.createNoteLink(notePath, {title});
|
||||||
@@ -109,20 +112,20 @@ export default class NotePathsWidget extends NoteContextAwareWidget {
|
|||||||
$noteLink.addClass("path-current");
|
$noteLink.addClass("path-current");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (notePathRecord.isInHoistedSubTree) {
|
if (!notePathRecord || notePathRecord.isInHoistedSubTree) {
|
||||||
$noteLink.addClass("path-in-hoisted-subtree");
|
$noteLink.addClass("path-in-hoisted-subtree");
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
icons.push(`<span class="bx bx-trending-up" title="This path is outside of hoisted note and you would have to unhoist."></span>`);
|
icons.push(`<span class="bx bx-trending-up" title="This path is outside of hoisted note and you would have to unhoist."></span>`);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (notePathRecord.isArchived) {
|
if (notePathRecord?.isArchived) {
|
||||||
$noteLink.addClass("path-archived");
|
$noteLink.addClass("path-archived");
|
||||||
|
|
||||||
icons.push(`<span class="bx bx-archive" title="Archived"></span>`);
|
icons.push(`<span class="bx bx-archive" title="Archived"></span>`);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (notePathRecord.isSearch) {
|
if (notePathRecord?.isSearch) {
|
||||||
$noteLink.addClass("path-search");
|
$noteLink.addClass("path-search");
|
||||||
|
|
||||||
icons.push(`<span class="bx bx-search" title="Search"></span>`);
|
icons.push(`<span class="bx bx-search" title="Search"></span>`);
|
||||||
|
|||||||
@@ -115,9 +115,9 @@ export default class PromotedAttributesWidget extends NoteContextAwareWidget {
|
|||||||
|
|
||||||
const $input = $("<input>")
|
const $input = $("<input>")
|
||||||
.prop("tabindex", 200 + definitionAttr.position)
|
.prop("tabindex", 200 + definitionAttr.position)
|
||||||
.prop("attribute-id", valueAttr.noteId === this.noteId ? valueAttr.attributeId : '') // if not owned, we'll force creation of a new attribute instead of updating the inherited one
|
.attr("data-attribute-id", valueAttr.noteId === this.noteId ? valueAttr.attributeId : '') // if not owned, we'll force creation of a new attribute instead of updating the inherited one
|
||||||
.prop("attribute-type", valueAttr.type)
|
.attr("data-attribute-type", valueAttr.type)
|
||||||
.prop("attribute-name", valueAttr.name)
|
.attr("data-attribute-name", valueAttr.name)
|
||||||
.prop("value", valueAttr.value)
|
.prop("value", valueAttr.value)
|
||||||
.addClass("form-control")
|
.addClass("form-control")
|
||||||
.addClass("promoted-attribute-input")
|
.addClass("promoted-attribute-input")
|
||||||
@@ -230,7 +230,7 @@ export default class PromotedAttributesWidget extends NoteContextAwareWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (definition.multiplicity === "multi") {
|
if (definition.multiplicity === "multi") {
|
||||||
const addButton = $("<span>")
|
const $addButton = $("<span>")
|
||||||
.addClass("bx bx-plus pointer")
|
.addClass("bx bx-plus pointer")
|
||||||
.prop("title", "Add new attribute")
|
.prop("title", "Add new attribute")
|
||||||
.on('click', async () => {
|
.on('click', async () => {
|
||||||
@@ -246,12 +246,28 @@ export default class PromotedAttributesWidget extends NoteContextAwareWidget {
|
|||||||
$new.find('input').trigger('focus');
|
$new.find('input').trigger('focus');
|
||||||
});
|
});
|
||||||
|
|
||||||
const removeButton = $("<span>")
|
const $removeButton = $("<span>")
|
||||||
.addClass("bx bx-trash pointer")
|
.addClass("bx bx-trash pointer")
|
||||||
.prop("title", "Remove this attribute")
|
.prop("title", "Remove this attribute")
|
||||||
.on('click', async () => {
|
.on('click', async () => {
|
||||||
if (valueAttr.attributeId) {
|
const attributeId = $input.attr("data-attribute-id");
|
||||||
await server.remove("notes/" + this.noteId + "/attributes/" + valueAttr.attributeId, this.componentId);
|
|
||||||
|
if (attributeId) {
|
||||||
|
await server.remove("notes/" + this.noteId + "/attributes/" + attributeId, this.componentId);
|
||||||
|
}
|
||||||
|
|
||||||
|
// if it's the last one the create new empty form immediately
|
||||||
|
const sameAttrSelector = `input[data-attribute-type='${valueAttr.type}'][data-attribute-name='${valueName}']`;
|
||||||
|
|
||||||
|
if (this.$widget.find(sameAttrSelector).length <= 1) {
|
||||||
|
const $new = await this.createPromotedAttributeCell(definitionAttr, {
|
||||||
|
attributeId: "",
|
||||||
|
type: valueAttr.type,
|
||||||
|
name: valueName,
|
||||||
|
value: ""
|
||||||
|
}, valueName);
|
||||||
|
|
||||||
|
$wrapper.after($new);
|
||||||
}
|
}
|
||||||
|
|
||||||
$wrapper.remove();
|
$wrapper.remove();
|
||||||
@@ -259,9 +275,9 @@ export default class PromotedAttributesWidget extends NoteContextAwareWidget {
|
|||||||
|
|
||||||
$multiplicityCell
|
$multiplicityCell
|
||||||
.append(" ")
|
.append(" ")
|
||||||
.append(addButton)
|
.append($addButton)
|
||||||
.append(" ")
|
.append(" ")
|
||||||
.append(removeButton);
|
.append($removeButton);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $wrapper;
|
return $wrapper;
|
||||||
@@ -275,7 +291,7 @@ export default class PromotedAttributesWidget extends NoteContextAwareWidget {
|
|||||||
if ($attr.prop("type") === "checkbox") {
|
if ($attr.prop("type") === "checkbox") {
|
||||||
value = $attr.is(':checked') ? "true" : "false";
|
value = $attr.is(':checked') ? "true" : "false";
|
||||||
}
|
}
|
||||||
else if ($attr.prop("attribute-type") === "relation") {
|
else if ($attr.attr("data-attribute-type") === "relation") {
|
||||||
const selectedPath = $attr.getSelectedNotePath();
|
const selectedPath = $attr.getSelectedNotePath();
|
||||||
|
|
||||||
value = selectedPath ? treeService.getNoteIdFromNotePath(selectedPath) : "";
|
value = selectedPath ? treeService.getNoteIdFromNotePath(selectedPath) : "";
|
||||||
@@ -285,13 +301,13 @@ export default class PromotedAttributesWidget extends NoteContextAwareWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const result = await server.put(`notes/${this.noteId}/attribute`, {
|
const result = await server.put(`notes/${this.noteId}/attribute`, {
|
||||||
attributeId: $attr.prop("attribute-id"),
|
attributeId: $attr.attr("data-attribute-id"),
|
||||||
type: $attr.prop("attribute-type"),
|
type: $attr.attr("data-attribute-type"),
|
||||||
name: $attr.prop("attribute-name"),
|
name: $attr.attr("data-attribute-name"),
|
||||||
value: value
|
value: value
|
||||||
}, this.componentId);
|
}, this.componentId);
|
||||||
|
|
||||||
$attr.prop("attribute-id", result.attributeId);
|
$attr.attr("data-attribute-id", result.attributeId);
|
||||||
}
|
}
|
||||||
|
|
||||||
entitiesReloadedEvent({loadResults}) {
|
entitiesReloadedEvent({loadResults}) {
|
||||||
|
|||||||
@@ -84,5 +84,11 @@ export default class EmptyTypeWidget extends TypeWidget {
|
|||||||
.on('click', () => this.triggerCommand('hoistNote', {noteId: workspaceNote.noteId}))
|
.on('click', () => this.triggerCommand('hoistNote', {noteId: workspaceNote.noteId}))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (workspaceNotes.length === 0) {
|
||||||
|
this.$autoComplete
|
||||||
|
.trigger('focus')
|
||||||
|
.trigger('select');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -57,9 +57,7 @@ class ImageTypeWidget extends TypeWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async doRefresh(note) {
|
async doRefresh(note) {
|
||||||
const imageHash = utils.randomString(10);
|
this.$imageView.prop("src", `api/images/${note.noteId}/${note.title}`);
|
||||||
|
|
||||||
this.$imageView.prop("src", `api/images/${note.noteId}/${note.title}?${imageHash}`);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
copyImageToClipboardEvent({ntxId}) {
|
copyImageToClipboardEvent({ntxId}) {
|
||||||
|
|||||||
@@ -18,9 +18,11 @@ body {
|
|||||||
on the last line of the editor. */
|
on the last line of the editor. */
|
||||||
position: fixed;
|
position: fixed;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
background-color: var(--main-background-color);
|
background-color: var(--main-background-color);
|
||||||
color: var(--main-text-color);
|
color: var(--main-text-color);
|
||||||
font-family: var(--main-font-family);
|
font-family: var(--main-font-family);
|
||||||
|
font-size: var(--main-font-size);
|
||||||
}
|
}
|
||||||
|
|
||||||
a, a:visited, a:hover {
|
a, a:visited, a:hover {
|
||||||
@@ -58,7 +60,7 @@ table td, table th {
|
|||||||
}
|
}
|
||||||
|
|
||||||
code, kbd, pre, samp {
|
code, kbd, pre, samp {
|
||||||
font-family: var(--monospace-font-family);
|
font-family: var(--monospace-font-family) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.input-group-text {
|
.input-group-text {
|
||||||
@@ -714,10 +716,6 @@ a.external:not(.no-arrow):after, a[href^="http://"]:not(.no-arrow):after, a[href
|
|||||||
border-color: var(--main-border-color) !important;
|
border-color: var(--main-border-color) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
body {
|
|
||||||
font-size: var(--main-font-size);
|
|
||||||
}
|
|
||||||
|
|
||||||
.gutter {
|
.gutter {
|
||||||
background: linear-gradient(to bottom, transparent, var(--accented-background-color), transparent);
|
background: linear-gradient(to bottom, transparent, var(--accented-background-color), transparent);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -60,6 +60,7 @@ function downloadNoteFile(noteId, res, contentDisposition = true) {
|
|||||||
res.setHeader('Content-Disposition', utils.getContentDisposition(filename));
|
res.setHeader('Content-Disposition', utils.getContentDisposition(filename));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
res.setHeader("Cache-Control", "no-cache, no-store, must-revalidate");
|
||||||
res.setHeader('Content-Type', note.mime);
|
res.setHeader('Content-Type', note.mime);
|
||||||
|
|
||||||
res.send(note.getContent());
|
res.send(note.getContent());
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ function returnImage(req, res) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
res.set('Content-Type', image.mime);
|
res.set('Content-Type', image.mime);
|
||||||
|
res.set("Cache-Control", "no-cache, no-store, must-revalidate");
|
||||||
|
|
||||||
res.send(image.getContent());
|
res.send(image.getContent());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,6 +42,11 @@ function getNeighbors(note, depth) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const targetNote = relation.getTargetNote();
|
const targetNote = relation.getTargetNote();
|
||||||
|
|
||||||
|
if (targetNote.hasLabel('excludeFromNoteMap')) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
retNoteIds.push(targetNote.noteId);
|
retNoteIds.push(targetNote.noteId);
|
||||||
|
|
||||||
for (const noteId of getNeighbors(targetNote, depth - 1)) {
|
for (const noteId of getNeighbors(targetNote, depth - 1)) {
|
||||||
@@ -56,6 +61,11 @@ function getNeighbors(note, depth) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const sourceNote = relation.getNote();
|
const sourceNote = relation.getNote();
|
||||||
|
|
||||||
|
if (sourceNote.hasLabel('excludeFromNoteMap')) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
retNoteIds.push(sourceNote.noteId);
|
retNoteIds.push(sourceNote.noteId);
|
||||||
|
|
||||||
for (const noteId of getNeighbors(sourceNote, depth - 1)) {
|
for (const noteId of getNeighbors(sourceNote, depth - 1)) {
|
||||||
|
|||||||
@@ -203,6 +203,10 @@ function changeTitle(req) {
|
|||||||
|
|
||||||
const noteTitleChanged = note.title !== title;
|
const noteTitleChanged = note.title !== title;
|
||||||
|
|
||||||
|
if (noteTitleChanged) {
|
||||||
|
noteService.saveNoteRevision(note);
|
||||||
|
}
|
||||||
|
|
||||||
note.title = title;
|
note.title = title;
|
||||||
|
|
||||||
note.save();
|
note.save();
|
||||||
|
|||||||
@@ -204,6 +204,11 @@ function queueSector(req) {
|
|||||||
entityChangesService.addEntityChangesForSector(entityName, sector);
|
entityChangesService.addEntityChangesForSector(entityName, sector);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function checkEntityChanges() {
|
||||||
|
const consistencyChecks = require("../../services/consistency_checks");
|
||||||
|
consistencyChecks.runEntityChangesChecks();
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
testSync,
|
testSync,
|
||||||
checkSync,
|
checkSync,
|
||||||
@@ -215,5 +220,6 @@ module.exports = {
|
|||||||
update,
|
update,
|
||||||
getStats,
|
getStats,
|
||||||
syncFinished,
|
syncFinished,
|
||||||
queueSector
|
queueSector,
|
||||||
|
checkEntityChanges
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -294,6 +294,7 @@ function register(app) {
|
|||||||
route(GET, '/api/sync/changed', [auth.checkApiAuth], syncApiRoute.getChanged, apiResultHandler);
|
route(GET, '/api/sync/changed', [auth.checkApiAuth], syncApiRoute.getChanged, apiResultHandler);
|
||||||
route(PUT, '/api/sync/update', [auth.checkApiAuth], syncApiRoute.update, apiResultHandler);
|
route(PUT, '/api/sync/update', [auth.checkApiAuth], syncApiRoute.update, apiResultHandler);
|
||||||
route(POST, '/api/sync/finished', [auth.checkApiAuth], syncApiRoute.syncFinished, apiResultHandler);
|
route(POST, '/api/sync/finished', [auth.checkApiAuth], syncApiRoute.syncFinished, apiResultHandler);
|
||||||
|
route(POST, '/api/sync/check-entity-changes', [auth.checkApiAuth], syncApiRoute.checkEntityChanges, apiResultHandler);
|
||||||
route(POST, '/api/sync/queue-sector/:entityName/:sector', [auth.checkApiAuth], syncApiRoute.queueSector, apiResultHandler);
|
route(POST, '/api/sync/queue-sector/:entityName/:sector', [auth.checkApiAuth], syncApiRoute.queueSector, apiResultHandler);
|
||||||
route(GET, '/api/sync/stats', [], syncApiRoute.getStats, apiResultHandler);
|
route(GET, '/api/sync/stats', [], syncApiRoute.getStats, apiResultHandler);
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ const packageJson = require('../../package');
|
|||||||
const {TRILIUM_DATA_DIR} = require('./data_dir');
|
const {TRILIUM_DATA_DIR} = require('./data_dir');
|
||||||
|
|
||||||
const APP_DB_VERSION = 185;
|
const APP_DB_VERSION = 185;
|
||||||
const SYNC_VERSION = 21;
|
const SYNC_VERSION = 22;
|
||||||
const CLIPPER_PROTOCOL_VERSION = "1.0";
|
const CLIPPER_PROTOCOL_VERSION = "1.0";
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
|||||||
@@ -62,6 +62,7 @@ const BUILTIN_ATTRIBUTES = [
|
|||||||
{ type: 'relation', name: 'renderNote', isDangerous: true }
|
{ type: 'relation', name: 'renderNote', isDangerous: true }
|
||||||
];
|
];
|
||||||
|
|
||||||
|
/** @returns {Note[]} */
|
||||||
function getNotesWithLabel(name, value) {
|
function getNotesWithLabel(name, value) {
|
||||||
const query = formatAttrForSearch({type: 'label', name, value}, true);
|
const query = formatAttrForSearch({type: 'label', name, value}, true);
|
||||||
return searchService.searchNotes(query, {
|
return searchService.searchNotes(query, {
|
||||||
@@ -71,6 +72,7 @@ function getNotesWithLabel(name, value) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO: should be in search service
|
// TODO: should be in search service
|
||||||
|
/** @returns {Note|null} */
|
||||||
function getNoteWithLabel(name, value) {
|
function getNoteWithLabel(name, value) {
|
||||||
// optimized version (~20 times faster) without using normal search, useful for e.g. finding date notes
|
// optimized version (~20 times faster) without using normal search, useful for e.g. finding date notes
|
||||||
const attrs = becca.findAttributes('label', name);
|
const attrs = becca.findAttributes('label', name);
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
module.exports = { buildDate:"2021-11-13T22:49:58+01:00", buildRevision: "c94603010630cfafe64575ab378c482bb39fb083" };
|
module.exports = { buildDate:"2021-12-22T22:39:24+01:00", buildRevision: "10a5773c66e678c6d2bd3bd9dc9952d5cd76b795" };
|
||||||
|
|||||||
@@ -258,7 +258,8 @@ class ConsistencyChecks {
|
|||||||
FROM branches
|
FROM branches
|
||||||
WHERE noteId = ?
|
WHERE noteId = ?
|
||||||
and parentNoteId = ?
|
and parentNoteId = ?
|
||||||
and isDeleted = 0`, [noteId, parentNoteId]);
|
and isDeleted = 0
|
||||||
|
ORDER BY utcDateCreated`, [noteId, parentNoteId]);
|
||||||
|
|
||||||
const branches = branchIds.map(branchId => becca.getBranch(branchId));
|
const branches = branchIds.map(branchId => becca.getBranch(branchId));
|
||||||
|
|
||||||
@@ -537,6 +538,27 @@ class ConsistencyChecks {
|
|||||||
logError(`Unrecognized entity change id=${id}, entityName=${entityName}, entityId=${entityId}`);
|
logError(`Unrecognized entity change id=${id}, entityName=${entityName}, entityId=${entityId}`);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.findAndFixIssues(`
|
||||||
|
SELECT
|
||||||
|
id, entityId
|
||||||
|
FROM
|
||||||
|
entity_changes
|
||||||
|
JOIN ${entityName} ON entityId = ${key}
|
||||||
|
WHERE
|
||||||
|
entity_changes.isErased = 1
|
||||||
|
AND entity_changes.entityName = '${entityName}'`,
|
||||||
|
({id, entityId}) => {
|
||||||
|
if (this.autoFix) {
|
||||||
|
sql.execute(`DELETE FROM ${entityName} WHERE ${key} = ?`, [entityId]);
|
||||||
|
|
||||||
|
this.reloadNeeded = true;
|
||||||
|
|
||||||
|
logFix(`Erasing entityName=${entityName}, entityId=${entityId} since entity change id=${id} has it as erased.`);
|
||||||
|
} else {
|
||||||
|
logError(`Entity change id=${id} has entityName=${entityName}, entityId=${entityId} as erased, but it's not.`);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
findEntityChangeIssues() {
|
findEntityChangeIssues() {
|
||||||
@@ -603,14 +625,14 @@ class ConsistencyChecks {
|
|||||||
this.fixedIssues = false;
|
this.fixedIssues = false;
|
||||||
this.reloadNeeded = false;
|
this.reloadNeeded = false;
|
||||||
|
|
||||||
|
this.findEntityChangeIssues();
|
||||||
|
|
||||||
this.findBrokenReferenceIssues();
|
this.findBrokenReferenceIssues();
|
||||||
|
|
||||||
this.findExistencyIssues();
|
this.findExistencyIssues();
|
||||||
|
|
||||||
this.findLogicIssues();
|
this.findLogicIssues();
|
||||||
|
|
||||||
this.findEntityChangeIssues();
|
|
||||||
|
|
||||||
this.findWronglyNamedAttributes();
|
this.findWronglyNamedAttributes();
|
||||||
|
|
||||||
this.findSyncIssues();
|
this.findSyncIssues();
|
||||||
@@ -701,6 +723,11 @@ function runOnDemandChecks(autoFix) {
|
|||||||
consistencyChecks.runChecks();
|
consistencyChecks.runChecks();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function runEntityChangesChecks() {
|
||||||
|
const consistencyChecks = new ConsistencyChecks(true);
|
||||||
|
consistencyChecks.findEntityChangeIssues();
|
||||||
|
}
|
||||||
|
|
||||||
sqlInit.dbReady.then(() => {
|
sqlInit.dbReady.then(() => {
|
||||||
setInterval(cls.wrap(runPeriodicChecks), 60 * 60 * 1000);
|
setInterval(cls.wrap(runPeriodicChecks), 60 * 60 * 1000);
|
||||||
|
|
||||||
@@ -709,5 +736,6 @@ sqlInit.dbReady.then(() => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
runOnDemandChecks
|
runOnDemandChecks,
|
||||||
|
runEntityChangesChecks
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -25,15 +25,6 @@ function createNote(parentNote, noteTitle) {
|
|||||||
}).note;
|
}).note;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getNoteStartingWith(parentNoteId, startsWith) {
|
|
||||||
const noteId = sql.getValue(`SELECT notes.noteId FROM notes JOIN branches USING(noteId)
|
|
||||||
WHERE parentNoteId = ? AND title LIKE '${startsWith}%'
|
|
||||||
AND notes.isDeleted = 0 AND isProtected = 0
|
|
||||||
AND branches.isDeleted = 0`, [parentNoteId]);
|
|
||||||
|
|
||||||
return becca.getNote(noteId);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @returns {Note} */
|
/** @returns {Note} */
|
||||||
function getRootCalendarNote() {
|
function getRootCalendarNote() {
|
||||||
let rootNote = attributeService.getNoteWithLabel(CALENDAR_ROOT_LABEL);
|
let rootNote = attributeService.getNoteWithLabel(CALENDAR_ROOT_LABEL);
|
||||||
@@ -65,8 +56,7 @@ function getYearNote(dateStr, rootNote) {
|
|||||||
|
|
||||||
const yearStr = dateStr.substr(0, 4);
|
const yearStr = dateStr.substr(0, 4);
|
||||||
|
|
||||||
let yearNote = attributeService.getNoteWithLabel(YEAR_LABEL, yearStr)
|
let yearNote = attributeService.getNoteWithLabel(YEAR_LABEL, yearStr);
|
||||||
|| getNoteStartingWith(rootNote.noteId, yearStr);
|
|
||||||
|
|
||||||
if (yearNote) {
|
if (yearNote) {
|
||||||
return yearNote;
|
return yearNote;
|
||||||
@@ -112,18 +102,12 @@ function getMonthNote(dateStr, rootNote) {
|
|||||||
return monthNote;
|
return monthNote;
|
||||||
}
|
}
|
||||||
|
|
||||||
const yearNote = getYearNote(dateStr, rootNote);
|
|
||||||
|
|
||||||
monthNote = getNoteStartingWith(yearNote.noteId, monthNumber);
|
|
||||||
|
|
||||||
if (monthNote) {
|
|
||||||
return monthNote;
|
|
||||||
}
|
|
||||||
|
|
||||||
const dateObj = dateUtils.parseLocalDate(dateStr);
|
const dateObj = dateUtils.parseLocalDate(dateStr);
|
||||||
|
|
||||||
const noteTitle = getMonthNoteTitle(rootNote, monthNumber, dateObj);
|
const noteTitle = getMonthNoteTitle(rootNote, monthNumber, dateObj);
|
||||||
|
|
||||||
|
const yearNote = getYearNote(dateStr, rootNote);
|
||||||
|
|
||||||
sql.transactional(() => {
|
sql.transactional(() => {
|
||||||
monthNote = createNote(yearNote, noteTitle);
|
monthNote = createNote(yearNote, noteTitle);
|
||||||
|
|
||||||
@@ -164,12 +148,6 @@ function getDateNote(dateStr) {
|
|||||||
const monthNote = getMonthNote(dateStr, rootNote);
|
const monthNote = getMonthNote(dateStr, rootNote);
|
||||||
const dayNumber = dateStr.substr(8, 2);
|
const dayNumber = dateStr.substr(8, 2);
|
||||||
|
|
||||||
dateNote = getNoteStartingWith(monthNote.noteId, dayNumber);
|
|
||||||
|
|
||||||
if (dateNote) {
|
|
||||||
return dateNote;
|
|
||||||
}
|
|
||||||
|
|
||||||
const dateObj = dateUtils.parseLocalDate(dateStr);
|
const dateObj = dateUtils.parseLocalDate(dateStr);
|
||||||
|
|
||||||
const noteTitle = getDateNoteTitle(rootNote, dayNumber, dateObj);
|
const noteTitle = getDateNoteTitle(rootNote, dayNumber, dateObj);
|
||||||
|
|||||||
@@ -7,12 +7,10 @@ const becca = require("../becca/becca");
|
|||||||
|
|
||||||
let maxEntityChangeId = 0;
|
let maxEntityChangeId = 0;
|
||||||
|
|
||||||
function addEntityChange(origEntityChange, keepOriginalId = false) {
|
function addEntityChange(origEntityChange) {
|
||||||
const ec = {...origEntityChange};
|
const ec = {...origEntityChange};
|
||||||
|
|
||||||
if (!keepOriginalId) {
|
delete ec.id;
|
||||||
delete ec.id;
|
|
||||||
}
|
|
||||||
|
|
||||||
ec.sourceId = ec.sourceId || cls.getSourceId() || sourceIdService.getCurrentSourceId();
|
ec.sourceId = ec.sourceId || cls.getSourceId() || sourceIdService.getCurrentSourceId();
|
||||||
ec.isSynced = ec.isSynced ? 1 : 0;
|
ec.isSynced = ec.isSynced ? 1 : 0;
|
||||||
|
|||||||
@@ -50,7 +50,13 @@ function exportToZip(taskContext, branch, format, res) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getDataFileName(note, baseFileName, existingFileNames) {
|
function getDataFileName(note, baseFileName, existingFileNames) {
|
||||||
const existingExtension = path.extname(baseFileName).toLowerCase();
|
let fileName = baseFileName;
|
||||||
|
|
||||||
|
if (fileName.length > 30) {
|
||||||
|
fileName = fileName.substr(0, 30);
|
||||||
|
}
|
||||||
|
|
||||||
|
let existingExtension = path.extname(fileName).toLowerCase();
|
||||||
let newExtension;
|
let newExtension;
|
||||||
|
|
||||||
// following two are handled specifically since we always want to have these extensions no matter the automatic detection
|
// following two are handled specifically since we always want to have these extensions no matter the automatic detection
|
||||||
@@ -68,13 +74,12 @@ function exportToZip(taskContext, branch, format, res) {
|
|||||||
newExtension = null;
|
newExtension = null;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
newExtension = mimeTypes.extension(note.mime) || "dat";
|
if (note.mime?.toLowerCase()?.trim() === "image/jpg") {
|
||||||
}
|
newExtension = 'jpg';
|
||||||
|
}
|
||||||
let fileName = baseFileName;
|
else {
|
||||||
|
newExtension = mimeTypes.extension(note.mime) || "dat";
|
||||||
if (fileName.length > 30) {
|
}
|
||||||
fileName = fileName.substr(0, 30);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// if the note is already named with extension (e.g. "jquery"), then it's silly to append exact same extension again
|
// if the note is already named with extension (e.g. "jquery"), then it's silly to append exact same extension again
|
||||||
|
|||||||
@@ -122,7 +122,11 @@ function saveImage(parentNoteId, uploadBuffer, originalName, shrinkImageSwitch,
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function shrinkImage(buffer, originalName) {
|
async function shrinkImage(buffer, originalName) {
|
||||||
const jpegQuality = optionService.getOptionInt('imageJpegQuality');
|
let jpegQuality = optionService.getOptionInt('imageJpegQuality');
|
||||||
|
|
||||||
|
if (jpegQuality < 10 || jpegQuality > 100) {
|
||||||
|
jpegQuality = 75;
|
||||||
|
}
|
||||||
|
|
||||||
let finalImageBuffer;
|
let finalImageBuffer;
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -351,7 +351,19 @@ async function importZip(taskContext, fileBuffer, importRootNote) {
|
|||||||
|
|
||||||
let note = becca.getNote(noteId);
|
let note = becca.getNote(noteId);
|
||||||
|
|
||||||
|
const isProtected = importRootNote.isProtected && protectedSessionService.isProtectedSessionAvailable();
|
||||||
|
|
||||||
if (note) {
|
if (note) {
|
||||||
|
// only skeleton was created because of altered order of cloned notes in ZIP, we need to update
|
||||||
|
// https://github.com/zadam/trilium/issues/2440
|
||||||
|
if (note.type === undefined) {
|
||||||
|
note.type = type;
|
||||||
|
note.mime = mime;
|
||||||
|
note.title = noteTitle;
|
||||||
|
note.isProtected = isProtected;
|
||||||
|
note.save();
|
||||||
|
}
|
||||||
|
|
||||||
note.setContent(content);
|
note.setContent(content);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -367,7 +379,7 @@ async function importZip(taskContext, fileBuffer, importRootNote) {
|
|||||||
// root notePosition should be ignored since it relates to original document
|
// root notePosition should be ignored since it relates to original document
|
||||||
// now import root should be placed after existing notes into new parent
|
// now import root should be placed after existing notes into new parent
|
||||||
notePosition: (noteMeta && firstNote) ? noteMeta.notePosition : undefined,
|
notePosition: (noteMeta && firstNote) ? noteMeta.notePosition : undefined,
|
||||||
isProtected: importRootNote.isProtected && protectedSessionService.isProtectedSessionAvailable(),
|
isProtected: isProtected,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
createdNoteIds[note.noteId] = true;
|
createdNoteIds[note.noteId] = true;
|
||||||
|
|||||||
@@ -732,23 +732,26 @@ function eraseAttributes(attributeIdsToErase) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function eraseDeletedEntities(eraseEntitiesAfterTimeInSeconds = null) {
|
function eraseDeletedEntities(eraseEntitiesAfterTimeInSeconds = null) {
|
||||||
if (eraseEntitiesAfterTimeInSeconds === null) {
|
// this is important also so that the erased entity changes are sent to the connected clients
|
||||||
eraseEntitiesAfterTimeInSeconds = optionService.getOptionInt('eraseEntitiesAfterTimeInSeconds');
|
sql.transactional(() => {
|
||||||
}
|
if (eraseEntitiesAfterTimeInSeconds === null) {
|
||||||
|
eraseEntitiesAfterTimeInSeconds = optionService.getOptionInt('eraseEntitiesAfterTimeInSeconds');
|
||||||
|
}
|
||||||
|
|
||||||
const cutoffDate = new Date(Date.now() - eraseEntitiesAfterTimeInSeconds * 1000);
|
const cutoffDate = new Date(Date.now() - eraseEntitiesAfterTimeInSeconds * 1000);
|
||||||
|
|
||||||
const noteIdsToErase = sql.getColumn("SELECT noteId FROM notes WHERE isDeleted = 1 AND utcDateModified <= ?", [dateUtils.utcDateTimeStr(cutoffDate)]);
|
const noteIdsToErase = sql.getColumn("SELECT noteId FROM notes WHERE isDeleted = 1 AND utcDateModified <= ?", [dateUtils.utcDateTimeStr(cutoffDate)]);
|
||||||
|
|
||||||
eraseNotes(noteIdsToErase);
|
eraseNotes(noteIdsToErase);
|
||||||
|
|
||||||
const branchIdsToErase = sql.getColumn("SELECT branchId FROM branches WHERE isDeleted = 1 AND utcDateModified <= ?", [dateUtils.utcDateTimeStr(cutoffDate)]);
|
const branchIdsToErase = sql.getColumn("SELECT branchId FROM branches WHERE isDeleted = 1 AND utcDateModified <= ?", [dateUtils.utcDateTimeStr(cutoffDate)]);
|
||||||
|
|
||||||
eraseBranches(branchIdsToErase);
|
eraseBranches(branchIdsToErase);
|
||||||
|
|
||||||
const attributeIdsToErase = sql.getColumn("SELECT attributeId FROM attributes WHERE isDeleted = 1 AND utcDateModified <= ?", [dateUtils.utcDateTimeStr(cutoffDate)]);
|
const attributeIdsToErase = sql.getColumn("SELECT attributeId FROM attributes WHERE isDeleted = 1 AND utcDateModified <= ?", [dateUtils.utcDateTimeStr(cutoffDate)]);
|
||||||
|
|
||||||
eraseAttributes(attributeIdsToErase);
|
eraseAttributes(attributeIdsToErase);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function eraseNotesWithDeleteId(deleteId) {
|
function eraseNotesWithDeleteId(deleteId) {
|
||||||
@@ -918,5 +921,6 @@ module.exports = {
|
|||||||
getUndeletedParentBranchIds,
|
getUndeletedParentBranchIds,
|
||||||
triggerNoteTitleChanged,
|
triggerNoteTitleChanged,
|
||||||
eraseDeletedNotesNow,
|
eraseDeletedNotesNow,
|
||||||
eraseNotesWithDeleteId
|
eraseNotesWithDeleteId,
|
||||||
|
saveNoteRevision
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -23,20 +23,18 @@ class AttributeExistsExp extends Expression {
|
|||||||
for (const attr of attrs) {
|
for (const attr of attrs) {
|
||||||
const note = attr.note;
|
const note = attr.note;
|
||||||
|
|
||||||
if (inputNoteSet.hasNoteId(note.noteId)) {
|
if (attr.isInheritable) {
|
||||||
if (attr.isInheritable) {
|
resultNoteSet.addAll(note.getSubtreeNotesIncludingTemplated());
|
||||||
resultNoteSet.addAll(note.getSubtreeNotesIncludingTemplated());
|
}
|
||||||
}
|
else if (note.isTemplate()) {
|
||||||
else if (note.isTemplate()) {
|
resultNoteSet.addAll(note.getTemplatedNotes());
|
||||||
resultNoteSet.addAll(note.getTemplatedNotes());
|
}
|
||||||
}
|
else {
|
||||||
else {
|
resultNoteSet.add(note);
|
||||||
resultNoteSet.add(note);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return resultNoteSet;
|
return resultNoteSet.intersection(inputNoteSet);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ const protectedSessionService = require('../../protected_session');
|
|||||||
const striptags = require('striptags');
|
const striptags = require('striptags');
|
||||||
const utils = require("../../utils");
|
const utils = require("../../utils");
|
||||||
|
|
||||||
|
// FIXME: create common subclass with NoteContentUnprotectedFulltextExp to avoid duplication
|
||||||
class NoteContentProtectedFulltextExp extends Expression {
|
class NoteContentProtectedFulltextExp extends Expression {
|
||||||
constructor(operator, tokens, raw) {
|
constructor(operator, tokens, raw) {
|
||||||
super();
|
super();
|
||||||
@@ -46,15 +47,7 @@ class NoteContentProtectedFulltextExp extends Expression {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
content = utils.normalize(content);
|
content = this.preprocessContent(content, type, mime);
|
||||||
|
|
||||||
if (type === 'text' && mime === 'text/html') {
|
|
||||||
if (!this.raw && content.length < 20000) { // striptags is slow for very large notes
|
|
||||||
content = striptags(content);
|
|
||||||
}
|
|
||||||
|
|
||||||
content = content.replace(/ /g, ' ');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this.tokens.find(token => !content.includes(token))) {
|
if (!this.tokens.find(token => !content.includes(token))) {
|
||||||
resultNoteSet.add(becca.notes[noteId]);
|
resultNoteSet.add(becca.notes[noteId]);
|
||||||
@@ -63,6 +56,23 @@ class NoteContentProtectedFulltextExp extends Expression {
|
|||||||
|
|
||||||
return resultNoteSet;
|
return resultNoteSet;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
preprocessContent(content, type, mime) {
|
||||||
|
content = utils.normalize(content.toString());
|
||||||
|
|
||||||
|
if (type === 'text' && mime === 'text/html') {
|
||||||
|
if (!this.raw && content.length < 20000) { // striptags is slow for very large notes
|
||||||
|
// allow link to preserve URLs: https://github.com/zadam/trilium/issues/2412
|
||||||
|
content = striptags(content, ['a']);
|
||||||
|
|
||||||
|
// at least the closing tag can be easily stripped
|
||||||
|
content = content.replace(/<\/a>/ig, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
content = content.replace(/ /g, ' ');
|
||||||
|
}
|
||||||
|
return content;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = NoteContentProtectedFulltextExp;
|
module.exports = NoteContentProtectedFulltextExp;
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ const becca = require('../../../becca/becca');
|
|||||||
const striptags = require('striptags');
|
const striptags = require('striptags');
|
||||||
const utils = require("../../utils");
|
const utils = require("../../utils");
|
||||||
|
|
||||||
|
// FIXME: create common subclass with NoteContentProtectedFulltextExp to avoid duplication
|
||||||
class NoteContentUnprotectedFulltextExp extends Expression {
|
class NoteContentUnprotectedFulltextExp extends Expression {
|
||||||
constructor(operator, tokens, raw) {
|
constructor(operator, tokens, raw) {
|
||||||
super();
|
super();
|
||||||
@@ -32,15 +33,7 @@ class NoteContentUnprotectedFulltextExp extends Expression {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
content = utils.normalize(content.toString());
|
content = this.preprocessContent(content, type, mime);
|
||||||
|
|
||||||
if (type === 'text' && mime === 'text/html') {
|
|
||||||
if (!this.raw && content.length < 20000) { // striptags is slow for very large notes
|
|
||||||
content = striptags(content);
|
|
||||||
}
|
|
||||||
|
|
||||||
content = content.replace(/ /g, ' ');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this.tokens.find(token => !content.includes(token))) {
|
if (!this.tokens.find(token => !content.includes(token))) {
|
||||||
resultNoteSet.add(becca.notes[noteId]);
|
resultNoteSet.add(becca.notes[noteId]);
|
||||||
@@ -49,6 +42,23 @@ class NoteContentUnprotectedFulltextExp extends Expression {
|
|||||||
|
|
||||||
return resultNoteSet;
|
return resultNoteSet;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
preprocessContent(content, type, mime) {
|
||||||
|
content = utils.normalize(content.toString());
|
||||||
|
|
||||||
|
if (type === 'text' && mime === 'text/html') {
|
||||||
|
if (!this.raw && content.length < 20000) { // striptags is slow for very large notes
|
||||||
|
// allow link to preserve URLs: https://github.com/zadam/trilium/issues/2412
|
||||||
|
content = striptags(content, ['a']);
|
||||||
|
|
||||||
|
// at least the closing tag can be easily stripped
|
||||||
|
content = content.replace(/<\/a>/ig, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
content = content.replace(/ /g, ' ');
|
||||||
|
}
|
||||||
|
return content;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = NoteContentUnprotectedFulltextExp;
|
module.exports = NoteContentUnprotectedFulltextExp;
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ const RelationWhereExp = require('../expressions/relation_where');
|
|||||||
const PropertyComparisonExp = require('../expressions/property_comparison');
|
const PropertyComparisonExp = require('../expressions/property_comparison');
|
||||||
const AttributeExistsExp = require('../expressions/attribute_exists');
|
const AttributeExistsExp = require('../expressions/attribute_exists');
|
||||||
const LabelComparisonExp = require('../expressions/label_comparison');
|
const LabelComparisonExp = require('../expressions/label_comparison');
|
||||||
const BeccaFlatTextExp = require('../expressions/note_cache_flat_text');
|
const NoteFlatTextExp = require('../expressions/note_flat_text.js');
|
||||||
const NoteContentProtectedFulltextExp = require('../expressions/note_content_protected_fulltext');
|
const NoteContentProtectedFulltextExp = require('../expressions/note_content_protected_fulltext');
|
||||||
const NoteContentUnprotectedFulltextExp = require('../expressions/note_content_unprotected_fulltext');
|
const NoteContentUnprotectedFulltextExp = require('../expressions/note_content_unprotected_fulltext');
|
||||||
const OrderByAndLimitExp = require('../expressions/order_by_and_limit');
|
const OrderByAndLimitExp = require('../expressions/order_by_and_limit');
|
||||||
@@ -31,13 +31,13 @@ function getFulltext(tokens, searchContext) {
|
|||||||
|
|
||||||
if (!searchContext.fastSearch) {
|
if (!searchContext.fastSearch) {
|
||||||
return new OrExp([
|
return new OrExp([
|
||||||
new BeccaFlatTextExp(tokens),
|
new NoteFlatTextExp(tokens),
|
||||||
new NoteContentProtectedFulltextExp('*=*', tokens),
|
new NoteContentProtectedFulltextExp('*=*', tokens),
|
||||||
new NoteContentUnprotectedFulltextExp('*=*', tokens)
|
new NoteContentUnprotectedFulltextExp('*=*', tokens)
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return new BeccaFlatTextExp(tokens);
|
return new NoteFlatTextExp(tokens);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -83,8 +83,12 @@ function findResultsWithExpression(expression, searchContext) {
|
|||||||
throw new Error(`Can't find note path for note ${JSON.stringify(note.getPojo())}`);
|
throw new Error(`Can't find note path for note ${JSON.stringify(note.getPojo())}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (notePathArray.includes("hidden")) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
return new SearchResult(notePathArray);
|
return new SearchResult(notePathArray);
|
||||||
});
|
}).filter(Boolean);
|
||||||
|
|
||||||
for (const res of searchResults) {
|
for (const res of searchResults) {
|
||||||
res.computeScore(searchContext.highlightedTokens);
|
res.computeScore(searchContext.highlightedTokens);
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ function getHiddenRoot() {
|
|||||||
|
|
||||||
if (!hidden) {
|
if (!hidden) {
|
||||||
hidden = noteService.createNewNote({
|
hidden = noteService.createNewNote({
|
||||||
|
branchId: 'hidden',
|
||||||
noteId: 'hidden',
|
noteId: 'hidden',
|
||||||
title: 'hidden',
|
title: 'hidden',
|
||||||
type: 'text',
|
type: 'text',
|
||||||
@@ -44,6 +45,7 @@ function getHiddenRoot() {
|
|||||||
// isInheritable: false means that this notePath is automatically not preffered but at the same time
|
// isInheritable: false means that this notePath is automatically not preffered but at the same time
|
||||||
// the flag is not inherited to the children
|
// the flag is not inherited to the children
|
||||||
hidden.addLabel('archived', "", false);
|
hidden.addLabel('archived', "", false);
|
||||||
|
hidden.addLabel('excludeFromNoteMap', "", true);
|
||||||
}
|
}
|
||||||
|
|
||||||
return hidden;
|
return hidden;
|
||||||
@@ -206,6 +208,12 @@ function createMissingSpecialNotes() {
|
|||||||
getSinglesNoteRoot();
|
getSinglesNoteRoot();
|
||||||
getSinglesNoteRoot();
|
getSinglesNoteRoot();
|
||||||
getGlobalNoteMap();
|
getGlobalNoteMap();
|
||||||
|
|
||||||
|
const hidden = getHiddenRoot();
|
||||||
|
|
||||||
|
if (!hidden.hasOwnedLabel('excludeFromNoteMap')) {
|
||||||
|
hidden.addLabel('excludeFromNoteMap', "", true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
|||||||
@@ -149,7 +149,10 @@ async function pullChanges(syncContext) {
|
|||||||
|
|
||||||
sql.transactional(() => {
|
sql.transactional(() => {
|
||||||
for (const {entityChange, entity} of entityChanges) {
|
for (const {entityChange, entity} of entityChanges) {
|
||||||
if (!sourceIdService.isLocalSourceId(entityChange.sourceId)) {
|
// FIXME: temporary fix
|
||||||
|
const existsAlready = !!sql.getValue("SELECT id FROM entity_changes WHERE entityName = ? AND entityId = ? AND utcDateChanged = ? AND hash = ?", [entityChange.entityName, entityChange.entityId, entityChange.utcDateChanged, entityChange.hash]);
|
||||||
|
|
||||||
|
if (!existsAlready && !sourceIdService.isLocalSourceId(entityChange.sourceId)) {
|
||||||
if (!atLeastOnePullApplied) { // send only for first
|
if (!atLeastOnePullApplied) { // send only for first
|
||||||
ws.syncPullInProgress();
|
ws.syncPullInProgress();
|
||||||
|
|
||||||
@@ -249,6 +252,14 @@ async function checkContentHash(syncContext) {
|
|||||||
|
|
||||||
const failedChecks = contentHashService.checkContentHashes(resp.entityHashes);
|
const failedChecks = contentHashService.checkContentHashes(resp.entityHashes);
|
||||||
|
|
||||||
|
if (failedChecks.length > 0) {
|
||||||
|
// before requeuing sectors make sure the entity changes are correct
|
||||||
|
const consistencyChecks = require("./consistency_checks");
|
||||||
|
consistencyChecks.runEntityChangesChecks();
|
||||||
|
|
||||||
|
await syncRequest(syncContext, 'POST', `/api/sync/check-entity-changes`);
|
||||||
|
}
|
||||||
|
|
||||||
for (const {entityName, sector} of failedChecks) {
|
for (const {entityName, sector} of failedChecks) {
|
||||||
entityChangesService.addEntityChangesForSector(entityName, sector);
|
entityChangesService.addEntityChangesForSector(entityName, sector);
|
||||||
|
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ function updateNormalEntity(remoteEntityChange, entity) {
|
|||||||
|
|
||||||
sql.execute(`DELETE FROM ${remoteEntityChange.entityName} WHERE ${primaryKey} = ?`, remoteEntityChange.entityId);
|
sql.execute(`DELETE FROM ${remoteEntityChange.entityName} WHERE ${primaryKey} = ?`, remoteEntityChange.entityId);
|
||||||
|
|
||||||
entityChangesService.addEntityChange(remoteEntityChange, true);
|
entityChangesService.addEntityChange(remoteEntityChange);
|
||||||
});
|
});
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -71,7 +71,7 @@ function updateNormalEntity(remoteEntityChange, entity) {
|
|||||||
sql.transactional(() => {
|
sql.transactional(() => {
|
||||||
sql.replace(remoteEntityChange.entityName, entity);
|
sql.replace(remoteEntityChange.entityName, entity);
|
||||||
|
|
||||||
entityChangesService.addEntityChange(remoteEntityChange, true);
|
entityChangesService.addEntityChange(remoteEntityChange);
|
||||||
});
|
});
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -86,7 +86,7 @@ function updateNoteReordering(entityChange, entity) {
|
|||||||
sql.execute("UPDATE branches SET notePosition = ? WHERE branchId = ?", [entity[key], key]);
|
sql.execute("UPDATE branches SET notePosition = ? WHERE branchId = ?", [entity[key], key]);
|
||||||
}
|
}
|
||||||
|
|
||||||
entityChangesService.addEntityChange(entityChange, true);
|
entityChangesService.addEntityChange(entityChange);
|
||||||
});
|
});
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
Reference in New Issue
Block a user