Compare commits
	
		
			336 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					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 | ||
| 
						 | 
					8028b09351 | ||
| 
						 | 
					ebe66eaed9 | ||
| 
						 | 
					5bce9a5f94 | ||
| 
						 | 
					dfd9927310 | ||
| 
						 | 
					9bf1735bde | ||
| 
						 | 
					2e8eeda5ab | ||
| 
						 | 
					1cef0ce5f9 | ||
| 
						 | 
					1efac99828 | ||
| 
						 | 
					0e9473119e | ||
| 
						 | 
					7bbfef7af3 | ||
| 
						 | 
					5cb93509c1 | ||
| 
						 | 
					89e89e04d8 | ||
| 
						 | 
					72df0d8861 | ||
| 
						 | 
					9910aebf45 | ||
| 
						 | 
					f9f8ecb2b1 | ||
| 
						 | 
					438f7c5b0b | ||
| 
						 | 
					4b1d1aba74 | ||
| 
						 | 
					6dea73cfe2 | ||
| 
						 | 
					58f5d0cf6e | ||
| 
						 | 
					7b77e40514 | ||
| 
						 | 
					660908c54b | ||
| 
						 | 
					e970564036 | ||
| 
						 | 
					b3038487f8 | ||
| 
						 | 
					cac98392a6 | ||
| 
						 | 
					dbd28377e3 | ||
| 
						 | 
					c76e4faf5d | ||
| 
						 | 
					e011b9ae63 | ||
| 
						 | 
					7c74c77a2c | ||
| 
						 | 
					c2a2f195aa | ||
| 
						 | 
					4e70cebf70 | 
							
								
								
									
										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
 | 
					# 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:
 | 
					Trilium Notes is a hierarchical note taking application. Picture tells a thousand words:
 | 
				
			||||||
 | 
					
 | 
				
			||||||

 | 
					
 | 
				
			||||||
@@ -10,6 +12,7 @@ Trilium Notes is a hierarchical note taking application. Picture tells a thousan
 | 
				
			|||||||
* WYSIWYG (What You See Is What You Get) editing
 | 
					* WYSIWYG (What You See Is What You Get) editing
 | 
				
			||||||
