Compare commits
	
		
			315 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | 3972c27e7a | ||
|  | 14cffbbe62 | ||
|  | 599c3c04af | ||
|  | f1412b631d | ||
|  | 41908050bb | ||
|  | f07033423c | ||
|  | daf96fcbf2 | ||
|  | 2bca94529e | ||
|  | b2c9a0da21 | ||
|  | 7a9542b4fc | ||
|  | 3a95c9e1bc | ||
|  | 3d2ef6be01 | ||
|  | d67246699a | ||
|  | 14c704d6db | ||
|  | 4c8eeb2e6f | ||
|  | c1b245c8b1 | ||
|  | 74202d67bb | ||
|  | 26066f39b1 | ||
|  | b255cf190c | ||
|  | bc77b143b0 | ||
|  | 9f0ff6ae7a | ||
|  | 736704c7d6 | ||
|  | 654c116c58 | ||
|  | 89a5cab98f | ||
|  | c39d0be8cd | ||
|  | e75b4cd848 | ||
|  | 378e8f35e5 | ||
|  | bdb5e2f13f | ||
|  | 8211bed449 | ||
|  | b243632483 | ||
|  | e4d2513451 | ||
|  | 385144451b | ||
|  | c8c533844e | ||
|  | 0e69f0c079 | ||
|  | aee60c444f | ||
|  | e7a504c66b | ||
|  | 45d9c7164c | ||
|  | bd913a63a8 | ||
|  | 5a1938c078 | ||
|  | 015cd68756 | ||
|  | 76c0e5b2b8 | ||
|  | 0f8f707acd | ||
|  | 083cccea28 | ||
|  | 31b76b23ce | ||
|  | af529f82e5 | ||
|  | fc6669d254 | ||
|  | c07785be67 | ||
|  | 80d2457b23 | ||
|  | 5dde2752d2 | ||
|  | 8bf4633cd0 | ||
|  | bd66b8a1c8 | ||
|  | be51e533fc | ||
|  | f47ae12019 | ||
|  | cab54a458f | ||
|  | a30734f1bc | ||
|  | 7ad9f7b129 | ||
|  | 40a32e6826 | ||
|  | ab0486aaf1 | ||
|  | 874593a167 | ||
|  | 03bf33630e | ||
|  | 933cce1b94 | ||
|  | 4a6ff573f8 | ||
|  | 1a737f7d19 | ||
|  | cb69914f09 | ||
|  | a372cbb2df | ||
|  | 0ce5caefe8 | ||
|  | 94dabb81f6 | ||
|  | cd45bcfd03 | ||
|  | 49a53f7a45 | ||
|  | 9fa6c0918c | ||
|  | e8d089e37e | ||
|  | a931ce25fa | ||
|  | b507abb4f7 | ||
|  | 66e7c6de62 | ||
|  | 4ce5ea9886 | ||
|  | 8c54b62f07 | ||
|  | 85eb50ed0f | ||
|  | 5ffd621e9d | ||
|  | df93cb09da | ||
|  | bbf04209f0 | ||
|  | 834bfa39c7 | ||
|  | 52b445f70b | ||
|  | 7b9b4fbb0c | ||
|  | 5af0ba1fcb | ||
|  | 85a9748291 | ||
|  | b4005a7ffe | ||
|  | 82de1c88d4 | ||
|  | 1687ed7e0b | ||
|  | c8b9c7d936 | ||
|  | d57057ba28 | ||
|  | 66cee8daa4 | ||
|  | afd7df0942 | ||
|  | bd6ae33d32 | ||
|  | 70660a0d68 | ||
|  | cdad18551a | ||
|  | 592c51d1a5 | ||
|  | 6a57b8a7e7 | ||
|  | 7a94e21c54 | ||
|  | 5b43f321e2 | ||
|  | a4eafb934f | ||
|  | 7b59a665dd | ||
|  | 3d15450ffc | ||
|  | b0c6d52461 | ||
|  | 2dc16dd29f | ||
|  | d8924c536b | ||
|  | 3ebbf2cc46 | ||
|  | f4079604c9 | ||
|  | 1f96a6beab | ||
|  | b277a250e5 | ||
|  | 5b0e1a644d | ||
|  | 6bb3cfa9a3 | ||
|  | 9720868f5a | ||
|  | 8d8ee2a87a | ||
|  | 542e82ee5d | ||
|  | 0104b19502 | ||
|  | 120888b53e | ||
|  | d2e2caed62 | ||
|  | 63066802a8 | ||
|  | 6128bb4ff3 | ||
|  | 982796255d | ||
|  | 36b15f474d | ||
|  | 13f71f8967 | ||
|  | 64336ffbee | ||
|  | b09463d1b2 | ||
|  | b5e6f46b9c | ||
|  | 08af4a0465 | ||
|  | 8c5df6321f | ||
|  | d19f044961 | ||
|  | e378d9f645 | ||
|  | 39dc0f71b4 | ||
|  | 0cef5c6b8c | ||
|  | 9b5a44cef4 | ||
|  | 29769ed91d | ||
|  | 867d794e17 | ||
|  | fdd8458336 | ||
|  | a0bec22e96 | ||
|  | 5aeb5cd214 | ||
|  | e827ddffb9 | ||
|  | 98f80998b9 | ||
|  | 69727d0b12 | ||
|  | 84faf32b98 | ||
|  | 6ed6e27602 | ||
|  | fb54678fef | ||
|  | 2cdcb3af12 | ||
|  | cf7a336ac2 | ||
|  | abfc64af95 | ||
|  | 42dd8d4754 | ||
|  | a4e64350e9 | ||
|  | 6f567e3e10 | ||
|  | c6c76ba360 | ||
|  | 429d3f518e | ||
|  | 26e4ad9bf9 | ||
|  | 6ab0cea4e3 | ||
|  | 277368ab43 | ||
|  | e2921a648d | ||
|  | c765dbc5cf | ||
|  | a066c6fe2b | ||
|  | 311952d4dd | ||
|  | 96dab5d51e | ||
|  | 15d951b04e | ||
|  | 8ba830c04b | ||
|  | acc82f39c4 | ||
|  | fad0ec757b | ||
|  | c9d73c6115 | ||
|  | ab2f28ceef | ||
|  | 87e415992c | ||
|  | 12439d8761 | ||
|  | 4f200c73dc | ||
|  | 5f7e74e15c | ||
|  | e8a5d0ae16 | ||
|  | 088fb00ca9 | ||
|  | 05676f3459 | ||
|  | 5d203b2278 | ||
|  | 795d50f02e | ||
|  | cfe0ae1eda | ||
|  | aa57a64c61 | ||
|  | e36a81e189 | ||
|  | 88c07a9e48 | ||
|  | bfd9f292a6 | ||
|  | 9edee9340b | ||
|  | 8550ed72f2 | ||
|  | efffc29649 | ||
|  | 0ec909fd7a | ||
|  | b10b0048f3 | ||
|  | 9bb188b519 | ||
|  | 7464835058 | ||
|  | 913b6bb6f6 | ||
|  | 000cf99546 | ||
|  | c918267750 | ||
|  | 68921ee59b | ||
|  | 7e856283ee | ||
|  | 9c1b8da573 | ||
|  | cb39b9cca8 | ||
|  | 788ac43ad1 | ||
|  | 57d19f3302 | ||
|  | 68bba623b6 | ||
|  | 35998058ce | ||
|  | cdf94181d2 | ||
|  | 91ee90d827 | ||
|  | d3316cd09c | ||
|  | ac1b06967f | ||
|  | 47eb1e3e02 | ||
|  | a69d8737ce | ||
|  | 341f47f0f2 | ||
|  | 19c605a9a8 | ||
|  | 54e4f54678 | ||
|  | 297a2cd9da | ||
|  | d746d707b5 | ||
|  | 299252b650 | ||
|  | fddd1c278f | ||
|  | f52d7e3c28 | ||
|  | a699210a29 | ||
|  | b3c32a39e9 | ||
|  | df27533b66 | ||
|  | b96a1274c5 | ||
|  | 001a5107dd | ||
|  | c8e456cdb1 | ||
|  | 0f6b00e1c8 | ||
|  | 5ea060a054 | ||
|  | 95bb2cf0bb | ||
|  | 4c472ce78b | ||
|  | 511fb89af0 | ||
|  | 7e524c0cd1 | ||
|  | e3e2dc9fff | ||
|  | 1612e9093d | ||
|  | f8649feea4 | ||
|  | ac978c3fa7 | ||
|  | efcc804149 | ||
|  | db514e8f41 | ||
|  | 9c32f66329 | ||
|  | 0fd5102a26 | ||
|  | 840af15dae | ||
|  | f1b0b3bcdb | ||
|  | e5c0acbb43 | ||
|  | 834661c461 | ||
|  | 5204ab5a7e | ||
|  | 74862536a8 | ||
|  | a24f1f5b95 | ||
|  | 0be76f746a | ||
|  | fad89ff63f | ||
|  | b8ae791191 | ||
|  | ce754cbd91 | ||
|  | 0fdb6af98a | ||
|  | f6c7f6a0f2 | ||
|  | 354999f37a | ||
|  | 348c622845 | ||
|  | 44bcdedaba | ||
|  | 755c0f3ce2 | ||
|  | 895bda41b5 | ||
|  | b2df622cb6 | ||
|  | 9ba6e6d0f5 | ||
|  | a5c9180533 | ||
|  | e86f1e0d05 | ||
|  | b6277049f3 | ||
|  | c831221cc4 | ||
|  | 577a168714 | ||
|  | b0bd27321a | ||
|  | 90c5348ca7 | ||
|  | 8e95b080da | ||
|  | 766a567a32 | ||
|  | 6d0218cb36 | ||
|  | d26170762b | ||
|  | b3209a9bbf | ||
|  | 61c2456cf6 | ||
|  | 1c6fc9029f | ||
|  | 5c91e38dfe | ||
|  | 07bf075894 | ||
|  | ddce5c959e | ||
|  | 3b9d1df05c | ||
|  | d239ef2956 | ||
|  | 7a865a9081 | ||
|  | 83d6c2970f | ||
|  | 8c7d159012 | ||
|  | d169f67901 | ||
|  | 982b723647 | ||
|  | 31d5ac05ff | ||
|  | 72d91d1571 | ||
|  | f4b57f4c57 | ||
|  | ee0833390a | ||
|  | 2acff07368 | ||
|  | bea1d24f07 | ||
|  | adc270c59f | ||
|  | 66064f7a94 | ||
|  | 1501fa8dbf | ||
|  | 60bba46d80 | ||
|  | 12c06ae97e | ||
|  | f0bea9cf71 | ||
|  | a555b6319c | ||
|  | 5dd93e4cdc | ||
|  | 3b4509d833 | ||
|  | 19308bbfbd | ||
|  | 4acc5432c3 | ||
|  | 08b8141fdf | ||
|  | e1200aa308 | ||
|  | 89666eb078 | ||
|  | d5605aa64d | ||
|  | 2582b016f9 | ||
|  | e8c52e25f0 | ||
|  | a149c6a105 | ||
|  | 131af9ab12 | ||
|  | aa2bbc6575 | ||
|  | 78e8c15786 | ||
|  | fda4146150 | ||
|  | ddc885066e | ||
|  | 08bc2afb49 | ||
|  | 1d0220b03d | ||
|  | 3033f7cc08 | ||
|  | 6b9ff47c88 | ||
|  | fd02c6102d | ||
|  | 30c712a6be | ||
|  | 3928c96640 | ||
|  | d86f655658 | ||
|  | abdad1c3ae | ||
|  | 9e5f1a0a87 | ||
|  | cdde6a4d8e | 
							
								
								
									
										4
									
								
								.dockerignore
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,4 @@ | ||||
| node_modules | ||||
| npm-debug.log | ||||
| dist | ||||
| .idea | ||||
							
								
								
									
										14
									
								
								.idea/dataSources.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,14 @@ | ||||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <project version="4"> | ||||
|   <component name="DataSourceManagerImpl" format="xml" multifile-model="true"> | ||||
|     <data-source source="LOCAL" name="document.db" uuid="a2c75661-f9e2-478f-a69f-6a9409e69997"> | ||||
|       <driver-ref>sqlite.xerial</driver-ref> | ||||
|       <synchronize>true</synchronize> | ||||
|       <jdbc-driver>org.sqlite.JDBC</jdbc-driver> | ||||
|       <jdbc-url>jdbc:sqlite:$USER_HOME$/trilium-data/document.db</jdbc-url> | ||||
|       <driver-properties> | ||||
|         <property name="enable_load_extension" value="true" /> | ||||
|       </driver-properties> | ||||
|     </data-source> | ||||
|   </component> | ||||
| </project> | ||||
							
								
								
									
										678
									
								
								.idea/dataSources/a2c75661-f9e2-478f-a69f-6a9409e69997.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,678 @@ | ||||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <dataSource name="document.db"> | ||||
|   <database-model serializer="dbm" rdbms="SQLITE" format-version="4.9"> | ||||
|     <root id="1"> | ||||
|       <ServerVersion>3.16.1</ServerVersion> | ||||
|     </root> | ||||
|     <schema id="2" parent="1" name="main"> | ||||
|       <Current>1</Current> | ||||
|       <Visible>1</Visible> | ||||
|     </schema> | ||||
|     <collation id="3" parent="1" name="BINARY"/> | ||||
|     <collation id="4" parent="1" name="NOCASE"/> | ||||
|     <collation id="5" parent="1" name="RTRIM"/> | ||||
|     <table id="6" parent="2" name="api_tokens"/> | ||||
|     <table id="7" parent="2" name="branches"/> | ||||
|     <table id="8" parent="2" name="event_log"/> | ||||
|     <table id="9" parent="2" name="images"/> | ||||
|     <table id="10" parent="2" name="labels"/> | ||||
|     <table id="11" parent="2" name="note_images"/> | ||||
|     <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="18" parent="2" name="sqlite_sequence"> | ||||
|       <System>1</System> | ||||
|     </table> | ||||
|     <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="21" parent="6" name="token"> | ||||
|       <Position>2</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|     </column> | ||||
|     <column id="22" parent="6" name="dateCreated"> | ||||
|       <Position>3</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|     </column> | ||||
|     <column id="23" parent="6" name="isDeleted"> | ||||
|       <Position>4</Position> | ||||
|       <DataType>INT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|       <DefaultExpression>0</DefaultExpression> | ||||
|     </column> | ||||
|     <column id="24" parent="6" name="hash"> | ||||
|       <Position>5</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|       <DefaultExpression>""</DefaultExpression> | ||||
|     </column> | ||||
|     <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="26" parent="6"> | ||||
|       <ColNames>apiTokenId</ColNames> | ||||
|       <Primary>1</Primary> | ||||
|       <UnderlyingIndexName>sqlite_autoindex_api_tokens_1</UnderlyingIndexName> | ||||
|     </key> | ||||
|     <column id="27" parent="7" name="branchId"> | ||||
|       <Position>1</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|     </column> | ||||
|     <column id="28" parent="7" name="noteId"> | ||||
|       <Position>2</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|     </column> | ||||
|     <column id="29" parent="7" name="parentNoteId"> | ||||
|       <Position>3</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|     </column> | ||||
|     <column id="30" parent="7" name="notePosition"> | ||||
|       <Position>4</Position> | ||||
|       <DataType>INTEGER|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|     </column> | ||||
|     <column id="31" parent="7" name="prefix"> | ||||
|       <Position>5</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|     </column> | ||||
|     <column id="32" parent="7" name="isExpanded"> | ||||
|       <Position>6</Position> | ||||
|       <DataType>BOOLEAN|0s</DataType> | ||||
|     </column> | ||||
|     <column id="33" parent="7" name="isDeleted"> | ||||
|       <Position>7</Position> | ||||
|       <DataType>INTEGER|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|       <DefaultExpression>0</DefaultExpression> | ||||
|     </column> | ||||
|     <column id="34" parent="7" name="dateModified"> | ||||
|       <Position>8</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|     </column> | ||||
|     <column id="35" parent="7" name="hash"> | ||||
|       <Position>9</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|       <DefaultExpression>""</DefaultExpression> | ||||
|     </column> | ||||
|     <column id="36" parent="7" name="dateCreated"> | ||||
|       <Position>10</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|       <DefaultExpression>'1970-01-01T00:00:00.000Z'</DefaultExpression> | ||||
|     </column> | ||||
|     <index id="37" parent="7" name="sqlite_autoindex_branches_1"> | ||||
|       <NameSurrogate>1</NameSurrogate> | ||||
|       <ColNames>branchId</ColNames> | ||||
|       <ColumnCollations></ColumnCollations> | ||||
|       <Unique>1</Unique> | ||||
|     </index> | ||||
|     <index id="38" parent="7" name="IDX_branches_noteId_parentNoteId"> | ||||
|       <ColNames>noteId | ||||
| parentNoteId</ColNames> | ||||
|       <ColumnCollations></ColumnCollations> | ||||
|     </index> | ||||
|     <index id="39" parent="7" name="IDX_branches_noteId"> | ||||
|       <ColNames>noteId</ColNames> | ||||
|       <ColumnCollations></ColumnCollations> | ||||
|     </index> | ||||
|     <index id="40" parent="7" name="IDX_branches_parentNoteId"> | ||||
|       <ColNames>parentNoteId</ColNames> | ||||
|       <ColumnCollations></ColumnCollations> | ||||
|     </index> | ||||
|     <key id="41" parent="7"> | ||||
|       <ColNames>branchId</ColNames> | ||||
|       <Primary>1</Primary> | ||||
|       <UnderlyingIndexName>sqlite_autoindex_branches_1</UnderlyingIndexName> | ||||
|     </key> | ||||
|     <column id="42" parent="8" name="eventId"> | ||||
|       <Position>1</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|     </column> | ||||
|     <column id="43" parent="8" name="noteId"> | ||||
|       <Position>2</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|     </column> | ||||
|     <column id="44" parent="8" name="comment"> | ||||
|       <Position>3</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|     </column> | ||||
|     <column id="45" parent="8" name="dateCreated"> | ||||
|       <Position>4</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|     </column> | ||||
|     <index id="46" parent="8" name="sqlite_autoindex_event_log_1"> | ||||
|       <NameSurrogate>1</NameSurrogate> | ||||
|       <ColNames>eventId</ColNames> | ||||
|       <ColumnCollations></ColumnCollations> | ||||
|       <Unique>1</Unique> | ||||
|     </index> | ||||
|     <key id="47" parent="8"> | ||||
|       <ColNames>eventId</ColNames> | ||||
|       <Primary>1</Primary> | ||||
|       <UnderlyingIndexName>sqlite_autoindex_event_log_1</UnderlyingIndexName> | ||||
|     </key> | ||||
|     <column id="48" parent="9" name="imageId"> | ||||
|       <Position>1</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|     </column> | ||||
|     <column id="49" parent="9" name="format"> | ||||
|       <Position>2</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|     </column> | ||||
|     <column id="50" parent="9" name="checksum"> | ||||
|       <Position>3</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|     </column> | ||||
|     <column id="51" parent="9" name="name"> | ||||
|       <Position>4</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|     </column> | ||||
|     <column id="52" parent="9" name="data"> | ||||
|       <Position>5</Position> | ||||
|       <DataType>BLOB|0s</DataType> | ||||
|     </column> | ||||
|     <column id="53" parent="9" name="isDeleted"> | ||||
|       <Position>6</Position> | ||||
|       <DataType>INT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|       <DefaultExpression>0</DefaultExpression> | ||||
|     </column> | ||||
|     <column id="54" parent="9" name="dateModified"> | ||||
|       <Position>7</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|     </column> | ||||
|     <column id="55" parent="9" name="dateCreated"> | ||||
|       <Position>8</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|     </column> | ||||
|     <column id="56" parent="9" name="hash"> | ||||
|       <Position>9</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|       <DefaultExpression>""</DefaultExpression> | ||||
|     </column> | ||||
|     <index id="57" parent="9" name="sqlite_autoindex_images_1"> | ||||
|       <NameSurrogate>1</NameSurrogate> | ||||
|       <ColNames>imageId</ColNames> | ||||
|       <ColumnCollations></ColumnCollations> | ||||
|       <Unique>1</Unique> | ||||
|     </index> | ||||
|     <key id="58" parent="9"> | ||||
|       <ColNames>imageId</ColNames> | ||||
|       <Primary>1</Primary> | ||||
|       <UnderlyingIndexName>sqlite_autoindex_images_1</UnderlyingIndexName> | ||||
|     </key> | ||||
|     <column id="59" parent="10" name="labelId"> | ||||
|       <Position>1</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|     </column> | ||||
|     <column id="60" parent="10" name="noteId"> | ||||
|       <Position>2</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|     </column> | ||||
|     <column id="61" parent="10" name="name"> | ||||
|       <Position>3</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|     </column> | ||||
|     <column id="62" parent="10" name="value"> | ||||
|       <Position>4</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|       <DefaultExpression>''</DefaultExpression> | ||||
|     </column> | ||||
|     <column id="63" parent="10" name="position"> | ||||
|       <Position>5</Position> | ||||
|       <DataType>INT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|       <DefaultExpression>0</DefaultExpression> | ||||
|     </column> | ||||
|     <column id="64" parent="10" name="dateCreated"> | ||||
|       <Position>6</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|     </column> | ||||
|     <column id="65" parent="10" name="dateModified"> | ||||
|       <Position>7</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|     </column> | ||||
|     <column id="66" parent="10" name="isDeleted"> | ||||
|       <Position>8</Position> | ||||
|       <DataType>INT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|     </column> | ||||
|     <column id="67" parent="10" name="hash"> | ||||
|       <Position>9</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|       <DefaultExpression>""</DefaultExpression> | ||||
|     </column> | ||||
|     <index id="68" parent="10" name="sqlite_autoindex_labels_1"> | ||||
|       <NameSurrogate>1</NameSurrogate> | ||||
|       <ColNames>labelId</ColNames> | ||||
|       <ColumnCollations></ColumnCollations> | ||||
|       <Unique>1</Unique> | ||||
|     </index> | ||||
|     <index id="69" parent="10" name="IDX_labels_noteId"> | ||||
|       <ColNames>noteId</ColNames> | ||||
|       <ColumnCollations></ColumnCollations> | ||||
|     </index> | ||||
|     <index id="70" parent="10" name="IDX_labels_name_value"> | ||||
|       <ColNames>name | ||||
| value</ColNames> | ||||
|       <ColumnCollations></ColumnCollations> | ||||
|     </index> | ||||
|     <key id="71" parent="10"> | ||||
|       <ColNames>labelId</ColNames> | ||||
|       <Primary>1</Primary> | ||||
|       <UnderlyingIndexName>sqlite_autoindex_labels_1</UnderlyingIndexName> | ||||
|     </key> | ||||
|     <column id="72" parent="11" name="noteImageId"> | ||||
|       <Position>1</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|     </column> | ||||
|     <column id="73" parent="11" name="noteId"> | ||||
|       <Position>2</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|     </column> | ||||
|     <column id="74" parent="11" name="imageId"> | ||||
|       <Position>3</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|     </column> | ||||
|     <column id="75" parent="11" name="isDeleted"> | ||||
|       <Position>4</Position> | ||||
|       <DataType>INT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|       <DefaultExpression>0</DefaultExpression> | ||||
|     </column> | ||||
|     <column id="76" parent="11" name="dateModified"> | ||||
|       <Position>5</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|     </column> | ||||
|     <column id="77" parent="11" name="dateCreated"> | ||||
|       <Position>6</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|     </column> | ||||
|     <column id="78" parent="11" name="hash"> | ||||
|       <Position>7</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|       <DefaultExpression>""</DefaultExpression> | ||||
|     </column> | ||||
|     <index id="79" parent="11" name="sqlite_autoindex_note_images_1"> | ||||
|       <NameSurrogate>1</NameSurrogate> | ||||
|       <ColNames>noteImageId</ColNames> | ||||
|       <ColumnCollations></ColumnCollations> | ||||
|       <Unique>1</Unique> | ||||
|     </index> | ||||
|     <index id="80" parent="11" name="IDX_note_images_noteId_imageId"> | ||||
|       <ColNames>noteId | ||||
| imageId</ColNames> | ||||
|       <ColumnCollations></ColumnCollations> | ||||
|     </index> | ||||
|     <index id="81" parent="11" name="IDX_note_images_noteId"> | ||||
|       <ColNames>noteId</ColNames> | ||||
|       <ColumnCollations></ColumnCollations> | ||||
|     </index> | ||||
|     <index id="82" parent="11" name="IDX_note_images_imageId"> | ||||
|       <ColNames>imageId</ColNames> | ||||
|       <ColumnCollations></ColumnCollations> | ||||
|     </index> | ||||
|     <key id="83" parent="11"> | ||||
|       <ColNames>noteImageId</ColNames> | ||||
|       <Primary>1</Primary> | ||||
|       <UnderlyingIndexName>sqlite_autoindex_note_images_1</UnderlyingIndexName> | ||||
|     </key> | ||||
|     <column id="84" parent="12" name="noteRevisionId"> | ||||
|       <Position>1</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|     </column> | ||||
|     <column id="85" parent="12" name="noteId"> | ||||
|       <Position>2</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|     </column> | ||||
|     <column id="86" parent="12" name="title"> | ||||
|       <Position>3</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|     </column> | ||||
|     <column id="87" parent="12" name="content"> | ||||
|       <Position>4</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|     </column> | ||||
|     <column id="88" parent="12" name="isProtected"> | ||||
|       <Position>5</Position> | ||||
|       <DataType>INT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|       <DefaultExpression>0</DefaultExpression> | ||||
|     </column> | ||||
|     <column id="89" parent="12" name="dateModifiedFrom"> | ||||
|       <Position>6</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|     </column> | ||||
|     <column id="90" parent="12" name="dateModifiedTo"> | ||||
|       <Position>7</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|     </column> | ||||
|     <column id="91" parent="12" name="type"> | ||||
|       <Position>8</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|       <DefaultExpression>''</DefaultExpression> | ||||
|     </column> | ||||
|     <column id="92" parent="12" name="mime"> | ||||
|       <Position>9</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|       <DefaultExpression>''</DefaultExpression> | ||||
|     </column> | ||||
|     <column id="93" parent="12" name="hash"> | ||||
|       <Position>10</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|       <DefaultExpression>""</DefaultExpression> | ||||
|     </column> | ||||
|     <index id="94" parent="12" name="sqlite_autoindex_note_revisions_1"> | ||||
|       <NameSurrogate>1</NameSurrogate> | ||||
|       <ColNames>noteRevisionId</ColNames> | ||||
|       <ColumnCollations></ColumnCollations> | ||||
|       <Unique>1</Unique> | ||||
|     </index> | ||||
|     <index id="95" parent="12" name="IDX_note_revisions_noteId"> | ||||
|       <ColNames>noteId</ColNames> | ||||
|       <ColumnCollations></ColumnCollations> | ||||
|     </index> | ||||
|     <index id="96" parent="12" name="IDX_note_revisions_dateModifiedFrom"> | ||||
|       <ColNames>dateModifiedFrom</ColNames> | ||||
|       <ColumnCollations></ColumnCollations> | ||||
|     </index> | ||||
|     <index id="97" parent="12" name="IDX_note_revisions_dateModifiedTo"> | ||||
|       <ColNames>dateModifiedTo</ColNames> | ||||
|       <ColumnCollations></ColumnCollations> | ||||
|     </index> | ||||
|     <key id="98" parent="12"> | ||||
|       <ColNames>noteRevisionId</ColNames> | ||||
|       <Primary>1</Primary> | ||||
|       <UnderlyingIndexName>sqlite_autoindex_note_revisions_1</UnderlyingIndexName> | ||||
|     </key> | ||||
|     <column id="99" parent="13" name="noteId"> | ||||
|       <Position>1</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|     </column> | ||||
|     <column id="100" parent="13" name="title"> | ||||
|       <Position>2</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|       <DefaultExpression>"unnamed"</DefaultExpression> | ||||
|     </column> | ||||
|     <column id="101" parent="13" name="content"> | ||||
|       <Position>3</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|       <DefaultExpression>""</DefaultExpression> | ||||
|     </column> | ||||
|     <column id="102" parent="13" name="isProtected"> | ||||
|       <Position>4</Position> | ||||
|       <DataType>INT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|       <DefaultExpression>0</DefaultExpression> | ||||
|     </column> | ||||
|     <column id="103" parent="13" name="isDeleted"> | ||||
|       <Position>5</Position> | ||||
|       <DataType>INT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|       <DefaultExpression>0</DefaultExpression> | ||||
|     </column> | ||||
|     <column id="104" parent="13" name="dateCreated"> | ||||
|       <Position>6</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|     </column> | ||||
|     <column id="105" parent="13" name="dateModified"> | ||||
|       <Position>7</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|     </column> | ||||
|     <column id="106" parent="13" name="type"> | ||||
|       <Position>8</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|       <DefaultExpression>'text'</DefaultExpression> | ||||
|     </column> | ||||
|     <column id="107" parent="13" name="mime"> | ||||
|       <Position>9</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|       <DefaultExpression>'text/html'</DefaultExpression> | ||||
|     </column> | ||||
|     <column id="108" parent="13" name="hash"> | ||||
|       <Position>10</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|       <DefaultExpression>""</DefaultExpression> | ||||
|     </column> | ||||
|     <index id="109" parent="13" name="sqlite_autoindex_notes_1"> | ||||
|       <NameSurrogate>1</NameSurrogate> | ||||
|       <ColNames>noteId</ColNames> | ||||
|       <ColumnCollations></ColumnCollations> | ||||
|       <Unique>1</Unique> | ||||
|     </index> | ||||
|     <index id="110" parent="13" name="IDX_notes_type"> | ||||
|       <ColNames>type</ColNames> | ||||
|       <ColumnCollations></ColumnCollations> | ||||
|     </index> | ||||
|     <key id="111" parent="13"> | ||||
|       <ColNames>noteId</ColNames> | ||||
|       <Primary>1</Primary> | ||||
|       <UnderlyingIndexName>sqlite_autoindex_notes_1</UnderlyingIndexName> | ||||
|     </key> | ||||
|     <column id="112" parent="14" name="optionId"> | ||||
|       <Position>1</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|     </column> | ||||
|     <column id="113" parent="14" name="name"> | ||||
|       <Position>2</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|     </column> | ||||
|     <column id="114" parent="14" name="value"> | ||||
|       <Position>3</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|     </column> | ||||
|     <column id="115" parent="14" name="dateModified"> | ||||
|       <Position>4</Position> | ||||
|       <DataType>INT|0s</DataType> | ||||
|     </column> | ||||
|     <column id="116" parent="14" name="isSynced"> | ||||
|       <Position>5</Position> | ||||
|       <DataType>INTEGER|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|       <DefaultExpression>0</DefaultExpression> | ||||
|     </column> | ||||
|     <column id="117" parent="14" name="hash"> | ||||
|       <Position>6</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|       <DefaultExpression>""</DefaultExpression> | ||||
|     </column> | ||||
|     <column id="118" parent="14" name="dateCreated"> | ||||
|       <Position>7</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|       <DefaultExpression>'1970-01-01T00:00:00.000Z'</DefaultExpression> | ||||
|     </column> | ||||
|     <index id="119" parent="14" name="sqlite_autoindex_options_1"> | ||||
|       <NameSurrogate>1</NameSurrogate> | ||||
|       <ColNames>optionId</ColNames> | ||||
|       <ColumnCollations></ColumnCollations> | ||||
|       <Unique>1</Unique> | ||||
|     </index> | ||||
|     <key id="120" parent="14"> | ||||
|       <ColNames>optionId</ColNames> | ||||
|       <Primary>1</Primary> | ||||
|       <UnderlyingIndexName>sqlite_autoindex_options_1</UnderlyingIndexName> | ||||
|     </key> | ||||
|     <column id="121" parent="15" name="branchId"> | ||||
|       <Position>1</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|     </column> | ||||
|     <column id="122" parent="15" name="notePath"> | ||||
|       <Position>2</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|     </column> | ||||
|     <column id="123" parent="15" name="hash"> | ||||
|       <Position>3</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|       <DefaultExpression>""</DefaultExpression> | ||||
|     </column> | ||||
|     <column id="124" parent="15" name="dateCreated"> | ||||
|       <Position>4</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|     </column> | ||||
|     <column id="125" parent="15" name="isDeleted"> | ||||
|       <Position>5</Position> | ||||
|       <DataType>INT|0s</DataType> | ||||
|     </column> | ||||
|     <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="127" parent="15"> | ||||
|       <ColNames>branchId</ColNames> | ||||
|       <Primary>1</Primary> | ||||
|       <UnderlyingIndexName>sqlite_autoindex_recent_notes_1</UnderlyingIndexName> | ||||
|     </key> | ||||
|     <column id="128" parent="16" name="sourceId"> | ||||
|       <Position>1</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|     </column> | ||||
|     <column id="129" parent="16" name="dateCreated"> | ||||
|       <Position>2</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|     </column> | ||||
|     <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="131" parent="16"> | ||||
|       <ColNames>sourceId</ColNames> | ||||
|       <Primary>1</Primary> | ||||
|       <UnderlyingIndexName>sqlite_autoindex_source_ids_1</UnderlyingIndexName> | ||||
|     </key> | ||||
|     <column id="132" parent="17" name="type"> | ||||
|       <Position>1</Position> | ||||
|       <DataType>text|0s</DataType> | ||||
|     </column> | ||||
|     <column id="133" parent="17" name="name"> | ||||
|       <Position>2</Position> | ||||
|       <DataType>text|0s</DataType> | ||||
|     </column> | ||||
|     <column id="134" parent="17" name="tbl_name"> | ||||
|       <Position>3</Position> | ||||
|       <DataType>text|0s</DataType> | ||||
|     </column> | ||||
|     <column id="135" parent="17" name="rootpage"> | ||||
|       <Position>4</Position> | ||||
|       <DataType>integer|0s</DataType> | ||||
|     </column> | ||||
|     <column id="136" parent="17" name="sql"> | ||||
|       <Position>5</Position> | ||||
|       <DataType>text|0s</DataType> | ||||
|     </column> | ||||
|     <column id="137" parent="18" name="name"> | ||||
|       <Position>1</Position> | ||||
|     </column> | ||||
|     <column id="138" parent="18" name="seq"> | ||||
|       <Position>2</Position> | ||||
|     </column> | ||||
|     <column id="139" parent="19" name="id"> | ||||
|       <Position>1</Position> | ||||
|       <DataType>INTEGER|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|       <SequenceIdentity>1</SequenceIdentity> | ||||
|     </column> | ||||
|     <column id="140" parent="19" name="entityName"> | ||||
|       <Position>2</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|     </column> | ||||
|     <column id="141" parent="19" name="entityId"> | ||||
|       <Position>3</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|     </column> | ||||
|     <column id="142" parent="19" name="sourceId"> | ||||
|       <Position>4</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|     </column> | ||||
|     <column id="143" parent="19" name="syncDate"> | ||||
|       <Position>5</Position> | ||||
|       <DataType>TEXT|0s</DataType> | ||||
|       <NotNull>1</NotNull> | ||||
|     </column> | ||||
|     <index id="144" parent="19" name="IDX_sync_entityName_entityId"> | ||||
|       <ColNames>entityName | ||||
| entityId</ColNames> | ||||
|       <ColumnCollations></ColumnCollations> | ||||
|       <Unique>1</Unique> | ||||
|     </index> | ||||
|     <index id="145" parent="19" name="IDX_sync_syncDate"> | ||||
|       <ColNames>syncDate</ColNames> | ||||
|       <ColumnCollations></ColumnCollations> | ||||
|     </index> | ||||
|     <key id="146" parent="19"> | ||||
|       <ColNames>id</ColNames> | ||||
|       <Primary>1</Primary> | ||||
|     </key> | ||||
|   </database-model> | ||||
| </dataSource> | ||||
							
								
								
									
										2
									
								
								.idea/dataSources/a2c75661-f9e2-478f-a69f-6a9409e69997/storage_v2/_src_/schema/main.uQUzAA.meta
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,2 @@ | ||||
| #n:main | ||||
| !<md> [0, 0, null, null, -2147483648, -2147483648] | ||||
							
								
								
									
										10
									
								
								.idea/inspectionProfiles/Project_Default.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,10 @@ | ||||
