Compare commits

..

10 Commits

Author SHA1 Message Date
zadam
aedb05cbab release 0.46.8 2021-04-18 21:39:01 +02:00
zadam
bb16840a72 release 0.46.8 2021-04-18 21:30:43 +02:00
zadam
7b5d44a329 use official gh client instead of github-release to execute releases 2021-04-18 21:29:10 +02:00
zadam
a53a65be1f clipper fix, closes #1840 2021-04-12 23:29:02 +02:00
zadam
44af431a93 ctrl + click / middle click now opens image in a text note in a new tab 2021-04-11 22:24:21 +02:00
zadam
caa11b8f7e fix inheriting inheritable attributes through template, closes #1828 2021-04-05 20:52:19 +02:00
zadam
41cce4dcb9 improved task manager with allowed year range, #1823 2021-04-04 23:57:55 +02:00
zadam
858072cc10 fix lexer to parse correctly quoted empty strings, #1825 2021-04-04 23:13:18 +02:00
zadam
855c5e0e67 fix unit tests 2021-04-04 22:44:22 +02:00
zadam
f0cc3d0bcd use entity changes instead of actual tables to fill in sector to sync, fixes #1809 2021-04-04 22:02:40 +02:00
21 changed files with 165 additions and 124 deletions

1
.idea/vcs.xml generated
View File

@@ -2,6 +2,5 @@
<project version="4"> <project version="4">
<component name="VcsDirectoryMappings"> <component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" /> <mapping directory="" vcs="Git" />
<mapping directory="$PROJECT_DIR$/../.." vcs="Git" />
</component> </component>
</project> </project>

View File

@@ -55,47 +55,20 @@ echo "Creating release in GitHub"
EXTRA= EXTRA=
if [[ $TAG == *"beta"* ]]; then if [[ $TAG == *"beta"* ]]; then
EXTRA=--pre-release EXTRA=--prerelease
fi fi
github-release release \ echo "$GITHUB_CLI_AUTH_TOKEN" | gh auth login --with-token
--tag $TAG \
--name "$TAG release" $EXTRA
echo "Uploading debian x64 package" gh release create "$TAG" \
--title "$TAG release" \
github-release upload \ --notes ""
--tag $TAG \ $EXTRA \
--name "$DEBIAN_X64_BUILD" \ "dist/$DEBIAN_X64_BUILD" \
--file "dist/$DEBIAN_X64_BUILD" "dist/$LINUX_X64_BUILD" \
"dist/$WINDOWS_X64_BUILD" \
echo "Uploading linux x64 build" "dist/$MAC_X64_BUILD" \
"dist/$SERVER_BUILD"
github-release upload \
--tag $TAG \
--name "$LINUX_X64_BUILD" \
--file "dist/$LINUX_X64_BUILD"
echo "Uploading windows x64 build"
github-release upload \
--tag $TAG \
--name "$WINDOWS_X64_BUILD" \
--file "dist/$WINDOWS_X64_BUILD"
echo "Uploading mac x64 build"
github-release upload \
--tag $TAG \
--name "$MAC_X64_BUILD" \
--file "dist/$MAC_X64_BUILD"
echo "Uploading linux x64 server build"
github-release upload \
--tag $TAG \
--name "$SERVER_BUILD" \
--file "dist/$SERVER_BUILD"
echo "Building docker image" echo "Building docker image"

Binary file not shown.

2
package-lock.json generated
View File

