mirror of
				https://github.com/zadam/trilium.git
				synced 2025-10-31 02:16:05 +01:00 
			
		
		
		
	Compare commits
	
		
			229 Commits
		
	
	
		
			v0.27.1-be
			...
			v0.30.4
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | ae17e4dc60 | ||
|  | 02eddc347a | ||
|  | ee58bf3d5c | ||
|  | ecbaffa5f3 | ||
|  | fe86c09f22 | ||
|  | c8e01d6cce | ||
|  | 7c404f03db | ||
|  | ed1cf6aad5 | ||
|  | 9703fd61e2 | ||
|  | 16790e388b | ||
|  | b48474998b | ||
|  | 95d8f07458 | ||
|  | e628c30c89 | ||
|  | 52a8aae74f | ||
|  | 54b5898582 | ||
|  | 64974d75d5 | ||
|  | bd118027fb | ||
|  | 36de217835 | ||
|  | b5283d58bb | ||
|  | 8414d97ffa | ||
|  | 9f30d4e673 | ||
|  | 8e0d1fa0df | ||
|  | 5b251b9977 | ||
|  | 8b3e721028 | ||
|  | 7e2a2baa5d | ||
|  | 003eed368b | ||
|  | 4b1cf05c0e | ||
|  | d9429c4f4b | ||
|  | b7bd94b6b0 | ||
|  | 51bbc10744 | ||
|  | fb5df33ee7 | ||
|  | d8ba0ccd7d | ||
|  | 886ea6c68c | ||
|  | 936f85c09e | ||
|  | b25deea21d | ||
|  | cf5ec44303 | ||
|  | 6f956c2415 | ||
|  | 3533160bef | ||
|  | 60cbfdcabd | ||
|  | 604f036a54 | ||
|  | dd4f3ec264 | ||
|  | ff67b8a0ba | ||
|  | 1d5fb0b646 | ||
|  | 80931a318f | ||
|  | 16f16cb36a | ||
|  | 899f24cde5 | ||
|  | 9f29521ab8 | ||
|  | c1ce0c6b22 | ||
|  | a7fce33750 | ||
|  | 6fd8e73150 | ||
|  | 1359dd86c2 | ||
|  | a1b610fc50 | ||
|  | c849d719e9 | ||
|  | 96de2e7008 | ||
|  | f140b77e7c | ||
|  | 22228de63b | ||
|  | b0596c9eb2 | ||
|  | 2a2319d434 | ||
|  | dad47d115f | ||
|  | 502026359c | ||
|  | 47b0e4e4d3 | ||
|  | 6c927d9159 | ||
|  | ddc79b2517 | ||
|  | 4c2e12d2cb | ||
|  | 14f7a8b7b9 | ||
|  | caa7dd9619 | ||
|  | 8aa7e2d0a0 | ||
|  | 6be8a3f343 | ||
|  | a097cefba7 | ||
|  | e4c78f3887 | ||
|  | 5baa251944 | ||
|  | cde68abec9 | ||
|  | 51175e3676 | ||
|  | 45ddfef30a | ||
|  | 1e1d78999e | ||
|  | 92fcd7b345 | ||
|  | e04f1cd574 | ||
|  | 176c3a5d51 | ||
|  | c09570cf39 | ||
|  | 4a093000be | ||
|  | 6952b643ae | ||
|  | c487a95bc7 | ||
|  | 8884177d9f | ||
|  | 8b250ed523 | ||
|  | d8b78d8025 | ||
|  | 42112b8053 | ||
|  | 6cc0dd5a80 | ||
|  | afd5f4823f | ||
|  | b0cf82c91b | ||
|  | 6a67cdd5af | ||
|  | bad7b84993 | ||
|  | d3ca6b5ae6 | ||
|  | da5009f089 | ||
|  | c08524c977 | ||
|  | f89537037e | ||
|  | c153793766 | ||
|  | 0aec5927d5 | ||
|  | 8aea9a1801 | ||
|  | 73247e3220 | ||
|  | 89344a6eda | ||
|  | 40d2e6ea83 | ||
|  | 910cfe9a17 | ||
|  | e58a80fc00 | ||
|  | 4a2319cb33 | ||
|  | 5619088c41 | ||
|  | 60271993eb | ||
|  | 6695e8b011 | ||
|  | 707df18b93 | ||
|  | 90895f1288 | ||
|  | ba1ca506af | ||
|  | f90ed99a40 | ||
|  | 67630b1a22 | ||
|  | 2c1580ea65 | ||
|  | 840a0b5f64 | ||
|  | b39f6ef7ad | ||
|  | fb27088fcd | ||
|  | 76fbff68ba | ||
|  | 54de4d236d | ||
|  | e211dd65ad | ||
|  | a87f4d8653 | ||
|  | b59c175c2e | ||
|  | 580104c4c5 | ||
|  | 0fc3053b0a | ||
|  | 929e0f69c2 | ||
|  | e70af1300a | ||
|  | 4d0e46021b | ||
|  | 1a9a49b739 | ||
|  | 2ac560c56e | ||
|  | c23387c0fb | ||
|  | 0ff250fe15 | ||
|  | 52b1e58b26 | ||
|  | 484715e440 | ||
|  | 9d42c3d802 | ||
|  | c654172d33 | ||
|  | e17b26c883 | ||
|  | 24d02d9cf5 | ||
|  | 7208a311ac | ||
|  | ad7355372b | ||
|  | f18b5babad | ||
|  | 9831ec0ca9 | ||
|  | 596544eca3 | ||
|  | 1f853024ee | ||
|  | 0308b13460 | ||
|  | 06b8a82f70 | ||
|  | 91ca07929d | ||
|  | afabaa5fdb | ||
|  | a6fd3fa77c | ||
|  | 58a2c08dcd | ||
|  | 299bbff2f4 | ||
|  | 19d8947123 | ||
|  | 35edce7523 | ||
|  | bc4cec69a5 | ||
|  | cce8c1b674 | ||
|  | aa58788769 | ||
|  | 6c62ab7a52 | ||
|  | d6ab638b30 | ||
|  | bd4db406de | ||
|  | e50f9cd0a3 | ||
|  | 2b64cbce2c | ||
|  | 2797c942ab | ||
|  | f1c3278874 | ||
|  | 424c22dcde | ||
|  | 5c223dfd12 | ||
|  | 6a3e7a5a8e | ||
|  | f88cdac000 | ||
|  | eeead90f32 | ||
|  | b607857409 | ||
|  | 9268f88bc3 | ||
|  | f7f0560a9f | ||
|  | 3d8905207e | ||
|  | dbc312010b | ||
|  | b115a7cf19 | ||
|  | 348562352c | ||
|  | 70fd917e7c | ||
|  | d2b60764cd | ||
|  | 581b1fdaa5 | ||
|  | 3c19a712c0 | ||
|  | cc27f16088 | ||
|  | dffdb82288 | ||
|  | 2b32addade | ||
|  | 0b251530fa | ||
|  | f5b933149a | ||
|  | 48bbfb8bdb | ||
|  | 889971c4d6 | ||
|  | 0722494d41 | ||
|  | 4b977a3306 | ||
|  | 3ff3021acd | ||
|  | 99e56a9c42 | ||
|  | 77279dfe16 | ||
|  | 93f8050454 | ||
|  | 31cfede7a7 | ||
|  | c8ec86e537 | ||
|  | 05aee884b6 | ||
|  | 012ba9e060 | ||
|  | 8e8fd88857 | ||
|  | 523ccdad6b | ||
|  | ded3f605be | ||
|  | 62b44e3549 | ||
|  | 030d12a465 | ||
|  | 4d15628840 | ||
|  | 81b849898c | ||
|  | 3824486b85 | ||
|  | 081ab00a0a | ||
|  | 04f6af5c9a | ||
|  | 4dc1f1f6eb | ||
|  | 3930a02123 | ||
|  | 3112de105e | ||
|  | 3b8d7b8fba | ||
|  | 9fca7f09a5 | ||
|  | fd39d6b3a9 | ||
|  | a103886ea5 | ||
|  | 373408e401 | ||
|  | db44c1d8e6 | ||
|  | 95a34c9e2d | ||
|  | 6ce401f260 | ||
|  | 5d74dcd256 | ||
|  | 5a9fc1697b | ||
|  | 927415838c | ||
|  | d72fcefdc7 | ||
|  | 0be173a8f7 | ||
|  | c3913a8735 | ||
|  | e2dfe1b6de | ||
|  | fec3e47eb8 | ||
|  | d72efd2450 | ||
|  | ef1c840aa7 | ||
|  | 1581464d8c | ||
|  | 9de29584a4 | ||
|  | 9e2e6fb50c | ||
|  | c85979b66b | 
							
								
								
									
										7
									
								
								.gitpod.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								.gitpod.yml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,7 @@ | ||||
| tasks: | ||||
|     - before: nvm install 10 && nvm use 10 | ||||
|       init: npm install | ||||
|       command: npm run start | ||||
| ports: | ||||
|     - port: 8080 | ||||
|       onOpen: open-preview | ||||
| @@ -16,586 +16,662 @@ | ||||
|     <table id="8" parent="2" name="branches"/> | ||||
|     <table id="9" parent="2" name="event_log"/> | ||||
|     <table id="10" parent="2" name="links"/> | ||||
|     <table id="11" parent="2" name="note_revisions"/> | ||||
|     <table id="12" parent="2" name="notes"/> | ||||
|     <table id="13" parent="2" name="options"/> | ||||
|     <table id="14" parent="2" name="recent_notes"/> | ||||
|     <table id="15" parent="2" name="source_ids"/> | ||||
|     <table id="16" parent="2" name="sqlite_master"> | ||||
|     <table id="11" parent="2" name="note_contents"/> | ||||
|     <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="17" parent="2" name="sqlite_sequence"> | ||||
|     <table id="18" parent="2" name="sqlite_sequence"> | ||||
|       <System>1</System> | ||||
|     </table> | ||||
|     <table id="18" parent="2" name="sync"/> | ||||
|     <column id="19" 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="20" parent="6" name="token"> | ||||
|     <column id="21" parent="6" name="token"> | ||||
|       <Position>2</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|     </column> | ||||
|     <column id="21" parent="6" name="dateCreated"> | ||||
|     <column id="22" parent="6" name="dateCreated"> | ||||
|       <Position>3</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|     </column> | ||||
|     <column id="22" 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="23" parent="6" name="hash"> | ||||
|     <column id="24" parent="6" name="hash"> | ||||
|       <Position>5</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|       <DefaultExpression>""</DefaultExpression> | ||||
|     </column> | ||||
|     <index id="24" 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="25" parent="6"> | ||||
|     <key id="26" parent="6"> | ||||
|       <ColNames>apiTokenId</ColNames> | ||||
|       <Primary>1</Primary> | ||||
|       <UnderlyingIndexName>sqlite_autoindex_api_tokens_1</UnderlyingIndexName> | ||||
|     </key> | ||||
|     <column id="26" parent="7" name="attributeId"> | ||||
|     <column id="27" parent="7" name="attributeId"> | ||||
|       <Position>1</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|     </column> | ||||
|     <column id="27" parent="7" name="noteId"> | ||||
|     <column id="28" parent="7" name="noteId"> | ||||
|       <Position>2</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|     </column> | ||||
|     <column id="28" parent="7" name="type"> | ||||
|     <column id="29" parent="7" name="type"> | ||||
|       <Position>3</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|     </column> | ||||
|     <column id="29" parent="7" name="name"> | ||||
|     <column id="30" parent="7" name="name"> | ||||
|       <Position>4</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|     </column> | ||||
|     <column id="30" parent="7" name="value"> | ||||
|     <column id="31" parent="7" name="value"> | ||||
|       <Position>5</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|       <DefaultExpression>''</DefaultExpression> | ||||
|     </column> | ||||
|     <column id="31" 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="32" parent="7" name="dateCreated"> | ||||
|     <column id="33" parent="7" name="dateCreated"> | ||||
|       <Position>7</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|     </column> | ||||
|     <column id="33" parent="7" name="dateModified"> | ||||
|     <column id="34" parent="7" name="dateModified"> | ||||
|       <Position>8</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|     </column> | ||||
|     <column id="34" parent="7" name="isDeleted"> | ||||
|     <column id="35" parent="7" name="isDeleted"> | ||||
|       <Position>9</Position> | ||||
|       <DataType>INT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|     </column> | ||||
|     <column id="35" parent="7" name="hash"> | ||||
|     <column id="36" parent="7" name="hash"> | ||||
|       <Position>10</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|       <DefaultExpression>""</DefaultExpression> | ||||
|     </column> | ||||
|     <column id="36" parent="7" name="isInheritable"> | ||||
|     <column id="37" parent="7" name="isInheritable"> | ||||
|       <Position>11</Position> | ||||
|       <DataType>int|0s</DataType> | ||||
|       <DefaultExpression>0</DefaultExpression> | ||||
|     </column> | ||||
|     <index id="37" 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> | ||||
|     <key id="38" parent="7"> | ||||
|     <index id="39" parent="7" name="IDX_attributes_noteId_index"> | ||||
|       <ColNames>noteId</ColNames> | ||||
|       <ColumnCollations></ColumnCollations> | ||||
|     </index> | ||||
|     <index id="40" parent="7" name="IDX_attributes_name_value"> | ||||
|       <ColNames>name | ||||
| value</ColNames> | ||||
|       <ColumnCollations></ColumnCollations> | ||||
|     </index> | ||||
|     <index id="41" parent="7" name="IDX_attributes_name_index"> | ||||
|       <ColNames>name</ColNames> | ||||
|       <ColumnCollations></ColumnCollations> | ||||
|     </index> | ||||
|     <index id="42" parent="7" name="IDX_attributes_value_index"> | ||||
|       <ColNames>value</ColNames> | ||||
|       <ColumnCollations></ColumnCollations> | ||||
|     </index> | ||||
|     <key id="43" parent="7"> | ||||
|       <ColNames>attributeId</ColNames> | ||||
|       <Primary>1</Primary> | ||||
|       <UnderlyingIndexName>sqlite_autoindex_attributes_1</UnderlyingIndexName> | ||||
|     </key> | ||||
|     <column id="39" parent="8" name="branchId"> | ||||
|     <column id="44" parent="8" name="branchId"> | ||||
|       <Position>1</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|     </column> | ||||
|     <column id="40" parent="8" name="noteId"> | ||||
|     <column id="45" parent="8" name="noteId"> | ||||
|       <Position>2</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|     </column> | ||||
|     <column id="41" parent="8" name="parentNoteId"> | ||||
|     <column id="46" parent="8" name="parentNoteId"> | ||||
|       <Position>3</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|     </column> | ||||
|     <column id="42" parent="8" name="notePosition"> | ||||
|     <column id="47" parent="8" name="notePosition"> | ||||
|       <Position>4</Position> | ||||
|       <DataType>INTEGER|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|     </column> | ||||
|     <column id="43" parent="8" name="prefix"> | ||||
|     <column id="48" parent="8" name="prefix"> | ||||
|       <Position>5</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|     </column> | ||||
|     <column id="44" parent="8" name="isExpanded"> | ||||
|     <column id="49" parent="8" name="isExpanded"> | ||||
|       <Position>6</Position> | ||||
|       <DataType>BOOLEAN|0s</DataType> | ||||
|     </column> | ||||
|     <column id="45" 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="46" parent="8" name="dateModified"> | ||||
|     <column id="51" parent="8" name="dateModified"> | ||||
|       <Position>8</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|     </column> | ||||
|     <column id="47" parent="8" name="hash"> | ||||
|     <column id="52" parent="8" name="hash"> | ||||
|       <Position>9</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|       <DefaultExpression>""</DefaultExpression> | ||||
|     </column> | ||||
|     <column id="48" parent="8" name="dateCreated"> | ||||
|     <column id="53" parent="8" name="dateCreated"> | ||||
|       <Position>10</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|       <DefaultExpression>'1970-01-01T00:00:00.000Z'</DefaultExpression> | ||||
|     </column> | ||||
|     <index id="49" 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="50" 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="51" parent="8" name="IDX_branches_noteId"> | ||||
|     <index id="56" parent="8" name="IDX_branches_noteId"> | ||||
|       <ColNames>noteId</ColNames> | ||||
|       <ColumnCollations></ColumnCollations> | ||||
|     </index> | ||||
|     <index id="52" parent="8" name="IDX_branches_parentNoteId"> | ||||
|     <index id="57" parent="8" name="IDX_branches_parentNoteId"> | ||||
|       <ColNames>parentNoteId</ColNames> | ||||
|       <ColumnCollations></ColumnCollations> | ||||
|     </index> | ||||
|     <key id="53" parent="8"> | ||||
|     <key id="58" parent="8"> | ||||
|       <ColNames>branchId</ColNames> | ||||
|       <Primary>1</Primary> | ||||
|       <UnderlyingIndexName>sqlite_autoindex_branches_1</UnderlyingIndexName> | ||||
|     </key> | ||||
|     <column id="54" parent="9" name="eventId"> | ||||
|     <column id="59" parent="9" name="eventId"> | ||||
|       <Position>1</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|     </column> | ||||
|     <column id="55" parent="9" name="noteId"> | ||||
|     <column id="60" parent="9" name="noteId"> | ||||
|       <Position>2</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|     </column> | ||||
|     <column id="56" parent="9" name="comment"> | ||||
|     <column id="61" parent="9" name="comment"> | ||||
|       <Position>3</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|     </column> | ||||
|     <column id="57" parent="9" name="dateCreated"> | ||||
|     <column id="62" parent="9" name="dateCreated"> | ||||
|       <Position>4</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|     </column> | ||||
|     <index id="58" 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="59" parent="9"> | ||||
|     <key id="64" parent="9"> | ||||
|       <ColNames>eventId</ColNames> | ||||
|       <Primary>1</Primary> | ||||
|       <UnderlyingIndexName>sqlite_autoindex_event_log_1</UnderlyingIndexName> | ||||
|     </key> | ||||
|     <column id="60" parent="10" name="linkId"> | ||||
|     <column id="65" parent="10" name="linkId"> | ||||
|       <Position>1</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|     </column> | ||||
|     <column id="61" parent="10" name="noteId"> | ||||
|     <column id="66" parent="10" name="noteId"> | ||||
|       <Position>2</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|     </column> | ||||
|     <column id="62" parent="10" name="targetNoteId"> | ||||
|     <column id="67" parent="10" name="targetNoteId"> | ||||
|       <Position>3</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|     </column> | ||||
|     <column id="63" parent="10" name="type"> | ||||
|     <column id="68" parent="10" name="type"> | ||||
|       <Position>4</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|     </column> | ||||
|     <column id="64" parent="10" name="isDeleted"> | ||||
|     <column id="69" parent="10" name="hash"> | ||||
|       <Position>5</Position> | ||||
|       <DataType>INTEGER|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|       <DefaultExpression>0</DefaultExpression> | ||||
|     </column> | ||||
|     <column id="65" parent="10" name="dateCreated"> | ||||
|       <Position>6</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|     </column> | ||||
|     <column id="66" parent="10" name="dateModified"> | ||||
|       <Position>7</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|     </column> | ||||
|     <column id="67" parent="10" name="hash"> | ||||
|       <Position>8</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|       <DefaultExpression>""</DefaultExpression> | ||||
|     </column> | ||||
|     <index id="68" parent="10" name="sqlite_autoindex_links_1"> | ||||
|     <column id="70" parent="10" name="isDeleted"> | ||||
|       <Position>6</Position> | ||||
|       <DataType>INTEGER|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|       <DefaultExpression>0</DefaultExpression> | ||||
|     </column> | ||||
|     <column id="71" parent="10" name="dateCreated"> | ||||
|       <Position>7</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|     </column> | ||||
|     <column id="72" parent="10" name="dateModified"> | ||||
|       <Position>8</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|     </column> | ||||
|     <index id="73" parent="10" name="sqlite_autoindex_links_1"> | ||||
|       <NameSurrogate>1</NameSurrogate> | ||||
|       <ColNames>linkId</ColNames> | ||||
|       <ColumnCollations></ColumnCollations> | ||||
|       <Unique>1</Unique> | ||||
|     </index> | ||||
|     <key id="69" parent="10"> | ||||
|     <index id="74" parent="10" name="IDX_links_noteId_index"> | ||||
|       <ColNames>noteId</ColNames> | ||||
|       <ColumnCollations></ColumnCollations> | ||||
|     </index> | ||||
|     <index id="75" parent="10" name="IDX_links_targetNoteId_index"> | ||||
|       <ColNames>targetNoteId</ColNames> | ||||
|       <ColumnCollations></ColumnCollations> | ||||
|     </index> | ||||
|     <key id="76" parent="10"> | ||||
|       <ColNames>linkId</ColNames> | ||||
|       <Primary>1</Primary> | ||||
|       <UnderlyingIndexName>sqlite_autoindex_links_1</UnderlyingIndexName> | ||||
|     </key> | ||||
|     <column id="70" parent="11" name="noteRevisionId"> | ||||
|     <column id="77" parent="11" name="noteContentId"> | ||||
|       <Position>1</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|     </column> | ||||
|     <column id="71" parent="11" name="noteId"> | ||||
|     <column id="78" parent="11" name="noteId"> | ||||
|       <Position>2</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|     </column> | ||||
|     <column id="72" parent="11" name="title"> | ||||
|     <column id="79" parent="11" name="isProtected"> | ||||
|       <Position>3</Position> | ||||
|       <DataType>INT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|       <DefaultExpression>0</DefaultExpression> | ||||
|     </column> | ||||
|     <column id="80" parent="11" name="content"> | ||||
|       <Position>4</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <DefaultExpression>NULL</DefaultExpression> | ||||
|     </column> | ||||
|     <column id="81" parent="11" name="hash"> | ||||
|       <Position>5</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|       <DefaultExpression>""</DefaultExpression> | ||||
|     </column> | ||||
|     <column id="82" parent="11" name="dateCreated"> | ||||
|       <Position>6</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|       <DefaultExpression>'2018-05-08T23:41:15.225Z'</DefaultExpression> | ||||
|     </column> | ||||
|     <column id="83" parent="11" name="dateModified"> | ||||
|       <Position>7</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|       <DefaultExpression>'2018-05-08T23:41:15.225Z'</DefaultExpression> | ||||
|     </column> | ||||
|     <index id="84" parent="11" name="sqlite_autoindex_note_contents_1"> | ||||
|       <NameSurrogate>1</NameSurrogate> | ||||
|       <ColNames>noteContentId</ColNames> | ||||
|       <ColumnCollations></ColumnCollations> | ||||
|       <Unique>1</Unique> | ||||
|     </index> | ||||
|     <index id="85" parent="11" name="IDX_note_contents_noteId"> | ||||
|       <ColNames>noteId</ColNames> | ||||
|       <ColumnCollations></ColumnCollations> | ||||
|       <Unique>1</Unique> | ||||
|     </index> | ||||
|     <key id="86" parent="11"> | ||||
|       <ColNames>noteContentId</ColNames> | ||||
|       <Primary>1</Primary> | ||||
|       <UnderlyingIndexName>sqlite_autoindex_note_contents_1</UnderlyingIndexName> | ||||
|     </key> | ||||
|     <column id="87" parent="12" name="noteRevisionId"> | ||||
|       <Position>1</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|     </column> | ||||
|     <column id="88" parent="12" name="noteId"> | ||||
|       <Position>2</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|     </column> | ||||
|     <column id="89" parent="12" name="title"> | ||||
|       <Position>3</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|     </column> | ||||
|     <column id="73" parent="11" name="content"> | ||||
|     <column id="90" parent="12" name="content"> | ||||
|       <Position>4</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|     </column> | ||||
|     <column id="74" parent="11" name="isProtected"> | ||||
|     <column id="91" parent="12" name="isProtected"> | ||||
|       <Position>5</Position> | ||||
|       <DataType>INT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|       <DefaultExpression>0</DefaultExpression> | ||||
|     </column> | ||||
|     <column id="75" parent="11" name="dateModifiedFrom"> | ||||
|     <column id="92" parent="12" name="dateModifiedFrom"> | ||||
|       <Position>6</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|     </column> | ||||
|     <column id="76" parent="11" name="dateModifiedTo"> | ||||
|     <column id="93" parent="12" name="dateModifiedTo"> | ||||
|       <Position>7</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|     </column> | ||||
|     <column id="77" parent="11" name="type"> | ||||
|     <column id="94" parent="12" name="type"> | ||||
|       <Position>8</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|       <DefaultExpression>''</DefaultExpression> | ||||
|     </column> | ||||
|     <column id="78" parent="11" name="mime"> | ||||
|     <column id="95" parent="12" name="mime"> | ||||
|       <Position>9</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|       <DefaultExpression>''</DefaultExpression> | ||||
|     </column> | ||||
|     <column id="79" parent="11" name="hash"> | ||||
|     <column id="96" parent="12" name="hash"> | ||||
|       <Position>10</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|       <DefaultExpression>""</DefaultExpression> | ||||
|     </column> | ||||
|     <index id="80" parent="11" name="sqlite_autoindex_note_revisions_1"> | ||||
|     <index id="97" parent="12" name="sqlite_autoindex_note_revisions_1"> | ||||
|       <NameSurrogate>1</NameSurrogate> | ||||
|       <ColNames>noteRevisionId</ColNames> | ||||
|       <ColumnCollations></ColumnCollations> | ||||
|       <Unique>1</Unique> | ||||
|     </index> | ||||
|     <index id="81" parent="11" name="IDX_note_revisions_noteId"> | ||||
|     <index id="98" parent="12" name="IDX_note_revisions_noteId"> | ||||
|       <ColNames>noteId</ColNames> | ||||
|       <ColumnCollations></ColumnCollations> | ||||
|     </index> | ||||
|     <index id="82" parent="11" name="IDX_note_revisions_dateModifiedFrom"> | ||||
|     <index id="99" parent="12" name="IDX_note_revisions_dateModifiedFrom"> | ||||
|       <ColNames>dateModifiedFrom</ColNames> | ||||
|       <ColumnCollations></ColumnCollations> | ||||
|     </index> | ||||
|     <index id="83" parent="11" name="IDX_note_revisions_dateModifiedTo"> | ||||
|     <index id="100" parent="12" name="IDX_note_revisions_dateModifiedTo"> | ||||
|       <ColNames>dateModifiedTo</ColNames> | ||||
|       <ColumnCollations></ColumnCollations> | ||||
|     </index> | ||||
|     <key id="84" parent="11"> | ||||
|     <key id="101" parent="12"> | ||||
|       <ColNames>noteRevisionId</ColNames> | ||||
|       <Primary>1</Primary> | ||||
|       <UnderlyingIndexName>sqlite_autoindex_note_revisions_1</UnderlyingIndexName> | ||||
|     </key> | ||||
|     <column id="85" parent="12" name="noteId"> | ||||
|     <column id="102" parent="13" name="noteId"> | ||||
|       <Position>1</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|     </column> | ||||
|     <column id="86" parent="12" name="title"> | ||||
|     <column id="103" parent="13" name="title"> | ||||
|       <Position>2</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|       <DefaultExpression>"note"</DefaultExpression> | ||||
|     </column> | ||||
|     <column id="87" parent="12" name="content"> | ||||
|     <column id="104" parent="13" name="isProtected"> | ||||
|       <Position>3</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <DefaultExpression>NULL</DefaultExpression> | ||||
|     </column> | ||||
|     <column id="88" parent="12" name="isProtected"> | ||||
|       <Position>4</Position> | ||||
|       <DataType>INT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|       <DefaultExpression>0</DefaultExpression> | ||||
|     </column> | ||||
|     <column id="89" parent="12" name="type"> | ||||
|       <Position>5</Position> | ||||
|     <column id="105" parent="13" name="type"> | ||||
|       <Position>4</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|       <DefaultExpression>'text'</DefaultExpression> | ||||
|     </column> | ||||
|     <column id="90" parent="12" name="mime"> | ||||
|       <Position>6</Position> | ||||
|     <column id="106" parent="13" name="mime"> | ||||
|       <Position>5</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|       <DefaultExpression>'text/html'</DefaultExpression> | ||||
|     </column> | ||||
|     <column id="91" parent="12" name="hash"> | ||||
|       <Position>7</Position> | ||||
|     <column id="107" parent="13" name="hash"> | ||||
|       <Position>6</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|       <DefaultExpression>""</DefaultExpression> | ||||
|     </column> | ||||
|     <column id="92" parent="12" name="isDeleted"> | ||||
|       <Position>8</Position> | ||||
|     <column id="108" parent="13" name="isDeleted"> | ||||
|       <Position>7</Position> | ||||
|       <DataType>INT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|       <DefaultExpression>0</DefaultExpression> | ||||
|     </column> | ||||
|     <column id="93" parent="12" name="dateCreated"> | ||||
|     <column id="109" parent="13" name="dateCreated"> | ||||
|       <Position>8</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|     </column> | ||||
|     <column id="110" parent="13" name="dateModified"> | ||||
|       <Position>9</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|     </column> | ||||
|     <column id="94" parent="12" name="dateModified"> | ||||
|       <Position>10</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|     </column> | ||||
|     <index id="95" parent="12" 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="96" parent="12"> | ||||
|     <key id="112" parent="13"> | ||||
|       <ColNames>noteId</ColNames> | ||||
|       <Primary>1</Primary> | ||||
|       <UnderlyingIndexName>sqlite_autoindex_notes_1</UnderlyingIndexName> | ||||
|     </key> | ||||
|     <column id="97" parent="13" name="name"> | ||||
|     <column id="113" parent="14" name="name"> | ||||
|       <Position>1</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|     </column> | ||||
|     <column id="98" parent="13" name="value"> | ||||
|     <column id="114" parent="14" name="value"> | ||||
|       <Position>2</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|     </column> | ||||
|     <column id="99" parent="13" name="dateModified"> | ||||
|     <column id="115" parent="14" name="dateModified"> | ||||
|       <Position>3</Position> | ||||
|       <DataType>INT|0s</DataType> | ||||
|     </column> | ||||
|     <column id="100" parent="13" name="isSynced"> | ||||
|     <column id="116" parent="14" name="isSynced"> | ||||
|       <Position>4</Position> | ||||
|       <DataType>INTEGER|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|       <DefaultExpression>0</DefaultExpression> | ||||
|     </column> | ||||
|     <column id="101" parent="13" name="hash"> | ||||
|     <column id="117" parent="14" name="hash"> | ||||
|       <Position>5</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|       <DefaultExpression>""</DefaultExpression> | ||||
|     </column> | ||||
|     <column id="102" parent="13" name="dateCreated"> | ||||
|     <column id="118" parent="14" name="dateCreated"> | ||||
|       <Position>6</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|       <DefaultExpression>'1970-01-01T00:00:00.000Z'</DefaultExpression> | ||||
|     </column> | ||||
|     <index id="103" parent="13" 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="104" parent="13"> | ||||
|     <key id="120" parent="14"> | ||||
|       <ColNames>name</ColNames> | ||||
|       <Primary>1</Primary> | ||||
|       <UnderlyingIndexName>sqlite_autoindex_options_1</UnderlyingIndexName> | ||||
|     </key> | ||||
|     <column id="105" parent="14" name="branchId"> | ||||
|     <column id="121" parent="15" name="branchId"> | ||||
|       <Position>1</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|     </column> | ||||
|     <column id="106" parent="14" name="notePath"> | ||||
|     <column id="122" parent="15" name="notePath"> | ||||
|       <Position>2</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|     </column> | ||||
|     <column id="107" parent="14" name="hash"> | ||||
|     <column id="123" parent="15" name="hash"> | ||||
|       <Position>3</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|       <DefaultExpression>""</DefaultExpression> | ||||
|     </column> | ||||
|     <column id="108" parent="14" name="dateCreated"> | ||||
|     <column id="124" parent="15" name="dateCreated"> | ||||
|       <Position>4</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|     </column> | ||||
|     <column id="109" parent="14" name="isDeleted"> | ||||
|     <column id="125" parent="15" name="isDeleted"> | ||||
|       <Position>5</Position> | ||||
|       <DataType>INT|0s</DataType> | ||||
|     </column> | ||||
|     <index id="110" parent="14" 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="111" parent="14"> | ||||
|     <key id="127" parent="15"> | ||||
|       <ColNames>branchId</ColNames> | ||||
|       <Primary>1</Primary> | ||||
|       <UnderlyingIndexName>sqlite_autoindex_recent_notes_1</UnderlyingIndexName> | ||||
|     </key> | ||||
|     <column id="112" parent="15" name="sourceId"> | ||||
|     <column id="128" parent="16" name="sourceId"> | ||||
|       <Position>1</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|     </column> | ||||
|     <column id="113" parent="15" name="dateCreated"> | ||||
|     <column id="129" parent="16" name="dateCreated"> | ||||
|       <Position>2</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|     </column> | ||||
|     <index id="114" parent="15" 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="115" parent="15"> | ||||
|     <key id="131" parent="16"> | ||||
|       <ColNames>sourceId</ColNames> | ||||
|       <Primary>1</Primary> | ||||
|       <UnderlyingIndexName>sqlite_autoindex_source_ids_1</UnderlyingIndexName> | ||||
|     </key> | ||||
|     <column id="116" parent="16" name="type"> | ||||
|     <column id="132" parent="17" name="type"> | ||||
|       <Position>1</Position> | ||||
|       <DataType>text|0s</DataType> | ||||
|     </column> | ||||
|     <column id="117" parent="16" name="name"> | ||||
|     <column id="133" parent="17" name="name"> | ||||
|       <Position>2</Position> | ||||
|       <DataType>text|0s</DataType> | ||||
|     </column> | ||||
|     <column id="118" parent="16" name="tbl_name"> | ||||
|     <column id="134" parent="17" name="tbl_name"> | ||||
|       <Position>3</Position> | ||||
|       <DataType>text|0s</DataType> | ||||
|     </column> | ||||
|     <column id="119" parent="16" name="rootpage"> | ||||
|     <column id="135" parent="17" name="rootpage"> | ||||
|       <Position>4</Position> | ||||
|       <DataType>integer|0s</DataType> | ||||
|     </column> | ||||
|     <column id="120" parent="16" name="sql"> | ||||
|     <column id="136" parent="17" name="sql"> | ||||
|       <Position>5</Position> | ||||
|       <DataType>text|0s</DataType> | ||||
|     </column> | ||||
|     <column id="121" parent="17" name="name"> | ||||
|     <column id="137" parent="18" name="name"> | ||||
|       <Position>1</Position> | ||||
|     </column> | ||||
|     <column id="122" parent="17" name="seq"> | ||||
|     <column id="138" parent="18" name="seq"> | ||||
|       <Position>2</Position> | ||||
|     </column> | ||||
|     <column id="123" parent="18" 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="124" parent="18" name="entityName"> | ||||
|     <column id="140" parent="19" name="entityName"> | ||||
|       <Position>2</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|     </column> | ||||
|     <column id="125" parent="18" name="entityId"> | ||||
|     <column id="141" parent="19" name="entityId"> | ||||
|       <Position>3</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|     </column> | ||||
|     <column id="126" parent="18" name="sourceId"> | ||||
|     <column id="142" parent="19" name="sourceId"> | ||||
|       <Position>4</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|     </column> | ||||
|     <column id="127" parent="18" name="syncDate"> | ||||
|     <column id="143" parent="19" name="syncDate"> | ||||
|       <Position>5</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|     </column> | ||||
|     <index id="128" parent="18" 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="129" parent="18" name="IDX_sync_syncDate"> | ||||
|     <index id="145" parent="19" name="IDX_sync_syncDate"> | ||||
|       <ColNames>syncDate</ColNames> | ||||
|       <ColumnCollations></ColumnCollations> | ||||
|     </index> | ||||
|     <key id="130" parent="18"> | ||||
|     <key id="146" parent="19"> | ||||
|       <ColNames>id</ColNames> | ||||
|       <Primary>1</Primary> | ||||
|     </key> | ||||
|   | ||||
| @@ -17,6 +17,7 @@ RUN set -x \ | ||||
|         libtool \ | ||||
|         make \ | ||||
|         nasm \ | ||||
|         libpng-dev \ | ||||
|     && npm install --production \ | ||||
|     && apk del .build-dependencies | ||||
|  | ||||
|   | ||||
							
								
								
									
										13
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										13
									
								
								README.md
									
									
									
									
									
								
							| @@ -18,6 +18,7 @@ Trilium Notes is a hierarchical note taking application with focus on building l | ||||
