diff --git a/docs/backend_api/AbstractBeccaEntity.html b/docs/backend_api/AbstractBeccaEntity.html
index e13c799bc..328d08f0c 100644
--- a/docs/backend_api/AbstractBeccaEntity.html
+++ b/docs/backend_api/AbstractBeccaEntity.html
@@ -991,7 +991,7 @@ This is a low level method, for notes and branches use `note.deleteNote()` and '
diff --git a/docs/backend_api/BAttribute.html b/docs/backend_api/BAttribute.html
index 8f6af88c6..29eb98f90 100644
--- a/docs/backend_api/BAttribute.html
+++ b/docs/backend_api/BAttribute.html
@@ -1904,7 +1904,7 @@ This is a low level method, for notes and branches use `note.deleteNote()` and '
diff --git a/docs/backend_api/BBranch.html b/docs/backend_api/BBranch.html
index ae6e4e014..f75738ee7 100644
--- a/docs/backend_api/BBranch.html
+++ b/docs/backend_api/BBranch.html
@@ -1916,7 +1916,7 @@ This is a low level method, for notes and branches use `note.deleteNote()` and '
diff --git a/docs/backend_api/BEtapiToken.html b/docs/backend_api/BEtapiToken.html
index 1fab94869..2c6eb8218 100644
--- a/docs/backend_api/BEtapiToken.html
+++ b/docs/backend_api/BEtapiToken.html
@@ -1461,7 +1461,7 @@ This is a low level method, for notes and branches use `note.deleteNote()` and '
diff --git a/docs/backend_api/BNote.html b/docs/backend_api/BNote.html
index d38f35c0f..b1d3e2b60 100644
--- a/docs/backend_api/BNote.html
+++ b/docs/backend_api/BNote.html
@@ -1318,7 +1318,7 @@ See addLabel, addRelation for more specific methods.
Source:
@@ -1654,7 +1654,7 @@ See addLabel, addRelation for more specific methods.
Source:
@@ -1900,7 +1900,7 @@ returned.
Source:
@@ -2135,7 +2135,7 @@ returned.
Source:
@@ -2335,7 +2335,7 @@ returned.
Source:
@@ -2556,6 +2556,10 @@ returned.
+
+ Gives all possible note paths leading to this note. Paths containing search note are ignored (could form cycles)
+
+
@@ -2597,7 +2601,7 @@ returned.
Source:
@@ -2703,7 +2707,7 @@ returned.
Source:
@@ -2877,7 +2881,7 @@ returned.
Source:
@@ -3055,7 +3059,7 @@ returned.
Source:
@@ -3316,6 +3320,364 @@ returned.
+ getBestNotePath(hoistedNoteIdopt) → {Array.<string>}
+
+
+
+
+
+
+
+ Returns note path considered to be the "best"
+
+
+
+
+
+
+
+
+
+
+ Parameters:
+
+
+
+
+
+
+ Name |
+
+
+ Type |
+
+
+ Attributes |
+
+
+
+ Default |
+
+
+ Description |
+
+
+
+
+
+
+
+
+ hoistedNoteId |
+
+
+
+
+
+string
+
+
+
+ |
+
+
+
+
+ <optional>
+
+
+
+
+
+ |
+
+
+
+
+
+ 'root'
+
+ |
+
+
+ |
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ - Source:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Returns:
+
+
+
+ array of noteIds constituting the particular note path
+
+
+
+
+
+ -
+ Type
+
+ -
+
+Array.<string>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ getBestNotePathString(hoistedNoteIdopt) → {string}
+
+
+
+
+
+
+
+ Returns note path considered to be the "best"
+
+
+
+
+
+
+
+
+
+
+ Parameters:
+
+
+
+
+
+
+ Name |
+
+
+ Type |
+
+
+ Attributes |
+
+
+
+ Default |
+
+
+ Description |
+
+
+
+
+
+
+
+
+ hoistedNoteId |
+
+
+
+
+
+string
+
+
+
+ |
+
+
+
+
+ <optional>
+
+
+
+
+
+ |
+
+
+
+
+
+ 'root'
+
+ |
+
+
+ |
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ - Source:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Returns:
+
+
+
+ serialized note path (e.g. 'root/a1h315/js725h')
+
+
+
+
+
+ -
+ Type
+
+ -
+
+string
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
getBranches() → {Array.<BBranch>}
@@ -3878,7 +4240,7 @@ returned.
Source:
@@ -3968,7 +4330,7 @@ returned.
Source:
@@ -4074,7 +4436,7 @@ returned.
Source:
@@ -4332,7 +4694,7 @@ returned.
Source:
@@ -4490,7 +4852,7 @@ returned.
Source:
@@ -4660,7 +5022,7 @@ returned.
Source:
@@ -4827,7 +5189,7 @@ returned.
Source:
@@ -4933,7 +5295,7 @@ returned.
Source:
@@ -5035,7 +5397,7 @@ returned.
Source:
@@ -5215,7 +5577,7 @@ This method can be significantly faster than the getAttribute()
Source:
@@ -5480,7 +5842,7 @@ This method can be significantly faster than the getAttribute()
Source:
@@ -5635,7 +5997,7 @@ This method can be significantly faster than the getAttribute()
Source:
@@ -5793,7 +6155,7 @@ This method can be significantly faster than the getAttribute()
Source:
@@ -5963,7 +6325,7 @@ This method can be significantly faster than the getAttribute()
Source:
@@ -6130,7 +6492,7 @@ This method can be significantly faster than the getAttribute()
Source:
@@ -6285,7 +6647,7 @@ This method can be significantly faster than the getAttribute()
Source:
@@ -6443,7 +6805,7 @@ This method can be significantly faster than the getAttribute()
Source:
@@ -6613,7 +6975,7 @@ This method can be significantly faster than the getAttribute()
Source:
@@ -7061,7 +7423,7 @@ This method can be significantly faster than the getAttribute()
Source:
@@ -7219,7 +7581,7 @@ This method can be significantly faster than the getAttribute()
Source:
@@ -7389,7 +7751,7 @@ This method can be significantly faster than the getAttribute()
Source:
@@ -7604,7 +7966,7 @@ This method can be significantly faster than the getAttribute()
Source:
@@ -7658,6 +8020,177 @@ This method can be significantly faster than the getAttribute()
+ getSortedNotePathRecords(hoistedNoteIdopt) → {Array.<{isArchived: boolean, isInHoistedSubTree: boolean, notePath: Array.<string>, isHidden: boolean}>}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Parameters:
+
+
+
+
+
+
+ Name |
+
+
+ Type |
+
+
+ Attributes |
+
+
+
+ Default |
+
+
+ Description |
+
+
+
+
+
+
+
+
+ hoistedNoteId |
+
+
+
+
+
+string
+
+
+
+ |
+
+
+
+
+ <optional>
+
+
+
+
+
+ |
+
+
+
+
+
+ 'root'
+
+ |
+
+
+ |
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ - Source:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Returns:
+
+
+
+
+
+ -
+ Type
+
+ -
+
+Array.<{isArchived: boolean, isInHoistedSubTree: boolean, notePath: Array.<string>, isHidden: boolean}>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
getStrongParentBranches() → {Array.<BBranch>}
@@ -7812,7 +8345,7 @@ This method can be significantly faster than the getAttribute()
Source:
@@ -7914,7 +8447,7 @@ This method can be significantly faster than the getAttribute()
Source:
@@ -8020,7 +8553,7 @@ This method can be significantly faster than the getAttribute()
Source:
@@ -8211,7 +8744,7 @@ This method can be significantly faster than the getAttribute()
Source:
@@ -8962,7 +9495,7 @@ This method can be significantly faster than the getAttribute()
Source:
@@ -9160,7 +9693,7 @@ This method can be significantly faster than the getAttribute()
Source:
@@ -9358,7 +9891,7 @@ This method can be significantly faster than the getAttribute()
Source:
@@ -9556,7 +10089,7 @@ This method can be significantly faster than the getAttribute()
Source:
@@ -9706,7 +10239,7 @@ This method can be significantly faster than the getAttribute()
Source:
@@ -9812,7 +10345,7 @@ This method can be significantly faster than the getAttribute()
Source:
@@ -10282,6 +10815,161 @@ This method can be significantly faster than the getAttribute()
+ isLabelTruthy(name) → {boolean}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Parameters:
+
+
+
+
+
+
+ Name |
+
+
+ Type |
+
+
+
+
+
+ Description |
+
+
+
+
+
+
+
+
+ name |
+
+
+
+
+
+string
+
+
+
+ |
+
+
+
+
+
+ label name |
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ - Source:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Returns:
+
+
+
+ true if label exists (including inherited) and does not have "false" value.
+
+
+
+
+
+ -
+ Type
+
+ -
+
+boolean
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
isRoot() → {boolean}
@@ -10828,7 +11516,7 @@ This is a low level method, for notes and branches use `note.deleteNote()` and '
Source:
@@ -11008,7 +11696,7 @@ This is a low level method, for notes and branches use `note.deleteNote()` and '
Source:
@@ -11188,7 +11876,7 @@ This is a low level method, for notes and branches use `note.deleteNote()` and '
Source:
@@ -11383,7 +12071,7 @@ This is a low level method, for notes and branches use `note.deleteNote()` and '
Source:
@@ -11615,7 +12303,7 @@ This is a low level method, for notes and branches use `note.deleteNote()` and '
Source:
@@ -11795,7 +12483,7 @@ This is a low level method, for notes and branches use `note.deleteNote()` and '
Source:
@@ -11955,7 +12643,7 @@ This is a low level method, for notes and branches use `note.deleteNote()` and '
Source:
@@ -12197,7 +12885,7 @@ This is a low level method, for notes and branches use `note.deleteNote()` and '
Source:
@@ -12408,7 +13096,7 @@ This is a low level method, for notes and branches use `note.deleteNote()` and '
Source:
@@ -12619,7 +13307,7 @@ This is a low level method, for notes and branches use `note.deleteNote()` and '
Source:
@@ -12671,7 +13359,7 @@ This is a low level method, for notes and branches use `note.deleteNote()` and '
diff --git a/docs/backend_api/BNoteRevision.html b/docs/backend_api/BNoteRevision.html
index 0951fa14d..3a9ccf0d3 100644
--- a/docs/backend_api/BNoteRevision.html
+++ b/docs/backend_api/BNoteRevision.html
@@ -2174,7 +2174,7 @@ This is a low level method, for notes and branches use `note.deleteNote()` and '
diff --git a/docs/backend_api/BOption.html b/docs/backend_api/BOption.html
index b5990cb92..849051190 100644
--- a/docs/backend_api/BOption.html
+++ b/docs/backend_api/BOption.html
@@ -267,7 +267,7 @@
Source:
@@ -335,7 +335,7 @@
Source:
@@ -403,7 +403,7 @@
Source:
@@ -471,7 +471,7 @@
Source:
@@ -1319,7 +1319,7 @@ This is a low level method, for notes and branches use `note.deleteNote()` and '
diff --git a/docs/backend_api/BRecentNote.html b/docs/backend_api/BRecentNote.html
index b36b38298..cdda77c51 100644
--- a/docs/backend_api/BRecentNote.html
+++ b/docs/backend_api/BRecentNote.html
@@ -1251,7 +1251,7 @@ This is a low level method, for notes and branches use `note.deleteNote()` and '
diff --git a/docs/backend_api/BackendScriptApi.html b/docs/backend_api/BackendScriptApi.html
index 12800f161..ba6ffbc2b 100644
--- a/docs/backend_api/BackendScriptApi.html
+++ b/docs/backend_api/BackendScriptApi.html
@@ -3254,7 +3254,7 @@ JSON MIME type. See also createNewNote() for more options.
- ensureNoteIsPresentInParent(noteId, parentNoteId, prefix) → {void}
+ ensureNoteIsPresentInParent(noteId, parentNoteId, prefix) → {Object}
@@ -3262,7 +3262,7 @@ JSON MIME type. See also createNewNote() for more options.
- If there's no branch between note and parent note, create one. Otherwise, do nothing.
+ If there's no branch between note and parent note, create one. Otherwise, do nothing. Returns the new or existing branch.
@@ -3437,7 +3437,7 @@ JSON MIME type. See also createNewNote() for more options.
-void
+Object
@@ -7889,7 +7889,7 @@ exists, then we'll use that transaction.
diff --git a/docs/backend_api/becca_entities_abstract_becca_entity.js.html b/docs/backend_api/becca_entities_abstract_becca_entity.js.html
index 4036de3f6..b64387b2d 100644
--- a/docs/backend_api/becca_entities_abstract_becca_entity.js.html
+++ b/docs/backend_api/becca_entities_abstract_becca_entity.js.html
@@ -212,7 +212,7 @@ module.exports = AbstractBeccaEntity;
diff --git a/docs/backend_api/becca_entities_battribute.js.html b/docs/backend_api/becca_entities_battribute.js.html
index 83fff96e5..d9715c60b 100644
--- a/docs/backend_api/becca_entities_battribute.js.html
+++ b/docs/backend_api/becca_entities_battribute.js.html
@@ -124,7 +124,7 @@ class BAttribute extends AbstractBeccaEntity {
}
if (this.type === 'relation' && !(this.value in this.becca.notes)) {
- throw new Error(`Cannot save relation '${this.name}' of note '${this.noteId}' since it target not existing note '${this.value}'.`);
+ throw new Error(`Cannot save relation '${this.name}' of note '${this.noteId}' since it targets not existing note '${this.value}'.`);
}
}
@@ -276,7 +276,7 @@ module.exports = BAttribute;
diff --git a/docs/backend_api/becca_entities_bbranch.js.html b/docs/backend_api/becca_entities_bbranch.js.html
index b4fa7f567..b2261711d 100644
--- a/docs/backend_api/becca_entities_bbranch.js.html
+++ b/docs/backend_api/becca_entities_bbranch.js.html
@@ -319,7 +319,7 @@ module.exports = BBranch;
diff --git a/docs/backend_api/becca_entities_betapi_token.js.html b/docs/backend_api/becca_entities_betapi_token.js.html
index 92ab19328..329843522 100644
--- a/docs/backend_api/becca_entities_betapi_token.js.html
+++ b/docs/backend_api/becca_entities_betapi_token.js.html
@@ -120,7 +120,7 @@ module.exports = BEtapiToken;
diff --git a/docs/backend_api/becca_entities_bnote.js.html b/docs/backend_api/becca_entities_bnote.js.html
index 97889071a..0c729d838 100644
--- a/docs/backend_api/becca_entities_bnote.js.html
+++ b/docs/backend_api/becca_entities_bnote.js.html
@@ -125,7 +125,7 @@ class BNote extends AbstractBeccaEntity {
* @private */
this.parents = [];
/** @type {BNote[]}
- * @private*/
+ * @private */
this.children = [];
/** @type {BAttribute[]}
* @private */
@@ -135,11 +135,11 @@ class BNote extends AbstractBeccaEntity {
* @private */
this.__attributeCache = null;
/** @type {BAttribute[]|null}
- * @private*/
+ * @private */
this.inheritableAttributeCache = null;
/** @type {BAttribute[]}
- * @private*/
+ * @private */
this.targetRelations = [];
this.becca.addNote(this.noteId, this);
@@ -560,6 +560,20 @@ class BNote extends AbstractBeccaEntity {
*/
hasLabel(name, value) { return this.hasAttribute(LABEL, name, value); }
+ /**
+ * @param {string} name - label name
+ * @returns {boolean} true if label exists (including inherited) and does not have "false" value.
+ */
+ isLabelTruthy(name) {
+ const label = this.getLabel(name);
+
+ if (!label) {
+ return false;
+ }
+
+ return label && label.value !== 'false';
+ }
+
/**
* @param {string} name - label name
* @param {string} [value] - label value
@@ -761,6 +775,21 @@ class BNote extends AbstractBeccaEntity {
return this.hasAttribute('label', 'archived');
}
+ areAllNotePathsArchived() {
+ // there's a slight difference between note being itself archived and all its note paths being archived
+ // - note is archived when it itself has an archived label or inherits it
+ // - note does not have or inherit archived label, but each note paths contains a note with (non-inheritable)
+ // archived label
+
+ const bestNotePathRecord = this.getSortedNotePathRecords()[0];
+
+ if (!bestNotePathRecord) {
+ throw new Error(`No note path available for note '${this.noteId}'`);
+ }
+
+ return bestNotePathRecord.isArchived;
+ }
+
hasInheritableArchivedLabel() {
for (const attr of this.getAttributes()) {
if (attr.name === 'archived' && attr.type === LABEL && attr.isInheritable) {
@@ -1164,6 +1193,8 @@ class BNote extends AbstractBeccaEntity {
}
/**
+ * Gives all possible note paths leading to this note. Paths containing search note are ignored (could form cycles)
+ *
* @returns {string[][]} - array of notePaths (each represented by array of noteIds constituting the particular note path)
*/
getAllNotePaths() {
@@ -1171,18 +1202,73 @@ class BNote extends AbstractBeccaEntity {
return [['root']];
}
- const notePaths = [];
+ const parentNotes = this.getParentNotes();
+ let notePaths = [];
- for (const parentNote of this.getParentNotes()) {
- for (const parentPath of parentNote.getAllNotePaths()) {
- parentPath.push(this.noteId);
- notePaths.push(parentPath);
- }
+ if (parentNotes.length === 1) { // optimization for most common case
+ notePaths = parentNotes[0].getAllNotePaths();
+ } else {
+ notePaths = parentNotes.flatMap(parentNote => parentNote.getAllNotePaths());
+ }
+
+ for (const notePath of notePaths) {
+ notePath.push(this.noteId);
}
return notePaths;
}
+ /**
+ * @param {string} [hoistedNoteId='root']
+ * @return {Array<{isArchived: boolean, isInHoistedSubTree: boolean, notePath: Array<string>, isHidden: boolean}>}
+ */
+ getSortedNotePathRecords(hoistedNoteId = 'root') {
+ const isHoistedRoot = hoistedNoteId === 'root';
+
+ const notePaths = this.getAllNotePaths().map(path => ({
+ notePath: path,
+ isInHoistedSubTree: isHoistedRoot || path.includes(hoistedNoteId),
+ isArchived: path.some(noteId => this.becca.notes[noteId].isArchived),
+ isHidden: path.includes('_hidden')
+ }));
+
+ notePaths.sort((a, b) => {
+ if (a.isInHoistedSubTree !== b.isInHoistedSubTree) {
+ return a.isInHoistedSubTree ? -1 : 1;
+ } else if (a.isArchived !== b.isArchived) {
+ return a.isArchived ? 1 : -1;
+ } else if (a.isHidden !== b.isHidden) {
+ return a.isHidden ? 1 : -1;
+ } else {
+ return a.notePath.length - b.notePath.length;
+ }
+ });
+
+ return notePaths;
+ }
+
+ /**
+ * Returns note path considered to be the "best"
+ *
+ * @param {string} [hoistedNoteId='root']
+ * @return {string[]} array of noteIds constituting the particular note path
+ */
+ getBestNotePath(hoistedNoteId = 'root') {
+ return this.getSortedNotePathRecords(hoistedNoteId)[0]?.notePath;
+ }
+
+ /**
+ * Returns note path considered to be the "best"
+ *
+ * @param {string} [hoistedNoteId='root']
+ * @return {string} serialized note path (e.g. 'root/a1h315/js725h')
+ */
+ getBestNotePathString(hoistedNoteId = 'root') {
+ const notePath = this.getBestNotePath(hoistedNoteId);
+
+ return notePath?.join("/");
+ }
+
/**
* @return boolean - true if there's no non-hidden path, note is not cloned to the visible tree
*/
@@ -1196,9 +1282,7 @@ class BNote extends AbstractBeccaEntity {
return false;
} else if (parentNote.noteId === '_hidden') {
continue;
- }
-
- if (!parentNote.isHiddenCompletely()) {
+ } else if (!parentNote.isHiddenCompletely()) {
return false;
}
}
@@ -1392,7 +1476,7 @@ class BNote extends AbstractBeccaEntity {
/**
* @param parentNoteId
- * @returns {{success: boolean, message: string}}
+ * @returns {{success: boolean, message: string, branchId: string, notePath: string}}
*/
cloneTo(parentNoteId) {
const cloningService = require("../../services/cloning");
@@ -1550,7 +1634,7 @@ module.exports = BNote;
diff --git a/docs/backend_api/becca_entities_bnote_revision.js.html b/docs/backend_api/becca_entities_bnote_revision.js.html
index c140f1d10..ea663e51f 100644
--- a/docs/backend_api/becca_entities_bnote_revision.js.html
+++ b/docs/backend_api/becca_entities_bnote_revision.js.html
@@ -194,12 +194,14 @@ class BNoteRevision extends AbstractBeccaEntity {
utcDateLastEdited: this.utcDateLastEdited,
utcDateCreated: this.utcDateCreated,
utcDateModified: this.utcDateModified,
+ content: this.content, // used when retrieving full note revision to frontend
contentLength: this.contentLength
};
}
getPojoToSave() {
const pojo = this.getPojo();
+ delete pojo.content; // not getting persisted
delete pojo.contentLength; // not getting persisted
if (pojo.isProtected) {
@@ -233,7 +235,7 @@ module.exports = BNoteRevision;
diff --git a/docs/backend_api/becca_entities_boption.js.html b/docs/backend_api/becca_entities_boption.js.html
index ab2c22f51..0f2856fdc 100644
--- a/docs/backend_api/becca_entities_boption.js.html
+++ b/docs/backend_api/becca_entities_boption.js.html
@@ -44,6 +44,11 @@ class BOption extends AbstractBeccaEntity {
constructor(row) {
super();
+ this.updateFromRow(row);
+ this.becca.options[this.name] = this;
+ }
+
+ updateFromRow(row) {
/** @type {string} */
this.name = row.name;
/** @type {string} */
@@ -52,8 +57,6 @@ class BOption extends AbstractBeccaEntity {
this.isSynced = !!row.isSynced;
/** @type {string} */
this.utcDateModified = row.utcDateModified;
-
- this.becca.options[this.name] = this;
}
beforeSaving() {
@@ -89,7 +92,7 @@ module.exports = BOption;
diff --git a/docs/backend_api/becca_entities_brecent_note.js.html b/docs/backend_api/becca_entities_brecent_note.js.html
index 2e2f5ccb1..02f6858fb 100644
--- a/docs/backend_api/becca_entities_brecent_note.js.html
+++ b/docs/backend_api/becca_entities_brecent_note.js.html
@@ -77,7 +77,7 @@ module.exports = BRecentNote;
diff --git a/docs/backend_api/index.html b/docs/backend_api/index.html
index 55d778a7e..f9f7919b5 100644
--- a/docs/backend_api/index.html
+++ b/docs/backend_api/index.html
@@ -56,7 +56,7 @@
diff --git a/docs/backend_api/module-sql.html b/docs/backend_api/module-sql.html
index 6517bc0d4..2605d6530 100644
--- a/docs/backend_api/module-sql.html
+++ b/docs/backend_api/module-sql.html
@@ -1300,7 +1300,7 @@
diff --git a/docs/backend_api/services_backend_script_api.js.html b/docs/backend_api/services_backend_script_api.js.html
index 44f40bed6..bffce4bcd 100644
--- a/docs/backend_api/services_backend_script_api.js.html
+++ b/docs/backend_api/services_backend_script_api.js.html
@@ -165,13 +165,13 @@ function BackendScriptApi(currentNote, apiParams) {
this.getNoteWithLabel = attributeService.getNoteWithLabel;
/**
- * If there's no branch between note and parent note, create one. Otherwise, do nothing.
+ * If there's no branch between note and parent note, create one. Otherwise, do nothing. Returns the new or existing branch.
*
* @method
* @param {string} noteId
* @param {string} parentNoteId
* @param {string} prefix - if branch will be created between note and parent note, set this prefix
- * @returns {void}
+ * @returns {{branch: BBranch|null}}
*/
this.ensureNoteIsPresentInParent = cloningService.ensureNoteIsPresentInParent;
@@ -499,11 +499,11 @@ function BackendScriptApi(currentNote, apiParams) {
if (opts.type === 'script' && !opts.scriptNoteId) { throw new Error("scriptNoteId is mandatory for launchers of type 'script'"); }
if (opts.type === 'customWidget' && !opts.widgetNoteId) { throw new Error("widgetNoteId is mandatory for launchers of type 'customWidget'"); }
- const parentNoteId = !!opts.isVisible ? '_lbVisibleLaunchers' : '_lbAvailableLaunchers';
+ const parentNoteId = opts.isVisible ? '_lbVisibleLaunchers' : '_lbAvailableLaunchers';
const noteId = 'al_' + opts.id;
const launcherNote =
- becca.getNote(opts.id) ||
+ becca.getNote(noteId) ||
specialNotesService.createLauncher({
noteId: noteId,
parentNoteId: parentNoteId,
@@ -542,7 +542,7 @@ function BackendScriptApi(currentNote, apiParams) {
if (opts.icon) {
launcherNote.setLabel('iconClass', `bx ${opts.icon}`);
} else {
- launcherNote.removeLabel('keyboardShortcut');
+ launcherNote.removeLabel('iconClass');
}
return {note: launcherNote};
@@ -584,7 +584,7 @@ module.exports = BackendScriptApi;
diff --git a/docs/backend_api/services_sql.js.html b/docs/backend_api/services_sql.js.html
index ec2ac56e6..783bf8301 100644
--- a/docs/backend_api/services_sql.js.html
+++ b/docs/backend_api/services_sql.js.html
@@ -245,7 +245,7 @@ function wrap(query, func) {
// in these cases error should be simply ignored.
console.log(e.message);
- return null
+ return null;
}
throw e;
@@ -309,7 +309,7 @@ function fillParamList(paramIds, truncate = true) {
}
// doing it manually to avoid this showing up on the sloq query list
- const s = stmt(`INSERT INTO param_list VALUES ${paramIds.map(paramId => `(?)`).join(',')}`, paramIds);
+ const s = stmt(`INSERT INTO param_list VALUES ${paramIds.map(paramId => `(?)`).join(',')}`);
s.run(paramIds);
}
@@ -413,7 +413,7 @@ module.exports = {
diff --git a/docs/frontend_api/FAttribute.html b/docs/frontend_api/FAttribute.html
index 641c3a737..901dc598c 100644
--- a/docs/frontend_api/FAttribute.html
+++ b/docs/frontend_api/FAttribute.html
@@ -850,7 +850,7 @@ and relation (representing named relationship between source and target note)
diff --git a/docs/frontend_api/FBranch.html b/docs/frontend_api/FBranch.html
index 284bc72d5..4ec45ea97 100644
--- a/docs/frontend_api/FBranch.html
+++ b/docs/frontend_api/FBranch.html
@@ -1062,7 +1062,7 @@ parents.
diff --git a/docs/frontend_api/FNote.html b/docs/frontend_api/FNote.html
index cff8d1e8f..cf7e9782c 100644
--- a/docs/frontend_api/FNote.html
+++ b/docs/frontend_api/FNote.html
@@ -977,6 +977,116 @@
+ getAllNotePaths() → {Array.<Array.<string>>}
+
+
+
+
+
+
+
+ Gives all possible note paths leading to this note. Paths containing search note are ignored (could form cycles)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ - Source:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Returns:
+
+
+
+ - array of notePaths (each represented by array of noteIds constituting the particular note path)
+
+
+
+
+
+ -
+ Type
+
+ -
+
+Array.<Array.<string>>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
getAttribute(type, name) → {FAttribute}
@@ -1097,7 +1207,7 @@
Source:
@@ -1275,7 +1385,7 @@
Source:
@@ -1475,7 +1585,7 @@
Source:
@@ -1533,6 +1643,364 @@
+ getBestNotePath(hoistedNoteIdopt) → {Array.<string>}
+
+
+
+
+
+
+
+ Returns note path considered to be the "best"
+
+
+
+
+
+
+
+
+
+
+ Parameters:
+
+
+
+
+
+
+ Name |
+
+
+ Type |
+
+
+ Attributes |
+
+
+
+ Default |
+
+
+ Description |
+
+
+
+
+
+
+
+
+ hoistedNoteId |
+
+
+
+
+
+string
+
+
+
+ |
+
+
+
+
+ <optional>
+
+
+
+
+
+ |
+
+
+
+
+
+ 'root'
+
+ |
+
+
+ |
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ - Source:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Returns:
+
+
+
+ array of noteIds constituting the particular note path
+
+
+
+
+
+ -
+ Type
+
+ -
+
+Array.<string>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ getBestNotePathString(hoistedNoteIdopt) → {string}
+
+
+
+
+
+
+
+ Returns note path considered to be the "best"
+
+
+
+
+
+
+
+
+
+
+ Parameters:
+
+
+
+
+
+
+ Name |
+
+
+ Type |
+
+
+ Attributes |
+
+
+
+ Default |
+
+
+ Description |
+
+
+
+
+
+
+
+
+ hoistedNoteId |
+
+
+
+
+
+string
+
+
+
+ |
+
+
+
+
+ <optional>
+
+
+
+
+
+ |
+
+
+
+
+
+ 'root'
+
+ |
+
+
+ |
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ - Source:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Returns:
+
+
+
+ serialized note path (e.g. 'root/a1h315/js725h')
+
+
+
+
+
+ -
+ Type
+
+ -
+
+string
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
getBranchIds() → {Array.<string>}
@@ -1583,7 +2051,7 @@
Source:
@@ -1687,7 +2155,7 @@
Source:
@@ -1789,7 +2257,7 @@
Source:
@@ -1891,7 +2359,7 @@
Source:
@@ -1993,7 +2461,7 @@
Source:
@@ -2144,7 +2612,7 @@
Source:
@@ -2299,7 +2767,7 @@
Source:
@@ -2466,7 +2934,7 @@
Source:
@@ -2576,7 +3044,7 @@
Source:
@@ -2678,7 +3146,7 @@
Source:
@@ -2852,7 +3320,7 @@
Source:
@@ -3030,7 +3498,7 @@
Source:
@@ -3230,7 +3698,7 @@
Source:
@@ -3385,7 +3853,7 @@
Source:
@@ -3540,7 +4008,7 @@
Source:
@@ -3707,7 +4175,7 @@
Source:
@@ -3862,7 +4330,7 @@
Source:
@@ -4017,7 +4485,7 @@
Source:
@@ -4184,7 +4652,7 @@
Source:
@@ -4290,7 +4758,7 @@
Source:
@@ -4392,7 +4860,7 @@
Source:
@@ -4494,7 +4962,7 @@
Source:
@@ -4596,7 +5064,7 @@
Source:
@@ -4747,7 +5215,7 @@
Source:
@@ -4902,7 +5370,7 @@
Source:
@@ -5072,7 +5540,7 @@
Source:
@@ -5223,7 +5691,7 @@
Source:
@@ -5390,7 +5858,7 @@
Source:
@@ -5496,7 +5964,7 @@
Source:
@@ -5557,6 +6025,177 @@
+ getSortedNotePathRecords(hoistedNoteIdopt) → {Array.<{isArchived: boolean, isInHoistedSubTree: boolean, notePath: Array.<string>, isHidden: boolean}>}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Parameters:
+
+
+
+
+
+
+ Name |
+
+
+ Type |
+
+
+ Attributes |
+
+
+
+ Default |
+
+
+ Description |
+
+
+
+
+
+
+
+
+ hoistedNoteId |
+
+
+
+
+
+string
+
+
+
+ |
+
+
+
+
+ <optional>
+
+
+
+
+
+ |
+
+
+
+
+
+ 'root'
+
+ |
+
+
+ |
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ - Source:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Returns:
+
+
+
+
+
+ -
+ Type
+
+ -
+
+Array.<{isArchived: boolean, isInHoistedSubTree: boolean, notePath: Array.<string>, isHidden: boolean}>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
(async) getTargetRelationSourceNotes() → {Array.<FNote>}
@@ -5609,7 +6248,7 @@
Source:
@@ -5715,7 +6354,7 @@
Source:
@@ -5889,7 +6528,7 @@
Source:
@@ -5995,7 +6634,7 @@
Source:
@@ -6146,7 +6785,7 @@
Source:
@@ -6324,7 +6963,7 @@
Source:
@@ -6479,7 +7118,7 @@
Source:
@@ -6634,7 +7273,7 @@
Source:
@@ -6789,7 +7428,7 @@
Source:
@@ -6897,7 +7536,7 @@
Source:
@@ -6981,7 +7620,7 @@
Source:
@@ -7075,7 +7714,7 @@
Source:
@@ -7181,7 +7820,7 @@
Source:
@@ -7287,7 +7926,7 @@
Source:
@@ -7336,6 +7975,161 @@
+
+
+
+
+
+ isLabelTruthy(name) → {boolean}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Parameters:
+
+
+
+
+
+
+ Name |
+
+
+ Type |
+
+
+
+
+
+ Description |
+
+
+
+
+
+
+
+
+ name |
+
+
+
+
+
+string
+
+
+
+ |
+
+
+
+
+
+ label name |
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ - Source:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Returns:
+
+
+
+ true if label exists (including inherited) and does not have "false" value.
+
+
+
+
+
+ -
+ Type
+
+ -
+
+boolean
+
+
+
+
+
+
+
+
+
+
+
+
@@ -7357,7 +8151,7 @@
diff --git a/docs/frontend_api/FNoteComplement.html b/docs/frontend_api/FNoteComplement.html
index 8c1bb3d94..1cac29fba 100644
--- a/docs/frontend_api/FNoteComplement.html
+++ b/docs/frontend_api/FNoteComplement.html
@@ -781,7 +781,7 @@
diff --git a/docs/frontend_api/FrontendScriptApi.html b/docs/frontend_api/FrontendScriptApi.html
index e866a8473..5e94a32c2 100644
--- a/docs/frontend_api/FrontendScriptApi.html
+++ b/docs/frontend_api/FrontendScriptApi.html
@@ -342,115 +342,7 @@ available in the JS frontend notes. You can use e.g. api.showMessage(api.s
Source:
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Properties:
-
-
-
-
-
-
-
-
- Type |
-
-
-
-
-
- Description |
-
-
-
-
-
-
-
-
-
-
-
-
-RightPanelWidget
-
-
-
- |
-
-
-
-
-
- |
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- - Deprecated:
- use api.RightPanelWidget instead
-
-
-
-
-
-
-
-
-
-
-
- - Source:
-
@@ -556,115 +448,7 @@ available in the JS frontend notes. You can use e.g. api.showMessage(api.s
- Source:
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-NoteContextCachingWidget
-
-
-
-
-
-
-
-
-
-
- Properties:
-
-
-
-
-
-
-
-
- Type |
-
-
-
-
-
- Description |
-
-
-
-
-
-
-
-
-
-
-
-
-NoteContextAwareWidget
-
-
-
- |
-
-
-
-
-
- |
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- - Deprecated:
- use NoteContextAwareWidget instead
-
-
-
-
-
-
-
-
-
-
-
- - Source:
-
@@ -770,223 +554,7 @@ available in the JS frontend notes. You can use e.g. api.showMessage(api.s
- Source:
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Properties:
-
-
-
-
-
-
-
-
- Type |
-
-
-
-
-
- Description |
-
-
-
-
-
-
-
-
-
-
-
-
-NoteContextAwareWidget
-
-
-
- |
-
-
-
-
-
- |
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- - Deprecated:
- use NoteContextAwareWidget instead
-
-
-
-
-
-
-
-
-
-
-
- - Source:
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Properties:
-
-
-
-
-
-
-
-
- Type |
-
-
-
-
-
- Description |
-
-
-
-
-
-
-
-
-
-
-
-
-NoteContextAwareWidget
-
-
-
- |
-
-
-
-
-
- |
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- - Deprecated:
- use NoteContextAwareWidget instead
-
-
-
-
-
-
-
-
-
-
-
- - Source:
-
@@ -1558,7 +1126,7 @@ available in the JS frontend notes. You can use e.g. api.showMessage(api.s
- Source:
@@ -1713,7 +1281,7 @@ available in the JS frontend notes. You can use e.g. api.showMessage(api.s
- Source:
@@ -2054,7 +1622,7 @@ available in the JS frontend notes. You can use e.g. api.showMessage(api.s
- Source:
@@ -2191,7 +1759,7 @@ available in the JS frontend notes. You can use e.g. api.showMessage(api.s
- Source:
@@ -2399,7 +1967,7 @@ available in the JS frontend notes. You can use e.g. api.showMessage(api.s
- Source:
@@ -2781,7 +2349,7 @@ available in the JS frontend notes. You can use e.g. api.showMessage(api.s
- Source:
@@ -2914,7 +2482,7 @@ available in the JS frontend notes. You can use e.g. api.showMessage(api.s
- Source:
@@ -3024,7 +2592,7 @@ available in the JS frontend notes. You can use e.g. api.showMessage(api.s
- Source:
@@ -3130,7 +2698,7 @@ available in the JS frontend notes. You can use e.g. api.showMessage(api.s
- Source:
@@ -3236,7 +2804,7 @@ available in the JS frontend notes. You can use e.g. api.showMessage(api.s
- Source:
@@ -3346,7 +2914,7 @@ available in the JS frontend notes. You can use e.g. api.showMessage(api.s
- Source:
@@ -3457,7 +3025,7 @@ implementation of actual widget type.
- Source:
@@ -3612,7 +3180,7 @@ implementation of actual widget type.
- Source:
@@ -3767,7 +3335,7 @@ implementation of actual widget type.
- Source:
@@ -3874,7 +3442,7 @@ if some action needs to happen on only one specific instance.
- Source:
@@ -4029,7 +3597,7 @@ if some action needs to happen on only one specific instance.
- Source:
@@ -4185,7 +3753,7 @@ if some action needs to happen on only one specific instance.
- Source:
@@ -4386,7 +3954,7 @@ otherwise (by e.g. createNoteLink())
- Source:
@@ -4492,7 +4060,7 @@ otherwise (by e.g. createNoteLink())
- Source:
@@ -4647,7 +4215,7 @@ otherwise (by e.g. createNoteLink())
- Source:
@@ -4802,7 +4370,7 @@ otherwise (by e.g. createNoteLink())
- Source:
@@ -4952,7 +4520,7 @@ otherwise (by e.g. createNoteLink())
- Source:
@@ -5130,7 +4698,7 @@ otherwise (by e.g. createNoteLink())
- Source:
@@ -5308,7 +4876,7 @@ otherwise (by e.g. createNoteLink())
- Source:
@@ -5459,7 +5027,7 @@ otherwise (by e.g. createNoteLink())
- Source:
@@ -5637,7 +5205,7 @@ otherwise (by e.g. createNoteLink())
- Source:
@@ -5811,7 +5379,7 @@ otherwise (by e.g. createNoteLink())
- Source:
@@ -5966,7 +5534,7 @@ otherwise (by e.g. createNoteLink())
- Source:
@@ -6120,7 +5688,7 @@ otherwise (by e.g. createNoteLink())
- Source:
@@ -6275,7 +5843,7 @@ otherwise (by e.g. createNoteLink())
- Source:
@@ -6436,7 +6004,7 @@ Internally this serializes the anonymous function into string and sends it to ba
- Source:
@@ -6596,7 +6164,7 @@ Internally this serializes the anonymous function into string and sends it to ba
- Source:
@@ -6752,7 +6320,7 @@ Internally this serializes the anonymous function into string and sends it to ba
- Source:
@@ -6907,7 +6475,7 @@ Internally this serializes the anonymous function into string and sends it to ba
- Source:
@@ -7058,7 +6626,7 @@ Internally this serializes the anonymous function into string and sends it to ba
- Source:
@@ -7213,7 +6781,7 @@ Internally this serializes the anonymous function into string and sends it to ba
- Source:
@@ -7350,7 +6918,7 @@ Internally this serializes the anonymous function into string and sends it to ba
- Source:
@@ -7510,7 +7078,7 @@ Internally this serializes the anonymous function into string and sends it to ba
- Source:
@@ -7670,7 +7238,7 @@ Internally this serializes the anonymous function into string and sends it to ba
- Source:
@@ -7762,7 +7330,7 @@ Typical use case is when new note has been created, we should wait until it is s
- Source:
@@ -7832,7 +7400,7 @@ Typical use case is when new note has been created, we should wait until it is s
diff --git a/docs/frontend_api/entities_fattribute.js.html b/docs/frontend_api/entities_fattribute.js.html
index dffe972bc..a9cb49a5f 100644
--- a/docs/frontend_api/entities_fattribute.js.html
+++ b/docs/frontend_api/entities_fattribute.js.html
@@ -121,7 +121,7 @@ export default FAttribute;
diff --git a/docs/frontend_api/entities_fbranch.js.html b/docs/frontend_api/entities_fbranch.js.html
index 2b7ced98d..deae06846 100644
--- a/docs/frontend_api/entities_fbranch.js.html
+++ b/docs/frontend_api/entities_fbranch.js.html
@@ -105,7 +105,7 @@ export default FBranch;
diff --git a/docs/frontend_api/entities_fnote.js.html b/docs/frontend_api/entities_fnote.js.html
index a31f615a2..84616e2cb 100644
--- a/docs/frontend_api/entities_fnote.js.html
+++ b/docs/frontend_api/entities_fnote.js.html
@@ -101,7 +101,7 @@ class FNote {
this.mime = row.mime;
}
- addParent(parentNoteId, branchId) {
+ addParent(parentNoteId, branchId, sort = true) {
if (parentNoteId === 'none') {
return;
}
@@ -111,6 +111,10 @@ class FNote {
}
this.parentToBranch[parentNoteId] = branchId;
+
+ if (sort) {
+ this.sortParents();
+ }
}
addChild(childNoteId, branchId, sort = true) {
@@ -217,7 +221,7 @@ class FNote {
// will sort the parents so that non-search & non-archived are first and archived at the end
// this is done so that non-search & non-archived paths are always explored as first when looking for note path
- resortParents() {
+ sortParents() {
this.parents.sort((aNoteId, bNoteId) => {
const aBranchId = this.parentToBranch[aNoteId];
@@ -225,7 +229,7 @@ class FNote {
return 1;
}
- const aNote = this.froca.getNoteFromCache([aNoteId]);
+ const aNote = this.froca.getNoteFromCache(aNoteId);
if (aNote.isArchived || aNote.isHiddenCompletely()) {
return 1;
@@ -271,6 +275,11 @@ class FNote {
return this.__filterAttrs(this.__getCachedAttributes([]), type, name);
}
+ /**
+ * @param {string[]} path
+ * @return {FAttribute[]}
+ * @private
+ */
__getCachedAttributes(path) {
// notes/clones cannot form tree cycles, it is possible to create attribute inheritance cycle via templates
// when template instance is a parent of template itself
@@ -323,63 +332,49 @@ class FNote {
return this.noteId === 'root';
}
- getAllNotePaths(encounteredNoteIds = null) {
+ /**
+ * Gives all possible note paths leading to this note. Paths containing search note are ignored (could form cycles)
+ *
+ * @returns {string[][]} - array of notePaths (each represented by array of noteIds constituting the particular note path)
+ */
+ getAllNotePaths() {
if (this.noteId === 'root') {
return [['root']];
}
- if (!encounteredNoteIds) {
- encounteredNoteIds = new Set();
+ const parentNotes = this.getParentNotes().filter(note => note.type !== 'search');
+ let notePaths = [];
+
+ if (parentNotes.length === 1) { // optimization for most common case
+ notePaths = parentNotes[0].getAllNotePaths();
+ } else {
+ notePaths = parentNotes.flatMap(parentNote => parentNote.getAllNotePaths());
}
- encounteredNoteIds.add(this.noteId);
-
- const parentNotes = this.getParentNotes();
- let paths;
-
- if (parentNotes.length === 1) { // optimization for the most common case
- if (encounteredNoteIds.has(parentNotes[0].noteId)) {
- return [];
- }
- else {
- paths = parentNotes[0].getAllNotePaths(encounteredNoteIds);
- }
- }
- else {
- paths = [];
-
- for (const parentNote of parentNotes) {
- if (encounteredNoteIds.has(parentNote.noteId)) {
- continue;
- }
-
- const newSet = new Set(encounteredNoteIds);
-
- paths.push(...parentNote.getAllNotePaths(newSet));
- }
+ for (const notePath of notePaths) {
+ notePath.push(this.noteId);
}
- for (const path of paths) {
- path.push(this.noteId);
- }
-
- return paths;
+ return notePaths;
}
- getSortedNotePaths(hoistedNotePath = 'root') {
+ /**
+ * @param {string} [hoistedNoteId='root']
+ * @return {Array<{isArchived: boolean, isInHoistedSubTree: boolean, notePath: Array<string>, isHidden: boolean}>}
+ */
+ getSortedNotePathRecords(hoistedNoteId = 'root') {
+ const isHoistedRoot = hoistedNoteId === 'root';
+
const notePaths = this.getAllNotePaths().map(path => ({
notePath: path,
- isInHoistedSubTree: path.includes(hoistedNotePath),
- isArchived: path.find(noteId => froca.notes[noteId].isArchived),
- isSearch: path.find(noteId => froca.notes[noteId].type === 'search'),
+ isInHoistedSubTree: isHoistedRoot || path.includes(hoistedNoteId),
+ isArchived: path.some(noteId => froca.notes[noteId].isArchived),
isHidden: path.includes('_hidden')
}));
notePaths.sort((a, b) => {
if (a.isInHoistedSubTree !== b.isInHoistedSubTree) {
return a.isInHoistedSubTree ? -1 : 1;
- } else if (a.isSearch !== b.isSearch) {
- return a.isSearch ? 1 : -1;
} else if (a.isArchived !== b.isArchived) {
return a.isArchived ? 1 : -1;
} else if (a.isHidden !== b.isHidden) {
@@ -392,6 +387,28 @@ class FNote {
return notePaths;
}
+ /**
+ * Returns note path considered to be the "best"
+ *
+ * @param {string} [hoistedNoteId='root']
+ * @return {string[]} array of noteIds constituting the particular note path
+ */
+ getBestNotePath(hoistedNoteId = 'root') {
+ return this.getSortedNotePathRecords(hoistedNoteId)[0]?.notePath;
+ }
+
+ /**
+ * Returns note path considered to be the "best"
+ *
+ * @param {string} [hoistedNoteId='root']
+ * @return {string} serialized note path (e.g. 'root/a1h315/js725h')
+ */
+ getBestNotePathString(hoistedNoteId = 'root') {
+ const notePath = this.getBestNotePath(hoistedNoteId);
+
+ return notePath?.join("/");
+ }
+
/**
* @return boolean - true if there's no non-hidden path, note is not cloned to the visible tree
*/
@@ -403,7 +420,7 @@ class FNote {
for (const parentNote of this.getParentNotes()) {
if (parentNote.noteId === 'root') {
return false;
- } else if (parentNote.noteId === '_hidden') {
+ } else if (parentNote.noteId === '_hidden' || parentNote.type === 'search') {
continue;
}
@@ -415,6 +432,13 @@ class FNote {
return true;
}
+ /**
+ * @param {FAttribute[]} attributes
+ * @param {string} type
+ * @param {string} name
+ * @return {FAttribute[]}
+ * @private
+ */
__filterAttrs(attributes, type, name) {
this.__validateTypeName(type, name);
@@ -551,7 +575,9 @@ class FNote {
* @returns {boolean} true if note has an attribute with given type and name (including inherited)
*/
hasAttribute(type, name) {
- return !!this.getAttribute(type, name);
+ const attributes = this.getAttributes();
+
+ return attributes.some(attr => attr.name === name && attr.type === type);
}
/**
@@ -619,6 +645,20 @@ class FNote {
*/
hasLabel(name) { return this.hasAttribute(LABEL, name); }
+ /**
+ * @param {string} name - label name
+ * @returns {boolean} true if label exists (including inherited) and does not have "false" value.
+ */
+ isLabelTruthy(name) {
+ const label = this.getLabel(name);
+
+ if (!label) {
+ return false;
+ }
+
+ return label && label.value !== 'false';
+ }
+
/**
* @param {string} name - relation name
* @returns {boolean} true if relation exists (excluding inherited)
@@ -730,7 +770,14 @@ class FNote {
});
// attrs are not resorted if position changes after initial load
- promotedAttrs.sort((a, b) => a.position < b.position ? -1 : 1);
+ promotedAttrs.sort((a, b) => {
+ if (a.noteId === b.noteId) {
+ return a.position < b.position ? -1 : 1;
+ } else {
+ // inherited promoted attributes should stay grouped: https://github.com/zadam/trilium/issues/3761
+ return a.noteId < b.noteId ? -1 : 1;
+ }
+ });
return promotedAttrs;
}
@@ -930,7 +977,7 @@ export default FNote;
diff --git a/docs/frontend_api/entities_fnote_complement.js.html b/docs/frontend_api/entities_fnote_complement.js.html
index 6ea35e95a..252ea6a2f 100644
--- a/docs/frontend_api/entities_fnote_complement.js.html
+++ b/docs/frontend_api/entities_fnote_complement.js.html
@@ -82,7 +82,7 @@ export default FNoteComplement;
diff --git a/docs/frontend_api/index.html b/docs/frontend_api/index.html
index 08a8eb159..6465f193a 100644
--- a/docs/frontend_api/index.html
+++ b/docs/frontend_api/index.html
@@ -56,7 +56,7 @@
diff --git a/docs/frontend_api/services_frontend_script_api.js.html b/docs/frontend_api/services_frontend_script_api.js.html
index a326810c3..679f2e5eb 100644
--- a/docs/frontend_api/services_frontend_script_api.js.html
+++ b/docs/frontend_api/services_frontend_script_api.js.html
@@ -63,36 +63,12 @@ function FrontendScriptApi(startNote, currentNote, originEntity = null, $contain
/** @property {dayjs} day.js library for date manipulation. See {@link https://day.js.org} for documentation */
this.dayjs = dayjs;
- /**
- * @property {RightPanelWidget}
- * @deprecated use api.RightPanelWidget instead
- */
- this.CollapsibleWidget = RightPanelWidget;
-
/** @property {RightPanelWidget} */
this.RightPanelWidget = RightPanelWidget;
/** @property {NoteContextAwareWidget} */
this.NoteContextAwareWidget = NoteContextAwareWidget;
- /**
- * @property {NoteContextAwareWidget}
- * @deprecated use NoteContextAwareWidget instead
- */
- this.TabAwareWidget = NoteContextAwareWidget;
-
- /**
- * @property {NoteContextAwareWidget}
- * @deprecated use NoteContextAwareWidget instead
- */
- this.TabCachingWidget = NoteContextAwareWidget;
-
- /**
- * @property {NoteContextAwareWidget}
- * @deprecated use NoteContextAwareWidget instead
- */
- this.NoteContextCachingWidget = NoteContextAwareWidget;
-
/** @property {BasicWidget} */
this.BasicWidget = BasicWidget;
@@ -117,7 +93,7 @@ function FrontendScriptApi(startNote, currentNote, originEntity = null, $contain
await ws.waitForMaxKnownEntityChangeId();
await appContext.tabManager.getActiveContext().setNote(notePath);
- appContext.triggerEvent('focusAndSelectTitle');
+ await appContext.triggerEvent('focusAndSelectTitle');
};
/**
@@ -134,7 +110,7 @@ function FrontendScriptApi(startNote, currentNote, originEntity = null, $contain
await appContext.tabManager.openContextWithNote(notePath, { activate });
if (activate) {
- appContext.triggerEvent('focusAndSelectTitle');
+ await appContext.triggerEvent('focusAndSelectTitle');
}
};
@@ -152,10 +128,10 @@ function FrontendScriptApi(startNote, currentNote, originEntity = null, $contain
const subContexts = appContext.tabManager.getActiveContext().getSubContexts();
const {ntxId} = subContexts[subContexts.length - 1];
- appContext.triggerCommand("openNewNoteSplit", {ntxId, notePath});
+ await appContext.triggerCommand("openNewNoteSplit", {ntxId, notePath});
if (activate) {
- appContext.triggerEvent('focusAndSelectTitle');
+ await appContext.triggerEvent('focusAndSelectTitle');
}
};
@@ -581,7 +557,7 @@ export default FrontendScriptApi;
diff --git a/package-lock.json b/package-lock.json
index 6055a636e..14cc4892b 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -12,7 +12,7 @@
"dependencies": {
"@braintree/sanitize-url": "6.0.2",
"@electron/remote": "2.0.9",
- "@excalidraw/excalidraw": "0.15.2",
+ "@excalidraw/excalidraw": "0.14.2",
"archiver": "5.3.1",
"async-mutex": "0.4.0",
"axios": "1.4.0",
@@ -462,9 +462,9 @@
}
},
"node_modules/@excalidraw/excalidraw": {
- "version": "0.15.2",
- "resolved": "https://registry.npmjs.org/@excalidraw/excalidraw/-/excalidraw-0.15.2.tgz",
- "integrity": "sha512-rTI02kgWSTXiUdIkBxt9u/581F3eXcqQgJdIxmz54TFtG3ughoxO5fr4t7Fr2LZIturBPqfocQHGKZ0t2KLKgw==",
+ "version": "0.14.2",
+ "resolved": "https://registry.npmjs.org/@excalidraw/excalidraw/-/excalidraw-0.14.2.tgz",
+ "integrity": "sha512-8LdjpTBWEK5waDWB7Bt/G9YBI4j0OxkstUhvaDGz7dwQGfzF6FW5CXBoYHNEoX0qmb+Fg/NPOlZ7FrKsrSVCqg==",
"peerDependencies": {
"react": "^17.0.2 || ^18.2.0",
"react-dom": "^17.0.2 || ^18.2.0"
@@ -13591,9 +13591,9 @@
"dev": true
},
"@excalidraw/excalidraw": {
- "version": "0.15.2",
- "resolved": "https://registry.npmjs.org/@excalidraw/excalidraw/-/excalidraw-0.15.2.tgz",
- "integrity": "sha512-rTI02kgWSTXiUdIkBxt9u/581F3eXcqQgJdIxmz54TFtG3ughoxO5fr4t7Fr2LZIturBPqfocQHGKZ0t2KLKgw==",
+ "version": "0.14.2",
+ "resolved": "https://registry.npmjs.org/@excalidraw/excalidraw/-/excalidraw-0.14.2.tgz",
+ "integrity": "sha512-8LdjpTBWEK5waDWB7Bt/G9YBI4j0OxkstUhvaDGz7dwQGfzF6FW5CXBoYHNEoX0qmb+Fg/NPOlZ7FrKsrSVCqg==",
"requires": {}
},
"@gar/promisify": {
diff --git a/package.json b/package.json
index a0a42ea5f..3353f12b9 100644
--- a/package.json
+++ b/package.json
@@ -33,7 +33,7 @@
"dependencies": {
"@braintree/sanitize-url": "6.0.2",
"@electron/remote": "2.0.9",
- "@excalidraw/excalidraw": "0.15.2",
+ "@excalidraw/excalidraw": "0.14.2",
"archiver": "5.3.1",
"async-mutex": "0.4.0",
"axios": "1.4.0",
diff --git a/src/becca/becca_loader.js b/src/becca/becca_loader.js
index f7659e964..dd8e990a4 100644
--- a/src/becca/becca_loader.js
+++ b/src/becca/becca_loader.js
@@ -69,18 +69,6 @@ function reload() {
require('../services/ws').reloadFrontend();
}
-function postProcessEntityUpdate(entityName, entity) {
- if (entityName === 'notes') {
- noteUpdated(entity);
- } else if (entityName === 'branches') {
- branchUpdated(entity);
- } else if (entityName === 'attributes') {
- attributeUpdated(entity);
- } else if (entityName === 'note_reordering') {
- noteReorderingUpdated(entity);
- }
-}
-
eventService.subscribeBeccaLoader([eventService.ENTITY_CHANGE_SYNCED], ({entityName, entityRow}) => {
if (!becca.loaded) {
return;
@@ -112,6 +100,25 @@ eventService.subscribeBeccaLoader(eventService.ENTITY_CHANGED, ({entityName, en
postProcessEntityUpdate(entityName, entity);
});
+/**
+ * This gets run on entity being created or updated.
+ *
+ * @param entityName
+ * @param entityRow - can be a becca entity (change comes from this trilium instance) or just a row (from sync).
+ * Should be therefore treated as a row.
+ */
+function postProcessEntityUpdate(entityName, entityRow) {
+ if (entityName === 'notes') {
+ noteUpdated(entityRow);
+ } else if (entityName === 'branches') {
+ branchUpdated(entityRow);
+ } else if (entityName === 'attributes') {
+ attributeUpdated(entityRow);
+ } else if (entityName === 'note_reordering') {
+ noteReorderingUpdated(entityRow);
+ }
+}
+
eventService.subscribeBeccaLoader([eventService.ENTITY_DELETED, eventService.ENTITY_DELETE_SYNCED], ({entityName, entityId}) => {
if (!becca.loaded) {
return;
@@ -149,6 +156,7 @@ function branchDeleted(branchId) {
.filter(parentBranch => parentBranch.branchId !== branch.branchId);
if (childNote.parents.length > 0) {
+ // subtree notes might lose some inherited attributes
childNote.invalidateSubTree();
}
}
@@ -163,8 +171,8 @@ function branchDeleted(branchId) {
delete becca.branches[branch.branchId];
}
-function noteUpdated(entity) {
- const note = becca.notes[entity.noteId];
+function noteUpdated(entityRow) {
+ const note = becca.notes[entityRow.noteId];
if (note) {
// type / mime could have been changed, and they are present in flatTextCache
@@ -172,15 +180,19 @@ function noteUpdated(entity) {
}
}
-function branchUpdated(branch) {
- const childNote = becca.notes[branch.noteId];
+function branchUpdated(branchRow) {
+ const childNote = becca.notes[branchRow.noteId];
if (childNote) {
childNote.flatTextCache = null;
childNote.sortParents();
+
+ // notes in the subtree can get new inherited attributes
+ // this is in theory needed upon branch creation, but there's no create event for sync changes
+ childNote.invalidateSubTree();
}
- const parentNote = becca.notes[branch.parentNoteId];
+ const parentNote = becca.notes[branchRow.parentNoteId];
if (parentNote) {
parentNote.sortChildren();
@@ -222,8 +234,10 @@ function attributeDeleted(attributeId) {
}
}
-function attributeUpdated(attribute) {
- const note = becca.notes[attribute.noteId];
+/** @param {BAttribute} attributeRow */
+function attributeUpdated(attributeRow) {
+ const attribute = becca.attributes[attributeRow.attributeId];
+ const note = becca.notes[attributeRow.noteId];
if (note) {
if (attribute.isAffectingSubtree || note.isInherited()) {
diff --git a/src/becca/entities/bnote.js b/src/becca/entities/bnote.js
index b8e58c740..09dd7e679 100644
--- a/src/becca/entities/bnote.js
+++ b/src/becca/entities/bnote.js
@@ -12,6 +12,7 @@ const TaskContext = require("../../services/task_context");
const dayjs = require("dayjs");
const utc = require('dayjs/plugin/utc');
const eventService = require("../../services/events");
+const cls = require("../../services/cls.js");
dayjs.extend(utc);
const LABEL = 'label';
@@ -84,7 +85,7 @@ class BNote extends AbstractBeccaEntity {
this.decrypt();
/** @type {string|null} */
- this.flatTextCache = null;
+ this.__flatTextCache = null;
return this;
}
@@ -108,7 +109,7 @@ class BNote extends AbstractBeccaEntity {
this.__attributeCache = null;
/** @type {BAttribute[]|null}
* @private */
- this.inheritableAttributeCache = null;
+ this.__inheritableAttributeCache = null;
/** @type {BAttribute[]}
* @private */
@@ -118,7 +119,7 @@ class BNote extends AbstractBeccaEntity {
/** @type {BNote[]|null}
* @private */
- this.ancestorCache = null;
+ this.__ancestorCache = null;
// following attributes are filled during searching from database
@@ -316,10 +317,12 @@ class BNote extends AbstractBeccaEntity {
isSynced: true
});
- eventService.emit(eventService.ENTITY_CHANGED, {
- entityName: 'note_contents',
- entity: this
- });
+ if (!cls.isEntityEventsDisabled()) {
+ eventService.emit(eventService.ENTITY_CHANGED, {
+ entityName: 'note_contents',
+ entity: this
+ });
+ }
}
setJsonContent(content) {
@@ -454,11 +457,11 @@ class BNote extends AbstractBeccaEntity {
}
}
- this.inheritableAttributeCache = [];
+ this.__inheritableAttributeCache = [];
for (const attr of this.__attributeCache) {
if (attr.isInheritable) {
- this.inheritableAttributeCache.push(attr);
+ this.__inheritableAttributeCache.push(attr);
}
}
}
@@ -475,11 +478,11 @@ class BNote extends AbstractBeccaEntity {
return [];
}
- if (!this.inheritableAttributeCache) {
- this.__getAttributes(path); // will refresh also this.inheritableAttributeCache
+ if (!this.__inheritableAttributeCache) {
+ this.__getAttributes(path); // will refresh also this.__inheritableAttributeCache
}
- return this.inheritableAttributeCache;
+ return this.__inheritableAttributeCache;
}
__validateTypeName(type, name) {
@@ -813,40 +816,40 @@ class BNote extends AbstractBeccaEntity {
* @returns {string} - returns flattened textual representation of note, prefixes and attributes
*/
getFlatText() {
- if (!this.flatTextCache) {
- this.flatTextCache = `${this.noteId} ${this.type} ${this.mime} `;
+ if (!this.__flatTextCache) {
+ this.__flatTextCache = `${this.noteId} ${this.type} ${this.mime} `;
for (const branch of this.parentBranches) {
if (branch.prefix) {
- this.flatTextCache += `${branch.prefix} `;
+ this.__flatTextCache += `${branch.prefix} `;
}
}
- this.flatTextCache += `${this.title} `;
+ this.__flatTextCache += `${this.title} `;
for (const attr of this.getAttributes()) {
// it's best to use space as separator since spaces are filtered from the search string by the tokenization into words
- this.flatTextCache += `${attr.type === 'label' ? '#' : '~'}${attr.name}`;
+ this.__flatTextCache += `${attr.type === 'label' ? '#' : '~'}${attr.name}`;
if (attr.value) {
- this.flatTextCache += `=${attr.value}`;
+ this.__flatTextCache += `=${attr.value}`;
}
- this.flatTextCache += ' ';
+ this.__flatTextCache += ' ';
}
- this.flatTextCache = utils.normalize(this.flatTextCache);
+ this.__flatTextCache = utils.normalize(this.__flatTextCache);
}
- return this.flatTextCache;
+ return this.__flatTextCache;
}
invalidateThisCache() {
- this.flatTextCache = null;
+ this.__flatTextCache = null;
this.__attributeCache = null;
- this.inheritableAttributeCache = null;
- this.ancestorCache = null;
+ this.__inheritableAttributeCache = null;
+ this.__ancestorCache = null;
}
invalidateSubTree(path = []) {
@@ -875,24 +878,6 @@ class BNote extends AbstractBeccaEntity {
}
}
- invalidateSubtreeFlatText() {
- this.flatTextCache = null;
-
- for (const childNote of this.children) {
- childNote.invalidateSubtreeFlatText();
- }
-
- for (const targetRelation of this.targetRelations) {
- if (targetRelation.name === 'template' || targetRelation.name === 'inherit') {
- const note = targetRelation.note;
-
- if (note) {
- note.invalidateSubtreeFlatText();
- }
- }
- }
- }
-
getRelationDefinitions() {
return this.getLabels()
.filter(l => l.name.startsWith("relation:"));
@@ -1083,28 +1068,28 @@ class BNote extends AbstractBeccaEntity {
/** @returns {BNote[]} */
getAncestors() {
- if (!this.ancestorCache) {
+ if (!this.__ancestorCache) {
const noteIds = new Set();
- this.ancestorCache = [];
+ this.__ancestorCache = [];
for (const parent of this.parents) {
if (noteIds.has(parent.noteId)) {
continue;
}
- this.ancestorCache.push(parent);
+ this.__ancestorCache.push(parent);
noteIds.add(parent.noteId);
for (const ancestorNote of parent.getAncestors()) {
if (!noteIds.has(ancestorNote.noteId)) {
- this.ancestorCache.push(ancestorNote);
+ this.__ancestorCache.push(ancestorNote);
noteIds.add(ancestorNote.noteId);
}
}
}
}
- return this.ancestorCache;
+ return this.__ancestorCache;
}
/** @returns {boolean} */
@@ -1192,7 +1177,7 @@ class BNote extends AbstractBeccaEntity {
/**
* @param {string} [hoistedNoteId='root']
- * @return {{isArchived: boolean, isInHoistedSubTree: boolean, notePath: string[], isHidden: boolean}[]}
+ * @return {Array<{isArchived: boolean, isInHoistedSubTree: boolean, notePath: Array, isHidden: boolean}>}
*/
getSortedNotePathRecords(hoistedNoteId = 'root') {
const isHoistedRoot = hoistedNoteId === 'root';
@@ -1491,7 +1476,7 @@ class BNote extends AbstractBeccaEntity {
if (this.isProtected && !this.isDecrypted && protectedSessionService.isProtectedSessionAvailable()) {
try {
this.title = protectedSessionService.decryptString(this.title);
- this.flatTextCache = null;
+ this.__flatTextCache = null;
this.isDecrypted = true;
}
diff --git a/src/etapi/mappers.js b/src/etapi/mappers.js
index ad959f36a..86fea9c3d 100644
--- a/src/etapi/mappers.js
+++ b/src/etapi/mappers.js
@@ -1,3 +1,4 @@
+/** @param {BNote} note */
function mapNoteToPojo(note) {
return {
noteId: note.noteId,
@@ -17,6 +18,7 @@ function mapNoteToPojo(note) {
};
}
+/** @param {BBranch} branch */
function mapBranchToPojo(branch) {
return {
branchId: branch.branchId,
@@ -29,6 +31,7 @@ function mapBranchToPojo(branch) {
};
}
+/** @param {BAttribute} attr */
function mapAttributeToPojo(attr) {
return {
attributeId: attr.attributeId,
@@ -46,4 +49,4 @@ module.exports = {
mapNoteToPojo,
mapBranchToPojo,
mapAttributeToPojo
-};
\ No newline at end of file
+};
diff --git a/src/public/app/entities/fnote.js b/src/public/app/entities/fnote.js
index eff96df81..d73f14de2 100644
--- a/src/public/app/entities/fnote.js
+++ b/src/public/app/entities/fnote.js
@@ -332,7 +332,7 @@ class FNote {
/**
* @param {string} [hoistedNoteId='root']
- * @return {{isArchived: boolean, isInHoistedSubTree: boolean, notePath: string[], isHidden: boolean}[]}
+ * @return {Array<{isArchived: boolean, isInHoistedSubTree: boolean, notePath: Array, isHidden: boolean}>}
*/
getSortedNotePathRecords(hoistedNoteId = 'root') {
const isHoistedRoot = hoistedNoteId === 'root';
diff --git a/src/public/app/widgets/title_bar_buttons.js b/src/public/app/widgets/title_bar_buttons.js
index 6a4f73ad5..89dd43601 100644
--- a/src/public/app/widgets/title_bar_buttons.js
+++ b/src/public/app/widgets/title_bar_buttons.js
@@ -56,13 +56,15 @@ export default class TitleBarButtonsWidget extends BasicWidget {
const $maximizeBtn = this.$widget.find(".maximize-btn");
const $closeBtn = this.$widget.find(".close-btn");
- //When the window is restarted, the window will not be reset when it is set to the top, so get the window status and set the icon background
- (function () {
+ // When the window is restarted, the window will not be reset when it is set to the top,
+ // so get the window status and set the icon background
+ setTimeout(() => {
const remote = utils.dynamicRequire('@electron/remote');
- if (remote.BrowserWindow.getFocusedWindow().isAlwaysOnTop()) {
+ if (remote.BrowserWindow.getFocusedWindow()?.isAlwaysOnTop()) {
$topBtn.addClass('active');
}
- }());
+ }, 1000);
+
$topBtn.on('click', () => {
$topBtn.trigger('blur');
const remote = utils.dynamicRequire('@electron/remote');
diff --git a/src/services/notes.js b/src/services/notes.js
index f3d219145..c9343bc4e 100644
--- a/src/services/notes.js
+++ b/src/services/notes.js
@@ -54,11 +54,10 @@ function deriveMime(type, mime) {
}
function copyChildAttributes(parentNote, childNote) {
- const hasAlreadyTemplate = childNote.hasRelation('template');
-
for (const attr of parentNote.getAttributes()) {
if (attr.name.startsWith("child:")) {
const name = attr.name.substr(6);
+ const hasAlreadyTemplate = childNote.hasRelation('template');
if (hasAlreadyTemplate && attr.type === 'relation' && name === 'template') {
// if the note already has a template, it means the template was chosen by the user explicitly
@@ -174,7 +173,7 @@ function createNewNote(params) {
// TODO: think about what can happen if the note already exists with the forced ID
// I guess on DB it's going to be fine, but becca references between entities
- // might get messed up (two Note instance for the same ID existing in the references)
+ // might get messed up (two note instances for the same ID existing in the references)
note = new BNote({
noteId: params.noteId, // optionally can force specific noteId
title: params.title,
@@ -195,7 +194,7 @@ function createNewNote(params) {
}
finally {
if (!isEntityEventsDisabled) {
- // re-enable entity events only if there were previously enabled
+ // re-enable entity events only if they were previously enabled
// (they can be disabled in case of import)
cls.enableEntityEvents();
}
@@ -215,27 +214,14 @@ function createNewNote(params) {
copyChildAttributes(parentNote, note);
+ eventService.emit(eventService.ENTITY_CREATED, { entityName: 'notes', entity: note });
+ eventService.emit(eventService.ENTITY_CHANGED, { entityName: 'notes', entity: note });
triggerNoteTitleChanged(note);
-
- eventService.emit(eventService.ENTITY_CREATED, {
- entityName: 'notes',
- entity: note
- });
-
- eventService.emit(eventService.ENTITY_CREATED, {
- entityName: 'note_contents',
- entity: note
- });
-
- eventService.emit(eventService.ENTITY_CREATED, {
- entityName: 'branches',
- entity: branch
- });
-
- eventService.emit(eventService.CHILD_NOTE_CREATED, {
- childNote: note,
- parentNote: parentNote
- });
+ // note_contents doesn't use "created" event
+ eventService.emit(eventService.ENTITY_CHANGED, { entityName: 'note_contents', entity: note });
+ eventService.emit(eventService.ENTITY_CREATED, { entityName: 'branches', entity: branch });
+ eventService.emit(eventService.ENTITY_CHANGED, { entityName: 'branches', entity: branch });
+ eventService.emit(eventService.CHILD_NOTE_CREATED, { childNote: note, parentNote: parentNote });
log.info(`Created new note '${note.noteId}', branch '${branch.branchId}' of type '${note.type}', mime '${note.mime}'`);
diff --git a/src/share/shaca/entities/snote.js b/src/share/shaca/entities/snote.js
index e083ca97b..9c8dbbfcc 100644
--- a/src/share/shaca/entities/snote.js
+++ b/src/share/shaca/entities/snote.js
@@ -40,7 +40,7 @@ class SNote extends AbstractShacaEntity {
/** @param {SAttribute[]|null} */
this.__attributeCache = null;
/** @param {SAttribute[]|null} */
- this.inheritableAttributeCache = null;
+ this.__inheritableAttributeCache = null;
/** @param {SAttribute[]} */
this.targetRelations = [];
@@ -190,11 +190,11 @@ class SNote extends AbstractShacaEntity {
}
}
- this.inheritableAttributeCache = [];
+ this.__inheritableAttributeCache = [];
for (const attr of this.__attributeCache) {
if (attr.isInheritable) {
- this.inheritableAttributeCache.push(attr);
+ this.__inheritableAttributeCache.push(attr);
}
}
}
@@ -208,11 +208,11 @@ class SNote extends AbstractShacaEntity {
return [];
}
- if (!this.inheritableAttributeCache) {
- this.__getAttributes(path); // will refresh also this.inheritableAttributeCache
+ if (!this.__inheritableAttributeCache) {
+ this.__getAttributes(path); // will refresh also this.__inheritableAttributeCache
}
- return this.inheritableAttributeCache;
+ return this.__inheritableAttributeCache;
}
/** @returns {boolean} */
diff --git a/test-etapi/get-inherited-attribute-cloned.http b/test-etapi/get-inherited-attribute-cloned.http
new file mode 100644
index 000000000..06c1aa976
--- /dev/null
+++ b/test-etapi/get-inherited-attribute-cloned.http
@@ -0,0 +1,116 @@
+POST {{triliumHost}}/etapi/create-note
+Authorization: {{authToken}}
+Content-Type: application/json
+
+{
+ "parentNoteId": "root",
+ "title": "Hello parent",
+ "type": "text",
+ "content": "Hi there!"
+}
+
+> {%
+client.assert(response.status === 201);
+client.global.set("parentNoteId", response.body.note.noteId);
+client.global.set("parentBranchId", response.body.branch.branchId);
+%}
+
+### Create inheritable parent attribute
+
+POST {{triliumHost}}/etapi/attributes
+Authorization: {{authToken}}
+Content-Type: application/json
+
+{
+ "noteId": "{{parentNoteId}}",
+ "type": "label",
+ "name": "mylabel",
+ "value": "",
+ "isInheritable": true,
+ "position": 10
+}
+
+> {%
+client.assert(response.status === 201);
+client.global.set("parentAttributeId", response.body.attributeId);
+%}
+
+### Create child note under root
+
+POST {{triliumHost}}/etapi/create-note
+Authorization: {{authToken}}
+Content-Type: application/json
+
+{
+ "parentNoteId": "root",
+ "title": "Hello child",
+ "type": "text",
+ "content": "Hi there!"
+}
+
+> {%
+client.assert(response.status === 201);
+client.global.set("childNoteId", response.body.note.noteId);
+client.global.set("childBranchId", response.body.branch.branchId);
+%}
+
+### Create child attribute
+
+POST {{triliumHost}}/etapi/attributes
+Authorization: {{authToken}}
+Content-Type: application/json
+
+{
+ "noteId": "{{childNoteId}}",
+ "type": "label",
+ "name": "mylabel",
+ "value": "val",
+ "isInheritable": false,
+ "position": 10
+}
+
+> {%
+client.assert(response.status === 201);
+client.global.set("childAttributeId", response.body.attributeId);
+%}
+
+### Clone child to parent
+
+POST {{triliumHost}}/etapi/branches
+Authorization: {{authToken}}
+Content-Type: application/json
+
+{
+ "noteId": "{{childNoteId}}",
+ "parentNoteId": "{{parentNoteId}}"
+}
+
+> {%
+client.assert(response.status === 201);
+client.assert(response.body.parentNoteId == client.global.get("parentNoteId"));
+%}
+
+###
+
+GET {{triliumHost}}/etapi/notes/{{childNoteId}}
+Authorization: {{authToken}}
+
+> {%
+
+function hasAttribute(list, attributeId) {
+ for (let i = 0; i < list.length; i++) {
+ if (list[i]["attributeId"] === attributeId) {
+ return true;
+ }
+ }
+ return false;
+}
+
+client.assert(response.status === 200);
+client.assert(response.body.noteId == client.global.get("childNoteId"));
+client.assert(response.body.attributes.length == 2);
+client.assert(hasAttribute(response.body.attributes,
+ client.global.get("parentAttributeId")));
+client.assert(hasAttribute(response.body.attributes,
+ client.global.get("childAttributeId")));
+%}
diff --git a/test-etapi/get-inherited-attribute.http b/test-etapi/get-inherited-attribute.http
new file mode 100644
index 000000000..d614f419e
--- /dev/null
+++ b/test-etapi/get-inherited-attribute.http
@@ -0,0 +1,44 @@
+POST {{triliumHost}}/etapi/attributes
+Authorization: {{authToken}}
+Content-Type: application/json
+
+{
+ "noteId": "root",
+ "type": "label",
+ "name": "mylabel",
+ "value": "val",
+ "isInheritable": true
+}
+
+> {% client.global.set("createdAttributeId", response.body.attributeId); %}
+
+###
+
+POST {{triliumHost}}/etapi/create-note
+Authorization: {{authToken}}
+Content-Type: application/json
+
+{
+ "parentNoteId": "root",
+ "title": "Hello",
+ "type": "text",
+ "content": "Hi there!"
+}
+
+> {%
+client.global.set("createdNoteId", response.body.note.noteId);
+client.global.set("createdBranchId", response.body.branch.branchId);
+%}
+
+###
+
+GET {{triliumHost}}/etapi/notes/{{createdNoteId}}
+Authorization: {{authToken}}
+
+> {%
+client.assert(response.status === 200);
+client.assert(response.body.noteId == client.global.get("createdNoteId"));
+client.assert(response.body.attributes.length == 1);
+client.assert(response.body.attributes[0].attributeId ==
+ client.global.get("createdAttributeId"));
+%}