Compare commits

...

27 Commits

Author SHA1 Message Date
zadam
30e75056bd release 0.61.3-beta 2023-07-31 23:03:45 +02:00
zadam
530e56dcb5 fixed comment 2023-07-31 22:59:47 +02:00
zadam
63675bfbae make migration 223 NOOP 2023-07-30 21:54:01 +02:00
zadam
696ce38083 ckeditor 38.1.1 2023-07-30 00:32:16 +02:00
zadam
12014b9f4d sync fixes and refactorings 2023-07-29 23:35:08 +02:00
zadam
e8b52f9e6c sync fixes and refactorings 2023-07-29 23:25:02 +02:00
zadam
04b125afc0 sync fixes and refactorings 2023-07-29 21:59:20 +02:00
zadam
2a7fe85020 VACUUM database after migration 2023-07-28 16:22:10 +02:00
zadam
119050e355 fix migration 2023-07-28 16:13:31 +02:00
zadam
72122d0f95 fix blobIds - they shouldn't contain + and / characters 2023-07-28 15:55:26 +02:00
zadam
bd22863bb7 release 0.61.2-beta 2023-07-28 00:06:23 +02:00
zadam
ce3834eb9e blob erasure is not synced, need to clean them up before each content hash check 2023-07-27 23:57:12 +02:00
zadam
8edb5428e5 small sync fixes and refactorings 2023-07-27 23:22:08 +02:00
zadam
527718eff7 add indices on blobId 2023-07-27 21:40:47 +02:00
zadam
5b0f487f3f handle ERR_* errors gracefully 2023-07-27 14:50:18 +02:00
zadam
a3fa8341ba handle ERR_CONNECTION_RESET gracefully 2023-07-27 10:55:34 +02:00
zadam
04813743e2 fix zooming speed, closes #4121 2023-07-25 22:33:35 +02:00
zadam
ddf75cd5e5 note tooltip displays the whole note with scrollbar, other behavior changes. closes #4120 2023-07-25 22:27:15 +02:00
zadam
d0e5ad5b7e release 0.61.1-beta 2023-07-24 23:47:57 +02:00
zadam
b4631e927c Merge remote-tracking branch 'origin/master' 2023-07-24 23:47:27 +02:00
zadam
b7bc843a37 fix demo document statistics, closes #4117 2023-07-24 23:46:02 +02:00
zadam
98d65f8767 Merge pull request #4114 from SiriusXT/master
Fix bugs in highlights,v0.61.0-beta
2023-07-24 22:58:20 +02:00
zadam
0599891ec0 Merge pull request #4001 from dymani/reopen-in-place
Reopen recently closed tab/split in place
2023-07-24 22:52:14 +02:00
zadam
cc06701565 swiftshader deletion is no longer needed 2023-07-24 22:33:36 +02:00
SnnBcd xt
40683985cd Fix bugs in highlights 2023-07-22 19:13:03 +00:00
dymani
50ec17433d Fix move pane button after reopen split 2023-06-03 05:59:53 +08:00
dymani
2c8eb82d42 Reopen recently closed tab/split in place 2023-06-03 05:54:33 +08:00
74 changed files with 1986 additions and 1868 deletions

1
.idea/misc.xml generated
View File

@@ -1,4 +1,3 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="JavaScriptSettings">
<option name="languageLevel" value="ES6" />

View File

@@ -24,9 +24,6 @@ mv "./dist/Trilium Notes-linux-x64" $BUILD_DIR
cp images/app-icons/png/128x128.png $BUILD_DIR/icon.png
# removing software WebGL binaries because they are pretty huge and not necessary
rm -r $BUILD_DIR/swiftshader
cp bin/tpl/anonymize-database.sql $BUILD_DIR/
cp -r dump-db $BUILD_DIR/

View File

@@ -22,9 +22,6 @@ rm -rf $BUILD_DIR
mv "./dist/Trilium Notes-win32-x64" $BUILD_DIR
# removing software WebGL binaries because they are pretty huge and not necessary
rm -r $BUILD_DIR/swiftshader
cp bin/tpl/anonymize-database.sql $BUILD_DIR/
cp -r dump-db $BUILD_DIR/

Binary file not shown.

View File

