Compare commits
251 Commits
v0.9.0-bet
...
v0.17.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3972c27e7a | ||
|
|
14cffbbe62 | ||
|
|
599c3c04af | ||
|
|
f1412b631d | ||
|
|
41908050bb | ||
|
|
f07033423c | ||
|
|
daf96fcbf2 | ||
|
|
2bca94529e | ||
|
|
b2c9a0da21 | ||
|
|
7a9542b4fc | ||
|
|
3a95c9e1bc | ||
|
|
3d2ef6be01 | ||
|
|
d67246699a | ||
|
|
14c704d6db | ||
|
|
4c8eeb2e6f | ||
|
|
c1b245c8b1 | ||
|
|
74202d67bb | ||
|
|
26066f39b1 | ||
|
|
b255cf190c | ||
|
|
bc77b143b0 | ||
|
|
9f0ff6ae7a | ||
|
|
736704c7d6 | ||
|
|
654c116c58 | ||
|
|
89a5cab98f | ||
|
|
c39d0be8cd | ||
|
|
e75b4cd848 | ||
|
|
378e8f35e5 | ||
|
|
bdb5e2f13f | ||
|
|
8211bed449 | ||
|
|
b243632483 | ||
|
|
e4d2513451 | ||
|
|
385144451b | ||
|
|
c8c533844e | ||
|
|
0e69f0c079 | ||
|
|
aee60c444f | ||
|
|
e7a504c66b | ||
|
|
45d9c7164c | ||
|
|
bd913a63a8 | ||
|
|
5a1938c078 | ||
|
|
015cd68756 | ||
|
|
76c0e5b2b8 | ||
|
|
0f8f707acd | ||
|
|
083cccea28 | ||
|
|
31b76b23ce | ||
|
|
af529f82e5 | ||
|
|
fc6669d254 | ||
|
|
c07785be67 | ||
|
|
80d2457b23 | ||
|
|
5dde2752d2 | ||
|
|
8bf4633cd0 | ||
|
|
bd66b8a1c8 | ||
|
|
be51e533fc | ||
|
|
f47ae12019 | ||
|
|
cab54a458f | ||
|
|
a30734f1bc | ||
|
|
7ad9f7b129 | ||
|
|
40a32e6826 | ||
|
|
ab0486aaf1 | ||
|
|
874593a167 | ||
|
|
03bf33630e | ||
|
|
933cce1b94 | ||
|
|
4a6ff573f8 | ||
|
|
1a737f7d19 | ||
|
|
cb69914f09 | ||
|
|
a372cbb2df | ||
|
|
0ce5caefe8 | ||
|
|
94dabb81f6 | ||
|
|
cd45bcfd03 | ||
|
|
49a53f7a45 | ||
|
|
9fa6c0918c | ||
|
|
e8d089e37e | ||
|
|
a931ce25fa | ||
|
|
b507abb4f7 | ||
|
|
66e7c6de62 | ||
|
|
4ce5ea9886 | ||
|
|
8c54b62f07 | ||
|
|
85eb50ed0f | ||
|
|
5ffd621e9d | ||
|
|
df93cb09da | ||
|
|
bbf04209f0 | ||
|
|
834bfa39c7 | ||
|
|
52b445f70b | ||
|
|
7b9b4fbb0c | ||
|
|
5af0ba1fcb | ||
|
|
85a9748291 | ||
|
|
b4005a7ffe | ||
|
|
82de1c88d4 | ||
|
|
1687ed7e0b | ||
|
|
c8b9c7d936 | ||
|
|
d57057ba28 | ||
|
|
66cee8daa4 | ||
|
|
afd7df0942 | ||
|
|
bd6ae33d32 | ||
|
|
70660a0d68 | ||
|
|
cdad18551a | ||
|
|
592c51d1a5 | ||
|
|
6a57b8a7e7 | ||
|
|
7a94e21c54 | ||
|
|
5b43f321e2 | ||
|
|
a4eafb934f | ||
|
|
7b59a665dd | ||
|
|
3d15450ffc | ||
|
|
b0c6d52461 | ||
|
|
2dc16dd29f | ||
|
|
d8924c536b | ||
|
|
3ebbf2cc46 | ||
|
|
f4079604c9 | ||
|
|
1f96a6beab | ||
|
|
b277a250e5 | ||
|
|
5b0e1a644d | ||
|
|
6bb3cfa9a3 | ||
|
|
9720868f5a | ||
|
|
8d8ee2a87a | ||
|
|
542e82ee5d | ||
|
|
0104b19502 | ||
|
|
120888b53e | ||
|
|
d2e2caed62 | ||
|
|
63066802a8 | ||
|
|
6128bb4ff3 | ||
|
|
982796255d | ||
|
|
36b15f474d | ||
|
|
13f71f8967 | ||
|
|
64336ffbee | ||
|
|
b09463d1b2 | ||
|
|
b5e6f46b9c | ||
|
|
08af4a0465 | ||
|
|
8c5df6321f | ||
|
|
d19f044961 | ||
|
|
e378d9f645 | ||
|
|
39dc0f71b4 | ||
|
|
0cef5c6b8c | ||
|
|
9b5a44cef4 | ||
|
|
29769ed91d | ||
|
|
867d794e17 | ||
|
|
fdd8458336 | ||
|
|
a0bec22e96 | ||
|
|
5aeb5cd214 | ||
|
|
e827ddffb9 | ||
|
|
98f80998b9 | ||
|
|
69727d0b12 | ||
|
|
84faf32b98 | ||
|
|
6ed6e27602 | ||
|
|
fb54678fef | ||
|
|
2cdcb3af12 | ||
|
|
cf7a336ac2 | ||
|
|
abfc64af95 | ||
|
|
42dd8d4754 | ||
|
|
a4e64350e9 | ||
|
|
6f567e3e10 | ||
|
|
c6c76ba360 | ||
|
|
429d3f518e | ||
|
|
26e4ad9bf9 | ||
|
|
6ab0cea4e3 | ||
|
|
277368ab43 | ||
|
|
e2921a648d | ||
|
|
c765dbc5cf | ||
|
|
a066c6fe2b | ||
|
|
311952d4dd | ||
|
|
96dab5d51e | ||
|
|
15d951b04e | ||
|
|
8ba830c04b | ||
|
|
acc82f39c4 | ||
|
|
fad0ec757b | ||
|
|
c9d73c6115 | ||
|
|
ab2f28ceef | ||
|
|
87e415992c | ||
|
|
12439d8761 | ||
|
|
4f200c73dc | ||
|
|
5f7e74e15c | ||
|
|
e8a5d0ae16 | ||
|
|
088fb00ca9 | ||
|
|
05676f3459 | ||
|
|
5d203b2278 | ||
|
|
795d50f02e | ||
|
|
cfe0ae1eda | ||
|
|
aa57a64c61 | ||
|
|
e36a81e189 | ||
|
|
88c07a9e48 | ||
|
|
bfd9f292a6 | ||
|
|
9edee9340b | ||
|
|
8550ed72f2 | ||
|
|
efffc29649 | ||
|
|
0ec909fd7a | ||
|
|
b10b0048f3 | ||
|
|
9bb188b519 | ||
|
|
7464835058 | ||
|
|
913b6bb6f6 | ||
|
|
000cf99546 | ||
|
|
c918267750 | ||
|
|
68921ee59b | ||
|
|
7e856283ee | ||
|
|
9c1b8da573 | ||
|
|
cb39b9cca8 | ||
|
|
788ac43ad1 | ||
|
|
57d19f3302 | ||
|
|
68bba623b6 | ||
|
|
35998058ce | ||
|
|
cdf94181d2 | ||
|
|
91ee90d827 | ||
|
|
d3316cd09c | ||
|
|
ac1b06967f | ||
|
|
47eb1e3e02 | ||
|
|
a69d8737ce | ||
|
|
341f47f0f2 | ||
|
|
19c605a9a8 | ||
|
|
54e4f54678 | ||
|
|
297a2cd9da | ||
|
|
d746d707b5 | ||
|
|
299252b650 | ||
|
|
fddd1c278f | ||
|
|
f52d7e3c28 | ||
|
|
a699210a29 | ||
|
|
b3c32a39e9 | ||
|
|
df27533b66 | ||
|
|
b96a1274c5 | ||
|
|
001a5107dd | ||
|
|
c8e456cdb1 | ||
|
|
0f6b00e1c8 | ||
|
|
5ea060a054 | ||
|
|
95bb2cf0bb | ||
|
|
4c472ce78b | ||
|
|
511fb89af0 | ||
|
|
7e524c0cd1 | ||
|
|
e3e2dc9fff | ||
|
|
1612e9093d | ||
|
|
f8649feea4 | ||
|
|
ac978c3fa7 | ||
|
|
efcc804149 | ||
|
|
db514e8f41 | ||
|
|
9c32f66329 | ||
|
|
0fd5102a26 | ||
|
|
840af15dae | ||
|
|
f1b0b3bcdb | ||
|
|
e5c0acbb43 | ||
|
|
834661c461 | ||
|
|
5204ab5a7e | ||
|
|
74862536a8 | ||
|
|
a24f1f5b95 | ||
|
|
0be76f746a | ||
|
|
fad89ff63f | ||
|
|
b8ae791191 | ||
|
|
ce754cbd91 | ||
|
|
0fdb6af98a | ||
|
|
f6c7f6a0f2 | ||
|
|
354999f37a | ||
|
|
348c622845 | ||
|
|
44bcdedaba | ||
|
|
755c0f3ce2 | ||
|
|
895bda41b5 | ||
|
|
b2df622cb6 | ||
|
|
9ba6e6d0f5 |
4
.dockerignore
Normal file
@@ -0,0 +1,4 @@
|
||||
node_modules
|
||||
npm-debug.log
|
||||
dist
|
||||
.idea
|
||||
14
.idea/dataSources.xml
generated
Normal file
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="DataSourceManagerImpl" format="xml" multifile-model="true">
|
||||
<data-source source="LOCAL" name="document.db" uuid="a2c75661-f9e2-478f-a69f-6a9409e69997">
|
||||
<driver-ref>sqlite.xerial</driver-ref>
|
||||
<synchronize>true</synchronize>
|
||||
<jdbc-driver>org.sqlite.JDBC</jdbc-driver>
|
||||
<jdbc-url>jdbc:sqlite:$USER_HOME$/trilium-data/document.db</jdbc-url>
|
||||
<driver-properties>
|
||||
<property name="enable_load_extension" value="true" />
|
||||
</driver-properties>
|
||||
</data-source>
|
||||
</component>
|
||||
</project>
|
||||
678
.idea/dataSources/a2c75661-f9e2-478f-a69f-6a9409e69997.xml
generated
Normal file
@@ -0,0 +1,678 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<dataSource name="document.db">
|
||||
<database-model serializer="dbm" rdbms="SQLITE" format-version="4.9">
|
||||
<root id="1">
|
||||
<ServerVersion>3.16.1</ServerVersion>
|
||||
</root>
|
||||
<schema id="2" parent="1" name="main">
|
||||
<Current>1</Current>
|
||||
<Visible>1</Visible>
|
||||
</schema>
|
||||
<collation id="3" parent="1" name="BINARY"/>
|
||||
<collation id="4" parent="1" name="NOCASE"/>
|
||||
<collation id="5" parent="1" name="RTRIM"/>
|
||||
<table id="6" parent="2" name="api_tokens"/>
|
||||
<table id="7" parent="2" name="branches"/>
|
||||
<table id="8" parent="2" name="event_log"/>
|
||||
<table id="9" parent="2" name="images"/>
|
||||
<table id="10" parent="2" name="labels"/>
|
||||
<table id="11" parent="2" name="note_images"/>
|
||||
<table id="12" parent="2" name="note_revisions"/>
|
||||
<table id="13" parent="2" name="notes"/>
|
||||
<table id="14" parent="2" name="options"/>
|
||||
<table id="15" parent="2" name="recent_notes"/>
|
||||
<table id="16" parent="2" name="source_ids"/>
|
||||
<table id="17" parent="2" name="sqlite_master">
|
||||
<System>1</System>
|
||||
</table>
|
||||
<table id="18" parent="2" name="sqlite_sequence">
|
||||
<System>1</System>
|
||||
</table>
|
||||
<table id="19" parent="2" name="sync"/>
|
||||
<column id="20" parent="6" name="apiTokenId">
|
||||
<Position>1</Position>
|
||||
<DataType>TEXT|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="21" parent="6" name="token">
|
||||
<Position>2</Position>
|
||||
<DataType>TEXT|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="22" parent="6" name="dateCreated">
|
||||
<Position>3</Position>
|
||||
<DataType>TEXT|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="23" parent="6" name="isDeleted">
|
||||
<Position>4</Position>
|
||||
<DataType>INT|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
<DefaultExpression>0</DefaultExpression>
|
||||
</column>
|
||||
<column id="24" parent="6" name="hash">
|
||||
<Position>5</Position>
|
||||
<DataType>TEXT|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
<DefaultExpression>""</DefaultExpression>
|
||||
</column>
|
||||
<index id="25" parent="6" name="sqlite_autoindex_api_tokens_1">
|
||||
<NameSurrogate>1</NameSurrogate>
|
||||
<ColNames>apiTokenId</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
<Unique>1</Unique>
|
||||
</index>
|
||||
<key id="26" parent="6">
|
||||
<ColNames>apiTokenId</ColNames>
|
||||
<Primary>1</Primary>
|
||||
<UnderlyingIndexName>sqlite_autoindex_api_tokens_1</UnderlyingIndexName>
|
||||
</key>
|
||||
<column id="27" parent="7" name="branchId">
|
||||
<Position>1</Position>
|
||||
<DataType>TEXT|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="28" parent="7" name="noteId">
|
||||
<Position>2</Position>
|
||||
<DataType>TEXT|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="29" parent="7" name="parentNoteId">
|
||||
<Position>3</Position>
|
||||
<DataType>TEXT|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="30" parent="7" name="notePosition">
|
||||
<Position>4</Position>
|
||||
<DataType>INTEGER|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="31" parent="7" name="prefix">
|
||||
<Position>5</Position>
|
||||
<DataType>TEXT|0s</DataType>
|
||||
</column>
|
||||
<column id="32" parent="7" name="isExpanded">
|
||||
<Position>6</Position>
|
||||
<DataType>BOOLEAN|0s</DataType>
|
||||
</column>
|
||||
<column id="33" parent="7" name="isDeleted">
|
||||
<Position>7</Position>
|
||||
<DataType>INTEGER|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
<DefaultExpression>0</DefaultExpression>
|
||||
</column>
|
||||
<column id="34" parent="7" name="dateModified">
|
||||
<Position>8</Position>
|
||||
<DataType>TEXT|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="35" parent="7" name="hash">
|
||||
<Position>9</Position>
|
||||
<DataType>TEXT|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
<DefaultExpression>""</DefaultExpression>
|
||||
</column>
|
||||
<column id="36" parent="7" name="dateCreated">
|
||||
<Position>10</Position>
|
||||
<DataType>TEXT|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
<DefaultExpression>'1970-01-01T00:00:00.000Z'</DefaultExpression>
|
||||
</column>
|
||||
<index id="37" parent="7" name="sqlite_autoindex_branches_1">
|
||||
<NameSurrogate>1</NameSurrogate>
|
||||
<ColNames>branchId</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
<Unique>1</Unique>
|
||||
</index>
|
||||
<index id="38" parent="7" name="IDX_branches_noteId_parentNoteId">
|
||||
<ColNames>noteId
|
||||
parentNoteId</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
</index>
|
||||
<index id="39" parent="7" name="IDX_branches_noteId">
|
||||
<ColNames>noteId</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
</index>
|
||||
<index id="40" parent="7" name="IDX_branches_parentNoteId">
|
||||
<ColNames>parentNoteId</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
</index>
|
||||
<key id="41" parent="7">
|
||||
<ColNames>branchId</ColNames>
|
||||
<Primary>1</Primary>
|
||||
<UnderlyingIndexName>sqlite_autoindex_branches_1</UnderlyingIndexName>
|
||||
</key>
|
||||
<column id="42" parent="8" name="eventId">
|
||||
<Position>1</Position>
|
||||
<DataType>TEXT|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="43" parent="8" name="noteId">
|
||||
<Position>2</Position>
|
||||
<DataType>TEXT|0s</DataType>
|
||||
</column>
|
||||
<column id="44" parent="8" name="comment">
|
||||
<Position>3</Position>
|
||||
<DataType>TEXT|0s</DataType>
|
||||
</column>
|
||||
<column id="45" parent="8" name="dateCreated">
|
||||
<Position>4</Position>
|
||||
<DataType>TEXT|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<index id="46" parent="8" name="sqlite_autoindex_event_log_1">
|
||||
<NameSurrogate>1</NameSurrogate>
|
||||
<ColNames>eventId</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
<Unique>1</Unique>
|
||||
</index>
|
||||
<key id="47" parent="8">
|
||||
<ColNames>eventId</ColNames>
|
||||
<Primary>1</Primary>
|
||||
<UnderlyingIndexName>sqlite_autoindex_event_log_1</UnderlyingIndexName>
|
||||
</key>
|
||||
<column id="48" parent="9" name="imageId">
|
||||
<Position>1</Position>
|
||||
<DataType>TEXT|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="49" parent="9" name="format">
|
||||
<Position>2</Position>
|
||||
<DataType>TEXT|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="50" parent="9" name="checksum">
|
||||
<Position>3</Position>
|
||||
<DataType>TEXT|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="51" parent="9" name="name">
|
||||
<Position>4</Position>
|
||||
<DataType>TEXT|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="52" parent="9" name="data">
|
||||
<Position>5</Position>
|
||||
<DataType>BLOB|0s</DataType>
|
||||
</column>
|
||||
<column id="53" parent="9" name="isDeleted">
|
||||
<Position>6</Position>
|
||||
<DataType>INT|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
<DefaultExpression>0</DefaultExpression>
|
||||
</column>
|
||||
<column id="54" parent="9" name="dateModified">
|
||||
<Position>7</Position>
|
||||
<DataType>TEXT|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="55" parent="9" name="dateCreated">
|
||||
<Position>8</Position>
|
||||
<DataType>TEXT|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="56" parent="9" name="hash">
|
||||
<Position>9</Position>
|
||||
<DataType>TEXT|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
<DefaultExpression>""</DefaultExpression>
|
||||
</column>
|
||||
<index id="57" parent="9" name="sqlite_autoindex_images_1">
|
||||
<NameSurrogate>1</NameSurrogate>
|
||||
<ColNames>imageId</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
<Unique>1</Unique>
|
||||
</index>
|
||||
<key id="58" parent="9">
|
||||
<ColNames>imageId</ColNames>
|
||||
<Primary>1</Primary>
|
||||
<UnderlyingIndexName>sqlite_autoindex_images_1</UnderlyingIndexName>
|
||||
</key>
|
||||
<column id="59" parent="10" name="labelId">
|
||||
<Position>1</Position>
|
||||
<DataType>TEXT|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="60" parent="10" name="noteId">
|
||||
<Position>2</Position>
|
||||
<DataType>TEXT|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="61" parent="10" name="name">
|
||||
<Position>3</Position>
|
||||
<DataType>TEXT|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="62" parent="10" name="value">
|
||||
<Position>4</Position>
|
||||
<DataType>TEXT|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
<DefaultExpression>''</DefaultExpression>
|
||||
</column>
|
||||
<column id="63" parent="10" name="position">
|
||||
<Position>5</Position>
|
||||
<DataType>INT|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
<DefaultExpression>0</DefaultExpression>
|
||||
</column>
|
||||
<column id="64" parent="10" name="dateCreated">
|
||||
<Position>6</Position>
|
||||
<DataType>TEXT|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="65" parent="10" name="dateModified">
|
||||
<Position>7</Position>
|
||||
<DataType>TEXT|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="66" parent="10" name="isDeleted">
|
||||
<Position>8</Position>
|
||||
<DataType>INT|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="67" parent="10" name="hash">
|
||||
<Position>9</Position>
|
||||
<DataType>TEXT|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
<DefaultExpression>""</DefaultExpression>
|
||||
</column>
|
||||
<index id="68" parent="10" name="sqlite_autoindex_labels_1">
|
||||
<NameSurrogate>1</NameSurrogate>
|
||||
<ColNames>labelId</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
<Unique>1</Unique>
|
||||
</index>
|
||||
<index id="69" parent="10" name="IDX_labels_noteId">
|
||||
<ColNames>noteId</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
</index>
|
||||
<index id="70" parent="10" name="IDX_labels_name_value">
|
||||
<ColNames>name
|
||||
value</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
</index>
|
||||
<key id="71" parent="10">
|
||||
<ColNames>labelId</ColNames>
|
||||
<Primary>1</Primary>
|
||||
<UnderlyingIndexName>sqlite_autoindex_labels_1</UnderlyingIndexName>
|
||||
</key>
|
||||
<column id="72" parent="11" name="noteImageId">
|
||||
<Position>1</Position>
|
||||
<DataType>TEXT|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="73" parent="11" name="noteId">
|
||||
<Position>2</Position>
|
||||
<DataType>TEXT|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="74" parent="11" name="imageId">
|
||||
<Position>3</Position>
|
||||
<DataType>TEXT|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="75" parent="11" name="isDeleted">
|
||||
<Position>4</Position>
|
||||
<DataType>INT|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
<DefaultExpression>0</DefaultExpression>
|
||||
</column>
|
||||
<column id="76" parent="11" name="dateModified">
|
||||
<Position>5</Position>
|
||||
<DataType>TEXT|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="77" parent="11" name="dateCreated">
|
||||
<Position>6</Position>
|
||||
<DataType>TEXT|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="78" parent="11" name="hash">
|
||||
<Position>7</Position>
|
||||
<DataType>TEXT|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
<DefaultExpression>""</DefaultExpression>
|
||||
</column>
|
||||
<index id="79" parent="11" name="sqlite_autoindex_note_images_1">
|
||||
<NameSurrogate>1</NameSurrogate>
|
||||
<ColNames>noteImageId</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
<Unique>1</Unique>
|
||||
</index>
|
||||
<index id="80" parent="11" name="IDX_note_images_noteId_imageId">
|
||||
<ColNames>noteId
|
||||
imageId</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
</index>
|
||||
<index id="81" parent="11" name="IDX_note_images_noteId">
|
||||
<ColNames>noteId</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
</index>
|
||||
<index id="82" parent="11" name="IDX_note_images_imageId">
|
||||
<ColNames>imageId</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
</index>
|
||||
<key id="83" parent="11">
|
||||
<ColNames>noteImageId</ColNames>
|
||||
<Primary>1</Primary>
|
||||
<UnderlyingIndexName>sqlite_autoindex_note_images_1</UnderlyingIndexName>
|
||||
</key>
|
||||
<column id="84" parent="12" name="noteRevisionId">
|
||||
<Position>1</Position>
|
||||
<DataType>TEXT|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="85" parent="12" name="noteId">
|
||||
<Position>2</Position>
|
||||
<DataType>TEXT|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="86" parent="12" name="title">
|
||||
<Position>3</Position>
|
||||
<DataType>TEXT|0s</DataType>
|
||||
</column>
|
||||
<column id="87" parent="12" name="content">
|
||||
<Position>4</Position>
|
||||
<DataType>TEXT|0s</DataType>
|
||||
</column>
|
||||
<column id="88" parent="12" name="isProtected">
|
||||
<Position>5</Position>
|
||||
<DataType>INT|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
<DefaultExpression>0</DefaultExpression>
|
||||
</column>
|
||||
<column id="89" parent="12" name="dateModifiedFrom">
|
||||
<Position>6</Position>
|
||||
<DataType>TEXT|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="90" parent="12" name="dateModifiedTo">
|
||||
<Position>7</Position>
|
||||
<DataType>TEXT|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="91" parent="12" name="type">
|
||||
<Position>8</Position>
|
||||
<DataType>TEXT|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
<DefaultExpression>''</DefaultExpression>
|
||||
</column>
|
||||
<column id="92" parent="12" name="mime">
|
||||
<Position>9</Position>
|
||||
<DataType>TEXT|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
<DefaultExpression>''</DefaultExpression>
|
||||
</column>
|
||||
<column id="93" parent="12" name="hash">
|
||||
<Position>10</Position>
|
||||
<DataType>TEXT|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
<DefaultExpression>""</DefaultExpression>
|
||||
</column>
|
||||
<index id="94" parent="12" name="sqlite_autoindex_note_revisions_1">
|
||||
<NameSurrogate>1</NameSurrogate>
|
||||
<ColNames>noteRevisionId</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
<Unique>1</Unique>
|
||||
</index>
|
||||
<index id="95" parent="12" name="IDX_note_revisions_noteId">
|
||||
<ColNames>noteId</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
</index>
|
||||
<index id="96" parent="12" name="IDX_note_revisions_dateModifiedFrom">
|
||||
<ColNames>dateModifiedFrom</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
</index>
|
||||
<index id="97" parent="12" name="IDX_note_revisions_dateModifiedTo">
|
||||
<ColNames>dateModifiedTo</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
</index>
|
||||
<key id="98" parent="12">
|
||||
<ColNames>noteRevisionId</ColNames>
|
||||
<Primary>1</Primary>
|
||||
<UnderlyingIndexName>sqlite_autoindex_note_revisions_1</UnderlyingIndexName>
|
||||
</key>
|
||||
<column id="99" parent="13" name="noteId">
|
||||
<Position>1</Position>
|
||||
<DataType>TEXT|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="100" parent="13" name="title">
|
||||
<Position>2</Position>
|
||||
<DataType>TEXT|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
<DefaultExpression>"unnamed"</DefaultExpression>
|
||||
</column>
|
||||
<column id="101" parent="13" name="content">
|
||||
<Position>3</Position>
|
||||
<DataType>TEXT|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
<DefaultExpression>""</DefaultExpression>
|
||||
</column>
|
||||
<column id="102" parent="13" name="isProtected">
|
||||
<Position>4</Position>
|
||||
<DataType>INT|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
<DefaultExpression>0</DefaultExpression>
|
||||
</column>
|
||||
<column id="103" parent="13" name="isDeleted">
|
||||
<Position>5</Position>
|
||||
<DataType>INT|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
<DefaultExpression>0</DefaultExpression>
|
||||
</column>
|
||||
<column id="104" parent="13" name="dateCreated">
|
||||
<Position>6</Position>
|
||||
<DataType>TEXT|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="105" parent="13" name="dateModified">
|
||||
<Position>7</Position>
|
||||
<DataType>TEXT|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="106" parent="13" name="type">
|
||||
<Position>8</Position>
|
||||
<DataType>TEXT|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
<DefaultExpression>'text'</DefaultExpression>
|
||||
</column>
|
||||
<column id="107" parent="13" name="mime">
|
||||
<Position>9</Position>
|
||||
<DataType>TEXT|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
<DefaultExpression>'text/html'</DefaultExpression>
|
||||
</column>
|
||||
<column id="108" parent="13" name="hash">
|
||||
<Position>10</Position>
|
||||
<DataType>TEXT|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
<DefaultExpression>""</DefaultExpression>
|
||||
</column>
|
||||
<index id="109" parent="13" name="sqlite_autoindex_notes_1">
|
||||
<NameSurrogate>1</NameSurrogate>
|
||||
<ColNames>noteId</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
<Unique>1</Unique>
|
||||
</index>
|
||||
<index id="110" parent="13" name="IDX_notes_type">
|
||||
<ColNames>type</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
</index>
|
||||
<key id="111" parent="13">
|
||||
<ColNames>noteId</ColNames>
|
||||
<Primary>1</Primary>
|
||||
<UnderlyingIndexName>sqlite_autoindex_notes_1</UnderlyingIndexName>
|
||||
</key>
|
||||
<column id="112" parent="14" name="optionId">
|
||||
<Position>1</Position>
|
||||
<DataType>TEXT|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="113" parent="14" name="name">
|
||||
<Position>2</Position>
|
||||
<DataType>TEXT|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="114" parent="14" name="value">
|
||||
<Position>3</Position>
|
||||
<DataType>TEXT|0s</DataType>
|
||||
</column>
|
||||
<column id="115" parent="14" name="dateModified">
|
||||
<Position>4</Position>
|
||||
<DataType>INT|0s</DataType>
|
||||
</column>
|
||||
<column id="116" parent="14" name="isSynced">
|
||||
<Position>5</Position>
|
||||
<DataType>INTEGER|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
<DefaultExpression>0</DefaultExpression>
|
||||
</column>
|
||||
<column id="117" parent="14" name="hash">
|
||||
<Position>6</Position>
|
||||
<DataType>TEXT|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
<DefaultExpression>""</DefaultExpression>
|
||||
</column>
|
||||
<column id="118" parent="14" name="dateCreated">
|
||||
<Position>7</Position>
|
||||
<DataType>TEXT|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
<DefaultExpression>'1970-01-01T00:00:00.000Z'</DefaultExpression>
|
||||
</column>
|
||||
<index id="119" parent="14" name="sqlite_autoindex_options_1">
|
||||
<NameSurrogate>1</NameSurrogate>
|
||||
<ColNames>optionId</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
<Unique>1</Unique>
|
||||
</index>
|
||||
<key id="120" parent="14">
|
||||
<ColNames>optionId</ColNames>
|
||||
<Primary>1</Primary>
|
||||
<UnderlyingIndexName>sqlite_autoindex_options_1</UnderlyingIndexName>
|
||||
</key>
|
||||
<column id="121" parent="15" name="branchId">
|
||||
<Position>1</Position>
|
||||
<DataType>TEXT|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="122" parent="15" name="notePath">
|
||||
<Position>2</Position>
|
||||
<DataType>TEXT|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="123" parent="15" name="hash">
|
||||
<Position>3</Position>
|
||||
<DataType>TEXT|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
<DefaultExpression>""</DefaultExpression>
|
||||
</column>
|
||||
<column id="124" parent="15" name="dateCreated">
|
||||
<Position>4</Position>
|
||||
<DataType>TEXT|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="125" parent="15" name="isDeleted">
|
||||
<Position>5</Position>
|
||||
<DataType>INT|0s</DataType>
|
||||
</column>
|
||||
<index id="126" parent="15" name="sqlite_autoindex_recent_notes_1">
|
||||
<NameSurrogate>1</NameSurrogate>
|
||||
<ColNames>branchId</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
<Unique>1</Unique>
|
||||
</index>
|
||||
<key id="127" parent="15">
|
||||
<ColNames>branchId</ColNames>
|
||||
<Primary>1</Primary>
|
||||
<UnderlyingIndexName>sqlite_autoindex_recent_notes_1</UnderlyingIndexName>
|
||||
</key>
|
||||
<column id="128" parent="16" name="sourceId">
|
||||
<Position>1</Position>
|
||||
<DataType>TEXT|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="129" parent="16" name="dateCreated">
|
||||
<Position>2</Position>
|
||||
<DataType>TEXT|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<index id="130" parent="16" name="sqlite_autoindex_source_ids_1">
|
||||
<NameSurrogate>1</NameSurrogate>
|
||||
<ColNames>sourceId</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
<Unique>1</Unique>
|
||||
</index>
|
||||
<key id="131" parent="16">
|
||||
<ColNames>sourceId</ColNames>
|
||||
<Primary>1</Primary>
|
||||
<UnderlyingIndexName>sqlite_autoindex_source_ids_1</UnderlyingIndexName>
|
||||
</key>
|
||||
<column id="132" parent="17" name="type">
|
||||
<Position>1</Position>
|
||||
<DataType>text|0s</DataType>
|
||||
</column>
|
||||
<column id="133" parent="17" name="name">
|
||||
<Position>2</Position>
|
||||
<DataType>text|0s</DataType>
|
||||
</column>
|
||||
<column id="134" parent="17" name="tbl_name">
|
||||
<Position>3</Position>
|
||||
<DataType>text|0s</DataType>
|
||||
</column>
|
||||
<column id="135" parent="17" name="rootpage">
|
||||
<Position>4</Position>
|
||||
<DataType>integer|0s</DataType>
|
||||
</column>
|
||||
<column id="136" parent="17" name="sql">
|
||||
<Position>5</Position>
|
||||
<DataType>text|0s</DataType>
|
||||
</column>
|
||||
<column id="137" parent="18" name="name">
|
||||
<Position>1</Position>
|
||||
</column>
|
||||
<column id="138" parent="18" name="seq">
|
||||
<Position>2</Position>
|
||||
</column>
|
||||
<column id="139" parent="19" name="id">
|
||||
<Position>1</Position>
|
||||
<DataType>INTEGER|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
<SequenceIdentity>1</SequenceIdentity>
|
||||
</column>
|
||||
<column id="140" parent="19" name="entityName">
|
||||
<Position>2</Position>
|
||||
<DataType>TEXT|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="141" parent="19" name="entityId">
|
||||
<Position>3</Position>
|
||||
<DataType>TEXT|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="142" parent="19" name="sourceId">
|
||||
<Position>4</Position>
|
||||
<DataType>TEXT|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<column id="143" parent="19" name="syncDate">
|
||||
<Position>5</Position>
|
||||
<DataType>TEXT|0s</DataType>
|
||||
<NotNull>1</NotNull>
|
||||
</column>
|
||||
<index id="144" parent="19" name="IDX_sync_entityName_entityId">
|
||||
<ColNames>entityName
|
||||
entityId</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
<Unique>1</Unique>
|
||||
</index>
|
||||
<index id="145" parent="19" name="IDX_sync_syncDate">
|
||||
<ColNames>syncDate</ColNames>
|
||||
<ColumnCollations></ColumnCollations>
|
||||
</index>
|
||||
<key id="146" parent="19">
|
||||
<ColNames>id</ColNames>
|
||||
<Primary>1</Primary>
|
||||
</key>
|
||||
</database-model>
|
||||
</dataSource>
|
||||
2
.idea/dataSources/a2c75661-f9e2-478f-a69f-6a9409e69997/storage_v2/_src_/schema/main.uQUzAA.meta
generated
Normal file
@@ -0,0 +1,2 @@
|
||||
#n:main
|
||||
!<md> [0, 0, null, null, -2147483648, -2147483648]
|
||||
10
.idea/inspectionProfiles/Project_Default.xml
generated
Normal file
@@ -0,0 +1,10 @@
|
||||
<component name="InspectionProjectProfileManager">
|
||||
<profile version="1.0">
|
||||
<option name="myName" value="Project Default" />
|
||||
<inspection_tool class="SpellCheckingInspection" enabled="false" level="TYPO" enabled_by_default="false">
|
||||
<option name="processCode" value="true" />
|
||||
<option name="processLiterals" value="true" />
|
||||
<option name="processComments" value="true" />
|
||||
</inspection_tool>
|
||||
</profile>
|
||||
</component>
|
||||
9
.idea/jsLinters/jslint.xml
generated
Normal file
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="JSLintConfiguration">
|
||||
<option devel="true" />
|
||||
<option es6="true" />
|
||||
<option maxerr="50" />
|
||||
<option node="true" />
|
||||
</component>
|
||||
</project>
|
||||
9
.idea/misc.xml
generated
Normal file
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="JavaScriptSettings">
|
||||
<option name="languageLevel" value="ES6" />
|
||||
</component>
|
||||
<component name="ProjectRootManager">
|
||||
<output url="file://$PROJECT_DIR$/out" />
|
||||
</component>
|
||||
</project>
|
||||
8
.idea/modules.xml
generated
Normal file
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/trilium.iml" filepath="$PROJECT_DIR$/trilium.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
||||
6
.idea/vcs.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
||||
21
Dockerfile
Normal file
@@ -0,0 +1,21 @@
|
||||
FROM node:8.11.2
|
||||
|
||||
RUN apt-get update && apt-get install -y nasm
|
||||
|
||||
# Create app directory
|
||||
WORKDIR /usr/src/app
|
||||
|
||||
# Install app dependencies
|
||||
# A wildcard is used to ensure both package.json AND package-lock.json are copied
|
||||
# where available (npm@5+)
|
||||
COPY package*.json ./
|
||||
|
||||
RUN npm install --production
|
||||
# If you are building your code for production
|
||||
# RUN npm install --only=production
|
||||
|
||||
# Bundle app source
|
||||
COPY . .
|
||||
|
||||
EXPOSE 8080
|
||||
CMD [ "node", "src/www" ]
|
||||
@@ -1,4 +1,6 @@
|
||||
# Trilium Notes
|
||||
|
||||
[](https://gitter.im/trilium-notes/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||
Trilium Notes is a hierarchical note taking application. Picture tells a thousand words:
|
||||
|
||||

|
||||
@@ -10,7 +12,7 @@ Trilium Notes is a hierarchical note taking application. Picture tells a thousan
|
||||
* WYSIWYG (What You See Is What You Get) editing
|
||||
* Fast and easy [navigation between notes](https://github.com/zadam/trilium/wiki/Note-navigation)
|
||||
* Seamless note versioning
|
||||
* Note attributes can be used to tag/label notes as an alternative note organization and querying
|
||||
* Note labels can be used to tag/label notes as an alternative note organization and querying
|
||||
* Can be deployed as web application and / or desktop application with offline access (electron based)
|
||||
* [Synchronization with](https://github.com/zadam/trilium/wiki/Synchronization) self-hosted sync server
|
||||
* Strong [note encryption](https://github.com/zadam/trilium/wiki/Protected-notes)
|
||||
@@ -35,11 +37,12 @@ List of documentation pages:
|
||||
* [Installation as webapp](https://github.com/zadam/trilium/wiki/Installation-as-webapp)
|
||||
* [Note navigation](https://github.com/zadam/trilium/wiki/Note-navigation)
|
||||
* [Tree manipulation](https://github.com/zadam/trilium/wiki/Tree-manipulation)
|
||||
* [Attributes](https://github.com/zadam/trilium/wiki/Attributes)
|
||||
* [Labels](https://github.com/zadam/trilium/wiki/Labels)
|
||||
* [Links](https://github.com/zadam/trilium/wiki/Links)
|
||||
* [Cloning notes](https://github.com/zadam/trilium/wiki/Cloning-notes)
|
||||
* [Protected notes](https://github.com/zadam/trilium/wiki/Protected-notes)
|
||||
* [Synchronization](https://github.com/zadam/trilium/wiki/Synchronization)
|
||||
* [Document](https://github.com/zadam/trilium/wiki/Document)
|
||||
* [Theming](https://github.com/zadam/trilium/wiki/Theming)
|
||||
* [Keyboard shortcuts](https://github.com/zadam/trilium/wiki/Keyboard-shortcuts)
|
||||
* [Troubleshooting](https://github.com/zadam/trilium/wiki/Troubleshooting)
|
||||
|
||||
8
bin/build-docker.sh
Executable file
@@ -0,0 +1,8 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
if [[ $# -eq 0 ]] ; then
|
||||
echo "Missing argument of new version"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
sudo docker build -t zadam/trilium:latest -t zadam/trilium:$1 .
|
||||
9
bin/push-docker-image.sh
Normal file
@@ -0,0 +1,9 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
if [[ $# -eq 0 ]] ; then
|
||||
echo "Missing argument of new version"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
sudo docker push zadam/trilium:latest
|
||||
sudo docker push zadam/trilium:$1
|
||||
@@ -24,9 +24,9 @@ jq '.version = "'$VERSION'"' package.json|sponge package.json
|
||||
|
||||
git add package.json
|
||||
|
||||
echo 'module.exports = { build_date:"'`date --iso-8601=seconds`'", build_revision: "'`git log -1 --format="%H"`'" };' > services/build.js
|
||||
echo 'module.exports = { buildDate:"'`date --iso-8601=seconds`'", buildRevision: "'`git log -1 --format="%H"`'" };' > src/services/build.js
|
||||
|
||||
git add services/build.js
|
||||
git add src/services/build.js
|
||||
|
||||
TAG=v$VERSION
|
||||
|
||||
@@ -47,6 +47,7 @@ bin/package.sh
|
||||
LINUX_X64_BUILD=trilium-linux-x64-$VERSION.7z
|
||||
LINUX_IA32_BUILD=trilium-linux-ia32-$VERSION.7z
|
||||
WINDOWS_X64_BUILD=trilium-windows-x64-$VERSION.7z
|
||||
SERVER_BUILD=trilium-linux-x64-server.elf
|
||||
|
||||
echo "Creating release in GitHub"
|
||||
|
||||
@@ -75,4 +76,21 @@ github-release upload \
|
||||
--name "$WINDOWS_X64_BUILD" \
|
||||
--file "dist/$WINDOWS_X64_BUILD"
|
||||
|
||||
echo "Packaging server version"
|
||||
|
||||
npm run build-pkg
|
||||
|
||||
github-release upload \
|
||||
--tag $TAG \
|
||||
--name "$SERVER_BUILD" \
|
||||
--file "dist/$SERVER_BUILD"
|
||||
|
||||
echo "Building docker image"
|
||||
|
||||
bin/build-docker.sh $VERSION
|
||||
|
||||
echo "Pushing docker image to dockerhub"
|
||||
|
||||
bin/push-docker-image.sh $VERSION
|
||||
|
||||
echo "Release finished!"
|
||||
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');
|
||||
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;
|
||||
171
db/schema.sql
@@ -1,49 +1,21 @@
|
||||
CREATE TABLE IF NOT EXISTS "options" (
|
||||
`name` TEXT NOT NULL PRIMARY KEY,
|
||||
`value` TEXT,
|
||||
`dateModified` INT,
|
||||
isSynced INTEGER NOT NULL DEFAULT 0);
|
||||
CREATE TABLE IF NOT EXISTS "sync" (
|
||||
`id` INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||
`entityName` TEXT NOT NULL,
|
||||
`entityId` TEXT NOT NULL,
|
||||
`sourceId` TEXT NOT NULL,
|
||||
`syncDate` TEXT NOT NULL);
|
||||
CREATE UNIQUE INDEX `IDX_sync_entityName_entityId` ON `sync` (
|
||||
`entityName`,
|
||||
`entityId`
|
||||
);
|
||||
CREATE INDEX `IDX_sync_syncDate` ON `sync` (
|
||||
`syncDate`
|
||||
);
|
||||
CREATE TABLE IF NOT EXISTS "source_ids" (
|
||||
`sourceId` TEXT NOT NULL,
|
||||
`dateCreated` TEXT NOT NULL,
|
||||
PRIMARY KEY(`sourceId`)
|
||||
);
|
||||
CREATE TABLE IF NOT EXISTS "notes" (
|
||||
`noteId` TEXT NOT NULL,
|
||||
`title` TEXT,
|
||||
`content` TEXT,
|
||||
`isProtected` INT NOT NULL DEFAULT 0,
|
||||
`isDeleted` INT NOT NULL DEFAULT 0,
|
||||
`dateCreated` TEXT NOT NULL,
|
||||
`dateModified` TEXT NOT NULL,
|
||||
type TEXT NOT NULL DEFAULT 'text',
|
||||
mime TEXT NOT NULL DEFAULT 'text/html',
|
||||
PRIMARY KEY(`noteId`)
|
||||
);
|
||||
CREATE TABLE IF NOT EXISTS "event_log" (
|
||||
`id` INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||
`noteId` TEXT,
|
||||
`comment` TEXT,
|
||||
`dateAdded` TEXT NOT NULL,
|
||||
FOREIGN KEY(noteId) REFERENCES notes(noteId)
|
||||
);
|
||||
CREATE TABLE IF NOT EXISTS "note_tree" (
|
||||
`noteTreeId` TEXT NOT NULL,
|
||||
`noteId` TEXT NOT NULL,
|
||||
`parentNoteId` TEXT NOT NULL,
|
||||
`notePosition` INTEGER NOT NULL,
|
||||
`prefix` TEXT,
|
||||
`isExpanded` BOOLEAN,
|
||||
`isDeleted` INTEGER NOT NULL DEFAULT 0,
|
||||
`dateModified` TEXT NOT NULL,
|
||||
PRIMARY KEY(`noteTreeId`)
|
||||
);
|
||||
CREATE TABLE IF NOT EXISTS "note_revisions" (
|
||||
`noteRevisionId` TEXT NOT NULL PRIMARY KEY,
|
||||
`noteId` TEXT NOT NULL,
|
||||
@@ -52,12 +24,15 @@ CREATE TABLE IF NOT EXISTS "note_revisions" (
|
||||
`isProtected` INT NOT NULL DEFAULT 0,
|
||||
`dateModifiedFrom` TEXT NOT NULL,
|
||||
`dateModifiedTo` TEXT NOT NULL
|
||||
, type TEXT DEFAULT '' NOT NULL, mime TEXT DEFAULT '' NOT NULL, hash TEXT DEFAULT "" NOT NULL);
|
||||
CREATE INDEX `IDX_note_revisions_noteId` ON `note_revisions` (
|
||||
`noteId`
|
||||
);
|
||||
CREATE TABLE IF NOT EXISTS "recent_notes" (
|
||||
`noteTreeId` TEXT NOT NULL PRIMARY KEY,
|
||||
`notePath` TEXT NOT NULL,
|
||||
`dateAccessed` TEXT NOT NULL,
|
||||
isDeleted INT
|
||||
CREATE INDEX `IDX_note_revisions_dateModifiedFrom` ON `note_revisions` (
|
||||
`dateModifiedFrom`
|
||||
);
|
||||
CREATE INDEX `IDX_note_revisions_dateModifiedTo` ON `note_revisions` (
|
||||
`dateModifiedTo`
|
||||
);
|
||||
CREATE TABLE IF NOT EXISTS "images"
|
||||
(
|
||||
@@ -69,7 +44,7 @@ CREATE TABLE IF NOT EXISTS "images"
|
||||
isDeleted INT NOT NULL DEFAULT 0,
|
||||
dateModified TEXT NOT NULL,
|
||||
dateCreated TEXT NOT NULL
|
||||
);
|
||||
, hash TEXT DEFAULT "" NOT NULL);
|
||||
CREATE TABLE note_images
|
||||
(
|
||||
noteImageId TEXT PRIMARY KEY NOT NULL,
|
||||
@@ -78,54 +53,84 @@ CREATE TABLE note_images
|
||||
isDeleted INT NOT NULL DEFAULT 0,
|
||||
dateModified TEXT NOT NULL,
|
||||
dateCreated TEXT NOT NULL
|
||||
);
|
||||
CREATE TABLE IF NOT EXISTS "attributes"
|
||||
(
|
||||
attributeId TEXT PRIMARY KEY NOT NULL,
|
||||
noteId TEXT NOT NULL,
|
||||
name TEXT NOT NULL,
|
||||
value TEXT,
|
||||
position INT NOT NULL DEFAULT 0,
|
||||
dateCreated TEXT NOT NULL,
|
||||
dateModified TEXT NOT NULL,
|
||||
isDeleted INT NOT NULL
|
||||
);
|
||||
CREATE UNIQUE INDEX `IDX_sync_entityName_entityId` ON `sync` (
|
||||
`entityName`,
|
||||
`entityId`
|
||||
);
|
||||
CREATE INDEX `IDX_sync_syncDate` ON `sync` (
|
||||
`syncDate`
|
||||
);
|
||||
CREATE INDEX `IDX_notes_isDeleted` ON `notes` (
|
||||
`isDeleted`
|
||||
);
|
||||
CREATE INDEX `IDX_note_tree_noteId` ON `note_tree` (
|
||||
`noteId`
|
||||
);
|
||||
CREATE INDEX `IDX_note_tree_noteId_parentNoteId` ON `note_tree` (
|
||||
`noteId`,
|
||||
`parentNoteId`
|
||||
);
|
||||
CREATE INDEX `IDX_note_revisions_noteId` ON `note_revisions` (
|
||||
`noteId`
|
||||
);
|
||||
CREATE INDEX `IDX_note_revisions_dateModifiedFrom` ON `note_revisions` (
|
||||
`dateModifiedFrom`
|
||||
);
|
||||
CREATE INDEX `IDX_note_revisions_dateModifiedTo` ON `note_revisions` (
|
||||
`dateModifiedTo`
|
||||
);
|
||||
, hash TEXT DEFAULT "" NOT NULL);
|
||||
CREATE INDEX IDX_note_images_noteId ON note_images (noteId);
|
||||
CREATE INDEX IDX_note_images_imageId ON note_images (imageId);
|
||||
CREATE INDEX IDX_note_images_noteId_imageId ON note_images (noteId, imageId);
|
||||
CREATE INDEX IDX_attributes_noteId ON attributes (noteId);
|
||||
CREATE INDEX IDX_attributes_name_value ON attributes (name, value);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS "api_tokens"
|
||||
(
|
||||
apiTokenId TEXT PRIMARY KEY NOT NULL,
|
||||
token TEXT NOT NULL,
|
||||
dateCreated TEXT NOT NULL,
|
||||
isDeleted INT NOT NULL DEFAULT 0
|
||||
);
|
||||
, hash TEXT DEFAULT "" NOT NULL);
|
||||
CREATE TABLE IF NOT EXISTS "branches" (
|
||||
`branchId` TEXT NOT NULL,
|
||||
`noteId` TEXT NOT NULL,
|
||||
`parentNoteId` TEXT NOT NULL,
|
||||
`notePosition` INTEGER NOT NULL,
|
||||
`prefix` TEXT,
|
||||
`isExpanded` BOOLEAN,
|
||||
`isDeleted` INTEGER NOT NULL DEFAULT 0,
|
||||
`dateModified` TEXT NOT NULL, hash TEXT DEFAULT "" NOT NULL, dateCreated TEXT NOT NULL DEFAULT '1970-01-01T00:00:00.000Z',
|
||||
PRIMARY KEY(`branchId`)
|
||||
);
|
||||
CREATE INDEX `IDX_branches_noteId` ON `branches` (
|
||||
`noteId`
|
||||
);
|
||||
CREATE INDEX `IDX_branches_noteId_parentNoteId` ON `branches` (
|
||||
`noteId`,
|
||||
`parentNoteId`
|
||||
);
|
||||
CREATE TABLE labels
|
||||
(
|
||||
labelId TEXT not null primary key,
|
||||
noteId TEXT not null,
|
||||
name TEXT not null,
|
||||
value TEXT default '' not null,
|
||||
position INT default 0 not null,
|
||||
dateCreated TEXT not null,
|
||||
dateModified TEXT not null,
|
||||
isDeleted INT not null
|
||||
, hash TEXT DEFAULT "" NOT NULL);
|
||||
CREATE INDEX IDX_labels_name_value
|
||||
on labels (name, value);
|
||||
CREATE INDEX IDX_labels_noteId
|
||||
on labels (noteId);
|
||||
CREATE TABLE IF NOT EXISTS "notes" (
|
||||
`noteId` TEXT NOT NULL,
|
||||
`title` TEXT NOT NULL DEFAULT "unnamed",
|
||||
`content` TEXT NOT NULL DEFAULT "",
|
||||
`isProtected` INT NOT NULL DEFAULT 0,
|
||||
`isDeleted` INT NOT NULL DEFAULT 0,
|
||||
`dateCreated` TEXT NOT NULL,
|
||||
`dateModified` TEXT NOT NULL,
|
||||
type TEXT NOT NULL DEFAULT 'text',
|
||||
mime TEXT NOT NULL DEFAULT 'text/html', hash TEXT DEFAULT "" NOT NULL,
|
||||
PRIMARY KEY(`noteId`)
|
||||
);
|
||||
CREATE INDEX IDX_branches_parentNoteId ON branches (parentNoteId);
|
||||
CREATE INDEX IDX_notes_type
|
||||
on notes (type);
|
||||
CREATE TABLE IF NOT EXISTS "recent_notes" (
|
||||
`branchId` TEXT NOT NULL PRIMARY KEY,
|
||||
`notePath` TEXT NOT NULL,
|
||||
hash TEXT DEFAULT "" NOT NULL,
|
||||
`dateCreated` TEXT NOT NULL,
|
||||
isDeleted INT
|
||||
);
|
||||
CREATE TABLE IF NOT EXISTS "event_log" (
|
||||
`eventId` TEXT NOT NULL PRIMARY KEY,
|
||||
`noteId` TEXT,
|
||||
`comment` TEXT,
|
||||
`dateCreated` TEXT NOT NULL
|
||||
);
|
||||
CREATE TABLE IF NOT EXISTS "options"
|
||||
(
|
||||
name TEXT not null PRIMARY KEY,
|
||||
value TEXT,
|
||||
dateModified INT,
|
||||
isSynced INTEGER default 0 not null,
|
||||
hash TEXT default "" not null,
|
||||
dateCreated TEXT default '1970-01-01T00:00:00.000Z' not null
|
||||
);
|
||||
|
||||
@@ -73,15 +73,15 @@ app.on('ready', () => {
|
||||
mainWindow = createMainWindow();
|
||||
|
||||
const result = globalShortcut.register('CommandOrControl+Alt+P', async () => {
|
||||
const date_notes = require('./src/services/date_notes');
|
||||
const utils = require('./src/services/utils');
|
||||
const dateNoteService = require('./src/services/date_notes');
|
||||
const dateUtils = require('./src/services/date_utils');
|
||||
|
||||
const parentNoteId = await date_notes.getDateNoteId(utils.nowDate());
|
||||
const parentNote = await dateNoteService.getDateNote(dateUtils.nowDate());
|
||||
|
||||
// window may be hidden / not in focus
|
||||
mainWindow.focus();
|
||||
|
||||
mainWindow.webContents.send('create-day-sub-note', parentNoteId);
|
||||
mainWindow.webContents.send('create-day-sub-note', parentNote.noteId);
|
||||
});
|
||||
|
||||
if (!result) {
|
||||
|
||||
6446
package-lock.json
generated
81
package.json
@@ -1,73 +1,81 @@
|
||||
{
|
||||
"name": "trilium",
|
||||
"description": "Trilium Notes",
|
||||
"version": "0.9.0-beta",
|
||||
"version": "0.17.0",
|
||||
"license": "AGPL-3.0-only",
|
||||
"main": "electron.js",
|
||||
"bin": {
|
||||
"trilium": "./src/www"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/zadam/trilium.git"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "node ./bin/www",
|
||||
"start": "node ./src/www",
|
||||
"test-electron": "xo",
|
||||
"rebuild-electron": "electron-rebuild",
|
||||
"start-electron": "electron .",
|
||||
"build-electron": "electron-packager . --out=dist --asar --overwrite --all",
|
||||
"start-electron": "electron . --disable-gpu",
|
||||
"build-electron": "electron-packager . --out=dist --asar --overwrite --platform=win32,linux --arch=ia32,x64 --app-version=",
|
||||
"start-forge": "electron-forge start",
|
||||
"package-forge": "electron-forge package",
|
||||
"make-forge": "electron-forge make",
|
||||
"publish-forge": "electron-forge publish"
|
||||
"publish-forge": "electron-forge publish",
|
||||
"build-pkg": "pkg . --targets node8-linux-x64 --output dist/trilium-linux-x64-server.elf"
|
||||
},
|
||||
"dependencies": {
|
||||
"async-mutex": "^0.1.3",
|
||||
"axios": "^0.17.1",
|
||||
"body-parser": "~1.18.2",
|
||||
"axios": "^0.18",
|
||||
"body-parser": "^1.18.3",
|
||||
"cls-hooked": "^4.2.2",
|
||||
"cookie-parser": "~1.4.3",
|
||||
"debug": "~3.1.0",
|
||||
"devtron": "^1.4.0",
|
||||
"ejs": "~2.5.7",
|
||||
"electron": "^1.8.2",
|
||||
"electron-debug": "^1.5.0",
|
||||
"electron-dl": "^1.11.0",
|
||||
"electron-in-page-search": "^1.2.4",
|
||||
"express": "~4.16.2",
|
||||
"express-promise-wrap": "^0.2.2",
|
||||
"ejs": "~2.6.1",
|
||||
"electron-debug": "^2.0.0",
|
||||
"electron-dl": "^1.12.0",
|
||||
"electron-in-page-search": "^1.3.2",
|
||||
"express": "~4.16.3",
|
||||
"express-session": "^1.15.6",
|
||||
"fs-extra": "^4.0.2",
|
||||
"helmet": "^3.9.0",
|
||||
"fs-extra": "^6.0.1",
|
||||
"helmet": "^3.12.1",
|
||||
"html": "^1.0.0",
|
||||
"image-type": "^3.0.0",
|
||||
"imagemin": "^5.3.1",
|
||||
"imagemin-giflossy": "^5.1.10",
|
||||
"imagemin-mozjpeg": "^7.0.0",
|
||||
"imagemin-pngquant": "^5.0.1",
|
||||
"ini": "^1.3.4",
|
||||
"imagemin-pngquant": "^5.1.0",
|
||||
"ini": "^1.3.5",
|
||||
"jimp": "^0.2.28",
|
||||
"moment": "^2.20.1",
|
||||
"multer": "^1.3.0",
|
||||
"moment": "^2.22.2",
|
||||
"multer": "^1.3.1",
|
||||
"open": "0.0.5",
|
||||
"rand-token": "^0.4.0",
|
||||
"request": "^2.83.0",
|
||||
"rcedit": "^1.1.0",
|
||||
"request": "^2.87.0",
|
||||
"request-promise": "^4.2.2",
|
||||
"rimraf": "^2.6.2",
|
||||
"sanitize-filename": "^1.6.1",
|
||||
"scrypt": "^6.0.3",
|
||||
"serve-favicon": "~2.4.5",
|
||||
"session-file-store": "^1.1.2",
|
||||
"simple-node-logger": "^0.93.30",
|
||||
"sqlite": "^2.9.0",
|
||||
"tar-stream": "^1.5.5",
|
||||
"serve-favicon": "~2.5.0",
|
||||
"session-file-store": "^1.2.0",
|
||||
"simple-node-logger": "^0.93.37",
|
||||
"sqlite": "^2.9.2",
|
||||
"tar-stream": "^1.6.1",
|
||||
"unescape": "^1.0.1",
|
||||
"ws": "^3.3.2"
|
||||
"ws": "^5.2.1",
|
||||
"xml2js": "^0.4.19"
|
||||
},
|
||||
"devDependencies": {
|
||||
"electron-compile": "^6.4.2",
|
||||
"electron-packager": "^11.0.1",
|
||||
"electron-prebuilt-compile": "1.8.2",
|
||||
"electron-rebuild": "^1.7.3",
|
||||
"tape": "^4.8.0",
|
||||
"xo": "^0.18.0"
|
||||
"electron": "^2.0.4",
|
||||
"electron-compile": "^6.4.3",
|
||||
"electron-packager": "^12.1.0",
|
||||
"electron-prebuilt-compile": "2.0.4",
|
||||
"electron-rebuild": "^1.8.1",
|
||||
"lorem-ipsum": "^1.0.5",
|
||||
"tape": "^4.9.1",
|
||||
"xo": "^0.21.1",
|
||||
"pkg": "^4.3.3"
|
||||
},
|
||||
"config": {
|
||||
"forge": {
|
||||
@@ -106,5 +114,12 @@
|
||||
"node",
|
||||
"browser"
|
||||
]
|
||||
},
|
||||
"pkg": {
|
||||
"assets": [
|
||||
"./db/**/*",
|
||||
"./src/public/**/*",
|
||||
"./src/views/**/*"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
13
src/app.js
@@ -9,6 +9,8 @@ const session = require('express-session');
|
||||
const FileStore = require('session-file-store')(session);
|
||||
const os = require('os');
|
||||
const sessionSecret = require('./services/session_secret');
|
||||
const cls = require('./services/cls');
|
||||
require('./entities/entity_constructor');
|
||||
|
||||
const app = express();
|
||||
|
||||
@@ -23,6 +25,17 @@ app.use((req, res, next) => {
|
||||
next();
|
||||
});
|
||||
|
||||
app.use((req, res, next) => {
|
||||
cls.namespace.bindEmitter(req);
|
||||
cls.namespace.bindEmitter(res);
|
||||
|
||||
cls.init(() => {
|
||||
cls.namespace.set("Hi");
|
||||
|
||||
next();
|
||||
});
|
||||
});
|
||||
|
||||
app.use(bodyParser.json({limit: '50mb'}));
|
||||
app.use(bodyParser.urlencoded({extended: false}));
|
||||
app.use(cookieParser());
|
||||
|
||||
24
src/entities/api_token.js
Normal file
@@ -0,0 +1,24 @@
|
||||
"use strict";
|
||||
|
||||
const Entity = require('./entity');
|
||||
const dateUtils = require('../services/date_utils');
|
||||
|
||||
class ApiToken extends Entity {
|
||||
static get tableName() { return "api_tokens"; }
|
||||
static get primaryKeyName() { return "apiTokenId"; }
|
||||
static get hashedProperties() { return ["apiTokenId", "token", "dateCreated", "isDeleted"]; }
|
||||
|
||||
beforeSaving() {
|
||||
super.beforeSaving();
|
||||
|
||||
if (!this.isDeleted) {
|
||||
this.isDeleted = false;
|
||||
}
|
||||
|
||||
if (!this.dateCreated) {
|
||||
this.dateCreated = dateUtils.nowDate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ApiToken;
|
||||
@@ -1,14 +0,0 @@
|
||||
"use strict";
|
||||
|
||||
const Entity = require('./entity');
|
||||
|
||||
class Attribute extends Entity {
|
||||
static get tableName() { return "attributes"; }
|
||||
static get primaryKeyName() { return "attributeId"; }
|
||||
|
||||
async getNote() {
|
||||
return this.repository.getEntity("SELECT * FROM notes WHERE noteId = ?", [this.noteId]);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Attribute;
|
||||
38
src/entities/branch.js
Normal file
@@ -0,0 +1,38 @@
|
||||
"use strict";
|
||||
|
||||
const Entity = require('./entity');
|
||||
const dateUtils = require('../services/date_utils');
|
||||
const repository = require('../services/repository');
|
||||
const sql = require('../services/sql');
|
||||
|
||||
class Branch extends Entity {
|
||||
static get tableName() { return "branches"; }
|
||||
static get primaryKeyName() { return "branchId"; }
|
||||
// notePosition is not part of hash because it would produce a lot of updates in case of reordering
|
||||
static get hashedProperties() { return ["branchId", "noteId", "parentNoteId", "dateModified", "isDeleted", "prefix"]; }
|
||||
|
||||
async getNote() {
|
||||
return await repository.getEntity("SELECT * FROM notes WHERE noteId = ?", [this.noteId]);
|
||||
}
|
||||
|
||||
async beforeSaving() {
|
||||
super.beforeSaving();
|
||||
|
||||
if (this.notePosition === undefined) {
|
||||
const maxNotePos = await sql.getValue('SELECT MAX(notePosition) FROM branches WHERE parentNoteId = ? AND isDeleted = 0', [this.parentNoteId]);
|
||||
this.notePosition = maxNotePos === null ? 0 : maxNotePos + 1;
|
||||
}
|
||||
|
||||
if (!this.isDeleted) {
|
||||
this.isDeleted = false;
|
||||
}
|
||||
|
||||
if (!this.dateCreated) {
|
||||
this.dateCreated = dateUtils.nowDate();
|
||||
}
|
||||
|
||||
this.dateModified = dateUtils.nowDate();
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Branch;
|
||||
@@ -1,17 +1,34 @@
|
||||
"use strict";
|
||||
|
||||
const utils = require('../services/utils');
|
||||
const repository = require('../services/repository');
|
||||
|
||||
class Entity {
|
||||
constructor(repository, row) {
|
||||
utils.assertArguments(repository, row);
|
||||
|
||||
this.repository = repository;
|
||||
|
||||
constructor(row = {}) {
|
||||
for (const key in row) {
|
||||
this[key] = row[key];
|
||||
}
|
||||
}
|
||||
|
||||
beforeSaving() {
|
||||
if (!this[this.constructor.primaryKeyName]) {
|
||||
this[this.constructor.primaryKeyName] = utils.newEntityId();
|
||||
}
|
||||
|
||||
let contentToHash = "";
|
||||
|
||||
for (const propertyName of this.constructor.hashedProperties) {
|
||||
contentToHash += "|" + this[propertyName];
|
||||
}
|
||||
|
||||
this["hash"] = utils.hash(contentToHash).substr(0, 10);
|
||||
}
|
||||
|
||||
async save() {
|
||||
await repository.updateEntity(this);
|
||||
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Entity;
|
||||
53
src/entities/entity_constructor.js
Normal file
@@ -0,0 +1,53 @@
|
||||
const Note = require('../entities/note');
|
||||
const NoteRevision = require('../entities/note_revision');
|
||||
const Image = require('../entities/image');
|
||||
const NoteImage = require('../entities/note_image');
|
||||
const Branch = require('../entities/branch');
|
||||
const Label = require('../entities/label');
|
||||
const RecentNote = require('../entities/recent_note');
|
||||
const ApiToken = require('../entities/api_token');
|
||||
const Option = require('../entities/option');
|
||||
const repository = require('../services/repository');
|
||||
|
||||
function createEntityFromRow(row) {
|
||||
let entity;
|
||||
|
||||
if (row.labelId) {
|
||||
entity = new Label(row);
|
||||
}
|
||||
else if (row.noteRevisionId) {
|
||||
entity = new NoteRevision(row);
|
||||
}
|
||||
else if (row.noteImageId) {
|
||||
entity = new NoteImage(row);
|
||||
}
|
||||
else if (row.imageId) {
|
||||
entity = new Image(row);
|
||||
}
|
||||
else if (row.branchId && row.notePath) {
|
||||
entity = new RecentNote(row);
|
||||
}
|
||||
else if (row.apiTokenId) {
|
||||
entity = new ApiToken(row);
|
||||
}
|
||||
else if (row.branchId) {
|
||||
entity = new Branch(row);
|
||||
}
|
||||
else if (row.noteId) {
|
||||
entity = new Note(row);
|
||||
}
|
||||
else if (row.name) {
|
||||
entity = new Option(row);
|
||||
}
|
||||
else {
|
||||
throw new Error('Unknown entity type for row: ' + JSON.stringify(row));
|
||||
}
|
||||
|
||||
return entity;
|
||||
}
|
||||
|
||||
repository.setEntityConstructor(createEntityFromRow);
|
||||
|
||||
module.exports = {
|
||||
createEntityFromRow
|
||||
};
|
||||
26
src/entities/image.js
Normal file
@@ -0,0 +1,26 @@
|
||||
"use strict";
|
||||
|
||||
const Entity = require('./entity');
|
||||
const dateUtils = require('../services/date_utils');
|
||||
|
||||
class Image extends Entity {
|
||||
static get tableName() { return "images"; }
|
||||
static get primaryKeyName() { return "imageId"; }
|
||||
static get hashedProperties() { return ["imageId", "format", "checksum", "name", "isDeleted", "dateModified", "dateCreated"]; }
|
||||
|
||||
beforeSaving() {
|
||||
super.beforeSaving();
|
||||
|
||||
if (!this.isDeleted) {
|
||||
this.isDeleted = false;
|
||||
}
|
||||
|
||||
if (!this.dateCreated) {
|
||||
this.dateCreated = dateUtils.nowDate();
|
||||
}
|
||||
|
||||
this.dateModified = dateUtils.nowDate();
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Image;
|
||||
41
src/entities/label.js
Normal file
@@ -0,0 +1,41 @@
|
||||
"use strict";
|
||||
|
||||
const Entity = require('./entity');
|
||||
const repository = require('../services/repository');
|
||||
const dateUtils = require('../services/date_utils');
|
||||
const sql = require('../services/sql');
|
||||
|
||||
class Label extends Entity {
|
||||
static get tableName() { return "labels"; }
|
||||
static get primaryKeyName() { return "labelId"; }
|
||||
static get hashedProperties() { return ["labelId", "noteId", "name", "value", "dateModified", "dateCreated"]; }
|
||||
|
||||
async getNote() {
|
||||
return await repository.getEntity("SELECT * FROM notes WHERE noteId = ?", [this.noteId]);
|
||||
}
|
||||
|
||||
async beforeSaving() {
|
||||
super.beforeSaving();
|
||||
|
||||
if (!this.value) {
|
||||
// null value isn't allowed
|
||||
this.value = "";
|
||||
}
|
||||
|
||||
if (this.position === undefined) {
|
||||
this.position = 1 + await sql.getValue(`SELECT COALESCE(MAX(position), 0) FROM labels WHERE noteId = ?`, [this.noteId]);
|
||||
}
|
||||
|
||||
if (!this.isDeleted) {
|
||||
this.isDeleted = false;
|
||||
}
|
||||
|
||||
if (!this.dateCreated) {
|
||||
this.dateCreated = dateUtils.nowDate();
|
||||
}
|
||||
|
||||
this.dateModified = dateUtils.nowDate();
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Label;
|
||||
@@ -1,26 +1,37 @@
|
||||
"use strict";
|
||||
|
||||
const Entity = require('./entity');
|
||||
const protected_session = require('../services/protected_session');
|
||||
const protectedSessionService = require('../services/protected_session');
|
||||
const repository = require('../services/repository');
|
||||
const dateUtils = require('../services/date_utils');
|
||||
|
||||
class Note extends Entity {
|
||||
static get tableName() { return "notes"; }
|
||||
static get primaryKeyName() { return "noteId"; }
|
||||
static get hashedProperties() { return ["noteId", "title", "content", "type", "dateModified", "isProtected", "isDeleted"]; }
|
||||
|
||||
constructor(repository, row) {
|
||||
super(repository, row);
|
||||
constructor(row) {
|
||||
super(row);
|
||||
|
||||
if (this.isProtected) {
|
||||
protected_session.decryptNote(this.dataKey, this);
|
||||
// check if there's noteId, otherwise this is a new entity which wasn't encrypted yet
|
||||
if (this.isProtected && this.noteId) {
|
||||
protectedSessionService.decryptNote(this);
|
||||
}
|
||||
|
||||
if (this.isJson()) {
|
||||
this.setContent(this.content);
|
||||
}
|
||||
|
||||
setContent(content) {
|
||||
this.content = content;
|
||||
|
||||
try {
|
||||
this.jsonContent = JSON.parse(this.content);
|
||||
}
|
||||
catch(e) {}
|
||||
}
|
||||
|
||||
isJson() {
|
||||
return this.type === "code" && this.mime === "application/json";
|
||||
return this.mime === "application/json";
|
||||
}
|
||||
|
||||
isJavaScript() {
|
||||
@@ -29,7 +40,7 @@ class Note extends Entity {
|
||||
}
|
||||
|
||||
isHtml() {
|
||||
return (this.type === "code" || this.type === "file") && this.mime === "text/html";
|
||||
return (this.type === "code" || this.type === "file" || this.type === "render") && this.mime === "text/html";
|
||||
}
|
||||
|
||||
getScriptEnv() {
|
||||
@@ -48,89 +59,106 @@ class Note extends Entity {
|
||||
return null;
|
||||
}
|
||||
|
||||
async getAttributes() {
|
||||
return this.repository.getEntities("SELECT * FROM attributes WHERE noteId = ? AND isDeleted = 0", [this.noteId]);
|
||||
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 attributes!
|
||||
async getAttributeMap() {
|
||||
// WARNING: this doesn't take into account the possibility to have multi-valued labels!
|
||||
async getLabelMap() {
|
||||
const map = {};
|
||||
|
||||
for (const attr of await this.getAttributes()) {
|
||||
map[attr.name] = attr.value;
|
||||
for (const label of await this.getLabels()) {
|
||||
map[label.name] = label.value;
|
||||
}
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
async hasAttribute(name) {
|
||||
const map = await this.getAttributeMap();
|
||||
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 attributes!
|
||||
async getAttribute(name) {
|
||||
return this.repository.getEntity("SELECT * FROM attributes WHERE noteId = ? AND name = ?", [this.noteId, name]);
|
||||
// WARNING: this doesn't take into account the possibility to have multi-valued labels!
|
||||
async getLabel(name) {
|
||||
return await repository.getEntity("SELECT * FROM labels WHERE noteId = ? AND name = ?", [this.noteId, name]);
|
||||
}
|
||||
|
||||
async getRevisions() {
|
||||
return this.repository.getEntities("SELECT * FROM note_revisions WHERE noteId = ?", [this.noteId]);
|
||||
return await repository.getEntities("SELECT * FROM note_revisions WHERE noteId = ?", [this.noteId]);
|
||||
}
|
||||
|
||||
async getTrees() {
|
||||
return this.repository.getEntities("SELECT * FROM note_tree WHERE isDeleted = 0 AND noteId = ?", [this.noteId]);
|
||||
async getNoteImages() {
|
||||
return await repository.getEntities("SELECT * FROM note_images WHERE noteId = ? AND isDeleted = 0", [this.noteId]);
|
||||
}
|
||||
|
||||
async getChild(name) {
|
||||
return this.repository.getEntity(`
|
||||
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 note_tree
|
||||
FROM branches
|
||||
JOIN notes USING(noteId)
|
||||
WHERE notes.isDeleted = 0
|
||||
AND note_tree.isDeleted = 0
|
||||
AND note_tree.parentNoteId = ?
|
||||
AND branches.isDeleted = 0
|
||||
AND branches.parentNoteId = ?
|
||||
AND notes.title = ?`, [this.noteId, name]);
|
||||
}
|
||||
|
||||
async getChildren() {
|
||||
return this.repository.getEntities(`
|
||||
async getChildNotes() {
|
||||
return await repository.getEntities(`
|
||||
SELECT notes.*
|
||||
FROM note_tree
|
||||
FROM branches
|
||||
JOIN notes USING(noteId)
|
||||
WHERE notes.isDeleted = 0
|
||||
AND note_tree.isDeleted = 0
|
||||
AND note_tree.parentNoteId = ?
|
||||
ORDER BY note_tree.notePosition`, [this.noteId]);
|
||||
AND branches.isDeleted = 0
|
||||
AND branches.parentNoteId = ?
|
||||
ORDER BY branches.notePosition`, [this.noteId]);
|
||||
}
|
||||
|
||||
async getParents() {
|
||||
return this.repository.getEntities(`
|
||||
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
|
||||
note_tree AS child_tree
|
||||
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]);
|
||||
}
|
||||
|
||||
async getNoteTree() {
|
||||
return this.repository.getEntities(`
|
||||
SELECT note_tree.*
|
||||
FROM note_tree
|
||||
JOIN notes USING(noteId)
|
||||
WHERE notes.isDeleted = 0
|
||||
AND note_tree.isDeleted = 0
|
||||
AND note_tree.noteId = ?`, [this.noteId]);
|
||||
}
|
||||
|
||||
beforeSaving() {
|
||||
this.content = JSON.stringify(this.jsonContent, null, '\t');
|
||||
super.beforeSaving();
|
||||
|
||||
if (this.isJson() && this.jsonContent) {
|
||||
this.content = JSON.stringify(this.jsonContent, null, '\t');
|
||||
}
|
||||
|
||||
if (this.isProtected) {
|
||||
protected_session.encryptNote(this.dataKey, this);
|
||||
protectedSessionService.encryptNote(this);
|
||||
}
|
||||
|
||||
if (!this.isDeleted) {
|
||||
this.isDeleted = false;
|
||||
}
|
||||
|
||||
if (!this.dateCreated) {
|
||||
this.dateCreated = dateUtils.nowDate();
|
||||
}
|
||||
|
||||
this.dateModified = dateUtils.nowDate();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
35
src/entities/note_image.js
Normal file
@@ -0,0 +1,35 @@
|
||||
"use strict";
|
||||
|
||||
const Entity = require('./entity');
|
||||
const repository = require('../services/repository');
|
||||
const dateUtils = require('../services/date_utils');
|
||||
|
||||
class NoteImage extends Entity {
|
||||
static get tableName() { return "note_images"; }
|
||||
static get primaryKeyName() { return "noteImageId"; }
|
||||
static get hashedProperties() { return ["noteImageId", "noteId", "imageId", "isDeleted", "dateModified", "dateCreated"]; }
|
||||
|
||||
async getNote() {
|
||||
return await repository.getEntity("SELECT * FROM notes WHERE noteId = ?", [this.noteId]);
|
||||
}
|
||||
|
||||
async getImage() {
|
||||
return await repository.getEntity("SELECT * FROM images WHERE imageId = ?", [this.imageId]);
|
||||
}
|
||||
|
||||
beforeSaving() {
|
||||
super.beforeSaving();
|
||||
|
||||
if (!this.isDeleted) {
|
||||
this.isDeleted = false;
|
||||
}
|
||||
|
||||
if (!this.dateCreated) {
|
||||
this.dateCreated = dateUtils.nowDate();
|
||||
}
|
||||
|
||||
this.dateModified = dateUtils.nowDate();
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = NoteImage;
|
||||
@@ -1,13 +1,32 @@
|
||||
"use strict";
|
||||
|
||||
const Entity = require('./entity');
|
||||
const protectedSessionService = require('../services/protected_session');
|
||||
const repository = require('../services/repository');
|
||||
|
||||
class NoteRevision extends Entity {
|
||||
static get tableName() { return "note_revisions"; }
|
||||
static get primaryKeyName() { return "noteRevisionId"; }
|
||||
static get hashedProperties() { return ["noteRevisionId", "noteId", "title", "content", "dateModifiedFrom", "dateModifiedTo"]; }
|
||||
|
||||
constructor(row) {
|
||||
super(row);
|
||||
|
||||
if (this.isProtected) {
|
||||
protectedSessionService.decryptNoteRevision(this);
|
||||
}
|
||||
}
|
||||
|
||||
async getNote() {
|
||||
return this.repository.getEntity("SELECT * FROM notes WHERE noteId = ?", [this.noteId]);
|
||||
return await repository.getEntity("SELECT * FROM notes WHERE noteId = ?", [this.noteId]);
|
||||
}
|
||||
|
||||
beforeSaving() {
|
||||
super.beforeSaving();
|
||||
|
||||
if (this.isProtected) {
|
||||
protectedSessionService.encryptNoteRevision(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
"use strict";
|
||||
|
||||
const Entity = require('./entity');
|
||||
|
||||
class NoteTree extends Entity {
|
||||
static get tableName() { return "note_tree"; }
|
||||
static get primaryKeyName() { return "noteTreeId"; }
|
||||
|
||||
async getNote() {
|
||||
return this.repository.getEntity("SELECT * FROM note_tree WHERE isDeleted = 0 AND noteId = ?", [this.noteId]);
|
||||
}
|
||||
|
||||
async getParentNote() {
|
||||
return this.repository.getEntity("SELECT * FROM note_tree WHERE isDeleted = 0 AND parentNoteId = ?", [this.parentNoteId]);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = NoteTree;
|
||||
18
src/entities/option.js
Normal file
@@ -0,0 +1,18 @@
|
||||
"use strict";
|
||||
|
||||
const Entity = require('./entity');
|
||||
const dateUtils = require('../services/date_utils');
|
||||
|
||||
class Option extends Entity {
|
||||
static get tableName() { return "options"; }
|
||||
static get primaryKeyName() { return "name"; }
|
||||
static get hashedProperties() { return ["name", "value"]; }
|
||||
|
||||
beforeSaving() {
|
||||
super.beforeSaving();
|
||||
|
||||
this.dateModified = dateUtils.nowDate();
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Option;
|
||||
24
src/entities/recent_note.js
Normal file
@@ -0,0 +1,24 @@
|
||||
"use strict";
|
||||
|
||||
const Entity = require('./entity');
|
||||
const dateUtils = require('../services/date_utils');
|
||||
|
||||
class RecentNote extends Entity {
|
||||
static get tableName() { return "recent_notes"; }
|
||||
static get primaryKeyName() { return "branchId"; }
|
||||
static get hashedProperties() { return ["branchId", "notePath", "dateCreated", "isDeleted"]; }
|
||||
|
||||
beforeSaving() {
|
||||
super.beforeSaving();
|
||||
|
||||
if (!this.isDeleted) {
|
||||
this.isDeleted = false;
|
||||
}
|
||||
|
||||
if (!this.dateCreated) {
|
||||
this.dateCreated = dateUtils.nowDate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = RecentNote;
|
||||
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 |
|
Before Width: | Height: | Size: 358 B After Width: | Height: | Size: 358 B |
|
Before Width: | Height: | Size: 252 B 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,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,173 +0,0 @@
|
||||
"use strict";
|
||||
|
||||
const contextMenu = (function() {
|
||||
const $tree = $("#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: "Export sub-tree", cmd: "exportSubTree", uiIcon: " ui-icon-arrowthick-1-ne"},
|
||||
{title: "Import sub-tree into", cmd: "importSubTree", uiIcon: "ui-icon-arrowthick-1-sw"},
|
||||
{title: "----"},
|
||||
{title: "Collapse sub-tree <kbd>Alt+-</kbd>", cmd: "collapseSubTree", uiIcon: "ui-icon-minus"},
|
||||
{title: "Force note sync", cmd: "forceNoteSync", uiIcon: "ui-icon-refresh"},
|
||||
{title: "Sort alphabetically <kbd>Alt+S</kbd>", cmd: "sortAlphabetically", uiIcon: " ui-icon-arrowthick-2-n-s"}
|
||||
|
||||
],
|
||||
beforeOpen: (event, ui) => {
|
||||
const node = $.ui.fancytree.getNode(ui.target);
|
||||
// Modify menu entries depending on node status
|
||||
$tree.contextmenu("enableEntry", "pasteAfter", clipboardIds.length > 0);
|
||||
$tree.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 === "exportSubTree") {
|
||||
exportSubTree(node.data.noteId);
|
||||
}
|
||||
else if (ui.cmd === "importSubTree") {
|
||||
importSubTree(node.data.noteId);
|
||||
}
|
||||
else if (ui.cmd === "collapseSubTree") {
|
||||
noteTree.collapseTree(node);
|
||||
}
|
||||
else if (ui.cmd === "forceNoteSync") {
|
||||
forceNoteSync(node.data.noteId);
|
||||
}
|
||||
else if (ui.cmd === "sortAlphabetically") {
|
||||
noteTree.sortAlphabetically(node.data.noteId);
|
||||
}
|
||||
else {
|
||||
messaging.logError("Unknown command: " + ui.cmd);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
pasteAfter,
|
||||
pasteInto,
|
||||
cut,
|
||||
copy,
|
||||
contextMenuSettings
|
||||
}
|
||||
})();
|
||||
@@ -1,137 +1,160 @@
|
||||
"use strict";
|
||||
import cloningService from '../services/cloning.js';
|
||||
import linkService from '../services/link.js';
|
||||
import noteDetailService from '../services/note_detail.js';
|
||||
import treeUtils from '../services/tree_utils.js';
|
||||
import server from "../services/server.js";
|
||||
import noteDetailText from "../services/note_detail_text.js";
|
||||
|
||||
const addLink = (function() {
|
||||
const $dialog = $("#add-link-dialog");
|
||||
const $form = $("#add-link-form");
|
||||
const $autoComplete = $("#note-autocomplete");
|
||||
const $linkTitle = $("#link-title");
|
||||
const $clonePrefix = $("#clone-prefix");
|
||||
const $linkTitleFormGroup = $("#add-link-title-form-group");
|
||||
const $prefixFormGroup = $("#add-link-prefix-form-group");
|
||||
const $linkTypes = $("input[name='add-link-type']");
|
||||
const $linkTypeHtml = $linkTypes.filter('input[value="html"]');
|
||||
const $dialog = $("#add-link-dialog");
|
||||
const $form = $("#add-link-form");
|
||||
const $autoComplete = $("#note-autocomplete");
|
||||
const $linkTitle = $("#link-title");
|
||||
const $clonePrefix = $("#clone-prefix");
|
||||
const $linkTitleFormGroup = $("#add-link-title-form-group");
|
||||
const $prefixFormGroup = $("#add-link-prefix-form-group");
|
||||
const $linkTypeDiv = $("#add-link-type-div");
|
||||
const $linkTypes = $("input[name='add-link-type']");
|
||||
const $linkTypeHtml = $linkTypes.filter('input[value="html"]');
|
||||
|
||||
function setLinkType(linkType) {
|
||||
$linkTypes.each(function () {
|
||||
$(this).prop('checked', $(this).val() === linkType);
|
||||
});
|
||||
|
||||
linkTypeChanged();
|
||||
}
|
||||
|
||||
function showDialog() {
|
||||
glob.activeDialog = $dialog;
|
||||
|
||||
if (noteEditor.getCurrentNoteType() === 'text') {
|
||||
$linkTypeHtml.prop('disabled', false);
|
||||
|
||||
setLinkType('html');
|
||||
}
|
||||
else {
|
||||
$linkTypeHtml.prop('disabled', true);
|
||||
|
||||
setLinkType('selected-to-current');
|
||||
}
|
||||
|
||||
$dialog.dialog({
|
||||
modal: true,
|
||||
width: 700
|
||||
});
|
||||
|
||||
$autoComplete.val('').focus();
|
||||
$clonePrefix.val('');
|
||||
$linkTitle.val('');
|
||||
|
||||
function setDefaultLinkTitle(noteId) {
|
||||
const noteTitle = noteTree.getNoteTitle(noteId);
|
||||
|
||||
$linkTitle.val(noteTitle);
|
||||
}
|
||||
|
||||
$autoComplete.autocomplete({
|
||||
source: noteTree.getAutocompleteItems(),
|
||||
minLength: 0,
|
||||
change: () => {
|
||||
const val = $autoComplete.val();
|
||||
const notePath = link.getNodePathFromLabel(val);
|
||||
if (!notePath) {
|
||||
return;
|
||||
}
|
||||
|
||||
const noteId = treeUtils.getNoteIdFromNotePath(notePath);
|
||||
|
||||
if (noteId) {
|
||||
setDefaultLinkTitle(noteId);
|
||||
}
|
||||
},
|
||||
// this is called when user goes through autocomplete list with keyboard
|
||||
// at this point the item isn't selected yet so we use supplied ui.item to see WHERE the cursor is
|
||||
focus: (event, ui) => {
|
||||
const notePath = link.getNodePathFromLabel(ui.item.value);
|
||||
const noteId = treeUtils.getNoteIdFromNotePath(notePath);
|
||||
|
||||
setDefaultLinkTitle(noteId);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
$form.submit(() => {
|
||||
const value = $autoComplete.val();
|
||||
|
||||
const notePath = link.getNodePathFromLabel(value);
|
||||
const noteId = treeUtils.getNoteIdFromNotePath(notePath);
|
||||
|
||||
if (notePath) {
|
||||
const linkType = $("input[name='add-link-type']:checked").val();
|
||||
|
||||
if (linkType === 'html') {
|
||||
const linkTitle = $linkTitle.val();
|
||||
|
||||
$dialog.dialog("close");
|
||||
|
||||
link.addLinkToEditor(linkTitle, '#' + notePath);
|
||||
}
|
||||
else if (linkType === 'selected-to-current') {
|
||||
const prefix = $clonePrefix.val();
|
||||
|
||||
cloning.cloneNoteTo(noteId, noteEditor.getCurrentNoteId(), prefix);
|
||||
|
||||
$dialog.dialog("close");
|
||||
}
|
||||
else if (linkType === 'current-to-selected') {
|
||||
const prefix = $clonePrefix.val();
|
||||
|
||||
cloning.cloneNoteTo(noteEditor.getCurrentNoteId(), noteId, prefix);
|
||||
|
||||
$dialog.dialog("close");
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
function setLinkType(linkType) {
|
||||
$linkTypes.each(function () {
|
||||
$(this).prop('checked', $(this).val() === linkType);
|
||||
});
|
||||
|
||||
function linkTypeChanged() {
|
||||
const value = $linkTypes.filter(":checked").val();
|
||||
linkTypeChanged();
|
||||
}
|
||||
|
||||
if (value === 'html') {
|
||||
$linkTitleFormGroup.show();
|
||||
$prefixFormGroup.hide();
|
||||
async function showDialog() {
|
||||
glob.activeDialog = $dialog;
|
||||
|
||||
if (noteDetailService.getCurrentNoteType() === 'text') {
|
||||
$linkTypeHtml.prop('disabled', false);
|
||||
|
||||
setLinkType('html');
|
||||
}
|
||||
else {
|
||||
$linkTypeHtml.prop('disabled', true);
|
||||
|
||||
setLinkType('selected-to-current');
|
||||
}
|
||||
|
||||
$dialog.dialog({
|
||||
modal: true,
|
||||
width: 700
|
||||
});
|
||||
|
||||
$autoComplete.val('').focus();
|
||||
$clonePrefix.val('');
|
||||
$linkTitle.val('');
|
||||
|
||||
async function setDefaultLinkTitle(noteId) {
|
||||
const noteTitle = await treeUtils.getNoteTitle(noteId);
|
||||
|
||||
$linkTitle.val(noteTitle);
|
||||
}
|
||||
|
||||
$autoComplete.autocomplete({
|
||||
source: async function(request, response) {
|
||||
const result = await server.get('autocomplete?query=' + encodeURIComponent(request.term));
|
||||
|
||||
if (result.length > 0) {
|
||||
response(result);
|
||||
}
|
||||
else {
|
||||
response([{
|
||||
label: "No results",
|
||||
value: "No results"
|
||||
}]);
|
||||
}
|
||||
},
|
||||
minLength: 2,
|
||||
change: async () => {
|
||||
const val = $autoComplete.val();
|
||||
const notePath = linkService.getNodePathFromLabel(val);
|
||||
if (!notePath) {
|
||||
return;
|
||||
}
|
||||
|
||||
const noteId = treeUtils.getNoteIdFromNotePath(notePath);
|
||||
|
||||
if (noteId) {
|
||||
await setDefaultLinkTitle(noteId);
|
||||
}
|
||||
},
|
||||
// this is called when user goes through autocomplete list with keyboard
|
||||
// at this point the item isn't selected yet so we use supplied ui.item to see WHERE the cursor is
|
||||
focus: async (event, ui) => {
|
||||
const notePath = linkService.getNodePathFromLabel(ui.item.value);
|
||||
const noteId = treeUtils.getNoteIdFromNotePath(notePath);
|
||||
|
||||
await setDefaultLinkTitle(noteId);
|
||||
}
|
||||
else {
|
||||
$linkTitleFormGroup.hide();
|
||||
$prefixFormGroup.show();
|
||||
});
|
||||
}
|
||||
|
||||
$form.submit(() => {
|
||||
const value = $autoComplete.val();
|
||||
|
||||
const notePath = linkService.getNodePathFromLabel(value);
|
||||
const noteId = treeUtils.getNoteIdFromNotePath(notePath);
|
||||
|
||||
if (notePath) {
|
||||
const linkType = $("input[name='add-link-type']:checked").val();
|
||||
|
||||
if (linkType === 'html') {
|
||||
const linkTitle = $linkTitle.val();
|
||||
|
||||
$dialog.dialog("close");
|
||||
|
||||
const linkHref = '#' + notePath;
|
||||
|
||||
if (hasSelection()) {
|
||||
const editor = noteDetailText.getEditor();
|
||||
|
||||
editor.execute('link', linkHref);
|
||||
}
|
||||
else {
|
||||
linkService.addLinkToEditor(linkTitle, linkHref);
|
||||
}
|
||||
}
|
||||
else if (linkType === 'selected-to-current') {
|
||||
const prefix = $clonePrefix.val();
|
||||
|
||||
cloningService.cloneNoteTo(noteId, noteDetailService.getCurrentNoteId(), prefix);
|
||||
|
||||
$dialog.dialog("close");
|
||||
}
|
||||
else if (linkType === 'current-to-selected') {
|
||||
const prefix = $clonePrefix.val();
|
||||
|
||||
cloningService.cloneNoteTo(noteDetailService.getCurrentNoteId(), noteId, prefix);
|
||||
|
||||
$dialog.dialog("close");
|
||||
}
|
||||
}
|
||||
|
||||
$linkTypes.change(linkTypeChanged);
|
||||
return false;
|
||||
});
|
||||
|
||||
$(document).bind('keydown', 'ctrl+l', e => {
|
||||
showDialog();
|
||||
// returns true if user selected some text, false if there's no selection
|
||||
function hasSelection() {
|
||||
const model = noteDetailText.getEditor().model;
|
||||
const selection = model.document.selection;
|
||||
|
||||
e.preventDefault();
|
||||
});
|
||||
return !selection.isCollapsed;
|
||||
}
|
||||
|
||||
return {
|
||||
showDialog
|
||||
};
|
||||
})();
|
||||
function linkTypeChanged() {
|
||||
const value = $linkTypes.filter(":checked").val();
|
||||
|
||||
$linkTitleFormGroup.toggle(!hasSelection() && value === 'html');
|
||||
$prefixFormGroup.toggle(!hasSelection() && value !== 'html');
|
||||
|
||||
$linkTypeDiv.toggle(!hasSelection());
|
||||
}
|
||||
|
||||
$linkTypes.change(linkTypeChanged);
|
||||
|
||||
export default {
|
||||
showDialog
|
||||
};
|
||||
@@ -1,224 +0,0 @@
|
||||
"use strict";
|
||||
|
||||
const attributesDialog = (function() {
|
||||
const $dialog = $("#attributes-dialog");
|
||||
const $saveAttributesButton = $("#save-attributes-button");
|
||||
const $attributesBody = $('#attributes-table tbody');
|
||||
|
||||
const attributesModel = new AttributesModel();
|
||||
let attributeNames = [];
|
||||
|
||||
function AttributesModel() {
|
||||
const self = this;
|
||||
|
||||
this.attributes = ko.observableArray();
|
||||
|
||||
this.loadAttributes = async function() {
|
||||
const noteId = noteEditor.getCurrentNoteId();
|
||||
|
||||
const attributes = await server.get('notes/' + noteId + '/attributes');
|
||||
|
||||
self.attributes(attributes.map(ko.observable));
|
||||
|
||||
addLastEmptyRow();
|
||||
|
||||
attributeNames = await server.get('attributes/names');
|
||||
|
||||
// attribute might not be rendered immediatelly so could not focus
|
||||
setTimeout(() => $(".attribute-name:last").focus(), 100);
|
||||
|
||||
$attributesBody.sortable({
|
||||
handle: '.handle',
|
||||
containment: $attributesBody,
|
||||
update: function() {
|
||||
let position = 0;
|
||||
|
||||
// we need to update positions by searching in the DOM, because order of the
|
||||
// attributes in the viewmodel (self.attributes()) stays the same
|
||||
$attributesBody.find('input[name="position"]').each(function() {
|
||||
const attr = self.getTargetAttribute(this);
|
||||
|
||||
attr().position = position++;
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
this.deleteAttribute = function(data, event) {
|
||||
const attr = self.getTargetAttribute(event.target);
|
||||
const attrData = attr();
|
||||
|
||||
if (attrData) {
|
||||
attrData.isDeleted = 1;
|
||||
|
||||
attr(attrData);
|
||||
|
||||
addLastEmptyRow();
|
||||
}
|
||||
};
|
||||
|
||||
function isValid() {
|
||||
for (let attrs = self.attributes(), i = 0; i < attrs.length; i++) {
|
||||
if (self.isEmptyName(i)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
this.save = async function() {
|
||||
// we need to defocus from input (in case of enter-triggered save) because value is updated
|
||||
// on blur event (because of conflict with jQuery UI Autocomplete). Without this, input would
|
||||
// stay in focus, blur wouldn't be triggered and change wouldn't be updated in the viewmodel.
|
||||
$saveAttributesButton.focus();
|
||||
|
||||
if (!isValid()) {
|
||||
alert("Please fix all validation errors and try saving again.");
|
||||
return;
|
||||
}
|
||||
|
||||
const noteId = noteEditor.getCurrentNoteId();
|
||||
|
||||
const attributesToSave = self.attributes()
|
||||
.map(attr => attr())
|
||||
.filter(attr => attr.attributeId !== "" || attr.name !== "");
|
||||
|
||||
const attributes = await server.put('notes/' + noteId + '/attributes', attributesToSave);
|
||||
|
||||
self.attributes(attributes.map(ko.observable));
|
||||
|
||||
addLastEmptyRow();
|
||||
|
||||
showMessage("Attributes have been saved.");
|
||||
|
||||
noteEditor.loadAttributeList();
|
||||
};
|
||||
|
||||
function addLastEmptyRow() {
|
||||
const attrs = self.attributes().filter(attr => attr().isDeleted === 0);
|
||||
const last = attrs.length === 0 ? null : attrs[attrs.length - 1]();
|
||||
|
||||
if (!last || last.name.trim() !== "" || last.value !== "") {
|
||||
self.attributes.push(ko.observable({
|
||||
attributeId: '',
|
||||
name: '',
|
||||
value: '',
|
||||
isDeleted: 0,
|
||||
position: 0
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
this.attributeChanged = function (data, event) {
|
||||
addLastEmptyRow();
|
||||
|
||||
const attr = self.getTargetAttribute(event.target);
|
||||
|
||||
attr.valueHasMutated();
|
||||
};
|
||||
|
||||
this.isNotUnique = function(index) {
|
||||
const cur = self.attributes()[index]();
|
||||
|
||||
if (cur.name.trim() === "") {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (let attrs = self.attributes(), i = 0; i < attrs.length; i++) {
|
||||
const attr = attrs[i]();
|
||||
|
||||
if (index !== i && cur.name === attr.name) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
this.isEmptyName = function(index) {
|
||||
const cur = self.attributes()[index]();
|
||||
|
||||
return cur.name.trim() === "" && (cur.attributeId !== "" || cur.value !== "");
|
||||
};
|
||||
|
||||
this.getTargetAttribute = function(target) {
|
||||
const context = ko.contextFor(target);
|
||||
const index = context.$index();
|
||||
|
||||
return self.attributes()[index];
|
||||
}
|
||||
}
|
||||
|
||||
async function showDialog() {
|
||||
glob.activeDialog = $dialog;
|
||||
|
||||
await attributesModel.loadAttributes();
|
||||
|
||||
$dialog.dialog({
|
||||
modal: true,
|
||||
width: 800,
|
||||
height: 500
|
||||
});
|
||||
}
|
||||
|
||||
$(document).bind('keydown', 'alt+a', e => {
|
||||
showDialog();
|
||||
|
||||
e.preventDefault();
|
||||
});
|
||||
|
||||
ko.applyBindings(attributesModel, document.getElementById('attributes-dialog'));
|
||||
|
||||
$(document).on('focus', '.attribute-name', function (e) {
|
||||
if (!$(this).hasClass("ui-autocomplete-input")) {
|
||||
$(this).autocomplete({
|
||||
// shouldn't be required and autocomplete should just accept array of strings, but that fails
|
||||
// because we have overriden filter() function in init.js
|
||||
source: attributeNames.map(attr => {
|
||||
return {
|
||||
label: attr,
|
||||
value: attr
|
||||
}
|
||||
}),
|
||||
minLength: 0
|
||||
});
|
||||
}
|
||||
|
||||
$(this).autocomplete("search", $(this).val());
|
||||
});
|
||||
|
||||
$(document).on('focus', '.attribute-value', async function (e) {
|
||||
if (!$(this).hasClass("ui-autocomplete-input")) {
|
||||
const attributeName = $(this).parent().parent().find('.attribute-name').val();
|
||||
|
||||
if (attributeName.trim() === "") {
|
||||
return;
|
||||
}
|
||||
|
||||
const attributeValues = await server.get('attributes/values/' + encodeURIComponent(attributeName));
|
||||
|
||||
if (attributeValues.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
$(this).autocomplete({
|
||||
// shouldn't be required and autocomplete should just accept array of strings, but that fails
|
||||
// because we have overriden filter() function in init.js
|
||||
source: attributeValues.map(attr => {
|
||||
return {
|
||||
label: attr,
|
||||
value: attr
|
||||
}
|
||||
}),
|
||||
minLength: 0
|
||||
});
|
||||
}
|
||||
|
||||
$(this).autocomplete("search", $(this).val());
|
||||
});
|
||||
|
||||
return {
|
||||
showDialog
|
||||
};
|
||||
})();
|
||||
51
src/public/javascripts/dialogs/branch_prefix.js
Normal file
@@ -0,0 +1,51 @@
|
||||
import treeService from '../services/tree.js';
|
||||
import server from '../services/server.js';
|
||||
import treeCache from "../services/tree_cache.js";
|
||||
import treeUtils from "../services/tree_utils.js";
|
||||
|
||||
const $dialog = $("#edit-tree-prefix-dialog");
|
||||
const $form = $("#edit-tree-prefix-form");
|
||||
const $treePrefixInput = $("#tree-prefix-input");
|
||||
const $noteTitle = $('#tree-prefix-note-title');
|
||||
|
||||
let branchId;
|
||||
|
||||
async function showDialog() {
|
||||
glob.activeDialog = $dialog;
|
||||
|
||||
await $dialog.dialog({
|
||||
modal: true,
|
||||
width: 500
|
||||
});
|
||||
|
||||
const currentNode = treeService.getCurrentNode();
|
||||
|
||||
branchId = currentNode.data.branchId;
|
||||
const branch = await treeCache.getBranch(branchId);
|
||||
|
||||
$treePrefixInput.val(branch.prefix).focus();
|
||||
|
||||
const noteTitle = await treeUtils.getNoteTitle(currentNode.data.noteId);
|
||||
|
||||
$noteTitle.html(noteTitle);
|
||||
}
|
||||
|
||||
async function savePrefix() {
|
||||
const prefix = $treePrefixInput.val();
|
||||
|
||||
await server.put('branches/' + branchId + '/set-prefix', { prefix: prefix });
|
||||
|
||||
await treeService.setPrefix(branchId, prefix);
|
||||
|
||||
$dialog.dialog("close");
|
||||
}
|
||||
|
||||
$form.submit(() => {
|
||||
savePrefix();
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
export default {
|
||||
showDialog
|
||||
};
|
||||
@@ -1,45 +0,0 @@
|
||||
"use strict";
|
||||
|
||||
const editTreePrefix = (function() {
|
||||
const $dialog = $("#edit-tree-prefix-dialog");
|
||||
const $form = $("#edit-tree-prefix-form");
|
||||
const $treePrefixInput = $("#tree-prefix-input");
|
||||
const $noteTitle = $('#tree-prefix-note-title');
|
||||
|
||||
let noteTreeId;
|
||||
|
||||
async function showDialog() {
|
||||
glob.activeDialog = $dialog;
|
||||
|
||||
await $dialog.dialog({
|
||||
modal: true,
|
||||
width: 500
|
||||
});
|
||||
|
||||
const currentNode = noteTree.getCurrentNode();
|
||||
|
||||
noteTreeId = currentNode.data.noteTreeId;
|
||||
|
||||
$treePrefixInput.val(currentNode.data.prefix).focus();
|
||||
|
||||
const noteTitle = noteTree.getNoteTitle(currentNode.data.noteId);
|
||||
|
||||
$noteTitle.html(noteTitle);
|
||||
}
|
||||
|
||||
$form.submit(() => {
|
||||
const prefix = $treePrefixInput.val();
|
||||
|
||||
server.put('tree/' + noteTreeId + '/set-prefix', {
|
||||
prefix: prefix
|
||||
}).then(() => noteTree.setPrefix(noteTreeId, prefix));
|
||||
|
||||
$dialog.dialog("close");
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
return {
|
||||
showDialog
|
||||
};
|
||||
})();
|
||||
@@ -1,38 +1,38 @@
|
||||
"use strict";
|
||||
import linkService from '../services/link.js';
|
||||
import utils from '../services/utils.js';
|
||||
import server from '../services/server.js';
|
||||
|
||||
const eventLog = (function() {
|
||||
const $dialog = $("#event-log-dialog");
|
||||
const $list = $("#event-log-list");
|
||||
const $dialog = $("#event-log-dialog");
|
||||
const $list = $("#event-log-list");
|
||||
|
||||
async function showDialog() {
|
||||
glob.activeDialog = $dialog;
|
||||
async function showDialog() {
|
||||
glob.activeDialog = $dialog;
|
||||
|
||||
$dialog.dialog({
|
||||
modal: true,
|
||||
width: 800,
|
||||
height: 700
|
||||
});
|
||||
$dialog.dialog({
|
||||
modal: true,
|
||||
width: 800,
|
||||
height: 700
|
||||
});
|
||||
|
||||
const result = await server.get('event-log');
|
||||
const result = await server.get('event-log');
|
||||
|
||||
$list.html('');
|
||||
$list.html('');
|
||||
|
||||
for (const event of result) {
|
||||
const dateTime = formatDateTime(parseDate(event.dateAdded));
|
||||
for (const event of result) {
|
||||
const dateTime = utils.formatDateTime(utils.parseDate(event.dateCreated));
|
||||
|
||||
if (event.noteId) {
|
||||
const noteLink = link.createNoteLink(event.noteId).prop('outerHTML');
|
||||
if (event.noteId) {
|
||||
const noteLink = await linkService.createNoteLink(event.noteId).prop('outerHTML');
|
||||
|
||||
event.comment = event.comment.replace('<note>', noteLink);
|
||||
}
|
||||
|
||||
const eventEl = $('<li>').html(dateTime + " - " + event.comment);
|
||||
|
||||
$list.append(eventEl);
|
||||
event.comment = event.comment.replace('<note>', noteLink);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
showDialog
|
||||
};
|
||||
})();
|
||||
const eventEl = $('<li>').html(dateTime + " - " + event.comment);
|
||||
|
||||
$list.append(eventEl);
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
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 $autoComplete = $("#jump-to-note-autocomplete");
|
||||
const $form = $("#jump-to-note-form");
|
||||
const $dialog = $("#jump-to-note-dialog");
|
||||
const $autoComplete = $("#jump-to-note-autocomplete");
|
||||
const $form = $("#jump-to-note-form");
|
||||
const $jumpToNoteButton = $("#jump-to-note-button");
|
||||
const $showInFullTextButton = $("#show-in-full-text-button");
|
||||
|
||||
async function showDialog() {
|
||||
glob.activeDialog = $dialog;
|
||||
async function showDialog() {
|
||||
glob.activeDialog = $dialog;
|
||||
|
||||
$autoComplete.val('');
|
||||
$autoComplete.val('');
|
||||
|
||||
$dialog.dialog({
|
||||
modal: true,
|
||||
width: 800
|
||||
});
|
||||
|
||||
await $autoComplete.autocomplete({
|
||||
source: await stopWatch("building autocomplete", noteTree.getAutocompleteItems),
|
||||
minLength: 0
|
||||
});
|
||||
}
|
||||
|
||||
function getSelectedNotePath() {
|
||||
const val = $autoComplete.val();
|
||||
return link.getNodePathFromLabel(val);
|
||||
}
|
||||
|
||||
function goToNote() {
|
||||
const notePath = getSelectedNotePath();
|
||||
|
||||
if (notePath) {
|
||||
noteTree.activateNode(notePath);
|
||||
|
||||
$dialog.dialog('close');
|
||||
}
|
||||
}
|
||||
|
||||
$(document).bind('keydown', 'ctrl+j', e => {
|
||||
showDialog();
|
||||
|
||||
e.preventDefault();
|
||||
$dialog.dialog({
|
||||
modal: true,
|
||||
width: 800
|
||||
});
|
||||
|
||||
$form.submit(() => {
|
||||
const action = $dialog.find("button:focus").val();
|
||||
await $autoComplete.autocomplete({
|
||||
source: async function(request, response) {
|
||||
const result = await server.get('autocomplete?query=' + encodeURIComponent(request.term));
|
||||
|
||||
goToNote();
|
||||
|
||||
return false;
|
||||
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
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
showDialog
|
||||
};
|
||||
})();
|
||||
function getSelectedNotePath() {
|
||||
const val = $autoComplete.val();
|
||||
return linkService.getNodePathFromLabel(val);
|
||||
}
|
||||
|
||||
function goToNote() {
|
||||
const notePath = getSelectedNotePath();
|
||||
|
||||
if (notePath) {
|
||||
treeService.activateNode(notePath);
|
||||
|
||||
$dialog.dialog('close');
|
||||
}
|
||||
}
|
||||
|
||||
function showInFullText(e) {
|
||||
// stop from propagating upwards (dangerous especially with ctrl+enter executable javascript notes)
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
const searchText = $autoComplete.val();
|
||||
|
||||
searchNotesService.resetSearch();
|
||||
searchNotesService.showSearch();
|
||||
searchNotesService.doSearch(searchText);
|
||||
|
||||
$dialog.dialog('close');
|
||||
}
|
||||
|
||||
$form.submit(() => {
|
||||
goToNote();
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
$jumpToNoteButton.click(goToNote);
|
||||
|
||||
$showInFullTextButton.click(showInFullText);
|
||||
|
||||
$dialog.bind('keydown', 'ctrl+return', showInFullText);
|
||||
|
||||
export default {
|
||||
showDialog
|
||||
};
|
||||
223
src/public/javascripts/dialogs/labels.js
Normal file
@@ -0,0 +1,223 @@
|
||||
import noteDetailService from '../services/note_detail.js';
|
||||
import utils from '../services/utils.js';
|
||||
import server from '../services/server.js';
|
||||
import infoService from "../services/info.js";
|
||||
|
||||
const $dialog = $("#labels-dialog");
|
||||
const $saveLabelsButton = $("#save-labels-button");
|
||||
const $labelsBody = $('#labels-table tbody');
|
||||
|
||||
const labelsModel = new LabelsModel();
|
||||
let labelNames = [];
|
||||
|
||||
function LabelsModel() {
|
||||
const self = this;
|
||||
|
||||
this.labels = ko.observableArray();
|
||||
|
||||
this.updateLabelPositions = function() {
|
||||
let position = 0;
|
||||
|
||||
// we need to update positions by searching in the DOM, because order of the
|
||||
// labels in the viewmodel (self.labels()) stays the same
|
||||
$labelsBody.find('input[name="position"]').each(function() {
|
||||
const label = self.getTargetLabel(this);
|
||||
|
||||
label().position = position++;
|
||||
});
|
||||
};
|
||||
|
||||
this.loadLabels = async function() {
|
||||
const noteId = noteDetailService.getCurrentNoteId();
|
||||
|
||||
const labels = await server.get('notes/' + noteId + '/labels');
|
||||
|
||||
self.labels(labels.map(ko.observable));
|
||||
|
||||
addLastEmptyRow();
|
||||
|
||||
labelNames = await server.get('labels/names');
|
||||
|
||||
// label might not be rendered immediatelly so could not focus
|
||||
setTimeout(() => $(".label-name:last").focus(), 100);
|
||||
|
||||
$labelsBody.sortable({
|
||||
handle: '.handle',
|
||||
containment: $labelsBody,
|
||||
update: this.updateLabelPositions
|
||||
});
|
||||
};
|
||||
|
||||
this.deleteLabel = function(data, event) {
|
||||
const label = self.getTargetLabel(event.target);
|
||||
const labelData = label();
|
||||
|
||||
if (labelData) {
|
||||
labelData.isDeleted = 1;
|
||||
|
||||
label(labelData);
|
||||
|
||||
addLastEmptyRow();
|
||||
}
|
||||
};
|
||||
|
||||
function isValid() {
|
||||
for (let labels = self.labels(), i = 0; i < labels.length; i++) {
|
||||
if (self.isEmptyName(i)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
this.save = async function() {
|
||||
// we need to defocus from input (in case of enter-triggered save) because value is updated
|
||||
// on blur event (because of conflict with jQuery UI Autocomplete). Without this, input would
|
||||
// stay in focus, blur wouldn't be triggered and change wouldn't be updated in the viewmodel.
|
||||
$saveLabelsButton.focus();
|
||||
|
||||
if (!isValid()) {
|
||||
alert("Please fix all validation errors and try saving again.");
|
||||
return;
|
||||
}
|
||||
|
||||
self.updateLabelPositions();
|
||||
|
||||
const noteId = noteDetailService.getCurrentNoteId();
|
||||
|
||||
const labelsToSave = self.labels()
|
||||
.map(label => label())
|
||||
.filter(label => label.labelId !== "" || label.name !== "");
|
||||
|
||||
const labels = await server.put('notes/' + noteId + '/labels', labelsToSave);
|
||||
|
||||
self.labels(labels.map(ko.observable));
|
||||
|
||||
addLastEmptyRow();
|
||||
|
||||
infoService.showMessage("Labels have been saved.");
|
||||
|
||||
noteDetailService.loadLabelList();
|
||||
};
|
||||
|
||||
function addLastEmptyRow() {
|
||||
const labels = self.labels().filter(attr => attr().isDeleted === 0);
|
||||
const last = labels.length === 0 ? null : labels[labels.length - 1]();
|
||||
|
||||
if (!last || last.name.trim() !== "" || last.value !== "") {
|
||||
self.labels.push(ko.observable({
|
||||
labelId: '',
|
||||
name: '',
|
||||
value: '',
|
||||
isDeleted: 0,
|
||||
position: 0
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
this.labelChanged = function (data, event) {
|
||||
addLastEmptyRow();
|
||||
|
||||
const label = self.getTargetLabel(event.target);
|
||||
|
||||
label.valueHasMutated();
|
||||
};
|
||||
|
||||
this.isNotUnique = function(index) {
|
||||
const cur = self.labels()[index]();
|
||||
|
||||
if (cur.name.trim() === "") {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (let labels = self.labels(), i = 0; i < labels.length; i++) {
|
||||
const label = labels[i]();
|
||||
|
||||
if (index !== i && cur.name === label.name) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
this.isEmptyName = function(index) {
|
||||
const cur = self.labels()[index]();
|
||||
|
||||
return cur.name.trim() === "" && (cur.labelId !== "" || cur.value !== "");
|
||||
};
|
||||
|
||||
this.getTargetLabel = function(target) {
|
||||
const context = ko.contextFor(target);
|
||||
const index = context.$index();
|
||||
|
||||
return self.labels()[index];
|
||||
}
|
||||
}
|
||||
|
||||
async function showDialog() {
|
||||
glob.activeDialog = $dialog;
|
||||
|
||||
await labelsModel.loadLabels();
|
||||
|
||||
$dialog.dialog({
|
||||
modal: true,
|
||||
width: 800,
|
||||
height: 500
|
||||
});
|
||||
}
|
||||
|
||||
ko.applyBindings(labelsModel, document.getElementById('labels-dialog'));
|
||||
|
||||
$(document).on('focus', '.label-name', function (e) {
|
||||
if (!$(this).hasClass("ui-autocomplete-input")) {
|
||||
$(this).autocomplete({
|
||||
// shouldn't be required and autocomplete should just accept array of strings, but that fails
|
||||
// because we have overriden filter() function in autocomplete.js
|
||||
source: labelNames.map(label => {
|
||||
return {
|
||||
label: label,
|
||||
value: label
|
||||
}
|
||||
}),
|
||||
minLength: 0
|
||||
});
|
||||
}
|
||||
|
||||
$(this).autocomplete("search", $(this).val());
|
||||
});
|
||||
|
||||
$(document).on('focus', '.label-value', async function (e) {
|
||||
if (!$(this).hasClass("ui-autocomplete-input")) {
|
||||
const labelName = $(this).parent().parent().find('.label-name').val();
|
||||
|
||||
if (labelName.trim() === "") {
|
||||
return;
|
||||
}
|
||||
|
||||
const labelValues = await server.get('labels/values/' + encodeURIComponent(labelName));
|
||||
|
||||
if (labelValues.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
$(this).autocomplete({
|
||||
// shouldn't be required and autocomplete should just accept array of strings, but that fails
|
||||
// because we have overriden filter() function in autocomplete.js
|
||||
source: labelValues.map(label => {
|
||||
return {
|
||||
label: label,
|
||||
value: label
|
||||
}
|
||||
}),
|
||||
minLength: 0
|
||||
});
|
||||
}
|
||||
|
||||
$(this).autocomplete("search", $(this).val());
|
||||
});
|
||||
|
||||
export default {
|
||||
showDialog
|
||||
};
|
||||
@@ -1,78 +0,0 @@
|
||||
"use strict";
|
||||
|
||||
const noteHistory = (function() {
|
||||
const $dialog = $("#note-history-dialog");
|
||||
const $list = $("#note-history-list");
|
||||
const $content = $("#note-history-content");
|
||||
const $title = $("#note-history-title");
|
||||
|
||||
let historyItems = [];
|
||||
|
||||
async function showCurrentNoteHistory() {
|
||||
await showNoteHistoryDialog(noteEditor.getCurrentNoteId());
|
||||
}
|
||||
|
||||
async function showNoteHistoryDialog(noteId, noteRevisionId) {
|
||||
glob.activeDialog = $dialog;
|
||||
|
||||
$dialog.dialog({
|
||||
modal: true,
|
||||
width: 800,
|
||||
height: 700
|
||||
});
|
||||
|
||||
$list.empty();
|
||||
$content.empty();
|
||||
|
||||
historyItems = await server.get('notes-history/' + noteId);
|
||||
|
||||
for (const item of historyItems) {
|
||||
const dateModified = parseDate(item.dateModifiedFrom);
|
||||
|
||||
$list.append($('<option>', {
|
||||
value: item.noteRevisionId,
|
||||
text: formatDateTime(dateModified)
|
||||
}));
|
||||
}
|
||||
|
||||
if (historyItems.length > 0) {
|
||||
if (!noteRevisionId) {
|
||||
noteRevisionId = $list.find("option:first").val();
|
||||
}
|
||||
|
||||
$list.val(noteRevisionId).trigger('change');
|
||||
}
|
||||
else {
|
||||
$title.text("No history for this note yet...");
|
||||
}
|
||||
}
|
||||
|
||||
$(document).bind('keydown', 'alt+h', e => {
|
||||
showCurrentNoteHistory();
|
||||
|
||||
e.preventDefault();
|
||||
});
|
||||
|
||||
$list.on('change', () => {
|
||||
const optVal = $list.find(":selected").val();
|
||||
|
||||
const historyItem = historyItems.find(r => r.noteRevisionId === optVal);
|
||||
|
||||
$title.html(historyItem.title);
|
||||
$content.html(historyItem.content);
|
||||
});
|
||||
|
||||
$(document).on('click', "a[action='note-history']", event => {
|
||||
const linkEl = $(event.target);
|
||||
const noteId = linkEl.attr('note-path');
|
||||
const noteRevisionId = linkEl.attr('note-history-id');
|
||||
|
||||
showNoteHistoryDialog(noteId, noteRevisionId);
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
return {
|
||||
showCurrentNoteHistory
|
||||
};
|
||||
})();
|
||||
78
src/public/javascripts/dialogs/note_revisions.js
Normal file
@@ -0,0 +1,78 @@
|
||||
import noteDetailService from '../services/note_detail.js';
|
||||
import utils from '../services/utils.js';
|
||||
import server from '../services/server.js';
|
||||
|
||||
const $dialog = $("#note-revisions-dialog");
|
||||
const $list = $("#note-revision-list");
|
||||
const $content = $("#note-revision-content");
|
||||
const $title = $("#note-revision-title");
|
||||
|
||||
let revisionItems = [];
|
||||
|
||||
async function showCurrentNoteRevisions() {
|
||||
await showNoteRevisionsDialog(noteDetailService.getCurrentNoteId());
|
||||
}
|
||||
|
||||
async function showNoteRevisionsDialog(noteId, noteRevisionId) {
|
||||
glob.activeDialog = $dialog;
|
||||
|
||||
$dialog.dialog({
|
||||
modal: true,
|
||||
width: 800,
|
||||
height: 700
|
||||
});
|
||||
|
||||
$list.empty();
|
||||
$content.empty();
|
||||
|
||||
revisionItems = await server.get('notes/' + noteId + '/revisions');
|
||||
|
||||
for (const item of revisionItems) {
|
||||
const dateModified = utils.parseDate(item.dateModifiedFrom);
|
||||
|
||||
$list.append($('<option>', {
|
||||
value: item.noteRevisionId,
|
||||
text: utils.formatDateTime(dateModified)
|
||||
}));
|
||||
}
|
||||
|
||||
if (revisionItems.length > 0) {
|
||||
if (!noteRevisionId) {
|
||||
noteRevisionId = $list.find("option:first").val();
|
||||
}
|
||||
|
||||
$list.val(noteRevisionId).trigger('change');
|
||||
}
|
||||
else {
|
||||
$title.text("No revisions for this note yet...");
|
||||
}
|
||||
}
|
||||
|
||||
$list.on('change', () => {
|
||||
const optVal = $list.find(":selected").val();
|
||||
|
||||
const revisionItem = revisionItems.find(r => r.noteRevisionId === optVal);
|
||||
|
||||
$title.html(revisionItem.title);
|
||||
|
||||
if (revisionItem.type === 'text') {
|
||||
$content.html(revisionItem.content);
|
||||
}
|
||||
else if (revisionItem.type === 'code') {
|
||||
$content.html($("<pre>").text(revisionItem.content));
|
||||
}
|
||||
});
|
||||
|
||||
$(document).on('click', "a[action='note-revision']", event => {
|
||||
const linkEl = $(event.target);
|
||||
const noteId = linkEl.attr('note-path');
|
||||
const noteRevisionId = linkEl.attr('note-revision-id');
|
||||
|
||||
showNoteRevisionsDialog(noteId, noteRevisionId);
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
export default {
|
||||
showCurrentNoteRevisions
|
||||
};
|
||||
@@ -1,57 +1,49 @@
|
||||
"use strict";
|
||||
import noteDetailService from '../services/note_detail.js';
|
||||
|
||||
const noteSource = (function() {
|
||||
const $dialog = $("#note-source-dialog");
|
||||
const $noteSource = $("#note-source");
|
||||
const $dialog = $("#note-source-dialog");
|
||||
const $noteSource = $("#note-source");
|
||||
|
||||
function showDialog() {
|
||||
glob.activeDialog = $dialog;
|
||||
function showDialog() {
|
||||
glob.activeDialog = $dialog;
|
||||
|
||||
$dialog.dialog({
|
||||
modal: true,
|
||||
width: 800,
|
||||
height: 500
|
||||
});
|
||||
|
||||
const noteText = noteEditor.getCurrentNote().detail.content;
|
||||
|
||||
$noteSource.text(formatHtml(noteText));
|
||||
}
|
||||
|
||||
function formatHtml(str) {
|
||||
const div = document.createElement('div');
|
||||
div.innerHTML = str.trim();
|
||||
|
||||
return formatNode(div, 0).innerHTML.trim();
|
||||
}
|
||||
|
||||
function formatNode(node, level) {
|
||||
const indentBefore = new Array(level++ + 1).join(' ');
|
||||
const indentAfter = new Array(level - 1).join(' ');
|
||||
let textNode;
|
||||
|
||||
for (let i = 0; i < node.children.length; i++) {
|
||||
textNode = document.createTextNode('\n' + indentBefore);
|
||||
node.insertBefore(textNode, node.children[i]);
|
||||
|
||||
formatNode(node.children[i], level);
|
||||
|
||||
if (node.lastElementChild === node.children[i]) {
|
||||
textNode = document.createTextNode('\n' + indentAfter);
|
||||
node.appendChild(textNode);
|
||||
}
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
$(document).bind('keydown', 'ctrl+u', e => {
|
||||
showDialog();
|
||||
|
||||
e.preventDefault();
|
||||
$dialog.dialog({
|
||||
modal: true,
|
||||
width: 800,
|
||||
height: 500
|
||||
});
|
||||
|
||||
return {
|
||||
showDialog
|
||||
};
|
||||
})();
|
||||
const noteText = noteDetailService.getCurrentNote().content;
|
||||
|
||||
$noteSource.text(formatHtml(noteText));
|
||||
}
|
||||
|
||||
function formatHtml(str) {
|
||||
const div = document.createElement('div');
|
||||
div.innerHTML = str.trim();
|
||||
|
||||
return formatNode(div, 0).innerHTML.trim();
|
||||
}
|
||||
|
||||
function formatNode(node, level) {
|
||||
const indentBefore = new Array(level++ + 1).join(' ');
|
||||
const indentAfter = new Array(level - 1).join(' ');
|
||||
let textNode;
|
||||
|
||||
for (let i = 0; i < node.children.length; i++) {
|
||||
textNode = document.createTextNode('\n' + indentBefore);
|
||||
node.insertBefore(textNode, node.children[i]);
|
||||
|
||||
formatNode(node.children[i], level);
|
||||
|
||||
if (node.lastElementChild === node.children[i]) {
|
||||
textNode = document.createTextNode('\n' + indentAfter);
|
||||
node.appendChild(textNode);
|
||||
}
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
export default {
|
||||
showDialog
|
||||
};
|
||||
242
src/public/javascripts/dialogs/options.js
Normal file
@@ -0,0 +1,242 @@
|
||||
"use strict";
|
||||
|
||||
import protectedSessionHolder from '../services/protected_session_holder.js';
|
||||
import server from '../services/server.js';
|
||||
import infoService from "../services/info.js";
|
||||
import zoomService from "../services/zoom.js";
|
||||
import utils from "../services/utils.js";
|
||||
|
||||
const $dialog = $("#options-dialog");
|
||||
const $tabs = $("#options-tabs");
|
||||
|
||||
const tabHandlers = [];
|
||||
|
||||
function addTabHandler(handler) {
|
||||
tabHandlers.push(handler);
|
||||
}
|
||||
|
||||
async function showDialog() {
|
||||
glob.activeDialog = $dialog;
|
||||
|
||||
const options = await server.get('options');
|
||||
|
||||
$dialog.dialog({
|
||||
modal: true,
|
||||
width: 900
|
||||
});
|
||||
|
||||
$tabs.tabs();
|
||||
|
||||
for (const handler of tabHandlers) {
|
||||
if (handler.optionsLoaded) {
|
||||
handler.optionsLoaded(options);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function saveOptions(optionName, optionValue) {
|
||||
await server.put('options/' + encodeURIComponent(optionName) + '/' + encodeURIComponent(optionValue));
|
||||
|
||||
infoService.showMessage("Options change have been saved.");
|
||||
}
|
||||
|
||||
export default {
|
||||
showDialog,
|
||||
saveOptions
|
||||
};
|
||||
|
||||
addTabHandler((function() {
|
||||
const $themeSelect = $("#theme-select");
|
||||
const $zoomFactorSelect = $("#zoom-factor-select");
|
||||
const $html = $("html");
|
||||
|
||||
function optionsLoaded(options) {
|
||||
$themeSelect.val(options.theme);
|
||||
|
||||
if (utils.isElectron()) {
|
||||
$zoomFactorSelect.val(options.zoomFactor);
|
||||
}
|
||||
else {
|
||||
$zoomFactorSelect.prop('disabled', true);
|
||||
}
|
||||
}
|
||||
|
||||
$themeSelect.change(function() {
|
||||
const newTheme = $(this).val();
|
||||
|
||||
$html.attr("class", "theme-" + newTheme);
|
||||
|
||||
server.put('options/theme/' + newTheme);
|
||||
});
|
||||
|
||||
$zoomFactorSelect.change(function() {
|
||||
const newZoomFactor = $(this).val();
|
||||
|
||||
zoomService.setZoomFactorAndSave(newZoomFactor);
|
||||
});
|
||||
|
||||
return {
|
||||
optionsLoaded
|
||||
};
|
||||
})());
|
||||
|
||||
addTabHandler((function() {
|
||||
const $form = $("#change-password-form");
|
||||
const $oldPassword = $("#old-password");
|
||||
const $newPassword1 = $("#new-password1");
|
||||
const $newPassword2 = $("#new-password2");
|
||||
|
||||
function optionsLoaded(options) {
|
||||
}
|
||||
|
||||
$form.submit(() => {
|
||||
const oldPassword = $oldPassword.val();
|
||||
const newPassword1 = $newPassword1.val();
|
||||
const newPassword2 = $newPassword2.val();
|
||||
|
||||
$oldPassword.val('');
|
||||
$newPassword1.val('');
|
||||
$newPassword2.val('');
|
||||
|
||||
if (newPassword1 !== newPassword2) {
|
||||
alert("New passwords are not the same.");
|
||||
return false;
|
||||
}
|
||||
|
||||
server.post('password/change', {
|
||||
'current_password': oldPassword,
|
||||
'new_password': newPassword1
|
||||
}).then(result => {
|
||||
if (result.success) {
|
||||
alert("Password has been changed. Trilium will be reloaded after you press OK.");
|
||||
|
||||
// password changed so current protected session is invalid and needs to be cleared
|
||||
protectedSessionHolder.resetProtectedSession();
|
||||
}
|
||||
else {
|
||||
infoService.showError(result.message);
|
||||
}
|
||||
});
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
return {
|
||||
optionsLoaded
|
||||
};
|
||||
})());
|
||||
|
||||
addTabHandler((function() {
|
||||
const $form = $("#protected-session-timeout-form");
|
||||
const $protectedSessionTimeout = $("#protected-session-timeout-in-seconds");
|
||||
const optionName = 'protectedSessionTimeout';
|
||||
|
||||
function optionsLoaded(options) {
|
||||
$protectedSessionTimeout.val(options[optionName]);
|
||||
}
|
||||
|
||||
$form.submit(() => {
|
||||
const protectedSessionTimeout = $protectedSessionTimeout.val();
|
||||
|
||||
saveOptions(optionName, protectedSessionTimeout).then(() => {
|
||||
protectedSessionHolder.setProtectedSessionTimeout(protectedSessionTimeout);
|
||||
});
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
return {
|
||||
optionsLoaded
|
||||
};
|
||||
})());
|
||||
|
||||
addTabHandler((function () {
|
||||
const $form = $("#note-revision-snapshot-time-interval-form");
|
||||
const $timeInterval = $("#note-revision-snapshot-time-interval-in-seconds");
|
||||
const optionName = 'noteRevisionSnapshotTimeInterval';
|
||||
|
||||
function optionsLoaded(options) {
|
||||
$timeInterval.val(options[optionName]);
|
||||
}
|
||||
|
||||
$form.submit(() => {
|
||||
saveOptions(optionName, $timeInterval.val());
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
return {
|
||||
optionsLoaded
|
||||
};
|
||||
})());
|
||||
|
||||
addTabHandler((async function () {
|
||||
const $appVersion = $("#app-version");
|
||||
const $dbVersion = $("#db-version");
|
||||
const $syncVersion = $("#sync-version");
|
||||
const $buildDate = $("#build-date");
|
||||
const $buildRevision = $("#build-revision");
|
||||
|
||||
const appInfo = await server.get('app-info');
|
||||
|
||||
$appVersion.html(appInfo.appVersion);
|
||||
$dbVersion.html(appInfo.dbVersion);
|
||||
$syncVersion.html(appInfo.syncVersion);
|
||||
$buildDate.html(appInfo.buildDate);
|
||||
$buildRevision.html(appInfo.buildRevision);
|
||||
$buildRevision.attr('href', 'https://github.com/zadam/trilium/commit/' + appInfo.buildRevision);
|
||||
|
||||
return {};
|
||||
})());
|
||||
|
||||
addTabHandler((async function () {
|
||||
const $forceFullSyncButton = $("#force-full-sync-button");
|
||||
const $fillSyncRowsButton = $("#fill-sync-rows-button");
|
||||
const $anonymizeButton = $("#anonymize-button");
|
||||
const $cleanupSoftDeletedButton = $("#cleanup-soft-deleted-items-button");
|
||||
const $cleanupUnusedImagesButton = $("#cleanup-unused-images-button");
|
||||
const $vacuumDatabaseButton = $("#vacuum-database-button");
|
||||
|
||||
$forceFullSyncButton.click(async () => {
|
||||
await server.post('sync/force-full-sync');
|
||||
|
||||
infoService.showMessage("Full sync triggered");
|
||||
});
|
||||
|
||||
$fillSyncRowsButton.click(async () => {
|
||||
await server.post('sync/fill-sync-rows');
|
||||
|
||||
infoService.showMessage("Sync rows filled successfully");
|
||||
});
|
||||
|
||||
|
||||
$anonymizeButton.click(async () => {
|
||||
await server.post('anonymization/anonymize');
|
||||
|
||||
infoService.showMessage("Created anonymized database");
|
||||
});
|
||||
|
||||
$cleanupSoftDeletedButton.click(async () => {
|
||||
if (confirm("Do you really want to clean up soft-deleted items?")) {
|
||||
await server.post('cleanup/cleanup-soft-deleted-items');
|
||||
|
||||
infoService.showMessage("Soft deleted items have been cleaned up");
|
||||
}
|
||||
});
|
||||
|
||||
$cleanupUnusedImagesButton.click(async () => {
|
||||
if (confirm("Do you really want to clean up unused images?")) {
|
||||
await server.post('cleanup/cleanup-unused-images');
|
||||
|
||||
infoService.showMessage("Unused images have been cleaned up");
|
||||
}
|
||||
});
|
||||
|
||||
$vacuumDatabaseButton.click(async () => {
|
||||
await server.post('cleanup/vacuum-database');
|
||||
|
||||
infoService.showMessage("Database has been vacuumed");
|
||||
});
|
||||
|
||||
return {};
|
||||
})());
|
||||
@@ -1,89 +1,87 @@
|
||||
"use strict";
|
||||
import linkService from '../services/link.js';
|
||||
import utils from '../services/utils.js';
|
||||
import server from '../services/server.js';
|
||||
|
||||
const recentChanges = (function() {
|
||||
const $dialog = $("#recent-changes-dialog");
|
||||
const $dialog = $("#recent-changes-dialog");
|
||||
|
||||
async function showDialog() {
|
||||
glob.activeDialog = $dialog;
|
||||
async function showDialog() {
|
||||
glob.activeDialog = $dialog;
|
||||
|
||||
$dialog.dialog({
|
||||
modal: true,
|
||||
width: 800,
|
||||
height: 700
|
||||
});
|
||||
$dialog.dialog({
|
||||
modal: true,
|
||||
width: 800,
|
||||
height: 700
|
||||
});
|
||||
|
||||
const result = await server.get('recent-changes/');
|
||||
const result = await server.get('recent-changes/');
|
||||
|
||||
$dialog.html('');
|
||||
$dialog.html('');
|
||||
|
||||
const groupedByDate = groupByDate(result);
|
||||
const groupedByDate = groupByDate(result);
|
||||
|
||||
for (const [dateDay, dayChanges] of groupedByDate) {
|
||||
const changesListEl = $('<ul>');
|
||||
for (const [dateDay, dayChanges] of groupedByDate) {
|
||||
const changesListEl = $('<ul>');
|
||||
|
||||
const dayEl = $('<div>').append($('<b>').html(formatDate(dateDay))).append(changesListEl);
|
||||
const dayEl = $('<div>').append($('<b>').html(utils.formatDate(dateDay))).append(changesListEl);
|
||||
|
||||
for (const change of dayChanges) {
|
||||
const formattedTime = formatTime(parseDate(change.dateModifiedTo));
|
||||
for (const change of dayChanges) {
|
||||
const formattedTime = utils.formatTime(utils.parseDate(change.dateModifiedTo));
|
||||
|
||||
const revLink = $("<a>", {
|
||||
href: 'javascript:',
|
||||
text: 'rev'
|
||||
}).attr('action', 'note-history')
|
||||
.attr('note-path', change.noteId)
|
||||
.attr('note-history-id', change.noteRevisionId);
|
||||
const revLink = $("<a>", {
|
||||
href: 'javascript:',
|
||||
text: 'rev'
|
||||
}).attr('action', 'note-revision')
|
||||
.attr('note-path', change.noteId)
|
||||
.attr('note-revision-id', change.noteRevisionId);
|
||||
|
||||
let noteLink;
|
||||
let noteLink;
|
||||
|
||||
if (change.current_isDeleted) {
|
||||
noteLink = change.current_title;
|
||||
}
|
||||
else {
|
||||
noteLink = link.createNoteLink(change.noteId, change.title);
|
||||
}
|
||||
|
||||
changesListEl.append($('<li>')
|
||||
.append(formattedTime + ' - ')
|
||||
.append(noteLink)
|
||||
.append(' (').append(revLink).append(')'));
|
||||
}
|
||||
|
||||
$dialog.append(dayEl);
|
||||
}
|
||||
}
|
||||
|
||||
function groupByDate(result) {
|
||||
const groupedByDate = new Map();
|
||||
const dayCache = {};
|
||||
|
||||
for (const row of result) {
|
||||
let dateDay = parseDate(row.dateModifiedTo);
|
||||
dateDay.setHours(0);
|
||||
dateDay.setMinutes(0);
|
||||
dateDay.setSeconds(0);
|
||||
dateDay.setMilliseconds(0);
|
||||
|
||||
// this stupidity is to make sure that we always use the same day object because Map uses only
|
||||
// reference equality
|
||||
if (dayCache[dateDay]) {
|
||||
dateDay = dayCache[dateDay];
|
||||
if (change.current_isDeleted) {
|
||||
noteLink = change.current_title;
|
||||
}
|
||||
else {
|
||||
dayCache[dateDay] = dateDay;
|
||||
noteLink = await linkService.createNoteLink(change.noteId, change.title);
|
||||
}
|
||||
|
||||
if (!groupedByDate.has(dateDay)) {
|
||||
groupedByDate.set(dateDay, []);
|
||||
}
|
||||
|
||||
groupedByDate.get(dateDay).push(row);
|
||||
changesListEl.append($('<li>')
|
||||
.append(formattedTime + ' - ')
|
||||
.append(noteLink)
|
||||
.append(' (').append(revLink).append(')'));
|
||||
}
|
||||
return groupedByDate;
|
||||
|
||||
$dialog.append(dayEl);
|
||||
}
|
||||
}
|
||||
|
||||
$(document).bind('keydown', 'alt+r', showDialog);
|
||||
function groupByDate(result) {
|
||||
const groupedByDate = new Map();
|
||||
const dayCache = {};
|
||||
|
||||
return {
|
||||
showDialog
|
||||
};
|
||||
})();
|
||||
for (const row of result) {
|
||||
let dateDay = utils.parseDate(row.dateModifiedTo);
|
||||
dateDay.setHours(0);
|
||||
dateDay.setMinutes(0);
|
||||
dateDay.setSeconds(0);
|
||||
dateDay.setMilliseconds(0);
|
||||
|
||||
// this stupidity is to make sure that we always use the same day object because Map uses only
|
||||
// reference equality
|
||||
if (dayCache[dateDay]) {
|
||||
dateDay = dayCache[dateDay];
|
||||
}
|
||||
else {
|
||||
dayCache[dateDay] = dateDay;
|
||||
}
|
||||
|
||||
if (!groupedByDate.has(dateDay)) {
|
||||
groupedByDate.set(dateDay, []);
|
||||
}
|
||||
|
||||
groupedByDate.get(dateDay).push(row);
|
||||
}
|
||||
return groupedByDate;
|
||||
}
|
||||
|
||||
export default {
|
||||
showDialog
|
||||
};
|
||||
@@ -1,102 +1,74 @@
|
||||
"use strict";
|
||||
import treeService from '../services/tree.js';
|
||||
import server from '../services/server.js';
|
||||
|
||||
const recentNotes = (function() {
|
||||
const $dialog = $("#recent-notes-dialog");
|
||||
const $searchInput = $('#recent-notes-search-input');
|
||||
const $dialog = $("#recent-notes-dialog");
|
||||
const $searchInput = $('#recent-notes-search-input');
|
||||
|
||||
// list of recent note paths
|
||||
let list = [];
|
||||
function addRecentNote(branchId, notePath) {
|
||||
setTimeout(async () => {
|
||||
// we include the note into recent list only if the user stayed on the note at least 5 seconds
|
||||
if (notePath && notePath === treeService.getCurrentNotePath()) {
|
||||
const result = await server.put('recent-notes/' + branchId + '/' + encodeURIComponent(notePath));
|
||||
}
|
||||
}, 1500);
|
||||
}
|
||||
|
||||
async function reload() {
|
||||
const result = await server.get('recent-notes');
|
||||
async function showDialog() {
|
||||
glob.activeDialog = $dialog;
|
||||
|
||||
list = result.map(r => r.notePath);
|
||||
}
|
||||
|
||||
function addRecentNote(noteTreeId, notePath) {
|
||||
setTimeout(async () => {
|
||||
// we include the note into recent list only if the user stayed on the note at least 5 seconds
|
||||
if (notePath && notePath === noteTree.getCurrentNotePath()) {
|
||||
const result = await server.put('recent-notes/' + noteTreeId + '/' + encodeURIComponent(notePath));
|
||||
|
||||
list = result.map(r => r.notePath);
|
||||
}
|
||||
}, 1500);
|
||||
}
|
||||
|
||||
function showDialog() {
|
||||
glob.activeDialog = $dialog;
|
||||
|
||||
$dialog.dialog({
|
||||
modal: true,
|
||||
width: 800,
|
||||
height: 100,
|
||||
position: { my: "center top+100", at: "top", of: window }
|
||||
});
|
||||
|
||||
$searchInput.val('');
|
||||
|
||||
// remove the current note
|
||||
const recNotes = list.filter(note => note !== noteTree.getCurrentNotePath());
|
||||
|
||||
$searchInput.autocomplete({
|
||||
source: recNotes.map(notePath => {
|
||||
let noteTitle;
|
||||
|
||||
try {
|
||||
noteTitle = noteTree.getNotePathTitle(notePath);
|
||||
}
|
||||
catch (e) {
|
||||
noteTitle = "[error - can't find note title]";
|
||||
|
||||
messaging.logError("Could not find title for notePath=" + notePath + ", stack=" + e.stack);
|
||||
}
|
||||
|
||||
return {
|
||||
label: noteTitle,
|
||||
value: notePath
|
||||
}
|
||||
}),
|
||||
minLength: 0,
|
||||
autoFocus: true,
|
||||
select: function (event, ui) {
|
||||
noteTree.activateNode(ui.item.value);
|
||||
|
||||
$searchInput.autocomplete('destroy');
|
||||
$dialog.dialog('close');
|
||||
},
|
||||
focus: function (event, ui) {
|
||||
event.preventDefault();
|
||||
},
|
||||
close: function (event, ui) {
|
||||
if (event.keyCode === 27) { // escape closes dialog
|
||||
$searchInput.autocomplete('destroy');
|
||||
$dialog.dialog('close');
|
||||
}
|
||||
else {
|
||||
// keep autocomplete open
|
||||
// we're kind of abusing autocomplete to work in a way which it's not designed for
|
||||
$searchInput.autocomplete("search", "");
|
||||
}
|
||||
},
|
||||
create: () => $searchInput.autocomplete("search", ""),
|
||||
classes: {
|
||||
"ui-autocomplete": "recent-notes-autocomplete"
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
reload();
|
||||
|
||||
$(document).bind('keydown', 'ctrl+e', e => {
|
||||
showDialog();
|
||||
|
||||
e.preventDefault();
|
||||
$dialog.dialog({
|
||||
modal: true,
|
||||
width: 800,
|
||||
height: 100,
|
||||
position: { my: "center top+100", at: "top", of: window }
|
||||
});
|
||||
|
||||
return {
|
||||
showDialog,
|
||||
addRecentNote,
|
||||
reload
|
||||
};
|
||||
})();
|
||||
$searchInput.val('');
|
||||
|
||||
const result = await server.get('recent-notes');
|
||||
|
||||
// remove the current note
|
||||
const recNotes = result.filter(note => note.notePath !== treeService.getCurrentNotePath());
|
||||
|
||||
const items = recNotes.map(rn => {
|
||||
return {
|
||||
label: rn.title,
|
||||
value: rn.notePath
|
||||
};
|
||||
});
|
||||
|
||||
$searchInput.autocomplete({
|
||||
source: items,
|
||||
minLength: 0,
|
||||
autoFocus: true,
|
||||
select: function (event, ui) {
|
||||
treeService.activateNode(ui.item.value);
|
||||
|
||||
$searchInput.autocomplete('destroy');
|
||||
$dialog.dialog('close');
|
||||
},
|
||||
focus: function (event, ui) {
|
||||
event.preventDefault();
|
||||
},
|
||||
close: function (event, ui) {
|
||||
if (event.keyCode === 27) { // escape closes dialog
|
||||
$searchInput.autocomplete('destroy');
|
||||
$dialog.dialog('close');
|
||||
}
|
||||
else {
|
||||
// keep autocomplete open
|
||||
// we're kind of abusing autocomplete to work in a way which it's not designed for
|
||||
$searchInput.autocomplete("search", "");
|
||||
}
|
||||
},
|
||||
create: () => $searchInput.autocomplete("search", ""),
|
||||
classes: {
|
||||
"ui-autocomplete": "recent-notes-autocomplete"
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export default {
|
||||
showDialog,
|
||||
addRecentNote
|
||||
};
|
||||
@@ -1,205 +0,0 @@
|
||||
"use strict";
|
||||
|
||||
const settings = (function() {
|
||||
const $dialog = $("#settings-dialog");
|
||||
const $tabs = $("#settings-tabs");
|
||||
|
||||
const settingModules = [];
|
||||
|
||||
function addModule(module) {
|
||||
settingModules.push(module);
|
||||
}
|
||||
|
||||
async function showDialog() {
|
||||
glob.activeDialog = $dialog;
|
||||
|
||||
const settings = await server.get('settings');
|
||||
|
||||
$dialog.dialog({
|
||||
modal: true,
|
||||
width: 900
|
||||
});
|
||||
|
||||
$tabs.tabs();
|
||||
|
||||
for (const module of settingModules) {
|
||||
if (module.settingsLoaded) {
|
||||
module.settingsLoaded(settings);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function saveSettings(settingName, settingValue) {
|
||||
await server.post('settings', {
|
||||
name: settingName,
|
||||
value: settingValue
|
||||
});
|
||||
|
||||
showMessage("Settings change have been saved.");
|
||||
}
|
||||
|
||||
return {
|
||||
showDialog,
|
||||
saveSettings,
|
||||
addModule
|
||||
};
|
||||
})();
|
||||
|
||||
settings.addModule((function() {
|
||||
const $form = $("#change-password-form");
|
||||
const $oldPassword = $("#old-password");
|
||||
const $newPassword1 = $("#new-password1");
|
||||
const $newPassword2 = $("#new-password2");
|
||||
|
||||
function settingsLoaded(settings) {
|
||||
}
|
||||
|
||||
$form.submit(() => {
|
||||
const oldPassword = $oldPassword.val();
|
||||
const newPassword1 = $newPassword1.val();
|
||||
const newPassword2 = $newPassword2.val();
|
||||
|
||||
$oldPassword.val('');
|
||||
$newPassword1.val('');
|
||||
$newPassword2.val('');
|
||||
|
||||
if (newPassword1 !== newPassword2) {
|
||||
alert("New passwords are not the same.");
|
||||
return false;
|
||||
}
|
||||
|
||||
server.post('password/change', {
|
||||
'current_password': oldPassword,
|
||||
'new_password': newPassword1
|
||||
}).then(result => {
|
||||
if (result.success) {
|
||||
alert("Password has been changed. Trilium will be reloaded after you press OK.");
|
||||
|
||||
// password changed so current protected session is invalid and needs to be cleared
|
||||
protected_session.resetProtectedSession();
|
||||
}
|
||||
else {
|
||||
showError(result.message);
|
||||
}
|
||||
});
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
return {
|
||||
settingsLoaded
|
||||
};
|
||||
})());
|
||||
|
||||
settings.addModule((function() {
|
||||
const $form = $("#protected-session-timeout-form");
|
||||
const $protectedSessionTimeout = $("#protected-session-timeout-in-seconds");
|
||||
const settingName = 'protected_session_timeout';
|
||||
|
||||
function settingsLoaded(settings) {
|
||||
$protectedSessionTimeout.val(settings[settingName]);
|
||||
}
|
||||
|
||||
$form.submit(() => {
|
||||
const protectedSessionTimeout = $protectedSessionTimeout.val();
|
||||
|
||||
settings.saveSettings(settingName, protectedSessionTimeout).then(() => {
|
||||
protected_session.setProtectedSessionTimeout(protectedSessionTimeout);
|
||||
});
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
return {
|
||||
settingsLoaded
|
||||
};
|
||||
})());
|
||||
|
||||
settings.addModule((function () {
|
||||
const $form = $("#history-snapshot-time-interval-form");
|
||||
const $timeInterval = $("#history-snapshot-time-interval-in-seconds");
|
||||
const settingName = 'history_snapshot_time_interval';
|
||||
|
||||
function settingsLoaded(settings) {
|
||||
$timeInterval.val(settings[settingName]);
|
||||
}
|
||||
|
||||
$form.submit(() => {
|
||||
settings.saveSettings(settingName, $timeInterval.val());
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
return {
|
||||
settingsLoaded
|
||||
};
|
||||
})());
|
||||
|
||||
settings.addModule((async function () {
|
||||
const $appVersion = $("#app-version");
|
||||
const $dbVersion = $("#db-version");
|
||||
const $buildDate = $("#build-date");
|
||||
const $buildRevision = $("#build-revision");
|
||||
|
||||
const appInfo = await server.get('app-info');
|
||||
|
||||
$appVersion.html(appInfo.app_version);
|
||||
$dbVersion.html(appInfo.db_version);
|
||||
$buildDate.html(appInfo.build_date);
|
||||
$buildRevision.html(appInfo.build_revision);
|
||||
$buildRevision.attr('href', 'https://github.com/zadam/trilium/commit/' + appInfo.build_revision);
|
||||
|
||||
return {};
|
||||
})());
|
||||
|
||||
settings.addModule((async function () {
|
||||
const $forceFullSyncButton = $("#force-full-sync-button");
|
||||
const $fillSyncRowsButton = $("#fill-sync-rows-button");
|
||||
const $anonymizeButton = $("#anonymize-button");
|
||||
const $cleanupSoftDeletedButton = $("#cleanup-soft-deleted-items-button");
|
||||
const $cleanupUnusedImagesButton = $("#cleanup-unused-images-button");
|
||||
const $vacuumDatabaseButton = $("#vacuum-database-button");
|
||||
|
||||
$forceFullSyncButton.click(async () => {
|
||||
await server.post('sync/force-full-sync');
|
||||
|
||||
showMessage("Full sync triggered");
|
||||
});
|
||||
|
||||
$fillSyncRowsButton.click(async () => {
|
||||
await server.post('sync/fill-sync-rows');
|
||||
|
||||
showMessage("Sync rows filled successfully");
|
||||
});
|
||||
|
||||
|
||||
$anonymizeButton.click(async () => {
|
||||
await server.post('anonymization/anonymize');
|
||||
|
||||
showMessage("Created anonymized database");
|
||||
});
|
||||
|
||||
$cleanupSoftDeletedButton.click(async () => {
|
||||
if (confirm("Do you really want to clean up soft-deleted items?")) {
|
||||
await server.post('cleanup/cleanup-soft-deleted-items');
|
||||
|
||||
showMessage("Soft deleted items have been cleaned up");
|
||||
}
|
||||
});
|
||||
|
||||
$cleanupUnusedImagesButton.click(async () => {
|
||||
if (confirm("Do you really want to clean up unused images?")) {
|
||||
await server.post('cleanup/cleanup-unused-images');
|
||||
|
||||
showMessage("Unused images have been cleaned up");
|
||||
}
|
||||
});
|
||||
|
||||
$vacuumDatabaseButton.click(async () => {
|
||||
await server.post('cleanup/vacuum-database');
|
||||
|
||||
showMessage("Database has been vacuumed");
|
||||
});
|
||||
|
||||
return {};
|
||||
})());
|
||||
@@ -1,106 +1,105 @@
|
||||
"use strict";
|
||||
import utils from '../services/utils.js';
|
||||
import libraryLoader from '../services/library_loader.js';
|
||||
import server from '../services/server.js';
|
||||
import infoService from "../services/info.js";
|
||||
|
||||
const sqlConsole = (function() {
|
||||
const $dialog = $("#sql-console-dialog");
|
||||
const $query = $('#sql-console-query');
|
||||
const $executeButton = $('#sql-console-execute');
|
||||
const $resultHead = $('#sql-console-results thead');
|
||||
const $resultBody = $('#sql-console-results tbody');
|
||||
const $dialog = $("#sql-console-dialog");
|
||||
const $query = $('#sql-console-query');
|
||||
const $executeButton = $('#sql-console-execute');
|
||||
const $resultHead = $('#sql-console-results thead');
|
||||
const $resultBody = $('#sql-console-results tbody');
|
||||
|
||||
let codeEditor;
|
||||
let codeEditor;
|
||||
|
||||
function showDialog() {
|
||||
glob.activeDialog = $dialog;
|
||||
function showDialog() {
|
||||
glob.activeDialog = $dialog;
|
||||
|
||||
$dialog.dialog({
|
||||
modal: true,
|
||||
width: $(window).width(),
|
||||
height: $(window).height(),
|
||||
open: function() {
|
||||
initEditor();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async function initEditor() {
|
||||
if (!codeEditor) {
|
||||
await requireLibrary(CODE_MIRROR);
|
||||
|
||||
CodeMirror.keyMap.default["Shift-Tab"] = "indentLess";
|
||||
CodeMirror.keyMap.default["Tab"] = "indentMore";
|
||||
|
||||
// removing Escape binding so that Escape will propagate to the dialog (which will close on escape)
|
||||
delete CodeMirror.keyMap.basic["Esc"];
|
||||
|
||||
CodeMirror.modeURL = 'libraries/codemirror/mode/%N/%N.js';
|
||||
|
||||
codeEditor = CodeMirror($query[0], {
|
||||
value: "",
|
||||
viewportMargin: Infinity,
|
||||
indentUnit: 4,
|
||||
highlightSelectionMatches: {showToken: /\w/, annotateScrollbar: false}
|
||||
});
|
||||
|
||||
codeEditor.setOption("mode", "text/x-sqlite");
|
||||
CodeMirror.autoLoadMode(codeEditor, "sql");
|
||||
$dialog.dialog({
|
||||
modal: true,
|
||||
width: $(window).width(),
|
||||
height: $(window).height(),
|
||||
open: function() {
|
||||
initEditor();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
codeEditor.focus();
|
||||
}
|
||||
async function initEditor() {
|
||||
if (!codeEditor) {
|
||||
await libraryLoader.requireLibrary(libraryLoader.CODE_MIRROR);
|
||||
|
||||
async function execute(e) {
|
||||
// stop from propagating upwards (dangerous especially with ctrl+enter executable javascript notes)
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
CodeMirror.keyMap.default["Shift-Tab"] = "indentLess";
|
||||
CodeMirror.keyMap.default["Tab"] = "indentMore";
|
||||
|
||||
const sqlQuery = codeEditor.getValue();
|
||||
// removing Escape binding so that Escape will propagate to the dialog (which will close on escape)
|
||||
delete CodeMirror.keyMap.basic["Esc"];
|
||||
|
||||
const result = await server.post("sql/execute", {
|
||||
query: sqlQuery
|
||||
CodeMirror.modeURL = 'libraries/codemirror/mode/%N/%N.js';
|
||||
|
||||
codeEditor = CodeMirror($query[0], {
|
||||
value: "",
|
||||
viewportMargin: Infinity,
|
||||
indentUnit: 4,
|
||||
highlightSelectionMatches: {showToken: /\w/, annotateScrollbar: false}
|
||||
});
|
||||
|
||||
if (!result.success) {
|
||||
showError(result.error);
|
||||
return;
|
||||
}
|
||||
else {
|
||||
showMessage("Query was executed successfully.");
|
||||
}
|
||||
|
||||
const rows = result.rows;
|
||||
|
||||
$resultHead.empty();
|
||||
$resultBody.empty();
|
||||
|
||||
if (rows.length > 0) {
|
||||
const result = rows[0];
|
||||
const rowEl = $("<tr>");
|
||||
|
||||
for (const key in result) {
|
||||
rowEl.append($("<th>").html(key));
|
||||
}
|
||||
|
||||
$resultHead.append(rowEl);
|
||||
}
|
||||
|
||||
for (const result of rows) {
|
||||
const rowEl = $("<tr>");
|
||||
|
||||
for (const key in result) {
|
||||
rowEl.append($("<td>").html(result[key]));
|
||||
}
|
||||
|
||||
$resultBody.append(rowEl);
|
||||
}
|
||||
codeEditor.setOption("mode", "text/x-sqlite");
|
||||
CodeMirror.autoLoadMode(codeEditor, "sql");
|
||||
}
|
||||
|
||||
$(document).bind('keydown', 'alt+o', showDialog);
|
||||
codeEditor.focus();
|
||||
}
|
||||
|
||||
$query.bind('keydown', 'ctrl+return', execute);
|
||||
async function execute(e) {
|
||||
// stop from propagating upwards (dangerous especially with ctrl+enter executable javascript notes)
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
$executeButton.click(execute);
|
||||
const sqlQuery = codeEditor.getValue();
|
||||
|
||||
return {
|
||||
showDialog
|
||||
};
|
||||
})();
|
||||
const result = await server.post("sql/execute", {
|
||||
query: sqlQuery
|
||||
});
|
||||
|
||||
if (!result.success) {
|
||||
infoService.showError(result.error);
|
||||
return;
|
||||
}
|
||||
else {
|
||||
infoService.showMessage("Query was executed successfully.");
|
||||
}
|
||||
|
||||
const rows = result.rows;
|
||||
|
||||
$resultHead.empty();
|
||||
$resultBody.empty();
|
||||
|
||||
if (rows.length > 0) {
|
||||
const result = rows[0];
|
||||
const rowEl = $("<tr>");
|
||||
|
||||
for (const key in result) {
|
||||
rowEl.append($("<th>").html(key));
|
||||
}
|
||||
|
||||
$resultHead.append(rowEl);
|
||||
}
|
||||
|
||||
for (const result of rows) {
|
||||
const rowEl = $("<tr>");
|
||||
|
||||
for (const key in result) {
|
||||
rowEl.append($("<td>").html(result[key]));
|
||||
}
|
||||
|
||||
$resultBody.append(rowEl);
|
||||
}
|
||||
}
|
||||
|
||||
$query.bind('keydown', 'ctrl+return', execute);
|
||||
|
||||
$executeButton.click(execute);
|
||||
|
||||
export default {
|
||||
showDialog
|
||||
};
|
||||
26
src/public/javascripts/entities/branch.js
Normal file
@@ -0,0 +1,26 @@
|
||||
class Branch {
|
||||
constructor(treeCache, row) {
|
||||
this.treeCache = treeCache;
|
||||
this.branchId = row.branchId;
|
||||
this.noteId = row.noteId;
|
||||
this.note = null;
|
||||
this.parentNoteId = row.parentNoteId;
|
||||
this.notePosition = row.notePosition;
|
||||
this.prefix = row.prefix;
|
||||
this.isExpanded = row.isExpanded;
|
||||
}
|
||||
|
||||
async getNote() {
|
||||
return await this.treeCache.getNote(this.noteId);
|
||||
}
|
||||
|
||||
isTopLevel() {
|
||||
return this.parentNoteId === 'root';
|
||||
}
|
||||
|
||||
get toString() {
|
||||
return `Branch(branchId=${this.branchId})`;
|
||||
}
|
||||
}
|
||||
|
||||
export default Branch;
|
||||
18
src/public/javascripts/entities/note_full.js
Normal file
@@ -0,0 +1,18 @@
|
||||
import NoteShort from './note_short.js';
|
||||
|
||||
class NoteFull extends NoteShort {
|
||||
constructor(treeCache, row) {
|
||||
super(treeCache, row);
|
||||
|
||||
this.content = row.content;
|
||||
|
||||
if (this.content !== "" && this.isJson()) {
|
||||
try {
|
||||
this.jsonContent = JSON.parse(this.content);
|
||||
}
|
||||
catch(e) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default NoteFull;
|
||||