Compare commits

..

46 Commits

Author SHA1 Message Date
zadam f7dd3558a7 release 0.31.2-beta 2019-04-22 22:53:04 +02:00
zadam eac5ec6e26 added searchForNote(s) also to frontend API 2019-04-22 22:52:09 +02:00
zadam f273b4334e implemented @limit for search 2019-04-22 18:08:33 +02:00
zadam 15eaf67189 fix broken history navigation 2019-04-22 17:13:24 +02:00
zadam 983d9c4319 fix merge issue 2019-04-22 17:05:04 +02:00
zadam a88a00b60e Merge branch 'stable'
# Conflicts:
#	package-lock.json
#	package.json
#	src/public/javascripts/services/note_detail_file.js
#	src/services/build.js
2019-04-22 16:29:16 +02:00
zadam a44f43b133 release 0.30.8 2019-04-22 15:02:39 +02:00
zadam 69677b15fe fix broken file download 2019-04-22 15:01:05 +02:00
zadam 2573c3b8a6 sync frontend dayjs to 1.8.12 2019-04-22 14:15:30 +02:00
zadam 0b0e9f2953 from now on, "latest" docker tag won't point to beta releases, closes #476 2019-04-21 12:48:16 +02:00
zadam 1990c9399e removed FTS5 completely 2019-04-21 12:03:13 +02:00
zadam 151641b659 fulltext search now doesn't use FTS5, closes #489 2019-04-21 11:54:13 +02:00
zadam 36c6376220 create trilium data directory if it doesn't exist, closes #491 2019-04-21 09:05:55 +02:00
zadam f0e22b70f6 tree reload doesn't necessarily re-activates the note 2019-04-20 21:14:48 +02:00
zadam 225e893461 fix closing of tooltips 2019-04-20 09:39:39 +02:00
zadam 064a11d872 don't allow deleting hoisted note + other related fixes, closes #488 2019-04-16 21:40:04 +02:00
zadam 3760835608 added noscript to make it clear that Trilium requires JavaScript 2019-04-15 21:30:26 +02:00
zadam e7ac2ec4ed add noscript to setup 2019-04-15 21:28:21 +02:00
zadam 8da8dfc543 if the resized and optimized image is larger than original, then keep the original 2019-04-15 21:12:47 +02:00
zadam 6b3c8b4bac Merge remote-tracking branch 'origin/master' 2019-04-15 21:08:38 +02:00
zadam 25fe78012f upgrade to ckeditor 12.1.0 which fixes some upload issues 2019-04-15 21:08:31 +02:00
zadam 147d79c65c release 0.31.1-beta 2019-04-14 21:18:22 +02:00
zadam 6fddd2bd62 Merge pull request #485 from fbartels/docker-compose
add example docker-compose
2019-04-14 20:22:55 +02:00
zadam 77c9b64dc7 re-enable animation for expanding since it seems to fix fancytree rendering glitches 2019-04-14 19:43:44 +02:00
zadam 47cb7c7f5e not refreshing tree after sort operation because it messes with everything ... 2019-04-14 18:32:56 +02:00
zadam 1bd769827d faster opening of the date notes (no animation) 2019-04-14 18:16:15 +02:00
Felix Bartels 288ec88c2f add example docker-compose
Signed-off-by: Felix Bartels <felix@host-consultants.de>
2019-04-14 15:24:35 +02:00
zadam 71916b5e28 empty icon for some menu actions do de-emphasize less important items 2019-04-14 13:51:21 +02:00
zadam b2d4330692 added button for reloading frontend 2019-04-14 12:48:50 +02:00
zadam 253a6ef081 added date note APIs to frontend script API 2019-04-14 12:24:48 +02:00
zadam e1e020c1a4 more reliable reimplementation of "create day sub note" 2019-04-14 12:18:52 +02:00
zadam 2b4413a1bd exposing tree cache methods to reload note's parents/children to frontend API 2019-04-13 22:56:45 +02:00
zadam dae674a7cd tree cache reloading 2019-04-13 22:10:16 +02:00
zadam 751bf94758 don't change sourceId for script execution which forces full tree reloads because it is annoying 2019-04-13 19:45:50 +02:00
zadam 4b934a4a81 fix sync / fulltext issue 2019-04-13 19:34:19 +02:00
zadam 7e374e795b fill sync rows to fix sync issues 2019-04-13 10:30:16 +02:00
zadam be378f71b0 sync checks for note_contents 2019-04-13 10:13:47 +02:00
zadam 27f74489fd fix login/logout redirects when not in the document root 2019-04-11 22:04:36 +02:00
zadam ae8f4ffbbe minor sync fix for fulltext 2019-04-11 21:44:35 +02:00
zadam 896776516b fix in page search in browser (didn't appear after ctrl+f) 2019-04-09 21:16:18 +02:00
zadam a27525af9e frontend API additions 2019-04-09 21:13:47 +02:00
zadam 83b4382787 missing cert and key error message 2019-04-08 20:08:29 +02:00
zadam ef1b32d586 search error handling & sanitize input for default fulltext 2019-04-02 22:52:42 +02:00
zadam fe3a0bc756 new find-in-page dialog (using electron-find) which actually works 2019-04-02 21:44:56 +02:00
zadam 2259e1d44d make sure to wait for DB connection to be up before starting ... 2019-04-02 19:42:41 +02:00
zadam 9ca4a016eb fix linux server build 2019-04-01 23:11:06 +02:00
75 changed files with 2661 additions and 681 deletions
+135 -241
View File
@@ -17,763 +17,657 @@
<table id="9" parent="2" name="event_log"/>
<table id="10" parent="2" name="links"/>
<table id="11" parent="2" name="note_contents"/>
<table id="12" parent="2" name="note_fulltext_config">
<WithoutRowId>1</WithoutRowId>
</table>
<table id="13" parent="2" name="note_fulltext_content"/>
<table id="14" parent="2" name="note_fulltext_data"/>
<table id="15" parent="2" name="note_fulltext_docsize"/>
<table id="16" parent="2" name="note_fulltext_idx">
<WithoutRowId>1</WithoutRowId>
</table>
<table id="17" parent="2" name="note_revisions"/>
<table id="18" parent="2" name="notes"/>
<table id="19" parent="2" name="options"/>
<table id="20" parent="2" name="recent_notes"/>
<table id="21" parent="2" name="source_ids"/>
<table id="22" parent="2" name="sqlite_master">
<table id="12" parent="2" name="note_revisions"/>
<table id="13" parent="2" name="notes"/>
<table id="14" parent="2" name="options"/>
<table id="15" parent="2" name="recent_notes"/>
<table id="16" parent="2" name="source_ids"/>
<table id="17" parent="2" name="sqlite_master">
<System>1</System>
</table>
<table id="23" parent="2" name="sqlite_sequence">
<table id="18" parent="2" name="sqlite_sequence">
<System>1</System>
</table>
<table id="24" parent="2" name="sync"/>
<virtual-table id="25" parent="2" name="note_fulltext">
<Module>fts5</Module>
<Arguments>noteId UNINDEXED
title
titleHash UNINDEXED
content
contentHash UNINDEXED</Arguments>
</virtual-table>
<column id="26" parent="6" name="apiTokenId">
<table id="19" parent="2" name="sync"/>
<column id="20" parent="6" name="apiTokenId">
<Position>1</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
</column>
<column id="27" parent="6" name="token">
<column id="21" parent="6" name="token">
<Position>2</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
</column>
<column id="28" parent="6" name="utcDateCreated">
<column id="22" parent="6" name="utcDateCreated">
<Position>3</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
</column>
<column id="29" parent="6" name="isDeleted">
<column id="23" parent="6" name="isDeleted">
<Position>4</Position>
<DataType>INT|0s</DataType>
<NotNull>1</NotNull>
<DefaultExpression>0</DefaultExpression>
</column>
<column id="30" parent="6" name="hash">
<column id="24" parent="6" name="hash">
<Position>5</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
<DefaultExpression>&quot;&quot;</DefaultExpression>
</column>
<index id="31" parent="6" name="sqlite_autoindex_api_tokens_1">
<index id="25" parent="6" name="sqlite_autoindex_api_tokens_1">
<NameSurrogate>1</NameSurrogate>
<ColNames>apiTokenId</ColNames>
<ColumnCollations></ColumnCollations>
<Unique>1</Unique>
</index>
<key id="32" parent="6">
<key id="26" parent="6">
<ColNames>apiTokenId</ColNames>
<Primary>1</Primary>
<UnderlyingIndexName>sqlite_autoindex_api_tokens_1</UnderlyingIndexName>
</key>
<column id="33" parent="7" name="attributeId">
<column id="27" parent="7" name="attributeId">
<Position>1</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
</column>
<column id="34" parent="7" name="noteId">
<column id="28" parent="7" name="noteId">
<Position>2</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
</column>
<column id="35" parent="7" name="type">
<column id="29" parent="7" name="type">
<Position>3</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
</column>
<column id="36" parent="7" name="name">
<column id="30" parent="7" name="name">
<Position>4</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
</column>
<column id="37" parent="7" name="value">
<column id="31" parent="7" name="value">
<Position>5</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
<DefaultExpression>&apos;&apos;</DefaultExpression>
</column>
<column id="38" parent="7" name="position">
<column id="32" parent="7" name="position">
<Position>6</Position>
<DataType>INT|0s</DataType>
<NotNull>1</NotNull>
<DefaultExpression>0</DefaultExpression>
</column>
<column id="39" parent="7" name="utcDateCreated">
<column id="33" parent="7" name="utcDateCreated">
<Position>7</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
</column>
<column id="40" parent="7" name="utcDateModified">
<column id="34" parent="7" name="utcDateModified">
<Position>8</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
</column>
<column id="41" parent="7" name="isDeleted">
<column id="35" parent="7" name="isDeleted">
<Position>9</Position>
<DataType>INT|0s</DataType>
<NotNull>1</NotNull>
</column>
<column id="42" parent="7" name="hash">
<column id="36" parent="7" name="hash">
<Position>10</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
<DefaultExpression>&quot;&quot;</DefaultExpression>
</column>
<column id="43" parent="7" name="isInheritable">
<column id="37" parent="7" name="isInheritable">
<Position>11</Position>
<DataType>int|0s</DataType>
<DefaultExpression>0</DefaultExpression>
</column>
<index id="44" parent="7" name="sqlite_autoindex_attributes_1">
<index id="38" parent="7" name="sqlite_autoindex_attributes_1">
<NameSurrogate>1</NameSurrogate>
<ColNames>attributeId</ColNames>
<ColumnCollations></ColumnCollations>
<Unique>1</Unique>
</index>
<index id="45" parent="7" name="IDX_attributes_noteId_index">
<index id="39" parent="7" name="IDX_attributes_noteId_index">
<ColNames>noteId</ColNames>
<ColumnCollations></ColumnCollations>
</index>
<index id="46" parent="7" name="IDX_attributes_name_value">
<index id="40" parent="7" name="IDX_attributes_name_value">
<ColNames>name
value</ColNames>
<ColumnCollations></ColumnCollations>
</index>
<index id="47" parent="7" name="IDX_attributes_name_index">
<index id="41" parent="7" name="IDX_attributes_name_index">
<ColNames>name</ColNames>
<ColumnCollations></ColumnCollations>
</index>
<index id="48" parent="7" name="IDX_attributes_value_index">
<index id="42" parent="7" name="IDX_attributes_value_index">
<ColNames>value</ColNames>
<ColumnCollations></ColumnCollations>
</index>
<key id="49" parent="7">
<key id="43" parent="7">
<ColNames>attributeId</ColNames>
<Primary>1</Primary>
<UnderlyingIndexName>sqlite_autoindex_attributes_1</UnderlyingIndexName>
</key>
<column id="50" parent="8" name="branchId">
<column id="44" parent="8" name="branchId">
<Position>1</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
</column>
<column id="51" parent="8" name="noteId">
<column id="45" parent="8" name="noteId">
<Position>2</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
</column>
<column id="52" parent="8" name="parentNoteId">
<column id="46" parent="8" name="parentNoteId">
<Position>3</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
</column>
<column id="53" parent="8" name="notePosition">
<column id="47" parent="8" name="notePosition">
<Position>4</Position>
<DataType>INTEGER|0s</DataType>
<NotNull>1</NotNull>
</column>
<column id="54" parent="8" name="prefix">
<column id="48" parent="8" name="prefix">
<Position>5</Position>
<DataType>TEXT|0s</DataType>
</column>
<column id="55" parent="8" name="isExpanded">
<column id="49" parent="8" name="isExpanded">
<Position>6</Position>
<DataType>BOOLEAN|0s</DataType>
</column>
<column id="56" parent="8" name="isDeleted">
<column id="50" parent="8" name="isDeleted">
<Position>7</Position>
<DataType>INTEGER|0s</DataType>
<NotNull>1</NotNull>
<DefaultExpression>0</DefaultExpression>
</column>
<column id="57" parent="8" name="utcDateModified">
<column id="51" parent="8" name="utcDateModified">
<Position>8</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
</column>
<column id="58" parent="8" name="utcDateCreated">
<column id="52" parent="8" name="utcDateCreated">
<Position>9</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
</column>
<column id="59" parent="8" name="hash">
<column id="53" parent="8" name="hash">
<Position>10</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
<DefaultExpression>&quot;&quot;</DefaultExpression>
</column>
<index id="60" parent="8" name="sqlite_autoindex_branches_1">
<index id="54" parent="8" name="sqlite_autoindex_branches_1">
<NameSurrogate>1</NameSurrogate>
<ColNames>branchId</ColNames>
<ColumnCollations></ColumnCollations>
<Unique>1</Unique>
</index>
<index id="61" parent="8" name="IDX_branches_noteId_parentNoteId">
<index id="55" parent="8" name="IDX_branches_noteId_parentNoteId">
<ColNames>noteId
parentNoteId</ColNames>
<ColumnCollations></ColumnCollations>
</index>
<index id="62" parent="8" name="IDX_branches_noteId">
<index id="56" parent="8" name="IDX_branches_noteId">
<ColNames>noteId</ColNames>
<ColumnCollations></ColumnCollations>
</index>
<index id="63" parent="8" name="IDX_branches_parentNoteId">
<index id="57" parent="8" name="IDX_branches_parentNoteId">
<ColNames>parentNoteId</ColNames>
<ColumnCollations></ColumnCollations>
</index>
<key id="64" parent="8">
<key id="58" parent="8">
<ColNames>branchId</ColNames>
<Primary>1</Primary>
<UnderlyingIndexName>sqlite_autoindex_branches_1</UnderlyingIndexName>
</key>
<column id="65" parent="9" name="eventId">
<column id="59" parent="9" name="eventId">
<Position>1</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
</column>
<column id="66" parent="9" name="noteId">
<column id="60" parent="9" name="noteId">
<Position>2</Position>
<DataType>TEXT|0s</DataType>
</column>
<column id="67" parent="9" name="comment">
<column id="61" parent="9" name="comment">
<Position>3</Position>
<DataType>TEXT|0s</DataType>
</column>
<column id="68" parent="9" name="utcDateCreated">
<column id="62" parent="9" name="utcDateCreated">
<Position>4</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
</column>
<index id="69" parent="9" name="sqlite_autoindex_event_log_1">
<index id="63" parent="9" name="sqlite_autoindex_event_log_1">
<NameSurrogate>1</NameSurrogate>
<ColNames>eventId</ColNames>
<ColumnCollations></ColumnCollations>
<Unique>1</Unique>
</index>
<key id="70" parent="9">
<key id="64" parent="9">
<ColNames>eventId</ColNames>
<Primary>1</Primary>
<UnderlyingIndexName>sqlite_autoindex_event_log_1</UnderlyingIndexName>
</key>
<column id="71" parent="10" name="linkId">
<column id="65" parent="10" name="linkId">
<Position>1</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
</column>
<column id="72" parent="10" name="noteId">
<column id="66" parent="10" name="noteId">
<Position>2</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
</column>
<column id="73" parent="10" name="targetNoteId">
<column id="67" parent="10" name="targetNoteId">
<Position>3</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
</column>
<column id="74" parent="10" name="type">
<column id="68" parent="10" name="type">
<Position>4</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
</column>
<column id="75" parent="10" name="hash">
<column id="69" parent="10" name="hash">
<Position>5</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
<DefaultExpression>&quot;&quot;</DefaultExpression>
</column>
<column id="76" parent="10" name="isDeleted">
<column id="70" parent="10" name="isDeleted">
<Position>6</Position>
<DataType>INTEGER|0s</DataType>
<NotNull>1</NotNull>
<DefaultExpression>0</DefaultExpression>
</column>
<column id="77" parent="10" name="utcDateCreated">
<column id="71" parent="10" name="utcDateCreated">
<Position>7</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
</column>
<column id="78" parent="10" name="utcDateModified">
<column id="72" parent="10" name="utcDateModified">
<Position>8</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
</column>
<index id="79" parent="10" name="sqlite_autoindex_links_1">
<index id="73" parent="10" name="sqlite_autoindex_links_1">
<NameSurrogate>1</NameSurrogate>
<ColNames>linkId</ColNames>
<ColumnCollations></ColumnCollations>
<Unique>1</Unique>
</index>
<index id="80" parent="10" name="IDX_links_noteId_index">
<index id="74" parent="10" name="IDX_links_noteId_index">
<ColNames>noteId</ColNames>
<ColumnCollations></ColumnCollations>
</index>
<index id="81" parent="10" name="IDX_links_targetNoteId_index">
<index id="75" parent="10" name="IDX_links_targetNoteId_index">
<ColNames>targetNoteId</ColNames>
<ColumnCollations></ColumnCollations>
</index>
<key id="82" parent="10">
<key id="76" parent="10">
<ColNames>linkId</ColNames>
<Primary>1</Primary>
<UnderlyingIndexName>sqlite_autoindex_links_1</UnderlyingIndexName>
</key>
<column id="83" parent="11" name="noteId">
<column id="77" parent="11" name="noteId">
<Position>1</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
</column>
<column id="84" parent="11" name="content">
<column id="78" parent="11" name="content">
<Position>2</Position>
<DataType>TEXT|0s</DataType>
<DefaultExpression>NULL</DefaultExpression>
</column>
<column id="85" parent="11" name="hash">
<column id="79" parent="11" name="hash">
<Position>3</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
<DefaultExpression>&quot;&quot;</DefaultExpression>
</column>
<column id="86" parent="11" name="utcDateModified">
<column id="80" parent="11" name="utcDateModified">
<Position>4</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
</column>
<index id="87" parent="11" name="sqlite_autoindex_note_contents_1">
<index id="81" parent="11" name="sqlite_autoindex_note_contents_1">
<NameSurrogate>1</NameSurrogate>
<ColNames>noteId</ColNames>
<ColumnCollations></ColumnCollations>
<Unique>1</Unique>
</index>
<key id="88" parent="11">
<key id="82" parent="11">
<ColNames>noteId</ColNames>
<Primary>1</Primary>
<UnderlyingIndexName>sqlite_autoindex_note_contents_1</UnderlyingIndexName>
</key>
<column id="89" parent="12" name="k">
<Position>1</Position>
<NotNull>1</NotNull>
</column>
<column id="90" parent="12" name="v">
<Position>2</Position>
</column>
<index id="91" parent="12" name="sqlite_autoindex_note_fulltext_config_1">
<NameSurrogate>1</NameSurrogate>
<ColNames>k</ColNames>
<ColumnCollations></ColumnCollations>
<Unique>1</Unique>
</index>
<key id="92" parent="12">
<ColNames>k</ColNames>
<Primary>1</Primary>
<UnderlyingIndexName>sqlite_autoindex_note_fulltext_config_1</UnderlyingIndexName>
</key>
<column id="93" parent="13" name="id">
<Position>1</Position>
<DataType>INTEGER|0s</DataType>
</column>
<column id="94" parent="13" name="c0">
<Position>2</Position>
</column>
<column id="95" parent="13" name="c1">
<Position>3</Position>
</column>
<column id="96" parent="13" name="c2">
<Position>4</Position>
</column>
<column id="97" parent="13" name="c3">
<Position>5</Position>
</column>
<column id="98" parent="13" name="c4">
<Position>6</Position>
</column>
<key id="99" parent="13">
<ColNames>id</ColNames>
<Primary>1</Primary>
</key>
<column id="100" parent="14" name="id">
<Position>1</Position>
<DataType>INTEGER|0s</DataType>
</column>
<column id="101" parent="14" name="block">
<Position>2</Position>
<DataType>BLOB|0s</DataType>
</column>
<key id="102" parent="14">
<ColNames>id</ColNames>
<Primary>1</Primary>
</key>
<column id="103" parent="15" name="id">
<Position>1</Position>
<DataType>INTEGER|0s</DataType>
</column>
<column id="104" parent="15" name="sz">
<Position>2</Position>
<DataType>BLOB|0s</DataType>
</column>
<key id="105" parent="15">
<ColNames>id</ColNames>
<Primary>1</Primary>
</key>
<column id="106" parent="16" name="segid">
<Position>1</Position>
<NotNull>1</NotNull>
</column>
<column id="107" parent="16" name="term">
<Position>2</Position>
<NotNull>1</NotNull>
</column>
<column id="108" parent="16" name="pgno">
<Position>3</Position>
</column>
<index id="109" parent="16" name="sqlite_autoindex_note_fulltext_idx_1">
<NameSurrogate>1</NameSurrogate>
<ColNames>segid
term</ColNames>
<ColumnCollations></ColumnCollations>
<Unique>1</Unique>
</index>
<key id="110" parent="16">
<ColNames>segid
term</ColNames>
<Primary>1</Primary>
<UnderlyingIndexName>sqlite_autoindex_note_fulltext_idx_1</UnderlyingIndexName>
</key>
<column id="111" parent="17" name="noteRevisionId">
<column id="83" parent="12" name="noteRevisionId">
<Position>1</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
</column>
<column id="112" parent="17" name="noteId">
<column id="84" parent="12" name="noteId">
<Position>2</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
</column>
<column id="113" parent="17" name="title">
<column id="85" parent="12" name="title">
<Position>3</Position>
<DataType>TEXT|0s</DataType>
</column>
<column id="114" parent="17" name="content">
<column id="86" parent="12" name="content">
<Position>4</Position>
<DataType>TEXT|0s</DataType>
</column>
<column id="115" parent="17" name="isProtected">
<column id="87" parent="12" name="isProtected">
<Position>5</Position>
<DataType>INT|0s</DataType>
<NotNull>1</NotNull>
<DefaultExpression>0</DefaultExpression>
</column>
<column id="116" parent="17" name="utcDateModifiedFrom">
<column id="88" parent="12" name="utcDateModifiedFrom">
<Position>6</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
</column>
<column id="117" parent="17" name="utcDateModifiedTo">
<column id="89" parent="12" name="utcDateModifiedTo">
<Position>7</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
</column>
<column id="118" parent="17" name="dateModifiedFrom">
<column id="90" parent="12" name="dateModifiedFrom">
<Position>8</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
</column>
<column id="119" parent="17" name="dateModifiedTo">
<column id="91" parent="12" name="dateModifiedTo">
<Position>9</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
</column>
<column id="120" parent="17" name="type">
<column id="92" parent="12" name="type">
<Position>10</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
<DefaultExpression>&apos;&apos;</DefaultExpression>
</column>
<column id="121" parent="17" name="mime">
<column id="93" parent="12" name="mime">
<Position>11</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
<DefaultExpression>&apos;&apos;</DefaultExpression>
</column>
<column id="122" parent="17" name="hash">
<column id="94" parent="12" name="hash">
<Position>12</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
<DefaultExpression>&quot;&quot;</DefaultExpression>
</column>
<index id="123" parent="17" name="sqlite_autoindex_note_revisions_1">
<index id="95" parent="12" name="sqlite_autoindex_note_revisions_1">
<NameSurrogate>1</NameSurrogate>
<ColNames>noteRevisionId</ColNames>
<ColumnCollations></ColumnCollations>
<Unique>1</Unique>
</index>
<index id="124" parent="17" name="IDX_note_revisions_noteId">
<index id="96" parent="12" name="IDX_note_revisions_noteId">
<ColNames>noteId</ColNames>
<ColumnCollations></ColumnCollations>
</index>
<index id="125" parent="17" name="IDX_note_revisions_dateModifiedFrom">
<index id="97" parent="12" name="IDX_note_revisions_dateModifiedFrom">
<ColNames>utcDateModifiedFrom</ColNames>
<ColumnCollations></ColumnCollations>
</index>
<index id="126" parent="17" name="IDX_note_revisions_dateModifiedTo">
<index id="98" parent="12" name="IDX_note_revisions_dateModifiedTo">
<ColNames>utcDateModifiedTo</ColNames>
<ColumnCollations></ColumnCollations>
</index>
<key id="127" parent="17">
<key id="99" parent="12">
<ColNames>noteRevisionId</ColNames>
<Primary>1</Primary>
<UnderlyingIndexName>sqlite_autoindex_note_revisions_1</UnderlyingIndexName>
</key>
<column id="128" parent="18" name="noteId">
<column id="100" parent="13" name="noteId">
<Position>1</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
</column>
<column id="129" parent="18" name="title">
<column id="101" parent="13" name="title">
<Position>2</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
<DefaultExpression>&quot;note&quot;</DefaultExpression>
</column>
<column id="130" parent="18" name="isProtected">
<column id="102" parent="13" name="isProtected">
<Position>3</Position>
<DataType>INT|0s</DataType>
<NotNull>1</NotNull>
<DefaultExpression>0</DefaultExpression>
</column>
<column id="131" parent="18" name="type">
<column id="103" parent="13" name="type">
<Position>4</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
<DefaultExpression>&apos;text&apos;</DefaultExpression>
</column>
<column id="132" parent="18" name="mime">
<column id="104" parent="13" name="mime">
<Position>5</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
<DefaultExpression>&apos;text/html&apos;</DefaultExpression>
</column>
<column id="133" parent="18" name="hash">
<column id="105" parent="13" name="hash">
<Position>6</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
<DefaultExpression>&quot;&quot;</DefaultExpression>
</column>
<column id="134" parent="18" name="isDeleted">
<column id="106" parent="13" name="isDeleted">
<Position>7</Position>
<DataType>INT|0s</DataType>
<NotNull>1</NotNull>
<DefaultExpression>0</DefaultExpression>
</column>
<column id="135" parent="18" name="dateCreated">
<column id="107" parent="13" name="dateCreated">
<Position>8</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
</column>
<column id="136" parent="18" name="dateModified">
<column id="108" parent="13" name="dateModified">
<Position>9</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
</column>
<column id="137" parent="18" name="utcDateCreated">
<column id="109" parent="13" name="utcDateCreated">
<Position>10</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
</column>
<column id="138" parent="18" name="utcDateModified">
<column id="110" parent="13" name="utcDateModified">
<Position>11</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
</column>
<index id="139" parent="18" name="sqlite_autoindex_notes_1">
<index id="111" parent="13" name="sqlite_autoindex_notes_1">
<NameSurrogate>1</NameSurrogate>
<ColNames>noteId</ColNames>
<ColumnCollations></ColumnCollations>
<Unique>1</Unique>
</index>
<key id="140" parent="18">
<key id="112" parent="13">
<ColNames>noteId</ColNames>
<Primary>1</Primary>
<UnderlyingIndexName>sqlite_autoindex_notes_1</UnderlyingIndexName>
</key>
<column id="141" parent="19" name="name">
<column id="113" parent="14" name="name">
<Position>1</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
</column>
<column id="142" parent="19" name="value">
<column id="114" parent="14" name="value">
<Position>2</Position>
<DataType>TEXT|0s</DataType>
</column>
<column id="143" parent="19" name="isSynced">
<column id="115" parent="14" name="isSynced">
<Position>3</Position>
<DataType>INTEGER|0s</DataType>
<NotNull>1</NotNull>
<DefaultExpression>0</DefaultExpression>
</column>
<column id="144" parent="19" name="hash">
<column id="116" parent="14" name="hash">
<Position>4</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
<DefaultExpression>&quot;&quot;</DefaultExpression>
</column>
<column id="145" parent="19" name="utcDateCreated">
<column id="117" parent="14" name="utcDateCreated">
<Position>5</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
</column>
<column id="146" parent="19" name="utcDateModified">
<column id="118" parent="14" name="utcDateModified">
<Position>6</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
</column>
<index id="147" parent="19" name="sqlite_autoindex_options_1">
<index id="119" parent="14" name="sqlite_autoindex_options_1">
<NameSurrogate>1</NameSurrogate>
<ColNames>name</ColNames>
<ColumnCollations></ColumnCollations>
<Unique>1</Unique>
</index>
<key id="148" parent="19">
<key id="120" parent="14">
<ColNames>name</ColNames>
<Primary>1</Primary>
<UnderlyingIndexName>sqlite_autoindex_options_1</UnderlyingIndexName>
</key>
<column id="149" parent="20" name="branchId">
<column id="121" parent="15" name="branchId">
<Position>1</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
</column>
<column id="150" parent="20" name="notePath">
<column id="122" parent="15" name="notePath">
<Position>2</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
</column>
<column id="151" parent="20" name="hash">
<column id="123" parent="15" name="hash">
<Position>3</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
<DefaultExpression>&quot;&quot;</DefaultExpression>
</column>
<column id="152" parent="20" name="utcDateCreated">
<column id="124" parent="15" name="utcDateCreated">
<Position>4</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
</column>
<column id="153" parent="20" name="isDeleted">
<column id="125" parent="15" name="isDeleted">
<Position>5</Position>
<DataType>INT|0s</DataType>
</column>
<index id="154" parent="20" name="sqlite_autoindex_recent_notes_1">
<index id="126" parent="15" name="sqlite_autoindex_recent_notes_1">
<NameSurrogate>1</NameSurrogate>
<ColNames>branchId</ColNames>
<ColumnCollations></ColumnCollations>
<Unique>1</Unique>
</index>
<key id="155" parent="20">
<key id="127" parent="15">
<ColNames>branchId</ColNames>
<Primary>1</Primary>
<UnderlyingIndexName>sqlite_autoindex_recent_notes_1</UnderlyingIndexName>
</key>
<column id="156" parent="21" name="sourceId">
<column id="128" parent="16" name="sourceId">
<Position>1</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
</column>
<column id="157" parent="21" name="utcDateCreated">
<column id="129" parent="16" name="utcDateCreated">
<Position>2</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
</column>
<index id="158" parent="21" name="sqlite_autoindex_source_ids_1">
<index id="130" parent="16" name="sqlite_autoindex_source_ids_1">
<NameSurrogate>1</NameSurrogate>
<ColNames>sourceId</ColNames>
<ColumnCollations></ColumnCollations>
<Unique>1</Unique>
</index>
<key id="159" parent="21">
<key id="131" parent="16">
<ColNames>sourceId</ColNames>
<Primary>1</Primary>
<UnderlyingIndexName>sqlite_autoindex_source_ids_1</UnderlyingIndexName>
</key>
<column id="160" parent="22" name="type">
<column id="132" parent="17" name="type">
<Position>1</Position>
<DataType>text|0s</DataType>
</column>
<column id="161" parent="22" name="name">
<column id="133" parent="17" name="name">
<Position>2</Position>
<DataType>text|0s</DataType>
</column>
<column id="162" parent="22" name="tbl_name">
<column id="134" parent="17" name="tbl_name">
<Position>3</Position>
<DataType>text|0s</DataType>
</column>
<column id="163" parent="22" name="rootpage">
<column id="135" parent="17" name="rootpage">
<Position>4</Position>
<DataType>int|0s</DataType>
</column>
<column id="164" parent="22" name="sql">
<column id="136" parent="17" name="sql">
<Position>5</Position>
<DataType>text|0s</DataType>
</column>
<column id="165" parent="23" name="name">
<column id="137" parent="18" name="name">
<Position>1</Position>
</column>
<column id="166" parent="23" name="seq">
<column id="138" parent="18" name="seq">
<Position>2</Position>
</column>
<column id="167" parent="24" name="id">
<column id="139" parent="19" name="id">
<Position>1</Position>
<DataType>INTEGER|0s</DataType>
<NotNull>1</NotNull>
<SequenceIdentity>1</SequenceIdentity>
</column>
<column id="168" parent="24" name="entityName">
<column id="140" parent="19" name="entityName">
<Position>2</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
</column>
<column id="169" parent="24" name="entityId">
<column id="141" parent="19" name="entityId">
<Position>3</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
</column>
<column id="170" parent="24" name="sourceId">
<column id="142" parent="19" name="sourceId">
<Position>4</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
</column>
<column id="171" parent="24" name="utcSyncDate">
<column id="143" parent="19" name="utcSyncDate">
<Position>5</Position>
<DataType>TEXT|0s</DataType>
<NotNull>1</NotNull>
</column>
<index id="172" parent="24" name="IDX_sync_entityName_entityId">
<index id="144" parent="19" name="IDX_sync_entityName_entityId">
<ColNames>entityName
entityId</ColNames>
<ColumnCollations></ColumnCollations>
<Unique>1</Unique>
</index>
<index id="173" parent="24" name="IDX_sync_utcSyncDate">
<index id="145" parent="19" name="IDX_sync_utcSyncDate">
<ColNames>utcSyncDate</ColNames>
<ColumnCollations></ColumnCollations>
</index>
<key id="174" parent="24">
<key id="146" parent="19">
<ColNames>id</ColNames>
<Primary>1</Primary>
</key>
+1 -1
View File
@@ -2,4 +2,4 @@
VERSION=`jq -r ".version" package.json`
sudo docker build -t zadam/trilium:latest -t zadam/trilium:$VERSION .
sudo docker build -t zadam/trilium:$VERSION .
+2
View File
@@ -14,6 +14,8 @@ rm node-v${NODE_VERSION}-linux-x64.tar.xz
mv node-v${NODE_VERSION}-linux-x64 node
cp -r ../../node_modules/ ./
cp -r ../../images/ ./
cp -r ../../libraries/ ./
cp -r ../../src/ ./
cp -r ../../db/ ./
cp -r ../../package.json ./
+1 -1
View File
@@ -2,6 +2,6 @@
SCHEMA_FILE_PATH=db/schema.sql
sqlite3 ~/trilium-data/document.db .schema | grep -v "sqlite_sequence" | grep -v "note_fulltext_" > "$SCHEMA_FILE_PATH"
sqlite3 ~/trilium-data/document.db .schema | grep -v "sqlite_sequence" > "$SCHEMA_FILE_PATH"
echo "DB schema exported to $SCHEMA_FILE_PATH"
+5 -2
View File
@@ -5,5 +5,8 @@ if [[ $# -eq 0 ]] ; then
exit 1
fi
sudo docker push zadam/trilium:latest
sudo docker push zadam/trilium:$1
sudo docker push zadam/trilium:$1
if [[ $1 != *"beta"* ]]; then
sudo docker push zadam/trilium:latest
fi
BIN
View File
Binary file not shown.
@@ -1 +0,0 @@
CREATE VIRTUAL TABLE note_fulltext USING fts5(noteId UNINDEXED, title, titleHash UNINDEXED, content, contentHash UNINDEXED);
+1 -10
View File
@@ -1,10 +1 @@
const repository = require('../../src/services/repository');
const noteFulltextService = require('../../src/services/note_fulltext');
module.exports = async () => {
const notes = await repository.getEntities('SELECT * FROM notes WHERE isDeleted = 0 AND isProtected = 0');
for (const note of notes) {
await noteFulltextService.updateNoteFulltext(note);
}
};
module.exports = async () => {};
+5
View File
@@ -0,0 +1,5 @@
const syncTableService = require('../../src/services/sync_table');
module.exports = async () => {
await syncTableService.fillAllSyncRows();
};
@@ -0,0 +1 @@
DROP TABLE IF EXISTS note_fulltext;
-2
View File
@@ -1,5 +1,3 @@
CREATE VIRTUAL TABLE note_fulltext USING fts5(noteId UNINDEXED, title, titleHash UNINDEXED, content, contentHash UNINDEXED)
/* note_fulltext(noteId,title,titleHash,content,contentHash) */;
CREATE TABLE IF NOT EXISTS "sync" (
`id` INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
`entityName` TEXT NOT NULL,
+17
View File
@@ -0,0 +1,17 @@
version: '2.1'
services:
trilium:
build:
context: .
image: zadam/trilium
restart: always
environment:
- TRILIUM_DATA_DIR=/data
ports:
- "8080:8080"
volumes:
- trilium:/data
volumes:
trilium:
+172 -22
View File
@@ -394,7 +394,7 @@ the backend.
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="services_backend_script_api.js.html">services/backend_script_api.js</a>, <a href="services_backend_script_api.js.html#line296">line 296</a>
<a href="services_backend_script_api.js.html">services/backend_script_api.js</a>, <a href="services_backend_script_api.js.html#line310">line 310</a>
</li></ul></dd>
@@ -758,7 +758,7 @@ the backend.
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="services_backend_script_api.js.html">services/backend_script_api.js</a>, <a href="services_backend_script_api.js.html#line184">line 184</a>
<a href="services_backend_script_api.js.html">services/backend_script_api.js</a>, <a href="services_backend_script_api.js.html#line198">line 198</a>
</li></ul></dd>
@@ -1044,7 +1044,7 @@ the backend.
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="services_backend_script_api.js.html">services/backend_script_api.js</a>, <a href="services_backend_script_api.js.html#line197">line 197</a>
<a href="services_backend_script_api.js.html">services/backend_script_api.js</a>, <a href="services_backend_script_api.js.html#line211">line 211</a>
</li></ul></dd>
@@ -1224,7 +1224,7 @@ the backend.
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="services_backend_script_api.js.html">services/backend_script_api.js</a>, <a href="services_backend_script_api.js.html#line145">line 145</a>
<a href="services_backend_script_api.js.html">services/backend_script_api.js</a>, <a href="services_backend_script_api.js.html#line159">line 159</a>
</li></ul></dd>
@@ -1423,7 +1423,7 @@ the backend.
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="services_backend_script_api.js.html">services/backend_script_api.js</a>, <a href="services_backend_script_api.js.html#line135">line 135</a>
<a href="services_backend_script_api.js.html">services/backend_script_api.js</a>, <a href="services_backend_script_api.js.html#line149">line 149</a>
</li></ul></dd>
@@ -1523,7 +1523,7 @@ the backend.
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="services_backend_script_api.js.html">services/backend_script_api.js</a>, <a href="services_backend_script_api.js.html#line301">line 301</a>
<a href="services_backend_script_api.js.html">services/backend_script_api.js</a>, <a href="services_backend_script_api.js.html#line315">line 315</a>
</li></ul></dd>
@@ -1981,7 +1981,7 @@ the backend.
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="services_backend_script_api.js.html">services/backend_script_api.js</a>, <a href="services_backend_script_api.js.html#line225">line 225</a>
<a href="services_backend_script_api.js.html">services/backend_script_api.js</a>, <a href="services_backend_script_api.js.html#line239">line 239</a>
</li></ul></dd>
@@ -2739,7 +2739,7 @@ if some action needs to happen on only one specific instance.
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="services_backend_script_api.js.html">services/backend_script_api.js</a>, <a href="services_backend_script_api.js.html#line244">line 244</a>
<a href="services_backend_script_api.js.html">services/backend_script_api.js</a>, <a href="services_backend_script_api.js.html#line258">line 258</a>
</li></ul></dd>
@@ -3084,7 +3084,7 @@ if some action needs to happen on only one specific instance.
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="services_backend_script_api.js.html">services/backend_script_api.js</a>, <a href="services_backend_script_api.js.html#line114">line 114</a>
<a href="services_backend_script_api.js.html">services/backend_script_api.js</a>, <a href="services_backend_script_api.js.html#line128">line 128</a>
</li></ul></dd>
@@ -3280,7 +3280,7 @@ if some action needs to happen on only one specific instance.
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="services_backend_script_api.js.html">services/backend_script_api.js</a>, <a href="services_backend_script_api.js.html#line124">line 124</a>
<a href="services_backend_script_api.js.html">services/backend_script_api.js</a>, <a href="services_backend_script_api.js.html#line138">line 138</a>
</li></ul></dd>
@@ -3384,7 +3384,7 @@ if some action needs to happen on only one specific instance.
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="services_backend_script_api.js.html">services/backend_script_api.js</a>, <a href="services_backend_script_api.js.html#line216">line 216</a>
<a href="services_backend_script_api.js.html">services/backend_script_api.js</a>, <a href="services_backend_script_api.js.html#line230">line 230</a>
</li></ul></dd>
@@ -3560,7 +3560,7 @@ if some action needs to happen on only one specific instance.
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="services_backend_script_api.js.html">services/backend_script_api.js</a>, <a href="services_backend_script_api.js.html#line235">line 235</a>
<a href="services_backend_script_api.js.html">services/backend_script_api.js</a>, <a href="services_backend_script_api.js.html#line249">line 249</a>
</li></ul></dd>
@@ -3713,7 +3713,7 @@ if some action needs to happen on only one specific instance.
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="services_backend_script_api.js.html">services/backend_script_api.js</a>, <a href="services_backend_script_api.js.html#line253">line 253</a>
<a href="services_backend_script_api.js.html">services/backend_script_api.js</a>, <a href="services_backend_script_api.js.html#line267">line 267</a>
</li></ul></dd>
@@ -3861,7 +3861,7 @@ if some action needs to happen on only one specific instance.
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="services_backend_script_api.js.html">services/backend_script_api.js</a>, <a href="services_backend_script_api.js.html#line208">line 208</a>
<a href="services_backend_script_api.js.html">services/backend_script_api.js</a>, <a href="services_backend_script_api.js.html#line222">line 222</a>
</li></ul></dd>
@@ -3891,6 +3891,160 @@ if some action needs to happen on only one specific instance.
<h4 class="name" id="searchForNote"><span class="type-signature"></span>searchForNote<span class="signature">(searchString)</span><span class="type-signature"> &rarr; {Promise.&lt;(<a href="Note.html">Note</a>|null)>}</span></h4>
<div class="description">
This is a powerful search method - you can search by attributes and their values, e.g.:
"@dateModified =* MONTH AND @log". See full documentation for all options at: https://github.com/zadam/trilium/wiki/Search
</div>
<h5>Parameters:</h5>
<table class="params">
<thead>
<tr>
<th>Name</th>
<th>Type</th>
<th class="last">Description</th>
</tr>
</thead>
<tbody>
<tr>
<td class="name"><code>searchString</code></td>
<td class="type">
<span class="param-type">string</span>
</td>
<td class="description last"></td>
</tr>
</tbody>
</table>
<dl class="details">
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="services_backend_script_api.js.html">services/backend_script_api.js</a>, <a href="services_backend_script_api.js.html#line114">line 114</a>
</li></ul></dd>
</dl>
<h5>Returns:</h5>
<dl>
<dt>
Type
</dt>
<dd>
<span class="param-type">Promise.&lt;(<a href="Note.html">Note</a>|null)></span>
</dd>
</dl>
@@ -4023,10 +4177,6 @@ if some action needs to happen on only one specific instance.
<h5>Returns:</h5>
<div class="param-desc">
$
</div>
<dl>
@@ -4231,7 +4381,7 @@ This method looks similar to toggleNoteInParent() but differs because we're look
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="services_backend_script_api.js.html">services/backend_script_api.js</a>, <a href="services_backend_script_api.js.html#line273">line 273</a>
<a href="services_backend_script_api.js.html">services/backend_script_api.js</a>, <a href="services_backend_script_api.js.html#line287">line 287</a>
</li></ul></dd>
@@ -4362,7 +4512,7 @@ This method looks similar to toggleNoteInParent() but differs because we're look
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="services_backend_script_api.js.html">services/backend_script_api.js</a>, <a href="services_backend_script_api.js.html#line260">line 260</a>
<a href="services_backend_script_api.js.html">services/backend_script_api.js</a>, <a href="services_backend_script_api.js.html#line274">line 274</a>
</li></ul></dd>
@@ -4576,7 +4726,7 @@ This method looks similar to toggleNoteInParent() but differs because we're look
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="services_backend_script_api.js.html">services/backend_script_api.js</a>, <a href="services_backend_script_api.js.html#line157">line 157</a>
<a href="services_backend_script_api.js.html">services/backend_script_api.js</a>, <a href="services_backend_script_api.js.html#line171">line 171</a>
</li></ul></dd>
@@ -4733,7 +4883,7 @@ transactional by default.
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="services_backend_script_api.js.html">services/backend_script_api.js</a>, <a href="services_backend_script_api.js.html#line286">line 286</a>
<a href="services_backend_script_api.js.html">services/backend_script_api.js</a>, <a href="services_backend_script_api.js.html#line300">line 300</a>
</li></ul></dd>
+52 -52
View File
@@ -396,7 +396,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_note.js.html">entities/note.js</a>, <a href="entities_note.js.html#line36">line 36</a>
<a href="entities_note.js.html">entities/note.js</a>, <a href="entities_note.js.html#line35">line 35</a>
</li></ul></dd>
@@ -581,7 +581,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_note.js.html">entities/note.js</a>, <a href="entities_note.js.html#line326">line 326</a>
<a href="entities_note.js.html">entities/note.js</a>, <a href="entities_note.js.html#line345">line 345</a>
</li></ul></dd>
@@ -746,7 +746,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_note.js.html">entities/note.js</a>, <a href="entities_note.js.html#line195">line 195</a>
<a href="entities_note.js.html">entities/note.js</a>, <a href="entities_note.js.html#line214">line 214</a>
</li></ul></dd>
@@ -922,7 +922,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_note.js.html">entities/note.js</a>, <a href="entities_note.js.html#line337">line 337</a>
<a href="entities_note.js.html">entities/note.js</a>, <a href="entities_note.js.html#line356">line 356</a>
</li></ul></dd>
@@ -1026,7 +1026,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_note.js.html">entities/note.js</a>, <a href="entities_note.js.html#line624">line 624</a>
<a href="entities_note.js.html">entities/note.js</a>, <a href="entities_note.js.html#line643">line 643</a>
</li></ul></dd>
@@ -1126,7 +1126,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_note.js.html">entities/note.js</a>, <a href="entities_note.js.html#line652">line 652</a>
<a href="entities_note.js.html">entities/note.js</a>, <a href="entities_note.js.html#line671">line 671</a>
</li></ul></dd>
@@ -1230,7 +1230,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_note.js.html">entities/note.js</a>, <a href="entities_note.js.html#line638">line 638</a>
<a href="entities_note.js.html">entities/note.js</a>, <a href="entities_note.js.html#line657">line 657</a>
</li></ul></dd>
@@ -1334,7 +1334,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_note.js.html">entities/note.js</a>, <a href="entities_note.js.html#line66">line 66</a>
<a href="entities_note.js.html">entities/note.js</a>, <a href="entities_note.js.html#line74">line 74</a>
</li></ul></dd>
@@ -1434,7 +1434,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_note.js.html">entities/note.js</a>, <a href="entities_note.js.html#line513">line 513</a>
<a href="entities_note.js.html">entities/note.js</a>, <a href="entities_note.js.html#line532">line 532</a>
</li></ul></dd>
@@ -1665,7 +1665,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_note.js.html">entities/note.js</a>, <a href="entities_note.js.html#line536">line 536</a>
<a href="entities_note.js.html">entities/note.js</a>, <a href="entities_note.js.html#line555">line 555</a>
</li></ul></dd>
@@ -1861,7 +1861,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_note.js.html">entities/note.js</a>, <a href="entities_note.js.html#line574">line 574</a>
<a href="entities_note.js.html">entities/note.js</a>, <a href="entities_note.js.html#line593">line 593</a>
</li></ul></dd>
@@ -2057,7 +2057,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_note.js.html">entities/note.js</a>, <a href="entities_note.js.html#line583">line 583</a>
<a href="entities_note.js.html">entities/note.js</a>, <a href="entities_note.js.html#line602">line 602</a>
</li></ul></dd>
@@ -2157,7 +2157,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_note.js.html">entities/note.js</a>, <a href="entities_note.js.html#line90">line 90</a>
<a href="entities_note.js.html">entities/note.js</a>, <a href="entities_note.js.html#line109">line 109</a>
</li></ul></dd>
@@ -2306,7 +2306,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_note.js.html">entities/note.js</a>, <a href="entities_note.js.html#line424">line 424</a>
<a href="entities_note.js.html">entities/note.js</a>, <a href="entities_note.js.html#line443">line 443</a>
</li></ul></dd>
@@ -2471,7 +2471,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_note.js.html">entities/note.js</a>, <a href="entities_note.js.html#line220">line 220</a>
<a href="entities_note.js.html">entities/note.js</a>, <a href="entities_note.js.html#line239">line 239</a>
</li></ul></dd>
@@ -2636,7 +2636,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_note.js.html">entities/note.js</a>, <a href="entities_note.js.html#line212">line 212</a>
<a href="entities_note.js.html">entities/note.js</a>, <a href="entities_note.js.html#line231">line 231</a>
</li></ul></dd>
@@ -2789,7 +2789,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_note.js.html">entities/note.js</a>, <a href="entities_note.js.html#line436">line 436</a>
<a href="entities_note.js.html">entities/note.js</a>, <a href="entities_note.js.html#line455">line 455</a>
</li></ul></dd>
@@ -2897,7 +2897,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_note.js.html">entities/note.js</a>, <a href="entities_note.js.html#line599">line 599</a>
<a href="entities_note.js.html">entities/note.js</a>, <a href="entities_note.js.html#line618">line 618</a>
</li></ul></dd>
@@ -3001,7 +3001,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_note.js.html">entities/note.js</a>, <a href="entities_note.js.html#line617">line 617</a>
<a href="entities_note.js.html">entities/note.js</a>, <a href="entities_note.js.html#line636">line 636</a>
</li></ul></dd>
@@ -3101,7 +3101,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_note.js.html">entities/note.js</a>, <a href="entities_note.js.html#line180">line 180</a>
<a href="entities_note.js.html">entities/note.js</a>, <a href="entities_note.js.html#line199">line 199</a>
</li></ul></dd>
@@ -3205,7 +3205,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_note.js.html">entities/note.js</a>, <a href="entities_note.js.html#line664">line 664</a>
<a href="entities_note.js.html">entities/note.js</a>, <a href="entities_note.js.html#line683">line 683</a>
</li></ul></dd>
@@ -3358,7 +3358,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_note.js.html">entities/note.js</a>, <a href="entities_note.js.html#line430">line 430</a>
<a href="entities_note.js.html">entities/note.js</a>, <a href="entities_note.js.html#line449">line 449</a>
</li></ul></dd>
@@ -3523,7 +3523,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_note.js.html">entities/note.js</a>, <a href="entities_note.js.html#line236">line 236</a>
<a href="entities_note.js.html">entities/note.js</a>, <a href="entities_note.js.html#line255">line 255</a>
</li></ul></dd>
@@ -3688,7 +3688,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_note.js.html">entities/note.js</a>, <a href="entities_note.js.html#line228">line 228</a>
<a href="entities_note.js.html">entities/note.js</a>, <a href="entities_note.js.html#line247">line 247</a>
</li></ul></dd>
@@ -3841,7 +3841,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_note.js.html">entities/note.js</a>, <a href="entities_note.js.html#line448">line 448</a>
<a href="entities_note.js.html">entities/note.js</a>, <a href="entities_note.js.html#line467">line 467</a>
</li></ul></dd>
@@ -3997,7 +3997,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_note.js.html">entities/note.js</a>, <a href="entities_note.js.html#line442">line 442</a>
<a href="entities_note.js.html">entities/note.js</a>, <a href="entities_note.js.html#line461">line 461</a>
</li></ul></dd>
@@ -4105,7 +4105,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_note.js.html">entities/note.js</a>, <a href="entities_note.js.html#line590">line 590</a>
<a href="entities_note.js.html">entities/note.js</a>, <a href="entities_note.js.html#line609">line 609</a>
</li></ul></dd>
@@ -4205,7 +4205,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_note.js.html">entities/note.js</a>, <a href="entities_note.js.html#line161">line 161</a>
<a href="entities_note.js.html">entities/note.js</a>, <a href="entities_note.js.html#line180">line 180</a>
</li></ul></dd>
@@ -4313,7 +4313,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_note.js.html">entities/note.js</a>, <a href="entities_note.js.html#line608">line 608</a>
<a href="entities_note.js.html">entities/note.js</a>, <a href="entities_note.js.html#line627">line 627</a>
</li></ul></dd>
@@ -4413,7 +4413,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_note.js.html">entities/note.js</a>, <a href="entities_note.js.html#line187">line 187</a>
<a href="entities_note.js.html">entities/note.js</a>, <a href="entities_note.js.html#line206">line 206</a>
</li></ul></dd>
@@ -4589,7 +4589,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_note.js.html">entities/note.js</a>, <a href="entities_note.js.html#line317">line 317</a>
<a href="entities_note.js.html">entities/note.js</a>, <a href="entities_note.js.html#line336">line 336</a>
</li></ul></dd>
@@ -4693,7 +4693,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_note.js.html">entities/note.js</a>, <a href="entities_note.js.html#line631">line 631</a>
<a href="entities_note.js.html">entities/note.js</a>, <a href="entities_note.js.html#line650">line 650</a>
</li></ul></dd>
@@ -4846,7 +4846,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_note.js.html">entities/note.js</a>, <a href="entities_note.js.html#line412">line 412</a>
<a href="entities_note.js.html">entities/note.js</a>, <a href="entities_note.js.html#line431">line 431</a>
</li></ul></dd>
@@ -4999,7 +4999,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_note.js.html">entities/note.js</a>, <a href="entities_note.js.html#line418">line 418</a>
<a href="entities_note.js.html">entities/note.js</a>, <a href="entities_note.js.html#line437">line 437</a>
</li></ul></dd>
@@ -5108,7 +5108,7 @@ Cache is note instance scoped.
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_note.js.html">entities/note.js</a>, <a href="entities_note.js.html#line244">line 244</a>
<a href="entities_note.js.html">entities/note.js</a>, <a href="entities_note.js.html#line263">line 263</a>
</li></ul></dd>
@@ -5190,7 +5190,7 @@ Cache is note instance scoped.
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_note.js.html">entities/note.js</a>, <a href="entities_note.js.html#line149">line 149</a>
<a href="entities_note.js.html">entities/note.js</a>, <a href="entities_note.js.html#line168">line 168</a>
</li></ul></dd>
@@ -5294,7 +5294,7 @@ Cache is note instance scoped.
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_note.js.html">entities/note.js</a>, <a href="entities_note.js.html#line141">line 141</a>
<a href="entities_note.js.html">entities/note.js</a>, <a href="entities_note.js.html#line160">line 160</a>
</li></ul></dd>
@@ -5398,7 +5398,7 @@ Cache is note instance scoped.
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_note.js.html">entities/note.js</a>, <a href="entities_note.js.html#line136">line 136</a>
<a href="entities_note.js.html">entities/note.js</a>, <a href="entities_note.js.html#line155">line 155</a>
</li></ul></dd>
@@ -5502,7 +5502,7 @@ Cache is note instance scoped.
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_note.js.html">entities/note.js</a>, <a href="entities_note.js.html#line131">line 131</a>
<a href="entities_note.js.html">entities/note.js</a>, <a href="entities_note.js.html#line150">line 150</a>
</li></ul></dd>
@@ -5606,7 +5606,7 @@ Cache is note instance scoped.
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_note.js.html">entities/note.js</a>, <a href="entities_note.js.html#line154">line 154</a>
<a href="entities_note.js.html">entities/note.js</a>, <a href="entities_note.js.html#line173">line 173</a>
</li></ul></dd>
@@ -5710,7 +5710,7 @@ Cache is note instance scoped.
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_note.js.html">entities/note.js</a>, <a href="entities_note.js.html#line249">line 249</a>
<a href="entities_note.js.html">entities/note.js</a>, <a href="entities_note.js.html#line268">line 268</a>
</li></ul></dd>
@@ -5937,7 +5937,7 @@ Cache is note instance scoped.
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_note.js.html">entities/note.js</a>, <a href="entities_note.js.html#line395">line 395</a>
<a href="entities_note.js.html">entities/note.js</a>, <a href="entities_note.js.html#line414">line 414</a>
</li></ul></dd>
@@ -6133,7 +6133,7 @@ Cache is note instance scoped.
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_note.js.html">entities/note.js</a>, <a href="entities_note.js.html#line499">line 499</a>
<a href="entities_note.js.html">entities/note.js</a>, <a href="entities_note.js.html#line518">line 518</a>
</li></ul></dd>
@@ -6329,7 +6329,7 @@ Cache is note instance scoped.
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_note.js.html">entities/note.js</a>, <a href="entities_note.js.html#line508">line 508</a>
<a href="entities_note.js.html">entities/note.js</a>, <a href="entities_note.js.html#line527">line 527</a>
</li></ul></dd>
@@ -6556,7 +6556,7 @@ Cache is note instance scoped.
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_note.js.html">entities/note.js</a>, <a href="entities_note.js.html#line369">line 369</a>
<a href="entities_note.js.html">entities/note.js</a>, <a href="entities_note.js.html#line388">line 388</a>
</li></ul></dd>
@@ -6656,7 +6656,7 @@ Cache is note instance scoped.
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_note.js.html">entities/note.js</a>, <a href="entities_note.js.html#line97">line 97</a>
<a href="entities_note.js.html">entities/note.js</a>, <a href="entities_note.js.html#line116">line 116</a>
</li></ul></dd>
@@ -6756,7 +6756,7 @@ Cache is note instance scoped.
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_note.js.html">entities/note.js</a>, <a href="entities_note.js.html#line126">line 126</a>
<a href="entities_note.js.html">entities/note.js</a>, <a href="entities_note.js.html#line145">line 145</a>
</li></ul></dd>
@@ -6952,7 +6952,7 @@ Cache is note instance scoped.
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_note.js.html">entities/note.js</a>, <a href="entities_note.js.html#line481">line 481</a>
<a href="entities_note.js.html">entities/note.js</a>, <a href="entities_note.js.html#line500">line 500</a>
</li></ul></dd>
@@ -7148,7 +7148,7 @@ Cache is note instance scoped.
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_note.js.html">entities/note.js</a>, <a href="entities_note.js.html#line490">line 490</a>
<a href="entities_note.js.html">entities/note.js</a>, <a href="entities_note.js.html#line509">line 509</a>
</li></ul></dd>
@@ -7406,7 +7406,7 @@ Cache is note instance scoped.
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_note.js.html">entities/note.js</a>, <a href="entities_note.js.html#line352">line 352</a>
<a href="entities_note.js.html">entities/note.js</a>, <a href="entities_note.js.html#line371">line 371</a>
</li></ul></dd>
@@ -7633,7 +7633,7 @@ Cache is note instance scoped.
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_note.js.html">entities/note.js</a>, <a href="entities_note.js.html#line462">line 462</a>
<a href="entities_note.js.html">entities/note.js</a>, <a href="entities_note.js.html#line481">line 481</a>
</li></ul></dd>
@@ -7860,7 +7860,7 @@ Cache is note instance scoped.
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="entities_note.js.html">entities/note.js</a>, <a href="entities_note.js.html#line472">line 472</a>
<a href="entities_note.js.html">entities/note.js</a>, <a href="entities_note.js.html#line491">line 491</a>
</li></ul></dd>
+27 -11
View File
@@ -35,7 +35,6 @@ const repository = require('../services/repository');
const sql = require('../services/sql');
const utils = require('../services/utils');
const dateUtils = require('../services/date_utils');
const noteFulltextService = require('../services/note_fulltext');
const syncTableService = require('../services/sync_table');
const LABEL = 'label';
@@ -90,10 +89,30 @@ class Note extends Entity {
}
}
/*
* Note content has quite special handling - it's not a separate entity, but a lazily loaded
* part of Note entity with it's own sync. Reasons behind this hybrid design has been:
*
* - content can be quite large and it's not necessary to load it / fill memory for any note access even if we don't need a content, especially for bulk operations like search
* - changes in the note metadata or title should not trigger note content sync (so we keep separate utcDateModified and sync rows)
* - but to the user note content and title changes are one and the same - single dateModified (so all changes must go through Note and content is not a separate entity)
*/
/** @returns {Promise&lt;*>} */
async getContent() {
async getContent(silentNotFoundError = false) {
if (this.content === undefined) {
this.content = await sql.getValue(`SELECT content FROM note_contents WHERE noteId = ?`, [this.noteId]);
const res = await sql.getRow(`SELECT content, hash FROM note_contents WHERE noteId = ?`, [this.noteId]);
if (!res) {
if (silentNotFoundError) {
return undefined;
}
else {
throw new Error("Cannot find note content for noteId=" + this.noteId);
}
}
this.content = res.content;
if (this.isProtected) {
if (this.isContentAvailable) {
@@ -123,6 +142,10 @@ class Note extends Entity {
/** @returns {Promise} */
async setContent(content) {
// force updating note itself so that dateChanged is represented correctly even for the content
this.forcedChange = true;
await this.save();
this.content = content;
const pojo = {
@@ -144,10 +167,6 @@ class Note extends Entity {
await sql.upsert("note_contents", "noteId", pojo);
await syncTableService.addNoteContentSync(this.noteId);
this.forcedChange = true;
await this.save();
}
/** @returns {Promise} */
@@ -736,10 +755,7 @@ class Note extends Entity {
delete pojo.isContentAvailable;
delete pojo.__attributeCache;
delete pojo.content;
}
async afterSaving() {
noteFulltextService.triggerNoteFulltextUpdate(this.noteId);
delete pojo.contentHash;
}
}
+2 -2
View File
@@ -272,7 +272,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="services_backend_script_api.js.html">services/backend_script_api.js</a>, <a href="services_backend_script_api.js.html#line159">line 159</a>
<a href="services_backend_script_api.js.html">services/backend_script_api.js</a>, <a href="services_backend_script_api.js.html#line173">line 173</a>
</li></ul></dd>
@@ -558,7 +558,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="services_backend_script_api.js.html">services/backend_script_api.js</a>, <a href="services_backend_script_api.js.html#line166">line 166</a>
<a href="services_backend_script_api.js.html">services/backend_script_api.js</a>, <a href="services_backend_script_api.js.html#line180">line 180</a>
</li></ul></dd>
@@ -127,10 +127,24 @@ function BackendScriptApi(currentNote, apiParams) {
*
* @method
* @param {string} searchString
* @returns ${Promise&lt;Note[]>}
* @returns {Promise&lt;Note[]>}
*/
this.searchForNotes = searchService.searchForNotes;
/**
* This is a powerful search method - you can search by attributes and their values, e.g.:
* "@dateModified =* MONTH AND @log". See full documentation for all options at: https://github.com/zadam/trilium/wiki/Search
*
* @method
* @param {string} searchString
* @returns {Promise&lt;Note|null>}
*/
this.searchForNote = async searchString => {
const notes = await searchService.searchForNotes(searchString);
return notes.length > 0 ? notes[0] : null;
};
/**
* Retrieves notes with given label name &amp; value
*
File diff suppressed because it is too large Load Diff
+118 -2
View File
@@ -199,7 +199,7 @@
<h4 class="name" id="utcDateCreated"><span class="type-signature"></span>utcDateCreated<span class="type-signature"></span></h4>
<h4 class="name" id="dateCreated"><span class="type-signature"></span>dateCreated<span class="type-signature"></span></h4>
@@ -257,7 +257,7 @@
<h4 class="name" id="utcDateModified"><span class="type-signature"></span>utcDateModified<span class="type-signature"></span></h4>
<h4 class="name" id="dateModified"><span class="type-signature"></span>dateModified<span class="type-signature"></span></h4>
@@ -314,6 +314,122 @@
<h4 class="name" id="utcDateCreated"><span class="type-signature"></span>utcDateCreated<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="entities_note_full.js.html">entities/note_full.js</a>, <a href="entities_note_full.js.html#line20">line 20</a>
</li></ul></dd>
</dl>
<h4 class="name" id="utcDateModified"><span class="type-signature"></span>utcDateModified<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="entities_note_full.js.html">entities/note_full.js</a>, <a href="entities_note_full.js.html#line23">line 23</a>
</li></ul></dd>
</dl>
@@ -38,6 +38,12 @@ class NoteFull extends NoteShort {
/** @param {string} */
this.content = row.content;
/** @param {string} */
this.dateCreated = row.dateCreated;
/** @param {string} */
this.dateModified = row.dateModified;
/** @param {string} */
this.utcDateCreated = row.utcDateCreated;
+1 -1
View File
@@ -303,7 +303,7 @@
<dt class="tag-source">Source:</dt>
<dd class="tag-source"><ul class="dummy"><li>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line49">line 49</a>
<a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line53">line 53</a>
</li></ul></dd>
@@ -36,6 +36,7 @@ import noteDetailService from './note_detail.js';
import noteTypeService from './note_type.js';
import noteTooltipService from './note_tooltip.js';
import protectedSessionService from'./protected_session.js';
import dateNotesService from'./date_notes.js';
/**
* This is the main frontend API interface for scripts. It's published in the local "api" object.
@@ -53,6 +54,9 @@ function FrontendScriptApi(startNote, currentNote, originEntity = null) {
/** @property {object|null} entity whose event triggered this execution */
this.originEntity = originEntity;
// to keep consistency with backend API
this.dayjs = dayjs;
/**
* Activates note in the tree and in the note detail.
*
@@ -159,6 +163,46 @@ function FrontendScriptApi(startNote, currentNote, originEntity = null) {
}
};
/**
* This is a powerful search method - you can search by attributes and their values, e.g.:
* "@dateModified =* MONTH AND @log". See full documentation for all options at: https://github.com/zadam/trilium/wiki/Search
*
* @method
* @param {string} searchString
* @returns {Promise&lt;NoteShort[]>}
*/
this.searchForNotes = async searchString => {
const noteIds = await this.runOnServer(async searchString => {
const notes = await api.searchForNotes(searchString);
return notes.map(note => note.noteId);
}, [searchString]);
return await treeCache.getNotes(noteIds);
};
/**
* This is a powerful search method - you can search by attributes and their values, e.g.:
* "@dateModified =* MONTH AND @log". See full documentation for all options at: https://github.com/zadam/trilium/wiki/Search
*
* @method
* @param {string} searchString
* @returns {Promise&lt;NoteShort|null>}
*/
this.searchForNote = async searchString => {
const notes = await this.searchForNotes(searchString);
return notes.length > 0 ? notes[0] : null;
};
/**
* Returns note by given noteId. If note is missing from cache, it's loaded.
**
* @param {string} noteId
* @return {Promise&lt;NoteShort>}
*/
this.getNote = async noteId => await treeCache.getNote(noteId);
/**
* Returns list of notes. If note is missing from cache, it's loaded.
*
@@ -171,6 +215,18 @@ function FrontendScriptApi(startNote, currentNote, originEntity = null) {
*/
this.getNotes = async (noteIds, silentNotFoundError = false) => await treeCache.getNotes(noteIds, silentNotFoundError);
/**
* @param {string} noteId
* @method
*/
this.reloadChildren = async noteId => await treeCache.reloadChildren(noteId);
/**
* @param {string} noteId
* @method
*/
this.reloadParents = async noteId => await treeCache.reloadParents(noteId);
/**
* Instance name identifies particular Trilium instance. It can be useful for scripts
* if some action needs to happen on only one specific instance.
@@ -238,6 +294,12 @@ function FrontendScriptApi(startNote, currentNote, originEntity = null) {
*/
this.getActiveNote = noteDetailService.getActiveNote;
/**
* @method
* @returns {Promise&lt;string>} returns note path of active note
*/
this.getActiveNotePath = treeService.getActiveNotePath;
/**
* This method checks whether user navigated away from the note from which the scripts has been started.
* This is necessary because script execution is async and by the time it is finished, the user might have
@@ -285,6 +347,41 @@ function FrontendScriptApi(startNote, currentNote, originEntity = null) {
* @method
*/
this.protectActiveNote = protectedSessionService.protectNoteAndSendToServer;
/**
* Returns date-note for today. If it doesn't exist, it is automatically created.
*
* @method
* @return {Promise&lt;NoteShort>}
*/
this.getTodayNote = dateNotesService.getTodayNote;
/**
* Returns date-note. If it doesn't exist, it is automatically created.
*
* @method
* @param {string} date - e.g. "2019-04-29"
* @return {Promise&lt;NoteShort>}
*/
this.getDateNote = dateNotesService.getDateNote;
/**
* Returns month-note. If it doesn't exist, it is automatically created.
*
* @method
* @param {string} month - e.g. "2019-04"
* @return {Promise&lt;NoteShort>}
*/
this.getMonthNote = dateNotesService.getMonthNote;
/**
* Returns year-note. If it doesn't exist, it is automatically created.
*
* @method
* @param {string} year - e.g. "2019"
* @return {Promise&lt;NoteShort>}
*/
this.getYearNote = dateNotesService.getYearNote;
}
export default FrontendScriptApi;</code></pre>
+3 -6
View File
@@ -30,6 +30,8 @@ function onClosed() {
}
async function createMainWindow() {
await sqlInit.dbConnection;
// if schema doesn't exist -> setup process
// if schema exists, then we need to wait until the migration process is finished
if (await sqlInit.schemaExists()) {
@@ -95,15 +97,10 @@ app.on('ready', async () => {
mainWindow = await createMainWindow();
const result = globalShortcut.register('CommandOrControl+Alt+P', cls.wrap(async () => {
const dateNoteService = require('./src/services/date_notes');
const dateUtils = require('./src/services/date_utils');
const parentNote = await dateNoteService.getDateNote(dateUtils.localNowDate());
// window may be hidden / not in focus
mainWindow.focus();
mainWindow.webContents.send('create-day-sub-note', parentNote.noteId);
mainWindow.webContents.send('create-day-sub-note');
}));
if (!result) {
+1 -1
View File
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+9 -4
View File
@@ -1,6 +1,6 @@
{
"name": "trilium",
"version": "0.30.7",
"version": "0.31.1-beta",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
@@ -2164,9 +2164,9 @@
"integrity": "sha1-QGXiATz5+5Ft39gu+1Bq1MZ2kGI="
},
"dayjs": {
"version": "1.8.11",
"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.8.11.tgz",
"integrity": "sha512-eBvWmKn52ax/TBYg7f+tzzqARPl0D0/NUmf5QrZ0Pp+/vFoTP3Q7y4t6p438+5NPN+t8y8Sps3Ed34OV8sNFBQ=="
"version": "1.8.12",
"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.8.12.tgz",
"integrity": "sha512-7keChCzzjU68sYJpk7kEI1Q9qbJyscyiW0STwEhqDFt+2js9pA/BSzmYE4PRxcMMoUMxNeY0TEMZHqV/JBR4OA=="
},
"debug": {
"version": "4.1.1",
@@ -3222,6 +3222,11 @@
}
}
},
"electron-find": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/electron-find/-/electron-find-1.0.5.tgz",
"integrity": "sha512-LmFp6wolElQwlnjFR4jo7cARUZq+siR0sCS5Qp5Jywz2puM5OJzjRRyUEHCMi9Pm7xLupVYjwNVF1JojVQ0QPA=="
},
"electron-in-page-search": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/electron-in-page-search/-/electron-in-page-search-1.3.2.tgz",
+3 -2
View File
@@ -2,7 +2,7 @@
"name": "trilium",
"productName": "Trilium Notes",
"description": "Trilium Notes",
"version": "0.31.0-beta",
"version": "0.31.2-beta",
"license": "AGPL-3.0-only",
"main": "electron.js",
"bin": {
@@ -28,11 +28,12 @@
"commonmark": "0.28.1",
"cookie-parser": "1.4.4",
"csurf": "1.9.0",
"dayjs": "1.8.11",
"dayjs": "1.8.12",
"debug": "4.1.1",
"ejs": "2.6.1",
"electron-debug": "2.1.0",
"electron-dl": "1.13.0",
"electron-find": "1.0.5",
"electron-in-page-search": "1.3.2",
"electron-window-state": "5.0.3",
"express": "4.16.4",
+14 -11
View File
@@ -7,7 +7,6 @@ const repository = require('../services/repository');
const sql = require('../services/sql');
const utils = require('../services/utils');
const dateUtils = require('../services/date_utils');
const noteFulltextService = require('../services/note_fulltext');
const syncTableService = require('../services/sync_table');
const LABEL = 'label';
@@ -72,12 +71,20 @@ class Note extends Entity {
*/
/** @returns {Promise<*>} */
async getContent() {
async getContent(silentNotFoundError = false) {
if (this.content === undefined) {
const res = await sql.getRow(`SELECT content, hash FROM note_contents WHERE noteId = ?`, [this.noteId]);
if (!res) {
if (silentNotFoundError) {
return undefined;
}
else {
throw new Error("Cannot find note content for noteId=" + this.noteId);
}
}
this.content = res.content;
this.contentHash = res.contentHash; // used only for note_fulltext consistency check
if (this.isProtected) {
if (this.isContentAvailable) {
@@ -107,6 +114,10 @@ class Note extends Entity {
/** @returns {Promise} */
async setContent(content) {
// force updating note itself so that dateChanged is represented correctly even for the content
this.forcedChange = true;
await this.save();
this.content = content;
const pojo = {
@@ -128,10 +139,6 @@ class Note extends Entity {
await sql.upsert("note_contents", "noteId", pojo);
await syncTableService.addNoteContentSync(this.noteId);
this.forcedChange = true;
await this.save();
}
/** @returns {Promise} */
@@ -722,10 +729,6 @@ class Note extends Entity {
delete pojo.content;
delete pojo.contentHash;
}
async afterSaving() {
noteFulltextService.triggerNoteFulltextUpdate(this.noteId);
}
}
module.exports = Note;
+8 -13
View File
@@ -39,6 +39,7 @@ import linkService from './services/link.js';
import noteAutocompleteService from './services/note_autocomplete.js';
import macInit from './services/mac_init.js';
import cssLoader from './services/css_loader.js';
import dateNoteService from './services/date_notes.js';
// required for CKEditor image upload plugin
window.glob.getActiveNode = treeService.getActiveNode;
@@ -114,20 +115,14 @@ $("body").on("click", "a.external", function () {
});
if (utils.isElectron()) {
require('electron').ipcRenderer.on('create-day-sub-note', async function(event, parentNoteId) {
await treeService.activateNote(parentNoteId);
require('electron').ipcRenderer.on('create-day-sub-note', async function(event) {
const todayNote = await dateNoteService.getTodayNote();
const node = await treeService.expandToNote(todayNote.noteId);
setTimeout(async () => {
const parentNode = treeService.getActiveNode();
const {note} = await treeService.createNote(parentNode, parentNode.data.noteId, 'into', {
type: "text",
isProtected: parentNode.data.isProtected
});
await treeService.activateNote(note.noteId);
}, 500);
await treeService.createNote(node, todayNote.noteId, 'into', {
type: "text",
isProtected: node.data.isProtected
});
});
}
+2 -2
View File
@@ -86,10 +86,10 @@ $("#note-menu-button").click(async e => {
enabled: isNotRoot && parentNote.type !== 'search' }
];
contextMenuWidget.initContextMenu(e, items, (event, cmd) => {
contextMenuWidget.initContextMenu(e, items, async (event, cmd) => {
if (cmd === "insertNoteAfter") {
const parentNoteId = node.data.parentNoteId;
const isProtected = treeUtils.getParentProtectedStatus(node);
const isProtected = await treeUtils.getParentProtectedStatus(node);
treeService.createNote(node, parentNoteId, 'after', { isProtected: isProtected });
}
+17 -16
View File
@@ -3,10 +3,10 @@ import utils from './utils.js';
import server from './server.js';
import infoService from "./info.js";
import treeCache from "./tree_cache.js";
import treeBuilder from "./tree_builder.js";
import hoistedNoteService from "./hoisted_note.js";
async function moveBeforeNode(nodesToMove, beforeNode) {
nodesToMove = filterRootNote(nodesToMove);
nodesToMove = await filterRootNote(nodesToMove);
if (beforeNode.data.noteId === 'root') {
alert('Cannot move notes before root note.');
@@ -30,9 +30,9 @@ async function moveBeforeNode(nodesToMove, beforeNode) {
}
async function moveAfterNode(nodesToMove, afterNode) {
nodesToMove = filterRootNote(nodesToMove);
nodesToMove = await filterRootNote(nodesToMove);
if (afterNode.data.noteId === 'root') {
if (afterNode.data.noteId === 'root' || afterNode.data.noteId === await hoistedNoteService.getHoistedNoteId()) {
alert('Cannot move notes after root note.');
return;
}
@@ -56,7 +56,7 @@ async function moveAfterNode(nodesToMove, afterNode) {
}
async function moveToNode(nodesToMove, toNode) {
nodesToMove = filterRootNote(nodesToMove);
nodesToMove = await filterRootNote(nodesToMove);
for (const nodeToMove of nodesToMove) {
const resp = await server.put('branches/' + nodeToMove.data.branchId + '/move-to/' + toNode.data.noteId);
@@ -77,13 +77,8 @@ async function moveToNode(nodesToMove, toNode) {
}
}
function filterRootNote(nodes) {
// some operations are not possible on root notes
return nodes.filter(node => node.data.noteId !== 'root');
}
async function deleteNodes(nodes) {
nodes = filterRootNote(nodes);
nodes = await filterRootNote(nodes);
if (nodes.length === 0 || !confirm('Are you sure you want to delete select note(s) and all the sub-notes?')) {
return;
@@ -93,8 +88,6 @@ async function deleteNodes(nodes) {
await server.remove('branches/' + node.data.branchId);
}
// following code assumes that nodes contain only top-most selected nodes - getSelectedNodes has been
// called with stopOnParent=true
let next = nodes[nodes.length - 1].getNextSibling();
@@ -103,7 +96,7 @@ async function deleteNodes(nodes) {
next = nodes[0].getPrevSibling();
}
if (!next && !utils.isTopLevelNode(nodes[0])) {
if (!next && !hoistedNoteService.isTopLevelNode(nodes[0])) {
next = nodes[0].getParent();
}
@@ -130,7 +123,7 @@ async function deleteNodes(nodes) {
}
async function moveNodeUpInHierarchy(node) {
if (utils.isRootNode(node) || utils.isTopLevelNode(node)) {
if (await hoistedNoteService.isRootNode(node) || await hoistedNoteService.isTopLevelNode(node)) {
return;
}
@@ -141,7 +134,7 @@ async function moveNodeUpInHierarchy(node) {
return;
}
if (!utils.isTopLevelNode(node) && node.getParent().getChildren().length <= 1) {
if (!hoistedNoteService.isTopLevelNode(node) && node.getParent().getChildren().length <= 1) {
node.getParent().folder = false;
node.getParent().renderTitle();
}
@@ -203,6 +196,14 @@ async function changeNode(func, node, beforeNoteId = null, afterNoteId = null) {
}
}
async function filterRootNote(nodes) {
const hoistedNoteId = await hoistedNoteService.getHoistedNoteId();
return nodes.filter(node =>
node.data.noteId !== 'root'
&& node.data.noteId !== hoistedNoteId);
}
export default {
moveBeforeNode,
moveAfterNode,
@@ -42,7 +42,7 @@ function initContextMenu(event, contextMenuItems, selectContextMenuItem) {
});
if (item.enabled !== undefined && !item.enabled) {
$link.addClass("disabled");
$item.addClass("disabled");
}
if (item.items) {
@@ -0,0 +1,41 @@
import treeCache from "./tree_cache.js";
import server from "./server.js";
/** @return {NoteShort} */
async function getTodayNote() {
return await getDateNote(dayjs().format("YYYY-MM-DD"));
}
/** @return {NoteShort} */
async function getDateNote(date) {
const note = await server.get('date-notes/date/' + date);
await treeCache.reloadParents(note.noteId);
return await treeCache.getNote(note.noteId);
}
/** @return {NoteShort} */
async function getMonthNote(month) {
const note = await server.get('date-notes/month/' + month);
await treeCache.reloadParents(note.noteId);
return await treeCache.getNote(note.noteId);
}
/** @return {NoteShort} */
async function getYearNote(year) {
const note = await server.get('date-notes/year/' + year);
await treeCache.reloadParents(note.noteId);
return await treeCache.getNote(note.noteId);
}
export default {
getTodayNote,
getDateNote,
getMonthNote,
getYearNote
}
+24 -17
View File
@@ -103,6 +103,7 @@ function registerEntrypoints() {
utils.bindShortcut('f5', utils.reloadApp);
$("#reload-frontend-button").click(utils.reloadApp);
utils.bindShortcut('ctrl+r', utils.reloadApp);
$("#open-dev-tools-button").toggle(utils.isElectron());
@@ -118,27 +119,33 @@ function registerEntrypoints() {
$("#open-dev-tools-button").click(openDevTools);
}
function openInPageSearch() {
if (utils.isElectron()) {
const $searchWindowWebview = $(".electron-in-page-search-window");
$searchWindowWebview.show();
let findInPage;
const searchInPage = require('electron-in-page-search').default;
const {remote} = require('electron');
if (utils.isElectron()) {
const { remote } = require('electron');
const { FindInPage } = require('electron-find');
const inPageSearch = searchInPage(remote.getCurrentWebContents(), {
searchWindowWebview: $searchWindowWebview[0],
//openDevToolsOfSearchWindow: true,
customCssPath: '/libraries/electron-in-page-search/default-style.css'
});
inPageSearch.openSearchWindow();
return false;
}
findInPage = new FindInPage(remote.getCurrentWebContents(), {
offsetTop: 10,
offsetRight: 10,
boxBgColor: 'var(--main-background-color)',
boxShadowColor: '#000',
inputColor: 'var(--input-text-color)',
inputBgColor: 'var(--input-background-color)',
inputFocusColor: '#555',
textColor: 'var(--main-text-color)',
textHoverBgColor: '#555',
caseSelectedColor: 'var(--main-border-color)'
});
}
utils.bindShortcut('ctrl+f', openInPageSearch);
if (utils.isElectron()) {
utils.bindShortcut('ctrl+f', () => {
findInPage.openFindWindow();
return false;
});
}
// FIXME: do we really need these at this point?
utils.bindShortcut("ctrl+shift+up", () => {
@@ -8,6 +8,7 @@ import noteDetailService from './note_detail.js';
import noteTypeService from './note_type.js';
import noteTooltipService from './note_tooltip.js';
import protectedSessionService from'./protected_session.js';
import dateNotesService from'./date_notes.js';
/**
* This is the main frontend API interface for scripts. It's published in the local "api" object.
@@ -25,6 +26,9 @@ function FrontendScriptApi(startNote, currentNote, originEntity = null) {
/** @property {object|null} entity whose event triggered this execution */
this.originEntity = originEntity;
// to keep consistency with backend API
this.dayjs = dayjs;
/**
* Activates note in the tree and in the note detail.
*
@@ -131,6 +135,46 @@ function FrontendScriptApi(startNote, currentNote, originEntity = null) {
}
};
/**
* This is a powerful search method - you can search by attributes and their values, e.g.:
* "@dateModified =* MONTH AND @log". See full documentation for all options at: https://github.com/zadam/trilium/wiki/Search
*
* @method
* @param {string} searchString
* @returns {Promise<NoteShort[]>}
*/
this.searchForNotes = async searchString => {
const noteIds = await this.runOnServer(async searchString => {
const notes = await api.searchForNotes(searchString);
return notes.map(note => note.noteId);
}, [searchString]);
return await treeCache.getNotes(noteIds);
};
/**
* This is a powerful search method - you can search by attributes and their values, e.g.:
* "@dateModified =* MONTH AND @log". See full documentation for all options at: https://github.com/zadam/trilium/wiki/Search
*
* @method
* @param {string} searchString
* @returns {Promise<NoteShort|null>}
*/
this.searchForNote = async searchString => {
const notes = await this.searchForNotes(searchString);
return notes.length > 0 ? notes[0] : null;
};
/**
* Returns note by given noteId. If note is missing from cache, it's loaded.
**
* @param {string} noteId
* @return {Promise<NoteShort>}
*/
this.getNote = async noteId => await treeCache.getNote(noteId);
/**
* Returns list of notes. If note is missing from cache, it's loaded.
*
@@ -143,6 +187,18 @@ function FrontendScriptApi(startNote, currentNote, originEntity = null) {
*/
this.getNotes = async (noteIds, silentNotFoundError = false) => await treeCache.getNotes(noteIds, silentNotFoundError);
/**
* @param {string} noteId
* @method
*/
this.reloadChildren = async noteId => await treeCache.reloadChildren(noteId);
/**
* @param {string} noteId
* @method
*/
this.reloadParents = async noteId => await treeCache.reloadParents(noteId);
/**
* Instance name identifies particular Trilium instance. It can be useful for scripts
* if some action needs to happen on only one specific instance.
@@ -210,6 +266,12 @@ function FrontendScriptApi(startNote, currentNote, originEntity = null) {
*/
this.getActiveNote = noteDetailService.getActiveNote;
/**
* @method
* @returns {Promise<string>} returns note path of active note
*/
this.getActiveNotePath = treeService.getActiveNotePath;
/**
* This method checks whether user navigated away from the note from which the scripts has been started.
* This is necessary because script execution is async and by the time it is finished, the user might have
@@ -257,6 +319,41 @@ function FrontendScriptApi(startNote, currentNote, originEntity = null) {
* @method
*/
this.protectActiveNote = protectedSessionService.protectNoteAndSendToServer;
/**
* Returns date-note for today. If it doesn't exist, it is automatically created.
*
* @method
* @return {Promise<NoteShort>}
*/
this.getTodayNote = dateNotesService.getTodayNote;
/**
* Returns date-note. If it doesn't exist, it is automatically created.
*
* @method
* @param {string} date - e.g. "2019-04-29"
* @return {Promise<NoteShort>}
*/
this.getDateNote = dateNotesService.getDateNote;
/**
* Returns month-note. If it doesn't exist, it is automatically created.
*
* @method
* @param {string} month - e.g. "2019-04"
* @return {Promise<NoteShort>}
*/
this.getMonthNote = dateNotesService.getMonthNote;
/**
* Returns year-note. If it doesn't exist, it is automatically created.
*
* @method
* @param {string} year - e.g. "2019"
* @return {Promise<NoteShort>}
*/
this.getYearNote = dateNotesService.getYearNote;
}
export default FrontendScriptApi;
@@ -26,8 +26,20 @@ async function unhoist() {
await setHoistedNoteId('root');
}
async function isTopLevelNode(node) {
return await isRootNode(node.getParent());
}
async function isRootNode(node) {
// even though check for 'root' should not be necessary, we keep it just in case
return node.data.noteId === "root"
|| node.data.noteId === await getHoistedNoteId();
}
export default {
getHoistedNoteId,
setHoistedNoteId,
unhoist
unhoist,
isTopLevelNode,
isRootNode
}
@@ -270,7 +270,7 @@ async function showChildrenOverview() {
$childrenOverview.empty();
const notePath = treeService.getActiveNotePath();
const notePath = await treeService.getActiveNotePath();
for (const childBranch of await note.getChildBranches()) {
const link = $('<a>', {
@@ -34,6 +34,9 @@ async function show() {
else {
$previewRow.hide();
}
// open doesn't work for protected notes since it works through browser which isn't in protected session
$openButton.toggle(!activeNote.isProtected);
}
$downloadButton.click(() => utils.download(getFileUrl()));
@@ -51,7 +54,7 @@ $openButton.click(() => {
function getFileUrl() {
// electron needs absolute URL so we extract current host, port, protocol
return utils.getHost() + "/api/notes/" + noteDetailService.getActiveNoteId();
return utils.getHost() + "/api/notes/" + noteDetailService.getActiveNoteId() + "/download";
}
export default {
@@ -7,8 +7,8 @@ function setupGlobalTooltip() {
$(document).on("mouseenter", "a", mouseEnterHandler);
$(document).on("mouseleave", "a", mouseLeaveHandler);
// close any tooltip after click, this fixes the problem that sometimes tooltips remained on the screen
//$(document).on("click", () => $('.tooltip').remove());
// close any note tooltip after click, this fixes the problem that sometimes tooltips remained on the screen
$(document).on("click", () => $('.note-tooltip').remove());
}
function setupElementTooltip($el) {
@@ -58,7 +58,8 @@ async function mouseEnterHandler() {
trigger: 'manual',
boundary: 'window',
title: html,
html: true
html: true,
template: '<div class="tooltip note-tooltip" role="tooltip"><div class="arrow"></div><div class="tooltip-inner"></div></div>'
});
$(this).tooltip('show');
@@ -80,12 +80,17 @@ async function doSearch(searchText) {
return;
}
const results = await server.get('search/' + encodeURIComponent(searchText));
const response = await server.get('search/' + encodeURIComponent(searchText));
if (!response.success) {
infoService.showError("Search failed.", 3000);
return;
}
$searchResultsInner.empty();
$searchResults.show();
for (const result of results) {
for (const result of response.results) {
const link = $('<a>', {
href: 'javascript:',
text: result.title
+44 -26
View File
@@ -39,10 +39,8 @@ function getActiveNode() {
return $tree.fancytree("getActiveNode");
}
function getActiveNotePath() {
const node = getActiveNode();
return treeUtils.getNotePath(node);
async function getActiveNotePath() {
return getHashValueFromAddress();
}
async function getNodesByBranchId(branchId) {
@@ -110,18 +108,13 @@ async function expandToNote(notePath, expandOpts) {
let node = getNode(childNoteId, parentNoteId);
if (!node && parentNoteId) {
const parents = getNodesByNoteId(parentNoteId);
for (const parent of parents) {
// force load parents. This is useful when fancytree doesn't contain recently created notes yet.
await parent.load(true);
}
await reloadNote(parentNoteId);
node = getNode(childNoteId, parentNoteId);
}
if (!node) {
console.error(`Can't find node for noteId=${childNoteId} with parentNoteId=${parentNoteId}`);
console.error(`Can't find node for noteId=${childNoteId} with parentNoteId=${parentNoteId} and hoistedNoteId=${hoistedNoteId}`);
}
if (childNoteId === noteId) {
@@ -330,16 +323,16 @@ async function setExpandedToServer(branchId, isExpanded) {
function addRecentNote(branchId, notePath) {
setTimeout(async () => {
// we include the note into recent list only if the user stayed on the note at least 5 seconds
if (notePath && notePath === getActiveNotePath()) {
if (notePath && notePath === await getActiveNotePath()) {
await server.post('recent-notes', { branchId, notePath });
}
}, 1500);
}
function setCurrentNotePathToHash(node) {
async function setCurrentNotePathToHash(node) {
utils.assertArguments(node);
const activeNotePath = treeUtils.getNotePath(node);
const activeNotePath = await treeUtils.getNotePath(node);
const currentBranchId = node.data.branchId;
document.location.hash = activeNotePath;
@@ -371,20 +364,26 @@ async function treeInitialized() {
const noteId = treeUtils.getNoteIdFromNotePath(startNotePath);
if (!await treeCache.getNote(noteId)) {
if (!await treeCache.noteExists(noteId)) {
// note doesn't exist so don't try to activate it
startNotePath = null;
}
if (startNotePath) {
const node = await activateNote(startNotePath);
// this is weird but it looks like even though init event has been called, but we the tree still
// can't find nodes for given path which causes double loading of data. Little timeout fixes this.
setTimeout(async () => {
const node = await activateNote(startNotePath);
// looks like this this doesn't work when triggered immediatelly after activating node
// so waiting a second helps
setTimeout(() => node.makeVisible({scrollIntoView: true}), 1000);
// looks like this this doesn't work when triggered immediatelly after activating node
// so waiting a second helps
setTimeout(() => node.makeVisible({scrollIntoView: true}), 1000);
}, 100);
}
}
let ignoreNextActivationNoteId = null;
function initFancyTree(tree) {
utils.assertArguments(tree);
@@ -413,14 +412,25 @@ function initFancyTree(tree) {
return false;
}
},
activate: (event, data) => {
beforeActivate: (event, data) => {
// this is for the case when tree reload has been called and we don't want to
if (ignoreNextActivationNoteId && getActiveNode() !== null) {
return false;
}
},
activate: async (event, data) => {
const node = data.node;
const noteId = node.data.noteId;
if (ignoreNextActivationNoteId === noteId) {
ignoreNextActivationNoteId = null;
return;
}
// click event won't propagate so let's close context menu manually
contextMenuWidget.hideContextMenu();
setCurrentNotePathToHash(node);
await setCurrentNotePathToHash(node);
noteDetailService.switchToNote(noteId);
@@ -490,7 +500,11 @@ function getTree() {
async function reload() {
const notes = await loadTree();
// this will also reload the note content
// make sure the reload won't trigger reactivation. This is important especially in cases where we wait for the reload
// to finish to then activate some other note. But since the activate() event is called asynchronously, it may be called
// (or finished calling) after we switched to a different note.
ignoreNextActivationNoteId = getActiveNode().data.noteId;
await getTree().reload(notes);
}
@@ -722,7 +736,7 @@ messagingService.subscribeToSyncMessages(syncData => {
utils.bindShortcut('ctrl+o', async () => {
const node = getActiveNode();
const parentNoteId = node.data.parentNoteId;
const isProtected = treeUtils.getParentProtectedStatus(node);
const isProtected = await treeUtils.getParentProtectedStatus(node);
if (node.data.noteId === 'root' || node.data.noteId === await hoistedNoteService.getHoistedNoteId()) {
return;
@@ -760,6 +774,8 @@ async function checkFolderStatus(node) {
}
async function reloadNote(noteId) {
await treeCache.reloadChildren(noteId);
for (const node of getNodesByNoteId(noteId)) {
await node.load(true);
@@ -773,11 +789,12 @@ utils.bindShortcut('ctrl+p', createNoteInto);
utils.bindShortcut('ctrl+.', scrollToActiveNote);
$(window).bind('hashchange', function() {
$(window).bind('hashchange', async function() {
if (isNotePathInAddress()) {
const notePath = getHashValueFromAddress();
const noteId = notePath.split("/").pop();
if (notePath !== '-' && getActiveNotePath() !== notePath) {
if (noteId !== '-' && noteId !== getActiveNode().data.noteId) {
console.debug("Switching to " + notePath + " because of hash change");
activateNote(notePath);
@@ -816,5 +833,6 @@ export default {
getNodesByNoteId,
checkFolderStatus,
reloadNote,
loadTreeCache
loadTreeCache,
expandToNote
};
+58 -1
View File
@@ -5,6 +5,9 @@ import infoService from "./info.js";
import messagingService from "./messaging.js";
import server from "./server.js";
/**
* TreeCache keeps a read only cache of note tree structure in frontend's memory.
*/
class TreeCache {
constructor() {
this.init();
@@ -17,8 +20,12 @@ class TreeCache {
}
init() {
/** @type {Object.<string, string>} */
this.parents = {};
/** @type {Object.<string, string>} */
this.children = {};
/** @type {Object.<string, string>} */
this.childParentToBranch = {};
/** @type {Object.<string, NoteShort>} */
@@ -46,6 +53,45 @@ class TreeCache {
}
}
/**
* Reload children of given noteId.
*/
async reloadChildren(noteId) {
const resp = await server.post('tree/load', { noteIds: [noteId] });
for (const childNoteId of this.children[noteId] || []) {
this.parents[childNoteId] = this.parents[childNoteId].filter(p => p !== noteId);
const branchId = this.getBranchIdByChildParent(childNoteId, noteId);
delete this.branches[branchId];
delete this.childParentToBranch[childNoteId + '-' + noteId];
}
this.children[noteId] = [];
delete this.notes[noteId];
this.addResp(resp.notes, resp.branches, resp.relations);
}
/**
* Reloads parents of given noteId - useful when new note is created to make sure note is loaded
* in a correct order.
*/
async reloadParents(noteId) {
// to be able to find parents we need first to make sure it is actually loaded
await this.getNote(noteId);
for (const parentNoteId of this.parents[noteId] || []) {
await this.reloadChildren(parentNoteId);
}
// this is done to load the new parents for the noteId
await this.reloadChildren(noteId);
}
/** @return {Promise<NoteShort[]>} */
async getNotes(noteIds, silentNotFoundError = false) {
const missingNoteIds = noteIds.filter(noteId => this.notes[noteId] === undefined);
@@ -53,6 +99,10 @@ class TreeCache {
const resp = await server.post('tree/load', { noteIds: missingNoteIds });
this.addResp(resp.notes, resp.branches, resp.relations);
for (const note of resp.notes) {
await this.reloadParents(note.noteId);
}
}
return noteIds.map(noteId => {
@@ -67,7 +117,14 @@ class TreeCache {
}).filter(note => note !== null);
}
/** @return NoteShort */
/** @return {Promise<boolean>} */
async noteExists(noteId) {
const notes = await this.getNotes([noteId], true);
return notes.length === 1;
}
/** @return {Promise<NoteShort>} */
async getNote(noteId) {
if (noteId === 'none') {
return null;
@@ -110,11 +110,11 @@ async function getTopLevelItems(event) {
items: insertChildNoteEnabled ? getNoteTypeItems("insertChildNote") : null,
enabled: insertChildNoteEnabled },
{ title: "Delete <kbd>Delete</kbd>", cmd: "delete", uiIcon: "trash",
enabled: isNotRoot && parentNote.type !== 'search' },
enabled: isNotRoot && !isHoisted && parentNote.type !== 'search' },
{ title: "----" },
isHoisted ? null : { title: "Hoist note <kbd>Ctrl-H</kbd>", cmd: "hoist", uiIcon: "arrow-up" },
isHoisted ? null : { title: "Hoist note <kbd>Ctrl-H</kbd>", cmd: "hoist", uiIcon: "empty" },
!isHoisted || !isNotRoot ? null : { title: "Unhoist note <kbd>Ctrl-H</kbd>", cmd: "unhoist", uiIcon: "arrow-up" },
{ title: "Edit branch prefix <kbd>F2</kbd>", cmd: "editBranchPrefix", uiIcon: "pencil",
{ title: "Edit branch prefix <kbd>F2</kbd>", cmd: "editBranchPrefix", uiIcon: "empty",
enabled: isNotRoot && parentNote.type !== 'search'},
{ title: "----" },
{ title: "Protect subtree", cmd: "protectSubtree", uiIcon: "shield-check" },
@@ -129,14 +129,14 @@ async function getTopLevelItems(event) {
{ title: "Paste after", cmd: "pasteAfter", uiIcon: "clipboard",
enabled: clipboardIds.length > 0 && isNotRoot && parentNote.type !== 'search' },
{ title: "----" },
{ title: "Export", cmd: "export", uiIcon: "arrow-up-right",
{ title: "Export", cmd: "export", uiIcon: "empty",
enabled: note.type !== 'search' },
{ title: "Import into note", cmd: "importIntoNote", uiIcon: "arrow-down-left",
{ title: "Import into note", cmd: "importIntoNote", uiIcon: "empty",
enabled: note.type !== 'search' },
{ title: "----" },
{ title: "Collapse subtree <kbd>Alt+-</kbd>", cmd: "collapseSubtree", uiIcon: "align-justify" },
{ title: "Force note sync", cmd: "forceNoteSync", uiIcon: "refresh" },
{ title: "Sort alphabetically <kbd>Alt+S</kbd>", cmd: "sortAlphabetically", uiIcon: "arrows-v" }
{ title: "Sort alphabetically <kbd>Alt+S</kbd>", cmd: "sortAlphabetically", uiIcon: "empty" }
].filter(row => row !== null);
}
@@ -156,13 +156,13 @@ async function getContextMenuItems(event) {
return items;
}
function selectContextMenuItem(event, cmd) {
async function selectContextMenuItem(event, cmd) {
// context menu is always triggered on current node
const node = treeService.getActiveNode();
if (cmd.startsWith("insertNoteAfter")) {
const parentNoteId = node.data.parentNoteId;
const isProtected = treeUtils.getParentProtectedStatus(node);
const isProtected = await treeUtils.getParentProtectedStatus(node);
const type = cmd.split("_")[1];
treeService.createNote(node, parentNoteId, 'after', {
@@ -109,8 +109,8 @@ const keyBindings = {
return false;
},
"backspace": node => {
if (!utils.isRootNode(node)) {
"backspace": async node => {
if (!await hoistedNoteService.isRootNode(node)) {
node.getParent().setActive().then(treeService.clearSelectedNodes);
}
},
+11 -5
View File
@@ -1,10 +1,11 @@
import utils from './utils.js';
import hoistedNoteService from './hoisted_note.js';
import treeCache from "./tree_cache.js";
const $tree = $("#tree");
function getParentProtectedStatus(node) {
return utils.isRootNode(node) ? 0 : node.getParent().data.isProtected;
async function getParentProtectedStatus(node) {
return await hoistedNoteService.isRootNode(node) ? 0 : node.getParent().data.isProtected;
}
function getNodeByKey(key) {
@@ -21,10 +22,15 @@ function getNoteIdFromNotePath(notePath) {
return path[path.length - 1];
}
function getNotePath(node) {
async function getNotePath(node) {
if (!node) {
console.error("Node is null");
return "";
}
const path = [];
while (node && !utils.isRootNode(node)) {
while (node && !await hoistedNoteService.isRootNode(node)) {
if (node.data.noteId) {
path.push(node.data.noteId);
}
@@ -32,7 +38,7 @@ function getNotePath(node) {
node = node.getParent();
}
path.push('root');
path.push(node.data.noteId); // root or hoisted noteId
return path.reverse().join("/");
}
-10
View File
@@ -60,14 +60,6 @@ function assertArguments() {
}
}
function isTopLevelNode(node) {
return isRootNode(node.getParent());
}
function isRootNode(node) {
return node.data.noteId === "root";
}
function escapeHtml(str) {
return $('<div/>').text(str).html();
}
@@ -211,8 +203,6 @@ export default {
isElectron,
isMac,
assertArguments,
isTopLevelNode,
isRootNode,
escapeHtml,
stopWatch,
formatValueWithWhitespace,
+5
View File
@@ -928,4 +928,9 @@ a.external:after, a[href^="http://"]:after, a[href^="https://"]:after {
.card {
background-color: inherit !important;
border-color: var(--main-border-color) !important;
}
.jam-empty {
width: 1em;
display: inline-block;
}
+21
View File
@@ -0,0 +1,21 @@
"use strict";
const dateNoteService = require('../../services/date_notes');
async function getDateNote(req) {
return await dateNoteService.getDateNote(req.params.date);
}
async function getMonthNote(req) {
return await dateNoteService.getMonthNote(req.params.month);
}
async function getYearNote(req) {
return await dateNoteService.getYearNote(req.params.year);
}
module.exports = {
getDateNote,
getMonthNote,
getYearNote
};
+1 -1
View File
@@ -14,7 +14,7 @@ async function returnImage(req, res) {
else if (image.type !== 'image') {
return res.sendStatus(400);
}
else if (image.data === null) {
else if (image.isDeleted || image.data === null) {
res.set('Content-Type', 'image/png');
return res.send(fs.readFileSync(RESOURCE_DIR + '/db/image-deleted.png'));
}
+11 -1
View File
@@ -9,7 +9,17 @@ const searchService = require('../../services/search');
async function searchNotes(req) {
const noteIds = await searchService.searchForNoteIds(req.params.searchString);
return noteIds.map(noteCacheService.getNotePath).filter(res => !!res);
try {
return {
success: true,
results: noteIds.map(noteCacheService.getNotePath).filter(res => !!res)
}
}
catch {
return {
success: false
}
}
}
async function searchFromNote(req) {
+2 -2
View File
@@ -24,7 +24,7 @@ async function login(req, res) {
}
req.session.loggedIn = true;
res.redirect('/');
res.redirect('..');
});
}
else {
@@ -44,7 +44,7 @@ function logout(req, res) {
req.session.regenerate(() => {
req.session.loggedIn = false;
res.redirect('/');
res.redirect('login');
});
}
+5
View File
@@ -30,6 +30,7 @@ const scriptRoute = require('./api/script');
const senderRoute = require('./api/sender');
const filesRoute = require('./api/file_upload');
const searchRoute = require('./api/search');
const dateNotesRoute = require('./api/date_notes');
const log = require('../services/log');
const express = require('express');
@@ -153,6 +154,10 @@ function register(app) {
apiRoute(GET, '/api/attributes/names', attributesRoute.getAttributeNames);
apiRoute(GET, '/api/attributes/values/:attributeName', attributesRoute.getValuesForAttribute);
apiRoute(GET, '/api/date-notes/date/:date', dateNotesRoute.getDateNote);
apiRoute(GET, '/api/date-notes/month/:month', dateNotesRoute.getMonthNote);
apiRoute(GET, '/api/date-notes/year/:year', dateNotesRoute.getYearNote);
route(GET, '/api/images/:noteId/:filename', [auth.checkApiAuthOrElectron], imageRoute.returnImage);
route(POST, '/api/images', [auth.checkApiAuthOrElectron, uploadMiddleware, csrfMiddleware], imageRoute.uploadImage, apiResultHandler);
+1 -1
View File
@@ -4,7 +4,7 @@ const build = require('./build');
const packageJson = require('../../package');
const {TRILIUM_DATA_DIR} = require('./data_dir');
const APP_DB_VERSION = 131;
const APP_DB_VERSION = 133;
const SYNC_VERSION = 8;
module.exports = {
+15 -1
View File
@@ -99,10 +99,24 @@ function BackendScriptApi(currentNote, apiParams) {
*
* @method
* @param {string} searchString
* @returns ${Promise<Note[]>}
* @returns {Promise<Note[]>}
*/
this.searchForNotes = searchService.searchForNotes;
/**
* This is a powerful search method - you can search by attributes and their values, e.g.:
* "@dateModified =* MONTH AND @log". See full documentation for all options at: https://github.com/zadam/trilium/wiki/Search
*
* @method
* @param {string} searchString
* @returns {Promise<Note|null>}
*/
this.searchForNote = async searchString => {
const notes = await searchService.searchForNotes(searchString);
return notes.length > 0 ? notes[0] : null;
};
/**
* Retrieves notes with given label name & value
*
+1 -1
View File
@@ -1 +1 @@
module.exports = { buildDate:"2019-04-01T21:50:32+02:00", buildRevision: "f47f3461e666d007668e03282f86b028012f7bef" };
module.exports = { buildDate:"2019-04-22T22:53:04+02:00", buildRevision: "eac5ec6e267a64c77aa6a1f791f95b15cc549869" };
+15 -17
View File
@@ -67,8 +67,8 @@ module.exports = function(filters, selectedColumns = 'notes.*') {
const params = [];
for (const filter of filters) {
if (filter.name.toLowerCase() === 'orderby') {
continue; // orderby is not real filter
if (['orderby', 'limit'].includes(filter.name.toLowerCase())) {
continue; // orderby and limit are not real filters
}
where += " " + filter.relation + " ";
@@ -82,18 +82,8 @@ module.exports = function(filters, selectedColumns = 'notes.*') {
where += `${accessor} IS NULL`;
}
else if (filter.operator === '=' || filter.operator === '!=') {
if (filter.name === 'text') {
const safeSearchText = utils.sanitizeSql(filter.value);
const not = filter.operator.includes("!") ? "NOT" : "";
// fulltext needs to use subselect because fulltext doesn't support OR operations at all
// which makes it impossible to combine more operations together
where += `notes.noteId ${not} IN (SELECT noteId FROM note_fulltext WHERE note_fulltext MATCH '${safeSearchText}')`;
}
else {
where += `${accessor} ${filter.operator} ?`;
params.push(filter.value);
}
where += `${accessor} ${filter.operator} ?`;
params.push(filter.value);
}
else if (filter.operator === '*=' || filter.operator === '!*=') {
where += `${accessor}`
@@ -106,9 +96,17 @@ module.exports = function(filters, selectedColumns = 'notes.*') {
+ ` LIKE ` + utils.prepareSqlForLike('', filter.value, '%');
}
else if (filter.operator === '*=*' || filter.operator === '!*=*') {
where += `${accessor}`
+ (filter.operator.includes('!') ? ' NOT' : '')
+ ` LIKE ` + utils.prepareSqlForLike('%', filter.value, '%');
const columns = filter.name === 'text' ? [getAccessor("title"), getAccessor("content")] : [accessor];
let condition = "(" + columns.map(column =>
`${column}` + ` LIKE ` + utils.prepareSqlForLike('%', filter.value, '%'))
.join(" OR ") + ")";
if (filter.operator.includes('!')) {
condition = "NOT(" + condition + ")";
}
where += condition;
}
else if ([">", ">=", "<", "<="].includes(filter.operator)) {
let floatParam;
+1 -37
View File
@@ -5,7 +5,6 @@ const sqlInit = require('./sql_init');
const log = require('./log');
const messagingService = require('./messaging');
const syncMutexService = require('./sync_mutex');
const noteFulltextService = require('./note_fulltext');
const repository = require('./repository');
const cls = require('./cls');
const syncTableService = require('./sync_table');
@@ -358,42 +357,6 @@ async function findLogicIssues() {
logFix(`Removed link ${linkId} because target note ${targetNoteId} is also deleted.`);
});
// this doesn't try to find notes for which the fulltext doesn't exist at all - reason is the "archived" label
// which is inheritable and not easy to filter out such rows in consistency check which would mean that it would
// find some false positives.
await findAndFixIssues(`
SELECT
noteId
FROM
notes
JOIN note_contents USING(noteId)
JOIN note_fulltext USING(noteId)
WHERE
notes.isDeleted = 0
AND notes.isProtected = 0
AND (note_fulltext.noteId IS NULL
OR note_fulltext.titleHash != notes.hash
OR note_fulltext.contentHash != note_contents.hash)`,
async ({noteId}) => {
noteFulltextService.triggerNoteFulltextUpdate(noteId);
logFix(`Triggered fulltext update of note ${noteId} since it was out of sync.`);
});
await findAndFixIssues(`
SELECT
noteId
FROM
notes
JOIN note_fulltext USING(noteId)
WHERE
(notes.isDeleted = 1 OR notes.isProtected = 1)`,
async ({noteId}) => {
noteFulltextService.triggerNoteFulltextUpdate(noteId);
logFix(`Triggered fulltext update of note ${noteId} since it was out of sync.`);
});
}
async function runSyncRowChecks(entityName, key) {
@@ -430,6 +393,7 @@ async function runSyncRowChecks(entityName, key) {
async function findSyncRowsIssues() {
await runSyncRowChecks("notes", "noteId");
await runSyncRowChecks("note_contents", "noteId");
await runSyncRowChecks("note_revisions", "noteRevisionId");
await runSyncRowChecks("branches", "branchId");
await runSyncRowChecks("recent_notes", "branchId");
+4
View File
@@ -36,6 +36,10 @@ const DIR_NAME = 'trilium-data';
function getTriliumDataDir() {
if (process.env.TRILIUM_DATA_DIR) {
if (!fs.existsSync(process.env.TRILIUM_DATA_DIR)) {
fs.mkdirSync(process.env.TRILIUM_DATA_DIR, 0o700);
}
return process.env.TRILIUM_DATA_DIR;
}
-4
View File
@@ -1,7 +1,6 @@
const eventService = require('./events');
const scriptService = require('./script');
const treeService = require('./tree');
const messagingService = require('./messaging');
const log = require('./log');
const Attribute = require('../entities/attribute');
@@ -29,9 +28,6 @@ eventService.subscribe(eventService.NOTE_TITLE_CHANGED, async note => {
for (const parent of parents) {
if (await parent.hasLabel("sorted")) {
await treeService.sortNotesAlphabetically(parent.noteId);
messagingService.refreshTree();
break; // sending the message once is enough
}
}
}
+7
View File
@@ -51,6 +51,13 @@ async function shrinkImage(buffer, originalName) {
log.error("Failed to optimize image '" + originalName + "\nStack: " + e.stack);
finalImageBuffer = resizedImage;
}
// if resizing & shrinking did not help with size then save the original
// (can happen when e.g. resizing PNG into JPEG)
if (finalImageBuffer.byteLength >= buffer.byteLength) {
finalImageBuffer = buffer;
}
return finalImageBuffer;
}
+11
View File
@@ -376,6 +376,16 @@ eventService.subscribe([eventService.ENTITY_CHANGED, eventService.ENTITY_DELETED
}
});
/**
* @param noteId
* @returns {boolean} - true if note exists (is not deleted) and is not archived.
*/
function isAvailable(noteId) {
const notePath = getNotePath(noteId);
return !!notePath;
}
eventService.subscribe(eventService.ENTER_PROTECTED_SESSION, () => {
if (loaded) {
loadProtectedNotes();
@@ -388,5 +398,6 @@ module.exports = {
findNotes,
getNotePath,
getNoteTitleForPath,
isAvailable,
load
};
-59
View File
@@ -1,59 +0,0 @@
const sql = require('./sql');
const repository = require('./repository');
const html2plaintext = require('html2plaintext');
const noteIdQueue = [];
async function updateNoteFulltext(note) {
if (note.isDeleted || note.isProtected || await note.hasLabel('archived')) {
await sql.execute(`DELETE
FROM note_fulltext
WHERE noteId = ?`, [note.noteId]);
} else {
let content = null;
let contentHash = null;
if (['text', 'code'].includes(note.type)) {
content = await note.getContent();
if (note.type === 'text' && note.mime === 'text/html') {
content = html2plaintext(content);
}
contentHash = note.contentHash;
}
// optimistically try to update first ...
const res = await sql.execute(`UPDATE note_fulltext SET title = ?, titleHash = ?, content = ?, contentHash = ? WHERE noteId = ?`, [note.title, note.hash, content, contentHash, note.noteId]);
// ... and insert only when the update did not work
if (res.stmt.changes === 0) {
await sql.execute(`INSERT INTO note_fulltext (title, titleHash, content, contentHash, noteId)
VALUES (?, ?, ?, ?, ?)`, [note.title, note.hash, content, contentHash, note.noteId]);
}
}
}
async function triggerNoteFulltextUpdate(noteId) {
if (!noteIdQueue.includes(noteId)) {
noteIdQueue.push(noteId);
}
while (noteIdQueue.length > 0) {
await sql.transactional(async () => {
if (noteIdQueue.length === 0) {
return;
}
const noteId = noteIdQueue.shift();
const note = await repository.getNote(noteId);
await updateNoteFulltext(note);
});
}
}
module.exports = {
triggerNoteFulltextUpdate,
updateNoteFulltext
};
+5 -1
View File
@@ -12,6 +12,7 @@ const Link = require('../entities/link');
const NoteRevision = require('../entities/note_revision');
const Branch = require('../entities/branch');
const Attribute = require('../entities/attribute');
const hoistedNoteService = require('../services/hoisted_note');
async function getNewNotePosition(parentNoteId, noteData) {
let newNotePos = 0;
@@ -364,7 +365,10 @@ async function deleteNote(branch) {
return;
}
if (branch.branchId === 'root' || branch.noteId === 'root') {
if (branch.branchId === 'root'
|| branch.noteId === 'root'
|| branch.noteId === await hoistedNoteService.getHoistedNoteId()) {
throw new Error("Can't delete root branch/note");
}
+34 -11
View File
@@ -43,22 +43,45 @@ function calculateSmartValue(v) {
}
module.exports = function (searchText) {
searchText = searchText.trim();
// if the string doesn't start with attribute then we consider it as just standard full text search
if (!searchText.trim().startsWith("@")) {
return [
{
if (!searchText.startsWith("@")) {
// replace with space instead of empty string since these characters are probably separators
const filters = [];
if (searchText.startsWith('"') && searchText.endsWith('"')) {
// "bla bla" will search for exact match
searchText = searchText.substr(1, searchText.length - 2);
filters.push({
relation: 'and',
name: 'text',
operator: '=',
value: searchText
},
{
relation: 'or',
name: 'noteId',
operator: '=',
operator: '*=*',
value: searchText
});
}
else {
const tokens = searchText.split(/\s+/);
for (const token of tokens) {
filters.push({
relation: 'and',
name: 'text',
operator: '*=*',
value: token
});
}
]
}
filters.push({
relation: 'or',
name: 'noteId',
operator: '=',
value: searchText
});
return filters;
}
const filters = [];
+16
View File
@@ -42,6 +42,21 @@ async function getNote(noteId) {
return await getEntity("SELECT * FROM notes WHERE noteId = ?", [noteId]);
}
/** @returns {Promise<Note[]>} */
async function getNotes(noteIds) {
// this note might be optimised, but remember that it must keep the existing order of noteIds
// (important e.g. for @orderBy in search)
const notes = [];
for (const noteId of noteIds) {
const note = await getNote(noteId);
notes.push(note);
}
return notes;
}
/** @returns {Promise<Branch|null>} */
async function getBranch(branchId) {
return await getEntity("SELECT * FROM branches WHERE branchId = ?", [branchId]);
@@ -125,6 +140,7 @@ module.exports = {
getEntities,
getEntity,
getNote,
getNotes,
getBranch,
getAttribute,
getOption,
-3
View File
@@ -71,9 +71,6 @@ async function executeScript(script, params, startNoteId, currentNoteId, originE
}
async function execute(ctx, script) {
// scripts run as "server" sourceId so clients recognize the changes as "foreign" and update themselves
cls.namespace.set('sourceId', sourceIdService.getCurrentSourceId());
return await (function() { return eval(`const apiContext = this;\r\n(${script}\r\n)()`); }.call(ctx));
}
+18 -12
View File
@@ -3,20 +3,12 @@ const sql = require('./sql');
const log = require('./log');
const parseFilters = require('./parse_filters');
const buildSearchQuery = require('./build_search_query');
const noteCacheService = require('./note_cache');
async function searchForNotes(searchString) {
const filters = parseFilters(searchString);
const noteIds = await searchForNoteIds(searchString);
const {query, params} = buildSearchQuery(filters);
try {
return await repository.getEntities(query, params);
}
catch (e) {
log.error("Search failed for " + query);
throw e;
}
return await repository.getNotes(noteIds);
}
async function searchForNoteIds(searchString) {
@@ -25,7 +17,21 @@ async function searchForNoteIds(searchString) {
const {query, params} = buildSearchQuery(filters, 'notes.noteId');
try {
return await sql.getColumn(query, params);
const noteIds = await sql.getColumn(query, params);
const availableNoteIds = noteIds.filter(noteCacheService.isAvailable);
const limitFilter = filters.find(filter => filter.name.toLowerCase() === 'limit');
if (limitFilter) {
const limit = parseInt(limitFilter.value);
return availableNoteIds.splice(0, limit);
}
else {
return availableNoteIds;
}
}
catch (e) {
log.error("Search failed for " + query);
+10 -3
View File
@@ -14,13 +14,19 @@ async function createConnection() {
return await sqlite.open(dataDir.DOCUMENT_PATH, {Promise});
}
const dbConnection = new Promise(async (resolve, reject) => {
// no need to create new connection now since DB stays the same all the time
const db = await createConnection();
sql.setDbConnection(db);
resolve();
});
let dbReadyResolve = null;
const dbReady = new Promise(async (resolve, reject) => {
dbReadyResolve = resolve;
// no need to create new connection now since DB stays the same all the time
const db = await createConnection();
sql.setDbConnection(db);
await dbConnection;
initDbConnection();
});
@@ -165,6 +171,7 @@ dbReady.then(async () => {
module.exports = {
dbReady,
dbConnection,
schemaExists,
isDbInitialized,
initDbConnection,
+6 -4
View File
@@ -49,7 +49,11 @@ async function sync() {
catch (e) {
proxyToggle = !proxyToggle;
if (e.message && (e.message.includes('ECONNREFUSED') || e.message.includes('ERR_CONNECTION_REFUSED'))) {
if (e.message &&
(e.message.includes('ECONNREFUSED') ||
e.message.includes('ERR_CONNECTION_REFUSED') ||
e.message.includes('Bad Gateway'))) {
log.info("No connection to sync server.");
return {
@@ -263,9 +267,7 @@ async function getEntityRow(entityName, entityId) {
const entity = await sql.getRow(`SELECT * FROM ${entityName} WHERE ${primaryKey} = ?`, [entityId]);
if (!entity) {
console.log(entityName, entityId);
console.log(`SELECT * FROM ${entityName} WHERE ${primaryKey} = '${entityId}'`);
throw new Error(`Entity ${entityName} ${entityId} not found.`);
}
if (['note_contents', 'note_revisions'].includes(entityName) && entity.content !== null) {
-5
View File
@@ -3,7 +3,6 @@ const log = require('./log');
const eventLogService = require('./event_log');
const syncTableService = require('./sync_table');
const eventService = require('./events');
const noteFulltextService = require('../services/note_fulltext');
async function updateEntity(sync, entity, sourceId) {
const {entityName} = sync;
@@ -62,8 +61,6 @@ async function updateNote(entity, sourceId) {
await syncTableService.addNoteSync(entity.noteId, sourceId);
});
noteFulltextService.triggerNoteFulltextUpdate(entity.noteId);
log.info("Update/sync note " + entity.noteId);
}
}
@@ -78,8 +75,6 @@ async function updateNoteContent(entity, sourceId) {
await sql.replace("note_contents", entity);
await syncTableService.addNoteContentSync(entity.noteId, sourceId);
noteFulltextService.triggerNoteFulltextUpdate(entity.noteId);
});
log.info("Update/sync note content for noteId=" + entity.noteId);
+14 -9
View File
@@ -5,6 +5,7 @@
<title>Trilium Notes</title>
</head>
<body class="desktop theme-<%= theme %>" style="--main-font-size: <%= mainFontSize %>%; --tree-font-size: <%= treeFontSize %>%; --detail-font-size: <%= detailFontSize %>%;">
<noscript>Trilium requires JavaScript to be enabled.</noscript>
<div id="container" style="display: none; grid-template-columns: minmax(<%= leftPaneMinWidth %>px, <%= leftPaneWidthPercent %>fr) <%= rightPaneWidthPercent %>fr">
<div id="header" class="hide-toggle">
<div id="history-navigation" style="display: none;">
@@ -61,12 +62,6 @@
Sync (<span id="outstanding-syncs-count">0</span>)
</a>
<a class="dropdown-item" id="show-help-button">
<span class="jam jam-help"></span>
Show Help
<kbd>F1</kbd>
</a>
<a class="dropdown-item" id="open-dev-tools-button">
<span class="jam jam-terminal"></span>
Open Dev Tools
@@ -79,8 +74,20 @@
<kbd>ALT+O</kbd>
</a>
<a class="dropdown-item" id="reload-frontend-button" title="Reload can help with some visual glitches without restarting the whole app.">
<span class="jam jam-empty"></span>
Reload frontend
<kbd>CTRL-R</kbd>
</a>
<a class="dropdown-item" id="show-help-button">
<span class="jam jam-help"></span>
Show Help
<kbd>F1</kbd>
</a>
<a class="dropdown-item" id="show-about-dialog-button">
<span class="jam jam-info"></span>
<span class="jam jam-empty"></span>
About Trilium Notes
</a>
@@ -241,8 +248,6 @@
<% include dialogs/note_info.ejs %>
</div>
<webview class="electron-in-page-search-window" nodeintegration disablewebsecurity src="libraries/electron-in-page-search/search-window.html"></webview>
<script type="text/javascript">
window.baseApiUrl = 'api/';
window.device = "desktop";
+1
View File
@@ -7,6 +7,7 @@
<link rel="apple-touch-icon" sizes="180x180" href="/images/app-icons/ios/apple-touch-icon.png">
</head>
<body class="mobile">
<noscript>Trilium requires JavaScript to be enabled.</noscript>
<div class="row" id="container-row" style="display: none;">
<div id="left-pane" class="d-sm-flex d-md-flex d-lg-flex d-xl-flex col-12 col-sm-5 col-md-4 col-lg-4 col-xl-4">
+1
View File
@@ -6,6 +6,7 @@
<title>Setup</title>
</head>
<body>
<noscript>Trilium requires JavaScript to be enabled.</noscript>
<div class="container">
<div id="setup-dialog" class="col-md-12 col-lg-8 col-xl-6 mx-auto" style="padding-top: 25px; font-size: larger; display: none;">
<h1>Trilium Notes setup</h1>
+8
View File
@@ -35,6 +35,14 @@ async function startTrilium() {
app.set('port', usedPort);
if (config['Network']['https']) {
if (!config['Network']['keyPath'] || !config['Network']['keyPath'].trim().length) {
throw new Error("keyPath in config.ini is required when https=true, but it's empty");
}
if (!config['Network']['certPath'] || !config['Network']['certPath'].trim().length) {
throw new Error("certPath in config.ini is required when https=true, but it's empty");
}
const options = {
key: fs.readFileSync(config['Network']['keyPath']),
cert: fs.readFileSync(config['Network']['certPath'])