@@ -3,11 +3,11 @@ module.exports = async () => {
const beccaLoader = require("../../src/becca/becca_loader");
const log = require("../../src/services/log");
const consistencyChecks = require("../../src/services/consistency_checks");
const noteService = require("../../src/services/notes");
const eraseService = require("../../src/services/erase");
await cls.init(async () => {
// precaution for the 0211 migration
noteService.eraseDeletedNotesNow();
eraseService.eraseDeletedNotesNow();
beccaLoader.load();

View File

@@ -0,0 +1 @@
SELECT 1;

View File

@@ -0,0 +1,14 @@
UPDATE blobs SET blobId = REPLACE(blobId, '+', 'X');
UPDATE blobs SET blobId = REPLACE(blobId, '/', 'Y');
UPDATE notes SET blobId = REPLACE(blobId, '+', 'X');
UPDATE notes SET blobId = REPLACE(blobId, '/', 'Y');
UPDATE attachments SET blobId = REPLACE(blobId, '+', 'X');
UPDATE attachments SET blobId = REPLACE(blobId, '/', 'Y');
UPDATE revisions SET blobId = REPLACE(blobId, '+', 'X');
UPDATE revisions SET blobId = REPLACE(blobId, '/', 'Y');
UPDATE entity_changes SET entityId = REPLACE(entityId, '+', 'X') WHERE entityName = 'blobs';
UPDATE entity_changes SET entityId = REPLACE(entityId, '/', 'Y') WHERE entityName = 'blobs';

View File

@@ -0,0 +1,3 @@
CREATE INDEX IF NOT EXISTS IDX_notes_blobId on notes (blobId);
CREATE INDEX IF NOT EXISTS IDX_revisions_blobId on revisions (blobId);
CREATE INDEX IF NOT EXISTS IDX_attachments_blobId on attachments (blobId);

View File

@@ -128,3 +128,7 @@ CREATE TABLE IF NOT EXISTS "attachments"
deleteId TEXT DEFAULT NULL);
CREATE INDEX IDX_attachments_ownerId_role
on attachments (ownerId, role);
CREATE INDEX IDX_notes_blobId on notes (blobId);
CREATE INDEX IDX_revisions_blobId on revisions (blobId);
CREATE INDEX IDX_attachments_blobId on attachments (blobId);

View File

@@ -259,7 +259,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_abstract_becca_entity.js.html">becca/entities/abstract_becca_entity.js</a>, <a href="becca_entities_abstract_becca_entity.js.html#line243">line 243</a>
<a href="becca_entities_abstract_becca_entity.js.html">becca/entities/abstract_becca_entity.js</a>, <a href="becca_entities_abstract_becca_entity.js.html#line244">line 244</a>
</li></ul></dd>
@@ -1022,7 +1022,7 @@ This is a low-level method, for notes and branches use `note.deleteNote()` and '
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_abstract_becca_entity.js.html">becca/entities/abstract_becca_entity.js</a>, <a href="becca_entities_abstract_becca_entity.js.html#line260">line 260</a>
<a href="becca_entities_abstract_becca_entity.js.html">becca/entities/abstract_becca_entity.js</a>, <a href="becca_entities_abstract_becca_entity.js.html#line261">line 261</a>
</li></ul></dd>

View File

@@ -825,7 +825,7 @@ and relation (representing named relationship between source and target note)</d
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_abstract_becca_entity.js.html">becca/entities/abstract_becca_entity.js</a>, <a href="becca_entities_abstract_becca_entity.js.html#line243">line 243</a>
<a href="becca_entities_abstract_becca_entity.js.html">becca/entities/abstract_becca_entity.js</a>, <a href="becca_entities_abstract_becca_entity.js.html#line244">line 244</a>
</li></ul></dd>
@@ -1940,7 +1940,7 @@ This is a low-level method, for notes and branches use `note.deleteNote()` and '
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_abstract_becca_entity.js.html">becca/entities/abstract_becca_entity.js</a>, <a href="becca_entities_abstract_becca_entity.js.html#line260">line 260</a>
<a href="becca_entities_abstract_becca_entity.js.html">becca/entities/abstract_becca_entity.js</a>, <a href="becca_entities_abstract_becca_entity.js.html#line261">line 261</a>
</li></ul></dd>

View File

@@ -945,7 +945,7 @@ of deletion should not act as a clone.
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_abstract_becca_entity.js.html">becca/entities/abstract_becca_entity.js</a>, <a href="becca_entities_abstract_becca_entity.js.html#line243">line 243</a>
<a href="becca_entities_abstract_becca_entity.js.html">becca/entities/abstract_becca_entity.js</a>, <a href="becca_entities_abstract_becca_entity.js.html#line244">line 244</a>
</li></ul></dd>
@@ -2054,7 +2054,7 @@ This is a low-level method, for notes and branches use `note.deleteNote()` and '
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_abstract_becca_entity.js.html">becca/entities/abstract_becca_entity.js</a>, <a href="becca_entities_abstract_becca_entity.js.html#line260">line 260</a>
<a href="becca_entities_abstract_becca_entity.js.html">becca/entities/abstract_becca_entity.js</a>, <a href="becca_entities_abstract_becca_entity.js.html#line261">line 261</a>
</li></ul></dd>

View File

@@ -694,7 +694,7 @@ from tokenHash and token.</div>
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_abstract_becca_entity.js.html">becca/entities/abstract_becca_entity.js</a>, <a href="becca_entities_abstract_becca_entity.js.html#line243">line 243</a>
<a href="becca_entities_abstract_becca_entity.js.html">becca/entities/abstract_becca_entity.js</a>, <a href="becca_entities_abstract_becca_entity.js.html#line244">line 244</a>
</li></ul></dd>
@@ -1497,7 +1497,7 @@ This is a low-level method, for notes and branches use `note.deleteNote()` and '
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_abstract_becca_entity.js.html">becca/entities/abstract_becca_entity.js</a>, <a href="becca_entities_abstract_becca_entity.js.html#line260">line 260</a>
<a href="becca_entities_abstract_becca_entity.js.html">becca/entities/abstract_becca_entity.js</a>, <a href="becca_entities_abstract_becca_entity.js.html#line261">line 261</a>
</li></ul></dd>

View File

@@ -1171,7 +1171,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_abstract_becca_entity.js.html">becca/entities/abstract_becca_entity.js</a>, <a href="becca_entities_abstract_becca_entity.js.html#line243">line 243</a>
<a href="becca_entities_abstract_becca_entity.js.html">becca/entities/abstract_becca_entity.js</a>, <a href="becca_entities_abstract_becca_entity.js.html#line244">line 244</a>
</li></ul></dd>
@@ -1588,7 +1588,7 @@ See addLabel, addRelation for more specific methods.
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line1303">line 1303</a>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line1308">line 1308</a>
</li></ul></dd>
@@ -1924,7 +1924,7 @@ See addLabel, addRelation for more specific methods.
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line1324">line 1324</a>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line1329">line 1329</a>
</li></ul></dd>
@@ -2170,7 +2170,7 @@ returned.
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line1337">line 1337</a>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line1342">line 1342</a>
</li></ul></dd>
@@ -2405,7 +2405,7 @@ returned.
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line1422">line 1422</a>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line1427">line 1427</a>
</li></ul></dd>
@@ -2521,7 +2521,7 @@ In the future, this functionality might get more generic and some of the require
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line1474">line 1474</a>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line1479">line 1479</a>
</li></ul></dd>
@@ -2742,7 +2742,7 @@ In the future, this functionality might get more generic and some of the require
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line1512">line 1512</a>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line1517">line 1517</a>
</li></ul></dd>
@@ -3008,7 +3008,7 @@ In the future, this functionality might get more generic and some of the require
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line1144">line 1144</a>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line1149">line 1149</a>
</li></ul></dd>
@@ -3114,7 +3114,7 @@ In the future, this functionality might get more generic and some of the require
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line1032">line 1032</a>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line1037">line 1037</a>
</li></ul></dd>
@@ -3216,7 +3216,7 @@ In the future, this functionality might get more generic and some of the require
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line1006">line 1006</a>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line1011">line 1011</a>
</li></ul></dd>
@@ -3318,7 +3318,7 @@ In the future, this functionality might get more generic and some of the require
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line1113">line 1113</a>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line1118">line 1118</a>
</li></ul></dd>
@@ -3423,7 +3423,7 @@ In the future, this functionality might get more generic and some of the require
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line1128">line 1128</a>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line1133">line 1133</a>
</li></ul></dd>
@@ -3525,7 +3525,7 @@ In the future, this functionality might get more generic and some of the require
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line1095">line 1095</a>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line1100">line 1100</a>
</li></ul></dd>
@@ -3699,7 +3699,7 @@ In the future, this functionality might get more generic and some of the require
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line572">line 572</a>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line574">line 574</a>
</li></ul></dd>
@@ -3878,7 +3878,7 @@ In the future, this functionality might get more generic and some of the require
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line583">line 583</a>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line585">line 585</a>
</li></ul></dd>
@@ -3946,6 +3946,11 @@ In the future, this functionality might get more generic and some of the require
<div class="description">
Beware that the method must not create a copy of the array, but actually returns its internal array
(for performance reasons)
</div>
@@ -4081,7 +4086,7 @@ In the future, this functionality might get more generic and some of the require
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line324">line 324</a>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line327">line 327</a>
</li></ul></dd>
@@ -4260,7 +4265,7 @@ In the future, this functionality might get more generic and some of the require
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line1197">line 1197</a>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line1202">line 1202</a>
</li></ul></dd>
@@ -4439,7 +4444,7 @@ In the future, this functionality might get more generic and some of the require
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line1207">line 1207</a>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line1212">line 1212</a>
</li></ul></dd>
@@ -5062,7 +5067,7 @@ In the future, this functionality might get more generic and some of the require
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line953">line 953</a>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line958">line 958</a>
</li></ul></dd>
@@ -5152,7 +5157,7 @@ In the future, this functionality might get more generic and some of the require
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line754">line 754</a>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line759">line 759</a>
</li></ul></dd>
@@ -5258,7 +5263,7 @@ In the future, this functionality might get more generic and some of the require
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line1058">line 1058</a>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line1063">line 1063</a>
</li></ul></dd>
@@ -5516,7 +5521,7 @@ In the future, this functionality might get more generic and some of the require
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line512">line 512</a>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line514">line 514</a>
</li></ul></dd>
@@ -5674,7 +5679,7 @@ In the future, this functionality might get more generic and some of the require
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line536">line 536</a>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line538">line 538</a>
</li></ul></dd>
@@ -5844,7 +5849,7 @@ In the future, this functionality might get more generic and some of the require
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line612">line 612</a>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line614">line 614</a>
</li></ul></dd>
@@ -6011,7 +6016,7 @@ In the future, this functionality might get more generic and some of the require
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line604">line 604</a>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line606">line 606</a>
</li></ul></dd>
@@ -6117,7 +6122,7 @@ In the future, this functionality might get more generic and some of the require
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line679">line 679</a>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line684">line 684</a>
</li></ul></dd>
@@ -6297,7 +6302,7 @@ This method can be significantly faster than the getAttribute()
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line594">line 594</a>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line596">line 596</a>
</li></ul></dd>
@@ -6365,6 +6370,11 @@ This method can be significantly faster than the getAttribute()
<div class="description">
Beware that the method must not create a copy of the array, but actually returns its internal array
(for performance reasons)
</div>
@@ -6562,7 +6572,7 @@ This method can be significantly faster than the getAttribute()
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line654">line 654</a>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line659">line 659</a>
</li></ul></dd>
@@ -6717,7 +6727,7 @@ This method can be significantly faster than the getAttribute()
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line518">line 518</a>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line520">line 520</a>
</li></ul></dd>
@@ -6875,7 +6885,7 @@ This method can be significantly faster than the getAttribute()
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line542">line 542</a>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line544">line 544</a>
</li></ul></dd>
@@ -7045,7 +7055,7 @@ This method can be significantly faster than the getAttribute()
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line628">line 628</a>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line630">line 630</a>
</li></ul></dd>
@@ -7212,7 +7222,7 @@ This method can be significantly faster than the getAttribute()
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line620">line 620</a>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line622">line 622</a>
</li></ul></dd>
@@ -7367,7 +7377,7 @@ This method can be significantly faster than the getAttribute()
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line530">line 530</a>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line532">line 532</a>
</li></ul></dd>
@@ -7525,7 +7535,7 @@ This method can be significantly faster than the getAttribute()
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line554">line 554</a>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line556">line 556</a>
</li></ul></dd>
@@ -7695,7 +7705,7 @@ This method can be significantly faster than the getAttribute()
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line644">line 644</a>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line646">line 646</a>
</li></ul></dd>
@@ -8143,7 +8153,7 @@ This method can be significantly faster than the getAttribute()
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line524">line 524</a>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line526">line 526</a>
</li></ul></dd>
@@ -8301,7 +8311,7 @@ This method can be significantly faster than the getAttribute()
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line548">line 548</a>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line550">line 550</a>
</li></ul></dd>
@@ -8471,7 +8481,7 @@ This method can be significantly faster than the getAttribute()
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line636">line 636</a>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line638">line 638</a>
</li></ul></dd>
@@ -8577,7 +8587,7 @@ This method can be significantly faster than the getAttribute()
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line1089">line 1089</a>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line1094">line 1094</a>
</li></ul></dd>
@@ -8788,7 +8798,7 @@ This method can be significantly faster than the getAttribute()
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line864">line 864</a>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line869">line 869</a>
</li></ul></dd>
@@ -8959,7 +8969,7 @@ This method can be significantly faster than the getAttribute()
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line1166">line 1166</a>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line1171">line 1171</a>
</li></ul></dd>
@@ -9167,7 +9177,7 @@ This method can be significantly faster than the getAttribute()
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line887">line 887</a>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line892">line 892</a>
</li></ul></dd>
@@ -9269,7 +9279,7 @@ This method can be significantly faster than the getAttribute()
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line946">line 946</a>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line951">line 951</a>
</li></ul></dd>
@@ -9375,7 +9385,7 @@ This method can be significantly faster than the getAttribute()
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line832">line 832</a>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line837">line 837</a>
</li></ul></dd>
@@ -9477,7 +9487,7 @@ This method can be significantly faster than the getAttribute()
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line1052">line 1052</a>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line1057">line 1057</a>
</li></ul></dd>
@@ -9668,7 +9678,7 @@ This method can be significantly faster than the getAttribute()
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line1037">line 1037</a>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line1042">line 1042</a>
</li></ul></dd>
@@ -9894,7 +9904,7 @@ This method can be significantly faster than the getAttribute()
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line442">line 442</a>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line444">line 444</a>
</li></ul></dd>
@@ -10190,7 +10200,7 @@ This method can be significantly faster than the getAttribute()
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line471">line 471</a>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line473">line 473</a>
</li></ul></dd>
@@ -10419,7 +10429,7 @@ This method can be significantly faster than the getAttribute()
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line562">line 562</a>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line564">line 564</a>
</li></ul></dd>
@@ -10617,7 +10627,7 @@ This method can be significantly faster than the getAttribute()
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line492">line 492</a>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line494">line 494</a>
</li></ul></dd>
@@ -10815,7 +10825,7 @@ This method can be significantly faster than the getAttribute()
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line506">line 506</a>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line508">line 508</a>
</li></ul></dd>
@@ -11013,7 +11023,7 @@ This method can be significantly faster than the getAttribute()
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line499">line 499</a>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line501">line 501</a>
</li></ul></dd>
@@ -11269,7 +11279,7 @@ This method can be significantly faster than the getAttribute()
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line1238">line 1238</a>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line1243">line 1243</a>
</li></ul></dd>
@@ -11375,7 +11385,7 @@ This method can be significantly faster than the getAttribute()
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line1216">line 1216</a>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line1221">line 1221</a>
</li></ul></dd>
@@ -11942,7 +11952,7 @@ This method can be significantly faster than the getAttribute()
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line477">line 477</a>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line479">line 479</a>
</li></ul></dd>
@@ -12315,7 +12325,7 @@ This is a low-level method, for notes and branches use `note.deleteNote()` and '
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_abstract_becca_entity.js.html">becca/entities/abstract_becca_entity.js</a>, <a href="becca_entities_abstract_becca_entity.js.html#line260">line 260</a>
<a href="becca_entities_abstract_becca_entity.js.html">becca/entities/abstract_becca_entity.js</a>, <a href="becca_entities_abstract_becca_entity.js.html#line261">line 261</a>
</li></ul></dd>
@@ -12526,7 +12536,7 @@ This is a low-level method, for notes and branches use `note.deleteNote()` and '
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line1282">line 1282</a>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line1287">line 1287</a>
</li></ul></dd>
@@ -12706,7 +12716,7 @@ This is a low-level method, for notes and branches use `note.deleteNote()` and '
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line1398">line 1398</a>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line1403">line 1403</a>
</li></ul></dd>
@@ -12886,7 +12896,7 @@ This is a low-level method, for notes and branches use `note.deleteNote()` and '
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line1406">line 1406</a>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line1411">line 1411</a>
</li></ul></dd>
@@ -13081,7 +13091,7 @@ This is a low-level method, for notes and branches use `note.deleteNote()` and '
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line1619">line 1619</a>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line1624">line 1624</a>
</li></ul></dd>
@@ -13183,7 +13193,7 @@ This is a low-level method, for notes and branches use `note.deleteNote()` and '
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line1566">line 1566</a>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line1571">line 1571</a>
</li></ul></dd>
@@ -13415,7 +13425,7 @@ This is a low-level method, for notes and branches use `note.deleteNote()` and '
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line1251">line 1251</a>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line1256">line 1256</a>
</li></ul></dd>
@@ -13874,7 +13884,7 @@ This is a low-level method, for notes and branches use `note.deleteNote()` and '
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line1382">line 1382</a>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line1387">line 1387</a>
</li></ul></dd>
@@ -14034,7 +14044,7 @@ This is a low-level method, for notes and branches use `note.deleteNote()` and '
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line1390">line 1390</a>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line1395">line 1395</a>
</li></ul></dd>
@@ -14276,7 +14286,7 @@ This is a low-level method, for notes and branches use `note.deleteNote()` and '
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line1349">line 1349</a>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line1354">line 1354</a>
</li></ul></dd>
@@ -14487,7 +14497,7 @@ This is a low-level method, for notes and branches use `note.deleteNote()` and '
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line1365">line 1365</a>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line1370">line 1370</a>
</li></ul></dd>
@@ -14698,7 +14708,7 @@ This is a low-level method, for notes and branches use `note.deleteNote()` and '
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line1374">line 1374</a>
<a href="becca_entities_bnote.js.html">becca/entities/bnote.js</a>, <a href="becca_entities_bnote.js.html#line1379">line 1379</a>
</li></ul></dd>

View File

@@ -552,7 +552,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_abstract_becca_entity.js.html">becca/entities/abstract_becca_entity.js</a>, <a href="becca_entities_abstract_becca_entity.js.html#line243">line 243</a>
<a href="becca_entities_abstract_becca_entity.js.html">becca/entities/abstract_becca_entity.js</a>, <a href="becca_entities_abstract_becca_entity.js.html#line244">line 244</a>
</li></ul></dd>
@@ -1355,7 +1355,7 @@ This is a low-level method, for notes and branches use `note.deleteNote()` and '
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_abstract_becca_entity.js.html">becca/entities/abstract_becca_entity.js</a>, <a href="becca_entities_abstract_becca_entity.js.html#line260">line 260</a>
<a href="becca_entities_abstract_becca_entity.js.html">becca/entities/abstract_becca_entity.js</a>, <a href="becca_entities_abstract_becca_entity.js.html#line261">line 261</a>
</li></ul></dd>

View File

@@ -484,7 +484,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_abstract_becca_entity.js.html">becca/entities/abstract_becca_entity.js</a>, <a href="becca_entities_abstract_becca_entity.js.html#line243">line 243</a>
<a href="becca_entities_abstract_becca_entity.js.html">becca/entities/abstract_becca_entity.js</a>, <a href="becca_entities_abstract_becca_entity.js.html#line244">line 244</a>
</li></ul></dd>
@@ -1287,7 +1287,7 @@ This is a low-level method, for notes and branches use `note.deleteNote()` and '
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="becca_entities_abstract_becca_entity.js.html">becca/entities/abstract_becca_entity.js</a>, <a href="becca_entities_abstract_becca_entity.js.html#line260">line 260</a>
<a href="becca_entities_abstract_becca_entity.js.html">becca/entities/abstract_becca_entity.js</a>, <a href="becca_entities_abstract_becca_entity.js.html#line261">line 261</a>
</li></ul></dd>

View File

@@ -160,8 +160,7 @@ class AbstractBeccaEntity {
if (this.hasStringContent()) {
content = content.toString();
}
else {
} else {
content = Buffer.isBuffer(content) ? content : Buffer.from(content);
}
@@ -184,32 +183,32 @@ class AbstractBeccaEntity {
this.save();
if (newBlobId !== oldBlobId) {
this.#deleteBlobIfNoteUsed(oldBlobId);
this.#deleteBlobIfNotUsed(oldBlobId);
}
}
});
}
#deleteBlobIfNoteUsed(blobId) {
if (sql.getValue("SELECT 1 FROM notes WHERE blobId = ? LIMIT 1", [blobId])) {
#deleteBlobIfNotUsed(oldBlobId) {
if (sql.getValue("SELECT 1 FROM notes WHERE blobId = ? LIMIT 1", [oldBlobId])) {
return;
}
if (sql.getValue("SELECT 1 FROM attachments WHERE blobId = ? LIMIT 1", [blobId])) {
if (sql.getValue("SELECT 1 FROM attachments WHERE blobId = ? LIMIT 1", [oldBlobId])) {
return;
}
if (sql.getValue("SELECT 1 FROM revisions WHERE blobId = ? LIMIT 1", [blobId])) {
if (sql.getValue("SELECT 1 FROM revisions WHERE blobId = ? LIMIT 1", [oldBlobId])) {
return;
}
sql.execute("DELETE FROM blobs WHERE blobId = ?", [blobId]);
sql.execute("DELETE FROM entity_changes WHERE entityName = 'blobs' AND entityId = ?", [blobId]);
sql.execute("DELETE FROM blobs WHERE blobId = ?", [oldBlobId]);
sql.execute("DELETE FROM entity_changes WHERE entityName = 'blobs' AND entityId = ?", [oldBlobId]);
}
#getUnencryptedContentForHashCalculation(unencryptedContent) {
if (this.isProtected) {
// a "random" prefix make sure that the calculated hash/blobId is different for an encrypted note and decrypted
// a "random" prefix makes sure that the calculated hash/blobId is different for a decrypted/encrypted content
const encryptedPrefixSuffix = "t$[nvQg7q)&amp;_ENCRYPTED_?M:Bf&amp;j3jr_";
return Buffer.isBuffer(unencryptedContent)
? Buffer.concat([Buffer.from(encryptedPrefixSuffix), unencryptedContent])
@@ -224,7 +223,7 @@ class AbstractBeccaEntity {
* We're using the unencrypted blob for the hash calculation, because otherwise the random IV would
* cause every content blob to be unique which would balloon the database size (esp. with revisioning).
* This has minor security implications (it's easy to infer that given content is shared between different
* notes/attachments, but the trade-off comes out clearly positive).
* notes/attachments), but the trade-off comes out clearly positive.
*/
const newBlobId = utils.hashedBlobId(unencryptedContentForHashCalculation);
const blobNeedsInsert = !sql.getValue('SELECT 1 FROM blobs WHERE blobId = ?', [newBlobId]);
@@ -242,7 +241,9 @@ class AbstractBeccaEntity {
sql.upsert("blobs", "blobId", pojo);
const hash = utils.hash(`${newBlobId}|${pojo.content.toString()}`);
// we can't reuse blobId as an entity_changes hash, because this one has to be calculatable without having
// access to the decrypted content
const hash = blobService.calculateContentHash(pojo);
entityChangesService.addEntityChange({
entityName: 'blobs',

View File

@@ -219,7 +219,7 @@ class BBranch extends AbstractBeccaEntity {
this.becca.notes[note.noteId].isBeingDeleted = true;
for (const attribute of note.getOwnedAttributes()) {
for (const attribute of note.getOwnedAttributes().slice()) {
attribute.markAsDeleted(deleteId);
}

View File

@@ -345,6 +345,9 @@ class BNote extends AbstractBeccaEntity {
}
/**
* Beware that the method must not create a copy of the array, but actually returns its internal array
* (for performance reasons)
*
* @param {string} [type] - (optional) attribute type to filter
* @param {string} [name] - (optional) attribute name to filter
* @returns {BAttribute[]} all note's attributes, including inherited ones
@@ -363,7 +366,6 @@ class BNote extends AbstractBeccaEntity {
return this.__attributeCache.filter(attr => attr.name === name);
}
else {
// a bit unsafe to return the original array, but defensive copy would be costly
return this.__attributeCache;
}
}
@@ -674,6 +676,9 @@ class BNote extends AbstractBeccaEntity {
}
/**
* Beware that the method must not create a copy of the array, but actually returns its internal array
* (for performance reasons)
*
* @param {string|null} [type] - (optional) attribute type to filter
* @param {string|null} [name] - (optional) attribute name to filter
* @param {string|null} [value] - (optional) attribute value to filter

View File

@@ -194,7 +194,75 @@ and relation (representing named relationship between source and target note)</d
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fattribute.js.html">entities/fattribute.js</a>, <a href="entities_fattribute.js.html#line16">line 16</a>
<a href="entities_fattribute.js.html">entities/fattribute.js</a>, <a href="entities_fattribute.js.html#line17">line 17</a>
</li></ul></dd>
</dl>
<h4 class="name" id="froca"><span class="type-signature"></span>froca<span class="type-signature"> :Froca</span></h4>
<h5>Type:</h5>
<ul>
<li>
<span class="param-type">Froca</span>
</li>
</ul>
<dl class="details">
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fattribute.js.html">entities/fattribute.js</a>, <a href="entities_fattribute.js.html#line10">line 10</a>
</li></ul></dd>
@@ -262,7 +330,7 @@ and relation (representing named relationship between source and target note)</d
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fattribute.js.html">entities/fattribute.js</a>, <a href="entities_fattribute.js.html#line28">line 28</a>
<a href="entities_fattribute.js.html">entities/fattribute.js</a>, <a href="entities_fattribute.js.html#line29">line 29</a>
</li></ul></dd>
@@ -330,7 +398,7 @@ and relation (representing named relationship between source and target note)</d
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fattribute.js.html">entities/fattribute.js</a>, <a href="entities_fattribute.js.html#line22">line 22</a>
<a href="entities_fattribute.js.html">entities/fattribute.js</a>, <a href="entities_fattribute.js.html#line23">line 23</a>
</li></ul></dd>
@@ -398,7 +466,7 @@ and relation (representing named relationship between source and target note)</d
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fattribute.js.html">entities/fattribute.js</a>, <a href="entities_fattribute.js.html#line18">line 18</a>
<a href="entities_fattribute.js.html">entities/fattribute.js</a>, <a href="entities_fattribute.js.html#line19">line 19</a>
</li></ul></dd>
@@ -466,7 +534,7 @@ and relation (representing named relationship between source and target note)</d
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fattribute.js.html">entities/fattribute.js</a>, <a href="entities_fattribute.js.html#line26">line 26</a>
<a href="entities_fattribute.js.html">entities/fattribute.js</a>, <a href="entities_fattribute.js.html#line27">line 27</a>
</li></ul></dd>
@@ -534,7 +602,7 @@ and relation (representing named relationship between source and target note)</d
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fattribute.js.html">entities/fattribute.js</a>, <a href="entities_fattribute.js.html#line20">line 20</a>
<a href="entities_fattribute.js.html">entities/fattribute.js</a>, <a href="entities_fattribute.js.html#line21">line 21</a>
</li></ul></dd>
@@ -602,7 +670,7 @@ and relation (representing named relationship between source and target note)</d
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fattribute.js.html">entities/fattribute.js</a>, <a href="entities_fattribute.js.html#line24">line 24</a>
<a href="entities_fattribute.js.html">entities/fattribute.js</a>, <a href="entities_fattribute.js.html#line25">line 25</a>
</li></ul></dd>
@@ -678,7 +746,7 @@ and relation (representing named relationship between source and target note)</d
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fattribute.js.html">entities/fattribute.js</a>, <a href="entities_fattribute.js.html#line32">line 32</a>
<a href="entities_fattribute.js.html">entities/fattribute.js</a>, <a href="entities_fattribute.js.html#line33">line 33</a>
</li></ul></dd>
@@ -780,7 +848,7 @@ and relation (representing named relationship between source and target note)</d
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fattribute.js.html">entities/fattribute.js</a>, <a href="entities_fattribute.js.html#line37">line 37</a>
<a href="entities_fattribute.js.html">entities/fattribute.js</a>, <a href="entities_fattribute.js.html#line38">line 38</a>
</li></ul></dd>

View File

@@ -198,7 +198,75 @@ parents.</div>
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fbranch.js.html">entities/fbranch.js</a>, <a href="entities_fbranch.js.html#line17">line 17</a>
<a href="entities_fbranch.js.html">entities/fbranch.js</a>, <a href="entities_fbranch.js.html#line18">line 18</a>
</li></ul></dd>
</dl>
<h4 class="name" id="froca"><span class="type-signature"></span>froca<span class="type-signature"> :Froca</span></h4>
<h5>Type:</h5>
<ul>
<li>
<span class="param-type">Froca</span>
</li>
</ul>
<dl class="details">
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fbranch.js.html">entities/fbranch.js</a>, <a href="entities_fbranch.js.html#line8">line 8</a>
</li></ul></dd>
@@ -266,7 +334,7 @@ parents.</div>
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fbranch.js.html">entities/fbranch.js</a>, <a href="entities_fbranch.js.html#line29">line 29</a>
<a href="entities_fbranch.js.html">entities/fbranch.js</a>, <a href="entities_fbranch.js.html#line30">line 30</a>
</li></ul></dd>
@@ -334,7 +402,7 @@ parents.</div>
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fbranch.js.html">entities/fbranch.js</a>, <a href="entities_fbranch.js.html#line27">line 27</a>
<a href="entities_fbranch.js.html">entities/fbranch.js</a>, <a href="entities_fbranch.js.html#line28">line 28</a>
</li></ul></dd>
@@ -402,7 +470,7 @@ parents.</div>
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fbranch.js.html">entities/fbranch.js</a>, <a href="entities_fbranch.js.html#line19">line 19</a>
<a href="entities_fbranch.js.html">entities/fbranch.js</a>, <a href="entities_fbranch.js.html#line20">line 20</a>
</li></ul></dd>
@@ -470,7 +538,7 @@ parents.</div>
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fbranch.js.html">entities/fbranch.js</a>, <a href="entities_fbranch.js.html#line23">line 23</a>
<a href="entities_fbranch.js.html">entities/fbranch.js</a>, <a href="entities_fbranch.js.html#line24">line 24</a>
</li></ul></dd>
@@ -538,7 +606,7 @@ parents.</div>
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fbranch.js.html">entities/fbranch.js</a>, <a href="entities_fbranch.js.html#line21">line 21</a>
<a href="entities_fbranch.js.html">entities/fbranch.js</a>, <a href="entities_fbranch.js.html#line22">line 22</a>
</li></ul></dd>
@@ -606,7 +674,7 @@ parents.</div>
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fbranch.js.html">entities/fbranch.js</a>, <a href="entities_fbranch.js.html#line25">line 25</a>
<a href="entities_fbranch.js.html">entities/fbranch.js</a>, <a href="entities_fbranch.js.html#line26">line 26</a>
</li></ul></dd>
@@ -682,7 +750,7 @@ parents.</div>
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fbranch.js.html">entities/fbranch.js</a>, <a href="entities_fbranch.js.html#line33">line 33</a>
<a href="entities_fbranch.js.html">entities/fbranch.js</a>, <a href="entities_fbranch.js.html#line34">line 34</a>
</li></ul></dd>
@@ -784,7 +852,7 @@ parents.</div>
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fbranch.js.html">entities/fbranch.js</a>, <a href="entities_fbranch.js.html#line38">line 38</a>
<a href="entities_fbranch.js.html">entities/fbranch.js</a>, <a href="entities_fbranch.js.html#line39">line 39</a>
</li></ul></dd>
@@ -886,7 +954,7 @@ parents.</div>
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fbranch.js.html">entities/fbranch.js</a>, <a href="entities_fbranch.js.html#line43">line 43</a>
<a href="entities_fbranch.js.html">entities/fbranch.js</a>, <a href="entities_fbranch.js.html#line44">line 44</a>
</li></ul></dd>
@@ -988,7 +1056,7 @@ parents.</div>
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fbranch.js.html">entities/fbranch.js</a>, <a href="entities_fbranch.js.html#line48">line 48</a>
<a href="entities_fbranch.js.html">entities/fbranch.js</a>, <a href="entities_fbranch.js.html#line49">line 49</a>
</li></ul></dd>

View File

@@ -264,7 +264,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line54">line 54</a>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line55">line 55</a>
</li></ul></dd>
@@ -332,7 +332,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line37">line 37</a>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line38">line 38</a>
</li></ul></dd>
@@ -400,7 +400,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line51">line 51</a>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line52">line 52</a>
</li></ul></dd>
@@ -468,7 +468,75 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line45">line 45</a>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line46">line 46</a>
</li></ul></dd>
</dl>
<h4 class="name" id="froca"><span class="type-signature"></span>froca<span class="type-signature"> :Froca</span></h4>
<h5>Type:</h5>
<ul>
<li>
<span class="param-type">Froca</span>
</li>
</ul>
<dl class="details">
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line35">line 35</a>
</li></ul></dd>
@@ -536,7 +604,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line65">line 65</a>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line66">line 66</a>
</li></ul></dd>
@@ -608,7 +676,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line75">line 75</a>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line76">line 76</a>
</li></ul></dd>
@@ -676,7 +744,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line61">line 61</a>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line62">line 62</a>
</li></ul></dd>
@@ -744,7 +812,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line48">line 48</a>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line49">line 49</a>
</li></ul></dd>
@@ -812,7 +880,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line43">line 43</a>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line44">line 44</a>
</li></ul></dd>
@@ -880,7 +948,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line40">line 40</a>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line41">line 41</a>
</li></ul></dd>
@@ -948,7 +1016,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line63">line 63</a>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line64">line 64</a>
</li></ul></dd>
@@ -1020,7 +1088,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line70">line 70</a>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line71">line 71</a>
</li></ul></dd>
@@ -1100,7 +1168,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line353">line 353</a>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line354">line 354</a>
</li></ul></dd>
@@ -1206,7 +1274,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line240">line 240</a>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line241">line 241</a>
</li></ul></dd>
@@ -1308,7 +1376,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line231">line 231</a>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line232">line 232</a>
</li></ul></dd>
@@ -1482,7 +1550,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line616">line 616</a>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line617">line 617</a>
</li></ul></dd>
@@ -1660,7 +1728,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line638">line 638</a>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line639">line 639</a>
</li></ul></dd>
@@ -1860,7 +1928,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line287">line 287</a>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line288">line 288</a>
</li></ul></dd>
@@ -2039,7 +2107,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line409">line 409</a>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line410">line 410</a>
</li></ul></dd>
@@ -2218,7 +2286,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line419">line 419</a>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line420">line 420</a>
</li></ul></dd>
@@ -2291,70 +2359,6 @@
<h5>Parameters:</h5>
<table class="params">
<thead>
<tr>
<th>Name</th>
<th>Type</th>
<th>Attributes</th>
<th>Default</th>
<th class="last">Description</th>
</tr>
</thead>
<tbody>
<tr>
<td class="name"><code>opts.preview</code></td>
<td class="type">
</td>
<td class="attributes">
&lt;optional><br>
</td>
<td class="default">
false
</td>
<td class="description last">retrieve only first 10 000 characters for a preview</td>
</tr>
</tbody>
</table>
@@ -2388,7 +2392,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line866">line 866</a>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line864">line 864</a>
</li></ul></dd>
@@ -2492,7 +2496,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line152">line 152</a>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line153">line 153</a>
</li></ul></dd>
@@ -2596,7 +2600,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line169">line 169</a>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line170">line 170</a>
</li></ul></dd>
@@ -2698,7 +2702,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line179">line 179</a>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line180">line 180</a>
</li></ul></dd>
@@ -2800,7 +2804,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line221">line 221</a>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line222">line 222</a>
</li></ul></dd>
@@ -2902,7 +2906,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line226">line 226</a>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line227">line 227</a>
</li></ul></dd>
@@ -3053,7 +3057,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line692">line 692</a>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line693">line 693</a>
</li></ul></dd>
@@ -3208,7 +3212,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line716">line 716</a>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line717">line 717</a>
</li></ul></dd>
@@ -3375,7 +3379,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line502">line 502</a>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line503">line 503</a>
</li></ul></dd>
@@ -3483,7 +3487,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line858">line 858</a>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line859">line 859</a>
</li></ul></dd>
@@ -3585,7 +3589,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line758">line 758</a>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line759">line 759</a>
</li></ul></dd>
@@ -3759,7 +3763,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line605">line 605</a>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line606">line 606</a>
</li></ul></dd>
@@ -3937,7 +3941,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line627">line 627</a>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line628">line 628</a>
</li></ul></dd>
@@ -4137,7 +4141,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line274">line 274</a>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line275">line 275</a>
</li></ul></dd>
@@ -4292,7 +4296,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line686">line 686</a>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line687">line 687</a>
</li></ul></dd>
@@ -4447,7 +4451,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line710">line 710</a>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line711">line 711</a>
</li></ul></dd>
@@ -4614,7 +4618,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line494">line 494</a>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line495">line 495</a>
</li></ul></dd>
@@ -4769,7 +4773,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line698">line 698</a>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line699">line 699</a>
</li></ul></dd>
@@ -4924,7 +4928,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line722">line 722</a>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line723">line 723</a>
</li></ul></dd>
@@ -5091,7 +5095,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line568">line 568</a>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line569">line 569</a>
</li></ul></dd>
@@ -5197,7 +5201,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line144">line 144</a>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line145">line 145</a>
</li></ul></dd>
@@ -5299,7 +5303,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line159">line 159</a>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line160">line 160</a>
</li></ul></dd>
@@ -5401,7 +5405,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line187">line 187</a>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line188">line 188</a>
</li></ul></dd>
@@ -5503,7 +5507,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line192">line 192</a>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line193">line 193</a>
</li></ul></dd>
@@ -5654,7 +5658,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line704">line 704</a>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line705">line 705</a>
</li></ul></dd>
@@ -5809,7 +5813,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line734">line 734</a>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line735">line 735</a>
</li></ul></dd>
@@ -5979,7 +5983,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line744">line 744</a>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line745">line 745</a>
</li></ul></dd>
@@ -6130,7 +6134,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line728">line 728</a>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line729">line 729</a>
</li></ul></dd>
@@ -6297,7 +6301,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line576">line 576</a>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line577">line 577</a>
</li></ul></dd>
@@ -6403,7 +6407,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line910">line 910</a>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line908">line 908</a>
</li></ul></dd>
@@ -6581,7 +6585,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line375">line 375</a>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line376">line 376</a>
</li></ul></dd>
@@ -6687,7 +6691,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line848">line 848</a>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line849">line 849</a>
</li></ul></dd>
@@ -6793,7 +6797,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line838">line 838</a>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line839">line 839</a>
</li></ul></dd>
@@ -6967,7 +6971,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line585">line 585</a>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line586">line 586</a>
</li></ul></dd>
@@ -7073,7 +7077,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line174">line 174</a>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line175">line 175</a>
</li></ul></dd>
@@ -7224,7 +7228,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line654">line 654</a>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line655">line 655</a>
</li></ul></dd>
@@ -7402,7 +7406,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line596">line 596</a>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line597">line 597</a>
</li></ul></dd>
@@ -7557,7 +7561,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line648">line 648</a>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line649">line 649</a>
</li></ul></dd>
@@ -7712,7 +7716,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line674">line 674</a>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line675">line 675</a>
</li></ul></dd>
@@ -7867,7 +7871,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line680">line 680</a>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line681">line 681</a>
</li></ul></dd>
@@ -7975,7 +7979,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line831">line 831</a>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line832">line 832</a>
</li></ul></dd>
@@ -8059,7 +8063,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line428">line 428</a>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line429">line 429</a>
</li></ul></dd>
@@ -8153,7 +8157,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line905">line 905</a>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line903">line 903</a>
</li></ul></dd>
@@ -8259,7 +8263,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line897">line 897</a>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line895">line 895</a>
</li></ul></dd>
@@ -8365,7 +8369,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line117">line 117</a>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line118">line 118</a>
</li></ul></dd>
@@ -8516,7 +8520,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line660">line 660</a>
<a href="entities_fnote.js.html">entities/fnote.js</a>, <a href="entities_fnote.js.html#line661">line 661</a>
</li></ul></dd>

View File

@@ -572,6 +572,66 @@ available in the JS frontend notes. You can use e.g. <code>api.showMessage(api.s
<h4 class="name" id="createNoteLink"><span class="type-signature"></span>createNoteLink<span class="type-signature"></span></h4>
<dl class="details">
<dt class="important tag-deprecated">Deprecated:</dt><dd><ul class="dummy"><li>- use api.createLink() instead</li></ul></dd>
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line308">line 308</a>
</li></ul></dd>
</dl>
<h4 class="name" id="currentNote"><span class="type-signature"></span>currentNote<span class="type-signature"></span></h4>
@@ -1759,7 +1819,7 @@ available in the JS frontend notes. You can use e.g. <code>api.showMessage(api.s
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line313">line 313</a>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line316">line 316</a>
</li></ul></dd>
@@ -1967,7 +2027,7 @@ available in the JS frontend notes. You can use e.g. <code>api.showMessage(api.s
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line455">line 455</a>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line458">line 458</a>
</li></ul></dd>
@@ -2639,7 +2699,7 @@ available in the JS frontend notes. You can use e.g. <code>api.showMessage(api.s
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line499">line 499</a>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line502">line 502</a>
</li></ul></dd>
@@ -2794,7 +2854,7 @@ available in the JS frontend notes. You can use e.g. <code>api.showMessage(api.s
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line491">line 491</a>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line494">line 494</a>
</li></ul></dd>
@@ -2904,7 +2964,7 @@ available in the JS frontend notes. You can use e.g. <code>api.showMessage(api.s
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line335">line 335</a>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line338">line 338</a>
</li></ul></dd>
@@ -3010,7 +3070,7 @@ available in the JS frontend notes. You can use e.g. <code>api.showMessage(api.s
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line319">line 319</a>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line322">line 322</a>
</li></ul></dd>
@@ -3116,7 +3176,7 @@ available in the JS frontend notes. You can use e.g. <code>api.showMessage(api.s
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line350">line 350</a>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line353">line 353</a>
</li></ul></dd>
@@ -3226,7 +3286,7 @@ available in the JS frontend notes. You can use e.g. <code>api.showMessage(api.s
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line327">line 327</a>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line330">line 330</a>
</li></ul></dd>
@@ -3337,7 +3397,7 @@ implementation of actual widget type.
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line344">line 344</a>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line347">line 347</a>
</li></ul></dd>
@@ -3492,7 +3552,7 @@ implementation of actual widget type.
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line359">line 359</a>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line362">line 362</a>
</li></ul></dd>
@@ -3647,7 +3707,7 @@ implementation of actual widget type.
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line403">line 403</a>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line406">line 406</a>
</li></ul></dd>
@@ -3909,7 +3969,7 @@ if some action needs to happen on only one specific instance.
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line421">line 421</a>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line424">line 424</a>
</li></ul></dd>
@@ -4372,7 +4432,7 @@ otherwise (by e.g. createLink())
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line394">line 394</a>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line397">line 397</a>
</li></ul></dd>
@@ -4527,7 +4587,7 @@ otherwise (by e.g. createLink())
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line412">line 412</a>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line415">line 415</a>
</li></ul></dd>
@@ -4682,7 +4742,7 @@ otherwise (by e.g. createLink())
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line430">line 430</a>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line433">line 433</a>
</li></ul></dd>
@@ -4832,7 +4892,7 @@ otherwise (by e.g. createLink())
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line510">line 510</a>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line513">line 513</a>
</li></ul></dd>
@@ -5517,7 +5577,7 @@ otherwise (by e.g. createLink())
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line374">line 374</a>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line377">line 377</a>
</li></ul></dd>
@@ -5691,7 +5751,7 @@ otherwise (by e.g. createLink())
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line384">line 384</a>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line387">line 387</a>
</li></ul></dd>
@@ -5846,7 +5906,7 @@ otherwise (by e.g. createLink())
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line484">line 484</a>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line487">line 487</a>
</li></ul></dd>
@@ -6000,7 +6060,7 @@ otherwise (by e.g. createLink())
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line475">line 475</a>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line478">line 478</a>
</li></ul></dd>
@@ -6787,7 +6847,7 @@ Internally this serializes the anonymous function into string and sends it to ba
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line439">line 439</a>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line442">line 442</a>
</li></ul></dd>
@@ -6938,7 +6998,7 @@ Internally this serializes the anonymous function into string and sends it to ba
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line366">line 366</a>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line369">line 369</a>
</li></ul></dd>
@@ -7642,7 +7702,7 @@ Typical use case is when a new note has been created, we should wait until it is
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line467">line 467</a>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line470">line 470</a>
</li></ul></dd>

View File

@@ -34,6 +34,7 @@
*/
class FAttribute {
constructor(froca, row) {
/** @type {Froca} */
this.froca = froca;
this.update(row);

View File

@@ -32,6 +32,7 @@
*/
class FBranch {
constructor(froca, row) {
/** @type {Froca} */
this.froca = froca;
this.update(row);

View File

@@ -59,6 +59,7 @@ class FNote {
* @param {Object.&lt;string, Object>} row
*/
constructor(froca, row) {
/** @type {Froca} */
this.froca = froca;
/** @type {string[]} */
@@ -887,12 +888,9 @@ class FNote {
return this.getBlob();
}
/**
* @param [opts.preview=false] - retrieve only first 10 000 characters for a preview
* @return {Promise&lt;FBlob>}
*/
async getBlob(opts = {}) {
return await this.froca.getBlob('notes', this.noteId, opts);
/** @return {Promise&lt;FBlob>} */
async getBlob() {
return await this.froca.getBlob('notes', this.noteId);
}
toString() {

View File

@@ -332,6 +332,9 @@ function FrontendScriptApi(startNote, currentNote, originEntity = null, $contain
*/
this.createLink = linkService.createLink;
/** @deprecated - use api.createLink() instead */
this.createNoteLink = linkService.createLink;
/**
* Adds given text to the editor cursor
*

View File

@@ -5,8 +5,8 @@
}
/*
* CKEditor 5 (v38.0.1) content styles.
* Generated on Thu, 25 May 2023 13:25:51 GMT.
* CKEditor 5 (v38.1.1) content styles.
* Generated on Thu, 27 Jul 2023 08:16:09 GMT.
* For more information, check out https://ckeditor.com/docs/ckeditor5/latest/installation/advanced/content-styles.html
*/
@@ -28,227 +28,107 @@
--ck-todo-list-checkmark-size: 16px;
}
/* @ckeditor/ckeditor5-block-quote/theme/blockquote.css */
.ck-content blockquote {
/* @ckeditor/ckeditor5-table/theme/tablecolumnresize.css */
.ck-content .table .ck-table-resized {
table-layout: fixed;
}
/* @ckeditor/ckeditor5-table/theme/tablecolumnresize.css */
.ck-content .table table {
overflow: hidden;
padding-right: 1.5em;
padding-left: 1.5em;
margin-left: 0;
margin-right: 0;
font-style: italic;
border-left: solid 5px hsl(0, 0%, 80%);
}
/* @ckeditor/ckeditor5-block-quote/theme/blockquote.css */
.ck-content[dir="rtl"] blockquote {
border-left: 0;
border-right: solid 5px hsl(0, 0%, 80%);
/* @ckeditor/ckeditor5-table/theme/tablecolumnresize.css */
.ck-content .table td,
.ck-content .table th {
overflow-wrap: break-word;
position: relative;
}
/* @ckeditor/ckeditor5-basic-styles/theme/code.css */
.ck-content code {
background-color: hsla(0, 0%, 78%, 0.3);
padding: .15em;
border-radius: 2px;
}
/* @ckeditor/ckeditor5-font/theme/fontsize.css */
.ck-content .text-tiny {
font-size: .7em;
}
/* @ckeditor/ckeditor5-font/theme/fontsize.css */
.ck-content .text-small {
font-size: .85em;
}
/* @ckeditor/ckeditor5-font/theme/fontsize.css */
.ck-content .text-big {
font-size: 1.4em;
}
/* @ckeditor/ckeditor5-font/theme/fontsize.css */
.ck-content .text-huge {
font-size: 1.8em;
}
/* @ckeditor/ckeditor5-highlight/theme/highlight.css */
.ck-content .marker-yellow {
background-color: var(--ck-highlight-marker-yellow);
}
/* @ckeditor/ckeditor5-highlight/theme/highlight.css */
.ck-content .marker-green {
background-color: var(--ck-highlight-marker-green);
}
/* @ckeditor/ckeditor5-highlight/theme/highlight.css */
.ck-content .marker-pink {
background-color: var(--ck-highlight-marker-pink);
}
/* @ckeditor/ckeditor5-highlight/theme/highlight.css */
.ck-content .marker-blue {
background-color: var(--ck-highlight-marker-blue);
}
/* @ckeditor/ckeditor5-highlight/theme/highlight.css */
.ck-content .pen-red {
color: var(--ck-highlight-pen-red);
background-color: transparent;
}
/* @ckeditor/ckeditor5-highlight/theme/highlight.css */
.ck-content .pen-green {
color: var(--ck-highlight-pen-green);
background-color: transparent;
}
/* @ckeditor/ckeditor5-image/theme/imagecaption.css */
.ck-content .image > figcaption {
/* @ckeditor/ckeditor5-table/theme/tablecaption.css */
.ck-content .table > figcaption {
display: table-caption;
caption-side: bottom;
caption-side: top;
word-break: break-word;
color: var(--ck-color-image-caption-text);
background-color: var(--ck-color-image-caption-background);
text-align: center;
color: var(--ck-color-table-caption-text);
background-color: var(--ck-color-table-caption-background);
padding: .6em;
font-size: .75em;
outline-offset: -1px;
}
/* @ckeditor/ckeditor5-image/theme/image.css */
.ck-content .image {
display: table;
clear: both;
text-align: center;
/* @ckeditor/ckeditor5-table/theme/table.css */
.ck-content .table {
margin: 0.9em auto;
min-width: 50px;
display: table;
}
/* @ckeditor/ckeditor5-image/theme/image.css */
.ck-content .image img {
display: block;
margin: 0 auto;
max-width: 100%;
min-width: 100%;
/* @ckeditor/ckeditor5-table/theme/table.css */
.ck-content .table table {
border-collapse: collapse;
border-spacing: 0;
width: 100%;
height: 100%;
border: 1px double hsl(0, 0%, 70%);
}
/* @ckeditor/ckeditor5-image/theme/image.css */
.ck-content .image-inline {
/*
* Normally, the .image-inline would have "display: inline-block" and "img { width: 100% }" (to follow the wrapper while resizing).;
* Unfortunately, together with "srcset", it gets automatically stretched up to the width of the editing root.
* This strange behavior does not happen with inline-flex.
*/
display: inline-flex;
max-width: 100%;
align-items: flex-start;
/* @ckeditor/ckeditor5-table/theme/table.css */
.ck-content .table table td,
.ck-content .table table th {
min-width: 2em;
padding: .4em;
border: 1px solid hsl(0, 0%, 75%);
}
/* @ckeditor/ckeditor5-image/theme/image.css */
.ck-content .image-inline picture {
/* @ckeditor/ckeditor5-table/theme/table.css */
.ck-content .table table th {
font-weight: bold;
background: hsla(0, 0%, 0%, 5%);
}
/* @ckeditor/ckeditor5-table/theme/table.css */
.ck-content[dir="rtl"] .table th {
text-align: right;
}
/* @ckeditor/ckeditor5-table/theme/table.css */
.ck-content[dir="ltr"] .table th {
text-align: left;
}
/* @ckeditor/ckeditor5-page-break/theme/pagebreak.css */
.ck-content .page-break {
position: relative;
clear: both;
padding: 5px 0;
display: flex;
align-items: center;
justify-content: center;
}
/* @ckeditor/ckeditor5-image/theme/image.css */
.ck-content .image-inline picture,
.ck-content .image-inline img {
flex-grow: 1;
flex-shrink: 1;
max-width: 100%;
}
/* @ckeditor/ckeditor5-image/theme/imageresize.css */
.ck-content .image.image_resized {
max-width: 100%;
display: block;
box-sizing: border-box;
}
/* @ckeditor/ckeditor5-image/theme/imageresize.css */
.ck-content .image.image_resized img {
/* @ckeditor/ckeditor5-page-break/theme/pagebreak.css */
.ck-content .page-break::after {
content: '';
position: absolute;
border-bottom: 2px dashed hsl(0, 0%, 77%);
width: 100%;
}
/* @ckeditor/ckeditor5-image/theme/imageresize.css */
.ck-content .image.image_resized > figcaption {
/* @ckeditor/ckeditor5-page-break/theme/pagebreak.css */
.ck-content .page-break__label {
position: relative;
z-index: 1;
padding: .3em .6em;
display: block;
text-transform: uppercase;
border: 1px solid hsl(0, 0%, 77%);
border-radius: 2px;
font-family: Helvetica, Arial, Tahoma, Verdana, Sans-Serif;
font-size: 0.75em;
font-weight: bold;
color: hsl(0, 0%, 20%);
background: hsl(0, 0%, 100%);
box-shadow: 2px 2px 1px hsla(0, 0%, 0%, 0.15);
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
/* @ckeditor/ckeditor5-image/theme/imagestyle.css */
.ck-content .image-style-block-align-left,
.ck-content .image-style-block-align-right {
max-width: calc(100% - var(--ck-image-style-spacing));
}
/* @ckeditor/ckeditor5-image/theme/imagestyle.css */
.ck-content .image-style-align-left,
.ck-content .image-style-align-right {
clear: none;
}
/* @ckeditor/ckeditor5-image/theme/imagestyle.css */
.ck-content .image-style-side {
float: right;
margin-left: var(--ck-image-style-spacing);
max-width: 50%;
}
/* @ckeditor/ckeditor5-image/theme/imagestyle.css */
.ck-content .image-style-align-left {
float: left;
margin-right: var(--ck-image-style-spacing);
}
/* @ckeditor/ckeditor5-image/theme/imagestyle.css */
.ck-content .image-style-align-center {
margin-left: auto;
margin-right: auto;
}
/* @ckeditor/ckeditor5-image/theme/imagestyle.css */
.ck-content .image-style-align-right {
float: right;
margin-left: var(--ck-image-style-spacing);
}
/* @ckeditor/ckeditor5-image/theme/imagestyle.css */
.ck-content .image-style-block-align-right {
margin-right: 0;
margin-left: auto;
}
/* @ckeditor/ckeditor5-image/theme/imagestyle.css */
.ck-content .image-style-block-align-left {
margin-left: 0;
margin-right: auto;
}
/* @ckeditor/ckeditor5-image/theme/imagestyle.css */
.ck-content p + .image-style-align-left,
.ck-content p + .image-style-align-right,
.ck-content p + .image-style-side {
margin-top: 0;
}
/* @ckeditor/ckeditor5-image/theme/imagestyle.css */
.ck-content .image-inline.image-style-align-left,
.ck-content .image-inline.image-style-align-right {
margin-top: var(--ck-inline-image-style-spacing);
margin-bottom: var(--ck-inline-image-style-spacing);
}
/* @ckeditor/ckeditor5-image/theme/imagestyle.css */
.ck-content .image-inline.image-style-align-left {
margin-right: var(--ck-inline-image-style-spacing);
}
/* @ckeditor/ckeditor5-image/theme/imagestyle.css */
.ck-content .image-inline.image-style-align-right {
margin-left: var(--ck-inline-image-style-spacing);
}
/* @ckeditor/ckeditor5-list/theme/list.css */
.ck-content ol {
list-style-type: decimal;
}
/* @ckeditor/ckeditor5-list/theme/list.css */
.ck-content ol ol {
list-style-type: lower-latin;
}
/* @ckeditor/ckeditor5-list/theme/list.css */
.ck-content ol ol ol {
list-style-type: lower-roman;
}
/* @ckeditor/ckeditor5-list/theme/list.css */
.ck-content ol ol ol ol {
list-style-type: upper-latin;
}
/* @ckeditor/ckeditor5-list/theme/list.css */
.ck-content ol ol ol ol ol {
list-style-type: upper-roman;
}
/* @ckeditor/ckeditor5-list/theme/list.css */
.ck-content ul {
list-style-type: disc;
}
/* @ckeditor/ckeditor5-list/theme/list.css */
.ck-content ul ul {
list-style-type: circle;
}
/* @ckeditor/ckeditor5-list/theme/list.css */
.ck-content ul ul ul {
list-style-type: square;
}
/* @ckeditor/ckeditor5-list/theme/list.css */
.ck-content ul ul ul ul {
list-style-type: square;
/* @ckeditor/ckeditor5-media-embed/theme/mediaembed.css */
.ck-content .media {
clear: both;
margin: 0.9em 0;
display: block;
min-width: 15em;
}
/* @ckeditor/ckeditor5-list/theme/todolist.css */
.ck-content .todo-list {
@@ -317,107 +197,240 @@
.ck-content .todo-list .todo-list__label .todo-list__label__description {
vertical-align: middle;
}
/* @ckeditor/ckeditor5-media-embed/theme/mediaembed.css */
.ck-content .media {
clear: both;
margin: 0.9em 0;
display: block;
min-width: 15em;
}
/* @ckeditor/ckeditor5-page-break/theme/pagebreak.css */
.ck-content .page-break {
position: relative;
clear: both;
padding: 5px 0;
display: flex;
align-items: center;
justify-content: center;
}
/* @ckeditor/ckeditor5-page-break/theme/pagebreak.css */
.ck-content .page-break::after {
content: '';
position: absolute;
border-bottom: 2px dashed hsl(0, 0%, 77%);
width: 100%;
}
/* @ckeditor/ckeditor5-page-break/theme/pagebreak.css */
.ck-content .page-break__label {
position: relative;
z-index: 1;
padding: .3em .6em;
display: block;
text-transform: uppercase;
border: 1px solid hsl(0, 0%, 77%);
border-radius: 2px;
font-family: Helvetica, Arial, Tahoma, Verdana, Sans-Serif;
font-size: 0.75em;
font-weight: bold;
color: hsl(0, 0%, 20%);
background: hsl(0, 0%, 100%);
box-shadow: 2px 2px 1px hsla(0, 0%, 0%, 0.15);
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
/* @ckeditor/ckeditor5-table/theme/tablecolumnresize.css */
.ck-content .table .ck-table-resized {
table-layout: fixed;
}
/* @ckeditor/ckeditor5-table/theme/tablecolumnresize.css */
.ck-content .table table {
overflow: hidden;
}
/* @ckeditor/ckeditor5-table/theme/tablecolumnresize.css */
.ck-content .table td,
.ck-content .table th {
position: relative;
}
/* @ckeditor/ckeditor5-table/theme/table.css */
.ck-content .table {
margin: 0.9em auto;
/* @ckeditor/ckeditor5-image/theme/image.css */
.ck-content .image {
display: table;
}
/* @ckeditor/ckeditor5-table/theme/table.css */
.ck-content .table table {
border-collapse: collapse;
border-spacing: 0;
width: 100%;
height: 100%;
border: 1px double hsl(0, 0%, 70%);
}
/* @ckeditor/ckeditor5-table/theme/table.css */
.ck-content .table table td,
.ck-content .table table th {
min-width: 2em;
padding: .4em;
border: 1px solid hsl(0, 0%, 75%);
}
/* @ckeditor/ckeditor5-table/theme/table.css */
.ck-content .table table th {
font-weight: bold;
background: hsla(0, 0%, 0%, 5%);
}
/* @ckeditor/ckeditor5-table/theme/table.css */
.ck-content[dir="rtl"] .table th {
text-align: right;
}
/* @ckeditor/ckeditor5-table/theme/table.css */
.ck-content[dir="ltr"] .table th {
text-align: left;
}
/* @ckeditor/ckeditor5-table/theme/tablecaption.css */
.ck-content .table > figcaption {
display: table-caption;
caption-side: top;
word-break: break-word;
clear: both;
text-align: center;
color: var(--ck-color-table-caption-text);
background-color: var(--ck-color-table-caption-background);
margin: 0.9em auto;
min-width: 50px;
}
/* @ckeditor/ckeditor5-image/theme/image.css */
.ck-content .image img {
display: block;
margin: 0 auto;
max-width: 100%;
min-width: 100%;
}
/* @ckeditor/ckeditor5-image/theme/image.css */
.ck-content .image-inline {
/*
* Normally, the .image-inline would have "display: inline-block" and "img { width: 100% }" (to follow the wrapper while resizing).;
* Unfortunately, together with "srcset", it gets automatically stretched up to the width of the editing root.
* This strange behavior does not happen with inline-flex.
*/
display: inline-flex;
max-width: 100%;
align-items: flex-start;
}
/* @ckeditor/ckeditor5-image/theme/image.css */
.ck-content .image-inline picture {
display: flex;
}
/* @ckeditor/ckeditor5-image/theme/image.css */
.ck-content .image-inline picture,
.ck-content .image-inline img {
flex-grow: 1;
flex-shrink: 1;
max-width: 100%;
}
/* @ckeditor/ckeditor5-image/theme/imageresize.css */
.ck-content .image.image_resized {
max-width: 100%;
display: block;
box-sizing: border-box;
}
/* @ckeditor/ckeditor5-image/theme/imageresize.css */
.ck-content .image.image_resized img {
width: 100%;
}
/* @ckeditor/ckeditor5-image/theme/imageresize.css */
.ck-content .image.image_resized > figcaption {
display: block;
}
/* @ckeditor/ckeditor5-image/theme/imagecaption.css */
.ck-content .image > figcaption {
display: table-caption;
caption-side: bottom;
word-break: break-word;
color: var(--ck-color-image-caption-text);
background-color: var(--ck-color-image-caption-background);
padding: .6em;
font-size: .75em;
outline-offset: -1px;
}
/* @ckeditor/ckeditor5-highlight/theme/highlight.css */
.ck-content .marker-yellow {
background-color: var(--ck-highlight-marker-yellow);
}
/* @ckeditor/ckeditor5-highlight/theme/highlight.css */
.ck-content .marker-green {
background-color: var(--ck-highlight-marker-green);
}
/* @ckeditor/ckeditor5-highlight/theme/highlight.css */
.ck-content .marker-pink {
background-color: var(--ck-highlight-marker-pink);
}
/* @ckeditor/ckeditor5-highlight/theme/highlight.css */
.ck-content .marker-blue {
background-color: var(--ck-highlight-marker-blue);
}
/* @ckeditor/ckeditor5-highlight/theme/highlight.css */
.ck-content .pen-red {
color: var(--ck-highlight-pen-red);
background-color: transparent;
}
/* @ckeditor/ckeditor5-highlight/theme/highlight.css */
.ck-content .pen-green {
color: var(--ck-highlight-pen-green);
background-color: transparent;
}
/* @ckeditor/ckeditor5-list/theme/list.css */
.ck-content ol {
list-style-type: decimal;
}
/* @ckeditor/ckeditor5-list/theme/list.css */
.ck-content ol ol {
list-style-type: lower-latin;
}
/* @ckeditor/ckeditor5-list/theme/list.css */
.ck-content ol ol ol {
list-style-type: lower-roman;
}
/* @ckeditor/ckeditor5-list/theme/list.css */
.ck-content ol ol ol ol {
list-style-type: upper-latin;
}
/* @ckeditor/ckeditor5-list/theme/list.css */
.ck-content ol ol ol ol ol {
list-style-type: upper-roman;
}
/* @ckeditor/ckeditor5-list/theme/list.css */
.ck-content ul {
list-style-type: disc;
}
/* @ckeditor/ckeditor5-list/theme/list.css */
.ck-content ul ul {
list-style-type: circle;
}
/* @ckeditor/ckeditor5-list/theme/list.css */
.ck-content ul ul ul {
list-style-type: square;
}
/* @ckeditor/ckeditor5-list/theme/list.css */
.ck-content ul ul ul ul {
list-style-type: square;
}
/* @ckeditor/ckeditor5-image/theme/imagestyle.css */
.ck-content .image-style-block-align-left,
.ck-content .image-style-block-align-right {
max-width: calc(100% - var(--ck-image-style-spacing));
}
/* @ckeditor/ckeditor5-image/theme/imagestyle.css */
.ck-content .image-style-align-left,
.ck-content .image-style-align-right {
clear: none;
}
/* @ckeditor/ckeditor5-image/theme/imagestyle.css */
.ck-content .image-style-side {
float: right;
margin-left: var(--ck-image-style-spacing);
max-width: 50%;
}
/* @ckeditor/ckeditor5-image/theme/imagestyle.css */
.ck-content .image-style-align-left {
float: left;
margin-right: var(--ck-image-style-spacing);
}
/* @ckeditor/ckeditor5-image/theme/imagestyle.css */
.ck-content .image-style-align-center {
margin-left: auto;
margin-right: auto;
}
/* @ckeditor/ckeditor5-image/theme/imagestyle.css */
.ck-content .image-style-align-right {
float: right;
margin-left: var(--ck-image-style-spacing);
}
/* @ckeditor/ckeditor5-image/theme/imagestyle.css */
.ck-content .image-style-block-align-right {
margin-right: 0;
margin-left: auto;
}
/* @ckeditor/ckeditor5-image/theme/imagestyle.css */
.ck-content .image-style-block-align-left {
margin-left: 0;
margin-right: auto;
}
/* @ckeditor/ckeditor5-image/theme/imagestyle.css */
.ck-content p + .image-style-align-left,
.ck-content p + .image-style-align-right,
.ck-content p + .image-style-side {
margin-top: 0;
}
/* @ckeditor/ckeditor5-image/theme/imagestyle.css */
.ck-content .image-inline.image-style-align-left,
.ck-content .image-inline.image-style-align-right {
margin-top: var(--ck-inline-image-style-spacing);
margin-bottom: var(--ck-inline-image-style-spacing);
}
/* @ckeditor/ckeditor5-image/theme/imagestyle.css */
.ck-content .image-inline.image-style-align-left {
margin-right: var(--ck-inline-image-style-spacing);
}
/* @ckeditor/ckeditor5-image/theme/imagestyle.css */
.ck-content .image-inline.image-style-align-right {
margin-left: var(--ck-inline-image-style-spacing);
}
/* @ckeditor/ckeditor5-basic-styles/theme/code.css */
.ck-content code {
background-color: hsla(0, 0%, 78%, 0.3);
padding: .15em;
border-radius: 2px;
}
/* @ckeditor/ckeditor5-block-quote/theme/blockquote.css */
.ck-content blockquote {
overflow: hidden;
padding-right: 1.5em;
padding-left: 1.5em;
margin-left: 0;
margin-right: 0;
font-style: italic;
border-left: solid 5px hsl(0, 0%, 80%);
}
/* @ckeditor/ckeditor5-block-quote/theme/blockquote.css */
.ck-content[dir="rtl"] blockquote {
border-left: 0;
border-right: solid 5px hsl(0, 0%, 80%);
}
/* @ckeditor/ckeditor5-font/theme/fontsize.css */
.ck-content .text-tiny {
font-size: .7em;
}
/* @ckeditor/ckeditor5-font/theme/fontsize.css */
.ck-content .text-small {
font-size: .85em;
}
/* @ckeditor/ckeditor5-font/theme/fontsize.css */
.ck-content .text-big {
font-size: 1.4em;
}
/* @ckeditor/ckeditor5-font/theme/fontsize.css */
.ck-content .text-huge {
font-size: 1.8em;
}
/* @ckeditor/ckeditor5-mention/theme/mention.css */
.ck-content .mention {
background: var(--ck-color-mention-background);
color: var(--ck-color-mention-text);
}
/* @ckeditor/ckeditor5-horizontal-line/theme/horizontalline.css */
.ck-content hr {
margin: 15px 0;
height: 4px;
background: hsl(0, 0%, 87%);
border: 0;
}
/* @ckeditor/ckeditor5-code-block/theme/codeblock.css */
.ck-content pre {
padding: 1em;
@@ -438,18 +451,6 @@
padding: 0;
border-radius: 0;
}
/* @ckeditor/ckeditor5-horizontal-line/theme/horizontalline.css */
.ck-content hr {
margin: 15px 0;
height: 4px;
background: hsl(0, 0%, 87%);
border: 0;
}
/* @ckeditor/ckeditor5-mention/theme/mention.css */
.ck-content .mention {
background: var(--ck-color-mention-background);
color: var(--ck-color-mention-text);
}
@media print {
/* @ckeditor/ckeditor5-page-break/theme/pagebreak.css */
.ck-content .page-break {

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

1333
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -2,7 +2,7 @@
"name": "trilium",
"productName": "Trilium Notes",
"description": "Trilium Notes",
"version": "0.61.0-beta",
"version": "0.61.3-beta",
"license": "AGPL-3.0-only",
"main": "electron.js",
"bin": {
@@ -53,7 +53,7 @@
"escape-html": "1.0.3",
"express": "4.18.2",
"express-partial-content": "1.0.2",
"express-rate-limit": "6.8.0",
"express-rate-limit": "6.8.1",
"express-session": "1.17.3",
"fs-extra": "11.1.1",
"helmet": "7.0.0",
@@ -65,10 +65,10 @@
"ini": "3.0.1",
"is-animated": "2.0.2",
"is-svg": "4.3.2",
"jimp": "0.22.8",
"jimp": "0.22.10",
"joplin-turndown-plugin-gfm": "1.0.12",
"jsdom": "22.1.0",
"marked": "5.1.1",
"marked": "5.1.2",
"mime-types": "2.1.35",
"multer": "1.4.5-lts.1",
"node-abi": "3.45.0",
@@ -92,24 +92,24 @@
"turndown": "7.1.2",
"unescape": "1.0.1",
"ws": "8.13.0",
"xml2js": "0.6.0",
"xml2js": "0.6.2",
"yauzl": "2.10.0"
},
"devDependencies": {
"cross-env": "7.0.3",
"electron": "25.3.1",
"electron-builder": "24.4.0",
"electron": "25.3.2",
"electron-builder": "24.6.3",
"electron-packager": "17.1.1",
"electron-rebuild": "3.2.9",
"eslint": "8.45.0",
"eslint": "8.46.0",
"eslint-config-airbnb-base": "15.0.0",
"eslint-config-prettier": "8.8.0",
"eslint-plugin-import": "2.27.5",
"eslint-config-prettier": "8.9.0",
"eslint-plugin-import": "2.28.0",
"eslint-plugin-jsonc": "2.9.0",
"eslint-plugin-prettier": "5.0.0",
"esm": "3.2.25",
"husky": "8.0.3",
"jasmine": "5.0.2",
"jasmine": "5.1.0",
"jsdoc": "4.0.2",
"jsonc-eslint-parser": "2.3.0",
"lint-staged": "13.2.3",

View File

@@ -61,8 +61,8 @@ class AbstractBeccaEntity {
}
/** @protected */
addEntityChange(isDeleted = false) {
entityChangesService.addEntityChange({
putEntityChange(isDeleted = false) {
entityChangesService.putEntityChange({
entityName: this.constructor.entityName,
entityId: this[this.constructor.primaryKeyName],
hash: this.generateHash(isDeleted),
@@ -101,7 +101,7 @@ class AbstractBeccaEntity {
return;
}
this.addEntityChange(false);
this.putEntityChange(false);
if (!cls.isEntityEventsDisabled()) {
const eventPayload = {
@@ -132,8 +132,7 @@ class AbstractBeccaEntity {
if (this.hasStringContent()) {
content = content.toString();
}
else {
} else {
content = Buffer.isBuffer(content) ? content : Buffer.from(content);
}
@@ -156,32 +155,34 @@ class AbstractBeccaEntity {
this.save();
if (newBlobId !== oldBlobId) {
this.#deleteBlobIfNoteUsed(oldBlobId);
this.#deleteBlobIfNotUsed(oldBlobId);
}
}
});
}
#deleteBlobIfNoteUsed(blobId) {
if (sql.getValue("SELECT 1 FROM notes WHERE blobId = ? LIMIT 1", [blobId])) {
#deleteBlobIfNotUsed(oldBlobId) {
if (sql.getValue("SELECT 1 FROM notes WHERE blobId = ? LIMIT 1", [oldBlobId])) {
return;
}
if (sql.getValue("SELECT 1 FROM attachments WHERE blobId = ? LIMIT 1", [blobId])) {
if (sql.getValue("SELECT 1 FROM attachments WHERE blobId = ? LIMIT 1", [oldBlobId])) {
return;
}
if (sql.getValue("SELECT 1 FROM revisions WHERE blobId = ? LIMIT 1", [blobId])) {
if (sql.getValue("SELECT 1 FROM revisions WHERE blobId = ? LIMIT 1", [oldBlobId])) {
return;
}
sql.execute("DELETE FROM blobs WHERE blobId = ?", [blobId]);
sql.execute("DELETE FROM entity_changes WHERE entityName = 'blobs' AND entityId = ?", [blobId]);
sql.execute("DELETE FROM blobs WHERE blobId = ?", [oldBlobId]);
// blobs are not marked as erased in entity_changes, they are just purged completely
// this is because technically every keystroke can create a new blob and there would be just too many
sql.execute("DELETE FROM entity_changes WHERE entityName = 'blobs' AND entityId = ?", [oldBlobId]);
}
#getUnencryptedContentForHashCalculation(unencryptedContent) {
if (this.isProtected) {
// a "random" prefix make sure that the calculated hash/blobId is different for an encrypted note and decrypted
// a "random" prefix makes sure that the calculated hash/blobId is different for a decrypted/encrypted content
const encryptedPrefixSuffix = "t$[nvQg7q)&_ENCRYPTED_?M:Bf&j3jr_";
return Buffer.isBuffer(unencryptedContent)
? Buffer.concat([Buffer.from(encryptedPrefixSuffix), unencryptedContent])
@@ -196,7 +197,7 @@ class AbstractBeccaEntity {
* We're using the unencrypted blob for the hash calculation, because otherwise the random IV would
* cause every content blob to be unique which would balloon the database size (esp. with revisioning).
* This has minor security implications (it's easy to infer that given content is shared between different
* notes/attachments, but the trade-off comes out clearly positive).
* notes/attachments), but the trade-off comes out clearly positive.
*/
const newBlobId = utils.hashedBlobId(unencryptedContentForHashCalculation);
const blobNeedsInsert = !sql.getValue('SELECT 1 FROM blobs WHERE blobId = ?', [newBlobId]);
@@ -214,9 +215,11 @@ class AbstractBeccaEntity {
sql.upsert("blobs", "blobId", pojo);
const hash = utils.hash(`${newBlobId}|${pojo.content.toString()}`);
// we can't reuse blobId as an entity_changes hash, because this one has to be calculatable without having
// access to the decrypted content
const hash = blobService.calculateContentHash(pojo);
entityChangesService.addEntityChange({
entityChangesService.putEntityChange({
entityName: 'blobs',
entityId: newBlobId,
hash: hash,
@@ -276,7 +279,7 @@ class AbstractBeccaEntity {
log.info(`Marking ${entityName} ${entityId} as deleted`);
this.addEntityChange(true);
this.putEntityChange(true);
eventService.emit(eventService.ENTITY_DELETED, { entityName, entityId, entity: this });
}
@@ -293,7 +296,7 @@ class AbstractBeccaEntity {
log.info(`Marking ${entityName} ${entityId} as deleted`);
this.addEntityChange(true);
this.putEntityChange(true);
eventService.emit(eventService.ENTITY_DELETED, { entityName, entityId, entity: this });
}

View File

@@ -75,7 +75,7 @@ function register(router) {
eu.route(router, 'post' ,'/etapi/refresh-note-ordering/:parentNoteId', (req, res, next) => {
eu.getAndCheckNote(req.params.parentNoteId);
entityChangesService.addNoteReorderingEntityChange(req.params.parentNoteId, "etapi");
entityChangesService.putNoteReorderingEntityChange(req.params.parentNoteId, "etapi");
res.sendStatus(204);
});

View File

@@ -182,8 +182,6 @@ export default class Entrypoints extends Component {
}
hideAllPopups() {
$(".tooltip").removeClass("show");
if (utils.isDesktop()) {
$(".aa-input").autocomplete("close");
}

View File

@@ -20,7 +20,7 @@ export default class TabManager extends Component {
this.activeNtxId = null;
// elements are arrays of note contexts for each tab (one main context + subcontexts [splits])
// elements are arrays of {contexts, position}, storing note contexts for each tab (one main context + subcontexts [splits]), and the original position of the tab
this.recentlyClosedTabs = [];
this.tabsUpdate = new SpacedUpdate(async () => {
@@ -448,21 +448,23 @@ export default class TabManager extends Component {
removeNoteContexts(noteContextsToRemove) {
const ntxIdsToRemove = noteContextsToRemove.map(nc => nc.ntxId);
const position = this.noteContexts.findIndex(nc => ntxIdsToRemove.includes(nc.ntxId));
this.children = this.children.filter(nc => !ntxIdsToRemove.includes(nc.ntxId));
this.addToRecentlyClosedTabs(noteContextsToRemove);
this.addToRecentlyClosedTabs(noteContextsToRemove, position);
this.triggerEvent('noteContextRemoved', {ntxIds: ntxIdsToRemove});
this.tabsUpdate.scheduleUpdate();
}
addToRecentlyClosedTabs(noteContexts) {
addToRecentlyClosedTabs(noteContexts, position) {
if (noteContexts.length === 1 && noteContexts[0].isEmpty()) {
return;
}
this.recentlyClosedTabs.push(noteContexts);
this.recentlyClosedTabs.push({contexts: noteContexts, position: position});
}
tabReorderEvent({ntxIdsInOrder}) {
@@ -574,7 +576,8 @@ export default class TabManager extends Component {
closeLastEmptyTab = this.noteContexts[0];
}
const noteContexts = this.recentlyClosedTabs.pop();
const lastClosedTab = this.recentlyClosedTabs.pop();
const noteContexts = lastClosedTab.contexts;
for (const noteContext of noteContexts) {
this.child(noteContext);
@@ -582,6 +585,30 @@ export default class TabManager extends Component {
await this.triggerEvent('newNoteContextCreated', {noteContext});
}
// restore last position of contexts stored in tab manager
const ntxsInOrder = [
...this.noteContexts.slice(0, lastClosedTab.position),
...this.noteContexts.slice(-noteContexts.length),
...this.noteContexts.slice(lastClosedTab.position, -noteContexts.length),
]
await this.noteContextReorderEvent({ntxIdsInOrder: ntxsInOrder.map(nc => nc.ntxId)});
let mainNtx = noteContexts.find(nc => nc.isMainContext());
if (mainNtx) {
// reopened a tab, need to reorder new tab widget in tab row
await this.triggerEvent('contextsReopened', {
mainNtxId: mainNtx.ntxId,
tabPosition: ntxsInOrder.filter(nc => nc.isMainContext()).findIndex(nc => nc.ntxId === mainNtx.ntxId)
});
} else {
// reopened a single split, need to reorder the pane widget in split note container
await this.triggerEvent('contextsReopened', {
ntxId: ntxsInOrder[lastClosedTab.position].ntxId,
// this is safe since lastClosedTab.position can never be 0 in this case
afterNtxId: ntxsInOrder[lastClosedTab.position - 1].ntxId
});
}
const noteContextToActivate = noteContexts.length === 1
? noteContexts[0]
: noteContexts.find(nc => nc.isMainContext());

View File

@@ -1,5 +1,6 @@
class FAttachment {
constructor(froca, row) {
/** @type {Froca} */
this.froca = froca;
this.update(row);
@@ -34,12 +35,9 @@ class FAttachment {
return this.froca.notes[this.ownerId];
}
/**
* @param [opts.preview=false] - retrieve only first 10 000 characters for a preview
* @return {FBlob}
*/
async getBlob(opts = {}) {
return await this.froca.getBlob('attachments', this.attachmentId, opts);
/** @return {FBlob} */
async getBlob() {
return await this.froca.getBlob('attachments', this.attachmentId);
}
}

View File

@@ -6,6 +6,7 @@ import promotedAttributeDefinitionParser from '../services/promoted_attribute_de
*/
class FAttribute {
constructor(froca, row) {
/** @type {Froca} */
this.froca = froca;
this.update(row);

View File

@@ -4,6 +4,7 @@
*/
class FBranch {
constructor(froca, row) {
/** @type {Froca} */
this.froca = froca;
this.update(row);

View File

@@ -31,6 +31,7 @@ class FNote {
* @param {Object.<string, Object>} row
*/
constructor(froca, row) {
/** @type {Froca} */
this.froca = froca;
/** @type {string[]} */
@@ -859,12 +860,9 @@ class FNote {
return this.getBlob();
}
/**
* @param [opts.preview=false] - retrieve only first 10 000 characters for a preview
* @return {Promise<FBlob>}
*/
async getBlob(opts = {}) {
return await this.froca.getBlob('notes', this.noteId, opts);
/** @return {Promise<FBlob>} */
async getBlob() {
return await this.froca.getBlob('notes', this.noteId);
}
toString() {

View File

@@ -67,7 +67,6 @@ export default class TreeContextMenu {
{ title: "Advanced", uiIcon: "bx bx-empty", enabled: true, items: [
{ title: 'Expand subtree <kbd data-command="expandSubtree"></kbd>', command: "expandSubtree", uiIcon: "bx bx-expand", enabled: noSelectedNotes },
{ title: 'Collapse subtree <kbd data-command="collapseSubtree"></kbd>', command: "collapseSubtree", uiIcon: "bx bx-collapse", enabled: noSelectedNotes },
{ title: "Force note sync", command: "forceNoteSync", uiIcon: "bx bx-refresh", enabled: noSelectedNotes },
{ title: 'Sort by ... <kbd data-command="sortChildNotes"></kbd>', command: "sortChildNotes", uiIcon: "bx bx-empty", enabled: noSelectedNotes && notSearch },
{ title: 'Recent changes in subtree', command: "recentChangesInSubtree", uiIcon: "bx bx-history", enabled: noSelectedNotes },
{ title: 'Convert to attachment', command: "convertNoteToAttachment", uiIcon: "bx bx-empty", enabled: isNotRoot && !isHoisted }

View File

@@ -19,7 +19,6 @@ let idCounter = 1;
*/
async function getRenderedContent(entity, options = {}) {
options = Object.assign({
trim: false,
tooltip: false
}, options);
@@ -29,7 +28,7 @@ async function getRenderedContent(entity, options = {}) {
const $renderedContent = $('<div class="rendered-content">');
if (type === 'text') {
await renderText(entity, options, $renderedContent);
await renderText(entity, $renderedContent);
}
else if (type === 'code') {
await renderCode(entity, options, $renderedContent);
@@ -86,12 +85,13 @@ async function getRenderedContent(entity, options = {}) {
};
}
async function renderText(note, options, $renderedContent) {
/** @param {FNote} note */
async function renderText(note, $renderedContent) {
// entity must be FNote
const blob = await note.getBlob({preview: options.trim});
const blob = await note.getBlob();
if (!utils.isHtmlEmpty(blob.content)) {
$renderedContent.append($('<div class="ck-content">').html(trim(blob.content, options.trim)));
$renderedContent.append($('<div class="ck-content">').html(blob.content));
if ($renderedContent.find('span.math-tex').length > 0) {
await libraryLoader.requireLibrary(libraryLoader.KATEX);
@@ -112,10 +112,11 @@ async function renderText(note, options, $renderedContent) {
}
}
async function renderCode(note, options, $renderedContent) {
const blob = await note.getBlob({preview: options.trim});
/** @param {FNote} note */
async function renderCode(note, $renderedContent) {
const blob = await note.getBlob();
$renderedContent.append($("<pre>").text(trim(blob.content, options.trim)));
$renderedContent.append($("<pre>").text(blob.content));
}
function renderImage(entity, $renderedContent, options = {}) {
@@ -144,8 +145,8 @@ function renderImage(entity, $renderedContent, options = {}) {
if (options.imageHasZoom) {
libraryLoader.requireLibrary(libraryLoader.WHEEL_ZOOM).then(() => {
WZoom.create(`#${$img.attr("id")}`, {
maxScale: 10,
speed: 20,
maxScale: 50,
speed: 1.3,
zoomOnClick: false
});
});
@@ -285,15 +286,6 @@ async function renderChildrenList($renderedContent, note) {
}
}
function trim(text, doTrim) {
if (!doTrim) {
return text;
}
else {
return text.substr(0, Math.min(text.length, 2000));
}
}
function getRenderingType(entity) {
let type = entity.type || entity.role;
const mime = entity.mime;

View File

@@ -368,12 +368,11 @@ class Froca {
}
/** @returns {Promise<FBlob>} */
async getBlob(entityType, entityId, opts = {}) {
opts.preview = !!opts.preview;
const key = `${entityType}-${entityId}-${opts.preview}`;
async getBlob(entityType, entityId) {
const key = `${entityType}-${entityId}`;
if (!this.blobPromises[key]) {
this.blobPromises[key] = server.get(`${entityType}/${entityId}/blob?preview=${opts.preview}`)
this.blobPromises[key] = server.get(`${entityType}/${entityId}/blob`)
.then(row => new FBlob(row))
.catch(e => console.error(`Cannot get blob for ${entityType} '${entityId}'`));

View File

@@ -304,6 +304,9 @@ function FrontendScriptApi(startNote, currentNote, originEntity = null, $contain
*/
this.createLink = linkService.createLink;
/** @deprecated - use api.createLink() instead */
this.createNoteLink = linkService.createLink;
/**
* Adds given text to the editor cursor
*

View File

@@ -8,27 +8,32 @@ import appContext from "../components/app_context.js";
function setupGlobalTooltip() {
$(document).on("mouseenter", "a", mouseEnterHandler);
$(document).on("mouseleave", "a", mouseLeaveHandler);
// close any note tooltip after click, this fixes the problem that sometimes tooltips remained on the screen
$(document).on("click", () => $('.note-tooltip').remove());
$(document).on("click", e => {
if ($(e.target).closest(".note-tooltip").length) {
// click within the tooltip shouldn't close it
return;
}
$('.note-tooltip').remove();
});
}
function setupElementTooltip($el) {
$el.on('mouseenter', mouseEnterHandler);
$el.on('mouseleave', mouseLeaveHandler);
}
async function mouseEnterHandler() {
const $link = $(this);
if ($link.hasClass("no-tooltip-preview")
|| $link.hasClass("disabled")) {
if ($link.hasClass("no-tooltip-preview") || $link.hasClass("disabled")) {
return;
}
// this is to avoid showing tooltip from inside the CKEditor link editor dialog
if ($link.closest(".ck-link-actions").length) {
} else if ($link.closest(".ck-link-actions").length) {
// this is to avoid showing tooltip from inside the CKEditor link editor dialog
return;
} else if ($link.closest(".note-tooltip").length) {
// don't show tooltip for links within tooltip
return;
}
@@ -39,8 +44,21 @@ async function mouseEnterHandler() {
return;
}
const linkId = $link.attr("data-link-id") || `link-${Math.floor(Math.random() * 1000000)}`;
$link.attr("data-link-id", linkId);
if ($(`.${linkId}`).is(":visible")) {
// tooltip is already open for this link
return;
}
const note = await froca.getNote(noteId);
const content = await renderTooltip(note);
const [content] = await Promise.all([
renderTooltip(note),
// to reduce flicker due to accidental mouseover, cursor must stay for a bit over the link for tooltip to appear
new Promise(res => setTimeout(res, 500))
]);
if (utils.isHtmlEmpty(content)) {
return;
@@ -53,7 +71,6 @@ async function mouseEnterHandler() {
// we now create tooltip which won't close because it won't receive mouseleave event
if ($(this).is(":hover")) {
$(this).tooltip({
delay: {"show": 300, "hide": 100},
container: 'body',
// https://github.com/zadam/trilium/issues/2794 https://github.com/zadam/trilium/issues/2988
// with bottom this flickering happens a bit less
@@ -63,15 +80,19 @@ async function mouseEnterHandler() {
title: html,
html: true,
template: '<div class="tooltip note-tooltip" role="tooltip"><div class="arrow"></div><div class="tooltip-inner"></div></div>',
sanitize: false
sanitize: false,
customClass: linkId
});
$(this).tooltip('show');
}
}
function mouseLeaveHandler() {
$(this).tooltip('dispose');
setTimeout(() => {
if (!$(this).is(":hover") && !$(`.${linkId}`).is(":hover")) {
// cursor is neither over the link nor over the tooltip, user likely is not interested
$(this).tooltip('dispose');
}
}, 1000);
}
}
async function renderTooltip(note) {

View File

@@ -18,13 +18,6 @@ async function syncNow(ignoreNotConfigured = false) {
}
}
async function forceNoteSync(noteId) {
await server.post(`sync/force-note-sync/${noteId}`);
toastService.showMessage("Note added to sync queue.");
}
export default {
syncNow,
forceNoteSync
syncNow
};

View File

@@ -44,4 +44,8 @@ export default class MovePaneButton extends OnClickButtonWidget {
async noteContextReorderEvent() {
this.refresh();
}
async contextsReopenedEvent() {
this.refresh();
}
}

View File

@@ -136,6 +136,15 @@ export default class SplitNoteContainer extends FlexContainer {
}
}
contextsReopenedEvent({ntxId, afterNtxId}) {
if (ntxId === undefined || afterNtxId === undefined) {
// no single split reopened
return;
}
this.$widget.find(`[data-ntx-id="${ntxId}"]`)
.insertAfter(this.$widget.find(`[data-ntx-id="${afterNtxId}"]`));
}
async refresh() {
this.toggleExt(true);
}

View File

@@ -85,7 +85,7 @@ export default class HighlightsListWidget extends RightPanelWidget {
const optionsHighlightsList = JSON.parse(options.get('highlightsList'));
if (note.isLabelTruthy('hideHighlightWidget') || !optionsHighlightsList) {
if (note.isLabelTruthy('hideHighlightWidget') || !optionsHighlightsList.length) {
this.toggleInt(false);
this.triggerCommand("reEvaluateRightPaneVisibility");
return;

View File

@@ -1564,10 +1564,6 @@ export default class NoteTreeWidget extends NoteContextAwareWidget {
this.triggerCommand("showImportDialog", {noteId: node.data.noteId});
}
forceNoteSyncCommand({node}) {
syncService.forceNoteSync(node.data.noteId);
}
editNoteTitleCommand({node}) {
appContext.triggerCommand('focusOnTitle');
}

View File

@@ -620,6 +620,15 @@ export default class TabRowWidget extends BasicWidget {
this.updateTabById(newMainNtxId);
}
contextsReopenedEvent({mainNtxId, tabPosition}) {
if (mainNtxId === undefined || tabPosition === undefined) {
// no tab reopened
return;
}
const tabEl = this.getTabById(mainNtxId)[0];
tabEl.parentNode.insertBefore(tabEl, this.tabEls[tabPosition]);
}
updateTabById(ntxId) {
const $tab = this.getTabById(ntxId);

View File

@@ -471,7 +471,7 @@ table.promoted-attributes-in-tooltip td, table.promoted-attributes-in-tooltip th
.note-tooltip-content {
/* height needs to stay small because tooltip has problem when it can't fit to either top or bottom of the cursor */
max-height: 300px;
overflow: hidden;
overflow: auto;
}
.note-tooltip-content .note-title-with-path .note-path {

View File

@@ -4,7 +4,7 @@ const sql = require('../../services/sql');
const utils = require('../../services/utils');
const entityChangesService = require('../../services/entity_changes');
const treeService = require('../../services/tree');
const noteService = require('../../services/notes');
const eraseService = require('../../services/erase');
const becca = require('../../becca/becca');
const TaskContext = require('../../services/task_context');
const branchService = require("../../services/branches");
@@ -72,7 +72,7 @@ function moveBranchBeforeNote(req) {
treeService.sortNotesIfNeeded(parentNote.noteId);
// if sorting is not needed, then still the ordering might have changed above manually
entityChangesService.addNoteReorderingEntityChange(parentNote.noteId);
entityChangesService.putNoteReorderingEntityChange(parentNote.noteId);
log.info(`Moved note ${branchToMove.noteId}, branch ${branchId} before note ${beforeBranch.noteId}, branch ${beforeBranchId}`);
@@ -123,7 +123,7 @@ function moveBranchAfterNote(req) {
treeService.sortNotesIfNeeded(parentNote.noteId);
// if sorting is not needed, then still the ordering might have changed above manually
entityChangesService.addNoteReorderingEntityChange(parentNote.noteId);
entityChangesService.putNoteReorderingEntityChange(parentNote.noteId);
log.info(`Moved note ${branchToMove.noteId}, branch ${branchId} after note ${afterNote.noteId}, branch ${afterBranchId}`);
@@ -193,7 +193,7 @@ function deleteBranch(req) {
if (eraseNotes) {
// erase automatically means deleting all clones + note itself
branch.getNote().deleteNote(deleteId, taskContext);
noteService.eraseNotesWithDeleteId(deleteId);
eraseService.eraseNotesWithDeleteId(deleteId);
noteDeleted = true;
} else {
noteDeleted = branch.deleteBranch(deleteId, taskContext);

View File

@@ -3,7 +3,7 @@
const options = require('../../services/options');
const utils = require('../../services/utils');
const dateUtils = require('../../services/date_utils');
const instanceId = require('../../services/member_id');
const instanceId = require('../../services/instance_id');
const passwordEncryptionService = require('../../services/encryption/password_encryption');
const protectedSessionService = require('../../services/protected_session');
const appInfo = require('../../services/app_info');

View File

@@ -1,6 +1,7 @@
"use strict";
const noteService = require('../../services/notes');
const eraseService = require('../../services/erase');
const treeService = require('../../services/tree');
const sql = require('../../services/sql');
const utils = require('../../services/utils');
@@ -15,9 +16,7 @@ function getNote(req) {
}
function getNoteBlob(req) {
const preview = req.query.preview === 'true';
return blobService.getBlobPojo('notes', req.params.noteId, { preview });
return blobService.getBlobPojo('notes', req.params.noteId);
}
function getNoteMetadata(req) {
@@ -67,7 +66,7 @@ function deleteNote(req) {
note.deleteNote(deleteId, taskContext);
if (eraseNotes) {
noteService.eraseNotesWithDeleteId(deleteId);
eraseService.eraseNotesWithDeleteId(deleteId);
}
if (last) {
@@ -152,11 +151,11 @@ function duplicateSubtree(req) {
}
function eraseDeletedNotesNow() {
noteService.eraseDeletedNotesNow();
eraseService.eraseDeletedNotesNow();
}
function eraseUnusedAttachmentsNow() {
noteService.eraseUnusedAttachmentsNow();
eraseService.eraseUnusedAttachmentsNow();
}
function getDeleteNotesPreview(req) {

View File

@@ -9,10 +9,8 @@ const optionService = require('../../services/options');
const contentHashService = require('../../services/content_hash');
const log = require('../../services/log');
const syncOptions = require('../../services/sync_options');
const dateUtils = require('../../services/date_utils');
const utils = require('../../services/utils');
const ws = require('../../services/ws');
const becca = require("../../becca/becca");
async function testSync() {
try {
@@ -84,54 +82,14 @@ function forceFullSync() {
syncService.sync();
}
function forceNoteSync(req) {
const noteId = req.params.noteId;
const note = becca.getNote(noteId);
const now = dateUtils.utcNowDateTime();
sql.execute(`UPDATE notes SET utcDateModified = ? WHERE noteId = ?`, [now, noteId]);
entityChangesService.moveEntityChangeToTop('notes', noteId);
sql.execute(`UPDATE blobs SET utcDateModified = ? WHERE blobId = ?`, [now, note.blobId]);
entityChangesService.moveEntityChangeToTop('blobs', note.blobId);
for (const branchId of sql.getColumn("SELECT branchId FROM branches WHERE noteId = ?", [noteId])) {
sql.execute(`UPDATE branches SET utcDateModified = ? WHERE branchId = ?`, [now, branchId]);
entityChangesService.moveEntityChangeToTop('branches', branchId);
}
for (const attributeId of sql.getColumn("SELECT attributeId FROM attributes WHERE noteId = ?", [noteId])) {
sql.execute(`UPDATE attributes SET utcDateModified = ? WHERE attributeId = ?`, [now, attributeId]);
entityChangesService.moveEntityChangeToTop('attributes', attributeId);
}
for (const revisionId of sql.getColumn("SELECT revisionId FROM revisions WHERE noteId = ?", [noteId])) {
sql.execute(`UPDATE revisions SET utcDateModified = ? WHERE revisionId = ?`, [now, revisionId]);
entityChangesService.moveEntityChangeToTop('revisions', revisionId);
}
for (const attachmentId of sql.getColumn("SELECT attachmentId FROM attachments WHERE noteId = ?", [noteId])) {
sql.execute(`UPDATE attachments SET utcDateModified = ? WHERE attachmentId = ?`, [now, attachmentId]);
entityChangesService.moveEntityChangeToTop('attachments', attachmentId);
}
log.info(`Forcing note sync for ${noteId}`);
// not awaiting for the job to finish (will probably take a long time)
syncService.sync();
}
function getChanged(req) {
const startTime = Date.now();
let lastEntityChangeId = parseInt(req.query.lastEntityChangeId);
const clientinstanceId = req.query.instanceId;
const clientInstanceId = req.query.instanceId;
let filteredEntityChanges = [];
while (filteredEntityChanges.length === 0) {
do {
const entityChanges = sql.getRows(`
SELECT *
FROM entity_changes
@@ -144,20 +102,22 @@ function getChanged(req) {
break;
}
filteredEntityChanges = entityChanges.filter(ec => ec.instanceId !== clientinstanceId);
filteredEntityChanges = entityChanges.filter(ec => ec.instanceId !== clientInstanceId);
if (filteredEntityChanges.length === 0) {
lastEntityChangeId = entityChanges[entityChanges.length - 1].id;
}
}
} while (filteredEntityChanges.length === 0);
const entityChangeRecords = syncService.getEntityChangeRecords(filteredEntityChanges);
if (entityChangeRecords.length > 0) {
lastEntityChangeId = entityChangeRecords[entityChangeRecords.length - 1].entityChange.id;
log.info(`Returning ${entityChangeRecords.length} entity changes in ${Date.now() - startTime}ms`);
}
const ret = {
return {
entityChanges: entityChangeRecords,
lastEntityChangeId,
outstandingPullCount: sql.getValue(`
@@ -165,14 +125,8 @@ function getChanged(req) {
FROM entity_changes
WHERE isSynced = 1
AND instanceId != ?
AND id > ?`, [clientinstanceId, lastEntityChangeId])
AND id > ?`, [clientInstanceId, lastEntityChangeId])
};
if (ret.entityChanges.length > 0) {
log.info(`Returning ${ret.entityChanges.length} entity changes in ${Date.now() - startTime}ms`);
}
return ret;
}
const partialRequests = {};
@@ -194,12 +148,12 @@ function update(req) {
}
if (!partialRequests[requestId]) {
throw new Error(`Partial request ${requestId}, index ${pageIndex} of ${pageCount} of pages does not have expected record.`);
throw new Error(`Partial request ${requestId}, page ${pageIndex + 1} of ${pageCount} of pages does not have expected record.`);
}
partialRequests[requestId].payload += req.body;
log.info(`Receiving partial request ${requestId}, page index ${pageIndex} out of ${pageCount} pages.`);
log.info(`Receiving a partial request ${requestId}, page ${pageIndex + 1} out of ${pageCount} pages.`);
if (pageIndex !== pageCount - 1) {
return;
@@ -212,9 +166,11 @@ function update(req) {
const {entities, instanceId} = body;
for (const {entityChange, entity} of entities) {
syncUpdateService.updateEntity(entityChange, entity, instanceId);
}
sql.transactional(() => {
for (const {entityChange, entity} of entities) {
syncUpdateService.updateEntity(entityChange, entity, instanceId);
}
});
}
setInterval(() => {
@@ -241,8 +197,7 @@ function queueSector(req) {
}
function checkEntityChanges() {
const consistencyChecks = require("../../services/consistency_checks");
consistencyChecks.runEntityChangesChecks();
require("../../services/consistency_checks").runEntityChangesChecks();
}
module.exports = {
@@ -251,7 +206,6 @@ module.exports = {
syncNow,
fillEntityChanges,
forceFullSync,
forceNoteSync,
getChanged,
update,
getStats,

View File

@@ -216,7 +216,6 @@ function register(app) {
apiRoute(PST, '/api/sync/now', syncApiRoute.syncNow);
apiRoute(PST, '/api/sync/fill-entity-changes', syncApiRoute.fillEntityChanges);
apiRoute(PST, '/api/sync/force-full-sync', syncApiRoute.forceFullSync);
apiRoute(PST, '/api/sync/force-note-sync/:noteId', syncApiRoute.forceNoteSync);
route(GET, '/api/sync/check', [auth.checkApiAuth], syncApiRoute.checkSync, apiResultHandler);
route(GET, '/api/sync/changed', [auth.checkApiAuth], syncApiRoute.getChanged, apiResultHandler);
route(PUT, '/api/sync/update', [auth.checkApiAuth], syncApiRoute.update, apiResultHandler);

View File

@@ -4,8 +4,8 @@ const build = require('./build');
const packageJson = require('../../package');
const {TRILIUM_DATA_DIR} = require('./data_dir');
const APP_DB_VERSION = 222;
const SYNC_VERSION = 30;
const APP_DB_VERSION = 225;
const SYNC_VERSION = 31;
const CLIPPER_PROTOCOL_VERSION = "1.0";
module.exports = {

View File

@@ -1,10 +1,9 @@
const becca = require('../becca/becca');
const NotFoundError = require("../errors/not_found_error");
const protectedSessionService = require("./protected_session");
const utils = require("./utils");
function getBlobPojo(entityName, entityId, opts = {}) {
opts.preview = !!opts.preview;
function getBlobPojo(entityName, entityId) {
const entity = becca.getEntity(entityName, entityId);
if (!entity) {
@@ -19,10 +18,6 @@ function getBlobPojo(entityName, entityId, opts = {}) {
pojo.content = null;
} else {
pojo.content = processContent(pojo.content, entity.isProtected, true);
if (opts.preview && pojo.content.length > 10000) {
pojo.content = `${pojo.content.substr(0, 10000)}\r\n\r\n... and ${pojo.content.length - 10000} more characters.`;
}
}
return pojo;
@@ -51,7 +46,12 @@ function processContent(content, isProtected, isStringContent) {
}
}
function calculateContentHash({blobId, content}) {
return utils.hash(`${blobId}|${content.toString()}`);
}
module.exports = {
getBlobPojo,
processContent
processContent,
calculateContentHash
};

View File

@@ -1 +1 @@
module.exports = { buildDate:"2023-07-22T17:51:08+02:00", buildRevision: "98b0baefe2fca32d4507ea8f690574968e602c10" };
module.exports = { buildDate:"2023-07-31T23:03:45+02:00", buildRevision: "530e56dcb595a764ec0f6221c09b1b3a7ac22f0d" };

View File

@@ -161,7 +161,7 @@ function cloneNoteAfter(noteId, afterBranchId) {
sql.execute("UPDATE branches SET notePosition = notePosition + 10 WHERE parentNoteId = ? AND notePosition > ? AND isDeleted = 0",
[afterNote.parentNoteId, afterNote.notePosition]);
eventChangesService.addNoteReorderingEntityChange(afterNote.parentNoteId);
eventChangesService.putNoteReorderingEntityChange(afterNote.parentNoteId);
const branch = new BBranch({
noteId: noteId,

View File

@@ -56,7 +56,7 @@ function getAndClearEntityChangeIds() {
return entityChangeIds;
}
function addEntityChange(entityChange) {
function putEntityChange(entityChange) {
if (namespace.get('ignoreEntityChangeIds')) {
return;
}
@@ -91,6 +91,6 @@ module.exports = {
isEntityEventsDisabled,
reset,
getAndClearEntityChangeIds,
addEntityChange,
putEntityChange,
ignoreEntityChangeIds,
};

View File

@@ -414,7 +414,7 @@ class ConsistencyChecks {
const hash = utils.hash(utils.randomString(10));
entityChangesService.addEntityChange({
entityChangesService.putEntityChange({
entityName: 'blobs',
entityId: blobId,
hash: hash,
@@ -597,23 +597,19 @@ class ConsistencyChecks {
runEntityChangeChecks(entityName, key) {
this.findAndFixIssues(`
SELECT
${key} as entityId
FROM
${entityName}
LEFT JOIN entity_changes ON entity_changes.entityName = '${entityName}'
AND entity_changes.entityId = ${key}
WHERE
entity_changes.id IS NULL`,
SELECT ${key} as entityId
FROM ${entityName}
LEFT JOIN entity_changes ec ON ec.entityName = '${entityName}' AND ec.entityId = ${entityName}.${key}
WHERE ec.id IS NULL`,
({entityId}) => {
const entityRow = sql.getRow(`SELECT * FROM ${entityName} WHERE ${key} = ?`, [entityId]);
if (this.autoFix) {
entityChangesService.addEntityChange({
entityChangesService.putEntityChange({
entityName,
entityId,
hash: utils.randomString(10), // doesn't matter, will force sync, but that's OK
isErased: !!entityRow.isErased,
isErased: false,
utcDateChanged: entityRow.utcDateModified || entityRow.utcDateCreated,
isSynced: entityName !== 'options' || entityRow.isSynced
});
@@ -625,15 +621,13 @@ class ConsistencyChecks {
});
this.findAndFixIssues(`
SELECT
id, entityId
FROM
entity_changes
LEFT JOIN ${entityName} ON entityId = ${key}
SELECT id, entityId
FROM entity_changes
LEFT JOIN ${entityName} ON entityId = ${entityName}.${key}
WHERE
entity_changes.isErased = 0
AND entity_changes.entityName = '${entityName}'
AND ${key} IS NULL`,
AND ${entityName}.${key} IS NULL`,
({id, entityId}) => {
if (this.autoFix) {
sql.execute("DELETE FROM entity_changes WHERE entityName = ? AND entityId = ?", [entityName, entityId]);
@@ -645,11 +639,9 @@ class ConsistencyChecks {
});
this.findAndFixIssues(`
SELECT
id, entityId
FROM
entity_changes
JOIN ${entityName} ON entityId = ${key}
SELECT id, entityId
FROM entity_changes
JOIN ${entityName} ON entityId = ${entityName}.${key}
WHERE
entity_changes.isErased = 1
AND entity_changes.entityName = '${entityName}'`,

View File

@@ -3,14 +3,19 @@
const sql = require('./sql');
const utils = require('./utils');
const log = require('./log');
const eraseService = require("./erase");
function getEntityHashes() {
// blob erasure is not synced, we should check before each sync if there's some blob to erase
eraseService.eraseUnusedBlobs();
const startTime = new Date();
const hashRows = sql.getRawRows(`
SELECT entityName,
entityId,
hash
hash,
isErased
FROM entity_changes
WHERE isSynced = 1
AND entityName != 'note_reordering'`);
@@ -21,12 +26,13 @@ function getEntityHashes() {
const hashMap = {};
for (const [entityName, entityId, hash] of hashRows) {
for (const [entityName, entityId, hash, isErased] of hashRows) {
const entityHashMap = hashMap[entityName] = hashMap[entityName] || {};
const sector = entityId[0];
entityHashMap[sector] = (entityHashMap[sector] || "") + hash
// if the entity is erased, its hash is not updated, so it has to be added extra
entityHashMap[sector] = (entityHashMap[sector] || "") + hash + isErased;
}
for (const entityHashMap of Object.values(hashMap)) {

View File

@@ -3,18 +3,19 @@ const dateUtils = require('./date_utils');
const log = require('./log');
const cls = require('./cls');
const utils = require('./utils');
const instanceId = require('./member_id');
const instanceId = require('./instance_id');
const becca = require("../becca/becca");
const blobService = require("../services/blob");
let maxEntityChangeId = 0;
function addEntityChangeWithInstanceId(origEntityChange, instanceId) {
function putEntityChangeWithInstanceId(origEntityChange, instanceId) {
const ec = {...origEntityChange, instanceId};
return addEntityChange(ec);
putEntityChange(ec);
}
function addEntityChange(origEntityChange) {
function putEntityChange(origEntityChange) {
const ec = {...origEntityChange};
delete ec.id;
@@ -31,11 +32,11 @@ function addEntityChange(origEntityChange) {
maxEntityChangeId = Math.max(maxEntityChangeId, ec.id);
cls.addEntityChange(ec);
cls.putEntityChange(ec);
}
function addNoteReorderingEntityChange(parentNoteId, componentId) {
addEntityChange({
function putNoteReorderingEntityChange(parentNoteId, componentId) {
putEntityChange({
entityName: "note_reordering",
entityId: parentNoteId,
hash: 'N/A',
@@ -54,24 +55,24 @@ function addNoteReorderingEntityChange(parentNoteId, componentId) {
});
}
function moveEntityChangeToTop(entityName, entityId) {
const ec = sql.getRow(`SELECT * FROM entity_changes WHERE entityName = ? AND entityId = ?`, [entityName, entityId]);
addEntityChange(ec);
function putEntityChangeForOtherInstances(ec) {
putEntityChange({
...ec,
changeId: null,
instanceId: null
});
}
function addEntityChangesForSector(entityName, sector) {
const startTime = Date.now();
const entityChanges = sql.getRows(`SELECT * FROM entity_changes WHERE entityName = ? AND SUBSTR(entityId, 1, 1) = ?`, [entityName, sector]);
sql.transactional(() => {
for (const ec of entityChanges) {
addEntityChange(ec);
putEntityChange(ec);
}
});
log.info(`Added sector ${sector} of '${entityName}' to sync queue in ${Date.now() - startTime}ms.`);
log.info(`Added sector ${sector} of '${entityName}' (${entityChanges.length} entities) to the sync queue.`);
}
function cleanupEntityChangesForMissingEntities(entityName, entityPrimaryKey) {
@@ -88,52 +89,48 @@ function fillEntityChanges(entityName, entityPrimaryKey, condition = '') {
cleanupEntityChangesForMissingEntities(entityName, entityPrimaryKey);
sql.transactional(() => {
const entityIds = sql.getColumn(`SELECT ${entityPrimaryKey} FROM ${entityName}`
+ (condition ? ` WHERE ${condition}` : ''));
const entityIds = sql.getColumn(`SELECT ${entityPrimaryKey} FROM ${entityName} ${condition}`);
let createdCount = 0;
for (const entityId of entityIds) {
const existingRows = sql.getValue("SELECT COUNT(1) FROM entity_changes WHERE entityName = ? AND entityId = ?", [entityName, entityId]);
// we don't want to replace existing entities (which would effectively cause full resync)
if (existingRows === 0) {
createdCount++;
let hash;
let utcDateChanged;
let isSynced;
if (entityName === 'blobs') {
// FIXME: hacky, not sure if it might cause some problems
hash = "fake value";
utcDateChanged = dateUtils.utcNowDateTime();
isSynced = true; // contents are always synced
} else {
const entity = becca.getEntity(entityName, entityId);
if (entity) {
hash = entity?.generateHash() || "|deleted";
utcDateChanged = entity?.getUtcDateChanged() || dateUtils.utcNowDateTime();
isSynced = entityName !== 'options' || !!entity?.isSynced;
} else {
// entity might be null (not present in becca) when it's deleted
// FIXME: hacky, not sure if it might cause some problems
hash = "deleted";
utcDateChanged = dateUtils.utcNowDateTime();
isSynced = true; // deletable (the ones with isDeleted) entities are synced
}
}
addEntityChange({
entityName,
entityId,
hash: hash,
isErased: false,
utcDateChanged: utcDateChanged,
isSynced: isSynced
});
if (existingRows !== 0) {
// we don't want to replace existing entities (which would effectively cause full resync)
continue;
}
createdCount++;
const ec = {
entityName,
entityId,
isErased: false
};
if (entityName === 'blobs') {
const blob = sql.getRow("SELECT blobId, content, utcDateModified FROM blobs WHERE blobId = ?", [entityId]);
ec.hash = blobService.calculateContentHash(blob);
ec.utcDateChanged = blob.utcDateModified;
ec.isSynced = true; // blobs are always synced
} else {
const entity = becca.getEntity(entityName, entityId);
if (entity) {
ec.hash = entity.generateHash() || "|deleted";
ec.utcDateChanged = entity.getUtcDateChanged() || dateUtils.utcNowDateTime();
ec.isSynced = entityName !== 'options' || !!entity.isSynced;
} else {
// entity might be null (not present in becca) when it's deleted
// FIXME: hacky, not sure if it might cause some problems
ec.hash = "deleted";
ec.utcDateChanged = dateUtils.utcNowDateTime();
ec.isSynced = true; // deletable (the ones with isDeleted) entities are synced
}
}
putEntityChange(ec);
}
if (createdCount > 0) {
@@ -153,7 +150,7 @@ function fillAllEntityChanges() {
fillEntityChanges("blobs", "blobId");
fillEntityChanges("attributes", "attributeId");
fillEntityChanges("etapi_tokens", "etapiTokenId");
fillEntityChanges("options", "name", 'isSynced = 1');
fillEntityChanges("options", "name", 'WHERE isSynced = 1');
});
}
@@ -162,10 +159,10 @@ function recalculateMaxEntityChangeId() {
}
module.exports = {
addNoteReorderingEntityChange,
moveEntityChangeToTop,
addEntityChange,
addEntityChangeWithInstanceId,
putNoteReorderingEntityChange,
putEntityChangeForOtherInstances,
putEntityChange,
putEntityChangeWithInstanceId,
fillAllEntityChanges,
addEntityChangesForSector,
getMaxEntityChangeId: () => maxEntityChangeId,

187
src/services/erase.js Normal file
View File

@@ -0,0 +1,187 @@
const sql = require("./sql.js");
const revisionService = require("./revisions.js");
const log = require("./log.js");
const entityChangesService = require("./entity_changes.js");
const optionService = require("./options.js");
const dateUtils = require("./date_utils.js");
const sqlInit = require("./sql_init.js");
const cls = require("./cls.js");
function eraseNotes(noteIdsToErase) {
if (noteIdsToErase.length === 0) {
return;
}
sql.executeMany(`DELETE FROM notes WHERE noteId IN (???)`, noteIdsToErase);
setEntityChangesAsErased(sql.getManyRows(`SELECT * FROM entity_changes WHERE entityName = 'notes' AND entityId IN (???)`, noteIdsToErase));
// we also need to erase all "dependent" entities of the erased notes
const branchIdsToErase = sql.getManyRows(`SELECT branchId FROM branches WHERE noteId IN (???)`, noteIdsToErase)
.map(row => row.branchId);
eraseBranches(branchIdsToErase);
const attributeIdsToErase = sql.getManyRows(`SELECT attributeId FROM attributes WHERE noteId IN (???)`, noteIdsToErase)
.map(row => row.attributeId);
eraseAttributes(attributeIdsToErase);
const revisionIdsToErase = sql.getManyRows(`SELECT revisionId FROM revisions WHERE noteId IN (???)`, noteIdsToErase)
.map(row => row.revisionId);
revisionService.eraseRevisions(revisionIdsToErase);
log.info(`Erased notes: ${JSON.stringify(noteIdsToErase)}`);
}
function setEntityChangesAsErased(entityChanges) {
for (const ec of entityChanges) {
ec.isErased = true;
ec.utcDateChanged = dateUtils.utcNowDateTime();
entityChangesService.putEntityChange(ec);
}
}
function eraseBranches(branchIdsToErase) {
if (branchIdsToErase.length === 0) {
return;
}
sql.executeMany(`DELETE FROM branches WHERE branchId IN (???)`, branchIdsToErase);
setEntityChangesAsErased(sql.getManyRows(`SELECT * FROM entity_changes WHERE entityName = 'branches' AND entityId IN (???)`, branchIdsToErase));
log.info(`Erased branches: ${JSON.stringify(branchIdsToErase)}`);
}
function eraseAttributes(attributeIdsToErase) {
if (attributeIdsToErase.length === 0) {
return;
}
sql.executeMany(`DELETE FROM attributes WHERE attributeId IN (???)`, attributeIdsToErase);
setEntityChangesAsErased(sql.getManyRows(`SELECT * FROM entity_changes WHERE entityName = 'attributes' AND entityId IN (???)`, attributeIdsToErase));
log.info(`Erased attributes: ${JSON.stringify(attributeIdsToErase)}`);
}
function eraseAttachments(attachmentIdsToErase) {
if (attachmentIdsToErase.length === 0) {
return;
}
sql.executeMany(`DELETE FROM attachments WHERE attachmentId IN (???)`, attachmentIdsToErase);
setEntityChangesAsErased(sql.getManyRows(`SELECT * FROM entity_changes WHERE entityName = 'attachments' AND entityId IN (???)`, attachmentIdsToErase));
log.info(`Erased attachments: ${JSON.stringify(attachmentIdsToErase)}`);
}
function eraseUnusedBlobs() {
const unusedBlobIds = sql.getColumn(`
SELECT blobs.blobId
FROM blobs
LEFT JOIN notes ON notes.blobId = blobs.blobId
LEFT JOIN attachments ON attachments.blobId = blobs.blobId
LEFT JOIN revisions ON revisions.blobId = blobs.blobId
WHERE notes.noteId IS NULL
AND attachments.attachmentId IS NULL
AND revisions.revisionId IS NULL`);
if (unusedBlobIds.length === 0) {
return;
}
sql.executeMany(`DELETE FROM blobs WHERE blobId IN (???)`, unusedBlobIds);
// blobs are not marked as erased in entity_changes, they are just purged completely
// this is because technically every keystroke can create a new blob and there would be just too many
sql.executeMany(`DELETE FROM entity_changes WHERE entityName = 'blobs' AND entityId IN (???)`, unusedBlobIds);
log.info(`Erased unused blobs: ${JSON.stringify(unusedBlobIds)}`);
}
function eraseDeletedEntities(eraseEntitiesAfterTimeInSeconds = null) {
// this is important also so that the erased entity changes are sent to the connected clients
sql.transactional(() => {
if (eraseEntitiesAfterTimeInSeconds === null) {
eraseEntitiesAfterTimeInSeconds = optionService.getOptionInt('eraseEntitiesAfterTimeInSeconds');
}
const cutoffDate = new Date(Date.now() - eraseEntitiesAfterTimeInSeconds * 1000);
const noteIdsToErase = sql.getColumn("SELECT noteId FROM notes WHERE isDeleted = 1 AND utcDateModified <= ?", [dateUtils.utcDateTimeStr(cutoffDate)]);
eraseNotes(noteIdsToErase);
const branchIdsToErase = sql.getColumn("SELECT branchId FROM branches WHERE isDeleted = 1 AND utcDateModified <= ?", [dateUtils.utcDateTimeStr(cutoffDate)]);
eraseBranches(branchIdsToErase);
const attributeIdsToErase = sql.getColumn("SELECT attributeId FROM attributes WHERE isDeleted = 1 AND utcDateModified <= ?", [dateUtils.utcDateTimeStr(cutoffDate)]);
eraseAttributes(attributeIdsToErase);
const attachmentIdsToErase = sql.getColumn("SELECT attachmentId FROM attachments WHERE isDeleted = 1 AND utcDateModified <= ?", [dateUtils.utcDateTimeStr(cutoffDate)]);
eraseAttachments(attachmentIdsToErase);
eraseUnusedBlobs();
});
}
function eraseNotesWithDeleteId(deleteId) {
const noteIdsToErase = sql.getColumn("SELECT noteId FROM notes WHERE isDeleted = 1 AND deleteId = ?", [deleteId]);
eraseNotes(noteIdsToErase);
const branchIdsToErase = sql.getColumn("SELECT branchId FROM branches WHERE isDeleted = 1 AND deleteId = ?", [deleteId]);
eraseBranches(branchIdsToErase);
const attributeIdsToErase = sql.getColumn("SELECT attributeId FROM attributes WHERE isDeleted = 1 AND deleteId = ?", [deleteId]);
eraseAttributes(attributeIdsToErase);
const attachmentIdsToErase = sql.getColumn("SELECT attachmentId FROM attachments WHERE isDeleted = 1 AND deleteId = ?", [deleteId]);
eraseAttachments(attachmentIdsToErase);
eraseUnusedBlobs();
}
function eraseDeletedNotesNow() {
eraseDeletedEntities(0);
}
function eraseUnusedAttachmentsNow() {
eraseScheduledAttachments(0);
}
function eraseScheduledAttachments(eraseUnusedAttachmentsAfterSeconds = null) {
if (eraseUnusedAttachmentsAfterSeconds === null) {
eraseUnusedAttachmentsAfterSeconds = optionService.getOptionInt('eraseUnusedAttachmentsAfterSeconds');
}
const cutOffDate = dateUtils.utcDateTimeStr(new Date(Date.now() - (eraseUnusedAttachmentsAfterSeconds * 1000)));
const attachmentIdsToErase = sql.getColumn('SELECT attachmentId FROM attachments WHERE utcDateScheduledForErasureSince < ?', [cutOffDate]);
eraseAttachments(attachmentIdsToErase);
}
sqlInit.dbReady.then(() => {
// first cleanup kickoff 5 minutes after startup
setTimeout(cls.wrap(() => eraseDeletedEntities()), 5 * 60 * 1000);
setTimeout(cls.wrap(() => eraseScheduledAttachments()), 6 * 60 * 1000);
setInterval(cls.wrap(() => eraseDeletedEntities()), 4 * 3600 * 1000);
setInterval(cls.wrap(() => eraseScheduledAttachments()), 3600 * 1000);
});
module.exports = {
eraseDeletedNotesNow,
eraseUnusedAttachmentsNow,
eraseNotesWithDeleteId,
eraseUnusedBlobs
};

View File

@@ -18,7 +18,7 @@ async function migrate() {
// backup before attempting migration
await backupService.backupNow(
// special name for the pre-0.60 migration to prevent later overwrite
// creating a special backup for versions 0.60.X and older, the changes in 0.61 are major.
currentDbVersion < 214
? `before-migration-v${currentDbVersion}`
: 'before-migration'
@@ -72,6 +72,9 @@ async function migrate() {
}
}
});
log.info("VACUUMing database, this might take a while ...");
sql.execute("VACUUM");
}
function executeMigration(mig) {

View File

@@ -265,7 +265,7 @@ function createNewNoteWithTarget(target, targetBranchId, params) {
const retObject = createNewNote(params);
entityChangesService.addNoteReorderingEntityChange(params.parentNoteId);
entityChangesService.putNoteReorderingEntityChange(params.parentNoteId);
return retObject;
}
@@ -855,158 +855,6 @@ async function asyncPostProcessContent(note, content) {
scanForLinks(note, content);
}
function eraseNotes(noteIdsToErase) {
if (noteIdsToErase.length === 0) {
return;
}
sql.executeMany(`DELETE FROM notes WHERE noteId IN (???)`, noteIdsToErase);
setEntityChangesAsErased(sql.getManyRows(`SELECT * FROM entity_changes WHERE entityName = 'notes' AND entityId IN (???)`, noteIdsToErase));
// we also need to erase all "dependent" entities of the erased notes
const branchIdsToErase = sql.getManyRows(`SELECT branchId FROM branches WHERE noteId IN (???)`, noteIdsToErase)
.map(row => row.branchId);
eraseBranches(branchIdsToErase);
const attributeIdsToErase = sql.getManyRows(`SELECT attributeId FROM attributes WHERE noteId IN (???)`, noteIdsToErase)
.map(row => row.attributeId);
eraseAttributes(attributeIdsToErase);
const revisionIdsToErase = sql.getManyRows(`SELECT revisionId FROM revisions WHERE noteId IN (???)`, noteIdsToErase)
.map(row => row.revisionId);
revisionService.eraseRevisions(revisionIdsToErase);
log.info(`Erased notes: ${JSON.stringify(noteIdsToErase)}`);
}
function setEntityChangesAsErased(entityChanges) {
for (const ec of entityChanges) {
ec.isErased = true;
entityChangesService.addEntityChange(ec);
}
}
function eraseBranches(branchIdsToErase) {
if (branchIdsToErase.length === 0) {
return;
}
sql.executeMany(`DELETE FROM branches WHERE branchId IN (???)`, branchIdsToErase);
setEntityChangesAsErased(sql.getManyRows(`SELECT * FROM entity_changes WHERE entityName = 'branches' AND entityId IN (???)`, branchIdsToErase));
log.info(`Erased branches: ${JSON.stringify(branchIdsToErase)}`);
}
function eraseAttributes(attributeIdsToErase) {
if (attributeIdsToErase.length === 0) {
return;
}
sql.executeMany(`DELETE FROM attributes WHERE attributeId IN (???)`, attributeIdsToErase);
setEntityChangesAsErased(sql.getManyRows(`SELECT * FROM entity_changes WHERE entityName = 'attributes' AND entityId IN (???)`, attributeIdsToErase));
log.info(`Erased attributes: ${JSON.stringify(attributeIdsToErase)}`);
}
function eraseAttachments(attachmentIdsToErase) {
if (attachmentIdsToErase.length === 0) {
return;
}
sql.executeMany(`DELETE FROM attachments WHERE attachmentId IN (???)`, attachmentIdsToErase);
setEntityChangesAsErased(sql.getManyRows(`SELECT * FROM entity_changes WHERE entityName = 'attachments' AND entityId IN (???)`, attachmentIdsToErase));
log.info(`Erased attachments: ${JSON.stringify(attachmentIdsToErase)}`);
}
function eraseUnusedBlobs() {
// this method is rather defense in depth - in normal operation, the unused blobs should be erased immediately
// after getting unused (handled in entity._setContent())
const unusedBlobIds = sql.getColumn(`
SELECT blobs.blobId
FROM blobs
LEFT JOIN notes ON notes.blobId = blobs.blobId
LEFT JOIN attachments ON attachments.blobId = blobs.blobId
LEFT JOIN revisions ON revisions.blobId = blobs.blobId
WHERE notes.noteId IS NULL
AND attachments.attachmentId IS NULL
AND revisions.revisionId IS NULL`);
if (unusedBlobIds.length === 0) {
return;
}
sql.executeMany(`DELETE FROM blobs WHERE blobId IN (???)`, unusedBlobIds);
setEntityChangesAsErased(sql.getManyRows(`SELECT * FROM entity_changes WHERE entityName = 'blobs' AND entityId IN (???)`, unusedBlobIds));
log.info(`Erased unused blobs: ${JSON.stringify(unusedBlobIds)}`);
}
function eraseDeletedEntities(eraseEntitiesAfterTimeInSeconds = null) {
// this is important also so that the erased entity changes are sent to the connected clients
sql.transactional(() => {
if (eraseEntitiesAfterTimeInSeconds === null) {
eraseEntitiesAfterTimeInSeconds = optionService.getOptionInt('eraseEntitiesAfterTimeInSeconds');
}
const cutoffDate = new Date(Date.now() - eraseEntitiesAfterTimeInSeconds * 1000);
const noteIdsToErase = sql.getColumn("SELECT noteId FROM notes WHERE isDeleted = 1 AND utcDateModified <= ?", [dateUtils.utcDateTimeStr(cutoffDate)]);
eraseNotes(noteIdsToErase);
const branchIdsToErase = sql.getColumn("SELECT branchId FROM branches WHERE isDeleted = 1 AND utcDateModified <= ?", [dateUtils.utcDateTimeStr(cutoffDate)]);
eraseBranches(branchIdsToErase);
const attributeIdsToErase = sql.getColumn("SELECT attributeId FROM attributes WHERE isDeleted = 1 AND utcDateModified <= ?", [dateUtils.utcDateTimeStr(cutoffDate)]);
eraseAttributes(attributeIdsToErase);
const attachmentIdsToErase = sql.getColumn("SELECT attachmentId FROM attachments WHERE isDeleted = 1 AND utcDateModified <= ?", [dateUtils.utcDateTimeStr(cutoffDate)]);
eraseAttachments(attachmentIdsToErase);
eraseUnusedBlobs();
});
}
function eraseNotesWithDeleteId(deleteId) {
const noteIdsToErase = sql.getColumn("SELECT noteId FROM notes WHERE isDeleted = 1 AND deleteId = ?", [deleteId]);
eraseNotes(noteIdsToErase);
const branchIdsToErase = sql.getColumn("SELECT branchId FROM branches WHERE isDeleted = 1 AND deleteId = ?", [deleteId]);
eraseBranches(branchIdsToErase);
const attributeIdsToErase = sql.getColumn("SELECT attributeId FROM attributes WHERE isDeleted = 1 AND deleteId = ?", [deleteId]);
eraseAttributes(attributeIdsToErase);
const attachmentIdsToErase = sql.getColumn("SELECT attachmentId FROM attachments WHERE isDeleted = 1 AND deleteId = ?", [deleteId]);
eraseAttachments(attachmentIdsToErase);
eraseUnusedBlobs();
}
function eraseDeletedNotesNow() {
eraseDeletedEntities(0);
}
function eraseUnusedAttachmentsNow() {
eraseScheduledAttachments(0);
}
// all keys should be replaced by the corresponding values
function replaceByMap(str, mapObj) {
const re = new RegExp(Object.keys(mapObj).join("|"),"g");
@@ -1138,26 +986,6 @@ function getNoteIdMapping(origNote) {
return noteIdMapping;
}
function eraseScheduledAttachments(eraseUnusedAttachmentsAfterSeconds = null) {
if (eraseUnusedAttachmentsAfterSeconds === null) {
eraseUnusedAttachmentsAfterSeconds = optionService.getOptionInt('eraseUnusedAttachmentsAfterSeconds');
}
const cutOffDate = dateUtils.utcDateTimeStr(new Date(Date.now() - (eraseUnusedAttachmentsAfterSeconds * 1000)));
const attachmentIdsToErase = sql.getColumn('SELECT attachmentId FROM attachments WHERE utcDateScheduledForErasureSince < ?', [cutOffDate]);
eraseAttachments(attachmentIdsToErase);
}
sqlInit.dbReady.then(() => {
// first cleanup kickoff 5 minutes after startup
setTimeout(cls.wrap(() => eraseDeletedEntities()), 5 * 60 * 1000);
setTimeout(cls.wrap(() => eraseScheduledAttachments()), 6 * 60 * 1000);
setInterval(cls.wrap(() => eraseDeletedEntities()), 4 * 3600 * 1000);
setInterval(cls.wrap(() => eraseScheduledAttachments()), 3600 * 1000);
});
module.exports = {
createNewNote,
createNewNoteWithTarget,
@@ -1168,9 +996,6 @@ module.exports = {
duplicateSubtreeWithoutRoot,
getUndeletedParentBranchIds,
triggerNoteTitleChanged,
eraseDeletedNotesNow,
eraseUnusedAttachmentsNow,
eraseNotesWithDeleteId,
saveRevisionIfNeeded,
downloadImages,
asyncPostProcessContent

View File

@@ -3,6 +3,7 @@
const log = require('./log');
const sql = require('./sql');
const protectedSessionService = require("./protected_session");
const dateUtils = require("./date_utils");
/**
* @param {BNote} note
@@ -40,7 +41,7 @@ function eraseRevisions(revisionIdsToErase) {
log.info(`Removing note revisions: ${JSON.stringify(revisionIdsToErase)}`);
sql.executeMany(`DELETE FROM revisions WHERE revisionId IN (???)`, revisionIdsToErase);
sql.executeMany(`UPDATE entity_changes SET isErased = 1 WHERE entityName = 'revisions' AND entityId IN (???)`, revisionIdsToErase);
sql.executeMany(`UPDATE entity_changes SET isErased = 1, utcDateChanged = '${dateUtils.utcNowDateTime()}' WHERE entityName = 'revisions' AND entityId IN (???)`, revisionIdsToErase);
}
module.exports = {

View File

@@ -4,7 +4,7 @@ const log = require('./log');
const sql = require('./sql');
const optionService = require('./options');
const utils = require('./utils');
const instanceId = require('./member_id');
const instanceId = require('./instance_id');
const dateUtils = require('./date_utils');
const syncUpdateService = require('./sync_update');
const contentHashService = require('./content_hash');
@@ -54,13 +54,12 @@ async function sync() {
});
}
catch (e) {
// we're dynamically switching whether we're using proxy or not based on whether we encountered error with the current method
proxyToggle = !proxyToggle;
if (e.message &&
(e.message.includes('ECONNREFUSED') ||
e.message.includes('ERR_CONNECTION_REFUSED') ||
e.message.includes('ERR_ADDRESS_UNREACHABLE') ||
e.message.includes('Bad Gateway'))) {
if (e.message?.includes('ECONNREFUSED') ||
e.message?.includes('ERR_') || // node network errors
e.message?.includes('Bad Gateway')) {
ws.syncFailed();
@@ -109,7 +108,7 @@ async function doLogin() {
});
if (resp.instanceId === instanceId) {
throw new Error(`Sync server has member ID '${resp.instanceId}' which is also local. This usually happens when the sync client is (mis)configured to sync with itself (URL points back to client) instead of the correct sync server.`);
throw new Error(`Sync server has instance ID '${resp.instanceId}' which is also local. This usually happens when the sync client is (mis)configured to sync with itself (URL points back to client) instead of the correct sync server.`);
}
syncContext.instanceId = resp.instanceId;
@@ -147,17 +146,19 @@ async function pullChanges(syncContext) {
sql.transactional(() => {
for (const {entityChange, entity} of entityChanges) {
const changeAppliedAlready = entityChange.changeId
&& !!sql.getValue("SELECT id FROM entity_changes WHERE changeId = ?", [entityChange.changeId]);
&& !!sql.getValue("SELECT 1 FROM entity_changes WHERE changeId = ?", [entityChange.changeId]);
if (!changeAppliedAlready) {
if (!atLeastOnePullApplied) { // send only for first
ws.syncPullInProgress();
atLeastOnePullApplied = true;
}
syncUpdateService.updateEntity(entityChange, entity, syncContext.instanceId);
if (changeAppliedAlready) {
continue;
}
if (!atLeastOnePullApplied) { // send only for first
ws.syncPullInProgress();
atLeastOnePullApplied = true;
}
syncUpdateService.updateEntity(entityChange, entity, syncContext.instanceId);
}
if (lastSyncedPull !== lastEntityChangeId) {
@@ -255,7 +256,7 @@ async function checkContentHash(syncContext) {
const failedChecks = contentHashService.checkContentHashes(resp.entityHashes);
if (failedChecks.length > 0) {
// before requeuing sectors, make sure the entity changes are correct
// before re-queuing sectors, make sure the entity changes are correct
const consistencyChecks = require("./consistency_checks");
consistencyChecks.runEntityChangesChecks();
@@ -352,7 +353,8 @@ function getEntityChangeRecords(entityChanges) {
length += JSON.stringify(record).length;
if (length > 1000000) {
if (length > 1_000_000) {
// each sync request/response should have at most ~1 MB.
break;
}
}

View File

@@ -4,106 +4,85 @@ const entityChangesService = require('./entity_changes');
const eventService = require('./events');
const entityConstructor = require("../becca/entity_constructor");
function updateEntity(entityChange, entityRow, instanceId) {
// can be undefined for options with isSynced=false
if (!entityRow) {
if (entityChange.isSynced) {
if (entityChange.isErased) {
eraseEntity(entityChange, instanceId);
}
else {
log.info(`Encountered synced non-erased entity change without entity: ${JSON.stringify(entityChange)}`);
}
}
else if (entityChange.entityName !== 'options') {
log.info(`Encountered unsynced non-option entity change without entity: ${JSON.stringify(entityChange)}`);
}
return;
function updateEntity(remoteEC, remoteEntityRow, instanceId) {
if (!remoteEntityRow && remoteEC.entityName === 'options') {
return; // can be undefined for options with isSynced=false
}
const updated = entityChange.entityName === 'note_reordering'
? updateNoteReordering(entityChange, entityRow, instanceId)
: updateNormalEntity(entityChange, entityRow, instanceId);
const updated = remoteEC.entityName === 'note_reordering'
? updateNoteReordering(remoteEC, remoteEntityRow, instanceId)
: updateNormalEntity(remoteEC, remoteEntityRow, instanceId);
if (updated) {
if (entityRow.isDeleted) {
if (remoteEntityRow?.isDeleted) {
eventService.emit(eventService.ENTITY_DELETE_SYNCED, {
entityName: entityChange.entityName,
entityId: entityChange.entityId
entityName: remoteEC.entityName,
entityId: remoteEC.entityId
});
}
else if (!entityChange.isErased) {
else if (!remoteEC.isErased) {
eventService.emit(eventService.ENTITY_CHANGE_SYNCED, {
entityName: entityChange.entityName,
entityRow
entityName: remoteEC.entityName,
entityRow: remoteEntityRow
});
}
}
}
function updateNormalEntity(remoteEntityChange, remoteEntityRow, instanceId) {
const localEntityChange = sql.getRow(`
SELECT utcDateChanged, hash, isErased
FROM entity_changes
WHERE entityName = ? AND entityId = ?`, [remoteEntityChange.entityName, remoteEntityChange.entityId]);
function updateNormalEntity(remoteEC, remoteEntityRow, instanceId) {
const localEC = sql.getRow(`SELECT * FROM entity_changes WHERE entityName = ? AND entityId = ?`, [remoteEC.entityName, remoteEC.entityId]);
if (localEntityChange && !localEntityChange.isErased && remoteEntityChange.isErased) {
sql.transactional(() => {
const primaryKey = entityConstructor.getEntityFromEntityName(remoteEntityChange.entityName).primaryKeyName;
sql.execute(`DELETE FROM ${remoteEntityChange.entityName} WHERE ${primaryKey} = ?`, remoteEntityChange.entityId);
entityChangesService.addEntityChangeWithInstanceId(remoteEntityChange, instanceId);
});
if (!localEC?.isErased && remoteEC.isErased) {
eraseEntity(remoteEC, instanceId);
return true;
} else if (localEC?.isErased && !remoteEC.isErased) {
// on this side, we can't unerase the entity, so force the entity to be erased on the other side.
entityChangesService.putEntityChangeForOtherInstances(localEC);
return false;
}
if (!localEntityChange
|| localEntityChange.utcDateChanged < remoteEntityChange.utcDateChanged
|| localEntityChange.hash !== remoteEntityChange.hash // sync error, we should still update
if (!localEC
|| localEC.utcDateChanged < remoteEC.utcDateChanged
|| (localEC.utcDateChanged === remoteEC.utcDateChanged && localEC.hash !== remoteEC.hash) // sync error, we should still update
) {
if (remoteEntityChange.entityName === 'blobs') {
remoteEntityRow.content = handleContent(remoteEntityRow.content);
if (remoteEC.entityName === 'blobs' && remoteEntityRow.content !== null) {
// we always use a Buffer object which is different from normal saving - there we use a simple string type for
// "string notes". The problem is that in general, it's not possible to detect whether a blob content
// is string note or note (syncs can arrive out of order)
remoteEntityRow.content = Buffer.from(remoteEntityRow.content, 'base64');
if (remoteEntityRow.content.byteLength === 0) {
// there seems to be a bug which causes empty buffer to be stored as NULL which is then picked up as inconsistency
// (possibly not a problem anymore with the newer better-sqlite3)
remoteEntityRow.content = "";
}
}
sql.transactional(() => {
sql.replace(remoteEntityChange.entityName, remoteEntityRow);
sql.replace(remoteEC.entityName, remoteEntityRow);
entityChangesService.addEntityChangeWithInstanceId(remoteEntityChange, instanceId);
});
entityChangesService.putEntityChangeWithInstanceId(remoteEC, instanceId);
return true;
} else if (localEC.hash !== remoteEC.hash && localEC.utcDateChanged > remoteEC.utcDateChanged) {
// the change on our side is newer than on the other side, so the other side should update
entityChangesService.putEntityChangeForOtherInstances(localEC);
return false;
}
return false;
}
function updateNoteReordering(entityChange, entity, instanceId) {
sql.transactional(() => {
for (const key in entity) {
sql.execute("UPDATE branches SET notePosition = ? WHERE branchId = ?", [entity[key], key]);
}
entityChangesService.addEntityChangeWithInstanceId(entityChange, instanceId);
});
return true;
}
function handleContent(content) {
// we always use a Buffer object which is different from normal saving - there we use a simple string type for
// "string notes". The problem is that in general, it's not possible to detect whether a blob content
// is string note or note (syncs can arrive out of order)
content = content === null ? null : Buffer.from(content, 'base64');
if (content && content.byteLength === 0) {
// there seems to be a bug which causes empty buffer to be stored as NULL which is then picked up as inconsistency
content = "";
function updateNoteReordering(remoteEC, remoteEntityRow, instanceId) {
for (const key in remoteEntityRow) {
sql.execute("UPDATE branches SET notePosition = ? WHERE branchId = ?", [remoteEntityRow[key], key]);
}
return content;
entityChangesService.putEntityChangeWithInstanceId(remoteEC, instanceId);
return true;
}
function eraseEntity(entityChange, instanceId) {
@@ -115,21 +94,19 @@ function eraseEntity(entityChange, instanceId) {
"attributes",
"revisions",
"attachments",
"blobs",
"blobs"
];
if (!entityNames.includes(entityName)) {
log.error(`Cannot erase entity '${entityName}', id '${entityId}'`);
log.error(`Cannot erase entity '${entityName}', id '${entityId}'.`);
return;
}
const keyName = entityConstructor.getEntityFromEntityName(entityName).primaryKeyName;
const primaryKeyName = entityConstructor.getEntityFromEntityName(entityName).primaryKeyName;
sql.execute(`DELETE FROM ${entityName} WHERE ${keyName} = ?`, [entityId]);
sql.execute(`DELETE FROM ${entityName} WHERE ${primaryKeyName} = ?`, [entityId]);
eventService.emit(eventService.ENTITY_DELETE_SYNCED, { entityName, entityId });
entityChangesService.addEntityChangeWithInstanceId(entityChange, instanceId);
entityChangesService.putEntityChangeWithInstanceId(entityChange, instanceId);
}
module.exports = {

View File

@@ -165,7 +165,7 @@ function sortNotes(parentNoteId, customSortBy = 'title', reverse = false, folder
}
if (someBranchUpdated) {
entityChangesService.addNoteReorderingEntityChange(parentNoteId);
entityChangesService.putNoteReorderingEntityChange(parentNoteId);
}
});
}

View File

@@ -28,8 +28,13 @@ function hashedBlobId(content) {
// sha512 is faster than sha256
const base64Hash = crypto.createHash('sha512').update(content).digest('base64');
// 20 characters of base64 gives us 120 bit of entropy which is plenty enough
return base64Hash.substr(0, 20);
// we don't want such + and / in the IDs
const kindaBase62Hash = base64Hash
.replace('+', 'X')
.replace('/', 'Y');
// 20 characters of base62 gives us ~120 bit of entropy which is plenty enough
return kindaBase62Hash.substr(0, 20);
}
function toBase64(plainText) {