| <component name="InspectionProjectProfileManager"> | ||||
|   <profile version="1.0"> | ||||
|     <option name="myName" value="Project Default" /> | ||||
|     <inspection_tool class="SpellCheckingInspection" enabled="false" level="TYPO" enabled_by_default="false"> | ||||
|       <option name="processCode" value="true" /> | ||||
|       <option name="processLiterals" value="true" /> | ||||
|       <option name="processComments" value="true" /> | ||||
|     </inspection_tool> | ||||
|   </profile> | ||||
| </component> | ||||
							
								
								
									
										9
									
								
								.idea/jsLinters/jslint.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,9 @@ | ||||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <project version="4"> | ||||
|   <component name="JSLintConfiguration"> | ||||
|     <option devel="true" /> | ||||
|     <option es6="true" /> | ||||
|     <option maxerr="50" /> | ||||
|     <option node="true" /> | ||||
|   </component> | ||||
| </project> | ||||
							
								
								
									
										9
									
								
								.idea/misc.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,9 @@ | ||||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <project version="4"> | ||||
|   <component name="JavaScriptSettings"> | ||||
|     <option name="languageLevel" value="ES6" /> | ||||
|   </component> | ||||
|   <component name="ProjectRootManager"> | ||||
|     <output url="file://$PROJECT_DIR$/out" /> | ||||
|   </component> | ||||
| </project> | ||||
							
								
								
									
										8
									
								
								.idea/modules.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,8 @@ | ||||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <project version="4"> | ||||
|   <component name="ProjectModuleManager"> | ||||
|     <modules> | ||||
|       <module fileurl="file://$PROJECT_DIR$/trilium.iml" filepath="$PROJECT_DIR$/trilium.iml" /> | ||||
|     </modules> | ||||
|   </component> | ||||
| </project> | ||||
							
								
								
									
										6
									
								
								.idea/vcs.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,6 @@ | ||||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <project version="4"> | ||||