@@ -1,6 +1,6 @@
{ {
"name": "trilium", "name": "trilium",
"version": "0.46.5", "version": "0.46.7",
"lockfileVersion": 1, "lockfileVersion": 1,
"requires": true, "requires": true,
"dependencies": { "dependencies": {

View File

@@ -51,6 +51,7 @@
"is-animated": "^2.0.1", "is-animated": "^2.0.1",
"is-svg": "4.2.1", "is-svg": "4.2.1",
"jimp": "0.16.1", "jimp": "0.16.1",
"joplin-turndown-plugin-gfm": "1.0.12",
"jsdom": "16.5.0", "jsdom": "16.5.0",
"mime-types": "2.1.29", "mime-types": "2.1.29",
"multer": "1.4.2", "multer": "1.4.2",
@@ -70,7 +71,6 @@
"striptags": "3.1.1", "striptags": "3.1.1",
"tmp": "^0.2.1", "tmp": "^0.2.1",
"turndown": "7.0.0", "turndown": "7.0.0",
"joplin-turndown-plugin-gfm": "1.0.12",
"unescape": "1.0.1", "unescape": "1.0.1",
"ws": "7.4.4", "ws": "7.4.4",
"yauzl": "2.10.0", "yauzl": "2.10.0",

View File

@@ -87,14 +87,16 @@ describe("Lexer expression", () => {
.toEqual(["#label", "*=*", "text"]); .toEqual(["#label", "*=*", "text"]);
}); });
it("simple label operator with in quotes and without", () => { it("simple label operator with in quotes", () => {
expect(lex("#label*=*'text'").expressionTokens) expect(lex("#label*=*'text'").expressionTokens)
.toEqual([ .toEqual([
{token: "#label", inQuotes: false, startIndex: 0, endIndex: 5}, {token: "#label", inQuotes: false, startIndex: 0, endIndex: 5},
{token: "*=*", inQuotes: false, startIndex: 6, endIndex: 8}, {token: "*=*", inQuotes: false, startIndex: 6, endIndex: 8},
{token: "text", inQuotes: true, startIndex: 10, endIndex: 13} {token: "text", inQuotes: true, startIndex: 10, endIndex: 13}
]); ]);
});
it("simple label operator with param without quotes", () => {
expect(lex("#label*=*text").expressionTokens) expect(lex("#label*=*text").expressionTokens)
.toEqual([ .toEqual([
{token: "#label", inQuotes: false, startIndex: 0, endIndex: 5}, {token: "#label", inQuotes: false, startIndex: 0, endIndex: 5},
@@ -103,6 +105,16 @@ describe("Lexer expression", () => {
]); ]);
}); });
it("simple label operator with empty string param", () => {
expect(lex("#label = ''").expressionTokens)
.toEqual([
{token: "#label", inQuotes: false, startIndex: 0, endIndex: 5},
{token: "=", inQuotes: false, startIndex: 7, endIndex: 7},
// weird case for empty strings which ends up with endIndex < startIndex :-(
{token: "", inQuotes: true, startIndex: 10, endIndex: 9}
]);
});
it("note. prefix also separates fulltext from expression", () => { it("note. prefix also separates fulltext from expression", () => {
expect(lex(`hello fulltext note.labels.capital = Prague`).expressionTokens.map(t => t.token)) expect(lex(`hello fulltext note.labels.capital = Prague`).expressionTokens.map(t => t.token))
.toEqual(["note", ".", "labels", ".", "capital", "=", "prague"]); .toEqual(["note", ".", "labels", ".", "capital", "=", "prague"]);

View File

@@ -19,6 +19,13 @@ function tokens(toks, cur = 0) {
}); });
} }
function assertIsArchived(exp) {
expect(exp.constructor.name).toEqual("PropertyComparisonExp");
expect(exp.propertyName).toEqual("isArchived");
expect(exp.operator).toEqual("=");
expect(exp.comparedValue).toEqual("false");
}
describe("Parser", () => { describe("Parser", () => {
it("fulltext parser without content", () => { it("fulltext parser without content", () => {
const rootExp = parse({ const rootExp = parse({
@@ -29,8 +36,9 @@ 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("NoteCacheFlatTextExp"); expect(rootExp.subExpressions[1].constructor.name).toEqual("OrExp");
expect(rootExp.subExpressions[1].tokens).toEqual(["hello", "hi"]); expect(rootExp.subExpressions[1].subExpressions[0].constructor.name).toEqual("NoteCacheFlatTextExp");
expect(rootExp.subExpressions[1].subExpressions[0].tokens).toEqual(["hello", "hi"]);
}); });
it("fulltext parser with content", () => { it("fulltext parser with content", () => {
@@ -40,9 +48,12 @@ describe("Parser", () => {
searchContext: new SearchContext({includeNoteContent: true}) searchContext: new SearchContext({includeNoteContent: true})
}); });
expect(rootExp.constructor.name).toEqual("OrExp"); expect(rootExp.constructor.name).toEqual("AndExp");
assertIsArchived(rootExp.subExpressions[0]);
const subs = rootExp.subExpressions; expect(rootExp.subExpressions[1].constructor.name).toEqual("OrExp");
const subs = rootExp.subExpressions[1].subExpressions;
expect(subs[0].constructor.name).toEqual("NoteCacheFlatTextExp"); expect(subs[0].constructor.name).toEqual("NoteCacheFlatTextExp");
expect(subs[0].tokens).toEqual(["hello", "hi"]); expect(subs[0].tokens).toEqual(["hello", "hi"]);
@@ -61,10 +72,12 @@ describe("Parser", () => {
searchContext: new SearchContext() searchContext: new SearchContext()
}); });
expect(rootExp.constructor.name).toEqual("LabelComparisonExp"); expect(rootExp.constructor.name).toEqual("AndExp");
expect(rootExp.attributeType).toEqual("label"); assertIsArchived(rootExp.subExpressions[0]);
expect(rootExp.attributeName).toEqual("mylabel"); expect(rootExp.subExpressions[1].constructor.name).toEqual("LabelComparisonExp");
expect(rootExp.comparator).toBeTruthy(); expect(rootExp.subExpressions[1].attributeType).toEqual("label");
expect(rootExp.subExpressions[1].attributeName).toEqual("mylabel");
expect(rootExp.subExpressions[1].comparator).toBeTruthy();
}); });
it("simple attribute negation", () => { it("simple attribute negation", () => {
@@ -74,10 +87,12 @@ describe("Parser", () => {
searchContext: new SearchContext() searchContext: new SearchContext()
}); });
expect(rootExp.constructor.name).toEqual("NotExp"); expect(rootExp.constructor.name).toEqual("AndExp");
expect(rootExp.subExpression.constructor.name).toEqual("AttributeExistsExp"); assertIsArchived(rootExp.subExpressions[0]);
expect(rootExp.subExpression.attributeType).toEqual("label"); expect(rootExp.subExpressions[1].constructor.name).toEqual("NotExp");
expect(rootExp.subExpression.attributeName).toEqual("mylabel"); expect(rootExp.subExpressions[1].subExpression.constructor.name).toEqual("AttributeExistsExp");
expect(rootExp.subExpressions[1].subExpression.attributeType).toEqual("label");
expect(rootExp.subExpressions[1].subExpression.attributeName).toEqual("mylabel");
rootExp = parse({ rootExp = parse({
fulltextTokens: [], fulltextTokens: [],
@@ -85,10 +100,12 @@ describe("Parser", () => {
searchContext: new SearchContext() searchContext: new SearchContext()
}); });
expect(rootExp.constructor.name).toEqual("NotExp"); expect(rootExp.constructor.name).toEqual("AndExp");
expect(rootExp.subExpression.constructor.name).toEqual("AttributeExistsExp"); assertIsArchived(rootExp.subExpressions[0]);
expect(rootExp.subExpression.attributeType).toEqual("relation"); expect(rootExp.subExpressions[1].constructor.name).toEqual("NotExp");
expect(rootExp.subExpression.attributeName).toEqual("myrelation"); expect(rootExp.subExpressions[1].subExpression.constructor.name).toEqual("AttributeExistsExp");
expect(rootExp.subExpressions[1].subExpression.attributeType).toEqual("relation");
expect(rootExp.subExpressions[1].subExpression.attributeName).toEqual("myrelation");
}); });
it("simple label AND", () => { it("simple label AND", () => {
@@ -99,7 +116,10 @@ describe("Parser", () => {
}); });
expect(rootExp.constructor.name).toEqual("AndExp"); expect(rootExp.constructor.name).toEqual("AndExp");
const [firstSub, secondSub] = rootExp.subExpressions; assertIsArchived(rootExp.subExpressions[0]);
expect(rootExp.subExpressions[1].constructor.name).toEqual("AndExp");
const [firstSub, secondSub] = rootExp.subExpressions[1].subExpressions;
expect(firstSub.constructor.name).toEqual("LabelComparisonExp"); expect(firstSub.constructor.name).toEqual("LabelComparisonExp");
expect(firstSub.attributeName).toEqual("first"); expect(firstSub.attributeName).toEqual("first");
@@ -116,7 +136,10 @@ describe("Parser", () => {
}); });
expect(rootExp.constructor.name).toEqual("AndExp"); expect(rootExp.constructor.name).toEqual("AndExp");
const [firstSub, secondSub] = rootExp.subExpressions; assertIsArchived(rootExp.subExpressions[0]);
expect(rootExp.subExpressions[1].constructor.name).toEqual("AndExp");
const [firstSub, secondSub] = rootExp.subExpressions[1].subExpressions;
expect(firstSub.constructor.name).toEqual("LabelComparisonExp"); expect(firstSub.constructor.name).toEqual("LabelComparisonExp");
expect(firstSub.attributeName).toEqual("first"); expect(firstSub.attributeName).toEqual("first");
@@ -132,8 +155,11 @@ describe("Parser", () => {
searchContext: new SearchContext() searchContext: new SearchContext()
}); });
expect(rootExp.constructor.name).toEqual("OrExp"); expect(rootExp.constructor.name).toEqual("AndExp");
const [firstSub, secondSub] = rootExp.subExpressions; assertIsArchived(rootExp.subExpressions[0]);
expect(rootExp.subExpressions[1].constructor.name).toEqual("OrExp");
const [firstSub, secondSub] = rootExp.subExpressions[1].subExpressions;
expect(firstSub.constructor.name).toEqual("LabelComparisonExp"); expect(firstSub.constructor.name).toEqual("LabelComparisonExp");
expect(firstSub.attributeName).toEqual("first"); expect(firstSub.attributeName).toEqual("first");
@@ -155,8 +181,9 @@ describe("Parser", () => {
expect(firstSub.constructor.name).toEqual("PropertyComparisonExp"); expect(firstSub.constructor.name).toEqual("PropertyComparisonExp");
expect(firstSub.propertyName).toEqual('isArchived'); expect(firstSub.propertyName).toEqual('isArchived');
expect(secondSub.constructor.name).toEqual("NoteCacheFlatTextExp"); expect(secondSub.constructor.name).toEqual("OrExp");
expect(secondSub.tokens).toEqual(["hello"]); expect(secondSub.subExpressions[0].constructor.name).toEqual("NoteCacheFlatTextExp");
expect(secondSub.subExpressions[0].tokens).toEqual(["hello"]);
expect(thirdSub.constructor.name).toEqual("LabelComparisonExp"); expect(thirdSub.constructor.name).toEqual("LabelComparisonExp");
expect(thirdSub.attributeName).toEqual("mylabel"); expect(thirdSub.attributeName).toEqual("mylabel");
@@ -169,8 +196,11 @@ describe("Parser", () => {
searchContext: new SearchContext() searchContext: new SearchContext()
}); });
expect(rootExp.constructor.name).toEqual("OrExp"); expect(rootExp.constructor.name).toEqual("AndExp");
const [firstSub, secondSub] = rootExp.subExpressions; assertIsArchived(rootExp.subExpressions[0]);
expect(rootExp.subExpressions[1].constructor.name).toEqual("OrExp");
const [firstSub, secondSub] = rootExp.subExpressions[1].subExpressions;
expect(firstSub.constructor.name).toEqual("LabelComparisonExp"); expect(firstSub.constructor.name).toEqual("LabelComparisonExp");
expect(firstSub.attributeName).toEqual("first"); expect(firstSub.attributeName).toEqual("first");
@@ -232,10 +262,12 @@ describe("Invalid expressions", () => {
searchContext: new SearchContext() searchContext: new SearchContext()
}); });
expect(rootExp.constructor.name).toEqual("LabelComparisonExp"); expect(rootExp.constructor.name).toEqual("AndExp");
expect(rootExp.attributeType).toEqual("label"); assertIsArchived(rootExp.subExpressions[0]);
expect(rootExp.attributeName).toEqual("first"); expect(rootExp.subExpressions[1].constructor.name).toEqual("LabelComparisonExp");
expect(rootExp.comparator).toBeTruthy(); expect(rootExp.subExpressions[1].attributeType).toEqual("label");
expect(rootExp.subExpressions[1].attributeName).toEqual("first");
expect(rootExp.subExpressions[1].comparator).toBeTruthy();
}); });
it("searching by relation without note property", () => { it("searching by relation without note property", () => {

View File

@@ -562,8 +562,8 @@ describe("Search", () => {
expect(noteCache.notes[searchResults[0].noteId].title).toEqual("Austria"); expect(noteCache.notes[searchResults[0].noteId].title).toEqual("Austria");
expect(noteCache.notes[searchResults[1].noteId].title).toEqual("Italy"); expect(noteCache.notes[searchResults[1].noteId].title).toEqual("Italy");
searchResults = searchService.findNotesWithQuery('# note.parents.title = Europe orderBy #capital DESC limit 0', searchContext); searchResults = searchService.findNotesWithQuery('# note.parents.title = Europe orderBy #capital DESC limit 1', searchContext);
expect(searchResults.length).toEqual(0); expect(searchResults.length).toEqual(1);
searchResults = searchService.findNotesWithQuery('# note.parents.title = Europe orderBy #capital DESC limit 1000', searchContext); searchResults = searchService.findNotesWithQuery('# note.parents.title = Europe orderBy #capital DESC limit 1000', searchContext);
expect(searchResults.length).toEqual(4); expect(searchResults.length).toEqual(4);

View File

@@ -1,6 +1,9 @@
const {note} = require('./note_cache_mocking.js'); const {note} = require('./note_cache_mocking.js');
const ValueExtractor = require('../../src/services/search/value_extractor.js'); const ValueExtractor = require('../../src/services/search/value_extractor.js');
const noteCache = require('../../src/services/note_cache/note_cache.js'); const noteCache = require('../../src/services/note_cache/note_cache.js');
const SearchContext = require("../../src/services/search/search_context.js");
const dsc = new SearchContext();
describe("Value extractor", () => { describe("Value extractor", () => {
beforeEach(() => { beforeEach(() => {
@@ -10,7 +13,7 @@ describe("Value extractor", () => {
it("simple title extraction", async () => { it("simple title extraction", async () => {
const europe = note("Europe").note; const europe = note("Europe").note;
const valueExtractor = new ValueExtractor(["note", "title"]); const valueExtractor = new ValueExtractor(dsc, ["note", "title"]);
expect(valueExtractor.validate()).toBeFalsy(); expect(valueExtractor.validate()).toBeFalsy();
expect(valueExtractor.extract(europe)).toEqual("Europe"); expect(valueExtractor.extract(europe)).toEqual("Europe");
@@ -21,12 +24,12 @@ describe("Value extractor", () => {
.label("Capital", "Vienna") .label("Capital", "Vienna")
.note; .note;
let valueExtractor = new ValueExtractor(["note", "labels", "capital"]); let valueExtractor = new ValueExtractor(dsc, ["note", "labels", "capital"]);
expect(valueExtractor.validate()).toBeFalsy(); expect(valueExtractor.validate()).toBeFalsy();
expect(valueExtractor.extract(austria)).toEqual("Vienna"); expect(valueExtractor.extract(austria)).toEqual("Vienna");
valueExtractor = new ValueExtractor(["#capital"]); valueExtractor = new ValueExtractor(dsc, ["#capital"]);
expect(valueExtractor.validate()).toBeFalsy(); expect(valueExtractor.validate()).toBeFalsy();
expect(valueExtractor.extract(austria)).toEqual("Vienna"); expect(valueExtractor.extract(austria)).toEqual("Vienna");
@@ -38,12 +41,12 @@ describe("Value extractor", () => {
.child(note("Austria") .child(note("Austria")
.child(vienna)); .child(vienna));
let valueExtractor = new ValueExtractor(["note", "children", "children", "title"]); let valueExtractor = new ValueExtractor(dsc, ["note", "children", "children", "title"]);
expect(valueExtractor.validate()).toBeFalsy(); expect(valueExtractor.validate()).toBeFalsy();
expect(valueExtractor.extract(europe.note)).toEqual("Vienna"); expect(valueExtractor.extract(europe.note)).toEqual("Vienna");
valueExtractor = new ValueExtractor(["note", "parents", "parents", "title"]); valueExtractor = new ValueExtractor(dsc, ["note", "parents", "parents", "title"]);
expect(valueExtractor.validate()).toBeFalsy(); expect(valueExtractor.validate()).toBeFalsy();
expect(valueExtractor.extract(vienna.note)).toEqual("Europe"); expect(valueExtractor.extract(vienna.note)).toEqual("Europe");
@@ -56,12 +59,12 @@ describe("Value extractor", () => {
.relation('neighbor', czechRepublic.note) .relation('neighbor', czechRepublic.note)
.relation('neighbor', slovakia.note); .relation('neighbor', slovakia.note);
let valueExtractor = new ValueExtractor(["note", "relations", "neighbor", "labels", "capital"]); let valueExtractor = new ValueExtractor(dsc, ["note", "relations", "neighbor", "labels", "capital"]);
expect(valueExtractor.validate()).toBeFalsy(); expect(valueExtractor.validate()).toBeFalsy();
expect(valueExtractor.extract(austria.note)).toEqual("Prague"); expect(valueExtractor.extract(austria.note)).toEqual("Prague");
valueExtractor = new ValueExtractor(["~neighbor", "labels", "capital"]); valueExtractor = new ValueExtractor(dsc, ["~neighbor", "labels", "capital"]);
expect(valueExtractor.validate()).toBeFalsy(); expect(valueExtractor.validate()).toBeFalsy();
expect(valueExtractor.extract(austria.note)).toEqual("Prague"); expect(valueExtractor.extract(austria.note)).toEqual("Prague");
@@ -70,17 +73,17 @@ describe("Value extractor", () => {
describe("Invalid value extractor property path", () => { describe("Invalid value extractor property path", () => {
it('each path must start with "note" (or label/relation)', it('each path must start with "note" (or label/relation)',
() => expect(new ValueExtractor(["neighbor"]).validate()).toBeTruthy()); () => expect(new ValueExtractor(dsc, ["neighbor"]).validate()).toBeTruthy());
it("extra path element after terminal label", it("extra path element after terminal label",
() => expect(new ValueExtractor(["~neighbor", "labels", "capital", "noteId"]).validate()).toBeTruthy()); () => expect(new ValueExtractor(dsc, ["~neighbor", "labels", "capital", "noteId"]).validate()).toBeTruthy());
it("extra path element after terminal title", it("extra path element after terminal title",
() => expect(new ValueExtractor(["note", "title", "isProtected"]).validate()).toBeTruthy()); () => expect(new ValueExtractor(dsc, ["note", "title", "isProtected"]).validate()).toBeTruthy());
it("relation name and note property is missing", it("relation name and note property is missing",
() => expect(new ValueExtractor(["note", "relations"]).validate()).toBeTruthy()); () => expect(new ValueExtractor(dsc, ["note", "relations"]).validate()).toBeTruthy());
it("relation is specified but target note property is not specified", it("relation is specified but target note property is not specified",
() => expect(new ValueExtractor(["note", "relations", "myrel"]).validate()).toBeTruthy()); () => expect(new ValueExtractor(dsc, ["note", "relations", "myrel"]).validate()).toBeTruthy());
}); });

View File

@@ -370,7 +370,6 @@ class Note extends Entity {
WHERE attributes.isDeleted = 0 WHERE attributes.isDeleted = 0
AND attributes.type = 'relation' AND attributes.type = 'relation'
AND attributes.name = 'template' AND attributes.name = 'template'
AND (treeWithAttrs.level = 0 OR attributes.isInheritable = 1)
) )
SELECT attributes.* FROM attributes JOIN treeWithAttrs ON attributes.noteId = treeWithAttrs.noteId SELECT attributes.* FROM attributes JOIN treeWithAttrs ON attributes.noteId = treeWithAttrs.noteId
WHERE attributes.isDeleted = 0 AND (attributes.isInheritable = 1 OR treeWithAttrs.level = 0) WHERE attributes.isDeleted = 0 AND (attributes.isInheritable = 1 OR treeWithAttrs.level = 0)

View File

@@ -5,7 +5,7 @@ const TPL = `
<td colspan="2"> <td colspan="2">
<span class="bx bx-trash"></span> <span class="bx bx-trash"></span>
Delete matched note Delete matched notes
</td> </td>
<td class="button-column"> <td class="button-column">
<span class="bx bx-x icon-action action-conf-del"></span> <span class="bx bx-x icon-action action-conf-del"></span>

View File

@@ -5,24 +5,47 @@ import linkService from "../../services/link.js";
import noteContentRenderer from "../../services/note_content_renderer.js"; import noteContentRenderer from "../../services/note_content_renderer.js";
export default class AbstractTextTypeWidget extends TypeWidget { export default class AbstractTextTypeWidget extends TypeWidget {
doRender() { setupImageOpening(singleClickOpens) {
this.$widget.on("dblclick", "img", e => { this.$widget.on("dblclick", "img", e => this.openImageInCurrentTab($(e.target)));
const $img = $(e.target);
const src = $img.prop("src");
const match = src.match(/\/api\/images\/([A-Za-z0-9]+)\//); this.$widget.on("click", "img", e => {
if ((e.which === 1 && e.ctrlKey) || e.which === 2) {
if (match) { this.openImageInNewTab($(e.target));
const noteId = match[1];
appContext.tabManager.getActiveTabContext().setNote(noteId);
} }
else { else if (e.which === 1 && singleClickOpens) {
window.open(src, '_blank'); this.openImageInCurrentTab($(e.target));
} }
}); });
} }
openImageInCurrentTab($img) {
const imgSrc = $img.prop("src");
const noteId = this.getNoteIdFromImage(imgSrc);
if (noteId) {
appContext.tabManager.getActiveTabContext().setNote(noteId);
} else {
window.open(imgSrc, '_blank');
}
}
openImageInNewTab($img) {
const imgSrc = $img.prop("src");
const noteId = this.getNoteIdFromImage(imgSrc);
if (noteId) {
appContext.tabManager.openTabWithNoteWithHoisting(noteId);
} else {
window.open(imgSrc, '_blank');
}
}
getNoteIdFromImage(imgSrc) {
const match = imgSrc.match(/\/api\/images\/([A-Za-z0-9]+)\//);
return match ? match[1] : null;
}
async loadIncludedNote(noteId, $el) { async loadIncludedNote(noteId, $el) {
const note = await treeCache.getNote(noteId); const note = await treeCache.getNote(noteId);

View File

@@ -84,6 +84,8 @@ export default class EditableTextTypeWidget extends AbstractTextTypeWidget {
keyboardActionService.setupActionsForElement('text-detail', this.$widget, this); keyboardActionService.setupActionsForElement('text-detail', this.$widget, this);
this.setupImageOpening(false);
super.doRender(); super.doRender();
} }

View File

@@ -34,6 +34,7 @@ const TPL = `
.note-detail-readonly-text img { .note-detail-readonly-text img {
max-width: 100%; max-width: 100%;
cursor: pointer;
} }
.edit-text-note-container { .edit-text-note-container {
@@ -66,6 +67,8 @@ export default class ReadOnlyTextTypeWidget extends AbstractTextTypeWidget {
this.triggerEvent('textPreviewDisabled', {tabContext: this.tabContext}); this.triggerEvent('textPreviewDisabled', {tabContext: this.tabContext});
}); });
this.setupImageOpening(true);
super.doRender(); super.doRender();
} }

View File

@@ -107,7 +107,11 @@ function processContent(images, note, content) {
const filename = path.basename(src); const filename = path.basename(src);
if (!dataUrl || !dataUrl.startsWith("data:image")) { if (!dataUrl || !dataUrl.startsWith("data:image")) {
log.info("Image could not be recognized as data URL:", dataUrl.substr(0, Math.min(100, dataUrl.length))); const excerpt = dataUrl
? dataUrl.substr(0, Math.min(100, dataUrl.length))
: "null";
log.info("Image could not be recognized as data URL: " + excerpt);
continue; continue;
} }

View File

@@ -200,9 +200,7 @@ function queueSector(req) {
const entityName = utils.sanitizeSqlIdentifier(req.params.entityName); const entityName = utils.sanitizeSqlIdentifier(req.params.entityName);
const sector = utils.sanitizeSqlIdentifier(req.params.sector); const sector = utils.sanitizeSqlIdentifier(req.params.sector);
const entityPrimaryKey = entityConstructor.getEntityFromEntityName(entityName).primaryKeyName; entityChangesService.addEntityChangesForSector(entityName, sector);
entityChangesService.addEntityChangesForSector(entityName, entityPrimaryKey, sector);
} }
module.exports = { module.exports = {

View File

@@ -1 +1 @@
module.exports = { buildDate:"2021-04-03T22:37:04+02:00", buildRevision: "ef37a52a06b471e60f9c0f11da704283bbcef6ab" }; module.exports = { buildDate:"2021-04-18T21:39:00+02:00", buildRevision: "bb16840a72f61ba558d35f12e4ad297c59cb0e3f" };

View File

@@ -53,22 +53,14 @@ function moveEntityChangeToTop(entityName, entityId) {
addEntityChange(entityName, entityId, hash, null, isSynced); addEntityChange(entityName, entityId, hash, null, isSynced);
} }
function addEntityChangesForSector(entityName, entityPrimaryKey, sector) { function addEntityChangesForSector(entityName, sector) {
const startTime = Date.now(); const startTime = Date.now();
const repository = require('./repository');
const entityChanges = sql.getRows(`SELECT * FROM entity_changes WHERE entityName = ? AND SUBSTR(entityId, 1, 1) = ?`, [entityName, sector]);
sql.transactional(() => { sql.transactional(() => {
const entityIds = sql.getColumn(`SELECT ${entityPrimaryKey} FROM ${entityName} WHERE SUBSTR(${entityPrimaryKey}, 1, 1) = ?`, [sector]); for (const ec of entityChanges) {
insertEntityChange(entityName, ec.entityId, ec.hash, ec.isErased, ec.utcDateChanged, ec.sourceId, ec.isSynced);
for (const entityId of entityIds) {
// retrieving entity one by one to avoid memory issues with note_contents
const entity = repository.getEntity(`SELECT * FROM ${entityName} WHERE ${entityPrimaryKey} = ?`, [entityId]);
if (entityName === 'options' && !entity.isSynced) {
continue
}
insertEntityChange(entityName, entityId, entity.generateHash(), false, entity.getUtcDateChanged(), 'content-check', true);
} }
}); });

View File

@@ -21,8 +21,8 @@ function lex(str) {
} }
} }
function finishWord(endIndex) { function finishWord(endIndex, createAlsoForEmptyWords = false) {
if (currentWord === '') { if (currentWord === '' && !createAlsoForEmptyWords) {
return; return;
} }
@@ -71,7 +71,7 @@ function lex(str) {
} }
} }
else if (quotes === chr) { else if (quotes === chr) {
finishWord(i - 1); finishWord(i - 1, true);
quotes = false; quotes = false;
} }

View File

@@ -245,9 +245,7 @@ async function checkContentHash(syncContext) {
const failedChecks = contentHashService.checkContentHashes(resp.entityHashes); const failedChecks = contentHashService.checkContentHashes(resp.entityHashes);
for (const {entityName, sector} of failedChecks) { for (const {entityName, sector} of failedChecks) {
const entityPrimaryKey = entityConstructor.getEntityFromEntityName(entityName).primaryKeyName; entityChangesService.addEntityChangesForSector(entityName, sector);
entityChangesService.addEntityChangesForSector(entityName, entityPrimaryKey, sector);
await syncRequest(syncContext, 'POST', `/api/sync/queue-sector/${entityName}/${sector}`); await syncRequest(syncContext, 'POST', `/api/sync/queue-sector/${entityName}/${sector}`);
} }

View File

@@ -7,6 +7,9 @@
<sourceFolder url="file://$MODULE_DIR$/src/public" isTestSource="false" /> <sourceFolder url="file://$MODULE_DIR$/src/public" isTestSource="false" />
<excludeFolder url="file://$MODULE_DIR$/dist" /> <excludeFolder url="file://$MODULE_DIR$/dist" />
<excludeFolder url="file://$MODULE_DIR$/src/public/app-dist" /> <excludeFolder url="file://$MODULE_DIR$/src/public/app-dist" />
<excludeFolder url="file://$MODULE_DIR$/libraries" />
<excludeFolder url="file://$MODULE_DIR$/docs" />
<excludeFolder url="file://$MODULE_DIR$/bin/better-sqlite3" />
</content> </content>
<orderEntry type="inheritedJdk" /> <orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" /> <orderEntry type="sourceFolder" forTests="false" />