| * [Relation maps](https://github.com/zadam/trilium/wiki/Relation-map) for visualizing notes and their relations | ||||
| * [Scripting](https://github.com/zadam/trilium/wiki/Scripts) - see [Advanced showcases](https://github.com/zadam/trilium/wiki/Advanced-showcases) | ||||
| * Scales well in both usability and performance upwards of 100 000 notes | ||||
| * Touch optimized [mobile frontend](https://github.com/zadam/trilium/wiki/Mobile-frontend) for smartphones and tablets | ||||
| * [Night theme](https://github.com/zadam/trilium/wiki/Themes) | ||||
| * [Evernote](https://github.com/zadam/trilium/wiki/Evernote-import) and [Markdown import & export](https://github.com/zadam/trilium/wiki/Markdown) | ||||
|  | ||||
| @@ -34,3 +35,15 @@ Trilium is provided as either desktop application (Linux, Windows, Mac) or web a | ||||
| [See wiki for complete list of documentation pages.](https://github.com/zadam/trilium/wiki/) | ||||
|  | ||||
| You can also read [Patterns of personal knowledge base](https://github.com/zadam/trilium/wiki/Patterns-of-personal-knowledge-base) to get some inspiration on how you might use Trilium. | ||||
|  | ||||
| ## Contribute | ||||
|  | ||||
| Use a browser based dev environment | ||||
|  | ||||
| [](https://gitpod.io/#https://github.com/zadam/trilium) | ||||
|  | ||||
| Or clone locally and run | ||||
| ``` | ||||
| npm install | ||||
| npm run start | ||||
| ``` | ||||
							
								
								
									
										7
									
								
								bin/build-debian.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										7
									
								
								bin/build-debian.sh
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,7 @@ | ||||
| #!/usr/bin/env bash | ||||
|  | ||||
| echo "Packaging debian x64 distribution..." | ||||
|  | ||||
| VERSION=`jq -r ".version" package.json` | ||||
|  | ||||
| ./node_modules/.bin/electron-installer-debian --config bin/deb-options.json --options.version=${VERSION} --arch amd64 | ||||
| @@ -3,22 +3,22 @@ | ||||
| BUILD_DIR=./dist/trilium-linux-x64 | ||||
| rm -rf $BUILD_DIR | ||||
|  | ||||
| # we build x64 as second so that we keep X64 binaries in node_modules for local development and server build | ||||
| echo "Rebuilding binaries for linux-x64" | ||||
| ./node_modules/.bin/electron-rebuild --arch=x64 | ||||
| rm -r node_modules/sqlite3/lib/binding/* | ||||
|  | ||||
| ./node_modules/.bin/electron-packager . --out=dist --executable-name=trilium --platform=linux --arch=x64 --overwrite | ||||
| cp -r bin/deps/linux-x64/sqlite/* node_modules/sqlite3/lib/binding/ | ||||
|  | ||||
| ./node_modules/.bin/electron-packager . --asar --out=dist --executable-name=trilium --platform=linux --arch=x64 --overwrite | ||||
|  | ||||
| mv "./dist/Trilium Notes-linux-x64" $BUILD_DIR | ||||
|  | ||||
| rm -r "$BUILD_DIR/resources/app/node_modules/sqlite3/lib/binding/*" | ||||
| cp src/public/images/app-icons/png/128x128.png $BUILD_DIR/icon.png | ||||
|  | ||||
| cp -r bin/deps/linux-x64/sqlite/electron* "$BUILD_DIR/resources/app/node_modules/sqlite3/lib/binding/" | ||||
|  | ||||
| rm -r $BUILD_DIR/resources/app/bin/deps | ||||
| # removing software WebGL binaries because they are pretty huge and not necessary | ||||
| rm -r $BUILD_DIR/swiftshader | ||||
|  | ||||
| echo "Packaging linux x64 electron distribution..." | ||||
| VERSION=`jq -r ".version" package.json` | ||||
| 7z a $BUILD_DIR-${VERSION}.7z $BUILD_DIR | ||||
|  | ||||
| cd dist | ||||
|  | ||||
| tar cJf trilium-linux-x64-${VERSION}.tar.xz trilium-linux-x64 | ||||
|   | ||||
| @@ -3,25 +3,30 @@ | ||||
| BUILD_DIR=./dist/trilium-mac-x64 | ||||
| rm -rf $BUILD_DIR | ||||
|  | ||||
| ./node_modules/.bin/electron-packager . --out=dist --executable-name=trilium --platform=darwin  --arch=x64 --overwrite --icon=src/public/images/app-icons/mac/icon.icns | ||||
| echo "Copying required mac binaries" | ||||
|  | ||||
| rm -r node_modules/sqlite3/lib/binding/* | ||||
| rm -r node_modules/mozjpeg/vendor/* | ||||
| rm -r node_modules/pngquant-bin/vendor/* | ||||
| rm -r node_modules/giflossy/vendor/* | ||||
|  | ||||
| cp -r bin/deps/mac-x64/sqlite/* node_modules/sqlite3/lib/binding/ | ||||
| cp bin/deps/mac-x64/image/cjpeg node_modules/mozjpeg/vendor/ | ||||
| cp bin/deps/mac-x64/image/pngquant node_modules/pngquant-bin/vendor/ | ||||
| cp bin/deps/mac-x64/image/gifsicle node_modules/giflossy/vendor/ | ||||
|  | ||||
| ./node_modules/.bin/electron-packager . --asar --out=dist --executable-name=trilium --platform=darwin --arch=x64 --overwrite --icon=src/public/images/app-icons/mac/icon.icns | ||||
|  | ||||
| # Mac build has by default useless directory level | ||||
| mv "./dist/Trilium Notes-darwin-x64" $BUILD_DIR | ||||
|  | ||||
| echo "Copying required mac binaries" | ||||
| ./bin/reset-local.sh | ||||
|  | ||||
| MAC_RES_DIR=$BUILD_DIR/Trilium\ Notes.app/Contents/Resources/app | ||||
|  | ||||
| rm -r "$MAC_RES_DIR/node_modules/sqlite3/lib/binding/*" | ||||
|  | ||||
| cp -r bin/deps/mac-x64/sqlite/* "$MAC_RES_DIR/node_modules/sqlite3/lib/binding/" | ||||
| cp bin/deps/mac-x64/image/cjpeg "$MAC_RES_DIR/node_modules/mozjpeg/vendor/" | ||||
| cp bin/deps/mac-x64/image/pngquant "$MAC_RES_DIR/node_modules/pngquant-bin/vendor/" | ||||
| cp bin/deps/mac-x64/image/gifsicle "$MAC_RES_DIR/node_modules/giflossy/vendor/" | ||||
|  | ||||
| rm -r "$MAC_RES_DIR/bin/deps" | ||||
|  | ||||
| echo "Packaging mac x64 electron distribution..." | ||||
| echo "Zipping mac x64 electron distribution..." | ||||
|  | ||||
| VERSION=`jq -r ".version" package.json` | ||||
| 7z a $BUILD_DIR-${VERSION}.7z $BUILD_DIR | ||||
|  | ||||
| cd dist | ||||
|  | ||||
| rm trilium-mac-x64-${VERSION}.zip | ||||
| zip -r9 --symlinks trilium-mac-x64-${VERSION}.zip trilium-mac-x64 | ||||
|   | ||||
| @@ -34,4 +34,5 @@ chmod 755 trilium.sh | ||||
| cd .. | ||||
|  | ||||
| VERSION=`jq -r ".version" ../package.json` | ||||
| 7z a trilium-linux-x64-server-${VERSION}.7z trilium-linux-x64-server | ||||
|  | ||||
| tar cJf trilium-linux-x64-server-${VERSION}.tar.xz trilium-linux-x64-server | ||||
| @@ -3,23 +3,30 @@ | ||||
| BUILD_DIR=./dist/trilium-windows-x64 | ||||
| rm -rf $BUILD_DIR | ||||
|  | ||||
| ./node_modules/.bin/electron-packager . --out=dist --executable-name=trilium --platform=win32  --arch=x64 --overwrite --icon=src/public/images/app-icons/win/icon.ico | ||||
| echo "Copying required windows binaries" | ||||
|  | ||||
| rm -r node_modules/sqlite3/lib/binding/* | ||||
| rm -r node_modules/mozjpeg/vendor/* | ||||
| rm -r node_modules/pngquant-bin/vendor/* | ||||
| rm -r node_modules/giflossy/vendor/* | ||||
|  | ||||
| cp -r bin/deps/win-x64/sqlite/* node_modules/sqlite3/lib/binding/ | ||||
| cp bin/deps/win-x64/image/cjpeg.exe node_modules/mozjpeg/vendor/ | ||||
| cp bin/deps/win-x64/image/pngquant.exe node_modules/pngquant-bin/vendor/ | ||||
| cp bin/deps/win-x64/image/gifsicle.exe node_modules/giflossy/vendor/ | ||||
|  | ||||
| ./node_modules/.bin/electron-packager . --asar --out=dist --executable-name=trilium --platform=win32  --arch=x64 --overwrite --icon=src/public/images/app-icons/win/icon.ico | ||||
|  | ||||
| mv "./dist/Trilium Notes-win32-x64" $BUILD_DIR | ||||
|  | ||||
| echo "Copying required windows binaries" | ||||
|  | ||||
| WIN_RES_DIR=$BUILD_DIR/resources/app | ||||
|  | ||||
| cp -r bin/deps/win-x64/sqlite/* $WIN_RES_DIR/node_modules/sqlite3/lib/binding/ | ||||
| cp bin/deps/win-x64/image/cjpeg.exe $WIN_RES_DIR/node_modules/mozjpeg/vendor/ | ||||
| cp bin/deps/win-x64/image/pngquant.exe $WIN_RES_DIR/node_modules/pngquant-bin/vendor/ | ||||
| cp bin/deps/win-x64/image/gifsicle.exe $WIN_RES_DIR/node_modules/giflossy/vendor/ | ||||
|  | ||||
| rm -r $WIN_RES_DIR/bin/deps | ||||
| # removing software WebGL binaries because they are pretty huge and not necessary | ||||
| rm -r $BUILD_DIR/swiftshader | ||||
|  | ||||
| echo "Packaging windows x64 electron distribution..." | ||||
| ./bin/reset-local.sh | ||||
|  | ||||
| echo "Zipping windows x64 electron distribution..." | ||||
| VERSION=`jq -r ".version" package.json` | ||||
| 7z a $BUILD_DIR-${VERSION}.7z $BUILD_DIR | ||||
|  | ||||
| cd dist | ||||
|  | ||||
| zip -r9 trilium-windows-x64-${VERSION}.zip trilium-windows-x64 | ||||
|   | ||||
| @@ -15,4 +15,7 @@ bin/build-mac-x64.sh | ||||
| # building X64 linux as the last so electron-rebuild will prepare X64 binaries for local development | ||||
| bin/build-linux-x64.sh | ||||
|  | ||||
| # this needs to be run after linux build | ||||
| bin/build-debian.sh | ||||
|  | ||||
| bin/build-server.sh | ||||
							
								
								
									
										14
									
								
								bin/deb-options.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								bin/deb-options.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,14 @@ | ||||
| { | ||||
|   "src": "dist/trilium-linux-x64", | ||||
|   "dest": "dist/", | ||||
|   "name": "trilium", | ||||
|   "productName": "Trilium Notes", | ||||
|   "genericName": "Note taker", | ||||
|   "description": "Trilium Notes is a hierarchical note taking application with focus on building large personal knowledge bases.", | ||||
|   "sections": "misc", | ||||
|   "maintainer": "zadam.apps@gmail.com", | ||||
|   "homepage": "https://github.com/zadam/trilium", | ||||
|   "bin": "trilium", | ||||
|   "icon": "dist/trilium-linux-x64/icon.png", | ||||
|   "categories": [ "Office" ] | ||||
| } | ||||
							
								
								
									
										0
									
								
								bin/deps/mac-x64/image/cjpeg
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							
							
						
						
									
										0
									
								
								bin/deps/mac-x64/image/cjpeg
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							
							
								
								
									
										0
									
								
								bin/deps/win-x64/image/cjpeg.exe
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							
							
						
						
									
										0
									
								
								bin/deps/win-x64/image/cjpeg.exe
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							| @@ -42,10 +42,11 @@ git push origin $TAG | ||||
|  | ||||
| bin/build.sh | ||||
|  | ||||
| LINUX_X64_BUILD=trilium-linux-x64-$VERSION.7z | ||||
| WINDOWS_X64_BUILD=trilium-windows-x64-$VERSION.7z | ||||
| MAC_X64_BUILD=trilium-mac-x64-$VERSION.7z | ||||
| SERVER_BUILD=trilium-linux-x64-server-$VERSION.7z | ||||
| LINUX_X64_BUILD=trilium-linux-x64-$VERSION.tar.xz | ||||
| DEBIAN_X64_BUILD=trilium_${VERSION}_amd64.deb | ||||
| WINDOWS_X64_BUILD=trilium-windows-x64-$VERSION.zip | ||||
| MAC_X64_BUILD=trilium-mac-x64-$VERSION.zip | ||||
| SERVER_BUILD=trilium-linux-x64-server-$VERSION.tar.xz | ||||
|  | ||||
| echo "Creating release in GitHub" | ||||
|  | ||||
| @@ -59,6 +60,13 @@ github-release release \ | ||||
|     --tag $TAG \ | ||||
|     --name "$TAG release" $EXTRA | ||||
|  | ||||
| echo "Uploading debian x64 package" | ||||
|  | ||||
| github-release upload \ | ||||
|     --tag $TAG \ | ||||
|     --name "$DEBIAN_X64_BUILD" \ | ||||
|     --file "dist/$DEBIAN_X64_BUILD" | ||||
|  | ||||
| echo "Uploading linux x64 build" | ||||
|  | ||||
| github-release upload \ | ||||
|   | ||||
							
								
								
									
										3
									
								
								bin/reset-local.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										3
									
								
								bin/reset-local.sh
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,3 @@ | ||||
| #!/usr/bin/env bash | ||||
|  | ||||
| ./node_modules/.bin/electron-rebuild --arch=x64 | ||||
							
								
								
									
										
											BIN
										
									
								
								db/demo.tar
									
									
									
									
									
								
							
							
						
						
									
										
											BIN
										
									
								
								db/demo.tar
									
									
									
									
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										62
									
								
								db/migrations/0122__add_iv_to_columns.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										62
									
								
								db/migrations/0122__add_iv_to_columns.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,62 @@ | ||||
| const sql = require('../../src/services/sql'); | ||||
|  | ||||
| function prependIv(cipherText, ivText) { | ||||
|     const arr = ivText.split("").map(c => parseInt(c) || 0); | ||||
|     const iv = Buffer.from(arr); | ||||
|     const payload = Buffer.from(cipherText, 'base64'); | ||||
|     const complete = Buffer.concat([iv, payload]); | ||||
|  | ||||
|     return complete.toString('base64'); | ||||
| } | ||||
|  | ||||
| async function updateEncryptedDataKey() { | ||||
|     const encryptedDataKey = await sql.getValue("SELECT value FROM options WHERE name = 'encryptedDataKey'"); | ||||
|     const encryptedDataKeyIv = await sql.getValue("SELECT value FROM options WHERE name = 'encryptedDataKeyIv'"); | ||||
|  | ||||
|     const newEncryptedDataKey = prependIv(encryptedDataKey, encryptedDataKeyIv); | ||||
|  | ||||
|     await sql.execute("UPDATE options SET value = ? WHERE name = 'encryptedDataKey'", [newEncryptedDataKey]); | ||||
|  | ||||
|     await sql.execute("DELETE FROM options WHERE name = 'encryptedDataKeyIv'"); | ||||
|     await sql.execute("DELETE FROM sync WHERE entityName = 'options' AND entityId = 'encryptedDataKeyIv'"); | ||||
| } | ||||
|  | ||||
| async function updateNotes() { | ||||
|     const protectedNotes = await sql.getRows("SELECT noteId, title, content FROM notes WHERE isProtected = 1"); | ||||
|  | ||||
|     for (const note of protectedNotes) { | ||||
|         if (note.title !== null) { | ||||
|             note.title = prependIv(note.title, "0" + note.noteId); | ||||
|         } | ||||
|  | ||||
|         if (note.content !== null) { | ||||
|             note.content = prependIv(note.content, "1" + note.noteId); | ||||
|         } | ||||
|  | ||||
|         await sql.execute("UPDATE notes SET title = ?, content = ? WHERE noteId = ?", [note.title, note.content, note.noteId]); | ||||
|     } | ||||
| } | ||||
|  | ||||
| async function updateNoteRevisions() { | ||||
|     const protectedNoteRevisions = await sql.getRows("SELECT noteRevisionId, title, content FROM note_revisions WHERE isProtected = 1"); | ||||
|  | ||||
|     for (const noteRevision of protectedNoteRevisions) { | ||||
|         if (noteRevision.title !== null) { | ||||
|             noteRevision.title = prependIv(noteRevision.title, "0" + noteRevision.noteRevisionId); | ||||
|         } | ||||
|  | ||||
|         if (noteRevision.content !== null) { | ||||
|             noteRevision.content = prependIv(noteRevision.content, "1" + noteRevision.noteRevisionId); | ||||
|         } | ||||
|  | ||||
|         await sql.execute("UPDATE note_revisions SET title = ?, content = ? WHERE noteRevisionId = ?", [noteRevision.title, noteRevision.content, noteRevision.noteRevisionId]); | ||||
|     } | ||||
| } | ||||
|  | ||||
| module.exports = async () => { | ||||
|     await updateEncryptedDataKey(); | ||||
|  | ||||
|     await updateNotes(); | ||||
|  | ||||
|     await updateNoteRevisions(); | ||||
| }; | ||||
							
								
								
									
										8
									
								
								db/migrations/0123__add_options_for_font_sizes.sql
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								db/migrations/0123__add_options_for_font_sizes.sql
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,8 @@ | ||||
| INSERT INTO options (name, value, dateCreated, dateModified, isSynced) | ||||
| VALUES ('mainFontSize', '100', '2019-01-13T18:31:00.874Z', '2019-01-13T18:31:00.874Z', 0); | ||||
|  | ||||
| INSERT INTO options (name, value, dateCreated, dateModified, isSynced) | ||||
| VALUES ('treeFontSize', '100', '2019-01-13T18:31:00.874Z', '2019-01-13T18:31:00.874Z', 0); | ||||
|  | ||||
| INSERT INTO options (name, value, dateCreated, dateModified, isSynced) | ||||
| VALUES ('detailFontSize', '110', '2019-01-13T18:31:00.874Z', '2019-01-13T18:31:00.874Z', 0); | ||||
							
								
								
									
										11
									
								
								db/migrations/0124__readd_font_sizes.sql
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								db/migrations/0124__readd_font_sizes.sql
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,11 @@ | ||||
| INSERT INTO options (name, value, dateCreated, dateModified, isSynced) | ||||
|   SELECT 'mainFontSize', '100', '2019-01-13T18:31:00.874Z', '2019-01-13T18:31:00.874Z', 0 | ||||
|     WHERE NOT EXISTS (SELECT 1 FROM options WHERE name = 'mainFontSize'); | ||||
|  | ||||
| INSERT INTO options (name, value, dateCreated, dateModified, isSynced) | ||||
|   SELECT 'treeFontSize', '100', '2019-01-13T18:31:00.874Z', '2019-01-13T18:31:00.874Z', 0 | ||||
|     WHERE NOT EXISTS (SELECT 1 FROM options WHERE name = 'treeFontSize'); | ||||
|  | ||||
| INSERT INTO options (name, value, dateCreated, dateModified, isSynced) | ||||
|   SELECT 'detailFontSize', '110', '2019-01-13T18:31:00.874Z', '2019-01-13T18:31:00.874Z', 0 | ||||
|     WHERE NOT EXISTS (SELECT 1 FROM options WHERE name = 'detailFontSize'); | ||||
							
								
								
									
										35
									
								
								db/migrations/0125__create_note_content_table.sql
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								db/migrations/0125__create_note_content_table.sql
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,35 @@ | ||||
| CREATE TABLE IF NOT EXISTS "note_contents" ( | ||||
|   `noteContentId`	TEXT NOT NULL, | ||||
|   `noteId`	TEXT NOT NULL, | ||||
|   `isProtected`	INT NOT NULL DEFAULT 0, | ||||
|   `content`	TEXT NULL DEFAULT NULL, | ||||
|   `hash` TEXT DEFAULT "" NOT NULL, | ||||
|   `dateCreated`	TEXT NOT NULL, | ||||
|   `dateModified` TEXT NOT NULL, | ||||
|   PRIMARY KEY(`noteContentId`) | ||||
| ); | ||||
|  | ||||
| CREATE UNIQUE INDEX `IDX_note_contents_noteId` ON `note_contents` (`noteId`); | ||||
|  | ||||
| INSERT INTO note_contents (noteContentId, noteId, isProtected, content, dateCreated, dateModified) | ||||
|   SELECT 'C' || SUBSTR(noteId, 2), noteId, isProtected, content, dateCreated, dateModified FROM notes; | ||||
|  | ||||
| CREATE TABLE IF NOT EXISTS "notes_mig" ( | ||||
|   `noteId`	TEXT NOT NULL, | ||||
|   `title`	TEXT NOT NULL DEFAULT "note", | ||||
|   `isProtected`	INT NOT NULL DEFAULT 0, | ||||
|   `type` TEXT NOT NULL DEFAULT 'text', | ||||
|   `mime` TEXT NOT NULL DEFAULT 'text/html', | ||||
|   `hash` TEXT DEFAULT "" NOT NULL, | ||||
|   `isDeleted`	INT NOT NULL DEFAULT 0, | ||||
|   `dateCreated`	TEXT NOT NULL, | ||||
|   `dateModified`	TEXT NOT NULL, | ||||
|   PRIMARY KEY(`noteId`) | ||||
| ); | ||||
|  | ||||
| INSERT INTO notes_mig (noteId, title, isProtected, isDeleted, dateCreated, dateModified, type, mime, hash) | ||||
| SELECT noteId, title, isProtected, isDeleted, dateCreated, dateModified, type, mime, hash FROM notes; | ||||
|  | ||||
| DROP TABLE notes; | ||||
|  | ||||
| ALTER TABLE notes_mig RENAME TO notes; | ||||
							
								
								
									
										2
									
								
								db/migrations/0126__fill_sync_for_note_contents.sql
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								db/migrations/0126__fill_sync_for_note_contents.sql
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,2 @@ | ||||
| INSERT OR REPLACE INTO sync (entityName, entityId, sourceId, syncDate) | ||||
| SELECT 'note_contents', noteContentId, '', '2019-03-02T18:07:29.182Z' FROM note_contents; | ||||
| @@ -96,19 +96,6 @@ CREATE TABLE attributes | ||||
|   hash         TEXT default "" not null, isInheritable int DEFAULT 0 NULL); | ||||
| CREATE INDEX IDX_attributes_name_value | ||||
|   on attributes (name, value); | ||||
| CREATE TABLE IF NOT EXISTS "notes" ( | ||||
|   `noteId`	TEXT NOT NULL, | ||||
|   `title`	TEXT NOT NULL DEFAULT "note", | ||||
|   `content`	TEXT NULL DEFAULT NULL, | ||||
|   `isProtected`	INT NOT NULL DEFAULT 0, | ||||
|   `type` TEXT NOT NULL DEFAULT 'text', | ||||
|   `mime` TEXT NOT NULL DEFAULT 'text/html', | ||||
|   `hash` TEXT DEFAULT "" NOT NULL, | ||||
|   `isDeleted`	INT NOT NULL DEFAULT 0, | ||||
|   `dateCreated`	TEXT NOT NULL, | ||||
|   `dateModified`	TEXT NOT NULL, | ||||
|   PRIMARY KEY(`noteId`) | ||||
| ); | ||||
| CREATE TABLE IF NOT EXISTS "links" ( | ||||
|   `linkId`	TEXT NOT NULL, | ||||
|   `noteId`	TEXT NOT NULL, | ||||
| @@ -130,3 +117,26 @@ CREATE INDEX IDX_attributes_noteId_index | ||||
|   on attributes (noteId); | ||||
| CREATE INDEX IDX_attributes_value_index | ||||
|   on attributes (value); | ||||
| CREATE TABLE IF NOT EXISTS "note_contents" ( | ||||
|   `noteContentId`	TEXT NOT NULL, | ||||
|   `noteId`	TEXT NOT NULL, | ||||
|   `isProtected`	INT NOT NULL DEFAULT 0, | ||||
|   `content`	TEXT NULL DEFAULT NULL, | ||||
|   `hash` TEXT DEFAULT "" NOT NULL, | ||||
|   `dateCreated`	TEXT NOT NULL, | ||||
|   `dateModified` TEXT NOT NULL, | ||||
|   PRIMARY KEY(`noteContentId`) | ||||
| ); | ||||
| CREATE UNIQUE INDEX `IDX_note_contents_noteId` ON `note_contents` (`noteId`); | ||||
| CREATE TABLE IF NOT EXISTS "notes" ( | ||||
|   `noteId`	TEXT NOT NULL, | ||||
|   `title`	TEXT NOT NULL DEFAULT "note", | ||||
|   `isProtected`	INT NOT NULL DEFAULT 0, | ||||
|   `type` TEXT NOT NULL DEFAULT 'text', | ||||
|   `mime` TEXT NOT NULL DEFAULT 'text/html', | ||||
|   `hash` TEXT DEFAULT "" NOT NULL, | ||||
|   `isDeleted`	INT NOT NULL DEFAULT 0, | ||||
|   `dateCreated`	TEXT NOT NULL, | ||||
|   `dateModified`	TEXT NOT NULL, | ||||
|   PRIMARY KEY(`noteId`) | ||||
| ); | ||||
|   | ||||
| @@ -282,7 +282,7 @@ | ||||
| </div> | ||||
|  | ||||
| <nav> | ||||
|     <h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="ApiToken.html">ApiToken</a></li><li><a href="Attribute.html">Attribute</a></li><li><a href="BackendScriptApi.html">BackendScriptApi</a></li><li><a href="Branch.html">Branch</a></li><li><a href="Entity.html">Entity</a></li><li><a href="Link.html">Link</a></li><li><a href="Note.html">Note</a></li><li><a href="NoteRevision.html">NoteRevision</a></li><li><a href="Option.html">Option</a></li><li><a href="RecentNote.html">RecentNote</a></li></ul><h3><a href="global.html">Global</a></h3> | ||||
|     <h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="ApiToken.html">ApiToken</a></li><li><a href="Attribute.html">Attribute</a></li><li><a href="BackendScriptApi.html">BackendScriptApi</a></li><li><a href="Branch.html">Branch</a></li><li><a href="Entity.html">Entity</a></li><li><a href="Link.html">Link</a></li><li><a href="Note.html">Note</a></li><li><a href="NoteContent.html">NoteContent</a></li><li><a href="NoteRevision.html">NoteRevision</a></li><li><a href="Option.html">Option</a></li><li><a href="RecentNote.html">RecentNote</a></li></ul><h3><a href="global.html">Global</a></h3> | ||||
| </nav> | ||||
|  | ||||
| <br class="clear"> | ||||
|   | ||||
| @@ -724,7 +724,7 @@ | ||||
| </div> | ||||
|  | ||||
| <nav> | ||||
|     <h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="ApiToken.html">ApiToken</a></li><li><a href="Attribute.html">Attribute</a></li><li><a href="BackendScriptApi.html">BackendScriptApi</a></li><li><a href="Branch.html">Branch</a></li><li><a href="Entity.html">Entity</a></li><li><a href="Link.html">Link</a></li><li><a href="Note.html">Note</a></li><li><a href="NoteRevision.html">NoteRevision</a></li><li><a href="Option.html">Option</a></li><li><a href="RecentNote.html">RecentNote</a></li></ul><h3><a href="global.html">Global</a></h3> | ||||
|     <h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="ApiToken.html">ApiToken</a></li><li><a href="Attribute.html">Attribute</a></li><li><a href="BackendScriptApi.html">BackendScriptApi</a></li><li><a href="Branch.html">Branch</a></li><li><a href="Entity.html">Entity</a></li><li><a href="Link.html">Link</a></li><li><a href="Note.html">Note</a></li><li><a href="NoteContent.html">NoteContent</a></li><li><a href="NoteRevision.html">NoteRevision</a></li><li><a href="Option.html">Option</a></li><li><a href="RecentNote.html">RecentNote</a></li></ul><h3><a href="global.html">Global</a></h3> | ||||
| </nav> | ||||
|  | ||||
| <br class="clear"> | ||||
|   | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -505,7 +505,7 @@ Each note can have multiple (at least one) branches, meaning it can be placed in | ||||
| </div> | ||||
|  | ||||
| <nav> | ||||
|     <h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="ApiToken.html">ApiToken</a></li><li><a href="Attribute.html">Attribute</a></li><li><a href="BackendScriptApi.html">BackendScriptApi</a></li><li><a href="Branch.html">Branch</a></li><li><a href="Entity.html">Entity</a></li><li><a href="Link.html">Link</a></li><li><a href="Note.html">Note</a></li><li><a href="NoteRevision.html">NoteRevision</a></li><li><a href="Option.html">Option</a></li><li><a href="RecentNote.html">RecentNote</a></li></ul><h3><a href="global.html">Global</a></h3> | ||||
|     <h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="ApiToken.html">ApiToken</a></li><li><a href="Attribute.html">Attribute</a></li><li><a href="BackendScriptApi.html">BackendScriptApi</a></li><li><a href="Branch.html">Branch</a></li><li><a href="Entity.html">Entity</a></li><li><a href="Link.html">Link</a></li><li><a href="Note.html">Note</a></li><li><a href="NoteContent.html">NoteContent</a></li><li><a href="NoteRevision.html">NoteRevision</a></li><li><a href="Option.html">Option</a></li><li><a href="RecentNote.html">RecentNote</a></li></ul><h3><a href="global.html">Global</a></h3> | ||||
| </nav> | ||||
|  | ||||
| <br class="clear"> | ||||
|   | ||||
| @@ -210,7 +210,7 @@ | ||||
| </div> | ||||
|  | ||||
| <nav> | ||||
|     <h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="ApiToken.html">ApiToken</a></li><li><a href="Attribute.html">Attribute</a></li><li><a href="BackendScriptApi.html">BackendScriptApi</a></li><li><a href="Branch.html">Branch</a></li><li><a href="Entity.html">Entity</a></li><li><a href="Link.html">Link</a></li><li><a href="Note.html">Note</a></li><li><a href="NoteRevision.html">NoteRevision</a></li><li><a href="Option.html">Option</a></li><li><a href="RecentNote.html">RecentNote</a></li></ul><h3><a href="global.html">Global</a></h3> | ||||
|     <h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="ApiToken.html">ApiToken</a></li><li><a href="Attribute.html">Attribute</a></li><li><a href="BackendScriptApi.html">BackendScriptApi</a></li><li><a href="Branch.html">Branch</a></li><li><a href="Entity.html">Entity</a></li><li><a href="Link.html">Link</a></li><li><a href="Note.html">Note</a></li><li><a href="NoteContent.html">NoteContent</a></li><li><a href="NoteRevision.html">NoteRevision</a></li><li><a href="Option.html">Option</a></li><li><a href="RecentNote.html">RecentNote</a></li></ul><h3><a href="global.html">Global</a></h3> | ||||
| </nav> | ||||
|  | ||||
| <br class="clear"> | ||||
|   | ||||
| @@ -352,7 +352,7 @@ this is different concept than attribute/relation.</div> | ||||
| </div> | ||||
|  | ||||
| <nav> | ||||
|     <h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="ApiToken.html">ApiToken</a></li><li><a href="Attribute.html">Attribute</a></li><li><a href="BackendScriptApi.html">BackendScriptApi</a></li><li><a href="Branch.html">Branch</a></li><li><a href="Entity.html">Entity</a></li><li><a href="Link.html">Link</a></li><li><a href="Note.html">Note</a></li><li><a href="NoteRevision.html">NoteRevision</a></li><li><a href="Option.html">Option</a></li><li><a href="RecentNote.html">RecentNote</a></li></ul><h3><a href="global.html">Global</a></h3> | ||||
|     <h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="ApiToken.html">ApiToken</a></li><li><a href="Attribute.html">Attribute</a></li><li><a href="BackendScriptApi.html">BackendScriptApi</a></li><li><a href="Branch.html">Branch</a></li><li><a href="Entity.html">Entity</a></li><li><a href="Link.html">Link</a></li><li><a href="Note.html">Note</a></li><li><a href="NoteContent.html">NoteContent</a></li><li><a href="NoteRevision.html">NoteRevision</a></li><li><a href="Option.html">Option</a></li><li><a href="RecentNote.html">RecentNote</a></li></ul><h3><a href="global.html">Global</a></h3> | ||||
| </nav> | ||||
|  | ||||
| <br class="clear"> | ||||
|   | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										494
									
								
								docs/backend_api/NoteContent.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										494
									
								
								docs/backend_api/NoteContent.html
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,494 @@ | ||||
| <!DOCTYPE html> | ||||
| <html lang="en"> | ||||
| <head> | ||||
|     <meta charset="utf-8"> | ||||
|     <title>JSDoc: Class: NoteContent</title> | ||||
|  | ||||
|     <script src="scripts/prettify/prettify.js"> </script> | ||||
|     <script src="scripts/prettify/lang-css.js"> </script> | ||||
|     <!--[if lt IE 9]> | ||||
|       <script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script> | ||||
|     <![endif]--> | ||||
|     <link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css"> | ||||
|     <link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css"> | ||||
| </head> | ||||
|  | ||||
| <body> | ||||
|  | ||||
| <div id="main"> | ||||
|  | ||||
|     <h1 class="page-title">Class: NoteContent</h1> | ||||
|  | ||||
|      | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| <section> | ||||
|  | ||||
| <header> | ||||
|      | ||||
|         <h2><span class="attribs"><span class="type-signature"></span></span>NoteContent<span class="signature">(row)</span><span class="type-signature"></span></h2> | ||||
|          | ||||
|             <div class="class-description">This represents a Note which is a central object in the Trilium Notes project.</div> | ||||
|          | ||||
|      | ||||
| </header> | ||||
|  | ||||
| <article> | ||||
|     <div class="container-overview"> | ||||
|      | ||||
|          | ||||
|  | ||||
|      | ||||
|     <h2>Constructor</h2> | ||||
|      | ||||
|  | ||||
|      | ||||
|     <h4 class="name" id="NoteContent"><span class="type-signature"></span>new NoteContent<span class="signature">(row)</span><span class="type-signature"></span></h4> | ||||
|      | ||||
|  | ||||
|      | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|     <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>row</code></td> | ||||
|              | ||||
|  | ||||
|             <td class="type"> | ||||
|              | ||||
|             </td> | ||||
|  | ||||
|              | ||||
|  | ||||
|              | ||||
|  | ||||
|             <td class="description last">object containing database row from "note_contents" table</td> | ||||
|         </tr> | ||||
|  | ||||
|      | ||||
|     </tbody> | ||||
| </table> | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|     <h5 class="subsection-title">Properties:</h5> | ||||
|  | ||||
|      | ||||
|  | ||||
| <table class="props"> | ||||
|     <thead> | ||||
|     <tr> | ||||
|          | ||||
|         <th>Name</th> | ||||
|          | ||||
|  | ||||
|         <th>Type</th> | ||||
|  | ||||
|          | ||||
|  | ||||
|          | ||||
|  | ||||
|         <th class="last">Description</th> | ||||
|     </tr> | ||||
|     </thead> | ||||
|  | ||||
|     <tbody> | ||||
|      | ||||
|  | ||||
|         <tr> | ||||
|              | ||||
|                 <td class="name"><code>noteContentId</code></td> | ||||
|              | ||||
|  | ||||
|             <td class="type"> | ||||
|              | ||||
|                  | ||||
| <span class="param-type">string</span> | ||||
|  | ||||
|  | ||||
|              | ||||
|             </td> | ||||
|  | ||||
|              | ||||
|  | ||||
|              | ||||
|  | ||||
|             <td class="description last">primary key</td> | ||||
|         </tr> | ||||
|  | ||||
|      | ||||
|  | ||||
|         <tr> | ||||
|              | ||||
|                 <td class="name"><code>noteId</code></td> | ||||
|              | ||||
|  | ||||
|             <td class="type"> | ||||
|              | ||||
|                  | ||||
| <span class="param-type">string</span> | ||||
|  | ||||
|  | ||||
|              | ||||
|             </td> | ||||
|  | ||||
|              | ||||
|  | ||||
|              | ||||
|  | ||||
|             <td class="description last">reference to owning note</td> | ||||
|         </tr> | ||||
|  | ||||
|      | ||||
|  | ||||
|         <tr> | ||||
|              | ||||
|                 <td class="name"><code>isProtected</code></td> | ||||
|              | ||||
|  | ||||
|             <td class="type"> | ||||
|              | ||||
|                  | ||||
| <span class="param-type">boolean</span> | ||||
|  | ||||
|  | ||||
|              | ||||
|             </td> | ||||
|  | ||||
|              | ||||
|  | ||||
|              | ||||
|  | ||||
|             <td class="description last">true if note content is protected</td> | ||||
|         </tr> | ||||
|  | ||||
|      | ||||
|  | ||||
|         <tr> | ||||
|              | ||||
|                 <td class="name"><code>content</code></td> | ||||
|              | ||||
|  | ||||
|             <td class="type"> | ||||
|              | ||||
|                  | ||||
| <span class="param-type">blob</span> | ||||
|  | ||||
|  | ||||
|              | ||||
|             </td> | ||||
|  | ||||
|              | ||||
|  | ||||
|              | ||||
|  | ||||
|             <td class="description last">note content - e.g. HTML text for text notes, file payload for files</td> | ||||
|         </tr> | ||||
|  | ||||
|      | ||||
|  | ||||
|         <tr> | ||||
|              | ||||
|                 <td class="name"><code>dateCreated</code></td> | ||||
|              | ||||
|  | ||||
|             <td class="type"> | ||||
|              | ||||
|                  | ||||
| <span class="param-type">string</span> | ||||
|  | ||||
|  | ||||
|              | ||||
|             </td> | ||||
|  | ||||
|              | ||||
|  | ||||
|              | ||||
|  | ||||
|             <td class="description last"></td> | ||||
|         </tr> | ||||
|  | ||||
|      | ||||
|  | ||||
|         <tr> | ||||
|              | ||||
|                 <td class="name"><code>dateModified</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="entities_note_content.js.html">entities/note_content.js</a>, <a href="entities_note_content.js.html#line20">line 20</a> | ||||
|     </li></ul></dd> | ||||
|      | ||||
|  | ||||
|      | ||||
|  | ||||
|      | ||||
|  | ||||
|      | ||||
| </dl> | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|      | ||||
|     </div> | ||||
|  | ||||
|      | ||||
|         <h3 class="subsection-title">Extends</h3> | ||||
|  | ||||
|          | ||||
|  | ||||
|  | ||||
|     <ul> | ||||
|         <li><a href="Entity.html">Entity</a></li> | ||||
|     </ul> | ||||
|  | ||||
|  | ||||
|      | ||||
|  | ||||
|      | ||||
|  | ||||
|      | ||||
|  | ||||
|      | ||||
|  | ||||
|      | ||||
|  | ||||
|      | ||||
|  | ||||
|      | ||||
|  | ||||
|      | ||||
|         <h3 class="subsection-title">Methods</h3> | ||||
|  | ||||
|          | ||||
|              | ||||
|  | ||||
|      | ||||
|  | ||||
|      | ||||
|     <h4 class="name" id="getNote"><span class="type-signature">(async) </span>getNote<span class="signature">()</span><span class="type-signature"> → {Promise.<<a href="Note.html">Note</a>>}</span></h4> | ||||
|      | ||||
|  | ||||
|      | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| <dl class="details"> | ||||
|  | ||||
|      | ||||
|  | ||||
|      | ||||
|  | ||||
|      | ||||
|  | ||||
|      | ||||
|  | ||||
|      | ||||
|  | ||||
|      | ||||
|  | ||||
|      | ||||
|  | ||||
|      | ||||
|  | ||||
|      | ||||
|  | ||||
|      | ||||
|  | ||||
|      | ||||
|  | ||||
|      | ||||
|  | ||||
|      | ||||
|     <dt class="tag-source">Source:</dt> | ||||
|     <dd class="tag-source"><ul class="dummy"><li> | ||||
|         <a href="entities_note_content.js.html">entities/note_content.js</a>, <a href="entities_note_content.js.html#line63">line 63</a> | ||||
|     </li></ul></dd> | ||||
|      | ||||
|  | ||||
|      | ||||
|  | ||||
|      | ||||
|  | ||||
|      | ||||
| </dl> | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| <h5>Returns:</h5> | ||||
|  | ||||
|          | ||||
|  | ||||
|  | ||||
| <dl> | ||||
|     <dt> | ||||
|         Type | ||||
|     </dt> | ||||
|     <dd> | ||||
|          | ||||
| <span class="param-type">Promise.<<a href="Note.html">Note</a>></span> | ||||
|  | ||||
|  | ||||
|     </dd> | ||||
| </dl> | ||||
|  | ||||
|      | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|          | ||||
|      | ||||
|  | ||||
|      | ||||
|  | ||||
|      | ||||
| </article> | ||||
|  | ||||
| </section> | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| </div> | ||||
|  | ||||
| <nav> | ||||
|     <h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="ApiToken.html">ApiToken</a></li><li><a href="Attribute.html">Attribute</a></li><li><a href="BackendScriptApi.html">BackendScriptApi</a></li><li><a href="Branch.html">Branch</a></li><li><a href="Entity.html">Entity</a></li><li><a href="Link.html">Link</a></li><li><a href="Note.html">Note</a></li><li><a href="NoteContent.html">NoteContent</a></li><li><a href="NoteRevision.html">NoteRevision</a></li><li><a href="Option.html">Option</a></li><li><a href="RecentNote.html">RecentNote</a></li></ul><h3><a href="global.html">Global</a></h3> | ||||
| </nav> | ||||
|  | ||||
| <br class="clear"> | ||||
|  | ||||
| <footer> | ||||
|     Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 3.5.5</a> | ||||
| </footer> | ||||
|  | ||||
| <script> prettyPrint(); </script> | ||||
| <script src="scripts/linenumber.js"> </script> | ||||
| </body> | ||||
| </html> | ||||
| @@ -397,7 +397,7 @@ | ||||
| </div> | ||||
|  | ||||
| <nav> | ||||
|     <h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="ApiToken.html">ApiToken</a></li><li><a href="Attribute.html">Attribute</a></li><li><a href="BackendScriptApi.html">BackendScriptApi</a></li><li><a href="Branch.html">Branch</a></li><li><a href="Entity.html">Entity</a></li><li><a href="Link.html">Link</a></li><li><a href="Note.html">Note</a></li><li><a href="NoteRevision.html">NoteRevision</a></li><li><a href="Option.html">Option</a></li><li><a href="RecentNote.html">RecentNote</a></li></ul><h3><a href="global.html">Global</a></h3> | ||||
|     <h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="ApiToken.html">ApiToken</a></li><li><a href="Attribute.html">Attribute</a></li><li><a href="BackendScriptApi.html">BackendScriptApi</a></li><li><a href="Branch.html">Branch</a></li><li><a href="Entity.html">Entity</a></li><li><a href="Link.html">Link</a></li><li><a href="Note.html">Note</a></li><li><a href="NoteContent.html">NoteContent</a></li><li><a href="NoteRevision.html">NoteRevision</a></li><li><a href="Option.html">Option</a></li><li><a href="RecentNote.html">RecentNote</a></li></ul><h3><a href="global.html">Global</a></h3> | ||||
| </nav> | ||||
|  | ||||
| <br class="clear"> | ||||
|   | ||||
| @@ -305,7 +305,7 @@ | ||||
| </div> | ||||
|  | ||||
| <nav> | ||||
|     <h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="ApiToken.html">ApiToken</a></li><li><a href="Attribute.html">Attribute</a></li><li><a href="BackendScriptApi.html">BackendScriptApi</a></li><li><a href="Branch.html">Branch</a></li><li><a href="Entity.html">Entity</a></li><li><a href="Link.html">Link</a></li><li><a href="Note.html">Note</a></li><li><a href="NoteRevision.html">NoteRevision</a></li><li><a href="Option.html">Option</a></li><li><a href="RecentNote.html">RecentNote</a></li></ul><h3><a href="global.html">Global</a></h3> | ||||
|     <h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="ApiToken.html">ApiToken</a></li><li><a href="Attribute.html">Attribute</a></li><li><a href="BackendScriptApi.html">BackendScriptApi</a></li><li><a href="Branch.html">Branch</a></li><li><a href="Entity.html">Entity</a></li><li><a href="Link.html">Link</a></li><li><a href="Note.html">Note</a></li><li><a href="NoteContent.html">NoteContent</a></li><li><a href="NoteRevision.html">NoteRevision</a></li><li><a href="Option.html">Option</a></li><li><a href="RecentNote.html">RecentNote</a></li></ul><h3><a href="global.html">Global</a></h3> | ||||
| </nav> | ||||
|  | ||||
| <br class="clear"> | ||||
|   | ||||
| @@ -282,7 +282,7 @@ | ||||
| </div> | ||||
|  | ||||
| <nav> | ||||
|     <h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="ApiToken.html">ApiToken</a></li><li><a href="Attribute.html">Attribute</a></li><li><a href="BackendScriptApi.html">BackendScriptApi</a></li><li><a href="Branch.html">Branch</a></li><li><a href="Entity.html">Entity</a></li><li><a href="Link.html">Link</a></li><li><a href="Note.html">Note</a></li><li><a href="NoteRevision.html">NoteRevision</a></li><li><a href="Option.html">Option</a></li><li><a href="RecentNote.html">RecentNote</a></li></ul><h3><a href="global.html">Global</a></h3> | ||||
|     <h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="ApiToken.html">ApiToken</a></li><li><a href="Attribute.html">Attribute</a></li><li><a href="BackendScriptApi.html">BackendScriptApi</a></li><li><a href="Branch.html">Branch</a></li><li><a href="Entity.html">Entity</a></li><li><a href="Link.html">Link</a></li><li><a href="Note.html">Note</a></li><li><a href="NoteContent.html">NoteContent</a></li><li><a href="NoteRevision.html">NoteRevision</a></li><li><a href="Option.html">Option</a></li><li><a href="RecentNote.html">RecentNote</a></li></ul><h3><a href="global.html">Global</a></h3> | ||||
| </nav> | ||||
|  | ||||
| <br class="clear"> | ||||
|   | ||||
| @@ -69,7 +69,7 @@ module.exports = ApiToken;</code></pre> | ||||
| </div> | ||||
|  | ||||
| <nav> | ||||
|     <h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="ApiToken.html">ApiToken</a></li><li><a href="Attribute.html">Attribute</a></li><li><a href="BackendScriptApi.html">BackendScriptApi</a></li><li><a href="Branch.html">Branch</a></li><li><a href="Entity.html">Entity</a></li><li><a href="Link.html">Link</a></li><li><a href="Note.html">Note</a></li><li><a href="NoteRevision.html">NoteRevision</a></li><li><a href="Option.html">Option</a></li><li><a href="RecentNote.html">RecentNote</a></li></ul><h3><a href="global.html">Global</a></h3> | ||||
|     <h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="ApiToken.html">ApiToken</a></li><li><a href="Attribute.html">Attribute</a></li><li><a href="BackendScriptApi.html">BackendScriptApi</a></li><li><a href="Branch.html">Branch</a></li><li><a href="Entity.html">Entity</a></li><li><a href="Link.html">Link</a></li><li><a href="Note.html">Note</a></li><li><a href="NoteContent.html">NoteContent</a></li><li><a href="NoteRevision.html">NoteRevision</a></li><li><a href="Option.html">Option</a></li><li><a href="RecentNote.html">RecentNote</a></li></ul><h3><a href="global.html">Global</a></h3> | ||||
| </nav> | ||||
|  | ||||
| <br class="clear"> | ||||
|   | ||||
| @@ -150,7 +150,7 @@ module.exports = Attribute;</code></pre> | ||||
| </div> | ||||
|  | ||||
| <nav> | ||||
|     <h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="ApiToken.html">ApiToken</a></li><li><a href="Attribute.html">Attribute</a></li><li><a href="BackendScriptApi.html">BackendScriptApi</a></li><li><a href="Branch.html">Branch</a></li><li><a href="Entity.html">Entity</a></li><li><a href="Link.html">Link</a></li><li><a href="Note.html">Note</a></li><li><a href="NoteRevision.html">NoteRevision</a></li><li><a href="Option.html">Option</a></li><li><a href="RecentNote.html">RecentNote</a></li></ul><h3><a href="global.html">Global</a></h3> | ||||
|     <h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="ApiToken.html">ApiToken</a></li><li><a href="Attribute.html">Attribute</a></li><li><a href="BackendScriptApi.html">BackendScriptApi</a></li><li><a href="Branch.html">Branch</a></li><li><a href="Entity.html">Entity</a></li><li><a href="Link.html">Link</a></li><li><a href="Note.html">Note</a></li><li><a href="NoteContent.html">NoteContent</a></li><li><a href="NoteRevision.html">NoteRevision</a></li><li><a href="Option.html">Option</a></li><li><a href="RecentNote.html">RecentNote</a></li></ul><h3><a href="global.html">Global</a></h3> | ||||
| </nav> | ||||
|  | ||||
| <br class="clear"> | ||||
|   | ||||
| @@ -104,7 +104,7 @@ module.exports = Branch;</code></pre> | ||||
| </div> | ||||
|  | ||||
| <nav> | ||||
|     <h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="ApiToken.html">ApiToken</a></li><li><a href="Attribute.html">Attribute</a></li><li><a href="BackendScriptApi.html">BackendScriptApi</a></li><li><a href="Branch.html">Branch</a></li><li><a href="Entity.html">Entity</a></li><li><a href="Link.html">Link</a></li><li><a href="Note.html">Note</a></li><li><a href="NoteRevision.html">NoteRevision</a></li><li><a href="Option.html">Option</a></li><li><a href="RecentNote.html">RecentNote</a></li></ul><h3><a href="global.html">Global</a></h3> | ||||
|     <h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="ApiToken.html">ApiToken</a></li><li><a href="Attribute.html">Attribute</a></li><li><a href="BackendScriptApi.html">BackendScriptApi</a></li><li><a href="Branch.html">Branch</a></li><li><a href="Entity.html">Entity</a></li><li><a href="Link.html">Link</a></li><li><a href="Note.html">Note</a></li><li><a href="NoteContent.html">NoteContent</a></li><li><a href="NoteRevision.html">NoteRevision</a></li><li><a href="Option.html">Option</a></li><li><a href="RecentNote.html">RecentNote</a></li></ul><h3><a href="global.html">Global</a></h3> | ||||
| </nav> | ||||
|  | ||||
| <br class="clear"> | ||||
|   | ||||
| @@ -36,7 +36,10 @@ class Entity { | ||||
|      */ | ||||
|     constructor(row = {}) { | ||||
|         for (const key in row) { | ||||
|             this[key] = row[key]; | ||||
|             // ! is used when joint-fetching notes and note_contents objects for performance | ||||
|             if (!key.startsWith('!')) { | ||||
|                 this[key] = row[key]; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         if ('isDeleted' in this) { | ||||
| @@ -87,7 +90,7 @@ module.exports = Entity;</code></pre> | ||||
| </div> | ||||
|  | ||||
| <nav> | ||||
|     <h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="ApiToken.html">ApiToken</a></li><li><a href="Attribute.html">Attribute</a></li><li><a href="BackendScriptApi.html">BackendScriptApi</a></li><li><a href="Branch.html">Branch</a></li><li><a href="Entity.html">Entity</a></li><li><a href="Link.html">Link</a></li><li><a href="Note.html">Note</a></li><li><a href="NoteRevision.html">NoteRevision</a></li><li><a href="Option.html">Option</a></li><li><a href="RecentNote.html">RecentNote</a></li></ul><h3><a href="global.html">Global</a></h3> | ||||
|     <h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="ApiToken.html">ApiToken</a></li><li><a href="Attribute.html">Attribute</a></li><li><a href="BackendScriptApi.html">BackendScriptApi</a></li><li><a href="Branch.html">Branch</a></li><li><a href="Entity.html">Entity</a></li><li><a href="Link.html">Link</a></li><li><a href="Note.html">Note</a></li><li><a href="NoteContent.html">NoteContent</a></li><li><a href="NoteRevision.html">NoteRevision</a></li><li><a href="Option.html">Option</a></li><li><a href="RecentNote.html">RecentNote</a></li></ul><h3><a href="global.html">Global</a></h3> | ||||
| </nav> | ||||
|  | ||||
| <br class="clear"> | ||||
|   | ||||
| @@ -86,7 +86,7 @@ module.exports = Link;</code></pre> | ||||
| </div> | ||||
|  | ||||
| <nav> | ||||
|     <h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="ApiToken.html">ApiToken</a></li><li><a href="Attribute.html">Attribute</a></li><li><a href="BackendScriptApi.html">BackendScriptApi</a></li><li><a href="Branch.html">Branch</a></li><li><a href="Entity.html">Entity</a></li><li><a href="Link.html">Link</a></li><li><a href="Note.html">Note</a></li><li><a href="NoteRevision.html">NoteRevision</a></li><li><a href="Option.html">Option</a></li><li><a href="RecentNote.html">RecentNote</a></li></ul><h3><a href="global.html">Global</a></h3> | ||||
|     <h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="ApiToken.html">ApiToken</a></li><li><a href="Attribute.html">Attribute</a></li><li><a href="BackendScriptApi.html">BackendScriptApi</a></li><li><a href="Branch.html">Branch</a></li><li><a href="Entity.html">Entity</a></li><li><a href="Link.html">Link</a></li><li><a href="Note.html">Note</a></li><li><a href="NoteContent.html">NoteContent</a></li><li><a href="NoteRevision.html">NoteRevision</a></li><li><a href="Option.html">Option</a></li><li><a href="RecentNote.html">RecentNote</a></li></ul><h3><a href="global.html">Global</a></h3> | ||||
| </nav> | ||||
|  | ||||
| <br class="clear"> | ||||
|   | ||||
| @@ -30,6 +30,7 @@ | ||||
|  | ||||
| const Entity = require('./entity'); | ||||
| const Attribute = require('./attribute'); | ||||
| const NoteContent = require('./note_content'); | ||||
| const protectedSessionService = require('../services/protected_session'); | ||||
| const repository = require('../services/repository'); | ||||
| const sql = require('../services/sql'); | ||||
| @@ -47,7 +48,6 @@ const RELATION_DEFINITION = 'relation-definition'; | ||||
|  * @property {string} type - one of "text", "code", "file" or "render" | ||||
|  * @property {string} mime - MIME type, e.g. "text/html" | ||||
|  * @property {string} title - note title | ||||
|  * @property {string} content - note content - e.g. HTML text for text notes, file payload for files | ||||
|  * @property {boolean} isProtected - true if note is protected | ||||
|  * @property {boolean} isDeleted - true if note is deleted | ||||
|  * @property {string} dateCreated | ||||
| @@ -58,7 +58,7 @@ const RELATION_DEFINITION = 'relation-definition'; | ||||
| class Note extends Entity { | ||||
|     static get entityName() { return "notes"; } | ||||
|     static get primaryKeyName() { return "noteId"; } | ||||
|     static get hashedProperties() { return ["noteId", "title", "content", "type", "isProtected", "isDeleted"]; } | ||||
|     static get hashedProperties() { return ["noteId", "title", "type", "isProtected", "isDeleted"]; } | ||||
|  | ||||
|     /** | ||||
|      * @param row - object containing database row from "notes" table | ||||
| @@ -75,19 +75,64 @@ class Note extends Entity { | ||||
|         if (this.isProtected && this.noteId) { | ||||
|             this.isContentAvailable = protectedSessionService.isProtectedSessionAvailable(); | ||||
|  | ||||
|             protectedSessionService.decryptNote(this); | ||||
|             if (this.isContentAvailable) { | ||||
|                 protectedSessionService.decryptNote(this); | ||||
|             } | ||||
|             else { | ||||
|                 // saving ciphertexts in case we do want to update protected note outside of protected session | ||||
|                 // (which is allowed) | ||||
|                 this.titleCipherText = this.title; | ||||
|                 this.title = "[protected]"; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         this.setContent(this.content); | ||||
|     } | ||||
|  | ||||
|     setContent(content) { | ||||
|         this.content = content; | ||||
|     /** @returns {Promise<NoteContent>} */ | ||||
|     async getNoteContent() { | ||||
|         if (!this.noteContent) { | ||||
|             this.noteContent = await repository.getEntity(`SELECT * FROM note_contents WHERE noteId = ?`, [this.noteId]); | ||||
|  | ||||
|         try { | ||||
|             this.jsonContent = JSON.parse(this.content); | ||||
|             if (!this.noteContent) { | ||||
|                 throw new Error("Note content not found for noteId=" + this.noteId); | ||||
|             } | ||||
|  | ||||
|             if (this.isStringNote()) { | ||||
|                 this.noteContent.content = this.noteContent.content.toString("UTF-8"); | ||||
|             } | ||||
|         } | ||||
|         catch(e) {} | ||||
|  | ||||
|         return this.noteContent; | ||||
|     } | ||||
|  | ||||
|     /** @returns {Promise<*>} */ | ||||
|     async getContent() { | ||||
|         const noteContent = await this.getNoteContent(); | ||||
|  | ||||
|         return noteContent.content; | ||||
|     } | ||||
|  | ||||
|     /** @returns {Promise<*>} */ | ||||
|     async getJsonContent() { | ||||
|         const content = await this.getContent(); | ||||
|  | ||||
|         return JSON.parse(content); | ||||
|     } | ||||
|  | ||||
|     /** @returns {Promise} */ | ||||
|     async setContent(content) { | ||||
|         if (!this.noteContent) { | ||||
|             // make sure it is loaded | ||||
|             await this.getNoteContent(); | ||||
|         } | ||||
|  | ||||
|         this.noteContent.content = content; | ||||
|  | ||||
|         await this.noteContent.save(); | ||||
|     } | ||||
|  | ||||
|     /** @returns {Promise} */ | ||||
|     async setJsonContent(content) { | ||||
|         await this.setContent(JSON.stringify(content)); | ||||
|     } | ||||
|  | ||||
|     /** @returns {boolean} true if this note is the root of the note tree. Root note has "root" noteId */ | ||||
| @@ -113,6 +158,11 @@ class Note extends Entity { | ||||
|         return (this.type === "code" || this.type === "file" || this.type === "render") && this.mime === "text/html"; | ||||
|     } | ||||
|  | ||||
|     /** @returns {boolean} true if the note has string content (not binary) */ | ||||
|     isStringNote() { | ||||
|         return ["text", "code", "relation-map", "search"].includes(this.type) || this.mime.startsWith('text/'); | ||||
|     } | ||||
|  | ||||
|     /** @returns {string} JS script environment - either "frontend" or "backend" */ | ||||
|     getScriptEnv() { | ||||
|         if (this.isHtml() || (this.isJavaScript() && this.mime.endsWith('env=frontend'))) { | ||||
| @@ -629,13 +679,6 @@ class Note extends Entity { | ||||
|     } | ||||
|  | ||||
|     beforeSaving() { | ||||
|         if (this.isJson() && this.jsonContent) { | ||||
|             this.content = JSON.stringify(this.jsonContent, null, '\t'); | ||||
|         } | ||||
|  | ||||
|         // we do this here because encryption needs the note ID for the IV | ||||
|         this.generateIdIfNecessary(); | ||||
|  | ||||
|         if (!this.isDeleted) { | ||||
|             this.isDeleted = false; | ||||
|         } | ||||
| @@ -654,12 +697,18 @@ class Note extends Entity { | ||||
|     // cannot be static! | ||||
|     updatePojo(pojo) { | ||||
|         if (pojo.isProtected) { | ||||
|             protectedSessionService.encryptNote(pojo); | ||||
|             if (this.isContentAvailable) { | ||||
|                 protectedSessionService.encryptNote(pojo); | ||||
|             } | ||||
|             else { | ||||
|                 // updating protected note outside of protected session means we will keep original ciphertexts | ||||
|                 pojo.title = pojo.titleCipherText; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         delete pojo.jsonContent; | ||||
|         delete pojo.isContentAvailable; | ||||
|         delete pojo.__attributeCache; | ||||
|         delete pojo.titleCipherText; | ||||
|     } | ||||
| } | ||||
|  | ||||
| @@ -673,7 +722,7 @@ module.exports = Note;</code></pre> | ||||
| </div> | ||||
|  | ||||
| <nav> | ||||
|     <h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="ApiToken.html">ApiToken</a></li><li><a href="Attribute.html">Attribute</a></li><li><a href="BackendScriptApi.html">BackendScriptApi</a></li><li><a href="Branch.html">Branch</a></li><li><a href="Entity.html">Entity</a></li><li><a href="Link.html">Link</a></li><li><a href="Note.html">Note</a></li><li><a href="NoteRevision.html">NoteRevision</a></li><li><a href="Option.html">Option</a></li><li><a href="RecentNote.html">RecentNote</a></li></ul><h3><a href="global.html">Global</a></h3> | ||||
|     <h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="ApiToken.html">ApiToken</a></li><li><a href="Attribute.html">Attribute</a></li><li><a href="BackendScriptApi.html">BackendScriptApi</a></li><li><a href="Branch.html">Branch</a></li><li><a href="Entity.html">Entity</a></li><li><a href="Link.html">Link</a></li><li><a href="Note.html">Note</a></li><li><a href="NoteContent.html">NoteContent</a></li><li><a href="NoteRevision.html">NoteRevision</a></li><li><a href="Option.html">Option</a></li><li><a href="RecentNote.html">RecentNote</a></li></ul><h3><a href="global.html">Global</a></h3> | ||||
| </nav> | ||||
|  | ||||
| <br class="clear"> | ||||
|   | ||||
							
								
								
									
										146
									
								
								docs/backend_api/entities_note_content.js.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										146
									
								
								docs/backend_api/entities_note_content.js.html
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,146 @@ | ||||
| <!DOCTYPE html> | ||||
| <html lang="en"> | ||||
| <head> | ||||
|     <meta charset="utf-8"> | ||||
|     <title>JSDoc: Source: entities/note_content.js</title> | ||||
|  | ||||
|     <script src="scripts/prettify/prettify.js"> </script> | ||||
|     <script src="scripts/prettify/lang-css.js"> </script> | ||||
|     <!--[if lt IE 9]> | ||||
|       <script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script> | ||||
|     <![endif]--> | ||||
|     <link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css"> | ||||
|     <link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css"> | ||||
| </head> | ||||
|  | ||||
| <body> | ||||
|  | ||||
| <div id="main"> | ||||
|  | ||||
|     <h1 class="page-title">Source: entities/note_content.js</h1> | ||||
|  | ||||
|      | ||||
|  | ||||
|  | ||||
|  | ||||
|      | ||||
|     <section> | ||||
|         <article> | ||||
|             <pre class="prettyprint source linenums"><code>"use strict"; | ||||
|  | ||||
| const Entity = require('./entity'); | ||||
| const protectedSessionService = require('../services/protected_session'); | ||||
| const repository = require('../services/repository'); | ||||
| const dateUtils = require('../services/date_utils'); | ||||
|  | ||||
| /** | ||||
|  * This represents a Note which is a central object in the Trilium Notes project. | ||||
|  * | ||||
|  * @property {string} noteContentId - primary key | ||||
|  * @property {string} noteId - reference to owning note | ||||
|  * @property {boolean} isProtected - true if note content is protected | ||||
|  * @property {blob} content - note content - e.g. HTML text for text notes, file payload for files | ||||
|  * @property {string} dateCreated | ||||
|  * @property {string} dateModified | ||||
|  * | ||||
|  * @extends Entity | ||||
|  */ | ||||
| class NoteContent extends Entity { | ||||
|     static get entityName() { | ||||
|         return "note_contents"; | ||||
|     } | ||||
|  | ||||
|     static get primaryKeyName() { | ||||
|         return "noteContentId"; | ||||
|     } | ||||
|  | ||||
|     static get hashedProperties() { | ||||
|         return ["noteContentId", "noteId", "isProtected", "content"]; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param row - object containing database row from "note_contents" table | ||||
|      */ | ||||
|     constructor(row) { | ||||
|         super(row); | ||||
|  | ||||
|         this.isProtected = !!this.isProtected; | ||||
|         /* true if content (meaning any kind of potentially encrypted content) is either not encrypted | ||||
|          * or encrypted, but with available protected session (so effectively decrypted) */ | ||||
|         this.isContentAvailable = true; | ||||
|  | ||||
|         // check if there's noteContentId, otherwise this is a new entity which wasn't encrypted yet | ||||
|         if (this.isProtected && this.noteContentId) { | ||||
|             this.isContentAvailable = protectedSessionService.isProtectedSessionAvailable(); | ||||
|  | ||||
|             if (this.isContentAvailable) { | ||||
|                 protectedSessionService.decryptNoteContent(this); | ||||
|             } | ||||
|             else { | ||||
|                 // saving ciphertexts in case we do want to update protected note outside of protected session | ||||
|                 // (which is allowed) | ||||
|                 this.contentCipherText = this.content; | ||||
|                 this.content = ""; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @returns {Promise<Note>} | ||||
|      */ | ||||
|     async getNote() { | ||||
|         return await repository.getNote(this.noteId); | ||||
|     } | ||||
|  | ||||
|     beforeSaving() { | ||||
|         if (!this.dateCreated) { | ||||
|             this.dateCreated = dateUtils.nowDate(); | ||||
|         } | ||||
|  | ||||
|         super.beforeSaving(); | ||||
|  | ||||
|         if (this.isChanged) { | ||||
|             this.dateModified = dateUtils.nowDate(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     // cannot be static! | ||||
|     updatePojo(pojo) { | ||||
|         if (pojo.isProtected) { | ||||
|             if (this.isContentAvailable) { | ||||
|                 protectedSessionService.encryptNoteContent(pojo); | ||||
|             } | ||||
|             else { | ||||
|                 // updating protected note outside of protected session means we will keep original ciphertext | ||||
|                 pojo.content = pojo.contentCipherText; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         delete pojo.isContentAvailable; | ||||
|         delete pojo.contentCipherText; | ||||
|     } | ||||
| } | ||||
|  | ||||
| module.exports = NoteContent;</code></pre> | ||||
|         </article> | ||||
|     </section> | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| </div> | ||||
|  | ||||
| <nav> | ||||
|     <h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="ApiToken.html">ApiToken</a></li><li><a href="Attribute.html">Attribute</a></li><li><a href="BackendScriptApi.html">BackendScriptApi</a></li><li><a href="Branch.html">Branch</a></li><li><a href="Entity.html">Entity</a></li><li><a href="Link.html">Link</a></li><li><a href="Note.html">Note</a></li><li><a href="NoteContent.html">NoteContent</a></li><li><a href="NoteRevision.html">NoteRevision</a></li><li><a href="Option.html">Option</a></li><li><a href="RecentNote.html">RecentNote</a></li></ul><h3><a href="global.html">Global</a></h3> | ||||
| </nav> | ||||
|  | ||||
| <br class="clear"> | ||||
|  | ||||
| <footer> | ||||
|     Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 3.5.5</a> | ||||
| </footer> | ||||
|  | ||||
| <script> prettyPrint(); </script> | ||||
| <script src="scripts/linenumber.js"> </script> | ||||
| </body> | ||||
| </html> | ||||
| @@ -85,7 +85,7 @@ module.exports = NoteRevision;</code></pre> | ||||
| </div> | ||||
|  | ||||
| <nav> | ||||
|     <h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="ApiToken.html">ApiToken</a></li><li><a href="Attribute.html">Attribute</a></li><li><a href="BackendScriptApi.html">BackendScriptApi</a></li><li><a href="Branch.html">Branch</a></li><li><a href="Entity.html">Entity</a></li><li><a href="Link.html">Link</a></li><li><a href="Note.html">Note</a></li><li><a href="NoteRevision.html">NoteRevision</a></li><li><a href="Option.html">Option</a></li><li><a href="RecentNote.html">RecentNote</a></li></ul><h3><a href="global.html">Global</a></h3> | ||||
|     <h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="ApiToken.html">ApiToken</a></li><li><a href="Attribute.html">Attribute</a></li><li><a href="BackendScriptApi.html">BackendScriptApi</a></li><li><a href="Branch.html">Branch</a></li><li><a href="Entity.html">Entity</a></li><li><a href="Link.html">Link</a></li><li><a href="Note.html">Note</a></li><li><a href="NoteContent.html">NoteContent</a></li><li><a href="NoteRevision.html">NoteRevision</a></li><li><a href="Option.html">Option</a></li><li><a href="RecentNote.html">RecentNote</a></li></ul><h3><a href="global.html">Global</a></h3> | ||||
| </nav> | ||||
|  | ||||
| <br class="clear"> | ||||
|   | ||||
| @@ -72,7 +72,7 @@ module.exports = Option;</code></pre> | ||||
| </div> | ||||
|  | ||||
| <nav> | ||||
|     <h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="ApiToken.html">ApiToken</a></li><li><a href="Attribute.html">Attribute</a></li><li><a href="BackendScriptApi.html">BackendScriptApi</a></li><li><a href="Branch.html">Branch</a></li><li><a href="Entity.html">Entity</a></li><li><a href="Link.html">Link</a></li><li><a href="Note.html">Note</a></li><li><a href="NoteRevision.html">NoteRevision</a></li><li><a href="Option.html">Option</a></li><li><a href="RecentNote.html">RecentNote</a></li></ul><h3><a href="global.html">Global</a></h3> | ||||
|     <h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="ApiToken.html">ApiToken</a></li><li><a href="Attribute.html">Attribute</a></li><li><a href="BackendScriptApi.html">BackendScriptApi</a></li><li><a href="Branch.html">Branch</a></li><li><a href="Entity.html">Entity</a></li><li><a href="Link.html">Link</a></li><li><a href="Note.html">Note</a></li><li><a href="NoteContent.html">NoteContent</a></li><li><a href="NoteRevision.html">NoteRevision</a></li><li><a href="Option.html">Option</a></li><li><a href="RecentNote.html">RecentNote</a></li></ul><h3><a href="global.html">Global</a></h3> | ||||
| </nav> | ||||
|  | ||||
| <br class="clear"> | ||||
|   | ||||
| @@ -69,7 +69,7 @@ module.exports = RecentNote;</code></pre> | ||||
| </div> | ||||
|  | ||||
| <nav> | ||||
|     <h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="ApiToken.html">ApiToken</a></li><li><a href="Attribute.html">Attribute</a></li><li><a href="BackendScriptApi.html">BackendScriptApi</a></li><li><a href="Branch.html">Branch</a></li><li><a href="Entity.html">Entity</a></li><li><a href="Link.html">Link</a></li><li><a href="Note.html">Note</a></li><li><a href="NoteRevision.html">NoteRevision</a></li><li><a href="Option.html">Option</a></li><li><a href="RecentNote.html">RecentNote</a></li></ul><h3><a href="global.html">Global</a></h3> | ||||
|     <h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="ApiToken.html">ApiToken</a></li><li><a href="Attribute.html">Attribute</a></li><li><a href="BackendScriptApi.html">BackendScriptApi</a></li><li><a href="Branch.html">Branch</a></li><li><a href="Entity.html">Entity</a></li><li><a href="Link.html">Link</a></li><li><a href="Note.html">Note</a></li><li><a href="NoteContent.html">NoteContent</a></li><li><a href="NoteRevision.html">NoteRevision</a></li><li><a href="Option.html">Option</a></li><li><a href="RecentNote.html">RecentNote</a></li></ul><h3><a href="global.html">Global</a></h3> | ||||
| </nav> | ||||
|  | ||||
| <br class="clear"> | ||||
|   | ||||
| @@ -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#line144">line 144</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> | ||||
|      | ||||
|  | ||||
| @@ -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#line151">line 151</a> | ||||
|         <a href="services_backend_script_api.js.html">services/backend_script_api.js</a>, <a href="services_backend_script_api.js.html#line156">line 156</a> | ||||
|     </li></ul></dd> | ||||
|      | ||||
|  | ||||
| @@ -588,7 +588,7 @@ | ||||
| </div> | ||||
|  | ||||
| <nav> | ||||
|     <h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="ApiToken.html">ApiToken</a></li><li><a href="Attribute.html">Attribute</a></li><li><a href="BackendScriptApi.html">BackendScriptApi</a></li><li><a href="Branch.html">Branch</a></li><li><a href="Entity.html">Entity</a></li><li><a href="Link.html">Link</a></li><li><a href="Note.html">Note</a></li><li><a href="NoteRevision.html">NoteRevision</a></li><li><a href="Option.html">Option</a></li><li><a href="RecentNote.html">RecentNote</a></li></ul><h3><a href="global.html">Global</a></h3> | ||||
|     <h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="ApiToken.html">ApiToken</a></li><li><a href="Attribute.html">Attribute</a></li><li><a href="BackendScriptApi.html">BackendScriptApi</a></li><li><a href="Branch.html">Branch</a></li><li><a href="Entity.html">Entity</a></li><li><a href="Link.html">Link</a></li><li><a href="Note.html">Note</a></li><li><a href="NoteContent.html">NoteContent</a></li><li><a href="NoteRevision.html">NoteRevision</a></li><li><a href="Option.html">Option</a></li><li><a href="RecentNote.html">RecentNote</a></li></ul><h3><a href="global.html">Global</a></h3> | ||||
| </nav> | ||||
|  | ||||
| <br class="clear"> | ||||
|   | ||||
| @@ -50,7 +50,7 @@ | ||||
| </div> | ||||
|  | ||||
| <nav> | ||||
|     <h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="ApiToken.html">ApiToken</a></li><li><a href="Attribute.html">Attribute</a></li><li><a href="BackendScriptApi.html">BackendScriptApi</a></li><li><a href="Branch.html">Branch</a></li><li><a href="Entity.html">Entity</a></li><li><a href="Link.html">Link</a></li><li><a href="Note.html">Note</a></li><li><a href="NoteRevision.html">NoteRevision</a></li><li><a href="Option.html">Option</a></li><li><a href="RecentNote.html">RecentNote</a></li></ul><h3><a href="global.html">Global</a></h3> | ||||
|     <h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="ApiToken.html">ApiToken</a></li><li><a href="Attribute.html">Attribute</a></li><li><a href="BackendScriptApi.html">BackendScriptApi</a></li><li><a href="Branch.html">Branch</a></li><li><a href="Entity.html">Entity</a></li><li><a href="Link.html">Link</a></li><li><a href="Note.html">Note</a></li><li><a href="NoteContent.html">NoteContent</a></li><li><a href="NoteRevision.html">NoteRevision</a></li><li><a href="Option.html">Option</a></li><li><a href="RecentNote.html">RecentNote</a></li></ul><h3><a href="global.html">Global</a></h3> | ||||
| </nav> | ||||
|  | ||||
| <br class="clear"> | ||||
|   | ||||
| @@ -39,6 +39,7 @@ const repository = require('./repository'); | ||||
| const axios = require('axios'); | ||||
| const cloningService = require('./cloning'); | ||||
| const messagingService = require('./messaging'); | ||||
| const appInfo = require('./app_info'); | ||||
|  | ||||
| /** | ||||
|  * This is the main backend API interface for scripts. It's published in the local "api" object. | ||||
| @@ -46,13 +47,17 @@ const messagingService = require('./messaging'); | ||||
|  * @constructor | ||||
|  * @hideconstructor | ||||
|  */ | ||||
| function BackendScriptApi(startNote, currentNote, originEntity) { | ||||
| function BackendScriptApi(currentNote, apiParams) { | ||||
|     /** @property {Note} note where script started executing */ | ||||
|     this.startNote = startNote; | ||||
|     this.startNote = apiParams.startNote; | ||||
|     /** @property {Note} note where script is currently executing */ | ||||
|     this.currentNote = currentNote; | ||||
|     /** @property {Entity} entity whose event triggered this executions */ | ||||
|     this.originEntity = originEntity; | ||||
|     this.originEntity = apiParams.originEntity; | ||||
|  | ||||
|     for (const key in apiParams) { | ||||
|         this[key] = apiParams[key]; | ||||
|     } | ||||
|  | ||||
|     this.axios = axios; | ||||
|  | ||||
| @@ -196,6 +201,23 @@ function BackendScriptApi(startNote, currentNote, originEntity) { | ||||
|      */ | ||||
|     this.createNote = noteService.createNote; | ||||
|  | ||||
|     /** | ||||
|      * Creates new note according to given params and force all connected clients to refresh their tree. | ||||
|      * | ||||
|      * @method | ||||
|      * | ||||
|      * @param {string} parentNoteId - create new note under this parent | ||||
|      * @param {string} title | ||||
|      * @param {string} [content=""] | ||||
|      * @param {CreateNoteExtraOptions} [extraOptions={}] | ||||
|      * @returns {Promise<{note: Note, branch: Branch}>} object contains newly created entities note and branch | ||||
|      */ | ||||
|     this.createNoteAndRefresh = async function(parentNoteId, title, content, extraOptions) { | ||||
|         await noteService.createNote(parentNoteId, title, content, extraOptions); | ||||
|  | ||||
|         messagingService.refreshTree(); | ||||
|     }; | ||||
|  | ||||
|     /** | ||||
|      * Log given message to trilium logs. | ||||
|      * | ||||
| @@ -212,14 +234,42 @@ function BackendScriptApi(startNote, currentNote, originEntity) { | ||||
|     this.getRootCalendarNote = dateNoteService.getRootCalendarNote; | ||||
|  | ||||
|     /** | ||||
|      * Returns day note for given date (YYYY-MM-DD format). If such note doesn't exist, it is created. | ||||
|      * Returns day note for given date. If such note doesn't exist, it is created. | ||||
|      * | ||||
|      * @method | ||||
|      * @param {string} date | ||||
|      * @param {string} date in YYYY-MM-DD format | ||||
|      * @returns {Promise<Note|null>} | ||||
|      */ | ||||
|     this.getDateNote = dateNoteService.getDateNote; | ||||
|  | ||||
|     /** | ||||
|      * Returns note for the first date of the week of the given date. | ||||
|      * | ||||
|      * @method | ||||
|      * @param {string} date in YYYY-MM-DD format | ||||
|      * @param {object} options - "startOfTheWeek" - either "monday" (default) or "sunday" | ||||
|      * @returns {Promise<Note|null>} | ||||
|      */ | ||||
|     this.getWeekNote = dateNoteService.getWeekNote; | ||||
|  | ||||
|     /** | ||||
|      * Returns month note for given date. If such note doesn't exist, it is created. | ||||
|      * | ||||
|      * @method | ||||
|      * @param {string} date in YYYY-MM format | ||||
|      * @returns {Promise<Note|null>} | ||||
|      */ | ||||
|     this.getMonthNote = dateNoteService.getMonthNote; | ||||
|  | ||||
|     /** | ||||
|      * Returns year note for given year. If such note doesn't exist, it is created. | ||||
|      * | ||||
|      * @method | ||||
|      * @param {string} year in YYYY format | ||||
|      * @returns {Promise<Note|null>} | ||||
|      */ | ||||
|     this.getYearNote = dateNoteService.getYearNote; | ||||
|  | ||||
|     /** | ||||
|      * @method | ||||
|      * @param {string} parentNoteId - this note's child notes will be sorted | ||||
| @@ -261,7 +311,12 @@ function BackendScriptApi(startNote, currentNote, originEntity) { | ||||
|      * | ||||
|      * @returns {Promise<void>} | ||||
|      */ | ||||
|     this.refreshTree = () => messagingService.sendMessageToAllClients({ type: 'refresh-tree' }); | ||||
|     this.refreshTree = messagingService.refreshTree; | ||||
|  | ||||
|     /** | ||||
|      * @return {{syncVersion, appVersion, buildRevision, dbVersion, dataDirectory, buildDate}|*} - object representing basic info about running Trilium version | ||||
|      */ | ||||
|     this.getAppInfo = () => appInfo | ||||
| } | ||||
|  | ||||
| module.exports = BackendScriptApi;</code></pre> | ||||
| @@ -274,7 +329,7 @@ module.exports = BackendScriptApi;</code></pre> | ||||
| </div> | ||||
|  | ||||
| <nav> | ||||
|     <h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="ApiToken.html">ApiToken</a></li><li><a href="Attribute.html">Attribute</a></li><li><a href="BackendScriptApi.html">BackendScriptApi</a></li><li><a href="Branch.html">Branch</a></li><li><a href="Entity.html">Entity</a></li><li><a href="Link.html">Link</a></li><li><a href="Note.html">Note</a></li><li><a href="NoteRevision.html">NoteRevision</a></li><li><a href="Option.html">Option</a></li><li><a href="RecentNote.html">RecentNote</a></li></ul><h3><a href="global.html">Global</a></h3> | ||||
|     <h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="ApiToken.html">ApiToken</a></li><li><a href="Attribute.html">Attribute</a></li><li><a href="BackendScriptApi.html">BackendScriptApi</a></li><li><a href="Branch.html">Branch</a></li><li><a href="Entity.html">Entity</a></li><li><a href="Link.html">Link</a></li><li><a href="Note.html">Note</a></li><li><a href="NoteContent.html">NoteContent</a></li><li><a href="NoteRevision.html">NoteRevision</a></li><li><a href="Option.html">Option</a></li><li><a href="RecentNote.html">RecentNote</a></li></ul><h3><a href="global.html">Global</a></h3> | ||||
| </nav> | ||||
|  | ||||
| <br class="clear"> | ||||
|   | ||||
| @@ -81,7 +81,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#line17">line 17</a> | ||||
|         <a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line18">line 18</a> | ||||
|     </li></ul></dd> | ||||
|      | ||||
|  | ||||
| @@ -221,7 +221,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#line23">line 23</a> | ||||
|         <a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line24">line 24</a> | ||||
|     </li></ul></dd> | ||||
|      | ||||
|  | ||||
| @@ -334,7 +334,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#line25">line 25</a> | ||||
|         <a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line26">line 26</a> | ||||
|     </li></ul></dd> | ||||
|      | ||||
|  | ||||
| @@ -444,7 +444,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#line21">line 21</a> | ||||
|         <a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line22">line 22</a> | ||||
|     </li></ul></dd> | ||||
|      | ||||
|  | ||||
| @@ -573,7 +573,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#line42">line 42</a> | ||||
|         <a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line43">line 43</a> | ||||
|     </li></ul></dd> | ||||
|      | ||||
|  | ||||
| @@ -726,7 +726,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#line34">line 34</a> | ||||
|         <a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line35">line 35</a> | ||||
|     </li></ul></dd> | ||||
|      | ||||
|  | ||||
| @@ -879,7 +879,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#line61">line 61</a> | ||||
|         <a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line62">line 62</a> | ||||
|     </li></ul></dd> | ||||
|      | ||||
|  | ||||
| @@ -1057,7 +1057,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#line198">line 198</a> | ||||
|         <a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line199">line 199</a> | ||||
|     </li></ul></dd> | ||||
|      | ||||
|  | ||||
| @@ -1188,7 +1188,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#line158">line 158</a> | ||||
|         <a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line159">line 159</a> | ||||
|     </li></ul></dd> | ||||
|      | ||||
|  | ||||
| @@ -1292,7 +1292,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#line222">line 222</a> | ||||
|         <a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line236">line 236</a> | ||||
|     </li></ul></dd> | ||||
|      | ||||
|  | ||||
| @@ -1396,7 +1396,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#line204">line 204</a> | ||||
|         <a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line205">line 205</a> | ||||
|     </li></ul></dd> | ||||
|      | ||||
|  | ||||
| @@ -1500,7 +1500,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#line216">line 216</a> | ||||
|         <a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line230">line 230</a> | ||||
|     </li></ul></dd> | ||||
|      | ||||
|  | ||||
| @@ -1609,7 +1609,7 @@ if some action needs to happen on only one specific instance. | ||||
|      | ||||
|     <dt class="tag-source">Source:</dt> | ||||
|     <dd class="tag-source"><ul class="dummy"><li> | ||||
|         <a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line151">line 151</a> | ||||
|         <a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line152">line 152</a> | ||||
|     </li></ul></dd> | ||||
|      | ||||
|  | ||||
| @@ -1808,7 +1808,7 @@ otherwise (by e.g. createNoteLink()) | ||||
|      | ||||
|     <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#line143">line 143</a> | ||||
|         <a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line144">line 144</a> | ||||
|     </li></ul></dd> | ||||
|      | ||||
|  | ||||
| @@ -1860,6 +1860,117 @@ otherwise (by e.g. createNoteLink()) | ||||
|      | ||||
|  | ||||
|      | ||||
|     <h4 class="name" id="isNoteStillLoaded"><span class="type-signature"></span>isNoteStillLoaded<span class="signature">()</span><span class="type-signature"> → {boolean}</span></h4> | ||||
|      | ||||
|  | ||||
|      | ||||
|  | ||||
|  | ||||
|  | ||||
| <div class="description"> | ||||
|     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 | ||||
| already navigated away from this page - the end result would be that script might return data for the wrong | ||||
| note. | ||||
| </div> | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| <dl class="details"> | ||||
|  | ||||
|      | ||||
|  | ||||
|      | ||||
|  | ||||
|      | ||||
|  | ||||
|      | ||||
|  | ||||
|      | ||||
|  | ||||
|      | ||||
|  | ||||
|      | ||||
|  | ||||
|      | ||||
|  | ||||
|      | ||||
|  | ||||
|      | ||||
|  | ||||
|      | ||||
|  | ||||
|      | ||||
|  | ||||
|      | ||||
|     <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#line216">line 216</a> | ||||
|     </li></ul></dd> | ||||
|      | ||||
|  | ||||
|      | ||||
|  | ||||
|      | ||||
|  | ||||
|      | ||||
| </dl> | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| <h5>Returns:</h5> | ||||
|  | ||||
|          | ||||
| <div class="param-desc"> | ||||
|     returns true if the original note is still loaded, false if user switched to another | ||||
| </div> | ||||
|  | ||||
|  | ||||
|  | ||||
| <dl> | ||||
|     <dt> | ||||
|         Type | ||||
|     </dt> | ||||
|     <dd> | ||||
|          | ||||
| <span class="param-type">boolean</span> | ||||
|  | ||||
|  | ||||
|     </dd> | ||||
| </dl> | ||||
|  | ||||
|      | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|          | ||||
|              | ||||
|  | ||||
|      | ||||
|  | ||||
|      | ||||
|     <h4 class="name" id="onNoteChange"><span class="type-signature"></span>onNoteChange<span class="signature">(func)</span><span class="type-signature"></span></h4> | ||||
|      | ||||
|  | ||||
| @@ -1957,7 +2068,7 @@ otherwise (by e.g. createNoteLink()) | ||||
|      | ||||
|     <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#line210">line 210</a> | ||||
|         <a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line224">line 224</a> | ||||
|     </li></ul></dd> | ||||
|      | ||||
|  | ||||
| @@ -2088,7 +2199,7 @@ otherwise (by e.g. createNoteLink()) | ||||
|      | ||||
|     <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#line165">line 165</a> | ||||
|         <a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line166">line 166</a> | ||||
|     </li></ul></dd> | ||||
|      | ||||
|  | ||||
| @@ -2140,6 +2251,88 @@ otherwise (by e.g. createNoteLink()) | ||||
|  | ||||
|          | ||||
|              | ||||
|  | ||||
|      | ||||
|  | ||||
|      | ||||
|     <h4 class="name" id="protectCurrentNote"><span class="type-signature"></span>protectCurrentNote<span class="signature">()</span><span class="type-signature"></span></h4> | ||||
|      | ||||
|  | ||||
|      | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| <dl class="details"> | ||||
|  | ||||
|      | ||||
|  | ||||
|      | ||||
|  | ||||
|      | ||||
|  | ||||
|      | ||||
|  | ||||
|      | ||||
|  | ||||
|      | ||||
|  | ||||
|      | ||||
|  | ||||
|      | ||||
|  | ||||
|      | ||||
|  | ||||
|      | ||||
|  | ||||
|      | ||||
|  | ||||
|      | ||||
|  | ||||
|      | ||||
|     <dt class="tag-source">Source:</dt> | ||||
|     <dd class="tag-source"><ul class="dummy"><li> | ||||
|         <a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line253">line 253</a> | ||||
|     </li></ul></dd> | ||||
|      | ||||
|  | ||||
|      | ||||
|  | ||||
|      | ||||
|  | ||||
|      | ||||
| </dl> | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|          | ||||
|              | ||||
|  | ||||
|      | ||||
|  | ||||
| @@ -2196,7 +2389,7 @@ otherwise (by e.g. createNoteLink()) | ||||
|      | ||||
|     <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#line189">line 189</a> | ||||
|         <a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line190">line 190</a> | ||||
|     </li></ul></dd> | ||||
|      | ||||
|  | ||||
| @@ -2373,7 +2566,7 @@ Internally this serializes the anonymous function into string and sends it to ba | ||||
|      | ||||
|     <dt class="tag-source">Source:</dt> | ||||
|     <dd class="tag-source"><ul class="dummy"><li> | ||||
|         <a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line111">line 111</a> | ||||
|         <a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line112">line 112</a> | ||||
|     </li></ul></dd> | ||||
|      | ||||
|  | ||||
| @@ -2526,7 +2719,7 @@ Internally this serializes the anonymous function into string and sends it to ba | ||||
|      | ||||
|     <dt class="tag-source">Source:</dt> | ||||
|     <dd class="tag-source"><ul class="dummy"><li> | ||||
|         <a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line228">line 228</a> | ||||
|         <a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line242">line 242</a> | ||||
|     </li></ul></dd> | ||||
|      | ||||
|  | ||||
| @@ -2657,7 +2850,7 @@ Internally this serializes the anonymous function into string and sends it to ba | ||||
|      | ||||
|     <dt class="tag-source">Source:</dt> | ||||
|     <dd class="tag-source"><ul class="dummy"><li> | ||||
|         <a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line234">line 234</a> | ||||
|         <a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line248">line 248</a> | ||||
|     </li></ul></dd> | ||||
|      | ||||
|  | ||||
| @@ -2792,7 +2985,7 @@ Internally this serializes the anonymous function into string and sends it to ba | ||||
|      | ||||
|     <dt class="tag-source">Source:</dt> | ||||
|     <dd class="tag-source"><ul class="dummy"><li> | ||||
|         <a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line181">line 181</a> | ||||
|         <a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line182">line 182</a> | ||||
|     </li></ul></dd> | ||||
|      | ||||
|  | ||||
| @@ -2927,7 +3120,7 @@ Internally this serializes the anonymous function into string and sends it to ba | ||||
|      | ||||
|     <dt class="tag-source">Source:</dt> | ||||
|     <dd class="tag-source"><ul class="dummy"><li> | ||||
|         <a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line173">line 173</a> | ||||
|         <a href="services_frontend_script_api.js.html">services/frontend_script_api.js</a>, <a href="services_frontend_script_api.js.html#line174">line 174</a> | ||||
|     </li></ul></dd> | ||||
|      | ||||
|  | ||||
|   | ||||
| @@ -141,7 +141,123 @@ | ||||
|  | ||||
|          | ||||
|              | ||||
| <h4 class="name" id="content"><span class="type-signature"></span>content<span class="type-signature"></span></h4> | ||||
| <h4 class="name" id="dateCreated"><span class="type-signature"></span>dateCreated<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#line14">line 14</a> | ||||
|     </li></ul></dd> | ||||
|      | ||||
|  | ||||
|      | ||||
|  | ||||
|      | ||||
|  | ||||
|      | ||||
| </dl> | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|          | ||||
|              | ||||
| <h4 class="name" id="dateModified"><span class="type-signature"></span>dateModified<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#line17">line 17</a> | ||||
|     </li></ul></dd> | ||||
|      | ||||
|  | ||||
|      | ||||
|  | ||||
|      | ||||
|  | ||||
|      | ||||
| </dl> | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|          | ||||
|              | ||||
| <h4 class="name" id="noteContent"><span class="type-signature"></span>noteContent<span class="type-signature"></span></h4> | ||||
|  | ||||
|  | ||||
|  | ||||
| @@ -199,64 +315,6 @@ | ||||
|  | ||||
|          | ||||
|      | ||||
| <h4 class="name" id="jsonContent"><span class="type-signature"></span>jsonContent<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#line16">line 16</a> | ||||
|     </li></ul></dd> | ||||
|      | ||||
|  | ||||
|      | ||||
|  | ||||
|      | ||||
|  | ||||
|      | ||||
| </dl> | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|          | ||||
|      | ||||
|  | ||||
|      | ||||
|  | ||||
|   | ||||
| @@ -59,7 +59,9 @@ | ||||
|     get toString() { | ||||
|         return `Attribute(attributeId=${this.attributeId}, type=${this.type}, name=${this.name})`; | ||||
|     } | ||||
| }</code></pre> | ||||
| } | ||||
|  | ||||
| export default Attribute;</code></pre> | ||||
|         </article> | ||||
|     </section> | ||||
|  | ||||
|   | ||||
| @@ -36,15 +36,13 @@ class NoteFull extends NoteShort { | ||||
|         super(treeCache, row); | ||||
|  | ||||
|         /** @param {string} */ | ||||
|         this.content = row.content; | ||||
|         this.noteContent = row.noteContent; | ||||
|  | ||||
|         if (this.content !== "" && this.isJson()) { | ||||
|             try { | ||||
|                 /** @param {object} */ | ||||
|                 this.jsonContent = JSON.parse(this.content); | ||||
|             } | ||||
|             catch(e) {} | ||||
|         } | ||||
|         /** @param {string} */ | ||||
|         this.dateCreated = row.dateCreated; | ||||
|  | ||||
|         /** @param {string} */ | ||||
|         this.dateModified = row.dateModified; | ||||
|     } | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -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#line48">line 48</a> | ||||
|         <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> | ||||
|     </li></ul></dd> | ||||
|      | ||||
|  | ||||
|   | ||||
| @@ -35,6 +35,7 @@ import treeCache from './tree_cache.js'; | ||||
| import noteDetailService from './note_detail.js'; | ||||
| import noteTypeService from './note_type.js'; | ||||
| import noteTooltipService from './note_tooltip.js'; | ||||
| import protectedSessionService from'./protected_session.js'; | ||||
|  | ||||
| /** | ||||
|  * This is the main frontend API interface for scripts. It's published in the local "api" object. | ||||
| @@ -70,7 +71,7 @@ function FrontendScriptApi(startNote, currentNote, originEntity = null) { | ||||
|     this.activateNewNote = async notePath => { | ||||
|         await treeService.reload(); | ||||
|  | ||||
|         await treeService.activateNote(notePath, true); | ||||
|         await treeService.activateNote(notePath, noteDetailService.focusAndSelectTitle); | ||||
|     }; | ||||
|  | ||||
|     /** | ||||
| @@ -231,6 +232,19 @@ function FrontendScriptApi(startNote, currentNote, originEntity = null) { | ||||
|      */ | ||||
|     this.getCurrentNoteContent = noteDetailService.getCurrentNoteContent; | ||||
|  | ||||
|     /** | ||||
|      * 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 | ||||
|      * already navigated away from this page - the end result would be that script might return data for the wrong | ||||
|      * note. | ||||
|      * | ||||
|      * @method | ||||
|      * @return {boolean} returns true if the original note is still loaded, false if user switched to another | ||||
|      */ | ||||
|     this.isNoteStillLoaded = () => { | ||||
|         return this.originEntity.noteId === noteDetailService.getCurrentNoteId(); | ||||
|     }; | ||||
|  | ||||
|     /** | ||||
|      * @method | ||||
|      * @param {function} func - callback called on note change as user is typing (not necessarily tied to save event) | ||||
| @@ -259,7 +273,12 @@ function FrontendScriptApi(startNote, currentNote, originEntity = null) { | ||||
|      * @method | ||||
|      * @param {object} $el - jquery object on which to setup the tooltip | ||||
|      */ | ||||
|     this.setupElementTooltip = noteTooltipService.setupElementTooltip | ||||
|     this.setupElementTooltip = noteTooltipService.setupElementTooltip; | ||||
|  | ||||
|     /** | ||||
|      * @method | ||||
|      */ | ||||
|     this.protectCurrentNote = protectedSessionService.protectNoteAndSendToServer; | ||||
| } | ||||
|  | ||||
| export default FrontendScriptApi;</code></pre> | ||||
|   | ||||
							
								
								
									
										18
									
								
								electron.js
									
									
									
									
									
								
							
							
						
						
									
										18
									
								
								electron.js
									
									
									
									
									
								
							| @@ -7,6 +7,7 @@ const cls = require('./src/services/cls'); | ||||
| const url = require("url"); | ||||
| const port = require('./src/services/port'); | ||||
| const appIconService = require('./src/services/app_icon'); | ||||
| const windowStateKeeper = require('electron-window-state'); | ||||
|  | ||||
| const app = electron.app; | ||||
| const globalShortcut = electron.globalShortcut; | ||||
| @@ -28,14 +29,23 @@ function onClosed() { | ||||
| } | ||||
|  | ||||
| async function createMainWindow() { | ||||
|     let mainWindowState = windowStateKeeper({ | ||||
|         // default window width & height so it's usable on 1600 * 900 display (including some extra panels etc.) | ||||
|         defaultWidth: 1200, | ||||
|         defaultHeight: 800 | ||||
|     }); | ||||
|  | ||||
|     const win = new electron.BrowserWindow({ | ||||
|         // initial window width & height so it's usable on 1600 * 900 display (including some extra panels etc.) | ||||
|         width: 1200, | ||||
|         height: 800, | ||||
|         x: mainWindowState.x, | ||||
|         y: mainWindowState.y, | ||||
|         width: mainWindowState.width, | ||||
|         height: mainWindowState.height, | ||||
|         title: 'Trilium Notes', | ||||
|         icon: path.join(__dirname, 'src/public/images/app-icons/png/256x256.png') | ||||
|     }); | ||||
|  | ||||
|     mainWindowState.manage(win); | ||||
|  | ||||
|     win.setMenu(null); | ||||
|     win.loadURL('http://localhost:' + await port); | ||||
|     win.on('closed', onClosed); | ||||
| @@ -81,7 +91,7 @@ app.on('ready', async () => { | ||||
|         const dateNoteService = require('./src/services/date_notes'); | ||||
|         const dateUtils = require('./src/services/date_utils'); | ||||
|  | ||||
|         const parentNote = await dateNoteService.getDateNote(dateUtils.nowDate()); | ||||
|         const parentNote = await dateNoteService.getDateNote(dateUtils.nowLocalDate()); | ||||
|  | ||||
|         // window may be hidden / not in focus | ||||
|         mainWindow.focus(); | ||||
|   | ||||
							
								
								
									
										5
									
								
								issue_template.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								issue_template.md
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | ||||
| For bug reports, please mention **version of the application** and include **log files** from following location: | ||||
|  | ||||
| * `/home/[user]/.local/share/trilium-data/log` for Linux | ||||
| * `C:\Users\[user]\AppData\Roaming\trilium-data\log` for Windows Vista and up | ||||
| * `/Users/[user]/Library/Application Support/trilium-data/log` for Mac OS | ||||
							
								
								
									
										3035
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										3035
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										54
									
								
								package.json
									
									
									
									
									
								
							
							
						
						
									
										54
									
								
								package.json
									
									
									
									
									
								
							| @@ -2,7 +2,7 @@ | ||||
|   "name": "trilium", | ||||
|   "productName": "Trilium Notes", | ||||
|   "description": "Trilium Notes", | ||||
|   "version": "0.27.1-beta", | ||||
|   "version": "0.30.4", | ||||
|   "license": "AGPL-3.0-only", | ||||
|   "main": "electron.js", | ||||
|   "bin": { | ||||
| @@ -17,7 +17,8 @@ | ||||
|     "start-electron": "electron . --disable-gpu", | ||||
|     "build-backend-docs": "jsdoc -c jsdoc-conf.json -d ./docs/backend_api src/entities/*.js src/services/backend_script_api.js", | ||||
|     "build-frontend-docs": "jsdoc -c jsdoc-conf.json -d ./docs/frontend_api src/public/javascripts/entities/*.js src/public/javascripts/services/frontend_script_api.js", | ||||
|     "build-docs": "npm run build-backend-docs && npm run build-frontend-docs" | ||||
|     "build-docs": "npm run build-backend-docs && npm run build-frontend-docs", | ||||
|     "postinstall": "electron-builder install-app-deps" | ||||
|   }, | ||||
|   "dependencies": { | ||||
|     "async-mutex": "0.1.3", | ||||
| @@ -25,54 +26,59 @@ | ||||
|     "body-parser": "1.18.3", | ||||
|     "cls-hooked": "4.2.2", | ||||
|     "commonmark": "0.28.1", | ||||
|     "cookie-parser": "1.4.3", | ||||
|     "cookie-parser": "1.4.4", | ||||
|     "debug": "4.1.1", | ||||
|     "ejs": "2.6.1", | ||||
|     "electron-debug": "2.0.0", | ||||
|     "electron-dl": "1.12.0", | ||||
|     "electron-debug": "2.1.0", | ||||
|     "electron-dl": "1.13.0", | ||||
|     "electron-in-page-search": "1.3.2", | ||||
|     "electron-window-state": "^5.0.3", | ||||
|     "express": "4.16.4", | ||||
|     "express-session": "1.15.6", | ||||
|     "file-type": "10.8.0", | ||||
|     "fs-extra": "7.0.1", | ||||
|     "get-port": "4.1.0", | ||||
|     "helmet": "3.15.0", | ||||
|     "get-port": "4.2.0", | ||||
|     "helmet": "3.15.1", | ||||
|     "html": "1.0.0", | ||||
|     "image-type": "3.0.0", | ||||
|     "imagemin": "6.0.0", | ||||
|     "imagemin": "6.1.0", | ||||
|     "imagemin-giflossy": "5.1.10", | ||||
|     "imagemin-mozjpeg": "8.0.0", | ||||
|     "imagemin-pngquant": "6.0.0", | ||||
|     "imagemin-pngquant": "7.0.0", | ||||
|     "ini": "1.3.5", | ||||
|     "jimp": "0.6.0", | ||||
|     "mime-types": "^2.1.21", | ||||
|     "moment": "2.23.0", | ||||
|     "mime-types": "^2.1.22", | ||||
|     "moment": "2.24.0", | ||||
|     "multer": "1.4.1", | ||||
|     "node-abi": "2.5.1", | ||||
|     "node-abi": "2.7.1", | ||||
|     "open": "0.0.5", | ||||
|     "rand-token": "0.4.0", | ||||
|     "rcedit": "1.1.1", | ||||
|     "rimraf": "2.6.2", | ||||
|     "rimraf": "2.6.3", | ||||
|     "sanitize-filename": "1.6.1", | ||||
|     "sax": "^1.2.4", | ||||
|     "semver": "^5.6.0", | ||||
|     "serve-favicon": "2.5.0", | ||||
|     "session-file-store": "1.2.0", | ||||
|     "simple-node-logger": "18.12.21", | ||||
|     "sqlite": "3.0.0", | ||||
|     "tar-stream": "1.6.2", | ||||
|     "turndown": "5.0.1", | ||||
|     "simple-node-logger": "18.12.22", | ||||
|     "sqlite": "3.0.2", | ||||
|     "tar-stream": "2.0.1", | ||||
|     "turndown": "5.0.3", | ||||
|     "unescape": "1.0.1", | ||||
|     "ws": "6.1.2", | ||||
|     "ws": "6.1.4", | ||||
|     "xml2js": "0.4.19" | ||||
|   }, | ||||
|   "devDependencies": { | ||||
|     "devtron": "1.4.0", | ||||
|     "electron": "4.0.0", | ||||
|     "electron-compile": "6.4.3", | ||||
|     "electron-packager": "13.0.1", | ||||
|     "electron-rebuild": "1.8.2", | ||||
|     "electron": "4.0.3", | ||||
|     "electron-builder": "20.38.5", | ||||
|     "electron-compile": "6.4.4", | ||||
|     "electron-installer-debian": "^1.1.1", | ||||
|     "electron-packager": "13.1.0", | ||||
|     "electron-rebuild": "1.8.4", | ||||
|     "lorem-ipsum": "1.0.6", | ||||
|     "tape": "4.9.1", | ||||
|     "xo": "0.23.0" | ||||
|     "tape": "4.10.1", | ||||
|     "xo": "0.24.0" | ||||
|   }, | ||||
|   "xo": { | ||||
|     "envs": [ | ||||
|   | ||||
| @@ -39,7 +39,7 @@ app.use((req, res, next) => { | ||||
|     }); | ||||
| }); | ||||
|  | ||||
| app.use(bodyParser.json({limit: '50mb'})); | ||||
| app.use(bodyParser.json({limit: '500mb'})); | ||||
| app.use(bodyParser.urlencoded({extended: false})); | ||||
| app.use(cookieParser()); | ||||
| app.use(express.static(path.join(__dirname, 'public'))); | ||||
| @@ -63,6 +63,8 @@ app.use(favicon(__dirname + '/public/images/app-icons/win/icon.ico')); | ||||
|  | ||||
| require('./routes/routes').register(app); | ||||
|  | ||||
| require('./routes/custom').register(app); | ||||
|  | ||||
| // catch 404 and forward to error handler | ||||
| app.use((req, res, next) => { | ||||
|     const err = new Error('Router not found for request ' + req.url); | ||||
|   | ||||
| @@ -8,7 +8,10 @@ class Entity { | ||||
|      */ | ||||
|     constructor(row = {}) { | ||||
|         for (const key in row) { | ||||
|             this[key] = row[key]; | ||||
|             // ! is used when joint-fetching notes and note_contents objects for performance | ||||
|             if (!key.startsWith('!')) { | ||||
|                 this[key] = row[key]; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         if ('isDeleted' in this) { | ||||
|   | ||||
| @@ -1,4 +1,5 @@ | ||||
| const Note = require('../entities/note'); | ||||
| const NoteContent = require('../entities/note_content'); | ||||
| const NoteRevision = require('../entities/note_revision'); | ||||
| const Link = require('../entities/link'); | ||||
| const Branch = require('../entities/branch'); | ||||
| @@ -12,10 +13,12 @@ const ENTITY_NAME_TO_ENTITY = { | ||||
|     "attributes": Attribute, | ||||
|     "branches": Branch, | ||||
|     "notes": Note, | ||||
|     "note_contents": NoteContent, | ||||
|     "note_revisions": NoteRevision, | ||||
|     "recent_notes": RecentNote, | ||||
|     "options": Option, | ||||
|     "api_tokens": ApiToken | ||||
|     "api_tokens": ApiToken, | ||||
|     "links": Link | ||||
| }; | ||||
|  | ||||
| function getEntityFromEntityName(entityName) { | ||||
| @@ -47,6 +50,9 @@ function createEntityFromRow(row) { | ||||
|     else if (row.branchId) { | ||||
|         entity = new Branch(row); | ||||
|     } | ||||
|     else if (row.noteContentId) { | ||||
|         entity = new NoteContent(row); | ||||
|     } | ||||
|     else if (row.noteId) { | ||||
|         entity = new Note(row); | ||||
|     } | ||||
|   | ||||
| @@ -2,6 +2,7 @@ | ||||
|  | ||||
| const Entity = require('./entity'); | ||||
| const Attribute = require('./attribute'); | ||||
| const NoteContent = require('./note_content'); | ||||
| const protectedSessionService = require('../services/protected_session'); | ||||
| const repository = require('../services/repository'); | ||||
| const sql = require('../services/sql'); | ||||
| @@ -12,6 +13,8 @@ const LABEL_DEFINITION = 'label-definition'; | ||||
| const RELATION = 'relation'; | ||||
| const RELATION_DEFINITION = 'relation-definition'; | ||||
|  | ||||
| const STRING_MIME_TYPES = ["application/x-javascript"]; | ||||
|  | ||||
| /** | ||||
|  * This represents a Note which is a central object in the Trilium Notes project. | ||||
|  * | ||||
| @@ -19,7 +22,6 @@ const RELATION_DEFINITION = 'relation-definition'; | ||||
|  * @property {string} type - one of "text", "code", "file" or "render" | ||||
|  * @property {string} mime - MIME type, e.g. "text/html" | ||||
|  * @property {string} title - note title | ||||
|  * @property {string} content - note content - e.g. HTML text for text notes, file payload for files | ||||
|  * @property {boolean} isProtected - true if note is protected | ||||
|  * @property {boolean} isDeleted - true if note is deleted | ||||
|  * @property {string} dateCreated | ||||
| @@ -30,7 +32,7 @@ const RELATION_DEFINITION = 'relation-definition'; | ||||
| class Note extends Entity { | ||||
|     static get entityName() { return "notes"; } | ||||
|     static get primaryKeyName() { return "noteId"; } | ||||
|     static get hashedProperties() { return ["noteId", "title", "content", "type", "isProtected", "isDeleted"]; } | ||||
|     static get hashedProperties() { return ["noteId", "title", "type", "isProtected", "isDeleted"]; } | ||||
|  | ||||
|     /** | ||||
|      * @param row - object containing database row from "notes" table | ||||
| @@ -47,19 +49,64 @@ class Note extends Entity { | ||||
|         if (this.isProtected && this.noteId) { | ||||
|             this.isContentAvailable = protectedSessionService.isProtectedSessionAvailable(); | ||||
|  | ||||
|             protectedSessionService.decryptNote(this); | ||||
|             if (this.isContentAvailable) { | ||||
|                 protectedSessionService.decryptNote(this); | ||||
|             } | ||||
|             else { | ||||
|                 // saving ciphertexts in case we do want to update protected note outside of protected session | ||||
|                 // (which is allowed) | ||||
|                 this.titleCipherText = this.title; | ||||
|                 this.title = "[protected]"; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         this.setContent(this.content); | ||||
|     } | ||||
|  | ||||
|     setContent(content) { | ||||
|         this.content = content; | ||||
|     /** @returns {Promise<NoteContent>} */ | ||||
|     async getNoteContent() { | ||||
|         if (!this.noteContent) { | ||||
|             this.noteContent = await repository.getEntity(`SELECT * FROM note_contents WHERE noteId = ?`, [this.noteId]); | ||||
|  | ||||
|         try { | ||||
|             this.jsonContent = JSON.parse(this.content); | ||||
|             if (!this.noteContent) { | ||||
|                 throw new Error("Note content not found for noteId=" + this.noteId); | ||||
|             } | ||||
|  | ||||
|             if (this.isStringNote()) { | ||||
|                 this.noteContent.content = this.noteContent.content.toString("UTF-8"); | ||||
|             } | ||||
|         } | ||||
|         catch(e) {} | ||||
|  | ||||
|         return this.noteContent; | ||||
|     } | ||||
|  | ||||
|     /** @returns {Promise<*>} */ | ||||
|     async getContent() { | ||||
|         const noteContent = await this.getNoteContent(); | ||||
|  | ||||
|         return noteContent.content; | ||||
|     } | ||||
|  | ||||
|     /** @returns {Promise<*>} */ | ||||
|     async getJsonContent() { | ||||
|         const content = await this.getContent(); | ||||
|  | ||||
|         return JSON.parse(content); | ||||
|     } | ||||
|  | ||||
|     /** @returns {Promise} */ | ||||
|     async setContent(content) { | ||||
|         if (!this.noteContent) { | ||||
|             // make sure it is loaded | ||||
|             await this.getNoteContent(); | ||||
|         } | ||||
|  | ||||
|         this.noteContent.content = content; | ||||
|  | ||||
|         await this.noteContent.save(); | ||||
|     } | ||||
|  | ||||
|     /** @returns {Promise} */ | ||||
|     async setJsonContent(content) { | ||||
|         await this.setContent(JSON.stringify(content, null, '\t')); | ||||
|     } | ||||
|  | ||||
|     /** @returns {boolean} true if this note is the root of the note tree. Root note has "root" noteId */ | ||||
| @@ -85,6 +132,13 @@ class Note extends Entity { | ||||
|         return (this.type === "code" || this.type === "file" || this.type === "render") && this.mime === "text/html"; | ||||
|     } | ||||
|  | ||||
|     /** @returns {boolean} true if the note has string content (not binary) */ | ||||
|     isStringNote() { | ||||
|         return ["text", "code", "relation-map", "search"].includes(this.type) | ||||
|             || this.mime.startsWith('text/') | ||||
|             || STRING_MIME_TYPES.includes(this.mime); | ||||
|     } | ||||
|  | ||||
|     /** @returns {string} JS script environment - either "frontend" or "backend" */ | ||||
|     getScriptEnv() { | ||||
|         if (this.isHtml() || (this.isJavaScript() && this.mime.endsWith('env=frontend'))) { | ||||
| @@ -601,13 +655,6 @@ class Note extends Entity { | ||||
|     } | ||||
|  | ||||
|     beforeSaving() { | ||||
|         if (this.isJson() && this.jsonContent) { | ||||
|             this.content = JSON.stringify(this.jsonContent, null, '\t'); | ||||
|         } | ||||
|  | ||||
|         // we do this here because encryption needs the note ID for the IV | ||||
|         this.generateIdIfNecessary(); | ||||
|  | ||||
|         if (!this.isDeleted) { | ||||
|             this.isDeleted = false; | ||||
|         } | ||||
| @@ -626,12 +673,19 @@ class Note extends Entity { | ||||
|     // cannot be static! | ||||
|     updatePojo(pojo) { | ||||
|         if (pojo.isProtected) { | ||||
|             protectedSessionService.encryptNote(pojo); | ||||
|             if (this.isContentAvailable) { | ||||
|                 protectedSessionService.encryptNote(pojo); | ||||
|             } | ||||
|             else { | ||||
|                 // updating protected note outside of protected session means we will keep original ciphertexts | ||||
|                 pojo.title = pojo.titleCipherText; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         delete pojo.jsonContent; | ||||
|         delete pojo.isContentAvailable; | ||||
|         delete pojo.__attributeCache; | ||||
|         delete pojo.titleCipherText; | ||||
|         delete pojo.noteContent; | ||||
|     } | ||||
| } | ||||
|  | ||||
|   | ||||
							
								
								
									
										96
									
								
								src/entities/note_content.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										96
									
								
								src/entities/note_content.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,96 @@ | ||||
| "use strict"; | ||||
|  | ||||
| const Entity = require('./entity'); | ||||
| const protectedSessionService = require('../services/protected_session'); | ||||
| const repository = require('../services/repository'); | ||||
| const dateUtils = require('../services/date_utils'); | ||||
|  | ||||
| /** | ||||
|  * This represents a Note which is a central object in the Trilium Notes project. | ||||
|  * | ||||
|  * @property {string} noteContentId - primary key | ||||
|  * @property {string} noteId - reference to owning note | ||||
|  * @property {boolean} isProtected - true if note content is protected | ||||
|  * @property {blob} content - note content - e.g. HTML text for text notes, file payload for files | ||||
|  * @property {string} dateCreated | ||||
|  * @property {string} dateModified | ||||
|  * | ||||
|  * @extends Entity | ||||
|  */ | ||||
| class NoteContent extends Entity { | ||||
|     static get entityName() { | ||||
|         return "note_contents"; | ||||
|     } | ||||
|  | ||||
|     static get primaryKeyName() { | ||||
|         return "noteContentId"; | ||||
|     } | ||||
|  | ||||
|     static get hashedProperties() { | ||||
|         return ["noteContentId", "noteId", "isProtected", "content"]; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param row - object containing database row from "note_contents" table | ||||
|      */ | ||||
|     constructor(row) { | ||||
|         super(row); | ||||
|  | ||||
|         this.isProtected = !!this.isProtected; | ||||
|         /* true if content (meaning any kind of potentially encrypted content) is either not encrypted | ||||
|          * or encrypted, but with available protected session (so effectively decrypted) */ | ||||
|         this.isContentAvailable = true; | ||||
|  | ||||
|         // check if there's noteContentId, otherwise this is a new entity which wasn't encrypted yet | ||||
|         if (this.isProtected && this.noteContentId) { | ||||
|             this.isContentAvailable = protectedSessionService.isProtectedSessionAvailable(); | ||||
|  | ||||
|             if (this.isContentAvailable) { | ||||
|                 protectedSessionService.decryptNoteContent(this); | ||||
|             } | ||||
|             else { | ||||
|                 // saving ciphertexts in case we do want to update protected note outside of protected session | ||||
|                 // (which is allowed) | ||||
|                 this.contentCipherText = this.content; | ||||
|                 this.content = ""; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @returns {Promise<Note>} | ||||
|      */ | ||||
|     async getNote() { | ||||
|         return await repository.getNote(this.noteId); | ||||
|     } | ||||
|  | ||||
|     beforeSaving() { | ||||
|         if (!this.dateCreated) { | ||||
|             this.dateCreated = dateUtils.nowDate(); | ||||
|         } | ||||
|  | ||||
|         super.beforeSaving(); | ||||
|  | ||||
|         if (this.isChanged) { | ||||
|             this.dateModified = dateUtils.nowDate(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     // cannot be static! | ||||
|     updatePojo(pojo) { | ||||
|         if (pojo.isProtected) { | ||||
|             if (this.isContentAvailable) { | ||||
|                 protectedSessionService.encryptNoteContent(pojo); | ||||
|             } | ||||
|             else { | ||||
|                 // updating protected note outside of protected session means we will keep original ciphertext | ||||
|                 pojo.content = pojo.contentCipherText; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         delete pojo.isContentAvailable; | ||||
|         delete pojo.contentCipherText; | ||||
|     } | ||||
| } | ||||
|  | ||||
| module.exports = NoteContent; | ||||
							
								
								
									
										
											BIN
										
									
								
								src/public/images/app-icons/ios/apple-touch-icon.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								src/public/images/app-icons/ios/apple-touch-icon.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 3.4 KiB | 
| @@ -1 +1 @@ | ||||
| <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#fafafa" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-shield"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"></path></svg> | ||||
| <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#000000" stroke-opacity="0.03" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-shield"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"></path></svg> | ||||
| Before Width: | Height: | Size: 274 B After Width: | Height: | Size: 296 B | 
| @@ -8,11 +8,11 @@ import optionsDialog from './dialogs/options.js'; | ||||
| import sqlConsoleDialog from './dialogs/sql_console.js'; | ||||
| import markdownImportDialog from './dialogs/markdown_import.js'; | ||||
| import exportDialog from './dialogs/export.js'; | ||||
| import importDialog from './dialogs/import.js'; | ||||
|  | ||||
| import cloning from './services/cloning.js'; | ||||
| import contextMenu from './services/tree_context_menu.js'; | ||||
| import dragAndDropSetup from './services/drag_and_drop.js'; | ||||
| import exportService from './services/export.js'; | ||||
| import link from './services/link.js'; | ||||
| import messagingService from './services/messaging.js'; | ||||
| import noteDetailService from './services/note_detail.js'; | ||||
| @@ -36,6 +36,8 @@ import hoistedNoteService from './services/hoisted_note.js'; | ||||
| import noteTypeService from './services/note_type.js'; | ||||
| 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'; | ||||
|  | ||||
| // required for CKEditor image upload plugin | ||||
| window.glob.getCurrentNode = treeService.getCurrentNode; | ||||
| @@ -78,6 +80,10 @@ window.onerror = function (msg, url, lineNo, columnNo, error) { | ||||
|     return false; | ||||
| }; | ||||
|  | ||||
| for (const appCssNoteId of window.appCssNoteIds) { | ||||
|     cssLoader.requireCss(`/api/notes/download/${appCssNoteId}`); | ||||
| } | ||||
|  | ||||
| const wikiBaseUrl = "https://github.com/zadam/trilium/wiki/"; | ||||
|  | ||||
| $(document).on("click", "button[data-help-page]", e => { | ||||
| @@ -88,8 +94,19 @@ $(document).on("click", "button[data-help-page]", e => { | ||||
|  | ||||
| $("#logout-button").toggle(!utils.isElectron()); | ||||
|  | ||||
| $("#logout-button").click(() => { | ||||
|     const $logoutForm = $('<form action="logout" method="POST">'); | ||||
|  | ||||
|     $("body").append($logoutForm); | ||||
|     $logoutForm.submit(); | ||||
| }); | ||||
|  | ||||
| $("#tree").on("click", ".unhoist-button", hoistedNoteService.unhoist); | ||||
|  | ||||
| $("body").on("click", "a.external", function () { | ||||
|     window.open($(this).attr("href"), '_blank'); | ||||
| }); | ||||
|  | ||||
| if (utils.isElectron()) { | ||||
|     require('electron').ipcRenderer.on('create-day-sub-note', async function(event, parentNoteId) { | ||||
|         // this might occur when day note had to be created | ||||
| @@ -110,28 +127,6 @@ if (utils.isElectron()) { | ||||
|     }); | ||||
| } | ||||
|  | ||||
| function exec(cmd) { | ||||
|     document.execCommand(cmd); | ||||
|  | ||||
|     return false; | ||||
| } | ||||
|  | ||||
| if (utils.isElectron() && utils.isMac()) { | ||||
|     utils.bindShortcut('ctrl+c', () => exec("copy")); | ||||
|     utils.bindShortcut('ctrl+v', () => exec('paste')); | ||||
|     utils.bindShortcut('ctrl+x', () => exec('cut')); | ||||
|     utils.bindShortcut('ctrl+a', () => exec('selectAll')); | ||||
|     utils.bindShortcut('ctrl+z', () => exec('undo')); | ||||
|     utils.bindShortcut('ctrl+y', () => exec('redo')); | ||||
|  | ||||
|     utils.bindShortcut('meta+c', () => exec("copy")); | ||||
|     utils.bindShortcut('meta+v', () => exec('paste')); | ||||
|     utils.bindShortcut('meta+x', () => exec('cut')); | ||||
|     utils.bindShortcut('meta+a', () => exec('selectAll')); | ||||
|     utils.bindShortcut('meta+z', () => exec('undo')); | ||||
|     utils.bindShortcut('meta+y', () => exec('redo')); | ||||
| } | ||||
|  | ||||
| $("#export-note-button").click(function () { | ||||
|     if ($(this).hasClass("disabled")) { | ||||
|         return; | ||||
| @@ -140,6 +135,16 @@ $("#export-note-button").click(function () { | ||||
|     exportDialog.showDialog('single'); | ||||
| }); | ||||
|  | ||||
| $('[data-toggle="tooltip"]').tooltip({ | ||||
|     html: true | ||||
| }); | ||||
|  | ||||
| $("#import-files-button").click(importDialog.showDialog); | ||||
|  | ||||
| macInit.init(); | ||||
|  | ||||
| searchNotesService.init(); // should be in front of treeService since that one manipulates address bar hash | ||||
|  | ||||
| treeService.showTree(); | ||||
|  | ||||
| entrypoints.registerEntrypoints(); | ||||
|   | ||||
| @@ -137,6 +137,10 @@ function linkTypeChanged() { | ||||
|  | ||||
| $linkTypes.change(linkTypeChanged); | ||||
|  | ||||
| // return back focus to note text detail after quitting add link | ||||
| // the problem is that cursor position is reset | ||||
| $dialog.on("hidden.bs.modal", () => noteDetailText.focus()); | ||||
|  | ||||
| export default { | ||||
|     showDialog | ||||
| }; | ||||
| @@ -97,7 +97,7 @@ function AttributesModel() { | ||||
|         await showAttributes(attributes); | ||||
|  | ||||
|         // attribute might not be rendered immediatelly so could not focus | ||||
|         setTimeout(() => $(".attribute-type-select:last").focus(), 100); | ||||
|         setTimeout(() => $(".attribute-type-select:last").focus(), 1000); | ||||
|     }; | ||||
|  | ||||
|     this.deleteAttribute = function(data, event) { | ||||
|   | ||||
| @@ -1,6 +1,9 @@ | ||||
| import treeService from '../services/tree.js'; | ||||
| import treeUtils from "../services/tree_utils.js"; | ||||
| import exportService from "../services/export.js"; | ||||
| import utils from "../services/utils.js"; | ||||
| import protectedSessionHolder from "../services/protected_session_holder.js"; | ||||
| import messagingService from "../services/messaging.js"; | ||||
| import infoService from "../services/info.js"; | ||||
|  | ||||
| const $dialog = $("#export-dialog"); | ||||
| const $form = $("#export-form"); | ||||
| @@ -9,10 +12,25 @@ const $subtreeFormats = $("#export-subtree-formats"); | ||||
| const $singleFormats = $("#export-single-formats"); | ||||
| const $subtreeType = $("#export-type-subtree"); | ||||
| const $singleType = $("#export-type-single"); | ||||
| const $exportProgressWrapper = $("#export-progress-count-wrapper"); | ||||
| const $exportProgressCount = $("#export-progress-count"); | ||||
| const $exportButton = $("#export-button"); | ||||
| const $opmlVersions = $("#opml-versions"); | ||||
|  | ||||
| let exportId = ''; | ||||
|  | ||||
| async function showDialog(defaultType) { | ||||
|     // each opening of the dialog resets the exportId so we don't associate it with previous exports anymore | ||||
|     exportId = ''; | ||||
|     $exportButton.removeAttr("disabled"); | ||||
|     $exportProgressWrapper.hide(); | ||||
|     $exportProgressCount.text('0'); | ||||
|  | ||||
|     if (defaultType === 'subtree') { | ||||
|         $subtreeType.prop("checked", true).change(); | ||||
|  | ||||
|         // to show/hide OPML versions | ||||
|         $("input[name=export-subtree-format]:checked").change(); | ||||
|     } | ||||
|     else if (defaultType === 'single') { | ||||
|         $singleType.prop("checked", true).change(); | ||||
| @@ -21,6 +39,8 @@ async function showDialog(defaultType) { | ||||
|         throw new Error("Unrecognized type " + defaultType); | ||||
|     } | ||||
|  | ||||
|     $("#opml-v2").prop("checked", true); // setting default | ||||
|  | ||||
|     glob.activeDialog = $dialog; | ||||
|  | ||||
|     $dialog.modal(); | ||||
| @@ -32,6 +52,9 @@ async function showDialog(defaultType) { | ||||
| } | ||||
|  | ||||
| $form.submit(() => { | ||||
|     // disabling so export can't be triggered again | ||||
|     $exportButton.attr("disabled", "disabled"); | ||||
|  | ||||
|     const exportType = $dialog.find("input[name='export-type']:checked").val(); | ||||
|  | ||||
|     if (!exportType) { | ||||
| @@ -44,15 +67,23 @@ $form.submit(() => { | ||||
|         ? $("input[name=export-subtree-format]:checked").val() | ||||
|         : $("input[name=export-single-format]:checked").val(); | ||||
|  | ||||
|     const exportVersion = exportFormat === 'opml' ? $dialog.find("input[name='opml-version']:checked").val() : "1.0"; | ||||
|  | ||||
|     const currentNode = treeService.getCurrentNode(); | ||||
|  | ||||
|     exportService.exportBranch(currentNode.data.branchId, exportType, exportFormat); | ||||
|  | ||||
|     $dialog.modal('hide'); | ||||
|     exportBranch(currentNode.data.branchId, exportType, exportFormat, exportVersion); | ||||
|  | ||||
|     return false; | ||||
| }); | ||||
|  | ||||
| function exportBranch(branchId, type, format, version) { | ||||
|     exportId = utils.randomString(10); | ||||
|  | ||||
|     const url = utils.getHost() + `/api/notes/${branchId}/export/${type}/${format}/${version}/${exportId}?protectedSessionId=` + encodeURIComponent(protectedSessionHolder.getProtectedSessionId()); | ||||
|  | ||||
|     utils.download(url); | ||||
| } | ||||
|  | ||||
| $('input[name=export-type]').change(function () { | ||||
|     if (this.value === 'subtree') { | ||||
|         if ($("input[name=export-subtree-format]:checked").length === 0) { | ||||
| @@ -72,6 +103,39 @@ $('input[name=export-type]').change(function () { | ||||
|     } | ||||
| }); | ||||
|  | ||||
| $('input[name=export-subtree-format]').change(function () { | ||||
|     if (this.value === 'opml') { | ||||
|         $opmlVersions.slideDown(); | ||||
|     } | ||||
|     else { | ||||
|         $opmlVersions.slideUp(); | ||||
|     } | ||||
| }); | ||||
|  | ||||
| messagingService.subscribeToMessages(async message => { | ||||
|     if (message.type === 'export-error') { | ||||
|         infoService.showError(message.message); | ||||
|         $dialog.modal('hide'); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     if (!message.exportId || message.exportId !== exportId) { | ||||
|         // incoming messages must correspond to this export instance | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     if (message.type === 'export-progress-count') { | ||||
|         $exportProgressWrapper.slideDown(); | ||||
|  | ||||
|         $exportProgressCount.text(message.progressCount); | ||||
|     } | ||||
|     else if (message.type === 'export-finished') { | ||||
|         $dialog.modal('hide'); | ||||
|  | ||||
|         infoService.showMessage("Export finished successfully."); | ||||
|     } | ||||
| }); | ||||
|  | ||||
| export default { | ||||
|     showDialog | ||||
| }; | ||||
							
								
								
									
										11
									
								
								src/public/javascripts/dialogs/help.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								src/public/javascripts/dialogs/help.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,11 @@ | ||||
| const $dialog = $("#help-dialog"); | ||||
|  | ||||
| async function showDialog() { | ||||
|     glob.activeDialog = $dialog; | ||||
|  | ||||
|     $dialog.modal(); | ||||
| } | ||||
|  | ||||
| export default { | ||||
|     showDialog | ||||
| } | ||||
							
								
								
									
										145
									
								
								src/public/javascripts/dialogs/import.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										145
									
								
								src/public/javascripts/dialogs/import.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,145 @@ | ||||
| import treeService from '../services/tree.js'; | ||||
| import utils from '../services/utils.js'; | ||||
| import treeUtils from "../services/tree_utils.js"; | ||||
| import server from "../services/server.js"; | ||||
| import infoService from "../services/info.js"; | ||||
| import messagingService from "../services/messaging.js"; | ||||
|  | ||||
| const $dialog = $("#import-dialog"); | ||||
| const $form = $("#import-form"); | ||||
| const $noteTitle = $dialog.find(".note-title"); | ||||
| const $fileUploadInput = $("#import-file-upload-input"); | ||||
| const $importProgressCountWrapper = $("#import-progress-count-wrapper"); | ||||
| const $importProgressCount = $("#import-progress-count"); | ||||
| const $importButton = $("#import-button"); | ||||
| const $safeImportCheckbox = $("#safe-import-checkbox"); | ||||
| const $shrinkImagesCheckbox = $("#shrink-images-checkbox"); | ||||
| const $textImportedAsTextCheckbox = $("#text-imported-as-text-checkbox"); | ||||
| const $codeImportedAsCodeCheckbox = $("#code-imported-as-code-checkbox"); | ||||
| const $explodeArchivesCheckbox = $("#explode-archives-checkbox"); | ||||
|  | ||||
| let importId; | ||||
|  | ||||
| async function showDialog() { | ||||
|     // each opening of the dialog resets the importId so we don't associate it with previous imports anymore | ||||
|     importId = ''; | ||||
|     $importProgressCountWrapper.hide(); | ||||
|     $importProgressCount.text('0'); | ||||
|     $fileUploadInput.val('').change(); // to trigger Import button disabling listener below | ||||
|  | ||||
|     $safeImportCheckbox.prop("checked", true); | ||||
|     $shrinkImagesCheckbox.prop("checked", true); | ||||
|     $textImportedAsTextCheckbox.prop("checked", true); | ||||
|     $codeImportedAsCodeCheckbox.prop("checked", true); | ||||
|     $explodeArchivesCheckbox.prop("checked", true); | ||||
|  | ||||
|     glob.activeDialog = $dialog; | ||||
|  | ||||
|     const currentNode = treeService.getCurrentNode(); | ||||
|     $noteTitle.text(await treeUtils.getNoteTitle(currentNode.data.noteId)); | ||||
|  | ||||
|     $dialog.modal(); | ||||
| } | ||||
|  | ||||
| $form.submit(() => { | ||||
|     const currentNode = treeService.getCurrentNode(); | ||||
|  | ||||
|     // disabling so that import is not triggered again. | ||||
|     $importButton.attr("disabled", "disabled"); | ||||
|  | ||||
|     importIntoNote(currentNode.data.noteId); | ||||
|  | ||||
|     return false; | ||||
| }); | ||||
|  | ||||
| async function importIntoNote(importNoteId) { | ||||
|     const files = Array.from($fileUploadInput[0].files); // shallow copy since we're resetting the upload button below | ||||
|  | ||||
|     // we generate it here (and not on opening) for the case when you try to import multiple times from the same | ||||
|     // dialog (which shouldn't happen, but still ...) | ||||
|     importId = utils.randomString(10); | ||||
|  | ||||
|     const options = { | ||||
|         safeImport: boolToString($safeImportCheckbox), | ||||
|         shrinkImages: boolToString($shrinkImagesCheckbox), | ||||
|         textImportedAsText: boolToString($textImportedAsTextCheckbox), | ||||
|         codeImportedAsCode: boolToString($codeImportedAsCodeCheckbox), | ||||
|         explodeArchives: boolToString($explodeArchivesCheckbox) | ||||
|     }; | ||||
|  | ||||
|     await uploadFiles(importNoteId, files, options); | ||||
|  | ||||
|     $dialog.modal('hide'); | ||||
| } | ||||
|  | ||||
| async function uploadFiles(importNoteId, files, options) { | ||||
|     let noteId; | ||||
|  | ||||
|     for (const file of files) { | ||||
|         const formData = new FormData(); | ||||
|         formData.append('upload', file); | ||||
|         formData.append('importId', importId); | ||||
|  | ||||
|         for (const key in options) { | ||||
|             formData.append(key, options[key]); | ||||
|         } | ||||
|  | ||||
|         ({noteId} = await $.ajax({ | ||||
|             url: baseApiUrl + 'notes/' + importNoteId + '/import', | ||||
|             headers: server.getHeaders(), | ||||
|             data: formData, | ||||
|             dataType: 'json', | ||||
|             type: 'POST', | ||||
|             timeout: 60 * 60 * 1000, | ||||
|             contentType: false, // NEEDED, DON'T REMOVE THIS | ||||
|             processData: false, // NEEDED, DON'T REMOVE THIS | ||||
|         })); | ||||
|     } | ||||
|  | ||||
|     infoService.showMessage("Import finished successfully."); | ||||
|  | ||||
|     await treeService.reload(); | ||||
|  | ||||
|     if (noteId) { | ||||
|         const node = await treeService.activateNote(noteId); | ||||
|  | ||||
|         node.setExpanded(true); | ||||
|     } | ||||
| } | ||||
|  | ||||
| function boolToString($el) { | ||||
|     return $el.is(":checked") ? "true" : "false"; | ||||
| } | ||||
|  | ||||
| messagingService.subscribeToMessages(async message => { | ||||
|     if (message.type === 'import-error') { | ||||
|         infoService.showError(message.message); | ||||
|         $dialog.modal('hide'); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     if (!message.importId || message.importId !== importId) { | ||||
|         // incoming messages must correspond to this import instance | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     if (message.type === 'import-progress-count') { | ||||
|         $importProgressCountWrapper.slideDown(); | ||||
|  | ||||
|         $importProgressCount.text(message.progressCount); | ||||
|     } | ||||
| }); | ||||
|  | ||||
| $fileUploadInput.change(() => { | ||||
|     if ($fileUploadInput.val()) { | ||||
|         $importButton.removeAttr("disabled"); | ||||
|     } | ||||
|     else { | ||||
|         $importButton.attr("disabled", "disabled"); | ||||
|     } | ||||
| }); | ||||
|  | ||||
| export default { | ||||
|     showDialog, | ||||
|     uploadFiles | ||||
| } | ||||
| @@ -6,8 +6,6 @@ const $dialog = $("#jump-to-note-dialog"); | ||||
| const $autoComplete = $("#jump-to-note-autocomplete"); | ||||
| const $showInFullTextButton = $("#show-in-full-text-button"); | ||||
|  | ||||
| $dialog.on("shown.bs.modal", e => $autoComplete.focus()); | ||||
|  | ||||
| async function showDialog() { | ||||
|     glob.activeDialog = $dialog; | ||||
|  | ||||
|   | ||||
							
								
								
									
										29
									
								
								src/public/javascripts/dialogs/note_info.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								src/public/javascripts/dialogs/note_info.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,29 @@ | ||||
| import noteDetailService from '../services/note_detail.js'; | ||||
|  | ||||
| const $dialog = $("#note-info-dialog"); | ||||
| const $noteId = $("#note-info-note-id"); | ||||
| const $dateCreated = $("#note-info-date-created"); | ||||
| const $dateModified = $("#note-info-date-modified"); | ||||
| const $type = $("#note-info-type"); | ||||
| const $mime = $("#note-info-mime"); | ||||
| const $okButton = $("#note-info-ok-button"); | ||||
|  | ||||
| function showDialog() { | ||||
|     glob.activeDialog = $dialog; | ||||
|  | ||||
|     $dialog.modal(); | ||||
|  | ||||
|     const currentNote = noteDetailService.getCurrentNote(); | ||||
|  | ||||
|     $noteId.text(currentNote.noteId); | ||||
|     $dateCreated.text(currentNote.dateCreated); | ||||
|     $dateModified.text(currentNote.dateModified); | ||||
|     $type.text(currentNote.type); | ||||
|     $mime.text(currentNote.mime); | ||||
| } | ||||
|  | ||||
| $okButton.click(() => $dialog.modal('hide')); | ||||
|  | ||||
| export default { | ||||
|     showDialog | ||||
| }; | ||||
| @@ -8,7 +8,7 @@ function showDialog() { | ||||
|  | ||||
|     $dialog.modal(); | ||||
|  | ||||
|     const noteText = noteDetailService.getCurrentNote().content; | ||||
|     const noteText = noteDetailService.getCurrentNote().noteContent.content; | ||||
|  | ||||
|     $noteSource.text(formatHtml(noteText)); | ||||
| } | ||||
|   | ||||
| @@ -5,6 +5,7 @@ import server from '../services/server.js'; | ||||
| import infoService from "../services/info.js"; | ||||
| import zoomService from "../services/zoom.js"; | ||||
| import utils from "../services/utils.js"; | ||||
| import cssLoader from "../services/css_loader.js"; | ||||
|  | ||||
| const $dialog = $("#options-dialog"); | ||||
|  | ||||
| @@ -44,10 +45,28 @@ addTabHandler((function() { | ||||
|     const $zoomFactorSelect = $("#zoom-factor-select"); | ||||
|     const $leftPaneMinWidth = $("#left-pane-min-width"); | ||||
|     const $leftPaneWidthPercent = $("#left-pane-width-percent"); | ||||
|     const $html = $("html"); | ||||
|     const $mainFontSize = $("#main-font-size"); | ||||
|     const $treeFontSize = $("#tree-font-size"); | ||||
|     const $detailFontSize = $("#detail-font-size"); | ||||
|     const $body = $("body"); | ||||
|     const $container = $("#container"); | ||||
|  | ||||
|     function optionsLoaded(options) { | ||||
|     async function optionsLoaded(options) { | ||||
|         const themes = [ | ||||
|             { val: 'white', title: 'White' }, | ||||
|             { val: 'dark', title: 'Dark' }, | ||||
|             { val: 'black', title: 'Black' } | ||||
|         ].concat(await server.get('options/user-themes')); | ||||
|  | ||||
|         $themeSelect.empty(); | ||||
|  | ||||
|         for (const theme of themes) { | ||||
|             $themeSelect.append($("<option>") | ||||
|                 .attr("value", theme.val) | ||||
|                 .attr("data-note-id", theme.noteId) | ||||
|                 .html(theme.title)); | ||||
|         } | ||||
|  | ||||
|         $themeSelect.val(options.theme); | ||||
|  | ||||
|         if (utils.isElectron()) { | ||||
| @@ -59,21 +78,35 @@ addTabHandler((function() { | ||||
|  | ||||
|         $leftPaneMinWidth.val(options.leftPaneMinWidth); | ||||
|         $leftPaneWidthPercent.val(options.leftPaneWidthPercent); | ||||
|  | ||||
|         $mainFontSize.val(options.mainFontSize); | ||||
|         $treeFontSize.val(options.treeFontSize); | ||||
|         $detailFontSize.val(options.detailFontSize); | ||||
|     } | ||||
|  | ||||
|     $themeSelect.change(function() { | ||||
|         const newTheme = $(this).val(); | ||||
|  | ||||
|         $html.attr("class", "theme-" + newTheme); | ||||
|         for (const clazz of Array.from($body[0].classList)) { // create copy to safely iterate over while removing classes | ||||
|             if (clazz.startsWith("theme-")) { | ||||
|                 $body.removeClass(clazz); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         const noteId = $(this).find(":selected").attr("data-note-id"); | ||||
|  | ||||
|         if (noteId) { | ||||
|             // make sure the CSS is loaded | ||||
|             // if the CSS has been loaded and then updated then the changes won't take effect though | ||||
|             cssLoader.requireCss(`/api/notes/download/${noteId}`); | ||||
|         } | ||||
|  | ||||
|         $body.addClass("theme-" + newTheme); | ||||
|  | ||||
|         server.put('options/theme/' + newTheme); | ||||
|     }); | ||||
|  | ||||
|     $zoomFactorSelect.change(function() { | ||||
|         const newZoomFactor = $(this).val(); | ||||
|  | ||||
|         zoomService.setZoomFactorAndSave(newZoomFactor); | ||||
|     }); | ||||
|     $zoomFactorSelect.change(function() { zoomService.setZoomFactorAndSave($(this).val()); }); | ||||
|  | ||||
|     function resizeLeftPanel() { | ||||
|         const leftPanePercent = parseInt($leftPaneWidthPercent.val()); | ||||
| @@ -83,20 +116,42 @@ addTabHandler((function() { | ||||
|         $container.css("grid-template-columns", `minmax(${leftPaneMinWidth}px, ${leftPanePercent}fr) ${rightPanePercent}fr`); | ||||
|     } | ||||
|  | ||||
|     $leftPaneMinWidth.change(function() { | ||||
|         const newMinWidth = $(this).val(); | ||||
|     $leftPaneMinWidth.change(async function() { | ||||
|         await server.put('options/leftPaneMinWidth/' + $(this).val()); | ||||
|  | ||||
|         resizeLeftPanel(); | ||||
|  | ||||
|         server.put('options/leftPaneMinWidth/' + newMinWidth); | ||||
|     }); | ||||
|  | ||||
|     $leftPaneWidthPercent.change(function() { | ||||
|         const newWidthPercent = $(this).val(); | ||||
|     $leftPaneWidthPercent.change(async function() { | ||||
|         await server.put('options/leftPaneWidthPercent/' + $(this).val()); | ||||
|  | ||||
|         resizeLeftPanel(); | ||||
|     }); | ||||
|  | ||||
|         server.put('options/leftPaneWidthPercent/' + newWidthPercent); | ||||
|     function applyFontSizes() { | ||||
|         console.log($mainFontSize.val() + "% !important"); | ||||
|  | ||||
|         $body.get(0).style.setProperty("--main-font-size", $mainFontSize.val() + "%"); | ||||
|         $body.get(0).style.setProperty("--tree-font-size", $treeFontSize.val() + "%"); | ||||
|         $body.get(0).style.setProperty("--detail-font-size", $detailFontSize.val() + "%"); | ||||
|     } | ||||
|  | ||||
|     $mainFontSize.change(async function() { | ||||
|         await server.put('options/mainFontSize/' + $(this).val()); | ||||
|  | ||||
|         applyFontSizes(); | ||||
|     }); | ||||
|  | ||||
|     $treeFontSize.change(async function() { | ||||
|         await server.put('options/treeFontSize/' + $(this).val()); | ||||
|  | ||||
|         applyFontSizes(); | ||||
|     }); | ||||
|  | ||||
|     $detailFontSize.change(async function() { | ||||
|         await server.put('options/detailFontSize/' + $(this).val()); | ||||
|  | ||||
|         applyFontSizes(); | ||||
|     }); | ||||
|  | ||||
|     return { | ||||
| @@ -198,15 +253,17 @@ addTabHandler((async function () { | ||||
|     const $syncVersion = $("#sync-version"); | ||||
|     const $buildDate = $("#build-date"); | ||||
|     const $buildRevision = $("#build-revision"); | ||||
|     const $dataDirectory = $("#data-directory"); | ||||
|  | ||||
|     const appInfo = await server.get('app-info'); | ||||
|  | ||||
|     $appVersion.html(appInfo.appVersion); | ||||
|     $dbVersion.html(appInfo.dbVersion); | ||||
|     $syncVersion.html(appInfo.syncVersion); | ||||
|     $buildDate.html(appInfo.buildDate); | ||||
|     $buildRevision.html(appInfo.buildRevision); | ||||
|     $appVersion.text(appInfo.appVersion); | ||||
|     $dbVersion.text(appInfo.dbVersion); | ||||
|     $syncVersion.text(appInfo.syncVersion); | ||||
|     $buildDate.text(appInfo.buildDate); | ||||
|     $buildRevision.text(appInfo.buildRevision); | ||||
|     $buildRevision.attr('href', 'https://github.com/zadam/trilium/commit/' + appInfo.buildRevision); | ||||
|     $dataDirectory.text(appInfo.dataDirectory); | ||||
|  | ||||
|     return {}; | ||||
| })()); | ||||
|   | ||||
| @@ -1,4 +1,3 @@ | ||||
| import utils from '../services/utils.js'; | ||||
| import libraryLoader from '../services/library_loader.js'; | ||||
| import server from '../services/server.js'; | ||||
| import infoService from "../services/info.js"; | ||||
| @@ -8,14 +7,17 @@ const $query = $('#sql-console-query'); | ||||
| const $executeButton = $('#sql-console-execute'); | ||||
| const $resultHead = $('#sql-console-results thead'); | ||||
| const $resultBody = $('#sql-console-results tbody'); | ||||
| const $tables = $("#sql-console-tables"); | ||||
|  | ||||
| let codeEditor; | ||||
|  | ||||
| $dialog.on("shown.bs.modal", e => initEditor()); | ||||
|  | ||||
| function showDialog() { | ||||
| async function showDialog() { | ||||
|     glob.activeDialog = $dialog; | ||||
|  | ||||
|     await showTables(); | ||||
|  | ||||
|     $dialog.modal(); | ||||
| } | ||||
|  | ||||
| @@ -96,6 +98,32 @@ async function execute(e) { | ||||
|     } | ||||
| } | ||||
|  | ||||
| async function showTables() { | ||||
|     const tables = await server.get('sql/schema'); | ||||
|  | ||||
|     $tables.empty(); | ||||
|  | ||||
|     for (const table of tables) { | ||||
|         const $tableLink = $('<button class="btn">').text(table.name); | ||||
|  | ||||
|         const $columns = $("<table>"); | ||||
|  | ||||
|         for (const column of table.columns) { | ||||
|             $columns.append( | ||||
|                 $("<tr>") | ||||
|                     .append($("<td>").text(column.name)) | ||||
|                     .append($("<td>").text(column.type)) | ||||
|             ); | ||||
|         } | ||||
|  | ||||
|         $tables.append($tableLink).append(" "); | ||||
|  | ||||
|         $tableLink | ||||
|             .tooltip({html: true, title: $columns.html()}) | ||||
|             .click(() => codeEditor.setValue("SELECT * FROM " + table.name + " LIMIT 100")); | ||||
|     } | ||||
| } | ||||
|  | ||||
| $query.bind('keydown', 'ctrl+return', execute); | ||||
|  | ||||
| $executeButton.click(execute); | ||||
|   | ||||
| @@ -8,15 +8,13 @@ class NoteFull extends NoteShort { | ||||
|         super(treeCache, row); | ||||
|  | ||||
|         /** @param {string} */ | ||||
|         this.content = row.content; | ||||
|         this.noteContent = row.noteContent; | ||||
|  | ||||
|         if (this.content !== "" && this.isJson()) { | ||||
|             try { | ||||
|                 /** @param {object} */ | ||||
|                 this.jsonContent = JSON.parse(this.content); | ||||
|             } | ||||
|             catch(e) {} | ||||
|         } | ||||
|         /** @param {string} */ | ||||
|         this.dateCreated = row.dateCreated; | ||||
|  | ||||
|         /** @param {string} */ | ||||
|         this.dateModified = row.dateModified; | ||||
|     } | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -160,7 +160,7 @@ async function createPromotedAttributeRow(definitionAttr, valueAttr) { | ||||
|                 $input.autocomplete({ | ||||
|                     appendTo: document.querySelector('body'), | ||||
|                     hint: false, | ||||
|                     autoselect: true, | ||||
|                     autoselect: false, | ||||
|                     openOnFocus: true, | ||||
|                     minLength: 0, | ||||
|                     tabAutocomplete: false | ||||
|   | ||||
| @@ -9,7 +9,9 @@ async function getAndExecuteBundle(noteId, originEntity = null) { | ||||
| } | ||||
|  | ||||
| async function executeBundle(bundle, originEntity) { | ||||
|     const apiContext = ScriptContext(bundle.note, bundle.allNotes, originEntity); | ||||
|     console.log(bundle); | ||||
|  | ||||
|     const apiContext = await ScriptContext(bundle.noteId, bundle.allNoteIds, originEntity); | ||||
|  | ||||
|     try { | ||||
|         return await (function () { | ||||
| @@ -17,7 +19,7 @@ async function executeBundle(bundle, originEntity) { | ||||
|         }.call(apiContext)); | ||||
|     } | ||||
|     catch (e) { | ||||
|         infoService.showAndLogError(`Execution of script "${bundle.note.title}" (${bundle.note.noteId}) failed with error: ${e.message}`); | ||||
|         infoService.showAndLogError(`Execution of ${bundle.noteId} failed with error: ${e.message}`); | ||||
|     } | ||||
| } | ||||
|  | ||||
| @@ -30,9 +32,13 @@ async function executeStartupBundles() { | ||||
| } | ||||
|  | ||||
| async function executeRelationBundles(note, relationName) { | ||||
|     const bundlesToRun = await server.get("script/relation/" + note.noteId + "/" + relationName); | ||||
|     note.bundleCache = note.bundleCache || {}; | ||||
|  | ||||
|     for (const bundle of bundlesToRun) { | ||||
|     if (!note.bundleCache[relationName]) { | ||||
|         note.bundleCache[relationName] = await server.get("script/relation/" + note.noteId + "/" + relationName); | ||||
|     } | ||||
|  | ||||
|     for (const bundle of note.bundleCache[relationName]) { | ||||
|         await executeBundle(bundle, note); | ||||
|     } | ||||
| } | ||||
|   | ||||
							
								
								
									
										13
									
								
								src/public/javascripts/services/css_loader.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								src/public/javascripts/services/css_loader.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,13 @@ | ||||
| async function requireCss(url) { | ||||
|     const css = Array | ||||
|         .from(document.querySelectorAll('link')) | ||||
|         .map(scr => scr.href); | ||||
|  | ||||
|     if (!css.includes(url)) { | ||||
|         $('head').append($('<link rel="stylesheet" type="text/css" />').attr('href', url)); | ||||
|     } | ||||
| } | ||||
|  | ||||
| export default { | ||||
|     requireCss | ||||
| } | ||||
| @@ -1,5 +1,6 @@ | ||||
| import treeService from './tree.js'; | ||||
| import treeChangesService from './branches.js'; | ||||
| import importDialog from '../dialogs/import.js'; | ||||
|  | ||||
| const dragAndDropSetup = { | ||||
|     autoExpandMS: 600, | ||||
| @@ -9,6 +10,14 @@ const dragAndDropSetup = { | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         if (!data.originalEvent.ctrlKey) { | ||||
|             // keep existing selection only if CTRL key is pressed | ||||
|             for (const selectedNode of treeService.getSelectedNodes()) { | ||||
|                 selectedNode.setSelected(false); | ||||
|                 selectedNode.renderTitle(); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         node.setSelected(true); | ||||
|  | ||||
|         // this is for dragging notes into relation map | ||||
| @@ -24,23 +33,34 @@ const dragAndDropSetup = { | ||||
|         return true; | ||||
|     }, | ||||
|     dragEnter: (node, data) => true, // allow drop on any node | ||||
|     dragOver: (node, data) => true, | ||||
|     dragDrop: (node, data) => { | ||||
|         // This function MUST be defined to enable dropping of items on the tree. | ||||
|         // data.hitMode is 'before', 'after', or 'over'. | ||||
|         const dataTransfer = data.dataTransfer; | ||||
|  | ||||
|         const selectedNodes = treeService.getSelectedNodes(); | ||||
|  | ||||
|         if (data.hitMode === "before") { | ||||
|             treeChangesService.moveBeforeNode(selectedNodes, node); | ||||
|         } | ||||
|         else if (data.hitMode === "after") { | ||||
|             treeChangesService.moveAfterNode(selectedNodes, node); | ||||
|         } | ||||
|         else if (data.hitMode === "over") { | ||||
|             treeChangesService.moveToNode(selectedNodes, node); | ||||
|         if (dataTransfer && dataTransfer.files && dataTransfer.files.length > 0) { | ||||
|             importDialog.uploadFiles(node.data.noteId, dataTransfer.files, { | ||||
|                 safeImport: true, | ||||
|                 shrinkImages: true, | ||||
|                 textImportedAsText: true, | ||||
|                 codeImportedAsCode: true, | ||||
|                 explodeArchives: true | ||||
|             }); | ||||
|         } | ||||
|         else { | ||||
|             throw new Error("Unknown hitMode=" + data.hitMode); | ||||
|             // This function MUST be defined to enable dropping of items on the tree. | ||||
|             // data.hitMode is 'before', 'after', or 'over'. | ||||
|  | ||||
|             const selectedNodes = treeService.getSelectedNodes(); | ||||
|  | ||||
|             if (data.hitMode === "before") { | ||||
|                 treeChangesService.moveBeforeNode(selectedNodes, node); | ||||
|             } else if (data.hitMode === "after") { | ||||
|                 treeChangesService.moveAfterNode(selectedNodes, node); | ||||
|             } else if (data.hitMode === "over") { | ||||
|                 treeChangesService.moveToNode(selectedNodes, node); | ||||
|             } else { | ||||
|                 throw new Error("Unknown hitMode=" + data.hitMode); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| }; | ||||
|   | ||||
| @@ -1,7 +1,6 @@ | ||||
| import utils from "./utils.js"; | ||||
| import treeService from "./tree.js"; | ||||
| import linkService from "./link.js"; | ||||
| import fileService from "./file.js"; | ||||
| import zoomService from "./zoom.js"; | ||||
| import noteRevisionsDialog from "../dialogs/note_revisions.js"; | ||||
| import optionsDialog from "../dialogs/options.js"; | ||||
| @@ -12,6 +11,8 @@ import recentChangesDialog from "../dialogs/recent_changes.js"; | ||||
| import sqlConsoleDialog from "../dialogs/sql_console.js"; | ||||
| import searchNotesService from "./search_notes.js"; | ||||
| import attributesDialog from "../dialogs/attributes.js"; | ||||
| import helpDialog from "../dialogs/help.js"; | ||||
| import noteInfoDialog from "../dialogs/note_info.js"; | ||||
| import protectedSessionService from "./protected_session.js"; | ||||
|  | ||||
| function registerEntrypoints() { | ||||
| @@ -54,15 +55,28 @@ function registerEntrypoints() { | ||||
|  | ||||
|     $("#options-button").click(optionsDialog.showDialog); | ||||
|  | ||||
|     $("#show-help-button").click(helpDialog.showDialog); | ||||
|     utils.bindShortcut('f1', helpDialog.showDialog); | ||||
|  | ||||
|     $("#open-sql-console-button").click(sqlConsoleDialog.showDialog); | ||||
|     utils.bindShortcut('alt+o', sqlConsoleDialog.showDialog); | ||||
|  | ||||
|     $("#show-note-info-button").click(noteInfoDialog.showDialog); | ||||
|  | ||||
|     if (utils.isElectron()) { | ||||
|         $("#history-navigation").show(); | ||||
|         $("#history-back-button").click(window.history.back); | ||||
|         $("#history-forward-button").click(window.history.forward); | ||||
|  | ||||
|         utils.bindShortcut('alt+left', window.history.back); | ||||
|         utils.bindShortcut('alt+right', window.history.forward); | ||||
|         if (utils.isMac()) { | ||||
|             // Mac has a different history navigation shortcuts - https://github.com/zadam/trilium/issues/376 | ||||
|             utils.bindShortcut('meta+left', window.history.back); | ||||
|             utils.bindShortcut('meta+right', window.history.forward); | ||||
|         } | ||||
|         else { | ||||
|             utils.bindShortcut('alt+left', window.history.back); | ||||
|             utils.bindShortcut('alt+right', window.history.forward); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     utils.bindShortcut('alt+m', e => { | ||||
| @@ -87,13 +101,18 @@ function registerEntrypoints() { | ||||
|  | ||||
|     utils.bindShortcut('ctrl+r', utils.reloadApp); | ||||
|  | ||||
|     $(document).bind('keydown', 'ctrl+shift+i', () => { | ||||
|         if (utils.isElectron()) { | ||||
|     $("#open-dev-tools-button").toggle(utils.isElectron()); | ||||
|  | ||||
|     if (utils.isElectron()) { | ||||
|         const openDevTools = () => { | ||||
|             require('electron').remote.getCurrentWindow().toggleDevTools(); | ||||
|  | ||||
|             return false; | ||||
|         } | ||||
|     }); | ||||
|         }; | ||||
|  | ||||
|         utils.bindShortcut('ctrl+shift+i', openDevTools); | ||||
|         $("#open-dev-tools-button").click(openDevTools); | ||||
|     } | ||||
|  | ||||
|     function openInPageSearch() { | ||||
|         if (utils.isElectron()) { | ||||
| @@ -117,10 +136,6 @@ function registerEntrypoints() { | ||||
|  | ||||
|     utils.bindShortcut('ctrl+f', openInPageSearch); | ||||
|  | ||||
|     if (utils.isMac()) { | ||||
|         utils.bindShortcut('meta+f', openInPageSearch); | ||||
|     } | ||||
|  | ||||
|     // FIXME: do we really need these at this point? | ||||
|     utils.bindShortcut("ctrl+shift+up", () => { | ||||
|         const node = treeService.getCurrentNode(); | ||||
| @@ -139,13 +154,11 @@ function registerEntrypoints() { | ||||
|     }); | ||||
|  | ||||
|     if (utils.isElectron()) { | ||||
|         $(document).bind('keydown', 'ctrl+-', zoomService.decreaseZoomFactor); | ||||
|         $(document).bind('keydown', 'ctrl+=', zoomService.increaseZoomFactor); | ||||
|         utils.bindShortcut('ctrl+-', zoomService.decreaseZoomFactor); | ||||
|         utils.bindShortcut('ctrl+=', zoomService.increaseZoomFactor); | ||||
|     } | ||||
|  | ||||
|     $("#note-title").bind('keydown', 'return', () => $("#note-detail-text").focus()); | ||||
|  | ||||
|     $("#upload-file-button").click(fileService.uploadFile); | ||||
| } | ||||
|  | ||||
| export default { | ||||
|   | ||||
| @@ -1,50 +0,0 @@ | ||||
| import treeService from './tree.js'; | ||||
| import protectedSessionHolder from './protected_session_holder.js'; | ||||
| import utils from './utils.js'; | ||||
| import server from './server.js'; | ||||
|  | ||||
| function exportBranch(branchId, type, format) { | ||||
|     const url = utils.getHost() + `/api/notes/${branchId}/export/${type}/${format}?protectedSessionId=` + encodeURIComponent(protectedSessionHolder.getProtectedSessionId()); | ||||
|  | ||||
|     console.log(url); | ||||
|  | ||||
|     utils.download(url); | ||||
| } | ||||
|  | ||||
| let importNoteId; | ||||
|  | ||||
| function importIntoNote(noteId) { | ||||
|     importNoteId = noteId; | ||||
|  | ||||
|     $("#import-upload").trigger('click'); | ||||
| } | ||||
|  | ||||
| $("#import-upload").change(async function() { | ||||
|     const formData = new FormData(); | ||||
|     formData.append('upload', this.files[0]); | ||||
|  | ||||
|     await $.ajax({ | ||||
|         url: baseApiUrl + 'notes/' + importNoteId + '/import', | ||||
|         headers: server.getHeaders(), | ||||
|         data: formData, | ||||
|         dataType: 'json', | ||||
|         type: 'POST', | ||||
|         contentType: false, // NEEDED, DON'T REMOVE THIS | ||||
|         processData: false, // NEEDED, DON'T REMOVE THIS | ||||
|     }) | ||||
|         .fail((xhr, status, error) => alert('Import error: ' + xhr.responseText)) | ||||
|         .done(async note => { | ||||
|             await treeService.reload(); | ||||
|  | ||||
|             if (note) { | ||||
|                 const node = await treeService.activateNote(note.noteId); | ||||
|  | ||||
|                 node.setExpanded(true); | ||||
|             } | ||||
|         }); | ||||
| }); | ||||
|  | ||||
| export default { | ||||
|     exportBranch, | ||||
|     importIntoNote | ||||
| }; | ||||
| @@ -1,29 +0,0 @@ | ||||
| import noteDetailService from "./note_detail.js"; | ||||
| import treeService from "./tree.js"; | ||||
| import server from "./server.js"; | ||||
|  | ||||
| function uploadFile() { | ||||
|     $("#file-upload").trigger('click'); | ||||
| } | ||||
|  | ||||
| $("#file-upload").change(async function() { | ||||
|     const formData = new FormData(); | ||||
|     formData.append('upload', this.files[0]); | ||||
|  | ||||
|     const resp = await $.ajax({ | ||||
|         url: baseApiUrl + 'notes/' + noteDetailService.getCurrentNoteId() + '/upload', | ||||
|         headers: server.getHeaders(), | ||||
|         data: formData, | ||||
|         type: 'POST', | ||||
|         contentType: false, // NEEDED, DON'T OMIT THIS | ||||
|         processData: false, // NEEDED, DON'T OMIT THIS | ||||
|     }); | ||||
|  | ||||
|     await treeService.reload(); | ||||
|  | ||||
|     await treeService.activateNote(resp.noteId); | ||||
| }); | ||||
|  | ||||
| export default { | ||||
|     uploadFile | ||||
| } | ||||
| @@ -43,7 +43,7 @@ function FrontendScriptApi(startNote, currentNote, originEntity = null) { | ||||
|     this.activateNewNote = async notePath => { | ||||
|         await treeService.reload(); | ||||
|  | ||||
|         await treeService.activateNote(notePath, noteDetailService.focusOnTitle); | ||||
|         await treeService.activateNote(notePath, noteDetailService.focusAndSelectTitle); | ||||
|     }; | ||||
|  | ||||
|     /** | ||||
|   | ||||
| @@ -1,3 +1,5 @@ | ||||
| import cssLoader from './css_loader.js'; | ||||
|  | ||||
| const CKEDITOR = {"js": ["libraries/ckeditor/ckeditor.js"]}; | ||||
|  | ||||
| const CODE_MIRROR = { | ||||
| @@ -34,7 +36,7 @@ const RELATION_MAP = { | ||||
|  | ||||
| async function requireLibrary(library) { | ||||
|     if (library.css) { | ||||
|         library.css.map(cssUrl => requireCss(cssUrl)); | ||||
|         library.css.map(cssUrl => cssLoader.requireCss(cssUrl)); | ||||
|     } | ||||
|  | ||||
|     if (library.js) { | ||||
| @@ -59,16 +61,6 @@ async function requireScript(url) { | ||||
|     await loadedScriptPromises[url]; | ||||
| } | ||||
|  | ||||
| async function requireCss(url) { | ||||
|     const css = Array | ||||
|         .from(document.querySelectorAll('link')) | ||||
|         .map(scr => scr.href); | ||||
|  | ||||
|     if (!css.includes(url)) { | ||||
|         $('head').append($('<link rel="stylesheet" type="text/css" />').attr('href', url)); | ||||
|     } | ||||
| } | ||||
|  | ||||
| export default { | ||||
|     requireLibrary, | ||||
|     CKEDITOR, | ||||
|   | ||||
| @@ -107,6 +107,7 @@ function init() { | ||||
| $(document).on('click', "a[data-action='note']", goToLink); | ||||
| $(document).on('click', 'div.popover-content a, div.ui-tooltip-content a', goToLink); | ||||
| $(document).on('dblclick', '#note-detail-text a', goToLink); | ||||
| $(document).on('click', '#note-detail-text.ck-read-only a', goToLink); | ||||
| $(document).on('click', 'span.ck-button__label', e => { | ||||
|     // this is a link preview dialog from CKEditor link editing | ||||
|     // for some reason clicked element is span | ||||
|   | ||||
							
								
								
									
										25
									
								
								src/public/javascripts/services/mac_init.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								src/public/javascripts/services/mac_init.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,25 @@ | ||||
| /** | ||||
|  * Mac specific initialization | ||||
|  */ | ||||
| import utils from "./utils.js"; | ||||
|  | ||||
| function init() { | ||||
|     if (utils.isElectron() && utils.isMac()) { | ||||
|         utils.bindShortcut('meta+c', () => exec("copy")); | ||||
|         utils.bindShortcut('meta+v', () => exec('paste')); | ||||
|         utils.bindShortcut('meta+x', () => exec('cut')); | ||||
|         utils.bindShortcut('meta+a', () => exec('selectAll')); | ||||
|         utils.bindShortcut('meta+z', () => exec('undo')); | ||||
|         utils.bindShortcut('meta+y', () => exec('redo')); | ||||
|     } | ||||
| } | ||||
|  | ||||
| function exec(cmd) { | ||||
|     document.execCommand(cmd); | ||||
|  | ||||
|     return false; | ||||
| } | ||||
|  | ||||
| export default { | ||||
|     init | ||||
| } | ||||
| @@ -38,7 +38,7 @@ function handleMessage(event) { | ||||
|     } | ||||
|  | ||||
|     if (message.type === 'sync') { | ||||
|         lastPingTs = new Date().getTime(); | ||||
|         lastPingTs = Date.now(); | ||||
|  | ||||
|         if (message.data.length > 0) { | ||||
|             console.debug(utils.now(), "Sync data: ", message.data); | ||||
| @@ -81,10 +81,10 @@ setTimeout(() => { | ||||
|     ws = connectWebSocket(); | ||||
|  | ||||
|     lastSyncId = glob.maxSyncIdAtLoad; | ||||
|     lastPingTs = new Date().getTime(); | ||||
|     lastPingTs = Date.now(); | ||||
|  | ||||
|     setInterval(async () => { | ||||
|         if (new Date().getTime() - lastPingTs > 30000) { | ||||
|         if (Date.now() - lastPingTs > 30000) { | ||||
|             console.log("Lost connection to server"); | ||||
|         } | ||||
|  | ||||
|   | ||||
| @@ -28,7 +28,7 @@ function clearText($el) { | ||||
| function showRecentNotes($el) { | ||||
|     $el.setSelectedPath(""); | ||||
|     $el.autocomplete("val", ""); | ||||
|     $el.autocomplete("open"); | ||||
|     $el.focus(); | ||||
| } | ||||
|  | ||||
| function initNoteAutocomplete($el, options) { | ||||
| @@ -61,7 +61,13 @@ function initNoteAutocomplete($el, options) { | ||||
|  | ||||
|         $clearTextButton.click(() => clearText($el)); | ||||
|  | ||||
|         $showRecentNotesButton.click(() => showRecentNotes($el)); | ||||
|         $showRecentNotesButton.click(e => { | ||||
|             showRecentNotes($el); | ||||
|  | ||||
|             // this will cause the click not give focus to the "show recent notes" button | ||||
|             // this is important because otherwise input will lose focus immediatelly and not show the results | ||||
|             return false; | ||||
|         }); | ||||
|  | ||||
|         $goToSelectedNoteButton.click(() => { | ||||
|             if ($el.hasClass("disabled")) { | ||||
| @@ -86,7 +92,9 @@ function initNoteAutocomplete($el, options) { | ||||
|                     suggestion: function(suggestion) { | ||||
|                         return suggestion.highlighted; | ||||
|                     } | ||||
|                 } | ||||
|                 }, | ||||
|                 // we can't cache identical searches because notes can be created / renamed, new recent notes can be added | ||||
|                 cache: false | ||||
|             } | ||||
|         ]); | ||||
|  | ||||
|   | ||||
| @@ -18,6 +18,7 @@ import noteDetailRelationMap from './note_detail_relation_map.js'; | ||||
| import bundleService from "./bundle.js"; | ||||
| import attributeService from "./attributes.js"; | ||||
| import utils from "./utils.js"; | ||||
| import importDialog from "../dialogs/import.js"; | ||||
|  | ||||
| const $noteTitle = $("#note-title"); | ||||
|  | ||||
| @@ -26,10 +27,10 @@ const $noteDetailComponents = $(".note-detail-component"); | ||||
| const $protectButton = $("#protect-button"); | ||||
| const $unprotectButton = $("#unprotect-button"); | ||||
| const $noteDetailWrapper = $("#note-detail-wrapper"); | ||||
| const $noteIdDisplay = $("#note-id-display"); | ||||
| const $childrenOverview = $("#children-overview"); | ||||
| const $scriptArea = $("#note-detail-script-area"); | ||||
| const $savedIndicator = $("#saved-indicator"); | ||||
| const $body = $("body"); | ||||
|  | ||||
| let currentNote = null; | ||||
|  | ||||
| @@ -116,7 +117,7 @@ async function saveNote() { | ||||
|     } | ||||
|  | ||||
|     note.title = $noteTitle.val(); | ||||
|     note.content = getCurrentNoteContent(note); | ||||
|     note.noteContent.content = getCurrentNoteContent(note); | ||||
|  | ||||
|     // it's important to set the flag back to false immediatelly after retrieving title and content | ||||
|     // otherwise we might overwrite another change (especially async code) | ||||
| @@ -131,6 +132,9 @@ async function saveNote() { | ||||
|     } | ||||
|  | ||||
|     $savedIndicator.fadeIn(); | ||||
|  | ||||
|     // run async | ||||
|     bundleService.executeRelationBundles(getCurrentNote(), 'runOnNoteChange'); | ||||
| } | ||||
|  | ||||
| async function saveNoteIfChanged() { | ||||
| @@ -142,11 +146,21 @@ async function saveNoteIfChanged() { | ||||
|     $savedIndicator.fadeIn(); | ||||
| } | ||||
|  | ||||
| function setNoteBackgroundIfProtected(note) { | ||||
|     $noteDetailWrapper.toggleClass("protected", note.isProtected); | ||||
|     $protectButton.toggleClass("active", note.isProtected); | ||||
|     $unprotectButton.toggleClass("active", !note.isProtected); | ||||
|     $unprotectButton.prop("disabled", !protectedSessionHolder.isProtectedSessionAvailable()); | ||||
| function updateNoteView() { | ||||
|     $noteDetailWrapper.toggleClass("protected", currentNote.isProtected); | ||||
|     $protectButton.toggleClass("active", currentNote.isProtected); | ||||
|     $protectButton.prop("disabled", currentNote.isProtected); | ||||
|     $unprotectButton.toggleClass("active", !currentNote.isProtected); | ||||
|     $unprotectButton.prop("disabled", !currentNote.isProtected || !protectedSessionHolder.isProtectedSessionAvailable()); | ||||
|  | ||||
|     for (const clazz of Array.from($body[0].classList)) { // create copy to safely iterate over while removing classes | ||||
|         if (clazz.startsWith("type-") || clazz.startsWith("mime-")) { | ||||
|             $body.removeClass(clazz); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     $body.addClass(utils.getNoteTypeClass(currentNote.type)); | ||||
|     $body.addClass(utils.getMimeTypeClass(currentNote.mime)); | ||||
| } | ||||
|  | ||||
| async function handleProtectedSession() { | ||||
| @@ -187,9 +201,7 @@ async function loadNoteDetail(noteId) { | ||||
|         attributeService.invalidateAttributes(); | ||||
|     } | ||||
|  | ||||
|     $noteIdDisplay.html(noteId); | ||||
|  | ||||
|     setNoteBackgroundIfProtected(currentNote); | ||||
|     updateNoteView(); | ||||
|  | ||||
|     $noteDetailWrapper.show(); | ||||
|  | ||||
| @@ -266,7 +278,7 @@ async function showChildrenOverview() { | ||||
|             text: await treeUtils.getNoteTitle(childBranch.noteId, childBranch.parentNoteId) | ||||
|         }).attr('data-action', 'note').attr('data-note-path', notePath + '/' + childBranch.noteId); | ||||
|  | ||||
|         const childEl = $('<div class="child-overview">').html(link); | ||||
|         const childEl = $('<div class="child-overview-item">').html(link); | ||||
|         $childrenOverview.append(childEl); | ||||
|     } | ||||
|  | ||||
| @@ -283,6 +295,10 @@ function focusOnTitle() { | ||||
|     $noteTitle.focus(); | ||||
| } | ||||
|  | ||||
| function focusAndSelectTitle() { | ||||
|     $noteTitle.focus().select(); | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Since detail loading may take some time and user might just browse through the notes using UP-DOWN keys, | ||||
|  * we intentionally decouple activation of the note in the tree and full load of the note so just avaiting on | ||||
| @@ -315,6 +331,20 @@ messagingService.subscribeToSyncMessages(syncData => { | ||||
|     } | ||||
| }); | ||||
|  | ||||
| $noteDetailWrapper.on("dragover", e => e.preventDefault()); | ||||
|  | ||||
| $noteDetailWrapper.on("dragleave", e => e.preventDefault()); | ||||
|  | ||||
| $noteDetailWrapper.on("drop", e => { | ||||
|     importDialog.uploadFiles(getCurrentNoteId(), e.originalEvent.dataTransfer.files, { | ||||
|         safeImport: true, | ||||
|         shrinkImages: true, | ||||
|         textImportedAsText: true, | ||||
|         codeImportedAsCode: true, | ||||
|         explodeArchives: true | ||||
|     }); | ||||
| }); | ||||
|  | ||||
| $(document).ready(() => { | ||||
|     $noteTitle.on('input', () => { | ||||
|         noteChanged(); | ||||
| @@ -336,16 +366,17 @@ setInterval(saveNoteIfChanged, 3000); | ||||
| export default { | ||||
|     reload, | ||||
|     switchToNote, | ||||
|     setNoteBackgroundIfProtected, | ||||
|     updateNoteView, | ||||
|     loadNote, | ||||
|     getCurrentNote, | ||||
|     getCurrentNoteContent, | ||||
|     getCurrentNoteType, | ||||
|     getCurrentNoteId, | ||||
|     focusOnTitle, | ||||
|     focusAndSelectTitle, | ||||
|     saveNote, | ||||
|     saveNoteIfChanged, | ||||
|     noteChanged, | ||||
|     getCurrentNoteContent, | ||||
|     onNoteChange, | ||||
|     addDetailLoadedListener | ||||
| }; | ||||
| @@ -49,7 +49,7 @@ async function show() { | ||||
|     // this needs to happen after the element is shown, otherwise the editor won't be refreshed | ||||
|     // CodeMirror breaks pretty badly on null so even though it shouldn't happen (guarded by consistency check) | ||||
|     // we provide fallback | ||||
|     codeEditor.setValue(currentNote.content || ""); | ||||
|     codeEditor.setValue(currentNote.noteContent.content || ""); | ||||
|  | ||||
|     const info = CodeMirror.findModeByMIME(currentNote.mime); | ||||
|  | ||||
|   | ||||
| @@ -5,6 +5,7 @@ import noteDetailService from "./note_detail.js"; | ||||
|  | ||||
| const $component = $('#note-detail-file'); | ||||
|  | ||||
| const $fileNoteId = $("#file-note-id"); | ||||
| const $fileName = $("#file-filename"); | ||||
| const $fileType = $("#file-filetype"); | ||||
| const $fileSize = $("#file-filesize"); | ||||
| @@ -21,12 +22,18 @@ async function show() { | ||||
|  | ||||
|     $component.show(); | ||||
|  | ||||
|     $fileNoteId.text(currentNote.noteId); | ||||
|     $fileName.text(attributeMap.originalFileName || "?"); | ||||
|     $fileSize.text((attributeMap.fileSize || "?") + " bytes"); | ||||
|     $fileType.text(currentNote.mime); | ||||
|  | ||||
|     $previewRow.toggle(!!currentNote.content); | ||||
|     $previewContent.text(currentNote.content); | ||||
|     if (currentNote.noteContent && currentNote.noteContent.content) { | ||||
|         $previewRow.show(); | ||||
|         $previewContent.text(currentNote.noteContent.content); | ||||
|     } | ||||
|     else { | ||||
|         $previewRow.hide(); | ||||
|     } | ||||
| } | ||||
|  | ||||
| $downloadButton.click(() => utils.download(getFileUrl())); | ||||
|   | ||||
| @@ -26,7 +26,7 @@ async function show() { | ||||
|     $fileSize.text((attributeMap.fileSize || "?") + " bytes"); | ||||
|     $fileType.text(currentNote.mime); | ||||
|  | ||||
|     $imageView.prop("src", `/api/images/${currentNote.noteId}/${currentNote.title}`); | ||||
|     $imageView.prop("src", `api/images/${currentNote.noteId}/${currentNote.title}`); | ||||
| } | ||||
|  | ||||
| $imageDownloadButton.click(() => utils.download(getFileUrl())); | ||||
|   | ||||
| @@ -93,9 +93,9 @@ function loadMapData() { | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     if (currentNote.content) { | ||||
|     if (currentNote.noteContent.content) { | ||||
|         try { | ||||
|             mapData = JSON.parse(currentNote.content); | ||||
|             mapData = JSON.parse(currentNote.noteContent.content); | ||||
|         } catch (e) { | ||||
|             console.log("Could not parse content: ", e); | ||||
|         } | ||||
|   | ||||
| @@ -1,19 +1,18 @@ | ||||
| import noteDetailService from "./note_detail.js"; | ||||
| import treeService from "./tree.js"; | ||||
| import infoService from './info.js'; | ||||
|  | ||||
| const $searchString = $("#search-string"); | ||||
| const $component = $('#note-detail-search'); | ||||
|  | ||||
| function getContent() { | ||||
|     JSON.stringify({ | ||||
|         searchString: $searchString.val() | ||||
|     }); | ||||
| } | ||||
| const $refreshButton = $('#note-detail-search-refresh-results-button'); | ||||
|  | ||||
| function show() { | ||||
|     $component.show(); | ||||
|  | ||||
|     console.log(noteDetailService.getCurrentNote()); | ||||
|  | ||||
|     try { | ||||
|         const json = JSON.parse(noteDetailService.getCurrentNote().content); | ||||
|         const json = JSON.parse(noteDetailService.getCurrentNote().noteContent.content); | ||||
|  | ||||
|         $searchString.val(json.searchString); | ||||
|     } | ||||
| @@ -25,6 +24,20 @@ function show() { | ||||
|     $searchString.on('input', noteDetailService.noteChanged); | ||||
| } | ||||
|  | ||||
| function getContent() { | ||||
|     return JSON.stringify({ | ||||
|         searchString: $searchString.val() | ||||
|     }); | ||||
| } | ||||
|  | ||||
| $refreshButton.click(async () => { | ||||
|     await noteDetailService.saveNoteIfChanged(); | ||||
|  | ||||
|     treeService.reload(); | ||||
|  | ||||
|     infoService.showMessage('Tree has been refreshed.'); | ||||
| }); | ||||
|  | ||||
| export default { | ||||
|     getContent, | ||||
|     show, | ||||
|   | ||||
| @@ -1,6 +1,7 @@ | ||||
| import libraryLoader from "./library_loader.js"; | ||||
| import noteDetailService from './note_detail.js'; | ||||
| import treeService from './tree.js'; | ||||
| import attributeService from "./attributes.js"; | ||||
|  | ||||
| const $component = $('#note-detail-text'); | ||||
|  | ||||
| @@ -19,7 +20,9 @@ async function show() { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     textEditor.setData(noteDetailService.getCurrentNote().content); | ||||
|     textEditor.isReadOnly = await isReadOnly(); | ||||
|  | ||||
|     textEditor.setData(noteDetailService.getCurrentNote().noteContent.content); | ||||
|  | ||||
|     $component.show(); | ||||
| } | ||||
| @@ -36,6 +39,12 @@ function getContent() { | ||||
|     return content; | ||||
| } | ||||
|  | ||||
| async function isReadOnly() { | ||||
|     const attributes = await attributeService.getAttributes(); | ||||
|  | ||||
|     return attributes.some(attr => attr.type === 'label' && attr.name === 'readOnly'); | ||||
| } | ||||
|  | ||||
| function focus() { | ||||
|     $component.focus(); | ||||
| } | ||||
|   | ||||
| @@ -111,18 +111,18 @@ async function renderTooltip(note, attributes) { | ||||
|     } | ||||
|  | ||||
|     if (note.type === 'text') { | ||||
|         // surround with <div> for a case when note.content is pure text (e.g. "[protected]") which | ||||
|         // surround with <div> for a case when note's content is pure text (e.g. "[protected]") which | ||||
|         // then fails the jquery non-empty text test | ||||
|         content += '<div>' + note.content + '</div>'; | ||||
|         content += '<div>' + note.noteContent.content + '</div>'; | ||||
|     } | ||||
|     else if (note.type === 'code') { | ||||
|         content += $("<pre>") | ||||
|             .text(note.content) | ||||
|             .text(note.noteContent.content) | ||||
|             .prop('outerHTML'); | ||||
|     } | ||||
|     else if (note.type === 'image') { | ||||
|         content += $("<img>") | ||||
|             .prop("src", `/api/images/${note.noteId}/${note.title}`) | ||||
|             .prop("src", `api/images/${note.noteId}/${note.title}`) | ||||
|             .prop('outerHTML'); | ||||
|     } | ||||
|     // other types of notes don't have tooltip preview | ||||
|   | ||||
| @@ -2,6 +2,7 @@ import treeService from './tree.js'; | ||||
| import noteDetailService from './note_detail.js'; | ||||
| import server from './server.js'; | ||||
| import infoService from "./info.js"; | ||||
| import confirmDialog from "../dialogs/confirm.js"; | ||||
|  | ||||
| const $executeScriptButton = $("#execute-script-button"); | ||||
| const $toggleEditButton = $('#toggle-edit-button'); | ||||
| @@ -110,35 +111,63 @@ function NoteTypeModel() { | ||||
|         self.updateExecuteScriptButtonVisibility(); | ||||
|     } | ||||
|  | ||||
|     this.selectText = function() { | ||||
|     function confirmChangeIfContent() { | ||||
|         if (!noteDetailService.getCurrentNoteContent()) { | ||||
|             return true; | ||||
|         } | ||||
|  | ||||
|         return confirmDialog.confirm("It is not recommended to change note type when note content is not empty. Do you want to continue anyway?"); | ||||
|     } | ||||
|  | ||||
|     this.selectText = async function() { | ||||
|         if (!await confirmChangeIfContent()) { | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         self.type('text'); | ||||
|         self.mime(''); | ||||
|         self.mime('text/html'); | ||||
|  | ||||
|         save(); | ||||
|     }; | ||||
|  | ||||
|     this.selectRender = function() { | ||||
|     this.selectRender = async function() { | ||||
|         if (!await confirmChangeIfContent()) { | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         self.type('render'); | ||||
|         self.mime('text/html'); | ||||
|  | ||||
|         save(); | ||||
|     }; | ||||
|  | ||||
|     this.selectRelationMap = function() { | ||||
|     this.selectRelationMap = async function() { | ||||
|         if (!await confirmChangeIfContent()) { | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         self.type('relation-map'); | ||||
|         self.mime('application/json'); | ||||
|  | ||||
|         save(); | ||||
|     }; | ||||
|  | ||||
|     this.selectCode = function() { | ||||
|     this.selectCode = async function() { | ||||
|         if (!await confirmChangeIfContent()) { | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         self.type('code'); | ||||
|         self.mime(''); | ||||
|         self.mime('text/plain'); | ||||
|  | ||||
|         save(); | ||||
|     }; | ||||
|  | ||||
|     this.selectCodeMime = function(el) { | ||||
|     this.selectCodeMime = async function(el) { | ||||
|         if (!await confirmChangeIfContent()) { | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         self.type('code'); | ||||
|         self.mime(el.mime); | ||||
|  | ||||
|   | ||||
| @@ -125,7 +125,7 @@ async function protectNoteAndSendToServer() { | ||||
|  | ||||
|     treeService.setProtected(note.noteId, note.isProtected); | ||||
|  | ||||
|     noteDetailService.setNoteBackgroundIfProtected(note); | ||||
|     noteDetailService.updateNoteView(); | ||||
| } | ||||
|  | ||||
| async function unprotectNoteAndSendToServer() { | ||||
| @@ -152,7 +152,7 @@ async function unprotectNoteAndSendToServer() { | ||||
|  | ||||
|     treeService.setProtected(currentNote.noteId, currentNote.isProtected); | ||||
|  | ||||
|     noteDetailService.setNoteBackgroundIfProtected(currentNote); | ||||
|     noteDetailService.updateNoteView(); | ||||
| } | ||||
|  | ||||
| async function protectSubtree(noteId, protect) { | ||||
|   | ||||
| @@ -8,7 +8,7 @@ let protectedSessionId = null; | ||||
| optionsInitService.optionsReady.then(options => protectedSessionTimeout = options.protectedSessionTimeout); | ||||
|  | ||||
| setInterval(() => { | ||||
|     if (lastProtectedSessionOperationDate !== null && new Date().getTime() - lastProtectedSessionOperationDate.getTime() > protectedSessionTimeout * 1000) { | ||||
|     if (lastProtectedSessionOperationDate !== null && Date.now() - lastProtectedSessionOperationDate.getTime() > protectedSessionTimeout * 1000) { | ||||
|         resetProtectedSession(); | ||||
|     } | ||||
| }, 5000); | ||||
|   | ||||
| @@ -1,9 +1,13 @@ | ||||
| import FrontendScriptApi from './frontend_script_api.js'; | ||||
| import utils from './utils.js'; | ||||
| import treeCache from './tree_cache.js'; | ||||
|  | ||||
| function ScriptContext(startNote, allNotes, originEntity = null) { | ||||
| async function ScriptContext(startNoteId, allNoteIds, originEntity = null) { | ||||
|     const modules = {}; | ||||
|  | ||||
|     const startNote = await treeCache.getNote(startNoteId); | ||||
|     const allNotes = await treeCache.getNotes(allNoteIds); | ||||
|  | ||||
|     return { | ||||
|         modules: modules, | ||||
|         notes: utils.toObject(allNotes, note => [note.noteId, note]), | ||||
|   | ||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user