|   <component name="VcsDirectoryMappings"> | ||||
|     <mapping directory="" vcs="Git" /> | ||||
|   </component> | ||||
| </project> | ||||
							
								
								
									
										21
									
								
								Dockerfile
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,21 @@ | ||||
| FROM node:8.11.2 | ||||
|  | ||||
| RUN apt-get update && apt-get install -y nasm | ||||
|  | ||||
| # Create app directory | ||||
| WORKDIR /usr/src/app | ||||
|  | ||||
| # Install app dependencies | ||||
| # A wildcard is used to ensure both package.json AND package-lock.json are copied | ||||
| # where available (npm@5+) | ||||
| COPY package*.json ./ | ||||
|  | ||||
| RUN npm install --production | ||||
| # If you are building your code for production | ||||
| # RUN npm install --only=production | ||||
|  | ||||
| # Bundle app source | ||||
| COPY . . | ||||
|  | ||||
| EXPOSE 8080 | ||||
| CMD [ "node", "src/www" ] | ||||
| @@ -1,4 +1,6 @@ | ||||
| # Trilium Notes | ||||
|  | ||||
| [](https://gitter.im/trilium-notes/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) | ||||
| Trilium Notes is a hierarchical note taking application. Picture tells a thousand words: | ||||
|  | ||||
|  | ||||
| @@ -10,7 +12,7 @@ Trilium Notes is a hierarchical note taking application. Picture tells a thousan | ||||
| * WYSIWYG (What You See Is What You Get) editing | ||||
| * Fast and easy [navigation between notes](https://github.com/zadam/trilium/wiki/Note-navigation) | ||||
| * Seamless note versioning | ||||
| * Note attributes can be used to tag/label notes as an alternative note organization and querying | ||||
| * Note labels can be used to tag/label notes as an alternative note organization and querying | ||||
| * Can be deployed as web application and / or desktop application with offline access (electron based) | ||||
| * [Synchronization with](https://github.com/zadam/trilium/wiki/Synchronization) self-hosted sync server | ||||
| * Strong [note encryption](https://github.com/zadam/trilium/wiki/Protected-notes) | ||||
| @@ -35,11 +37,12 @@ List of documentation pages: | ||||
| * [Installation as webapp](https://github.com/zadam/trilium/wiki/Installation-as-webapp) | ||||
| * [Note navigation](https://github.com/zadam/trilium/wiki/Note-navigation) | ||||
| * [Tree manipulation](https://github.com/zadam/trilium/wiki/Tree-manipulation) | ||||
| * [Attributes](https://github.com/zadam/trilium/wiki/Attributes) | ||||
| * [Labels](https://github.com/zadam/trilium/wiki/Labels) | ||||
| * [Links](https://github.com/zadam/trilium/wiki/Links) | ||||
| * [Cloning notes](https://github.com/zadam/trilium/wiki/Cloning-notes) | ||||
| * [Protected notes](https://github.com/zadam/trilium/wiki/Protected-notes) | ||||
| * [Synchronization](https://github.com/zadam/trilium/wiki/Synchronization) | ||||
| * [Document](https://github.com/zadam/trilium/wiki/Document) | ||||
| * [Theming](https://github.com/zadam/trilium/wiki/Theming) | ||||
| * [Keyboard shortcuts](https://github.com/zadam/trilium/wiki/Keyboard-shortcuts) | ||||
| * [Troubleshooting](https://github.com/zadam/trilium/wiki/Troubleshooting) | ||||
|   | ||||
							
								
								
									
										8
									
								
								bin/build-docker.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						| @@ -0,0 +1,8 @@ | ||||
| #!/usr/bin/env bash | ||||
|  | ||||
| if [[ $# -eq 0 ]] ; then | ||||
|     echo "Missing argument of new version" | ||||
|     exit 1 | ||||
| fi | ||||
|  | ||||
| sudo docker build -t zadam/trilium:latest -t zadam/trilium:$1 . | ||||
							
								
								
									
										9
									
								
								bin/push-docker-image.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,9 @@ | ||||
| #!/usr/bin/env bash | ||||
|  | ||||
| if [[ $# -eq 0 ]] ; then | ||||
|     echo "Missing argument of new version" | ||||
|     exit 1 | ||||
| fi | ||||
|  | ||||
| sudo docker push zadam/trilium:latest | ||||
| sudo docker push zadam/trilium:$1 | ||||
| @@ -24,9 +24,9 @@ jq '.version = "'$VERSION'"' package.json|sponge package.json | ||||
|  | ||||
| git add package.json | ||||
|  | ||||
| echo 'module.exports = { build_date:"'`date --iso-8601=seconds`'", build_revision: "'`git log -1 --format="%H"`'" };' > services/build.js | ||||
| echo 'module.exports = { buildDate:"'`date --iso-8601=seconds`'", buildRevision: "'`git log -1 --format="%H"`'" };' > src/services/build.js | ||||
|  | ||||
| git add services/build.js | ||||
| git add src/services/build.js | ||||
|  | ||||
| TAG=v$VERSION | ||||
|  | ||||
| @@ -47,6 +47,7 @@ bin/package.sh | ||||
| LINUX_X64_BUILD=trilium-linux-x64-$VERSION.7z | ||||
| LINUX_IA32_BUILD=trilium-linux-ia32-$VERSION.7z | ||||
| WINDOWS_X64_BUILD=trilium-windows-x64-$VERSION.7z | ||||
| SERVER_BUILD=trilium-linux-x64-server.elf | ||||
|  | ||||
| echo "Creating release in GitHub" | ||||
|  | ||||
| @@ -75,4 +76,21 @@ github-release upload \ | ||||
|     --name "$WINDOWS_X64_BUILD" \ | ||||
|     --file "dist/$WINDOWS_X64_BUILD" | ||||
|  | ||||
| echo "Packaging server version" | ||||
|  | ||||
| npm run build-pkg | ||||
|  | ||||
| github-release upload \ | ||||
|     --tag $TAG \ | ||||
|     --name "$SERVER_BUILD" \ | ||||
|     --file "dist/$SERVER_BUILD" | ||||
|  | ||||
| echo "Building docker image" | ||||
|  | ||||
| bin/build-docker.sh $VERSION | ||||
|  | ||||
| echo "Pushing docker image to dockerhub" | ||||
|  | ||||
| bin/push-docker-image.sh $VERSION | ||||
|  | ||||
| echo "Release finished!" | ||||
| @@ -1,3 +1,7 @@ | ||||
| [General] | ||||
| # Instance name can be used to distinguish between different instances | ||||
| instanceName= | ||||
|  | ||||
| [Network] | ||||
| port=8080 | ||||
| # true for TLS/SSL/HTTPS (secure), false for HTTP (unsecure). | ||||
|   | ||||
							
								
								
									
										53
									
								
								db/main_branches.sql
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,53 @@ | ||||
| INSERT INTO branches (branchId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('root', 'root', 'none', 0, null, 1, 0, '2018-01-01T00:00:00.000Z'); | ||||
| INSERT INTO branches (branchId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('dLgtLUFn3GoN', '1Heh2acXfPNt', 'root', 21, null, 1, 0, '2017-12-23T00:46:39.304Z'); | ||||
| INSERT INTO branches (branchId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('QLfS835GSfIh', '3RkyK9LI18dO', '1Heh2acXfPNt', 1, null, 1, 0, '2017-12-23T01:20:04.181Z'); | ||||
| INSERT INTO branches (branchId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('QJAcYJ1gGUh9', 'L1Ox40M1aEyy', '3RkyK9LI18dO', 0, null, 0, 0, '2017-12-23T01:20:45.365Z'); | ||||
| INSERT INTO branches (branchId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('wLTa2l3lYi83', 'HJusZTbBU494', '3RkyK9LI18dO', 2, null, 1, 0, '2017-12-23T01:20:50.709Z'); | ||||
| INSERT INTO branches (branchId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('yMhwsE7uvEij', '3oldoiMUPOlr', 'HJusZTbBU494', 1, null, 1, 0, '2017-12-23T01:20:55.775Z'); | ||||
| INSERT INTO branches (branchId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('EjQTcVVHFmmZ', 'MG0wntwILQW6', '3oldoiMUPOlr', 1, null, 1, 0, '2017-12-23T01:21:10.517Z'); | ||||
| INSERT INTO branches (branchId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('jvhKcwz4pYTr', 'ZC78NlmdXeC6', 'WdWZFuWNVDZk', 0, null, 1, 0, '2017-12-23T04:06:21.579Z'); | ||||
| INSERT INTO branches (branchId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('CarTrwkGVcPz', 'NncfGH8dyNjJ', 'WdWZFuWNVDZk', 1, null, 0, 0, '2017-12-23T04:06:24.012Z'); | ||||
| INSERT INTO branches (branchId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('6M7qPlr7at6N', 'eouCLkjbruai', 'NncfGH8dyNjJ', 0, null, 0, 0, '2017-12-23T01:23:28.291Z'); | ||||
| INSERT INTO branches (branchId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('tQgognnAH9WI', 'C44aq4mkaX67', 'NncfGH8dyNjJ', 1, null, 0, 0, '2017-12-23T01:23:31.879Z'); | ||||
| INSERT INTO branches (branchId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('xyAi7MmgvAgR', 'C44aq4mkaX67', 'ZC78NlmdXeC6', 1, null, 0, 0, '2017-12-23T01:23:47.756Z'); | ||||
| INSERT INTO branches (branchId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('xQ3fjRp9yaPq', 'I6Cw88AirBBl', 'C44aq4mkaX67', 0, null, 0, 0, '2017-12-23T01:24:04.681Z'); | ||||
| INSERT INTO branches (branchId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('2GOsNT5LsvTP', 'mcEwFMSjhlvL', 'C44aq4mkaX67', 1, null, 0, 0, '2017-12-23T01:29:35.974Z'); | ||||
| INSERT INTO branches (branchId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('RxUiraiR655R', 'CF2lUIJAr6Ey', 'NncfGH8dyNjJ', 2, null, 0, 0, '2017-12-23T01:34:37.658Z'); | ||||
| INSERT INTO branches (branchId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('mZuSrZ18Zmv0', 'xkXwueRoDNeN', 'MG0wntwILQW6', 0, null, 0, 0, '2017-12-23T01:35:40.306Z'); | ||||
| INSERT INTO branches (branchId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('hbcWTnEnXPwF', 'eXHZAKsMYgur', '1Heh2acXfPNt', 3, null, 1, 0, '2017-12-23T03:32:42.868Z'); | ||||
| INSERT INTO branches (branchId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('8a3aNxjG0nu7', '2WU27ekfy07E', 'eXHZAKsMYgur', 0, null, 0, 0, '2017-12-23T03:32:49.379Z'); | ||||
| INSERT INTO branches (branchId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('4Tu6vaPdCxCM', 'TjWEndYCCg7g', 'eXHZAKsMYgur', 1, null, 0, 0, '2017-12-23T03:33:23.584Z'); | ||||
| INSERT INTO branches (branchId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('lBPOmhP12egP', '8nRNDJGyGs2Z', 'TjWEndYCCg7g', 0, null, 0, 0, '2017-12-23T03:33:37.327Z'); | ||||
| INSERT INTO branches (branchId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('C5ipVqeDWySp', '9zSwD89vgzNO', '8nRNDJGyGs2Z', 0, null, 0, 0, '2017-12-23T03:37:04.912Z'); | ||||
| INSERT INTO branches (branchId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('uSitzbGcSATJ', 'u5t1EvWa3CMO', 'TjWEndYCCg7g', 1, null, 0, 0, '2017-12-23T03:39:21.918Z'); | ||||
| INSERT INTO branches (branchId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('GZ6aRI8rdSJt', '8nRNDJGyGs2Z', 'MG0wntwILQW6', 1, '', 0, 0, '2017-12-23T03:42:28.310Z'); | ||||
| INSERT INTO branches (branchId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('HsN4600rQoL9', 'Iha4YwchR413', '3oldoiMUPOlr', 0, null, 1, 0, '2017-12-23T03:44:30.945Z'); | ||||
| INSERT INTO branches (branchId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('uipfvAfwWRgx', '6ZuXjCSWgjB4', 'HJusZTbBU494', 0, null, 0, 0, '2017-12-23T03:44:54.096Z'); | ||||
| INSERT INTO branches (branchId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('nMRpPWWH8WRk', 'GpGnjmcAPeWG', '6ZuXjCSWgjB4', 0, null, 1, 0, '2017-12-23T03:44:57.036Z'); | ||||
| INSERT INTO branches (branchId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('c4wt27WNjepw', '21K84UqGhqlt', 'GpGnjmcAPeWG', 0, null, 0, 0, '2017-12-23T03:45:10.933Z'); | ||||
| INSERT INTO branches (branchId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('0fpnraUGs9Kl', 'rz5t0r9Qr2WC', 'HJusZTbBU494', 2, null, 1, 0, '2017-12-23T03:45:20.914Z'); | ||||
| INSERT INTO branches (branchId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('d8L8zYlLTbym', 'R6pheWjdwmNU', 'rz5t0r9Qr2WC', 0, null, 1, 0, '2017-12-23T03:45:28.002Z'); | ||||
| INSERT INTO branches (branchId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('T4USGzfllu5t', '5v5Dx6LMHXIO', 'Iha4YwchR413', 0, null, 0, 0, '2017-12-23T03:45:44.184Z'); | ||||
| INSERT INTO branches (branchId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('c4JgFNIobvQW', 'MLQjmREtcnJ3', 'R6pheWjdwmNU', 0, null, 0, 0, '2017-12-23T03:47:48.208Z'); | ||||
| INSERT INTO branches (branchId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('nfWjptAU2ZDg', 'pTTjrxgnvURB', 'R6pheWjdwmNU', 1, null, 0, 0, '2017-12-23T03:47:55.932Z'); | ||||
| INSERT INTO branches (branchId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('T2ToYBfyPy0g', 'cFK9sGYZaMWs', 'rz5t0r9Qr2WC', 1, null, 0, 0, '2017-12-23T03:49:32.210Z'); | ||||
| INSERT INTO branches (branchId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('NG4gbKOnsM3v', '21K84UqGhqlt', 'MLQjmREtcnJ3', 0, '28. 11. 2017', 0, 0, '2017-12-23T03:53:38.110Z'); | ||||
| INSERT INTO branches (branchId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('Fstg4tkccO4N', '5v5Dx6LMHXIO', 'MLQjmREtcnJ3', 1, '21. 12. 2017', 0, 0, '2017-12-23T03:53:49.737Z'); | ||||
| INSERT INTO branches (branchId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('MN8B7qXDUViO', 'xkXwueRoDNeN', 'MLQjmREtcnJ3', 2, '22. 12. 2017', 0, 0, '2017-12-23T03:53:57.486Z'); | ||||
| INSERT INTO branches (branchId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('gSRkHpB7Bu3D', 'pOFVzbXLmzhX', 'R6pheWjdwmNU', 2, null, 0, 0, '2017-12-23T03:54:46.138Z'); | ||||
| INSERT INTO branches (branchId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('6brdjeWDOB6w', '0xtvjqrcGiRB', 'ZC78NlmdXeC6', 0, null, 0, 0, '2017-12-23T04:02:06.650Z'); | ||||
| INSERT INTO branches (branchId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('AqKUM2zUVFUF', 'Zl69uXBSen0w', 'ZC78NlmdXeC6', 2, null, 1, 0, '2017-12-23T04:02:16.685Z'); | ||||
| INSERT INTO branches (branchId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('Ez7NN2WVzRc4', '62BKAQMVP2KW', 'Zl69uXBSen0w', 1, null, 0, 0, '2017-12-23T04:02:39.164Z'); | ||||
| INSERT INTO branches (branchId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('t3vVElqMIQVa', 'h4OfLEAYspud', 'WdWZFuWNVDZk', 2, null, 1, 0, '2017-12-23T04:06:25.769Z'); | ||||
| INSERT INTO branches (branchId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('O983DHtLpgmr', '1hASbLRDL7oo', 'h4OfLEAYspud', 0, null, 0, 0, '2017-12-23T16:42:26.347Z'); | ||||
| INSERT INTO branches (branchId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('RsvL795Mk1bp', '1hASbLRDL7oo', 'GpGnjmcAPeWG', 1, '', 0, 0, '2017-12-23T04:04:56.830Z'); | ||||
| INSERT INTO branches (branchId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('79e4hrHLFmx6', 'jyqG9GucsMdn', 'Iha4YwchR413', 1, null, 0, 0, '2017-12-23T04:05:16.439Z'); | ||||
| INSERT INTO branches (branchId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('oWO8rctUjf7d', 'WdWZFuWNVDZk', '1Heh2acXfPNt', 5, null, 1, 0, '2017-12-23T04:06:16.179Z'); | ||||
| INSERT INTO branches (branchId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('GOxcrZrxalFN', 'yK4SBJfwD3tY', '1Heh2acXfPNt', 8, null, 1, 0, '2017-12-23T04:06:32.833Z'); | ||||
| INSERT INTO branches (branchId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('bSPmEvjLzQKU', 'r4BnsmSQeVr1', 'yK4SBJfwD3tY', 0, null, 0, 0, '2017-12-23T04:06:37.427Z'); | ||||
| INSERT INTO branches (branchId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('bMtxCD6cwNR9', 'QbL3pTvhgzM8', 'yK4SBJfwD3tY', 2, null, 0, 0, '2017-12-23T04:06:43.841Z'); | ||||
| INSERT INTO branches (branchId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('o4ycR7xIi4oI', 'moMbTKwN15Ps', 'yK4SBJfwD3tY', 3, null, 1, 0, '2017-12-23T04:06:49.331Z'); | ||||
| INSERT INTO branches (branchId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('abTEhnOsAsSg', 'PEGQGg0In3Ar', 'GpGnjmcAPeWG', 2, null, 0, 0, '2017-12-23T16:44:35.900Z'); | ||||
| INSERT INTO branches (branchId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('bryQseMhyzaI', 'IlULcDiOTI4K', '1Heh2acXfPNt', 0, null, 0, 0, '2017-12-23T18:04:26.439Z'); | ||||
| INSERT INTO branches (branchId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('ccslPJf3wQV3', 'vBv6ovBupfTj', 'IlULcDiOTI4K', 0, null, 0, 0, '2017-12-23T18:04:50.904Z'); | ||||
| INSERT INTO branches (branchId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('5Dt9YCMn59sY', 'mw4f2xB4J5fV', 'IlULcDiOTI4K', 1, null, 0, 0, '2017-12-23T18:05:24.868Z'); | ||||
| @@ -1,52 +0,0 @@ | ||||
| INSERT INTO note_tree (noteTreeId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('dLgtLUFn3GoN', '1Heh2acXfPNt', 'root', 21, null, 1, 0, '2017-12-23T00:46:39.304Z'); | ||||
| INSERT INTO note_tree (noteTreeId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('QLfS835GSfIh', '3RkyK9LI18dO', '1Heh2acXfPNt', 1, null, 1, 0, '2017-12-23T01:20:04.181Z'); | ||||
| INSERT INTO note_tree (noteTreeId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('QJAcYJ1gGUh9', 'L1Ox40M1aEyy', '3RkyK9LI18dO', 0, null, 0, 0, '2017-12-23T01:20:45.365Z'); | ||||
| INSERT INTO note_tree (noteTreeId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('wLTa2l3lYi83', 'HJusZTbBU494', '3RkyK9LI18dO', 2, null, 1, 0, '2017-12-23T01:20:50.709Z'); | ||||
| INSERT INTO note_tree (noteTreeId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('yMhwsE7uvEij', '3oldoiMUPOlr', 'HJusZTbBU494', 1, null, 1, 0, '2017-12-23T01:20:55.775Z'); | ||||
| INSERT INTO note_tree (noteTreeId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('EjQTcVVHFmmZ', 'MG0wntwILQW6', '3oldoiMUPOlr', 1, null, 1, 0, '2017-12-23T01:21:10.517Z'); | ||||
| INSERT INTO note_tree (noteTreeId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('jvhKcwz4pYTr', 'ZC78NlmdXeC6', 'WdWZFuWNVDZk', 0, null, 1, 0, '2017-12-23T04:06:21.579Z'); | ||||
| INSERT INTO note_tree (noteTreeId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('CarTrwkGVcPz', 'NncfGH8dyNjJ', 'WdWZFuWNVDZk', 1, null, 0, 0, '2017-12-23T04:06:24.012Z'); | ||||
| INSERT INTO note_tree (noteTreeId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('6M7qPlr7at6N', 'eouCLkjbruai', 'NncfGH8dyNjJ', 0, null, 0, 0, '2017-12-23T01:23:28.291Z'); | ||||
| INSERT INTO note_tree (noteTreeId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('tQgognnAH9WI', 'C44aq4mkaX67', 'NncfGH8dyNjJ', 1, null, 0, 0, '2017-12-23T01:23:31.879Z'); | ||||
| INSERT INTO note_tree (noteTreeId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('xyAi7MmgvAgR', 'C44aq4mkaX67', 'ZC78NlmdXeC6', 1, null, 0, 0, '2017-12-23T01:23:47.756Z'); | ||||
| INSERT INTO note_tree (noteTreeId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('xQ3fjRp9yaPq', 'I6Cw88AirBBl', 'C44aq4mkaX67', 0, null, 0, 0, '2017-12-23T01:24:04.681Z'); | ||||
| INSERT INTO note_tree (noteTreeId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('2GOsNT5LsvTP', 'mcEwFMSjhlvL', 'C44aq4mkaX67', 1, null, 0, 0, '2017-12-23T01:29:35.974Z'); | ||||
| INSERT INTO note_tree (noteTreeId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('RxUiraiR655R', 'CF2lUIJAr6Ey', 'NncfGH8dyNjJ', 2, null, 0, 0, '2017-12-23T01:34:37.658Z'); | ||||
| INSERT INTO note_tree (noteTreeId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('mZuSrZ18Zmv0', 'xkXwueRoDNeN', 'MG0wntwILQW6', 0, null, 0, 0, '2017-12-23T01:35:40.306Z'); | ||||
| INSERT INTO note_tree (noteTreeId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('hbcWTnEnXPwF', 'eXHZAKsMYgur', '1Heh2acXfPNt', 3, null, 1, 0, '2017-12-23T03:32:42.868Z'); | ||||
| INSERT INTO note_tree (noteTreeId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('8a3aNxjG0nu7', '2WU27ekfy07E', 'eXHZAKsMYgur', 0, null, 0, 0, '2017-12-23T03:32:49.379Z'); | ||||
| INSERT INTO note_tree (noteTreeId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('4Tu6vaPdCxCM', 'TjWEndYCCg7g', 'eXHZAKsMYgur', 1, null, 0, 0, '2017-12-23T03:33:23.584Z'); | ||||
| INSERT INTO note_tree (noteTreeId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('lBPOmhP12egP', '8nRNDJGyGs2Z', 'TjWEndYCCg7g', 0, null, 0, 0, '2017-12-23T03:33:37.327Z'); | ||||
| INSERT INTO note_tree (noteTreeId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('C5ipVqeDWySp', '9zSwD89vgzNO', '8nRNDJGyGs2Z', 0, null, 0, 0, '2017-12-23T03:37:04.912Z'); | ||||
| INSERT INTO note_tree (noteTreeId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('uSitzbGcSATJ', 'u5t1EvWa3CMO', 'TjWEndYCCg7g', 1, null, 0, 0, '2017-12-23T03:39:21.918Z'); | ||||
| INSERT INTO note_tree (noteTreeId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('GZ6aRI8rdSJt', '8nRNDJGyGs2Z', 'MG0wntwILQW6', 1, '', 0, 0, '2017-12-23T03:42:28.310Z'); | ||||
| INSERT INTO note_tree (noteTreeId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('HsN4600rQoL9', 'Iha4YwchR413', '3oldoiMUPOlr', 0, null, 1, 0, '2017-12-23T03:44:30.945Z'); | ||||
| INSERT INTO note_tree (noteTreeId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('uipfvAfwWRgx', '6ZuXjCSWgjB4', 'HJusZTbBU494', 0, null, 0, 0, '2017-12-23T03:44:54.096Z'); | ||||
| INSERT INTO note_tree (noteTreeId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('nMRpPWWH8WRk', 'GpGnjmcAPeWG', '6ZuXjCSWgjB4', 0, null, 1, 0, '2017-12-23T03:44:57.036Z'); | ||||
| INSERT INTO note_tree (noteTreeId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('c4wt27WNjepw', '21K84UqGhqlt', 'GpGnjmcAPeWG', 0, null, 0, 0, '2017-12-23T03:45:10.933Z'); | ||||
| INSERT INTO note_tree (noteTreeId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('0fpnraUGs9Kl', 'rz5t0r9Qr2WC', 'HJusZTbBU494', 2, null, 1, 0, '2017-12-23T03:45:20.914Z'); | ||||
| INSERT INTO note_tree (noteTreeId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('d8L8zYlLTbym', 'R6pheWjdwmNU', 'rz5t0r9Qr2WC', 0, null, 1, 0, '2017-12-23T03:45:28.002Z'); | ||||
| INSERT INTO note_tree (noteTreeId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('T4USGzfllu5t', '5v5Dx6LMHXIO', 'Iha4YwchR413', 0, null, 0, 0, '2017-12-23T03:45:44.184Z'); | ||||
| INSERT INTO note_tree (noteTreeId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('c4JgFNIobvQW', 'MLQjmREtcnJ3', 'R6pheWjdwmNU', 0, null, 0, 0, '2017-12-23T03:47:48.208Z'); | ||||
| INSERT INTO note_tree (noteTreeId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('nfWjptAU2ZDg', 'pTTjrxgnvURB', 'R6pheWjdwmNU', 1, null, 0, 0, '2017-12-23T03:47:55.932Z'); | ||||
| INSERT INTO note_tree (noteTreeId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('T2ToYBfyPy0g', 'cFK9sGYZaMWs', 'rz5t0r9Qr2WC', 1, null, 0, 0, '2017-12-23T03:49:32.210Z'); | ||||
| INSERT INTO note_tree (noteTreeId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('NG4gbKOnsM3v', '21K84UqGhqlt', 'MLQjmREtcnJ3', 0, '28. 11. 2017', 0, 0, '2017-12-23T03:53:38.110Z'); | ||||
| INSERT INTO note_tree (noteTreeId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('Fstg4tkccO4N', '5v5Dx6LMHXIO', 'MLQjmREtcnJ3', 1, '21. 12. 2017', 0, 0, '2017-12-23T03:53:49.737Z'); | ||||
| INSERT INTO note_tree (noteTreeId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('MN8B7qXDUViO', 'xkXwueRoDNeN', 'MLQjmREtcnJ3', 2, '22. 12. 2017', 0, 0, '2017-12-23T03:53:57.486Z'); | ||||
| INSERT INTO note_tree (noteTreeId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('gSRkHpB7Bu3D', 'pOFVzbXLmzhX', 'R6pheWjdwmNU', 2, null, 0, 0, '2017-12-23T03:54:46.138Z'); | ||||
| INSERT INTO note_tree (noteTreeId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('6brdjeWDOB6w', '0xtvjqrcGiRB', 'ZC78NlmdXeC6', 0, null, 0, 0, '2017-12-23T04:02:06.650Z'); | ||||
| INSERT INTO note_tree (noteTreeId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('AqKUM2zUVFUF', 'Zl69uXBSen0w', 'ZC78NlmdXeC6', 2, null, 1, 0, '2017-12-23T04:02:16.685Z'); | ||||
| INSERT INTO note_tree (noteTreeId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('Ez7NN2WVzRc4', '62BKAQMVP2KW', 'Zl69uXBSen0w', 1, null, 0, 0, '2017-12-23T04:02:39.164Z'); | ||||
| INSERT INTO note_tree (noteTreeId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('t3vVElqMIQVa', 'h4OfLEAYspud', 'WdWZFuWNVDZk', 2, null, 1, 0, '2017-12-23T04:06:25.769Z'); | ||||
| INSERT INTO note_tree (noteTreeId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('O983DHtLpgmr', '1hASbLRDL7oo', 'h4OfLEAYspud', 0, null, 0, 0, '2017-12-23T16:42:26.347Z'); | ||||
| INSERT INTO note_tree (noteTreeId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('RsvL795Mk1bp', '1hASbLRDL7oo', 'GpGnjmcAPeWG', 1, '', 0, 0, '2017-12-23T04:04:56.830Z'); | ||||
| INSERT INTO note_tree (noteTreeId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('79e4hrHLFmx6', 'jyqG9GucsMdn', 'Iha4YwchR413', 1, null, 0, 0, '2017-12-23T04:05:16.439Z'); | ||||
| INSERT INTO note_tree (noteTreeId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('oWO8rctUjf7d', 'WdWZFuWNVDZk', '1Heh2acXfPNt', 5, null, 1, 0, '2017-12-23T04:06:16.179Z'); | ||||
| INSERT INTO note_tree (noteTreeId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('GOxcrZrxalFN', 'yK4SBJfwD3tY', '1Heh2acXfPNt', 8, null, 1, 0, '2017-12-23T04:06:32.833Z'); | ||||
| INSERT INTO note_tree (noteTreeId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('bSPmEvjLzQKU', 'r4BnsmSQeVr1', 'yK4SBJfwD3tY', 0, null, 0, 0, '2017-12-23T04:06:37.427Z'); | ||||
| INSERT INTO note_tree (noteTreeId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('bMtxCD6cwNR9', 'QbL3pTvhgzM8', 'yK4SBJfwD3tY', 2, null, 0, 0, '2017-12-23T04:06:43.841Z'); | ||||
| INSERT INTO note_tree (noteTreeId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('o4ycR7xIi4oI', 'moMbTKwN15Ps', 'yK4SBJfwD3tY', 3, null, 1, 0, '2017-12-23T04:06:49.331Z'); | ||||
| INSERT INTO note_tree (noteTreeId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('abTEhnOsAsSg', 'PEGQGg0In3Ar', 'GpGnjmcAPeWG', 2, null, 0, 0, '2017-12-23T16:44:35.900Z'); | ||||
| INSERT INTO note_tree (noteTreeId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('bryQseMhyzaI', 'IlULcDiOTI4K', '1Heh2acXfPNt', 0, null, 0, 0, '2017-12-23T18:04:26.439Z'); | ||||
| INSERT INTO note_tree (noteTreeId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('ccslPJf3wQV3', 'vBv6ovBupfTj', 'IlULcDiOTI4K', 0, null, 0, 0, '2017-12-23T18:04:50.904Z'); | ||||
| INSERT INTO note_tree (noteTreeId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) VALUES ('5Dt9YCMn59sY', 'mw4f2xB4J5fV', 'IlULcDiOTI4K', 1, null, 0, 0, '2017-12-23T18:05:24.868Z'); | ||||
							
								
								
									
										23
									
								
								db/migrations/0077__non_null_attribute_value.sql
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,23 @@ | ||||
| UPDATE attributes SET value = '' WHERE value IS NULL; | ||||
|  | ||||
| CREATE TABLE IF NOT EXISTS "attributes_mig" | ||||
| ( | ||||
|   attributeId TEXT PRIMARY KEY NOT NULL, | ||||
|   noteId TEXT NOT NULL, | ||||
|   name TEXT NOT NULL, | ||||
|   value TEXT NOT NULL DEFAULT '', | ||||
|   position INT NOT NULL DEFAULT 0, | ||||
|   dateCreated TEXT NOT NULL, | ||||
|   dateModified TEXT NOT NULL, | ||||
|   isDeleted INT NOT NULL | ||||
| ); | ||||
|  | ||||
| INSERT INTO attributes_mig (attributeId, noteId, name, value, position, dateCreated, dateModified, isDeleted) | ||||
|     SELECT attributeId, noteId, name, value, position, dateCreated, dateModified, isDeleted FROM attributes; | ||||
|  | ||||
| DROP TABLE attributes; | ||||
|  | ||||
| ALTER TABLE attributes_mig RENAME TO attributes; | ||||
|  | ||||
| CREATE INDEX IDX_attributes_noteId ON attributes (noteId); | ||||
| CREATE INDEX IDX_attributes_name_value ON attributes (name, value); | ||||
							
								
								
									
										1
									
								
								db/migrations/0078__javascript_type.sql
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1 @@ | ||||
| UPDATE notes SET mime = 'application/javascript;env=frontend' WHERE type = 'code' AND mime = 'application/javascript'; | ||||
							
								
								
									
										38
									
								
								db/migrations/0079__rename_note_tree.sql
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,38 @@ | ||||
| CREATE TABLE "branches" ( | ||||
|   `branchId`	TEXT NOT NULL, | ||||
|   `noteId`	TEXT NOT NULL, | ||||
|   `parentNoteId`	TEXT NOT NULL, | ||||
|   `notePosition`	INTEGER NOT NULL, | ||||
|   `prefix`	TEXT, | ||||
|   `isExpanded`	BOOLEAN, | ||||
|   `isDeleted`	INTEGER NOT NULL DEFAULT 0, | ||||
|   `dateModified`	TEXT NOT NULL, | ||||
|   PRIMARY KEY(`branchId`) | ||||
| ); | ||||
|  | ||||
| INSERT INTO branches (branchId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified) | ||||
|     SELECT noteTreeId, noteId, parentNoteId, notePosition, prefix, isExpanded, isDeleted, dateModified FROM note_tree; | ||||
|  | ||||
| DROP TABLE note_tree; | ||||
|  | ||||
| CREATE INDEX `IDX_branches_noteId` ON `branches` ( | ||||
|   `noteId` | ||||
| ); | ||||
|  | ||||
| CREATE INDEX `IDX_branches_noteId_parentNoteId` ON `branches` ( | ||||
|   `noteId`, | ||||
|   `parentNoteId` | ||||
| ); | ||||
|  | ||||
| CREATE TABLE `recent_notes_mig` ( | ||||
|   `branchId` TEXT NOT NULL PRIMARY KEY, | ||||
|   `notePath` TEXT NOT NULL, | ||||
|   `dateAccessed` TEXT NOT NULL, | ||||
|   isDeleted INT | ||||
| ); | ||||
|  | ||||
| INSERT INTO recent_notes_mig (branchId, notePath, dateAccessed, isDeleted) | ||||
|     SELECT noteTreeId, notePath, dateAccessed, isDeleted FROM recent_notes; | ||||
|  | ||||
| DROP TABLE recent_notes; | ||||
| ALTER TABLE recent_notes_mig RENAME TO recent_notes; | ||||
							
								
								
									
										22
									
								
								db/migrations/0080__rename_attributes_to_labels.sql
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,22 @@ | ||||
| create table labels | ||||
| ( | ||||
|   labelId  TEXT not null primary key, | ||||
|   noteId       TEXT not null, | ||||
|   name         TEXT not null, | ||||
|   value        TEXT default '' not null, | ||||
|   position     INT  default 0 not null, | ||||
|   dateCreated  TEXT not null, | ||||
|   dateModified TEXT not null, | ||||
|   isDeleted    INT  not null | ||||
| ); | ||||
|  | ||||
| create index IDX_labels_name_value | ||||
|   on labels (name, value); | ||||
|  | ||||
| create index IDX_labels_noteId | ||||
|   on labels (noteId); | ||||
|  | ||||
| INSERT INTO labels (labelId, noteId, name, "value", "position", dateCreated, dateModified, isDeleted) | ||||
|   SELECT attributeId, noteId, name, "value", "position", dateCreated, dateModified, isDeleted FROM attributes; | ||||
|  | ||||
| DROP TABLE attributes; | ||||
| @@ -0,0 +1 @@ | ||||
| UPDATE options SET name = 'note_revision_snapshot_time_interval' WHERE name = 'history_snapshot_time_interval'; | ||||
							
								
								
									
										14
									
								
								db/migrations/0082__camelCase_options.sql
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,14 @@ | ||||
| UPDATE "options" SET "name" = 'passwordVerificationHash' WHERE "name" = 'password_verification_hash'; | ||||
| UPDATE "options" SET "name" = 'dbVersion' WHERE "name" = 'db_version'; | ||||
| UPDATE "options" SET "name" = 'passwordDerivedKeySalt' WHERE "name" = 'password_derived_key_salt'; | ||||
| UPDATE "options" SET "name" = 'documentId' WHERE "name" = 'document_id'; | ||||
| UPDATE "options" SET "name" = 'lastSyncedPull' WHERE "name" = 'last_synced_pull'; | ||||
| UPDATE "options" SET "name" = 'startNotePath' WHERE "name" = 'start_note_path'; | ||||
| UPDATE "options" SET "name" = 'lastSyncedPush' WHERE "name" = 'last_synced_push'; | ||||
| UPDATE "options" SET "name" = 'documentSecret' WHERE "name" = 'document_secret'; | ||||
| UPDATE "options" SET "name" = 'lastBackupDate' WHERE "name" = 'last_backup_date'; | ||||
| UPDATE "options" SET "name" = 'noteRevisionSnapshotTimeInterval' WHERE "name" = 'note_revision_snapshot_time_interval'; | ||||
| UPDATE "options" SET "name" = 'protectedSessionTimeout' WHERE "name" = 'protected_session_timeout'; | ||||
| UPDATE "options" SET "name" = 'encryptedDataKey' WHERE "name" = 'encrypted_data_key'; | ||||
| UPDATE "options" SET "name" = 'encryptedDataKeyIv' WHERE "name" = 'encrypted_data_key_iv'; | ||||
| UPDATE "options" SET "name" = 'passwordVerificationSalt' WHERE "name" = 'password_verification_salt'; | ||||
							
								
								
									
										7
									
								
								db/migrations/0083__camelCase_labels.sql
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,7 @@ | ||||
| UPDATE labels SET name = 'disableVersioning' WHERE name = 'disable_versioning'; | ||||
| UPDATE labels SET name = 'calendarRoot' WHERE name = 'calendar_root'; | ||||
| UPDATE labels SET name = 'hideInAutocomplete' WHERE name = 'hide_in_autocomplete'; | ||||
| UPDATE labels SET name = 'excludeFromExport' WHERE name = 'exclude_from_export'; | ||||
| UPDATE labels SET name = 'manualTransactionHandling' WHERE name = 'manual_transaction_handling'; | ||||
| UPDATE labels SET name = 'disableInclusion' WHERE name = 'disable_inclusion'; | ||||
| UPDATE labels SET name = 'appCss' WHERE name = 'app_css'; | ||||
							
								
								
									
										4
									
								
								db/migrations/0084__camelCase_reddit_ids.sql
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,4 @@ | ||||
| UPDATE labels SET name = 'redditId' WHERE name = 'reddit_id'; | ||||
| UPDATE labels SET name = 'redditKind' WHERE name = 'reddit_kind'; | ||||
| UPDATE labels SET name = 'redditCreatedUtc' WHERE name = 'reddit_created_utc'; | ||||
| UPDATE labels SET name = 'redditDateNote' WHERE name = 'reddit_date_note'; | ||||
							
								
								
									
										2
									
								
								db/migrations/0085__camelCase_run_values.sql
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,2 @@ | ||||
| UPDATE labels SET value = 'frontendStartup' WHERE value = 'frontend_startup'; | ||||
| UPDATE labels SET value = 'backendStartup' WHERE value = 'backend_startup'; | ||||
							
								
								
									
										7
									
								
								db/migrations/0086__camelCase_custom_labels.sql
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,7 @@ | ||||
| UPDATE labels SET name = 'dateData' WHERE name = 'date_data'; | ||||
| UPDATE labels SET name = 'dateNote' WHERE name = 'date_note'; | ||||
| UPDATE labels SET name = 'fileSize' WHERE name = 'file_size'; | ||||
| UPDATE labels SET name = 'hideInAutocomplete' WHERE name = 'hide_in_autocomplete'; | ||||
| UPDATE labels SET name = 'monthNote' WHERE name = 'month_note'; | ||||
| UPDATE labels SET name = 'originalFileName' WHERE name = 'original_file_name'; | ||||
| UPDATE labels SET name = 'yearNote' WHERE name = 'year_note'; | ||||
							
								
								
									
										5
									
								
								db/migrations/0087__add_type_mime_to_note_revision.sql
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,5 @@ | ||||
| ALTER TABLE note_revisions ADD type TEXT DEFAULT '' NOT NULL; | ||||
| ALTER TABLE note_revisions ADD mime TEXT DEFAULT '' NOT NULL; | ||||
|  | ||||
| UPDATE note_revisions SET type = (SELECT type FROM notes WHERE notes.noteId = note_revisions.noteId); | ||||
| UPDATE note_revisions SET mime = (SELECT mime FROM notes WHERE notes.noteId = note_revisions.noteId); | ||||
							
								
								
									
										34
									
								
								db/migrations/0088__non_null_note_title_content.sql
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,34 @@ | ||||
| CREATE TABLE event_logc027 | ||||
| ( | ||||
|   id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, | ||||
|   noteId TEXT, | ||||
|   comment TEXT, | ||||
|   dateAdded TEXT NOT NULL | ||||
| ); | ||||
| INSERT INTO event_logc027(id, noteId, comment, dateAdded) SELECT id, noteId, comment, dateAdded FROM event_log; | ||||
| DROP TABLE event_log; | ||||
| ALTER TABLE event_logc027 RENAME TO event_log; | ||||
|  | ||||
| CREATE TABLE IF NOT EXISTS "notes_mig" ( | ||||
|   `noteId`	TEXT NOT NULL, | ||||
|   `title`	TEXT NOT NULL DEFAULT "unnamed", | ||||
|   `content`	TEXT NOT NULL DEFAULT "", | ||||
|   `isProtected`	INT NOT NULL DEFAULT 0, | ||||
|   `isDeleted`	INT NOT NULL DEFAULT 0, | ||||
|   `dateCreated`	TEXT NOT NULL, | ||||
|   `dateModified`	TEXT NOT NULL, | ||||
|   type TEXT NOT NULL DEFAULT 'text', | ||||
|   mime TEXT NOT NULL DEFAULT 'text/html', | ||||
|   PRIMARY KEY(`noteId`) | ||||
| ); | ||||
|  | ||||
| INSERT INTO notes_mig (noteId, title, content, isProtected, isDeleted, dateCreated, dateModified, type, mime) | ||||
|     SELECT noteId, title, content, isProtected, isDeleted, dateCreated, dateModified, type, mime FROM notes; | ||||
|  | ||||
| DROP TABLE notes; | ||||
|  | ||||
| ALTER TABLE notes_mig RENAME TO notes; | ||||
|  | ||||
| CREATE INDEX `IDX_notes_isDeleted` ON `notes` ( | ||||
|   `isDeleted` | ||||
| ); | ||||
							
								
								
									
										5
									
								
								db/migrations/0089__add_root_branch.sql
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,5 @@ | ||||
| INSERT INTO branches (branchId, noteId, parentNoteId, notePosition, prefix, isExpanded, dateModified) | ||||
|     VALUES ('root', 'root', 'none', 0, null, 1, '2018-01-01T00:00:00.000Z'); | ||||
|  | ||||
| INSERT INTO sync (entityName, entityId, sourceId, syncDate) | ||||
|     VALUES ('branches' ,'root', 'SYNC_FILL', '2018-01-01T00:00:00.000Z'); | ||||
							
								
								
									
										1
									
								
								db/migrations/0090__branch_index.sql
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1 @@ | ||||
| CREATE INDEX IDX_branches_parentNoteId ON branches (parentNoteId); | ||||
							
								
								
									
										2
									
								
								db/migrations/0091__drop_isDeleted_index.sql
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,2 @@ | ||||
| -- index confuses planner and is mostly useless anyway since we're mostly used in non-deleted notes (which are presumably majority) | ||||
| DROP INDEX IDX_notes_isDeleted; | ||||
							
								
								
									
										2
									
								
								db/migrations/0092__add_type_index.sql
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,2 @@ | ||||
| create index IDX_notes_type | ||||
|   on notes (type); | ||||
							
								
								
									
										9
									
								
								db/migrations/0093__add_hash_field.sql
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,9 @@ | ||||
| ALTER TABLE notes ADD hash TEXT DEFAULT "" NOT NULL; | ||||
| ALTER TABLE branches ADD hash TEXT DEFAULT "" NOT NULL; | ||||
| ALTER TABLE note_revisions ADD hash TEXT DEFAULT "" NOT NULL; | ||||
| ALTER TABLE recent_notes ADD hash TEXT DEFAULT "" NOT NULL; | ||||
| ALTER TABLE options ADD hash TEXT DEFAULT "" NOT NULL; | ||||
| ALTER TABLE note_images ADD hash TEXT DEFAULT "" NOT NULL; | ||||
| ALTER TABLE images ADD hash TEXT DEFAULT "" NOT NULL; | ||||
| ALTER TABLE labels ADD hash TEXT DEFAULT "" NOT NULL; | ||||
| ALTER TABLE api_tokens ADD hash TEXT DEFAULT "" NOT NULL; | ||||
							
								
								
									
										30
									
								
								db/migrations/0094__unify_auditing_fields.sql
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,30 @@ | ||||
| ALTER TABLE branches ADD dateCreated TEXT NOT NULL DEFAULT '1970-01-01T00:00:00.000Z'; | ||||
|  | ||||
| CREATE TABLE `event_log_mig` ( | ||||
|   `id`	INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, | ||||
|   `noteId`	TEXT, | ||||
|   `comment`	TEXT, | ||||
|   `dateCreated`	TEXT NOT NULL | ||||
| ); | ||||
|  | ||||
| INSERT INTO event_log_mig (id, noteId, comment, dateCreated) | ||||
| SELECT id, noteId, comment, dateAdded FROM event_log; | ||||
|  | ||||
| DROP TABLE event_log; | ||||
| ALTER TABLE event_log_mig RENAME TO event_log; | ||||
|  | ||||
| ALTER TABLE options ADD dateCreated TEXT NOT NULL DEFAULT '1970-01-01T00:00:00.000Z'; | ||||
|  | ||||
| CREATE TABLE `recent_notes_mig` ( | ||||
|   `branchId` TEXT NOT NULL PRIMARY KEY, | ||||
|   `notePath` TEXT NOT NULL, | ||||
|   hash TEXT DEFAULT "" NOT NULL, | ||||
|   `dateCreated` TEXT NOT NULL, | ||||
|   isDeleted INT | ||||
| ); | ||||
|  | ||||
| INSERT INTO recent_notes_mig (branchId, notePath, hash, dateCreated, isDeleted) | ||||
| SELECT branchId, notePath, hash, dateAccessed, isDeleted FROM recent_notes; | ||||
|  | ||||
| DROP TABLE recent_notes; | ||||
| ALTER TABLE recent_notes_mig RENAME TO recent_notes; | ||||
							
								
								
									
										1
									
								
								db/migrations/0095__mime_type_for_render.sql
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1 @@ | ||||
| UPDATE notes SET mime = 'text/html' WHERE type = 'render'; | ||||
							
								
								
									
										29
									
								
								db/migrations/0096__unify_surrogate_keys.sql
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,29 @@ | ||||
| CREATE TABLE `event_log_mig` ( | ||||
|   `eventId`	TEXT NOT NULL PRIMARY KEY, | ||||
|   `noteId`	TEXT, | ||||
|   `comment`	TEXT, | ||||
|   `dateCreated`	TEXT NOT NULL | ||||
| ); | ||||
|  | ||||
| INSERT INTO event_log_mig (eventId, noteId, comment, dateCreated) | ||||
| SELECT id, noteId, comment, dateCreated FROM event_log; | ||||
|  | ||||
| DROP TABLE event_log; | ||||
| ALTER TABLE event_log_mig RENAME TO event_log; | ||||
|  | ||||
| create table options_mig | ||||
| ( | ||||
|   optionId TEXT NOT NULL PRIMARY KEY, | ||||
|   name TEXT not null, | ||||
|   value TEXT, | ||||
|   dateModified INT, | ||||
|   isSynced INTEGER default 0 not null, | ||||
|   hash TEXT default "" not null, | ||||
|   dateCreated TEXT default '1970-01-01T00:00:00.000Z' not null | ||||
| ); | ||||
|  | ||||
| INSERT INTO options_mig (optionId, name, value, dateModified, isSynced, hash, dateCreated) | ||||
|   SELECT name || "_key", name, value, dateModified, isSynced, hash, dateCreated FROM options; | ||||
|  | ||||
| DROP TABLE options; | ||||
| ALTER TABLE options_mig RENAME TO options; | ||||
							
								
								
									
										2
									
								
								db/migrations/0097__add_zoomFactor.sql
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,2 @@ | ||||
| INSERT INTO options (optionId, name, value, dateCreated, dateModified, isSynced) | ||||
| VALUES ('zoomFactor_key', 'zoomFactor', '1.0', '2018-06-01T03:35:55.041Z', '2018-06-01T03:35:55.041Z', 0); | ||||
							
								
								
									
										1
									
								
								db/migrations/0098__rename_hideInAutocomplete.sql
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1 @@ | ||||
| UPDATE labels SET name = 'archived' WHERE name = 'hideInAutocomplete' | ||||
							
								
								
									
										2
									
								
								db/migrations/0099__add_theme_option.sql
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,2 @@ | ||||
| INSERT INTO options (optionId, name, value, dateCreated, dateModified, isSynced) | ||||
| VALUES ('theme_key', 'theme', 'white', '2018-06-01T03:35:55.041Z', '2018-06-01T03:35:55.041Z', 0); | ||||
							
								
								
									
										15
									
								
								db/migrations/0100__remove_optionId.sql
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,15 @@ | ||||
| create table options_mig | ||||
| ( | ||||
|   name TEXT not null PRIMARY KEY, | ||||
|   value TEXT, | ||||
|   dateModified INT, | ||||
|   isSynced INTEGER default 0 not null, | ||||
|   hash TEXT default "" not null, | ||||
|   dateCreated TEXT default '1970-01-01T00:00:00.000Z' not null | ||||
| ); | ||||
|  | ||||
| INSERT INTO options_mig (name, value, dateModified, isSynced, hash, dateCreated) | ||||
| SELECT name, value, dateModified, isSynced, hash, dateCreated FROM options; | ||||
|  | ||||
| DROP TABLE options; | ||||
| ALTER TABLE options_mig RENAME TO options; | ||||
							
								
								
									
										169
									
								
								db/schema.sql
									
									
									
									
									
								
							
							
						
						| @@ -1,49 +1,21 @@ | ||||
| CREATE TABLE IF NOT EXISTS "options" ( | ||||
|     `name`	TEXT NOT NULL PRIMARY KEY, | ||||
|     `value`	TEXT, | ||||
|     `dateModified` INT, | ||||
|     isSynced INTEGER NOT NULL DEFAULT 0); | ||||
| CREATE TABLE IF NOT EXISTS "sync" ( | ||||
|   `id`	INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, | ||||
|   `entityName`	TEXT NOT NULL, | ||||
|   `entityId`	TEXT NOT NULL, | ||||
|   `sourceId` TEXT NOT NULL, | ||||
|   `syncDate`	TEXT NOT NULL); | ||||
| CREATE UNIQUE INDEX `IDX_sync_entityName_entityId` ON `sync` ( | ||||
|   `entityName`, | ||||
|   `entityId` | ||||
| ); | ||||
| CREATE INDEX `IDX_sync_syncDate` ON `sync` ( | ||||
|   `syncDate` | ||||
| ); | ||||
| CREATE TABLE IF NOT EXISTS "source_ids" ( | ||||
|   `sourceId`	TEXT NOT NULL, | ||||
|   `dateCreated`	TEXT NOT NULL, | ||||
|   PRIMARY KEY(`sourceId`) | ||||
| ); | ||||
| CREATE TABLE IF NOT EXISTS "notes" ( | ||||
|   `noteId`	TEXT NOT NULL, | ||||
|   `title`	TEXT, | ||||
|   `content`	TEXT, | ||||
|   `isProtected`	INT NOT NULL DEFAULT 0, | ||||
|   `isDeleted`	INT NOT NULL DEFAULT 0, | ||||
|   `dateCreated`	TEXT NOT NULL, | ||||
|   `dateModified`	TEXT NOT NULL, | ||||
|   type TEXT NOT NULL DEFAULT 'text', | ||||
|   mime TEXT NOT NULL DEFAULT 'text/html', | ||||
|   PRIMARY KEY(`noteId`) | ||||
| ); | ||||
| CREATE TABLE IF NOT EXISTS "event_log" ( | ||||
|   `id`	INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, | ||||
|   `noteId`	TEXT, | ||||
|   `comment`	TEXT, | ||||
|   `dateAdded`	TEXT NOT NULL, | ||||
|   FOREIGN KEY(noteId) REFERENCES notes(noteId) | ||||
| ); | ||||
| CREATE TABLE IF NOT EXISTS "note_tree" ( | ||||
|   `noteTreeId`	TEXT NOT NULL, | ||||
|   `noteId`	TEXT NOT NULL, | ||||
|   `parentNoteId`	TEXT NOT NULL, | ||||
|   `notePosition`	INTEGER NOT NULL, | ||||
|   `prefix`	TEXT, | ||||
|   `isExpanded`	BOOLEAN, | ||||
|   `isDeleted`	INTEGER NOT NULL DEFAULT 0, | ||||
|   `dateModified`	TEXT NOT NULL, | ||||
|   PRIMARY KEY(`noteTreeId`) | ||||
| ); | ||||
| CREATE TABLE IF NOT EXISTS "note_revisions" ( | ||||
|   `noteRevisionId`	TEXT NOT NULL PRIMARY KEY, | ||||
|   `noteId`	TEXT NOT NULL, | ||||
| @@ -52,12 +24,15 @@ CREATE TABLE IF NOT EXISTS "note_revisions" ( | ||||
|   `isProtected`	INT NOT NULL DEFAULT 0, | ||||
|   `dateModifiedFrom` TEXT NOT NULL, | ||||
|   `dateModifiedTo` TEXT NOT NULL | ||||
| , type TEXT DEFAULT '' NOT NULL, mime TEXT DEFAULT '' NOT NULL, hash TEXT DEFAULT "" NOT NULL); | ||||
| CREATE INDEX `IDX_note_revisions_noteId` ON `note_revisions` ( | ||||
|   `noteId` | ||||
| ); | ||||
| CREATE TABLE IF NOT EXISTS "recent_notes" ( | ||||
|   `noteTreeId` TEXT NOT NULL PRIMARY KEY, | ||||
|   `notePath` TEXT NOT NULL, | ||||
|   `dateAccessed` TEXT NOT NULL, | ||||
|   isDeleted INT | ||||
| CREATE INDEX `IDX_note_revisions_dateModifiedFrom` ON `note_revisions` ( | ||||
|   `dateModifiedFrom` | ||||
| ); | ||||
| CREATE INDEX `IDX_note_revisions_dateModifiedTo` ON `note_revisions` ( | ||||
|   `dateModifiedTo` | ||||
| ); | ||||
| CREATE TABLE IF NOT EXISTS "images" | ||||
| ( | ||||
| @@ -69,7 +44,7 @@ CREATE TABLE IF NOT EXISTS "images" | ||||
|   isDeleted INT NOT NULL DEFAULT 0, | ||||
|   dateModified TEXT NOT NULL, | ||||
|   dateCreated TEXT NOT NULL | ||||
| ); | ||||
| , hash TEXT DEFAULT "" NOT NULL); | ||||
| CREATE TABLE note_images | ||||
| ( | ||||
|   noteImageId TEXT PRIMARY KEY NOT NULL, | ||||
| @@ -78,54 +53,84 @@ CREATE TABLE note_images | ||||
|   isDeleted INT NOT NULL DEFAULT 0, | ||||
|   dateModified TEXT NOT NULL, | ||||
|   dateCreated TEXT NOT NULL | ||||
| ); | ||||
| CREATE TABLE IF NOT EXISTS "attributes" | ||||
| ( | ||||
|   attributeId TEXT PRIMARY KEY NOT NULL, | ||||
|   noteId TEXT NOT NULL, | ||||
|   name TEXT NOT NULL, | ||||
|   value TEXT, | ||||
|   position INT NOT NULL DEFAULT 0, | ||||
|   dateCreated TEXT NOT NULL, | ||||
|   dateModified TEXT NOT NULL, | ||||
|   isDeleted INT NOT NULL | ||||
| ); | ||||
| CREATE UNIQUE INDEX `IDX_sync_entityName_entityId` ON `sync` ( | ||||
|   `entityName`, | ||||
|   `entityId` | ||||
| ); | ||||
| CREATE INDEX `IDX_sync_syncDate` ON `sync` ( | ||||
|   `syncDate` | ||||
| ); | ||||
| CREATE INDEX `IDX_notes_isDeleted` ON `notes` ( | ||||
|   `isDeleted` | ||||
| ); | ||||
| CREATE INDEX `IDX_note_tree_noteId` ON `note_tree` ( | ||||
|   `noteId` | ||||
| ); | ||||
| CREATE INDEX `IDX_note_tree_noteId_parentNoteId` ON `note_tree` ( | ||||
|   `noteId`, | ||||
|   `parentNoteId` | ||||
| ); | ||||
| CREATE INDEX `IDX_note_revisions_noteId` ON `note_revisions` ( | ||||
|   `noteId` | ||||
| ); | ||||
| CREATE INDEX `IDX_note_revisions_dateModifiedFrom` ON `note_revisions` ( | ||||
|   `dateModifiedFrom` | ||||
| ); | ||||
| CREATE INDEX `IDX_note_revisions_dateModifiedTo` ON `note_revisions` ( | ||||
|   `dateModifiedTo` | ||||
| ); | ||||
| , hash TEXT DEFAULT "" NOT NULL); | ||||
| CREATE INDEX IDX_note_images_noteId ON note_images (noteId); | ||||
| CREATE INDEX IDX_note_images_imageId ON note_images (imageId); | ||||
| CREATE INDEX IDX_note_images_noteId_imageId ON note_images (noteId, imageId); | ||||
| CREATE INDEX IDX_attributes_noteId ON attributes (noteId); | ||||
| CREATE INDEX IDX_attributes_name_value ON attributes (name, value); | ||||
|  | ||||
| CREATE TABLE IF NOT EXISTS "api_tokens" | ||||
| ( | ||||
|   apiTokenId TEXT PRIMARY KEY NOT NULL, | ||||
|   token TEXT NOT NULL, | ||||
|   dateCreated TEXT NOT NULL, | ||||
|   isDeleted INT NOT NULL DEFAULT 0 | ||||
| , hash TEXT DEFAULT "" NOT NULL); | ||||
| CREATE TABLE IF NOT EXISTS "branches" ( | ||||
|   `branchId`	TEXT NOT NULL, | ||||
|   `noteId`	TEXT NOT NULL, | ||||
|   `parentNoteId`	TEXT NOT NULL, | ||||
|   `notePosition`	INTEGER NOT NULL, | ||||
|   `prefix`	TEXT, | ||||
|   `isExpanded`	BOOLEAN, | ||||
|   `isDeleted`	INTEGER NOT NULL DEFAULT 0, | ||||
|   `dateModified`	TEXT NOT NULL, hash TEXT DEFAULT "" NOT NULL, dateCreated TEXT NOT NULL DEFAULT '1970-01-01T00:00:00.000Z', | ||||
|   PRIMARY KEY(`branchId`) | ||||
| ); | ||||
| CREATE INDEX `IDX_branches_noteId` ON `branches` ( | ||||
|   `noteId` | ||||
| ); | ||||
| CREATE INDEX `IDX_branches_noteId_parentNoteId` ON `branches` ( | ||||
|   `noteId`, | ||||
|   `parentNoteId` | ||||
| ); | ||||
| CREATE TABLE labels | ||||
| ( | ||||
|   labelId  TEXT not null primary key, | ||||
|   noteId       TEXT not null, | ||||
|   name         TEXT not null, | ||||
|   value        TEXT default '' not null, | ||||
|   position     INT  default 0 not null, | ||||
|   dateCreated  TEXT not null, | ||||
|   dateModified TEXT not null, | ||||
|   isDeleted    INT  not null | ||||
| , hash TEXT DEFAULT "" NOT NULL); | ||||
| CREATE INDEX IDX_labels_name_value | ||||
|   on labels (name, value); | ||||
| CREATE INDEX IDX_labels_noteId | ||||
|   on labels (noteId); | ||||
| CREATE TABLE IF NOT EXISTS "notes" ( | ||||
|   `noteId`	TEXT NOT NULL, | ||||
|   `title`	TEXT NOT NULL DEFAULT "unnamed", | ||||
|   `content`	TEXT NOT NULL DEFAULT "", | ||||
|   `isProtected`	INT NOT NULL DEFAULT 0, | ||||
|   `isDeleted`	INT NOT NULL DEFAULT 0, | ||||
|   `dateCreated`	TEXT NOT NULL, | ||||
|   `dateModified`	TEXT NOT NULL, | ||||
|   type TEXT NOT NULL DEFAULT 'text', | ||||
|   mime TEXT NOT NULL DEFAULT 'text/html', hash TEXT DEFAULT "" NOT NULL, | ||||
|   PRIMARY KEY(`noteId`) | ||||
| ); | ||||
| CREATE INDEX IDX_branches_parentNoteId ON branches (parentNoteId); | ||||
| CREATE INDEX IDX_notes_type | ||||
|   on notes (type); | ||||
| CREATE TABLE IF NOT EXISTS "recent_notes" ( | ||||
|   `branchId` TEXT NOT NULL PRIMARY KEY, | ||||
|   `notePath` TEXT NOT NULL, | ||||
|   hash TEXT DEFAULT "" NOT NULL, | ||||
|   `dateCreated` TEXT NOT NULL, | ||||
|   isDeleted INT | ||||
| ); | ||||
| CREATE TABLE IF NOT EXISTS "event_log" ( | ||||
|   `eventId`	TEXT NOT NULL PRIMARY KEY, | ||||
|   `noteId`	TEXT, | ||||
|   `comment`	TEXT, | ||||
|   `dateCreated`	TEXT NOT NULL | ||||
| ); | ||||
| CREATE TABLE IF NOT EXISTS "options" | ||||
| ( | ||||
|   name TEXT not null PRIMARY KEY, | ||||
|   value TEXT, | ||||
|   dateModified INT, | ||||
|   isSynced INTEGER default 0 not null, | ||||
|   hash TEXT default "" not null, | ||||
|   dateCreated TEXT default '1970-01-01T00:00:00.000Z' not null | ||||
| ); | ||||
							
								
								
									
										18
									
								
								electron.js
									
									
									
									
									
								
							
							
						
						| @@ -3,11 +3,11 @@ | ||||
| const electron = require('electron'); | ||||
| const path = require('path'); | ||||
| const config = require('./src/services/config'); | ||||
| const log = require('./src/services/log'); | ||||
| const url = require("url"); | ||||
|  | ||||
| const app = electron.app; | ||||
| const globalShortcut = electron.globalShortcut; | ||||
| const clipboard = electron.clipboard; | ||||
|  | ||||
| // Adds debug features like hotkeys for triggering dev tools and reload | ||||
| require('electron-debug')(); | ||||
| @@ -15,6 +15,8 @@ require('electron-debug')(); | ||||
| // Prevent window being garbage collected | ||||
| let mainWindow; | ||||
|  | ||||
| require('electron-dl')({ saveAs: true }); | ||||
|  | ||||
| function onClosed() { | ||||
|     // Dereference the window | ||||
|     // For multiple windows store them in an array | ||||
| @@ -70,17 +72,21 @@ app.on('activate', () => { | ||||
| app.on('ready', () => { | ||||
|     mainWindow = createMainWindow(); | ||||
|  | ||||
|     globalShortcut.register('CommandOrControl+Alt+P', async () => { | ||||
|         const date_notes = require('./src/services/date_notes'); | ||||
|         const utils = require('./src/services/utils'); | ||||
|     const result = globalShortcut.register('CommandOrControl+Alt+P', async () => { | ||||
|         const dateNoteService = require('./src/services/date_notes'); | ||||
|         const dateUtils = require('./src/services/date_utils'); | ||||
|  | ||||
|         const parentNoteId = await date_notes.getDateNoteId(utils.nowDate()); | ||||
|         const parentNote = await dateNoteService.getDateNote(dateUtils.nowDate()); | ||||
|  | ||||
|         // window may be hidden / not in focus | ||||
|         mainWindow.focus(); | ||||
|  | ||||
|         mainWindow.webContents.send('create-day-sub-note', parentNoteId); | ||||
|         mainWindow.webContents.send('create-day-sub-note', parentNote.noteId); | ||||
|     }); | ||||
|  | ||||
|     if (!result) { | ||||
|         log.error("Could not register global shortcut CTRL+ALT+P"); | ||||
|     } | ||||
| }); | ||||
|  | ||||
| app.on('will-quit', () => { | ||||
|   | ||||
							
								
								
									
										6478
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
							
								
								
									
										80
									
								
								package.json
									
									
									
									
									
								
							
							
						
						| @@ -1,70 +1,81 @@ | ||||
| { | ||||
|   "name": "trilium", | ||||
|   "description": "Trilium Notes", | ||||
|   "version": "0.6.1", | ||||
|   "version": "0.17.0", | ||||
|   "license": "AGPL-3.0-only", | ||||
|   "main": "electron.js", | ||||
|   "bin": { | ||||
|     "trilium": "./src/www" | ||||
|   }, | ||||
|   "repository": { | ||||
|     "type": "git", | ||||
|     "url": "https://github.com/zadam/trilium.git" | ||||
|   }, | ||||
|   "scripts": { | ||||
|     "start": "node ./bin/www", | ||||
|     "start": "node ./src/www", | ||||
|     "test-electron": "xo", | ||||
|     "rebuild-electron": "electron-rebuild", | ||||
|     "start-electron": "electron .", | ||||
|     "build-electron": "electron-packager . --out=dist --asar --overwrite --all", | ||||
|     "start-electron": "electron . --disable-gpu", | ||||
|     "build-electron": "electron-packager . --out=dist --asar --overwrite --platform=win32,linux --arch=ia32,x64 --app-version=", | ||||
|     "start-forge": "electron-forge start", | ||||
|     "package-forge": "electron-forge package", | ||||
|     "make-forge": "electron-forge make", | ||||
|     "publish-forge": "electron-forge publish" | ||||
|     "publish-forge": "electron-forge publish", | ||||
|     "build-pkg": "pkg . --targets node8-linux-x64 --output dist/trilium-linux-x64-server.elf" | ||||
|   }, | ||||
|   "dependencies": { | ||||
|     "async-mutex": "^0.1.3", | ||||
|     "axios": "^0.17.1", | ||||
|     "body-parser": "~1.18.2", | ||||
|     "axios": "^0.18", | ||||
|     "body-parser": "^1.18.3", | ||||
|     "cls-hooked": "^4.2.2", | ||||
|     "cookie-parser": "~1.4.3", | ||||
|     "debug": "~3.1.0", | ||||
|     "devtron": "^1.4.0", | ||||
|     "ejs": "~2.5.7", | ||||
|     "electron": "^1.8.2-beta.4", | ||||
|     "electron-debug": "^1.5.0", | ||||
|     "electron-in-page-search": "^1.2.4", | ||||
|     "express": "~4.16.2", | ||||
|     "express-promise-wrap": "^0.2.2", | ||||
|     "ejs": "~2.6.1", | ||||
|     "electron-debug": "^2.0.0", | ||||
|     "electron-dl": "^1.12.0", | ||||
|     "electron-in-page-search": "^1.3.2", | ||||
|     "express": "~4.16.3", | ||||
|     "express-session": "^1.15.6", | ||||
|     "fs-extra": "^4.0.2", | ||||
|     "helmet": "^3.9.0", | ||||
|     "fs-extra": "^6.0.1", | ||||
|     "helmet": "^3.12.1", | ||||
|     "html": "^1.0.0", | ||||
|     "image-type": "^3.0.0", | ||||
|     "imagemin": "^5.3.1", | ||||
|     "imagemin-giflossy": "^5.1.10", | ||||
|     "imagemin-mozjpeg": "^7.0.0", | ||||
|     "imagemin-pngquant": "^5.0.1", | ||||
|     "ini": "^1.3.4", | ||||
|     "imagemin-pngquant": "^5.1.0", | ||||
|     "ini": "^1.3.5", | ||||
|     "jimp": "^0.2.28", | ||||
|     "moment": "^2.20.1", | ||||
|     "multer": "^1.3.0", | ||||
|     "moment": "^2.22.2", | ||||
|     "multer": "^1.3.1", | ||||
|     "open": "0.0.5", | ||||
|     "rand-token": "^0.4.0", | ||||
|     "request": "^2.83.0", | ||||
|     "rcedit": "^1.1.0", | ||||
|     "request": "^2.87.0", | ||||
|     "request-promise": "^4.2.2", | ||||
|     "rimraf": "^2.6.2", | ||||
|     "sanitize-filename": "^1.6.1", | ||||
|     "scrypt": "^6.0.3", | ||||
|     "serve-favicon": "~2.4.5", | ||||
|     "session-file-store": "^1.1.2", | ||||
|     "simple-node-logger": "^0.93.30", | ||||
|     "sqlite": "^2.9.0", | ||||
|     "serve-favicon": "~2.5.0", | ||||
|     "session-file-store": "^1.2.0", | ||||
|     "simple-node-logger": "^0.93.37", | ||||
|     "sqlite": "^2.9.2", | ||||
|     "tar-stream": "^1.6.1", | ||||
|     "unescape": "^1.0.1", | ||||
|     "ws": "^3.3.2" | ||||
|     "ws": "^5.2.1", | ||||
|     "xml2js": "^0.4.19" | ||||
|   }, | ||||
|   "devDependencies": { | ||||
|     "electron-compile": "^6.4.2", | ||||
|     "electron-packager": "^10.1.1", | ||||
|     "electron-prebuilt-compile": "1.8.2-beta.4", | ||||
|     "electron-rebuild": "^1.7.3", | ||||
|     "tape": "^4.8.0", | ||||
|     "xo": "^0.18.0" | ||||
|     "electron": "^2.0.4", | ||||
|     "electron-compile": "^6.4.3", | ||||
|     "electron-packager": "^12.1.0", | ||||
|     "electron-prebuilt-compile": "2.0.4", | ||||
|     "electron-rebuild": "^1.8.1", | ||||
|     "lorem-ipsum": "^1.0.5", | ||||
|     "tape": "^4.9.1", | ||||
|     "xo": "^0.21.1", | ||||
|     "pkg": "^4.3.3" | ||||
|   }, | ||||
|   "config": { | ||||
|     "forge": { | ||||
| @@ -103,5 +114,12 @@ | ||||
|       "node", | ||||
|       "browser" | ||||
|     ] | ||||
|   }, | ||||
|   "pkg": { | ||||
|     "assets": [ | ||||
|       "./db/**/*", | ||||
|       "./src/public/**/*", | ||||
|       "./src/views/**/*" | ||||
|     ] | ||||
|   } | ||||
| } | ||||
|   | ||||
							
								
								
									
										15
									
								
								src/app.js
									
									
									
									
									
								
							
							
						
						| @@ -9,6 +9,8 @@ const session = require('express-session'); | ||||
| const FileStore = require('session-file-store')(session); | ||||
| const os = require('os'); | ||||
| const sessionSecret = require('./services/session_secret'); | ||||
| const cls = require('./services/cls'); | ||||
| require('./entities/entity_constructor'); | ||||
|  | ||||
| const app = express(); | ||||
|  | ||||
| @@ -23,6 +25,17 @@ app.use((req, res, next) => { | ||||
|     next(); | ||||
| }); | ||||
|  | ||||
| app.use((req, res, next) => { | ||||
|     cls.namespace.bindEmitter(req); | ||||
|     cls.namespace.bindEmitter(res); | ||||
|  | ||||
|     cls.init(() => { | ||||
|         cls.namespace.set("Hi"); | ||||
|  | ||||
|         next(); | ||||
|     }); | ||||
| }); | ||||
|  | ||||
| app.use(bodyParser.json({limit: '50mb'})); | ||||
| app.use(bodyParser.urlencoded({extended: false})); | ||||
| app.use(cookieParser()); | ||||
| @@ -73,7 +86,7 @@ require('./services/backup'); | ||||
| // trigger consistency checks timer | ||||
| require('./services/consistency_checks'); | ||||
|  | ||||
| require('./plugins/reddit'); | ||||
| require('./services/scheduler'); | ||||
|  | ||||
| module.exports = { | ||||
|     app, | ||||
|   | ||||
							
								
								
									
										24
									
								
								src/entities/api_token.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,24 @@ | ||||
| "use strict"; | ||||
|  | ||||
| const Entity = require('./entity'); | ||||
| const dateUtils = require('../services/date_utils'); | ||||
|  | ||||
| class ApiToken extends Entity { | ||||
|     static get tableName() { return "api_tokens"; } | ||||
|     static get primaryKeyName() { return "apiTokenId"; } | ||||
|     static get hashedProperties() { return ["apiTokenId", "token", "dateCreated", "isDeleted"]; } | ||||
|  | ||||
|     beforeSaving() { | ||||
|         super.beforeSaving(); | ||||
|  | ||||
|         if (!this.isDeleted) { | ||||
|             this.isDeleted = false; | ||||
|         } | ||||
|  | ||||
|         if (!this.dateCreated) { | ||||
|             this.dateCreated = dateUtils.nowDate(); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| module.exports = ApiToken; | ||||
| @@ -1,14 +0,0 @@ | ||||
| "use strict"; | ||||
|  | ||||
| const Entity = require('./entity'); | ||||
|  | ||||
| class Attribute extends Entity { | ||||
|     static get tableName() { return "attributes"; } | ||||
|     static get primaryKeyName() { return "attributeId"; } | ||||
|  | ||||
|     async getNote() { | ||||
|         return this.repository.getEntity("SELECT * FROM notes WHERE noteId = ?", [this.noteId]); | ||||
|     } | ||||
| } | ||||
|  | ||||
| module.exports = Attribute; | ||||
							
								
								
									
										38
									
								
								src/entities/branch.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,38 @@ | ||||
| "use strict"; | ||||
|  | ||||
| const Entity = require('./entity'); | ||||
| const dateUtils = require('../services/date_utils'); | ||||
| const repository = require('../services/repository'); | ||||
| const sql = require('../services/sql'); | ||||
|  | ||||
| class Branch extends Entity { | ||||
|     static get tableName() { return "branches"; } | ||||
|     static get primaryKeyName() { return "branchId"; } | ||||
|     // notePosition is not part of hash because it would produce a lot of updates in case of reordering | ||||
|     static get hashedProperties() { return ["branchId", "noteId", "parentNoteId", "dateModified", "isDeleted", "prefix"]; } | ||||
|  | ||||
|     async getNote() { | ||||
|         return await repository.getEntity("SELECT * FROM notes WHERE noteId = ?", [this.noteId]); | ||||
|     } | ||||
|  | ||||
|     async beforeSaving() { | ||||
|         super.beforeSaving(); | ||||
|  | ||||
|         if (this.notePosition === undefined) { | ||||
|             const maxNotePos = await sql.getValue('SELECT MAX(notePosition) FROM branches WHERE parentNoteId = ? AND isDeleted = 0', [this.parentNoteId]); | ||||
|             this.notePosition = maxNotePos === null ? 0 : maxNotePos + 1; | ||||
|         } | ||||
|  | ||||
|         if (!this.isDeleted) { | ||||
|             this.isDeleted = false; | ||||
|         } | ||||
|  | ||||
|         if (!this.dateCreated) { | ||||
|             this.dateCreated = dateUtils.nowDate(); | ||||
|         } | ||||
|  | ||||
|         this.dateModified = dateUtils.nowDate(); | ||||
|     } | ||||
| } | ||||
|  | ||||
| module.exports = Branch; | ||||
| @@ -1,17 +1,34 @@ | ||||
| "use strict"; | ||||
|  | ||||
| const utils = require('../services/utils'); | ||||
| const repository = require('../services/repository'); | ||||
|  | ||||
| class Entity { | ||||
|     constructor(repository, row) { | ||||
|         utils.assertArguments(repository, row); | ||||
|  | ||||
|         this.repository = repository; | ||||
|  | ||||
|     constructor(row = {}) { | ||||
|         for (const key in row) { | ||||
|             this[key] = row[key]; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     beforeSaving() { | ||||
|         if (!this[this.constructor.primaryKeyName]) { | ||||
|             this[this.constructor.primaryKeyName] = utils.newEntityId(); | ||||
|         } | ||||
|  | ||||
|         let contentToHash = ""; | ||||
|  | ||||
|         for (const propertyName of this.constructor.hashedProperties) { | ||||
|             contentToHash += "|" + this[propertyName]; | ||||
|         } | ||||
|  | ||||
|         this["hash"] = utils.hash(contentToHash).substr(0, 10); | ||||
|     } | ||||
|  | ||||
|     async save() { | ||||
|         await repository.updateEntity(this); | ||||
|  | ||||
|         return this; | ||||
|     } | ||||
| } | ||||
|  | ||||
| module.exports = Entity; | ||||
							
								
								
									
										53
									
								
								src/entities/entity_constructor.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,53 @@ | ||||
| const Note = require('../entities/note'); | ||||
| const NoteRevision = require('../entities/note_revision'); | ||||
| const Image = require('../entities/image'); | ||||
| const NoteImage = require('../entities/note_image'); | ||||
| const Branch = require('../entities/branch'); | ||||
| const Label = require('../entities/label'); | ||||
| const RecentNote = require('../entities/recent_note'); | ||||
| const ApiToken = require('../entities/api_token'); | ||||
| const Option = require('../entities/option'); | ||||
| const repository = require('../services/repository'); | ||||
|  | ||||
| function createEntityFromRow(row) { | ||||
|     let entity; | ||||
|  | ||||
|     if (row.labelId) { | ||||
|         entity = new Label(row); | ||||
|     } | ||||
|     else if (row.noteRevisionId) { | ||||
|         entity = new NoteRevision(row); | ||||
|     } | ||||
|     else if (row.noteImageId) { | ||||
|         entity = new NoteImage(row); | ||||
|     } | ||||
|     else if (row.imageId) { | ||||
|         entity = new Image(row); | ||||
|     } | ||||
|     else if (row.branchId && row.notePath) { | ||||
|         entity = new RecentNote(row); | ||||
|     } | ||||
|     else if (row.apiTokenId) { | ||||
|         entity = new ApiToken(row); | ||||
|     } | ||||
|     else if (row.branchId) { | ||||
|         entity = new Branch(row); | ||||
|     } | ||||
|     else if (row.noteId) { | ||||
|         entity = new Note(row); | ||||
|     } | ||||
|     else if (row.name) { | ||||
|         entity = new Option(row); | ||||
|     } | ||||
|     else { | ||||
|         throw new Error('Unknown entity type for row: ' + JSON.stringify(row)); | ||||
|     } | ||||
|  | ||||
|     return entity; | ||||
| } | ||||
|  | ||||
| repository.setEntityConstructor(createEntityFromRow); | ||||
|  | ||||
| module.exports = { | ||||
|     createEntityFromRow | ||||
| }; | ||||
							
								
								
									
										26
									
								
								src/entities/image.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,26 @@ | ||||
| "use strict"; | ||||
|  | ||||
| const Entity = require('./entity'); | ||||
| const dateUtils = require('../services/date_utils'); | ||||
|  | ||||
| class Image extends Entity { | ||||
|     static get tableName() { return "images"; } | ||||
|     static get primaryKeyName() { return "imageId"; } | ||||
|     static get hashedProperties() { return ["imageId", "format", "checksum", "name", "isDeleted", "dateModified", "dateCreated"]; } | ||||
|  | ||||
|     beforeSaving() { | ||||
|         super.beforeSaving(); | ||||
|  | ||||
|         if (!this.isDeleted) { | ||||
|             this.isDeleted = false; | ||||
|         } | ||||
|  | ||||
|         if (!this.dateCreated) { | ||||
|             this.dateCreated = dateUtils.nowDate(); | ||||
|         } | ||||
|  | ||||
|         this.dateModified = dateUtils.nowDate(); | ||||
|     } | ||||
| } | ||||
|  | ||||
| module.exports = Image; | ||||
							
								
								
									
										41
									
								
								src/entities/label.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,41 @@ | ||||
| "use strict"; | ||||
|  | ||||
| const Entity = require('./entity'); | ||||
| const repository = require('../services/repository'); | ||||
| const dateUtils = require('../services/date_utils'); | ||||
| const sql = require('../services/sql'); | ||||
|  | ||||
| class Label extends Entity { | ||||
|     static get tableName() { return "labels"; } | ||||
|     static get primaryKeyName() { return "labelId"; } | ||||
|     static get hashedProperties() { return ["labelId", "noteId", "name", "value", "dateModified", "dateCreated"]; } | ||||
|  | ||||
|     async getNote() { | ||||
|         return await repository.getEntity("SELECT * FROM notes WHERE noteId = ?", [this.noteId]); | ||||
|     } | ||||
|  | ||||
|     async beforeSaving() { | ||||
|         super.beforeSaving(); | ||||
|  | ||||
|         if (!this.value) { | ||||
|             // null value isn't allowed | ||||
|             this.value = ""; | ||||
|         } | ||||
|  | ||||
|         if (this.position === undefined) { | ||||
|             this.position = 1 + await sql.getValue(`SELECT COALESCE(MAX(position), 0) FROM labels WHERE noteId = ?`, [this.noteId]); | ||||
|         } | ||||
|  | ||||
|         if (!this.isDeleted) { | ||||
|             this.isDeleted = false; | ||||
|         } | ||||
|  | ||||
|         if (!this.dateCreated) { | ||||
|             this.dateCreated = dateUtils.nowDate(); | ||||
|         } | ||||
|  | ||||
|         this.dateModified = dateUtils.nowDate(); | ||||
|     } | ||||
| } | ||||
|  | ||||
| module.exports = Label; | ||||
| @@ -1,50 +1,164 @@ | ||||
| "use strict"; | ||||
|  | ||||
| const Entity = require('./entity'); | ||||
| const protected_session = require('../services/protected_session'); | ||||
| const protectedSessionService = require('../services/protected_session'); | ||||
| const repository = require('../services/repository'); | ||||
| const dateUtils = require('../services/date_utils'); | ||||
|  | ||||
| class Note extends Entity { | ||||
|     static get tableName() { return "notes"; } | ||||
|     static get primaryKeyName() { return "noteId"; } | ||||
|     static get hashedProperties() { return ["noteId", "title", "content", "type", "dateModified", "isProtected", "isDeleted"]; } | ||||
|  | ||||
|     constructor(repository, row) { | ||||
|         super(repository, row); | ||||
|     constructor(row) { | ||||
|         super(row); | ||||
|  | ||||
|         if (this.isProtected) { | ||||
|             protected_session.decryptNote(this.dataKey, this); | ||||
|         // check if there's noteId, otherwise this is a new entity which wasn't encrypted yet | ||||
|         if (this.isProtected && this.noteId) { | ||||
|             protectedSessionService.decryptNote(this); | ||||
|         } | ||||
|  | ||||
|         if (this.isJson()) { | ||||
|         this.setContent(this.content); | ||||
|     } | ||||
|  | ||||
|     setContent(content) { | ||||
|         this.content = content; | ||||
|  | ||||
|         try { | ||||
|             this.jsonContent = JSON.parse(this.content); | ||||
|         } | ||||
|         catch(e) {} | ||||
|     } | ||||
|  | ||||
|     isJson() { | ||||
|         return this.type === "code" && this.mime === "application/json"; | ||||
|         return this.mime === "application/json"; | ||||
|     } | ||||
|  | ||||
|     async getAttributes() { | ||||
|         return this.repository.getEntities("SELECT * FROM attributes WHERE noteId = ? AND isDeleted = 0", [this.noteId]); | ||||
|     isJavaScript() { | ||||
|         return (this.type === "code" || this.type === "file") | ||||
|             && (this.mime.startsWith("application/javascript") || this.mime === "application/x-javascript"); | ||||
|     } | ||||
|  | ||||
|     async getAttribute(name) { | ||||
|         return this.repository.getEntity("SELECT * FROM attributes WHERE noteId = ? AND name = ?", [this.noteId, name]); | ||||
|     isHtml() { | ||||
|         return (this.type === "code" || this.type === "file" || this.type === "render") && this.mime === "text/html"; | ||||
|     } | ||||
|  | ||||
|     getScriptEnv() { | ||||
|         if (this.isHtml() || (this.isJavaScript() && this.mime.endsWith('env=frontend'))) { | ||||
|             return "frontend"; | ||||
|         } | ||||
|  | ||||
|         if (this.type === 'render') { | ||||
|             return "frontend"; | ||||
|         } | ||||
|  | ||||
|         if (this.isJavaScript() && this.mime.endsWith('env=backend')) { | ||||
|             return "backend"; | ||||
|         } | ||||
|  | ||||
|         return null; | ||||
|     } | ||||
|  | ||||
|     async getLabels() { | ||||
|         return await repository.getEntities("SELECT * FROM labels WHERE noteId = ? AND isDeleted = 0", [this.noteId]); | ||||
|     } | ||||
|  | ||||
|     // WARNING: this doesn't take into account the possibility to have multi-valued labels! | ||||
|     async getLabelMap() { | ||||
|         const map = {}; | ||||
|  | ||||
|         for (const label of await this.getLabels()) { | ||||
|             map[label.name] = label.value; | ||||
|         } | ||||
|  | ||||
|         return map; | ||||
|     } | ||||
|  | ||||
|     async hasLabel(name) { | ||||
|         const map = await this.getLabelMap(); | ||||
|  | ||||
|         return map.hasOwnProperty(name); | ||||
|     } | ||||
|  | ||||
|     // WARNING: this doesn't take into account the possibility to have multi-valued labels! | ||||
|     async getLabel(name) { | ||||
|         return await repository.getEntity("SELECT * FROM labels WHERE noteId = ? AND name = ?", [this.noteId, name]); | ||||
|     } | ||||
|  | ||||
|     async getRevisions() { | ||||
|         return this.repository.getEntities("SELECT * FROM note_revisions WHERE noteId = ?", [this.noteId]); | ||||
|         return await repository.getEntities("SELECT * FROM note_revisions WHERE noteId = ?", [this.noteId]); | ||||
|     } | ||||
|  | ||||
|     async getTrees() { | ||||
|         return this.repository.getEntities("SELECT * FROM note_tree WHERE isDeleted = 0 AND noteId = ?", [this.noteId]); | ||||
|     async getNoteImages() { | ||||
|         return await repository.getEntities("SELECT * FROM note_images WHERE noteId = ? AND isDeleted = 0", [this.noteId]); | ||||
|     } | ||||
|  | ||||
|     async getBranches() { | ||||
|         return await repository.getEntities("SELECT * FROM branches WHERE isDeleted = 0 AND noteId = ?", [this.noteId]); | ||||
|     } | ||||
|  | ||||
|     async getChildNote(name) { | ||||
|         return await repository.getEntity(` | ||||
|           SELECT notes.*  | ||||
|           FROM branches  | ||||
|             JOIN notes USING(noteId)  | ||||
|           WHERE notes.isDeleted = 0 | ||||
|                 AND branches.isDeleted = 0 | ||||
|                 AND branches.parentNoteId = ? | ||||
|                 AND notes.title = ?`, [this.noteId, name]); | ||||
|     } | ||||
|  | ||||
|     async getChildNotes() { | ||||
|         return await repository.getEntities(` | ||||
|           SELECT notes.*  | ||||
|           FROM branches  | ||||
|             JOIN notes USING(noteId)  | ||||
|           WHERE notes.isDeleted = 0 | ||||
|                 AND branches.isDeleted = 0 | ||||
|                 AND branches.parentNoteId = ? | ||||
|           ORDER BY branches.notePosition`, [this.noteId]); | ||||
|     } | ||||
|  | ||||
|     async getChildBranches() { | ||||
|         return await repository.getEntities(` | ||||
|           SELECT branches.*  | ||||
|           FROM branches  | ||||
|           WHERE branches.isDeleted = 0 | ||||
|                 AND branches.parentNoteId = ? | ||||
|           ORDER BY branches.notePosition`, [this.noteId]); | ||||
|     } | ||||
|  | ||||
|     async getParentNotes() { | ||||
|         return await repository.getEntities(` | ||||
|           SELECT parent_notes.*  | ||||
|           FROM  | ||||
|             branches AS child_tree  | ||||
|             JOIN notes AS parent_notes ON parent_notes.noteId = child_tree.parentNoteId  | ||||
|           WHERE child_tree.noteId = ? | ||||
|                 AND child_tree.isDeleted = 0 | ||||
|                 AND parent_notes.isDeleted = 0`, [this.noteId]); | ||||
|     } | ||||
|  | ||||
|     beforeSaving() { | ||||
|         super.beforeSaving(); | ||||
|  | ||||
|         if (this.isJson() && this.jsonContent) { | ||||
|             this.content = JSON.stringify(this.jsonContent, null, '\t'); | ||||
|         } | ||||
|  | ||||
|         if (this.isProtected) { | ||||
|             protected_session.encryptNote(this.dataKey, this); | ||||
|             protectedSessionService.encryptNote(this); | ||||
|         } | ||||
|  | ||||
|         if (!this.isDeleted) { | ||||
|             this.isDeleted = false; | ||||
|         } | ||||
|  | ||||
|         if (!this.dateCreated) { | ||||
|             this.dateCreated = dateUtils.nowDate(); | ||||
|         } | ||||
|  | ||||
|         this.dateModified = dateUtils.nowDate(); | ||||
|     } | ||||
| } | ||||
|  | ||||
|   | ||||
							
								
								
									
										35
									
								
								src/entities/note_image.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,35 @@ | ||||
| "use strict"; | ||||
|  | ||||
| const Entity = require('./entity'); | ||||
| const repository = require('../services/repository'); | ||||
| const dateUtils = require('../services/date_utils'); | ||||
|  | ||||
| class NoteImage extends Entity { | ||||
|     static get tableName() { return "note_images"; } | ||||
|     static get primaryKeyName() { return "noteImageId"; } | ||||
|     static get hashedProperties() { return ["noteImageId", "noteId", "imageId", "isDeleted", "dateModified", "dateCreated"]; } | ||||
|  | ||||
|     async getNote() { | ||||
|         return await repository.getEntity("SELECT * FROM notes WHERE noteId = ?", [this.noteId]); | ||||
|     } | ||||
|  | ||||
|     async getImage() { | ||||
|         return await repository.getEntity("SELECT * FROM images WHERE imageId = ?", [this.imageId]); | ||||
|     } | ||||
|  | ||||
|     beforeSaving() { | ||||
|         super.beforeSaving(); | ||||
|  | ||||
|         if (!this.isDeleted) { | ||||
|             this.isDeleted = false; | ||||
|         } | ||||
|  | ||||
|         if (!this.dateCreated) { | ||||
|             this.dateCreated = dateUtils.nowDate(); | ||||
|         } | ||||
|  | ||||
|         this.dateModified = dateUtils.nowDate(); | ||||
|     } | ||||
| } | ||||
|  | ||||
| module.exports = NoteImage; | ||||
| @@ -1,13 +1,32 @@ | ||||
| "use strict"; | ||||
|  | ||||
| const Entity = require('./entity'); | ||||
| const protectedSessionService = require('../services/protected_session'); | ||||
| const repository = require('../services/repository'); | ||||
|  | ||||
| class NoteRevision extends Entity { | ||||
|     static get tableName() { return "note_revisions"; } | ||||
|     static get primaryKeyName() { return "noteRevisionId"; } | ||||
|     static get hashedProperties() { return ["noteRevisionId", "noteId", "title", "content", "dateModifiedFrom", "dateModifiedTo"]; } | ||||
|  | ||||
|     constructor(row) { | ||||
|         super(row); | ||||
|  | ||||
|         if (this.isProtected) { | ||||
|             protectedSessionService.decryptNoteRevision(this); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     async getNote() { | ||||
|         return this.repository.getEntity("SELECT * FROM notes WHERE noteId = ?", [this.noteId]); | ||||
|         return await repository.getEntity("SELECT * FROM notes WHERE noteId = ?", [this.noteId]); | ||||
|     } | ||||
|  | ||||
|     beforeSaving() { | ||||
|         super.beforeSaving(); | ||||
|  | ||||
|         if (this.isProtected) { | ||||
|             protectedSessionService.encryptNoteRevision(this); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -1,18 +0,0 @@ | ||||
| "use strict"; | ||||
|  | ||||
| const Entity = require('./entity'); | ||||
|  | ||||
| class NoteTree extends Entity { | ||||
|     static get tableName() { return "note_tree"; } | ||||
|     static get primaryKeyName() { return "noteTreeId"; } | ||||
|  | ||||
|     async getNote() { | ||||
|         return this.repository.getEntity("SELECT * FROM note_tree WHERE isDeleted = 0 AND noteId = ?", [this.noteId]); | ||||
|     } | ||||
|  | ||||
|     async getParentNote() { | ||||
|         return this.repository.getEntity("SELECT * FROM note_tree WHERE isDeleted = 0 AND parentNoteId = ?", [this.parentNoteId]); | ||||
|     } | ||||
| } | ||||
|  | ||||
| module.exports = NoteTree; | ||||
							
								
								
									
										18
									
								
								src/entities/option.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,18 @@ | ||||
| "use strict"; | ||||
|  | ||||
| const Entity = require('./entity'); | ||||
| const dateUtils = require('../services/date_utils'); | ||||
|  | ||||
| class Option extends Entity { | ||||
|     static get tableName() { return "options"; } | ||||
|     static get primaryKeyName() { return "name"; } | ||||
|     static get hashedProperties() { return ["name", "value"]; } | ||||
|  | ||||
|     beforeSaving() { | ||||
|         super.beforeSaving(); | ||||
|  | ||||
|         this.dateModified = dateUtils.nowDate(); | ||||
|     } | ||||
| } | ||||
|  | ||||
| module.exports = Option; | ||||
							
								
								
									
										24
									
								
								src/entities/recent_note.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,24 @@ | ||||
| "use strict"; | ||||
|  | ||||
| const Entity = require('./entity'); | ||||
| const dateUtils = require('../services/date_utils'); | ||||
|  | ||||
| class RecentNote extends Entity { | ||||
|     static get tableName() { return "recent_notes"; } | ||||
|     static get primaryKeyName() { return "branchId"; } | ||||
|     static get hashedProperties() { return ["branchId", "notePath", "dateCreated", "isDeleted"]; } | ||||
|  | ||||
|     beforeSaving() { | ||||
|         super.beforeSaving(); | ||||
|  | ||||
|         if (!this.isDeleted) { | ||||
|             this.isDeleted = false; | ||||
|         } | ||||
|  | ||||
|         if (!this.dateCreated) { | ||||
|             this.dateCreated = dateUtils.nowDate(); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| module.exports = RecentNote; | ||||
| @@ -1,144 +0,0 @@ | ||||
| "use strict"; | ||||
|  | ||||
| const sql = require('../services/sql'); | ||||
| const notes = require('../services/notes'); | ||||
| const axios = require('axios'); | ||||
| const log = require('../services/log'); | ||||
| const utils = require('../services/utils'); | ||||
| const unescape = require('unescape'); | ||||
| const attributes = require('../services/attributes'); | ||||
| const sync_mutex = require('../services/sync_mutex'); | ||||
| const config = require('../services/config'); | ||||
| const date_notes = require('../services/date_notes'); | ||||
|  | ||||
| // "reddit" date note is subnote of date note which contains all reddit comments from that date | ||||
| const REDDIT_DATE_ATTRIBUTE = 'reddit_date_note'; | ||||
|  | ||||
| async function createNote(parentNoteId, noteTitle, noteText) { | ||||
|     return (await notes.createNewNote(parentNoteId, { | ||||
|         title: noteTitle, | ||||
|         content: noteText, | ||||
|         target: 'into', | ||||
|         isProtected: false | ||||
|     })).noteId; | ||||
| } | ||||
|  | ||||
| function redditId(kind, id) { | ||||
|     return kind + "_" + id; | ||||
| } | ||||
|  | ||||
| async function getDateNoteIdForReddit(dateTimeStr, rootNoteId) { | ||||
|     const dateStr = dateTimeStr.substr(0, 10); | ||||
|  | ||||
|     let redditDateNoteId = await attributes.getNoteIdWithAttribute(REDDIT_DATE_ATTRIBUTE, dateStr); | ||||
|  | ||||
|     if (!redditDateNoteId) { | ||||
|         const dateNoteId = await date_notes.getDateNoteId(dateTimeStr, rootNoteId); | ||||
|  | ||||
|         redditDateNoteId = await createNote(dateNoteId, "Reddit"); | ||||
|  | ||||
|         await attributes.createAttribute(redditDateNoteId, REDDIT_DATE_ATTRIBUTE, dateStr); | ||||
|         await attributes.createAttribute(redditDateNoteId, "hide_in_autocomplete"); | ||||
|     } | ||||
|  | ||||
|     return redditDateNoteId; | ||||
| } | ||||
|  | ||||
| async function importComments(rootNoteId, accountName, afterId = null) { | ||||
|     let url = `https://www.reddit.com/user/${accountName}.json`; | ||||
|  | ||||
|     if (afterId) { | ||||
|         url += "?after=" + afterId; | ||||
|     } | ||||
|  | ||||
|     const response = await axios.get(url); | ||||
|     const listing = response.data; | ||||
|  | ||||
|     if (listing.kind !== 'Listing') { | ||||
|         log.info(`Reddit: Unknown object kind ${listing.kind}`); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     const children = listing.data.children; | ||||
|  | ||||
|     let importedComments = 0; | ||||
|  | ||||
|     for (const child of children) { | ||||
|         const comment = child.data; | ||||
|  | ||||
|         let commentNoteId = await attributes.getNoteIdWithAttribute('reddit_id', redditId(child.kind, comment.id)); | ||||
|  | ||||
|         if (commentNoteId) { | ||||
|             continue; | ||||
|         } | ||||
|  | ||||
|         const dateTimeStr = utils.dateStr(new Date(comment.created_utc * 1000)); | ||||
|  | ||||
|         const permaLink = 'https://reddit.com' + comment.permalink; | ||||
|  | ||||
|         const noteText = | ||||
| `<p><a href="${permaLink}">${permaLink}</a></p> | ||||
| <p>author: <a href="https://reddit.com/u/${comment.author}">${comment.author}</a>,  | ||||
| subreddit: <a href="https://reddit.com/r/${comment.subreddit}">${comment.subreddit}</a>,  | ||||
| karma: ${comment.score}, created at ${dateTimeStr}</p><p></p>` | ||||
|             + unescape(comment.body_html); | ||||
|  | ||||
|         let parentNoteId = await getDateNoteIdForReddit(dateTimeStr, rootNoteId); | ||||
|  | ||||
|         await sql.doInTransaction(async () => { | ||||
|             commentNoteId = await createNote(parentNoteId, comment.link_title, noteText); | ||||
|  | ||||
|             log.info("Reddit: Imported comment to note " + commentNoteId); | ||||
|             importedComments++; | ||||
|  | ||||
|             await attributes.createAttribute(commentNoteId, "reddit_kind", child.kind); | ||||
|             await attributes.createAttribute(commentNoteId, "reddit_id", redditId(child.kind, comment.id)); | ||||
|             await attributes.createAttribute(commentNoteId, "reddit_created_utc", comment.created_utc); | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     // if there have been no imported comments on this page, there shouldn't be any to import | ||||
|     // on the next page since those are older | ||||
|     if (listing.data.after && importedComments > 0) { | ||||
|         importedComments += await importComments(rootNoteId, accountName, listing.data.after); | ||||
|     } | ||||
|  | ||||
|     return importedComments; | ||||
| } | ||||
|  | ||||
| let redditAccounts = []; | ||||
|  | ||||
| async function runImport() { | ||||
|     const rootNoteId = await date_notes.getRootNoteId(); | ||||
|  | ||||
|     // technically mutex shouldn't be necessary but we want to avoid doing potentially expensive import | ||||
|     // concurrently with sync | ||||
|     await sync_mutex.doExclusively(async () => { | ||||
|         let importedComments = 0; | ||||
|  | ||||
|         for (const account of redditAccounts) { | ||||
|             importedComments += await importComments(rootNoteId, account); | ||||
|         } | ||||
|  | ||||
|         log.info(`Reddit: Imported ${importedComments} comments.`); | ||||
|     }); | ||||
| } | ||||
|  | ||||
| sql.dbReady.then(async () => { | ||||
|     if (!config['Reddit'] || config['Reddit']['enabled'] !== true) { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     const redditAccountsStr = config['Reddit']['accounts']; | ||||
|  | ||||
|     if (!redditAccountsStr) { | ||||
|         log.info("Reddit: No reddit accounts defined in option 'reddit_accounts'"); | ||||
|     } | ||||
|  | ||||
|     redditAccounts = redditAccountsStr.split(",").map(s => s.trim()); | ||||
|  | ||||
|     const pollingIntervalInSeconds = config['Reddit']['pollingIntervalInSeconds'] || (4 * 3600); | ||||
|  | ||||
|     setInterval(runImport, pollingIntervalInSeconds * 1000); | ||||
|     setTimeout(runImport, 10000); // 10 seconds after startup - intentionally after initial sync | ||||
| }); | ||||
							
								
								
									
										
											BIN
										
									
								
								src/public/images/icons/back-24.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 511 B | 
| Before Width: | Height: | Size: 245 B After Width: | Height: | Size: 245 B | 
| Before Width: | Height: | Size: 339 B After Width: | Height: | Size: 339 B | 
| Before Width: | Height: | Size: 463 B After Width: | Height: | Size: 463 B | 
							
								
								
									
										
											BIN
										
									
								
								src/public/images/icons/edit-20.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 312 B | 
| Before Width: | Height: | Size: 288 B After Width: | Height: | Size: 288 B | 
| Before Width: | Height: | Size: 284 B After Width: | Height: | Size: 284 B | 
| Before Width: | Height: | Size: 292 B After Width: | Height: | Size: 292 B | 
							
								
								
									
										
											BIN
										
									
								
								src/public/images/icons/forward-24.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 511 B | 
| Before Width: | Height: | Size: 155 B After Width: | Height: | Size: 155 B | 
| Before Width: | Height: | Size: 323 B | 
							
								
								
									
										
											BIN
										
									
								
								src/public/images/icons/paperclip-16.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 358 B | 
							
								
								
									
										
											BIN
										
									
								
								src/public/images/icons/play-16.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 252 B | 
							
								
								
									
										
											BIN
										
									
								
								src/public/images/icons/play-20.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 288 B | 
							
								
								
									
										
											BIN
										
									
								
								src/public/images/icons/save-20.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 388 B | 
							
								
								
									
										
											BIN
										
									
								
								src/public/images/icons/search-20.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 431 B | 
| Before Width: | Height: | Size: 419 B After Width: | Height: | Size: 419 B | 
							
								
								
									
										
											BIN
										
									
								
								src/public/images/icons/search-small-16.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 354 B | 
							
								
								
									
										
											BIN
										
									
								
								src/public/images/icons/shield-20.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 388 B | 
							
								
								
									
										
											BIN
										
									
								
								src/public/images/icons/shield-off-20.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 462 B | 
							
								
								
									
										
											BIN
										
									
								
								src/public/images/icons/tree-root-16.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 240 B | 
| Before Width: | Height: | Size: 337 B | 
							
								
								
									
										
											BIN
										
									
								
								src/public/images/icons/x-20.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 259 B | 
							
								
								
									
										1
									
								
								src/public/images/trilium.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1 @@ | ||||
| <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" x="0px" y="0px" viewBox="0 0 100 100" enable-background="new 0 0 100 100" xml:space="preserve"><path d="M63.966,45.043c0.008-0.009,0.021-0.021,0.027-0.029c0.938-1.156-0.823-13.453-5.063-20.125  c-1.389-2.186-2.239-3.423-3.219-4.719c-3.907-5.166-6-6.125-6-6.125S35.732,24.78,36.149,44.315  c-1.754,0.065-11.218,7.528-14.826,14.388c-1.206,2.291-1.856,3.645-2.493,5.141c-2.539,5.957-2.33,8.25-2.33,8.25  s16.271,6.79,33.014-3.294c0.007,0.021,0.013,0.046,0.02,0.063c0.537,1.389,12.08,5.979,19.976,5.621  c2.587-0.116,4.084-0.238,5.696-0.444c6.424-0.818,8.298-2.157,8.298-2.157S81.144,54.396,63.966,45.043z M50.787,65.343  c1.059-1.183,4.648-5.853,0.995-11.315c-0.253-0.377-0.496-0.236-0.496-0.236s0.063,10.822-5.162,12.359  c-5.225,1.537-13.886,4.4-20.427,0.455C25,66.186,26.924,53.606,38.544,47.229c0.546,1.599,2.836,6.854,9.292,6.409  c0.453-0.031,0.453-0.313,0.453-0.313s-9.422-5.328-8.156-10.625s3.089-14.236,9.766-17.948c0.714-0.397,10.746,7.593,10.417,20.94  c-1.606-0.319-7.377-1.004-10.226,4.864c-0.198,0.409,0.046,0.549,0.046,0.549s9.31-5.521,13.275-1.789  c3.965,3.733,10.813,9.763,10.71,17.4C74.111,67.533,62.197,72.258,50.787,65.343z M35.613,35.145c0,0-0.991,3.241-0.603,7.524  l-13.393-7.524C21.618,35.145,27.838,30.931,35.613,35.145z M21.193,36.03l13.344,7.612c-3.872,1.872-6.142,4.388-6.142,4.388  C20.78,43.531,21.193,36.03,21.193,36.03z M72.287,49.064c0,0-2.321-2.471-6.23-4.263l13.187-7.881  C79.243,36.92,79.808,44.413,72.287,49.064z M78.687,36.113l-13.237,7.794c0.3-4.291-0.754-7.511-0.754-7.511  C72.383,32.025,78.687,36.113,78.687,36.113z M42.076,73.778c0,0,3.309-0.737,6.845-3.185l0.056,15.361  C48.977,85.955,42.244,82.621,42.076,73.778z M49.956,85.888L50,70.526c3.539,2.445,6.846,3.181,6.846,3.181  C56.686,82.551,49.956,85.888,49.956,85.888z"></path></svg> | ||||
| After Width: | Height: | Size: 1.8 KiB | 
| @@ -1,21 +0,0 @@ | ||||
| const api = (function() { | ||||
|     const pluginButtonsEl = $("#plugin-buttons"); | ||||
|  | ||||
|     async function activateNote(notePath) { | ||||
|         await noteTree.activateNode(notePath); | ||||
|     } | ||||
|  | ||||
|     function addButtonToToolbar(buttonId, button) { | ||||
|         $("#" + buttonId).remove(); | ||||
|  | ||||
|         button.attr('id', buttonId); | ||||
|  | ||||
|         pluginButtonsEl.append(button); | ||||
|     } | ||||
|  | ||||
|  | ||||
|     return { | ||||
|         addButtonToToolbar, | ||||
|         activateNote | ||||
|     } | ||||
| })(); | ||||
| @@ -1,33 +0,0 @@ | ||||
| "use strict"; | ||||
|  | ||||
| const cloning = (function() { | ||||
|     async function cloneNoteTo(childNoteId, parentNoteId, prefix) { | ||||
|         const resp = await server.put('notes/' + childNoteId + '/clone-to/' + parentNoteId, { | ||||
|             prefix: prefix | ||||
|         }); | ||||
|  | ||||
|         if (!resp.success) { | ||||
|             alert(resp.message); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         await noteTree.reload(); | ||||
|     } | ||||
|  | ||||
|     // beware that first arg is noteId and second is noteTreeId! | ||||
|     async function cloneNoteAfter(noteId, afterNoteTreeId) { | ||||
|         const resp = await server.put('notes/' + noteId + '/clone-after/' + afterNoteTreeId); | ||||
|  | ||||
|         if (!resp.success) { | ||||
|             alert(resp.message); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         await noteTree.reload(); | ||||
|     } | ||||
|  | ||||
|     return { | ||||
|         cloneNoteAfter, | ||||
|         cloneNoteTo | ||||
|     }; | ||||
| })(); | ||||
| @@ -1,164 +0,0 @@ | ||||
| "use strict"; | ||||
|  | ||||
| const contextMenu = (function() { | ||||
|     const treeEl = $("#tree"); | ||||
|  | ||||
|     let clipboardIds = []; | ||||
|     let clipboardMode = null; | ||||
|  | ||||
|     async function pasteAfter(node) { | ||||
|         if (clipboardMode === 'cut') { | ||||
|             const nodes = clipboardIds.map(nodeKey => treeUtils.getNodeByKey(nodeKey)); | ||||
|  | ||||
|             await treeChanges.moveAfterNode(nodes, node); | ||||
|  | ||||
|             clipboardIds = []; | ||||
|             clipboardMode = null; | ||||
|         } | ||||
|         else if (clipboardMode === 'copy') { | ||||
|             for (const noteId of clipboardIds) { | ||||
|                 await cloning.cloneNoteAfter(noteId, node.data.noteTreeId); | ||||
|             } | ||||
|  | ||||
|             // copy will keep clipboardIds and clipboardMode so it's possible to paste into multiple places | ||||
|         } | ||||
|         else if (clipboardIds.length === 0) { | ||||
|             // just do nothing | ||||
|         } | ||||
|         else { | ||||
|             throwError("Unrecognized clipboard mode=" + clipboardMode); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     async function pasteInto(node) { | ||||
|         if (clipboardMode === 'cut') { | ||||
|             const nodes = clipboardIds.map(nodeKey => treeUtils.getNodeByKey(nodeKey)); | ||||
|  | ||||
|             await treeChanges.moveToNode(nodes, node); | ||||
|  | ||||
|             clipboardIds = []; | ||||
|             clipboardMode = null; | ||||
|         } | ||||
|         else if (clipboardMode === 'copy') { | ||||
|             for (const noteId of clipboardIds) { | ||||
|                 await cloning.cloneNoteTo(noteId, node.data.noteId); | ||||
|             } | ||||
|             // copy will keep clipboardIds and clipboardMode so it's possible to paste into multiple places | ||||
|         } | ||||
|         else if (clipboardIds.length === 0) { | ||||
|             // just do nothing | ||||
|         } | ||||
|         else { | ||||
|             throwError("Unrecognized clipboard mode=" + mode); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     function copy(nodes) { | ||||
|         clipboardIds = nodes.map(node => node.data.noteId); | ||||
|         clipboardMode = 'copy'; | ||||
|  | ||||
|         showMessage("Note(s) have been copied into clipboard."); | ||||
|     } | ||||
|  | ||||
|     function cut(nodes) { | ||||
|         clipboardIds = nodes.map(node => node.key); | ||||
|         clipboardMode = 'cut'; | ||||
|  | ||||
|         showMessage("Note(s) have been cut into clipboard."); | ||||
|     } | ||||
|  | ||||
|     const contextMenuSettings = { | ||||
|         delegate: "span.fancytree-title", | ||||
|         autoFocus: true, | ||||
|         menu: [ | ||||
|             {title: "Insert note here <kbd>Ctrl+O</kbd>", cmd: "insertNoteHere", uiIcon: "ui-icon-plus"}, | ||||
|             {title: "Insert child note <kbd>Ctrl+P</kbd>", cmd: "insertChildNote", uiIcon: "ui-icon-plus"}, | ||||
|             {title: "Delete <kbd>Ctrl+Del</kbd>", cmd: "delete", uiIcon: "ui-icon-trash"}, | ||||
|             {title: "----"}, | ||||
|             {title: "Edit tree prefix <kbd>F2</kbd>", cmd: "editTreePrefix", uiIcon: "ui-icon-pencil"}, | ||||
|             {title: "----"}, | ||||
|             {title: "Protect sub-tree", cmd: "protectSubTree", uiIcon: "ui-icon-locked"}, | ||||
|             {title: "Unprotect sub-tree", cmd: "unprotectSubTree", uiIcon: "ui-icon-unlocked"}, | ||||
|             {title: "----"}, | ||||
|             {title: "Copy / clone <kbd>Ctrl+C</kbd>", cmd: "copy", uiIcon: "ui-icon-copy"}, | ||||
|             {title: "Cut <kbd>Ctrl+X</kbd>", cmd: "cut", uiIcon: "ui-icon-scissors"}, | ||||
|             {title: "Paste into <kbd>Ctrl+V</kbd>", cmd: "pasteInto", uiIcon: "ui-icon-clipboard"}, | ||||
|             {title: "Paste after", cmd: "pasteAfter", uiIcon: "ui-icon-clipboard"}, | ||||
|             {title: "----"}, | ||||
|             {title: "Collapse sub-tree <kbd>Alt+-</kbd>", cmd: "collapse-sub-tree", uiIcon: "ui-icon-minus"}, | ||||
|             {title: "Force note sync", cmd: "force-note-sync", uiIcon: "ui-icon-refresh"}, | ||||
|             {title: "Sort alphabetically <kbd>Alt+S</kbd>", cmd: "sort-alphabetically", uiIcon: " ui-icon-arrowthick-2-n-s"} | ||||
|  | ||||
|         ], | ||||
|         beforeOpen: (event, ui) => { | ||||
|             const node = $.ui.fancytree.getNode(ui.target); | ||||
|             // Modify menu entries depending on node status | ||||
|             treeEl.contextmenu("enableEntry", "pasteAfter", clipboardIds.length > 0); | ||||
|             treeEl.contextmenu("enableEntry", "pasteInto", clipboardIds.length > 0); | ||||
|  | ||||
|             // Activate node on right-click | ||||
|             node.setActive(); | ||||
|             // Disable tree keyboard handling | ||||
|             ui.menu.prevKeyboard = node.tree.options.keyboard; | ||||
|             node.tree.options.keyboard = false; | ||||
|         }, | ||||
|         close: (event, ui) => {}, | ||||
|         select: (event, ui) => { | ||||
|             const node = $.ui.fancytree.getNode(ui.target); | ||||
|  | ||||
|             if (ui.cmd === "insertNoteHere") { | ||||
|                 const parentNoteId = node.data.parentNoteId; | ||||
|                 const isProtected = treeUtils.getParentProtectedStatus(node); | ||||
|  | ||||
|                 noteTree.createNote(node, parentNoteId, 'after', isProtected); | ||||
|             } | ||||
|             else if (ui.cmd === "insertChildNote") { | ||||
|                 noteTree.createNote(node, node.data.noteId, 'into'); | ||||
|             } | ||||
|             else if (ui.cmd === "editTreePrefix") { | ||||
|                 editTreePrefix.showDialog(node); | ||||
|             } | ||||
|             else if (ui.cmd === "protectSubTree") { | ||||
|                 protected_session.protectSubTree(node.data.noteId, true); | ||||
|             } | ||||
|             else if (ui.cmd === "unprotectSubTree") { | ||||
|                 protected_session.protectSubTree(node.data.noteId, false); | ||||
|             } | ||||
|             else if (ui.cmd === "copy") { | ||||
|                 copy(noteTree.getSelectedNodes()); | ||||
|             } | ||||
|             else if (ui.cmd === "cut") { | ||||
|                 cut(noteTree.getSelectedNodes()); | ||||
|             } | ||||
|             else if (ui.cmd === "pasteAfter") { | ||||
|                 pasteAfter(node); | ||||
|             } | ||||
|             else if (ui.cmd === "pasteInto") { | ||||
|                 pasteInto(node); | ||||
|             } | ||||
|             else if (ui.cmd === "delete") { | ||||
|                 treeChanges.deleteNodes(noteTree.getSelectedNodes(true)); | ||||
|             } | ||||
|             else if (ui.cmd === "collapse-sub-tree") { | ||||
|                 noteTree.collapseTree(node); | ||||
|             } | ||||
|             else if (ui.cmd === "force-note-sync") { | ||||
|                 forceNoteSync(node.data.noteId); | ||||
|             } | ||||
|             else if (ui.cmd === "sort-alphabetically") { | ||||
|                 noteTree.sortAlphabetically(node.data.noteId); | ||||
|             } | ||||
|             else { | ||||
|                 messaging.logError("Unknown command: " + ui.cmd); | ||||
|             } | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     return { | ||||
|         pasteAfter, | ||||
|         pasteInto, | ||||
|         cut, | ||||
|         copy, | ||||
|         contextMenuSettings | ||||
|     } | ||||
| })(); | ||||
| @@ -1,28 +1,33 @@ | ||||
| "use strict"; | ||||
| import cloningService from '../services/cloning.js'; | ||||
| import linkService from '../services/link.js'; | ||||
| import noteDetailService from '../services/note_detail.js'; | ||||
| import treeUtils from '../services/tree_utils.js'; | ||||
| import server from "../services/server.js"; | ||||
| import noteDetailText from "../services/note_detail_text.js"; | ||||
|  | ||||
| const addLink = (function() { | ||||
|     const $dialog = $("#add-link-dialog"); | ||||
|     const $form = $("#add-link-form"); | ||||
|     const $autoComplete = $("#note-autocomplete"); | ||||
|     const $linkTitle = $("#link-title"); | ||||
|     const $clonePrefix = $("#clone-prefix"); | ||||
|     const $linkTitleFormGroup = $("#add-link-title-form-group"); | ||||
|     const $prefixFormGroup = $("#add-link-prefix-form-group"); | ||||
|     const $linkTypes = $("input[name='add-link-type']"); | ||||
|     const $linkTypeHtml = $linkTypes.filter('input[value="html"]'); | ||||
| const $dialog = $("#add-link-dialog"); | ||||
| const $form = $("#add-link-form"); | ||||
| const $autoComplete = $("#note-autocomplete"); | ||||
| const $linkTitle = $("#link-title"); | ||||
| const $clonePrefix = $("#clone-prefix"); | ||||
| const $linkTitleFormGroup = $("#add-link-title-form-group"); | ||||
| const $prefixFormGroup = $("#add-link-prefix-form-group"); | ||||
| const $linkTypeDiv = $("#add-link-type-div"); | ||||
| const $linkTypes = $("input[name='add-link-type']"); | ||||
| const $linkTypeHtml = $linkTypes.filter('input[value="html"]'); | ||||
|  | ||||
|     function setLinkType(linkType) { | ||||
| function setLinkType(linkType) { | ||||
|     $linkTypes.each(function () { | ||||
|         $(this).prop('checked', $(this).val() === linkType); | ||||
|     }); | ||||
|  | ||||
|     linkTypeChanged(); | ||||
|     } | ||||
| } | ||||
|  | ||||
|     function showDialog() { | ||||
| async function showDialog() { | ||||
|     glob.activeDialog = $dialog; | ||||
|  | ||||
|         if (noteEditor.getCurrentNoteType() === 'text') { | ||||
|     if (noteDetailService.getCurrentNoteType() === 'text') { | ||||
|         $linkTypeHtml.prop('disabled', false); | ||||
|  | ||||
|         setLinkType('html'); | ||||
| @@ -42,18 +47,30 @@ const addLink = (function() { | ||||
|     $clonePrefix.val(''); | ||||
|     $linkTitle.val(''); | ||||
|  | ||||
|         function setDefaultLinkTitle(noteId) { | ||||
|             const noteTitle = noteTree.getNoteTitle(noteId); | ||||
|     async function setDefaultLinkTitle(noteId) { | ||||
|         const noteTitle = await treeUtils.getNoteTitle(noteId); | ||||
|  | ||||
|         $linkTitle.val(noteTitle); | ||||
|     } | ||||
|  | ||||
|     $autoComplete.autocomplete({ | ||||
|             source: noteTree.getAutocompleteItems(), | ||||
|             minLength: 0, | ||||
|             change: () => { | ||||
|         source: async function(request, response) { | ||||
|             const result = await server.get('autocomplete?query=' + encodeURIComponent(request.term)); | ||||
|  | ||||
|             if (result.length > 0) { | ||||
|                 response(result); | ||||
|             } | ||||
|             else { | ||||
|                 response([{ | ||||
|                     label: "No results", | ||||
|                     value: "No results" | ||||
|                 }]); | ||||
|             } | ||||
|         }, | ||||
|         minLength: 2, | ||||
|         change: async () => { | ||||
|             const val = $autoComplete.val(); | ||||
|                 const notePath = link.getNodePathFromLabel(val); | ||||
|             const notePath = linkService.getNodePathFromLabel(val); | ||||
|             if (!notePath) { | ||||
|                 return; | ||||
|             } | ||||
| @@ -61,24 +78,24 @@ const addLink = (function() { | ||||
|             const noteId = treeUtils.getNoteIdFromNotePath(notePath); | ||||
|  | ||||
|             if (noteId) { | ||||
|                     setDefaultLinkTitle(noteId); | ||||
|                 await setDefaultLinkTitle(noteId); | ||||
|             } | ||||
|         }, | ||||
|         // this is called when user goes through autocomplete list with keyboard | ||||
|         // at this point the item isn't selected yet so we use supplied ui.item to see WHERE the cursor is | ||||
|             focus: (event, ui) => { | ||||
|                 const notePath = link.getNodePathFromLabel(ui.item.value); | ||||
|         focus: async (event, ui) => { | ||||
|             const notePath = linkService.getNodePathFromLabel(ui.item.value); | ||||
|             const noteId = treeUtils.getNoteIdFromNotePath(notePath); | ||||
|  | ||||
|                 setDefaultLinkTitle(noteId); | ||||
|             await setDefaultLinkTitle(noteId); | ||||
|         } | ||||
|     }); | ||||
|     } | ||||
| } | ||||
|  | ||||
|     $form.submit(() => { | ||||
| $form.submit(() => { | ||||
|     const value = $autoComplete.val(); | ||||
|  | ||||
|         const notePath = link.getNodePathFromLabel(value); | ||||
|     const notePath = linkService.getNodePathFromLabel(value); | ||||
|     const noteId = treeUtils.getNoteIdFromNotePath(notePath); | ||||
|  | ||||
|     if (notePath) { | ||||
| @@ -89,49 +106,55 @@ const addLink = (function() { | ||||
|  | ||||
|             $dialog.dialog("close"); | ||||
|  | ||||
|                 link.addLinkToEditor(linkTitle, '#' + notePath); | ||||
|             const linkHref = '#' + notePath; | ||||
|  | ||||
|             if (hasSelection()) { | ||||
|                 const editor = noteDetailText.getEditor(); | ||||
|  | ||||
|                 editor.execute('link', linkHref); | ||||
|             } | ||||
|             else { | ||||
|                 linkService.addLinkToEditor(linkTitle, linkHref); | ||||
|             } | ||||
|         } | ||||
|         else if (linkType === 'selected-to-current') { | ||||
|             const prefix = $clonePrefix.val(); | ||||
|  | ||||
|                 cloning.cloneNoteTo(noteId, noteEditor.getCurrentNoteId(), prefix); | ||||
|             cloningService.cloneNoteTo(noteId, noteDetailService.getCurrentNoteId(), prefix); | ||||
|  | ||||
|             $dialog.dialog("close"); | ||||
|         } | ||||
|         else if (linkType === 'current-to-selected') { | ||||
|             const prefix = $clonePrefix.val(); | ||||
|  | ||||
|                 cloning.cloneNoteTo(noteEditor.getCurrentNoteId(), noteId, prefix); | ||||
|             cloningService.cloneNoteTo(noteDetailService.getCurrentNoteId(), noteId, prefix); | ||||
|  | ||||
|             $dialog.dialog("close"); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     return false; | ||||
|     }); | ||||
| }); | ||||
|  | ||||
|     function linkTypeChanged() { | ||||
| // returns true if user selected some text, false if there's no selection | ||||
| function hasSelection() { | ||||
|     const model = noteDetailText.getEditor().model; | ||||
|     const selection = model.document.selection; | ||||
|  | ||||
|     return !selection.isCollapsed; | ||||
| } | ||||
|  | ||||
| function linkTypeChanged() { | ||||
|     const value = $linkTypes.filter(":checked").val(); | ||||
|  | ||||
|         if (value === 'html') { | ||||
|             $linkTitleFormGroup.show(); | ||||
|             $prefixFormGroup.hide(); | ||||
|         } | ||||
|         else { | ||||
|             $linkTitleFormGroup.hide(); | ||||
|             $prefixFormGroup.show(); | ||||
|         } | ||||
|     } | ||||
|     $linkTitleFormGroup.toggle(!hasSelection() && value === 'html'); | ||||
|     $prefixFormGroup.toggle(!hasSelection() && value !== 'html'); | ||||
|  | ||||
|     $linkTypes.change(linkTypeChanged); | ||||
|     $linkTypeDiv.toggle(!hasSelection()); | ||||
| } | ||||
|  | ||||
|     $(document).bind('keydown', 'ctrl+l', e => { | ||||
|         showDialog(); | ||||
| $linkTypes.change(linkTypeChanged); | ||||
|  | ||||
|         e.preventDefault(); | ||||
|     }); | ||||
|  | ||||
|     return { | ||||
| export default { | ||||
|     showDialog | ||||
|     }; | ||||
| })(); | ||||
| }; | ||||
| @@ -1,224 +0,0 @@ | ||||
| "use strict"; | ||||
|  | ||||
| const attributesDialog = (function() { | ||||
|     const $dialog = $("#attributes-dialog"); | ||||
|     const $saveAttributesButton = $("#save-attributes-button"); | ||||
|     const $attributesBody = $('#attributes-table tbody'); | ||||
|  | ||||
|     const attributesModel = new AttributesModel(); | ||||
|     let attributeNames = []; | ||||
|  | ||||
|     function AttributesModel() { | ||||
|         const self = this; | ||||
|  | ||||
|         this.attributes = ko.observableArray(); | ||||
|  | ||||
|         this.loadAttributes = async function() { | ||||
|             const noteId = noteEditor.getCurrentNoteId(); | ||||
|  | ||||
|             const attributes = await server.get('notes/' + noteId + '/attributes'); | ||||
|  | ||||
|             self.attributes(attributes.map(ko.observable)); | ||||
|  | ||||
|             addLastEmptyRow(); | ||||
|  | ||||
|             attributeNames = await server.get('attributes/names'); | ||||
|  | ||||
|             // attribute might not be rendered immediatelly so could not focus | ||||
|             setTimeout(() => $(".attribute-name:last").focus(), 100); | ||||
|  | ||||
|             $attributesBody.sortable({ | ||||
|                 handle: '.handle', | ||||
|                 containment: $attributesBody, | ||||
|                 update: function() { | ||||
|                     let position = 0; | ||||
|  | ||||
|                     // we need to update positions by searching in the DOM, because order of the | ||||
|                     // attributes in the viewmodel (self.attributes()) stays the same | ||||
|                     $attributesBody.find('input[name="position"]').each(function() { | ||||
|                         const attr = self.getTargetAttribute(this); | ||||
|  | ||||
|                         attr().position = position++; | ||||
|                     }); | ||||
|                 } | ||||
|             }); | ||||
|         }; | ||||
|  | ||||
|         this.deleteAttribute = function(data, event) { | ||||
|             const attr = self.getTargetAttribute(event.target); | ||||
|             const attrData = attr(); | ||||
|  | ||||
|             if (attrData) { | ||||
|                 attrData.isDeleted = 1; | ||||
|  | ||||
|                 attr(attrData); | ||||
|  | ||||
|                 addLastEmptyRow(); | ||||
|             } | ||||
|         }; | ||||
|  | ||||
|         function isValid() { | ||||
|             for (let attrs = self.attributes(), i = 0; i < attrs.length; i++) { | ||||
|                 if (self.isEmptyName(i)) { | ||||
|                     return false; | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             return true; | ||||
|         } | ||||
|  | ||||
|         this.save = async function() { | ||||
|             // we need to defocus from input (in case of enter-triggered save) because value is updated | ||||
|             // on blur event (because of conflict with jQuery UI Autocomplete). Without this, input would | ||||
|             // stay in focus, blur wouldn't be triggered and change wouldn't be updated in the viewmodel. | ||||
|             $saveAttributesButton.focus(); | ||||
|  | ||||
|             if (!isValid()) { | ||||
|                 alert("Please fix all validation errors and try saving again."); | ||||
|                 return; | ||||
|             } | ||||
|  | ||||
|             const noteId = noteEditor.getCurrentNoteId(); | ||||
|  | ||||
|             const attributesToSave = self.attributes() | ||||
|                 .map(attr => attr()) | ||||
|                 .filter(attr => attr.attributeId !== "" || attr.name !== ""); | ||||
|  | ||||
|             const attributes = await server.put('notes/' + noteId + '/attributes', attributesToSave); | ||||
|  | ||||
|             self.attributes(attributes.map(ko.observable)); | ||||
|  | ||||
|             addLastEmptyRow(); | ||||
|  | ||||
|             showMessage("Attributes have been saved."); | ||||
|  | ||||
|             noteEditor.loadAttributeList(); | ||||
|         }; | ||||
|  | ||||
|         function addLastEmptyRow() { | ||||
|             const attrs = self.attributes().filter(attr => attr().isDeleted === 0); | ||||
|             const last = attrs.length === 0 ? null : attrs[attrs.length - 1](); | ||||
|  | ||||
|             if (!last || last.name.trim() !== "" || last.value !== "") { | ||||
|                 self.attributes.push(ko.observable({ | ||||
|                     attributeId: '', | ||||
|                     name: '', | ||||
|                     value: '', | ||||
|                     isDeleted: 0, | ||||
|                     position: 0 | ||||
|                 })); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         this.attributeChanged = function (data, event) { | ||||
|             addLastEmptyRow(); | ||||
|  | ||||
|             const attr = self.getTargetAttribute(event.target); | ||||
|  | ||||
|             attr.valueHasMutated(); | ||||
|         }; | ||||
|  | ||||
|         this.isNotUnique = function(index) { | ||||
|             const cur = self.attributes()[index](); | ||||
|  | ||||
|             if (cur.name.trim() === "") { | ||||
|                 return false; | ||||
|             } | ||||
|  | ||||
|             for (let attrs = self.attributes(), i = 0; i < attrs.length; i++) { | ||||
|                 const attr = attrs[i](); | ||||
|  | ||||
|                 if (index !== i && cur.name === attr.name) { | ||||
|                     return true; | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             return false; | ||||
|         }; | ||||
|  | ||||
|         this.isEmptyName = function(index) { | ||||
|             const cur = self.attributes()[index](); | ||||
|  | ||||
|             return cur.name.trim() === "" && (cur.attributeId !== "" || cur.value !== ""); | ||||
|         }; | ||||
|  | ||||
|         this.getTargetAttribute = function(target) { | ||||
|             const context = ko.contextFor(target); | ||||
|             const index = context.$index(); | ||||
|  | ||||
|             return self.attributes()[index]; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     async function showDialog() { | ||||
|         glob.activeDialog = $dialog; | ||||
|  | ||||
|         await attributesModel.loadAttributes(); | ||||
|  | ||||
|         $dialog.dialog({ | ||||
|             modal: true, | ||||
|             width: 800, | ||||
|             height: 500 | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     $(document).bind('keydown', 'alt+a', e => { | ||||
|         showDialog(); | ||||
|  | ||||
|         e.preventDefault(); | ||||
|     }); | ||||
|  | ||||
|     ko.applyBindings(attributesModel, document.getElementById('attributes-dialog')); | ||||
|  | ||||
|     $(document).on('focus', '.attribute-name', function (e) { | ||||
|         if (!$(this).hasClass("ui-autocomplete-input")) { | ||||
|             $(this).autocomplete({ | ||||
|                 // shouldn't be required and autocomplete should just accept array of strings, but that fails | ||||
|                 // because we have overriden filter() function in init.js | ||||
|                 source: attributeNames.map(attr => { | ||||
|                     return { | ||||
|                         label: attr, | ||||
|                         value: attr | ||||
|                     } | ||||
|                 }), | ||||
|                 minLength: 0 | ||||
|             }); | ||||
|         } | ||||
|  | ||||
|         $(this).autocomplete("search", $(this).val()); | ||||
|     }); | ||||
|  | ||||
|     $(document).on('focus', '.attribute-value', async function (e) { | ||||
|         if (!$(this).hasClass("ui-autocomplete-input")) { | ||||
|             const attributeName = $(this).parent().parent().find('.attribute-name').val(); | ||||
|  | ||||
|             if (attributeName.trim() === "") { | ||||
|                 return; | ||||
|             } | ||||
|  | ||||
|             const attributeValues = await server.get('attributes/values/' + encodeURIComponent(attributeName)); | ||||
|  | ||||
|             if (attributeValues.length === 0) { | ||||
|                 return; | ||||
|             } | ||||
|  | ||||
|             $(this).autocomplete({ | ||||
|                 // shouldn't be required and autocomplete should just accept array of strings, but that fails | ||||
|                 // because we have overriden filter() function in init.js | ||||
|                 source: attributeValues.map(attr => { | ||||
|                     return { | ||||
|                         label: attr, | ||||
|                         value: attr | ||||
|                     } | ||||
|                 }), | ||||
|                 minLength: 0 | ||||
|             }); | ||||
|         } | ||||
|  | ||||
|         $(this).autocomplete("search", $(this).val()); | ||||
|     }); | ||||
|  | ||||
|     return { | ||||
|         showDialog | ||||
|     }; | ||||
| })(); | ||||
							
								
								
									
										51
									
								
								src/public/javascripts/dialogs/branch_prefix.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,51 @@ | ||||
| import treeService from '../services/tree.js'; | ||||
| import server from '../services/server.js'; | ||||
| import treeCache from "../services/tree_cache.js"; | ||||
| import treeUtils from "../services/tree_utils.js"; | ||||
|  | ||||
| const $dialog = $("#edit-tree-prefix-dialog"); | ||||
| const $form = $("#edit-tree-prefix-form"); | ||||
| const $treePrefixInput = $("#tree-prefix-input"); | ||||
| const $noteTitle = $('#tree-prefix-note-title'); | ||||
|  | ||||
| let branchId; | ||||
|  | ||||
| async function showDialog() { | ||||
|     glob.activeDialog = $dialog; | ||||
|  | ||||
|     await $dialog.dialog({ | ||||
|         modal: true, | ||||
|         width: 500 | ||||
|     }); | ||||
|  | ||||
|     const currentNode = treeService.getCurrentNode(); | ||||
|  | ||||
|     branchId = currentNode.data.branchId; | ||||
|     const branch = await treeCache.getBranch(branchId); | ||||
|  | ||||
|     $treePrefixInput.val(branch.prefix).focus(); | ||||
|  | ||||
|     const noteTitle = await treeUtils.getNoteTitle(currentNode.data.noteId); | ||||
|  | ||||
|     $noteTitle.html(noteTitle); | ||||
| } | ||||
|  | ||||
| async function savePrefix() { | ||||
|     const prefix = $treePrefixInput.val(); | ||||
|  | ||||
|     await server.put('branches/' + branchId + '/set-prefix', { prefix: prefix }); | ||||
|  | ||||
|     await treeService.setPrefix(branchId, prefix); | ||||
|  | ||||
|     $dialog.dialog("close"); | ||||
| } | ||||
|  | ||||
| $form.submit(() => { | ||||
|     savePrefix(); | ||||
|  | ||||
|     return false; | ||||
| }); | ||||
|  | ||||
| export default { | ||||
|     showDialog | ||||
| }; | ||||
| @@ -1,45 +0,0 @@ | ||||
| "use strict"; | ||||
|  | ||||
| const editTreePrefix = (function() { | ||||
|     const $dialog = $("#edit-tree-prefix-dialog"); | ||||
|     const $form = $("#edit-tree-prefix-form"); | ||||
|     const $treePrefixInput = $("#tree-prefix-input"); | ||||
|     const $noteTitle = $('#tree-prefix-note-title'); | ||||
|  | ||||
|     let noteTreeId; | ||||
|  | ||||
|     async function showDialog() { | ||||
|         glob.activeDialog = $dialog; | ||||
|  | ||||
|         await $dialog.dialog({ | ||||
|             modal: true, | ||||
|             width: 500 | ||||
|         }); | ||||
|  | ||||
|         const currentNode = noteTree.getCurrentNode(); | ||||
|  | ||||
|         noteTreeId = currentNode.data.noteTreeId; | ||||
|  | ||||
|         $treePrefixInput.val(currentNode.data.prefix).focus(); | ||||
|  | ||||
|         const noteTitle = noteTree.getNoteTitle(currentNode.data.noteId); | ||||
|  | ||||
|         $noteTitle.html(noteTitle); | ||||
|     } | ||||
|  | ||||
|     $form.submit(() => { | ||||
|         const prefix = $treePrefixInput.val(); | ||||
|  | ||||
|         server.put('tree/' + noteTreeId + '/set-prefix', { | ||||
|             prefix: prefix | ||||
|         }).then(() => noteTree.setPrefix(noteTreeId, prefix)); | ||||
|  | ||||
|         $dialog.dialog("close"); | ||||
|  | ||||
|         return false; | ||||
|     }); | ||||
|  | ||||
|     return { | ||||
|         showDialog | ||||
|     }; | ||||
| })(); | ||||
| @@ -1,10 +1,11 @@ | ||||
| "use strict"; | ||||
| import linkService from '../services/link.js'; | ||||
| import utils from '../services/utils.js'; | ||||
| import server from '../services/server.js'; | ||||
|  | ||||
| const eventLog = (function() { | ||||
|     const $dialog = $("#event-log-dialog"); | ||||
|     const $list = $("#event-log-list"); | ||||
| const $dialog = $("#event-log-dialog"); | ||||
| const $list = $("#event-log-list"); | ||||
|  | ||||
|     async function showDialog() { | ||||
| async function showDialog() { | ||||
|     glob.activeDialog = $dialog; | ||||
|  | ||||
|     $dialog.dialog({ | ||||
| @@ -18,10 +19,10 @@ const eventLog = (function() { | ||||
|     $list.html(''); | ||||
|  | ||||
|     for (const event of result) { | ||||
|             const dateTime = formatDateTime(parseDate(event.dateAdded)); | ||||
|         const dateTime = utils.formatDateTime(utils.parseDate(event.dateCreated)); | ||||
|  | ||||
|         if (event.noteId) { | ||||
|                 const noteLink = link.createNoteLink(event.noteId).prop('outerHTML'); | ||||
|             const noteLink = await linkService.createNoteLink(event.noteId).prop('outerHTML'); | ||||
|  | ||||
|             event.comment = event.comment.replace('<note>', noteLink); | ||||
|         } | ||||
| @@ -30,9 +31,8 @@ const eventLog = (function() { | ||||
|  | ||||
|         $list.append(eventEl); | ||||
|     } | ||||
|     } | ||||
| } | ||||
|  | ||||
|     return { | ||||
| export default { | ||||
|     showDialog | ||||
|     }; | ||||
| })(); | ||||
| }; | ||||
|   | ||||
| @@ -1,11 +1,15 @@ | ||||
| "use strict"; | ||||
| import treeService from '../services/tree.js'; | ||||
| import linkService from '../services/link.js'; | ||||
| import server from '../services/server.js'; | ||||
| import searchNotesService from '../services/search_notes.js'; | ||||
|  | ||||
| const jumpToNote = (function() { | ||||
|     const $dialog = $("#jump-to-note-dialog"); | ||||
|     const $autoComplete = $("#jump-to-note-autocomplete"); | ||||
|     const $form = $("#jump-to-note-form"); | ||||
| const $dialog = $("#jump-to-note-dialog"); | ||||
| const $autoComplete = $("#jump-to-note-autocomplete"); | ||||
| const $form = $("#jump-to-note-form"); | ||||
| const $jumpToNoteButton = $("#jump-to-note-button"); | ||||
| const $showInFullTextButton = $("#show-in-full-text-button"); | ||||
|  | ||||
|     async function showDialog() { | ||||
| async function showDialog() { | ||||
|     glob.activeDialog = $dialog; | ||||
|  | ||||
|     $autoComplete.val(''); | ||||
| @@ -16,41 +20,67 @@ const jumpToNote = (function() { | ||||
|     }); | ||||
|  | ||||
|     await $autoComplete.autocomplete({ | ||||
|             source: await stopWatch("building autocomplete", noteTree.getAutocompleteItems), | ||||
|             minLength: 0 | ||||
|         source: async function(request, response) { | ||||
|             const result = await server.get('autocomplete?query=' + encodeURIComponent(request.term)); | ||||
|  | ||||
|             if (result.length > 0) { | ||||
|                 response(result); | ||||
|             } | ||||
|             else { | ||||
|                 response([{ | ||||
|                     label: "No results", | ||||
|                     value: "No results" | ||||
|                 }]); | ||||
|             } | ||||
|         }, | ||||
|         focus: function(event, ui) { | ||||
|             return $(ui.item).val() !== 'No results'; | ||||
|         }, | ||||
|         minLength: 2 | ||||
|     }); | ||||
|     } | ||||
| } | ||||
|  | ||||
|     function getSelectedNotePath() { | ||||
| function getSelectedNotePath() { | ||||
|     const val = $autoComplete.val(); | ||||
|         return link.getNodePathFromLabel(val); | ||||
|     } | ||||
|     return linkService.getNodePathFromLabel(val); | ||||
| } | ||||
|  | ||||
|     function goToNote() { | ||||
| function goToNote() { | ||||
|     const notePath = getSelectedNotePath(); | ||||
|  | ||||
|     if (notePath) { | ||||
|             noteTree.activateNode(notePath); | ||||
|         treeService.activateNode(notePath); | ||||
|  | ||||
|         $dialog.dialog('close'); | ||||
|     } | ||||
|     } | ||||
|  | ||||
|     $(document).bind('keydown', 'ctrl+j', e => { | ||||
|         showDialog(); | ||||
| } | ||||
|  | ||||
| function showInFullText(e) { | ||||
|     // stop from propagating upwards (dangerous especially with ctrl+enter executable javascript notes) | ||||
|     e.preventDefault(); | ||||
|     }); | ||||
|     e.stopPropagation(); | ||||
|  | ||||
|     $form.submit(() => { | ||||
|         const action = $dialog.find("button:focus").val(); | ||||
|     const searchText = $autoComplete.val(); | ||||
|  | ||||
|     searchNotesService.resetSearch(); | ||||
|     searchNotesService.showSearch(); | ||||
|     searchNotesService.doSearch(searchText); | ||||
|  | ||||
|     $dialog.dialog('close'); | ||||
| } | ||||
|  | ||||
| $form.submit(() => { | ||||
|     goToNote(); | ||||
|  | ||||
|     return false; | ||||
|     }); | ||||
| }); | ||||
|  | ||||
|     return { | ||||
| $jumpToNoteButton.click(goToNote); | ||||
|  | ||||
| $showInFullTextButton.click(showInFullText); | ||||
|  | ||||
| $dialog.bind('keydown', 'ctrl+return', showInFullText); | ||||
|  | ||||
| export default { | ||||
|     showDialog | ||||
|     }; | ||||
| })(); | ||||
| }; | ||||
							
								
								
									
										223
									
								
								src/public/javascripts/dialogs/labels.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,223 @@ | ||||
| import noteDetailService from '../services/note_detail.js'; | ||||
| import utils from '../services/utils.js'; | ||||
| import server from '../services/server.js'; | ||||
| import infoService from "../services/info.js"; | ||||
|  | ||||
| const $dialog = $("#labels-dialog"); | ||||
| const $saveLabelsButton = $("#save-labels-button"); | ||||
| const $labelsBody = $('#labels-table tbody'); | ||||
|  | ||||
| const labelsModel = new LabelsModel(); | ||||
| let labelNames = []; | ||||
|  | ||||
| function LabelsModel() { | ||||
|     const self = this; | ||||
|  | ||||
|     this.labels = ko.observableArray(); | ||||
|  | ||||
|     this.updateLabelPositions = function() { | ||||
|         let position = 0; | ||||
|  | ||||
|         // we need to update positions by searching in the DOM, because order of the | ||||
|         // labels in the viewmodel (self.labels()) stays the same | ||||
|         $labelsBody.find('input[name="position"]').each(function() { | ||||
|             const label = self.getTargetLabel(this); | ||||
|  | ||||
|             label().position = position++; | ||||
|         }); | ||||
|     }; | ||||
|  | ||||
|     this.loadLabels = async function() { | ||||
|         const noteId = noteDetailService.getCurrentNoteId(); | ||||
|  | ||||
|         const labels = await server.get('notes/' + noteId + '/labels'); | ||||
|  | ||||
|         self.labels(labels.map(ko.observable)); | ||||
|  | ||||
|         addLastEmptyRow(); | ||||
|  | ||||
|         labelNames = await server.get('labels/names'); | ||||
|  | ||||
|         // label might not be rendered immediatelly so could not focus | ||||
|         setTimeout(() => $(".label-name:last").focus(), 100); | ||||
|  | ||||
|         $labelsBody.sortable({ | ||||
|             handle: '.handle', | ||||
|             containment: $labelsBody, | ||||
|             update: this.updateLabelPositions | ||||
|         }); | ||||
|     }; | ||||
|  | ||||
|     this.deleteLabel = function(data, event) { | ||||
|         const label = self.getTargetLabel(event.target); | ||||
|         const labelData = label(); | ||||
|  | ||||
|         if (labelData) { | ||||
|             labelData.isDeleted = 1; | ||||
|  | ||||
|             label(labelData); | ||||
|  | ||||
|             addLastEmptyRow(); | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     function isValid() { | ||||
|         for (let labels = self.labels(), i = 0; i < labels.length; i++) { | ||||
|             if (self.isEmptyName(i)) { | ||||
|                 return false; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     this.save = async function() { | ||||
|         // we need to defocus from input (in case of enter-triggered save) because value is updated | ||||
|         // on blur event (because of conflict with jQuery UI Autocomplete). Without this, input would | ||||
|         // stay in focus, blur wouldn't be triggered and change wouldn't be updated in the viewmodel. | ||||
|         $saveLabelsButton.focus(); | ||||
|  | ||||
|         if (!isValid()) { | ||||
|             alert("Please fix all validation errors and try saving again."); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         self.updateLabelPositions(); | ||||
|  | ||||
|         const noteId = noteDetailService.getCurrentNoteId(); | ||||
|  | ||||
|         const labelsToSave = self.labels() | ||||
|             .map(label => label()) | ||||
|             .filter(label => label.labelId !== "" || label.name !== ""); | ||||
|  | ||||
|         const labels = await server.put('notes/' + noteId + '/labels', labelsToSave); | ||||
|  | ||||
|         self.labels(labels.map(ko.observable)); | ||||
|  | ||||
|         addLastEmptyRow(); | ||||
|  | ||||
|         infoService.showMessage("Labels have been saved."); | ||||
|  | ||||
|         noteDetailService.loadLabelList(); | ||||
|     }; | ||||
|  | ||||
|     function addLastEmptyRow() { | ||||
|         const labels = self.labels().filter(attr => attr().isDeleted === 0); | ||||
|         const last = labels.length === 0 ? null : labels[labels.length - 1](); | ||||
|  | ||||
|         if (!last || last.name.trim() !== "" || last.value !== "") { | ||||
|             self.labels.push(ko.observable({ | ||||
|                 labelId: '', | ||||
|                 name: '', | ||||
|                 value: '', | ||||
|                 isDeleted: 0, | ||||
|                 position: 0 | ||||
|             })); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     this.labelChanged = function (data, event) { | ||||
|         addLastEmptyRow(); | ||||
|  | ||||
|         const label = self.getTargetLabel(event.target); | ||||
|  | ||||
|         label.valueHasMutated(); | ||||
|     }; | ||||
|  | ||||
|     this.isNotUnique = function(index) { | ||||
|         const cur = self.labels()[index](); | ||||
|  | ||||
|         if (cur.name.trim() === "") { | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         for (let labels = self.labels(), i = 0; i < labels.length; i++) { | ||||
|             const label = labels[i](); | ||||
|  | ||||
|             if (index !== i && cur.name === label.name) { | ||||
|                 return true; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return false; | ||||
|     }; | ||||
|  | ||||
|     this.isEmptyName = function(index) { | ||||
|         const cur = self.labels()[index](); | ||||
|  | ||||
|         return cur.name.trim() === "" && (cur.labelId !== "" || cur.value !== ""); | ||||
|     }; | ||||
|  | ||||
|     this.getTargetLabel = function(target) { | ||||
|         const context = ko.contextFor(target); | ||||
|         const index = context.$index(); | ||||
|  | ||||
|         return self.labels()[index]; | ||||
|     } | ||||
| } | ||||
|  | ||||
| async function showDialog() { | ||||
|     glob.activeDialog = $dialog; | ||||
|  | ||||
|     await labelsModel.loadLabels(); | ||||
|  | ||||
|     $dialog.dialog({ | ||||
|         modal: true, | ||||
|         width: 800, | ||||
|         height: 500 | ||||
|     }); | ||||
| } | ||||
|  | ||||
| ko.applyBindings(labelsModel, document.getElementById('labels-dialog')); | ||||
|  | ||||
| $(document).on('focus', '.label-name', function (e) { | ||||
|     if (!$(this).hasClass("ui-autocomplete-input")) { | ||||
|         $(this).autocomplete({ | ||||
|             // shouldn't be required and autocomplete should just accept array of strings, but that fails | ||||
|             // because we have overriden filter() function in autocomplete.js | ||||
|             source: labelNames.map(label => { | ||||
|                 return { | ||||
|                     label: label, | ||||
|                     value: label | ||||
|                 } | ||||
|             }), | ||||
|             minLength: 0 | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     $(this).autocomplete("search", $(this).val()); | ||||
| }); | ||||
|  | ||||
| $(document).on('focus', '.label-value', async function (e) { | ||||
|     if (!$(this).hasClass("ui-autocomplete-input")) { | ||||
|         const labelName = $(this).parent().parent().find('.label-name').val(); | ||||
|  | ||||
|         if (labelName.trim() === "") { | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         const labelValues = await server.get('labels/values/' + encodeURIComponent(labelName)); | ||||
|  | ||||
|         if (labelValues.length === 0) { | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         $(this).autocomplete({ | ||||
|             // shouldn't be required and autocomplete should just accept array of strings, but that fails | ||||
|             // because we have overriden filter() function in autocomplete.js | ||||
|             source: labelValues.map(label => { | ||||
|                 return { | ||||
|                     label: label, | ||||
|                     value: label | ||||
|                 } | ||||
|             }), | ||||
|             minLength: 0 | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     $(this).autocomplete("search", $(this).val()); | ||||
| }); | ||||
|  | ||||
| export default { | ||||
|     showDialog | ||||
| }; | ||||
| @@ -1,78 +0,0 @@ | ||||
| "use strict"; | ||||
|  | ||||
| const noteHistory = (function() { | ||||
|     const $dialog = $("#note-history-dialog"); | ||||
|     const $list = $("#note-history-list"); | ||||
|     const $content = $("#note-history-content"); | ||||
|     const $title = $("#note-history-title"); | ||||
|  | ||||
|     let historyItems = []; | ||||
|  | ||||
|     async function showCurrentNoteHistory() { | ||||
|         await showNoteHistoryDialog(noteEditor.getCurrentNoteId()); | ||||
|     } | ||||
|  | ||||
|     async function showNoteHistoryDialog(noteId, noteRevisionId) { | ||||
|         glob.activeDialog = $dialog; | ||||
|  | ||||
|         $dialog.dialog({ | ||||
|             modal: true, | ||||
|             width: 800, | ||||
|             height: 700 | ||||
|         }); | ||||
|  | ||||
|         $list.empty(); | ||||
|         $content.empty(); | ||||
|  | ||||
|         historyItems = await server.get('notes-history/' + noteId); | ||||
|  | ||||
|         for (const item of historyItems) { | ||||
|             const dateModified = parseDate(item.dateModifiedFrom); | ||||
|  | ||||
|             $list.append($('<option>', { | ||||
|                 value: item.noteRevisionId, | ||||
|                 text: formatDateTime(dateModified) | ||||
|             })); | ||||
|         } | ||||
|  | ||||
|         if (historyItems.length > 0) { | ||||
|             if (!noteRevisionId) { | ||||
|                 noteRevisionId = $list.find("option:first").val(); | ||||
|             } | ||||
|  | ||||
|             $list.val(noteRevisionId).trigger('change'); | ||||
|         } | ||||
|         else { | ||||
|             $title.text("No history for this note yet..."); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     $(document).bind('keydown', 'alt+h', e => { | ||||
|         showCurrentNoteHistory(); | ||||
|  | ||||
|         e.preventDefault(); | ||||
|     }); | ||||
|  | ||||
|     $list.on('change', () => { | ||||
|         const optVal = $list.find(":selected").val(); | ||||
|  | ||||
|         const historyItem = historyItems.find(r => r.noteRevisionId === optVal); | ||||
|  | ||||
|         $title.html(historyItem.title); | ||||
|         $content.html(historyItem.content); | ||||
|     }); | ||||
|  | ||||
|     $(document).on('click', "a[action='note-history']", event => { | ||||
|         const linkEl = $(event.target); | ||||
|         const noteId = linkEl.attr('note-path'); | ||||
|         const noteRevisionId = linkEl.attr('note-history-id'); | ||||
|  | ||||
|         showNoteHistoryDialog(noteId, noteRevisionId); | ||||
|  | ||||
|         return false; | ||||
|     }); | ||||
|  | ||||
|     return { | ||||
|         showCurrentNoteHistory | ||||
|     }; | ||||
| })(); | ||||
							
								
								
									
										78
									
								
								src/public/javascripts/dialogs/note_revisions.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,78 @@ | ||||
| import noteDetailService from '../services/note_detail.js'; | ||||
| import utils from '../services/utils.js'; | ||||
| import server from '../services/server.js'; | ||||
|  | ||||
| const $dialog = $("#note-revisions-dialog"); | ||||
| const $list = $("#note-revision-list"); | ||||
| const $content = $("#note-revision-content"); | ||||
| const $title = $("#note-revision-title"); | ||||
|  | ||||
| let revisionItems = []; | ||||
|  | ||||
| async function showCurrentNoteRevisions() { | ||||
|     await showNoteRevisionsDialog(noteDetailService.getCurrentNoteId()); | ||||
| } | ||||
|  | ||||
| async function showNoteRevisionsDialog(noteId, noteRevisionId) { | ||||
|     glob.activeDialog = $dialog; | ||||
|  | ||||
|     $dialog.dialog({ | ||||
|         modal: true, | ||||
|         width: 800, | ||||
|         height: 700 | ||||
|     }); | ||||
|  | ||||
|     $list.empty(); | ||||
|     $content.empty(); | ||||
|  | ||||
|     revisionItems = await server.get('notes/' + noteId + '/revisions'); | ||||
|  | ||||
|     for (const item of revisionItems) { | ||||
|         const dateModified = utils.parseDate(item.dateModifiedFrom); | ||||
|  | ||||
|         $list.append($('<option>', { | ||||
|             value: item.noteRevisionId, | ||||
|             text: utils.formatDateTime(dateModified) | ||||
|         })); | ||||
|     } | ||||
|  | ||||
|     if (revisionItems.length > 0) { | ||||
|         if (!noteRevisionId) { | ||||
|             noteRevisionId = $list.find("option:first").val(); | ||||
|         } | ||||
|  | ||||
|         $list.val(noteRevisionId).trigger('change'); | ||||
|     } | ||||
|     else { | ||||
|         $title.text("No revisions for this note yet..."); | ||||
|     } | ||||
| } | ||||
|  | ||||
| $list.on('change', () => { | ||||
|     const optVal = $list.find(":selected").val(); | ||||
|  | ||||
|     const revisionItem = revisionItems.find(r => r.noteRevisionId === optVal); | ||||
|  | ||||
|     $title.html(revisionItem.title); | ||||
|  | ||||
|     if (revisionItem.type === 'text') { | ||||
|         $content.html(revisionItem.content); | ||||
|     } | ||||
|     else if (revisionItem.type === 'code') { | ||||
|         $content.html($("<pre>").text(revisionItem.content)); | ||||
|     } | ||||
| }); | ||||
|  | ||||
| $(document).on('click', "a[action='note-revision']", event => { | ||||
|     const linkEl = $(event.target); | ||||
|     const noteId = linkEl.attr('note-path'); | ||||
|     const noteRevisionId = linkEl.attr('note-revision-id'); | ||||
|  | ||||
|     showNoteRevisionsDialog(noteId, noteRevisionId); | ||||
|  | ||||
|     return false; | ||||
| }); | ||||
|  | ||||
| export default { | ||||
|     showCurrentNoteRevisions | ||||
| }; | ||||
| @@ -1,10 +1,9 @@ | ||||
| "use strict"; | ||||
| import noteDetailService from '../services/note_detail.js'; | ||||
|  | ||||
| const noteSource = (function() { | ||||
|     const $dialog = $("#note-source-dialog"); | ||||
|     const $noteSource = $("#note-source"); | ||||
| const $dialog = $("#note-source-dialog"); | ||||
| const $noteSource = $("#note-source"); | ||||
|  | ||||
|     function showDialog() { | ||||
| function showDialog() { | ||||
|     glob.activeDialog = $dialog; | ||||
|  | ||||
|     $dialog.dialog({ | ||||
| @@ -13,19 +12,19 @@ const noteSource = (function() { | ||||
|         height: 500 | ||||
|     }); | ||||
|  | ||||
|         const noteText = noteEditor.getCurrentNote().detail.content; | ||||
|     const noteText = noteDetailService.getCurrentNote().content; | ||||
|  | ||||
|     $noteSource.text(formatHtml(noteText)); | ||||
|     } | ||||
| } | ||||
|  | ||||
|     function formatHtml(str) { | ||||
| function formatHtml(str) { | ||||
|     const div = document.createElement('div'); | ||||
|     div.innerHTML = str.trim(); | ||||
|  | ||||
|     return formatNode(div, 0).innerHTML.trim(); | ||||
|     } | ||||
| } | ||||
|  | ||||
|     function formatNode(node, level) { | ||||
| function formatNode(node, level) { | ||||
|     const indentBefore = new Array(level++ + 1).join('  '); | ||||
|     const indentAfter  = new Array(level - 1).join('  '); | ||||
|     let textNode; | ||||
| @@ -43,15 +42,8 @@ const noteSource = (function() { | ||||
|     } | ||||
|  | ||||
|     return node; | ||||
|     } | ||||
| } | ||||
|  | ||||
|     $(document).bind('keydown', 'ctrl+u', e => { | ||||
|         showDialog(); | ||||
|  | ||||
|         e.preventDefault(); | ||||
|     }); | ||||
|  | ||||
|     return { | ||||
| export default { | ||||
|     showDialog | ||||
|     }; | ||||
| })(); | ||||
| }; | ||||
							
								
								
									
										242
									
								
								src/public/javascripts/dialogs/options.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,242 @@ | ||||
| "use strict"; | ||||
|  | ||||
| import protectedSessionHolder from '../services/protected_session_holder.js'; | ||||
| import server from '../services/server.js'; | ||||
| import infoService from "../services/info.js"; | ||||
| import zoomService from "../services/zoom.js"; | ||||
| import utils from "../services/utils.js"; | ||||
|  | ||||
| const $dialog = $("#options-dialog"); | ||||
| const $tabs = $("#options-tabs"); | ||||
|  | ||||
| const tabHandlers = []; | ||||
|  | ||||
| function addTabHandler(handler) { | ||||
|     tabHandlers.push(handler); | ||||
| } | ||||
|  | ||||
| async function showDialog() { | ||||
|     glob.activeDialog = $dialog; | ||||
|  | ||||
|     const options = await server.get('options'); | ||||
|  | ||||
|     $dialog.dialog({ | ||||
|         modal: true, | ||||
|         width: 900 | ||||
|     }); | ||||
|  | ||||
|     $tabs.tabs(); | ||||
|  | ||||
|     for (const handler of tabHandlers) { | ||||
|         if (handler.optionsLoaded) { | ||||
|             handler.optionsLoaded(options); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| async function saveOptions(optionName, optionValue) { | ||||
|     await server.put('options/' + encodeURIComponent(optionName) + '/' + encodeURIComponent(optionValue)); | ||||
|  | ||||
|     infoService.showMessage("Options change have been saved."); | ||||
| } | ||||
|  | ||||
| export default { | ||||
|     showDialog, | ||||
|     saveOptions | ||||
| }; | ||||
|  | ||||
| addTabHandler((function() { | ||||
|     const $themeSelect = $("#theme-select"); | ||||
|     const $zoomFactorSelect = $("#zoom-factor-select"); | ||||
|     const $html = $("html"); | ||||
|  | ||||
|     function optionsLoaded(options) { | ||||
|         $themeSelect.val(options.theme); | ||||
|  | ||||
|         if (utils.isElectron()) { | ||||
|             $zoomFactorSelect.val(options.zoomFactor); | ||||
|         } | ||||
|         else { | ||||
|             $zoomFactorSelect.prop('disabled', true); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     $themeSelect.change(function() { | ||||
|         const newTheme = $(this).val(); | ||||
|  | ||||
|         $html.attr("class", "theme-" + newTheme); | ||||
|  | ||||
|         server.put('options/theme/' + newTheme); | ||||
|     }); | ||||
|  | ||||
|     $zoomFactorSelect.change(function() { | ||||
|         const newZoomFactor = $(this).val(); | ||||
|  | ||||
|         zoomService.setZoomFactorAndSave(newZoomFactor); | ||||
|     }); | ||||
|  | ||||
|     return { | ||||
|         optionsLoaded | ||||
|     }; | ||||
| })()); | ||||
|  | ||||
| addTabHandler((function() { | ||||
|     const $form = $("#change-password-form"); | ||||
|     const $oldPassword = $("#old-password"); | ||||
|     const $newPassword1 = $("#new-password1"); | ||||
|     const $newPassword2 = $("#new-password2"); | ||||
|  | ||||
|     function optionsLoaded(options) { | ||||
|     } | ||||
|  | ||||
|     $form.submit(() => { | ||||
|         const oldPassword = $oldPassword.val(); | ||||
|         const newPassword1 = $newPassword1.val(); | ||||
|         const newPassword2 = $newPassword2.val(); | ||||
|  | ||||
|         $oldPassword.val(''); | ||||
|         $newPassword1.val(''); | ||||
|         $newPassword2.val(''); | ||||
|  | ||||
|         if (newPassword1 !== newPassword2) { | ||||
|             alert("New passwords are not the same."); | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         server.post('password/change', { | ||||
|             'current_password': oldPassword, | ||||
|             'new_password': newPassword1 | ||||
|         }).then(result => { | ||||
|             if (result.success) { | ||||
|                 alert("Password has been changed. Trilium will be reloaded after you press OK."); | ||||
|  | ||||
|                 // password changed so current protected session is invalid and needs to be cleared | ||||
|                 protectedSessionHolder.resetProtectedSession(); | ||||
|             } | ||||
|             else { | ||||
|                 infoService.showError(result.message); | ||||
|             } | ||||
|         }); | ||||
|  | ||||
|         return false; | ||||
|     }); | ||||
|  | ||||
|     return { | ||||
|         optionsLoaded | ||||
|     }; | ||||
| })()); | ||||
|  | ||||
| addTabHandler((function() { | ||||
|     const $form = $("#protected-session-timeout-form"); | ||||
|     const $protectedSessionTimeout = $("#protected-session-timeout-in-seconds"); | ||||
|     const optionName = 'protectedSessionTimeout'; | ||||
|  | ||||
|     function optionsLoaded(options) { | ||||
|         $protectedSessionTimeout.val(options[optionName]); | ||||
|     } | ||||
|  | ||||
|     $form.submit(() => { | ||||
|         const protectedSessionTimeout = $protectedSessionTimeout.val(); | ||||
|  | ||||
|         saveOptions(optionName, protectedSessionTimeout).then(() => { | ||||
|             protectedSessionHolder.setProtectedSessionTimeout(protectedSessionTimeout); | ||||
|         }); | ||||
|  | ||||
|         return false; | ||||
|     }); | ||||
|  | ||||
|     return { | ||||
|         optionsLoaded | ||||
|     }; | ||||
| })()); | ||||
|  | ||||
| addTabHandler((function () { | ||||
|     const $form = $("#note-revision-snapshot-time-interval-form"); | ||||
|     const $timeInterval = $("#note-revision-snapshot-time-interval-in-seconds"); | ||||
|     const optionName = 'noteRevisionSnapshotTimeInterval'; | ||||
|  | ||||
|     function optionsLoaded(options) { | ||||
|         $timeInterval.val(options[optionName]); | ||||
|     } | ||||
|  | ||||
|     $form.submit(() => { | ||||
|         saveOptions(optionName, $timeInterval.val()); | ||||
|  | ||||
|         return false; | ||||
|     }); | ||||
|  | ||||
|     return { | ||||
|         optionsLoaded | ||||
|     }; | ||||
| })()); | ||||
|  | ||||
| addTabHandler((async function () { | ||||
|     const $appVersion = $("#app-version"); | ||||
|     const $dbVersion = $("#db-version"); | ||||
|     const $syncVersion = $("#sync-version"); | ||||
|     const $buildDate = $("#build-date"); | ||||
|     const $buildRevision = $("#build-revision"); | ||||
|  | ||||
|     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); | ||||
|     $buildRevision.attr('href', 'https://github.com/zadam/trilium/commit/' + appInfo.buildRevision); | ||||
|  | ||||
|     return {}; | ||||
| })()); | ||||
|  | ||||
| addTabHandler((async function () { | ||||
|     const $forceFullSyncButton = $("#force-full-sync-button"); | ||||
|     const $fillSyncRowsButton = $("#fill-sync-rows-button"); | ||||
|     const $anonymizeButton = $("#anonymize-button"); | ||||
|     const $cleanupSoftDeletedButton = $("#cleanup-soft-deleted-items-button"); | ||||
|     const $cleanupUnusedImagesButton = $("#cleanup-unused-images-button"); | ||||
|     const $vacuumDatabaseButton = $("#vacuum-database-button"); | ||||
|  | ||||
|     $forceFullSyncButton.click(async () => { | ||||
|         await server.post('sync/force-full-sync'); | ||||
|  | ||||
|         infoService.showMessage("Full sync triggered"); | ||||
|     }); | ||||
|  | ||||
|     $fillSyncRowsButton.click(async () => { | ||||
|         await server.post('sync/fill-sync-rows'); | ||||
|  | ||||
|         infoService.showMessage("Sync rows filled successfully"); | ||||
|     }); | ||||
|  | ||||
|  | ||||
|     $anonymizeButton.click(async () => { | ||||
|         await server.post('anonymization/anonymize'); | ||||
|  | ||||
|         infoService.showMessage("Created anonymized database"); | ||||
|     }); | ||||
|  | ||||
|     $cleanupSoftDeletedButton.click(async () => { | ||||
|         if (confirm("Do you really want to clean up soft-deleted items?")) { | ||||
|             await server.post('cleanup/cleanup-soft-deleted-items'); | ||||
|  | ||||
|             infoService.showMessage("Soft deleted items have been cleaned up"); | ||||
|         } | ||||
|     }); | ||||
|  | ||||
|     $cleanupUnusedImagesButton.click(async () => { | ||||
|         if (confirm("Do you really want to clean up unused images?")) { | ||||
|             await server.post('cleanup/cleanup-unused-images'); | ||||
|  | ||||
|             infoService.showMessage("Unused images have been cleaned up"); | ||||
|         } | ||||
|     }); | ||||
|  | ||||
|     $vacuumDatabaseButton.click(async () => { | ||||
|         await server.post('cleanup/vacuum-database'); | ||||
|  | ||||
|         infoService.showMessage("Database has been vacuumed"); | ||||
|     }); | ||||
|  | ||||
|     return {}; | ||||
| })()); | ||||
| @@ -1,9 +1,10 @@ | ||||
| "use strict"; | ||||
| import linkService from '../services/link.js'; | ||||
| import utils from '../services/utils.js'; | ||||
| import server from '../services/server.js'; | ||||
|  | ||||
| const recentChanges = (function() { | ||||
|     const $dialog = $("#recent-changes-dialog"); | ||||
| const $dialog = $("#recent-changes-dialog"); | ||||
|  | ||||
|     async function showDialog() { | ||||
| async function showDialog() { | ||||
|     glob.activeDialog = $dialog; | ||||
|  | ||||
|     $dialog.dialog({ | ||||
| @@ -21,17 +22,17 @@ const recentChanges = (function() { | ||||
|     for (const [dateDay, dayChanges] of groupedByDate) { | ||||
|         const changesListEl = $('<ul>'); | ||||
|  | ||||
|             const dayEl = $('<div>').append($('<b>').html(formatDate(dateDay))).append(changesListEl); | ||||
|         const dayEl = $('<div>').append($('<b>').html(utils.formatDate(dateDay))).append(changesListEl); | ||||
|  | ||||
|         for (const change of dayChanges) { | ||||
|                 const formattedTime = formatTime(parseDate(change.dateModifiedTo)); | ||||
|             const formattedTime = utils.formatTime(utils.parseDate(change.dateModifiedTo)); | ||||
|  | ||||
|             const revLink = $("<a>", { | ||||
|                 href: 'javascript:', | ||||
|                 text: 'rev' | ||||
|                 }).attr('action', 'note-history') | ||||
|             }).attr('action', 'note-revision') | ||||
|                 .attr('note-path', change.noteId) | ||||
|                     .attr('note-history-id', change.noteRevisionId); | ||||
|                 .attr('note-revision-id', change.noteRevisionId); | ||||
|  | ||||
|             let noteLink; | ||||
|  | ||||
| @@ -39,7 +40,7 @@ const recentChanges = (function() { | ||||
|                 noteLink = change.current_title; | ||||
|             } | ||||
|             else { | ||||
|                     noteLink = link.createNoteLink(change.noteId, change.title); | ||||
|                 noteLink = await linkService.createNoteLink(change.noteId, change.title); | ||||
|             } | ||||
|  | ||||
|             changesListEl.append($('<li>') | ||||
| @@ -50,14 +51,14 @@ const recentChanges = (function() { | ||||
|  | ||||
|         $dialog.append(dayEl); | ||||
|     } | ||||
|     } | ||||
| } | ||||
|  | ||||
|     function groupByDate(result) { | ||||
| function groupByDate(result) { | ||||
|     const groupedByDate = new Map(); | ||||
|     const dayCache = {}; | ||||
|  | ||||
|     for (const row of result) { | ||||
|             let dateDay = parseDate(row.dateModifiedTo); | ||||
|         let dateDay = utils.parseDate(row.dateModifiedTo); | ||||
|         dateDay.setHours(0); | ||||
|         dateDay.setMinutes(0); | ||||
|         dateDay.setSeconds(0); | ||||
| @@ -79,11 +80,8 @@ const recentChanges = (function() { | ||||
|         groupedByDate.get(dateDay).push(row); | ||||
|     } | ||||
|     return groupedByDate; | ||||
|     } | ||||
| } | ||||
|  | ||||
|     $(document).bind('keydown', 'alt+r', showDialog); | ||||
|  | ||||
|     return { | ||||
| export default { | ||||
|     showDialog | ||||
|     }; | ||||
| })(); | ||||
| }; | ||||