Merge branch 'master' into dev

# Conflicts:
#	package-lock.json
#	package.json
This commit is contained in:
zadam
2023-01-05 16:17:33 +01:00
14 changed files with 51 additions and 37 deletions

View File

@@ -8,5 +8,5 @@
<h4>Example script</h4>
<pre>
alert("Current note is " + api.getActiveContextNote().title);
api.showMessage("Current note is " + api.getActiveContextNote().title);
</pre>

View File

@@ -32,7 +32,7 @@ export default class TreeContextMenu {
const isHoisted = note.noteId === appContext.tabManager.getActiveContext().hoistedNoteId;
const parentNote = isNotRoot ? await froca.getNote(branch.parentNoteId) : null;
// some actions don't support multi-note so they are disabled when notes are selected
// some actions don't support multi-note, so they are disabled when notes are selected
// the only exception is when the only selected note is the one that was right-clicked, then
// it's clear what the user meant to do.
const selNodes = this.treeWidget.getSelectedNodes();

View File

@@ -216,7 +216,7 @@ class Froca {
getNotesFromCache(noteIds, silentNotFoundError = false) {
return noteIds.map(noteId => {
if (!this.notes[noteId] && !silentNotFoundError) {
console.trace(`Can't find note "${noteId}"`);
console.trace(`Can't find note '${noteId}'`);
return null;
}
@@ -235,7 +235,7 @@ class Froca {
return noteIds.map(noteId => {
if (!this.notes[noteId] && !silentNotFoundError) {
console.trace(`Can't find note "${noteId}"`);
console.trace(`Can't find note '${noteId}'`);
return null;
} else {
@@ -285,7 +285,7 @@ class Froca {
getBranch(branchId, silentNotFoundError = false) {
if (!(branchId in this.branches)) {
if (!silentNotFoundError) {
logError(`Not existing branch ${branchId}`);
logError(`Not existing branch '${branchId}'`);
}
}
else {
@@ -295,13 +295,13 @@ class Froca {
async getBranchId(parentNoteId, childNoteId) {
if (childNoteId === 'root') {
return 'root';
return 'none_root';
}
const child = await this.getNote(childNoteId);
if (!child) {
logError(`Could not find branchId for parent=${parentNoteId}, child=${childNoteId} since child does not exist`);
logError(`Could not find branchId for parent '${parentNoteId}', child '${childNoteId}' since child does not exist`);
return null;
}
@@ -318,9 +318,9 @@ class Froca {
.then(row => new FNoteComplement(row))
.catch(e => console.error(`Cannot get note complement for note '${noteId}'`));
// we don't want to keep large payloads forever in memory so we clean that up quite quickly
// we don't want to keep large payloads forever in memory, so we clean that up quite quickly
// this cache is more meant to share the data between different components within one business transaction (e.g. loading of the note into the tab context and all the components)
// this is also a work around for missing invalidation after change
// this is also a workaround for missing invalidation after change
this.noteComplementPromises[noteId].then(
() => setTimeout(() => this.noteComplementPromises[noteId] = null, 1000)
);

View File

@@ -320,8 +320,6 @@ export default class NoteDetailWidget extends NoteContextAwareWidget {
&& attributeService.isAffecting(attr, this.note));
if (label || relation) {
console.log("OOOO");
// probably incorrect event
// calling this.refresh() is not enough since the event needs to be propagated to children as well
this.triggerEvent('noteTypeMimeChanged', {noteId: this.noteId});

View File

@@ -1606,7 +1606,7 @@ export default class NoteTreeWidget extends NoteContextAwareWidget {
const resp = await server.post(`special-notes/launchers/${node.data.noteId}/${launcherType}`);
if (!resp.success) {
alert(resp.message);
toastService.showError(resp.message);
}
await ws.waitForMaxKnownEntityChangeId();

View File

@@ -14,8 +14,8 @@ const BAttribute = require('../../becca/entities/battribute');
const htmlSanitizer = require('../../services/html_sanitizer');
const {formatAttrForSearch} = require("../../services/attribute_formatter");
function findClippingNote(todayNote, pageUrl) {
const notes = todayNote.searchNotesInSubtree(
function findClippingNote(clipperInboxNote, pageUrl) {
const notes = clipperInboxNote.searchNotesInSubtree(
formatAttrForSearch({
type: 'label',
name: "pageUrl",
@@ -47,6 +47,7 @@ function addClipping(req) {
const clipperInbox = getClipperInboxNote();
pageUrl = htmlSanitizer.sanitizeUrl(pageUrl);
let clippingNote = findClippingNote(clipperInbox, pageUrl);
if (!clippingNote) {
@@ -57,8 +58,6 @@ function addClipping(req) {
type: 'text'
}).note;
pageUrl = htmlSanitizer.sanitize(pageUrl);
clippingNote.setLabel('clipType', 'clippings');
clippingNote.setLabel('pageUrl', pageUrl);
clippingNote.setLabel('iconClass', 'bx bx-globe');
@@ -96,7 +95,7 @@ function createNote(req) {
note.setLabel('clipType', clipType);
if (pageUrl) {
pageUrl = htmlSanitizer.sanitize(pageUrl);
pageUrl = htmlSanitizer.sanitizeUrl(pageUrl);
note.setLabel('pageUrl', pageUrl);
note.setLabel('iconClass', 'bx bx-globe');

View File

@@ -13,7 +13,7 @@ function exportBranch(req, res) {
const branch = becca.getBranch(branchId);
if (!branch) {
const message = `Cannot export branch ${branchId} since it does not exist.`;
const message = `Cannot export branch '${branchId}' since it does not exist.`;
log.error(message);
res.setHeader("Content-Type", "text/plain")

View File

@@ -1 +1 @@
module.exports = { buildDate:"2022-12-29T00:12:54+01:00", buildRevision: "d36cf47974cd8bc6bd45c1da774a9a55d45f998e" };
module.exports = { buildDate:"2023-01-04T22:36:31+01:00", buildRevision: "3a5fa2954dea0ff2ecdce9a28b3bb01f039d314d" };

View File

@@ -1,4 +1,5 @@
const sanitizeHtml = require('sanitize-html');
const sanitizeUrl = require('@braintree/sanitize-url').sanitizeUrl;
// intended mainly as protection against XSS via import
// secondarily it (partly) protects against "CSS takeover"
@@ -50,5 +51,6 @@ function sanitize(dirtyHtml) {
}
module.exports = {
sanitize
sanitize,
sanitizeUrl
};

View File

@@ -48,14 +48,13 @@ class OrderByAndLimitExp extends Expression {
}
// if both are numbers then parse them for numerical comparison
// beware that isNaN will return false for empty string and null
if (valA.trim() !== "" && valB.trim() !== "" && !isNaN(valA) && !isNaN(valB)) {
if (this.isNumber(valA) && this.isNumber(valB)) {
valA = parseFloat(valA);
valB = parseFloat(valB);
}
if (!valA && !valB) {
// the attribute is not defined in either note so continue to next order definition
// the attribute value is empty/zero in both notes so continue to the next order definition
continue;
} else if (!valB || valA < valB) {
return smaller;
@@ -77,6 +76,17 @@ class OrderByAndLimitExp extends Expression {
return noteSet;
}
isNumber(x) {
if (typeof x === 'number') {
return true;
} else if (typeof x === 'string') {
// isNaN will return false for blank string
return x.trim() !== "" && !isNaN(x);
} else {
return false;
}
}
}
module.exports = OrderByAndLimitExp;

View File

@@ -1,8 +1,8 @@
"use strict";
/**
* Search string is lower cased for case insensitive comparison. But when retrieving properties
* we need case sensitive form so we have this translation object.
* Search string is lower cased for case-insensitive comparison. But when retrieving properties
* we need case-sensitive form, so we have this translation object.
*/
const PROP_MAPPING = {
"noteid": "noteId",