* Fast and easy [navigation between notes](https://github.com/zadam/trilium/wiki/Note-navigation)
 | 
					* Fast and easy [navigation between notes](https://github.com/zadam/trilium/wiki/Note-navigation)
 | 
				
			||||||
* Seamless note versioning
 | 
					* Seamless note versioning
 | 
				
			||||||
 | 
					* 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)
 | 
					* 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
 | 
					* [Synchronization with](https://github.com/zadam/trilium/wiki/Synchronization) self-hosted sync server
 | 
				
			||||||
* Strong [note encryption](https://github.com/zadam/trilium/wiki/Protected-notes)
 | 
					* Strong [note encryption](https://github.com/zadam/trilium/wiki/Protected-notes)
 | 
				
			||||||
@@ -34,10 +37,12 @@ List of documentation pages:
 | 
				
			|||||||
* [Installation as webapp](https://github.com/zadam/trilium/wiki/Installation-as-webapp)
 | 
					* [Installation as webapp](https://github.com/zadam/trilium/wiki/Installation-as-webapp)
 | 
				
			||||||
* [Note navigation](https://github.com/zadam/trilium/wiki/Note-navigation)
 | 
					* [Note navigation](https://github.com/zadam/trilium/wiki/Note-navigation)
 | 
				
			||||||
* [Tree manipulation](https://github.com/zadam/trilium/wiki/Tree-manipulation)
 | 
					* [Tree manipulation](https://github.com/zadam/trilium/wiki/Tree-manipulation)
 | 
				
			||||||
 | 
					* [Labels](https://github.com/zadam/trilium/wiki/Labels)
 | 
				
			||||||
* [Links](https://github.com/zadam/trilium/wiki/Links)
 | 
					* [Links](https://github.com/zadam/trilium/wiki/Links)
 | 
				
			||||||
* [Cloning notes](https://github.com/zadam/trilium/wiki/Cloning-notes)
 | 
					* [Cloning notes](https://github.com/zadam/trilium/wiki/Cloning-notes)
 | 
				
			||||||
* [Protected notes](https://github.com/zadam/trilium/wiki/Protected-notes)
 | 
					* [Protected notes](https://github.com/zadam/trilium/wiki/Protected-notes)
 | 
				
			||||||
* [Synchronization](https://github.com/zadam/trilium/wiki/Synchronization)
 | 
					* [Synchronization](https://github.com/zadam/trilium/wiki/Synchronization)
 | 
				
			||||||
* [Document](https://github.com/zadam/trilium/wiki/Document)
 | 
					* [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)
 | 
					* [Keyboard shortcuts](https://github.com/zadam/trilium/wiki/Keyboard-shortcuts)
 | 
				
			||||||
* [Troubleshooting](https://github.com/zadam/trilium/wiki/Troubleshooting)
 | 
					* [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
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					docker push zadam/trilium:latest
 | 
				
			||||||
 | 
					docker push zadam/trilium:$1
 | 
				
			||||||
@@ -24,9 +24,9 @@ jq '.version = "'$VERSION'"' package.json|sponge package.json
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
git add 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
 | 
					TAG=v$VERSION
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -75,4 +75,8 @@ github-release upload \
 | 
				
			|||||||
    --name "$WINDOWS_X64_BUILD" \
 | 
					    --name "$WINDOWS_X64_BUILD" \
 | 
				
			||||||
    --file "dist/$WINDOWS_X64_BUILD"
 | 
					    --file "dist/$WINDOWS_X64_BUILD"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bin/build-docker.sh $VERSION
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bin/push-docker-image.sh $VERSION
 | 
				
			||||||
 | 
					
 | 
				
			||||||
echo "Release finished!"
 | 
					echo "Release finished!"
 | 
				
			||||||
@@ -1,3 +1,7 @@
 | 
				
			|||||||
 | 
					[General]
 | 
				
			||||||
 | 
					# Instance name can be used to distinguish between different instances
 | 
				
			||||||
 | 
					instanceName=
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[Network]
 | 
					[Network]
 | 
				
			||||||
port=8080
 | 
					port=8080
 | 
				
			||||||
# true for TLS/SSL/HTTPS (secure), false for HTTP (unsecure).
 | 
					# 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');
 | 
					 | 
				
			||||||
@@ -0,0 +1 @@
 | 
				
			|||||||
 | 
					DROP INDEX IDX_attributes_noteId_name;
 | 
				
			||||||
							
								
								
									
										1
									
								
								db/migrations/0073__add_isDeleted_to_attributes.sql
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1 @@
 | 
				
			|||||||
 | 
					ALTER TABLE attributes ADD COLUMN isDeleted INT NOT NULL DEFAULT 0;
 | 
				
			||||||
							
								
								
									
										1
									
								
								db/migrations/0074__add_position_to_attribute.sql
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1 @@
 | 
				
			|||||||
 | 
					ALTER TABLE attributes ADD COLUMN position INT NOT NULL DEFAULT 0;
 | 
				
			||||||
							
								
								
									
										7
									
								
								db/migrations/0075__add_api_token.sql
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,7 @@
 | 
				
			|||||||
 | 
					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
 | 
				
			||||||
 | 
					);
 | 
				
			||||||
							
								
								
									
										1
									
								
								db/migrations/0076__add_attribute_name_index.sql
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1 @@
 | 
				
			|||||||
 | 
					CREATE INDEX IDX_attributes_name_value ON attributes (name, value);
 | 
				
			||||||
							
								
								
									
										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;
 | 
				
			||||||
							
								
								
									
										173
									
								
								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" (
 | 
					CREATE TABLE IF NOT EXISTS "sync" (
 | 
				
			||||||
  `id`	INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
 | 
					  `id`	INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
 | 
				
			||||||
  `entityName`	TEXT NOT NULL,
 | 
					  `entityName`	TEXT NOT NULL,
 | 
				
			||||||
  `entityId`	TEXT NOT NULL,
 | 
					  `entityId`	TEXT NOT NULL,
 | 
				
			||||||
  `sourceId` TEXT NOT NULL,
 | 
					  `sourceId` TEXT NOT NULL,
 | 
				
			||||||
  `syncDate`	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" (
 | 
					CREATE TABLE IF NOT EXISTS "source_ids" (
 | 
				
			||||||
  `sourceId`	TEXT NOT NULL,
 | 
					  `sourceId`	TEXT NOT NULL,
 | 
				
			||||||
  `dateCreated`	TEXT NOT NULL,
 | 
					  `dateCreated`	TEXT NOT NULL,
 | 
				
			||||||
  PRIMARY KEY(`sourceId`)
 | 
					  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" (
 | 
					CREATE TABLE IF NOT EXISTS "note_revisions" (
 | 
				
			||||||
  `noteRevisionId`	TEXT NOT NULL PRIMARY KEY,
 | 
					  `noteRevisionId`	TEXT NOT NULL PRIMARY KEY,
 | 
				
			||||||
  `noteId`	TEXT NOT NULL,
 | 
					  `noteId`	TEXT NOT NULL,
 | 
				
			||||||
@@ -52,12 +24,15 @@ CREATE TABLE IF NOT EXISTS "note_revisions" (
 | 
				
			|||||||
  `isProtected`	INT NOT NULL DEFAULT 0,
 | 
					  `isProtected`	INT NOT NULL DEFAULT 0,
 | 
				
			||||||
  `dateModifiedFrom` TEXT NOT NULL,
 | 
					  `dateModifiedFrom` TEXT NOT NULL,
 | 
				
			||||||
  `dateModifiedTo` 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" (
 | 
					CREATE INDEX `IDX_note_revisions_dateModifiedFrom` ON `note_revisions` (
 | 
				
			||||||
  `noteTreeId` TEXT NOT NULL PRIMARY KEY,
 | 
					  `dateModifiedFrom`
 | 
				
			||||||
  `notePath` TEXT NOT NULL,
 | 
					);
 | 
				
			||||||
  `dateAccessed` TEXT NOT NULL,
 | 
					CREATE INDEX `IDX_note_revisions_dateModifiedTo` ON `note_revisions` (
 | 
				
			||||||
  isDeleted INT
 | 
					  `dateModifiedTo`
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
CREATE TABLE IF NOT EXISTS "images"
 | 
					CREATE TABLE IF NOT EXISTS "images"
 | 
				
			||||||
(
 | 
					(
 | 
				
			||||||
@@ -69,7 +44,7 @@ CREATE TABLE IF NOT EXISTS "images"
 | 
				
			|||||||
  isDeleted INT NOT NULL DEFAULT 0,
 | 
					  isDeleted INT NOT NULL DEFAULT 0,
 | 
				
			||||||
  dateModified TEXT NOT NULL,
 | 
					  dateModified TEXT NOT NULL,
 | 
				
			||||||
  dateCreated TEXT NOT NULL
 | 
					  dateCreated TEXT NOT NULL
 | 
				
			||||||
);
 | 
					, hash TEXT DEFAULT "" NOT NULL);
 | 
				
			||||||
CREATE TABLE note_images
 | 
					CREATE TABLE note_images
 | 
				
			||||||
(
 | 
					(
 | 
				
			||||||
  noteImageId TEXT PRIMARY KEY NOT NULL,
 | 
					  noteImageId TEXT PRIMARY KEY NOT NULL,
 | 
				
			||||||
@@ -78,44 +53,84 @@ CREATE TABLE note_images
 | 
				
			|||||||
  isDeleted INT NOT NULL DEFAULT 0,
 | 
					  isDeleted INT NOT NULL DEFAULT 0,
 | 
				
			||||||
  dateModified TEXT NOT NULL,
 | 
					  dateModified TEXT NOT NULL,
 | 
				
			||||||
  dateCreated TEXT NOT NULL
 | 
					  dateCreated TEXT NOT NULL
 | 
				
			||||||
);
 | 
					, hash TEXT DEFAULT "" NOT NULL);
 | 
				
			||||||
CREATE TABLE IF NOT EXISTS "attributes"
 | 
					 | 
				
			||||||
(
 | 
					 | 
				
			||||||
  attributeId TEXT PRIMARY KEY NOT NULL,
 | 
					 | 
				
			||||||
  noteId TEXT NOT NULL,
 | 
					 | 
				
			||||||
  name TEXT NOT NULL,
 | 
					 | 
				
			||||||
  value TEXT,
 | 
					 | 
				
			||||||
  dateCreated TEXT NOT NULL,
 | 
					 | 
				
			||||||
  dateModified TEXT 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`
 | 
					 | 
				
			||||||
);
 | 
					 | 
				
			||||||
CREATE INDEX IDX_note_images_noteId ON note_images (noteId);
 | 
					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_imageId ON note_images (imageId);
 | 
				
			||||||
CREATE INDEX IDX_note_images_noteId_imageId ON note_images (noteId, imageId);
 | 
					CREATE INDEX IDX_note_images_noteId_imageId ON note_images (noteId, imageId);
 | 
				
			||||||
CREATE INDEX IDX_attributes_noteId ON attributes (noteId);
 | 
					CREATE TABLE IF NOT EXISTS "api_tokens"
 | 
				
			||||||
CREATE UNIQUE INDEX IDX_attributes_noteId_name ON attributes (noteId, name);
 | 
					(
 | 
				
			||||||
 | 
					  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
 | 
				
			||||||
 | 
					);
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										24
									
								
								electron.js
									
									
									
									
									
								
							
							
						
						@@ -3,9 +3,11 @@
 | 
				
			|||||||
const electron = require('electron');
 | 
					const electron = require('electron');
 | 
				
			||||||
const path = require('path');
 | 
					const path = require('path');
 | 
				
			||||||
const config = require('./src/services/config');
 | 
					const config = require('./src/services/config');
 | 
				
			||||||
 | 
					const log = require('./src/services/log');
 | 
				
			||||||
const url = require("url");
 | 
					const url = require("url");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const app = electron.app;
 | 
					const app = electron.app;
 | 
				
			||||||
 | 
					const globalShortcut = electron.globalShortcut;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Adds debug features like hotkeys for triggering dev tools and reload
 | 
					// Adds debug features like hotkeys for triggering dev tools and reload
 | 
				
			||||||
require('electron-debug')();
 | 
					require('electron-debug')();
 | 
				
			||||||
@@ -13,6 +15,8 @@ require('electron-debug')();
 | 
				
			|||||||
// Prevent window being garbage collected
 | 
					// Prevent window being garbage collected
 | 
				
			||||||
let mainWindow;
 | 
					let mainWindow;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					require('electron-dl')({ saveAs: true });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function onClosed() {
 | 
					function onClosed() {
 | 
				
			||||||
    // Dereference the window
 | 
					    // Dereference the window
 | 
				
			||||||
    // For multiple windows store them in an array
 | 
					    // For multiple windows store them in an array
 | 
				
			||||||
@@ -67,6 +71,26 @@ app.on('activate', () => {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
app.on('ready', () => {
 | 
					app.on('ready', () => {
 | 
				
			||||||
    mainWindow = createMainWindow();
 | 
					    mainWindow = createMainWindow();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const result = globalShortcut.register('CommandOrControl+Alt+P', async () => {
 | 
				
			||||||
 | 
					        const dateNoteService = require('./src/services/date_notes');
 | 
				
			||||||
 | 
					        const dateUtils = require('./src/services/date_utils');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const parentNote = await dateNoteService.getDateNote(dateUtils.nowDate());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // window may be hidden / not in focus
 | 
				
			||||||
 | 
					        mainWindow.focus();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        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', () => {
 | 
				
			||||||
 | 
					    globalShortcut.unregisterAll();
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
require('./src/www');
 | 
					require('./src/www');
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										9244
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
							
								
								
									
										58
									
								
								package.json
									
									
									
									
									
								
							
							
						
						@@ -1,7 +1,7 @@
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
  "name": "trilium",
 | 
					  "name": "trilium",
 | 
				
			||||||
  "description": "Trilium Notes",
 | 
					  "description": "Trilium Notes",
 | 
				
			||||||
  "version": "0.5.6",
 | 
					  "version": "0.16.0",
 | 
				
			||||||
  "license": "AGPL-3.0-only",
 | 
					  "license": "AGPL-3.0-only",
 | 
				
			||||||
  "main": "electron.js",
 | 
					  "main": "electron.js",
 | 
				
			||||||
  "repository": {
 | 
					  "repository": {
 | 
				
			||||||
@@ -9,11 +9,11 @@
 | 
				
			|||||||
    "url": "https://github.com/zadam/trilium.git"
 | 
					    "url": "https://github.com/zadam/trilium.git"
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  "scripts": {
 | 
					  "scripts": {
 | 
				
			||||||
    "start": "node ./bin/www",
 | 
					    "start": "node ./src/www",
 | 
				
			||||||
    "test-electron": "xo",
 | 
					    "test-electron": "xo",
 | 
				
			||||||
    "rebuild-electron": "electron-rebuild",
 | 
					    "rebuild-electron": "electron-rebuild",
 | 
				
			||||||
    "start-electron": "electron .",
 | 
					    "start-electron": "electron . --disable-gpu",
 | 
				
			||||||
    "build-electron": "electron-packager . --out=dist --asar --overwrite --all",
 | 
					    "build-electron": "electron-packager . --out=dist --asar --overwrite --platform=win32,linux --arch=ia32,x64 --app-version=",
 | 
				
			||||||
    "start-forge": "electron-forge start",
 | 
					    "start-forge": "electron-forge start",
 | 
				
			||||||
    "package-forge": "electron-forge package",
 | 
					    "package-forge": "electron-forge package",
 | 
				
			||||||
    "make-forge": "electron-forge make",
 | 
					    "make-forge": "electron-forge make",
 | 
				
			||||||
@@ -21,50 +21,56 @@
 | 
				
			|||||||
  },
 | 
					  },
 | 
				
			||||||
  "dependencies": {
 | 
					  "dependencies": {
 | 
				
			||||||
    "async-mutex": "^0.1.3",
 | 
					    "async-mutex": "^0.1.3",
 | 
				
			||||||
    "axios": "^0.17.1",
 | 
					    "axios": "^0.18",
 | 
				
			||||||
    "body-parser": "~1.18.2",
 | 
					    "body-parser": "^1.18.3",
 | 
				
			||||||
 | 
					    "cls-hooked": "^4.2.2",
 | 
				
			||||||
    "cookie-parser": "~1.4.3",
 | 
					    "cookie-parser": "~1.4.3",
 | 
				
			||||||
    "debug": "~3.1.0",
 | 
					    "debug": "~3.1.0",
 | 
				
			||||||
    "devtron": "^1.4.0",
 | 
					    "devtron": "^1.4.0",
 | 
				
			||||||
    "ejs": "~2.5.7",
 | 
					    "ejs": "~2.6.1",
 | 
				
			||||||
    "electron": "^1.8.2-beta.4",
 | 
					 | 
				
			||||||
    "electron-debug": "^1.5.0",
 | 
					    "electron-debug": "^1.5.0",
 | 
				
			||||||
    "electron-in-page-search": "^1.2.4",
 | 
					    "electron-dl": "^1.12.0",
 | 
				
			||||||
    "express": "~4.16.2",
 | 
					    "electron-in-page-search": "^1.3.2",
 | 
				
			||||||
    "express-promise-wrap": "^0.2.2",
 | 
					    "express": "~4.16.3",
 | 
				
			||||||
    "express-session": "^1.15.6",
 | 
					    "express-session": "^1.15.6",
 | 
				
			||||||
    "fs-extra": "^4.0.2",
 | 
					    "fs-extra": "^6.0.1",
 | 
				
			||||||
    "helmet": "^3.9.0",
 | 
					    "helmet": "^3.12.1",
 | 
				
			||||||
    "html": "^1.0.0",
 | 
					    "html": "^1.0.0",
 | 
				
			||||||
    "image-type": "^3.0.0",
 | 
					    "image-type": "^3.0.0",
 | 
				
			||||||
    "imagemin": "^5.3.1",
 | 
					    "imagemin": "^5.3.1",
 | 
				
			||||||
    "imagemin-giflossy": "^5.1.10",
 | 
					    "imagemin-giflossy": "^5.1.10",
 | 
				
			||||||
    "imagemin-mozjpeg": "^7.0.0",
 | 
					    "imagemin-mozjpeg": "^7.0.0",
 | 
				
			||||||
    "imagemin-pngquant": "^5.0.1",
 | 
					    "imagemin-pngquant": "^5.1.0",
 | 
				
			||||||
    "ini": "^1.3.4",
 | 
					    "ini": "^1.3.5",
 | 
				
			||||||
    "jimp": "^0.2.28",
 | 
					    "jimp": "^0.2.28",
 | 
				
			||||||
    "moment": "^2.20.1",
 | 
					    "moment": "^2.22.1",
 | 
				
			||||||
    "multer": "^1.3.0",
 | 
					    "multer": "^1.3.0",
 | 
				
			||||||
 | 
					    "open": "0.0.5",
 | 
				
			||||||
    "rand-token": "^0.4.0",
 | 
					    "rand-token": "^0.4.0",
 | 
				
			||||||
    "request": "^2.83.0",
 | 
					    "rcedit": "^1.1.0",
 | 
				
			||||||
 | 
					    "request": "^2.87.0",
 | 
				
			||||||
    "request-promise": "^4.2.2",
 | 
					    "request-promise": "^4.2.2",
 | 
				
			||||||
    "rimraf": "^2.6.2",
 | 
					    "rimraf": "^2.6.2",
 | 
				
			||||||
    "sanitize-filename": "^1.6.1",
 | 
					    "sanitize-filename": "^1.6.1",
 | 
				
			||||||
    "scrypt": "^6.0.3",
 | 
					    "scrypt": "^6.0.3",
 | 
				
			||||||
    "serve-favicon": "~2.4.5",
 | 
					    "serve-favicon": "~2.5.0",
 | 
				
			||||||
    "session-file-store": "^1.1.2",
 | 
					    "session-file-store": "^1.2.0",
 | 
				
			||||||
    "simple-node-logger": "^0.93.30",
 | 
					    "simple-node-logger": "^0.93.37",
 | 
				
			||||||
    "sqlite": "^2.9.0",
 | 
					    "sqlite": "^2.9.2",
 | 
				
			||||||
 | 
					    "tar-stream": "^1.6.1",
 | 
				
			||||||
    "unescape": "^1.0.1",
 | 
					    "unescape": "^1.0.1",
 | 
				
			||||||
    "ws": "^3.3.2"
 | 
					    "ws": "^5.2.0",
 | 
				
			||||||
 | 
					    "xml2js": "^0.4.19"
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  "devDependencies": {
 | 
					  "devDependencies": {
 | 
				
			||||||
 | 
					    "electron": "^2.0.1",
 | 
				
			||||||
    "electron-compile": "^6.4.2",
 | 
					    "electron-compile": "^6.4.2",
 | 
				
			||||||
    "electron-packager": "^10.1.1",
 | 
					    "electron-packager": "^12.1.0",
 | 
				
			||||||
    "electron-prebuilt-compile": "1.8.2-beta.4",
 | 
					    "electron-prebuilt-compile": "2.0.0",
 | 
				
			||||||
    "electron-rebuild": "^1.7.3",
 | 
					    "electron-rebuild": "^1.7.3",
 | 
				
			||||||
    "tape": "^4.8.0",
 | 
					    "lorem-ipsum": "^1.0.4",
 | 
				
			||||||
    "xo": "^0.18.0"
 | 
					    "tape": "^4.9.0",
 | 
				
			||||||
 | 
					    "xo": "^0.21.1"
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  "config": {
 | 
					  "config": {
 | 
				
			||||||
    "forge": {
 | 
					    "forge": {
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										15
									
								
								src/app.js
									
									
									
									
									
								
							
							
						
						@@ -9,6 +9,8 @@ const session = require('express-session');
 | 
				
			|||||||
const FileStore = require('session-file-store')(session);
 | 
					const FileStore = require('session-file-store')(session);
 | 
				
			||||||
const os = require('os');
 | 
					const os = require('os');
 | 
				
			||||||
const sessionSecret = require('./services/session_secret');
 | 
					const sessionSecret = require('./services/session_secret');
 | 
				
			||||||
 | 
					const cls = require('./services/cls');
 | 
				
			||||||
 | 
					require('./entities/entity_constructor');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const app = express();
 | 
					const app = express();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -23,6 +25,17 @@ app.use((req, res, next) => {
 | 
				
			|||||||
    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.json({limit: '50mb'}));
 | 
				
			||||||
app.use(bodyParser.urlencoded({extended: false}));
 | 
					app.use(bodyParser.urlencoded({extended: false}));
 | 
				
			||||||
app.use(cookieParser());
 | 
					app.use(cookieParser());
 | 
				
			||||||
@@ -73,7 +86,7 @@ require('./services/backup');
 | 
				
			|||||||
// trigger consistency checks timer
 | 
					// trigger consistency checks timer
 | 
				
			||||||
require('./services/consistency_checks');
 | 
					require('./services/consistency_checks');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
require('./plugins/reddit');
 | 
					require('./services/scheduler');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
module.exports = {
 | 
					module.exports = {
 | 
				
			||||||
    app,
 | 
					    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";
 | 
					"use strict";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const utils = require('../services/utils');
 | 
					const utils = require('../services/utils');
 | 
				
			||||||
 | 
					const repository = require('../services/repository');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Entity {
 | 
					class Entity {
 | 
				
			||||||
    constructor(repository, row) {
 | 
					    constructor(row = {}) {
 | 
				
			||||||
        utils.assertArguments(repository, row);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        this.repository = repository;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        for (const key in row) {
 | 
					        for (const key in row) {
 | 
				
			||||||
            this[key] = row[key];
 | 
					            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;
 | 
					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";
 | 
					"use strict";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const Entity = require('./entity');
 | 
					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 {
 | 
					class Note extends Entity {
 | 
				
			||||||
    static get tableName() { return "notes"; }
 | 
					    static get tableName() { return "notes"; }
 | 
				
			||||||
    static get primaryKeyName() { return "noteId"; }
 | 
					    static get primaryKeyName() { return "noteId"; }
 | 
				
			||||||
 | 
					    static get hashedProperties() { return ["noteId", "title", "content", "type", "dateModified", "isProtected", "isDeleted"]; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    constructor(repository, row) {
 | 
					    constructor(row) {
 | 
				
			||||||
        super(repository, row);
 | 
					        super(row);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (this.isProtected) {
 | 
					        // check if there's noteId, otherwise this is a new entity which wasn't encrypted yet
 | 
				
			||||||
            protected_session.decryptNote(this.dataKey, this);
 | 
					        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);
 | 
					            this.jsonContent = JSON.parse(this.content);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					        catch(e) {}
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    isJson() {
 | 
					    isJson() {
 | 
				
			||||||
        return this.type === "code" && this.mime === "application/json";
 | 
					        return this.mime === "application/json";
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    async getAttributes() {
 | 
					    isJavaScript() {
 | 
				
			||||||
        return this.repository.getEntities("SELECT * FROM attributes WHERE noteId = ?", [this.noteId]);
 | 
					        return (this.type === "code" || this.type === "file")
 | 
				
			||||||
 | 
					            && (this.mime.startsWith("application/javascript") || this.mime === "application/x-javascript");
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    async getAttribute(name) {
 | 
					    isHtml() {
 | 
				
			||||||
        return this.repository.getEntity("SELECT * FROM attributes WHERE noteId = ? AND name = ?", [this.noteId, name]);
 | 
					        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() {
 | 
					    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() {
 | 
					    async getNoteImages() {
 | 
				
			||||||
        return this.repository.getEntities("SELECT * FROM note_tree WHERE isDeleted = 0 AND noteId = ?", [this.noteId]);
 | 
					        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() {
 | 
					    beforeSaving() {
 | 
				
			||||||
 | 
					        super.beforeSaving();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (this.isJson() && this.jsonContent) {
 | 
				
			||||||
            this.content = JSON.stringify(this.jsonContent, null, '\t');
 | 
					            this.content = JSON.stringify(this.jsonContent, null, '\t');
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (this.isProtected) {
 | 
					        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";
 | 
					"use strict";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const Entity = require('./entity');
 | 
					const Entity = require('./entity');
 | 
				
			||||||
 | 
					const protectedSessionService = require('../services/protected_session');
 | 
				
			||||||
 | 
					const repository = require('../services/repository');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class NoteRevision extends Entity {
 | 
					class NoteRevision extends Entity {
 | 
				
			||||||
    static get tableName() { return "note_revisions"; }
 | 
					    static get tableName() { return "note_revisions"; }
 | 
				
			||||||
    static get primaryKeyName() { return "noteRevisionId"; }
 | 
					    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() {
 | 
					    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,143 +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);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    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,59 +1,76 @@
 | 
				
			|||||||
"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 dialogEl = $("#add-link-dialog");
 | 
					const $form = $("#add-link-form");
 | 
				
			||||||
    const formEl = $("#add-link-form");
 | 
					const $autoComplete = $("#note-autocomplete");
 | 
				
			||||||
    const autoCompleteEl = $("#note-autocomplete");
 | 
					const $linkTitle = $("#link-title");
 | 
				
			||||||
    const linkTitleEl = $("#link-title");
 | 
					const $clonePrefix = $("#clone-prefix");
 | 
				
			||||||
    const clonePrefixEl = $("#clone-prefix");
 | 
					const $linkTitleFormGroup = $("#add-link-title-form-group");
 | 
				
			||||||
    const linkTitleFormGroup = $("#add-link-title-form-group");
 | 
					const $prefixFormGroup = $("#add-link-prefix-form-group");
 | 
				
			||||||
    const prefixFormGroup = $("#add-link-prefix-form-group");
 | 
					const $linkTypeDiv = $("#add-link-type-div");
 | 
				
			||||||
    const linkTypeEls = $("input[name='add-link-type']");
 | 
					const $linkTypes = $("input[name='add-link-type']");
 | 
				
			||||||
    const linkTypeHtmlEl = linkTypeEls.filter('input[value="html"]');
 | 
					const $linkTypeHtml = $linkTypes.filter('input[value="html"]');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    function setLinkType(linkType) {
 | 
					function setLinkType(linkType) {
 | 
				
			||||||
        linkTypeEls.each(function () {
 | 
					    $linkTypes.each(function () {
 | 
				
			||||||
        $(this).prop('checked', $(this).val() === linkType);
 | 
					        $(this).prop('checked', $(this).val() === linkType);
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    linkTypeChanged();
 | 
					    linkTypeChanged();
 | 
				
			||||||
    }
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    function showDialog() {
 | 
					async function showDialog() {
 | 
				
			||||||
        glob.activeDialog = dialogEl;
 | 
					    glob.activeDialog = $dialog;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (noteEditor.getCurrentNoteType() === 'text') {
 | 
					    if (noteDetailService.getCurrentNoteType() === 'text') {
 | 
				
			||||||
            linkTypeHtmlEl.prop('disabled', false);
 | 
					        $linkTypeHtml.prop('disabled', false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        setLinkType('html');
 | 
					        setLinkType('html');
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    else {
 | 
					    else {
 | 
				
			||||||
            linkTypeHtmlEl.prop('disabled', true);
 | 
					        $linkTypeHtml.prop('disabled', true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        setLinkType('selected-to-current');
 | 
					        setLinkType('selected-to-current');
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        dialogEl.dialog({
 | 
					    $dialog.dialog({
 | 
				
			||||||
        modal: true,
 | 
					        modal: true,
 | 
				
			||||||
        width: 700
 | 
					        width: 700
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        autoCompleteEl.val('').focus();
 | 
					    $autoComplete.val('').focus();
 | 
				
			||||||
        clonePrefixEl.val('');
 | 
					    $clonePrefix.val('');
 | 
				
			||||||
        linkTitleEl.val('');
 | 
					    $linkTitle.val('');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        function setDefaultLinkTitle(noteId) {
 | 
					    async function setDefaultLinkTitle(noteId) {
 | 
				
			||||||
            const noteTitle = noteTree.getNoteTitle(noteId);
 | 
					        const noteTitle = await treeUtils.getNoteTitle(noteId);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            linkTitleEl.val(noteTitle);
 | 
					        $linkTitle.val(noteTitle);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        autoCompleteEl.autocomplete({
 | 
					    $autoComplete.autocomplete({
 | 
				
			||||||
            source: noteTree.getAutocompleteItems(),
 | 
					        source: async function(request, response) {
 | 
				
			||||||
            minLength: 0,
 | 
					            const result = await server.get('autocomplete?query=' + encodeURIComponent(request.term));
 | 
				
			||||||
            change: () => {
 | 
					
 | 
				
			||||||
                const val = autoCompleteEl.val();
 | 
					            if (result.length > 0) {
 | 
				
			||||||
                const notePath = link.getNodePathFromLabel(val);
 | 
					                response(result);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            else {
 | 
				
			||||||
 | 
					                response([{
 | 
				
			||||||
 | 
					                    label: "No results",
 | 
				
			||||||
 | 
					                    value: "No results"
 | 
				
			||||||
 | 
					                }]);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        minLength: 2,
 | 
				
			||||||
 | 
					        change: async () => {
 | 
				
			||||||
 | 
					            const val = $autoComplete.val();
 | 
				
			||||||
 | 
					            const notePath = linkService.getNodePathFromLabel(val);
 | 
				
			||||||
            if (!notePath) {
 | 
					            if (!notePath) {
 | 
				
			||||||
                return;
 | 
					                return;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
@@ -61,77 +78,83 @@ const addLink = (function() {
 | 
				
			|||||||
            const noteId = treeUtils.getNoteIdFromNotePath(notePath);
 | 
					            const noteId = treeUtils.getNoteIdFromNotePath(notePath);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (noteId) {
 | 
					            if (noteId) {
 | 
				
			||||||
                    setDefaultLinkTitle(noteId);
 | 
					                await setDefaultLinkTitle(noteId);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        // this is called when user goes through autocomplete list with keyboard
 | 
					        // 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
 | 
					        // at this point the item isn't selected yet so we use supplied ui.item to see WHERE the cursor is
 | 
				
			||||||
            focus: (event, ui) => {
 | 
					        focus: async (event, ui) => {
 | 
				
			||||||
                const notePath = link.getNodePathFromLabel(ui.item.value);
 | 
					            const notePath = linkService.getNodePathFromLabel(ui.item.value);
 | 
				
			||||||
            const noteId = treeUtils.getNoteIdFromNotePath(notePath);
 | 
					            const noteId = treeUtils.getNoteIdFromNotePath(notePath);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                setDefaultLinkTitle(noteId);
 | 
					            await setDefaultLinkTitle(noteId);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
    }
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    formEl.submit(() => {
 | 
					$form.submit(() => {
 | 
				
			||||||
        const value = autoCompleteEl.val();
 | 
					    const value = $autoComplete.val();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        const notePath = link.getNodePathFromLabel(value);
 | 
					    const notePath = linkService.getNodePathFromLabel(value);
 | 
				
			||||||
    const noteId = treeUtils.getNoteIdFromNotePath(notePath);
 | 
					    const noteId = treeUtils.getNoteIdFromNotePath(notePath);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (notePath) {
 | 
					    if (notePath) {
 | 
				
			||||||
        const linkType = $("input[name='add-link-type']:checked").val();
 | 
					        const linkType = $("input[name='add-link-type']:checked").val();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (linkType === 'html') {
 | 
					        if (linkType === 'html') {
 | 
				
			||||||
                const linkTitle = linkTitleEl.val();
 | 
					            const linkTitle = $linkTitle.val();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                dialogEl.dialog("close");
 | 
					            $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') {
 | 
					        else if (linkType === 'selected-to-current') {
 | 
				
			||||||
                const prefix = clonePrefixEl.val();
 | 
					            const prefix = $clonePrefix.val();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                cloning.cloneNoteTo(noteId, noteEditor.getCurrentNoteId(), prefix);
 | 
					            cloningService.cloneNoteTo(noteId, noteDetailService.getCurrentNoteId(), prefix);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                dialogEl.dialog("close");
 | 
					            $dialog.dialog("close");
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        else if (linkType === 'current-to-selected') {
 | 
					        else if (linkType === 'current-to-selected') {
 | 
				
			||||||
                const prefix = clonePrefixEl.val();
 | 
					            const prefix = $clonePrefix.val();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                cloning.cloneNoteTo(noteEditor.getCurrentNoteId(), noteId, prefix);
 | 
					            cloningService.cloneNoteTo(noteDetailService.getCurrentNoteId(), noteId, prefix);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                dialogEl.dialog("close");
 | 
					            $dialog.dialog("close");
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return false;
 | 
					    return false;
 | 
				
			||||||
    });
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    function linkTypeChanged() {
 | 
					// returns true if user selected some text, false if there's no selection
 | 
				
			||||||
        const value = linkTypeEls.filter(":checked").val();
 | 
					function hasSelection() {
 | 
				
			||||||
 | 
					    const model = noteDetailText.getEditor().model;
 | 
				
			||||||
 | 
					    const selection = model.document.selection;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (value === 'html') {
 | 
					    return !selection.isCollapsed;
 | 
				
			||||||
            linkTitleFormGroup.show();
 | 
					}
 | 
				
			||||||
            prefixFormGroup.hide();
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        else {
 | 
					 | 
				
			||||||
            linkTitleFormGroup.hide();
 | 
					 | 
				
			||||||
            prefixFormGroup.show();
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    linkTypeEls.change(linkTypeChanged);
 | 
					function linkTypeChanged() {
 | 
				
			||||||
 | 
					    const value = $linkTypes.filter(":checked").val();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    $(document).bind('keydown', 'ctrl+l', e => {
 | 
					    $linkTitleFormGroup.toggle(!hasSelection() && value === 'html');
 | 
				
			||||||
        showDialog();
 | 
					    $prefixFormGroup.toggle(!hasSelection() && value !== 'html');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        e.preventDefault();
 | 
					    $linkTypeDiv.toggle(!hasSelection());
 | 
				
			||||||
    });
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return {
 | 
					$linkTypes.change(linkTypeChanged);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default {
 | 
				
			||||||
    showDialog
 | 
					    showDialog
 | 
				
			||||||
    };
 | 
					};
 | 
				
			||||||
})();
 | 
					 | 
				
			||||||
@@ -1,186 +0,0 @@
 | 
				
			|||||||
"use strict";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const attributesDialog = (function() {
 | 
					 | 
				
			||||||
    const dialogEl = $("#attributes-dialog");
 | 
					 | 
				
			||||||
    const saveAttributesButton = $("#save-attributes-button");
 | 
					 | 
				
			||||||
    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);
 | 
					 | 
				
			||||||
        };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        function isValid() {
 | 
					 | 
				
			||||||
            for (let attrs = self.attributes(), i = 0; i < attrs.length; i++) {
 | 
					 | 
				
			||||||
                if (self.isEmptyName(i) || self.isNotUnique(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();
 | 
					 | 
				
			||||||
            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: ''
 | 
					 | 
				
			||||||
                }));
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        this.attributeChanged = function (row) {
 | 
					 | 
				
			||||||
            addLastEmptyRow();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            for (const attr of self.attributes()) {
 | 
					 | 
				
			||||||
                if (row.attributeId === attr().attributeId) {
 | 
					 | 
				
			||||||
                    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 !== "");
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    async function showDialog() {
 | 
					 | 
				
			||||||
        glob.activeDialog = dialogEl;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        await attributesModel.loadAttributes();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        dialogEl.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 dialogEl = $("#edit-tree-prefix-dialog");
 | 
					 | 
				
			||||||
    const formEl = $("#edit-tree-prefix-form");
 | 
					 | 
				
			||||||
    const treePrefixInputEl = $("#tree-prefix-input");
 | 
					 | 
				
			||||||
    const noteTitleEl = $('#tree-prefix-note-title');
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    let noteTreeId;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    async function showDialog() {
 | 
					 | 
				
			||||||
        glob.activeDialog = dialogEl;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        await dialogEl.dialog({
 | 
					 | 
				
			||||||
            modal: true,
 | 
					 | 
				
			||||||
            width: 500
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        const currentNode = noteTree.getCurrentNode();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        noteTreeId = currentNode.data.noteTreeId;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        treePrefixInputEl.val(currentNode.data.prefix).focus();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        const noteTitle = noteTree.getNoteTitle(currentNode.data.noteId);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        noteTitleEl.html(noteTitle);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    formEl.submit(() => {
 | 
					 | 
				
			||||||
        const prefix = treePrefixInputEl.val();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        server.put('tree/' + noteTreeId + '/set-prefix', {
 | 
					 | 
				
			||||||
            prefix: prefix
 | 
					 | 
				
			||||||
        }).then(() => noteTree.setPrefix(noteTreeId, prefix));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        dialogEl.dialog("close");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        return false;
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    return {
 | 
					 | 
				
			||||||
        showDialog
 | 
					 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
})();
 | 
					 | 
				
			||||||
@@ -1,13 +1,14 @@
 | 
				
			|||||||
"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 dialogEl = $("#event-log-dialog");
 | 
					const $list = $("#event-log-list");
 | 
				
			||||||
    const listEl = $("#event-log-list");
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    async function showDialog() {
 | 
					async function showDialog() {
 | 
				
			||||||
        glob.activeDialog = dialogEl;
 | 
					    glob.activeDialog = $dialog;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        dialogEl.dialog({
 | 
					    $dialog.dialog({
 | 
				
			||||||
        modal: true,
 | 
					        modal: true,
 | 
				
			||||||
        width: 800,
 | 
					        width: 800,
 | 
				
			||||||
        height: 700
 | 
					        height: 700
 | 
				
			||||||
@@ -15,24 +16,23 @@ const eventLog = (function() {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    const result = await server.get('event-log');
 | 
					    const result = await server.get('event-log');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        listEl.html('');
 | 
					    $list.html('');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    for (const event of result) {
 | 
					    for (const event of result) {
 | 
				
			||||||
            const dateTime = formatDateTime(parseDate(event.dateAdded));
 | 
					        const dateTime = utils.formatDateTime(utils.parseDate(event.dateCreated));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (event.noteId) {
 | 
					        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);
 | 
					            event.comment = event.comment.replace('<note>', noteLink);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        const eventEl = $('<li>').html(dateTime + " - " + event.comment);
 | 
					        const eventEl = $('<li>').html(dateTime + " - " + event.comment);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            listEl.append(eventEl);
 | 
					        $list.append(eventEl);
 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return {
 | 
					export default {
 | 
				
			||||||
    showDialog
 | 
					    showDialog
 | 
				
			||||||
    };
 | 
					};
 | 
				
			||||||
})();
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,56 +1,86 @@
 | 
				
			|||||||
"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 dialogEl = $("#jump-to-note-dialog");
 | 
					const $autoComplete = $("#jump-to-note-autocomplete");
 | 
				
			||||||
    const autoCompleteEl = $("#jump-to-note-autocomplete");
 | 
					const $form = $("#jump-to-note-form");
 | 
				
			||||||
    const formEl = $("#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 = dialogEl;
 | 
					    glob.activeDialog = $dialog;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        autoCompleteEl.val('');
 | 
					    $autoComplete.val('');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        dialogEl.dialog({
 | 
					    $dialog.dialog({
 | 
				
			||||||
        modal: true,
 | 
					        modal: true,
 | 
				
			||||||
        width: 800
 | 
					        width: 800
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        await autoCompleteEl.autocomplete({
 | 
					    await $autoComplete.autocomplete({
 | 
				
			||||||
            source: await stopWatch("building autocomplete", noteTree.getAutocompleteItems),
 | 
					        source: async function(request, response) {
 | 
				
			||||||
            minLength: 0
 | 
					            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 = autoCompleteEl.val();
 | 
					    const val = $autoComplete.val();
 | 
				
			||||||
        return link.getNodePathFromLabel(val);
 | 
					    return linkService.getNodePathFromLabel(val);
 | 
				
			||||||
    }
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    function goToNote() {
 | 
					function goToNote() {
 | 
				
			||||||
    const notePath = getSelectedNotePath();
 | 
					    const notePath = getSelectedNotePath();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (notePath) {
 | 
					    if (notePath) {
 | 
				
			||||||
            noteTree.activateNode(notePath);
 | 
					        treeService.activateNode(notePath);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            dialogEl.dialog('close');
 | 
					        $dialog.dialog('close');
 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    $(document).bind('keydown', 'ctrl+j', e => {
 | 
					function showInFullText(e) {
 | 
				
			||||||
        showDialog();
 | 
					    // stop from propagating upwards (dangerous especially with ctrl+enter executable javascript notes)
 | 
				
			||||||
 | 
					 | 
				
			||||||
    e.preventDefault();
 | 
					    e.preventDefault();
 | 
				
			||||||
    });
 | 
					    e.stopPropagation();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    formEl.submit(() => {
 | 
					    const searchText = $autoComplete.val();
 | 
				
			||||||
        const action = dialogEl.find("button:focus").val();
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    searchNotesService.resetSearch();
 | 
				
			||||||
 | 
					    searchNotesService.showSearch();
 | 
				
			||||||
 | 
					    searchNotesService.doSearch(searchText);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    $dialog.dialog('close');
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					$form.submit(() => {
 | 
				
			||||||
    goToNote();
 | 
					    goToNote();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return false;
 | 
					    return false;
 | 
				
			||||||
    });
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return {
 | 
					$jumpToNoteButton.click(goToNote);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					$showInFullTextButton.click(showInFullText);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					$dialog.bind('keydown', 'ctrl+return', showInFullText);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default {
 | 
				
			||||||
    showDialog
 | 
					    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
 | 
				
			||||||
 | 
					};
 | 
				
			||||||