mirror of
https://github.com/zadam/trilium.git
synced 2025-11-05 04:45:47 +01:00
Compare commits
32 Commits
v0.61.2-be
...
v0.61.5-be
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
515c5411a6 | ||
|
|
3f7a5504c7 | ||
|
|
8c7c37cf98 | ||
|
|
c0f48c0e99 | ||
|
|
abedf2bba4 | ||
|
|
bb0137b2fd | ||
|
|
6c54c7d17d | ||
|
|
90255ac55b | ||
|
|
e741c2826c | ||
|
|
026992db78 | ||
|
|
33780c1e17 | ||
|
|
ede9c43f67 | ||
|
|
5c12ac4eee | ||
|
|
522518cf0d | ||
|
|
1d869d25c2 | ||
|
|
a9cdd93cb4 | ||
|
|
4240da349d | ||
|
|
c257bc07a8 | ||
|
|
00eaa16985 | ||
|
|
fefb059564 | ||
|
|
9166765ced | ||
|
|
6ae7661603 | ||
|
|
30e75056bd | ||
|
|
530e56dcb5 | ||
|
|
63675bfbae | ||
|
|
696ce38083 | ||
|
|
12014b9f4d | ||
|
|
e8b52f9e6c | ||
|
|
04b125afc0 | ||
|
|
2a7fe85020 | ||
|
|
119050e355 | ||
|
|
72122d0f95 |
@@ -1,11 +1,13 @@
|
||||
//https://prettier.io/docs/en/options.html
|
||||
//https://prettier.io/docs/en/options.html
|
||||
module.exports = {
|
||||
semi: true,
|
||||
trailingComma: 'es5',
|
||||
trailingComma: 'none',
|
||||
singleQuote: true,
|
||||
printWidth: 120,
|
||||
printWidth: 100,
|
||||
tabWidth: 4,
|
||||
// useTabs: false,
|
||||
// bracketSpacing: true,
|
||||
useTabs: false,
|
||||
quoteProps: "as-needed",
|
||||
bracketSpacing: true,
|
||||
arrowParens: "avoid"
|
||||
// htmlWhitespaceSensitivity: 'ignore',
|
||||
};
|
||||
|
||||
1
db/migrations/0223__NOOP.sql
Normal file
1
db/migrations/0223__NOOP.sql
Normal file
@@ -0,0 +1 @@
|
||||
SELECT 1;
|
||||
@@ -1,3 +0,0 @@
|
||||
CREATE INDEX IDX_notes_blobId on notes (blobId);
|
||||
CREATE INDEX IDX_revisions_blobId on revisions (blobId);
|
||||
CREATE INDEX IDX_attachments_blobId on attachments (blobId);
|
||||
14
db/migrations/0224__fix_blobIds.sql
Normal file
14
db/migrations/0224__fix_blobIds.sql
Normal 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';
|
||||
3
db/migrations/0225__create_blobId_indices.sql
Normal file
3
db/migrations/0225__create_blobId_indices.sql
Normal 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);
|
||||
@@ -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#line244">line 244</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#line246">line 246</a>
|
||||
</li></ul></dd>
|
||||
|
||||
|
||||
@@ -394,90 +394,6 @@
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h4 class="name" id="addEntityChange"><span class="type-signature">(protected) </span>addEntityChange<span class="signature">()</span><span class="type-signature"></span></h4>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<dl class="details">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<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#line64">line 64</a>
|
||||
</li></ul></dd>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</dl>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1022,7 +938,91 @@ 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#line261">line 261</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#line263">line 263</a>
|
||||
</li></ul></dd>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</dl>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h4 class="name" id="putEntityChange"><span class="type-signature">(protected) </span>putEntityChange<span class="signature">()</span><span class="type-signature"></span></h4>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<dl class="details">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<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#line64">line 64</a>
|
||||
</li></ul></dd>
|
||||
|
||||
|
||||
|
||||
@@ -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#line244">line 244</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#line246">line 246</a>
|
||||
</li></ul></dd>
|
||||
|
||||
|
||||
@@ -965,95 +965,6 @@ and relation (representing named relationship between source and target note)</d
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h4 class="name" id="addEntityChange"><span class="type-signature">(protected) </span>addEntityChange<span class="signature">()</span><span class="type-signature"></span></h4>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<dl class="details">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<dt class="tag-overrides">Overrides:</dt>
|
||||
<dd class="tag-overrides"><ul class="dummy"><li>
|
||||
<a href="AbstractBeccaEntity.html#addEntityChange">AbstractBeccaEntity#addEntityChange</a>
|
||||
</li></ul></dd>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<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#line64">line 64</a>
|
||||
</li></ul></dd>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</dl>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1940,7 +1851,96 @@ 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#line261">line 261</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#line263">line 263</a>
|
||||
</li></ul></dd>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</dl>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h4 class="name" id="putEntityChange"><span class="type-signature">(protected) </span>putEntityChange<span class="signature">()</span><span class="type-signature"></span></h4>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<dl class="details">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<dt class="tag-overrides">Overrides:</dt>
|
||||
<dd class="tag-overrides"><ul class="dummy"><li>
|
||||
<a href="AbstractBeccaEntity.html#putEntityChange">AbstractBeccaEntity#putEntityChange</a>
|
||||
</li></ul></dd>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<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#line64">line 64</a>
|
||||
</li></ul></dd>
|
||||
|
||||
|
||||
|
||||
@@ -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#line244">line 244</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#line246">line 246</a>
|
||||
</li></ul></dd>
|
||||
|
||||
|
||||
@@ -1085,95 +1085,6 @@ of deletion should not act as a clone.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h4 class="name" id="addEntityChange"><span class="type-signature">(protected) </span>addEntityChange<span class="signature">()</span><span class="type-signature"></span></h4>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<dl class="details">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<dt class="tag-overrides">Overrides:</dt>
|
||||
<dd class="tag-overrides"><ul class="dummy"><li>
|
||||
<a href="AbstractBeccaEntity.html#addEntityChange">AbstractBeccaEntity#addEntityChange</a>
|
||||
</li></ul></dd>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<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#line64">line 64</a>
|
||||
</li></ul></dd>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</dl>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -2054,7 +1965,96 @@ 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#line261">line 261</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#line263">line 263</a>
|
||||
</li></ul></dd>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</dl>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h4 class="name" id="putEntityChange"><span class="type-signature">(protected) </span>putEntityChange<span class="signature">()</span><span class="type-signature"></span></h4>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<dl class="details">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<dt class="tag-overrides">Overrides:</dt>
|
||||
<dd class="tag-overrides"><ul class="dummy"><li>
|
||||
<a href="AbstractBeccaEntity.html#putEntityChange">AbstractBeccaEntity#putEntityChange</a>
|
||||
</li></ul></dd>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<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#line64">line 64</a>
|
||||
</li></ul></dd>
|
||||
|
||||
|
||||
|
||||
@@ -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#line244">line 244</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#line246">line 246</a>
|
||||
</li></ul></dd>
|
||||
|
||||
|
||||
@@ -834,95 +834,6 @@ from tokenHash and token.</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h4 class="name" id="addEntityChange"><span class="type-signature">(protected) </span>addEntityChange<span class="signature">()</span><span class="type-signature"></span></h4>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<dl class="details">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<dt class="tag-overrides">Overrides:</dt>
|
||||
<dd class="tag-overrides"><ul class="dummy"><li>
|
||||
<a href="AbstractBeccaEntity.html#addEntityChange">AbstractBeccaEntity#addEntityChange</a>
|
||||
</li></ul></dd>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<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#line64">line 64</a>
|
||||
</li></ul></dd>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</dl>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1497,7 +1408,96 @@ 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#line261">line 261</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#line263">line 263</a>
|
||||
</li></ul></dd>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</dl>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h4 class="name" id="putEntityChange"><span class="type-signature">(protected) </span>putEntityChange<span class="signature">()</span><span class="type-signature"></span></h4>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<dl class="details">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<dt class="tag-overrides">Overrides:</dt>
|
||||
<dd class="tag-overrides"><ul class="dummy"><li>
|
||||
<a href="AbstractBeccaEntity.html#putEntityChange">AbstractBeccaEntity#putEntityChange</a>
|
||||
</li></ul></dd>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<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#line64">line 64</a>
|
||||
</li></ul></dd>
|
||||
|
||||
|
||||
|
||||
@@ -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#line244">line 244</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#line246">line 246</a>
|
||||
</li></ul></dd>
|
||||
|
||||
|
||||
@@ -1636,95 +1636,6 @@ See addLabel, addRelation for more specific methods.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h4 class="name" id="addEntityChange"><span class="type-signature">(protected) </span>addEntityChange<span class="signature">()</span><span class="type-signature"></span></h4>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<dl class="details">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<dt class="tag-overrides">Overrides:</dt>
|
||||
<dd class="tag-overrides"><ul class="dummy"><li>
|
||||
<a href="AbstractBeccaEntity.html#addEntityChange">AbstractBeccaEntity#addEntityChange</a>
|
||||
</li></ul></dd>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<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#line64">line 64</a>
|
||||
</li></ul></dd>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</dl>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -12325,7 +12236,96 @@ 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#line261">line 261</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#line263">line 263</a>
|
||||
</li></ul></dd>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</dl>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h4 class="name" id="putEntityChange"><span class="type-signature">(protected) </span>putEntityChange<span class="signature">()</span><span class="type-signature"></span></h4>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<dl class="details">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<dt class="tag-overrides">Overrides:</dt>
|
||||
<dd class="tag-overrides"><ul class="dummy"><li>
|
||||
<a href="AbstractBeccaEntity.html#putEntityChange">AbstractBeccaEntity#putEntityChange</a>
|
||||
</li></ul></dd>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<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#line64">line 64</a>
|
||||
</li></ul></dd>
|
||||
|
||||
|
||||
|
||||
@@ -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#line244">line 244</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#line246">line 246</a>
|
||||
</li></ul></dd>
|
||||
|
||||
|
||||
@@ -692,95 +692,6 @@
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h4 class="name" id="addEntityChange"><span class="type-signature">(protected) </span>addEntityChange<span class="signature">()</span><span class="type-signature"></span></h4>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<dl class="details">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<dt class="tag-overrides">Overrides:</dt>
|
||||
<dd class="tag-overrides"><ul class="dummy"><li>
|
||||
<a href="AbstractBeccaEntity.html#addEntityChange">AbstractBeccaEntity#addEntityChange</a>
|
||||
</li></ul></dd>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<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#line64">line 64</a>
|
||||
</li></ul></dd>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</dl>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1355,7 +1266,96 @@ 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#line261">line 261</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#line263">line 263</a>
|
||||
</li></ul></dd>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</dl>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h4 class="name" id="putEntityChange"><span class="type-signature">(protected) </span>putEntityChange<span class="signature">()</span><span class="type-signature"></span></h4>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<dl class="details">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<dt class="tag-overrides">Overrides:</dt>
|
||||
<dd class="tag-overrides"><ul class="dummy"><li>
|
||||
<a href="AbstractBeccaEntity.html#putEntityChange">AbstractBeccaEntity#putEntityChange</a>
|
||||
</li></ul></dd>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<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#line64">line 64</a>
|
||||
</li></ul></dd>
|
||||
|
||||
|
||||
|
||||
@@ -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#line244">line 244</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#line246">line 246</a>
|
||||
</li></ul></dd>
|
||||
|
||||
|
||||
@@ -624,95 +624,6 @@
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h4 class="name" id="addEntityChange"><span class="type-signature">(protected) </span>addEntityChange<span class="signature">()</span><span class="type-signature"></span></h4>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<dl class="details">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<dt class="tag-overrides">Overrides:</dt>
|
||||
<dd class="tag-overrides"><ul class="dummy"><li>
|
||||
<a href="AbstractBeccaEntity.html#addEntityChange">AbstractBeccaEntity#addEntityChange</a>
|
||||
</li></ul></dd>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<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#line64">line 64</a>
|
||||
</li></ul></dd>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</dl>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1287,7 +1198,96 @@ 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#line261">line 261</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#line263">line 263</a>
|
||||
</li></ul></dd>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</dl>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h4 class="name" id="putEntityChange"><span class="type-signature">(protected) </span>putEntityChange<span class="signature">()</span><span class="type-signature"></span></h4>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<dl class="details">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<dt class="tag-overrides">Overrides:</dt>
|
||||
<dd class="tag-overrides"><ul class="dummy"><li>
|
||||
<a href="AbstractBeccaEntity.html#putEntityChange">AbstractBeccaEntity#putEntityChange</a>
|
||||
</li></ul></dd>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<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#line64">line 64</a>
|
||||
</li></ul></dd>
|
||||
|
||||
|
||||
|
||||
@@ -89,8 +89,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),
|
||||
@@ -129,7 +129,7 @@ class AbstractBeccaEntity {
|
||||
return;
|
||||
}
|
||||
|
||||
this.addEntityChange(false);
|
||||
this.putEntityChange(false);
|
||||
|
||||
if (!cls.isEntityEventsDisabled()) {
|
||||
const eventPayload = {
|
||||
@@ -203,6 +203,8 @@ class AbstractBeccaEntity {
|
||||
}
|
||||
|
||||
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]);
|
||||
}
|
||||
|
||||
@@ -245,7 +247,7 @@ class AbstractBeccaEntity {
|
||||
// access to the decrypted content
|
||||
const hash = blobService.calculateContentHash(pojo);
|
||||
|
||||
entityChangesService.addEntityChange({
|
||||
entityChangesService.putEntityChange({
|
||||
entityName: 'blobs',
|
||||
entityId: newBlobId,
|
||||
hash: hash,
|
||||
@@ -305,7 +307,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 });
|
||||
}
|
||||
@@ -322,7 +324,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 });
|
||||
}
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
}
|
||||
|
||||
/*
|
||||
* CKEditor 5 (v38.0.1) content styles.
|
||||
* Generated on Thu, 25 May 2023 13:25:51 GMT.
|
||||
* CKEditor 5 (v39.0.1) content styles.
|
||||
* Generated on Thu, 10 Aug 2023 09:21:07 GMT.
|
||||
* For more information, check out https://ckeditor.com/docs/ckeditor5/latest/installation/advanced/content-styles.html
|
||||
*/
|
||||
|
||||
@@ -15,8 +15,8 @@
|
||||
--ck-color-image-caption-text: hsl(0, 0%, 20%);
|
||||
--ck-color-mention-background: hsla(341, 100%, 30%, 0.1);
|
||||
--ck-color-mention-text: hsl(341, 100%, 30%);
|
||||
--ck-color-table-caption-background: hsl(0, 0%, 97%);
|
||||
--ck-color-table-caption-text: hsl(0, 0%, 20%);
|
||||
--ck-color-selector-caption-background: hsl(0, 0%, 97%);
|
||||
--ck-color-selector-caption-text: hsl(0, 0%, 20%);
|
||||
--ck-highlight-marker-blue: hsl(201, 97%, 72%);
|
||||
--ck-highlight-marker-green: hsl(120, 93%, 68%);
|
||||
--ck-highlight-marker-pink: hsl(345, 96%, 73%);
|
||||
@@ -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-table/theme/table.css */
|
||||
.ck-content .table {
|
||||
margin: 0.9em auto;
|
||||
display: table;
|
||||
}
|
||||
/* @ckeditor/ckeditor5-font/theme/fontsize.css */
|
||||
.ck-content .text-tiny {
|
||||
font-size: .7em;
|
||||
/* @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-font/theme/fontsize.css */
|
||||
.ck-content .text-small {
|
||||
font-size: .85em;
|
||||
/* @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-font/theme/fontsize.css */
|
||||
.ck-content .text-big {
|
||||
font-size: 1.4em;
|
||||
/* @ckeditor/ckeditor5-table/theme/table.css */
|
||||
.ck-content .table table th {
|
||||
font-weight: bold;
|
||||
background: hsla(0, 0%, 0%, 5%);
|
||||
}
|
||||
/* @ckeditor/ckeditor5-font/theme/fontsize.css */
|
||||
.ck-content .text-huge {
|
||||
font-size: 1.8em;
|
||||
/* @ckeditor/ckeditor5-table/theme/table.css */
|
||||
.ck-content[dir="rtl"] .table th {
|
||||
text-align: right;
|
||||
}
|
||||
/* @ckeditor/ckeditor5-highlight/theme/highlight.css */
|
||||
.ck-content .marker-yellow {
|
||||
background-color: var(--ck-highlight-marker-yellow);
|
||||
/* @ckeditor/ckeditor5-table/theme/table.css */
|
||||
.ck-content[dir="ltr"] .table th {
|
||||
text-align: left;
|
||||
}
|
||||
/* @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-selector-caption-text);
|
||||
background-color: var(--ck-color-selector-caption-background);
|
||||
padding: .6em;
|
||||
font-size: .75em;
|
||||
outline-offset: -1px;
|
||||
}
|
||||
/* @ckeditor/ckeditor5-image/theme/image.css */
|
||||
.ck-content .image {
|
||||
display: table;
|
||||
/* @ckeditor/ckeditor5-page-break/theme/pagebreak.css */
|
||||
.ck-content .page-break {
|
||||
position: relative;
|
||||
clear: both;
|
||||
text-align: center;
|
||||
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 {
|
||||
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-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-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-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 {
|
||||
|
||||
2
libraries/ckeditor/ckeditor.js
vendored
2
libraries/ckeditor/ckeditor.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
379
libraries/mermaid.min.js
vendored
379
libraries/mermaid.min.js
vendored
File diff suppressed because one or more lines are too long
1034
package-lock.json
generated
1034
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
26
package.json
26
package.json
@@ -2,7 +2,7 @@
|
||||
"name": "trilium",
|
||||
"productName": "Trilium Notes",
|
||||
"description": "Trilium Notes",
|
||||
"version": "0.61.2-beta",
|
||||
"version": "0.61.5-beta",
|
||||
"license": "AGPL-3.0-only",
|
||||
"main": "electron.js",
|
||||
"bin": {
|
||||
@@ -31,9 +31,9 @@
|
||||
"prepare": "husky install || echo 'Husky install failed, expected on flatpak build'"
|
||||
},
|
||||
"dependencies": {
|
||||
"@braintree/sanitize-url": "6.0.2",
|
||||
"@braintree/sanitize-url": "6.0.4",
|
||||
"@electron/remote": "2.0.10",
|
||||
"@excalidraw/excalidraw": "0.15.2",
|
||||
"@excalidraw/excalidraw": "0.15.3",
|
||||
"archiver": "5.3.1",
|
||||
"async-mutex": "0.4.0",
|
||||
"axios": "1.4.0",
|
||||
@@ -53,7 +53,7 @@
|
||||
"escape-html": "1.0.3",
|
||||
"express": "4.18.2",
|
||||
"express-partial-content": "1.0.2",
|
||||
"express-rate-limit": "6.8.1",
|
||||
"express-rate-limit": "6.9.0",
|
||||
"express-session": "1.17.3",
|
||||
"fs-extra": "11.1.1",
|
||||
"helmet": "7.0.0",
|
||||
@@ -68,10 +68,10 @@
|
||||
"jimp": "0.22.10",
|
||||
"joplin-turndown-plugin-gfm": "1.0.12",
|
||||
"jsdom": "22.1.0",
|
||||
"marked": "5.1.2",
|
||||
"marked": "7.0.3",
|
||||
"mime-types": "2.1.35",
|
||||
"multer": "1.4.5-lts.1",
|
||||
"node-abi": "3.45.0",
|
||||
"node-abi": "3.46.0",
|
||||
"normalize-strings": "1.1.1",
|
||||
"open": "8.4.1",
|
||||
"rand-token": "1.0.1",
|
||||
@@ -97,14 +97,14 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"cross-env": "7.0.3",
|
||||
"electron": "25.3.2",
|
||||
"electron": "25.5.0",
|
||||
"electron-builder": "24.6.3",
|
||||
"electron-packager": "17.1.1",
|
||||
"electron-rebuild": "3.2.9",
|
||||
"eslint": "8.45.0",
|
||||
"eslint": "8.47.0",
|
||||
"eslint-config-airbnb-base": "15.0.0",
|
||||
"eslint-config-prettier": "8.9.0",
|
||||
"eslint-plugin-import": "2.27.5",
|
||||
"eslint-config-prettier": "9.0.0",
|
||||
"eslint-plugin-import": "2.28.0",
|
||||
"eslint-plugin-jsonc": "2.9.0",
|
||||
"eslint-plugin-prettier": "5.0.0",
|
||||
"esm": "3.2.25",
|
||||
@@ -112,11 +112,11 @@
|
||||
"jasmine": "5.1.0",
|
||||
"jsdoc": "4.0.2",
|
||||
"jsonc-eslint-parser": "2.3.0",
|
||||
"lint-staged": "13.2.3",
|
||||
"lint-staged": "14.0.0",
|
||||
"lorem-ipsum": "2.0.8",
|
||||
"nodemon": "3.0.1",
|
||||
"prettier": "3.0.0",
|
||||
"rcedit": "3.0.1",
|
||||
"prettier": "3.0.2",
|
||||
"rcedit": "3.1.0",
|
||||
"webpack": "5.88.2",
|
||||
"webpack-cli": "5.1.4"
|
||||
},
|
||||
|
||||
@@ -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 = {
|
||||
@@ -219,7 +219,7 @@ class AbstractBeccaEntity {
|
||||
// access to the decrypted content
|
||||
const hash = blobService.calculateContentHash(pojo);
|
||||
|
||||
entityChangesService.addEntityChange({
|
||||
entityChangesService.putEntityChange({
|
||||
entityName: 'blobs',
|
||||
entityId: newBlobId,
|
||||
hash: hash,
|
||||
@@ -279,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 });
|
||||
}
|
||||
@@ -296,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 });
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
|
||||
@@ -21,6 +21,7 @@ import NoteListWidget from "../widgets/note_list.js";
|
||||
import GlobalMenuWidget from "../widgets/buttons/global_menu.js";
|
||||
import LauncherContainer from "../widgets/containers/launcher_container.js";
|
||||
import RootContainer from "../widgets/containers/root_container.js";
|
||||
import SharedInfoWidget from "../widgets/shared_info.js";
|
||||
|
||||
const MOBILE_CSS = `
|
||||
<style>
|
||||
@@ -144,6 +145,7 @@ export default class MobileLayout {
|
||||
.css("top: 5px;")
|
||||
)
|
||||
.child(new CloseDetailButtonWidget().contentSized()))
|
||||
.child(new SharedInfoWidget())
|
||||
.child(new FloatingButtons()
|
||||
.child(new EditButton())
|
||||
.child(new RelationMapButtons())
|
||||
|
||||
@@ -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 }
|
||||
|
||||
@@ -31,7 +31,7 @@ async function getRenderedContent(entity, options = {}) {
|
||||
await renderText(entity, $renderedContent);
|
||||
}
|
||||
else if (type === 'code') {
|
||||
await renderCode(entity, options, $renderedContent);
|
||||
await renderCode(entity, $renderedContent);
|
||||
}
|
||||
else if (type === 'image') {
|
||||
renderImage(entity, $renderedContent, options);
|
||||
|
||||
@@ -148,6 +148,11 @@ function parseNavigationStateFromUrl(url) {
|
||||
|
||||
const hash = url.substr(hashIdx + 1); // strip also the initial '#'
|
||||
const [notePath, paramString] = hash.split("?");
|
||||
|
||||
if (!notePath.match(/^[_a-z0-9]{4,}(\/[_a-z0-9]{4,})*$/i)) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const viewScope = {
|
||||
viewMode: 'default'
|
||||
};
|
||||
|
||||
@@ -259,8 +259,6 @@ function init() {
|
||||
};
|
||||
|
||||
$.fn.setSelectedExternalLink = function (externalLink) {
|
||||
console.trace("setSelectedExternalLink");
|
||||
|
||||
if (externalLink) {
|
||||
$(this)
|
||||
.closest(".input-group")
|
||||
|
||||
@@ -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
|
||||
};
|
||||
|
||||
@@ -37,7 +37,9 @@ export default class NoteListWidget extends NoteContextAwareWidget {
|
||||
threshold: 0.1
|
||||
});
|
||||
|
||||
observer.observe(this.$widget[0]);
|
||||
// there seems to be a race condition on Firefox which triggers the observer only before the widget is visible
|
||||
// (intersection is false). https://github.com/zadam/trilium/issues/4165
|
||||
setTimeout(() => observer.observe(this.$widget[0]), 10);
|
||||
}
|
||||
|
||||
checkRenderStatus() {
|
||||
|
||||
@@ -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');
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ import linkService from "../../services/link.js";
|
||||
import server from "../../services/server.js";
|
||||
import froca from "../../services/froca.js";
|
||||
import NoteContextAwareWidget from "../note_context_aware_widget.js";
|
||||
import options from "../../services/options.js";
|
||||
|
||||
const TPL = `
|
||||
<div class="edited-notes-widget">
|
||||
@@ -34,7 +35,9 @@ export default class EditedNotesWidget extends NoteContextAwareWidget {
|
||||
return {
|
||||
show: this.isEnabled(),
|
||||
// promoted attributes have priority over edited notes
|
||||
activate: this.note.getPromotedDefinitionAttributes().length === 0,
|
||||
activate:
|
||||
(this.note.getPromotedDefinitionAttributes().length === 0 || !options.is('promotedAttributesOpenInRibbon'))
|
||||
&& options.is('editedNotesOpenInRibbon'),
|
||||
title: 'Edited Notes',
|
||||
icon: 'bx bx-calendar-edit'
|
||||
};
|
||||
|
||||
@@ -4,6 +4,7 @@ import treeService from "../../services/tree.js";
|
||||
import noteAutocompleteService from "../../services/note_autocomplete.js";
|
||||
import NoteContextAwareWidget from "../note_context_aware_widget.js";
|
||||
import attributeService from "../../services/attributes.js";
|
||||
import options from "../../services/options.js";
|
||||
|
||||
const TPL = `
|
||||
<div>
|
||||
@@ -62,7 +63,7 @@ export default class PromotedAttributesWidget extends NoteContextAwareWidget {
|
||||
|
||||
return {
|
||||
show: true,
|
||||
activate: true,
|
||||
activate: options.is('promotedAttributesOpenInRibbon'),
|
||||
title: "Promoted Attributes",
|
||||
icon: "bx bx-table"
|
||||
};
|
||||
|
||||
@@ -31,6 +31,7 @@ import VacuumDatabaseOptions from "./options/advanced/vacuum_database.js";
|
||||
import DatabaseAnonymizationOptions from "./options/advanced/database_anonymization.js";
|
||||
import BackendLogWidget from "./content/backend_log.js";
|
||||
import AttachmentErasureTimeoutOptions from "./options/other/attachment_erasure_timeout.js";
|
||||
import RibbonOptions from "./options/appearance/ribbon.js";
|
||||
|
||||
const TPL = `<div class="note-detail-content-widget note-detail-printable">
|
||||
<style>
|
||||
@@ -57,7 +58,8 @@ const CONTENT_WIDGETS = {
|
||||
FontsOptions,
|
||||
ZoomFactorOptions,
|
||||
NativeTitleBarOptions,
|
||||
MaxContentWidthOptions
|
||||
MaxContentWidthOptions,
|
||||
RibbonOptions
|
||||
],
|
||||
_optionsShortcuts: [ KeyboardShortcutsOptions ],
|
||||
_optionsTextNotes: [
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
import OptionsWidget from "../options_widget.js";
|
||||
|
||||
const TPL = `
|
||||
<div class="options-section">
|
||||
<h4>Ribbon widgets</h4>
|
||||
<label>
|
||||
<input type="checkbox" class="promoted-attributes-open-in-ribbon">
|
||||
Promoted Attributes ribbon tab will automatically open if promoted attributes are present on the note
|
||||
</label>
|
||||
|
||||
<label>
|
||||
<input type="checkbox" class="edited-notes-open-in-ribbon">
|
||||
Edited Notes ribbon tab will automatically open on day notes
|
||||
</label>
|
||||
</div>`;
|
||||
|
||||
export default class RibbonOptions extends OptionsWidget {
|
||||
doRender() {
|
||||
this.$widget = $(TPL);
|
||||
|
||||
this.$promotedAttributesOpenInRibbon = this.$widget.find(".promoted-attributes-open-in-ribbon");
|
||||
this.$promotedAttributesOpenInRibbon.on('change', () =>
|
||||
this.updateCheckboxOption('promotedAttributesOpenInRibbon', this.$promotedAttributesOpenInRibbon));
|
||||
|
||||
this.$editedNotesOpenInRibbon = this.$widget.find(".edited-notes-open-in-ribbon");
|
||||
this.$editedNotesOpenInRibbon.on('change', () =>
|
||||
this.updateCheckboxOption('editedNotesOpenInRibbon', this.$editedNotesOpenInRibbon));
|
||||
}
|
||||
|
||||
async optionsLoaded(options) {
|
||||
this.setCheckboxState(this.$promotedAttributesOpenInRibbon, options.promotedAttributesOpenInRibbon);
|
||||
this.setCheckboxState(this.$editedNotesOpenInRibbon, options.editedNotesOpenInRibbon);
|
||||
}
|
||||
}
|
||||
@@ -722,25 +722,26 @@ a.external:not(.no-arrow):after, a[href^="http://"]:not(.no-arrow):after, a[href
|
||||
content: '' !important;
|
||||
}
|
||||
|
||||
.include-note.box-size-small .include-note-content {
|
||||
/* Using data- attribute to support both CKEditor and readonly view */
|
||||
.include-note[data-box-size=small] .include-note-content {
|
||||
max-height: 10em;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.include-note.box-size-small .include-note-content.type-pdf {
|
||||
.include-note[data-box-size=small] .include-note-content.type-pdf {
|
||||
height: 10em; /* PDF is rendered in iframe and must be sized absolutely */
|
||||
}
|
||||
|
||||
.include-note.box-size-medium .include-note-content {
|
||||
.include-note[data-box-size=medium] .include-note-content {
|
||||
max-height: 20em;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.include-note.box-size-medium .include-note-content.type-pdf .rendered-content {
|
||||
.include-note[data-box-size=medium] .include-note-content.type-pdf .rendered-content {
|
||||
height: 20em; /* PDF is rendered in iframe and must be sized absolutely */
|
||||
}
|
||||
|
||||
.include-note.box-size-full .include-note-content.type-pdf .rendered-content {
|
||||
.include-note[data-box-size=full] .include-note-content.type-pdf .rendered-content {
|
||||
height: 50em; /* PDF is rendered in iframe and it's not possible to put full height so at least a large height */
|
||||
}
|
||||
|
||||
|
||||
@@ -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}`);
|
||||
|
||||
|
||||
@@ -11,28 +11,26 @@ const ws = require('../../services/ws');
|
||||
const log = require('../../services/log');
|
||||
const utils = require('../../services/utils');
|
||||
const path = require('path');
|
||||
const BAttribute = require('../../becca/entities/battribute');
|
||||
const htmlSanitizer = require('../../services/html_sanitizer');
|
||||
const {formatAttrForSearch} = require("../../services/attribute_formatter");
|
||||
const jsdom = require("jsdom");
|
||||
const { JSDOM } = jsdom;
|
||||
|
||||
function addClipping(req) {
|
||||
// if a note under the clipperInbox as the same 'pageUrl' attribute,
|
||||
// if a note under the clipperInbox has the same 'pageUrl' attribute,
|
||||
// add the content to that note and clone it under today's inbox
|
||||
// otherwise just create a new note under today's inbox
|
||||
let {title, content, pageUrl, images} = req.body;
|
||||
const clipType = 'clippings';
|
||||
|
||||
const clipperInbox = getClipperInboxNote();
|
||||
const dailyNote = dateNoteService.getDayNote(dateUtils.localNowDate());
|
||||
|
||||
pageUrl = htmlSanitizer.sanitizeUrl(pageUrl);
|
||||
let clippingNote = findClippingNote(clipperInbox, pageUrl, clipType);
|
||||
|
||||
if (!clippingNote) {
|
||||
clippingNote = noteService.createNewNote({
|
||||
parentNoteId: dailyNote.noteId,
|
||||
parentNoteId: clipperInbox.noteId,
|
||||
title: title,
|
||||
content: '',
|
||||
type: 'text'
|
||||
@@ -49,8 +47,8 @@ function addClipping(req) {
|
||||
|
||||
clippingNote.setContent(`${existingContent}${existingContent.trim() ? "<br>" : ""}${rewrittenContent}`);
|
||||
|
||||
if (clippingNote.parentNoteId !== dailyNote.noteId) {
|
||||
cloneService.cloneNoteToParentNote(clippingNote.noteId, dailyNote.noteId);
|
||||
if (clippingNote.parentNoteId !== clipperInbox.noteId) {
|
||||
cloneService.cloneNoteToParentNote(clippingNote.noteId, clipperInbox.noteId);
|
||||
}
|
||||
|
||||
return {
|
||||
@@ -80,7 +78,7 @@ function getClipperInboxNote() {
|
||||
let clipperInbox = attributeService.getNoteWithLabel('clipperInbox');
|
||||
|
||||
if (!clipperInbox) {
|
||||
clipperInbox = dateNoteService.getRootCalendarNote();
|
||||
clipperInbox = dateNoteService.getDayNote(dateUtils.localNowDate());
|
||||
}
|
||||
|
||||
return clipperInbox;
|
||||
@@ -127,7 +125,10 @@ function createNote(req) {
|
||||
|
||||
const existingContent = note.getContent();
|
||||
const rewrittenContent = processContent(images, note, content);
|
||||
note.setContent(`${existingContent}${existingContent.trim() ? "<br/>" : ""}${rewrittenContent}`);
|
||||
const newContent = `${existingContent}${existingContent.trim() ? "<br/>" : ""}${rewrittenContent}`;
|
||||
note.setContent(newContent);
|
||||
|
||||
noteService.asyncPostProcessContent(note, newContent); // to mark attachments as used
|
||||
|
||||
return {
|
||||
noteId: note.noteId
|
||||
@@ -152,20 +153,9 @@ function processContent(images, note, content) {
|
||||
|
||||
const buffer = Buffer.from(dataUrl.split(",")[1], 'base64');
|
||||
|
||||
const {note: imageNote, url} = imageService.saveImage(note.noteId, buffer, filename, true);
|
||||
|
||||
new BAttribute({
|
||||
noteId: imageNote.noteId,
|
||||
type: 'label',
|
||||
name: 'archived'
|
||||
}).save(); // so that these image notes don't show up in search / autocomplete
|
||||
|
||||
new BAttribute({
|
||||
noteId: note.noteId,
|
||||
type: 'relation',
|
||||
name: 'imageLink',
|
||||
value: imageNote.noteId
|
||||
}).save();
|
||||
const attachment = imageService.saveImageToAttachment(note.noteId, buffer, filename, true);
|
||||
const sanitizedTitle = attachment.title.replace(/[^a-z0-9-.]/gi, "");
|
||||
const url = `api/attachments/${attachment.attachmentId}/image/${sanitizedTitle}`;
|
||||
|
||||
log.info(`Replacing '${imageId}' with '${url}' in note '${note.noteId}'`);
|
||||
|
||||
|
||||
@@ -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');
|
||||
|
||||
@@ -55,7 +55,9 @@ const ALLOWED_OPTIONS = new Set([
|
||||
'eraseUnusedAttachmentsAfterSeconds',
|
||||
'disableTray',
|
||||
'customSearchEngineName',
|
||||
'customSearchEngineUrl'
|
||||
'customSearchEngineUrl',
|
||||
'promotedAttributesOpenInRibbon',
|
||||
'editedNotesOpenInRibbon'
|
||||
]);
|
||||
|
||||
function getOptions() {
|
||||
|
||||
@@ -21,8 +21,8 @@ function getRevisions(req) {
|
||||
LENGTH(blobs.content) AS contentLength
|
||||
FROM revisions
|
||||
JOIN blobs ON revisions.blobId = blobs.blobId
|
||||
WHERE noteId = ?
|
||||
ORDER BY utcDateCreated DESC`, [req.params.noteId]);
|
||||
WHERE revisions.noteId = ?
|
||||
ORDER BY revisions.utcDateCreated DESC`, [req.params.noteId]);
|
||||
}
|
||||
|
||||
function getRevision(req) {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -4,8 +4,8 @@ const build = require('./build');
|
||||
const packageJson = require('../../package');
|
||||
const {TRILIUM_DATA_DIR} = require('./data_dir');
|
||||
|
||||
const APP_DB_VERSION = 223;
|
||||
const SYNC_VERSION = 30;
|
||||
const APP_DB_VERSION = 225;
|
||||
const SYNC_VERSION = 31;
|
||||
const CLIPPER_PROTOCOL_VERSION = "1.0";
|
||||
|
||||
module.exports = {
|
||||
|
||||
@@ -1 +1 @@
|
||||
module.exports = { buildDate:"2023-07-28T00:06:23+02:00", buildRevision: "ce3834eb9ecd710bde0e6843bd52bb306faf55ce" };
|
||||
module.exports = { buildDate:"2023-08-16T23:02:15+02:00", buildRevision: "3f7a5504c77263a7118cede5c0d9b450ba37f424" };
|
||||
|
||||
@@ -65,6 +65,7 @@ module.exports = [
|
||||
{ type: 'label', name: 'executeButton'},
|
||||
{ type: 'label', name: 'executeDescription'},
|
||||
{ type: 'label', name: 'newNotesOnTop'},
|
||||
{ type: 'label', name: 'clipperInbox'},
|
||||
|
||||
// relation names
|
||||
{ type: 'relation', name: 'internalLink' },
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
@@ -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}'`,
|
||||
|
||||
@@ -14,7 +14,8 @@ function getEntityHashes() {
|
||||
const hashRows = sql.getRawRows(`
|
||||
SELECT entityName,
|
||||
entityId,
|
||||
hash
|
||||
hash,
|
||||
isErased
|
||||
FROM entity_changes
|
||||
WHERE isSynced = 1
|
||||
AND entityName != 'note_reordering'`);
|
||||
@@ -25,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)) {
|
||||
|
||||
@@ -3,19 +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;
|
||||
@@ -32,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',
|
||||
@@ -55,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}' (${entityChanges.length} entities) 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) {
|
||||
@@ -103,39 +103,34 @@ function fillEntityChanges(entityName, entityPrimaryKey, condition = '') {
|
||||
|
||||
createdCount++;
|
||||
|
||||
let hash;
|
||||
let utcDateChanged;
|
||||
let isSynced;
|
||||
const ec = {
|
||||
entityName,
|
||||
entityId,
|
||||
isErased: false
|
||||
};
|
||||
|
||||
if (entityName === 'blobs') {
|
||||
const blob = sql.getRow("SELECT blobId, content, utcDateModified FROM blobs WHERE blobId = ?", [entityId]);
|
||||
hash = blobService.calculateContentHash(blob);
|
||||
utcDateChanged = blob.utcDateModified;
|
||||
isSynced = true; // blobs are always synced
|
||||
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) {
|
||||
hash = entity?.generateHash() || "|deleted";
|
||||
utcDateChanged = entity?.getUtcDateChanged() || dateUtils.utcNowDateTime();
|
||||
isSynced = entityName !== 'options' || !!entity?.isSynced;
|
||||
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
|
||||
hash = "deleted";
|
||||
utcDateChanged = dateUtils.utcNowDateTime();
|
||||
isSynced = true; // deletable (the ones with isDeleted) entities are synced
|
||||
ec.hash = "deleted";
|
||||
ec.utcDateChanged = dateUtils.utcNowDateTime();
|
||||
ec.isSynced = true; // deletable (the ones with isDeleted) entities are synced
|
||||
}
|
||||
}
|
||||
|
||||
addEntityChange({
|
||||
entityName,
|
||||
entityId,
|
||||
hash: hash,
|
||||
isErased: false,
|
||||
utcDateChanged: utcDateChanged,
|
||||
isSynced: isSynced
|
||||
});
|
||||
putEntityChange(ec);
|
||||
}
|
||||
|
||||
if (createdCount > 0) {
|
||||
@@ -164,10 +159,10 @@ function recalculateMaxEntityChangeId() {
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
addNoteReorderingEntityChange,
|
||||
moveEntityChangeToTop,
|
||||
addEntityChange,
|
||||
addEntityChangeWithInstanceId,
|
||||
putNoteReorderingEntityChange,
|
||||
putEntityChangeForOtherInstances,
|
||||
putEntityChange,
|
||||
putEntityChangeWithInstanceId,
|
||||
fillAllEntityChanges,
|
||||
addEntityChangesForSector,
|
||||
getMaxEntityChangeId: () => maxEntityChangeId,
|
||||
|
||||
@@ -37,8 +37,9 @@ function eraseNotes(noteIdsToErase) {
|
||||
function setEntityChangesAsErased(entityChanges) {
|
||||
for (const ec of entityChanges) {
|
||||
ec.isErased = true;
|
||||
ec.utcDateChanged = dateUtils.utcNowDateTime();
|
||||
|
||||
entityChangesService.addEntityChange(ec);
|
||||
entityChangesService.putEntityChange(ec);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -301,16 +301,10 @@ function importEnex(taskContext, file, parentNote) {
|
||||
? resource.title
|
||||
: `image.${resource.mime.substr(6)}`; // default if real name is not present
|
||||
|
||||
const {url, note: imageNote} = imageService.saveImage(noteEntity.noteId, resource.content, originalName, taskContext.data.shrinkImages);
|
||||
|
||||
for (const attr of resource.attributes) {
|
||||
if (attr.name !== 'originalFileName') { // this one is already saved in imageService
|
||||
imageNote.addAttribute(attr.type, attr.name, attr.value);
|
||||
}
|
||||
}
|
||||
|
||||
updateDates(imageNote, utcDateCreated, utcDateModified);
|
||||
const attachment = imageService.saveImageToAttachment(noteEntity.noteId, resource.content, originalName, taskContext.data.shrinkImages);
|
||||
|
||||
const sanitizedTitle = attachment.title.replace(/[^a-z0-9-.]/gi, "");
|
||||
const url = `api/attachments/${attachment.attachmentId}/image/${sanitizedTitle}`;
|
||||
const imageLink = `<img src="${url}">`;
|
||||
|
||||
content = content.replace(mediaRegex, imageLink);
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -265,7 +265,7 @@ function createNewNoteWithTarget(target, targetBranchId, params) {
|
||||
|
||||
const retObject = createNewNote(params);
|
||||
|
||||
entityChangesService.addNoteReorderingEntityChange(params.parentNoteId);
|
||||
entityChangesService.putNoteReorderingEntityChange(params.parentNoteId);
|
||||
|
||||
return retObject;
|
||||
}
|
||||
|
||||
@@ -88,7 +88,9 @@ const defaultOptions = [
|
||||
{ name: 'disableTray', value: 'false', isSynced: false },
|
||||
{ name: 'eraseUnusedAttachmentsAfterSeconds', value: '2592000', isSynced: true },
|
||||
{ name: 'customSearchEngineName', value: 'DuckDuckGo', isSynced: true },
|
||||
{ name: 'customSearchEngineUrl', value: 'https://duckduckgo.com/?q={keyword}', isSynced: true }
|
||||
{ name: 'customSearchEngineUrl', value: 'https://duckduckgo.com/?q={keyword}', isSynced: true },
|
||||
{ name: 'promotedAttributesOpenInRibbon', value: 'true', isSynced: true },
|
||||
{ name: 'editedNotesOpenInRibbon', value: 'true', isSynced: true }
|
||||
];
|
||||
|
||||
function initStartupOptions() {
|
||||
|
||||
@@ -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 = {
|
||||
|
||||
@@ -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,12 +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_') || // node network errors
|
||||
e.message.includes('Bad Gateway'))) {
|
||||
if (e.message?.includes('ECONNREFUSED') ||
|
||||
e.message?.includes('ERR_') || // node network errors
|
||||
e.message?.includes('Bad Gateway')) {
|
||||
|
||||
ws.syncFailed();
|
||||
|
||||
@@ -108,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;
|
||||
@@ -146,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) {
|
||||
@@ -254,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();
|
||||
|
||||
@@ -351,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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,98 +4,83 @@ 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') {
|
||||
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 = remoteEntityRow.content === null ? null : Buffer.from(remoteEntityRow.content, 'base64');
|
||||
remoteEntityRow.content = Buffer.from(remoteEntityRow.content, 'base64');
|
||||
|
||||
if (remoteEntityRow.content?.byteLength === 0) {
|
||||
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]);
|
||||
}
|
||||
function updateNoteReordering(remoteEC, remoteEntityRow, instanceId) {
|
||||
for (const key in remoteEntityRow) {
|
||||
sql.execute("UPDATE branches SET notePosition = ? WHERE branchId = ?", [remoteEntityRow[key], key]);
|
||||
}
|
||||
|
||||
entityChangesService.addEntityChangeWithInstanceId(entityChange, instanceId);
|
||||
});
|
||||
entityChangesService.putEntityChangeWithInstanceId(remoteEC, instanceId);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -109,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 = {
|
||||
|
||||
@@ -165,7 +165,7 @@ function sortNotes(parentNoteId, customSortBy = 'title', reverse = false, folder
|
||||
}
|
||||
|
||||
if (someBranchUpdated) {
|
||||
entityChangesService.addNoteReorderingEntityChange(parentNoteId);
|
||||
entityChangesService.putNoteReorderingEntityChange(parentNoteId);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user